Files
Batrachophreno 62d3ee304b Lemurian Sea Hotfixes (#22694)
Tunes overmap spawns, hazard weighting, away site and offship exceptions
for the 8th time, admin logging, grav anom safeties for shuttles, etc.

<img width="425" height="245" alt="image"
src="https://github.com/user-attachments/assets/405a9887-9daf-4781-a9c3-2b837db09fbb"
/>

^ LOOKING AT YOU, TRAMP FREIGHTER
2026-06-13 22:18:36 +00:00

405 lines
14 KiB
Plaintext

/var/singleton/overmap_event_handler/overmap_event_handler = new()
/singleton/overmap_event_handler
var/list/hazard_by_turf
var/list/datum/event/ship_events
/singleton/overmap_event_handler/New()
..()
hazard_by_turf = list()
ship_events = list()
/singleton/overmap_event_handler/proc/create_events(var/z_level, var/overmap_size, var/number_of_events)
// Acquire the list of not-yet utilized overmap turfs on this Z-level
var/list/candidate_turfs = block(locate(OVERMAP_EDGE, OVERMAP_EDGE, z_level),locate(overmap_size - OVERMAP_EDGE, overmap_size - OVERMAP_EDGE,z_level))
candidate_turfs = where(candidate_turfs, /proc/can_not_locate, /obj/effect/overmap/visitable)
var/list/available_event_types = get_available_event_types()
if(!length(available_event_types))
return
for(var/i = 1 to number_of_events)
if(!candidate_turfs.len)
break
var/overmap_event_type = pick(available_event_types)
var/datum/overmap_event/datum_spawn = new overmap_event_type
var/list/event_turfs = acquire_event_turfs(datum_spawn.count, datum_spawn.radius, candidate_turfs, datum_spawn.continuous)
candidate_turfs -= event_turfs
for(var/event_turf in event_turfs)
var/type = pick(datum_spawn.hazards)
new type(event_turf)
qdel(datum_spawn)//idk help how do I do this better?
/singleton/overmap_event_handler/proc/get_available_event_types()
var/list/available_event_types = list()
for(var/overmap_event_type in subtypesof(/datum/overmap_event))
var/datum/overmap_event/datum_spawn = new overmap_event_type
if(datum_spawn.spawns_in_current_sector())
available_event_types += overmap_event_type
qdel(datum_spawn)
return available_event_types
/singleton/overmap_event_handler/proc/acquire_event_turfs(var/number_of_turfs, var/distance_from_origin, var/list/candidate_turfs, var/continuous = TRUE)
number_of_turfs = min(number_of_turfs, candidate_turfs.len)
candidate_turfs = candidate_turfs.Copy() // Not this proc's responsibility to adjust the given lists
var/origin_turf = pick(candidate_turfs)
var/list/selected_turfs = list(origin_turf)
var/list/selection_turfs = list(origin_turf)
candidate_turfs -= origin_turf
while(selection_turfs.len && selected_turfs.len < number_of_turfs)
var/selection_turf = pick(selection_turfs)
var/random_neighbour = get_random_neighbour(selection_turf, candidate_turfs, continuous, distance_from_origin)
if(random_neighbour)
candidate_turfs -= random_neighbour
selected_turfs += random_neighbour
if(get_dist(origin_turf, random_neighbour) < distance_from_origin)
selection_turfs += random_neighbour
else
selection_turfs -= selection_turf
return selected_turfs
/singleton/overmap_event_handler/proc/get_random_neighbour(var/turf/origin_turf, var/list/candidate_turfs, var/continuous = TRUE, var/range)
var/fitting_turfs
if(continuous)
fitting_turfs = origin_turf.CardinalTurfs(FALSE)
else
fitting_turfs = orange(range, origin_turf)
fitting_turfs = shuffle(fitting_turfs)
for(var/turf/T in fitting_turfs)
if(T in candidate_turfs)
return T
/singleton/overmap_event_handler/proc/start_hazard(var/obj/effect/overmap/visitable/ship/ship, var/obj/effect/overmap/event/hazard)//make these accept both hazards or events
if(!(ship in ship_events))
ship_events += ship
for(var/event_type in hazard.events)
if(is_event_active(ship,event_type, hazard.difficulty))//event's already active, don't bother
continue
var/datum/event_meta/EM = new(hazard.difficulty, "Overmap event - [hazard.name]", event_type, add_to_queue = FALSE, is_one_shot = TRUE)
var/datum/event/E = new event_type(EM, FALSE, ship, hazard)
LAZYADD(ship_events[ship], E)
/singleton/overmap_event_handler/proc/stop_hazard(var/obj/effect/overmap/visitable/ship/ship, var/obj/effect/overmap/event/hazard)
for(var/event_type in hazard.events)
var/datum/event/E = is_event_active(ship,event_type,hazard.difficulty)
if(E)
E.kill()
LAZYREMOVE(ship_events[ship], E)
/singleton/overmap_event_handler/proc/is_event_active(var/ship, var/event_type, var/severity)
if(!ship_events[ship])
return
var/list/active_ship_events = ship_events[ship]
for(var/datum/event/E as anything in active_ship_events)
if(E.type == event_type && E.severity == severity)
return E
/singleton/overmap_event_handler/proc/on_turf_entered(var/turf/new_loc, var/obj/effect/overmap/visitable/ship/ship, var/old_loc)
if(!istype(ship))
return
if(new_loc == old_loc)
return
for(var/obj/effect/overmap/event/E in hazard_by_turf[new_loc])
start_hazard(ship, E)
/singleton/overmap_event_handler/proc/on_turf_exited(var/turf/old_loc, var/obj/effect/overmap/visitable/ship/ship, var/new_loc)
if(!istype(ship))
return
if(new_loc == old_loc)
return
for(var/obj/effect/overmap/event/E in hazard_by_turf[old_loc])
if(is_event_included(hazard_by_turf[new_loc],E))
continue
stop_hazard(ship,E)
/singleton/overmap_event_handler/proc/update_hazards(var/turf/T)//catch all updater
if(!istype(T))
return
var/list/active_hazards = list()
for(var/obj/effect/overmap/event/E in T)
if(is_event_included(active_hazards, E, TRUE))
continue
active_hazards += E
if(!active_hazards.len)
hazard_by_turf -= T
GLOB.entered_event.unregister(T, src, PROC_REF(on_turf_entered))
GLOB.exited_event.unregister(T, src, PROC_REF(on_turf_exited))
else
hazard_by_turf |= T
hazard_by_turf[T] = active_hazards
GLOB.entered_event.register(T, src, PROC_REF(on_turf_entered))
GLOB.exited_event.register(T, src, PROC_REF(on_turf_exited))
for(var/obj/effect/overmap/visitable/ship/ship in T)
var/list/active_ship_events = ship_events[ship]
for(var/datum/event/E as anything in active_ship_events)
if(is_event_in_turf(E,T))
continue
E.kill()
LAZYREMOVE(ship_events[ship], E)
for(var/obj/effect/overmap/event/E in active_hazards)
start_hazard(ship,E)
/singleton/overmap_event_handler/proc/is_event_in_turf(var/datum/event/E, var/turf/T)
for(var/obj/effect/overmap/event/hazard in hazard_by_turf[T])
if((E in hazard.events) && E.severity == hazard.difficulty)
return TRUE
/singleton/overmap_event_handler/proc/is_event_included(var/list/hazards, var/obj/effect/overmap/event/E, var/equal_or_better)//this proc is only used so it can break out of 2 loops cleanly
for(var/obj/effect/overmap/event/A in hazards)
if(istype(A,E.type) || istype(E,A.type))
if(same_entries(A.events, E.events))
if(equal_or_better)
if(A.difficulty >= E.difficulty)
return TRUE
else
hazards -= A
else
if(A.difficulty == E.difficulty)
return TRUE
// We don't subtype /obj/effect/overmap/visitable because that'll create sections one can travel to
// And with them "existing" on the overmap Z-level things quickly get odd.
/obj/effect/overmap/event
name = "event"
icon = 'icons/obj/overmap/overmap_effects.dmi'
icon_state = "blank"
opacity = 0
var/list/events
var/list/event_icon_states = list("event")
var/difficulty = EVENT_LEVEL_MODERATE
/// Basically cached events on which Z level
var/list/victims
/// Can this event be destroyed by ship guns?
var/can_be_destroyed = TRUE
/// Events must be detected by sensors, but are otherwise instantly visible.
requires_contact = TRUE
instant_contact = TRUE
// Vars that determine movability, current moving direction, and moving speed
/// Whether this event can move or not
var/movable_event = FALSE
/// The percentage chance that this event will turn itself into a mobile version
var/movable_event_chance
/// In which direction this event is currently planning on moving, will select a random dir if null
var/moving_dir = null
/// How many times the event has to process before moving (2 seconds per).
/// This is automatically randomized to be 1/3 higher or lower on init, so not all events fire simultaneously.
var/movable_speed = 75
/// Ticks up each process until move speed is matched, at which point the event will move
var/move_counter = 0
/// Percentage chance that the event changes direction
var/dir_change_chance = 33
/// How long to delay the next move counter if there's a ship in our loc, this gives bad events some time to happen
var/ship_delay_time = 10
/// Ticks up each process until move speed is matched, at which point the event will move
var/ship_delay_counter = 0
/// Text that appears in the tooltip.
var/tooltip_text = "A hazard."
/obj/effect/overmap/event/Initialize()
. = ..()
icon_state = pick(event_icon_states)
overmap_event_handler.update_hazards(loc)
if(movable_event)
start_moving()
else if(prob(movable_event_chance))
make_movable()
/obj/effect/overmap/event/proc/make_movable()
// For a default movable_speed of 60, this could result in a movable speed of anywhere between 40 and 80 (ie. moves every ~1.5-2.5 minutes).
var/speed_randomness = movable_speed / 3
movable_speed = movable_speed + rand((0 - speed_randomness), (speed_randomness))
// Hee hee
if(prob(1))
movable_speed = initial(movable_speed) / 4
start_moving()
/obj/effect/overmap/event/proc/start_moving()
if(!moving_dir) moving_dir = pick(GLOB.alldirs)
START_PROCESSING(SSprocessing, src)
/obj/effect/overmap/event/process()
if(locate(/obj/effect/overmap/visitable/ship) in loc)
ship_delay_counter++
if(ship_delay_counter < ship_delay_time)
return
ship_delay_counter = 0
move_counter++
if(move_counter >= movable_speed)
handle_move()
move_counter = 0
/obj/effect/overmap/event/proc/handle_move()
Move(get_step(src, moving_dir))
if(prob(dir_change_chance))
moving_dir = turn(moving_dir, pick(45, -45))
/obj/effect/overmap/event/Bump(var/atom/A)
if(istype(A,/turf/unsimulated/map/edge))
handle_wraparound()
..()
/obj/effect/overmap/event/Move()
var/turf/old_loc = loc
. = ..()
if(.)
overmap_event_handler.update_hazards(old_loc)
overmap_event_handler.update_hazards(loc)
/obj/effect/overmap/event/forceMove(atom/destination)
var/old_loc = loc
. = ..()
if(.)
overmap_event_handler.update_hazards(old_loc)
overmap_event_handler.update_hazards(loc)
/obj/effect/overmap/event/Destroy()//takes a look at this one as well, make sure everything is A-OK
if(movable_event)
STOP_PROCESSING(SSprocessing, src)
var/turf/T = loc
. = ..()
overmap_event_handler.update_hazards(T)
/obj/effect/overmap/event/meteor
name = "asteroid field"
events = list(/datum/event/meteor_wave/overmap)
opacity = 1
event_icon_states = list("meteor1", "meteor2", "meteor3", "meteor4")
difficulty = EVENT_LEVEL_MAJOR
tooltip_text = "Large rocks and debris traveling at high speed that can destroy entire hull sections."
/obj/effect/overmap/event/electric
name = "electrical storm"
events = list(/datum/event/electrical_storm)
event_icon_states = list("electrical1", "electrical2")
difficulty = EVENT_LEVEL_MAJOR
can_be_destroyed = FALSE
tooltip_text = "Electromagnetic storm effects that can damage or disable electrical grids."
/obj/effect/overmap/event/dust
name = "dust cloud"
events = list(/datum/event/meteor_wave/dust/overmap)
opacity = 1
event_icon_states = list("dust1", "dust2", "dust3", "dust4")
can_be_destroyed = FALSE
tooltip_text = "Small dust and debris traveling at high speed that can damage or destroy external windows."
/obj/effect/overmap/event/ion
name = "ion cloud"
events = list(/datum/event/ionstorm)
event_icon_states = list("ion1", "ion2", "ion3", "ion4")
difficulty = EVENT_LEVEL_MAJOR
can_be_destroyed = FALSE
tooltip_text = "Ionic effects that can damage synthetics like IPCs or AI, as well as disrupt telecommunications."
/obj/effect/overmap/event/carp
name = "carp shoal"
events = list(/datum/event/carp_migration/overmap)
difficulty = EVENT_LEVEL_MODERATE
event_icon_states = list("carp")
movable_event_chance = 30
tooltip_text = "Xenofauna: usually hostile."
/obj/effect/overmap/event/carp/major
name = "carp school"
opacity = 1
difficulty = EVENT_LEVEL_MAJOR
movable_event_chance = 15
/obj/effect/overmap/event/gravity_anomaly
name = "gravitic anomaly"
events = list(/datum/event/gravity_anomaly)
difficulty = EVENT_LEVEL_MODERATE
event_icon_states = list("grav1")
can_be_destroyed = FALSE
tooltip_text = "Unstable gravitic shear effects detected; wide-field artificial gravity should be powered down before transit."
/obj/effect/overmap/event/MouseEntered(location, control, params)
. = ..()
openToolTip(usr, src, params, tooltip_text)
/obj/effect/overmap/event/MouseExited(location, control, params)
. = ..()
closeToolTip(usr)
/// These now are basically only used to spawn hazards. Will be useful when we need to spawn group of moving hazards
/datum/overmap_event
var/name = "map event"
var/radius = 2
var/count = 6
var/hazards
var/opacity = 0
/// If it should form continous blobs, or can have gaps.
var/continuous = TRUE
/// If set, this event will only spawn in the named space sectors.
var/list/sectors = list()
/datum/overmap_event/proc/spawns_in_current_sector()
if(!length(sectors))
return TRUE
if(!SSatlas.current_sector)
return FALSE
return (SSatlas.current_sector.name in sectors)
/datum/overmap_event/meteor
name = "asteroid field"
count = 10
radius = 4
opacity = 1
continuous = FALSE
hazards = /obj/effect/overmap/event/meteor
/datum/overmap_event/electric
name = "electrical storm"
count = 4
radius = 2
hazards = /obj/effect/overmap/event/electric
/datum/overmap_event/dust
name = "dust cloud"
count = 9
radius = 3
opacity = 1
hazards = /obj/effect/overmap/event/dust
/datum/overmap_event/ion
name = "ion cloud"
count = 4
radius = 2
hazards = /obj/effect/overmap/event/ion
/datum/overmap_event/carp
name = "carp shoal"
count = 6
radius = 2
continuous = FALSE
hazards = /obj/effect/overmap/event/carp
/datum/overmap_event/carp/major
name = "carp school"
count = 5
radius = 3
opacity = 1
hazards = /obj/effect/overmap/event/carp/major
/datum/overmap_event/gravity
name = "dark matter influx"
count = 19
radius = 12
hazards = /obj/effect/overmap/event/gravity_anomaly
sectors = list(SECTOR_LEMURIAN_SEA, SECTOR_LEMURIAN_SEA_FAR)