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

Refactors jobspawning #1180

Merged
merged 6 commits into from
Jan 2, 2025
Merged
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
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
Loading