/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/psiren name = "psiren shoal" events = list(/datum/event/wandering_psirens/overmap) difficulty = EVENT_LEVEL_MODERATE event_icon_states = list("carp") movable_event_chance = 30 tooltip_text = "Xenofauna: usually hostile." /obj/effect/overmap/event/psiren/major name = "psiren school" opacity = 1 difficulty = EVENT_LEVEL_MAJOR movable_event_chance = 15 /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) /datum/overmap_event/psiren name = "psiren shoal" count = 3 radius = 1 continuous = FALSE hazards = /obj/effect/overmap/event/psiren /datum/overmap_event/psiren/major name = "psiren school" count = 2 radius = 2 opacity = 1 hazards = /obj/effect/overmap/event/psiren/major