diff --git a/code/__defines/color.dm b/code/__defines/color.dm index ba9efe20de..d439958d76 100644 --- a/code/__defines/color.dm +++ b/code/__defines/color.dm @@ -14,4 +14,6 @@ #define COLOR_ASSEMBLY_LBLUE "#5D99BE" #define COLOR_ASSEMBLY_BLUE "#38559E" #define COLOR_ASSEMBLY_PURPLE "#6F6192" -#define COLOR_ASSEMBLY_HOT_PINK "#FF69B4" \ No newline at end of file +#define COLOR_ASSEMBLY_HOT_PINK "#FF69B4" + +#define COLOR_ASTEROID_ROCK "#735555" diff --git a/code/__defines/overmap.dm b/code/__defines/overmap.dm new file mode 100644 index 0000000000..503675b44e --- /dev/null +++ b/code/__defines/overmap.dm @@ -0,0 +1,16 @@ + //How far from the edge of overmap zlevel could randomly placed objects spawn +#define OVERMAP_EDGE 2 + +#define SHIP_SIZE_TINY 1 +#define SHIP_SIZE_SMALL 2 +#define SHIP_SIZE_LARGE 3 + +//multipliers for max_speed to find 'slow' and 'fast' speeds for the ship +#define SHIP_SPEED_SLOW 1/(40 SECONDS) +#define SHIP_SPEED_FAST 3/(20 SECONDS)// 15 speed + +#define OVERMAP_WEAKNESS_NONE 0 +#define OVERMAP_WEAKNESS_FIRE 1 +#define OVERMAP_WEAKNESS_EMP 2 +#define OVERMAP_WEAKNESS_MINING 4 +#define OVERMAP_WEAKNESS_EXPLOSIVE 8 diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm index 6b924ef082..27c5231145 100644 --- a/code/_helpers/game.dm +++ b/code/_helpers/game.dm @@ -26,6 +26,15 @@ max_z = max(z, max_z) return max_z +/proc/living_observers_present(var/list/zlevels) + if(LAZYLEN(zlevels)) + for(var/mob/M in player_list) //if a tree ticks on the empty zlevel does it really tick + if(M.stat != DEAD) //(no it doesn't) + var/turf/T = get_turf(M) + if(T && (T.z in zlevels)) + return TRUE + return FALSE + /proc/get_area(atom/A) if(isarea(A)) return A diff --git a/code/_helpers/turfs.dm b/code/_helpers/turfs.dm index e8ca671c11..a9cede52de 100644 --- a/code/_helpers/turfs.dm +++ b/code/_helpers/turfs.dm @@ -33,6 +33,20 @@ available_turfs = start_turfs return pick(available_turfs) +// Picks a turf that is clearance tiles away from the map edge given by dir, on z-level Z +/proc/pick_random_edge_turf(var/dir, var/Z, var/clearance = TRANSITIONEDGE + 1) + if(!dir) + return + switch(dir) + if(NORTH) + return locate(rand(clearance, world.maxx - clearance), world.maxy - clearance, Z) + if(SOUTH) + return locate(rand(clearance, world.maxx - clearance), clearance, Z) + if(EAST) + return locate(world.maxx - clearance, rand(clearance, world.maxy - clearance), Z) + if(WEST) + return locate(clearance, rand(clearance, world.maxy - clearance), Z) + /proc/is_below_sound_pressure(var/turf/T) var/datum/gas_mixture/environment = T ? T.return_air() : null var/pressure = environment ? environment.return_pressure() : 0 diff --git a/code/controllers/subsystems/events.dm b/code/controllers/subsystems/events.dm index 8b8bc1d8b0..6de40f464d 100644 --- a/code/controllers/subsystems/events.dm +++ b/code/controllers/subsystems/events.dm @@ -1,6 +1,8 @@ SUBSYSTEM_DEF(events) name = "Events" - wait = 20 + wait = 2 SECONDS + + var/tmp/list/currentrun = null var/list/datum/event/active_events = list() var/list/datum/event/finished_events = list() @@ -11,23 +13,37 @@ SUBSYSTEM_DEF(events) var/datum/event_meta/new_event = new /datum/controller/subsystem/events/Initialize() + allEvents = typesof(/datum/event) - /datum/event event_containers = list( EVENT_LEVEL_MUNDANE = new/datum/event_container/mundane, EVENT_LEVEL_MODERATE = new/datum/event_container/moderate, EVENT_LEVEL_MAJOR = new/datum/event_container/major ) - allEvents = typesof(/datum/event) - /datum/event + if(global.using_map.use_overmap) + GLOB.overmap_event_handler.create_events(global.using_map.overmap_z, global.using_map.overmap_size, global.using_map.overmap_event_areas) return ..() /datum/controller/subsystem/events/fire(resumed) - for(var/datum/event/E in active_events) + if (!resumed) + src.currentrun = active_events.Copy() + + //cache for sanic speed (lists are references anyways) + var/list/currentrun = src.currentrun + while (currentrun.len) + var/datum/event/E = currentrun[currentrun.len] + currentrun.len-- if(E.processing_active) E.process() + if (MC_TICK_CHECK) + return for(var/i = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR) var/list/datum/event_container/EC = event_containers[i] EC.process() +/datum/controller/subsystem/events/stat_entry() + ..("E:[active_events.len]") + /datum/controller/subsystem/events/Recover() if(SSevents.active_events) active_events |= SSevents.active_events @@ -35,6 +51,8 @@ SUBSYSTEM_DEF(events) finished_events |= SSevents.finished_events /datum/controller/subsystem/events/proc/event_complete(var/datum/event/E) + active_events -= E + if(!E.event_meta || !E.severity) // datum/event is used here and there for random reasons, maintaining "backwards compatibility" log_debug("Event of '[E.type]' with missing meta-data has completed.") return @@ -50,7 +68,7 @@ SUBSYSTEM_DEF(events) log_debug("Event '[EM.name]' has completed at [stationtime2text()].") /datum/controller/subsystem/events/proc/delay_events(var/severity, var/delay) - var/list/datum/event_container/EC = event_containers[severity] + var/datum/event_container/EC = event_containers[severity] EC.next_event_time += delay /datum/controller/subsystem/events/proc/RoundEnd() diff --git a/code/controllers/subsystems/skybox.dm b/code/controllers/subsystems/skybox.dm index 59e841ee52..aadc086b08 100644 --- a/code/controllers/subsystems/skybox.dm +++ b/code/controllers/subsystems/skybox.dm @@ -81,10 +81,10 @@ SUBSYSTEM_DEF(skybox) overmap.appearance_flags = RESET_COLOR res.overlays += overmap - // TODO - Allow events to apply custom overlays to skybox! (Awesome!) - //for(var/datum/event/E in SSevent.active_events) - // if(E.has_skybox_image && E.isRunning && (z in E.affecting_z)) - // res.overlays += E.get_skybox_image() + // Allow events to apply custom overlays to skybox! (Awesome!) + for(var/datum/event/E in SSevents.active_events) + if(E.has_skybox_image && E.isRunning && (z in E.affecting_z)) + res.overlays += E.get_skybox_image() return res diff --git a/code/game/gamemodes/events/dust.dm b/code/game/gamemodes/events/dust.dm index f41e040ee5..400ae1058d 100644 --- a/code/game/gamemodes/events/dust.dm +++ b/code/game/gamemodes/events/dust.dm @@ -7,27 +7,57 @@ No command report on the common version of this event. The "dust" will damage the hull of the station causin minor hull breaches. */ -/proc/dust_swarm(var/strength = "weak") +/proc/dust_swarm(var/strength = "weak", var/list/affecting_z) var/numbers = 1 + var/dust_type = /obj/effect/space_dust switch(strength) if("weak") - numbers = rand(2,4) - for(var/i = 0 to numbers) - new/obj/effect/space_dust/weak() + numbers = rand(2,4) + dust_type = /obj/effect/space_dust/weak if("norm") - numbers = rand(5,10) - for(var/i = 0 to numbers) - new/obj/effect/space_dust() + numbers = rand(5,10) + dust_type = /obj/effect/space_dust if("strong") - numbers = rand(10,15) - for(var/i = 0 to numbers) - new/obj/effect/space_dust/strong() + numbers = rand(10,15) + dust_type = /obj/effect/space_dust/strong if("super") - numbers = rand(15,25) - for(var/i = 0 to numbers) - new/obj/effect/space_dust/super() - return + numbers = rand(15,25) + dust_type = /obj/effect/space_dust/super + var/startside = pick(cardinal) + for(var/i = 0 to numbers) + var/startx = 0 + var/starty = 0 + var/endy = 0 + var/endx = 0 + switch(startside) + if(NORTH) + starty = world.maxy-TRANSITIONEDGE-1 + startx = rand(TRANSITIONEDGE+1, world.maxx-TRANSITIONEDGE-1) + endy = TRANSITIONEDGE + endx = rand(TRANSITIONEDGE+1, world.maxx-TRANSITIONEDGE-1) + if(EAST) + starty = rand(TRANSITIONEDGE+1, world.maxy-TRANSITIONEDGE-1) + startx = world.maxx-TRANSITIONEDGE-1 + endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE) + endx = TRANSITIONEDGE + if(SOUTH) + starty = TRANSITIONEDGE+1 + startx = rand(TRANSITIONEDGE+1, world.maxx-TRANSITIONEDGE-1) + endy = world.maxy-TRANSITIONEDGE + endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) + if(WEST) + starty = rand(TRANSITIONEDGE+1, world.maxy-TRANSITIONEDGE-1) + startx = TRANSITIONEDGE+1 + endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE) + endx = world.maxx-TRANSITIONEDGE + + var/randomz = pick(affecting_z) + var/turf/startloc = locate(startx, starty, randomz) + var/turf/endloc = locate(endx, endy, randomz) + var/obj/effect/space_dust/D = new dust_type(startloc) + D.set_dir(GLOB.reverse_dir[startside]) + walk_towards(D, endloc, 1) /obj/effect/space_dust name = "Space Dust" @@ -39,96 +69,51 @@ The "dust" will damage the hull of the station causin minor hull breaches. var/strength = 2 //ex_act severity number var/life = 2 //how many things we hit before qdel(src) - weak - strength = 3 - life = 1 +/obj/effect/space_dust/weak + strength = 3 + life = 1 - strong - strength = 1 - life = 6 +/obj/effect/space_dust/strong + strength = 1 + life = 6 - super - strength = 1 - life = 40 +/obj/effect/space_dust/super + strength = 1 + life = 40 + +/obj/effect/space_dust/Destroy() + walk(src, 0) // Because we might have called walk_towards, we must stop the walk loop or BYOND keeps an internal reference to us forever. + return ..() + +/obj/effect/space_dust/touch_map_edge() + qdel(src) + +/obj/effect/space_dust/Bump(atom/A) + spawn(0) + if(prob(50)) + for(var/mob/M in range(10, src)) + if(!M.stat && !istype(M, /mob/living/silicon/ai)) + shake_camera(M, 3, 1) + if (A) + playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, 1) + + if(ismob(A)) + A.ex_act(strength)//This should work for now I guess + else if(!istype(A,/obj/machinery/power/emitter) && !istype(A,/obj/machinery/field_generator)) //Protect the singularity from getting released every round! + A.ex_act(strength) //Changing emitter/field gen ex_act would make it immune to bombs and C4 + + life-- + if(life <= 0) + walk(src,0) + qdel(src) + return 0 + return - New() - ..() - var/startx = 0 - var/starty = 0 - var/endy = 0 - var/endx = 0 - var/startside = pick(cardinal) +/obj/effect/space_dust/Bumped(atom/A) + Bump(A) + return - switch(startside) - if(NORTH) - starty = world.maxy-(TRANSITIONEDGE+1) - startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) - endy = TRANSITIONEDGE - endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) - if(EAST) - starty = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1)) - startx = world.maxx-(TRANSITIONEDGE+1) - endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE) - endx = TRANSITIONEDGE - if(SOUTH) - starty = (TRANSITIONEDGE+1) - startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) - endy = world.maxy-TRANSITIONEDGE - endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) - if(WEST) - starty = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1)) - startx = (TRANSITIONEDGE+1) - endy = rand(TRANSITIONEDGE,world.maxy-TRANSITIONEDGE) - endx = world.maxx-TRANSITIONEDGE - //VOREStation Edit - No space dust outside of space - var/list/z_levels = using_map.station_levels.Copy() - for(var/datum/planet/P in SSplanets.planets) - z_levels.Remove(P.expected_z_levels) - var/z_level = pick(z_levels) - //VOREStation Edit End - var/goal = locate(endx, endy, z_level) - src.x = startx - src.y = starty - src.z = z_level - spawn(0) - walk_towards(src, goal, 1) - return - - Destroy() - walk(src, 0) // Because we might have called walk_towards, we must stop the walk loop or BYOND keeps an internal reference to us forever. - return ..() - - touch_map_edge() - qdel(src) - - Bump(atom/A) - spawn(0) - if(prob(50)) - for(var/mob/M in range(10, src)) - if(!M.stat && !istype(M, /mob/living/silicon/ai)) - shake_camera(M, 3, 1) - if (A) - playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, 1) - - if(ismob(A)) - A.ex_act(strength)//This should work for now I guess - else if(!istype(A,/obj/machinery/power/emitter) && !istype(A,/obj/machinery/field_generator)) //Protect the singularity from getting released every round! - A.ex_act(strength) //Changing emitter/field gen ex_act would make it immune to bombs and C4 - - life-- - if(life <= 0) - walk(src,0) - qdel(src) - return 0 - return - - - Bumped(atom/A) - Bump(A) - return - - - ex_act(severity) - qdel(src) - return +/obj/effect/space_dust/ex_act(severity) + qdel(src) + return diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm index 1ac4779414..453d848e8d 100644 --- a/code/game/gamemodes/meteor/meteors.dm +++ b/code/game/gamemodes/meteor/meteors.dm @@ -18,21 +18,18 @@ //Meteor spawning global procs /////////////////////////////// -/proc/pick_meteor_start(var/startSide = pick(cardinal)) - var/startLevel = pick(using_map.station_levels - using_map.sealed_levels) - var/pickedstart = spaceDebrisStartLoc(startSide, startLevel) - - return list(startLevel, pickedstart) - -/proc/spawn_meteors(var/number = 10, var/list/meteortypes, var/startSide) +/proc/spawn_meteors(var/number = 10, var/list/meteortypes, var/startSide, var/zlevel) + log_debug("Spawning [number] meteors on the [dir2text(startSide)] of [zlevel]") for(var/i = 0; i < number; i++) - spawn_meteor(meteortypes, startSide) + spawn_meteor(meteortypes, startSide, zlevel) -/proc/spawn_meteor(var/list/meteortypes, var/startSide) - var/start = pick_meteor_start(startSide) +/proc/spawn_meteor(var/list/meteortypes, var/startSide, var/startLevel) + if(isnull(startSide)) + startSide = pick(cardinal) + if(isnull(startLevel)) + startLevel = pick(using_map.station_levels - using_map.sealed_levels) - var/startLevel = start[1] - var/turf/pickedstart = start[2] + var/turf/pickedstart = spaceDebrisStartLoc(startSide, startLevel) var/turf/pickedgoal = spaceDebrisFinishLoc(startSide, startLevel) var/Me = pickweight(meteortypes) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 1447a37997..ca177403ac 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -269,20 +269,23 @@ var/const/enterloopsanity = 100 for(var/obj/O in src) O.hide(O.hides_under_flooring() && !is_plating()) -/turf/proc/AdjacentTurfs() - var/L[] = new() - for(var/turf/simulated/t in oview(src,1)) - if(!t.density) - if(!LinkBlocked(src, t) && !TurfBlockedNonWindow(t)) - L.Add(t) - return L +/turf/proc/AdjacentTurfs(var/check_blockage = TRUE) + . = list() + for(var/t in (trange(1,src) - src)) + var/turf/T = t + if(check_blockage) + if(!T.density) + if(!LinkBlocked(src, T) && !TurfBlockedNonWindow(T)) + . += t + else + . += t -/turf/proc/CardinalTurfs() - var/L[] = new() - for(var/turf/simulated/T in AdjacentTurfs()) +/turf/proc/CardinalTurfs(var/check_blockage = TRUE) + . = list() + for(var/ad in AdjacentTurfs(check_blockage)) + var/turf/T = ad if(T.x == src.x || T.y == src.y) - L.Add(T) - return L + . += T /turf/proc/Distance(turf/t) if(get_dist(src,t) == 1) diff --git a/code/modules/events/carp_migration.dm b/code/modules/events/carp_migration.dm index b30a5d4898..53efc99e8d 100644 --- a/code/modules/events/carp_migration.dm +++ b/code/modules/events/carp_migration.dm @@ -1,51 +1,75 @@ /datum/event/carp_migration - announceWhen = 50 - endWhen = 900 - + startWhen = 0 // Start immediately + announceWhen = 45 // Adjusted by setup + endWhen = 75 // Adjusted by setup + var/carp_cap = 10 var/list/spawned_carp = list() /datum/event/carp_migration/setup() - announceWhen = rand(40, 60) - endWhen = rand(600,1200) + announceWhen = rand(30, 60) // 1 to 2 minutes + endWhen += severity * 25 + carp_cap = 2 + 3 ** severity // No more than this many at once regardless of waves. (5, 11, 29) /datum/event/carp_migration/announce() var/announcement = "" if(severity == EVENT_LEVEL_MAJOR) - announcement = "Massive migration of unknown biological entities has been detected near [station_name()], please stand-by." + announcement = "Massive migration of unknown biological entities has been detected near [location_name()], please stand-by." else - announcement = "Unknown biological [spawned_carp.len == 1 ? "entity has" : "entities have"] been detected near [station_name()], please stand-by." + announcement = "Unknown biological [spawned_carp.len == 1 ? "entity has" : "entities have"] been detected near [location_name()], please stand-by." command_announcement.Announce(announcement, "Lifesign Alert") -/datum/event/carp_migration/start() - if(severity == EVENT_LEVEL_MAJOR) - spawn_fish(landmarks_list.len) - else if(severity == EVENT_LEVEL_MODERATE) - spawn_fish(rand(4, 6)) //12 to 30 carp, in small groups - else - spawn_fish(rand(1, 3), 1, 2) //1 to 6 carp, alone or in pairs +/datum/event/carp_migration/tick() + if(activeFor % 4 != 0) + return // Only process every 8 seconds. + if(count_spawned_carps() < carp_cap && living_observers_present(affecting_z)) + spawn_fish(rand(1, severity * 2) - 1, severity, severity * 2) -/datum/event/carp_migration/proc/spawn_fish(var/num_groups, var/group_size_min=3, var/group_size_max=5) - var/list/spawn_locations = list() - - for(var/obj/effect/landmark/C in landmarks_list) - if(C.name == "carpspawn") - spawn_locations.Add(C.loc) - spawn_locations = shuffle(spawn_locations) - num_groups = min(num_groups, spawn_locations.len) +/datum/event/carp_migration/proc/spawn_fish(var/num_groups, var/group_size_min, var/group_size_max, var/dir) + if(isnull(dir)) + dir = (victim && prob(80)) ? victim.fore_dir : pick(GLOB.cardinal) var/i = 1 while (i <= num_groups) + var/Z = pick(affecting_z) var/group_size = rand(group_size_min, group_size_max) - for (var/j = 1, j <= group_size, j++) - spawned_carp.Add(new /mob/living/simple_mob/animal/space/carp/event(spawn_locations[i])) + var/turf/map_center = locate(round(world.maxx/2), round(world.maxy/2), Z) + var/turf/group_center = pick_random_edge_turf(dir, Z, TRANSITIONEDGE + 2) + var/list/turfs = getcircle(group_center, 2) + for (var/j = 0, j < group_size, j++) + var/mob/living/simple_mob/animal/M = new /mob/living/simple_mob/animal/space/carp/event(turfs[(i % turfs.len) + 1]) + GLOB.destroyed_event.register(M, src, .proc/on_carp_destruction) + spawned_carp.Add(M) + M.ai_holder?.give_destination(map_center) // Ask carp to swim towards the middle of the map + // TODO - Our throwing systems don't support callbacks. If it ever does, we can throw first to simulate ship speed. i++ +// Counts living carp spawned by this event. +/datum/event/carp_migration/proc/count_spawned_carps() + . = 0 + for(var/I in spawned_carp) + var/mob/living/simple_mob/animal/M = I + if(!QDELETED(M) && M.stat != DEAD) + . += 1 + +// If carp is bomphed, remove it from the list. +/datum/event/carp_migration/proc/on_carp_destruction(var/mob/M) + spawned_carp -= M + GLOB.destroyed_event.unregister(M, src, .proc/on_carp_destruction) + /datum/event/carp_migration/end() + . = ..() + // Clean up carp that died in space for some reason. spawn(0) for(var/mob/living/simple_mob/SM in spawned_carp) - if(!SM.stat) + if(SM.stat == DEAD) var/turf/T = get_turf(SM) if(istype(T, /turf/space)) if(prob(75)) qdel(SM) - sleep(1) \ No newline at end of file + CHECK_TICK + +// +// Overmap version of the event! +// +/datum/event/carp_migration/overmap + announceWhen = 1 // Announce much faster! diff --git a/code/modules/events/dust.dm b/code/modules/events/dust.dm index 3c7a116827..54a1ec8baf 100644 --- a/code/modules/events/dust.dm +++ b/code/modules/events/dust.dm @@ -3,13 +3,17 @@ endWhen = 30 /datum/event/dust/announce() - command_announcement.Announce("Debris resulting from activity on another nearby asteroid is approaching \the [station_name()]", "Dust Alert") + if(victim) + command_announcement.Announce("The [location_name()] is now passing through a belt of space dust.", "[location_name()] Sensor Array") + else + command_announcement.Announce("Debris resulting from activity on another nearby asteroid is approaching \the [location_name()]", "Dust Alert") -/datum/event/dust/start() - dust_swarm(get_severity()) +/datum/event/dust/tick() + if(prob(10)) + dust_swarm(severity, affecting_z) /datum/event/dust/end() - command_announcement.Announce("\The [station_name()] is no longer in danger of impact from space debris.", "Dust Notice") + command_announcement.Announce("\The [location_name()] is no longer in danger of impact from space debris.", "Dust Notice") /datum/event/dust/proc/get_severity() switch(severity) diff --git a/code/modules/events/electrical_storm.dm b/code/modules/events/electrical_storm.dm index 73198dec9b..dbf935326f 100644 --- a/code/modules/events/electrical_storm.dm +++ b/code/modules/events/electrical_storm.dm @@ -1,28 +1,61 @@ /datum/event/electrical_storm - var/lightsoutAmount = 1 - var/lightsoutRange = 25 + announceWhen = 0 // Warn them shortly before it begins. + startWhen = 30 // 1 minute + endWhen = 60 // Set in setup() + has_skybox_image = TRUE + var/tmp/lightning_color + var/tmp/list/valid_apcs // List of valid APCs. +/datum/event/electrical_storm/get_skybox_image() + if(!lightning_color) + lightning_color = pick("#ffd98c", "#ebc7ff", "#bdfcff", "#bdd2ff", "#b0ffca", "#ff8178", "#ad74cc") + var/image/res = image('icons/skybox/electrobox.dmi', "lightning") + res.color = lightning_color + res.appearance_flags = RESET_COLOR + res.blend_mode = BLEND_ADD + return res /datum/event/electrical_storm/announce() - command_announcement.Announce("An electrical issue has been detected in your area, please repair potential electronic overloads.", "Electrical Alert") - + ..() + switch(severity) + if(EVENT_LEVEL_MUNDANE) + command_announcement.Announce("A minor electrical storm has been detected near the [location_name()]. Please watch out for possible electrical discharges.", "[location_name()] Sensor Array") + if(EVENT_LEVEL_MODERATE) + command_announcement.Announce("The [location_name()] is about to pass through an electrical storm. Please secure sensitive electrical equipment until the storm passes.", "[location_name()] Sensor Array") + if(EVENT_LEVEL_MAJOR) + command_announcement.Announce("Alert. A strong electrical storm has been detected in proximity of the [location_name()]. It is recommended to immediately secure sensitive electrical equipment until the storm passes.", "[location_name()] Sensor Array") /datum/event/electrical_storm/start() - var/list/epicentreList = list() + ..() + valid_apcs = list() + for(var/obj/machinery/power/apc/A in global.machines) + if(A.z in affecting_z) + valid_apcs.Add(A) + endWhen = (severity * 60) + startWhen - for(var/i=1, i <= lightsoutAmount, i++) - var/list/possibleEpicentres = list() - for(var/obj/effect/landmark/newEpicentre in landmarks_list) - if(newEpicentre.name == "lightsout" && !(newEpicentre in epicentreList)) - possibleEpicentres += newEpicentre - if(possibleEpicentres.len) - epicentreList += pick(possibleEpicentres) - else - break +/datum/event/electrical_storm/tick() + ..() + // See if shields can stop it first (It would be nice to port baystation's cooler shield gens perhaps) + // TODO - We need a better shield generator system to handle this properly. + if(!valid_apcs.len) + log_debug("No valid APCs found for electrical storm event ship=[victim]!") + return + var/list/picked_apcs = list() + for(var/i=0, i< severity * 2, i++) // up to 2/4/6 APCs per tick depending on severity + picked_apcs |= pick(valid_apcs) + for(var/obj/machinery/power/apc/T in picked_apcs) + affect_apc(T) - if(!epicentreList.len) +/datum/event/electrical_storm/proc/affect_apc(var/obj/machinery/power/apc/T) + // Main breaker is turned off. Consider this APC protected. + if(!T.operating) return - for(var/obj/effect/landmark/epicentre in epicentreList) - for(var/obj/machinery/power/apc/apc in range(epicentre,lightsoutRange)) - apc.overload_lighting() + // Decent chance to overload lighting circuit. + if(prob(3 * severity)) + T.overload_lighting() + + // Relatively small chance to emag the apc as apc_damage event does. + if(prob(0.2 * severity)) + T.emagged = 1 + T.update_icon() diff --git a/code/modules/events/event.dm b/code/modules/events/event.dm index e44e161a1a..9718f821b2 100644 --- a/code/modules/events/event.dm +++ b/code/modules/events/event.dm @@ -1,3 +1,4 @@ +// Event Meta instances represent choices for the event manager to choose for random events. /datum/event_meta var/name = "" var/enabled = 1 // Whether or not the event is available for random selection at all @@ -39,6 +40,10 @@ return total_weight +/datum/event_meta/no_overmap/get_weight() //these events have overmap equivalents, and shouldn't fire randomly if overmap is used + return global.using_map.use_overmap ? 0 : ..() + +// Event datums define and execute the actual events themselves. /datum/event //NOTE: Times are measured in master controller ticks! var/startWhen = 0 //When in the lifetime to call start(). var/announceWhen = 0 //When in the lifetime to call announce(). @@ -51,6 +56,9 @@ var/endedAt = 0 //When this event ended. var/processing_active = TRUE var/datum/event_meta/event_meta = null + var/list/affecting_z = null // List of z-levels to affect, null lets the event choose (usally station_levels) + var/has_skybox_image = FALSE // True if SSskybox should query this event for an image to put in the skybox. + var/obj/effect/overmap/visitable/ship/victim = null // Ship that triggered this event on itself. Some messages might be different etc. /datum/event/nothing @@ -65,6 +73,8 @@ //Allows you to start before announcing or vice versa. //Only called once. /datum/event/proc/start() + if(has_skybox_image) + SSskybox.rebuild_skyboxes(affecting_z) return //Called when the tick is equal to the announceWhen variable. @@ -87,6 +97,8 @@ //For example: if(activeFor == myOwnVariable + 30) doStuff() //Only called once. /datum/event/proc/end() + if(has_skybox_image) + SSskybox.rebuild_skyboxes(affecting_z) return //Returns the latest point of event processing. @@ -132,9 +144,12 @@ end() endedAt = world.time - SSevents.active_events -= src SSevents.event_complete(src) +//Called during building of skybox to get overlays +/datum/event/proc/get_skybox_image() + return + /datum/event/New(var/datum/event_meta/EM) // event needs to be responsible for this, as stuff like APLUs currently make their own events for curious reasons SSevents.active_events += src @@ -146,5 +161,17 @@ startedAt = world.time + if(!affecting_z) + affecting_z = using_map.station_levels + setup() ..() + +/datum/event/Destroy() + victim = null + . = ..() + +/datum/event/proc/location_name() + if(victim) + return victim.name + return station_name() diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm index abfa4407fe..592c963be8 100644 --- a/code/modules/events/ion_storm.dm +++ b/code/modules/events/ion_storm.dm @@ -1,20 +1,51 @@ //This file was auto-corrected by findeclaration.exe on 29/05/2012 15:03:04 /datum/event/ionstorm + has_skybox_image = TRUE var/botEmagChance = 0 //VOREStation Edit + var/cloud_hueshift var/list/players = list() -/datum/event/ionstorm/announce() +/datum/event/ionstorm/get_skybox_image() + if(!cloud_hueshift) + cloud_hueshift = color_rotation(rand(-3, 3) * 15) + var/image/res = image('icons/skybox/ionbox.dmi', "ions") + res.color = cloud_hueshift + res.appearance_flags = RESET_COLOR + res.blend_mode = BLEND_ADD + return res + +/datum/event/ionstorm/setup() endWhen = rand(500, 1500) + +// Interestingly enough, announce() actually *DOES* this event for some reason. +/datum/event/ionstorm/announce() // command_alert("The station has entered an ion storm. Monitor all electronic equipment for malfunctions", "Anomaly Alert") for (var/mob/living/carbon/human/player in player_list) if( !player.mind || player_is_antag(player.mind, only_offstation_roles = 1) || player.client.inactivity > MinutesToTicks(10)) continue players += player.real_name + // Flomph synthetics + for(var/mob/living/carbon/S in living_mob_list) + if (!S.isSynthetic()) + continue + if(!(S.z in affecting_z)) + continue + var/area/A = get_area(S) + if(!A || A.flags & RAD_SHIELDED) // Rad shielding will protect from ions too + continue + to_chat(S, "Your integrated sensors detect an ionospheric anomaly. Your systems will be impacted as you begin a partial restart.") + var/ionbug = rand(3, 9) + S.confused += ionbug + S.eye_blurry += (ionbug - 1) + + // Ionize silicon mobs for (var/mob/living/silicon/ai/target in silicon_mob_list) + if(!(target.z in affecting_z)) + continue var/law = target.generate_ion_law() - to_chat(target, "You have detected a change in your laws information:") + to_chat(target, "You have detected a change in your laws information:") to_chat(target, law) target.add_ion_law(law) target.show_laws() @@ -31,13 +62,15 @@ /datum/event/ionstorm/tick() if(botEmagChance) for(var/mob/living/bot/bot in mob_list) + if(!(bot.z in affecting_z)) + continue if(prob(botEmagChance)) bot.emag_act(1) /datum/event/ionstorm/end() - spawn(rand(5000,8000)) - if(prob(50)) - ion_storm_announcement() + if(prob(50)) + spawn(rand(5000,8000)) + command_announcement.Announce("It has come to our attention that \the [location_name()] passed through an ion storm. Please monitor all electronic equipment for malfunctions.", "Anomaly Alert") /* /proc/IonStorm(botEmagChance = 10) diff --git a/code/modules/events/meteors.dm b/code/modules/events/meteors.dm index b4f202722c..a57ca87576 100644 --- a/code/modules/events/meteors.dm +++ b/code/modules/events/meteors.dm @@ -1,30 +1,45 @@ /datum/event/meteor_wave - startWhen = 5 - endWhen = 7 + startWhen = 30 // About one minute early warning + endWhen = 60 // Adjusted automatically in tick() + has_skybox_image = TRUE var/next_meteor = 6 var/waves = 1 var/start_side + var/next_meteor_lower = 10 + var/next_meteor_upper = 20 + +/datum/event/meteor_wave/get_skybox_image() + var/image/res = image('icons/skybox/rockbox.dmi', "rockbox") + res.color = COLOR_ASTEROID_ROCK + res.appearance_flags = RESET_COLOR + return res /datum/event/meteor_wave/setup() - waves = 2 + rand(1, severity) //EVENT_LEVEL_MAJOR is 3-5 waves + waves = (2 + rand(1, severity)) * severity start_side = pick(cardinal) endWhen = worst_case_end() /datum/event/meteor_wave/announce() switch(severity) if(EVENT_LEVEL_MAJOR) - command_announcement.Announce("Meteors have been detected on collision course with \the [station_name()].", "Meteor Alert", new_sound = 'sound/AI/meteors.ogg') + command_announcement.Announce("Meteors have been detected on collision course with \the [location_name()].", "Meteor Alert", new_sound = 'sound/AI/meteors.ogg') else - command_announcement.Announce("\The [station_name()] is now in a meteor shower.", "Meteor Alert") + command_announcement.Announce("\The [location_name()] is now in a meteor shower.", "Meteor Alert") /datum/event/meteor_wave/tick() if(waves && activeFor >= next_meteor) - var/pick_side = prob(80) ? start_side : (prob(50) ? turn(start_side, 90) : turn(start_side, -90)) + send_wave() - spawn() spawn_meteors(severity * rand(1,2), get_meteors(), pick_side) - next_meteor += rand(15, 30) / severity - waves-- - endWhen = worst_case_end() +/datum/event/meteor_wave/proc/send_wave() + var/pick_side = prob(80) ? start_side : (prob(50) ? turn(start_side, 90) : turn(start_side, -90)) + + spawn() spawn_meteors(get_wave_size(), get_meteors(), pick_side, pick(affecting_z)) + next_meteor += rand(next_meteor_lower, next_meteor_upper) / severity + waves-- + endWhen = worst_case_end() + +/datum/event/meteor_wave/proc/get_wave_size() + return severity * rand(2, 3) /datum/event/meteor_wave/proc/worst_case_end() return activeFor + ((30 / severity) * waves) + 10 @@ -32,9 +47,9 @@ /datum/event/meteor_wave/end() switch(severity) if(EVENT_LEVEL_MAJOR) - command_announcement.Announce("\The [station_name()] has cleared the meteor storm.", "Meteor Alert") + command_announcement.Announce("\The [location_name()] has cleared the meteor storm.", "Meteor Alert") else - command_announcement.Announce("\The [station_name()] has cleared the meteor shower", "Meteor Alert") + command_announcement.Announce("\The [location_name()] has cleared the meteor shower", "Meteor Alert") /datum/event/meteor_wave/proc/get_meteors() if(EVENT_LEVEL_MAJOR) @@ -44,3 +59,41 @@ return meteors_threatening else return meteors_normal + + +/datum/event/meteor_wave/overmap + next_meteor_lower = 5 + next_meteor_upper = 10 + next_meteor = 0 + +/datum/event/meteor_wave/overmap/tick() + if(victim && !victim.is_still()) // Meteors mostly fly in your face + start_side = prob(90) ? victim.fore_dir : pick(GLOB.cardinal) + else //Unless you're standing still + start_side = pick(GLOB.cardinal) + ..() + +/datum/event/meteor_wave/overmap/get_wave_size() + . = ..() + if(!victim) + return + var/skill = victim.get_helm_skill() + var/speed = victim.get_speed() + if(skill >= SKILL_PROF) + . = round(. * 0.5) + if(victim.is_still()) //Standing still means less shit flies your way + . = round(. * 0.1) + if(speed < SHIP_SPEED_SLOW) //Slow and steady + . = round(. * 0.5) + if(speed > SHIP_SPEED_FAST) //Sanic stahp + . *= 2 + + //Smol ship evasion + if(victim.vessel_size < SHIP_SIZE_LARGE && speed < SHIP_SPEED_FAST) + var/skill_needed = SKILL_PROF + if(speed < SHIP_SPEED_SLOW) + skill_needed = SKILL_ADEPT + if(victim.vessel_size < SHIP_SIZE_SMALL) + skill_needed = skill_needed - 1 + if(skill >= max(skill_needed, victim.skill_needed)) + . = round(. * 0.5) diff --git a/code/modules/mob/new_player/skill.dm b/code/modules/mob/new_player/skill.dm index dc7e5c354e..b2d061d5a6 100644 --- a/code/modules/mob/new_player/skill.dm +++ b/code/modules/mob/new_player/skill.dm @@ -3,6 +3,7 @@ var/global/const SKILL_BASIC = 1 SKILL_ADEPT = 2 SKILL_EXPERT = 3 + SKILL_PROF = 4 /datum/skill/var ID = "none" // ID of the skill, used in code diff --git a/code/modules/overmap/_defines.dm b/code/modules/overmap/_defines.dm deleted file mode 100644 index 6cfde46793..0000000000 --- a/code/modules/overmap/_defines.dm +++ /dev/null @@ -1,145 +0,0 @@ -//How far from the edge of overmap zlevel could randomly placed objects spawn -#define OVERMAP_EDGE 2 - -#define SHIP_SIZE_TINY 1 -#define SHIP_SIZE_SMALL 2 -#define SHIP_SIZE_LARGE 3 - -//multipliers for max_speed to find 'slow' and 'fast' speeds for the ship -#define SHIP_SPEED_SLOW 1/(40 SECONDS) -#define SHIP_SPEED_FAST 3/(20 SECONDS)// 15 speed - -#define OVERMAP_WEAKNESS_NONE 0 -#define OVERMAP_WEAKNESS_FIRE 1 -#define OVERMAP_WEAKNESS_EMP 2 -#define OVERMAP_WEAKNESS_MINING 4 -#define OVERMAP_WEAKNESS_EXPLOSIVE 8 - -//Dimension of overmap (squares 4 lyfe) -var/global/list/map_sectors = list() - -/area/overmap/ - name = "System Map" - icon_state = "start" - requires_power = 0 - base_turf = /turf/unsimulated/map - -/turf/unsimulated/map - icon = 'icons/turf/space.dmi' - icon_state = "map" - initialized = FALSE // TODO - Fix unsimulated turf initialization so this override is not necessary! - -/turf/unsimulated/map/edge - opacity = 1 - density = 1 - -/turf/unsimulated/map/Initialize() - . = ..() - name = "[x]-[y]" - var/list/numbers = list() - - if(x == 1 || x == global.using_map.overmap_size) - numbers += list("[round(y/10)]","[round(y%10)]") - if(y == 1 || y == global.using_map.overmap_size) - numbers += "-" - if(y == 1 || y == global.using_map.overmap_size) - numbers += list("[round(x/10)]","[round(x%10)]") - - for(var/i = 1 to numbers.len) - var/image/I = image('icons/effects/numbers.dmi',numbers[i]) - I.pixel_x = 5*i - 2 - I.pixel_y = world.icon_size/2 - 3 - if(y == 1) - I.pixel_y = 3 - I.pixel_x = 5*i + 4 - if(y == global.using_map.overmap_size) - I.pixel_y = world.icon_size - 9 - I.pixel_x = 5*i + 4 - if(x == 1) - I.pixel_x = 5*i - 2 - if(x == global.using_map.overmap_size) - I.pixel_x = 5*i + 2 - add_overlay(I) - -//list used to track which zlevels are being 'moved' by the proc below -var/list/moving_levels = list() -//Proc to 'move' stars in spess -//yes it looks ugly, but it should only fire when state actually change. -//null direction stops movement -proc/toggle_move_stars(zlevel, direction) - if(!zlevel) - return - - if (moving_levels["[zlevel]"] != direction) - moving_levels["[zlevel]"] = direction - - var/list/spaceturfs = block(locate(1, 1, zlevel), locate(world.maxx, world.maxy, zlevel)) - for(var/turf/space/T in spaceturfs) - T.toggle_transit(direction) - CHECK_TICK -/* -//list used to cache empty zlevels to avoid nedless map bloat -var/list/cached_space = list() - -proc/overmap_spacetravel(var/turf/space/T, var/atom/movable/A) - var/obj/effect/map/M = map_sectors["[T.z]"] - if (!M) - return - var/mapx = M.x - var/mapy = M.y - var/nx = 1 - var/ny = 1 - var/nz = M.map_z - - if(T.x <= TRANSITIONEDGE) - nx = world.maxx - TRANSITIONEDGE - 2 - ny = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) - mapx = max(1, mapx-1) - - else if (A.x >= (world.maxx - TRANSITIONEDGE - 1)) - nx = TRANSITIONEDGE + 2 - ny = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) - mapx = min(world.maxx, mapx+1) - - else if (T.y <= TRANSITIONEDGE) - ny = world.maxy - TRANSITIONEDGE -2 - nx = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) - mapy = max(1, mapy-1) - - else if (A.y >= (world.maxy - TRANSITIONEDGE - 1)) - ny = TRANSITIONEDGE + 2 - nx = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) - mapy = min(world.maxy, mapy+1) - - testing("[A] moving from [M] ([M.x], [M.y]) to ([mapx],[mapy]).") - - var/turf/map = locate(mapx,mapy,OVERMAP_ZLEVEL) - var/obj/effect/map/TM = locate() in map - if(TM) - nz = TM.map_z - testing("Destination: [TM]") - else - if(cached_space.len) - var/obj/effect/map/sector/temporary/cache = cached_space[cached_space.len] - cached_space -= cache - nz = cache.map_z - cache.x = mapx - cache.y = mapy - testing("Destination: *cached* [TM]") - else - world.maxz++ - nz = world.maxz - TM = new /obj/effect/map/sector/temporary(mapx, mapy, nz) - testing("Destination: *new* [TM]") - - var/turf/dest = locate(nx,ny,nz) - if(dest) - A.loc = dest - - if(istype(M, /obj/effect/map/sector/temporary)) - var/obj/effect/map/sector/temporary/source = M - if (source.can_die()) - testing("Catching [M] for future use") - source.loc = null - cached_space += source -*/ \ No newline at end of file diff --git a/code/modules/overmap/events/event_handler.dm b/code/modules/overmap/events/event_handler.dm new file mode 100644 index 0000000000..67785e95ac --- /dev/null +++ b/code/modules/overmap/events/event_handler.dm @@ -0,0 +1,163 @@ +GLOBAL_DATUM_INIT(overmap_event_handler, /decl/overmap_event_handler, new) + +/decl/overmap_event_handler + var/list/hazard_by_turf + var/list/ship_events + +/decl/overmap_event_handler/New() + ..() + hazard_by_turf = list() + ship_events = list() + +// Populates overmap with random events! Should be called once at startup at some point. +/decl/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/overmap_turfs = block(locate(OVERMAP_EDGE, OVERMAP_EDGE, z_level), locate(overmap_size - OVERMAP_EDGE, overmap_size - OVERMAP_EDGE, z_level)) + var/list/candidate_turfs = list() + for(var/Trf in overmap_turfs) + var/turf/T = Trf + if(!(locate(/obj/effect/overmap/visitable) in T)) + candidate_turfs += T + + for(var/i = 1 to number_of_events) + if(!candidate_turfs.len) + break + var/overmap_event_type = pick(subtypesof(/datum/overmap_event)) + var/datum/overmap_event/datum_spawn = new overmap_event_type + log_debug("Generating cloud of [datum_spawn.count] [datum_spawn] overmap event hazards") + + 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? + +/decl/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 + +/decl/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 = trange(range, origin_turf) + fitting_turfs = shuffle(fitting_turfs) + for(var/turf/T in fitting_turfs) + if(T in candidate_turfs) + return T + +/decl/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) + E.startWhen = 0 + E.endWhen = INFINITY + // TODO - Leshana - Note: event.setup() is called before these are set! + E.affecting_z = ship.map_z + E.victim = ship + LAZYADD(ship_events[ship], E) + +/decl/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) + +/decl/overmap_event_handler/proc/is_event_active(var/ship, var/event_type, var/severity) + if(!ship_events[ship]) return + for(var/datum/event/E in ship_events[ship]) + if(E.type == event_type && E.severity == severity) + return E + +/decl/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) + +/decl/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 // If new turf has the same event as well... keep it going! + stop_hazard(ship, E) + +/decl/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 + else + hazard_by_turf |= T + hazard_by_turf[T] = active_hazards + + for(var/obj/effect/overmap/visitable/ship/ship in T) + for(var/datum/event/E in ship_events[ship]) + 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) + +/decl/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 + +/decl/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 // TODO - Improve this SPAGHETTI CODE! Done only when called from update_hazards. ~Leshana + else + if(A.difficulty == E.difficulty) + return TRUE diff --git a/code/modules/overmap/events/generation.dm b/code/modules/overmap/events/generation.dm new file mode 100644 index 0000000000..9b70308866 --- /dev/null +++ b/code/modules/overmap/events/generation.dm @@ -0,0 +1,49 @@ +/* +** /datum/overmap_event - Descriptors of how/what to spawn during overmap event generation +*/ + +//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 // Radius of the spawn circle around chosen epicenter + var/count = 6 // How many hazards to spawn + var/hazards // List (or single) typepath of hazard to spawn + var/continuous = TRUE //if it should form continous blob, or can have gaps + +/datum/overmap_event/meteor + name = "asteroid field" + count = 15 + radius = 4 + continuous = FALSE + hazards = /obj/effect/overmap/event/meteor + +/datum/overmap_event/electric + name = "electrical storm" + count = 11 + radius = 3 + hazards = /obj/effect/overmap/event/electric + +/datum/overmap_event/dust + name = "dust cloud" + count = 16 + radius = 4 + hazards = /obj/effect/overmap/event/dust + +/datum/overmap_event/ion + name = "ion cloud" + count = 8 + radius = 3 + hazards = /obj/effect/overmap/event/ion + +/datum/overmap_event/carp + name = "carp shoal" + count = 8 + radius = 3 + continuous = FALSE + hazards = /obj/effect/overmap/event/carp + +/datum/overmap_event/carp/major + name = "carp school" + count = 5 + radius = 4 + hazards = /obj/effect/overmap/event/carp/major \ No newline at end of file diff --git a/code/modules/overmap/events/overmap_event.dm b/code/modules/overmap/events/overmap_event.dm new file mode 100644 index 0000000000..091146dfd9 --- /dev/null +++ b/code/modules/overmap/events/overmap_event.dm @@ -0,0 +1,86 @@ +/* +** /obj/effect/overmap/event - Actual instances of event hazards on the overmap map +*/ + +// 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.dmi' + icon_state = "event" + opacity = 1 + var/list/events // List of event datum paths + var/list/event_icon_states // Randomly picked from + var/difficulty = EVENT_LEVEL_MODERATE + var/weaknesses //if the BSA can destroy them and with what + var/list/victims //basically cached events on which Z level + +/obj/effect/overmap/event/Initialize() + . = ..() + icon_state = pick(event_icon_states) + GLOB.overmap_event_handler.update_hazards(loc) + +/obj/effect/overmap/event/Move() + var/turf/old_loc = loc + . = ..() + if(.) + GLOB.overmap_event_handler.update_hazards(old_loc) + GLOB.overmap_event_handler.update_hazards(loc) + +/obj/effect/overmap/event/forceMove(atom/destination) + var/old_loc = loc + . = ..() + if(.) + GLOB.overmap_event_handler.update_hazards(old_loc) + GLOB.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 + var/turf/T = loc + . = ..() + GLOB.overmap_event_handler.update_hazards(T) + +// +// Definitions for specific types! +// + +/obj/effect/overmap/event/meteor + name = "asteroid field" + events = list(/datum/event/meteor_wave/overmap) + event_icon_states = list("meteor1", "meteor2", "meteor3", "meteor4") + difficulty = EVENT_LEVEL_MAJOR + weaknesses = OVERMAP_WEAKNESS_MINING | OVERMAP_WEAKNESS_EXPLOSIVE + +/obj/effect/overmap/event/electric + name = "electrical storm" + events = list(/datum/event/electrical_storm) + opacity = 0 + event_icon_states = list("electrical1", "electrical2", "electrical3", "electrical4") + difficulty = EVENT_LEVEL_MAJOR + weaknesses = OVERMAP_WEAKNESS_EMP + +/obj/effect/overmap/event/dust + name = "dust cloud" + events = list(/datum/event/dust) + event_icon_states = list("dust1", "dust2", "dust3", "dust4") + weaknesses = OVERMAP_WEAKNESS_MINING | OVERMAP_WEAKNESS_EXPLOSIVE | OVERMAP_WEAKNESS_FIRE + +/obj/effect/overmap/event/ion + name = "ion cloud" + events = list(/datum/event/ionstorm) + opacity = 0 + event_icon_states = list("ion1", "ion2", "ion3", "ion4") + difficulty = EVENT_LEVEL_MAJOR + weaknesses = OVERMAP_WEAKNESS_EMP + +/obj/effect/overmap/event/carp + name = "carp shoal" + events = list(/datum/event/carp_migration/overmap) + opacity = 0 + difficulty = EVENT_LEVEL_MODERATE + event_icon_states = list("carp1", "carp2") + weaknesses = OVERMAP_WEAKNESS_EXPLOSIVE | OVERMAP_WEAKNESS_FIRE + +/obj/effect/overmap/event/carp/major + name = "carp school" + difficulty = EVENT_LEVEL_MAJOR + event_icon_states = list("carp3", "carp4") diff --git a/code/modules/overmap/turfs.dm b/code/modules/overmap/turfs.dm new file mode 100644 index 0000000000..952865878e --- /dev/null +++ b/code/modules/overmap/turfs.dm @@ -0,0 +1,72 @@ +//Dimension of overmap (squares 4 lyfe) +var/global/list/map_sectors = list() + +/area/overmap/ + name = "System Map" + icon_state = "start" + requires_power = 0 + base_turf = /turf/unsimulated/map + +/turf/unsimulated/map + icon = 'icons/turf/space.dmi' + icon_state = "map" + initialized = FALSE // TODO - Fix unsimulated turf initialization so this override is not necessary! + +/turf/unsimulated/map/edge + opacity = 1 + density = 1 + +/turf/unsimulated/map/Initialize() + . = ..() + name = "[x]-[y]" + var/list/numbers = list() + + if(x == 1 || x == global.using_map.overmap_size) + numbers += list("[round(y/10)]","[round(y%10)]") + if(y == 1 || y == global.using_map.overmap_size) + numbers += "-" + if(y == 1 || y == global.using_map.overmap_size) + numbers += list("[round(x/10)]","[round(x%10)]") + + for(var/i = 1 to numbers.len) + var/image/I = image('icons/effects/numbers.dmi',numbers[i]) + I.pixel_x = 5*i - 2 + I.pixel_y = world.icon_size/2 - 3 + if(y == 1) + I.pixel_y = 3 + I.pixel_x = 5*i + 4 + if(y == global.using_map.overmap_size) + I.pixel_y = world.icon_size - 9 + I.pixel_x = 5*i + 4 + if(x == 1) + I.pixel_x = 5*i - 2 + if(x == global.using_map.overmap_size) + I.pixel_x = 5*i + 2 + add_overlay(I) + +/turf/unsimulated/map/Entered(var/atom/movable/O, var/atom/oldloc) + ..() + if(istype(O, /obj/effect/overmap/visitable/ship)) + GLOB.overmap_event_handler.on_turf_entered(src, O, oldloc) + +/turf/unsimulated/map/Exited(var/atom/movable/O, var/atom/newloc) + ..() + if(istype(O, /obj/effect/overmap/visitable/ship)) + GLOB.overmap_event_handler.on_turf_exited(src, O, newloc) + +//list used to track which zlevels are being 'moved' by the proc below +var/list/moving_levels = list() +//Proc to 'move' stars in spess +//yes it looks ugly, but it should only fire when state actually change. +//null direction stops movement +proc/toggle_move_stars(zlevel, direction) + if(!zlevel) + return + + if (moving_levels["[zlevel]"] != direction) + moving_levels["[zlevel]"] = direction + + var/list/spaceturfs = block(locate(1, 1, zlevel), locate(world.maxx, world.maxy, zlevel)) + for(var/turf/space/T in spaceturfs) + T.toggle_transit(direction) + CHECK_TICK diff --git a/vorestation.dme b/vorestation.dme index b2390f9998..7dba23bc88 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -62,6 +62,7 @@ #include "code\__defines\mobs_vr.dm" #include "code\__defines\nifsoft.dm" #include "code\__defines\objects.dm" +#include "code\__defines\overmap.dm" #include "code\__defines\planets.dm" #include "code\__defines\process_scheduler.dm" #include "code\__defines\qdel.dm" @@ -2872,11 +2873,14 @@ #include "code\modules\organs\subtypes\vox.dm" #include "code\modules\organs\subtypes\vox_vr.dm" #include "code\modules\organs\subtypes\xenos.dm" -#include "code\modules\overmap\_defines.dm" #include "code\modules\overmap\overmap_object.dm" #include "code\modules\overmap\overmap_shuttle.dm" #include "code\modules\overmap\sectors.dm" #include "code\modules\overmap\spacetravel.dm" +#include "code\modules\overmap\turfs.dm" +#include "code\modules\overmap\events\event_handler.dm" +#include "code\modules\overmap\events\generation.dm" +#include "code\modules\overmap\events\overmap_event.dm" #include "code\modules\overmap\ships\landable.dm" #include "code\modules\overmap\ships\ship.dm" #include "code\modules\overmap\ships\computers\computer_shims.dm"