Skip to content

Instantly share code, notes, and snippets.

@gamingoninsulin
Created December 20, 2024 16:52
Show Gist options
  • Save gamingoninsulin/dd76aee46a1f0a24e45cf10cd1185ebc to your computer and use it in GitHub Desktop.
Save gamingoninsulin/dd76aee46a1f0a24e45cf10cd1185ebc to your computer and use it in GitHub Desktop.
SurvivalGame.lua
dofile( "$SURVIVAL_DATA/Scripts/game/managers/BeaconManager.lua" )
dofile( "$SURVIVAL_DATA/Scripts/game/managers/EffectManager.lua" )
dofile( "$SURVIVAL_DATA/Scripts/game/managers/ElevatorManager.lua" )
dofile( "$SURVIVAL_DATA/Scripts/game/managers/QuestManager.lua" )
dofile( "$SURVIVAL_DATA/Scripts/game/managers/RespawnManager.lua" )
dofile( "$SURVIVAL_DATA/Scripts/game/managers/UnitManager.lua" )
dofile( "$SURVIVAL_DATA/Scripts/game/survival_constants.lua" )
dofile( "$SURVIVAL_DATA/Scripts/game/survival_harvestable.lua" )
dofile( "$SURVIVAL_DATA/Scripts/game/survival_shapes.lua" )
dofile( "$SURVIVAL_DATA/Scripts/game/survival_units.lua" )
dofile( "$SURVIVAL_DATA/Scripts/game/survival_projectiles.lua" )
dofile( "$SURVIVAL_DATA/Scripts/game/survival_meleeattacks.lua" )
dofile( "$SURVIVAL_DATA/Scripts/game/util/recipes.lua" )
dofile( "$SURVIVAL_DATA/Scripts/game/util/Timer.lua" )
dofile( "$SURVIVAL_DATA/Scripts/game/managers/QuestEntityManager.lua" )
dofile( "$GAME_DATA/Scripts/game/managers/EventManager.lua" )
---@class SurvivalGame : GameClass
---@field sv table
---@field cl table
---@field warehouses table
SurvivalGame = class( nil )
SurvivalGame.enableLimitedInventory = true
SurvivalGame.enableRestrictions = true
SurvivalGame.enableFuelConsumption = true
SurvivalGame.enableAmmoConsumption = true
SurvivalGame.enableUpgrade = true
local SyncInterval = 400 -- 400 ticks | 10 seconds
local IntroFadeDuration = 1.1
local IntroEndFadeDuration = 1.1
local IntroFadeTimeout = 5.0
function SurvivalGame.server_onCreate( self )
print( "SurvivalGame.server_onCreate" )
self.sv = {}
self.sv.saved = self.storage:load()
print( "Saved:", self.sv.saved )
if self.sv.saved == nil then
self.sv.saved = {}
self.sv.saved.data = self.data
printf( "Seed: %.0f", self.sv.saved.data.seed )
self.sv.saved.overworld = sm.world.createWorld( "$SURVIVAL_DATA/Scripts/game/worlds/Overworld.lua", "Overworld", { dev = self.sv.saved.data.dev }, self.sv.saved.data.seed )
self.storage:save( self.sv.saved )
end
self.data = nil
print( self.sv.saved.data )
if self.sv.saved.data and self.sv.saved.data.dev then
g_godMode = false
g_survivalDev = true
sm.log.info( "Starting SurvivalGame in DEV mode" )
end
self:loadCraftingRecipes()
g_enableCollisionTumble = true
g_eventManager = EventManager()
g_eventManager:sv_onCreate()
g_elevatorManager = ElevatorManager()
g_elevatorManager:sv_onCreate()
g_respawnManager = RespawnManager()
g_respawnManager:sv_onCreate( self.sv.saved.overworld )
g_beaconManager = BeaconManager()
g_beaconManager:sv_onCreate()
g_unitManager = UnitManager()
g_unitManager:sv_onCreate( self.sv.saved.overworld )
self.sv.questEntityManager = sm.scriptableObject.createScriptableObject( sm.uuid.new( "c6988ecb-0fc1-4d45-afde-dc583b8b75ee" ) )
self.sv.questManager = sm.storage.load( STORAGE_CHANNEL_QUESTMANAGER )
if not self.sv.questManager then
self.sv.questManager = sm.scriptableObject.createScriptableObject( sm.uuid.new( "83b0cc7e-b164-47b8-a83c-0d33ba5f72ec" ) )
sm.storage.save( STORAGE_CHANNEL_QUESTMANAGER, self.sv.questManager )
end
-- Game script managed global warehouse table
self.sv.warehouses = sm.storage.load( STORAGE_CHANNEL_WAREHOUSES )
if self.sv.warehouses then
print( "Loaded warehouses:" )
print( self.sv.warehouses )
else
self.sv.warehouses = {}
sm.storage.save( STORAGE_CHANNEL_WAREHOUSES, self.sv.warehouses )
end
self.sv.time = sm.storage.load( STORAGE_CHANNEL_TIME )
if self.sv.time then
print( "Loaded timeData:" )
print( self.sv.time )
else
self.sv.time = {}
self.sv.time.timeOfDay = 6 / 24 -- 06:00
self.sv.time.timeProgress = true
sm.storage.save( STORAGE_CHANNEL_TIME, self.sv.time )
end
self.network:setClientData( { dev = g_survivalDev }, 1 )
self:sv_updateClientData()
self.sv.syncTimer = Timer()
self.sv.syncTimer:start( 0 )
end
function SurvivalGame.server_onRefresh( self )
g_craftingRecipes = nil
g_refineryRecipes = nil
self:loadCraftingRecipes()
end
function SurvivalGame.client_onCreate( self )
self.cl = {}
self.cl.time = {}
self.cl.time.timeOfDay = 0.0
self.cl.time.timeProgress = true
if not sm.isHost then
self:loadCraftingRecipes()
g_enableCollisionTumble = true
end
if g_respawnManager == nil then
assert( not sm.isHost )
g_respawnManager = RespawnManager()
end
g_respawnManager:cl_onCreate()
if g_beaconManager == nil then
assert( not sm.isHost )
g_beaconManager = BeaconManager()
end
g_beaconManager:cl_onCreate()
if g_unitManager == nil then
assert( not sm.isHost )
g_unitManager = UnitManager()
end
g_unitManager:cl_onCreate()
g_effectManager = EffectManager()
g_effectManager:cl_onCreate()
-- Music effect
g_survivalMusic = sm.effect.createEffect( "SurvivalMusic" )
assert(g_survivalMusic)
-- Survival HUD
g_survivalHud = sm.gui.createSurvivalHudGui()
assert(g_survivalHud)
end
function SurvivalGame.bindChatCommands( self )
local addCheats = true
if true then
sm.game.bindChatCommand( "/ammo", { { "int", "quantity", true } }, "cl_onChatCommand", "Give ammo (default 50)" )
sm.game.bindChatCommand( "/spudgun", {}, "cl_onChatCommand", "Give the spudgun" )
sm.game.bindChatCommand( "/gatling", {}, "cl_onChatCommand", "Give the potato gatling gun" )
sm.game.bindChatCommand( "/shotgun", {}, "cl_onChatCommand", "Give the fries shotgun" )
sm.game.bindChatCommand( "/sunshake", {}, "cl_onChatCommand", "Give 1 sunshake" )
sm.game.bindChatCommand( "/baguette", {}, "cl_onChatCommand", "Give 1 revival baguette" )
sm.game.bindChatCommand( "/keycard", {}, "cl_onChatCommand", "Give 1 keycard" )
sm.game.bindChatCommand( "/powercore", {}, "cl_onChatCommand", "Give 1 powercore" )
sm.game.bindChatCommand( "/components", { { "int", "quantity", true } }, "cl_onChatCommand", "Give <quantity> components (default 10)" )
sm.game.bindChatCommand( "/glowsticks", { { "int", "quantity", true } }, "cl_onChatCommand", "Give <quantity> components (default 10)" )
sm.game.bindChatCommand( "/tumble", { { "bool", "enable", true } }, "cl_onChatCommand", "Set tumble state" )
sm.game.bindChatCommand( "/god", {}, "cl_onChatCommand", "Mechanic characters will take no damage" )
sm.game.bindChatCommand( "/respawn", {}, "cl_onChatCommand", "Respawn at last bed (or at the crash site)" )
sm.game.bindChatCommand( "/encrypt", {}, "cl_onChatCommand", "Restrict interactions in all warehouses" )
sm.game.bindChatCommand( "/decrypt", {}, "cl_onChatCommand", "Unrestrict interactions in all warehouses" )
sm.game.bindChatCommand( "/limited", {}, "cl_onChatCommand", "Use the limited inventory" )
sm.game.bindChatCommand( "/unlimited", {}, "cl_onChatCommand", "Use the unlimited inventory" )
sm.game.bindChatCommand( "/ambush", { { "number", "magnitude", true }, { "int", "wave", true } }, "cl_onChatCommand", "Starts a 'random' encounter" )
--sm.game.bindChatCommand( "/recreate", {}, "cl_onChatCommand", "Recreate world" )
sm.game.bindChatCommand( "/timeofday", { { "number", "timeOfDay", true } }, "cl_onChatCommand", "Sets the time of the day as a fraction (0.5=mid day)" )
sm.game.bindChatCommand( "/timeprogress", { { "bool", "enabled", true } }, "cl_onChatCommand", "Enables or disables time progress" )
sm.game.bindChatCommand( "/day", {}, "cl_onChatCommand", "Disable time progression and set time to daytime" )
sm.game.bindChatCommand( "/spawn", { { "string", "unitName", true }, { "int", "amount", true } }, "cl_onChatCommand", "Spawn a unit: 'woc', 'tapebot', 'totebot', 'haybot'" )
sm.game.bindChatCommand( "/harvestable", { { "string", "harvestableName", true } }, "cl_onChatCommand", "Create a harvestable: 'tree', 'stone'" )
sm.game.bindChatCommand( "/cleardebug", {}, "cl_onChatCommand", "Clear debug draw objects" )
sm.game.bindChatCommand( "/import", { { "string", "name", false } }, "cl_onChatCommand", "Imports blueprint $SURVIVAL_DATA/LocalBlueprints/<name>.blueprint" )
sm.game.bindChatCommand( "/starterkit", {}, "cl_onChatCommand", "Spawn a starter kit" )
sm.game.bindChatCommand( "/mechanicstartkit", {}, "cl_onChatCommand", "Spawn a starter kit for starting at mechanic station" )
sm.game.bindChatCommand( "/pipekit", {}, "cl_onChatCommand", "Spawn a pipe kit" )
sm.game.bindChatCommand( "/foodkit", {}, "cl_onChatCommand", "Spawn a food kit" )
sm.game.bindChatCommand( "/seedkit", {}, "cl_onChatCommand", "Spawn a seed kit" )
sm.game.bindChatCommand( "/die", {}, "cl_onChatCommand", "Kill the player" )
sm.game.bindChatCommand( "/sethp", { { "number", "hp", false } }, "cl_onChatCommand", "Set player hp value" )
sm.game.bindChatCommand( "/setwater", { { "number", "water", false } }, "cl_onChatCommand", "Set player water value" )
sm.game.bindChatCommand( "/setfood", { { "number", "food", false } }, "cl_onChatCommand", "Set player food value" )
sm.game.bindChatCommand( "/aggroall", {}, "cl_onChatCommand", "All hostile units will be made aware of the player's position" )
sm.game.bindChatCommand( "/goto", { { "string", "name", false } }, "cl_onChatCommand", "Teleport to predefined position" )
sm.game.bindChatCommand( "/raid", { { "int", "level", false }, { "int", "wave", true }, { "number", "hours", true } }, "cl_onChatCommand", "Start a level <level> raid at player position at wave <wave> in <delay> hours." )
sm.game.bindChatCommand( "/stopraid", {}, "cl_onChatCommand", "Cancel all incoming raids" )
sm.game.bindChatCommand( "/disableraids", { { "bool", "enabled", false } }, "cl_onChatCommand", "Disable raids if true" )
sm.game.bindChatCommand( "/camera", {}, "cl_onChatCommand", "Spawn a SplineCamera tool" )
sm.game.bindChatCommand( "/noaggro", { { "bool", "enable", true } }, "cl_onChatCommand", "Toggles the player as a target" )
sm.game.bindChatCommand( "/killall", {}, "cl_onChatCommand", "Kills all spawned units" )
sm.game.bindChatCommand( "/printglobals", {}, "cl_onChatCommand", "Print all global lua variables" )
sm.game.bindChatCommand( "/clearpathnodes", {}, "cl_onChatCommand", "Clear all path nodes in overworld" )
sm.game.bindChatCommand( "/enablepathpotatoes", { { "bool", "enable", true } }, "cl_onChatCommand", "Creates path nodes at potato hits in overworld and links to previous node" )
sm.game.bindChatCommand( "/activatequest", { { "string", "name", true } }, "cl_onChatCommand", "Activate quest" )
sm.game.bindChatCommand( "/completequest", { { "string", "name", true } }, "cl_onChatCommand", "Complete quest" )
sm.game.bindChatCommand( "/settilebool", { { "string", "name", false }, { "bool", "value", false } }, "cl_onChatCommand", "Set named tile value at player position as a bool" )
sm.game.bindChatCommand( "/settilefloat", { { "string", "name", false }, { "number", "value", false } }, "cl_onChatCommand", "Set named tile value at player position as a floating point number" )
sm.game.bindChatCommand( "/settilestring", { { "string", "name", false }, { "string", "value", false } }, "cl_onChatCommand", "Set named tile value at player position as a bool" )
sm.game.bindChatCommand( "/printtilevalues", {}, "cl_onChatCommand", "Print all tile values at player position" )
sm.game.bindChatCommand( "/reloadcell", {{ "int", "x", true }, { "int", "y", true }}, "cl_onChatCommand", "Reload cells at self or {x,y}" )
sm.game.bindChatCommand( "/tutorialstartkit", {}, "cl_onChatCommand", "Spawn a starter kit for building a scrap car" )
--custom
sm.game.bindChatCommand( "/cell", { { "string", "x,y,z", false } }, "cl_onChatCommand", "Teleport to numerical cell position" )
sm.game.bindChatCommand( "/tp", { { "string", "x,y,z", false } }, "cl_onChatCommand", "Teleport to numerical position" )
sm.game.bindChatCommand( "/dir", { }, "cl_onChatCommand", "Tell you direction vector you are looking at." )
end
end
function SurvivalGame.client_onClientDataUpdate( self, clientData, channel )
if channel == 2 then
self.cl.time = clientData.time
elseif channel == 1 then
g_survivalDev = clientData.dev
self:bindChatCommands()
end
end
function SurvivalGame.loadCraftingRecipes( self )
LoadCraftingRecipes({
workbench = "$SURVIVAL_DATA/CraftingRecipes/workbench.json",
dispenser = "$SURVIVAL_DATA/CraftingRecipes/dispenser.json",
cookbot = "$SURVIVAL_DATA/CraftingRecipes/cookbot.json",
craftbot = "$SURVIVAL_DATA/CraftingRecipes/craftbot.json",
dressbot = "$SURVIVAL_DATA/CraftingRecipes/dressbot.json"
})
end
function SurvivalGame.server_onFixedUpdate( self, timeStep )
-- Update time
local prevTime = self.sv.time.timeOfDay
if self.sv.time.timeProgress then
self.sv.time.timeOfDay = self.sv.time.timeOfDay + timeStep / DAYCYCLE_TIME
end
local newDay = self.sv.time.timeOfDay >= 1.0
if newDay then
self.sv.time.timeOfDay = math.fmod( self.sv.time.timeOfDay, 1 )
end
if self.sv.time.timeOfDay >= DAYCYCLE_DAWN and prevTime < DAYCYCLE_DAWN then
g_unitManager:sv_initNewDay()
end
-- Ambush
--if not g_survivalDev then
-- for _,ambush in ipairs( AMBUSHES ) do
-- if self.sv.time.timeOfDay >= ambush.time and ( prevTime < ambush.time or newDay ) then
-- self:sv_ambush( { magnitude = ambush.magnitude, wave = ambush.wave } )
-- end
-- end
--end
-- Client and save sync
self.sv.syncTimer:tick()
if self.sv.syncTimer:done() then
self.sv.syncTimer:start( SyncInterval )
sm.storage.save( STORAGE_CHANNEL_TIME, self.sv.time )
self:sv_updateClientData()
end
g_elevatorManager:sv_onFixedUpdate()
g_unitManager:sv_onFixedUpdate()
if g_eventManager then
g_eventManager:sv_onFixedUpdate()
end
end
function SurvivalGame.sv_updateClientData( self )
self.network:setClientData( { time = self.sv.time }, 2 )
end
function SurvivalGame.client_onUpdate( self, dt )
-- Update time
if self.cl.time.timeProgress then
self.cl.time.timeOfDay = math.fmod( self.cl.time.timeOfDay + dt / DAYCYCLE_TIME, 1.0 )
end
sm.game.setTimeOfDay( self.cl.time.timeOfDay )
-- Update lighting values
local index = 1
while index < #DAYCYCLE_LIGHTING_TIMES and self.cl.time.timeOfDay >= DAYCYCLE_LIGHTING_TIMES[index + 1] do
index = index + 1
end
assert( index <= #DAYCYCLE_LIGHTING_TIMES )
local light = 0.0
if index < #DAYCYCLE_LIGHTING_TIMES then
local p = ( self.cl.time.timeOfDay - DAYCYCLE_LIGHTING_TIMES[index] ) / ( DAYCYCLE_LIGHTING_TIMES[index + 1] - DAYCYCLE_LIGHTING_TIMES[index] )
light = sm.util.lerp( DAYCYCLE_LIGHTING_VALUES[index], DAYCYCLE_LIGHTING_VALUES[index + 1], p )
else
light = DAYCYCLE_LIGHTING_VALUES[index]
end
sm.render.setOutdoorLighting( light )
end
function SurvivalGame.client_showMessage( self, msg )
sm.gui.chatMessage( msg )
end
function SurvivalGame.cl_onChatCommand( self, params )
local unitSpawnNames =
{
woc = unit_woc,
tapebot = unit_tapebot,
tb = unit_tapebot,
redtapebot = unit_tapebot_red,
rtb = unit_tapebot_red,
totebot = unit_totebot_green,
green = unit_totebot_green,
t = unit_totebot_green,
totered = unit_totebot_red,
red = unit_totebot_red,
tr = unit_totebot_red,
haybot = unit_haybot,
h = unit_haybot,
worm = unit_worm,
farmbot = unit_farmbot,
f = unit_farmbot,
}
if params[1] == "/ammo" then
self.network:sendToServer( "sv_giveItem", { player = sm.localPlayer.getPlayer(), item = obj_plantables_potato, quantity = ( params[2] or 50 ) } )
elseif params[1] == "/spudgun" then
self.network:sendToServer( "sv_giveItem", { player = sm.localPlayer.getPlayer(), item = tool_spudgun, quantity = 1 } )
elseif params[1] == "/gatling" then
self.network:sendToServer( "sv_giveItem", { player = sm.localPlayer.getPlayer(), item = tool_gatling, quantity = 1 } )
elseif params[1] == "/shotgun" then
self.network:sendToServer( "sv_giveItem", { player = sm.localPlayer.getPlayer(), item = tool_shotgun, quantity = 1 } )
elseif params[1] == "/sunshake" then
self.network:sendToServer( "sv_giveItem", { player = sm.localPlayer.getPlayer(), item = obj_consumable_sunshake, quantity = 1 } )
elseif params[1] == "/baguette" then
self.network:sendToServer( "sv_giveItem", { player = sm.localPlayer.getPlayer(), item = obj_consumable_longsandwich, quantity = 1 } )
elseif params[1] == "/keycard" then
self.network:sendToServer( "sv_giveItem", { player = sm.localPlayer.getPlayer(), item = obj_survivalobject_keycard, quantity = 1 } )
elseif params[1] == "/camera" then
self.network:sendToServer( "sv_giveItem", { player = sm.localPlayer.getPlayer(), item = sm.uuid.new( "5bbe87d3-d60a-48b5-9ca9-0086c80ebf7f" ), quantity = 1 } )
elseif params[1] == "/powercore" then
self.network:sendToServer( "sv_giveItem", { player = sm.localPlayer.getPlayer(), item = obj_survivalobject_powercore, quantity = 1 } )
elseif params[1] == "/components" then
self.network:sendToServer( "sv_giveItem", { player = sm.localPlayer.getPlayer(), item = obj_consumable_component, quantity = ( params[2] or 10 ) } )
elseif params[1] == "/glowsticks" then
self.network:sendToServer( "sv_giveItem", { player = sm.localPlayer.getPlayer(), item = obj_consumable_glowstick, quantity = ( params[2] or 10 ) } )
elseif params[1] == "/god" then
self.network:sendToServer( "sv_switchGodMode" )
elseif params[1] == "/encrypt" then
self.network:sendToServer( "sv_enableRestrictions", true )
elseif params[1] == "/decrypt" then
self.network:sendToServer( "sv_enableRestrictions", false )
elseif params[1] == "/unlimited" then
self.network:sendToServer( "sv_setLimitedInventory", false )
elseif params[1] == "/limited" then
self.network:sendToServer( "sv_setLimitedInventory", true )
elseif params[1] == "/ambush" then
self.network:sendToServer( "sv_ambush", { magnitude = params[2] or 1, wave = params[3] } )
elseif params[1] == "/recreate" then
self.network:sendToServer( "sv_recreateWorld", sm.localPlayer.getPlayer() )
elseif params[1] == "/timeofday" then
self.network:sendToServer( "sv_setTimeOfDay", params[2] )
elseif params[1] == "/timeprogress" then
self.network:sendToServer( "sv_setTimeProgress", params[2] )
elseif params[1] == "/day" then
self.network:sendToServer( "sv_setTimeOfDay", 0.5 )
self.network:sendToServer( "sv_setTimeProgress", false )
elseif params[1] == "/die" then
self.network:sendToServer( "sv_killPlayer", { player = sm.localPlayer.getPlayer() })
elseif params[1] == "/spawn" then
local rayCastValid, rayCastResult = sm.localPlayer.getRaycast( 100 )
if rayCastValid then
local spawnParams = {
uuid = sm.uuid.getNil(),
world = sm.localPlayer.getPlayer().character:getWorld(),
position = rayCastResult.pointWorld,
yaw = 0.0,
amount = 1
}
if unitSpawnNames[params[2]] then
spawnParams.uuid = unitSpawnNames[params[2]]
else
spawnParams.uuid = sm.uuid.new( params[2] )
end
if params[3] then
spawnParams.amount = params[3]
end
self.network:sendToServer( "sv_spawnUnit", spawnParams )
end
elseif params[1] == "/harvestable" then
local character = sm.localPlayer.getPlayer().character
if character then
local harvestableUuid = sm.uuid.getNil()
if params[2] == "tree" then
harvestableUuid = sm.uuid.new( "c4ea19d3-2469-4059-9f13-3ddb4f7e0b79" )
elseif params[2] == "stone" then
harvestableUuid = sm.uuid.new( "0d3362ae-4cb3-42ae-8a08-d3f9ed79e274" )
elseif params[2] == "soil" then
harvestableUuid = hvs_soil
elseif params[2] == "fencelong" then
harvestableUuid = sm.uuid.new( "c0f19413-6d8e-4b20-819a-949553242259" )
elseif params[2] == "fenceshort" then
harvestableUuid = sm.uuid.new( "144b5e79-483e-4da6-86ab-c575d0fdcd11" )
elseif params[2] == "fencecorner" then
harvestableUuid = sm.uuid.new( "ead875db-59d0-45f5-861e-b3075e1f8434" )
elseif params[2] == "beehive" then
harvestableUuid = hvs_farmables_beehive
elseif params[2] == "cotton" then
harvestableUuid = hvs_farmables_cottonplant
elseif params[2] then
harvestableUuid = sm.uuid.new( params[2] )
end
local spawnParams = { world = character:getWorld(), uuid = harvestableUuid, position = character.worldPosition, quat = sm.vec3.getRotation( sm.vec3.new( 0, 1, 0 ), sm.vec3.new( 0, 0, 1 ) ) }
self.network:sendToServer( "sv_spawnHarvestable", spawnParams )
end
elseif params[1] == "/cleardebug" then
sm.debugDraw.clear()
elseif params[1] == "/import" then
local rayCastValid, rayCastResult = sm.localPlayer.getRaycast( 100 )
if rayCastValid then
local importParams = {
world = sm.localPlayer.getPlayer().character:getWorld(),
name = params[2],
position = rayCastResult.pointWorld
}
self.network:sendToServer( "sv_importCreation", importParams )
end
elseif params[1] == "/noaggro" then
if type( params[2] ) == "boolean" then
self.network:sendToServer( "sv_n_switchAggroMode", { aggroMode = not params[2] } )
else
self.network:sendToServer( "sv_n_switchAggroMode", { aggroMode = not sm.game.getEnableAggro() } )
end
elseif params[1] == "/reloadcell" then
local world = sm.localPlayer.getPlayer():getCharacter():getWorld()
local player = sm.localPlayer.getPlayer()
local pos = player.character:getWorldPosition();
local x = params[2] or math.floor( pos.x / 64 )
local y = params[3] or math.floor( pos.y / 64 )
self.network:sendToServer( "sv_reloadCell", { x = x, y = y, world = world, player = player } )
else
self.network:sendToServer( "sv_onChatCommand", params )
end
end
function SurvivalGame.sv_reloadCell( self, params, player )
print( "sv_reloadCell Reloading cell at {" .. params.x .. " : " .. params.y .. "}" )
self.sv.saved.overworld:loadCell( params.x, params.y, player )
self.network:sendToClients( "cl_reloadCell", params )
end
function SurvivalGame.cl_reloadCell( self, params )
print( "cl_reloadCell reloading " .. params.x .. " : " .. params.y )
for x = -2, 2 do
for y = -2, 2 do
params.world:reloadCell( params.x+x, params.y+y, "cl_reloadCellTestCallback" )
end
end
end
function SurvivalGame.cl_reloadCellTestCallback( self, world, x, y, result )
print( "cl_reloadCellTestCallback" )
print( "result = " .. result )
end
function SurvivalGame.sv_giveItem( self, params )
sm.container.beginTransaction()
sm.container.collect( params.player:getInventory(), params.item, params.quantity, false )
sm.container.endTransaction()
end
function SurvivalGame.cl_n_onJoined( self, params )
self.cl.playIntroCinematic = params.newPlayer
end
function SurvivalGame.client_onLoadingScreenLifted( self )
g_effectManager:cl_onLoadingScreenLifted()
self.network:sendToServer( "sv_n_loadingScreenLifted" )
if self.cl.playIntroCinematic then
local callbacks = {}
callbacks[#callbacks + 1] = { fn = "cl_onCinematicEvent", params = { cinematicName = "cinematic.survivalstart01" }, ref = self }
g_effectManager:cl_playNamedCinematic( "cinematic.survivalstart01", callbacks )
end
end
function SurvivalGame.sv_n_loadingScreenLifted( self, _, player )
if not g_survivalDev then
QuestManager.Sv_TryActivateQuest( "quest_tutorial" )
end
end
function SurvivalGame.cl_onCinematicEvent( self, eventName, params )
local myPlayer = sm.localPlayer.getPlayer()
local myCharacter = myPlayer and myPlayer.character or nil
if eventName == "survivalstart01.dramatics_standup" then
if sm.exists( myCharacter ) then
sm.event.sendToCharacter( myCharacter, "cl_e_onEvent", "dramatics_standup" )
end
elseif eventName == "survivalstart01.fadeout" then
sm.event.sendToPlayer( myPlayer, "cl_e_startFadeToBlack", { duration = IntroFadeDuration, timeout = IntroFadeTimeout } )
elseif eventName == "survivalstart01.fadein" then
sm.event.sendToPlayer( myPlayer, "cl_n_endFadeToBlack", { duration = IntroEndFadeDuration } )
end
end
function SurvivalGame.sv_switchGodMode( self )
g_godMode = not g_godMode
self.network:sendToClients( "client_showMessage", "GODMODE: " .. ( g_godMode and "On" or "Off" ) )
end
function SurvivalGame.sv_n_switchAggroMode( self, params )
sm.game.setEnableAggro(params.aggroMode )
self.network:sendToClients( "client_showMessage", "AGGRO: " .. ( params.aggroMode and "On" or "Off" ) )
end
function SurvivalGame.sv_enableRestrictions( self, state )
sm.game.setEnableRestrictions( state )
self.network:sendToClients( "client_showMessage", ( state and "Restricted" or "Unrestricted" ) )
end
function SurvivalGame.sv_setLimitedInventory( self, state )
sm.game.setLimitedInventory( state )
self.network:sendToClients( "client_showMessage", ( state and "Limited inventory" or "Unlimited inventory" ) )
end
function SurvivalGame.sv_ambush( self, params )
if sm.exists( self.sv.saved.overworld ) then
sm.event.sendToWorld( self.sv.saved.overworld, "sv_ambush", params )
end
end
function SurvivalGame.sv_recreateWorld( self, player )
local character = player:getCharacter()
if character:getWorld() == self.sv.saved.overworld then
self.sv.saved.overworld:destroy()
self.sv.saved.overworld = sm.world.createWorld( "$SURVIVAL_DATA/Scripts/game/worlds/Overworld.lua", "Overworld", { dev = g_survivalDev }, self.sv.saved.data.seed )
self.storage:save( self.sv.saved )
local params = { pos = character:getWorldPosition(), dir = character:getDirection() }
self.sv.saved.overworld:loadCell( math.floor( params.pos.x/64 ), math.floor( params.pos.y/64 ), player, "sv_recreatePlayerCharacter", params )
self.network:sendToClients( "client_showMessage", "Recreating world" )
else
self.network:sendToClients( "client_showMessage", "Recreate world only allowed for overworld" )
end
end
function SurvivalGame.sv_setTimeOfDay( self, timeOfDay )
if timeOfDay then
self.sv.time.timeOfDay = timeOfDay
self.sv.syncTimer.count = self.sv.syncTimer.ticks -- Force sync
end
self.network:sendToClients( "client_showMessage", ( "Time of day set to "..self.sv.time.timeOfDay ) )
end
function SurvivalGame.sv_setTimeProgress( self, timeProgress )
if timeProgress ~= nil then
self.sv.time.timeProgress = timeProgress
self.sv.syncTimer.count = self.sv.syncTimer.ticks -- Force sync
end
self.network:sendToClients( "client_showMessage", ( "Time scale set to "..( self.sv.time.timeProgress and "on" or "off ") ) )
end
function SurvivalGame.sv_killPlayer( self, params )
params.damage = 9999
sm.event.sendToPlayer( params.player, "sv_e_receiveDamage", params )
end
function SurvivalGame.sv_spawnUnit( self, params )
sm.event.sendToWorld( params.world, "sv_e_spawnUnit", params )
end
function SurvivalGame.sv_spawnHarvestable( self, params )
sm.event.sendToWorld( params.world, "sv_spawnHarvestable", params )
end
function SurvivalGame.sv_importCreation( self, params )
sm.creation.importFromFile( params.world, "$SURVIVAL_DATA/LocalBlueprints/"..params.name..".blueprint", params.position )
end
function SurvivalGame.sv_onChatCommand( self, params, player )
if params[1] == "/tumble" then
if params[2] ~= nil then
player.character:setTumbling( params[2] )
else
player.character:setTumbling( not player.character:isTumbling() )
end
if player.character:isTumbling() then
self.network:sendToClients( "client_showMessage", "Player is tumbling" )
else
self.network:sendToClients( "client_showMessage", "Player is not tumbling" )
end
elseif params[1] == "/sethp" then
sm.event.sendToPlayer( player, "sv_e_debug", { hp = params[2] } )
elseif params[1] == "/setwater" then
sm.event.sendToPlayer( player, "sv_e_debug", { water = params[2] } )
elseif params[1] == "/setfood" then
sm.event.sendToPlayer( player, "sv_e_debug", { food = params[2] } )
elseif params[1] == "/goto" then
local pos
if params[2] == "here" then
pos = player.character:getWorldPosition()
elseif params[2] == "start" then
pos = START_AREA_SPAWN_POINT
else
self.network:sendToClient( player, "client_showMessage", "Unknown place" )
end
if pos then
local cellX, cellY = math.floor( pos.x/64 ), math.floor( pos.y/64 )
if not sm.exists( self.sv.saved.overworld ) then
sm.world.loadWorld( self.sv.saved.overworld )
end
self.sv.saved.overworld:loadCell( cellX, cellY, player, "sv_recreatePlayerCharacter", { pos = pos, dir = player.character:getDirection() } )
end
elseif params[1] == "/respawn" then
sm.event.sendToPlayer( player, "sv_e_respawn" )
elseif params[1] == "/activatequest" then
local questName = params[2]
if questName then
QuestManager.Sv_ActivateQuest( questName )
end
elseif params[1] == "/completequest" then
local questName = params[2]
if questName then
QuestManager.Sv_CompleteQuest( questName )
end
elseif params[1] == "/cell" then
local pos
if params[2] == "here" then
pos = player.character:getWorldPosition()
local cellX, cellY = math.floor( pos.x/64 ), math.floor( pos.y/64 )
print("x:"..cellX.." y:"..cellY)
self.network:sendToClient( player, "client_showMessage", "x:"..cellX.." y:"..cellY.." z:"..math.floor(pos.z) )
--self.network:sendToClient( player, "client_showMessage", "looking x:"..dir.x.." y:"..dir.y.." z:"..dir.z )
return
else
if params[2] ~= nil then
local cellX,cellY,z = params[2]:match("([^,]+),([^,]+),([^,]+)")
local x = cellX * 64
local y = cellY * 64
x = x + 32
y = y + 32
pos = sm.vec3.new( tonumber(x),tonumber(y),tonumber(z) )
else
self.network:sendToClient( player, "client_showMessage", "Usage: /tp x,y,z or /tp here to see current coords" )
end
end
if pos then
local cellX, cellY = math.floor( pos.x/64 ), math.floor( pos.y/64 )
print("x:"..cellX.." y:"..cellY)
local dir = player.character:getDirection()
dir.x = 0
dir.y = 1
dir.z = -1
self.sv.saved.overworld:loadCell( cellX, cellY, player, "sv_recreatePlayerCharacter", { pos = pos, dir = dir } )
end
elseif params[1] == "/tp" then
local pos
if params[2] == "here" then
pos = player.character:getWorldPosition()
local dir = player.character:getDirection()
self.network:sendToClient( player, "client_showMessage", "x:"..math.floor(pos.x).." y:"..math.floor(pos.y).." z:"..math.floor(pos.z) )
--self.network:sendToClient( player, "client_showMessage", "looking x:"..dir.x.." y:"..dir.y.." z:"..dir.z )
return
elseif params[2] == "cell" then
pos = player.character:getWorldPosition()
local cellX, cellY = math.floor( pos.x/64 ), math.floor( pos.y/64 )
self.network:sendToClient( player, "client_showMessage", "x:"..cellX.." y:"..cellY )
--self.network:sendToClient( player, "client_showMessage", "looking x:"..dir.x.." y:"..dir.y.." z:"..dir.z )
return
else
if params[2] ~= nil then
local x,y,z = params[2]:match("([^,]+),([^,]+),([^,]+)")
pos = sm.vec3.new( tonumber(x),tonumber(y),tonumber(z) )
else
self.network:sendToClient( player, "client_showMessage", "Usage: /tp x,y,z or /tp here to see current coords" )
end
end
if pos then
local cellX, cellY = math.floor( pos.x/64 ), math.floor( pos.y/64 )
print("x:"..cellX.." y:"..cellY)
local dir = player.character:getDirection()
dir.x = 0
dir.y = 1
dir.z = -1
self.sv.saved.overworld:loadCell( cellX, cellY, player, "sv_recreatePlayerCharacter", { pos = pos, dir = dir } )
end
elseif params[1] == "/cell" then
local pos
if params[2] == "here" then
pos = player.character:getWorldPosition()
local cellX, cellY = math.floor( pos.x/64 ), math.floor( pos.y/64 )
print("x:"..cellX.." y:"..cellY)
self.network:sendToClient( player, "client_showMessage", "x:"..cellX.." y:"..cellY.." z:"..math.floor(pos.z) )
--self.network:sendToClient( player, "client_showMessage", "looking x:"..dir.x.." y:"..dir.y.." z:"..dir.z )
return
else
if params[2] ~= nil then
local cellX,cellY,z = params[2]:match("([^,]+),([^,]+),([^,]+)")
local x = cellX * 64
local y = cellY * 64
x = x + 32
y = y + 32
pos = sm.vec3.new( tonumber(x),tonumber(y),tonumber(z) )
else
self.network:sendToClient( player, "client_showMessage", "Usage: /tp x,y,z or /tp here to see current coords" )
end
end
if pos then
local cellX, cellY = math.floor( pos.x/64 ), math.floor( pos.y/64 )
print("x:"..cellX.." y:"..cellY)
local dir = player.character:getDirection()
dir.x = 0
dir.y = 1
dir.z = -1
self.sv.saved.overworld:loadCell( cellX, cellY, player, "sv_recreatePlayerCharacter", { pos = pos, dir = dir } )
end
elseif params[1] == "/tp" then
local pos
if params[2] == "here" then
pos = player.character:getWorldPosition()
local dir = player.character:getDirection()
self.network:sendToClient( player, "client_showMessage", "x:"..math.floor(pos.x).." y:"..math.floor(pos.y).." z:"..math.floor(pos.z) )
--self.network:sendToClient( player, "client_showMessage", "looking x:"..dir.x.." y:"..dir.y.." z:"..dir.z )
return
elseif params[2] == "cell" then
pos = player.character:getWorldPosition()
local cellX, cellY = math.floor( pos.x/64 ), math.floor( pos.y/64 )
self.network:sendToClient( player, "client_showMessage", "x:"..cellX.." y:"..cellY )
--self.network:sendToClient( player, "client_showMessage", "looking x:"..dir.x.." y:"..dir.y.." z:"..dir.z )
return
else
if params[2] ~= nil then
local x,y,z = params[2]:match("([^,]+),([^,]+),([^,]+)")
pos = sm.vec3.new( tonumber(x),tonumber(y),tonumber(z) )
else
self.network:sendToClient( player, "client_showMessage", "Usage: /tp x,y,z or /tp here to see current coords" )
end
end
if pos then
local cellX, cellY = math.floor( pos.x/64 ), math.floor( pos.y/64 )
print("x:"..cellX.." y:"..cellY)
local dir = player.character:getDirection()
dir.x = 0
dir.y = 1
dir.z = -1
self.sv.saved.overworld:loadCell( cellX, cellY, player, "sv_recreatePlayerCharacter", { pos = pos, dir = dir } )
end
else
params.player = player
if sm.exists( player.character ) then
sm.event.sendToWorld( player.character:getWorld(), "sv_e_onChatCommand", params )
end
end
end
function SurvivalGame.server_onPlayerJoined( self, player, newPlayer )
print( player.name, "joined the game" )
if newPlayer then --Player is first time joiners
local inventory = player:getInventory()
sm.container.beginTransaction()
if g_survivalDev then
--Hotbar
sm.container.setItem( inventory, 0, tool_sledgehammer, 1 )
sm.container.setItem( inventory, 1, tool_spudgun, 1 )
sm.container.setItem( inventory, 7, obj_plantables_potato, 50 )
sm.container.setItem( inventory, 8, tool_lift, 1 )
sm.container.setItem( inventory, 9, tool_connect, 1 )
--Actual inventory
sm.container.setItem( inventory, 10, tool_paint, 1 )
sm.container.setItem( inventory, 11, tool_weld, 1 )
else
sm.container.setItem( inventory, 0, tool_sledgehammer, 1 )
sm.container.setItem( inventory, 1, tool_lift, 1 )
end
sm.container.endTransaction()
local spawnPoint = g_survivalDev and SURVIVAL_DEV_SPAWN_POINT or START_AREA_SPAWN_POINT
if not sm.exists( self.sv.saved.overworld ) then
sm.world.loadWorld( self.sv.saved.overworld )
end
self.sv.saved.overworld:loadCell( math.floor( spawnPoint.x/64 ), math.floor( spawnPoint.y/64 ), player, "sv_createNewPlayer" )
self.network:sendToClient( player, "cl_n_onJoined", { newPlayer = newPlayer } )
else
local inventory = player:getInventory()
local sledgehammerCount = sm.container.totalQuantity( inventory, tool_sledgehammer )
if sledgehammerCount == 0 then
sm.container.beginTransaction()
sm.container.collect( inventory, tool_sledgehammer, 1 )
sm.container.endTransaction()
elseif sledgehammerCount > 1 then
sm.container.beginTransaction()
sm.container.spend( inventory, tool_sledgehammer, sledgehammerCount - 1 )
sm.container.endTransaction()
end
local tool_lift_creative = sm.uuid.new( "5cc12f03-275e-4c8e-b013-79fc0f913e1b" )
local creativeLiftCount = sm.container.totalQuantity( inventory, tool_lift_creative )
if creativeLiftCount > 0 then
sm.container.beginTransaction()
sm.container.spend( inventory, tool_lift_creative, creativeLiftCount )
sm.container.endTransaction()
end
local liftCount = sm.container.totalQuantity( inventory, tool_lift )
if liftCount == 0 then
sm.container.beginTransaction()
sm.container.collect( inventory, tool_lift, 1 )
sm.container.endTransaction()
elseif liftCount > 1 then
sm.container.beginTransaction()
sm.container.spend( inventory, tool_lift, liftCount - 1 )
sm.container.endTransaction()
end
end
if player.id > 1 then --Too early for self. Questmanager is not created yet...
QuestManager.Sv_OnEvent( QuestEvent.PlayerJoined, { player = player } )
end
g_unitManager:sv_onPlayerJoined( player )
end
function SurvivalGame.server_onPlayerLeft( self, player )
print( player.name, "left the game" )
if player.id > 1 then
QuestManager.Sv_OnEvent( QuestEvent.PlayerLeft, { player = player } )
end
g_elevatorManager:sv_onPlayerLeft( player )
end
function SurvivalGame.sv_e_requestWarehouseRestrictions( self, params )
-- Send the warehouse restrictions to the world that asked
print("SurvivalGame.sv_e_requestWarehouseRestrictions")
-- Warehouse get
local warehouse = nil
if params.warehouseIndex then
warehouse = self.sv.warehouses[params.warehouseIndex]
end
if warehouse then
sm.event.sendToWorld( params.world, "server_updateRestrictions", warehouse.restrictions )
end
end
function SurvivalGame.sv_e_setWarehouseRestrictions( self, params )
-- Set the restrictions for this warehouse and propagate the restrictions to all floors
-- Warehouse get
local warehouse = nil
if params.warehouseIndex then
warehouse = self.sv.warehouses[params.warehouseIndex]
end
if warehouse then
for _, newRestrictionSetting in pairs( params.restrictions ) do
if warehouse.restrictions[newRestrictionSetting.name] then
warehouse.restrictions[newRestrictionSetting.name].state = newRestrictionSetting.state
else
warehouse.restrictions[newRestrictionSetting.name] = newRestrictionSetting
end
end
self.sv.warehouses[params.warehouseIndex] = warehouse
sm.storage.save( STORAGE_CHANNEL_WAREHOUSES, self.sv.warehouses )
for i, world in ipairs( warehouse.worlds ) do
if sm.exists( world ) then
sm.event.sendToWorld( world, "server_updateRestrictions", warehouse.restrictions )
end
end
end
end
function SurvivalGame.sv_e_createElevatorDestination( self, params )
print( "SurvivalGame.sv_e_createElevatorDestination" )
print( params )
-- Warehouse get or create
local warehouse
if params.warehouseIndex then
warehouse = self.sv.warehouses[params.warehouseIndex]
else
assert( params.name == "ELEVATOR_ENTRANCE" )
warehouse = {}
warehouse.test = params.test
warehouse.world = params.portal:getWorldA()
warehouse.worlds = {}
warehouse.exits = params.exits
warehouse.maxLevels = params.maxLevels
warehouse.index = #self.sv.warehouses + 1
warehouse.restrictions = { erasable = { name = "erasable", state = false }, connectable = { name = "connectable", state = false } }
self.sv.warehouses[#self.sv.warehouses + 1] = warehouse
sm.storage.save( STORAGE_CHANNEL_WAREHOUSES, self.sv.warehouses )
end
-- Level up
local level
if params.level then
if params.name == "ELEVATOR_UP" then
level = params.level + 1
elseif params.name == "ELEVATOR_DOWN" then
level = params.level - 1
elseif params.name == "ELEVATOR_EXIT" then
if #warehouse.exits > 0 then
for _,cell in ipairs( warehouse.exits ) do
if not sm.exists( warehouse.world ) then
sm.world.loadWorld( warehouse.world )
end
local name = params.name.." "..cell.x..","..cell.y
sm.portal.addWorldPortalHook( warehouse.world, name, params.portal )
print( "Added portal hook '"..name.."' in world "..warehouse.world.id )
g_elevatorManager:sv_loadBForPlayersInElevator( params.portal, warehouse.world, cell.x, cell.y )
end
else
sm.log.error( "No exit hint found, this elevator is going nowhere!" )
end
return
else
assert( false )
end
else
if params.name == "ELEVATOR_EXIT" then
level = warehouse.maxLevels
elseif params.name == "ELEVATOR_ENTRANCE" then
level = 1
else
end
end
-- Create warehouse world
local worldData = {}
worldData.level = level
worldData.warehouseIndex = warehouse.index
worldData.maxLevels = warehouse.maxLevels
local world = sm.world.createWorld( "$SURVIVAL_DATA/Scripts/game/worlds/WarehouseWorld.lua", "WarehouseWorld", worldData )
print( "Created WarehouseWorld "..world.id )
-- Use the same restrictions for the new floor as the other floors
warehouse.worlds[#warehouse.worlds+1] = world
if warehouse.restrictions then
sm.event.sendToWorld( world, "server_updateRestrictions", warehouse.restrictions )
end
-- Elevator portal hook
local name
if params.name == "ELEVATOR_UP" then
name = "ELEVATOR_DOWN"
elseif params.name == "ELEVATOR_DOWN" then
name = "ELEVATOR_UP"
else
name = params.name
end
sm.portal.addWorldPortalHook( world, name, params.portal )
print( "Added portal hook '"..name.."' in world "..world.id )
g_elevatorManager:sv_loadBForPlayersInElevator( params.portal, world, 0, 0 )
end
function SurvivalGame.sv_e_elevatorEvent( self, params )
print( "SurvivalGame.sv_e_elevatorEvent" )
print( params )
g_elevatorManager[params.fn]( g_elevatorManager, params )
end
function SurvivalGame.sv_createNewPlayer( self, world, x, y, player )
local params = { player = player, x = x, y = y }
sm.event.sendToWorld( self.sv.saved.overworld, "sv_spawnNewCharacter", params )
end
function SurvivalGame.sv_recreatePlayerCharacter( self, world, x, y, player, params )
local yaw = math.atan2( params.dir.y, params.dir.x ) - math.pi/2
local pitch = math.asin( params.dir.z )
local newCharacter = sm.character.createCharacter( player, self.sv.saved.overworld, params.pos, yaw, pitch )
player:setCharacter( newCharacter )
print( "Recreate character in new world" )
print( params )
end
function SurvivalGame.sv_e_respawn( self, params )
if params.player.character and sm.exists( params.player.character ) then
g_respawnManager:sv_requestRespawnCharacter( params.player )
else
local spawnPoint = g_survivalDev and SURVIVAL_DEV_SPAWN_POINT or START_AREA_SPAWN_POINT
if not sm.exists( self.sv.saved.overworld ) then
sm.world.loadWorld( self.sv.saved.overworld )
end
self.sv.saved.overworld:loadCell( math.floor( spawnPoint.x/64 ), math.floor( spawnPoint.y/64 ), params.player, "sv_createNewPlayer" )
end
end
function SurvivalGame.sv_loadedRespawnCell( self, world, x, y, player )
g_respawnManager:sv_respawnCharacter( player, world )
end
function SurvivalGame.sv_e_onSpawnPlayerCharacter( self, player )
if player.character and sm.exists( player.character ) then
g_respawnManager:sv_onSpawnCharacter( player )
g_beaconManager:sv_onSpawnCharacter( player )
else
sm.log.warning("SurvivalGame.sv_e_onSpawnPlayerCharacter for a character that doesn't exist")
end
end
function SurvivalGame.sv_e_markBag( self, params )
if sm.exists( params.world ) then
sm.event.sendToWorld( params.world, "sv_e_markBag", params )
else
sm.log.warning("SurvivalGame.sv_e_markBag in a world that doesn't exist")
end
end
function SurvivalGame.sv_e_unmarkBag( self, params )
if sm.exists( params.world ) then
sm.event.sendToWorld( params.world, "sv_e_unmarkBag", params )
else
sm.log.warning("SurvivalGame.sv_e_unmarkBag in a world that doesn't exist")
end
end
-- Beacons
function SurvivalGame.sv_e_createBeacon( self, params )
if sm.exists( params.beacon.world ) then
sm.event.sendToWorld( params.beacon.world, "sv_e_createBeacon", params )
else
sm.log.warning( "SurvivalGame.sv_e_createBeacon in a world that doesn't exist" )
end
end
function SurvivalGame.sv_e_destroyBeacon( self, params )
if sm.exists( params.beacon.world ) then
sm.event.sendToWorld( params.beacon.world, "sv_e_destroyBeacon", params )
else
sm.log.warning( "SurvivalGame.sv_e_destroyBeacon in a world that doesn't exist" )
end
end
function SurvivalGame.sv_e_unloadBeacon( self, params )
if sm.exists( params.beacon.world ) then
sm.event.sendToWorld( params.beacon.world, "sv_e_unloadBeacon", params )
else
sm.log.warning( "SurvivalGame.sv_e_unloadBeacon in a world that doesn't exist" )
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment