Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adds crappy system for delayed/looping spawns #111

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 84 additions & 8 deletions code/modules/admin/game_master/game_master.dm
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@ GLOBAL_VAR_INIT(radio_communication_clarity, 100)
/// The amount of xenos to spawn in the spawn section
var/xeno_spawn_count = DEFAULT_XENO_AMOUNT_TO_SPAWN

/// The number of seconds between the placement of a xeno spawner and the moment of their actual spawning.
var/xeno_spawn_delay = 0

/// Whether new spawners will loop their spawns.
var/xeno_spawn_looping = FALSE

/// Whether newly placed spawners (including instant ones) will fail if placed near living, connected humans.
var/xeno_spawn_fail_human

/// If the spawned xeno is an AI in the spawn section
var/spawn_ai = TRUE

Expand Down Expand Up @@ -147,6 +156,25 @@ GLOBAL_VAR_INIT(radio_communication_clarity, 100)
data["spawn_ai"] = spawn_ai
data["spawn_click_intercept"] = spawn_click_intercept
data["xeno_spawn_count"] = xeno_spawn_count
data["xeno_spawn_delay"] = xeno_spawn_delay
data["xeno_spawn_looping"] = xeno_spawn_looping
data["xeno_spawn_fail_human"] = xeno_spawn_fail_human

if(length(GLOB.gm_xeno_spawners))
data["xeno_spawners"] = list()
for(var/datum/game_master_xeno_spawner/spawner as anything in GLOB.gm_xeno_spawners)
// list addition, gotta double wrap!
data["xeno_spawners"] += list(list(
"ref" = REF(spawner),
"spawn_type" = initial(spawner.spawn_type.name), // name's a little prettier
"spawn_count" = spawner.spawn_count,
"spawn_delay" = spawner.spawn_delay / (1 SECONDS),
"looping" = spawner.looping,
"fail_human" = spawner.fail_if_human_near,
"fail_range" = spawner.fail_range // they can't modify it, but nice to tell them about
))
else
data["xeno_spawners"] = ""

// Behavior stuff
data["selected_behavior"] = selected_behavior
Expand Down Expand Up @@ -186,31 +214,47 @@ GLOBAL_VAR_INIT(radio_communication_clarity, 100)
return

xeno_spawn_count = clamp(new_number, 1, 10)
return
return TRUE

if("set_xeno_spawn_delay")
var/new_delay = text2num(params["value"])
if(isnull(new_delay)) // obviously !new_delay won't work, because 0 is valid
return
// gotta set some kind of maximum
xeno_spawn_delay = round(clamp(new_delay, 0, 1 HOURS))
return TRUE

if("set_selected_xeno")
selected_xeno = params["new_xeno"]
xeno_spawn_count = DEFAULT_XENO_AMOUNT_TO_SPAWN
return
return TRUE

if("set_selected_hive")
selected_hive = params["new_hive"]
xeno_spawn_count = DEFAULT_XENO_AMOUNT_TO_SPAWN
return
return TRUE

if("xeno_spawn_ai_toggle")
spawn_ai = !spawn_ai
return
return TRUE

if("xeno_spawn_looping_toggle")
xeno_spawn_looping = !xeno_spawn_looping
return TRUE

if("xeno_spawn_fail_human_toggle")
xeno_spawn_fail_human = !xeno_spawn_fail_human
return TRUE

if("toggle_click_spawn")
if(spawn_click_intercept)
reset_click_overrides()
return
return TRUE

reset_click_overrides()
spawn_click_intercept = TRUE
current_click_intercept_action = SPAWN_CLICK_INTERCEPT_ACTION
return
return TRUE

if("delete_all_xenos")
if(tgui_alert(ui.user, "Do you want to delete all xenos?", "Confirmation", list("Yes", "No")) != "Yes")
Expand Down Expand Up @@ -273,6 +317,30 @@ GLOBAL_VAR_INIT(radio_communication_clarity, 100)
jumping_client.jump_to_turf(objective_turf)
return TRUE

if("jump_to_spawner")
if(!params["spawner_ref"])
return TRUE

var/datum/game_master_xeno_spawner/spawner = locate(params["spawner_ref"])
if(!spawner)
return TRUE
var/turf/spawner_turf = spawner.spawn_loc

var/client/jumping_client = ui.user.client
jumping_client.jump_to_turf(spawner_turf)
return TRUE

if("delete_spawner")
if(!params["spawner_ref"])
return TRUE

var/datum/game_master_xeno_spawner/spawner = locate(params["spawner_ref"])
if(!spawner)
return TRUE

qdel(spawner)
return TRUE

if("remove_objective")
if(!params["val"])
return
Expand Down Expand Up @@ -348,8 +416,16 @@ GLOBAL_VAR_INIT(radio_communication_clarity, 100)

var/turf/spawn_turf = get_turf(object)

for(var/i = 1 to xeno_spawn_count)
new spawning_xeno_type(spawn_turf, null, selected_hive, !spawn_ai)
new /datum/game_master_xeno_spawner(
spawn_turf,
spawning_xeno_type,
xeno_spawn_count,
(xeno_spawn_delay SECONDS),
xeno_spawn_looping,
selected_hive,
spawn_ai,
xeno_spawn_fail_human
)

return TRUE

Expand Down
103 changes: 103 additions & 0 deletions code/modules/admin/game_master/game_master_xeno_spawner.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
GLOBAL_LIST_EMPTY(gm_xeno_spawners)

/datum/game_master_xeno_spawner
/// The actual image holder that is added to game masters' clients
var/image/gm_image

/// The location the xenomorphs will spawn at.
var/turf/spawn_loc

/// The type of xeno to be spawned.
var/mob/living/carbon/xenomorph/spawn_type

/// The number of xenos to be spawned.
var/spawn_count

/// The delay until xenos are spawned (may be 0, in which case xenos spawn instantaneously).
var/spawn_delay

/// If TRUE, the spawner will not delete itself after firing.
var/looping

/// The hive the spawned xenos will belong to.
var/spawn_hive

/// Whether or not to spawn xenos with AI: if TRUE, the spawned xenos will have AI.
var/spawn_ai

/// If TRUE, the spawn will fail if any humans are within "fail_range" tiles.
/// This will cause the spawner to either delete itself or reset its spawn timer, depending on whether it loops.
var/fail_if_human_near

var/fail_range = 9

/datum/game_master_xeno_spawner/New(turf/s_loc, mob/living/carbon/xenomorph/s_type, s_count, s_delay, loop, s_hive, s_ai, fail_human)
GLOB.gm_xeno_spawners += src

spawn_loc = s_loc
spawn_type = s_type
spawn_count = s_count
spawn_delay = s_delay
looping = loop
spawn_hive = s_hive
spawn_ai = s_ai
fail_if_human_near = fail_human

// we can return early if we aren't waiting for anything
if(spawn_delay <= 0)
spawn_xenos()
return

addtimer(CALLBACK(src, PROC_REF(spawn_xenos)), spawn_delay)

// get the icon state for the xeno type we're trying to spawn
var/derived_icon_state = initial(spawn_type.mutation_type) + " " + initial(spawn_type.icon_state)
gm_image = new(
initial(spawn_type.icon),
spawn_loc,
derived_icon_state,
layer = ABOVE_FLY_LAYER,
)
// centers the xeno
gm_image.pixel_x = -(initial(spawn_type.icon_size) - 32)/2
// slightly more visible than observers
gm_image.alpha = 150
// color matrix. this makes them look sorta overexposed. janky, but it's more important that it looks distinguishable than good
gm_image.color = list(
1.5, 0, 0,
0, 1.5, 0,
0, 0, 1.5,
0.25, 0.25, 0.25
)

for(var/client/game_master in GLOB.game_masters)
game_master.images |= gm_image

/datum/game_master_xeno_spawner/Destroy(force, silent, ...)
. = ..()

GLOB.gm_xeno_spawners -= src

// cleans up the image
if(gm_image)
for(var/client/game_master in GLOB.game_masters)
game_master.images -= gm_image
QDEL_NULL(gm_image)

/datum/game_master_xeno_spawner/proc/spawn_xenos()
var/failure = FALSE

if(fail_if_human_near)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If fail_range exists and is not a null, fail_if_human_near may not be needed as the range already signals that it should do something within that range. Could maybe change the range instead of toggling the function, though in practice you probably would rarely want it to work like that.

for(var/mob/living/carbon/human as anything in GLOB.alive_human_list)
if(human.client && human.z == spawn_loc.z && (get_dist(human, spawn_loc) <= fail_range))
failure = TRUE
break

if(!failure)
for(var/i = 1 to spawn_count)
new spawn_type(spawn_loc, null, spawn_hive, !spawn_ai)

if(spawn_delay && looping)
addtimer(CALLBACK(src, PROC_REF(spawn_xenos)), spawn_delay)
else
qdel(src)
1 change: 1 addition & 0 deletions colonialmarines.dme
Original file line number Diff line number Diff line change
Expand Up @@ -1385,6 +1385,7 @@
#include "code\modules\admin\ToRban.dm"
#include "code\modules\admin\game_master\game_master.dm"
#include "code\modules\admin\game_master\game_master_submenu.dm"
#include "code\modules\admin\game_master\game_master_xeno_spawner.dm"
#include "code\modules\admin\game_master\extra_buttons\rappel_menu.dm"
#include "code\modules\admin\game_master\extra_buttons\rename_platoon.dm"
#include "code\modules\admin\game_master\extra_buttons\toggle_join_xeno.dm"
Expand Down
88 changes: 87 additions & 1 deletion tgui/packages/tgui/interfaces/GameMaster.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const GameMaster = (props, context) => {
const { data, act } = useBackend(context);

return (
<Window width={400} height={500}>
<Window width={650} height={500}>
<Window.Content scrollable>
<Stack direction="column" grow>
<GameMasterSpawningPanel />
Expand Down Expand Up @@ -63,6 +63,41 @@ export const GameMasterSpawningPanel = (props, context) => {
</Stack.Item>
</Stack>
</Stack.Item>
<Stack.Item>
<Stack>
<Stack.Item>Delay (s)</Stack.Item>
<Stack.Item>
<Button.Input
ml={1}
minWidth={6}
minHeight={1.5}
content={data.xeno_spawn_delay}
currentValue={data.xeno_spawn_delay}
onCommit={(e, value) => {
act('set_xeno_spawn_delay', { value });
}}
/>
</Stack.Item>
<Stack.Item>
<Button.Checkbox
checked={data.xeno_spawn_looping}
content="Looping"
onClick={() => {
act('xeno_spawn_looping_toggle');
}}
/>
</Stack.Item>
<Stack.Item>
<Button.Checkbox
checked={data.xeno_spawn_fail_human}
content="Fail spawn if humans nearby"
onClick={() => {
act('xeno_spawn_fail_human_toggle');
}}
/>
</Stack.Item>
</Stack>
</Stack.Item>
<Stack.Item>
<Stack>
<Stack.Item>
Expand Down Expand Up @@ -105,6 +140,57 @@ export const GameMasterSpawningPanel = (props, context) => {
</Stack.Item>
</Stack>
</Stack.Item>
{data.xeno_spawners && (
<Stack.Item>
<Collapsible title="Spawners">
<Stack vertical>
{data.xeno_spawners.map((spawner) => {
if (spawner) {
let spawner_ref = spawner.ref;
return (
<Stack.Item>
<Stack direction="column">
<Divider />
<Stack.Item>Caste: {spawner.spawn_type}</Stack.Item>
<Stack.Item>Count: {spawner.spawn_count}</Stack.Item>
<Stack.Item>Delay: {spawner.spawn_delay}s</Stack.Item>
<Stack.Item>
Looping: {spawner.looping ? 'True' : 'False'}
</Stack.Item>
<Stack.Item>
Fail if human within {spawner.fail_range}:{' '}
{spawner.fail_human ? 'True' : 'False'}
</Stack.Item>
<Stack.Item>
<Stack vertical>
<Stack.Item>
<Button
content="Jump To"
onClick={() => {
act('jump_to_spawner', { spawner_ref });
}}
/>
</Stack.Item>
<Stack.Item>
<Button
content="Delete"
onClick={() => {
act('delete_spawner', { spawner_ref });
}}
/>
</Stack.Item>
</Stack>
</Stack.Item>
</Stack>
</Stack.Item>
);
}
})}
<Divider />
</Stack>
</Collapsible>
</Stack.Item>
)}
</Stack>
</Section>
);
Expand Down
Loading