diff --git a/code/_globalvars/game_modes.dm b/code/_globalvars/game_modes.dm index 12fb1bae48..806ac576d4 100644 --- a/code/_globalvars/game_modes.dm +++ b/code/_globalvars/game_modes.dm @@ -13,4 +13,5 @@ GLOBAL_VAR_INIT(sac_image, null) GLOBAL_VAR_INIT(cult_vote_called, FALSE) GLOBAL_VAR_INIT(cult_mastered, FALSE) GLOBAL_VAR_INIT(reckoning_complete, FALSE) -GLOBAL_VAR_INIT(sac_complete, FALSE) \ No newline at end of file +GLOBAL_VAR_INIT(sac_complete, FALSE) +GLOBAL_DATUM(cult_narsie, /obj/singularity/narsie/large/cult) \ No newline at end of file diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index 00e6729a6f..dfabe23025 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -287,9 +287,9 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." GLOB.blood_target = null else blood_target = GLOB.blood_target - if(Cviewer) - if(Cviewer.seeking && Cviewer.master) - blood_target = Cviewer.master + if(Cviewer && Cviewer.seeking && Cviewer.master) + blood_target = Cviewer.master + desc = "Your blood sense is leading you to [Cviewer.master]" if(!blood_target) if(!GLOB.sac_complete) if(icon_state == "runed_sense0") @@ -301,22 +301,21 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." desc = "Nar-Sie demands that [GLOB.sac_mind] be sacrificed before the summoning ritual can begin." add_overlay(GLOB.sac_image) else - if(SSticker.mode.eldergod) - desc = "The sacrifice is complete, prepare to summon Nar-Sie!" - else - desc = "The summoning is complete, glory to Nar-Sie!" if(icon_state == "runed_sense1") return animate(src, transform = null, time = 1, loop = 0) angle = 0 cut_overlays() icon_state = "runed_sense1" + desc = "The sacrifice is complete, bring the wrath of Nar-Sie upon the crew!" add_overlay(narnar) return var/turf/P = get_turf(blood_target) var/turf/Q = get_turf(mob_viewer) var/area/A = get_area(P) if(P.z != Q.z) //The target is on a different Z level, we cannot sense that far. + icon_state = "runed_sense2" + desc = "[blood_target] is no longer in your sector, you cannot sense its presence here." return desc = "You are currently tracking [blood_target] in [A.name]." var/target_angle = Get_Angle(Q, P) diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 7694eb8daf..e8d575a517 100644 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -284,7 +284,7 @@ SUBSYSTEM_DEF(ticker) //Now animate the cinematic switch(station_missed) if(NUKE_NEAR_MISS) //nuke was nearby but (mostly) missed - if( mode && !override ) + if(mode && !override ) override = mode.name switch( override ) if("nuclear emergency") //Nuke wasn't on station when it blew up @@ -294,6 +294,17 @@ SUBSYSTEM_DEF(ticker) station_explosion_detonation(bomb) flick("station_intact_fade_red",cinematic) cinematic.icon_state = "summary_nukefail" + if("cult") + cinematic.icon_state = null + flick("intro_cult",cinematic) + sleep(25) + world << sound('sound/magic/enter_blood.ogg') + sleep(28) + world << sound('sound/machines/terminal_off.ogg') + sleep(20) + flick("station_corrupted",cinematic) + world << sound('sound/effects/ghost.ogg') + actually_blew_up = FALSE if("gang war") //Gang Domination (just show the override screen) cinematic.icon_state = "intro_malf_still" flick("intro_malf",cinematic) @@ -342,6 +353,13 @@ SUBSYSTEM_DEF(ticker) world << sound('sound/effects/explosionfar.ogg') station_explosion_detonation(bomb) //TODO: no idea what this case could be cinematic.icon_state = "summary_selfdes" + if("cult") //Station nuked (nuke,explosion,summary) + flick("intro_nuke",cinematic) + sleep(35) + flick("station_explode_fade_red",cinematic) + world << sound('sound/effects/explosionfar.ogg') + station_explosion_detonation(bomb) //TODO: no idea what this case could be + cinematic.icon_state = "summary_cult" if("no_core") //Nuke failed to detonate as it had no core flick("intro_nuke",cinematic) sleep(35) diff --git a/code/game/gamemodes/cult/cult_comms.dm b/code/game/gamemodes/cult/cult_comms.dm index 6258ea284d..ede9f326fc 100644 --- a/code/game/gamemodes/cult/cult_comms.dm +++ b/code/game/gamemodes/cult/cult_comms.dm @@ -152,7 +152,7 @@ return TRUE /datum/action/innate/cult/master/IsAvailable() - if(!owner.mind || !owner.mind.has_antag_datum(ANTAG_DATUM_CULT_MASTER)) + if(!owner.mind || !owner.mind.has_antag_datum(ANTAG_DATUM_CULT_MASTER) || GLOB.cult_narsie) return 0 return ..() diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm index 550d263aa3..e80159e4da 100644 --- a/code/game/gamemodes/cult/runes.dm +++ b/code/game/gamemodes/cult/runes.dm @@ -148,8 +148,7 @@ structure_check() searches for nearby cultist structures required for the invoca /obj/effect/rune/proc/fail_invoke() //This proc contains the effects of a rune if it is not invoked correctly, through either invalid wording or not enough cultists. By default, it's just a basic fizzle. - visible_message("The markings pulse with a \ - small flash of red light, then fall dark.") + visible_message("The markings pulse with a small flash of red light, then fall dark.") var/oldcolor = color color = rgb(255, 0, 0) animate(src, color = oldcolor, time = 5) @@ -307,10 +306,11 @@ structure_check() searches for nearby cultist structures required for the invoca A.forceMove(target) if(movedsomething) ..() - visible_message("There is a sharp crack of inrushing air, and everything above the rune disappears!") + visible_message("There is a sharp crack of inrushing air, and everything above the rune disappears!", null, "You hear a sharp crack.") to_chat(user, "You[moveuserlater ? "r vision blurs, and you suddenly appear somewhere else":" send everything above the rune away"].") if(moveuserlater) user.forceMove(target) + target.visible_message("There is a boom of outrushing air as something appears above the rune!", null, "You hear a boom.") else fail_invoke() @@ -478,10 +478,12 @@ structure_check() searches for nearby cultist structures required for the invoca send_to_playing_players('sound/effects/dimensional_rend.ogg') var/turf/T = get_turf(src) sleep(40) + SSticker.mode.eldergod = FALSE if(src) color = "#FF0000" - SSticker.mode.eldergod = FALSE - new /obj/singularity/narsie/large(T) //Causes Nar-Sie to spawn even if the rune has been removed + deltimer(GLOB.blood_target_reset_timer) + new /obj/singularity/narsie/large/cult(T) //Causes Nar-Sie to spawn and makes it the blood target even if the rune has been removed + /obj/effect/rune/narsie/attackby(obj/I, mob/user, params) //Since the narsie rune takes a long time to make, add logging to removal. if((istype(I, /obj/item/weapon/tome) && iscultist(user))) @@ -962,4 +964,4 @@ structure_check() searches for nearby cultist structures required for the invoca "Your link to the world fades. Your form breaks apart.") for(var/obj/I in new_human) new_human.dropItemToGround(I, TRUE) - new_human.dust() + new_human.dust() \ No newline at end of file diff --git a/code/game/gamemodes/wizard/soulstone.dm b/code/game/gamemodes/wizard/soulstone.dm index 503fc0f396..c72152668d 100644 --- a/code/game/gamemodes/wizard/soulstone.dm +++ b/code/game/gamemodes/wizard/soulstone.dm @@ -210,13 +210,16 @@ var/datum/action/innate/seek_master/SM = new() SM.Grant(newstruct) newstruct.key = target.key + var/obj/screen/alert/bloodsense/BS if(newstruct.mind && ((stoner && iscultist(stoner)) || cultoverride) && SSticker && SSticker.mode) SSticker.mode.add_cultist(newstruct.mind, 0) + BS = newstruct.alerts.Find("bloodsense") if(iscultist(stoner) || cultoverride) to_chat(newstruct, "You are still bound to serve the cult[stoner ? " and [stoner]":""], follow their orders and help them complete their goals at all costs.") else if(stoner) to_chat(newstruct, "You are still bound to serve your creator, [stoner], follow their orders and help them complete their goals at all costs.") - var/obj/screen/alert/bloodsense/BS = newstruct.throw_alert("bloodsense", /obj/screen/alert/bloodsense) + BS = newstruct.throw_alert("bloodsense", /obj/screen/alert/bloodsense) + if(BS) BS.Cviewer = newstruct newstruct.cancel_camera() diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index d15d71c23c..9ed7796f1a 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -1,361 +1,370 @@ - -/mob/living/proc/run_armor_check(def_zone = null, attack_flag = "melee", absorb_text = null, soften_text = null, armour_penetration, penetrated_text) - var/armor = getarmor(def_zone, attack_flag) - - //the if "armor" check is because this is used for everything on /living, including humans - if(armor && armour_penetration) - armor = max(0, armor - armour_penetration) - if(penetrated_text) - to_chat(src, "[penetrated_text]") - else - to_chat(src, "Your armor was penetrated!") - else if(armor >= 100) - if(absorb_text) - to_chat(src, "[absorb_text]") - else - to_chat(src, "Your armor absorbs the blow!") - else if(armor > 0) - if(soften_text) - to_chat(src, "[soften_text]") - else - to_chat(src, "Your armor softens the blow!") - return armor - - -/mob/living/proc/getarmor(def_zone, type) - return 0 - -//this returns the mob's protection against eye damage (number between -1 and 2) -/mob/living/proc/get_eye_protection() - return 0 - -//this returns the mob's protection against ear damage (0:no protection; 1: some ear protection; 2: has no ears) -/mob/living/proc/get_ear_protection() - return 0 - -/mob/living/proc/on_hit(obj/item/projectile/P) - return - -/mob/living/bullet_act(obj/item/projectile/P, def_zone) - var/armor = run_armor_check(def_zone, P.flag, "","",P.armour_penetration) - if(!P.nodamage) - apply_damage(P.damage, P.damage_type, def_zone, armor) - if(P.dismemberment) - check_projectile_dismemberment(P, def_zone) - return P.on_hit(src, armor) - -/mob/living/proc/check_projectile_dismemberment(obj/item/projectile/P, def_zone) - return 0 - -/obj/item/proc/get_volume_by_throwforce_and_or_w_class() - if(throwforce && w_class) - return Clamp((throwforce + w_class) * 5, 30, 100)// Add the item's throwforce to its weight class and multiply by 5, then clamp the value between 30 and 100 - else if(w_class) - return Clamp(w_class * 8, 20, 100) // Multiply the item's weight class by 8, then clamp the value between 20 and 100 - else - return 0 - -/mob/living/hitby(atom/movable/AM, skipcatch, hitpush = 1, blocked = 0) - if(istype(AM, /obj/item)) - var/obj/item/I = AM - var/zone = ran_zone("chest", 65)//Hits a random part of the body, geared towards the chest - var/dtype = BRUTE - var/volume = I.get_volume_by_throwforce_and_or_w_class() - if(istype(I,/obj/item/weapon)) //If the item is a weapon... - var/obj/item/weapon/W = I - dtype = W.damtype - - if (W.throwforce > 0) //If the weapon's throwforce is greater than zero... - if (W.throwhitsound) //...and throwhitsound is defined... - playsound(loc, W.throwhitsound, volume, 1, -1) //...play the weapon's throwhitsound. - else if(W.hitsound) //Otherwise, if the weapon's hitsound is defined... - playsound(loc, W.hitsound, volume, 1, -1) //...play the weapon's hitsound. - else if(!W.throwhitsound) //Otherwise, if throwhitsound isn't defined... - playsound(loc, 'sound/weapons/genhit.ogg',volume, 1, -1) //...play genhit.ogg. - - else if(!I.throwhitsound && I.throwforce > 0) //Otherwise, if the item doesn't have a throwhitsound and has a throwforce greater than zero... - playsound(loc, 'sound/weapons/genhit.ogg', volume, 1, -1)//...play genhit.ogg - if(!I.throwforce)// Otherwise, if the item's throwforce is 0... - playsound(loc, 'sound/weapons/throwtap.ogg', 1, volume, -1)//...play throwtap.ogg. - if(!blocked) - visible_message("[src] has been hit by [I].", \ - "[src] has been hit by [I].") - var/armor = run_armor_check(zone, "melee", "Your armor has protected your [parse_zone(zone)].", "Your armor has softened hit to your [parse_zone(zone)].",I.armour_penetration) - apply_damage(I.throwforce, dtype, zone, armor) - if(I.thrownby) - add_logs(I.thrownby, src, "hit", I) - else - return 1 - else - playsound(loc, 'sound/weapons/genhit.ogg', 50, 1, -1) - ..() - - -/mob/living/mech_melee_attack(obj/mecha/M) - if(M.occupant.a_intent == INTENT_HARM) - M.do_attack_animation(src) - if(M.damtype == "brute") - step_away(src,M,15) - switch(M.damtype) - if(BRUTE) - Paralyse(1) - take_overall_damage(rand(M.force/2, M.force)) - playsound(src, 'sound/weapons/punch4.ogg', 50, 1) - if(BURN) - take_overall_damage(0, rand(M.force/2, M.force)) - playsound(src, 'sound/items/Welder.ogg', 50, 1) - if(TOX) - M.mech_toxin_damage(src) - else - return - updatehealth() - visible_message("[M.name] has hit [src]!", \ - "[M.name] has hit [src]!", null, COMBAT_MESSAGE_RANGE) - add_logs(M.occupant, src, "attacked", M, "(INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])") - else - step_away(src,M) - add_logs(M.occupant, src, "pushed", M) - visible_message("[M] pushes [src] out of the way.", null, null, 5) - -/mob/living/fire_act() - adjust_fire_stacks(3) - IgniteMob() - -/mob/living/proc/grabbedby(mob/living/carbon/user, supress_message = 0) - if(user == src || anchored || !isturf(user.loc)) - return 0 - if(!user.pulling || user.pulling != src) - user.start_pulling(src, supress_message) - return - - if(!(status_flags & CANPUSH)) - to_chat(user, "[src] can't be grabbed more aggressively!") - return 0 - grippedby(user) - -//proc to upgrade a simple pull into a more aggressive grab. -/mob/living/proc/grippedby(mob/living/carbon/user) - if(user.grab_state < GRAB_KILL) - user.changeNext_move(CLICK_CD_GRABBING) - playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - - if(user.grab_state) //only the first upgrade is instantaneous - var/old_grab_state = user.grab_state - var/grab_upgrade_time = 30 - visible_message("[user] starts to tighten [user.p_their()] grip on [src]!", \ - "[user] starts to tighten [user.p_their()] grip on you!") - if(!do_mob(user, src, grab_upgrade_time)) - return 0 - if(!user.pulling || user.pulling != src || user.grab_state != old_grab_state || user.a_intent != INTENT_GRAB) - return 0 - user.grab_state++ - switch(user.grab_state) - if(GRAB_AGGRESSIVE) - add_logs(user, src, "grabbed", addition="aggressively") - visible_message("[user] has grabbed [src] aggressively!", \ - "[user] has grabbed [src] aggressively!") - drop_all_held_items() - stop_pulling() - if(GRAB_NECK) - visible_message("[user] has grabbed [src] by the neck!",\ - "[user] has grabbed you by the neck!") - update_canmove() //we fall down - if(!buckled && !density) - Move(user.loc) - if(GRAB_KILL) - visible_message("[user] is strangling [src]!", \ - "[user] is strangling you!") - update_canmove() //we fall down - if(!buckled && !density) - Move(user.loc) - return 1 - - -/mob/living/attack_slime(mob/living/simple_animal/slime/M) + +/mob/living/proc/run_armor_check(def_zone = null, attack_flag = "melee", absorb_text = null, soften_text = null, armour_penetration, penetrated_text) + var/armor = getarmor(def_zone, attack_flag) + + //the if "armor" check is because this is used for everything on /living, including humans + if(armor && armour_penetration) + armor = max(0, armor - armour_penetration) + if(penetrated_text) + to_chat(src, "[penetrated_text]") + else + to_chat(src, "Your armor was penetrated!") + else if(armor >= 100) + if(absorb_text) + to_chat(src, "[absorb_text]") + else + to_chat(src, "Your armor absorbs the blow!") + else if(armor > 0) + if(soften_text) + to_chat(src, "[soften_text]") + else + to_chat(src, "Your armor softens the blow!") + return armor + + +/mob/living/proc/getarmor(def_zone, type) + return 0 + +//this returns the mob's protection against eye damage (number between -1 and 2) +/mob/living/proc/get_eye_protection() + return 0 + +//this returns the mob's protection against ear damage (0:no protection; 1: some ear protection; 2: has no ears) +/mob/living/proc/get_ear_protection() + return 0 + +/mob/living/proc/on_hit(obj/item/projectile/P) + return + +/mob/living/bullet_act(obj/item/projectile/P, def_zone) + var/armor = run_armor_check(def_zone, P.flag, "","",P.armour_penetration) + if(!P.nodamage) + apply_damage(P.damage, P.damage_type, def_zone, armor) + if(P.dismemberment) + check_projectile_dismemberment(P, def_zone) + return P.on_hit(src, armor) + +/mob/living/proc/check_projectile_dismemberment(obj/item/projectile/P, def_zone) + return 0 + +/obj/item/proc/get_volume_by_throwforce_and_or_w_class() + if(throwforce && w_class) + return Clamp((throwforce + w_class) * 5, 30, 100)// Add the item's throwforce to its weight class and multiply by 5, then clamp the value between 30 and 100 + else if(w_class) + return Clamp(w_class * 8, 20, 100) // Multiply the item's weight class by 8, then clamp the value between 20 and 100 + else + return 0 + +/mob/living/hitby(atom/movable/AM, skipcatch, hitpush = 1, blocked = 0) + if(istype(AM, /obj/item)) + var/obj/item/I = AM + var/zone = ran_zone("chest", 65)//Hits a random part of the body, geared towards the chest + var/dtype = BRUTE + var/volume = I.get_volume_by_throwforce_and_or_w_class() + if(istype(I,/obj/item/weapon)) //If the item is a weapon... + var/obj/item/weapon/W = I + dtype = W.damtype + + if (W.throwforce > 0) //If the weapon's throwforce is greater than zero... + if (W.throwhitsound) //...and throwhitsound is defined... + playsound(loc, W.throwhitsound, volume, 1, -1) //...play the weapon's throwhitsound. + else if(W.hitsound) //Otherwise, if the weapon's hitsound is defined... + playsound(loc, W.hitsound, volume, 1, -1) //...play the weapon's hitsound. + else if(!W.throwhitsound) //Otherwise, if throwhitsound isn't defined... + playsound(loc, 'sound/weapons/genhit.ogg',volume, 1, -1) //...play genhit.ogg. + + else if(!I.throwhitsound && I.throwforce > 0) //Otherwise, if the item doesn't have a throwhitsound and has a throwforce greater than zero... + playsound(loc, 'sound/weapons/genhit.ogg', volume, 1, -1)//...play genhit.ogg + if(!I.throwforce)// Otherwise, if the item's throwforce is 0... + playsound(loc, 'sound/weapons/throwtap.ogg', 1, volume, -1)//...play throwtap.ogg. + if(!blocked) + visible_message("[src] has been hit by [I].", \ + "[src] has been hit by [I].") + var/armor = run_armor_check(zone, "melee", "Your armor has protected your [parse_zone(zone)].", "Your armor has softened hit to your [parse_zone(zone)].",I.armour_penetration) + apply_damage(I.throwforce, dtype, zone, armor) + if(I.thrownby) + add_logs(I.thrownby, src, "hit", I) + else + return 1 + else + playsound(loc, 'sound/weapons/genhit.ogg', 50, 1, -1) + ..() + + +/mob/living/mech_melee_attack(obj/mecha/M) + if(M.occupant.a_intent == INTENT_HARM) + M.do_attack_animation(src) + if(M.damtype == "brute") + step_away(src,M,15) + switch(M.damtype) + if(BRUTE) + Paralyse(1) + take_overall_damage(rand(M.force/2, M.force)) + playsound(src, 'sound/weapons/punch4.ogg', 50, 1) + if(BURN) + take_overall_damage(0, rand(M.force/2, M.force)) + playsound(src, 'sound/items/Welder.ogg', 50, 1) + if(TOX) + M.mech_toxin_damage(src) + else + return + updatehealth() + visible_message("[M.name] has hit [src]!", \ + "[M.name] has hit [src]!", null, COMBAT_MESSAGE_RANGE) + add_logs(M.occupant, src, "attacked", M, "(INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])") + else + step_away(src,M) + add_logs(M.occupant, src, "pushed", M) + visible_message("[M] pushes [src] out of the way.", null, null, 5) + +/mob/living/fire_act() + adjust_fire_stacks(3) + IgniteMob() + +/mob/living/proc/grabbedby(mob/living/carbon/user, supress_message = 0) + if(user == src || anchored || !isturf(user.loc)) + return 0 + if(!user.pulling || user.pulling != src) + user.start_pulling(src, supress_message) + return + + if(!(status_flags & CANPUSH)) + to_chat(user, "[src] can't be grabbed more aggressively!") + return 0 + grippedby(user) + +//proc to upgrade a simple pull into a more aggressive grab. +/mob/living/proc/grippedby(mob/living/carbon/user) + if(user.grab_state < GRAB_KILL) + user.changeNext_move(CLICK_CD_GRABBING) + playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + + if(user.grab_state) //only the first upgrade is instantaneous + var/old_grab_state = user.grab_state + var/grab_upgrade_time = 30 + visible_message("[user] starts to tighten [user.p_their()] grip on [src]!", \ + "[user] starts to tighten [user.p_their()] grip on you!") + if(!do_mob(user, src, grab_upgrade_time)) + return 0 + if(!user.pulling || user.pulling != src || user.grab_state != old_grab_state || user.a_intent != INTENT_GRAB) + return 0 + user.grab_state++ + switch(user.grab_state) + if(GRAB_AGGRESSIVE) + add_logs(user, src, "grabbed", addition="aggressively") + visible_message("[user] has grabbed [src] aggressively!", \ + "[user] has grabbed [src] aggressively!") + drop_all_held_items() + stop_pulling() + if(GRAB_NECK) + visible_message("[user] has grabbed [src] by the neck!",\ + "[user] has grabbed you by the neck!") + update_canmove() //we fall down + if(!buckled && !density) + Move(user.loc) + if(GRAB_KILL) + visible_message("[user] is strangling [src]!", \ + "[user] is strangling you!") + update_canmove() //we fall down + if(!buckled && !density) + Move(user.loc) + return 1 + + +/mob/living/attack_slime(mob/living/simple_animal/slime/M) if(!SSticker.HasRoundStarted()) - to_chat(M, "You cannot attack people before the game has started.") - return - - if(M.buckled) - if(M in buckled_mobs) - M.Feedstop() - return // can't attack while eating! - - if (stat != DEAD) - add_logs(M, src, "attacked") - M.do_attack_animation(src) - visible_message("The [M.name] glomps [src]!", \ - "The [M.name] glomps [src]!", null, COMBAT_MESSAGE_RANGE) - return 1 - -/mob/living/attack_animal(mob/living/simple_animal/M) - M.face_atom(src) - if(M.melee_damage_upper == 0) - M.visible_message("\The [M] [M.friendly] [src]!") - return 0 - else - if(M.attack_sound) - playsound(loc, M.attack_sound, 50, 1, 1) - M.do_attack_animation(src) - visible_message("\The [M] [M.attacktext] [src]!", \ - "\The [M] [M.attacktext] [src]!", null, COMBAT_MESSAGE_RANGE) - add_logs(M, src, "attacked") - return 1 - - -/mob/living/attack_paw(mob/living/carbon/monkey/M) - if(isturf(loc) && istype(loc.loc, /area/start)) - to_chat(M, "No attacking people at spawn, you jackass.") - return 0 - - if (M.a_intent == INTENT_HARM) - if(M.is_muzzled() || (M.wear_mask && M.wear_mask.flags_cover & MASKCOVERSMOUTH)) - to_chat(M, "You can't bite with your mouth covered!") - return 0 - M.do_attack_animation(src, ATTACK_EFFECT_BITE) - if (prob(75)) - add_logs(M, src, "attacked") - playsound(loc, 'sound/weapons/bite.ogg', 50, 1, -1) - visible_message("[M.name] bites [src]!", \ - "[M.name] bites [src]!", null, COMBAT_MESSAGE_RANGE) - return 1 - else - visible_message("[M.name] has attempted to bite [src]!", \ - "[M.name] has attempted to bite [src]!", null, COMBAT_MESSAGE_RANGE) - return 0 - -/mob/living/attack_larva(mob/living/carbon/alien/larva/L) - switch(L.a_intent) - if("help") - visible_message("[L.name] rubs its head against [src].") - return 0 - - else - L.do_attack_animation(src) - if(prob(90)) - add_logs(L, src, "attacked") - visible_message("[L.name] bites [src]!", \ - "[L.name] bites [src]!", null, COMBAT_MESSAGE_RANGE) - playsound(loc, 'sound/weapons/bite.ogg', 50, 1, -1) - return 1 - else - visible_message("[L.name] has attempted to bite [src]!", \ - "[L.name] has attempted to bite [src]!", null, COMBAT_MESSAGE_RANGE) - return 0 - -/mob/living/attack_alien(mob/living/carbon/alien/humanoid/M) - switch(M.a_intent) - if ("help") - visible_message("[M] caresses [src] with its scythe like arm.") - return 0 - - if ("grab") - grabbedby(M) - return 0 - if("harm") - M.do_attack_animation(src) - return 1 - if("disarm") - M.do_attack_animation(src, ATTACK_EFFECT_DISARM) - return 1 - -/mob/living/ex_act(severity, target, origin) - if(origin && istype(origin, /datum/spacevine_mutation) && isvineimmune(src)) - return - ..() - -//Looking for irradiate()? It's been moved to radiation.dm under the rad_act() for mobs. - -/mob/living/acid_act(acidpwr, acid_volume) - take_bodypart_damage(acidpwr * min(1, acid_volume * 0.1)) - return 1 - -/mob/living/proc/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE) + to_chat(M, "You cannot attack people before the game has started.") + return + + if(M.buckled) + if(M in buckled_mobs) + M.Feedstop() + return // can't attack while eating! + + if (stat != DEAD) + add_logs(M, src, "attacked") + M.do_attack_animation(src) + visible_message("The [M.name] glomps [src]!", \ + "The [M.name] glomps [src]!", null, COMBAT_MESSAGE_RANGE) + return 1 + +/mob/living/attack_animal(mob/living/simple_animal/M) + M.face_atom(src) + if(M.melee_damage_upper == 0) + M.visible_message("\The [M] [M.friendly] [src]!") + return 0 + else + if(M.attack_sound) + playsound(loc, M.attack_sound, 50, 1, 1) + M.do_attack_animation(src) + visible_message("\The [M] [M.attacktext] [src]!", \ + "\The [M] [M.attacktext] [src]!", null, COMBAT_MESSAGE_RANGE) + add_logs(M, src, "attacked") + return 1 + + +/mob/living/attack_paw(mob/living/carbon/monkey/M) + if(isturf(loc) && istype(loc.loc, /area/start)) + to_chat(M, "No attacking people at spawn, you jackass.") + return 0 + + if (M.a_intent == INTENT_HARM) + if(M.is_muzzled() || (M.wear_mask && M.wear_mask.flags_cover & MASKCOVERSMOUTH)) + to_chat(M, "You can't bite with your mouth covered!") + return 0 + M.do_attack_animation(src, ATTACK_EFFECT_BITE) + if (prob(75)) + add_logs(M, src, "attacked") + playsound(loc, 'sound/weapons/bite.ogg', 50, 1, -1) + visible_message("[M.name] bites [src]!", \ + "[M.name] bites [src]!", null, COMBAT_MESSAGE_RANGE) + return 1 + else + visible_message("[M.name] has attempted to bite [src]!", \ + "[M.name] has attempted to bite [src]!", null, COMBAT_MESSAGE_RANGE) + return 0 + +/mob/living/attack_larva(mob/living/carbon/alien/larva/L) + switch(L.a_intent) + if("help") + visible_message("[L.name] rubs its head against [src].") + return 0 + + else + L.do_attack_animation(src) + if(prob(90)) + add_logs(L, src, "attacked") + visible_message("[L.name] bites [src]!", \ + "[L.name] bites [src]!", null, COMBAT_MESSAGE_RANGE) + playsound(loc, 'sound/weapons/bite.ogg', 50, 1, -1) + return 1 + else + visible_message("[L.name] has attempted to bite [src]!", \ + "[L.name] has attempted to bite [src]!", null, COMBAT_MESSAGE_RANGE) + return 0 + +/mob/living/attack_alien(mob/living/carbon/alien/humanoid/M) + switch(M.a_intent) + if ("help") + visible_message("[M] caresses [src] with its scythe like arm.") + return 0 + + if ("grab") + grabbedby(M) + return 0 + if("harm") + M.do_attack_animation(src) + return 1 + if("disarm") + M.do_attack_animation(src, ATTACK_EFFECT_DISARM) + return 1 + +/mob/living/ex_act(severity, target, origin) + if(origin && istype(origin, /datum/spacevine_mutation) && isvineimmune(src)) + return + ..() + +//Looking for irradiate()? It's been moved to radiation.dm under the rad_act() for mobs. + +/mob/living/acid_act(acidpwr, acid_volume) + take_bodypart_damage(acidpwr * min(1, acid_volume * 0.1)) + return 1 + +/mob/living/proc/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE) if(tesla_shock && HAS_SECONDARY_FLAG(src, TESLA_IGNORE)) - return FALSE - if(shock_damage > 0) - if(!illusion) - adjustFireLoss(shock_damage) - visible_message( - "[src] was shocked by \the [source]!", \ - "You feel a powerful shock coursing through your body!", \ - "You hear a heavy electrical crack." \ - ) - return shock_damage - -/mob/living/emp_act(severity) - var/list/L = src.get_contents() - for(var/obj/O in L) - O.emp_act(severity) - ..() - -/mob/living/singularity_act() - var/gain = 20 - investigate_log("([key_name(src)]) has been consumed by the singularity.","singulo") //Oh that's where the clown ended up! - gib() - return(gain) - -/mob/living/narsie_act() - if(status_flags & GODMODE) - return - - if(is_servant_of_ratvar(src) && !stat) - to_chat(src, "You resist Nar-Sie's influence... but not all of it. Run!") - adjustBruteLoss(35) - if(src && reagents) - reagents.add_reagent("heparin", 5) - return FALSE - if(client) + return FALSE + if(shock_damage > 0) + if(!illusion) + adjustFireLoss(shock_damage) + visible_message( + "[src] was shocked by \the [source]!", \ + "You feel a powerful shock coursing through your body!", \ + "You hear a heavy electrical crack." \ + ) + return shock_damage + +/mob/living/emp_act(severity) + var/list/L = src.get_contents() + for(var/obj/O in L) + O.emp_act(severity) + ..() + +/mob/living/singularity_act() + var/gain = 20 + investigate_log("([key_name(src)]) has been consumed by the singularity.","singulo") //Oh that's where the clown ended up! + gib() + return(gain) + +/mob/living/narsie_act() + if(status_flags & GODMODE) + return + + if(is_servant_of_ratvar(src) && !stat) + to_chat(src, "You resist Nar-Sie's influence... but not all of it. Run!") + adjustBruteLoss(35) + if(src && reagents) + reagents.add_reagent("heparin", 5) + return FALSE + if(GLOB.cult_narsie && GLOB.cult_narsie.souls_needed[src]) + GLOB.cult_narsie.resize(1.1) + GLOB.cult_narsie.souls_needed -= src + GLOB.cult_narsie.souls += 1 + if((GLOB.cult_narsie.souls == GLOB.cult_narsie.soul_goal) && (GLOB.cult_narsie.resolved == FALSE)) + GLOB.cult_narsie.resolved = TRUE + world << sound('sound/machines/Alarm.ogg') + addtimer(CALLBACK(GLOBAL_PROC, .proc/cult_ending_helper, 1), 120) + addtimer(CALLBACK(GLOBAL_PROC, .proc/ending_helper), 270) + if(client) makeNewConstruct(/mob/living/simple_animal/hostile/construct/harvester, src, cultoverride = TRUE) - else + else switch(rand(1, 6)) - if(1) - new /mob/living/simple_animal/hostile/construct/armored/hostile(get_turf(src)) - if(2) - new /mob/living/simple_animal/hostile/construct/wraith/hostile(get_turf(src)) - if(3 to 6) - new /mob/living/simple_animal/hostile/construct/builder/hostile(get_turf(src)) - spawn_dust() - gib() - return TRUE - - -/mob/living/ratvar_act() - if(status_flags & GODMODE) - return - - if(stat != DEAD && !is_servant_of_ratvar(src)) - for(var/obj/item/weapon/implant/mindshield/M in implants) - qdel(M) - if(!add_servant_of_ratvar(src)) - to_chat(src, "A blinding light boils you alive! Run!") - adjustFireLoss(35) - if(src) - adjust_fire_stacks(1) - IgniteMob() - return FALSE - return TRUE - - -//called when the mob receives a bright flash -/mob/living/proc/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /obj/screen/fullscreen/flash) - if(get_eye_protection() < intensity && (override_blindness_check || !(disabilities & BLIND))) - overlay_fullscreen("flash", type) - addtimer(CALLBACK(src, .proc/clear_fullscreen, "flash", 25), 25) - return 1 - -//called when the mob receives a loud bang -/mob/living/proc/soundbang_act() - return 0 - -//to damage the clothes worn by a mob -/mob/living/proc/damage_clothes(damage_amount, damage_type = BRUTE, damage_flag = 0, def_zone) - return - - -/mob/living/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect, end_pixel_y) - if(A != src) - end_pixel_y = get_standard_pixel_y_offset(lying) - used_item = get_active_held_item() - ..() - floating = 0 // If we were without gravity, the bouncing animation got stopped, so we make sure we restart the bouncing after the next movement. + if(1) + new /mob/living/simple_animal/hostile/construct/armored/hostile(get_turf(src)) + if(2) + new /mob/living/simple_animal/hostile/construct/wraith/hostile(get_turf(src)) + if(3 to 6) + new /mob/living/simple_animal/hostile/construct/builder/hostile(get_turf(src)) + spawn_dust() + gib() + return TRUE + + +/mob/living/ratvar_act() + if(status_flags & GODMODE) + return + + if(stat != DEAD && !is_servant_of_ratvar(src)) + for(var/obj/item/weapon/implant/mindshield/M in implants) + qdel(M) + if(!add_servant_of_ratvar(src)) + to_chat(src, "A blinding light boils you alive! Run!") + adjustFireLoss(35) + if(src) + adjust_fire_stacks(1) + IgniteMob() + return FALSE + return TRUE + + +//called when the mob receives a bright flash +/mob/living/proc/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /obj/screen/fullscreen/flash) + if(get_eye_protection() < intensity && (override_blindness_check || !(disabilities & BLIND))) + overlay_fullscreen("flash", type) + addtimer(CALLBACK(src, .proc/clear_fullscreen, "flash", 25), 25) + return 1 + +//called when the mob receives a loud bang +/mob/living/proc/soundbang_act() + return 0 + +//to damage the clothes worn by a mob +/mob/living/proc/damage_clothes(damage_amount, damage_type = BRUTE, damage_flag = 0, def_zone) + return + + +/mob/living/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect, end_pixel_y) + if(A != src) + end_pixel_y = get_standard_pixel_y_offset(lying) + used_item = get_active_held_item() + ..() + floating = 0 // If we were without gravity, the bouncing animation got stopped, so we make sure we restart the bouncing after the next movement. \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm index 7c2fcb178b..f988fc2477 100644 --- a/code/modules/mob/living/simple_animal/constructs.dm +++ b/code/modules/mob/living/simple_animal/constructs.dm @@ -171,7 +171,30 @@ attacktext = "slashes" attack_sound = 'sound/weapons/bladeslice.ogg' construct_spells = list(/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift) - playstyle_string = "You are a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls." + playstyle_string = "You are a Wraith. Though relatively fragile, you are fast, deadly, can phase through walls, and your attacks will lower the cooldown on phasing." + var/attack_refund = 10 //1 second per attack + var/crit_refund = 50 //5 seconds when putting a target into critical + var/kill_refund = 250 //full refund on kills + +/mob/living/simple_animal/hostile/construct/wraith/AttackingTarget() //refund jaunt cooldown when attacking living targets + var/prev_stat + if(isliving(target) && !iscultist(target)) + var/mob/living/L = target + prev_stat = L.stat + + . = ..() + + if(. && isnum(prev_stat)) + var/mob/living/L = target + var/refund = 0 + if(QDELETED(L) || (L.stat == DEAD && prev_stat != DEAD)) //they're dead, you killed them + refund += kill_refund + else if(L.InCritical() && prev_stat == CONSCIOUS) //you knocked them into critical + refund += crit_refund + if(L.stat != DEAD && prev_stat != DEAD) + refund += attack_refund + for(var/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/S in mob_spell_list) + S.charge_counter = min(S.charge_counter + refund, S.charge_max) /mob/living/simple_animal/hostile/construct/wraith/hostile //actually hostile, will move around, hit things AIStatus = AI_ON @@ -265,8 +288,8 @@ name = "Harvester" real_name = "Harvester" desc = "A long, thin construct built to herald Nar-Sie's rise. It'll be all over soon." - icon_state = "harvester" - icon_living = "harvester" + icon_state = "chosen" + icon_living = "chosen" maxHealth = 60 health = 60 sight = SEE_MOBS @@ -277,9 +300,10 @@ construct_spells = list(/obj/effect/proc_holder/spell/aoe_turf/area_conversion, /obj/effect/proc_holder/spell/aoe_turf/conjure/lesserforcewall) playstyle_string = "You are a Harvester. You are incapable of directly killing humans, but your attacks will remove their limbs: \ - Bring those who still cling to this world of illusion back to the Geometer so they may know Truth." + Bring those who still cling to this world of illusion back to the Geometer so they may know Truth. Your form and any you are pulling can pass through runed walls effortlessly." can_repair_constructs = TRUE + /mob/living/simple_animal/hostile/construct/harvester/Bump(atom/AM) . = ..() if(istype(AM, /turf/closed/wall/mineral/cult) && AM != loc) //we can go through cult walls @@ -306,6 +330,8 @@ if(!LAZYLEN(parts)) if(undismembermerable_limbs) //they have limbs we can't remove, and no parts we can, attack! return ..() + C.Weaken(30) + visible_message("[src] paralyzes [C]!") to_chat(src, "\"Bring [C.p_them()] to me.\"") return FALSE do_attack_animation(C) @@ -314,6 +340,11 @@ return FALSE . = ..() +/mob/living/simple_animal/hostile/construct/harvester/Initialize() + . = ..() + var/datum/action/innate/seek_prey/seek = new() + seek.Grant(src) + seek.Activate() ///////////////////////Master-Tracker/////////////////////// @@ -326,11 +357,14 @@ var/tracking = FALSE var/mob/living/simple_animal/hostile/construct/the_construct + /datum/action/innate/seek_master/Grant(var/mob/living/C) the_construct = C ..() /datum/action/innate/seek_master/Activate() + if(!SSticker.mode.eldergod) + the_construct.master = GLOB.blood_target if(!the_construct.master) to_chat(the_construct, "You have no master to seek!") the_construct.seeking = FALSE @@ -346,6 +380,43 @@ to_chat(the_construct, "You are now tracking your master.") +/datum/action/innate/seek_prey + name = "Seek the Harvest" + desc = "None can hide from Nar'Sie, activate to track a survivor attempting to flee the red harvest!" + background_icon_state = "bg_demon" + buttontooltipstyle = "cult" + button_icon_state = "cult_mark" + var/tracking = FALSE + var/mob/living/simple_animal/hostile/construct/harvester/the_construct + +/datum/action/innate/seek_prey/Grant(var/mob/living/C) + the_construct = C + ..() + +/datum/action/innate/seek_prey/Activate() + if(GLOB.cult_narsie == null) + return + if(tracking) + desc = "None can hide from Nar'Sie, activate to track a survivor attempting to flee the red harvest!" + button_icon_state = "cult_mark" + tracking = FALSE + the_construct.seeking = FALSE + the_construct.master = GLOB.cult_narsie + to_chat(the_construct, "You are now tracking Nar'Sie, return to reap the harvest!") + return + else + if(LAZYLEN(GLOB.cult_narsie.souls_needed)) + the_construct.master = pick(GLOB.cult_narsie.souls_needed) + to_chat(the_construct, "You are now tracking your prey, [the_construct.master] - harvest them!") + else + to_chat(the_construct, "Nar'Sie has completed her harvest!") + return + desc = "Activate to track Nar'Sie!" + button_icon_state = "sintouch" + tracking = TRUE + the_construct.seeking = TRUE + + /////////////////////////////ui stuff///////////////////////////// /mob/living/simple_animal/hostile/construct/update_health_hud() diff --git a/code/modules/power/singularity/narsie.dm b/code/modules/power/singularity/narsie.dm index 1997b6bcbd..16910e05a6 100644 --- a/code/modules/power/singularity/narsie.dm +++ b/code/modules/power/singularity/narsie.dm @@ -36,11 +36,59 @@ if(A) var/mutable_appearance/alert_overlay = mutable_appearance('icons/effects/effects.dmi', "ghostalertsie") notify_ghosts("Nar-Sie has risen in \the [A.name]. Reach out to the Geometer to be given a new shell for your soul.", source = src, alert_overlay = alert_overlay, action=NOTIFY_ATTACK) + INVOKE_ASYNC(src, .proc/narsie_spawn_animation) - narsie_spawn_animation() +/obj/singularity/narsie/large/cult // For the new cult ending, guaranteed to end the round within 3 minutes + var/list/souls_needed = list() + var/soul_goal = 0 + var/souls = 0 + var/resolved = FALSE - sleep(19) - SSshuttle.emergency.request(null, set_coefficient = 0) //instantly arrives +/obj/singularity/narsie/large/cult/proc/resize(var/ratio) + var/matrix/ntransform = matrix(transform) //aka transform.Copy() + ntransform.Scale(ratio) + animate(src, transform = ntransform, time = 40, easing = EASE_IN|EASE_OUT) + +/obj/singularity/narsie/large/cult/Initialize() + . = ..() + GLOB.cult_narsie = src + GLOB.blood_target = src + resize(0.6) + for(var/datum/mind/cult_mind in SSticker.mode.cult) + if(isliving(cult_mind.current)) + var/mob/living/L = cult_mind.current + L.narsie_act() + for(var/mob/living/player in GLOB.player_list) + if(player.stat != DEAD && player.loc.z == ZLEVEL_STATION && !iscultist(player) && isliving(player)) + souls_needed[player] = TRUE + soul_goal = round(1 + LAZYLEN(souls_needed) * 0.6) + INVOKE_ASYNC(src, .proc/begin_the_end) + +/obj/singularity/narsie/large/cult/proc/begin_the_end() + sleep(50) + priority_announce("An acausal dimensional event has been detected in your sector. Event has been flagged EXTINCTION-CLASS. Directing all available assets toward simulating solutions. SOLUTION ETA: 60 SECONDS.","Central Command Higher Dimensional Affairs", 'sound/misc/airraid.ogg') + sleep(550) + priority_announce("Simulations on acausal dimensional event complete. Deploying solution package now. Deployment ETA: TWO MINUTES. ","Central Command Higher Dimensional Affairs") + sleep(50) + set_security_level("delta") + SSshuttle.registerHostileEnvironment(src) + SSshuttle.lockdown = TRUE + sleep(1150) + if(resolved == FALSE) + resolved = TRUE + world << sound('sound/machines/Alarm.ogg') + addtimer(CALLBACK(GLOBAL_PROC, .proc/cult_ending_helper), 120) + addtimer(CALLBACK(GLOBAL_PROC, .proc/ending_helper), 220) + +/obj/singularity/narsie/large/cult/Destroy() + GLOB.cult_narsie = null + return ..() + +/proc/ending_helper() + SSticker.force_ending = 1 + +/proc/cult_ending_helper(var/no_explosion = 0) + SSticker.station_explosion_cinematic(no_explosion, "cult", null) /obj/singularity/narsie/large/attack_ghost(mob/dead/observer/user as mob) @@ -134,7 +182,7 @@ return to_chat(target, "NAR-SIE HAS LOST INTEREST IN YOU.") target = food - if(isliving(target)) + if(ishuman(target)) to_chat(target, "NAR-SIE HUNGERS FOR YOUR SOUL.") else to_chat(target, "NAR-SIE HAS CHOSEN YOU TO LEAD HER TO HER NEXT MEAL.") diff --git a/icons/effects/station_explosion.dmi b/icons/effects/station_explosion.dmi index 7dcab37710..bddc66e137 100644 Binary files a/icons/effects/station_explosion.dmi and b/icons/effects/station_explosion.dmi differ diff --git a/icons/mob/mob.dmi b/icons/mob/mob.dmi index 95b5721d5a..72aa86f600 100644 Binary files a/icons/mob/mob.dmi and b/icons/mob/mob.dmi differ diff --git a/tgstation.dme b/tgstation.dme index fc80c6ef65..64d7bb0231 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -710,10 +710,10 @@ #include "code\game\objects\effects\decals\decal.dm" #include "code\game\objects\effects\decals\misc.dm" #include "code\game\objects\effects\decals\remains.dm" -#include "code\game\objects\effects\decals\cleanable\aliens.dm" -#include "code\game\objects\effects\decals\cleanable\humans.dm" -#include "code\game\objects\effects\decals\cleanable\misc.dm" -#include "code\game\objects\effects\decals\cleanable\robots.dm" +#include "code\game\objects\effects\decals\Cleanable\aliens.dm" +#include "code\game\objects\effects\decals\Cleanable\humans.dm" +#include "code\game\objects\effects\decals\Cleanable\misc.dm" +#include "code\game\objects\effects\decals\Cleanable\robots.dm" #include "code\game\objects\effects\effect_system\effect_system.dm" #include "code\game\objects\effects\effect_system\effects_explosion.dm" #include "code\game\objects\effects\effect_system\effects_foam.dm" diff --git a/tgstation.dme.rej b/tgstation.dme.rej deleted file mode 100644 index 2e9484f3ee..0000000000 --- a/tgstation.dme.rej +++ /dev/null @@ -1,16 +0,0 @@ -diff a/tgstation.dme b/tgstation.dme (rejected hunks) -@@ -677,10 +677,10 @@ - #include "code\game\objects\effects\decals\decal.dm" - #include "code\game\objects\effects\decals\misc.dm" - #include "code\game\objects\effects\decals\remains.dm" --#include "code\game\objects\effects\decals\Cleanable\aliens.dm" --#include "code\game\objects\effects\decals\Cleanable\humans.dm" --#include "code\game\objects\effects\decals\Cleanable\misc.dm" --#include "code\game\objects\effects\decals\Cleanable\robots.dm" -+#include "code\game\objects\effects\decals\cleanable\aliens.dm" -+#include "code\game\objects\effects\decals\cleanable\humans.dm" -+#include "code\game\objects\effects\decals\cleanable\misc.dm" -+#include "code\game\objects\effects\decals\cleanable\robots.dm" - #include "code\game\objects\effects\effect_system\effect_system.dm" - #include "code\game\objects\effects\effect_system\effects_explosion.dm" - #include "code\game\objects\effects\effect_system\effects_foam.dm"