diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_biodome_winter.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_biodome_winter.dmm index 975fc20982a..e5d83859df9 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_surface_biodome_winter.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_biodome_winter.dmm @@ -6,7 +6,7 @@ /obj/machinery/shower{ pixel_y = 12 }, -/turf/open/floor/iron/white, +/turf/open/floor/iron/white/textured_large, /area/ruin/powered/snow_biodome) "ac" = ( /obj/item/stack/medical/ointment, @@ -29,37 +29,37 @@ /turf/open/floor/plating, /area/ruin/powered/snow_biodome) "ag" = ( -/obj/structure/reagent_dispensers/beerkeg, -/obj/machinery/light/small/directional/north, -/turf/open/floor/plating, +/obj/structure/displaycase/freezeray, +/turf/open/floor/iron/dark/textured, /area/ruin/powered/snow_biodome) "ah" = ( -/obj/item/reagent_containers/food/drinks/mug, -/turf/open/floor/plating, +/obj/machinery/light/cold/directional/west, +/turf/open/floor/circuit, /area/ruin/powered/snow_biodome) "ai" = ( /obj/structure/sink{ pixel_y = 26 }, -/turf/open/floor/iron/white, +/obj/structure/mirror/directional/east, +/turf/open/floor/iron/white/textured_large, /area/ruin/powered/snow_biodome) "aj" = ( -/turf/open/floor/iron/white, +/turf/open/floor/iron/white/textured_large, /area/ruin/powered/snow_biodome) "ak" = ( /turf/open/floor/plating/asteroid/snow, /area/ruin/powered/snow_biodome) "al" = ( -/obj/effect/decal/cleanable/oil, +/obj/structure/reagent_dispensers/beerkeg, /turf/open/floor/plating, /area/ruin/powered/snow_biodome) "am" = ( -/obj/machinery/door/airlock/hatch, +/obj/item/reagent_containers/food/drinks/mug, /turf/open/floor/plating, /area/ruin/powered/snow_biodome) "an" = ( /obj/machinery/light/small/directional/south, -/turf/open/floor/iron/white, +/turf/open/floor/iron/white/textured_large, /area/ruin/powered/snow_biodome) "ao" = ( /turf/open/floor/plating/ice, @@ -73,11 +73,13 @@ /area/ruin/powered/snow_biodome) "ar" = ( /obj/machinery/door/airlock/wood, -/turf/open/floor/plating, +/turf/open/floor/wood/parquet, /area/ruin/powered/snow_biodome) "as" = ( -/obj/structure/fans, -/turf/closed/wall/mineral/wood, +/obj/machinery/door/poddoor{ + id = "winter_biodome" + }, +/turf/open/floor/iron/dark/textured, /area/ruin/powered/snow_biodome) "at" = ( /turf/open/floor/wood, @@ -85,7 +87,7 @@ "au" = ( /obj/structure/bed, /obj/item/bedsheet/blue, -/turf/open/floor/wood, +/turf/open/floor/wood/parquet, /area/ruin/powered/snow_biodome) "av" = ( /obj/structure/bookcase/random, @@ -93,28 +95,34 @@ /area/ruin/powered/snow_biodome) "aw" = ( /obj/structure/table/wood, +/obj/item/flashlight/lamp/green, /turf/open/floor/wood, /area/ruin/powered/snow_biodome) "ax" = ( /obj/structure/table/wood, -/obj/item/food/canned/beans, +/obj/item/food/canned/beans{ + pixel_y = 6 + }, +/obj/machinery/button/door{ + desc = "Who the hell hid behind a can of beans?"; + id = "winter_biodome"; + name = "secret button" + }, +/obj/effect/turf_decal/siding/wood{ + dir = 1 + }, /turf/open/floor/wood, /area/ruin/powered/snow_biodome) "ay" = ( -/obj/structure/closet/crate/trashcart, -/obj/item/trash/semki, -/obj/item/trash/candy, -/turf/open/floor/plating/asteroid/snow, +/obj/machinery/door/airlock/wood, +/turf/open/floor/wood, /area/ruin/powered/snow_biodome) "az" = ( /obj/structure/flora/tree/pine, /turf/open/floor/plating/asteroid/snow, /area/ruin/powered/snow_biodome) "aA" = ( -/obj/structure/chair/comfy/brown{ - dir = 4 - }, -/turf/open/floor/wood, +/turf/open/floor/wood/parquet, /area/ruin/powered/snow_biodome) "aB" = ( /obj/structure/flora/rock/pile/icy, @@ -125,8 +133,8 @@ /turf/open/floor/plating/asteroid/snow, /area/ruin/powered/snow_biodome) "aD" = ( -/obj/machinery/door/airlock/wood, -/turf/open/floor/wood, +/obj/machinery/light/small/directional/east, +/turf/open/floor/wood/parquet, /area/ruin/powered/snow_biodome) "aE" = ( /obj/structure/extinguisher_cabinet, @@ -159,11 +167,13 @@ /obj/structure/toilet{ dir = 8 }, -/turf/open/floor/iron/white, +/turf/open/floor/iron/white/textured_large, /area/ruin/powered/snow_biodome) "aL" = ( -/obj/structure/displaycase/captain, -/turf/open/floor/wood, +/obj/structure/closet/crate/trashcart, +/obj/item/trash/semki, +/obj/item/trash/candy, +/turf/open/floor/plating/asteroid/snow, /area/ruin/powered/snow_biodome) "aM" = ( /turf/open/floor/carpet, @@ -190,6 +200,9 @@ /turf/open/floor/plating/asteroid/snow, /area/ruin/powered/snow_biodome) "bl" = ( +/obj/effect/turf_decal/siding/wood{ + dir = 1 + }, /turf/open/floor/wood{ initial_gas_mix = "o2=22;n2=82;TEMP=180"; name = "bridge" @@ -201,7 +214,7 @@ /area/ruin/powered/snow_biodome) "bw" = ( /obj/machinery/light/small/directional/west, -/turf/open/floor/wood, +/turf/open/floor/wood/parquet, /area/ruin/powered/snow_biodome) "bx" = ( /obj/machinery/light/directional/east, @@ -249,8 +262,16 @@ /area/ruin/powered/snow_biodome) "gz" = ( /obj/structure/chair/stool/directional/north, +/mob/living/simple_animal/hostile/skeleton/eskimo, /turf/open/floor/pod/dark, /area/ruin/powered/snow_biodome) +"hr" = ( +/mob/living/simple_animal/hostile/skeleton/eskimo, +/obj/effect/turf_decal/siding/wood{ + dir = 1 + }, +/turf/open/floor/wood, +/area/ruin/powered/snow_biodome) "hA" = ( /obj/machinery/light/built/directional/north, /obj/effect/mapping_helpers/no_lava, @@ -258,6 +279,17 @@ initial_gas_mix = "LAVALAND_ATMOS" }, /area/ruin/powered/snow_biodome) +"og" = ( +/turf/open/floor/iron/stairs{ + dir = 1 + }, +/area/ruin/powered/snow_biodome) +"pr" = ( +/obj/effect/turf_decal/siding/wood/corner{ + dir = 4 + }, +/turf/open/floor/plating/asteroid/snow, +/area/ruin/powered/snow_biodome) "qt" = ( /obj/machinery/door/airlock/silver, /obj/structure/fans/tiny, @@ -273,6 +305,17 @@ "tl" = ( /turf/open/floor/pod/light, /area/ruin/powered/snow_biodome) +"vr" = ( +/obj/machinery/light/small/directional/north, +/turf/open/floor/plating/asteroid/snow, +/area/ruin/powered/snow_biodome) +"vN" = ( +/turf/open/floor/plating/snowed/smoothed, +/area/ruin/powered/snow_biodome) +"wN" = ( +/obj/machinery/light/cold/directional/east, +/turf/open/floor/circuit, +/area/ruin/powered/snow_biodome) "xU" = ( /obj/item/storage/toolbox/mechanical, /turf/open/floor/pod/dark, @@ -288,6 +331,12 @@ /obj/item/paper, /turf/open/floor/pod/dark, /area/ruin/powered/snow_biodome) +"BM" = ( +/obj/effect/turf_decal/siding/wood/corner{ + dir = 8 + }, +/turf/open/floor/plating/asteroid/snow, +/area/ruin/powered/snow_biodome) "Dd" = ( /obj/structure/rack, /obj/item/clothing/suit/hooded/wintercoat/science, @@ -304,6 +353,15 @@ /obj/machinery/door/airlock/glass_large, /turf/open/floor/pod/dark, /area/ruin/powered/snow_biodome) +"EA" = ( +/obj/effect/turf_decal/siding/wood/corner, +/turf/open/floor/plating/asteroid/snow, +/area/ruin/powered/snow_biodome) +"Gi" = ( +/obj/structure/flora/bush, +/obj/machinery/light/small/directional/north, +/turf/open/floor/plating/asteroid/snow, +/area/ruin/powered/snow_biodome) "HP" = ( /turf/open/floor/pod/dark, /area/ruin/powered/snow_biodome) @@ -344,6 +402,13 @@ /obj/item/paper_bin, /turf/open/floor/pod/dark, /area/ruin/powered/snow_biodome) +"PM" = ( +/obj/effect/turf_decal/siding/wood, +/turf/open/floor/wood{ + initial_gas_mix = "o2=22;n2=82;TEMP=180"; + name = "bridge" + }, +/area/ruin/powered/snow_biodome) "Qa" = ( /obj/effect/mapping_helpers/no_lava, /obj/effect/mapping_helpers/no_lava, @@ -375,9 +440,21 @@ }, /turf/open/floor/pod/dark, /area/ruin/powered/snow_biodome) +"VE" = ( +/obj/effect/turf_decal/siding/wood/corner{ + dir = 1 + }, +/turf/open/floor/plating/asteroid/snow, +/area/ruin/powered/snow_biodome) "Wg" = ( /turf/closed/wall/r_wall, /area/ruin/powered/snow_biodome) +"Zx" = ( +/obj/effect/turf_decal/siding/wood{ + dir = 1 + }, +/turf/open/floor/wood, +/area/ruin/powered/snow_biodome) (1,1,1) = {" aa @@ -399,8 +476,8 @@ Wg Wg Wg Wg -Wg -Wg +aa +aa aa aa aa @@ -670,12 +747,12 @@ tb (10,1,1) = {" Wg Wg -af +al Wg -ak -ak -ak -ak +aq +aq +aq +aq ak ak ak @@ -686,8 +763,8 @@ ao ak aI az -ak -ak +pr +EA ak ak ak @@ -702,11 +779,11 @@ tb (11,1,1) = {" Wg ac -af -aq -aq +am aq aq +au +au aq aq aq @@ -719,7 +796,7 @@ ao ak ak bl -bl +PM ak aI ak @@ -737,10 +814,10 @@ ad af ar bw -at +aA aD +ar at -aL aq ak az @@ -751,7 +828,7 @@ ao ao ao bl -bl +PM ak ak ak @@ -766,12 +843,12 @@ tb (13,1,1) = {" Wg ae -af +am aq au au aq -at +aq bz aq ak @@ -783,7 +860,7 @@ ao ao ao bl -bl +PM ao ak ak @@ -797,17 +874,17 @@ tb "} (14,1,1) = {" Wg -af -al +Wg +Wg aq aq aq aE -at +aH at aq -ak -ak +aq +vr aC ak ao @@ -815,7 +892,7 @@ ak ao ao bl -bl +PM ao ao aI @@ -829,25 +906,25 @@ tb "} (15,1,1) = {" Wg -ae -af -aq -av +ah +Wg +Wg +hr at at -aH +at +aM aM aO -ap -ak -ak +vN +vN ak ak ak ak ao bl -bl +PM ao ao ak @@ -862,24 +939,24 @@ Qa (16,1,1) = {" Wg ag -ah +og as -aw -aA -aA +Zx +at +at at aM +aM aP -ak -ak +vN aQ -ak +vN ak az ak ak bl -bl +PM ak ao ao @@ -893,25 +970,25 @@ Qa "} (17,1,1) = {" Wg -ah -af -aq +wN +Wg +Wg ax aw aF at at aq -ak -aI -ak -ak +aq +Gi ak ak ak ak ak ak +VE +BM ak ao ao @@ -926,10 +1003,10 @@ tb (18,1,1) = {" Wg Wg -am +Wg aq av -at +aG aG at bz @@ -959,11 +1036,11 @@ tb Wg ab aj -aq -av -bx -aH +ay at +at +aH +bx aN aq ak @@ -993,8 +1070,8 @@ ai an aq aq -aq -aq +av +av aq aq aq @@ -1024,10 +1101,10 @@ Wg Wg aK Wg -ay -ak -ak -ak +aq +aq +aq +aq ak ak ak @@ -1057,7 +1134,7 @@ Wg Wg Wg ak -ak +aL aI ak ap diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 10a587a4d53..83038631320 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -690,6 +690,8 @@ #define COMSIG_CARBON_GAIN_TRAUMA "carbon_gain_trauma" ///Called when a carbon loses a brain trauma (source = carbon, trauma = what trauma was removed) #define COMSIG_CARBON_LOSE_TRAUMA "carbon_lose_trauma" +///Called when a carbon updates their health (source = carbon) +#define COMSIG_CARBON_HEALTH_UPDATE "carbon_health_update" // simple_animal signals /// called when a simplemob is given sentience from a potion (target = person who sentienced) @@ -1246,6 +1248,8 @@ #define COMSIG_MOB_ITEM_ATTACK_QDELETED "mob_item_attack_qdeleted" ///from base of mob/RangedAttack(): (atom/A, modifiers) #define COMSIG_MOB_ATTACK_RANGED "mob_attack_ranged" +///from base of mob/ranged_secondary_attack(): (atom/target, modifiers) +#define COMSIG_MOB_ATTACK_RANGED_SECONDARY "mob_attack_ranged_secondary" ///From base of atom/ctrl_click(): (atom/A) #define COMSIG_MOB_CTRL_CLICKED "mob_ctrl_clicked" ///From base of mob/update_movespeed():area diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 54eb27eb2c1..d67a85726a7 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -281,10 +281,27 @@ //ED209's ignore monkeys #define JUDGE_IGNOREMONKEYS (1<<4) -#define MEGAFAUNA_DEFAULT_RECOVERY_TIME 5 - #define SHADOW_SPECIES_LIGHT_THRESHOLD 0.2 +#define COOLDOWN_UPDATE_SET_MELEE "set_melee" +#define COOLDOWN_UPDATE_ADD_MELEE "add_melee" +#define COOLDOWN_UPDATE_SET_RANGED "set_ranged" +#define COOLDOWN_UPDATE_ADD_RANGED "add_ranged" +#define COOLDOWN_UPDATE_SET_ENRAGE "set_enrage" +#define COOLDOWN_UPDATE_ADD_ENRAGE "add_enrage" +#define COOLDOWN_UPDATE_SET_SPAWN "set_spawn" +#define COOLDOWN_UPDATE_ADD_SPAWN "add_spawn" +#define COOLDOWN_UPDATE_SET_HELP "set_help" +#define COOLDOWN_UPDATE_ADD_HELP "add_help" +#define COOLDOWN_UPDATE_SET_DASH "set_dash" +#define COOLDOWN_UPDATE_ADD_DASH "add_dash" +#define COOLDOWN_UPDATE_SET_TRANSFORM "set_transform" +#define COOLDOWN_UPDATE_ADD_TRANSFORM "add_transform" +#define COOLDOWN_UPDATE_SET_CHASER "set_chaser" +#define COOLDOWN_UPDATE_ADD_CHASER "add_chaser" +#define COOLDOWN_UPDATE_SET_ARENA "set_arena" +#define COOLDOWN_UPDATE_ADD_ARENA "add_arena" + // Offsets defines #define OFFSET_UNIFORM "uniform" diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index c4152a4e130..d018dd29e60 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -117,6 +117,9 @@ //Deals with covering the target in ants. #define STATUS_EFFECT_ANTS /datum/status_effect/ants +/// Doubles attack cooldowns on simplemobs and recovery time on megafauna. +#define STATUS_EFFECT_STAGGER /datum/status_effect/stagger + ///////////// // NEUTRAL // ///////////// diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index dcc8bb44303..9c6b1e5cfe5 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -304,6 +304,8 @@ * Useful for mobs that have their abilities mapped to right click. */ /mob/proc/ranged_secondary_attack(atom/target, modifiers) + if(SEND_SIGNAL(src, COMSIG_MOB_ATTACK_RANGED_SECONDARY, target, modifiers) & COMPONENT_CANCEL_ATTACK_CHAIN) + return TRUE /** * Middle click diff --git a/code/datums/ai/telegraph_effects.dm b/code/datums/ai/telegraph_effects.dm index 4c44cf1d1fd..20693f4b205 100644 --- a/code/datums/ai/telegraph_effects.dm +++ b/code/datums/ai/telegraph_effects.dm @@ -7,3 +7,8 @@ /obj/effect/temp_visual/telegraphing/vending_machine_tilt duration = 1 SECONDS + +/obj/effect/temp_visual/telegraphing/thunderbolt + icon = 'icons/mob/telegraphing/telegraph.dmi' + icon_state = "target_circle" + duration = 2 SECONDS diff --git a/code/datums/components/butchering.dm b/code/datums/components/butchering.dm index 3a474837f5b..147a99ea274 100644 --- a/code/datums/components/butchering.dm +++ b/code/datums/components/butchering.dm @@ -11,8 +11,10 @@ var/butchering_enabled = TRUE /// Whether or not this component is compatible with blunt tools. var/can_be_blunt = FALSE + /// Callback for butchering + var/datum/callback/butcher_callback -/datum/component/butchering/Initialize(_speed, _effectiveness, _bonus_modifier, _butcher_sound, disabled, _can_be_blunt) +/datum/component/butchering/Initialize(_speed, _effectiveness, _bonus_modifier, _butcher_sound, disabled, _can_be_blunt, _butcher_callback) if(_speed) speed = _speed if(_effectiveness) @@ -25,6 +27,8 @@ butchering_enabled = FALSE if(_can_be_blunt) can_be_blunt = _can_be_blunt + if(_butcher_callback) + butcher_callback = _butcher_callback if(isitem(parent)) RegisterSignal(parent, COMSIG_ITEM_ATTACK, .proc/onItemAttack) @@ -126,13 +130,10 @@ if(butcher) butcher.visible_message(span_notice("[butcher] butchers [meat]."), \ span_notice("You butcher [meat].")) - ButcherEffects(meat) + butcher_callback?.Invoke(butcher, meat) meat.harvest(butcher) meat.gib(FALSE, FALSE, TRUE) -/datum/component/butchering/proc/ButcherEffects(mob/living/meat) //extra effects called on butchering, override this via subtypes - return - ///Special snowflake component only used for the recycler. /datum/component/butchering/recycler diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index 5c70a4b0c47..50bf5fa16be 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -1067,3 +1067,26 @@ desc = "You are a Ghoul! A eldritch monster reanimated to serve its master." icon_state = "mind_control" + +/datum/status_effect/stagger + id = "stagger" + status_type = STATUS_EFFECT_REFRESH + duration = 30 SECONDS + tick_interval = 1 SECONDS + alert_type = null + +/datum/status_effect/stagger/on_apply() + owner.next_move_modifier *= 1.5 + if(ishostile(owner)) + var/mob/living/simple_animal/hostile/simple_owner = owner + simple_owner.ranged_cooldown_time *= 2.5 + return TRUE + +/datum/status_effect/stagger/on_remove() + . = ..() + if(QDELETED(owner)) + return + owner.next_move_modifier /= 1.5 + if(ishostile(owner)) + var/mob/living/simple_animal/hostile/simple_owner = owner + simple_owner.ranged_cooldown_time /= 2.5 diff --git a/code/datums/voice_of_god_command.dm b/code/datums/voice_of_god_command.dm index b7eb87e0bf9..26173a56383 100644 --- a/code/datums/voice_of_god_command.dm +++ b/code/datums/voice_of_god_command.dm @@ -120,14 +120,23 @@ GLOBAL_LIST_INIT(voice_of_god_commands, init_voice_of_god_commands()) return /// This command knocks the listeners down. -/datum/voice_of_god_command/paralyze - trigger = "drop|fall|trip|knockdown|stop|wait|stand\\s*still|hold\\s*on|halt" +/datum/voice_of_god_command/knockdown + trigger = "drop|fall|trip|knockdown" cooldown = COOLDOWN_STUN -/datum/voice_of_god_command/paralyze/execute(list/listeners, mob/living/user, power_multiplier = 1, message) +/datum/voice_of_god_command/knockdown/execute(list/listeners, mob/living/user, power_multiplier = 1, message) for(var/mob/living/target as anything in listeners) target.Knockdown(4 SECONDS * power_multiplier) +/// This command stops the listeners from moving. +/datum/voice_of_god_command/immobilize + trigger = "stop|wait|stand\\s*still|hold\\s*on|halt" + cooldown = COOLDOWN_STUN + +/datum/voice_of_god_command/immobilize/execute(list/listeners, mob/living/user, power_multiplier = 1, message) + for(var/mob/living/target as anything in listeners) + target.Immobilize(4 SECONDS * power_multiplier) + /// This command makes carbon listeners throw up like Mr. Creosote. /datum/voice_of_god_command/vomit trigger = "vomit|throw\\s*up|sick" diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm index 9691ff3112e..bf4bfee9e4c 100644 --- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm +++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm @@ -520,3 +520,12 @@ /obj/effect/constructing_effect/proc/end() qdel(src) + +/obj/effect/temp_visual/electricity + icon_state = "electricity3" + duration = 0.5 SECONDS + +/obj/effect/temp_visual/thunderbolt + icon_state = "thunderbolt" + icon = 'icons/effects/32x96.dmi' + duration = 0.6 SECONDS diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm index aa5e700fea0..8bb3d02e02c 100644 --- a/code/game/objects/items/weaponry.dm +++ b/code/game/objects/items/weaponry.dm @@ -270,19 +270,8 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 user.visible_message(span_suicide("[user] is slitting [user.p_their()] stomach open with [src]! It looks like [user.p_theyre()] trying to commit seppuku!")) return(BRUTELOSS) -/obj/item/katana/cursed +/obj/item/katana/cursed //used by wizard events, see the tendril_loot.dm file for the miner one slot_flags = null - item_flags = DROPDEL - -/obj/item/katana/cursed/equipped(mob/living/carbon/human/user) - . = ..() - if(!istype(user)) - return - user.gain_trauma(/datum/brain_trauma/magic/stalker, TRAUMA_RESILIENCE_MAGIC) - -/obj/item/katana/cursed/Initialize() - . = ..() - ADD_TRAIT(src, TRAIT_NODROP, CURSED_ITEM_TRAIT(type)) /obj/item/wirerod name = "wired rod" diff --git a/code/modules/mining/lavaland/ruins/gym.dm b/code/game/objects/structures/gym.dm similarity index 100% rename from code/modules/mining/lavaland/ruins/gym.dm rename to code/game/objects/structures/gym.dm diff --git a/code/game/turfs/open/floor/fancy_floor.dm b/code/game/turfs/open/floor/fancy_floor.dm index 48747062ee0..e562f037bbf 100644 --- a/code/game/turfs/open/floor/fancy_floor.dm +++ b/code/game/turfs/open/floor/fancy_floor.dm @@ -185,6 +185,11 @@ slowdown = 1.5 planetary_atmos = FALSE +/turf/open/floor/grass/snow/actually_safe + slowdown = 0 + planetary_atmos = FALSE + initial_gas_mix = OPENTURF_DEFAULT_ATMOS + /turf/open/floor/grass/fakebasalt //Heart is not a real planeteer power name = "aesthetic volcanic flooring" diff --git a/code/game/turfs/open/lava.dm b/code/game/turfs/open/lava.dm index be0859d6944..5ab11d40bbb 100644 --- a/code/game/turfs/open/lava.dm +++ b/code/game/turfs/open/lava.dm @@ -16,6 +16,12 @@ barefootstep = FOOTSTEP_LAVA clawfootstep = FOOTSTEP_LAVA heavyfootstep = FOOTSTEP_LAVA + /// How much fire damage we deal to living mobs stepping on us + var/lava_damage = 20 + /// How many firestacks we add to living mobs stepping on us + var/lava_firestacks = 20 + /// How much temperature we expose objects with + var/temperature_damage = 10000 /turf/open/lava/ex_act(severity, target) contents_explosion(severity, target) @@ -139,7 +145,7 @@ O.resistance_flags &= ~FIRE_PROOF if(O.armor.fire > 50) //obj with 100% fire armor still get slowly burned away. O.armor = O.armor.setRating(fire = 50) - O.fire_act(10000, 1000 * delta_time) + O.fire_act(temperature_damage, 1000 * delta_time) if(istype(O, /obj/structure/closet)) var/obj/structure/closet/C = O for(var/I in C.contents) @@ -173,9 +179,9 @@ ADD_TRAIT(L, TRAIT_PERMANENTLY_ONFIRE,TURF_TRAIT) L.update_fire() - L.adjustFireLoss(20 * delta_time) + L.adjustFireLoss(lava_damage * delta_time) if(L) //mobs turning into object corpses could get deleted here. - L.adjust_fire_stacks(20 * delta_time) + L.adjust_fire_stacks(lava_firestacks * delta_time) L.IgniteMob() /turf/open/lava/smooth diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm index 4a2af34c68a..309cffc353b 100644 --- a/code/modules/antagonists/wizard/equipment/soulstone.dm +++ b/code/modules/antagonists/wizard/equipment/soulstone.dm @@ -9,7 +9,8 @@ desc = "A fragment of the legendary treasure known simply as the 'Soul Stone'. The shard still flickers with a fraction of the full artefact's power." w_class = WEIGHT_CLASS_TINY slot_flags = ITEM_SLOT_BELT - var/old_shard = FALSE + var/one_use = FALSE + var/grab_sleeping = TRUE var/spent = FALSE /// This controls the color of the soulstone as well as restrictions for who can use it. THEME_CULT is red and is the default of cultist THEME_WIZARD is purple and is the default of wizard and THEME_HOLY is for purified soul stone var/theme = THEME_CULT @@ -20,7 +21,7 @@ return required_role ? (who.mind && who.mind.has_antag_datum(required_role, TRUE)) : TRUE /obj/item/soulstone/proc/was_used() - if(old_shard) + if(one_use) spent = TRUE name = "dull [name]" desc = "A fragment of the legendary treasure known simply as \ @@ -36,7 +37,8 @@ required_role = /datum/antagonist/wizard /obj/item/soulstone/anybody/revolver - old_shard = TRUE + one_use = TRUE + grab_sleeping = FALSE /obj/item/soulstone/anybody/purified icon_state = "purified_soulstone" @@ -44,7 +46,11 @@ /obj/item/soulstone/anybody/chaplain name = "mysterious old shard" - old_shard = TRUE + one_use = TRUE + grab_sleeping = FALSE + +/obj/item/soulstone/anybody/mining + grab_sleeping = FALSE /obj/item/soulstone/pickup(mob/living/user) ..() @@ -54,7 +60,7 @@ /obj/item/soulstone/examine(mob/user) . = ..() if(role_check(user) || isobserver(user)) - if (old_shard) + if(!grab_sleeping) . += span_cult("A soulstone, used to capture a soul, either from dead humans or from freed shades.") else . += span_cult("A soulstone, used to capture souls, either from unconscious or sleeping humans or from freed shades.") @@ -223,7 +229,7 @@ if(contents.len) to_chat(user, "[span_userdanger("Capture failed!")]: [src] is full! Free an existing soul to make room.") else - if((!old_shard && T.stat != CONSCIOUS) || (old_shard && T.stat == DEAD)) + if((grab_sleeping && T.stat != CONSCIOUS) || (!grab_sleeping && T.stat == DEAD)) if(T.client == null) to_chat(user, "[span_userdanger("Capture failed!")]: The soul has already fled its mortal frame. You attempt to bring it back...") getCultGhost(T,user) @@ -398,4 +404,3 @@ init_shade(T, user , shade_controller = chosen_ghost) qdel(T) return TRUE - diff --git a/code/modules/cargo/exports/lavaland.dm b/code/modules/cargo/exports/lavaland.dm index 8039fb8d5e7..83b1b73d82e 100644 --- a/code/modules/cargo/exports/lavaland.dm +++ b/code/modules/cargo/exports/lavaland.dm @@ -1,4 +1,4 @@ -//Tendril chest artifacts and ruin loot. Includes ash drake loot since they drop two sets of armor + random item +//Tendril chest artifacts and ruin loot. //Consumable or one-use items like the magic D20 and gluttony's blessing are omitted /datum/export/lavaland/minor @@ -6,36 +6,37 @@ unit_name = "minor lava planet artifact" export_types = list(/obj/item/immortality_talisman, /obj/item/book_of_babel, - /obj/item/gun/magic/hook, /obj/item/wisp_lantern, - /obj/item/reagent_containers/glass/bottle/potion/flight, /obj/item/katana/cursed, /obj/item/clothing/glasses/godeye, /obj/item/melee/ghost_sword, - /obj/item/clothing/suit/space/hardsuit/cult, - /obj/item/grenade/clusterbuster/inferno, /obj/item/clothing/neck/necklace/memento_mori, /obj/item/organ/heart/cursed/wizard, /obj/item/clothing/suit/hooded/cloak/drake, - /obj/item/dragons_blood, - /obj/item/lava_staff, /obj/item/ship_in_a_bottle, /obj/item/clothing/shoes/clown_shoes/banana_shoes, /obj/item/gun/magic/staff/honk, /obj/item/kitchen/knife/envy, /obj/item/gun/ballistic/revolver/russian/soul, - /obj/item/veilrender/vealrender) + /obj/item/veilrender/vealrender, + /obj/item/clothing/suit/space/hardsuit/berserker, + /obj/item/freeze_cube, + /obj/item/soulstone/anybody/mining, + /obj/item/clothing/gloves/gauntlets, + /obj/item/jacobs_ladder, + ) -/datum/export/lavaland/major //valuable chest/ruin loot and staff of storms +/datum/export/lavaland/major //valuable chest/ruin loot, minor megafauna loot cost = CARGO_CRATE_VALUE * 40 unit_name = "lava planet artifact" - export_types = list(/obj/item/guardiancreator, + export_types = list(/obj/item/guardiancreator/miner, /obj/item/rod_of_asclepius, - /obj/item/clothing/suit/space/hardsuit/ert/paranormal, + /obj/item/dragons_blood, + /obj/item/lava_staff, /obj/item/prisoncube, - /obj/item/staff/storm) + ) -//Megafauna loot, except for ash drakes and legion +//Megafauna loot, except for ash drakes /datum/export/lavaland/megafauna cost = CARGO_CRATE_VALUE * 80 @@ -45,19 +46,12 @@ /obj/item/organ/vocal_cords/colossus, /obj/machinery/anomalous_crystal, /obj/item/mayhem, - /obj/item/gun/magic/staff/spellblade) + /obj/item/gun/magic/staff/spellblade, + /obj/item/storm_staff, + /obj/item/clothing/suit/space/hardsuit/hostile_environment, + ) /datum/export/lavaland/megafauna/total_printout(datum/export_report/ex, notes = TRUE) //in the unlikely case a miner feels like selling megafauna loot . = ..() if(. && notes) . += " On behalf of the Nanotrasen RnD division: Thank you for your hard work." - -/datum/export/lavaland/megafauna/hev/suit - cost = CARGO_CRATE_VALUE * 60 - unit_name = "H.E.C.K. suit" - export_types = list(/obj/item/clothing/suit/space/hostile_environment) - -/datum/export/lavaland/megafauna/hev/helmet - cost = CARGO_CRATE_VALUE * 20 - unit_name = "H.E.C.K. helmet" - export_types = list(/obj/item/clothing/head/helmet/space/hostile_environment) diff --git a/code/modules/clothing/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm index 7eb1cf52cad..e9f6f864ec3 100644 --- a/code/modules/clothing/glasses/_glasses.dm +++ b/code/modules/clothing/glasses/_glasses.dm @@ -506,72 +506,6 @@ worn_icon_state = "psych_glasses" glass_colour_type = /datum/client_colour/glass_colour/red -/obj/item/clothing/glasses/godeye - name = "eye of god" - desc = "A strange eye, said to have been torn from an omniscient creature that used to roam the wastes." - icon_state = "godeye" - inhand_icon_state = "godeye" - vision_flags = SEE_TURFS|SEE_MOBS|SEE_OBJS - darkness_view = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - resistance_flags = LAVA_PROOF | FIRE_PROOF - custom_materials = null - clothing_flags = SCAN_REAGENTS - var/double = FALSE - -/obj/item/clothing/glasses/godeye/equipped(mob/user, slot) - . = ..() - if(ishuman(user) && slot == ITEM_SLOT_EYES) - ADD_TRAIT(src, TRAIT_NODROP, EYE_OF_GOD_TRAIT) - - pain(user) - -/obj/item/clothing/glasses/godeye/dropped(mob/user) - . = ..() - // Behead someone, their "glasses" drop on the floor - // and thus, the god eye should no longer be sticky - REMOVE_TRAIT(src, TRAIT_NODROP, EYE_OF_GOD_TRAIT) - -/obj/item/clothing/glasses/godeye/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/clothing/glasses/godeye) && W != src && W.loc == user) - if(!double) - var/obj/item/clothing/glasses/godeye/double/T = /obj/item/clothing/glasses/godeye/double - - icon_state = initial(T.icon_state) - inhand_icon_state = initial(T.inhand_icon_state) - - if(iscarbon(user)) - var/mob/living/carbon/C = user - C.update_inv_glasses() - - // Apply pain now, so message is still for solo eye bore - if(C.glasses == src) - pain(C) - - name = initial(T.name) - desc = initial(T.desc) - double = TRUE - else - to_chat(user, span_notice("[W] winks at you and vanishes into the abyss, you feel really unlucky.")) - qdel(W) - ..() - -/obj/item/clothing/glasses/godeye/proc/pain(mob/living/victim) - // If pain/pain immunity ever implemented, check for it here. - - to_chat(victim, span_userdanger("You experience blinding pain, as [src] [double ? "burrow" : "burrows"] into your skull.")) - victim.emote("scream") - victim.flash_act() - -/obj/item/clothing/glasses/godeye/double - name = "eyes of god" - desc = "A pair of strange eyes, said to have been torn from an omniscient creature that used to roam the wastes. There's no real reason to have two, but that isn't stopping you." - icon_state = "doublegodeye" - inhand_icon_state = "doublegodeye" - - double = TRUE - - /obj/item/clothing/glasses/debug name = "debug glasses" desc = "Medical, security and diagnostic hud. Alt click to toggle xray." diff --git a/code/modules/clothing/outfits/ert.dm b/code/modules/clothing/outfits/ert.dm index 3f99f89ffbc..16118b9ed6a 100644 --- a/code/modules/clothing/outfits/ert.dm +++ b/code/modules/clothing/outfits/ert.dm @@ -335,7 +335,7 @@ /obj/item/storage/box/survival/engineer = 1, ) belt = /obj/item/storage/belt/champion - glasses = /obj/item/clothing/glasses/godeye + glasses = /obj/item/clothing/glasses/trickblindfold mask = /obj/item/clothing/mask/gas/clown_hat shoes = /obj/item/clothing/shoes/clown_shoes/combat l_pocket = /obj/item/food/grown/banana diff --git a/code/modules/clothing/spacesuits/berserker.dm b/code/modules/clothing/spacesuits/berserker.dm deleted file mode 100644 index 2a3704c098c..00000000000 --- a/code/modules/clothing/spacesuits/berserker.dm +++ /dev/null @@ -1,107 +0,0 @@ -/obj/item/clothing/suit/space/hardsuit/berserker - name = "berserker hardsuit" - desc = "Voices echo from the hardsuit, driving the user insane." - icon_state = "hardsuit-berserker" - inhand_icon_state = "hardsuit-berserker" - slowdown = 0 - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/berserker - armor = list(MELEE = 30, BULLET = 10, LASER = 10, ENERGY = 20, BOMB = 50, BIO = 100, RAD = 10, FIRE = 100, ACID = 100) - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF - allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/pickaxe, /obj/item/spear, /obj/item/organ/regenerative_core/legion, /obj/item/kitchen/knife, /obj/item/kinetic_crusher, /obj/item/resonator, /obj/item/melee/transforming/cleaving_saw) - - -/obj/item/clothing/suit/space/hardsuit/berserker/Initialize() - . = ..() - AddComponent(/datum/component/anti_magic, TRUE, TRUE, TRUE, ITEM_SLOT_OCLOTHING) - -/obj/item/clothing/suit/space/hardsuit/berserker/RemoveHelmet() - var/obj/item/clothing/head/helmet/space/hardsuit/berserker/helm = helmet - if(helm?.berserk_active) - return - return ..() - -#define MAX_BERSERK_CHARGE 100 -#define PROJECTILE_HIT_MULTIPLIER 1.5 -#define DAMAGE_TO_CHARGE_SCALE 0.25 -#define CHARGE_DRAINED_PER_SECOND 5 -#define BERSERK_MELEE_ARMOR_ADDED 50 -#define BERSERK_ATTACK_SPEED_MODIFIER 0.25 - -/obj/item/clothing/head/helmet/space/hardsuit/berserker - name = "berserker helmet" - desc = "Peering into the eyes of the helmet is enough to seal damnation." - icon_state = "hardsuit0-berserker" - inhand_icon_state = "hardsuit0-berserker" - hardsuit_type = "berserker" - armor = list(MELEE = 30, BULLET = 10, LASER = 10, ENERGY = 20, BOMB = 50, BIO = 100, RAD = 10, FIRE = 100, ACID = 100) - actions_types = list(/datum/action/item_action/berserk_mode) - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF - /// Current charge of berserk, goes from 0 to 100 - var/berserk_charge = 0 - /// Status of berserk - var/berserk_active = FALSE - -/obj/item/clothing/head/helmet/space/hardsuit/berserker/Initialize() - . = ..() - ADD_TRAIT(src, TRAIT_NODROP, LOCKED_HELMET_TRAIT) - -/obj/item/clothing/head/helmet/space/hardsuit/berserker/examine() - . = ..() - . += span_notice("Berserk mode is [berserk_charge]% charged.") - -/obj/item/clothing/head/helmet/space/hardsuit/berserker/process(delta_time) - . = ..() - if(berserk_active) - berserk_charge = clamp(berserk_charge - CHARGE_DRAINED_PER_SECOND * delta_time, 0, MAX_BERSERK_CHARGE) - if(!berserk_charge) - if(ishuman(loc)) - end_berserk(loc) - -/obj/item/clothing/head/helmet/space/hardsuit/berserker/dropped(mob/user) - . = ..() - end_berserk(user) - -/obj/item/clothing/head/helmet/space/hardsuit/berserker/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(berserk_active) - return - var/berserk_value = damage * DAMAGE_TO_CHARGE_SCALE - if(attack_type == PROJECTILE_ATTACK) - berserk_value *= PROJECTILE_HIT_MULTIPLIER - berserk_charge = clamp(round(berserk_charge + berserk_value), 0, MAX_BERSERK_CHARGE) - if(berserk_charge >= MAX_BERSERK_CHARGE) - to_chat(owner, span_notice("Berserk mode is fully charged.")) - -/// Starts berserk, giving the wearer 50 melee armor, doubled attacking speed, NOGUNS trait, adding a color and giving them the berserk movespeed modifier -/obj/item/clothing/head/helmet/space/hardsuit/berserker/proc/berserk_mode(mob/living/carbon/human/user) - to_chat(user, span_warning("You enter berserk mode.")) - playsound(user, 'sound/magic/staff_healing.ogg', 50) - user.add_movespeed_modifier(/datum/movespeed_modifier/berserk) - user.physiology.armor.melee += BERSERK_MELEE_ARMOR_ADDED - user.next_move_modifier *= BERSERK_ATTACK_SPEED_MODIFIER - user.add_atom_colour(COLOR_BUBBLEGUM_RED, TEMPORARY_COLOUR_PRIORITY) - ADD_TRAIT(user, TRAIT_NOGUNS, BERSERK_TRAIT) - ADD_TRAIT(src, TRAIT_NODROP, BERSERK_TRAIT) - berserk_active = TRUE - -/// Ends berserk, reverting the changes from the proc [berserk_mode] -/obj/item/clothing/head/helmet/space/hardsuit/berserker/proc/end_berserk(mob/living/carbon/human/user) - if(!berserk_active) - return - to_chat(user, span_warning("You exit berserk mode.")) - playsound(user, 'sound/magic/summonitems_generic.ogg', 50) - user.remove_movespeed_modifier(/datum/movespeed_modifier/berserk) - user.physiology.armor.melee -= BERSERK_MELEE_ARMOR_ADDED - user.next_move_modifier /= BERSERK_ATTACK_SPEED_MODIFIER - user.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, COLOR_BUBBLEGUM_RED) - REMOVE_TRAIT(user, TRAIT_NOGUNS, BERSERK_TRAIT) - REMOVE_TRAIT(src, TRAIT_NODROP, BERSERK_TRAIT) - berserk_active = FALSE - -#undef MAX_BERSERK_CHARGE -#undef PROJECTILE_HIT_MULTIPLIER -#undef DAMAGE_TO_CHARGE_SCALE -#undef CHARGE_DRAINED_PER_SECOND -#undef BERSERK_MELEE_ARMOR_ADDED -#undef BERSERK_ATTACK_SPEED_MODIFIER diff --git a/code/modules/clothing/suits/cloaks.dm b/code/modules/clothing/suits/cloaks.dm index 116dad6122e..11a425db64e 100644 --- a/code/modules/clothing/suits/cloaks.dm +++ b/code/modules/clothing/suits/cloaks.dm @@ -72,7 +72,7 @@ icon_state = "dragon" desc = "A suit of armour fashioned from the remains of an ash drake." allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe, /obj/item/spear) - armor = list(MELEE = 70, BULLET = 30, LASER = 50, ENERGY = 50, BOMB = 70, BIO = 60, RAD = 50, FIRE = 100, ACID = 100) + armor = list(MELEE = 65, BULLET = 15, LASER = 40, ENERGY = 40, BOMB = 70, BIO = 60, RAD = 50, FIRE = 100, ACID = 100) hoodtype = /obj/item/clothing/head/hooded/cloakhood/drake body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS cold_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS @@ -86,7 +86,7 @@ name = "drake helm" icon_state = "dragon" desc = "The skull of a dragon." - armor = list(MELEE = 70, BULLET = 30, LASER = 50, ENERGY = 50, BOMB = 70, BIO = 60, RAD = 50, FIRE = 100, ACID = 100) + armor = list(MELEE = 65, BULLET = 15, LASER = 40, ENERGY = 40, BOMB = 70, BIO = 60, RAD = 50, FIRE = 100, ACID = 100) clothing_flags = SNUG_FIT cold_protection = HEAD min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT @@ -99,7 +99,7 @@ icon_state = "godslayer" desc = "A suit of armour fashioned from the remnants of a knight's armor, and parts of a wendigo." allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe, /obj/item/spear) - armor = list("melee" = 50, "bullet" = 25, "laser" = 25, "energy" = 25, "bomb" = 50, "bio" = 50, "rad" = 100, "fire" = 100, "acid" = 100) + armor = list(MELEE = 50, BULLET = 25, LASER = 25, ENERGY = 25, BOMB = 50, BIO = 50, RAD = 100, FIRE = 100, ACID = 100) clothing_flags = STOPSPRESSUREDAMAGE | THICKMATERIAL hoodtype = /obj/item/clothing/head/hooded/cloakhood/godslayer cold_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS @@ -121,7 +121,7 @@ name = "godslayer helm" icon_state = "godslayer" desc = "The horns and skull of a wendigo, held together by the remaining icey energy of a demonic miner." - armor = list("melee" = 50, "bullet" = 25, "laser" = 25, "energy" = 25, "bomb" = 50, "bio" = 50, "rad" = 100, "fire" = 100, "acid" = 100) + armor = list(MELEE = 50, BULLET = 25, LASER = 25, ENERGY = 25, BOMB = 50, BIO = 50, RAD = 100, FIRE = 100, ACID = 100) clothing_flags = STOPSPRESSUREDAMAGE | THICKMATERIAL | SNUG_FIT cold_protection = HEAD min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT diff --git a/code/modules/mining/equipment/explorer_gear.dm b/code/modules/mining/equipment/explorer_gear.dm index 0622dae4c18..4c84424960c 100644 --- a/code/modules/mining/equipment/explorer_gear.dm +++ b/code/modules/mining/equipment/explorer_gear.dm @@ -56,53 +56,3 @@ /obj/item/clothing/mask/gas/explorer/folded/Initialize() . = ..() adjustmask() - -/obj/item/clothing/suit/space/hostile_environment - name = "H.E.C.K. suit" - desc = "Hostile Environment Cross-Kinetic Suit: A suit designed to withstand the wide variety of hazards from Lavaland. It wasn't enough for its last owner." - icon_state = "hostile_env" - inhand_icon_state = "hostile_env" - clothing_flags = THICKMATERIAL //not spaceproof - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF | LAVA_PROOF - slowdown = 0 - armor = list(MELEE = 70, BULLET = 40, LASER = 10, ENERGY = 20, BOMB = 50, BIO = 100, RAD = 100, FIRE = 100, ACID = 100) - allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe) - -/obj/item/clothing/suit/space/hostile_environment/process(delta_time) - . = ..() - var/mob/living/carbon/C = loc - if(istype(C) && DT_PROB(1, delta_time)) //cursed by bubblegum - if(DT_PROB(7.5, delta_time)) - new /datum/hallucination/oh_yeah(C) - to_chat(C, span_colossus("[pick("I AM IMMORTAL.","I SHALL TAKE BACK WHAT'S MINE.","I SEE YOU.","YOU CANNOT ESCAPE ME FOREVER.","DEATH CANNOT HOLD ME.")]")) - else - to_chat(C, span_warning("[pick("You hear faint whispers.","You smell ash.","You feel hot.","You hear a roar in the distance.")]")) - -/obj/item/clothing/head/helmet/space/hostile_environment - name = "H.E.C.K. helmet" - desc = "Hostile Environiment Cross-Kinetic Helmet: A helmet designed to withstand the wide variety of hazards from Lavaland. It wasn't enough for its last owner." - icon_state = "hostile_env" - inhand_icon_state = "hostile_env" - w_class = WEIGHT_CLASS_NORMAL - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - clothing_flags = THICKMATERIAL // no space protection - armor = list(MELEE = 70, BULLET = 40, LASER = 10, ENERGY = 20, BOMB = 50, BIO = 100, RAD = 100, FIRE = 100, ACID = 100) - resistance_flags = FIRE_PROOF | LAVA_PROOF - -/obj/item/clothing/head/helmet/space/hostile_environment/Initialize() - . = ..() - update_appearance() - -/obj/item/clothing/head/helmet/space/hostile_environment/update_overlays() - . = ..() - var/mutable_appearance/glass_overlay = mutable_appearance(icon, "hostile_env_glass") - glass_overlay.appearance_flags = RESET_COLOR - . += glass_overlay - -/obj/item/clothing/head/helmet/space/hostile_environment/worn_overlays(mutable_appearance/standing, isinhands) - . = ..() - if(!isinhands) - var/mutable_appearance/M = mutable_appearance('icons/mob/clothing/head.dmi', "hostile_env_glass") - M.appearance_flags = RESET_COLOR - . += M diff --git a/code/modules/mining/equipment/vendor_items.dm b/code/modules/mining/equipment/vendor_items.dm deleted file mode 100644 index 6ed7f962d14..00000000000 --- a/code/modules/mining/equipment/vendor_items.dm +++ /dev/null @@ -1,14 +0,0 @@ -/**********************Mining Equipment Vendor Items**************************/ -//misc stuff you can buy from the vendor that has special code but doesn't really need its own file - -/**********************Facehugger toy**********************/ -/obj/item/clothing/mask/facehugger/toy - inhand_icon_state = "facehugger_inactive" - desc = "A toy often used to play pranks on other miners by putting it in their beds. It takes a bit to recharge after latching onto something." - throwforce = 0 - real = 0 - sterile = 1 - tint = 3 //Makes it feel more authentic when it latches on - -/obj/item/clothing/mask/facehugger/toy/Die() - return diff --git a/code/modules/mining/lavaland/megafauna_loot.dm b/code/modules/mining/lavaland/megafauna_loot.dm new file mode 100644 index 00000000000..6295765235a --- /dev/null +++ b/code/modules/mining/lavaland/megafauna_loot.dm @@ -0,0 +1,1011 @@ +//This file contains all Lavaland megafauna loot. Does not include crusher trophies. + + +//Hierophant: Hierophant Club + +#define HIEROPHANT_BLINK_RANGE 5 +#define HIEROPHANT_BLINK_COOLDOWN 15 SECONDS + +/datum/action/innate/dash/hierophant + current_charges = 1 + max_charges = 1 + charge_rate = HIEROPHANT_BLINK_COOLDOWN + recharge_sound = null + phasein = /obj/effect/temp_visual/hierophant/blast/visual + phaseout = /obj/effect/temp_visual/hierophant/blast/visual + // It's a simple purple beam, works well enough for the purple hiero effects. + beam_effect = "plasmabeam" + +/datum/action/innate/dash/hierophant/Teleport(mob/user, atom/target) + var/dist = get_dist(user, target) + if(dist > HIEROPHANT_BLINK_RANGE) + to_chat(user, span_hierophant_warning("Blink destination out of range.")) + return + var/turf/target_turf = get_turf(target) + if(target_turf.is_blocked_turf_ignore_climbable()) + to_chat(user, span_hierophant_warning("Blink destination blocked.")) + return + . = ..() + if(!current_charges) + var/obj/item/hierophant_club/club = src.target + if(istype(club)) + club.blink_charged = FALSE + club.update_appearance() + +/datum/action/innate/dash/hierophant/charge() + var/obj/item/hierophant_club/club = target + if(istype(club)) + club.blink_charged = TRUE + club.update_appearance() + + current_charges = clamp(current_charges + 1, 0, max_charges) + owner.update_action_buttons_icon() + + if(recharge_sound) + playsound(dashing_item, recharge_sound, 50, TRUE) + to_chat(owner, span_notice("[src] now has [current_charges]/[max_charges] charges.")) + +/obj/item/hierophant_club + name = "hierophant club" + desc = "The strange technology of this large club allows various nigh-magical teleportation feats. It used to beat you, but now you can set the beat." + icon_state = "hierophant_club_ready_beacon" + inhand_icon_state = "hierophant_club_ready_beacon" + icon = 'icons/obj/lavaland/artefacts.dmi' + lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi' + righthand_file = 'icons/mob/inhands/64x64_righthand.dmi' + inhand_x_dimension = 64 + inhand_y_dimension = 64 + slot_flags = ITEM_SLOT_BACK + w_class = WEIGHT_CLASS_NORMAL + force = 15 + attack_verb_continuous = list("clubs", "beats", "pummels") + attack_verb_simple = list("club", "beat", "pummel") + hitsound = 'sound/weapons/sonic_jackhammer.ogg' + resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF + actions_types = list(/datum/action/item_action/vortex_recall) + /// Linked teleport beacon for the group teleport functionality. + var/obj/effect/hierophant/beacon + /// TRUE if currently doing a teleport to the beacon, FALSE otherwise. + var/teleporting = FALSE //if we ARE teleporting + /// Action enabling the blink-dash functionality. + var/datum/action/innate/dash/hierophant/blink + /// Whether the blink ability is activated. IF TRUE, left clicking a location will blink to it. If FALSE, this is disabled. + var/blink_activated = TRUE + /// Whether the blink is charged. Set and unset by the blink action. Used as part of setting the appropriate icon states. + var/blink_charged = TRUE + +/obj/item/hierophant_club/Initialize() + . = ..() + blink = new(src) + +/obj/item/hierophant_club/Destroy() + . = ..() + QDEL_NULL(blink) + +/obj/item/hierophant_club/ComponentInitialize() + . = ..() + AddElement(/datum/element/update_icon_updates_onmob) + +/obj/item/hierophant_club/examine(mob/user) + . = ..() + . += span_hierophant_warning("The[beacon ? " beacon is not currently":"re is a beacon"] attached.") + +/obj/item/hierophant_club/suicide_act(mob/living/user) + say("Xverwpsgexmrk...", forced = "hierophant club suicide") + user.visible_message(span_suicide("[user] holds [src] into the air! It looks like [user.p_theyre()] trying to commit suicide!")) + new/obj/effect/temp_visual/hierophant/telegraph(get_turf(user)) + playsound(user,'sound/machines/airlockopen.ogg', 75, TRUE) + user.visible_message(span_hierophant_warning("[user] fades out, leaving [user.p_their()] belongings behind!")) + for(var/obj/item/I in user) + if(I != src) + user.dropItemToGround(I) + for(var/turf/T in RANGE_TURFS(1, user)) + new /obj/effect/temp_visual/hierophant/blast/visual(T, user, TRUE) + user.dropItemToGround(src) //Drop us last, so it goes on top of their stuff + qdel(user) + +/obj/item/hierophant_club/attack_self(mob/user) + blink_activated = !blink_activated + to_chat(user, span_notice("You [blink_activated ? "enable" : "disable"] the blink function on [src].")) + +/obj/item/hierophant_club/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + . = ..() + // If our target is the beacon and the hierostaff is next to the beacon, we're trying to pick it up. + if((target == beacon) && target.Adjacent(src)) + return + if(blink_activated) + blink.Teleport(user, target) + +/obj/item/hierophant_club/update_icon_state() + icon_state = inhand_icon_state = "hierophant_club[blink_charged ? "_ready":""][(!QDELETED(beacon)) ? "":"_beacon"]" + return ..() + +/obj/item/hierophant_club/ui_action_click(mob/user, action) + if(!user.is_holding(src)) //you need to hold the staff to teleport + to_chat(user, span_warning("You need to hold the club in your hands to [beacon ? "teleport with it":"detach the beacon"]!")) + return + if(!beacon || QDELETED(beacon)) + if(isturf(user.loc)) + user.visible_message(span_hierophant_warning("[user] starts fiddling with [src]'s pommel..."), \ + span_notice("You start detaching the hierophant beacon...")) + if(do_after(user, 50, target = user) && !beacon) + var/turf/T = get_turf(user) + playsound(T,'sound/magic/blind.ogg', 200, TRUE, -4) + new /obj/effect/temp_visual/hierophant/telegraph/teleport(T, user) + beacon = new/obj/effect/hierophant(T) + user.update_action_buttons_icon() + user.visible_message(span_hierophant_warning("[user] places a strange machine beneath [user.p_their()] feet!"), \ + "[span_hierophant("You detach the hierophant beacon, allowing you to teleport yourself and any allies to it at any time!")]\n\ + [span_notice("You can remove the beacon to place it again by striking it with the club.")]") + else + to_chat(user, span_warning("You need to be on solid ground to detach the beacon!")) + return + if(get_dist(user, beacon) <= 2) //beacon too close abort + to_chat(user, span_warning("You are too close to the beacon to teleport to it!")) + return + var/turf/beacon_turf = get_turf(beacon) + if(beacon_turf?.is_blocked_turf(TRUE)) + to_chat(user, span_warning("The beacon is blocked by something, preventing teleportation!")) + return + if(!isturf(user.loc)) + to_chat(user, span_warning("You don't have enough space to teleport from here!")) + return + teleporting = TRUE //start channel + user.update_action_buttons_icon() + user.visible_message(span_hierophant_warning("[user] starts to glow faintly...")) + beacon.icon_state = "hierophant_tele_on" + var/obj/effect/temp_visual/hierophant/telegraph/edge/TE1 = new /obj/effect/temp_visual/hierophant/telegraph/edge(user.loc) + var/obj/effect/temp_visual/hierophant/telegraph/edge/TE2 = new /obj/effect/temp_visual/hierophant/telegraph/edge(beacon.loc) + if(do_after(user, 40, target = user) && user && beacon) + var/turf/T = get_turf(beacon) + var/turf/source = get_turf(user) + if(T.is_blocked_turf(TRUE)) + teleporting = FALSE + to_chat(user, span_warning("The beacon is blocked by something, preventing teleportation!")) + user.update_action_buttons_icon() + beacon.icon_state = "hierophant_tele_off" + return + new /obj/effect/temp_visual/hierophant/telegraph(T, user) + new /obj/effect/temp_visual/hierophant/telegraph(source, user) + playsound(T,'sound/magic/wand_teleport.ogg', 200, TRUE) + playsound(source,'sound/machines/airlockopen.ogg', 200, TRUE) + if(!do_after(user, 3, target = user) || !user || !beacon || QDELETED(beacon)) //no walking away shitlord + teleporting = FALSE + if(user) + user.update_action_buttons_icon() + if(beacon) + beacon.icon_state = "hierophant_tele_off" + return + if(T.is_blocked_turf(TRUE)) + teleporting = FALSE + to_chat(user, span_warning("The beacon is blocked by something, preventing teleportation!")) + user.update_action_buttons_icon() + beacon.icon_state = "hierophant_tele_off" + return + user.log_message("teleported self from [AREACOORD(source)] to [beacon]", LOG_GAME) + new /obj/effect/temp_visual/hierophant/telegraph/teleport(T, user) + new /obj/effect/temp_visual/hierophant/telegraph/teleport(source, user) + for(var/t in RANGE_TURFS(1, T)) + new /obj/effect/temp_visual/hierophant/blast/visual(t, user, TRUE) + for(var/t in RANGE_TURFS(1, source)) + new /obj/effect/temp_visual/hierophant/blast/visual(t, user, TRUE) + for(var/mob/living/L in range(1, source)) + INVOKE_ASYNC(src, .proc/teleport_mob, source, L, T, user) + sleep(6) //at this point the blasts detonate + if(beacon) + beacon.icon_state = "hierophant_tele_off" + else + qdel(TE1) + qdel(TE2) + if(beacon) + beacon.icon_state = "hierophant_tele_off" + teleporting = FALSE + if(user) + user.update_action_buttons_icon() + +/obj/item/hierophant_club/proc/teleport_mob(turf/source, mob/teleporting, turf/target, mob/user) + var/turf/turf_to_teleport_to = get_step(target, get_dir(source, teleporting)) //get position relative to caster + if(!turf_to_teleport_to || turf_to_teleport_to.is_blocked_turf(TRUE)) + return + animate(teleporting, alpha = 0, time = 2, easing = EASE_OUT) //fade out + sleep(1) + if(!teleporting) + return + teleporting.visible_message(span_hierophant_warning("[teleporting] fades out!")) + sleep(2) + if(!teleporting) + return + var/success = do_teleport(teleporting, turf_to_teleport_to, no_effects = TRUE, channel = TELEPORT_CHANNEL_MAGIC) + sleep(1) + if(!teleporting) + return + animate(teleporting, alpha = 255, time = 2, easing = EASE_IN) //fade IN + sleep(1) + if(!teleporting) + return + teleporting.visible_message(span_hierophant_warning("[teleporting] fades in!")) + if(user != teleporting && success) + log_combat(user, teleporting, "teleported", null, "from [AREACOORD(source)]") + +/obj/item/hierophant_club/pickup(mob/living/user) + . = ..() + blink.Grant(user, src) + user.update_icons() + +/obj/item/hierophant_club/dropped(mob/user) + . = ..() + blink.Remove(user) + user.update_icons() + +#undef HIEROPHANT_BLINK_RANGE +#undef HIEROPHANT_BLINK_COOLDOWN + +//Bubblegum: Mayhem in a Bottle, H.E.C.K. Suit, Soulscythe + +/obj/item/mayhem + name = "mayhem in a bottle" + desc = "A magically infused bottle of blood, the scent of which will drive anyone nearby into a murderous frenzy." + icon = 'icons/obj/wizard.dmi' + icon_state = "vial" + +/obj/item/mayhem/attack_self(mob/user) + for(var/mob/living/carbon/human/target in range(7,user)) + target.apply_status_effect(STATUS_EFFECT_MAYHEM) + to_chat(user, span_notice("You shatter the bottle!")) + playsound(user.loc, 'sound/effects/glassbr1.ogg', 100, TRUE) + message_admins(span_adminnotice("[ADMIN_LOOKUPFLW(user)] has activated a bottle of mayhem!")) + user.log_message("activated a bottle of mayhem", LOG_ATTACK) + qdel(src) + +/obj/item/clothing/suit/space/hardsuit/hostile_environment + name = "H.E.C.K. suit" + desc = "Hostile Environment Cross-Kinetic Suit: A suit designed to withstand the wide variety of hazards from Lavaland. It wasn't enough for its last owner." + icon_state = "hostile_env" + inhand_icon_state = "hostile_env" + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF | LAVA_PROOF | ACID_PROOF + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/hostile_environment + slowdown = 0 + armor = list(MELEE = 70, BULLET = 40, LASER = 10, ENERGY = 20, BOMB = 50, BIO = 100, RAD = 100, FIRE = 100, ACID = 100) + allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe) + +/obj/item/clothing/suit/space/hardsuit/hostile_environment/process(delta_time) + . = ..() + var/mob/living/carbon/wearer = loc + if(istype(wearer) && DT_PROB(1, delta_time)) //cursed by bubblegum + if(DT_PROB(7.5, delta_time)) + new /datum/hallucination/oh_yeah(wearer) + to_chat(wearer, span_colossus("[pick("I AM IMMORTAL.","I SHALL TAKE BACK WHAT'S MINE.","I SEE YOU.","YOU CANNOT ESCAPE ME FOREVER.","DEATH CANNOT HOLD ME.")]")) + else + to_chat(wearer, span_warning("[pick("You hear faint whispers.","You smell ash.","You feel hot.","You hear a roar in the distance.")]")) + +/obj/item/clothing/head/helmet/space/hardsuit/hostile_environment + name = "H.E.C.K. helmet" + desc = "Hostile Environiment Cross-Kinetic Helmet: A helmet designed to withstand the wide variety of hazards from Lavaland. It wasn't enough for its last owner." + icon_state = "hardsuit0-heck" + inhand_icon_state = "hostile_env" + hardsuit_type = "heck" + w_class = WEIGHT_CLASS_NORMAL + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + armor = list(MELEE = 70, BULLET = 40, LASER = 10, ENERGY = 20, BOMB = 50, BIO = 100, RAD = 100, FIRE = 100, ACID = 100) + resistance_flags = FIRE_PROOF | LAVA_PROOF | ACID_PROOF + actions_types = list() + +/obj/item/clothing/head/helmet/space/hardsuit/hostile_environment/Initialize() + . = ..() + update_appearance() + AddComponent(/datum/component/butchering, 5, 150, null, null, null, TRUE, CALLBACK(src, .proc/consume)) + +/obj/item/clothing/head/helmet/space/hardsuit/hostile_environment/equipped(mob/user, slot, initial = FALSE) + . = ..() + RegisterSignal(user, COMSIG_HUMAN_EARLY_UNARMED_ATTACK, .proc/butcher_target) + var/datum/component/butchering/butchering = GetComponent(/datum/component/butchering) + butchering.butchering_enabled = TRUE + to_chat(user, span_notice("You feel a bloodlust. You can now butcher corpses with your bare arms.")) + +/obj/item/clothing/head/helmet/space/hardsuit/hostile_environment/dropped(mob/user, silent = FALSE) + . = ..() + UnregisterSignal(user, COMSIG_HUMAN_EARLY_UNARMED_ATTACK) + var/datum/component/butchering/butchering = GetComponent(/datum/component/butchering) + butchering.butchering_enabled = FALSE + to_chat(user, span_notice("You lose your bloodlust.")) + +/obj/item/clothing/head/helmet/space/hardsuit/hostile_environment/proc/butcher_target(mob/user, atom/target, proximity) + SIGNAL_HANDLER + if(!isliving(target)) + return + return SEND_SIGNAL(src, COMSIG_ITEM_ATTACK, target, user) + +/obj/item/clothing/head/helmet/space/hardsuit/hostile_environment/proc/consume(mob/living/user, mob/living/butchered) + if(butchered.mob_biotypes & (MOB_ROBOTIC | MOB_SPIRIT)) + return + var/health_consumed = butchered.maxHealth * 0.1 + user.heal_ordered_damage(health_consumed, list(BRUTE, BURN, TOX)) + to_chat(user, span_notice("You heal from the corpse of [butchered].")) + var/datum/client_colour/color = user.add_client_colour(/datum/client_colour/bloodlust) + QDEL_IN(color, 1 SECONDS) + +/obj/item/clothing/head/helmet/space/hardsuit/hostile_environment/update_overlays() + . = ..() + var/mutable_appearance/glass_overlay = mutable_appearance(icon, "hostile_env_glass") + glass_overlay.appearance_flags = RESET_COLOR + . += glass_overlay + +/obj/item/clothing/head/helmet/space/hardsuit/hostile_environment/worn_overlays(mutable_appearance/standing, isinhands) + . = ..() + if(!isinhands) + var/mutable_appearance/glass_overlay = mutable_appearance('icons/mob/clothing/head.dmi', "hostile_env_glass") + glass_overlay.appearance_flags = RESET_COLOR + . += glass_overlay + +#define MAX_BLOOD_LEVEL 100 + +/obj/item/soulscythe + name = "soulscythe" + desc = "An old relic of hell created by devils to establish themselves as the leadership of hell over the demons. It grows stronger while it possesses a powerful soul." + icon = 'icons/obj/lavaland/artefacts.dmi' + icon_state = "soulscythe" + lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi' + righthand_file = 'icons/mob/inhands/64x64_righthand.dmi' + attack_verb_continuous = list("chops", "slices", "cuts", "reaps") + attack_verb_simple = list("chop", "slice", "cut", "reap") + hitsound = 'sound/weapons/bladeslice.ogg' + inhand_x_dimension = 64 + inhand_y_dimension = 64 + force = 20 + throwforce = 17 + armour_penetration = 50 + sharpness = SHARP_EDGED + bare_wound_bonus = 10 + layer = MOB_LAYER + /// Soulscythe mob in the scythe + var/mob/living/simple_animal/soulscythe/soul + /// Are we grabbing a spirit? + var/using = FALSE + /// Currently charging? + var/charging = FALSE + /// Cooldown between moves + COOLDOWN_DECLARE(move_cooldown) + /// Cooldown between attacks + COOLDOWN_DECLARE(attack_cooldown) + +/obj/item/soulscythe/Initialize() + . = ..() + soul = new(src) + RegisterSignal(soul, COMSIG_LIVING_RESIST, .proc/on_resist) + RegisterSignal(soul, COMSIG_MOB_ATTACK_RANGED, .proc/on_attack) + RegisterSignal(soul, COMSIG_MOB_ATTACK_RANGED_SECONDARY, .proc/on_secondary_attack) + RegisterSignal(src, COMSIG_OBJ_INTEGRITY_CHANGED, .proc/on_integrity_change) + +/obj/item/soulscythe/examine(mob/user) + . = ..() + . += soul.ckey ? span_nicegreen("There is a soul inhabiting it.") : span_danger("It's dormant.") + +/obj/item/soulscythe/attack(mob/living/attacked, mob/living/user, params) + . = ..() + if(attacked.stat != DEAD) + give_blood(10) + +/obj/item/soulscythe/attack_hand(mob/user, list/modifiers) + if(soul.ckey && !soul.faction_check_mob(user)) + to_chat(user, span_warning("You can't pick up [src]!")) + return + return ..() + +/obj/item/soulscythe/pickup(mob/user) + . = ..() + if(soul.ckey) + animate(src) //stop spinnage + +/obj/item/soulscythe/dropped(mob/user, silent) + . = ..() + if(soul.ckey) + reset_spin() //resume spinnage + +/obj/item/soulscythe/attack_self(mob/user, modifiers) + if(using || soul.ckey || soul.stat) + return + if(!(GLOB.ghost_role_flags & GHOSTROLE_STATION_SENTIENCE)) + balloon_alert(user, "you can't awaken the scythe!") + return + using = TRUE + balloon_alert(user, "you hold the scythe up...") + ADD_TRAIT(src, TRAIT_NODROP, type) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [user.real_name]'s soulscythe?", ROLE_PAI, FALSE, 100, POLL_IGNORE_POSSESSED_BLADE) + if(LAZYLEN(candidates)) + var/mob/dead/observer/picked_ghost = pick(candidates) + soul.ckey = picked_ghost.ckey + soul.copy_languages(user, LANGUAGE_MASTER) //Make sure the sword can understand and communicate with the user. + soul.update_atom_languages() + soul.faction = list("[REF(user)]") + balloon_alert(user, "the scythe glows up") + add_overlay("soulscythe_gem") + density = TRUE + if(!ismob(loc)) + reset_spin() + else + balloon_alert(user, "the scythe is dormant!") + REMOVE_TRAIT(src, TRAIT_NODROP, type) + using = FALSE + +/obj/item/soulscythe/relaymove(mob/living/user, direction) + if(!COOLDOWN_FINISHED(src, move_cooldown) || charging) + return + if(!isturf(loc)) + balloon_alert(user, "resist out!") + COOLDOWN_START(src, move_cooldown, 1 SECONDS) + return + if(!use_blood(1, FALSE)) + return + if(pixel_x != base_pixel_x || pixel_y != base_pixel_y) + animate(src, 0.2 SECONDS, pixel_x = base_pixel_y, pixel_y = base_pixel_y, flags = ANIMATION_PARALLEL) + Move(get_step(src, direction), direction) + COOLDOWN_START(src, move_cooldown, (direction in GLOB.cardinals) ? 0.1 SECONDS : 0.2 SECONDS) + +/obj/item/soulscythe/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) + . = ..() + if(!charging) + return + charging = FALSE + throwforce *= 0.5 + reset_spin() + if(ismineralturf(hit_atom)) + var/turf/closed/mineral/hit_rock = hit_atom + hit_rock.gets_drilled() + if(isliving(hit_atom)) + var/mob/living/hit_mob = hit_atom + if(hit_mob.stat != DEAD) + give_blood(15) + +/obj/item/soulscythe/AllowClick() + return TRUE + +/obj/item/soulscythe/proc/use_blood(amount = 0, message = TRUE) + if(amount > soul.blood_level) + if(message) + to_chat(soul, span_warning("Not enough blood!")) + return FALSE + soul.blood_level -= amount + return TRUE + +/obj/item/soulscythe/proc/give_blood(amount) + soul.blood_level = min(MAX_BLOOD_LEVEL, soul.blood_level + amount) + +/obj/item/soulscythe/proc/on_resist(mob/living/user) + SIGNAL_HANDLER + + if(isturf(loc)) + return + INVOKE_ASYNC(src, .proc/break_out) + +/obj/item/soulscythe/proc/break_out() + if(!use_blood(10)) + return + balloon_alert(soul, "you resist...") + if(!do_after(soul, 5 SECONDS, target = src, timed_action_flags = IGNORE_TARGET_LOC_CHANGE)) + balloon_alert(soul, "interrupted!") + return + balloon_alert(soul, "you break out") + if(ismob(loc)) + var/mob/holder = loc + holder.temporarilyRemoveItemFromInventory(src) + forceMove(drop_location()) + +/obj/item/soulscythe/proc/on_integrity_change(datum/source, old_value, new_value) + SIGNAL_HANDLER + + soul.set_health(new_value) + +/obj/item/soulscythe/proc/on_attack(mob/living/source, atom/attacked_atom, modifiers) + SIGNAL_HANDLER + + if(!COOLDOWN_FINISHED(src, attack_cooldown) || !isturf(loc)) + return + if(get_dist(source, attacked_atom) > 1) + INVOKE_ASYNC(src, .proc/shoot_target, attacked_atom) + else + INVOKE_ASYNC(src, .proc/slash_target, attacked_atom) + +/obj/item/soulscythe/proc/on_secondary_attack(mob/living/source, atom/attacked_atom, modifiers) + SIGNAL_HANDLER + + if(!COOLDOWN_FINISHED(src, attack_cooldown) || !isturf(loc)) + return + INVOKE_ASYNC(src, .proc/charge_target, attacked_atom) + +/obj/item/soulscythe/proc/shoot_target(atom/attacked_atom) + if(!use_blood(15)) + return + COOLDOWN_START(src, attack_cooldown, 3 SECONDS) + var/obj/projectile/projectile = new /obj/projectile/soulscythe(get_turf(src)) + projectile.preparePixelProjectile(attacked_atom, src) + projectile.firer = src + projectile.fire(null, attacked_atom) + visible_message(span_danger("[src] fires at [attacked_atom]!"), span_notice("You fire at [attacked_atom]!")) + playsound(src, 'sound/magic/fireball.ogg', 50, TRUE) + +/obj/item/soulscythe/proc/slash_target(atom/attacked_atom) + if(isliving(attacked_atom) && use_blood(10)) + var/mob/living/attacked_mob = attacked_atom + if(attacked_mob.stat != DEAD) + give_blood(15) + attacked_mob.apply_damage(damage = force * (ishostile(attacked_mob) ? 2 : 1), sharpness = SHARP_EDGED, bare_wound_bonus = 5) + to_chat(attacked_mob, span_userdanger("You're slashed by [src]!")) + else if((ismachinery(attacked_atom) || isstructure(attacked_atom)) && use_blood(5)) + var/obj/attacked_obj = attacked_atom + attacked_obj.take_damage(force, BRUTE, MELEE, FALSE) + else + return + COOLDOWN_START(src, attack_cooldown, 1 SECONDS) + animate(src) + SpinAnimation(5) + addtimer(CALLBACK(src, .proc/reset_spin), 1 SECONDS) + visible_message(span_danger("[src] slashes [attacked_atom]!"), span_notice("You slash [attacked_atom]!")) + playsound(src, 'sound/weapons/bladeslice.ogg', 50, TRUE) + do_attack_animation(attacked_atom, ATTACK_EFFECT_SLASH) + +/obj/item/soulscythe/proc/charge_target(atom/attacked_atom) + if(charging || !use_blood(30)) + return + COOLDOWN_START(src, attack_cooldown, 5 SECONDS) + animate(src) + charging = TRUE + visible_message(span_danger("[src] starts charging...")) + balloon_alert(soul, "you start charging...") + if(!do_after(soul, 2 SECONDS, target = src, timed_action_flags = IGNORE_TARGET_LOC_CHANGE)) + balloon_alert(soul, "interrupted!") + return + visible_message(span_danger("[src] charges at [attacked_atom]!"), span_notice("You charge at [attacked_atom]!")) + new /obj/effect/temp_visual/mook_dust(get_turf(src)) + playsound(src, 'sound/weapons/thudswoosh.ogg', 50, TRUE) + SpinAnimation(1) + throwforce *= 2 + throw_at(attacked_atom, 10, 3, soul, FALSE) + +/obj/item/soulscythe/proc/reset_spin() + animate(src) + SpinAnimation(15) + +/mob/living/simple_animal/soulscythe + name = "mysterious spirit" + maxHealth = 200 + health = 200 + gender = NEUTER + mob_biotypes = MOB_SPIRIT + faction = list() + /// Blood level, used for movement and abilities in a soulscythe + var/blood_level = MAX_BLOOD_LEVEL + +/mob/living/simple_animal/soulscythe/get_status_tab_items() + . = ..() + . += "Blood: [blood_level]/[MAX_BLOOD_LEVEL]" + +/mob/living/simple_animal/soulscythe/Life(delta_time, times_fired) + . = ..() + if(!stat) + blood_level = min(MAX_BLOOD_LEVEL, blood_level + round(1 * delta_time)) + +/obj/projectile/soulscythe + name = "soulslash" + icon_state = "soulslash" + flag = MELEE //jokair + damage = 15 + light_range = 1 + light_power = 1 + light_color = LIGHT_COLOR_BLOOD_MAGIC + +/obj/projectile/soulscythe/on_hit(atom/target, blocked = FALSE) + if(ishostile(target)) + damage *= 2 + return ..() + +#undef MAX_BLOOD_LEVEL + +//Ash Drake: Spectral Blade, Lava Staff, Dragon's Blood + +/obj/item/melee/ghost_sword + name = "\improper spectral blade" + desc = "A rusted and dulled blade. It doesn't look like it'd do much damage. It glows weakly." + icon_state = "spectral" + inhand_icon_state = "spectral" + lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' + flags_1 = CONDUCT_1 + sharpness = SHARP_EDGED + w_class = WEIGHT_CLASS_BULKY + force = 1 + throwforce = 1 + hitsound = 'sound/effects/ghost2.ogg' + attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "rends") + attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "rend") + resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF + var/summon_cooldown = 0 + var/list/mob/dead/observer/spirits + +/obj/item/melee/ghost_sword/Initialize() + . = ..() + spirits = list() + START_PROCESSING(SSobj, src) + AddElement(/datum/element/point_of_interest) + AddComponent(/datum/component/butchering, 150, 90) + +/obj/item/melee/ghost_sword/Destroy() + for(var/mob/dead/observer/G in spirits) + G.invisibility = GLOB.observer_default_invisibility + spirits.Cut() + STOP_PROCESSING(SSobj, src) + . = ..() + +/obj/item/melee/ghost_sword/attack_self(mob/user) + if(summon_cooldown > world.time) + to_chat(user, span_warning("You just recently called out for aid. You don't want to annoy the spirits!")) + return + to_chat(user, span_notice("You call out for aid, attempting to summon spirits to your side.")) + + notify_ghosts("[user] is raising [user.p_their()] [name], calling for your help!", + enter_link="(Click to help)", + source = user, ignore_key = POLL_IGNORE_SPECTRAL_BLADE, header = "Spectral blade") + + summon_cooldown = world.time + 600 + +/obj/item/melee/ghost_sword/Topic(href, href_list) + if(href_list["orbit"]) + var/mob/dead/observer/ghost = usr + if(istype(ghost)) + ghost.ManualFollow(src) + +/obj/item/melee/ghost_sword/process() + ghost_check() + +/obj/item/melee/ghost_sword/proc/ghost_check() + var/ghost_counter = 0 + var/turf/T = get_turf(src) + var/list/contents = T.GetAllContents() + var/mob/dead/observer/current_spirits = list() + for(var/thing in contents) + var/atom/A = thing + A.transfer_observers_to(src) + for(var/i in orbiters?.orbiter_list) + if(!isobserver(i)) + continue + var/mob/dead/observer/G = i + ghost_counter++ + G.invisibility = 0 + current_spirits |= G + for(var/mob/dead/observer/G in spirits - current_spirits) + G.invisibility = GLOB.observer_default_invisibility + spirits = current_spirits + return ghost_counter + +/obj/item/melee/ghost_sword/attack(mob/living/target, mob/living/carbon/human/user) + force = 0 + var/ghost_counter = ghost_check() + force = clamp((ghost_counter * 4), 0, 75) + user.visible_message(span_danger("[user] strikes with the force of [ghost_counter] vengeful spirits!")) + ..() + +/obj/item/melee/ghost_sword/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + var/ghost_counter = ghost_check() + final_block_chance += clamp((ghost_counter * 5), 0, 75) + owner.visible_message(span_danger("[owner] is protected by a ring of [ghost_counter] ghosts!")) + return ..() + +/obj/item/dragons_blood + name = "bottle of dragons blood" + desc = "You're not actually going to drink this, are you?" + icon = 'icons/obj/wizard.dmi' + icon_state = "vial" + +/obj/item/dragons_blood/attack_self(mob/living/carbon/human/user) + if(!istype(user)) + return + + var/mob/living/carbon/human/consumer = user + var/random = rand(1,4) + + switch(random) + if(1) + to_chat(user, span_danger("Your appearance morphs to that of a very small humanoid ash dragon! You get to look like a freak without the cool abilities.")) + consumer.dna.features = list("mcolor" = "A02720", "tail_lizard" = "Dark Tiger", "tail_human" = "None", "snout" = "Sharp", "horns" = "Curled", "ears" = "None", "wings" = "None", "frills" = "None", "spines" = "Long", "body_markings" = "Dark Tiger Body", "legs" = "Digitigrade Legs") + consumer.eye_color = "fee5a3" + consumer.set_species(/datum/species/lizard) + if(2) + to_chat(user, span_danger("Your flesh begins to melt! Miraculously, you seem fine otherwise.")) + consumer.set_species(/datum/species/skeleton) + if(3) + to_chat(user, span_danger("Power courses through you! You can now shift your form at will.")) + if(user.mind) + var/obj/effect/proc_holder/spell/targeted/shapeshift/dragon/dragon_shapeshift = new + user.mind.AddSpell(dragon_shapeshift) + if(4) + to_chat(user, span_danger("You feel like you could walk straight through lava now.")) + LAZYOR(consumer.weather_immunities, WEATHER_LAVA) + + playsound(user,'sound/items/drink.ogg', 30, TRUE) + qdel(src) + +/obj/item/lava_staff + name = "staff of lava" + desc = "The ability to fill the emergency shuttle with lava. What more could you want out of life?" + icon_state = "lavastaff" + lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi' + icon = 'icons/obj/guns/magic.dmi' + slot_flags = ITEM_SLOT_BACK + w_class = WEIGHT_CLASS_NORMAL + force = 18 + damtype = BURN + resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF + attack_verb_continuous = list("sears", "clubs", "burn") + attack_verb_simple = list("sear", "club", "burn") + hitsound = 'sound/weapons/sear.ogg' + var/turf_type = /turf/open/lava/smooth/weak + var/transform_string = "lava" + var/reset_turf_type = /turf/open/floor/plating/asteroid/basalt + var/reset_string = "basalt" + var/create_cooldown = 10 SECONDS + var/create_delay = 3 SECONDS + var/reset_cooldown = 5 SECONDS + var/timer = 0 + var/static/list/banned_turfs = typecacheof(list(/turf/open/space/transit, /turf/closed)) + +/obj/item/lava_staff/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + . = ..() + if(timer > world.time) + return + if(is_type_in_typecache(target, banned_turfs)) + return + if(target in view(user.client.view, get_turf(user))) + var/turf/open/T = get_turf(target) + if(!istype(T)) + return + if(!istype(T, turf_type)) + var/obj/effect/temp_visual/lavastaff/L = new /obj/effect/temp_visual/lavastaff(T) + L.alpha = 0 + animate(L, alpha = 255, time = create_delay) + user.visible_message(span_danger("[user] points [src] at [T]!")) + timer = world.time + create_delay + 1 + if(do_after(user, create_delay, target = T)) + var/old_name = T.name + if(T.TerraformTurf(turf_type, flags = CHANGETURF_INHERIT_AIR)) + user.visible_message(span_danger("[user] turns \the [old_name] into [transform_string]!")) + message_admins("[ADMIN_LOOKUPFLW(user)] fired the lava staff at [ADMIN_VERBOSEJMP(T)]") + log_game("[key_name(user)] fired the lava staff at [AREACOORD(T)].") + timer = world.time + create_cooldown + playsound(T,'sound/magic/fireball.ogg', 200, TRUE) + else + timer = world.time + qdel(L) + else + var/old_name = T.name + if(T.TerraformTurf(reset_turf_type, flags = CHANGETURF_INHERIT_AIR)) + user.visible_message(span_danger("[user] turns \the [old_name] into [reset_string]!")) + timer = world.time + reset_cooldown + playsound(T,'sound/magic/fireball.ogg', 200, TRUE) + +/obj/effect/temp_visual/lavastaff + icon_state = "lavastaff_warn" + duration = 50 + +/turf/open/lava/smooth/weak + lava_damage = 10 + lava_firestacks = 10 + temperature_damage = 2500 + +//Blood-Drunk Miner: Cleaving Saw + +/obj/item/melee/transforming/cleaving_saw + name = "cleaving saw" + desc = "This saw, effective at drawing the blood of beasts, transforms into a long cleaver that makes use of centrifugal force." + force = 12 + force_on = 20 //force when active + throwforce = 20 + throwforce_on = 20 + icon = 'icons/obj/lavaland/artefacts.dmi' + lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi' + righthand_file = 'icons/mob/inhands/64x64_righthand.dmi' + inhand_x_dimension = 64 + inhand_y_dimension = 64 + icon_state = "cleaving_saw" + icon_state_on = "cleaving_saw_open" + worn_icon_state = "cleaving_saw" + slot_flags = ITEM_SLOT_BELT + attack_verb_off = list("attacks", "saws", "slices", "tears", "lacerates", "rips", "dices", "cuts") + attack_verb_on = list("cleaves", "swipes", "slashes", "chops") + hitsound = 'sound/weapons/bladeslice.ogg' + hitsound_on = 'sound/weapons/bladeslice.ogg' + w_class = WEIGHT_CLASS_BULKY + sharpness = SHARP_EDGED + faction_bonus_force = 30 + nemesis_factions = list("mining", "boss") + resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF + var/transform_cooldown + var/swiping = FALSE + var/bleed_stacks_per_hit = 3 + +/obj/item/melee/transforming/cleaving_saw/examine(mob/user) + . = ..() + . += "It is [active ? "open, will cleave enemies in a wide arc and deal additional damage to fauna":"closed, and can be used for rapid consecutive attacks that cause fauna to bleed"].\n"+\ + "Both modes will build up existing bleed effects, doing a burst of high damage if the bleed is built up high enough.\n"+\ + "Transforming it immediately after an attack causes the next attack to come out faster." + +/obj/item/melee/transforming/cleaving_saw/suicide_act(mob/user) + user.visible_message(span_suicide("[user] is [active ? "closing [src] on [user.p_their()] neck" : "opening [src] into [user.p_their()] chest"]! It looks like [user.p_theyre()] trying to commit suicide!")) + transform_cooldown = 0 + transform_weapon(user, TRUE) + return BRUTELOSS + +/obj/item/melee/transforming/cleaving_saw/transform_weapon(mob/living/user, supress_message_text) + if(transform_cooldown > world.time) + return FALSE + . = ..() + if(.) + transform_cooldown = world.time + (CLICK_CD_MELEE * 0.5) + user.changeNext_move(CLICK_CD_MELEE * 0.25) + +/obj/item/melee/transforming/cleaving_saw/transform_messages(mob/living/user, supress_message_text) + if(!supress_message_text) + if(active) + to_chat(user, span_notice("You open [src]. It will now cleave enemies in a wide arc and deal additional damage to fauna.")) + else + to_chat(user, span_notice("You close [src]. It will now attack rapidly and cause fauna to bleed.")) + playsound(user, 'sound/magic/clockwork/fellowship_armory.ogg', 35, TRUE, frequency = 90000 - (active * 30000)) + +/obj/item/melee/transforming/cleaving_saw/clumsy_transform_effect(mob/living/user) + if(HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50)) + to_chat(user, span_warning("You accidentally cut yourself with [src], like a doofus!")) + user.take_bodypart_damage(10) + +/obj/item/melee/transforming/cleaving_saw/melee_attack_chain(mob/user, atom/target, params) + ..() + if(!active) + user.changeNext_move(CLICK_CD_MELEE * 0.5) //when closed, it attacks very rapidly + +/obj/item/melee/transforming/cleaving_saw/nemesis_effects(mob/living/user, mob/living/target) + if(istype(target, /mob/living/simple_animal/hostile/asteroid/elite)) + return + var/datum/status_effect/stacking/saw_bleed/B = target.has_status_effect(STATUS_EFFECT_SAWBLEED) + if(!B) + target.apply_status_effect(STATUS_EFFECT_SAWBLEED,bleed_stacks_per_hit) + else + B.add_stacks(bleed_stacks_per_hit) + +/obj/item/melee/transforming/cleaving_saw/attack(mob/living/target, mob/living/carbon/human/user) + if(!active || swiping || !target.density || get_turf(target) == get_turf(user)) + if(!active) + faction_bonus_force = 0 + ..() + if(!active) + faction_bonus_force = initial(faction_bonus_force) + else + var/turf/user_turf = get_turf(user) + var/dir_to_target = get_dir(user_turf, get_turf(target)) + swiping = TRUE + var/static/list/cleaving_saw_cleave_angles = list(0, -45, 45) //so that the animation animates towards the target clicked and not towards a side target + for(var/i in cleaving_saw_cleave_angles) + var/turf/turf = get_step(user_turf, turn(dir_to_target, i)) + for(var/mob/living/living_target in turf) + if(user.Adjacent(living_target) && living_target.body_position != LYING_DOWN) + melee_attack_chain(user, living_target) + swiping = FALSE + +//Legion: Staff of Storms + +/obj/item/storm_staff + name = "staff of storms" + desc = "An ancient staff retrieved from the remains of Legion. The wind stirs as you move it." + icon_state = "staffofstorms" + icon = 'icons/obj/guns/magic.dmi' + lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi' + slot_flags = ITEM_SLOT_BACK + w_class = WEIGHT_CLASS_BULKY + force = 20 + damtype = BURN + hitsound = 'sound/weapons/taserhit.ogg' + wound_bonus = -30 + bare_wound_bonus = 20 + resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF + var/max_thunder_charges = 3 + var/thunder_charges = 3 + var/thunder_charge_time = 15 SECONDS + var/static/list/excluded_areas = list(/area/space) + var/list/targeted_turfs = list() + +/obj/item/storm_staff/examine(mob/user) + . = ..() + . += span_notice("It has [thunder_charges] charges remaining.") + . += span_notice("Use it in hand to dispel storms.") + . += span_notice("Use it on targets to summon thunderbolts from the sky.") + . += span_notice("The thunderbolts are boosted if in an area with weather effects.") + +/obj/item/storm_staff/attack_self(mob/user) + var/area/user_area = get_area(user) + var/turf/user_turf = get_turf(user) + if(!user_area || !user_turf || (is_type_in_list(user_area, excluded_areas))) + to_chat(user, span_warning("Something is preventing you from using the staff here.")) + return + var/datum/weather/affected_weather + for(var/datum/weather/weather as anything in SSweather.processing) + if((user_turf.z in weather.impacted_z_levels) && ispath(user_area.type, weather.area_type)) + affected_weather = weather + break + if(!affected_weather) + return + if(affected_weather.stage == END_STAGE) + balloon_alert(user, "already ended!") + return + if(affected_weather.stage == WIND_DOWN_STAGE) + balloon_alert(user, "already ending!") + return + balloon_alert(user, "you hold the staff up...") + if(!do_after(user, 3 SECONDS, target = src)) + balloon_alert(user, "interrupted!") + return + user.visible_message(span_warning("[user] holds [src] skywards as an orange beam travels into the sky!"), \ + span_notice("You hold [src] skyward, dispelling the storm!")) + playsound(user, 'sound/magic/staff_change.ogg', 200, FALSE) + var/old_color = user.color + user.color = list(340/255, 240/255, 0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0) + var/old_transform = user.transform + user.transform *= 1.2 + animate(user, color = old_color, transform = old_transform, time = 1 SECONDS) + affected_weather.wind_down() + log_game("[user] ([key_name(user)]) has dispelled a storm at [AREACOORD(user_turf)]") + +/obj/item/storm_staff/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + . = ..() + if(!thunder_charges) + balloon_alert(user, "needs to charge!") + return + var/turf/target_turf = get_turf(target) + var/area/target_area = get_area(target) + if(!target_turf || !target_area || (is_type_in_list(target_area, excluded_areas))) + balloon_alert(user, "can't bolt here!") + return + if(target_turf in targeted_turfs) + balloon_alert(user, "already targeted!") + return + if(HAS_TRAIT(user, TRAIT_PACIFISM)) + balloon_alert(user, "you don't want to harm!") + return + var/power_boosted = FALSE + for(var/datum/weather/weather as anything in SSweather.processing) + if(weather.stage != MAIN_STAGE) + continue + if((target_turf.z in weather.impacted_z_levels) && ispath(target_area.type, weather.area_type)) + power_boosted = TRUE + break + playsound(src, 'sound/magic/lightningshock.ogg', 10, TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, falloff_distance = 0) + targeted_turfs += target_turf + balloon_alert(user, "you aim at [target_turf]...") + new /obj/effect/temp_visual/telegraphing/thunderbolt(target_turf) + addtimer(CALLBACK(src, .proc/throw_thunderbolt, target_turf, power_boosted), 1.5 SECONDS) + thunder_charges-- + addtimer(CALLBACK(src, .proc/recharge), thunder_charge_time) + log_game("[key_name(user)] fired the staff of storms at [AREACOORD(target_turf)].") + +/obj/item/storm_staff/proc/recharge(mob/user) + thunder_charges = min(thunder_charges + 1, max_thunder_charges) + playsound(src, 'sound/magic/charge.ogg', 10, TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, falloff_distance = 0) + +/obj/item/storm_staff/proc/throw_thunderbolt(turf/target, boosted) + targeted_turfs -= target + new /obj/effect/temp_visual/thunderbolt(target) + var/list/affected_turfs = list(target) + if(boosted) + for(var/direction in GLOB.alldirs) + var/turf_to_add = get_step(target, direction) + if(!turf_to_add) + continue + affected_turfs += turf_to_add + for(var/turf/turf as anything in affected_turfs) + new /obj/effect/temp_visual/electricity(turf) + for(var/mob/living/hit_mob in turf) + to_chat(hit_mob, span_userdanger("You've been struck by lightning!")) + hit_mob.electrocute_act(15 * (isanimal(hit_mob) ? 3 : 1) * (turf == target ? 2 : 1) * (boosted ? 2 : 1), src, flags = SHOCK_TESLA|SHOCK_NOSTUN) + for(var/obj/hit_thing in turf) + hit_thing.take_damage(20, BURN, ENERGY, FALSE) + playsound(target, 'sound/magic/lightningbolt.ogg', 100, TRUE) + target.visible_message(span_danger("A thunderbolt strikes [target]!")) + explosion(target, light_impact_range = (boosted ? 1 : 0), flame_range = (boosted ? 2 : 1), silent = TRUE) diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index 3f2d39c8c07..3f665b11f3a 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -1,7 +1,4 @@ -//The chests dropped by mob spawner tendrils. Also contains associated loot. - -#define HIEROPHANT_BLINK_RANGE 5 -#define HIEROPHANT_BLINK_COOLDOWN 15 SECONDS +//The chests dropped by tendrils and megafauna. /obj/structure/closet/crate/necropolis name = "necropolis chest" @@ -11,8 +8,7 @@ /obj/structure/closet/crate/necropolis/tendril desc = "It's watching you suspiciously. You need a skeleton key to open it." - ///prevents bust_open to fire - integrity_failure = 0 + integrity_failure = 0 //prevents bust_open from firing /// var to check if it got opened by a key var/spawned_loot = FALSE @@ -30,9 +26,9 @@ if(1) new /obj/item/shared_storage/red(src) if(2) - new /obj/item/soulstone/anybody(src) + new /obj/item/soulstone/anybody/mining(src) if(3) - new /obj/item/katana/cursed(src) + new /obj/item/organ/cyberimp/arm/katana(src) if(4) new /obj/item/clothing/glasses/godeye(src) if(5) @@ -87,732 +83,7 @@ return FALSE return ..() -//KA modkit design discs -/obj/item/disk/design_disk/modkit_disc - name = "KA Mod Disk" - desc = "A design disc containing the design for a unique kinetic accelerator modkit. It's compatible with a research console." - icon_state = "datadisk1" - var/modkit_design = /datum/design/unique_modkit - -/obj/item/disk/design_disk/modkit_disc/Initialize() - . = ..() - blueprints[1] = new modkit_design - -/obj/item/disk/design_disk/modkit_disc/mob_and_turf_aoe - name = "Offensive Mining Explosion Mod Disk" - modkit_design = /datum/design/unique_modkit/offensive_turf_aoe - -/obj/item/disk/design_disk/modkit_disc/rapid_repeater - name = "Rapid Repeater Mod Disk" - modkit_design = /datum/design/unique_modkit/rapid_repeater - -/obj/item/disk/design_disk/modkit_disc/resonator_blast - name = "Resonator Blast Mod Disk" - modkit_design = /datum/design/unique_modkit/resonator_blast - -/obj/item/disk/design_disk/modkit_disc/bounty - name = "Death Syphon Mod Disk" - modkit_design = /datum/design/unique_modkit/bounty - -/datum/design/unique_modkit - category = list("Mining Designs", "Cyborg Upgrade Modules") //can't be normally obtained - build_type = PROTOLATHE | AWAY_LATHE | MECHFAB - departmental_flags = DEPARTMENTAL_FLAG_CARGO - -/datum/design/unique_modkit/offensive_turf_aoe - name = "Kinetic Accelerator Offensive Mining Explosion Mod" - desc = "A device which causes kinetic accelerators to fire AoE blasts that destroy rock and damage creatures." - id = "hyperaoemod" - materials = list(/datum/material/iron = 7000, /datum/material/glass = 3000, /datum/material/silver = 3000, /datum/material/gold = 3000, /datum/material/diamond = 4000) - build_path = /obj/item/borg/upgrade/modkit/aoe/turfs/andmobs - -/datum/design/unique_modkit/rapid_repeater - name = "Kinetic Accelerator Rapid Repeater Mod" - desc = "A device which greatly reduces a kinetic accelerator's cooldown on striking a living target or rock, but greatly increases its base cooldown." - id = "repeatermod" - materials = list(/datum/material/iron = 5000, /datum/material/glass = 5000, /datum/material/uranium = 8000, /datum/material/bluespace = 2000) - build_path = /obj/item/borg/upgrade/modkit/cooldown/repeater - -/datum/design/unique_modkit/resonator_blast - name = "Kinetic Accelerator Resonator Blast Mod" - desc = "A device which causes kinetic accelerators to fire shots that leave and detonate resonator blasts." - id = "resonatormod" - materials = list(/datum/material/iron = 5000, /datum/material/glass = 5000, /datum/material/silver = 5000, /datum/material/uranium = 5000) - build_path = /obj/item/borg/upgrade/modkit/resonator_blasts - -/datum/design/unique_modkit/bounty - name = "Kinetic Accelerator Death Syphon Mod" - desc = "A device which causes kinetic accelerators to permanently gain damage against creature types killed with it." - id = "bountymod" - materials = list(/datum/material/iron = 4000, /datum/material/silver = 4000, /datum/material/gold = 4000, /datum/material/bluespace = 4000) - reagents_list = list(/datum/reagent/blood = 40) - build_path = /obj/item/borg/upgrade/modkit/bounty - -//Spooky special loot - -//Rod of Asclepius -/obj/item/rod_of_asclepius - name = "\improper Rod of Asclepius" - desc = "A wooden rod about the size of your forearm with a snake carved around it, winding its way up the sides of the rod. Something about it seems to inspire in you the responsibilty and duty to help others." - icon = 'icons/obj/lavaland/artefacts.dmi' - lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi' - icon_state = "asclepius_dormant" - var/activated = FALSE - var/usedHand - -/obj/item/rod_of_asclepius/attack_self(mob/user) - if(activated) - return - if(!iscarbon(user)) - to_chat(user, span_warning("The snake carving seems to come alive, if only for a moment, before returning to its dormant state, almost as if it finds you incapable of holding its oath.")) - return - var/mob/living/carbon/itemUser = user - usedHand = itemUser.get_held_index_of_item(src) - if(itemUser.has_status_effect(STATUS_EFFECT_HIPPOCRATIC_OATH)) - to_chat(user, span_warning("You can't possibly handle the responsibility of more than one rod!")) - return - var/failText = span_warning("The snake seems unsatisfied with your incomplete oath and returns to its previous place on the rod, returning to its dormant, wooden state. You must stand still while completing your oath!") - to_chat(itemUser, span_notice("The wooden snake that was carved into the rod seems to suddenly come alive and begins to slither down your arm! The compulsion to help others grows abnormally strong...")) - if(do_after(itemUser, 40, target = itemUser)) - itemUser.say("I swear to fulfill, to the best of my ability and judgment, this covenant:", forced = "hippocratic oath") - else - to_chat(itemUser, failText) - return - if(do_after(itemUser, 20, target = itemUser)) - itemUser.say("I will apply, for the benefit of the sick, all measures that are required, avoiding those twin traps of overtreatment and therapeutic nihilism.", forced = "hippocratic oath") - else - to_chat(itemUser, failText) - return - if(do_after(itemUser, 30, target = itemUser)) - itemUser.say("I will remember that I remain a member of society, with special obligations to all my fellow human beings, those sound of mind and body as well as the infirm.", forced = "hippocratic oath") - else - to_chat(itemUser, failText) - return - if(do_after(itemUser, 30, target = itemUser)) - itemUser.say("If I do not violate this oath, may I enjoy life and art, respected while I live and remembered with affection thereafter. May I always act so as to preserve the finest traditions of my calling and may I long experience the joy of healing those who seek my help.", forced = "hippocratic oath") - else - to_chat(itemUser, failText) - return - to_chat(itemUser, span_notice("The snake, satisfied with your oath, attaches itself and the rod to your forearm with an inseparable grip. Your thoughts seem to only revolve around the core idea of helping others, and harm is nothing more than a distant, wicked memory...")) - var/datum/status_effect/hippocratic_oath/effect = itemUser.apply_status_effect(STATUS_EFFECT_HIPPOCRATIC_OATH) - effect.hand = usedHand - activated() - -/obj/item/rod_of_asclepius/proc/activated() - item_flags = DROPDEL - ADD_TRAIT(src, TRAIT_NODROP, CURSED_ITEM_TRAIT(type)) - desc = "A short wooden rod with a mystical snake inseparably gripping itself and the rod to your forearm. It flows with a healing energy that disperses amongst yourself and those around you. " - icon_state = "asclepius_active" - activated = TRUE - -//Memento Mori -/obj/item/clothing/neck/necklace/memento_mori - name = "Memento Mori" - desc = "A mysterious pendant. An inscription on it says: \"Certain death tomorrow means certain life today.\"" - icon = 'icons/obj/lavaland/artefacts.dmi' - icon_state = "memento_mori" - worn_icon_state = "memento" - actions_types = list(/datum/action/item_action/hands_free/memento_mori) - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - var/mob/living/carbon/human/active_owner - -/obj/item/clothing/neck/necklace/memento_mori/item_action_slot_check(slot) - return slot == ITEM_SLOT_NECK - -/obj/item/clothing/neck/necklace/memento_mori/dropped(mob/user) - ..() - if(active_owner) - mori() - -//Just in case -/obj/item/clothing/neck/necklace/memento_mori/Destroy() - if(active_owner) - mori() - return ..() - -/obj/item/clothing/neck/necklace/memento_mori/proc/memento(mob/living/carbon/human/user) - to_chat(user, span_warning("You feel your life being drained by the pendant...")) - if(do_after(user, 40, target = user)) - to_chat(user, span_notice("Your lifeforce is now linked to the pendant! You feel like removing it would kill you, and yet you instinctively know that until then, you won't die.")) - ADD_TRAIT(user, TRAIT_NODEATH, CLOTHING_TRAIT) - ADD_TRAIT(user, TRAIT_NOHARDCRIT, CLOTHING_TRAIT) - ADD_TRAIT(user, TRAIT_NOCRITDAMAGE, CLOTHING_TRAIT) - icon_state = "memento_mori_active" - active_owner = user - -/obj/item/clothing/neck/necklace/memento_mori/proc/mori() - icon_state = "memento_mori" - if(!active_owner) - return - var/mob/living/carbon/human/H = active_owner //to avoid infinite looping when dust unequips the pendant - active_owner = null - to_chat(H, span_userdanger("You feel your life rapidly slipping away from you!")) - H.dust(TRUE, TRUE) - -/datum/action/item_action/hands_free/memento_mori - check_flags = NONE - name = "Memento Mori" - desc = "Bind your life to the pendant." - -/datum/action/item_action/hands_free/memento_mori/Trigger() - var/obj/item/clothing/neck/necklace/memento_mori/MM = target - if(!MM.active_owner) - if(ishuman(owner)) - MM.memento(owner) - Remove(MM.active_owner) //Remove the action button, since there's no real use in having it now. - -//Wisp Lantern -/obj/item/wisp_lantern - name = "spooky lantern" - desc = "This lantern gives off no light, but is home to a friendly wisp." - icon = 'icons/obj/lighting.dmi' - icon_state = "lantern-blue" - inhand_icon_state = "lantern" - lefthand_file = 'icons/mob/inhands/equipment/mining_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/mining_righthand.dmi' - var/obj/effect/wisp/wisp - -/obj/item/wisp_lantern/attack_self(mob/user) - if(!wisp) - to_chat(user, span_warning("The wisp has gone missing!")) - icon_state = "lantern" - return - - if(wisp.loc == src) - to_chat(user, span_notice("You release the wisp. It begins to bob around your head.")) - icon_state = "lantern" - wisp.orbit(user, 20) - SSblackbox.record_feedback("tally", "wisp_lantern", 1, "Freed") - - else - to_chat(user, span_notice("You return the wisp to the lantern.")) - icon_state = "lantern-blue" - wisp.forceMove(src) - SSblackbox.record_feedback("tally", "wisp_lantern", 1, "Returned") - -/obj/item/wisp_lantern/Initialize() - . = ..() - wisp = new(src) - -/obj/item/wisp_lantern/Destroy() - if(wisp) - if(wisp.loc == src) - qdel(wisp) - else - wisp.visible_message(span_notice("[wisp] has a sad feeling for a moment, then it passes.")) - return ..() - -/obj/effect/wisp - name = "friendly wisp" - desc = "Happy to light your way." - icon = 'icons/obj/lighting.dmi' - icon_state = "orb" - light_system = MOVABLE_LIGHT - light_range = 7 - light_flags = LIGHT_ATTACHED - layer = ABOVE_ALL_MOB_LAYER - var/sight_flags = SEE_MOBS - var/lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - -/obj/effect/wisp/orbit(atom/thing, radius, clockwise, rotation_speed, rotation_segments, pre_rotation, lockinorbit) - . = ..() - if(ismob(thing)) - RegisterSignal(thing, COMSIG_MOB_UPDATE_SIGHT, .proc/update_user_sight) - var/mob/being = thing - being.update_sight() - to_chat(thing, span_notice("The wisp enhances your vision.")) - -/obj/effect/wisp/stop_orbit(datum/component/orbiter/orbits) - . = ..() - if(ismob(orbits.parent)) - UnregisterSignal(orbits.parent, COMSIG_MOB_UPDATE_SIGHT) - to_chat(orbits.parent, span_notice("Your vision returns to normal.")) - -/obj/effect/wisp/proc/update_user_sight(mob/user) - SIGNAL_HANDLER - user.sight |= sight_flags - if(!isnull(lighting_alpha)) - user.lighting_alpha = min(user.lighting_alpha, lighting_alpha) - -//Red/Blue Cubes -/obj/item/warp_cube - name = "blue cube" - desc = "A mysterious blue cube." - icon = 'icons/obj/lavaland/artefacts.dmi' - icon_state = "blue_cube" - var/teleport_color = "#3FBAFD" - var/obj/item/warp_cube/linked - var/teleporting = FALSE - -/obj/item/warp_cube/Destroy() - if(!QDELETED(linked)) - qdel(linked) - linked = null - return ..() - -/obj/item/warp_cube/attack_self(mob/user) - var/turf/current_location = get_turf(user) - var/area/current_area = current_location.loc - if(!linked || (current_area.area_flags & NOTELEPORT)) - to_chat(user, span_warning("[src] fizzles uselessly.")) - return - if(teleporting) - return - teleporting = TRUE - linked.teleporting = TRUE - var/turf/T = get_turf(src) - new /obj/effect/temp_visual/warp_cube(T, user, teleport_color, TRUE) - SSblackbox.record_feedback("tally", "warp_cube", 1, type) - new /obj/effect/temp_visual/warp_cube(get_turf(linked), user, linked.teleport_color, FALSE) - var/obj/effect/warp_cube/link_holder = new /obj/effect/warp_cube(T) - user.forceMove(link_holder) //mess around with loc so the user can't wander around - sleep(2.5) - if(QDELETED(user)) - qdel(link_holder) - return - if(QDELETED(linked)) - user.forceMove(get_turf(link_holder)) - qdel(link_holder) - return - link_holder.forceMove(get_turf(linked)) - sleep(2.5) - if(QDELETED(user)) - qdel(link_holder) - return - teleporting = FALSE - if(!QDELETED(linked)) - linked.teleporting = FALSE - user.forceMove(get_turf(link_holder)) - qdel(link_holder) - -/obj/item/warp_cube/red - name = "red cube" - desc = "A mysterious red cube." - icon_state = "red_cube" - teleport_color = "#FD3F48" - -/obj/item/warp_cube/red/Initialize() - . = ..() - if(!linked) - var/obj/item/warp_cube/blue = new(src.loc) - linked = blue - blue.linked = src - -/obj/effect/warp_cube - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - anchored = TRUE - -//Meat Hook -/obj/item/gun/magic/hook - name = "meat hook" - desc = "Mid or feed." - ammo_type = /obj/item/ammo_casing/magic/hook - icon_state = "hook" - inhand_icon_state = "hook" - lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' - fire_sound = 'sound/weapons/batonextend.ogg' - max_charges = 1 - item_flags = NEEDS_PERMIT | NOBLUDGEON - sharpness = SHARP_POINTY - pinless = TRUE - force = 18 - -/obj/item/gun/magic/hook/shoot_with_empty_chamber(mob/living/user) - to_chat(user, span_warning("[src] isn't ready to fire yet!")) - -/obj/item/ammo_casing/magic/hook - name = "hook" - desc = "A hook." - projectile_type = /obj/projectile/hook - caliber = CALIBER_HOOK - icon_state = "hook" - firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect/energy - -/obj/projectile/hook - name = "hook" - icon_state = "hook" - icon = 'icons/obj/lavaland/artefacts.dmi' - pass_flags = PASSTABLE - damage = 20 - stamina = 20 - armour_penetration = 60 - damage_type = BRUTE - hitsound = 'sound/effects/splat.ogg' - var/chain - var/knockdown_time = (0.5 SECONDS) - -/obj/projectile/hook/fire(setAngle) - if(firer) - chain = firer.Beam(src, icon_state = "chain") - ..() - //TODO: root the firer until the chain returns - -/obj/projectile/hook/on_hit(atom/target) - . = ..() - if(ismovable(target)) - var/atom/movable/A = target - if(A.anchored) - return - A.visible_message(span_danger("[A] is snagged by [firer]'s hook!")) - new /datum/forced_movement(A, get_turf(firer), 5, TRUE) - if (isliving(target)) - var/mob/living/fresh_meat = target - fresh_meat.Knockdown(knockdown_time) - return - //TODO: keep the chain beamed to A - //TODO: needs a callback to delete the chain - -/obj/projectile/hook/Destroy() - qdel(chain) - return ..() - -//just a nerfed version of the real thing for the bounty hunters. -/obj/item/gun/magic/hook/bounty - name = "hook" - ammo_type = /obj/item/ammo_casing/magic/hook/bounty - -/obj/item/ammo_casing/magic/hook/bounty - projectile_type = /obj/projectile/hook/bounty - -/obj/projectile/hook/bounty - damage = 0 - stamina = 40 - -//Immortality Talisman -/obj/item/immortality_talisman - name = "\improper Immortality Talisman" - desc = "A dread talisman that can render you completely invulnerable." - icon = 'icons/obj/lavaland/artefacts.dmi' - icon_state = "talisman" - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - actions_types = list(/datum/action/item_action/immortality) - var/cooldown = 0 - -/obj/item/immortality_talisman/Initialize() - . = ..() - AddComponent(/datum/component/anti_magic, TRUE, TRUE, TRUE) - -/datum/action/item_action/immortality - name = "Immortality" - -/obj/item/immortality_talisman/attack_self(mob/user) - if(cooldown < world.time) - SSblackbox.record_feedback("amount", "immortality_talisman_uses", 1) - cooldown = world.time + 600 - new /obj/effect/immortality_talisman(get_turf(user), user) - else - to_chat(user, span_warning("[src] is not ready yet!")) - -/obj/effect/immortality_talisman - name = "hole in reality" - desc = "It's shaped an awful lot like a person." - icon_state = "blank" - icon = 'icons/effects/effects.dmi' - var/vanish_description = "vanishes from reality" - var/can_destroy = TRUE - -/obj/effect/immortality_talisman/Initialize(mapload, mob/new_user) - . = ..() - if(new_user) - vanish(new_user) - -/obj/effect/immortality_talisman/proc/vanish(mob/user) - user.visible_message(span_danger("[user] [vanish_description], leaving a hole in [user.p_their()] place!")) - - desc = "It's shaped an awful lot like [user.name]." - setDir(user.dir) - - user.forceMove(src) - user.notransform = TRUE - user.status_flags |= GODMODE - - can_destroy = FALSE - - addtimer(CALLBACK(src, .proc/unvanish, user), 10 SECONDS) - -/obj/effect/immortality_talisman/proc/unvanish(mob/user) - user.status_flags &= ~GODMODE - user.notransform = FALSE - user.forceMove(get_turf(src)) - - user.visible_message(span_danger("[user] pops back into reality!")) - can_destroy = TRUE - qdel(src) - -/obj/effect/immortality_talisman/attackby() - return - -/obj/effect/immortality_talisman/singularity_pull() - return - -/obj/effect/immortality_talisman/Destroy(force) - if(!can_destroy && !force) - return QDEL_HINT_LETMELIVE - else - . = ..() - -/obj/effect/immortality_talisman/void - vanish_description = "is dragged into the void" - -//Shared Bag - -/obj/item/shared_storage - name = "paradox bag" - desc = "Somehow, it's in two places at once." - icon = 'icons/obj/lavaland/artefacts.dmi' - icon_state = "paradox_bag" - worn_icon_state = "paradoxbag" - slot_flags = ITEM_SLOT_BELT - resistance_flags = INDESTRUCTIBLE - -/obj/item/shared_storage/red - name = "paradox bag" - desc = "Somehow, it's in two places at once." - -/obj/item/shared_storage/red/Initialize() - . = ..() - var/datum/component/storage/STR = AddComponent(/datum/component/storage/concrete) - STR.max_w_class = WEIGHT_CLASS_NORMAL - STR.max_combined_w_class = 15 - STR.max_items = 21 - new /obj/item/shared_storage/blue(drop_location(), STR) - -/obj/item/shared_storage/blue/Initialize(mapload, datum/component/storage/concrete/master) - . = ..() - if(!istype(master)) - return INITIALIZE_HINT_QDEL - var/datum/component/storage/STR = AddComponent(/datum/component/storage, master) - STR.max_w_class = WEIGHT_CLASS_NORMAL - STR.max_combined_w_class = 15 - STR.max_items = 21 - -//Book of Babel - -/obj/item/book_of_babel - name = "Book of Babel" - desc = "An ancient tome written in countless tongues." - icon = 'icons/obj/library.dmi' - icon_state = "book1" - w_class = 2 - -/obj/item/book_of_babel/attack_self(mob/user) - if(!user.can_read(src)) - return FALSE - to_chat(user, span_notice("You flip through the pages of the book, quickly and conveniently learning every language in existence. Somewhat less conveniently, the aging book crumbles to dust in the process. Whoops.")) - user.grant_all_languages() - new /obj/effect/decal/cleanable/ash(get_turf(user)) - qdel(src) - - -//Potion of Flight -/obj/item/reagent_containers/glass/bottle/potion - icon = 'icons/obj/lavaland/artefacts.dmi' - icon_state = "potionflask" - -/obj/item/reagent_containers/glass/bottle/potion/flight - name = "strange elixir" - desc = "A flask with an almost-holy aura emitting from it. The label on the bottle says: 'erqo'hyy tvi'rf lbh jv'atf'." - list_reagents = list(/datum/reagent/flightpotion = 5) - -/obj/item/reagent_containers/glass/bottle/potion/update_icon_state() - icon_state = "potionflask[reagents.total_volume ? null : "_empty"]" - return ..() - -/datum/reagent/flightpotion - name = "Flight Potion" - description = "Strange mutagenic compound of unknown origins." - reagent_state = LIQUID - color = "#FFEBEB" - -/datum/reagent/flightpotion/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message = TRUE) - . = ..() - if(iscarbon(exposed_mob) && exposed_mob.stat != DEAD) - var/mob/living/carbon/exposed_carbon = exposed_mob - var/holycheck = ishumanbasic(exposed_carbon) - if(!HAS_TRAIT(exposed_carbon, TRAIT_CAN_USE_FLIGHT_POTION) || reac_volume < 5) - if((methods & INGEST) && show_message) - to_chat(exposed_carbon, span_notice("You feel nothing but a terrible aftertaste.")) - return - if(exposed_carbon.dna.species.has_innate_wings) - to_chat(exposed_carbon, span_userdanger("A terrible pain travels down your back as your wings change shape!")) - else - to_chat(exposed_carbon, span_userdanger("A terrible pain travels down your back as wings burst out!")) - exposed_carbon.dna.species.GiveSpeciesFlight(exposed_carbon) - if(holycheck) - to_chat(exposed_carbon, span_notice("You feel blessed!")) - ADD_TRAIT(exposed_carbon, TRAIT_HOLY, SPECIES_TRAIT) - playsound(exposed_carbon.loc, 'sound/items/poster_ripped.ogg', 50, TRUE, -1) - exposed_carbon.adjustBruteLoss(20) - exposed_carbon.emote("scream") - - -/obj/item/jacobs_ladder - name = "jacob's ladder" - desc = "A celestial ladder that violates the laws of physics." - icon = 'icons/obj/structures.dmi' - icon_state = "ladder00" - -/obj/item/jacobs_ladder/attack_self(mob/user) - var/turf/T = get_turf(src) - var/ladder_x = T.x - var/ladder_y = T.y - to_chat(user, span_notice("You unfold the ladder. It extends much farther than you were expecting.")) - var/last_ladder = null - for(var/i in 1 to world.maxz) - if(is_centcom_level(i) || is_reserved_level(i) || is_away_level(i)) - continue - var/turf/T2 = locate(ladder_x, ladder_y, i) - last_ladder = new /obj/structure/ladder/unbreakable/jacob(T2, null, last_ladder) - qdel(src) - -// Inherit from unbreakable but don't set ID, to suppress the default Z linkage -/obj/structure/ladder/unbreakable/jacob - name = "jacob's ladder" - desc = "An indestructible celestial ladder that violates the laws of physics." - -//Concussive Gauntlets -/obj/item/clothing/gloves/gauntlets - name = "concussive gauntlets" - desc = "Pickaxes... for your hands!" - icon_state = "concussive_gauntlets" - inhand_icon_state = "concussive_gauntlets" - toolspeed = 0.1 - strip_delay = 40 - equip_delay_other = 20 - cold_protection = HANDS - min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT - heat_protection = HANDS - max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT - resistance_flags = LAVA_PROOF | FIRE_PROOF //they are from lavaland after all - armor = list(MELEE = 15, BULLET = 25, LASER = 15, ENERGY = 15, BOMB = 100, BIO = 0, RAD = 0, FIRE = 100, ACID = 30) //mostly bone bracer armor - -/obj/item/clothing/gloves/gauntlets/equipped(mob/user, slot) - . = ..() - if(slot == ITEM_SLOT_GLOVES) - tool_behaviour = TOOL_MINING - RegisterSignal(user, COMSIG_HUMAN_EARLY_UNARMED_ATTACK, .proc/rocksmash) - RegisterSignal(user, COMSIG_MOVABLE_BUMP, .proc/rocksmash) - else - stopmining(user) - -/obj/item/clothing/gloves/gauntlets/dropped(mob/user) - . = ..() - stopmining(user) - -/obj/item/clothing/gloves/gauntlets/proc/stopmining(mob/user) - tool_behaviour = initial(tool_behaviour) - UnregisterSignal(user, COMSIG_HUMAN_EARLY_UNARMED_ATTACK) - UnregisterSignal(user, COMSIG_MOVABLE_BUMP) - -/obj/item/clothing/gloves/gauntlets/proc/rocksmash(mob/living/carbon/human/H, atom/A, proximity) - SIGNAL_HANDLER - if(!istype(A, /turf/closed/mineral)) - return - A.attackby(src, H) - return COMPONENT_CANCEL_ATTACK_CHAIN - - -///Bosses - -//Miniboss Miner - -/obj/item/melee/transforming/cleaving_saw - name = "cleaving saw" - desc = "This saw, effective at drawing the blood of beasts, transforms into a long cleaver that makes use of centrifugal force." - force = 12 - force_on = 20 //force when active - throwforce = 20 - throwforce_on = 20 - icon = 'icons/obj/lavaland/artefacts.dmi' - lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi' - righthand_file = 'icons/mob/inhands/64x64_righthand.dmi' - inhand_x_dimension = 64 - inhand_y_dimension = 64 - icon_state = "cleaving_saw" - icon_state_on = "cleaving_saw_open" - worn_icon_state = "cleaving_saw" - slot_flags = ITEM_SLOT_BELT - attack_verb_off = list("attacks", "saws", "slices", "tears", "lacerates", "rips", "dices", "cuts") - attack_verb_on = list("cleaves", "swipes", "slashes", "chops") - hitsound = 'sound/weapons/bladeslice.ogg' - hitsound_on = 'sound/weapons/bladeslice.ogg' - w_class = WEIGHT_CLASS_BULKY - sharpness = SHARP_EDGED - faction_bonus_force = 30 - nemesis_factions = list("mining", "boss") - var/transform_cooldown - var/swiping = FALSE - var/bleed_stacks_per_hit = 3 - -/obj/item/melee/transforming/cleaving_saw/examine(mob/user) - . = ..() - . += "It is [active ? "open, will cleave enemies in a wide arc and deal additional damage to fauna":"closed, and can be used for rapid consecutive attacks that cause fauna to bleed"].\n"+\ - "Both modes will build up existing bleed effects, doing a burst of high damage if the bleed is built up high enough.\n"+\ - "Transforming it immediately after an attack causes the next attack to come out faster." - -/obj/item/melee/transforming/cleaving_saw/suicide_act(mob/user) - user.visible_message(span_suicide("[user] is [active ? "closing [src] on [user.p_their()] neck" : "opening [src] into [user.p_their()] chest"]! It looks like [user.p_theyre()] trying to commit suicide!")) - transform_cooldown = 0 - transform_weapon(user, TRUE) - return BRUTELOSS - -/obj/item/melee/transforming/cleaving_saw/transform_weapon(mob/living/user, supress_message_text) - if(transform_cooldown > world.time) - return FALSE - . = ..() - if(.) - transform_cooldown = world.time + (CLICK_CD_MELEE * 0.5) - user.changeNext_move(CLICK_CD_MELEE * 0.25) - -/obj/item/melee/transforming/cleaving_saw/transform_messages(mob/living/user, supress_message_text) - if(!supress_message_text) - if(active) - to_chat(user, span_notice("You open [src]. It will now cleave enemies in a wide arc and deal additional damage to fauna.")) - else - to_chat(user, span_notice("You close [src]. It will now attack rapidly and cause fauna to bleed.")) - playsound(user, 'sound/magic/clockwork/fellowship_armory.ogg', 35, TRUE, frequency = 90000 - (active * 30000)) - -/obj/item/melee/transforming/cleaving_saw/clumsy_transform_effect(mob/living/user) - if(HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50)) - to_chat(user, span_warning("You accidentally cut yourself with [src], like a doofus!")) - user.take_bodypart_damage(10) - -/obj/item/melee/transforming/cleaving_saw/melee_attack_chain(mob/user, atom/target, params) - ..() - if(!active) - user.changeNext_move(CLICK_CD_MELEE * 0.5) //when closed, it attacks very rapidly - -/obj/item/melee/transforming/cleaving_saw/nemesis_effects(mob/living/user, mob/living/target) - if(istype(target, /mob/living/simple_animal/hostile/asteroid/elite)) - return - var/datum/status_effect/stacking/saw_bleed/B = target.has_status_effect(STATUS_EFFECT_SAWBLEED) - if(!B) - target.apply_status_effect(STATUS_EFFECT_SAWBLEED,bleed_stacks_per_hit) - else - B.add_stacks(bleed_stacks_per_hit) - -/obj/item/melee/transforming/cleaving_saw/attack(mob/living/target, mob/living/carbon/human/user) - if(!active || swiping || !target.density || get_turf(target) == get_turf(user)) - if(!active) - faction_bonus_force = 0 - ..() - if(!active) - faction_bonus_force = initial(faction_bonus_force) - else - var/turf/user_turf = get_turf(user) - var/dir_to_target = get_dir(user_turf, get_turf(target)) - swiping = TRUE - var/static/list/cleaving_saw_cleave_angles = list(0, -45, 45) //so that the animation animates towards the target clicked and not towards a side target - for(var/i in cleaving_saw_cleave_angles) - var/turf/T = get_step(user_turf, turn(dir_to_target, i)) - for(var/mob/living/L in T) - if(user.Adjacent(L) && L.density) - melee_attack_chain(user, L) - swiping = FALSE - -//Dragon +//Megafauna chests /obj/structure/closet/crate/necropolis/dragon name = "dragon chest" @@ -826,7 +97,6 @@ new /obj/item/lava_staff(src) if(3) new /obj/item/book/granter/spell/sacredflame(src) - new /obj/item/gun/magic/wand/fireball(src) if(4) new /obj/item/dragons_blood(src) @@ -837,230 +107,17 @@ ..() new /obj/item/crusher_trophy/tail_spike(src) -/obj/item/melee/ghost_sword - name = "\improper spectral blade" - desc = "A rusted and dulled blade. It doesn't look like it'd do much damage. It glows weakly." - icon_state = "spectral" - inhand_icon_state = "spectral" - lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' - flags_1 = CONDUCT_1 - sharpness = SHARP_EDGED - w_class = WEIGHT_CLASS_BULKY - force = 1 - throwforce = 1 - hitsound = 'sound/effects/ghost2.ogg' - attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "rends") - attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "rend") - var/summon_cooldown = 0 - var/list/mob/dead/observer/spirits - -/obj/item/melee/ghost_sword/Initialize() - . = ..() - spirits = list() - START_PROCESSING(SSobj, src) - AddElement(/datum/element/point_of_interest) - AddComponent(/datum/component/butchering, 150, 90) - -/obj/item/melee/ghost_sword/Destroy() - for(var/mob/dead/observer/G in spirits) - G.invisibility = GLOB.observer_default_invisibility - spirits.Cut() - STOP_PROCESSING(SSobj, src) - . = ..() - -/obj/item/melee/ghost_sword/attack_self(mob/user) - if(summon_cooldown > world.time) - to_chat(user, span_warning("You just recently called out for aid. You don't want to annoy the spirits!")) - return - to_chat(user, span_notice("You call out for aid, attempting to summon spirits to your side.")) - - notify_ghosts("[user] is raising [user.p_their()] [name], calling for your help!", - enter_link="(Click to help)", - source = user, ignore_key = POLL_IGNORE_SPECTRAL_BLADE, header = "Spectral blade") - - summon_cooldown = world.time + 600 - -/obj/item/melee/ghost_sword/Topic(href, href_list) - if(href_list["orbit"]) - var/mob/dead/observer/ghost = usr - if(istype(ghost)) - ghost.ManualFollow(src) - -/obj/item/melee/ghost_sword/process() - ghost_check() - -/obj/item/melee/ghost_sword/proc/ghost_check() - var/ghost_counter = 0 - var/turf/T = get_turf(src) - var/list/contents = T.GetAllContents() - var/mob/dead/observer/current_spirits = list() - for(var/thing in contents) - var/atom/A = thing - A.transfer_observers_to(src) - - for(var/i in orbiters?.orbiter_list) - if(!isobserver(i)) - continue - var/mob/dead/observer/G = i - ghost_counter++ - G.invisibility = 0 - current_spirits |= G - - for(var/mob/dead/observer/G in spirits - current_spirits) - G.invisibility = GLOB.observer_default_invisibility - - spirits = current_spirits - - return ghost_counter - -/obj/item/melee/ghost_sword/attack(mob/living/target, mob/living/carbon/human/user) - force = 0 - var/ghost_counter = ghost_check() - - force = clamp((ghost_counter * 4), 0, 75) - user.visible_message(span_danger("[user] strikes with the force of [ghost_counter] vengeful spirits!")) - ..() - -/obj/item/melee/ghost_sword/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - var/ghost_counter = ghost_check() - final_block_chance += clamp((ghost_counter * 5), 0, 75) - owner.visible_message(span_danger("[owner] is protected by a ring of [ghost_counter] ghosts!")) - return ..() - -//Blood - -/obj/item/dragons_blood - name = "bottle of dragons blood" - desc = "You're not actually going to drink this, are you?" - icon = 'icons/obj/wizard.dmi' - icon_state = "vial" - -/obj/item/dragons_blood/attack_self(mob/living/carbon/human/user) - if(!istype(user)) - return - - var/mob/living/carbon/human/H = user - var/random = rand(1,4) - - switch(random) - if(1) - to_chat(user, span_danger("Your appearance morphs to that of a very small humanoid ash dragon! You get to look like a freak without the cool abilities.")) - H.dna.features = list("mcolor" = "A02720", "tail_lizard" = "Dark Tiger", "tail_human" = "None", "snout" = "Sharp", "horns" = "Curled", "ears" = "None", "wings" = "None", "frills" = "None", "spines" = "Long", "body_markings" = "Dark Tiger Body", "legs" = "Digitigrade Legs") - H.eye_color = "fee5a3" - H.set_species(/datum/species/lizard) - if(2) - to_chat(user, span_danger("Your flesh begins to melt! Miraculously, you seem fine otherwise.")) - H.set_species(/datum/species/skeleton) - if(3) - to_chat(user, span_danger("Power courses through you! You can now shift your form at will.")) - if(user.mind) - var/obj/effect/proc_holder/spell/targeted/shapeshift/dragon/D = new - user.mind.AddSpell(D) - if(4) - to_chat(user, span_danger("You feel like you could walk straight through lava now.")) - LAZYOR(H.weather_immunities, "lava") - - playsound(user.loc,'sound/items/drink.ogg', rand(10,50), TRUE) - qdel(src) - -/datum/disease/transformation/dragon - name = "dragon transformation" - cure_text = "nothing" - cures = list(/datum/reagent/medicine/adminordrazine) - agent = "dragon's blood" - desc = "What do dragons have to do with Space Station 13?" - stage_prob = 10 - severity = DISEASE_SEVERITY_BIOHAZARD - visibility_flags = 0 - stage1 = list("Your bones ache.") - stage2 = list("Your skin feels scaly.") - stage3 = list("You have an overwhelming urge to terrorize some peasants.", "Your teeth feel sharper.") - stage4 = list("Your blood burns.") - stage5 = list("You're a fucking dragon. However, any previous allegiances you held still apply. It'd be incredibly rude to eat your still human friends for no reason.") - new_form = /mob/living/simple_animal/hostile/megafauna/dragon/lesser - - -//Lava Staff - -/obj/item/lava_staff - name = "staff of lava" - desc = "The ability to fill the emergency shuttle with lava. What more could you want out of life?" - icon_state = "staffofstorms" - inhand_icon_state = "staffofstorms" - lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi' - icon = 'icons/obj/guns/magic.dmi' - slot_flags = ITEM_SLOT_BACK - w_class = WEIGHT_CLASS_BULKY - force = 25 - damtype = BURN - resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF - hitsound = 'sound/weapons/sear.ogg' - var/turf_type = /turf/open/lava/smooth - var/transform_string = "lava" - var/reset_turf_type = /turf/open/floor/plating/asteroid/basalt - var/reset_string = "basalt" - var/create_cooldown = 100 - var/create_delay = 30 - var/reset_cooldown = 50 - var/timer = 0 - var/static/list/banned_turfs = typecacheof(list(/turf/open/space/transit, /turf/closed)) - -/obj/item/lava_staff/afterattack(atom/target, mob/user, proximity_flag, click_parameters) - . = ..() - if(timer > world.time) - return - - if(is_type_in_typecache(target, banned_turfs)) - return - - if(target in view(user.client.view, get_turf(user))) - - var/turf/open/T = get_turf(target) - if(!istype(T)) - return - if(!istype(T, turf_type)) - var/obj/effect/temp_visual/lavastaff/L = new /obj/effect/temp_visual/lavastaff(T) - L.alpha = 0 - animate(L, alpha = 255, time = create_delay) - user.visible_message(span_danger("[user] points [src] at [T]!")) - timer = world.time + create_delay + 1 - if(do_after(user, create_delay, target = T)) - var/old_name = T.name - if(T.TerraformTurf(turf_type, flags = CHANGETURF_INHERIT_AIR)) - user.visible_message(span_danger("[user] turns \the [old_name] into [transform_string]!")) - message_admins("[ADMIN_LOOKUPFLW(user)] fired the lava staff at [ADMIN_VERBOSEJMP(T)]") - log_game("[key_name(user)] fired the lava staff at [AREACOORD(T)].") - timer = world.time + create_cooldown - playsound(T,'sound/magic/fireball.ogg', 200, TRUE) - else - timer = world.time - qdel(L) - else - var/old_name = T.name - if(T.TerraformTurf(reset_turf_type, flags = CHANGETURF_INHERIT_AIR)) - user.visible_message(span_danger("[user] turns \the [old_name] into [reset_string]!")) - timer = world.time + reset_cooldown - playsound(T,'sound/magic/fireball.ogg', 200, TRUE) - -/obj/effect/temp_visual/lavastaff - icon_state = "lavastaff_warn" - duration = 50 - -//Bubblegum /obj/structure/closet/crate/necropolis/bubblegum name = "bubblegum chest" /obj/structure/closet/crate/necropolis/bubblegum/PopulateContents() - new /obj/item/clothing/suit/space/hostile_environment(src) - new /obj/item/clothing/head/helmet/space/hostile_environment(src) + new /obj/item/clothing/suit/space/hardsuit/hostile_environment(src) var/loot = rand(1,2) switch(loot) if(1) new /obj/item/mayhem(src) if(2) - new /obj/item/gun/magic/staff/spellblade(src) + new /obj/item/soulscythe(src) /obj/structure/closet/crate/necropolis/bubblegum/crusher name = "bloody bubblegum chest" @@ -1069,22 +126,6 @@ ..() new /obj/item/crusher_trophy/demon_claws(src) -/obj/item/mayhem - name = "mayhem in a bottle" - desc = "A magically infused bottle of blood, the scent of which will drive anyone nearby into a murderous frenzy." - icon = 'icons/obj/wizard.dmi' - icon_state = "vial" - -/obj/item/mayhem/attack_self(mob/user) - for(var/mob/living/carbon/human/H in range(7,user)) - H.apply_status_effect(STATUS_EFFECT_MAYHEM) - to_chat(user, span_notice("You shatter the bottle!")) - playsound(user.loc, 'sound/effects/glassbr1.ogg', 100, TRUE) - message_admins(span_adminnotice("[ADMIN_LOOKUPFLW(user)] has activated a bottle of mayhem!")) - user.log_message("activated a bottle of mayhem", LOG_ATTACK) - qdel(src) - -//Colossus /obj/structure/closet/crate/necropolis/colossus name = "colossus chest" @@ -1106,241 +147,8 @@ ..() new /obj/item/crusher_trophy/blaster_tubes(src) -//Hierophant -/datum/action/innate/dash/hierophant - current_charges = 1 - max_charges = 1 - charge_rate = HIEROPHANT_BLINK_COOLDOWN - recharge_sound = null - phasein = /obj/effect/temp_visual/hierophant/blast/visual - phaseout = /obj/effect/temp_visual/hierophant/blast/visual - // It's a simple purple beam, works well enough for the purple hiero effects. - beam_effect = "plasmabeam" +//Other chests and minor stuff -/datum/action/innate/dash/hierophant/Teleport(mob/user, atom/target) - var/dist = get_dist(user, target) - if(dist > HIEROPHANT_BLINK_RANGE) - to_chat(user, span_hierophant_warning("Blink destination out of range.")) - return - var/turf/target_turf = get_turf(target) - if(target_turf.is_blocked_turf_ignore_climbable()) - to_chat(user, span_hierophant_warning("Blink destination blocked.")) - return - . = ..() - - if(!current_charges) - var/obj/item/hierophant_club/club = src.target - if(istype(club)) - club.blink_charged = FALSE - club.update_appearance() - -/datum/action/innate/dash/hierophant/charge() - var/obj/item/hierophant_club/club = target - if(istype(club)) - club.blink_charged = TRUE - club.update_appearance() - - current_charges = clamp(current_charges + 1, 0, max_charges) - owner.update_action_buttons_icon() - - if(recharge_sound) - playsound(dashing_item, recharge_sound, 50, TRUE) - to_chat(owner, span_notice("[src] now has [current_charges]/[max_charges] charges.")) - -/obj/item/hierophant_club - name = "hierophant club" - desc = "The strange technology of this large club allows various nigh-magical teleportation feats. It used to beat you, but now you can set the beat." - icon_state = "hierophant_club_ready_beacon" - inhand_icon_state = "hierophant_club_ready_beacon" - icon = 'icons/obj/lavaland/artefacts.dmi' - lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi' - righthand_file = 'icons/mob/inhands/64x64_righthand.dmi' - inhand_x_dimension = 64 - inhand_y_dimension = 64 - slot_flags = ITEM_SLOT_BACK - w_class = WEIGHT_CLASS_NORMAL - force = 15 - attack_verb_continuous = list("clubs", "beats", "pummels") - attack_verb_simple = list("club", "beat", "pummel") - hitsound = 'sound/weapons/sonic_jackhammer.ogg' - actions_types = list(/datum/action/item_action/vortex_recall) - - /// Linked teleport beacon for the group teleport functionality. - var/obj/effect/hierophant/beacon - /// TRUE if currently doing a teleport to the beacon, FALSE otherwise. - var/teleporting = FALSE //if we ARE teleporting - - /// Action enabling the blink-dash functionality. - var/datum/action/innate/dash/hierophant/blink - /// Whether the blink ability is activated. IF TRUE, left clicking a location will blink to it. If FALSE, this is disabled. - var/blink_activated = TRUE - /// Whether the blink is charged. Set and unset by the blink action. Used as part of setting the appropriate icon states. - var/blink_charged = TRUE - -/obj/item/hierophant_club/Initialize() - . = ..() - blink = new(src) - -/obj/item/hierophant_club/Destroy() - . = ..() - QDEL_NULL(blink) - -/obj/item/hierophant_club/ComponentInitialize() - . = ..() - AddElement(/datum/element/update_icon_updates_onmob) - -/obj/item/hierophant_club/examine(mob/user) - . = ..() - . += span_hierophant_warning("The[beacon ? " beacon is not currently":"re is a beacon"] attached.") - -/obj/item/hierophant_club/suicide_act(mob/living/user) - say("Xverwpsgexmrk...", forced = "hierophant club suicide") - user.visible_message(span_suicide("[user] holds [src] into the air! It looks like [user.p_theyre()] trying to commit suicide!")) - new/obj/effect/temp_visual/hierophant/telegraph(get_turf(user)) - playsound(user,'sound/machines/airlockopen.ogg', 75, TRUE) - user.visible_message(span_hierophant_warning("[user] fades out, leaving [user.p_their()] belongings behind!")) - for(var/obj/item/I in user) - if(I != src) - user.dropItemToGround(I) - for(var/turf/T in RANGE_TURFS(1, user)) - new /obj/effect/temp_visual/hierophant/blast/visual(T, user, TRUE) - user.dropItemToGround(src) //Drop us last, so it goes on top of their stuff - qdel(user) - -/obj/item/hierophant_club/attack_self(mob/user) - blink_activated = !blink_activated - to_chat(user, span_notice("You [blink_activated ? "enable" : "disable"] the blink function on [src].")) - -/obj/item/hierophant_club/afterattack(atom/target, mob/user, proximity_flag, click_parameters) - . = ..() - // If our target is the beacon and the hierostaff is next to the beacon, we're trying to pick it up. - if((target == beacon) && target.Adjacent(src)) - return - if(blink_activated) - blink.Teleport(user, target) - -/obj/item/hierophant_club/update_icon_state() - icon_state = inhand_icon_state = "hierophant_club[blink_charged ? "_ready":""][(!QDELETED(beacon)) ? "":"_beacon"]" - return ..() - -/obj/item/hierophant_club/ui_action_click(mob/user, action) - if(!user.is_holding(src)) //you need to hold the staff to teleport - to_chat(user, span_warning("You need to hold the club in your hands to [beacon ? "teleport with it":"detach the beacon"]!")) - return - if(!beacon || QDELETED(beacon)) - if(isturf(user.loc)) - user.visible_message(span_hierophant_warning("[user] starts fiddling with [src]'s pommel..."), \ - span_notice("You start detaching the hierophant beacon...")) - if(do_after(user, 50, target = user) && !beacon) - var/turf/T = get_turf(user) - playsound(T,'sound/magic/blind.ogg', 200, TRUE, -4) - new /obj/effect/temp_visual/hierophant/telegraph/teleport(T, user) - beacon = new/obj/effect/hierophant(T) - user.update_action_buttons_icon() - user.visible_message(span_hierophant_warning("[user] places a strange machine beneath [user.p_their()] feet!"), \ - "[span_hierophant("You detach the hierophant beacon, allowing you to teleport yourself and any allies to it at any time!")]\n\ - [span_notice("You can remove the beacon to place it again by striking it with the club.")]") - else - to_chat(user, span_warning("You need to be on solid ground to detach the beacon!")) - return - if(get_dist(user, beacon) <= 2) //beacon too close abort - to_chat(user, span_warning("You are too close to the beacon to teleport to it!")) - return - var/turf/beacon_turf = get_turf(beacon) - if(beacon_turf?.is_blocked_turf(TRUE)) - to_chat(user, span_warning("The beacon is blocked by something, preventing teleportation!")) - return - if(!isturf(user.loc)) - to_chat(user, span_warning("You don't have enough space to teleport from here!")) - return - teleporting = TRUE //start channel - user.update_action_buttons_icon() - user.visible_message(span_hierophant_warning("[user] starts to glow faintly...")) - beacon.icon_state = "hierophant_tele_on" - var/obj/effect/temp_visual/hierophant/telegraph/edge/TE1 = new /obj/effect/temp_visual/hierophant/telegraph/edge(user.loc) - var/obj/effect/temp_visual/hierophant/telegraph/edge/TE2 = new /obj/effect/temp_visual/hierophant/telegraph/edge(beacon.loc) - if(do_after(user, 40, target = user) && user && beacon) - var/turf/T = get_turf(beacon) - var/turf/source = get_turf(user) - if(T.is_blocked_turf(TRUE)) - teleporting = FALSE - to_chat(user, span_warning("The beacon is blocked by something, preventing teleportation!")) - user.update_action_buttons_icon() - beacon.icon_state = "hierophant_tele_off" - return - new /obj/effect/temp_visual/hierophant/telegraph(T, user) - new /obj/effect/temp_visual/hierophant/telegraph(source, user) - playsound(T,'sound/magic/wand_teleport.ogg', 200, TRUE) - playsound(source,'sound/machines/airlockopen.ogg', 200, TRUE) - if(!do_after(user, 3, target = user) || !user || !beacon || QDELETED(beacon)) //no walking away shitlord - teleporting = FALSE - if(user) - user.update_action_buttons_icon() - if(beacon) - beacon.icon_state = "hierophant_tele_off" - return - if(T.is_blocked_turf(TRUE)) - teleporting = FALSE - to_chat(user, span_warning("The beacon is blocked by something, preventing teleportation!")) - user.update_action_buttons_icon() - beacon.icon_state = "hierophant_tele_off" - return - user.log_message("teleported self from [AREACOORD(source)] to [beacon]", LOG_GAME) - new /obj/effect/temp_visual/hierophant/telegraph/teleport(T, user) - new /obj/effect/temp_visual/hierophant/telegraph/teleport(source, user) - for(var/t in RANGE_TURFS(1, T)) - new /obj/effect/temp_visual/hierophant/blast/visual(t, user, TRUE) - for(var/t in RANGE_TURFS(1, source)) - new /obj/effect/temp_visual/hierophant/blast/visual(t, user, TRUE) - for(var/mob/living/L in range(1, source)) - INVOKE_ASYNC(src, .proc/teleport_mob, source, L, T, user) - sleep(6) //at this point the blasts detonate - if(beacon) - beacon.icon_state = "hierophant_tele_off" - else - qdel(TE1) - qdel(TE2) - if(beacon) - beacon.icon_state = "hierophant_tele_off" - teleporting = FALSE - if(user) - user.update_action_buttons_icon() - -/obj/item/hierophant_club/proc/teleport_mob(turf/source, mob/M, turf/target, mob/user) - var/turf/turf_to_teleport_to = get_step(target, get_dir(source, M)) //get position relative to caster - if(!turf_to_teleport_to || turf_to_teleport_to.is_blocked_turf(TRUE)) - return - animate(M, alpha = 0, time = 2, easing = EASE_OUT) //fade out - sleep(1) - if(!M) - return - M.visible_message(span_hierophant_warning("[M] fades out!")) - sleep(2) - if(!M) - return - var/success = do_teleport(M, turf_to_teleport_to, no_effects = TRUE, channel = TELEPORT_CHANNEL_MAGIC) - sleep(1) - if(!M) - return - animate(M, alpha = 255, time = 2, easing = EASE_IN) //fade IN - sleep(1) - if(!M) - return - M.visible_message(span_hierophant_warning("[M] fades in!")) - if(user != M && success) - log_combat(user, M, "teleported", null, "from [AREACOORD(source)]") - -/obj/item/hierophant_club/pickup(mob/living/user) - . = ..() - blink.Grant(user, src) - user.update_icons() - -/obj/item/hierophant_club/dropped(mob/user) - . = ..() - blink.Remove(user) - user.update_icons() - -//Just some minor stuff /obj/structure/closet/crate/necropolis/puzzle name = "puzzling chest" @@ -1348,7 +156,7 @@ var/loot = rand(1,3) switch(loot) if(1) - new /obj/item/soulstone/anybody(src) + new /obj/item/soulstone/anybody/mining(src) if(2) new /obj/item/wisp_lantern(src) if(3) @@ -1360,6 +168,3 @@ icon = 'icons/obj/lavaland/artefacts.dmi' icon_state = "skeleton_key" w_class = WEIGHT_CLASS_SMALL - -#undef HIEROPHANT_BLINK_RANGE -#undef HIEROPHANT_BLINK_COOLDOWN diff --git a/code/modules/mining/lavaland/tendril_loot.dm b/code/modules/mining/lavaland/tendril_loot.dm new file mode 100644 index 00000000000..cdcca6004aa --- /dev/null +++ b/code/modules/mining/lavaland/tendril_loot.dm @@ -0,0 +1,1070 @@ +//This file contains loot you can obtain from tendril chests. + +//KA modkit design discs +/obj/item/disk/design_disk/modkit_disc + name = "KA Mod Disk" + desc = "A design disc containing the design for a unique kinetic accelerator modkit. It's compatible with a research console." + icon_state = "datadisk1" + var/modkit_design = /datum/design/unique_modkit + +/obj/item/disk/design_disk/modkit_disc/Initialize() + . = ..() + blueprints[1] = new modkit_design + +/obj/item/disk/design_disk/modkit_disc/mob_and_turf_aoe + name = "Offensive Mining Explosion Mod Disk" + modkit_design = /datum/design/unique_modkit/offensive_turf_aoe + +/obj/item/disk/design_disk/modkit_disc/rapid_repeater + name = "Rapid Repeater Mod Disk" + modkit_design = /datum/design/unique_modkit/rapid_repeater + +/obj/item/disk/design_disk/modkit_disc/resonator_blast + name = "Resonator Blast Mod Disk" + modkit_design = /datum/design/unique_modkit/resonator_blast + +/obj/item/disk/design_disk/modkit_disc/bounty + name = "Death Syphon Mod Disk" + modkit_design = /datum/design/unique_modkit/bounty + +/datum/design/unique_modkit + category = list("Mining Designs", "Cyborg Upgrade Modules") //can't be normally obtained + build_type = PROTOLATHE | AWAY_LATHE | MECHFAB + departmental_flags = DEPARTMENTAL_FLAG_CARGO + +/datum/design/unique_modkit/offensive_turf_aoe + name = "Kinetic Accelerator Offensive Mining Explosion Mod" + desc = "A device which causes kinetic accelerators to fire AoE blasts that destroy rock and damage creatures." + id = "hyperaoemod" + materials = list(/datum/material/iron = 7000, /datum/material/glass = 3000, /datum/material/silver = 3000, /datum/material/gold = 3000, /datum/material/diamond = 4000) + build_path = /obj/item/borg/upgrade/modkit/aoe/turfs/andmobs + +/datum/design/unique_modkit/rapid_repeater + name = "Kinetic Accelerator Rapid Repeater Mod" + desc = "A device which greatly reduces a kinetic accelerator's cooldown on striking a living target or rock, but greatly increases its base cooldown." + id = "repeatermod" + materials = list(/datum/material/iron = 5000, /datum/material/glass = 5000, /datum/material/uranium = 8000, /datum/material/bluespace = 2000) + build_path = /obj/item/borg/upgrade/modkit/cooldown/repeater + +/datum/design/unique_modkit/resonator_blast + name = "Kinetic Accelerator Resonator Blast Mod" + desc = "A device which causes kinetic accelerators to fire shots that leave and detonate resonator blasts." + id = "resonatormod" + materials = list(/datum/material/iron = 5000, /datum/material/glass = 5000, /datum/material/silver = 5000, /datum/material/uranium = 5000) + build_path = /obj/item/borg/upgrade/modkit/resonator_blasts + +/datum/design/unique_modkit/bounty + name = "Kinetic Accelerator Death Syphon Mod" + desc = "A device which causes kinetic accelerators to permanently gain damage against creature types killed with it." + id = "bountymod" + materials = list(/datum/material/iron = 4000, /datum/material/silver = 4000, /datum/material/gold = 4000, /datum/material/bluespace = 4000) + reagents_list = list(/datum/reagent/blood = 40) + build_path = /obj/item/borg/upgrade/modkit/bounty + +//Spooky special loot + +//Rod of Asclepius +/obj/item/rod_of_asclepius + name = "\improper Rod of Asclepius" + desc = "A wooden rod about the size of your forearm with a snake carved around it, winding its way up the sides of the rod. Something about it seems to inspire in you the responsibilty and duty to help others." + icon = 'icons/obj/lavaland/artefacts.dmi' + lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi' + icon_state = "asclepius_dormant" + var/activated = FALSE + var/usedHand + +/obj/item/rod_of_asclepius/attack_self(mob/user) + if(activated) + return + if(!iscarbon(user)) + to_chat(user, span_warning("The snake carving seems to come alive, if only for a moment, before returning to its dormant state, almost as if it finds you incapable of holding its oath.")) + return + var/mob/living/carbon/itemUser = user + usedHand = itemUser.get_held_index_of_item(src) + if(itemUser.has_status_effect(STATUS_EFFECT_HIPPOCRATIC_OATH)) + to_chat(user, span_warning("You can't possibly handle the responsibility of more than one rod!")) + return + var/failText = span_warning("The snake seems unsatisfied with your incomplete oath and returns to its previous place on the rod, returning to its dormant, wooden state. You must stand still while completing your oath!") + to_chat(itemUser, span_notice("The wooden snake that was carved into the rod seems to suddenly come alive and begins to slither down your arm! The compulsion to help others grows abnormally strong...")) + if(do_after(itemUser, 40, target = itemUser)) + itemUser.say("I swear to fulfill, to the best of my ability and judgment, this covenant:", forced = "hippocratic oath") + else + to_chat(itemUser, failText) + return + if(do_after(itemUser, 20, target = itemUser)) + itemUser.say("I will apply, for the benefit of the sick, all measures that are required, avoiding those twin traps of overtreatment and therapeutic nihilism.", forced = "hippocratic oath") + else + to_chat(itemUser, failText) + return + if(do_after(itemUser, 30, target = itemUser)) + itemUser.say("I will remember that I remain a member of society, with special obligations to all my fellow human beings, those sound of mind and body as well as the infirm.", forced = "hippocratic oath") + else + to_chat(itemUser, failText) + return + if(do_after(itemUser, 30, target = itemUser)) + itemUser.say("If I do not violate this oath, may I enjoy life and art, respected while I live and remembered with affection thereafter. May I always act so as to preserve the finest traditions of my calling and may I long experience the joy of healing those who seek my help.", forced = "hippocratic oath") + else + to_chat(itemUser, failText) + return + to_chat(itemUser, span_notice("The snake, satisfied with your oath, attaches itself and the rod to your forearm with an inseparable grip. Your thoughts seem to only revolve around the core idea of helping others, and harm is nothing more than a distant, wicked memory...")) + var/datum/status_effect/hippocratic_oath/effect = itemUser.apply_status_effect(STATUS_EFFECT_HIPPOCRATIC_OATH) + effect.hand = usedHand + activated() + +/obj/item/rod_of_asclepius/proc/activated() + item_flags = DROPDEL + ADD_TRAIT(src, TRAIT_NODROP, CURSED_ITEM_TRAIT(type)) + desc = "A short wooden rod with a mystical snake inseparably gripping itself and the rod to your forearm. It flows with a healing energy that disperses amongst yourself and those around you. " + icon_state = "asclepius_active" + activated = TRUE + +//Memento Mori +/obj/item/clothing/neck/necklace/memento_mori + name = "Memento Mori" + desc = "A mysterious pendant. An inscription on it says: \"Certain death tomorrow means certain life today.\"" + icon = 'icons/obj/lavaland/artefacts.dmi' + icon_state = "memento_mori" + worn_icon_state = "memento" + actions_types = list(/datum/action/item_action/hands_free/memento_mori) + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + var/mob/living/carbon/human/active_owner + +/obj/item/clothing/neck/necklace/memento_mori/item_action_slot_check(slot) + return slot == ITEM_SLOT_NECK + +/obj/item/clothing/neck/necklace/memento_mori/dropped(mob/user) + ..() + if(active_owner) + mori() + +//Just in case +/obj/item/clothing/neck/necklace/memento_mori/Destroy() + if(active_owner) + mori() + return ..() + +/obj/item/clothing/neck/necklace/memento_mori/proc/memento(mob/living/carbon/human/user) + to_chat(user, span_warning("You feel your life being drained by the pendant...")) + if(do_after(user, 40, target = user)) + to_chat(user, span_notice("Your lifeforce is now linked to the pendant! You feel like removing it would kill you, and yet you instinctively know that until then, you won't die.")) + ADD_TRAIT(user, TRAIT_NODEATH, CLOTHING_TRAIT) + ADD_TRAIT(user, TRAIT_NOHARDCRIT, CLOTHING_TRAIT) + ADD_TRAIT(user, TRAIT_NOCRITDAMAGE, CLOTHING_TRAIT) + RegisterSignal(user, COMSIG_CARBON_HEALTH_UPDATE, .proc/check_health) + icon_state = "memento_mori_active" + active_owner = user + +/obj/item/clothing/neck/necklace/memento_mori/proc/mori() + icon_state = "memento_mori" + if(!active_owner) + return + UnregisterSignal(active_owner, COMSIG_CARBON_HEALTH_UPDATE) + var/mob/living/carbon/human/H = active_owner //to avoid infinite looping when dust unequips the pendant + active_owner = null + to_chat(H, span_userdanger("You feel your life rapidly slipping away from you!")) + H.dust(TRUE, TRUE) + +/obj/item/clothing/neck/necklace/memento_mori/proc/check_health(mob/living/source) + SIGNAL_HANDLER + + var/list/guardians = source.hasparasites() + if(!length(guardians)) + return + if(source.health <= HEALTH_THRESHOLD_DEAD) + for(var/mob/guardian in guardians) + if(guardian.loc == src) + continue + consume_guardian(guardian) + else if(source.health > HEALTH_THRESHOLD_CRIT) + for(var/mob/guardian in guardians) + if(guardian.loc != src) + continue + regurgitate_guardian(guardian) + +/obj/item/clothing/neck/necklace/memento_mori/proc/consume_guardian(mob/living/simple_animal/hostile/guardian/guardian) + new /obj/effect/temp_visual/guardian/phase/out(get_turf(guardian)) + guardian.locked = TRUE + guardian.forceMove(src) + to_chat(guardian, span_userdanger("You have been locked away in your summoner's pendant!")) + guardian.playsound_local(get_turf(guardian), 'sound/magic/summonitems_generic.ogg', 50, TRUE) + +/obj/item/clothing/neck/necklace/memento_mori/proc/regurgitate_guardian(mob/living/simple_animal/hostile/guardian/guardian) + guardian.locked = FALSE + guardian.Recall(TRUE) + to_chat(guardian, span_notice("You have been returned back from your summoner's pendant!")) + guardian.playsound_local(get_turf(guardian), 'sound/magic/repulse.ogg', 50, TRUE) + +/datum/action/item_action/hands_free/memento_mori + check_flags = NONE + name = "Memento Mori" + desc = "Bind your life to the pendant." + +/datum/action/item_action/hands_free/memento_mori/Trigger() + var/obj/item/clothing/neck/necklace/memento_mori/MM = target + if(!MM.active_owner) + if(ishuman(owner)) + MM.memento(owner) + Remove(MM.active_owner) //Remove the action button, since there's no real use in having it now. + +//Wisp Lantern +/obj/item/wisp_lantern + name = "spooky lantern" + desc = "This lantern gives off no light, but is home to a friendly wisp." + icon = 'icons/obj/lighting.dmi' + icon_state = "lantern-blue" + inhand_icon_state = "lantern" + lefthand_file = 'icons/mob/inhands/equipment/mining_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/mining_righthand.dmi' + var/obj/effect/wisp/wisp + +/obj/item/wisp_lantern/attack_self(mob/user) + if(!wisp) + to_chat(user, span_warning("The wisp has gone missing!")) + icon_state = "lantern" + return + + if(wisp.loc == src) + to_chat(user, span_notice("You release the wisp. It begins to bob around your head.")) + icon_state = "lantern" + wisp.orbit(user, 20) + SSblackbox.record_feedback("tally", "wisp_lantern", 1, "Freed") + + else + to_chat(user, span_notice("You return the wisp to the lantern.")) + icon_state = "lantern-blue" + wisp.forceMove(src) + SSblackbox.record_feedback("tally", "wisp_lantern", 1, "Returned") + +/obj/item/wisp_lantern/Initialize() + . = ..() + wisp = new(src) + +/obj/item/wisp_lantern/Destroy() + if(wisp) + if(wisp.loc == src) + qdel(wisp) + else + wisp.visible_message(span_notice("[wisp] has a sad feeling for a moment, then it passes.")) + return ..() + +/obj/effect/wisp + name = "friendly wisp" + desc = "Happy to light your way." + icon = 'icons/obj/lighting.dmi' + icon_state = "orb" + light_system = MOVABLE_LIGHT + light_range = 7 + light_flags = LIGHT_ATTACHED + layer = ABOVE_ALL_MOB_LAYER + var/sight_flags = SEE_MOBS + var/lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + +/obj/effect/wisp/orbit(atom/thing, radius, clockwise, rotation_speed, rotation_segments, pre_rotation, lockinorbit) + . = ..() + if(ismob(thing)) + RegisterSignal(thing, COMSIG_MOB_UPDATE_SIGHT, .proc/update_user_sight) + var/mob/being = thing + being.update_sight() + to_chat(thing, span_notice("The wisp enhances your vision.")) + +/obj/effect/wisp/stop_orbit(datum/component/orbiter/orbits) + . = ..() + if(ismob(orbits.parent)) + UnregisterSignal(orbits.parent, COMSIG_MOB_UPDATE_SIGHT) + to_chat(orbits.parent, span_notice("Your vision returns to normal.")) + +/obj/effect/wisp/proc/update_user_sight(mob/user) + SIGNAL_HANDLER + user.sight |= sight_flags + if(!isnull(lighting_alpha)) + user.lighting_alpha = min(user.lighting_alpha, lighting_alpha) + +//Red/Blue Cubes +/obj/item/warp_cube + name = "blue cube" + desc = "A mysterious blue cube." + icon = 'icons/obj/lavaland/artefacts.dmi' + icon_state = "blue_cube" + var/teleport_color = "#3FBAFD" + var/obj/item/warp_cube/linked + var/teleporting = FALSE + +/obj/item/warp_cube/Destroy() + if(!QDELETED(linked)) + qdel(linked) + linked = null + return ..() + +/obj/item/warp_cube/attack_self(mob/user) + var/turf/current_location = get_turf(user) + var/area/current_area = current_location.loc + if(!linked || (current_area.area_flags & NOTELEPORT)) + to_chat(user, span_warning("[src] fizzles uselessly.")) + return + if(teleporting) + return + teleporting = TRUE + linked.teleporting = TRUE + var/turf/T = get_turf(src) + new /obj/effect/temp_visual/warp_cube(T, user, teleport_color, TRUE) + SSblackbox.record_feedback("tally", "warp_cube", 1, type) + new /obj/effect/temp_visual/warp_cube(get_turf(linked), user, linked.teleport_color, FALSE) + var/obj/effect/warp_cube/link_holder = new /obj/effect/warp_cube(T) + user.forceMove(link_holder) //mess around with loc so the user can't wander around + sleep(2.5) + if(QDELETED(user)) + qdel(link_holder) + return + if(QDELETED(linked)) + user.forceMove(get_turf(link_holder)) + qdel(link_holder) + return + link_holder.forceMove(get_turf(linked)) + sleep(2.5) + if(QDELETED(user)) + qdel(link_holder) + return + teleporting = FALSE + if(!QDELETED(linked)) + linked.teleporting = FALSE + user.forceMove(get_turf(link_holder)) + qdel(link_holder) + +/obj/item/warp_cube/red + name = "red cube" + desc = "A mysterious red cube." + icon_state = "red_cube" + teleport_color = "#FD3F48" + +/obj/item/warp_cube/red/Initialize() + . = ..() + if(!linked) + var/obj/item/warp_cube/blue = new(src.loc) + linked = blue + blue.linked = src + +/obj/effect/warp_cube + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + anchored = TRUE + +//Immortality Talisman +/obj/item/immortality_talisman + name = "\improper Immortality Talisman" + desc = "A dread talisman that can render you completely invulnerable." + icon = 'icons/obj/lavaland/artefacts.dmi' + icon_state = "talisman" + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + actions_types = list(/datum/action/item_action/immortality) + var/cooldown = 0 + +/obj/item/immortality_talisman/Initialize() + . = ..() + AddComponent(/datum/component/anti_magic, TRUE, TRUE, TRUE) + +/datum/action/item_action/immortality + name = "Immortality" + +/obj/item/immortality_talisman/attack_self(mob/user) + if(cooldown < world.time) + SSblackbox.record_feedback("amount", "immortality_talisman_uses", 1) + cooldown = world.time + 600 + new /obj/effect/immortality_talisman(get_turf(user), user) + else + to_chat(user, span_warning("[src] is not ready yet!")) + +/obj/effect/immortality_talisman + name = "hole in reality" + desc = "It's shaped an awful lot like a person." + icon_state = "blank" + icon = 'icons/effects/effects.dmi' + var/vanish_description = "vanishes from reality" + var/can_destroy = TRUE + +/obj/effect/immortality_talisman/Initialize(mapload, mob/new_user) + . = ..() + if(new_user) + vanish(new_user) + +/obj/effect/immortality_talisman/proc/vanish(mob/user) + user.visible_message(span_danger("[user] [vanish_description], leaving a hole in [user.p_their()] place!")) + + desc = "It's shaped an awful lot like [user.name]." + setDir(user.dir) + + user.forceMove(src) + user.notransform = TRUE + user.status_flags |= GODMODE + + can_destroy = FALSE + + addtimer(CALLBACK(src, .proc/unvanish, user), 10 SECONDS) + +/obj/effect/immortality_talisman/proc/unvanish(mob/user) + user.status_flags &= ~GODMODE + user.notransform = FALSE + user.forceMove(get_turf(src)) + + user.visible_message(span_danger("[user] pops back into reality!")) + can_destroy = TRUE + qdel(src) + +/obj/effect/immortality_talisman/attackby() + return + +/obj/effect/immortality_talisman/singularity_pull() + return + +/obj/effect/immortality_talisman/Destroy(force) + if(!can_destroy && !force) + return QDEL_HINT_LETMELIVE + else + . = ..() + +/obj/effect/immortality_talisman/void + vanish_description = "is dragged into the void" + +//Shared Bag + +/obj/item/shared_storage + name = "paradox bag" + desc = "Somehow, it's in two places at once." + icon = 'icons/obj/lavaland/artefacts.dmi' + icon_state = "paradox_bag" + worn_icon_state = "paradoxbag" + slot_flags = ITEM_SLOT_BELT + resistance_flags = INDESTRUCTIBLE + +/obj/item/shared_storage/red + name = "paradox bag" + desc = "Somehow, it's in two places at once." + +/obj/item/shared_storage/red/Initialize() + . = ..() + var/datum/component/storage/STR = AddComponent(/datum/component/storage/concrete) + STR.max_w_class = WEIGHT_CLASS_NORMAL + STR.max_combined_w_class = 15 + STR.max_items = 21 + new /obj/item/shared_storage/blue(drop_location(), STR) + +/obj/item/shared_storage/blue/Initialize(mapload, datum/component/storage/concrete/master) + . = ..() + if(!istype(master)) + return INITIALIZE_HINT_QDEL + var/datum/component/storage/STR = AddComponent(/datum/component/storage, master) + STR.max_w_class = WEIGHT_CLASS_NORMAL + STR.max_combined_w_class = 15 + STR.max_items = 21 + +//Book of Babel + +/obj/item/book_of_babel + name = "Book of Babel" + desc = "An ancient tome written in countless tongues." + icon = 'icons/obj/library.dmi' + icon_state = "book1" + w_class = WEIGHT_CLASS_SMALL + +/obj/item/book_of_babel/attack_self(mob/user) + if(!user.can_read(src)) + return FALSE + to_chat(user, span_notice("You flip through the pages of the book, quickly and conveniently learning every language in existence. Somewhat less conveniently, the aging book crumbles to dust in the process. Whoops.")) + user.grant_all_languages() + new /obj/effect/decal/cleanable/ash(get_turf(user)) + qdel(src) + + +//Potion of Flight +/obj/item/reagent_containers/glass/bottle/potion + icon = 'icons/obj/lavaland/artefacts.dmi' + icon_state = "potionflask" + +/obj/item/reagent_containers/glass/bottle/potion/flight + name = "strange elixir" + desc = "A flask with an almost-holy aura emitting from it. The label on the bottle says: 'erqo'hyy tvi'rf lbh jv'atf'." + list_reagents = list(/datum/reagent/flightpotion = 5) + +/obj/item/reagent_containers/glass/bottle/potion/update_icon_state() + icon_state = "potionflask[reagents.total_volume ? null : "_empty"]" + return ..() + +/datum/reagent/flightpotion + name = "Flight Potion" + description = "Strange mutagenic compound of unknown origins." + reagent_state = LIQUID + color = "#FFEBEB" + +/datum/reagent/flightpotion/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message = TRUE) + . = ..() + if(iscarbon(exposed_mob) && exposed_mob.stat != DEAD) + var/mob/living/carbon/exposed_carbon = exposed_mob + var/holycheck = ishumanbasic(exposed_carbon) + if(!HAS_TRAIT(exposed_carbon, TRAIT_CAN_USE_FLIGHT_POTION) || reac_volume < 5) + if((methods & INGEST) && show_message) + to_chat(exposed_carbon, span_notice("You feel nothing but a terrible aftertaste.")) + return + if(exposed_carbon.dna.species.has_innate_wings) + to_chat(exposed_carbon, span_userdanger("A terrible pain travels down your back as your wings change shape!")) + else + to_chat(exposed_carbon, span_userdanger("A terrible pain travels down your back as wings burst out!")) + exposed_carbon.dna.species.GiveSpeciesFlight(exposed_carbon) + if(holycheck) + to_chat(exposed_carbon, span_notice("You feel blessed!")) + ADD_TRAIT(exposed_carbon, TRAIT_HOLY, SPECIES_TRAIT) + playsound(exposed_carbon.loc, 'sound/items/poster_ripped.ogg', 50, TRUE, -1) + exposed_carbon.adjustBruteLoss(20) + exposed_carbon.emote("scream") + + +/obj/item/jacobs_ladder + name = "jacob's ladder" + desc = "A celestial ladder that violates the laws of physics." + icon = 'icons/obj/structures.dmi' + icon_state = "ladder00" + +/obj/item/jacobs_ladder/attack_self(mob/user) + var/turf/T = get_turf(src) + var/ladder_x = T.x + var/ladder_y = T.y + to_chat(user, span_notice("You unfold the ladder. It extends much farther than you were expecting.")) + var/last_ladder = null + for(var/i in 1 to world.maxz) + if(is_centcom_level(i) || is_reserved_level(i) || is_away_level(i)) + continue + var/turf/T2 = locate(ladder_x, ladder_y, i) + last_ladder = new /obj/structure/ladder/unbreakable/jacob(T2, null, last_ladder) + qdel(src) + +// Inherit from unbreakable but don't set ID, to suppress the default Z linkage +/obj/structure/ladder/unbreakable/jacob + name = "jacob's ladder" + desc = "An indestructible celestial ladder that violates the laws of physics." + +//Concussive Gauntlets +/obj/item/clothing/gloves/gauntlets + name = "concussive gauntlets" + desc = "Pickaxes... for your hands!" + icon_state = "concussive_gauntlets" + inhand_icon_state = "concussive_gauntlets" + toolspeed = 0.1 + strip_delay = 40 + equip_delay_other = 20 + cold_protection = HANDS + min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT + heat_protection = HANDS + max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT + resistance_flags = LAVA_PROOF | FIRE_PROOF //they are from lavaland after all + armor = list(MELEE = 15, BULLET = 25, LASER = 15, ENERGY = 15, BOMB = 100, BIO = 0, RAD = 0, FIRE = 100, ACID = 30) //mostly bone bracer armor + +/obj/item/clothing/gloves/gauntlets/equipped(mob/user, slot) + . = ..() + if(slot == ITEM_SLOT_GLOVES) + tool_behaviour = TOOL_MINING + RegisterSignal(user, COMSIG_HUMAN_EARLY_UNARMED_ATTACK, .proc/rocksmash) + RegisterSignal(user, COMSIG_MOVABLE_BUMP, .proc/rocksmash) + else + stopmining(user) + +/obj/item/clothing/gloves/gauntlets/dropped(mob/user) + . = ..() + stopmining(user) + +/obj/item/clothing/gloves/gauntlets/proc/stopmining(mob/user) + tool_behaviour = initial(tool_behaviour) + UnregisterSignal(user, COMSIG_HUMAN_EARLY_UNARMED_ATTACK) + UnregisterSignal(user, COMSIG_MOVABLE_BUMP) + +/obj/item/clothing/gloves/gauntlets/proc/rocksmash(mob/living/carbon/human/H, atom/A, proximity) + SIGNAL_HANDLER + if(!istype(A, /turf/closed/mineral)) + return + A.attackby(src, H) + return COMPONENT_CANCEL_ATTACK_CHAIN + +/obj/item/clothing/suit/space/hardsuit/berserker + name = "berserker hardsuit" + desc = "Voices echo from the hardsuit, driving the user insane." + icon_state = "hardsuit-berserker" + inhand_icon_state = "hardsuit-berserker" + slowdown = 0 + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/berserker + armor = list(MELEE = 30, BULLET = 30, LASER = 10, ENERGY = 20, BOMB = 50, BIO = 100, RAD = 10, FIRE = 100, ACID = 100) + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF + allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/pickaxe, /obj/item/spear, /obj/item/organ/regenerative_core/legion, /obj/item/kitchen/knife, /obj/item/kinetic_crusher, /obj/item/resonator, /obj/item/melee/transforming/cleaving_saw) + + +/obj/item/clothing/suit/space/hardsuit/berserker/Initialize() + . = ..() + AddComponent(/datum/component/anti_magic, TRUE, TRUE, TRUE, ITEM_SLOT_OCLOTHING) + +/obj/item/clothing/suit/space/hardsuit/berserker/RemoveHelmet() + var/obj/item/clothing/head/helmet/space/hardsuit/berserker/helm = helmet + if(helm?.berserk_active) + return + return ..() + +#define MAX_BERSERK_CHARGE 100 +#define PROJECTILE_HIT_MULTIPLIER 1.5 +#define DAMAGE_TO_CHARGE_SCALE 0.75 +#define CHARGE_DRAINED_PER_SECOND 5 +#define BERSERK_MELEE_ARMOR_ADDED 50 +#define BERSERK_ATTACK_SPEED_MODIFIER 0.25 + +/obj/item/clothing/head/helmet/space/hardsuit/berserker + name = "berserker helmet" + desc = "Peering into the eyes of the helmet is enough to seal damnation." + icon_state = "hardsuit0-berserker" + inhand_icon_state = "hardsuit0-berserker" + hardsuit_type = "berserker" + armor = list(MELEE = 30, BULLET = 30, LASER = 10, ENERGY = 20, BOMB = 50, BIO = 100, RAD = 10, FIRE = 100, ACID = 100) + actions_types = list(/datum/action/item_action/berserk_mode) + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF + /// Current charge of berserk, goes from 0 to 100 + var/berserk_charge = 0 + /// Status of berserk + var/berserk_active = FALSE + +/obj/item/clothing/head/helmet/space/hardsuit/berserker/Initialize() + . = ..() + ADD_TRAIT(src, TRAIT_NODROP, LOCKED_HELMET_TRAIT) + +/obj/item/clothing/head/helmet/space/hardsuit/berserker/examine() + . = ..() + . += span_notice("Berserk mode is [berserk_charge]% charged.") + +/obj/item/clothing/head/helmet/space/hardsuit/berserker/process(delta_time) + . = ..() + if(berserk_active) + berserk_charge = clamp(berserk_charge - CHARGE_DRAINED_PER_SECOND * delta_time, 0, MAX_BERSERK_CHARGE) + if(!berserk_charge) + if(ishuman(loc)) + end_berserk(loc) + +/obj/item/clothing/head/helmet/space/hardsuit/berserker/dropped(mob/user) + . = ..() + end_berserk(user) + +/obj/item/clothing/head/helmet/space/hardsuit/berserker/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + if(berserk_active) + return + var/berserk_value = damage * DAMAGE_TO_CHARGE_SCALE + if(attack_type == PROJECTILE_ATTACK) + berserk_value *= PROJECTILE_HIT_MULTIPLIER + berserk_charge = clamp(round(berserk_charge + berserk_value), 0, MAX_BERSERK_CHARGE) + if(berserk_charge >= MAX_BERSERK_CHARGE) + to_chat(owner, span_notice("Berserk mode is fully charged.")) + balloon_alert(owner, "berserk charged") + +/obj/item/clothing/head/helmet/space/hardsuit/berserker/IsReflect() + if(berserk_active) + return TRUE + +/// Starts berserk, giving the wearer 50 melee armor, doubled attacking speed, NOGUNS trait, adding a color and giving them the berserk movespeed modifier +/obj/item/clothing/head/helmet/space/hardsuit/berserker/proc/berserk_mode(mob/living/carbon/human/user) + to_chat(user, span_warning("You enter berserk mode.")) + playsound(user, 'sound/magic/staff_healing.ogg', 50) + user.add_movespeed_modifier(/datum/movespeed_modifier/berserk) + user.physiology.armor.melee += BERSERK_MELEE_ARMOR_ADDED + user.next_move_modifier *= BERSERK_ATTACK_SPEED_MODIFIER + user.add_atom_colour(COLOR_BUBBLEGUM_RED, TEMPORARY_COLOUR_PRIORITY) + ADD_TRAIT(user, TRAIT_NOGUNS, BERSERK_TRAIT) + ADD_TRAIT(src, TRAIT_NODROP, BERSERK_TRAIT) + berserk_active = TRUE + +/// Ends berserk, reverting the changes from the proc [berserk_mode] +/obj/item/clothing/head/helmet/space/hardsuit/berserker/proc/end_berserk(mob/living/carbon/human/user) + if(!berserk_active) + return + to_chat(user, span_warning("You exit berserk mode.")) + playsound(user, 'sound/magic/summonitems_generic.ogg', 50) + user.remove_movespeed_modifier(/datum/movespeed_modifier/berserk) + user.physiology.armor.melee -= BERSERK_MELEE_ARMOR_ADDED + user.next_move_modifier /= BERSERK_ATTACK_SPEED_MODIFIER + user.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, COLOR_BUBBLEGUM_RED) + REMOVE_TRAIT(user, TRAIT_NOGUNS, BERSERK_TRAIT) + REMOVE_TRAIT(src, TRAIT_NODROP, BERSERK_TRAIT) + berserk_active = FALSE + +#undef MAX_BERSERK_CHARGE +#undef PROJECTILE_HIT_MULTIPLIER +#undef DAMAGE_TO_CHARGE_SCALE +#undef CHARGE_DRAINED_PER_SECOND +#undef BERSERK_MELEE_ARMOR_ADDED +#undef BERSERK_ATTACK_SPEED_MODIFIER + +/obj/item/clothing/glasses/godeye + name = "eye of god" + desc = "A strange eye, said to have been torn from an omniscient creature that used to roam the wastes." + icon_state = "godeye" + inhand_icon_state = "godeye" + vision_flags = SEE_TURFS + darkness_view = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF + custom_materials = null + var/obj/effect/proc_holder/scan/scan + +/obj/item/clothing/glasses/godeye/Initialize() + . = ..() + scan = new(src) + +/obj/item/clothing/glasses/godeye/equipped(mob/living/user, slot) + . = ..() + if(ishuman(user) && slot == ITEM_SLOT_EYES) + ADD_TRAIT(src, TRAIT_NODROP, EYE_OF_GOD_TRAIT) + pain(user) + user.AddAbility(scan) + +/obj/item/clothing/glasses/godeye/dropped(mob/living/user) + . = ..() + // Behead someone, their "glasses" drop on the floor + // and thus, the god eye should no longer be sticky + REMOVE_TRAIT(src, TRAIT_NODROP, EYE_OF_GOD_TRAIT) + user.RemoveAbility(scan) + +/obj/item/clothing/glasses/godeye/proc/pain(mob/living/victim) + to_chat(victim, span_userdanger("You experience blinding pain, as [src] burrows into your skull.")) + victim.emote("scream") + victim.flash_act() + +/obj/effect/proc_holder/scan + name = "Scan" + desc = "Scan an enemy, to get their location and stagger them, increasing their time between attacks." + action_background_icon_state = "bg_clock" + action_icon = 'icons/mob/actions/actions_items.dmi' + action_icon_state = "scan" + ranged_mousepointer = 'icons/effects/mouse_pointers/scan_target.dmi' + var/cooldown_time = 45 SECONDS + COOLDOWN_DECLARE(scan_cooldown) + +/obj/effect/proc_holder/scan/on_lose(mob/living/user) + remove_ranged_ability() + +/obj/effect/proc_holder/scan/Click(location, control, params) + . = ..() + if(!isliving(usr)) + return TRUE + var/mob/living/user = usr + fire(user) + +/obj/effect/proc_holder/scan/fire(mob/living/carbon/user) + if(active) + remove_ranged_ability(span_notice("Your eye relaxes.")) + else + add_ranged_ability(user, span_notice("Your eye starts spinning fast. Left-click a creature to scan it!"), TRUE) + +/obj/effect/proc_holder/scan/InterceptClickOn(mob/living/caller, params, atom/target) + . = ..() + if(.) + return + if(ranged_ability_user.stat) + remove_ranged_ability() + return + if(!COOLDOWN_FINISHED(src, scan_cooldown)) + balloon_alert(ranged_ability_user, "not ready!") + return + if(!isliving(target) || target == ranged_ability_user) + balloon_alert(ranged_ability_user, "invalid target!") + return + var/mob/living/living_target = target + living_target.apply_status_effect(STATUS_EFFECT_STAGGER) + var/datum/status_effect/agent_pinpointer/scan_pinpointer = ranged_ability_user.apply_status_effect(/datum/status_effect/agent_pinpointer/scan) + scan_pinpointer.scan_target = living_target + living_target.Jitter(5 SECONDS) + to_chat(living_target, span_warning("You've been staggered!")) + living_target.add_filter("scan", 2, list("type" = "outline", "color" = COLOR_YELLOW, "size" = 1)) + addtimer(CALLBACK(living_target, /atom/.proc/remove_filter, "scan"), 30 SECONDS) + ranged_ability_user.playsound_local(get_turf(ranged_ability_user), 'sound/magic/smoke.ogg', 50, TRUE) + balloon_alert(ranged_ability_user, "[living_target] scanned") + COOLDOWN_START(src, scan_cooldown, cooldown_time) + addtimer(CALLBACK(src, /atom/.proc/balloon_alert, ranged_ability_user, "scan recharged"), cooldown_time) + remove_ranged_ability() + return TRUE + +/datum/status_effect/agent_pinpointer/scan + duration = 15 SECONDS + alert_type = /atom/movable/screen/alert/status_effect/agent_pinpointer/scan + tick_interval = 2 SECONDS + range_fuzz_factor = 0 + minimum_range = 1 + range_mid = 5 + range_far = 15 + +/datum/status_effect/agent_pinpointer/scan/scan_for_target() + return + +/atom/movable/screen/alert/status_effect/agent_pinpointer/scan + name = "Scan Target" + desc = "Contact may or may not be close." + +/obj/item/organ/cyberimp/arm/katana + name = "dark shard" + desc = "An eerie metal shard surrounded by dark energies." + icon = 'icons/obj/lavaland/artefacts.dmi' + icon_state = "cursed_katana_organ" + status = ORGAN_ORGANIC + organ_flags = ORGAN_FROZEN|ORGAN_UNREMOVABLE + items_to_create = list(/obj/item/cursed_katana) + extend_sound = 'sound/items/unsheath.ogg' + retract_sound = 'sound/items/sheath.ogg' + +/obj/item/organ/cyberimp/arm/katana/attack_self(mob/user, modifiers) + . = ..() + to_chat(user, span_userdanger("The mass goes up your arm and goes inside it!")) + playsound(user, 'sound/magic/demon_consume.ogg', 50, TRUE) + var/index = user.get_held_index_of_item(src) + zone = (index == LEFT_HANDS ? BODY_ZONE_L_ARM : BODY_ZONE_R_ARM) + SetSlotFromZone() + user.temporarilyRemoveItemFromInventory(src, TRUE) + Insert(user) + +/obj/item/organ/cyberimp/arm/katana/screwdriver_act(mob/living/user, obj/item/screwtool) + return + +/obj/item/organ/cyberimp/arm/katana/Retract() + var/obj/item/cursed_katana/katana = active_item + if(!katana || katana.shattered) + return + if(!katana.drew_blood) + to_chat(owner, span_userdanger("[katana] lashes out at you in hunger!")) + playsound(owner, 'sound/magic/demon_attack1.ogg', 50, TRUE) + var/obj/item/bodypart/part = owner.get_holding_bodypart_of_item(katana) + if(part) + part.receive_damage(brute = 25, wound_bonus = 10, sharpness = SHARP_EDGED) + katana.drew_blood = FALSE + katana.wash(CLEAN_TYPE_BLOOD) + return ..() + +#define LEFT_SLASH "Left Slash" +#define RIGHT_SLASH "Right Slash" +#define COMBO_STEPS "steps" +#define COMBO_PROC "proc" +#define ATTACK_STRIKE "Hilt Strike" +#define ATTACK_SLICE "Wide Slice" +#define ATTACK_DASH "Dash Attack" +#define ATTACK_CUT "Tendon Cut" +#define ATTACK_CLOAK "Dark Cloak" +#define ATTACK_SHATTER "Shatter" + +/obj/item/cursed_katana + name = "cursed katana" + desc = "A katana used to seal something vile away long ago. \ + Even with the weapon destroyed, all the pieces containing the creature have coagulated back together to find a new host." + icon = 'icons/obj/lavaland/artefacts.dmi' + icon_state = "cursed_katana" + lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' + force = 15 + armour_penetration = 30 + block_chance = 30 + sharpness = SHARP_EDGED + w_class = WEIGHT_CLASS_HUGE + attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts") + attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut") + hitsound = 'sound/weapons/bladeslice.ogg' + resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | FREEZE_PROOF + var/shattered = FALSE + var/drew_blood = FALSE + var/timerid + var/list/input_list = list() + var/list/combo_strings = list() + var/static/list/combo_list = list( + ATTACK_STRIKE = list(COMBO_STEPS = list(LEFT_SLASH, LEFT_SLASH, RIGHT_SLASH), COMBO_PROC = .proc/strike), + ATTACK_SLICE = list(COMBO_STEPS = list(RIGHT_SLASH, LEFT_SLASH, LEFT_SLASH), COMBO_PROC = .proc/slice), + ATTACK_DASH = list(COMBO_STEPS = list(LEFT_SLASH, RIGHT_SLASH, RIGHT_SLASH), COMBO_PROC = .proc/dash), + ATTACK_CUT = list(COMBO_STEPS = list(RIGHT_SLASH, RIGHT_SLASH, LEFT_SLASH), COMBO_PROC = .proc/cut), + ATTACK_CLOAK = list(COMBO_STEPS = list(LEFT_SLASH, RIGHT_SLASH, LEFT_SLASH, RIGHT_SLASH), COMBO_PROC = .proc/cloak), + ATTACK_SHATTER = list(COMBO_STEPS = list(RIGHT_SLASH, LEFT_SLASH, RIGHT_SLASH, LEFT_SLASH), COMBO_PROC = .proc/shatter), + ) + +/obj/item/cursed_katana/Initialize() + . = ..() + for(var/combo in combo_list) + var/list/combo_specifics = combo_list[combo] + var/step_string = english_list(combo_specifics[COMBO_STEPS]) + combo_strings += span_notice("[combo] - [step_string]") + +/obj/item/cursed_katana/examine(mob/user) + . = ..() + . += drew_blood ? span_nicegreen("It's sated... for now.") : span_danger("It will not be sated until it tastes blood.") + . += span_notice("There seem to be inscriptions on it... you could examine them closer?") + +/obj/item/cursed_katana/examine_more(mob/user) + . = ..() + . += combo_strings + +/obj/item/cursed_katana/dropped(mob/user) + . = ..() + reset_inputs(null, TRUE) + if(isturf(loc)) + qdel(src) + +/obj/item/cursed_katana/attack_self(mob/user) + . = ..() + reset_inputs(user, TRUE) + +/obj/item/cursed_katana/attack(mob/living/target, mob/user, click_parameters) + if(target.stat == DEAD || target == user) + return ..() + if(HAS_TRAIT(user, TRAIT_PACIFISM)) + balloon_alert(user, "you don't want to harm!") + return + drew_blood = TRUE + var/list/modifiers = params2list(click_parameters) + if(LAZYACCESS(modifiers, RIGHT_CLICK)) + input_list += RIGHT_SLASH + if(LAZYACCESS(modifiers, LEFT_CLICK)) + input_list += LEFT_SLASH + if(ishostile(target)) + user.changeNext_move(CLICK_CD_RAPID) + if(length(input_list) > 4) + reset_inputs(user, TRUE) + if(check_input(target, user)) + reset_inputs(null, TRUE) + return TRUE + else + timerid = addtimer(CALLBACK(src, .proc/reset_inputs, user, FALSE), 5 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_STOPPABLE) + return ..() + +/obj/item/cursed_katana/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + if(attack_type == PROJECTILE_ATTACK) + final_block_chance = 0 //Don't bring a sword to a gunfight + return ..() + +/obj/item/cursed_katana/proc/check_input(mob/living/target, mob/user) + for(var/combo in combo_list) + var/list/combo_specifics = combo_list[combo] + if(compare_list(input_list,combo_specifics[COMBO_STEPS])) + INVOKE_ASYNC(src, combo_specifics[COMBO_PROC], target, user) + return TRUE + return FALSE + +/obj/item/cursed_katana/proc/reset_inputs(mob/user, deltimer) + input_list.Cut() + if(user) + balloon_alert(user, "you return to neutral stance") + if(deltimer && timerid) + deltimer(timerid) + +/obj/item/cursed_katana/proc/strike(mob/living/target, mob/user) + user.visible_message(span_warning("[user] strikes [target] with [src]'s hilt!"), + span_notice("You hilt strike [target]!")) + to_chat(target, span_userdanger("You've been struck by [user]!")) + playsound(src, 'sound/weapons/genhit3.ogg', 50, TRUE) + RegisterSignal(target, COMSIG_MOVABLE_IMPACT, .proc/strike_throw_impact) + var/atom/throw_target = get_edge_target_turf(target, user.dir) + target.throw_at(throw_target, 5, 3, user, FALSE, gentle = TRUE) + target.apply_damage(damage = 17, bare_wound_bonus = 10) + to_chat(target, span_userdanger("You've been struck by [user]!")) + user.do_attack_animation(target, ATTACK_EFFECT_PUNCH) + +/obj/item/cursed_katana/proc/strike_throw_impact(mob/living/source, atom/hit_atom, datum/thrownthing/thrownthing) + SIGNAL_HANDLER + + UnregisterSignal(source, COMSIG_MOVABLE_IMPACT) + if(isclosedturf(hit_atom)) + source.apply_damage(damage = 5) + if(ishostile(source)) + var/mob/living/simple_animal/hostile/target = source + target.ranged_cooldown += 5 SECONDS + else if(iscarbon(source)) + var/mob/living/carbon/target = source + target.set_confusion(max(target.get_confusion(), 8)) + return NONE + +/obj/item/cursed_katana/proc/slice(mob/living/target, mob/user) + user.visible_message(span_warning("[user] does a wide slice!"), + span_notice("You do a wide slice!")) + playsound(src, 'sound/weapons/bladeslice.ogg', 50, TRUE) + var/turf/user_turf = get_turf(user) + var/dir_to_target = get_dir(user_turf, get_turf(target)) + var/static/list/cursed_katana_slice_angles = list(0, -45, 45, -90, 90) //so that the animation animates towards the target clicked and not towards a side target + for(var/iteration in cursed_katana_slice_angles) + var/turf/turf = get_step(user_turf, turn(dir_to_target, iteration)) + user.do_attack_animation(turf, ATTACK_EFFECT_SLASH) + for(var/mob/living/additional_target in turf) + if(user.Adjacent(additional_target) && additional_target.density) + additional_target.apply_damage(damage = 15, sharpness = SHARP_EDGED, bare_wound_bonus = 10) + to_chat(additional_target, span_userdanger("You've been sliced by [user]!")) + target.apply_damage(damage = 5, sharpness = SHARP_EDGED, wound_bonus = 10) + +/obj/item/cursed_katana/proc/cloak(mob/living/target, mob/user) + user.alpha = 150 + user.invisibility = INVISIBILITY_OBSERVER // so hostile mobs cant see us or target us + user.sight |= SEE_SELF // so we can see us + user.visible_message(span_warning("[user] vanishes into thin air!"), + span_notice("You enter the dark cloak.")) + playsound(src, 'sound/magic/smoke.ogg', 50, TRUE) + if(ishostile(target)) + var/mob/living/simple_animal/hostile/hostile_target = target + hostile_target.LoseTarget() + addtimer(CALLBACK(src, .proc/uncloak, user), 5 SECONDS, TIMER_UNIQUE) + +/obj/item/cursed_katana/proc/uncloak(mob/user) + user.alpha = 255 + user.invisibility = 0 + user.sight &= ~SEE_SELF + user.visible_message(span_warning("[user] appears from thin air!"), + span_notice("You exit the dark cloak.")) + playsound(src, 'sound/magic/summonitems_generic.ogg', 50, TRUE) + +/obj/item/cursed_katana/proc/cut(mob/living/target, mob/user) + user.visible_message(span_warning("[user] cuts [target]'s tendons!"), + span_notice("You tendon cut [target]!")) + to_chat(target, span_userdanger("Your tendons have been cut by [user]!")) + target.apply_damage(damage = 15, sharpness = SHARP_EDGED, wound_bonus = 15) + user.do_attack_animation(target, ATTACK_EFFECT_DISARM) + playsound(src, 'sound/weapons/rapierhit.ogg', 50, TRUE) + var/datum/status_effect/stacking/saw_bleed/bloodletting/status = target.has_status_effect(/datum/status_effect/stacking/saw_bleed/bloodletting) + if(!status) + target.apply_status_effect(/datum/status_effect/stacking/saw_bleed/bloodletting, 6) + else + status.add_stacks(6) + +/obj/item/cursed_katana/proc/dash(mob/living/target, mob/user) + user.visible_message(span_warning("[user] dashes through [target]!"), + span_notice("You dash through [target]!")) + to_chat(target, span_userdanger("[user] dashes through you!")) + playsound(src, 'sound/magic/blink.ogg', 50, TRUE) + target.apply_damage(damage = 17, sharpness = SHARP_POINTY, bare_wound_bonus = 10) + var/turf/dash_target = get_turf(target) + for(var/distance in 0 to 8) + var/turf/current_dash_target = dash_target + current_dash_target = get_step(current_dash_target, user.dir) + if(!current_dash_target.is_blocked_turf(TRUE)) + dash_target = current_dash_target + else + break + new /obj/effect/temp_visual/guardian/phase/out(get_turf(user)) + new /obj/effect/temp_visual/guardian/phase(dash_target) + do_teleport(user, dash_target, channel = TELEPORT_CHANNEL_MAGIC) + +/obj/item/cursed_katana/proc/shatter(mob/living/target, mob/user) + user.visible_message(span_warning("[user] shatters [src] over [target]!"), + span_notice("You shatter [src] over [target]!")) + to_chat(target, span_userdanger("[user] shatters [src] over you!")) + target.apply_damage(damage = ishostile(target) ? 75 : 35, wound_bonus = 20) + user.do_attack_animation(target, ATTACK_EFFECT_SMASH) + playsound(src, 'sound/effects/glassbr3.ogg', 100, TRUE) + shattered = TRUE + moveToNullspace() + balloon_alert(user, "katana shattered") + addtimer(CALLBACK(src, .proc/coagulate, user), 45 SECONDS) + +/obj/item/cursed_katana/proc/coagulate(mob/user) + balloon_alert(user, "katana coagulated") + shattered = FALSE + playsound(src, 'sound/magic/demon_consume.ogg', 50, TRUE) + +#undef LEFT_SLASH +#undef RIGHT_SLASH +#undef COMBO_STEPS +#undef COMBO_PROC +#undef ATTACK_STRIKE +#undef ATTACK_SLICE +#undef ATTACK_DASH +#undef ATTACK_CUT +#undef ATTACK_CLOAK +#undef ATTACK_SHATTER diff --git a/code/modules/mob/living/carbon/alien/special/facehugger.dm b/code/modules/mob/living/carbon/alien/special/facehugger.dm index 26669a4171e..7783378f53a 100644 --- a/code/modules/mob/living/carbon/alien/special/facehugger.dm +++ b/code/modules/mob/living/carbon/alien/special/facehugger.dm @@ -40,23 +40,6 @@ AddElement(/datum/element/connect_loc, loc_connections) AddElement(/datum/element/atmos_sensitive, mapload) -/obj/item/clothing/mask/facehugger/lamarr - name = "Lamarr" - desc = "The Research Director's pet, a domesticated and debeaked xenomorph facehugger. Friendly, but may still try to couple with your head." - sterile = 1 - -/obj/item/clothing/mask/facehugger/dead - icon_state = "facehugger_dead" - inhand_icon_state = "facehugger_inactive" - worn_icon_state = "facehugger_dead" - stat = DEAD - -/obj/item/clothing/mask/facehugger/impregnated - icon_state = "facehugger_impregnated" - inhand_icon_state = "facehugger_impregnated" - worn_icon_state = "facehugger_impregnated" - stat = DEAD - /obj/item/clothing/mask/facehugger/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) ..() if(obj_integrity < 90) @@ -274,6 +257,33 @@ return TRUE return FALSE +/obj/item/clothing/mask/facehugger/lamarr + name = "Lamarr" + desc = "The Research Director's pet, a domesticated and debeaked xenomorph facehugger. Friendly, but may still try to couple with your head." + sterile = TRUE + +/obj/item/clothing/mask/facehugger/dead + icon_state = "facehugger_dead" + inhand_icon_state = "facehugger_inactive" + worn_icon_state = "facehugger_dead" + stat = DEAD + +/obj/item/clothing/mask/facehugger/impregnated + icon_state = "facehugger_impregnated" + inhand_icon_state = "facehugger_impregnated" + worn_icon_state = "facehugger_impregnated" + stat = DEAD + +/obj/item/clothing/mask/facehugger/toy + inhand_icon_state = "facehugger_inactive" + desc = "A toy often used to play pranks on other miners by putting it in their beds. It takes a bit to recharge after latching onto something." + real = FALSE + sterile = TRUE + tint = 3 //Makes it feel more authentic when it latches on + +/obj/item/clothing/mask/facehugger/toy/Die() + return + #undef MIN_ACTIVE_TIME #undef MAX_ACTIVE_TIME diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 218eac809da..5e7eb7caeaa 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -511,13 +511,12 @@ update_stat() if(((maxHealth - total_burn) < HEALTH_THRESHOLD_DEAD*2) && stat == DEAD ) become_husk(BURN) - med_hud_set_health() - if(stat == SOFT_CRIT) add_movespeed_modifier(/datum/movespeed_modifier/carbon_softcrit) else remove_movespeed_modifier(/datum/movespeed_modifier/carbon_softcrit) + SEND_SIGNAL(src, COMSIG_CARBON_HEALTH_UPDATE) /mob/living/carbon/update_stamina() var/stam = getStaminaLoss() diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/modules/mob/living/simple_animal/guardian/guardian.dm index ed09aaf2006..065159289fe 100644 --- a/code/modules/mob/living/simple_animal/guardian/guardian.dm +++ b/code/modules/mob/living/simple_animal/guardian/guardian.dm @@ -61,6 +61,8 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians var/tech_fluff_string = "BOOT SEQUENCE COMPLETE. ERROR MODULE LOADED. THIS SHOULDN'T HAPPEN. Submit a bug report!" var/carp_fluff_string = "CARP CARP CARP SOME SORT OF HORRIFIC BUG BLAME THE CODERS CARP CARP CARP" var/miner_fluff_string = "You encounter... Mythril, it shouldn't exist... Submit a bug report!" + ///Are we forced to not be able to manifest/recall? + var/locked = FALSE /mob/living/simple_animal/hostile/guardian/Initialize(mapload, theme) GLOB.parasites += src @@ -348,7 +350,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians //MANIFEST, RECALL, TOGGLE MODE/LIGHT, SHOW TYPE /mob/living/simple_animal/hostile/guardian/proc/Manifest(forced) - if(istype(summoner.loc, /obj/effect) || (cooldown > world.time && !forced)) + if(istype(summoner.loc, /obj/effect) || (cooldown > world.time && !forced) || locked) return FALSE if(loc == summoner) forceMove(summoner.loc) @@ -359,7 +361,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians return FALSE /mob/living/simple_animal/hostile/guardian/proc/Recall(forced) - if(!summoner || loc == summoner || (cooldown > world.time && !forced)) + if(!summoner || loc == summoner || (cooldown > world.time && !forced) || locked) return FALSE new /obj/effect/temp_visual/guardian/phase/out(loc) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/_megafauna.dm similarity index 88% rename from code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm rename to code/modules/mob/living/simple_animal/hostile/megafauna/_megafauna.dm index 94030c7ca66..90bb97eaa57 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/_megafauna.dm @@ -153,12 +153,19 @@ if (EXPLODE_LIGHT) adjustBruteLoss(50) -/// Sets the next time the megafauna can use a melee or ranged attack, in deciseconds -/mob/living/simple_animal/hostile/megafauna/proc/SetRecoveryTime(buffer_time, ranged_buffer_time) - recovery_time = world.time + buffer_time - ranged_cooldown = world.time + buffer_time - if(isnum(ranged_buffer_time)) - ranged_cooldown = world.time + ranged_buffer_time +/// Sets/adds the next time the megafauna can use a melee or ranged attack, in deciseconds. It is a list to allow using named args. Use the ignore_staggered var if youre setting the cooldown to ranged_cooldown_time. +/mob/living/simple_animal/hostile/megafauna/proc/update_cooldowns(list/cooldown_updates, ignore_staggered = FALSE) + if(!ignore_staggered && has_status_effect(STATUS_EFFECT_STAGGER)) + for(var/update in cooldown_updates) + cooldown_updates[update] *= 2 + if(cooldown_updates[COOLDOWN_UPDATE_SET_MELEE]) + recovery_time = world.time + cooldown_updates[COOLDOWN_UPDATE_SET_MELEE] + if(cooldown_updates[COOLDOWN_UPDATE_ADD_MELEE]) + recovery_time += cooldown_updates[COOLDOWN_UPDATE_ADD_MELEE] + if(cooldown_updates[COOLDOWN_UPDATE_SET_RANGED]) + ranged_cooldown = world.time + cooldown_updates[COOLDOWN_UPDATE_SET_RANGED] + if(cooldown_updates[COOLDOWN_UPDATE_ADD_RANGED]) + ranged_cooldown += cooldown_updates[COOLDOWN_UPDATE_ADD_RANGED] /// Grants medals and achievements to surrounding players /mob/living/simple_animal/hostile/megafauna/proc/grant_achievement(medaltype, scoretype, crusher_kill, list/grant_achievement = list()) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm index 217dc39f86f..348ebf313ab 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm @@ -38,7 +38,7 @@ Difficulty: Medium projectiletype = /obj/projectile/kinetic/miner projectilesound = 'sound/weapons/kenetic_accel.ogg' ranged = TRUE - ranged_cooldown_time = 16 + ranged_cooldown_time = 1.6 SECONDS pixel_x = -16 base_pixel_x = -16 crusher_loot = list(/obj/item/melee/transforming/cleaving_saw, /obj/item/gun/energy/kinetic_accelerator, /obj/item/crusher_trophy/miner_eye) @@ -53,7 +53,8 @@ Difficulty: Medium var/obj/item/melee/transforming/cleaving_saw/miner/miner_saw var/time_until_next_transform = 0 var/dashing = FALSE - var/dash_cooldown = 15 + var/dash_cooldown = 0 + var/dash_cooldown_time = 1.5 SECONDS var/guidance = FALSE var/transform_stop_attack = FALSE // stops the blood drunk miner from attacking after transforming his weapon until the next attack chain deathmessage = "falls to the ground, decaying into glowing particles." @@ -89,6 +90,17 @@ Difficulty: Medium chosen_message = "You are now transforming your weapon." chosen_attack_num = 3 +/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/update_cooldowns(list/cooldown_updates, ignore_staggered = FALSE) + . = ..() + if(cooldown_updates[COOLDOWN_UPDATE_SET_DASH]) + dash_cooldown = world.time + cooldown_updates[COOLDOWN_UPDATE_SET_DASH] + if(cooldown_updates[COOLDOWN_UPDATE_ADD_DASH]) + dash_cooldown += cooldown_updates[COOLDOWN_UPDATE_ADD_DASH] + if(cooldown_updates[COOLDOWN_UPDATE_SET_TRANSFORM]) + time_until_next_transform = world.time + cooldown_updates[COOLDOWN_UPDATE_SET_TRANSFORM] + if(cooldown_updates[COOLDOWN_UPDATE_ADD_TRANSFORM]) + time_until_next_transform += cooldown_updates[COOLDOWN_UPDATE_ADD_TRANSFORM] + /mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/OpenFire() if(client) switch(chosen_attack) @@ -188,7 +200,7 @@ Difficulty: Medium /mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/shoot_ka() if(ranged_cooldown <= world.time && get_dist(src, target) <= MINER_DASH_RANGE && !Adjacent(target)) - ranged_cooldown = world.time + ranged_cooldown_time + update_cooldowns(list(COOLDOWN_UPDATE_ADD_RANGED = ranged_cooldown_time), ignore_staggered = TRUE) visible_message(span_danger("[src] fires the proto-kinetic accelerator!")) face_atom(target) new /obj/effect/temp_visual/dir_setting/firing_effect(loc, dir) @@ -226,7 +238,7 @@ Difficulty: Medium accessable_turfs -= t if(!LAZYLEN(accessable_turfs)) return - dash_cooldown = world.time + initial(dash_cooldown) + update_cooldowns(list(COOLDOWN_UPDATE_SET_DASH = dash_cooldown_time)) target_turf = pick(accessable_turfs) var/turf/step_back_turf = get_step(target_turf, get_cardinal_dir(target_turf, own_turf)) var/turf/step_forward_turf = get_step(own_turf, get_cardinal_dir(own_turf, target_turf)) @@ -257,7 +269,7 @@ Difficulty: Medium transform_stop_attack = TRUE icon_state = "miner[miner_saw.active ? "_transformed":""]" icon_living = "miner[miner_saw.active ? "_transformed":""]" - time_until_next_transform = world.time + rand(50, 100) + update_cooldowns(list(COOLDOWN_UPDATE_SET_TRANSFORM = rand(5 SECONDS, 10 SECONDS))) /obj/effect/temp_visual/dir_setting/miner_death icon_state = "miner_death" @@ -293,7 +305,7 @@ Difficulty: Medium desc = "A miner destined to hop across dimensions for all eternity, hunting anomalous creatures." speed = 8 move_to_delay = 8 - ranged_cooldown_time = 8 - dash_cooldown = 8 + ranged_cooldown_time = 0.8 SECONDS + dash_cooldown = 0.8 SECONDS #undef MINER_DASH_RANGE diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm index e79182c1414..a760685c0b0 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm @@ -61,7 +61,7 @@ Difficulty: Hard blood_volume = BLOOD_VOLUME_MAXIMUM //BLEED FOR ME var/charging = FALSE var/enrage_till = 0 - var/enrage_time = 70 + var/enrage_time = 7 SECONDS var/revving_charge = FALSE gps_name = "Bloody Signal" achievement_type = /datum/award/achievement/boss/bubblegum_kill @@ -105,13 +105,20 @@ Difficulty: Hard chosen_message = "You are now warping to blood around your clicked position." chosen_attack_num = 4 +/mob/living/simple_animal/hostile/megafauna/bubblegum/update_cooldowns(list/cooldown_updates, ignore_staggered = FALSE) + . = ..() + if(cooldown_updates[COOLDOWN_UPDATE_SET_ENRAGE]) + enrage_till = world.time + cooldown_updates[COOLDOWN_UPDATE_SET_ENRAGE] + if(cooldown_updates[COOLDOWN_UPDATE_ADD_ENRAGE]) + enrage_till += cooldown_updates[COOLDOWN_UPDATE_ADD_ENRAGE] + /mob/living/simple_animal/hostile/megafauna/bubblegum/OpenFire() if(charging) return anger_modifier = clamp(((maxHealth - health)/60),0,20) enrage_time = initial(enrage_time) * clamp(anger_modifier / 20, 0.5, 1) - ranged_cooldown = world.time + 50 + update_cooldowns(list(COOLDOWN_UPDATE_ADD_RANGED = 5 SECONDS)) if(client) switch(chosen_attack) @@ -140,18 +147,18 @@ Difficulty: Hard charge(delay = 6) charge(delay = 4) charge(delay = 2) - SetRecoveryTime(15) + update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 1.5 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 1.5 SECONDS)) /mob/living/simple_animal/hostile/megafauna/bubblegum/proc/hallucination_charge() if(!BUBBLEGUM_SMASH || prob(33)) hallucination_charge_around(times = 6, delay = 8) - SetRecoveryTime(10) + update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 1 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 1 SECONDS)) else hallucination_charge_around(times = 4, delay = 9) hallucination_charge_around(times = 4, delay = 8) hallucination_charge_around(times = 4, delay = 7) triple_charge() - SetRecoveryTime(20) + update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 2 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 2 SECONDS)) /mob/living/simple_animal/hostile/megafauna/bubblegum/proc/surround_with_hallucinations() for(var/i = 1 to 5) @@ -160,7 +167,7 @@ Difficulty: Hard charge(delay = 6) else SLEEP_CHECK_DEATH(6) - SetRecoveryTime(20) + update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 2 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 2 SECONDS)) /mob/living/simple_animal/hostile/megafauna/bubblegum/proc/charge(atom/chargeat = target, delay = 3, chargepast = 2) if(!chargeat) @@ -342,7 +349,7 @@ Difficulty: Hard /mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_enrage() if(!BUBBLEGUM_CAN_ENRAGE) return FALSE - enrage_till = world.time + enrage_time + update_cooldowns(list(COOLDOWN_UPDATE_SET_ENRAGE = enrage_time)) update_approach() change_move_delay(3.75) add_atom_colour(COLOR_BUBBLEGUM_RED, TEMPORARY_COLOUR_PRIORITY) @@ -428,7 +435,7 @@ Difficulty: Hard if(!charging) . = ..() if(.) - recovery_time = world.time + 20 // can only attack melee once every 2 seconds but rapid_melee gives higher priority + update_cooldowns(list(COOLDOWN_UPDATE_ADD_MELEE = 2 SECONDS)) // can only attack melee once every 2 seconds but rapid_melee gives higher priority /mob/living/simple_animal/hostile/megafauna/bubblegum/bullet_act(obj/projectile/P) if(BUBBLEGUM_IS_ENRAGED) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index 2faf78e7d6f..75b921678dd 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -91,7 +91,7 @@ /mob/living/simple_animal/hostile/megafauna/colossus/OpenFire() anger_modifier = clamp(((maxHealth - health)/50),0,20) - ranged_cooldown = world.time + 120 + update_cooldowns(list(COOLDOWN_UPDATE_ADD_RANGED = 12 SECONDS)) if(client) switch(chosen_attack) @@ -108,7 +108,7 @@ if(enrage(target)) if(move_to_delay == initial(move_to_delay)) visible_message(span_colossus("\"You can't dodge.\"")) - ranged_cooldown = world.time + 30 + update_cooldowns(list(COOLDOWN_UPDATE_ADD_RANGED = 3 SECONDS)) telegraph() dir_shots(GLOB.alldirs) move_to_delay = 3 @@ -136,7 +136,7 @@ . = TRUE /mob/living/simple_animal/hostile/megafauna/colossus/proc/alternating_dir_shots() - ranged_cooldown = world.time + 40 + update_cooldowns(list(COOLDOWN_UPDATE_ADD_RANGED = 4 SECONDS)) dir_shots(GLOB.diagonals) SLEEP_CHECK_DEATH(10) dir_shots(GLOB.cardinals) @@ -189,7 +189,7 @@ P.fire(set_angle) /mob/living/simple_animal/hostile/megafauna/colossus/proc/random_shots() - ranged_cooldown = world.time + 30 + update_cooldowns(list(COOLDOWN_UPDATE_ADD_RANGED = 3 SECONDS)) var/turf/U = get_turf(src) playsound(U, 'sound/magic/clockwork/invoke_general.ogg', 300, TRUE, 5) for(var/T in RANGE_TURFS(12, U) - U) @@ -197,7 +197,7 @@ shoot_projectile(T) /mob/living/simple_animal/hostile/megafauna/colossus/proc/blast(set_angle) - ranged_cooldown = world.time + 20 + update_cooldowns(list(COOLDOWN_UPDATE_ADD_RANGED = 2 SECONDS)) var/turf/target_turf = get_turf(target) playsound(src, 'sound/magic/clockwork/invoke_general.ogg', 200, TRUE, 2) newtonian_move(get_dir(target_turf, src)) @@ -272,113 +272,6 @@ else SSexplosions.medturf += target -//There can only ever be one blackbox, and we want to know if there already is one when we spawn -GLOBAL_DATUM(blackbox, /obj/machinery/smartfridge/black_box) -//Black Box -/obj/machinery/smartfridge/black_box - name = "black box" - desc = "A completely indestructible chunk of crystal, rumoured to predate the start of this universe. It looks like you could store things inside it." - icon = 'icons/obj/lavaland/artefacts.dmi' - icon_state = "blackbox" - light_range = 8 - max_n_of_items = INFINITY - resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF - pixel_y = -4 - use_power = NO_POWER_USE - base_build_path = /obj/machinery/smartfridge/black_box - var/memory_saved = FALSE - var/list/stored_items = list() - var/list/blacklist = list() - -/obj/machinery/smartfridge/black_box/ComponentInitialize() - . = ..() - AddElement(/datum/element/update_icon_blocker) - -/obj/machinery/smartfridge/black_box/accept_check(obj/item/O) - if(!istype(O)) - return FALSE - if(blacklist[O]) - visible_message(span_boldwarning("[src] ripples as it rejects [O]. The device will not accept items that have been removed from it.")) - return FALSE - return TRUE - -/obj/machinery/smartfridge/black_box/Initialize() - . = ..() - if(GLOB.blackbox != src) - return INITIALIZE_HINT_QDEL_FORCE - GLOB.blackbox = src - ReadMemory() - -/obj/machinery/smartfridge/black_box/process() - ..() - if(!memory_saved && SSticker.current_state == GAME_STATE_FINISHED) - WriteMemory() - memory_saved = TRUE - -/obj/machinery/smartfridge/black_box/proc/WriteMemory() - var/json_file = file("data/npc_saves/Blackbox.json") - stored_items = list() - - for(var/obj/O in (contents-component_parts)) - stored_items += O.type - var/list/file_data = list() - file_data["data"] = stored_items - fdel(json_file) - WRITE_FILE(json_file, json_encode(file_data)) - -/obj/machinery/smartfridge/black_box/proc/ReadMemory() - if(fexists("data/npc_saves/Blackbox.sav")) //legacy compatability to convert old format to new - var/savefile/S = new /savefile("data/npc_saves/Blackbox.sav") - S["stored_items"] >> stored_items - fdel("data/npc_saves/Blackbox.sav") - else - var/json_file = file("data/npc_saves/Blackbox.json") - if(!fexists(json_file)) - return - var/list/json = json_decode(file2text(json_file)) - stored_items = json["data"] - if(isnull(stored_items)) - stored_items = list() - - for(var/item in stored_items) - create_item(item) - -//in it's own proc to avoid issues with items that nolonger exist in the code base. -//try catch doesn't always prevent byond runtimes from halting a proc, -/obj/machinery/smartfridge/black_box/proc/create_item(item_type) - var/obj/O = new item_type(src) - blacklist[O] = TRUE - -/obj/machinery/smartfridge/black_box/Destroy(force = FALSE) - if(force) - if(GLOB.blackbox == src) - GLOB.blackbox = null - for(var/thing in src) - qdel(thing) - return ..() - else - return QDEL_HINT_LETMELIVE - - -//No taking it apart - -/obj/machinery/smartfridge/black_box/default_deconstruction_screwdriver() - return - -/obj/machinery/smartfridge/black_box/exchange_parts() - return - - -/obj/machinery/smartfridge/black_box/default_pry_open() - return - - -/obj/machinery/smartfridge/black_box/default_unfasten_wrench() - return - -/obj/machinery/smartfridge/black_box/default_deconstruction_crowbar() - return - ///Anomolous Crystal/// #define ACTIVATE_TOUCH "touch" @@ -404,12 +297,16 @@ GLOBAL_DATUM(blackbox, /obj/machinery/smartfridge/black_box) density = TRUE var/activation_method var/list/possible_methods = list(ACTIVATE_TOUCH, ACTIVATE_SPEECH, ACTIVATE_HEAT, ACTIVATE_BULLET, ACTIVATE_ENERGY, ACTIVATE_BOMB, ACTIVATE_MOB_BUMP, ACTIVATE_WEAPON, ACTIVATE_MAGIC) - var/activation_damage_type = null - var/last_use_timer = 0 - var/cooldown_add = 30 + /// Cooldown on this crystal + var/cooldown_add = 3 SECONDS + /// Time needed to use this crystal + var/use_time = 0 + /// If we are being used + var/active = FALSE var/list/affected_targets = list() var/activation_sound = 'sound/effects/break_stone.ogg' + COOLDOWN_DECLARE(cooldown_timer) /obj/machinery/anomalous_crystal/Initialize(mapload) . = ..() @@ -449,16 +346,32 @@ GLOBAL_DATUM(blackbox, /obj/machinery/smartfridge/black_box) ActivationReaction(P.firer, P.flag, P.damage_type) /obj/machinery/anomalous_crystal/proc/ActivationReaction(mob/user, method, damtype) - if(world.time < last_use_timer) + if(!COOLDOWN_FINISHED(src, cooldown_timer)) return FALSE if(activation_damage_type && activation_damage_type != damtype) return FALSE if(method != activation_method) return FALSE - last_use_timer = (world.time + cooldown_add) + if(active) + return FALSE + if(use_time) + charge_animation() + COOLDOWN_START(src, cooldown_timer, cooldown_add) playsound(user, activation_sound, 100, TRUE) return TRUE +/obj/machinery/anomalous_crystal/proc/charge_animation() + icon_state = "anomaly_crystal_charging" + active = TRUE + set_anchored(TRUE) + balloon_alert_to_viewers("charging...") + playsound(src, 'sound/magic/disable_tech.ogg', 50, TRUE) + sleep(use_time) + icon_state = initial(icon_state) + active = FALSE + set_anchored(FALSE) + return TRUE + /obj/machinery/anomalous_crystal/Bumped(atom/movable/AM) ..() if(ismob(AM)) @@ -471,20 +384,21 @@ GLOBAL_DATUM(blackbox, /obj/machinery/smartfridge/black_box) observer_desc = "This crystal strips and equips its targets as clowns." possible_methods = list(ACTIVATE_MOB_BUMP, ACTIVATE_SPEECH) activation_sound = 'sound/items/bikehorn.ogg' + use_time = 3 SECONDS /obj/machinery/anomalous_crystal/honk/ActivationReaction(mob/user) - if(..() && ishuman(user) && !(user in affected_targets)) + if(..() && ishuman(user) && !(user in affected_targets) && (user in viewers(src))) var/mob/living/carbon/human/new_clown = user for(var/obj/item/to_strip in new_clown) new_clown.dropItemToGround(to_strip) new_clown.dress_up_as_job(SSjob.GetJobType(/datum/job/clown)) affected_targets.Add(new_clown) - /obj/machinery/anomalous_crystal/theme_warp //Warps the area you're in to look like a new one observer_desc = "This crystal warps the area around it to a theme." activation_method = ACTIVATE_TOUCH - cooldown_add = 200 + cooldown_add = 20 SECONDS + use_time = 5 SECONDS var/terrain_theme = "winter" var/NewTerrainFloors var/NewTerrainWalls @@ -505,7 +419,7 @@ GLOBAL_DATUM(blackbox, /obj/machinery/smartfridge/black_box) NewFlora = list(/mob/living/simple_animal/hostile/asteroid/goldgrub) florachance = 1 if("winter") //Snow terrain is slow to move in and cold! Get the assistants to shovel your driveway. - NewTerrainFloors = /turf/open/floor/grass/snow/safe + NewTerrainFloors = /turf/open/floor/grass/snow/actually_safe NewTerrainWalls = /turf/closed/wall/mineral/wood NewTerrainChairs = /obj/structure/chair/wood NewTerrainTables = /obj/structure/table/glass @@ -554,39 +468,24 @@ GLOBAL_DATUM(blackbox, /obj/machinery/smartfridge/black_box) /obj/machinery/anomalous_crystal/emitter //Generates a projectile when interacted with observer_desc = "This crystal generates a projectile when activated." activation_method = ACTIVATE_TOUCH - cooldown_add = 50 - var/obj/projectile/generated_projectile = /obj/projectile/beam/emitter + cooldown_add = 5 SECONDS + var/obj/projectile/generated_projectile = /obj/projectile/colossus /obj/machinery/anomalous_crystal/emitter/Initialize() . = ..() - generated_projectile = pick(/obj/projectile/colossus) - - var/proj_name = initial(generated_projectile.name) - observer_desc = "This crystal generates \a [proj_name] when activated." + observer_desc = "This crystal generates \a [initial(generated_projectile.name)] when activated." /obj/machinery/anomalous_crystal/emitter/ActivationReaction(mob/user, method) if(..()) var/obj/projectile/P = new generated_projectile(get_turf(src)) - P.setDir(dir) - switch(dir) - if(NORTH) - P.yo = 20 - P.xo = 0 - if(EAST) - P.yo = 0 - P.xo = 20 - if(WEST) - P.yo = 0 - P.xo = -20 - else - P.yo = -20 - P.xo = 0 - P.fire() + P.firer = src + P.fire(dir2angle(dir)) /obj/machinery/anomalous_crystal/dark_reprise //Revives anyone nearby, but turns them into shadowpeople and renders them uncloneable, so the crystal is your only hope of getting up again if you go down. observer_desc = "When activated, this crystal revives anyone nearby, but turns them into Shadowpeople and makes them unclonable, making the crystal their only hope of getting up again." activation_method = ACTIVATE_TOUCH activation_sound = 'sound/hallucinations/growl1.ogg' + use_time = 3 SECONDS /obj/machinery/anomalous_crystal/dark_reprise/ActivationReaction(mob/user, method) if(..()) @@ -608,6 +507,7 @@ GLOBAL_DATUM(blackbox, /obj/machinery/smartfridge/black_box) observer_desc = "This crystal allows ghosts to turn into a fragile creature that can heal people." activation_method = ACTIVATE_TOUCH activation_sound = 'sound/effects/ghost2.ogg' + use_time = 5 SECONDS var/ready_to_deploy = FALSE /obj/machinery/anomalous_crystal/helpers/ActivationReaction(mob/user, method) @@ -701,6 +601,7 @@ GLOBAL_DATUM(blackbox, /obj/machinery/smartfridge/black_box) /obj/machinery/anomalous_crystal/possessor //Allows you to bodyjack small animals, then exit them at your leisure, but you can only do this once per activation. Because they blow up. Also, if the bodyjacked animal dies, SO DO YOU. observer_desc = "When activated, this crystal allows you to take over small animals, and then exit them at the possessors leisure. Exiting the animal kills it, and if you die while possessing the animal, you die as well." activation_method = ACTIVATE_TOUCH + use_time = 1 SECONDS /obj/machinery/anomalous_crystal/possessor/ActivationReaction(mob/user, method) if(..()) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner.dm index 79a25687aef..4d52a82ad3d 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner.dm @@ -83,7 +83,7 @@ Difficulty: Extremely Hard /mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/OpenFire() check_enraged() projectile_speed_multiplier = 1 - enraged * 0.5 - SetRecoveryTime(100, 100) + update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 10 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 10 SECONDS)) if(client) switch(chosen_attack) @@ -185,7 +185,7 @@ Difficulty: Extremely Hard P.fire(rand(0, 360)) addtimer(CALLBACK(P, /obj/projectile/frost_orb/proc/orb_explosion, projectile_speed_multiplier), 20) // make the orbs home in after a second SLEEP_CHECK_DEATH(added_delay) - SetRecoveryTime(40, 60) + update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 4 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 6 SECONDS)) /// Called when the orb is exploding, shoots out projectiles /obj/projectile/frost_orb/proc/orb_explosion(projectile_speed_multiplier) @@ -219,7 +219,7 @@ Difficulty: Extremely Hard P.original = target P.fire() SLEEP_CHECK_DEATH(1) - SetRecoveryTime(15, 15) + update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 1.5 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 1.5 SECONDS)) /// Shoots out ice blasts in a shotgun like pattern /mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/proc/ice_shotgun(shots = 5, list/patterns = list(list(-40, -20, 0, 20, 40), list(-30, -10, 10, 30))) @@ -238,7 +238,7 @@ Difficulty: Extremely Hard P.original = target P.fire() SLEEP_CHECK_DEATH(8) - SetRecoveryTime(15, 20) + update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 1.5 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 2 SECONDS)) /// Checks if the demonic frost miner is ready to be enraged /mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/proc/check_enraged() @@ -246,7 +246,7 @@ Difficulty: Extremely Hard return if(health > maxHealth*0.25) return - SetRecoveryTime(80, 80) + update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 8 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 8 SECONDS)) adjustHealth(-maxHealth) enraged = TRUE enraging = TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm index 5a6602eb3b6..b64840eb304 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm @@ -111,7 +111,7 @@ return anger_modifier = clamp(((maxHealth - health)/50),0,20) - ranged_cooldown = world.time + ranged_cooldown_time + update_cooldowns(list(COOLDOWN_UPDATE_SET_RANGED = ranged_cooldown_time), ignore_staggered = TRUE) if(client) switch(chosen_attack) @@ -164,7 +164,7 @@ /mob/living/simple_animal/hostile/megafauna/dragon/proc/lava_swoop(amount = 30) if(health < maxHealth * 0.5) - return swoop_attack(lava_arena = TRUE, swoop_cooldown = 60) + return swoop_attack(lava_arena = TRUE, swoop_cooldown = 6 SECONDS) INVOKE_ASYNC(src, .proc/lava_pools, amount) swoop_attack(FALSE, target, 1000) // longer cooldown until it gets reset below SLEEP_CHECK_DEATH(0) @@ -174,19 +174,19 @@ fire_cone() SLEEP_CHECK_DEATH(10) fire_cone() - SetRecoveryTime(40) + update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 4 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 4 SECONDS)) /mob/living/simple_animal/hostile/megafauna/dragon/proc/mass_fire(spiral_count = 12, range = 15, times = 3) SLEEP_CHECK_DEATH(0) for(var/i = 1 to times) - SetRecoveryTime(50) + update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 5 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 5 SECONDS)) playsound(get_turf(src),'sound/magic/fireball.ogg', 200, TRUE) var/increment = 360 / spiral_count for(var/j = 1 to spiral_count) var/list/turfs = line_target(j * increment + i * increment / 2, range, src) INVOKE_ASYNC(src, .proc/fire_line, turfs) SLEEP_CHECK_DEATH(25) - SetRecoveryTime(30) + update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 3 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 3 SECONDS)) /mob/living/simple_animal/hostile/megafauna/dragon/proc/lava_arena() if(!target) @@ -241,7 +241,7 @@ /mob/living/simple_animal/hostile/megafauna/dragon/proc/arena_escape_enrage() // you ran somehow / teleported away from my arena attack now i'm mad fucker SLEEP_CHECK_DEATH(0) - SetRecoveryTime(80) + update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 8 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 8 SECONDS)) visible_message(span_boldwarning("[src] starts to glow vibrantly as its wounds close up!")) adjustBruteLoss(-250) // yeah you're gonna pay for that, don't run nerd add_atom_colour(rgb(255, 255, 0), TEMPORARY_COLOUR_PRIORITY) @@ -396,7 +396,7 @@ set_density(TRUE) SLEEP_CHECK_DEATH(1) swooping &= ~SWOOP_DAMAGEABLE - SetRecoveryTime(swoop_cooldown) + update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = swoop_cooldown, COOLDOWN_UPDATE_SET_RANGED = swoop_cooldown)) if(!lava_success) arena_escape_enrage() @@ -601,7 +601,7 @@ return swoop_attack(FALSE, A) lava_pools(10, 2) // less pools but longer delay before spawns - player_cooldown = world.time + 200 // needs seperate cooldown or cant use fire attacks + player_cooldown = world.time + 20 SECONDS // needs seperate cooldown or cant use fire attacks /mob/living/simple_animal/hostile/megafauna/dragon/lesser/grant_achievement(medaltype,scoretype) return diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm index 5cc8ef82fa6..6b9a8e5a1a2 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm @@ -56,7 +56,7 @@ Difficulty: Hard speed = 10 move_to_delay = 10 ranged = TRUE - ranged_cooldown_time = 40 + ranged_cooldown_time = 4 SECONDS aggro_vision_range = 21 //so it can see to one side of the arena to the other loot = list(/obj/item/hierophant_club) crusher_loot = list(/obj/item/hierophant_club, /obj/item/crusher_trophy/vortex_talisman) @@ -75,9 +75,11 @@ Difficulty: Hard var/burst_range = 3 //range on burst aoe var/beam_range = 5 //range on cross blast beams var/chaser_speed = 3 //how fast chasers are currently - var/chaser_cooldown = 101 //base cooldown/cooldown var between spawning chasers - var/major_attack_cooldown = 60 //base cooldown for major attacks - var/arena_cooldown = 200 //base cooldown/cooldown var for creating an arena + var/major_attack_cooldown = 6 SECONDS //base cooldown for major attacks + var/chaser_cooldown_time = 10.1 SECONDS //base cooldown for spawning chasers + var/chaser_cooldown = 0 + var/arena_cooldown_time = 20 SECONDS //base cooldown for making arenas + var/arena_cooldown = 0 var/blinking = FALSE //if we're doing something that requires us to stand still and not attack var/obj/effect/hierophant/spawned_beacon //the beacon we teleport back to var/timeout_time = 15 //after this many Life() ticks with no target, we return to our beacon @@ -121,6 +123,17 @@ Difficulty: Hard chosen_message = "You are now repeatedly blinking at your target." chosen_attack_num = 4 +/mob/living/simple_animal/hostile/megafauna/hierophant/update_cooldowns(list/cooldown_updates, ignore_staggered = FALSE) + . = ..() + if(cooldown_updates[COOLDOWN_UPDATE_SET_CHASER]) + chaser_cooldown = world.time + cooldown_updates[COOLDOWN_UPDATE_SET_CHASER] + if(cooldown_updates[COOLDOWN_UPDATE_ADD_CHASER]) + chaser_cooldown += cooldown_updates[COOLDOWN_UPDATE_ADD_CHASER] + if(cooldown_updates[COOLDOWN_UPDATE_SET_ARENA]) + arena_cooldown = world.time + cooldown_updates[COOLDOWN_UPDATE_SET_ARENA] + if(cooldown_updates[COOLDOWN_UPDATE_ADD_ARENA]) + arena_cooldown += cooldown_updates[COOLDOWN_UPDATE_ADD_ARENA] + /mob/living/simple_animal/hostile/megafauna/hierophant/OpenFire() if(blinking) return @@ -130,7 +143,7 @@ Difficulty: Hard var/cross_counter = 1 + round(anger_modifier * 0.12) arena_trap(target) - ranged_cooldown = world.time + max(5, ranged_cooldown_time - anger_modifier * 0.75) //scale cooldown lower with high anger. + update_cooldowns(list(COOLDOWN_UPDATE_SET_RANGED = max(0.5 SECONDS, ranged_cooldown_time - anger_modifier * 0.75)), ignore_staggered = TRUE) //scale cooldown lower with high anger. var/target_slowness = 0 var/mob/living/L @@ -182,7 +195,7 @@ Difficulty: Hard if(chaser_cooldown < world.time) //if chasers are off cooldown, fire some! var/obj/effect/temp_visual/hierophant/chaser/C = new /obj/effect/temp_visual/hierophant/chaser(loc, src, target, chaser_speed, FALSE) - chaser_cooldown = world.time + initial(chaser_cooldown) + update_cooldowns(list(COOLDOWN_UPDATE_SET_CHASER = chaser_cooldown_time)) if((prob(anger_modifier) || target.Adjacent(src)) && target != src) var/obj/effect/temp_visual/hierophant/chaser/OC = new(loc, src, target, chaser_speed * 1.5, FALSE) OC.moving = 4 @@ -202,7 +215,7 @@ Difficulty: Hard INVOKE_ASYNC(src, .proc/burst, get_turf(src)) /mob/living/simple_animal/hostile/megafauna/hierophant/proc/blink_spam(blink_counter, target_slowness, cross_counter) - ranged_cooldown = world.time + max(5, major_attack_cooldown - anger_modifier * 0.75) + update_cooldowns(list(COOLDOWN_UPDATE_SET_RANGED = max(0.5 SECONDS, major_attack_cooldown - anger_modifier * 0.75))) if(health < maxHealth * 0.5 && blink_counter > 1) visible_message(span_hierophant("\"Mx ampp rsx iwgeti.\"")) var/oldcolor = color @@ -224,7 +237,7 @@ Difficulty: Hard blink(target) /mob/living/simple_animal/hostile/megafauna/hierophant/proc/cross_blast_spam(blink_counter, target_slowness, cross_counter) - ranged_cooldown = world.time + max(5, major_attack_cooldown - anger_modifier * 0.75) + update_cooldowns(list(COOLDOWN_UPDATE_SET_RANGED = max(0.5 SECONDS, major_attack_cooldown - anger_modifier * 0.75))) visible_message(span_hierophant("\"Piezi mx rsalivi xs vyr.\"")) blinking = TRUE var/oldcolor = color @@ -244,7 +257,7 @@ Difficulty: Hard /mob/living/simple_animal/hostile/megafauna/hierophant/proc/chaser_swarm(blink_counter, target_slowness, cross_counter) - ranged_cooldown = world.time + max(5, major_attack_cooldown - anger_modifier * 0.75) + update_cooldowns(list(COOLDOWN_UPDATE_SET_RANGED = max(0.5 SECONDS, major_attack_cooldown - anger_modifier * 0.75))) visible_message(span_hierophant("\"Mx gerrsx lmhi.\"")) blinking = TRUE var/oldcolor = color @@ -264,7 +277,7 @@ Difficulty: Hard C.moving = 3 C.moving_dir = pick_n_take(cardinal_copy) SLEEP_CHECK_DEATH(8 + target_slowness) - chaser_cooldown = world.time + initial(chaser_cooldown) + update_cooldowns(list(COOLDOWN_UPDATE_SET_CHASER = chaser_cooldown_time)) animate(src, color = oldcolor, time = 8) addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 8) SLEEP_CHECK_DEATH(8) @@ -301,7 +314,7 @@ Difficulty: Hard return if((istype(get_area(T), /area/ruin/unpowered/hierophant) || istype(get_area(src), /area/ruin/unpowered/hierophant)) && victim != src) return - arena_cooldown = world.time + initial(arena_cooldown) + update_cooldowns(list(COOLDOWN_UPDATE_SET_ARENA = arena_cooldown_time)) for(var/d in GLOB.cardinals) INVOKE_ASYNC(src, .proc/arena_squares, T, d) for(var/t in RANGE_TURFS(11, T)) @@ -451,7 +464,7 @@ Difficulty: Hard if(L.stat != DEAD) if(ranged_cooldown <= world.time) calculate_rage() - ranged_cooldown = world.time + max(5, ranged_cooldown_time - anger_modifier * 0.75) + update_cooldowns(list(COOLDOWN_UPDATE_SET_RANGED = max(0.5 SECONDS, ranged_cooldown_time - anger_modifier * 0.75)), ignore_staggered = TRUE) INVOKE_ASYNC(src, .proc/burst, get_turf(src)) else burst_range = 3 diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm index 724623912be..0146ec4b2d3 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm @@ -38,7 +38,7 @@ del_on_death = TRUE retreat_distance = 5 minimum_distance = 5 - ranged_cooldown_time = 20 + ranged_cooldown_time = 2 SECONDS gps_name = "Echoing Signal" achievement_type = /datum/award/achievement/boss/legion_kill crusher_achievement_type = /datum/award/achievement/boss/legion_crusher @@ -84,7 +84,7 @@ /mob/living/simple_animal/hostile/megafauna/legion/OpenFire(the_target) if(charging) return - ranged_cooldown = world.time + ranged_cooldown_time + update_cooldowns(list(COOLDOWN_UPDATE_SET_RANGED = ranged_cooldown_time), ignore_staggered = TRUE) if(client) switch(chosen_attack) @@ -203,7 +203,7 @@ last_legion = FALSE break if(last_legion) - loot = list(/obj/item/staff/storm) + loot = list(/obj/item/storm_staff) elimination = FALSE else if(prob(20)) //20% chance for sick lootz. loot = list(/obj/structure/closet/crate/necropolis/tendril) @@ -251,72 +251,6 @@ faction = L.faction.Copy() GiveTarget(L.target) -//Loot - -/obj/item/staff/storm - name = "staff of storms" - desc = "An ancient staff retrieved from the remains of Legion. The wind stirs as you move it." - icon_state = "staffofstorms" - inhand_icon_state = "staffofstorms" - icon = 'icons/obj/guns/magic.dmi' - slot_flags = ITEM_SLOT_BACK - w_class = WEIGHT_CLASS_BULKY - force = 25 - damtype = BURN - hitsound = 'sound/weapons/sear.ogg' - wound_bonus = -40 - bare_wound_bonus = 20 - var/storm_type = /datum/weather/ash_storm - var/storm_nextuse = 0 - var/staff_cooldown = 20 SECONDS // The minimum time between uses. - var/storm_telegraph_duration = 10 SECONDS - var/storm_duration = 10 SECONDS - var/static/list/excluded_areas = list() - -/obj/item/staff/storm/attack_self(mob/user) - if(storm_nextuse > world.time) - to_chat(user, span_warning("The staff is still recharging!")) - return - - var/area/user_area = get_area(user) - var/turf/user_turf = get_turf(user) - if(!user_area || !user_turf || (user_area.type in excluded_areas)) - to_chat(user, span_warning("Something is preventing you from using the staff here.")) - return - var/datum/weather/A - for(var/V in SSweather.processing) - var/datum/weather/W = V - if((user_turf.z in W.impacted_z_levels) && W.area_type == user_area.type) - A = W - break - - if(A) - if(A.stage != END_STAGE) - if(A.stage == WIND_DOWN_STAGE) - to_chat(user, span_warning("The storm is already ending! It would be a waste to use the staff now.")) - return - user.visible_message(span_warning("[user] holds [src] skywards as an orange beam travels into the sky!"), \ - span_notice("You hold [src] skyward, dispelling the storm!")) - playsound(user, 'sound/magic/staff_change.ogg', 200, FALSE) - A.wind_down() - log_game("[user] ([key_name(user)]) has dispelled a storm at [AREACOORD(user_turf)]") - return - else - A = new storm_type(list(user_turf.z)) - A.name = "staff storm" - log_game("[user] ([key_name(user)]) has summoned [A] at [AREACOORD(user_turf)]") - if (is_special_character(user)) - message_admins("[A] has been summoned in [ADMIN_VERBOSEJMP(user_turf)] by [ADMIN_LOOKUPFLW(user)], a non-antagonist") - A.area_type = user_area.type - A.telegraph_duration = storm_telegraph_duration - A.end_duration = storm_duration - - user.visible_message(span_warning("[user] holds [src] skywards as red lightning crackles into the sky!"), \ - span_notice("You hold [src] skyward, calling down a terrible storm!")) - playsound(user, 'sound/magic/staff_change.ogg', 200, FALSE) - A.telegraph() - storm_nextuse = world.time + staff_cooldown - ///A basic turret that shoots at nearby mobs. Intended to be used for the legion megafauna. /obj/structure/legionturret name = "\improper Legion sentinel" diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm index 128b2f3687f..bf7d46a7789 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm @@ -60,9 +60,9 @@ GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swa AIStatus = AI_OFF del_on_death = TRUE var/swarmer_spawn_cooldown = 0 - var/swarmer_spawn_cooldown_amt = 150 //Deciseconds between the swarmers we spawn + var/swarmer_spawn_cooldown_amt = 15 SECONDS //Deciseconds between the swarmers we spawn var/call_help_cooldown = 0 - var/call_help_cooldown_amt = 150 //Deciseconds between calling swarmers to help us when attacked + var/call_help_cooldown_amt = 15 SECONDS //Deciseconds between calling swarmers to help us when attacked var/static/list/swarmer_caps @@ -80,16 +80,26 @@ GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swa if(.) var/createtype = GetUncappedAISwarmerType() if(createtype && world.time > swarmer_spawn_cooldown && GLOB.AISwarmers.len < (GetTotalAISwarmerCap()*0.5)) - swarmer_spawn_cooldown = world.time + swarmer_spawn_cooldown_amt + update_cooldowns(list(COOLDOWN_UPDATE_SET_SPAWN = swarmer_spawn_cooldown_amt)) new createtype(loc) /mob/living/simple_animal/hostile/megafauna/swarmer_swarm_beacon/adjustHealth(amount, updating_health = TRUE, forced = FALSE) . = ..() if(. > 0 && world.time > call_help_cooldown) - call_help_cooldown = world.time + call_help_cooldown_amt + update_cooldowns(list(COOLDOWN_UPDATE_SET_HELP = call_help_cooldown_amt)) summon_backup(25) //long range, only called max once per 15 seconds, so it's not deathlag +/mob/living/simple_animal/hostile/megafauna/swarmer_swarm_beacon/update_cooldowns(list/cooldown_updates, ignore_staggered = FALSE) + . = ..() + if(cooldown_updates[COOLDOWN_UPDATE_SET_SPAWN]) + swarmer_spawn_cooldown = world.time + cooldown_updates[COOLDOWN_UPDATE_SET_SPAWN] + if(cooldown_updates[COOLDOWN_UPDATE_ADD_SPAWN]) + swarmer_spawn_cooldown += cooldown_updates[COOLDOWN_UPDATE_ADD_SPAWN] + if(cooldown_updates[COOLDOWN_UPDATE_SET_HELP]) + call_help_cooldown = world.time + cooldown_updates[COOLDOWN_UPDATE_SET_HELP] + if(cooldown_updates[COOLDOWN_UPDATE_ADD_HELP]) + call_help_cooldown += cooldown_updates[COOLDOWN_UPDATE_ADD_HELP] //SWARMER AI //AI versions of the swarmer mini-antag diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/wendigo.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/wendigo.dm index 7255eb5cb11..0223f1e4dfd 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/wendigo.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/wendigo.dm @@ -95,7 +95,7 @@ Difficulty: Hard starting = get_turf(src) /mob/living/simple_animal/hostile/megafauna/wendigo/OpenFire() - SetRecoveryTime(10 SECONDS) + update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 10 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 10 SECONDS)) if(WENDIGO_ENRAGED) speed = 4 move_to_delay = 4 @@ -164,7 +164,7 @@ Difficulty: Hard /mob/living/simple_animal/hostile/megafauna/wendigo/proc/heavy_stomp() can_move = FALSE wendigo_slam(src, 5, 3 - WENDIGO_ENRAGED, 8) - SetRecoveryTime(0 SECONDS) + update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 0 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 0 SECONDS)) can_move = TRUE /// Teleports to a location 4 turfs away from the enemy in view @@ -185,7 +185,7 @@ Difficulty: Hard if(target) shockwave.original = target shockwave.fire() - SetRecoveryTime(0 SECONDS) + update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 0 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 0 SECONDS)) /mob/living/simple_animal/hostile/megafauna/wendigo/proc/teleport(range = 6) var/list/possible_ends = view(range, target.loc) - view(range - 1, target.loc) @@ -209,7 +209,7 @@ Difficulty: Hard to_chat(dizzy_target, span_danger("The wendigo screams loudly!")) SLEEP_CHECK_DEATH(1 SECONDS) spiral_attack() - SetRecoveryTime(3 SECONDS) + update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 3 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 3 SECONDS)) SLEEP_CHECK_DEATH(3 SECONDS) can_move = TRUE diff --git a/code/modules/projectiles/guns/special/meat_hook.dm b/code/modules/projectiles/guns/special/meat_hook.dm new file mode 100644 index 00000000000..21041093a7a --- /dev/null +++ b/code/modules/projectiles/guns/special/meat_hook.dm @@ -0,0 +1,77 @@ +//Meat Hook + +/obj/item/gun/magic/hook + name = "meat hook" + desc = "Mid or feed." + ammo_type = /obj/item/ammo_casing/magic/hook + icon_state = "hook" + inhand_icon_state = "hook" + lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' + fire_sound = 'sound/weapons/batonextend.ogg' + pinless = TRUE + max_charges = 1 + item_flags = NEEDS_PERMIT | NOBLUDGEON + sharpness = SHARP_POINTY + force = 18 + +/obj/item/gun/magic/hook/shoot_with_empty_chamber(mob/living/user) + to_chat(user, span_warning("[src] isn't ready to fire yet!")) + +/obj/item/ammo_casing/magic/hook + name = "hook" + desc = "A hook." + projectile_type = /obj/projectile/hook + caliber = CALIBER_HOOK + icon_state = "hook" + firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect/energy + +/obj/projectile/hook + name = "hook" + icon_state = "hook" + icon = 'icons/obj/lavaland/artefacts.dmi' + pass_flags = PASSTABLE + damage = 20 + stamina = 20 + armour_penetration = 60 + damage_type = BRUTE + hitsound = 'sound/effects/splat.ogg' + var/chain + var/knockdown_time = (0.5 SECONDS) + +/obj/projectile/hook/fire(setAngle) + if(firer) + chain = firer.Beam(src, icon_state = "chain") + ..() + //TODO: root the firer until the chain returns + +/obj/projectile/hook/on_hit(atom/target) + . = ..() + if(ismovable(target)) + var/atom/movable/A = target + if(A.anchored) + return + A.visible_message(span_danger("[A] is snagged by [firer]'s hook!")) + new /datum/forced_movement(A, get_turf(firer), 5, TRUE) + if (isliving(target)) + var/mob/living/fresh_meat = target + fresh_meat.Knockdown(knockdown_time) + return + //TODO: keep the chain beamed to A + //TODO: needs a callback to delete the chain + +/obj/projectile/hook/Destroy() + qdel(chain) + return ..() + +//just a nerfed version of the real thing for the bounty hunters. +/obj/item/gun/magic/hook/bounty + name = "hook" + ammo_type = /obj/item/ammo_casing/magic/hook/bounty + +/obj/item/ammo_casing/magic/hook/bounty + projectile_type = /obj/projectile/hook/bounty + +/obj/projectile/hook/bounty + damage = 0 + stamina = 40 diff --git a/code/modules/ruins/lavalandruin_code/biodome_winter.dm b/code/modules/ruins/lavalandruin_code/biodome_winter.dm new file mode 100644 index 00000000000..a529f92c36e --- /dev/null +++ b/code/modules/ruins/lavalandruin_code/biodome_winter.dm @@ -0,0 +1,49 @@ +/obj/structure/displaycase/freezeray + start_showpiece_type = /obj/item/freeze_cube + alert = FALSE + +/obj/item/freeze_cube + name = "freeze cube" + desc = "A block of semi-clear ice treated with chemicals to behave as a throwable weapon. \ + Somehow, it does not transfer its freezing temperatures until it comes into contact with a living creature." + icon = 'icons/obj/lavaland/artefacts.dmi' + icon_state = "freeze_cube" + throwforce = 10 + damtype = BURN + var/cooldown_time = 5 SECONDS + COOLDOWN_DECLARE(freeze_cooldown) + +/obj/item/freeze_cube/examine(mob/user) + . = ..() + . += span_notice("Throw this at objects or creatures to freeze them, it will boomerang back so be cautious!") + +/obj/item/freeze_cube/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, quickstart = TRUE) + . = ..() + if(!.) + return + icon_state = "freeze_cube_thrown" + addtimer(VARSET_CALLBACK(src, icon_state, initial(icon_state)), 1 SECONDS) + +/obj/item/freeze_cube/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) + icon_state = initial(icon_state) + var/caught = hit_atom.hitby(src, FALSE, FALSE, throwingdatum=throwingdatum) + var/mob/thrown_by = thrownby?.resolve() + if(ismovable(hit_atom) && !caught && (!thrown_by || thrown_by && COOLDOWN_FINISHED(src, freeze_cooldown))) + freeze(hit_atom) + if(thrown_by && !caught) + addtimer(CALLBACK(src, /atom/movable.proc/throw_at, thrown_by, throw_range+2, throw_speed, null, TRUE), 1) + +/obj/item/freeze_cube/proc/freeze(atom/movable/hit_atom) + playsound(src, 'sound/effects/glassbr3.ogg', 50, TRUE) + COOLDOWN_START(src, freeze_cooldown, cooldown_time) + if(isobj(hit_atom)) + var/obj/hit_object = hit_atom + if(hit_object.resistance_flags & FREEZE_PROOF) + hit_object.visible_message(span_warning("[hit_object] is freeze-proof!")) + return + if(!(hit_object.obj_flags & FROZEN)) + hit_object.make_frozen_visual() + else if(isliving(hit_atom)) + var/mob/living/hit_mob = hit_atom + walk(hit_mob, 0) //stops them mid pathing even if they're stunimmune + hit_mob.apply_status_effect(/datum/status_effect/ice_block_talisman, 3 SECONDS) diff --git a/code/modules/surgery/organs/augments_arms.dm b/code/modules/surgery/organs/augments_arms.dm index 2cb8fcfe526..acd69fc0086 100644 --- a/code/modules/surgery/organs/augments_arms.dm +++ b/code/modules/surgery/organs/augments_arms.dm @@ -13,6 +13,10 @@ var/list/items_list = list()// I would use contents, but they shuffle on every activation/deactivation leading to interface inconsistencies. /// You can use this var for item path, it would be converted into an item on New(). var/obj/item/active_item + /// Sound played when extending + var/extend_sound = 'sound/mecha/mechmove03.ogg' + /// Sound played when retracting + var/retract_sound = 'sound/mecha/mechmove03.ogg' /obj/item/organ/cyberimp/arm/Initialize() . = ..() @@ -53,7 +57,8 @@ /obj/item/organ/cyberimp/arm/examine(mob/user) . = ..() - . += span_info("[src] is assembled in the [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm configuration. You can use a screwdriver to reassemble it.") + if(status == ORGAN_ROBOTIC) + . += span_info("[src] is assembled in the [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm configuration. You can use a screwdriver to reassemble it.") /obj/item/organ/cyberimp/arm/screwdriver_act(mob/living/user, obj/item/screwtool) . = ..() @@ -89,7 +94,7 @@ /obj/item/organ/cyberimp/arm/emp_act(severity) . = ..() - if(. & EMP_PROTECT_SELF) + if(. & EMP_PROTECT_SELF || status == ORGAN_ROBOTIC) return if(prob(15/severity) && owner) to_chat(owner, span_warning("The electromagnetic pulse causes [src] to malfunction!")) @@ -115,13 +120,13 @@ if(!active_item || (active_item in src)) return - owner.visible_message(span_notice("[owner] retracts [active_item] back into [owner.p_their()] [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm."), + owner?.visible_message(span_notice("[owner] retracts [active_item] back into [owner.p_their()] [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm."), span_notice("[active_item] snaps back into your [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm."), span_hear("You hear a short mechanical noise.")) owner.transferItemToLoc(active_item, src, TRUE) active_item = null - playsound(get_turf(owner), 'sound/mecha/mechmove03.ogg', 50, TRUE) + playsound(get_turf(owner), retract_sound, 50, TRUE) /obj/item/organ/cyberimp/arm/proc/Extend(obj/item/augment) if(!(augment in src)) @@ -157,7 +162,7 @@ owner.visible_message(span_notice("[owner] extends [active_item] from [owner.p_their()] [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm."), span_notice("You extend [active_item] from your [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm."), span_hear("You hear a short mechanical noise.")) - playsound(get_turf(owner), 'sound/mecha/mechmove03.ogg', 50, TRUE) + playsound(get_turf(owner), extend_sound, 50, TRUE) /obj/item/organ/cyberimp/arm/ui_action_click() if((organ_flags & ORGAN_FAILING) || (!active_item && !contents.len)) diff --git a/icons/effects/32x96.dmi b/icons/effects/32x96.dmi index e7a465c26ac..b5eed61222b 100644 Binary files a/icons/effects/32x96.dmi and b/icons/effects/32x96.dmi differ diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi index 9789f884183..6ce277a6f4c 100644 Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ diff --git a/icons/effects/mouse_pointers/scan_target.dmi b/icons/effects/mouse_pointers/scan_target.dmi new file mode 100644 index 00000000000..bc3e2eaf16b Binary files /dev/null and b/icons/effects/mouse_pointers/scan_target.dmi differ diff --git a/icons/mob/actions/actions_items.dmi b/icons/mob/actions/actions_items.dmi index 5e947b4c8a2..cad0528e716 100644 Binary files a/icons/mob/actions/actions_items.dmi and b/icons/mob/actions/actions_items.dmi differ diff --git a/icons/mob/clothing/back.dmi b/icons/mob/clothing/back.dmi index 7f643df4bd5..f7d3cf068e7 100644 Binary files a/icons/mob/clothing/back.dmi and b/icons/mob/clothing/back.dmi differ diff --git a/icons/mob/clothing/eyes.dmi b/icons/mob/clothing/eyes.dmi index 0826b0e8d3a..41a265c5ef7 100644 Binary files a/icons/mob/clothing/eyes.dmi and b/icons/mob/clothing/eyes.dmi differ diff --git a/icons/mob/clothing/head.dmi b/icons/mob/clothing/head.dmi index efa00fc46fe..ee3b6d1a3df 100644 Binary files a/icons/mob/clothing/head.dmi and b/icons/mob/clothing/head.dmi differ diff --git a/icons/mob/inhands/64x64_lefthand.dmi b/icons/mob/inhands/64x64_lefthand.dmi index 8d8c7a1ccae..f5024467798 100644 Binary files a/icons/mob/inhands/64x64_lefthand.dmi and b/icons/mob/inhands/64x64_lefthand.dmi differ diff --git a/icons/mob/inhands/64x64_righthand.dmi b/icons/mob/inhands/64x64_righthand.dmi index 6e83daf4983..9061d87a3e4 100644 Binary files a/icons/mob/inhands/64x64_righthand.dmi and b/icons/mob/inhands/64x64_righthand.dmi differ diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi index ddcfd7d0780..19a852b1225 100644 Binary files a/icons/mob/inhands/items_lefthand.dmi and b/icons/mob/inhands/items_lefthand.dmi differ diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi index 4ac249f2eb3..0c650166de4 100644 Binary files a/icons/mob/inhands/items_righthand.dmi and b/icons/mob/inhands/items_righthand.dmi differ diff --git a/icons/mob/inhands/weapons/melee_lefthand.dmi b/icons/mob/inhands/weapons/melee_lefthand.dmi index e40e7ed8572..dcda17cf871 100644 Binary files a/icons/mob/inhands/weapons/melee_lefthand.dmi and b/icons/mob/inhands/weapons/melee_lefthand.dmi differ diff --git a/icons/mob/inhands/weapons/melee_righthand.dmi b/icons/mob/inhands/weapons/melee_righthand.dmi index 4920754a163..0bbfb5938d1 100644 Binary files a/icons/mob/inhands/weapons/melee_righthand.dmi and b/icons/mob/inhands/weapons/melee_righthand.dmi differ diff --git a/icons/mob/inhands/weapons/staves_lefthand.dmi b/icons/mob/inhands/weapons/staves_lefthand.dmi index e2e70bc2db2..8aec740579f 100644 Binary files a/icons/mob/inhands/weapons/staves_lefthand.dmi and b/icons/mob/inhands/weapons/staves_lefthand.dmi differ diff --git a/icons/mob/inhands/weapons/staves_righthand.dmi b/icons/mob/inhands/weapons/staves_righthand.dmi index 3e04a41a69b..6cba11eef9c 100644 Binary files a/icons/mob/inhands/weapons/staves_righthand.dmi and b/icons/mob/inhands/weapons/staves_righthand.dmi differ diff --git a/icons/mob/inhands/weapons/swords_lefthand.dmi b/icons/mob/inhands/weapons/swords_lefthand.dmi index fcc79c88591..293976209ea 100644 Binary files a/icons/mob/inhands/weapons/swords_lefthand.dmi and b/icons/mob/inhands/weapons/swords_lefthand.dmi differ diff --git a/icons/mob/inhands/weapons/swords_righthand.dmi b/icons/mob/inhands/weapons/swords_righthand.dmi index 240e8929d5c..a7bd228ad0d 100644 Binary files a/icons/mob/inhands/weapons/swords_righthand.dmi and b/icons/mob/inhands/weapons/swords_righthand.dmi differ diff --git a/icons/mob/telegraphing/telegraph.dmi b/icons/mob/telegraphing/telegraph.dmi new file mode 100644 index 00000000000..21e6788acd1 Binary files /dev/null and b/icons/mob/telegraphing/telegraph.dmi differ diff --git a/icons/obj/clothing/glasses.dmi b/icons/obj/clothing/glasses.dmi index c599c4f9194..b4167e069cb 100644 Binary files a/icons/obj/clothing/glasses.dmi and b/icons/obj/clothing/glasses.dmi differ diff --git a/icons/obj/clothing/hats.dmi b/icons/obj/clothing/hats.dmi index 11828bb1480..413250ef8c4 100644 Binary files a/icons/obj/clothing/hats.dmi and b/icons/obj/clothing/hats.dmi differ diff --git a/icons/obj/guns/magic.dmi b/icons/obj/guns/magic.dmi index bbdbeeef9d2..8888a396196 100644 Binary files a/icons/obj/guns/magic.dmi and b/icons/obj/guns/magic.dmi differ diff --git a/icons/obj/guns/projectiles.dmi b/icons/obj/guns/projectiles.dmi index 7a0509129cb..da455fd4dae 100644 Binary files a/icons/obj/guns/projectiles.dmi and b/icons/obj/guns/projectiles.dmi differ diff --git a/icons/obj/lavaland/artefacts.dmi b/icons/obj/lavaland/artefacts.dmi index afb33c18490..589c5ee0a27 100644 Binary files a/icons/obj/lavaland/artefacts.dmi and b/icons/obj/lavaland/artefacts.dmi differ diff --git a/tgstation.dme b/tgstation.dme index e3b990c117a..b735b7f40e7 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1422,6 +1422,7 @@ #include "code\game\objects\structures\grille.dm" #include "code\game\objects\structures\guillotine.dm" #include "code\game\objects\structures\guncase.dm" +#include "code\game\objects\structures\gym.dm" #include "code\game\objects\structures\headpike.dm" #include "code\game\objects\structures\hivebot.dm" #include "code\game\objects\structures\holosign.dm" @@ -2114,7 +2115,6 @@ #include "code\modules\clothing\shoes\sneakers.dm" #include "code\modules\clothing\shoes\wheelys.dm" #include "code\modules\clothing\spacesuits\_spacesuits.dm" -#include "code\modules\clothing\spacesuits\berserker.dm" #include "code\modules\clothing\spacesuits\bountyhunter.dm" #include "code\modules\clothing\spacesuits\chronosuit.dm" #include "code\modules\clothing\spacesuits\freedom.dm" @@ -2609,13 +2609,13 @@ #include "code\modules\mining\equipment\regenerative_core.dm" #include "code\modules\mining\equipment\resonator.dm" #include "code\modules\mining\equipment\survival_pod.dm" -#include "code\modules\mining\equipment\vendor_items.dm" #include "code\modules\mining\equipment\wormhole_jaunter.dm" #include "code\modules\mining\laborcamp\laborshuttle.dm" #include "code\modules\mining\laborcamp\laborstacker.dm" #include "code\modules\mining\lavaland\ash_flora.dm" +#include "code\modules\mining\lavaland\megafauna_loot.dm" #include "code\modules\mining\lavaland\necropolis_chests.dm" -#include "code\modules\mining\lavaland\ruins\gym.dm" +#include "code\modules\mining\lavaland\tendril_loot.dm" #include "code\modules\mob\emote.dm" #include "code\modules\mob\inventory.dm" #include "code\modules\mob\login.dm" @@ -2917,6 +2917,7 @@ #include "code\modules\mob\living\simple_animal\hostile\jungle\mega_arachnid.dm" #include "code\modules\mob\living\simple_animal\hostile\jungle\mook.dm" #include "code\modules\mob\living\simple_animal\hostile\jungle\seedling.dm" +#include "code\modules\mob\living\simple_animal\hostile\megafauna\_megafauna.dm" #include "code\modules\mob\living\simple_animal\hostile\megafauna\blood_drunk_miner.dm" #include "code\modules\mob\living\simple_animal\hostile\megafauna\bubblegum.dm" #include "code\modules\mob\living\simple_animal\hostile\megafauna\clockwork_knight.dm" @@ -2925,7 +2926,6 @@ #include "code\modules\mob\living\simple_animal\hostile\megafauna\drake.dm" #include "code\modules\mob\living\simple_animal\hostile\megafauna\hierophant.dm" #include "code\modules\mob\living\simple_animal\hostile\megafauna\legion.dm" -#include "code\modules\mob\living\simple_animal\hostile\megafauna\megafauna.dm" #include "code\modules\mob\living\simple_animal\hostile\megafauna\swarmer.dm" #include "code\modules\mob\living\simple_animal\hostile\megafauna\wendigo.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\basilisk.dm" @@ -3206,6 +3206,7 @@ #include "code\modules\projectiles\guns\special\blastcannon.dm" #include "code\modules\projectiles\guns\special\chem_gun.dm" #include "code\modules\projectiles\guns\special\grenade_launcher.dm" +#include "code\modules\projectiles\guns\special\meat_hook.dm" #include "code\modules\projectiles\guns\special\medbeam.dm" #include "code\modules\projectiles\guns\special\syringe_gun.dm" #include "code\modules\projectiles\projectile\beams.dm" @@ -3399,6 +3400,7 @@ #include "code\modules\ruins\icemoonruin_code\mailroom.dm" #include "code\modules\ruins\icemoonruin_code\wrath.dm" #include "code\modules\ruins\lavalandruin_code\biodome_clown_planet.dm" +#include "code\modules\ruins\lavalandruin_code\biodome_winter.dm" #include "code\modules\ruins\lavalandruin_code\elephantgraveyard.dm" #include "code\modules\ruins\lavalandruin_code\pizzaparty.dm" #include "code\modules\ruins\lavalandruin_code\puzzle.dm"