Skip to content

Commit

Permalink
Refactors jobspawning (#1180)
Browse files Browse the repository at this point in the history
* jobspawn code

* cleanup SendToLatejoin

* ai qol

* fix

* removes all but the clown and cyborg landmarks

* forgot 1 commit
  • Loading branch information
Kapu1178 authored Jan 2, 2025
1 parent 867c5e2 commit 4d84314
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 285 deletions.
264 changes: 31 additions & 233 deletions _maps/map_files/Theseus/Theseus.dmm

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions code/__DEFINES/jobs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,10 @@

#define FACTION_NONE "None"
#define FACTION_STATION "Station"

/// Spawn point is always fixed.
#define JOBSPAWN_FORCE_FIXED 0
/// Spawn point prefers a fixed spawnpoint, but can be a latejoin one.
#define JOBSPAWN_ALLOW_RANDOM 1
/// Spawn point is always a random spawnpoint.
#define JOBSPAWN_FORCE_RANDOM 2
8 changes: 5 additions & 3 deletions code/controllers/subsystem/job.dm
Original file line number Diff line number Diff line change
Expand Up @@ -758,21 +758,23 @@ SUBSYSTEM_DEF(job)
if(buckle && isliving(joining_mob))
buckle_mob(joining_mob, FALSE, FALSE)

/// Send an existing mob to their latejoin spawnpoint. Returns FALSE if it couldn't find a proper one, and resorted to the last resort.
/datum/controller/subsystem/job/proc/SendToLateJoin(mob/M, buckle = TRUE)
var/atom/destination

if(M.mind && !is_unassigned_job(M.mind.assigned_role) && length(GLOB.high_priority_spawns)) //We're doing something special today.
destination = pick(GLOB.high_priority_spawns[M.mind.assigned_role.title])
if(M.mind?.assigned_role && !is_unassigned_job(M.mind.assigned_role)) //We're doing something special today.
destination = M.mind.assigned_role.get_latejoin_spawn_point()
destination.JoinPlayerHere(M, FALSE)
return TRUE

if(latejoin_trackers.len)
if(length(latejoin_trackers))
destination = pick(latejoin_trackers)
destination.JoinPlayerHere(M, buckle)
return TRUE

destination = get_last_resort_spawn_points()
destination.JoinPlayerHere(M, buckle)
return FALSE


/datum/controller/subsystem/job/proc/get_last_resort_spawn_points()
Expand Down
21 changes: 11 additions & 10 deletions code/controllers/subsystem/ticker.dm
Original file line number Diff line number Diff line change
Expand Up @@ -357,8 +357,6 @@ SUBSYSTEM_DEF(ticker)
qdel(bomb)

/datum/controller/subsystem/ticker/proc/create_characters()
var/list/spawn_spots = SSjob.latejoin_trackers.Copy()
var/list/spawn_spots_reload = spawn_spots.Copy() //In case we run out, we need something to reload from.
for(var/mob/dead/new_player/player as anything in GLOB.new_player_list)
if(!player.mind)
//New player has logged out.
Expand All @@ -371,21 +369,24 @@ SUBSYSTEM_DEF(ticker)
if(PLAYER_READY_TO_PLAY)
GLOB.joined_player_list += player.ckey
var/atom/spawn_loc = player.mind.assigned_role.get_roundstart_spawn_point()
if(spawn_loc) //If we've been given an override, just take it and get out of here.
player.create_character(spawn_loc)
player.create_character(spawn_loc)

else //We haven't been passed an override destination. Give us the usual treatment.
if(!length(spawn_spots))
spawn_spots = spawn_spots_reload.Copy()

spawn_loc = pick_n_take(spawn_spots)
player.create_character(spawn_loc)
else //PLAYER_NOT_READY
//Reload their player panel so they see latejoin instead of ready.
player.npp.update()

CHECK_TICK

/// Returns a (probably) unused latejoin spawn point. Used by roundstart code to spread players out.
/datum/controller/subsystem/ticker/proc/get_random_spawnpoint()
var/static/list/spawnpoints
if(!length(spawnpoints))
if(length(SSjob.latejoin_trackers))
spawnpoints = SSjob.latejoin_trackers.Copy()
else
return SSjob.get_last_resort_spawn_points()
return pick_n_take(spawnpoints)

/datum/controller/subsystem/ticker/proc/collect_minds()
for(var/i in GLOB.new_player_list)
var/mob/dead/new_player/P = i
Expand Down
47 changes: 36 additions & 11 deletions code/modules/jobs/job_types/_job.dm
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,8 @@ GLOBAL_LIST_INIT(job_display_order, list(
/// Should this job be allowed to be picked for the bureaucratic error event?
var/allow_bureaucratic_error = TRUE

///Is this job affected by weird spawns like the ones from station traits
var/random_spawns_possible = TRUE
/// How this job decides where to spawn.
var/spawn_logic = JOBSPAWN_ALLOW_RANDOM

/// List of family heirlooms this job can get with the family heirloom quirk. List of types.
var/list/family_heirlooms
Expand Down Expand Up @@ -433,22 +433,45 @@ GLOBAL_LIST_INIT(job_display_order, list(
return "Due to extreme staffing shortages, newly promoted Acting Captain [captain.real_name] on deck!"


/// Returns either an atom the mob should spawn in, or null, if we have no special overrides.
/// Returns either an atom the mob should spawn on.
/datum/job/proc/get_roundstart_spawn_point()
if(random_spawns_possible)
return get_latejoin_spawn_point()
SHOULD_NOT_OVERRIDE(TRUE)

if(length(GLOB.high_priority_spawns[title]))
return pick(GLOB.high_priority_spawns[title])
if(spawn_logic == JOBSPAWN_FORCE_RANDOM)
return get_roundstart_spawn_point_random()

var/atom/spawn_point = get_roundstart_spawn_point_fixed()
if(isnull(spawn_point))
// That's okay, the map may not have any fixed spawnpoints for this job and this job allows that.
if(spawn_logic == JOBSPAWN_ALLOW_RANDOM)
return get_roundstart_spawn_point_random()

else // Something has gone horribly wrong
stack_trace("Something has gone very wrong. [type] could not find a job spawn location.")
return SSjob.get_last_resort_spawn_points()

return spawn_point


/// Returns a fixed spawn location to use. This is probably one of a few job landmarks.
/datum/job/proc/get_roundstart_spawn_point_fixed()
PROTECTED_PROC(TRUE)
return get_jobspawn_landmark()

return null //We don't care where we go. Let Ticker decide for us.
/// Returns a roundstart spawnpoint to use if spawn logic determined it should spawn at a "random" location.
/datum/job/proc/get_roundstart_spawn_point_random()
PROTECTED_PROC(TRUE)
return SSticker.get_random_spawnpoint()

/// Returns an unused jobspawn landmark. You CAN run out of landmarks, please be mindful of this!
/datum/job/proc/get_jobspawn_landmark()
SHOULD_NOT_OVERRIDE(TRUE)
RETURN_TYPE(/obj/effect/landmark/start)

/// Handles finding and picking a valid roundstart effect landmark spawn point, in case no uncommon different spawning events occur.
/datum/job/proc/get_default_roundstart_spawn_point()
var/obj/effect/landmark/start/spawnpoint = get_start_landmark_for(title)
if(!spawnpoint)
log_world("Couldn't find a round start spawn point for [title].")
return

spawnpoint.used = TRUE

Expand All @@ -474,7 +497,9 @@ GLOBAL_LIST_INIT(job_display_order, list(
else
spawn_instance = new spawn_type(player_client.mob.loc)
spawn_point.JoinPlayerHere(spawn_instance, TRUE)

spawn_instance.apply_prefs_job(player_client, src)

if(!player_client)
qdel(spawn_instance)
return // Disconnected while checking for the appearance ban.
Expand All @@ -483,7 +508,7 @@ GLOBAL_LIST_INIT(job_display_order, list(

/// Applies the preference options to the spawning mob, taking the job into account. Assumes the client has the proper mind.
/mob/living/proc/apply_prefs_job(client/player_client, datum/job/job)

return

/mob/living/carbon/human/apply_prefs_job(client/player_client, datum/job/job)
var/fully_randomize = GLOB.current_anonymous_theme || is_banned_from(player_client.ckey, "Appearance")
Expand Down
5 changes: 2 additions & 3 deletions code/modules/jobs/job_types/ai.dm
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
/datum/job_department/silicon,
)

random_spawns_possible = FALSE
spawn_logic = JOBSPAWN_FORCE_FIXED
job_flags = JOB_NEW_PLAYER_JOINABLE | JOB_EQUIP_RANK
var/do_special_check = TRUE

Expand All @@ -39,10 +39,9 @@
ai_spawn.log_current_laws()


/datum/job/ai/get_roundstart_spawn_point()
/datum/job/ai/get_roundstart_spawn_point_fixed()
return get_latejoin_spawn_point()


/datum/job/ai/get_latejoin_spawn_point()
var/list/primary_spawn_points = list() // Ideal locations.
var/list/secondary_spawn_points = list() // Fallback locations.
Expand Down
5 changes: 3 additions & 2 deletions code/modules/jobs/job_types/antagonists/nuclear_operative.dm
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
/datum/job/nuclear_operative
title = ROLE_NUCLEAR_OPERATIVE
spawn_logic = JOBSPAWN_FORCE_FIXED


/datum/job/nuclear_operative/get_roundstart_spawn_point()
return pick(GLOB.nukeop_start)
/datum/job/nuclear_operative/get_roundstart_spawn_point_fixed()
return get_latejoin_spawn_point()


/datum/job/nuclear_operative/get_latejoin_spawn_point()
Expand Down
2 changes: 1 addition & 1 deletion code/modules/jobs/job_types/cyborg.dm
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
departments_list = list(
/datum/job_department/silicon,
)
random_spawns_possible = FALSE
spawn_logic = JOBSPAWN_FORCE_FIXED
job_flags = JOB_NEW_PLAYER_JOINABLE | JOB_EQUIP_RANK


Expand Down
54 changes: 33 additions & 21 deletions code/modules/mob/living/silicon/ai/ai.dm
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@

///Command report cooldown
COOLDOWN_DECLARE(command_report_cd)
/// An image to add to client.images so the AI player can see their own eye sprite.
var/image/sense_of_self

/mob/living/silicon/ai/Initialize(mapload, datum/ai_laws/L, mob/target_ai)
. = ..()
Expand Down Expand Up @@ -870,32 +872,42 @@
/mob/living/silicon/ai/reset_perspective(atom/new_eye)
if(camera_light_on)
light_cameras()

if(istype(new_eye, /obj/machinery/camera))
current = new_eye
if(client)
if(ismovable(new_eye))
if(new_eye != GLOB.ai_camera_room_landmark)
end_multicam()
client.perspective = EYE_PERSPECTIVE
client.eye = new_eye
else

if(!client)
return

client.images -= sense_of_self

if(ismovable(new_eye))
if(new_eye != GLOB.ai_camera_room_landmark)
end_multicam()
if(isturf(loc))
if(eyeobj)
client.eye = eyeobj
client.perspective = EYE_PERSPECTIVE
else
client.eye = client.mob
client.perspective = MOB_PERSPECTIVE
else
client.perspective = EYE_PERSPECTIVE
client.eye = new_eye

else
end_multicam()
if(isturf(loc))
if(eyeobj)
client.eye = eyeobj
client.perspective = EYE_PERSPECTIVE
client.eye = loc
update_sight()
if(client.eye != src)
var/atom/AT = client.eye
AT.get_remote_view_fullscreens(src)
client.images += sense_of_self
else
client.eye = client.mob
client.perspective = MOB_PERSPECTIVE
else
clear_fullscreen("remote_view", 0)
client.perspective = EYE_PERSPECTIVE
client.eye = loc

update_sight()

if(client.eye != src)
var/atom/AT = client.eye
AT.get_remote_view_fullscreens(src)
else
clear_fullscreen("remote_view", 0)

/mob/living/silicon/ai/revive(full_heal = FALSE, admin_revive = FALSE)
. = ..()
Expand Down
5 changes: 4 additions & 1 deletion code/modules/mob/living/silicon/ai/freelook/eye.dm
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
// AI EYE
//
// An invisible (no icon) mob that the AI controls to look around the station with.
// An invisible mob that the AI controls to look around the station with.
// It streams chunks as it moves around, which will show it what the AI can and cannot see.
/mob/camera/ai_eye
name = "Inactive AI Eye"

icon_state = "ai_camera"
icon = 'icons/mob/cameramob.dmi'
plane = ABOVE_LIGHTING_PLANE
invisibility = INVISIBILITY_MAXIMUM
hud_possible = list(
AI_DETECT_HUD = HUD_LIST_LIST
Expand Down Expand Up @@ -190,6 +191,8 @@
eyeobj.set_real_name("[name] (AI Eye)")
set_eyeobj_visible(TRUE)

sense_of_self = image(eyeobj.icon, eyeobj, eyeobj.icon_state)

/mob/living/silicon/ai/proc/set_eyeobj_visible(state = TRUE)
if(!eyeobj)
return
Expand Down

0 comments on commit 4d84314

Please sign in to comment.