diff --git a/.drone.yml b/.drone.yml index ce3c6f52a58..67e19d57d89 100644 --- a/.drone.yml +++ b/.drone.yml @@ -9,7 +9,7 @@ clone: environment: MACRO_COUNT: 0 GENDER_COUNT: 6 - TO_WORLD_COUNT: 211 + TO_WORLD_COUNT: 209 trigger: branch: @@ -175,6 +175,6 @@ steps: --- kind: signature -hmac: dff04fdff79e6ed2acdbe84a55f4c13f57655f345f56be03bf6d9c433cb65422 +hmac: aa5f81d300bb1f01656d04254a788970d593b10792ac97b91161ec95ce622b1a ... diff --git a/.travis.yml b/.travis.yml index 96bfa28da93..bf895556930 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ env: BYOND_MINOR="1517" MACRO_COUNT=0 GENDER_COUNT=6 - TO_WORLD_COUNT=211 + TO_WORLD_COUNT=209 FLYWAY_BUILD="5.2.4" NODE_VERSION=10 RUST_G_VERSION="0.4.2+a2" diff --git a/aurorastation.dme b/aurorastation.dme index 9c6cb19dac5..072763f8211 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -550,7 +550,6 @@ #include "code\game\gamemodes\endgame\supermatter_cascade\universe.dm" #include "code\game\gamemodes\events\black_hole.dm" #include "code\game\gamemodes\events\clang.dm" -#include "code\game\gamemodes\events\dust.dm" #include "code\game\gamemodes\events\power_failure.dm" #include "code\game\gamemodes\events\wormholes.dm" #include "code\game\gamemodes\events\holidays\Christmas.dm" @@ -1724,7 +1723,6 @@ #include "code\modules\events\ccia_general_notice.dm" #include "code\modules\events\comms_blackout.dm" #include "code\modules\events\communications_blackout.dm" -#include "code\modules\events\dust.dm" #include "code\modules\events\electrical_storm.dm" #include "code\modules\events\event.dm" #include "code\modules\events\event_container.dm" @@ -2509,6 +2507,7 @@ #include "code\modules\overmap\README.dm" #include "code\modules\overmap\sectors.dm" #include "code\modules\overmap\spacetravel.dm" +#include "code\modules\overmap\events\event.dm" #include "code\modules\overmap\ships\landable.dm" #include "code\modules\overmap\ships\ship.dm" #include "code\modules\overmap\ships\computers\engine_control.dm" diff --git a/code/__defines/lists.dm b/code/__defines/lists.dm index 0fd4677eb3f..2df9fdd950f 100644 --- a/code/__defines/lists.dm +++ b/code/__defines/lists.dm @@ -1,7 +1,7 @@ #define LAZYINITLIST(L) if (!L) L = list() #define UNSETEMPTY(L) if (L && !L.len) L = null -#define LAZYREMOVE(L, I) if(L) { L -= I; if(!L.len) { L = null; } } +#define LAZYREMOVE(L, I) if(L) { L -= I; if(!length(L)) { L = null; } } #define LAZYADD(L, I) if(!L) { L = list(); } L += I; #define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= L.len ? L[I] : null) : L[I]) : null) #define LAZYLEN(L) length(L) diff --git a/code/__defines/space_sectors.dm b/code/__defines/space_sectors.dm index a9712a81b90..8fa686928c9 100644 --- a/code/__defines/space_sectors.dm +++ b/code/__defines/space_sectors.dm @@ -13,4 +13,7 @@ #define SECTOR_NRRAHRAHUL "Nrrahrahul" #define SECTOR_GAKAL "Gakal" #define SECTOR_UUEOAESA "Uueoa-Esa" -#define ALL_BADLAND_SECTORS list(SECTOR_VALLEY_HALE, SECTOR_BADLANDS, SECTOR_NEW_ANKARA, SECTOR_AEMAQ, SECTOR_SRANDMARR, SECTOR_NRRAHRAHUL, SECTOR_GAKAL, SECTOR_UUEOAESA) \ No newline at end of file +#define ALL_BADLAND_SECTORS list(SECTOR_VALLEY_HALE, SECTOR_BADLANDS, SECTOR_NEW_ANKARA, SECTOR_AEMAQ, SECTOR_SRANDMARR, SECTOR_NRRAHRAHUL, SECTOR_GAKAL, SECTOR_UUEOAESA) + + +#define ALL_POSSIBLE_SECTORS list(ALL_TAU_CETI_SECTORS, ALL_BADLAND_SECTORS) \ No newline at end of file diff --git a/code/_helpers/functional.dm b/code/_helpers/functional.dm index 5de219459db..7fbf9518337 100644 --- a/code/_helpers/functional.dm +++ b/code/_helpers/functional.dm @@ -29,5 +29,21 @@ if(LAZYLEN(extra_arguments)) {\ return TRUE return FALSE +/proc/can_not_locate(var/atom/container, var/container_thing) + return !(locate(container_thing) in container) // We could just do !can_locate(container, container_thing) but BYOND is pretty awful when it comes to deep proc calls + +/proc/where(var/list/list_to_filter, var/list/predicates, var/list/extra_predicate_input) + . = list() + for(var/entry in list_to_filter) + var/predicate_input + if(extra_predicate_input) + predicate_input = (list(entry) + extra_predicate_input) + else + predicate_input = entry + + if(all_predicates_true(predicate_input, predicates)) + . += entry + + #undef PREPARE_ARGUMENTS #undef PREPARE_INPUT diff --git a/code/controllers/subsystems/event.dm b/code/controllers/subsystems/event.dm index d045825c394..a36fb8a1afc 100644 --- a/code/controllers/subsystems/event.dm +++ b/code/controllers/subsystems/event.dm @@ -40,6 +40,9 @@ var/datum/controller/subsystem/events/SSevents ) initialized = TRUE + if(current_map.use_overmap) + overmap_event_handler.create_events(current_map.overmap_z, current_map.overmap_size, current_map.overmap_event_areas) + /datum/controller/subsystem/events/Recover() active_events = SSevents.active_events finished_events = SSevents.finished_events diff --git a/code/game/gamemodes/events/dust.dm b/code/game/gamemodes/events/dust.dm deleted file mode 100644 index 258ac9d6aff..00000000000 --- a/code/game/gamemodes/events/dust.dm +++ /dev/null @@ -1,120 +0,0 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -/* -Space dust -Commonish random event that causes small clumps of "space dust" to hit the station at high speeds. -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") - var/numbers = 1 - switch(strength) - if("weak") - numbers = rand(2,4) - for(var/i = 0 to numbers) - new/obj/effect/space_dust/weak() - if("norm") - numbers = rand(5,10) - for(var/i = 0 to numbers) - new/obj/effect/space_dust() - if("strong") - numbers = rand(10,15) - for(var/i = 0 to numbers) - new/obj/effect/space_dust/strong() - if("super") - numbers = rand(15,25) - for(var/i = 0 to numbers) - new/obj/effect/space_dust/super() - return - - -/obj/effect/space_dust - name = "Space Dust" - desc = "Dust in space." - icon = 'icons/obj/meteor.dmi' - icon_state = "dust" - density = 1 - anchored = 1 - var/strength = 2 //ex_act severity number - var/life = 2 //how many things we hit before qdel(src) - - weak - strength = 3 - life = 1 - - strong - strength = 1 - life = 6 - - super - strength = 1 - life = 40 - - - New() - ..() - var/startx = 0 - var/starty = 0 - var/endy = 0 - var/endx = 0 - var/startside = pick(cardinal) - - 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 - var/z_level = pick(current_map.station_levels) - 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 - - touch_map_edge() - qdel(src) - - Collide(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 - - ex_act(severity) - qdel(src) - return diff --git a/code/game/gamemodes/meteor/meteor.dm b/code/game/gamemodes/meteor/meteor.dm index a3e99845cb3..aa8a5a3c193 100644 --- a/code/game/gamemodes/meteor/meteor.dm +++ b/code/game/gamemodes/meteor/meteor.dm @@ -1,45 +1,100 @@ -#define METEOR_DELAY 6000 +// The following four defines can be used to tweak the difficulty of the gamemode +#define METEOR_FAILSAFE_THRESHOLD 45 MINUTES // Failsafe that guarantees Severity will be at least 15 when the round hits this time. +// In general, a PVE oriented game mode. A middle ground between Extended and actual antagonist based rounds. /datum/game_mode/meteor name = "Meteor" - round_description = "The space station has been stuck in a major meteor shower." - extended_round_description = "The station is on an unavoidable collision course with an asteroid field. The station will be continuously slammed with meteors, venting hallways, rooms, and ultimately destroying a majority of the basic life functions of the entire structure. Coordinate with your fellow crew members to survive the inevitable destruction of the station and get back home in one piece!" + round_description = "You are about to enter an asteroid belt!" + extended_round_description = "We are on an unavoidable collision course with an asteroid field. You have only a moment to prepare before you are barraged by dust and meteors. As if it was not enough, all kinds of negative events seem to happen more frequently. Good Luck." config_tag = "meteor" - required_players = 0 + required_players = 15 // Definitely not good for low-pop votable = 0 - deny_respawn = 1 - var/next_wave = METEOR_DELAY + shuttle_delay = 2 + var/next_wave = INFINITY // Set in post_setup() correctly to take into account potential longer pre-start times. + var/alert_sent = 0 + var/meteor_severity = 1 // Slowly increases the tension at the beginning of meteor strikes. Prevents "tunguska on first wave" style problems. + var/failsafe_triggered = 0 + var/alert_title + var/alert_text + var/start_text + var/maximal_severity = 40 + var/meteor_wave_delay = 30 SECONDS //minimum wait between waves in tenths of seconds + var/meteor_grace_period = 15 MINUTES //waves will not arrive until this far into round + + // Moved these from defines to variables, to allow for in-round tweaking via varedit: + var/escalation_probability = 45 + var/send_admin_broadcasts = TRUE // Enables debugging/information mode, sending admin messages when waves occur and when severity escalates. + + event_delay_mod_moderate = 0.5 // As a bonus, more frequent events. + event_delay_mod_major = 0.3 + + +/datum/game_mode/meteor/proc/set_meteor_severity(value) + meteor_severity = Clamp(value, 0, maximal_severity) + +/datum/game_mode/meteor/proc/set_meteor_wave_delay(value) + meteor_wave_delay = max(10 SECONDS, value) + +/datum/game_mode/meteor/post_setup() + ..() + alert_title = "Automated Beacon AB-[rand(10, 99)]" + alert_text = "This is an automatic warning. Your facility: [current_map.full_name] is on a collision course with a nearby asteroid belt. Estimated time until impact is: [meteor_grace_period / 1200] MINUTES. Please perform necessary actions to secure your ship or station from the threat. Have a nice day." + start_text = "This is an automatic warning. Your facility: [current_map.full_name] has entered an asteroid belt. Estimated time until you leave the belt is: [rand(20,30)] HOURS and [rand(1, 59)] MINUTES. For your safety, please consider changing course or using protective equipment. Have a nice day." + next_wave = round_duration_in_ticks + meteor_grace_period + +/datum/game_mode/meteor/proc/on_meteor_warn() + alert_sent = 1 + command_announcement.Announce(alert_text, alert_title) + +/datum/game_mode/meteor/proc/on_enter_field() + alert_sent = 2 + command_announcement.Announce(start_text, alert_title) + if(current_map.use_overmap) + var/area/map = locate(/area/overmap) + for(var/turf/T in map) + T.overlays += image('icons/obj/overmap.dmi', "meteor[rand(1,4)]") + next_wave = round_duration_in_ticks + meteor_wave_delay /datum/game_mode/meteor/process() - if(world.time >= next_wave) - next_wave = world.time + meteor_wave_delay - spawn() spawn_meteors(6) + // Send an alert halfway through the round. + if((round_duration_in_ticks >= (next_wave / 2)) && !alert_sent) + on_meteor_warn() + // And then another one when the meteors start flying around. + if((round_duration_in_ticks >= next_wave) && (alert_sent == 1)) + on_enter_field() + if((round_duration_in_ticks >= METEOR_FAILSAFE_THRESHOLD) && (meteor_severity < 15) && !failsafe_triggered) + log_and_message_admins("Meteor mode severity failsafe triggered: Severity forced to 15.") + meteor_severity = 15 + failsafe_triggered = 1 -/datum/game_mode/meteor/declare_completion() - var/text - var/survivors = 0 - for(var/mob/living/player in player_list) - if(player.stat != DEAD) - var/turf/location = get_turf(player.loc) - if(!location) - continue - if(istype(location.loc.type, /area/shuttle/escape)) - text += "
[player.real_name] escaped on the emergency shuttle" - if(istype(location.loc.type, /area/shuttle/escape_pod)) - text += "
[player.real_name] escaped in a life pod." - else - text += "
[player.real_name] survived but is stranded without any hope of rescue." - survivors++ + if(round_duration_in_ticks >= next_wave) + next_wave = round_duration_in_ticks + meteor_wave_delay + // Starts as barely noticeable dust impact, ends as barrage of most severe meteor types the code has to offer. Have fun. + spawn() + spawn_meteors(meteor_severity, get_meteor_types(), pick(cardinal), pick(current_map.station_levels)) + var/escalated = FALSE + if(prob(escalation_probability) && (meteor_severity < maximal_severity)) + meteor_severity++ + escalated = TRUE + if(send_admin_broadcasts) + log_and_message_admins("Meteor: Wave fired. Escalation: [escalated ? "Yes" : "No"]. Severity: [meteor_severity]/[maximal_severity]") - if(survivors) - to_world("The following survived the meteor storm:[text]") - else - to_world("Nobody survived the meteor storm!") +/datum/game_mode/meteor/proc/get_meteor_types() + switch(meteor_severity) + if(1 to 9) + return meteors_dust + if(10 to 19) + return meteors_normal + if(20 to 29) + return meteors_threatening + if(30 to 34) + return meteors_catastrophic + if(35 to 39) + return meteors_armageddon + if(40 to INFINITY) + return meteors_cataclysm + // Just in case we /somehow/ get here (looking at you, varedit) + return meteors_normal - feedback_set_details("round_end_result","end - evacuation") - feedback_set("round_end_result",survivors) - ..() - return 1 - -#undef METEOR_DELAY +#undef METEOR_FAILSAFE_THRESHOLD diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm index ce44ea93f00..d30821024c9 100644 --- a/code/game/gamemodes/meteor/meteors.dm +++ b/code/game/gamemodes/meteor/meteors.dm @@ -1,158 +1,171 @@ -/var/const/meteor_wave_delay = 625 //minimum wait between waves in tenths of seconds -//set to at least 100 unless you want evarr ruining every round +//Meteor groups, used for various random events and the Meteor gamemode. -/var/const/meteors_in_wave = 50 -/var/const/meteors_in_small_wave = 10 +// Dust, used by space dust event and during earliest stages of meteor mode. +/var/list/meteors_dust = list(/obj/effect/meteor/dust) +// Standard meteors, used during early stages of the meteor gamemode. +/var/list/meteors_normal = list(\ + /obj/effect/meteor/medium=8,\ + /obj/effect/meteor/dust=3,\ + /obj/effect/meteor/irradiated=3,\ + /obj/effect/meteor/big=3,\ + /obj/effect/meteor/flaming=1,\ + /obj/effect/meteor/golden=1,\ + /obj/effect/meteor/silver=1\ + ) -/proc/spawn_meteors(var/number = meteors_in_small_wave) +// Threatening meteors, used during the meteor gamemode. +/var/list/meteors_threatening = list(\ + /obj/effect/meteor/big=10,\ + /obj/effect/meteor/medium=5,\ + /obj/effect/meteor/golden=3,\ + /obj/effect/meteor/silver=3,\ + /obj/effect/meteor/flaming=3,\ + /obj/effect/meteor/irradiated=3,\ + /obj/effect/meteor/emp=3\ + ) + +// Catastrophic meteors, pretty dangerous without shields and used during the meteor gamemode. +/var/list/meteors_catastrophic = list(\ + /obj/effect/meteor/big=75,\ + /obj/effect/meteor/flaming=10,\ + /obj/effect/meteor/irradiated=10,\ + /obj/effect/meteor/emp=10,\ + /obj/effect/meteor/medium=5,\ + /obj/effect/meteor/golden=4,\ + /obj/effect/meteor/silver=4 + ) + +// Armageddon meteors, very dangerous, and currently used only during the meteor gamemode. +/var/list/meteors_armageddon = list(\ + /obj/effect/meteor/big=25,\ + /obj/effect/meteor/flaming=10,\ + /obj/effect/meteor/irradiated=10,\ + /obj/effect/meteor/emp=10,\ + /obj/effect/meteor/medium=3,\ + /obj/effect/meteor/golden=2,\ + /obj/effect/meteor/silver=2\ + ) + +// Cataclysm meteor selection. Very very dangerous and effective even against shields. Used in late game meteor gamemode only. +/var/list/meteors_cataclysm = list(\ + /obj/effect/meteor/big=40,\ + /obj/effect/meteor/emp=20,\ + /obj/effect/meteor/irradiated=10,\ + /obj/effect/meteor/golden=10,\ + /obj/effect/meteor/silver=10,\ + /obj/effect/meteor/flaming=10,\ + /obj/effect/meteor/supermatter=1\ + ) + +/proc/spawn_meteors(var/number = 10, var/list/meteortypes, var/startSide, var/zlevel) for(var/i = 0; i < number; i++) - spawn(0) - spawn_meteor() + spawn_meteor(meteortypes, startSide, zlevel) -/proc/spawn_meteor(var/ship_debris_only = FALSE) - var/startx +/proc/spawn_meteor(var/list/meteortypes, var/startSide, var/zlevel) + var/turf/pickedstart = spaceDebrisStartLoc(startSide, zlevel) + var/turf/pickedgoal = spaceDebrisFinishLoc(startSide, zlevel) + + var/Me = pickweight(meteortypes) + var/obj/effect/meteor/M = new Me(pickedstart) + M.dest = pickedgoal + spawn(0) + walk_towards(M, M.dest, 3) + return + +/proc/spaceDebrisStartLoc(startSide, Z) var/starty - var/endx - var/endy - var/turf/pickedstart - var/turf/pickedgoal - var/max_i = 10//number of tries to spawn meteor. + var/startx + switch(startSide) + if(NORTH) + starty = world.maxy-(TRANSITIONEDGE+1) + startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) + if(EAST) + starty = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1)) + startx = world.maxx-(TRANSITIONEDGE+1) + if(SOUTH) + starty = (TRANSITIONEDGE+1) + startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) + if(WEST) + starty = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1)) + startx = (TRANSITIONEDGE+1) + var/turf/T = locate(startx, starty, Z) + return T - switch(pick(1,2,3,4)) - if(1) //NORTH - starty = world.maxy-(TRANSITIONEDGE+2) - startx = rand((TRANSITIONEDGE+2), world.maxx-(TRANSITIONEDGE+2)) +/proc/spaceDebrisFinishLoc(startSide, Z) + var/endy + var/endx + switch(startSide) + if(NORTH) endy = TRANSITIONEDGE endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) - if(2) //EAST - starty = rand((TRANSITIONEDGE+2),world.maxy-(TRANSITIONEDGE+1)) - startx = world.maxx-(TRANSITIONEDGE+2) + if(EAST) endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE) endx = TRANSITIONEDGE - if(3) //SOUTH - starty = (TRANSITIONEDGE+2) - startx = rand((TRANSITIONEDGE+2), world.maxx-(TRANSITIONEDGE+2)) + if(SOUTH) endy = world.maxy-TRANSITIONEDGE endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) - if(4) //WEST - starty = rand((TRANSITIONEDGE+2), world.maxy-(TRANSITIONEDGE+2)) - startx = (TRANSITIONEDGE+2) + if(WEST) endy = rand(TRANSITIONEDGE,world.maxy-TRANSITIONEDGE) endx = world.maxx-TRANSITIONEDGE - - pickedstart = locate(startx, starty, 6) - pickedgoal = locate(endx, endy, 6) - max_i-- - if(max_i<=0) return - - var/obj/effect/meteor/M - if(ship_debris_only) - M = new /obj/effect/meteor/ship_debris(pickedstart) - else - switch(rand(1, 100)) - if(1 to 10) - M = new /obj/effect/meteor/big(pickedstart) - if(11 to 50) - M = new /obj/effect/meteor(pickedstart) - if(51 to 55) - M = new /obj/effect/meteor/flaming(pickedstart) - if(56 to 59) - M = new /obj/effect/meteor/irradiated(pickedstart) - if(60 to 64) - M = new /obj/effect/meteor/golden(pickedstart) - if(65 to 69) - M = new /obj/effect/meteor/silver(pickedstart) - if(70 to 73) - M = new /obj/effect/meteor/emp(pickedstart) - if(74 to 76) - M = new /obj/effect/meteor/diamond(pickedstart) - if(77 to 78) - M = new /obj/effect/meteor/artifact(pickedstart) - if(79 to 80) - M = new /obj/effect/meteor/supermatter(pickedstart) - if(81 to 82) - M = new /obj/effect/meteor/meaty(pickedstart) - if(83 to 100) - M = new /obj/effect/meteor/small(pickedstart) - - M.dest = pickedgoal - spawn(1) - walk_towards(M, M.dest, 2) + var/turf/T = locate(endx, endy, Z) + return T /obj/effect/meteor name = "meteor" icon = 'icons/obj/meteor.dmi' icon_state = "large" - density = 1 - anchored = 1.0 - var/hits = 3 - var/detonation_chance = 50 - var/power = 2 - var/power_step = 0.75 - var/dest - var/shieldsoundrange = 260 // The maximum number of tiles away the sound can be heard, falls off over distance, so it will be quiet near the limit + density = TRUE + anchored = TRUE pass_flags = PASSTABLE - var/done = 0//This is set to 1 when the meteor is done colliding, and is used to ignore additional bumps while waiting for deletion + + var/hits = 4 + var/hitpwr = 2 //Level of ex_act to be called on hit. + var/dest + var/heavy = FALSE + var/z_original + var/meteor_loot = list(/obj/item/ore/iron) //the thing that the meteors will drop when it explodes var/dropamt = 3 //amount of said thing -/obj/effect/meteor/small - name = "small meteor" - icon_state = "small" - pass_flags = PASSTABLE | PASSGRILLE - power = 1 - power_step = 0.5 - hits = 2 - detonation_chance = 30 - shieldsoundrange = 160 - dropamt = 1 /obj/effect/meteor/Destroy() walk(src,0) //this cancels the walk_towards() proc return ..() /obj/effect/meteor/Collide(atom/A) - if (!done) - spawn(0) + ..() + if(A && !QDELETED(src)) // Prevents explosions and other effects when we were deleted by whatever we Bumped() - currently used by shields. + ram_turf(get_turf(A)) + get_hit() //should only get hit once per move attempt - if (A) - A.ex_act(2) - playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, 1) - make_debris() - meteor_effect() +/obj/effect/meteor/proc/ram_turf(var/turf/T) + //first bust whatever is in the turf + for(var/atom/A in T) + if(A != src && !A.CanPass(src, src.loc, 0.5, 0)) //only ram stuff that would actually block us + A.ex_act(hitpwr) - if (istype(A, /obj/effect/energy_field))//If a normal/small meteor impacts an energy field, then it makes a widely audible impact sound and qdels - done = 1 - hits = 0 - power *= 0.5 - power_step *= 0.5 - var/turf/T = src.loc - if (!T) - T = A.loc + //then, ram the turf if it still exists + if(T && !T.CanPass(src, src.loc, 0.5, 0)) + T.ex_act(hitpwr) - if (T)//We have a double safety check on T to prevent runtime errors - meteor_shield_impact_sound(T, shieldsoundrange) - msg_admin_attack("Meteor impacted energy field at coords (JMP)") - spawn()//Delaying the Qdel a frame provides a little more safety - qdel(src) +/obj/effect/meteor/proc/get_hit() + hits-- + if(hits <= 0) + make_debris() + meteor_effect() + qdel(src) - if (--src.hits == 0 && !done) - //Prevent meteors from blowing up the singularity's containment. - //Changing emitter and generator ex_act would result in them being bomb and C4 proof. - done = 1 - if(!istype(A,/obj/machinery/power/emitter) && \ - !istype(A,/obj/machinery/field_generator) && \ - prob(detonation_chance)) - explosion(loc, power, power + power_step, power + power_step * 2, power + power_step * 3, 0) - msg_admin_attack("Meteor exploded at coords (JMP)") - else - msg_admin_attack("Meteor dissipated without exploding at coords (JMP)") - spawn() - qdel(src) +/obj/effect/meteor/proc/meteor_effect() + if(heavy) + for(var/mob/M in player_list) + var/turf/T = get_turf(M) + if(!T || T.z != src.z) + continue + var/dist = get_dist(M.loc, src.loc) + shake_camera(M, dist > 20 ? 3 : 5, dist > 20 ? 1 : 3) /obj/effect/meteor/ex_act(severity) - if (severity < 4) qdel(src) return @@ -173,93 +186,58 @@ S.update_icon() O.throw_at(dest, 5, 10) -/obj/effect/meteor/proc/meteor_effect() - return +/obj/effect/meteor/touch_map_edge() + qdel(src) + +/obj/effect/meteor/medium + name = "meteor" + dropamt = 2 + +/obj/effect/meteor/medium/meteor_effect() + ..() + explosion(src.loc, 0, 1, 2, 3, 0) + msg_admin_attack("Meteor exploded at coords (JMP)") /obj/effect/meteor/big - name = "big meteor" - hits = 5 - power = 4 - power_step = 1 - detonation_chance = 60 - shieldsoundrange = 310//This can be set larger than the dimensions of the map, to allow it to remain louder at extreme distance - dropamt = 5 + name = "large meteor" + icon_state = "large" + hits = 6 + heavy = 1 + dropamt = 3 - ex_act(severity) - return - - Collide(atom/A) - if (!done) - spawn(0) - //Prevent meteors from blowing up the singularity's containment. - //Changing emitter and generator ex_act would result in them being bomb and C4 proof - if(!istype(A,/obj/machinery/power/emitter) && \ - !istype(A,/obj/machinery/field_generator)) - if(--src.hits <= 0) - qdel(src) //Dont blow up singularity containment if we get stuck there. - - if (istype(A, /obj/effect/energy_field))//If a big meteor impacts an energy field, then it detonates immediately with reduced power - done = 1 - hits = 0 - power *= 0.5 - power_step *= 0.5 - for(var/mob/M in player_list) - var/turf/T = get_turf(M) - if(!T || T.z != src.z) - continue - shake_camera(M, 3, get_dist(M.loc, src.loc) > 20 ? 1 : 3) - var/turf/T = src.loc - if (!T) - T = A.loc - - if (T) - meteor_shield_impact_sound(T, shieldsoundrange) - explosion(loc, power, power + power_step, power + power_step * 2, power + power_step * 3, 0) - msg_admin_attack("Large Meteor impacted energy field and then exploded at coords (JMP)") - spawn()//Have to delay the qdel a little, or the playsound will throw a runtime - qdel(src) - - else if (A) - for(var/mob/M in player_list) - var/turf/T = get_turf(M) - if(!T || T.z != src.z) - continue - shake_camera(M, 3, get_dist(M.loc, src.loc) > 20 ? 1 : 3) - playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, 1) - explosion(src.loc, 0, 1, 2, 3, 0) - make_debris() - meteor_effect() - - if (--src.hits == 0 && !done) - done = 1 - if(prob(detonation_chance) && !istype(A, /obj/structure/grille)) - explosion(loc, power, power + power_step, power + power_step * 2, power + power_step * 3, 0) - msg_admin_attack("Large Meteor exploded at coords (JMP)") - else - msg_admin_attack("Large Meteor dissipated without a final explosion at coords (JMP)") - spawn() - qdel(src) +/obj/effect/meteor/big/meteor_effect() + ..() + explosion(src.loc, 1, 2, 3, 4, 0) + msg_admin_attack("Meteor exploded at coords (JMP)") /obj/effect/meteor/dust name = "space dust" icon_state = "dust" pass_flags = PASSTABLE | PASSGRILLE - hits = 1 meteor_loot = list(/obj/item/ore/glass) dropamt = 1 + hits = 1 + hitpwr = 3 + /obj/effect/meteor/flaming name = "flaming meteor" icon_state = "flaming" hits = 3 meteor_loot = list(/obj/item/ore/phoron) +/obj/effect/meteor/flaming/meteor_effect() + ..() + explosion(src.loc, 1, 2, 3, 4, 0, 0, 5) + msg_admin_attack("Meteor exploded at coords (JMP)") + /obj/effect/meteor/irradiated name = "glowing meteor" icon_state = "glowing" meteor_loot = list(/obj/item/ore/uranium) /obj/effect/meteor/irradiated/meteor_effect() + explosion(src.loc, 0, 0, 4, 3, 0) new /obj/effect/decal/cleanable/greenglow(get_turf(src)) for(var/mob/living/L in view(5, src)) L.apply_damage(40, IRRADIATE, damage_flags = DAM_DISPERSED) @@ -308,6 +286,7 @@ explosion(src.loc, 1, 2, 3, 4, 0) for(var/obj/machinery/power/apc/A in range(rand(12, 20), src)) A.energy_fail(round(10 * rand(8, 12))) + msg_admin_attack("Meteor exploded at coords (JMP)") /obj/effect/meteor/meaty name = "meaty ore" diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index 841667d6a25..c5719f2d877 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -130,7 +130,7 @@ var/list/y_arr if(src.x <= 1) - if(istype(A, /obj/effect/meteor)||istype(A, /obj/effect/space_dust)) + if(istype(A, /obj/effect/meteor)) qdel(A) return @@ -187,7 +187,7 @@ A.loc.Entered(A) else if (src.y >= world.maxy) - if(istype(A, /obj/effect/meteor)||istype(A, /obj/effect/space_dust)) + if(istype(A, /obj/effect/meteor)) qdel(A) return var/list/cur_pos = src.get_global_map_pos() diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index b17157cbb1f..b315356b3c0 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -341,20 +341,22 @@ 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/turf/t in oview(src,1)) + 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 12d10b95620..11295714468 100644 --- a/code/modules/events/carp_migration.dm +++ b/code/modules/events/carp_migration.dm @@ -12,6 +12,7 @@ ) ic_name = "biological entities" + var/deploy_drones = TRUE /datum/event/carp_migration/setup() announceWhen = rand(40, 60) @@ -21,7 +22,7 @@ /datum/event/carp_migration/announce() var/announcement = "" var/soundfile = 'sound/AI/spacecarp.ogg' - if(severity == EVENT_LEVEL_MAJOR) + if(severity == EVENT_LEVEL_MAJOR && deploy_drones) announcement = "Massive migration of unknown biological entities has been detected near [station_name()], please stand-by. The NDV Icarus has dispatched combat drones to assist." soundfile = 'sound/AI/massivespacecarp.ogg' else @@ -30,11 +31,11 @@ /datum/event/carp_migration/start() if(severity == EVENT_LEVEL_MAJOR) - spawn_fish(length(landmarks_list), spawn_drones = TRUE) - spawn_caverndweller(length(landmarks_list), spawn_drones = TRUE) + spawn_fish(length(landmarks_list), spawn_drones = deploy_drones) + spawn_caverndweller(length(landmarks_list), spawn_drones = deploy_drones) else if(severity == EVENT_LEVEL_MODERATE) - spawn_fish(rand(4, 6), spawn_drones = TRUE) //12 to 30 carp, in small groups - spawn_caverndweller(rand(1, 2), spawn_drones = TRUE) //less of those, also don't happen in the regular event + spawn_fish(rand(4, 6), spawn_drones = deploy_drones) //12 to 30 carp, in small groups + spawn_caverndweller(rand(1, 2), spawn_drones = deploy_drones) //less of those, also don't happen in the regular event else spawn_fish(rand(1, 3), 1, 2) //1 to 6 carp, alone or in pairs @@ -119,4 +120,12 @@ for(var/j = 1, j <= group_size, j++) var/mob/living/simple_animal/cosmozoan/cozmo = new(spawn_locations[i]) spawned_carp += WEAKREF(cozmo) - i++ \ No newline at end of file + i++ + +/datum/event/carp_migration/overmap + despawn_turfs = list(/turf/space) + deploy_drones = FALSE + +/datum/event/carp_migration/overmap/setup() + announceWhen = 1 + despawn_turfs = typecacheof(despawn_turfs) \ No newline at end of file diff --git a/code/modules/events/dust.dm b/code/modules/events/dust.dm deleted file mode 100644 index ec9fc7c37bb..00000000000 --- a/code/modules/events/dust.dm +++ /dev/null @@ -1,23 +0,0 @@ -/datum/event/dust - startWhen = 10 - endWhen = 30 - ic_name = "space dust" - -/datum/event/dust/announce() - command_announcement.Announce("The station is now passing through a belt of space dust.", "Dust Alert", new_sound = 'sound/AI/spacedust.ogg') - -/datum/event/dust/start() - dust_swarm(get_severity()) - -/datum/event/dust/end() - command_announcement.Announce("The station has now passed through the belt of space dust.", "Dust Notice") - -/datum/event/dust/proc/get_severity() - switch(severity) - if(EVENT_LEVEL_MUNDANE) - return "weak" - if(EVENT_LEVEL_MODERATE) - return prob(80) ? "norm" : "strong" - if(EVENT_LEVEL_MAJOR) - return "super" - return "weak" diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm index 9958013cdca..e1c66ab262e 100644 --- a/code/modules/events/event_container.dm +++ b/code/modules/events/event_container.dm @@ -158,7 +158,7 @@ var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT new /datum/event_meta(EVENT_LEVEL_MODERATE, "Electrical Storm", /datum/event/electrical_storm, 50, list(ASSIGNMENT_ENGINEER = 15, ASSIGNMENT_JANITOR = 20)), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Gravity Failure", /datum/event/gravity, 100), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Ion Storm", /datum/event/ionstorm, 0, list(ASSIGNMENT_AI = 45, ASSIGNMENT_CYBORG = 25, ASSIGNMENT_ENGINEER = 6, ASSIGNMENT_SCIENTIST = 6)), - new /datum/event_meta(EVENT_LEVEL_MODERATE, "Meteor Shower", /datum/event/meteor_wave/shower, 40, list(ASSIGNMENT_ENGINEER = 13)), + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Meteor Shower", /datum/event/meteor_wave , 40, list(ASSIGNMENT_ENGINEER = 13)), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Prison Break", /datum/event/prison_break, 0, list(ASSIGNMENT_SECURITY = 15, ASSIGNMENT_CYBORG = 20),1), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Containment Error - Xenobiology", /datum/event/prison_break/xenobiology, 0, list(ASSIGNMENT_SCIENTIST = 15, ASSIGNMENT_CYBORG = 20),1), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Containment Error - Bridge", /datum/event/prison_break/bridge, 0, list(ASSIGNMENT_ENGINEER = 15, ASSIGNMENT_CYBORG = 20),1), diff --git a/code/modules/events/grid_check.dm b/code/modules/events/grid_check.dm index 77d62f5dc49..5e252ff817f 100644 --- a/code/modules/events/grid_check.dm +++ b/code/modules/events/grid_check.dm @@ -6,4 +6,4 @@ power_failure(0, severity) /datum/event/grid_check/announce() - command_announcement.Announce("Abnormal activity detected in [station_name()]'s powernet. As a precautionary measure, the station's power will be shut off for an indeterminate duration.", "Automated Grid Check", new_sound = 'sound/AI/poweroff.ogg') \ No newline at end of file + command_announcement.Announce("Abnormal activity detected in [station_name()]'s powernet. As a precautionary measure, the power will be shut off for an indeterminate duration.", "Automated Grid Check", new_sound = 'sound/AI/poweroff.ogg') \ No newline at end of file diff --git a/code/modules/events/meteors.dm b/code/modules/events/meteors.dm index 6c93ebf59a5..5dc318accf4 100644 --- a/code/modules/events/meteors.dm +++ b/code/modules/events/meteors.dm @@ -2,93 +2,147 @@ startWhen = 86 endWhen = 9999//safety value, will be set during ticks - var/wave_delay = 13//Note, wave delay is in procs. actual time is equal to wave_delay * 2.1 - var/min_waves = 11 - var/max_waves = 16 - var/min_meteors = 1 - var/max_meteors = 2 - var/duration = 340//Total duration in seconds that the storm will last after it starts + var/next_meteor = 40 + var/waves = 1 + var/start_side + var/next_meteor_lower = 10 + var/next_meteor_upper = 20 - var/downed_ship = FALSE - - var/waves = 8 - var/next_wave = 86 ic_name = "a meteor storm" /datum/event/meteor_wave/setup() - startWhen += rand(-15,15)//slightly randomised start time - waves = rand(min_waves,max_waves) - next_wave = startWhen - wave_delay = round(((duration - 10)/waves)/2.1, 1) + waves = 0 + for(var/n in 1 to severity) + waves += rand(5,15) + + start_side = pick(cardinal) + endWhen = worst_case_end() /datum/event/meteor_wave/announce() - command_announcement.Announce("A heavy meteor storm has been detected on collision course with the station. Estimated three minutes until impact, please activate station shields, and seek shelter in the central ring.", "Meteor Alert", new_sound = 'sound/AI/meteors.ogg') + command_announcement.Announce(current_map.meteors_detected_message, "Meteor Alert", new_sound = 'sound/AI/meteors.ogg') /datum/event/meteor_wave/start() - command_announcement.Announce("Contact with meteor wave imminent, all hands brace for impact.", "Meteor Alert") - -/datum/event/meteor_wave/tick() - if(activeFor >= next_wave) - var/amount = rand(min_meteors,max_meteors) - - event_meteor_wave(amount) - next_wave += wave_delay - waves-- - if(waves <= 0) - endWhen = activeFor + 1 - else - endWhen = next_wave + wave_delay + command_announcement.Announce(current_map.meteor_contact_message, "Meteor Alert") /datum/event/meteor_wave/end(var/faked) if(faked) return spawn(100)//We give 10 seconds before announcing, for the last wave of meteors to hit the station - command_announcement.Announce("The station has survived the meteor storm, it is now safe to commence repairs.", "Meteor Alert") + command_announcement.Announce(current_map.meteor_end_message, "Meteor Alert") -/datum/event/meteor_wave/shower - wave_delay = 6 - min_waves = 7 - max_waves = 9 - min_meteors = 0 - max_meteors = 1 - duration = 180 //Total duration in seconds that the storm will last after it starts +/datum/event/meteor_wave/tick() - waves = 4//this is randomised - next_wave = 86 + if(waves && activeFor >= next_meteor) + send_wave() -/datum/event/meteor_wave/shower/announce() - command_announcement.Announce("A meteor shower is approaching the station, estimated contact in three minutes. Crew are recommended to stay away from the outer areas of the station.", "Meteor Alert") +/datum/event/meteor_wave/proc/worst_case_end() + return activeFor + ((30 / severity) * waves) + 30 -/datum/event/meteor_wave/shower/start() - command_announcement.Announce("Meteors have reached the station. Please stay away from outer areas until the shower has passed.", "Meteor Alert") +/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(current_map.meteor_levels)) + 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,4) -/datum/event/meteor_wave/shower/end(var/faked) - if(faked) - return - spawn(100) - command_announcement.Announce("The station has cleared the meteor shower, please return to your stations.", "Meteor Alert") +/datum/event/meteor_wave/proc/get_meteors() + switch(severity) + if(EVENT_LEVEL_MAJOR) + return meteors_major + if(EVENT_LEVEL_MODERATE) + return meteors_moderate + else + return meteors_minor +/var/list/meteors_minor = list( + /obj/effect/meteor/medium = 80, + /obj/effect/meteor/dust = 30, + /obj/effect/meteor/irradiated = 30, + /obj/effect/meteor/big = 30, + /obj/effect/meteor/flaming = 10, + /obj/effect/meteor/golden = 10, + /obj/effect/meteor/silver = 10 +) -//An event specific version of the meteor wave proc, to bypass the delays -/datum/event/meteor_wave/proc/event_meteor_wave(var/number = meteors_in_wave) - for(var/i = 0 to number) - spawn(rand(10, 80)) - spawn_meteor(downed_ship) +/var/list/meteors_moderate = list( + /obj/effect/meteor/medium = 80, + /obj/effect/meteor/big = 30, + /obj/effect/meteor/dust = 30, + /obj/effect/meteor/irradiated = 30, + /obj/effect/meteor/flaming = 10, + /obj/effect/meteor/golden = 10, + /obj/effect/meteor/silver = 10, + /obj/effect/meteor/emp = 10 +) + +/var/list/meteors_major = list( + /obj/effect/meteor/medium = 80, + /obj/effect/meteor/big = 30, + /obj/effect/meteor/dust = 30, + /obj/effect/meteor/irradiated = 30, + /obj/effect/meteor/emp = 30, + /obj/effect/meteor/flaming = 10, + /obj/effect/meteor/golden = 10, + /obj/effect/meteor/silver = 10 +) + +/var/list/downed_ship_meteors = list( + /obj/effect/meteor/ship_debris = 90, + /obj/effect/meteor/dust = 10 +) /datum/event/meteor_wave/downed_ship - downed_ship = TRUE ic_name = "a downed vessel" no_fake = TRUE +/datum/event/meteor_wave/downed_ship/get_meteors() + return downed_ship_meteors + /datum/event/meteor_wave/downed_ship/announce() - command_announcement.Announce("The NDV Icarus reports that it has downed an unknown vessel that was approaching your station. Prepare for debris impact - please evacuate the surface level if needed.", "Ship Debris Alert", new_sound = 'sound/AI/unknownvesseldowned.ogg') + command_announcement.Announce(current_map.ship_meteor_end_message, "Ship Debris Alert", new_sound = 'sound/AI/unknownvesseldowned.ogg') /datum/event/meteor_wave/downed_ship/start() - command_announcement.Announce("Ship debris colliding now, all hands brace for impact.", "Ship Debris Alert") + command_announcement.Announce(current_map.ship_meteor_contact_message, "Ship Debris Alert") /datum/event/meteor_wave/downed_ship/end(var/faked) if(faked) return spawn(100)//We give 10 seconds before announcing, for the last wave of meteors to hit the station - command_announcement.Announce("The last of the ship debris has hit or passed by the station, it is now safe to commence repairs.", "Ship Debris Alert") \ No newline at end of file + command_announcement.Announce(current_map.ship_meteor_end_message, "Ship Debris Alert") + +/datum/event/meteor_wave/overmap + next_meteor_lower = 5 + next_meteor_upper = 10 + next_meteor = 0 + +/datum/event/meteor_wave/overmap/announce() + return + +/datum/event/meteor_wave/dust + ic_name = "a dust belt" + +/datum/event/meteor_wave/dust/announce() + command_announcement.Announce(current_map.dust_detected_message, "Dust Belt Alert") + +/datum/event/meteor_wave/dust/start() + command_announcement.Announce(current_map.dust_contact_message, "Dust Belt Alert") + +/datum/event/meteor_wave/dust/end(var/faked) + if(faked) + return + spawn(100)//We give 10 seconds before announcing, for the last wave of meteors to hit the station + command_announcement.Announce(current_map.dust_end_message, "Dust Belt Alert") + +/datum/event/meteor_wave/dust/get_meteors() + return meteors_dust + +/datum/event/meteor_wave/dust/overmap + next_meteor_lower = 5 + next_meteor_upper = 10 + next_meteor = 0 + +/datum/event/meteor_wave/dust/overmap/announce() + return diff --git a/code/modules/events/radiation_storm.dm b/code/modules/events/radiation_storm.dm index fc70ffb1d7c..4be1c2c2cb1 100644 --- a/code/modules/events/radiation_storm.dm +++ b/code/modules/events/radiation_storm.dm @@ -11,7 +11,7 @@ ic_name = "radiation" /datum/event/radiation_storm/announce() - command_announcement.Announce("High levels of radiation detected near the station. Please evacuate into one of the shielded maintenance tunnels.", "Anomaly Alert", new_sound = 'sound/AI/radiation.ogg') + command_announcement.Announce(current_map.radiation_detected_message, "Anomaly Alert", new_sound = 'sound/AI/radiation.ogg') /datum/event/radiation_storm/start() make_maint_all_access() @@ -19,7 +19,7 @@ /datum/event/radiation_storm/tick() if(activeFor == enterBelt) - command_announcement.Announce("The station has entered the radiation belt. Please remain in a sheltered area until we have passed the radiation belt.", "Anomaly Alert") + command_announcement.Announce(current_map.radiation_contact_message, "Anomaly Alert") radiate() if(activeFor >= enterBelt && activeFor <= leaveBelt) @@ -30,7 +30,7 @@ radiate() else if(activeFor == leaveBelt) - command_announcement.Announce("The station has passed the radiation belt. Please report to medbay if you experience any unusual symptoms. Maintenance will lose all-access again shortly.", "Anomaly Alert") + command_announcement.Announce(current_map.radiation_end_message, "Anomaly Alert") lights() /datum/event/radiation_storm/proc/radiate() diff --git a/code/modules/events/rogue_drones.dm b/code/modules/events/rogue_drones.dm index e3a5bb3b8c2..6d49cc27208 100644 --- a/code/modules/events/rogue_drones.dm +++ b/code/modules/events/rogue_drones.dm @@ -18,14 +18,7 @@ D.disabled = rand(15, 60) /datum/event/rogue_drone/announce() - var/msg - if(prob(33)) - msg = "A combat drone wing operating out of the NDV Icarus has failed to return from a sweep of this sector, if any are sighted approach with caution." - else if(prob(50)) - msg = "Contact has been lost with a combat drone wing operating out of the NDV Icarus. If any are sighted in the area, approach with caution." - else - msg = "Unidentified hackers have targetted a combat drone wing deployed from the NDV Icarus. If any are sighted in the area, approach with caution." - command_announcement.Announce(msg, "Rogue drone alert", new_sound = 'sound/AI/combatdrones.ogg') + command_announcement.Announce(pick(current_map.rogue_drone_detected_messages), "Rogue drone alert", new_sound = 'sound/AI/combatdrones.ogg') /datum/event/rogue_drone/end(var/faked) var/num_recovered = 0 @@ -37,6 +30,6 @@ if(!faked) if(num_recovered > length(drones_list) * 0.75) - command_announcement.Announce("Icarus drone control reports the malfunctioning wing has been recovered safely.", "Rogue drone alert") + command_announcement.Announce(current_map.rogue_drone_end_message, "Rogue drone alert") else - command_announcement.Announce("Icarus drone control registers disappointment at the loss of the drones, but the survivors have been recovered.", "Rogue drone alert") + command_announcement.Announce(current_map.rogue_drone_destroyed_message, "Rogue drone alert") diff --git a/code/modules/events/wallrot.dm b/code/modules/events/wallrot.dm index ef0409ae2e3..fa48f19d687 100644 --- a/code/modules/events/wallrot.dm +++ b/code/modules/events/wallrot.dm @@ -6,7 +6,7 @@ endWhen = announceWhen + 1 /datum/event/wallrot/announce() - command_announcement.Announce("Harmful fungi detected at coordinates ([origin_turf.x], [origin_turf.y], [origin_turf.z]). Station structures may be contaminated.", "Biohazard Alert", new_sound = pick('sound/AI/fungi.ogg', 'sound/AI/funguy.ogg', 'sound/AI/fun_guy.ogg', 'sound/AI/fun_gi.ogg')) + command_announcement.Announce("Harmful fungi detected at coordinates ([origin_turf.x], [origin_turf.y], [origin_turf.z]). The structure may be contaminated.", "Biohazard Alert", new_sound = pick('sound/AI/fungi.ogg', 'sound/AI/funguy.ogg', 'sound/AI/fun_guy.ogg', 'sound/AI/fun_gi.ogg')) /datum/event/wallrot/start() set waitfor = FALSE @@ -25,7 +25,7 @@ // Have a chance to rot lots of other walls. var/rotcount = 0 var/actual_severity = severity * rand(10, 20) - for(var/turf/simulated/wall/W in range(12, origin_turf)) + for(var/turf/simulated/wall/W in range(12, origin_turf)) if(prob(30)) W.rot() rotcount++ diff --git a/code/modules/overmap/_defines.dm b/code/modules/overmap/_defines.dm index 30c69c318f6..22d0efab087 100644 --- a/code/modules/overmap/_defines.dm +++ b/code/modules/overmap/_defines.dm @@ -20,6 +20,9 @@ var/global/list/map_sectors = list() /turf/unsimulated/map/Initialize(mapload) . = ..() + + icon_state = "map_[rand(1,6)]" + name = "[x]-[y]" var/list/numbers = list() diff --git a/code/modules/overmap/events/event.dm b/code/modules/overmap/events/event.dm new file mode 100644 index 00000000000..bf2f5b98b33 --- /dev/null +++ b/code/modules/overmap/events/event.dm @@ -0,0 +1,297 @@ +/var/decl/overmap_event_handler/overmap_event_handler = new() + +/decl/overmap_event_handler + var/list/hazard_by_turf + var/list/datum/event/ship_events + +/decl/overmap_event_handler/New() + ..() + hazard_by_turf = list() + ship_events = list() + +/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/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) + + 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 + + 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 = oview(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 + if("victim" in E.vars)//for meteors and other overmap events that uses ships//might need a better solution + E.vars["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 + 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 + +/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 + 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 + entered_event.unregister(T, src, /decl/overmap_event_handler/proc/on_turf_entered) + exited_event.unregister(T, src, /decl/overmap_event_handler/proc/on_turf_exited) + else + hazard_by_turf |= T + hazard_by_turf[T] = active_hazards + entered_event.register(T, src,/decl/overmap_event_handler/proc/on_turf_entered) + exited_event.register(T, src, /decl/overmap_event_handler/proc/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) + +/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 + 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.dmi' + icon_state = "event" + opacity = 1 + var/list/events + var/list/event_icon_states = list("event") + var/difficulty = EVENT_LEVEL_MODERATE + var/list/victims //basically cached events on which Z level + +/obj/effect/overmap/event/Initialize() + . = ..() + icon_state = pick(event_icon_states) + overmap_event_handler.update_hazards(loc) + +/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 + 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) + event_icon_states = list("meteor1", "meteor2", "meteor3", "meteor4") + difficulty = EVENT_LEVEL_MAJOR + +/obj/effect/overmap/event/electric + name = "electrical storm" + events = list(/datum/event/electrical_storm) + opacity = 0 + event_icon_states = list("electrical1", "electrical2") + difficulty = EVENT_LEVEL_MAJOR + +/obj/effect/overmap/event/dust + name = "dust cloud" + events = list(/datum/event/meteor_wave/dust/overmap) + event_icon_states = list("dust1", "dust2", "dust3", "dust4") + +/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 + +/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("carp") + +/obj/effect/overmap/event/carp/major + name = "carp school" + difficulty = EVENT_LEVEL_MAJOR + +/obj/effect/overmap/event/gravity + name = "dark matter influx" + events = list(/datum/event/gravity) + opacity = 0 + +//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 = 1 + 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 + opacity = 0 + 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 + opacity = 0 + hazards = /obj/effect/overmap/event/ion + +/datum/overmap_event/carp + name = "carp shoal" + count = 8 + radius = 3 + opacity = 0 + 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 + +/datum/overmap_event/gravity + name = "dark matter influx" + count = 12 + radius = 4 + opacity = 0 + hazards = /obj/effect/overmap/event/gravity \ No newline at end of file diff --git a/icons/obj/overmap.dmi b/icons/obj/overmap.dmi index 002377325f2..507179a221e 100644 Binary files a/icons/obj/overmap.dmi and b/icons/obj/overmap.dmi differ diff --git a/icons/turf/space.dmi b/icons/turf/space.dmi index cb3fb8f47e4..c9e127d4303 100644 Binary files a/icons/turf/space.dmi and b/icons/turf/space.dmi differ diff --git a/maps/_common/mapsystem/map.dm b/maps/_common/mapsystem/map.dm index 49a15a9bd5e..c2db26ae105 100644 --- a/maps/_common/mapsystem/map.dm +++ b/maps/_common/mapsystem/map.dm @@ -11,6 +11,7 @@ var/list/sealed_levels = list() // Z-levels that don't allow random transit at edge var/list/restricted_levels = list() // Z-levels that dont allow ghosts to randomly move around var/list/empty_levels = null // Empty Z-levels that may be used for various things (currently used by bluespace jump) + var/list/meteor_levels = list() //What z-levels the meteors can hit var/list/map_levels // Z-levels available to various consoles, such as the crew monitor. Defaults to station_levels if unset. @@ -80,6 +81,30 @@ var/list/map_shuttles = list() // A list of all our shuttles. var/default_sector = SECTOR_ROMANOVICH //What is the default space sector for this map + //event messages + + var/meteors_detected_message = "A meteor storm has been detected on collision course with the station. Estimated three minutes until impact, please activate station shields, and seek shelter in the central ring." + var/meteor_contact_message = "Contact with meteor wave imminent, all hands brace for impact." + var/meteor_end_message = "The station has cleared the meteor shower, please return to your stations." + + var/ship_detected_end_message = "The NDV Icarus reports that it has downed an unknown vessel that was approaching your station. Prepare for debris impact - please evacuate the surface level if needed." + var/ship_meteor_contact_message = "Ship debris colliding now, all hands brace for impact." + var/ship_meteor_end_message = "The last of the ship debris has hit or passed by the station, it is now safe to commence repairs." + + var/dust_detected_message = "A belt of space dust is approaching the station." + var/dust_contact_message = "The station is now passing through a belt of space dust." + var/dust_end_message = "The station has now passed through the belt of space dust." + + var/radiation_detected_message = "High levels of radiation detected near the station. Please evacuate into one of the shielded maintenance tunnels." + var/radiation_contact_message = "The station has entered the radiation belt. Please remain in a sheltered area until we have passed the radiation belt." + var/radiation_end_message = "The station has passed the radiation belt. Please report to medbay if you experience any unusual symptoms. Maintenance will lose all-access again shortly." + + var/list/rogue_drone_detected_messages = list("A combat drone wing operating out of the NDV Icarus has failed to return from a sweep of this sector, if any are sighted approach with caution.", + "Contact has been lost with a combat drone wing operating out of the NDV Icarus. If any are sighted in the area, approach with caution.", + "Unidentified hackers have targetted a combat drone wing deployed from the NDV Icarus. If any are sighted in the area, approach with caution.") + var/rogue_drone_end_message = "Icarus drone control reports the malfunctioning wing has been recovered safely." + var/rogue_drone_destroyed_message = "Icarus drone control registers disappointment at the loss of the drones, but the survivors have been recovered." + /datum/map/New() if(!map_levels) map_levels = station_levels.Copy() diff --git a/maps/aurora/code/aurora.dm b/maps/aurora/code/aurora.dm index ea900e09474..33210f3a5e4 100644 --- a/maps/aurora/code/aurora.dm +++ b/maps/aurora/code/aurora.dm @@ -16,6 +16,7 @@ player_levels = list(2, 3, 4, 5, 6, 7, 8) restricted_levels = list() accessible_z_levels = list("2" = 60, "6" = 20, "7" = 20) + meteor_levels = list(6) base_turf_by_z = list( "1" = /turf/space, "2" = /turf/space, diff --git a/maps/exodus/code/exodus.dm b/maps/exodus/code/exodus.dm index 7e1f18b3773..cb35dc1e8a8 100644 --- a/maps/exodus/code/exodus.dm +++ b/maps/exodus/code/exodus.dm @@ -49,6 +49,7 @@ contact_levels = list(1, 5) player_levels = list(1, 3, 4, 5, 6) accessible_z_levels = list("1" = 5, "3" = 10, "4" = 15, "5" = 10, "6" = 60) + meteor_levels = list(1) map_shuttles = list( /datum/shuttle/autodock/ferry/escape_pod/pod/escape_pod1, diff --git a/maps/runtime/code/runtime.dm b/maps/runtime/code/runtime.dm index d84611046aa..88e7ffede15 100644 --- a/maps/runtime/code/runtime.dm +++ b/maps/runtime/code/runtime.dm @@ -13,6 +13,9 @@ contact_levels = list(1, 2, 3) player_levels = list(1, 2, 3) accessible_z_levels = list(1, 2, 3) + meteor_levels = list(1, 2, 3) + + overmap_event_areas = 10 station_name = "NSV Runtime" station_short = "Runtime" diff --git a/maps/runtime/runtime-1.dmm b/maps/runtime/runtime-1.dmm index ba0d328b712..8ea80b118d4 100644 --- a/maps/runtime/runtime-1.dmm +++ b/maps/runtime/runtime-1.dmm @@ -1772,6 +1772,12 @@ /obj/turbolift_map_holder/runtime, /turf/simulated/floor/plating, /area/turbolift/main_station) +"Yc" = ( +/obj/effect/landmark{ + name = "carpspawn" + }, +/turf/template_noop, +/area/template_noop) "YP" = ( /obj/effect/floor_decal/corner/blue{ dir = 10 @@ -2032,7 +2038,7 @@ aa aa aa aa -aa +Yc aa aa aa @@ -2542,7 +2548,7 @@ aa aa aa aa -aa +Yc aa aa "} @@ -3038,7 +3044,7 @@ aa aa aa aa -aa +Yc aa aa ab @@ -3783,7 +3789,7 @@ aa aa aa aa -aa +Yc aa aa aa @@ -4296,7 +4302,7 @@ aa aa aa aa -aa +Yc aa aa aa diff --git a/maps/runtime/runtime-2.dmm b/maps/runtime/runtime-2.dmm index fab75070d8b..3320b27215a 100644 --- a/maps/runtime/runtime-2.dmm +++ b/maps/runtime/runtime-2.dmm @@ -133,6 +133,12 @@ temperature = 278 }, /area/turret_protected/ai_upload_foyer) +"fr" = ( +/obj/effect/landmark{ + name = "carpspawn" + }, +/turf/template_noop, +/area/template_noop) "gD" = ( /obj/structure/cable{ d1 = 1; @@ -1401,7 +1407,7 @@ iR iR iR iR -iR +fr iR iR iR @@ -1535,7 +1541,7 @@ iR iR iR iR -iR +fr iR iR iR @@ -2605,7 +2611,7 @@ iR iR iR iR -iR +fr iR iR iR @@ -3749,7 +3755,7 @@ iR iR iR iR -iR +fr iR iR iR @@ -3995,7 +4001,7 @@ iR iR iR iR -iR +fr iR iR iR diff --git a/maps/runtime/runtime-3.dmm b/maps/runtime/runtime-3.dmm index f0070322c6d..9c5af899360 100644 --- a/maps/runtime/runtime-3.dmm +++ b/maps/runtime/runtime-3.dmm @@ -47,8 +47,8 @@ "i" = ( /obj/machinery/light{ brightness_range = 16; - must_start_working = 1; - dir = 8 + dir = 8; + must_start_working = 1 }, /obj/effect/floor_decal/industrial/warning{ dir = 4 @@ -70,8 +70,8 @@ "l" = ( /obj/machinery/light{ brightness_range = 16; - must_start_working = 1; - dir = 4 + dir = 4; + must_start_working = 1 }, /obj/effect/floor_decal/industrial/warning{ dir = 8 @@ -187,11 +187,17 @@ }, /turf/simulated/wall/r_wall, /area/construction/hallway) +"I" = ( +/obj/effect/landmark{ + name = "carpspawn" + }, +/turf/template_noop, +/area/template_noop) "J" = ( /obj/machinery/light{ brightness_range = 16; - must_start_working = 1; - dir = 8 + dir = 8; + must_start_working = 1 }, /obj/effect/floor_decal/industrial/warning{ dir = 4 @@ -536,7 +542,7 @@ b b b b -b +I b b b @@ -620,7 +626,7 @@ b b b b -b +I b b b @@ -1205,7 +1211,7 @@ b b b b -b +I b b b @@ -2177,7 +2183,7 @@ b b b b -b +I b b b @@ -2726,7 +2732,7 @@ b b b b -b +I b b b @@ -2807,7 +2813,7 @@ b b b b -b +I b b b