Skip to content

Commit

Permalink
Catastrophe events, Leviathan (#458)
Browse files Browse the repository at this point in the history
* Catastrophe events, Leviathan

* Some fixes

* Fix everything

* Update example config

* Even more updates!

* Fix

* Don't play sounds to new players

* More adjustments

* Bubble effect doesn't convert open or space turfs

* Fix bubble

* Fixes

* Infestation mobs are weak to fire

* Aggregate infestation caste!

* Resolve conflicts
  • Loading branch information
EgorDinamit authored Oct 15, 2023
1 parent 8f93215 commit a000269
Show file tree
Hide file tree
Showing 33 changed files with 564 additions and 21 deletions.
5 changes: 5 additions & 0 deletions baystation12.dme
Original file line number Diff line number Diff line change
Expand Up @@ -1856,7 +1856,10 @@
#include "code\modules\events\gravity.dm"
#include "code\modules\events\grid_check.dm"
#include "code\modules\events\infestation.dm"
#include "code\modules\events\infestation_hive_space.dm"
#include "code\modules\events\ion_storm.dm"
#include "code\modules\events\leviathan_attack.dm"
#include "code\modules\events\leviathan_spawn.dm"
#include "code\modules\events\mail.dm"
#include "code\modules\events\maint_drones.dm"
#include "code\modules\events\meteors.dm"
Expand Down Expand Up @@ -2426,11 +2429,13 @@
#include "code\modules\mob\living\simple_animal\hostile\giant_spider\nurse.dm"
#include "code\modules\mob\living\simple_animal\hostile\giant_spider\spitter.dm"
#include "code\modules\mob\living\simple_animal\hostile\infestation\_infestation.dm"
#include "code\modules\mob\living\simple_animal\hostile\infestation\aggregate.dm"
#include "code\modules\mob\living\simple_animal\hostile\infestation\assembler.dm"
#include "code\modules\mob\living\simple_animal\hostile\infestation\broodling.dm"
#include "code\modules\mob\living\simple_animal\hostile\infestation\eviscerator.dm"
#include "code\modules\mob\living\simple_animal\hostile\infestation\floatfly.dm"
#include "code\modules\mob\living\simple_animal\hostile\infestation\larva.dm"
#include "code\modules\mob\living\simple_animal\hostile\infestation\meatchip.dm"
#include "code\modules\mob\living\simple_animal\hostile\infestation\rhino.dm"
#include "code\modules\mob\living\simple_animal\hostile\infestation\spitter.dm"
#include "code\modules\mob\living\simple_animal\hostile\infestation\structures\hive_heart.dm"
Expand Down
9 changes: 5 additions & 4 deletions code/__defines/misc.dm
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,11 @@
#define MAX_TEXTFILE_LENGTH 128000 // 512GQ file

// Event defines.
#define EVENT_LEVEL_MUNDANE 1
#define EVENT_LEVEL_MODERATE 2
#define EVENT_LEVEL_MAJOR 3
#define EVENT_LEVEL_EXO 4
#define EVENT_LEVEL_MUNDANE 1
#define EVENT_LEVEL_MODERATE 2
#define EVENT_LEVEL_MAJOR 3
#define EVENT_LEVEL_CATASTROPHE 4
#define EVENT_LEVEL_EXO 5

//General-purpose life speed define for plants.
#define HYDRO_SPEED_MULTIPLIER 1
Expand Down
2 changes: 1 addition & 1 deletion code/_onclick/item_attack.dm
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ avoid code duplication. This includes items that may sometimes act as a standard
return 1

//Called when a weapon is used to make a successful melee attack on a mob. Returns whether damage was dealt.
/obj/item/proc/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone)
/obj/item/proc/apply_hit_effect(mob/living/target, mob/living/user, hit_zone)
if(hitsound)
playsound(loc, hitsound, 50, 1, -1)

Expand Down
36 changes: 33 additions & 3 deletions code/controllers/configuration.dm
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,29 @@ var/list/gamemode_cache = list()
// Event settings
var/expected_round_length = 3 HOURS
/// If the first delay has a custom start time
var/static/list/event_first_run = list(EVENT_LEVEL_MUNDANE = null, EVENT_LEVEL_MODERATE = null, EVENT_LEVEL_MAJOR = list("lower" = 80 MINUTES, "upper" = 100 MINUTES), EVENT_LEVEL_EXO = list("lower" = 50 MINUTES, "upper" = 80 MINUTES))
var/static/list/event_first_run = list(
EVENT_LEVEL_MUNDANE = null,
EVENT_LEVEL_MODERATE = null,
EVENT_LEVEL_MAJOR = list("lower" = 80 MINUTES, "upper" = 100 MINUTES),
EVENT_LEVEL_CATASTROPHE = list("lower" = 150 MINUTES, "upper" = 200 MINUTES),
EVENT_LEVEL_EXO = list("lower" = 50 MINUTES, "upper" = 80 MINUTES),
)
/// The lowest delay until next event
var/static/list/event_delay_lower = list(EVENT_LEVEL_MUNDANE = 10 MINUTES, EVENT_LEVEL_MODERATE = 30 MINUTES, EVENT_LEVEL_MAJOR = 50 MINUTES, EVENT_LEVEL_EXO = 40 MINUTES)
var/static/list/event_delay_lower = list(
EVENT_LEVEL_MUNDANE = 10 MINUTES,
EVENT_LEVEL_MODERATE = 30 MINUTES,
EVENT_LEVEL_MAJOR = 50 MINUTES,
EVENT_LEVEL_CATASTROPHE = 100 MINUTES,
EVENT_LEVEL_EXO = 40 MINUTES,
)
/// The upper delay until next event
var/static/list/event_delay_upper = list(EVENT_LEVEL_MUNDANE = 15 MINUTES, EVENT_LEVEL_MODERATE = 45 MINUTES, EVENT_LEVEL_MAJOR = 70 MINUTES, EVENT_LEVEL_EXO = 60 MINUTES)
var/static/list/event_delay_upper = list(
EVENT_LEVEL_MUNDANE = 15 MINUTES,
EVENT_LEVEL_MODERATE = 45 MINUTES,
EVENT_LEVEL_MAJOR = 70 MINUTES,
EVENT_LEVEL_CATASTROPHE = 120 MINUTES,
EVENT_LEVEL_EXO = 60 MINUTES,
)

var/abandon_allowed = 1
var/ooc_allowed = 1
Expand Down Expand Up @@ -665,17 +683,29 @@ var/list/gamemode_cache = list()
var/values = text2numlist(value, ";")
config.event_first_run[EVENT_LEVEL_MAJOR] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2]))

if("event_custom_start_catastrophe")
var/values = text2numlist(value, ";")
config.event_first_run[EVENT_LEVEL_CATASTROPHE] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2]))

if("event_custom_start_exo")
var/values = text2numlist(value, ";")
config.event_first_run[EVENT_LEVEL_EXO] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2]))

if("event_delay_lower")
var/values = text2numlist(value, ";")
config.event_delay_lower[EVENT_LEVEL_MUNDANE] = MinutesToTicks(values[1])
config.event_delay_lower[EVENT_LEVEL_MODERATE] = MinutesToTicks(values[2])
config.event_delay_lower[EVENT_LEVEL_MAJOR] = MinutesToTicks(values[3])
config.event_delay_lower[EVENT_LEVEL_CATASTROPHE] = MinutesToTicks(values[4])
config.event_delay_lower[EVENT_LEVEL_EXO] = MinutesToTicks(values[5])

if("event_delay_upper")
var/values = text2numlist(value, ";")
config.event_delay_upper[EVENT_LEVEL_MUNDANE] = MinutesToTicks(values[1])
config.event_delay_upper[EVENT_LEVEL_MODERATE] = MinutesToTicks(values[2])
config.event_delay_upper[EVENT_LEVEL_MAJOR] = MinutesToTicks(values[3])
config.event_delay_lower[EVENT_LEVEL_CATASTROPHE] = MinutesToTicks(values[4])
config.event_delay_lower[EVENT_LEVEL_EXO] = MinutesToTicks(values[5])

if("starlight")
value = text2num(value)
Expand Down
3 changes: 2 additions & 1 deletion code/controllers/subsystems/event.dm
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ SUBSYSTEM_DEF(event)
EVENT_LEVEL_MUNDANE = new/datum/event_container/mundane,
EVENT_LEVEL_MODERATE = new/datum/event_container/moderate,
EVENT_LEVEL_MAJOR = new/datum/event_container/major,
EVENT_LEVEL_EXO = new/datum/event_container/exo
EVENT_LEVEL_CATASTROPHE = new/datum/event_container/catastrophe,
EVENT_LEVEL_EXO = new/datum/event_container/exo,
)

if(GLOB.using_map.use_overmap)
Expand Down
18 changes: 18 additions & 0 deletions code/game/objects/effects/temporary.dm
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,21 @@
animate(src, transform = M1, alpha = 192, time = 0.5 SECONDS)
animate(time = 2 SECONDS)
animate(transform = M2, alpha = 0, time = 0.5 SECONDS)

/obj/effect/temp_visual/bite
name = "bite"
icon_state = "bite"
icon = 'icons/effects/effects.dmi'
opacity = FALSE
anchored = TRUE
mouse_opacity = FALSE
layer = ABOVE_HUMAN_LAYER

duration = 1 SECONDS

/obj/effect/temp_visual/bite/Initialize()
. = ..()
addtimer(CALLBACK(src, .proc/FadeOut), (duration * 0.8))

/obj/effect/temp_visual/bite/proc/FadeOut()
animate(src, alpha = 0, (duration * 0.2))
11 changes: 9 additions & 2 deletions code/modules/events/event_container.dm
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#define ASSIGNMENT_EXPLORATION "Exploration"
#define ASSIGNMENT_SECURITY "Security"

var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT_LEVEL_MODERATE = "Moderate", EVENT_LEVEL_MAJOR = "Major", EVENT_LEVEL_EXO = "Exoplanet")
var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT_LEVEL_MODERATE = "Moderate", EVENT_LEVEL_MAJOR = "Major", EVENT_LEVEL_CATASTROPHE = "Catastrophe", EVENT_LEVEL_EXO = "Exoplanet")

/datum/event_container
var/severity = -1
Expand Down Expand Up @@ -196,10 +196,17 @@ var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT
new /datum/event_meta(EVENT_LEVEL_MAJOR, "Bluespace Drive Instability", /datum/event/bsd_instability, 0,list(ASSIGNMENT_ENGINEER = 10, ASSIGNMENT_ANY = 5), 1),
)

/datum/event_container/catastrophe
severity = EVENT_LEVEL_CATASTROPHE
available_events = list(
new /datum/event_meta(EVENT_LEVEL_CATASTROPHE, "Nothing", /datum/event/nothing, 100),
new /datum/event_meta(EVENT_LEVEL_CATASTROPHE, "Leviathan", /datum/event/leviathan_spawn, 0, list(ASSIGNMENT_COMMAND = 5, ASSIGNMENT_EXPLORATION = 5, ASSIGNMENT_SECURITY = 5, ASSIGNMENT_MEDICAL = 5, ASSIGNMENT_ENGINEER = 5))
)

/datum/event_container/exo
severity = EVENT_LEVEL_EXO
available_events = list(
new /datum/event_meta(EVENT_LEVEL_EXO, "Nothing", /datum/event/nothing, 100),
new /datum/event_meta(EVENT_LEVEL_EXO, "Nothing", /datum/event/nothing, 100),
new /datum/event_meta(EVENT_LEVEL_EXO, "Exoplanet Awakening", /datum/event/exo_awakening, 0, list(ASSIGNMENT_ANY = 2, ASSIGNMENT_EXPLORATION = 5))
)

Expand Down
40 changes: 40 additions & 0 deletions code/modules/events/infestation_hive_space.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Nothing more than a far more lazy copy of carp migration
// Used exclusively by infestation hive overmap obstacle
/datum/event/infestation_hive_space
var/mobs_per_tick = 1
/// Types of mobs that we can spawn in and throw at the ship, mostly flying types
var/list/available_mob_types = list(/mob/living/simple_animal/hostile/infestation/floatfly)

/datum/event/infestation_hive_space/announce()
var/announcement = ""
if(severity > EVENT_LEVEL_MODERATE)
announcement = "A massive migration of unknown biological entities has been detected in the vicinity of the [location_name()]. Exercise external operations with caution."
else
announcement = "A large migration of unknown biological entities has been detected in the vicinity of the [location_name()]. Caution is advised."

command_announcement.Announce(announcement, "[location_name()] Sensor Array", zlevels = affecting_z)

/datum/event/infestation_hive_space/tick()
SpawnMobs()

/datum/event/infestation_hive_space/proc/SpawnMobs(dir, speed)
if(!living_observers_present(affecting_z))
return

var/Z = pick(affecting_z)
if(!dir)
dir = pick(GLOB.cardinal)

if(!speed)
speed = rand(1, 6)

for(var/i = 1 to mobs_per_tick)
var/turf/T = get_random_edge_turf(dir,TRANSITIONEDGE + 2, Z)
if(istype(T, /turf/space))
var/mob/living/simple_animal/hostile/M = pick(available_mob_types)
M = new M(T)
M.throw_at(get_random_edge_turf(GLOB.reverse_dir[dir], TRANSITIONEDGE + 2, Z), 250, speed, callback = CALLBACK(src, /datum/event/infestation_hive_space/proc/check_gib,M))

/datum/event/infestation_hive_space/proc/check_gib(mob/living/L)
if(L.health <= 15)
L.gib()
99 changes: 99 additions & 0 deletions code/modules/events/leviathan_attack.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Sends you to fucking hell for daring to enter this sector, or for being too slow
// Bombards the ship with beams that may damage the ship and cause breaches
// Additionally, spawns hearts of the hive directly on the ship
/datum/event/leviathan_attack
has_skybox_image = TRUE
var/beams_per_tick = 1
// Checks against world.time, not event ticks
var/infestation_spawn_cooldown
var/infestation_spawn_cooldown_time = 60 SECONDS
/// How many random mobs are spawned around heart of the hive on activation
var/infestation_spawn_count = 5
/// Types of mobs = chance the infestation spawn will create; It utilizes pickweight
var/list/infestation_spawn_types = list(
/mob/living/simple_animal/hostile/infestation/larva = 30,
/mob/living/simple_animal/hostile/infestation/broodling = 15,
/mob/living/simple_animal/hostile/infestation/floatfly = 10,
/mob/living/simple_animal/hostile/infestation/eviscerator = 6,
/mob/living/simple_animal/hostile/infestation/spitter = 5,
/mob/living/simple_animal/hostile/infestation/assembler = 3,
/mob/living/simple_animal/hostile/infestation/rhino = 1,
/mob/living/simple_animal/hostile/infestation/larva/implant/implanter = 1,
)

/datum/event/leviathan_attack/get_skybox_image()
var/image/res = overlay_image('icons/skybox/ionbox.dmi', "ions", COLOR_MAROON, RESET_COLOR)
res.blend_mode = BLEND_ADD
return res

/datum/event/leviathan_attack/announce()
command_announcement.Announce(
"DANGER! [location_name()] is currently in close vicinity to Leviathan-class abomination!\n\
It is advised to immediately leave the area before damage to the ship reaches critical level!",
"[GLOB.using_map.company_name] Infestation Alert",
zlevels = affecting_z,
)

/datum/event/leviathan_attack/tick()
BeamAttack()
if(world.time >= infestation_spawn_cooldown)
InfestationSpawn()

/datum/event/leviathan_attack/proc/BeamAttack()
if(!living_observers_present(affecting_z))
return

var/Z = pick(affecting_z)
var/dir = pick(GLOB.cardinal)

for(var/i = 1 to beams_per_tick)
var/turf/T = get_random_edge_turf(dir, TRANSITIONEDGE + 2, Z)
if(istype(T, /turf/space))
var/obj/item/projectile/beam/gigabeam/leviathan/P = new(T)
P.dispersion += pick(0, 0.1, 0.2)
P.launch(get_random_edge_turf(GLOB.reverse_dir[dir], TRANSITIONEDGE + 2, Z), pick(BP_ALL_LIMBS), T)

/datum/event/leviathan_attack/proc/InfestationSpawn(turf/T)
infestation_spawn_cooldown = world.time + infestation_spawn_cooldown_time
if(!living_observers_present(affecting_z))
return

if(!istype(T))
T = pick_area_turf_in_single_z_level(list(/proc/is_not_space_area), list(/proc/not_turf_contains_dense_objects, /proc/is_not_open_space, /proc/is_not_space_turf), pick(affecting_z))

new /obj/effect/hive_heart(T)
new /datum/bubble_effect/infestation(T.x, T.y, T.z, 1, 1)
var/list/valid_spawns = list()
for(var/turf/TT in oview(T, 3))
if(is_space_turf(TT))
continue
if(is_open_space(TT))
continue
valid_spawns += TT
for(var/i = 1 to infestation_spawn_count)
var/mob/living/L = pickweight(infestation_spawn_types)
var/turf/TT = pick(valid_spawns)
new L(TT)

/datum/bubble_effect/infestation/New()
..()
START_PROCESSING(SSfastprocess, src)

/datum/bubble_effect/infestation/Destroy()
STOP_PROCESSING(SSfastprocess, src)
return ..()

/datum/bubble_effect/infestation/Process()
if(radius > 7)
qdel(src)
return PROCESS_KILL
Tick()

/datum/bubble_effect/infestation/TurfEffect(turf/T)
if(TICK_CHECK)
return TRUE
if(T.density || is_open_space(T) || is_space_turf(T))
return
if(prob(33))
return // Continue
T.ChangeTurf(/turf/simulated/floor/exoplanet/flesh)
27 changes: 27 additions & 0 deletions code/modules/events/leviathan_spawn.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// This event spawns a "Leaviathan" - giant abomination overmap "obstacle";
// As long as it is alive - the sector will be constantly flooded with infestation hive overmap obstacles,
// which operate similar to carp schools.
// When ship approaches Leviathan itself - it will be bombarded with abominations and flesh meteors.
// It takes 8 OFD charges to kill it.
/datum/event/leviathan_spawn
announceWhen = 5
endWhen = 6
var/spawned_loc = "UNKNOWN"

/datum/event/leviathan_spawn/announce()
command_announcement.Announce(
"Leviathan class abomination has been spotted within the sector. All civilian personnel is advised to evacuate \
to the closest safe area immediatelly. \nMilitary responders must contain the threat until it is too late.\n\n\
Last known coordinates of the creature: [spawned_loc]",
"[GLOB.using_map.company_name] Infestation Alert",
'sound/effects/alarm_catastrophe.ogg',
zlevels = affecting_z,
)

/datum/event/leviathan_spawn/start()
var/list/candidate_turfs = block(locate(OVERMAP_EDGE, OVERMAP_EDGE, GLOB.using_map.overmap_z), locate(GLOB.using_map.overmap_size - OVERMAP_EDGE, GLOB.using_map.overmap_size - OVERMAP_EDGE, GLOB.using_map.overmap_z))
candidate_turfs = where(candidate_turfs, /proc/can_not_locate, /obj/effect/overmap/visitable)
var/turf/T = pick(candidate_turfs)
new /obj/effect/temp_visual/ftl(T)
new /obj/effect/overmap/event/leviathan(T)
spawned_loc = "[T.x]:[T.y]"
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@
QDEL_IN(src, (5 SECONDS))
return ..()

// While they are "resistant" to high temperatures, they are specifically weak to fire
/mob/living/simple_animal/hostile/infestation/fire_burn_temperature()
. = ..()
. *= 3

/mob/living/simple_animal/hostile/infestation/proc/BecomeEgg()
name = "egg"
desc = "A weird egg..?"
Expand Down
Loading

0 comments on commit a000269

Please sign in to comment.