Files
Bubberstation/code/modules/events/crystal_event.dm
LemonInTheDark 6d1cb94ffb LINDA Reforged (#55604)
Speeds up gas movement significantly
Documents the intent and finer details of the atmos system (Thanks dunc)
Fixes excited groups constantly rebuilding, this broke 4 years ago
Fixes superconductors just straight up not working
Allows turfs to sleep while inside an excited group
Adds a new subprocess to SSAir to support rebuilding in this state
Most heat based behavior no longer relies on being inside a fire
Adds a new element to support doing this cleanly
Adds a new subprocess to SSAir to support doing this while a turf is asleep
Refactors air_update_turf to allow for finer control
Makes apcs take damage in heat to prevent infinite plasma fire diffs
Cleans up immutable gas mixtures to make them work properly when the mix has gas in it
Planetary turfs no longer create a new copy of themselves each time they process. We instead use a global
immutable mix
Cleans up a typed for loop in reactions
Canisters will take damage from outside heat now
Speeds up excited group dismantle
Increases the superconductor threshold by 200k
Cleans up some roundstart ATs on some ruins
Uses /turf/open/var/excited to track if a turf is actively processing, preventing a |=
Prevents openspace from trying to melt
Tweaks a canister examine line
Makes planetary turfs reset to base when broken down as part of an excited group
Makes it impossible for planetary turfs to rebuild, just like space tiles
Fixes closed turfs not activating their replacement when destroyed by moving closed -> open turf activation to
the adjacent air subsystem. They were activating and then going back to sleep before adjacent air got a chance
to tick.
Fire alarms will trigger when the area gets too cold for humans
2021-01-08 08:14:08 +01:00

858 lines
31 KiB
Plaintext

///Global list that stores the 4 kinds of waves the crystal invasion can have and the portals each one can spawn
GLOBAL_LIST_INIT(crystal_invasion_waves, list(
"small wave" = list(
/obj/structure/crystal_portal/small=4,
/obj/structure/crystal_portal/medium=1
),
"medium wave" = list(
/obj/structure/crystal_portal/small=4,
/obj/structure/crystal_portal/medium=3,
/obj/structure/crystal_portal/big=1
),
"big wave" = list(
/obj/structure/crystal_portal/small=5,
/obj/structure/crystal_portal/medium=3,
/obj/structure/crystal_portal/big=2
),
"huge wave" = list(
/obj/structure/crystal_portal/small=7,
/obj/structure/crystal_portal/medium=5,
/obj/structure/crystal_portal/big=3
),
))
///Global list that stores the all the instantiated portals around the station, used when stabilizing the crystal
GLOBAL_LIST_EMPTY(crystal_portals)
///Global list that store the huge portals that spawn at the start of the event
GLOBAL_LIST_EMPTY(huge_crystal_portals)
/*
This section is for the event controller
*/
/datum/round_event_control/crystal_invasion
name = "Crystal Invasion"
typepath = /datum/round_event/crystal_invasion
weight = 0
min_players = 35
max_occurrences = 0 //no random chance to you
earliest_start = 25 MINUTES
/datum/round_event/crystal_invasion
startWhen = 5
announceWhen = 1
endWhen = 460
///Is the name of the wave, used to check wich wave will be generated
var/wave_name
///Max number of portals that can spawn per type of wave
var/portal_numbers
///Check if this is the first wave or not
var/spawned = FALSE
///Store the destabilized crystal
var/obj/machinery/destabilized_crystal/dest_crystal
///Check if the event will end badly
var/is_zk_scenario = TRUE
///Ticks that have to pass before second wave
var/second_wave = 310
///Store the areas where the huge portals appears
var/list/center_areas = list()
/datum/round_event/crystal_invasion/start()
destabilize_supermatter_crystal()
choose_wave_type()
addtimer(CALLBACK(src, .proc/create_random_portals), 2 SECONDS)
///Destabilize the Supermatter crystal and spawn 6 portals around it
/datum/round_event/crystal_invasion/proc/destabilize_supermatter_crystal()
var/list/sm_crystal = list()
for(var/obj/machinery/power/supermatter_crystal/temp in GLOB.machines)
if(istype(temp, /obj/machinery/power/supermatter_crystal/shard))
continue
sm_crystal += temp
if(!length(sm_crystal))
log_game("No engine found, killing the crystal invasion event.")
kill()
return
var/obj/machinery/power/supermatter_crystal/crystal = pick(sm_crystal)
dest_crystal = crystal.destabilize(portal_numbers)
RegisterSignal(dest_crystal, COMSIG_PARENT_QDELETING, .proc/on_dest_crystal_qdel)
for(var/t in RANGE_TURFS(8, dest_crystal.loc))
var/turf/turf_loc = t
var/distance_from_center = get_dist(turf_loc, dest_crystal)
switch(distance_from_center)
if(0)
distance_from_center = 1 //Same tile, let's avoid a division by zero.
if(-1)
kill()
CRASH("Negative distance measurement from the center turf detected, this should never happen")
if(prob(325 / distance_from_center))
if(isopenturf(turf_loc) || isspaceturf(turf_loc))
turf_loc.ChangeTurf(/turf/open/indestructible/crystal_floor, flags = CHANGETURF_INHERIT_AIR)
else
turf_loc.ChangeTurf(/turf/closed/indestructible/crystal_wall)
var/list/crystal_spawner_turfs = list()
for(var/range_turf in RANGE_TURFS(6, dest_crystal.loc))
if(!isopenturf(range_turf) || isspaceturf(range_turf))
continue
crystal_spawner_turfs += range_turf
for(var/i in 1 to 6)
var/pick_portal = pickweight(GLOB.crystal_invasion_waves["big wave"])
var/turf/portal_spawner_turf = pick(crystal_spawner_turfs)
new pick_portal(portal_spawner_turf)
///Chooses random areas where to put the main huge portals and then spawn around them 6 more portals
/datum/round_event/crystal_invasion/proc/create_random_portals()
var/list/center_finder = list()
for(var/es in GLOB.generic_event_spawns)
var/obj/effect/landmark/event_spawn/temp = es
if(is_station_level(temp.z))
center_finder += temp
if(!center_finder.len)
kill()
CRASH("No landmarks on the station map, aborting")
for(var/j in 1 to portal_numbers)
if(!length(center_finder))
kill()
CRASH("center_finder had less entries than portal_numbers ([j]/[portal_numbers])")
var/obj/center = pick_n_take(center_finder)
var/turf/center_turf = center.loc
var/area/center_area = get_area(center_turf)
center_areas += center_area
explosion(center_turf,0,0,5,7,7)
new /obj/structure/crystal_portal/huge(center_turf)
for(var/t in RANGE_TURFS(5, center_turf))
var/turf/turf_loc = t
var/distance_from_center = GET_TRUE_DIST(turf_loc, center_turf)
switch(distance_from_center)
if(0)
distance_from_center = 1 //Same tile, let's avoid a division by zero.
if(-1)
kill()
CRASH("Negative distance measurement from the center turf detected, this should never happen")
if(prob(250 / distance_from_center))
if(isopenturf(turf_loc) || isspaceturf(turf_loc))
turf_loc.ChangeTurf(/turf/open/indestructible/crystal_floor, flags = CHANGETURF_INHERIT_AIR)
else
turf_loc.ChangeTurf(/turf/closed/indestructible/crystal_wall)
var/list/portal_spawner_turfs = list()
for(var/range_turf in RANGE_TURFS(6, center_turf))
if(!isopenturf(range_turf) || isspaceturf(range_turf))
continue
portal_spawner_turfs += range_turf
for(var/i in 1 to 6)
if(!length(portal_spawner_turfs))
break
var/pick_portal = pickweight(GLOB.crystal_invasion_waves[wave_name])
var/turf/portal_spawner_turf = pick_n_take(portal_spawner_turfs)
new pick_portal(portal_spawner_turf)
dest_crystal.icon_state = "psy_shielded"
dest_crystal.changed_icon = FALSE
addtimer(CALLBACK(src, .proc/announce_locations), 8 SECONDS)
///After 8 seconds from the initial explosions centcom will announce the location of the huge portals
/datum/round_event/crystal_invasion/proc/announce_locations()
priority_announce("WARNING - After tracking the powerspikes from your station we have determined that huge portals have appeared at the following locations:[center_areas.Join(", ")]. \
Please close those before attempting to stabilize the crystal.", "Alert")
sound_to_playing_players('sound/misc/notice1.ogg')
/datum/round_event/crystal_invasion/announce(fake)
priority_announce("WARNING - Destabilization of the Supermatter Crystal Matrix detected, please stand by and await further instructions.", "Alert")
sound_to_playing_players('sound/misc/notice1.ogg')
///Choose the type of the wave
/datum/round_event/crystal_invasion/proc/choose_wave_type()
if(!wave_name)
wave_name = pickweight(list(
"small wave" = 35,
"medium wave" = 45,
"big wave" = 15,
"huge wave" = 5))
switch(wave_name)
if("small wave")
portal_numbers = 2
if("medium wave")
portal_numbers = rand(2, 3)
if("big wave")
portal_numbers = rand(3, 4)
if("huge wave")
portal_numbers = rand(4, 5)
else
kill()
CRASH("Wave name of [wave_name] not recognised.")
priority_announce("WARNING - Numerous energy fluctuations have been detected from your Supermatter; we estimate a [wave_name] of crystalline creatures \
coming from \[REDACTED]; there will be [portal_numbers] main portals spread around the station that you must close. Harvest a \[REDACTED] \
crystal from a portal by using the anomaly neutralizer, place it inside a crystal stabilizer, and inject it into your Supermatter to stop a ZK-Lambda-Class Cosmic Fragmentation Scenario from occurring.", "Alert")
sound_to_playing_players('sound/misc/notice1.ogg')
///Spawn one portal in a random location choosen from the generic_event_spawns list
/datum/round_event/crystal_invasion/proc/spawn_portal(list/wave_type, list/spawners)
if(!spawners.len)
CRASH("No landmarks on the station map, aborting")
var/pick_portal = pickweight(wave_type)
var/obj/spawner = pick(spawners)
new pick_portal(spawner.loc)
///If after 10 minutes the crystal is not stabilized more portals are spawned and the event progress further
/datum/round_event/crystal_invasion/proc/more_portals()
priority_announce("WARNING - Detected another spike from the destabilized crystal. More portals are spawning all around the station, the next spike could \
cause a \[REDACTED] class event we assume you have five more minutes before total crystal annihilation", "Alert")
sound_to_playing_players('sound/misc/notice1.ogg')
var/list/spawners = list()
for(var/es in GLOB.generic_event_spawns)
var/obj/effect/landmark/event_spawn/temp = es
if(is_station_level(temp.z))
spawners += temp
for(var/i in 1 to rand(10, 15))
spawn_portal(GLOB.crystal_invasion_waves["medium wave"], spawners)
/datum/round_event/crystal_invasion/tick()
if(activeFor == second_wave)
more_portals()
if(dest_crystal.is_stabilized == TRUE)
processing = FALSE
is_zk_scenario = FALSE
finish_event()
if(activeFor == endWhen - 10)
processing = FALSE
finish_event()
///Handles wich end the event shall have
/datum/round_event/crystal_invasion/proc/finish_event()
if(is_zk_scenario == TRUE)
zk_event_announcement()
else
restore()
kill()
///This proc announces that the event is concluding with the worst scenario
/datum/round_event/crystal_invasion/proc/zk_event_announcement()
dest_crystal.active = FALSE
priority_announce("WARNING - The crystal has reached critical instability point. ZK-Event inbound, please do not panic, anyone who panics will \
be terminated on the spot. Have a nice day", "Alert")
sound_to_playing_players('sound/machines/alarm.ogg')
addtimer(CALLBACK(src, .proc/do_zk_event), 10 SECONDS)
///This proc actually manages the end of the event
/datum/round_event/crystal_invasion/proc/do_zk_event()
var/list/spawners = list()
for(var/es in GLOB.generic_event_spawns)
var/obj/effect/landmark/event_spawn/temp = es
if(is_station_level(temp.z))
spawners += temp
for(var/i in 1 to rand(15, 25))
spawn_portal(GLOB.crystal_invasion_waves["huge wave"], spawners)
explosion(dest_crystal.loc, 15, 26, 33, 35, 1, 1) //a bit smaller than max supermatter explosion
priority_announce("WARNING - Portal are appearing everywhere, you failed to contain the event. You people should feel ashamed of yourselves!","Alarm")
QDEL_NULL(dest_crystal)
///Restore the Destabilized Crystal as it was before
/datum/round_event/crystal_invasion/proc/restore()
priority_announce("The Crystal has been restored and is now stable again, your sector of space is now safe from the ZK-Lambda-Class Scenario, \
kill the remaining crystal monsters and go back to work")
sound_to_playing_players('sound/misc/notice2.ogg')
var/turf/loc_turf = get_turf(dest_crystal.loc)
new/obj/machinery/power/supermatter_crystal(loc_turf)
for(var/Portal in GLOB.crystal_portals)
qdel(Portal)
QDEL_NULL(dest_crystal)
///Handle the dest_crystal var to be null if the destabilized crystal is deleted by a badmin before the end of the event
/datum/round_event/crystal_invasion/proc/on_dest_crystal_qdel()
UnregisterSignal(dest_crystal, COMSIG_PARENT_QDELETING)
processing = FALSE
message_admins("Deleted Destabilized crystal, aborting")
dest_crystal = null
kill()
/turf/open/indestructible/crystal_floor
name = "Crystal floor"
desc = "A crystallized floor"
icon_state = "noslip-damaged1"
baseturfs = /turf/open/space
/turf/open/indestructible/crystal_floor/examine(mob/user)
. += ..()
. += "<span class='notice'>The floor is made of sturdy crystals.</span>"
/turf/open/indestructible/crystal_floor/welder_act(mob/living/user, obj/item/I)
. = ..()
if(!I.tool_start_check(user, amount=0))
return FALSE
to_chat(user, "<span class='notice'>You begin heating up the crystal...</span>")
if(I.use_tool(src, user, 2.5 SECONDS, volume=100))
to_chat(user, "<span class='notice'>The crystal crumbles into dust.</span>")
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
return TRUE
return FALSE
/turf/closed/indestructible/crystal_wall
name = "Crystal wall"
desc = "A crystallized wall"
icon = 'icons/turf/mining.dmi'
icon_state = "rock_highchance"
baseturfs = /turf/open/space
/turf/closed/indestructible/crystal_wall/examine(mob/user)
. += ..()
. += "<span class='notice'>The wall is made of sturdy crystals.</span>"
/turf/open/indestructible/crystal_floor/welder_act(mob/living/user, obj/item/I)
. = ..()
if(!I.tool_start_check(user, amount=0))
return FALSE
to_chat(user, "<span class='notice'>You begin heating up the crystal...</span>")
if(I.use_tool(src, user, 2.5 SECONDS, volume=100))
to_chat(user, "<span class='notice'>The crystal crumbles into dust.</span>")
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
return TRUE
return FALSE
/*
This section is for the destabilized SM
*/
/obj/machinery/destabilized_crystal
name = "destabilized crystal"
desc = "A strangely translucent and iridescent crystal."
icon = 'icons/obj/supermatter.dmi'
icon_state = "psy"
density = TRUE
anchored = TRUE
flags_1 = PREVENT_CONTENTS_EXPLOSION_1
light_range = 4
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF
///If not active the crystal will not emit radiations and gases
var/active = TRUE
///Check if the crew managed to stop the ZK-class event by stabilizing the crystal
var/is_stabilized = FALSE
///Our sound loop
var/datum/looping_sound/destabilized_crystal/soundloop
var/changed_icon = TRUE
/obj/machinery/destabilized_crystal/Initialize()
. = ..()
soundloop = new(list(src), TRUE)
/obj/machinery/destabilized_crystal/Destroy()
QDEL_NULL(soundloop)
return ..()
/obj/machinery/destabilized_crystal/process()
if(!active)
return
if(prob(75))
radiation_pulse(src, 250, 6)
if(prob(30))
playsound(loc, 'sound/weapons/emitter2.ogg', 100, TRUE, extrarange = 10)
if(prob(15))
fire_nuclear_particle()
if(length(GLOB.huge_crystal_portals) == 0 && !changed_icon)
icon_state = "psy"
changed_icon = TRUE
var/turf/loc_turf = loc
var/datum/gas_mixture/env = loc_turf.return_air()
var/datum/gas_mixture/removed
var/gasefficency = 0.5
removed = env.remove(gasefficency * env.total_moles())
if(!removed)
return
removed.assert_gases(/datum/gas/bz, /datum/gas/miasma)
if(!removed || !removed.total_moles() || isspaceturf(loc_turf))
removed.gases[/datum/gas/bz][MOLES] += 0.5
removed.gases[/datum/gas/bz][MOLES] += 15.5
removed.gases[/datum/gas/miasma][MOLES] += 5.5
env.merge(removed)
air_update_turf(FALSE, FALSE)
/obj/machinery/destabilized_crystal/attackby(obj/item/W, mob/living/user, params)
if(!istype(user))
return
if(istype(W, /obj/item/crystal_stabilizer))
if(length(GLOB.huge_crystal_portals) > 0)
to_chat(user, "<span class='notice'>The shield protecting the crystal is still up! Close all the main portals before attempting this again!</span>")
return
var/obj/item/crystal_stabilizer/injector = W
if(!injector.filled)
to_chat(user, "<span class='notice'>\The [W] is empty!</span>")
return
to_chat(user, "<span class='notice'>You carefully begin injecting \the [src] with \the [W]... take care not to move until all the steps are finished!</span>")
if(!W.use_tool(src, user, 1 SECONDS, volume = 100))
return
to_chat(user, "<span class='notice'>Seems that \the [src] internal resonance is fading with the fluid!</span>")
playsound(get_turf(src), 'sound/effects/supermatter.ogg', 35, TRUE)
if(!W.use_tool(src, user, 1.5 SECONDS, volume = 100))
return
to_chat(user, "<span class='notice'>The [src] is reacting violently with the fluid!</span>")
fire_nuclear_particle()
radiation_pulse(src, 1000, 6)
if(!W.use_tool(src, user, 2 SECONDS, volume = 100))
return
to_chat(user, "<span class='notice'>The [src] has been restored and restabilized!</span>")
playsound(get_turf(src), 'sound/effects/supermatter.ogg', 35, TRUE)
injector.filled = FALSE
active = FALSE
is_stabilized = TRUE
/obj/machinery/destabilized_crystal/examine(mob/user)
. = ..()
. += "<span class='notice'>The Crystal appears to be heavily destabilized. Maybe it can be fixed by injecting it with something from another world.</span>"
if(length(GLOB.huge_crystal_portals) > 0)
. += "<span class='notice'>The Destabilized Crystal appears to be protected by some kind of shield.</span>"
else
. += "<span class='notice'>The shield that was protecting the Crystal is gone, it's time to restore it.</span>"
/obj/machinery/destabilized_crystal/Bumped(atom/movable/movable_atom)
if(!isliving(movable_atom))
return
var/mob/living/user = movable_atom
if(isnull(user.mind))
return
movable_atom.visible_message("<span class='danger'>\The [movable_atom] slams into \the [src] inducing a resonance... [movable_atom.p_their()] body starts to glow and burst into flames before flashing into dust!</span>",\
"<span class='userdanger'>You slam into \the [src] as your ears are filled with unearthly ringing. Your last thought is \"Oh, fuck.\"</span>",\
"<span class='hear'>You hear an unearthly noise as a wave of heat washes over you.</span>")
playsound(get_turf(src), 'sound/effects/supermatter.ogg', 50, TRUE)
consume(movable_atom)
/obj/machinery/destabilized_crystal/proc/consume(atom/movable/movable_atom)
if(isliving(movable_atom))
var/mob/living/user = movable_atom
if(user.status_flags & GODMODE || isnull(user.mind))
return
message_admins("[src] has consumed [key_name_admin(user)] [ADMIN_JMP(src)].")
investigate_log("has consumed [key_name(user)].", INVESTIGATE_SUPERMATTER)
user.dust(force = TRUE)
for(var/find_portal in GLOB.crystal_portals)
var/obj/structure/crystal_portal/portal = find_portal
portal.modify_component()
priority_announce("The sacrifice of a crewmember (hopefully the clown) appears to have weakened the portals and slowed down the number of monsters coming through!")
sound_to_playing_players('sound/misc/notice2.ogg')
/*
This section is for the crystal stabilizer item and the crystal from the closed portals
*/
/obj/item/crystal_stabilizer
name = "Supermatter Stabilizer"
desc = "Used when the Supermatter Matrix is starting to reach the destruction point."
icon = 'icons/obj/supermatter.dmi'
icon_state = "stabilizer"
slot_flags = ITEM_SLOT_BACK
w_class = WEIGHT_CLASS_HUGE
///The stabilizer is one use only
var/filled = FALSE
/obj/item/crystal_stabilizer/examine(user)
. = ..()
. += "<span class='notice'>There is a compartment for something small... like a crystal...</span>"
if(!filled)
. += "<span class='notice'>The [src] is empty.</span>"
else
. += "<span class='notice'>The [src] is full and can be used to stabilize the Supermatter.</span>"
/obj/item/crystal_stabilizer/attackby(obj/item/W, mob/living/user, params)
. = ..()
if((W.item_flags & ABSTRACT) || !istype(user))
return
if(istype(W, /obj/item/stack/sheet/otherworld_crystal))
if(filled)
return
to_chat(user, "<span class='notice'>You refill the [src]</span>")
playsound(src, 'sound/effects/spray2.ogg', 100, TRUE)
filled = TRUE
qdel(W)
/obj/item/stack/sheet/otherworld_crystal
name = "otherworld crystals"
icon_state = "otherworld-crystal"
singular_name = "otherworld crystal"
icon = 'icons/obj/stack_objects.dmi'
material_type = /datum/material/otherworld_crystal
merge_type = /obj/item/stack/sheet/otherworld_crystal
/*
This section is for the signaler part of the crystal portals
*/
/obj/item/assembly/signaler/crystal_anomaly
name = "Nothing here"
desc = "Nothing to see here."
///Link to the crystal
var/anomaly_type = /obj/structure/crystal_portal
/obj/item/assembly/signaler/crystal_anomaly/receive_signal(datum/signal/signal)
if(!signal)
return FALSE
if(signal.data["code"] != code)
return FALSE
if(suicider)
manual_suicide(suicider)
for(var/obj/structure/crystal_portal/portal in get_turf(src))
portal.closed = TRUE
qdel(portal)
return TRUE
/*
This section is for the crystal portals variations
*/
/obj/structure/crystal_portal
name = "crystal portal"
desc = "this shouldn't be here"
icon = 'icons/effects/effects.dmi'
icon_state = "anom"
color = COLOR_SILVER
anchored = TRUE
light_range = 3
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF
///Max amount of mobs that a portal can spawn in any given time
var/max_mobs = 5
///Spawn time between each mobs
var/spawn_time = 0
///Type of mob that the portal will spawn, if more than one type in the list will choose randomly
var/mob_types = list(/mob/living/simple_animal/hostile/carp)
///Fluff text for each mob spawned
var/spawn_text = "emerges from"
///Affiliation to a faction, used to stop mobs from destroying their portals (unused for now)
var/faction = list("hostile")
///Spawner component
var/spawner_type = /datum/component/spawner
///This var check if the portal has been closed by a player with a neutralizer
var/closed = FALSE
///Link to the signaler object for signaling uses
var/obj/item/assembly/signaler/crystal_anomaly/a_signal = /obj/item/assembly/signaler/crystal_anomaly
/obj/structure/crystal_portal/Initialize()
. = ..()
AddComponent(spawner_type, mob_types, spawn_time, faction, spawn_text, max_mobs)
GLOB.crystal_portals += src
mob_types = typelist("crystal_portal mob_types", mob_types)
faction = typelist("crystal_portal faction", faction)
a_signal = new a_signal(src)
a_signal.code = rand(1,100)
a_signal.anomaly_type = type
var/frequency = rand(MIN_FREE_FREQ, MAX_FREE_FREQ)
if(ISMULTIPLE(frequency, 2))//signaller frequencies are always uneven!
frequency++
a_signal.set_frequency(frequency)
/obj/structure/crystal_portal/Destroy()
GLOB.crystal_portals -= src
if(a_signal)
QDEL_NULL(a_signal)
if(!closed)
switch(name)
if("Small Portal")
explosion(loc, 0,1,3)
if("Medium Portal")
explosion(loc, 0,3,5)
if("Big Portal")
explosion(loc, 1,3,5)
if("Huge Portal")
explosion(loc, 2,5,7)
new/obj/item/stack/sheet/otherworld_crystal(loc)
return ..()
/obj/structure/crystal_portal/examine(user)
. = ..()
. += "<span class='notice'>The [src] seems to be releasing some sort or high frequency wavelength, maybe it could be closed if another signal is sent back or if an equivalent device is used on it.</span>"
/obj/structure/crystal_portal/attack_animal(mob/living/simple_animal/M)
if(faction_check(faction, M.faction, FALSE) && !M.client)
return ..()
/obj/structure/crystal_portal/attackby(obj/item/W, mob/living/user, params)
if((W.item_flags & ABSTRACT) || !istype(user))
return
if(istype(W, /obj/item/anomaly_neutralizer))
to_chat(user, "<span class='notice'>You start closing \the [src]...</span>")
if(W.use_tool(src, user, 5.5 SECONDS, volume = 100))
to_chat(user, "<span class='notice'>You successfully close \the [src]!</span>")
closed = TRUE
qdel(src)
if(W.tool_behaviour == TOOL_ANALYZER)
to_chat(user, "<span class='notice'>Analyzing... [src]'s unstable field is fluctuating along frequency [format_frequency(a_signal.frequency)], code [a_signal.code].</span>")
/obj/structure/crystal_portal/proc/modify_component()
spawn_time += 5
AddComponent(spawner_type, mob_types, spawn_time, faction, spawn_text, max_mobs)
/obj/structure/crystal_portal/small
name = "Small Portal"
desc = "A small portal to an unkown dimension!"
color = COLOR_BRIGHT_BLUE
max_mobs = 2
spawn_time = 5 SECONDS
mob_types = list(
/mob/living/simple_animal/hostile/crystal_monster/minion,
/mob/living/simple_animal/hostile/crystal_monster/thug
)
/obj/structure/crystal_portal/medium
name = "Medium Portal"
desc = "A medium portal to an unkown dimension!"
color = COLOR_GREEN
max_mobs = 3
spawn_time = 15 SECONDS
mob_types = list(
/mob/living/simple_animal/hostile/crystal_monster/minion,
/mob/living/simple_animal/hostile/crystal_monster/thug,
/mob/living/simple_animal/hostile/crystal_monster/recruit
)
/obj/structure/crystal_portal/big
name = "Big Portal"
desc = "A big portal to an unkown dimension!"
color = COLOR_RED
max_mobs = 4
spawn_time = 15 SECONDS
mob_types = list(
/mob/living/simple_animal/hostile/crystal_monster/minion,
/mob/living/simple_animal/hostile/crystal_monster/thug,
/mob/living/simple_animal/hostile/crystal_monster/recruit,
/mob/living/simple_animal/hostile/crystal_monster/killer
)
/obj/structure/crystal_portal/huge
name = "Huge Portal"
desc = "A huge portal to an unkown dimension!"
color = COLOR_BLACK
max_mobs = 2
spawn_time = 40 SECONDS
mob_types = list(
/mob/living/simple_animal/hostile/crystal_monster/boss
)
/obj/structure/crystal_portal/huge/Initialize()
. = ..()
GLOB.huge_crystal_portals += src
/obj/structure/crystal_portal/huge/Destroy()
GLOB.huge_crystal_portals -= src
return ..()
/*
This section is for the crystal monsters variations
*/
/mob/living/simple_animal/hostile/crystal_monster
name = "crystal monster"
desc = "A monster made of crystals similar to the Supermatter ones."
icon = 'icons/mob/crystal_mobs.dmi'
icon_state = "crystal_minion"
icon_living = "crystal_minion"
icon_dead = "crystal_minion"
gender = NEUTER
mob_biotypes = MOB_MINERAL|MOB_HUMANOID
turns_per_move = 1
speak_emote = list("resonates")
emote_see = list("resonates")
a_intent = INTENT_HARM
minbodytemp = 0
maxbodytemp = 1500
healable = 0 //they're crystals how would bruise packs help them??
attack_verb_continuous = "smashes"
attack_verb_simple = "smash"
attack_sound = 'sound/effects/supermatter.ogg'
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
unsuitable_atmos_damage = 10
robust_searching = 1
stat_attack = HARD_CRIT
faction = list("crystal")
see_in_dark = 8
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
deathmessage = "collapses into dust!"
del_on_death = 1
footstep_type = FOOTSTEP_MOB_SHOE
stop_automated_movement = FALSE
stop_automated_movement_when_pulled = FALSE
wander = TRUE
/mob/living/simple_animal/hostile/crystal_monster/Initialize()
. = ..()
ADD_TRAIT(src, TRAIT_TESLA_SHOCKIMMUNE, INNATE_TRAIT)
/mob/living/simple_animal/hostile/crystal_monster/minion
name = "crystal minion"
desc = "A monster made of crystals similar to the Supermatter ones."
icon_state = "crystal_minion"
icon_living = "crystal_minion"
icon_dead = "crystal_minion"
maxHealth = 20
health = 20
speed = 0.8
harm_intent_damage = 7
melee_damage_lower = 7
melee_damage_upper = 10
move_force = MOVE_FORCE_WEAK
move_resist = MOVE_FORCE_WEAK
pull_force = MOVE_FORCE_WEAK
var/death_cloud_size = 2
/mob/living/simple_animal/hostile/crystal_monster/minion/Destroy()
if(isturf(loc))
var/datum/effect_system/smoke_spread/chem/S = new
create_reagents(3)
reagents.add_reagent(/datum/reagent/toxin/lexorin, 4)
S.attach(loc)
S.set_up(reagents, death_cloud_size, loc, silent = TRUE)
S.start()
return ..()
/mob/living/simple_animal/hostile/crystal_monster/thug
name = "crystal thug"
desc = "A monster made of crystals similar to the Supermatter ones."
icon_state = "crystal_thug"
icon_living = "crystal_thug"
icon_dead = "crystal_thug"
maxHealth = 20
health = 20
speed = 0.9
harm_intent_damage = 8
melee_damage_lower = 8
melee_damage_upper = 11
move_force = MOVE_FORCE_NORMAL
move_resist = MOVE_FORCE_NORMAL
pull_force = MOVE_FORCE_NORMAL
dodging = TRUE
dodge_prob = 25
/mob/living/simple_animal/hostile/crystal_monster/thug/attackby(obj/item/O, mob/user, params)
if(prob(30))
var/list/temp_turfs = list()
for(var/turf/around_turfs in view(7, src))
if(!isopenturf(around_turfs) || isspaceturf(around_turfs))
continue
temp_turfs += around_turfs
if(length(temp_turfs))
var/turf/open/choosen_turf = pick(temp_turfs)
do_teleport(src, choosen_turf)
return
return ..()
/mob/living/simple_animal/hostile/crystal_monster/recruit
name = "crystal recruit"
desc = "A monster made of crystals similar to the Supermatter ones."
icon_state = "crystal_recruit"
icon_living = "crystal_recruit"
icon_dead = "crystal_recruit"
maxHealth = 20
health = 20
speed = 1.2
harm_intent_damage = 9
melee_damage_lower = 10
melee_damage_upper = 15
move_force = MOVE_FORCE_STRONG
move_resist = MOVE_FORCE_STRONG
pull_force = MOVE_FORCE_STRONG
obj_damage = 100
environment_smash = ENVIRONMENT_SMASH_WALLS
/mob/living/simple_animal/hostile/crystal_monster/recruit/Bump(atom/clong)
. = ..()
if(isturf(clong))
var/turf/turf_bump = clong
turf_bump.Melt()
playsound(get_turf(src), 'sound/effects/supermatter.ogg', 35, TRUE)
/mob/living/simple_animal/hostile/crystal_monster/killer
name = "crystal killer"
desc = "A monster made of crystals similar to the Supermatter ones."
icon_state = "crystal_killer"
icon_living = "crystal_killer"
icon_dead = "crystal_killer"
maxHealth = 35
health = 35
speed = 0.75
harm_intent_damage = 10
melee_damage_lower = 15
melee_damage_upper = 20
move_force = MOVE_FORCE_VERY_STRONG
move_resist = MOVE_FORCE_VERY_STRONG
pull_force = MOVE_FORCE_VERY_STRONG
dodging = TRUE
dodge_prob = 35
environment_smash = ENVIRONMENT_SMASH_RWALLS
projectiletype = /obj/projectile/temp/basilisk
projectilesound = 'sound/weapons/pierce.ogg'
ranged = 1
ranged_message = "throws"
ranged_cooldown_time = 3.5 SECONDS
/obj/projectile/temp/crystal_killer
name = "freezing blast"
icon_state = "ice_2"
color = COLOR_YELLOW
damage = 0
damage_type = BURN
nodamage = TRUE
flag = ENERGY
temperature = -75
/mob/living/simple_animal/hostile/crystal_monster/killer/Bump(atom/clong)
. = ..()
if(isturf(clong))
var/turf/turf_bump = clong
turf_bump.Melt()
playsound(get_turf(src), 'sound/effects/supermatter.ogg', 35, TRUE)
/mob/living/simple_animal/hostile/crystal_monster/boss
name = "crystal boss"
desc = "A monster made of crystals similar to the Supermatter ones."
icon_state = "crystal_boss"
icon_living = "crystal_boss"
icon_dead = "crystal_boss"
maxHealth = 150
health = 100
speed = 1.3
harm_intent_damage = 15
melee_damage_lower = 25
melee_damage_upper = 40
move_force = MOVE_FORCE_EXTREMELY_STRONG
move_resist = MOVE_FORCE_EXTREMELY_STRONG
pull_force = MOVE_FORCE_EXTREMELY_STRONG
environment_smash = ENVIRONMENT_SMASH_RWALLS
projectiletype = /obj/projectile/magic/aoe/lightning/no_zap
projectilesound = 'sound/weapons/pierce.ogg'
ranged = 1
ranged_message = "throws"
ranged_cooldown_time = 5.5 SECONDS
/mob/living/simple_animal/hostile/crystal_monster/boss/Bump(atom/clong)
. = ..()
if(isliving(clong))
var/mob/living/mob = clong
if(mob.stat >= HARD_CRIT)
mob.dust()
if(health < maxHealth)
health = min(health+30,maxHealth)
else if(isturf(clong))
var/turf/turf_bump = clong
turf_bump.Melt()
playsound(get_turf(src), 'sound/effects/supermatter.ogg', 35, TRUE)
/mob/living/simple_animal/hostile/crystal_monster/boss/AttackingTarget()
. = ..()
if(isliving(target))
var/mob/living/mob = target
if(mob.stat >= HARD_CRIT)
mob.dust()
if(health < maxHealth)
health = min(health+30,maxHealth)
else
var/obj/item/bodypart/body_part = mob.get_bodypart(pick(BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG))
if(body_part)
body_part.drop_limb(FALSE, TRUE)
else if(isturf(target))
var/turf/turf_bump = target
turf_bump.Melt()
playsound(get_turf(src), 'sound/effects/supermatter.ogg', 35, TRUE)