mirror of
https://github.com/ComfyFactory/ComfyFactorio.git
synced 2024-12-26 22:56:43 +02:00
565 lines
18 KiB
Lua
565 lines
18 KiB
Lua
-- This file is part of thesixthroc's Pirate Ship softmod, licensed under GPLv3 and stored at https://github.com/ComfyFactory/ComfyFactorio and https://github.com/danielmartin0/ComfyFactorio-Pirates.
|
|
|
|
local Public = {}
|
|
|
|
local Memory = require('maps.pirates.memory')
|
|
local Math = require('maps.pirates.math')
|
|
local Balance = require('maps.pirates.balance')
|
|
local Common = require('maps.pirates.common')
|
|
local CoreData = require('maps.pirates.coredata')
|
|
local Utils = require('maps.pirates.utils_local')
|
|
local _inspect = require('utils.inspect').inspect
|
|
|
|
-- local Structures = require 'maps.pirates.structures.structures'
|
|
local Boats = require('maps.pirates.structures.boats.boats')
|
|
local Surfaces = require('maps.pirates.surfaces.surfaces')
|
|
local Crowsnest = require('maps.pirates.surfaces.crowsnest')
|
|
local Server = require('utils.server')
|
|
local Dock = require('maps.pirates.surfaces.dock')
|
|
-- local Islands = require 'maps.pirates.surfaces.islands.islands'
|
|
local Sea = require('maps.pirates.surfaces.sea.sea')
|
|
local Crew = require('maps.pirates.crew')
|
|
local Roles = require('maps.pirates.roles.roles')
|
|
-- local Parrot = require 'maps.pirates.parrot'
|
|
-- local Quest = require 'maps.pirates.quest'
|
|
|
|
-- local Shop = require 'maps.pirates.shop.shop'
|
|
local Overworld = require('maps.pirates.overworld')
|
|
local Hold = require('maps.pirates.surfaces.hold')
|
|
local Cabin = require('maps.pirates.surfaces.cabin')
|
|
-- local Upgrades = require 'maps.pirates.shop.boat_upgrades'
|
|
local Task = require('utils.task')
|
|
local Token = require('utils.token')
|
|
local ShopDock = require('maps.pirates.shop.dock')
|
|
-- local IslandEnum = require 'maps.pirates.surfaces.islands.island_enum'
|
|
|
|
function Public.get_fuel_depletion_rate_once_per_second()
|
|
local memory = Memory.get_crew_memory()
|
|
local state = memory.boat.state
|
|
|
|
if state == Boats.enum_state.ATSEA_SAILING or state == Boats.enum_state.APPROACHING then
|
|
local ret = Balance.fuel_depletion_rate_sailing()
|
|
memory.playtesting_stats.fuel_spent_at_sea = memory.playtesting_stats.fuel_spent_at_sea + ret
|
|
return ret
|
|
elseif state == Boats.enum_state.LEAVING_DOCK then
|
|
local ret = Balance.fuel_depletion_rate_sailing() * 1.25
|
|
memory.playtesting_stats.fuel_spent_at_destinations_while_moving = memory.playtesting_stats.fuel_spent_at_destinations_while_moving
|
|
+ ret
|
|
return ret
|
|
elseif state == Boats.enum_state.RETREATING then
|
|
local ret = Balance.fuel_depletion_rate_sailing() / 4
|
|
memory.playtesting_stats.fuel_spent_at_destinations_while_moving = memory.playtesting_stats.fuel_spent_at_destinations_while_moving
|
|
+ ret
|
|
return ret
|
|
elseif state == Boats.enum_state.LANDED then
|
|
local ret = Balance.fuel_depletion_rate_static()
|
|
memory.playtesting_stats.fuel_spent_at_destinations_passively = memory.playtesting_stats.fuel_spent_at_destinations_passively
|
|
+ ret
|
|
return ret
|
|
elseif state == Boats.enum_state.DOCKED then
|
|
return -0.1
|
|
else
|
|
return 0
|
|
end
|
|
end
|
|
|
|
function Public.set_off_from_starting_dock()
|
|
local memory = Memory.get_crew_memory()
|
|
if memory.game_lost then
|
|
return
|
|
end
|
|
|
|
memory.crewstatus = Crew.enum.LEAVING_INITIAL_DOCK
|
|
memory.boat.state = Boats.enum_state.LEAVING_DOCK
|
|
|
|
Boats.place_boat(memory.boat, CoreData.moving_boat_floor, false, false)
|
|
|
|
Common.current_destination().type = Surfaces.enum.LOBBY
|
|
|
|
memory.map_being_loaded_destination_index = 0 --This is a dummy value, overwritten later
|
|
memory.loading_ticks = 0
|
|
|
|
local surface = game.surfaces[CoreData.lobby_surface_name]
|
|
local p = Utils.psum({ memory.boat.position, Boats.get_scope(memory.boat).Data.crewname_rendering_position })
|
|
memory.boat.rendering_crewname_text = rendering.draw_text({
|
|
text = memory.name,
|
|
surface = surface,
|
|
target = p,
|
|
color = CoreData.colors.renderingtext_yellow,
|
|
scale = 8,
|
|
font = 'default-game',
|
|
alignment = 'left',
|
|
})
|
|
end
|
|
|
|
function Public.go_from_starting_dock_to_first_destination()
|
|
local memory = Memory.get_crew_memory()
|
|
local boat = memory.boat
|
|
|
|
local crew_members = Crew.choose_crew_members()
|
|
local crew_members_count = #memory.crewplayerindices
|
|
|
|
if crew_members_count == 0 then
|
|
Boats.destroy_boat(boat)
|
|
Crew.disband_crew()
|
|
return
|
|
end
|
|
|
|
memory.crewstatus = Crew.enum.ADVENTURING
|
|
memory.age = 0
|
|
memory.age_since_first_island = 0
|
|
|
|
local message = '[' .. memory.name .. '] Crew members: '
|
|
local b = false
|
|
for _, index in pairs(memory.crewplayerindices) do
|
|
if game.players[index] and game.players[index].name then
|
|
if b == true then
|
|
message = message .. ', '
|
|
else
|
|
b = true
|
|
end
|
|
message = message .. game.players[index].name
|
|
end
|
|
end
|
|
message = message .. '.'
|
|
Server.to_discord_embed_raw(CoreData.comfy_emojis.pogkot .. message)
|
|
|
|
Roles.assign_captain_based_on_priorities()
|
|
|
|
for _, player in pairs(crew_members) do
|
|
for item, amount in pairs(Balance.starting_items_player) do
|
|
player.insert({ name = item, count = amount })
|
|
end
|
|
end
|
|
|
|
boat.stored_resources = {}
|
|
|
|
Hold.create_hold_surface(1)
|
|
boat.EEI_stage = 1
|
|
boat.random_class_purchase_count = 0
|
|
Cabin.create_cabin_surface()
|
|
|
|
local items = Balance.starting_items_crew_upstairs()
|
|
-- Boats.deck_place_random_obstacle_boxes(boat, 6, items, 0)
|
|
|
|
-- Let's try just adding the items to nearby boxes
|
|
local scope = Boats.get_scope(boat)
|
|
local surface = game.surfaces[boat.surface_name]
|
|
local boxes = surface.find_entities_filtered({
|
|
name = 'wooden-chest',
|
|
area = {
|
|
{ x = boat.position.x - scope.Data.width / 2, y = boat.position.y - scope.Data.height / 2 },
|
|
{ x = boat.position.x + scope.Data.width / 2, y = boat.position.y + scope.Data.height / 2 },
|
|
},
|
|
})
|
|
boxes = Math.shuffle(boxes)
|
|
for i = 1, #items do
|
|
if boxes[i] then
|
|
local inventory = boxes[i].get_inventory(defines.inventory.chest)
|
|
for name, count in pairs(items[i]) do
|
|
inventory.insert({ name = name, count = count })
|
|
end
|
|
else
|
|
game.print('fail at ' .. boxes[i].position.x .. ' ' .. boxes[i].position.y)
|
|
end
|
|
end
|
|
|
|
-- Immediately move leftwards, lining us up with future docks (so newbies hit docks naturally).
|
|
Crowsnest.move_crowsnest(0, -24)
|
|
|
|
Public.progress_to_destination(memory.map_being_loaded_destination_index)
|
|
|
|
-- local scope = Boats.get_scope(boat)
|
|
-- local boatwidth, boatheight = scope.Data.width, scope.Data.height
|
|
-- Common.surface_place_random_obstacle_boxes(game.surfaces[boat.surface_name], {x = boat.position.x - boatwidth*0.575, y = boat.position.y}, boatwidth*0.85, boatheight*0.8, 'oil-refinery', {[1] = 3, [2] = 3, [3] = 0, [4] = 0}, items)
|
|
|
|
Boats.update_EEIs(boat)
|
|
|
|
-- if Common.difficulty_scale() == 1 then
|
|
-- Boats.upgrade_chests(boat, 'iron-chest')
|
|
-- Hold.upgrade_chests(1, 'iron-chest')
|
|
-- Crowsnest.upgrade_chests('iron-chest')
|
|
-- elseif Common.difficulty_scale() > 1 then
|
|
-- Boats.upgrade_chests(boat, 'steel-chest')
|
|
-- Hold.upgrade_chests(1, 'steel-chest')
|
|
-- Crowsnest.upgrade_chests('steel-chest')
|
|
-- end
|
|
end
|
|
|
|
local place_dock_jetty_and_boats = Token.register(function(data)
|
|
Memory.set_working_id(data.crew_id)
|
|
local memory = Memory.get_crew_memory()
|
|
if memory.game_lost then
|
|
return
|
|
end
|
|
Surfaces.Dock.place_dock_jetty_and_boats()
|
|
|
|
local destination = Common.current_destination()
|
|
ShopDock.create_dock_markets(game.surfaces[destination.surface_name], Surfaces.Dock.Data.markets_position)
|
|
end)
|
|
|
|
function Public.progress_to_destination(destination_index)
|
|
local memory = Memory.get_crew_memory()
|
|
if memory.game_lost then
|
|
return
|
|
end
|
|
|
|
local boat = memory.boat
|
|
|
|
local oldsurface = game.surfaces[boat.surface_name]
|
|
local old_type = Surfaces.SurfacesCommon.decode_surface_name(oldsurface.name).type
|
|
|
|
local destination_data = memory.destinations[destination_index]
|
|
local static_params = destination_data.static_params
|
|
local type = destination_data.type
|
|
local subtype = destination_data.subtype
|
|
local newsurface_name = Surfaces.SurfacesCommon.encode_surface_name(memory.id, destination_index, type, subtype)
|
|
local newsurface = game.surfaces[newsurface_name]
|
|
|
|
log('progressing to destination ' .. destination_index .. ' with type ' .. type)
|
|
|
|
local initial_boatspeed, starting_boatposition
|
|
|
|
-- if type == Surfaces.enum.ISLAND then --moved from overworld generation, so that it updates properly
|
|
-- Public.choose_quest_structures(destination_data)
|
|
-- end
|
|
|
|
if type == Surfaces.enum.DOCK then
|
|
local BoatData = Boats.get_scope(boat).Data
|
|
starting_boatposition = Utils.snap_coordinates_for_rails({
|
|
x = Dock.Data.playerboat_starting_xcoord,
|
|
y = Dock.Data.player_boat_top + BoatData.height / 2,
|
|
})
|
|
-- starting_boatposition = {x = -destination_data.static_params.width/2 + BoatData.width + 10, y = Dock.Data.player_boat_top - BoatData.height/2}
|
|
Common.current_destination().dynamic_data.time_remaining = 180
|
|
|
|
-- Delay.add(Delay.enum.PLACE_DOCK_JETTY_AND_BOATS)
|
|
Task.set_timeout_in_ticks(2, place_dock_jetty_and_boats, { crew_id = memory.id })
|
|
else
|
|
starting_boatposition =
|
|
{ x = static_params.boat_starting_xposition, y = static_params.boat_starting_yposition or 0 }
|
|
end
|
|
|
|
if oldsurface.name == CoreData.lobby_surface_name then
|
|
initial_boatspeed = 2.5
|
|
else
|
|
initial_boatspeed = 1.4
|
|
end
|
|
|
|
boat.speed = initial_boatspeed
|
|
boat.state = destination_data.init_boat_state
|
|
boat.dockedposition = nil
|
|
|
|
local old_water = 'deepwater'
|
|
if old_type == Surfaces.enum.LOBBY or old_type == Surfaces.enum.DOCK then
|
|
old_water = 'water'
|
|
end
|
|
|
|
Boats.teleport_boat(boat, newsurface_name, starting_boatposition, CoreData.moving_boat_floor, old_water)
|
|
|
|
if old_type == Surfaces.enum.LOBBY then
|
|
Crowsnest.draw_extra_bits()
|
|
end
|
|
Crowsnest.paint_around_destination(destination_index, CoreData.overworld_presence_tile)
|
|
|
|
if memory.loading_ticks then
|
|
memory.loading_ticks = -120
|
|
end
|
|
|
|
if old_type == Surfaces.enum.SEA then
|
|
game.delete_surface(oldsurface)
|
|
|
|
if memory.elite_biters then
|
|
for id, elite_biter in pairs(memory.elite_biters) do
|
|
if not elite_biter.valid then
|
|
memory.elite_biters[id] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
memory.elite_biters_stream_registrations = {}
|
|
end
|
|
|
|
memory.destinationsvisited_indices[#memory.destinationsvisited_indices + 1] = destination_index
|
|
|
|
memory.currentdestination_index = destination_index --already done when we collide with it typically
|
|
local destination = Common.current_destination()
|
|
|
|
destination.dynamic_data.timer = 0
|
|
destination.dynamic_data.timeratlandingtime = nil
|
|
destination.dynamic_data.enemyboats = {}
|
|
destination.dynamic_data.elite_spawners = {}
|
|
|
|
destination.dynamic_data.healthbars = {}
|
|
|
|
memory.extra_time_at_sea = 0
|
|
|
|
if old_type == Surfaces.enum.SEA or old_type == Surfaces.enum.CHANNEL or old_type == Surfaces.enum.DOCK then
|
|
-- move over anyone who was left behind, such as dead players
|
|
for _, player in pairs(game.connected_players) do
|
|
if Common.validate_player_and_character(player) then
|
|
if player.character.surface == oldsurface then
|
|
player.character.teleport(memory.spawnpoint, newsurface)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
Surfaces.destination_on_arrival(Common.current_destination())
|
|
end
|
|
|
|
function Public.check_for_end_of_boat_movement(boat)
|
|
local memory = Memory.get_crew_memory()
|
|
local destination = Common.current_destination()
|
|
|
|
local collided = Boats.collision_infront(boat)
|
|
|
|
local approaching_island = boat.state == Boats.enum_state.APPROACHING and destination.type == Surfaces.enum.ISLAND
|
|
local retreating_island = boat.state == Boats.enum_state.RETREATING and destination.type == Surfaces.enum.ISLAND
|
|
|
|
local approaching_dock = destination.type == Surfaces.enum.DOCK and boat.state == Boats.enum_state.APPROACHING
|
|
local leaving_dock = destination.type == Surfaces.enum.DOCK and boat.state == Boats.enum_state.LEAVING_DOCK
|
|
|
|
--=== Collision
|
|
if approaching_island and collided then
|
|
boat.state = Boats.enum_state.LANDED
|
|
boat.speed = 0
|
|
boat.dockedposition = boat.position
|
|
|
|
game.surfaces[boat.surface_name].play_sound({ path = 'utility/axe_fighting' })
|
|
game.surfaces[boat.surface_name].play_sound({ path = 'utility/axe_fighting' })
|
|
end
|
|
|
|
--=== Enemy
|
|
if boat.force_name == memory.enemy_force_name then
|
|
if approaching_island then
|
|
if collided then
|
|
boat.landing_time = destination.dynamic_data.timer
|
|
|
|
Boats.place_landingtrack(boat, CoreData.enemy_landing_tile)
|
|
|
|
return true
|
|
|
|
-- elseif boat.spawner and boat.spawner.valid and boat.spawner.destructible then
|
|
-- -- This code was somehow making the spawners destructible but still able to be shot at.
|
|
-- local boat2 = Utils.deepcopy(boat)
|
|
-- boat2.position = {x = boat.position.x + 6, y = boat.position.y}
|
|
-- if Boats.collision_infront(boat2) then
|
|
-- boat.spawner.destructible = false
|
|
-- end
|
|
end
|
|
end
|
|
|
|
--=== Friendly
|
|
elseif boat.force_name == memory.force_name then
|
|
if approaching_island and collided then
|
|
Surfaces.destination_on_crewboat_hits_shore(destination)
|
|
return true
|
|
elseif
|
|
retreating_island
|
|
and boat.position.x
|
|
< ((boat.dockedposition.x or 999) - Boats.get_scope(boat).Data.width - 2 * Boats.get_scope(boat).Data.rightmost_gate_position - 8)
|
|
then
|
|
Public.go_from_currentdestination_to_sea()
|
|
return true
|
|
elseif
|
|
approaching_dock
|
|
and boat.position.x + Boats.get_scope(boat).Data.rightmost_gate_position
|
|
>= Dock.Data.rightmostgate_stopping_xposition
|
|
then
|
|
boat.state = Boats.enum_state.DOCKED
|
|
boat.speed = 0
|
|
boat.dockedposition = boat.position
|
|
|
|
destination.dynamic_data.timeratlandingtime = destination.dynamic_data.timer
|
|
|
|
Boats.place_boat(boat, CoreData.static_boat_floor, false, false)
|
|
return true
|
|
elseif leaving_dock and boat.position.x >= game.surfaces[boat.surface_name].map_gen_settings.width / 2 - 60 then
|
|
Public.go_from_currentdestination_to_sea()
|
|
|
|
return true
|
|
|
|
--=== Fallthrough right-hand side
|
|
elseif
|
|
destination.type == Surfaces.enum.ISLAND
|
|
and boat.position.x >= game.surfaces[boat.surface_name].map_gen_settings.width / 2 - 10
|
|
then
|
|
Public.go_from_currentdestination_to_sea()
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function Public.try_retreat_from_island(player, manual) -- Assumes the cost can be paid
|
|
local memory = Memory.get_crew_memory()
|
|
if memory.game_lost then
|
|
return
|
|
end
|
|
local destination = Common.current_destination()
|
|
local boat = memory.boat
|
|
|
|
if boat.state == Boats.enum_state.RETREATING then
|
|
return
|
|
end
|
|
|
|
if Common.query_can_pay_cost_to_leave() then
|
|
if
|
|
destination.dynamic_data.timeratlandingtime
|
|
and destination.dynamic_data.timer < destination.dynamic_data.timeratlandingtime + 10
|
|
then
|
|
if player and Common.validate_player(player) then
|
|
Common.notify_player_error(player, { 'pirates.error_undock_too_early' })
|
|
end
|
|
else
|
|
Common.consume_undock_cost_resources()
|
|
Public.retreat_from_island(manual)
|
|
end
|
|
else
|
|
if player and Common.validate_player(player) then
|
|
Common.notify_force_error(player.force, { 'pirates.error_undock_insufficient_resources' })
|
|
end
|
|
end
|
|
end
|
|
|
|
function Public.retreat_from_island(manual)
|
|
local memory = Memory.get_crew_memory()
|
|
local boat = memory.boat
|
|
|
|
if boat.state == Boats.enum_state.RETREATING then
|
|
return
|
|
end
|
|
|
|
boat.state = Boats.enum_state.RETREATING
|
|
boat.speed = 1.25
|
|
|
|
Boats.place_boat(boat, CoreData.moving_boat_floor, false, false)
|
|
|
|
local force = memory.force
|
|
if not (force and force.valid) then
|
|
return
|
|
end
|
|
if manual then
|
|
Common.notify_force(force, { 'pirates.ship_undocked_1' })
|
|
else
|
|
Common.notify_force(force, { 'pirates.ship_undocked_2' })
|
|
end
|
|
|
|
Surfaces.destination_on_departure(Common.current_destination())
|
|
end
|
|
|
|
function Public.undock_from_dock(manual)
|
|
local memory = Memory.get_crew_memory()
|
|
local boat = memory.boat
|
|
local destination = Common.current_destination()
|
|
|
|
boat.state = Boats.enum_state.LEAVING_DOCK
|
|
destination.dynamic_data.time_remaining = -1
|
|
|
|
Boats.place_boat(boat, CoreData.moving_boat_floor, false, false)
|
|
|
|
Crew.summon_crew()
|
|
|
|
local force = memory.force
|
|
if not (force and force.valid) then
|
|
return
|
|
end
|
|
if manual then
|
|
Common.notify_force(force, { 'pirates.ship_undocked_1' })
|
|
else
|
|
Common.notify_force(force, { 'pirates.ship_undocked_3' })
|
|
end
|
|
end
|
|
|
|
local parrot_set_sail_advice = Token.register(function(data)
|
|
local crew_id = data.crew_id
|
|
Memory.set_working_id(crew_id)
|
|
|
|
local memory = Memory.get_crew_memory()
|
|
if not Common.is_id_valid(memory.id) then
|
|
return
|
|
end --check if crew disbanded
|
|
if memory.game_lost then
|
|
return
|
|
end
|
|
|
|
if memory.boat and memory.boat.state and memory.boat.state == Boats.enum_state.ATSEA_WAITING_TO_SAIL then
|
|
Common.parrot_speak(memory.force, { 'pirates.parrot_set_sail_advice' })
|
|
end
|
|
end)
|
|
|
|
function Public.go_from_currentdestination_to_sea()
|
|
local memory = Memory.get_crew_memory()
|
|
local destination = Common.current_destination()
|
|
if memory.game_lost then
|
|
return
|
|
end
|
|
|
|
Sea.ensure_sea_surface()
|
|
local seaname = memory.sea_name
|
|
local boat = memory.boat
|
|
|
|
local new_boatposition = Utils.snap_coordinates_for_rails({ x = Boats.get_scope(boat).Data.width / 2, y = 0 })
|
|
|
|
Boats.teleport_boat(boat, seaname, new_boatposition, CoreData.static_boat_floor, 'water')
|
|
|
|
if memory.overworldx == 0 and boat then
|
|
local difficulty_name = CoreData.get_difficulty_option_informal_name_from_value(memory.difficulty)
|
|
if difficulty_name == 'nightmare' then
|
|
Boats.upgrade_chests(boat, 'steel-chest')
|
|
Hold.upgrade_chests(1, 'steel-chest')
|
|
Crowsnest.upgrade_chests('steel-chest')
|
|
|
|
Common.parrot_speak(memory.force, { 'pirates.parrot_hard_praise' })
|
|
elseif difficulty_name ~= 'easy' then
|
|
Boats.upgrade_chests(boat, 'iron-chest')
|
|
Hold.upgrade_chests(1, 'iron-chest')
|
|
Crowsnest.upgrade_chests('iron-chest')
|
|
|
|
Common.parrot_speak(memory.force, { 'pirates.parrot_normal_praise' })
|
|
|
|
Task.set_timeout_in_ticks(60 * 10, parrot_set_sail_advice, { crew_id = memory.id })
|
|
end
|
|
end
|
|
|
|
boat.state = Boats.enum_state.ATSEA_SAILING
|
|
|
|
boat.speed = 0
|
|
boat.position = new_boatposition
|
|
boat.surface_name = seaname
|
|
boat.fish_caught_while_at_sea = 0 -- how many times a fish was caught, rather than amount of fish caught in total
|
|
|
|
memory.enemy_force.reset_evolution()
|
|
|
|
--@FIX: This doesn't change the evo during sea travel, which is relevant now that krakens are in the game:
|
|
local base_evo = Balance.base_evolution_leagues(memory.overworldx)
|
|
Common.set_evo(base_evo)
|
|
memory.dynamic_kraken_evo = 0
|
|
|
|
memory.loading_ticks = nil
|
|
memory.map_being_loaded_destination_index = nil
|
|
|
|
local d = destination.iconized_map_width + Crowsnest.platformwidth
|
|
|
|
Crowsnest.paint_around_destination(destination.destination_index, 'deepwater')
|
|
|
|
Overworld.try_overworld_move_v2({ x = d, y = 0 })
|
|
|
|
-- If crew revealed treasure, but couldn't figure out how to dig it, give them tip
|
|
if destination.dynamic_data.some_player_was_close_to_buried_treasure then
|
|
local maps = destination.dynamic_data.treasure_maps or {}
|
|
for _, map in pairs(maps) do
|
|
if map.state == 'picked_up' then
|
|
Common.parrot_speak(memory.force, { 'pirates.parrot_buried_treasure_tip' })
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
Surfaces.clean_up(destination)
|
|
end
|
|
|
|
return Public
|