From d00b4e66bbd58ff4e8ac045e5d2c5bf04db63ad0 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Fri, 30 Aug 2024 01:12:29 -0400 Subject: [PATCH 01/35] fixes some bugs (#6679) - syringes & blood - turrets - a cargo voucher --- code/game/click/items.dm | 1 + code/game/machinery/turrets/turret-ai_holder.dm | 5 +++++ code/game/machinery/turrets/turret.dm | 7 ++++--- .../engineering_points_vendor.dm | 4 ++-- code/modules/projectiles/projectile.dm | 11 +++++++---- code/modules/reagents/reagent_containers/syringes.dm | 2 +- 6 files changed, 20 insertions(+), 10 deletions(-) diff --git a/code/game/click/items.dm b/code/game/click/items.dm index 368585390948..9514e4b15f66 100644 --- a/code/game/click/items.dm +++ b/code/game/click/items.dm @@ -35,6 +35,7 @@ // - item use & receive item use (item_interaction() on /atom, definiteily) // - tool use & receive tool use (we already have tool_interaction() on /atom) // - melee attack & receive melee attack (melee_interaction() on /atom? not melee_act directly?) + // - melee attack shouldn't require attackby() to allow it to, it should be automatic on harm intent (?) // - the item should have final say but we need a way to allow click redirections so.. if(resolve_attackby(target, user, params, null, .)) return CLICKCHAIN_DO_NOT_PROPAGATE diff --git a/code/game/machinery/turrets/turret-ai_holder.dm b/code/game/machinery/turrets/turret-ai_holder.dm index 330b4d8a4247..6c2a3d1a6669 100644 --- a/code/game/machinery/turrets/turret-ai_holder.dm +++ b/code/game/machinery/turrets/turret-ai_holder.dm @@ -35,6 +35,11 @@ set_ticking(idle_retarget_pulse_time) /datum/ai_holder/turret/tick(cycles) + var/obj/machinery/porta_turret/turret = agent + // check if we should do anything + if(turret.disabled || !turret.enabled) + idle() + return // first, evaluate var/found_in_wake_range = continuous_evaluation() // then, diff --git a/code/game/machinery/turrets/turret.dm b/code/game/machinery/turrets/turret.dm index 4a8590311c1d..265f3c8afd84 100644 --- a/code/game/machinery/turrets/turret.dm +++ b/code/game/machinery/turrets/turret.dm @@ -441,7 +441,8 @@ else to_chat(user, "Access denied.") - ..() + else + return ..() /obj/machinery/porta_turret/emag_act(remaining_charges, mob/user) if(!emagged) @@ -574,7 +575,7 @@ if(assess_perp(L) < 4) return TURRET_NOT_TARGET //if threat level < 4, keep going - if(L.stat != CONSCIOUS) //if the perp is lying down, it's still a target but a less-important target + if(L.stat != CONSCIOUS && (lethal || emagged)) //if the perp is lying down, it's still a target but a less-important target return check_down ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET return TURRET_PRIORITY_TARGET //if the perp has passed all previous tests, congrats, it is now a "shoot-me!" nominee @@ -648,7 +649,7 @@ * @return TRUE on success */ /obj/machinery/porta_turret/proc/try_fire_at(atom/target, angle) - if(disabled || is_integrity_broken()) + if(disabled || !enabled || is_integrity_broken()) return FALSE if(is_on_cooldown()) return FALSE diff --git a/code/modules/mining/ore_redemption_machine/engineering_points_vendor.dm b/code/modules/mining/ore_redemption_machine/engineering_points_vendor.dm index b55ba7453580..f2ad1e9bebe6 100644 --- a/code/modules/mining/ore_redemption_machine/engineering_points_vendor.dm +++ b/code/modules/mining/ore_redemption_machine/engineering_points_vendor.dm @@ -167,7 +167,7 @@ order.comment = "Voucher redemption" order.ordered_at = stationdate2text() + " - " + stationtime2text() order.status = SUP_ORDER_APPROVED //auto approved - order.approved_by = "[src]" + order.approved_by = "[user]" order.approved_at = stationdate2text() + " - " + stationtime2text() SSsupply.order_history += order//tell supply the order exists. @@ -224,7 +224,7 @@ name = "Laser reflector voucher" desc = "A voucher redeemable, at any NT cargo department, for a single laser reflector." icon_state = "engineering_voucher" - redeemable_for = new /datum/supply_pack/nanotrasen/engineering/engine/fusion_fuel_compressor + redeemable_for = new /datum/supply_pack/nanotrasen/engineering/reflector /obj/item/engineering_mystical_tech name = "XYE" diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 823dbbb39735..ac7164dcc9df 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -492,10 +492,13 @@ if(trajectory_moving_to) // create tracers var/datum/point/visual_impact_point = get_intersection_point(trajectory_moving_to) - // kick it forwards a bit - visual_impact_point.shift_in_projectile_angle(angle, 2) - // draw - finalize_hitscan_tracers(visual_impact_point, impact_effect = TRUE) + if(visual_impact_point) + // kick it forwards a bit + visual_impact_point.shift_in_projectile_angle(angle, 2) + // draw + finalize_hitscan_tracers(visual_impact_point, impact_effect = TRUE) + else + finalize_hitscan_tracers(impact_effect = TRUE, kick_forwards = 32) else finalize_hitscan_tracers(impact_effect = TRUE, kick_forwards = 32) diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index 07a18cc86494..575fcd642760 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -119,7 +119,7 @@ B = T.take_blood(src,amount) drawing = 0 - if (B) + if (B && !(B in reagents.reagent_list)) reagents.reagent_list += B reagents.update_total() on_reagent_change() From 6c5be2def0de6ce890a561af4e69def0b8be7011 Mon Sep 17 00:00:00 2001 From: InquisitiveEpidemic <52363810+InquisitiveEpidemic@users.noreply.github.com> Date: Fri, 30 Aug 2024 20:25:04 -0500 Subject: [PATCH 02/35] Adds PKA capacity mods (#6696) adds PKA Capacity modes, that increase total capacity at the cost of damage ## About The Pull Request Adds a mod capacity mod for Proto Kinetic Accelerators, increasing capacity by 15 and decreasing damage by 6 each with a maximum of 2 installable mods. ## Why It's Good For The Game This adds an option that fills the gap for the to be removed damage increase mod, exchanging damage for more customization, ultimately giving a small but useful increase in options. The 6:15 damage to mod capacity means it will not be viable to increase damage if damage mods end up being preserved ## Changelog :cl: add: PKA Capacity Mod :add: Mining vendor sells PKA Capacity Mod /:cl: --- .../ore_redemption_machine/equipment_vendor.dm | 1 + .../projectiles/guns/energy/kinetic_accelerator.dm | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/code/modules/mining/ore_redemption_machine/equipment_vendor.dm b/code/modules/mining/ore_redemption_machine/equipment_vendor.dm index 2b765fa5120a..5b9693b6ccb0 100644 --- a/code/modules/mining/ore_redemption_machine/equipment_vendor.dm +++ b/code/modules/mining/ore_redemption_machine/equipment_vendor.dm @@ -75,6 +75,7 @@ new /datum/data/mining_equipment("KA Range Increase", /obj/item/ka_modkit/range, 1000), new /datum/data/mining_equipment("KA Damage Increase", /obj/item/ka_modkit/damage, 1000), new /datum/data/mining_equipment("KA Cooldown Decrease", /obj/item/ka_modkit/cooldown, 1200), + new /datum/data/mining_equipment("KA Capacity Increase", /obj/item/ka_modkit/capacity, 1500), new /datum/data/mining_equipment("KA Holster", /obj/item/clothing/accessory/holster/waist/kinetic_accelerator, 350), new /datum/data/mining_equipment("Fine Excavation Kit - Chisels",/obj/item/storage/excavation, 500), new /datum/data/mining_equipment("Fine Excavation Kit - Measuring Tape",/obj/item/measuring_tape, 125), diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm index a5d8cbf30dfa..c20f29e8e20c 100644 --- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm +++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm @@ -406,6 +406,20 @@ minebot_exclusive = TRUE +//Capacity +/obj/item/ka_modkit/capacity + name = "capacity increase" + desc = "A cutdown accelerator frame that increases mod capacity while reducing damage. Not compatible with minebots." + modifier = -6 + cost = -15 + maximum_of_type = 2 + minebot_upgrade = FALSE + denied_type = /obj/item/ka_modkit/capacity + +/obj/item/ka_modkit/capacity/modify_projectile(obj/projectile/kinetic/K) + K.damage += modifier + + //AoE blasts /obj/item/ka_modkit/aoe modifier = 0 From 5c1f96824af0b2a73dbecef50da67ab9d66cb6f6 Mon Sep 17 00:00:00 2001 From: Niezan Date: Fri, 30 Aug 2024 18:25:13 -0700 Subject: [PATCH 03/35] Dart gun & 5.7x28mm mag fixs (#6685) ## About The Pull Request fixes 5.7x28mm mags as well as dart gun mag fixs. also adds tiny dart sprites and fixes the magazine texture. ## Why It's Good For The Game bug fixs + fixs sprites. ## Changelog :cl: fix: 5.7x28mm mags can now be loaded / unloaded properly. fix: dart gun magazines can now be loaded / unloaded properly. imageadd: dart sprite added. fix: dart magazines now have the correct texture. /:cl: --- .../ammunition/calibers/normal/a5_7mm.dm | 1 + .../ammunition/calibers/special/dart.dm | 8 +++++--- icons/modules/projectiles/casings/slim.dmi | Bin 1299 -> 1347 bytes 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/code/modules/projectiles/ammunition/calibers/normal/a5_7mm.dm b/code/modules/projectiles/ammunition/calibers/normal/a5_7mm.dm index 567dbf5fece8..ad7e3d669142 100644 --- a/code/modules/projectiles/ammunition/calibers/normal/a5_7mm.dm +++ b/code/modules/projectiles/ammunition/calibers/normal/a5_7mm.dm @@ -33,6 +33,7 @@ /obj/item/ammo_magazine/a5_7mm ammo_caliber = /datum/ammo_caliber/a5_7mm + ammo_preload = /obj/item/ammo_casing/a5_7mm /obj/item/ammo_magazine/a5_7mm/nt_les name = "magazine (5.7x28mm)" diff --git a/code/modules/projectiles/ammunition/calibers/special/dart.dm b/code/modules/projectiles/ammunition/calibers/special/dart.dm index a52d0c1939dc..118dac8ddb33 100644 --- a/code/modules/projectiles/ammunition/calibers/special/dart.dm +++ b/code/modules/projectiles/ammunition/calibers/special/dart.dm @@ -6,12 +6,13 @@ /obj/item/ammo_casing/dart/chemdart name = "chemical dart" desc = "A casing containing a small hardened, hollow dart." - icon_state = "dartcasing" + icon_state = "dart" caliber = /datum/ammo_caliber/dart projectile_type = /obj/projectile/bullet/chemdart /obj/item/ammo_casing/dart/chemdart/small name = "short chemical dart" + icon_state = "dartsmall" desc = "A casing containing a small hardened, hollow dart." projectile_type = /obj/projectile/bullet/chemdart/small @@ -21,6 +22,7 @@ name = "dart cartridge" desc = "A rack of hollow darts." + icon = 'icons/modules/projectiles/magazines/darts.dmi' icon_state = "darts-5" base_icon_state = "darts" rendering_system = GUN_RENDERING_STATES @@ -30,7 +32,7 @@ origin_tech = list(TECH_MATERIAL = 2) magazine_type = MAGAZINE_TYPE_NORMAL ammo_caliber = /datum/ammo_caliber/dart - ammo_type = /obj/item/ammo_casing/dart/chemdart + ammo_preload = /obj/item/ammo_casing/dart/chemdart ammo_max = 5 /obj/item/ammo_magazine/chemdart/small @@ -43,7 +45,7 @@ rendering_count = 3 origin_tech = list(TECH_MATERIAL = 2) - ammo_type = /obj/item/ammo_casing/dart/chemdart/small + ammo_preload = /obj/item/ammo_casing/dart/chemdart/small ammo_max = 3 //* Projectiles *// diff --git a/icons/modules/projectiles/casings/slim.dmi b/icons/modules/projectiles/casings/slim.dmi index 034c040811df67ece8fd2fe345745b50a22418b1..6a88fe907a216b361f73de8362f2eb527714f34e 100644 GIT binary patch delta 1252 zcmXw%e>l?#9LMMJtH;jpB&D3a^Yosy@k=n+}d=2vWN`_d|rN_AS9 zGtZqPMXC$S*L;Vvb825dqqHC8wmo5noy~0f?$qV;-{s|g=+qSDbAE2~UVUN6qX*jP44 zJomu$gqWx?o0=U_ql1+i&$U11eSK-75ZGk%C>Fu{;Q}utPnP!YN-hqY>Y}AY5-%1K zjyD{d5%=f+)?7e$O{!mrlwrD0o6w)qaX zaS7PG>Xa=mBQ4*2{O*3>wtt6{;M?)6uEG9E=JCHvk3{*KtUFLuJ3GCejTE9#=0JO7 zSm+^2rSe^0=D44|<-u6o(QAQoH6Q$h6DAK8LLvhIiyqCV}}tC-SUhDRRe8KxBqLtcp|r*=(43 zzhe)>-*7|z5#|=0VYJ+q1i9_|3peU=%$A#SeZRyo1H3O&u!x-c_D<*RB>1LWhqWJE zw*r5?|LMKQ-RohMFGk=aRB41byAIQgR41cGHw7CIaIb=;@aG|mdesREW9>?p)K>j2 z-_pYwPl?o9L*YC%nBhmRQX5E(ov)@$Xuax^duoM5$w@#w^eQ0Eh4xV=GdR3bgIBQw z2Vh(ivSEm=S%HO^w+h4_@y{lXdoNkPmE@a5kJWtwu%W#}uM)4zSLDJv+3e}~Mo5K# z0#K|IWik9|XvZmdK@8wK5TzHadM&;+Vwj)(pn1-zmI;n|Db2pX6Uzrvf%3~4QDQ0m z+sfepD1gDBP8)Oea#kGyjEXzh0O=8suI^jD(?*Zfb>y=hCeqvzPm8Tht;lOilzFfD zLgJ=`w%xCwPh(%kmr2eZHAUS4JXEy1KFbaeTep(a$Cqx=P8w}8{74n^>4_6o_=nJK zi3sXB-3?&8ueG)aRom->1A#`qh)8Q0l%5FzD2GUqzk51NODO9i0iN0U{ z5Sa%hZ1t8H|?IoxD63ji|3<%&DF-Yx5T7B|+O zZ%Z#aig9yO$hggLZd!S09lq}MQvG*3c}c}Zp#mdPT2h$KPi7p6b+{q7?i0u>9dn*95OPW!roTX<@nzuIhQEyg#ND&vww+Cb1>4MP(S2>WW;nvToqE0Z zw@YsH-jGHoj&k+oCmnU1W?IGLtJDFB=Y0It?4Gdl?J38Z?c`nItsm1<0>inLQJei7 zU)((1bz(SpG8&Ov0Iv-MSH^ChCjhLx5<>ejDpNc+F_<9k^(Jkoa$ zeQIIk`ly;=QE&q`O_LJWZo-)|vnaM#9eK?w@1yb=lx+%>_0eY~4Hf~BwIxQq!C=@E zF~#Oqj&W-(Co}pek9RQe_QoAkR50k6@2nN9}C` z&VR=ljvrKAfauMv@gW8ZFLFBBDyw>F%!V!m&~yaj`HHfcR6CAHSh(r}m>GYdBB)jAkg8+W3D zYnd8W5%xgc-v6N*36!ldCcN!hp2*2N6egrHC)9p(M6>JC5GlJZUk)rqoNae`NchilViE`= z8S)_?39(g=)1h7gKLS|x(um(-LLD8fj11su0_;IM}cBYHGgPD z7u`C&1gn~C4+x6Q@5lh)|9MgLrB9SISn5b`$Q`6hqZ7%gU?*E}t_pV%{07sHc1fUd zGDM-9_$~R`#Zj#ec2&ZB>&{@s<7|w3|S;FB9grrUEGc~H%NHEjF$deo1q}JaX z$>sS$CeZ7Juz_uJo)gF2y(oGh!!D3lzn9A;y&xPqUZLMA8x3T*kTdC~vf)?MiS(Bo zb<50>7nX&GnHA3Zu9FzNPUP-(%iCinV>|YSRs^e^>Aq4B@u{RCC~RRz!07uwQg7zp zX8xNVWNHMdrUB`q5CcNgpwkFECK^;Ira+ru^w&5Iv6#=dz9v;ee?{kSBtKTW@5P_J zEcxMPU)~%tc+{@aH%qo3(tp2zTuF?B#HzU_gU6W3D+Td4{Y>bOwt232w*tq|%NaL_ zL=ge=^4ZxxjZmkqdVT*c2x9Ii&(sN(k|5=04{a8{>LV=cLD&axn|fkVr^HFudNO6; Q$Z{bh_%Q9p{`m9%0})tlaR2}S From 9a5c5e619f8894672a63d522d700ad4efb31568c Mon Sep 17 00:00:00 2001 From: Athena148 <140056159+Athena148@users.noreply.github.com> Date: Fri, 30 Aug 2024 21:25:21 -0400 Subject: [PATCH 04/35] Backspaces the PKA Damage Modkit from code (#6695) ## About The Pull Request Removes the Proto Kinetic Accelerator damage modkit from the codebase. ## Why It's Good For The Game PKA's have been a long standing issue, being able to deal upwards of 60-80 damage in one hit if you use full damage modkits on your PKA. This is completely ludicrous if you're in an area where they can be used, such as vacuum, the outside of Rift (NSB Atlas map) and a few other areas. This is a TEMPORARY SOLUTION until Silicons reworks PKA's. ## Changelog :cl: del: Removes the PKA Damage Modkit. /:cl: --- .../mining/ore_redemption_machine/equipment_vendor.dm | 1 - .../projectiles/guns/energy/kinetic_accelerator.dm | 11 ----------- 2 files changed, 12 deletions(-) diff --git a/code/modules/mining/ore_redemption_machine/equipment_vendor.dm b/code/modules/mining/ore_redemption_machine/equipment_vendor.dm index 5b9693b6ccb0..16bacb7da61e 100644 --- a/code/modules/mining/ore_redemption_machine/equipment_vendor.dm +++ b/code/modules/mining/ore_redemption_machine/equipment_vendor.dm @@ -73,7 +73,6 @@ new /datum/data/mining_equipment("KA Super Chassis", /obj/item/ka_modkit/chassis_mod, 250), new /datum/data/mining_equipment("KA Hyper Chassis", /obj/item/ka_modkit/chassis_mod/orange, 300), new /datum/data/mining_equipment("KA Range Increase", /obj/item/ka_modkit/range, 1000), - new /datum/data/mining_equipment("KA Damage Increase", /obj/item/ka_modkit/damage, 1000), new /datum/data/mining_equipment("KA Cooldown Decrease", /obj/item/ka_modkit/cooldown, 1200), new /datum/data/mining_equipment("KA Capacity Increase", /obj/item/ka_modkit/capacity, 1500), new /datum/data/mining_equipment("KA Holster", /obj/item/clothing/accessory/holster/waist/kinetic_accelerator, 350), diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm index c20f29e8e20c..b8101a537ae8 100644 --- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm +++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm @@ -363,17 +363,6 @@ /obj/item/ka_modkit/range/modify_projectile(obj/projectile/kinetic/K) K.range += modifier * WORLD_ICON_SIZE - -//Damage -/obj/item/ka_modkit/damage - name = "damage increase" - desc = "Increases the damage of kinetic accelerator when installed." - modifier = 10 - -/obj/item/ka_modkit/damage/modify_projectile(obj/projectile/kinetic/K) - K.damage += modifier - - //Cooldown /obj/item/ka_modkit/cooldown name = "cooldown decrease" From e697b2924d2056ea1b1596a522a53342a334c9f2 Mon Sep 17 00:00:00 2001 From: Athena148 <140056159+Athena148@users.noreply.github.com> Date: Fri, 30 Aug 2024 21:27:27 -0400 Subject: [PATCH 05/35] The Enigmatic Salvagers (#6692) ## About The Pull Request Adds a unique, event only Hivebot faction named the 'Enigmatic Salvagers.' The Enigmatic Salvagers are a special type of Hivebot faction that comb through the Galaxy, searching for old Abductor ships, worlds and installations to salvage and then modify themselves with the parts. Legend has it that they're controlled by a shadowy Artificial Intelligence that exists far beyond habitable space. Why are they salvaging Abductor technology? What is the goal of this Artificial Intelligence, and why is it puppeting a Hivebot army for its purposes? Maybe we'll find out. ## Why It's Good For The Game Adds a new event/admin spawn only Hivebot faction. Should be fun for all parties. ## Changelog :cl: add: Adds the 'Enigmatic Salvagers' soundadd: Adds new Hivebot/mechanical sounds imageadd: Adds sprites for the Enigmatic Salvagers /:cl: --------- Co-authored-by: silicons <2003111+silicons@users.noreply.github.com> --- citadel.dme | 1 + code/game/objects/structures/loot_piles.dm | 3 + .../mechanical/hivebot/enigma_hivebot.dm | 603 ++++++++++++++++++ .../projectiles/projectile/beam/beams.dm | 12 + icons/mob/124x124_enigma.dmi | Bin 0 -> 640 bytes icons/mob/enigma.dmi | Bin 0 -> 25816 bytes sound/enigma/enigma_hit.ogg | Bin 0 -> 32075 bytes sound/enigma/enigma_hit2.ogg | Bin 0 -> 39089 bytes sound/enigma/enigma_move.ogg | Bin 0 -> 29038 bytes sound/enigma/enigma_move2.ogg | Bin 0 -> 17164 bytes 10 files changed, 619 insertions(+) create mode 100644 code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/enigma_hivebot.dm create mode 100644 icons/mob/124x124_enigma.dmi create mode 100644 icons/mob/enigma.dmi create mode 100644 sound/enigma/enigma_hit.ogg create mode 100644 sound/enigma/enigma_hit2.ogg create mode 100644 sound/enigma/enigma_move.ogg create mode 100644 sound/enigma/enigma_move2.ogg diff --git a/citadel.dme b/citadel.dme index af1a0eb72209..b29ecb2e6910 100644 --- a/citadel.dme +++ b/citadel.dme @@ -3854,6 +3854,7 @@ #include "code\modules\mob\living\simple_mob\subtypes\mechanical\mechanical.dm" #include "code\modules\mob\living\simple_mob\subtypes\mechanical\viscerator.dm" #include "code\modules\mob\living\simple_mob\subtypes\mechanical\cyber_horror\cyber_horror.dm" +#include "code\modules\mob\living\simple_mob\subtypes\mechanical\hivebot\enigma_hivebot.dm" #include "code\modules\mob\living\simple_mob\subtypes\mechanical\hivebot\hivebot.dm" #include "code\modules\mob\living\simple_mob\subtypes\mechanical\hivebot\ranged_damage.dm" #include "code\modules\mob\living\simple_mob\subtypes\mechanical\hivebot\ranged_damage_vr.dm" diff --git a/code/game/objects/structures/loot_piles.dm b/code/game/objects/structures/loot_piles.dm index a18bd0046e62..95978d8a76fa 100644 --- a/code/game/objects/structures/loot_piles.dm +++ b/code/game/objects/structures/loot_piles.dm @@ -809,6 +809,9 @@ Loot piles can be depleted, if loot_depleted is turned on. Note that players wh /obj/structure/loot_pile/mecha/gygax/dark icon_state = "darkgygax-broken" +/obj/structure/loot_pile/mecha/mimir + icon_state = "mimir_wreck" + // Todo: Better loot. /obj/structure/loot_pile/mecha/gygax/dark/adv icon_state = "darkgygax_adv-broken" diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/enigma_hivebot.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/enigma_hivebot.dm new file mode 100644 index 000000000000..a495df25563b --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/enigma_hivebot.dm @@ -0,0 +1,603 @@ +// This is the dedicated file for Enigma Hivebots. +// Enigma hivebots are usually significantly tougher than baseline Hivebots, and are modified with salvaged abductor parts. +// These Hivebots are often meant for Event purposes, and carry a different faction than baseline Hivebots. Use these carefully! + +// Code Stuff +/mob/living/simple_mob/mechanical/hivebot/enigma/death() + ..() + visible_message(SPAN_WARNING("\The [src] demateralizes in a flash of energy!")) + new /obj/effect/debris/cleanable/blood/gibs/robot(src.loc) + var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread + s.set_up(3, 1, src) + s.start() + qdel(src) + +/mob/living/simple_mob/mechanical/hivebot/enigma/director/handle_special() + for(var/mob/living/L in range(16, src)) + if(L == src) + continue // Don't buff ourselves. + if(IIsAlly(L) && L.isSynthetic()) // Don't buff enemies. + L.add_modifier(/datum/modifier/aura/hivebot_commander_buff/enigma, null, src) + +/datum/modifier/aura/hivebot_commander_buff/enigma + name = "Strategicals" + on_created_text = "Signal established with commander. Optimizating combat performance..." + on_expired_text = "Lost signal to commander. Optimization halting." + stacks = MODIFIER_STACK_FORBID + aura_max_distance = 12 + mob_overlay_state = "signal_blue" + + disable_duration_percent = 0.7 + outgoing_melee_damage_percent = 1.3 + attack_speed_percent = 1.3 + accuracy = 30 + slowdown = -1 + evasion = 30 + +/mob/living/simple_mob/mechanical/hivebot/enigma/archaeologist/handle_special() + if(last_resupply + resupply_cooldown > world.time) + return // On cooldown. + + for(var/mob/living/simple_mob/SM in hearers(resupply_range, src)) + if(SM == src) + continue // We don't use charges buuuuut in case that changes in the future... + if(IIsAlly(SM)) // Don't resupply enemies. + if(!isnull(SM.special_attack_charges) && SM.special_attack_charges < initial(SM.special_attack_charges)) + SM.special_attack_charges += 1 + to_chat(SM, SPAN_NOTICE("\The [src] has resupplied you, and you can use your special ability one additional time.")) + to_chat(src, SPAN_NOTICE("You have resupplied \the [SM].")) + last_resupply = world.time + break // Only one resupply per pulse. + +/mob/living/simple_mob/mechanical/hivebot/enigma/custodian/apply_melee_effects(atom/A) + if(isliving(A)) + var/mob/living/L = A + if(L.mob_size <= MOB_MEDIUM) + visible_message(SPAN_DANGER("\The [src] sends \the [L] flying with their hydraulic fists!")) + playsound(src, 'sound/enigma/enigma_hit2.ogg', 50, 1) + var/throw_dir = get_dir(src, L) + var/throw_dist = L.incapacitated(INCAPACITATION_DISABLED) ? 4 : 1 + L.throw_at_old(get_edge_target_turf(L, throw_dir), throw_dist, 1, src) + else + to_chat(L, SPAN_WARNING( "\The [src] punches you with incredible force, but you remain in place.")) + +// Melee + +/mob/living/simple_mob/mechanical/hivebot/enigma/custodian + name = "custodian" + icon = 'icons/mob/enigma.dmi' + desc = "A strangely shaped humanoid synthetic, standing taller than the average Human. Its armor seems reinforced against common energy and laser weapons, however likely less so against ballistics. Power seems to course through its arms, probably best to not let it hit you... A odd elaborate golden 'E' is etched into the side of its chassis." + icon_state = "custodian" + icon_living = "custodian" + maxHealth = 500 + health = 500 + armor_legacy_mob = list( + "melee" = 20, + "bullet" = 0, + "laser" = 50, + "energy" = 50, + "bomb" = 100, + "bio" = 100, + "rad" = 100 + ) + legacy_melee_damage_lower = 40 + legacy_melee_damage_upper = 40 + movement_cooldown = 4 + icon_scale_x = 1.4 + icon_scale_y = 1.4 + faction = "enigma" + attack_sound = 'sound/enigma/enigma_hit2.ogg' + movement_sound = 'sound/enigma/enigma_move2.ogg' + ai_holder_type = /datum/ai_holder/polaris/simple_mob/hivebot + +/mob/living/simple_mob/mechanical/hivebot/enigma/custodian/Initialize(mapload) + var/shield_type = /obj/item/shield_projector/rectangle{ + shield_health = 150; + max_shield_health = 150; + shield_regen_delay = 10 SECONDS; + shield_regen_amount = 10; + size_x = 1; + size_y = 1; + always_on = TRUE; + } + var/obj/item/shield_projector/shield_projector = new shield_type(src) + shield_projector.create_shields() + return ..() + +/mob/living/simple_mob/mechanical/hivebot/enigma/disassembler + name = "disassembler" + icon = 'icons/mob/enigma.dmi' + desc = "A small drone, decorated in hues of pink and purple material. Two sets of claws comprise its front legs and back legs, and thhere seems to be some sort of golden 'E' symbol which marks the under-chassis. It appears to move quite fast and fit into small spaces." + icon_state = "disassembler" + icon_living = "disassembler" + maxHealth = 200 + health = 200 + armor_legacy_mob = list( + "melee" = 10, + "bullet" = 0, + "laser" = 30, + "energy" = 30, + "bomb" = 0, + "bio" = 100, + "rad" = 100 + ) + + legacy_melee_damage_lower = 20 + legacy_melee_damage_upper = 20 + base_attack_cooldown = 6 + movement_cooldown = 1 + faction = "enigma" + attack_sound = 'sound/enigma/enigma_hit.ogg' + movement_sound = 'sound/enigma/enigma_move.ogg' + ai_holder_type = /datum/ai_holder/polaris/simple_mob/hivebot + +// Support + +/mob/living/simple_mob/mechanical/hivebot/enigma/director + name = "research director" + icon = 'icons/mob/124x124_enigma.dmi' + desc = "A towering machine which stands well above the average person. Its makeup is entirely alien, and its hull seems to shiver and move constantly. Contained within a dome shaped head appears to be some sort of impossibly advanced neural center. It looks to be directing the machines around it, as if it's some sort of Prophet." + icon_state = "research_director" + icon_living = "research_director" + maxHealth = 600 + health = 600 + armor_legacy_mob = list( + "melee" = 30, + "bullet" = 20, + "laser" = 50, + "energy" = 50, + "bomb" = 70, + "bio" = 100, + "rad" = 100 + ) + + legacy_melee_damage_lower = 25 + legacy_melee_damage_upper = 25 + movement_cooldown = 5 + base_pixel_x = 1.5 + base_pixel_y = 1.5 + faction = "enigma" + attack_sound = 'sound/weapons/slash.ogg' + movement_sound = 'sound/enigma/enigma_move.ogg' + ai_holder_type = /datum/ai_holder/polaris/simple_mob/event + +/mob/living/simple_mob/mechanical/hivebot/enigma/archaeologist + name = "xeno archaeologist" + icon = 'icons/mob/enigma.dmi' + desc = "A humanoid synthetic, standing at around the height of the average Human. This one seems off however, and if one were to look closer it has a number of archaeological tools integrated seemlessly into its chassis. A hum of energy from its advanced sensor package follows it where-ever it goes, ready to scan and dig." + icon_state = "archaeologist" + icon_living = "archaeologist" + maxHealth = 150 + health = 150 + armor_legacy_mob = list( + "melee" = 0, + "bullet" = 0, + "laser" = 30, + "energy" = 30, + "bomb" = 0, + "bio" = 100, + "rad" = 100 + ) + + legacy_melee_damage_lower = 10 + legacy_melee_damage_upper = 10 + movement_cooldown = 2 + faction = "enigma" + attack_sound = 'sound/items/drill_hit.ogg' + movement_sound = 'sound/enigma/enigma_move.ogg' + ai_holder_type = /datum/ai_holder/polaris/simple_mob/event + var/resupply_range = 5 + var/resupply_cooldown = 4 SECONDS + var/last_resupply = null + +// Ranged + +/mob/living/simple_mob/mechanical/hivebot/enigma/atomizer + name = "atomizer" + icon = 'icons/mob/enigma.dmi' + desc = "A floating orb which utilizes some sort of advanced anti-gravity technology. It's decorated in gold plating, and seems to be coursing with barely contained energy. On the side of its chassis is a odd golden 'E' shape." + icon_state = "atomizer" + icon_living = "atomizer" + maxHealth = 350 + health = 350 + armor_legacy_mob = list( + "melee" = 0, + "bullet" = 0, + "laser" = 40, + "energy" = 40, + "bomb" = 10, + "bio" = 100, + "rad" = 100 + ) + + legacy_melee_damage_lower = 0 + legacy_melee_damage_upper = 0 + movement_cooldown = 6 + faction = "enigma" + movement_sound = 'sound/enigma/enigma_move.ogg' + ai_holder_type = /datum/ai_holder/polaris/simple_mob/ranged/kiting + projectiletype = /obj/projectile/beam/cyan/hivebot + +/mob/living/simple_mob/mechanical/hivebot/enigma/atomizer/Initialize(mapload) + var/shield_type = /obj/item/shield_projector/rectangle{ + shield_health = 150; + max_shield_health = 150; + shield_regen_delay = 10 SECONDS; + shield_regen_amount = 10; + size_x = 1; + size_y = 1; + always_on = TRUE; + } + var/obj/item/shield_projector/shield_projector = new shield_type(src) + shield_projector.create_shields() + return ..() + + +/mob/living/simple_mob/mechanical/hivebot/enigma/sweeper + name = "sweeper" + icon = 'icons/mob/enigma.dmi' + desc = "A towering mechanical construct, radiating with power. Its gauntlet contains some sort of inbuild shield projector which it uses to advance without sustaining harm, while some sort of shoulder mounted armament can be seen on its back, pointing at anything it intends to obliterate. A odd golden 'E' symbol can be seen on its breast-plate." + icon_state = "sweeper" + icon_living = "sweeper" + maxHealth = 400 + health = 400 + armor_legacy_mob = list( + "melee" = 20, + "bullet" = 30, + "laser" = 50, + "energy" = 50, + "bomb" = 40, + "bio" = 100, + "rad" = 100 + ) + legacy_melee_damage_lower = 20 + legacy_melee_damage_upper = 20 + movement_cooldown = 6 + faction = "enigma" + attack_sound = 'sound/enigma/enigma_hit2.ogg' + movement_sound = 'sound/enigma/enigma_move2.ogg' + icon_scale_x = 1.1 + icon_scale_y = 1.1 + ai_holder_type = /datum/ai_holder/polaris/simple_mob/ranged/kiting + projectiletype = /obj/projectile/arc/fragmentation/mortar + base_attack_cooldown = 30 + +/mob/living/simple_mob/mechanical/hivebot/enigma/sweeper/Initialize(mapload) + var/shield_type = /obj/item/shield_projector/rectangle{ + shield_health = 250; + max_shield_health = 250; + shield_regen_delay = 5 SECONDS; + shield_regen_amount = 10; + size_x = 1; + size_y = 1; + always_on = TRUE; + } + var/obj/item/shield_projector/shield_projector = new shield_type(src) + shield_projector.create_shields() + return ..() + + + +// Troopers + +/mob/living/simple_mob/mechanical/hivebot/enigma/trooper/basic + name = "trooper" + icon = 'icons/mob/enigma.dmi' + desc = "A small robotic unit with a humanoid form. It carries gold markings on its head and face plate, along with shouldering some sort of alien energy weapon." + icon_living = "trooper" + icon_state = "trooper" + maxHealth = 250 + health = 250 + armor_legacy_mob = list( + "melee" = 0, + "bullet" = 0, + "laser" = 20, + "energy" = 20, + "bomb" = 0, + "bio" = 100, + "rad" = 100 + ) + + legacy_melee_damage_lower = 5 + legacy_melee_damage_upper = 5 + movement_cooldown = 2 + faction = "enigma" + movement_sound = 'sound/enigma/enigma_move.ogg' + ai_holder_type = /datum/ai_holder/polaris/simple_mob/hivebot + projectiletype = /obj/projectile/beam/weak + +/mob/living/simple_mob/mechanical/hivebot/enigma/trooper/marksman + name = "marksman trooper" + icon = 'icons/mob/enigma.dmi' + desc = "A slightly taller robotic unit with a humanoid form. It has dark green markings on its faceplate, and seems to carry some sort of advanced energy sniper." + icon_state = "marksman" + icon_living = "marksman" + maxHealth = 200 + health = 200 + armor_legacy_mob = list( + "melee" = 0, + "bullet" = 0, + "laser" = 50, + "energy" = 50, + "bomb" = 0, + "bio" = 100, + "rad" = 100 + ) + + legacy_melee_damage_lower = 5 + legacy_melee_damage_upper = 5 + movement_cooldown = 4 + faction = "enigma" + movement_sound = 'sound/enigma/enigma_move.ogg' + ai_holder_type = /datum/ai_holder/polaris/simple_mob/ranged/sniper + projectiletype = /obj/projectile/beam/xray + +/mob/living/simple_mob/mechanical/hivebot/enigma/trooper/magnetic + name = "magnetic trooper" + icon = 'icons/mob/enigma.dmi' + desc = "Another mechanical construct, this one looks more advanced than the others. Equipped with a extensively modified Ion weapon sporting abductor technology, better steer any machinery clear of this. It sports blue markings." + icon_state = "magnetic" + icon_living = "magnetic" + maxHealth = 300 + health = 300 + armor_legacy_mob = list( + "melee" = 0, + "bullet" = 20, + "laser" = 30, + "energy" = 30, + "bomb" = 0, + "bio" = 100, + "rad" = 100 + ) + + legacy_melee_damage_lower = 0 + legacy_melee_damage_upper = 0 + movement_cooldown = 2 + faction = "enigma" + movement_sound = 'sound/enigma/enigma_move.ogg' + ai_holder_type = /datum/ai_holder/polaris/simple_mob/ranged/kiting + projectiletype = /obj/projectile/ion + base_attack_cooldown = 25 +// Boss + + +// VERY POWERFUL! Uses ripped Advanced Dark Gygax code. +/mob/living/simple_mob/mechanical/hivebot/enigma/mimir + name = "Mimir" + desc = "A heavy exosuit, entirely made up of salvaged Abductor technology and materials. It's one of a kind, and extremely dangerous. Attempting to take it on is thought to be utter suicide due to its wide range of armaments." + icon = 'icons/mob/enigma.dmi' + movement_sound = 'sound/enigma/enigma_move2.ogg' + attack_sound = 'sound/enigma/enigma_hit2.ogg' + icon_state = "mimir" + icon_living = "mimir" + faction = "enigma" + icon_scale_x = 1.3 + icon_scale_y = 1.3 + movement_cooldown = 3 + maxHealth = 1000 + health = 1000 + armor_legacy_mob = list( + "melee" = 50, + "bullet" = 50, + "laser" = 70, + "energy" = 70, + "bomb" = 50, + "bio" = 100, + "rad" = 100 + ) + + special_attack_min_range = 1 + special_attack_max_range = 7 + special_attack_cooldown = 10 SECONDS + projectiletype = /obj/projectile/beam/cyan/hivebot + ai_holder_type = /datum/ai_holder/polaris/simple_mob/intentional/adv_dark_gygax + var/obj/effect/overlay/energy_ball/energy_ball = null + +/mob/living/simple_mob/mechanical/hivebot/enigma/mimir/Initialize(mapload) + var/shield_type = /obj/item/shield_projector/rectangle{ + shield_health = 500; + max_shield_health = 500; + shield_regen_delay = 5 SECONDS; + shield_regen_amount = 30; + size_x = 1; + size_y = 1; + always_on = TRUE; + } + var/obj/item/shield_projector/shield_projector = new shield_type(src) + shield_projector.create_shields() + return ..() + +/mob/living/simple_mob/mechanical/hivebot/enigma/mimir/Destroy() + if(energy_ball) + energy_ball.stop_orbit() + qdel(energy_ball) + return ..() + +/mob/living/simple_mob/mechanical/hivebot/enigma/mimir/do_special_attack(atom/A) + . = TRUE // So we don't fire a bolt as well. + switch(a_intent) + if(INTENT_DISARM) // Side gun + electric_defense(A) + if(INTENT_HARM) // Rockets + launch_rockets(A) + if(INTENT_GRAB) // Micro-singulo + launch_microsingularity(A) + +#define ELECTRIC_ZAP_POWER 20000 + +// Charges a tesla shot, while emitting a dangerous electric field. The exosuit is immune to electric damage while this is ongoing. +// It also briefly blinds anyone looking directly at the mech without flash protection. +/mob/living/simple_mob/mechanical/hivebot/enigma/mimir/proc/electric_defense(atom/target) + set waitfor = FALSE + + // Temporary immunity to shock to avoid killing themselves with their own attack. + var/old_shock_resist = shock_resist + shock_resist = 1 + + // Make the energy ball. This is purely visual since the tesla ball is hyper-deadly. + energy_ball = new(loc) + energy_ball.adjust_scale(0.5) + energy_ball.orbit(src, 32, TRUE, 1 SECOND) + + visible_message(SPAN_WARNING( "\The [src] creates \an [energy_ball] around itself!")) + + playsound(src.loc, 'sound/effects/lightning_chargeup.ogg', 100, 1, extrarange = 30) + + // Shock nearby things that aren't ourselves. + for(var/i = 1 to 10) + energy_ball.adjust_scale(0.5 + (i/10)) + energy_ball.set_light(i/2, i/2, "#0000FF") + for(var/thing in range(3, src)) + // This is stupid because mechs are stupid and not mobs. + if(isliving(thing)) + var/mob/living/L = thing + + if(L == src) + continue + if(L.stat) + continue // Otherwise it can get pretty laggy if there's loads of corpses around. + L.inflict_shock_damage(i * 2) + if(L && L.has_polaris_AI()) // Some mobs delete themselves when dying. + L.ai_holder.react_to_attack_polaris(src) + + else if(istype(thing, /obj/vehicle/sealed/mecha)) + var/obj/vehicle/sealed/mecha/M = thing + M.take_damage_legacy(i * 2, "energy") // Mechs don't have a concept for siemens so energy armor check is the best alternative. + + sleep(1 SECOND) + + // Shoot a tesla bolt, and flashes people who are looking at the mecha without sufficent eye protection. + visible_message(SPAN_WARNING( "\The [energy_ball] explodes in a flash of light, sending a shock everywhere!")) + playsound(src.loc, 'sound/effects/lightningbolt.ogg', 100, 1, extrarange = 30) + tesla_zap(src.loc, 5, ELECTRIC_ZAP_POWER, FALSE) + for(var/mob/living/L in viewers(src)) + if(L == src) + continue + var/dir_towards_us = get_dir(L, src) + if(L.dir && L.dir & dir_towards_us) + to_chat(L, SPAN_DANGER("The flash of light blinds you briefly.")) + L.flash_eyes(intensity = FLASH_PROTECTION_MODERATE, override_blindness_check = FALSE, affect_silicon = TRUE) + + // Get rid of our energy ball. + energy_ball.stop_orbit() + qdel(energy_ball) + + sleep(1 SECOND) + // Resist resistance to old value. + shock_resist = old_shock_resist // Not using initial() in case the value gets modified by an admin or something. + +#undef ELECTRIC_ZAP_POWER + +/mob/living/simple_mob/mechanical/hivebot/enigma/mimir/proc/launch_rockets(atom/target) + set waitfor = FALSE + + // Telegraph our next move. + Beam(target, icon_state = "sat_beam", time = 3.5 SECONDS, maxdistance = INFINITY) + visible_message(SPAN_WARNING( "\The [src] deploys a missile rack!")) + playsound(src, 'sound/effects/turret/move1.wav', 50, 1) + sleep(0.5 SECONDS) + + for(var/i = 1 to 3) + if(target) // Might get deleted in the meantime. + var/turf/T = get_turf(target) + if(T) + visible_message(SPAN_WARNING( "\The [src] fires a rocket into the air!")) + playsound(src, 'sound/weapons/rpg.ogg', 70, 1) + face_atom(T) + var/obj/projectile/arc/explosive_rocket/rocket = new(loc) + rocket.old_style_target(T, src) + rocket.fire() + sleep(1 SECOND) + + visible_message(SPAN_WARNING( "\The [src] retracts the missile rack.")) + playsound(src, 'sound/effects/turret/move2.wav', 50, 1) + +// Arcing rocket projectile that produces a weak explosion when it lands. +// Shouldn't punch holes in the floor, but will still hurt. +/obj/projectile/arc/explosive_rocket + name = "rocket" + icon_state = "mortar" + +/obj/projectile/arc/explosive_rocket/on_impact(turf/T) + new /obj/effect/explosion(T) // Weak explosions don't produce this on their own, apparently. + explosion(T, 0, 0, 2, adminlog = FALSE) + +/mob/living/simple_mob/mechanical/hivebot/enigma/mimir/proc/launch_microsingularity(atom/target) + var/turf/T = get_turf(target) + visible_message(SPAN_WARNING( "\The [src] fires an energetic sphere into the air!")) + playsound(src, 'sound/weapons/Laser.ogg', 50, 1) + face_atom(T) + var/obj/projectile/arc/microsingulo/sphere = new(loc) + sphere.old_style_target(T, src) + sphere.fire() + +/obj/projectile/arc/microsingulo + name = "micro singularity" + icon_state = "bluespace" + +/obj/projectile/arc/microsingulo/on_impact(turf/T) + new /obj/effect/temporary_effect/pulse/microsingulo(T) + + +/obj/effect/temporary_effect/pulse/microsingulo + name = "micro singularity" + desc = "It's sucking everything in!" + icon = 'icons/obj/objects.dmi' + icon_state = "bhole3" + light_range = 4 + light_power = 5 + light_color = "#2ECCFA" + pulses_remaining = 10 + pulse_delay = 2 SECONDS + +/obj/effect/temporary_effect/pulse/microsingulo/on_pulse() + for(var/atom/A in range(pull_radius, src)) + A.singularity_pull(src, pull_strength) +// Used to control the mob's positioning based on which special attack it has done. +// Note that the intent will not change again until the next special attack is about to happen. + +// Changes the mob's intent, which controls which special attack is used. +// INTENT_DISARM causes Electric Defense, INTENT_GRAB causes Micro-Singularity, and INTENT_HARM causes Missile Barrage. +/datum/ai_holder/polaris/simple_mob/intentional/adv_dark_gygax/pre_special_attack(atom/A) + if(isliving(A)) + var/mob/living/target = A + + // If we're surrounded, Electric Defense will quickly fix that. + var/tally = 0 + var/list/potential_targets = list_targets() // Returns list of mobs and certain objects like mechs and turrets. + for(var/atom/movable/AM in potential_targets) + if(get_dist(holder, AM) > electric_defense_radius) + continue + if(!can_attack(AM)) + continue + tally++ + + // Should we shock them? + if(tally >= electric_defense_threshold || get_dist(target, holder) <= electric_defense_radius) + holder.a_intent = INTENT_DISARM + return + + // Otherwise they're a fair distance away and we're not getting mobbed up close. + // See if we should use missiles or microsingulo. + tally = 0 // Let's recycle the var. + for(var/atom/movable/AM in potential_targets) + if(get_dist(target, AM) > microsingulo_radius) // Deliberately tests distance between target and nearby targets and not the holder. + continue + if(!can_attack(AM)) + continue + if(AM.anchored) // Microsingulo doesn't do anything to anchored things. + tally-- + else + tally++ + + // Lots of people means minisingulo would be more useful. + if(tally >= microsingulo_threshold) + holder.a_intent = INTENT_GRAB + else // Otherwise use rockets. + holder.a_intent = INTENT_HARM + + else + if(get_dist(holder, A) >= rocket_explosive_radius + 1) + holder.a_intent = INTENT_HARM // Fire rockets if it's an obj/turf. + else + holder.a_intent = INTENT_DISARM // Electricity might not work but it's safe up close. diff --git a/code/modules/projectiles/projectile/beam/beams.dm b/code/modules/projectiles/projectile/beam/beams.dm index 47ed59a70088..b90a9342d3a7 100644 --- a/code/modules/projectiles/projectile/beam/beams.dm +++ b/code/modules/projectiles/projectile/beam/beams.dm @@ -115,6 +115,18 @@ tracer_type = /obj/effect/projectile/tracer/laser_omni impact_type = /obj/effect/projectile/impact/laser_omni +/obj/projectile/beam/weak + name = "weak cyan beam" + icon_state = "cyan" + fire_sound = 'sound/weapons/Taser.ogg' + damage = 20 + light_color = "#74b1c2" + + muzzle_type = /obj/effect/projectile/muzzle/laser_omni + tracer_type = /obj/effect/projectile/tracer/laser_omni + impact_type = /obj/effect/projectile/impact/laser_omni + + /obj/projectile/beam/emitter name = "emitter beam" icon_state = "emitter" diff --git a/icons/mob/124x124_enigma.dmi b/icons/mob/124x124_enigma.dmi new file mode 100644 index 0000000000000000000000000000000000000000..475110c4fdf02c8f296a82653049406d64ae34ef GIT binary patch literal 640 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

@;b3Vtd2IH3F(<~YZM&NnUJEU(->{Q- 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 Wk|>;O8vLme;Gvd*X1Tg;$o~L(Cl=HI literal 0 HcmV?d00001 diff --git a/sound/enigma/enigma_hit.ogg b/sound/enigma/enigma_hit.ogg new file mode 100644 index 0000000000000000000000000000000000000000..91f1a6a61d9ec35f95c8324e6f90bd12b742a6bd GIT binary patch literal 32075 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 Ku$B}G`Tqg-7YbDX literal 0 HcmV?d00001 diff --git a/sound/enigma/enigma_hit2.ogg b/sound/enigma/enigma_hit2.ogg new file mode 100644 index 0000000000000000000000000000000000000000..5f7b861758323e84c93d9767dffcc3bc2bc0e8f4 GIT binary patch literal 39089 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�)LE)Y~(*pcuM_!$h-9y2J@OS3Y9 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 literal 0 HcmV?d00001 From 5a36e02f5f9a8daa966dc6bf2a9fd65f7d9c758a Mon Sep 17 00:00:00 2001 From: BlueWildrose <57083662+BlueWildrose@users.noreply.github.com> Date: Fri, 30 Aug 2024 19:29:44 -0600 Subject: [PATCH 06/35] Triumph + Rift Departure Shuttle renovations & fixes (#6704) ## About The Pull Request Non-map changes: - Fixed electrochromatic window spawners not passing the given ID over to the windows they're spawning. Both: - Adds fire safety equipment - Window spawners come with a low wall now. Triumph: - Moves the vendors back to the other wall - Replaces the cigarette vendor with an MRE vendor (don't encourage smoking in a shuttle.) - Changes up the bridge to be significantly less garbage - Medbay's surgery table is replaced with a sleeper. Medbay's beds are now rolling beds, and IV drips with anesthetic tanks are set beside them. A blood vendor's been added. - Lets people in the bridge see people outside in the passenger section. - Adds point defense system - Adds electrochromatic windows in the medbay, security, and command sections. Completely toggleable. - Adds brig section in security area - Adds safety equipment like softsuit closets & oxygen closets. Rift: - Fixes the tintable windows. - Fixes access restrictions on security and medical sections. - Adds additional blast doors. ## Why It's Good For The Game Brings the departure shuttles which are designed to carry civillians and various crew members up to code in terms of safety and functionality. May eventually include its own built in air system later. Fixes good. ## Changelog :cl: tweak: Triumph's departure shuttle has been significantly improved in terms of equipment. fix: Atlas and Triumph's departure shuttles have been given proper safety and functionality. /:cl: --- code/modules/mapping/spawner/window.dm | 2 +- maps/rift/levels/rift-11-orbital.dmm | 172 ++++++-- maps/triumph/levels/flagship.dmm | 539 +++++++++++++++++++------ 3 files changed, 576 insertions(+), 137 deletions(-) diff --git a/code/modules/mapping/spawner/window.dm b/code/modules/mapping/spawner/window.dm index 4e8a8017b04f..a2b05f3380f6 100644 --- a/code/modules/mapping/spawner/window.dm +++ b/code/modules/mapping/spawner/window.dm @@ -44,7 +44,7 @@ var/new_window = new window_full_path(loc) if (spawn_low_wall) new low_wall_path(loc) - if(id && istype(window_full_path, /obj/structure/window/reinforced/polarized)) + if(id && istype(new_window, /obj/structure/window/reinforced/polarized)) var/obj/structure/window/reinforced/polarized/P = new_window P.id = id else diff --git a/maps/rift/levels/rift-11-orbital.dmm b/maps/rift/levels/rift-11-orbital.dmm index 8b39ef2f618d..90f246634f87 100644 --- a/maps/rift/levels/rift-11-orbital.dmm +++ b/maps/rift/levels/rift-11-orbital.dmm @@ -312,6 +312,31 @@ }, /turf/simulated/floor/holofloor/tiled/dark, /area/holodeck/source_emptycourt) +"bg" = ( +/obj/structure/bed/chair/shuttle, +/obj/effect/floor_decal/borderfloor{ + dir = 9 + }, +/obj/effect/floor_decal/corner/lightgrey/border{ + dir = 9 + }, +/obj/effect/floor_decal/borderfloor/corner2{ + dir = 10 + }, +/obj/effect/floor_decal/corner/lightgrey/bordercorner2{ + dir = 10 + }, +/obj/structure/closet/hydrant{ + pixel_y = 32 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/shuttle/escape) +"bh" = ( +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_medical" + }, +/turf/simulated/floor, +/area/shuttle/escape) "bi" = ( /obj/structure/window/reinforced{ dir = 8 @@ -2268,7 +2293,7 @@ /obj/structure/table/reinforced, /obj/machinery/keycard_auth{ pixel_x = -24; - pixel_y = -8 + pixel_y = 10 }, /obj/item/storage/secure/briefcase, /obj/item/multitool, @@ -2310,7 +2335,7 @@ /obj/structure/table/reinforced, /obj/machinery/keycard_auth{ pixel_x = 24; - pixel_y = -8 + pixel_y = 11 }, /obj/item/folder/blue, /obj/item/radio{ @@ -2513,10 +2538,13 @@ /obj/structure/bed/chair/bay/comfy/black{ dir = 8 }, +/obj/structure/closet/walllocker/autolok_wall{ + pixel_y = 30; + pixel_x = -24 + }, /turf/simulated/floor/tiled/steel_grid, /area/shuttle/escape) "hY" = ( -/obj/structure/closet/walllocker/autolok_wall, /obj/structure/handrail{ dir = 1 }, @@ -2529,6 +2557,10 @@ /obj/structure/bed/chair/bay/comfy/black{ dir = 4 }, +/obj/structure/closet/walllocker/autolok_wall{ + pixel_y = 30; + pixel_x = 24 + }, /turf/simulated/floor/tiled/steel_grid, /area/shuttle/escape) "ic" = ( @@ -3306,6 +3338,7 @@ "kw" = ( /obj/structure/table/standard, /obj/item/material/ashtray/glass, +/obj/random/action_figure, /turf/simulated/floor/tiled/monotile, /area/shuttle/escape) "kx" = ( @@ -3602,6 +3635,10 @@ /obj/effect/floor_decal/corner/lightgrey/bordercorner2{ dir = 8 }, +/obj/structure/closet/hydrant{ + dir = 4; + pixel_x = -32 + }, /turf/simulated/floor/tiled/steel_grid, /area/shuttle/escape) "lm" = ( @@ -3692,6 +3729,10 @@ /obj/effect/floor_decal/corner/lightgrey/bordercorner2{ dir = 6 }, +/obj/structure/closet/hydrant{ + dir = 4; + pixel_x = 32 + }, /turf/simulated/floor/tiled/steel_grid, /area/shuttle/escape) "lr" = ( @@ -3982,13 +4023,17 @@ /area/shuttle/escape) "mp" = ( /obj/structure/table/glass, -/obj/random/action_figure, /obj/effect/floor_decal/borderfloor{ dir = 8 }, /obj/effect/floor_decal/corner/lightgrey/border{ dir = 8 }, +/obj/item/radio{ + pixel_x = 2; + pixel_y = 2 + }, +/obj/item/radio, /turf/simulated/floor/tiled/steel_grid, /area/shuttle/escape) "mq" = ( @@ -4167,6 +4212,9 @@ /obj/effect/floor_decal/corner/lightgrey/bordercorner2{ dir = 5 }, +/obj/machinery/vending/wallmed1/public{ + pixel_x = 26 + }, /turf/simulated/floor/tiled/steel_grid, /area/shuttle/escape) "mM" = ( @@ -4272,7 +4320,7 @@ name = "Shuttle Security"; req_one_access = null }, -/obj/map_helper/access_helper/airlock/station/security, +/obj/map_helper/access_helper/airlock/station/security/department, /turf/simulated/floor/tiled/monotile, /area/shuttle/escape) "mZ" = ( @@ -4280,7 +4328,7 @@ name = "Shuttle Medical"; req_one_access = null }, -/obj/map_helper/access_helper/airlock/station/medical, +/obj/map_helper/access_helper/airlock/station/medical/department, /turf/simulated/floor/tiled/monotile, /area/shuttle/escape) "na" = ( @@ -4306,7 +4354,9 @@ id = "emergency_shuttle_lockdown"; name = "Emergency Shuttle Blast Door" }, -/obj/spawner/window/full/firelocks, +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_security" + }, /turf/simulated/floor/reinforced, /area/shuttle/escape) "nd" = ( @@ -4402,6 +4452,9 @@ "nr" = ( /obj/item/handcuffs, /obj/structure/table/standard, +/obj/structure/closet/hydrant{ + pixel_y = -26 + }, /turf/simulated/floor/tiled/steel_grid, /area/shuttle/escape) "ns" = ( @@ -4531,7 +4584,13 @@ /turf/simulated/floor/tiled/steel_grid, /area/shuttle/escape) "nM" = ( -/obj/spawner/window/full/firelocks, +/obj/machinery/door/blast/regular/open{ + id = "emergency_shuttle_lockdown"; + name = "Emergency Shuttle Blast Door" + }, +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_cockpit" + }, /turf/simulated/floor, /area/shuttle/escape) "nN" = ( @@ -4656,6 +4715,9 @@ dir = 1 }, /obj/structure/handrail, +/obj/structure/closet/hydrant{ + pixel_y = 32 + }, /turf/simulated/floor/tiled/steel_grid, /area/shuttle/escape) "og" = ( @@ -6293,6 +6355,10 @@ /obj/item/grenade/chem_grenade/metalfoam, /turf/unsimulated/floor/dark, /area/centcom/specops) +"un" = ( +/obj/spawner/window/low_wall/reinforced/full/firelocks, +/turf/simulated/floor, +/area/shuttle/escape) "ur" = ( /obj/structure/cable{ icon_state = "1-2" @@ -6819,6 +6885,15 @@ icon_state = "lino" }, /area/tdome/tdomeobserve) +"yJ" = ( +/obj/structure/bed/chair/shuttle{ + dir = 4 + }, +/obj/structure/closet/hydrant{ + pixel_y = -26 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/shuttle/escape) "yO" = ( /obj/structure/table/standard, /obj/machinery/computer/skills{ @@ -7676,6 +7751,12 @@ /obj/item/clothing/glasses/sunglasses/sechud/tactical, /turf/unsimulated/floor/dark, /area/centcom/specops) +"BW" = ( +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_cockpit" + }, +/turf/simulated/floor, +/area/shuttle/escape) "Cb" = ( /obj/machinery/lathe/autolathe{ desc = "Your typical Autolathe. It appears to have much more options than your regular one, however..."; @@ -7978,6 +8059,12 @@ }, /turf/simulated/floor/tiled/techfloor/grid, /area/centcom/specops/dock) +"Ey" = ( +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_security" + }, +/turf/simulated/floor, +/area/shuttle/escape) "EC" = ( /obj/machinery/vending/tool, /turf/unsimulated/floor/dark, @@ -8295,6 +8382,15 @@ /obj/item/storage/belt/security/tactical, /turf/unsimulated/floor/dark, /area/centcom/specops) +"Jr" = ( +/obj/machinery/atmospherics/pipe/simple/visible/fuel, +/obj/machinery/door/blast/regular/open{ + id = "emergency_shuttle_lockdown"; + name = "Emergency Shuttle Blast Door" + }, +/obj/spawner/window/low_wall/reinforced/full/firelocks, +/turf/simulated/floor/reinforced, +/area/shuttle/escape) "Jx" = ( /obj/structure/table/rack/shelf/steel, /obj/item/storage/firstaid/combat, @@ -8425,6 +8521,25 @@ /obj/effect/shuttle_landmark/shuttle_initializer/specops, /turf/simulated/floor/tiled/dark, /area/shuttle/specops/general) +"Kv" = ( +/obj/structure/bed/chair/shuttle, +/obj/effect/floor_decal/borderfloor{ + dir = 5 + }, +/obj/effect/floor_decal/corner/lightgrey/border{ + dir = 5 + }, +/obj/effect/floor_decal/borderfloor/corner2{ + dir = 5 + }, +/obj/effect/floor_decal/corner/lightgrey/bordercorner2{ + dir = 5 + }, +/obj/structure/closet/hydrant{ + pixel_y = 32 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/shuttle/escape) "Ky" = ( /obj/item/radio/intercom{ dir = 1; @@ -9075,6 +9190,17 @@ "Rn" = ( /turf/unsimulated/wall, /area/centcom/specops/dock) +"Ro" = ( +/obj/machinery/atmospherics/pipe/simple/visible/fuel, +/obj/machinery/door/blast/regular/open{ + id = "emergency_shuttle_lockdown"; + name = "Emergency Shuttle Blast Door" + }, +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_medical" + }, +/turf/simulated/floor/reinforced, +/area/shuttle/escape) "Rs" = ( /obj/structure/cable{ icon_state = "1-4" @@ -11580,7 +11706,7 @@ gx ji jP kt -nc +Jr kt kt kt @@ -11965,7 +12091,7 @@ gy gz nM gy -jk +bg jR rY ia @@ -12352,7 +12478,7 @@ gz gz hi hX -nM +BW jm ia ku @@ -12362,7 +12488,7 @@ lL gz gy mY -nM +Ey iE gz of @@ -12546,7 +12672,7 @@ nM ha hj hY -nM +BW jn jT jT @@ -12558,7 +12684,7 @@ mK ia nf ns -nM +un og oI px @@ -12934,7 +13060,7 @@ nM hc hl hY -nM +BW jo jV jV @@ -12946,7 +13072,7 @@ mL ia ng nu -nM +un oi oK gz @@ -13128,7 +13254,7 @@ gz gz hm ib -nM +BW jp ia kx @@ -13138,7 +13264,7 @@ lO gz gy mZ -nM +bh iE gz of @@ -13333,7 +13459,7 @@ gy mM ia Cx -Cx +yJ gy oj oM @@ -13517,7 +13643,7 @@ gy gz nM gy -jF +Kv jW rY ia @@ -13908,7 +14034,7 @@ gx js jY kt -nc +Jr kt kt kt @@ -14108,8 +14234,8 @@ lJ mr mP nb -nc -nc +Ro +Ro nb ok gx diff --git a/maps/triumph/levels/flagship.dmm b/maps/triumph/levels/flagship.dmm index a9bba780a6c5..e79f15c02043 100644 --- a/maps/triumph/levels/flagship.dmm +++ b/maps/triumph/levels/flagship.dmm @@ -14,6 +14,15 @@ icon_state = "dark" }, /area/centcom/security) +"ag" = ( +/obj/structure/bed/chair/shuttle{ + dir = 1 + }, +/obj/structure/closet/walllocker/emergsuit_wall{ + pixel_y = -26 + }, +/turf/simulated/shuttle/floor/black, +/area/shuttle/escape) "ah" = ( /obj/effect/floor_decal/borderfloorblack, /obj/effect/floor_decal/corner/blue/border, @@ -583,12 +592,12 @@ /turf/unsimulated/floor/steel, /area/centcom/security) "bV" = ( -/obj/machinery/vending/cigarette, /obj/machinery/light{ dir = 8; use_power = 0; old_wall = 1 }, +/obj/structure/closet/emcloset, /turf/simulated/shuttle/floor/darkred, /area/shuttle/escape) "bX" = ( @@ -1154,8 +1163,8 @@ }, /area/centcom/medical) "dx" = ( -/obj/item/bedsheet/medical, -/obj/structure/bed/padded, +/obj/structure/medical_stand/anesthetic, +/obj/structure/bed/roller, /turf/simulated/shuttle/floor, /area/shuttle/escape) "dy" = ( @@ -1964,6 +1973,13 @@ "ga" = ( /turf/unsimulated/wall, /area/centcom/living) +"gb" = ( +/obj/structure/handrail, +/obj/structure/window/reinforced{ + dir = 8 + }, +/turf/simulated/shuttle/floor/darkred, +/area/shuttle/escape) "gc" = ( /obj/structure/table/reinforced, /obj/item/paper_bin{ @@ -2001,6 +2017,13 @@ }, /turf/unsimulated/floor/wood, /area/centcom/restaurant) +"gg" = ( +/obj/structure/handrail, +/obj/structure/closet/walllocker/emergsuit_wall{ + pixel_y = 32 + }, +/turf/simulated/shuttle/floor/black, +/area/shuttle/escape) "gh" = ( /obj/effect/floor_decal/corner/grey/diagonal{ dir = 4 @@ -2274,6 +2297,15 @@ icon_state = "white" }, /area/centcom/medical) +"gS" = ( +/obj/structure/handrail{ + dir = 1 + }, +/obj/machinery/status_display{ + pixel_y = -32 + }, +/turf/simulated/shuttle/floor/black, +/area/shuttle/escape) "gW" = ( /obj/structure/table/glass, /obj/item/storage/firstaid/regular, @@ -2288,6 +2320,15 @@ icon_state = "white" }, /area/centcom/medical) +"gX" = ( +/obj/random/firstaid, +/obj/item/storage/firstaid/adv, +/obj/item/storage/firstaid/bonemed, +/obj/item/storage/firstaid/clotting, +/obj/item/storage/firstaid/clotting, +/obj/structure/table/reinforced, +/turf/simulated/shuttle/floor, +/area/shuttle/escape) "gY" = ( /obj/structure/sign/nanotrasen, /turf/unsimulated/wall, @@ -2645,11 +2686,19 @@ icon_state = "white" }, /area/centcom/control) +"im" = ( +/obj/machinery/computer/communications{ + dir = 4 + }, +/obj/machinery/keycard_auth{ + pixel_x = -24; + pixel_y = -24 + }, +/turf/simulated/shuttle/floor/white, +/area/shuttle/escape) "in" = ( -/obj/machinery/light{ - dir = 4; - use_power = 0; - old_wall = 1 +/obj/structure/handrail{ + dir = 8 }, /turf/simulated/shuttle/floor/white, /area/shuttle/escape) @@ -2700,6 +2749,19 @@ }, /turf/unsimulated/floor/steel, /area/centcom/security) +"iC" = ( +/obj/structure/fans/tiny, +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_medical" + }, +/obj/machinery/door/blast/regular/open{ + id = "emergency_shuttle_lockdown"; + name = "Emergency Shuttle Blast Door" + }, +/turf/unsimulated/floor{ + name = "plating" + }, +/area/shuttle/escape) "iE" = ( /turf/unsimulated/floor/steel{ icon_state = "white" @@ -3392,6 +3454,19 @@ /obj/spawner/window/low_wall/borosillicate/full/firelocks, /turf/simulated/floor/plating, /area/shuttle/specops/engine) +"kX" = ( +/obj/structure/handrail{ + dir = 1 + }, +/obj/machinery/door/window/brigdoor/southright{ + dir = 8; + name = "holding cell" + }, +/obj/machinery/status_display{ + pixel_y = -32 + }, +/turf/simulated/shuttle/floor/darkred, +/area/shuttle/escape) "kY" = ( /obj/effect/floor_decal/borderfloor, /obj/effect/floor_decal/borderfloor/corner2{ @@ -3455,6 +3530,13 @@ }, /turf/unsimulated/floor/steel, /area/centcom/evac) +"lo" = ( +/obj/structure/handrail, +/obj/machinery/vending/wallmed1/public{ + pixel_y = 32 + }, +/turf/simulated/shuttle/floor/black, +/area/shuttle/escape) "lp" = ( /obj/machinery/computer/card{ dir = 4 @@ -3756,6 +3838,12 @@ icon_state = "carpet" }, /area/centcom/command) +"mb" = ( +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_cockpit" + }, +/turf/simulated/floor, +/area/shuttle/escape) "md" = ( /obj/structure/window/reinforced{ dir = 1 @@ -3902,9 +3990,8 @@ }, /area/centcom/security) "mA" = ( -/obj/machinery/optable, -/obj/machinery/oxygen_pump/anesthetic{ - pixel_y = 29 +/obj/machinery/sleep_console{ + dir = 4 }, /turf/simulated/shuttle/floor, /area/shuttle/escape) @@ -3986,8 +4073,10 @@ }, /area/centcom/specops) "mE" = ( -/obj/spawner/window/low_wall/reinforced/full, /obj/structure/fans/tiny, +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_medical" + }, /turf/unsimulated/floor{ name = "plating" }, @@ -4005,6 +4094,15 @@ /obj/map_helper/access_helper/airlock/station/command/vault, /turf/simulated/floor/reinforced, /area/centcom/command) +"mG" = ( +/obj/structure/bed/chair/shuttle{ + dir = 8 + }, +/obj/machinery/status_display{ + pixel_y = -32 + }, +/turf/simulated/shuttle/floor/white, +/area/shuttle/escape) "mH" = ( /obj/effect/floor_decal/corner/grey/diagonal{ dir = 4 @@ -4101,6 +4199,18 @@ icon_state = "dark" }, /area/centcom/specops) +"mU" = ( +/obj/item/defib_kit, +/obj/structure/table/reinforced, +/obj/item/storage/firstaid/surgery, +/obj/item/reagent_containers/spray/cleaner{ + desc = "Someone has crossed out the Space from Space Cleaner and written in Surgery. 'Do not remove under punishment of death!!!' is scrawled on the back."; + name = "Surgery Cleaner"; + pixel_x = 2; + pixel_y = 2 + }, +/turf/simulated/shuttle/floor, +/area/shuttle/escape) "mV" = ( /obj/structure/bed/chair/office/dark{ dir = 4 @@ -4475,6 +4585,13 @@ icon_state = "white" }, /area/centcom/control) +"nU" = ( +/obj/structure/bed/chair/shuttle, +/obj/structure/closet/walllocker/emergsuit_wall{ + pixel_y = 32 + }, +/turf/simulated/shuttle/floor/darkred, +/area/shuttle/escape) "nV" = ( /obj/structure/table/rack, /obj/structure/window/reinforced, @@ -4630,6 +4747,15 @@ icon_state = "white" }, /area/centcom/bathroom) +"ov" = ( +/obj/structure/table/reinforced, +/obj/item/radio{ + pixel_x = 2; + pixel_y = 2 + }, +/obj/item/radio, +/turf/simulated/shuttle/floor/darkred, +/area/shuttle/escape) "ox" = ( /obj/structure/flora/ausbushes/ywflowers, /turf/simulated/floor/outdoors/grass/heavy/interior, @@ -4646,6 +4772,13 @@ icon_state = "white" }, /area/centcom/medical) +"oz" = ( +/obj/structure/bed/chair/shuttle, +/obj/structure/closet/walllocker/emergsuit_wall{ + pixel_y = 32 + }, +/turf/simulated/shuttle/floor/black, +/area/shuttle/escape) "oA" = ( /obj/structure/table/standard, /obj/effect/floor_decal/borderfloor{ @@ -4703,7 +4836,11 @@ /area/centcom/living) "oL" = ( /obj/structure/fans/tiny, -/obj/spawner/window/low_wall/reinforced/full, +/obj/spawner/window/low_wall/reinforced/full/firelocks, +/obj/machinery/door/blast/regular/open{ + id = "emergency_shuttle_lockdown"; + name = "Emergency Shuttle Blast Door" + }, /turf/simulated/floor, /area/shuttle/escape) "oN" = ( @@ -5147,6 +5284,24 @@ icon_state = "dark" }, /area/centcom/specops) +"qk" = ( +/obj/structure/closet/walllocker/autolok_wall{ + pixel_y = 32 + }, +/obj/machinery/light{ + dir = 4; + use_power = 0 + }, +/turf/simulated/shuttle/floor/white, +/area/shuttle/escape) +"qm" = ( +/obj/machinery/light{ + dir = 4; + use_power = 0 + }, +/obj/machinery/vending/fitness, +/turf/simulated/shuttle/floor/darkred, +/area/shuttle/escape) "qn" = ( /obj/machinery/door/firedoor, /turf/unsimulated/floor/steel, @@ -5263,6 +5418,13 @@ icon_state = "white" }, /area/centcom/medical) +"qH" = ( +/obj/structure/handrail, +/obj/machinery/status_display{ + pixel_y = 32 + }, +/turf/simulated/shuttle/floor, +/area/shuttle/escape) "qI" = ( /obj/structure/table/rack, /obj/item/hardsuit_module/chem_dispenser/combat, @@ -6655,6 +6817,13 @@ /obj/item/clothing/glasses/welding, /turf/unsimulated/floor/steel, /area/centcom/control) +"uV" = ( +/obj/structure/closet/hydrant{ + dir = 4; + pixel_x = -32 + }, +/turf/simulated/shuttle/floor/white, +/area/shuttle/escape) "uW" = ( /obj/item/storage/belt/security/tactical, /obj/item/storage/belt/security/tactical, @@ -6898,6 +7067,10 @@ /obj/machinery/light/flamp/noshade, /turf/unsimulated/floor/steel, /area/centcom/evac) +"vR" = ( +/obj/machinery/vending/mre, +/turf/simulated/shuttle/floor/darkred, +/area/shuttle/escape) "vS" = ( /obj/structure/sign/greencross, /turf/unsimulated/wall, @@ -7402,6 +7575,17 @@ }, /turf/unsimulated/floor/steel, /area/centcom/control) +"xj" = ( +/obj/structure/fans/tiny, +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_security" + }, +/obj/machinery/door/blast/regular/open{ + id = "emergency_shuttle_lockdown"; + name = "Emergency Shuttle Blast Door" + }, +/turf/simulated/floor, +/area/shuttle/escape) "xk" = ( /obj/machinery/computer/crew{ dir = 4 @@ -8421,9 +8605,11 @@ /turf/unsimulated/floor/steel, /area/centcom/control) "AA" = ( -/obj/structure/table/steel, -/obj/item/defib_kit, -/turf/simulated/shuttle/floor, +/obj/structure/fans/tiny, +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_medical" + }, +/turf/simulated/floor, /area/shuttle/escape) "AB" = ( /obj/structure/table/standard, @@ -9233,6 +9419,14 @@ }, /turf/simulated/shuttle/floor/black, /area/shuttle/escape) +"Dp" = ( +/obj/machinery/button/windowtint{ + id = "emergency_medical"; + pixel_x = 24; + pixel_y = -24 + }, +/turf/simulated/shuttle/floor, +/area/shuttle/escape) "Dq" = ( /obj/machinery/telecomms/processor/preset_cent, /turf/unsimulated/floor{ @@ -9430,6 +9624,12 @@ }, /turf/unsimulated/floor/steel, /area/centcom/evac) +"Ef" = ( +/obj/structure/shuttle/engine/propulsion{ + dir = 1 + }, +/turf/simulated/shuttle/plating/airless/carry, +/area/shuttle/escape) "Eg" = ( /obj/structure/sign/securearea{ name = "\improper ARMORY"; @@ -9749,6 +9949,17 @@ /obj/machinery/portable_atmospherics/powered/scrubber/huge/stationary/shuttle, /turf/unsimulated/floor/steel, /area/centcom/evac) +"Fl" = ( +/obj/structure/fans/tiny, +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_cockpit" + }, +/obj/machinery/door/blast/regular/open{ + id = "emergency_shuttle_lockdown"; + name = "Emergency Shuttle Blast Door" + }, +/turf/simulated/floor, +/area/shuttle/escape) "Fm" = ( /obj/structure/window/reinforced, /turf/unsimulated/floor/steel, @@ -9975,9 +10186,13 @@ /turf/unsimulated/floor/steel, /area/centcom/main_hall) "FX" = ( -/obj/structure/bed/chair/shuttle{ - dir = 1 +/obj/machinery/button/remote/blast_door{ + id = "emergency_shuttle_lockdown"; + name = "Emergency Blast Doors"; + pixel_x = -24; + pixel_y = -7 }, +/obj/machinery/pointdefense_control, /turf/simulated/shuttle/floor/white, /area/shuttle/escape) "FY" = ( @@ -10838,6 +11053,12 @@ icon_state = "lino" }, /area/centcom/command) +"ID" = ( +/obj/structure/closet/walllocker/emergsuit_wall{ + pixel_y = 32 + }, +/turf/simulated/shuttle/floor/black, +/area/shuttle/escape) "IG" = ( /obj/structure/table/reinforced, /obj/item/tool/crowbar, @@ -11081,6 +11302,10 @@ icon_state = "white" }, /area/centcom/medical) +"Jr" = ( +/obj/structure/shuttle/engine/propulsion, +/turf/simulated/shuttle/plating/airless/carry, +/area/shuttle/escape) "Js" = ( /obj/machinery/r_n_d/protolathe, /turf/unsimulated/floor/steel, @@ -12392,6 +12617,12 @@ icon_state = "white" }, /area/centcom/medical) +"No" = ( +/obj/structure/closet/walllocker/emergsuit_wall{ + pixel_y = -26 + }, +/turf/simulated/shuttle/floor/black, +/area/shuttle/escape) "Nq" = ( /obj/structure/table/rack, /obj/structure/window/reinforced{ @@ -12783,13 +13014,11 @@ /turf/simulated/shuttle/floor, /area/shuttle/escape) "Oz" = ( -/obj/machinery/vending/fitness, -/obj/machinery/light{ - dir = 8; - use_power = 0; - old_wall = 1 +/obj/structure/fans/tiny, +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_security" }, -/turf/simulated/shuttle/floor/darkred, +/turf/simulated/floor, /area/shuttle/escape) "OA" = ( /obj/machinery/tele_pad, @@ -12885,13 +13114,10 @@ }, /area/centcom/bathroom) "OZ" = ( -/obj/structure/table/steel, -/obj/random/firstaid, -/obj/item/storage/firstaid/adv, -/obj/item/storage/firstaid/bonemed, -/obj/item/storage/firstaid/clotting, -/obj/item/storage/firstaid/clotting, -/turf/simulated/shuttle/floor, +/obj/structure/handrail{ + dir = 1 + }, +/turf/simulated/shuttle/floor/darkred, /area/shuttle/escape) "Pa" = ( /obj/effect/floor_decal/industrial/warning/dust/corner, @@ -12918,6 +13144,17 @@ }, /turf/simulated/shuttle/wall/voidcraft, /area/shuttle/specops/engine) +"Ph" = ( +/obj/structure/handrail{ + dir = 1 + }, +/obj/machinery/button/windowtint{ + id = "emergency_security"; + pixel_x = 8; + pixel_y = -24 + }, +/turf/simulated/shuttle/floor/darkred, +/area/shuttle/escape) "Pj" = ( /obj/effect/floor_decal/borderfloorblack{ dir = 6 @@ -12946,11 +13183,10 @@ /turf/simulated/floor/plating, /area/shuttle/specops/engine) "Pn" = ( -/obj/machinery/light{ - dir = 4; - use_power = 0 +/obj/machinery/sleeper{ + dir = 4 }, -/turf/simulated/shuttle/floor/white, +/turf/simulated/shuttle/floor, /area/shuttle/escape) "Po" = ( /obj/effect/floor_decal/derelict/d13, @@ -13256,6 +13492,13 @@ icon_state = "white" }, /area/centcom/medical) +"PY" = ( +/obj/structure/table/reinforced, +/obj/machinery/holoplant{ + pixel_y = 6 + }, +/turf/simulated/shuttle/floor/darkred, +/area/shuttle/escape) "Qa" = ( /obj/structure/table/steel, /obj/item/storage/firstaid/regular, @@ -13303,6 +13546,10 @@ icon_state = "dark" }, /area/centcom/security) +"Qd" = ( +/obj/machinery/vending/blood, +/turf/simulated/shuttle/floor, +/area/shuttle/escape) "Qg" = ( /obj/effect/floor_decal/borderfloorblack{ dir = 8 @@ -13564,6 +13811,17 @@ }, /turf/unsimulated/floor/steel, /area/centcom/security) +"QW" = ( +/obj/structure/closet/walllocker/autolok_wall{ + pixel_y = -26 + }, +/obj/machinery/light{ + dir = 4; + use_power = 0; + old_wall = 1 + }, +/turf/simulated/shuttle/floor/white, +/area/shuttle/escape) "QX" = ( /obj/machinery/shieldwallgen, /turf/unsimulated/floor{ @@ -13766,6 +14024,9 @@ old_wall = 1; use_power = 0 }, +/obj/machinery/status_display{ + pixel_y = -32 + }, /turf/simulated/shuttle/floor/black, /area/shuttle/escape) "Ry" = ( @@ -13946,6 +14207,9 @@ /obj/structure/bed/chair/shuttle{ dir = 8 }, +/obj/structure/closet/walllocker/emergsuit_wall{ + pixel_y = 32 + }, /turf/simulated/shuttle/floor, /area/shuttle/escape) "Sh" = ( @@ -14477,6 +14741,12 @@ icon_state = "white" }, /area/centcom/control) +"TV" = ( +/obj/machinery/computer/crew{ + dir = 4 + }, +/turf/simulated/shuttle/floor/white, +/area/shuttle/escape) "TW" = ( /obj/effect/floor_decal/steeldecal/steel_decals9, /obj/effect/floor_decal/steeldecal/steel_decals9{ @@ -14826,6 +15096,15 @@ icon_state = "dark" }, /area/centcom/specops) +"Vm" = ( +/obj/structure/handrail{ + dir = 1 + }, +/obj/structure/closet/walllocker/emergsuit_wall{ + pixel_y = -26 + }, +/turf/simulated/shuttle/floor/black, +/area/shuttle/escape) "Vn" = ( /obj/structure/table/standard, /obj/effect/floor_decal/borderfloor{ @@ -15082,6 +15361,13 @@ /obj/structure/closet/secure_closet/hop, /turf/unsimulated/floor/steel, /area/centcom/command) +"Wj" = ( +/obj/structure/handrail, +/obj/machinery/status_display{ + pixel_y = 32 + }, +/turf/simulated/shuttle/floor/black, +/area/shuttle/escape) "Wk" = ( /obj/structure/sign/warning/caution, /turf/unsimulated/wall, @@ -15403,7 +15689,22 @@ }, /area/centcom/security) "Xq" = ( -/obj/structure/bed/chair/shuttle, +/obj/structure/table/reinforced, +/obj/machinery/button/windowtint{ + id = "emergency_cockpit"; + pixel_x = -24; + pixel_y = 6 + }, +/obj/machinery/keycard_auth{ + pixel_x = -24; + pixel_y = -8 + }, +/obj/item/folder/blue, +/obj/item/radio, +/obj/item/radio{ + pixel_x = 2; + pixel_y = 2 + }, /turf/simulated/shuttle/floor/white, /area/shuttle/escape) "Xr" = ( @@ -16234,6 +16535,14 @@ icon_state = "white" }, /area/centcom/medical) +"Zy" = ( +/obj/machinery/light{ + dir = 4; + use_power = 0 + }, +/obj/structure/closet/firecloset/full/double, +/turf/simulated/shuttle/floor/darkred, +/area/shuttle/escape) "ZA" = ( /obj/effect/floor_decal/borderfloorwhite, /obj/effect/floor_decal/corner/paleblue/border, @@ -16296,6 +16605,10 @@ icon_state = "steel" }, /area/centcom/bathroom) +"ZM" = ( +/obj/machinery/power/pointdefense, +/turf/simulated/floor/plating/eris/under, +/area/shuttle/escape) "ZN" = ( /obj/structure/sign/department/armory, /turf/unsimulated/wall, @@ -29061,11 +29374,11 @@ AF AF AF jQ -oL -oL -oL -oL -oL +jQ +Fl +Fl +Fl +jQ jQ AF AF @@ -29253,15 +29566,15 @@ AF AF AF AF -AF -jQ +ZM +Fl Xq -yz +TV PU -yz +im FX -jQ -AF +Fl +ZM AF AF AF @@ -29446,17 +29759,17 @@ AF AF AF AF -AF -AF +Ef jQ -Xq -yz +wG QG -yz -FX +QG +QG +QG +mG +wG jQ -AF -AF +Jr AF AF AF @@ -29643,11 +29956,11 @@ AF jQ jQ jQ -wG -Pn +qk +in yz in -wG +QW jQ jQ jQ @@ -29836,14 +30149,14 @@ AF AF jQ bV -al wG -jQ +mb +mb Kg -jQ +mb +mb wG -va -Oz +bV jQ AF AF @@ -30029,15 +30342,15 @@ AF AF AF jQ -Gg -yz +Wj +uV Ui pa yz pa Ui -yz -Jg +uV +gS jQ AF AF @@ -30223,7 +30536,7 @@ AF AF AF jQ -NW +oz yz Ew NW @@ -30231,7 +30544,7 @@ yz Ew NW yz -Dm +ag jQ AF AF @@ -30999,7 +31312,7 @@ AF AF AF jQ -NW +oz yz Ew NW @@ -31007,7 +31320,7 @@ yz Ew NW yz -Dm +ag jQ AF AF @@ -31195,12 +31508,12 @@ AF jQ Gg yz -Pn yz yz yz yz -Pn +yz +yz Jg jQ AF @@ -31387,15 +31700,15 @@ AF AF AF jQ -jQ -oL -oL -wG -CG -CG -wG -oL -oL +al +vR +Zy +ov +yz +yz +PY +qm +va jQ AF AF @@ -31581,15 +31894,15 @@ AF AF AF jQ -Ne -AA -OZ -jQ -Gg -Jg jQ -ze -lM +AA +AA +wG +gg +Vm +wG +Oz +Oz jQ AF AF @@ -31775,15 +32088,15 @@ AF AF AF jQ -Sp -Sp -Sp +Ne +mU +gX mE CG CG -oL -nb -lM +Oz +ze +OZ jQ AF AF @@ -31975,9 +32288,9 @@ dx mE CG CG -oL +Oz nb -lM +Ph jQ AF AF @@ -32162,17 +32475,17 @@ AF AF AF AF -jQ +iC mA Sp dx jQ -Gg +lo Jg jQ -nb -lM -jQ +nU +OZ +xj AF AF AF @@ -32356,8 +32669,8 @@ AF AF AF AF -jQ -Sp +iC +Pn Sp Sp Cw @@ -32365,8 +32678,8 @@ CG CG uq lM -lM -jQ +OZ +xj AF AF AF @@ -32551,15 +32864,15 @@ AF AF AF jQ -Sp -Sp -Sp +qH +Dp +Qd jQ Gg Jg jQ -lM -lM +gb +kX jQ AF AF @@ -32749,8 +33062,8 @@ Sg Xs wG wG -CG -CG +ID +No jQ FB ya From d1a4e379b0e7d534d900942b6a1ef7230b340bdd Mon Sep 17 00:00:00 2001 From: MediHound <47318585+MediHound@users.noreply.github.com> Date: Sun, 1 Sep 2024 01:36:42 +0200 Subject: [PATCH 07/35] Shadekin and Crewkin changes (#6393) --Do not testmerge yet! PR was made so Kev can work on it as well.-- ## About The Pull Request After a discussion with the lore team, changes were proposed to Shadekin, both for their lore and their mechanics. This PR will do the necessary changes. ## Why It's Good For The Game These changes are tied to the lore of the species, as well as to the wishes and limitations that the lore team asked for them. ## Changelog :cl: add: Pocket Dimension ability for Shadekin and Crewkin, a small storage. change: Adjust all lore texts in code to fit the new Shadekin lore. /:cl: --------- Co-authored-by: silicons <2003111+silicons@users.noreply.github.com> --- citadel.dme | 1 + code/__DEFINES/coloration.dm | 1 - .../signals_atom/signals_atom-reachability.dm | 10 ++++++ code/__DEFINES/procs/clickcode.dm | 2 +- code/game/click/reachability.dm | 3 +- code/game/objects/systems/storage.dm | 2 +- .../organs/internal/species/shadekin.dm | 34 +++++++++++++++++++ code/modules/species/shadekin/shadekin.dm | 14 ++++---- .../species/shadekin/shadekin_blackeyed.dm | 15 ++++---- 9 files changed, 65 insertions(+), 17 deletions(-) create mode 100644 code/__DEFINES/dcs/signals/signals_atom/signals_atom-reachability.dm diff --git a/citadel.dme b/citadel.dme index b29ecb2e6910..d285353ca10e 100644 --- a/citadel.dme +++ b/citadel.dme @@ -177,6 +177,7 @@ #include "code\__DEFINES\dcs\signals\elements\signals_element_conflict_checking.dm" #include "code\__DEFINES\dcs\signals\items\signals_inducer.dm" #include "code\__DEFINES\dcs\signals\modules\signals_module_fishing.dm" +#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-reachability.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_appearance.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_attack.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_buckling.dm" diff --git a/code/__DEFINES/coloration.dm b/code/__DEFINES/coloration.dm index 66a67f85f757..fb04c405e95a 100644 --- a/code/__DEFINES/coloration.dm +++ b/code/__DEFINES/coloration.dm @@ -1,4 +1,3 @@ - //* This file is explicitly licensed under the MIT license. *// //* Copyright (c) 2024 Citadel Station developers. *// diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom-reachability.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-reachability.dm new file mode 100644 index 000000000000..3b98fa4b0f7d --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-reachability.dm @@ -0,0 +1,10 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station developers. *// + +/** + * These are all **very** low level signals. + * Handle with care. + */ + +/// from base of /atom/movable/proc/DirectAccess(): (list/accessible) +#define COMSIG_ATOM_REACHABILITY_DIRECTACCESS "atom-directaccess" diff --git a/code/__DEFINES/procs/clickcode.dm b/code/__DEFINES/procs/clickcode.dm index 6293c3498e06..a064ae31258b 100644 --- a/code/__DEFINES/procs/clickcode.dm +++ b/code/__DEFINES/procs/clickcode.dm @@ -41,7 +41,7 @@ //! Reachability Depths - checked from level of DirectAccess and turf adjacency. /// default reachability depth -#define DEFAULT_REACHABILITY_DEPTH 3 // enough to reach into pill bottles in box in backpack +#define DEFAULT_REACHABILITY_DEPTH 4 //! Reachability /// can't reach - this *must* be a fals-y value. diff --git a/code/game/click/reachability.dm b/code/game/click/reachability.dm index 2604d39f2207..4fec1fe4b952 100644 --- a/code/game/click/reachability.dm +++ b/code/game/click/reachability.dm @@ -187,7 +187,8 @@ * checks what we can directly reach */ /atom/movable/proc/DirectAccess() - return list(src, loc) + . = list(src, loc) + SEND_SIGNAL(src, COMSIG_ATOM_REACHABILITY_DIRECTACCESS, .) /mob/DirectAccess() return ..() + get_equipped_items() diff --git a/code/game/objects/systems/storage.dm b/code/game/objects/systems/storage.dm index 913261e1214f..f323897778fd 100644 --- a/code/game/objects/systems/storage.dm +++ b/code/game/objects/systems/storage.dm @@ -456,7 +456,7 @@ target = parent, ) return TRUE - if(!actor.performer.Reachability(parent)) + if(!actor.performer.Reachability(indirection || parent)) return TRUE if(!try_insert(inserting, actor, silent, suppressed)) return TRUE diff --git a/code/modules/organs/internal/species/shadekin.dm b/code/modules/organs/internal/species/shadekin.dm index bc529e693f57..0cfe9e48c2ef 100644 --- a/code/modules/organs/internal/species/shadekin.dm +++ b/code/modules/organs/internal/species/shadekin.dm @@ -5,6 +5,40 @@ var/max_dark_energy = 100 var/dark_energy_infinite = FALSE + organ_actions = list( + /datum/action/organ_action/shadekin_storage, + ) + +/datum/action/organ_action/shadekin_storage + name = "Access Storage" + desc = "Access your dimensional pocket." + +/obj/item/organ/internal/brain/shadekin/Initialize(mapload) + . = ..() + obj_storage = new /datum/object_system/storage/shadekin(src) + obj_storage.indirect(src) + +/obj/item/organ/internal/brain/shadekin/on_insert(mob/owner, initializing) + . = ..() + RegisterSignal(owner, COMSIG_ATOM_REACHABILITY_DIRECTACCESS, PROC_REF(handle_storage_reachability)) + +/obj/item/organ/internal/brain/shadekin/on_remove(mob/owner) + . = ..() + UnregisterSignal(owner, COMSIG_ATOM_REACHABILITY_DIRECTACCESS) + +/obj/item/organ/internal/brain/shadekin/proc/handle_storage_reachability(atom/source, list/direct_access) + var/atom/movable/storage_indirection/indirection = locate() in contents + if(!indirection) + return + direct_access += indirection + +/obj/item/organ/internal/brain/shadekin/ui_action_click(datum/action/action, datum/event_args/actor/actor) + obj_storage.show(actor.performer) + +/datum/object_system/storage/shadekin + max_single_weight_class = WEIGHT_CLASS_SMALL + max_items = 7 + /obj/item/organ/internal/brain/shadekin/crewkin dark_energy = 50 max_dark_energy = 50 diff --git a/code/modules/species/shadekin/shadekin.dm b/code/modules/species/shadekin/shadekin.dm index cd3b048c69f0..1de2c40e446a 100644 --- a/code/modules/species/shadekin/shadekin.dm +++ b/code/modules/species/shadekin/shadekin.dm @@ -13,13 +13,15 @@ SPRITE_ACCESSORY_SLOT_TAIL = /datum/sprite_accessory/tail/bodyset/shadekin, ) - //TODO: Something that's not wiki copypaste blurb = {" - Very little is known about these creatures. They appear to be largely mammalian in appearance. - Seemingly very rare to encounter, there have been widespread myths of these creatures the galaxy over, - but next to no verifiable evidence to their existence. However, they have recently been more verifiably - documented in the Virgo system, following a mining bombardment of Virgo 3. The crew of NSB Adephagia have - taken to calling these creatures 'Shadekin', and the name has generally stuck and spread. + Shadekin are rather unusual creatures, coming from the Azuel system. Their appearance is largely + mammalian, even though they aren't mammals. The official, formal name for the species is Lumelea, + but thanks to a period of difficulties when the Lumelea first met other species, the nickname + Shadekin was made popular by the galaxy's various species, and it stuck to this day as an + informal name. After a few hundred years of living side by side, it's by now widely known that + Shadekin culture revolves around tribes with various levels of technology, with some tribes + integrating into other cultures and cities, as well as some Shadekin leaving their tribe to + travel alone. NanoTrasen is one of the biggest employers of Shadekin. "} wikilink = "https://citadel-station.net/wikiRP/index.php?title=Race:_Shadekin" catalogue_data = list(/datum/category_item/catalogue/fauna/shadekin) diff --git a/code/modules/species/shadekin/shadekin_blackeyed.dm b/code/modules/species/shadekin/shadekin_blackeyed.dm index 11152d27e9da..1890bcf5f497 100644 --- a/code/modules/species/shadekin/shadekin_blackeyed.dm +++ b/code/modules/species/shadekin/shadekin_blackeyed.dm @@ -14,14 +14,15 @@ SPRITE_ACCESSORY_SLOT_TAIL = /datum/sprite_accessory/tail/bodyset/shadekin, ) - //TODO: Something more fitting for black-eyes - //TODO: CIT ADDENDUM: since we're not really on the tether anymore we'll need a bullshit reason as to why they're around wherever we are. blurb = {" - Very little is known about these creatures. They appear to be largely mammalian in appearance. - Seemingly very rare to encounter, there have been widespread myths of these creatures the galaxy over, - but next to no verifiable evidence to their existence. However, they have recently been more verifiably - documented in the Virgo system, following a mining bombardment of Virgo 3. The crew of NSB Adephagia have - taken to calling these creatures 'Shadekin', and the name has generally stuck and spread. + Shadekin are rather unusual creatures, coming from the Azuel system. Their appearance is largely + mammalian, even though they aren't mammals. The official, formal name for the species is Lumelea, + but thanks to a period of difficulties when the Lumelea first met other species, the nickname + Shadekin was made popular by the galaxy's various species, and it stuck to this day as an + informal name. After a few hundred years of living side by side, it's by now widely known that + Shadekin culture revolves around tribes with various levels of technology, with some tribes + integrating into other cultures and cities, as well as some Shadekin leaving their tribe to + travel alone. NanoTrasen is one of the biggest employers of Shadekin. "} wikilink = "https://citadel-station.net/wikiRP/index.php?title=Race:_Shadekin" From 6db1b9c04125540b8104f522ef510ed1b8e9b377 Mon Sep 17 00:00:00 2001 From: Niezan Date: Sat, 31 Aug 2024 21:11:01 -0700 Subject: [PATCH 08/35] ups sanitize line breaks allowed (#6711) ## About The Pull Request ups the line breaks allowed max in emotes (via upping it in sanitize) ## Why It's Good For The Game i was regularly hitting this. this is a rage patch, i admit it. ## Changelog :cl: qol: emotes now can have more line breaks. /:cl: --- code/__HELPERS/text.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index 08ba49b5fd2e..7fcdc575e3a9 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -36,7 +36,7 @@ if(extra) var/temp_input = replace_characters(input, list("\n"=" ","\t"=" "))//one character is replaced by two - if(length_char(input) < (length_char(temp_input) - (6 * 2))) //12 is the number of linebreaks allowed per message + if(length_char(input) < (length_char(temp_input) - 24)) //24 is the number of linebreaks allowed per message input = replace_characters(temp_input,list(" "=" "))//replace again, this time the double spaces with single ones if(encode) From 9326af3d932c302291f36f3b4f386ddca0f6984a Mon Sep 17 00:00:00 2001 From: BlueWildrose <57083662+BlueWildrose@users.noreply.github.com> Date: Sun, 1 Sep 2024 09:29:09 -0600 Subject: [PATCH 09/35] Stops xenoarcheology talking objects from annoying ghosts (#6705) ## About The Pull Request title ## Why It's Good For The Game If normal objects don't annoy ghosts who have Ghost Ears enabled, I don't see why these things should either. ## Changelog :cl: fix: Xenoarcheology's parroting objects will no longer annoy ghosts who have Ghost Ears enabled. /:cl: --- code/modules/xenoarcheaology/finds/talking.dm | 2 -- 1 file changed, 2 deletions(-) diff --git a/code/modules/xenoarcheaology/finds/talking.dm b/code/modules/xenoarcheaology/finds/talking.dm index c6527c6dc84c..c3460957bd2c 100644 --- a/code/modules/xenoarcheaology/finds/talking.dm +++ b/code/modules/xenoarcheaology/finds/talking.dm @@ -114,8 +114,6 @@ continue //skip monkeys and leavers if (istype(M, /mob/new_player)) continue - if(M.stat == DEAD && M.get_preference_toggle(/datum/game_preference_toggle/observer/ghost_ears)) - listening|=M for(var/mob/M in listening) to_chat(M, "[icon2html(thing = holder_atom, target = M)] [holder_atom] reverberates, \"[msg]\"") From 6ec07b0e57351c7208a36927d7792902d8bd22cc Mon Sep 17 00:00:00 2001 From: Niezan Date: Sun, 1 Sep 2024 08:29:19 -0700 Subject: [PATCH 10/35] racks construct costs 2 steel (#6707) ## About The Pull Request if u deconstruct racks, they drop 2 steel. however, they only take 1 steel to make. hmm. now they cost 2 steel to make. ## Why It's Good For The Game unduplicates ur matter ## Changelog :cl: tweak: Rack now costs 2 steel to make. /:cl: --- code/modules/materials/definitions/metals/steel.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/materials/definitions/metals/steel.dm b/code/modules/materials/definitions/metals/steel.dm index 44cf063ed243..71a4db047970 100644 --- a/code/modules/materials/definitions/metals/steel.dm +++ b/code/modules/materials/definitions/metals/steel.dm @@ -80,7 +80,7 @@ . += create_stack_recipe_datum( name = "rack", product = /obj/structure/table/rack, - cost = 1, + cost = 2, time = 0.5 SECONDS, ) . += create_stack_recipe_datum( From 822e0a3aa607e5feb498d0effbaf4dfc49275a6a Mon Sep 17 00:00:00 2001 From: Niezan Date: Sun, 1 Sep 2024 08:29:46 -0700 Subject: [PATCH 11/35] replicator fix (v2) (#6710) ## About The Pull Request somehow the icon state name for the replicator fix didn't save??????? fmlfmlfml ## Why It's Good For The Game fml ## Changelog :cl: fix: replicator state /:cl: --- icons/obj/xenoarchaeology.dmi | Bin 94435 -> 94440 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/icons/obj/xenoarchaeology.dmi b/icons/obj/xenoarchaeology.dmi index 8f90645f609437b9d5d547705ff14ee2943d6914..36e0634dba2af6446f954eba8d172c98f4230bc2 100644 GIT binary patch delta 784 zcmV+r1MmFf;RWd71&}0vj(SvBbVOxyV{&P5bZKvH004NLtys-&oIDg=4^LsWTdTqb zCdpzpRjMkBR^J;$9=HVykBy8?l0N;n$aDrM&b^5$G>ehWxyQ%8KZeij>$mR@KiU0{ zhin2rYG>Jjm0$8~_oSQ9u>&h)w(CH*U3mR71{HuE*r%g+J=jlw*{;!kWCtd)-7l|B zFtP(Hvt0v5y|4olUnHOXu6$)(o^R8|5+$}#`(NS-{OaV4$dW)*{*Y899&=rCQ2@ESDJSu%{xNE;{M>J7Cg4PH` zsuQfsIT>wdoEzAI?W?M&R-{zy5=n$}I=m4&Rrtl~XjUxRo}`MNT$6&*AJOg*_JS>;y8dEQMH?&@cbTIZQQlC=yIFBiA`LY6JH;$yu|fuq}a7l-s*|(qf?Ec3?_CMN}hdVx# OuJ41k1GlyV0WEFdDtoQ~ delta 797 zcmV+&1LFMX;RWO21&}0viF#C6bVOxyV{&P5bZKvH004NLtys%$+d2?kTVFx6TcQvt z$w?O86e)@~-OO)6~?PntVvKCZ95)cW2;wn&Z$wjvF@T6_? zmjOwVksptvAPE#77br;rWkTR?Q(!};BFR(<86mDZ)mrB-tvyL=Pte*>T4~*ov~Chw zvucrCa>F|c>RN)t^qgk-VB;wz+5k*arpQ4sQVX)0apf0DXfGw@D^ z*TqqV0RDv(?inkqcYtZb<_x}hk7Z)NYG5M5sOD(#H!#s+Z(yRu-M~bN`RFX5ps@pE zk_mSmOS0gyoTq9x_VHuP4-*HYY`}-kV3ZA#1}kW=5{1RFhh$K*&+X#^5ZW z#OhAj7QCx_+^W$NctU|2J%J}wx6u=LN@;s#Tf(S{;{bM1)NX#l^LuQyao5_S%We85 zHgRQ6e0{j`64&!NwpP}H$HRWbJ*qnBDX~j$y;*-5HFj-GWmUe|w44hrD=v7&1(!J& ze0fWJqeb&*7cb@b0X#CXZG#q bJ-h$$ko^y3)Pn{^I3&`8y92ko0|6~ Date: Sun, 1 Sep 2024 10:30:00 -0500 Subject: [PATCH 12/35] Fix vox scales marking covering the eyes (#6708) ## About The Pull Request Fixes the vox scales marking covering their eyes in the left and right directions (i.e. their eyes would be marking-colored). Before (colors exaggerated to show the difference): ![image](https://github.com/user-attachments/assets/10becfc3-c0e0-4676-9b75-64c6624d0264) After: ![image](https://github.com/user-attachments/assets/db8752ae-5805-4822-b6fd-de0e0adac06c) There seems to be a bug regarding the leg markings, where the left/right markings for the farther leg (right leg when facing left, left leg when facing right) are not applied, possibly because the game thinks that the legs would occlude each other the way they would with humans. I don't know markings code enough to fix this, so that is beyond the scope of this PR (but a very annoying bug nonetheless). ## Why It's Good For The Game Bug fix for vox markings. ## Changelog :cl: fix: Fixed issue with Vox Scales marking covering the eyes when facing left/right. /:cl: --- .../sprite_accessories/markings/vox_stuff.dmi | Bin 4667 -> 4665 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/icons/mob/sprite_accessories/markings/vox_stuff.dmi b/icons/mob/sprite_accessories/markings/vox_stuff.dmi index c1587e1ddbfddd46143a6ef1b00b61da2bd2d669..62747f617e28b1a1fdfdf87c585d2601dd5ada27 100644 GIT binary patch delta 511 zcmV#P#(IdwmKyq4U=s53@?%*ne}sJ_1P;N=kCzMZ|G`FL z3yNO?zY#qGOYxV`IxLzx>e2m;d-0CWF&r*B@DZez`U^dpwKW8qc2u?jsG2l0|8cWTV@()WY>*|6)2;;|+Fvjd<` zQsR^QXYuzl1YUNo28-oC2enu%>zw}m)56s%lK~MIlMoRFe~z=rk8i#}<{-3^AOE~+ zX^F@I*9TA&PcZ<+GU$$p8=l7SP^t<-#3RrHg+mR86t3g68|kHTY7Z+$ScN@~&8 zQrVY^{?=BAwb#UUdHMFMR{38~&H(&c)KkOj|F+#B9-mrL9N5I&GOgKnYx))aTT9Kq zZZ*v2jg9^FLf34c*oqKMzuo@7(f{fgpwdn?%;t@a{q@&upV*2JO~2iKJ@*SW1Vwd2 zyno#`TJgK>*Ns4Z|2Na$n&LoB$F>iX0TCBU0MLk<>jSvWZKcs)8mG2C;Nm#7^#K>h z>8%gAG)`@O000000Dc?)YwHLuDhjdEpe6Nf{~wP|8^zo`dZ7RS002ovPDHLkV1g06 B3h@8{ delta 515 zcmV+e0{s2CB)cTA^Z@}fv-JTn4u6luq9`htW6Ppg-~#OTuvjdM)oMEe3Bd`>I39ZATsiPj<-?$g==p4i0q5~g6YN@}_qgh)+plL^C+gS0AMu3a9 zy4_0tLevTxqlXaKSSVU=W4K6{&P@^#j?)n-#;x}tpbw)5f+mW5e0wXIE(!F<_lyFLM!?4 z&#RV}h#YWz05$Ox15hl3?wGjY$-SQbdD+rR&6ib0KiO+(h0LPx-CXo2JQnrV2Q#ar z7Hut+eW~bgZG~8SO>CE!Z@+4l|Mlbyz^_F;HN5_B+a2QZsU^jMP24ThntivXU(vs{ z)cosK!))Hz*k3|_&Gw0{2+{Q0?f)D7uZ{sK?Nq~T-q_e*f6exZtq9Td+wIqLzfeO^ zR5!%?*KMN}zuSJ@2-Np~GySb84%BpP`;!3?7EAyD0F9`*K7iZYRvP`Kacb)WE{;=M zA8>J;-ui$` Date: Sun, 1 Sep 2024 17:31:13 +0200 Subject: [PATCH 13/35] Adds Teshari Eyes Marking (#6694) Teshi Eyes for roboteshes. Since roboteshes currently cannot really select their eyecolor. Unless its heterochromia ## About The Pull Request This allows Roboteshari to pick a marking to pick their eyecolor. ## Why It's Good For The Game Being able to pick an eye color for both eyes as robotesh is good. ## Changelog :cl: add: Teshari Eye Markinh /:cl: --- .../sprite_accessories/markings/tesh.dm | 6 ++++++ .../markings/tesh_stuff.dmi | Bin 2817 -> 2838 bytes 2 files changed, 6 insertions(+) diff --git a/code/modules/sprite_accessories/markings/tesh.dm b/code/modules/sprite_accessories/markings/tesh.dm index b6aa4baac171..4a2ac81d4f70 100644 --- a/code/modules/sprite_accessories/markings/tesh.dm +++ b/code/modules/sprite_accessories/markings/tesh.dm @@ -9,6 +9,12 @@ id = "marking_teshari_heterochromia" icon_state = "teshi_heterochromia" body_parts = list(BP_HEAD) + +/datum/sprite_accessory/marking/tesh/teshi_eyes + name = "Teshari Eyes" + id = "marking_teshari_eyes" + icon_state = "teshi_eyes" + body_parts = list(BP_HEAD) /datum/sprite_accessory/marking/tesh/tesh_feathers name = "Teshari Feathers" diff --git a/icons/mob/sprite_accessories/markings/tesh_stuff.dmi b/icons/mob/sprite_accessories/markings/tesh_stuff.dmi index 9f642fdfe98db8e31ff0b5b9e8f1133c1746ac51..bce2ee9eef75d7d6730e89667acfa76995a80875 100644 GIT binary patch delta 2733 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 LXUxh>u`&Mz;8PG8 delta 2712 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 From f4c95bb054679722dba682f8a88da341cded5136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AD=BB=E7=83=8F?= <85062773+washikarasu@users.noreply.github.com> Date: Sun, 1 Sep 2024 19:43:52 +0100 Subject: [PATCH 14/35] Electrochromic Helpers, Placing Said Helpers in Atlas, and fixing Cargo's Conveyor ID's (#6698) ## About The Pull Request This PR adds a new mapping helper that lets you ID electrochromic windows placed below, and IDs conveyors. ## Why It's Good For The Game Because we needed more control over what windows are affected by which tint other than the surface area covered on windows placed by builders, which could not be ID'd and because having the two conveyor belts be synced sucks. ## Changelog Adds new mapping helper: /obj/map_helper/electrochromatic_linker and litters Riftmap with them. Separates the controls of the two cargo belts in Riftmap. :cl: tweak: tweaked conveyor belt ids imageadd: edited access helper into a fitting icon code: added mapping helper /:cl: --- citadel.dme | 1 + .../map_helpers/electrochromatic_linker.dm | 17 +++ .../helpers/electrochromatic_helper.dmi | Bin 0 -> 373 bytes maps/rift/levels/rift-02-underground2.dmm | 9 ++ maps/rift/levels/rift-04-surface1.dmm | 81 +++++++++---- maps/rift/levels/rift-05-surface2.dmm | 109 ++++++++++++++++-- maps/rift/levels/rift-06-surface3.dmm | 46 ++++++++ 7 files changed, 230 insertions(+), 33 deletions(-) create mode 100644 code/modules/mapping/map_helpers/electrochromatic_linker.dm create mode 100644 icons/mapping/helpers/electrochromatic_helper.dmi diff --git a/citadel.dme b/citadel.dme index d285353ca10e..91cbf1b6423b 100644 --- a/citadel.dme +++ b/citadel.dme @@ -3213,6 +3213,7 @@ #include "code\modules\mapping\map_helpers\_map_helpers.dm" #include "code\modules\mapping\map_helpers\baseturf.dm" #include "code\modules\mapping\map_helpers\component_injector.dm" +#include "code\modules\mapping\map_helpers\electrochromatic_linker.dm" #include "code\modules\mapping\map_helpers\engine_loader.dm" #include "code\modules\mapping\map_helpers\gear_marker.dm" #include "code\modules\mapping\map_helpers\paint.dm" diff --git a/code/modules/mapping/map_helpers/electrochromatic_linker.dm b/code/modules/mapping/map_helpers/electrochromatic_linker.dm new file mode 100644 index 000000000000..0e6fa3ef7fa2 --- /dev/null +++ b/code/modules/mapping/map_helpers/electrochromatic_linker.dm @@ -0,0 +1,17 @@ +/obj/map_helper/electrochromatic_linker + icon = 'icons/mapping/helpers/electrochromatic_helper.dmi' + icon_state = "electrochromatic" + early = FALSE + late = TRUE + /** + * the ID to bind electrochromatic objects on our tile to + */ + var/id + +/obj/map_helper/electrochromatic_linker/LateInitialize() + for(var/obj/structure/window/reinforced/polarized/chromatic_window in loc) + chromatic_window.id = id + /** + * Same behaviour needs to be applied to polarised airlocks, currently unable too + */ + qdel(src) diff --git a/icons/mapping/helpers/electrochromatic_helper.dmi b/icons/mapping/helpers/electrochromatic_helper.dmi new file mode 100644 index 0000000000000000000000000000000000000000..13963d3312594b48080131f18952f3abbe37f246 GIT binary patch literal 373 zcmV-*0gC>KP)V=-0C=2J zR&a84_w-Y6@%7{?OD!tS%+FJ>RWQ*r;NmRLOex6#a*U0*I5Sc+(=$pSoZ^zil2jm5 zDK#fGxuhsRIin~)H?br$S&54?C9|j)C}qgSnO2mTn+jnoE4cc(fNcN(wwoTal>T+t z0001#NklI2;yKH=_13YZm@axMTYOicGBLjmKMDLv~w6? Tt3th`00000NkvXXu0mjf@g0}i literal 0 HcmV?d00001 diff --git a/maps/rift/levels/rift-02-underground2.dmm b/maps/rift/levels/rift-02-underground2.dmm index 31c630ddd71b..5f9d41e3c720 100644 --- a/maps/rift/levels/rift-02-underground2.dmm +++ b/maps/rift/levels/rift-02-underground2.dmm @@ -7891,6 +7891,9 @@ "CH" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/sun, +/obj/map_helper/electrochromatic_linker{ + id = "ce_office" + }, /turf/simulated/floor, /area/crew_quarters/heads/chief) "CI" = ( @@ -8485,6 +8488,9 @@ "ET" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/palebottlegreen, +/obj/map_helper/electrochromatic_linker{ + id = "chapel_office" + }, /turf/simulated/floor/plating, /area/chapel/office) "EU" = ( @@ -8963,6 +8969,9 @@ "Ha" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/palebottlegreen, +/obj/map_helper/electrochromatic_linker{ + id = "chapel_hall" + }, /turf/simulated/floor/plating, /area/chapel/main) "Hb" = ( diff --git a/maps/rift/levels/rift-04-surface1.dmm b/maps/rift/levels/rift-04-surface1.dmm index 3f2d4abc72c4..42bf056e1e34 100644 --- a/maps/rift/levels/rift-04-surface1.dmm +++ b/maps/rift/levels/rift-04-surface1.dmm @@ -873,6 +873,9 @@ "aFU" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/babyblue, +/obj/map_helper/electrochromatic_linker{ + id = "cmo_office" + }, /turf/simulated/floor/plating, /area/crew_quarters/heads/cmo) "aGd" = ( @@ -1702,7 +1705,8 @@ }, /obj/machinery/conveyor_switch{ pixel_y = 12; - tag = "cargoshuttle" + tag = "cargoshuttle"; + id = "CVLoad" }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 8 @@ -2136,7 +2140,8 @@ "bxV" = ( /obj/machinery/conveyor{ dir = 4; - tag = "cargoshuttle" + tag = "cargoshuttle"; + id = "CVLoad" }, /obj/structure/railing, /obj/structure/railing{ @@ -2258,7 +2263,8 @@ }, /obj/machinery/conveyor{ dir = 4; - tag = "cargoshuttle" + tag = "cargoshuttle"; + id = "CVLoad" }, /obj/structure/railing, /turf/simulated/floor/plating, @@ -2381,7 +2387,8 @@ "bFI" = ( /obj/structure/plasticflaps, /obj/machinery/conveyor{ - tag = "mailroom" + tag = "mailroom"; + id = "DLBelt" }, /turf/simulated/floor/plating, /area/quartermaster/delivery) @@ -3142,6 +3149,9 @@ /area/hallway/primary/surfaceone) "cdW" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, +/obj/map_helper/electrochromatic_linker{ + id = "iaa" + }, /turf/simulated/floor/plating, /area/lawoffice) "cec" = ( @@ -3286,6 +3296,9 @@ "clK" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/beastybrown, +/obj/map_helper/electrochromatic_linker{ + id = "QM_office" + }, /turf/simulated/floor/plating, /area/quartermaster/qm) "cmb" = ( @@ -3514,6 +3527,9 @@ "ctO" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/babyblue, +/obj/map_helper/electrochromatic_linker{ + id = "patient_room_b" + }, /turf/simulated/floor/plating, /area/medical/patient_a) "cuR" = ( @@ -3679,6 +3695,9 @@ "czx" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/babyblue, +/obj/map_helper/electrochromatic_linker{ + id = "exam_room" + }, /turf/simulated/floor/plating, /area/medical/exam_room) "czN" = ( @@ -6961,7 +6980,8 @@ pixel_x = -32 }, /obj/machinery/conveyor{ - tag = "mailroom" + tag = "mailroom"; + id = "DLBelt" }, /turf/simulated/floor/plating, /area/quartermaster/delivery) @@ -7936,6 +7956,9 @@ "flS" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/babyblue, +/obj/map_helper/electrochromatic_linker{ + id = "psych_office" + }, /turf/simulated/floor/plating, /area/tether/surfacebase/medical/mentalhealth) "flT" = ( @@ -9100,7 +9123,8 @@ "gbL" = ( /obj/machinery/conveyor{ dir = 4; - tag = "cargoshuttle" + tag = "cargoshuttle"; + id = "CVLoad" }, /obj/structure/railing, /obj/machinery/camera/network/cargo, @@ -10301,7 +10325,8 @@ }, /obj/structure/disposalpipe/trunk, /obj/machinery/conveyor{ - tag = "mailroom" + tag = "mailroom"; + id = "DLBelt" }, /turf/simulated/floor/plating, /area/quartermaster/delivery) @@ -10543,7 +10568,8 @@ dir = 1 }, /obj/machinery/conveyor{ - tag = "mailroom" + tag = "mailroom"; + id = "DLBelt" }, /turf/simulated/floor/plating, /area/quartermaster/delivery) @@ -15997,6 +16023,9 @@ "kko" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/babyblue, +/obj/map_helper/electrochromatic_linker{ + id = "patient_room_a" + }, /turf/simulated/floor/plating, /area/medical/patient_b) "kku" = ( @@ -18431,7 +18460,8 @@ "lJv" = ( /obj/machinery/conveyor{ dir = 4; - tag = "cargoshuttle" + tag = "cargoshuttle"; + id = "CVLoad" }, /obj/machinery/door/blast/shutters{ density = 0; @@ -19447,10 +19477,6 @@ }, /turf/simulated/floor/tiled/monowhite, /area/medical/psych_ward) -"mpA" = ( -/obj/structure/undies_wardrobe, -/turf/simulated/wall/r_wall/prepainted, -/area/rift/surfacebase/outside/outside1) "mpD" = ( /obj/machinery/atmospherics/component/unary/outlet_injector{ id = "violet_protocol"; @@ -23284,6 +23310,9 @@ "oBN" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/darkred, +/obj/map_helper/electrochromatic_linker{ + id = "det_office" + }, /turf/simulated/floor/plating, /area/security/forensics) "oBO" = ( @@ -23926,7 +23955,8 @@ "oTu" = ( /obj/machinery/conveyor_switch{ pixel_y = 12; - tag = "cargoshuttle" + tag = "cargoshuttle"; + id = "CVLoad" }, /obj/effect/floor_decal/industrial/warning{ dir = 5 @@ -28591,7 +28621,8 @@ "rEL" = ( /obj/machinery/conveyor{ dir = 4; - tag = "cargoshuttle" + tag = "cargoshuttle"; + id = "CVLoad" }, /obj/machinery/light{ dir = 8 @@ -30802,7 +30833,8 @@ "sMB" = ( /obj/machinery/conveyor{ dir = 4; - tag = "cargoshuttle" + tag = "cargoshuttle"; + id = "CVLoad" }, /obj/machinery/door/firedoor/glass, /obj/structure/plasticflaps/mining, @@ -31830,7 +31862,8 @@ dir = 4 }, /obj/machinery/conveyor{ - tag = "mailroom" + tag = "mailroom"; + id = "DLBelt" }, /turf/simulated/floor/plating, /area/quartermaster/delivery) @@ -32223,7 +32256,8 @@ }, /obj/machinery/conveyor_switch/oneway{ convdir = -1; - tag = "mailroom" + tag = "mailroom"; + id = "DLBelt" }, /turf/simulated/floor/tiled/steel_grid, /area/quartermaster/delivery) @@ -33143,7 +33177,8 @@ "udl" = ( /obj/machinery/conveyor{ dir = 4; - tag = "cargoshuttle" + tag = "cargoshuttle"; + id = "CVLoad" }, /obj/structure/railing, /turf/simulated/floor/plating, @@ -34887,7 +34922,8 @@ "vaP" = ( /obj/machinery/conveyor{ dir = 4; - tag = "cargoshuttle" + tag = "cargoshuttle"; + id = "CVLoad" }, /obj/structure/railing, /turf/simulated/floor/plating, @@ -36741,7 +36777,8 @@ "wjJ" = ( /obj/machinery/conveyor{ dir = 4; - tag = "cargoshuttle" + tag = "cargoshuttle"; + id = "CVLoad" }, /turf/simulated/floor/plating, /area/quartermaster/hallway) @@ -46117,7 +46154,7 @@ iFN iFN iFN iFN -mpA +vUA vUA vUA iFN diff --git a/maps/rift/levels/rift-05-surface2.dmm b/maps/rift/levels/rift-05-surface2.dmm index a62116cdac90..16dac551e5bf 100644 --- a/maps/rift/levels/rift-05-surface2.dmm +++ b/maps/rift/levels/rift-05-surface2.dmm @@ -2279,6 +2279,9 @@ spawn_grille = 0 }, /obj/effect/paint/babyblue, +/obj/map_helper/electrochromatic_linker{ + id = "operating_theatre_2" + }, /turf/simulated/floor/plating, /area/medical/surgery2) "bmY" = ( @@ -2749,6 +2752,9 @@ /obj/structure/window/reinforced/tinted/frosted{ dir = 8 }, +/obj/map_helper/electrochromatic_linker{ + id = "sec_processing_r" + }, /turf/simulated/floor/tiled/red, /area/security/security_processing) "bBF" = ( @@ -4236,6 +4242,9 @@ /obj/structure/window/reinforced/tinted/frosted{ dir = 4 }, +/obj/map_helper/electrochromatic_linker{ + id = "sec_processing_l" + }, /turf/simulated/floor/tiled/red, /area/security/security_processing) "cDU" = ( @@ -5753,6 +5762,9 @@ /obj/machinery/holosign/surgery{ id = "operating_theatre_2" }, +/obj/map_helper/electrochromatic_linker{ + id = "operating_theatre_2" + }, /obj/machinery/door/airlock/glass/medical/polarized{ id_tint = "operating_theatre_2"; name = "Operating Theatre 2"; @@ -6990,6 +7002,9 @@ /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/darkred, /obj/structure/cable/green, +/obj/map_helper/electrochromatic_linker{ + id = "Interr" + }, /turf/simulated/floor/plating, /area/security/interrogation) "etT" = ( @@ -7261,6 +7276,14 @@ }, /turf/simulated/floor/tiled/steel, /area/security/hallway) +"eCP" = ( +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, +/obj/effect/paint/babyblue, +/obj/map_helper/electrochromatic_linker{ + id = "resleeving-tint" + }, +/turf/simulated/floor/plating, +/area/medical/resleeving) "eCU" = ( /obj/effect/floor_decal/industrial/outline/grey, /obj/random/maintenance/research, @@ -7636,6 +7659,9 @@ opacity = 0 }, /obj/effect/paint/purplegray, +/obj/map_helper/electrochromatic_linker{ + id = "robotics_inner" + }, /turf/simulated/floor/plating, /area/assembly/robotics) "eTD" = ( @@ -7677,6 +7703,9 @@ /obj/structure/window/reinforced/tinted/frosted{ dir = 4 }, +/obj/map_helper/electrochromatic_linker{ + id = "sec_processing_l" + }, /turf/simulated/floor/tiled/red, /area/security/security_processing) "eUD" = ( @@ -9104,6 +9133,9 @@ /obj/structure/window/reinforced/tinted/frosted{ dir = 8 }, +/obj/map_helper/electrochromatic_linker{ + id = "sec_processing_r" + }, /turf/simulated/floor/tiled/red, /area/security/security_processing) "fQU" = ( @@ -9162,6 +9194,9 @@ spawn_grille = 0 }, /obj/effect/paint/babyblue, +/obj/map_helper/electrochromatic_linker{ + id = "operating_theatre_1" + }, /turf/simulated/floor/plating, /area/medical/surgery) "fTu" = ( @@ -11266,6 +11301,9 @@ /obj/structure/disposalpipe/segment, /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/babyblue, +/obj/map_helper/electrochromatic_linker{ + id = "medbay_outer" + }, /turf/simulated/floor/plating, /area/medical/reception) "hlW" = ( @@ -14386,6 +14424,9 @@ opacity = 0 }, /obj/effect/paint/purplegray, +/obj/map_helper/electrochromatic_linker{ + id = "research_outer" + }, /turf/simulated/floor/plating, /area/rnd/research) "jdp" = ( @@ -16801,6 +16842,9 @@ /obj/structure/cable/green{ icon_state = "2-8" }, +/obj/map_helper/electrochromatic_linker{ + id = "Interr" + }, /turf/simulated/floor/plating, /area/security/interrogation) "kHg" = ( @@ -17651,6 +17695,14 @@ }, /turf/simulated/floor/wood, /area/crew_quarters/sleep/Dorm_3) +"ldw" = ( +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, +/obj/effect/paint/darkred, +/obj/map_helper/electrochromatic_linker{ + id = "sec_processing_r" + }, +/turf/simulated/floor/plating, +/area/security/security_processing) "ldL" = ( /obj/effect/floor_decal/borderfloor{ dir = 4 @@ -18544,6 +18596,9 @@ /area/maintenance/dormitory) "lIZ" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, +/obj/map_helper/electrochromatic_linker{ + id = "research_inner" + }, /turf/simulated/floor/plating, /area/rnd/research) "lJq" = ( @@ -19571,6 +19626,14 @@ }, /turf/simulated/floor/wood, /area/tether/surfacebase/reading_room) +"moI" = ( +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, +/obj/effect/paint/darkred, +/obj/map_helper/electrochromatic_linker{ + id = "sec_processing_l" + }, +/turf/simulated/floor/plating, +/area/security/security_processing) "moS" = ( /obj/structure/toilet{ pixel_y = 9 @@ -19693,6 +19756,9 @@ /obj/structure/cable/green{ icon_state = "0-2" }, +/obj/map_helper/electrochromatic_linker{ + id = "Interr" + }, /turf/simulated/floor/plating, /area/security/interrogation) "mqP" = ( @@ -22752,6 +22818,9 @@ opacity = 0 }, /obj/effect/paint/purplegray, +/obj/map_helper/electrochromatic_linker{ + id = "rd_office" + }, /turf/simulated/floor/plating, /area/crew_quarters/heads/hor) "ofP" = ( @@ -25336,6 +25405,9 @@ /obj/machinery/holosign/surgery{ id = "operating_theatre_1" }, +/obj/map_helper/electrochromatic_linker{ + id = "operating_theatre_1" + }, /obj/map_helper/access_helper/airlock/station/medical/surgery, /obj/machinery/door/airlock/glass/medical/polarized{ id_tint = "operating_theatre_1"; @@ -28985,6 +29057,9 @@ /area/crew_quarters/pool) "sdE" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, +/obj/map_helper/electrochromatic_linker{ + id = "sauna" + }, /turf/simulated/floor/plating, /area/triumph/surfacebase/sauna) "sen" = ( @@ -31443,6 +31518,9 @@ opacity = 0 }, /obj/effect/paint/purplegray, +/obj/map_helper/electrochromatic_linker{ + id = "robotics_outer" + }, /turf/simulated/floor/plating, /area/assembly/robotics) "tUr" = ( @@ -31631,6 +31709,9 @@ opacity = 0 }, /obj/effect/paint/darkred, +/obj/map_helper/electrochromatic_linker{ + id = "hos_office" + }, /turf/simulated/floor/plating, /area/crew_quarters/heads/hos) "tZW" = ( @@ -34470,6 +34551,9 @@ "vPP" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/babyblue, +/obj/map_helper/electrochromatic_linker{ + id = "medbay_outer" + }, /turf/simulated/floor/plating, /area/medical/reception) "vQg" = ( @@ -35298,6 +35382,9 @@ /obj/structure/cable/green{ icon_state = "4-8" }, +/obj/map_helper/electrochromatic_linker{ + id = "sauna" + }, /turf/simulated/floor/plating, /area/triumph/surfacebase/sauna) "wtF" = ( @@ -48633,8 +48720,8 @@ oLJ oLJ kid kid -oFb -oFb +eCP +eCP kid kid kid @@ -49218,7 +49305,7 @@ eYx gHr ixy odu -oFb +eCP jvX vjt wnW @@ -49412,7 +49499,7 @@ eYx iKu kRa lto -oFb +eCP eUN dyj wBH @@ -49606,7 +49693,7 @@ oUw dTk bor lto -oFb +eCP epz tQp xkt @@ -57752,7 +57839,7 @@ yga xNm bhS nuH -keC +moI pUO xQz owT @@ -57946,7 +58033,7 @@ myF pwT bhS nuH -keC +moI hGI ijR oZw @@ -58140,7 +58227,7 @@ mJx wXp bhS nuH -keC +moI eUi cCa fEm @@ -58722,7 +58809,7 @@ tZG tiu bhS nuH -keC +ldw fQG bBy bVn @@ -58916,7 +59003,7 @@ tZG mve bhS nuH -keC +ldw hGI ijR eTG @@ -59110,7 +59197,7 @@ iFX eCN ohv oQS -keC +ldw pUO mzx yaJ diff --git a/maps/rift/levels/rift-06-surface3.dmm b/maps/rift/levels/rift-06-surface3.dmm index 7806faa9b5ee..496bc980a809 100644 --- a/maps/rift/levels/rift-06-surface3.dmm +++ b/maps/rift/levels/rift-06-surface3.dmm @@ -2066,6 +2066,11 @@ "agL" = ( /obj/item/folder/blue, /obj/structure/table/wooden_reinforced, +/obj/machinery/button/windowtint/multitint{ + pixel_x = -8; + pixel_y = 7; + id = "pathfinder_office" + }, /turf/simulated/floor/tiled/monotile, /area/exploration/pathfinder_office) "agN" = ( @@ -19893,6 +19898,9 @@ opacity = 0 }, /obj/effect/paint/commandblue, +/obj/map_helper/electrochromatic_linker{ + id = "hop_office" + }, /turf/simulated/floor/plating, /area/crew_quarters/heads/hop) "fUe" = ( @@ -21063,6 +21071,9 @@ "igJ" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/violet, +/obj/map_helper/electrochromatic_linker{ + id = "pathfinder_office" + }, /turf/simulated/floor/plating, /area/exploration/pathfinder_office) "iio" = ( @@ -21188,6 +21199,9 @@ id = "hop_office" }, /obj/effect/paint/commandblue, +/obj/map_helper/electrochromatic_linker{ + id = "hop_office" + }, /turf/simulated/floor/plating, /area/crew_quarters/heads/hop) "iCv" = ( @@ -22196,6 +22210,9 @@ req_one_access = null }, /obj/map_helper/access_helper/airlock/station/command/bridge, +/obj/map_helper/electrochromatic_linker{ + id = "bridge_tint" + }, /turf/simulated/floor/tiled/dark, /area/bridge) "khK" = ( @@ -22509,6 +22526,9 @@ }, /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/commandblue, +/obj/map_helper/electrochromatic_linker{ + id = "bridge_tint" + }, /turf/simulated/floor/plating, /area/bridge) "kQV" = ( @@ -23441,6 +23461,9 @@ req_one_access = null }, /obj/map_helper/access_helper/airlock/station/command/bridge, +/obj/map_helper/electrochromatic_linker{ + id = "bridge_tint" + }, /turf/simulated/floor/tiled/dark, /area/bridge) "mCH" = ( @@ -24438,6 +24461,9 @@ "oJN" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/commandblue, +/obj/map_helper/electrochromatic_linker{ + id = "blueshieldoffice" + }, /turf/simulated/floor/plating, /area/crew_quarters/heads/blueshield) "oLI" = ( @@ -24666,6 +24692,9 @@ req_one_access = null }, /obj/map_helper/access_helper/airlock/station/command/bridge, +/obj/map_helper/electrochromatic_linker{ + id = "bridge_tint" + }, /turf/simulated/floor/tiled/dark, /area/bridge) "pis" = ( @@ -25454,6 +25483,9 @@ opacity = 0 }, /obj/effect/paint/commandblue, +/obj/map_helper/electrochromatic_linker{ + id = "bridge_tint" + }, /turf/simulated/floor/plating, /area/bridge) "qKs" = ( @@ -26545,6 +26577,11 @@ /obj/structure/flora/pottedplant/smallcactus{ pixel_y = 12 }, +/obj/machinery/button/windowtint/multitint{ + pixel_x = 9; + pixel_y = 8; + id = "blueshieldoffice" + }, /turf/simulated/floor/carpet/blue, /area/crew_quarters/heads/blueshield) "toU" = ( @@ -27118,6 +27155,9 @@ opacity = 0 }, /obj/effect/paint/commandblue, +/obj/map_helper/electrochromatic_linker{ + id = "bridge_tint" + }, /turf/simulated/floor/plating, /area/bridge) "uyX" = ( @@ -28302,6 +28342,9 @@ "wns" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/commandblue, +/obj/map_helper/electrochromatic_linker{ + id = "meeting_tint" + }, /turf/simulated/floor/plating, /area/bridge/bridge_hallway) "wnH" = ( @@ -28553,6 +28596,9 @@ "wTS" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/commandblue, +/obj/map_helper/electrochromatic_linker{ + id = "meeting_tint" + }, /turf/simulated/floor/plating, /area/bridge/meeting_room) "wUC" = ( From 0da8419e5af2351554768c732fef2b52a07c56cf Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Sun, 1 Sep 2024 15:02:12 -0400 Subject: [PATCH 15/35] removes simplemob shadekins (#6713) --- citadel.dme | 4 - code/game/rendering/screen.dm | 3 - code/modules/maps/_shim/away_spawner.dm | 16 - .../subtypes/vore/shadekin/ability_objects.dm | 168 -------- .../subtypes/vore/shadekin/ability_procs.dm | 131 ------ .../subtypes/vore/shadekin/shadekin.dm | 388 ------------------ .../subtypes/vore/shadekin/types.dm | 326 --------------- .../shadekin/crew_shadekin_abilities.dm | 2 +- .../species/shadekin/shadekin_abilities.dm | 2 +- 9 files changed, 2 insertions(+), 1038 deletions(-) delete mode 100644 code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_objects.dm delete mode 100644 code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_procs.dm delete mode 100644 code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm delete mode 100644 code/modules/mob/living/simple_mob/subtypes/vore/shadekin/types.dm diff --git a/citadel.dme b/citadel.dme index 91cbf1b6423b..898d2771d3f1 100644 --- a/citadel.dme +++ b/citadel.dme @@ -3933,10 +3933,6 @@ #include "code\modules\mob\living\simple_mob\subtypes\vore\demon\demon_subtypes.dm" #include "code\modules\mob\living\simple_mob\subtypes\vore\demon\~defines.dm" #include "code\modules\mob\living\simple_mob\subtypes\vore\morph\morph.dm" -#include "code\modules\mob\living\simple_mob\subtypes\vore\shadekin\ability_objects.dm" -#include "code\modules\mob\living\simple_mob\subtypes\vore\shadekin\ability_procs.dm" -#include "code\modules\mob\living\simple_mob\subtypes\vore\shadekin\shadekin.dm" -#include "code\modules\mob\living\simple_mob\subtypes\vore\shadekin\types.dm" #include "code\modules\mob\living\voice\voice.dm" #include "code\modules\mob\new_player\join_menu.dm" #include "code\modules\mob\new_player\login.dm" diff --git a/code/game/rendering/screen.dm b/code/game/rendering/screen.dm index 480f07350067..77bd443fe473 100644 --- a/code/game/rendering/screen.dm +++ b/code/game/rendering/screen.dm @@ -422,9 +422,6 @@ var/darkness = round(1 - T.get_lumcount(),0.1) to_chat(usr,"Darkness: [darkness]") if("energy") - var/mob/living/simple_mob/shadekin/SK = usr - if(istype(SK)) - to_chat(usr,"Energy: [SK.energy] ([SK.dark_gains])") var/mob/living/carbon/human/H = usr if(istype(H) && istype(H.species, /datum/species/shadekin)) to_chat(usr,"Energy: [H.shadekin_get_energy(H)]") diff --git a/code/modules/maps/_shim/away_spawner.dm b/code/modules/maps/_shim/away_spawner.dm index beff6090453e..08ed95275c1c 100644 --- a/code/modules/maps/_shim/away_spawner.dm +++ b/code/modules/maps/_shim/away_spawner.dm @@ -92,22 +92,6 @@ For now this is still usable but bad. depleted = TRUE return -//Shadekin spawner. Could have them show up on any mission, so it's here. -//Make sure to put them away from others, so they don't get demolished by rude mobs. -/obj/tether_away_spawner/shadekin - name = "Shadekin Spawner" - icon = 'icons/mob/vore_shadekin.dmi' - icon_state = "spawner" - - faction = "shadekin" - prob_spawn = 1 - prob_fall = 1 - //guard = 10 //Don't wander too far, to stay alive. - mobs_to_pick_from = list( - /mob/living/simple_mob/shadekin - ) - - // Underdark mob spawners /obj/tether_away_spawner/underdark_drone_swarm name = "Underdark Drone Swarm Spawner" diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_objects.dm b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_objects.dm deleted file mode 100644 index b59ca72eea99..000000000000 --- a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_objects.dm +++ /dev/null @@ -1,168 +0,0 @@ -/obj/effect/shadekin_ability - name = "" - desc = "" - icon = 'icons/mob/screen_spells.dmi' - var/ability_name = "FIX ME" - var/cost = 50 - var/mob/living/simple_mob/shadekin/my_kin - var/shift_mode = NOT_WHILE_SHIFTED - var/ab_sound - -/obj/effect/shadekin_ability/Initialize(mapload) - . = ..() - my_kin = loc - moveToNullspace() - -/obj/effect/shadekin_ability/Destroy() - my_kin = null - return ..() - -/obj/effect/shadekin_ability/proc/atom_button_text() - var/shift_denial - - if(shift_mode == NOT_WHILE_SHIFTED && (my_kin.ability_flags & AB_PHASE_SHIFTED)) - shift_denial = "Physical Only" - else if(shift_mode == ONLY_WHILE_SHIFTED && !(my_kin.ability_flags & AB_PHASE_SHIFTED)) - shift_denial = "Shifted Only" - - if(shift_denial) - name = shift_denial - else - name = my_kin.energy >= cost ? "Activate" : "No Energy" - return src - -/obj/effect/shadekin_ability/Click(var/location, var/control, var/params) - if(my_kin.stat) return - - var/list/clickprops = params2list(params) - var/opts = clickprops["shift"] - - if(opts) - to_chat(my_kin,"[name] (Cost: [cost]%) - [desc]") - else - do_ability(my_kin) - -/obj/effect/shadekin_ability/proc/do_ability() - if(my_kin.stat) - to_chat(my_kin,"Can't use that ability in your state!") - return FALSE - if(shift_mode == NOT_WHILE_SHIFTED && (my_kin.ability_flags & AB_PHASE_SHIFTED)) - to_chat(my_kin,"Can't use that ability while phase shifted!") - return FALSE - else if(shift_mode == ONLY_WHILE_SHIFTED && !(my_kin.ability_flags & AB_PHASE_SHIFTED)) - to_chat(my_kin,"Can only use that ability while phase shifted!") - return FALSE - else if(my_kin.energy < cost) - to_chat(my_kin,"Not enough energy for that ability!") - return FALSE - - my_kin.energy -= cost - if(ab_sound) - playsound(src,ab_sound,75,1) - - return TRUE - -///////////////////////////////////////////////////////////////// -/obj/effect/shadekin_ability/phase_shift - ability_name = "Phase Shift" - desc = "Shift yourself out of alignment with realspace to travel quickly between dark areas (or light areas, with a price)." - icon_state = "tech_passwall" - cost = 100 - shift_mode = SHIFTED_OR_NOT - ab_sound = 'sound/effects/stealthoff.ogg' -/obj/effect/shadekin_ability/phase_shift/do_ability() - if(!..()) - return - my_kin.phase_shift() - if(my_kin.ability_flags & AB_PHASE_SHIFTED) - cost = 0 //Shifting back is free (but harmful in light) - else - cost = initial(cost) -///////////////////////////////////////////////////////////////// -/obj/effect/shadekin_ability/heal_boop - ability_name = "Regenerate Other" - desc = "Spend energy to heal physical wounds in another creature." - icon_state = "tech_biomedaura" - cost = 50 - shift_mode = NOT_WHILE_SHIFTED - ab_sound = 'sound/effects/EMPulse.ogg' -/obj/effect/shadekin_ability/heal_boop/do_ability() - if(!..()) - return - if(!my_kin.mend_other()) - my_kin.energy += cost //Refund due to abort -/* -/datum/modifier/shadekin/heal_boop - name = "Shadekin Regen" - desc = "You feel serene and well rested." - mob_overlay_state = "green_sparkles" - - on_created_text = "Sparkles begin to appear around you, and all your ills seem to fade away." - on_expired_text = "The sparkles have faded, although you feel much healthier than before." - stacks = MODIFIER_STACK_EXTEND - -/datum/modifier/shadekin/heal_boop/tick() - if(!holder.getBruteLoss() && !holder.getFireLoss() && !holder.getToxLoss() && !holder.getOxyLoss() && !holder.getCloneLoss()) // No point existing if the spell can't heal. - expire() - return - holder.adjustBruteLoss(-2) - holder.adjustFireLoss(-2) - holder.adjustToxLoss(-2) - holder.adjustOxyLoss(-2) - holder.adjustCloneLoss(-2) -*/ -///////////////////////////////////////////////////////////////// -/obj/effect/shadekin_ability/create_shade - ability_name = "Create Shade" - desc = "Create a field of darkness that follows you." - icon_state = "tech_dispelold" - cost = 25 - shift_mode = NOT_WHILE_SHIFTED - ab_sound = 'sound/effects/bamf.ogg' -/obj/effect/shadekin_ability/create_shade/do_ability() - if(!..()) - return - my_kin.add_modifier(/datum/modifier/shadekin/create_shade,20 SECONDS) -/* -/datum/modifier/shadekin/create_shade - name = "Shadekin Shadegen" - desc = "Darkness envelops you." - mob_overlay_state = "" - - on_created_text = "You drag part of The Dark into realspace, enveloping yourself." - on_expired_text = "You lose your grasp on The Dark and realspace reasserts itself." - stacks = MODIFIER_STACK_EXTEND - var/mob/living/simple_mob/shadekin/my_kin - -/datum/modifier/shadekin/create_shade/tick() - if(my_kin.ability_flags & AB_PHASE_SHIFTED) - expire() - -/datum/modifier/shadekin/create_shade/on_applied() - my_kin = holder - holder.glow_toggle = TRUE - holder.glow_range = 8 - holder.glow_intensity = -10 - holder.glow_color = "#FFFFFF" - holder.set_light(8, -10, "#FFFFFF") - -/datum/modifier/shadekin/create_shade/on_expire() - holder.glow_toggle = initial(holder.glow_toggle) - holder.glow_range = initial(holder.glow_range) - holder.glow_intensity = initial(holder.glow_intensity) - holder.glow_color = initial(holder.glow_color) - holder.set_light(0) - my_kin = null -*/ -/* -///////////////////////////////////////////////////////////////// -/obj/effect/shadekin_ability/energy_feast - ability_name = "Devour Energy" - desc = "Devour the energy from another creature (potentially fatal)." - icon_state = "gen_eat" - cost = 25 - shift_mode = NOT_WHILE_SHIFTED -/obj/effect/shadekin_ability/energy_feast/do_ability() - if(!..()) - return -*/ diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_procs.dm b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_procs.dm deleted file mode 100644 index cd42c0628b68..000000000000 --- a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_procs.dm +++ /dev/null @@ -1,131 +0,0 @@ -// Phase shifting procs (and related procs) -/mob/living/simple_mob/shadekin/proc/phase_shift() - var/turf/T = get_turf(src) - if(!T.CanPass(src,T) || loc != T) - to_chat(src,"You can't use that here!") - return FALSE - - forceMove(T) - set_stunned(0) - set_paralyzed(0) - if(buckled) - buckled.unbuckle_mob() - if(pulledby) - pulledby.stop_pulling() - stop_pulling() - - //Shifting in - if(ability_flags & AB_PHASE_SHIFTED) - ability_flags &= ~AB_PHASE_SHIFTED - mouse_opacity = 2 - name = real_name - for(var/belly in vore_organs) - var/obj/belly/B = belly - B.escapable = initial(B.escapable) - - cut_overlays() - alpha = initial(alpha) - invisibility = initial(invisibility) - see_invisible = initial(see_invisible) - incorporeal_move = initial(incorporeal_move) - density = initial(density) - force_max_speed = initial(force_max_speed) - - //Cosmetics mostly - flick("tp_in",src) - custom_emote(1,"phases in!") - sleep(5) //The duration of the TP animation - - //Potential phase-in vore - if(can_be_drop_pred) //Toggleable in vore panel - var/list/potentials = living_mobs(0) - if(potentials.len) - var/mob/living/target = pick(potentials) - if(istype(target) && vore_selected) - target.forceMove(vore_selected) - to_chat(target,"\The [src] phases in around you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!") - - // Do this after the potential vore, so we get the belly - update_icon() - - //Affect nearby lights - var/destroy_lights = 0 - if(eye_state == RED_EYES) - destroy_lights = 80 - if(eye_state == PURPLE_EYES) - destroy_lights = 25 - - for(var/obj/machinery/light/L in GLOB.machines) - if(L.z != z || get_dist(src,L) > 10) - continue - - if(prob(destroy_lights)) - spawn(rand(5,25)) - L.broken() - else - L.flicker(10) - - //Shifting out - else - ability_flags |= AB_PHASE_SHIFTED - mouse_opacity = 0 - custom_emote(1,"phases out!") - real_name = name - name = "Something" - - for(var/belly in vore_organs) - var/obj/belly/B = belly - B.escapable = FALSE - - cut_overlays() - flick("tp_out",src) - sleep(5) - invisibility = INVISIBILITY_LEVEL_TWO - see_invisible = INVISIBILITY_LEVEL_TWO - update_icon() - alpha = 127 - - incorporeal_move = TRUE - density = FALSE - force_max_speed = TRUE - -/mob/living/simple_mob/shadekin/UnarmedAttack() - if(ability_flags & AB_PHASE_SHIFTED) - return FALSE //Nope. - - . = ..() - -/mob/living/simple_mob/shadekin/can_fall() - if(ability_flags & AB_PHASE_SHIFTED) - return FALSE //Nope! - - return ..() - -/mob/living/simple_mob/shadekin/zMove(direction) - if(ability_flags & AB_PHASE_SHIFTED) - var/turf/destination = get_vertical_step(src, direction) - if(destination) - forceMove(destination) - return TRUE - - return ..() - -// Healing others -/mob/living/simple_mob/shadekin/proc/mend_other() - //I hate to crunch a view() but I only want ones I can see - var/list/viewed = oview(1) - var/list/targets = list() - for(var/mob/living/L in viewed) - targets += L - if(!targets.len) - to_chat(src,"Nobody nearby to mend!") - return FALSE - - var/mob/living/target = input(src,"Pick someone to mend:","Mend Other") as null|anything in targets - if(!target) - return FALSE - - target.add_modifier(/datum/modifier/shadekin/heal_boop,1 MINUTE) - visible_message("\The [src] gently places a hand on \the [target]...") - face_atom(target) - return TRUE diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm deleted file mode 100644 index f3229b147d27..000000000000 --- a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm +++ /dev/null @@ -1,388 +0,0 @@ -/datum/vision/baseline/shadekin - hard_darksight = 0 - -/mob/living/simple_mob/shadekin //Spawning the prototype spawns a random one, see initialize() - name = "shadekin" - desc = "Some sort of fluffer. Big ears, long tail." - catalogue_data = list(/datum/category_item/catalogue/fauna/shadekin) - icon = 'icons/mob/vore_shadekin.dmi' - icon_state = "map_example" - icon_living = "map_example" - faction = "shadekin" - ui_icons = 'icons/screen/hud/common/shadekin.dmi' - mob_class = MOB_CLASS_HUMANOID - mob_bump_flag = HUMAN - - maxHealth = 200 - health = 200 - - movement_cooldown = 2 - vision_innate = /datum/vision/baseline/shadekin - attack_sound = 'sound/weapons/bladeslice.ogg' - has_langs = list(LANGUAGE_GALCOM,LANGUAGE_SHADEKIN) - - legacy_melee_damage_lower = 10 - legacy_melee_damage_upper = 20 - - min_oxy = 0 - max_oxy = 0 - min_tox = 0 - max_tox = 0 - min_co2 = 0 - max_co2 = 0 - min_n2 = 0 - max_n2 = 0 - minbodytemp = 0 - maxbodytemp = 900 - - say_list_type = /datum/say_list/shadekin - - response_help = "pets the" - response_disarm = "bops the" - response_harm = "hits the" - - attacktext = list("mauled","slashed","clawed") - friendly = list("boops", "pawbs", "mars softly at", "sniffs on") - - vore_active = TRUE - vore_pounce_chance = 10 - vore_icons = SA_ICON_LIVING - swallowTime = 2 SECONDS - vore_escape_chance = 25 - - //None, they stay as their defaults. - vore_digest_chance = 0 - vore_absorb_chance = 0 - vore_bump_chance = 0 //They follow people, this would be DENGEROUS - - var/eye_state = RED_EYES //Eye color/energy gain/loss mode - var/eye_icon_state = null //Default, changed in init - var/eye_desc //Eye color description added to examine - - var/mob/living/carbon/human/henlo_human //Human we're stalking currently - - //Behavior - var/stalker = TRUE //Do we creep up on humans - var/shy_approach = FALSE //Do we creep up slowly on humans to boop them - - //Icon handling - var/image/tailimage //Cached tail image - - //Darknesssss - var/energy = 100 //For abilities - var/energy_adminbuse = FALSE //For adminbuse infinite energy - var/dark_gains = 0 //Last tick's change in energy - var/ability_flags = 0 //Flags for active abilities - var/atom/movable/screen/darkhud //Holder to update this icon - var/atom/movable/screen/energyhud //Holder to update this icon - - var/list/shadekin_abilities - -/mob/living/simple_mob/shadekin/Initialize(mapload) - //You spawned the prototype, and want a totally random one. - if(type == /mob/living/simple_mob/shadekin) - - //I'm told by VerySoft these are the liklihood values - var/list/sk_types = list( - /mob/living/simple_mob/shadekin/red = 20, //Actively seek people out to nom, so fairly common to see (relatively speaking), - /mob/living/simple_mob/shadekin/blue = 15, //Explorers that like to interact with people, so still fairly common, - /mob/living/simple_mob/shadekin/purple = 15, //Also explorers that may or may not homf people, - /mob/living/simple_mob/shadekin/yellow = 1 //Very rare, usually never leaves their home - ) - var/new_type = pickweight(sk_types) - - new new_type(loc) - return INITIALIZE_HINT_QDEL - - if(icon_state == "map_example") - icon_state = pick("white","dark","brown") - - icon_living = icon_state - - switch(eye_state) - if(BLUE_EYES) - eye_icon_state = "e_blue" - if(RED_EYES) - eye_icon_state = "e_red" - if(PURPLE_EYES) - eye_icon_state = "e_purple" - if(YELLOW_EYES) - eye_icon_state = "e_yellow" - if(GREEN_EYES) - eye_icon_state = "e_green" - if(ORANGE_EYES) - eye_icon_state = "e_orange" - else - eye_icon_state = "e_red" - - tailimage = image('icons/mob/vore_shadekin64.dmi',null,icon_state) - tailimage.pixel_x = -16 - - if(eye_desc) - desc += " This one has [eye_desc]!" - - var/list/ability_types = subtypesof(/obj/effect/shadekin_ability) - shadekin_abilities = list() - for(var/type in ability_types) - shadekin_abilities += new type(null, src) - - update_icon() - - return ..() - -/mob/living/simple_mob/shadekin/Destroy() - QDEL_LIST_NULL(shadekin_abilities) - . = ..() - -/mob/living/simple_mob/shadekin/init_vore() - if(LAZYLEN(vore_organs)) - return - - var/obj/belly/B = new /obj/belly(src) - vore_selected = B - B.immutable = 1 - B.name = vore_stomach_name ? vore_stomach_name : "stomach" - B.desc = vore_stomach_flavor ? vore_stomach_flavor : "Your surroundings are warm, soft, and slimy. Makes sense, considering you're inside \the [name]." - B.digest_mode = vore_default_mode - B.escapable = vore_escape_chance > 0 - B.escapechance = vore_escape_chance - B.digestchance = vore_digest_chance - B.absorbchance = vore_absorb_chance - B.human_prey_swallow_time = swallowTime - B.nonhuman_prey_swallow_time = swallowTime - B.vore_verb = "swallow" - // TODO - Customizable per mob - B.emote_lists[DM_HOLD] = list( - "The walls gently squeeze against you. The wet sounds of shifting flesh against your form fill the air.", - "The hot, humid air rushes around you for a moment as the creature urps. The walls clench in around you for a moment, before relaxing again.", - "Your body is soaked in the fluids that cling to the churning walls. They squeeze across your form gently, conforming to your shape.", - "You can feel the world around you shift and sway as the creature moves! The flesh is stretchy, doughy. You can sink into it a little ways before it bounces back, curling you into a small shape." - ) - B.emote_lists[DM_DIGEST] = list( - "The walls slop thick slime across your body! It tingles briefly before the sting and ache sets in!", - "The sound of your body slipping and sliding against the powerfully churning stomach fills the air!", - "The grip of that stomach is harsh. Eagerly mushing and rubbing that slime into your body in attempts to break you down!", - "The intense churning and grinding jostles your around within the thick slime as you're slowly broken down!" - ) - B.emote_lists[DM_ABSORB] = list( - "The walls cling to you awfully close... It's almost like you're sinking into them.", - "You can feel the walls press in tightly against you, clinging to you posessively!", - "It almost feels like you're sinking into the soft, doughy flesh!", - "You can feel the walls press in around you. Almost molten, so squishy!!" - ) - B.emote_lists[DM_DRAIN] = list( - "The walls churn down on you heavily!! It's hard to move!", - "You can feel yourself getting weaker with every moment! The doughy walls sap your strength!", - "You're practically smothered in the oppressive heat of the creature's stomach!", - "It's hot, wet and tight!" - ) - B.emote_lists[DM_HEAL] = list( - "The walls pulse against you almost rhythmically. It feels nice, almost like a massage.", - "You're gently squeezed in pleasant warmth, softly churned.", - "The doughy feel of the heavy flesh clinging to you makes you feel a little stronger with every passing moment.", - "The flesh caresses across your body gently as you're held." - ) - B.digest_messages_prey = list( - "Your body is steadily softened more and more over time! Eventually you pass out. The creature's stomach rumbles powerfully as you are reduced to paste, processed for energy!", - "The creature's slimy gut lets out a heavy groan as you're slowly melted away. Gushing deeper through the creature.", - "The stinging and aching gives way to numbness as you're slowly smothered out. Your body is steadily reduced to nutrients and energy for the creature to continue on its way.", - "The chaos of being digested fades as you're snuffed out by a harsh clench! You're steadily broken down into a thick paste, processed and absorbed by the predator!" - ) - -/mob/living/simple_mob/shadekin/Life(seconds, times_fired) - if((. = ..())) - return - - if(ability_flags & AB_PHASE_SHIFTED) - density = FALSE - -/mob/living/simple_mob/shadekin/BiologicalLife(seconds, times_fired) - if((. = ..())) - return - - //Convert spare nutrition into energy at a certain ratio - if(. && nutrition > initial(nutrition) && energy < 100) - nutrition = max(0, nutrition-5) - energy = min(100,energy+1) - -/mob/living/simple_mob/shadekin/update_icon() - . = ..() - - cut_overlay(tailimage) - - tailimage.icon_state = icon_state - - add_overlay(tailimage) - add_overlay(eye_icon_state) - -/mob/living/simple_mob/shadekin/statpanel_data(client/C) - . = ..() - if(C.statpanel_tab("Shadekin")) - STATPANEL_DATA_LINE("") - for(var/A in shadekin_abilities) - var/obj/effect/shadekin_ability/ability = A - ability.atom_button_text() - STATPANEL_DATA_CLICK("[ability.ability_name]", "[ability.name]", "\ref[ability]") - -//They phase back to the dark when killed -/mob/living/simple_mob/shadekin/death(gibbed, deathmessage = "phases to somewhere far away!") - cut_overlays() - icon_state = "" - flick("tp_out",src) - spawn(1 SECOND) - qdel(src) //Back from whence you came! - - . = ..(FALSE, deathmessage) - -//They reach nutritional equilibrium (important for blue-eyes healbelly) -/mob/living/simple_mob/shadekin/Life(seconds, times_fired) - if((. = ..())) - handle_shade() - -/mob/living/simple_mob/shadekin/is_incorporeal() - if(ability_flags & AB_PHASE_SHIFTED) - return TRUE - return FALSE - -/mob/living/simple_mob/shadekin/handle_atmos() - if(ability_flags & AB_PHASE_SHIFTED) - return - else - return .=..() - -/mob/living/simple_mob/shadekin/proc/handle_shade() - //Shifted kin don't gain/lose energy (and save time if we're at the cap) - var/darkness = 1 - - - var/turf/T = get_turf(src) - if(!T) - dark_gains = 0 - return - - var/brightness = T.get_lumcount() //Brightness in 0.0 to 1.0 - darkness = 1-brightness //Invert - - if(ability_flags & AB_PHASE_SHIFTED) - dark_gains = 0 - else - //Heal (very) slowly in good darkness - if(darkness >= 0.75) - adjustFireLoss(-0.05) - adjustBruteLoss(-0.05) - adjustToxLoss(-0.05) - - switch(eye_state) - //Blue has constant, steady (slow) regen and ignores darkness. - if(BLUE_EYES) - dark_gains = 0.5 - //Red has extremely tiny energy buildup in dark, none in light, and hunts for energy. - if(RED_EYES) - if(darkness >= 0.75) - dark_gains = 0.25 - //Purple eyes have moderate gains in darkness and loss in light. - if(PURPLE_EYES) - dark_gains = round((darkness - 0.5) * 2, 0.1) - //Yellow has extreme gains in darkness and loss in light. - if(YELLOW_EYES) - dark_gains = round((darkness - 0.5) * 4, 0.1) - //Similar to blues, but passive is less, and affected by dark - if(GREEN_EYES) - dark_gains = 0.25 - dark_gains += round((darkness - 0.5), 0.1) - //More able to get energy out of the dark, worse attack gains tho - if(ORANGE_EYES) - if(darkness >= 0.65) - dark_gains = 0.30 - - energy = max(0,min(initial(energy),energy + dark_gains)) - - if(energy_adminbuse) - energy = 100 - - //Update turf darkness hud - if(darkhud) - switch(darkness) - if(0.80 to 1.00) - darkhud.icon_state = "dark2" - if(0.60 to 0.80) - darkhud.icon_state = "dark1" - if(0.40 to 0.60) - darkhud.icon_state = "dark" - if(0.20 to 0.40) - darkhud.icon_state = "dark-1" - if(0.00 to 0.20) - darkhud.icon_state = "dark-2" - - //Update energy storage hud - if(energyhud) - switch(energy) - if(80 to INFINITY) - energyhud.icon_state = "energy0" - if(60 to 80) - energyhud.icon_state = "energy1" - if(40 to 60) - energyhud.icon_state = "energy2" - if(20 to 40) - energyhud.icon_state = "energy3" - if(0 to 20) - energyhud.icon_state = "energy4" - -/mob/living/simple_mob/shadekin/speech_bubble_appearance() - return "ghost" - -/mob/living/simple_mob/shadekin/apply_melee_effects(var/atom/A) - . = ..(A) - if(isliving(A)) //We punched something! - var/mob/living/L = A - if(L.stat != DEAD) - var/gains = 0 - switch(eye_state) - if(RED_EYES) - gains = 8 - if(BLUE_EYES) - gains = 1 - if(PURPLE_EYES) - gains = 4 - if(YELLOW_EYES) - gains = 3 - if(GREEN_EYES) - gains = 1 - if(ORANGE_EYES) - gains = 5 - - energy += gains - -//Special hud elements for darkness and energy gains -/mob/living/simple_mob/shadekin/extra_huds(var/datum/hud/hud,var/icon/ui_style,var/list/hud_elements) - //Darkness hud - darkhud = new /atom/movable/screen() - darkhud.icon = ui_style - darkhud.icon_state = "dark" - darkhud.name = "darkness" - darkhud.screen_loc = "CENTER-2:16,SOUTH:5" //Left of the left hand - darkhud.alpha = 150 - hud_elements |= darkhud - - //Energy hud - energyhud = new /atom/movable/screen() - energyhud.icon = ui_style - energyhud.icon_state = "energy0" - energyhud.name = "energy" - energyhud.screen_loc = "CENTER+1:16,SOUTH:5" //Right of the right hand - energyhud.alpha = 150 - hud_elements |= energyhud - -// When someone clicks us with an empty hand -/mob/living/simple_mob/shadekin/attack_hand(mob/user, list/params) - . = ..() - if(user.a_intent == INTENT_HELP) - shy_approach = FALSE //ACCLIMATED - -/datum/say_list/shadekin - speak = list("Marrr.", "Marrr?", "Marrr!") - emote_hear = list("chrrrrrs", "wurbles", "wrrrrbles") - emote_see = list("tailtwitches", "earflicks") - say_maybe_target = list("...mar?") - say_got_target = list("MAR!!!") - //reactions = list("Mar?" = "Marrr!", "Mar!" = "Marrr???", "Mar." = "Marrr.") diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/types.dm b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/types.dm deleted file mode 100644 index 810735642908..000000000000 --- a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/types.dm +++ /dev/null @@ -1,326 +0,0 @@ -///////////////////////////////////////////////////////////////// -/mob/living/simple_mob/shadekin/red - name = "red-eyed shadekin" - eye_state = RED_EYES - //hostile = TRUE - //animal = TRUE - //stop_when_pulled = FALSE - //destroy_surroundings = TRUE - armor_legacy_mob = list( - "melee" = 30, - "bullet" = 20, - "laser" = 20, - "energy" = 50, - "bomb" = 10, - "bio" = 100, - "rad" = 100) - - eye_desc = "red eyes" - - vore_stomach_flavor = "You slip past pointy triangle teeth and down the slick, \ - slippery gullet of the creature. It's warm, and the air is thick. You can hear \ - its body squelch and shift around you as you settle into its stomach! Thick digestive \ - enzymes cling to you within that dark space, tingling and stinging immediately! The weight of \ - the doughy walls press in around you instantly, churning you up as you begin to digest!" - - player_msg = "You hunt for energy to fuel yourself, not minding in the least \ - if you strip it off unsuspecting prey. You're stronger than other shadekin, faster, and more capable in \ - a brawl, but you barely generate any of your own energy. You can stand in a dark spot to gather scraps \ - of energy in a pinch, but otherwise need to take it, by force if necessary." - -/mob/living/simple_mob/shadekin/red/white - icon_state = "white" -/mob/living/simple_mob/shadekin/red/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/red/brown - icon_state = "brown" - -/mob/living/simple_mob/shadekin/red/ai - ai_holder_type = /datum/ai_holder/polaris/simple_mob/melee - -/mob/living/simple_mob/shadekin/red/ai/white - icon_state = "white" -/mob/living/simple_mob/shadekin/red/ai/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/red/ai/brown - icon_state = "brown" - -///////////////////////////////////////////////////////////////// -/mob/living/simple_mob/shadekin/blue - name = "blue-eyed shadekin" - eye_state = BLUE_EYES - health = 100 - //hostile = FALSE - //animal = FALSE - //stop_when_pulled = TRUE - //specific_targets = TRUE //For finding injured people - //destroy_surroundings = FALSE - vore_default_mode = DM_HEAL - vore_escape_chance = 75 - vore_standing_too = 1 - vore_pounce_chance = 100 - swallowTime = 4 SECONDS //A little longer to compensate for the above - vore_ignores_undigestable = FALSE - attacktext = list("shoved") - armor_legacy_mob = list( - "melee" = 5, - "bullet" = 5, - "laser" = 5, - "energy" = 5, - "bomb" = 0, - "bio" = 100, - "rad" = 100) - - eye_desc = "blue eyes" - shy_approach = TRUE - stalker = TRUE - vore_stomach_flavor = "You slip past pointy triangle teeth and down the slick, \ - slippery gullet of the creature. It's warm, and the air is thick. You can hear its body \ - squelch and shift around you as you settle into its stomach! It's oddly calm, and very dark. \ - The doughy flesh rolls across your form in gentle waves. The aches and pains across your form slowly begin to \ - diminish, your body is healing much faster than normal! You're also soon soaked in harmless slime." - - player_msg = "You've chosen to generate your own energy rather than taking \ - it from others. Most of the time, anyway. You don't have a need to steal energy from others, and gather it up \ - without doing so, albeit slowly. Dark and light are irrelevant to you, they are just different places to explore and \ - discover new things and new people." - -/mob/living/simple_mob/shadekin/blue/white - icon_state = "white" -/mob/living/simple_mob/shadekin/blue/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/blue/brown - icon_state = "brown" - -/mob/living/simple_mob/shadekin/blue/ai - ai_holder_type = /datum/ai_holder/polaris/simple_mob/passive - -/mob/living/simple_mob/shadekin/blue/ai/white - icon_state = "white" -/mob/living/simple_mob/shadekin/blue/ai/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/blue/ai/brown - icon_state = "brown" - -///////////////////////////////////////////////////////////////// -/mob/living/simple_mob/shadekin/purple - name = "purple-eyed shadekin" - eye_state = PURPLE_EYES - health = 150 - //hostile = FALSE - //animal = TRUE - //stop_when_pulled = FALSE - //destroy_surroundings = TRUE - vore_default_mode = DM_HOLD - vore_digest_chance = 25 - vore_absorb_chance = 25 - armor_legacy_mob = list( - "melee" = 15, - "bullet" = 15, - "laser" = 15, - "energy" = 15, - "bomb" = 15, - "bio" = 100, - "rad" = 100) - - eye_desc = "purple eyes" - shy_approach = TRUE - stalker = TRUE - vore_stomach_flavor = "You slip past pointy triangle teeth and down the slick, slippery gullet of the creature. \ - It's warm, and the air is thick. You can hear its body squelch and shift around you as you settle into its stomach! \ - It's relatively calm inside the dark organ. Wet and almost molten for how gooey your surroundings feel. \ - You can feel the doughy walls cling to you posessively... It's almost like you could sink into them. \ - There is also an ominous gurgling from somewhere nearby..." - - player_msg = "You're familiar with generating your own energy, but occasionally \ - steal it from others when it suits you. You generate energy at a moderate pace in dark areas, and staying in well-lit \ - areas is taxing on your energy. You can harvest energy from others in a fight, but since you don't need to, you may \ - just choose to simply not fight." - -/mob/living/simple_mob/shadekin/purple/white - icon_state = "white" -/mob/living/simple_mob/shadekin/purple/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/purple/brown - icon_state = "brown" - -/mob/living/simple_mob/shadekin/purple/ai - ai_holder_type = /datum/ai_holder/polaris/simple_mob/retaliate - -/mob/living/simple_mob/shadekin/purple/ai/white - icon_state = "white" -/mob/living/simple_mob/shadekin/purple/ai/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/purple/ai/brown - icon_state = "brown" - -///////////////////////////////////////////////////////////////// -/mob/living/simple_mob/shadekin/yellow - name = "yellow-eyed shadekin" - eye_state = YELLOW_EYES - health = 100 - //hostile = FALSE - //animal = TRUE - //stop_when_pulled = FALSE - //destroy_surroundings = TRUE - vore_default_mode = DM_DRAIN - vore_digest_chance = 5 - vore_ignores_undigestable = FALSE - armor_legacy_mob = list( - "melee" = 5, - "bullet" = 5, - "laser" = 5, - "energy" = 5, - "bomb" = 0, - "bio" = 100, - "rad" = 100) - - eye_desc = "yellow eyes" - stalker = FALSE - vore_stomach_flavor = "You slip past pointy triangle teeth and down the slick, slippery gullet \ - of the creature. It's warm, and the air is thick. You can hear its body squelch and shift around you \ - as you settle into its stomach! The doughy walls within cling to you heavily, churning down on you, wearing \ - you out!! There doesn't appear to be any actual danger here, harmless slime clings to you, but it's getting \ - harder and harder to move as those walls press in on you insistently!" - - player_msg = "Your kind rarely ventures into realspace. Being in any well-lit \ - area is very taxing on you, but you gain energy extremely fast in any very dark area. You're weaker than other \ - shadekin, but your fast energy generation in the dark allows you to phase shift more often." - -/mob/living/simple_mob/shadekin/yellow/white - icon_state = "white" -/mob/living/simple_mob/shadekin/yellow/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/yellow/brown - icon_state = "brown" - -/mob/living/simple_mob/shadekin/yellow/ai - ai_holder_type = /datum/ai_holder/polaris/simple_mob/melee/hit_and_run - -/mob/living/simple_mob/shadekin/yellow/ai/white - icon_state = "white" -/mob/living/simple_mob/shadekin/yellow/ai/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/yellow/ai/brown - icon_state = "brown" - -/mob/living/simple_mob/shadekin/yellow/ai/retaliate - ai_holder_type = /datum/ai_holder/polaris/simple_mob/retaliate - -/mob/living/simple_mob/shadekin/yellow/ai/retaliate/white - icon_state = "white" -/mob/living/simple_mob/shadekin/yellow/ai/retaliate/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/yellow/ai/retaliate/brown - icon_state = "brown" - -///////////////////////////////////////////////////////////////// -/mob/living/simple_mob/shadekin/green - name = "green-eyed shadekin" - eye_state = GREEN_EYES - health = 125 - //hostile = FALSE - //animal = TRUE - //stop_when_pulled = FALSE - //destroy_surroundings = TRUE - vore_default_mode = DM_DRAIN - vore_digest_chance = 0 - vore_ignores_undigestable = FALSE - armor_legacy_mob = list( - "melee" = 5, - "bullet" = 5, - "laser" = 5, - "energy" = 5, - "bomb" = 0, - "bio" = 100, - "rad" = 100) - - eye_desc = "green eyes" - stalker = TRUE - vore_stomach_flavor = "You slip past pointy triangle teeth and down the slick, slippery gullet \ - of the creature. It's warm, and the air is thick. You can hear its body squelch and shift around you \ - as you settle into its stomach! The doughy walls within cling to you heavily, churning down on you, wearing \ - you out!! There doesn't appear to be any actual danger here, harmless slime clings to you, but it's getting \ - harder and harder to move as those walls press in on you insistently!" - - player_msg = "Your kind rarely ventures into realspace. Being in any well-lit area is very taxing on you, but you \ - have more experience than your yellow-eyed cousins. You gain energy decently fast in any very dark area. You're weaker than other \ - shadekin, but your slight energy generation constnatly, and especially in the dark allows for a good mix of uses." - -/mob/living/simple_mob/shadekin/green/white - icon_state = "white" -/mob/living/simple_mob/shadekin/green/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/green/brown - icon_state = "brown" - -/mob/living/simple_mob/shadekin/green/ai - ai_holder_type = /datum/ai_holder/polaris/simple_mob/passive - -/mob/living/simple_mob/shadekin/green/ai/white - icon_state = "white" -/mob/living/simple_mob/shadekin/green/ai/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/green/ai/brown - icon_state = "brown" - -///////////////////////////////////////////////////////////////// -/mob/living/simple_mob/shadekin/orange - name = "orange-eyed shadekin" - eye_state = ORANGE_EYES - health = 175 - //hostile = TRUE - //animal = TRUE - //stop_when_pulled = FALSE - //destroy_surroundings = TRUE - armor_legacy_mob = list( - "melee" = 20, - "bullet" = 15, - "laser" = 15, - "energy" = 25, - "bomb" = 10, - "bio" = 100, - "rad" = 100) - - eye_desc = "orange eyes" - - vore_stomach_flavor = "You slip past pointy triangle teeth and down the slick, \ - slippery gullet of the creature. It's warm, and the air is thick. You can hear \ - its body squelch and shift around you as you settle into its stomach! Thick digestive \ - enzymes cling to you within that dark space, tingling and stinging immediately! The weight of \ - the doughy walls press in around you instantly, churning you up as you begin to digest!" - - player_msg = "You usually hunt for energy to fuel yourself, though not as often as your red-eyed cousins. \ - You're stronger than most shadekin, faster, and more capable in a brawl, but you don't generate much of your own energy. \ - You can stand in a dark spot to gather some energy, but otherwise need to take it, by force if necessary." - -/mob/living/simple_mob/shadekin/orange/white - icon_state = "white" -/mob/living/simple_mob/shadekin/orange/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/orange/brown - icon_state = "brown" - -/mob/living/simple_mob/shadekin/orange/ai - ai_holder_type = /datum/ai_holder/polaris/simple_mob/melee - -/mob/living/simple_mob/shadekin/orange/ai/white - icon_state = "white" -/mob/living/simple_mob/shadekin/orange/ai/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/orange/ai/brown - icon_state = "brown" - -///////////////////////////////////////////////////////////////// -//Fluffy specific fluffer -/mob/living/simple_mob/shadekin/blue/rivyr - name = "Rivyr" - desc = "She appears to be a fluffer of some sort. Deep blue eyes and curious attitude." - icon_state = "rivyr" - eye_desc = "" - vore_stomach_flavor = "Blue flesh gleams in the fading light as you slip down the little mar's gullet! \ - Gooey flesh and heat surrounds your form as you're tucked away into the darkness of her stomach! Thick slimes cling \ - to you, but they seem to be harmless. The organ gently churns around you, clinging to your shape and forcing \ - you to curl up a bit. You can feel her rub at you some through the layers of flesh and fluff, while aches \ - and pains begin to fade away across your body." - player_msg = "Mar? Mar mar. Mar mar mar. Mar. Mar mar? Mar! Mar. Marrrr." diff --git a/code/modules/species/shadekin/crew_shadekin_abilities.dm b/code/modules/species/shadekin/crew_shadekin_abilities.dm index 359b1eca0824..e6b878cb70a0 100644 --- a/code/modules/species/shadekin/crew_shadekin_abilities.dm +++ b/code/modules/species/shadekin/crew_shadekin_abilities.dm @@ -109,7 +109,7 @@ on_created_text = "You drag part of The Dark into realspace, enveloping yourself." on_expired_text = "You lose your grasp on The Dark and realspace reasserts itself." stacks = MODIFIER_STACK_EXTEND - var/mob/living/simple_mob/shadekin/my_kin + var/mob/living/carbon/human/my_kin /datum/modifier/crew_shadekin/create_shade/tick() if(my_kin.ability_flags & AB_PHASE_SHIFTED) diff --git a/code/modules/species/shadekin/shadekin_abilities.dm b/code/modules/species/shadekin/shadekin_abilities.dm index 79842dfb72b4..218cb4d4cd25 100644 --- a/code/modules/species/shadekin/shadekin_abilities.dm +++ b/code/modules/species/shadekin/shadekin_abilities.dm @@ -287,7 +287,7 @@ on_created_text = "You drag part of The Dark into realspace, enveloping yourself." on_expired_text = "You lose your grasp on The Dark and realspace reasserts itself." stacks = MODIFIER_STACK_EXTEND - var/mob/living/simple_mob/shadekin/my_kin + var/mob/living/carbon/human/my_kin /datum/modifier/shadekin/create_shade/tick() if(my_kin.ability_flags & AB_PHASE_SHIFTED) From 2f758a1355bffe91c8ec15e58344868433af5e7a Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Sun, 1 Sep 2024 15:03:44 -0400 Subject: [PATCH 16/35] completely removes simplemob automated vore support (#6714) --- code/modules/events/carp_migration.dm | 2 +- .../maps/away_missions/140x140/snowfield.dm | 1 - .../mob/living/simple_mob/simple_mob_vr.dm | 141 +----------------- .../simple_mob/subtypes/animal/pets/cat_vr.dm | 32 ---- .../simple_mob/subtypes/animal/pets/fox_vr.dm | 96 ------------ .../living/simple_mob/subtypes/vore/bee.dm | 2 - .../simple_mob/subtypes/vore/cookiegirl.dm | 8 - .../subtypes/vore/corrupt_hounds.dm | 18 --- .../simple_mob/subtypes/vore/deathclaw.dm | 6 - .../simple_mob/subtypes/vore/demon/demon.dm | 8 - .../subtypes/vore/demon/demon_subtypes.dm | 2 - .../living/simple_mob/subtypes/vore/dino.dm | 2 - .../living/simple_mob/subtypes/vore/dragon.dm | 5 - .../living/simple_mob/subtypes/vore/fennec.dm | 6 - .../living/simple_mob/subtypes/vore/frog.dm | 3 - .../living/simple_mob/subtypes/vore/hippo.dm | 13 -- .../living/simple_mob/subtypes/vore/jelly.dm | 3 - .../living/simple_mob/subtypes/vore/mimic.dm | 4 - .../simple_mob/subtypes/vore/morph/morph.dm | 1 - .../living/simple_mob/subtypes/vore/otie.dm | 43 ------ .../simple_mob/subtypes/vore/panther.dm | 4 - .../living/simple_mob/subtypes/vore/rat.dm | 4 - .../simple_mob/subtypes/vore/redpanda.dm | 13 -- .../living/simple_mob/subtypes/vore/snake.dm | 3 - .../simple_mob/subtypes/vore/solargrub.dm | 10 -- .../simple_mob/subtypes/vore/solarmoth_ch.dm | 6 - .../living/simple_mob/subtypes/vore/vore.dm | 2 +- .../living/simple_mob/subtypes/vore/wolf.dm | 2 - .../simple_mob/subtypes/vore/wolfgirl.dm | 3 - .../subtypes/vore/zz_vore_overrides.dm | 86 ----------- code/modules/vore/eating/living_vr.dm | 5 - code/modules/vore/eating/simple_animal_vr.dm | 79 ---------- maps/away_missions/140x140/zoo.dmm | 2 +- 33 files changed, 6 insertions(+), 609 deletions(-) diff --git a/code/modules/events/carp_migration.dm b/code/modules/events/carp_migration.dm index 072178188b82..8459e76d4d4c 100644 --- a/code/modules/events/carp_migration.dm +++ b/code/modules/events/carp_migration.dm @@ -84,7 +84,7 @@ GLOBAL_LIST_INIT(carp_count,list())// a list of Z levels (string), associated wi if(WEST) return locate(clearance, rand(clearance, world.maxy - clearance), Z) -/datum/event/carp_migration/proc/check_gib(var/mob/living/simple_mob/hostile/carp/M) //awesome road kills +/datum/event/carp_migration/proc/check_gib(var/mob/living/simple_mob/animal/space/carp/M) //awesome road kills if(M.health <= 0 && prob(60)) M.gib() diff --git a/code/modules/maps/away_missions/140x140/snowfield.dm b/code/modules/maps/away_missions/140x140/snowfield.dm index 376d17f2e380..77ae30c9e42f 100644 --- a/code/modules/maps/away_missions/140x140/snowfield.dm +++ b/code/modules/maps/away_missions/140x140/snowfield.dm @@ -59,7 +59,6 @@ icon_living = "polarbear" icon_dead = "polarbear-dead" icon_gib = "bear-gib" - vore_active = 1 say_list_type = /datum/say_list/polar_bear faction = "polar" diff --git a/code/modules/mob/living/simple_mob/simple_mob_vr.dm b/code/modules/mob/living/simple_mob/simple_mob_vr.dm index 73b8dab42235..ec7c1836ebe5 100644 --- a/code/modules/mob/living/simple_mob/simple_mob_vr.dm +++ b/code/modules/mob/living/simple_mob/simple_mob_vr.dm @@ -1,45 +1,8 @@ -// Flags for specifying which states we have vore icon_states for. -#define SA_ICON_LIVING 0x01 -#define SA_ICON_DEAD 0x02 -#define SA_ICON_REST 0x04 - /mob/living/simple_mob base_attack_cooldown = 15 var/temperature_range = 40 // How close will they get to environmental temperature before their body stops changing its heat - var/vore_active = 0 // If vore behavior is enabled for this mob - - var/vore_capacity = 1 // The capacity (in people) this person can hold - var/vore_max_size = RESIZE_HUGE // The max size this mob will consider eating - var/vore_min_size = RESIZE_TINY // The min size this mob will consider eating - var/vore_bump_chance = 0 // Chance of trying to eat anyone that bumps into them, regardless of hostility - var/vore_bump_emote = "grabs hold of" // Allow messages for bumpnom mobs to have a flavorful bumpnom - var/vore_pounce_chance = 5 // Chance of this mob knocking down an opponent - var/vore_pounce_cooldown = 0 // Cooldown timer - if it fails a pounce it won't pounce again for a while - var/vore_pounce_successrate = 100 // Chance of a pounce succeeding against a theoretical 0-health opponent - var/vore_pounce_falloff = 1 // Success rate falloff per %health of target mob. - var/vore_pounce_maxhealth = 80 // Mob will not attempt to pounce targets above this %health - var/vore_standing_too = 0 // Can also eat non-stunned mobs - var/vore_ignores_undigestable = 1 // Refuse to eat mobs who are undigestable by the prefs toggle. - var/swallowsound = null // What noise plays when you succeed in eating the mob. - - var/vore_default_mode = DM_DIGEST // Default bellymode (DM_DIGEST, DM_HOLD, DM_ABSORB) - var/vore_default_flags = 0 // No flags - var/vore_digest_chance = 25 // Chance to switch to digest mode if resisted - var/vore_absorb_chance = 0 // Chance to switch to absorb mode if resisted - var/vore_escape_chance = 25 // Chance of resisting out of mob - - var/vore_stomach_name // The name for the first belly if not "stomach" - var/vore_stomach_flavor // The flavortext for the first belly if not the default - - var/vore_default_item_mode = IM_DIGEST_FOOD //How belly will interact with items - var/vore_default_contaminates = TRUE //Will it contaminate? - var/vore_default_contamination_flavor = "Generic" //Contamination descriptors - var/vore_default_contamination_color = "green" //Contamination color - - var/vore_fullness = 0 // How "full" the belly is (controls icons) - var/vore_icons = 0 // Bitfield for which fields we have vore icons for. var/life_disabled = 0 // For performance reasons var/obj/item/radio/headset/mob_headset/mob_radio //Adminbus headset for simplemob shenanigans. @@ -47,67 +10,20 @@ // Release belly contents before being gc'd! /mob/living/simple_mob/Destroy() release_vore_contents() - prey_excludes.Cut() - . = ..() + return ..() //For all those ID-having mobs /mob/living/simple_mob/GetIdCard() if(access_card) return access_card -// Update fullness based on size & quantity of belly contents -/mob/living/simple_mob/proc/update_fullness() - var/new_fullness = 0 - for(var/belly in vore_organs) - var/obj/belly/B = belly - for(var/mob/living/M in B) - new_fullness += M.size_multiplier - new_fullness = round(new_fullness, 1) // Because intervals of 0.25 are going to make sprite artists cry. - vore_fullness = min(vore_capacity, new_fullness) - -/mob/living/simple_mob/update_icon() - . = ..() - if(vore_active) - update_fullness() - if(!vore_fullness) - return 0 - else if((stat == CONSCIOUS) && (!icon_rest || !resting || !incapacitated(INCAPACITATION_DISABLED)) && (vore_icons & SA_ICON_LIVING)) - icon_state = "[icon_living]-[vore_fullness]" - else if(stat >= DEAD && (vore_icons & SA_ICON_DEAD)) - icon_state = "[icon_dead]-[vore_fullness]" - else if(((stat == UNCONSCIOUS) || resting || incapacitated(INCAPACITATION_DISABLED) ) && icon_rest && (vore_icons & SA_ICON_REST)) - icon_state = "[icon_rest]-[vore_fullness]" - -/mob/living/simple_mob/proc/will_eat(var/mob/living/M) - return FALSE // no more mobvore - -// Attempt to eat target -// TODO - Review this. Could be some issues here -/mob/living/simple_mob/proc/EatTarget(var/mob/living/M) - var/old_target = M - set_AI_busy(1) - . = animal_nom(M) - playsound(src, swallowsound, 50, 1) - update_icon() - - if(.) - // If we succesfully ate them, lose the target - set_AI_busy(0) - return old_target - else if(old_target == M) - // If we didn't but they are still our target, go back to attack. - // but don't run the handler immediately, wait until next tick - // Otherwise we'll be in a possibly infinate loop - set_AI_busy(0) - /mob/living/simple_mob/death() release_vore_contents() - . = ..() + return ..() // Make sure you don't call ..() on this one, otherwise you duplicate work. /mob/living/simple_mob/init_vore() - if(!vore_active || no_vore) - return + . = ..() if(!IsAdvancedToolUser()) add_verb(src, /mob/living/simple_mob/proc/animal_nom) @@ -116,63 +32,12 @@ if(LAZYLEN(vore_organs)) return - // Since they have bellies, add verbs to toggle settings on them. - add_verb(src, /mob/living/simple_mob/proc/toggle_digestion) - add_verb(src, /mob/living/simple_mob/proc/toggle_fancygurgle) - - //A much more detailed version of the default /living implementation - var/obj/belly/B = new /obj/belly(src) - vore_selected = B - B.immutable = 1 - B.name = vore_stomach_name ? vore_stomach_name : "stomach" - B.desc = vore_stomach_flavor ? vore_stomach_flavor : "Your surroundings are warm, soft, and slimy. Makes sense, considering you're inside \the [name]." - B.digest_mode = vore_default_mode - B.mode_flags = vore_default_flags - B.item_digest_mode = vore_default_item_mode - B.contaminates = vore_default_contaminates - B.contamination_flavor = vore_default_contamination_flavor - B.contamination_color = vore_default_contamination_color - B.escapable = vore_escape_chance > 0 - B.escapechance = vore_escape_chance - B.digestchance = vore_digest_chance - B.absorbchance = vore_absorb_chance - B.human_prey_swallow_time = swallowTime - B.nonhuman_prey_swallow_time = swallowTime - B.vore_verb = "swallow" - B.emote_lists[DM_HOLD] = list( // We need more that aren't repetitive. I suck at endo. -Ace - "The insides knead at you gently for a moment.", - "The guts glorp wetly around you as some air shifts.", - "The predator takes a deep breath and sighs, shifting you somewhat.", - "The stomach squeezes you tight for a moment, then relaxes harmlessly.", - "The predator's calm breathing and thumping heartbeat pulses around you.", - "The warm walls kneads harmlessly against you.", - "The liquids churn around you, though there doesn't seem to be much effect.", - "The sound of bodily movements drown out everything for a moment.", - "The predator's movements gently force you into a different position.") - B.emote_lists[DM_DIGEST] = list( - "The burning acids eat away at your form.", - "The muscular stomach flesh grinds harshly against you.", - "The caustic air stings your chest when you try to breathe.", - "The slimy guts squeeze inward to help the digestive juices soften you up.", - "The onslaught against your body doesn't seem to be letting up; you're food now.", - "The predator's body ripples and crushes against you as digestive enzymes pull you apart.", - "The juices pooling beneath you sizzle against your sore skin.", - "The churning walls slowly pulverize you into meaty nutrients.", - "The stomach glorps and gurgles as it tries to work you into slop.") - // Checks to see if mob doesn't like this kind of turf /mob/living/simple_mob/IMove(turf/newloc, safety = TRUE) if(istype(newloc,/turf/simulated/floor/sky)) return MOVEMENT_FAILED //Mobs aren't that stupid, probably return ..() // Procede as normal. -//Grab = Nomf -/mob/living/simple_mob/UnarmedAttack(var/atom/A, var/proximity) - . = ..() - - if(a_intent == INTENT_GRAB && isliving(A) && !has_hands) - animal_nom(A) - // todo: shitcode, rewrite on say rewrite /mob/living/simple_mob/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name) switch(message_mode) diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat_vr.dm index 1a3f1d0d3574..c3fbb8385e20 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat_vr.dm @@ -1,35 +1,3 @@ -/mob/living/simple_mob/animal/passive/cat/runtime/init_vore() - ..() - var/obj/belly/B = vore_selected - B.name = "Stomach" - B.desc = "The slimy wet insides of Runtime! Not quite as clean as the cat on the outside." - - B.emote_lists[DM_HOLD] = list( - "Runtime's stomach kneads gently on you and you're fairly sure you can hear her start purring.", - "Most of what you can hear are slick noises, Runtime breathing, and distant purring.", - "Runtime seems perfectly happy to have you in there. She lays down for a moment to groom and squishes you against the walls.", - "The CMO's pet seems to have found a patient of her own, and is treating them with warm, wet kneading walls.", - "Runtime mostly just lazes about, and you're left to simmer in the hot, slick guts unharmed.", - "Runtime's master might let you out of this fleshy prison, eventually. Maybe. Hopefully?") - - B.emote_lists[DM_DIGEST] = list( - "Runtime's stomach is treating you rather like a mouse, kneading acids into you with vigor.", - "A thick dollop of bellyslime drips from above while the CMO's pet's gut works on churning you up.", - "Runtime seems to have decided you're food, based on the acrid air in her guts and the pooling fluids.", - "Runtime's stomach tries to claim you, kneading and pressing inwards again and again against your form.", - "Runtime flops onto their side for a minute, spilling acids over your form as you remain trapped in them.", - "The CMO's pet doesn't seem to think you're any different from any other meal. At least, their stomach doesn't.") - - B.digest_messages_prey = list( - "Runtime's stomach slowly melts your body away. Her stomach refuses to give up it's onslaught, continuing until you're nothing more than nutrients for her body to absorb.", - "After an agonizing amount of time, Runtime's stomach finally manages to claim you, melting you down and adding you to her stomach.", - "Runtime's stomach continues to slowly work away at your body before tightly squeezing around you once more, causing the remainder of your body to lose form and melt away into the digesting slop around you.", - "Runtime's slimy gut continues to constantly squeeze and knead away at your body, the bulge you create inside of her stomach growing smaller as time progresses before soon dissapearing completely as you melt away.", - "Runtime's belly lets off a soft groan as your body finally gives out, the cat's eyes growing heavy as it settles down to enjoy it's good meal.", - "Runtime purrs happily as you slowly slip away inside of her gut, your body's nutrients are then used to put a layer of padding on the now pudgy cat.", - "The acids inside of Runtime's stomach, aided by the constant motions of the smooth walls surrounding you finally manage to melt you away into nothing more mush. She curls up on the floor, slowly kneading the air as her stomach moves its contents — including you — deeper into her digestive system.", - "Your form begins to slowly soften and break apart, rounding out Runtime's swollen belly. The carnivorous cat rumbles and purrs happily at the feeling of such a filling meal.") - // Ascian's Tactical Kitten /obj/item/holder/cat/fluff/tabiranth name = "Spirit" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/fox_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/fox_vr.dm index 8f48f46ea5c3..bd25ba380764 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/fox_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/fox_vr.dm @@ -62,28 +62,6 @@ wander = TRUE base_wander_delay = 4 -/mob/living/simple_mob/animal/passive/fox/init_vore() - ..() - var/obj/belly/B = vore_selected - B.name = "Stomach" - B.desc = "Slick foxguts. Cute on the outside, slimy on the inside!" - - B.emote_lists[DM_HOLD] = list( - "The foxguts knead and churn around you harmlessly.", - "With a loud glorp, some air shifts inside the belly.", - "A thick drop of warm bellyslime drips onto you from above.", - "The fox turns suddenly, causing you to shift a little.", - "During a moment of relative silence, you can hear the fox breathing.", - "The slimey stomach walls squeeze you lightly, then relax.") - - B.emote_lists[DM_DIGEST] = list( - "The guts knead at you, trying to work you into thick soup.", - "You're ground on by the slimey walls, treated like a mouse.", - "The acrid air is hard to breathe, and stings at your lungs.", - "You can feel the acids coating you, ground in by the slick walls.", - "The fox's stomach churns hungrily over your form, trying to take you.", - "With a loud glorp, the stomach spills more acids onto you.") - /mob/living/simple_mob/animal/passive/fox/apply_melee_effects(var/atom/A) if(ismouse(A)) var/mob/living/simple_mob/animal/passive/mouse/mouse = A @@ -144,58 +122,6 @@ var/datum/ai_holder/polaris/AI = ai_holder AI.set_follow(friend) -/* Old fox friend AI, I'm not sure how to add the fancy "friend is dead" stuff so I'm commenting it out for someone else to figure it out, this is just baseline stuff. -//Basic friend AI -/mob/living/simple_mob/animal/passive/fox/fluff - var/mob/living/carbon/human/friend - var/befriend_job = null - -/mob/living/simple_mob/animal/passive/fox/fluff/Life(seconds, times_fired) - . = ..() - if(!. || !friend) return - - var/friend_dist = get_dist(src,friend) - - if (friend_dist <= 4) - if(stance == STANCE_IDLE) - if(set_follow(friend)) - handle_stance(STANCE_FOLLOW) - - if (friend_dist <= 1) - if (friend.stat >= DEAD || friend.health <= config_legacy.health_threshold_softcrit) - if (prob((friend.stat < DEAD)? 50 : 15)) - var/verb = pick("yaps", "howls", "whines") - audible_emote(pick("[verb] in distress.", "[verb] anxiously.")) - else - if (prob(5)) - visible_emote(pick("nips [friend].", - "brushes against [friend].", - "tugs on [friend].", - "chrrrrs.")) - else if (friend.health <= 50) - if (prob(10)) - var/verb = pick("yaps", "howls", "whines") - audible_emote("[verb] anxiously.") - -/mob/living/simple_mob/animal/passive/fox/fluff/verb/friend() - set name = "Become Friends" - set category = VERB_CATEGORY_IC - set src in view(1) - - if(friend && usr == friend) - setDir(get_dir(src, friend)) - say("Yap!") - return - - if (!(ishuman(usr) && befriend_job && usr.job == befriend_job)) - to_chat(usr, "[src] ignores you.") - return - - friend = usr - - setDir(get_dir(src, friend)) - say("Yap!") -*/ /obj/item/reagent_containers/food/snacks/meat/fox name = "Fox meat" desc = "The fox doesn't say a goddamn thing, now." @@ -212,28 +138,6 @@ makes_dirt = FALSE // No more dirt randomized = FALSE -/mob/living/simple_mob/animal/passive/fox/renault/init_vore() - ..() - var/obj/belly/B = vore_selected - B.name = "Stomach" - B.desc = "Slick foxguts. They seem somehow more regal than perhaps other foxes!" - - B.emote_lists[DM_HOLD] = list( - "Renault's stomach walls squeeze around you more tightly for a moment, before relaxing, as if testing you a bit.", - "There's a sudden squeezing as Renault presses a forepaw against his gut over you, squeezing you against the slick walls.", - "The 'head fox' has a stomach that seems to think you belong to it. It might be hard to argue, as it kneads at your form.", - "If being in the captain's fox is a promotion, it might not feel like one. The belly just coats you with more thick foxslime.", - "It doesn't seem like Renault wants to let you out. The stomach and owner possessively squeeze around you.", - "Renault's stomach walls squeeze closer, as he belches quietly, before swallowing more air. Does he do that on purpose?") - - B.emote_lists[DM_DIGEST] = list( - "Renault's stomach walls grind hungrily inwards, kneading acids against your form, and treating you like any other food.", - "The captain's fox impatiently kneads and works acids against you, trying to claim your body for fuel.", - "The walls knead in firmly, squeezing and tossing you around briefly in disorienting aggression.", - "Renault belches, letting the remaining air grow more acrid. It burns your lungs with each breath.", - "A thick glob of acids drip down from above, adding to the pool of caustic fluids in Renault's belly.", - "There's a loud gurgle as the stomach declares the intent to make you a part of Renault.") - /mob/living/simple_mob/animal/passive/fox/syndicate name = "syndi-fox" desc = "It's a DASTARDLY fox! The horror! Call the shuttle!" diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/bee.dm b/code/modules/mob/living/simple_mob/subtypes/vore/bee.dm index e11e90aa7ec8..28d0fb60f015 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/bee.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/bee.dm @@ -55,8 +55,6 @@ // Activate Noms! /mob/living/simple_mob/vore/bee - vore_active = 1 - vore_icons = SA_ICON_LIVING /mob/living/simple_mob/vore/bee/apply_melee_effects(var/atom/A) if(isliving(A)) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/cookiegirl.dm b/code/modules/mob/living/simple_mob/subtypes/vore/cookiegirl.dm index 2317205d8d88..4011ebb24d1d 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/cookiegirl.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/cookiegirl.dm @@ -20,14 +20,6 @@ // Activate Noms! /mob/living/simple_mob/vore/cookiegirl - vore_active = 1 - vore_bump_chance = 2 - vore_pounce_chance = 25 - vore_standing_too = 1 - vore_ignores_undigestable = 0 // Do they look like they care? - vore_default_mode = DM_HOLD // They're cookiepeople, what do you expect? - vore_digest_chance = 10 // Gonna become as sweet as sugar, soon. - vore_icons = SA_ICON_LIVING | SA_ICON_REST /datum/ai_holder/polaris/simple_mob/passive/cookiegirl/on_hear_say(mob/living/speaker, message) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm b/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm index 49f0ff6b825d..e8bef7354c06 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm @@ -60,12 +60,6 @@ buckle_flags = BUCKLING_NO_USER_BUCKLE_OTHER_TO_SELF buckle_lying = FALSE - vore_active = TRUE - vore_capacity = 1 - vore_pounce_chance = 15 - vore_icons = SA_ICON_LIVING | SA_ICON_REST - vore_stomach_name = "fuel processor" - vore_stomach_flavor = "You have ended up in the fuel processor of this corrupted machine. This place was definitely not designed with safety and comfort in mind. The heated and cramped surroundings oozing potent fluids all over your form, eager to do nothing less than breaking you apart to fuel its rampage for the next few days... hours... minutes? Oh dear..." loot_list = list(/obj/item/borg/upgrade/syndicate = 6, /obj/item/borg/upgrade/vtec = 6, /obj/item/material/knife/ritual = 6, /obj/item/disk/nifsoft/compliance = 6) @@ -77,8 +71,6 @@ icon_dead = "prettyboi-dead" icon_rest = "prettyboi_rest" - vore_pounce_chance = 40 - attacktext = list("malsnuggled","scrunched","squeezed","assaulted","violated") say_list_type = /datum/say_list/corrupthound_prettyboi @@ -96,9 +88,6 @@ projectiletype = /obj/projectile/beam/sniper projectilesound = 'sound/weapons/gauss_shoot.ogg' - - vore_pounce_chance = 0 //It does ranged attacks anyway - ai_holder_type = /datum/ai_holder/polaris/simple_mob/ranged/sniper /mob/living/simple_mob/vore/aggressive/corrupthound/gunner @@ -117,13 +106,8 @@ projectiletype = /obj/projectile/bullet/rifle/a556 projectilesound = 'sound/weapons/Gunshot_light.ogg' - - vore_pounce_chance = 0 //It does ranged attacks anyway - ai_holder_type = /datum/ai_holder/polaris/simple_mob/ranged/kiting - - /mob/living/simple_mob/vore/aggressive/corrupthound/sword name = "fencer hound" desc = "Good boy machine broke. Who thought it was a good idea to install an energy sword in his tail?" @@ -139,8 +123,6 @@ attack_edge = 1 attacktext = list("slashed") - vore_pounce_chance = 0 //No... - /mob/living/simple_mob/vore/aggressive/corrupthound/sword/attackby(var/obj/item/O as obj, var/mob/user as mob) if(O.damage_force) if(prob(20)) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm b/code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm index b39bf0d73d02..60bc6d985d26 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm @@ -35,12 +35,6 @@ // Activate Noms! /mob/living/simple_mob/vore/aggressive/deathclaw - vore_active = 1 - vore_capacity = 2 - vore_max_size = RESIZE_HUGE - vore_min_size = RESIZE_SMALL - vore_pounce_chance = 0 // Beat them into crit before eating. - vore_icons = SA_ICON_LIVING /datum/ai_holder/polaris/simple_mob/melee/deathclaw can_breakthrough = TRUE diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon.dm b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon.dm index e4ce752d3828..ff6d8c6ae4b1 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon.dm @@ -37,20 +37,12 @@ legacy_melee_damage_upper = 1 attacktext = list("clawed") - vore_active = TRUE - vore_icons = SA_ICON_LIVING var/shifted_out = FALSE var/shift_state = AB_SHIFT_NONE var/last_shift = 0 var/is_shifting = FALSE -/mob/living/simple_mob/vore/demon/init_vore() - ..() - var/obj/belly/B = vore_selected - B.name = "Stomach" - B.desc = "You slide down the slick, slippery gullet of the creature. It's warm, and the air is thick. You can feel the doughy walls of the creatures gut push and knead into your form! Slimy juices coat your form stinging against your flesh as they waste no time to start digesting you. The creature's heartbeat and the gurgling of their stomach are all you can hear as your jostled about, treated like nothing but food." - /mob/living/simple_mob/vore/demon/UnarmedAttack() if(shifted_out) return FALSE diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_subtypes.dm b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_subtypes.dm index 4cd39bf1719e..438035b6f823 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_subtypes.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_subtypes.dm @@ -6,7 +6,6 @@ icon_dead = "engorge_dead" icon_rest = "engorge_rest" - vore_icons = null /mob/living/simple_mob/vore/demon/zellic name = "Zellic" @@ -16,4 +15,3 @@ icon_dead = "zellic_dead" icon_rest = null - vore_icons = null diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/dino.dm b/code/modules/mob/living/simple_mob/subtypes/vore/dino.dm index 11afced1cf8e..3db981881bd6 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/dino.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/dino.dm @@ -42,9 +42,7 @@ // Activate Noms! /mob/living/simple_mob/vore/aggressive/dino - vore_active = 1 swallowTime = 1 SECOND // Hungry little bastards. - vore_icons = SA_ICON_LIVING /mob/living/simple_mob/vore/aggressive/dino/virgo3b faction = "virgo3b" diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/dragon.dm b/code/modules/mob/living/simple_mob/subtypes/vore/dragon.dm index cd92391c01c9..8581053f9ef5 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/dragon.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/dragon.dm @@ -38,16 +38,11 @@ // Activate Noms! /mob/living/simple_mob/vore/aggressive/dragon - vore_active = 1 - vore_capacity = 2 - vore_pounce_chance = 0 // Beat them into crit before eating. - vore_icons = SA_ICON_LIVING /mob/living/simple_mob/vore/aggressive/dragon/virgo3b maxHealth = 200 health = 200 faction = "virgo3b" - /datum/say_list/dragonboss say_got_target = list("roars and snaps it jaws!") diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/fennec.dm b/code/modules/mob/living/simple_mob/subtypes/vore/fennec.dm index cd7ccba29cfe..e4fdab4758be 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/fennec.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/fennec.dm @@ -36,12 +36,6 @@ // Activate Noms! /mob/living/simple_mob/vore/fennec - vore_active = 1 - vore_bump_chance = 10 - vore_bump_emote = "playfully lunges at" - vore_pounce_chance = 40 - vore_default_mode = DM_HOLD - vore_icons = SA_ICON_LIVING /datum/say_list/fennec speak = list("SKREEEE!","Chrp?","Ararrrararr.") diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/frog.dm b/code/modules/mob/living/simple_mob/subtypes/vore/frog.dm index 81eb61dbe020..89c8a3357801 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/frog.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/frog.dm @@ -38,9 +38,6 @@ // Activate Noms! /mob/living/simple_mob/vore/aggressive/frog - vore_active = 1 - vore_pounce_chance = 50 - vore_icons = SA_ICON_LIVING /mob/living/simple_mob/vore/aggressive/frog/space name = "space frog" diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/hippo.dm b/code/modules/mob/living/simple_mob/subtypes/vore/hippo.dm index e86b3a697e35..dce5180b32e0 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/hippo.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/hippo.dm @@ -55,19 +55,6 @@ // Activate Noms! /mob/living/simple_mob/vore/hippo //I don't know why it's in a seperate line but everyone does it so i do it - vore_active = 1 - vore_capacity = 1 - vore_bump_chance = 15 - vore_bump_emote = "lazily wraps its tentacles around" - vore_standing_too = 1 - vore_ignores_undigestable = 0 - vore_default_mode = DM_HOLD - vore_digest_chance = 10 - vore_escape_chance = 20 - vore_pounce_chance = 35 //it's hippo sized it doesn't care it just eats you - vore_stomach_name = "rumen" //First stomach of a ruminant. It's where the pre digestion bacteria stuff happens. Very warm. - vore_stomach_flavor = "You are squeezed into the sweltering insides of the herbivore rumen." - vore_icons = SA_ICON_LIVING /mob/living/simple_mob/vore/hippo/MouseDroppedOnLegacy(mob/living/M, mob/living/user) return diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/jelly.dm b/code/modules/mob/living/simple_mob/subtypes/vore/jelly.dm index eae1a874b442..5f454b39b521 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/jelly.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/jelly.dm @@ -33,9 +33,6 @@ // Activate Noms! /mob/living/simple_mob/animal/space/jelly - vore_active = 1 - vore_pounce_chance = 0 - vore_icons = SA_ICON_LIVING swallowTime = 2 SECONDS // Hungry little bastards. /datum/say_list/jelly diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/mimic.dm b/code/modules/mob/living/simple_mob/subtypes/vore/mimic.dm index c35c59a9603b..d605984192c9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/mimic.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/mimic.dm @@ -97,11 +97,7 @@ showvoreprefs = 0 //Hides mechanical vore prefs for mimics. You can't see their gaping maws when they're just sitting idle. /mob/living/simple_mob/vore/aggressive/mimic - vore_active = 1 - vore_pounce_chance = 10 swallowTime = 3 SECONDS - vore_capacity = 1 - vore_default_mode = DM_DIGEST /datum/ai_holder/polaris/mimic wander = FALSE diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm b/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm index c8623e75ce07..1e3d344a322f 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm @@ -40,7 +40,6 @@ meat_amount = 2 showvoreprefs = 0 - vore_active = 1 var/morphed = FALSE var/tooltip = TRUE diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/otie.dm b/code/modules/mob/living/simple_mob/subtypes/vore/otie.dm index f8baf102e1f1..7d28db4ad0e5 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/otie.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/otie.dm @@ -44,10 +44,6 @@ // Activate Noms! /mob/living/simple_mob/otie - vore_active = 1 - vore_capacity = 1 - vore_pounce_chance = 20 - vore_icons = SA_ICON_LIVING | SA_ICON_REST /mob/living/simple_mob/otie/feral //gets the pet2tame feature. starts out hostile tho so get gamblin' name = "mutated feral otie" @@ -144,10 +140,6 @@ tamed = 1 has_eye_glow = TRUE loot_list = list(/obj/item/clothing/glasses/sunglasses/sechud,/obj/item/clothing/suit/armor/vest/alt) - vore_pounce_chance = 60 // Good boys don't do too much police brutality. - - var/check_records = 0 // If true, arrests people without a record. - var/check_arrest = 1 // If true, arrests people who are set to arrest. /mob/living/simple_mob/otie/security/phoron name = "mutated guard otie" @@ -182,41 +174,6 @@ mod_min = 150 mod_max = 150 -/mob/living/simple_mob/otie/attackby(var/obj/item/O, var/mob/user) // Trade donuts for bellybrig victims. - if(istype(O, /obj/item/reagent_containers/food)) - qdel(O) - playsound(src.loc,'sound/items/eatfood.ogg', rand(10,50), 1) - if(!has_polaris_AI())//No autobarf on player control. - return - if(istype(O, /obj/item/reagent_containers/food/snacks/donut) && istype(src, /mob/living/simple_mob/otie/security)) - to_chat(user,"The guard pup accepts your offer for their catch.") - release_vore_contents() - else if(prob(2)) //Small chance to get prey out from non-sec oties. - to_chat(user,"The pup accepts your offer for their catch.") - release_vore_contents() - return - . = ..() - -/mob/living/simple_mob/otie/security/feed_grabbed_to_self(var/mob/living/user, var/mob/living/prey) // Make the gut start out safe for bellybrigging. - if(ishuman(prey)) - vore_selected.digest_mode = DM_HOLD - if(check_threat(prey) >= 4) - GLOB.global_announcer.autosay("[src] has detained suspect [target_name(prey)] in [get_area(src)].", "SmartCollar oversight", "Security") - if(istype(prey,/mob/living/simple_mob/animal/passive/mouse)) - vore_selected.digest_mode = DM_DIGEST - . = ..() - -/mob/living/simple_mob/otie/security/proc/check_threat(var/mob/living/M) - if(!M || !ishuman(M) || M.stat == DEAD || src == M) - return 0 - return M.assess_perp(0, 0, 0, check_records, check_arrest) - -/mob/living/simple_mob/otie/security/proc/target_name(mob/living/T) - if(ishuman(T)) - var/mob/living/carbon/human/H = T - return H.get_id_name("unidentified person") - return "unidentified lifeform" - //Pet 4 friendly /mob/living/simple_mob/otie/attack_hand(mob/user, list/params) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/panther.dm b/code/modules/mob/living/simple_mob/subtypes/vore/panther.dm index 615dbd13f257..196981a2436d 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/panther.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/panther.dm @@ -36,10 +36,6 @@ // Activate Noms! /mob/living/simple_mob/vore/aggressive/panther - vore_active = 1 - vore_capacity = 2 - vore_pounce_chance = 10 - vore_icons = SA_ICON_LIVING | SA_ICON_REST /datum/say_list/panther speak = list("RAWR!","Rawr!","GRR!","Growl!") diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/rat.dm b/code/modules/mob/living/simple_mob/subtypes/vore/rat.dm index 5706bd267e6b..2101732ed4d8 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/rat.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/rat.dm @@ -36,10 +36,6 @@ icon_x_dimension = 64 icon_y_dimension = 32 - vore_active = TRUE - vore_capacity = 1 - vore_pounce_chance = 45 - vore_icons = SA_ICON_LIVING | SA_ICON_REST var/life_since_foodscan = 0 diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/redpanda.dm b/code/modules/mob/living/simple_mob/subtypes/vore/redpanda.dm index 5bb907bab6eb..96d20383ec1a 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/redpanda.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/redpanda.dm @@ -35,15 +35,6 @@ say_list_type = /datum/say_list/redpanda ai_holder_type = /datum/ai_holder/polaris/simple_mob/passive -// Activate Noms! -/mob/living/simple_mob/vore/redpanda - vore_active = 1 - vore_bump_chance = 10 - vore_bump_emote = "playfully lunges at" - vore_pounce_chance = 40 - vore_default_mode = DM_HOLD // above will only matter if someone toggles it anyway - vore_icons = SA_ICON_LIVING - /mob/living/simple_mob/vore/redpanda/fae name = "dark wah" desc = "Ominous, but still cute!" @@ -54,10 +45,6 @@ icon_dead = "wah_fae_dead" icon_rest = "wah_fae_rest" - vore_ignores_undigestable = 0 // wah don't care you're edible or not, you still go in - vore_digest_chance = 0 // instead of digesting if you struggle... - vore_absorb_chance = 20 // you get to become adorable purple wahpudge. - vore_bump_chance = 75 maxHealth = 100 health = 100 legacy_melee_damage_lower = 10 diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/snake.dm b/code/modules/mob/living/simple_mob/subtypes/vore/snake.dm index 0509c018843b..1c08dc55d987 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/snake.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/snake.dm @@ -34,7 +34,4 @@ // Activate Noms! /mob/living/simple_mob/vore/aggressive/giant_snake - vore_active = 1 - vore_pounce_chance = 25 - vore_icons = SA_ICON_LIVING swallowTime = 2 SECONDS // Hungry little bastards. diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/solargrub.dm b/code/modules/mob/living/simple_mob/subtypes/vore/solargrub.dm index c2541a3f631f..4ec007004a1f 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/solargrub.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/solargrub.dm @@ -120,8 +120,6 @@ GLOBAL_LIST_EMPTY(solargrubs) if(prob(1) && charge >= 32000 && can_evolve == 1) // CitRP: We can quote this out and see what happens; && moth_amount <= 1) //it's reading from the moth_amount global list to determine if it can evolve. There should only ever be a maxcap of 1 existing solar moth alive at any time. TODO: make the code decrease the list after 1 has spawned this shift. anchored = 0 PN = attached.powernet - release_vore_contents() - prey_excludes.Cut() GLOB.moth_amount += 1 //CitRP: There was some magic going on around this here part, it might actualy be working. death_star() @@ -131,14 +129,6 @@ GLOBAL_LIST_EMPTY(solargrubs) new chosen_form(get_turf(src)) qdel(src) -/mob/living/simple_mob/vore/solargrub //active noms - vore_bump_chance = 50 - vore_bump_emote = "applies minimal effort to try and slurp up" - vore_active = 1 - vore_capacity = 1 - vore_pounce_chance = 0 //grubs only eat incapacitated targets - vore_default_mode = DM_DIGEST - /mob/living/simple_mob/vore/solargrub/apply_melee_effects(var/atom/A) if(isliving(A)) var/mob/living/L = A diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/solarmoth_ch.dm b/code/modules/mob/living/simple_mob/subtypes/vore/solarmoth_ch.dm index ca127910d87a..06cd05802403 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/solarmoth_ch.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/solarmoth_ch.dm @@ -153,12 +153,6 @@ /mob/living/simple_mob/vore/solarmoth //active noms - vore_bump_chance = 50 - vore_bump_emote = "applies minimal effort to try and slurp up" - vore_active = 1 - vore_capacity = 1 - vore_pounce_chance = 0 //moths only eat incapacitated targets. It's too lazy burning you to a crisp to try to pounce you - vore_default_mode = DM_DIGEST /mob/living/simple_mob/vore/solarmoth/lunarmoth name = "Lunarmoth" diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/vore.dm b/code/modules/mob/living/simple_mob/subtypes/vore/vore.dm index 796d736e8448..2aefb32844ac 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/vore.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/vore.dm @@ -1,6 +1,6 @@ +// todo: get rid of /vore and /vore/aggressive /mob/living/simple_mob/vore mob_class = MOB_CLASS_ANIMAL - mob_bump_flag = 0 meat_type = /obj/item/reagent_containers/food/snacks/meat bone_type = /obj/item/stack/material/bone hide_type = /obj/item/stack/animalhide diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/wolf.dm b/code/modules/mob/living/simple_mob/subtypes/vore/wolf.dm index ed724697a757..f87e3aac491e 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/wolf.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/wolf.dm @@ -30,8 +30,6 @@ // Activate Noms! /mob/living/simple_mob/animal/wolf - vore_active = 1 - vore_icons = SA_ICON_LIVING // Adds Phoron Wolf diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/wolfgirl.dm b/code/modules/mob/living/simple_mob/subtypes/vore/wolfgirl.dm index 2150e4c39dac..fc2c1605e284 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/wolfgirl.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/wolfgirl.dm @@ -35,9 +35,6 @@ */ // Activate Noms! /mob/living/simple_mob/vore/wolfgirl - vore_active = 1 - vore_pounce_chance = 40 - vore_icons = SA_ICON_LIVING /datum/ai_holder/polaris/simple_mob/retaliate/cooperative/wolfgirl/on_hear_say(mob/living/speaker, message) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/zz_vore_overrides.dm b/code/modules/mob/living/simple_mob/subtypes/vore/zz_vore_overrides.dm index ee00e8ce8330..9e74a4a7a9c7 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/zz_vore_overrides.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/zz_vore_overrides.dm @@ -1,115 +1,29 @@ -// -// This file overrides settings on upstream simple animals to turn on vore behavior -// - -/* -## For anything that previously inhertited from: /mob/living/simple_mob/hostile/vore ## - - vore_active = 1 - icon = 'icons/mob/vore.dmi' - -## For anything that previously inhertied from: /mob/living/simple_mob/hostile/vore/large ## - - vore_active = 1 - icon = 'icons/mob/vore64x64.dmi' - old_x = -16 - old_y = -16 - pixel_x = -16 - pixel_y = -16 - vore_pounce_chance = 50 -*/ - -// -// Okay! Here we go! -// - /mob/living/simple_mob/animal/space/bear - vore_active = 1 icon = 'icons/mob/vore.dmi' icon_state = "spacebear" icon_living = "spacebear" icon_dead = "spacebear-dead" icon_gib = "bear-gib" - vore_icons = SA_ICON_LIVING /mob/living/simple_mob/animal/space/bear/hudson name = "Hudson" /mob/living/simple_mob/animal/space/bear/brown - vore_active = 1 icon = 'icons/mob/vore.dmi' name = "brown bear" icon_state = "brownbear" icon_living = "brownbear" icon_dead = "brownbear-dead" icon_gib = "bear-gib" - vore_icons = SA_ICON_LIVING /mob/living/simple_mob/animal/space/carp icon = 'icons/mob/vore.dmi' - vore_active = 1 - vore_icons = SA_ICON_LIVING /mob/living/simple_mob/animal/space/carp/large - vore_icons = 0 /mob/living/simple_mob/animal/space/carp/large/huge - vore_icons = 0 /mob/living/simple_mob/animal/space/carp/holographic - vore_icons = 0 - -/mob/living/simple_mob/animal/passive/cat - vore_active = 1 - //specific_targets = 0 // Targeting UNLOCKED - vore_max_size = RESIZE_TINY - -/mob/living/simple_mob/animal/passive/cat/fluff - vore_ignores_undigestable = 0 - vore_pounce_chance = 100 - vore_digest_chance = 0 // just use the toggle - vore_default_mode = DM_HOLD //can use the toggle if you wanna be catfood - vore_standing_too = TRUE //gonna get pounced - -/mob/living/simple_mob/animal/passive/fox - vore_active = 1 - vore_max_size = RESIZE_TINY - -/mob/living/simple_mob/fox/fluff - vore_ignores_undigestable = 0 - vore_pounce_chance = 100 - vore_digest_chance = 0 // just use the toggle - vore_default_mode = DM_HOLD //can use the toggle if you wanna be foxfood - vore_standing_too = TRUE // gonna get pounced - -/mob/living/simple_mob/animal/space/goose - vore_active = 1 - vore_max_size = RESIZE_SMALL - -/mob/living/simple_mob/animal/passive/penguin - vore_active = 1 - vore_max_size = RESIZE_SMALL - - -/mob/living/simple_mob/hostile/carp/pike - vore_active = 1 /mob/living/simple_mob/animal/space/carp/holographic - vore_icons = 0 - vore_digest_chance = 0 - vore_absorb_chance = 0 - -// Override stuff for holodeck carp to make them not digest when set to safe! -/mob/living/simple_mob/animal/space/carp/holographic/init_vore() - . = ..() - var/safe = (faction == "neutral") - for(var/belly in vore_organs) - var/obj/belly/B = belly - B.digest_mode = safe ? DM_HOLD : vore_default_mode - -/mob/living/simple_mob/animal/space/carp/holographic/set_safety(var/safe) - . = ..() - for(var/belly in vore_organs) - var/obj/belly/B = belly - B.digest_mode = safe ? DM_HOLD : vore_default_mode /mob/living/simple_mob/animal/passive/mouse faction = "mouse" //Giving mice a faction so certain mobs can get along with them. diff --git a/code/modules/vore/eating/living_vr.dm b/code/modules/vore/eating/living_vr.dm index 76b11ed93417..09f70bcacd6d 100644 --- a/code/modules/vore/eating/living_vr.dm +++ b/code/modules/vore/eating/living_vr.dm @@ -390,15 +390,10 @@ //You're in a belly! if(isbelly(loc)) var/obj/belly/B = loc - var/confirm = alert(src, "You're in a mob. Don't use this as a trick to get out of hostile animals. This is for escaping from preference-breaking and if you're otherwise unable to escape from endo (pred AFK for a long time).", "Confirmation", "Okay", "Cancel") - if(!confirm == "Okay" || loc != B) - return //Actual escaping absorbed = 0 //Make sure we're not absorbed muffled = 0 //Removes Muffling forceMove(get_turf(src)) //Just move me up to the turf, let's not cascade through bellies, there's been a problem, let's just leave. - for(var/mob/living/simple_mob/SA in range(10)) - SA.prey_excludes[src] = world.time log_and_message_admins("[key_name(src)] used the OOC escape button to get out of [key_name(B.owner)] ([B.owner ? "JMP" : "null"])") if(!ishuman(B.owner)) diff --git a/code/modules/vore/eating/simple_animal_vr.dm b/code/modules/vore/eating/simple_animal_vr.dm index 19726b5b3e4d..a4dfd5683531 100644 --- a/code/modules/vore/eating/simple_animal_vr.dm +++ b/code/modules/vore/eating/simple_animal_vr.dm @@ -1,7 +1,6 @@ ///////////////////// Simple Animal ///////////////////// /mob/living/simple_mob var/swallowTime = (3 SECONDS) //How long it takes to eat its prey in 1/10 of a second. The default is 3 seconds. - var/list/prey_excludes = list() //For excluding people from being eaten. // // Simple nom proc for if you get ckey'd into a simple_mob mob! Avoids grabs. @@ -23,81 +22,3 @@ return feed_grabbed_to_self(src,T) update_icon() - return - -/* - * Simple proc for animals to have their digestion toggled on/off externally - * Added as a verb in /mob/living/simple_mob/init_vore() if vore is enabled for this mob. - */ -/mob/living/simple_mob/proc/toggle_digestion() - set name = "Toggle Animal's Digestion" - set desc = "Enables digestion on this mob for 20 minutes." - set category = VERB_CATEGORY_OOC - set src in oview(1) - - var/mob/living/carbon/human/user = usr - if(!istype(user) || user.stat) return - - if(!istype(src.ai_holder, /datum/ai_holder/polaris)) - return - var/datum/ai_holder/polaris/ai_holder = src.ai_holder - - if(!vore_selected) - to_chat(user, "[src] isn't planning on eating anything much less digesting it.") - return - if(ai_holder.retaliate || (ai_holder.hostile && faction != user.faction)) - to_chat(user, "This predator isn't friendly, and doesn't give a shit about your opinions of it digesting you.") - return - if(vore_selected.digest_mode == DM_HOLD) - var/confirm = tgui_alert(user, "Enabling digestion on [name] will cause it to digest all stomach contents. Using this to break OOC prefs is against the rules. Digestion will reset after 20 minutes.", "Enabling [name]'s Digestion", list("Enable", "Cancel")) - if(confirm == "Enable") - vore_selected.digest_mode = DM_DIGEST - addtimer(VARSET_CALLBACK(vore_selected, digest_mode, vore_default_mode), 20 MINUTES) - else - var/confirm = tgui_alert(user, "This mob is currently set to process all stomach contents. Do you want to disable this?", "Disabling [name]'s Digestion", list("Disable", "Cancel")) - if(confirm == "Disable") - vore_selected.digest_mode = DM_HOLD - -// Added as a verb in /mob/living/simple_mob/init_vore() if vore is enabled for this mob. -/mob/living/simple_mob/proc/toggle_fancygurgle() - set name = "Toggle Animal's Gurgle sounds" - set desc = "Switches between Fancy and Classic sounds on this mob." - set category = VERB_CATEGORY_OOC - set src in oview(1) - - var/mob/living/user = usr //I mean, At least ghosts won't use it. - if(!istype(user) || user.stat) - return - if(!vore_selected) - to_chat(user, SPAN_WARNING("[src] isn't vore capable.")) - return - - vore_selected.fancy_vore = !vore_selected.fancy_vore - to_chat(user, "[src] is now using [vore_selected.fancy_vore ? "Fancy" : "Classic"] vore sounds.") - -/mob/living/simple_mob/attackby(var/obj/item/O, var/mob/user) - if(!istype(src.ai_holder, /datum/ai_holder/polaris)) - return ..() - var/datum/ai_holder/polaris/ai_holder = src.ai_holder - if (istype(O, /obj/item/newspaper) && !(ckey || (ai_holder.hostile && faction != user.faction)) && isturf(user.loc)) - if (ai_holder.retaliate && prob(vore_pounce_chance/2)) // This is a gamble! - user.afflict_paralyze(20 * 5) //They get tackled anyway whether they're edible or not. - user.visible_message("\the [user] swats \the [src] with \the [O] and promptly gets tackled!!") - if (will_eat(user)) - set_AI_busy(TRUE) - animal_nom(user) - update_icon() - set_AI_busy(FALSE) - else if (!ai_holder.target) // no using this to clear a retaliate mob's target - ai_holder.target = user //just because you're not tasty doesn't mean you get off the hook. A swat for a swat. - else - user.visible_message("\the [user] swats \the [src] with \the [O]!!") - release_vore_contents() - for(var/mob/living/L in living_mobs(0)) //add everyone on the tile to the do-not-eat list for a while - if(!(L in prey_excludes)) // Unless they're already on it, just to avoid fuckery. - prey_excludes += L - spawn(5 MINUTES) - if(src && L) - prey_excludes -= L - else - ..() diff --git a/maps/away_missions/140x140/zoo.dmm b/maps/away_missions/140x140/zoo.dmm index e4b346826209..84e18f701d06 100644 --- a/maps/away_missions/140x140/zoo.dmm +++ b/maps/away_missions/140x140/zoo.dmm @@ -7328,7 +7328,7 @@ /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) "ua" = ( -/mob/living/simple_mob/fox{ +/mob/living/simple_mob/animal/passive/fox{ faction = "zoo" }, /turf/simulated/floor/holofloor/snow, From 2533caefd93e41e9c30297b62bf8828c9854400f Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Mon, 2 Sep 2024 14:34:27 -0400 Subject: [PATCH 17/35] combat code overhaul + projectiles v8: impact handling (#6579) --- .github/workflows/size_labeling.yml | 2 +- citadel.dme | 133 +- code/__DEFINES/_core.dm | 3 + code/__DEFINES/_flags/atom_flags.dm | 8 +- code/__DEFINES/_planes+layers.dm | 1 + code/__DEFINES/callbacks.dm | 6 +- code/__DEFINES/combat/armor.dm | 64 +- code/__DEFINES/combat/attack_types.dm | 7 + code/__DEFINES/combat/explosions.dm | 4 +- code/__DEFINES/combat/shieldcall.dm | 206 ++- code/__DEFINES/datums/event_args.dm | 5 + code/__DEFINES/dcs/flags.dm | 6 +- ...m_buckling.dm => signals_atom-buckling.dm} | 36 +- ...stem.dm => signals_atom-context_system.dm} | 2 +- .../signals_atom/signals_atom-defense.dm | 37 + ...m_throwing.dm => signals_atom-throwing.dm} | 11 +- ..._system.dm => signals_atom-tool_system.dm} | 2 +- .../signals_atom/signals_atom_defense.dm | 5 - .../signals_item/signals_item_inventory.dm | 8 +- ..._inventory.dm => signals_mob-inventory.dm} | 3 + .../signals_mob/signals_mob-perspective.dm | 7 + .../signals/signals_mob/signals_mob_legacy.dm | 13 + .../signals_mob/signals_mob_perspectiive.dm | 4 - code/__DEFINES/event_args.dm | 7 - code/__DEFINES/math.dm | 40 +- code/__DEFINES/misc.dm | 10 - code/__DEFINES/procs/clickcode.dm | 15 +- code/__DEFINES/projectiles/projectile.dm | 119 ++ code/__HELPERS/game/combat/arc.dm | 58 + code/__HELPERS/lists/string.dm | 4 +- code/__HELPERS/mobs.dm | 2 +- code/__HELPERS/type2type/type2type.dm | 10 +- code/__HELPERS/unsorted.dm | 2 +- code/__global_init.dm | 12 + code/controllers/subsystem/ai_movement.dm | 15 +- .../subsystem/processing/fastprocess.dm | 6 - .../subsystem/processing/process_20fps.dm | 7 + .../subsystem/processing/process_5fps.dm | 7 + code/controllers/subsystem/throwing.dm | 3 + .../datastructs => datums/armor}/armor.dm | 10 + code/datums/callback.dm | 16 + code/datums/components/atoms/fishing_spot.dm | 4 +- code/datums/components/gps_signal.dm | 2 +- code/datums/components/horror_aura.dm | 2 +- code/datums/components/items/active_parry.dm | 10 + code/datums/components/items/passive_parry.dm | 348 +++++ code/datums/components/items/shield_block.dm | 11 + code/datums/components/items/wielding.dm | 10 +- code/datums/components/mobs/block_frame.dm | 75 + code/datums/components/mobs/parry_frame.dm | 561 +++++++ .../datums/components/movable/spatial_grid.dm | 2 +- .../datums/components/riding/riding_filter.dm | 8 +- .../components/riding/riding_handler.dm | 4 +- code/datums/datumvars.dm | 7 +- code/datums/elements/conflict_checking.dm | 2 + code/datums/event_args/clickchain.dm | 11 + code/datums/outfits/ghostrole.dm | 6 +- code/datums/outfits/horror_killers.dm | 2 +- code/datums/outfits/outfit.dm | 8 +- code/datums/outfits/pirates.dm | 4 +- code/datums/position_point_vector.dm | 3 + code/datums/shieldcall.dm | 153 +- code/datums/soundbytes/effects/combat.dm | 47 + code/datums/status_effects/status_effect.dm | 7 +- code/datums/unarmed_attack.dm | 3 + code/datums/uplink/visible_weapons.dm | 12 +- code/game/antagonist/outsider/deathsquad.dm | 2 +- code/game/atoms/atom-construction.dm | 60 + code/game/atoms/atom-damage.dm | 333 +++++ code/game/atoms/atom-defense.dm | 532 +++++++ code/game/atoms/atom.dm | 1 + code/game/atoms/buckling.dm | 38 +- code/game/atoms/defense.dm | 547 ------- code/game/atoms/defense_old.dm | 4 - .../movable/{throwing.dm => movable-throw.dm} | 2 +- code/game/atoms/movable/movement.dm | 16 +- code/game/atoms/vv.dm | 2 +- code/game/click/context.dm | 6 +- code/game/click/items.dm | 18 +- code/game/click/mobs.dm | 5 +- .../nanotrasen-supply/contraband.dm | 2 +- .../nanotrasen/nanotrasen-supply/munitions.dm | 2 +- .../nanotrasen/nanotrasen-supply/security.dm | 2 +- .../gamemodes/changeling/powers/armblade.dm | 69 +- code/game/gamemodes/cult/cult_structures.dm | 7 +- code/game/gamemodes/events/black_hole.dm | 2 +- code/game/gamemodes/meteor/meteors.dm | 2 +- code/game/gamemodes/sandbox/h_sandbox.dm | 2 +- .../technomancer/devices/shield_armor.dm | 23 +- .../technomancer/devices/tesla_armor.dm | 24 +- .../technomancer/spells/energy_siphon.dm | 26 +- .../spells/projectile/chain_lightning.dm | 16 +- .../spells/projectile/lightning.dm | 8 +- .../gamemodes/technomancer/spells/reflect.dm | 109 +- .../gamemodes/technomancer/spells/shield.dm | 33 +- code/game/machinery/_machinery.dm | 22 +- code/game/machinery/doors/airlock/airlock.dm | 2 +- code/game/machinery/doors/defense.dm | 2 +- code/game/machinery/doors/unpowered.dm | 2 +- code/game/machinery/doors/windowdoor.dm | 2 +- code/game/machinery/fire_alarm.dm | 5 +- code/game/machinery/iv_drip.dm | 2 +- code/game/machinery/turnstile.dm | 5 - code/game/machinery/turrets/subtypes/misc.dm | 2 +- .../machinery/turrets/turret-ai_holder.dm | 3 +- code/game/machinery/turrets/turret.dm | 12 +- code/game/objects/defense.dm | 82 - code/game/objects/effects/_effect.dm | 1 + code/game/objects/effects/effect_system.dm | 4 +- .../game/objects/effects/item_pickup_ghost.dm | 10 +- code/game/objects/effects/mines.dm | 9 +- code/game/objects/effects/misc.dm | 20 - code/game/objects/effects/spiders.dm | 8 +- code/game/objects/effects/traps.dm | 4 +- code/game/objects/items-carry_weight.dm | 74 + code/game/objects/items-defense.dm | 12 + code/game/objects/items-interaction.dm | 159 ++ code/game/objects/items.dm | 300 +--- code/game/objects/items/contraband.dm | 10 +- .../objects/items/devices/chameleonproj.dm | 4 +- code/game/objects/items/devices/gps.dm | 4 +- code/game/objects/items/devices/spy_bug.dm | 7 +- .../objects/items/id_cards/station_ids.dm | 1 - code/game/objects/items/latexballoon.dm | 12 +- code/game/objects/items/melee/melee.dm | 22 + .../{weapons/melee => melee/types}/misc.dm | 22 +- .../items/melee/types/ninja_energy_blade.dm | 66 + .../objects/items/melee/types/transforming.dm | 177 +++ .../items/melee/types/transforming/energy.dm | 176 +++ .../melee/types/transforming/energy/axe.dm | 49 + .../types/transforming/energy/ionic_rapier.dm | 56 + .../melee/types/transforming/energy/saber.dm | 141 ++ .../melee/types/transforming/hfmachete.dm | 50 + code/game/objects/items/shield/shield.dm | 17 + .../items/shield/types/shields_legacy.dm | 204 +++ .../items/shield/types/shields_legacy_vr.dm | 14 + .../items/shield/types/transforming.dm | 110 ++ .../items/shield/types/transforming/energy.dm | 111 ++ .../shield/types/transforming/telescopic.dm | 37 + .../items/shield_projector/shield_matrix.dm | 79 + .../shield_projector/shield_projector.dm} | 78 - code/game/objects/items/shooting_range.dm | 23 +- code/game/objects/items/storage/belt.dm | 4 +- code/game/objects/items/storage/lockbox.dm | 2 +- code/game/objects/items/storage/secure.dm | 2 +- .../game/objects/items/storage/uplink_kits.dm | 2 +- code/game/objects/items/tools/switchtool.dm | 11 +- .../objects/items/weapons/cigs_lighters.dm | 6 +- .../items/weapons/grenades/concussion.dm | 2 +- .../items/weapons/grenades/explosive.dm | 30 +- .../items/weapons/grenades/flashbang.dm | 2 +- .../items/weapons/grenades/projectile.dm | 33 +- .../objects/items/weapons/material/knives.dm | 10 +- .../items/weapons/material/material.dm | 1 + .../objects/items/weapons/material/swords.dm | 42 +- .../items/weapons/material/twohanded.dm | 12 +- .../objects/items/weapons/melee/deflect.dm | 31 - .../objects/items/weapons/melee/energy.dm | 729 --------- .../game/objects/items/weapons/melee/melee.dm | 7 - code/game/objects/items/weapons/shields.dm | 494 ------ .../objects/items/weapons/swords_axes_etc.dm | 9 +- code/game/objects/items/weapons/tanks/tank.dm | 30 +- code/game/objects/obj-construction.dm | 9 + code/game/objects/obj-defense.dm | 118 ++ code/game/objects/{objs.dm => obj.dm} | 0 code/game/objects/random/mapping.dm | 10 +- code/game/objects/random/misc.dm | 4 +- code/game/objects/structures/catwalk.dm | 2 +- code/game/objects/structures/cliff.dm | 7 +- .../structures/crates_lockers/__closet.dm | 2 +- .../crates_lockers/closets/coffin.dm | 4 +- .../crates_lockers/closets/gimmick.dm | 4 +- .../crates_lockers/closets/secure/personal.dm | 2 +- .../crates_lockers/closets/secure/security.dm | 6 +- .../crates_lockers/closets/syndicate.dm | 2 +- .../structures/crates_lockers/crates.dm | 19 +- code/game/objects/structures/curtains.dm | 11 +- code/game/objects/structures/decorations.dm | 13 +- code/game/objects/structures/door_assembly.dm | 6 +- code/game/objects/structures/flora/rocks.dm | 9 +- code/game/objects/structures/flora/trees.dm | 4 +- code/game/objects/structures/grille.dm | 44 +- code/game/objects/structures/janicart.dm | 12 +- code/game/objects/structures/mirror.dm | 7 +- .../objects/structures/props/beam_prism.dm | 33 +- .../structures/props/projectile_lock.dm | 15 +- .../objects/structures/props/puzzledoor.dm | 7 +- .../objects/structures/tables/interactions.dm | 8 +- code/game/objects/structures/target_stake.dm | 7 +- code/game/objects/structures/window.dm | 2 +- code/game/turfs/defense.dm | 2 - code/game/turfs/simulated/wall/defense.dm | 48 - .../turfs/simulated/wall/wall-construction.dm | 7 + code/game/turfs/simulated/wall/wall-damage.dm | 14 + .../game/turfs/simulated/wall/wall-defense.dm | 97 ++ code/game/turfs/simulated/wall/wall.dm | 6 +- .../game/turfs/simulated/wall/wall_attacks.dm | 6 +- code/game/turfs/turf-construction.dm | 7 + code/modules/admin/topic.dm | 2 +- code/modules/admin/verbs/SDQL2/SDQL_2.dm | 4 +- code/modules/admin/verbs/smite.dm | 7 - .../admin/view_variables/view_variables.dm | 2 +- code/modules/assembly/infrared.dm | 4 +- .../atmospherics/environmental/zas/debug.dm | 16 +- code/modules/awaymissions/loot_vr.dm | 4 +- code/modules/blob2/blobs/base_blob.dm | 17 +- code/modules/clothing/clothing_accessories.dm | 10 - code/modules/clothing/gloves/arm_guards.dm | 8 - code/modules/clothing/sets/armor/ablative.dm | 64 + code/modules/clothing/shoes/leg_guards.dm | 8 - code/modules/clothing/spacesuits/alien.dm | 2 +- code/modules/clothing/spacesuits/syndi.dm | 2 +- code/modules/clothing/spacesuits/void/merc.dm | 18 +- .../clothing/spacesuits/void/xeno/tajara.dm | 4 +- code/modules/clothing/suits/armor.dm | 67 +- .../clothing/under/accessories/armor.dm | 32 +- .../clothing/under/accessories/holster.dm | 2 +- code/modules/examine/descriptions/weapons.dm | 2 +- code/modules/flufftext/Hallucination.dm | 2 +- code/modules/food/drinks/bottle.dm | 2 +- .../gamemaster/actions/electrified_door.dm | 2 +- code/modules/ghostroles/spawnpoint.dm | 2 +- code/modules/hardsuits/_rig.dm | 5 - code/modules/hardsuits/modules/combat.dm | 6 +- code/modules/hardsuits/suits/merc.dm | 2 +- code/modules/holodeck/HolodeckObjects.dm | 11 +- code/modules/holomap/holomap_datum.dm | 4 +- code/modules/hydroponics/trays/tray.dm | 16 +- .../integrated_electronics/subtypes/time.dm | 2 +- code/modules/loot/packs/weapons.dm | 4 +- .../mapping/map_helpers/engine_loader.dm | 2 +- code/modules/mining/kinetic_crusher.dm | 8 +- code/modules/mining/machine_processing.dm | 4 +- code/modules/mining/mine_outcrops.dm | 9 +- code/modules/mining/mine_turfs.dm | 10 +- code/modules/mob/defense.dm | 83 -- code/modules/mob/grab.dm | 26 +- code/modules/mob/inventory/items.dm | 15 +- code/modules/mob/living/bot/secbot.dm | 4 +- .../mob/living/carbon/carbon-defense.dm | 14 +- .../mob/living/carbon/human/defense.dm | 52 +- ...human_damage.dm => human-damage-legacy.dm} | 3 - .../mob/living/carbon/human/human-damage.dm | 4 + ...man_defense.dm => human-defense-legacy.dm} | 80 +- .../mob/living/carbon/human/human-defense.dm | 28 + .../living/carbon/human/human_attackhand.dm | 13 +- .../living/carbon/human/traits/weaver_objs.dm | 6 +- code/modules/mob/living/inventory.dm | 34 - .../{damage_procs.dm => living-damage.dm} | 14 + .../{defense.dm => living-defense-legacy.dm} | 112 +- code/modules/mob/living/living-defense.dm | 114 +- .../modules/mob/living/silicon/robot/robot.dm | 10 +- .../robot/robot_modules/station/misc.dm | 4 +- .../silicon/robot/robot_modules/swarm.dm | 2 +- .../silicon/robot/robot_modules/syndicate.dm | 4 +- .../mob/living/silicon/silicon-damage.dm | 14 + .../living/silicon/silicon-defense-legacy.dm | 57 + code/modules/mob/living/silicon/silicon.dm | 74 - .../simple_animal/constructs/constructs.dm | 4 +- code/modules/mob/living/simple_mob/combat.dm | 10 +- .../subtypes/animal/giant_spider/hunter.dm | 7 +- .../subtypes/animal/giant_spider/lurker.dm | 2 +- .../simple_mob/subtypes/animal/roach/roach.dm | 2 +- .../subtypes/animal/space/mouse_army.dm | 4 +- .../living/simple_mob/subtypes/horror/Eddy.dm | 4 +- .../simple_mob/subtypes/horror/Master.dm | 4 +- .../simple_mob/subtypes/horror/Rickey.dm | 4 +- .../simple_mob/subtypes/horror/Smiley.dm | 4 +- .../simple_mob/subtypes/horror/Steve.dm | 4 +- .../simple_mob/subtypes/horror/Willy.dm | 4 +- .../simple_mob/subtypes/horror/bradley.dm | 4 +- .../simple_mob/subtypes/horror/sally.dm | 4 +- .../simple_mob/subtypes/horror/shittytim.dm | 4 +- .../simple_mob/subtypes/horror/timling.dm | 4 +- .../simple_mob/subtypes/humanoid/cultist.dm | 14 +- .../subtypes/humanoid/mercs/mercs.dm | 47 +- .../simple_mob/subtypes/humanoid/pirates.dm | 26 +- .../simple_mob/subtypes/illusion/illusion.dm | 8 +- .../mechanical/cyber_horror/cyber_horror.dm | 20 +- .../mechanical/hivebot/ranged_damage.dm | 26 +- .../mechanical/mecha/adv_dark_gygax.dm | 13 +- .../subtypes/mechanical/mecha/mecha.dm | 8 +- .../subtypes/mechanical/mecha/odysseus.dm | 7 +- .../subtypes/occult/constructs/juggernaut.dm | 74 +- .../simple_mob/subtypes/slime/feral/feral.dm | 8 +- .../subtypes/slime/xenobio/subtypes.dm | 37 +- .../subtypes/vore/corrupt_hounds.dm | 15 +- code/modules/mob/mob-damage.dm | 7 + code/modules/mob/mob-defense.dm | 121 ++ code/modules/mob/mob.dm | 3 - code/modules/mob/movement.dm | 10 +- .../computers/modular_computer/damage.dm | 10 +- code/modules/multiz/atoms.dm | 2 +- code/modules/multiz/movement.dm | 6 +- code/modules/multiz/turf.dm | 3 - code/modules/organs/external/external.dm | 2 +- .../organs/internal/augment/armmounted.dm | 2 +- .../overmap/legacy/ships/computers/sensors.dm | 6 +- code/modules/power/antimatter/control.dm | 11 +- code/modules/power/antimatter/shielding.dm | 9 +- code/modules/power/fusion/core/_core.dm | 5 +- code/modules/power/fusion/core/core_field.dm | 128 +- .../power/fusion/fusion_particle_catcher.dm | 43 - code/modules/power/port_gen.dm | 6 +- .../power/singularity/field_generator.dm | 15 +- .../particle_accelerator/particle.dm | 92 +- .../particle_accelerator/particle_smasher.dm | 10 +- code/modules/power/singularity/singularity.dm | 4 +- code/modules/power/supermatter/supermatter.dm | 52 +- code/modules/projectiles/README.md | 8 + .../projectiles/ammunition/ammo_caliber.dm | 2 +- .../ammunition/calibers/special/dart.dm | 9 +- code/modules/projectiles/gun.dm | 105 +- code/modules/projectiles/gun_item_renderer.dm | 2 +- code/modules/projectiles/gun_mob_renderer.dm | 2 +- .../ballistic/microbattery/medigun_cells.dm | 49 +- .../ballistic/microbattery/revolver_cells.dm | 22 +- .../guns/energy/kinetic_accelerator.dm | 15 +- .../projectiles/guns/energy/particle.dm | 8 +- .../projectiles/guns/energy/sizegun_vr.dm | 11 +- .../projectiles/guns/energy/special.dm | 2 +- code/modules/projectiles/guns/launcher.dm | 5 - .../guns/legacy_vr_guns/crestrose.dm | 14 +- .../guns/legacy_vr_guns/pummeler.dm | 9 +- .../guns/legacy_vr_guns/sickshot.dm | 8 +- code/modules/projectiles/guns/magic/staff.dm | 21 +- .../projectiles/guns/projectile/automatic.dm | 13 - .../projectiles/guns/projectile/boltaction.dm | 6 +- .../projectiles/guns/projectile/pistol.dm | 5 - .../projectiles/guns/projectile/shotgun.dm | 7 +- code/modules/projectiles/guns/vox.dm | 9 +- .../modules/projectiles/projectile-tracing.dm | 51 - code/modules/projectiles/projectile.dm | 1320 ----------------- code/modules/projectiles/projectile/README.md | 6 + code/modules/projectiles/projectile/arc.dm | 82 +- .../modules/projectiles/projectile/helpers.dm | 68 + .../modules/projectiles/projectile/pellets.dm | 118 -- .../projectile/projectile-hitscan_visuals.dm | 195 +++ .../projectile/projectile-physics.dm | 434 ++++++ .../projectile/projectile-tracing.dm | 68 + .../projectiles/projectile/projectile.dm | 1180 +++++++++++++++ .../projectile/projectile_effect.dm | 51 + .../projectile/{ => subtypes}/animate.dm | 0 .../projectiles/projectile/subtypes/arc.dm | 82 + .../projectile/{ => subtypes}/beam/beams.dm | 45 +- .../{ => subtypes}/beam/beams_vr.dm | 18 +- .../projectile/{ => subtypes}/beam/blaster.dm | 0 .../projectile/{ => subtypes}/blob.dm | 7 +- .../projectile/{ => subtypes}/bullets.dm | 145 +- .../projectile/{ => subtypes}/bullets_vr.dm | 0 .../projectile/{ => subtypes}/change.dm | 7 +- .../projectile/{ => subtypes}/energy.dm | 21 +- .../projectile/{ => subtypes}/energy_vr.dm | 0 .../projectile/{ => subtypes}/explosive.dm | 18 +- .../projectile/{ => subtypes}/force.dm | 9 +- .../projectile/{ => subtypes}/hook.dm | 15 +- .../projectile/{ => subtypes}/magic.dm | 4 +- .../projectile/{ => subtypes}/magnetic.dm | 71 +- .../projectiles/projectile/subtypes/pellet.dm | 153 ++ .../projectile/{ => subtypes}/reusable.dm | 53 +- .../projectile/{ => subtypes}/scatter.dm | 0 .../projectile/{ => subtypes}/special.dm | 111 +- .../projectile/{ => subtypes}/trace.dm | 4 +- code/modules/projectiles/unsorted/magic.dm | 143 +- code/modules/random_map/drop/drop_types.dm | 8 +- code/modules/reagents/chemistry/holder.dm | 2 +- .../machinery/reagent_dispenser/fuel.dm | 13 +- .../machinery/reagent_dispenser/oil.dm | 5 +- .../reagents/reagent_containers/syringes.dm | 6 +- code/modules/research/designs/weapons.dm | 4 +- code/modules/shieldgen/energy_field.dm | 2 +- code/modules/shieldgen/energy_shield.dm | 7 +- code/modules/shieldgen/sheldwallgen.dm | 17 +- code/modules/shuttles/shuttle_console.dm | 10 - .../species/promethean/promethean_blob.dm | 7 +- .../species/xenomorphs/alien_facehugger.dm | 6 +- code/modules/spells/spell_projectile.dm | 7 +- .../modules/spells/targeted/ethereal_jaunt.dm | 2 - code/modules/surgery/generic.dm | 2 +- code/modules/vehicles/sealed.dm | 2 +- .../mecha/equipment/weapons/energy/pulse.dm | 8 - .../sealed/mecha/equipment/weapons/weapons.dm | 4 +- code/modules/vehicles/sealed/mecha/mecha.dm | 67 +- .../vehicles/sealed/mecha/mecha_actions.dm | 18 +- .../vehicles/sealed/mecha/mecha_wreckage.dm | 10 - .../sealed/mecha/subtypes/combat/gorilla.dm | 6 +- .../sealed/mecha/subtypes/working/ripley.dm | 7 - .../modules/vehicles_legacy/Securitrain_vr.dm | 9 +- code/modules/vehicles_legacy/bike.dm | 7 +- code/modules/vehicles_legacy/cargo_train.dm | 2 +- code/modules/vehicles_legacy/rover_vr.dm | 9 +- code/modules/vehicles_legacy/train.dm | 9 +- code/modules/vehicles_legacy/vehicle.dm | 8 +- code/modules/vore/fluffstuff/custom_items.dm | 137 +- .../xenoarcheaology/artifacts/artifact.dm | 14 +- .../xenoarcheaology/tools/coolant_tank.dm | 7 +- code/modules/xenobio/items/weapons.dm | 8 +- code/modules/xenobio2/mob/xeno procs.dm | 2 +- code/modules/xenobio2/mob/xeno.dm | 2 +- config/entries/game.txt | 12 +- icons/effects/defensive/README.md | 3 + icons/effects/defensive/main_parry.dmi | Bin 0 -> 37674 bytes icons/items/melee/basic.dmi | Bin 0 -> 2957 bytes icons/items/melee/transforming.dmi | Bin 0 -> 30606 bytes icons/items/shields/basic.dmi | Bin 0 -> 22474 bytes icons/items/shields/transforming.dmi | Bin 0 -> 7584 bytes icons/mob/items/lefthand_melee.dmi | Bin 79062 -> 49066 bytes icons/mob/items/righthand_melee.dmi | Bin 75923 -> 46536 bytes icons/obj/weapons.dmi | Bin 81769 -> 69684 bytes icons/obj/weapons_vr.dmi | Bin 5045 -> 2977 bytes maps/away_missions/140x140/zoo.dmm | 2 +- maps/away_missions/archive/spacebattle.dmm | 6 +- .../archive/stationCollision.dmm | 2 +- maps/rift/levels/rift-11-orbital.dmm | 8 +- .../piratebase_192/levels/piratebase_192.dmm | 6 +- .../tradeport_140/levels/tradeport_140.dmm | 2 +- .../tradeport_192/levels/tradeport_192.dmm | 2 +- maps/templates/admin/dhael_centcom.dmm | 48 +- maps/templates/admin/ert_base.dmm | 20 +- maps/templates/admin/kk_mercship.dmm | 24 +- maps/templates/admin/mercbase.dmm | 24 +- maps/templates/admin/skipjack.dmm | 2 +- maps/templates/admin/thunderdome.dmm | 8 +- .../shuttles/overmaps/generic/cruiser.dmm | 20 +- .../shuttles/overmaps/generic/shelter_6.dmm | 16 +- maps/triumph/levels/flagship.dmm | 48 +- .../effects/combat/block-metal-on-metal-1.ogg | Bin 0 -> 10382 bytes .../effects/combat/block-metal-on-metal-2.ogg | Bin 0 -> 12604 bytes .../effects/combat/block-metal-on-wood-1.ogg | Bin 0 -> 8236 bytes .../effects/combat/block-metal-on-wood-2.ogg | Bin 0 -> 6631 bytes .../effects/combat/block-wood-on-wood-1.ogg | Bin 0 -> 12163 bytes .../effects/combat/block-wood-on-wood-2.ogg | Bin 0 -> 12764 bytes .../soundbytes/effects/combat/parry-cycle.ogg | Bin 0 -> 22373 bytes .../effects/combat/parry-metal1.ogg | Bin 0 -> 10612 bytes .../effects/combat/parry-metal2.ogg | Bin 0 -> 9412 bytes .../soundbytes/effects/combat/parry-wood1.ogg | Bin 0 -> 10321 bytes .../soundbytes/effects/combat/parry-wood2.ogg | Bin 0 -> 11514 bytes 437 files changed, 9340 insertions(+), 6546 deletions(-) create mode 100644 code/__DEFINES/datums/event_args.dm rename code/__DEFINES/dcs/signals/signals_atom/{signals_atom_buckling.dm => signals_atom-buckling.dm} (60%) rename code/__DEFINES/dcs/signals/signals_atom/{signals_atom_context_system.dm => signals_atom-context_system.dm} (95%) create mode 100644 code/__DEFINES/dcs/signals/signals_atom/signals_atom-defense.dm rename code/__DEFINES/dcs/signals/signals_atom/{signals_atom_throwing.dm => signals_atom-throwing.dm} (79%) rename code/__DEFINES/dcs/signals/signals_atom/{signals_atom_tool_system.dm => signals_atom-tool_system.dm} (91%) delete mode 100644 code/__DEFINES/dcs/signals/signals_atom/signals_atom_defense.dm rename code/__DEFINES/dcs/signals/signals_mob/{signals_mob_inventory.dm => signals_mob-inventory.dm} (85%) create mode 100644 code/__DEFINES/dcs/signals/signals_mob/signals_mob-perspective.dm create mode 100644 code/__DEFINES/dcs/signals/signals_mob/signals_mob_legacy.dm delete mode 100644 code/__DEFINES/dcs/signals/signals_mob/signals_mob_perspectiive.dm delete mode 100644 code/__DEFINES/event_args.dm create mode 100644 code/__DEFINES/projectiles/projectile.dm create mode 100644 code/__HELPERS/game/combat/arc.dm delete mode 100644 code/controllers/subsystem/processing/fastprocess.dm create mode 100644 code/controllers/subsystem/processing/process_20fps.dm create mode 100644 code/controllers/subsystem/processing/process_5fps.dm rename code/{__HELPERS/datastructs => datums/armor}/armor.dm (96%) create mode 100644 code/datums/components/items/active_parry.dm create mode 100644 code/datums/components/items/passive_parry.dm create mode 100644 code/datums/components/items/shield_block.dm create mode 100644 code/datums/components/mobs/block_frame.dm create mode 100644 code/datums/components/mobs/parry_frame.dm create mode 100644 code/datums/soundbytes/effects/combat.dm create mode 100644 code/game/atoms/atom-construction.dm create mode 100644 code/game/atoms/atom-damage.dm create mode 100644 code/game/atoms/atom-defense.dm delete mode 100644 code/game/atoms/defense.dm rename code/game/atoms/movable/{throwing.dm => movable-throw.dm} (99%) delete mode 100644 code/game/objects/defense.dm create mode 100644 code/game/objects/items-carry_weight.dm create mode 100644 code/game/objects/items-defense.dm create mode 100644 code/game/objects/items/melee/melee.dm rename code/game/objects/items/{weapons/melee => melee/types}/misc.dm (94%) create mode 100644 code/game/objects/items/melee/types/ninja_energy_blade.dm create mode 100644 code/game/objects/items/melee/types/transforming.dm create mode 100644 code/game/objects/items/melee/types/transforming/energy.dm create mode 100644 code/game/objects/items/melee/types/transforming/energy/axe.dm create mode 100644 code/game/objects/items/melee/types/transforming/energy/ionic_rapier.dm create mode 100644 code/game/objects/items/melee/types/transforming/energy/saber.dm create mode 100644 code/game/objects/items/melee/types/transforming/hfmachete.dm create mode 100644 code/game/objects/items/shield/shield.dm create mode 100644 code/game/objects/items/shield/types/shields_legacy.dm create mode 100644 code/game/objects/items/shield/types/shields_legacy_vr.dm create mode 100644 code/game/objects/items/shield/types/transforming.dm create mode 100644 code/game/objects/items/shield/types/transforming/energy.dm create mode 100644 code/game/objects/items/shield/types/transforming/telescopic.dm create mode 100644 code/game/objects/items/shield_projector/shield_matrix.dm rename code/{modules/shieldgen/directional_shield.dm => game/objects/items/shield_projector/shield_projector.dm} (81%) delete mode 100644 code/game/objects/items/weapons/melee/deflect.dm delete mode 100644 code/game/objects/items/weapons/melee/energy.dm delete mode 100644 code/game/objects/items/weapons/melee/melee.dm delete mode 100644 code/game/objects/items/weapons/shields.dm create mode 100644 code/game/objects/obj-construction.dm create mode 100644 code/game/objects/obj-defense.dm rename code/game/objects/{objs.dm => obj.dm} (100%) delete mode 100644 code/game/turfs/defense.dm delete mode 100644 code/game/turfs/simulated/wall/defense.dm create mode 100644 code/game/turfs/simulated/wall/wall-construction.dm create mode 100644 code/game/turfs/simulated/wall/wall-damage.dm create mode 100644 code/game/turfs/simulated/wall/wall-defense.dm create mode 100644 code/game/turfs/turf-construction.dm create mode 100644 code/modules/clothing/sets/armor/ablative.dm delete mode 100644 code/modules/mob/defense.dm rename code/modules/mob/living/carbon/human/{human_damage.dm => human-damage-legacy.dm} (98%) create mode 100644 code/modules/mob/living/carbon/human/human-damage.dm rename code/modules/mob/living/carbon/human/{human_defense.dm => human-defense-legacy.dm} (90%) rename code/modules/mob/living/{damage_procs.dm => living-damage.dm} (93%) rename code/modules/mob/living/{defense.dm => living-defense-legacy.dm} (86%) create mode 100644 code/modules/mob/living/silicon/silicon-damage.dm create mode 100644 code/modules/mob/living/silicon/silicon-defense-legacy.dm create mode 100644 code/modules/mob/mob-damage.dm create mode 100644 code/modules/mob/mob-defense.dm delete mode 100644 code/modules/power/fusion/fusion_particle_catcher.dm create mode 100644 code/modules/projectiles/README.md delete mode 100644 code/modules/projectiles/projectile-tracing.dm delete mode 100644 code/modules/projectiles/projectile.dm create mode 100644 code/modules/projectiles/projectile/README.md create mode 100644 code/modules/projectiles/projectile/helpers.dm delete mode 100644 code/modules/projectiles/projectile/pellets.dm create mode 100644 code/modules/projectiles/projectile/projectile-hitscan_visuals.dm create mode 100644 code/modules/projectiles/projectile/projectile-physics.dm create mode 100644 code/modules/projectiles/projectile/projectile-tracing.dm create mode 100644 code/modules/projectiles/projectile/projectile.dm create mode 100644 code/modules/projectiles/projectile/projectile_effect.dm rename code/modules/projectiles/projectile/{ => subtypes}/animate.dm (100%) create mode 100644 code/modules/projectiles/projectile/subtypes/arc.dm rename code/modules/projectiles/projectile/{ => subtypes}/beam/beams.dm (89%) rename code/modules/projectiles/projectile/{ => subtypes}/beam/beams_vr.dm (84%) rename code/modules/projectiles/projectile/{ => subtypes}/beam/blaster.dm (100%) rename code/modules/projectiles/projectile/{ => subtypes}/blob.dm (92%) rename code/modules/projectiles/projectile/{ => subtypes}/bullets.dm (84%) rename code/modules/projectiles/projectile/{ => subtypes}/bullets_vr.dm (100%) rename code/modules/projectiles/projectile/{ => subtypes}/change.dm (93%) rename code/modules/projectiles/projectile/{ => subtypes}/energy.dm (93%) rename code/modules/projectiles/projectile/{ => subtypes}/energy_vr.dm (100%) rename code/modules/projectiles/projectile/{ => subtypes}/explosive.dm (66%) rename code/modules/projectiles/projectile/{ => subtypes}/force.dm (70%) rename code/modules/projectiles/projectile/{ => subtypes}/hook.dm (95%) rename code/modules/projectiles/projectile/{ => subtypes}/magic.dm (94%) rename code/modules/projectiles/projectile/{ => subtypes}/magnetic.dm (72%) create mode 100644 code/modules/projectiles/projectile/subtypes/pellet.dm rename code/modules/projectiles/projectile/{ => subtypes}/reusable.dm (73%) rename code/modules/projectiles/projectile/{ => subtypes}/scatter.dm (100%) rename code/modules/projectiles/projectile/{ => subtypes}/special.dm (80%) rename code/modules/projectiles/projectile/{ => subtypes}/trace.dm (94%) create mode 100644 icons/effects/defensive/README.md create mode 100644 icons/effects/defensive/main_parry.dmi create mode 100644 icons/items/melee/basic.dmi create mode 100644 icons/items/melee/transforming.dmi create mode 100644 icons/items/shields/basic.dmi create mode 100644 icons/items/shields/transforming.dmi create mode 100644 sound/soundbytes/effects/combat/block-metal-on-metal-1.ogg create mode 100644 sound/soundbytes/effects/combat/block-metal-on-metal-2.ogg create mode 100644 sound/soundbytes/effects/combat/block-metal-on-wood-1.ogg create mode 100644 sound/soundbytes/effects/combat/block-metal-on-wood-2.ogg create mode 100644 sound/soundbytes/effects/combat/block-wood-on-wood-1.ogg create mode 100644 sound/soundbytes/effects/combat/block-wood-on-wood-2.ogg create mode 100644 sound/soundbytes/effects/combat/parry-cycle.ogg create mode 100644 sound/soundbytes/effects/combat/parry-metal1.ogg create mode 100644 sound/soundbytes/effects/combat/parry-metal2.ogg create mode 100644 sound/soundbytes/effects/combat/parry-wood1.ogg create mode 100644 sound/soundbytes/effects/combat/parry-wood2.ogg diff --git a/.github/workflows/size_labeling.yml b/.github/workflows/size_labeling.yml index d0dfe06c8377..47d3ee779c4a 100644 --- a/.github/workflows/size_labeling.yml +++ b/.github/workflows/size_labeling.yml @@ -5,7 +5,7 @@ jobs: runs-on: ubuntu-latest steps: - name: size-label - uses: pascalgn/size-label-action@v0.4.3 + uses: pascalgn/size-label-action@v0.5.4 env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" IGNORED: "**/*.bundle.*\n**/*.chunk.*" # **/*.dmm\n diff --git a/citadel.dme b/citadel.dme index 898d2771d3f1..ff216dc4a2ad 100644 --- a/citadel.dme +++ b/citadel.dme @@ -46,7 +46,6 @@ #include "code\__DEFINES\damage_organs.dm" #include "code\__DEFINES\directional.dm" #include "code\__DEFINES\dna.dm" -#include "code\__DEFINES\event_args.dm" #include "code\__DEFINES\fonts.dm" #include "code\__DEFINES\frames.dm" #include "code\__DEFINES\gamemode.dm" @@ -162,6 +161,7 @@ #include "code\__DEFINES\controllers\timer.dm" #include "code\__DEFINES\datums\beam.dm" #include "code\__DEFINES\datums\design.dm" +#include "code\__DEFINES\datums\event_args.dm" #include "code\__DEFINES\dcs\flags.dm" #include "code\__DEFINES\dcs\helpers.dm" #include "code\__DEFINES\dcs\components\riding.dm" @@ -177,33 +177,34 @@ #include "code\__DEFINES\dcs\signals\elements\signals_element_conflict_checking.dm" #include "code\__DEFINES\dcs\signals\items\signals_inducer.dm" #include "code\__DEFINES\dcs\signals\modules\signals_module_fishing.dm" +#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-buckling.dm" +#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-context_system.dm" +#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-defense.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-reachability.dm" +#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-throwing.dm" +#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-tool_system.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_appearance.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_attack.dm" -#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_buckling.dm" -#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_context_system.dm" -#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_defense.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_main.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_mouse.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_movable.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_movement.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_radiation.dm" -#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_throwing.dm" -#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_tool_system.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_x_act.dm" #include "code\__DEFINES\dcs\signals\signals_item\signals_item-interaction.dm" #include "code\__DEFINES\dcs\signals\signals_item\signals_item_economy.dm" #include "code\__DEFINES\dcs\signals\signals_item\signals_item_inventory.dm" #include "code\__DEFINES\dcs\signals\signals_item\signals_item_mouse.dm" #include "code\__DEFINES\dcs\signals\signals_item\signals_item_storage.dm" +#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob-inventory.dm" +#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob-perspective.dm" #include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_appearance.dm" #include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_combat.dm" #include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_defense.dm" -#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_inventory.dm" +#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_legacy.dm" #include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_main.dm" #include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_mobility.dm" #include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_organs.dm" -#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_perspectiive.dm" #include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_simple.dm" #include "code\__DEFINES\economy\category.dm" #include "code\__DEFINES\economy\currency.dm" @@ -293,6 +294,7 @@ #include "code\__DEFINES\projectiles\ammo_casing.dm" #include "code\__DEFINES\projectiles\ammo_magazine.dm" #include "code\__DEFINES\projectiles\guns.dm" +#include "code\__DEFINES\projectiles\projectile.dm" #include "code\__DEFINES\projectiles\system.dm" #include "code\__DEFINES\radiation\flags.dm" #include "code\__DEFINES\radiation\ignore.dm" @@ -380,7 +382,6 @@ #include "code\__HELPERS\vector.dm" #include "code\__HELPERS\verbs.dm" #include "code\__HELPERS\animations\attack.dm" -#include "code\__HELPERS\datastructs\armor.dm" #include "code\__HELPERS\datastructs\bodytypes.dm" #include "code\__HELPERS\datastructs\filters.dm" #include "code\__HELPERS\datastructs\ingredients.dm" @@ -391,6 +392,7 @@ #include "code\__HELPERS\files\paths.dm" #include "code\__HELPERS\files\walk.dm" #include "code\__HELPERS\game\depth.dm" +#include "code\__HELPERS\game\combat\arc.dm" #include "code\__HELPERS\game\turfs\line.dm" #include "code\__HELPERS\game\turfs\offsets.dm" #include "code\__HELPERS\graphs\astar.dm" @@ -639,10 +641,11 @@ #include "code\controllers\subsystem\persistence\modules\bulk_entity_serializers\trash.dm" #include "code\controllers\subsystem\processing\chemistry.dm" #include "code\controllers\subsystem\processing\circuits.dm" -#include "code\controllers\subsystem\processing\fastprocess.dm" #include "code\controllers\subsystem\processing\instruments.dm" #include "code\controllers\subsystem\processing\moving_cameras.dm" #include "code\controllers\subsystem\processing\obj.dm" +#include "code\controllers\subsystem\processing\process_20fps.dm" +#include "code\controllers\subsystem\processing\process_5fps.dm" #include "code\controllers\subsystem\processing\processing.dm" #include "code\controllers\subsystem\processing\projectiles.dm" #include "code\controllers\subsystem\processing\turfs.dm" @@ -698,6 +701,7 @@ #include "code\datums\announce\location\global.dm" #include "code\datums\announce\location\main_station.dm" #include "code\datums\announce\location\overmap_sector.dm" +#include "code\datums\armor\armor.dm" #include "code\datums\armor\core.dm" #include "code\datums\armor\mecha.dm" #include "code\datums\armor\military.dm" @@ -766,7 +770,10 @@ #include "code\datums\components\atoms\radioactive.dm" #include "code\datums\components\crafting\crafting.dm" #include "code\datums\components\crafting\guncrafting.dm" +#include "code\datums\components\items\passive_parry.dm" #include "code\datums\components\items\wielding.dm" +#include "code\datums\components\mobs\block_frame.dm" +#include "code\datums\components\mobs\parry_frame.dm" #include "code\datums\components\movable\aquarium.dm" #include "code\datums\components\movable\spatial_grid.dm" #include "code\datums\components\objects\slippery.dm" @@ -896,6 +903,7 @@ #include "code\datums\repositories\unique.dm" #include "code\datums\soundbytes\_soundbyte.dm" #include "code\datums\soundbytes\announcer.dm" +#include "code\datums\soundbytes\effects\combat.dm" #include "code\datums\soundbytes\effects\explosion.dm" #include "code\datums\soundbytes\effects\sparks.dm" #include "code\datums\soundbytes\effects\spray.dm" @@ -1012,18 +1020,20 @@ #include "code\game\area\station\security_areas.dm" #include "code\game\atoms\action_feedback.dm" #include "code\game\atoms\appearance.dm" +#include "code\game\atoms\atom-construction.dm" +#include "code\game\atoms\atom-damage.dm" +#include "code\game\atoms\atom-defense.dm" #include "code\game\atoms\atom.dm" #include "code\game\atoms\buckling.dm" -#include "code\game\atoms\defense.dm" #include "code\game\atoms\defense_old.dm" #include "code\game\atoms\materials.dm" #include "code\game\atoms\movement.dm" #include "code\game\atoms\say.dm" #include "code\game\atoms\vv.dm" +#include "code\game\atoms\movable\movable-throw.dm" #include "code\game\atoms\movable\movable.dm" #include "code\game\atoms\movable\movement.dm" #include "code\game\atoms\movable\pulling.dm" -#include "code\game\atoms\movable\throwing.dm" #include "code\game\atoms\movable\vv.dm" #include "code\game\atoms\movable\special\overlay.dm" #include "code\game\atoms\movable\special\render.dm" @@ -1450,16 +1460,19 @@ #include "code\game\magic\Uristrunes.dm" #include "code\game\objects\attacks.dm" #include "code\game\objects\banners.dm" -#include "code\game\objects\defense.dm" #include "code\game\objects\empulse.dm" #include "code\game\objects\explosion.dm" #include "code\game\objects\explosion_recursive.dm" +#include "code\game\objects\items-carry_weight.dm" +#include "code\game\objects\items-defense.dm" #include "code\game\objects\items-interaction.dm" #include "code\game\objects\items.dm" #include "code\game\objects\materials.dm" #include "code\game\objects\misc.dm" #include "code\game\objects\mob_spawner.dm" -#include "code\game\objects\objs.dm" +#include "code\game\objects\obj-construction.dm" +#include "code\game\objects\obj-defense.dm" +#include "code\game\objects\obj.dm" #include "code\game\objects\structures.dm" #include "code\game\objects\stumble_into_vr.dm" #include "code\game\objects\topic.dm" @@ -1644,6 +1657,15 @@ #include "code\game\objects\items\id_cards\station_ids.dm" #include "code\game\objects\items\id_cards\syndicate_ids.dm" #include "code\game\objects\items\janitorial\soap.dm" +#include "code\game\objects\items\melee\melee.dm" +#include "code\game\objects\items\melee\types\misc.dm" +#include "code\game\objects\items\melee\types\ninja_energy_blade.dm" +#include "code\game\objects\items\melee\types\transforming.dm" +#include "code\game\objects\items\melee\types\transforming\energy.dm" +#include "code\game\objects\items\melee\types\transforming\hfmachete.dm" +#include "code\game\objects\items\melee\types\transforming\energy\axe.dm" +#include "code\game\objects\items\melee\types\transforming\energy\ionic_rapier.dm" +#include "code\game\objects\items\melee\types\transforming\energy\saber.dm" #include "code\game\objects\items\robot\gripper.dm" #include "code\game\objects\items\robot\robot_items.dm" #include "code\game\objects\items\robot\robot_parts.dm" @@ -1654,6 +1676,14 @@ #include "code\game\objects\items\scanners\reagent.dm" #include "code\game\objects\items\scanners\slime.dm" #include "code\game\objects\items\scanners\spectrometer.dm" +#include "code\game\objects\items\shield\shield.dm" +#include "code\game\objects\items\shield\types\shields_legacy.dm" +#include "code\game\objects\items\shield\types\shields_legacy_vr.dm" +#include "code\game\objects\items\shield\types\transforming.dm" +#include "code\game\objects\items\shield\types\transforming\energy.dm" +#include "code\game\objects\items\shield\types\transforming\telescopic.dm" +#include "code\game\objects\items\shield_projector\shield_matrix.dm" +#include "code\game\objects\items\shield_projector\shield_projector.dm" #include "code\game\objects\items\stacks\fifty_spawner.dm" #include "code\game\objects\items\stacks\marker_beacons.dm" #include "code\game\objects\items\stacks\matter_synth.dm" @@ -1735,7 +1765,6 @@ #include "code\game\objects\items\weapons\RPD.dm" #include "code\game\objects\items\weapons\RSF.dm" #include "code\game\objects\items\weapons\scrolls.dm" -#include "code\game\objects\items\weapons\shields.dm" #include "code\game\objects\items\weapons\stunbaton.dm" #include "code\game\objects\items\weapons\surgery_tools.dm" #include "code\game\objects\items\weapons\swords_axes_etc.dm" @@ -1787,10 +1816,6 @@ #include "code\game\objects\items\weapons\material\thrown.dm" #include "code\game\objects\items\weapons\material\twohanded.dm" #include "code\game\objects\items\weapons\material\whetstone.dm" -#include "code\game\objects\items\weapons\melee\deflect.dm" -#include "code\game\objects\items\weapons\melee\energy.dm" -#include "code\game\objects\items\weapons\melee\melee.dm" -#include "code\game\objects\items\weapons\melee\misc.dm" #include "code\game\objects\items\weapons\tanks\jetpack.dm" #include "code\game\objects\items\weapons\tanks\tank.dm" #include "code\game\objects\items\weapons\tanks\tank_types.dm" @@ -1981,8 +2006,8 @@ #include "code\game\rendering\plane_masters\plane_render.dm" #include "code\game\turfs\baseturfs.dm" #include "code\game\turfs\change_turf.dm" -#include "code\game\turfs\defense.dm" #include "code\game\turfs\simulated.dm" +#include "code\game\turfs\turf-construction.dm" #include "code\game\turfs\turf.dm" #include "code\game\turfs\turf_ao.dm" #include "code\game\turfs\turf_flick_animations.dm" @@ -2016,9 +2041,11 @@ #include "code\game\turfs\simulated\flooring\flooring_traps.dm" #include "code\game\turfs\simulated\flooring\shuttle_vr.dm" #include "code\game\turfs\simulated\misc\fancy_shuttles.dm" -#include "code\game\turfs\simulated\wall\defense.dm" #include "code\game\turfs\simulated\wall\materials.dm" #include "code\game\turfs\simulated\wall\rot.dm" +#include "code\game\turfs\simulated\wall\wall-construction.dm" +#include "code\game\turfs\simulated\wall\wall-damage.dm" +#include "code\game\turfs\simulated\wall\wall-defense.dm" #include "code\game\turfs\simulated\wall\wall.dm" #include "code\game\turfs\simulated\wall\wall_attacks.dm" #include "code\game\turfs\simulated\wall\wall_icon.dm" @@ -2481,6 +2508,7 @@ #include "code\modules\clothing\masks\gasmask.dm" #include "code\modules\clothing\masks\miscellaneous.dm" #include "code\modules\clothing\masks\voice.dm" +#include "code\modules\clothing\sets\armor\ablative.dm" #include "code\modules\clothing\shoes\_shoes.dm" #include "code\modules\clothing\shoes\boots.dm" #include "code\modules\clothing\shoes\colour.dm" @@ -3423,7 +3451,6 @@ #include "code\modules\mob\animations.dm" #include "code\modules\mob\client.dm" #include "code\modules\mob\death.dm" -#include "code\modules\mob\defense.dm" #include "code\modules\mob\emote.dm" #include "code\modules\mob\floating_message.dm" #include "code\modules\mob\gender.dm" @@ -3435,6 +3462,8 @@ #include "code\modules\mob\life.dm" #include "code\modules\mob\login.dm" #include "code\modules\mob\logout.dm" +#include "code\modules\mob\mob-damage.dm" +#include "code\modules\mob\mob-defense.dm" #include "code\modules\mob\mob-keybind-triggers.dm" #include "code\modules\mob\mob.dm" #include "code\modules\mob\mob_defines.dm" @@ -3503,13 +3532,13 @@ #include "code\modules\mob\inventory\stripping.dm" #include "code\modules\mob\living\autohiss.dm" #include "code\modules\mob\living\butchering.dm" -#include "code\modules\mob\living\damage_procs.dm" #include "code\modules\mob\living\death.dm" #include "code\modules\mob\living\default_language.dm" -#include "code\modules\mob\living\defense.dm" #include "code\modules\mob\living\health.dm" #include "code\modules\mob\living\inventory.dm" #include "code\modules\mob\living\life.dm" +#include "code\modules\mob\living\living-damage.dm" +#include "code\modules\mob\living\living-defense-legacy.dm" #include "code\modules\mob\living\living-defense.dm" #include "code\modules\mob\living\living.dm" #include "code\modules\mob\living\living_defines.dm" @@ -3595,11 +3624,12 @@ #include "code\modules\mob\living\carbon\human\emote.dm" #include "code\modules\mob\living\carbon\human\examine.dm" #include "code\modules\mob\living\carbon\human\health.dm" +#include "code\modules\mob\living\carbon\human\human-damage-legacy.dm" +#include "code\modules\mob\living\carbon\human\human-damage.dm" +#include "code\modules\mob\living\carbon\human\human-defense-legacy.dm" #include "code\modules\mob\living\carbon\human\human-defense.dm" #include "code\modules\mob\living\carbon\human\human.dm" #include "code\modules\mob\living\carbon\human\human_attackhand.dm" -#include "code\modules\mob\living\carbon\human\human_damage.dm" -#include "code\modules\mob\living\carbon\human\human_defense.dm" #include "code\modules\mob\living\carbon\human\human_defines.dm" #include "code\modules\mob\living\carbon\human\human_helpers.dm" #include "code\modules\mob\living\carbon\human\human_modular_limbs.dm" @@ -3645,6 +3675,8 @@ #include "code\modules\mob\living\silicon\offense.dm" #include "code\modules\mob\living\silicon\perspective.dm" #include "code\modules\mob\living\silicon\say.dm" +#include "code\modules\mob\living\silicon\silicon-damage.dm" +#include "code\modules\mob\living\silicon\silicon-defense-legacy.dm" #include "code\modules\mob\living\silicon\silicon.dm" #include "code\modules\mob\living\silicon\subystems.dm" #include "code\modules\mob\living\silicon\translation.dm" @@ -4228,7 +4260,6 @@ #include "code\modules\power\fission\rods.dm" #include "code\modules\power\fusion\_setup.dm" #include "code\modules\power\fusion\fusion_circuits.dm" -#include "code\modules\power\fusion\fusion_particle_catcher.dm" #include "code\modules\power\fusion\fusion_reactions.dm" #include "code\modules\power\fusion\fusion_reagents.dm" #include "code\modules\power\fusion\magpower.dm" @@ -4326,8 +4357,6 @@ #include "code\modules\projectiles\gun.dm" #include "code\modules\projectiles\gun_item_renderer.dm" #include "code\modules\projectiles\gun_mob_renderer.dm" -#include "code\modules\projectiles\projectile-tracing.dm" -#include "code\modules\projectiles\projectile.dm" #include "code\modules\projectiles\ammunition\ammo_caliber.dm" #include "code\modules\projectiles\ammunition\ammo_casing.dm" #include "code\modules\projectiles\ammunition\ammo_magazine.dm" @@ -4425,25 +4454,32 @@ #include "code\modules\projectiles\guns\projectile\caseless\pellet.dm" #include "code\modules\projectiles\guns\projectile\sniper\collapsible_sniper.dm" #include "code\modules\projectiles\projectile\arc.dm" -#include "code\modules\projectiles\projectile\blob.dm" -#include "code\modules\projectiles\projectile\bullets.dm" -#include "code\modules\projectiles\projectile\bullets_vr.dm" -#include "code\modules\projectiles\projectile\change.dm" -#include "code\modules\projectiles\projectile\energy.dm" -#include "code\modules\projectiles\projectile\energy_vr.dm" -#include "code\modules\projectiles\projectile\explosive.dm" -#include "code\modules\projectiles\projectile\force.dm" -#include "code\modules\projectiles\projectile\hook.dm" -#include "code\modules\projectiles\projectile\magic.dm" -#include "code\modules\projectiles\projectile\magnetic.dm" -#include "code\modules\projectiles\projectile\pellets.dm" -#include "code\modules\projectiles\projectile\reusable.dm" -#include "code\modules\projectiles\projectile\scatter.dm" -#include "code\modules\projectiles\projectile\special.dm" -#include "code\modules\projectiles\projectile\trace.dm" -#include "code\modules\projectiles\projectile\beam\beams.dm" -#include "code\modules\projectiles\projectile\beam\beams_vr.dm" -#include "code\modules\projectiles\projectile\beam\blaster.dm" +#include "code\modules\projectiles\projectile\helpers.dm" +#include "code\modules\projectiles\projectile\projectile-hitscan_visuals.dm" +#include "code\modules\projectiles\projectile\projectile-physics.dm" +#include "code\modules\projectiles\projectile\projectile-tracing.dm" +#include "code\modules\projectiles\projectile\projectile.dm" +#include "code\modules\projectiles\projectile\projectile_effect.dm" +#include "code\modules\projectiles\projectile\subtypes\arc.dm" +#include "code\modules\projectiles\projectile\subtypes\blob.dm" +#include "code\modules\projectiles\projectile\subtypes\bullets.dm" +#include "code\modules\projectiles\projectile\subtypes\bullets_vr.dm" +#include "code\modules\projectiles\projectile\subtypes\change.dm" +#include "code\modules\projectiles\projectile\subtypes\energy.dm" +#include "code\modules\projectiles\projectile\subtypes\energy_vr.dm" +#include "code\modules\projectiles\projectile\subtypes\explosive.dm" +#include "code\modules\projectiles\projectile\subtypes\force.dm" +#include "code\modules\projectiles\projectile\subtypes\hook.dm" +#include "code\modules\projectiles\projectile\subtypes\magic.dm" +#include "code\modules\projectiles\projectile\subtypes\magnetic.dm" +#include "code\modules\projectiles\projectile\subtypes\pellet.dm" +#include "code\modules\projectiles\projectile\subtypes\reusable.dm" +#include "code\modules\projectiles\projectile\subtypes\scatter.dm" +#include "code\modules\projectiles\projectile\subtypes\special.dm" +#include "code\modules\projectiles\projectile\subtypes\trace.dm" +#include "code\modules\projectiles\projectile\subtypes\beam\beams.dm" +#include "code\modules\projectiles\projectile\subtypes\beam\beams_vr.dm" +#include "code\modules\projectiles\projectile\subtypes\beam\blaster.dm" #include "code\modules\projectiles\targeting\targeting_client.dm" #include "code\modules\projectiles\targeting\targeting_gun.dm" #include "code\modules\projectiles\targeting\targeting_mob.dm" @@ -4608,7 +4644,6 @@ #include "code\modules\sculpting\sculpting_block.dm" #include "code\modules\security levels\keycard authentication.dm" #include "code\modules\security levels\security levels.dm" -#include "code\modules\shieldgen\directional_shield.dm" #include "code\modules\shieldgen\emergency_shield.dm" #include "code\modules\shieldgen\energy_field.dm" #include "code\modules\shieldgen\energy_shield.dm" diff --git a/code/__DEFINES/_core.dm b/code/__DEFINES/_core.dm index 041c95757dcb..2a6a70722d89 100644 --- a/code/__DEFINES/_core.dm +++ b/code/__DEFINES/_core.dm @@ -12,5 +12,8 @@ /// * put the stack trace in stack trace storage #define stack_trace(message) _stack_trace(message, __FILE__, __LINE__) +/// get variable if not null or +#define VALUE_OR_DEFAULT(VAL, DEFAULT) (isnull(VAL)? (DEFAULT) : (VAL)) + /// byond bug https://secure.byond.com/forum/?post=2072419 #define BLOCK_BYOND_BUG_2072419 diff --git a/code/__DEFINES/_flags/atom_flags.dm b/code/__DEFINES/_flags/atom_flags.dm index 8b10246acf6f..8a7ccb364e17 100644 --- a/code/__DEFINES/_flags/atom_flags.dm +++ b/code/__DEFINES/_flags/atom_flags.dm @@ -59,7 +59,7 @@ DEFINE_BITFIELD(atom_flags, list( #define MOVABLE_NO_THROW_DAMAGE_SCALING (1<<1) /// Do not spin when thrown. #define MOVABLE_NO_THROW_SPIN (1<<2) -/// We are currently about to be yanked by a Moved() triggering a Move() +/// We are currently about to be yanked by a Moved(), Entered(), or Exited() triggering a Move() /// /// * used so things like projectile hitscans know to yield #define MOVABLE_IN_MOVED_YANK (1<<3) @@ -85,11 +85,13 @@ DEFINE_BITFIELD(movable_flags, list( #define ATOM_PASS_OVERHEAD_THROW (1<<7) /// let buckled mobs pass always #define ATOM_PASS_BUCKLED (1<<8) +/// "please don't interact with us" +#define ATOM_PASS_INCORPOREAL (1<<9) /// all actual pass flags / maximum pass #define ATOM_PASS_ALL (ATOM_PASS_TABLE | ATOM_PASS_GLASS | ATOM_PASS_GRILLE | \ ATOM_PASS_BLOB | ATOM_PASS_MOB | ATOM_PASS_THROWN | ATOM_PASS_CLICK | \ - ATOM_PASS_OVERHEAD_THROW | ATOM_PASS_BUCKLED) + ATOM_PASS_OVERHEAD_THROW | ATOM_PASS_BUCKLED | ATOM_PASS_INCORPOREAL) DEFINE_BITFIELD(pass_flags, list( BITFIELD(ATOM_PASS_TABLE), @@ -101,6 +103,7 @@ DEFINE_BITFIELD(pass_flags, list( BITFIELD(ATOM_PASS_CLICK), BITFIELD(ATOM_PASS_OVERHEAD_THROW), BITFIELD(ATOM_PASS_BUCKLED), + BITFIELD(ATOM_PASS_INCORPOREAL), )) DEFINE_BITFIELD(pass_flags_self, list( @@ -113,6 +116,7 @@ DEFINE_BITFIELD(pass_flags_self, list( BITFIELD(ATOM_PASS_CLICK), BITFIELD(ATOM_PASS_OVERHEAD_THROW), BITFIELD(ATOM_PASS_BUCKLED), + BITFIELD(ATOM_PASS_INCORPOREAL), )) //? /atom/movable movement_type - only one primary type should be on the atom at a time, but these are flags for quick checks. diff --git a/code/__DEFINES/_planes+layers.dm b/code/__DEFINES/_planes+layers.dm index 37eaaacbeb63..b38ab1a0fdd9 100644 --- a/code/__DEFINES/_planes+layers.dm +++ b/code/__DEFINES/_planes+layers.dm @@ -178,6 +178,7 @@ #define STAIRS_LAYER (TURF_LAYER+0.5) /// Layer for stairs. #define DOOR_OPEN_LAYER (TURF_LAYER+0.7) /// Under all objects if opened. 2.7 due to tables being at 2.6. #define TABLE_LAYER (TURF_LAYER+0.8) /// Just under stuff that wants to be slightly below common objects. +/// Below this layer, projectiles won't collide with things unless it's a directly clicked target. #define PROJECTILE_HIT_THRESHOLD_LAYER 2.8 #define UNDER_JUNK_LAYER (TURF_LAYER+0.9) /// Things that want to be slightly below common objects. diff --git a/code/__DEFINES/callbacks.dm b/code/__DEFINES/callbacks.dm index 33eda1bac26d..1c590425eb2a 100644 --- a/code/__DEFINES/callbacks.dm +++ b/code/__DEFINES/callbacks.dm @@ -1,10 +1,12 @@ /// Arbitrary sentinel value for global proc callbacks #define GLOBAL_PROC "some_magic_bullshit" +/// Arbitrary sentinel value for making sure a callback didn't sleep +#define CALLBACK_SLEEP_SENTINEL "___THE PROC SLEPT___" /// A shorthand for the callback datum, [documented here](datum/callback.html) #define CALLBACK new /datum/callback -///Per the DM reference, spawn(-1) will execute the spawned code immediately until a block is met. +/// Per the DM reference, spawn(-1) will execute the spawned code immediately until a block is met. #define MAKE_SPAWN_ACT_LIKE_WAITFOR -1 -///Create a codeblock that will not block the callstack if a block is met. +/// Create a codeblock that will not block the callstack if a block is met. #define ASYNC spawn(MAKE_SPAWN_ACT_LIKE_WAITFOR) #define INVOKE_ASYNC(proc_owner, proc_path, proc_arguments...) \ diff --git a/code/__DEFINES/combat/armor.dm b/code/__DEFINES/combat/armor.dm index f57d5e4f6f22..67ede87ad789 100644 --- a/code/__DEFINES/combat/armor.dm +++ b/code/__DEFINES/combat/armor.dm @@ -59,14 +59,26 @@ GLOBAL_REAL_LIST(armor_enums) = list( ARMOR_ACID, ) +GLOBAL_REAL_LIST(armor_types) = list( + ARMOR_MELEE, + ARMOR_BULLET, + ARMOR_LASER, + ARMOR_ENERGY, + ARMOR_BOMB, + ARMOR_BIO, + ARMOR_RAD, + ARMOR_FIRE, + ARMOR_ACID, +) + //? --- armor tiers --- -#define ARMOR_TIER_DEFAULT 0 +#define ARMOR_TIER_DEFAULT ARMOR_TIER_BASELINE #define ARMOR_TIER_LAUGHABLE -3 #define ARMOR_TIER_LOW -2 #define ARMOR_TIER_BELOW -1 -#define ARMOR_TIER_NORMAL 0 +#define ARMOR_TIER_BASELINE 0 #define ARMOR_TIER_ABOVE 1 #define ARMOR_TIER_HIGH 2 #define ARMOR_TIER_OVERWHELMING 3 @@ -76,37 +88,49 @@ GLOBAL_REAL_LIST(armor_enums) = list( //? melee -#define MELEE_TIER_DEFAULT ARMOR_TIER_DEFAULT +#define MELEE_TIER_DEFAULT MELEE_TIER_MEDIUM #define MELEE_TIER_UNARMED_DEFAULT ARMOR_TIER_LOW #define MELEE_TIER_UNARMED_FISTS ARMOR_TIER_LOW #define MELEE_TIER_UNARMED_CLAW ARMOR_TIER_BELOW -#define MELEE_TIER_LIGHT ARMOR_TIER_DEFAULT +#define MELEE_TIER_LIGHT ARMOR_TIER_BASELINE #define MELEE_TIER_MEDIUM ARMOR_TIER_ABOVE #define MELEE_TIER_HEAVY ARMOR_TIER_HIGH #define MELEE_TIER_EXTREME ARMOR_TIER_OVERWHELMING //? bullet -#define BULLET_TIER_DEFAULT ARMOR_TIER_DEFAULT - -#define BULLET_TIER_LAUGHABLE ARMOR_TIER_BELOW //! super improvised rounds / pistols / whatever. -#define BULLET_TIER_LOW ARMOR_TIER_DEFAULT //! pistols -#define BULLET_TIER_MEDIUM ARMOR_TIER_ABOVE //! smgs -#define BULLET_TIER_HIGH ARMOR_TIER_HIGH //! rifles -#define BULLET_TIER_EXTREME ARMOR_TIER_OVERWHELMING //! lmgs, light mech weapons -#define BULLET_TIER_RIDICULOUS ARMOR_TIER_RIDICULOUS //! heavy mech weapons +#define BULLET_TIER_DEFAULT BULLET_TIER_MEDIUM + +/// super improvised rounds / pistols / whatever. +#define BULLET_TIER_LAUGHABLE ARMOR_TIER_BELOW +/// pistols +#define BULLET_TIER_LOW ARMOR_TIER_BASELINE +/// smgs +#define BULLET_TIER_MEDIUM ARMOR_TIER_ABOVE +/// rifles +#define BULLET_TIER_HIGH ARMOR_TIER_HIGH +/// lmgs, light mech weapons +#define BULLET_TIER_EXTREME ARMOR_TIER_OVERWHELMING +/// heavy mech weapons +#define BULLET_TIER_RIDICULOUS ARMOR_TIER_RIDICULOUS //? laser -#define LASER_TIER_DEFAULT ARMOR_TIER_DEFAULT - -#define LASER_TIER_LAUGHABLE ARMOR_TIER_BELOW //! improvised laser focis / etc -#define LASER_TIER_LOW ARMOR_TIER_DEFAULT //! low tier lasers -#define LASER_TIER_MEDIUM ARMOR_TIER_ABOVE //! laser carbines, energy guns, etc -#define LASER_TIER_HIGH ARMOR_TIER_HIGH //! x-ray rifles, snipers -#define LASER_TIER_EXTREME ARMOR_TIER_OVERWHELMING //! mech weapons, usualy -#define LASER_TIER_RIDICULOUS ARMOR_TIER_RIDICULOUS //! power transmission laser? +#define LASER_TIER_DEFAULT LASER_TIER_MEDIUM + +/// improvised laser focis / etc +#define LASER_TIER_LAUGHABLE ARMOR_TIER_BELOW +/// low tier lasers +#define LASER_TIER_LOW ARMOR_TIER_BASELINE +/// laser carbines, energy guns, etc +#define LASER_TIER_MEDIUM ARMOR_TIER_ABOVE +/// x-ray rifles, snipers +#define LASER_TIER_HIGH ARMOR_TIER_HIGH +/// mech weapons, usualy +#define LASER_TIER_EXTREME ARMOR_TIER_OVERWHELMING +/// power transmission laser? +#define LASER_TIER_RIDICULOUS ARMOR_TIER_RIDICULOUS //? --- armor calculations --- diff --git a/code/__DEFINES/combat/attack_types.dm b/code/__DEFINES/combat/attack_types.dm index 26989b0a23c8..a0b4176d80ea 100644 --- a/code/__DEFINES/combat/attack_types.dm +++ b/code/__DEFINES/combat/attack_types.dm @@ -9,3 +9,10 @@ #define ATTACK_TYPE_THROWN (1<<2) /// damage source is /mob #define ATTACK_TYPE_UNARMED (1<<3) +/// we're being contacted by something +/// +/// * used internally by parry frames, mostly +/// * damage source is null +#define ATTACK_TYPE_TOUCH (1<<4) +/// a damage instance created by a block / parry frame transmuting damage and passing it to the user +#define ATTACK_TYPE_DEFENSIVE_PASSTHROUGH (1<<5) diff --git a/code/__DEFINES/combat/explosions.dm b/code/__DEFINES/combat/explosions.dm index c8709ccfd8a3..a60badb34c03 100644 --- a/code/__DEFINES/combat/explosions.dm +++ b/code/__DEFINES/combat/explosions.dm @@ -22,10 +22,9 @@ #define LEGACY_EXPLOSION_SEVERE_POWER EXPLOSION_CONSTANT_SEVERE #define LEGACY_EXPLOSION_MINOR_POWER EXPLOSION_CONSTANT_MINOR -#define LEGACY_EXPLOSION_DEVASTATE_INTEGRITY 1000 +#define LEGACY_EXPLOSION_DEVASTATE_INTEGRITY 500 #define LEGACY_EXPLOSION_SEVERE_INTEGRITY 180 #define LEGACY_EXPLOSION_MINOR_INTEGRITY 50 -#define LEGACY_EXPLOSION_INTEGRITY_MULT (0.01 * rand(50, 200)) // why the extra numbers? so if someone does weird math we don't out of bounds GLOBAL_REAL(_legacy_expowers, /list) = list( @@ -47,7 +46,6 @@ GLOBAL_REAL(_legacy_ex_atom_damage, /list) = list( 0 ) -#define LEGACY_EXPLOSION_ATOM_DAMAGE(P) (global._legacy_ex_atom_damage[P] * LEGACY_EXPLOSION_INTEGRITY_MULT) // this works out becuase epxlosions are 1-3 in legacy, so we can just use it as list indices #define LEGACY_EX_ACT(ATOM, POWER, TARGET) ATOM.legacy_ex_act(POWER, TARGET); ATOM.ex_act(_legacy_expowers[POWER]); diff --git a/code/__DEFINES/combat/shieldcall.dm b/code/__DEFINES/combat/shieldcall.dm index 889d27cad26f..965076dd8566 100644 --- a/code/__DEFINES/combat/shieldcall.dm +++ b/code/__DEFINES/combat/shieldcall.dm @@ -1,44 +1,198 @@ //* This file is explicitly licensed under the MIT license. *// //* Copyright (c) 2023 Citadel Station developers. *// -//? keys for list/additional in atom_shieldcall +//* shieldcall return status *// -// None yet +/// terminate; either fully mitigated or we're done here +#define SHIELDCALL_FLAG_TERMINATE (1<<0) +/// terminate attacker swing entirely +/// +/// * usually you don't want this +#define SHIELDCALL_FLAG_CANCEL_SWING (1<<1) +/// stop attack effects +/// +/// * this is basically the [PROJECTILE_IMPACT_BLOCKED] for shieldcalls +/// * the thing hitting won't do direct damage but aftereffects like exploding rounds still explode +#define SHIELDCALL_FLAG_ATTACK_BLOCKED (1<<2) +/// attack redirected entirely +#define SHIELDCALL_FLAG_ATTACK_REDIRECT (1<<3) +/// attack goes through +/// +/// * both this and REDIRECT should be used if the original attack should keep going +/// * also use SHIELDCALL_FLAG_ATTACK_BLOCKED if original attack shouldn't process! otherwise it might be a pierce. +/// * example: reflecting a bullet +#define SHIELDCALL_FLAG_ATTACK_PASSTHROUGH (1<<4) +/// this attack already invoked a 'specialized' shieldcall proc, and is now invoking +/// the generalized atom_shieldcall() proc. +#define SHIELDCALL_FLAG_SECOND_CALL (1<<5) +/// asks the shieldcall nicely to not make a message +#define SHIELDCALL_FLAG_SUPPRESS_MESSAGE (1<<6) +/// asks the shieldcall nicely to not make a sound +#define SHIELDCALL_FLAG_SUPPRESS_SOUND (1<<7) +/// do not call armorcalls +#define SHIELDCALL_FLAG_SKIP_ARMORCALLS (1<<8) +/// do not call shieldcalls +#define SHIELDCALL_FLAG_SKIP_SHIELDCALLS (1<<9) +/// this is a passive / single parry +#define SHIELDCALL_FLAG_SINGLE_PARRY (1<<10) -//? shieldcall "struct" - this *must* match up with /atom/proc/run/check_shieldcall! +/// these flags mean to stop processing the attack +#define SHIELDCALL_FLAGS_BLOCK_ATTACK (SHIELDCALL_FLAG_ATTACK_BLOCKED) +/// these flags means that the attack should keep going after us, regardless of if we're hit +#define SHIELDCALL_FLAGS_PIERCE_ATTACK (SHIELDCALL_FLAG_ATTACK_PASSTHROUGH) +/// stop shieldcall chain +#define SHIELDCALL_FLAGS_SHOULD_TERMINATE (SHIELDCALL_FLAG_TERMINATE) +/// these flags means something happens / should happen +#define SHIELDCALL_FLAGS_SHOULD_PROCESS (SHIELDCALL_FLAGS_BLOCK_ATTACK | SHIELDCALL_FLAGS_PIERCE_ATTACK) + +/// flags set in a projectile reflect +/// +/// * you should be using /datum/shieldcall's bullet intercept / bullet signals if possible but this works too +#define SHIELDCALL_FLAGS_FOR_PROJECTILE_DEFLECT (SHIELDCALL_FLAG_TERMINATE | SHIELDCALL_FLAG_ATTACK_BLOCKED | SHIELDCALL_FLAG_ATTACK_REDIRECT | SHIELDCALL_FLAG_ATTACK_PASSTHROUGH) +/// flags set in a full block +#define SHIELDCALL_FLAGS_FOR_COMPLETE_BLOCK (SHIELDCALL_FLAG_TERMINATE | SHIELDCALL_FLAG_ATTACK_BLOCKED) + +//* Atom Shieldcall Args *// +//* *// +//* The shieldcall system is a very low-level 'damage instance' interception API. *// +//* It's used by the shieldcalls list in atoms to perform low-level intercepts, *// +//* as well as by default features like armor to perform their damage intercepts. *// +//* *// +//* For speed reasons, shieldcalls pass a single argument list down instead of *// +//* returning new lists every call, as shieldcalls need to be able to edit many *// +//* facets of a damage instance. *// +//* *// +//* Many of these are optional. *// +//* +//* Adding new arguments should be done sparingly. +//* Removing arguments requires every single proc with SHIELDCALL_PROC_HEADER *// +//* to be audited. *// /// damage amount #define SHIELDCALL_ARG_DAMAGE 1 /// damage type -#define SHIELDCALL_ARG_DAMTYPE 2 +#define SHIELDCALL_ARG_DAMAGE_TYPE 2 /// damage tier -#define SHIELDCALL_ARG_TIER 3 +#define SHIELDCALL_ARG_DAMAGE_TIER 3 /// armor flag -#define SHIELDCALL_ARG_FLAG 4 +#define SHIELDCALL_ARG_DAMAGE_FLAG 4 /// damage mode -#define SHIELDCALL_ARG_MODE 5 +#define SHIELDCALL_ARG_DAMAGE_MODE 5 /// attack type -#define SHIELDCALL_ARG_TYPE 6 -/// attacking weapon datum - same as used in armor +#define SHIELDCALL_ARG_ATTACK_TYPE 6 +/// attacking weapon datum +/// +/// * /obj/projectile if projectile +/// * /datum/unarmed_attack if unarmed melee +/// * /obj/item if item melee +/// * /datum/thrownthing if thrown +/// * null if touch #define SHIELDCALL_ARG_WEAPON 7 -/// list for additional data -#define SHIELDCALL_ARG_ADDITIONAL 8 -/// flags returned -#define SHIELDCALL_ARG_RETVAL 9 +/// flags returned from other shieldcalls +#define SHIELDCALL_ARG_FLAGS 8 +/// hit zone; this is usually a bodypart but this is also optional +#define SHIELDCALL_ARG_HIT_ZONE 9 +/// additional list returns; usually empty, but may exist +#define SHIELDCALL_ARG_ADDITIONAL 10 +/// the clickchain of a melee attack +/// +/// * this is passed in so you can grab data like who is doing it / who started the attack. +/// * filled in sometimes but not always if unarmed or item melee. +#define SHIELDCALL_ARG_CLICKCHAIN 11 + +/// A proc header with all the shieldcall args. +/// +/// * We use this so it's easy to check where shieldcall args are being used if shieldcalls need to be refactored. +#define SHIELDCALL_PROC_HEADER damage, damage_type, damage_tier, damage_flag, damage_mode, attack_type, datum/weapon, shieldcall_flags, hit_zone, list/additional, datum/event_args/actor/clickchain/clickchain -//? shieldcall additional data keys +//* list keys for list/additional in atom shieldcalls *// // none yet -//? shieldcall return flags - -/// abort further shieldcalls - hit is totally blocked or mitigated -#define SHIELDCALL_FULLY_MITIGATED (1<<0) -/// hit was partially blocked or mitigated -#define SHIELDCALL_PARTIALLY_MITIGATED (1<<1) -/// attack was forcefully missed e.g. by reactive teleport armor -#define SHIELDCALL_FORCED_MISS (1<<2) -/// fake; this is a check -#define SHIELDCALL_JUST_CHECKING (1<<3) -/// terminate further shieldcalls -#define SHIELDCALL_CEASE (1<<4) +//* Helpers to manipulate shieldcall args *// + +#define RESOLVE_SHIELDCALL_ATTACK_TEXT(SHIELDCALL) resolve_shieldcall_attack_text(SHIELDCALL) + +/proc/resolve_shieldcall_attack_text(list/shieldcall_args) + switch(shieldcall_args[SHIELDCALL_ARG_ATTACK_TYPE]) + if(ATTACK_TYPE_PROJECTILE) + . = shieldcall_args[SHIELDCALL_ARG_WEAPON] + if(ATTACK_TYPE_THROWN) + var/datum/thrownthing/thrown = shieldcall_args[SHIELDCALL_ARG_WEAPON] + if(thrown) + . = "the impact from [thrown.thrownthing]" + if(ATTACK_TYPE_MELEE, ATTACK_TYPE_UNARMED) + . = "the force of the blow" + + if(!.) + . = "the attack" + +#define RESOLVE_SHIELDCALL_WEAPON_DESCRIPTOR(SHIELDCALL) resolve_shieldcall_weapon_descriptor(SHIELDCALL) + +/proc/resolve_shieldcall_weapon_descriptor(list/shieldcall_args) + switch(shieldcall_args[SHIELDCALL_ARG_ATTACK_TYPE]) + if(ATTACK_TYPE_MELEE) + var/obj/item/weapon = shieldcall_args[SHIELDCALL_ARG_WEAPON] + return "a [weapon]" + if(ATTACK_TYPE_PROJECTILE) + var/obj/projectile/proj = shieldcall_args[SHIELDCALL_ARG_WEAPON] + return "a [proj]" + if(ATTACK_TYPE_THROWN) + var/datum/thrownthing/thrown = shieldcall_args[SHIELDCALL_ARG_WEAPON] + return "a [thrown.thrownthing]" + if(ATTACK_TYPE_MELEE, ATTACK_TYPE_UNARMED) + var/datum/unarmed_attack/style = shieldcall_args[SHIELDCALL_ARG_WEAPON] + return "a [style.attack_name]" + if(!.) + . = "an attack" + +//* handle_touch - contact_flags *//. + +// broad descriptors + +/// generally helpful actions (shake up, etc) +#define SHIELDCALL_CONTACT_FLAG_HELPFUL (1<<0) +/// potentially helpful but also potentially harmful actions +#define SHIELDCALL_CONTACT_FLAG_NEUTRAL (1<<1) +/// harmful actions +#define SHIELDCALL_CONTACT_FLAG_HARMFUL (1<<2) + +// categories + +/// medical items/techniques +#define SHIELDCALL_CONTACT_FLAG_MEDICAL (1<<23) + +//* handle_touch - contact_specific *// + +/// trying to inject someone +#define SHIELDCALL_CONTACT_SPECIFIC_SYRINGE_INJECTION "inject" +/// trying to shake someone up +#define SHIELDCALL_CONTACT_SPECIFIC_SHAKE_UP "help" +/// trying to drag someone +#define SHIELDCALL_CONTACT_SPECIFIC_PULL "pull" +/// trying to grab someone, **or** intensify a grab +#define SHIELDCALL_CONTACT_SPECIFIC_GRAB "grab" +/// trying to perform a surgery step - generic +#define SHIELDCALL_CONTACT_SPECIFIC_SURGERY "surgery" +/// being sprayed with chemicals +#define SHIELDCALL_CONTACT_SPECIFIC_CHEMICAL_SPRAY "spray" + +//* handle_touch - helpers *// + +/** + * Gets text to put in say, "blocks \the [text]" when someone has a blocked touch. + */ +#define RESOLVE_SHIELDCALL_TOUCH_TEXT(CONTACT_FLAGS, CONTACT_SPECIFIC) resolve_shieldcall_touch_text(CONTACT_FLAGS, CONTACT_SPECIFIC) + +/proc/resolve_shieldcall_touch_text(flags, specific) + switch(specific) + if(SHIELDCALL_CONTACT_SPECIFIC_SYRINGE_INJECTION) + return "syringe" + if(SHIELDCALL_CONTACT_SPECIFIC_SHAKE_UP) + return "help-up" + if(SHIELDCALL_CONTACT_SPECIFIC_GRAB) + return "grab" + if(SHIELDCALL_CONTACT_SPECIFIC_SURGERY) + return "operation" + if(SHIELDCALL_CONTACT_SPECIFIC_CHEMICAL_SPRAY) + return "spray" diff --git a/code/__DEFINES/datums/event_args.dm b/code/__DEFINES/datums/event_args.dm new file mode 100644 index 000000000000..2fa589bf4e6f --- /dev/null +++ b/code/__DEFINES/datums/event_args.dm @@ -0,0 +1,5 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +// make sure a var that is either event_args/actor or a single mob/user is event args; if it's not +#define E_ARGS_WRAP_USER_TO_ACTOR(USER) USER = ismob(USER)? new /datum/event_args/actor(USER) : USER diff --git a/code/__DEFINES/dcs/flags.dm b/code/__DEFINES/dcs/flags.dm index 47695369dcd7..f69925f96982 100644 --- a/code/__DEFINES/dcs/flags.dm +++ b/code/__DEFINES/dcs/flags.dm @@ -1,12 +1,10 @@ /// Return this from `/datum/component/Initialize` or `datum/component/OnTransfer` to have the component be deleted if it's applied to an incorrect type. /// `parent` must not be modified if this is to be returned. /// This will be noted in the runtime logs -#define COMPONENT_INCOMPATIBLE (1<<0) -/// Returned in PostTransfer to prevent transfer, similar to `COMPONENT_INCOMPATIBLE` -#define COMPONENT_NOTRANSFER (1<<1) +#define COMPONENT_INCOMPATIBLE -1 /// Return value to cancel attaching -#define ELEMENT_INCOMPATIBLE (1<<0) +#define ELEMENT_INCOMPATIBLE -1 // /datum/element flags /// Causes the detach proc to be called when the host object is being deleted diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_buckling.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-buckling.dm similarity index 60% rename from code/__DEFINES/dcs/signals/signals_atom/signals_atom_buckling.dm rename to code/__DEFINES/dcs/signals/signals_atom/signals_atom-buckling.dm index 29d0bc4c9c04..640048265bb4 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_buckling.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-buckling.dm @@ -1,16 +1,23 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// -////! buckling. flags are always buckling opflags, see __DEFINES/procs/buckling.dm -/// called on mob buclked: (mob, flags, user, semantic) +//* -- Buckling -- *// +//* flags are always buckling opflags *// +//* see __DEFINES/procs/buckling.dm *// + +//* These are called when the buckle/unbuckle op have already happened *// + +/// called on the atom the mob buckled to: (mob, flags, user, semantic) #define COMSIG_MOVABLE_MOB_BUCKLED "mob_buckled" -/// called on mob unbuckled: (mob, flags, user, semantic) +/// called on the atom the mob unbuckled from: (mob, flags, user, semantic) #define COMSIG_MOVABLE_MOB_UNBUCKLED "mob_unbuckled" -//! weird names to be more distinct from movable signals /// called on the mob that just got buckled: (mob, flags, user, semantic) -#define COMSIG_MOB_BUCKLED "buckled" +#define COMSIG_MOB_BUCKLED_TO "buckled" /// called on the mob that just got unbuckled: (mob, flags, user, semantic) -#define COMSIG_MOB_UNBUCKLED "unbuckled" +#define COMSIG_MOB_UNBUCKLED_FROM "unbuckled" + +//* For the below, component_block/force_buckle_operation works to varying degrees on varying procs. *// -//! For the below, component_block/force_buckle_operation works to varying degrees on varying procs. /// called during mob buckling: (mob, flags, user, semantic) #define COMSIG_MOVABLE_PRE_BUCKLE_MOB "pre_buckle_mob" /// called during can buckle mob: (mob, flags, user, semantic) @@ -24,15 +31,18 @@ /// called on mob resist buckle: (mob, semantic). Can't force, can only block. #define COMSIG_MOVABLE_MOB_RESIST_BUCKLE "mob_resist_buckle" /// called during can buckle on mob: (AM, flags, user, semantic, movable_opinion) -#define COMSIG_MOB_CAN_BUCKLE "mob_can_buckle" +#define COMSIG_MOB_CAN_BUCKLE_TO "mob_can_buckle" /// called during can unbuckle on mob: (AM, flags, user, semantic, movable_opinion) -#define COMSIG_MOB_CAN_UNBUCKLE "mob_can_unbuckle" +#define COMSIG_MOB_CAN_UNBUCKLE_FROM "mob_can_unbuckle" /// block mob buckle/unbuckle **silently** - #define COMPONENT_BLOCK_BUCKLE_OPERATION (1<<0) + #define SIGNAL_RAISE_BLOCK_BUCKLE_OPERATION (1<<0) + /// + /// * has priority over [SIGNAL_RAISE_FORCE_BUCKLE_OPERATION] where applicable /// force allow buckle/unbuckled **silently** - #define COMPONENT_FORCE_BUCKLE_OPERATION (1<<1) + #define SIGNAL_RAISE_FORCE_BUCKLE_OPERATION (1<<1) + +//* Interactions; only blocking default interactions work. *// -//! Interactions; only blocking default interactions work. /// called from drag_drop_buckle_interaction: (A, user) #define COMSIG_MOVABLE_DRAG_DROP_BUCKLE_INTERACTION "drag_drop_buckle" /// called from click_unbuckle_interaction: (user) @@ -40,4 +50,4 @@ /// called from resist_unbuckle_interaction(M) #define COMSIG_MOVABLE_RESIST_UNBUCKLE_INTERACTION "resist_unbuckle" /// cancel rest of procs - #define COMPONENT_HANDLED_BUCKLE_INTERACTION (1<<0) + #define SIGNAL_RAISE_BUCKLE_INTERACTION_HANDLED (1<<0) diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_context_system.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-context_system.dm similarity index 95% rename from code/__DEFINES/dcs/signals/signals_atom/signals_atom_context_system.dm rename to code/__DEFINES/dcs/signals/signals_atom/signals_atom-context_system.dm index f6efa7cee461..a5fb0b6a8047 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_context_system.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-context_system.dm @@ -1,5 +1,5 @@ //* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2023 Citadel Station developers. *// +//* Copyright (c) 2024 silicons *// /// from base of /atom/proc/context_query: (list/options, datum/event_args/actor/e_args) /// options list is the same format as /atom/proc/context_query, insert directly to it. diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom-defense.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-defense.dm new file mode 100644 index 000000000000..4d20c82cc267 --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-defense.dm @@ -0,0 +1,37 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) Citadel Station Developers *// + +// todo: integrity signals? + +/// called from bullet_act(): (args) +#define COMSIG_ATOM_BULLET_ACT "bullet_act" + #define BULLET_ACT_ARG_PROJECTILE 1 + /// index of arg for PROJECTILE_IMPACT_* flags + #define BULLET_ACT_ARG_FLAGS 2 + #define BULLET_ACT_ARG_ZONE 3 + #define BULLET_ACT_ARG_EFFICIENCY 4 + +/// called from run_armorcalls(): (list/shieldcall_args, fake_attack) +/// +/// * This is an extremely low-level signal. Handle with care. +#define COMSIG_ATOM_ARMORCALL "atom-armorcalls" +/// called from run_shieldcalls(): (list/shieldcall_args, fake_attack) +/// +/// * This is an extremely low-level signal. Handle with care. +#define COMSIG_ATOM_SHIELDCALL "atom-shieldcalls" + +/// called from atom_shieldcall_handle_*l: (shieldcall_type) +/// +/// * use this for stuff that should spin up a full shield when attacked but is usually inactive. +/// * this is not used for base of /atom_shieldcall(), as it already has a signal! +#define COMSIG_ATOM_SHIELDCALL_ITERATION "atom-shieldcall-iteration" + /// atom_shieldcall_handle_unarmed_melee() + #define ATOM_SHIELDCALL_ITERATING_UNARMED_MELEE (1<<1) + /// atom_shieldcall_handle_item_melee() + #define ATOM_SHIELDCALL_ITERATING_ITEM_MELEE (1<<2) + /// atom_shieldcall_handle_bullet_act() + #define ATOM_SHIELDCALL_ITERATING_BULLET_ACT (1<<3) + /// atom_shieldcall_handle_touch() + #define ATOM_SHIELDCALL_ITERATING_TOUCH (1<<4) + /// atom_shieldcall_handle_throw_impact() + #define ATOM_SHIELDCALL_ITERATING_THROW_IMPACT (1<<5) diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_throwing.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-throwing.dm similarity index 79% rename from code/__DEFINES/dcs/signals/signals_atom/signals_atom_throwing.dm rename to code/__DEFINES/dcs/signals/signals_atom/signals_atom-throwing.dm index 48c615ad3e2f..ae1ba120b00d 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_throwing.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-throwing.dm @@ -1,8 +1,9 @@ -//! Wanna add hitpush signals? TOO BAD, DON'T. Modify the thrownthing datum these pass in! +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// -/// from base of /atom/proc/throw_impacted: (AM, thrownthing) +/// from /atom/movable/proc/_throw_do_hit: (impactor, thrownthing) #define COMSIG_ATOM_THROW_IMPACTED "throw_impacted" -/// from base of /atom/movable/proc/throw_impact: (AM, thrownthing) +/// from /atom/movable/proc/_throw_do_hit: (blocker, thrownthing) #define COMSIG_MOVABLE_THROW_IMPACT "throw_impact" // This set of returns can be for both of the above! /// cancel further actions in this hit @@ -12,9 +13,9 @@ /// completely terminate throw silently immediately. Use if you're deleting the atom. #define COMPONENT_THROW_HIT_TERMINATE (1<<2) -/// called on throws landing on something: (landed_on, thrownthing) +/// from /atom/movable/proc/_throw_finalize: (landed_on, thrownthing) #define COMSIG_MOVABLE_THROW_LAND "throw_land" -/// called on something landing on us from a throw +/// from /atom/movable/proc/_throw_finalize: (landing_movable, thrownthing) #define COMSIG_ATOM_THROW_LANDED "throw_landed" // This set of returns can be for both of the above! /// cancel further actions diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_tool_system.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-tool_system.dm similarity index 91% rename from code/__DEFINES/dcs/signals/signals_atom/signals_atom_tool_system.dm rename to code/__DEFINES/dcs/signals/signals_atom/signals_atom-tool_system.dm index 5d9c7ab9c1de..3bb7af39fc40 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_tool_system.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-tool_system.dm @@ -1,5 +1,5 @@ //* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2023 Citadel Station developers. *// +//* Copyright (c) 2024 silicons *// /// from base of _tool_act: (I, user, function, flags, hint) where I = item, e_args = clickchain data, function = tool behaviour, flags = tool operation flags, hint = set by dynamic tool system /// return CLICKCHAIN_COMPONENT_SIGNAL_HANDLED to abort normal tool_act handling. diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_defense.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_defense.dm deleted file mode 100644 index 89d0a90cfe37..000000000000 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_defense.dm +++ /dev/null @@ -1,5 +0,0 @@ -//* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2023 Citadel Station developers. *// - -// todo: this file left intentionally empty since shieldcalls were moved to datums -// todo: add signals for integrity diff --git a/code/__DEFINES/dcs/signals/signals_item/signals_item_inventory.dm b/code/__DEFINES/dcs/signals/signals_item/signals_item_inventory.dm index 070449036739..77773426f55c 100644 --- a/code/__DEFINES/dcs/signals/signals_item/signals_item_inventory.dm +++ b/code/__DEFINES/dcs/signals/signals_item/signals_item_inventory.dm @@ -1,10 +1,10 @@ -/// From base of obj/item/dropped: (mob/user, flags, atom/newLoc) +/// From base of obj/item/dropped: (mob/user, inv_op_flags, atom/new_loc) #define COMSIG_ITEM_DROPPED "item_drop" #define COMPONENT_ITEM_DROPPED_RELOCATE (1<<0) #define COMPONENT_ITEM_DROPPED_SUPPRESS_SOUND (1<<1) -/// From base of obj/item/pickup: (mob/user, flags, atom/oldLoc) +/// From base of obj/item/pickup: (mob/user, inv_op_flags, atom/old_loc) #define COMSIG_ITEM_PICKUP "item_pickup" -/// From base of obj/item/equipped(): (/mob/equipper, slot, accessory) +/// From base of obj/item/equipped(): (/mob/equipper, slot, inv_op_flags) #define COMSIG_ITEM_EQUIPPED "item_equip" -/// From base of obj/item/unequipped(): (/mob/unequipped, slot, accessory) +/// From base of obj/item/unequipped(): (/mob/unequipped, slot, inv_op_flags) #define COMSIG_ITEM_UNEQUIPPED "item_unequip" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_inventory.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob-inventory.dm similarity index 85% rename from code/__DEFINES/dcs/signals/signals_mob/signals_mob_inventory.dm rename to code/__DEFINES/dcs/signals/signals_mob/signals_mob-inventory.dm index 1aff02f288d3..ffdc89ec7470 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_inventory.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob-inventory.dm @@ -1,3 +1,6 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + /// A mob has just equipped an item. Called on [/mob] from base of [/obj/item/equipped()]: (/obj/item/equipped_item, slot, inv_op_flags) #define COMSIG_MOB_ITEM_EQUIPPED "mob_equipped_item" /// A mob has just unequipped an item. Called on [/mob] from base of [/obj/item/unequipped()]: (/obj/item/equipped_item, slot, inv_op_flags) diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob-perspective.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob-perspective.dm new file mode 100644 index 000000000000..971e9b9a00d6 --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob-perspective.dm @@ -0,0 +1,7 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +/// emitted by reset_perspective, but only if the perspective needed switching: (perspective) +#define COMSIG_MOB_RESET_PERSPECTIVE "reset_perspective" +/// emitted by update_perspective: () +#define COMSIG_MOB_UPDATE_PERSPECTIVE "update_perspective" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_legacy.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_legacy.dm new file mode 100644 index 000000000000..7e43f572821d --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_legacy.dm @@ -0,0 +1,13 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +// todo: burn all this shit with fire + +/// used by passive parry to detect +/// the entire mob item attack system is a dumpster fire and needs rewritten +/// for now, this is a signal with (item, user, hit_zone) +#define COMSIG_MOB_LEGACY_RESOLVE_ITEM_ATTACK "legacy-mob-item-resolve-attack" +/// used by passive parry to detect +/// signal with (user, list/params) +/// :skull: +#define COMSIG_MOB_LEGACY_ATTACK_HAND_INTERCEPT "legacy-mob-legacy-attack-hand" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_perspectiive.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_perspectiive.dm deleted file mode 100644 index ecaa7708fb5e..000000000000 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_perspectiive.dm +++ /dev/null @@ -1,4 +0,0 @@ -/// emitted by reset_perspective, but only if the perspective needed switching: (perspective) -#define COMSIG_MOB_RESET_PERSPECTIVE "reset_perspective" -/// emitted by update_perspective: () -#define COMSIG_MOB_UPDATE_PERSPECTIVE "update_perspective" diff --git a/code/__DEFINES/event_args.dm b/code/__DEFINES/event_args.dm deleted file mode 100644 index cfbbc03c37c3..000000000000 --- a/code/__DEFINES/event_args.dm +++ /dev/null @@ -1,7 +0,0 @@ -//? for /datum/event_args/actor - -#define WRAP_MOB_TO_ACTOR_EVENT_ARGS(VARNAME) VARNAME = ismob(VARNAME)? new /datum/event_args/actor(VARNAME) : VARNAME - -//? for /datum/event_args/actor/clickchain - -#define WRAP_MOB_TO_CLICKCHAIN_EVENT_ARGS(VARNAME) VARNAME = ismob(VARNAME)? new /datum/event_args/actor/clickchain(VARNAME) : VARNAME diff --git a/code/__DEFINES/math.dm b/code/__DEFINES/math.dm index d23d6c4f7d88..4fe4d17dee5e 100644 --- a/code/__DEFINES/math.dm +++ b/code/__DEFINES/math.dm @@ -2,13 +2,22 @@ // This file is quadruple wrapped for your pleasure // ( +/// The value of the mathematical constant 'e' #define NUM_E 2.71828183 - +/// The value of sqrt(2); defined for speed #define SQRT_2 1.414214 - -#define M_PI (3.14159265) -///closer then enough -#define INFINITY (1.#INF) +/// The value of the mathematical constant 'Pi' +#define M_PI 3.14159265 + +/// A quick way to write (1.#INF) +#define INFINITY (1.#INF) + +/// the highest number that does not lose precision when only using the one's place +/// +/// * floating point has serious inaccuracies; after this limit, we can no longer track to one's place +/// * you usually don't have to worry about this if you're not writing anything that requires accuracy +/// * if you are, note that accuracy is lost even below this limit for fractionals. +/// * please look up how IEEE single precision floats work for more details. #define SHORT_REAL_LIMIT 16777216 //"fancy" math for calculating time in ms from tick_usage percentage and the length of ticks @@ -28,13 +37,21 @@ #define SIGN(x) ( (x)!=0 ? (x) / abs(x) : 0 ) /// ceil() +// +// todo: get rid of this, this is native now #define ROUND_UP(x) ( -round(-(x))) /// floor() +// +// todo: get rid of this, this is native now #define ROUND_DOWN(x) (round(x)) -// x to the nearest higher multiple of y +/// x to the nearest higher multiple of y +/// +/// * This is not replaced by native ceil(), as that is always CEILING(x, 1)! #define CEILING(x, y) ( -round(-(x) / (y)) * (y) ) -// x to the nearest lower multiple of y +/// x to the nearest lower multiple of y +/// +/// * This is not replaced by native floor(), as that is always FLOOR(x, 1)! #define FLOOR(x, y) ( round((x) / (y)) * (y) ) // Similar to clamp but the bottom rolls around to the top and vice versa. min is inclusive, max is exclusive @@ -43,16 +60,15 @@ // Real modulus that handles decimals #define MODULUS_F(x, y) ( (x) - (y) * round((x) / (y)) ) -// Cotangent +/// Cotangent #define COT(x) (1 / tan(x)) - -// Secant +/// Secant #define SEC(x) (1 / cos(x)) - -// Cosecant +/// Cosecant #define CSC(x) (1 / sin(x)) // ArcTan2. Returns the degree between two points in an x and y system. +// todo: get rid of this, this is native now /proc/arctantwo(x1,y1,x2,y2) var/dx = x2-x1 var/dy = y2-y1 diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 34d76cb7f739..eb8d894025cf 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -192,14 +192,6 @@ Will print: "/mob/living/carbon/human/death" (you can optionally embed it in a s #define NTOS_EMAIL_NOTIFALREADY 1 #define NTOS_EMAIL_NEWMESSAGE 2 - -// Special return values from bullet_act(). Positive return values are already used to indicate the blocked level of the projectile. -/// If the projectile should continue flying after calling bullet_act() -#define PROJECTILE_CONTINUE -1 -/// If the projectile should treat the attack as a miss (suppresses attack and admin logs) - only applies to mobs. -#define PROJECTILE_FORCE_MISS -2 - - // Vending stuff #define CAT_NORMAL 1 #define CAT_HIDDEN 2 @@ -275,8 +267,6 @@ var/list/economy_station_departments = list( ///The number of deciseconds in a day #define MIDNIGHT_ROLLOVER 864000 -///Needed for the R-UST port -#define PIXEL_MULTIPLIER WORLD_ICON_SIZE/32 /// Maximum effective value of client.view (According to DM references) #define MAX_CLIENT_VIEW 34 diff --git a/code/__DEFINES/procs/clickcode.dm b/code/__DEFINES/procs/clickcode.dm index a064ae31258b..5eaa74894e1a 100644 --- a/code/__DEFINES/procs/clickcode.dm +++ b/code/__DEFINES/procs/clickcode.dm @@ -19,7 +19,9 @@ */ /// stop the click chain from proceeding past this point; usually used if we're deleting or being inserted -/// DO NOT ABUSE THIS PROC TO INTERRUPT AFTERATTACKS WITHOUT CARE; this is NOT what this is here for! +/// +/// * This is an unconditional abort. +/// * DO NOT ABUSE THIS PROC TO INTERRUPT AFTERATTACKS WITHOUT CARE; this is NOT what this is here for! #define CLICKCHAIN_DO_NOT_PROPAGATE (1<<0) /// person can reach us normally #define CLICKCHAIN_HAS_PROXIMITY (1<<1) @@ -38,6 +40,17 @@ #define CLICKCHAIN_DO_NOT_ATTACK (1<<7) /// intercepted by component #define CLICKCHAIN_COMPONENT_SIGNAL_HANDLED (1<<8) +/// this is a reflex counterattack by something +/// +/// * used to prevent loops where both parties reactively attack each other instantly. +#define CLICKCHAIN_REFLEX_COUNTER (1<<9) +/// put this in if we should entirely abort the attack +#define CLICKCHAIN_FULL_BLOCKED (1<<10) + +/// check these for 'unconditional abort' +#define CLICKCHAIN_FLAGS_UNCONDITIONAL_ABORT (CLICKCHAIN_DO_NOT_PROPAGATE) +/// check these for 'abort attack' +#define CLICKCHAIN_FLAGS_ATTACK_ABORT (CLICKCHAIN_DO_NOT_PROPAGATE | CLICKCHAIN_FULL_BLOCKED) //! Reachability Depths - checked from level of DirectAccess and turf adjacency. /// default reachability depth diff --git a/code/__DEFINES/projectiles/projectile.dm b/code/__DEFINES/projectiles/projectile.dm new file mode 100644 index 000000000000..f87cff191198 --- /dev/null +++ b/code/__DEFINES/projectiles/projectile.dm @@ -0,0 +1,119 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +//* pre_impact(), impact(), bullet_act(), on_impact() impact_flags *// +/// pre_impact, bullet_act, on_impact are called in that order /// + +/// pointblank hit +#define PROJECTILE_IMPACT_POINT_BLANK (1<<0) +/// piercing hit; if returned, forces pierce for current impact +/// +/// * projectile has the right to perform special behavior like reducing damage after the impact +#define PROJECTILE_IMPACT_PIERCE (1<<1) +/// was blocked from directly hitting target +/// +/// * on impact probably shouldn't do direct damage, but explosive rounds will explode, etc +#define PROJECTILE_IMPACT_BLOCKED (1<<2) +/// if sensing this flag, **immediately** destroy the projectile without elaboration +/// +/// * overrides everything else +#define PROJECTILE_IMPACT_DELETE (1<<3) +/// do not hit; if this is present, we phase through without interaction +/// +/// * bullet_act(), and on_impact() will be cancelled by this. +/// * on_phase() is called instead to allow for standard hooks to fire +#define PROJECTILE_IMPACT_PHASE (1<<4) +/// signifies that the projectile is reflected. +/// +/// * projectile is not deleted like in PIERCING or PHASE +/// * fires off on_reflect() +#define PROJECTILE_IMPACT_REFLECT (1<<5) +/// we should pass through without interaction +/// +/// * bullet_act(), on_impact(), on_reflect(), and on_phase() will all be cancelled by this. +#define PROJECTILE_IMPACT_PASSTHROUGH (1<<6) +/// instructs piercing projectiles that support this +/// to not reduce damage because the impact was so trivial +/// compared to the force of the projectile +#define PROJECTILE_IMPACT_TRIVIAL (1<<7) +/// aborting duplicate impact due to already being in impacted list of projectile +#define PROJECTILE_IMPACT_DUPLICATE (1<<8) +/// passed from another bullet_act(), +/// like from a target stake to the mounted target +#define PROJECTILE_IMPACT_INDIRECTED (1<<9) +/// used by /impact() on projectile to signal to impact_loop() +/// that the projectile should keep impacting everything on the turf it was trying to hit +#define PROJECTILE_IMPACT_CONTINUE_LOOP (1<<10) +/// target was deleted, stop processing on target side but not projectile side +#define PROJECTILE_IMPACT_TARGET_DELETED (1<<11) +/// this is an impact (usually on the ground) from a projectile expiring +#define PROJECTILE_IMPACT_IS_EXPIRING (1<<12) +/// requests that no sound is made +/// +/// * this is separate from suppression / silencing projectile-side! +/// * notably, this is an absolute that should be always obeyed; while things like special round effects can ignore normal suppression. +#define PROJECTILE_IMPACT_SUPPRESS_SOUND (1<<13) +/// requests that no message is made +/// +/// * this is separate from suppression / silencing projectile-side! +/// * notably, this is an absolute that should be always obeyed; while things like special round effects can ignore normal suppression. +#define PROJECTILE_IMPACT_SUPPRESS_MESSAGE (1<<14) // phasing? +/// do not process damage normally +/// +/// * stops generic 'inflict damage instance' from being procced automatically. +#define PROJECTILE_IMPACT_SKIP_STANDARD_DAMAGE (1<<15) + +/// any of these means the projectile should delete immediately +#define PROJECTILE_IMPACT_FLAGS_SHOULD_DELETE (PROJECTILE_IMPACT_DELETE) +/// any of these means the projectile should not impact +#define PROJECTILE_IMPACT_FLAGS_SHOULD_NOT_HIT (PROJECTILE_IMPACT_REFLECT | PROJECTILE_IMPACT_PHASE | PROJECTILE_IMPACT_PASSTHROUGH) +/// any of these means don't just delete after hit +#define PROJECTILE_IMPACT_FLAGS_SHOULD_GO_THROUGH (PROJECTILE_IMPACT_REFLECT | PROJECTILE_IMPACT_PHASE | PROJECTILE_IMPACT_PASSTHROUGH | PROJECTILE_IMPACT_PIERCE) +/// any of these means the projectile should abort bullet_act +#define PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT (PROJECTILE_IMPACT_DELETE | PROJECTILE_IMPACT_REFLECT | PROJECTILE_IMPACT_PHASE | PROJECTILE_IMPACT_PASSTHROUGH) +/// any of these means the projectile should abort bullet_act, but not on_impact() for the projectile +#define PROJECTILE_IMPACT_FLAGS_TARGET_ABORT (PROJECTILE_IMPACT_DELETE | PROJECTILE_IMPACT_REFLECT | PROJECTILE_IMPACT_PHASE | PROJECTILE_IMPACT_PASSTHROUGH | PROJECTILE_IMPACT_TARGET_DELETED) + +//* projectile_type bitfield *// + +//? base types; all projectiles should have one of these ?// + +/// kinetic matter, basically +#define PROJECTILE_TYPE_KINETIC (1<<0) +/// energy projectiles that aren't a beam +#define PROJECTILE_TYPE_ENERGY (1<<1) +/// particle beam, basically +#define PROJECTILE_TYPE_BEAM (1<<2) + +//? specific types; projectiles may have one or more of these in addition to the above ?// + +/// photonic energy, basically (yes yes lasers are unrealistic i don't care) +#define PROJECTILE_TYPE_PHOTONIC (1<<22) +/// exotic energy or exotic matter +#define PROJECTILE_TYPE_EXOTIC (1<<23) + +//? special types + +/// trace projectile, aka "always let this through shields so stuff knows to fire at it" +#define PROJECTILE_TYPE_TRACE (1<<24) + +DEFINE_BITFIELD_NEW(projectile_types, list( + /obj/projectile = list( + "projectile_type", + ), + /obj/structure/prop/prism = list( + "projectile_type", + "projectile_type_cant", + ), +), list( + BITFIELD_NEW("Base - Kinetic", PROJECTILE_TYPE_KINETIC), + BITFIELD_NEW("Base - Energy", PROJECTILE_TYPE_ENERGY), + BITFIELD_NEW("Base - Beam", PROJECTILE_TYPE_BEAM), + BITFIELD_NEW("Flag - Photonic Energy", PROJECTILE_TYPE_PHOTONIC), + BITFIELD_NEW("Flag - Exotic Energy / Matter", PROJECTILE_TYPE_EXOTIC), +)) + +//* helpers *// + +/// tiles per second to pixels per decisecond +#define PROJECTILE_SPEED_FOR_TPS(tiles) (tiles * WORLD_ICON_SIZE) diff --git a/code/__HELPERS/game/combat/arc.dm b/code/__HELPERS/game/combat/arc.dm new file mode 100644 index 000000000000..e51718e2f66b --- /dev/null +++ b/code/__HELPERS/game/combat/arc.dm @@ -0,0 +1,58 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +/** + * Checks if an attacking atom is within the defensive arc of a defending atom. + * + * * This does not support pixel movement. + * * A null source is always inside defensive arc. + * + * todo: verify behavior. + * + * Attacking entity can be: + * + * * /obj/projectile - treated as projectile + * * /datum/thrownthing - treated as thrown + * * anything else - treated as an /atom-ish source. + * + * @params + * * defending - the defending atom + * * attacking - the attacking entity + * * arc - the arc to check + * * round_up_arc - if the attacking atom is not a projectile or something with angle sim, should we round their defensive angle up or down? + * * use_dir - use this dir, not the defending atom's dir + * + * @return TRUE if they're within arc, FALSE otherwise + */ +/proc/check_defensive_arc_tile(atom/defending, attacking, arc, round_up_arc, use_dir = defending.dir) + // clockwise from north + var/our_angle = dir2angle(use_dir) + // clockwise from north + var/their_angle + if(istype(attacking, /obj/projectile)) + // projectile source + var/obj/projectile/proj = attacking + // projectile angle var is clockwise from north + // turn it around to get the angle from our PoV + their_angle = (proj.angle + 180) % 360 + else + // atom-ish source + var/atom/atom_source + if(istype(attacking, /datum/thrownthing)) + var/datum/thrownthing/thrown = attacking + atom_source = thrown.thrownthing + else if(isatom(attacking)) + atom_source = attacking + else + return TRUE + their_angle = dir2angle(get_dir(defending, atom_source)) + // if we're rounding up our arc, we boost our arc since it's an atom source to nearest 45 + if(round_up_arc) + arc = CEILING(arc, 45) + // normalize it to +- of our angle + their_angle -= our_angle + if(their_angle > 180) + their_angle -= 360 + return abs(their_angle) <= arc + +// todo: pixel movement variant for overmaps and others. diff --git a/code/__HELPERS/lists/string.dm b/code/__HELPERS/lists/string.dm index c6317712253e..2acdf2a131b7 100644 --- a/code/__HELPERS/lists/string.dm +++ b/code/__HELPERS/lists/string.dm @@ -1,8 +1,10 @@ /** * Returns a list in plain english as a string. + * + * * input - (optional) list or null; if null, we use empty_text */ /proc/english_list(list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" ) - var/total = input.len + var/total = length(input) if (!total) return "[nothing_text]" else if (total == 1) diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index f9f5a1e3a709..5851e9a63a31 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -2,7 +2,7 @@ return /obj/vehicle/get_mob() - return occupants + return SAFEPICK(occupants) /obj/vehicle_old/train/get_mob() return SAFEPICK(buckled_mobs) diff --git a/code/__HELPERS/type2type/type2type.dm b/code/__HELPERS/type2type/type2type.dm index ca1a90903354..5b82e5f88c12 100644 --- a/code/__HELPERS/type2type/type2type.dm +++ b/code/__HELPERS/type2type/type2type.dm @@ -133,7 +133,9 @@ return 10 /** - * Converts an angle (degrees) into an ss13 direction. + * Gets the direction of an angle, in degrees, that is clockwise of north + * + * todo: verify math and do documentation * * @params * * degree - angle clockwise of north @@ -154,10 +156,12 @@ return SOUTHWEST if (degree < 315) return WEST - return NORTH|WEST + return NORTHWEST /** - * Returns the north-zero clockwise angle in degrees, given a direction. + * Gets the angle, in degrees, clockwise of north of a direction + * + * @return angle */ /proc/dir2angle(direction) switch (direction) diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index cbfb0473ff91..20b933d326dc 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -997,7 +997,7 @@ return FALSE if(/obj/item/pickaxe/plasmacutter) return 3800 - if(/obj/item/melee/energy) + if(/obj/item/melee/transforming/energy) return 3500 else return FALSE diff --git a/code/__global_init.dm b/code/__global_init.dm index 97bb1f766de0..b9b52380fb0d 100644 --- a/code/__global_init.dm +++ b/code/__global_init.dm @@ -33,3 +33,15 @@ var/datum/world_debug_enabler/world_debug_enabler = new if (debug_server) LIBCALL(debug_server, "auxtools_init")() enable_debugging() + debug_loop() + +/datum/world_debug_enabler/proc/debug_loop() + set waitfor = FALSE + debug_loop_impl() + +/** + * the sole job of this is keep ticking so the debug server can still do stuff while no clients are conencted + */ +/datum/world_debug_enabler/proc/debug_loop_impl() + while(TRUE) + sleep(world.tick_lag) diff --git a/code/controllers/subsystem/ai_movement.dm b/code/controllers/subsystem/ai_movement.dm index d9540ab26a03..619744ad82fb 100644 --- a/code/controllers/subsystem/ai_movement.dm +++ b/code/controllers/subsystem/ai_movement.dm @@ -83,15 +83,14 @@ SUBSYSTEM_DEF(ai_movement) var/reschedule_delay = being_processed.move(++being_processed.movement_cycle) // check if we are still ticking; if not, we got ejected, so we abort as we don't need to eject or insert again if(buckets[bucket_offset] == being_processed) - // eject; we don't change being_processed.ticking_(next|previous) - if(being_processed.movement_bucket_next == being_processed) - buckets[bucket_offset] = null - else - buckets[bucket_offset] = being_processed.movement_bucket_next - being_processed.movement_bucket_next.movement_bucket_prev = being_processed.movement_bucket_prev - being_processed.movement_bucket_prev.movement_bucket_next = being_processed.movement_bucket_next - if(reschedule_delay) + // eject; we don't change being_processed.ticking_(next|previous) + if(being_processed.movement_bucket_next == being_processed) + buckets[bucket_offset] = null + else + buckets[bucket_offset] = being_processed.movement_bucket_next + being_processed.movement_bucket_next.movement_bucket_prev = being_processed.movement_bucket_prev + being_processed.movement_bucket_prev.movement_bucket_next = being_processed.movement_bucket_next // insert; we now set its ticking_(next|previous) // note that we don't do catchup var/inject_offset = ((now_index_raw + round(DS2TICKS(reschedule_delay))) % bucket_amount) + 1 diff --git a/code/controllers/subsystem/processing/fastprocess.dm b/code/controllers/subsystem/processing/fastprocess.dm deleted file mode 100644 index 9622e0214692..000000000000 --- a/code/controllers/subsystem/processing/fastprocess.dm +++ /dev/null @@ -1,6 +0,0 @@ -//Fires five times every second. - -PROCESSING_SUBSYSTEM_DEF(fastprocess) - name = "Fast Processing" - wait = 2 - stat_tag = "FP" diff --git a/code/controllers/subsystem/processing/process_20fps.dm b/code/controllers/subsystem/processing/process_20fps.dm new file mode 100644 index 000000000000..3afd5b018d4d --- /dev/null +++ b/code/controllers/subsystem/processing/process_20fps.dm @@ -0,0 +1,7 @@ +/** + * Fires 20 times a second. Kind of on the nose, huh? + */ +PROCESSING_SUBSYSTEM_DEF(process_20fps) + name = "Processing - 20 fps" + wait = 0.5 + stat_tag = "P20" diff --git a/code/controllers/subsystem/processing/process_5fps.dm b/code/controllers/subsystem/processing/process_5fps.dm new file mode 100644 index 000000000000..05dc2d48d895 --- /dev/null +++ b/code/controllers/subsystem/processing/process_5fps.dm @@ -0,0 +1,7 @@ +/** + * Fires 5 times a second. Kind of on the nose, huh? + */ +PROCESSING_SUBSYSTEM_DEF(process_5fps) + name = "Processing - 5 FPS" + wait = 2 + stat_tag = "P5" diff --git a/code/controllers/subsystem/throwing.dm b/code/controllers/subsystem/throwing.dm index 007def420e51..edab5fc5e61f 100644 --- a/code/controllers/subsystem/throwing.dm +++ b/code/controllers/subsystem/throwing.dm @@ -390,6 +390,9 @@ SUBSYSTEM_DEF(throwing) /** * get damage scaling - default handling + * + * @params + * * target - (optional) thing being hit */ /datum/thrownthing/proc/get_damage_multiplier(atom/target) if(!resist) diff --git a/code/__HELPERS/datastructs/armor.dm b/code/datums/armor/armor.dm similarity index 96% rename from code/__HELPERS/datastructs/armor.dm rename to code/datums/armor/armor.dm index 929eb064b56a..7465bc2fb1e4 100644 --- a/code/__HELPERS/datastructs/armor.dm +++ b/code/datums/armor/armor.dm @@ -184,6 +184,16 @@ else return 0 +/** + * The big, bad proc that deals with inbound shieldcalls. + */ +/datum/armor/proc/handle_shieldcall(list/shieldcall_args, fake_attack) + shieldcall_args[SHIELDCALL_ARG_DAMAGE] = resultant_damage( + shieldcall_args[SHIELDCALL_ARG_DAMAGE], + shieldcall_args[SHIELDCALL_ARG_DAMAGE_TIER], + shieldcall_args[SHIELDCALL_ARG_DAMAGE_FLAG], + ) + /datum/armor/proc/resultant_damage(damage, tier, flag) switch(flag) if(ARMOR_MELEE) diff --git a/code/datums/callback.dm b/code/datums/callback.dm index 721d050b5c39..2087fd6ef448 100644 --- a/code/datums/callback.dm +++ b/code/datums/callback.dm @@ -70,6 +70,9 @@ if(usr) user = WEAKREF(usr) +/datum/callback/proc/operator""() + return "callback [object] ([ref(object)])[isdatum(object) ? " ([object.type])" : ""] args: \[[english_list(arguments)]\]" + /** * Invoke this callback * @@ -103,6 +106,19 @@ return call(delegate)(arglist(calling_arguments)) return call(object, delegate)(arglist(calling_arguments)) +/** + * Invoke this callback and crash if it sleeps. + * + * * Use when a callback should never sleep, as call() cannot be verified by static analysis. + */ +/datum/callback/proc/invoke_no_sleep(...) + . = CALLBACK_SLEEP_SENTINEL + ASYNC + . = Invoke(arglist(args)) + if(. == CALLBACK_SLEEP_SENTINEL) + . = null + CRASH("Callback [src] slept on a no-sleeping invoke.") + /** * Invoke this callback async (waitfor=false) * diff --git a/code/datums/components/atoms/fishing_spot.dm b/code/datums/components/atoms/fishing_spot.dm index 77e800f7d878..3e43fd412dc4 100644 --- a/code/datums/components/atoms/fishing_spot.dm +++ b/code/datums/components/atoms/fishing_spot.dm @@ -1,12 +1,12 @@ // A thing you can fish in /datum/component/fishing_spot registered_type = /datum/component/fishing_spot - + /// Defines the probabilities and fish availibilty var/datum/fish_source/fish_source /datum/component/fishing_spot/Initialize(configuration) - if(!isatom(parent) || ((. = ..()) & COMPONENT_INCOMPATIBLE)) + if(!isatom(parent) || ((. = ..()) == COMPONENT_INCOMPATIBLE)) return COMPONENT_INCOMPATIBLE if(ispath(configuration, /datum/fish_source)) // Create new one of the given type diff --git a/code/datums/components/gps_signal.dm b/code/datums/components/gps_signal.dm index 5171dce10212..e54553a884b4 100644 --- a/code/datums/components/gps_signal.dm +++ b/code/datums/components/gps_signal.dm @@ -43,7 +43,7 @@ GLOBAL_LIST_EMPTY(gps_transmitters) var/registered = FALSE /datum/component/gps_signal/Initialize(gps_tag = "COM0", disabled = FALSE) - if(!isatom(parent) || ((. = ..()) & COMPONENT_INCOMPATIBLE)) + if(!isatom(parent) || ((. = ..()) == COMPONENT_INCOMPATIBLE)) return COMPONENT_INCOMPATIBLE src.gps_tag = gps_tag src.disabled = disabled diff --git a/code/datums/components/horror_aura.dm b/code/datums/components/horror_aura.dm index ea835d879743..45581a602425 100644 --- a/code/datums/components/horror_aura.dm +++ b/code/datums/components/horror_aura.dm @@ -11,7 +11,7 @@ It also serves the purposes of portraying the Lore accurate effect of "Acausal L /datum/component/horror_aura/Initialize(radius) if(radius) src.radius = radius - if(. & COMPONENT_INCOMPATIBLE) + if(. == COMPONENT_INCOMPATIBLE) return else if(!istype(parent)) return COMPONENT_INCOMPATIBLE diff --git a/code/datums/components/items/active_parry.dm b/code/datums/components/items/active_parry.dm new file mode 100644 index 000000000000..1a5dc7dadb9e --- /dev/null +++ b/code/datums/components/items/active_parry.dm @@ -0,0 +1,10 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) Citadel Station Developers *// + +/** + * generic parry provider on items + */ +/datum/component/active_parry + registered_type = /datum/component/active_parry + +// todo: default implementation via active defensive hotkey diff --git a/code/datums/components/items/passive_parry.dm b/code/datums/components/items/passive_parry.dm new file mode 100644 index 000000000000..678aa914dfca --- /dev/null +++ b/code/datums/components/items/passive_parry.dm @@ -0,0 +1,348 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) Citadel Station Developers *// + +/** + * Shieldcall used as a listener for [/datum/component/passive_parry] + */ +/datum/shieldcall/bound/passive_parry + expected_type = /datum/component/passive_parry + +/** + * generic parry provider on items + * + * this is effectively the old rng block system + * + * also known as: autoparry. + */ +/datum/component/passive_parry + registered_type = /datum/component/passive_parry + + /// passive parry data + var/datum/passive_parry/parry_data + /// callback to invoke before the parry is initiated + /// + /// * must return /datum/parry_frame or null. + /// * if it returns a parry frame, it'll override the frame provided by the passive parry datum + /// * if it returns null, it'll cancel the parry + /// * invoked, if existing, with (obj/item/parent, mob/defending, attack_type, datum/weapon, datum/passive_parry/parry_data) + /// * this allows you to construct a custom parry frame + /// * this will be null'd, not qdel'd, when the component is qdel'd + var/datum/callback/parry_intercept + /// our registered shieldcall + var/datum/shieldcall/bound/passive_parry/hooked_shieldcall + /// the mob we're registered with right now + var/mob/hooked + +/datum/component/passive_parry/Initialize(datum/passive_parry/data, datum/callback/intercept) + . = ..() + if(. == COMPONENT_INCOMPATIBLE) + return + if(!isitem(parent)) + return COMPONENT_INCOMPATIBLE + data = fetch_data(data) + if(!data) + stack_trace("invalid data") + return COMPONENT_INCOMPATIBLE + src.parry_data = data + src.parry_intercept = intercept + +/datum/component/passive_parry/Destroy() + parry_data = null // parry data holds no refs + parry_intercept = null // i'd hope this doesn't hold a ref to us. + return ..() + +/datum/component/passive_parry/proc/fetch_data(datum/passive_parry/datalike) + if(IS_ANONYMOUS_TYPEPATH(datalike)) + return new datalike + if(ispath(datalike)) + return resolve_passive_parry_data(datalike) + if(istype(datalike)) + return datalike + +/** + * About to start a parry. Resolve parry_frame datum. + */ +/datum/component/passive_parry/proc/ignite(atom/defending, attack_type, datum/weapon) + RETURN_TYPE(/datum/parry_frame) + if(parry_intercept) + return parry_intercept.invoke_no_sleep(parent, defending, attack_type, weapon, parry_data) + else + var/obj/item/item = parent + return item.passive_parry_intercept(defending, attack_type, weapon, parry_data) + +/datum/component/passive_parry/RegisterWithParent() + . = ..() + RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equipped)) + RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(on_dropped)) + if(!hooked) + var/obj/item/item = parent + if(item.worn_mob()) + on_equipped(item, item.worn_mob(), item.worn_slot) + +/datum/component/passive_parry/UnregisterFromParent() + . = ..() + UnregisterSignal(parent, COMSIG_ITEM_EQUIPPED) + if(hooked) + var/obj/item/item = parent + on_unequipped(item, hooked) + +/datum/component/passive_parry/proc/on_dropped(obj/item/source, inv_op_flags, atom/new_loc) + // delete on drop to save memory + qdel(src) + +/datum/component/passive_parry/proc/on_equipped(obj/item/source, mob/user, slot) + if(!check_slot(slot)) + return + if(hooked) + return + ASSERT(user) + hooked = user + hooked_shieldcall = new(src) + user.register_shieldcall(hooked_shieldcall) + RegisterSignal(user, COMSIG_ATOM_SHIELDCALL_ITERATION, PROC_REF(shieldcall_iterating)) + +/datum/component/passive_parry/proc/on_unequipped(obj/item/source, mob/user) + ASSERT(user == hooked) + hooked = null + user.unregister_shieldcall(hooked_shieldcall) + QDEL_NULL(hooked_shieldcall) + UnregisterSignal(user, COMSIG_ATOM_SHIELDCALL_ITERATION, PROC_REF(shieldcall_iterating)) + +/datum/component/passive_parry/proc/check_slot(slot_id) + return islist(parry_data.parry_slot_id)? (slot_id in parry_data.parry_slot_id) : (!parry_data.parry_slot_id || (parry_data.parry_slot_id == slot_id)) + +/datum/component/passive_parry/proc/shieldcall_iterating(mob/source, shieldcall_type) + SIGNAL_HANDLER + ASSERT(source == hooked) + var/datum/passive_parry/data = fetch_data(parry_data) + // normal shieldcall handlers handle it + if(!data.parry_frame_simulated) + return + var/datum/parry_frame/resolved = ignite(source) + if(!resolved) + return + // for now, we only care about if they already have a frame + // in the future, maybe this can fire as long as we aren't the source of a parry frame on them. + // todo: cooldown enforcement + // todo: mobility enforcement + // todo: full parry swing cycle? + if(!source.GetComponent(/datum/component/parry_frame)) + source.AddComponent(/datum/component/parry_frame, resolved, data.parry_frame_timing) + +//* Bindings - Bullet *// + +/datum/shieldcall/bound/passive_parry/handle_bullet(atom/defending, shieldcall_returns, fake_attack, list/bullet_act_args) + // todo: no support for fake attacks yet + if(fake_attack) + return + // this is a definite 'do as i say, not as i do' moment + // this works because the proc names and args and types are **exactly** matching + // this is why the procs are all together + // do NOT try this at home. + return bound:handle_bullet(arglist(args)) + +/datum/component/passive_parry/proc/handle_bullet(atom/defending, shieldcall_returns, fake_attack, list/bullet_act_args) + var/datum/passive_parry/data = parry_data + if(data.parry_frame_simulated) + return + if(!prob(isnull(data.parry_chance_projectile) ? data.parry_chance_default : data.parry_chance_projectile)) + return + if(!check_defensive_arc_tile(defending, bullet_act_args[BULLET_ACT_ARG_PROJECTILE], data.parry_arc, !data.parry_arc_round_down)) + return + // - Projectile-specific - + var/obj/projectile/proj = bullet_act_args[BULLET_ACT_ARG_PROJECTILE] + if(!(proj.projectile_type & data.parry_projectile_types)) + return + // - End - + var/datum/parry_frame/resolved = ignite(defending, ATTACK_TYPE_PROJECTILE, bullet_act_args[BULLET_ACT_ARG_PROJECTILE]) + if(!resolved) + return + return resolved.handle_bullet(defending, shieldcall_returns | SHIELDCALL_FLAG_SINGLE_PARRY, fake_attack, data.parry_frame_efficiency, bullet_act_args, parent) + +//* Bindings - Melee *// + +/datum/shieldcall/bound/passive_parry/handle_item_melee(atom/defending, shieldcall_returns, fake_attack, obj/item/weapon, datum/event_args/actor/clickchain/e_args) + // todo: no support for fake attacks yet + if(fake_attack) + return + // this is a definite 'do as i say, not as i do' moment + // this works because the proc names and args and types are **exactly** matching + // this is why the procs are all together + // do NOT try this at home. + return bound:handle_item_melee(arglist(args)) + +/datum/component/passive_parry/proc/handle_item_melee(atom/defending, shieldcall_returns, fake_attack, obj/item/weapon, datum/event_args/actor/clickchain/e_args) + var/datum/passive_parry/data = parry_data + if(data.parry_frame_simulated) + return + if(!prob(isnull(data.parry_chance_melee) ? data.parry_chance_default : data.parry_chance_melee)) + return + if(!check_defensive_arc_tile(defending, e_args.performer, data.parry_arc, !data.parry_arc_round_down)) + return + var/datum/parry_frame/resolved = ignite(defending, ATTACK_TYPE_MELEE, weapon) + if(!resolved) + return + return resolved.handle_item_melee(defending, shieldcall_returns | SHIELDCALL_FLAG_SINGLE_PARRY, fake_attack, data.parry_frame_efficiency, weapon, e_args, parent) + +/datum/shieldcall/bound/passive_parry/handle_unarmed_melee(atom/defending, shieldcall_returns, fake_attack, datum/unarmed_attack/style, datum/event_args/actor/clickchain/e_args) + // todo: no support for fake attacks yet + if(fake_attack) + return + // this is a definite 'do as i say, not as i do' moment + // this works because the proc names and args and types are **exactly** matching + // this is why the procs are all together + // do NOT try this at home. + return bound:handle_unarmed_melee(arglist(args)) + +/datum/component/passive_parry/proc/handle_unarmed_melee(atom/defending, shieldcall_returns, fake_attack, datum/unarmed_attack/style, datum/event_args/actor/clickchain/e_args) + var/datum/passive_parry/data = parry_data + if(data.parry_frame_simulated) + return + if(!prob(isnull(data.parry_chance_melee) ? data.parry_chance_default : data.parry_chance_melee)) + return + if(!check_defensive_arc_tile(defending, e_args.performer, data.parry_arc, !data.parry_arc_round_down)) + return + var/datum/parry_frame/resolved = ignite(defending, ATTACK_TYPE_UNARMED, style) + if(!resolved) + return + return resolved.handle_unarmed_melee(defending, shieldcall_returns | SHIELDCALL_FLAG_SINGLE_PARRY, fake_attack, data.parry_frame_efficiency, style, e_args, parent) + +/datum/shieldcall/bound/passive_parry/handle_touch(atom/defending, shieldcall_returns, fake_attack, datum/event_args/actor/clickchain/e_args, contact_flags, contact_specific) + // todo: no support for fake attacks yet + if(fake_attack) + return + // this is a definite 'do as i say, not as i do' moment + // this works because the proc names and args and types are **exactly** matching + // this is why the procs are all together + // do NOT try this at home. + return bound:handle_touch(arglist(args)) + +/datum/component/passive_parry/proc/handle_touch(atom/defending, shieldcall_returns, fake_attack, datum/event_args/actor/clickchain/e_args, contact_flags, contact_specific) + var/datum/passive_parry/data = parry_data + if(data.parry_frame_simulated) + return + if(!prob(isnull(data.parry_chance_touch) ? data.parry_chance_default : data.parry_chance_touch)) + return + if(!check_defensive_arc_tile(defending, e_args.performer, data.parry_arc, !data.parry_arc_round_down)) + return + var/datum/parry_frame/resolved = ignite(defending, ATTACK_TYPE_TOUCH, null) + if(!resolved) + return + return resolved.handle_touch(defending, shieldcall_returns | SHIELDCALL_FLAG_SINGLE_PARRY, fake_attack, data.parry_frame_efficiency, e_args, contact_flags, contact_specific, parent) + +//* Bindings - Thrown *// + +/datum/shieldcall/bound/passive_parry/handle_throw_impact(atom/defending, shieldcall_returns, fake_attack, datum/thrownthing/thrown) + // todo: no support for fake attacks yet + if(fake_attack) + return + // this is a definite 'do as i say, not as i do' moment + // this works because the proc names and args and types are **exactly** matching + // this is why the procs are all together + // do NOT try this at home. + return bound:handle_throw_impact(arglist(args)) + +/datum/component/passive_parry/proc/handle_throw_impact(atom/defending, shieldcall_returns, fake_attack, datum/thrownthing/thrown) + var/datum/passive_parry/data = parry_data + if(data.parry_frame_simulated) + return + if(!prob(isnull(data.parry_chance_thrown) ? data.parry_chance_default : data.parry_chance_thrown)) + return + if(!check_defensive_arc_tile(defending, thrown, data.parry_arc, !data.parry_arc_round_down)) + return + var/datum/parry_frame/resolved = ignite(defending, ATTACK_TYPE_THROWN, thrown) + if(!resolved) + return + return resolved.handle_throw_impact(defending, shieldcall_returns | SHIELDCALL_FLAG_SINGLE_PARRY, fake_attack, data.parry_frame_efficiency, thrown, parent) + +//* Item *// + +/** + * Called by /datum/component/passive_parry when we're about to start up the parry frame + * Called if parry intercept callback isn't set. + * + * @params + * * defending - mob being defended + * * attack_type - (optional) attack type + * * weapon - (optional) the weapon + * * parry_data - (optional) the existing parry data + * + * @return parry frame datum to use, or null to cancel + */ +/obj/item/proc/passive_parry_intercept(mob/defending, attack_type, datum/weapon, datum/passive_parry/parry_data) + return parry_data.parry_frame + +//* Data *// + +GLOBAL_LIST_EMPTY(passive_parry_data) +/** + * get a cached version of a passive paary datum + */ +/proc/resolve_passive_parry_data(datum/passive_parry/datalike) + if(isnull(datalike)) + return + if(IS_ANONYMOUS_TYPEPATH(datalike)) + return new datalike + if(istype(datalike)) + return datalike + if(!GLOB.passive_parry_data[datalike]) + GLOB.passive_parry_data[datalike] = new datalike + return GLOB.passive_parry_data[datalike] + +/** + * datum for holding data on passive parrying + */ +/datum/passive_parry + /// parry chance for harmful melee: [0, 100] + var/parry_chance_melee + /// parry chance for (seemingly) benign melee: [0, 100] + var/parry_chance_touch + /// parry chance for inbound projectile: [0, 100] + var/parry_chance_projectile + /// parry chance for inbound throw + var/parry_chance_thrown + /// default parry chance if one of the above is null + var/parry_chance_default = 0 + + /// passive parry arc + var/parry_arc = 180 + /// passive parry arc should round down for non-projectiles + /// + /// * at 136 (1 more than 135), having this TRUE means non-projectiles can hit them from behind as behind is 180. + /// * at 136 (1 more than 135), having this be FALSE means non-projectiles **cannot** hit them from behind as behind is 180. + var/parry_arc_round_down = TRUE + + /// valid slot ids; null for all, list for multiple, singular for single + var/parry_slot_id = SLOT_ID_HANDS + + /// projectile types we autoparry on + var/parry_projectile_types = ALL + + /// parry frame data to use by default + /// + /// * can be a typepath + /// * can be an anonymous typepath + /// * can be a datum + var/parry_frame = /datum/parry_frame/passive_block + /// simulate a full parry frame with carry-through and duration, or just run the frame once + var/parry_frame_simulated = FALSE + /// if not simulated, what's our efficiency? [0, 1] + /// + /// * only used if not simulated + var/parry_frame_efficiency = 1 + /// if simulated, how far in do we start? [0, infinity] + /// + /// * only used if simulated + var/parry_frame_timing = 0 + +/datum/passive_parry/New() + if(IS_ANONYMOUS_TYPEPATH(parry_frame)) + parry_frame = new parry_frame + else if(ispath(parry_frame)) + parry_frame = new parry_frame + else if(istype(parry_frame, /datum/parry_frame)) + else + CRASH("invalid parry frame") + +/datum/parry_frame/passive_block + parry_can_prevent_contact = TRUE diff --git a/code/datums/components/items/shield_block.dm b/code/datums/components/items/shield_block.dm new file mode 100644 index 000000000000..9e1adc211689 --- /dev/null +++ b/code/datums/components/items/shield_block.dm @@ -0,0 +1,11 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) Citadel Station Developers *// + +/** + * generic shield-like block provider on items + */ +/datum/component/shield_block + registered_type = /datum/component/shield_block + +// todo: default implementation via toggled defensive hotkey +// todo: default 'passive' mode for when you have it held but haven't toggled defensive mode. diff --git a/code/datums/components/items/wielding.dm b/code/datums/components/items/wielding.dm index 1294b37e5b26..96fc1e4bfe77 100644 --- a/code/datums/components/items/wielding.dm +++ b/code/datums/components/items/wielding.dm @@ -1,7 +1,7 @@ // todo: can element this by usign 3 signals instead of 2, one to receive a keybind signal. /datum/component/wielding registered_type = /datum/component/wielding - + /// hands needed var/hands /// lazylist @@ -16,7 +16,7 @@ /datum/component/wielding/Initialize(hands = 2, datum/callback/on_wield, datum/callback/on_unwield) if(!isitem(parent)) return COMPONENT_INCOMPATIBLE - if((. = ..()) & COMPONENT_INCOMPATIBLE) + if((. = ..()) == COMPONENT_INCOMPATIBLE) return src.hands = hands src.on_wield = on_wield @@ -91,6 +91,9 @@ /datum/component/wielding/proc/offhand_destroyed(obj/item/offhand/wielding/I) unwield() + +//* Offhands *// + /obj/item/offhand/wielding name = "wielding offhand" desc = "You shouldn't be able to see this." @@ -103,7 +106,8 @@ host = null return ..() -// item procs +//* Item Hooks *// + /obj/item/proc/on_wield(mob/user, hands) return diff --git a/code/datums/components/mobs/block_frame.dm b/code/datums/components/mobs/block_frame.dm new file mode 100644 index 000000000000..bbbbedd27b3d --- /dev/null +++ b/code/datums/components/mobs/block_frame.dm @@ -0,0 +1,75 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) Citadel Station Developers *// + +/** + * ## Active Defensives + * + * Datastructure for active block on mobs. + * + * * One, or more, may exist at a time; all of them will be shieldcall-registered, be very careful when doing this. + * * Items should generally not allow adding another packet while one is active + */ +/datum/component/block_frame + registered_type = /datum/component/block_frame + + /// active defensive data + var/datum/block_frame/active_block + /// current number of processed hits + var/hit_count = 0 + /// world.time of start + var/start_time + +/datum/shieldcall/bound/block_frame + expected_type = /datum/component/parry_frame + +// todo: default implementation of a hold-down blocking system. + +/** + * Datastructure for block data, now far more simplified. + * + * * Please avoid anonymous typing this where possible, this is a heavy datum and caching helps a lot. + * * The reason this is separate from parrying is because block system is far more focused on exact damage simulation, while parrying is focused on deflecting a hit and handling the effects of that. + * + * todo: this should be a serializable prototype + */ +/datum/block_frame + /// shield arc, in both CW/CCW from user facing direction + /// + /// * given RP doesn't have combat mode, you should really just keep this at 180 + /// * realistically the cutoffs are 45, 90, 135, and 180 for anything that's not a projectile as only those sim physics + var/block_arc = 180 + /// maximum block per attack instance + var/block_damage_max = INFINITY + /// damage block % above minimum + var/block_damage_ratio = 0 + /// damage block minimum + var/block_damage_min = 0 + /// if set, use this armor datum for processing how much damage to block + /// + /// * use this for tiered simulation + /// * [block_damage_max] is the only other variable used for calculations if this is set, all others are on armor already + /// * set to typepath or instance + var/datum/armor/block_via_armor + + /// attack types we are allowed to parry + var/block_attack_types = NONE + + /// if 100% of damage is blocked, do we set SHIELDCALL_BLOCKED and similar flags? + /// + /// * this means things like syringes would be blocked from injecting. + var/block_can_prevent_contact = FALSE + /// always add BLOCKED, even if not 100% mitigated / transmuted + var/block_always_prevents_contact = FALSE + + /// ratio [0, INFINITY] of inbound damage to convert to another type + var/block_transmute = 0 + /// damage type to transmute to + var/block_transmute_type = HALLOSS + /// damage flag the transmuted damage counts as; null = inherit from attack + /// + /// * only used if block_transmute_simulation is on + var/block_transmute_flag = null + /// the transmuted damage is directly applied with full melee sim, instead of just a damage instance + /// + /// * DO NOT TURN THIS ON WITHOUT GOOD REASON. Melee sim is several times more expensive than armor / low-level intercepts for damage instances. + var/block_transmute_simulation = FALSE diff --git a/code/datums/components/mobs/parry_frame.dm b/code/datums/components/mobs/parry_frame.dm new file mode 100644 index 000000000000..d3fd3c85a772 --- /dev/null +++ b/code/datums/components/mobs/parry_frame.dm @@ -0,0 +1,561 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) Citadel Station Developers *// + +/** + * ## Active Parry + * + * Datastructure for active parry on mobs. + * + * * One, or more, may exist at a time; all of them will be shieldcall-registered, be very careful when doing this. + * * Items should generally not allow adding another parry frame while one is active + */ +/datum/component/parry_frame + registered_type = /datum/component/parry_frame + + /// active defensive data + var/datum/parry_frame/active_parry + /// current number of processed hits + var/hit_count = 0 + /// world.time of start + var/start_time + /// world time of drop + var/drop_time + /// registered shieldcall + var/datum/shieldcall/bound/parry_frame/shieldcall + +/** + * * frame - the parry frame + * * kick_time_forwards - start this many deciseconds into the frame. + */ +/datum/component/parry_frame/Initialize(datum/parry_frame/frame, kick_time_forwards) + if(!ismovable(parent)) + return COMPONENT_INCOMPATIBLE + . = ..() + if(. == COMPONENT_INCOMPATIBLE) + return + src.active_parry = frame + src.start_time = world.time - kick_time_forwards + src.drop_time = src.start_time + max(frame.parry_timing_active, frame.parry_timing_perfect) + frame.parry_timing_stop + if(src.drop_time < world.time) + . = COMPONENT_INCOMPATIBLE + CRASH("attempted to start a parry that ended in the past.") + shieldcall = new(src) + shieldcall.tool_text = parent + var/delete_in = src.drop_time - world.time + QDEL_IN(src, delete_in) + // this is non-transferable, duh + new frame.parry_vfx(null, parent, frame) + +/datum/component/parry_frame/Destroy() + . = ..() + // shieldcall must be deleted after unregister + QDEL_NULL(shieldcall) + +/datum/component/parry_frame/RegisterWithParent() + var/atom/movable/AM = parent + AM.register_shieldcall(shieldcall) + +/datum/component/parry_frame/UnregisterFromParent() + var/atom/movable/AM = parent + AM.unregister_shieldcall(shieldcall) + +/datum/component/parry_frame/proc/on_parry(attack_type, datum/weapon, shieldcall_returns, efficiency) + ++hit_count + // check drop + if(hit_count > active_parry.parry_drop_after_hits) + qdel(src) + return + +//* -- Shieldcall -- *// + +/** + * Shieldcall used as a listener for [/datum/component/parry_frame] + */ +/datum/shieldcall/bound/parry_frame + expected_type = /datum/component/parry_frame + + /// text to describe the used tool, if any + var/tool_text + +//* -- Parry Frame -- *// + +/** + * Datastructure for parry data, now far more simplified. + * + * * Please avoid anonymous typing this where possible, this is a heavy datum and caching helps a lot. + * * While this is very close to /datum/block_frame, it is different in separate major ways. + * * Parrying tends to be more powerful and complex, as it's meant to simulate a very dynamic action. + * * Parrying is more expensive to deal with than blocking. + * + * todo: this should be a serializable prototype + * todo: estimated_severity for audiovisuals needs a revisit; sound is not linear. + */ +/datum/parry_frame + //* Arc *// + /// shield arc, in both CW/CCW from user facing direction + /// + /// * given RP doesn't have combat mode, you should really just keep this at 180 + /// * realistically the cutoffs are 45, 90, 135, and 180 for anything that's not a projectile as only those sim physics + var/parry_arc = 180 + /// should we round parry arc down for non-projectiles? + /// + /// * this means 179 can't cover behind us + /// * this is needed because non-projectiles don't have exact angles + var/parry_arc_round_down = TRUE + + //* Timing *// + /// spinup time + /// + /// * keep this at 0 in most cases + /// * the parry does nothing while it's spinning up + /// * parries landing on the tick this ends count as having spun up + /// * overrules both perfect and active timing + var/parry_timing_start = 0 SECONDS + /// perfect time + /// + /// * this is the amount of time we are a perfect parry after tick 0 + /// * this means that this overlaps with [parry_timing_start]! + /// * this is done for performance reasons. + /// * parries landing on the tick this ends still count as perfect + /// * overrules active timing + /// * overruled by start timing + var/parry_timing_perfect = 0 SECONDS + /// no-falloff time + /// + /// * this is the amount of time we are at full [parry_efficiency_active] after tick 0 + /// * this means that this overlaps with both [parry_timing_start] and [parry_timing_perfect]. + /// * this is done for performance reasons. + /// * parries landing on the tick this ends still count as fully active + /// * overruled by start and perfect timing + var/parry_timing_active = 0 SECONDS + /// end time + /// + /// * this is the amount of time we are still active after [parry_timing_active] or [parry_timing_perfect], whatever is longer + /// * efficiency linearly drops from active efficiency to 0 during this time + /// * the parry immediately drops after + /// * parries landing on the tick this ends are dropped + var/parry_timing_stop = 0 SECONDS + + //* Attack Types *// + /// attack types we are allowed to parry + var/parry_attack_types = NONE + + //* Efficiency *// + /// parry efficiency at perfect; [0, 1] + /// + /// * parry efficiency is ratio of damage to block + var/parry_efficiency_perfect = 1 + /// parry efficiency at active; [0, 1] + /// + /// * parry efficiency is ratio of damage to block + var/parry_efficiency_active = 1 + /// minimum efficiency to drop to + var/parry_efficiency_floor = 0 + /// parry efficiency at which we count as a full block + var/parry_efficiency_blocked = 1 + /// parry efficiency at which redirection occurs + var/parry_efficiency_redirection = 1 + + //* Defender Effects *// + /// action-lock the defender while parrying + // todo: implement + var/parry_lock_defender = TRUE + /// drop action-lock on defender when a parry succeeds + // todo: implement + var/parry_free_defender_on_success = TRUE + + //* Configuration *// + /// immediately drop the parry after this many hits + var/parry_drop_after_hits = 1 + + //* Counter Effects *// + /// counterattack on hit + /// + /// * keep this off, this is a good exercise in 'just because you can doesn't mean you should' + // todo: implement + var/parry_counter_attack = FALSE + /// status effects to apply on hit to attacker + /// + /// supports: + /// * /datum/status_effect status effects; associate to duration + /// + /// does not support: + /// * any status effect supertype that isn't listed above. right now, that's grouped and stacking. + var/list/parry_counter_effects + + //* Counter Effects - Projectiles / Vector *// + /// default handling: reflect attack types + /// + /// * yeah you probably shouldn't put anything other than ATTACK_TYPE_PROJECTILE in here. + var/parry_redirect_attack_types = NONE + /// default handling: reflect attack back at attacker + /// + /// * yeah you probably should leave this off + var/parry_redirect_return_to_sender = FALSE + /// redirection arc CW/CCW of angle of incidence + /// + /// * if return_to_sender is off, this is the valid arc from attack source it can be reflected to + /// * if return_to_sender is on, this is the arc in error from attack source we can reflect to + var/parry_redirect_arc = 45 + + //* Defense - Damage *// + /// if 100% of damage is blocked, do we set SHIELDCALL_BLOCKED and similar flags? + /// + /// * this means things like syringes would be blocked from injecting. + var/parry_can_prevent_contact = FALSE + /// always add BLOCKED, even if not 100% mitigated / transmuted + var/parry_always_prevents_contact = FALSE + /// maximum damage blocked per attack instance + // todo: implement + var/parry_damage_max = INFINITY + + //* Defense - Transmute *// + // todo: implement + /// ratio [0, INFINITY] of **blocked** damage to convert to another type + var/parry_transmute = 0 + /// damage type to transmute to; null to default to attacking damage type + var/parry_transmute_type = null + /// damage tier used for transmuted damage; null to default to attacking tier + var/parry_transmute_tier = null + /// damage mode used for transmuted damage; null to default to attacking mode + var/parry_transmute_mode = null + /// damage flag the transmuted damage counts as; null = inherit from attack + var/parry_transmute_flag = null + /// the transmuted damage should be simulated as close to a proper melee hit as possible, + /// instead of just going through run_damage_instance() + /// + /// * DO NOT TURN THIS ON WITHOUT GOOD REASON. Melee sim is several times more expensive than armor / low-level intercepts for damage instances. + /// * Currently does nothing, as we do not have a way to simulate a standard melee hit arbitrarily without side effects. + var/parry_transmute_simulation = FALSE + + //* Defender Cooldown *// + /// hard cooldown to apply to parrying with the thing parrying wth + // todo: implement + var/parry_cooldown_tool = 2 SECONDS + /// hard cooldown to apply to parrying at all as the mob + // todo: implement + var/parry_cooldown_user = 0 SECONDS + /// is the parry cooldown ignored if a successful parry was made + // todo: implement + var/parry_cooldown_on_success = FALSE + + //* Audiovisuals & Feedback *// + /// a sound, or a list of sounds that can be played when we're hit + /// list can be weighted by associated number for relative chance + /// + /// * sound can be a file + /// * sound can be a /datum/soundbyte typepath or instance + var/list/parry_sfx = /datum/soundbyte/grouped/metal_parry + /// a typepath of /atom/movable/parry_frame to use as our visual; this is placed in the defending atom's vis_contents + var/parry_vfx = /atom/movable/render/parry_frame/default + /// "[person] [start_verb] with [item]" + // todo: implement + var/start_verb = "shifts into a defensive stance" + /// "[person] [block_verb] [attack_source_descriptor] with [item]" + var/block_verb = "parries" + /// "[person] [deflect_verb] [attack_source_descriptor] with [item]" + /// + /// * used if an attack was redirected and not just blocked + var/deflect_verb = "deflects" + +/** + * * 0 if in spinup + * * perfect efficiency if in perfect (from 0) + * * active efficiency if in active (from 0) + * * linear falloff to 0 if in spindown (from active time end) + */ +/datum/parry_frame/proc/calculate_parry_efficiency(time_into_parry) + if(time_into_parry < parry_timing_start) + return 0 + if(time_into_parry <= parry_timing_perfect) + return parry_timing_perfect + if(time_into_parry <= parry_timing_active) + return parry_timing_active + var/drop_time = parry_timing_active + parry_timing_stop + if(time_into_parry >= drop_time) + return 0 + var/time_into_drop = time_into_parry - parry_timing_active + return parry_efficiency_active - (parry_efficiency_active - parry_efficiency_floor) * ((time_into_drop) / parry_timing_stop) + +/** + * @params + * * time_into_parry - ds elapsed since parry start + * + * @return TRUE / FALSE + */ +/datum/parry_frame/proc/is_parry_perfect(time_into_parry) + return (time_into_parry >= parry_timing_start) && (time_into_parry <= parry_timing_perfect) + +/** + * Called when parrying something + * + * @params + * * defending - thing being defended against an attack + * * attack_type - (optional) type of attack + * * efficiency - (optional) parry efficiency + * * weapon - (optional) incoming weapon, depends on ATTACK_TYPE + * * shieldcall_flags - (optional) the attack's shieldcall flags + * * severity - (optional) arbitrary 0 to 100 severity of how bad the hit is estimated to be + * * attack_source_descriptor - (optional) text, or an entity to describe the attack. entities will be automatically handled. + * * tool_text - (optional) text to describe the parry tool + */ +/datum/parry_frame/proc/perform_audiovisuals(atom/defending, attack_type, efficiency, datum/weapon, shieldcall_flags, severity = 75, attack_source_descriptor, tool_text) + playsound(defending, (islist(parry_sfx) && length(parry_sfx)) ? pick(parry_sfx) : parry_sfx , severity, TRUE) + new parry_vfx(null, defending, src, shieldcall_flags & SHIELDCALL_FLAG_SINGLE_PARRY) + + var/parry_verb + if((attack_type & parry_redirect_attack_types) && (efficiency >= parry_efficiency_redirection)) + parry_verb = deflect_verb + else + parry_verb = block_verb + + var/attack_descriptor = "the attack" + if(attack_source_descriptor) + // generic processing + attack_descriptor = "\the [attack_source_descriptor]" + // item processing + if(isitem(attack_source_descriptor)) + var/obj/item/item_source_descriptor = attack_source_descriptor + var/mob/mob_holding_item = item_source_descriptor.worn_mob() + if(mob_holding_item) + attack_descriptor = "[mob_holding_item]'s [item_source_descriptor]" + + defending.visible_message( + SPAN_DANGER("[defending] [parry_verb] [attack_descriptor][tool_text && " with \the [tool_text]"]!"), + ) + +/** + * Called when something is parried + * + * @params + * * defending - thing being defended against an attack + * * attack_type - (optional) type of attack + * * efficiency - (optional) parry efficiency + * * weapon - (optional) incoming weapon, depends on ATTACK_TYPE + * * shieldcall_flags - (optional) the attack's shieldcall flags + * * e_args - (optional) for melee, the event args of the attack + * + * @return SHIELDCALL_* flags; these override the caller's! + */ +/datum/parry_frame/proc/perform_aftereffects(atom/defending, attack_type, efficiency, datum/weapon, shieldcall_flags, datum/event_args/actor/clickchain/e_args) + . = shieldcall_flags + + var/atom/movable/aggressor + + // detect aggressor + if(istype(weapon, /obj/projectile)) + var/obj/projectile/weapon_proj = weapon + aggressor = weapon_proj.firer + else if(e_args) + aggressor = e_args.performer + else if(istype(weapon, /datum/thrownthing)) + var/datum/thrownthing/weapon_thrown = weapon + aggressor = weapon_thrown.thrower + + switch(attack_type) + if(ATTACK_TYPE_PROJECTILE) + var/obj/projectile/proj = weapon + if((parry_redirect_attack_types & ATTACK_TYPE_PROJECTILE) && (efficiency >= parry_efficiency_redirection)) + var/outgoing_angle + if(parry_redirect_return_to_sender && aggressor) + outgoing_angle = arctan(aggressor.y - defending.y, aggressor.x - defending.x) + else + // todo: this should be angle of incidence maybe? + outgoing_angle = turn(proj.angle, 180) + rand(-parry_redirect_arc, parry_redirect_arc) + proj.set_angle(outgoing_angle) + . |= SHIELDCALL_FLAG_ATTACK_REDIRECT + + // effects that are only valid if we have a retaliation target + if(aggressor) + if(ismob(aggressor)) + var/mob/aggressor_mob = aggressor + for(var/key in parry_counter_effects) + var/value = parry_counter_effects[key] + if(ispath(key, /datum/status_effect)) + aggressor_mob.apply_status_effect(key, value) + + if(parry_counter_attack) + // RIP AND TEAR + // todo: counterattacks are offline due to clickchain not being entirely nailed down + // we need clickchain flags to be actually checked in active block/parry so that REFLEX_COUNTER flagged clicks don't get re-parried. + pass() + +/** + * Called to transmute an instance of damage into another instance of damage and apply it to the defender. + */ +/datum/parry_frame/proc/perform_transmuted_damage(atom/defending, damage, damage_tier, damage_type, damage_mode, damage_flag, hit_zone, shieldcall_flags) + // todo: parry_transmute_simulation + defending.run_damage_instance( + parry_transmute * damage, + isnull(parry_transmute_type) ? damage_type : parry_transmute_type, + isnull(parry_transmute_tier) ? damage_tier : parry_transmute_tier, + isnull(parry_transmute_flag) ? damage_flag : parry_transmute_flag, + isnull(parry_transmute_mode) ? damage_mode : parry_transmute_mode, + ATTACK_TYPE_DEFENSIVE_PASSTHROUGH, + null, + SHIELDCALL_FLAG_SECOND_CALL, + hit_zone, + ) + +//* Bindings - Bullet *// + +/datum/shieldcall/bound/parry_frame/handle_bullet(atom/defending, shieldcall_returns, fake_attack, list/bullet_act_args) + var/datum/component/parry_frame/frame = bound + if(!(frame.active_parry.parry_attack_types & ATTACK_TYPE_PROJECTILE)) + return + if(!check_defensive_arc_tile(defending, bullet_act_args[BULLET_ACT_ARG_PROJECTILE], frame.active_parry.parry_arc, !frame.active_parry.parry_arc_round_down)) + return + var/efficiency = frame.active_parry.calculate_parry_efficiency(frame.start_time - world.time) + . = frame.active_parry.handle_bullet(defending, shieldcall_returns, fake_attack, efficiency, bullet_act_args, tool_text) + frame.on_parry(ATTACK_TYPE_PROJECTILE, bullet_act_args[BULLET_ACT_ARG_PROJECTILE], ., efficiency) + +/datum/parry_frame/proc/handle_bullet(atom/defending, shieldcall_returns, fake_attack, efficiency, list/bullet_act_args, tool_text) + . = shieldcall_returns + // todo: doesn't take into account any damage randomization + var/obj/projectile/proj = bullet_act_args[BULLET_ACT_ARG_PROJECTILE] + var/estimated_severity = clamp(proj.damage / 20 * 75, 0, 100) + bullet_act_args[BULLET_ACT_ARG_EFFICIENCY] = bullet_act_args[BULLET_ACT_ARG_EFFICIENCY] * clamp(1 - efficiency, 0, 1) + . = perform_aftereffects(defending, ATTACK_TYPE_PROJECTILE, efficiency, proj, .) + perform_audiovisuals(defending, ATTACK_TYPE_PROJECTILE, efficiency, proj, ., estimated_severity, proj, tool_text) + if(. & (SHIELDCALL_FLAG_ATTACK_PASSTHROUGH | SHIELDCALL_FLAG_ATTACK_REDIRECT)) + bullet_act_args[BULLET_ACT_ARG_FLAGS] |= PROJECTILE_IMPACT_REFLECT + if(parry_always_prevents_contact || (parry_can_prevent_contact && (efficiency >= parry_efficiency_blocked))) + bullet_act_args[BULLET_ACT_ARG_FLAGS] |= PROJECTILE_IMPACT_BLOCKED + +//* Bindings - Melee *// + +/datum/shieldcall/bound/parry_frame/handle_item_melee(atom/defending, shieldcall_returns, fake_attack, obj/item/weapon, datum/event_args/actor/clickchain/e_args) + var/datum/component/parry_frame/frame = bound + if(!(frame.active_parry.parry_attack_types & ATTACK_TYPE_MELEE)) + return + if(e_args && !check_defensive_arc_tile(defending, e_args.performer, frame.active_parry.parry_arc, !frame.active_parry.parry_arc_round_down)) + return + var/efficiency = frame.active_parry.calculate_parry_efficiency(frame.start_time - world.time) + . = frame.active_parry.handle_item_melee(defending, shieldcall_returns, fake_attack, efficiency, weapon, e_args, tool_text) + frame.on_parry(ATTACK_TYPE_MELEE, weapon, ., efficiency) + +/datum/parry_frame/proc/handle_item_melee(atom/defending, shieldcall_returns, fake_attack, efficiency, obj/item/weapon, datum/event_args/actor/clickchain/e_args, tool_text) + . = shieldcall_returns + // todo: doesn't take into account any damage randomization + var/estimated_severity = clamp(weapon.damage_force * e_args.damage_multiplier / 20 * 75, 0, 100) + e_args.damage_multiplier *= clamp(1 - efficiency, 0, 1) + . = perform_aftereffects(defending, ATTACK_TYPE_MELEE, efficiency, weapon, ., e_args) + perform_audiovisuals(defending, ATTACK_TYPE_MELEE, efficiency, weapon, ., estimated_severity, weapon, tool_text) + if(parry_always_prevents_contact || (parry_can_prevent_contact && (efficiency >= parry_efficiency_blocked))) + . |= SHIELDCALL_FLAG_ATTACK_BLOCKED + +/datum/shieldcall/bound/parry_frame/handle_unarmed_melee(atom/defending, shieldcall_returns, fake_attack, datum/unarmed_attack/style, datum/event_args/actor/clickchain/e_args) + var/datum/component/parry_frame/frame = bound + if(!(frame.active_parry.parry_attack_types & ATTACK_TYPE_UNARMED)) + return + if(e_args && !check_defensive_arc_tile(defending, e_args.performer, frame.active_parry.parry_arc, !frame.active_parry.parry_arc_round_down)) + return + var/efficiency = frame.active_parry.calculate_parry_efficiency(frame.start_time - world.time) + . = frame.active_parry.handle_unarmed_melee(defending, shieldcall_returns, fake_attack, efficiency, style, e_args, tool_text) + frame.on_parry(ATTACK_TYPE_UNARMED, style, ., efficiency) + +/datum/parry_frame/proc/handle_unarmed_melee(atom/defending, shieldcall_returns, fake_attack, efficiency, datum/unarmed_attack/style, datum/event_args/actor/clickchain/e_args, tool_text) + . = shieldcall_returns + // todo: doesn't take into account any damage randomization + var/estimated_severity = clamp(style.damage * e_args.damage_multiplier / 20 * 75, 0, 100) + e_args.damage_multiplier *= clamp(1 - efficiency, 0, 1) + . = perform_aftereffects(defending, ATTACK_TYPE_UNARMED, efficiency, style, ., e_args) + perform_audiovisuals(defending, ATTACK_TYPE_UNARMED, efficiency, style, ., estimated_severity, style, tool_text) + if(parry_always_prevents_contact || (parry_can_prevent_contact && (efficiency >= parry_efficiency_blocked))) + . |= SHIELDCALL_FLAG_ATTACK_BLOCKED + +/datum/shieldcall/bound/parry_frame/handle_touch(atom/defending, shieldcall_returns, fake_attack, datum/event_args/actor/clickchain/e_args, contact_flags, contact_specific) + var/datum/component/parry_frame/frame = bound + if(!(frame.active_parry.parry_attack_types & ATTACK_TYPE_TOUCH)) + return + if(e_args && !check_defensive_arc_tile(defending, e_args.performer, frame.active_parry.parry_arc, !frame.active_parry.parry_arc_round_down)) + return + var/efficiency = frame.active_parry.calculate_parry_efficiency(frame.start_time - world.time) + . = frame.active_parry.handle_touch(defending, shieldcall_returns, fake_attack, efficiency, e_args, contact_flags, contact_specific, tool_text) + frame.on_parry(ATTACK_TYPE_TOUCH, null, ., efficiency) + +/datum/parry_frame/proc/handle_touch(atom/defending, shieldcall_returns, fake_attack, efficiency, datum/event_args/actor/clickchain/e_args, contact_flags, contact_specific, tool_text) + . = shieldcall_returns + // todo: doesn't take into account any damage randomization + var/estimated_severity = 50 + e_args.damage_multiplier *= clamp(1 - efficiency, 0, 1) + . = perform_aftereffects(defending, ATTACK_TYPE_TOUCH, efficiency, null, ., e_args) + perform_audiovisuals(defending, ATTACK_TYPE_TOUCH, efficiency, null, ., estimated_severity, e_args.performer, tool_text) + if(parry_always_prevents_contact || (parry_can_prevent_contact && (efficiency >= parry_efficiency_blocked))) + . |= SHIELDCALL_FLAG_ATTACK_BLOCKED + +//* Bindings - Thrown *// + +/datum/shieldcall/bound/parry_frame/handle_throw_impact(atom/defending, shieldcall_returns, fake_attack, datum/thrownthing/thrown) + var/datum/component/parry_frame/frame = bound + if(!(frame.active_parry.parry_attack_types & ATTACK_TYPE_THROWN)) + return + if(!check_defensive_arc_tile(defending, thrown, frame.active_parry.parry_arc, !frame.active_parry.parry_arc_round_down)) + return + var/efficiency = frame.active_parry.calculate_parry_efficiency(frame.start_time - world.time) + . = frame.active_parry.handle_throw_impact(defending, shieldcall_returns, fake_attack, efficiency, thrown, tool_text) + frame.on_parry(ATTACK_TYPE_THROWN, thrown, ., efficiency) + +/datum/parry_frame/proc/handle_throw_impact(atom/defending, shieldcall_returns, fake_attack, efficiency, datum/thrownthing/thrown, tool_text) + . = shieldcall_returns + // todo: doesn't take into account any damage randomization + // todo: why isn't thrownthing just with a get_damage() or a better inflict_damage() and get_damage_tuple() idfk man + var/estimated_severity = clamp(thrown.thrownthing.throw_force * thrown.get_damage_multiplier() / 20 * 75, 0, 100) + thrown.damage_multiplier *= clamp(1 - efficiency, 0, 1) + . = perform_aftereffects(defending, ATTACK_TYPE_THROWN, efficiency, thrown, ., thrown.thrownthing, tool_text) + perform_audiovisuals(defending, ATTACK_TYPE_THROWN, efficiency, thrown, ., estimated_severity) + if(parry_always_prevents_contact || (parry_can_prevent_contact && (efficiency >= parry_efficiency_blocked))) + . |= SHIELDCALL_FLAG_ATTACK_BLOCKED + +//* -- VFX Render -- *// + +INITIALIZE_IMMEDIATE(/atom/movable/render/parry_frame) +/** + * A visualizer for a parry frame. + */ +/atom/movable/render/parry_frame + var/atom/movable/bound + /// set this in your custom procs for cycle and single/spinup/spindown + var/qdel_time = 1 SECONDS + +/atom/movable/render/parry_frame/Initialize(mapload, atom/movable/bind_to, datum/parry_frame/frame, single_deflect) + SHOULD_CALL_PARENT(FALSE) + if(!istype(frame)) + . = INITIALIZE_HINT_QDEL + CRASH("no valid frame, this is bad") + src.bound = bind_to + bind_to.vis_contents += src + cycle(frame, single_deflect) + QDEL_IN(src, qdel_time) + return INITIALIZE_HINT_NORMAL + +/atom/movable/render/parry_frame/Destroy() + bound.vis_contents -= src + bound = null + return ..() + +/atom/movable/render/parry_frame/proc/cycle(datum/parry_frame/frame, single_deflect) + if(single_deflect) + single() + return + spinup(frame.parry_timing_start) + addtimer(CALLBACK(src, PROC_REF(spindown), frame.parry_timing_stop), max(frame.parry_timing_active, frame.parry_timing_perfect)) + +/atom/movable/render/parry_frame/proc/single() + return + +/atom/movable/render/parry_frame/proc/spinup(start_time) + return + +/atom/movable/render/parry_frame/proc/spindown(stop_time) + return + +//* -- VFX Default -- *// + +/atom/movable/render/parry_frame/default + icon = 'icons/effects/defensive/main_parry.dmi' + icon_state = "hold" + +/atom/movable/render/parry_frame/default/single() + animate(src, time = 0.3 SECONDS, alpha = 0) + qdel_time = 0.3 SECONDS diff --git a/code/datums/components/movable/spatial_grid.dm b/code/datums/components/movable/spatial_grid.dm index a549c642d1ae..ca650de950ce 100644 --- a/code/datums/components/movable/spatial_grid.dm +++ b/code/datums/components/movable/spatial_grid.dm @@ -16,7 +16,7 @@ /datum/component/spatial_grid/Initialize(datum/spatial_grid/grid) . = ..() - if(. & COMPONENT_INCOMPATIBLE) + if(. == COMPONENT_INCOMPATIBLE) return if(!ismovable(parent)) return COMPONENT_INCOMPATIBLE diff --git a/code/datums/components/riding/riding_filter.dm b/code/datums/components/riding/riding_filter.dm index 464e7c9f7fab..8ab7fcbf0116 100644 --- a/code/datums/components/riding/riding_filter.dm +++ b/code/datums/components/riding/riding_filter.dm @@ -43,7 +43,7 @@ /datum/component/riding_filter/Initialize(handler_typepath) . = ..() - if(. & COMPONENT_INCOMPATIBLE) + if(. == COMPONENT_INCOMPATIBLE) return if(!istype(parent, expected_typepath)) return COMPONENT_INCOMPATIBLE @@ -80,11 +80,11 @@ /datum/component/riding_filter/proc/signal_hook_user_buckle(atom/movable/source, mob/M, flags, mob/user, semantic) SIGNAL_HANDLER_DOES_SLEEP - return check_user_mount(M, flags, user, semantic)? COMPONENT_FORCE_BUCKLE_OPERATION : COMPONENT_BLOCK_BUCKLE_OPERATION + return check_user_mount(M, flags, user, semantic)? SIGNAL_RAISE_FORCE_BUCKLE_OPERATION : SIGNAL_RAISE_BLOCK_BUCKLE_OPERATION /datum/component/riding_filter/proc/signal_hook_pre_buckle(atom/movable/source, mob/M, flags, mob/user, semantic) SIGNAL_HANDLER - return on_mount_attempt(M, flags, user, semantic)? COMPONENT_FORCE_BUCKLE_OPERATION : COMPONENT_BLOCK_BUCKLE_OPERATION + return on_mount_attempt(M, flags, user, semantic)? SIGNAL_RAISE_FORCE_BUCKLE_OPERATION : SIGNAL_RAISE_BLOCK_BUCKLE_OPERATION /datum/component/riding_filter/proc/signal_hook_post_buckle(atom/movable/source, mob/M, flags, mob/user, semantic) SIGNAL_HANDLER @@ -106,7 +106,7 @@ */ /datum/component/riding_filter/proc/signal_hook_can_buckle(atom/movable/source, mob/M, flags, mob/user, semantic) SIGNAL_HANDLER - return check_mount_attempt(M, flags, user, semantic)? COMPONENT_FORCE_BUCKLE_OPERATION : COMPONENT_BLOCK_BUCKLE_OPERATION + return check_mount_attempt(M, flags, user, semantic)? SIGNAL_RAISE_FORCE_BUCKLE_OPERATION : SIGNAL_RAISE_BLOCK_BUCKLE_OPERATION /** * called on buckling process right before point of no return diff --git a/code/datums/components/riding/riding_handler.dm b/code/datums/components/riding/riding_handler.dm index 10230638e7fd..0b260810dbbe 100644 --- a/code/datums/components/riding/riding_handler.dm +++ b/code/datums/components/riding/riding_handler.dm @@ -77,7 +77,7 @@ /datum/component/riding_handler/Initialize() . = ..() - if(. & COMPONENT_INCOMPATIBLE) + if(. == COMPONENT_INCOMPATIBLE) return if(!istype(parent, expected_typepath)) return COMPONENT_INCOMPATIBLE @@ -127,7 +127,7 @@ /datum/component/riding_handler/proc/signal_hook_pre_buckle_mob(atom/movable/source, mob/M, flags, mob/user, semantic) SIGNAL_HANDLER_DOES_SLEEP if(!check_rider(M, semantic, TRUE, user = user)) - return COMPONENT_BLOCK_BUCKLE_OPERATION + return SIGNAL_RAISE_BLOCK_BUCKLE_OPERATION /datum/component/riding_handler/proc/signal_hook_pixel_offset_changed(atom/movable/source) full_update_riders(null, TRUE) diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm index 6909f279a409..c93c2373e7b9 100644 --- a/code/datums/datumvars.dm +++ b/code/datums/datumvars.dm @@ -11,7 +11,12 @@ datum_flags |= DF_VAR_EDITED return TRUE -/datum/proc/vv_get_var(var_name) +/** + * @params + * * var_name - name of the variable + * * resolve - automatically resolve the variable if it's lazy loaded? + */ +/datum/proc/vv_get_var(var_name, resolve) switch(var_name) if ("vars") return debug_variable(var_name, list(), 0, src) diff --git a/code/datums/elements/conflict_checking.dm b/code/datums/elements/conflict_checking.dm index f298d184f017..8c8e35db6982 100644 --- a/code/datums/elements/conflict_checking.dm +++ b/code/datums/elements/conflict_checking.dm @@ -1,5 +1,7 @@ /** * Simple conflict checking for getting number of conflicting things on someone with the same ID. + * + * todo: this is a bit slow innit. */ /datum/element/conflict_checking element_flags = ELEMENT_BESPOKE | ELEMENT_DETACH diff --git a/code/datums/event_args/clickchain.dm b/code/datums/event_args/clickchain.dm index 50858fa0553c..66fc14937bdf 100644 --- a/code/datums/event_args/clickchain.dm +++ b/code/datums/event_args/clickchain.dm @@ -2,6 +2,8 @@ * used to hold data about a click (melee/ranged/other) action * * the click may be real or fake. + * + * * This is required for item swings / interaction, usually, not just base /event_args/actor. */ /datum/event_args/actor/clickchain /// optional: attack intent @@ -11,6 +13,15 @@ /// optional: target atom var/atom/target + //* Attack Data *// + + /// Overall damage multiplier + /// + /// todo: implement; needs slight clickchain/melee overhaul + /// + /// * Allowed to be changed by shieldcalls and other intercepts + var/damage_multiplier = 1 + /datum/event_args/actor/clickchain/New(mob/performer, mob/initiator, atom/target, list/params, intent) ..() src.target = target diff --git a/code/datums/outfits/ghostrole.dm b/code/datums/outfits/ghostrole.dm index 896dc9c57267..7ab69512758a 100644 --- a/code/datums/outfits/ghostrole.dm +++ b/code/datums/outfits/ghostrole.dm @@ -69,13 +69,13 @@ /datum/outfit/pirate/immigrant name = "Pirate - Immigrant" belt = /obj/item/gun/ballistic/pirate - r_pocket = /obj/item/melee/energy/sword/pirate + r_pocket = /obj/item/melee/transforming/energy/sword/cutlass /datum/outfit/pirate/dilettante name = "Pirate - Dilettante" uniform = /obj/item/clothing/under/surplus shoes = /obj/item/clothing/shoes/boots/jackboots - belt = /obj/item/melee/energy/sword/pirate + belt = /obj/item/melee/transforming/energy/sword/cutlass l_hand = /obj/item/shield/makeshift /datum/outfit/pirate/professional @@ -85,5 +85,5 @@ shoes = /obj/item/clothing/shoes/boots/jackboots mask = /obj/item/clothing/mask/balaclava belt = /obj/item/gun/energy/zip - r_pocket = /obj/item/melee/energy/sword/pirate + r_pocket = /obj/item/melee/transforming/energy/sword/cutlass r_hand = /obj/item/shield/makeshift diff --git a/code/datums/outfits/horror_killers.dm b/code/datums/outfits/horror_killers.dm index 87ce4e992701..a1f7e22b70c8 100644 --- a/code/datums/outfits/horror_killers.dm +++ b/code/datums/outfits/horror_killers.dm @@ -41,7 +41,7 @@ gloves = /obj/item/clothing/gloves/black l_ear = /obj/item/radio/headset glasses = /obj/item/clothing/glasses/sunglasses - l_pocket = /obj/item/melee/energy/sword + l_pocket = /obj/item/melee/transforming/energy/sword mask = /obj/item/clothing/mask/gas/clown_hat id_slot = SLOT_ID_WORN_ID diff --git a/code/datums/outfits/outfit.dm b/code/datums/outfits/outfit.dm index e9f5223c5e08..85823242f516 100644 --- a/code/datums/outfits/outfit.dm +++ b/code/datums/outfits/outfit.dm @@ -263,8 +263,8 @@ belt = /obj/item/storage/belt/security/tactical/bandolier l_pocket = /obj/item/cell/device/weapon r_pocket = /obj/item/cell/device/weapon - r_hand = /obj/item/melee/energy/sword/imperial - l_hand = /obj/item/shield/energy/imperial + r_hand = /obj/item/melee/transforming/energy/sword/imperial + l_hand = /obj/item/shield/transforming/energy/imperial suit_store = /obj/item/gun/energy/imperial /datum/outfit/imperial/officer @@ -279,6 +279,6 @@ belt = /obj/item/storage/belt/security/tactical/bandolier l_pocket = /obj/item/cell/device/weapon r_pocket = /obj/item/cell/device/weapon - r_hand = /obj/item/melee/energy/sword/imperial - l_hand = /obj/item/shield/energy/imperial + r_hand = /obj/item/melee/transforming/energy/sword/imperial + l_hand = /obj/item/shield/transforming/energy/imperial suit_store = /obj/item/gun/energy/imperial diff --git a/code/datums/outfits/pirates.dm b/code/datums/outfits/pirates.dm index 3208e284a182..c2a061eb2772 100644 --- a/code/datums/outfits/pirates.dm +++ b/code/datums/outfits/pirates.dm @@ -6,7 +6,7 @@ shoes = /obj/item/clothing/shoes/brown head = /obj/item/clothing/head/bandana glasses = /obj/item/clothing/glasses/eyepatch - l_hand = /obj/item/melee/energy/sword/pirate + l_hand = /obj/item/melee/transforming/energy/sword/cutlass /datum/outfit/pirate/norm @@ -25,7 +25,7 @@ gloves = /obj/item/clothing/gloves/light_brown mask = /obj/item/clothing/mask/breath back = /obj/item/tank/vox - l_hand = /obj/item/melee/energy/sword/pirate + l_hand = /obj/item/melee/transforming/energy/sword/cutlass r_hand = /obj/item/gun/ballistic/shotgun/pump/rifle/vox_hunting l_pocket = /obj/item/ammo_magazine/a7_62mm/clip r_pocket = /obj/item/ammo_magazine/a7_62mm/clip diff --git a/code/datums/position_point_vector.dm b/code/datums/position_point_vector.dm index 6f9121469b92..3babfe0fb1df 100644 --- a/code/datums/position_point_vector.dm +++ b/code/datums/position_point_vector.dm @@ -119,10 +119,13 @@ /** * angle is clockwise from north + * + * @return self */ /datum/point/proc/shift_in_projectile_angle(angle, distance) x += sin(angle) * distance y += cos(angle) * distance + return src /** * doesn't use set base pixel x/y diff --git a/code/datums/shieldcall.dm b/code/datums/shieldcall.dm index 8f66b776107d..37f612450fe7 100644 --- a/code/datums/shieldcall.dm +++ b/code/datums/shieldcall.dm @@ -1,15 +1,43 @@ //* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2023 Citadel Station developers. *// +//* Copyright (c) 2024 silicons *// + +GLOBAL_LIST_EMPTY(cached_shieldcall_datums) + +/** + * return a globally cached shieldcall + * mostly for hardcoded ones that always work a certain way regardless of what it's actually representing + */ +/proc/fetch_cached_shieldcall(path) + if(GLOB.cached_shieldcall_datums[path]) + return GLOB.cached_shieldcall_datums[path] + GLOB.cached_shieldcall_datums[path] = new path + return GLOB.cached_shieldcall_datums[path] /** * Shieldcall handling datums + * + * These are semi-expensive datums that act as hooks + * to intercept arbitrary attacks at the /atom level. + * + * Please be careful with their usage. + * They hook most attacks, and as such are pretty expensive to have on an atom, compute-wise. + * This is pretty much only efficient for single-target; if it's possible, any suppression hook for attacks + * is better than using this on a large number of targets. */ /datum/shieldcall /// priority ; should never change once we're registered on something. lower has higher priority. var/priority = 0 /// goes to mob when in inventory - whether equipped to slot or in hand /// do not modify while applied. it will not un/register properly. + /// + /// * Turn this off if you're doing your own handling. var/shields_in_inventory = TRUE + /// Allow interception of `atom_shieldcall`. + /// + /// * "Yes, I read and understand the terms and conditions" + /// * "Yes, I will handle SHIELDCALL_FLAG_SECOND_CALL to prevent a double-block scenario" + /// * "Yes, I understand that atom_shieldcall is low level and is called in addition to other shieldcall handling procs" + var/low_level_intercept = FALSE /** * sent over from the atom @@ -17,6 +45,127 @@ * @params * * defending - the atom in question * * shieldcall_args - indexed list of shieldcall args. + * * fake_attack - just checking! + * + * @return nothing, because you should be modifying the return flags via shieldcall_args list! + */ +/datum/shieldcall/proc/handle_shieldcall(atom/defending, list/shieldcall_args, fake_attack) + return + +//* Melee Handling *// + +/** + * sent over from the atom + * + * * this is generic pre-intercept for melee; please keep this cheap + * * for stuff like reactive teleport armor, use this because it will stop the hit entirely. + * * for damage modification, inject directly into e_args + * + * @params + * * defending - the atom being attacked + * * shieldcall_returns - existing returns from other shieldcalls + * * fake_attack - just checking! + * * weapon - the item being used to swing with + * * e_args - (optional) the clickchain event, if any; **This is mutable.** + * + * @return SHIELDCALL_FLAG_* flags + */ +/datum/shieldcall/proc/handle_item_melee(atom/defending, shieldcall_returns, fake_attack, obj/item/weapon, datum/event_args/actor/clickchain/e_args) + return NONE + +/** + * sent over from the atom + * + * * this is generic pre-intercept for melee; please keep this cheap + * * for stuff like reactive teleport armor, use this because it will stop the hit entirely. + * * for damage modification, inject directly into e_args + * + * @params + * * defending - the atom in question + * * shieldcall_returns - existing returns from other shieldcalls + * * fake_attack - just checking! + * * style - the unarmed_attack datum being used + * * e_args (optional) the clickchain event, if any; **This is mutable.** + * + * @return SHIELDCALL_FLAG_* flags + */ +/datum/shieldcall/proc/handle_unarmed_melee(atom/defending, shieldcall_returns, fake_attack, datum/unarmed_attack/style, datum/event_args/actor/clickchain/e_args) + return NONE + +//* Interaction Handling *// + +/** + * sent over from the atom + * + * * for generic 'tried to touch' things + * * this is really funny because it lets us do things like teleport the RD on a hug + * + * @params + * * defending - the atom in question + * * shieldcall_returns - existing returns from other shieldcalls + * * fake_attack - just checking! + * * e_args (optional) the clickchain event, if any; **This is mutable.** + * * contact_flags - SHIELDCALL_CONTACT_FLAG_* + * * contact_specific - SHIELDCALL_CONTACT_SPECIFIC_* + * + * @return SHIELDCALL_FLAG_* flags + */ +/datum/shieldcall/proc/handle_touch(atom/defending, shieldcall_returns, fake_attack, datum/event_args/actor/clickchain/e_args, contact_flags, contact_specific) + return NONE + +//* Projectile Handling *// + +/** + * sent over from the atom + * + * * this is pre-intercept for projectiles; please keep this cheap. + * * for stuff like reactive teleport armor, use this because it will stop the hit entirely. + * * passed in bullet act args is mutable. + * * we DO NOT process SHIELDCALL_FLAG flags other than _TERMINATE, because we have direct access to impact_flags of the bullet! + * + * @params + * * defending - the atom in question + * * shieldcall_returns - existing returns from other shieldcalls + * * fake_attack - just checking! + * * bullet_act_args - indexed list of bullet_act args. + * + * @return SHIELDCALL_FLAG_TERMINATE or NONE */ -/datum/shieldcall/proc/handle_shieldcall(atom/defending, list/shieldcall_args) +/datum/shieldcall/proc/handle_bullet(atom/defending, shieldcall_returns, fake_attack, list/bullet_act_args) + return NONE + +//* Throw Handling *// + +/** + * sent over from the atom + * + * * this is pre-intercept for throwns + * * for stuff like reactive teleport armor, use this because it will stop the hit entirely + * + * todo: implement for turf + * todo: implement for obj + * + * @params + * * defending - the thing being hit + * * shieldcall_returns - existing returns from other shieldcalls + * * fake_attack - just checking! + * * thrown - the thrown object's data + * + * @return SHIELDCALL_FLAG_* flags + */ +/datum/shieldcall/proc/handle_throw_impact(atom/defending, shieldcall_returns, fake_attack, datum/thrownthing/thrown) return + +//* Bound Variant *// + +/datum/shieldcall/bound + var/expected_type + var/datum/bound + +/datum/shieldcall/bound/New(datum/binding) + ASSERT(expected_type && istype(binding, expected_type)) + src.bound = binding + +/datum/shieldcall/bound/Destroy() + src.bound = null + return ..() diff --git a/code/datums/soundbytes/effects/combat.dm b/code/datums/soundbytes/effects/combat.dm new file mode 100644 index 000000000000..dd1dd5ef5dd9 --- /dev/null +++ b/code/datums/soundbytes/effects/combat.dm @@ -0,0 +1,47 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +/datum/soundbyte/parry_start + name = "Active Parry (Start)" + is_sfx = TRUE + path = 'sound/soundbytes/effects/combat/parry-cycle.ogg' + +/datum/soundbyte/grouped/metal_parry + name = "Parry (Metal-Metal)" + is_sfx = TRUE + path = list( + 'sound/soundbytes/effects/combat/parry-metal1.ogg', + 'sound/soundbytes/effects/combat/parry-metal2.ogg', + ) + +/datum/soundbyte/grouped/wood_parry + name = "Parry (Wood-Wood)" + is_sfx = TRUE + path = list( + 'sound/soundbytes/effects/combat/parry-wood1.ogg', + 'sound/soundbytes/effects/combat/parry-wood2.ogg', + ) + +/datum/soundbyte/grouped/block_metal_with_wood + name = "Block (Metal-Wood)" + is_sfx = TRUE + path = list( + 'sound/soundbytes/effects/combat/block-metal-on-wood-1.ogg', + 'sound/soundbytes/effects/combat/block-metal-on-wood-2.ogg', + ) + +/datum/soundbyte/grouped/block_metal_with_metal + name = "Block (Metal-Metal)" + is_sfx = TRUE + path = list( + 'sound/soundbytes/effects/combat/block-metal-on-metal-1.ogg', + 'sound/soundbytes/effects/combat/block-metal-on-metal-2.ogg', + ) + +/datum/soundbyte/grouped/block_wood_with_wood + name = "Block (Wood-Wood)" + is_sfx = TRUE + path = list( + 'sound/soundbytes/effects/combat/block-wood-on-wood-1.ogg', + 'sound/soundbytes/effects/combat/block-wood-on-wood-2.ogg', + ) diff --git a/code/datums/status_effects/status_effect.dm b/code/datums/status_effects/status_effect.dm index e0980c2b8def..010e8d88d0c2 100644 --- a/code/datums/status_effects/status_effect.dm +++ b/code/datums/status_effects/status_effect.dm @@ -6,6 +6,9 @@ * * each effect potentially has its own amount of variable arguments that * can be passed into apply_status_effect. they will be detailed per-file. + * + * todo: /datum/prototype/status_effect + * todo: /datum/prototype/status_effect/simple for normal ones. */ /datum/status_effect abstract_type = /datum/status_effect @@ -196,9 +199,11 @@ /** * remove a status effect * + * * will remove grouped effects entirely. + * * @params * * path - path to effect - * * stacks - stacks to remove for grouped and stacking, default is all. + * * stacks - stacks to remove for stacking, default is all. * * @return stacks **left**. for single effects this is probably 0. */ diff --git a/code/datums/unarmed_attack.dm b/code/datums/unarmed_attack.dm index 6179ef081fe7..ff6e629e7b91 100644 --- a/code/datums/unarmed_attack.dm +++ b/code/datums/unarmed_attack.dm @@ -52,6 +52,9 @@ GLOBAL_LIST_EMPTY(unarmed_attack_cache) var/eye_attack_text var/eye_attack_text_victim +/datum/unarmed_attack/proc/operator""() + return pick(attack_verb_legacy) + //* Feedback /datum/unarmed_attack/proc/get_sparring_variant() diff --git a/code/datums/uplink/visible_weapons.dm b/code/datums/uplink/visible_weapons.dm index 04a3acad2112..776ebc499b9e 100644 --- a/code/datums/uplink/visible_weapons.dm +++ b/code/datums/uplink/visible_weapons.dm @@ -17,17 +17,17 @@ /datum/uplink_item/item/visible_weapons/energy_sword name = "Energy Sword, Colorable" item_cost = 40 - path = /obj/item/melee/energy/sword + path = /obj/item/melee/transforming/energy/sword /datum/uplink_item/item/visible_weapons/energy_sword_pirate name = "Energy Cutlass, Colorable" item_cost = 40 - path = /obj/item/melee/energy/sword/pirate + path = /obj/item/melee/transforming/energy/sword/cutlass -/datum/uplink_item/item/visible_weapons/energy_spear - name = "Energy Spear, Colorable" - item_cost = 50 - path = /obj/item/melee/energy/spear +// /datum/uplink_item/item/visible_weapons/energy_spear +// name = "Energy Spear, Colorable" +// item_cost = 50 +// path = /obj/item/melee/transforming/energy/spear /datum/uplink_item/item/visible_weapons/claymore name = "Claymore" diff --git a/code/game/antagonist/outsider/deathsquad.dm b/code/game/antagonist/outsider/deathsquad.dm index 931ae8626d41..26517f966973 100644 --- a/code/game/antagonist/outsider/deathsquad.dm +++ b/code/game/antagonist/outsider/deathsquad.dm @@ -49,7 +49,7 @@ var/datum/antagonist/deathsquad/deathsquad player.equip_to_slot_or_del(new /obj/item/gun/ballistic/revolver/combat(player), SLOT_ID_BELT) player.equip_to_slot_or_del(new /obj/item/gun/energy/pulse_rifle(player), /datum/inventory_slot/abstract/hand/right) player.equip_to_slot_or_del(new /obj/item/hardsuit/ert/assetprotection(player), SLOT_ID_BACK) - player.equip_to_slot_or_del(new /obj/item/melee/energy/sword(player), SLOT_ID_SUIT_STORAGE) + player.equip_to_slot_or_del(new /obj/item/melee/transforming/energy/sword(player), SLOT_ID_SUIT_STORAGE) // player.implant_loyalty() var/obj/item/card/id/id = create_id("Asset Protection", player) diff --git a/code/game/atoms/atom-construction.dm b/code/game/atoms/atom-construction.dm new file mode 100644 index 000000000000..94d85815a35e --- /dev/null +++ b/code/game/atoms/atom-construction.dm @@ -0,0 +1,60 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +//* Deconstruction *// + +/** + * called to semantically deconstruct an atom + * + * @params + * * method - how we were deconstructed + */ +/atom/proc/deconstruct(method = ATOM_DECONSTRUCT_DISASSEMBLED) + SHOULD_NOT_OVERRIDE(TRUE) + + // send signal + // todo: signal + // do da funny logic + deconstructed(method) + // drop things after so things that rely on having objects don't break + drop_products(method, drop_location()) + // goodbye, cruel world + break_apart(method) + +/** + * called to actually destroy ourselves + */ +/atom/proc/break_apart(method) + qdel(src) + +/** + * called when we are deconstructed + * + * **do not drop products in here** + * + * @params + * - method - how we were deconstructed + */ +/atom/proc/deconstructed(method) + return + +/** + * called to drop the products of deconstruction + * + * @params + * * method - how we were deconstructed + * * where - where to drop products; set in base if null to drop_location(). + */ +/atom/proc/drop_products(method, atom/where = drop_location()) + return + +/** + * called to move a product to a place + * + * @params + * * method - how we were deconstructed + * * dropping - movable in question + * * where - where to move to + */ +/atom/proc/drop_product(method, atom/movable/dropping, atom/where) + dropping.forceMove(where || drop_location()) diff --git a/code/game/atoms/atom-damage.dm b/code/game/atoms/atom-damage.dm new file mode 100644 index 000000000000..61ebc475db1c --- /dev/null +++ b/code/game/atoms/atom-damage.dm @@ -0,0 +1,333 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +//* Damage Instance Handling *// + +/** + * standard damage handling - process an instance of damage + * + * args are the same as shieldcall args, because this directly invokes shield/armorcalls + * + * * additional args past the normal shieldcall args are allowed, but not before! + * * the entire args list is extracted via return, allowing for handling by caller. + * * damage is assumed to be zone'd if def_zone is set; otherwise it's overall + * * please note that overall damage generally doesn't check armor properly for speed reasons! + * + * @return modified args + */ +/atom/proc/run_damage_instance(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_OVERRIDE(TRUE) + process_damage_instance(args, hit_zone) + if(shieldcall_flags & SHIELDCALL_FLAGS_BLOCK_ATTACK) + return args.Copy() // args are only valid during a call; it's destroyed after. + inflict_damage_instance(arglist(args)) + return args.Copy() // args are only valid during a call; it's destroyed after. + +/** + * [/atom/proc/run_damage_instance()], but doesn't actually do damage. + * + * @return modified args + */ +/atom/proc/check_damage_instance(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_OVERRIDE(TRUE) + process_damage_instance(args, hit_zone, TRUE) + return args.Copy() // args are only valid during a call; it's destroyed after. + +/** + * process an instance of damage through defense handling. + */ +/atom/proc/process_damage_instance(list/shieldcall_args, filter_zone, fake_attack) + if(!(shieldcall_args[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAG_SKIP_SHIELDCALLS)) + run_shieldcalls(shieldcall_args, fake_attack) + if(shieldcall_args[SHIELDCALL_ARG_FLAGS] & (SHIELDCALL_FLAGS_SHOULD_TERMINATE | SHIELDCALL_FLAGS_BLOCK_ATTACK)) + return + if(!(shieldcall_args[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAG_SKIP_ARMORCALLS)) + run_armorcalls(shieldcall_args, fake_attack, filter_zone) + +/** + * inflict an instance of damage. + * + * * this happens after shieldcalls, armor checks, etc, all resolve. + * * at this point, nothing should modify damage + * * for things like limb damage and armor handling, check the armor/etc in process_damage_instance + * * for this reason, we do not allow any returns. + * * if hit_zone is not specified, this is considered overall damage. + * * overall damage is implementation-defined, so it's recommended to, ironically, not try to standardize that too much. + * * this is pretty much the hand-off proc where damage goes from hit processing / defense checks to the damage system for an entity + * * the damage system can be a medical system or just the atom integrity system. + */ +/atom/proc/inflict_damage_instance(SHIELDCALL_PROC_HEADER) + if(!integrity_enabled) + return + if(inflict_damage_type_special(args)) + return + switch(damage_type) + if(BRUTE) + if(BURN) + else + return // normal atoms can't take non brute-burn damage + // default atom damage handling + inflict_atom_damage( + damage, + damage_type, + damage_tier, + damage_flag, + damage_mode, + hit_zone, + attack_type, + weapon, + ) + +/** + * decodes damage type to what it should actually do + * + * * this is for hybrid / semantic damage types like bio-acid and searing damage to work + * + * @return TRUE to handle the damage type. + */ +/atom/proc/inflict_damage_type_special(list/shieldcall_args) + return FALSE + +//* Damage Processing API *// + +/** + * takes damage from a generic attack, taking into account armor but not shields. + * this does not handle playing sounds / anything, this is strictly generic damage handling + * usable by anything. + * + * * This does **not** invoke the shieldcall API! + * * This does **not** invoke the armor API! + * * This is because damage instance processing should be processing that. + * + * @params + * * damage - raw damage + * * damage_type - (optional) damage type to inflict + * * damage_tier - (optional) resulting damage tier + * * damage_flag - (optional) resulting damage armor flag from [code/__DEFINES/combat/armor.dm] + * * damage_mode - (optional) DAMAGE_MODE_* flags + * * hit_zone - (optional) the zone being hit + * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] + * * weapon - (optional) attacking datum; same format as shieldcall API. See shieldcalls for more information. + * + * @return raw damage taken + */ +/atom/proc/inflict_atom_damage(damage, damage_type, damage_tier, damage_flag, damage_mode, hit_zone, attack_type, datum/weapon) + if(!integrity_enabled) + return 0 + if(integrity_flags & INTEGRITY_INDESTRUCTIBLE) + return 0 + if(!damage) + return 0 + . = integrity + damage_integrity(damage) + . = . - integrity + + +//* Integrity - Direct Manipulation *// + +/** + * damages integrity directly, ignoring armor / shields + * + * @params + * * amount - how much + * * gradual - burst or gradual? if you want to play a sound or something, you usually want to check this. + * * do_not_break - skip calling atom_break + */ +/atom/proc/damage_integrity(amount, gradual, do_not_break) + SHOULD_CALL_PARENT(TRUE) + SHOULD_NOT_SLEEP(TRUE) + var/was_working = integrity > integrity_failure + integrity = max(0, integrity - amount) + if(was_working && integrity <= integrity_failure && !do_not_break) + atom_break() + if(integrity <= 0) + atom_destruction() + +/** + * heals integrity directly + * + * @params + * * amount - how much + * * gradual - burst or gradual? if you want to play a sound or something, you usually want to check this. + * * do_not_fix - skip calling atom_fix + * + * @return amount healed + */ +/atom/proc/heal_integrity(amount, gradual, do_not_fix) + SHOULD_CALL_PARENT(TRUE) + SHOULD_NOT_SLEEP(TRUE) + var/was_failing = integrity <= integrity_failure + . = integrity + integrity = min(integrity_max, integrity + amount) + . = integrity - . + if(was_failing && integrity > integrity_failure && !do_not_fix) + atom_fix() + +/** + * directly sets integrity - ignores armor / sihelds + * + * will not call [damage_integrity] or [heal_integrity] + * will call [atom_break], [atom_fix], [atom_destruction] + * + * @params + * * amount - how much to set to? + */ +/atom/proc/set_integrity(amount) + SHOULD_CALL_PARENT(TRUE) + SHOULD_NOT_SLEEP(TRUE) + var/was_failing = integrity <= integrity_failure + integrity = clamp(amount, 0, integrity_max) + if(!was_failing && integrity <= integrity_failure) + atom_break() + else if(was_failing && integrity > integrity_failure) + atom_fix() + if(integrity <= 0) + atom_destruction() + +/** + * sets max integrity - will automatically reduce integrity if it's above max. + * + * will not call [damage_integrity] + * will call [atom_break], [atom_fix], [atom_destruction] + * + * @params + * * amount - how much to set to + */ +/atom/proc/set_max_integrity(amount) + integrity_max = max(amount, 0) + if(integrity < integrity_max) + return + var/was_broken = integrity <= integrity_failure + integrity = integrity_max + if(!was_broken && (integrity <= integrity_failure)) + atom_break() + if(integrity <= 0) + atom_destruction() + +/** + * sets integrity and max integrity - will automatically reduce integrity if it's above max. + * + * will not call [damage_integrity] + * will call [atom_break], [atom_fix], [atom_destruction] + * + * @params + * * integrity - how much to set integrity to + * * integrity_max - how much to set integrity_max to + */ +/atom/proc/set_full_integrity(integrity, integrity_max) + src.integrity_max = max(integrity_max, 0) + var/was_broken = src.integrity <= integrity_failure + src.integrity = clamp(integrity, 0, integrity_max) + var/now_broken = integrity <= integrity_failure + if(!was_broken && now_broken) + atom_break() + else if(was_broken && !now_broken) + atom_fix() + if(integrity <= 0) + atom_destruction() + +/** + * Set integrity to a multiple of initial. + * + * And fully restore it if specified. + * Otherwise, will retain the last percentage. + */ +/atom/proc/set_multiplied_integrity(factor, restore) + var/was_broken = src.integrity <= integrity_failure + if(restore) + integrity = integrity_max = initial(integrity_max) * factor + if(was_broken && integrity > integrity_failure) + atom_fix() + return + var/ratio = integrity / integrity_max + integrity_max = initial(integrity_max) * factor + integrity = integrity_max * ratio + var/now_broken = integrity <= integrity_failure + if(!was_broken && now_broken) + atom_break() + else if(was_broken && !now_broken) + atom_fix() + if(integrity <= 0) + atom_destruction() + +/** + * adjusts integrity - routes directly to [damage_integrity] and [heal_integrity] + * + * will call [damage_integrity] + * will call [atom_break], [atom_fix], [atom_destruction] + * + * @params + * * amount - how much + * * gradual - burst or gradual? + * * no_checks - do not call fix/break + */ +/atom/proc/adjust_integrity(amount, gradual, no_checks) + SHOULD_CALL_PARENT(TRUE) + SHOULD_NOT_SLEEP(TRUE) + if(amount > 0) + return heal_integrity(amount, gradual, no_checks) + else + return damage_integrity(amount, gradual, no_checks) + +/** + * adjusts max integrity - will automatically reduce integrity if it's above max. + * + * will call [damage_integrity] + * will call [atom_break], [atom_fix], [atom_destruction] + * + * @params + * * amount - how much to adjust by + * * gradual - burst or gradual? + */ +/atom/proc/adjust_max_integrity(amount, gradual) + // lazy route lmao + set_max_integrity(integrity_max + amount) + +//* Integrity - Check / Getters *// + +/** + * percent integrity, rounded. + */ +/atom/proc/percent_integrity(round_to = 0.1) + return integrity_max? round(integrity / integrity_max, round_to) : 0 + +/atom/proc/is_integrity_broken() + return atom_flags & ATOM_BROKEN + +/atom/proc/is_integrity_damaged() + return integrity < integrity_max + +//* Integrity - Events *// + +/** + * called when integrity reaches 0 from a non 0 value + */ +/atom/proc/atom_destruction() + SHOULD_CALL_PARENT(TRUE) + SHOULD_NOT_SLEEP(TRUE) + if(!(integrity_flags & INTEGRITY_NO_DECONSTRUCT)) + deconstruct(ATOM_DECONSTRUCT_DESTROYED) + +/** + * called when integrity drops below or at integrity_failure + * + * if integrity_failure is 0, this is called before destruction. + */ +/atom/proc/atom_break() + SHOULD_CALL_PARENT(TRUE) + SHOULD_NOT_SLEEP(TRUE) + if(integrity > integrity_failure) + damage_integrity(integrity - integrity_failure, do_not_break = TRUE) + atom_flags |= ATOM_BROKEN + +/** + * called when integrity rises above integrity_failure + * + * if integrity_failure is 0, this still works. + */ +/atom/proc/atom_fix() + SHOULD_CALL_PARENT(TRUE) + SHOULD_NOT_SLEEP(TRUE) + if(integrity < integrity_failure) + heal_integrity(integrity_failure - integrity + 1, do_not_fix = TRUE) + atom_flags &= ~ATOM_BROKEN diff --git a/code/game/atoms/atom-defense.dm b/code/game/atoms/atom-defense.dm new file mode 100644 index 000000000000..3f2f7141d7be --- /dev/null +++ b/code/game/atoms/atom-defense.dm @@ -0,0 +1,532 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +//! Welcome to hell. + +// todo: everything needs comsigs comsigs comsigs + +//* External API / Damage Receiving *// + +/** + * todo: implement on most atoms/generic damage system + * todo: replace legacy_ex_act entirely with this + * + * React to being hit by an explosive shockwave + * + * ? Tip for overrides: . = ..() when you want signal to be sent, mdify power before if you need to; to ignore parent + * ? block power, just `return power` in your proc after . = ..(). + * + * @params + * - power - power our turf was hit with + * - direction - DIR_BIT bits; can bwe null if it wasn't a wave explosion!! + * - explosion - explosion automata datum; can be null + * + * @return power after falloff (e.g. hit with 30 power, return 20 to apply 10 falloff) + */ +/atom/proc/ex_act(power, dir, datum/automata/wave/explosion/E) + SHOULD_CALL_PARENT(TRUE) + SEND_SIGNAL(src, COMSIG_ATOM_EX_ACT, power, dir, E) + return power + +/** + * called on melee hit + * + * todo: add clickchain datum, instead of multiplier + * + * * check CLICKCHAIN_FLAGS_* as needed, especially UNCONDITIONAL_ABORT and ATTACK_ABORT + * * clickchain flags are sent down through parent calls. + * + * @params + * * user - person attacking + * * weapon - weapon used + * * target_zone - zone targeted + * * mult - damage multiplier + * + * @return clickchain flags to append + */ +/atom/proc/melee_act(mob/user, obj/item/weapon, target_zone, datum/event_args/actor/clickchain/clickchain) + return CLICKCHAIN_DO_NOT_ATTACK + +/** + * called on unarmed melee hit + * + * todo: add clickchain datum, instead of multiplier + * + * @params + * * user - person attacking + * * style - unarmed attack datum + * * target_zone - zone targeted + * * mult - damage multiplier + * + * @return clickchain flags to append + */ +/atom/proc/unarmed_act(mob/attacker, datum/unarmed_attack/style, target_zone, datum/event_args/actor/clickchain/clickchain) + return CLICKCHAIN_DO_NOT_ATTACK + +/** + * Because this is the proc that calls on_impact(), handling is necessarily + * done in here for a lot of things. + * + * * Overrides should modify proc args directly (e.g. impact_flags) and then call ..() + * if it needs to be taken account by default handling. + * * Overrides should not edit args after ..(), as args are only passed up, not down. + * * Overrides should, for that reason, always call ..() last. + * * This semantically means 'we are **about** to be hit, do anything for special processing'. + * * If you need to delete a projectile on impact, use `on_bullet_act()`; that's called after the contact actually happens. + * + * Things to keep in mind + * * 'efficiency' arg is **extremely** powerful. Please don't lower it to dismal values for no reason. + * * use PROJECTILE_IMPACT_BLOCKED instead of setting efficiency to 0 if an impact is entirely blocked + * * semantically, efficiency 0 means shield from all damages, IMPACT_BLOCKED means it hit something else + * * bullet_act is this way because we don't make a `/datum/event_args/actor/clickchain` with every call, so it needs a way of propagating blocking behavior and impact flags up/down the chain. + * + * @params + * * proj - the projectile + * * impact_flags - PROJECTILE_IMPACT_* flags + * * def_zone - impacting zone; calculated by projectile side, usually + * * efficiency - 0 to 1, inclusive. ratio of effects, including damage, to pass through. + * + * todo: add PROJECTILE_IMPACT_DELETE_AFTER as opposed to DELETE? so rest of effects can still run + * todo: shieldcalls still fire if target aborts without unconditional abort, they should not do that. + * + * @return new impact_flags + */ +/atom/proc/bullet_act(obj/projectile/proj, impact_flags, def_zone, efficiency = 1) + SHOULD_NOT_SLEEP(TRUE) + SHOULD_CALL_PARENT(TRUE) + // lower calls can change flags before we trigger + // check if we're still hitting + if(impact_flags & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return impact_flags + // 0. fire signal + SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, args) + // check if we're still hitting + if(impact_flags & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return impact_flags + // 1. fire shieldcalls + atom_shieldcall_handle_bullet(args, FALSE, NONE) + // check if we're still hitting + if(impact_flags & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return impact_flags + // 2. fire on_bullet_act + impact_flags |= on_bullet_act(proj, impact_flags, args) + if(impact_flags & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return impact_flags + // 3. fire projectile-side on_impact + return proj.on_impact(src, impact_flags, def_zone, efficiency) + +/** + * So, turns out, BYOND passes `args` list **down** on a ..() via arglist(args) equivalent but not back up. + * + * This means that modified bullet act args can't actually propagate through. + * To handle this, we have this function to actually perform the effects of said bullet act. + * + * * This basically semantically means 'we are being hit, do effects for it'. + * * This is called before the projectile-side impact, which is where the damage is usually inflicted. + * * Modify `impact_flags` directly before ..(), and `.` after ..() + * * Check `.` after ..() if it isn't the last call so you know when to abort the processing as needed. + * * Args will propagate **up** (closer to /atom) `..()` calls, but not back down (if base `/atom` changes something you won't get it on your sub-type) + * * For this reason `bullet_act_args` is provided so you can mutably edit it. Do **not** edit the projectile or the impact flags; return the impact flags for automatic addition. + * + * @params + * * proj - hitting projectile; immutable + * * impact_flags - impact flags; immutable. edit directly before ..() call, return edited values after. + * * bullet_act_args - access to the rest of the args in `bullet_act`. Mutable, except for the projectile and the impact flags. + */ +/atom/proc/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + return impact_flags + +//* Hitsound API *// + +// todo: stuff like metal limbs punching walls making special sounds +// todo: this probably needs a rework + +/** + * gets hitsound override. return a value to be fed into playsound, or null for default. + * + * @params + * * damage_type - damage type like brute / burn / etc + * * damage_mode - damage mode for piercing / whatnot + * * attack_type - attack type enum like melee / projectile / thrown / unarmed / etc + * * weapon - attacking /obj/item for melee / thrown, /obj/projectile for ranged, /mob for unarmed + */ +/atom/proc/hitsound_override(damage_type, damage_mode, attack_type, datum/weapon) + return // default is null + +/atom/proc/hitsound_melee(obj/item/I) + . = I.attacksound_override(src, ATTACK_TYPE_MELEE) + if(!isnull(.)) + return + . = hitsound_override(I.damtype, I.damage_mode, ATTACK_TYPE_MELEE, I) + if(.) + return + . = (I.damtype == BURN? hit_sound_burn : hit_sound_brute) || I.attack_sound + if(.) + return + switch(I.damtype) + if(BRUTE) + return "swing_hit" + if(BURN) + return "'sound/items/welder.ogg" + else + return "swing_hit" + +/atom/proc/hitsound_projectile(obj/projectile/P) + //? todo: projectile gets final say + . = hitsound_override(P.damtype, P.damage_mode, ATTACK_TYPE_PROJECTILE, P) + if(.) + return + return islist(P.impact_sounds)? pick(P.impact_sounds) : P.impact_sounds + +/atom/proc/hitsound_throwhit(obj/item/I) + . = I.attacksound_override(src, ATTACK_TYPE_THROWN) + if(!isnull(.)) + return + . = hitsound_override(I.damtype, I.damage_mode, ATTACK_TYPE_THROWN, I) + if(.) + return + . = (I.damtype == BURN? hit_sound_burn : hit_sound_brute) || I.attack_sound + if(.) + return + switch(I.damtype) + if(BRUTE) + return "swing_hit" + if(BURN) + return 'sound/items/welder.ogg' + else + return "swing_hit" + +/atom/proc/hitsound_unarmed(mob/M, datum/unarmed_attack/style) + //? todo: style gets final say + . = hitsound_override(M, style.damage_mode, ATTACK_TYPE_UNARMED, style) + if(.) + return + // todo: way to override this from style side? we don't just want hitsound brute/burn. + . = (style.damage_type == BURN? hit_sound_burn : hit_sound_brute) || style.attack_sound + + +//* Armor *// + +/** + * resets our armor to initial values + */ +/atom/proc/reset_armor() + set_armor(initial(armor_type)) + +/** + * sets our armor + * + * @params + * * what - list of armor values or a /datum/armor path + */ +/atom/proc/set_armor(what) + armor = fetch_armor_struct(what) + +/** + * gets our armor datum or otherwise make sure it exists + */ +/atom/proc/fetch_armor() + RETURN_TYPE(/datum/armor) + return armor || (armor = generate_armor()) + +/** + * get default armor datum + */ +/atom/proc/generate_armor() + return fetch_armor_struct(armor_type) + +/** + * runs an attack against armor + * + * * side effects are **not** allowed + * * this is the 'just checking' version. + * + * params are modified and then returned as a list + * + * * See [atom_shieldcall()] for what is going on here. + * * SHIELDCALL_ARG_* are used as the return list's indices. + * + * @params + * * damage - raw damage + * * damtype - damage type + * * tier - penetration / attack tier + * * flag - armor flag as seen in [code/__DEFINES/combat/armor.dm] + * * mode - damage_mode + * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] + * * weapon - (optional) the attacking weapon datum; see [code/__DEFINES/combat/shieldcall.dm] + * * flags - shieldcall flags passed through components. [code/__DEFINES/combat/shieldcall.dm] + * * hit_zone - where were they hit? + * * additional - a way to retrieve data out of the shieldcall, passed in by attacks. [code/__DEFINES/combat/shieldcall.dm] + * * clickchain - the clickchain for melee attacks. + * + * @return args, modified, as list. + */ +/atom/proc/check_armor(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_SLEEP(TRUE) + run_armorcalls(args, TRUE) + return args.Copy() + +/** + * runs an attack against armor + * + * * side effects are allowed + * + * params are modified and then returned as a list + * + * * See [atom_shieldcall()] for what is going on here. + * * SHIELDCALL_ARG_* are used as the return list's indices. + * + * @params + * * damage - raw damage + * * damtype - damage type + * * tier - penetration / attack tier + * * flag - armor flag as seen in [code/__DEFINES/combat/armor.dm] + * * mode - damage_mode + * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] + * * weapon - (optional) the attacking weapon datum; see [code/__DEFINES/combat/shieldcall.dm] + * * flags - shieldcall flags passed through components. [code/__DEFINES/combat/shieldcall.dm] + * * hit_zone - where were they hit? + * * additional - a way to retrieve data out of the shieldcall, passed in by attacks. [code/__DEFINES/combat/shieldcall.dm] + * * clickchain - the clickchain for melee attacks. + * + * @return args, modified, as list. + */ +/atom/proc/run_armor(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_SLEEP(TRUE) + run_armorcalls(args, FALSE) + return args.Copy() + +//* Shieldcalls *// + +/** + * runs an attack against shields + * + * * side effects are **not** allowed + * * this is the 'just checking' version. + * + * params are modified and then returned as a list + * + * * This is the dynamic shieldcall system. It's best to do what you want in specific shieldcall hooks if possible. + * * What this means is that this can't, say, redirect or delete a projectile, because bullet act handling is where that happens. + * * This more or less just lets you modify incoming damage instances sometimes. + * * The args are not copied! They're passed back directly. This has implications. + * * Make sure you pass in SHIELDCALL_FLAG_SECOND_CALL if **any** kind of shieldcall invocation has happened during this attack. + * * SECOND_CALL is required to tell things that something is not the first time, so you don't get doubled blocking efficiency. + * + * @params + * * damage - raw damage + * * damtype - damage type + * * damage_tier - penetration / attack tier + * * damage_flag - armor flag as seen in [code/__DEFINES/combat/armor.dm] + * * damage_mode - damage_mode + * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] + * * weapon - (optional) the attacking weapon datum; see [code/__DEFINES/combat/shieldcall.dm] + * * shieldcall_flags - shieldcall flags passed through components. [code/__DEFINES/combat/shieldcall.dm] + * * hit_zone - where were they hit? + * * additional - a way to retrieve data out of the shieldcall, passed in by attacks. [code/__DEFINES/combat/shieldcall.dm] + * * clickchain - the clickchain for melee attacks. + * + * @return args, modified, as list. + */ +/atom/proc/atom_shieldcheck(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_SLEEP(TRUE) + run_shieldcalls(args, TRUE) + return args.Copy() + +/** + * runs an attack against shields + * + * * side effects are allowed + * * this is run during an actual attack + * + * params are modified and then returned as a list + * + * * This is the dynamic shieldcall system. It's best to do what you want in specific shieldcall hooks if possible. + * * What this means is that this can't, say, redirect or delete a projectile, because bullet act handling is where that happens. + * * This more or less just lets you modify incoming damage instances sometimes. + * * The args are not copied! They're passed back directly. This has implications. + * * Make sure you pass in SHIELDCALL_FLAG_SECOND_CALL if **any** kind of shieldcall invocation has happened during this attack. + * * SECOND_CALL is required to tell things that something is not the first time, so you don't get doubled blocking efficiency. + * + * @params + * * damage - raw damage + * * damtype - damage type + * * damage_tier - penetration / attack tier + * * damage_flag - armor flag as seen in [code/__DEFINES/combat/armor.dm] + * * damage_mode - damage_mode + * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] + * * weapon - (optional) the attacking weapon datum; see [code/__DEFINES/combat/shieldcall.dm] + * * shieldcall_flags - shieldcall flags passed through components. [code/__DEFINES/combat/shieldcall.dm] + * * hit_zone - where were they hit? + * * additional - a way to retrieve data out of the shieldcall, passed in by attacks. [code/__DEFINES/combat/shieldcall.dm] + * * clickchain - the clickchain for melee attacks. + * + * @return args, modified, as list. + */ +/atom/proc/atom_shieldcall(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_SLEEP(TRUE) + run_shieldcalls(args, FALSE) + return args.Copy() + +/** + * Runs a damage instance against shieldcalls + * + * * This is a low level proc. Make sure you undersatnd how shieldcalls work [__DEFINES/combat/shieldcall.dm]. + */ +/atom/proc/run_shieldcalls(list/shieldcall_args, fake_attack) + SHOULD_NOT_SLEEP(TRUE) + SEND_SIGNAL(src, COMSIG_ATOM_SHIELDCALL, shieldcall_args, fake_attack) + if(shieldcall_args[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAG_TERMINATE) + return + for(var/datum/shieldcall/calling as anything in shieldcalls) + if(!calling.low_level_intercept) + continue + calling.handle_shieldcall(src, args, fake_attack) + if(shieldcall_args[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAG_TERMINATE) + break + +/** + * Runs a damage instance against armor + * + * * This is a low level proc. Make sure you undersatnd how shieldcalls work [__DEFINES/combat/shieldcall.dm]. + */ +/atom/proc/run_armorcalls(list/shieldcall_args, fake_attack) + SHOULD_NOT_SLEEP(TRUE) + SEND_SIGNAL(src, COMSIG_ATOM_ARMORCALL, shieldcall_args, fake_attack) + if(shieldcall_args[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAG_TERMINATE) + return + var/datum/armor/our_armor = fetch_armor() + our_armor.handle_shieldcall(shieldcall_args, fake_attack) + +/atom/proc/register_shieldcall(datum/shieldcall/delegate) + SHOULD_NOT_SLEEP(TRUE) + LAZYINITLIST(shieldcalls) + BINARY_INSERT(delegate, shieldcalls, /datum/shieldcall, delegate, priority, COMPARE_KEY) + +/atom/proc/unregister_shieldcall(datum/shieldcall/delegate) + SHOULD_NOT_SLEEP(TRUE) + LAZYREMOVE(shieldcalls, delegate) + +//* Shieldcalls - Focused / High-Level *// + +/** + * Runs shieldcalls for handle_touch + * + * * Use this instead of manually looping, as it fires a signal that makes things like /datum/passive_parry spin up. + * + * @params + * * e_args (optional) the clickchain event, if any; **This is mutable.** + * * contact_flags - SHIELDCALL_CONTACT_FLAG_* + * * contact_specific - SHIELDCALL_CONTACT_SPECIFIC_* + * * fake_attack - just checking! + * * shieldcall_flags - shieldcall flags. [code/__DEFINES/combat/shieldcall.dm] + * * clickchain_flags - clickchain flags. [code/__DEFINES/procs/clickcode.dm] + * + * @return SHIELDCALL_FLAG_* flags + */ +/atom/proc/atom_shieldcall_handle_touch(datum/event_args/actor/clickchain/e_args, contact_flags, contact_specific, fake_attack, shieldcall_flags) + SHOULD_NOT_SLEEP(TRUE) + // cannot parry yourself + if(e_args.performer == src) + return shieldcall_flags + // send query signal + SEND_SIGNAL(src, COMSIG_ATOM_SHIELDCALL_ITERATION, ATOM_SHIELDCALL_ITERATING_TOUCH) + . = shieldcall_flags + for(var/datum/shieldcall/shieldcall as anything in shieldcalls) + . |= shieldcall.handle_touch(src, ., fake_attack, e_args, contact_flags, contact_specific) + +/** + * Runs shieldcalls for handle_unarmed_melee + * + * * Use this instead of manually looping, as it fires a signal that makes things like /datum/passive_parry spin up. + * + * @params + * * style - the unarmed_attack datum being used + * * e_args (optional) the clickchain event, if any; **This is mutable.** + * * fake_attack - (optional) just checking! + * * shieldcall_flags - (optional) shieldcall flags. [code/__DEFINES/combat/shieldcall.dm] + * * clickchain_flags - (optional) clickchain flags. [code/__DEFINES/procs/clickcode.dm] + * + * @return SHIELDCALL_FLAG_* flags + */ +/atom/proc/atom_shieldcall_handle_unarmed_melee(datum/unarmed_attack/style, datum/event_args/actor/clickchain/e_args, fake_attack, shieldcall_flags, clickchain_flags) + SHOULD_NOT_SLEEP(TRUE) + // cannot parry yourself + if(e_args.performer == src) + return shieldcall_flags + // send query signal + SEND_SIGNAL(src, COMSIG_ATOM_SHIELDCALL_ITERATION, ATOM_SHIELDCALL_ITERATING_UNARMED_MELEE) + . = shieldcall_flags + for(var/datum/shieldcall/shieldcall as anything in shieldcalls) + . |= shieldcall.handle_unarmed_melee(src, ., fake_attack, style, e_args) + +/** + * Runs shieldcalls for handle_item_melee + * + * * Use this instead of manually looping, as it fires a signal that makes things like /datum/passive_parry spin up. + * + * @params + * * weapon - the item being used to swing with + * * e_args - (optional) the clickchain event, if any; **This is mutable.** + * * fake_attack - (optional) just checking! + * * shieldcall_flags - (optional) shieldcall flags. [code/__DEFINES/combat/shieldcall.dm] + * * clickchain_flags - (optional) clickchain flags. [code/__DEFINES/procs/clickcode.dm] + * + * @return SHIELDCALL_FLAG_* flags + */ +/atom/proc/atom_shieldcall_handle_item_melee(obj/item/weapon, datum/event_args/actor/clickchain/e_args, fake_attack, shieldcall_flags, clickchain_flags) + SHOULD_NOT_SLEEP(TRUE) + // cannot parry yourself + if(e_args.performer == src) + return shieldcall_flags + // send query signal + SEND_SIGNAL(src, COMSIG_ATOM_SHIELDCALL_ITERATION, ATOM_SHIELDCALL_ITERATING_ITEM_MELEE) + . = shieldcall_flags + for(var/datum/shieldcall/shieldcall as anything in shieldcalls) + . |= shieldcall.handle_item_melee(src, ., fake_attack, weapon, e_args) + +/** + * Runs shieldcalls for handle_bullet + * + * * Use this instead of manually looping, as it fires a signal that makes things like /datum/passive_parry spin up. + * + * @params + * * bullet_act_args - indexed list of bullet_act args. + * * shieldcall_returns - existing returns from other shieldcalls + * * fake_attack - just checking! + * * shieldcall_flags - shieldcall flags. [code/__DEFINES/combat/shieldcall.dm] + * + * @return SHIELDCALL_FLAG_TERMINATE or NONE + */ +/atom/proc/atom_shieldcall_handle_bullet(list/bullet_act_args, fake_attack, shieldcall_flags) + SHOULD_NOT_SLEEP(TRUE) + // cannot parry yourself + var/obj/projectile/proj = bullet_act_args[BULLET_ACT_ARG_PROJECTILE] + if(proj.firer == src && (bullet_act_args[BULLET_ACT_ARG_FLAGS] & PROJECTILE_IMPACT_POINT_BLANK)) + return shieldcall_flags + // send query signal + SEND_SIGNAL(src, COMSIG_ATOM_SHIELDCALL_ITERATION, ATOM_SHIELDCALL_ITERATING_BULLET_ACT) + . = shieldcall_flags + for(var/datum/shieldcall/shieldcall as anything in shieldcalls) + . |= shieldcall.handle_bullet(src, ., fake_attack, bullet_act_args) + +/** + * Runs shieldcalls for handle_throw_impact + * + * @params + * * thrown - the thrown object's data + * * fake_attack - just checking! + * * shieldcall_flags - shieldcall flags. [code/__DEFINES/combat/shieldcall.dm] + * + * @return SHIELDCALL_FLAG_* flags + */ +/atom/proc/atom_shieldcall_handle_throw_impact(datum/thrownthing/thrown, fake_attack, shieldcall_flags) + SHOULD_NOT_SLEEP(TRUE) + // cannot parry yourself + if(thrown.thrower == src && thrown.dist_travelled <= 1) + return shieldcall_flags + // send query signal + SEND_SIGNAL(src, COMSIG_ATOM_SHIELDCALL_ITERATION, ATOM_SHIELDCALL_ITERATING_THROW_IMPACT) + . = shieldcall_flags + for(var/datum/shieldcall/shieldcall as anything in shieldcalls) + . |= shieldcall.handle_throw_impact(src, ., fake_attack, thrown) diff --git a/code/game/atoms/atom.dm b/code/game/atoms/atom.dm index b9bcee5fe40d..95403d73eb59 100644 --- a/code/game/atoms/atom.dm +++ b/code/game/atoms/atom.dm @@ -882,6 +882,7 @@ return T.has_gravity() +// todo: annihilate this in favor of ATOM_PASS_INCORPOREAL /atom/proc/is_incorporeal() return FALSE diff --git a/code/game/atoms/buckling.dm b/code/game/atoms/buckling.dm index 45e68574979c..b286d57f88ef 100644 --- a/code/game/atoms/buckling.dm +++ b/code/game/atoms/buckling.dm @@ -1,5 +1,5 @@ //* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2023 Citadel Station developers. *// +//* Copyright (c) 2024 silicons *// /atom/movable/MouseDroppedOn(atom/dropping, mob/user, proximity, params) if(drag_drop_buckle_interaction(dropping, user)) @@ -40,7 +40,7 @@ return FALSE if(!user.Adjacent(src) || !A.Adjacent(src)) return FALSE - if(SEND_SIGNAL(src, COMSIG_MOVABLE_DRAG_DROP_BUCKLE_INTERACTION, A, user) & COMPONENT_HANDLED_BUCKLE_INTERACTION) + if(SEND_SIGNAL(src, COMSIG_MOVABLE_DRAG_DROP_BUCKLE_INTERACTION, A, user) & SIGNAL_RAISE_BUCKLE_INTERACTION_HANDLED) return TRUE if(!buckle_allowed || (buckle_flags & BUCKLING_NO_DEFAULT_BUCKLE)) return FALSE @@ -69,7 +69,7 @@ // todo: refactor below if(user.incapacitated()) return TRUE - if(SEND_SIGNAL(src, COMSIG_MOVABLE_CLICK_UNBUCKLE_INTERACTION, user) & COMPONENT_HANDLED_BUCKLE_INTERACTION) + if(SEND_SIGNAL(src, COMSIG_MOVABLE_CLICK_UNBUCKLE_INTERACTION, user) & SIGNAL_RAISE_BUCKLE_INTERACTION_HANDLED) return TRUE if(!buckle_allowed || (buckle_flags & BUCKLING_NO_DEFAULT_UNBUCKLE)) return FALSE @@ -92,7 +92,7 @@ /atom/movable/proc/user_unbuckle_mob(mob/M, flags, mob/user, semantic) SHOULD_CALL_PARENT(TRUE) . = SEND_SIGNAL(src, COMSIG_MOVABLE_USER_UNBUCKLE_MOB, M, flags, user, semantic) - if(. & COMPONENT_BLOCK_BUCKLE_OPERATION) + if(. & SIGNAL_RAISE_BLOCK_BUCKLE_OPERATION) return FALSE . = unbuckle_mob(M, flags, user, semantic) if(!.) @@ -126,7 +126,7 @@ /atom/movable/proc/user_buckle_mob(mob/M, flags, mob/user, semantic) SHOULD_CALL_PARENT(TRUE) . = SEND_SIGNAL(src, COMSIG_MOVABLE_USER_BUCKLE_MOB, M, flags, user, semantic) - if(. & COMPONENT_BLOCK_BUCKLE_OPERATION) + if(. & SIGNAL_RAISE_BLOCK_BUCKLE_OPERATION) return FALSE if((buckle_flags & BUCKLING_NO_USER_BUCKLE_OTHER_TO_SELF) && (user == src)) return FALSE @@ -166,7 +166,7 @@ if(M == src) return FALSE - if(SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_BUCKLE_MOB, M, flags, user, semantic) & COMPONENT_BLOCK_BUCKLE_OPERATION) + if(SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_BUCKLE_MOB, M, flags, user, semantic) & SIGNAL_RAISE_BLOCK_BUCKLE_OPERATION) return FALSE if(!(flags & BUCKLE_OP_FORCE) && !can_buckle_mob(M, flags, user, semantic)) @@ -244,9 +244,9 @@ /atom/movable/proc/can_buckle_mob(mob/M, flags, mob/user, semantic) SHOULD_CALL_PARENT(TRUE) . = SEND_SIGNAL(src, COMSIG_MOVABLE_CAN_BUCKLE_MOB, M, flags, user, semantic) - if(. & COMPONENT_BLOCK_BUCKLE_OPERATION) + if(. & SIGNAL_RAISE_BLOCK_BUCKLE_OPERATION) return FALSE - else if(. & COMPONENT_FORCE_BUCKLE_OPERATION) + else if(. & SIGNAL_RAISE_FORCE_BUCKLE_OPERATION) return TRUE if(!(flags & BUCKLE_OP_IGNORE_LOC) && !M.Adjacent(src)) return FALSE @@ -276,9 +276,9 @@ /atom/movable/proc/can_unbuckle_mob(mob/M, flags, mob/user, semantic) SHOULD_CALL_PARENT(TRUE) . = SEND_SIGNAL(src, COMSIG_MOVABLE_CAN_UNBUCKLE_MOB, M, flags, user, semantic) - if(. & COMPONENT_BLOCK_BUCKLE_OPERATION) + if(. & SIGNAL_RAISE_BLOCK_BUCKLE_OPERATION) return FALSE - else if(. & COMPONENT_FORCE_BUCKLE_OPERATION) + else if(. & SIGNAL_RAISE_FORCE_BUCKLE_OPERATION) return TRUE return TRUE /** @@ -329,7 +329,7 @@ /atom/movable/proc/resist_unbuckle_interaction(mob/M) set waitfor = FALSE ASSERT(M in buckled_mobs) - if(SEND_SIGNAL(src, COMSIG_MOVABLE_RESIST_UNBUCKLE_INTERACTION, M) & COMPONENT_HANDLED_BUCKLE_INTERACTION) + if(SEND_SIGNAL(src, COMSIG_MOVABLE_RESIST_UNBUCKLE_INTERACTION, M) & SIGNAL_RAISE_BUCKLE_INTERACTION_HANDLED) return if(!buckle_allowed || (buckle_flags & BUCKLING_NO_DEFAULT_RESIST)) return FALSE @@ -410,14 +410,14 @@ */ /mob/proc/buckled(atom/movable/AM, flags, mob/user, semantic) SHOULD_CALL_PARENT(TRUE) - SEND_SIGNAL(src, COMSIG_MOB_BUCKLED, AM, flags, user, semantic) + SEND_SIGNAL(src, COMSIG_MOB_BUCKLED_TO, AM, flags, user, semantic) /** * called when we're unbuckled from something */ /mob/proc/unbuckled(atom/movable/AM, flags, mob/user, semantic) SHOULD_CALL_PARENT(TRUE) - SEND_SIGNAL(src, COMSIG_MOB_UNBUCKLED, AM, flags, user, semantic) + SEND_SIGNAL(src, COMSIG_MOB_UNBUCKLED_FROM, AM, flags, user, semantic) /** * can we buckle to something? @@ -426,10 +426,10 @@ */ /mob/proc/can_buckle(atom/movable/AM, flags, mob/user, semantic, movable_opinion) SHOULD_CALL_PARENT(TRUE) - . = SEND_SIGNAL(src, COMSIG_MOB_CAN_BUCKLE, AM, flags, user, semantic, movable_opinion) - if(. & COMPONENT_BLOCK_BUCKLE_OPERATION) + . = SEND_SIGNAL(src, COMSIG_MOB_CAN_BUCKLE_TO, AM, flags, user, semantic, movable_opinion) + if(. & SIGNAL_RAISE_BLOCK_BUCKLE_OPERATION) return FALSE - else if(. & COMPONENT_FORCE_BUCKLE_OPERATION) + else if(. & SIGNAL_RAISE_FORCE_BUCKLE_OPERATION) return TRUE return movable_opinion @@ -440,10 +440,10 @@ */ /mob/proc/can_unbuckle(atom/movable/AM, flags, mob/user, semantic, movable_opinion) SHOULD_CALL_PARENT(TRUE) - . = SEND_SIGNAL(src, COMSIG_MOB_CAN_UNBUCKLE, AM, flags, user, semantic, movable_opinion) - if(. & COMPONENT_BLOCK_BUCKLE_OPERATION) + . = SEND_SIGNAL(src, COMSIG_MOB_CAN_UNBUCKLE_FROM, AM, flags, user, semantic, movable_opinion) + if(. & SIGNAL_RAISE_BLOCK_BUCKLE_OPERATION) return FALSE - else if(. & COMPONENT_FORCE_BUCKLE_OPERATION) + else if(. & SIGNAL_RAISE_FORCE_BUCKLE_OPERATION) return TRUE return movable_opinion diff --git a/code/game/atoms/defense.dm b/code/game/atoms/defense.dm deleted file mode 100644 index 6fbf61c69c72..000000000000 --- a/code/game/atoms/defense.dm +++ /dev/null @@ -1,547 +0,0 @@ -//* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2023 Citadel Station developers. *// - -//! Welcome to the atom damage module. -//! Enjoy the bitfield and #define vomit. - -// todo: everything needs comsigs comsigs comsigs - -//? Hooks / External - -/** - * todo: implement on most atoms/generic damage system - * todo: replace legacy_ex_act entirely with this - * - * React to being hit by an explosive shockwave - * - * ? Tip for overrides: . = ..() when you want signal to be sent, mdify power before if you need to; to ignore parent - * ? block power, just `return power` in your proc after . = ..(). - * - * @params - * - power - power our turf was hit with - * - direction - DIR_BIT bits; can bwe null if it wasn't a wave explosion!! - * - explosion - explosion automata datum; can be null - * - * @return power after falloff (e.g. hit with 30 power, return 20 to apply 10 falloff) - */ -/atom/proc/ex_act(power, dir, datum/automata/wave/explosion/E) - SHOULD_CALL_PARENT(TRUE) - SEND_SIGNAL(src, COMSIG_ATOM_EX_ACT, power, dir, E) - return power - -/** - * called on melee hit - * - * @params - * * user - person attacking - * * weapon - weapon used - * * target_zone - zone targeted - * * mult - damage multiplier - * - * @return clickchain flags to append - */ -/atom/proc/melee_act(mob/user, obj/item/weapon, target_zone, mult = 1) - return CLICKCHAIN_DO_NOT_ATTACK - -/** - * called on unarmed melee hit - * - * @params - * * user - person attacking - * * style - unarmed attack datum - * * target_zone - zone targeted - * * mult - damage multiplier - * - * @return clickchain flags to append - */ -/atom/proc/unarmed_act(mob/attacker, datum/unarmed_attack/style, target_zone, mult = 1) - return CLICKCHAIN_DO_NOT_ATTACK - -//? Damage API - -/** - * takes damage from a generic attack, taking into account armor but not shields. - * this does not handle playing sounds / anything, this is strictly generic damage handling - * usable by anything. - * - * @params - * * damage - raw damage - * * tier - (optional) penetration / attack tier - * * flag - (optional) armor flag as seen in [code/__DEFINES/combat/armor.dm]; leave out to not run armor. - * * mode - (optional) damage_mode - * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] - * * weapon - (optional) attacking /obj/item for melee or thrown, /obj/projectile for ranged, /mob for unarmed - * * gradual - loud effects like glass clanging should not happen. use for stuff like acid and fire. - * - * @return raw damage taken - */ -/atom/proc/inflict_atom_damage(damage, tier, flag, mode, attack_type, datum/weapon, gradual) - if(!integrity_enabled) - return 0 - if(integrity_flags & INTEGRITY_INDESTRUCTIBLE) - return 0 - if(flag) - var/list/returned = run_armor(arglist(args)) - damage = returned[1] - mode = returned[4] - if(!damage) - return - . = integrity - damage_integrity(damage) - . = . - integrity - -//? Hitsound API - -// todo: stuff like metal limbs punching walls making special sounds -// todo: this probably needs a rework - -/** - * gets hitsound override. return a value to be fed into playsound, or null for default. - * - * @params - * * damage_type - damage type like brute / burn / etc - * * damage_mode - damage mode for piercing / whatnot - * * attack_type - attack type enum like melee / projectile / thrown / unarmed / etc - * * weapon - attacking /obj/item for melee / thrown, /obj/projectile for ranged, /mob for unarmed - */ -/atom/proc/hitsound_override(damage_type, damage_mode, attack_type, datum/weapon) - return // default is null - -/atom/proc/hitsound_melee(obj/item/I) - . = I.attacksound_override(src, ATTACK_TYPE_MELEE) - if(!isnull(.)) - return - . = hitsound_override(I.damtype, I.damage_mode, ATTACK_TYPE_MELEE, I) - if(.) - return - . = (I.damtype == BURN? hit_sound_burn : hit_sound_brute) || I.attack_sound - if(.) - return - switch(I.damtype) - if(BRUTE) - return "swing_hit" - if(BURN) - return "'sound/items/welder.ogg" - else - return "swing_hit" - -/atom/proc/hitsound_projectile(obj/projectile/P) - //? todo: projectile gets final say - . = hitsound_override(P.damtype, P.damage_mode, ATTACK_TYPE_PROJECTILE, P) - if(.) - return - return islist(P.impact_sounds)? pick(P.impact_sounds) : P.impact_sounds - -/atom/proc/hitsound_throwhit(obj/item/I) - . = I.attacksound_override(src, ATTACK_TYPE_THROWN) - if(!isnull(.)) - return - . = hitsound_override(I.damtype, I.damage_mode, ATTACK_TYPE_THROWN, I) - if(.) - return - . = (I.damtype == BURN? hit_sound_burn : hit_sound_brute) || I.attack_sound - if(.) - return - switch(I.damtype) - if(BRUTE) - return "swing_hit" - if(BURN) - return 'sound/items/welder.ogg' - else - return "swing_hit" - -/atom/proc/hitsound_unarmed(mob/M, datum/unarmed_attack/style) - //? todo: style gets final say - . = hitsound_override(M, style.damage_mode, ATTACK_TYPE_UNARMED, style) - if(.) - return - // todo: way to override this from style side? we don't just want hitsound brute/burn. - . = (style.damage_type == BURN? hit_sound_burn : hit_sound_brute) || style.attack_sound - -//? Direct Integrity - -/** - * damages integrity directly, ignoring armor / shields - * - * @params - * * amount - how much - * * gradual - burst or gradual? if you want to play a sound or something, you usually want to check this. - * * do_not_break - skip calling atom_break - */ -/atom/proc/damage_integrity(amount, gradual, do_not_break) - SHOULD_CALL_PARENT(TRUE) - SHOULD_NOT_SLEEP(TRUE) - var/was_working = integrity > integrity_failure - integrity = max(0, integrity - amount) - if(was_working && integrity <= integrity_failure && !do_not_break) - atom_break() - if(integrity <= 0) - atom_destruction() - -/** - * heals integrity directly - * - * @params - * * amount - how much - * * gradual - burst or gradual? if you want to play a sound or something, you usually want to check this. - * * do_not_fix - skip calling atom_fix - * - * @return amount healed - */ -/atom/proc/heal_integrity(amount, gradual, do_not_fix) - SHOULD_CALL_PARENT(TRUE) - SHOULD_NOT_SLEEP(TRUE) - var/was_failing = integrity <= integrity_failure - . = integrity - integrity = min(integrity_max, integrity + amount) - . = integrity - . - if(was_failing && integrity > integrity_failure && !do_not_fix) - atom_fix() - -/** - * directly sets integrity - ignores armor / sihelds - * - * will not call [damage_integrity] or [heal_integrity] - * will call [atom_break], [atom_fix], [atom_destruction] - * - * @params - * * amount - how much to set to? - */ -/atom/proc/set_integrity(amount) - SHOULD_CALL_PARENT(TRUE) - SHOULD_NOT_SLEEP(TRUE) - var/was_failing = integrity <= integrity_failure - integrity = clamp(amount, 0, integrity_max) - if(!was_failing && integrity <= integrity_failure) - atom_break() - else if(was_failing && integrity > integrity_failure) - atom_fix() - if(integrity <= 0) - atom_destruction() - -/** - * sets max integrity - will automatically reduce integrity if it's above max. - * - * will not call [damage_integrity] - * will call [atom_break], [atom_fix], [atom_destruction] - * - * @params - * * amount - how much to set to - */ -/atom/proc/set_max_integrity(amount) - integrity_max = max(amount, 0) - if(integrity < integrity_max) - return - var/was_broken = integrity <= integrity_failure - integrity = integrity_max - if(!was_broken && (integrity <= integrity_failure)) - atom_break() - if(integrity <= 0) - atom_destruction() - -/** - * sets integrity and max integrity - will automatically reduce integrity if it's above max. - * - * will not call [damage_integrity] - * will call [atom_break], [atom_fix], [atom_destruction] - * - * @params - * * integrity - how much to set integrity to - * * integrity_max - how much to set integrity_max to - */ -/atom/proc/set_full_integrity(integrity, integrity_max) - src.integrity_max = max(integrity_max, 0) - var/was_broken = src.integrity <= integrity_failure - src.integrity = clamp(integrity, 0, integrity_max) - var/now_broken = integrity <= integrity_failure - if(!was_broken && now_broken) - atom_break() - else if(was_broken && !now_broken) - atom_fix() - if(integrity <= 0) - atom_destruction() - -/** - * Set integrity to a multiple of initial. - * - * And fully restore it if specified. - * Otherwise, will retain the last percentage. - */ -/atom/proc/set_multiplied_integrity(factor, restore) - var/was_broken = src.integrity <= integrity_failure - if(restore) - integrity = integrity_max = initial(integrity_max) * factor - if(was_broken && integrity > integrity_failure) - atom_fix() - return - var/ratio = integrity / integrity_max - integrity_max = initial(integrity_max) * factor - integrity = integrity_max * ratio - var/now_broken = integrity <= integrity_failure - if(!was_broken && now_broken) - atom_break() - else if(was_broken && !now_broken) - atom_fix() - if(integrity <= 0) - atom_destruction() - -/** - * adjusts integrity - routes directly to [damage_integrity] and [heal_integrity] - * - * will call [damage_integrity] - * will call [atom_break], [atom_fix], [atom_destruction] - * - * @params - * * amount - how much - * * gradual - burst or gradual? - * * no_checks - do not call fix/break - */ -/atom/proc/adjust_integrity(amount, gradual, no_checks) - SHOULD_CALL_PARENT(TRUE) - SHOULD_NOT_SLEEP(TRUE) - if(amount > 0) - return heal_integrity(amount, gradual, no_checks) - else - return damage_integrity(amount, gradual, no_checks) - -/** - * adjusts max integrity - will automatically reduce integrity if it's above max. - * - * will call [damage_integrity] - * will call [atom_break], [atom_fix], [atom_destruction] - * - * @params - * * amount - how much to adjust by - * * gradual - burst or gradual? - */ -/atom/proc/adjust_max_integrity(amount, gradual) - // lazy route lmao - set_max_integrity(integrity_max + amount) - -/** - * percent integrity, rounded. - */ -/atom/proc/percent_integrity(round_to = 0.1) - return integrity_max? round(integrity / integrity_max, round_to) : 0 - -/atom/proc/is_integrity_broken() - return atom_flags & ATOM_BROKEN - -/atom/proc/is_integrity_damaged() - return integrity < integrity_max - -//? Thresholds & Events - -/** - * called when integrity reaches 0 from a non 0 value - */ -/atom/proc/atom_destruction() - SHOULD_CALL_PARENT(TRUE) - SHOULD_NOT_SLEEP(TRUE) - if(!(integrity_flags & INTEGRITY_NO_DECONSTRUCT)) - deconstruct(ATOM_DECONSTRUCT_DESTROYED) - -/** - * called when integrity drops below or at integrity_failure - * - * if integrity_failure is 0, this is called before destruction. - */ -/atom/proc/atom_break() - SHOULD_CALL_PARENT(TRUE) - SHOULD_NOT_SLEEP(TRUE) - if(integrity > integrity_failure) - damage_integrity(integrity - integrity_failure, do_not_break = TRUE) - atom_flags |= ATOM_BROKEN - -/** - * called when integrity rises above integrity_failure - * - * if integrity_failure is 0, this still works. - */ -/atom/proc/atom_fix() - SHOULD_CALL_PARENT(TRUE) - SHOULD_NOT_SLEEP(TRUE) - if(integrity < integrity_failure) - heal_integrity(integrity_failure - integrity + 1, do_not_fix = TRUE) - atom_flags &= ~ATOM_BROKEN - -//? Deconstruction - -/** - * called to semantically deconstruct an atom - * - * @params - * * method - how we were deconstructed - */ -/atom/proc/deconstruct(method = ATOM_DECONSTRUCT_DISASSEMBLED) - SHOULD_NOT_OVERRIDE(TRUE) - - // send signal - // todo: signal - // do da funny logic - deconstructed(method) - // drop things after so things that rely on having objects don't break - drop_products(method, drop_location()) - // goodbye, cruel world - break_apart(method) - -/** - * called to actually destroy ourselves - */ -/atom/proc/break_apart(method) - qdel(src) - -/** - * called when we are deconstructed - * - * **do not drop products in here** - * - * @params - * - method - how we were deconstructed - */ -/atom/proc/deconstructed(method) - return - -/** - * called to drop the products of deconstruction - * - * @params - * * method - how we were deconstructed - * * where - where to drop products; set in base if null to drop_location(). - */ -/atom/proc/drop_products(method, atom/where = drop_location()) - return - -/** - * called to move a product to a place - * - * @params - * * method - how we were deconstructed - * * dropping - movable in question - * * where - where to move to - */ -/atom/proc/drop_product(method, atom/movable/dropping, atom/where) - dropping.forceMove(where || drop_location()) - -//? Armor - -/** - * resets our armor to initial values - */ -/atom/proc/reset_armor() - set_armor(initial(armor_type)) - -/** - * sets our armor - * - * @params - * * what - list of armor values or a /datum/armor path - */ -/atom/proc/set_armor(what) - armor = fetch_armor_struct(what) - -/** - * gets our armor datum or otherwise make sure it exists - */ -/atom/proc/fetch_armor() - RETURN_TYPE(/datum/armor) - return armor || (armor = generate_armor()) - -/** - * get default armor datum - */ -/atom/proc/generate_armor() - return fetch_armor_struct(armor_type) - -/** - * calculates the resulting damage from an attack, taking into account our armor and soak - * - * @params - * * damage - raw damage - * * tier - penetration / attack tier - * * flag - armor flag as seen in [code/__DEFINES/combat/armor.dm] - * * mode - damage_mode - * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] - * * weapon - (optional) attacking /obj/item for melee or thrown, /obj/projectile for ranged, /mob for unarmed - * - * @return args as list. - */ -/atom/proc/check_armor(damage, tier, flag, mode, attack_type, datum/weapon) - damage = fetch_armor().resultant_damage(damage, tier, flag) - return args.Copy() - -/** - * runs armor against an incoming attack - * this proc can have side effects - * - * @params - * * damage - raw damage - * * tier - penetration / attack tier - * * flag - armor flag as seen in [code/__DEFINES/combat/armor.dm] - * * mode - damage_mode - * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] - * * weapon - (optional) attacking /obj/item for melee or thrown, /obj/projectile for ranged, /mob for unarmed - * - * @return args as list. - */ -/atom/proc/run_armor(damage, tier, flag, mode, attack_type, datum/weapon) - damage = fetch_armor().resultant_damage(damage, tier, flag) - return args.Copy() - -//? shieldcalls - -/** - * checks for shields - * not always accurate - * - * params are modified and then returned as a list. - * - * @params - * * damage - raw damage - * * damtype - damage type - * * tier - penetration / attack tier - * * flag - armor flag as seen in [code/__DEFINES/combat/armor.dm] - * * mode - damage_mode - * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] - * * weapon - (optional) attacking /obj/item for melee or thrown, /obj/projectile for ranged, /mob for unarmed - * * additional - a way to retrieve data out of the shieldcall, passed in by attacks. [code/__DEFINES/dcs/signals/atoms/signals_atom_defense.dm] - * * retval - shieldcall flags passed through components. [code/__DEFINES/dcs/signals/atoms/signals_atom_defense.dm] - * - * @return modified args as list - */ -/atom/proc/atom_shieldcheck(damage, damtype, tier, flag, mode, attack_type, datum/weapon, list/additional = list(), retval = NONE) - retval |= SHIELDCALL_JUST_CHECKING - for(var/datum/shieldcall/calling as anything in shieldcalls) - calling.handle_shieldcall(src, args) - return args.Copy() - -/** - * runs an attack against shields - * side effects are allowed - * - * params are modified and then returned as a list - * - * @params - * * damage - raw damage - * * damtype - damage type - * * tier - penetration / attack tier - * * flag - armor flag as seen in [code/__DEFINES/combat/armor.dm] - * * mode - damage_mode - * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] - * * weapon - (optional) attacking /obj/item for melee or thrown, /obj/projectile for ranged, /mob for unarmed - * * additional - a way to retrieve data out of the shieldcall, passed in by attacks. [code/__DEFINES/combat/shieldcall.dm] - * * retval - shieldcall flags passed through components. [code/__DEFINES/combat/shieldcall.dm] - * - * @return modified args as list - */ -/atom/proc/atom_shieldcall(damage, damtype, tier, flag, mode, attack_type, datum/weapon, list/additional = list(), retval = NONE) - for(var/datum/shieldcall/calling as anything in shieldcalls) - calling.handle_shieldcall(src, args) - return args.Copy() - -/atom/proc/register_shieldcall(datum/shieldcall/delegate) - LAZYINITLIST(shieldcalls) - BINARY_INSERT(delegate, shieldcalls, /datum/shieldcall, delegate, priority, COMPARE_KEY) - -/atom/proc/unregister_shieldcall(datum/shieldcall/delegate) - LAZYREMOVE(shieldcalls, delegate) diff --git a/code/game/atoms/defense_old.dm b/code/game/atoms/defense_old.dm index ab7d141eed0c..d162a947cbfd 100644 --- a/code/game/atoms/defense_old.dm +++ b/code/game/atoms/defense_old.dm @@ -12,10 +12,6 @@ // todo: SHOULD_CALL_PARENT(TRUE) SEND_SIGNAL(src, COMSIG_ATOM_EMP_ACT, severity) -/atom/proc/bullet_act(obj/projectile/P, def_zone) - P.on_hit(src, 0, def_zone) - . = 0 - // Called when a blob expands onto the tile the atom occupies. /atom/proc/blob_act() return diff --git a/code/game/atoms/movable/throwing.dm b/code/game/atoms/movable/movable-throw.dm similarity index 99% rename from code/game/atoms/movable/throwing.dm rename to code/game/atoms/movable/movable-throw.dm index b6b21f4ce20c..f329f1ab5bd2 100644 --- a/code/game/atoms/movable/throwing.dm +++ b/code/game/atoms/movable/movable-throw.dm @@ -1,5 +1,5 @@ //* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2023 Citadel Station developers. *// +//* Copyright (c) 2024 silicons *// //! Welcome to unoptimized hell. Enjoy your comsigs. diff --git a/code/game/atoms/movable/movement.dm b/code/game/atoms/movable/movement.dm index 64c2e193c2be..a0d0b0c016bf 100644 --- a/code/game/atoms/movable/movement.dm +++ b/code/game/atoms/movable/movement.dm @@ -438,13 +438,21 @@ * Make sure you know what you're doing if you override or call this. * * This *must* be a pure proc. You cannot act on the atom if you override this! Use Bump() for that. + * + * * **warning**: `newloc` is a ss13 construct. BYOND-native pixel movement doesn't have that. + * + * @params + * * AM - the thing trying to un-overlap us + * * newloc - (optional) where they're going */ /atom/movable/Uncross(atom/movable/AM, atom/newloc) . = TRUE if(SEND_SIGNAL(src, COMSIG_MOVABLE_UNCROSS, AM) & COMPONENT_MOVABLE_BLOCK_UNCROSS) return FALSE - if(isturf(newloc) && !CheckExit(AM, newloc)) - return FALSE + if(isturf(newloc)) + var/our_opinion = CheckExit(AM, newloc) + if(!our_opinion && (AM.generic_canpass || !AM.CanPassThrough(src, newloc, our_opinion))) + return FALSE /** * Called when something uncrosses us. @@ -514,7 +522,7 @@ var/list/old_grabbed if(allow_grabbed) old_grabbed = list() - for(var/mob/M in grabbing()) + for(var/mob/M in get_grabbing()) if(check_grab(M) < allow_grabbed) continue old_grabbed += M @@ -542,7 +550,7 @@ /mob/getLocationTransitForceMoveTargets(atom/destination, recurse_levels = 0, allow_buckled = TRUE, allow_pulled = TRUE, allow_grabbed = GRAB_PASSIVE) . = ..() if(allow_grabbed) - var/list/grabbing = grabbing() + var/list/grabbing = get_grabbing() for(var/mob/M in grabbing) if(check_grab(M) < allow_grabbed) continue diff --git a/code/game/atoms/vv.dm b/code/game/atoms/vv.dm index 2373c129b6aa..f36655a135e7 100644 --- a/code/game/atoms/vv.dm +++ b/code/game/atoms/vv.dm @@ -78,7 +78,7 @@ else if(was_failing && !now_failing) atom_fix() -/atom/vv_get_var(var_name) +/atom/vv_get_var(var_name, resolve) switch(var_name) if(NAMEOF(src, base_layer)) if(isnull(base_layer)) diff --git a/code/game/click/context.dm b/code/game/click/context.dm index 41001e80a434..1a0686db1d69 100644 --- a/code/game/click/context.dm +++ b/code/game/click/context.dm @@ -25,10 +25,14 @@ return TRUE return FALSE +/** + * @params + * * e_args - the actor data or a single mob + */ /atom/proc/context_menu(datum/event_args/actor/e_args) set waitfor = FALSE // admin proccall support - WRAP_MOB_TO_ACTOR_EVENT_ARGS(e_args) + E_ARGS_WRAP_USER_TO_ACTOR(e_args) // todo: dynamically rebuild menu based on distance? var/client/receiving = e_args.initiator.client if(isnull(receiving)) diff --git a/code/game/click/items.dm b/code/game/click/items.dm index 9514e4b15f66..607c9c2a1745 100644 --- a/code/game/click/items.dm +++ b/code/game/click/items.dm @@ -10,6 +10,9 @@ /** * Called when trying to click something that the user can Reachability() to. * + * todo: this should allow passing in a clickchain datum instead. + * todo: lazy_melee_attack() for when you don't want to. + * * @params * - target - thing hitting * - user - user using us @@ -46,6 +49,9 @@ /** * Called when trying to click something that the user can't Reachability() to. * + * todo: this should allow passing in a clickchain datum instead. + * todo: lazy_ranged_attack() for when you don't want to. + * * @params * - target - thing hitting * - user - user using us @@ -313,6 +319,8 @@ * called when we're used to attack a non-mob * this doesn't actually need to be an obj. * + * todo: purge mult + * * @params * * target - atom being attacked * * clickchain - the /datum/event_args/actor/clickchain arguments included @@ -338,17 +346,21 @@ return CLICKCHAIN_DO_NOT_PROPAGATE //? legacy: decloak clickchain.performer.break_cloak() + // set mult + clickchain.damage_multiplier *= mult // click cooldown // todo: clickcd rework clickchain.performer.setClickCooldown(clickchain.performer.get_attack_speed(src)) // animation clickchain.performer.animate_swing_at_target(target) // perform the hit - . = melee_object_hit(target, clickchain, clickchain_flags, mult) + . = melee_object_hit(target, clickchain, clickchain_flags) /** * called at base of attack_object after standard melee attack misses * + * todo: purge mult + * * @return clickchain flags to append * * @params @@ -378,7 +390,7 @@ * * clickchain_flags - __DEFINES/procs/clickcode.dm flags * * mult - damage multiplier */ -/obj/item/proc/melee_object_hit(atom/target, datum/event_args/actor/clickchain/clickchain, clickchain_flags, mult = 1) +/obj/item/proc/melee_object_hit(atom/target, datum/event_args/actor/clickchain/clickchain, clickchain_flags) SHOULD_CALL_PARENT(TRUE) // harmless, just tap them and leave @@ -406,7 +418,7 @@ visible = SPAN_DANGER("[target] has been [islist(attack_verb)? pick(attack_verb) : attack_verb] with [src] by [clickchain.performer]!") ) // damage - target.melee_act(clickchain.performer, src, mult = mult) + target.melee_act(clickchain.performer, src, null, clickchain) // animate target.animate_hit_by_weapon(clickchain.performer, src) diff --git a/code/game/click/mobs.dm b/code/game/click/mobs.dm index 7ac0ad4c7373..3f0f13fb0bb3 100644 --- a/code/game/click/mobs.dm +++ b/code/game/click/mobs.dm @@ -1,6 +1,9 @@ /** * Called when trying to click on someone we can Reachability() to without an item in hand. * + * todo: this should allow passing in a clickchain datum instead. + * todo: lazy_melee_attack() for when you don't want to. + * * @params * - target - thing we're clicking * - clickchain_flags - see [code/__DEFINES/procs/clickcode.dm] @@ -98,7 +101,7 @@ log_attack(key_name(src), ismob(target)? key_name(target) : "[target] ([ref(target)])", "attacked with [style.attack_name] newhp ~[newhp || "unknown"]") /mob/proc/melee_attack_hit(atom/target, datum/event_args/actor/clickchain/clickchain, datum/unarmed_attack/style, clickchain_flags, target_zone, mult) - . = target.unarmed_act(src, style, target_zone, mult) + . = target.unarmed_act(src, style, target_zone, clickchain) if(. & CLICKCHAIN_ATTACK_MISSED) return . | melee_attack_miss(target, clickchain, style, clickchain_flags, target_zone, mult) // todo: the rest of this proc not qdel-safe diff --git a/code/game/content/factions/corporations/nanotrasen/nanotrasen-supply/contraband.dm b/code/game/content/factions/corporations/nanotrasen/nanotrasen-supply/contraband.dm index 18172ed42cab..c5dfcecbd017 100644 --- a/code/game/content/factions/corporations/nanotrasen/nanotrasen-supply/contraband.dm +++ b/code/game/content/factions/corporations/nanotrasen/nanotrasen-supply/contraband.dm @@ -96,7 +96,7 @@ name = "sapper's kit" /obj/item/storage/box/cargo_null_entry_kit/sapper/legacy_spawn_contents() - new /obj/item/melee/energy/sword/ionic_rapier(src) + new /obj/item/melee/transforming/energy/sword/ionic_rapier(src) new /obj/item/storage/box/syndie_kit/space(src) new /obj/item/storage/box/syndie_kit/demolitions(src) new /obj/item/multitool/ai_detector(src) diff --git a/code/game/content/factions/corporations/nanotrasen/nanotrasen-supply/munitions.dm b/code/game/content/factions/corporations/nanotrasen/nanotrasen-supply/munitions.dm index d3fc78511e77..23757c6fe2cf 100644 --- a/code/game/content/factions/corporations/nanotrasen/nanotrasen-supply/munitions.dm +++ b/code/game/content/factions/corporations/nanotrasen/nanotrasen-supply/munitions.dm @@ -32,7 +32,7 @@ name = "Weapons - Experimental weapons crate" contains = list( /obj/item/gun/energy/xray = 2, - /obj/item/shield/energy = 2, + /obj/item/shield/transforming/energy = 2, ) container_type = /obj/structure/closet/crate/secure/corporate/nanotrasen container_name = "Experimental weapons crate" diff --git a/code/game/content/factions/corporations/nanotrasen/nanotrasen-supply/security.dm b/code/game/content/factions/corporations/nanotrasen/nanotrasen-supply/security.dm index bb09c938ed6b..fd0841c892d1 100644 --- a/code/game/content/factions/corporations/nanotrasen/nanotrasen-supply/security.dm +++ b/code/game/content/factions/corporations/nanotrasen/nanotrasen-supply/security.dm @@ -466,7 +466,7 @@ /obj/item/clothing/accessory/badge/holo/hos, /obj/item/clothing/accessory/holster/waist, /obj/item/melee/telebaton, - /obj/item/shield/riot/tele, + /obj/item/shield/transforming/telescopic, /obj/item/clothing/head/beret/sec/corporate/hos, /obj/item/flashlight/maglight, ) diff --git a/code/game/gamemodes/changeling/powers/armblade.dm b/code/game/gamemodes/changeling/powers/armblade.dm index db77320eed6c..bc89f0efdeff 100644 --- a/code/game/gamemodes/changeling/powers/armblade.dm +++ b/code/game/gamemodes/changeling/powers/armblade.dm @@ -47,6 +47,14 @@ return 1 return 0 +// todo: full rework of all of this; changeling weapons are balanced by a numbskull holy shit fuck bay +// - block chances are way, way too high +// - insufficient armor penetration (ironically) for citrp combat balancing directives +// - need to rethink changeling defensives in general, they shouldn't be reliant on parrying + +/datum/parry_frame/passive_block/armblade + parry_sfx = 'sound/weapons/slash.ogg' + /obj/item/melee/changeling name = "arm weapon" desc = "A grotesque weapon made out of bone and flesh that cleaves through people as a hot knife through butter." @@ -62,8 +70,11 @@ var/weapType = "weapon" var/weapLocation = "arm" - defend_chance = 40 // The base chance for the weapon to parry. - projectile_parry_chance = 15 // The base chance for a projectile to be deflected. + passive_parry = /datum/passive_parry/melee{ + parry_chance_default = 40; + parry_chance_projectile = 15; + parry_frame = /datum/parry_frame/passive_block/armblade; + } /obj/item/melee/changeling/Initialize(mapload) . = ..() @@ -106,28 +117,6 @@ host.embedded -= src qdel(src) -/obj/item/melee/changeling/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(default_parry_check(user, attacker, damage_source) && prob(defend_chance)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(user.loc, 'sound/weapons/slash.ogg', 50, 1) - return 1 - if(unique_parry_check(user, attacker, damage_source) && prob(projectile_parry_chance)) - user.visible_message("\The [user] deflects [attack_text] with \the [src]!") - playsound(user.loc, 'sound/weapons/slash.ogg', 50, 1) - return 1 - - return 0 - -/obj/item/melee/changeling/unique_parry_check(mob/user, mob/attacker, atom/damage_source) - if(user.incapacitated() || !istype(damage_source, /obj/projectile)) - return 0 - - var/bad_arc = global.reverse_dir[user.dir] - if(!check_shield_arc(user, bad_arc, damage_source, attacker)) - return 0 - - return 1 - /obj/item/melee/changeling/arm_blade name = "arm blade" desc = "A grotesque blade made out of bone and flesh that cleaves through people as a hot knife through butter." @@ -138,15 +127,23 @@ edge = 1 pry = 1 attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - defend_chance = 60 - projectile_parry_chance = 25 + + passive_parry = /datum/passive_parry/melee{ + parry_chance_default = 60; + parry_chance_projectile = 25; + parry_frame = /datum/parry_frame/passive_block/armblade; + } /obj/item/melee/changeling/arm_blade/greater name = "arm greatblade" desc = "A grotesque blade made out of bone and flesh that cleaves through people and armor as a hot knife through butter." armor_penetration = 30 - defend_chance = 70 - projectile_parry_chance = 35 + + passive_parry = /datum/passive_parry/melee{ + parry_chance_default = 70; + parry_chance_projectile = 35; + parry_frame = /datum/parry_frame/passive_block/armblade; + } /obj/item/melee/changeling/claw name = "hand claw" @@ -156,13 +153,21 @@ sharp = 1 edge = 1 attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - defend_chance = 50 - projectile_parry_chance = 15 + + passive_parry = /datum/passive_parry/melee{ + parry_chance_default = 50; + parry_chance_projectile = 15; + parry_frame = /datum/parry_frame/passive_block/armblade; + } /obj/item/melee/changeling/claw/greater name = "hand greatclaw" damage_force = 20 armor_penetration = 20 pry = 1 - defend_chance = 60 - projectile_parry_chance = 25 + + passive_parry = /datum/passive_parry/melee{ + parry_chance_default = 60; + parry_chance_projectile = 25; + parry_frame = /datum/parry_frame/passive_block/armblade; + } diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm index 21fc3fd4d9c5..c9e899bd85c5 100644 --- a/code/game/gamemodes/cult/cult_structures.dm +++ b/code/game/gamemodes/cult/cult_structures.dm @@ -46,12 +46,13 @@ /obj/structure/cult/pylon/attackby(obj/item/W as obj, mob/user as mob) attackpylon(user, W.damage_force) -/obj/structure/cult/pylon/inflict_atom_damage(damage, tier, flag, mode, attack_type, datum/weapon, gradual) +/obj/structure/cult/pylon/inflict_atom_damage(damage, damage_type, damage_tier, damage_flag, damage_mode, hit_zone, attack_type, datum/weapon) pylonhit(damage) return damage -/obj/structure/cult/pylon/bullet_act(var/obj/projectile/Proj) - pylonhit(Proj.get_structure_damage()) +/obj/structure/cult/pylon/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + pylonhit(proj.get_structure_damage()) /obj/structure/cult/pylon/proc/pylonhit(var/damage) if(!isbroken) diff --git a/code/game/gamemodes/events/black_hole.dm b/code/game/gamemodes/events/black_hole.dm index 1bc471d0be17..38e926507bd4 100644 --- a/code/game/gamemodes/events/black_hole.dm +++ b/code/game/gamemodes/events/black_hole.dm @@ -11,7 +11,7 @@ /obj/effect/bhole/Initialize(mapload) . = ..() - START_PROCESSING(SSfastprocess, src) + START_PROCESSING(SSprocess_5fps, src) /obj/effect/bhole/process() if(!isturf(loc)) diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm index 3e5e2ef17498..7d7e72d4aa4e 100644 --- a/code/game/gamemodes/meteor/meteors.dm +++ b/code/game/gamemodes/meteor/meteors.dm @@ -162,7 +162,7 @@ if(T) if(istype(T, /turf/simulated/wall)) var/turf/simulated/wall/W = T - W.inflict_atom_damage(wall_power, flag = ARMOR_BOMB) // Stronger walls can halt asteroids. + W.inflict_atom_damage(wall_power, damage_flag = ARMOR_BOMB) // Stronger walls can halt asteroids. /obj/effect/meteor/proc/get_shield_damage() return max(((max(hits, 2)) * (heavy + 1) * rand(6, 12)) / hitpwr , 0) diff --git a/code/game/gamemodes/sandbox/h_sandbox.dm b/code/game/gamemodes/sandbox/h_sandbox.dm index 9ac85720d743..9ecdb22d753d 100644 --- a/code/game/gamemodes/sandbox/h_sandbox.dm +++ b/code/game/gamemodes/sandbox/h_sandbox.dm @@ -139,7 +139,7 @@ datum/hSB continue if(istype(O, /obj/item/dummy)) continue - if(istype(O, /obj/item/melee/energy/sword)) + if(istype(O, /obj/item/melee/transforming/energy/sword)) continue if(istype(O, /obj/structure)) continue diff --git a/code/game/gamemodes/technomancer/devices/shield_armor.dm b/code/game/gamemodes/technomancer/devices/shield_armor.dm index 6bee50e73dd6..73892bfbcc7b 100644 --- a/code/game/gamemodes/technomancer/devices/shield_armor.dm +++ b/code/game/gamemodes/technomancer/devices/shield_armor.dm @@ -33,7 +33,25 @@ qdel(spark_system) return ..() -/obj/item/clothing/suit/armor/shield/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") +/obj/item/clothing/suit/armor/shield/equipped(mob/user, slot, flags) + . = ..() + if(slot == SLOT_ID_HANDS) + return + // if you're reading this: this is not the right way to do shieldcalls + // this is just a lazy implementation + // signals have highest priority, this as a piece of armor shouldn't have that. + RegisterSignal(user, COMSIG_ATOM_SHIELDCALL, PROC_REF(shieldcall)) + +/obj/item/clothing/suit/armor/shield/unequipped(mob/user, slot, flags) + . = ..() + if(slot == SLOT_ID_HANDS) + return + UnregisterSignal(user, COMSIG_ATOM_SHIELDCALL) + +/obj/item/clothing/suit/armor/shield/proc/shieldcall(mob/user, list/shieldcall_args, fake_attack) + var/damage = shieldcall_args[SHIELDCALL_ARG_DAMAGE] + var/damage_source = shieldcall_args[SHIELDCALL_ARG_WEAPON] + //Since this is a pierce of armor that is passive, we do not need to check if the user is incapacitated. if(!active) return 0 @@ -66,12 +84,11 @@ P.agony -= agony_blocked P.damage = P.damage - damage_blocked - user.visible_message("\The [user]'s [src] absorbs [attack_text]!") + user.visible_message("\The [user]'s [src] absorbs the attack!") to_chat(user, "Your shield has absorbed most of \the [damage_source].") spark_system.start() playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) - return 0 // This shield does not block all damage, so returning 0 is needed to tell the game to apply the new damage. /obj/item/clothing/suit/armor/shield/attack_self(mob/user) . = ..() diff --git a/code/game/gamemodes/technomancer/devices/tesla_armor.dm b/code/game/gamemodes/technomancer/devices/tesla_armor.dm index d731b5b37af2..c3fe7f29bc91 100644 --- a/code/game/gamemodes/technomancer/devices/tesla_armor.dm +++ b/code/game/gamemodes/technomancer/devices/tesla_armor.dm @@ -21,7 +21,27 @@ var/normal_icon_state = "tesla_armor_0" var/cooldown_to_charge = 15 SECONDS -/obj/item/clothing/suit/armor/tesla/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") +/obj/item/clothing/suit/armor/tesla/equipped(mob/user, slot, flags) + . = ..() + if(slot == SLOT_ID_HANDS) + return + // if you're reading this: this is not the right way to do shieldcalls + // this is just a lazy implementation + // signals have highest priority, this as a piece of armor shouldn't have that. + RegisterSignal(user, COMSIG_ATOM_SHIELDCALL, PROC_REF(shieldcall)) + +/obj/item/clothing/suit/armor/tesla/unequipped(mob/user, slot, flags) + . = ..() + if(slot == SLOT_ID_HANDS) + return + UnregisterSignal(user, COMSIG_ATOM_SHIELDCALL) + +/obj/item/clothing/suit/armor/tesla/proc/shieldcall(mob/user, list/shieldcall_args, fake_attack) + var/damage_source = shieldcall_args[SHIELDCALL_ARG_WEAPON] + + var/datum/event_args/actor/clickchain/clickchain = shieldcall_args[SHIELDCALL_ARG_CLICKCHAIN] + var/mob/attacker = clickchain?.performer + //First, some retaliation. if(active) if(istype(damage_source, /obj/projectile)) @@ -47,7 +67,7 @@ ready = 1 update_icon() to_chat(user, "\The [src] is ready to protect you once more.") - visible_message("\The [user]'s [src.name] blocks [attack_text]!") + visible_message("\The [user]'s [src.name] blocks the attack!") update_icon() return 1 return 0 diff --git a/code/game/gamemodes/technomancer/spells/energy_siphon.dm b/code/game/gamemodes/technomancer/spells/energy_siphon.dm index cc51d809667d..b05bc7fb2670 100644 --- a/code/game/gamemodes/technomancer/spells/energy_siphon.dm +++ b/code/game/gamemodes/technomancer/spells/energy_siphon.dm @@ -172,30 +172,22 @@ icon_state = "lightning" range = WORLD_ICON_SIZE * 6 power = 5 // This fires really fast, so this may add up if someone keeps standing in the beam. - penetrating = 5 + legacy_penetrating = 5 -/obj/projectile/beam/lightning/energy_siphon/Bump(atom/A as mob|obj|turf|area, forced=0) - if(A == firer) // For this, you CAN shoot yourself. - on_impact(A) - - density = 0 - invisibility = 101 - - qdel(src) - return 1 - ..() - -/obj/projectile/beam/lightning/energy_siphon/projectile_attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier=0) - if(target_mob == firer) // This shouldn't actually occur due to Bump(), but just in-case. - return 1 +/obj/projectile/beam/lightning/energy_siphon/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + var/mob/living/target_mob = target + if(!isliving(target_mob)) + return if(ishuman(target_mob)) // Otherwise someone else stood in the beam and is going to pay for it. var/mob/living/carbon/human/H = target_mob var/obj/item/organ/external/affected = H.get_organ(check_zone(BP_TORSO)) H.electrocute_act(power, src, H.get_siemens_coefficient_organ(affected), affected, 0) else target_mob.electrocute_act(power, src, 0.75, BP_TORSO) - return 0 // Since this is a continous beam, it needs to keep flying until it hits the Technomancer. - + return PROJECTILE_IMPACT_PIERCE #undef SIPHON_CELL_TO_ENERGY #undef SIPHON_FBP_TO_ENERGY diff --git a/code/game/gamemodes/technomancer/spells/projectile/chain_lightning.dm b/code/game/gamemodes/technomancer/spells/projectile/chain_lightning.dm index 8d4254e3f33d..75160bb02526 100644 --- a/code/game/gamemodes/technomancer/spells/projectile/chain_lightning.dm +++ b/code/game/gamemodes/technomancer/spells/projectile/chain_lightning.dm @@ -34,7 +34,15 @@ var/list/hit_mobs = list() //Mobs which were already hit. var/power = 35 //How hard it will hit for with electrocute_act(), decreases with each bounce. -/obj/projectile/beam/chain_lightning/projectile_attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier=0) +// todo: rework this shit :/ + +/obj/projectile/beam/chain_lightning/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & (PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT | PROJECTILE_IMPACT_BLOCKED)) + return + var/mob/living/target_mob = target + if(!isliving(target_mob)) + return //First we shock the guy we just hit. if(ishuman(target_mob)) var/mob/living/carbon/human/H = target_mob @@ -70,11 +78,9 @@ if(new_target) var/turf/curloc = get_turf(target_mob) curloc.visible_message("\The [src] bounces to \the [new_target]!") - redirect(new_target.x, new_target.y, curloc, firer) + legacy_redirect(new_target.x, new_target.y, curloc, firer) bounces-- - - return 0 - return 1 + return PROJECTILE_IMPACT_PIERCE diff --git a/code/game/gamemodes/technomancer/spells/projectile/lightning.dm b/code/game/gamemodes/technomancer/spells/projectile/lightning.dm index 4ba595480de3..58e3bd19292d 100644 --- a/code/game/gamemodes/technomancer/spells/projectile/lightning.dm +++ b/code/game/gamemodes/technomancer/spells/projectile/lightning.dm @@ -32,7 +32,13 @@ var/power = 60 //How hard it will hit for with electrocute_act(). -/obj/projectile/beam/lightning/projectile_attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier=0) +/obj/projectile/beam/lightning/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & (PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT | PROJECTILE_IMPACT_BLOCKED)) + return + var/mob/living/target_mob = target + if(!isliving(target_mob)) + return if(ishuman(target_mob)) var/mob/living/carbon/human/H = target_mob var/obj/item/organ/external/affected = H.get_organ(check_zone(BP_TORSO)) diff --git a/code/game/gamemodes/technomancer/spells/reflect.dm b/code/game/gamemodes/technomancer/spells/reflect.dm index b1bd2526260f..293f5de0827d 100644 --- a/code/game/gamemodes/technomancer/spells/reflect.dm +++ b/code/game/gamemodes/technomancer/spells/reflect.dm @@ -30,64 +30,79 @@ spark_system = null return ..() -/obj/item/spell/reflect/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") +/obj/item/spell/reflect/pickup(mob/user, flags, atom/oldLoc) + . = ..() + // if you're reading this: this is not the right way to do shieldcalls + // this is just a lazy implementation + // signals have highest priority, this as a piece of armor shouldn't have that. + RegisterSignal(user, COMSIG_ATOM_SHIELDCALL, PROC_REF(shieldcall)) + +/obj/item/spell/reflect/dropped(mob/user, flags, atom/newLoc) + . = ..() + UnregisterSignal(user, COMSIG_ATOM_SHIELDCALL) + +/obj/item/spell/reflect/proc/shieldcall(datum/source, list/shieldcall_args, fake_attack) + if(shieldcall_args[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAG_TERMINATE) + return + var/mob/user = source if(user.incapacitated()) - return 0 + return - var/damage_to_energy_cost = (damage_to_energy_multiplier * damage) + var/mob/attacker + var/damage_to_energy_cost = (damage_to_energy_multiplier * shieldcall_args[SHIELDCALL_ARG_DAMAGE]) + var/damage_source = shieldcall_args[SHIELDCALL_ARG_WEAPON] if(!pay_energy(damage_to_energy_cost)) to_chat(owner, "Your shield fades due to lack of energy!") qdel(src) - return 0 - - //block as long as they are not directly behind us - var/bad_arc = global.reverse_dir[user.dir] //arc of directions from which we cannot block - if(check_shield_arc(user, bad_arc, damage_source, attacker)) + return - if(istype(damage_source, /obj/projectile)) - var/obj/projectile/P = damage_source + if(istype(damage_source, /obj/projectile)) + var/obj/projectile/P = damage_source + attacker = P.firer - if(P.starting && !P.reflected) - visible_message("\The [user]'s [src.name] reflects [attack_text]!") + if(P.starting && !P.reflected) + visible_message("\The [user]'s [src.name] reflects [P]!") - var/turf/curloc = get_turf(user) + var/turf/curloc = get_turf(user) - // redirect the projectile - P.redirect(P.starting.x, P.starting.y, curloc, user) - P.reflected = 1 - if(check_for_scepter()) - P.damage = P.damage * 1.5 + // redirect the projectile + P.legacy_redirect(P.starting.x, P.starting.y, curloc, user) + P.reflected = 1 + if(check_for_scepter()) + P.damage = P.damage * 1.5 - spark_system.start() - playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) - // now send a log so that admins don't think they're shooting themselves on purpose. - log_and_message_admins("[user] reflected [attacker]'s attack back at them.") - - if(!reflecting) - reflecting = 1 - spawn(2 SECONDS) //To ensure that most or all of a burst fire cycle is reflected. - to_chat(owner, "Your shield fades due being used up!") - qdel(src) - - return PROJECTILE_CONTINUE // complete projectile permutation - - else if(istype(damage_source, /obj/item)) - var/obj/item/W = damage_source + spark_system.start() + playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) + // now send a log so that admins don't think they're shooting themselves on purpose. if(attacker) - W.melee_interaction_chain(attacker) - to_chat(attacker, "Your [damage_source.name] goes through \the [src] in one location, comes out \ - on the same side, and hits you!") - - spark_system.start() - playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) - log_and_message_admins("[user] reflected [attacker]'s attack back at them.") - if(!reflecting) - reflecting = 1 - spawn(2 SECONDS) //To ensure that most or all of a burst fire cycle is reflected. - to_chat(owner, "Your shield fades due being used up!") - qdel(src) - return 1 - return 0 + if(!reflecting) + reflecting = 1 + spawn(2 SECONDS) //To ensure that most or all of a burst fire cycle is reflected. + to_chat(owner, "Your shield fades due being used up!") + qdel(src) + + shieldcall_args[SHIELDCALL_ARG_FLAGS] |= SHIELDCALL_FLAG_ATTACK_PASSTHROUGH | SHIELDCALL_FLAG_ATTACK_REDIRECT | SHIELDCALL_FLAG_ATTACK_BLOCKED | SHIELDCALL_FLAG_TERMINATE + + else if(istype(damage_source, /obj/item)) + var/obj/item/W = damage_source + var/datum/event_args/actor/clickchain/clickchain = shieldcall_args[SHIELDCALL_ARG_CLICKCHAIN] + attacker = clickchain.performer + if(attacker) + W.melee_interaction_chain(attacker, attacker) + to_chat(attacker, "Your [damage_source] goes through \the [src] in one location, comes out \ + on the same side, and hits you!") + + spark_system.start() + playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) + + log_and_message_admins("[user] reflected [attacker]'s attack back at them.") + + if(!reflecting) + reflecting = 1 + spawn(2 SECONDS) //To ensure that most or all of a burst fire cycle is reflected. + to_chat(owner, "Your shield fades due being used up!") + qdel(src) + shieldcall_args[SHIELDCALL_ARG_FLAGS] |= SHIELDCALL_FLAG_ATTACK_REDIRECT | SHIELDCALL_FLAG_ATTACK_BLOCKED | SHIELDCALL_FLAG_TERMINATE diff --git a/code/game/gamemodes/technomancer/spells/shield.dm b/code/game/gamemodes/technomancer/spells/shield.dm index ad005ef6572b..cd487b6917e3 100644 --- a/code/game/gamemodes/technomancer/spells/shield.dm +++ b/code/game/gamemodes/technomancer/spells/shield.dm @@ -28,7 +28,24 @@ spark_system = null return ..() -/obj/item/spell/shield/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") +/obj/item/spell/shield/equipped(mob/user, slot, flags) + . = ..() + if(slot == SLOT_ID_HANDS) + return + // if you're reading this: this is not the right way to do shieldcalls + // this is just a lazy implementation + // signals have highest priority, this as a piece of armor shouldn't have that. + RegisterSignal(user, COMSIG_ATOM_SHIELDCALL, PROC_REF(shieldcall)) + +/obj/item/spell/shield/unequipped(mob/user, slot, flags) + . = ..() + if(slot == SLOT_ID_HANDS) + return + UnregisterSignal(user, COMSIG_ATOM_SHIELDCALL) + +/obj/item/spell/shield/proc/shieldcall(mob/user, list/shieldcall_args, fake_attack) + var/damage = shieldcall_args[SHIELDCALL_ARG_DAMAGE] + if(user.incapacitated()) return 0 @@ -50,12 +67,8 @@ qdel(src) return 0 - //block as long as they are not directly behind us - var/bad_arc = global.reverse_dir[user.dir] //arc of directions from which we cannot block - if(check_shield_arc(user, bad_arc, damage_source, attacker)) - user.visible_message("\The [user]'s [src] blocks [attack_text]!") - spark_system.start() - playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) - adjust_instability(2) - return 1 - return 0 + + user.visible_message("\The [user]'s [src] blocks the attack!") + spark_system.start() + playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) + adjust_instability(2) diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index dfb3d955096c..3c8879162bf9 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -156,7 +156,7 @@ ///Volume of interface sounds. var/clickvol = 40 var/obj/item/circuitboard/circuit = null - ///If false, SSmachines. If true, SSfastprocess. + ///If false, SSmachines. If true, SSprocess_5fps. var/speed_process = FALSE var/interaction_flags_machine = INTERACT_MACHINE_WIRES_IF_OPEN | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN_SILICON | INTERACT_MACHINE_SET_MACHINE @@ -176,7 +176,7 @@ if(!speed_process) START_MACHINE_PROCESSING(src) else - START_PROCESSING(SSfastprocess, src) + START_PROCESSING(SSprocess_5fps, src) if(!mapload) // area handles this power_change() @@ -186,7 +186,7 @@ if(!speed_process) STOP_MACHINE_PROCESSING(src) else - STOP_PROCESSING(SSfastprocess, src) + STOP_PROCESSING(SSprocess_5fps, src) if(component_parts) for(var/atom/A in component_parts) if(A.loc == src) // If the components are inside the machine, delete them. @@ -236,22 +236,6 @@ panel_open = panel_opened update_appearance() -/obj/machinery/legacy_ex_act(severity) - switch(severity) - if(1.0) - qdel(src) - return - if(2.0) - if(prob(50)) - qdel(src) - return - if(3.0) - if(prob(25)) - qdel(src) - return - else - return - /obj/machinery/vv_edit_var(var_name, new_value) if(var_name == NAMEOF(src, use_power)) update_use_power(new_value) diff --git a/code/game/machinery/doors/airlock/airlock.dm b/code/game/machinery/doors/airlock/airlock.dm index 819a31bca723..541fb37f2597 100644 --- a/code/game/machinery/doors/airlock/airlock.dm +++ b/code/game/machinery/doors/airlock/airlock.dm @@ -909,7 +909,7 @@ About the new airlock wires panel: for(var/turf/turf in locs) for(var/atom/movable/AM in turf) if(AM.airlock_crush(DOOR_CRUSH_DAMAGE)) - inflict_atom_damage(DOOR_CRUSH_DAMAGE, flag = ARMOR_MELEE) + inflict_atom_damage(DOOR_CRUSH_DAMAGE, damage_flag = ARMOR_MELEE) use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people has_beeped = 0 diff --git a/code/game/machinery/doors/defense.dm b/code/game/machinery/doors/defense.dm index eb2c3b434913..850c13bc2a3f 100644 --- a/code/game/machinery/doors/defense.dm +++ b/code/game/machinery/doors/defense.dm @@ -3,5 +3,5 @@ if(exposed_temperature > maxtemperature) var/burndamage = log(RAND_F(0.9, 1.1) * (exposed_temperature - maxtemperature)) - inflict_atom_damage(burndamage, flag = ARMOR_FIRE, gradual = TRUE) + inflict_atom_damage(burndamage, damage_flag = ARMOR_FIRE, damage_mode = DAMAGE_MODE_GRADUAL) return ..() diff --git a/code/game/machinery/doors/unpowered.dm b/code/game/machinery/doors/unpowered.dm index 6236374a1055..1b6b5fb30232 100644 --- a/code/game/machinery/doors/unpowered.dm +++ b/code/game/machinery/doors/unpowered.dm @@ -9,7 +9,7 @@ return /obj/machinery/door/unpowered/attackby(obj/item/I as obj, mob/user as mob) - if(istype(I, /obj/item/melee/energy/blade)) return + if(istype(I, /obj/item/melee/ninja_energy_blade)) return if(src.locked) return ..() return diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 9bdb1357afc6..95921c715c2f 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -194,7 +194,7 @@ return //Emags and ninja swords? You may pass. - if (istype(I, /obj/item/melee/energy/blade)) + if (istype(I, /obj/item/melee/ninja_energy_blade)) if(emag_act(10, user)) var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() spark_system.set_up(5, 0, src.loc) diff --git a/code/game/machinery/fire_alarm.dm b/code/game/machinery/fire_alarm.dm index 1ee973b36652..40757b9a3aae 100644 --- a/code/game/machinery/fire_alarm.dm +++ b/code/game/machinery/fire_alarm.dm @@ -121,8 +121,9 @@ CREATE_WALL_MOUNTING_TYPES_SHIFTED(/obj/machinery/fire_alarm/alarms_hidden, 21) /obj/machinery/fire_alarm/attack_ai(mob/user) return attack_hand(user) -/obj/machinery/fire_alarm/bullet_act() - return alarm() +/obj/machinery/fire_alarm/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + alarm() /obj/machinery/fire_alarm/emp_act(severity) if(prob(50 / severity)) diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm index 77f4f9f052ba..c2d4b3020add 100644 --- a/code/game/machinery/iv_drip.dm +++ b/code/game/machinery/iv_drip.dm @@ -240,7 +240,7 @@ if(!speed_process) START_MACHINE_PROCESSING(src) else - START_PROCESSING(SSfastprocess, src) + START_PROCESSING(SSprocess_5fps, src) update_appearance() //! Plumbing Signal diff --git a/code/game/machinery/turnstile.dm b/code/game/machinery/turnstile.dm index 36e45c60134c..e694c18177b7 100644 --- a/code/game/machinery/turnstile.dm +++ b/code/game/machinery/turnstile.dm @@ -18,11 +18,6 @@ /obj/machinery/turnstile/CanAtmosPass(turf/T) return TRUE -/* -/obj/machinery/turnstile/bullet_act(obj/item/projectile/P, def_zone) - return -1 //Pass through! -*/ - /obj/machinery/turnstile/proc/allowed_access(var/mob/B) if(B.pulledby && ismob(B.pulledby)) return allowed(B.pulledby) | allowed(B) diff --git a/code/game/machinery/turrets/subtypes/misc.dm b/code/game/machinery/turrets/subtypes/misc.dm index 83e75bc795d2..e7ed365d0ea0 100644 --- a/code/game/machinery/turrets/subtypes/misc.dm +++ b/code/game/machinery/turrets/subtypes/misc.dm @@ -75,7 +75,7 @@ integrity_max = 200 turret_type = "industrial" -/obj/machinery/porta_turret/industrial/bullet_act(obj/projectile/Proj) +/obj/machinery/porta_turret/industrial/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) . = ..() if(enabled) if(!attacked && !emagged) diff --git a/code/game/machinery/turrets/turret-ai_holder.dm b/code/game/machinery/turrets/turret-ai_holder.dm index 6c2a3d1a6669..01590029a9e9 100644 --- a/code/game/machinery/turrets/turret-ai_holder.dm +++ b/code/game/machinery/turrets/turret-ai_holder.dm @@ -161,7 +161,8 @@ */ /datum/ai_holder/turret/proc/trace_trajectory(atom/target, angle) var/obj/projectile/trace/trace = new(agent.loc) - trace.prepare_trace(target, null, TRUE) + trace.only_opacity = TRUE + trace.prepare_trace(target) trace.fire(angle) return trace.could_hit_target diff --git a/code/game/machinery/turrets/turret.dm b/code/game/machinery/turrets/turret.dm index 265f3c8afd84..02a1860c9e10 100644 --- a/code/game/machinery/turrets/turret.dm +++ b/code/game/machinery/turrets/turret.dm @@ -463,16 +463,18 @@ if(!gradual && prob(45) && amount > 5) spark_system.start() -/obj/machinery/porta_turret/bullet_act(obj/projectile/P, def_zone) +/obj/machinery/porta_turret/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() aggro_for(6 SECONDS) - if(P.firer) + if(proj.firer) // todo: proper AI provoke API. var/datum/ai_holder/turret/snowflake_ai_holder = ai_holder - snowflake_ai_holder.retaliate(P.firer) - return ..() + snowflake_ai_holder.retaliate(proj.firer) -/obj/machinery/porta_turret/melee_act(mob/user, obj/item/weapon, target_zone, mult) +/obj/machinery/porta_turret/melee_act(mob/user, obj/item/weapon, target_zone, datum/event_args/actor/clickchain/clickchain) . = ..() + if(. & CLICKCHAIN_FLAGS_ATTACK_ABORT) + return if(. > 0) aggro_for(6 SECONDS, user) // todo: proper AI provoke API. diff --git a/code/game/objects/defense.dm b/code/game/objects/defense.dm deleted file mode 100644 index 29786cc77d47..000000000000 --- a/code/game/objects/defense.dm +++ /dev/null @@ -1,82 +0,0 @@ -//* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2023 Citadel Station developers. *// - -/obj/ex_act(power, dir, datum/automata/wave/explosion/E) - . = ..() - // todo: wave explosions - if(temporary_legacy_dont_auto_handle_obj_damage_for_mechs) - return - inflict_atom_damage(power * (1 / 2.5), flag = ARMOR_BOMB) - -/obj/legacy_ex_act(severity, target) - . = ..() - if(temporary_legacy_dont_auto_handle_obj_damage_for_mechs) - return - inflict_atom_damage(global._legacy_ex_atom_damage[severity], flag = ARMOR_BOMB) - -/obj/melee_act(mob/user, obj/item/weapon, target_zone, mult) - if(temporary_legacy_dont_auto_handle_obj_damage_for_mechs) - return - inflict_atom_damage(weapon.damage_force, weapon.damage_tier, weapon.damage_flag, weapon.damage_mode, ATTACK_TYPE_MELEE, weapon) - return NONE - -/obj/unarmed_act(mob/attacker, datum/unarmed_attack/style, target_zone, mult) - // todo: this should just be style.attack(attacker, src) - if(temporary_legacy_dont_auto_handle_obj_damage_for_mechs) - return - inflict_atom_damage(style.get_unarmed_damage(attacker, src), style.damage_tier, style.damage_flag, style.damage_mode, ATTACK_TYPE_UNARMED, attacker) - return NONE - -/obj/bullet_act(obj/projectile/P, def_zone) - . = ..() - // todo: should this really be here? - if(temporary_legacy_dont_auto_handle_obj_damage_for_mechs) - return - inflict_atom_damage(P.get_structure_damage(), P.damage_tier, P.damage_flag, P.damage_mode, ATTACK_TYPE_PROJECTILE, P) - -/obj/throw_impacted(atom/movable/AM, datum/thrownthing/TT) - . = ..() - // todo: /atom/movable/proc/throw_impact_attack(atom/target) - if(temporary_legacy_dont_auto_handle_obj_damage_for_mechs) - return - if(isitem(AM)) - var/obj/item/I = AM - inflict_atom_damage(I.throw_force * TT.get_damage_multiplier(src), TT.get_damage_tier(src), I.damage_flag, I.damage_mode, ATTACK_TYPE_THROWN, AM) - else - inflict_atom_damage(AM.throw_force * TT.get_damage_multiplier(src), TT.get_damage_tier(src), ARMOR_MELEE, null, ATTACK_TYPE_THROWN, AM) - // if we got destroyed - if(QDELETED(src) && (obj_flags & OBJ_ALLOW_THROW_THROUGH)) - . |= COMPONENT_THROW_HIT_PIERCE - -/obj/blob_act(obj/structure/blob/blob) - . = ..() - inflict_atom_damage(100, flag = ARMOR_MELEE, attack_type = ATTACK_TYPE_MELEE) - -/obj/drop_products(method, atom/where) - . = ..() - if(obj_storage?.drop_on_deconstruction_methods & method) - obj_storage.drop_everything_at(where) - -/obj/hitsound_melee(obj/item/I) - if(!isnull(material_primary)) - var/datum/material/primary = get_primary_material() - . = I.damtype == BURN? primary.sound_melee_burn : primary.sound_melee_brute - if(!isnull(.)) - return - return ..() - -/obj/hitsound_throwhit(obj/item/I) - if(!isnull(material_primary)) - var/datum/material/primary = get_primary_material() - . = I.damtype == BURN? primary.sound_melee_burn : primary.sound_melee_brute - if(!isnull(.)) - return - return ..() - -/obj/hitsound_unarmed(mob/M, datum/unarmed_attack/style) - if(!isnull(material_primary)) - var/datum/material/primary = get_primary_material() - . = style.damage_type == BURN? primary.sound_melee_burn : primary.sound_melee_brute - if(!isnull(.)) - return - return ..() diff --git a/code/game/objects/effects/_effect.dm b/code/game/objects/effects/_effect.dm index f2f3ff7e6f1b..14ae0396a50e 100644 --- a/code/game/objects/effects/_effect.dm +++ b/code/game/objects/effects/_effect.dm @@ -10,6 +10,7 @@ * however, at a certain point, do consider using /structure or /machinery instead. */ /obj/effect + anchored = TRUE integrity_enabled = FALSE density = FALSE opacity = FALSE diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm index bc62c5021dc1..5e949319b074 100644 --- a/code/game/objects/effects/effect_system.dm +++ b/code/game/objects/effects/effect_system.dm @@ -406,7 +406,7 @@ steam.start() -- spawns the effect return if(!ismovable(holder)) return - START_PROCESSING(SSfastprocess, src) + START_PROCESSING(SSprocess_5fps, src) on = TRUE /datum/effect_system/ion_trail_follow/process(wait) @@ -425,7 +425,7 @@ steam.start() -- spawns the effect return oldposition = null on = FALSE - STOP_PROCESSING(SSfastprocess, src) + STOP_PROCESSING(SSprocess_5fps, src) ///////////////////////////////////////////// //////// Attach a steam trail to an object (eg. a reacting beaker) that will follow it diff --git a/code/game/objects/effects/item_pickup_ghost.dm b/code/game/objects/effects/item_pickup_ghost.dm index 0944e2753eea..58675c8ff70a 100644 --- a/code/game/objects/effects/item_pickup_ghost.dm +++ b/code/game/objects/effects/item_pickup_ghost.dm @@ -1,11 +1,17 @@ /obj/effect/temporary_effect/item_pickup_ghost - anchored = 1 plane = MOB_PLANE layer = ABOVE_MOB_LAYER - mouse_opacity = 0//just in case something dumb happens + mouse_opacity = MOUSE_OPACITY_TRANSPARENT time_to_die = 0.5 SECONDS var/lifetime = 0.25 SECONDS //so it doesn't die before animation ends +/obj/effect/temporary_effect/item_pickup_ghost/Initialize(mapload, obj/item/from_item, atom/towards_target) + . = ..() + if(from_item) + assumeform(from_item) + if(towards_target) + animate_towards(towards_target) + /obj/effect/temporary_effect/item_pickup_ghost/proc/assumeform(var/obj/item/picked_up) icon = picked_up.icon icon_state = picked_up.icon_state diff --git a/code/game/objects/effects/mines.dm b/code/game/objects/effects/mines.dm index 88e0f9904874..8338325c57e6 100644 --- a/code/game/objects/effects/mines.dm +++ b/code/game/objects/effects/mines.dm @@ -18,6 +18,8 @@ wires = new(src) /obj/effect/mine/proc/explode(var/mob/living/M) + if(QDELETED(src)) + return var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread() triggered = 1 s.set_up(3, 1, src) @@ -27,8 +29,9 @@ qdel(s) qdel(src) -/obj/effect/mine/bullet_act() - if(prob(50)) +/obj/effect/mine/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + spawn(0) explode() /obj/effect/mine/legacy_ex_act(severity) @@ -167,7 +170,7 @@ var/turf/O = get_turf(src) if(!O) return - src.fragmentate(O, 20, 7, list(/obj/projectile/bullet/pellet/fragment)) //only 20 weak fragments because you're stepping directly on it + shrapnel_explosion(20, 7, /obj/projectile/bullet/pellet/fragment) visible_message("\The [src.name] detonates!") spawn(0) qdel(s) diff --git a/code/game/objects/effects/misc.dm b/code/game/objects/effects/misc.dm index 1ce7e665ac46..d03e8a0d3c43 100644 --- a/code/game/objects/effects/misc.dm +++ b/code/game/objects/effects/misc.dm @@ -116,25 +116,5 @@ icon_state = "begin" anchored = TRUE -/obj/effect/list_container - name = "list container" - -/obj/effect/list_container/mobl - name = "mobl" - var/master = null - - var/list/container = list( ) - -/obj/effect/stop - icon_state = "empty" - name = "Geas" - desc = "You can't resist." - var/atom/movable/victim - -/obj/effect/stop/Uncross(atom/movable/AM) - . = ..() - if(AM == victim) - return FALSE - /obj/effect/spawner name = "object spawner" diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index 557cd497a544..a0b957a07837 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -15,10 +15,10 @@ if(exposed_temperature > 300 + T0C) damage_integrity(5) -/obj/effect/spider/melee_act(mob/user, obj/item/weapon, target_zone, mult) - if(weapon.damtype == BURN) - mult *= 2 - return ..() +/obj/effect/spider/process_damage_instance(list/shieldcall_args, filter_zone) + . = ..() + if(shieldcall_args[SHIELDCALL_ARG_DAMAGE_TYPE]) + shieldcall_args[SHIELDCALL_ARG_DAMAGE] *= 2 /obj/effect/spider/stickyweb icon_state = "stickyweb1" diff --git a/code/game/objects/effects/traps.dm b/code/game/objects/effects/traps.dm index 01a3f46511bd..42855a8026c5 100644 --- a/code/game/objects/effects/traps.dm +++ b/code/game/objects/effects/traps.dm @@ -554,7 +554,7 @@ Add those other swinging traps you mentioned above! /* This is all per-tick processing stuff. It isn't working the way I want, so I'm reverting it. if (istype(AM, /mob/living)) - START_PROCESSING(SSfastprocess, src) + START_PROCESSING(SSprocess_5fps, src) var/mob/living/M = AM M.visible_message("[M] is slashed by the spinning blades!", \ "You are slashed by the spinning blades!") @@ -565,7 +565,7 @@ if (istype(AM, /mob/living)) M.apply_damage(damage, BRUTE) /obj/effect/trap/pop_up/pillar/Destroy() - STOP_PROCESSING(SSfastprocess, src) + STOP_PROCESSING(SSprocess_5fps, src) return ..() */ diff --git a/code/game/objects/items-carry_weight.dm b/code/game/objects/items-carry_weight.dm new file mode 100644 index 000000000000..f26b7114f7f3 --- /dev/null +++ b/code/game/objects/items-carry_weight.dm @@ -0,0 +1,74 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +//* Carry Weight *// +//* The carry weight system is a modular system used to discourage *// +//* carrying too much, through both weight (recursive weight) and *// +//* encumbrance (only on the stuff worn, and potentially also in hand) *// + +/obj/item/proc/get_weight() + return weight + obj_storage?.get_containing_weight() + +/obj/item/proc/get_encumbrance() + return encumbrance + +/obj/item/proc/get_flat_encumbrance() + return flat_encumbrance + +/obj/item/proc/update_weight() + if(isnull(weight_registered)) + return null + . = get_weight() + if(. == weight_registered) + return 0 + . -= weight_registered + weight_registered += . + var/mob/living/wearer = worn_mob() + if(istype(wearer)) + wearer.adjust_current_carry_weight(.) + +/obj/item/proc/update_encumbrance() + if(isnull(encumbrance_registered)) + return null + . = get_encumbrance() + if(. == encumbrance_registered) + return 0 + . -= encumbrance_registered + encumbrance_registered += . + var/mob/living/wearer = worn_mob() + if(istype(wearer)) + wearer.adjust_current_carry_encumbrance(.) + +/obj/item/proc/update_flat_encumbrance() + var/mob/living/wearer = worn_mob() + if(istype(wearer)) + wearer.recalculate_carry() + +/obj/item/proc/set_weight(amount) + if(amount == weight) + return + var/old = weight + weight = amount + update_weight() + propagate_weight(old, weight) + +/obj/item/proc/set_encumbrance(amount) + if(amount == encumbrance) + return + encumbrance = amount + update_encumbrance() + +/obj/item/proc/set_flat_encumbrance(amount) + if(amount == flat_encumbrance) + return + flat_encumbrance = amount + update_flat_encumbrance() + +/obj/item/proc/set_slowdown(amount) + if(amount == slowdown) + return + slowdown = amount + worn_mob()?.update_item_slowdown() + +/obj/item/proc/propagate_weight(old_weight, new_weight) + loc?.on_contents_weight_change(src, old_weight, new_weight) diff --git a/code/game/objects/items-defense.dm b/code/game/objects/items-defense.dm new file mode 100644 index 000000000000..4ad604fcbcd6 --- /dev/null +++ b/code/game/objects/items-defense.dm @@ -0,0 +1,12 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +//* Armor *// + +/** + * Called during a mob armor call cycle + */ +/obj/item/proc/mob_armorcall(mob/defending, list/shieldcall_args, fake_attack) + // use our own armor + var/datum/armor/our_armor = fetch_armor() + our_armor.handle_shieldcall(shieldcall_args, fake_attack) diff --git a/code/game/objects/items-interaction.dm b/code/game/objects/items-interaction.dm index 8e557d574c81..a1b37b7c17c2 100644 --- a/code/game/objects/items-interaction.dm +++ b/code/game/objects/items-interaction.dm @@ -1,6 +1,165 @@ //* This file is explicitly licensed under the MIT license. *// //* Copyright (c) 2024 silicons *// +//* Attack Hand *// + +/obj/item/on_attack_hand(datum/event_args/actor/clickchain/e_args) + . = ..() + if(.) + return + + if(e_args.performer.is_in_inventory(src)) + if(e_args.performer.is_holding(src)) + if(obj_storage?.allow_open_via_offhand_click && obj_storage.auto_handle_interacted_open(e_args)) + return TRUE + else + if(obj_storage?.allow_open_via_equipped_click && obj_storage.auto_handle_interacted_open(e_args)) + return TRUE + if(!e_args.performer.is_holding(src)) + if(should_attempt_pickup(e_args) && attempt_pickup(e_args.performer)) + return TRUE + +/obj/item/proc/should_attempt_pickup(datum/event_args/actor/actor) + return TRUE + +/** + * @params + * * actor - (optional) person doing it + * * silent - suppress feedback + */ +/obj/item/proc/should_allow_pickup(datum/event_args/actor/actor, silent) + if(anchored) + if(!silent) + actor?.chat_feedback( + SPAN_NOTICE("\The [src] won't budge, you can't pick it up!"), + target = src, + ) + return FALSE + return TRUE + +/obj/item/proc/attempt_pickup(mob/user) + . = TRUE + if (!user) + return + + if(!should_allow_pickup(new /datum/event_args/actor(user))) + return FALSE + + if(!CHECK_MOBILITY(user, MOBILITY_CAN_PICKUP)) + user.action_feedback(SPAN_WARNING("You can't do that right now."), src) + return + + // todo: rewrite this part iin hand rewrite + if (hasorgans(user)) + var/mob/living/carbon/human/H = user + var/obj/item/organ/external/temp = H.organs_by_name[H.hand? "l_hand" : "r_hand"] + if(!temp) + to_chat(user, "You try to use your hand, but realize it is no longer attached!") + return + if(!temp.is_usable()) + to_chat(user, "You try to move your [temp.name], but cannot!") + return + + var/old_loc = src.loc + var/obj/item/actually_picked_up = src + var/has_to_drop_to_ground_on_fail = FALSE + + if(isturf(old_loc)) + // if picking up from floor + throwing?.terminate() + else if(item_flags & ITEM_IN_STORAGE) + // trying to take out of backpack + var/datum/object_system/storage/resolved + if(istype(loc, /atom/movable/storage_indirection)) + var/atom/movable/storage_indirection/holder = loc + resolved = holder.parent + else if(isobj(loc)) + var/obj/obj_loc = loc + resolved = obj_loc.obj_storage + if(isnull(resolved)) + item_flags &= ~ITEM_IN_STORAGE + CRASH("in storage at [loc] ([REF(loc)]) ([loc?.type || "NULL"]) but cannot resolve storage system") + actually_picked_up = resolved.try_remove(src, user, new /datum/event_args/actor(user)) + // they're in user, but not equipped now. this is so it doesn't touch the ground first. + has_to_drop_to_ground_on_fail = TRUE + + if(isnull(actually_picked_up)) + to_chat(user, SPAN_WARNING("[src] somehow slips through your grasp. What just happened?")) + return + if(!user.put_in_hands(actually_picked_up)) + if(has_to_drop_to_ground_on_fail) + actually_picked_up.forceMove(user.drop_location()) + return + // success + if(isturf(old_loc)) + new /obj/effect/temporary_effect/item_pickup_ghost(old_loc, actually_picked_up, user) + +//* Drag / Drop *// + +/obj/item/OnMouseDrop(atom/over, mob/user, proximity, params) + . = ..() + if(. & CLICKCHAIN_DO_NOT_PROPAGATE) + return + if(anchored) // Don't. + return + if(user.restrained()) + return // don't. + // todo: restraint levels, e.g. handcuffs vs straightjacket + if(!user.is_in_inventory(src)) + // not being held + if(!isturf(loc)) // yea nah + return ..() + if(user.Adjacent(src)) + // check for equip + if(istype(over, /atom/movable/screen/inventory/hand)) + var/atom/movable/screen/inventory/hand/H = over + user.put_in_hand(src, H.index) + return CLICKCHAIN_DO_NOT_PROPAGATE + else if(istype(over, /atom/movable/screen/inventory/slot)) + var/atom/movable/screen/inventory/slot/S = over + user.equip_to_slot_if_possible(src, S.slot_id) + return CLICKCHAIN_DO_NOT_PROPAGATE + // check for slide + if(Adjacent(over) && user.CanSlideItem(src, over) && (istype(over, /obj/structure/table/rack) || istype(over, /obj/structure/table) || istype(over, /turf))) + var/turf/old = get_turf(src) + if(over == old) // same tile don't bother + return CLICKCHAIN_DO_NOT_PROPAGATE + if(!Move(get_turf(over))) + return CLICKCHAIN_DO_NOT_PROPAGATE + //! todo: i want to strangle the mofo who did planes instead of invisibility, which makes it computationally infeasible to check ghost invisibility in get hearers in view + //! :) FUCK YOU. + //! this if check is all for you. FUCK YOU. + if(!isobserver(user)) + user.visible_message(SPAN_NOTICE("[user] slides [src] over."), SPAN_NOTICE("You slide [src] over."), range = MESSAGE_RANGE_COMBAT_SUBTLE) + log_inventory("[user] slid [src] from [COORD(old)] to [COORD(over)]") + return CLICKCHAIN_DO_NOT_PROPAGATE + else + // being held, check for attempt unequip + if(istype(over, /atom/movable/screen/inventory/hand)) + var/atom/movable/screen/inventory/hand/H = over + user.put_in_hand(src, H.index) + return CLICKCHAIN_DO_NOT_PROPAGATE + else if(istype(over, /atom/movable/screen/inventory/slot)) + var/atom/movable/screen/inventory/slot/S = over + user.equip_to_slot_if_possible(src, S.slot_id) + return CLICKCHAIN_DO_NOT_PROPAGATE + else if(istype(over, /turf)) + user.drop_item_to_ground(src) + return CLICKCHAIN_DO_NOT_PROPAGATE + +// funny! +// todo: move to mob files +/mob/proc/CanSlideItem(obj/item/I, turf/over) + return FALSE + +// todo: move to mob files +/mob/living/CanSlideItem(obj/item/I, turf/over) + return Adjacent(I) && !incapacitated() && !stat && !restrained() + +// todo: move to mob files +/mob/observer/dead/CanSlideItem(obj/item/I, turf/over) + return is_spooky + //* Inhand Triggers *// /** diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index bd331dd7d3a3..e61d13a64890 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -27,7 +27,18 @@ /// the action will check for var/item_action_mobility_flags = MOBILITY_CAN_HOLD | MOBILITY_CAN_USE - //? Flags + //* Combat *// + /// passive parry data / frame + /// + /// * anonymous typepath is allowed + /// * typepath is allowed + /// * instance is allowed + /// + /// note that the component will not be modified while held; + /// if this is changed, the component needs to be remade. + var/passive_parry + + //* Flags *// /// Item flags. /// These flags are listed in [code/__DEFINES/inventory/item_flags.dm]. var/item_flags = ITEM_ENCUMBERS_WHILE_HELD @@ -423,14 +434,6 @@ /obj/item/ui_action_click(datum/action/action, datum/event_args/actor/actor) attack_self(usr) -//RETURN VALUES -//handle_shield should return a positive value to indicate that the attack is blocked and should be prevented. -//If a negative value is returned, it should be treated as a special return value for bullet_act() and handled appropriately. -//For non-projectile attacks this usually means the attack is blocked. -//Otherwise should return 0 to indicate that the attack is not affected in any way. -/obj/item/proc/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - return 0 - /obj/item/proc/get_loc_turf() var/atom/L = loc while(L && !istype(L, /turf/)) @@ -526,7 +529,7 @@ if (!..()) return 0 - if(istype(src, /obj/item/melee/energy)) + if(istype(src, /obj/item/melee/transforming/energy)) return //if we haven't made our blood_overlay already @@ -759,27 +762,6 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. var/datum/action/action = item_actions action.revoke(user.inventory.actions) -//* Armor *// - -/** - * called to be checked for mob armor - * - * @returns copy of args with modified values - */ -/obj/item/proc/checking_mob_armor(damage, tier, flag, mode, attack_type, datum/weapon, target_zone) - damage = fetch_armor().resultant_damage(damage, tier, flag) - return args.Copy() - -/** - * called to be used as mob armor - * side effects are allowed - * - * @returns copy of args with modified values - */ -/obj/item/proc/running_mob_armor(damage, tier, flag, mode, attack_type, datum/weapon, target_zone) - damage = fetch_armor().resultant_damage(damage, tier, flag) - return args.Copy() - //* Attack *// /** @@ -830,7 +812,20 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. /obj/item/proc/is_shred(strict) return (damage_mode & DAMAGE_MODE_SHRED) -//* Interaction *// +//* Combat *// + +/obj/item/proc/load_passive_parry() + if(!passive_parry) + return + passive_parry = resolve_passive_parry_data(passive_parry) + var/datum/component/passive_parry/loaded = GetComponent(/datum/component/passive_parry) + if(loaded) + loaded.parry_data = passive_parry + +/obj/item/proc/reload_passive_parry() + load_passive_parry() + +//* Interactions *// /obj/item/attackby(obj/item/I, mob/user, list/params, clickchain_flags, damage_multiplier) if(isturf(loc) && I.obj_storage?.allow_mass_gather && I.obj_storage.allow_mass_gather_via_click) @@ -859,237 +854,8 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. /obj/item/proc/attacksound_override(atom/target, attack_type) return -//* Carry Weight *// - -/obj/item/proc/get_weight() - return weight + obj_storage?.get_containing_weight() - -/obj/item/proc/get_encumbrance() - return encumbrance - -/obj/item/proc/get_flat_encumbrance() - return flat_encumbrance - -/obj/item/proc/update_weight() - if(isnull(weight_registered)) - return null - . = get_weight() - if(. == weight_registered) - return 0 - . -= weight_registered - weight_registered += . - var/mob/living/wearer = worn_mob() - if(istype(wearer)) - wearer.adjust_current_carry_weight(.) - -/obj/item/proc/update_encumbrance() - if(isnull(encumbrance_registered)) - return null - . = get_encumbrance() - if(. == encumbrance_registered) - return 0 - . -= encumbrance_registered - encumbrance_registered += . - var/mob/living/wearer = worn_mob() - if(istype(wearer)) - wearer.adjust_current_carry_encumbrance(.) - -/obj/item/proc/update_flat_encumbrance() - var/mob/living/wearer = worn_mob() - if(istype(wearer)) - wearer.recalculate_carry() - -/obj/item/proc/set_weight(amount) - if(amount == weight) - return - var/old = weight - weight = amount - update_weight() - propagate_weight(old, weight) - -/obj/item/proc/set_encumbrance(amount) - if(amount == encumbrance) - return - encumbrance = amount - update_encumbrance() - -/obj/item/proc/set_flat_encumbrance(amount) - if(amount == flat_encumbrance) - return - flat_encumbrance = amount - update_flat_encumbrance() - -/obj/item/proc/set_slowdown(amount) - if(amount == slowdown) - return - slowdown = amount - worn_mob()?.update_item_slowdown() - -/obj/item/proc/propagate_weight(old_weight, new_weight) - loc?.on_contents_weight_change(src, old_weight, new_weight) - -//* Interactions *// - -/obj/item/on_attack_hand(datum/event_args/actor/clickchain/e_args) - . = ..() - if(.) - return - - if(e_args.performer.is_in_inventory(src)) - if(e_args.performer.is_holding(src)) - if(obj_storage?.allow_open_via_offhand_click && obj_storage.auto_handle_interacted_open(e_args)) - return TRUE - else - if(obj_storage?.allow_open_via_equipped_click && obj_storage.auto_handle_interacted_open(e_args)) - return TRUE - if(!e_args.performer.is_holding(src)) - if(should_attempt_pickup(e_args) && attempt_pickup(e_args.performer)) - return TRUE - -/obj/item/OnMouseDrop(atom/over, mob/user, proximity, params) - . = ..() - if(. & CLICKCHAIN_DO_NOT_PROPAGATE) - return - if(anchored) // Don't. - return - if(user.restrained()) - return // don't. - // todo: restraint levels, e.g. handcuffs vs straightjacket - if(!user.is_in_inventory(src)) - // not being held - if(!isturf(loc)) // yea nah - return ..() - if(user.Adjacent(src)) - // check for equip - if(istype(over, /atom/movable/screen/inventory/hand)) - var/atom/movable/screen/inventory/hand/H = over - user.put_in_hand(src, H.index) - return CLICKCHAIN_DO_NOT_PROPAGATE - else if(istype(over, /atom/movable/screen/inventory/slot)) - var/atom/movable/screen/inventory/slot/S = over - user.equip_to_slot_if_possible(src, S.slot_id) - return CLICKCHAIN_DO_NOT_PROPAGATE - // check for slide - if(Adjacent(over) && user.CanSlideItem(src, over) && (istype(over, /obj/structure/table/rack) || istype(over, /obj/structure/table) || istype(over, /turf))) - var/turf/old = get_turf(src) - if(over == old) // same tile don't bother - return CLICKCHAIN_DO_NOT_PROPAGATE - if(!Move(get_turf(over))) - return CLICKCHAIN_DO_NOT_PROPAGATE - //! todo: i want to strangle the mofo who did planes instead of invisibility, which makes it computationally infeasible to check ghost invisibility in get hearers in view - //! :) FUCK YOU. - //! this if check is all for you. FUCK YOU. - if(!isobserver(user)) - user.visible_message(SPAN_NOTICE("[user] slides [src] over."), SPAN_NOTICE("You slide [src] over."), range = MESSAGE_RANGE_COMBAT_SUBTLE) - log_inventory("[user] slid [src] from [COORD(old)] to [COORD(over)]") - return CLICKCHAIN_DO_NOT_PROPAGATE - else - // being held, check for attempt unequip - if(istype(over, /atom/movable/screen/inventory/hand)) - var/atom/movable/screen/inventory/hand/H = over - user.put_in_hand(src, H.index) - return CLICKCHAIN_DO_NOT_PROPAGATE - else if(istype(over, /atom/movable/screen/inventory/slot)) - var/atom/movable/screen/inventory/slot/S = over - user.equip_to_slot_if_possible(src, S.slot_id) - return CLICKCHAIN_DO_NOT_PROPAGATE - else if(istype(over, /turf)) - user.drop_item_to_ground(src) - return CLICKCHAIN_DO_NOT_PROPAGATE - -// funny! -// todo: move to mob files -/mob/proc/CanSlideItem(obj/item/I, turf/over) - return FALSE - -// todo: move to mob files -/mob/living/CanSlideItem(obj/item/I, turf/over) - return Adjacent(I) && !incapacitated() && !stat && !restrained() - -// todo: move to mob files -/mob/observer/dead/CanSlideItem(obj/item/I, turf/over) - return is_spooky - //* Inventory *// -/obj/item/proc/should_attempt_pickup(datum/event_args/actor/actor) - return TRUE - -/** - * @params - * * actor - (optional) person doing it - * * silent - suppress feedback - */ -/obj/item/proc/should_allow_pickup(datum/event_args/actor/actor, silent) - if(anchored) - if(!silent) - actor?.chat_feedback( - SPAN_NOTICE("\The [src] won't budge, you can't pick it up!"), - target = src, - ) - return FALSE - return TRUE - -/obj/item/proc/attempt_pickup(mob/user) - . = TRUE - if (!user) - return - - if(!should_allow_pickup(new /datum/event_args/actor(user))) - return FALSE - - if(!CHECK_MOBILITY(user, MOBILITY_CAN_PICKUP)) - user.action_feedback(SPAN_WARNING("You can't do that right now."), src) - return - - if (hasorgans(user)) - var/mob/living/carbon/human/H = user - var/obj/item/organ/external/temp = H.organs_by_name["r_hand"] - if (H.hand) - temp = H.organs_by_name["l_hand"] - if(temp && !temp.is_usable()) - to_chat(user, "You try to move your [temp.name], but cannot!") - return - if(!temp) - to_chat(user, "You try to use your hand, but realize it is no longer attached!") - return - - var/old_loc = src.loc - var/obj/item/actually_picked_up = src - var/has_to_drop_to_ground_on_fail = FALSE - - if(isturf(old_loc)) - // if picking up from floor - throwing?.terminate() - else if(item_flags & ITEM_IN_STORAGE) - // trying to take out of backpack - var/datum/object_system/storage/resolved - if(istype(loc, /atom/movable/storage_indirection)) - var/atom/movable/storage_indirection/holder = loc - resolved = holder.parent - else if(isobj(loc)) - var/obj/obj_loc = loc - resolved = obj_loc.obj_storage - if(isnull(resolved)) - item_flags &= ~ITEM_IN_STORAGE - CRASH("in storage at [loc] ([REF(loc)]) ([loc?.type || "NULL"]) but cannot resolve storage system") - actually_picked_up = resolved.try_remove(src, user, new /datum/event_args/actor(user)) - // they're in user, but not equipped now. this is so it doesn't touch the ground first. - has_to_drop_to_ground_on_fail = TRUE - - if(isnull(actually_picked_up)) - to_chat(user, SPAN_WARNING("[src] somehow slips through your grasp. What just happened?")) - return - if(!user.put_in_hands(actually_picked_up)) - if(has_to_drop_to_ground_on_fail) - actually_picked_up.forceMove(user.drop_location()) - return - // success - if(isturf(old_loc)) - var/obj/effect/temporary_effect/item_pickup_ghost/ghost = new(old_loc) - ghost.assumeform(actually_picked_up) - ghost.animate_towards(user) - /** * Called when someone clisk us on a storage, before the storage handler's * 'put item in' runs. Return FALSE to deny. @@ -1172,8 +938,18 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. //* VV *// +/obj/item/vv_get_var(var_name, resolve) + switch(var_name) + if(NAMEOF(src, passive_parry)) + if(resolve) + load_passive_parry() + return ..() + /obj/item/vv_edit_var(var_name, var_value, mass_edit, raw_edit) switch(var_name) + if(NAMEOF(src, passive_parry)) + . = ..() + reload_passive_parry() if(NAMEOF(src, item_flags)) var/requires_update = (item_flags & (ITEM_ENCUMBERS_WHILE_HELD | ITEM_ENCUMBERS_ONLY_HELD)) != (var_value & (ITEM_ENCUMBERS_WHILE_HELD | ITEM_ENCUMBERS_ONLY_HELD)) . = ..() @@ -1193,7 +969,7 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. L.update_carry_slowdown() if(NAMEOF(src, slowdown)) . = ..() - if(. ) + if(.) var/mob/living/L = worn_mob() // check, as worn_mob() returns /mob, not /living if(istype(L)) diff --git a/code/game/objects/items/contraband.dm b/code/game/objects/items/contraband.dm index a54ee11a41e7..e8e86f7fc15e 100644 --- a/code/game/objects/items/contraband.dm +++ b/code/game/objects/items/contraband.dm @@ -109,12 +109,12 @@ /obj/item/grenade/flashbang/clusterbang, /obj/item/grenade/flashbang/clusterbang, /obj/item/grenade/spawnergrenade/spesscarp, - /obj/item/melee/energy/sword/ionic_rapier, + /obj/item/melee/transforming/energy/sword/ionic_rapier, /obj/item/clothing/shoes/syndigaloshes, /obj/item/storage/backpack/dufflebag/syndie, /obj/item/binoculars, /obj/item/storage/firstaid/combat, - /obj/item/melee/energy/sword, + /obj/item/melee/transforming/energy/sword, /obj/item/melee/telebaton, /obj/item/pen/reagent/paralysis, /obj/item/pickaxe/diamonddrill, @@ -124,7 +124,7 @@ /obj/item/reagent_containers/food/snacks/xenomeat, /obj/item/reagent_containers/glass/beaker/neurotoxin, /obj/item/hardsuit/combat, - /obj/item/shield/energy, + /obj/item/shield/transforming/energy, /obj/item/stamp/centcom, /obj/item/stamp/oricon, /obj/item/storage/fancy/cigar/havana, @@ -217,13 +217,13 @@ /obj/item/grenade/flashbang/clusterbang, /obj/item/grenade/flashbang/clusterbang, /obj/item/grenade/spawnergrenade/spesscarp, - /obj/item/melee/energy/sword, + /obj/item/melee/transforming/energy/sword, /obj/item/melee/telebaton, /obj/item/pen/reagent/paralysis, /obj/item/pickaxe/diamonddrill, /obj/item/reagent_containers/glass/beaker/neurotoxin, /obj/item/hardsuit/combat, - /obj/item/shield/energy, + /obj/item/shield/transforming/energy, /obj/item/stamp/centcom, /obj/item/stamp/oricon, /obj/item/storage/fancy/cigar/havana, diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm index 40a96b4cd4f0..366b6f29a8ae 100644 --- a/code/game/objects/items/devices/chameleonproj.dm +++ b/code/game/objects/items/devices/chameleonproj.dm @@ -120,10 +120,10 @@ to_chat(M, "Your chameleon-projector deactivates.") master.disrupt() -/obj/effect/dummy/chameleon/bullet_act() +/obj/effect/dummy/chameleon/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() for(var/mob/M in src) to_chat(M, "Your chameleon-projector deactivates.") - ..() master.disrupt() /obj/effect/dummy/chameleon/relaymove(var/mob/user, direction) diff --git a/code/game/objects/items/devices/gps.dm b/code/game/objects/items/devices/gps.dm index a2712ccf6d4f..009374244b8b 100644 --- a/code/game/objects/items/devices/gps.dm +++ b/code/game/objects/items/devices/gps.dm @@ -212,7 +212,7 @@ hud_bound?.add_screen(hud_arrow) hud_arrow.set_disabled(FALSE) update_tracking() - START_PROCESSING(SSfastprocess, src) + START_PROCESSING(SSprocess_5fps, src) return TRUE /** @@ -225,7 +225,7 @@ tracking = null // just kick it out hud_arrow?.set_disabled(TRUE) - STOP_PROCESSING(SSfastprocess, src) + STOP_PROCESSING(SSprocess_5fps, src) return TRUE /obj/item/gps/process(delta_time) diff --git a/code/game/objects/items/devices/spy_bug.dm b/code/game/objects/items/devices/spy_bug.dm index 8165600c4cb9..fd10b1265039 100644 --- a/code/game/objects/items/devices/spy_bug.dm +++ b/code/game/objects/items/devices/spy_bug.dm @@ -109,14 +109,15 @@ qdel(src) ..() -/obj/item/camerabug/bullet_act() +/obj/item/camerabug/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return visible_message("The [src] lens shatters!") new brokentype(get_turf(src)) if(linkedmonitor) linkedmonitor.unpair(src) linkedmonitor = null - spawn(0) - qdel(src) /obj/item/camerabug/Destroy() if(linkedmonitor) diff --git a/code/game/objects/items/id_cards/station_ids.dm b/code/game/objects/items/id_cards/station_ids.dm index f14e2d1b912f..ccbca7e089f1 100644 --- a/code/game/objects/items/id_cards/station_ids.dm +++ b/code/game/objects/items/id_cards/station_ids.dm @@ -71,7 +71,6 @@ . += "" if(Adjacent(user)) . += SPAN_NOTICE("Alt-click to [extra_info_visible ? "close" : "open"] the confidential information flap.") - return . /obj/item/card/id/get_description_info() . = ..() diff --git a/code/game/objects/items/latexballoon.dm b/code/game/objects/items/latexballoon.dm index 8c5ecb1731b0..5b469f669e58 100644 --- a/code/game/objects/items/latexballoon.dm +++ b/code/game/objects/items/latexballoon.dm @@ -30,16 +30,8 @@ item_state = "lgloves" loc.assume_air(air_contents) -/obj/item/latexballon/legacy_ex_act(severity) - burst() - switch(severity) - if (1) - qdel(src) - if (2) - if (prob(50)) - qdel(src) - -/obj/item/latexballon/bullet_act() +/obj/item/latexballon/inflict_atom_damage(damage, damage_type, damage_tier, damage_flag, damage_mode, hit_zone, attack_type, datum/weapon) + . = ..() burst() /obj/item/latexballon/fire_act(datum/gas_mixture/air, temperature, volume) diff --git a/code/game/objects/items/melee/melee.dm b/code/game/objects/items/melee/melee.dm new file mode 100644 index 000000000000..ec0b78656760 --- /dev/null +++ b/code/game/objects/items/melee/melee.dm @@ -0,0 +1,22 @@ +/datum/passive_parry/melee + parry_arc = 155 + parry_arc_round_down = TRUE + +/** + * this is like /device and /weapon but a little less dumb + * + * this has some simple wrappers for default defense stuff, so 'common'ly melee weapons + * like knives, armblades, etc, can easily use them. + * + * * Certain things may or may not 'graduate' to /obj/item level later. + */ +/obj/item/melee + icon = 'icons/obj/weapons.dmi' + attack_sound = "swing_hit" + item_icons = list( + SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', + SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi', + ) + passive_parry = /datum/passive_parry{ + parry_chance_melee = 5; + } diff --git a/code/game/objects/items/weapons/melee/misc.dm b/code/game/objects/items/melee/types/misc.dm similarity index 94% rename from code/game/objects/items/weapons/melee/misc.dm rename to code/game/objects/items/melee/types/misc.dm index 41774bbc0db3..ec2cf5250f4c 100644 --- a/code/game/objects/items/weapons/melee/misc.dm +++ b/code/game/objects/items/melee/types/misc.dm @@ -68,13 +68,10 @@ can_speak = 1 var/list/voice_mobs = list() //The curse of the sword is that it has someone trapped inside. - -/obj/item/melee/cursedblade/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(default_parry_check(user, attacker, damage_source) && prob(50)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(user.loc, 'sound/weapons/punchmiss.ogg', 50, 1) - return 1 - return 0 + passive_parry = /datum/passive_parry/melee{ + parry_chance_projectile = 15; + parry_chance_default = 50; + } /obj/item/melee/cursedblade/proc/ghost_inhabit(var/mob/candidate) if(!isobserver(candidate)) @@ -329,19 +326,14 @@ var/wieldsound = null var/unwieldsound = null -//Allow a small chance of parrying melee attacks when wielded - maybe generalize this to other weapons someday -/obj/item/melee/twohanded/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(wielded && default_parry_check(user, attacker, damage_source) && prob(15)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(user.loc, 'sound/weapons/punchmiss.ogg', 50, 1) - return 1 - return 0 + passive_parry = /datum/passive_parry/melee{ + parry_chance_melee = 15; + } /obj/item/melee/twohanded/attack_self(mob/user) . = ..() if(.) return - . = ..() if(!wielded) wielded = 1 else if(wielded) diff --git a/code/game/objects/items/melee/types/ninja_energy_blade.dm b/code/game/objects/items/melee/types/ninja_energy_blade.dm new file mode 100644 index 000000000000..c7d110bc483e --- /dev/null +++ b/code/game/objects/items/melee/types/ninja_energy_blade.dm @@ -0,0 +1,66 @@ +/obj/item/melee/ninja_energy_blade + name = "energy blade" + desc = "A concentrated beam of energy in the shape of a blade. Very stylish... and lethal." + icon_state = "ninja_energy_blade" + item_state = "ninja_energy_blade" + damage_force = 40 //Normal attacks deal very high damage - about the same as wielded fire axe + armor_penetration = 100 + sharp = 1 + edge = 1 + anchored = 1 // Never spawned outside of inventory, should be fine. + throw_force = 1 //Throwing or dropping the item deletes it. + throw_speed = 1 + throw_range = 1 + w_class = WEIGHT_CLASS_BULKY//So you can't hide it in your pocket or some such. + atom_flags = NOBLOODY + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + + var/mob/living/creator + var/datum/effect_system/spark_spread/spark_system + + var/lrange = 2 + var/lpower = 2 + var/lcolor = "#00FF00" + + passive_parry = /datum/passive_parry{ + parry_chance_default = 60; + parry_chance_projectile = 60; + } + +/obj/item/melee/ninja_energy_blade/Initialize(mapload) + . = ..() + spark_system = new /datum/effect_system/spark_spread() + spark_system.set_up(5, 0, src) + spark_system.attach(src) + + START_PROCESSING(SSobj, src) + set_light(lrange, lpower, lcolor) + +/obj/item/melee/ninja_energy_blade/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/melee/ninja_energy_blade/attack_self(mob/user) + . = ..() + if(.) + return + qdel(src) + +/obj/item/melee/ninja_energy_blade/dropped(mob/user, atom_flags, atom/newLoc) + . = ..() + qdel(src) + +/obj/item/melee/ninja_energy_blade/process(delta_time) + if(!creator || loc != creator || !creator.is_holding(src)) + // Tidy up a bit. + if(istype(loc,/mob/living)) + var/mob/living/carbon/human/host = loc + if(istype(host)) + for(var/obj/item/organ/external/organ in host.organs) + for(var/obj/item/O in organ.implants) + if(O == src) + organ.implants -= src + host.pinned -= src + host.embedded -= src + host._handle_inventory_hud_remove(src) + qdel(src) diff --git a/code/game/objects/items/melee/types/transforming.dm b/code/game/objects/items/melee/types/transforming.dm new file mode 100644 index 000000000000..fe7f4160f939 --- /dev/null +++ b/code/game/objects/items/melee/types/transforming.dm @@ -0,0 +1,177 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +/** + * single-toggle weapons + */ +/obj/item/melee/transforming + icon = 'icons/items/melee/transforming.dmi' + damage_mode = NONE + item_icons = null + + /// are we active? + var/active = FALSE + /// when active, do we use an overlay instead of an icon state? + var/active_via_overlay = FALSE + + /// activation sound; also deactivation if it's not specified + var/activation_sound = 'sound/weapons/empty.ogg' + var/deactivation_sound + /// sound volume + var/toggle_sound_volume = 50 + + /// do not allow passive parry while off + var/no_block_while_off = TRUE + + //* active / inactive damage *// + + var/active_damage_force + var/inactive_damage_force + + var/active_damage_mode + var/inactive_damage_mode + + var/active_damage_tier + var/inactive_damage_tier + + var/active_damage_type + var/inactive_damage_type + + //* active / inactive effects *// + + var/list/active_attack_verb + var/list/inactive_attack_verb + + //* active / inactive inventory costs *// + + var/active_weight_class + var/inactive_weight_class + + var/active_weight_volume + var/inactive_weight_volume + + //* active / inactive throwing *// + + var/active_throw_force + var/inactive_throw_force + + var/active_throw_resist + var/inactive_throw_resist + + var/active_throw_range + var/inactive_throw_range + + var/active_throw_speed + var/inactive_throw_speed + +/obj/item/melee/transforming/Initialize(mapload) + . = ..() + if(islist(active_attack_verb)) + active_attack_verb = typelist(NAMEOF(src, active_attack_verb), active_attack_verb) + if(islist(inactive_attack_verb)) + inactive_attack_verb = typelist(NAMEOF(src, inactive_attack_verb), inactive_attack_verb) + +/obj/item/melee/transforming/passive_parry_intercept(mob/defending, attack_type, datum/weapon, datum/passive_parry/parry_data) + if(!active && no_block_while_off) + return // cancel + return ..() + +/obj/item/melee/transforming/update_icon_state() + icon_state = "[base_icon_state || initial(icon_state)][active && !active_via_overlay ? "-active" : ""]" + return ..() + +/obj/item/melee/transforming/update_overlays() + . = ..() + if(!active || !active_via_overlay) + return + . += build_active_overlay() + +/obj/item/melee/transforming/proc/build_active_overlay() + RETURN_TYPE(/image) + var/image/creating = image(icon, "[base_icon_state || icon_state]-active") + return creating + +/obj/item/melee/transforming/proc/build_active_worn_overlay(worn_state) + RETURN_TYPE(/image) + var/image/creating = image(icon, "[worn_state]-active") + return creating + +/obj/item/melee/transforming/render_apply_overlays(mutable_appearance/MA, bodytype, inhands, datum/inventory_slot/slot_meta, icon_used) + . = ..() + if(active_via_overlay && active) + MA.overlays += build_active_worn_overlay(MA.icon_state) + +/obj/item/melee/transforming/on_attack_self(datum/event_args/actor/e_args) + . = ..() + if(.) + return + add_fingerprint(e_args.performer) + toggle(e_args) + return CLICKCHAIN_DO_NOT_PROPAGATE + +/** + * actor can be /datum/event_args/actor or a single mob. + */ +/obj/item/melee/transforming/proc/toggle(datum/event_args/actor/actor, silent) + set_activation(!active, actor, silent) + +/obj/item/melee/transforming/proc/set_activation(state, datum/event_args/actor/actor, silent) + active = state + if(active) + on_activate(actor, silent) + else + on_deactivate(actor, silent) + update_icon() + update_worn_icon() + +/** + * actor can be /datum/event_args/actor or a single mob. + */ +/obj/item/melee/transforming/proc/on_activate(datum/event_args/actor/actor, silent) + E_ARGS_WRAP_USER_TO_ACTOR(actor) + + damage_force = VALUE_OR_DEFAULT(active_damage_force, initial(damage_force)) + damage_tier = VALUE_OR_DEFAULT(active_damage_tier, initial(damage_tier)) + damage_mode = VALUE_OR_DEFAULT(active_damage_mode, initial(damage_mode)) + damtype = VALUE_OR_DEFAULT(active_damage_type, initial(damtype)) + + throw_force = VALUE_OR_DEFAULT(active_throw_force, initial(throw_force)) + throw_resist = VALUE_OR_DEFAULT(active_throw_resist, initial(throw_resist)) + throw_range = VALUE_OR_DEFAULT(active_throw_range, initial(throw_range)) + throw_speed = VALUE_OR_DEFAULT(active_throw_speed, initial(throw_speed)) + + set_weight_class(VALUE_OR_DEFAULT(active_weight_class, initial(w_class))) + set_weight_volume(VALUE_OR_DEFAULT(active_weight_volume, initial(weight_volume))) + + attack_verb = active_attack_verb + + if(!silent && activation_sound) + playsound(src, activation_sound, toggle_sound_volume, TRUE) + + // todo: logging + +/** + * actor can be /datum/event_args/actor or a single mob. + */ +/obj/item/melee/transforming/proc/on_deactivate(datum/event_args/actor/actor, silent) + E_ARGS_WRAP_USER_TO_ACTOR(actor) + + damage_force = VALUE_OR_DEFAULT(inactive_damage_force, initial(damage_force)) + damage_tier = VALUE_OR_DEFAULT(inactive_damage_tier, initial(damage_tier)) + damage_mode = VALUE_OR_DEFAULT(inactive_damage_mode, initial(damage_mode)) + damtype = VALUE_OR_DEFAULT(inactive_damage_type, initial(damtype)) + + throw_force = VALUE_OR_DEFAULT(inactive_throw_force, initial(throw_force)) + throw_resist = VALUE_OR_DEFAULT(inactive_throw_resist, initial(throw_resist)) + throw_range = VALUE_OR_DEFAULT(inactive_throw_range, initial(throw_range)) + throw_speed = VALUE_OR_DEFAULT(inactive_throw_speed, initial(throw_speed)) + + set_weight_class(VALUE_OR_DEFAULT(inactive_weight_class, initial(w_class))) + set_weight_volume(VALUE_OR_DEFAULT(inactive_weight_volume, initial(weight_volume))) + + attack_verb = inactive_attack_verb + + if(!silent && (activation_sound || deactivation_sound)) + playsound(src, deactivation_sound || activation_sound, toggle_sound_volume, TRUE) + + // todo: logging diff --git a/code/game/objects/items/melee/types/transforming/energy.dm b/code/game/objects/items/melee/types/transforming/energy.dm new file mode 100644 index 000000000000..f011dec3091b --- /dev/null +++ b/code/game/objects/items/melee/types/transforming/energy.dm @@ -0,0 +1,176 @@ +/datum/passive_parry/melee/energy + parry_frame = /datum/parry_frame/passive_block/energy + +/datum/parry_frame/passive_block/energy + parry_sfx = 'sound/weapons/blade1.ogg' + +/obj/item/melee/transforming/energy + armor_penetration = 50 + atom_flags = NOCONDUCT | NOBLOODY + active_via_overlay = TRUE + + var/lrange = 2 + var/lpower = 2 + var/lcolor = "#0099FF" + var/colorable = FALSE + var/rainbow = FALSE + // If it uses energy. + var/use_cell = FALSE + var/hitcost = 120 + var/obj/item/cell/bcell = null + var/cell_type = /obj/item/cell/device + + passive_parry = /datum/passive_parry/melee/energy + + activation_sound = 'sound/weapons/saberon.ogg' + deactivation_sound = 'sound/weapons/saberoff.ogg' + +/obj/item/melee/transforming/energy/examine(mob/user, dist) + . = ..() + if(colorable) + . += SPAN_NOTICE("Alt-click to recolor it.") + +/obj/item/melee/transforming/energy/on_activate(datum/event_args/actor/actor, silent) + . = ..() + set_light(lrange, lpower, lcolor) + +/obj/item/melee/transforming/energy/on_deactivate(datum/event_args/actor/actor, silent) + . = ..() + set_light(0) + +/obj/item/melee/transforming/energy/proc/use_charge(var/cost) + if(active) + if(bcell) + if(bcell.checked_use(cost)) + return 1 + else + return 0 + return null + +/obj/item/melee/transforming/energy/examine(mob/user, dist) + . = ..() + if(use_cell) + if(bcell) + . += "The blade is [round(bcell.percent())]% charged." + if(!bcell) + . += "The blade does not have a power source installed." + +/obj/item/melee/transforming/energy/toggle(datum/event_args/actor/actor, silent) + if(use_cell) + if((!bcell || bcell.charge < hitcost) && !active) + if(!silent) + actor.chat_feedback( + "\The [src] does not seem to have power.", + target = src, + ) + return FALSE + return ..() + +/obj/item/melee/transforming/energy/attack_mob(mob/target, mob/user, clickchain_flags, list/params, mult, target_zone, intent) + . = ..() + if(active && use_cell) + if(!use_charge(hitcost)) + set_activation(FALSE) + visible_message("\The [src]'s blade flickers, before deactivating.") + +/obj/item/melee/transforming/energy/attackby(obj/item/W, mob/user) + if(istype(W, /obj/item/multitool) && colorable && !active) + if(!rainbow) + rainbow = TRUE + else + rainbow = FALSE + to_chat(user, "You manipulate the color controller in [src].") + update_icon() + if(use_cell) + if(istype(W, cell_type)) + if(!bcell) + if(!user.attempt_insert_item_for_installation(W, src)) + return + bcell = W + to_chat(user, "You install a cell in [src].") + update_icon() + else + to_chat(user, "[src] already has a cell.") + else if(W.is_screwdriver() && bcell) + bcell.update_icon() + bcell.forceMove(get_turf(loc)) + bcell = null + to_chat(user, "You remove the cell from \the [src].") + set_activation(FALSE) + update_icon() + return + return ..() + +/obj/item/melee/transforming/energy/get_cell(inducer) + return bcell + +/obj/item/melee/transforming/energy/build_active_overlay() + var/image/creating = ..() + if(rainbow) + creating.icon_state += "-rainbow" + else + creating.color = lcolor + return creating + +/obj/item/melee/transforming/energy/build_active_worn_overlay() + var/image/creating = ..() + if(rainbow) + creating.icon_state += "-rainbow" + else + creating.color = lcolor + return creating + +/obj/item/melee/transforming/energy/AltClick(mob/living/user) + if(!colorable) //checks if is not colorable + return + if(!in_range(src, user)) //Basic checks to prevent abuse + return + if(user.incapacitated() || !istype(user)) + to_chat(user, "You can't do that right now!") + return + + if(alert("Are you sure you want to recolor your blade?", "Confirm Recolor", "Yes", "No") == "Yes") + var/energy_color_input = input(usr,"","Choose Energy Color",lcolor) as color|null + if(energy_color_input) + lcolor = "#[sanitize_hexcolor(energy_color_input)]" + color = lcolor + update_icon() + return ..() + +// todo: no inhand! +// /obj/item/melee/transforming/energy/spear +// name = "energy spear" +// desc = "Concentrated energy forming a sharp tip at the end of a long rod." +// icon_state = "espear" +// armor_penetration = 75 +// sharp = 1 +// edge = 1 +// damage_force = 5 +// throw_force = 10 +// throw_speed = 7 +// throw_range = 11 +// reach = 2 +// w_class = WEIGHT_CLASS_BULKY +// active_damage_force = 25 +// active_throw_force = 30 +// active_weight_class = WEIGHT_CLASS_HUGE +// colorable = TRUE +// lcolor = "#800080" + +// passive_parry = /datum/passive_parry/melee/energy{ +// parry_chance_default = 50 +// } + +// /obj/item/melee/transforming/energy/spear/activate(mob/living/user) +// if(!active) +// to_chat(user, "\The [src] is now energised.") +// ..() +// attack_verb = list("jabbed", "stabbed", "impaled") +// AddComponent(/datum/component/jousting) + +// /obj/item/melee/transforming/energy/spear/deactivate(mob/living/user) +// if(active) +// to_chat(user, "\The [src] deactivates!") +// ..() +// attack_verb = list("whacked", "beat", "slapped", "thonked") +// DelComponent(/datum/component/jousting) diff --git a/code/game/objects/items/melee/types/transforming/energy/axe.dm b/code/game/objects/items/melee/types/transforming/energy/axe.dm new file mode 100644 index 000000000000..42d6e7be1c9a --- /dev/null +++ b/code/game/objects/items/melee/types/transforming/energy/axe.dm @@ -0,0 +1,49 @@ + +/obj/item/melee/transforming/energy/axe + name = "energy axe" + desc = "An energised battle axe." + icon_state = "energy_axe" + base_icon_state = "energy_axe" + damage_force = 20 + throw_force = 10 + throw_speed = 1 + throw_range = 5 + w_class = WEIGHT_CLASS_NORMAL + origin_tech = list(TECH_MAGNET = 3, TECH_COMBAT = 4) + attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") + sharp = 1 + edge = 1 + can_cleave = TRUE + + active_damage_force = 60 + active_throw_force = 35 + active_weight_class = WEIGHT_CLASS_HUGE + active_damage_type = SEARING + +/obj/item/melee/transforming/energy/axe/on_activate(datum/event_args/actor/actor, silent) + . = ..() + actor.chat_feedback( + SPAN_WARNING("You energize \the [src]."), + target = src, + ) + +/obj/item/melee/transforming/energy/axe/on_deactivate(datum/event_args/actor/actor, silent) + . = ..() + actor.chat_feedback( + SPAN_WARNING("You de-energize \the [src]."), + target = src, + ) + +/obj/item/melee/transforming/energy/axe/charge + name = "charge axe" + desc = "An energised axe." + active_damage_force = 30 + active_throw_force = 20 + armor_penetration = 25 + damage_force = 15 + use_cell = TRUE + hitcost = 120 + +/obj/item/melee/transforming/energy/axe/charge/loaded/Initialize(mapload) + . = ..() + bcell = new/obj/item/cell/device/weapon(src) diff --git a/code/game/objects/items/melee/types/transforming/energy/ionic_rapier.dm b/code/game/objects/items/melee/types/transforming/energy/ionic_rapier.dm new file mode 100644 index 000000000000..16c15a514bbe --- /dev/null +++ b/code/game/objects/items/melee/types/transforming/energy/ionic_rapier.dm @@ -0,0 +1,56 @@ +/obj/item/melee/transforming/energy/sword/ionic_rapier + name = "ionic rapier" + desc = "Designed specifically for disrupting electronics at close range, it is extremely deadly against synthetics, but almost harmless to pure organic targets." + description_info = "This is a dangerous melee weapon that will deliver a moderately powerful electromagnetic pulse to whatever it strikes. \ + Striking a lesser robotic entity will compel it to attack you, as well. It also does extra burn damage to robotic entities, but it does \ + very little damage to purely organic targets." + icon_state = "ionrapier" + item_state = "ionrapier" + active_damage_force = 10 + active_throw_force = 3 + sharp = 1 + edge = 1 + armor_penetration = 0 + atom_flags = NOBLOODY + lrange = 2 + lpower = 2 + lcolor = "#0000FF" + + passive_parry = /datum/passive_parry{ + parry_chance_default = 60; + parry_chance_projectile = 30; + } + +/obj/item/melee/transforming/energy/sword/ionic_rapier/afterattack(atom/target, mob/user, clickchain_flags, list/params) + if(istype(target, /obj) && (clickchain_flags & CLICKCHAIN_HAS_PROXIMITY) && active) + // EMP stuff. + var/obj/O = target + O.emp_act(3) // A weaker severity is used because this has infinite uses. + playsound(get_turf(O), 'sound/effects/EMPulse.ogg', 100, 1) + user.setClickCooldown(user.get_attack_speed(src)) // A lot of objects don't set click delay. + return ..() + +/obj/item/melee/transforming/energy/sword/ionic_rapier/melee_mob_hit(mob/target, mob/user, clickchain_flags, list/params, mult, target_zone, intent) + . = ..() + var/mob/living/L = target + if(!istype(L)) + return + if(L.isSynthetic() && active) + // Do some extra damage. Not a whole lot more since emp_act() is pretty nasty on FBPs already. + L.emp_act(3) // A weaker severity is used because this has infinite uses. + playsound(get_turf(L), 'sound/effects/EMPulse.ogg', 100, 1) + L.adjustFireLoss(damage_force * 3) // 15 Burn, for 20 total. + playsound(get_turf(L), 'sound/weapons/blade1.ogg', 100, 1) + + // Make lesser robots really mad at us. + if(L.mob_class & MOB_CLASS_SYNTHETIC) + if(L.has_polaris_AI()) + L.taunt(user) + L.adjustFireLoss(damage_force * 6) // 30 Burn, for 50 total. + +/obj/item/melee/transforming/energy/sword/ionic_rapier/lance + name = "zero-point lance" + desc = "Designed specifically for disrupting electronics at relatively close range, however it is still capable of dealing some damage to living beings." + active_damage_force = 20 + armor_penetration = 15 + reach = 2 diff --git a/code/game/objects/items/melee/types/transforming/energy/saber.dm b/code/game/objects/items/melee/types/transforming/energy/saber.dm new file mode 100644 index 000000000000..48e1eb491400 --- /dev/null +++ b/code/game/objects/items/melee/types/transforming/energy/saber.dm @@ -0,0 +1,141 @@ +/obj/item/melee/transforming/energy/sword + name = "energy saber" + desc = "May the fourth be within you." + icon_state = "saber" + base_icon_state = "saber" + damage_force = 3 + throw_force = 5 + throw_speed = 1 + throw_range = 5 + w_class = WEIGHT_CLASS_SMALL + atom_flags = NOBLOODY + origin_tech = list(TECH_MAGNET = 3, TECH_ILLEGAL = 4) + colorable = TRUE + drop_sound = 'sound/items/drop/sword.ogg' + pickup_sound = 'sound/items/pickup/sword.ogg' + + active_damage_force = 30 + active_throw_force = 20 + active_weight_class = WEIGHT_CLASS_BULKY + active_damage_mode = DAMAGE_MODE_SHARP | DAMAGE_MODE_EDGE + active_attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + + passive_parry = /datum/passive_parry{ + parry_chance_default = 60; + parry_chance_projectile = 65; + } + +/obj/item/melee/transforming/energy/sword/on_activate(datum/event_args/actor/actor, silent) + . = ..() + actor.chat_feedback( + SPAN_WARNING("You energize \the [src]."), + target = src, + ) + +/obj/item/melee/transforming/energy/sword/on_deactivate(datum/event_args/actor/actor, silent) + . = ..() + actor.chat_feedback( + SPAN_WARNING("You de-energize \the [src]."), + target = src, + ) + +/obj/item/melee/transforming/energy/sword/passive_parry_intercept(mob/defending, attack_type, datum/weapon, datum/passive_parry/parry_data) + . = ..() + if(!.) + return + + var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() + spark_system.set_up(5, 0, defending.loc) + spark_system.start() + +/obj/item/melee/transforming/energy/sword/attackby(obj/item/W, mob/living/user, params) + if(istype(W, /obj/item/melee/transforming/energy/sword)) + if(HAS_TRAIT(W, TRAIT_ITEM_NODROP) || HAS_TRAIT(src, TRAIT_ITEM_NODROP)) + to_chat(user, "\the [HAS_TRAIT(src, TRAIT_ITEM_NODROP) ? src : W] is stuck to your hand, you can't attach it to \the [HAS_TRAIT(src, TRAIT_ITEM_NODROP) ? W : src]!") + return + if(istype(W, /obj/item/melee/transforming/energy/sword/charge)) + to_chat(user,"These blades are incompatible, you can't attach them to each other!") + return + else + to_chat(user, "You combine the two energy swords, making a single supermassive blade! You're cool.") + new /obj/item/melee/transforming/energy/sword/dual(user.drop_location()) + qdel(W) + qdel(src) + else + return ..() + +/obj/item/melee/transforming/energy/sword/cutlass + name = "energy cutlass" + desc = "Arrrr matey." + icon_state = "cutlass" + base_icon_state = "cutlass" + colorable = TRUE + +/obj/item/melee/transforming/energy/sword/dual + name = "double-bladed energy sword" + desc = "Handle with care." + icon_state = "saber-dual" + base_icon_state = "saber-dual" + damage_force = 3 + active_damage_force = 60 + throw_force = 5 + throw_speed = 3 + armor_penetration = 35 + colorable = TRUE + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + + passive_parry = /datum/passive_parry{ + parry_chance_default = 60; + parry_chance_projectile = 85; + } + +/obj/item/melee/transforming/energy/sword/charge + name = "charge sword" + desc = "A small, handheld device which emits a high-energy 'blade'." + origin_tech = list(TECH_COMBAT = 5, TECH_MAGNET = 3, TECH_ILLEGAL = 4) + active_damage_force = 25 + armor_penetration = 25 + colorable = TRUE + use_cell = TRUE + hitcost = 75 + +/obj/item/melee/transforming/energy/sword/charge/loaded/Initialize(mapload) + . = ..() + bcell = new/obj/item/cell/device/weapon(src) + +/obj/item/melee/transforming/energy/sword/charge/attackby(obj/item/W, mob/living/user, params) + if(istype(W, /obj/item/melee/transforming/energy/sword/charge)) + if(HAS_TRAIT(W, TRAIT_ITEM_NODROP) || HAS_TRAIT(src, TRAIT_ITEM_NODROP)) + to_chat(user, "\the [HAS_TRAIT(src, TRAIT_ITEM_NODROP) ? src : W] is stuck to your hand, you can't attach it to \the [HAS_TRAIT(src, TRAIT_ITEM_NODROP) ? W : src]!") + return + else + to_chat(user, "You combine the two charge swords, making a single supermassive blade! You're cool.") + new /obj/item/melee/transforming/energy/sword/charge/dualsaber(user.drop_location()) + qdel(W) + qdel(src) + else + return ..() + +/obj/item/melee/transforming/energy/sword/charge/dualsaber + name = "double-bladed charge sword" + desc = "Make sure you bought batteries." + icon_state = "saber-dual" + base_icon_state = "saber-dual" + damage_force = 3 + active_damage_force = 50 + throw_force = 5 + throw_speed = 3 + armor_penetration = 30 + colorable = TRUE + hitcost = 150 + + passive_parry = /datum/passive_parry{ + parry_chance_default = 60; + parry_chance_projectile = 65; + } + +/obj/item/melee/transforming/energy/sword/imperial + name = "imperial sword" + desc = "What the hell is this?" + icon_state = "imperial_sword" + base_icon_state = "imperial_sword" diff --git a/code/game/objects/items/melee/types/transforming/hfmachete.dm b/code/game/objects/items/melee/types/transforming/hfmachete.dm new file mode 100644 index 000000000000..5b5653a84f74 --- /dev/null +++ b/code/game/objects/items/melee/types/transforming/hfmachete.dm @@ -0,0 +1,50 @@ +/obj/item/melee/transforming/hfmachete + name = "high-frequency machete" + desc = "A high-frequency broad blade used either as an implement or in combat like a short sword." + icon_state = "hfmachete" + base_icon_state = "hfmachete" + damage_mode = DAMAGE_MODE_SHARP | DAMAGE_MODE_EDGE + damage_force = 20 // You can be crueler than that, Jack. + throw_force = 40 + throw_speed = 8 + throw_range = 8 + w_class = WEIGHT_CLASS_NORMAL + siemens_coefficient = 1 + origin_tech = list(TECH_COMBAT = 3, TECH_ILLEGAL = 3) + attack_verb = list("attacked", "diced", "cleaved", "torn", "cut", "slashed") + armor_penetration = 50 + var/base_state = "hfmachete" + attack_sound = "machete_hit_sound" // dont mind the meaty hit sounds if you hit something that isnt meaty + can_cleave = TRUE + embed_chance = 0 // let's not + + active_damage_force = 40 + + activation_sound = 'sound/weapons/hf_machete/hfmachete1.ogg' + deactivation_sound = 'sound/weapons/hf_machete/hfmachete0.ogg' + + active_weight_class = WEIGHT_CLASS_BULKY + inactive_weight_class = WEIGHT_CLASS_NORMAL + +/obj/item/melee/transforming/hfmachete/on_activate(datum/event_args/actor/actor, silent) + . = ..() + actor.chat_feedback( + SPAN_WARNING("You energize \the [src]."), + target = src, + ) + +/obj/item/melee/transforming/hfmachete/on_deactivate(datum/event_args/actor/actor, silent) + . = ..() + actor.chat_feedback( + SPAN_WARNING("You de-energize \the [src]."), + target = src, + ) + +/obj/item/melee/transforming/hfmachete/afterattack(atom/target, mob/user, clickchain_flags, list/params) + if(!(clickchain_flags & CLICKCHAIN_HAS_PROXIMITY)) + return + ..() + if(target) + if(istype(target,/obj/effect/plant)) + var/obj/effect/plant/P = target + P.die_off() diff --git a/code/game/objects/items/shield/shield.dm b/code/game/objects/items/shield/shield.dm new file mode 100644 index 000000000000..c55b07df8598 --- /dev/null +++ b/code/game/objects/items/shield/shield.dm @@ -0,0 +1,17 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +/datum/passive_parry/shield + parry_arc = 155 + parry_arc_round_down = TRUE + parry_frame = /datum/parry_frame/passive_block/shield + +/datum/parry_frame/passive_block/shield + parry_sfx = /datum/soundbyte/grouped/block_metal_with_metal + block_verb = "blocks" // full block for now + +/obj/item/shield + name = "shield" + passive_parry = /datum/passive_parry/shield{ + parry_chance_default = 50; + } diff --git a/code/game/objects/items/shield/types/shields_legacy.dm b/code/game/objects/items/shield/types/shields_legacy.dm new file mode 100644 index 000000000000..1b73e20897f7 --- /dev/null +++ b/code/game/objects/items/shield/types/shields_legacy.dm @@ -0,0 +1,204 @@ +/obj/item/shield/riot + name = "riot shield" + desc = "A shield adept for close quarters engagement. It's also capable of protecting from less powerful projectiles." + icon = 'icons/items/shields/basic.dmi' + icon_state = "riot" + slot_flags = SLOT_BACK + damage_force = 5 + throw_force = 5 + throw_speed = 1 + throw_range = 4 + w_class = WEIGHT_CLASS_BULKY + origin_tech = list(TECH_MATERIAL = 2) + materials_base = list(MAT_GLASS = 7500, MAT_STEEL = 1000) + attack_verb = list("shoved", "bashed") + worth_intrinsic = 300 + var/cooldown = 0 //shield bash cooldown. based on world.time + +/obj/item/shield/riot/passive_parry_intercept(mob/defending, attack_type, datum/weapon, datum/passive_parry/parry_data) + if(istype(weapon, /obj/projectile)) + var/obj/projectile/proj = weapon + if(((is_sharp(proj) && proj.armor_penetration >= 10) || proj.damage_tier >= ARMOR_TIER_HIGH || istype(proj, /obj/projectile/beam)) && prob(50)) + //If we're at this point, the bullet/beam is going to go through the shield, however it will hit for less damage. + //Bullets get slowed down, while beams are diffused as they hit the shield, so these shields are not /completely/ + //useless. Extremely penetrating projectiles will go through the shield without less damage. + defending.visible_message("\The [defending]'s [src.name] is pierced by [proj]!") + proj.dampen_on_pierce_experimental(src, 20, ARMOR_TIER_HIGH) + playsound(src, /datum/soundbyte/grouped/block_metal_with_metal, 65, TRUE) + return null + return ..() + +/obj/item/shield/riot/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W, /obj/item/melee/baton)) + if(cooldown < world.time - 25) + user.visible_message("[user] bashes [src] with [W]!") + playsound(user.loc, 'sound/effects/shieldbash.ogg', 50, 1) + cooldown = world.time + else + ..() + +/obj/item/shield/riot/flash + name = "strobe shield" + desc = "A shield with a built in, high intensity light capable of blinding and disorienting suspects. Takes regular handheld flashes as bulbs." + icon_state = "riot-flash" + item_state = "riot-flash" + var/obj/item/flash/embedded_flash + var/flashfail = 0 + +/obj/item/shield/riot/flash/Initialize(mapload) + . = ..() + embedded_flash = new(src) + +/obj/item/shield/riot/flash/attack_mob(mob/target, mob/user, clickchain_flags, list/params, mult, target_zone, intent) + if(user.a_intent == INTENT_HARM) + return ..() + return embedded_flash.attack_mob(arglist(args)) + +/obj/item/shield/riot/flash/attack_self(mob/user) + . = ..() + if(.) + return + . = embedded_flash.attack_self(user) + update_icon() + +/obj/item/shield/riot/flash/passive_parry_intercept(mob/defending, attack_type, datum/weapon, datum/passive_parry/parry_data) + . = ..() + if(!.) + return + if(embedded_flash.broken) + return + if(!(attack_type & (ATTACK_TYPE_MELEE | ATTACK_TYPE_UNARMED))) + return + // var/datum/event_args/actor/clickchain/clickchain = shieldcall_args[SHIELDCALL_ARG_CLICKCHAIN] + // var/mob/attacker = clickchain?.performer + // if(attacker) + // log_attack(key_name(attacker), key_name(defending), "flash shield auto-invoke") + // embedded_flash.melee_interaction_chain(attacker, defending) + // else + log_attack(key_name(defending), "none (AoE)", "flash shield auto-invoke") + embedded_flash.attack_self(defending) + update_icon() + +/obj/item/shield/riot/flash/attackby(obj/item/W, mob/user) + if(istype(W, /obj/item/flash)) + var/obj/item/flash/flash = W + if(flashfail) + to_chat(user, "No sense replacing it with a broken bulb!") + return + else + to_chat(user, "You begin to replace the bulb...") + if(do_after(user, 20, target = user)) + if(flashfail || !flash || QDELETED(flash)) + return + playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) + qdel(embedded_flash) + embedded_flash = flash + flash.forceMove(src) + update_icon() + return + ..() + +/obj/item/shield/riot/flash/emp_act(severity) + . = ..() + embedded_flash.emp_act(severity) + update_icon() + +/obj/item/shield/riot/flash/update_icon_state() + . = ..() + if(!embedded_flash || embedded_flash.broken) + icon_state = "riot" + item_state = "riot" + else + icon_state = "flashshield" + item_state = "flashshield" + +/obj/item/shield/riot/flash/examine(mob/user, dist) + . = ..() + if (embedded_flash?.broken) + . += "The mounted bulb has burnt out. You can try replacing it with a new one." + +/obj/item/shield/makeshift + name = "metal shield" + desc = "A large shield made of wired and welded sheets of metal. The handle is made of cloth and leather, making it unwieldy." + icon = 'icons/obj/weapons.dmi' + icon_state = "makeshift" + inhand_state = "metal" + slot_flags = null + damage_force = 10 + throw_force = 7 + +/obj/item/shield/riot/tower + name = "tower shield" + desc = "An immense tower shield. Designed to ensure maximum protection to the user, at the expense of mobility." + item_state = "metal" + damage_force = 16 + encumbrance = ITEM_ENCUMBRANCE_SHIELD_TOWER + throw_force = 15 //Massive piece of metal + w_class = WEIGHT_CLASS_HUGE + +/obj/item/shield/riot/tower/swat + name = "swat shield" + +/obj/item/shield/riot/energy_proof + name = "energy resistant shield" + desc = "An ablative shield designed to absorb and disperse energy attacks. This comes at significant cost to its ability to withstand ballistics and kinetics, breaking apart easily." + icon_state = "riot-laser" + +/obj/item/shield/riot/kinetic_proof + name = "kinetic resistant shield" + desc = "A polymer and ceramic shield designed to absorb ballistic projectiles and kinetic force. It doesn't do very well into energy attacks, especially from weapons that inflict burns." + icon_state = "riot-bullet" + +//Exotics/Costume Shields +/obj/item/shield/riot/roman + name = "scutum" + desc = "A replica shield for close quarters engagement. Its modern materials are also capable of protecting from less powerful projectiles." + icon = 'icons/obj/weapons.dmi' + icon_state = "roman" + slot_flags = SLOT_BACK + materials_base = list(MAT_WOOD = 7500, MAT_STEEL = 1000) + +/obj/item/shield/fluff/roman + name = "replica scutum" + desc = "A replica shield for close quarters engagement. It looks sturdy enough to withstand foam weapons, and nothing more." + icon = 'icons/obj/weapons.dmi' + icon_state = "roman" + slot_flags = SLOT_BACK + damage_force = 5.0 + throw_force = 5.0 + throw_speed = 2 + throw_range = 6 + +/obj/item/shield/riot/buckler + name = "buckler" + desc = "A wrist mounted round shield for close quarters engagement. Its modern materials are also capable of protecting from less powerful projectiles." + icon = 'icons/obj/weapons.dmi' + icon_state = "buckler" + slot_flags = SLOT_BACK | SLOT_BELT + materials_base = list(MAT_WOOD = 7500, MAT_STEEL = 1000) + +/obj/item/shield/riot/foam + name = "foam riot shield" + desc = "A shield for close quarters engagement. It looks sturdy enough to withstand foam weapons, and nothing more." + icon = 'icons/obj/weapons.dmi' + icon_state = "foam" + slot_flags = SLOT_BACK + damage_force = 0 + throw_force = 0 + throw_speed = 2 + throw_range = 6 + materials_base = list(MAT_PLASTIC = 7500, "foam" = 1000) + +/obj/item/shield/riot/foam/passive_parry_intercept(mob/defending, attack_type, datum/weapon, datum/passive_parry/parry_data) + var/allowed = FALSE + if(isobj(weapon)) + var/obj/casted_object = weapon + if(istype(casted_object, /obj/projectile)) + var/obj/projectile/casted_projectile = casted_object + if(istype(casted_projectile, /obj/projectile/bullet/reusable/foam)) + allowed = TRUE + else if(casted_object.get_primary_material_id() == /datum/material/toy_foam::id) + allowed = TRUE + if(!allowed) + return + return ..() diff --git a/code/game/objects/items/shield/types/shields_legacy_vr.dm b/code/game/objects/items/shield/types/shields_legacy_vr.dm new file mode 100644 index 000000000000..d0d00bba9d9a --- /dev/null +++ b/code/game/objects/items/shield/types/shields_legacy_vr.dm @@ -0,0 +1,14 @@ + +/obj/item/shield/fluff/wolfgirlshield + name = "Autumn Shield" + desc = "A shiny silvery shield with a large red leaf symbol in the center." + icon = 'icons/obj/weapons_vr.dmi' + icon_state = "wolfgirlshield" + slot_flags = SLOT_BACK | SLOT_OCLOTHING + damage_force = 5.0 + throw_force = 5.0 + throw_speed = 2 + throw_range = 6 + item_icons = list(SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi', SLOT_ID_BACK = 'icons/vore/custom_items_vr.dmi', SLOT_ID_SUIT = 'icons/vore/custom_items_vr.dmi') + attack_verb = list("shoved", "bashed") + allowed = list(/obj/item/melee/fluffstuff/wolfgirlsword) diff --git a/code/game/objects/items/shield/types/transforming.dm b/code/game/objects/items/shield/types/transforming.dm new file mode 100644 index 000000000000..d60a18805403 --- /dev/null +++ b/code/game/objects/items/shield/types/transforming.dm @@ -0,0 +1,110 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +/** + * toggleable shields, like energy combat shields and telescoping shields + */ +/obj/item/shield/transforming + /// are we active? + var/active = FALSE + /// when active, do we use an overlay instead of an icon state? + /// + /// * applies to regular overlay + /// * applies to worn overlay as well + var/active_via_overlay = FALSE + + var/active_weight_class = WEIGHT_CLASS_BULKY + var/inactive_weight_class + var/active_weight_volume + var/inactive_weight_volume + + var/active_damage_force + var/inactive_damage_force + + /// activation sound; also deactivation if it's not specified + var/activation_sound = 'sound/weapons/empty.ogg' + var/deactivation_sound + /// sound volume + var/toggle_sound_volume = 50 + +/obj/item/shield/transforming/passive_parry_intercept(mob/defending, attack_type, datum/weapon, datum/passive_parry/parry_data) + if(!active) + return // cancel + return ..() + +/obj/item/shield/transforming/update_icon_state() + icon_state = "[initial(icon_state)][active && !active_via_overlay ? "-active" : ""]" + return ..() + +/obj/item/shield/transforming/update_overlays() + . = ..() + if(!active || !active_via_overlay) + return + . += build_active_overlay() + +/obj/item/shield/transforming/proc/build_active_overlay() + RETURN_TYPE(/image) + var/image/creating = image(icon, "[base_icon_state || icon_state]-active") + return creating + +/obj/item/shield/transforming/proc/build_active_worn_overlay(worn_state) + RETURN_TYPE(/image) + var/image/creating = image(icon, "[worn_state]-active") + return creating + +/obj/item/shield/transforming/render_apply_overlays(mutable_appearance/MA, bodytype, inhands, datum/inventory_slot/slot_meta, icon_used) + . = ..() + if(active_via_overlay && active) + MA.overlays += build_active_worn_overlay(MA.icon_state) + +/obj/item/shield/transforming/on_attack_self(datum/event_args/actor/e_args) + . = ..() + if(.) + return + add_fingerprint(e_args.performer) + toggle(e_args) + return CLICKCHAIN_DO_NOT_PROPAGATE + +/** + * actor can be /datum/event_args/actor or a single mob. + */ +/obj/item/shield/transforming/proc/toggle(datum/event_args/actor/actor, silent) + active = !active + if(active) + on_activate(actor, silent) + else + on_deactivate(actor, silent) + update_icon() + update_worn_icon() + +/** + * actor can be /datum/event_args/actor or a single mob. + */ +/obj/item/shield/transforming/proc/on_activate(datum/event_args/actor/actor, silent) + E_ARGS_WRAP_USER_TO_ACTOR(actor) + + damage_force = VALUE_OR_DEFAULT(active_damage_force, initial(damage_force)) + + set_weight_class(VALUE_OR_DEFAULT(active_weight_class, initial(w_class))) + set_weight_volume(VALUE_OR_DEFAULT(active_weight_volume, initial(weight_volume))) + + if(!silent && activation_sound) + playsound(src, activation_sound, toggle_sound_volume, TRUE) + + // todo: logging + +/** + * actor can be /datum/event_args/actor or a single mob. + */ +/obj/item/shield/transforming/proc/on_deactivate(datum/event_args/actor/actor, silent) + E_ARGS_WRAP_USER_TO_ACTOR(actor) + + damage_force = VALUE_OR_DEFAULT(inactive_damage_force, initial(damage_force)) + + set_weight_class(VALUE_OR_DEFAULT(inactive_weight_class, initial(w_class))) + set_weight_volume(VALUE_OR_DEFAULT(inactive_weight_volume, initial(weight_volume))) + + if(!silent && (activation_sound || deactivation_sound)) + playsound(src, deactivation_sound || activation_sound, toggle_sound_volume, TRUE) + + // todo: logging diff --git a/code/game/objects/items/shield/types/transforming/energy.dm b/code/game/objects/items/shield/types/transforming/energy.dm new file mode 100644 index 000000000000..143665070c65 --- /dev/null +++ b/code/game/objects/items/shield/types/transforming/energy.dm @@ -0,0 +1,111 @@ +/obj/item/shield/transforming/energy + name = "energy combat shield" + desc = "A shield capable of stopping most projectile and melee attacks. It can be retracted, expanded, and stored anywhere." + icon = 'icons/items/shields/transforming.dmi' + icon_state = "energy" + base_icon_state = "energy" + slot_flags = SLOT_EARS + atom_flags = NOCONDUCT + + damage_force = 3 + active_damage_force = 10 + + w_class = WEIGHT_CLASS_TINY + active_weight_class = WEIGHT_CLASS_BULKY + + throw_force = 5.0 + throw_speed = 1 + throw_range = 4 + + passive_parry = /datum/passive_parry/shield{ + parry_chance_default = 50; + parry_frame = /datum/parry_frame/passive_block/energy; + } + + active_via_overlay = TRUE + + var/lrange = 1.5 + var/lpower = 1.5 + var/lcolor = "#006AFF" + + origin_tech = list(TECH_MATERIAL = 4, TECH_MAGNET = 3, TECH_ILLEGAL = 4) + attack_verb = list("shoved", "bashed") + + /// drop projectiles sometimes? + var/legacy_projectile_damage_drop = TRUE + /// divisor to projectile damage before we drop the hit + var/legacy_projectile_damage_drop_divisor = 1.6 + + activation_sound = 'sound/weapons/saberon.ogg' + deactivation_sound = 'sound/weapons/saberoff.ogg' + +/obj/item/shield/transforming/energy/passive_parry_intercept(mob/defending, attack_type, datum/weapon, datum/passive_parry/parry_data) + if(istype(weapon, /obj/projectile)) + var/obj/projectile/casted_projectile = weapon + if(legacy_projectile_damage_drop) + if((is_sharp(casted_projectile) && casted_projectile.damage > 10) || (casted_projectile.projectile_type & PROJECTILE_TYPE_BEAM)) + if(prob(casted_projectile.damage / legacy_projectile_damage_drop_divisor)) + return // drop the shield + . = ..() + if(!.) + return + var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() + spark_system.set_up(3, 0, defending.loc) + spark_system.start() + +/obj/item/shield/transforming/energy/on_activate(datum/event_args/actor/actor, silent) + . = ..() + slot_flags = NONE + if(!silent) + actor.chat_feedback( + SPAN_WARNING("You activate \the [src]."), + target = src, + ) + set_light(lrange, lpower, lcolor) + +/obj/item/shield/transforming/energy/on_deactivate(datum/event_args/actor/actor, silent) + . = ..() + slot_flags = SLOT_EARS + if(!silent) + actor.chat_feedback( + SPAN_WARNING("You collapse \the [src]."), + target = src, + ) + set_light(0) + +/obj/item/shield/transforming/energy/build_active_overlay() + var/image/built = ..() + if(lcolor) + built.color = lcolor + return built + +/obj/item/shield/transforming/energy/build_active_worn_overlay() + var/image/built = ..() + if(lcolor) + built.color = lcolor + return built + +// todo: legacy below + +/obj/item/shield/transforming/energy/AltClick(mob/living/user) + if(!in_range(src, user)) //Basic checks to prevent abuse + return + if(user.incapacitated() || !istype(user)) + to_chat(user, "You can't do that right now!") + return + if(alert("Are you sure you want to recolor your shield?", "Confirm Recolor", "Yes", "No") == "Yes") + var/energy_color_input = input(usr,"","Choose Energy Color",lcolor) as color|null + if(energy_color_input) + lcolor = sanitize_hexcolor(energy_color_input, desired_format=6, include_crunch=1) + update_icon() + +/obj/item/shield/transforming/energy/examine(mob/user, dist) + . = ..() + . += "Alt-click to recolor it." + +/obj/item/shield/transforming/energy/imperial + name = "imperial shield" + desc = "What the hell is this?" + desc = "It's really easy to mispronounce the name of this shield if you've only read it in books." + icon_state = "imperial_shield" + base_icon_state = "imperial_shield" diff --git a/code/game/objects/items/shield/types/transforming/telescopic.dm b/code/game/objects/items/shield/types/transforming/telescopic.dm new file mode 100644 index 000000000000..76e96d790f6f --- /dev/null +++ b/code/game/objects/items/shield/types/transforming/telescopic.dm @@ -0,0 +1,37 @@ +/obj/item/shield/transforming/telescopic + name = "telescopic shield" + desc = "An advanced riot shield made of lightweight materials that collapses for easy storage." + icon = 'icons/items/shields/transforming.dmi' + icon_state = "telescopic" + base_icon_state = "telescopic" + slot_flags = NONE + + damage_force = 5 + active_damage_force = 10 + inactive_damage_force = 5 + + w_class = WEIGHT_CLASS_NORMAL + active_weight_class = WEIGHT_CLASS_BULKY + inactive_weight_class = WEIGHT_CLASS_NORMAL + + activation_sound = 'sound/weapons/empty.ogg' + + throw_force = 5 + throw_speed = 3 + throw_range = 5 + +/obj/item/shield/transforming/telescopic/on_activate(datum/event_args/actor/actor, silent) + . = ..() + slot_flags = SLOT_BACK + actor.chat_feedback( + SPAN_WARNING("You extend \the [src]."), + target = src, + ) + +/obj/item/shield/transforming/telescopic/on_deactivate(datum/event_args/actor/actor, silent) + . = ..() + slot_flags = NONE + actor.chat_feedback( + SPAN_WARNING("You collapse \the [src]."), + target = src, + ) diff --git a/code/game/objects/items/shield_projector/shield_matrix.dm b/code/game/objects/items/shield_projector/shield_matrix.dm new file mode 100644 index 000000000000..eee6c5f280c2 --- /dev/null +++ b/code/game/objects/items/shield_projector/shield_matrix.dm @@ -0,0 +1,79 @@ +// todo: /obj/effect/shield_matrix +// This is the actual shield. The projector is a different item. +/obj/effect/directional_shield + name = "directional combat shield" + desc = "A wide shield, which has the property to block incoming projectiles but allow outgoing projectiles to pass it. \ + Slower moving objects are not blocked, so people can walk in and out of the barrier, and things can be thrown into and out \ + of it." + icon = 'icons/effects/effects.dmi' + icon_state = "directional_shield" + density = FALSE // People can move pass these shields. + opacity = FALSE + anchored = TRUE + integrity_flags = INTEGRITY_ACIDPROOF | INTEGRITY_FIREPROOF | INTEGRITY_LAVAPROOF + layer = MOB_LAYER + 0.1 + mouse_opacity = FALSE + var/obj/item/shield_projector/projector = null // The thing creating the shield. + var/x_offset = 0 // Offset from the 'center' of where the projector is, so that if it moves, the shield can recalc its position. + var/y_offset = 0 // Ditto. + +/obj/effect/directional_shield/New(var/newloc, var/new_projector) + if(new_projector) + projector = new_projector + var/turf/us = get_turf(src) + var/turf/them = get_turf(projector) + if(them) + x_offset = us.x - them.x + y_offset = us.y - them.y + else + update_color() + ..(newloc) + +/obj/effect/directional_shield/proc/relocate() + if(!projector) + return // Nothing to follow. + var/turf/T = get_turf(projector) + if(!T) + return + var/turf/new_pos = locate(T.x + x_offset, T.y + y_offset, T.z) + if(new_pos) + forceMove(new_pos) + else + qdel(src) + +/obj/effect/directional_shield/proc/update_color(var/new_color) + if(!projector) + color = "#0099FF" + else + animate(src, color = new_color, 5) +// color = new_color + +/obj/effect/directional_shield/Destroy() + if(projector) + projector.active_shields -= src + projector = null + return ..() + +/obj/effect/directional_shield/CanPass(atom/movable/mover, turf/target) + . = ..() + if(istype(mover, /obj/projectile)) + var/obj/projectile/P = mover + if(P.projectile_type & PROJECTILE_TYPE_TRACE) + return TRUE + if(check_defensive_arc_tile(src, P, 90, null, dir)) + return FALSE + return TRUE + +/obj/effect/directional_shield/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + impact_flags &= ~PROJECTILE_IMPACT_FLAGS_SHOULD_NOT_HIT + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + adjust_health(-proj.get_structure_damage()) + playsound(src, 'sound/effects/EMPulse.ogg', 75, 1) + +// All the shields tied to their projector are one 'unit', and don't have individualized health values like most other shields. +/obj/effect/directional_shield/proc/adjust_health(amount) + if(projector) + projector.adjust_health(amount) // Projector will kill the shield if needed. + // If the shield lacks a projector, then it was probably spawned in by an admin for bus, so it's indestructable. diff --git a/code/modules/shieldgen/directional_shield.dm b/code/game/objects/items/shield_projector/shield_projector.dm similarity index 81% rename from code/modules/shieldgen/directional_shield.dm rename to code/game/objects/items/shield_projector/shield_projector.dm index fd1bc9df0f6d..e9b3547cf28e 100644 --- a/code/modules/shieldgen/directional_shield.dm +++ b/code/game/objects/items/shield_projector/shield_projector.dm @@ -1,81 +1,3 @@ -// This is the actual shield. The projector is a different item. -/obj/effect/directional_shield - name = "directional combat shield" - desc = "A wide shield, which has the property to block incoming projectiles but allow outgoing projectiles to pass it. \ - Slower moving objects are not blocked, so people can walk in and out of the barrier, and things can be thrown into and out \ - of it." - icon = 'icons/effects/effects.dmi' - icon_state = "directional_shield" - density = FALSE // People can move pass these shields. - opacity = FALSE - anchored = TRUE - integrity_flags = INTEGRITY_ACIDPROOF | INTEGRITY_FIREPROOF | INTEGRITY_LAVAPROOF - layer = MOB_LAYER + 0.1 - mouse_opacity = FALSE - var/obj/item/shield_projector/projector = null // The thing creating the shield. - var/x_offset = 0 // Offset from the 'center' of where the projector is, so that if it moves, the shield can recalc its position. - var/y_offset = 0 // Ditto. - -/obj/effect/directional_shield/New(var/newloc, var/new_projector) - if(new_projector) - projector = new_projector - var/turf/us = get_turf(src) - var/turf/them = get_turf(projector) - if(them) - x_offset = us.x - them.x - y_offset = us.y - them.y - else - update_color() - ..(newloc) - -/obj/effect/directional_shield/proc/relocate() - if(!projector) - return // Nothing to follow. - var/turf/T = get_turf(projector) - if(!T) - return - var/turf/new_pos = locate(T.x + x_offset, T.y + y_offset, T.z) - if(new_pos) - forceMove(new_pos) - else - qdel(src) - -/obj/effect/directional_shield/proc/update_color(var/new_color) - if(!projector) - color = "#0099FF" - else - animate(src, color = new_color, 5) -// color = new_color - -/obj/effect/directional_shield/Destroy() - if(projector) - projector.active_shields -= src - projector = null - return ..() - -/obj/effect/directional_shield/CanPass(atom/movable/mover, turf/target) - . = ..() - if(istype(mover, /obj/projectile)) - var/obj/projectile/P = mover - if(istype(P, /obj/projectile/test)) // Turrets need to try to kill the shield and so their test bullet needs to penetrate. - return TRUE - - var/bad_arc = global.reverse_dir[dir] // Arc of directions from which we cannot block. - if(check_shield_arc(src, bad_arc, P)) // This is actually for mobs but it will work for our purposes as well. - return FALSE - return TRUE - -/obj/effect/directional_shield/bullet_act(var/obj/projectile/P) - adjust_health(-P.get_structure_damage()) - P.on_hit() - playsound(src, 'sound/effects/EMPulse.ogg', 75, 1) - -// All the shields tied to their projector are one 'unit', and don't have individualized health values like most other shields. -/obj/effect/directional_shield/proc/adjust_health(amount) - if(projector) - projector.adjust_health(amount) // Projector will kill the shield if needed. - // If the shield lacks a projector, then it was probably spawned in by an admin for bus, so it's indestructable. - // This actually creates the shields. It's an item so that it can be carried, but it could also be placed inside a stationary object if desired. // It should work inside the contents of any mob. diff --git a/code/game/objects/items/shooting_range.dm b/code/game/objects/items/shooting_range.dm index 0d19a6ecb53b..3f2ad6ff22e5 100644 --- a/code/game/objects/items/shooting_range.dm +++ b/code/game/objects/items/shooting_range.dm @@ -75,12 +75,14 @@ desc = "A shooting target that looks vaguely human shaped but not enough to cause controversy." hp = 1800 // alium onest too kinda -/obj/item/target/bullet_act(var/obj/projectile/Proj) - var/p_x = Proj.p_x + pick(0,0,0,0,0,-1,1) // really ugly way of coding "sometimes offset Proj.p_x!" - var/p_y = Proj.p_y + pick(0,0,0,0,0,-1,1) +/obj/item/target/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + + var/p_x = proj.p_x + pick(0,0,0,0,0,-1,1) // really ugly way of coding "sometimes offset proj.p_x!" + var/p_y = proj.p_y + pick(0,0,0,0,0,-1,1) var/decaltype = 1 // 1 - scorch, 2 - bullet - if(istype(/obj/projectile/bullet, Proj)) + if(istype(/obj/projectile/bullet, proj)) decaltype = 2 @@ -88,7 +90,7 @@ if( virtualIcon.GetPixel(p_x, p_y) ) // if the located pixel isn't blank (null) - hp -= Proj.damage + hp -= proj.damage if(hp <= 0) for(var/mob/O in oviewers()) if ((O.client && !( O.has_status_effect(/datum/status_effect/sight/blindness) ))) @@ -109,7 +111,7 @@ bmark.pixel_x-- bmark.pixel_y-- - if(Proj.damage >= 20 || istype(Proj, /obj/projectile/beam/practice)) + if(proj.damage >= 20 || istype(proj, /obj/projectile/beam/practice)) bmark.icon_state = "scorch" bmark.setDir(pick(NORTH,SOUTH,EAST,WEST)) // random scorch design @@ -121,12 +123,12 @@ // Bullets are hard. They make dents! bmark.icon_state = "dent" - if(Proj.damage >= 10 && bulletholes.len <= 35) // maximum of 35 bullet holes + if(proj.damage >= 10 && bulletholes.len <= 35) // maximum of 35 bullet holes if(decaltype == 2) // bullet - if(prob(Proj.damage+30)) // bullets make holes more commonly! + if(prob(proj.damage+30)) // bullets make holes more commonly! new/datum/bullethole(src, bmark.pixel_x, bmark.pixel_y) // create new bullet hole else // Lasers! - if(prob(Proj.damage-10)) // lasers make holes less commonly + if(prob(proj.damage-10)) // lasers make holes less commonly new/datum/bullethole(src, bmark.pixel_x, bmark.pixel_y) // create new bullet hole // draw bullet holes @@ -141,8 +143,7 @@ return - return PROJECTILE_CONTINUE // the bullet/projectile goes through the target! - + return . | PROJECTILE_IMPACT_PASSTHROUGH // Small memory holder entity for transparent bullet holes /datum/bullethole diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm index aecfe62bf7f6..2e9fea684955 100644 --- a/code/game/objects/items/storage/belt.dm +++ b/code/game/objects/items/storage/belt.dm @@ -262,8 +262,8 @@ /obj/item/cell/device/weapon, /obj/item/material/butterfly, /obj/item/material/knife, - /obj/item/melee/energy/sword, - /obj/item/shield/energy, + /obj/item/melee/transforming/energy/sword, + /obj/item/shield/transforming/energy, /obj/item/ammo_casing/, /obj/item/ammo_magazine/, /obj/item/storage/box/beanbags, diff --git a/code/game/objects/items/storage/lockbox.dm b/code/game/objects/items/storage/lockbox.dm index 558830cbd480..98673ed4e5c1 100644 --- a/code/game/objects/items/storage/lockbox.dm +++ b/code/game/objects/items/storage/lockbox.dm @@ -36,7 +36,7 @@ return else to_chat(user, "Access Denied") - else if(istype(W, /obj/item/melee/energy/blade)) + else if(istype(W, /obj/item/melee/ninja_energy_blade)) if(emag_act(INFINITY, user, W, "The locker has been sliced open by [user] with an energy blade!", "You hear metal being sliced and sparks flying.")) var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() spark_system.set_up(5, 0, src.loc) diff --git a/code/game/objects/items/storage/secure.dm b/code/game/objects/items/storage/secure.dm index ee732cef7faf..1c4f05bca68a 100644 --- a/code/game/objects/items/storage/secure.dm +++ b/code/game/objects/items/storage/secure.dm @@ -33,7 +33,7 @@ /obj/item/storage/secure/attackby(obj/item/W as obj, mob/user as mob) if(locked) - if (istype(W, /obj/item/melee/energy/blade) && emag_act(INFINITY, user, "You slice through the lock of \the [src]")) + if (istype(W, /obj/item/melee/ninja_energy_blade) && emag_act(INFINITY, user, "You slice through the lock of \the [src]")) var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() spark_system.set_up(5, 0, src.loc) spark_system.start() diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index 710acf38d926..03498f3fb1e0 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -28,7 +28,7 @@ new /obj/item/plastique(src) if("murder") - new /obj/item/melee/energy/sword(src) + new /obj/item/melee/transforming/energy/sword(src) new /obj/item/clothing/glasses/thermal/syndi(src) new /obj/item/card/emag(src) new /obj/item/clothing/shoes/syndigaloshes(src) diff --git a/code/game/objects/items/tools/switchtool.dm b/code/game/objects/items/tools/switchtool.dm index c6d9b2bd7545..ae87558fd9e3 100644 --- a/code/game/objects/items/tools/switchtool.dm +++ b/code/game/objects/items/tools/switchtool.dm @@ -169,11 +169,6 @@ /obj/item/switchtool/proc/get_switchtool_enum(obj/item/I) return tools[I] -/obj/item/switchtool/handle_shield(mob/user) - if(get_switchtool_enum(deployed) == SWITCHTOOL_SHIELD) - return TRUE - return FALSE - /obj/item/switchtool/update_overlays() . = ..() if(!deployed) @@ -344,8 +339,8 @@ /obj/item/multitool/holoswitch = SWITCHTOOL_MULTITOOL, /obj/item/flashlight/holoswitch = SWITCHTOOL_LIGHT, /obj/item/soap/holoswitch = SWITCHTOOL_SOAP, - /obj/item/melee/energy/sword/holoswitch = SWITCHTOOL_SWORD, - /obj/item/shield/holoswitch = SWITCHTOOL_SHIELD + /obj/item/melee/transforming/energy/sword/holoswitch = SWITCHTOOL_SWORD, + // /obj/item/shield/holoswitch = SWITCHTOOL_SHIELD ) tool_functions = list( TOOL_SCALPEL, @@ -484,7 +479,7 @@ desc = "This should not exist." tool_speed = 0.9 -/obj/item/melee/energy/sword/holoswitch +/obj/item/melee/transforming/energy/sword/holoswitch name = "hardlight blade" desc = "This should not exist." diff --git a/code/game/objects/items/weapons/cigs_lighters.dm b/code/game/objects/items/weapons/cigs_lighters.dm index 630b93d5e7c4..58db3effd425 100644 --- a/code/game/objects/items/weapons/cigs_lighters.dm +++ b/code/game/objects/items/weapons/cigs_lighters.dm @@ -289,8 +289,8 @@ CIGARETTE PACKETS ARE IN FANCY.DM /obj/item/clothing/mask/smokable/cigarette/attackby(obj/item/W as obj, mob/user as mob) ..() - if(istype(W, /obj/item/melee/energy/sword)) - var/obj/item/melee/energy/sword/S = W + if(istype(W, /obj/item/melee/transforming/energy/sword)) + var/obj/item/melee/transforming/energy/sword/S = W if(S.active) light("[user] swings their [W], barely missing their nose. They light their [name] in the process.") @@ -464,7 +464,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM quench() /obj/item/clothing/mask/smokable/pipe/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W, /obj/item/melee/energy/sword)) + if(istype(W, /obj/item/melee/transforming/energy/sword)) return ..() diff --git a/code/game/objects/items/weapons/grenades/concussion.dm b/code/game/objects/items/weapons/grenades/concussion.dm index b8d5da08343b..c3ea4db24d4a 100644 --- a/code/game/objects/items/weapons/grenades/concussion.dm +++ b/code/game/objects/items/weapons/grenades/concussion.dm @@ -84,5 +84,5 @@ var/turf/O = get_turf(src) if(!O) return - src.fragmentate(O, num_fragments, spread_range, fragment_types) + shrapnel_explosion(num_fragments, spread_range, fragment_types) ..() diff --git a/code/game/objects/items/weapons/grenades/explosive.dm b/code/game/objects/items/weapons/grenades/explosive.dm index 0eae29d84cca..c750cac61625 100644 --- a/code/game/objects/items/weapons/grenades/explosive.dm +++ b/code/game/objects/items/weapons/grenades/explosive.dm @@ -21,7 +21,7 @@ if(explosion_size) on_explosion(O) - src.fragmentate(O, num_fragments, spread_range, fragment_types) + shrapnel_explosion(num_fragments, spread_range, fragment_types) qdel(src) /obj/item/grenade/explosive/proc/on_explosion(var/turf/O) @@ -38,34 +38,6 @@ fragment_types = list(/obj/projectile/bullet/pellet/fragment) num_fragments = 200 //total number of fragments produced by the grenade - - -/obj/proc/fragmentate(var/turf/T=get_turf(src), var/fragment_number = 30, var/spreading_range = 5, var/list/fragtypes=list(/obj/projectile/bullet/pellet/fragment/)) - set waitfor = 0 - var/list/target_turfs = getcircle(T, spreading_range) - var/fragments_per_projectile = round(fragment_number/target_turfs.len) - - for(var/turf/O in target_turfs) - sleep(0) - var/fragment_type = pickweight(fragtypes) - var/obj/projectile/bullet/pellet/fragment/P = new fragment_type(T) - P.pellets = fragments_per_projectile - P.shot_from = name - - P.old_style_target(O) - P.fire() - - //Make sure to hit any mobs in the source turf - for(var/mob/living/M in T) - //lying on a frag grenade while the grenade is on the ground causes you to absorb most of the shrapnel. - //you will most likely be dead, but others nearby will be spared the fragments that hit you instead. - if(M.lying && isturf(src.loc)) - P.projectile_attack_mob(M, 0, 5) - else if(!M.lying && src.loc != get_turf(src)) //if it's not on the turf, it must be in the mob! - P.projectile_attack_mob(M, 0, 25) //you're holding a grenade, dude! - else - P.projectile_attack_mob(M, 0, 100) //otherwise, allow a decent amount of fragments to pass - /obj/item/grenade/explosive/mini name = "mini fragmentation grenade" desc = "A miniaturized fragmentation grenade, this one poses relatively little threat on its own." diff --git a/code/game/objects/items/weapons/grenades/flashbang.dm b/code/game/objects/items/weapons/grenades/flashbang.dm index 7018d5f8d96f..1a447da35f2b 100644 --- a/code/game/objects/items/weapons/grenades/flashbang.dm +++ b/code/game/objects/items/weapons/grenades/flashbang.dm @@ -110,7 +110,7 @@ var/turf/O = get_turf(src) if(!O) return - src.fragmentate(O, num_fragments, spread_range, fragment_types) + shrapnel_explosion(num_fragments, spread_range, fragment_types) ..() /obj/item/grenade/flashbang/stingbang/shredbang diff --git a/code/game/objects/items/weapons/grenades/projectile.dm b/code/game/objects/items/weapons/grenades/projectile.dm index 3b781ef46118..2621d12150bf 100644 --- a/code/game/objects/items/weapons/grenades/projectile.dm +++ b/code/game/objects/items/weapons/grenades/projectile.dm @@ -7,6 +7,7 @@ //The radius of the circle used to launch projectiles. Lower values mean less projectiles are used but if set too low gaps may appear in the spread pattern var/spread_range = 7 + var/total_pellets = 1 // default value of 1 just forces one per turf as we round up loadable = FALSE @@ -17,12 +18,9 @@ if(!O) return - src.launch_many_projectiles(O, spread_range, projectile_types) - + shrapnel_explosion(total_pellets, spread_range, projectile_types) qdel(src) - - /obj/item/grenade/shooter/rubber name = "rubber pellet grenade" desc = "An anti-riot grenade that fires a cloud of rubber projectiles upon detonation." @@ -47,30 +45,3 @@ /obj/item/grenade/shooter/energy/tesla name = "tesla grenade" projectile_types = list(/obj/projectile/beam/chain_lightning/lesser) - - -// This is just fragmentate, but less specific. Don't know how to make either of them less awful, at the moment -/obj/proc/launch_many_projectiles(var/turf/T=get_turf(src), var/spreading_range = 5, var/list/projectiletypes=list(/obj/projectile/bullet/pistol/rubber)) - set waitfor = 0 - var/list/target_turfs = getcircle(T, spreading_range) - - for(var/turf/O in target_turfs) - sleep(0) - var/shot_type = pick(projectiletypes) - - var/obj/projectile/P = new shot_type(T) - P.shot_from = src.name - - P.old_style_target(O) - P.fire() - - //Make sure to hit any mobs in the source turf - for(var/mob/living/M in T) - //lying on a frag grenade while the grenade is on the ground causes you to absorb most of the shrapnel. - //you will most likely be dead, but others nearby will be spared the fragments that hit you instead. - if(M.lying && isturf(src.loc)) - P.projectile_attack_mob(M, 0, 5) - else if(!M.lying && src.loc != get_turf(src)) //if it's not on the turf, it must be in the mob! - P.projectile_attack_mob(M, 0, 25) //you're holding a grenade, dude! - else - P.projectile_attack_mob(M, 0, 100) //otherwise, allow a decent amount of fragments to pass diff --git a/code/game/objects/items/weapons/material/knives.dm b/code/game/objects/items/weapons/material/knives.dm index 2c7c409f1a73..a7fa5162c084 100644 --- a/code/game/objects/items/weapons/material/knives.dm +++ b/code/game/objects/items/weapons/material/knives.dm @@ -171,12 +171,10 @@ item_state = "armblade" slot_flags = NONE -/obj/item/material/knife/machete/armblade/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(default_parry_check(user, attacker, damage_source) && prob(33)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(user.loc, 'sound/weapons/punchmiss.ogg', 50, 1) - return TRUE - return FALSE + passive_parry = /datum/passive_parry{ + parry_chance_projectile = 0; + parry_chance_default = 33; + } /obj/item/material/knife/machete/armblade/hardsuit var/obj/item/hardsuit_module/armblade/storing_module diff --git a/code/game/objects/items/weapons/material/material.dm b/code/game/objects/items/weapons/material/material.dm index f1ebf5f3b956..c3394a87aea4 100644 --- a/code/game/objects/items/weapons/material/material.dm +++ b/code/game/objects/items/weapons/material/material.dm @@ -36,6 +36,7 @@ // var/dulled_divisor = 0.1 //Just drops the damage to a tenth // var/drops_debris = 1 + /obj/item/material/Initialize(mapload, material) if(!isnull(material)) material_parts = material diff --git a/code/game/objects/items/weapons/material/swords.dm b/code/game/objects/items/weapons/material/swords.dm index 0b4cdc519d42..b29f86dd53b5 100644 --- a/code/game/objects/items/weapons/material/swords.dm +++ b/code/game/objects/items/weapons/material/swords.dm @@ -11,19 +11,17 @@ pickup_sound = 'sound/items/pickup/sword.ogg' force_multiplier = 1.5 + passive_parry = /datum/passive_parry/melee{ + parry_chance_default = 50; + parry_chance_projectile = 10; + } + /obj/item/material/sword/plasteel material_parts = /datum/material/plasteel /obj/item/material/sword/durasteel material_parts = /datum/material/durasteel -/obj/item/material/sword/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(unique_parry_check(user, attacker, damage_source) && prob(50)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(user.loc, 'sound/weapons/punchmiss.ogg', 50, 1) - return 1 - return 0 - /obj/item/material/sword/suicide_act(mob/user) var/datum/gender/TU = GLOB.gender_datums[user.get_visible_gender()] viewers(user) << "[user] is falling on the [src.name]! It looks like [TU.he] [TU.is] trying to commit suicide." @@ -63,31 +61,13 @@ SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi', ) -/obj/item/material/sword/sabre/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(default_parry_check(user, attacker, damage_source) && prob(60)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - - playsound(user.loc, 'sound/items/drop/knife.ogg', 50, 1) - return 1 - if(unique_parry_check(user, attacker, damage_source) && prob(50)) - user.visible_message("\The [user] deflects [attack_text] with \the [src]!") - - playsound(user.loc, 'sound/weapons/plasma_cutter.ogg', 50, 1) - return 1 - - return 0 - -/obj/item/material/sword/sabre/unique_parry_check(mob/user, mob/attacker, atom/damage_source) - if(user.incapacitated() || !istype(damage_source, /obj/projectile/)) - return 0 - - var/bad_arc = global.reverse_dir[user.dir] - if(!check_shield_arc(user, bad_arc, damage_source, attacker)) - return 0 - - return 1 + passive_parry = /datum/passive_parry/melee{ + parry_chance_default = 50; + } -//meant to play when unsheathing the blade from the sabre sheath. +// meant to play when unsheathing the blade from the sabre sheath. +// todo: -_- this should be on the sheath +// todo: we need a better way to do unsheath sounds for weapons and storage... /obj/item/material/sword/sabre/on_enter_storage(datum/object_system/storage/storage) . = ..() playsound(loc, 'sound/effects/holster/sheathin.ogg', 50, 1) diff --git a/code/game/objects/items/weapons/material/twohanded.dm b/code/game/objects/items/weapons/material/twohanded.dm index 4e615154fed7..16f3fb71cd2b 100644 --- a/code/game/objects/items/weapons/material/twohanded.dm +++ b/code/game/objects/items/weapons/material/twohanded.dm @@ -28,6 +28,10 @@ drop_sound = 'sound/items/drop/sword.ogg' pickup_sound = 'sound/items/pickup/sword.ogg' + passive_parry = /datum/passive_parry/melee{ + parry_chance_melee = 15; + } + /obj/item/material/twohanded/update_held_icon() var/mob/living/M = loc if(istype(M) && M.can_wield_item(src) && is_held_twohanded(M)) @@ -50,14 +54,6 @@ . = ..() update_icon() -//Allow a small chance of parrying melee attacks when wielded - maybe generalize this to other weapons someday -/obj/item/material/twohanded/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(wielded && default_parry_check(user, attacker, damage_source) && prob(15)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(user.loc, 'sound/weapons/punchmiss.ogg', 50, 1) - return 1 - return 0 - /obj/item/material/twohanded/update_icon() icon_state = "[base_icon][wielded]" item_state = icon_state diff --git a/code/game/objects/items/weapons/melee/deflect.dm b/code/game/objects/items/weapons/melee/deflect.dm deleted file mode 100644 index e951f4d5c5a1..000000000000 --- a/code/game/objects/items/weapons/melee/deflect.dm +++ /dev/null @@ -1,31 +0,0 @@ -/* - * The home of basic deflect / defense code. - */ - -/obj/item/melee - var/defend_chance = 5 // The base chance for the weapon to parry. - var/projectile_parry_chance = 0 // The base chance for a projectile to be deflected. - -/obj/item/melee/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(.) - return . - if(default_parry_check(user, attacker, damage_source) && prob(defend_chance)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - return 1 - if(unique_parry_check(user, attacker, damage_source) && prob(projectile_parry_chance)) - user.visible_message("\The [user] deflects [attack_text] with \the [src]!") - return 1 - - return 0 - -/obj/item/melee/unique_parry_check(mob/user, mob/attacker, atom/damage_source) - if(.) - return . - if(user.incapacitated() || !istype(damage_source, /obj/projectile)) - return 0 - - var/bad_arc = global.reverse_dir[user.dir] - if(!check_shield_arc(user, bad_arc, damage_source, attacker)) - return 0 - - return 1 diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm deleted file mode 100644 index 467de03f3614..000000000000 --- a/code/game/objects/items/weapons/melee/energy.dm +++ /dev/null @@ -1,729 +0,0 @@ -/obj/item/melee/energy - var/active = 0 - var/active_force - var/active_throwforce - var/active_w_class - var/active_embed_chance = 0 //In the off chance one of these is supposed to embed, you can just tweak this var - icon = 'icons/obj/weapons.dmi' - sharp = 0 - edge = 0 - armor_penetration = 50 - atom_flags = NOCONDUCT | NOBLOODY - var/lrange = 2 - var/lpower = 2 - var/lcolor = "#0099FF" - var/colorable = FALSE - var/rainbow = FALSE - // If it uses energy. - var/use_cell = FALSE - var/hitcost = 120 - var/obj/item/cell/bcell = null - var/cell_type = /obj/item/cell/device - item_icons = list( - SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', - SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi', - ) - -/obj/item/melee/energy/proc/activate(mob/living/user) - if(active) - return - active = 1 - if(rainbow) - item_state = "[icon_state]_blade_rainbow" - else - item_state = "[icon_state]_blade" - embed_chance = active_embed_chance - damage_force = active_force - throw_force = active_throwforce - sharp = 1 - edge = 1 - set_weight_class(active_w_class) - playsound(user, 'sound/weapons/saberon.ogg', 50, 1) - update_icon() - set_light(lrange, lpower, lcolor) - to_chat(user, "Alt-click to recolor it.") - -/obj/item/melee/energy/proc/deactivate(mob/living/user) - if(!active) - return - playsound(user, 'sound/weapons/saberoff.ogg', 50, 1) - item_state = "[icon_state]" - active = 0 - embed_chance = initial(embed_chance) - damage_force = initial(damage_force) - throw_force = initial(throw_force) - sharp = initial(sharp) - edge = initial(edge) - set_weight_class(initial(w_class)) - update_icon() - set_light(0,0) - -/obj/item/melee/energy/proc/use_charge(var/cost) - if(active) - if(bcell) - if(bcell.checked_use(cost)) - return 1 - else - return 0 - return null - -/obj/item/melee/energy/examine(mob/user, dist) - . = ..() - if(use_cell) - if(bcell) - . += "The blade is [round(bcell.percent())]% charged." - if(!bcell) - . += "The blade does not have a power source installed." - -/obj/item/melee/energy/attack_self(mob/user) - . = ..() - if(.) - return - if(use_cell) - if((!bcell || bcell.charge < hitcost) && !active) - to_chat(user, "\The [src] does not seem to have power.") - return - - var/datum/gender/TU = GLOB.gender_datums[user.get_visible_gender()] - if (active) - if ((MUTATION_CLUMSY in user.mutations) && prob(50)) - user.visible_message("\The [user] accidentally cuts [TU.himself] with \the [src].",\ - "You accidentally cut yourself with \the [src].") - var/mob/living/carbon/human/H = ishuman(user)? user : null - H.take_random_targeted_damage(brute = 5, burn = 5) - deactivate(user) - else - activate(user) - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - - add_fingerprint(user) - return - -/obj/item/melee/energy/suicide_act(mob/user) - var/datum/gender/TU = GLOB.gender_datums[user.get_visible_gender()] - if(active) - user.visible_message(pick("\The [user] is slitting [TU.his] stomach open with \the [src]! It looks like [TU.he] [TU.is] trying to commit seppuku.",\ - "\The [user] is falling on \the [src]! It looks like [TU.he] [TU.is] trying to commit suicide.")) - return (BRUTELOSS|FIRELOSS) - -/obj/item/melee/energy/attack_mob(mob/target, mob/user, clickchain_flags, list/params, mult, target_zone, intent) - . = ..() - if(active && use_cell) - if(!use_charge(hitcost)) - deactivate(user) - visible_message("\The [src]'s blade flickers, before deactivating.") - -/obj/item/melee/energy/attackby(obj/item/W, mob/user) - if(istype(W, /obj/item/multitool) && colorable && !active) - if(!rainbow) - rainbow = TRUE - else - rainbow = FALSE - to_chat(user, "You manipulate the color controller in [src].") - update_icon() - if(use_cell) - if(istype(W, cell_type)) - if(!bcell) - if(!user.attempt_insert_item_for_installation(W, src)) - return - bcell = W - to_chat(user, "You install a cell in [src].") - update_icon() - else - to_chat(user, "[src] already has a cell.") - else if(W.is_screwdriver() && bcell) - bcell.update_icon() - bcell.forceMove(get_turf(loc)) - bcell = null - to_chat(user, "You remove the cell from \the [src].") - deactivate() - update_icon() - return - return ..() - -/obj/item/melee/energy/get_cell(inducer) - return bcell - -/obj/item/melee/energy/update_icon() - . = ..() - var/mutable_appearance/blade_overlay = mutable_appearance(icon, "[icon_state]_blade") - blade_overlay.color = lcolor - color = lcolor - if(rainbow) - blade_overlay = mutable_appearance(icon, "[icon_state]_blade_rainbow") - blade_overlay.color = "FFFFFF" - color = "FFFFFF" - cut_overlays() //So that it doesn't keep stacking overlays non-stop on top of each other - if(active) - add_overlay(blade_overlay) - if(istype(usr,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = usr - H.update_inv_l_hand() - H.update_inv_r_hand() - -/obj/item/melee/energy/AltClick(mob/living/user) - if(!colorable) //checks if is not colorable - return - if(!in_range(src, user)) //Basic checks to prevent abuse - return - if(user.incapacitated() || !istype(user)) - to_chat(user, "You can't do that right now!") - return - - if(alert("Are you sure you want to recolor your blade?", "Confirm Recolor", "Yes", "No") == "Yes") - var/energy_color_input = input(usr,"","Choose Energy Color",lcolor) as color|null - if(energy_color_input) - lcolor = "#[sanitize_hexcolor(energy_color_input)]" - color = lcolor - deactivate() - update_icon() - . = ..() - - -/* - * Energy Axe - */ -/obj/item/melee/energy/axe - name = "energy axe" - desc = "An energised battle axe." - icon_state = "eaxe" - item_state = "eaxe" - //active_force = 150 //holy... - active_force = 60 - active_throwforce = 35 - active_w_class = WEIGHT_CLASS_HUGE - //damage_force = 40 - //throw_force = 25 - damage_force = 20 - throw_force = 10 - throw_speed = 1 - throw_range = 5 - w_class = WEIGHT_CLASS_NORMAL - origin_tech = list(TECH_MAGNET = 3, TECH_COMBAT = 4) - attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") - sharp = 1 - edge = 1 - can_cleave = TRUE - -/obj/item/melee/energy/axe/activate(mob/living/user) - ..() - damtype = SEARING - to_chat(user, "\The [src] is now energised.") - -/obj/item/melee/energy/axe/deactivate(mob/living/user) - ..() - damtype = BRUTE - to_chat(user, "\The [src] is de-energised. It's just a regular axe now.") - -/obj/item/melee/energy/axe/suicide_act(mob/user) - var/datum/gender/TU = GLOB.gender_datums[user.get_visible_gender()] - visible_message("\The [user] swings \the [src] towards [TU.his] head! It looks like [TU.he] [TU.is] trying to commit suicide.") - return (BRUTELOSS|FIRELOSS) - -/obj/item/melee/energy/axe/charge - name = "charge axe" - desc = "An energised axe." - active_force = 35 - active_throwforce = 20 - damage_force = 15 - use_cell = TRUE - hitcost = 120 - -/obj/item/melee/energy/axe/charge/loaded/Initialize(mapload) - . = ..() - bcell = new/obj/item/cell/device/weapon(src) - -/* - * Energy Sword - */ -/obj/item/melee/energy/sword - color - name = "energy sword" - desc = "May the damage_force be within you." - icon_state = "esword" - item_state = "esword" - active_force = 30 - active_throwforce = 20 - active_w_class = WEIGHT_CLASS_BULKY - damage_force = 3 - throw_force = 5 - throw_speed = 1 - throw_range = 5 - w_class = WEIGHT_CLASS_SMALL - atom_flags = NOBLOODY - origin_tech = list(TECH_MAGNET = 3, TECH_ILLEGAL = 4) - sharp = 1 - edge = 1 - colorable = TRUE - drop_sound = 'sound/items/drop/sword.ogg' - pickup_sound = 'sound/items/pickup/sword.ogg' - projectile_parry_chance = 65 - -/obj/item/melee/energy/sword/dropped(mob/user, atom_flags, atom/newLoc) - . = ..() - if(!istype(loc,/mob)) - deactivate(user) - -/obj/item/melee/energy/sword/activate(mob/living/user) - if(!active) - to_chat(user, "\The [src] is now energised.") - - ..() - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - -/obj/item/melee/energy/sword/deactivate(mob/living/user) - if(active) - to_chat(user, "\The [src] deactivates!") - ..() - attack_verb = list() - -/obj/item/melee/energy/sword/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(active && default_parry_check(user, attacker, damage_source) && prob(60)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) - return 1 - if(active && unique_parry_check(user, attacker, damage_source) && prob(projectile_parry_chance)) - user.visible_message("\The [user] deflects [attack_text] with \the [src]!") - - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) - return 1 - - return 0 - -/obj/item/melee/energy/sword/unique_parry_check(mob/user, mob/attacker, atom/damage_source) - if(user.incapacitated() || !istype(damage_source, /obj/projectile/)) - return 0 - - var/bad_arc = global.reverse_dir[user.dir] - if(!check_shield_arc(user, bad_arc, damage_source, attacker)) - return 0 - - return 1 - -/obj/item/melee/energy/sword/attackby(obj/item/W, mob/living/user, params) - if(istype(W, /obj/item/melee/energy/sword)) - if(HAS_TRAIT(W, TRAIT_ITEM_NODROP) || HAS_TRAIT(src, TRAIT_ITEM_NODROP)) - to_chat(user, "\the [HAS_TRAIT(src, TRAIT_ITEM_NODROP) ? src : W] is stuck to your hand, you can't attach it to \the [HAS_TRAIT(src, TRAIT_ITEM_NODROP) ? W : src]!") - return - if(istype(W, /obj/item/melee/energy/sword/charge)) - to_chat(user,"These blades are incompatible, you can't attach them to each other!") - return - else - to_chat(user, "You combine the two energy swords, making a single supermassive blade! You're cool.") - new /obj/item/melee/energy/sword/dualsaber(user.drop_location()) - qdel(W) - qdel(src) - else - return ..() - -/obj/item/melee/energy/sword/pirate - name = "energy cutlass" - desc = "Arrrr matey." - icon_state = "cutlass" - item_state = "cutlass" - colorable = TRUE - -//Return of the King -/obj/item/melee/energy/sword/dualsaber - name = "double-bladed energy sword" - desc = "Handle with care." - icon_state = "dualsaber" - item_state = "dualsaber" - damage_force = 3 - active_force = 60 - throw_force = 5 - throw_speed = 3 - armor_penetration = 35 - colorable = TRUE - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - projectile_parry_chance = 85 - -/* - *Ionic Rapier - */ - -/obj/item/melee/energy/sword/ionic_rapier - name = "ionic rapier" - desc = "Designed specifically for disrupting electronics at close range, it is extremely deadly against synthetics, but almost harmless to pure organic targets." - description_info = "This is a dangerous melee weapon that will deliver a moderately powerful electromagnetic pulse to whatever it strikes. \ - Striking a lesser robotic entity will compel it to attack you, as well. It also does extra burn damage to robotic entities, but it does \ - very little damage to purely organic targets." - icon_state = "ionrapier" - item_state = "ionrapier" - active_force = 5 - active_throwforce = 3 - active_embed_chance = 0 - sharp = 1 - edge = 1 - armor_penetration = 0 - atom_flags = NOBLOODY - lrange = 2 - lpower = 2 - lcolor = "#0000FF" - projectile_parry_chance = 30 // It's not specifically designed for cutting and slashing, but it can still, maybe, save your life. - -/obj/item/melee/energy/sword/ionic_rapier/afterattack(atom/target, mob/user, clickchain_flags, list/params) - if(istype(target, /obj) && (clickchain_flags & CLICKCHAIN_HAS_PROXIMITY) && active) - // EMP stuff. - var/obj/O = target - O.emp_act(3) // A weaker severity is used because this has infinite uses. - playsound(get_turf(O), 'sound/effects/EMPulse.ogg', 100, 1) - user.setClickCooldown(user.get_attack_speed(src)) // A lot of objects don't set click delay. - return ..() - -/obj/item/melee/energy/sword/ionic_rapier/melee_mob_hit(mob/target, mob/user, clickchain_flags, list/params, mult, target_zone, intent) - . = ..() - var/mob/living/L = target - if(!istype(L)) - return - if(L.isSynthetic() && active) - // Do some extra damage. Not a whole lot more since emp_act() is pretty nasty on FBPs already. - L.emp_act(3) // A weaker severity is used because this has infinite uses. - playsound(get_turf(L), 'sound/effects/EMPulse.ogg', 100, 1) - L.adjustFireLoss(damage_force * 3) // 15 Burn, for 20 total. - playsound(get_turf(L), 'sound/weapons/blade1.ogg', 100, 1) - - // Make lesser robots really mad at us. - if(L.mob_class & MOB_CLASS_SYNTHETIC) - if(L.has_polaris_AI()) - L.taunt(user) - L.adjustFireLoss(damage_force * 6) // 30 Burn, for 50 total. - -/obj/item/melee/energy/sword/ionic_rapier/lance - name = "zero-point lance" - desc = "Designed specifically for disrupting electronics at relatively close range, however it is still capable of dealing some damage to living beings." - active_force = 20 - armor_penetration = 15 - reach = 2 - -/* - * Charge blade. Uses a cell, and costs energy per strike. - */ - -/obj/item/melee/energy/sword/charge - name = "charge sword" - desc = "A small, handheld device which emits a high-energy 'blade'." - origin_tech = list(TECH_COMBAT = 5, TECH_MAGNET = 3, TECH_ILLEGAL = 4) - active_force = 25 - armor_penetration = 25 - projectile_parry_chance = 40 - colorable = TRUE - use_cell = TRUE - hitcost = 75 - -/obj/item/melee/energy/sword/charge/loaded/Initialize(mapload) - . = ..() - bcell = new/obj/item/cell/device/weapon(src) - -/obj/item/melee/energy/sword/charge/attackby(obj/item/W, mob/living/user, params) - if(istype(W, /obj/item/melee/energy/sword/charge)) - if(HAS_TRAIT(W, TRAIT_ITEM_NODROP) || HAS_TRAIT(src, TRAIT_ITEM_NODROP)) - to_chat(user, "\the [HAS_TRAIT(src, TRAIT_ITEM_NODROP) ? src : W] is stuck to your hand, you can't attach it to \the [HAS_TRAIT(src, TRAIT_ITEM_NODROP) ? W : src]!") - return - else - to_chat(user, "You combine the two charge swords, making a single supermassive blade! You're cool.") - new /obj/item/melee/energy/sword/charge/dualsaber(user.drop_location()) - qdel(W) - qdel(src) - else - return ..() - -//Charge Type Double Esword -/obj/item/melee/energy/sword/charge/dualsaber - name = "double-bladed charge sword" - desc = "Make sure you bought batteries." - icon_state = "dualsaber" - item_state = "dualsaber" - damage_force = 3 - active_force = 50 - throw_force = 5 - throw_speed = 3 - armor_penetration = 30 - colorable = TRUE - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - projectile_parry_chance = 65 - hitcost = 150 - -//Energy Blade (ninja uses this) - -//Can't be activated or deactivated, so no reason to be a subtype of energy -/obj/item/melee/energy/blade - name = "energy blade" - desc = "A concentrated beam of energy in the shape of a blade. Very stylish... and lethal." - icon_state = "blade" - item_state = "blade" - damage_force = 40 //Normal attacks deal very high damage - about the same as wielded fire axe - armor_penetration = 100 - sharp = 1 - edge = 1 - anchored = 1 // Never spawned outside of inventory, should be fine. - throw_force = 1 //Throwing or dropping the item deletes it. - throw_speed = 1 - throw_range = 1 - w_class = WEIGHT_CLASS_BULKY//So you can't hide it in your pocket or some such. - atom_flags = NOBLOODY - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - var/mob/living/creator - var/datum/effect_system/spark_spread/spark_system - projectile_parry_chance = 60 - lcolor = "#00FF00" - -/obj/item/melee/energy/blade/Initialize(mapload) - . = ..() - spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, src) - spark_system.attach(src) - - START_PROCESSING(SSobj, src) - set_light(lrange, lpower, lcolor) - -/obj/item/melee/energy/blade/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/melee/energy/blade/attack_self(mob/user) - . = ..() - if(.) - return - qdel(src) - -/obj/item/melee/energy/blade/dropped(mob/user, atom_flags, atom/newLoc) - . = ..() - qdel(src) - -/obj/item/melee/energy/blade/process(delta_time) - if(!creator || loc != creator || !creator.is_holding(src)) - // Tidy up a bit. - if(istype(loc,/mob/living)) - var/mob/living/carbon/human/host = loc - if(istype(host)) - for(var/obj/item/organ/external/organ in host.organs) - for(var/obj/item/O in organ.implants) - if(O == src) - organ.implants -= src - host.pinned -= src - host.embedded -= src - host._handle_inventory_hud_remove(src) - qdel(src) - -/obj/item/melee/energy/blade/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(default_parry_check(user, attacker, damage_source) && prob(60)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) - return 1 - if(unique_parry_check(user, attacker, damage_source) && prob(projectile_parry_chance)) - user.visible_message("\The [user] deflects [attack_text] with \the [src]!") - - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) - return 1 - - return 0 - -/obj/item/melee/energy/blade/unique_parry_check(mob/user, mob/attacker, atom/damage_source) - - if(user.incapacitated() || !istype(damage_source, /obj/projectile/)) - return 0 - - var/bad_arc = global.reverse_dir[user.dir] - if(!check_shield_arc(user, bad_arc, damage_source, attacker)) - return 0 - - return 1 - -//Energy Spear - -/obj/item/melee/energy/spear - name = "energy spear" - desc = "Concentrated energy forming a sharp tip at the end of a long rod." - icon_state = "espear" - armor_penetration = 75 - sharp = 1 - edge = 1 - damage_force = 5 - throw_force = 10 - throw_speed = 7 - throw_range = 11 - reach = 2 - w_class = WEIGHT_CLASS_BULKY - active_force = 25 - active_throwforce = 30 - active_w_class = WEIGHT_CLASS_HUGE - colorable = TRUE - lcolor = "#800080" - -/obj/item/melee/energy/spear/activate(mob/living/user) - if(!active) - to_chat(user, "\The [src] is now energised.") - ..() - attack_verb = list("jabbed", "stabbed", "impaled") - AddComponent(/datum/component/jousting) - - -/obj/item/melee/energy/spear/deactivate(mob/living/user) - if(active) - to_chat(user, "\The [src] deactivates!") - ..() - attack_verb = list("whacked", "beat", "slapped", "thonked") - DelComponent(/datum/component/jousting) - -/obj/item/melee/energy/spear/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(active && default_parry_check(user, attacker, damage_source) && prob(50)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) - return 1 - return 0 - -/obj/item/melee/energy/hfmachete // ported from /vg/station - vgstation-coders/vgstation13#13913, fucked up by hatterhat - name = "high-frequency machete" - desc = "A high-frequency broad blade used either as an implement or in combat like a short sword." - icon_state = "hfmachete0" - sharp = TRUE - edge = TRUE - damage_force = 20 // You can be crueler than that, Jack. - throw_force = 40 - throw_speed = 8 - throw_range = 8 - w_class = WEIGHT_CLASS_NORMAL - siemens_coefficient = 1 - origin_tech = list(TECH_COMBAT = 3, TECH_ILLEGAL = 3) - attack_verb = list("attacked", "diced", "cleaved", "torn", "cut", "slashed") - armor_penetration = 50 - var/base_state = "hfmachete" - attack_sound = "machete_hit_sound" // dont mind the meaty hit sounds if you hit something that isnt meaty - can_cleave = TRUE - embed_chance = 0 // let's not - -/obj/item/melee/energy/hfmachete/update_icon() - icon_state = "[base_state][active]" - -/obj/item/melee/energy/hfmachete/attack_self(mob/user) - toggleActive(user) - add_fingerprint(user) - -/obj/item/melee/energy/hfmachete/proc/toggleActive(mob/user, var/togglestate = "") - switch(togglestate) - if("on") - active = 1 - if("off") - active = 0 - else - active = !active - if(active) - damage_force = 40 - throw_force = 20 - throw_speed = 3 - // sharpness = 1.7 - // sharpness_flags += HOT_EDGE | CUT_WALL | CUT_AIRLOCK - if only there a good sharpness system - armor_penetration = 100 - to_chat(user, " [src] starts vibrating.") - playsound(user, 'sound/weapons/hf_machete/hfmachete1.ogg', 40, 0) - set_weight_class(WEIGHT_CLASS_BULKY) - // user.lazy_register_event(/lazy_event/on_moved, src, PROC_REF(mob_moved)) - else - damage_force = initial(damage_force) - throw_force = initial(throw_force) - throw_speed = initial(throw_speed) - // sharpness = initial(sharpness) - // sharpness_flags = initial(sharpness_flags) - if only there was a good sharpness system - armor_penetration = initial(armor_penetration) - to_chat(user, " [src] stops vibrating.") - playsound(user, 'sound/weapons/hf_machete/hfmachete0.ogg', 40, 0) - set_weight_class(WEIGHT_CLASS_NORMAL) - // user.lazy_unregister_event(/lazy_event/on_moved, src, PROC_REF(mob_moved)) - update_icon() - -/obj/item/melee/energy/hfmachete/afterattack(atom/target, mob/user, clickchain_flags, list/params) - if(!(clickchain_flags & CLICKCHAIN_HAS_PROXIMITY)) - return - ..() - if(target) - if(istype(target,/obj/effect/plant)) - var/obj/effect/plant/P = target - P.die_off() - -/* -/obj/item/melee/energy/hfmachete/dropped(mob/user, atom_flags, atom/newLoc) - user.lazy_unregister_event(/lazy_event/on_moved, src, PROC_REF(mob_moved)) - -/obj/item/melee/energy/hfmachete/throw_at_old(atom/target, range, speed, thrower) // todo: get silicons to interpret this because >sleeps - if(!usr) - return ..() - spawn() - playsound(src, get_sfx("machete_throw"),30, 0) - animate(src, transform = turn(matrix(), -30), time = 1, loop = -1) - animate(transform = turn(matrix(), -60), time = 1) - animate(transform = turn(matrix(), -90), time = 1) - animate(transform = turn(matrix(), -120), time = 1) - animate(transform = turn(matrix(), -150), time = 1) - animate(transform = null, time = 1) - while(throwing) - sleep(5) - animate(src) - ..(target, range, speed = 3, thrower) -*/ - -// none of these are working properly in testing which is something you absolutely hate to see -/* -/obj/item/melee/energy/hfmachete/throw_at_old(atom/target, range, speed, thrower) - playsound(src, get_sfx("machete_throw"), 30, 0) - . = ..() - -/obj/item/melee/energy/hfmachete/throw_impact(atom/hit_atom, speed) - if(isturf(hit_atom)) - for(var/mob/M in hit_atom) - playsound(M, get_sfx("machete_throw_hit"), 60, 0) - ..() - -/obj/item/melee/energy/hfmachete/attack(mob/M, mob/living/user) - playsound(M, get_sfx("machete_hit"), 50, 0) - ..() -*/ -/* -/obj/item/melee/energy/hfmachete/proc/mob_moved(atom/movable/mover) - if(iscarbon(mover) && active) - for(var/obj/effect/plantsegment/P in range(mover,0)) - qdel(P) - -/obj/item/melee/energy/hfmachete/attackby(obj/item/W, mob/living/user) - ..() - if(istype(W, /obj/item/melee/energy/hfmachete)) - to_chat(user, "You combine the two [W] together, making a single scissor-bladed weapon! You feel fucking invincible!") - qdel(W) - W = null - qdel(src) - var/B = new /obj/item/bloodlust(user.loc) - user.put_in_hands(B) - // blust one day lads. -*/ - -/obj/item/melee/energy/sword/imperial - name = "energy gladius" - desc = "A broad, short energy blade. You'll be glad to have this in a fight." - icon_state = "sword0" - icon = 'icons/obj/weapons_vr.dmi' - item_icons = list(SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi') - -/obj/item/melee/energy/sword/imperial/activate(mob/living/user) - ..() - icon_state = "sword1" diff --git a/code/game/objects/items/weapons/melee/melee.dm b/code/game/objects/items/weapons/melee/melee.dm deleted file mode 100644 index 0856d665d166..000000000000 --- a/code/game/objects/items/weapons/melee/melee.dm +++ /dev/null @@ -1,7 +0,0 @@ -/obj/item/melee - icon = 'icons/obj/weapons.dmi' - attack_sound = "swing_hit" - item_icons = list( - SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', - SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi', - ) diff --git a/code/game/objects/items/weapons/shields.dm b/code/game/objects/items/weapons/shields.dm deleted file mode 100644 index b036965c9baf..000000000000 --- a/code/game/objects/items/weapons/shields.dm +++ /dev/null @@ -1,494 +0,0 @@ -//** Shield Helpers -//These are shared by various items that have shield-like behaviour - -//bad_arc is the ABSOLUTE arc of directions from which we cannot block. If you want to fix it to e.g. the user's facing you will need to rotate the dirs yourself. -/proc/check_shield_arc(mob/user, var/bad_arc, atom/damage_source = null, mob/attacker = null) - //check attack direction - var/attack_dir = 0 //direction from the user to the source of the attack - if(istype(damage_source, /obj/projectile)) - var/obj/projectile/P = damage_source - attack_dir = get_dir(get_turf(user), P.starting) - else if(attacker) - attack_dir = get_dir(get_turf(user), get_turf(attacker)) - else if(damage_source) - attack_dir = get_dir(get_turf(user), get_turf(damage_source)) - - if(!(attack_dir && (attack_dir & bad_arc))) - return 1 - return 0 - -/proc/default_parry_check(mob/user, mob/attacker, atom/damage_source) - //parry only melee attacks - if(istype(damage_source, /obj/projectile) || (attacker && get_dist(user, attacker) > 1) || user.incapacitated()) - return 0 - - //block as long as they are not directly behind us - var/bad_arc = global.reverse_dir[user.dir] //arc of directions from which we cannot block - if(!check_shield_arc(user, bad_arc, damage_source, attacker)) - return 0 - - return 1 - -/obj/item/proc/unique_parry_check(mob/user, mob/attacker, atom/damage_source) // An overrideable version of the above proc. - return default_parry_check(user, attacker, damage_source) - -/obj/item/shield - name = "shield" - var/base_block_chance = 50 - preserve_item = 1 - item_icons = list( - SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', - SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi', - ) - -/obj/item/shield/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(user.incapacitated()) - return 0 - - //block as long as they are not directly behind us - var/bad_arc = global.reverse_dir[user.dir] //arc of directions from which we cannot block - if(check_shield_arc(user, bad_arc, damage_source, attacker)) - if(prob(get_block_chance(user, damage, damage_source, attacker))) - user.visible_message("\The [user] blocks [attack_text] with \the [src]!") - return 1 - return 0 - -/obj/item/shield/proc/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null) - return base_block_chance - -/obj/item/shield/riot - name = "riot shield" - desc = "A shield adept for close quarters engagement. It's also capable of protecting from less powerful projectiles." - icon = 'icons/obj/weapons.dmi' - icon_state = "riot" - slot_flags = SLOT_BACK - damage_force = 5.0 - throw_force = 5.0 - throw_speed = 1 - throw_range = 4 - w_class = WEIGHT_CLASS_BULKY - origin_tech = list(TECH_MATERIAL = 2) - materials_base = list(MAT_GLASS = 7500, MAT_STEEL = 1000) - attack_verb = list("shoved", "bashed") - worth_intrinsic = 300 - var/cooldown = 0 //shield bash cooldown. based on world.time - -/obj/item/shield/riot/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(user.incapacitated()) - return 0 - - //block as long as they are not directly behind us - var/bad_arc = global.reverse_dir[user.dir] //arc of directions from which we cannot block - if(check_shield_arc(user, bad_arc, damage_source, attacker)) - if(prob(get_block_chance(user, damage, damage_source, attacker))) - //At this point, we succeeded in our roll for a block attempt, however these kinds of shields struggle to stand up - //to strong bullets and lasers. They still do fine to pistol rounds of all kinds, however. - if(istype(damage_source, /obj/projectile)) - var/obj/projectile/P = damage_source - if((is_sharp(P) && P.armor_penetration >= 10) || istype(P, /obj/projectile/beam)) - //If we're at this point, the bullet/beam is going to go through the shield, however it will hit for less damage. - //Bullets get slowed down, while beams are diffused as they hit the shield, so these shields are not /completely/ - //useless. Extremely penetrating projectiles will go through the shield without less damage. - user.visible_message("\The [user]'s [src.name] is pierced by [attack_text]!") - if(P.armor_penetration < 30) //PTR bullets and x-rays will bypass this entirely. - P.damage = P.damage / 2 - return 0 - //Otherwise, if we're here, we're gonna stop the attack entirely. - user.visible_message("\The [user] blocks [attack_text] with \the [src]!") - playsound(user.loc, 'sound/weapons/Genhit.ogg', 50, 1) - return 1 - return 0 - -/obj/item/shield/riot/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W, /obj/item/melee/baton)) - if(cooldown < world.time - 25) - user.visible_message("[user] bashes [src] with [W]!") - playsound(user.loc, 'sound/effects/shieldbash.ogg', 50, 1) - cooldown = world.time - else - ..() - -/obj/item/shield/riot/flash - name = "strobe shield" - desc = "A shield with a built in, high intensity light capable of blinding and disorienting suspects. Takes regular handheld flashes as bulbs." - icon_state = "flashshield" - item_state = "flashshield" - var/obj/item/flash/embedded_flash - var/flashfail = 0 - -/obj/item/shield/riot/flash/Initialize(mapload) - . = ..() - embedded_flash = new(src) - -/obj/item/shield/riot/flash/attack_mob(mob/target, mob/user, clickchain_flags, list/params, mult, target_zone, intent) - if(user.a_intent == INTENT_HARM) - return ..() - return embedded_flash.attack_mob(arglist(args)) - -/obj/item/shield/riot/flash/attack_self(mob/user) - . = ..() - if(.) - return - . = embedded_flash.attack_self(user) - update_icon() - -/obj/item/shield/riot/flash/handle_shield(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) - . = ..() - if (. && damage && !embedded_flash.broken) - embedded_flash.melee_interaction_chain() - update_icon() - -/obj/item/shield/riot/flash/attackby(obj/item/W, mob/user) - if(istype(W, /obj/item/flash)) - var/obj/item/flash/flash = W - if(flashfail) - to_chat(user, "No sense replacing it with a broken bulb!") - return - else - to_chat(user, "You begin to replace the bulb...") - if(do_after(user, 20, target = user)) - if(flashfail || !flash || QDELETED(flash)) - return - playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) - qdel(embedded_flash) - embedded_flash = flash - flash.forceMove(src) - update_icon() - return - ..() - -/obj/item/shield/riot/flash/emp_act(severity) - . = ..() - embedded_flash.emp_act(severity) - update_icon() - -/obj/item/shield/riot/flash/update_icon_state() - . = ..() - if(!embedded_flash || embedded_flash.broken) - icon_state = "riot" - item_state = "riot" - else - icon_state = "flashshield" - item_state = "flashshield" - -/obj/item/shield/riot/flash/examine(mob/user, dist) - . = ..() - if (embedded_flash?.broken) - . += "The mounted bulb has burnt out. You can try replacing it with a new one." - -/obj/item/shield/makeshift - name = "metal shield" - desc = "A large shield made of wired and welded sheets of metal. The handle is made of cloth and leather, making it unwieldy." - icon = 'icons/obj/weapons.dmi' - icon_state = "makeshift_shield" - item_state = "metal" - slot_flags = null - damage_force = 10 - throw_force = 7 - -/obj/item/shield/riot/tower - name = "tower shield" - desc = "An immense tower shield. Designed to ensure maximum protection to the user, at the expense of mobility." - item_state = "metal" - icon_state = "metal" - damage_force = 16 - encumbrance = ITEM_ENCUMBRANCE_SHIELD_TOWER - throw_force = 15 //Massive piece of metal - w_class = WEIGHT_CLASS_HUGE - -/obj/item/shield/riot/tower/swat - name = "swat shield" - -/* I don't know if I really want this in the game. I DO want the code though. -/obj/item/shield/riot/implant - name = "hardlight shield implant" - desc = "A hardlight plane of force projected from the implant. While it is capable of withstanding immense amounts of abuse, it will eventually overload from sustained impacts, especially against energy attacks. Recharges while retracted." - item_state = "holoshield" - icon_state = "holoshield" - slowdown = 1 - shield_flags = SHIELD_FLAGS_DEFAULT - integrity_max = 100 - obj_integrity = 100 - can_shatter = FALSE - clothing_flags = ITEM_CAN_BLOCK - shield_flags = SHIELD_FLAGS_DEFAULT | SHIELD_KINETIC_STRONG | SHIELD_DISABLER_DISRUPTED - var/recharge_timerid - var/recharge_delay = 15 SECONDS - -/// Entirely overriden take_damage. This shouldn't exist outside of an implant (other than maybe christmas). -/obj/item/shield/riot/implant/take_damage_legacy(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir, armour_penetration = 0) - obj_integrity -= damage_amount - if(obj_integrity < 0) - obj_integrity = 0 - if(obj_integrity == 0) - if(ismob(loc)) - var/mob/living/L = loc - playsound(src, /datum/soundbyte/grouped/sparks, 100, TRUE) - L.visible_message("[src] overloads from the damage sustained!") - L.dropItemToGround(src) //implant component catch hook will grab it. - -/obj/item/shield/riot/implant/Moved() - . = ..() - if(istype(loc, /obj/item/organ/cyberimp/arm/shield)) - recharge_timerid = addtimer(CALLBACK(src, PROC_REF(recharge)), recharge_delay, flags = TIMER_STOPPABLE) - else //extending - if(recharge_timerid) - deltimer(recharge_timerid) - recharge_timerid = null - -/obj/item/shield/riot/implant/proc/recharge() - if(obj_integrity == integrity_max) - return - obj_integrity = integrity_max - if(ismob(loc.loc)) //cyberimplant.user - to_chat(loc, "[src] has recharged its reinforcement matrix and is ready for use!") -*/ - -/obj/item/shield/riot/energy_proof - name = "energy resistant shield" - desc = "An ablative shield designed to absorb and disperse energy attacks. This comes at significant cost to its ability to withstand ballistics and kinetics, breaking apart easily." - icon_state = "riot_laser" - -/obj/item/shield/riot/kinetic_proof - name = "kinetic resistant shield" - desc = "A polymer and ceramic shield designed to absorb ballistic projectiles and kinetic force. It doesn't do very well into energy attacks, especially from weapons that inflict burns." - icon_state = "riot_bullet" - -//Exotics/Costume Shields -/obj/item/shield/riot/roman - name = "scutum" - desc = "A replica shield for close quarters engagement. Its modern materials are also capable of protecting from less powerful projectiles." - icon = 'icons/obj/weapons.dmi' - icon_state = "roman_shield" - slot_flags = SLOT_BACK - materials_base = list(MAT_WOOD = 7500, MAT_STEEL = 1000) - item_icons = list( - SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', - SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi', - ) - -/obj/item/shield/riot/buckler - name = "buckler" - desc = "A wrist mounted round shield for close quarters engagement. Its modern materials are also capable of protecting from less powerful projectiles." - icon = 'icons/obj/weapons.dmi' - icon_state = "buckler" - slot_flags = SLOT_BACK | SLOT_BELT - materials_base = list(MAT_WOOD = 7500, MAT_STEEL = 1000) - item_icons = list( - SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', - SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi', - ) - -/* - * Energy Shield - */ - -/obj/item/shield/energy - name = "energy combat shield" - desc = "A shield capable of stopping most projectile and melee attacks. It can be retracted, expanded, and stored anywhere." - icon = 'icons/obj/weapons.dmi' - icon_state = "eshield" - item_state = "eshield" - slot_flags = SLOT_EARS - atom_flags = NOCONDUCT - damage_force = 3.0 - throw_force = 5.0 - throw_speed = 1 - throw_range = 4 - w_class = WEIGHT_CLASS_SMALL - var/lrange = 1.5 - var/lpower = 1.5 - var/lcolor = "#006AFF" - origin_tech = list(TECH_MATERIAL = 4, TECH_MAGNET = 3, TECH_ILLEGAL = 4) - attack_verb = list("shoved", "bashed") - var/active = 0 - item_icons = list( - SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', - SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi', - ) - worth_intrinsic = 500 // op as balls - -/obj/item/shield/energy/handle_shield(mob/user) - if(!active) - return 0 //turn it on first! - . = ..() - - if(.) - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) - -/obj/item/shield/energy/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null) - if(istype(damage_source, /obj/projectile)) - var/obj/projectile/P = damage_source - if((is_sharp(P) && damage > 10) || istype(P, /obj/projectile/beam)) - return (base_block_chance - round(damage / 3)) //block bullets and beams using the old block chance - return base_block_chance - -/obj/item/shield/energy/attack_self(mob/user) - . = ..() - if(.) - return - if ((MUTATION_CLUMSY in user.mutations) && prob(50)) - to_chat(user, "You beat yourself in the head with [src].") - var/mob/living/carbon/human/H = ishuman(user)? user : null - H?.take_random_targeted_damage(brute = 5) - active = !active - if (active) - damage_force = 10 - update_icon() - set_weight_class(WEIGHT_CLASS_BULKY) - slot_flags = null - playsound(user, 'sound/weapons/saberon.ogg', 50, 1) - to_chat(user, "\The [src] is now active.") - - else - damage_force = 3 - update_icon() - set_weight_class(WEIGHT_CLASS_TINY) - slot_flags = SLOT_EARS - playsound(user, 'sound/weapons/saberoff.ogg', 50, 1) - to_chat(user, "\The [src] can now be concealed.") - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - - add_fingerprint(user) - return - -/obj/item/shield/energy/update_icon() - var/mutable_appearance/blade_overlay = mutable_appearance(icon, "[icon_state]_blade") - if(lcolor) - blade_overlay.color = lcolor - cut_overlays() //So that it doesn't keep stacking overlays non-stop on top of each other - if(active) - add_overlay(blade_overlay) - item_state = "[icon_state]_blade" - set_light(lrange, lpower, lcolor) - else - set_light(0) - item_state = "[icon_state]" - -/obj/item/shield/energy/AltClick(mob/living/user) - if(!in_range(src, user)) //Basic checks to prevent abuse - return - if(user.incapacitated() || !istype(user)) - to_chat(user, "You can't do that right now!") - return - if(alert("Are you sure you want to recolor your shield?", "Confirm Recolor", "Yes", "No") == "Yes") - var/energy_color_input = input(usr,"","Choose Energy Color",lcolor) as color|null - if(energy_color_input) - lcolor = sanitize_hexcolor(energy_color_input, desired_format=6, include_crunch=1) - update_icon() - -/obj/item/shield/energy/examine(mob/user, dist) - . = ..() - . += "Alt-click to recolor it." - -/obj/item/shield/riot/tele - name = "telescopic shield" - desc = "An advanced riot shield made of lightweight materials that collapses for easy storage." - icon = 'icons/obj/weapons.dmi' - icon_state = "teleriot0" - slot_flags = null - damage_force = 3 - throw_force = 3 - throw_speed = 3 - throw_range = 4 - w_class = WEIGHT_CLASS_NORMAL - var/active = 0 -/* -/obj/item/shield/energy/IsShield() - if(active) - return 1 - else - return 0 -*/ -/obj/item/shield/riot/tele/attack_self(mob/user) - . = ..() - if(.) - return - active = !active - icon_state = "teleriot[active]" - playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1) - - if(active) - damage_force = 8 - throw_force = 5 - throw_speed = 2 - set_weight_class(WEIGHT_CLASS_BULKY) - slot_flags = SLOT_BACK - to_chat(user, "You extend \the [src].") - else - damage_force = 3 - throw_force = 3 - throw_speed = 3 - set_weight_class(WEIGHT_CLASS_NORMAL) - slot_flags = null - to_chat(user, "[src] can now be concealed.") - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - - add_fingerprint(user) - return - -/obj/item/shield/energy/imperial - name = "energy scutum" - desc = "It's really easy to mispronounce the name of this shield if you've only read it in books." - icon = 'icons/obj/weapons_vr.dmi' - icon_state = "eshield0" // eshield1 for expanded - item_icons = list(SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi') - -/obj/item/shield/fluff/wolfgirlshield - name = "Autumn Shield" - desc = "A shiny silvery shield with a large red leaf symbol in the center." - icon = 'icons/obj/weapons_vr.dmi' - icon_state = "wolfgirlshield" - slot_flags = SLOT_BACK | SLOT_OCLOTHING - damage_force = 5.0 - throw_force = 5.0 - throw_speed = 2 - throw_range = 6 - item_icons = list(SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi', SLOT_ID_BACK = 'icons/vore/custom_items_vr.dmi', SLOT_ID_SUIT = 'icons/vore/custom_items_vr.dmi') - attack_verb = list("shoved", "bashed") - var/cooldown = 0 //shield bash cooldown. based on world.time - allowed = list(/obj/item/melee/fluffstuff/wolfgirlsword) - -/obj/item/shield/fluff/roman - name = "replica scutum" - desc = "A replica shield for close quarters engagement. It looks sturdy enough to withstand foam weapons, and nothing more." - icon = 'icons/obj/weapons.dmi' - icon_state = "roman_shield" - slot_flags = SLOT_BACK - item_icons = list( - SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', - SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi', - ) - damage_force = 5.0 - throw_force = 5.0 - throw_speed = 2 - throw_range = 6 - -//Foam Shield -/obj/item/shield/riot/foam - name = "foam riot shield" - desc = "A shield for close quarters engagement. It looks sturdy enough to withstand foam weapons, and nothing more." - icon = 'icons/obj/weapons.dmi' - icon_state = "foamriot" - slot_flags = SLOT_BACK - base_block_chance = 5 - damage_force = 0 - throw_force = 0 - throw_speed = 2 - throw_range = 6 - materials_base = list(MAT_PLASTIC = 7500, "foam" = 1000) - item_icons = list( - SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', - SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi', - ) diff --git a/code/game/objects/items/weapons/swords_axes_etc.dm b/code/game/objects/items/weapons/swords_axes_etc.dm index dd4676495514..b5c7a599daa3 100644 --- a/code/game/objects/items/weapons/swords_axes_etc.dm +++ b/code/game/objects/items/weapons/swords_axes_etc.dm @@ -38,7 +38,9 @@ icon_state = "tonfa" item_state = "tonfa" atom_flags = NOBLOODY - defend_chance = 15 + passive_parry = /datum/passive_parry{ + parry_chance_melee = 15; + } //Telescopic baton /obj/item/melee/telebaton @@ -145,12 +147,15 @@ ) item_state = "armblade" damage_force = 15 // same damage_force as a drill - defend_chance = 20 // did you know melee weapons have a default 5% chance to block frontal melee? sharp = TRUE edge = TRUE var/SA_bonus_damage = 35 // 50 total against animals and aberrations. var/SA_vulnerability = MOB_CLASS_ANIMAL | MOB_CLASS_ABERRATION + passive_parry = /datum/passive_parry{ + parry_chance_melee = 20; + } + /obj/item/melee/disruptor/afterattack(atom/target, mob/user, clickchain_flags, list/params) . = ..() if(isliving(target)) diff --git a/code/game/objects/items/weapons/tanks/tank.dm b/code/game/objects/items/weapons/tanks/tank.dm index 4a3291e1b993..24bb915b4fa1 100644 --- a/code/game/objects/items/weapons/tanks/tank.dm +++ b/code/game/objects/items/weapons/tanks/tank.dm @@ -455,7 +455,19 @@ var/list/global/tank_gauge_cache = list() var/num_fragments = round(rand(8,10) * sqrt(strength * mult)) - src.fragmentate(T, num_fragments, rand(5) + 7, list(/obj/projectile/bullet/pellet/fragment/tank/small = 7,/obj/projectile/bullet/pellet/fragment/tank = 2,/obj/projectile/bullet/pellet/fragment/strong = 1)) + shrapnel_explosion( + num_fragments, + rand(5, 7), + list( + /obj/projectile/bullet/pellet/fragment/tank/small, + /obj/projectile/bullet/pellet/fragment/tank/small, + /obj/projectile/bullet/pellet/fragment/tank/small, + /obj/projectile/bullet/pellet/fragment/tank/small, + /obj/projectile/bullet/pellet/fragment/tank, + /obj/projectile/bullet/pellet/fragment/tank, + /obj/projectile/bullet/pellet/fragment/strong = 1, + ), + ) if(istype(loc, /obj/item/transfer_valve)) var/obj/item/transfer_valve/TTV = loc @@ -482,13 +494,25 @@ var/list/global/tank_gauge_cache = list() visible_message("[icon2html(thing = src, target = world)] \The [src] flies apart!", "You hear a bang!") T.hotspot_expose(air_contents.temperature, 70, 1) - var/strength = 1+((pressure-TANK_LEAK_PRESSURE)/TANK_FRAGMENT_SCALE) var/mult = (air_contents.total_moles**2/3)/((29*0.64) **2/3) //tanks appear to be experiencing a reduction on scale of about 0.64 total moles var/num_fragments = round(rand(6,8) * sqrt(strength * mult)) //Less chunks, but bigger - src.fragmentate(T, num_fragments, 7, list(/obj/projectile/bullet/pellet/fragment/tank/small = 1,/obj/projectile/bullet/pellet/fragment/tank = 5,/obj/projectile/bullet/pellet/fragment/strong = 4)) + + shrapnel_explosion( + num_fragments, + rand(5, 7), + list( + /obj/projectile/bullet/pellet/fragment/tank/small, + /obj/projectile/bullet/pellet/fragment/tank/small, + /obj/projectile/bullet/pellet/fragment/tank/small, + /obj/projectile/bullet/pellet/fragment/tank/small, + /obj/projectile/bullet/pellet/fragment/tank, + /obj/projectile/bullet/pellet/fragment/tank, + /obj/projectile/bullet/pellet/fragment/strong = 1, + ), + ) if(istype(loc, /obj/item/transfer_valve)) var/obj/item/transfer_valve/TTV = loc diff --git a/code/game/objects/obj-construction.dm b/code/game/objects/obj-construction.dm new file mode 100644 index 000000000000..6b1ec1001e85 --- /dev/null +++ b/code/game/objects/obj-construction.dm @@ -0,0 +1,9 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +//* Deconstruction *// + +/obj/drop_products(method, atom/where) + . = ..() + if(obj_storage?.drop_on_deconstruction_methods & method) + obj_storage.drop_everything_at(where) diff --git a/code/game/objects/obj-defense.dm b/code/game/objects/obj-defense.dm new file mode 100644 index 000000000000..4bbfcff3a177 --- /dev/null +++ b/code/game/objects/obj-defense.dm @@ -0,0 +1,118 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +/obj/ex_act(power, dir, datum/automata/wave/explosion/E) + . = ..() + // todo: wave explosions + // no named arguments for speed reasons + run_damage_instance(power * (1 / 2.5) * (0.01 * rand(80, 120)), BRUTE, null, ARMOR_BOMB) + +/obj/legacy_ex_act(severity, target) + . = ..() + // todo: wave explosions + // no named arguments for speed reasons + run_damage_instance(global._legacy_ex_atom_damage[severity] * (0.01 * rand(80, 120)), BRUTE, null, ARMOR_BOMB) + +/obj/melee_act(mob/user, obj/item/weapon, target_zone, datum/event_args/actor/clickchain/clickchain) + var/shieldcall_returns = atom_shieldcall_handle_item_melee(weapon, clickchain, FALSE, NONE) + if(shieldcall_returns & SHIELDCALL_FLAGS_BLOCK_ATTACK) + return CLICKCHAIN_FULL_BLOCKED + // todo: maybe the item side should handle this? + run_damage_instance( + weapon.damage_force * (clickchain ? clickchain.damage_multiplier : 1), + weapon.damtype, + weapon.damage_tier, + weapon.damage_flag, + weapon.damage_mode, + ATTACK_TYPE_MELEE, + weapon, + NONE, + target_zone, + null, + null, + ) + return NONE + +/obj/unarmed_act(mob/attacker, datum/unarmed_attack/style, target_zone, datum/event_args/actor/clickchain/clickchain) + var/shieldcall_returns = atom_shieldcall_handle_unarmed_melee(style, clickchain, FALSE, NONE) + if(shieldcall_returns & SHIELDCALL_FLAGS_BLOCK_ATTACK) + return CLICKCHAIN_FULL_BLOCKED + // todo: maybe the unarmed_style side should handle this? + run_damage_instance( + style.get_unarmed_damage(attacker, src) * (clickchain ? clickchain.damage_multiplier : 1), + style.damage_type, + style.damage_tier, + style.damage_flag, + style.damage_mode, + ATTACK_TYPE_UNARMED, + style, + NONE, + target_zone, + null, + null, + ) + return NONE + +/obj/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + if(!(impact_flags & (PROJECTILE_IMPACT_BLOCKED | PROJECTILE_IMPACT_SKIP_STANDARD_DAMAGE))) + // todo: maybe the projectile side should handle this? + run_damage_instance( + proj.get_structure_damage() * bullet_act_args[BULLET_ACT_ARG_EFFICIENCY], + proj.damage_type, + proj.damage_tier, + proj.damage_flag, + proj.damage_mode, + ATTACK_TYPE_PROJECTILE, + proj, + NONE, + bullet_act_args[BULLET_ACT_ARG_ZONE], + null, + null, + ) + if(QDELETED(src)) + impact_flags |= PROJECTILE_IMPACT_TARGET_DELETED + return ..() + +/obj/throw_impacted(atom/movable/AM, datum/thrownthing/TT) + . = ..() + if(TT.throw_flags & THROW_AT_IS_GENTLE) + return + // todo: /atom/movable/proc/throw_impact_attack(atom/target) + if(temporary_legacy_dont_auto_handle_obj_damage_for_mechs) + return + if(isitem(AM)) + var/obj/item/I = AM + inflict_atom_damage(I.throw_force * TT.get_damage_multiplier(src), TT.get_damage_tier(src), I.damage_flag, I.damage_mode, ATTACK_TYPE_THROWN, AM) + else + inflict_atom_damage(AM.throw_force * TT.get_damage_multiplier(src), TT.get_damage_tier(src), ARMOR_MELEE, null, ATTACK_TYPE_THROWN, AM) + // if we got destroyed + if(QDELETED(src) && (obj_flags & OBJ_ALLOW_THROW_THROUGH)) + . |= COMPONENT_THROW_HIT_PIERCE + +/obj/blob_act(obj/structure/blob/blob) + . = ..() + inflict_atom_damage(100, damage_flag = ARMOR_MELEE, attack_type = ATTACK_TYPE_MELEE) + +/obj/hitsound_melee(obj/item/I) + if(!isnull(material_primary)) + var/datum/material/primary = get_primary_material() + . = I.damtype == BURN? primary.sound_melee_burn : primary.sound_melee_brute + if(!isnull(.)) + return + return ..() + +/obj/hitsound_throwhit(obj/item/I) + if(!isnull(material_primary)) + var/datum/material/primary = get_primary_material() + . = I.damtype == BURN? primary.sound_melee_burn : primary.sound_melee_brute + if(!isnull(.)) + return + return ..() + +/obj/hitsound_unarmed(mob/M, datum/unarmed_attack/style) + if(!isnull(material_primary)) + var/datum/material/primary = get_primary_material() + . = style.damage_type == BURN? primary.sound_melee_burn : primary.sound_melee_brute + if(!isnull(.)) + return + return ..() diff --git a/code/game/objects/objs.dm b/code/game/objects/obj.dm similarity index 100% rename from code/game/objects/objs.dm rename to code/game/objects/obj.dm diff --git a/code/game/objects/random/mapping.dm b/code/game/objects/random/mapping.dm index d6e34c541f6a..dfda3c019e94 100644 --- a/code/game/objects/random/mapping.dm +++ b/code/game/objects/random/mapping.dm @@ -405,11 +405,11 @@ // /obj/item/archaeological_find //), prob(1);list( - /obj/item/melee/energy/sword, - /obj/item/melee/energy/sword, - /obj/item/melee/energy/sword, - /obj/item/shield/energy, - /obj/item/shield/energy, + /obj/item/melee/transforming/energy/sword, + /obj/item/melee/transforming/energy/sword, + /obj/item/melee/transforming/energy/sword, + /obj/item/shield/transforming/energy, + /obj/item/shield/transforming/energy, /obj/structure/closet/crate/science ), prob(1);list( diff --git a/code/game/objects/random/misc.dm b/code/game/objects/random/misc.dm index 6a874a3abaed..a7ec6ec77a90 100644 --- a/code/game/objects/random/misc.dm +++ b/code/game/objects/random/misc.dm @@ -811,7 +811,7 @@ prob(8);/obj/item/gun/energy/gun/eluger, prob(8);/obj/item/gun/energy/xray, prob(8);/obj/item/gun/ballistic/automatic/c20r, - prob(8);/obj/item/melee/energy/sword, + prob(8);/obj/item/melee/transforming/energy/sword, prob(8);/obj/item/gun/ballistic/derringer, prob(8);/obj/item/gun/ballistic/konigin, prob(8);/obj/item/gun/ballistic/revolver/lemat, @@ -835,7 +835,7 @@ prob(4);/obj/item/gun/ballistic/deagle, prob(4);/obj/item/gun/ballistic/deagle/taj, prob(4);/obj/item/material/knife/tacknife/combatknife, - prob(4);/obj/item/melee/energy/sword, + prob(4);/obj/item/melee/transforming/energy/sword, prob(2);/obj/item/gun/ballistic/automatic/mini_uzi, prob(2);/obj/item/gun/ballistic/automatic/mini_uzi/taj, prob(4);/obj/item/gun/ballistic/automatic/wt274, diff --git a/code/game/objects/structures/catwalk.dm b/code/game/objects/structures/catwalk.dm index e5cfe97fa403..f34b8a2dfba4 100644 --- a/code/game/objects/structures/catwalk.dm +++ b/code/game/objects/structures/catwalk.dm @@ -194,7 +194,7 @@ if(6 to 50) inflict_atom_damage( rand(10, 20), - flag = ARMOR_MELEE, + damage_flag = ARMOR_MELEE, ) visible_message("The planks creak and groan as they're crossed.") if(51 to 100) diff --git a/code/game/objects/structures/cliff.dm b/code/game/objects/structures/cliff.dm index 8dae834f31c5..f649caacdfc3 100644 --- a/code/game/objects/structures/cliff.dm +++ b/code/game/objects/structures/cliff.dm @@ -141,13 +141,14 @@ two tiles on initialization, and which way a cliff is facing may change during m return ..() // Projectiles and objects flying 'upward' have a chance to hit the cliff instead, wasting the shot. - else if(istype(mover, /obj)) - var/obj/O = mover - if(check_shield_arc(src, dir, O)) // This is actually for mobs but it will work for our purposes as well. + else if(istype(mover, /obj/projectile) || mover.throwing) + if(get_dir(mover, src) & dir) if(prob(uphill_penalty / (1 + is_double_cliff) )) // Firing upwards facing NORTH means it will likely have to pass through two cliffs, so the chance is halved. return FALSE return TRUE + return ..() + /obj/structure/cliff/Bumped(atom/A) if(isliving(A)) var/mob/living/L = A diff --git a/code/game/objects/structures/crates_lockers/__closet.dm b/code/game/objects/structures/crates_lockers/__closet.dm index 979cae2af26c..d44188937940 100644 --- a/code/game/objects/structures/crates_lockers/__closet.dm +++ b/code/game/objects/structures/crates_lockers/__closet.dm @@ -314,7 +314,7 @@ return if(!user.attempt_insert_item_for_installation(I, opened? loc : src)) return - else if(istype(I, /obj/item/melee/energy/blade)) + else if(istype(I, /obj/item/melee/ninja_energy_blade)) if(emag_act(INFINITY, user, "The locker has been sliced open by [user] with \an [I]!", "You hear metal being sliced and sparks flying.")) var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() spark_system.set_up(5, 0, loc) diff --git a/code/game/objects/structures/crates_lockers/closets/coffin.dm b/code/game/objects/structures/crates_lockers/closets/coffin.dm index 4c1e44438b8c..fcf23cf8bdeb 100644 --- a/code/game/objects/structures/crates_lockers/closets/coffin.dm +++ b/code/game/objects/structures/crates_lockers/closets/coffin.dm @@ -32,6 +32,7 @@ opened = 1 color = "#c2b29f" use_old_icon_update = TRUE + obj_flags = OBJ_MELEE_TARGETABLE /obj/structure/closet/grave/attack_hand(mob/user, list/params) if(opened) @@ -146,9 +147,6 @@ .=..() alpha = 255 // Needed because of grave hiding -/obj/structure/closet/grave/bullet_act(var/obj/projectile/P) - return PROJECTILE_CONTINUE // It's a hole in the ground, doesn't usually stop or even care about bullets - /obj/structure/closet/grave/return_air_for_internal_lifeform(var/mob/living/L) var/gasid = GAS_ID_CARBON_DIOXIDE if(ishuman(L)) diff --git a/code/game/objects/structures/crates_lockers/closets/gimmick.dm b/code/game/objects/structures/crates_lockers/closets/gimmick.dm index ed1c5f42e035..3f3901f4a740 100644 --- a/code/game/objects/structures/crates_lockers/closets/gimmick.dm +++ b/code/game/objects/structures/crates_lockers/closets/gimmick.dm @@ -57,7 +57,7 @@ starts_with = list( /obj/item/clothing/suit/armor/tdome/red = 3, - /obj/item/melee/energy/sword = 3, + /obj/item/melee/transforming/energy/sword = 3, /obj/item/gun/energy/laser = 3, /obj/item/melee/baton = 3, /obj/item/storage/box/flashbangs = 3, @@ -71,7 +71,7 @@ starts_with = list( /obj/item/clothing/suit/armor/tdome/green = 3, - /obj/item/melee/energy/sword = 3, + /obj/item/melee/transforming/energy/sword = 3, /obj/item/gun/energy/laser = 3, /obj/item/melee/baton = 3, /obj/item/storage/box/flashbangs = 3, diff --git a/code/game/objects/structures/crates_lockers/closets/secure/personal.dm b/code/game/objects/structures/crates_lockers/closets/secure/personal.dm index 24ea0ce03cfe..ad445635d1b4 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/personal.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/personal.dm @@ -50,7 +50,7 @@ update_icon() else to_chat(user, "Access Denied") - else if(istype(W, /obj/item/melee/energy/blade)) + else if(istype(W, /obj/item/melee/ninja_energy_blade)) if(emag_act(INFINITY, user, "The locker has been sliced open by [user] with \an [W]!", "You hear metal being sliced and sparks flying.")) var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() spark_system.set_up(5, 0, loc) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm index dfe4064a21ea..1b53f9f27386 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -97,7 +97,7 @@ /obj/item/clothing/glasses/sunglasses/sechud, /obj/item/barrier_tape_roll/police, /obj/item/shield/riot, - /obj/item/shield/riot/tele, + /obj/item/shield/transforming/telescopic, /obj/item/storage/box/holobadge/hos, /obj/item/storage/box/firingpins, /obj/item/clothing/accessory/badge/holo/hos, @@ -173,7 +173,7 @@ /obj/item/radio/headset/heads/hos/alt, /obj/item/clothing/accessory/armor/helmetcamera/security, /obj/item/clothing/accessory/armor/helmetcamera/security/body, - /obj/item/shield/riot/tele, + /obj/item/shield/transforming/telescopic, /obj/item/storage/box/holobadge/hos, /obj/item/clothing/accessory/badge/holo/hos, /obj/item/reagent_containers/spray/pepper, @@ -500,7 +500,7 @@ GLOBAL_LIST_BOILERPLATE(all_brig_closets, /obj/structure/closet/secure_closet/br /obj/item/clothing/glasses/sunglasses/sechud, /obj/item/barrier_tape_roll/police, /obj/item/shield/riot, - /obj/item/shield/riot/tele, + /obj/item/shield/transforming/telescopic, /obj/item/storage/box/holobadge/hos, /obj/item/clothing/accessory/badge/holo/hos, /obj/item/reagent_containers/spray/pepper, diff --git a/code/game/objects/structures/crates_lockers/closets/syndicate.dm b/code/game/objects/structures/crates_lockers/closets/syndicate.dm index e0b788d4508b..513aeb888ce4 100644 --- a/code/game/objects/structures/crates_lockers/closets/syndicate.dm +++ b/code/game/objects/structures/crates_lockers/closets/syndicate.dm @@ -16,7 +16,7 @@ /obj/item/cell/high, /obj/item/card/id/syndicate, /obj/item/multitool, - /obj/item/shield/energy, + /obj/item/shield/transforming/energy, /obj/item/clothing/shoes/magboots) diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index 5f5d7baf0312..04e971a25f02 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -211,7 +211,7 @@ /obj/structure/closet/crate/secure/attackby(obj/item/W as obj, mob/user as mob) if(is_type_in_list(W, list(/obj/item/packageWrap, /obj/item/stack/cable_coil, /obj/item/radio/electropack, /obj/item/tool/wirecutters))) return ..() - if(istype(W, /obj/item/melee/energy/blade)) + if(istype(W, /obj/item/melee/ninja_energy_blade)) emag_act(INFINITY, user) if(!opened) src.togglelock(user) @@ -261,17 +261,17 @@ req_access += pick(get_all_station_access()) ..() -/obj/structure/closet/crate/secure/bullet_act(var/obj/projectile/Proj) - if(!(Proj.damage_type == BRUTE || Proj.damage_type == BURN)) - return +/obj/structure/closet/crate/secure/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + if(!(proj.damage_type == BRUTE || proj.damage_type == BURN)) + return ..() - if(locked && tamper_proof && integrity <= Proj.damage) + if(locked && tamper_proof && integrity <= proj.damage) if(tamper_proof == 2) // Mainly used for events to prevent any chance of opening the box improperly. visible_message("The anti-tamper mechanism of [src] triggers an explosion!") var/turf/T = get_turf(src.loc) explosion(T, 0, 0, 0, 1) // Non-damaging, but it'll alert security. qdel(src) - return + return impact_flags var/open_chance = rand(1,5) switch(open_chance) if(1) @@ -287,12 +287,9 @@ qdel(src) if(5) visible_message("The anti-tamper mechanism of [src] fails!") - return - - ..() - - return + return impact_flags + return ..() /obj/structure/closet/crate/plastic name = "plastic crate" diff --git a/code/game/objects/structures/curtains.dm b/code/game/objects/structures/curtains.dm index 8dfecee5121e..b3f82578d8af 100644 --- a/code/game/objects/structures/curtains.dm +++ b/code/game/objects/structures/curtains.dm @@ -7,6 +7,10 @@ opacity = 1 density = 0 anchored = TRUE + integrity = 40 + integrity_failure = 30 + integrity_max = 40 + var/obj/item/stack/mat = /obj/item/stack/material/plastic /obj/structure/curtain/open @@ -15,13 +19,6 @@ layer = 3.3 //3.3 so its above windows, not the same as them. anything below 3.3 puts the curtain beneath the window sprite in current build opacity = 0 -/obj/structure/curtain/bullet_act(obj/projectile/P, def_zone) - if(!P.nodamage) - visible_message("[P] tears [src] down!") - qdel(src) - else - ..(P, def_zone) - /obj/structure/curtain/attack_hand(mob/user, list/params) playsound(get_turf(loc), "rustle", 15, 1, -5) toggle() diff --git a/code/game/objects/structures/decorations.dm b/code/game/objects/structures/decorations.dm index f7c5260e74e9..bcbf68b08f0f 100644 --- a/code/game/objects/structures/decorations.dm +++ b/code/game/objects/structures/decorations.dm @@ -3,15 +3,10 @@ desc = "Ornately twisted rope holding up a religious seal." icon = 'icons/obj/decals.dmi' icon_state = "shrine_seal" - layer = 3.3 //3.3 so its above windows, not the same as them. anything below 3.3 puts the curtain beneath the window sprite in current build - opacity = 0 - -/obj/structure/shrine_seal/bullet_act(obj/projectile/P, def_zone) - if(!P.nodamage) - visible_message("[P] tears [src] down!") - qdel(src) - else - ..(P, def_zone) + layer = ABOVE_WINDOW_LAYER + integrity = 40 + integrity_max = 40 + integrity_failure = 30 /obj/structure/shrine_seal/attackby(obj/item/P, mob/user) if(P.is_wirecutter()) diff --git a/code/game/objects/structures/door_assembly.dm b/code/game/objects/structures/door_assembly.dm index 147f5d25ff76..87795c2998e8 100644 --- a/code/game/objects/structures/door_assembly.dm +++ b/code/game/objects/structures/door_assembly.dm @@ -324,7 +324,7 @@ // Airlock frames are indestructable, so bullets hitting them would always be stopped. // To fix this, airlock assemblies will sometimes let bullets pass through, since generally the sprite shows them partially open. -/obj/structure/door_assembly/bullet_act(var/obj/projectile/P) - if(prob(40)) // Chance for the frame to let the bullet keep going. - return PROJECTILE_CONTINUE +/obj/structure/door_assembly/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + if(prob(40)) + return PROJECTILE_IMPACT_PASSTHROUGH return ..() diff --git a/code/game/objects/structures/flora/rocks.dm b/code/game/objects/structures/flora/rocks.dm index 74528dfd6a46..63ec2bbd4ed1 100644 --- a/code/game/objects/structures/flora/rocks.dm +++ b/code/game/objects/structures/flora/rocks.dm @@ -38,11 +38,12 @@ return . = ..() -/obj/structure/flora/rock/bullet_act(obj/projectile/P, def_zone) - if(P.damage_flag == ARMOR_BOMB) //Intended for kinetic accelerators/daggers to just get rid of this stuff quickly. They're rocks. - GetDrilled() - return +/obj/structure/flora/rock/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_TARGET_ABORT) + return + if(proj.damage_flag == ARMOR_BOMB) //Intended for kinetic accelerators/daggers to just get rid of this stuff quickly. They're rocks. + GetDrilled() /obj/structure/flora/rock/proc/GetDrilled() new outcropdrop(get_turf(src),rand(mindrop,upperdrop)) diff --git a/code/game/objects/structures/flora/trees.dm b/code/game/objects/structures/flora/trees.dm index 3b6f73af5e2b..75898d9168ce 100644 --- a/code/game/objects/structures/flora/trees.dm +++ b/code/game/objects/structures/flora/trees.dm @@ -65,10 +65,10 @@ animate(src, transform=turn(M, shake_animation_degrees * shake_dir), pixel_x=init_px + 2*shake_dir, time=1) animate(transform=M, pixel_x=init_px, time=6, easing=ELASTIC_EASING) -/obj/structure/flora/tree/inflict_atom_damage(damage, tier, flag, mode, attack_type, datum/weapon, gradual) +/obj/structure/flora/tree/inflict_atom_damage(damage, damage_type, damage_tier, damage_flag, damage_mode, hit_zone, attack_type, datum/weapon) . = ..() // ruins some of the wood if you use high power modes or types - if(. > 5 && ((mode & (DAMAGE_MODE_ABLATING | DAMAGE_MODE_PIERCE | DAMAGE_MODE_SHRED)) || (flag == ARMOR_BOMB))) + if(. > 5 && ((damage_mode & (DAMAGE_MODE_ABLATING | DAMAGE_MODE_PIERCE | DAMAGE_MODE_SHRED)) || (damage_flag == ARMOR_BOMB))) product_amount -= round((. * 0.5) / integrity_max * initial(product_amount)) /obj/structure/flora/tree/atom_break() diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm index 4d97e12a8f6a..58a1913f12f9 100644 --- a/code/game/objects/structures/grille.dm +++ b/code/game/objects/structures/grille.dm @@ -43,36 +43,14 @@ return TRUE return ..() -/obj/structure/grille/bullet_act(var/obj/projectile/Proj) - //Flimsy grilles aren't so great at stopping projectiles. However they can absorb some of the impact - var/damage = Proj.get_structure_damage() - var/passthrough = 0 - - if(!damage) return - - //20% chance that the grille provides a bit more cover than usual. Support structure for example might take up 20% of the grille's area. - //If they click on the grille itself then we assume they are aiming at the grille itself and the extra cover behaviour is always used. - switch(Proj.damage_type) - if(BRUTE) - //bullets - if(Proj.original == src || prob(20)) - Proj.damage *= clamp( Proj.damage/60, 0, 0.5) - if(prob(max((damage-10)/25, 0))*100) - passthrough = 1 - else - Proj.damage *= clamp( Proj.damage/60, 0, 1) - passthrough = 1 - if(BURN) - //beams and other projectiles are either blocked completely by grilles or stop half the damage. - if(!(Proj.original == src || prob(20))) - Proj.damage *= 0.5 - passthrough = 1 - - if(passthrough) - . = PROJECTILE_CONTINUE - damage = between(0, (damage - Proj.damage)*(Proj.damage_type == BRUTE? 0.4 : 1), 10) //if the bullet passes through then the grille avoids most of the damage - - inflict_atom_damage(damage, Proj.damage_tier, Proj.damage_flag, Proj.damage_mode, ATTACK_TYPE_PROJECTILE, Proj) +/obj/structure/grille/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + if(proj.original_target == src) + impact_flags |= PROJECTILE_IMPACT_TRIVIAL + else + impact_flags |= PROJECTILE_IMPACT_TRIVIAL | PROJECTILE_IMPACT_PIERCE + . = ..() + if(impact_flags & PROJECTILE_IMPACT_PIERCE) + proj.dampen_on_pierce_experimental(src, 10, ARMOR_TIER_ABOVE) /obj/structure/grille/attackby(obj/item/W as obj, mob/user as mob) if(!istype(W)) @@ -120,12 +98,12 @@ WD.update_appearance() return ..() -/obj/structure/grille/unarmed_act(mob/attacker, datum/unarmed_attack/style, target_zone, mult) +/obj/structure/grille/unarmed_act(mob/attacker, datum/unarmed_attack/style, target_zone, datum/event_args/actor/clickchain/clickchain) if(shock(attacker, 70)) return FALSE return ..() -/obj/structure/grille/melee_act(mob/user, obj/item/weapon, target_zone, mult) +/obj/structure/grille/melee_act(mob/user, obj/item/weapon, target_zone, datum/event_args/actor/clickchain/clickchain) if(shock(user, 70, weapon)) return FALSE return ..() @@ -174,7 +152,7 @@ /obj/structure/grille/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) if(!destroyed) if(exposed_temperature > T0C + 1500) - inflict_atom_damage(1, flag = ARMOR_FIRE) + inflict_atom_damage(1, damage_flag = ARMOR_FIRE) ..() /obj/structure/grille/proc/is_on_frame() diff --git a/code/game/objects/structures/janicart.dm b/code/game/objects/structures/janicart.dm index a231a24ab6db..cd4858dd972a 100644 --- a/code/game/objects/structures/janicart.dm +++ b/code/game/objects/structures/janicart.dm @@ -177,6 +177,7 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) anchored = 1 density = 1 atom_flags = OPENCONTAINER + integrity_flags = INTEGRITY_INDESTRUCTIBLE //copypaste sorry var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite var/obj/item/storage/bag/trash/mybag = null @@ -279,13 +280,10 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) L.pixel_x = -13 L.pixel_y = 7 -/obj/structure/bed/chair/janicart/bullet_act(var/obj/projectile/Proj) - if(has_buckled_mobs()) - if(prob(85)) - var/mob/living/L = pick(buckled_mobs) - return L.bullet_act(Proj) - visible_message("[Proj] ricochets off the [callme]!") - +/obj/structure/bed/chair/janicart/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + if(has_buckled_mobs() && prob(85)) + return proj.impact_redirect(pick(buckled_mobs), args) + return ..() /obj/item/key name = "key" diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm index df8f872b1056..9e12dc21cec7 100644 --- a/code/game/objects/structures/mirror.dm +++ b/code/game/objects/structures/mirror.dm @@ -39,14 +39,13 @@ desc = "Oh no, seven years of bad luck!" -/obj/structure/mirror/bullet_act(var/obj/projectile/Proj) - - if(prob(Proj.get_structure_damage() * 2)) +/obj/structure/mirror/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + if(prob(proj.get_structure_damage() * 2)) if(!shattered) shatter() else if(glass) playsound(src, 'sound/effects/hit_on_shattered_glass.ogg', 70, 1) - ..() /obj/structure/mirror/attackby(obj/item/I as obj, mob/user as mob) if(I.is_wrench()) diff --git a/code/game/objects/structures/props/beam_prism.dm b/code/game/objects/structures/props/beam_prism.dm index fad0ecd0328f..8a2d8b095fde 100644 --- a/code/game/objects/structures/props/beam_prism.dm +++ b/code/game/objects/structures/props/beam_prism.dm @@ -1,5 +1,6 @@ //A series(?) of prisms for PoIs. The base one only works for beams. +// todo: refactor to /obj/structure/reflector /obj/structure/prop/prism name = "prismatic turret" desc = "A raised, externally powered 'turret'. It seems to have a massive crystal ring around its base." @@ -12,6 +13,12 @@ layer = 3.1 //Layer over projectiles. plane = -10 //Layer over projectiles. + /// projectile_type's to reflect + /// all of these must be on a projectile + var/projectile_type = PROJECTILE_TYPE_BEAM | PROJECTILE_TYPE_PHOTONIC + /// can't reflect these + var/projectile_type_cant = NONE + var/rotation_lock = 0 // Can you rotate the prism at all? var/free_rotate = 1 // Does the prism rotate in any direction, or only in the eight standard compass directions? var/external_control_lock = 0 // Does the prism only rotate from the controls of an external switch? @@ -19,8 +26,6 @@ var/compass_directions = list("North" = 0, "South" = 180, "East" = 90, "West" = 270, "Northwest" = 315, "Northeast" = 45, "Southeast" = 135, "Southwest" = 225) var/interaction_sound = 'sound/mecha/mechmove04.ogg' - var/redirect_type = /obj/projectile/beam - var/dialID = null var/obj/structure/prop/prismcontrol/remote_dial = null @@ -119,19 +124,17 @@ else animate(src, transform = turn(src.transform, rotate_degrees), time = 6) -/obj/structure/prop/prism/bullet_act(var/obj/projectile/Proj) - if(istype(Proj, redirect_type)) - if(!silent) - visible_message("\The [src] redirects \the [Proj]!") - flick("[initial(icon_state)]+glow", src) - - var/new_x = (1 * round(10 * cos(degrees_from_north - 90))) + x //Vectors vectors vectors. - var/new_y = (-1 * round(10 * sin(degrees_from_north - 90))) + y - var/turf/curloc = get_turf(src) - - Proj.penetrating += 1 // Needed for the beam to get out of the turret. - - Proj.redirect(new_x, new_y, curloc, null) +/obj/structure/prop/prism/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + if(((proj.projectile_type & projectile_type) != projectile_type) || (proj.projectile_type & projectile_type_cant)) + return ..() + if(!silent) + visible_message("\The [src] redirects \the [proj]!") + flick("[initial(icon_state)]+glow", src) + + // todo: this is not the right way; visuals are weird and the raycast is :/ + proj.physics_kick_forwards(16) + proj.set_angle(degrees_from_north) + return PROJECTILE_IMPACT_REFLECT /obj/structure/prop/prism/incremental free_rotate = 0 diff --git a/code/game/objects/structures/props/projectile_lock.dm b/code/game/objects/structures/props/projectile_lock.dm index 1bd5eaad95da..110d54c74a58 100644 --- a/code/game/objects/structures/props/projectile_lock.dm +++ b/code/game/objects/structures/props/projectile_lock.dm @@ -28,9 +28,11 @@ else icon_state = "[initial(icon_state)]" +// todo: this is shitcode rework this /obj/structure/prop/lock/projectile name = "beam lock" desc = "An esoteric object that responds to high intensity light." + integrity_flags = INTEGRITY_INDESTRUCTIBLE var/projectile_key = /obj/projectile/beam var/timed = 0 @@ -39,11 +41,14 @@ interaction_message = "The object remains inert to your touch." -/obj/structure/prop/lock/projectile/bullet_act(var/obj/projectile/Proj) - if(!istype(Proj, projectile_key) || timing) - return +/obj/structure/prop/lock/projectile/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + if(!istype(proj, projectile_key)) + return ..() - if(istype(Proj, /obj/projectile/beam/heavylaser/cannon) || istype(Proj, /obj/projectile/beam/emitter) || (Proj.damage >= 80 && Proj.damtype == BURN)) + if(timing) + return PROJECTILE_IMPACT_DELETE + + if(istype(proj, /obj/projectile/beam/heavylaser/cannon) || istype(proj, /obj/projectile/beam/emitter) || (proj.damage >= 80 && proj.damtype == BURN)) toggle_lock() visible_message("\The [src] [enabled ? "disengages" : "engages"] its locking mechanism.") @@ -51,3 +56,5 @@ timing = 1 spawn(time_limit) toggle_lock() + + return PROJECTILE_IMPACT_DELETE diff --git a/code/game/objects/structures/props/puzzledoor.dm b/code/game/objects/structures/props/puzzledoor.dm index 42416e90c3f2..8f429ac12f7b 100644 --- a/code/game/objects/structures/props/puzzledoor.dm +++ b/code/game/objects/structures/props/puzzledoor.dm @@ -26,10 +26,9 @@ return 0 return 1 -/obj/machinery/door/blast/puzzle/bullet_act(var/obj/projectile/Proj) - if(!istype(Proj, /obj/projectile/test)) - visible_message("\The [src] is completely unaffected by \the [Proj].") - qdel(Proj) //No piercing. No. +/obj/machinery/door/blast/puzzle/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + impact_flags &= ~PROJECTILE_IMPACT_FLAGS_SHOULD_GO_THROUGH + return ..() /obj/machinery/door/blast/puzzle/legacy_ex_act(severity) visible_message("\The [src] is completely unaffected by the blast.") diff --git a/code/game/objects/structures/tables/interactions.dm b/code/game/objects/structures/tables/interactions.dm index 4f20ef5225db..6d48e296228e 100644 --- a/code/game/objects/structures/tables/interactions.dm +++ b/code/game/objects/structures/tables/interactions.dm @@ -33,10 +33,10 @@ return 1 if (get_dist(P.starting, loc) <= 1) //Tables won't help you if people are THIS close return 1 - if (get_turf(P.original) == cover) + if (get_turf(P.original_target) == cover) var/chance = 20 - if (ismob(P.original)) - var/mob/M = P.original + if (ismob(P.original_target)) + var/mob/M = P.original_target if (M.lying) chance += 20 //Lying down lets you catch less bullets if(flipped==1) @@ -71,7 +71,7 @@ else playsound(loc, 'sound/weapons/tablehit1.ogg', 50, 1) var/turf/old_loc = loc - inflict_atom_damage(40, flag = ARMOR_MELEE) + inflict_atom_damage(40, damage_flag = ARMOR_MELEE) if(QDELETED(src)) // got broken visible_message(SPAN_DANGER("[src] shatters under the impact!")) diff --git a/code/game/objects/structures/target_stake.dm b/code/game/objects/structures/target_stake.dm index ab9122056f71..136ba2bc219c 100644 --- a/code/game/objects/structures/target_stake.dm +++ b/code/game/objects/structures/target_stake.dm @@ -47,8 +47,7 @@ else return ..() -/obj/structure/target_stake/bullet_act(obj/projectile/P, def_zone) +/obj/structure/target_stake/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(pinned_target) - return pinned_target.bullet_act(P, def_zone) - else - return ..() + return proj.impact_redirect(pinned_target, args) + return ..() diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index b1f77bcbba06..4f5081b13d04 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -291,7 +291,7 @@ /obj/structure/window/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) . = ..() if (exposed_temperature > maximal_heat) - inflict_atom_damage(damage_per_fire_tick, flag = ARMOR_FIRE, gradual = TRUE) + inflict_atom_damage(damage_per_fire_tick, damage_flag = ARMOR_FIRE, damage_mode = DAMAGE_MODE_GRADUAL) /obj/structure/window/drop_products(method, atom/where) . = ..() diff --git a/code/game/turfs/defense.dm b/code/game/turfs/defense.dm deleted file mode 100644 index 34a90ac239ac..000000000000 --- a/code/game/turfs/defense.dm +++ /dev/null @@ -1,2 +0,0 @@ -/turf/break_apart(method) - ScrapeAway(1, CHANGETURF_INHERIT_AIR) diff --git a/code/game/turfs/simulated/wall/defense.dm b/code/game/turfs/simulated/wall/defense.dm deleted file mode 100644 index 8811811f9492..000000000000 --- a/code/game/turfs/simulated/wall/defense.dm +++ /dev/null @@ -1,48 +0,0 @@ -/turf/simulated/wall/throw_impacted(atom/movable/AM, datum/thrownthing/TT) - . = ..() - if(TT.throw_flags & THROW_AT_IS_GENTLE) - return - - // todo: /atom/movable/proc/throw_impact_attack(atom/target) - if(isitem(AM)) - var/obj/item/I = AM - inflict_atom_damage(I.throw_force * TT.get_damage_multiplier(src), I.damage_tier, I.damage_flag, I.damage_mode, ATTACK_TYPE_THROWN, AM) - else - inflict_atom_damage(AM.throw_force * TT.get_damage_multiplier(src), MELEE_TIER_LIGHT, ARMOR_MELEE, null, ATTACK_TYPE_THROWN, AM) - -/turf/simulated/wall/unarmed_act(mob/attacker, datum/unarmed_attack/style, target_zone, mult) - // todo: this should just be style.attack(attacker, src) - inflict_atom_damage(style.get_unarmed_damage(attacker, src), style.damage_tier, style.damage_flag, style.damage_mode, ATTACK_TYPE_UNARMED, attacker) - return NONE - -/turf/simulated/wall/melee_act(mob/user, obj/item/weapon, target_zone, mult) - inflict_atom_damage(weapon.damage_force, weapon.damage_tier, weapon.damage_flag, weapon.damage_mode, ATTACK_TYPE_MELEE, weapon) - return NONE - -/turf/simulated/wall/bullet_act(var/obj/projectile/Proj) - if(istype(Proj,/obj/projectile/beam)) - burn(2500) - else if(istype(Proj,/obj/projectile/ion)) - burn(500) - - if(Proj.damage_type == BURN && Proj.damage > 0) - if(thermite) - thermitemelt() - - if(Proj.ricochet_sounds && prob(15)) - playsound(src, pick(Proj.ricochet_sounds), 100, 1) - - inflict_atom_damage(Proj.get_structure_damage(), Proj.damage_tier, Proj.damage_flag, Proj.damage_mode, ATTACK_TYPE_PROJECTILE, Proj) - -/turf/simulated/wall/break_apart(method) - dismantle_wall() - -/turf/simulated/wall/damage_integrity(amount, gradual, do_not_break) - . = ..() - // todo: optimize - update_appearance() - -/turf/simulated/wall/heal_integrity(amount, gradual, do_not_fix) - . = ..() - // todo: optimize - update_appearance() diff --git a/code/game/turfs/simulated/wall/wall-construction.dm b/code/game/turfs/simulated/wall/wall-construction.dm new file mode 100644 index 000000000000..7633c8afaafb --- /dev/null +++ b/code/game/turfs/simulated/wall/wall-construction.dm @@ -0,0 +1,7 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +//* Deconstruction *// + +/turf/simulated/wall/break_apart(method) + dismantle_wall() // this handles deletion too diff --git a/code/game/turfs/simulated/wall/wall-damage.dm b/code/game/turfs/simulated/wall/wall-damage.dm new file mode 100644 index 000000000000..e46661a6902b --- /dev/null +++ b/code/game/turfs/simulated/wall/wall-damage.dm @@ -0,0 +1,14 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +//* Integrity - Direct Manipulation *// + +/turf/simulated/wall/damage_integrity(amount, gradual, do_not_break) + . = ..() + // todo: optimize + update_appearance() + +/turf/simulated/wall/heal_integrity(amount, gradual, do_not_fix) + . = ..() + // todo: optimize + update_appearance() diff --git a/code/game/turfs/simulated/wall/wall-defense.dm b/code/game/turfs/simulated/wall/wall-defense.dm new file mode 100644 index 000000000000..eeed876c8ccc --- /dev/null +++ b/code/game/turfs/simulated/wall/wall-defense.dm @@ -0,0 +1,97 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +/turf/simulated/wall/throw_impacted(atom/movable/AM, datum/thrownthing/TT) + . = ..() + if(TT.throw_flags & THROW_AT_IS_GENTLE) + return + // todo: this method of detecting destruction is shitcode but turf refs don't change so qdeleted() won't work + var/old_type = type + // todo: /atom/movable/proc/throw_impact_attack(atom/target) + if(isitem(AM)) + var/obj/item/I = AM + inflict_atom_damage(I.throw_force * TT.get_damage_multiplier(src), I.damage_tier, I.damage_flag, I.damage_mode, ATTACK_TYPE_THROWN, AM) + else + inflict_atom_damage(AM.throw_force * TT.get_damage_multiplier(src), MELEE_TIER_LIGHT, ARMOR_MELEE, null, ATTACK_TYPE_THROWN, AM) + // turf refs don't change so while QDELETED() doesn't work this is a close approximate + // until we have a better system or we decide to pay some overhead to track with a number or something + if(old_type != type) + if(!density) + . |= COMPONENT_THROW_HIT_PIERCE // :trol: + return + +/turf/simulated/wall/unarmed_act(mob/attacker, datum/unarmed_attack/style, target_zone, datum/event_args/actor/clickchain/clickchain) + var/shieldcall_returns = atom_shieldcall_handle_unarmed_melee(style, clickchain, FALSE, NONE) + if(shieldcall_returns & SHIELDCALL_FLAGS_BLOCK_ATTACK) + return CLICKCHAIN_FULL_BLOCKED + // todo: maybe the unarmed_style side should handle this? + run_damage_instance( + style.damage * (clickchain ? clickchain.damage_multiplier : 1), + style.damage_type, + style.damage_tier, + style.damage_flag, + style.damage_mode, + ATTACK_TYPE_UNARMED, + style, + NONE, + target_zone, + null, + null, + ) + return NONE + +/turf/simulated/wall/melee_act(mob/user, obj/item/weapon, target_zone, datum/event_args/actor/clickchain/clickchain) + var/shieldcall_returns = atom_shieldcall_handle_item_melee(weapon, clickchain, FALSE, NONE) + if(shieldcall_returns & SHIELDCALL_FLAGS_BLOCK_ATTACK) + return CLICKCHAIN_FULL_BLOCKED + // todo: maybe the item side should handle this? + run_damage_instance( + weapon.damage_force * (clickchain ? clickchain.damage_multiplier : 1), + weapon.damtype, + weapon.damage_tier, + weapon.damage_flag, + weapon.damage_mode, + ATTACK_TYPE_MELEE, + weapon, + NONE, + target_zone, + null, + null, + ) + return NONE + +/turf/simulated/wall/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + // todo: this method of detecting destruction is shitcode but turf refs don't change so qdeleted() won't work + var/old_type = type + if(!(impact_flags & (PROJECTILE_IMPACT_BLOCKED | PROJECTILE_IMPACT_SKIP_STANDARD_DAMAGE))) + // todo: maybe the projectile side should handle this? + run_damage_instance( + proj.get_structure_damage() * bullet_act_args[BULLET_ACT_ARG_EFFICIENCY], + proj.damage_type, + proj.damage_tier, + proj.damage_flag, + proj.damage_mode, + ATTACK_TYPE_PROJECTILE, + proj, + NONE, + bullet_act_args[BULLET_ACT_ARG_ZONE], + null, + null, + ) + // turf refs don't change so while QDELETED() doesn't work this is a close approximate + // until we have a better system or we decide to pay some overhead to track with a number or something + if(old_type != type) + impact_flags |= PROJECTILE_IMPACT_TARGET_DELETED + return ..() + + //! legacy code handling + if((proj.projectile_type & (PROJECTILE_TYPE_ENERGY | PROJECTILE_TYPE_BEAM)) && !proj.nodamage && proj.damage) + burn(2500) + if(proj.damage_type == BURN && proj.damage && !proj.nodamage) + if(thermite) + thermitemelt() + if(proj.ricochet_sounds && prob(15)) + playsound(src, pick(proj.ricochet_sounds), 75, TRUE) + //! end + + return ..() diff --git a/code/game/turfs/simulated/wall/wall.dm b/code/game/turfs/simulated/wall/wall.dm index c0e7a557702c..2112e988f2f5 100644 --- a/code/game/turfs/simulated/wall/wall.dm +++ b/code/game/turfs/simulated/wall/wall.dm @@ -150,7 +150,7 @@ /turf/simulated/wall/adjacent_fire_act(turf/simulated/floor/adj_turf, datum/gas_mixture/adj_air, adj_temp, adj_volume) burn(adj_temp) if(adj_temp > material_outer.melting_point) - inflict_atom_damage(log(RAND_F(0.9, 1.1) * (adj_temp - material_outer.melting_point)), flag = ARMOR_FIRE, gradual = TRUE) + inflict_atom_damage(log(RAND_F(0.9, 1.1) * (adj_temp - material_outer.melting_point)), damage_flag = ARMOR_FIRE, damage_mode = DAMAGE_MODE_GRADUAL) return ..() @@ -182,11 +182,11 @@ ScrapeAway() if(2.0) if(prob(75)) - inflict_atom_damage(rand(150, 250), flag = ARMOR_BOMB) + inflict_atom_damage(rand(150, 250), damage_flag = ARMOR_BOMB) else dismantle_wall(1,1) if(3.0) - inflict_atom_damage(rand(0, 150), flag = ARMOR_BOMB) + inflict_atom_damage(rand(0, 150), damage_flag = ARMOR_BOMB) /turf/simulated/wall/proc/can_melt() return material_outer?.material_flags & MATERIAL_FLAG_UNMELTABLE diff --git a/code/game/turfs/simulated/wall/wall_attacks.dm b/code/game/turfs/simulated/wall/wall_attacks.dm index fcd2707cdfda..9737790e6ac2 100644 --- a/code/game/turfs/simulated/wall/wall_attacks.dm +++ b/code/game/turfs/simulated/wall/wall_attacks.dm @@ -170,8 +170,8 @@ thermitemelt(user) return - else if( istype(I, /obj/item/melee/energy/blade) ) - var/obj/item/melee/energy/blade/EB = I + else if( istype(I, /obj/item/melee/ninja_energy_blade) ) + var/obj/item/melee/ninja_energy_blade/EB = I EB.spark_system.start() to_chat(user, "You slash \the [src] with \the [EB]; the thermite ignites!") @@ -222,7 +222,7 @@ dismantle_verb = "cutting" dismantle_sound = I.tool_sound // cut_delay *= 0.7 // Tools themselves now can shorten the time it takes. - else if(istype(I,/obj/item/melee/energy/blade)) + else if(istype(I,/obj/item/melee/ninja_energy_blade)) dismantle_sound = /datum/soundbyte/grouped/sparks dismantle_verb = "slicing" cut_delay *= 0.5 diff --git a/code/game/turfs/turf-construction.dm b/code/game/turfs/turf-construction.dm new file mode 100644 index 000000000000..d98289771d89 --- /dev/null +++ b/code/game/turfs/turf-construction.dm @@ -0,0 +1,7 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +//* Deconstruction *// + +/turf/break_apart(method) + ScrapeAway(1, CHANGETURF_INHERIT_AIR) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index b5b07d730e7e..1ad138f9faef 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1659,7 +1659,7 @@ if(!check_rights(R_FUN,0)) removed_paths += dirty_path continue - else if(ispath(path, /obj/item/melee/energy/blade))//Not an item one should be able to spawn./N + else if(ispath(path, /obj/item/melee/ninja_energy_blade))//Not an item one should be able to spawn./N if(!check_rights(R_FUN,0)) removed_paths += dirty_path continue diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm index b8c9b9ee87aa..62daecd72cc6 100644 --- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm +++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm @@ -1057,8 +1057,8 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null v = SSresearch if("SSprojectiles") v = SSprojectiles - if("SSfastprocess") - v = SSfastprocess + if("SSprocess_5fps") + v = SSprocess_5fps if("SSticker") v = SSticker if("SStimer") diff --git a/code/modules/admin/verbs/smite.dm b/code/modules/admin/verbs/smite.dm index 6430a74a451a..6163ca1f500b 100644 --- a/code/modules/admin/verbs/smite.dm +++ b/code/modules/admin/verbs/smite.dm @@ -130,13 +130,6 @@ to_chat(target,"You've been hit by bluespace artillery!") log_and_message_admins("[key_name(target)] has been hit by Bluespace Artillery fired by [key_name(user ? user : usr)]") - var/obj/effect/stop/S - S = new /obj/effect/stop - S.victim = target - S.loc = target.loc - spawn(20) - qdel(S) - var/turf/simulated/floor/T = get_turf(target) if(istype(T)) if(prob(80)) T.break_tile_to_plating() diff --git a/code/modules/admin/view_variables/view_variables.dm b/code/modules/admin/view_variables/view_variables.dm index b7520338a0e7..ac03d8543b71 100644 --- a/code/modules/admin/view_variables/view_variables.dm +++ b/code/modules/admin/view_variables/view_variables.dm @@ -108,7 +108,7 @@ names = sortList(names) for(var/V in names) if(D.can_vv_get(V)) - variable_html += D.vv_get_var(V) + variable_html += D.vv_get_var(V, TRUE) if(VVING_A_LIST) var/list/L = D for(var/i in 1 to L.len) diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm index 2f95cd1aadf5..48e03c8cbd8b 100644 --- a/code/modules/assembly/infrared.dm +++ b/code/modules/assembly/infrared.dm @@ -47,7 +47,7 @@ on = TRUE Rebuild() update_icon() - START_PROCESSING(SSfastprocess, src) + START_PROCESSING(SSprocess_5fps, src) /obj/item/assembly/infra/proc/turn_off() if(!on) @@ -58,7 +58,7 @@ on = FALSE Rebuild() update_icon() - STOP_PROCESSING(SSfastprocess, src) + STOP_PROCESSING(SSprocess_5fps, src) /obj/item/assembly/infra/update_icon() cut_overlays() diff --git a/code/modules/atmospherics/environmental/zas/debug.dm b/code/modules/atmospherics/environmental/zas/debug.dm index 40ea525478b0..c4c31b9f9d17 100644 --- a/code/modules/atmospherics/environmental/zas/debug.dm +++ b/code/modules/atmospherics/environmental/zas/debug.dm @@ -1,11 +1,11 @@ -var/image/assigned = image('icons/testing/Zone.dmi', icon_state = "assigned") -var/image/created = image('icons/testing/Zone.dmi', icon_state = "created") -var/image/merged = image('icons/testing/Zone.dmi', icon_state = "merged") -var/image/invalid_zone = image('icons/testing/Zone.dmi', icon_state = "invalid") -var/image/air_blocked = image('icons/testing/Zone.dmi', icon_state = "block") -var/image/zone_blocked = image('icons/testing/Zone.dmi', icon_state = "zoneblock") -var/image/blocked = image('icons/testing/Zone.dmi', icon_state = "fullblock") -var/image/mark = image('icons/testing/Zone.dmi', icon_state = "mark") +GLOBAL_DATUM_INIT(zas_debug_image_assigned, /image, image('icons/testing/Zone.dmi', icon_state = "assigned")) +GLOBAL_DATUM_INIT(zas_debug_image_created, /image, image('icons/testing/Zone.dmi', icon_state = "created")) +GLOBAL_DATUM_INIT(zas_debug_image_merged, /image, image('icons/testing/Zone.dmi', icon_state = "merged")) +GLOBAL_DATUM_INIT(zas_debug_image_invalid_zone, /image, image('icons/testing/Zone.dmi', icon_state = "invalid")) +GLOBAL_DATUM_INIT(zas_debug_image_air_blocked, /image, image('icons/testing/Zone.dmi', icon_state = "block")) +GLOBAL_DATUM_INIT(zas_debug_image_zone_blocked, /image, image('icons/testing/Zone.dmi', icon_state = "zoneblock")) +GLOBAL_DATUM_INIT(zas_debug_image_blocked, /image, image('icons/testing/Zone.dmi', icon_state = "fullblock")) +GLOBAL_DATUM_INIT(zas_debug_image_mark, /image, image('icons/testing/Zone.dmi', icon_state = "mark")) /datum/zas_edge/var/dbg_out = 0 diff --git a/code/modules/awaymissions/loot_vr.dm b/code/modules/awaymissions/loot_vr.dm index 504fd2d144d5..7b50df091c34 100644 --- a/code/modules/awaymissions/loot_vr.dm +++ b/code/modules/awaymissions/loot_vr.dm @@ -128,7 +128,7 @@ prob(10);/obj/item/melee/baton,\ prob(10);/obj/item/melee/telebaton,\ prob(10);/obj/item/melee/classic_baton,\ - prob(10);/obj/item/melee/energy/sword,\ + prob(10);/obj/item/melee/transforming/energy/sword,\ prob(9);/obj/item/gun/ballistic/automatic/wt550/lethal,\ prob(9);/obj/item/gun/ballistic/automatic/pdw,\ prob(9);/obj/item/gun/ballistic/derringer,\ @@ -145,7 +145,7 @@ prob(8);/obj/item/gun/energy/xray,\ prob(8);/obj/item/gun/ballistic/automatic/c20r,\ prob(8);/obj/item/gun/ballistic/automatic/stg,\ - prob(8);/obj/item/melee/energy/sword,\ + prob(8);/obj/item/melee/transforming/energy/sword,\ /* prob(8);/obj/item/gun/ballistic/automatic/m41a,\ */ prob(7);/obj/item/gun/energy/captain,\ prob(7);/obj/item/gun/energy/sniperrifle,\ diff --git a/code/modules/blob2/blobs/base_blob.dm b/code/modules/blob2/blobs/base_blob.dm index 19e8ade9f76a..6b44bb0e4828 100644 --- a/code/modules/blob2/blobs/base_blob.dm +++ b/code/modules/blob2/blobs/base_blob.dm @@ -250,18 +250,17 @@ var/list/blobs = list() adjust_integrity_blob(-damage) return -/obj/structure/blob/bullet_act(var/obj/projectile/P) - if(!P) - return +/obj/structure/blob/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() - if(istype(P.firer) && P.firer.faction == "blob") + if(istype(proj.firer) && proj.firer.faction == "blob") return - var/damage = P.get_structure_damage() // So tasers don't hurt the blob. + var/damage = proj.get_structure_damage() // So tasers don't hurt the blob. if(!damage) return - switch(P.damage_type) + switch(proj.damage_type) if(BRUTE) if(overmind) damage *= overmind.blob_type.brute_multiplier @@ -270,12 +269,10 @@ var/list/blobs = list() damage *= overmind.blob_type.burn_multiplier if(overmind) - damage = overmind.blob_type.on_received_damage(src, damage, P.damage_type, P.firer) + damage = overmind.blob_type.on_received_damage(src, damage, proj.damage_type, proj.firer) adjust_integrity_blob(-damage) - return ..() - /obj/structure/blob/water_act(amount) if(overmind) overmind.blob_type.on_water(src, amount) @@ -304,4 +301,4 @@ var/list/blobs = list() qdel(src) /turf/simulated/wall/blob_act() - inflict_atom_damage(100, flag = ARMOR_MELEE, attack_type = ATTACK_TYPE_MELEE) + inflict_atom_damage(100, damage_flag = ARMOR_MELEE, attack_type = ATTACK_TYPE_MELEE) diff --git a/code/modules/clothing/clothing_accessories.dm b/code/modules/clothing/clothing_accessories.dm index 40fc520530b5..74d5a627e049 100644 --- a/code/modules/clothing/clothing_accessories.dm +++ b/code/modules/clothing/clothing_accessories.dm @@ -288,16 +288,6 @@ A.emp_act(severity) ..() -/obj/item/clothing/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - . = ..() - if((. == 0) && LAZYLEN(accessories)) - for(var/obj/item/I in accessories) - var/check = I.handle_shield(user, damage, damage_source, attacker, def_zone, attack_text) - - if(check != 0) // Projectiles sometimes use negatives IIRC, 0 is only returned if something is not blocked. - . = check - break - /obj/item/clothing/strip_menu_options(mob/user) . = ..() if(!length(accessories)) diff --git a/code/modules/clothing/gloves/arm_guards.dm b/code/modules/clothing/gloves/arm_guards.dm index 55642593b120..319237e3d25b 100644 --- a/code/modules/clothing/gloves/arm_guards.dm +++ b/code/modules/clothing/gloves/arm_guards.dm @@ -28,14 +28,6 @@ return FALSE return TRUE -/obj/item/clothing/gloves/arm_guard/laserproof - name = "ablative arm guards" - desc = "These arm guards will protect your hands and arms from energy weapons." - icon_state = "arm_guards_laser" - item_state_slots = list(SLOT_ID_RIGHT_HAND = "swat", SLOT_ID_LEFT_HAND = "swat") - siemens_coefficient = 0.4 //This is worse than the other ablative pieces, to avoid this from becoming the poor warden's insulated gloves. - armor_type = /datum/armor/station/ablative - /obj/item/clothing/gloves/arm_guard/bulletproof name = "ballistic arm guards" desc = "These arm guards will protect your hands and arms from ballistic weapons." diff --git a/code/modules/clothing/sets/armor/ablative.dm b/code/modules/clothing/sets/armor/ablative.dm new file mode 100644 index 000000000000..f8c39360c514 --- /dev/null +++ b/code/modules/clothing/sets/armor/ablative.dm @@ -0,0 +1,64 @@ +// todo: this shouldn't be a signalled shieldcall, as +/obj/item/clothing/suit/armor/laserproof + name = "ablative armor vest" + desc = "A vest that excels in protecting the wearer against energy projectiles." + icon_state = "armor_reflec" + blood_overlay_type = "armor" + armor_type = /datum/armor/station/ablative + siemens_coefficient = 0.1 + +/obj/item/clothing/suit/armor/laserproof/equipped(mob/user, slot, flags) + . = ..() + if(slot == SLOT_ID_HANDS) + return + // if you're reading this: this is not the right way to do shieldcalls + // this is just a lazy implementation + // signals have highest priority, this as a piece of armor shouldn't have that. + RegisterSignal(user, COMSIG_ATOM_SHIELDCALL, PROC_REF(shieldcall)) + +/obj/item/clothing/suit/armor/laserproof/unequipped(mob/user, slot, flags) + . = ..() + if(slot == SLOT_ID_HANDS) + return + UnregisterSignal(user, COMSIG_ATOM_SHIELDCALL) + +/obj/item/clothing/suit/armor/laserproof/proc/shieldcall(mob/defending, list/shieldcall_args, fake_attack) + var/damage_source = shieldcall_args[SHIELDCALL_ARG_WEAPON] + var/def_zone = shieldcall_args[SHIELDCALL_ARG_HIT_ZONE] + if(istype(damage_source, /obj/projectile/energy) || istype(damage_source, /obj/projectile/beam)) + var/obj/projectile/P = damage_source + + if(P.reflected) // Can't reflect twice + return + + var/reflectchance = 50 - round(shieldcall_args[SHIELDCALL_ARG_DAMAGE]/3) + if(!(def_zone in list(BP_TORSO, BP_GROIN))) + reflectchance /= 2 + if(P.starting && prob(reflectchance)) + visible_message("\The [defending]'s [src.name] reflects [P]!") + + // Find a turf near or on the original location to bounce to + var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) + var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) + var/turf/curloc = get_turf(defending) + + // redirect the projectile + P.legacy_redirect(new_x, new_y, curloc, defending) + P.reflected = 1 + shieldcall_args[SHIELDCALL_ARG_FLAGS] |= SHIELDCALL_FLAG_ATTACK_PASSTHROUGH | SHIELDCALL_FLAG_ATTACK_REDIRECT | SHIELDCALL_FLAG_ATTACK_BLOCKED | SHIELDCALL_FLAG_TERMINATE + +/obj/item/clothing/gloves/arm_guard/laserproof + name = "ablative arm guards" + desc = "These arm guards will protect your hands and arms from energy weapons." + icon_state = "arm_guards_laser" + item_state_slots = list(SLOT_ID_RIGHT_HAND = "swat", SLOT_ID_LEFT_HAND = "swat") + siemens_coefficient = 0.4 //This is worse than the other ablative pieces, to avoid this from becoming the poor warden's insulated gloves. + armor_type = /datum/armor/station/ablative + +/obj/item/clothing/shoes/leg_guard/laserproof + name = "ablative leg guards" + desc = "These will protect your legs and feet from energy weapons." + icon_state = "leg_guards_laser" + item_state_slots = list(SLOT_ID_RIGHT_HAND = "jackboots", SLOT_ID_LEFT_HAND = "jackboots") + siemens_coefficient = 0.1 + armor_type = /datum/armor/station/ablative diff --git a/code/modules/clothing/shoes/leg_guards.dm b/code/modules/clothing/shoes/leg_guards.dm index b88fed8ce0a0..e77aa2e7e416 100644 --- a/code/modules/clothing/shoes/leg_guards.dm +++ b/code/modules/clothing/shoes/leg_guards.dm @@ -31,14 +31,6 @@ return FALSE return TRUE -/obj/item/clothing/shoes/leg_guard/laserproof - name = "ablative leg guards" - desc = "These will protect your legs and feet from energy weapons." - icon_state = "leg_guards_laser" - item_state_slots = list(SLOT_ID_RIGHT_HAND = "jackboots", SLOT_ID_LEFT_HAND = "jackboots") - siemens_coefficient = 0.1 - armor_type = /datum/armor/station/ablative - /obj/item/clothing/shoes/leg_guard/bulletproof name = "ballistic leg guards" desc = "These will protect your legs and feet from ballistic weapons." diff --git a/code/modules/clothing/spacesuits/alien.dm b/code/modules/clothing/spacesuits/alien.dm index bf61251514e8..6821a426ccec 100644 --- a/code/modules/clothing/spacesuits/alien.dm +++ b/code/modules/clothing/spacesuits/alien.dm @@ -33,7 +33,7 @@ w_class = WEIGHT_CLASS_NORMAL atom_flags = PHORONGUARD clothing_flags = CLOTHING_THICK_MATERIAL | CLOTHING_INJECTION_PORT - allowed = list(/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword,/obj/item/handcuffs,/obj/item/tank) + allowed = list(/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/transforming/energy/sword,/obj/item/handcuffs,/obj/item/tank) armor_type = /datum/armor/vox/space/armored siemens_coefficient = 0.2 heat_protection_cover = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS diff --git a/code/modules/clothing/spacesuits/syndi.dm b/code/modules/clothing/spacesuits/syndi.dm index 51f0fdf74159..313e9fab76c8 100644 --- a/code/modules/clothing/spacesuits/syndi.dm +++ b/code/modules/clothing/spacesuits/syndi.dm @@ -11,7 +11,7 @@ icon_state = "syndicate" desc = "A crimson spacesuit sporting clean lines and durable plating. Robust, reliable, and slightly suspicious." w_class = WEIGHT_CLASS_NORMAL - allowed = list(/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword,/obj/item/handcuffs,/obj/item/tank/emergency/oxygen) + allowed = list(/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/transforming/energy/sword,/obj/item/handcuffs,/obj/item/tank/emergency/oxygen) armor_type = /datum/armor/agent/space siemens_coefficient = 0.6 diff --git a/code/modules/clothing/spacesuits/void/merc.dm b/code/modules/clothing/spacesuits/void/merc.dm index be3f3ab703db..b9252aa9e2f8 100644 --- a/code/modules/clothing/spacesuits/void/merc.dm +++ b/code/modules/clothing/spacesuits/void/merc.dm @@ -20,7 +20,7 @@ weight = ITEM_WEIGHT_VOIDSUIT w_class = WEIGHT_CLASS_NORMAL armor_type = /datum/armor/merc/space - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword,/obj/item/handcuffs) + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/transforming/energy/sword,/obj/item/handcuffs) siemens_coefficient = 0.6 /obj/item/clothing/head/helmet/space/void/merc/fire @@ -38,7 +38,7 @@ desc = "A blackened suit that has had many of its protective plates coated in or replaced with high-grade thermal insulation, to protect against incineration. Property of Gorlex Marauders." armor_type = /datum/armor/merc/space/ghostrider max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword,/obj/item/handcuffs,/obj/item/material/twohanded/fireaxe,/obj/item/flamethrower) + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/transforming/energy/sword,/obj/item/handcuffs,/obj/item/material/twohanded/fireaxe,/obj/item/flamethrower) siemens_coefficient = 0.7 //Soviet Void Suit @@ -91,7 +91,7 @@ item_state_slots = list(SLOT_ID_RIGHT_HAND = "syndie_voidsuit", SLOT_ID_LEFT_HAND = "syndie_voidsuit") w_class = WEIGHT_CLASS_NORMAL armor_type = /datum/armor/merc/space/clown - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword,/obj/item/handcuffs) + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/transforming/energy/sword,/obj/item/handcuffs) siemens_coefficient = 0.6 //Four below avalible through cargo @@ -111,7 +111,7 @@ desc = "One of the few combat-grade suits avalible in the frontier, and the poster-child of Hephaestus Industries. Comes equipped with a wrist-bound oxygen timer." w_class = WEIGHT_CLASS_NORMAL armor_type = /datum/armor/station/secsuit - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword,/obj/item/handcuffs) + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/transforming/energy/sword,/obj/item/handcuffs) siemens_coefficient = 0.6 species_restricted = null helmet_type = /obj/item/clothing/head/helmet/space/void/odst @@ -131,7 +131,7 @@ desc = "A standard Icarus line suit that has been repourposed to protect from heavier biohazards." w_class = WEIGHT_CLASS_NORMAL armor_type = /datum/armor/exploration/space - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword,/obj/item/handcuffs) + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/transforming/energy/sword,/obj/item/handcuffs) siemens_coefficient = 0.6 species_restricted = null helmet_type = /obj/item/clothing/head/helmet/space/void/odst_med @@ -151,7 +151,7 @@ desc = "Favoured suit of deep-space engineers, comfortable and comparable to suits avalible to Nanotrasen Engineers. Comes equipped with a wrist-bound oxygen timer." w_class = WEIGHT_CLASS_NORMAL armor_type = /datum/armor/engineering/space - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword,/obj/item/handcuffs) + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/transforming/energy/sword,/obj/item/handcuffs) siemens_coefficient = 0.6 species_restricted = null helmet_type = /obj/item/clothing/head/helmet/space/void/odst_eng @@ -171,7 +171,7 @@ desc = "Cheaper version of the main Icarus line, often marketed to Frontier settlements. Perfect for Expeditions." w_class = WEIGHT_CLASS_NORMAL armor_type = /datum/armor/exploration/space - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword,/obj/item/handcuffs) + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/transforming/energy/sword,/obj/item/handcuffs) siemens_coefficient = 0.6 species_restricted = null helmet_type = /obj/item/clothing/head/helmet/space/void/odst_exp @@ -195,7 +195,7 @@ item_state_slots = list(SLOT_ID_RIGHT_HAND = "syndie_voidsuit", SLOT_ID_LEFT_HAND = "syndie_voidsuit") w_class = WEIGHT_CLASS_NORMAL armor_type = /datum/armor/merc/space - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword,/obj/item/handcuffs) + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/transforming/energy/sword,/obj/item/handcuffs) siemens_coefficient = 0.6 species_restricted = null helmet_type = /obj/item/clothing/head/helmet/space/void/odst_necro @@ -207,6 +207,6 @@ item_state_slots = list(SLOT_ID_RIGHT_HAND = "syndie_voidsuit", SLOT_ID_LEFT_HAND = "syndie_voidsuit") w_class = WEIGHT_CLASS_NORMAL armor_type = /datum/armor/merc/space - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword,/obj/item/handcuffs) + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/transforming/energy/sword,/obj/item/handcuffs) siemens_coefficient = 0.6 species_restricted = null diff --git a/code/modules/clothing/spacesuits/void/xeno/tajara.dm b/code/modules/clothing/spacesuits/void/xeno/tajara.dm index 44e08971eed1..2c1c2e0bced0 100644 --- a/code/modules/clothing/spacesuits/void/xeno/tajara.dm +++ b/code/modules/clothing/spacesuits/void/xeno/tajara.dm @@ -15,7 +15,7 @@ /obj/item/ammo_magazine, /obj/item/ammo_casing, /obj/item/melee/baton, - /obj/item/melee/energy/sword, + /obj/item/melee/transforming/energy/sword, /obj/item/handcuffs ) species_restricted = list(SPECIES_TAJ) @@ -46,7 +46,7 @@ /obj/item/ammo_magazine, /obj/item/ammo_casing, /obj/item/melee/baton, - /obj/item/melee/energy/sword, + /obj/item/melee/transforming/energy/sword, /obj/item/handcuffs ) species_restricted = list(SPECIES_TAJ) diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm index 99728ffc838b..5914082a6418 100644 --- a/code/modules/clothing/suits/armor.dm +++ b/code/modules/clothing/suits/armor.dm @@ -79,37 +79,6 @@ item_state_slots = list(SLOT_ID_RIGHT_HAND = "bulletproof_new", SLOT_ID_LEFT_HAND = "bulletproof_new") blood_overlay_type = "armor" -/obj/item/clothing/suit/armor/laserproof - name = "ablative armor vest" - desc = "A vest that excels in protecting the wearer against energy projectiles." - icon_state = "armor_reflec" - blood_overlay_type = "armor" - armor_type = /datum/armor/station/ablative - siemens_coefficient = 0.1 - -/obj/item/clothing/suit/armor/laserproof/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(istype(damage_source, /obj/projectile/energy) || istype(damage_source, /obj/projectile/beam)) - var/obj/projectile/P = damage_source - - if(P.reflected) // Can't reflect twice - return ..() - - var/reflectchance = 40 - round(damage/3) - if(!(def_zone in list(BP_TORSO, BP_GROIN))) - reflectchance /= 2 - if(P.starting && prob(reflectchance)) - visible_message("\The [user]'s [src.name] reflects [attack_text]!") - - // Find a turf near or on the original location to bounce to - var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) - var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) - var/turf/curloc = get_turf(user) - - // redirect the projectile - P.redirect(new_x, new_y, curloc, user) - P.reflected = 1 - - return PROJECTILE_CONTINUE // complete projectile permutation /obj/item/clothing/suit/armor/combat name = "combat vest" @@ -193,7 +162,22 @@ blood_overlay_type = "armor" armor_type = /datum/armor/none -/obj/item/clothing/suit/armor/reactive/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") +/obj/item/clothing/suit/armor/reactive/equipped(mob/user, slot, flags) + . = ..() + if(slot == SLOT_ID_HANDS) + return + // if you're reading this: this is not the right way to do shieldcalls + // this is just a lazy implementation + // signals have highest priority, this as a piece of armor shouldn't have that. + RegisterSignal(user, COMSIG_ATOM_SHIELDCALL, PROC_REF(shieldcall)) + +/obj/item/clothing/suit/armor/reactive/unequipped(mob/user, slot, flags) + . = ..() + if(slot == SLOT_ID_HANDS) + return + UnregisterSignal(user, COMSIG_ATOM_SHIELDCALL) + +/obj/item/clothing/suit/armor/reactive/proc/shieldcall(mob/user, list/shieldcall_args, fake_attack) if(prob(50)) user.visible_message("The reactive teleport system flings [user] clear of the attack!") var/list/turfs = new/list() @@ -211,10 +195,8 @@ spark_system.set_up(5, 0, user.loc) spark_system.start() playsound(user.loc, /datum/soundbyte/grouped/sparks, 50, 1) - - user.loc = picked - return PROJECTILE_FORCE_MISS - return 0 + user.forceMove(picked) + shieldcall_args[SHIELDCALL_ARG_FLAGS] |= SHIELDCALL_FLAG_ATTACK_BLOCKED | SHIELDCALL_FLAG_ATTACK_PASSTHROUGH /obj/item/clothing/suit/armor/reactive/attack_self(mob/user) . = ..() @@ -250,6 +232,13 @@ valid_accessory_slots = null var/block_chance = 20 +/obj/item/clothing/suit/armor/alien/mob_armorcall(mob/defending, list/shieldcall_args, fake_attack) + if(prob(block_chance)) + defending.visible_message(SPAN_DANGER("[src] completely absorbs [RESOLVE_SHIELDCALL_ATTACK_TEXT(shieldcall_args)]!")) + shieldcall_args[SHIELDCALL_ARG_FLAGS] |= SHIELDCALL_FLAGS_FOR_COMPLETE_BLOCK + return + return ..() + /obj/item/clothing/suit/armor/alien/tank name = "alien protection suit" desc = "It's really resilient yet lightweight, so it's probably meant to be armor. Strangely enough it seems to have been designed for a humanoid shape." @@ -260,12 +249,6 @@ armor_type = /datum/armor/alien/heavy block_chance = 40 -/obj/item/clothing/suit/armor/alien/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(prob(block_chance)) - user.visible_message("\The [src] completely absorbs [attack_text]!") - return TRUE - return FALSE - //Non-hardsuit ERT armor. /obj/item/clothing/suit/armor/vest/ert name = "emergency response team armor" diff --git a/code/modules/clothing/under/accessories/armor.dm b/code/modules/clothing/under/accessories/armor.dm index 612445c6eb7b..f42b06b1c0a6 100644 --- a/code/modules/clothing/under/accessories/armor.dm +++ b/code/modules/clothing/under/accessories/armor.dm @@ -178,28 +178,44 @@ armor_type = /datum/armor/station/ablative siemens_coefficient = 0.2 -/obj/item/clothing/accessory/armor/armorplate/ablative/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") +/obj/item/clothing/accessory/armor/armorplate/ablative/equipped(mob/user, slot, flags) + . = ..() + if(slot == SLOT_ID_HANDS) + return + // if you're reading this: this is not the right way to do shieldcalls + // this is just a lazy implementation + // signals have highest priority, this as a piece of armor shouldn't have that. + RegisterSignal(user, COMSIG_ATOM_SHIELDCALL, PROC_REF(shieldcall)) + +/obj/item/clothing/accessory/armor/armorplate/ablative/unequipped(mob/user, slot, flags) + . = ..() + if(slot == SLOT_ID_HANDS) + return + UnregisterSignal(user, COMSIG_ATOM_SHIELDCALL) + +/obj/item/clothing/accessory/armor/armorplate/ablative/proc/shieldcall(mob/defending, list/shieldcall_args, fake_attack) + var/damage_source = shieldcall_args[SHIELDCALL_ARG_WEAPON] + var/def_zone = shieldcall_args[SHIELDCALL_ARG_HIT_ZONE] if(istype(damage_source, /obj/projectile/energy) || istype(damage_source, /obj/projectile/beam)) var/obj/projectile/P = damage_source if(P.reflected) - return ..() + return - var/reflectchance = 20 - round(damage/3) + var/reflectchance = 20 - round(shieldcall_args[SHIELDCALL_ARG_DAMAGE]/3) if(!(def_zone in list(BP_TORSO, BP_GROIN))) reflectchance /= 2 if(P.starting && prob(reflectchance)) - visible_message("\The [user]'s [src.name] reflects [attack_text]!") + visible_message("\The [defending]'s [src.name] reflects [P]!") var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) - var/turf/curloc = get_turf(user) + var/turf/curloc = get_turf(defending) - P.redirect(new_x, new_y, curloc, user) + P.legacy_redirect(new_x, new_y, curloc, defending) P.reflected = 1 - - return PROJECTILE_CONTINUE + shieldcall_args[SHIELDCALL_ARG_FLAGS] |= SHIELDCALL_FLAGS_FOR_PROJECTILE_DEFLECT ////////////// //Arm guards diff --git a/code/modules/clothing/under/accessories/holster.dm b/code/modules/clothing/under/accessories/holster.dm index 4107b17d5459..20d169cc85ea 100644 --- a/code/modules/clothing/under/accessories/holster.dm +++ b/code/modules/clothing/under/accessories/holster.dm @@ -157,7 +157,7 @@ desc = "A handsome synthetic leather scabbard with matching belt." icon_state = "holster_machete" concealed_holster = 0 - can_hold = list(/obj/item/material/knife/machete, /obj/item/melee/energy/hfmachete, /obj/item/reagent_containers/spray, /obj/item/soap, + can_hold = list(/obj/item/material/knife/machete, /obj/item/melee/transforming/hfmachete, /obj/item/reagent_containers/spray, /obj/item/soap, /obj/item/c_tube, /obj/item/bikehorn) cant_hold = list(/obj/item/material/knife/machete/armblade) sound_in = 'sound/effects/holster/sheathin.ogg' diff --git a/code/modules/examine/descriptions/weapons.dm b/code/modules/examine/descriptions/weapons.dm index 835d35882dcc..1120bdcfb6ce 100644 --- a/code/modules/examine/descriptions/weapons.dm +++ b/code/modules/examine/descriptions/weapons.dm @@ -66,7 +66,7 @@ set to 'harm', you will inflict damage when using it, regardless if it is on or not. Each stun reduces the baton's charge, which can be replenished by \ putting it inside a weapon recharger." -/obj/item/melee/energy/sword +/obj/item/melee/transforming/energy/sword description_antag = "The energy sword is a very strong melee weapon, capable of severing limbs easily, if they are targeted. It can also has a chance \ to block projectiles and melee attacks while it is on and being held. The sword can be toggled on or off by using it in your hand. While it is off, \ it can be concealed in your pocket or bag." diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index e553eba475a4..93f79ff03da4 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -328,7 +328,7 @@ proc/check_panel(mob/M) return GLOBAL_LIST_INIT(non_fakeattack_weapons, list(/obj/item/gun/ballistic, /obj/item/ammo_magazine/a357/speedloader,\ - /obj/item/gun/energy/crossbow, /obj/item/melee/energy/sword,\ + /obj/item/gun/energy/crossbow, /obj/item/melee/transforming/energy/sword,\ /obj/item/storage/box/syndicate, /obj/item/storage/box/emps,\ /obj/item/cartridge/syndicate, /obj/item/clothing/under/chameleon,\ /obj/item/clothing/shoes/syndigaloshes, /obj/item/card/id/syndicate,\ diff --git a/code/modules/food/drinks/bottle.dm b/code/modules/food/drinks/bottle.dm index ac9f648ac278..606531dcaec3 100644 --- a/code/modules/food/drinks/bottle.dm +++ b/code/modules/food/drinks/bottle.dm @@ -162,7 +162,7 @@ if(target_zone == "head" && istype(L, /mob/living/carbon/)) user.visible_message("\The [user] smashes [src] over [L]'s head!") if(weaken_duration) - L.apply_effect(min(weaken_duration, 5), WEAKEN, blocked) // Never weaken more than a flash! + L.apply_effect(min(weaken_duration, 5), WEAKEN) // Never weaken more than a flash! else user.visible_message("\The [user] smashes [src] into [L]!") diff --git a/code/modules/gamemaster/actions/electrified_door.dm b/code/modules/gamemaster/actions/electrified_door.dm index e4ba9c2efb34..c1d4ff1ad914 100644 --- a/code/modules/gamemaster/actions/electrified_door.dm +++ b/code/modules/gamemaster/actions/electrified_door.dm @@ -63,7 +63,7 @@ if(!chosen_door || !chosen_door.arePowerSystemsOn()) return chosen_door.visible_message("\The [chosen_door]'s hydraulics detonate!") - chosen_door.fragmentate(get_turf(chosen_door), rand(5, 10), rand(3, 5), list(/obj/projectile/bullet/pellet/fragment/tank/small)) + chosen_door.shrapnel_explosion(rand(5, 10), rand(3, 5), /obj/projectile/bullet/pellet/fragment/tank/small) explosion(get_turf(chosen_door),-1,-1,2,3) chosen_door.lock() diff --git a/code/modules/ghostroles/spawnpoint.dm b/code/modules/ghostroles/spawnpoint.dm index 5d424b155bbf..7439c71193ef 100644 --- a/code/modules/ghostroles/spawnpoint.dm +++ b/code/modules/ghostroles/spawnpoint.dm @@ -20,7 +20,7 @@ GLOBAL_LIST_EMPTY(ghostrole_spawnpoints) var/spawntext /datum/component/ghostrole_spawnpoint/Initialize(role_type, allowed_spawns = INFINITY, list/params, datum/callback/proc_to_call_or_callback, notify_ghosts = TRUE, spawntext) - if((. = ..()) & COMPONENT_INCOMPATIBLE) + if((. = ..()) == COMPONENT_INCOMPATIBLE) return if(!isatom(parent)) return COMPONENT_INCOMPATIBLE diff --git a/code/modules/hardsuits/_rig.dm b/code/modules/hardsuits/_rig.dm index 44d584ac1691..a0283f4234a3 100644 --- a/code/modules/hardsuits/_rig.dm +++ b/code/modules/hardsuits/_rig.dm @@ -1136,11 +1136,6 @@ if(!CHECK_MOBILITY(user, MOBILITY_CAN_MOVE)) return - if(locate(/obj/effect/stop/, wearer.loc)) - for(var/obj/effect/stop/S in wearer.loc) - if(S.victim == wearer) - return - if(!wearer.lastarea) wearer.lastarea = get_area(wearer.loc) diff --git a/code/modules/hardsuits/modules/combat.dm b/code/modules/hardsuits/modules/combat.dm index dc2d4874ef62..66cdec37be3d 100644 --- a/code/modules/hardsuits/modules/combat.dm +++ b/code/modules/hardsuits/modules/combat.dm @@ -202,7 +202,7 @@ /obj/item/hardsuit_module/mounted/energy_blade/process(delta_time) if(holder && holder.wearer) - if(!(locate(/obj/item/melee/energy/blade) in holder.wearer)) + if(!(locate(/obj/item/melee/ninja_energy_blade) in holder.wearer)) deactivate() return 0 @@ -219,7 +219,7 @@ deactivate() return - var/obj/item/melee/energy/blade/blade = new(M) + var/obj/item/melee/ninja_energy_blade/blade = new(M) blade.creator = M M.put_in_hands(blade) @@ -232,7 +232,7 @@ if(!M) return - for(var/obj/item/melee/energy/blade/blade in M.contents) + for(var/obj/item/melee/ninja_energy_blade/blade in M.contents) qdel(blade) /obj/item/hardsuit_module/fabricator diff --git a/code/modules/hardsuits/suits/merc.dm b/code/modules/hardsuits/suits/merc.dm index ce1877f6d356..956e6a901cc2 100644 --- a/code/modules/hardsuits/suits/merc.dm +++ b/code/modules/hardsuits/suits/merc.dm @@ -31,7 +31,7 @@ /obj/item/ammo_magazine, /obj/item/ammo_casing, /obj/item/melee/baton, - /obj/item/melee/energy/sword, + /obj/item/melee/transforming/energy/sword, /obj/item/handcuffs, /obj/item/bluespace_radio, ) diff --git a/code/modules/holodeck/HolodeckObjects.dm b/code/modules/holodeck/HolodeckObjects.dm index 0854a36c59cf..0afe78ce86f7 100644 --- a/code/modules/holodeck/HolodeckObjects.dm +++ b/code/modules/holodeck/HolodeckObjects.dm @@ -239,16 +239,7 @@ /obj/item/holo/esword/red lcolor = "#FF0000" -/obj/item/holo/esword/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(active && default_parry_check(user, attacker, damage_source) && prob(50)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) - return TRUE - return FALSE +// todo: the parry system was removed from this because that sucks maybe readd it later lol /obj/item/holo/esword/attack_self(mob/user) . = ..() diff --git a/code/modules/holomap/holomap_datum.dm b/code/modules/holomap/holomap_datum.dm index f6d4f61bba54..df8f754888d9 100644 --- a/code/modules/holomap/holomap_datum.dm +++ b/code/modules/holomap/holomap_datum.dm @@ -14,8 +14,8 @@ if(isAI) T = get_turf(user.client.eye) - cursor.pixel_x = (T.x - 6 + HOLOMAP_PIXEL_OFFSET_X(T.z)) * PIXEL_MULTIPLIER - cursor.pixel_y = (T.y - 6 + HOLOMAP_PIXEL_OFFSET_Y(T.z)) * PIXEL_MULTIPLIER + cursor.pixel_x = (T.x - 6 + HOLOMAP_PIXEL_OFFSET_X(T.z)) * (WORLD_ICON_SIZE / 32) + cursor.pixel_y = (T.y - 6 + HOLOMAP_PIXEL_OFFSET_Y(T.z)) * (WORLD_ICON_SIZE / 32) legend.pixel_x = HOLOMAP_LEGEND_X(T.z) legend.pixel_y = HOLOMAP_LEGEND_Y(T.z) diff --git a/code/modules/hydroponics/trays/tray.dm b/code/modules/hydroponics/trays/tray.dm index e1689c458cbf..6003b3b9eabb 100644 --- a/code/modules/hydroponics/trays/tray.dm +++ b/code/modules/hydroponics/trays/tray.dm @@ -201,26 +201,22 @@ check_health() update_icon() -/obj/machinery/portable_atmospherics/hydroponics/bullet_act(var/obj/projectile/Proj) - +/obj/machinery/portable_atmospherics/hydroponics/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() //Don't act on seeds like dionaea that shouldn't change. if(seed && seed.get_trait(TRAIT_IMMUTABLE) > 0) return //Override for somatoray projectiles. - if(istype(Proj ,/obj/projectile/energy/floramut)&& prob(20)) - if(istype(Proj, /obj/projectile/energy/floramut/gene)) - var/obj/projectile/energy/floramut/gene/G = Proj + if(istype(proj ,/obj/projectile/energy/floramut)&& prob(20)) + if(istype(proj, /obj/projectile/energy/floramut/gene)) + var/obj/projectile/energy/floramut/gene/G = proj if(seed) seed = seed.diverge_mutate_gene(G.gene, get_turf(loc)) //get_turf just in case it's not in a turf. else mutate(1) - return - else if(istype(Proj ,/obj/projectile/energy/florayield) && prob(20)) + else if(istype(proj ,/obj/projectile/energy/florayield) && prob(20)) yield_mod = min(10,yield_mod+rand(1,2)) - return - - ..() /obj/machinery/portable_atmospherics/hydroponics/proc/check_health() if(seed && !dead && health <= 0) diff --git a/code/modules/integrated_electronics/subtypes/time.dm b/code/modules/integrated_electronics/subtypes/time.dm index ffd8651f9128..645bf29ae8e5 100644 --- a/code/modules/integrated_electronics/subtypes/time.dm +++ b/code/modules/integrated_electronics/subtypes/time.dm @@ -85,7 +85,7 @@ /obj/item/integrated_circuit/time/ticker/Destroy() if(is_running) - STOP_PROCESSING(SSfastprocess, src) + STOP_PROCESSING(SSprocess_5fps, src) return ..() /obj/item/integrated_circuit/time/ticker/on_data_written() diff --git a/code/modules/loot/packs/weapons.dm b/code/modules/loot/packs/weapons.dm index 2443fa9c5f6c..f6a9f723839c 100644 --- a/code/modules/loot/packs/weapons.dm +++ b/code/modules/loot/packs/weapons.dm @@ -3,8 +3,8 @@ /datum/prototype/struct/loot_pack/weapons/melee1 some = list( - /obj/item/melee/energy/sword, - /obj/item/shield/energy, + /obj/item/melee/transforming/energy/sword, + /obj/item/shield/transforming/energy, /obj/item/melee/baton, /obj/item/melee/chainofcommand, /obj/item/melee/nanite_knife, diff --git a/code/modules/mapping/map_helpers/engine_loader.dm b/code/modules/mapping/map_helpers/engine_loader.dm index 7d0d8e97ea4e..1dc4d35494f3 100644 --- a/code/modules/mapping/map_helpers/engine_loader.dm +++ b/code/modules/mapping/map_helpers/engine_loader.dm @@ -36,7 +36,7 @@ their_for_map = initial(map_path.id) if(their_for_map != src.for_map) continue - var/name = initial(path.name) + var/name = lowertext(initial(path.name)) potential_filtered[path] = isnum(probabilities[name])? probabilities[name] : 1 var/picked_path = pickweightAllowZero(potential_filtered) diff --git a/code/modules/mining/kinetic_crusher.dm b/code/modules/mining/kinetic_crusher.dm index a6e7c3b66276..4bc04ca17d20 100644 --- a/code/modules/mining/kinetic_crusher.dm +++ b/code/modules/mining/kinetic_crusher.dm @@ -311,7 +311,7 @@ damage_type = BRUTE damage_flag = ARMOR_BOMB range = WORLD_ICON_SIZE * 6 - accuracy = INFINITY // NO. + accuracy_disabled = TRUE // log_override = TRUE var/obj/item/kinetic_crusher/hammer_synced @@ -319,7 +319,10 @@ hammer_synced = null return ..() -/obj/projectile/destabilizer/on_hit(atom/target, blocked = FALSE) +/obj/projectile/destabilizer/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(isliving(target)) var/mob/living/L = target if(hammer_synced.can_mark(L)) @@ -341,7 +344,6 @@ var/turf/simulated/mineral/M = target_turf new /obj/effect/temp_visual/kinetic_blast(M) M.GetDrilled(firer) - ..() /* //trophies diff --git a/code/modules/mining/machine_processing.dm b/code/modules/mining/machine_processing.dm index af02717eb6d7..a39668372302 100644 --- a/code/modules/mining/machine_processing.dm +++ b/code/modules/mining/machine_processing.dm @@ -199,9 +199,9 @@ speed_process = !speed_process // switching gears if(speed_process) // high gear STOP_MACHINE_PROCESSING(src) - START_PROCESSING(SSfastprocess, src) + START_PROCESSING(SSprocess_5fps, src) else // low gear - STOP_PROCESSING(SSfastprocess, src) + STOP_PROCESSING(SSprocess_5fps, src) START_MACHINE_PROCESSING(src) /obj/machinery/mineral/processing_unit/process(delta_time) diff --git a/code/modules/mining/mine_outcrops.dm b/code/modules/mining/mine_outcrops.dm index d5c769d77473..03fb1c355fd1 100644 --- a/code/modules/mining/mine_outcrops.dm +++ b/code/modules/mining/mine_outcrops.dm @@ -108,11 +108,12 @@ return . = ..() -/obj/structure/outcrop/bullet_act(obj/projectile/P, def_zone) - if(P.damage_flag == ARMOR_BOMB) //Intended for kinetic accelerators/daggers to just get rid of this stuff quickly. They're rocks. - GetDrilled() - return +/obj/structure/outcrop/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_TARGET_ABORT) + return + if(proj.damage_flag == ARMOR_BOMB) //Intended for kinetic accelerators/daggers to just get rid of this stuff quickly. They're rocks. + GetDrilled() /obj/structure/outcrop/proc/GetDrilled() new outcropdrop(get_turf(src), rand(mindrop,upperdrop)) diff --git a/code/modules/mining/mine_turfs.dm b/code/modules/mining/mine_turfs.dm index c8c81622e3ea..adb4c3169627 100644 --- a/code/modules/mining/mine_turfs.dm +++ b/code/modules/mining/mine_turfs.dm @@ -296,10 +296,10 @@ CREATE_STANDARD_TURFS(/turf/simulated/mineral/floor/ignore_cavegen) new oretype(src) resources[ore] = 0 -/turf/simulated/mineral/bullet_act(var/obj/projectile/Proj) // only emitters for now +/turf/simulated/mineral/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) . = ..() - if(Proj.excavation_amount) - var/newDepth = excavation_level + Proj.excavation_amount // Used commonly below + if(proj.excavation_amount) + var/newDepth = excavation_level + proj.excavation_amount // Used commonly below if(newDepth >= 200) // first, if the turf is completely drilled then don't bother checking for finds and just drill it GetDrilled(0) return @@ -312,8 +312,8 @@ CREATE_STANDARD_TURFS(/turf/simulated/mineral/floor/ignore_cavegen) if(prob(50)) artifact_debris() - excavation_level += Proj.excavation_amount - update_archeo_overlays(Proj.excavation_amount) + excavation_level += proj.excavation_amount + update_archeo_overlays(proj.excavation_amount) /turf/simulated/mineral/Bumped(AM) diff --git a/code/modules/mob/defense.dm b/code/modules/mob/defense.dm deleted file mode 100644 index fae10ea95e3a..000000000000 --- a/code/modules/mob/defense.dm +++ /dev/null @@ -1,83 +0,0 @@ -/** - * calculates the resulting damage from an attack, taking into account our armor and soak - * - * @params - * * damage - raw damage - * * tier - penetration / attack tier - * * flag - armor flag as seen in [code/__DEFINES/combat/armor.dm] - * * mode - damage_mode - * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] - * * weapon - (optional) attacking /obj/item for melee or thrown, /obj/projectile for ranged, /mob for unarmed - * * target_zone - where it's impacting - * - * @return args as list. - */ -/mob/proc/check_mob_armor(damage, tier, flag, mode, attack_type, datum/weapon, target_zone) - var/list/returned = check_armor(damage, tier, flag, mode, attack_type, weapon) - damage = returned[1] - mode = returned[4] - return args.Copy() - -/** - * runs armor against an incoming attack - * this proc can have side effects - * - * @params - * * damage - raw damage - * * tier - penetration / attack tier - * * flag - armor flag as seen in [code/__DEFINES/combat/armor.dm] - * * mode - damage_mode - * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] - * * weapon - (optional) attacking /obj/item for melee or thrown, /obj/projectile for ranged, /mob for unarmed - * * target_zone - where it's impacting - * - * @return args as list. - */ -/mob/proc/run_mob_armor(damage, tier, flag, mode, attack_type, datum/weapon, target_zone) - var/list/returned = run_armor(damage, tier, flag, mode, attack_type, weapon) - damage = returned[1] - mode = returned[4] - return args.Copy() - -/** - * check overall armor - * does not support modifying damage modes. - * - * @params - * * damage - raw damage - * * tier - penetration / attack tier - * * flag - armor flag as seen in [code/__DEFINES/combat/armor.dm] - * * mode - damage_mode - * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] - * * weapon - (optional) attacking /obj/item for melee or thrown, /obj/projectile for ranged, /mob for unarmed - * * target_zone - where it's impacting - * - * @return args as list. - */ -/mob/proc/check_overall_armor(damage, tier, flag, mode, attack_type, datum/weapon, target_zone) - var/list/returned = check_armor(damage, tier, flag, mode, attack_type, weapon) - damage = returned[1] - mode = returned[4] - return args.Copy() - -/** - * runs overall armor against an incoming attack - * this proc can have side effects - * does not support modifying damage modes. - * - * @params - * * damage - raw damage - * * tier - penetration / attack tier - * * flag - armor flag as seen in [code/__DEFINES/combat/armor.dm] - * * mode - damage_mode - * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] - * * weapon - (optional) attacking /obj/item for melee or thrown, /obj/projectile for ranged, /mob for unarmed - * * target_zone - where it's impacting - * - * @return args as list. - */ -/mob/proc/run_overall_armor(damage, tier, flag, mode, attack_type, datum/weapon, target_zone) - var/list/returned = run_armor(damage, tier, flag, mode, attack_type, weapon) - damage = returned[1] - mode = returned[4] - return args.Copy() diff --git a/code/modules/mob/grab.dm b/code/modules/mob/grab.dm index aad94cc6f478..d42f8c6c55b3 100644 --- a/code/modules/mob/grab.dm +++ b/code/modules/mob/grab.dm @@ -1,21 +1,39 @@ /** * returns everyone we're grabbing associated to state */ -/mob/proc/grabbing() +/mob/proc/get_grabbing() RETURN_TYPE(/list) . = list() for(var/obj/item/grab/G in get_held_items()) .[G.affecting] = G.state +/** + * returns everyone we're grabbing that are at least the given grab state + */ +/mob/proc/get_grabbing_of_state(state) + RETURN_TYPE(/list) + . = list() + for(var/obj/item/grab/G in get_held_items()) + if(G.state < state) + continue + . += G.affecting + /** * returns everyone we're grabbing, recursively; this can include ourselves! + * + * @return grabbed mobs associated to states */ -/mob/proc/grabbing_recursive(list/L = list()) +/mob/proc/get_grabbing_recursive(list/L = list(), safety = 15, list/processed = list()) RETURN_TYPE(/list) + if(processed[src]) + return + processed[src] = TRUE + if(safety <= 0) + CRASH("infinite loop guard tripped") . = L for(var/obj/item/grab/G in get_held_items()) .[G.affecting] = max(.[G.affecting], G.state) - grabbing_recursive(G.affecting) + G.affecting.get_grabbing_recursive(., --safety, processed) /** * check the grab state of us to someone @@ -242,7 +260,7 @@ /obj/item/grab/throw_resolve_override(atom/movable/resolved, mob/user) return TRUE -/obj/item/grab/melee_object_hit(atom/target, datum/event_args/actor/clickchain/clickchain, clickchain_flags, mult) +/obj/item/grab/melee_object_hit(atom/target, datum/event_args/actor/clickchain/clickchain, clickchain_flags) switch(state) if(GRAB_PASSIVE) clickchain.visible_feedback( diff --git a/code/modules/mob/inventory/items.dm b/code/modules/mob/inventory/items.dm index 59b2a1d438c0..e0c62b3f61b9 100644 --- a/code/modules/mob/inventory/items.dm +++ b/code/modules/mob/inventory/items.dm @@ -34,7 +34,7 @@ /obj/item/proc/equipped(mob/user, slot, flags) SHOULD_CALL_PARENT(TRUE) SEND_SIGNAL(src, COMSIG_ITEM_EQUIPPED, user, slot, flags) - SEND_SIGNAL(user, COMSIG_MOB_ITEM_EQUIPPED, user, slot, flags) + SEND_SIGNAL(user, COMSIG_MOB_ITEM_EQUIPPED, src, slot, flags) worn_slot = slot if(!(flags & INV_OP_IS_ACCESSORY)) // todo: shouldn't be in here @@ -68,7 +68,7 @@ /obj/item/proc/unequipped(mob/user, slot, flags) SHOULD_CALL_PARENT(TRUE) SEND_SIGNAL(src, COMSIG_ITEM_UNEQUIPPED, user, slot, flags) - SEND_SIGNAL(user, COMSIG_MOB_ITEM_UNEQUIPPED, user, slot, flags) + SEND_SIGNAL(user, COMSIG_MOB_ITEM_UNEQUIPPED, src, slot, flags) worn_slot = null if(!(flags & INV_OP_IS_ACCESSORY)) // todo: shouldn't be in here @@ -145,11 +145,20 @@ */ /obj/item/proc/pickup(mob/user, flags, atom/oldLoc) SHOULD_CALL_PARENT(TRUE) + + // we load the component here as it hooks equipped, + // so loading it here means it can still handle the equipped signal. + if(passive_parry) + LoadComponent(/datum/component/passive_parry, passive_parry) + SEND_SIGNAL(src, COMSIG_ITEM_PICKUP, user, flags, oldLoc) SEND_SIGNAL(user, COMSIG_MOB_ITEM_PICKUP, src, flags, oldLoc) + reset_pixel_offsets() hud_layerise() + item_flags |= ITEM_IN_INVENTORY + // todo: should this be here transform = null if(isturf(oldLoc) && !(flags & (INV_OP_SILENT | INV_OP_DIRECTLY_EQUIPPING))) @@ -374,6 +383,8 @@ * checks if we're held in hand * * if so, returns mob we're in + * + * @return the mob holding us */ /obj/item/proc/is_held() return (worn_slot == SLOT_ID_HANDS)? worn_mob() : null diff --git a/code/modules/mob/living/bot/secbot.dm b/code/modules/mob/living/bot/secbot.dm index 182892376d35..48e5a56b50a8 100644 --- a/code/modules/mob/living/bot/secbot.dm +++ b/code/modules/mob/living/bot/secbot.dm @@ -226,9 +226,9 @@ if(health < curhealth && on == TRUE) react_to_attack_polaris(user) -/mob/living/bot/secbot/bullet_act(var/obj/projectile/P) +/mob/living/bot/secbot/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) var/curhealth = health - var/mob/shooter = P.firer + var/mob/shooter = proj.firer . = ..() //if we already have a target just ignore to avoid lots of checking if(!target && health < curhealth && shooter && (shooter in view(world.view, src))) diff --git a/code/modules/mob/living/carbon/carbon-defense.dm b/code/modules/mob/living/carbon/carbon-defense.dm index 63a455454c09..8471cf172b62 100644 --- a/code/modules/mob/living/carbon/carbon-defense.dm +++ b/code/modules/mob/living/carbon/carbon-defense.dm @@ -1,5 +1,17 @@ //* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2024 Citadel Station developers. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +//* Projectile Handling *// + +/mob/living/carbon/process_bullet_miss(obj/projectile/proj, impact_flags, def_zone, efficiency) + . = ..() + if(!.) + return + // perform normal baymiss + . = get_zone_with_miss_chance(., src, -10, TRUE) + // check if we even have that organ; if not, they automatically miss + if(!get_organ(.)) + return null //* Misc Effects *// diff --git a/code/modules/mob/living/carbon/human/defense.dm b/code/modules/mob/living/carbon/human/defense.dm index c5b686488c2c..f1035982f304 100644 --- a/code/modules/mob/living/carbon/human/defense.dm +++ b/code/modules/mob/living/carbon/human/defense.dm @@ -1,37 +1,20 @@ -/mob/living/carbon/human/check_mob_armor(damage, tier, flag, mode, attack_type, datum/weapon, target_zone) - var/obj/item/organ/external/part = get_organ(target_zone) - for(var/obj/item/I as anything in inventory?.items_that_cover(part.body_part_flags)) - var/list/results = I.checking_mob_armor(arglist(args)) - damage = results[1] - mode = results[4] - return ..() +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// -/mob/living/carbon/human/run_mob_armor(damage, tier, flag, mode, attack_type, datum/weapon, target_zone) - var/obj/item/organ/external/part = get_organ(target_zone) - for(var/obj/item/I as anything in inventory?.items_that_cover(part.body_part_flags)) - var/list/results = I.running_mob_armor(arglist(args)) - damage = results[1] - mode = results[4] - return ..() +/mob/living/carbon/human/run_armorcalls(list/shieldcall_args, fake_attack, filter_zone) + ..() // perform default /mob level -/mob/living/carbon/human/check_overall_armor(damage, tier, flag, mode, attack_type, datum/weapon, target_zone) - var/total = 0 - var/total_size = 0 - for(var/key in organs_by_name) - var/rel_size = organ_rel_size[key] - if(!rel_size) - continue - var/obj/item/organ/external/part = organs_by_name[key] - var/resultant = damage + if(filter_zone) + // just one zone + var/obj/item/organ/external/part = get_organ(filter_zone) for(var/obj/item/I as anything in inventory?.items_that_cover(part.body_part_flags)) - var/list/results = I.checking_mob_armor(resultant, tier, flag, mode, attack_type, weapon, target_zone) - resultant = results[1] - total += resultant * rel_size - total_size += rel_size - damage = total / total_size - return ..() + I.mob_armorcall(src, shieldcall_args, fake_attack) + if(shieldcall_args[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAG_TERMINATE) + break + return -/mob/living/carbon/human/run_overall_armor(damage, tier, flag, mode, attack_type, datum/weapon, target_zone) + var/damage = shieldcall_args[SHIELDCALL_ARG_DAMAGE] + // all zones, uh oh, this is about to get very ugly var/total = 0 var/total_size = 0 for(var/key in organs_by_name) @@ -41,9 +24,10 @@ var/obj/item/organ/external/part = organs_by_name[key] var/resultant = damage for(var/obj/item/I as anything in inventory?.items_that_cover(part.body_part_flags)) - var/list/results = I.running_mob_armor(resultant, tier, flag, mode, attack_type, weapon, target_zone) - resultant = results[1] + var/list/copied_args = args.Copy() + copied_args[SHIELDCALL_ARG_DAMAGE] = resultant + I.mob_armorcall(src, copied_args, fake_attack) + resultant = copied_args[SHIELDCALL_ARG_DAMAGE] total += resultant * rel_size total_size += rel_size - damage = total / total_size - return ..() + shieldcall_args[SHIELDCALL_ARG_DAMAGE] = total / total_size diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human-damage-legacy.dm similarity index 98% rename from code/modules/mob/living/carbon/human/human_damage.dm rename to code/modules/mob/living/carbon/human/human-damage-legacy.dm index 212de7a6f405..f5a2e375af4f 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human-damage-legacy.dm @@ -350,9 +350,6 @@ This function restores all organs. current_organ.rejuvenate_legacy(ignore_prosthetic_prefs) /mob/living/carbon/human/apply_damage(var/damage = 0, var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0, var/soaked = 0, var/sharp = 0, var/edge = 0, var/obj/used_weapon = null) - if(GLOB.Debug2) - log_world("## DEBUG: human/apply_damage() was called on [src], with [damage] damage, an armor value of [blocked], and a soak value of [soaked].") - var/obj/item/organ/external/organ = null if(isorgan(def_zone)) organ = def_zone diff --git a/code/modules/mob/living/carbon/human/human-damage.dm b/code/modules/mob/living/carbon/human/human-damage.dm new file mode 100644 index 000000000000..1e411fffb295 --- /dev/null +++ b/code/modules/mob/living/carbon/human/human-damage.dm @@ -0,0 +1,4 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +// todo: start translating code over diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human-defense-legacy.dm similarity index 90% rename from code/modules/mob/living/carbon/human/human_defense.dm rename to code/modules/mob/living/carbon/human/human-defense-legacy.dm index c5ac3393588d..663da91a0673 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human-defense-legacy.dm @@ -1,44 +1,3 @@ -/* -Contains most of the procs that are called when a mob is attacked by something - -bullet_act -legacy_ex_act -meteor_act - -*/ - -/mob/living/carbon/human/bullet_act(var/obj/projectile/P, var/def_zone) - def_zone = check_zone(def_zone) - if(!has_organ(def_zone)) - return PROJECTILE_FORCE_MISS //if they don't have the organ in question then the projectile just passes by. - - var/obj/item/organ/external/organ = get_organ() - - //Shields - var/shield_check = check_shields(P.damage, P, null, def_zone, "the [P.name]") - if(shield_check) // If the block roll succeeded, this is true. - if(shield_check < 0) // The shield did something weird and the bullet needs to keep doing things (e.g. it was reflected). - return shield_check // Likely equal to PROJECTILE_FORCE_MISS or PROJECTILE_CONTINUE. - else // Otherwise we blocked normally and stopped all the damage. - return 0 - - if(!P.nodamage) - organ.add_autopsy_data("[P.name]", P.damage) - - //Shrapnel - if(P.can_embed()) - var/armor = getarmor_organ(organ, "bullet") - if(!prob(armor/2)) //Even if the armor doesn't stop the bullet from hurting you, it might stop it from embedding. - var/hit_embed_chance = P.embed_chance + (P.damage - armor) //More damage equals more chance to embed - if(prob(max(hit_embed_chance, 0))) - var/obj/item/material/shard/shrapnel/SP = new() - SP.name = (P.name != "shrapnel")? "[P.name] shrapnel" : "shrapnel" - SP.desc = "[SP.desc] It looks like it was fired from [P.shot_from]." - SP.loc = organ - organ.embed(SP) - - return ..() - /mob/living/carbon/human/stun_effect_act(var/stun_amount, var/agony_amount, var/def_zone) var/obj/item/organ/external/affected = get_organ(check_zone(def_zone)) var/siemens_coeff = get_siemens_coefficient_organ(affected) @@ -207,14 +166,8 @@ meteor_act return gear return null -/mob/living/carbon/human/proc/check_shields(var/damage = 0, var/atom/damage_source = null, var/mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - for(var/obj/item/shield in list(l_hand, r_hand, wear_suit)) - if(!shield) continue - . = shield.handle_shield(src, damage, damage_source, attacker, def_zone, attack_text) - if(.) return - return 0 - /mob/living/carbon/human/resolve_item_attack(obj/item/I, mob/living/user, var/target_zone) + SEND_SIGNAL(src, COMSIG_MOB_LEGACY_RESOLVE_ITEM_ATTACK, I, user, target_zone) if(check_neckgrab_attack(I, user, target_zone)) return null @@ -226,7 +179,9 @@ meteor_act if(!hit_zone) return null - if(check_shields(I.damage_force, I, user, target_zone, "the [I.name]")) + var/shieldcall_results = atom_shieldcall_handle_item_melee(I, new /datum/event_args/actor/clickchain(user), FALSE, NONE) + // todo: clickchain should be checked for damage mult + if(shieldcall_results & SHIELDCALL_FLAGS_BLOCK_ATTACK) return var/obj/item/organ/external/affecting = get_organ(hit_zone) @@ -377,17 +332,29 @@ meteor_act miss_chance = max(5 * (distance - 2), 0) zone = get_zone_with_miss_chance(zone, src, miss_chance, ranged_attack=1) - if(zone && TT.thrower != src) - var/shield_check = check_shields(throw_damage, O, TT.thrower, zone, "[O]") - if(shield_check == PROJECTILE_FORCE_MISS) - zone = null - else if(shield_check) - return + var/force_pierce = FALSE + var/no_attack = FALSE + if(zone) + // perform shieldcall + // todo: reconcile all the way down to /atom, or at least a higher level than /human. + var/retval + for(var/datum/shieldcall/shieldcall as anything in shieldcalls) + retval |= shieldcall.handle_throw_impact(src, TT) + if(retval & SHIELDCALL_FLAGS_SHOULD_TERMINATE) + break + if(retval & SHIELDCALL_FLAGS_SHOULD_PROCESS) + if(retval & SHIELDCALL_FLAGS_PIERCE_ATTACK) + force_pierce = TRUE + if(retval & SHIELDCALL_FLAGS_BLOCK_ATTACK) + no_attack = TRUE if(!zone) visible_message("\The [O] misses [src] narrowly!") return COMPONENT_THROW_HIT_NEVERMIND | COMPONENT_THROW_HIT_PIERCE + if(no_attack) + return force_pierce? COMPONENT_THROW_HIT_PIERCE | COMPONENT_THROW_HIT_NEVERMIND : NONE + var/obj/item/organ/external/affecting = get_organ(zone) var/hit_area = affecting.name @@ -406,7 +373,6 @@ meteor_act if(armor < 100) apply_damage(throw_damage, dtype, zone, armor, soaked, is_sharp(O), has_edge(O), O) - //thrown weapon embedded object code. if(dtype == BRUTE && istype(O,/obj/item)) var/obj/item/I = O @@ -450,6 +416,8 @@ meteor_act anchored = TRUE pinned += O + return force_pierce? COMPONENT_THROW_HIT_PIERCE | COMPONENT_THROW_HIT_NEVERMIND : NONE + // This does a prob check to catch the thing flying at you, with a minimum of 1% /mob/living/carbon/human/proc/can_catch(var/obj/O) if(!get_active_held_item()) // If active hand is empty diff --git a/code/modules/mob/living/carbon/human/human-defense.dm b/code/modules/mob/living/carbon/human/human-defense.dm index 09f8eb24c177..4de1b17baac5 100644 --- a/code/modules/mob/living/carbon/human/human-defense.dm +++ b/code/modules/mob/living/carbon/human/human-defense.dm @@ -1,6 +1,34 @@ //* This file is explicitly licensed under the MIT license. *// //* Copyright (c) 2024 Citadel Station developers. *// +//* Projectile Handling *// + +/mob/living/carbon/human/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_TARGET_ABORT) + return + + if(impact_flags & PROJECTILE_IMPACT_BLOCKED) + return + + // todo: this shit shouldn't be here + var/obj/item/organ/external/organ = get_organ() + + if(!proj.nodamage) + organ.add_autopsy_data("[proj.name]", proj.damage) + + //Shrapnel + if(proj.can_embed()) + var/armor = getarmor_organ(organ, "bullet") + if(!prob(armor/2)) //Even if the armor doesn't stop the bullet from hurting you, it might stop it from embedding. + var/hit_embed_chance = proj.embed_chance + (proj.damage - armor) //More damage equals more chance to embed + if(prob(max(hit_embed_chance, 0))) + var/obj/item/material/shard/shrapnel/SP = new() + SP.name = (proj.name != "shrapnel")? "[proj.name] shrapnel" : "shrapnel" + SP.desc = "[SP.desc] It looks like it was fired from [proj.shot_from]." + SP.loc = organ + organ.embed(SP) + //* Misc Effects *// /mob/living/carbon/human/slip_act(slip_class, source, hard_strength, soft_strength, suppressed) diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm index 56cf6cf52b3e..0667987fb4fa 100644 --- a/code/modules/mob/living/carbon/human/human_attackhand.dm +++ b/code/modules/mob/living/carbon/human/human_attackhand.dm @@ -53,9 +53,11 @@ visible_message("[H] reaches for [src], but misses!") return FALSE - if(H != src && check_shields(0, null, H, H.zone_sel.selecting, H.name)) - H.do_attack_animation(src) - return FALSE + if(user.a_intent != INTENT_HARM) + var/shieldcall_results = atom_shieldcall_handle_touch(new /datum/event_args/actor/clickchain(user)) + if(shieldcall_results & SHIELDCALL_FLAGS_BLOCK_ATTACK) + H.do_attack_animation(src) + return FALSE if(istype(user,/mob/living/carbon)) var/mob/living/carbon/C = user @@ -195,6 +197,11 @@ if(!attack) return FALSE + var/shieldcall_results = atom_shieldcall_handle_unarmed_melee(attack, new /datum/event_args/actor/clickchain(user)) + if(shieldcall_results & SHIELDCALL_FLAGS_BLOCK_ATTACK) + H.do_attack_animation(src) + return FALSE + if(attack.unarmed_override(H, src, hit_zone)) return FALSE diff --git a/code/modules/mob/living/carbon/human/traits/weaver_objs.dm b/code/modules/mob/living/carbon/human/traits/weaver_objs.dm index b08f5cd4bb27..45211ebf7d6a 100644 --- a/code/modules/mob/living/carbon/human/traits/weaver_objs.dm +++ b/code/modules/mob/living/carbon/human/traits/weaver_objs.dm @@ -22,9 +22,9 @@ var/global/list/weavable_items = list() visible_message("\The [src] has been [W.get_attack_verb(src, user)] with \the [W][(user ? " by [user]." : ".")]") qdel(src) -/obj/effect/weaversilk/bullet_act(var/obj/projectile/Proj) - ..() - if(Proj.get_structure_damage()) +/obj/effect/weaversilk/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + if(proj.get_structure_damage()) qdel(src) /obj/effect/weaversilk/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) diff --git a/code/modules/mob/living/inventory.dm b/code/modules/mob/living/inventory.dm index 8f179980acf6..c9db1ad494af 100644 --- a/code/modules/mob/living/inventory.dm +++ b/code/modules/mob/living/inventory.dm @@ -185,40 +185,6 @@ SLOT_ID_MASK ) -/mob/living/ret_grab(obj/effect/list_container/mobl/L as obj, flag) - if ((!( istype(l_hand, /obj/item/grab) ) && !( istype(r_hand, /obj/item/grab) ))) - if (!( L )) - return null - else - return L.container - else - if (!( L )) - L = new /obj/effect/list_container/mobl( null ) - L.container += src - L.master = src - if (istype(l_hand, /obj/item/grab)) - var/obj/item/grab/G = l_hand - if (!( L.container.Find(G.affecting) )) - L.container += G.affecting - if (G.affecting) - G.affecting.ret_grab(L, 1) - if (istype(r_hand, /obj/item/grab)) - var/obj/item/grab/G = r_hand - if (!( L.container.Find(G.affecting) )) - L.container += G.affecting - if (G.affecting) - G.affecting.ret_grab(L, 1) - if (!( flag )) - if (L.master == src) - var/list/temp = list( ) - temp += L.container - //L = null - qdel(L) - return temp - else - return L.container - return - /mob/living/abiotic(full_body) if(full_body) if(item_considered_abiotic(wear_mask)) diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/living-damage.dm similarity index 93% rename from code/modules/mob/living/damage_procs.dm rename to code/modules/mob/living/living-damage.dm index 379abddbf02e..ac09b0d13bc5 100644 --- a/code/modules/mob/living/damage_procs.dm +++ b/code/modules/mob/living/living-damage.dm @@ -206,3 +206,17 @@ return radiation = max(0, radiation - amt) +//* Damage Instance Handling *// + +/mob/living/inflict_damage_instance(SHIELDCALL_PROC_HEADER) + if(inflict_damage_type_special(args)) + return + + var/weapon_descriptor = RESOLVE_SHIELDCALL_WEAPON_DESCRIPTOR(args) + var/brute = damage_type == BRUTE? damage : 0 + var/burn = damage_type == BURN? damage : 0 + + if(hit_zone) + take_targeted_damage(brute, burn, damage_mode, hit_zone, weapon_descriptor) + else + take_overall_damage(brute, burn, damage_mode, weapon_descriptor) diff --git a/code/modules/mob/living/defense.dm b/code/modules/mob/living/living-defense-legacy.dm similarity index 86% rename from code/modules/mob/living/defense.dm rename to code/modules/mob/living/living-defense-legacy.dm index 1986b015bc07..6f88e783acd2 100644 --- a/code/modules/mob/living/defense.dm +++ b/code/modules/mob/living/living-defense-legacy.dm @@ -96,6 +96,7 @@ . = ..() if(.) return + SEND_SIGNAL(src, COMSIG_MOB_LEGACY_ATTACK_HAND_INTERCEPT, user, params) var/mob/living/L = user if(!istype(L)) return @@ -110,65 +111,6 @@ else afflict_radiation(strength * RAD_MOB_ACT_COEFFICIENT - RAD_MOB_ACT_PROTECTION_PER_WAVE_SOURCE, TRUE) -/mob/living/bullet_act(var/obj/projectile/P, var/def_zone) - - //Being hit while using a deadman switch - if(istype(get_active_held_item(),/obj/item/assembly/signaler)) - var/obj/item/assembly/signaler/signaler = get_active_held_item() - if(signaler.deadman && prob(80)) - log_and_message_admins("has triggered a signaler deadman's switch") - src.visible_message("[src] triggers their deadman's switch!") - signaler.signal() - - if(ai_holder && P.firer) - ai_holder.react_to_attack_polaris(P.firer) - - //Armor - var/soaked = get_armor_soak(def_zone, P.damage_flag, P.armor_penetration) - var/absorb = run_armor_check(def_zone, P.damage_flag, P.armor_penetration) - var/proj_sharp = is_sharp(P) - var/proj_edge = has_edge(P) - var/final_damage = P.get_final_damage(src) - - if ((proj_sharp || proj_edge) && (soaked >= round(P.damage*0.8))) - proj_sharp = 0 - proj_edge = 0 - - if ((proj_sharp || proj_edge) && prob(legacy_mob_armor(def_zone, P.damage_flag))) - proj_sharp = 0 - proj_edge = 0 - - var/list/impact_sounds = islist(P.impact_sounds)? LAZYACCESS(P.impact_sounds, get_bullet_impact_effect_type(def_zone)) : P.impact_sounds - if(length(impact_sounds)) - playsound(src, pick(impact_sounds), 75) - else if(!isnull(impact_sounds)) - playsound(src, impact_sounds, 75) - - //Stun Beams - if(P.taser_effect) - stun_effect_act(0, P.agony, def_zone, P) - to_chat(src, "You have been hit by [P]!") - if(!P.nodamage) - apply_damage(final_damage, P.damage_type, def_zone, absorb, soaked, 0, P, sharp=proj_sharp, edge=proj_edge) - qdel(P) - return - - if(!P.nodamage) - apply_damage(final_damage, P.damage_type, def_zone, absorb, soaked, 0, P, sharp=proj_sharp, edge=proj_edge) - P.on_hit(src, absorb, soaked, def_zone) - - if(absorb == 100) - return 2 - else if (absorb >= 0) - return 1 - else - return 0 - -// return absorb - -/mob/living/get_bullet_impact_effect_type(var/def_zone) - return BULLET_IMPACT_MEAT - //Handles the effects of "stun" weapons /mob/living/proc/stun_effect_act(var/stun_amount, var/agony_amount, var/def_zone, var/used_weapon=null) flash_pain() @@ -185,7 +127,7 @@ apply_effect(EYE_BLUR, agony_amount/10) /mob/living/proc/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0, var/def_zone = null, var/stun = 1) - return 0 //only carbon liveforms have this proc + return 0 //only carbon liveforms have this proc /mob/living/emp_act(severity) var/list/L = src.get_equipped_items(TRUE, TRUE) @@ -231,6 +173,13 @@ apply_damage(damage, damage_type, def_zone, absorb, soaked) /mob/living/proc/resolve_item_attack(obj/item/I, mob/living/user, var/target_zone) + SEND_SIGNAL(src, COMSIG_MOB_LEGACY_RESOLVE_ITEM_ATTACK, I, user, target_zone) + + var/shieldcall_results = atom_shieldcall_handle_item_melee(I, new /datum/event_args/actor/clickchain(user), FALSE, NONE) + // todo: clickchain should be checked for damage mult + if(shieldcall_results & SHIELDCALL_FLAGS_BLOCK_ATTACK) + return + return target_zone //Called when the mob is hit with an item in combat. Returns the blocked result @@ -284,6 +233,32 @@ visible_message("\The [O] misses [src] narrowly!") return COMPONENT_THROW_HIT_PIERCE | COMPONENT_THROW_HIT_NEVERMIND + var/force_pierce = FALSE + var/no_attack = FALSE + + var/zone + if (istype(TT.thrower, /mob/living)) + zone = check_zone(TT.target_zone) + else + zone = ran_zone(BP_TORSO,75) //Hits a random part of the body, geared towards the chest + + if(zone) + // perform shieldcall + // todo: reconcile all the way down to /atom, or at least a higher level than /human. + var/retval + for(var/datum/shieldcall/shieldcall as anything in shieldcalls) + retval |= shieldcall.handle_throw_impact(src, TT) + if(retval & SHIELDCALL_FLAGS_SHOULD_TERMINATE) + break + if(retval & SHIELDCALL_FLAGS_SHOULD_PROCESS) + if(retval & SHIELDCALL_FLAGS_PIERCE_ATTACK) + force_pierce = TRUE + if(retval & SHIELDCALL_FLAGS_BLOCK_ATTACK) + no_attack = TRUE + + if(no_attack) + return force_pierce? COMPONENT_THROW_HIT_PIERCE | COMPONENT_THROW_HIT_NEVERMIND : NONE + src.visible_message("[src] has been hit by [O].") var/armor = run_armor_check(null, "melee") var/soaked = get_armor_soak(null, "melee") @@ -325,11 +300,13 @@ var/turf/T = near_wall(dir,2) if(T) - src.loc = T + src.forceMove(T) visible_message("[src] is pinned to the wall by [O]!","You are pinned to the wall by [O]!") src.anchored = 1 src.pinned += O + return force_pierce? COMPONENT_THROW_HIT_PIERCE | COMPONENT_THROW_HIT_NEVERMIND : NONE + /mob/living/proc/embed(var/obj/O, var/def_zone=null) O.loc = src src.embedded += O @@ -487,6 +464,7 @@ /mob/living/proc/reagent_permeability() return 1 +// todo: rework // Returns a number to determine if something is harder or easier to hit than normal. /mob/living/proc/get_evasion() var/result = evasion // First we get the 'base' evasion. Generally this is zero. @@ -495,14 +473,6 @@ result += M.evasion return result +// todo: rework /mob/living/proc/get_accuracy_penalty() - // Certain statuses make it harder to score a hit. - var/accuracy_penalty = 0 - if(has_status_effect(/datum/status_effect/sight/blindness)) - accuracy_penalty += 75 - if(eye_blurry) - accuracy_penalty += 30 - if(confused) - accuracy_penalty += 45 - - return accuracy_penalty + return 0 diff --git a/code/modules/mob/living/living-defense.dm b/code/modules/mob/living/living-defense.dm index 3bdb111c5bc8..bce917a898b5 100644 --- a/code/modules/mob/living/living-defense.dm +++ b/code/modules/mob/living/living-defense.dm @@ -1,5 +1,115 @@ //* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2024 Citadel Station developers. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +//* Projectile Handling *// + +/mob/living/bullet_act(obj/projectile/proj, impact_flags, def_zone, efficiency) + //! LEGACY + + // Using someone as a shield + // todo: need a counter to this.. + for(var/mob/living/victim in get_grabbing_of_state(GRAB_NECK)) + if(victim.stat == DEAD) + // small mobs are penalized; this is a holdover. + var/shield_chance = min(80, (30 * (mob_size / 10))) + if(prob(shield_chance)) + visible_message("\The [src] uses [victim] as a shield!") + if(!(proj.impact_redirect(victim, args) | (PROJECTILE_IMPACT_FLAGS_SHOULD_GO_THROUGH | PROJECTILE_IMPACT_DUPLICATE))) + return + else + visible_message("\The [src] tries to use [victim] as a shield, but fails!") + else + visible_message("\The [src] uses [victim] as a shield!") + if(!(proj.impact_redirect(victim, args) | (PROJECTILE_IMPACT_FLAGS_SHOULD_GO_THROUGH | PROJECTILE_IMPACT_DUPLICATE))) + return + // Process baymiss & zonemiss + def_zone = process_bullet_miss(proj, impact_flags, def_zone, efficiency) + def_zone = proj.process_zone_miss(src, def_zone, proj.distance_travelled, TRUE) + if(!def_zone) + if(!proj.silenced) + visible_message(SPAN_WARNING("\The [proj] misses [src] narrowly!")) + playsound(src, pick(proj.miss_sounds), 60, TRUE) + add_attack_logs( + proj.firer, + src, + "shot with [src] ([type]) (missed)", + ) + impact_flags |= PROJECTILE_IMPACT_PASSTHROUGH + return ..() + + //! END + + return ..() + +/mob/living/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + // todo: better logging + if(impact_flags & PROJECTILE_IMPACT_FLAGS_TARGET_ABORT) + add_attack_logs( + proj.firer, + src, + "shot with [src] ([type]) (aborted)", + ) + return + add_attack_logs( + proj.firer, + src, + "shot with [src] ([type])[(impact_flags & PROJECTILE_IMPACT_BLOCKED)? " (blocked)" : ""]", + ) + // emit feedback + if(!(impact_flags & PROJECTILE_IMPACT_BLOCKED)) + if(proj.silenced) + to_chat(src, SPAN_DANGER("You've been hit in the [parse_zone(bullet_act_args[BULLET_ACT_ARG_ZONE])] with \the [proj]!")) + else + visible_message(SPAN_DANGER("\The [src] is hit by [proj] in the [parse_zone(bullet_act_args[BULLET_ACT_ARG_ZONE])]")) + + //! LEGACY + + //Being hit while using a deadman switch + for(var/obj/item/assembly/signaler/signaler in get_held_items()) + if(signaler.deadman && prob(80)) + log_and_message_admins("has triggered a signaler deadman's switch") + visible_message("[src] triggers their deadman's switch!") + signaler.signal() + + if(ai_holder && proj.firer) + ai_holder.react_to_attack_polaris(proj.firer) + + //! END + + if(!(impact_flags & (PROJECTILE_IMPACT_BLOCKED | PROJECTILE_IMPACT_SKIP_STANDARD_DAMAGE))) + // todo: this should just be in base projectile on_impact + impact_flags |= proj.inflict_impact_damage(src, bullet_act_args[BULLET_ACT_ARG_EFFICIENCY], impact_flags, bullet_act_args[BULLET_ACT_ARG_ZONE]) + return ..() + +/mob/living/get_bullet_impact_effect_type(var/def_zone) + return BULLET_IMPACT_MEAT + +/** + * @return zone to hit, or null to miss + */ +/mob/living/proc/process_bullet_miss(obj/projectile/proj, impact_flags, def_zone, efficiency) + var/hit_probability = process_baymiss(proj) + if(!prob(hit_probability)) + return null + return def_zone + +/** + * * our_opinion is intentionally mutable; it is however only mutable from before ..(), so call ..() after modifying for pre-modification + * * our_opinion and impact_check are defaulted in the base function; this means that if you need to use it before, default it yourself. + * + * todo: 0 to 100 for accuracy might not be amazing; maybe allow negative values evasion-style? + * todo: don't default our_opinion and impact_check so early wtf; BYOND proc structure disagrees with the design here. + * + * @params + * * proj - the projectile + * * our_opinion - base probability of hitting + * * impact_check - are we checking if we should impact at all? used by pellets. + * + * @return 0 to 100 % probability of hitting + */ +/mob/living/proc/process_baymiss(obj/projectile/proj, our_opinion = 100, impact_check = TRUE) + our_opinion = clamp(our_opinion - get_evasion(), 5, INFINITY) + return proj.process_accuracy(src, our_opinion, null, impact_check) //* Misc Effects *// @@ -20,3 +130,5 @@ */ /mob/living/proc/slip_act(slip_class, source, hard_strength, soft_strength, suppressed) return 1 +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station developers. *// diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 9c4ea9a0aecf..d141d84f0076 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -533,11 +533,13 @@ /mob/living/silicon/robot/restrained() return 0 -/mob/living/silicon/robot/bullet_act(var/obj/projectile/Proj) - ..(Proj) - if(prob(75) && Proj.damage > 0) +/mob/living/silicon/robot/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + // todo: why is this in bullet act and not where we take damage maybe? + if(prob(75) && proj.damage > 0) spark_system.start() - return 2 /mob/living/silicon/robot/attackby(obj/item/W as obj, mob/user as mob) if (istype(W, /obj/item/handcuffs)) // fuck i don't even know why isrobot() in handcuff code isn't working so this will have to do diff --git a/code/modules/mob/living/silicon/robot/robot_modules/station/misc.dm b/code/modules/mob/living/silicon/robot/robot_modules/station/misc.dm index 1d21b94abe8a..bffbbf6c866f 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules/station/misc.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules/station/misc.dm @@ -37,7 +37,7 @@ /obj/item/robot_module/robot/standard/handle_special_module_init(mob/living/silicon/robot/R) . = ..() - src.emag = new /obj/item/melee/energy/sword(src) + src.emag = new /obj/item/melee/transforming/energy/sword(src) /obj/item/robot_module/robot/quad/basic name = "Standard Quadruped module" @@ -59,4 +59,4 @@ . = ..() // These get a larger water synth. synths_by_kind[MATSYN_WATER]:max_energy = 1000 - src.emag = new /obj/item/melee/energy/sword(src) + src.emag = new /obj/item/melee/transforming/energy/sword(src) diff --git a/code/modules/mob/living/silicon/robot/robot_modules/swarm.dm b/code/modules/mob/living/silicon/robot/robot_modules/swarm.dm index 543a007c2d6d..5012c5031a35 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules/swarm.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules/swarm.dm @@ -27,4 +27,4 @@ /obj/item/robot_module/drone/swarm/melee/get_modules() . = ..() - . |= /obj/item/melee/energy/sword/ionic_rapier/lance + . |= /obj/item/melee/transforming/energy/sword/ionic_rapier/lance diff --git a/code/modules/mob/living/silicon/robot/robot_modules/syndicate.dm b/code/modules/mob/living/silicon/robot/robot_modules/syndicate.dm index 02c593c5337c..fcb77a94c8f2 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules/syndicate.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules/syndicate.dm @@ -35,7 +35,7 @@ . = ..() . |= list( /obj/item/pinpointer/shuttle/merc, - /obj/item/melee/energy/sword + /obj/item/melee/transforming/energy/sword ) /obj/item/robot_module/robot/syndicate/handle_special_module_init(mob/living/silicon/robot/R) @@ -90,7 +90,7 @@ /obj/item/multitool/ai_detector, /obj/item/pickaxe/plasmacutter, /obj/item/rcd/electric/mounted/borg/lesser, // Can't eat rwalls to prevent AI core cheese. - /obj/item/melee/energy/sword/ionic_rapier, + /obj/item/melee/transforming/energy/sword/ionic_rapier, // FBP repair. /obj/item/robotanalyzer, diff --git a/code/modules/mob/living/silicon/silicon-damage.dm b/code/modules/mob/living/silicon/silicon-damage.dm new file mode 100644 index 000000000000..d22faa575803 --- /dev/null +++ b/code/modules/mob/living/silicon/silicon-damage.dm @@ -0,0 +1,14 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +//* Damage Instance Handling *// + +/mob/living/silicon/inflict_damage_instance(damage, damage_type, damage_tier, damage_flag, damage_mode, attack_type, datum/weapon, shieldcall_flags, hit_zone, list/additional, datum/event_args/actor/clickchain/clickchain) + if(inflict_damage_type_special(args)) + return + // we only care about those + switch(damage_type) + if(BRUTE) + adjustBruteLoss(damage) + if(BURN) + adjustBruteLoss(damage) diff --git a/code/modules/mob/living/silicon/silicon-defense-legacy.dm b/code/modules/mob/living/silicon/silicon-defense-legacy.dm new file mode 100644 index 000000000000..bf80122caf1f --- /dev/null +++ b/code/modules/mob/living/silicon/silicon-defense-legacy.dm @@ -0,0 +1,57 @@ +/mob/living/silicon/emp_act(severity) + switch(severity) + if(1) + src.take_random_targeted_damage(brute = 0, burn = 20, damage_mode = DAMAGE_MODE_INTERNAL, weapon_descriptor = "electromagnetic surge") + Confuse(5) + if(2) + src.take_random_targeted_damage(brute = 0, burn = 15, damage_mode = DAMAGE_MODE_INTERNAL, weapon_descriptor = "electromagnetic surge") + Confuse(4) + if(3) + src.take_random_targeted_damage(brute = 0, burn = 10, damage_mode = DAMAGE_MODE_INTERNAL, weapon_descriptor = "electromagnetic surge") + Confuse(3) + if(4) + src.take_random_targeted_damage(brute = 0, burn = 5, damage_mode = DAMAGE_MODE_INTERNAL, weapon_descriptor = "electromagnetic surge") + Confuse(2) + flash_eyes(affect_silicon = 1) + to_chat(src, "*BZZZT*") + to_chat(src, "Warning: Electromagnetic pulse detected.") + ..() + +/mob/living/silicon/stun_effect_act(var/stun_amount, var/agony_amount, var/def_zone, var/used_weapon=null) + return //immune + +/mob/living/silicon/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0, var/def_zone = null, var/stun = 1) + if(shock_damage > 0) + var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread + s.set_up(5, 1, loc) + s.start() + + shock_damage *= siemens_coeff //take reduced damage + take_overall_damage(0, shock_damage) + visible_message("[src] was shocked by \the [source]!", \ + "Energy pulse detected, system damaged!", \ + "You hear an electrical crack.") + if(prob(20)) + afflict_stun(20 * 2) + return + +/mob/living/silicon/legacy_ex_act(severity) + if(!has_status_effect(/datum/status_effect/sight/blindness)) + flash_eyes() + + switch(severity) + if(1.0) + if (stat != 2) + adjustBruteLoss(100) + adjustFireLoss(100) + if(!anchored) + gib() + if(2.0) + if (stat != 2) + adjustBruteLoss(60) + adjustFireLoss(60) + if(3.0) + if (stat != 2) + adjustBruteLoss(30) + + update_health() diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 479e26ccf902..0f0a42b46378 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -112,62 +112,9 @@ else src.bodytemp.icon_state = "temp-2" -/mob/living/silicon/emp_act(severity) - switch(severity) - if(1) - src.take_random_targeted_damage(brute = 0, burn = 20, damage_mode = DAMAGE_MODE_INTERNAL, weapon_descriptor = "electromagnetic surge") - Confuse(5) - if(2) - src.take_random_targeted_damage(brute = 0, burn = 15, damage_mode = DAMAGE_MODE_INTERNAL, weapon_descriptor = "electromagnetic surge") - Confuse(4) - if(3) - src.take_random_targeted_damage(brute = 0, burn = 10, damage_mode = DAMAGE_MODE_INTERNAL, weapon_descriptor = "electromagnetic surge") - Confuse(3) - if(4) - src.take_random_targeted_damage(brute = 0, burn = 5, damage_mode = DAMAGE_MODE_INTERNAL, weapon_descriptor = "electromagnetic surge") - Confuse(2) - flash_eyes(affect_silicon = 1) - to_chat(src, "*BZZZT*") - to_chat(src, "Warning: Electromagnetic pulse detected.") - ..() - -/mob/living/silicon/stun_effect_act(var/stun_amount, var/agony_amount, var/def_zone, var/used_weapon=null) - return //immune - -/mob/living/silicon/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0, var/def_zone = null, var/stun = 1) - if(shock_damage > 0) - var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(5, 1, loc) - s.start() - - shock_damage *= siemens_coeff //take reduced damage - take_overall_damage(0, shock_damage) - visible_message("[src] was shocked by \the [source]!", \ - "Energy pulse detected, system damaged!", \ - "You hear an electrical crack.") - if(prob(20)) - afflict_stun(20 * 2) - return - -/mob/living/silicon/proc/damage_mob(var/brute = 0, var/fire = 0, var/tox = 0) - return - /mob/living/silicon/IsAdvancedToolUser() return 1 -/mob/living/silicon/bullet_act(var/obj/projectile/Proj) - - if(!Proj.nodamage) - switch(Proj.damage_type) - if(BRUTE) - adjustBruteLoss(Proj.get_final_damage(src)) - if(BURN) - adjustFireLoss(Proj.get_final_damage(src)) - - Proj.on_hit(src,2) - update_health() - return 2 - /mob/living/silicon/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0, var/check_protection = 1) return 0//The only effect that can hit them atm is flashes and they still directly edit so this works for now @@ -300,27 +247,6 @@ /mob/living/silicon/binarycheck() return 1 -/mob/living/silicon/legacy_ex_act(severity) - if(!has_status_effect(/datum/status_effect/sight/blindness)) - flash_eyes() - - switch(severity) - if(1.0) - if (stat != 2) - adjustBruteLoss(100) - adjustFireLoss(100) - if(!anchored) - gib() - if(2.0) - if (stat != 2) - adjustBruteLoss(60) - adjustFireLoss(60) - if(3.0) - if (stat != 2) - adjustBruteLoss(30) - - update_health() - /mob/living/silicon/proc/receive_alarm(var/datum/alarm_handler/alarm_handler, var/datum/alarm/alarm, was_raised) if(!next_alarm_notice) next_alarm_notice = world.time + SecondsToTicks(10) diff --git a/code/modules/mob/living/simple_animal/constructs/constructs.dm b/code/modules/mob/living/simple_animal/constructs/constructs.dm index 5f2b43820182..32e833179976 100644 --- a/code/modules/mob/living/simple_animal/constructs/constructs.dm +++ b/code/modules/mob/living/simple_animal/constructs/constructs.dm @@ -210,7 +210,7 @@ weakened = 0 ..() -/mob/living/simple_animal/construct/armoured/bullet_act(var/obj/projectile/P) +/mob/living/simple_animal/construct/armoured/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) // if(istype(P, /obj/projectile/energy) || istype(P, /obj/projectile/beam)) //If it's going to be slow, it's probably going to need every reflect it can get. var/reflectchance = 80 - round(P.damage/3) if(prob(reflectchance)) @@ -364,7 +364,7 @@ /spell/targeted/construct_advanced/slam ) -/mob/living/simple_animal/construct/behemoth/bullet_act(var/obj/projectile/P) +/mob/living/simple_animal/construct/behemoth/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) var/reflectchance = 80 - round(P.damage/3) if(prob(reflectchance)) visible_message("The [P.name] gets reflected by [src]'s shell!", \ diff --git a/code/modules/mob/living/simple_mob/combat.dm b/code/modules/mob/living/simple_mob/combat.dm index 9ac88e373eea..e226cbbfded4 100644 --- a/code/modules/mob/living/simple_mob/combat.dm +++ b/code/modules/mob/living/simple_mob/combat.dm @@ -62,10 +62,10 @@ playsound(src, 'sound/weapons/punchmiss.ogg', 75, 1) return FALSE // We missed. - if(ishuman(L)) - var/mob/living/carbon/human/H = L - if(H.check_shields(damage = damage_to_do, damage_source = src, attacker = src, def_zone = null, attack_text = "the attack")) - return FALSE // We were blocked. + var/datum/event_args/actor/clickchain/simulated_clickchain = new(src, target = L) + var/list/shieldcall_result = L.atom_shieldcall(damage_to_do, BRUTE, MELEE_TIER_MEDIUM, ARMOR_MELEE, NONE, ATTACK_TYPE_MELEE, clickchain = simulated_clickchain) + if(shieldcall_result[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAGS_BLOCK_ATTACK) + return FALSE if(apply_attack(A, damage_to_do)) apply_melee_effects(A) @@ -138,7 +138,7 @@ // For some reason there isn't an argument for accuracy, so access the projectile directly instead. // Also, placing dispersion here instead of in forced_spread will randomize the chosen angle between dispersion and -dispersion in fire() instead of having to do that here. - P.accuracy += calculate_accuracy() + P.accuracy_overall_modify *= 1 + calculate_accuracy() / 100 P.dispersion += calculate_dispersion() P.launch_projectile(target = A, target_zone = null, user = src, params = null, angle_override = null, forced_spread = 0) diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/hunter.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/hunter.dm index 6161a011dda2..9589a72493c2 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/hunter.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/hunter.dm @@ -87,10 +87,9 @@ if(L == src) continue - if(ishuman(L)) - var/mob/living/carbon/human/H = L - if(H.check_shields(damage = 0, damage_source = src, attacker = src, def_zone = null, attack_text = "the leap")) - continue // We were blocked. + var/list/shieldcall_result = L.atom_shieldcall(40, BRUTE, MELEE_TIER_MEDIUM, ARMOR_MELEE, NONE, ATTACK_TYPE_MELEE) + if(shieldcall_result[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAGS_BLOCK_ATTACK) + continue victim = L break diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/lurker.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/lurker.dm index 237bf4f729fd..00cdeb796809 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/lurker.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/lurker.dm @@ -116,7 +116,7 @@ ..() // For the poison. // Force unstealthing if attacked. -/mob/living/simple_mob/animal/giant_spider/lurker/bullet_act(obj/projectile/P) +/mob/living/simple_mob/animal/giant_spider/lurker/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) . = ..() break_cloak() diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/roach/roach.dm b/code/modules/mob/living/simple_mob/subtypes/animal/roach/roach.dm index 98d4713d3de1..baeb4b6a8c18 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/roach/roach.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/roach/roach.dm @@ -537,7 +537,7 @@ ..() // For the poison. // Force unstealthing if attacked. -/mob/living/simple_mob/animal/roach/zeitraum/bullet_act(obj/projectile/P) +/mob/living/simple_mob/animal/roach/zeitraum/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) . = ..() break_cloak() diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/mouse_army.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/mouse_army.dm index 57d8d7963a95..f76c3a8289a1 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/mouse_army.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/mouse_army.dm @@ -417,8 +417,10 @@ ..() // For the poison. // Force unstealthing if attacked. -/mob/living/simple_mob/animal/space/mouse_army/stealth/bullet_act(obj/projectile/P) +/mob/living/simple_mob/animal/space/mouse_army/stealth/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return break_cloak() /mob/living/simple_mob/animal/space/mouse_army/stealth/hit_with_weapon(obj/item/O, mob/living/user, effective_force, hit_zone) diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Eddy.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Eddy.dm index 592bba73f355..af439b298ee2 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Eddy.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Eddy.dm @@ -48,9 +48,9 @@ playsound(src, 'sound/h_sounds/headcrab.ogg', 50, 1) ..() -/mob/living/simple_mob/horror/Eddy/bullet_act() +/mob/living/simple_mob/horror/Eddy/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) - ..() /mob/living/simple_mob/horror/Eddy/attack_hand(mob/user, list/params) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Master.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Master.dm index e17dcaf7b3c3..d49a957e3d42 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Master.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Master.dm @@ -49,9 +49,9 @@ playsound(src, 'sound/h_sounds/imbeciles.ogg', 50, 1) ..() -/mob/living/simple_mob/horror/Master/bullet_act() +/mob/living/simple_mob/horror/Master/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) - ..() /mob/living/simple_mob/horror/Master/attack_hand(mob/user, list/params) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Rickey.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Rickey.dm index bd5b19c6b2e3..c949dafb96e2 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Rickey.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Rickey.dm @@ -50,9 +50,9 @@ playsound(src, 'sound/h_sounds/headcrab.ogg', 50, 1) ..() -/mob/living/simple_mob/horror/Rickey/bullet_act() +/mob/living/simple_mob/horror/Rickey/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) - ..() /mob/living/simple_mob/horror/Rickey/attack_hand(mob/user, list/params) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Smiley.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Smiley.dm index 4a33ac456be5..c5f532332c85 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Smiley.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Smiley.dm @@ -49,9 +49,9 @@ playsound(src, 'sound/h_sounds/lynx.ogg', 50, 1) ..() -/mob/living/simple_mob/horror/Helix/bullet_act() +/mob/living/simple_mob/horror/Helix/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) - ..() /mob/living/simple_mob/horror/Helix/attack_hand(mob/user, list/params) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Steve.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Steve.dm index eb80a126aa91..507441749a72 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Steve.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Steve.dm @@ -54,9 +54,9 @@ playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) ..() -/mob/living/simple_mob/horror/Steve/bullet_act() +/mob/living/simple_mob/horror/Steve/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) - ..() /mob/living/simple_mob/horror/Steve/attack_hand(mob/user, list/params) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Willy.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Willy.dm index 35fc24509ee5..8e7fe09304d5 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Willy.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Willy.dm @@ -50,9 +50,9 @@ playsound(src, 'sound/h_sounds/sampler.ogg', 50, 1) ..() -/mob/living/simple_mob/horror/Willy/bullet_act() +/mob/living/simple_mob/horror/Willy/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) - ..() /mob/living/simple_mob/horror/Willy/attack_hand(mob/user, list/params) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/bradley.dm b/code/modules/mob/living/simple_mob/subtypes/horror/bradley.dm index 5dce24cb5faa..e10d0e557053 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/bradley.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/bradley.dm @@ -48,9 +48,9 @@ playsound(src, 'sound/h_sounds/mumble.ogg', 50, 1) ..() -/mob/living/simple_mob/horror/bradley/bullet_act() +/mob/living/simple_mob/horror/bradley/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) - ..() /mob/living/simple_mob/horror/bradley/attack_hand(mob/user, list/params) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/sally.dm b/code/modules/mob/living/simple_mob/subtypes/horror/sally.dm index 0d4af8cfc580..b051923756db 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/sally.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/sally.dm @@ -47,9 +47,9 @@ playsound(src, 'sound/h_sounds/lynx.ogg', 50, 1) ..() -/mob/living/simple_mob/horror/Sally/bullet_act() +/mob/living/simple_mob/horror/Sally/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) - ..() /mob/living/simple_mob/horror/Sally/attack_hand(mob/user, list/params) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/shittytim.dm b/code/modules/mob/living/simple_mob/subtypes/horror/shittytim.dm index 8667b99e7321..2c051b4d6301 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/shittytim.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/shittytim.dm @@ -48,9 +48,9 @@ playsound(src, 'sound/h_sounds/shitty_tim.ogg', 50, 1) ..() -/mob/living/simple_mob/horror/BigTim/bullet_act() +/mob/living/simple_mob/horror/BigTim/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) - ..() /mob/living/simple_mob/horror/BigTim/attack_hand(mob/user, list/params) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/timling.dm b/code/modules/mob/living/simple_mob/subtypes/horror/timling.dm index f6c26f137cf4..60fe08909b2a 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/timling.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/timling.dm @@ -48,9 +48,9 @@ playsound(src, 'sound/h_sounds/shitty_tim.ogg', 50, 1) ..() -/mob/living/simple_mob/horror/TinyTim/bullet_act() +/mob/living/simple_mob/horror/TinyTim/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) - ..() /mob/living/simple_mob/horror/TinyTim/attack_hand(mob/user, list/params) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/cultist.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/cultist.dm index fd903d2671f3..bf0caab9bcbd 100644 --- a/code/modules/mob/living/simple_mob/subtypes/humanoid/cultist.dm +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/cultist.dm @@ -508,17 +508,15 @@ to_chat(user, "This weapon is ineffective, it does no damage.") visible_message("\The [user] gently taps [src] with \the [O].") -/mob/living/simple_mob/humanoid/cultist/elite/bullet_act(var/obj/projectile/Proj) - if(!Proj) return +/mob/living/simple_mob/humanoid/cultist/elite/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(prob(50)) - visible_message("[Proj] disappears into the mirror world as it hits the shield.") - if(Proj.firer) + visible_message("[proj] disappears into the mirror world as it hits the shield.") + if(proj.firer) if(istype(src.ai_holder, /datum/ai_holder/polaris)) var/datum/ai_holder/polaris/ai_holder = src.ai_holder - ai_holder.react_to_attack_polaris(Proj.firer) - return - else - ..() + ai_holder.react_to_attack_polaris(proj.firer) + return PROJECTILE_IMPACT_DELETE + return ..() /mob/living/simple_mob/humanoid/cultist/elite/death() new /obj/effect/decal/remains/human (src.loc) diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs.dm index 59acf04dd4dc..344444ff8224 100644 --- a/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs.dm +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs.dm @@ -161,7 +161,7 @@ attack_edge = 1 attacktext = list("slashed") - loot_list = list(/obj/item/melee/energy/sword = 100, /obj/item/shield/energy = 100) + loot_list = list(/obj/item/melee/transforming/energy/sword = 100, /obj/item/shield/transforming/energy = 100) // They have a shield, so they try to block /mob/living/simple_mob/humanoid/merc/melee/sword/attackby(var/obj/item/O as obj, var/mob/user as mob) @@ -177,16 +177,13 @@ to_chat(user, "This weapon is ineffective, it does no damage.") visible_message("\The [user] gently taps [src] with \the [O].") -/mob/living/simple_mob/humanoid/merc/melee/sword/bullet_act(var/obj/projectile/Proj) - if(!Proj) return +/mob/living/simple_mob/humanoid/merc/melee/sword/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(prob(35)) - visible_message("[src] blocks [Proj] with its shield!") - if(Proj.firer) - ai_holder.react_to_attack_polaris(Proj.firer) - return - else - ..() - + visible_message("[src] blocks [proj] with its shield!") + if(proj.firer) + ai_holder.react_to_attack_polaris(proj.firer) + return PROJECTILE_IMPACT_BLOCKED + return ..() //////////////////////////////// // Ranged @@ -553,15 +550,13 @@ else visible_message("\The [user] gently taps [src] with \the [O].") -/mob/living/simple_mob/humanoid/merc/ranged/space/suppressor/bullet_act(var/obj/projectile/Proj) - if(!Proj) return +/mob/living/simple_mob/humanoid/merc/ranged/space/suppressor/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(prob(50)) - visible_message("[src] blocks [Proj] with its shield!") - if(Proj.firer) - ai_holder.react_to_attack_polaris(Proj.firer) - return - else - ..() + visible_message("[src] blocks [proj] with its shield!") + if(proj.firer) + ai_holder.react_to_attack_polaris(proj.firer) + return PROJECTILE_IMPACT_BLOCKED + return ..() //////////////////////////////// // PoI Mercs @@ -683,7 +678,7 @@ ai_holder_type = /datum/ai_holder/polaris/simple_mob/melee/evasive corpse = /obj/spawner/corpse/vox/boarder_m - loot_list = list(/obj/item/melee/energy/sword = 100) + loot_list = list(/obj/item/melee/transforming/energy/sword = 100) // They're good with the swords? I dunno. I like the idea they can deflect. /mob/living/simple_mob/humanoid/merc/voxpirate/boarder/attackby(var/obj/item/O, var/mob/user) @@ -699,15 +694,13 @@ to_chat(user, "This weapon is ineffective, it does no damage.") visible_message("\The [user] gently taps [src] with \the [O].") -/mob/living/simple_mob/humanoid/merc/voxpirate/boarder/bullet_act(var/obj/projectile/Proj) - if(!Proj) return +/mob/living/simple_mob/humanoid/merc/voxpirate/boarder/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(prob(35)) - visible_message("[src] blocks [Proj] with its sword!") - if(Proj.firer) - ai_holder.react_to_attack_polaris(Proj.firer) - return - else - ..() + visible_message("[src] blocks [proj] with its sword!") + if(proj.firer) + ai_holder.react_to_attack_polaris(proj.firer) + return PROJECTILE_IMPACT_BLOCKED + return ..() //////////////////////////////// // Vox Ranged diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/pirates.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/pirates.dm index d7716b6f4471..640560545917 100644 --- a/code/modules/mob/living/simple_mob/subtypes/humanoid/pirates.dm +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/pirates.dm @@ -122,7 +122,7 @@ attack_sound = 'sound/weapons/blade1.ogg' - loot_list = list(/obj/item/melee/energy/sword/pirate = 100) + loot_list = list(/obj/item/melee/transforming/energy/sword/cutlass = 100) corpse = /obj/spawner/corpse/pirate/melee_energy @@ -134,7 +134,7 @@ icon_living = "piratemelee-las-armor" movement_cooldown = 4 armor_legacy_mob = list(melee = 30, bullet = 20, laser = 20, energy = 5, bomb = 5, bio = 100, rad = 100) - loot_list = list(/obj/item/melee/energy/sword/pirate = 100, /obj/item/clothing/accessory/armor/armorplate/stab = 100) + loot_list = list(/obj/item/melee/transforming/energy/sword/cutlass = 100, /obj/item/clothing/accessory/armor/armorplate/stab = 100) corpse = /obj/spawner/corpse/pirate/melee_energy_armor @@ -164,15 +164,13 @@ to_chat(user, "This weapon is ineffective, it does no damage.") visible_message("\The [user] gently taps [src] with \the [O].") -/mob/living/simple_mob/humanoid/merc/melee/sword/bullet_act(var/obj/projectile/Proj) - if(!Proj) return +/mob/living/simple_mob/humanoid/merc/melee/sword/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(prob(25)) - visible_message("[src] blocks [Proj] with its shield!") - if(Proj.firer) - ai_holder.react_to_attack_polaris(Proj.firer) - return - else - ..() + visible_message("[src] blocks [proj] with its shield!") + if(proj.firer) + ai_holder.react_to_attack_polaris(proj.firer) + return PROJECTILE_IMPACT_BLOCKED + return ..() // Armored Variant /mob/living/simple_mob/humanoid/pirate/shield/armored @@ -352,7 +350,7 @@ armor_legacy_mob = list(melee = 30, bullet = 20, laser = 20, energy = 5, bomb = 5, bio = 100, rad = 100) - loot_list = list(/obj/item/melee/energy/sword/pirate = 100, /obj/item/clothing/suit/armor/riot/alt = 100) + loot_list = list(/obj/item/melee/transforming/energy/sword/cutlass = 100, /obj/item/clothing/suit/armor/riot/alt = 100) corpse = /obj/spawner/corpse/pirate/mate @@ -526,13 +524,13 @@ icon_state = "old-piratemelee-las" icon_living = "old-piratemelee-las" icon_dead = "old-piratemelee_dead" - loot_list = list(/obj/item/melee/energy/sword/pirate = 100) + loot_list = list(/obj/item/melee/transforming/energy/sword/cutlass = 100) //Armored Variant /mob/living/simple_mob/humanoid/pirate/las/armored/old icon_state = "old-piratemelee-las-armor" icon_living = "old-piratemelee-las-armor" - loot_list = list(/obj/item/melee/energy/sword/pirate = 100, /obj/item/clothing/suit/armor/material/makeshift = 100) + loot_list = list(/obj/item/melee/transforming/energy/sword/cutlass = 100, /obj/item/clothing/suit/armor/material/makeshift = 100) //Shield Pirate /mob/living/simple_mob/humanoid/pirate/shield/old @@ -653,7 +651,7 @@ armor_legacy_mob = list(melee = 30, bullet = 20, laser = 20, energy = 5, bomb = 5, bio = 100, rad = 100) - loot_list = list(/obj/item/melee/energy/sword/pirate = 100, /obj/item/clothing/suit/pirate = 100) + loot_list = list(/obj/item/melee/transforming/energy/sword/cutlass = 100, /obj/item/clothing/suit/pirate = 100) /////////////////////////////// diff --git a/code/modules/mob/living/simple_mob/subtypes/illusion/illusion.dm b/code/modules/mob/living/simple_mob/subtypes/illusion/illusion.dm index b724d80556b8..9e81d5932b36 100644 --- a/code/modules/mob/living/simple_mob/subtypes/illusion/illusion.dm +++ b/code/modules/mob/living/simple_mob/subtypes/illusion/illusion.dm @@ -45,14 +45,10 @@ return -/mob/living/simple_mob/illusion/bullet_act(obj/projectile/P) - if(!P) - return - +/mob/living/simple_mob/illusion/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(realistic) return ..() - - return PROJECTILE_FORCE_MISS + return PROJECTILE_IMPACT_PHASE /mob/living/simple_mob/illusion/attack_hand(mob/user, list/params) var/mob/living/M = user diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/cyber_horror/cyber_horror.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/cyber_horror/cyber_horror.dm index c61bba406f0f..1f2841dc4cd9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/cyber_horror/cyber_horror.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/cyber_horror/cyber_horror.dm @@ -182,11 +182,9 @@ if(L == src) continue - if(ishuman(L)) - var/mob/living/carbon/human/H = L - if(H.check_shields(damage = 0, damage_source = src, attacker = src, def_zone = null, attack_text = "the leap")) - // We were blocked. - continue + var/list/shieldcall_result = L.atom_shieldcall(40, BRUTE, MELEE_TIER_MEDIUM, ARMOR_MELEE, NONE, ATTACK_TYPE_MELEE) + if(shieldcall_result[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAGS_BLOCK_ATTACK) + continue victim = L break @@ -299,7 +297,7 @@ ..() // For the poison. // Force unstealthing if attacked. -/mob/living/simple_mob/mechanical/cyber_horror/tajaran/bullet_act(obj/projectile/P) +/mob/living/simple_mob/mechanical/cyber_horror/tajaran/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) . = ..() break_cloak() @@ -435,7 +433,10 @@ damage = 15 damage_type = BRUTE -/obj/projectile/arc/blue_energy/priest/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/arc/blue_energy/priest/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(ishuman(target)) var/mob/living/carbon/human/M = target M.Confuse(rand(3,5)) @@ -500,8 +501,11 @@ icon_state = "plasma3" rad_power = RAD_INTENSITY_PROJ_ARC_HORROR_PRIEST -/obj/projectile/arc/radioactive/priest/on_impact(turf/T) +/obj/projectile/arc/radioactive/priest/on_impact(atom/target, impact_flags, def_zone, efficiency) . = ..() + if(!isturf(target)) + return + var/turf/T = target new /obj/effect/explosion(T) explosion(T, 0, 1, 4) diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm index 0f3ce3a23194..ba2be85fb94c 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm @@ -169,15 +169,23 @@ /obj/projectile/arc/emp_blast name = "emp blast" icon_state = "bluespace" - -/obj/projectile/arc/emp_blast/on_impact(turf/T) - empulse(T, 2, 4, 7, 10) // Normal EMP grenade. - return ..() - -/obj/projectile/arc/emp_blast/weak/on_impact(turf/T) - empulse(T, 1, 2, 3, 4) // Sec EMP grenade. - return ..() - + var/emp_dev = 2 + var/emp_heavy = 4 + var/emp_med = 7 + var/emp_light = 10 + +/obj/projectile/arc/emp_blast/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + empulse(target, emp_dev, emp_heavy, emp_med, emp_light) // Normal EMP grenade. + return . | PROJECTILE_IMPACT_DELETE + +/obj/projectile/arc/emp_blast/weak + emp_dev = 1 + emp_heavy = 2 + emp_med = 3 + emp_light = 4 // Fires shots that irradiate the tile hit. /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/radiation diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm index 7c5cef5f8a12..478bc8fd8720 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm @@ -227,7 +227,11 @@ name = "rocket" icon_state = "mortar" -/obj/projectile/arc/explosive_rocket/on_impact(turf/T) +/obj/projectile/arc/explosive_rocket/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(!isturf(target)) + return + var/turf/T = target new /obj/effect/explosion(T) // Weak explosions don't produce this on their own, apparently. explosion(T, 0, 0, 2, adminlog = FALSE) @@ -244,10 +248,13 @@ name = "micro singularity" icon_state = "bluespace" -/obj/projectile/arc/microsingulo/on_impact(turf/T) +/obj/projectile/arc/microsingulo/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(!isturf(target)) + return + var/turf/T = target new /obj/effect/temporary_effect/pulse/microsingulo(T) - /obj/effect/temporary_effect/pulse/microsingulo name = "micro singularity" desc = "It's sucking everything in!" diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/mecha.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/mecha.dm index 53e146c324fa..3ec87c9c34f2 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/mecha.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/mecha.dm @@ -90,7 +90,7 @@ if(has_repair_droid) add_overlay(image(icon = 'icons/mecha/mecha_equipment.dmi', icon_state = "repair_droid")) -/mob/living/simple_mob/mechanical/mecha/bullet_act() +/mob/living/simple_mob/mechanical/mecha/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) . = ..() sparks.start() @@ -111,11 +111,11 @@ return ..() */ -/mob/living/simple_mob/mechanical/mecha/bullet_act(obj/projectile/P) +/mob/living/simple_mob/mechanical/mecha/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(prob(deflect_chance)) - visible_message(SPAN_WARNING( "\The [P] is deflected by \the [src]'s armor!")) + visible_message(SPAN_WARNING( "\The [proj] is deflected by \the [src]'s armor!")) deflect_sprite() - return 0 + return PROJECTILE_IMPACT_BLOCKED return ..() /mob/living/simple_mob/mechanical/mecha/proc/deflect_sprite() diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/odysseus.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/odysseus.dm index bc6b49659e4c..9781a4618770 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/odysseus.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/odysseus.dm @@ -67,14 +67,13 @@ damage = 5 // Getting hit with a launched syringe probably hurts, and makes it at least slightly relevant against synthetics. var/piercing = FALSE // If true, ignores thick material. -/obj/projectile/fake_syringe/on_hit(atom/target, blocked = 0, def_zone = null) +/obj/projectile/fake_syringe/on_impact(atom/target, impact_flags, def_zone, efficiency) if(isliving(target)) var/mob/living/L = target if(!L.can_inject(null, null, def_zone, piercing)) - return FALSE + return impact_flags | PROJECTILE_IMPACT_BLOCKED L.custom_pain(SPAN_WARNING("You feel a tiny prick!"), 1, TRUE) - return ..() // This will add the modifier and return the correct value. - + return ..() // Fake syringe, which inflicts a long lasting modifier that slowly kills them. /obj/projectile/fake_syringe/poison diff --git a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/juggernaut.dm b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/juggernaut.dm index 172854fd9d55..56ddbc516b3e 100644 --- a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/juggernaut.dm +++ b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/juggernaut.dm @@ -63,46 +63,45 @@ . = ..() AddComponent(/datum/component/horror_aura/strong) -/mob/living/simple_mob/construct/juggernaut/bullet_act(var/obj/projectile/P) - var/reflectchance = 80 - round(P.damage/3) - if(prob(reflectchance)) +/mob/living/simple_mob/construct/juggernaut/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + var/reflectchance = 80 - round(proj.damage/3) + if(prob(reflectchance) && !istype(src, /mob/living/simple_mob/construct/juggernaut/behemoth)) var/damage_mod = rand(2,4) - var/projectile_dam_type = P.damage_type - var/incoming_damage = (round(P.damage / damage_mod) - (round((P.damage / damage_mod) * 0.3))) - var/armorcheck = run_armor_check(null, P.damage_flag) - var/soakedcheck = get_armor_soak(null, P.damage_flag) - if(!(istype(P, /obj/projectile/energy) || istype(P, /obj/projectile/beam))) - visible_message("The [P.name] bounces off of [src]'s shell!", \ - "The [P.name] bounces off of [src]'s shell!") + var/projectile_dam_type = proj.damage_type + var/incoming_damage = (round(proj.damage / damage_mod) - (round((proj.damage / damage_mod) * 0.3))) + var/armorcheck = run_armor_check(null, proj.damage_flag) + var/soakedcheck = get_armor_soak(null, proj.damage_flag) + if(!(istype(proj, /obj/projectile/energy) || istype(proj, /obj/projectile/beam))) + visible_message("The [proj.name] bounces off of [src]'s shell!", \ + "The [proj.name] bounces off of [src]'s shell!") new /obj/item/material/shard/shrapnel(src.loc) - if(!(P.damage_type == BRUTE || P.damage_type == BURN)) + if(!(proj.damage_type == BRUTE || proj.damage_type == BURN)) projectile_dam_type = BRUTE incoming_damage = round(incoming_damage / 4) //Damage from strange sources is converted to brute for physical projectiles, though severely decreased. - apply_damage(incoming_damage, projectile_dam_type, null, armorcheck, soakedcheck, is_sharp(P), has_edge(P), P) - return -1 //Doesn't reflect non-beams or non-energy projectiles. They just smack and drop with little to no effect. + apply_damage(incoming_damage, projectile_dam_type, null, armorcheck, soakedcheck, is_sharp(proj), has_edge(proj), proj) + return ..() else - visible_message("The [P.name] gets reflected by [src]'s shell!", \ - "The [P.name] gets reflected by [src]'s shell!") + visible_message("The [proj.name] gets reflected by [src]'s shell!", \ + "The [proj.name] gets reflected by [src]'s shell!") damage_mod = rand(3,5) - incoming_damage = (round(P.damage / damage_mod) - (round((P.damage / damage_mod) * 0.3))) - if(!(P.damage_type == BRUTE || P.damage_type == BURN)) + incoming_damage = (round(proj.damage / damage_mod) - (round((proj.damage / damage_mod) * 0.3))) + if(!(proj.damage_type == BRUTE || proj.damage_type == BURN)) projectile_dam_type = BURN incoming_damage = round(incoming_damage / 4) //Damage from strange sources is converted to burn for energy-type projectiles, though severely decreased. - apply_damage(incoming_damage, P.damage_type, null, armorcheck, soakedcheck, is_sharp(P), has_edge(P), P) + apply_damage(incoming_damage, proj.damage_type, null, armorcheck, soakedcheck, is_sharp(proj), has_edge(proj), proj) // Find a turf near or on the original location to bounce to - if(P.starting) - var/new_x = P.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) - var/new_y = P.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) + if(proj.starting) + var/new_x = proj.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) + var/new_y = proj.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) var/turf/curloc = get_turf(src) // redirect the projectile - P.redirect(new_x, new_y, curloc, src) - P.reflected = 1 - - return -1 // complete projectile permutation + proj.legacy_redirect(new_x, new_y, curloc, src) + proj.reflected = 1 - return (..(P)) + return PROJECTILE_IMPACT_REFLECT + return ..() /* * The Behemoth. Admin-allowance only, still try to keep it in some guideline of 'Balanced', even if it means Security has to be fully geared to be so. @@ -138,22 +137,21 @@ /spell/targeted/construct_advanced/slam ) -/mob/living/simple_mob/construct/juggernaut/behemoth/bullet_act(var/obj/projectile/P) - var/reflectchance = 80 - round(P.damage/3) +/mob/living/simple_mob/construct/juggernaut/behemoth/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + var/reflectchance = 80 - round(proj.damage/3) if(prob(reflectchance)) - visible_message("The [P.name] gets reflected by [src]'s shell!", \ - "The [P.name] gets reflected by [src]'s shell!") + visible_message("The [proj.name] gets reflected by [src]'s shell!", \ + "The [proj.name] gets reflected by [src]'s shell!") // Find a turf near or on the original location to bounce to - if(P.starting) - var/new_x = P.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) - var/new_y = P.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) + if(proj.starting) + var/new_x = proj.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) + var/new_y = proj.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) var/turf/curloc = get_turf(src) // redirect the projectile - P.redirect(new_x, new_y, curloc, src) - P.reflected = 1 + proj.legacy_redirect(new_x, new_y, curloc, src) + proj.reflected = 1 - return -1 // complete projectile permutation - - return (..(P)) + return PROJECTILE_IMPACT_REFLECT + return ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm b/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm index 349fd1bfe428..6165ec87ae73 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm @@ -73,9 +73,11 @@ icon_scale_y = 2 sharp = TRUE -/obj/projectile/icicle/on_impact(atom/A) - playsound(get_turf(A), "shatter", 70, 1) - return ..() +/obj/projectile/icicle/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + playsound(get_turf(target), "shatter", 70, 1) /obj/projectile/icicle/get_structure_damage() return damage / 2 // They're really deadly against mobs, but less effective against solid things. diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm index 9bee1fc035f0..8923e1610597 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm @@ -199,12 +199,11 @@ log_and_message_admins("[src] ignited due to exposure to fire.") ignite() -/mob/living/simple_mob/slime/xenobio/dark_purple/bullet_act(var/obj/projectile/P, var/def_zone) - if(P.damage_type && P.damage_type == BURN && P.damage) // Most bullets won't trigger the explosion, as a mercy towards Security. - log_and_message_admins("[src] ignited due to bring hit by a burning projectile[P.firer ? " by [key_name(P.firer)]" : ""].") +/mob/living/simple_mob/slime/xenobio/dark_purple/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + if(proj.damage_type && proj.damage_type == BURN && proj.damage) // Most bullets won't trigger the explosion, as a mercy towards Security. + log_and_message_admins("[src] ignited due to bring hit by a burning projectile[proj.firer ? " by [key_name(proj.firer)]" : ""].") ignite() - else - return ..() /mob/living/simple_mob/slime/xenobio/dark_purple/attackby(var/obj/item/W, var/mob/user) if(istype(W) && W.damage_force && W.damtype == BURN) @@ -285,21 +284,20 @@ /mob/living/simple_mob/slime/xenobio/amber ) -/mob/living/simple_mob/slime/xenobio/silver/bullet_act(var/obj/projectile/P, var/def_zone) - if(istype(P,/obj/projectile/beam) || istype(P, /obj/projectile/energy)) - visible_message(SPAN_DANGER("\The [src] reflects \the [P]!")) +/mob/living/simple_mob/slime/xenobio/silver/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + if(istype(proj,/obj/projectile/beam) || istype(proj, /obj/projectile/energy)) + visible_message(SPAN_DANGER("\The [src] reflects \the [proj]!")) // Find a turf near or on the original location to bounce to - var/new_x = P.starting.x + pick(0, 0, 0, -1, 1, -2, 2) - var/new_y = P.starting.y + pick(0, 0, 0, -1, 1, -2, 2) + var/new_x = proj.starting.x + pick(0, 0, 0, -1, 1, -2, 2) + var/new_y = proj.starting.y + pick(0, 0, 0, -1, 1, -2, 2) var/turf/curloc = get_turf(src) // redirect the projectile - P.redirect(new_x, new_y, curloc, src) - P.reflected = TRUE - return PROJECTILE_CONTINUE // complete projectile permutation - else - return ..() + proj.legacy_redirect(new_x, new_y, curloc, src) + proj.reflected = TRUE + impact_flags |= PROJECTILE_IMPACT_REFLECT + return ..() // Tier 3 @@ -654,12 +652,11 @@ log_and_message_admins("[src] exploded due to exposure to fire.") explode() -/mob/living/simple_mob/slime/xenobio/oil/bullet_act(obj/projectile/P, def_zone) - if(P.damage_type && P.damage_type == BURN && P.damage) // Most bullets won't trigger the explosion, as a mercy towards Security. - log_and_message_admins("[src] exploded due to bring hit by a burning projectile[P.firer ? " by [key_name(P.firer)]" : ""].") +/mob/living/simple_mob/slime/xenobio/oil/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + if(proj.damage_type && proj.damage_type == BURN && proj.damage) // Most bullets won't trigger the explosion, as a mercy towards Security. + log_and_message_admins("[src] exploded due to bring hit by a burning projectile[proj.firer ? " by [key_name(proj.firer)]" : ""].") explode() - else - return ..() /mob/living/simple_mob/slime/xenobio/oil/attackby(obj/item/W, mob/living/user) if(istype(W) && W.damage_force && W.damtype == BURN) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm b/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm index e8bef7354c06..0313e5be44b2 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm @@ -136,16 +136,13 @@ to_chat(user, "This weapon is ineffective, it does no damage.") visible_message("\The [user] gently taps [src] with \the [O].") -/mob/living/simple_mob/vore/aggressive/corrupthound/sword/bullet_act(var/obj/projectile/Proj) - if(!Proj) return +/mob/living/simple_mob/vore/aggressive/corrupthound/sword/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(prob(35)) - visible_message("[src] deflects [Proj] with its sword tail!") - if(Proj.firer) - ai_holder.react_to_attack_polaris(Proj.firer) - return - else - ..() - + visible_message("[src] deflects [proj] with its sword tail!") + if(proj.firer) + ai_holder.react_to_attack_polaris(proj.firer) + return PROJECTILE_IMPACT_BLOCKED + return ..() /mob/living/simple_mob/vore/aggressive/corrupthound/isSynthetic() return TRUE diff --git a/code/modules/mob/mob-damage.dm b/code/modules/mob/mob-damage.dm new file mode 100644 index 000000000000..f797f95b8702 --- /dev/null +++ b/code/modules/mob/mob-damage.dm @@ -0,0 +1,7 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +//* Damage Instance Handling *// + +/mob/inflict_damage_instance(SHIELDCALL_PROC_HEADER) + return diff --git a/code/modules/mob/mob-defense.dm b/code/modules/mob/mob-defense.dm new file mode 100644 index 000000000000..632159f429c6 --- /dev/null +++ b/code/modules/mob/mob-defense.dm @@ -0,0 +1,121 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +//* Armor Handling *// + +/** + * An override of /atom/proc/run_armorcalls(), with zone filter capability. + * + * This is what you should override, instead of the check/run procs. + * + * * At current moment, due to expensiveness reasons, not providing filter_zone will result in only SHIELDCALL_ARG_DAMAGE being modified. + * + * @params + * * shieldcall_args - passed in shieldcall args list to modify + * * fake_attack - are we just checking armor? + * * filter_zone - confine to a certain hit zone. this is **required** for full processing, otherwise we just check overall damage. + */ +/mob/run_armorcalls(list/shieldcall_args, fake_attack, filter_zone) + ..() // perform default /atom level + +/** + * Generic, low-level armor check for inbound attacks + * + * * This will filter armor by zone. + * * This operates like [atom_shieldcall()]. + * + * @return modified argument list, with SHIELDCALL_ARG_* defines as indices. + */ +/mob/proc/check_mob_armor(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_OVERRIDE(TRUE) + run_armorcalls(args, TRUE, hit_zone) // by default, use atom/var/armor on ourselves + +/** + * Generic, low-level armor processing for inbound attacks + * + * * This will filter armor by zone. + * * This operates like [atom_shieldcall()]. + * + * @return modified argument list, with SHIELDCALL_ARG_* defines as indices. + */ +/mob/proc/run_mob_armor(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_OVERRIDE(TRUE) + run_armorcalls(args, FALSE, hit_zone) // by default, use atom/var/armor on ourselves + +/** + * Checks the average armor for a full-body attack. + * + * * this is used for lazy-sim's like explosion where we're not simulating every limb's individual damage tick. + * * This operates like [atom_shieldcall()]. + * + * @return modified argument list, with SHIELDCALL_ARG_* defines as indices. + */ +/mob/proc/check_mob_overall_armor(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_OVERRIDE(TRUE) + run_armorcalls(args, TRUE) // by default, use atom/var/armor on ourselves + +/** + * Checks the average armor for a full-body attack. + * + * * this is used for lazy-sim's like explosion where we're not simulating every limb's individual damage tick. + * * This operates like [atom_shieldcall()]. + * + * @return modified argument list, with SHIELDCALL_ARG_* defines as indices. + */ +/mob/proc/run_mob_overall_armor(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_OVERRIDE(TRUE) + run_armorcalls(args, FALSE) // by default, use atom/var/armor on ourselves + +//* Defense Handling *// + +/** + * Generic, low-level defense check for inbound attacks + * + * * This will filter defense by zone. + * * This operates like [atom_shieldcall()]. + * + * @return modified argument list, with SHIELDCALL_ARG_* defines as indices. + */ +/mob/proc/check_mob_defense(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_OVERRIDE(TRUE) + run_armorcalls(args, TRUE, hit_zone) // by default, use atom/var/armor on ourselves + run_shieldcalls(args, TRUE) + +/** + * Generic, low-level defense processing for inbound attacks + * + * * This will filter defense by zone. + * * This operates like [atom_shieldcall()]. + * + * @return modified argument list, with SHIELDCALL_ARG_* defines as indices. + */ +/mob/proc/run_mob_defense(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_OVERRIDE(TRUE) + run_armorcalls(args, FALSE, hit_zone) // by default, use atom/var/armor on ourselves + run_shieldcalls(args, FALSE) + +/** + * Checks the average defense for a full-body attack. + * + * * this is used for lazy-sim's like explosion where we're not simulating every limb's individual damage tick. + * * This operates like [atom_shieldcall()]. + * + * @return modified argument list, with SHIELDCALL_ARG_* defines as indices. + */ +/mob/proc/check_mob_overall_defense(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_OVERRIDE(TRUE) + run_armorcalls(args, TRUE) // by default, use atom/var/armor on ourselves + run_shieldcalls(args, TRUE) + +/** + * Checks the average defense for a full-body attack. + * + * * this is used for lazy-sim's like explosion where we're not simulating every limb's individual damage tick. + * * This operates like [atom_shieldcall()]. + * + * @return modified argument list, with SHIELDCALL_ARG_* defines as indices. + */ +/mob/proc/run_mob_overall_defense(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_OVERRIDE(TRUE) + run_armorcalls(args, FALSE) // by default, use atom/var/armor on ourselves + run_shieldcalls(args, FALSE) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 74257bf1b8e0..6c4eccc05ae3 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -427,9 +427,6 @@ . += M . -= src -/mob/proc/ret_grab(obj/effect/list_container/mobl/L as obj, flag) - return - /** * Get the notes of this mob * diff --git a/code/modules/mob/movement.dm b/code/modules/mob/movement.dm index a42342538f4a..c02dd0d4d7ec 100644 --- a/code/modules/mob/movement.dm +++ b/code/modules/mob/movement.dm @@ -47,9 +47,6 @@ . = ..() if(.) return - if(istype(mover, /obj/projectile)) - var/obj/projectile/P = mover - return !P.can_hit_target(src, P.permutated, src == P.original, TRUE) // thrown things still hit us even when nondense if(can_cross_under(mover)) return TRUE @@ -61,8 +58,11 @@ return TRUE return ..() +/** + * Can something cross under us without being blocked by us? + */ /mob/proc/can_cross_under(atom/movable/mover) - return !mover.density && !mover.throwing + return !mover.density && !mover.throwing && !istype(mover, /obj/projectile) /** * Toggle the move intent of the mob @@ -284,7 +284,7 @@ //Something with pulling things if(locate(/obj/item/grab, mob)) add_delay_grab = 7 - var/list/grabbed = mob.ret_grab() + var/list/grabbed = mob.get_grabbing_recursive() + src // im fucking screaming; this is because old code always considers self as grabbed. if(grabbed) if(grabbed.len == 2) grabbed -= mob diff --git a/code/modules/modular_computers/computers/modular_computer/damage.dm b/code/modules/modular_computers/computers/modular_computer/damage.dm index 64d34486891a..32da9c40a242 100644 --- a/code/modules/modular_computers/computers/modular_computer/damage.dm +++ b/code/modules/modular_computers/computers/modular_computer/damage.dm @@ -51,12 +51,12 @@ * "Burn" damage is equally strong against internal components and exterior casing * "Brute" damage mostly damages the casing. */ -/obj/item/modular_computer/bullet_act(obj/projectile/Proj) +/obj/item/modular_computer/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) . = ..() - switch(Proj.damage_type) + switch(proj.damage_type) if(BRUTE) - take_damage_legacy(Proj.damage, Proj.damage / 2) + take_damage_legacy(proj.damage, proj.damage / 2) if(HALLOSS) - take_damage_legacy(Proj.damage, Proj.damage / 3, 0) + take_damage_legacy(proj.damage, proj.damage / 3, 0) if(BURN) - take_damage_legacy(Proj.damage, Proj.damage / 1.5) + take_damage_legacy(proj.damage, proj.damage / 1.5) diff --git a/code/modules/multiz/atoms.dm b/code/modules/multiz/atoms.dm index 9aa67f7f61cf..5abbda59d9a8 100644 --- a/code/modules/multiz/atoms.dm +++ b/code/modules/multiz/atoms.dm @@ -23,4 +23,4 @@ * new_loc - new turf */ /atom/proc/z_pass_out(atom/movable/AM, dir, turf/new_loc) - return !AM || Uncross(AM) + return !AM || Uncross(AM, new_loc) diff --git a/code/modules/multiz/movement.dm b/code/modules/multiz/movement.dm index 650e4f85d0dc..907f3d5a4106 100644 --- a/code/modules/multiz/movement.dm +++ b/code/modules/multiz/movement.dm @@ -314,7 +314,7 @@ return 1 var/atom/A = find_fall_target(oldloc, landing) - if(special_fall_handle(A) || !A || !A.check_impact(src)) + if(special_fall_handle(A) || !A || !A.check_z_impact(src)) return var/mob/drop_mob = locate(/mob, landing) if(drop_mob && !(drop_mob == src) && ismob(drop_mob) && isliving(drop_mob)) //Shitload of checks. This is because the game finds various ways to screw me over. @@ -371,16 +371,14 @@ return TRUE return prevent_z_fall(falling_atom, 0, NONE) & (FALL_TERMINATED | FALL_BLOCKED) - /** * If you are hit: how is it handled. * Return TRUE if the generic fall_impact should be called. * Return FALSE if you handled it yourself or if there's no effect from hitting you. */ -/atom/proc/check_impact(atom/movable/falling_atom) +/atom/proc/check_z_impact(atom/movable/falling_atom) return TRUE - /** * Called by CheckFall when we actually hit something. Various Vars will be described below. * hit_atom is the thing we fall on. diff --git a/code/modules/multiz/turf.dm b/code/modules/multiz/turf.dm index a4261ffa6b4a..e35652e4e2bd 100644 --- a/code/modules/multiz/turf.dm +++ b/code/modules/multiz/turf.dm @@ -113,9 +113,6 @@ return TRUE // impact! return ..() -/turf/check_impact(atom/movable/falling_atom) - return TRUE - //* lookups /turf/proc/above() diff --git a/code/modules/organs/external/external.dm b/code/modules/organs/external/external.dm index a7861e3289a5..ded3e53b1096 100644 --- a/code/modules/organs/external/external.dm +++ b/code/modules/organs/external/external.dm @@ -1269,7 +1269,7 @@ Note that amputating the affected organ does in fact remove the infection from t /obj/item/organ/external/proc/embed(var/obj/item/W, var/silent = 0) if(!owner || loc != owner) return - if(owner.species.species_flags & IS_SLIME) + if(owner.species.reagent_tag == IS_SLIME) create_wound( CUT, 15 ) //fixes proms being bugged into paincrit;instead whatever would embed now just takes a chunk out src.visible_message("[owner] has been seriously wounded by [W]!") W.add_blood(owner) diff --git a/code/modules/organs/internal/augment/armmounted.dm b/code/modules/organs/internal/augment/armmounted.dm index b6e32224a72a..c4b1c99dd71c 100644 --- a/code/modules/organs/internal/augment/armmounted.dm +++ b/code/modules/organs/internal/augment/armmounted.dm @@ -70,7 +70,7 @@ /obj/item/organ/internal/augment/armmounted/hand/sword name = "energy blade implant" - integrated_object_type = /obj/item/melee/energy/sword + integrated_object_type = /obj/item/melee/transforming/energy/sword /* * Shoulder augment. diff --git a/code/modules/overmap/legacy/ships/computers/sensors.dm b/code/modules/overmap/legacy/ships/computers/sensors.dm index ad0e255d7373..f142942fcc8b 100644 --- a/code/modules/overmap/legacy/ships/computers/sensors.dm +++ b/code/modules/overmap/legacy/ships/computers/sensors.dm @@ -219,9 +219,9 @@ else if(health < max_health * 0.75) . += "It shows signs of damage!" -/obj/machinery/shipsensors/bullet_act(var/obj/projectile/Proj) - take_damage_legacy(Proj.get_structure_damage()) - ..() +/obj/machinery/shipsensors/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + take_damage_legacy(proj.get_structure_damage()) /obj/machinery/shipsensors/proc/toggle() if(!use_power && (health == 0 || !in_vacuum())) diff --git a/code/modules/power/antimatter/control.dm b/code/modules/power/antimatter/control.dm index 75ad385a6001..f65362e5f7ef 100644 --- a/code/modules/power/antimatter/control.dm +++ b/code/modules/power/antimatter/control.dm @@ -8,6 +8,7 @@ use_power = USE_POWER_IDLE idle_power_usage = 100 active_power_usage = 1000 + integrity_flags = INTEGRITY_INDESTRUCTIBLE var/list/obj/machinery/am_shielding/linked_shielding var/list/obj/machinery/am_shielding/linked_cores @@ -116,12 +117,10 @@ check_stability() return - -/obj/machinery/power/am_control_unit/bullet_act(var/obj/projectile/Proj) - if(Proj.damage_flag != "bullet") - stability -= Proj.damage - return 0 - +/obj/machinery/power/am_control_unit/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + if(proj.damage_flag != ARMOR_BULLET) + stability -= proj.damage + return ..() /obj/machinery/power/am_control_unit/power_change() ..() diff --git a/code/modules/power/antimatter/shielding.dm b/code/modules/power/antimatter/shielding.dm index 8a8784bc5ab9..d7997462c6da 100644 --- a/code/modules/power/antimatter/shielding.dm +++ b/code/modules/power/antimatter/shielding.dm @@ -98,12 +98,11 @@ check_stability() return +/obj/machinery/am_shielding/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() -/obj/machinery/am_shielding/bullet_act(var/obj/projectile/Proj) - if(Proj.damage_flag != "bullet") - stability -= Proj.damage/2 - return 0 - + if(proj.damage_flag != "bullet") + stability -= proj.damage/2 /obj/machinery/am_shielding/update_icon() cut_overlays() diff --git a/code/modules/power/fusion/core/_core.dm b/code/modules/power/fusion/core/_core.dm index eff1ef5682ba..2ff85f772b77 100644 --- a/code/modules/power/fusion/core/_core.dm +++ b/code/modules/power/fusion/core/_core.dm @@ -87,9 +87,10 @@ var/list/fusion_cores = list() owned_field.AddParticles(name, quantity) . = 1 -/obj/machinery/power/fusion_core/bullet_act(var/obj/projectile/Proj) +/obj/machinery/power/fusion_core/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(owned_field) - . = owned_field.bullet_act(Proj) + return proj.impact_redirect(owned_field, args) + return ..() /obj/machinery/power/fusion_core/proc/set_strength(var/value) value = clamp(value, MIN_FIELD_STR, MAX_FIELD_STR) diff --git a/code/modules/power/fusion/core/core_field.dm b/code/modules/power/fusion/core/core_field.dm index 35daf97aa660..ec9c2ab34109 100644 --- a/code/modules/power/fusion/core/core_field.dm +++ b/code/modules/power/fusion/core/core_field.dm @@ -26,7 +26,6 @@ GLOBAL_VAR_INIT(max_fusion_air_heat, INFINITY) var/obj/machinery/power/fusion_core/owned_core var/list/dormant_reactant_quantities = list() - var/list/particle_catchers = list() var/list/ignore_types @@ -60,67 +59,24 @@ GLOBAL_VAR_INIT(max_fusion_air_heat, INFINITY) if(!owned_core) qdel(src) id_tag = owned_core.id_tag - //create the gimmicky things to handle field collisions - var/obj/effect/fusion_particle_catcher/catcher - - catcher = new (locate(src.x,src.y,src.z)) - catcher.parent = src - catcher.SetSize(1) - particle_catchers.Add(catcher) - - catcher = new (locate(src.x-1,src.y,src.z)) - catcher.parent = src - catcher.SetSize(3) - particle_catchers.Add(catcher) - catcher = new (locate(src.x+1,src.y,src.z)) - catcher.parent = src - catcher.SetSize(3) - particle_catchers.Add(catcher) - catcher = new (locate(src.x,src.y+1,src.z)) - catcher.parent = src - catcher.SetSize(3) - particle_catchers.Add(catcher) - catcher = new (locate(src.x,src.y-1,src.z)) - catcher.parent = src - catcher.SetSize(3) - particle_catchers.Add(catcher) - - catcher = new (locate(src.x-2,src.y,src.z)) - catcher.parent = src - catcher.SetSize(5) - particle_catchers.Add(catcher) - catcher = new (locate(src.x+2,src.y,src.z)) - catcher.parent = src - catcher.SetSize(5) - particle_catchers.Add(catcher) - catcher = new (locate(src.x,src.y+2,src.z)) - catcher.parent = src - catcher.SetSize(5) - particle_catchers.Add(catcher) - catcher = new (locate(src.x,src.y-2,src.z)) - catcher.parent = src - catcher.SetSize(5) - particle_catchers.Add(catcher) - - catcher = new (locate(src.x-3,src.y,src.z)) - catcher.parent = src - catcher.SetSize(7) - particle_catchers.Add(catcher) - catcher = new (locate(src.x+3,src.y,src.z)) - catcher.parent = src - catcher.SetSize(7) - particle_catchers.Add(catcher) - catcher = new (locate(src.x,src.y+3,src.z)) - catcher.parent = src - catcher.SetSize(7) - particle_catchers.Add(catcher) - catcher = new (locate(src.x,src.y-3,src.z)) - catcher.parent = src - catcher.SetSize(7) - particle_catchers.Add(catcher) START_PROCESSING(SSobj, src) +/obj/effect/fusion_em_field/bullet_act(obj/projectile/proj, impact_flags, def_zone, efficiency) + if(!(proj.projectile_type & PROJECTILE_TYPE_BEAM)) + return ..() + AddEnergy(proj.damage) + update_icon() + impact_flags |= PROJECTILE_IMPACT_DELETE + return ..() + +/obj/effect/fusion_em_field/CanAllowThrough(atom/movable/mover, turf/target) + if(istype(mover, /obj/projectile)) + var/obj/projectile/proj = mover + if(proj.projectile_type & PROJECTILE_TYPE_BEAM) + return FALSE + return TRUE + /obj/effect/fusion_em_field/process(delta_time) //make sure the field generator is still intact if(!owned_core || QDELETED(owned_core)) @@ -340,8 +296,20 @@ GLOBAL_VAR_INIT(max_fusion_air_heat, INFINITY) tick_instability += rand(30,50) AM.emp_act(empsev) -/obj/effect/fusion_em_field/proc/change_size(var/newsize = 1) - var/changed = 0 +/** + * Immediately change the field's size. + * + * * This is the radius of the field. + * * This will change `locs` of this field, and its bounds! + */ +/obj/effect/fusion_em_field/proc/change_size(new_size = 1) + ASSERT(!((new_size - 1) % 2)) + ASSERT(new_size <= 13) + + if(new_size == size) + return FALSE + + //! LEGACY var/static/list/size_to_icon = list( "3" = 'icons/effects/96x96.dmi', "5" = 'icons/effects/160x160.dmi', @@ -351,19 +319,27 @@ GLOBAL_VAR_INIT(max_fusion_air_heat, INFINITY) "13" = 'icons/effects/416x416.dmi' ) - if( ((newsize-1)%2==0) && (newsize<=13) ) - icon = 'icons/obj/machines/power/fusion.dmi' - if(newsize>1) - icon = size_to_icon["[newsize]"] - icon_state = "emfield_s[newsize]" - pixel_x = ((newsize-1) * -16) * PIXEL_MULTIPLIER - pixel_y = ((newsize-1) * -16) * PIXEL_MULTIPLIER - size = newsize - changed = newsize + icon = 'icons/obj/machines/power/fusion.dmi' - for(var/obj/effect/fusion_particle_catcher/catcher in particle_catchers) - catcher.UpdateSize() - return changed + if(new_size>1) + icon = size_to_icon["[new_size]"] + icon_state = "emfield_s[new_size]" + //! END + + size = new_size + + // this will shift locs! + var/new_additional_radius = (new_size - 1) / 2 + bound_x = -new_additional_radius * WORLD_ICON_SIZE + bound_y = -new_additional_radius * WORLD_ICON_SIZE + bound_width = new_additional_radius * 2 * WORLD_ICON_SIZE + WORLD_ICON_SIZE + bound_height = new_additional_radius * 2 * WORLD_ICON_SIZE + WORLD_ICON_SIZE + + // shift visuals + pixel_x = -new_additional_radius * WORLD_ICON_SIZE + pixel_y = -new_additional_radius * WORLD_ICON_SIZE + + return TRUE //the !!fun!! part /obj/effect/fusion_em_field/proc/React() @@ -480,18 +456,12 @@ GLOBAL_VAR_INIT(max_fusion_air_heat, INFINITY) /obj/effect/fusion_em_field/Destroy() set_light(0) RadiateAll() - for(var/obj/effect/fusion_particle_catcher/catcher in particle_catchers) - qdel(catcher) if(owned_core) owned_core.owned_field = null owned_core = null STOP_PROCESSING(SSobj, src) . = ..() -/obj/effect/fusion_em_field/bullet_act(var/obj/projectile/Proj) - AddEnergy(Proj.damage) - update_icon() - return 0 //All procs below this point are called in _core.dm, starting at line 41. //Stability monitoring. Gives radio annoucements if field stability is below 80% /obj/effect/fusion_em_field/proc/stability_monitor() diff --git a/code/modules/power/fusion/fusion_particle_catcher.dm b/code/modules/power/fusion/fusion_particle_catcher.dm deleted file mode 100644 index ea8ae497ff57..000000000000 --- a/code/modules/power/fusion/fusion_particle_catcher.dm +++ /dev/null @@ -1,43 +0,0 @@ -/obj/effect/fusion_particle_catcher - icon = 'icons/effects/effects.dmi' - density = 1 - anchored = 1 - invisibility = 101 - var/obj/effect/fusion_em_field/parent - var/mysize = 0 - - light_color = COLOR_BLUE - -/obj/effect/fusion_particle_catcher/Destroy() - . =..() - parent.particle_catchers -= src - parent = null - -/obj/effect/fusion_particle_catcher/proc/SetSize(var/newsize) - name = "collector [newsize]" - mysize = newsize - UpdateSize() - -/obj/effect/fusion_particle_catcher/proc/AddParticles(var/name, var/quantity = 1) - if(parent && parent.size >= mysize) - parent.AddParticles(name, quantity) - return 1 - return 0 - -/obj/effect/fusion_particle_catcher/proc/UpdateSize() - if(parent.size >= mysize) - density = 1 - name = "collector [mysize] ON" - else - density = 0 - name = "collector [mysize] OFF" - -/obj/effect/fusion_particle_catcher/bullet_act(var/obj/projectile/Proj) - parent.AddEnergy(Proj.damage) - update_icon() - return 0 - -/obj/effect/fusion_particle_catcher/CanAllowThrough(atom/movable/mover, turf/target) - if(istype(mover, /obj/effect/accelerated_particle) || istype(mover, /obj/projectile/beam)) - return !density - return TRUE diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm index c74746180773..fc548866d6b2 100644 --- a/code/modules/power/port_gen.dm +++ b/code/modules/power/port_gen.dm @@ -607,10 +607,10 @@ tesla_zap(src, 5, power_gen * 50) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(explosion), get_turf(src), 2, 3, 4, 8), 100) // Not a normal explosion. -/obj/machinery/power/rtg/abductor/bullet_act(obj/projectile/Proj) +/obj/machinery/power/rtg/abductor/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) . = ..() - if(!going_kaboom && istype(Proj) && !Proj.nodamage && ((Proj.damage_type == BURN) || (Proj.damage_type == BRUTE))) - log_and_message_admins("[ADMIN_LOOKUPFLW(Proj.firer)] triggered an Abductor Core explosion at [x],[y],[z] via projectile.") + if(!going_kaboom && istype(proj) && !proj.nodamage && ((proj.damage_type == BURN) || (proj.damage_type == BRUTE))) + log_and_message_admins("[ADMIN_LOOKUPFLW(proj.firer)] triggered an Abductor Core explosion at [x],[y],[z] via projectile.") asplod() /obj/machinery/power/rtg/abductor/attack_hand(mob/user, list/params) diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/power/singularity/field_generator.dm index 2233e458f083..6a80497c66b0 100644 --- a/code/modules/power/singularity/field_generator.dm +++ b/code/modules/power/singularity/field_generator.dm @@ -21,7 +21,7 @@ field_generator power level display anchored = 0 density = 1 use_power = USE_POWER_OFF - + armor = /datum/armor/object/heavy worth_intrinsic = 350 var/const/num_power_levels = 6 // Total number of power level icon has var/Varedit_start = 0 @@ -152,23 +152,20 @@ field_generator power level display ..() return - /obj/machinery/field_generator/emp_act() return 0 -/obj/machinery/field_generator/bullet_act(var/obj/projectile/Proj) - if(istype(Proj, /obj/projectile/beam)) - power += Proj.damage * EMITTER_DAMAGE_POWER_TRANSFER +/obj/machinery/field_generator/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + if(istype(proj, /obj/projectile/beam)) + power += proj.damage * EMITTER_DAMAGE_POWER_TRANSFER update_icon() - return 0 - + return PROJECTILE_IMPACT_DELETE + return ..() /obj/machinery/field_generator/Destroy() src.cleanup() . = ..() - - /obj/machinery/field_generator/proc/turn_off() active = 0 warming_up = 0 diff --git a/code/modules/power/singularity/particle_accelerator/particle.dm b/code/modules/power/singularity/particle_accelerator/particle.dm index 3c6aadbaf710..c19fd665569b 100644 --- a/code/modules/power/singularity/particle_accelerator/particle.dm +++ b/code/modules/power/singularity/particle_accelerator/particle.dm @@ -5,8 +5,11 @@ desc = "Small things moving very fast." icon = 'icons/obj/machines/particle_accelerator2.dmi' icon_state = "particle1"//Need a new icon for this - anchored = 1 - density = 1 + anchored = TRUE + density = TRUE + generic_canpass = FALSE + plane = MOB_PLANE + layer = ABOVE_MOB_LAYER var/movement_range = 10 var/energy = 10 //energy in eV var/mega_energy = 0 //energy in MeV @@ -18,6 +21,10 @@ var/turf/source var/movetotarget = 1 +/obj/effect/accelerated_particle/CanPassThrough(atom/blocker, turf/target, blocker_opinion) + SHOULD_CALL_PARENT(FALSE) + return TRUE + /obj/effect/accelerated_particle/weak icon_state = "particle0" movement_range = 8 @@ -39,18 +46,44 @@ energy = -20 /obj/effect/accelerated_particle/Initialize(mapload, dir = SOUTH) + setDir(dir) . = ..() - src.loc = loc - src.setDir(dir) - INVOKE_ASYNC(src, PROC_REF(move), 1) + START_PROCESSING(SSprocess_5fps, src) + +/obj/effect/accelerated_particle/Destroy() + STOP_PROCESSING(SSprocess_5fps, src) + return ..() + +/obj/effect/accelerated_particle/process(delta_time) + var/old_z = z + var/turf/where_to = get_step(src, dir) + if(!where_to) + qdel(src) + return + if(!Move(where_to)) + if(!QDELETED(src)) + qdel(src) + return + else + return PROCESS_KILL + // being deleted changes Z so that's also an implicit qdeleted() check. + if(z != old_z) + if(!QDELETED(src)) + qdel(src) + return + else + return PROCESS_KILL /obj/effect/accelerated_particle/Moved() . = ..() if(!isturf(loc)) return - for(var/atom/movable/AM as anything in loc.contents) + for(var/atom/movable/AM as anything in loc) do_the_funny(AM) + if(QDELETED(src)) + return +// todo: particle_accelerator_act() or something /obj/effect/accelerated_particle/proc/do_the_funny(atom/A) if (A) if(ismob(A)) @@ -58,24 +91,14 @@ if((istype(A,/obj/machinery/the_singularitygen))||(istype(A,/obj/singularity/))||(istype(A, /obj/machinery/particle_smasher))) A:energy += energy //R-UST port - else if(istype(A,/obj/machinery/power/fusion_core)) - var/obj/machinery/power/fusion_core/collided_core = A - if(particle_type && particle_type != "neutron") - if(collided_core.AddParticles(particle_type, 1 + additional_particles)) - collided_core.owned_field.plasma_temperature += mega_energy - collided_core.owned_field.energy += energy - loc = null - else if(istype(A, /obj/effect/fusion_particle_catcher)) - var/obj/effect/fusion_particle_catcher/PC = A + if(istype(A, /obj/effect/fusion_em_field)) + var/obj/effect/fusion_em_field/field = A if(particle_type && particle_type != "neutron") - if(PC.parent.owned_core.AddParticles(particle_type, 1 + additional_particles)) - PC.parent.plasma_temperature += mega_energy - PC.parent.energy += energy - loc = null - - -/obj/effect/accelerated_particle/legacy_ex_act(severity) - qdel(src) + if(field.owned_core.AddParticles(particle_type, 1 + additional_particles)) + field.plasma_temperature += mega_energy + field.energy += energy + qdel(src) + return /obj/effect/accelerated_particle/singularity_act() return @@ -84,26 +107,3 @@ if(!istype(M)) return M.afflict_radiation(energy * 5, TRUE) - -/obj/effect/accelerated_particle/proc/move(var/lag) - var/turf/new_target - if(target) - if(movetotarget) - new_target = get_step_towards(src, target) - if(get_dist(src,new_target) < 1) - movetotarget = 0 - else - new_target = get_step_away(src, source) - else - new_target = get_step(src, dir) - if(new_target) - forceMove(new_target) - else - qdel(src) - return - movement_range-- - if(movement_range <= 0) - qdel(src) - else - sleep(lag) - move(lag) diff --git a/code/modules/power/singularity/particle_accelerator/particle_smasher.dm b/code/modules/power/singularity/particle_accelerator/particle_smasher.dm index a98c670f46a3..fb7500fcf428 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_smasher.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_smasher.dm @@ -10,6 +10,7 @@ anchored = 0 density = 1 use_power = USE_POWER_OFF + armor = /datum/armor/object/heavy var/image/material_layer // Holds the image used for the filled overlay. var/image/material_glow // Holds the image used for the glow overlay. @@ -127,11 +128,12 @@ else set_light(0, 0, "#FFFFFF") -/obj/machinery/particle_smasher/bullet_act(var/obj/projectile/Proj) - if(istype(Proj, /obj/projectile/beam)) - if(Proj.damage >= 50) +/obj/machinery/particle_smasher/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + if(istype(proj, /obj/projectile/beam)) + if(proj.damage >= 50) TryCraft() - return 0 + return PROJECTILE_IMPACT_DELETE + return ..() /obj/machinery/particle_smasher/process(delta_time) if(!src.anchored) // Rapidly loses focus. diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index 0b12ef9396ea..71ed027c49ac 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -64,8 +64,8 @@ GLOBAL_LIST_BOILERPLATE(all_singularities, /obj/singularity) if(3) energy -= round(((energy+1)/4),1) -/obj/singularity/bullet_act(obj/projectile/P) - return 0 //Will there be an impact? Who knows. Will we see it? No. +/obj/singularity/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + return PROJECTILE_IMPACT_DELETE /obj/singularity/Bump(atom/A) consume(A) diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm index 599a8ed31d96..63c73826cfaf 100644 --- a/code/modules/power/supermatter/supermatter.dm +++ b/code/modules/power/supermatter/supermatter.dm @@ -361,25 +361,26 @@ return 1 - -/obj/machinery/power/supermatter/bullet_act(var/obj/projectile/Proj) +/obj/machinery/power/supermatter/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) var/turf/L = loc - if(!istype(L)) // We don't run process() when we are in space - return 0 // This stops people from being able to really power up the supermatter - // Then bring it inside to explode instantly upon landing on a valid turf. + // We don't run process() when we are in space + // This stops people from being able to really power up the supermatter + // Then bring it inside to explode instantly upon landing on a valid turf. + if(!istype(L)) + return PROJECTILE_IMPACT_DELETE var/added_energy var/added_damage - var/proj_damage = Proj.get_structure_damage() - if(istype(Proj, /obj/projectile/beam)) + var/proj_damage = proj.get_structure_damage() + if(istype(proj, /obj/projectile/beam)) added_energy = proj_damage * config_bullet_energy * CHARGING_FACTOR / POWER_FACTOR power += added_energy else added_damage = proj_damage * config_bullet_energy damage += added_damage if(added_energy || added_damage) - investigate_log("Hit by \"[Proj.name]\". +[added_energy] Energy, +[added_damage] Damage.", INVESTIGATE_SUPERMATTER) - return 0 + investigate_log("Hit by \"[proj.name]\". +[added_energy] Energy, +[added_damage] Damage.", INVESTIGATE_SUPERMATTER) + return PROJECTILE_IMPACT_DELETE /obj/machinery/power/supermatter/attack_robot(mob/user as mob) if(Adjacent(user)) @@ -465,20 +466,25 @@ Consume(W) -/obj/machinery/power/supermatter/Bumped(atom/AM as mob|obj) - if(istype(AM, /obj/effect)) - return - if(istype(AM, /mob/living)) - var/mob/living/M = AM - var/datum/gender/T = GLOB.gender_datums[M.get_visible_gender()] - AM.visible_message("\The [AM] slams into \the [src] inducing a resonance... [T.his] body starts to glow and catch flame before flashing into ash.",\ - "You slam into \the [src] as your ears are filled with unearthly ringing. Your last thought is \"Oh, fuck.\"",\ - "You hear an uneartly ringing, then what sounds like a shrilling kettle as you are washed with a wave of heat.") - else if(!grav_pulling) //To prevent spam, detonating supermatter does not indicate non-mobs being destroyed - AM.visible_message("\The [AM] smacks into \the [src] and rapidly flashes to ash.",\ - "You hear a loud crack as you are washed with a wave of heat.") - - Consume(AM) +/obj/machinery/power/supermatter/Bumped(atom/movable/bumped_atom) + . = ..() + var/their_loc = bumped_atom.loc + spawn(0) + if(their_loc != bumped_atom.loc) + return + if(istype(bumped_atom, /obj/effect)) + return + if(istype(bumped_atom, /mob/living)) + var/mob/living/M = bumped_atom + var/datum/gender/T = GLOB.gender_datums[M.get_visible_gender()] + bumped_atom.visible_message("\The [bumped_atom] slams into \the [src] inducing a resonance... [T.his] body starts to glow and catch flame before flashing into ash.",\ + "You slam into \the [src] as your ears are filled with unearthly ringing. Your last thought is \"Oh, fuck.\"",\ + "You hear an uneartly ringing, then what sounds like a shrilling kettle as you are washed with a wave of heat.") + else if(!grav_pulling) //To prevent spam, detonating supermatter does not indicate non-mobs being destroyed + bumped_atom.visible_message("\The [bumped_atom] smacks into \the [src] and rapidly flashes to ash.",\ + "You hear a loud crack as you are washed with a wave of heat.") + + Consume(bumped_atom) /obj/machinery/power/supermatter/proc/Consume(var/mob/living/user) // todo: rework the fucking supermatter so we don't need this diff --git a/code/modules/projectiles/README.md b/code/modules/projectiles/README.md new file mode 100644 index 000000000000..845e6a2075b1 --- /dev/null +++ b/code/modules/projectiles/README.md @@ -0,0 +1,8 @@ +# Projectiles + +The projectiles module contains the following and the systems that support them: + +* Guns +* Ammunition +* Magazines +* Projectiles diff --git a/code/modules/projectiles/ammunition/ammo_caliber.dm b/code/modules/projectiles/ammunition/ammo_caliber.dm index 4bc9635dff49..a16391347c3c 100644 --- a/code/modules/projectiles/ammunition/ammo_caliber.dm +++ b/code/modules/projectiles/ammunition/ammo_caliber.dm @@ -1,5 +1,5 @@ //* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2024 silicons *// +//* Copyright (c) 2024 Citadel Station Developers *// GLOBAL_LIST_INIT(calibers, init_calibers()) diff --git a/code/modules/projectiles/ammunition/calibers/special/dart.dm b/code/modules/projectiles/ammunition/calibers/special/dart.dm index 118dac8ddb33..a4772c10cd30 100644 --- a/code/modules/projectiles/ammunition/calibers/special/dart.dm +++ b/code/modules/projectiles/ammunition/calibers/special/dart.dm @@ -63,8 +63,13 @@ . = ..() create_reagents(reagent_amount) -/obj/projectile/bullet/chemdart/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null) - if(blocked < 2 && isliving(target)) +/obj/projectile/bullet/chemdart/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if((. & PROJECTILE_IMPACT_BLOCKED) || (efficiency < 0.95)) + return + if(isliving(target)) var/mob/living/L = target if(L.can_inject(target_zone=def_zone)) reagents.trans_to_mob(L, reagent_amount, CHEM_INJECT) diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 76512e496447..8fb6cf408a30 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -55,6 +55,15 @@ zoomdevicename = "scope" inhand_default_type = INHAND_DEFAULT_ICON_GUNS + //* Accuracy, Dispersion, Instability *// + + /// entirely disable baymiss on fired projectiles + /// + /// * this is a default value; set to null by default to have the projectile's say. + var/accuracy_disabled = null + + // legacy below // + var/burst = 1 var/fire_delay = 6 //delay after shooting before the gun can be used again var/burst_delay = 2 //delay between shots, if firing in bursts @@ -321,9 +330,10 @@ if(!istype(A)) return ..() if(user.a_intent == INTENT_HARM) //point blank shooting - if (A == user && user.zone_sel.selecting == O_MOUTH && !mouthshoot) - handle_suicide(user) - return + // todo: disabled for now + // if (A == user && user.zone_sel.selecting == O_MOUTH && !mouthshoot) + // handle_suicide(user) + // return var/mob/living/L = user if(user && user.client && istype(L) && L.aiming && L.aiming.active && L.aiming.aiming_at != A && A != user) PreFire(A,user) //They're using the new gun system, locate what they're aiming at. @@ -501,7 +511,7 @@ var/acc = burst_accuracy[min(i, burst_accuracy.len)] var/disp = dispersion[min(i, dispersion.len)] - P.accuracy = accuracy + acc + P.accuracy_overall_modify *= 1 + acc / 100 P.dispersion = disp P.shot_from = src.name @@ -662,22 +672,24 @@ disp_mod += one_handed_penalty*0.5 //dispersion per point of two-handedness //Accuracy modifiers - P.accuracy = accuracy + acc_mod - P.dispersion = disp_mod + if(!isnull(accuracy_disabled)) + P.accuracy_disabled = accuracy_disabled - P.accuracy -= user.get_accuracy_penalty() + P.accuracy_overall_modify *= 1 + (acc_mod / 100) + P.accuracy_overall_modify *= 1 - (user.get_accuracy_penalty() / 100) + P.dispersion = disp_mod //accuracy bonus from aiming if (aim_targets && (target in aim_targets)) //If you aim at someone beforehead, it'll hit more often. //Kinda balanced by fact you need like 2 seconds to aim //As opposed to no-delay pew pew - P.accuracy += 30 + P.accuracy_overall_modify *= 1.3 // Some modifiers make it harder or easier to hit things. for(var/datum/modifier/M in user.modifiers) if(!isnull(M.accuracy)) - P.accuracy += M.accuracy + P.accuracy_overall_modify += 1 + (M.accuracy / 100) if(!isnull(M.accuracy_dispersion)) P.dispersion = max(P.dispersion + M.accuracy_dispersion, 0) @@ -717,44 +729,45 @@ else playsound(src, shot_sound, 50, 1) +// todo: rework all this this is fucking dumb //Suicide handling. -/obj/item/gun/var/mouthshoot = 0 //To stop people from suiciding twice... >.> - -/obj/item/gun/proc/handle_suicide(mob/living/user) - if(!ishuman(user)) - return - var/mob/living/carbon/human/M = user - - mouthshoot = 1 - M.visible_message("[user] sticks their gun in their mouth, ready to pull the trigger...") - if(!do_after(user, 40)) - M.visible_message("[user] decided life was worth living") - mouthshoot = 0 - return - var/obj/projectile/in_chamber = consume_next_projectile() - if (istype(in_chamber)) - user.visible_message("[user] pulls the trigger.") - play_fire_sound(M, in_chamber) - if(istype(in_chamber, /obj/projectile/beam/lasertag)) - user.show_message("You feel rather silly, trying to commit suicide with a toy.") - mouthshoot = 0 - return - - in_chamber.on_hit(M) - if(in_chamber.damage_type != HALLOSS && !in_chamber.nodamage) - log_and_message_admins("[key_name(user)] commited suicide using \a [src]") - user.apply_damage(in_chamber.damage*2.5, in_chamber.damage_type, "head", used_weapon = "Point blank shot in the mouth with \a [in_chamber]", sharp=1) - user.death() - else if(in_chamber.damage_type == HALLOSS) - to_chat(user, "Ow...") - user.apply_effect(110,AGONY,0) - qdel(in_chamber) - mouthshoot = 0 - return - else - handle_click_empty(user) - mouthshoot = 0 - return +// /obj/item/gun/var/mouthshoot = 0 //To stop people from suiciding twice... >.> + +// /obj/item/gun/proc/handle_suicide(mob/living/user) +// if(!ishuman(user)) +// return +// var/mob/living/carbon/human/M = user + +// mouthshoot = 1 +// M.visible_message("[user] sticks their gun in their mouth, ready to pull the trigger...") +// if(!do_after(user, 40)) +// M.visible_message("[user] decided life was worth living") +// mouthshoot = 0 +// return +// var/obj/projectile/in_chamber = consume_next_projectile() +// if (istype(in_chamber)) +// user.visible_message("[user] pulls the trigger.") +// play_fire_sound(M, in_chamber) +// if(istype(in_chamber, /obj/projectile/beam/lasertag)) +// user.show_message("You feel rather silly, trying to commit suicide with a toy.") +// mouthshoot = 0 +// return + +// in_chamber.on_hit(M) +// if(in_chamber.damage_type != HALLOSS && !in_chamber.nodamage) +// log_and_message_admins("[key_name(user)] commited suicide using \a [src]") +// user.apply_damage(in_chamber.damage*2.5, in_chamber.damage_type, "head", used_weapon = "Point blank shot in the mouth with \a [in_chamber]", sharp=1) +// user.death() +// else if(in_chamber.damage_type == HALLOSS) +// to_chat(user, "Ow...") +// user.apply_effect(110,AGONY,0) +// qdel(in_chamber) +// mouthshoot = 0 +// return +// else +// handle_click_empty(user) +// mouthshoot = 0 +// return /obj/item/gun/proc/toggle_scope(var/zoom_amount=2.0) //looking through a scope limits your periphereal vision diff --git a/code/modules/projectiles/gun_item_renderer.dm b/code/modules/projectiles/gun_item_renderer.dm index 3ee4b5652857..d7b57cc54155 100644 --- a/code/modules/projectiles/gun_item_renderer.dm +++ b/code/modules/projectiles/gun_item_renderer.dm @@ -1,6 +1,6 @@ //* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2024 Citadel Station developers. *// +//* Copyright (c) 2024 Citadel Station Developers *// /** * gun render system diff --git a/code/modules/projectiles/gun_mob_renderer.dm b/code/modules/projectiles/gun_mob_renderer.dm index fb98d935d003..5bbce3dc96b3 100644 --- a/code/modules/projectiles/gun_mob_renderer.dm +++ b/code/modules/projectiles/gun_mob_renderer.dm @@ -1,6 +1,6 @@ //* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2024 Citadel Station developers. *// +//* Copyright (c) 2024 Citadel Station Developers *// /** * gun render system diff --git a/code/modules/projectiles/guns/ballistic/microbattery/medigun_cells.dm b/code/modules/projectiles/guns/ballistic/microbattery/medigun_cells.dm index 389ba521281a..caeac99fe5e7 100644 --- a/code/modules/projectiles/guns/ballistic/microbattery/medigun_cells.dm +++ b/code/modules/projectiles/guns/ballistic/microbattery/medigun_cells.dm @@ -20,7 +20,12 @@ tracer_type = /obj/effect/projectile/tracer/medigun impact_type = /obj/effect/projectile/impact/medigun -/obj/projectile/beam/medical_cell/on_hit(var/mob/living/carbon/human/target) //what does it do when it hits someone? +/obj/projectile/beam/medical_cell/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(ishuman(target)) + on_hit_legacy(target) + +/obj/projectile/beam/medical_cell/proc/on_hit_legacy(var/mob/living/carbon/human/target) //what does it do when it hits someone? return /obj/item/ammo_casing/microbattery/medical/brute @@ -29,7 +34,7 @@ type_name = "BRUTE" projectile_type = /obj/projectile/beam/medical_cell/brute -/obj/projectile/beam/medical_cell/brute/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/brute/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustBruteLoss(-10) else @@ -41,7 +46,7 @@ type_name = "BURN" projectile_type = /obj/projectile/beam/medical_cell/burn -/obj/projectile/beam/medical_cell/burn/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/burn/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustFireLoss(-10) else @@ -53,7 +58,7 @@ type_name = "STABILIZE" projectile_type = /obj/projectile/beam/medical_cell/stabilize -/obj/projectile/beam/medical_cell/stabilize/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/stabilize/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustOxyLoss(-30) for(var/name in list(BP_HEAD, BP_L_HAND, BP_R_HAND, BP_L_ARM, BP_R_ARM, BP_L_FOOT, BP_R_FOOT, BP_L_LEG, BP_R_LEG, BP_GROIN, BP_TORSO)) @@ -81,7 +86,7 @@ type_name = "TOXIN" projectile_type = /obj/projectile/beam/medical_cell/toxin -/obj/projectile/beam/medical_cell/toxin/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/toxin/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustToxLoss(-10) else @@ -93,7 +98,7 @@ type_name = "OMNI" projectile_type = /obj/projectile/beam/medical_cell/omni -/obj/projectile/beam/medical_cell/omni/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/omni/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustBruteLoss(-2.5) target.adjustFireLoss(-2.5) @@ -108,7 +113,7 @@ type_name = "ANTIRAD" projectile_type = /obj/projectile/beam/medical_cell/antirad -/obj/projectile/beam/medical_cell/antirad/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/antirad/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustToxLoss(-2.5) target.cure_radiation(RAD_MOB_CURE_STRENGTH_MEDIGUN) @@ -121,7 +126,7 @@ type_name = "BRUTE-II" projectile_type = /obj/projectile/beam/medical_cell/brute2 -/obj/projectile/beam/medical_cell/brute2/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/brute2/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustBruteLoss(-20) else @@ -133,7 +138,7 @@ type_name = "BURN-II" projectile_type = /obj/projectile/beam/medical_cell/burn2 -/obj/projectile/beam/medical_cell/burn2/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/burn2/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustFireLoss(-20) else @@ -145,7 +150,7 @@ type_name = "STABILIZE-II" projectile_type = /obj/projectile/beam/medical_cell/stabilize2 -/obj/projectile/beam/medical_cell/stabilize2/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/stabilize2/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustOxyLoss(-200) for(var/name in list(BP_HEAD, BP_L_HAND, BP_R_HAND, BP_L_ARM, BP_R_ARM, BP_L_FOOT, BP_R_FOOT, BP_L_LEG, BP_R_LEG, BP_GROIN, BP_TORSO)) @@ -168,7 +173,7 @@ type_name = "OMNI-II" projectile_type = /obj/projectile/beam/medical_cell/omni2 -/obj/projectile/beam/medical_cell/omni2/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/omni2/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustBruteLoss(-5) target.adjustFireLoss(-5) @@ -183,7 +188,7 @@ type_name = "TOXIN-II" projectile_type = /obj/projectile/beam/medical_cell/toxin2 -/obj/projectile/beam/medical_cell/toxin2/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/toxin2/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustToxLoss(-20) else @@ -195,7 +200,7 @@ type_name = "HASTE" projectile_type = /obj/projectile/beam/medical_cell/haste -/obj/projectile/beam/medical_cell/haste/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/haste/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.add_modifier(/datum/modifier/medigunhaste, 20 SECONDS) else @@ -215,7 +220,7 @@ type_name = "RESIST" projectile_type = /obj/projectile/beam/medical_cell/resist -/obj/projectile/beam/medical_cell/resist/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/resist/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.add_modifier(/datum/modifier/resistance, 20 SECONDS) else @@ -235,7 +240,7 @@ type_name = "CORPSE MEND" projectile_type = /obj/projectile/beam/medical_cell/corpse_mend -/obj/projectile/beam/medical_cell/corpse_mend/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/corpse_mend/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) if(target.stat == DEAD) target.adjustBruteLoss(-50) @@ -251,7 +256,7 @@ type_name = "BRUTE-III" projectile_type = /obj/projectile/beam/medical_cell/brute3 -/obj/projectile/beam/medical_cell/brute3/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/brute3/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustBruteLoss(-40) else @@ -263,7 +268,7 @@ type_name = "BURN-III" projectile_type = /obj/projectile/beam/medical_cell/burn3 -/obj/projectile/beam/medical_cell/burn3/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/burn3/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustFireLoss(-40) else @@ -275,7 +280,7 @@ type_name = "TOXIN-III" projectile_type = /obj/projectile/beam/medical_cell/toxin3 -/obj/projectile/beam/medical_cell/toxin3/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/toxin3/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustToxLoss(-40) else @@ -287,7 +292,7 @@ type_name = "OMNI-III" projectile_type = /obj/projectile/beam/medical_cell/omni3 -/obj/projectile/beam/medical_cell/omni3/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/omni3/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustBruteLoss(-10) target.adjustFireLoss(-10) @@ -303,7 +308,7 @@ type_name = "SHRINK" projectile_type = /obj/projectile/beam/medical_cell/shrink -/obj/projectile/beam/medical_cell/shrink/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/shrink/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.resize(0.5) target.show_message("The beam fires into your body, changing your size!") @@ -317,7 +322,7 @@ type_name = "GROW" projectile_type = /obj/projectile/beam/medical_cell/grow -/obj/projectile/beam/medical_cell/grow/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/grow/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.resize(2.0) target.show_message("The beam fires into your body, changing your size!") @@ -331,7 +336,7 @@ type_name = "NORMALSIZE" projectile_type = /obj/projectile/beam/medical_cell/normalsize -/obj/projectile/beam/medical_cell/normalsize/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/normalsize/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.resize(1) target.show_message("The beam fires into your body, changing your size!") diff --git a/code/modules/projectiles/guns/ballistic/microbattery/revolver_cells.dm b/code/modules/projectiles/guns/ballistic/microbattery/revolver_cells.dm index c7ee2819ed9c..e43caa1981ce 100644 --- a/code/modules/projectiles/guns/ballistic/microbattery/revolver_cells.dm +++ b/code/modules/projectiles/guns/ballistic/microbattery/revolver_cells.dm @@ -40,9 +40,6 @@ damage = 2 agony = 20 pellets = 6 //number of pellets - range_step = 2 //projectile will lose a fragment each time it travels this distance. Can be a non-integer. - base_spread = 90 //lower means the pellets spread more across body parts. If zero then this is considered a shrapnel explosion instead of a shrapnel cone - spread_step = 10 embed_chance = 0 sharp = 0 damage_flag = ARMOR_MELEE @@ -67,14 +64,17 @@ sharp = 0 damage_flag = ARMOR_MELEE -/obj/projectile/bullet/stripper/on_hit(var/atom/stripped) - if(ishuman(stripped)) - var/mob/living/carbon/human/H = stripped +/obj/projectile/bullet/stripper/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + + if(ishuman(target)) + var/mob/living/carbon/human/H = target if(!H.permit_stripped) return H.drop_slots_to_ground(list(SLOT_ID_SUIT, SLOT_ID_UNIFORM, SLOT_ID_BACK, SLOT_ID_SHOES, SLOT_ID_GLOVES)) //Hats can stay! Most other things fall off with removing these. - ..() /obj/item/ammo_casing/microbattery/combat/final name = "\'Hydra\' microbattery - FINAL OPTION" @@ -94,7 +94,11 @@ tracer_type = /obj/effect/projectile/tracer/laser_omni impact_type = /obj/effect/projectile/impact/laser_omni -/obj/projectile/beam/final_option/on_hit(var/atom/impacted) +/obj/projectile/beam/final_option/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(isliving(impacted)) var/mob/living/L = impacted if(L.mind) @@ -104,5 +108,3 @@ nif = H.nif SStranscore.m_backup(L.mind,nif,one_time = TRUE) L.gib() - - ..() diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm index b8101a537ae8..ef599f88a7ca 100644 --- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm +++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm @@ -223,7 +223,7 @@ kinetic_gun = null return ..() -/obj/projectile/kinetic/Bump(atom/target) +/obj/projectile/kinetic/pre_impact(atom/target, impact_flags, def_zone) if(kinetic_gun) var/list/mods = kinetic_gun.get_modkits() for(var/obj/item/ka_modkit/M in mods) @@ -234,20 +234,15 @@ pressure_decrease_active = TRUE return ..() -/obj/projectile/kinetic/projectile_attack_mob(mob/living/target_mob, distance, miss_modifier) - if(!pressure_decrease_active && !lavaland_environment_check(get_turf(src))) - name = "weakened [name]" - damage = damage * pressure_decrease - pressure_decrease_active = TRUE - return ..() - /obj/projectile/kinetic/legacy_on_range() strike_thing() ..() -/obj/projectile/kinetic/on_hit(atom/target) - strike_thing(target) +/obj/projectile/kinetic/on_impact(atom/target, impact_flags, def_zone, efficiency) . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + strike_thing(target) /obj/projectile/kinetic/proc/strike_thing(atom/target) if(!pressure_decrease_active && !lavaland_environment_check(get_turf(src))) diff --git a/code/modules/projectiles/guns/energy/particle.dm b/code/modules/projectiles/guns/energy/particle.dm index 910bedde7903..52184f2956c7 100644 --- a/code/modules/projectiles/guns/energy/particle.dm +++ b/code/modules/projectiles/guns/energy/particle.dm @@ -187,9 +187,9 @@ light_power = 1 light_color = "#CCFFFF" -/turf/simulated/mineral/bullet_act(var/obj/projectile/Proj) - if(istype(Proj, /obj/projectile/bullet/particle)) - if(prob(Proj.damage)) +/turf/simulated/mineral/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + if(istype(proj, /obj/projectile/bullet/particle)) + if(prob(proj.damage)) GetDrilled() - ..() diff --git a/code/modules/projectiles/guns/energy/sizegun_vr.dm b/code/modules/projectiles/guns/energy/sizegun_vr.dm index a96008be2e08..73b690bbd4cc 100644 --- a/code/modules/projectiles/guns/energy/sizegun_vr.dm +++ b/code/modules/projectiles/guns/energy/sizegun_vr.dm @@ -73,7 +73,11 @@ tracer_type = /obj/effect/projectile/tracer/xray impact_type = /obj/effect/projectile/impact/xray -/obj/projectile/beam/sizelaser/on_hit(var/atom/target) +/obj/projectile/beam/sizelaser/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + var/mob/living/M = target if(!M.permit_sizegun) M.visible_message("[src] has no effect on [M].") @@ -87,18 +91,13 @@ var/mob/living/H = M H.resize(set_size, TRUE) H.updateicon() - else - return 1 - /obj/projectile/beam/sizelaser/shrink set_size = 0.5 //50% of current size - /obj/projectile/beam/sizelaser/grow set_size = 2.0 //200% of current size - /obj/item/gun/energy/stripper//Because it can be fun name = "stripper gun" desc = "A gun designed to remove unnessary layers from people. For external use only!" diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index 4286b0cd921d..b117377439ea 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -428,7 +428,7 @@ icon_state = "ermitter_gun" item_state = "pulse" projectile_type = /obj/projectile/beam/emitter - fire_delay = 10 + fire_delay = 2 SECONDS charge_cost = 900 cell_type = /obj/item/cell accept_cell_type = /obj/item/cell diff --git a/code/modules/projectiles/guns/launcher.dm b/code/modules/projectiles/guns/launcher.dm index f95cee55f07d..a752f382bd73 100644 --- a/code/modules/projectiles/guns/launcher.dm +++ b/code/modules/projectiles/guns/launcher.dm @@ -14,11 +14,6 @@ /obj/item/gun/launcher/can_hit(var/mob/living/target as mob, var/mob/living/user as mob) return 1 -//Override this to avoid a runtime with suicide handling. -/obj/item/gun/launcher/handle_suicide(mob/living/user) - to_chat(user, "Shooting yourself with \a [src] is pretty tricky. You can't seem to manage it.") - return - /obj/item/gun/launcher/proc/update_release_force(obj/projectile) return 0 diff --git a/code/modules/projectiles/guns/legacy_vr_guns/crestrose.dm b/code/modules/projectiles/guns/legacy_vr_guns/crestrose.dm index e96a67f93eb3..322951fb7dd5 100644 --- a/code/modules/projectiles/guns/legacy_vr_guns/crestrose.dm +++ b/code/modules/projectiles/guns/legacy_vr_guns/crestrose.dm @@ -31,10 +31,10 @@ update_icon() update_held_icon() - -/obj/item/gun/ballistic/automatic/fluff/crestrose/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(default_parry_check(user, attacker, damage_source) && prob(50)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(user.loc, 'sound/weapons/punchmiss.ogg', 50, 1) - return 1 - return 0 +// todo: uhh we can fix it later maybe idfk +// /obj/item/gun/ballistic/automatic/fluff/crestrose/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") +// if(default_parry_check(user, attacker, damage_source) && prob(50)) +// user.visible_message("\The [user] parries [attack_text] with \the [src]!") +// playsound(user.loc, 'sound/weapons/punchmiss.ogg', 50, 1) +// return 1 +// return 0 diff --git a/code/modules/projectiles/guns/legacy_vr_guns/pummeler.dm b/code/modules/projectiles/guns/legacy_vr_guns/pummeler.dm index 1c4283e0b106..85a97302a6fe 100644 --- a/code/modules/projectiles/guns/legacy_vr_guns/pummeler.dm +++ b/code/modules/projectiles/guns/legacy_vr_guns/pummeler.dm @@ -36,13 +36,14 @@ vacuum_traversal = 0 range = WORLD_ICON_SIZE * 6 //Scary name, but just deletes the projectile after this range -/obj/projectile/pummel/on_hit(var/atom/movable/target, var/blocked = 0) +/obj/projectile/pummel/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(isliving(target)) var/mob/living/L = target var/throwdir = get_dir(firer,L) - if(prob(40) && !blocked) + if(prob(40) && (efficiency >= 0.9)) L.afflict_stun(20 * 1) L.Confuse(1) L.throw_at_old(get_edge_target_turf(L, throwdir), rand(3,6), 10) - - return 1 diff --git a/code/modules/projectiles/guns/legacy_vr_guns/sickshot.dm b/code/modules/projectiles/guns/legacy_vr_guns/sickshot.dm index efa27a577727..72b43ca83f12 100644 --- a/code/modules/projectiles/guns/legacy_vr_guns/sickshot.dm +++ b/code/modules/projectiles/guns/legacy_vr_guns/sickshot.dm @@ -34,7 +34,11 @@ vacuum_traversal = 0 range = WORLD_ICON_SIZE * 5 //Scary name, but just deletes the projectile after this range -/obj/projectile/sickshot/on_hit(var/atom/movable/target, var/blocked = 0) +/obj/projectile/sickshot/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(isliving(target)) var/mob/living/L = target if(prob(20)) @@ -44,5 +48,3 @@ var/mob/living/carbon/human/H = target H.vomit() H.Confuse(2) - - return 1 diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm index 90a5b2d2af9f..2cb005d1b736 100644 --- a/code/modules/projectiles/guns/magic/staff.dm +++ b/code/modules/projectiles/guns/magic/staff.dm @@ -27,9 +27,6 @@ icon_state = "staffofhealing" item_state = "staffofhealing" -/obj/item/gun/magic/staff/healing/handle_suicide() //Stops people trying to commit suicide to heal themselves - return - /* /obj/item/gun/magic/staff/chaos name = "staff of chaos" @@ -98,12 +95,12 @@ return ..() */ -/obj/item/gun/magic/staff/locker - name = "staff of the locker" - desc = "An artefact that expells encapsulating bolts, for incapacitating thy enemy." - fire_sound = 'sound/magic/staff_change.ogg' - ammo_type = /obj/item/ammo_casing/magic/locker - icon_state = "locker" - item_state = "locker" - max_charges = 6 - recharge_rate = 4 +// /obj/item/gun/magic/staff/locker +// name = "staff of the locker" +// desc = "An artefact that expells encapsulating bolts, for incapacitating thy enemy." +// fire_sound = 'sound/magic/staff_change.ogg' +// ammo_type = /obj/item/ammo_casing/magic/locker +// icon_state = "locker" +// item_state = "locker" +// max_charges = 6 +// recharge_rate = 4 diff --git a/code/modules/projectiles/guns/projectile/automatic.dm b/code/modules/projectiles/guns/projectile/automatic.dm index e892f40b9857..3e9afa3f3fe4 100644 --- a/code/modules/projectiles/guns/projectile/automatic.dm +++ b/code/modules/projectiles/guns/projectile/automatic.dm @@ -735,10 +735,6 @@ . = ..() icon_state = (ammo_magazine)? "toy_smg" : "toy_smg-empty" -/obj/item/gun/ballistic/automatic/advanced_smg/foam/handle_suicide(mob/living/user) - user.show_message("You feel rather silly, trying to commit suicide with a toy.") - mouthshoot = 0 - /obj/item/gun/ballistic/automatic/advanced_smg/foam/blue icon_state = "toy_smg_blue" @@ -767,10 +763,6 @@ else icon_state = "toy_c20r" -/obj/item/gun/ballistic/automatic/c20r/foam/handle_suicide(mob/living/user) - user.show_message("You feel rather silly, trying to commit suicide with a toy.") - mouthshoot = 0 - //Foam LMG /obj/item/gun/ballistic/automatic/lmg/foam name = "toy light machine gun" @@ -795,8 +787,3 @@ /obj/item/gun/ballistic/automatic/lmg/foam/update_icon() . = ..() update_held_icon() - -/obj/item/gun/ballistic/automatic/lmg/foam/handle_suicide(mob/living/user) - user.show_message("You feel rather silly, trying to commit suicide with a toy.") - mouthshoot = 0 - return diff --git a/code/modules/projectiles/guns/projectile/boltaction.dm b/code/modules/projectiles/guns/projectile/boltaction.dm index afe3233f7326..1106d7ea4a3b 100644 --- a/code/modules/projectiles/guns/projectile/boltaction.dm +++ b/code/modules/projectiles/guns/projectile/boltaction.dm @@ -50,7 +50,7 @@ // Stole hacky terrible code from doublebarrel shotgun. -Spades /obj/item/gun/ballistic/shotgun/pump/rifle/ceremonial/attackby(var/obj/item/A as obj, mob/user as mob) - if(istype(A, /obj/item/surgical/circular_saw) || istype(A, /obj/item/melee/energy) || istype(A, /obj/item/pickaxe/plasmacutter) && w_class != WEIGHT_CLASS_NORMAL) + if(istype(A, /obj/item/surgical/circular_saw) || istype(A, /obj/item/melee/transforming/energy) || istype(A, /obj/item/pickaxe/plasmacutter) && w_class != WEIGHT_CLASS_NORMAL) to_chat(user, "You begin to shorten the barrel and stock of \the [src].") if(loaded.len) afterattack(user, user) @@ -90,7 +90,7 @@ holy = TRUE /obj/item/gun/ballistic/shotgun/pump/rifle/lever/attackby(var/obj/item/A as obj, mob/user as mob) - if(istype(A, /obj/item/surgical/circular_saw) || istype(A, /obj/item/melee/energy) || istype(A, /obj/item/pickaxe/plasmacutter) && w_class != WEIGHT_CLASS_NORMAL) + if(istype(A, /obj/item/surgical/circular_saw) || istype(A, /obj/item/melee/transforming/energy) || istype(A, /obj/item/pickaxe/plasmacutter) && w_class != WEIGHT_CLASS_NORMAL) to_chat(user, "You begin to shorten the barrel and stock of \the [src].") if(loaded.len) afterattack(user, user) @@ -134,7 +134,7 @@ holy = TRUE /obj/item/gun/ballistic/shotgun/pump/rifle/lever/vintage/attackby(var/obj/item/A as obj, mob/user as mob) - if(istype(A, /obj/item/surgical/circular_saw) || istype(A, /obj/item/melee/energy) || istype(A, /obj/item/pickaxe/plasmacutter) && w_class != WEIGHT_CLASS_NORMAL) + if(istype(A, /obj/item/surgical/circular_saw) || istype(A, /obj/item/melee/transforming/energy) || istype(A, /obj/item/pickaxe/plasmacutter) && w_class != WEIGHT_CLASS_NORMAL) to_chat(user, "You begin to shorten the barrel and stock of \the [src].") if(loaded.len) afterattack(user, user) diff --git a/code/modules/projectiles/guns/projectile/pistol.dm b/code/modules/projectiles/guns/projectile/pistol.dm index 08476e331c76..c188758bb3ca 100644 --- a/code/modules/projectiles/guns/projectile/pistol.dm +++ b/code/modules/projectiles/guns/projectile/pistol.dm @@ -537,11 +537,6 @@ allowed_magazines = list(/obj/item/ammo_magazine/foam/pistol) fire_sound = 'sound/items/syringeproj.ogg' -/obj/item/gun/ballistic/pistol/foam/handle_suicide(mob/living/user) - user.show_message("You feel rather silly, trying to commit suicide with a toy.") - mouthshoot = 0 - return - /obj/item/gun/ballistic/pistol/foam/blue icon_state = "toy_pistol_blue" diff --git a/code/modules/projectiles/guns/projectile/shotgun.dm b/code/modules/projectiles/guns/projectile/shotgun.dm index 3aa3ab634014..96bd6b3fbd2f 100644 --- a/code/modules/projectiles/guns/projectile/shotgun.dm +++ b/code/modules/projectiles/guns/projectile/shotgun.dm @@ -187,7 +187,7 @@ //this is largely hacky and bad :( -Pete /obj/item/gun/ballistic/shotgun/doublebarrel/attackby(var/obj/item/A as obj, mob/user as mob) - if(istype(A, /obj/item/surgical/circular_saw) || istype(A, /obj/item/melee/energy) || istype(A, /obj/item/pickaxe/plasmacutter)) + if(istype(A, /obj/item/surgical/circular_saw) || istype(A, /obj/item/melee/transforming/energy) || istype(A, /obj/item/pickaxe/plasmacutter)) to_chat(user, "You begin to shorten the barrel of \the [src].") if(loaded.len) var/burstsetting = burst @@ -350,11 +350,6 @@ one_handed_penalty = 5 fire_sound = 'sound/items/syringeproj.ogg' -/obj/item/gun/ballistic/shotgun/pump/foam/handle_suicide(mob/living/user) - user.show_message("You feel rather silly, trying to commit suicide with a toy.") - mouthshoot = 0 - return - /obj/item/gun/ballistic/shotgun/pump/foam/pump(mob/M as mob) playsound(M, action_sound, 60, 1) diff --git a/code/modules/projectiles/guns/vox.dm b/code/modules/projectiles/guns/vox.dm index 53f3c262bc7a..ca21ee38d086 100644 --- a/code/modules/projectiles/guns/vox.dm +++ b/code/modules/projectiles/guns/vox.dm @@ -151,8 +151,11 @@ /obj/projectile/sonic/strong damage = 45 -/obj/projectile/sonic/strong/on_hit(var/atom/movable/target, var/blocked = 0) +/obj/projectile/sonic/strong/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(ismob(target)) + var/mob/M = target var/throwdir = get_dir(firer,target) - target.throw_at_old(get_edge_target_turf(target, throwdir), rand(1,6), 10) - return 1 + M.throw_at_old(get_edge_target_turf(target, throwdir), rand(1,6), 10) diff --git a/code/modules/projectiles/projectile-tracing.dm b/code/modules/projectiles/projectile-tracing.dm deleted file mode 100644 index dc6144a7735b..000000000000 --- a/code/modules/projectiles/projectile-tracing.dm +++ /dev/null @@ -1,51 +0,0 @@ - -//* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2024 Citadel Station developers. *// - -/obj/projectile/trace - atom_flags = ATOM_ABSTRACT | ATOM_NONWORLD - invisibility = INVISIBILITY_ABSTRACT - hitscan = TRUE - has_tracer = FALSE - damage = 0 - nodamage = TRUE - /// did we manage to hit the given target? - var/could_hit_target = FALSE - /// delete on hitting target? - var/del_on_success = FALSE - /// do we check opacity? - var/check_opacity = FALSE - -/obj/projectile/trace/Bump(atom/A) - if(A == original) - could_hit_target = TRUE - if(del_on_success) - qdel(src) - return - return ..() - -/obj/projectile/trace/projectile_attack_mob() - return - -/obj/projectile/trace/Moved() - . = ..() - if(check_opacity && isturf(loc)) - // *sigh* // - var/turf/T = loc - if(T.has_opaque_atom) - qdel(src) - -/obj/projectile/trace/proc/prepare_trace(atom/target, pass_flags = ATOM_PASS_GLASS | ATOM_PASS_GRILLE | ATOM_PASS_TABLE, check_opacity) - src.pass_flags = pass_flags - src.original = target - src.check_opacity = check_opacity - src.range = max(src.range, (get_dist(src, target) + 1) * WORLD_ICON_SIZE) - -/** - * Simple trace to see if we could hit a given target - */ -/obj/projectile/trace/proc/simple_trace(atom/target, angle) - prepare_trace(target) - fire(angle) - del_on_success = TRUE - return could_hit_target diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm deleted file mode 100644 index ac7164dcc9df..000000000000 --- a/code/modules/projectiles/projectile.dm +++ /dev/null @@ -1,1320 +0,0 @@ -/** - * ## Physics Specifications - * - * We track physics as absolute pixel on a tile, not byond's pixel x/y - * thus the first pixel at bottom left of tile is 1, 1 - * and the last pixel at top right is 32, 32 (for a world icon size of 32 pixels) - * - * We cross over to the next tile at above 32, for up/right, - * and to the last tile at below 1, for bottom/left. - * - * The code might handle it based on how it's implemented, - * but as long as the error is 1 pixel or below, it's not a big deal. - * - * The reason we're so accurate (1 pixel/below is pretty insanely strict) is - * so players have the projectile act like what the screen says it should be like; - * hence why projectiles can realistically path across corners based on their 'hitbox center'. - */ -/obj/projectile - name = "projectile" - icon = 'icons/obj/projectiles.dmi' - icon_state = "bullet" - density = FALSE - anchored = TRUE - integrity_flags = INTEGRITY_INDESTRUCTIBLE | INTEGRITY_ACIDPROOF | INTEGRITY_FIREPROOF | INTEGRITY_LAVAPROOF - pass_flags = ATOM_PASS_TABLE - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - depth_level = INFINITY // nothing should be passing over us from depth - - //* Physics - Configuration *// - - /// speed, in pixels per second - var/speed = 25 * WORLD_ICON_SIZE - /// are we a hitscan projectile? - var/hitscan = FALSE - /// angle, in degrees **clockwise of north** - var/angle - /// max distance in pixels - /// - /// * please set this to a multiple of [WORLD_ICON_SIZE] so we scale with tile size. - var/range = WORLD_ICON_SIZE * 50 - // todo: lifespan - - //* Physics - Homing *// - /// Are we homing in on something? - var/homing = FALSE - /// current homing target - var/atom/homing_target - /// angle per second - /// - /// * this is smoother the less time between SSprojectiles fires - var/homing_turn_speed = 100 - /// rand(min, max) for inaccuracy offsets - var/homing_inaccuracy_min = 0 - /// rand(min, max) for inaccuracy offsets - var/homing_inaccuracy_max = 0 - /// pixels; added to the real location of target so we're not exactly on-point - var/homing_offset_x = 0 - /// pixels; added to the real location of target so we're not exactly on-point - var/homing_offset_y = 0 - - //* Physics - Tracers *// - - /// tracer /datum/point's - var/list/tracer_vertices - /// first point is a muzzle effect - var/tracer_muzzle_flash - /// last point is an impact - var/tracer_impact_effect - /// default tracer duration - var/tracer_duration = 5 - - //* Physics - State *// - - /// paused? if so, we completely get passed over during processing - var/paused = FALSE - /// currently hitscanning - var/hitscanning = FALSE - /// a flag to prevent movement hooks from resetting our physics on a forced movement - var/trajectory_ignore_forcemove = FALSE - /// cached value: move this much x for this much distance - /// basically, dx / distance - var/calculated_dx - /// cached value: move this much y for this much distance - /// basically, dy / distance - var/calculated_dy - /// cached sign of dx; 1, -1, or 0 - var/calculated_sdx - /// cached sign of dy; 1, -1, or 0 - var/calculated_sdy - /// our current pixel location on turf - /// byond pixel_x rounds, and we don't want that - /// - /// * at below 0 or at equals to WORLD_ICON_SIZE, we move to the next turf - var/current_px - /// our current pixel location on turf - /// byond pixel_y rounds, and we don't want that - /// - /// * at below 0 or at equals to WORLD_ICON_SIZE, we move to the next turf - var/current_py - /// the pixel location we're moving to, or the [current_px] after this iteration step - /// - /// * used so stuff like hitscan deflections work based on the actual raycasted collision step, and not the prior step. - var/next_px - /// the pixel location we're moving to, or the [current_px] after this iteration step - /// - /// * used so stuff like hitscan deflections work based on the actual raycasted collision step, and not the prior step. - var/next_py - /// used to track if we got kicked forwards after calling Move() - var/trajectory_kick_forwards = 0 - /// to avoid going too fast when kicked forwards by a mirror, if we overshoot the pixels we're - /// supposed to move this gets set to penalize the next move with a weird algorithm - /// that i won't bother explaining - var/trajectory_penalty_applied = 0 - /// currently travelled distance in pixels - var/distance_travelled - /// if we get forcemoved, this gets reset to 0 as a trip - /// this way, we know exactly how far we moved - var/distance_travelled_this_iteration - /// where the physics loop and/or some other thing moving us is trying to move to - /// used to determine where to draw hitscan tracers - // todo: this being here is kinda a symptom that things are handled weirdly but whatever - // optimally physics loop should handle tracking for stuff like animations, not require on hit processing to check turfs - var/turf/trajectory_moving_to - - //Fired processing vars - var/fired = FALSE //Have we been fired yet - var/ignore_source_check = FALSE - - var/original_angle = 0 //Angle at firing - var/nondirectional_sprite = FALSE //Set TRUE to prevent projectiles from having their sprites rotated based on firing angle - var/spread = 0 //amount (in degrees) of projectile spread - animate_movement = 0 //Use SLIDE_STEPS in conjunction with legacy - var/ricochets = 0 - var/ricochets_max = 2 - var/ricochet_chance = 30 - - //Hitscan - /// do we have a tracer? if not we completely ignore hitscan logic - var/has_tracer = TRUE - var/tracer_type - var/muzzle_type - var/impact_type - - var/miss_sounds - var/ricochet_sounds - var/list/impact_sounds //for different categories, IMPACT_MEAT etc - - //Fancy hitscan lighting effects! - var/hitscan_light_intensity = 1.5 - var/hitscan_light_range = 0.75 - var/hitscan_light_color_override - var/muzzle_flash_intensity = 3 - var/muzzle_flash_range = 1.5 - var/muzzle_flash_color_override - var/impact_light_intensity = 3 - var/impact_light_range = 2 - var/impact_light_color_override - - //Targetting - var/yo = null - var/xo = null - var/atom/original = null // the original target clicked - var/turf/starting = null // the projectile's starting turf - var/list/permutated = list() // we've passed through these atoms, don't try to hit them again - var/p_x = 16 - var/p_y = 16 // the pixel location of the tile that the player clicked. Default is the center - - var/def_zone = "" //Aiming at - var/mob/firer = null//Who shot it - var/silenced = 0 //Attack message - var/shot_from = "" // name of the object which shot us - - var/accuracy = 0 - var/dispersion = 0.0 - - // Sub-munitions. Basically, multi-projectile shotgun, rather than pellets. - var/use_submunitions = FALSE - var/only_submunitions = FALSE // Will the projectile delete itself after firing the submunitions? - var/list/submunitions = list() // Assoc list of the paths of any submunitions, and how many they are. [projectilepath] = [projectilecount]. - var/submunition_spread_max = 30 // Divided by 10 to get the percentile dispersion. - var/submunition_spread_min = 5 // Above. - /// randomize spread? if so, evenly space between 0 and max on each side. - var/submunition_constant_spread = FALSE - var/force_max_submunition_spread = FALSE // Do we just force the maximum? - var/spread_submunition_damage = FALSE // Do we assign damage to our sub projectiles based on our main projectile damage? - - //? Damage - default handling - /// damage amount - var/damage = 10 - /// damage tier - goes hand in hand with [damage_armor] - var/damage_tier = BULLET_TIER_DEFAULT - /// todo: legacy - BRUTE, BURN, TOX, OXY, CLONE, HALLOSS, ELECTROCUTE, BIOACID are the only things that should be in here - var/damage_type = BRUTE - /// armor flag for damage - goes hand in hand with [damage_tier] - var/damage_flag = ARMOR_BULLET - /// damage mode - see [code/__DEFINES/combat/damage.dm] - var/damage_mode = NONE - - var/SA_bonus_damage = 0 // Some bullets inflict extra damage on simple animals. - var/SA_vulnerability = null // What kind of simple animal the above bonus damage should be applied to. Set to null to apply to all SAs. - var/nodamage = 0 //Determines if the projectile will skip any damage inflictions - var/taser_effect = 0 //If set then the projectile will apply it's agony damage using stun_effect_act() to mobs it hits, and other damage will be ignored - var/projectile_type = /obj/projectile - var/penetrating = 0 //If greater than zero, the projectile will pass through dense objects as specified by on_penetrate() - //Effects - var/incendiary = 0 //1 for ignite on hit, 2 for trail of fire. 3 maybe later for burst of fire around the impact point. - Mech - var/flammability = 0 //Amount of fire stacks to add for the above. - var/combustion = TRUE //Does this set off flammable objects on fire/hit? - var/stun = 0 - var/weaken = 0 - var/paralyze = 0 - var/irradiate = 0 - var/stutter = 0 - var/eyeblur = 0 - var/drowsy = 0 - var/agony = 0 - var/reflected = 0 // This should be set to 1 if reflected by any means, to prevent infinite reflections. - var/modifier_type_to_apply = null // If set, will apply a modifier to mobs that are hit by this projectile. - var/modifier_duration = null // How long the above modifier should last for. Leave null to be permanent. - var/excavation_amount = 0 // How much, if anything, it drills from a mineral turf. - /// If this projectile is holy. Silver bullets, etc. Currently no effects. - var/holy = FALSE - - // Antimagic - /// Should we check for antimagic effects? - var/antimagic_check = FALSE - /// Antimagic charges to use, if any - var/antimagic_charges_used = 0 - /// Multiplier for damage if antimagic is on the target - var/antimagic_damage_factor = 0 - - var/embed_chance = 0 //Base chance for a projectile to embed - - var/fire_sound = 'sound/weapons/Gunshot_old.ogg' // Can be overriden in gun.dm's fire_sound var. It can also be null but I don't know why you'd ever want to do that. -Ace - - // todo: currently unimplemneted - var/vacuum_traversal = TRUE //Determines if the projectile can exist in vacuum, if false, the projectile will be deleted if it enters vacuum. - - var/no_attack_log = FALSE - -/obj/projectile/Destroy() - // stop processing - STOP_PROCESSING(SSprojectiles, src) - // cleanup - cleanup_hitscan_tracers() - return ..() - -/obj/projectile/proc/legacy_on_range() //if we want there to be effects when they reach the end of their range - finalize_hitscan_tracers(impact_effect = FALSE, kick_forwards = 8) - qdel(src) - -/obj/projectile/Crossed(atom/movable/AM) //A mob moving on a tile with a projectile is hit by it. - if(AM.is_incorporeal()) - return - ..() - if(isliving(AM) && !check_pass_flags(ATOM_PASS_MOB)) - var/mob/living/L = AM - if(can_hit_target(L, permutated, (AM == original))) - Bump(AM) - -/obj/projectile/forceMove(atom/target) - var/is_a_jump = isturf(target) != isturf(loc) || target.z != z || !trajectory_ignore_forcemove - if(is_a_jump) - record_hitscan_end() - render_hitscan_tracers() - . = ..() - if(!.) - stack_trace("projectile forcemove failed; please do not try to forcemove projectiles to invalid locations!") - distance_travelled_this_iteration = 0 - if(!trajectory_ignore_forcemove) - reset_physics_to_turf() - if(is_a_jump) - record_hitscan_start() - -/obj/projectile/proc/fire(set_angle_to, atom/direct_target) - if(only_submunitions) // refactor projectiles whwen holy shit this is awful lmao - // todo: this should make a muzzle flash - qdel(src) - return - //If no angle needs to resolve it from xo/yo! - if(direct_target) - direct_target.bullet_act(src, def_zone) - // todo: this should make a muzzle flash - qdel(src) - return - if(isnum(set_angle_to)) - set_angle(set_angle_to) - - // setup physics - setup_physics() - - var/turf/starting = get_turf(src) - if(isnull(angle)) //Try to resolve through offsets if there's no angle set. - if(isnull(xo) || isnull(yo)) - stack_trace("WARNING: Projectile [type] deleted due to being unable to resolve a target after angle was null!") - qdel(src) - return - var/turf/target = locate(clamp(starting + xo, 1, world.maxx), clamp(starting + yo, 1, world.maxy), starting.z) - set_angle(get_visual_angle(src, target)) - if(dispersion) - set_angle(angle + rand(-dispersion, dispersion)) - original_angle = angle - forceMove(starting) - permutated = list() - fired = TRUE - // kickstart - if(hitscan) - physics_hitscan() - else - START_PROCESSING(SSprojectiles, src) - physics_iteration(WORLD_ICON_SIZE, SSprojectiles.wait) - -/obj/projectile/Move(atom/newloc, dir = NONE) - . = ..() - if(.) - if(fired && can_hit_target(original, permutated, TRUE)) - Bump(original) - -//Spread is FORCED! -/obj/projectile/proc/preparePixelProjectile(atom/target, atom/source, params, spread = 0) - var/turf/curloc = get_turf(source) - var/turf/targloc = get_turf(target) - - if(istype(source, /atom/movable)) - var/atom/movable/MT = source - if(MT.locs && MT.locs.len) // Multi tile! - for(var/turf/T in MT.locs) - if(get_dist(T, target) < get_turf(curloc)) - curloc = get_turf(T) - - trajectory_ignore_forcemove = TRUE - forceMove(get_turf(source)) - trajectory_ignore_forcemove = FALSE - starting = curloc - original = target - if(targloc || !params) - yo = targloc.y - curloc.y - xo = targloc.x - curloc.x - set_angle(get_visual_angle(src, targloc) + spread) - - if(isliving(source) && params) - var/list/calculated = calculate_projectile_angle_and_pixel_offsets(source, params) - p_x = calculated[2] - p_y = calculated[3] - - set_angle(calculated[1] + spread) - else if(targloc) - yo = targloc.y - curloc.y - xo = targloc.x - curloc.x - set_angle(get_visual_angle(src, targloc) + spread) - else - stack_trace("WARNING: Projectile [type] fired without either mouse parameters, or a target atom to aim at!") - qdel(src) - -/proc/calculate_projectile_angle_and_pixel_offsets(mob/user, params) - var/list/mouse_control = params2list(params) - var/p_x = 0 - var/p_y = 0 - var/angle = 0 - if(mouse_control["icon-x"]) - p_x = text2num(mouse_control["icon-x"]) - if(mouse_control["icon-y"]) - p_y = text2num(mouse_control["icon-y"]) - if(mouse_control["screen-loc"]) - //Split screen-loc up into X+Pixel_X and Y+Pixel_Y - var/list/screen_loc_params = splittext(mouse_control["screen-loc"], ",") - - //Split X+Pixel_X up into list(X, Pixel_X) - var/list/screen_loc_X = splittext(screen_loc_params[1],":") - - //Split Y+Pixel_Y up into list(Y, Pixel_Y) - var/list/screen_loc_Y = splittext(screen_loc_params[2],":") - var/x = text2num(screen_loc_X[1]) * 32 + text2num(screen_loc_X[2]) - 32 - var/y = text2num(screen_loc_Y[1]) * 32 + text2num(screen_loc_Y[2]) - 32 - - //Calculate the "resolution" of screen based on client's view and world's icon size. This will work if the user can view more tiles than average. - var/screenviewX = user.client.current_viewport_width * world.icon_size - var/screenviewY = user.client.current_viewport_height * world.icon_size - - var/ox = round(screenviewX/2) - user.client.pixel_x //"origin" x - var/oy = round(screenviewY/2) - user.client.pixel_y //"origin" y - angle = arctan(y - oy, x - ox) - return list(angle, p_x, p_y) - -/obj/projectile/proc/redirect(x, y, starting, source) - old_style_target(locate(x, y, z), starting? get_turf(starting) : get_turf(source)) - -/obj/projectile/proc/old_style_target(atom/target, atom/source) - if(!source) - source = get_turf(src) - starting = get_turf(source) - original = target - set_angle(get_visual_angle(source, target)) - -/obj/projectile/proc/vol_by_damage() - if(damage) - return clamp((damage) * 0.67, 30, 100)// Multiply projectile damage by 0.67, then clamp the value between 30 and 100 - else - return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume. - -//Returns true if the target atom is on our current turf and above the right layer -//If direct target is true it's the originally clicked target. -/obj/projectile/proc/can_hit_target(atom/target, list/passthrough, direct_target = FALSE, ignore_loc = FALSE) - if(QDELETED(target)) - return FALSE - if(!ignore_source_check && firer) - var/mob/M = firer - if((target == firer) || ((target == firer.loc) && istype(firer.loc, /obj/vehicle/sealed/mecha)) || (target in firer.buckled_mobs) || (istype(M) && (M.buckled == target))) - return FALSE - if(!ignore_loc && (loc != target.loc)) - return FALSE - if(target in passthrough) - return FALSE - if(target.density) //This thing blocks projectiles, hit it regardless of layer/mob stuns/etc. - return TRUE - if(!isliving(target)) - if(direct_target) - return TRUE - if(target.layer < PROJECTILE_HIT_THRESHOLD_LAYER) - return FALSE - else - var/mob/living/L = target - if(!direct_target) - if(!L.density) - return FALSE - return TRUE - -/obj/projectile/Bump(atom/A) - if(A in permutated) - trajectory_ignore_forcemove = TRUE - forceMove(get_turf(A)) - trajectory_ignore_forcemove = FALSE - return FALSE - if(firer && !reflected) - if(A == firer || (A == firer.loc && istype(A, /obj/vehicle/sealed/mecha))) //cannot shoot yourself or your mech - trajectory_ignore_forcemove = TRUE - forceMove(get_turf(A)) - trajectory_ignore_forcemove = FALSE - return FALSE - - var/distance = get_dist(starting, get_turf(src)) - var/turf/target_turf = get_turf(A) - var/passthrough = FALSE - - if(ismob(A)) - var/mob/M = A - if(istype(A, /mob/living)) - //if they have a neck grab on someone, that person gets hit instead - var/obj/item/grab/G = locate() in M - if(G && G.state >= GRAB_NECK) - if(G.affecting.stat == DEAD) - var/shield_chance = min(80, (30 * (M.mob_size / 10))) //Small mobs have a harder time keeping a dead body as a shield than a human-sized one. Unathi would have an easier job, if they are made to be SIZE_LARGE in the future. -Mech - if(prob(shield_chance)) - visible_message("\The [M] uses [G.affecting] as a shield!") - if(Bump(G.affecting)) - return - else - visible_message("\The [M] tries to use [G.affecting] as a shield, but fails!") - else - visible_message("\The [M] uses [G.affecting] as a shield!") - if(Bump(G.affecting)) - return //If Bump() returns 0 (keep going) then we continue on to attack M. - - passthrough = !projectile_attack_mob(M, distance) - else - passthrough = 1 //so ghosts don't stop bullets - else - passthrough = (A.bullet_act(src, def_zone) == PROJECTILE_CONTINUE) //backwards compatibility - if(isturf(A)) - for(var/obj/O in A) - O.bullet_act(src) - for(var/mob/living/M in A) - projectile_attack_mob(M, distance) - - //penetrating projectiles can pass through things that otherwise would not let them - if(!passthrough && penetrating > 0) - if(check_penetrate(A)) - passthrough = TRUE - penetrating-- - - if(passthrough) - trajectory_ignore_forcemove = TRUE - forceMove(target_turf) - permutated.Add(A) - trajectory_ignore_forcemove = FALSE - return FALSE - - if(A) - on_impact(A) - - if(hitscanning) - if(trajectory_moving_to) - // create tracers - var/datum/point/visual_impact_point = get_intersection_point(trajectory_moving_to) - if(visual_impact_point) - // kick it forwards a bit - visual_impact_point.shift_in_projectile_angle(angle, 2) - // draw - finalize_hitscan_tracers(visual_impact_point, impact_effect = TRUE) - else - finalize_hitscan_tracers(impact_effect = TRUE, kick_forwards = 32) - else - finalize_hitscan_tracers(impact_effect = TRUE, kick_forwards = 32) - - qdel(src) - return TRUE - -//TODO: make it so this is called more reliably, instead of sometimes by bullet_act() and sometimes not -/obj/projectile/proc/on_hit(atom/target, blocked = 0, def_zone) - if(blocked >= 100) - return 0//Full block - if(!isliving(target)) - return 0 -// if(isanimal(target)) return 0 - var/mob/living/L = target - L.apply_effects(stun, weaken, paralyze, irradiate, stutter, eyeblur, drowsy, agony, blocked, incendiary, flammability) // add in AGONY! - if(modifier_type_to_apply) - L.add_modifier(modifier_type_to_apply, modifier_duration) - return 1 - -//called when the projectile stops flying because it Bump'd with something -/obj/projectile/proc/on_impact(atom/A) - if(damage && damage_type == BURN) - var/turf/T = get_turf(A) - if(T) - T.hotspot_expose(700, 5) - -//Checks if the projectile is eligible for embedding. Not that it necessarily will. -/obj/projectile/proc/can_embed() - //embed must be enabled and damage type must be brute - if(embed_chance == 0 || damage_type != BRUTE) - return 0 - return 1 - -/obj/projectile/proc/get_structure_damage() - if(damage_type == BRUTE || damage_type == BURN) - return damage - return 0 - -//return 1 if the projectile should be allowed to pass through after all, 0 if not. -/obj/projectile/proc/check_penetrate(atom/A) - return 1 - -/obj/projectile/proc/check_fire(atom/target as mob, mob/living/user as mob) //Checks if you can hit them or not. - check_trajectory(target, user, pass_flags, atom_flags) - -/obj/projectile/CanAllowThrough() - . = ..() - return TRUE - -//Called when the projectile intercepts a mob. Returns 1 if the projectile hit the mob, 0 if it missed and should keep flying. -/obj/projectile/proc/projectile_attack_mob(mob/living/target_mob, distance, miss_modifier = 0) - if(!istype(target_mob)) - return - - //roll to-hit - miss_modifier = max(15*(distance-2) - accuracy + miss_modifier + target_mob.get_evasion(), 0) - var/hit_zone = get_zone_with_miss_chance(def_zone, target_mob, miss_modifier, ranged_attack=(distance > 1 || original != target_mob)) //if the projectile hits a target we weren't originally aiming at then retain the chance to miss - - var/result = PROJECTILE_FORCE_MISS - if(hit_zone) - def_zone = hit_zone //set def_zone, so if the projectile ends up hitting someone else later (to be implemented), it is more likely to hit the same part - result = target_mob.bullet_act(src, def_zone) - - if(result == PROJECTILE_FORCE_MISS) - if(!silenced) - visible_message("\The [src] misses [target_mob] narrowly!") - playsound(target_mob.loc, pick(miss_sounds), 60, 1) - return FALSE - - //hit messages - if(silenced) - to_chat(target_mob, "You've been hit in the [parse_zone(def_zone)] by \the [src]!") - else - visible_message("\The [target_mob] is hit by \the [src] in the [parse_zone(def_zone)]!")//X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter - - //admin logs - if(!no_attack_log) - if(istype(firer, /mob) && istype(target_mob)) - add_attack_logs(firer,target_mob,"Shot with \a [src.type] projectile") - - //sometimes bullet_act() will want the projectile to continue flying - if (result == PROJECTILE_CONTINUE) - return FALSE - - return TRUE - -/** - * i hate everything - * - * todo: refactor guns - * projectiles - * and everything else - * - * i am losing my fucking mind - * this shouldn't have to fucking exist because the ammo casing and/or gun should be doing it - * and submunitions SHOULDNT BE HANDLED HERE!! - */ -/obj/projectile/proc/launch_projectile_common(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0) - original = target - def_zone = check_zone(target_zone) - firer = user - - if(use_submunitions && submunitions.len) - var/temp_min_spread = 0 - if(force_max_submunition_spread) - temp_min_spread = submunition_spread_max - else - temp_min_spread = submunition_spread_min - - var/damage_override = null - - if(spread_submunition_damage) - damage_override = damage - if(nodamage) - damage_override = 0 - - var/projectile_count = 0 - - for(var/proj in submunitions) - projectile_count += submunitions[proj] - - damage_override = round(damage_override / max(1, projectile_count)) - - for(var/path in submunitions) - var/amt = submunitions[path] - for(var/count in 1 to amt) - var/obj/projectile/SM = new path(get_turf(loc)) - SM.shot_from = shot_from - SM.silenced = silenced - if(!isnull(damage_override)) - SM.damage = damage_override - if(submunition_constant_spread) - SM.dispersion = 0 - var/calculated = angle + round((count / amt - 0.5) * submunition_spread_max, 1) - SM.launch_projectile(target, target_zone, user, params, calculated) - else - SM.dispersion = rand(temp_min_spread, submunition_spread_max) / 10 - SM.launch_projectile(target, target_zone, user, params, angle_override) - -/obj/projectile/proc/launch_projectile(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0) - var/direct_target - if(get_turf(target) == get_turf(src)) - direct_target = target - - preparePixelProjectile(target, user? user : get_turf(src), params, forced_spread) - launch_projectile_common(target, target_zone, user, params, angle_override, forced_spread) - return fire(angle_override, direct_target) - -//called to launch a projectile from a gun -/obj/projectile/proc/launch_from_gun(atom/target, target_zone, mob/user, params, angle_override, forced_spread, obj/item/gun/launcher) - - shot_from = launcher.name - silenced = launcher.silenced - - return launch_projectile(target, target_zone, user, params, angle_override, forced_spread) - -/obj/projectile/proc/launch_projectile_from_turf(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0) - var/direct_target - if(get_turf(target) == get_turf(src)) - direct_target = target - - preparePixelProjectile(target, user? user : get_turf(src), params, forced_spread) - launch_projectile_common(target, target_zone, user, params, angle_override, forced_spread) - return fire(angle_override, direct_target) - -/** - * Standard proc to determine damage when impacting something. This does not affect the special damage variables/effect variables, only damage and damtype. - * May or may not be called before/after armor calculations. - * - * @params - * - target The atom hit - * - * @return Damage to apply to target. - */ -/obj/projectile/proc/run_damage_vulnerability(atom/target) - var/final_damage = damage - if(isliving(target)) - var/mob/living/L = target - if(issimple(target)) - var/mob/living/simple_mob/SM = L - if(SM.mob_class & SA_vulnerability) - final_damage += SA_bonus_damage - if(L.anti_magic_check(TRUE, TRUE, antimagic_charges_used, FALSE)) - final_damage *= antimagic_damage_factor - return final_damage - -/** - * Probably isn't needed but saves me the time and I can regex this later: - * Gets the final `damage` that should be used on something - */ -/obj/projectile/proc/get_final_damage(atom/target) - return run_damage_vulnerability(target) - -//* Hitscan Visuals *// - -/** - * returns a /datum/point based on where we currently are - */ -/obj/projectile/proc/get_tracer_point() - RETURN_TYPE(/datum/point) - var/datum/point/point = new - if(trajectory_moving_to) - // we're in move. use next px/py to respect 1. kick forwards 2. deflections - point.x = (trajectory_moving_to.x - 1) * WORLD_ICON_SIZE + next_px - point.y = (trajectory_moving_to.y - 1) * WORLD_ICON_SIZE + next_py - else - point.x = (x - 1) * WORLD_ICON_SIZE + current_px - point.y = (y - 1) * WORLD_ICON_SIZE + current_py - point.z = z - return point - -/** - * * returns a /datum/point based on where we'll be when we loosely intersect a tile - * * returns null if we'll never intersect it - * * returns our current point if we're already loosely intersecting it - * * loosely intersecting means that we are level with the tile in either x or y. - */ -/obj/projectile/proc/get_intersection_point(turf/colliding) - RETURN_TYPE(/datum/point) - ASSERT(!isnull(angle)) - - // calculate hwere we are - var/our_x = (x - 1) * WORLD_ICON_SIZE + current_px - var/our_y = (y - 1) * WORLD_ICON_SIZE + current_py - - // calculate how far we have to go to touch their closest x / y axis - var/d_to_reach_x - var/d_to_reach_y - - if(colliding.x != x) - switch(calculated_sdx) - if(0) - return - if(1) - if(colliding.x < x) - return - d_to_reach_x = (((colliding.x - 1) * WORLD_ICON_SIZE + 0.5) - our_x) / calculated_dx - if(-1) - if(colliding.x > x) - return - d_to_reach_x = (((colliding.x - 0) * WORLD_ICON_SIZE + 0.5) - our_x) / calculated_dx - else - d_to_reach_x = 0 - - if(colliding.y != y) - switch(calculated_sdy) - if(0) - return - if(1) - if(colliding.y < y) - return - d_to_reach_y = (((colliding.y - 1) * WORLD_ICON_SIZE + 0.5) - our_y) / calculated_dy - if(-1) - if(colliding.y > y) - return - d_to_reach_y = (((colliding.y - 0) * WORLD_ICON_SIZE + 0.5) - our_y) / calculated_dy - else - d_to_reach_y = 0 - - var/needed_distance = max(d_to_reach_x, d_to_reach_y) - - // calculate if we'll actually be touching the tile once we go that far - var/future_x = our_x + needed_distance * calculated_dx - var/future_y = our_y + needed_distance * calculated_dy - // let's be slightly lenient and do 1 instead of 0.5 - if(future_x < (colliding.x - 1) * WORLD_ICON_SIZE && future_x > (colliding.x) * WORLD_ICON_SIZE + 1 && \ - future_y < (colliding.y - 1) * WORLD_ICON_SIZE && future_y > (colliding.y) * WORLD_ICON_SIZE + 1) - return // not gonna happen - - // make the point based on how far we need to go - var/datum/point/point = new - point.x = future_x - point.y = future_y - point.z = z - return point - -/** - * records the start of a hitscan - * - * this can edit the point passed in! - */ -/obj/projectile/proc/record_hitscan_start(datum/point/point, muzzle_marker, kick_forwards) - if(!hitscanning) - return - if(isnull(point)) - point = get_tracer_point() - tracer_vertices = list(point) - tracer_muzzle_flash = muzzle_marker - - // kick forwards - point.shift_in_projectile_angle(angle, kick_forwards) - -/** - * ends the hitscan tracer - * - * this can edit the point passed in! - */ -/obj/projectile/proc/record_hitscan_end(datum/point/point, impact_marker, kick_forwards) - if(!hitscanning) - return - if(isnull(point)) - point = get_tracer_point() - tracer_vertices += point - tracer_impact_effect = impact_marker - - // kick forwards - point.shift_in_projectile_angle(angle, kick_forwards) - -/** - * records a deflection (change in angle, aka generate new tracer) - */ -/obj/projectile/proc/record_hitscan_deflection(datum/point/point) - if(!hitscanning) - return - if(isnull(point)) - point = get_tracer_point() - // there's no way you need more than 25 - // if this is hit, fix your shit, don't bump this up; there's absolutely no reason for example, - // to simulate reflectors working !!25!! times. - if(length(tracer_vertices) >= 25) - CRASH("tried to add more than 25 vertices to a hitscan tracer") - tracer_vertices += point - -/obj/projectile/proc/render_hitscan_tracers(duration = tracer_duration) - // don't stay too long - ASSERT(duration >= 0 && duration <= 10 SECONDS) - // check everything - if(!has_tracer || !duration || !length(tracer_vertices)) - return - var/list/atom/movable/beam_components = list() - - // muzzle - if(muzzle_type && tracer_muzzle_flash) - var/datum/point/starting = tracer_vertices[1] - var/atom/movable/muzzle_effect = starting.instantiate_movable_with_unmanaged_offsets(muzzle_type) - if(muzzle_effect) - // turn it - var/matrix/muzzle_transform = matrix() - muzzle_transform.Turn(original_angle) - muzzle_effect.transform = muzzle_transform - muzzle_effect.color = color - muzzle_effect.set_light(muzzle_flash_range, muzzle_flash_intensity, muzzle_flash_color_override? muzzle_flash_color_override : color) - // add to list - beam_components += muzzle_effect - // impact - if(impact_type && tracer_impact_effect) - var/datum/point/starting = tracer_vertices[length(tracer_vertices)] - var/atom/movable/impact_effect = starting.instantiate_movable_with_unmanaged_offsets(impact_type) - if(impact_effect) - // turn it - var/matrix/impact_transform = matrix() - impact_transform.Turn(angle) - impact_effect.transform = impact_transform - impact_effect.color = color - impact_effect.set_light(impact_light_range, impact_light_intensity, impact_light_color_override? impact_light_color_override : color) - // add to list - beam_components += impact_effect - // path tracers - if(tracer_type) - var/tempref = "\ref[src]" - for(var/i in 1 to length(tracer_vertices) - 1) - var/j = i + 1 - var/datum/point/first_point = tracer_vertices[i] - var/datum/point/second_point = tracer_vertices[j] - generate_tracer_between_points(first_point, second_point, beam_components, tracer_type, color, duration, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity, tempref) - - QDEL_LIST_IN(beam_components, duration) - - -/obj/projectile/proc/cleanup_hitscan_tracers() - tracer_vertices=null - -/obj/projectile/proc/finalize_hitscan_tracers(datum/point/end_point, impact_effect, kick_forwards) - // if end wasn't recorded yet and we're still on a turf, record end - if(isnull(tracer_impact_effect) && loc) - record_hitscan_end(end_point, impact_marker = impact_effect, kick_forwards = kick_forwards) - // render & cleanup - render_hitscan_tracers() - cleanup_hitscan_tracers() - -//* Physics - Configuration *// - -/** - * sets our angle - */ -/obj/projectile/proc/set_angle(new_angle) - angle = new_angle - - // update sprite - if(!nondirectional_sprite) - var/matrix/M = new - M.Turn(angle) - transform = M - - // update trajectory - calculated_dx = sin(new_angle) - calculated_dy = cos(new_angle) - calculated_sdx = calculated_dx == 0? 0 : (calculated_dx > 0? 1 : -1) - calculated_sdy = calculated_dy == 0? 0 : (calculated_dy > 0? 1 : -1) - - // record our tracer's change - if(hitscanning) - record_hitscan_deflection() - -/** - * sets our speed in pixels per decisecond - */ -/obj/projectile/proc/set_speed(new_speed) - speed = clamp(new_speed, 1, WORLD_ICON_SIZE * 100) - -/** - * sets our angle and speed - */ -/obj/projectile/proc/set_velocity(new_angle, new_speed) - // this is so this can be micro-optimized later but for once i'm not going to do it early for no reason - set_speed(new_speed) - set_angle(new_angle) - -/** - * todo: this is somewhat mildly terrible - */ -/obj/projectile/proc/set_homing_target(atom/A) - if(!A || (!isturf(A) && !isturf(A.loc))) - return FALSE - homing = TRUE - homing_target = A - homing_offset_x = rand(homing_inaccuracy_min, homing_inaccuracy_max) - homing_offset_y = rand(homing_inaccuracy_min, homing_inaccuracy_max) - if(prob(50)) - homing_offset_x = -homing_offset_x - if(prob(50)) - homing_offset_y = -homing_offset_y - -/** - * initializes physics vars - */ -/obj/projectile/proc/setup_physics() - distance_travelled = 0 - -/** - * called after an unhandled forcemove is detected, or other event - * that should reset our on-turf state - */ -/obj/projectile/proc/reset_physics_to_turf() - // we use this because we can center larger than 32x32 projectiles - // without disrupting physics this way - // - // we add by (WORLD_ICON_SIZE / 2) because - // pixel_x / pixel_y starts at center, - // - current_px = pixel_x - base_pixel_x + (WORLD_ICON_SIZE / 2) - current_py = pixel_y - base_pixel_y + (WORLD_ICON_SIZE / 2) - // interrupt active move logic - trajectory_moving_to = null - -//* Physics - Processing *// - -/obj/projectile/process(delta_time) - if(paused) - return - physics_iteration(min(10 * WORLD_ICON_SIZE, delta_time * speed * SSprojectiles.global_projectile_speed_multiplier), delta_time) - -/** - * immediately processes hitscan - */ -/obj/projectile/proc/physics_hitscan(safety = 250, resuming) - // setup - if(!resuming) - hitscanning = TRUE - record_hitscan_start(muzzle_marker = TRUE, kick_forwards = 16) - - // just move as many times as we can - while(!QDELETED(src) && loc) - // check safety - safety-- - if(safety <= 0) - // if you're here, you shouldn't be. do not bump safety up, fix whatever - // you're doing because no one should be making projectiles go more than 250 - // tiles in a single life. - stack_trace("projectile hit iteration limit for hitscan") - break - - // move forwards by 1 tile length - distance_travelled += physics_step(WORLD_ICON_SIZE) - // if we're being yanked, yield - if(movable_flags & MOVABLE_IN_MOVED_YANK) - spawn(0) - physics_hitscan(safety, TRUE) - return - - // see if we're done - if(distance_travelled >= range) - legacy_on_range() - break - - hitscanning = FALSE - -/** - * ticks forwards a number of pixels - * - * todo: potential lazy animate support for performance, as we honestly don't need to animate at full fps if the server's above 20fps - * - * * delta_tiem is in deciseconds, not seconds. - */ -/obj/projectile/proc/physics_iteration(pixels, delta_time, additional_animation_length) - // setup iteration - var/safety = 15 - var/pixels_remaining = pixels - distance_travelled_this_iteration = 0 - - // apply penalty - var/penalizing = clamp(trajectory_penalty_applied, 0, pixels_remaining) - pixels_remaining -= penalizing - trajectory_penalty_applied -= penalizing - - // clamp to max distance - pixels_remaining = min(pixels_remaining, range - distance_travelled) - - // move as many times as we need to - // - // * break if we're loc = null (by deletion or otherwise) - // * break if we get paused - while(pixels_remaining > 0) - // check safety - safety-- - if(safety <= 0) - CRASH("ran out of safety! what happened?") - - // move - var/pixels_moved = physics_step(pixels_remaining) - distance_travelled += pixels_moved - distance_travelled_this_iteration += pixels_moved - pixels_remaining -= pixels_moved - // we're being yanked, yield - if(movable_flags & MOVABLE_IN_MOVED_YANK) - spawn(0) - physics_iteration(pixels_remaining, delta_time, distance_travelled_this_iteration) - return - if(!loc || paused) - break - - // penalize next one if we were kicked forwards forcefully too far - trajectory_penalty_applied = max(0, -pixels_remaining) - - // if we don't have a loc anymore just bail - if(!loc) - return - - // if we're at max range - if(distance_travelled >= range) - // todo: egh - legacy_on_range() - if(QDELETED(src)) - return - - // process homing - physics_tick_homing(delta_time) - - // perform animations - // we assume at this point any deflections that should have happened, has happened, - // so we just do a naive animate based on our current loc and pixel x/y - // - // todo: animation needs to take into account angle changes, - // but that's expensive as shit so uh lol - // - // the reason we use distance_travelled_this_iteration is so if something disappears - // by forceMove or whatnot, - // we won't have it bounce from its previous location to the new one as it's not going - // to be accurate anymore - // - // so instead, as of right now, we backtrack via how much we know we moved. - var/final_px = base_pixel_x + current_px - (WORLD_ICON_SIZE / 2) - var/final_py = base_pixel_y + current_py - (WORLD_ICON_SIZE / 2) - var/anim_dist = distance_travelled_this_iteration + additional_animation_length - pixel_x = final_px - (anim_dist * sin(angle)) - pixel_y = final_py - (anim_dist * cos(angle)) - - animate( - src, - delta_time, - flags = ANIMATION_END_NOW, - pixel_x = final_px, - pixel_y = final_py, - ) - -/** - * based on but exactly http://www.cs.yorku.ca/~amana/research/grid.pdf - * - * move into the next tile, or the specified number of pixels, - * whichever is less pixels moved - * - * this will modify our current_px/current_py as necessary - * - * @return pixels moved - */ -/obj/projectile/proc/physics_step(limit) - // distance to move in our angle to get to next turf for horizontal and vertical - var/d_next_horizontal = \ - (calculated_sdx? ((calculated_sdx > 0? (WORLD_ICON_SIZE + 0.5) - current_px : -current_px + 0.5) / calculated_dx) : INFINITY) - var/d_next_vertical = \ - (calculated_sdy? ((calculated_sdy > 0? (WORLD_ICON_SIZE + 0.5) - current_py : -current_py + 0.5) / calculated_dy) : INFINITY) - var/turf/move_to_target - - /** - * explanation on why current and next are done: - * - * projectiles track their pixel x/y on turf, not absolute pixel x/y from edge of map - * this is done to make it simpler to reason about, but is not necessarily the most simple - * or efficient way to do things. - * - * part of the problems with this approach is that Move() is not infallible. the projectile can be blocked. - * if we immediately set current pixel x/y, if the projectile is intercepted by a Bump, we now dont' know the 'real' - * position of the projectile because it's out of sync with where it should be - * - * now, things that require math operations on it don't know the actual location of the projectile until this proc - * rolls it back - * - * so instead, we never touch current px/py until the move is known to be successful, then we set it - * to the stored next px/py - * - * this way, things accessing can mutate our state freely without worrying about needing to handle rollbacks - * - * this entire system however adds overhead - * if we want to not have overhead, we'll need to rewrite hit processing and have it so moves are fully illegal to fail - * but doing that is literally not possible because anything can reject a move for any reason whatsoever - * and we cannot control that, so, instead, we make projectiles track in absolute pixel x/y coordinates from edge of map - * - * that way, we don't even need to care about where the .loc is, we just know where the projectile is supposed to be by - * knowing where it isn't, and by taking the change in its pixels the projectile controller can tell the projectile - * where to go- - * - * (all shitposting aside, this is for future work; it works right now and we have an API to do set angle, kick forwards, etc) - * (so i'm not going to touch this more because it's 4 AM and honestly this entire raycaster is already far less overhead) - * (than the old system of a 16-loop of brute forced 2 pixel increments) - */ - - if(d_next_horizontal == d_next_vertical) - // we're diagonal - if(d_next_horizontal <= limit) - move_to_target = locate(x + calculated_sdx, y + calculated_sdy, z) - . = d_next_horizontal - if(!move_to_target) - // we hit the world edge and weren't transit; time to get deleted. - if(hitscanning) - finalize_hitscan_tracers(impact_effect = FALSE) - qdel(src) - return - next_px = calculated_sdx > 0? 0.5 : (WORLD_ICON_SIZE + 0.5) - next_py = calculated_sdy > 0? 0.5 : (WORLD_ICON_SIZE + 0.5) - else if(d_next_horizontal < d_next_vertical) - // closer is to move left/right - if(d_next_horizontal <= limit) - move_to_target = locate(x + calculated_sdx, y, z) - . = d_next_horizontal - if(!move_to_target) - // we hit the world edge and weren't transit; time to get deleted. - if(hitscanning) - finalize_hitscan_tracers(impact_effect = FALSE) - qdel(src) - return - next_px = calculated_sdx > 0? 0.5 : (WORLD_ICON_SIZE + 0.5) - next_py = current_py + d_next_horizontal * calculated_dy - else if(d_next_vertical < d_next_horizontal) - // closer is to move up/down - if(d_next_vertical <= limit) - move_to_target = locate(x, y + calculated_sdy, z) - . = d_next_vertical - if(!move_to_target) - // we hit the world edge and weren't transit; time to get deleted. - if(hitscanning) - finalize_hitscan_tracers(impact_effect = FALSE) - qdel(src) - return - next_px = current_px + d_next_vertical * calculated_dx - next_py = calculated_sdy > 0? 0.5 : (WORLD_ICON_SIZE + 0.5) - - // if we need to move - if(move_to_target) - var/atom/old_loc = loc - trajectory_moving_to = move_to_target - if(!Move(move_to_target) && ((loc != move_to_target) || !trajectory_moving_to)) - // if we don't successfully move, don't change anything, we didn't move. - . = 0 - if(loc == old_loc) - stack_trace("projectile failed to move, but is still on turf instead of deleted or relocated.") - qdel(src) // bye - else - // only do these if we successfully move, or somehow end up in that turf anyways - if(trajectory_kick_forwards) - . += trajectory_kick_forwards - trajectory_kick_forwards = 0 - current_px = next_px - current_py = next_py - #ifdef CF_PROJECTILE_RAYCAST_VISUALS - new /atom/movable/render/projectile_raycast(move_to_target, current_px, current_py, "#77ff77") - #endif - trajectory_moving_to = null - else - // not moving to another tile, so, just move on current tile - if(trajectory_kick_forwards) - trajectory_kick_forwards = 0 - stack_trace("how did something kick us forwards when we didn't even move?") - . = limit - current_px += limit * calculated_dx - current_py += limit * calculated_dy - next_px = current_px - next_py = current_py - #ifdef CF_PROJECTILE_RAYCAST_VISUALS - new /atom/movable/render/projectile_raycast(loc, current_px, current_py, "#ff3333") - #endif - -#ifdef CF_PROJECTILE_RAYCAST_VISUALS -GLOBAL_VAR_INIT(projectile_raycast_debug_visual_delay, 2 SECONDS) - -/atom/movable/render/projectile_raycast - plane = OBJ_PLANE - icon = 'icons/system/color_32x32.dmi' - icon_state = "white-pixel" - -/** - * px, py are absolute pixel coordinates on the tile, not pixel_x / pixel_y of this renderer! - */ -/atom/movable/render/projectile_raycast/Initialize(mapload, px, py, color) - src.pixel_x = px - 1 - src.pixel_y = py - 1 - src.color = color - . = ..() - QDEL_IN(src, GLOB.projectile_raycast_debug_visual_delay) -#endif - -/** - * immediately, without processing, kicks us forward a number of pixels - * - * since we immediately cross over into a turf when entering, - * things like mirrors/reflectors will immediately set angle - * - * it looks ugly and is pretty bad to just reflect off the edge of a turf so said things can - * call this proc to kick us forwards by a bit - */ -/obj/projectile/proc/physics_kick_forwards(pixels) - trajectory_kick_forwards += pixels - next_px += pixels * calculated_dx - next_py += pixels * calculated_dy - -/** - * only works during non-hitscan - * - * this is called once per tick - * homing is smoother the higher fps the server / SSprojectiles runs at - * - * todo: this is somewhat mildly terrible - * todo: this has absolutely no arc/animation support; this is bad - */ -/obj/projectile/proc/physics_tick_homing(delta_time) - if(!homing) - return FALSE - // checks if they're 1. on a turf, 2. on our z - // todo: should we add support for tracking something even if it leaves a turf? - if(homing_target?.z != z) - // bye bye! - return FALSE - // todo: this assumes single-tile objects. at some point, we should upgrade this to be unnecessarily expensive and always center-mass. - var/dx = (homing_target.x - src.x) * WORLD_ICON_SIZE + (0 - current_px) + homing_offset_x - var/dy = (homing_target.y - src.y) * WORLD_ICON_SIZE + (0 - current_py) + homing_offset_y - // say it with me, arctan() - // is CCW of east if (dx, dy) - // and CW of north if (dy, dx) - // where dx and dy is distance in x/y pixels from us to them. - - var/nudge_towards = closer_angle_difference(arctan(dy, dx)) - var/max_turn_speed = homing_turn_speed * delta_time - - set_angle(angle + clamp(nudge_towards, -max_turn_speed, max_turn_speed)) - -//* Physics - Querying *// - -/** - * predict what turf we'll be in after going forwards a certain amount of pixels - * - * doesn't actually sim; so this will go through walls/obstacles! - * - * * if we go out of bounds, we will return null; this doesn't level-wrap - */ -/obj/projectile/proc/physics_predicted_turf_after_iteration(pixels) - // -1 at the end if 0, because: - // - // -32 is go back 1 tile and be at the 1st pixel (as 0 is going back) - // 0 is go back 1 tile and be at the 32nd pixel. - var/incremented_px = (current_px + pixels * calculated_dx) || - 1 - var/incremented_py = (current_py + pixels * calculated_dy) || - 1 - - var/incremented_tx = floor(incremented_px / 32) - var/incremented_ty = floor(incremented_py / 32) - - return locate(x + incremented_tx, y + incremented_ty, z) - -/** - * predict what turfs we'll hit, excluding the current turf, after going forwards - * a certain amount of pixels - * - * doesn't actually sim; so this will go through walls/obstacles! - */ -/obj/projectile/proc/physics_predicted_turfs_during_iteration(pixels) - return pixel_physics_raycast(loc, current_px, current_py, angle, pixels) - -//* Targeting *// - -/** - * Checks if something is a valid target when directly clicked. - */ -/obj/projectile/proc/is_valid_target(atom/target) - if(isobj(target)) - var/obj/O = target - return O.obj_flags & OBJ_RANGE_TARGETABLE - else if(isliving(target)) - return TRUE - else if(isturf(target)) - return target.density - return FALSE diff --git a/code/modules/projectiles/projectile/README.md b/code/modules/projectiles/projectile/README.md new file mode 100644 index 000000000000..187e857cfe43 --- /dev/null +++ b/code/modules/projectiles/projectile/README.md @@ -0,0 +1,6 @@ +# /obj/projectile + +The base type of physics-simulated raycasting flying projectiles. + +Used for when you need precision, as throwing is very imprecise. + diff --git a/code/modules/projectiles/projectile/arc.dm b/code/modules/projectiles/projectile/arc.dm index f0a0a86cbe89..8277bd2bfa5f 100644 --- a/code/modules/projectiles/projectile/arc.dm +++ b/code/modules/projectiles/projectile/arc.dm @@ -10,6 +10,7 @@ name = "arcing shot" icon_state = "fireball" // WIP movement_type = MOVEMENT_UNSTOPPABLE + impact_ground_on_expiry = TRUE plane = ABOVE_PLANE // Since projectiles are 'in the air', they might visually overlap mobs while in flight, so the projectile needs to be above their plane. speed = 10 * WORLD_ICON_SIZE var/fired_dir = null // Which direction was the projectile fired towards. Needed to invert the projectile turning based on if facing left or right. @@ -17,8 +18,6 @@ var/visual_y_offset = -16 // Adjusts how high the projectile and its shadow start, visually. This is so the projectile and shadow align with the center of the tile. var/obj/effect/projectile_shadow/shadow = null // Visual indicator for the projectile's 'true' position. Needed due to being bound to two dimensions in reality. -/obj/projectile/arc/Bump() - return /obj/projectile/arc/Initialize(mapload) shadow = new(get_turf(src)) @@ -28,7 +27,6 @@ QDEL_NULL(shadow) return ..() - /obj/projectile/arc/proc/calculate_initial_pixel_distance(atom/user, atom/target) var/datum/point/A = new(user) var/datum/point/B = new(target) @@ -44,12 +42,6 @@ var/datum/point/starting_point = new(starting) return pixel_length_between_points(current_point, starting_point) -/obj/projectile/arc/legacy_on_range() - if(loc) - on_impact(loc) - return ..() - - /obj/projectile/arc/launch_projectile(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0) fired_dir = get_dir(user, target) // Used to determine if the projectile should turn in the air. distance_to_fly = calculate_initial_pixel_distance(user, target) // Calculates how many pixels to travel before hitting the ground. @@ -93,7 +85,6 @@ shadow.pixel_x = pixel_x shadow.pixel_y = pixel_y + visual_y_offset - /obj/effect/projectile_shadow name = "shadow" desc = "You better avoid the thing coming down!" @@ -102,64 +93,13 @@ anchored = TRUE animate_movement = 0 // Just like the projectile it's following. -////////////// -// Subtypes -////////////// - -// This is a test projectile in the sense that its testing the code to make sure it works, -// as opposed to a 'can I hit this thing' projectile. -/obj/projectile/arc/test/on_impact(turf/T) - new /obj/effect/explosion(T) - T.color = "#FF0000" - -// Generic, Hivebot related -/obj/projectile/arc/blue_energy - name = "energy missile" - icon_state = "force_missile" - damage = 15 - damage_type = BURN - -/obj/projectile/arc/blue_energy/on_impact(turf/T) - for(var/mob/living/L in T) - projectile_attack_mob(L) // Everything on the turf it lands gets hit. - -// Fragmentation arc shot -/obj/projectile/arc/fragmentation - name = "fragmentation shot" - icon_state = "shell" - var/list/fragment_types = list( - /obj/projectile/bullet/pellet/fragment, /obj/projectile/bullet/pellet/fragment, \ - /obj/projectile/bullet/pellet/fragment, /obj/projectile/bullet/pellet/fragment/strong - ) - var/fragment_amount = 63 // Same as a grenade. - var/spread_range = 7 - -/obj/projectile/arc/fragmentation/on_impact(turf/T) - fragmentate(T, fragment_amount, spread_range, fragment_types) - -/obj/projectile/arc/fragmentation/mortar - icon_state = "mortar" - fragment_amount = 10 - spread_range = 3 - -// EMP arc shot -/obj/projectile/arc/emp_blast - name = "emp blast" - icon_state = "bluespace" - -/obj/projectile/arc/emp_blast/on_impact(turf/T) - empulse(T, 2, 4, 7, 10) // Normal EMP grenade. - -/obj/projectile/arc/emp_blast/weak/on_impact(turf/T) - empulse(T, 1, 2, 3, 4) // Sec EMP grenade. - -// Radiation arc shot -/obj/projectile/arc/radioactive - name = "radiation blast" - icon_state = "green_pellet" - icon_scale_x = 2 - icon_scale_y = 2 - var/rad_power = RAD_INTENSITY_PROJ_ARC - -/obj/projectile/arc/radioactive/on_impact(turf/T) - radiation_pulse(T, rad_power) +//* We do not hit normally. *// + +/obj/projectile/arc/scan_crossed_atom(atom/movable/target) + return + +/obj/projectile/arc/scan_moved_turf(turf/tile) + return + +/obj/projectile/arc/impact_loop(turf/was_moving_onto, atom/bumped) + return diff --git a/code/modules/projectiles/projectile/helpers.dm b/code/modules/projectiles/projectile/helpers.dm new file mode 100644 index 000000000000..3469a09b6a67 --- /dev/null +++ b/code/modules/projectiles/projectile/helpers.dm @@ -0,0 +1,68 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +/** + * Creates a shrapnel explosion + * + * todo: should this be in a single file with procs like explosion(), emp_pulse(), and other blast-like effects? + * todo: /pellet shouldn't be /bullet/pellet + * todo: there has to be a better way to set vars on every cloud than passing them in as args + * + * @params + * * total_fragments - total pellets. this is divided by turfs within the given radius and rounded up. + * * radius - radius to target. fragments are spread into every turf in the range, making one pellet cloud projectile + * * fragment_types - either a type, or a list of types to pick from. weighted lists are not allowed for speed reasons. all types must be /obj/projectile/bullet/pellet. + * * source - (optional) actual source; used to detect if something is on the ground or not + * * shot_from_name - (legacy - pending reconsideration) what we were shot from + * * firer - (legacy - pending reconsideration) what to set firer to + */ +/turf/proc/shrapnel_explosion(total_fragments, radius, fragment_types = /obj/projectile/bullet/pellet/fragment, atom/movable/source, shot_from_name, firer) + SHOULD_NOT_OVERRIDE(TRUE) + shrapnel_explosion_impl(total_fragments, radius, fragment_types, source, shot_from_name, firer) + +/turf/proc/shrapnel_explosion_impl(total_fragments, radius, fragment_types, atom/movable/source, shot_from_name, firer) + SHOULD_NOT_OVERRIDE(TRUE) + if(radius > 8) + // remember that for radius 8 we're making 32 projectiles already + CRASH("attempted radius [radius]; this is too large.") + // todo: verify getcircle() behavior is what we want + // todo: the answer is probably no, as this doesn't work near map edges. + var/list/target_turfs = getcircle(src, radius) + // round up + var/fragments_per_projectile = ceil(total_fragments / length(target_turfs)) + // make shrapnel clouds + . = list() + for(var/turf/T in target_turfs) + var/fragment_type = islist(fragment_types) ? pick(fragment_types) : fragment_types + var/obj/projectile/bullet/pellet/pellet_cloud = new fragment_type(src) + pellet_cloud.pellets = fragments_per_projectile + pellet_cloud.shot_from = shot_from_name + pellet_cloud.firer = firer + pellet_cloud.fire(arctan(T.y - y, T.x - x), null, TRUE) + . += pellet_cloud + +/** + * make a shrapnel explosion + */ +/obj/proc/shrapnel_explosion(total_fragments, radius, fragment_types) + var/turf/turf = get_turf(src) + if(!turf) + return + var/list/obj/projectile/bullet/pellet/pellet_clouds = turf.shrapnel_explosion(total_fragments, radius, fragment_types, src, name, src) + // hit things in our turf + for(var/mob/living/victim in turf) + for(var/obj/projectile/bullet/pellet/pellet_cloud as anything in pellet_clouds) + if(QDELETED(pellet_cloud)) + continue + // they're laying on us, o h n o + if(victim.lying && (loc == turf)) + if(prob(90)) + pellet_cloud.impact(victim) + // they're not laying down but they're holding the source and standing + else if(!victim.lying && victim.is_holding(src)) + if(prob(25)) + pellet_cloud.impact(victim) + // they are either holding us while laying down or just on the turf without holding us + else + if(prob(15)) + pellet_cloud.impact(victim) diff --git a/code/modules/projectiles/projectile/pellets.dm b/code/modules/projectiles/projectile/pellets.dm deleted file mode 100644 index 61e9b45cda0c..000000000000 --- a/code/modules/projectiles/projectile/pellets.dm +++ /dev/null @@ -1,118 +0,0 @@ - -//For projectiles that actually represent clouds of projectiles -/obj/projectile/bullet/pellet - name = "shrapnel" //'shrapnel' sounds more dangerous (i.e. cooler) than 'pellet' - damage = 20 - //icon_state = "bullet" //TODO: would be nice to have it's own icon state - var/pellets = 4 //number of pellets - var/range_step = 2 //projectile will lose a fragment each time it travels this distance. Can be a non-integer. - var/base_spread = 90 //lower means the pellets spread more across body parts. If zero then this is considered a shrapnel explosion instead of a shrapnel cone - var/spread_step = 10 //higher means the pellets spread more across body parts with distance - -/obj/projectile/bullet/pellet/proc/get_pellets(var/distance) - var/pellet_loss = round((distance - 1)/range_step) //pellets lost due to distance - return max(pellets - pellet_loss, 1) - -/obj/projectile/bullet/pellet/projectile_attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier) - if (pellets < 0) return 1 - - var/total_pellets = get_pellets(distance) - var/spread = max(base_spread - (spread_step*distance), 0) - - //shrapnel explosions miss prone mobs with a chance that increases with distance - var/prone_chance = 0 - if(!base_spread) - prone_chance = max(spread_step*(distance - 2), 0) - - var/hits = 0 - for (var/i in 1 to total_pellets) - if(target_mob.lying && target_mob != original && prob(prone_chance)) - continue - - //pellet hits spread out across different zones, but 'aim at' the targeted zone with higher probability - //whether the pellet actually hits the def_zone or a different zone should still be determined by the parent using get_zone_with_miss_chance(). - var/old_zone = def_zone - def_zone = ran_zone(def_zone, spread) - if (..()) hits++ - def_zone = old_zone //restore the original zone the projectile was aimed at - - pellets -= hits //each hit reduces the number of pellets left - if (hits >= total_pellets || pellets <= 0) - return 1 - return 0 - -/obj/projectile/bullet/pellet/get_structure_damage() - var/distance = get_dist(loc, starting) - return ..() * get_pellets(distance) - -/obj/projectile/bullet/pellet/Move() - . = ..() - - //If this is a shrapnel explosion, allow mobs that are prone to get hit, too - if(. && !base_spread && isturf(loc)) - for(var/mob/living/M in loc) - if(M.lying || !M.CanPass(src, loc)) //Bump if lying or if we would normally Bump. - if(Bump(M)) //Bump will make sure we don't hit a mob multiple times - return - -//Explosive grenade projectile, borrowed from fragmentation grenade code. -/obj/projectile/bullet/pellet/fragment - damage = 10 - armor_penetration = 30 - range_step = 2 //projectiles lose a fragment each time they travel this distance. Can be a non-integer. - - base_spread = 0 //causes it to be treated as a shrapnel explosion instead of cone - spread_step = 20 - - silenced = 1 //embedding messages are still produced so it's kind of weird when enabled. - no_attack_log = 1 - muzzle_type = null - -/obj/projectile/bullet/pellet/fragment/strong - damage = 15 - armor_penetration = 20 - -/obj/projectile/bullet/pellet/fragment/weak - damage = 4 - armor_penetration = 40 - -/obj/projectile/bullet/pellet/fragment/rubber - name = "stingball shrapnel" - damage = 3 - agony = 8 - sharp = FALSE - edge = FALSE - damage_flag = ARMOR_MELEE - -/obj/projectile/bullet/pellet/fragment/rubber/strong - damage = 8 - agony = 16 - -// Tank rupture fragments -/obj/projectile/bullet/pellet/fragment/tank - name = "metal fragment" - damage = 9 //Big chunks flying off. - range_step = 2 //controls damage falloff with distance. projectiles lose a "pellet" each time they travel this distance. Can be a non-integer. - - base_spread = 0 //causes it to be treated as a shrapnel explosion instead of cone - spread_step = 20 - - armor_penetration = 20 - - silenced = 1 - no_attack_log = 1 - muzzle_type = null - pellets = 3 - -/obj/projectile/bullet/pellet/fragment/tank/small - name = "small metal fragment" - damage = 6 - armor_penetration = 5 - pellets = 5 - -/obj/projectile/bullet/pellet/fragment/tank/big - name = "large metal fragment" - damage = 17 - armor_penetration = 10 - range_step = 5 //controls damage falloff with distance. projectiles lose a "pellet" each time they travel this distance. Can be a non-integer. - pellets = 1 diff --git a/code/modules/projectiles/projectile/projectile-hitscan_visuals.dm b/code/modules/projectiles/projectile/projectile-hitscan_visuals.dm new file mode 100644 index 000000000000..f9a370eee631 --- /dev/null +++ b/code/modules/projectiles/projectile/projectile-hitscan_visuals.dm @@ -0,0 +1,195 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +//* Hitscan Visuals *// + +/** + * returns a /datum/point based on where we currently are + */ +/obj/projectile/proc/get_tracer_point() + RETURN_TYPE(/datum/point) + var/datum/point/point = new + if(trajectory_moving_to) + // we're in move. use next px/py to respect 1. kick forwards 2. deflections + point.x = (trajectory_moving_to.x - 1) * WORLD_ICON_SIZE + next_px + point.y = (trajectory_moving_to.y - 1) * WORLD_ICON_SIZE + next_py + else + point.x = (x - 1) * WORLD_ICON_SIZE + current_px + point.y = (y - 1) * WORLD_ICON_SIZE + current_py + point.z = z + return point + +/** + * * returns a /datum/point based on where we'll be when we loosely intersect a tile + * * returns null if we'll never intersect it + * * returns our current point if we're already loosely intersecting it + * * loosely intersecting means that we are level with the tile in either x or y. + */ +/obj/projectile/proc/get_intersection_point(turf/colliding) + RETURN_TYPE(/datum/point) + ASSERT(!isnull(angle)) + + // calculate where we are + var/our_x + var/our_y + if(trajectory_moving_to) + // we're in move. use next px/py to respect 1. kick forwards 2. deflections + our_x = (trajectory_moving_to.x - 1) * WORLD_ICON_SIZE + next_px + our_y = (trajectory_moving_to.y - 1) * WORLD_ICON_SIZE + next_py + else + our_x = (x - 1) * WORLD_ICON_SIZE + current_px + our_y = (y - 1) * WORLD_ICON_SIZE + current_py + + // calculate how far we have to go to touch their closest x / y axis + var/d_to_reach_x + var/d_to_reach_y + + if(colliding.x != x) + switch(calculated_sdx) + if(0) + return + if(1) + if(colliding.x < x) + return + d_to_reach_x = (((colliding.x - 1) * WORLD_ICON_SIZE + 0.5) - our_x) / calculated_dx + if(-1) + if(colliding.x > x) + return + d_to_reach_x = (((colliding.x - 0) * WORLD_ICON_SIZE + 0.5) - our_x) / calculated_dx + else + d_to_reach_x = 0 + + if(colliding.y != y) + switch(calculated_sdy) + if(0) + return + if(1) + if(colliding.y < y) + return + d_to_reach_y = (((colliding.y - 1) * WORLD_ICON_SIZE + 0.5) - our_y) / calculated_dy + if(-1) + if(colliding.y > y) + return + d_to_reach_y = (((colliding.y - 0) * WORLD_ICON_SIZE + 0.5) - our_y) / calculated_dy + else + d_to_reach_y = 0 + + var/needed_distance = max(d_to_reach_x, d_to_reach_y) + + // calculate if we'll actually be touching the tile once we go that far + var/future_x = our_x + needed_distance * calculated_dx + var/future_y = our_y + needed_distance * calculated_dy + // let's be slightly lenient and do 1 instead of 0.5 + if(future_x < (colliding.x - 1) * WORLD_ICON_SIZE && future_x > (colliding.x) * WORLD_ICON_SIZE + 1 && \ + future_y < (colliding.y - 1) * WORLD_ICON_SIZE && future_y > (colliding.y) * WORLD_ICON_SIZE + 1) + return // not gonna happen + + // make the point based on how far we need to go + var/datum/point/point = new + point.x = future_x + point.y = future_y + point.z = z + return point + +/** + * records the start of a hitscan + * + * this can edit the point passed in! + */ +/obj/projectile/proc/record_hitscan_start(datum/point/point, muzzle_marker, kick_forwards) + if(!hitscanning) + return + if(isnull(point)) + point = get_tracer_point() + tracer_vertices = list(point) + tracer_muzzle_flash = muzzle_marker + + // kick forwards + point.shift_in_projectile_angle(angle, kick_forwards) + +/** + * ends the hitscan tracer + * + * this can edit the point passed in! + */ +/obj/projectile/proc/record_hitscan_end(datum/point/point, impact_marker, kick_forwards) + if(!hitscanning) + return + if(isnull(point)) + point = get_tracer_point() + tracer_vertices += point + tracer_impact_effect = impact_marker + + // kick forwards + point.shift_in_projectile_angle(angle, kick_forwards) + +/** + * records a deflection (change in angle, aka generate new tracer) + */ +/obj/projectile/proc/record_hitscan_deflection(datum/point/point) + if(!hitscanning) + return + if(isnull(point)) + point = get_tracer_point() + // there's no way you need more than 25 + // if this is hit, fix your shit, don't bump this up; there's absolutely no reason for example, + // to simulate reflectors working !!25!! times. + if(length(tracer_vertices) >= 25) + CRASH("tried to add more than 25 vertices to a hitscan tracer") + tracer_vertices += point + +/obj/projectile/proc/render_hitscan_tracers(duration = tracer_duration) + // don't stay too long + ASSERT(duration >= 0 && duration <= 10 SECONDS) + // check everything + if(!has_tracer || !duration || !length(tracer_vertices)) + return + var/list/atom/movable/beam_components = list() + + // muzzle + if(muzzle_type && tracer_muzzle_flash) + var/datum/point/starting = tracer_vertices[1] + var/atom/movable/muzzle_effect = starting.instantiate_movable_with_unmanaged_offsets(muzzle_type) + if(muzzle_effect) + // turn it + var/matrix/muzzle_transform = matrix() + muzzle_transform.Turn(original_angle) + muzzle_effect.transform = muzzle_transform + muzzle_effect.color = color + muzzle_effect.set_light(muzzle_flash_range, muzzle_flash_intensity, muzzle_flash_color_override? muzzle_flash_color_override : color) + // add to list + beam_components += muzzle_effect + // impact + if(impact_type && tracer_impact_effect) + var/datum/point/starting = tracer_vertices[length(tracer_vertices)] + var/atom/movable/impact_effect = starting.instantiate_movable_with_unmanaged_offsets(impact_type) + if(impact_effect) + // turn it + var/matrix/impact_transform = matrix() + impact_transform.Turn(angle) + impact_effect.transform = impact_transform + impact_effect.color = color + impact_effect.set_light(impact_light_range, impact_light_intensity, impact_light_color_override? impact_light_color_override : color) + // add to list + beam_components += impact_effect + // path tracers + if(tracer_type) + var/tempref = "\ref[src]" + for(var/i in 1 to length(tracer_vertices) - 1) + var/j = i + 1 + var/datum/point/first_point = tracer_vertices[i] + var/datum/point/second_point = tracer_vertices[j] + generate_tracer_between_points(first_point, second_point, beam_components, tracer_type, color, duration, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity, tempref) + + QDEL_LIST_IN(beam_components, duration) + +/obj/projectile/proc/cleanup_hitscan_tracers() + tracer_vertices = null + +/obj/projectile/proc/finalize_hitscan_tracers(datum/point/end_point, impact_effect, kick_forwards) + // if end wasn't recorded yet and we're still on a turf, record end + if(isnull(tracer_impact_effect) && loc) + record_hitscan_end(end_point, impact_marker = impact_effect, kick_forwards = kick_forwards) + // render & cleanup + render_hitscan_tracers() + cleanup_hitscan_tracers() diff --git a/code/modules/projectiles/projectile/projectile-physics.dm b/code/modules/projectiles/projectile/projectile-physics.dm new file mode 100644 index 000000000000..ed446daf0ff0 --- /dev/null +++ b/code/modules/projectiles/projectile/projectile-physics.dm @@ -0,0 +1,434 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +//* Physics - Configuration *// + +/** + * sets our angle + */ +/obj/projectile/proc/set_angle(new_angle) + angle = new_angle + + // update sprite + if(!nondirectional_sprite) + var/matrix/M = new + M.Turn(angle) + transform = M + + // update trajectory + calculated_dx = sin(new_angle) + calculated_dy = cos(new_angle) + calculated_sdx = calculated_dx == 0? 0 : (calculated_dx > 0? 1 : -1) + calculated_sdy = calculated_dy == 0? 0 : (calculated_dy > 0? 1 : -1) + + var/normalized_to_first_quadrant = MODULUS_F(new_angle, 90) + angle_chebyshev_divisor = cos(normalized_to_first_quadrant >= 45? (90 - normalized_to_first_quadrant) : normalized_to_first_quadrant) + + // record our tracer's change + if(hitscanning) + record_hitscan_deflection() + +/** + * sets our speed in pixels per decisecond + */ +/obj/projectile/proc/set_speed(new_speed) + speed = clamp(new_speed, 1, WORLD_ICON_SIZE * 100) + +/** + * sets our angle and speed + */ +/obj/projectile/proc/set_velocity(new_angle, new_speed) + // this is so this can be micro-optimized later but for once i'm not going to do it early for no reason + set_speed(new_speed) + set_angle(new_angle) + +/** + * todo: this is somewhat mildly terrible + */ +/obj/projectile/proc/set_homing_target(atom/A) + if(!A || (!isturf(A) && !isturf(A.loc))) + return FALSE + homing = TRUE + homing_target = A + homing_offset_x = rand(homing_inaccuracy_min, homing_inaccuracy_max) + homing_offset_y = rand(homing_inaccuracy_min, homing_inaccuracy_max) + if(prob(50)) + homing_offset_x = -homing_offset_x + if(prob(50)) + homing_offset_y = -homing_offset_y + +/** + * initializes physics vars + */ +/obj/projectile/proc/setup_physics() + distance_travelled = 0 + +/** + * called after an unhandled forcemove is detected, or other event + * that should reset our on-turf state + */ +/obj/projectile/proc/reset_physics_to_turf() + // we use this because we can center larger than 32x32 projectiles + // without disrupting physics this way + // + // we add by (WORLD_ICON_SIZE / 2) because + // pixel_x / pixel_y starts at center, + // + current_px = pixel_x - base_pixel_x + (WORLD_ICON_SIZE / 2) + current_py = pixel_y - base_pixel_y + (WORLD_ICON_SIZE / 2) + // interrupt active move logic + trajectory_moving_to = null + +//* Physics - Processing *// + +/obj/projectile/process(delta_time) + if(paused) + return + physics_iteration(min(10 * WORLD_ICON_SIZE, delta_time * speed * SSprojectiles.global_projectile_speed_multiplier), delta_time) + +/** + * immediately processes hitscan + */ +/obj/projectile/proc/physics_hitscan(safety = 250, resuming) + // setup + if(!resuming) + hitscanning = TRUE + record_hitscan_start(muzzle_marker = TRUE, kick_forwards = 16) + + // just move as many times as we can + while(!QDELETED(src) && loc) + // check safety + safety-- + if(safety <= 0) + // if you're here, you shouldn't be. do not bump safety up, fix whatever + // you're doing because no one should be making projectiles go more than 250 + // tiles in a single life. + stack_trace("projectile hit iteration limit for hitscan") + break + + // move forwards by 1 tile length + var/pixels_moved = physics_step(WORLD_ICON_SIZE) + distance_travelled += pixels_moved + // if we're being yanked, yield + if(movable_flags & MOVABLE_IN_MOVED_YANK) + spawn(0) + physics_hitscan(safety, TRUE) + return + + // see if we're done + if(distance_travelled >= range) + legacy_on_range() + break + + hitscanning = FALSE + +/** + * ticks forwards a number of pixels + * + * todo: potential lazy animate support for performance, as we honestly don't need to animate at full fps if the server's above 20fps + * + * * delta_tiem is in deciseconds, not seconds. + */ +/obj/projectile/proc/physics_iteration(pixels, delta_time, additional_animation_length) + // setup iteration + var/safety = 15 + var/pixels_remaining = pixels + distance_travelled_this_iteration = 0 + + // apply penalty + var/penalizing = clamp(trajectory_penalty_applied, 0, pixels_remaining) + pixels_remaining -= penalizing + trajectory_penalty_applied -= penalizing + + // clamp to max distance + pixels_remaining = min(pixels_remaining, range - distance_travelled) + + // move as many times as we need to + // + // * break if we're loc = null (by deletion or otherwise) + // * break if we get paused + while(pixels_remaining > 0) + // check safety + safety-- + if(safety <= 0) + CRASH("ran out of safety! what happened?") + + // move + var/pixels_moved = physics_step(pixels_remaining) + distance_travelled += pixels_moved + distance_travelled_this_iteration += pixels_moved + pixels_remaining -= pixels_moved + // we're being yanked, yield + if(movable_flags & MOVABLE_IN_MOVED_YANK) + spawn(0) + physics_iteration(pixels_remaining, delta_time, distance_travelled_this_iteration) + return + // this is also a catch-all for deletion + if(!loc || paused) + break + + // penalize next one if we were kicked forwards forcefully too far + trajectory_penalty_applied = max(0, -pixels_remaining) + + // if we don't have a loc anymore just bail + if(!loc) + return + + // if we're at max range + if(distance_travelled >= range) + // todo: egh + legacy_on_range() + if(QDELETED(src)) + return + + // process homing + physics_tick_homing(delta_time) + + // perform animations + // we assume at this point any deflections that should have happened, has happened, + // so we just do a naive animate based on our current loc and pixel x/y + // + // todo: animation needs to take into account angle changes, + // but that's expensive as shit so uh lol + // + // the reason we use distance_travelled_this_iteration is so if something disappears + // by forceMove or whatnot, + // we won't have it bounce from its previous location to the new one as it's not going + // to be accurate anymore + // + // so instead, as of right now, we backtrack via how much we know we moved. + var/final_px = base_pixel_x + current_px - (WORLD_ICON_SIZE / 2) + var/final_py = base_pixel_y + current_py - (WORLD_ICON_SIZE / 2) + var/anim_dist = distance_travelled_this_iteration + additional_animation_length + pixel_x = final_px - (anim_dist * sin(angle)) + pixel_y = final_py - (anim_dist * cos(angle)) + + animate( + src, + delta_time, + flags = ANIMATION_END_NOW, + pixel_x = final_px, + pixel_y = final_py, + ) + +/** + * based on but exactly http://www.cs.yorku.ca/~amana/research/grid.pdf + * + * move into the next tile, or the specified number of pixels, + * whichever is less pixels moved + * + * this will modify our current_px/current_py as necessary + * + * @return pixels moved + */ +/obj/projectile/proc/physics_step(limit) + // distance to move in our angle to get to next turf for horizontal and vertical + var/d_next_horizontal = \ + (calculated_sdx? ((calculated_sdx > 0? (WORLD_ICON_SIZE + 0.5) - current_px : -current_px + 0.5) / calculated_dx) : INFINITY) + var/d_next_vertical = \ + (calculated_sdy? ((calculated_sdy > 0? (WORLD_ICON_SIZE + 0.5) - current_py : -current_py + 0.5) / calculated_dy) : INFINITY) + var/turf/move_to_target + + /** + * explanation on why current and next are done: + * + * projectiles track their pixel x/y on turf, not absolute pixel x/y from edge of map + * this is done to make it simpler to reason about, but is not necessarily the most simple + * or efficient way to do things. + * + * part of the problems with this approach is that Move() is not infallible. the projectile can be blocked. + * if we immediately set current pixel x/y, if the projectile is intercepted by a Bump, we now dont' know the 'real' + * position of the projectile because it's out of sync with where it should be + * + * now, things that require math operations on it don't know the actual location of the projectile until this proc + * rolls it back + * + * so instead, we never touch current px/py until the move is known to be successful, then we set it + * to the stored next px/py + * + * this way, things accessing can mutate our state freely without worrying about needing to handle rollbacks + * + * this entire system however adds overhead + * if we want to not have overhead, we'll need to rewrite hit processing and have it so moves are fully illegal to fail + * but doing that is literally not possible because anything can reject a move for any reason whatsoever + * and we cannot control that, so, instead, we make projectiles track in absolute pixel x/y coordinates from edge of map + * + * that way, we don't even need to care about where the .loc is, we just know where the projectile is supposed to be by + * knowing where it isn't, and by taking the change in its pixels the projectile controller can tell the projectile + * where to go- + * + * (all shitposting aside, this is for future work; it works right now and we have an API to do set angle, kick forwards, etc) + * (so i'm not going to touch this more because it's 4 AM and honestly this entire raycaster is already far less overhead) + * (than the old system of a 16-loop of brute forced 2 pixel increments) + */ + + if(d_next_horizontal == d_next_vertical) + // we're diagonal + if(d_next_horizontal <= limit) + move_to_target = locate(x + calculated_sdx, y + calculated_sdy, z) + . = d_next_horizontal + if(!move_to_target) + // we hit the world edge and weren't transit; time to get deleted. + if(hitscanning) + finalize_hitscan_tracers(impact_effect = FALSE) + qdel(src) + return + next_px = calculated_sdx > 0? 0.5 : (WORLD_ICON_SIZE + 0.5) + next_py = calculated_sdy > 0? 0.5 : (WORLD_ICON_SIZE + 0.5) + else if(d_next_horizontal < d_next_vertical) + // closer is to move left/right + if(d_next_horizontal <= limit) + move_to_target = locate(x + calculated_sdx, y, z) + . = d_next_horizontal + if(!move_to_target) + // we hit the world edge and weren't transit; time to get deleted. + if(hitscanning) + finalize_hitscan_tracers(impact_effect = FALSE) + qdel(src) + return + next_px = calculated_sdx > 0? 0.5 : (WORLD_ICON_SIZE + 0.5) + next_py = current_py + d_next_horizontal * calculated_dy + else if(d_next_vertical < d_next_horizontal) + // closer is to move up/down + if(d_next_vertical <= limit) + move_to_target = locate(x, y + calculated_sdy, z) + . = d_next_vertical + if(!move_to_target) + // we hit the world edge and weren't transit; time to get deleted. + if(hitscanning) + finalize_hitscan_tracers(impact_effect = FALSE) + qdel(src) + return + next_px = current_px + d_next_vertical * calculated_dx + next_py = calculated_sdy > 0? 0.5 : (WORLD_ICON_SIZE + 0.5) + + // if we need to move + if(move_to_target) + var/atom/old_loc = loc + trajectory_moving_to = move_to_target + // mark next distance so impact processing can work + next_distance = distance_travelled + . + if(!Move(move_to_target) && ((loc != move_to_target) || !trajectory_moving_to)) + // if we don't successfully move, don't change anything, we didn't move. + . = 0 + if(loc == old_loc) + stack_trace("projectile failed to move, but is still on turf instead of deleted or relocated.") + qdel(src) // bye + else + // only do these if we successfully move, or somehow end up in that turf anyways + if(trajectory_kick_forwards) + . += trajectory_kick_forwards + trajectory_kick_forwards = 0 + current_px = next_px + current_py = next_py + #ifdef CF_PROJECTILE_RAYCAST_VISUALS + new /atom/movable/render/projectile_raycast(move_to_target, current_px, current_py, "#77ff77") + #endif + trajectory_moving_to = null + else + // not moving to another tile, so, just move on current tile + if(trajectory_kick_forwards) + trajectory_kick_forwards = 0 + stack_trace("how did something kick us forwards when we didn't even move?") + . = limit + current_px += limit * calculated_dx + current_py += limit * calculated_dy + next_px = current_px + next_py = current_py + #ifdef CF_PROJECTILE_RAYCAST_VISUALS + new /atom/movable/render/projectile_raycast(loc, current_px, current_py, "#ff3333") + #endif + +#ifdef CF_PROJECTILE_RAYCAST_VISUALS +GLOBAL_VAR_INIT(projectile_raycast_debug_visual_delay, 2 SECONDS) + +/atom/movable/render/projectile_raycast + plane = OBJ_PLANE + icon = 'icons/system/color_32x32.dmi' + icon_state = "white-pixel" + +/** + * px, py are absolute pixel coordinates on the tile, not pixel_x / pixel_y of this renderer! + */ +/atom/movable/render/projectile_raycast/Initialize(mapload, px, py, color) + src.pixel_x = px - 1 + src.pixel_y = py - 1 + src.color = color + . = ..() + QDEL_IN(src, GLOB.projectile_raycast_debug_visual_delay) +#endif + +/** + * immediately, without processing, kicks us forward a number of pixels + * + * since we immediately cross over into a turf when entering, + * things like mirrors/reflectors will immediately set angle + * + * it looks ugly and is pretty bad to just reflect off the edge of a turf so said things can + * call this proc to kick us forwards by a bit + */ +/obj/projectile/proc/physics_kick_forwards(pixels) + trajectory_kick_forwards += pixels + next_px += pixels * calculated_dx + next_py += pixels * calculated_dy + +/** + * only works during non-hitscan + * + * this is called once per tick + * homing is smoother the higher fps the server / SSprojectiles runs at + * + * todo: this is somewhat mildly terrible + * todo: this has absolutely no arc/animation support; this is bad + */ +/obj/projectile/proc/physics_tick_homing(delta_time) + if(!homing) + return FALSE + // checks if they're 1. on a turf, 2. on our z + // todo: should we add support for tracking something even if it leaves a turf? + if(homing_target?.z != z) + // bye bye! + return FALSE + // todo: this assumes single-tile objects. at some point, we should upgrade this to be unnecessarily expensive and always center-mass. + var/dx = (homing_target.x - src.x) * WORLD_ICON_SIZE + (0 - current_px) + homing_offset_x + var/dy = (homing_target.y - src.y) * WORLD_ICON_SIZE + (0 - current_py) + homing_offset_y + // say it with me, arctan() + // is CCW of east if (dx, dy) + // and CW of north if (dy, dx) + // where dx and dy is distance in x/y pixels from us to them. + + var/nudge_towards = closer_angle_difference(arctan(dy, dx)) + var/max_turn_speed = homing_turn_speed * delta_time + + set_angle(angle + clamp(nudge_towards, -max_turn_speed, max_turn_speed)) + +//* Physics - Querying *// + +/** + * predict what turf we'll be in after going forwards a certain amount of pixels + * + * doesn't actually sim; so this will go through walls/obstacles! + * + * * if we go out of bounds, we will return null; this doesn't level-wrap + */ +/obj/projectile/proc/physics_predicted_turf_after_iteration(pixels) + // -1 at the end if 0, because: + // + // -32 is go back 1 tile and be at the 1st pixel (as 0 is going back) + // 0 is go back 1 tile and be at the 32nd pixel. + var/incremented_px = (current_px + pixels * calculated_dx) || - 1 + var/incremented_py = (current_py + pixels * calculated_dy) || - 1 + + var/incremented_tx = floor(incremented_px / 32) + var/incremented_ty = floor(incremented_py / 32) + + return locate(x + incremented_tx, y + incremented_ty, z) + +/** + * predict what turfs we'll hit, excluding the current turf, after going forwards + * a certain amount of pixels + * + * doesn't actually sim; so this will go through walls/obstacles! + */ +/obj/projectile/proc/physics_predicted_turfs_during_iteration(pixels) + return pixel_physics_raycast(loc, current_px, current_py, angle, pixels) diff --git a/code/modules/projectiles/projectile/projectile-tracing.dm b/code/modules/projectiles/projectile/projectile-tracing.dm new file mode 100644 index 000000000000..7c4b4d10014f --- /dev/null +++ b/code/modules/projectiles/projectile/projectile-tracing.dm @@ -0,0 +1,68 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station developers. *// + +/obj/projectile/trace + atom_flags = ATOM_ABSTRACT | ATOM_NONWORLD + invisibility = INVISIBILITY_ABSTRACT + hitscan = TRUE + has_tracer = FALSE + damage = 0 + nodamage = TRUE + projectile_type = PROJECTILE_TYPE_TRACE + + /// did we manage to hit the given target? + var/could_hit_target = FALSE + /// delete on hitting target? + var/del_on_success = TRUE + /// do we check opacity? + var/check_opacity = FALSE + /// do we only care about opacity, and not pass flags or anything else? + var/only_opacity = FALSE + /// do we only need to reach their turf? + var/require_turf_only = FALSE + /// target turf, if we only require reaching their turf + var/turf/require_turf_cached + +/obj/projectile/trace/CanPassThrough(atom/blocker, turf/target, blocker_opinion) + if(only_opacity && !blocker.opacity) + return TRUE + return ..() + +/obj/projectile/trace/pre_impact(atom/target, impact_flags, def_zone) + if(target == original_target) + could_hit_target = TRUE + if(del_on_success) + return PROJECTILE_IMPACT_DELETE + if(get_turf(target) == require_turf_cached) + could_hit_target = TRUE + if(del_on_success) + return PROJECTILE_IMPACT_DELETE + . = ..() + // tracers only count as 'can move across' if pre_impact() says we should phase/pierce. + if(. & PROJECTILE_IMPACT_FLAGS_SHOULD_GO_THROUGH) + return PROJECTILE_IMPACT_PASSTHROUGH + else + return PROJECTILE_IMPACT_DELETE + +/obj/projectile/trace/Moved() + . = ..() + if(QDELETED(src)) + return + if(require_turf_cached == loc) + could_hit_target = TRUE + if(del_on_success) + qdel(src) + return + if(check_opacity && isturf(loc)) + // *sigh* // + var/turf/T = loc + if(T.has_opaque_atom) + qdel(src) + +/** + * always call this before firing. + */ +/obj/projectile/trace/proc/prepare_trace(atom/target) + src.original_target = target + if(require_turf_only) + src.require_turf_cached = get_turf(target) diff --git a/code/modules/projectiles/projectile/projectile.dm b/code/modules/projectiles/projectile/projectile.dm new file mode 100644 index 000000000000..dd6fc8e9bab8 --- /dev/null +++ b/code/modules/projectiles/projectile/projectile.dm @@ -0,0 +1,1180 @@ +/** + * ## Physics Specifications + * + * We track physics as absolute pixel on a tile, not byond's pixel x/y + * thus the first pixel at bottom left of tile is 1, 1 + * and the last pixel at top right is 32, 32 (for a world icon size of 32 pixels) + * + * We cross over to the next tile at above 32, for up/right, + * and to the last tile at below 1, for bottom/left. + * + * The code might handle it based on how it's implemented, + * but as long as the error is 1 pixel or below, it's not a big deal. + * + * The reason we're so accurate (1 pixel/below is pretty insanely strict) is + * so players have the projectile act like what the screen says it should be like; + * hence why projectiles can realistically path across corners based on their 'hitbox center'. + */ +/obj/projectile + name = "projectile" + icon = 'icons/obj/projectiles.dmi' + icon_state = "bullet" + density = FALSE + anchored = TRUE + integrity_flags = INTEGRITY_INDESTRUCTIBLE | INTEGRITY_ACIDPROOF | INTEGRITY_FIREPROOF | INTEGRITY_LAVAPROOF + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + depth_level = INFINITY // nothing should be passing over us from depth + + //* Collision Handling *// + + /** PROJECTILE PIERCING + * WARNING: + * Projectile piercing MUST be done using these variables. + * Ordinary passflags will result in can_hit_target being false unless directly clicked on - similar to pass_flags_phase but without even going to process_hit. + * The two flag variables below both use pass flags. + * In the context of ATOM_PASS_THROWN, it means the projectile will ignore things that are currently "in the air" from a throw. + * + * Also, projectiles sense hits using Bump(), and then pierce them if necessary. + * They simply do not follow conventional movement rules. + * NEVER flag a projectile as PHASING movement type. + * If you so badly need to make one go through *everything*, override check_pierce() for your projectile to always return PROJECTILE_PIERCE_PHASE/HIT. + */ + /// The "usual" flags of pass_flags is used in that can_hit_target ignores these unless they're specifically targeted/clicked on. This behavior entirely bypasses process_hit if triggered, rather than phasing which uses prehit_pierce() to check. + pass_flags = ATOM_PASS_TABLE + /// we handle our own go through + generic_canpass = FALSE + /// If FALSE, allow us to hit something directly targeted/clicked/whatnot even if we're able to phase through it + var/phases_through_direct_target = FALSE + /// anything with these pass flags are automatically pierced + var/pass_flags_pierce = NONE + /// anything with these pass flags are automatically phased through + var/pass_flags_phase = NONE + /// number of times we've pierced something. Incremented BEFORE bullet_act and similar procs run! + var/pierces = 0 + /// What we already hit + /// initialized on fire() + var/list/impacted + + //* -- Combat - Accuracy -- *// + //* These are applied in additional to mob + //* evasion and miss handling! Projectiles should *// + //* generally be pretty accurate for that reason. *// + //* *// + //* All accuracy ranges use **manhattan** *// + //* distance, not euclidean! *// + + /// if enabled, projectile-side baymiss is entirely disabled + /// + /// * the target can still forcefully miss us, unfortunately, if it doesn't use the standard API + /// * this might be violently fixed in the future + var/accuracy_disabled = FALSE + /// perfect accuracy below this range (in pixels) + /// + /// * this means the projectile doesn't enforce inaccuracy; not the target! + /// * remember that even if a projectile clips a single pixel on a target turf, it hits. + /// * right now, accuracy is slightly more than it should be due to distance being ticked up post-impact. + var/accuracy_perfect_range = WORLD_ICON_SIZE * 7 + /// linear - accuracy outside of perfect range + /// + /// * [0, 100] inclusive as % + var/accuracy_drop_start = 100 + /// linear - hit % falloff per pixel + var/accuracy_drop_slope = 5 / WORLD_ICON_SIZE + /// linear - lowest possible accuracy to drop to + var/accuracy_drop_end = 20 + /// alter end result hit probability by this value + /// + /// todo: is this really the best way? + /// + /// * this is a multiplier for hit chance if less than 1 + /// * this is a divisor for miss chance if more than 1 + /// * 0.5 will turn a 80% hit to a 40% + /// * 2 will turn a 80% hit to a 90% + /// * 2 will turn a 40% hit to a 70% + var/accuracy_overall_modify = 1 + + //* Combat - Effects *// + + /// projectile effects + /// + /// * this is a static typelist on this typepath + /// * do not under any circumstances edit this + /// * this is /tmp because this should never change on a typepath + VAR_PROTECTED/tmp/list/base_projectile_effects + /// projectile effects + /// + /// * this is configured at runtime and can be edited + /// * this is non /tmp because this is infact serializable + VAR_PROTECTED/list/additional_projectile_effects + + //* Configuration *// + + /// Projectile type bitfield; set all that is relevant + var/projectile_type = NONE + /// Impact ground on expiry (range, or lifetime) + var/impact_ground_on_expiry = FALSE + + //* Physics - Configuration *// + + /// speed, in pixels per second + var/speed = 25 * WORLD_ICON_SIZE + /// are we a hitscan projectile? + var/hitscan = FALSE + /// angle, in degrees **clockwise of north** + var/angle + /// multiplier to distance travelled at the **current** [angle] to get it to chebyshev dist + var/angle_chebyshev_divisor + /// max distance in pixels + /// + /// * please set this to a multiple of [WORLD_ICON_SIZE] so we scale with tile size. + var/range = WORLD_ICON_SIZE * 50 + // todo: lifespan + + //* Physics - Homing *// + /// Are we homing in on something? + var/homing = FALSE + /// current homing target + var/atom/homing_target + /// angle per second + /// + /// * this is smoother the less time between SSprojectiles fires + var/homing_turn_speed = 100 + /// rand(min, max) for inaccuracy offsets + var/homing_inaccuracy_min = 0 + /// rand(min, max) for inaccuracy offsets + var/homing_inaccuracy_max = 0 + /// pixels; added to the real location of target so we're not exactly on-point + var/homing_offset_x = 0 + /// pixels; added to the real location of target so we're not exactly on-point + var/homing_offset_y = 0 + + //* Physics - Tracers *// + + /// tracer /datum/point's + var/list/tracer_vertices + /// first point is a muzzle effect + var/tracer_muzzle_flash + /// last point is an impact + var/tracer_impact_effect + /// default tracer duration + var/tracer_duration = 5 + + //* Physics - State *// + + /// paused? if so, we completely get passed over during processing + var/paused = FALSE + /// currently hitscanning + var/hitscanning = FALSE + /// a flag to prevent movement hooks from resetting our physics on a forced movement + var/trajectory_ignore_forcemove = FALSE + /// cached value: move this much x for this much distance + /// basically, dx / distance + var/calculated_dx + /// cached value: move this much y for this much distance + /// basically, dy / distance + var/calculated_dy + /// cached sign of dx; 1, -1, or 0 + var/calculated_sdx + /// cached sign of dy; 1, -1, or 0 + var/calculated_sdy + /// our current pixel location on turf + /// byond pixel_x rounds, and we don't want that + /// + /// * at below 0 or at equals to WORLD_ICON_SIZE, we move to the next turf + var/current_px + /// our current pixel location on turf + /// byond pixel_y rounds, and we don't want that + /// + /// * at below 0 or at equals to WORLD_ICON_SIZE, we move to the next turf + var/current_py + /// the pixel location we're moving to, or the [current_px] after this iteration step + /// + /// * used so stuff like hitscan deflections work based on the actual raycasted collision step, and not the prior step. + /// * only valid if [trajectory_moving_to] is set + var/next_px + /// the pixel location we're moving to, or the [current_px] after this iteration step + /// + /// * used so stuff like hitscan deflections work based on the actual raycasted collision step, and not the prior step. + /// * only valid if [trajectory_moving_to] is set + var/next_py + /// the pixel distance we'll have travelled after the current Move() + /// + /// * use this during impact processing or you'll be off by anywhere from 1 to 32 pixels. + /// * only valid if [trajectory_moving_to] is set + var/next_distance + /// used to track if we got kicked forwards after calling Move() + var/trajectory_kick_forwards = 0 + /// to avoid going too fast when kicked forwards by a mirror, if we overshoot the pixels we're + /// supposed to move this gets set to penalize the next move with a weird algorithm + /// that i won't bother explaining + var/trajectory_penalty_applied = 0 + /// currently travelled distance in pixels + var/distance_travelled + /// if we get forcemoved, this gets reset to 0 as a trip + /// this way, we know exactly how far we moved + var/distance_travelled_this_iteration + /// where the physics loop and/or some other thing moving us is trying to move to + /// used to determine where to draw hitscan tracers + // todo: this being here is kinda a symptom that things are handled weirdly but whatever + // optimally physics loop should handle tracking for stuff like animations, not require on hit processing to check turfs + var/turf/trajectory_moving_to + + //* Targeting *// + + /// Originally clicked target + var/atom/original_target + + //* legacy below *// + + //Fired processing vars + var/fired = FALSE //Have we been fired yet + + var/original_angle = 0 //Angle at firing + var/nondirectional_sprite = FALSE //Set TRUE to prevent projectiles from having their sprites rotated based on firing angle + var/spread = 0 //amount (in degrees) of projectile spread + animate_movement = 0 //Use SLIDE_STEPS in conjunction with legacy + var/ricochets = 0 + var/ricochets_max = 2 + var/ricochet_chance = 30 + + //Hitscan + /// do we have a tracer? if not we completely ignore hitscan logic + var/has_tracer = TRUE + var/tracer_type + var/muzzle_type + var/impact_type + + var/miss_sounds + var/ricochet_sounds + var/list/impact_sounds //for different categories, IMPACT_MEAT etc + + //Fancy hitscan lighting effects! + var/hitscan_light_intensity = 1.5 + var/hitscan_light_range = 0.75 + var/hitscan_light_color_override + var/muzzle_flash_intensity = 3 + var/muzzle_flash_range = 1.5 + var/muzzle_flash_color_override + var/impact_light_intensity = 3 + var/impact_light_range = 2 + var/impact_light_color_override + + //Targetting + var/yo = null + var/xo = null + var/turf/starting = null // the projectile's starting turf + var/p_x = 16 + var/p_y = 16 // the pixel location of the tile that the player clicked. Default is the center + + var/def_zone = BP_TORSO + var/mob/firer = null//Who shot it + var/silenced = 0 //Attack message + var/shot_from = "" // name of the object which shot us + + var/dispersion = 0.0 + + // Sub-munitions. Basically, multi-projectile shotgun, rather than pellets. + var/use_submunitions = FALSE + var/only_submunitions = FALSE // Will the projectile delete itself after firing the submunitions? + var/list/submunitions = list() // Assoc list of the paths of any submunitions, and how many they are. [projectilepath] = [projectilecount]. + var/submunition_spread_max = 30 // Divided by 10 to get the percentile dispersion. + var/submunition_spread_min = 5 // Above. + /// randomize spread? if so, evenly space between 0 and max on each side. + var/submunition_constant_spread = FALSE + var/force_max_submunition_spread = FALSE // Do we just force the maximum? + var/spread_submunition_damage = FALSE // Do we assign damage to our sub projectiles based on our main projectile damage? + + //? Damage - default handling + /// damage amount + var/damage = 10 + /// damage tier - goes hand in hand with [damage_armor] + var/damage_tier = BULLET_TIER_DEFAULT + /// todo: legacy - BRUTE, BURN, TOX, OXY, CLONE, HALLOSS, ELECTROCUTE, BIOACID are the only things that should be in here + var/damage_type = BRUTE + /// armor flag for damage - goes hand in hand with [damage_tier] + var/damage_flag = ARMOR_BULLET + /// damage mode - see [code/__DEFINES/combat/damage.dm] + var/damage_mode = NONE + + var/SA_bonus_damage = 0 // Some bullets inflict extra damage on simple animals. + var/SA_vulnerability = null // What kind of simple animal the above bonus damage should be applied to. Set to null to apply to all SAs. + var/nodamage = 0 //Determines if the projectile will skip any damage inflictions + var/taser_effect = 0 //If set then the projectile will apply it's agony damage using stun_effect_act() to mobs it hits, and other damage will be ignored + var/legacy_penetrating = 0 //If greater than zero, the projectile will pass through dense objects as specified by on_penetrate() + //Effects + var/incendiary = 0 //1 for ignite on hit, 2 for trail of fire. 3 maybe later for burst of fire around the impact point. - Mech + var/flammability = 0 //Amount of fire stacks to add for the above. + var/combustion = TRUE //Does this set off flammable objects on fire/hit? + var/stun = 0 + var/weaken = 0 + var/paralyze = 0 + var/irradiate = 0 + var/stutter = 0 + var/eyeblur = 0 + var/drowsy = 0 + var/agony = 0 + var/reflected = 0 // This should be set to 1 if reflected by any means, to prevent infinite reflections. + var/modifier_type_to_apply = null // If set, will apply a modifier to mobs that are hit by this projectile. + var/modifier_duration = null // How long the above modifier should last for. Leave null to be permanent. + var/excavation_amount = 0 // How much, if anything, it drills from a mineral turf. + /// If this projectile is holy. Silver bullets, etc. Currently no effects. + var/holy = FALSE + + // Antimagic + /// Should we check for antimagic effects? + var/antimagic_check = FALSE + /// Antimagic charges to use, if any + var/antimagic_charges_used = 0 + /// Multiplier for damage if antimagic is on the target + var/antimagic_damage_factor = 0 + + var/embed_chance = 0 //Base chance for a projectile to embed + + var/fire_sound = 'sound/weapons/Gunshot_old.ogg' // Can be overriden in gun.dm's fire_sound var. It can also be null but I don't know why you'd ever want to do that. -Ace + + // todo: currently unimplemneted + var/vacuum_traversal = TRUE //Determines if the projectile can exist in vacuum, if false, the projectile will be deleted if it enters vacuum. + + var/no_attack_log = FALSE + +/obj/projectile/Initialize(mapload) + if(islist(base_projectile_effects)) + base_projectile_effects = typelist(NAMEOF(src, base_projectile_effects), base_projectile_effects) + return ..() + +/obj/projectile/Destroy() + // stop processing + STOP_PROCESSING(SSprojectiles, src) + // cleanup + cleanup_hitscan_tracers() + return ..() + +/obj/projectile/proc/process_legacy_penetration(atom/A) + return TRUE + +/obj/projectile/proc/legacy_on_range() //if we want there to be effects when they reach the end of their range + finalize_hitscan_tracers(impact_effect = FALSE, kick_forwards = 8) + + for(var/datum/projectile_effect/effect as anything in base_projectile_effects) + if(effect.hook_lifetime) + effect.on_lifetime(src, impact_ground_on_expiry) + for(var/datum/projectile_effect/effect as anything in additional_projectile_effects) + if(effect.hook_lifetime) + effect.on_lifetime(src, impact_ground_on_expiry) + + if(impact_ground_on_expiry) + impact(loc, PROJECTILE_IMPACT_IS_EXPIRING) + + expire() + +/obj/projectile/proc/fire(set_angle_to, atom/direct_target, no_source_check) + if(only_submunitions) // refactor projectiles whwen holy shit this is awful lmao + // todo: this should make a muzzle flash + qdel(src) + return + + // setup impact checking + impacted = list() + // make sure firer is in it + if(firer && !no_source_check) + impacted[firer] = TRUE + if(ismob(firer)) + var/atom/buckle_iterating = firer.buckled + while(buckle_iterating) + if(impacted[buckle_iterating]) + CRASH("how did we loop in buckle iteration check?") + impacted[buckle_iterating] = TRUE + if(ismob(buckle_iterating)) + var/mob/cast_for_next = buckle_iterating + buckle_iterating = cast_for_next.buckled + else + break + // set angle if needed + if(isnum(set_angle_to)) + set_angle(set_angle_to) + // handle direct hit + if(direct_target) + if(direct_target.bullet_act(src, PROJECTILE_IMPACT_POINT_BLANK, def_zone) & PROJECTILE_IMPACT_FLAGS_SHOULD_GO_THROUGH) + impacted[direct_target] = TRUE + else + // todo: this should make a muzzle flash + qdel(src) + return + // setup physics + setup_physics() + + // legacy below + var/turf/starting = get_turf(src) + if(isnull(angle)) //Try to resolve through offsets if there's no angle set. + if(isnull(xo) || isnull(yo)) + stack_trace("WARNING: Projectile [type] deleted due to being unable to resolve a target after angle was null!") + qdel(src) + return + var/turf/target = locate(clamp(starting + xo, 1, world.maxx), clamp(starting + yo, 1, world.maxy), starting.z) + set_angle(get_visual_angle(src, target)) + if(dispersion) + set_angle(angle + rand(-dispersion, dispersion)) + original_angle = angle + forceMove(starting) + fired = TRUE + // legacy aboev + + // start physics & kickstart movement + if(hitscan) + physics_hitscan() + else + START_PROCESSING(SSprojectiles, src) + physics_iteration(WORLD_ICON_SIZE, SSprojectiles.wait) + +//Spread is FORCED! +/obj/projectile/proc/preparePixelProjectile(atom/target, atom/source, params, spread = 0) + var/turf/curloc = get_turf(source) + var/turf/targloc = get_turf(target) + + if(istype(source, /atom/movable)) + var/atom/movable/MT = source + if(MT.locs && MT.locs.len) // Multi tile! + for(var/turf/T in MT.locs) + if(get_dist(T, target) < get_turf(curloc)) + curloc = get_turf(T) + + trajectory_ignore_forcemove = TRUE + forceMove(get_turf(source)) + trajectory_ignore_forcemove = FALSE + starting = curloc + original_target = target + if(targloc || !params) + yo = targloc.y - curloc.y + xo = targloc.x - curloc.x + set_angle(get_visual_angle(src, targloc) + spread) + + if(isliving(source) && params) + var/list/calculated = calculate_projectile_angle_and_pixel_offsets(source, params) + p_x = calculated[2] + p_y = calculated[3] + + set_angle(calculated[1] + spread) + else if(targloc) + yo = targloc.y - curloc.y + xo = targloc.x - curloc.x + set_angle(get_visual_angle(src, targloc) + spread) + else + stack_trace("WARNING: Projectile [type] fired without either mouse parameters, or a target atom to aim at!") + qdel(src) + +/proc/calculate_projectile_angle_and_pixel_offsets(mob/user, params) + var/list/mouse_control = params2list(params) + var/p_x = 0 + var/p_y = 0 + var/angle = 0 + if(mouse_control["icon-x"]) + p_x = text2num(mouse_control["icon-x"]) + if(mouse_control["icon-y"]) + p_y = text2num(mouse_control["icon-y"]) + if(mouse_control["screen-loc"]) + //Split screen-loc up into X+Pixel_X and Y+Pixel_Y + var/list/screen_loc_params = splittext(mouse_control["screen-loc"], ",") + + //Split X+Pixel_X up into list(X, Pixel_X) + var/list/screen_loc_X = splittext(screen_loc_params[1],":") + + //Split Y+Pixel_Y up into list(Y, Pixel_Y) + var/list/screen_loc_Y = splittext(screen_loc_params[2],":") + var/x = text2num(screen_loc_X[1]) * 32 + text2num(screen_loc_X[2]) - 32 + var/y = text2num(screen_loc_Y[1]) * 32 + text2num(screen_loc_Y[2]) - 32 + + //Calculate the "resolution" of screen based on client's view and world's icon size. This will work if the user can view more tiles than average. + var/screenviewX = user.client.current_viewport_width * world.icon_size + var/screenviewY = user.client.current_viewport_height * world.icon_size + + var/ox = round(screenviewX/2) - user.client.pixel_x //"origin" x + var/oy = round(screenviewY/2) - user.client.pixel_y //"origin" y + angle = arctan(y - oy, x - ox) + return list(angle, p_x, p_y) + +/obj/projectile/proc/legacy_redirect(x, y, starting, source) + reflected = TRUE + old_style_target(locate(x, y, z), starting? get_turf(starting) : get_turf(source)) + +/obj/projectile/proc/old_style_target(atom/target, atom/source) + if(!source) + source = get_turf(src) + starting = get_turf(source) + original_target = target + set_angle(get_visual_angle(source, target)) + +/obj/projectile/proc/vol_by_damage() + if(damage) + return clamp((damage) * 0.67, 30, 100)// Multiply projectile damage by 0.67, then clamp the value between 30 and 100 + else + return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume. + +//Checks if the projectile is eligible for embedding. Not that it necessarily will. +/obj/projectile/proc/can_embed() + //embed must be enabled and damage type must be brute + if(embed_chance == 0 || damage_type != BRUTE) + return 0 + return 1 + +/obj/projectile/proc/get_structure_damage() + if(damage_type == BRUTE || damage_type == BURN) + return damage + return 0 + +/obj/projectile/proc/check_fire(atom/target as mob, mob/living/user as mob) //Checks if you can hit them or not. + check_trajectory(target, user, pass_flags, atom_flags) + +/** + * i hate everything + * + * todo: refactor guns + * projectiles + * and everything else + * + * i am losing my fucking mind + * this shouldn't have to fucking exist because the ammo casing and/or gun should be doing it + * and submunitions SHOULDNT BE HANDLED HERE!! + */ +/obj/projectile/proc/launch_projectile_common(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0) + original_target = target + def_zone = check_zone(target_zone) + firer = user + + if(use_submunitions && submunitions.len) + var/temp_min_spread = 0 + if(force_max_submunition_spread) + temp_min_spread = submunition_spread_max + else + temp_min_spread = submunition_spread_min + + var/damage_override = null + + if(spread_submunition_damage) + damage_override = damage + if(nodamage) + damage_override = 0 + + var/projectile_count = 0 + + for(var/proj in submunitions) + projectile_count += submunitions[proj] + + damage_override = round(damage_override / max(1, projectile_count)) + + for(var/path in submunitions) + var/amt = submunitions[path] + for(var/count in 1 to amt) + var/obj/projectile/SM = new path(get_turf(loc)) + SM.shot_from = shot_from + SM.silenced = silenced + if(!isnull(damage_override)) + SM.damage = damage_override + if(submunition_constant_spread) + SM.dispersion = 0 + var/calculated = angle + round((count / amt - 0.5) * submunition_spread_max, 1) + SM.launch_projectile(target, target_zone, user, params, calculated) + else + SM.dispersion = rand(temp_min_spread, submunition_spread_max) / 10 + SM.launch_projectile(target, target_zone, user, params, angle_override) + +/obj/projectile/proc/launch_projectile(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0) + var/direct_target + if(get_turf(target) == get_turf(src)) + direct_target = target + + preparePixelProjectile(target, user? user : get_turf(src), params, forced_spread) + launch_projectile_common(target, target_zone, user, params, angle_override, forced_spread) + return fire(angle_override, direct_target) + +//called to launch a projectile from a gun +/obj/projectile/proc/launch_from_gun(atom/target, target_zone, mob/user, params, angle_override, forced_spread, obj/item/gun/launcher) + + shot_from = launcher.name + silenced = launcher.silenced + + return launch_projectile(target, target_zone, user, params, angle_override, forced_spread) + +/obj/projectile/proc/launch_projectile_from_turf(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0) + var/direct_target + if(get_turf(target) == get_turf(src)) + direct_target = target + + preparePixelProjectile(target, user? user : get_turf(src), params, forced_spread) + launch_projectile_common(target, target_zone, user, params, angle_override, forced_spread) + return fire(angle_override, direct_target) + +/** + * Standard proc to determine damage when impacting something. This does not affect the special damage variables/effect variables, only damage and damtype. + * May or may not be called before/after armor calculations. + * + * @params + * - target The atom hit + * + * @return Damage to apply to target. + */ +/obj/projectile/proc/run_damage_vulnerability(atom/target) + var/final_damage = damage + if(isliving(target)) + var/mob/living/L = target + if(issimple(target)) + var/mob/living/simple_mob/SM = L + if(SM.mob_class & SA_vulnerability) + final_damage += SA_bonus_damage + if(L.anti_magic_check(TRUE, TRUE, antimagic_charges_used, FALSE)) + final_damage *= antimagic_damage_factor + return final_damage + +/** + * Probably isn't needed but saves me the time and I can regex this later: + * Gets the final `damage` that should be used on something + */ +/obj/projectile/proc/get_final_damage(atom/target) + return run_damage_vulnerability(target) + +// !legacy code above! + +//* Collision Handling *// + +/obj/projectile/CanAllowThrough() + SHOULD_CALL_PARENT(FALSE) + return TRUE + +/obj/projectile/CanPassThrough(atom/blocker, turf/target, blocker_opinion) + // performance + SHOULD_CALL_PARENT(FALSE) + // always can go through already impacted things + if(impacted[blocker]) + return TRUE + return blocker_opinion + +/obj/projectile/Crossed(atom/movable/AM) + ..() + scan_crossed_atom(AM) + +// todo: should we inline this? +/obj/projectile/proc/scan_crossed_atom(atom/movable/target) + if(!should_impact(target)) + return + impact(target) + +/obj/projectile/Bump(atom/A) + . = ..() + impact_loop(get_turf(A), A) + +/obj/projectile/forceMove(atom/target) + var/is_a_jump = isturf(target) != isturf(loc) || target.z != z || !trajectory_ignore_forcemove + if(is_a_jump) + record_hitscan_end() + render_hitscan_tracers() + . = ..() + if(!.) + stack_trace("projectile forcemove failed; please do not try to forcemove projectiles to invalid locations!") + distance_travelled_this_iteration = 0 + if(!trajectory_ignore_forcemove) + reset_physics_to_turf() + if(is_a_jump) + record_hitscan_start() + +/obj/projectile/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change) + . = ..() + // if we're being yanked, we'll get Moved() again + if(movable_flags & MOVABLE_IN_MOVED_YANK) + return + // not even fired yet + if(!fired) + return + // scan the turf for anything we need to hit + scan_moved_turf(loc) + // trigger effects + for(var/datum/projectile_effect/effect as anything in base_projectile_effects) + if(effect.hook_moved) + effect.on_moved(src, old_loc) + for(var/datum/projectile_effect/effect as anything in additional_projectile_effects) + if(effect.hook_moved) + effect.on_moved(src, old_loc) + +// todo: should we inline this? +/obj/projectile/proc/scan_moved_turf(turf/tile) + if(original_target?.loc != tile) + return + if(!should_impact(original_target)) + return + impact(original_target) + +/** + * checks if we're valid to hit a target + * + * this is called 'should' because it's not called in impact() + * this checks if we should try to impact when processing collisions + * this doesn't actually prevent us from having an impact call + */ +/obj/projectile/proc/should_impact(atom/target, is_colliding_us) + // 1. emulate the usual physics / cross + // remember that impact_loop() scans all atoms, not just the hit one. + if(impacted[target]) + return FALSE + if(QDELETED(target)) + return FALSE + // 1.5: legacy bullshit + if(target.is_incorporeal()) + return FALSE + // 2. are they the thing blocking us? + if(is_colliding_us) + return TRUE + // 3. process projectile things + if(target == original_target) + return TRUE + else if(!target.density || (target.pass_flags_self & pass_flags)) + return FALSE + else if(target.layer < PROJECTILE_HIT_THRESHOLD_LAYER) + return FALSE + return TRUE + +/** + * this strangely named proc is basically the hit processing loop + * + * "now why the hells would you do this" + * + * well, you see, turf movement handling doesn't support what we need to do, + * and for good reason. + * + * most of the time, turf movement handling is more than enough for any game use case. + * it is not nearly accurate/comprehensive enough for projectiles + * and we're not going to make it that, because that's a ton of overhead for everything else + * + * so instead, projectiles handle it themselves on a Bump(). + * + * when this happens, the projectile should hit everything that's going to collide it anyways + * in the turf, not just one thing; this way, hits are instant for a given collision. + * + * @params + * * was_moving_onto - the turf we were moving to + * * bumped - what bumped us? + */ +/obj/projectile/proc/impact_loop(turf/was_moving_onto, atom/bumped) + var/impact_return + // so unfortunately, only one border object is considered here + // why? + // because you see, for speed reasons we're not going to iterate once just to gather border. + // so we assume that 'bumped' is border, or it just doesn't happen. + + // make sure we're not inf looping + ASSERT(!(impacted[bumped])) + // see if we should impact + impact_return = impact(bumped) + if(!(impact_return & PROJECTILE_IMPACT_CONTINUE_LOOP)) + return + + // at this point we're technically safe to just move again because + // we processed bumped + // problem is, that's O(n^2) behavior + // we don't want to process just one bumped atom + // as turf/Enter() will loop through everything again + // + // we want to process all of them. + + // at this point you might ask + // why not use MOVEMENT_UNSTOPPABLE? + // if we did, we wouldn't have the border-prioritization we have + // that'd be bad as you'd be hit by a bullet behind a directional window + + // wow, projectiles are annoying to deal with + + // begin: main loop + + // first, targeted atom + if(original_target?.loc == was_moving_onto) + if(should_impact(original_target)) + impact_return = impact(bumped) + if(!(impact_return & PROJECTILE_IMPACT_CONTINUE_LOOP)) + return + + // then, mobs + for(var/mob/mob_target in was_moving_onto) + if(!should_impact(mob_target)) + continue + impact_return = impact(mob_target) + if(!(impact_return & PROJECTILE_IMPACT_CONTINUE_LOOP)) + return + + // then, objs + for(var/obj/obj_target in was_moving_onto) + if(!should_impact(obj_target)) + continue + impact_return = impact(obj_target) + if(!(impact_return & PROJECTILE_IMPACT_CONTINUE_LOOP)) + return + + // then, the turf + if(should_impact(was_moving_onto)) + impact_return = impact(was_moving_onto) + if(!(impact_return & PROJECTILE_IMPACT_CONTINUE_LOOP)) + return + + // if we passed everything and we're still going, + // we can safely move onto their turf again, and this time we should succeed. + if(trajectory_moving_to) + Move(trajectory_moving_to) + +//* Lifetime & Deletion *// + +// todo: on_lifetime() --> expire() + +/** + * Called to delete if: + * + * * ran out of range + * * hit something and shouldn't pass through + * + * @params + * * impacting - we're deleting from impact, rather than range + */ +/obj/projectile/proc/expire(impacting) + qdel(src) + +//* Impact Processing *// + +/** + * Called to perform an impact + * + * @params + * * target - thing being hit + * * impact_flags - impact flags to feed in + * * def_zone - zone to hit; otherwise this'll be calculated. + * + * @return resultant impact_flags + */ +/obj/projectile/proc/impact(atom/target, impact_flags, def_zone = src.def_zone || BP_TORSO) + SHOULD_NOT_OVERRIDE(TRUE) + + if(impacted[target]) + return impact_flags | PROJECTILE_IMPACT_PASSTHROUGH | PROJECTILE_IMPACT_DUPLICATE | PROJECTILE_IMPACT_CONTINUE_LOOP + impacted[target] = TRUE + var/where_we_were = loc + impact_flags = pre_impact(target, impact_flags, def_zone) + var/keep_going + + // priority 1: delete? + if(impact_flags & PROJECTILE_IMPACT_FLAGS_SHOULD_DELETE) + if(hitscanning) + finalize_hitscan_tracers() + qdel(src) + return impact_flags + // priority 2: should we hit? + if(impact_flags & PROJECTILE_IMPACT_FLAGS_SHOULD_NOT_HIT) + keep_going = TRUE + // phasing? + if(impact_flags & PROJECTILE_IMPACT_PHASE) + impact_flags = on_phase(target, impact_flags, def_zone) + // reflect? + else if(impact_flags & PROJECTILE_IMPACT_REFLECT) + impact_flags = on_reflect(target, impact_flags, def_zone) + // else, is passthrough. do nothing + else + impact_flags = target.bullet_act(src, impact_flags, def_zone, 1) + // did we pierce? + if(impact_flags & PROJECTILE_IMPACT_PIERCE) + keep_going = TRUE + impact_flags = on_pierce(target, impact_flags, def_zone) + // are we otherwise meant to keep going? + else if(impact_flags & PROJECTILE_IMPACT_FLAGS_SHOULD_GO_THROUGH) + keep_going = TRUE + + // did anything triggered up above trigger a delete? + if(impact_flags & PROJECTILE_IMPACT_FLAGS_SHOULD_DELETE) + if(hitscanning) + finalize_hitscan_tracers() + qdel(src) + return impact_flags + + // trigger projectile effects + if(base_projectile_effects || additional_projectile_effects) + // todo: can we move this to on_impact_new and have 'blocked' passed in? hrm. + for(var/datum/projectile_effect/effect as anything in base_projectile_effects) + if(effect.hook_impact) + impact_flags = effect.on_impact(src, target, impact_flags, def_zone) + for(var/datum/projectile_effect/effect as anything in additional_projectile_effects) + if(effect.hook_impact) + impact_flags = effect.on_impact(src, target, impact_flags, def_zone) + // did anything triggered up above trigger a delete? + if(impact_flags & PROJECTILE_IMPACT_FLAGS_SHOULD_DELETE) + if(hitscanning) + finalize_hitscan_tracers() + qdel(src) + return impact_flags + + // see if we should keep going or delete + if(keep_going) + if(loc == where_we_were) + // if we are supposed to keep going and we didn't get yanked, continue the impact loop. + impact_flags |= PROJECTILE_IMPACT_CONTINUE_LOOP + else + // or if we aren't supposed to keep going, delete. + if(hitscanning) + if(trajectory_moving_to) + // create tracers + var/datum/point/visual_impact_point = get_intersection_point(trajectory_moving_to) + // it's possible to not have an intersection point, if say, angle was being messed with mid-move + // this entire system is suboptimal design-wise but atleast it's fast. + if(visual_impact_point) + // kick it forwards a bit + visual_impact_point.shift_in_projectile_angle(angle, 2) + // draw + finalize_hitscan_tracers(visual_impact_point, impact_effect = TRUE) + else + finalize_hitscan_tracers(impact_effect = TRUE, kick_forwards = 32) + else + finalize_hitscan_tracers(impact_effect = TRUE, kick_forwards = 32) + expire(TRUE) + + return impact_flags + +/** + * Called at the start of impact. + * + * * Hooks to return flags / whatnot should happen here + * * You are allowed to edit the projectile here, but it is absolutely not recommended. + * + * @return new impact_flags + */ +/obj/projectile/proc/pre_impact(atom/target, impact_flags, def_zone) + if(target.pass_flags_self & pass_flags_phase) + return impact_flags | PROJECTILE_IMPACT_PHASE + if(target.pass_flags_self & pass_flags_pierce) + return impact_flags | PROJECTILE_IMPACT_PIERCE + return impact_flags + +/** + * Called after bullet_act() of the target. + * + * * Please take into account impact_flags. + * * Most impact flags returned are not re-checked for performance; pierce/phase calculations should be done in pre_impact(). + * * please see [/atom/proc/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args)] + * * Args at this point are no longer mutable after the ..() call. + * + * Things to keep in mind, if you ignore the above and didn't read bullet_act(): + * * Args are changed directly and passed up, but not passed back down. This means setting efficiency at base of /on_impact doesn't change a subtype's call + * * This also means you need to default efficiency to 1 if you have things acting on it, as it won't be propagated for you. + * * 'efficiency' is extremely powerful + * * impact_flags having PROJECTILE_IMPACT_DELETE is a good sign to delete and do nothing else. + * + * todo: add PROJECTILE_IMPACT_DELETE_AFTER as opposed to DELETE? so rest of effects can still run + * + * @return new impact_flags; only PROJECTILE_IMPACT_DELETE is rechecked. + */ +/obj/projectile/proc/on_impact(atom/target, impact_flags, def_zone, efficiency = 1) + //! legacy shit + var/blocked = clamp((1 - efficiency) * 100, 0, 100) + if(damage && damage_type == BURN) + var/turf/T = get_turf(target) + if(T) + T.hotspot_expose(700, 5) + if(isliving(target)) + var/mob/living/L = target + L.apply_effects(stun, weaken, paralyze, irradiate, stutter, eyeblur, drowsy, agony, blocked, incendiary, flammability) + if(modifier_type_to_apply) + L.add_modifier(modifier_type_to_apply, modifier_duration) + //! end + return impact_flags + +/** + * called in bullet_act() to redirect our impact to another atom + * + * use like this: + * + * `return proj.impact_redirect(target, args)` + * + * * You **must** use this, not just `return target.bullet_act(arglist(args))` + * * This does book-keeping like adding the target to permutated, ensure the target can't be hit multiple times in a row, and more. + * * Don't be too funny about bullet_act_args, it's a directly passed in args list. Don't be stupid. + */ +/obj/projectile/proc/impact_redirect(atom/target, list/bullet_act_args) + if(impacted[target]) + return bullet_act_args[BULLET_ACT_ARG_FLAGS] | PROJECTILE_IMPACT_DUPLICATE + bullet_act_args[BULLET_ACT_ARG_FLAGS] |= PROJECTILE_IMPACT_INDIRECTED + return target.bullet_act(arglist(bullet_act_args)) + +/** + * phasing through + * + * * Most impact flags returned are not re-checked for performance; pierce/phase calculations should be done in pre_impact(). + * + * @return new impact flags; only PROJETILE_IMPACT_DELETE is rechecked. + */ +/obj/projectile/proc/on_phase(atom/target, impact_flags, def_zone) + return impact_flags + +/** + * reflected off of + * + * * Most impact flags returned are not re-checked for performance; pierce/phase calculations should be done in pre_impact(). + * + * @return new impact flags; only PROJETILE_IMPACT_DELETE is rechecked. + */ +/obj/projectile/proc/on_reflect(atom/target, impact_flags, def_zone) + return impact_flags + +/** + * piercing through + * + * * Most impact flags returned are not re-checked for performance; pierce/phase calculations should be done in pre_impact(). + * + * @return new impact flags; only PROJETILE_IMPACT_DELETE is rechecked. + */ +/obj/projectile/proc/on_pierce(atom/target, impact_flags, def_zone) + return impact_flags + +//* Impact Processing - Combat *// + +/** + * processes default hit probability for baymiss + * + * * This is called by things like /mob/living as needed; there is no default baymiss handling on /projectile anymore. + * * More than 100 target_opinion means that much % more than 100 of *not missing*. + * * e.g. 200 target_opinion makes a 75% inherent hit chance (25% miss chance) to 87.5% hit cahnce (12.5% miss chance) + * + * todo: 0 to 100 for accuracy might not be amazing; maybe allow negative values evasion-style? + * + * @params + * * target - what we're hitting + * * target_opinion - the return from processing hit chance on their side + * * distance - distance in pixels + * * impact_check - are we checking for impact? this way things like pellets can do their own rolls after 100% hitting + * + * @return hit probability as % in [0, 100]; > 100 is allowed. + */ +/obj/projectile/proc/process_accuracy(atom/target, target_opinion = 100, distance, impact_check) + if(isnull(distance)) + distance = (trajectory_moving_to ? next_distance : distance_travelled) * angle_chebyshev_divisor + if(accuracy_disabled) + return 100 + . = 100 + // perform accuracy curving + if(distance > accuracy_perfect_range) + . = accuracy_drop_start + var/extra_distance = distance - accuracy_perfect_range + var/drop_percent = extra_distance * accuracy_drop_slope + . = clamp(. - drop_percent, ., accuracy_drop_end) + if(accuracy_overall_modify != 1) + if(accuracy_overall_modify < 1) + // below 1: multiplier for hit chance + . *= accuracy_overall_modify + else + // above 1: divisor for miss chance + . = 100 - ((100 - .) / accuracy_overall_modify) + if(target_opinion < 100) + . *= (target_opinion / 100) + else if(target_opinion > 100) + . = 100 - ((100 - .) / (target_opinion / 100)) + +/** + * processes zone accuracy + * + * * this is here to override 'special' baymiss, like 'don't even hit this zone' systems. + * + * @params + * * target - what we're hitting + * * target_opinion - the return from processing hit zone on their side + * * distance - distance in pixels + * * impact_check - are we checking for impact? this way things like pellets can do their own processing + */ +/obj/projectile/proc/process_zone_miss(atom/target, target_opinion, distance, impact_check) + return target_opinion + +/** + * Applies the standard damage instance to an entity. + * + * @params + * * target - thing being hit + * * efficiency - 0 to 1+ - efficiency of hit, where 0% is full block + * * impact_flags - impact flags passed in + * * hit_zone - zone to hit + * + * @return BULLET_ACT_* flags to append into the calling bullet_act(). + */ +/obj/projectile/proc/inflict_impact_damage(atom/target, efficiency, impact_flags, hit_zone) + . = NONE + + //! LEGACY COMBAT CODE + // SHIM!!! + var/list/shieldcall_modified_args = target.check_damage_instance(damage, damage_type, damage_tier, damage_flag, damage_mode, ATTACK_TYPE_PROJECTILE, src, SHIELDCALL_FLAG_SECOND_CALL, hit_zone) + // todo: this handling very obviously should not be here + // dear lord this code is a dumpster fire + if(shieldcall_modified_args[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAGS_PIERCE_ATTACK) + . |= PROJECTILE_IMPACT_REFLECT + if(shieldcall_modified_args[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAGS_BLOCK_ATTACK) + return + // END + if(isliving(target)) + var/mob/living/L = target + //Armor + var/soaked = L.get_armor_soak(hit_zone, src.damage_flag, src.armor_penetration) + var/absorb = L.run_armor_check(hit_zone, src.damage_flag, src.armor_penetration) + var/proj_sharp = is_sharp(src) + var/proj_edge = has_edge(src) + var/final_damage = src.get_final_damage(target) * efficiency + + if ((proj_sharp || proj_edge) && (soaked >= round(src.damage*0.8))) + proj_sharp = 0 + proj_edge = 0 + + if ((proj_sharp || proj_edge) && prob(L.legacy_mob_armor(hit_zone, src.damage_flag))) + proj_sharp = 0 + proj_edge = 0 + + var/list/impact_sounds = islist(src.impact_sounds)? LAZYACCESS(src.impact_sounds, L.get_bullet_impact_effect_type(hit_zone)) : src.impact_sounds + if(length(impact_sounds)) + playsound(L, pick(impact_sounds), 75) + else if(!isnull(impact_sounds)) + playsound(L, impact_sounds, 75) + + //Stun Beams + if(src.taser_effect) + L.stun_effect_act(0, src.agony, hit_zone, src) + to_chat(L, "You have been hit by [src]!") + if(!src.nodamage) + L.apply_damage(final_damage, src.damage_type, hit_zone, absorb, soaked, 0, src, sharp=proj_sharp, edge=proj_edge) + return + + if(!src.nodamage) + L.apply_damage(final_damage, src.damage_type, hit_zone, absorb, soaked, 0, src, sharp=proj_sharp, edge=proj_edge) + //! END + + for(var/datum/projectile_effect/effect as anything in base_projectile_effects) + if(effect.hook_damage) + effect.on_damage(src, target, impact_flags, hit_zone, efficiency) + for(var/datum/projectile_effect/effect as anything in additional_projectile_effects) + if(effect.hook_damage) + effect.on_damage(src, target, impact_flags, hit_zone, efficiency) + + if(legacy_penetrating > 0) + if(process_legacy_penetration(target)) + . |= PROJECTILE_IMPACT_PIERCE | PROJECTILE_IMPACT_PASSTHROUGH + +/** + * wip algorithm to dampen a projectile when it pierces + * + * * entity - thing hit + * * force - nominal force to resist the damping; generally, projectiles at this lose a moderate chunk of energy, while 2x loses minimal, 0.5x loses a lot. + * * tier - effective armor tier of object; modulates actual energy lost + */ +/obj/projectile/proc/dampen_on_pierce_experimental(atom/entity, force, tier) + var/tdiff = damage_tier - tier + var/dmult = src.damage / force + var/malus = dmult >= 1 ? ((1 / dmult) ** tdiff * 10) : (10 * ((1 / dmult) / (1 + tdiff))) + src.damage = clamp(src.damage - malus, src.damage * 0.5, src.damage) + +//* Targeting *// + +/** + * Checks if something is a valid target when directly clicked. + */ +/obj/projectile/proc/is_valid_target(atom/target) + if(isobj(target)) + var/obj/O = target + return O.obj_flags & OBJ_RANGE_TARGETABLE + else if(isliving(target)) + return TRUE + else if(isturf(target)) + return target.density + return FALSE diff --git a/code/modules/projectiles/projectile/projectile_effect.dm b/code/modules/projectiles/projectile/projectile_effect.dm new file mode 100644 index 000000000000..cadfae340b97 --- /dev/null +++ b/code/modules/projectiles/projectile/projectile_effect.dm @@ -0,0 +1,51 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +/** + * projectile effects + * + * * can be anonymous type'd, so global cache exists for given projectile typepath, not here! + */ +/datum/projectile_effect + /// has impact effect + var/hook_impact = FALSE + /// has moved effect + var/hook_moved = FALSE + /// has on-range / lifetime expired effect + var/hook_lifetime = FALSE + /// has damage effect + var/hook_damage = FALSE + +/** + * * you'll note that [efficiency] is not a thing here + * * this is because this runs regardless of target's opinion + * * this means you should probably be careful and check impact_flags! + * + * @return new impact flags + */ +/datum/projectile_effect/proc/on_impact(obj/projectile/proj, atom/target, impact_flags, def_zone) + return impact_flags + +/** + * * you'll note that [efficiency] **is** a thing here + * * this only runs if we're able to damage the target + * + * @return new impact flags + */ +/datum/projectile_effect/proc/on_damage(obj/projectile/proj, atom/target, impact_flags, def_zone, efficiency) + return impact_flags + +/** + * Do not delete the projectile, the projectile does that. + * + * todo: add way to delete projectile so it doesn't drop stuff i guess for foam and whatnot + * + * @params + * * proj - the projectile + * * impact_ground_on_expiry - we should impact the ground on expiry; a projectile var, but relay'd in for override capability later + */ +/datum/projectile_effect/proc/on_lifetime(obj/projectile/proj, impact_ground_on_expiry) + return + +/datum/projectile_effect/proc/on_moved(obj/projectile/proj, atom/old_loc) + return diff --git a/code/modules/projectiles/projectile/animate.dm b/code/modules/projectiles/projectile/subtypes/animate.dm similarity index 100% rename from code/modules/projectiles/projectile/animate.dm rename to code/modules/projectiles/projectile/subtypes/animate.dm diff --git a/code/modules/projectiles/projectile/subtypes/arc.dm b/code/modules/projectiles/projectile/subtypes/arc.dm new file mode 100644 index 000000000000..7245b3001119 --- /dev/null +++ b/code/modules/projectiles/projectile/subtypes/arc.dm @@ -0,0 +1,82 @@ + +////////////// +// Subtypes +////////////// + +// This is a test projectile in the sense that its testing the code to make sure it works, +// as opposed to a 'can I hit this thing' projectile. +/obj/projectile/arc/test/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(!isturf(target)) + return + var/turf/T = target + new /obj/effect/explosion(T) + T.color = "#FF0000" + +// Generic, Hivebot related +/obj/projectile/arc/blue_energy + name = "energy missile" + icon_state = "force_missile" + damage = 15 + damage_type = BURN + +/obj/projectile/arc/blue_energy/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(!isturf(target)) + return + var/turf/T = target + for(var/mob/living/L in T) + impact(L) + +// Fragmentation arc shot +/obj/projectile/arc/fragmentation + name = "fragmentation shot" + icon_state = "shell" + var/list/fragment_types = list( + /obj/projectile/bullet/pellet/fragment, /obj/projectile/bullet/pellet/fragment, \ + /obj/projectile/bullet/pellet/fragment, /obj/projectile/bullet/pellet/fragment/strong + ) + var/fragment_amount = 63 // Same as a grenade. + var/spread_range = 7 + +/obj/projectile/arc/fragmentation/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(!isturf(target)) + return + var/turf/T = target + T.shrapnel_explosion(fragment_amount, spread_range, fragment_types, src, name, firer) + +/obj/projectile/arc/fragmentation/mortar + icon_state = "mortar" + fragment_amount = 10 + spread_range = 3 + +// EMP arc shot +/obj/projectile/arc/emp_blast + name = "emp blast" + icon_state = "bluespace" + +/obj/projectile/arc/emp_blast/on_impact(atom/target, impact_flags, def_zone, efficiency) + var/turf/T = target + empulse(T, 2, 4, 7, 10) // Normal EMP grenade. + return impact_flags + +/obj/projectile/arc/emp_blast/weak/on_impact(atom/target, impact_flags, def_zone, efficiency) + var/turf/T = target + empulse(T, 1, 2, 3, 4) // Sec EMP grenade. + return impact_flags + +// Radiation arc shot +/obj/projectile/arc/radioactive + name = "radiation blast" + icon_state = "green_pellet" + icon_scale_x = 2 + icon_scale_y = 2 + var/rad_power = RAD_INTENSITY_PROJ_ARC + +/obj/projectile/arc/radioactive/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(!isturf(target)) + return + var/turf/T = target + radiation_pulse(T, rad_power) diff --git a/code/modules/projectiles/projectile/beam/beams.dm b/code/modules/projectiles/projectile/subtypes/beam/beams.dm similarity index 89% rename from code/modules/projectiles/projectile/beam/beams.dm rename to code/modules/projectiles/projectile/subtypes/beam/beams.dm index b90a9342d3a7..617097a40deb 100644 --- a/code/modules/projectiles/projectile/beam/beams.dm +++ b/code/modules/projectiles/projectile/subtypes/beam/beams.dm @@ -6,6 +6,7 @@ damage = 40 damage_type = BURN damage_flag = ARMOR_LASER + projectile_type = PROJECTILE_TYPE_BEAM | PROJECTILE_TYPE_PHOTONIC eyeblur = 4 var/frequency = 1 hitscan = TRUE @@ -76,14 +77,14 @@ /obj/projectile/beam/heavylaser/cannon damage = 80 - armor_penetration = 50 + armor_penetration = 45 light_color = "#FF0D00" /obj/projectile/beam/xray name = "xray beam" icon_state = "xray" fire_sound = 'sound/weapons/eluger.ogg' - damage = 25 + damage = 30 armor_penetration = 50 light_color = "#00CC33" @@ -131,7 +132,8 @@ name = "emitter beam" icon_state = "emitter" fire_sound = 'sound/weapons/emitter.ogg' - damage = 0 // The actual damage is computed in /code/modules/power/singularity/emitter.dm + damage = 40 + armor_penetration = 70 light_color = "#00CC33" excavation_amount = 70 // 3 shots to mine a turf @@ -143,7 +145,6 @@ name = "lasertag beam" damage = 0 eyeblur = 0 - no_attack_log = 1 damage_type = BURN damage_flag = ARMOR_LASER @@ -157,23 +158,27 @@ tracer_type = /obj/effect/projectile/tracer/laser_blue impact_type = /obj/effect/projectile/impact/laser_blue -/obj/projectile/beam/lasertag/blue/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/beam/lasertag/blue/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(ishuman(target)) var/mob/living/carbon/human/M = target if(istype(M.wear_suit, /obj/item/clothing/suit/redtag)) - M.afflict_paralyze(20 * 5) - return 1 + M.afflict_paralyze(1.5 SECONDS) /obj/projectile/beam/lasertag/red icon_state = "laser" light_color = "#FF0D00" -/obj/projectile/beam/lasertag/red/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/beam/lasertag/red/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(ishuman(target)) var/mob/living/carbon/human/M = target if(istype(M.wear_suit, /obj/item/clothing/suit/bluetag)) - M.afflict_paralyze(20 * 5) - return 1 + M.afflict_paralyze(1.5 SECONDS) /obj/projectile/beam/lasertag/omni//A laser tag bolt that stuns EVERYONE icon_state = "omnilaser" @@ -183,12 +188,14 @@ tracer_type = /obj/effect/projectile/tracer/laser_omni impact_type = /obj/effect/projectile/impact/laser_omni -/obj/projectile/beam/lasertag/omni/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/beam/lasertag/omni/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(ishuman(target)) var/mob/living/carbon/human/M = target if((istype(M.wear_suit, /obj/item/clothing/suit/bluetag))||(istype(M.wear_suit, /obj/item/clothing/suit/redtag))) - M.afflict_paralyze(20 * 5) - return 1 + M.afflict_paralyze(1.5 SECONDS) /obj/projectile/beam/sniper name = "sniper beam" @@ -267,18 +274,18 @@ tracer_type = /obj/effect/projectile/tracer/laser_omni impact_type = /obj/effect/projectile/impact/laser_omni -/obj/projectile/beam/stun/disabler/on_hit(atom/target, blocked = 0, def_zone) - . = ..(target, blocked, def_zone) - - if(. && istype(target, /mob/living/silicon/robot) && prob(agony)) +/obj/projectile/beam/stun/disabler/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(!(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT)) + return + if(istype(target, /mob/living/silicon/robot) && prob(agony)) var/mob/living/silicon/robot/R = target var/drainamt = agony * (rand(5, 15) / 10) // 100 to 300 drain R.drain_energy(DYNAMIC_CELL_UNITS_TO_KJ(drainamt * 10)) if(istype(firer, /mob/living/silicon/robot)) // Mischevious sappers, the swarm drones are. var/mob/living/silicon/robot/A = firer - if(A.cell) - A.cell.give(drainamt * 2) + A.cell?.give(drainamt * 2) /obj/projectile/beam/shock name = "shock beam" diff --git a/code/modules/projectiles/projectile/beam/beams_vr.dm b/code/modules/projectiles/projectile/subtypes/beam/beams_vr.dm similarity index 84% rename from code/modules/projectiles/projectile/beam/beams_vr.dm rename to code/modules/projectiles/projectile/subtypes/beam/beams_vr.dm index 7e2e9e04484c..7e21f84b61cc 100644 --- a/code/modules/projectiles/projectile/beam/beams_vr.dm +++ b/code/modules/projectiles/projectile/subtypes/beam/beams_vr.dm @@ -26,9 +26,13 @@ tracer_type = /obj/effect/projectile/tracer/xray impact_type = /obj/effect/projectile/impact/xray -/obj/projectile/beam/energy_net/on_hit(var/atom/netted) - do_net(netted) - ..() +/obj/projectile/beam/energy_net/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(!ismob(target)) + return + do_net(target) /obj/projectile/beam/energy_net/proc/do_net(var/mob/M) var/obj/item/energy_net/net = new (get_turf(M)) @@ -47,7 +51,6 @@ icon_state = "healbeam" damage = 0 //stops it damaging walls nodamage = TRUE - no_attack_log = TRUE damage_type = BURN damage_flag = ARMOR_LASER light_color = "#80F5FF" @@ -58,7 +61,11 @@ tracer_type = /obj/effect/projectile/tracer/medigun impact_type = /obj/effect/projectile/impact/medigun -/obj/projectile/beam/medigun/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/beam/medigun/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(istype(target, /mob/living/carbon/human)) var/mob/living/carbon/human/M = target if(M.health < M.maxHealth) @@ -74,4 +81,3 @@ M.adjustFireLoss(-15) M.adjustToxLoss(-5) M.adjustOxyLoss(-5) - return 1 diff --git a/code/modules/projectiles/projectile/beam/blaster.dm b/code/modules/projectiles/projectile/subtypes/beam/blaster.dm similarity index 100% rename from code/modules/projectiles/projectile/beam/blaster.dm rename to code/modules/projectiles/projectile/subtypes/beam/blaster.dm diff --git a/code/modules/projectiles/projectile/blob.dm b/code/modules/projectiles/projectile/subtypes/blob.dm similarity index 92% rename from code/modules/projectiles/projectile/blob.dm rename to code/modules/projectiles/projectile/subtypes/blob.dm index 6b643c160f2b..cbb0fae06578 100644 --- a/code/modules/projectiles/projectile/blob.dm +++ b/code/modules/projectiles/projectile/subtypes/blob.dm @@ -25,7 +25,11 @@ reagents = null ..() -/obj/projectile/energy/blob/on_impact(var/atom/A) +/obj/projectile/energy/blob/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(splatter) var/turf/location = get_turf(src) var/datum/effect_system/smoke_spread/chem/S = new /datum/effect_system/smoke_spread/chem @@ -34,7 +38,6 @@ playsound(location, 'sound/effects/slime_squish.ogg', 30, 1, -3) spawn(0) S.start() - ..() /obj/projectile/energy/blob/proc/ready_chemicals() if(reagents) diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/subtypes/bullets.dm similarity index 84% rename from code/modules/projectiles/projectile/bullets.dm rename to code/modules/projectiles/projectile/subtypes/bullets.dm index c6d6b9ef8960..5f5b67065805 100644 --- a/code/modules/projectiles/projectile/bullets.dm +++ b/code/modules/projectiles/projectile/subtypes/bullets.dm @@ -8,7 +8,7 @@ damage_flag = ARMOR_BULLET embed_chance = 20 //Modified in the actual embed process, but this should keep embed chance about the same sharp = 1 - var/mob_passthrough_check = 0 + projectile_type = PROJECTILE_TYPE_KINETIC muzzle_type = /obj/effect/projectile/muzzle/bullet miss_sounds = list('sound/weapons/guns/miss1.ogg','sound/weapons/guns/miss2.ogg','sound/weapons/guns/miss3.ogg','sound/weapons/guns/miss4.ogg') @@ -16,37 +16,16 @@ 'sound/weapons/guns/ricochet3.ogg', 'sound/weapons/guns/ricochet4.ogg') impact_sounds = list(BULLET_IMPACT_MEAT = SOUNDS_BULLET_MEAT, BULLET_IMPACT_METAL = SOUNDS_BULLET_METAL) -/obj/projectile/bullet/on_hit(var/atom/target, var/blocked = 0) - if (..(target, blocked)) - var/mob/living/L = target - shake_camera(L, 3, 2) - -/obj/projectile/bullet/projectile_attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier) - if(penetrating > 0 && damage > 20 && prob(damage)) - mob_passthrough_check = 1 - else - mob_passthrough_check = 0 - return ..() - -/obj/projectile/bullet/can_embed() - //prevent embedding if the projectile is passing through the mob - if(mob_passthrough_check) - return 0 - return ..() - -/obj/projectile/bullet/check_penetrate(var/atom/A) - if(!A || !A.density) return 1 //if whatever it was got destroyed when we hit it, then I guess we can just keep going - - if(istype(A, /obj/vehicle/sealed/mecha)) - return 1 //mecha have their own penetration handling - - if(ismob(A)) - if(!mob_passthrough_check) - return 0 - if(iscarbon(A)) - damage *= 0.7 //squishy mobs absorb KE - return 1 +/obj/projectile/bullet/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + var/mob/living/L = target + if(!istype(L)) + return + shake_camera(L, 3, 2) +/obj/projectile/bullet/process_legacy_penetration(atom/A) var/chance = damage if(istype(A, /turf/simulated/wall)) var/turf/simulated/wall/W = A @@ -57,14 +36,12 @@ if(D.glass) chance *= 2 else if(istype(A, /obj/structure/girder)) chance = 100 + else if(ismob(A)) + chance = damage >= 20 && prob(damage) - if(prob(chance)) - if(A.opacity) - //display a message so that people on the other side aren't so confused - A.visible_message("\The [src] pierces through \the [A]!") - return 1 - - return 0 + . = prob(chance) + if(.) + damage *= 0.7 /* short-casing projectiles, like the kind used in pistols or SMGs */ @@ -128,7 +105,7 @@ damage = 10 // high rof kinda fucked up lets be real agony = 10 // brute easily heals, agony not so much armor_penetration = 30 // reduces shield blockchance - accuracy = -20 // he do miss actually + accuracy_overall_modify = 0.8 // heehoo speed = 25 * WORLD_ICON_SIZE /obj/projectile/bullet/pistol/medium/ap/suppressor/turbo // spicy boys @@ -193,20 +170,15 @@ fire_sound = 'sound/weapons/weaponsounds_shotgunshot.ogg' damage = 13 pellets = 6 - range_step = 1 - spread_step = 10 + pellet_loss = 0.66 / WORLD_ICON_SIZE /obj/projectile/bullet/pellet/shotgun_improvised name = "shrapnel" - damage = 1 + damage = 4 pellets = 10 - range_step = 1 - spread_step = 10 /obj/projectile/bullet/pellet/shotgun/flak damage = 2 //The main weapon using these fires four at a time, usually with different destinations. Usually. - range_step = 2 - spread_step = 30 armor_penetration = 10 // This is my boomstick, @@ -218,8 +190,6 @@ SA_vulnerability = MOB_CLASS_DEMONIC | MOB_CLASS_ABERRATION embed_chance = -1 pellets = 6 - range_step = 1 - spread_step = 20 holy = TRUE /obj/projectile/bullet/shotgun/stake @@ -242,27 +212,31 @@ combustion = FALSE -/obj/projectile/bullet/shotgun/ion/on_hit(var/atom/target, var/blocked = 0) - ..() +/obj/projectile/bullet/shotgun/ion/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return empulse(target, 0, 0, 2, 0) //Only affects what it hits - return 1 + . |= PROJECTILE_IMPACT_DELETE //Frag shot /obj/projectile/bullet/shotgun/frag12 name ="frag12 slug" damage = 25 -/obj/projectile/bullet/shotgun/frag12/on_hit(atom/target, blocked = FALSE) - ..() +/obj/projectile/bullet/shotgun/frag12/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return explosion(target, -1, 0, 1) - return 1 + . |= PROJECTILE_IMPACT_DELETE /* "Rifle" rounds */ /obj/projectile/bullet/rifle fire_sound = 'sound/weapons/Gunshot_generic_rifle.ogg' armor_penetration = 15 - penetrating = 1 + legacy_penetrating = 1 /obj/projectile/bullet/rifle/a762 fire_sound = 'sound/weapons/weaponsounds_heavyrifleshot.ogg' @@ -286,7 +260,7 @@ /obj/projectile/bullet/rifle/a762/hp damage = 40 armor_penetration = -50 - penetrating = 0 + legacy_penetrating = 0 /obj/projectile/bullet/rifle/a762/hunter // Optimized for killing simple animals and not people, because Balance(tm) damage = 25 @@ -318,19 +292,19 @@ /obj/projectile/bullet/rifle/a556/hp damage = 35 armor_penetration = -50 - penetrating = 0 + legacy_penetrating = 0 /obj/projectile/bullet/rifle/a556/hunter damage = 15 SA_bonus_damage = 35 // 50 total on animals. SA_vulnerability = MOB_CLASS_ANIMAL -/obj/projectile/bullet/rifle/a12_7mm // 14.5×114mm is bigger than a .50 BMG round. +/obj/projectile/bullet/rifle/a12_7mm fire_sound = 'sound/weapons/Gunshot_cannon.ogg' // This is literally an anti-tank rifle caliber. It better sound like a fucking cannon. damage = 80 stun = 3 weaken = 3 - penetrating = 5 + legacy_penetrating = 5 armor_penetration = 80 hitscan = 1 //so the PTR isn't useless as a sniper weapon @@ -361,10 +335,12 @@ embed_chance = 0 edge = 1 -/obj/projectile/bullet/burstbullet/on_hit(var/atom/target, var/blocked = 0) - if(isturf(target)) - explosion(target, -1, 0, 2) - ..() +/obj/projectile/bullet/burstbullet/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + explosion(target, -1, 0, 2) + . |= PROJECTILE_IMPACT_DELETE /obj/projectile/bullet/burstbullet/service name = "charge bullet" @@ -376,10 +352,13 @@ SA_vulnerability = MOB_CLASS_DEMONIC | MOB_CLASS_ABERRATION holy = TRUE -/obj/projectile/bullet/burstbullet/service/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/bullet/burstbullet/service/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(isturf(target)) explosion(target, 0, 1, 2) - ..() + return . | PROJECTILE_IMPACT_DELETE /* Black Powder */ @@ -399,8 +378,7 @@ /obj/projectile/bullet/pellet/blunderbuss //More Damage at close range greater falloff damage = 10 pellets = 8 - range_step = 0.5 //Very quick falloff - spread_step = 30 + pellet_loss = 1.5 / WORLD_ICON_SIZE /obj/projectile/bullet/pellet/blunderbuss/silver damage = 5 @@ -418,8 +396,6 @@ /obj/projectile/bullet/pellet/heavy_shotgun //I want this to use similar calcuations to blunderbuss shot for falloff. damage = 3 //Fires five pellets at a time. - range_step = 0.75 - spread_step = 30 armor_penetration = 10 /obj/projectile/bullet/pellet/heavy_shotgun/silver @@ -432,32 +408,33 @@ /obj/projectile/bullet/heavy_shotgun/grit name = "custom heavy slug" -/obj/projectile/bullet/heavy_shotgun/grit/on_hit(var/atom/movable/target, var/blocked = 0) +/obj/projectile/bullet/heavy_shotgun/grit/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(isliving(target)) var/mob/living/L = target var/throwdir = get_dir(firer,L) - if(prob(10) && !blocked) + if(prob(10) && (efficiency < 0.95)) L.afflict_stun(20 * 1) L.Confuse(1) L.throw_at_old(get_edge_target_turf(L, throwdir), rand(3,6), 10) - return 1 - /obj/projectile/bullet/pellet/heavy_shotgun/grit name = "heavy buckshot" - range_step = 1 -/obj/projectile/bullet/pellet/heavy_shotgun/grit/on_hit(var/atom/movable/target, var/blocked = 0) +/obj/projectile/bullet/pellet/heavy_shotgun/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(isliving(target)) var/mob/living/L = target var/throwdir = get_dir(firer,L) - if(prob(10) && !blocked) - L.afflict_stun(20 * 1) + if(prob(10) && (efficiency >= 0.9)) + L.afflict_stun(2 SECONDS) L.Confuse(1) L.throw_at_old(get_edge_target_turf(L, throwdir), rand(3,6), 10) - return 1 - /* Incendiary */ /obj/projectile/bullet/incendiary @@ -503,10 +480,14 @@ incendiary = 1 flammability = 4 armor_penetration = 40 - penetrating = 5 + legacy_penetrating = 5 combustion = TRUE -/obj/projectile/bullet/incendiary/caseless/on_hit(var/atom/movable/target, var/blocked = 0) +/obj/projectile/bullet/incendiary/caseless/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + // todo: burn this to the ground if(isliving(target)) var/mob/living/L = target L.adjustFireLoss(10) @@ -519,7 +500,7 @@ damage_type = BRUTE incendiary = 1 flammability = 4 - penetrating = 1 + legacy_penetrating = 1 combustion = TRUE diff --git a/code/modules/projectiles/projectile/bullets_vr.dm b/code/modules/projectiles/projectile/subtypes/bullets_vr.dm similarity index 100% rename from code/modules/projectiles/projectile/bullets_vr.dm rename to code/modules/projectiles/projectile/subtypes/bullets_vr.dm diff --git a/code/modules/projectiles/projectile/change.dm b/code/modules/projectiles/projectile/subtypes/change.dm similarity index 93% rename from code/modules/projectiles/projectile/change.dm rename to code/modules/projectiles/projectile/subtypes/change.dm index a8a60f70ed98..187c92e96e7a 100644 --- a/code/modules/projectiles/projectile/change.dm +++ b/code/modules/projectiles/projectile/subtypes/change.dm @@ -8,8 +8,11 @@ combustion = FALSE -/obj/projectile/change/on_hit(var/atom/change) - wabbajack(change) +/obj/projectile/change/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + wabbajack(target) /obj/projectile/change/proc/wabbajack(var/mob/M) if(istype(M, /mob/living) && M.stat != DEAD) diff --git a/code/modules/projectiles/projectile/energy.dm b/code/modules/projectiles/projectile/subtypes/energy.dm similarity index 93% rename from code/modules/projectiles/projectile/energy.dm rename to code/modules/projectiles/projectile/subtypes/energy.dm index cc3b78328ffc..48717e3024a0 100644 --- a/code/modules/projectiles/projectile/energy.dm +++ b/code/modules/projectiles/projectile/subtypes/energy.dm @@ -4,6 +4,7 @@ damage = 0 damage_type = BURN damage_flag = ARMOR_ENERGY + projectile_type = PROJECTILE_TYPE_ENERGY var/flash_strength = 10 //releases a burst of light on impact or after travelling a distance @@ -17,10 +18,11 @@ var/brightness = 7 var/light_colour = "#ffffff" -/obj/projectile/energy/flash/on_impact(var/atom/A) - var/turf/T = flash_range? src.loc : get_turf(A) - if(!istype(T)) return - +/obj/projectile/energy/flash/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(!isturf(target)) + return + var/turf/T = get_turf(target) //blind adjacent people for (var/mob/living/carbon/M in viewers(T, flash_range)) if(M.eyecheck() < 1) @@ -56,11 +58,9 @@ incendiary = 1 flammability = 2 -/obj/projectile/energy/flash/flare/on_impact(var/atom/A) +/obj/projectile/energy/flash/flare/on_impact(atom/target, impact_flags, def_zone, efficiency) light_colour = pick("#e58775", "#ffffff", "#90ff90", "#a09030") - - ..() //initial flash - + . = ..() //residual illumination new /obj/effect/particle_effect/smoke/illumination(src.loc, rand(190,240) SECONDS, 8, 3, light_colour) //same lighting power as flare @@ -206,9 +206,10 @@ to_chat(M, "Your ears start to ring!") M.update_icons() //Just to apply matrix transform for laying asap -/obj/projectile/energy/plasmastun/on_hit(var/atom/target) - bang(target) +/obj/projectile/energy/plasmastun/on_impact(atom/target, impact_flags, def_zone, efficiency) . = ..() + bang(target) + return . | PROJECTILE_IMPACT_DELETE /obj/projectile/energy/blue_pellet name = "suppressive pellet" diff --git a/code/modules/projectiles/projectile/energy_vr.dm b/code/modules/projectiles/projectile/subtypes/energy_vr.dm similarity index 100% rename from code/modules/projectiles/projectile/energy_vr.dm rename to code/modules/projectiles/projectile/subtypes/energy_vr.dm diff --git a/code/modules/projectiles/projectile/explosive.dm b/code/modules/projectiles/projectile/subtypes/explosive.dm similarity index 66% rename from code/modules/projectiles/projectile/explosive.dm rename to code/modules/projectiles/projectile/subtypes/explosive.dm index 05d9d1786811..8139b108fe42 100644 --- a/code/modules/projectiles/projectile/explosive.dm +++ b/code/modules/projectiles/projectile/subtypes/explosive.dm @@ -8,26 +8,16 @@ damage = 30 //Meaty whack. *Chuckles* movable_flags = MOVABLE_NO_THROW_SPIN | MOVABLE_NO_THROW_DAMAGE_SCALING | MOVABLE_NO_THROW_SPEED_SCALING -/obj/projectile/bullet/srmrocket/on_hit(atom/target, blocked=0) - ..() +/obj/projectile/bullet/srmrocket/on_impact(atom/target, impact_flags, def_zone, efficiency) if(!isliving(target)) //if the target isn't alive, so is a wall or something explosion(target, 0, 1, 2, 4) else explosion(target, 0, 0, 2, 4) - return 1 - + return PROJECTILE_IMPACT_DELETE /obj/projectile/bullet/srmrocket/weak //Used in the jury rigged one. damage = 10 -/obj/projectile/bullet/srmrocket/weak/on_hit(atom/target, blocked=0) - ..() +/obj/projectile/bullet/srmrocket/weak/on_impact(atom/target, impact_flags, def_zone, efficiency) explosion(target, 0, 0, 2, 4)//No need to have a question. - return 1 - -/*Old vars here for reference. - var/devastation = 0 - var/heavy_blast = 1 - var/light_blast = 2 - var/flash_blast = 4 -*/ + return PROJECTILE_IMPACT_DELETE diff --git a/code/modules/projectiles/projectile/force.dm b/code/modules/projectiles/projectile/subtypes/force.dm similarity index 70% rename from code/modules/projectiles/projectile/force.dm rename to code/modules/projectiles/projectile/subtypes/force.dm index a95cefff5633..05e11a848f6f 100644 --- a/code/modules/projectiles/projectile/force.dm +++ b/code/modules/projectiles/projectile/subtypes/force.dm @@ -10,11 +10,12 @@ /obj/projectile/forcebolt/strong name = "force bolt" -/obj/projectile/forcebolt/on_hit(var/atom/movable/target, var/blocked = 0) - if(istype(target)) +/obj/projectile/forcebolt/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(ismovable(target)) + var/atom/movable/movable_target = target var/throwdir = get_dir(firer,target) - target.throw_at_old(get_edge_target_turf(target, throwdir),10,10) - return 1 + movable_target.throw_at_old(get_edge_target_turf(target, throwdir),10,10) /* /obj/projectile/forcebolt/strong/on_hit(var/atom/target, var/blocked = 0) diff --git a/code/modules/projectiles/projectile/hook.dm b/code/modules/projectiles/projectile/subtypes/hook.dm similarity index 95% rename from code/modules/projectiles/projectile/hook.dm rename to code/modules/projectiles/projectile/subtypes/hook.dm index 797063ec8ab8..bcf0837c6888 100644 --- a/code/modules/projectiles/projectile/hook.dm +++ b/code/modules/projectiles/projectile/subtypes/hook.dm @@ -58,12 +58,11 @@ ..() // Does the regular launching stuff. -/obj/projectile/energy/hook/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null) - if(..()) - perform_intent_unique(target) - -/obj/projectile/energy/hook/on_impact(var/atom/A) - perform_intent_unique(get_turf(A)) +/obj/projectile/energy/hook/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + perform_intent_unique(target) /obj/projectile/energy/hook/proc/ranged_disarm(var/mob/living/carbon/human/H) if(istype(H)) @@ -116,8 +115,8 @@ else if(firer) var/obj/T - if(original in target.contents && istype(original, /obj)) - T = original + if(original_target.loc == target && istype(original_target, /obj)) + T = original_target var/list/possible_targets = list() for(var/obj/item/I in target.contents) diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/subtypes/magic.dm similarity index 94% rename from code/modules/projectiles/projectile/magic.dm rename to code/modules/projectiles/projectile/subtypes/magic.dm index 507121f0248b..7a1ae4faa0dd 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/subtypes/magic.dm @@ -36,8 +36,8 @@ /obj/item/ammo_casing/magic/honk projectile_type = /obj/projectile/bullet/honker -/obj/item/ammo_casing/magic/locker - projectile_type = /obj/projectile/magic/locker +// /obj/item/ammo_casing/magic/locker + // projectile_type = /obj/projectile/magic/locker //Spell book ammo casing /obj/item/ammo_casing/magic/book diff --git a/code/modules/projectiles/projectile/magnetic.dm b/code/modules/projectiles/projectile/subtypes/magnetic.dm similarity index 72% rename from code/modules/projectiles/projectile/magnetic.dm rename to code/modules/projectiles/projectile/subtypes/magnetic.dm index 1705a9c1c65b..0133907721bf 100644 --- a/code/modules/projectiles/projectile/magnetic.dm +++ b/code/modules/projectiles/projectile/subtypes/magnetic.dm @@ -6,7 +6,7 @@ damage = 65 stun = 1 weaken = 1 - penetrating = 5 + legacy_penetrating = 5 armor_penetration = 70 /obj/projectile/bullet/magnetic/slug @@ -53,7 +53,7 @@ agony = 50 incendiary = 1 flammability = 0 //Deuterium and Tritium are both held in water, but the object moving so quickly will ignite the target. - penetrating = 2 + legacy_penetrating = 2 embed_chance = 0 armor_penetration = 40 range = WORLD_ICON_SIZE * 20 @@ -63,7 +63,13 @@ var/detonate_mob = 0 //Will this fuelrod explode when it hits a mob? var/energetic_impact = 0 //Does this fuelrod cause a bright flash on impact with a mob? -/obj/projectile/bullet/magnetic/fuelrod/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null) //Future-proofing. Special effects for impact. +/obj/projectile/bullet/magnetic/fuelrod/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(searing) + efficiency = max(efficiency, 1) + if(. & PROJECTILE_IMPACT_BLOCKED) + return + if(istype(target,/mob/living)) var/mob/living/V = target if(detonate_mob) @@ -81,28 +87,21 @@ M.afflict_stun(20 * 2) M.afflict_paralyze(20 * 10) - if(searing) - if(blocked) - blocked = 0 - - return ..(target, blocked, def_zone) - -/obj/projectile/bullet/magnetic/fuelrod/on_impact(var/atom/A) //Future-proofing, again. In the event new fuel rods are introduced, and have special effects for when they stop flying. + // what the fuck?? if(src.loc) if(detonate_travel && detonate_mob) visible_message("\The [src] shatters in a violent explosion!") - explosion(src.loc, 1, 1, 3, 4) + explosion(src.loc, 0, 1, 3, 4) else if(detonate_travel) visible_message("\The [src] explodes in a shower of embers!") - explosion(src.loc, -1, 1, 2, 3) - ..(A) + explosion(src.loc, 0, 1, 2, 3) /obj/projectile/bullet/magnetic/fuelrod/tritium icon_state = "fuel-tritium" damage = 100 //Much harder to get than tritium - needs mhydrogen flammability = -1 armor_penetration = 50 - penetrating = 3 + legacy_penetrating = 3 /obj/projectile/bullet/magnetic/fuelrod/phoron name = "blazing fuel rod" @@ -111,7 +110,7 @@ incendiary = 2 flammability = 2 armor_penetration = 60 - penetrating = 5 + legacy_penetrating = 5 irradiate = 20 detonate_mob = 1 @@ -123,7 +122,7 @@ flammability = 4 weaken = 2 armor_penetration = 100 - penetrating = 100 //Theoretically, this shouldn't stop flying for a while, unless someone lines it up with a wall or fires it into a mountain. + legacy_penetrating = 100 //Theoretically, this shouldn't stop flying for a while, unless someone lines it up with a wall or fires it into a mountain. irradiate = 120 range = WORLD_ICON_SIZE * 75 searing = 1 @@ -131,14 +130,16 @@ detonate_mob = 1 energetic_impact = 1 -/obj/projectile/bullet/magnetic/fuelrod/supermatter/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null) //You cannot touch the supermatter without disentigrating. Assumedly, this is true for condensed rods of it flying at relativistic speeds. +/obj/projectile/bullet/magnetic/fuelrod/supermatter/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(istype(target,/turf/simulated/wall) || istype(target,/mob/living)) target.visible_message("The [src] burns a perfect hole through \the [target] with a blinding flash!") playsound(target.loc, 'sound/effects/teleport.ogg', 40, 0) - return ..(target, blocked, def_zone) -/obj/projectile/bullet/magnetic/fuelrod/supermatter/check_penetrate() - return 1 +/obj/projectile/bullet/magnetic/fuelrod/supermatter/pre_impact(atom/target, impact_flags, def_zone) + return ..() | PROJECTILE_IMPACT_PIERCE /obj/projectile/bullet/magnetic/bore name = "phorogenic blast" @@ -146,24 +147,26 @@ damage = 20 incendiary = 1 armor_penetration = 20 - penetrating = 0 + legacy_penetrating = 0 damage_flag = ARMOR_MELEE irradiate = 20 range = WORLD_ICON_SIZE * 6 -/obj/projectile/bullet/magnetic/bore/Bump(atom/A, forced=0) - if(istype(A, /turf/simulated/mineral)) - var/turf/simulated/mineral/MI = A - loc = get_turf(A) // Careful. - permutated.Add(A) +/obj/projectile/bullet/magnetic/bore/pre_impact(atom/target, impact_flags, def_zone) + if(istype(target, /turf/simulated/mineral)) + return PROJECTILE_IMPACT_PIERCE | impact_flags + return ..() + +/obj/projectile/bullet/magnetic/bore/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(istype(target, /turf/simulated/mineral)) + var/turf/simulated/mineral/MI = target MI.GetDrilled(TRUE) - return 0 - else if(istype(A, /turf/simulated/wall) || istype(A, /turf/simulated/shuttle/wall)) // Cause a loud, but relatively minor explosion on the wall it hits. - explosion(A, -1, -1, 1, 3) - qdel(src) - return 1 - else - ..() + else if(istype(target, /turf/simulated/wall) || istype(target, /turf/simulated/shuttle/wall)) + explosion(target, 0, 0, 1, 3) + . |= PROJECTILE_IMPACT_DELETE /obj/projectile/bullet/magnetic/bore/powerful name = "energetic phorogenic blast" @@ -171,7 +174,7 @@ damage = 30 incendiary = 2 armor_penetration = 20 - penetrating = 0 + legacy_penetrating = 0 damage_flag = ARMOR_MELEE irradiate = 20 range = WORLD_ICON_SIZE * 12 diff --git a/code/modules/projectiles/projectile/subtypes/pellet.dm b/code/modules/projectiles/projectile/subtypes/pellet.dm new file mode 100644 index 000000000000..cd9891e7f3cc --- /dev/null +++ b/code/modules/projectiles/projectile/subtypes/pellet.dm @@ -0,0 +1,153 @@ +// For projectiles that actually represent clouds of projectiles +// todo: message handling for this type is all over the place +// todo: target still makes message of being hit even if they weren't +// todo: just handle message in this file lmao +/obj/projectile/bullet/pellet + name = "shrapnel" //'shrapnel' sounds more dangerous (i.e. cooler) than 'pellet' + + damage = 20 + + /// number of pelelts + var/pellets = 4 + /// distance before pellets start falling off + var/pellet_loss_start = WORLD_ICON_SIZE * 2 + /// pellets lost per pixel moved + var/pellet_loss = 0.5 / WORLD_ICON_SIZE + /// last distance travelled + var/pellet_loss_last_distance = 0 + /// base spread chance to not hit center mass / the target limb + /// + /// * in 0 to 100 prob() value + var/pellet_zone_spread = 10 + /// min distance before spread + var/pellet_zone_spread_gain_threshold = 2 * WORLD_ICON_SIZE + /// spread chance per pixel to not hit center mass / target limb + /// + /// * in 0 to 100 prob() value + var/pellet_zone_spread_gain = 9 / WORLD_ICON_SIZE // complete spread after 2+10 tiles + +/obj/projectile/bullet/pellet/scan_moved_turf(turf/tile) + ..() + if(QDELETED(src)) + return + for(var/mob/victim in tile) + if(victim.atom_flags & (ATOM_NONWORLD | ATOM_ABSTRACT)) + continue + if(impacted[victim]) + continue + impact(victim) + +/obj/projectile/bullet/pellet/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change) + . = ..() + var/travelled = distance_travelled - pellet_loss_last_distance + pellet_loss_last_distance = distance_travelled + if(pellet_loss_start > 0) + var/reduction = min(pellet_loss_start, travelled) + travelled -= reduction + pellet_loss_start -= reduction + if(travelled <= 0) + return + pellets -= pellet_loss * travelled + +/obj/projectile/bullet/pellet/process_accuracy(atom/target, target_opinion, distance, impact_check) + if(impact_check) + return 100 + return ..() + +/obj/projectile/bullet/pellet/process_zone_miss(atom/target, target_opinion, distance, impact_check) + if(impact_check) + // if being hit, always direct to our def_zone + return def_zone + return ..() + +// todo: "you are hit by [number of] pellets in the chest" support. +/obj/projectile/bullet/pellet/inflict_impact_damage(atom/target, efficiency, impact_flags, hit_zone) + /** + * What's going on here: + * + * It's too expensive to simulate too many bullet_act()'s for no reason, but pellets + * aren't meant to hit one body part or even do one instance to every body part hit. + * + * Thus, we force process_damage_instance to do your dirty work as it's cheaper to run + * lower level shieldcalls/armorcalls than to simulate high level bullet hits. + */ + var/original_hit_zone = hit_zone + var/distance_penalty = distance_travelled > pellet_zone_spread_gain_threshold ? (distance_travelled * pellet_zone_spread_gain) : 0 + var/zone_true_chance = 100 - (pellet_zone_spread + distance_penalty) + var/pellet_hit_chance = 100 + . = NONE + if(isliving(target)) + var/mob/living/living_target = target + pellet_hit_chance = living_target.process_baymiss(src, impact_check = FALSE) + if(!target.density && (target != original_target)) + pellet_hit_chance *= 0.2 + var/hit_pellets = 0 + for(var/i in 1 to ceil(pellets)) + if(!prob(pellet_hit_chance)) + continue + // args is just a list ref, so we can do this (directly modify args) + hit_zone = ran_zone(def_zone, zone_true_chance) + . |= ..() + ++hit_pellets + pellets -= hit_pellets + hit_zone = original_hit_zone + if(pellets <= 0) + . |= PROJECTILE_IMPACT_DELETE + else + . |= PROJECTILE_IMPACT_PIERCE + +// todo: this is only needed because process_damage_instance isn't used for structured right now! +/obj/projectile/bullet/pellet/get_structure_damage() + return ..() * pellets + +//Explosive grenade projectile, borrowed from fragmentation grenade code. +/obj/projectile/bullet/pellet/fragment + damage = 10 + armor_penetration = 30 + + silenced = 1 //embedding messages are still produced so it's kind of weird when enabled. + muzzle_type = null + +/obj/projectile/bullet/pellet/fragment/strong + damage = 15 + armor_penetration = 20 + +/obj/projectile/bullet/pellet/fragment/weak + damage = 4 + armor_penetration = 40 + +/obj/projectile/bullet/pellet/fragment/rubber + name = "stingball shrapnel" + damage = 3 + agony = 8 + sharp = FALSE + edge = FALSE + damage_flag = ARMOR_MELEE + +/obj/projectile/bullet/pellet/fragment/rubber/strong + damage = 8 + agony = 16 + +// Tank rupture fragments +/obj/projectile/bullet/pellet/fragment/tank + name = "metal fragment" + damage = 9 //Big chunks flying off. + + armor_penetration = 20 + + silenced = 1 + muzzle_type = null + pellets = 3 + +/obj/projectile/bullet/pellet/fragment/tank/small + name = "small metal fragment" + damage = 6 + armor_penetration = 5 + pellets = 5 + +/obj/projectile/bullet/pellet/fragment/tank/big + name = "large metal fragment" + damage = 17 + armor_penetration = 10 + pellet_loss = 0.2 / WORLD_ICON_SIZE + pellets = 1 diff --git a/code/modules/projectiles/projectile/reusable.dm b/code/modules/projectiles/projectile/subtypes/reusable.dm similarity index 73% rename from code/modules/projectiles/projectile/reusable.dm rename to code/modules/projectiles/projectile/subtypes/reusable.dm index a2f5fabbb96d..4c2f5904ff82 100644 --- a/code/modules/projectiles/projectile/reusable.dm +++ b/code/modules/projectiles/projectile/subtypes/reusable.dm @@ -4,54 +4,19 @@ name = "reusable bullet" desc = "How do you even reuse a bullet?" var/ammo_type = /obj/item/ammo_casing/arrow - var/dropped = FALSE //var/fragile = FALSE //var/durable = FALSE //var/shattered = 0 //var/broken_type = null -/obj/projectile/bullet/reusable/on_hit(atom/target, blocked = FALSE) - . = ..() - handle_drop() - //handle_shatter() - -/obj/projectile/bullet/reusable/legacy_on_range() +/obj/projectile/bullet/reusable/expire(impacting) handle_drop() - ..() + return ..() /obj/projectile/bullet/reusable/proc/handle_drop() - if(!dropped) - var/turf/T = get_turf(src) - new ammo_type(T) - dropped = TRUE -/* - else - var/turf/T = get_turf(src) - new broken_type(T) - dropped = TRUE - -/obj/projectile/bullet/reusable/proc/handle_shatter() - if(fragile) - switch(rand(1,100)) - if(1 to 50) - src.shattered = 1 - if(31 to 100) - return - if(durable) - switch(rand(1,100)) - if(1 to 5) - src.shattered = 1 - if(6 to 100) - return - else - switch(rand(1,100)) - if(1 to 25) - src.shattered = 1 - if(16 to 100) - return - return -*/ + var/turf/T = get_turf(src) + new ammo_type(T) //Arrows /obj/projectile/bullet/reusable/arrow @@ -97,14 +62,20 @@ icon_state = "plunger" ammo_type = /obj/item/ammo_casing/arrow/plunger -/obj/projectile/bullet/reusable/plunger/on_hit(atom/hit_atom) +/obj/projectile/bullet/reusable/plunger/on_impact(atom/target, impact_flags, def_zone, efficiency) . = ..() - var/mob/living/carbon/H = hit_atom + // use target abort as this is a target effect. + if(. & PROJECTILE_IMPACT_FLAGS_TARGET_ABORT) + return + var/mob/living/carbon/H = target + if(!istype(H)) + return var/obj/item/plunger/P if(!H.wear_mask) H.equip_to_slot_if_possible(P, SLOT_MASK) else handle_drop() + return . | PROJECTILE_IMPACT_DELETE //Foam Darts /obj/projectile/bullet/reusable/foam diff --git a/code/modules/projectiles/projectile/scatter.dm b/code/modules/projectiles/projectile/subtypes/scatter.dm similarity index 100% rename from code/modules/projectiles/projectile/scatter.dm rename to code/modules/projectiles/projectile/subtypes/scatter.dm diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/subtypes/special.dm similarity index 80% rename from code/modules/projectiles/projectile/special.dm rename to code/modules/projectiles/projectile/subtypes/special.dm index deadb73495d2..1846df5ce77f 100644 --- a/code/modules/projectiles/projectile/special.dm +++ b/code/modules/projectiles/projectile/subtypes/special.dm @@ -17,9 +17,12 @@ var/sev3_range = 1 var/sev4_range = 1 -/obj/projectile/ion/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/ion/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return empulse(target, sev1_range, sev2_range, sev3_range, sev4_range) - return 1 + return . | PROJECTILE_IMPACT_DELETE /obj/projectile/ion/small sev1_range = -1 @@ -41,9 +44,12 @@ sharp = 1 edge = 1 -/obj/projectile/bullet/gyro/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/bullet/gyro/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return explosion(target, -1, 0, 2) - ..() + return . | PROJECTILE_IMPACT_DELETE /obj/projectile/temp name = "freeze beam" @@ -61,8 +67,10 @@ combustion = FALSE -/obj/projectile/temp/on_hit(atom/target, blocked = FALSE) - ..() +/obj/projectile/temp/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(isliving(target)) var/mob/living/L = target @@ -84,8 +92,6 @@ new_temperature = round(new_temperature * temp_factor) L.bodytemperature = new_temperature - return 1 - /obj/projectile/temp/hot name = "heat beam" target_temperature = 1000 @@ -133,7 +139,11 @@ combustion = FALSE -/obj/projectile/energy/floramut/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/energy/floramut/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + var/mob/living/M = target if(ishuman(target)) var/mob/living/carbon/human/H = M @@ -163,8 +173,6 @@ // for (var/mob/V in viewers(src)) // V.show_message("The radiation beam dissipates harmlessly through [M]", 3) M.show_message("The radiation beam dissipates harmlessly through your body.") - else - return 1 /obj/projectile/energy/floramut/gene name = "gamma somatoray" @@ -188,7 +196,10 @@ light_power = 0.5 light_color = "#FFFFFF" -/obj/projectile/energy/florayield/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/energy/florayield/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return var/mob/M = target if(ishuman(target)) //These rays make plantmen fat. var/mob/living/carbon/human/H = M @@ -196,16 +207,15 @@ M.nutrition += 30 else if (istype(target, /mob/living/carbon/)) M.show_message("The radiation beam dissipates harmlessly through your body.") - else - return 1 - /obj/projectile/beam/mindflayer name = "flayer ray" - combustion = FALSE -/obj/projectile/beam/mindflayer/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/beam/mindflayer/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(ishuman(target)) var/mob/living/carbon/human/M = target M.Confuse(rand(5,8)) @@ -229,14 +239,16 @@ combustion = FALSE -/obj/projectile/bola/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/bola/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(ishuman(target)) var/mob/living/carbon/human/M = target var/obj/item/handcuffs/legcuffs/bola/B = new(src.loc) if(!B.place_legcuffs(M,firer)) - if(B) - qdel(B) - ..() + qdel(B) /obj/projectile/webball name = "ball of web" @@ -248,13 +260,16 @@ combustion = FALSE -/obj/projectile/webball/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/webball/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(isturf(target.loc)) var/obj/effect/spider/stickyweb/W = locate() in get_turf(target) if(!W && prob(75)) visible_message("\The [src] splatters a layer of web on \the [target]!") new /obj/effect/spider/stickyweb(target.loc) - ..() /obj/projectile/beam/tungsten name = "core of molten tungsten" @@ -272,7 +287,10 @@ tracer_type = /obj/effect/projectile/tungsten/tracer impact_type = /obj/effect/projectile/tungsten/impact -/obj/projectile/beam/tungsten/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/beam/tungsten/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_BLOCKED) + return if(isliving(target)) var/mob/living/L = target L.add_modifier(/datum/modifier/grievous_wounds, 30 SECONDS) @@ -316,17 +334,13 @@ else if(armor_special) target.visible_message("\The [src] slams into \the [target]'s [target_limb] with a low rumble!") - ..() - -/obj/projectile/beam/tungsten/on_impact(var/atom/A) - if(istype(A,/turf/simulated/shuttle/wall) || istype(A,/turf/simulated/wall) || (istype(A,/turf/simulated/mineral) && A.density) || istype(A,/obj/vehicle/sealed/mecha) || istype(A,/obj/machinery/door)) + if(istype(target,/turf/simulated/shuttle/wall) || istype(target,/turf/simulated/wall) || (istype(target,/turf/simulated/mineral) && target.density) || istype(target,/obj/vehicle/sealed/mecha) || istype(target,/obj/machinery/door)) var/blast_dir = src.dir - A.visible_message("\The [A] begins to glow!") + target.visible_message("\The [target] begins to glow!") spawn(2 SECONDS) - var/blastloc = get_step(A, blast_dir) + var/blastloc = get_step(target, blast_dir) if(blastloc) explosion(blastloc, -1, -1, 2, 3) - ..() /obj/projectile/bullet/honker damage = 0 @@ -384,19 +398,18 @@ light_range = 4 light_power = 3 light_color = "#00ccff" + var/heavy = FALSE -/obj/projectile/plasma/on_hit(var/atom/target, var/blocked = 0) - explosion(target, -1, 0, 1, 2) - ..() +/obj/projectile/plasma/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return -/obj/projectile/plasma/on_impact(var/atom/A) - if(istype(A,/turf/simulated/shuttle/wall) || istype(A,/turf/simulated/wall) || (istype(A,/turf/simulated/mineral) && A.density) || istype(A,/obj/vehicle/sealed/mecha) || istype(A,/obj/machinery/door)) - var/blast_dir = src.dir - A.visible_message("\The [A] is engulfed in roiling plasma!") - var/blastloc = get_step(A, blast_dir) - if(blastloc) - explosion(blastloc, -1, 0, 1, 2) - ..() + var/blast_dir = src.dir + target.visible_message("\The [target] is engulfed in roiling plasma!") + var/blastloc = get_step(target, blast_dir) + if(blastloc) + explosion(blastloc, -1, 0, heavy? 2 : 1, heavy? 3 : 2) /obj/projectile/plasma/hot name ="heavy plasma bolt" @@ -404,16 +417,4 @@ light_range = 5 light_power = 4 light_color = "#00ccff" - -/obj/projectile/plasma/hot/on_hit(var/atom/target, var/blocked = 0) - explosion(target, -1, 0, 2, 3) - ..() - -/obj/projectile/plasma/hot/on_impact(var/atom/A) - if(istype(A,/turf/simulated/shuttle/wall) || istype(A,/turf/simulated/wall) || (istype(A,/turf/simulated/mineral) && A.density) || istype(A,/obj/vehicle/sealed/mecha) || istype(A,/obj/machinery/door)) - var/blast_dir = src.dir - A.visible_message("\The [A] is engulfed in roiling plasma!") - var/blastloc = get_step(A, blast_dir) - if(blastloc) - explosion(blastloc, -1, 0, 2, 3) - ..() + heavy = TRUE diff --git a/code/modules/projectiles/projectile/trace.dm b/code/modules/projectiles/projectile/subtypes/trace.dm similarity index 94% rename from code/modules/projectiles/projectile/trace.dm rename to code/modules/projectiles/projectile/subtypes/trace.dm index a01e1574e40b..9c825bc3f757 100644 --- a/code/modules/projectiles/projectile/trace.dm +++ b/code/modules/projectiles/projectile/subtypes/trace.dm @@ -20,6 +20,7 @@ nodamage = TRUE damage = 0 has_tracer = FALSE + projectile_type = PROJECTILE_TYPE_TRACE var/list/hit = list() /obj/projectile/test/fire(angle, atom/direct_target) @@ -30,6 +31,3 @@ if(A != src) hit |= A return ..() - -/obj/projectile/test/projectile_attack_mob() - return diff --git a/code/modules/projectiles/unsorted/magic.dm b/code/modules/projectiles/unsorted/magic.dm index aa8735a1f020..690cd6a04e24 100644 --- a/code/modules/projectiles/unsorted/magic.dm +++ b/code/modules/projectiles/unsorted/magic.dm @@ -12,14 +12,17 @@ name = "bolt of death" icon_state = "pulse1_bl" -/obj/projectile/magic/death/on_hit(target, var/mob/living/L) +/obj/projectile/magic/death/on_impact(atom/target, impact_flags, def_zone, efficiency) . = ..() - if(ismob(target)) - var/mob/M = target + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(isliving(target)) + var/mob/living/L = target if(L.anti_magic_check()) - M.visible_message("[src] vanishes on contact with [target]!") - return blocked - M.death(0) + L.visible_message("[src] vanishes on contact with [target]!") + . |= PROJECTILE_IMPACT_DELETE + return + L.death(0) /obj/projectile/magic/resurrection name = "bolt of resurrection" @@ -28,17 +31,21 @@ damage_type = OXY nodamage = 1 -/obj/projectile/magic/resurrection/on_hit(mob/living/carbon/target) +/obj/projectile/magic/resurrection/on_impact(atom/target, impact_flags, def_zone, efficiency) . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(isliving(target)) - if(target.anti_magic_check()) - target.visible_message("[src] vanishes on contact with [target]!") - return blocked - if(target.revive(full_heal = TRUE)) - to_chat(target, "You rise with a start, you're alive!!!") - else if(target.stat != DEAD) - to_chat(target, "You feel great!") - target.rejuvenate(fix_missing = TRUE) + var/mob/living/L = target + if(L.anti_magic_check()) + L.visible_message("[src] vanishes on contact with [L]!") + . |= PROJECTILE_IMPACT_DELETE + return + if(L.revive(full_heal = TRUE)) + to_chat(L, "You rise with a start, you're alive!!!") + else if(L.stat != DEAD) + to_chat(L, "You feel great!") + L.rejuvenate(fix_missing = TRUE) /obj/projectile/magic/teleport name = "bolt of teleportation" @@ -49,13 +56,16 @@ var/inner_tele_radius = 0 var/outer_tele_radius = 6 -/obj/projectile/magic/teleport/on_hit(mob/target, var/mob/living/L) +/obj/projectile/magic/teleport/on_impact(atom/target, impact_flags, def_zone, efficiency) . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(ismob(target)) - var/mob/M = target + var/mob/L = target if(L.anti_magic_check()) - M.visible_message("[src] fizzles on contact with [target]!") - return blocked + L.visible_message("[src] fizzles on contact with [target]!") + . |= PROJECTILE_IMPACT_DELETE + return var/teleammount = 0 var/teleloc = target if(!isturf(target)) @@ -76,8 +86,10 @@ nodamage = 1 var/list/door_types = list(/obj/structure/simple_door/wood, /obj/structure/simple_door/iron, /obj/structure/simple_door/silver, /obj/structure/simple_door/gold, /obj/structure/simple_door/uranium, /obj/structure/simple_door/sandstone, /obj/structure/simple_door/phoron, /obj/structure/simple_door/diamond) -/obj/projectile/magic/door/on_hit(atom/target) +/obj/projectile/magic/door/on_impact(atom/target, impact_flags, def_zone, efficiency) . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(istype(target, /obj/machinery/door)) OpenDoor(target) else @@ -291,6 +303,7 @@ if(owner) C.ChangeOwner(owner) */ + /obj/projectile/magic/spellblade name = "blade energy" icon_state = "lavastaff" @@ -299,14 +312,17 @@ sharp = TRUE magic = TRUE -/obj/projectile/magic/spellblade/on_hit(target, var/mob/living/L) +/obj/projectile/magic/spellblade/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(ismob(target)) - var/mob/M = target + var/mob/L = target if(L.anti_magic_check()) - M.visible_message("[src] vanishes on contact with [target]!") + L.visible_message("[src] vanishes on contact with [target]!") qdel(src) return - . = ..() /obj/projectile/magic/arcane_barrage name = "arcane bolt" @@ -318,16 +334,19 @@ magic = TRUE impact_sounds = 'sound/weapons/barragespellhit.ogg' -/obj/projectile/magic/arcane_barrage/on_hit(target, var/mob/living/L) +/obj/projectile/magic/arcane_barrage/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(ismob(target)) - var/mob/M = target + var/mob/L = target if(L.anti_magic_check()) - M.visible_message("[src] vanishes on contact with [target]!") + L.visible_message("[src] vanishes on contact with [target]!") qdel(src) return - . = ..() - +/* needs more work /obj/projectile/magic/locker name = "locker bolt" icon_state = "locker" @@ -352,21 +371,24 @@ return ..() */ -/obj/projectile/magic/locker/on_hit(target) - if(created) - return ..() - var/obj/structure/closet/decay/C = new(get_turf(src)) - if(LAZYLEN(contents)) - for(var/atom/movable/AM in contents) +/obj/projectile/magic/locker/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(!created) + var/obj/structure/closet/decay/C = new(src) C.update_icon() - created = TRUE - return ..() + if(locker_suck && !(target.atom_flags & (ATOM_ABSTRACT | ATOM_NONWORLD))) + if(ismovable(target)) + var/atom/movable/AM = target + AM.forceMove(src) /obj/projectile/magic/locker/Destroy() locker_suck = FALSE for(var/atom/movable/AM in contents) AM.forceMove(get_turf(src)) . = ..() +*/ /obj/structure/closet/decay breakout_time = 600 @@ -428,16 +450,17 @@ chain = caster.Beam(src, icon_state = "lightning[rand(1, 12)]", time = INFINITY, maxdistance = INFINITY) ..() -/obj/projectile/magic/aoe/lightning/on_hit(target, var/mob/living/L) +/obj/projectile/magic/aoe/lightning/on_impact(atom/target, impact_flags, def_zone, efficiency) . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(ismob(target)) - var/mob/M = target + var/mob/L = target if(L.anti_magic_check()) - M.visible_message("[src] fizzles on contact with [target]!") - qdel(src) - return blocked + L.visible_message("[src] fizzles on contact with [target]!") + return . | PROJECTILE_IMPACT_DELETE tesla_zap(src, zap_range, zap_power) - qdel(src) + return . | PROJECTILE_IMPACT_DELETE /obj/projectile/magic/aoe/lightning/Destroy() qdel(chain) @@ -456,17 +479,21 @@ var/exp_flash = 3 var/exp_fire = 2 -/obj/projectile/magic/aoe/fireball/on_hit(target) +/obj/projectile/magic/aoe/fireball/on_impact(atom/target, impact_flags, def_zone, efficiency) . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(ismob(target)) var/mob/living/M = target if(M.anti_magic_check()) visible_message("[src] vanishes into smoke on contact with [target]!") - return + return . | PROJECTILE_IMPACT_DELETE M.take_overall_damage(0,10) //between this 10 burn, the 10 brute, the explosion brute, and the onfire burn, your at about 65 damage if you stop drop and roll immediately var/turf/T = get_turf(target) explosion(T, -1, exp_heavy, exp_light, exp_flash, 0)//, flame_range = exp_fire) + return . | PROJECTILE_IMPACT_DELETE +/* requires IMPACT_DELETE_AFTER /obj/projectile/magic/aoe/fireball/infernal name = "infernal fireball" exp_heavy = -1 @@ -474,15 +501,14 @@ exp_flash = 4 exp_fire= 5 -/obj/projectile/magic/aoe/fireball/infernal/on_hit(target) +/obj/projectile/magic/aoe/fireball/infernal/on_impact(atom/target, impact_flags, def_zone, efficiency) . = ..() - if(ismob(target)) - var/mob/living/M = target - if(M.anti_magic_check()) - return + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return var/turf/T = get_turf(target) for(var/i=0, i<50, i+=10) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(explosion), T, -1, exp_heavy, exp_light, exp_flash, FALSE, FALSE, exp_fire), i) +*/ /obj/projectile/magic/nuclear name = "\proper blazing manliness" @@ -491,7 +517,10 @@ var/mob/living/victim = null var/used = 0 -/obj/projectile/magic/nuclear/on_hit(target) +/obj/projectile/magic/nuclear/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(used) return if(ismob(target)) @@ -500,16 +529,12 @@ used = 1 visible_message("[victim] slams into [target] with explosive force!") explosion(src, 2, 3, 4, -1, TRUE, FALSE, 5) + return PROJECTILE_IMPACT_DELETE else used = 1 victim.take_overall_damage(30,30) explosion(src, -1, -1, -1, -1, FALSE, FALSE, 5) - return - -/obj/projectile/magic/nuclear/Destroy() - for(var/atom/movable/AM in contents) - AM.forceMove(get_turf(src)) - . = ..() + return PROJECTILE_IMPACT_DELETE //Spellcards @@ -533,8 +558,10 @@ damage = 4 var/fire_stacks = 4 -/obj/projectile/magic/spellcard/book/spark/on_hit(atom/target, blocked = FALSE) +/obj/projectile/magic/spellcard/book/spark/on_impact(atom/target, impact_flags, def_zone, efficiency) . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return var/mob/living/carbon/M = target if(ismob(target)) if(M.anti_magic_check()) diff --git a/code/modules/random_map/drop/drop_types.dm b/code/modules/random_map/drop/drop_types.dm index 9bd0d665fd50..c39f4ae18cc5 100644 --- a/code/modules/random_map/drop/drop_types.dm +++ b/code/modules/random_map/drop/drop_types.dm @@ -37,7 +37,7 @@ var/global/list/datum/supply_drop_loot/supply_drop /obj/item/storage/belt/security/tactical/bandolier, /obj/item/clothing/accessory/storage/black_drop_pouches, /obj/item/storage/backpack/dufflebag/sec, - /obj/item/shield/energy, + /obj/item/shield/transforming/energy, /obj/item/gun/energy/ionrifle, /obj/item/gun/energy/xray, /obj/item/storage/box/emps, @@ -58,7 +58,7 @@ var/global/list/datum/supply_drop_loot/supply_drop /obj/item/storage/belt/security/tactical/bandolier, /obj/item/clothing/accessory/storage/black_drop_pouches, /obj/item/storage/backpack/dufflebag/sec, - /obj/item/shield/riot/tele, + /obj/item/shield/transforming/telescopic, /obj/item/storage/box/emps, /obj/item/storage/box/flashbangs, /obj/item/gun/ballistic/automatic/sts35, @@ -85,7 +85,7 @@ var/global/list/datum/supply_drop_loot/supply_drop /obj/item/gun/ballistic/automatic/bullpup, /obj/item/ammo_magazine/a7_62mm/ap, /obj/item/ammo_magazine/a7_62mm, - /obj/item/shield/energy, + /obj/item/shield/transforming/energy, /obj/item/grenade/explosive/frag, /obj/item/grenade/explosive/frag, /obj/item/grenade/smokebomb, @@ -107,7 +107,7 @@ var/global/list/datum/supply_drop_loot/supply_drop /obj/item/clothing/suit/armor/riot, /obj/item/clothing/gloves/arm_guard/riot, /obj/item/clothing/shoes/leg_guard/riot, - /obj/item/shield/riot/tele, + /obj/item/shield/transforming/telescopic, /obj/item/storage/box/flashbangs, /obj/item/storage/box/handcuffs, /obj/item/melee/baton, diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm index 9855c529c98f..70a9ac9731a8 100644 --- a/code/modules/reagents/chemistry/holder.dm +++ b/code/modules/reagents/chemistry/holder.dm @@ -402,7 +402,7 @@ var/mob/living/L = target if(ishuman(L)) var/mob/living/carbon/human/H = L - if(H.check_shields(0, null, null, null, "the spray") == 1) //If they block the spray, it does nothing. + if(H.atom_shieldcall_handle_touch(null, SHIELDCALL_CONTACT_FLAG_NEUTRAL, SHIELDCALL_CONTACT_SPECIFIC_CHEMICAL_SPRAY) & SHIELDCALL_FLAGS_BLOCK_ATTACK) amount = 0 perm = L.reagent_permeability() return trans_to_mob(target, amount, CHEM_TOUCH, perm, copy) diff --git a/code/modules/reagents/machinery/reagent_dispenser/fuel.dm b/code/modules/reagents/machinery/reagent_dispenser/fuel.dm index f02c4724a39c..e7e9faab4139 100644 --- a/code/modules/reagents/machinery/reagent_dispenser/fuel.dm +++ b/code/modules/reagents/machinery/reagent_dispenser/fuel.dm @@ -84,13 +84,14 @@ return ..() -/obj/structure/reagent_dispensers/fueltank/bullet_act(var/obj/projectile/Proj) - if(Proj.get_structure_damage()) - if(istype(Proj.firer)) - message_admins("[key_name_admin(Proj.firer)] shot fueltank at [loc.loc.name] ([loc.x],[loc.y],[loc.z]) (JMP).") - log_game("[key_name(Proj.firer)] shot fueltank at [loc.loc.name] ([loc.x],[loc.y],[loc.z]).") +/obj/structure/reagent_dispensers/fueltank/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + if(proj.get_structure_damage()) + if(istype(proj.firer)) + message_admins("[key_name_admin(proj.firer)] shot fueltank at [loc.loc.name] ([loc.x],[loc.y],[loc.z]) (JMP).") + log_game("[key_name(proj.firer)] shot fueltank at [loc.loc.name] ([loc.x],[loc.y],[loc.z]).") - if(!istype(Proj ,/obj/projectile/beam/lasertag) && !istype(Proj ,/obj/projectile/beam/practice) ) + if(!istype(proj ,/obj/projectile/beam/lasertag) && !istype(proj ,/obj/projectile/beam/practice) ) explode() /obj/structure/reagent_dispensers/fueltank/legacy_ex_act() diff --git a/code/modules/reagents/machinery/reagent_dispenser/oil.dm b/code/modules/reagents/machinery/reagent_dispenser/oil.dm index 0ee657bc919d..e28b3074f4e1 100644 --- a/code/modules/reagents/machinery/reagent_dispenser/oil.dm +++ b/code/modules/reagents/machinery/reagent_dispenser/oil.dm @@ -21,8 +21,9 @@ ) starting_capacity = 5000 -/obj/structure/reagent_dispensers/cookingoil/bullet_act(var/obj/projectile/Proj) - if(Proj.get_structure_damage()) +/obj/structure/reagent_dispensers/cookingoil/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + if(proj.get_structure_damage()) explode() /obj/structure/reagent_dispensers/cookingoil/legacy_ex_act() diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index 575fcd642760..586eea6f7c45 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -246,8 +246,10 @@ var/hit_area = affecting.name - if((user != target) && H.check_shields(7, src, user, "\the [src]")) - return + if(user != target) + var/list/shieldcall_results = target.run_mob_defense(7, attack_type = ATTACK_TYPE_MELEE, weapon = src, hit_zone = hit_area, clickchain = new /datum/event_args/actor/clickchain(user)) + if(shieldcall_results[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAG_ATTACK_BLOCKED) + return if (target != user && H.legacy_mob_armor(target_zone, "melee") > 5 && prob(50)) for(var/mob/O in viewers(world.view, user)) diff --git a/code/modules/research/designs/weapons.dm b/code/modules/research/designs/weapons.dm index 350c6ea1ae39..e966586ce3a4 100644 --- a/code/modules/research/designs/weapons.dm +++ b/code/modules/research/designs/weapons.dm @@ -331,14 +331,14 @@ id = "chargesword" req_tech = list(TECH_COMBAT = 6, TECH_MAGNET = 4, TECH_ENGINEERING = 5, TECH_ILLEGAL = 4, TECH_ARCANE = 1) materials_base = list(MAT_PLASTEEL = 3500, MAT_GLASS = 1000, MAT_LEAD = 2250, MAT_METALHYDROGEN = 500) - build_path = /obj/item/melee/energy/sword/charge + build_path = /obj/item/melee/transforming/energy/sword/charge /datum/design/science/weapon/melee/eaxe design_name = "Energy Axe" id = "chargeaxe" req_tech = list(TECH_COMBAT = 6, TECH_MAGNET = 5, TECH_ENGINEERING = 4, TECH_ILLEGAL = 4) materials_base = list(MAT_PLASTEEL = 3500, MAT_OSMIUM = 2000, MAT_LEAD = 2000, MAT_METALHYDROGEN = 500) - build_path = /obj/item/melee/energy/axe/charge + build_path = /obj/item/melee/transforming/energy/axe/charge /datum/design/science/weapon/grenade abstract_type = /datum/design/science/weapon/grenade diff --git a/code/modules/shieldgen/energy_field.dm b/code/modules/shieldgen/energy_field.dm index 8d195c0f97f2..666014f6393f 100644 --- a/code/modules/shieldgen/energy_field.dm +++ b/code/modules/shieldgen/energy_field.dm @@ -58,7 +58,7 @@ user.do_attack_animation(src) user.setClickCooldown(user.get_attack_speed()) -/obj/effect/energy_field/inflict_atom_damage(damage, tier, flag, mode, attack_type, datum/weapon, gradual) +/obj/effect/energy_field/inflict_atom_damage(damage, damage_type, damage_tier, damage_flag, damage_mode, hit_zone, attack_type, datum/weapon) adjust_strength(damage / 20) return damage diff --git a/code/modules/shieldgen/energy_shield.dm b/code/modules/shieldgen/energy_shield.dm index 4f3d459109f1..434a484ad33d 100644 --- a/code/modules/shieldgen/energy_shield.dm +++ b/code/modules/shieldgen/energy_shield.dm @@ -231,15 +231,15 @@ if(!disabled_for) take_damage_legacy(rand(10,15) / severity, SHIELD_DAMTYPE_PHYSICAL) - // Fire /obj/effect/shield/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) if(!disabled_for) take_damage_legacy(rand(5,10), SHIELD_DAMTYPE_HEAT) - // Projectiles -/obj/effect/shield/bullet_act(var/obj/projectile/proj) +/obj/effect/shield/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + impact_flags &= ~PROJECTILE_IMPACT_FLAGS_SHOULD_NOT_HIT + . = ..() if(proj.damage_type == BURN) take_damage_legacy(proj.get_structure_damage(), SHIELD_DAMTYPE_HEAT) else if (proj.damage_type == BRUTE) @@ -247,7 +247,6 @@ else //TODO - This will never happen because of get_structure_damage() only returning values for BRUTE and BURN damage types take_damage_legacy(proj.get_structure_damage(), SHIELD_DAMTYPE_EM) - // Attacks with hand tools. Blocked by Hyperkinetic flag. /obj/effect/shield/attackby(var/obj/item/I as obj, var/mob/user as mob) user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) diff --git a/code/modules/shieldgen/sheldwallgen.dm b/code/modules/shieldgen/sheldwallgen.dm index d5cbc172a198..7220b26436b0 100644 --- a/code/modules/shieldgen/sheldwallgen.dm +++ b/code/modules/shieldgen/sheldwallgen.dm @@ -213,11 +213,9 @@ src.cleanup(8) ..() -/obj/machinery/shieldwallgen/bullet_act(var/obj/projectile/Proj) - storedpower -= 400 * Proj.get_structure_damage() - ..() - return - +/obj/machinery/shieldwallgen/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + storedpower -= 400 * proj.get_structure_damage() //////////////Containment Field START /obj/machinery/shieldwall @@ -277,18 +275,15 @@ else gen_secondary.storedpower -= power_usage - -/obj/machinery/shieldwall/bullet_act(var/obj/projectile/Proj) +/obj/machinery/shieldwall/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() if(needs_power) var/obj/machinery/shieldwallgen/G if(prob(50)) G = gen_primary else G = gen_secondary - G.storedpower -= 400 * Proj.get_structure_damage() - ..() - return - + G.storedpower -= 400 * proj.get_structure_damage() /obj/machinery/shieldwall/legacy_ex_act(severity) if(needs_power) diff --git a/code/modules/shuttles/shuttle_console.dm b/code/modules/shuttles/shuttle_console.dm index 9459cc25fba8..9a639edbff8b 100644 --- a/code/modules/shuttles/shuttle_console.dm +++ b/code/modules/shuttles/shuttle_console.dm @@ -136,16 +136,6 @@ to_chat(user, "You short out the console's ID checking system. It's now available to everyone!") return 1 -/obj/machinery/computer/shuttle_control/bullet_act(var/obj/projectile/Proj) - visible_message("\The [Proj] ricochets off \the [src]!") - -/obj/machinery/computer/shuttle_control/legacy_ex_act() - return - -/obj/machinery/computer/shuttle_control/emp_act() - return - - GLOBAL_LIST_BOILERPLATE(papers_dockingcode, /obj/item/paper/dockingcodes) /hook/roundstart/proc/populate_dockingcodes() for(var/paper in GLOB.papers_dockingcode) diff --git a/code/modules/species/promethean/promethean_blob.dm b/code/modules/species/promethean/promethean_blob.dm index 510e07807926..835fe946bced 100644 --- a/code/modules/species/promethean/promethean_blob.dm +++ b/code/modules/species/promethean/promethean_blob.dm @@ -195,11 +195,10 @@ set_light(max(1,min(5,rad_glow/15)), max(1,min(10,rad_glow/25)), color) update_icon() -/mob/living/simple_mob/slime/promethean/bullet_act(obj/projectile/P) +/mob/living/simple_mob/slime/promethean/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(humanform) - return humanform.bullet_act(P) - else - return ..() + return proj.impact_redirect(humanform, args) + return ..() /mob/living/simple_mob/slime/promethean/death(gibbed, deathmessage = "rapidly loses cohesion, splattering across the ground...") if(humanform) diff --git a/code/modules/species/xenomorphs/alien_facehugger.dm b/code/modules/species/xenomorphs/alien_facehugger.dm index 6d3c82a512c3..159d148bed6f 100644 --- a/code/modules/species/xenomorphs/alien_facehugger.dm +++ b/code/modules/species/xenomorphs/alien_facehugger.dm @@ -66,9 +66,9 @@ var/const/MAX_ACTIVE_TIME = 400 Die() return -/obj/item/clothing/mask/facehugger/bullet_act() +/obj/item/clothing/mask/facehugger/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() Die() - return /obj/item/clothing/mask/facehugger/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) if(exposed_temperature > T0C+80) @@ -351,7 +351,7 @@ var/const/MAX_ACTIVE_TIME = 400 Die() return -/mob/living/simple_mob/animal/space/alien/facehugger/bullet_act() +/mob/living/simple_mob/animal/space/alien/facehugger/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) Die() return diff --git a/code/modules/spells/spell_projectile.dm b/code/modules/spells/spell_projectile.dm index 60538b4ba7b1..8b3e0be5ea11 100644 --- a/code/modules/spells/spell_projectile.dm +++ b/code/modules/spells/spell_projectile.dm @@ -6,7 +6,6 @@ var/spell/targeted/projectile/carried - penetrating = 0 range = WORLD_ICON_SIZE * 10 //set by the duration of the spell var/proj_trail = 0 //if it leaves a trail @@ -47,10 +46,12 @@ prox_cast(carried.choose_prox_targets(user = carried.holder, spell_holder = src)) return 1 -/obj/projectile/spell_projectile/on_impact() +/obj/projectile/spell_projectile/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(loc && carried) prox_cast(carried.choose_prox_targets(user = carried.holder, spell_holder = src)) - return 1 /obj/projectile/spell_projectile/seeking name = "seeking spell" diff --git a/code/modules/spells/targeted/ethereal_jaunt.dm b/code/modules/spells/targeted/ethereal_jaunt.dm index 078cc2ac2ae4..d1cb091cd77a 100644 --- a/code/modules/spells/targeted/ethereal_jaunt.dm +++ b/code/modules/spells/targeted/ethereal_jaunt.dm @@ -105,5 +105,3 @@ /obj/effect/dummy/spell_jaunt/legacy_ex_act(blah) return -/obj/effect/dummy/spell_jaunt/bullet_act(blah) - return diff --git a/code/modules/surgery/generic.dm b/code/modules/surgery/generic.dm index 55b3c517a8da..4624eae98cc1 100644 --- a/code/modules/surgery/generic.dm +++ b/code/modules/surgery/generic.dm @@ -76,7 +76,7 @@ /obj/item/surgical/scalpel/laser3 = 95, \ /obj/item/surgical/scalpel/laser2 = 85, \ /obj/item/surgical/scalpel/laser1 = 75, \ - /obj/item/melee/energy/sword = 5 + /obj/item/melee/transforming/energy/sword = 5 ) priority = 2 req_open = 0 diff --git a/code/modules/vehicles/sealed.dm b/code/modules/vehicles/sealed.dm index 27b14622f94d..deaf039bbe40 100644 --- a/code/modules/vehicles/sealed.dm +++ b/code/modules/vehicles/sealed.dm @@ -176,7 +176,7 @@ * * silent - suppress user messages * * suppressed - suppress external messages */ -/obj/vehicle/sealed/proc/mob_exit(mob/exiting, datum/event_args/actor/actor, atom/new_loc, silent, suppressed) +/obj/vehicle/sealed/proc/mob_exit(mob/exiting, datum/event_args/actor/actor, atom/new_loc = drop_location(), silent, suppressed) var/old_control_flags = occupants[exiting] remove_occupant(exiting) exiting.forceMove(new_loc) diff --git a/code/modules/vehicles/sealed/mecha/equipment/weapons/energy/pulse.dm b/code/modules/vehicles/sealed/mecha/equipment/weapons/energy/pulse.dm index c575a01e3aa6..b2de5f21e69a 100644 --- a/code/modules/vehicles/sealed/mecha/equipment/weapons/energy/pulse.dm +++ b/code/modules/vehicles/sealed/mecha/equipment/weapons/energy/pulse.dm @@ -11,11 +11,3 @@ /obj/projectile/beam/pulse/heavy name = "heavy pulse laser" icon_state = "pulse1_bl" - var/life = 20 - -/obj/projectile/beam/pulse/heavy/Bump(atom/A) - A.bullet_act(src, def_zone) - src.life -= 10 - if(life <= 0) - qdel(src) - return diff --git a/code/modules/vehicles/sealed/mecha/equipment/weapons/weapons.dm b/code/modules/vehicles/sealed/mecha/equipment/weapons/weapons.dm index 90966f171338..6e9b11c38847 100644 --- a/code/modules/vehicles/sealed/mecha/equipment/weapons/weapons.dm +++ b/code/modules/vehicles/sealed/mecha/equipment/weapons/weapons.dm @@ -83,11 +83,11 @@ if(!istype(P)) return - P.accuracy -= user.get_accuracy_penalty() + P.accuracy_overall_modify *= 1 - (user.get_accuracy_penalty() / 100) // Some modifiers make it harder or easier to hit things. for(var/datum/modifier/M in user.modifiers) if(!isnull(M.accuracy)) - P.accuracy += M.accuracy + P.accuracy_overall_modify *= 1 + (M.accuracy / 100) if(!isnull(M.accuracy_dispersion)) P.dispersion = max(P.dispersion + M.accuracy_dispersion, 0) diff --git a/code/modules/vehicles/sealed/mecha/mecha.dm b/code/modules/vehicles/sealed/mecha/mecha.dm index e06cb7c084a5..f5e86771f096 100644 --- a/code/modules/vehicles/sealed/mecha/mecha.dm +++ b/code/modules/vehicles/sealed/mecha/mecha.dm @@ -304,16 +304,16 @@ src.legacy_eject_occupant() for(var/mob/M in src) //Be Extra Sure M.forceMove(get_turf(src)) - M.loc.Entered(M) if(M != src.occupant_legacy) step_rand(M) + for(var/atom/movable/A in src.cargo) A.forceMove(get_turf(src)) var/turf/T = get_turf(A) if(T) T.Entered(A) step_rand(A) - + cargo = list() if(prob(30)) explosion(get_turf(loc), 0, 0, 1, 3) @@ -918,6 +918,7 @@ /////////////////////////////////// //ATM, the ignore_threshold is literally only used for the pulse rifles beams used mostly by deathsquads. +// todo: this is uh, not a check, this is a **roll**. /obj/vehicle/sealed/mecha/proc/check_for_internal_damage(var/list/possible_int_damage,var/ignore_threshold=null) if(!islist(possible_int_damage) || !length(possible_int_damage)) return if(prob(30)) @@ -960,16 +961,6 @@ occupant_message("Internal fire extinquished.") if(MECHA_INT_TANK_BREACH) occupant_message("Damaged internal tank has been sealed.") - return - - -//////////////////////////////////////// -//////// Health related procs //////// -//////////////////////////////////////// - -/obj/vehicle/sealed/mecha/bullet_act(obj/projectile/Proj) - . = ..() - /obj/vehicle/sealed/mecha/proc/take_damage_legacy(amount, type="brute") update_damage_alerts() @@ -1158,17 +1149,16 @@ return -// todo: MAKE INFLICT_DAMAGE_INSTANCE() A THING ON HIT HANDLING PR!! -/obj/vehicle/sealed/mecha/bullet_act(var/obj/projectile/Proj) //wrapper - if(istype(Proj, /obj/projectile/test)) - var/obj/projectile/test/Test = Proj - Test.hit |= occupant_legacy // Register a hit on the occupant_legacy, for things like turrets, or in simple-mob cases stopping friendly fire in firing line mode. - return +/obj/vehicle/sealed/mecha/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + if(istype(proj, /obj/projectile/test)) + var/obj/projectile/test/Test = proj + Test.hit |= occupant_legacy // Register a hit on the occupant, for things like turrets, or in simple-mob cases stopping friendly fire in firing line mode. + return ..() - src.log_message("Hit by projectile. Type: [Proj.name]([Proj.damage_flag]).",1) - call((proc_res["dynbulletdamage"]||src), "dynbulletdamage")(Proj) //calls equipment - ..() - return + src.log_message("Hit by projectile. Type: [proj.name]([proj.damage_flag]).",1) + impact_flags |= call((proc_res["dynbulletdamage"]||src), "dynbulletdamage")(proj) //calls equipment + impact_flags |= PROJECTILE_IMPACT_SKIP_STANDARD_DAMAGE + return ..() /obj/vehicle/sealed/mecha/proc/dynbulletdamage(var/obj/projectile/Proj) var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] @@ -1210,7 +1200,7 @@ if(pass_damage < temp_damage_minimum)//too pathetic to really damage you. src.occupant_message("The armor deflects incoming projectile.") src.visible_message("The [src.name] armor deflects\the [Proj]") - return + return PROJECTILE_IMPACT_BLOCKED else if(Proj.armor_penetration < temp_minimum_penetration) //If you don't have enough pen, you won't do full damage src.occupant_message("\The [Proj] struggles to pierce \the [src] armor.") @@ -1230,25 +1220,21 @@ src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),ignore_threshold) //AP projectiles have a chance to cause additional damage - if(Proj.penetrating) - var/distance = get_dist(Proj.starting, get_turf(loc)) - var/hit_occupant = 1 //only allow the occupant_legacy to be hit once - for(var/i in 1 to min(Proj.penetrating, round(Proj.damage/15))) + if(Proj.legacy_penetrating) + var/hit_occupant = 1 //only allow the occupant to be hit once + for(var/i in 1 to min(Proj.legacy_penetrating, round(Proj.damage/15))) if(src.occupant_legacy && hit_occupant && prob(20)) - Proj.projectile_attack_mob(src.occupant_legacy, distance) + Proj.impact(occupant_legacy) hit_occupant = 0 else if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT), 1) - Proj.penetrating-- + Proj.legacy_penetrating-- if(prob(15)) break //give a chance to exit early - Proj.on_hit(src) //on_hit just returns if it's argument is not a living mob so does this actually do anything? - return - //This refer to whenever you are caught in an explosion. /obj/vehicle/sealed/mecha/legacy_ex_act(severity) var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] @@ -1267,19 +1253,13 @@ src.log_append_to_last("Armor saved, changing severity to [severity].") switch(severity) if(1.0) - src.take_damage_legacy(initial(src.integrity), "bomb") + src.take_damage_legacy(initial(src.integrity)/1.25, "bomb") if(2.0) - if (prob(30)) - src.take_damage_legacy(initial(src.integrity), "bomb") - else - src.take_damage_legacy(initial(src.integrity)/2, "bomb") - src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) + src.take_damage_legacy(initial(src.integrity)/2.5, "bomb") + src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) if(3.0) - if (prob(5)) - qdel(src) - else - src.take_damage_legacy(initial(src.integrity)/5, "bomb") - src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) + src.take_damage_legacy(initial(src.integrity)/8, "bomb") + src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) return /*Will fix later -Sieve @@ -2724,7 +2704,6 @@ // removing.mobility_flags = NONE removing.clear_alert("charge") removing.clear_alert("mech damage") - removing.reset_perspective() if(occupant_legacy == removing) occupant_legacy = null diff --git a/code/modules/vehicles/sealed/mecha/mecha_actions.dm b/code/modules/vehicles/sealed/mecha/mecha_actions.dm index 188179b6e542..a0ec46bdf318 100644 --- a/code/modules/vehicles/sealed/mecha/mecha_actions.dm +++ b/code/modules/vehicles/sealed/mecha/mecha_actions.dm @@ -68,7 +68,7 @@ return var/obj/vehicle/sealed/mecha/chassis = target chassis.lights() - button_icon_state = "mech_lights_[chassis.lights ? "off" : "on"]" + button_icon_state = "mech_lights_[chassis.lights ? "on" : "off"]" update_buttons() /datum/action/mecha/mech_toggle_internals @@ -80,7 +80,7 @@ if(.) return var/obj/vehicle/sealed/mecha/chassis = target - button_icon_state = "mech_internals_[chassis.use_internal_tank ? "off" : "on"]" + button_icon_state = "mech_internals_[chassis.use_internal_tank ? "on" : "off"]" update_buttons() chassis.internal_tank() @@ -105,7 +105,7 @@ return var/obj/vehicle/sealed/mecha/chassis = target chassis.strafing() - button_icon_state = "mech_strafe_[chassis.strafing ? "off" : "on"]" + button_icon_state = "mech_strafe_[chassis.strafing ? "on" : "off"]" update_buttons() /datum/action/mecha/mech_defence_mode @@ -118,7 +118,7 @@ return var/obj/vehicle/sealed/mecha/chassis = target chassis.defence_mode() - button_icon_state = "mech_defense_mode_[chassis.defence_mode ? "off" : "on"]" + button_icon_state = "mech_defense_mode_[chassis.defence_mode ? "on" : "off"]" update_buttons() /datum/action/mecha/mech_overload_mode @@ -131,7 +131,7 @@ return var/obj/vehicle/sealed/mecha/chassis = target chassis.overload() - button_icon_state = "mech_overload_[chassis.overload ? "off" : "on"]" + button_icon_state = "mech_overload_[chassis.overload ? "on" : "off"]" update_buttons() /datum/action/mecha/mech_smoke @@ -155,7 +155,7 @@ return var/obj/vehicle/sealed/mecha/chassis = target chassis.zoom() - button_icon_state = "mech_zoom_[chassis.zoom ? "off" : "on"]" + button_icon_state = "mech_zoom_[chassis.zoom ? "on" : "off"]" update_buttons() /datum/action/mecha/mech_toggle_thrusters @@ -168,7 +168,7 @@ return var/obj/vehicle/sealed/mecha/chassis = target chassis.thrusters() - button_icon_state = "mech_thrusters_[chassis.thrusters ? "off" : "on"]" + button_icon_state = "mech_thrusters_[chassis.thrusters ? "on" : "off"]" update_buttons() /datum/action/mecha/mech_cycle_equip //I'll be honest, i don't understand this part, buuuuuut it works! @@ -241,7 +241,7 @@ if(.) return var/obj/vehicle/sealed/mecha/chassis = target - button_icon_state = "mech_phasing_[chassis.phasing ? "off" : "on"]" + button_icon_state = "mech_phasing_[chassis.phasing ? "on" : "off"]" update_buttons() chassis.phasing() @@ -254,7 +254,7 @@ if(.) return var/obj/vehicle/sealed/mecha/chassis = target - button_icon_state = "mech_phasing_[chassis.cloaked ? "off" : "on"]" + button_icon_state = "mech_phasing_[chassis.cloaked ? "on" : "off"]" update_buttons() chassis.toggle_cloaking() diff --git a/code/modules/vehicles/sealed/mecha/mecha_wreckage.dm b/code/modules/vehicles/sealed/mecha/mecha_wreckage.dm index 210ec44013fe..c7f2b5ee2392 100644 --- a/code/modules/vehicles/sealed/mecha/mecha_wreckage.dm +++ b/code/modules/vehicles/sealed/mecha/mecha_wreckage.dm @@ -20,16 +20,6 @@ crowbar_salvage = new return -/obj/effect/decal/mecha_wreckage/legacy_ex_act(severity) - if(severity < 2) - spawn - qdel(src) - return - -/obj/effect/decal/mecha_wreckage/bullet_act(var/obj/projectile/Proj) - return - - /obj/effect/decal/mecha_wreckage/attackby(obj/item/W as obj, mob/user as mob) if(istype(W, /obj/item/weldingtool)) var/obj/item/weldingtool/WT = W diff --git a/code/modules/vehicles/sealed/mecha/subtypes/combat/gorilla.dm b/code/modules/vehicles/sealed/mecha/subtypes/combat/gorilla.dm index 49381b860169..10e46a3ccd1c 100644 --- a/code/modules/vehicles/sealed/mecha/subtypes/combat/gorilla.dm +++ b/code/modules/vehicles/sealed/mecha/subtypes/combat/gorilla.dm @@ -128,9 +128,11 @@ icon_state = "shell" damage = 1000 // In order to 1-hit any other mech and royally fuck anyone unfortunate enough to get in the way. -/obj/projectile/bullet/cannon/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/bullet/cannon/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return explosion(target, 0, 0, 2, 4) - return 1 /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/cannon/weak name = "8.8 cm KwK 36" diff --git a/code/modules/vehicles/sealed/mecha/subtypes/working/ripley.dm b/code/modules/vehicles/sealed/mecha/subtypes/working/ripley.dm index abbbddf2bf1e..4565776996a8 100644 --- a/code/modules/vehicles/sealed/mecha/subtypes/working/ripley.dm +++ b/code/modules/vehicles/sealed/mecha/subtypes/working/ripley.dm @@ -27,13 +27,6 @@ icon_scale_x = 1.2 icon_scale_y = 1.2 -/obj/vehicle/sealed/mecha/working/ripley/Destroy() - for(var/atom/movable/A in src.cargo) - A.forceMove(loc) - step_rand(A) - cargo.Cut() - ..() - /obj/vehicle/sealed/mecha/working/ripley/firefighter desc = "Standard APLU chassis was refitted with additional thermal protection and cistern." name = "APLU \"Firefighter\"" diff --git a/code/modules/vehicles_legacy/Securitrain_vr.dm b/code/modules/vehicles_legacy/Securitrain_vr.dm index 005f7ac2b577..81be49d1a6ba 100644 --- a/code/modules/vehicles_legacy/Securitrain_vr.dm +++ b/code/modules/vehicles_legacy/Securitrain_vr.dm @@ -101,12 +101,11 @@ ..() //cargo trains are open topped, so there is a chance the projectile will hit the mob ridding the train instead -/obj/vehicle_old/train/security/bullet_act(var/obj/projectile/Proj) +/obj/vehicle_old/train/security/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(has_buckled_mobs() && prob(70)) - var/mob/living/M = pick(buckled_mobs) - M.bullet_act(Proj) - return - ..() + var/mob/buckled = pick(buckled_mobs) + return proj.impact_redirect(buckled, args) + return ..() /obj/vehicle_old/train/security/update_icon() if(open) diff --git a/code/modules/vehicles_legacy/bike.dm b/code/modules/vehicles_legacy/bike.dm index 121c533d38f4..9e5ee11e95c7 100644 --- a/code/modules/vehicles_legacy/bike.dm +++ b/code/modules/vehicles_legacy/bike.dm @@ -174,12 +174,11 @@ ..() -/obj/vehicle_old/bike/bullet_act(var/obj/projectile/Proj) +/obj/vehicle_old/bike/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(has_buckled_mobs() && prob(protection_percent)) var/mob/living/L = pick(buckled_mobs) - L.bullet_act(Proj) - return - ..() + return L.bullet_act(arglist(args)) + return ..() /obj/vehicle_old/bike/update_icon() cut_overlays() diff --git a/code/modules/vehicles_legacy/cargo_train.dm b/code/modules/vehicles_legacy/cargo_train.dm index 0f5c981c533d..084640716307 100644 --- a/code/modules/vehicles_legacy/cargo_train.dm +++ b/code/modules/vehicles_legacy/cargo_train.dm @@ -86,7 +86,7 @@ /* //cargo trains are open topped, so there is a chance the projectile will hit the mob ridding the train instead -/obj/vehicle_old/train/cargo/bullet_act(var/obj/projectile/Proj) +/obj/vehicle_old/train/cargo/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(has_buckled_mobs() && prob(70)) var/mob/living/L = pick(buckled_mobs) L.bullet_act(Proj) diff --git a/code/modules/vehicles_legacy/rover_vr.dm b/code/modules/vehicles_legacy/rover_vr.dm index cbc180b2767d..ef0305f9abb4 100644 --- a/code/modules/vehicles_legacy/rover_vr.dm +++ b/code/modules/vehicles_legacy/rover_vr.dm @@ -102,12 +102,11 @@ ..() //cargo trains are open topped, so there is a chance the projectile will hit the mob ridding the train instead -/obj/vehicle_old/train/rover/bullet_act(var/obj/projectile/Proj) +/obj/vehicle_old/train/rover/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(has_buckled_mobs() && prob(70)) - var/mob/living/M = pick(buckled_mobs) - M.bullet_act(Proj) - return - ..() + var/mob/buckled = pick(buckled_mobs) + return proj.impact_redirect(buckled, args) + return ..() /obj/vehicle_old/train/rover/update_icon() if(open) diff --git a/code/modules/vehicles_legacy/train.dm b/code/modules/vehicles_legacy/train.dm index 1cd8e8bab08f..32f45a77dabe 100644 --- a/code/modules/vehicles_legacy/train.dm +++ b/code/modules/vehicles_legacy/train.dm @@ -55,12 +55,11 @@ add_attack_logs(D,M,"Ran over with [src.name]") //trains are commonly open topped, so there is a chance the projectile will hit the mob riding the train instead -/obj/vehicle_old/train/bullet_act(var/obj/projectile/Proj) +/obj/vehicle_old/train/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(has_buckled_mobs() && prob(70)) - var/mob/living/L = pick(buckled_mobs) - L.bullet_act(Proj) - return - ..() + var/mob/buckled = pick(buckled_mobs) + return proj.impact_redirect(buckled, args) + return ..() /obj/vehicle_old/train/update_icon() if(open) diff --git a/code/modules/vehicles_legacy/vehicle.dm b/code/modules/vehicles_legacy/vehicle.dm index 8e5b29152079..80de4b285178 100644 --- a/code/modules/vehicles_legacy/vehicle.dm +++ b/code/modules/vehicles_legacy/vehicle.dm @@ -13,6 +13,8 @@ anchored = 1 animate_movement=1 light_range = 3 + // todo: uses old integrity for now + integrity_flags = INTEGRITY_INDESTRUCTIBLE buckle_allowed = TRUE buckle_flags = BUCKLING_PASS_PROJECTILES_UPWARDS @@ -121,9 +123,9 @@ else ..() -/obj/vehicle_old/bullet_act(var/obj/projectile/Proj) - health -= Proj.get_structure_damage() - ..() +/obj/vehicle_old/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + health -= proj.get_structure_damage() healthcheck() /obj/vehicle_old/proc/adjust_health(amount) diff --git a/code/modules/vore/fluffstuff/custom_items.dm b/code/modules/vore/fluffstuff/custom_items.dm index 0ac75f8d2924..531e04b41d11 100644 --- a/code/modules/vore/fluffstuff/custom_items.dm +++ b/code/modules/vore/fluffstuff/custom_items.dm @@ -66,50 +66,6 @@ if(!parts) qdel(src) -/* -//JoanRisu:Joan Risu -/obj/item/flame/lighter/zippo/fluff/joan - name = "Federation Zippo Lighter" - desc = "A red zippo lighter with the United Federation Logo on it." - icon = 'icons/vore/custom_items_vr.dmi' - icon_state = "joanzip" - -//JoanRisu:Joan Risu -/obj/item/sword/fluff/joanaria - name = "Aria" - desc = "A beautifully crafted rapier owned by Joan Risu. It has a thin blade and is used for quick attacks." - icon = 'icons/vore/custom_items_vr.dmi' - icon_state = "joanaria" - icon_override = 'icons/vore/custom_items_vr.dmi' - item_state = "joanariamob" - origin_tech = "materials=7" - damage_force = 15 - sharp = 1 - edge = 1 - attack_sound = 'sound/weapons/bladeslice.ogg' - - -/obj/item/sword/fluff/joanaria/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - - if(default_parry_check(user, attacker, damage_source) && prob(75)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(user.loc, 'sound/weapons/punchmiss.ogg', 50, 1) - return 1 - return 0 - -//joanrisu:Katarina Eine -/obj/item/material/knife/tacknife/combatknife/fluff/katarina - name = "tactical Knife" - desc = "A tactical knife with a small butterly engraved on the blade." -*/ - -/obj/item/material/knife/tacknife/combatknife/fluff/katarina/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - - if(default_parry_check(user, attacker, damage_source) && prob(75)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(user.loc, 'sound/weapons/punchmiss.ogg', 50, 1) - return 1 - return 0 //For General use /obj/item/sword/fluff/joanaria/scisword @@ -119,87 +75,6 @@ icon_state = "scisword" origin_tech = "materials=7" -/* -//john.wayne9392:Harmony Prechtl -/obj/item/twohanded/fireaxe/fluff/mjollnir - name = "Mjollnir" - desc = "Large hammer that looks like it can do a great deal of damage if properly used." - icon = 'icons/vore/custom_items_vr.dmi' - icon_state = "harmonymjollnir" - origin_tech = "materials=7" - attack_verb = list("attacked", "hammered", "smashed", "slammed", "crushed") - -//JoanRisu:Joan Risu -/obj/item/card/id/centcom/station/fluff/joanbadge - name = "Faded Badge" - desc = "A faded badge, backed with leather, that reads 'NT Security Force' across the front." - icon = 'icons/vore/custom_items_vr.dmi' - icon_state = "joanbadge" - registered_name = "Joan Risu" - assignment = "Centcom Officer" - - -/obj/item/card/id/centcom/station/fluff/joanbadge/attack_self(mob/user) - . = ..() - if(.) - return - if(isliving(user)) - user.visible_message("[user] flashes their golden security badge.\nIt reads:NT Security.","You display the faded badge.\nIt reads: NT Security.") - -/obj/item/card/id/centcom/station/fluff/joanbadge/attack(mob/living/carbon/human/M, mob/living/user) - if(isliving(user)) - user.visible_message("[user] invades [M]'s personal space, thrusting [src] into their face insistently.","You invade [M]'s personal space, thrusting [src] into their face insistently.") - -//JoanRisu:Joan Risu -/obj/item/pda/heads/hos/joanpda - icon = 'icons/vore/custom_items_vr.dmi' - icon_state = "pda-joan" - -//Vorrarkul:Lucina Dakarim -/obj/item/pda/heads/cmo/fluff/lucinapda - icon = 'icons/vore/custom_items_vr.dmi' - icon_state = "pda-lucina" - -//john.wayne9392:Harmony Prechtl -/obj/item/modkit_conversion/fluff/harmonyspace - name = "Harmony's captain space suit modkit" - desc = "A kit containing all the needed tools and parts to modify a Captain's hardsuit. It has green and yellow parts inside." - - icon = 'icons/vore/custom_items_vr.dmi' - icon_state = "harmony_kit" - - from_helmet = /obj/item/clothing/head/helmet/space/capspace - from_suit = /obj/item/clothing/suit/armor/captain - to_helmet = /obj/item/clothing/head/helmet/space/capspace/fluff/harmhelm - to_suit = /obj/item/clothing/suit/armor/captain/fluff/harmsuit - -//john.wayne9392:Harmony Prechtl -/obj/item/modkit_conversion/fluff/harmonysuit - name = "Harmony's captain suit modkit" - desc = "A sewing kit containing all the needed tools and fabric to modify a Captain's suit and hat. It has green and yellow fabrics inside." - - icon = 'icons/vore/custom_items_vr.dmi' - icon_state = "harmony_kit" - - from_helmet = /obj/item/clothing/head/caphat - from_suit = /obj/item/clothing/under/rank/captain - to_helmet = /obj/item/clothing/head/centhat/fluff/harmhat - to_suit = /obj/item/clothing/under/rank/captain/fluff/harmuniform - -//scree:Scree -/obj/item/modkit_conversion/fluff/screekit - name = "Scree's hardsuit modification kit" - desc = "A kit containing all the needed tools and parts to modify a hardsuit for a specific user. This one looks like it's fitted for a winged creature." - - icon = 'icons/vore/custom_items_vr.dmi' - icon_state = "modkit" - - from_helmet = /obj/item/clothing/head/helmet/space/void - from_suit = /obj/item/clothing/suit/space/void - to_helmet = /obj/item/clothing/head/helmet/space/void/engineering/hazmat/fluff/screehelm - to_suit = /obj/item/clothing/suit/space/void/engineering/hazmat/fluff/screespess -*/ - //General Use /obj/item/flag name = "Nanotrasen Banner" @@ -1254,6 +1129,7 @@ ..() //jacknoir413:Areax Third +// todo: check sprite, if it matches citmain just integrate this to citrp proper. /obj/item/melee/baton/fluff/stunstaff name = "Electrostaff" desc = "Six-foot long staff from dull, rugged metal, with two thin spikes protruding from each end. Small etching near to the middle of it reads 'Children Of Nyx Facilities: Product No. 12'." @@ -1270,6 +1146,10 @@ attack_verb = list("beaten") lightcolor = "#CC33FF" + passive_parry = /datum/passive_parry/melee{ + parry_chance_melee = 30; + } + //Two Handed var/wielded = 0 var/base_name = "stunstaff" @@ -1294,13 +1174,6 @@ update_icon() ..() -/obj/item/melee/baton/fluff/stunstaff/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(wielded && default_parry_check(user, attacker, damage_source) && prob(30)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(user.loc, 'sound/weapons/punchmiss.ogg', 50, 1) - return 1 - return 0 - /obj/item/melee/baton/fluff/stunstaff/update_icon() icon_state = "[base_icon][wielded][status]" item_state = icon_state diff --git a/code/modules/xenoarcheaology/artifacts/artifact.dm b/code/modules/xenoarcheaology/artifacts/artifact.dm index b29df3796257..a55ec3da15ef 100644 --- a/code/modules/xenoarcheaology/artifacts/artifact.dm +++ b/code/modules/xenoarcheaology/artifacts/artifact.dm @@ -4,6 +4,7 @@ icon = 'icons/obj/xenoarchaeology.dmi' icon_state = "ano00" var/icon_num = 0 + integrity_flags = INTEGRITY_INDESTRUCTIBLE density = TRUE var/datum/artifact_effect/my_effect var/datum/artifact_effect/secondary_effect @@ -223,7 +224,7 @@ if(secondary_effect && secondary_effect.trigger == TRIGGER_TOXIN && prob(25)) secondary_effect.ToggleActivate(0) else if(istype(W,/obj/item/melee/baton) && W:status ||\ - istype(W,/obj/item/melee/energy) ||\ + istype(W,/obj/item/melee/transforming/energy) ||\ istype(W,/obj/item/melee/cultblade) ||\ istype(W,/obj/item/card/emag) ||\ istype(W,/obj/item/multitool)) @@ -274,16 +275,17 @@ to_chat(M, "You accidentally touch [src].") ..() -/obj/machinery/artifact/bullet_act(var/obj/projectile/P) - if(istype(P,/obj/projectile/bullet)) +/obj/machinery/artifact/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + if(istype(proj,/obj/projectile/bullet)) if(my_effect.trigger == TRIGGER_FORCE) my_effect.ToggleActivate() if(secondary_effect && secondary_effect.trigger == TRIGGER_FORCE && prob(25)) secondary_effect.ToggleActivate(0) - else if(istype(P,/obj/projectile/beam) ||\ - istype(P,/obj/projectile/ion) ||\ - istype(P,/obj/projectile/energy)) + else if(istype(proj,/obj/projectile/beam) ||\ + istype(proj,/obj/projectile/ion) ||\ + istype(proj,/obj/projectile/energy)) if(my_effect.trigger == TRIGGER_ENERGY) my_effect.ToggleActivate() if(secondary_effect && secondary_effect.trigger == TRIGGER_ENERGY && prob(25)) diff --git a/code/modules/xenoarcheaology/tools/coolant_tank.dm b/code/modules/xenoarcheaology/tools/coolant_tank.dm index ba9ac92b9f42..feeeadb1fca8 100644 --- a/code/modules/xenoarcheaology/tools/coolant_tank.dm +++ b/code/modules/xenoarcheaology/tools/coolant_tank.dm @@ -9,9 +9,10 @@ . = ..() reagents.add_reagent("coolant", 1000) -/obj/structure/reagent_dispensers/coolanttank/bullet_act(var/obj/projectile/Proj) - if(Proj.get_structure_damage()) - if(!istype(Proj ,/obj/projectile/beam/lasertag) && !istype(Proj ,/obj/projectile/beam/practice) ) // TODO: make this not terrible +/obj/structure/reagent_dispensers/coolanttank/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + if(proj.get_structure_damage()) + if(!istype(proj ,/obj/projectile/beam/lasertag) && !istype(proj ,/obj/projectile/beam/practice) ) // TODO: make this not terrible explode() /obj/structure/reagent_dispensers/coolanttank/legacy_ex_act() diff --git a/code/modules/xenobio/items/weapons.dm b/code/modules/xenobio/items/weapons.dm index 243054319cad..e3a2c4b73cde 100644 --- a/code/modules/xenobio/items/weapons.dm +++ b/code/modules/xenobio/items/weapons.dm @@ -91,7 +91,11 @@ /obj/projectile/beam/stun/xeno/weak //Weaker variant for non-research equipment, turrets, or rapid fire types. agony = 3 -/obj/projectile/beam/stun/xeno/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null) +/obj/projectile/beam/stun/xeno/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(istype(target, /mob/living)) var/mob/living/L = target if(L.mob_class & MOB_CLASS_SLIME) @@ -106,5 +110,3 @@ if(H.species && H.species.get_species_id() == SPECIES_ID_PROMETHEAN) if(agony == initial(agony)) // ?????? agony = round((14 * agony) - agony) //60-4 = 56, 56 / 4 = 14. Prior was flat 60 - agony of the beam to equate to 60. - - ..() diff --git a/code/modules/xenobio2/mob/xeno procs.dm b/code/modules/xenobio2/mob/xeno procs.dm index ad6846e281e0..99c7e66db44e 100644 --- a/code/modules/xenobio2/mob/xeno procs.dm +++ b/code/modules/xenobio2/mob/xeno procs.dm @@ -143,7 +143,7 @@ Divergence proc, used in mutation to make unique datums. /mob/living/simple_mob/xeno/proc/BuildReagentLists() return -/mob/living/simple_mob/xeno/bullet_act(var/obj/projectile/P) +/mob/living/simple_mob/xeno/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) //Shamelessly stolen from ablative armor. if((traitdat.traits[TRAIT_XENO_CHROMATIC]) && istype(P, /obj/projectile/beam)) visible_message(")\The beam reflects off of the [src]!") diff --git a/code/modules/xenobio2/mob/xeno.dm b/code/modules/xenobio2/mob/xeno.dm index 4f801e75df5f..6b9cef60afb4 100644 --- a/code/modules/xenobio2/mob/xeno.dm +++ b/code/modules/xenobio2/mob/xeno.dm @@ -92,7 +92,7 @@ Also includes Life and New if(!health) set_stat(DEAD) -/mob/living/simple_mob/xeno/bullet_act(var/obj/projectile/Proj) +/mob/living/simple_mob/xeno/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(istype(Proj, /obj/projectile/beam/stun/xeno)) var/obj/projectile/beam/stun/xeno/hit = Proj stasis += hit.stasisforce diff --git a/config/entries/game.txt b/config/entries/game.txt index 8f155dab9f77..b83f9f2e7cde 100644 --- a/config/entries/game.txt +++ b/config/entries/game.txt @@ -11,15 +11,9 @@ STARLIGHT ## Set to 0 to disable holidays (you monster) ALLOW_HOLIDAYS -## Engine submap probabilities -## Supermatter -ENGINE_SUBMAP EngineSubmap_SM 40 -## R-UST Fusion -ENGINE_SUBMAP EngineSubmap_RUST 40 -## Tesla -ENGINE_SUBMAP EngineSubmap_Tesla 20 -## Singulo -ENGINE_SUBMAP EngineSubmap_Singulo 20 +## Engine submap probabilities by name +# todo: add the rest once names are standardized +# ENGINE_SUBMAP ProcEngine_Triumph_RUST ## Alert Levels # ALERT_DESC_GREEN TEXT HERE diff --git a/icons/effects/defensive/README.md b/icons/effects/defensive/README.md new file mode 100644 index 000000000000..750cab6abc41 --- /dev/null +++ b/icons/effects/defensive/README.md @@ -0,0 +1,3 @@ +# Effects - Defensive + +Basically shields, parries, etc. diff --git a/icons/effects/defensive/main_parry.dmi b/icons/effects/defensive/main_parry.dmi new file mode 100644 index 0000000000000000000000000000000000000000..307ef5b4a7e86889f4f87450fd53f9f4c026e26a GIT binary patch literal 37674 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 literal 0 HcmV?d00001 diff --git a/icons/items/melee/basic.dmi b/icons/items/melee/basic.dmi new file mode 100644 index 0000000000000000000000000000000000000000..c1207c837d185f0effe348531348af796ba41079 GIT binary patch literal 2957 zcmbVOdpy(o8~<*F(3o?Gq);jnlH;<<<|r|exr9(;>0%hBrOY<1CKSs#E|px$rHgB7 zC8sTSN?Qq&`z3bVFAHBY+i&N*UccY#_s8#?*YEfH%LsWMBGKeV6od(oY3S&^^p^_%PN z)SGn;=Y>x=L@RMJEF(>CvLwQOtMdG0 zqW{4LF928Fzb2adZY2_~;>Sj#jkoSnYymroZ#_u7b>qTUz}HR9YHY~}9xGX~Q7h~Q zSHBFM-cGgx3l{JNyzzr zWa;SHmw;Qt@_GF|fo22ZsD0cfftPxY@Iby?PL#;6w^$N+=>FxRx=C}g6p7`TsC_0) zmCeEXoB*kxQwYja@K+j_!r9h1yP9;#Om9>{2LU!N@NeeO1|(~j7^}2_p`-xHfg}Dd zc1!NLnmI(N^OiATk^@ghocR8N!OPw~F!3M-ne9B_A69C%Jdn04MkZ76g63E?l#L?R zq0K?8;ecI055K7_c5VydpkR4zwD33&jmz+n*T1`lkj9Tko?S@yNdC$$6z%k6A?lYz zzn1yd7ROM#zHE}ve@Sd7gh}XLf*$I<*4|Jy znvI>H`wQvSg(q?=`^t&3m$$!x_bZ+ajng~F!jLMqoPG}?Q*#I}mUO*v$HLA zT@jSpiN`~eGFTdlq<2bj>t_P#>GA_V2y#ud-+^>2S{N~?M! zKhf8TWOT#}V1q+)>SEr;$2ZN{^cwCj6-5AM@dyd=-b+}T;*O}I4ryC@NqjBNEk<*`>a!Zx(0*-pK z7cMcb&kzw}Fswk}(jRiCx0jGxtUNB+U^u>FJSx?hd($LKMPYne@V5|Ed>dEoHX}^3 zB}pk&Jde9;2Kcn%Z>7=(H0}u**{a%3RQT+I!@V_7KhG!CE!>j3{Bd*(+fKIFS}D1l;-RdvG2_(D~FB==0x;^s+| zWe9Y`$6F*@o_{HBIB@T>788^6Y5!;YHrdok5;fod?BwZ8_u|F(Ayr86C3<>7g*e_K zUhB&9uMeetc@Uer?j4L#lQ|YzbGT57(xUK~GvIOVjeEHkV<@>%{-axL*3iXhEjPo; z2V7}tE>s~|(eAWW!D>o%jRvs}yB`AbZJi&ezy%WgN^7w+L!Sa;1AW;QqEpGeJ=SeT zpW1?)gAse>gOp0z&yRPe5UTCTHgxus^OLX3k_>MprwYAgEuW{{dnNwC)%K9PK;Vb| z_Q+{5(oe}3i=IKe@W?6fWp@!%e#jhwx}|SRc3r^SHcc!8=9+lwJ-dHPE}&VFww@q2 zC^u74tc~XFMGa!C;(8Ano(BD2${qMC;cB2$IO6V?QUaoS+y_Ip;1YE^lL>y8fuvSkK(FOFVDjRe&{;PFeGyvCnQyKr&XLX;DI>h)?xhF{`lN z_m1!Gc(rKs#_F7ej9sncvv#LZNUe;HoH(CdS!ukbxcySw`m>PcbGTp$=qXw*8f<>F zNPs(dQr()M1-T0;%WKPZX@%y2w4(2=E^4$&Wj#Zi^VPb09iMFRl)Uv@_ju#UCj&XD z<#HvB`{+#Ne9M|&x6S&A=L33FU)e89jv;KSL?c!GY*j5@RRIB>C?7B>2F<})mzvMC zlEuyZbwI~pPwsy`Uj7R2j}J_zV*)rgb_^77E~xULZM~;p@iToU%@nLj!wiPaTxi(t z_f*+*wWj2Izls@-`1Z<6JM_vS=Aj++A*u$llok!Q*jx*(^j$%nBB82&mOQh3>%uEN z597K5tiz&ikX7$QFnngk{oF^(MsME=*~>42RXAVc+xK(|b#-?HL77)^fBjWlR1>S5$&(;k#N^~#?)bro`f>HKzl-i5K8dNy*D)r?FfU-P*Gy$ zFehl{(m6#y43bP6854D$t0_is42-1&`*XqhyV?F%;Qwl#}#OI?BuW+eh68p2N->=cQq9o9YQMt!lNKlS}8{Z}k5V#La##JCJHETI9MW z&G`g6e9wDqIAv?9ISAn<6#%f`W(x;>D5rT94* znh)2kjokKwie@d7>;kXE`**8zq=oPOR_3AK^L*w`Uxe|^KrVMP?UC{`PpY5tQkBDM zdlB=Dk!!HiogXI{mqoFiV+eXDHhd8>_FCSDvVLN)nYLecanZyF@)I}` zZ&%i**{gDTcf;V&%{y0T+hQ|~0C0q-$g$5pc1G%zGXE3#)^q_ PzXs5D$B&fS23-3G=HY_0 literal 0 HcmV?d00001 diff --git a/icons/items/melee/transforming.dmi b/icons/items/melee/transforming.dmi new file mode 100644 index 0000000000000000000000000000000000000000..0a1c652185cff5dbea8a30e712722c21e177e2e6 GIT binary patch literal 30606 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%0AP7KYJRsvFrKYm2HT*q7D<4mcfj6hhu=^-YySR zWbMLCoiWh5iOG7OMD`ScoUaf>>fj@K-4>z)(+ucnH$Z9q@safGfoR{?Xyb3)*X6Lu zk9+zb4e)`E{-_H4E18)))(mv!=${w((@ST>Lw8`4L&Wz2ax0gAd?@rSA^!Z=j#Xk6 zEQc_|(m zf;!ZhX$In6lCFG&v&o$BL2#z5_{=akH0C>D!QdU=`tjv$3Xy&DQnC7?pglhvPDk6m z>6!2BCeocK-c`B%{TWT=-^(4+mw8G4D35dFgW5yApW~%k2M3)4#{Z?(T%{;KUj@$4 zoz4a4ec@fx9NJ=VXce2wJK}*j-5l43Scd|iPVDd%RZZUDjhnDyDk_ibd zOG_UT4_|vkt*FpkpRT;qMCn@gsD@|yy`+f!gI&fHpEg|OhjwD}g3<@|97eOd4)4yv z*kD4s+=#i9v}3*;2V1%A4Y*WG)*00pcrm_h_WVvTKK=~h3%-xSvk@o$GBKbyNqrnR zangH8%|zmwqCX2J)2J_t{eTL0Hrae?tp>zscv(R%Lrjh5wRt87eGFz571LFFI##;l zXx0ksSwzkz#IKhsnbs!K|1SL%g@=tL`vaMlY#6DRly(dkZy&vQ8(b+iKVC>bgb)0# zdMgX^znYC}qHpP32q?B9uu(V+{OSt}1ulRFT z4_}6{ALQZEc%c3-zm_dx7t+1_oPVgD`%GwB6N)TWPqWp4+*@-fL@l2YOx)@S=;ZuW zcXH5~>ILJE_VcS2rN0tg{*GlX4wKy5cu9Q*C#w}GSibfrZ%Kq!m5lxP1A^J3R7@^t z*cUcQ6^m&n76%jVu;sriVKE)V1U_gd13kG-JR9^c^Gt7Vum8ixm!Ss@$zeWHM4w7Y zX{|3c$l5zo3%ka`lmfvw-Voxq)_MsFGvl=pfptuYM*iS$2L+n(+Jz;8_|m#}G*MFk zk~7LETTteuxW*2fTv|~3ibx|w|lLTv@3}HuLClm?)e_yV`K|hy&8<-?d3HE z-^=D(76pH{bBey4mArQrYIMx;gPuF03OIqqwyzdhCBIqd3QF|Ji^L$W4>UCSb#xW+ z1ET?A4+~(iaj+a>=RI~5sid+I4n7d)LY#|ECDJ)pDN#HONlaRN-8+@=V>ZqoV%`~pgmj^FLM8U8Ap~lYXhVXPDJbtE76>j}!|U9is!?b}z_R`?7r_>E(rtGg*NSSb^Qy}%qp*MR7v-IO~$oinY=LsxoGMzs_)YnAUkN9!* zm>3j}z(SA*$cAWc{sS7^8sEhdF%BFv4I2CFuIDm(3h!7VjFjuASLjh>|!0?rw@t_ zfKKj{P>R9T)IOOTsv|jJ$YQFqf97+378x@|JT*OGg2`FmB?KJ1562saPhLZCACp8+ zZ8!VMnG|y^CH}C+@h6dJCJBi>gyJ(Zkr$kV36;=;ULaL=DLt4gc2v_D*1H{RR@?d4 z@mx!@S~?+AAUVj)hljB@CkCnSr_s`n^@`K3m+Ju%+<4qkUKkFD~rloa)-K9T=)J#oF z8Rpjm*lRLWT(oJ~SJwCz<|jo|A98wO&XP?y&)#qPAFZge`&T0}a_Rg|){5B4Op~{; zc%dWK6)WfV&|yLIqtH={%H+WL2r|JnN@S-@0*#n2?QV6sBr?Qrtb zb%%xS9IRVn@83!H<9Gg(6k!980mcdp){;K^cUdQge{Nj!CSm@4Gj$>kSRj{j0Rv%X zy5=UfWYo&Jl`UzS2m&az$N@A|b~ iT^8B9x(>8A0}`VOE@7K){B;sM3^O;gG%Yo8yY)ZC^ASk^ literal 0 HcmV?d00001 diff --git a/icons/items/shields/basic.dmi b/icons/items/shields/basic.dmi new file mode 100644 index 0000000000000000000000000000000000000000..6afb78610041dea16c2a12482441071043ec5926 GIT binary patch literal 22474 zcmcG$1z1$w+b+K8R0O0;N*W}jOQjSkNoho-yK^uoDG?+j1t~#7I)@JF?v{?BhZtu5 zdwk#b_xrx{|GsmsbFOo)ORr(Cy=T_i&$`$B+|RR~y~8!tm2VKz5&{5lPE@!dV*kip+vMQ4V&4}I5! zwAAQl^z-M)zT^|ceySK<2*gX06s4ti!PqKC35b@4z^|3v-d_%thB}*0GjdNgIy^rs zn7Q*6)AZ+4LXXcD-Uk@(f(Z50r%PWvFH8+3BuwzQ8AZL%G4wcMpQ<+Eqq@nOkZ+Tj zqhU?QL2ULKs#yf*o!D~Dc59C+yLZ$LK;Rl*=;PazO1t6z`HhCN#9}37g;`to-Kj_%5r**Macx%??Wbawu$h61548zI*QV}~40j7&Sq<|J zG?rT*y$$&+5*c@ICIPPzyXvXAL>hb3i)=?s+q__p%n8R_s0i-{uDbfrh5e3rbFtuZkz@g3%70E5i@(>;P7=RnBQ0q563 z!pXfp0}{f68`r*Tim&fV4|=7;lHHm%B&-I?K7aoFCM+!Mw`=S)#w}9P*h2#V{CFIO z1Aqtb|BD08@m(M+EKAqW@OwU#NyhiK02J}Fzt202ES7I(2jDemFeZRse$OGgc)-TO z{6tmt^FpJab*U=@`}ldFc#4r}PH$*$b6&LKCL$tYKK=7mf}w0!5$E9RV)k0t)9u2ED#kROncc%HKA9mWn;4tCgjkqPKWn_#yzRx1*|{9(AiGH7xr6^CO06&q0WP zRdu(-{8XeRhla=HY_A*bJ(oG^ll}SG&3grHxp_#F;q+|Ml97>TuST^|#YTSa4|Ie9 zn|<8WqWMO)gNw#3N1(Z921~_FY?C}WnC(|*M$xizc6lQ10Jf&RUa@h()64zO&X76` zzHz*uEM{@fqucxfv~Lj&rofWloG=Fws{R`FQSQv}DZSX;uT~fpbVCY$BsX8~q+?(^Mf#!h3Q&|ruNSX(HoaqS>vzHT^OAqUzi{PlQ6 zEQd9Xg3qc%#naT8_c&CW9-*=5+HvmEM2;M5Y?f`@-As-PhKj9OUSFOj$y~e`W0b(S zeU3soY1FiqJ`ZZXAqG^WI<|v1@>xaWiO!&hwWJ&6wYvUSD|O1C!VVn1FtPnn%o&mB zONTg{#gzAv=@LyFL^O9YR*(i6BnDx)2w~f^O?ii}c7Johpr4smz~+$lJja?6db7yDyh6?21&Yv7-5TjU4B@U*1=vEP)ZXI|w=!1% zT$~>zER-JpfM7bz*WKS=J@c&~{#vP+wWMN{q0-(0mb{UtoAK;&r7*N)64QT^5STmpv={;RFw`| zI2jZ~{VF*894!g#jcni~X<5J0DzI( zB+-mAwgwH`1RsC~VKN3539d45^5REHJHk~=%;(VxT9eZc%A*svLru0%nqx` zV@EG1E336cjrF`(c=$nzszdY-PG<=%k|+uv+e46~ytoyzxq%rWKg;9w{``W1H@#~| zR|N$HE$jU$!wELKhqR=Mk1Z@L9`0J;KI_7iA37Y0n5oKtJNO~^b&&$UYR@NFaU_i( z^~TJMmfvq|aDiX|i1h)UvcPNKA~Pr>3O3;aVs0(vt}MtMh_XpqvJ}s@A35n6L*SBr z=hE%<>_xuk80K=fi_0W6tGw z`>si+&4X{OKIWpGSlIkfWP1mAntV0&sd-}zW|auhDu=C#rSF{7sn}|fc{flW?APk( z>M~b)x%2#}>RXu7Hn}l(z_92>6zCedmbY4A$?O_anwyIYl=>X6=XZ&cvAEjCH?O_a z@Te@!5(mle7kcHsGOFf`x`qa0B}S#oS}q=JVftPq&}Bw%%V&*#jpW6F9G1zt@x_ zah;DEn@%u8<&QOb$VA7yXU!>`3{7kt-WTB!6o8Yc(zB`V*a43{^Fww<5020ho-EGI9}I%L`x zOy&7i#W{J{EFeHew(y+ZVQ=Hm$C43%5!NBfOAy8QNrJXy(lz+t04hf(;}r?!oD0ir zta!G4qaPk`t@A?(Yxa&!B1V1{Fs|gy;no|H;v_c(2WWz>&K$T;o227eVaSMRHmiwV~~47MPdG>ukZ}j z#kfH+6O^7kG_w7m`ZnD!xl6wxH=!(UK#$MF+NjjK)^+6hDjcz|fk55uoU*)|#Yr%6 zS4pE3!T!bz=56)(!KDtcQbJtq*F{O%-!|*{g7V87{d>~OWAIP;Z_o()(3ow{ zX1!NqdqF?k;kBBd*3L@9J*CV)5BIA5{4Hg;tzSAue%&{9fY)}z%k}Y@|6N54c?AW9 z;pN9c;0M_Y^bs2~CGdao;HyqKmWoJ>%vlm9fJeeee0jM&UKqR};JeV^leRDB^b)1O zR)Q0e7;1tlGOW|wybQhGnT7r+D&j2;dU-Tu8N<~T!-NN%Y!71rrB;iwKr?$=C>9Vs zF-|axKbRt>v9(xYHU%Y}mLu)4`=3kQ{HQxulTgS2ps>8u^3K}cUO`>`=7862pe>Z7 z3slisja~vDlp^o3MQ3|>Jjj537r*sty)T)$aO8Anvg_1hsEWbKNJ(>*g4C|mg;M73 z>k}H1>_n>UPUYXO6j9Al8cTNfKTAzPFi!=(0aGVqP@W8WjSg^yyFWKj1%)@amza8J>lC!a z6_Wo81=y{^^m*YSZf&1PR>IyN8p$?{AI6?cyMrVlBfeMr~tRq4(Ei( zr-aBo>lq$}-t4Hb8Pzl};8a6JZ^*q_z0i2N13AuBn`*GpHohj3A#mQA`0*>AOMB&e zbhYEW?WImNH!CaF4Gs-V;QROQA7f)NfSRLUJQx5)Mj)Zl6GF$Pi`SzsL{yhLAI3x} zuLlgDzHJ~o$o0rAI&Al=c=AtcnHr=|7P*m~5MkB@J2v4jbPiSzWU#Ea+-eO+A)V0U`jT69a?|K~*q zv=2N)_=jQwaG;8dq4cjF9tW-mV?-#!uPMD@k5pz0Yc*pOC z7(3S*=YRkH(ALR?PX4R}f_g{@csN~yj2h|3y^AK+AI7!M(2-(aG0M0`=IU(us8^tC zYc#J60)?mCM4O1eepe{96vihwlZr{EmC&xrXL$f(OYVVlzgY}wi`*F}qMt)(1 zSaMhVrBJAW#nsp7JsOvfc7D1)BsBUo8{ei+sbRhb{Uu6--- zgk8*MoTF}BX`~{b0}2+W+vB3Aw1^pUASERwfG#k$dS(2KY2QiV)akxnBc%+@y9=@O z&8zuxh~({To=r?3ZXA7He#y&gocG*}Bq!s`{XH7eLO`MwM4J8dn zANI>|>&w)nE$O548$RSV;*`?njcz^VDFIu|62!| z-x;I8ypQzn$|Vf*@sVIHGpM9-N?C;!o5L>43yS%`iX;f64=T4VS2hzvc?=VXJz|14 z0_dy@F@xV}2!`V7_sT(yI{!^)zyNib!UcM%*9zp;`JxaJNk1Fo7FzkJfc3p5m6Wey7LnVppwh%W0kn@pA3cJEAu(1K|w$o*O9Xp)T-)D&7(JvZm zjHx#D#{@G>g<|WAOvkVegfI#?b~;6L1>k4?r!f8vpD_3jjOPX2H#MK8h~5CHU8~yX zu@))if|u7rT}X5A;Xza|KEokG1)ReV$uJ1P>^jc4hhnf~ek<9e*N0kHODXjk zy}J&zjQ-0v%MlO_h8x2$p123VUUL z+KINOfH|_`1@Z^S@KH%Gu{_9_UNLwx(t0f+o@o9Px(>DEr22j3;6rqlOgEYHorh{w0nT5&@dIV1b$%Q@~4eU>Eh#?{{qyOE51D2 zT~o(@8eVKY1OBF_HbnZUDZ!HxQ9<8_4~@xO;n7|Rx4&uN+&O!qS?^^(T)&lc4-G4@ zA1frr4v4CH49w2?nETKrkdnP{ueOp@qBOm)zS-p`*f0H=WI3WF78Z|PpeKg92a1QMF=}bx+?wdPLGEcRYxQ*h;y8lck|Ph602{M1l@8BE$@m zw?PF5>bU<=%H{jX`x<&L&R3~^`0&1?AUf8`UtyElEgNk+DoV*jXRLypiP00r{4Wo=7pLnfLuEqJk*e8HCe9#bXtw?W71=*hifIBF|{W z5Th$_Jq_DoRnODjBn|>|k0YLU{&a`e``q00M-T|%WQbsZFbb&<! z;J9RD-Hd^5J_=rC=ZU;JMPXcC!%2X9C~gPjc-gQK6uIE380FRN_77)(JiLp4Cufy{ z%9x)DEm~)0eIFYh#7QA?qt0~nv-!%aKQ>iZ>Kr{I#HPpGruL!&8O46nJ0%(QA5&Ow zd@3>jEXR(MeuQ860Az{>Lmiz8dz?tZaMQi%7B=7yiX>I{7!|AR;zrAGa6MRJ|C>Dy zqM^g94zH2QCVru%Rj2KUh!%vOAZj9E^Frg-4tmc;%lolDU@d?{@BQ5T32$G{-_Q(} z!Ty+EQ_4oN{_HO*DQl$!mW#1HT5HV=G!HdP?&Od$Z0s|@c)3(E2<3M_XL`b>vb;u3 zgoIK5WIs?G${M9}><_?sK>I4QcNdc;-5{ledq|nwkdKtEur3$ zK2}#TK&-hZj)TO&`zk7QWJGvqV&<(jM>H1jnit6@=dHjt27X1Y>;@0vMcq&6OgxJ` zEQ`&+kJm9WE?@C=MX#C;PIJfzME7I*hp!wX9}|+_hw@xB-{>=2rxhMb*4G`I<4A$g2t5DKV3+d-Zb3Sd}R7Is?Lx-(*(kb1Jva zH9appG?n_Mo=WDfzYl?^toFuM#uZ;7lfL={L))IJ-ZX%EHGn@uG4&B6*!z5(3V~|Y z3h#Q-iTN2X0OE9F`o%EbnyHJYZJ{52wR-W+p*seuMp|(}dVjxsq3~cm0#H>(NY{w$}e#C3es))L;%jb&SPgOgh(pOpRV#x1SjELlsMkCsg z)B*WHrRtBeCycAk6EX-lcBw|Xx@$8AU*q_<3)l5ujb7sp)EYT2{&r&Uy-j_F9~kg^MFy^BsdkUBG1~FihbrxXm@^Sz2QJ2&=rxB zU^JooMtdDJjiv(qcYNuSWAPI{OtP=tyT8!JBhm2>u#n*HQ?TrUwX8&K*#+G%`Nr(^ z*2_}vgb@}gU%v7nfscKn#*$^bv|Sh_`$)oh3Kw4B*xmoD0?~=;ENdf4ySijE$!#wVC`4L@_iZhP&$*gI4|_|-_y3kF5$hsM=MTg<=B zcy3FpekuU<>zUx-A-AHdvp_M^rTZLo)pgeHFK2`@Ffq@}3R?Tp8FI(08hgk%(#!0A zOp1P3@6>|jJ*5>c=7w8`OSR5?uo1P3amDDVv(kh^LQSb{u0?i!3>vA>sg7_tnhURL z5?mBBsg7oT;iZQpsduT^olA_Jd^bjkHKL(oxt+7??%9J=4Id9XbpYbVyc^??Grb23 zG(n<^c6)oBW`^=Iye?bw*8ztNAdYYr;l=dXGQ6=ff&wd!c3r!RvQE<^8QXo&VWwtC zxU~3@oK4O7K0WjGd%D%~9}de%JDmZK-*!VHBxg=4io^6yv)LO|S?eV8{&X@tDV5uB zQLhxSfeMj;Sx4K$XyyDv(yyBvp7e7`A+0+N!KkhB7q4l_q81dx>kc8pR+)C17L4kV zdmbfA)tPj|8!sA`igdr;p#G;8pv$hLDJYcTbD&~Sde`a?gO{Bv?Ng@ko7{bEDH2#F zwxqY7_6Y;&DS<(80aTe+t=<4#FIK2OsnLnB!Vo~9HLU@dW zKHu_arBc3zS~7jhT)J`qZ&A|8MRHT3GgiDt0;lbDMYeAg{5o|ufZ!65NqHs4Hn7Gs zqOHAj7?zxme_xh`{&G-+_{#$B#?^WNzpLzA*s9oI$B(Gc-O7PDd_?V!t%C!wMR6dM zuHG?nWy0pyoL2axX#j?R_)hQ%{PlpmKFeM>$J>0Opmdz;;Q$LRr5M`iSjDz`o+s?x zHCK!nUlo-H+sO7QVd|(yP{{O=AL{#4V{Ea3)o#=`9}+Bl^WR@H>}M6NpAB5?EuW=( zTUaLIGa4S_(E+Ix&QC&ySA`F{9K>q`Z&R#$zo*v%=SJ3Lc(Nm z_li4H>xBP_q4CC2&*K*K_$g``dtujjDMIL(QB^QYPp9b`mpMcOQ(-rpLp%0)>`pCw zqVtibhB~34af?+;_9fef*=%&IoGi&?vbKt!x z!a$Q<(7Kl@qti_`SKK>u)H{z;1ojyBCj|mmXQ!Tze|j!V(GSCFz5c-%Nz3Gc+f1mp z=uV+NDYiNQNsj;ta|Qi5dfIOQ(nCmBmY3FucXKeYpIBqa05-oC+-vKMC=Ab=)tu2mq;}HvvH1UV}oCVU9wE zMD<%PcT(V>{Zpvb+1Cr+U;c z&xqcty}ktH?z5;#b-bnd18L~?<6Z7L!od>Xk!p_oCP$IRh)>OyRW`xNzVDNVo~c)# z=)&8b9Hd`T)p9uIun5z>>3y7UiTa+jaU@%k-)xm(|LD3$^7oWq=7JB~MD9xNhDA7h zVonqQM4Nt6CaR^roIU?F19S3!wO1{WBHp8CYU<^zNo$uhLJ4tk0H6qx#@dfp|cCqqvF6A+^1~ zLTOHJ<~0BM?-6ThT@rfn-S*?c^phWv`isB$FmKX(u5by4X0!=qar#I@O4&)2aZ`je zB<5MZJ!z;s{5T&b<5EbO92@2-5x(_MK2%>O)NZ0FY+2FKSm?+o-{_3)rJ38!UpH3^ z#}Ib2f=5OhW!i2AE2A37=-pj<5Md0AUQ%ZP6)yC%XnI`Rr*%)WvzWYf*s94OwJ9ebD(MIXtnvCF|ts`FD6{7(mlBY_A-o^CiR` zmnMRtk?*JhqtZjJS;LULj6mF1M!oK^5}RHN@20KLxM&@1D8WI>3Xz}HG4e<-tbjEDIxP4tB?BdueX5 z_n|C#a0`OHaL8aEs(@Xiby(7)=;n`pwHi)V6L?ibS9C>XHr@7iG@a+>Mtf}b=5V2y zdb;?BnBIl9Xa=eGr#XpMfk^Vb$3YJhlba7EcBS8XezR=v!15>&=l|6gRNoVPDd{!< z^S!Y0nu)(2S1}Tr^uk{P-Rqr=wy36p8LZGWblX8n<)%q|zMnk_L%s9!Nlh`|vbW7# zZn4_GmFV}&KvLn_bIszHs~h+u!4ek|aVBH4n>Ji?t5({()+um6C-d&gYtUl1x+$th zySU52Yaj$XX1@AzUi={;_REJZh-b;s`QX0pSVWz{b2!gHi`&7yXLnxDCpIDP&v%KZ zriO1gpH;`K?q|pbr9e?J=VzZ6Tc#If`S)$TB%P}(R}Uq;cwz=?je2f-oSB81uf`qz zl4K6ZX~i9U^RBzDb*`-X22bme8VoCL|0E4aI9^sVA4xN4+6s6Cr7HH)%BtF!IpP8- z-#P1ujZspsP};pnZ)^J{zc7xJz#|b-zZE@xA=Ys#`*f#d2`wKozG63LhYOLH4TAk1 zOa>Wm6FTn#@~b{1-xsu(^ai?Ue0LQX6F?oQ=%{zswVZ5~cW(Wd@mo4M+=DNh!s$S8XHGMC0JjAiOP?4##CJ*}Gb7@o1ggV_)Y zjJ1;G!(Z>3nBt+hv!yKBdVQb}tg=u92Xb5D(Jo8txJeC{TO&=`A1-7Bo6tkFZ_uIu z`agK#WTwi+9PjOk!z4Cl@hd!U1GN(KmQiB1S>ST;Yk=kCG%_roAu=w+>`WsTJY$pG zsEeF8^OTi|r>i1{#$NBkM>3w@<9Lajci40Gnv45tGV~dEx5dn- zyy5k)7{LJmb#utKb8Sm!_Dm)KrAw-vO>n@GL@L6P&{eG%r>N>E0KFz!O=4tc zydPDI$zCpDKU;ljBt(T5=*^Ih>J8>{`wb@@$#c>`taUU+r7WBbk)arqC`w16BqTQQ6J^=cIr?!J&70rBo*~+f6BY^Vx?4LJ0!0H+laQn!`HUo zohaqsYp!Y^FsXIpe9V{%uUPT-lZroHKz`vZW!_Fn!XZKuLm1d8W`b2)rRUSx z@RW7FX{2t{t@X;ZvL;Do94E5bS}199P?!!gm1I5kw|4JuERu~TK5CGlas{A%@tc7XfEYrfW~!^jTkxC3vl+)?C@0C&dA zs6J)WC$!OiGY1oK9zc-e)|NH(IMLptUGJSAIE#*@OBPGchAFyzIiuXFfLHhSYExDu!NSk|I}C~k`}O>KV1#xqZ2^w zs;6ACekVzu`m<-l?6_?azCPh5?%aS;urYu+v{Bk_R7lC$|AVi_Zh8B?!O}#+fPP36 zeN=&0|G3OTjG19h81-MlCJr!D_sT*ckl$6PZaER+?PuQgG@iY&%D?ziTKXFsqIAlp zK5V)S0yzIrvS>27H` zE#&fe_6Zx0MP*|P6})Y_^R@e6&(T%&`i|?X&78PzCdqbiXGQxla3O2iHu39Up7ifm zG=SG&HWh&7Oq;3H zaNzcdYBfES_O+ysuZ_RUeSOWmVxhAT9!aT>0ZsxFtrR{<6vKNr6arwRl~O60@~?1^ z6Y>0LqZl-f_UU^#vvKucF9~x8Se^FRKxTdo{v-s|#mUY8P|GEDpAL(MDmj)=5Bp>* zg^$g2&pupK%$GdPi=}+?j4y~8i|{bQxY2NDm#TI_7SyyKcGxhl3aeQ-q=a{O!ud(X zklP>RoM((RBrrk)i)uI=anPeZ8_$DiA#P&PJCThf`l{TN&ueq_wOHVK-XV>ZIgbS@ z!&@X$+=nxT0!uhnjk2*eVD$S>#7ixddC!_yNn*n@8>xU*LlKe&!)9Il^_3Y-y zI$p^vz9(d_R!g4c^;M4kR=QQmQS2q#e@mP^vs%SURiX5Vm{?0>-0B}BUfCPGpxzIG%H?^Z-|k{9lA^0C zTFjZcnuav4ij#n2BaVTm7Zdo@Wu!oZ-}-EI_&m|1RE81}SP=TE=kSo@;fk)6D&Qi_rt~|ln;5)h z2`0z(!<3Dc2`|%9R9Oxakm;vX-L#pKqw4%Uev~i3&d3-D0weO>E~oT}RFe7z#*qCP z^;$#MN!{zR0sDXnew%6@S0Ki{&euh>5-Bxy7HWA-%rH6aU zz9&iR5eI-7kAQ7LTB$;*y*C9h{WYfr3yMAAt7@C?1`@HWxLHLpTf%0@ ztDnWv{4f{8I#NePgjW|%lW7Hf5yfJ?baXSxm)fCuOL+W3d__+Q2(l5hzj7Uj)v{Ud z7>b27z9=^9?@O#A?NtzpGF+=OI z^J)BE`7X-I>iESuHO)WsOKmB}&Zh*XW%!3>Cb9e92a3sok!15XsWMO| zgOn?T5NbBmyIQ(>eYdrt*ve0jRQy%(0A^U-ty@b_eSTXFW0*fJ*OYfyq&{CfZOU8| z4H;=Y3(+i+Cr_2^u`2=!pl+<@H zic4-&XKVBfD%YSSJzGJp`X1}`) z%i?-c$L(HZxP%=YtQB0Eyn3i*w^$-m6)}S8^%k?|6J*bz`y;AH%8ltpC2LWRK9VZt zNs3_3Y*|EhgiysMSmJZU0agl+yne2I(ULza_(gO-*Yz-f+V}uR`=iY)ohQvlPE?>J zvo`Y0&<;kxafw}7n;Q^DCeKoYcuc=;=9qOfh#AzqQgf6@cPtw!&%Y*y}NmSUmh%C z7`Z_d#5O9%5DZ?bpfJ$|c9m7ZfjnTlcX1EpD$IgvppyS2u|T+yv-Mi}f$#DMGxT>A zHsmfb<=rgbv1X5ei<_Icf%#E$6a{e%b9WR2aTIf36#KSPF0o)a1ZeKQ?RLe%k`)Hd z1$J%DG7FIOtjI`u_{_tBRbagsb*>v zJM79>O81Mb!uC5LZ`T){c-eYTq}5rdo0$(Ts==kYHGda`+H-mh^q%SeQAyQ*zjOMZ z&S-v5k5XGmzS>;TvJnQ%ZFFZtey&_9`c{?fDPI}r$PKq=1uV9J1+&yxiy2S$ET2?c zQYkYPc)zh^=2qufXTpaPx2Hf@W%UmU>4~O~NZy;`uJl06Q^G%@Ow;coCeQVlU67WF zm)UvGeg?$+h=<%c7rJGKTy-eqsiOn{O%NU z`SW9DSl~>v%$NRye4^3x^OiVZExTVr12c{UTqLX$sJca>AaJ`CIyZ)ZE-hGka=v_v z?xb;PVe|sY!iJ-^%<(%NFBJn_hxc(`7*L-31?rBDL^n-hL9ddZtqhqJRRIzk-T4(n zFrz$CDH#r1>C$xL)R0!X}|Jd()hC87kOKR^$HyH13@`>Tyx5Syj z@^B|Oqp8?eL=e|;5pzqT7>(he@a)O+Kqv0WMjv2vQf3XL=99DXyHdN4Sny?4xxY2yJ4;3tXdC@t+ znj2&NzFQ7Baf8)nfn1?dp+@;~<_x#07jXqA-I?0ve~QEaPV4h_hI7LZ$G7C)s)Uiq z5rDWwlW0DOR22YmfIj$#V$EF?)3e2rKuCu2{J|=D8k$xAJQrbZF?aRn!u{*s{xI~H zZ^e~!+z^t&6+(mWAIkoZ$oQAdI- zs7nyK?z-Tq`P$^fBuJy55RN_qy;$x_qW%UfQW%)2FQ<6ay?1A4z&9kb71QgDHcl<; zJSbyWfjjHs)d3QD+H`L_we>;Up{waASEAoHAuuu&XMdAckN8|zAOix%qMp)>9CB`# zjhiG%J+obj{6$(Vlr?(PkGMKTTdlZI>QnU7;XE~rRT~~2p3!pNMO{!$2Aaz$Mf=qJX~PA0(W#J}(}M}Oi$(O`x3*6;%eD>w zQVFY9jRnT=b*WP=s9#-@dRAr>M-rH(7|!nhnS1n3tJ+c|`4mBpa>Uso7_(WS;&Z*nFu7=8z7uH!laF%a)8RD4cy6eQL@_w{zX4JP+enHjf7% z0R$|}qR5w${2b_6rF1}3AP|_(MQd#O8)QqqFMp^rFo}!O*zr%p2-xtq&50~2G!B=J zSkW+x{S9*NTYO(n4lUq6{MKL3l*_+ea%WV0+Z&G71c!D(Ao9l8{|(_8a!GGj67YN^ z`n+Na;`zK;uKw4YJU@K<_BEH@_|-dI9J zDt1$P_l1~mAJ(E#zDuPc5@Yqnc(j&)207Bszwl$(dDn*%WsRJdSB!LN5LP+u!%gmW z&9YA8f1^ygIJfmw3zVe6Nu6zQy7PE}XpkDsXA|9sW4I!@=A%uB0aXrl+OaJ~tuoq= zsJBmNP0h?e9LpoGT}i^|MyWmgo2hj3P9W8zKT|d@#4P*n!+vP*M@6$^ZWA_1=0IJ_ zJWq!If8P9Q4`gVLjzJZB452ZQF2ec6gMtKuEg21OwAbYV#X-w1b+SF;kH4Yb}R_m#N=VWr{ z=x)%wi6%#bZNt--^lGG2YeLO507y;*9b#CSjlG(J?Q^s)pf#f-pU@2-ZVEF8yx$Z% z;4-79n5ZLh-Hm22z4g(Hp$`Dt5;c%z&=?bmYM(b{m=OS9~|U#UNUjO z`T}&WuP0fxb0KJ-d-?u$9TULGQ*6eTm0=#h|F19!|IACNovfhQg)q_tW-yLK`(a6d z%?Mflt5K^=f!jG1EF6Viq6 zAjE=Q&>;x0L{*<}z!7aasb7Tg}*x3RIU&IE}5HHr-lbv)|0J!J!n^xp=#8&$^ z1RUK!u8cd_*ixpzVt~Ye2|2g_h`cqNw@A(Y9Qi14Uzgz1U$NR(B-4KcFBs>b;WvYZ z-$P@9?!UZwwKW(%-FZym6TM*WN&J%05&M5&)F8Ib=KxxbuJQ)WvHZa}`3bM)C)~eW z`S5ziKS3-Tjrvv3-x?wC2D2NQ&)jc0fd^*JFhQx?LKtEQ*ND4+J9SNeVSvF);X7Tj&^Jz#MDK@Z z7B!&vkr3kU_oMnjXg7bsRTOMdD^~2f`mJ_PL8rbus|V3q>cX*KLYX zvs#7+)mfyb%GhY;Q7LwO>AN6dc3x#Vp9%5mfI|cH3m28nJMyTg7ofI#N6&vnXb2oo z$gy1A)HP0e?w;QSXMrsBbs@$1PRWIIZj zIQ$jZ>60?6f}frjyo-bA-u+CyZ(-DN6P{X+Zqq?@i(r%60m0dwhbyB*rOWe{O|8aP z&%>)_v@SLzfXDeo1qkz@WZ*+@dWbZ%(M&hpP7Zuj(}Cwq2XZM&%xMYdwwk*oW;QX1Tjv(#* z5bpo4lq(O1dTsyTA=%1Fh@y0)2#wTXlqK0(RI-c=W(cV)S<2E_h9o8vA-hSm*q6Z| zjOAn~OQ?gBH3^-jF*LTBG3NJ->-^s9eXr|%ulJAlpSiB@eCL|yxu5TIfA0IazxU?@ zrb?)W*a1LXMOOjznbH1M_5dyT?!0p&xT8uBcanQ)<&E&GD;rS@Zh0zzV&KpwdzJy( zh5(81>B2fwxmEei_GUi&6VP#@#&0o}vNq|BqJTy4375R0NN{+vN%)~UJSp_?G+=*w z{vdZuuFTIZ4^b`FY=oUVWg7}gJ-dN%=noua&={}qRRLneQi_Yk(q(n3dCGMDT8Y*Q z-vErHu(gFkM4V?f{?M5Hw|t~;k?|TZTu~c-M)jor zlIE;Zs-9+*y1E2u`L&#r zjbW`7>9;8LINMz?d)H|>!8l9?vs0zr!+q4%%^0l|Bf1o^BDbTeV;0)Kmp7dtXwocw2`Cb3%N)5|*0@alQ z9X>X;5MRyiO_2*dmoRyI3NGMg${P`X9=9t*#t?d|hVdvV#X4%`<32qB?s(^^-68eY z9v2!IJB$iV+(@+r6j*wk3IENn(u;{aS_wC*b7^9QBY%LCOYtMpQPJ}WuWI-HxaHl~ zyimKaQKmMQ3aPLD_I=31&R^m5p7w9*Fo&qg&>?-{_B7sQ;N+WpomafKqcBHWP;2pJ z5;LgP)KxA}@3X0DxjV=_*rAo9YaIc4pIzwqkP0nX{z>#neaO!J|3NtFf57U0L2>Kl zTYh{%<3Hr@E!a9=Tg63(#;~;fjq)=DY~2~tyql+d+wAGb<=~hk(jF4#nA^Clr+!(( zI9?lr7LO!%Uq5{O*4s1Zc<3$%19^#j@PuBn9)x8Ow~=|X#Ft$Uf)iOsp7n79j@B|f zAE^PzL;2*1OT&XGoJ@Zc!>WhrsGo1n;xD3`%Xg>DuZFQB6=6^<*eQR}h(?|o)Tb}S zTrQM;*S$#b-wL(*+cNewVZyL5Xu89|exr`zNd*I-JQ}4`kMfc_N;tnlf&G8Hz(1?q zy9U#acL=AfEeW)o_vVxjWw>q|dYZbc)~|*XpIC`*oETIT56aBY|+)% z~acQp`P_-KOzeA|JM+Kig-ydBr-_6`LkIT2SMM$E++WrJsIt{`V zAFCHW;wY8=*13rV23DcS{2SA<$eQA5jfPxP^6_h=vMr4}i$iMm1kTaH?v$x^- zW5hEqEK_`~)Jj&|HpUz)!_!U9ZVW1H`4G|3iRaz@qHj{UeiPZ8>?h}aAd<)- z3FeRTM0#-hQOw-fmlb3;Gg3x`=^XEO&f9Gv)RWz4<=IoU=o_W)*mr+%O3*ypv@SR` zJxGRBH8k1-htaX&oRz%hW=!8J?w|Caud}nRYYS5$Jnm}DvJ~EraUxBQOz+J`Xab~% z0S=O7!a9v;Q(bCL`h5k+R;lOng_?xVzgn-rP#i>ME6P=1C;ujM!VR*wcg+N}P5A*< zsu)8(XY=1o$g4oFz+od}AD=!ed&YfZ`F&B6K)O^CUxiEXpv^|(!z;rAeC8=&V*xfN zR!9EHKO#rWs?4SORRs%zc|zXACb3sniCGbRsaN^fko&vfXYO6ND~sQ;BA=5dR7gPQ zXMgAWrI~=&eEkF@GNxNRJ7#4t_3wGq0r`S%OJTqb%fM-w&N(x3_8j&*@u}%hvJjD^l@P>KsQbyRhQuCSDuSrt187QnM2w@GcJw_Z!vT7JK;1vNss|vrO&PS4`y<5pM74l)}pJXOba%~e#02$PE(6AKR zt7j3;xX5WlzRSSvU+>zVCS#Ug=2Sx<7$kL`lco!~oM$s#jj;XiZ=h~cqzFNJR>1rZ z$R=aBP$@><;YAVRePz#JW(@^Gc1v`%u(w?~vd$hR3$ZU>&21m8vahdo#-j zl6W$((GE33>DrwnQZlKJC#8Q~Gbo_!%t02>_C9yLIJ6vIx$fxGC`DKiOG|3^;#cyt zBi$(m?z&NkA9zIbWjbn#5=1Sum%4dDby0bQ9*J!GGE0w~rrFSkJx7TyH8kqkFM||* z2}+6fk-JG@uQL4qTGD^2Uw_~lgIjplpI_g3#2UHpdH-L;4*y-u0xyI4Y5onKOk14c zz2%|}xxwrDJC9(Ll#X3-5^4qKhbOxcU!Q}hG9^#sC;Q+^-g-+RgZA24Vty%%`QG|X zeA3w-O#OQuaNx*ZIWe$U?4!fbkZ}mHzdMGH+ld@I4uKPdFx0fRbgw>I)Xc|`-Yo*Y z=>&;-bEj8&pYd$-vqSuGl_cDH37>`W5d8E>CiN_5fgPyLrN>- z?wfn~B+11+TKn)i^QL&E@@yFW=G!-525)cER}BZe2>Y}Gt^XOlO)TjA7@-AHC1@cu z*PJSu2m+H&Cel|uvOa{&>T8dnj?;GiYGyQBRF~Ri(=m%%Bo;-mgqymj)?e4TaNw~s z%wOUX5r63Zb!4D}sA|=YCXguaXq8WqjYloHi)aHTm|da7Zo}m(QD=;6}far20d=^WsXd*o?e3TgR0EI1NIBzx%=0cgHlK%@KE&ui7+>MzWSM7v{#POZWMq+tk8Ibmm2F?=1V2zI+a_k4^6|E(BNwAfh+~vzX>@bH|Tc1LL#EcxFo`ul0w3e7KNE~mH zqMj}urF(`OO%0#nHNzgC5G^EbEf_BNW}vr2Z$Pyzkmrd{8Ie1y4IK)&f10TK@@FyI zIfq!Ec8S zqEsDW=_TZ&RT{SCLxfvGr9Zq2tU{Sz`h4E=Qr%E`qkm;mSTC7|%WNr01LOz;%9s%C ze{#1myG%yHW>VNWJmtg&aseKLZz|rfzQQxopC!hr-l~~|iOlyNVdYq13D-K2E%D!d zzQwX$Ftf4}3;Xn_Qi`z4$u|)oZT6>bjZ7x+_~jebs6Y9Tba00}sMzIiaTNq`^~A`V lU-aG?w;ePvSP5{q<}ic`w-c`>fT9<4&g_zDxv|T={{v)u%}xLS literal 0 HcmV?d00001 diff --git a/icons/items/shields/transforming.dmi b/icons/items/shields/transforming.dmi new file mode 100644 index 0000000000000000000000000000000000000000..56d1329a34866ee38c08682ec3bb9cfd8f1e9fbf GIT binary patch literal 7584 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ΝdlZ0*vPWOq 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 z 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 diff --git a/icons/mob/items/righthand_melee.dmi b/icons/mob/items/righthand_melee.dmi index 9609132d0a7b2fa74468f0fc9699ae2aa260b0c4..1cfcd5f622c5f677652858b824db674b1afdd3f1 100644 GIT binary patch literal 46536 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 z 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 literal 75923 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 diff --git a/icons/obj/weapons.dmi b/icons/obj/weapons.dmi index 1d4e812f670817423f1165e2d3f438784abea680..cda3aaf191cc0bc746c3dd31d5f05fb8a172c8c1 100644 GIT binary patch literal 69684 zcmYhibyQUC_da|O1nCaxl2GXeVMtM0I;FcqkQ$H<>F)0CZV;627Le|)p?-&FeZK2` z|2WJr>%`vo?(4ozu!5W>zzgB$;}s$R0LIDl+yL;>*G*a7N!-}c z(81i!$=uck0KTWDCCl3`@}qT*4i~W2muPXy!s?}2FiFUr=h}Tlq@8jl2n2(}6KtO_ z%yIIR`Ve6tI`))=us4Ybza(Urn3UW~seFC}(q0V^GD@7~hZlvNcb})1(IBY~bKe z;zvCiBlNh}d;IW1j+uV@R>C)-5sG#i2>2V?k^C;l-%v-QR;jVPYW(41`aD#AWrZ*zWM`}^|BL8;Zcs{7qkD)zT8sQpDQ243{dP0%793l>>3*MD9A=3P`O8G&~6^(R2_GoY_1MeXW&0u4a9w5xm9ku!tCAV@)Jt5@H1ft2DwOlp3nY_!(o#ugo{9 zbxEjAKQSa)NJWZZ^p7-}bu=6SBZtNjPdJ3lHo~t+kl0XgHiMZUKL-@#q;R8behdm+ z53GuBlnhk+NJbe)i~K&o%(69#=+x*ikM(sR77q;ZRgrBcmp^xCD^|PQJA;uePu3s6hGX}?mD1XBXie7$h&L976pl6y>1)W5gFg|}{hs&qlkyK+8 zf0NFM2$9e0Nyaq0YF1P@2rrevMpf17FjvmZI+#Qth<)5(_(abbZ1h=DCxE&(TO@>V z`W2E~q;@o-)`>WdrULYyZo75ZFR9`)@%N#=`kWx`i`!`2=%L$GA*tV_cIlB3mf6;| z>GrtZgW-Q8!_jnHC=g_?kZqP3%reo{Dd_qgIp_K1NK7Yp4w2 z$!O5m!$Az8nrx}5`prK(iNFT9(57bayFHvGLj-=Dya%x zl>@>6yR0&AC1u9T5Uf8d2-FdQ47BvyXu24vezJoOp2OSu`y#J5mV!=o{M`ucs=w;K z_4N#G4)$F}(F|u|tw+GgNgZ?0u`vtGdh@C|DmNYBdh>O)1Ly4ku|VsZe+%nv`TZ9Ylz7>8vykWy z_E>hbixNqMKh|R;nIG-gi^;9-Tw5B&1oEKLQ$LKWbfXvTBjg{IHH2A_I(@YLICJ^W zI@npok2Tr0OYGgr8&@lF-OC6gzRS9es$h7Z%qaevXtwhmvhQ#1j#fM0-LF1veHMmreVZO=1GJl#6s*b|PQ4u$*UXlNk~N3QmR#?wVka z`&yTW+YxDRF-Vx+O~yb-pNLRTqk0=uGN zq4tYOcCoxZ84gw@_VGV|4piQKQPScZ9BL5lnQ(YlqlP5=V6OQ&;73sA`)b=B{=wS^ zwhxm4M-SSEL15ca$8q>-XlXk&$#q0<%^6~+Gr9*VzcmIA{vvOpCoCBj3%H-)FE<*6?3{5qTF^)E8iPSr9j+I6LYhZQPLumvvftsn zD_Eo`73lS7V1~f`q^MUV7Gh^KtOilyy=S_U#-y>-ipfF-_^4(MoGR2#BqsSs+-y;n zAy=U@Q6mNk2{364Yf zV00z=>iEwk(%6!RljMKG1_uZ8;NapKAGhBzJ6`Uo7@3;Jc-~!kdOhCX7BpDRSMODH zJQ5G&kyn`dMMq5A+qh+2F+za^p-qY@zq}rEl>`)3R`(f%^EISRzy{v*m2Ix_KZ*C%dx+%C6=u* zBm#bl`z-8P;B9SEZ7*>3c3cuXjA)36ii)nt3*BxURkXvTN)#9pf5*_r45#p9C^O(0 zCcM{(R@c;o5qQ%#O{@wk-*M@;#hb zcgO?1leWbjgx+4wRqgE$P3b>=$u%@gA7@~}-v7Gr=cF*$S8660%Fl_nwkx@rt(Yr*o=$p}hUmvsAusmzS5}60WY1O)WVE34~Kj z*$|vy1TK7h1}4TIL-Viq>#fB`F88N%j@o8ra}8YhLF?Mm($ZeM1e&xTrw5{FGSurV zrpK;Vyq*-z&5KNyo4?6;czh@-D?4nlUgVa;f$>HF^U=3_4h-KAD3@ULCKs+@9_pDa z&JQm&p7P6JzS|O5)@_fXUXLj}wglIs5uLEu^5E)ra`q%NTyif`i#lS(B0K~DemPfk z0uPniPk4IFJ3EDkN}mW^Ky2ydTfJdSfB`Rrp2BS>YhhN`>%qXu>Eh63#ghk@fi50X-`1ql%ea+j2Pu2O2aD^O>&s2sE)nUAlszvUBTvxMp+zii z5B@XTy3@`Vj5>roE-t(+jh&!py^TW=FM;dKEdNXH$7<(ng+Qe97~vT~9yvWDI$;#u zjFS{o5K9qn=HB*2erA(nL+SOv+1p6ha>(OcXW-um$Rn@qOsU31Zf>sh=g*&?6GTG; zPe^FUeyh8?fPGfiJXT^^5=F86rS+GLi0KOJqDOy!|G8>o>4u2{C+n}CyLtR~3V$o{qC@>9Mr&V>#?F^%}F(u5$bhNbtqW7VCu5KMwB^CS+43^piO&I{IN#rd_9Zj zW-2^5_|)Fcu9HyL;|oK=;Qq%YzHa;Z3FF)buVai+gKgRu;=`EJ4aS~A4NH~vk5l(f zOkh9;n$-bRplAB3=7@;pkfoA7p}Wlq@MdPgRVG*fOg|94{q#Fom!SAuZxs)Z*3njk zU4}js+ z*B2KTPqW?L-hMxTI;LNcMTCu~72wH3?<#@gd9I&O+YI<#wo6tt6f#C$vPkXoJ5i2)B~w}wSXtUSE&=T}qx?GGrMjy;>?8F>t~ zGwlh#u@ue86tr_Vc=N-n@XuqCk{I`XXY4K33sH<`j3|3iUG#B6M0bvtTN0F2z{Gjh z7v6CK9?4|e$Hx&|mNRzgkK>9@LkAo;2K|K3hub+#E>uMY-`V4o(Ak4Z?xCc(xYAIP z$ob$Et~dqo3T}#6C*iic6YF99z&cJ7#T%|xQ#n2^ORi|66-^S;^J!vn)N=rhbS<n<7zZEsPsnjuZQV|pCfmUnPZn$o`YW*|pGjZWTGwXhlx9*3X5 zWX=*^XUsgJzla4SKt-=++9N*0UtGz(G#o(y)57guNd;)4hF8qo!gZ<{`?6{Rj(08} zAKKinqh;l18{F=b=3Q37qt@*{<;_*9bN8={9%sL`r1??s*HOw+|L3v_m_QtnOdvFJ z4{&-{2ONpkdXF~~rsEc`&A6HeMr$*nu>OLVmp!Ma>sgAQQ_g&eSaFbvkQ+#tWHPS( zdTQr6d;!Wk2daa#+z$B8UdeovXxoQ_P}J~8>MxdF(fsjoMSCZXdYF=4yjEVHvrlK{ z=4}bH(cU9beBu2JwJULG-3|GZw!ql6UF2GtKe*Z5R|Y!g70{*$)ttX`T1>ivw$ z$XMB9D?e8#Z2t}`g*!W%l)pSS|(x-KpaL;7Z*o#z3MwKI9LL8 z+L9R|6xF`Tm}}X$P&Pdzd$Y4?91$YQC1vwA>fRg&9*BSltiuJTZTz;ae>1$LdAB)t z%ZGn8#5DWIw!4acY{v@en?J#X1^x`Hpi*mXn5CEMEX7;BSAQR`+(n=nwj&d7BtosBU@h^F%>#AZJ1^~UJvT8~>-IOg zIa&L@U)B)Bh|781Ls~gbk(I>%t+=peu&})m7EqZV*oFwiNxeWq(!pScQ_xcKMGTM0 z)Zp%@x+HbdiV*qI6i;hA6p+%wLGH;8Yx2~6~0e8JhVge!zaGELGGal zqAVA%1QiK&z`%lR0=?ZNyf|K=ux-0kEn!1s3utG+Q{uh4nO$n+5(qZgjrPO44 zJ2?17OQ|RH>cUn2y|L|}JF~Idopf=UwoayEiC2cuaqzh(;+3dx^(iLs-Q!9otR`iU zYVz2G%uDF;d*^&}$hxnERP34J^reuOKu&}yChsCgVcqc$+fRb4Hc0qU*`j-q8IM={vujW|7hn*1zI{%(~Qm7g~j)FI|IB195Gn3KeB ziTIkwe6PZP)>6loY|W29ufDiS&hazYd{E_hpaPjIhhL+km!gjs-Q=aCin;#WkxcU{ zs_}i7J`6*W+t|n{EyVV;vG;4nwo`#Cw|EiPf%G_AlF;yL1)IE%7~$aVicZa#RpKoA zp?yy~Ut8yu+2cNnO7{EGve{a5Z&1KzFGJ0HdX~PZsOZ(JSDi8PLZ8tB{}cX&vokRk z^^f8&@B#wpoW!3e4V$--y#a)rzU8y3l;*6@i6%aNFJcGL`jGNEOPNbAq zLhOGGubf=S8t)=U3O%EoW*xAv?U|K_#EhuC9y%S)ejrEp0{p1ZDTn&4$!uqUU~{N? zFaIr==j1Q@#!Y1vY;2ghAb8_ZpJ*dFWi1nkw26Yrh<;(<>~=(a&Qtu9t2i|?En-e> zp7PGQ^vS2`X8-a!q!8cv^_{Zpvrm#?Zw3B>4b^K2*1wPNLI#HI%aJ_&7&3Xvkj`NKec^p8254EF?^f{I3l6uY@3~V~!U|~^8 zJuu<{3aYBzRaMw;d3ek?iI-=H%>IZXcA8vO?>|ndsAm7^f~%8yLr>zIydGH5;rX)j zT|k6vieSvo6uwW{H(L&vAK!^!N+QXmaMYYG9=4Qf()5%5xVxHx(?gU?;(lMO{NU~z zPg$9Z@kY~!u+3W_t(JkYFz}a1z*o8Of#SD)l=P;DK7<4pw69y0cbc_cKR}OBdN?YP-%TI#+AHD@&?hA$CDq$r1td*P&FY3-bo6L(z#%CoD<9Dh zYYB%)T{%bDMkgw$A0h_0nHgC4uk+zdMl34#6j^9S~)xOgT3{0ssA zuMR;`k+rslqoT#=)NCK*_WX-mQy?gC?zEHGUgUdxRVrUIK&z5SO<-O@b2F>C!&>LlISic6a`ZtC?{SB*1=p&zs>=B;@VTLBIkzgJIs>z zMZT)i^?7xK2SrNWlFT#w2%W*8z@N@)Z^phN z_Q{<<}x*zZ@=s%f1~Ms+X3nRNTE zi>0?LRMLs(q`8qQ${cO`kW1k&X{Xr@&m$*^5v^9>bc%%NhlMpv!{6w`aM1~uNXh;_ zI<*vvEu>kzLIY6t`@%B6JjZTJg%F6cy#M|#9*rE|yDR5KrYbzogwz++y;m-}uVF~n zXbVt>J`CWyvEv80$S!}Azbudf%;~}yFMS0P*A)6xpVn2JjUR4t1g`#d1LJe>%PHPnFxyESC{>##ZZ1s{Y?}`yYp;ivj;&IVaNi-?HS^v5&n{L#pN_S7q30atR^r z_*?q;BaCHIL-d7$ni8Wk_Ok?H3h-0@&sYAo?RW^7nwpB2$7Yrp123gdsnzr*x~5cd zcK*Kpc!#V@x3hM0+1q7nm(s6*Ym{E{I z#g#3zvEvvz9DoRVLjOew0X$1oLGjb={V?LgVbtdhZeu}3_fJi+^+ceoA#A=i#)AYOnG+n-EBErp?H=L@ z*6^F~-UQAn_83wjL3tU@FWL=NG2dDx%1`HJ}$6}5Cc$v*&*9P&|u%??N4-Hw(f_o>NMiAk@h1Mi;OBO zT&uz9?fSN4?Yz{LrHWxmE*T704$Lw$#b@zL_|7X~7JeZ*Ll{+OVLU+<5tudvd(qWu z_uCL3Pq^Sg_NZ1XxoIgy*c0qhQwaDchF zIp9qt7N9o}NdeEd2E&%0|E_5^1xLNukHB#O2OO9i z+qVtxs+Zi^##uqF2(BAIV{`3L)xrcQ7q*V$QZlUTaSF(j!~;|qzF65}GS-?zY{FMv z-cmC&ho5uQjjyrPC0izOV-F(W6B70;E@~;rN;lnKe@m#9)`)NoOJ-Y_`dks{?WGaB9Q3Da9^b$vwwAqVWVndt+1Iy|TY;M}Nf z=w5Sk<8^qrzhF-yakbh&6A}1N{~4AHA5`-`^@pv{UIH1c#asL>g0-4!P1{n}y{XSQ zLfoVPgf0lge9wJB^`}oyPmR`pg&o^z*fT2EV>69Q=mNq&b>rq>9$18g zlFYj8KL-Xr$(i~znRAvg8+0X8qMY1RJyf7sbEn|M8pIp%zq!X^B-t!?#h4Lv}4yul2ZIYW*&%2lu1 z+}*ouBQft0Z!ze;PejUVF<(9c^bQG?QutaV?Ple=YawYK?Eb^$lu~MP#@ix1FcE)b z5!JUE7<}ho-Y|kB^~h~{qMP@_k@F`p`wQhK{Xp!7jaBXyONz$k#z|jbVBU=qH2OD0UDX43<>Iva9R^0jayP3l&^g;LiRDQZ&^c44CH(>k&0Dp^zafLC_0SDo5t(@U za-i7*zrpW9!idxB3z67)Ozkn}3o_D+t!;)LO8vtcSgu)?nf!J?yW53&eq$|BPXPL~ z}Jr3E}o8otEd+F_DlYA0A-YZd1|x zyo7vkfW>DrKzQrlg2@^&*@1l)tE86~iZ@M!1a1X`WFeP#a}`1QHr$TnuQJMX67u<6?V89bza6UmCeXW{*W;4A^7&Vm>825!-B;3*^7qnyl!&rK^A*x>e)-vuyiKh#NuLaK zm5GUolZzO6n(M8Ww2xlT8Lqd#pM>sXMQ}t!;8^gukdRPrWhEB)VFYdn{dH(coh)?F zIvIa7L658{Km{nbSJf)_8U33Tb0SVYaVql8w{*UigPBE4X`v7{?wES>iCx2B&Gqt$ z;cxLBw6<1Q(YifyZ?xWsc~MJXA7HEJ^gg=KMFYq`;sHu;X>Xb%`y>C<1@@)BPk{UK zO{1wR{(&(&D_Umxm+sj?{N!X!57-{<*^&U#Ypsc62GK-{ki(P5WSa16Kcqu}SOV_T zf8QWzdpbdZlKB*O>qGek+pX2_D-K;*s8c`NC9A#`KlxHFm{=!jTz*|n7uz=g}%UB<&vo;Z`C<^SU+Dnikm z{SnE)krjvTllvasZ$P2Kp%>C;W9L*}!}ky_JS=}r1vXxB_DpxU{hg;u*AHYtQzSw} z2Wdb0k7OQdb+I(qggi7vz~aJohKjOwOh2R$!)V~4!pHD4kFC%{Q=&F7_=SdW9SW9yK~GlZ9C@ zdbp{s{-7!fIWL&}_v;URPy_joPNV7281F&qHP=DHRb5;J03Tn4*W)`R_-E?U0nbQs z=Dp=hH(aOtOT8)o_R-%>dc9b-+MnLdV1BFPpeEw(~fmzY19@9mO@X# zPN$kL_5PeE*O+9XxScIoCh`LW;`PDO2M-eK^nY9caG=<$c0OE~ZDL4yLrYK3Vc~=A zj5ZR{&f-Cf9+!FrxF?|POM=Lcd9+O>pRrast{tz=UA?3{N zxqHJFa|(sh9ddYr3jp>s;jEaL;LM_ow;O~U0=Byieh798x8LYIm8_N;?R~nW#s@}5 zzLHZ==+YR`?o0*8qUIcsvdCZ==_m_3X!82&pd&{Q&Qcu<#+kRXAb=^!A$ z3kMc?H(d6rOf1yx{-|LJTXWT0_l&#ly+w+9_8;(o&A4 zt{e0!lB<#~h|?^KsTE-~OUWA}nG*OuuNR-tlvVZ|OG6Yq;gwt4G8(VDKYU>ITH-xB z9nVos&fsc<59k&_HBbhR5VdbDAQD=R7qN~j0 zI6+O7*b~jWZyzY*nENbJrYKT2AuA2}n%CWZyJWkqX9=6pL)|K9QPk2}JV?f@W}lXs z*p^&s|9iX7kwKr(p-Dx;eY`bytiX9~sjPPC|B=bJrVRzx_ULlR~Np_6=Ng9rN}V{SxS8?Xjr1FiJ7V& z7#P4K7o>VZ(a-g}dYc|~%E{aOTWqBj#W}_^IXO9ZxeUJZo05w5^Bb;0BHepA~D?k>ks^GlqPb zbgN5uCva*V)|cLrJlF|UhwUG)@~mA=tQ5d*(OWM+Hcg5Nz^)iI9Q75ldTMQ#*G>&> zV=3x=t3$ea8pvBOwwuUS&NZ=U7%WLxZw9opjJ3X?yr}!=8|}7i^>d}J`)Gy!LX{}~ zY`wuf@yt?<@^Bh+*5j|~k|*rj9D;^acb3?YY@VVmG&Vh*S4q`mx_I1bJK@`Hap9Ka zdr!;xM*DSvL?9)DbKhJI$x>g_KUf1`rI}cXZ)&9GfskxD`#!C0=NR?userLybE*CG z_)z7r>^5*r{9DU6sLU3*-VECNrvqEjOJQDMBc#6s4M_F8V|xhpm`Yh;1ID&{aiU#T z_~jrG9Z%)ZgXXrLMTkHXnugOFfT;bxH>su7K4#a~P>lu~|Nf!be*IW(RdEu}U}^L8V)1Lp&_}L+PuCEU%k7ag_u<9G7B6dSCT{BmBS$jhV~+9O{Z{&Jja7BI*d0HF89Mj7 zUv$^&a?KTZa=x#N%dMz@(DK`!AI(ofYI?=dZ?Z%MnVyZ7(h#K;UkWTUl z7dvF_-lW1AMs(TWXXauWJR4kcOSTX*^Dmt zXVYM4aX<`4TX8fq!kj7(!ee4y?{};pI`ZD?dpv!;MqlLb%&KSLGF11HzeY3SRX(t5 z!Inxqa~!pJ!!zOGzGw{Zd;3`=SG}Cl6eNj+Q@M#@{eoE$y**<17UNG91Rc`7_}FU@^g{>!E6*wm!5&x8s@yQOGzY5B`hYDF$31M}b0{CC~5v z*uvRt-H(1&E$-_g1A6FUq$BO+C1MMe-G13n^uuIfanV~r;h-Lpwuiek{FYWcl!PGb z54|(T7ODJE4*Iq6>Q+9z7G5ByB$fr2KE~Y_prBafge+SV{j{xqtGKOml3f2?yaGjg zPvB_(uRj-+KP{)6P8@vj*4FH(kc$_*cbGv!3`j?NpOuGPZ5Wvm9c`c#Wx1$19Qpzv zQocfK5>#l&4`52BnE4*Wg|1`eDOC90{-R^eW7c2HsP~K`uf5MaLCWNI4*%k0%%tzr zp=F=0Qy_T5(v9cP-lN0Kr8^;P`M|`+kDgA=9Nm#l2@jiF4+*?{Cx;q5_l1b<0x7Od z%z`EE1#puLOLO=aAaBdE8-``bt+HYJI;d;jdu zvXqP?looP~j5IHm+rGWMViF~*;d`L_f9K=p^z`&tTd2Jx{y%i}5WMt%JU6sKAO#xntT_yD#289s z6XV1kTfVgZvHbZnx0=gHVCRgDx^xI?2pj+s*0RwZpScvn!(o6PNDm1DZ&*}PlGDTd zf~NKs4!Gh6Zw4~g{GnTTA?TIv*EYo~=<{i@;_NlmC$RmB#igdi$yW-ZqA%dQgMzAp zf`~%D?axGm$VbRO*uml9COIM=yNV2WgGp?L zh||Y(2Xa!>C8}lArMIt%@MxW*es*ghnEh9bS3e=#z-ZoEakdJ!VH2qStgYoNU9OCO zuhXh*cWr8GJ32Kz4F~)RBasvp6QfEP3>MAWvD5hZT7YVwp&qIgWDIq1bi~HO@&+G$ zb91vJi$xKYdAQgR!{@lgdcZY%ET84{at@N7mzSrkD{NqBhzwXQH>*@S;tC9L(a;6x zUm_vC)c+{+b9K9aZchD7QkiN*sC6`#n;w;F1nj?kY{376j0;va+7ZXp+O2mLKN$i5>P1UIX{U*j1NIB0KY6LCex|bF11uiwOt7 z!NHxJp27j8+D$mx$(h%G-6P&>)&JC}HNytca@_O3?#I}GdcDXOcg2#f0;Pu|y zRxh=ANNT{i3-hD5qUU*KRKn7dwg@_ww3XF+WN`1aGm|oi?f%>BqvXJ;u@ z0a7@2PDn;uLtETfkrMr95VHcX-r)4s-Rg%SY0*<*Ypvk)ChoO^(C)7>1UUY4cz}>u zTUb~a0GEg_7@-Xmt#@z`HDJqhJX-<*P^a10+$If4RJzwtYQ_BPLI67#i zo-WvaGct${SUqxU$az@*7$1RxeI&2j1%qyN_C5s!ntF40XZSRNc@Yu(#Y>AF!eL-Q zlogX9X~_~zLy$<3>5H9NKZJ>D>7yu@|B4v--{d{*;AUBPu#JUuo$8?LgXUhu*BiwW zLpf_`29KvSmU|in_loI6?q>ib1Flkmumtr zs$3zdjCbM|Aq=aX}Xcut31R9&{I?Uz2X!&E~9XAWmnf@f*7h*~W0IE=XgoqVf z6oB{I1=*qaoG(IL!PEY-EKl$klG)$e8ZzjoPoGjMxHu_}Y({t!;^6_%!dWVZdZDlY zY;vUv!{O1*gBP)lh@y^y_yDWxOejHIM~;)rImcdS4uW@I6a_&Ejqo8C#G4sMSzA*k z0G%Yn&kvzH5c%K2y~2>8<<|CUmv31(-}e*Be950cCJBKhOKFyV*jOr`qbB+{SNsm? zcIYzZ1JYgR=9NWs^uIsmDV1ag*DMkJH2V>N@Z@2}_~>+oG-I)?FU(rs%mWZ6=O6nw zTOE4Y_v;rsaDg3O|MSz}z+h7?>5^bB#YogD{a2u3s-$b^i1 z_UUi>3@jm81s5KO%b^$803_SgH`HA=Sde9gCsM?NS@pxi#^lM>IyA@|^Ss8nM>9Gl zZSm^I2desZKC8Za-p96AAnz775MDLN)6W6`?R?Ozo|-PO;q2Ym-Zs3uIxIW$kZRS{ zPj}@Ylke3*m<8P&Ty^Y@dmWj(Z!F~u&i_IT=<^?%2~P)CB;>vc%{nNr{JN0+0>&Aw zXTO$sRC2IopGsn*6FlzDAtF#|R8L59t0&+kMomqf2>ROA&FMO=pkSMZ=sxIlK^O4~ zv>vsDfZdx0BB7&l(15;T1uVNOEgbtPJ*;Ov$ru;3o1mPVoI?;c0pF)!7YuO$Ia7L z^p0nLlhq%(k(&-UOPfs_AurwD(!-dk1292jgbe~yxZrc`i1wMiy^j$?4SG?m5 zNxcmXjZvJ!eN-036?=Y%!=_X%r;WDo@Tf$#&;`Li9r^l}M>_PewGApx{xcmwC0`bXu@Vs_NaAx3xOAGhL!ivShM8_?W@oFXuf5 z)Eay2dBGAd&iAdqTp$4-1O=JGq2l7=tbySLdg=p|;u3y_XFrL6F_%YVOCh75=mJR` zREN{1!-0{o&W`9Y3LnopctgQ-ryt`bB<}S`!8>3$&QbYOQNA%rdITaQB&%xxL3=H6e%l` zH{A(A>cL~Bx4}Pu;t8IeXT6Nd2%m8161z%fX{-Nk-sYq6Se@!|H4|hK!Lm|E$WpfL z^@FRJr5rj)nh`%v?%do`7fPzxEX780nM8!||MOi^;ZRGkK9%n(nC??LRI)2uiA&Tf z=m1vRzaOj?>%#~is?skTJswTb+)nH-jB0)MBO+D)X=`F23f}N)>*&A%5BDEr_A0cS z6gwpKC3lau0*7y_jM6gdSyTS=!voR-UM`6CKl+H(>3brYO(T?Se3XN>CusLqzcH>U zNZqZcUw?K?z0!^Dc;%&-XTRi3_i0pJ_O43-OpAYNlh2{q5NRZt?@h)VHVy(mB$Oy#@DZ>QVn^LXdPrkakHn2h7TTPjQ zSq3DKi+3PK772F7_YTu}esKSnr$WNVwBj?(@w+GD@>eQS2M6r_N|(In$8pHX+L=yk zE$TGk*N(CvOhTfq&e-^PVjZu*!+VZ$qZi)o>qw7b0hLN4Wl8NeWzgX#&%nX?mpe3G z+ISEDVrfBDx=tiBxQc8;*83xHolP{{L1*osIAkjAGD8ST&e-yZ&vrZwEGPUoHO~Qd zd}v^U!r5}twBI%dvu(endyT9|4sc&fep*O5@4-J9Q?sym8_%Tm3*=Q%Ck!GBp#Pn- zJd=9I$JFyIezi*TZexb$l}5?-e1Ciy%9g~4q9@8!Sum41PPzbSWy2EZ^vPRE%)aBWwsCy;&`=2@X-W@audD(YIa?HC@| zaLC59;}Bt^f&60CMjN-YMC*~jZVOp?16Hk7k6kOdKfgbc1FIIzDDa{OBGnY#iEM@d z(D4X+m-q3nH)lM0{S-M3V;VHD#sk+nbYlQXnvVw}j0!J49N~&4`^wwo z3nA4N53XiM9*BC{HKpdhGV=cS@87^-!wLpqXK#Q3gSs7!Cd`C)= z2ASTL&ydwd1SE<_%w8FLsLx3NaG3-+GCxB^yN0{HXvn&#hKDfS-R%!Pl<3>c z_wdL|_gxFvuc3%YqGSqy+ZgKY6_dpSmdwhJK`iHv8x(23iP?vijkZw%aQWDb65VTN z6&^;ij=|u}`82%YSvUwzi$3#h?hR0Gd-{alg@TC%0Gpo{V zu>!HVklU_G>{m}>4e?@9AwI}Eqq={?5@Gq&5P(wrq9E_K$!RxQHi^RUgiqk0(?x>F zp+x}WfcXWGtMit}Z7Fhb*`BLyckVcZRiAJA7!AhvQlc}w!A~%O=*M!E2UrGT<8}y& zht1qZSg94&7TP7O=9iLlBqI!N&t}3V6`wHZC_*6YtUF!(Yz||>U6*jH;=hcS2)Gfu zUhI%`Cr2DvK*5RQceBUz9XeHLzm zWR|QdH=gfVJBtl0yNeAKpf!X|Kg=x}SSh~sgAP&@DMV2KFrDS1+bo6Nz+p%IFmFDY z*FMWFF7T`!SX7}|x7cDGZw&TBMO;{g-gBFKgF9)z|NIeEP>2KrFdpFob7uyojSUhg zU{{Bz0590DdS4wcN@OWC26DX!xYMYr$(I>V4_}!Q^%LDq5e47PgUoGUsek!`jf{fg zqga%yn`#4CdV^g$oOdz>N*>49%vSo0^SxGs&6^pkPk-v#>w?PUcX`1_Gc02|zxoND z1Y#hs;JAeU149&=$xK{)3;rVjitdK|e?&2Gi2d*V$XFZ6ckmbcGsf90zvb2f>rI#x zK$2dTf?wb0XvEF!?f43ve5qz#2nZvvFSM7TjU~=q6Iu8)lOODj7<}_Nuo+bDaNkp6 zSoX)RG0}J+as2k}C6~y(o{cnSj+kBmT~KmXx$j&WMGGs@^l>`ukV;T6Ew`W`^r|fn zwyd>69KcN%4PF_AD&WE%Kv&2lrmUdQ3C5xlZZCW&0khRiItX4^ICmt$84Qm|+YV8e zBJ;=CpRbQt`9PP1nkM7ZEPKBumXyUQ6+vfb8sfp%0;9aW0R#jD;h%DLEA5`NTwE&3 zo|;~#d$vr3WH^b>4+8Ai>5%}1cL^~Gjt*FVe`Uy9_%zahfWn#s-{XKc zDkZ8>&&R*)Y?FR&4&l;W*C&WT5~ec~m7&3kVYiRxcE<0<+tY-U^nE9aKM{-cF-z28 zbA4E3RWy*&_9}FEP^MX1Ot7}nfK{%46!L@y__+9#wx^|$VNeLYPZlcc;JrL%7(sc7 z3(keda2_uPuavCJeG0Lw)1R|01D*_+IXzyiy6!A=G#0)BUvuQIF+blj$?*M6nn`F{ zZYVTY=chbf1OPma!~${12E(I{736y}?@&pO4wwV5*{7cUe(X$H*XZG@(;1qSl!TRo zYgNQji=25$*zl+gE&^`-x$GB)L`~GuHqc%leM1O+j@{EPlD z5_-Q6(}WrwIUis>AP~sEz2ZCEQQ~YLA0N5%JAB{x!+>D*tD=R{^Z+Be$A& zae!}0)j=iU%OJs(u~O^B!?nSumB=(668Xy2C(FwVhKoC&b?#i*jIgJ)VS2jOUpn9# zsj%;+9$Mq%DK2N7&WR__N;D|7={Gf<>LM(pChFjJZj}_>lZ*5@dTCWSKc|BLgbuK> z06d?hF9>ROaxWwE5%JLU9(+2~9jRQ~Ng;q4=Q?4?W`h}NNLaF-&D9sj3 zQ7X9X6+}aSPPTGbsC)~eMLOD=_|8f8{vQ9z%DXrD2VL?Jf$dILVIM0sjj<$Y4Vdww zUJG-3Arpiq$T?>lv}3#!`Qd|uzD#W%^1Ew=O>j-0)NOeOOgYT&6i`%|;^N9G3+>gbxk8t)Oci)QrB zzT-l)sm8k`70bGx(O2{&2(6c=#@ElLLVe7RK6ogA-2A+UWX|zj`;{XpkY!|52Va}d zyxyj!w)>4k*f-gP={4T(D?6?0BFSk{i6eED#>hhc1-}iDR9N#0;((?|7B1n5fZlZZ zwvs{n@p-1!SZgD8j;mM0<$aH164D+UgKK?Odi03Rp2`SYbwoKUck33dwzl>;3IJdO zv$kP>A5Pot?e6x$I9OR<=_0vFE#ZO7A332{x8Kpz(~E{a7_dsZCUzt@aq;vjXF@Qg zocVo|JLDHZ@2dF!#|4l`Lc~9fG_Mxswz?ZMmWs$@#y>(7k*2YPCIoQX^7wb}G7#Oe zQ6THeKT4w}ALYFZX3AV&r!a%_z@h`9KurB7(bxAVicGYY9>G+BN*X^#;t`+TP5M8` z^9V5_mM_1yQqK#{_6`qk8;2&`TkYGUUpD$9qw0r<54xqxc0gBqeqsO=^5S$uu|Pgxd*Tb^?RgX?pjr)Tav7Jba(=fq)0fI9 zEB|2TC_Suwj`iFjMwD2tkY8f)ymcX=sF$X`DrV zu3v4rH#TP*nLgRQS}lPaFRPIHYlO|=z#Ns%C!&JSXue)+k$HhFE4zgW5uuR#!6xS! zlhB(yAmvxYy_9)gmEQ~K$l-Hy7SC*Lt=iu+fLUb3{Aq!K<$j~U&6}cd^r>xPLg+go z6=&y?2mTtDC(tD&PZlNGqw$_QDg;M19ab6l{YI01@6|(9PeP^FUyc1j`)BHk(j9__ z?O9jeeH})VOF5D0P@tIGc5o+K?;6Ye8@9>hG%>B!t_7D)H;N~zEr#Qf$p|I9nbnoi zJ7THPmVB*0wfx)(7c#j^#LlFt(weeH%^Q6r48c2D%87k+f2_oV#j?9sD~t-4ODp~0 zf;e*(pg#fXIb9)5+4eZ|F{RXaO7Yap}x`TKVZ0Ko8|U%!3<_9!tF$pK>r>j0C2pLAJy`BAZefWSsf zlfiOW_Lnb#AAJIHkmjbQAHq1`27R~gHzMtA;lV>-lADwu9Ek-(Jh`pC{GAhw#ANc2 z_4UjCoCZIB5UM0UW6YhO=T*|9RPy+AiHvQ;XEoo3{@n*E5BetnW&j9d#ph$k8UkN6 z%8%bIMH0Zolyql9)uhH552B%?Yi(<53l#9L`rBO-`&|lVcy$d5HT>|{B zr)<=#vI4Ea01Kq|BwtddZ6X&JpZP0SRED1?T&p^J z0S4-wJ9qjf9r5^e$m_RL`_4_Ok$UCL6W3fy9s6@8&wZ}#XH%w1wk7C&Y}5SR+#S-N zj4%@F_&}*P0Yh4yx4iAH_s@x_=MLSxR=P$i7YP?xx9 z9r?WftL;hm(qf5$QGmuI(&p+FA*0~Ezn$DNGFO2&0?8Ng@Q)uqo;%#WdGqFpDI zBt(KeK`Aj|pK5iL!(@m3I4GNEVq_!~A>71m4mqA|tw6%pltQ%;4OWyUX*{__!wbo) z-**owC7H)9-#;PMzszyP?ITAMv`Bv!xfVv82G^C(~Y1^#R*cG7;Th zDz`*J6?_3DJB@d?)8Hy_rJzey@HgVX`~CB$;`8S>fLBElh?Cc5M^pbH&`_bB=~0!j zV!G-6j?W{%6i%_jvzGxqlr&$AMOLet(hu<5Z_qybjZS)x-VG5_gP$7MhjqJHiVdIH z+GMG?$;Tg@M`SoLg?xAT$UHiJG)Z^)>$?aLu?WfeSf|Q)X<`9W%bH|u76}7GN48=4 zk^X?x!waY0*8>TEqWPD*Q`ErkC2lL4e?CiS#N3a0eC}5j5C-f=KH?_QfY}?E8W8Lq zZ*w{c@6*W&J`@GC)fYX~iAo1A`AO(GIWW4OX2?ELAkY|71k_6RaK_`5C$gtk5CDRC>o^V8Q0 zt9u-T60lc?YI|zL5b2dOyw6y-@VXOEXij3kwajuuvZd!08T)M-A}R8tGu~G#zF=Sr zMx}L5#-uQvzoPH>HOOMB7XVfm!oXovq;}hnsR5!;q`%}j-s--2%`M1O6)C?F2r3k@ zAjL_Mf`yiRWQtcO^7ZxYzouleO5T5w`UhWJZSZUOf#tkaxKMX*P3gSPU(jbtyFaPB zzCccxNC7TT?~_Bf7oD`OA2hb#H4HB*y87q@fEp4T@6K+)tk%oLk9m9Fy?BBwibI)P z);OBX@BWPV53aD38kgK6GJ@_HzdHo90(rAQf90l@`vYg>G~!Lh=2QEZrmqB2O3&tRLojLlfy%vbE1#os=? z2Ozn5ol6sEN>xXyhBJKO*=`w4T9}^HVNhv*aAv@de zx_jqH7vFP7z2fR$YXNunD+G*8J%4Bi0>{2K@e^3B*YRwL-DT?sN!JZ%uSt#`KIb~Z zFue1*gm>}4L%k$ljl9+-WbE95s|HXQWW_^m<4o&2b-1mbznfrTD7%MXMig~}sQ5Fb z3z-8{R}Z(doN0Ky*!+gz>((R>s2Aa6ToVt}B)yhe;wrMWK2V1!<6W3G8VUmMl_q}N ze{mwLh`%cM_#0-pJz> z3T?pFDx_g$eF&Hu{cDeCb_A3r@#is%*Ll=_l#E&>C{Z{hBz|f+G9Nh-;}1$yeTdnG zyWZMQ3kb-!8bUrmyvqjA!3{PZY3U@BCO^YXE9W52%U^IgG~h^lm1%eBm4Ao(bTtO| zTaDLpI9K7yQkHYj<>rRhfe}wm206~C-WZ&NQ{LS?uMT<0%Y7@k;0ZQGyfd)x^5RYq zbN@PDl|o1hQRrn3H8r(VX&2=`3+_xx6n&r+|L0x!dJZ1Y@J__EcbO!!x~$~Rnju-&r9fI{g>54 zIge`=&yO~&7~hC{ODW=z2v23x%9rt9_zYAqajZn#6&Gnwl#eC?g_(Jtt#pnc14O2? zPjBxYXSPrx-xc;&Av56W4bEm$x|nu$Dv2-z!aPw-i9lH`#uOFM<`j^)@{(5Er{uE=^$1+MlnTm-yqvt0&S-U3P`29{a z`kMNo>MP9ly{;Z)egE{vEW5PvCE4rOgF$hEQ%HysL?ti)l>o>>>$1fIIIgy&0Z{z2 zA>4(pP1j2SaUXcasq>sh65>Hz!BAj+z5u8c$(dfyt@p>)%YBo@g5u*>xx0$Nc>&JO zqP1|0?8}!gV?KVw^YS{;oNi)c$0twh`qdqT56)@P5qXsf2EPNv=us=Y1LTjaSJS^y z&Tq{E<|rbu4J;cGeHR_lo|^_IuL9T)$1OzwSOKvB9tEKoMJtW+eGd-_Xwsx(hQ9t& z>|M-7M4^+0U;Ww(e?pguuh{*8D^;)Hb-oyfG%D&h(}Fje3k=I42nQ9(pG>_S%+cbO zl43L_I^B~R3@A*&ol6Ku5vp@QxfS55m*8ea#0%)hwzb`*XJGg-GsAv-d`x2{9cjr# z+_yC|Wf|UNfa%#Y z<<={I3V0T(YkoNTLMQ-U9`1fZjvcLN1T6iIzkcHeDLBpU(UB@(C*@wc6+O1B^2*;h zXbY|PQfPDQH2Kx5mkFPQ+%HTkFVg&{ITEZpqdxw4OM->@uCO!erD3t7KlSib?+OQz zeGQMD%lp2z9ywxEXv<=sutZePAbN`v>r! zC@p>EycQG`I2GKx6Fu*CheBMGU?sKp&B5#QIXpMZ^c6Sfo)w9m-W9i~+xgABnyI-2 zUsm}7w)LYqL@&YyUZR$E2Q7=?m~%YEOqxUB6B9%oCK2j!YO$qHx6otm_%NU2B{|GB z(;WqZ(AlCMf(qLaLDxn%J6##sg~CdEkHFG>I#yN+#jko6>eRl@uoL?qNcS}z)2B}p z9`DV^a~_mXaj2g)HZ**mUJ;TnB3zcmP8c8EM>?wv$cVbHszn;LYN@>p@XdoTvwxD? zN}Jd{-8RIDbdr%kNa|I+gv@E zpu6|(kwtfHNvV!aw5u0!^qcc>5jMp|RkmEeowrnUeP*o**05qhvZYyzp#%)uC=Gn3V92^{M8=3|J zTQK)k8W1r9Izv;F`VEKWOulr^Z3zkGQx{j)Ag!Glh&c>_JF^j6V1@VYJbc@3N#B~( z=V(~J6!hjaU{BxF&4?pYGD4xA01jG?95^#R0RaFXl)xVs5a~v;NJX9HIJ+FF{OU2! z8+8&edX5EoTnIJn<+JVr!?wn6#Nu9?G%Z?*o3B73_+xO84$uU?%#%D{V&yDk_p3pm z49|z@gD!v-o;4WQErgpvLHO;;F;B4-mQ}H5?`YtFG3NnJ)^gyGPSAGV-HVwj zwt;<#l2HPrv@G9~ZD(g^MW6#KdD-3BsYYrAvdvhO;ZDE^yjgm4xYAyKbE1+w`p!Nn z4)|weWaOQ+nmEfbufbEC_;k^~G?RJn1OAuo-NBEOAbmNsg$=cJ?&$iZr2B5mSf6u}V4+-&}i`S=NX3o>l(BNL!k<<$`K%UhiP?eiG2F90S zS(qyt8XD-o6PC_k3owCVXn-eLjux4cQd5s^fz|-sQA;W(kJ#v$mhX!Q%_wp9Xy72H z7R|2bf4cT_D+e!UmPRT#ayu%ff4bl4opEMoR$%C7;P6#CzSApz!U`^237dD5f?wdR zzbRX)y?wttjAMJWSjEmR?6>`>v;N&+)x=>2&Jid0_6l4G)!lEx^A4Wqrgx=LQGn9K z^=Xg+u7A{lfLko>;9yV-_7RVO_{M~o(KKxm z!lTX8)0f1=5LG^?Jgdd zXr5-e+0{JN&-^_|*Y@jD_5MLWrZ?bTU?#WU#`+h6xmR{!EEyS0lCP3>7li&_;|i5n zgMh}HfgEOqG~s%8bYZup&*ENt62s}$?QsD)VZW*lK0tCgu5Ma2yt`}DF7zx-@Y6HW zR{-D!*eo#v3H4XHNP&v=t*v?YjVmv=wzTb?sM!Kvl8}sTxI5<;-2*Ml%`XlsDxy*? zDss-tQ}%yE>9<`=0ynMuBF+_v;OK~Av-d=Orc?weHITz2##0ceTJtWrF->o!_W85m zArTK!D>elK)G%owJoRS{vEsQv>R=SdyExnwVWW)dTo`l2iF$rY1zJ5QwQGQcheYdL@Mj*@P9u<|F zr8TtnczppMTGiDF(u9=1X{n86YvisE5Jkzd5m(c|1D^`MU2&FX)wr*H=|5NhbNj^h z6PMDqbh6!8lhbY9Xst6HUYr1a9L|eOmCGGnY`Axi>w~8L% zTP0q4IwQy9W^7qR#6gK=>-pi@(63B-=g)Y$0Td=1Bqppp`vxB2|ia6oC8YMdEWz#<1mX$s z!Q9c0kLMntWV@Lk(E9V^^bT?*{jyKvjMt61Lw|qyZ}^&kP~QYY$cQg|H?DqmRJ=Kh zM1A7E7nz%%e<1JwH+1mWAN80_8smem**O^>=Vlk;zl`?0+--6*9oIb8C=XmOm6w%; z9#d0Q)yJfeOF21B4Q7&P65RjQrSf{TDD9Zz&Z?n;rSg0XIiur)t=Zc?6$j3~sEie$ zF@`_VPysyV8PusYYtNrHP%m)-_$p9DX{H~whB6TUo`ZcGl{;~DX7UN4sRy+2FUC@F zYwlw%``1BzJR$>w@?xGWMy%un(?^L2#ZMuPi@nkZDmVJrWMn+O4p-mAuD^8Op1PVi zHLb0qgD)pnuL*FhBR_G8%H43sX~)Vse0Q+O-FK^&VWkSQzFR!{smUL8OYUCTart#1 z*aWg+fq`QpMa1vl>T)6?BG)Od1_?+xS~wUOSZy!<3fI%q8$&N80;X`_G^jM8e$I2u zFAl8;S}+cl^W78>a9L=(L}6Gj>GFpUah=N9O{LBMWPYmF%@g)5r)yyr)QJ$+nzyOa zsDNI-Iejq4<%XbODQclT+|vKN`N!$)`n@-A%1)8zv)FS#=kto`pMg-SkZ`EhXwi*x z)GXZrho7j}?dyaem6W^-^hN3|TSJfVCPWD(*V4S)Zy&OHFEQK#A198U-O%i`15#(4 z-1+k+q4Z|o#t~3alFXt{GkGzB;Zae%R4h+ECMJ^bDECnZb`o%L)x197ZNhW*nX0+F z?gd}VZj*VE^A(7usi$1R!dNfT1H-5T$sl%i_S|GS{avi=YuAO^eOFL(BLNG4*=Z;C zte4#f@`T_QXLeIS`ml>gy(3e~7?=;upcm;3(E-REg=o;N-uTWQ9_>F$M1KTW?k~1c z_oS-|2LiN;`U@rADhya4e6H`F&kavBhrhnD;V}1{+U1mKsu;1rL2HOIRBnx)6rfX- zQ>tAfmNw-iEGjA*o9sJ#kIC6T*qhs(AM-rS70l7Pxa9mB|1rgvmp!Sx7Y~YxPM_Q- zwVm=v6uf{34j!++tidP6%)RBkSd67j?fIqDkt$Pmx`CGihGRcLvlF+P1Y8HYe-_g@tUn zgYzfanY7(!^5LUV(C7f(sPJmS0862 zkV*u@ZFFjybNzuv>K->O3Gc5a>ZY^hlm#KmwBqmB;n$*LoEwISMax}Fhf z^#hB(!xRc0IP|`2981KrLUKjy>tjP@0R>^Iju(%5j*tDAA~SEA`EFI#ySlnIk&}|1 zYTUisrVl8=iH1&}CV<|eufqK1Au++(26*f?u$buXK1OSp1^|!e&;uGZf7NVSsiB8y zvPWNKko71{6-Fq)&Alyoaj_0SYG!e*=2S^XFgfbbBGY;+^yyyG9Upg7Rn-u|3$dOa zTg*<++j-O{K9>vhYj?0R^>lYXx)Kl{atyohuKhtC2t0KG>8ZXBP;Z3s3mcdl${Xc1 zb@ekv0~4{`vG_OFV9y@aEpJzcm;%|1{uPmXV5&^1Dy=Lz)(AH%=G%~Tyo=0K`xuw>^Vtlw z222A4Z{a^I6pf6Si$X%Uw$60hTd1L|;E?ItN3gxSA@e)JgBc{d*uc8i5sY?s(&Kdb z!?1kf7mO=EoH5IffNi$*Y9OA6@$pXYX>oqO;00FvFYFd|s38e;OMm}mubJ0&rX}^h z9e0x_u-R9!1%~m&X+7*~Ha1Bc%TK+s>_;QVfBlMA6d-s!TxiT0A78UM7bwiY@bhpN zdigTgQcUKCLPOj`*0q~c3f|uQ+GTXha3B;iXz1+=L5!Sh1{J)H#_F&C>X=P##k7zBiQXZ)+LUBm)D#kN z>>{I?cZNzZhq_&-rrlBRE2jHSeEw)%VoKR=d0T-30nq|XVRmL1hs*BX@v-A!5*YbK zCbie@sI75c-$sLvK|qP-0y{BQ&F5A61mbNSy^#0oFQMi~BqVX7CPY`@SjFI37)^Uv zB5mVLObcdWpP;&xIuMH^rkD$CCSV9776Pq(>T5fVS_L8!HQ6x#;{q^Rj~;LRxpKSY zcv4-B3dX0I%87fwdGm%z-pBpZt_o;T2MEGJT7Cl^6*p-aemr&N3iA!fUxx4ki!>OA zh=}lNYipJ^$fvtg`i4io_B9QQjN7*CaJ-Z661|C*P*K;>S$MuZ8fwl8e_CvYiPSNJ z3`#715c8Y&4m7ZTH|v_PECo+uhn&FE7(sD{ZqKi4W$S)ChoKZ=p8CN#Y>`qqkB`j32$46c1v!oi^5$f#k}8S`Pv z0a`^g;C2E3k7!Wwy0zg~i^QBeP$^28n#?&kMbmnD(c9Qu-KlrRn=!3xE~;Y&L%DFA zD&p|eEp>ZtKKt3iPmM>*8Bn#H8Q?7zu+pj3y_cQ8zIsGlk&SzWKqPcwe-^6<28 zwyi7&QTyKkxVTCv#Ygvg+|-Li!Un-jmWx8_;jR9T7uW?gsShkt5X4Yaw3}btnf>)z zjFF-6UO(yIeS7O1dy@~hOCIpCUKkB%|GZ7u3YG(a>GDw`PUERy*sk7Q#NAdcMaAYa zpq{LbbFP=2wDf9uRnB*3Dor%y9Y^Mm`(%GYh}_&9jmQ`dypyBt|P8n(1@&^BY;4Gu4AUrcdwa%`hJ-_+xPuu%=+$R1=%ZLCfo)lchh>$_E;X^ ztXuC%1i5cbFo-l@s(Pl!uWC|J8PqsuWC=ij3I)jmgw|GnSTD8EJUqn^gd7aqrv&rX zljU9nGAR@R;QAY;XSDzuyRN;&#|v!N5IVjORif4$prk722u+hcu||h3dx@~@VFRo| z*K5 z7W;~C3pa2@+PlCsL-5>&DA#SJ$9Bid#Z~!%9z&h3ZpDjCK`wS5yvVfObenVSg)@HF zryNFqM>i<7G%DG)`b9z*AyCFYya$J0z+OPFw`*5?)Q={FEN#aGY(exD`xM@mSXrXm zEyn2D*4Mcvn)qV)=kR#ZxSmDVjCbZQf3D1XmS8Hn8%~9&M9Ngq!*5XE*$s5unQ0`1 z*ujPX_lDEJ?94?GzH9p?eG}?Nz-9~3zr1FL8~28u z#3Ha<{QNHNr}m+7@?D3SffN~Ui2`>=V=Y`^oGvSqMo2R!Z|3GfhcwbDvq@@5hp+@} z*3bx4j0ajSkQfuidy`ux;k(@TSRZE@(9Y_tUj943GrQPC!uzoKd!}0V9b+E@lu$(kwQqoaMv3Qg$pzwZ{t>;$4zuy$CI%nB-+Ar6+81Z>%SYn~{2T&^#!k4ETu5xf- z>vW4}S=GZhH2OGo&GbgC81CPZnMlyE+?3yCd+jxzV%ytR(6+>nYt+)Nbrg_o+#^+{ zmkFZo+mkcjw%e($#b11?69ht5Iyb#9-KC|3qQdcspF^WePHVGK!wZQoQ(hfu7`}P& zVRmTp^A_M?^|K@jo6_Ed!Qt@Y_RdZbCu5BQ6^D6}8u=_6e!}opYK*D9%45^CJ>`z& zp7zYU0M(Zw!C@9oRD?rVMd&0QGn)$&Mgj zQ0|s;$=^)u%_fc-q&3Re*wM!mAACXx{hZv`?s=yoFXhoAf^s+j?$@;xA|=u}6NU30MzspP{?ErkRA2tD0RV zqp>K!CptUjye9cnO+XPRma46-rRgPE&n?NzyDwj?_8}>h0|rBmJ}orL{#nB4jAB(O zXr&ILtgmw71EX4u9Lypwc|kG^HGh_{PugVM-toS8YD#(WErR^FfB?(1{tMT7?$(>` z*Lip(jV6i?J^T7>);x{Hq&Bmko#F+WHTtyQ4n~Z6pZ4?qYa=r*-7Jtb+uo_Ucq;T( z>;C<|M)s=@pf8|u00n;idhL;JKt<$>xmel2($5Fn-?$EU=Q6boNT^5PRB9R(dH95o zQ-OK$BrA&sm}ZKN0pDwWUQNb;+V_F!Sq3l($EJaK2ltol$~-_(a#IBK+NXLkvHf{6 z0Nw(>+_)6+pk`{e6mtg$J%L~PSqncCJfobAy872-d#*2sZcdEG*@r53yVUoj^6KC> zQ@`u{-_?ahmfrR~UU@TJf3k+}y?Mv;&x?VdABTmTE6qjBn`(A94aYJ6j_*9Ak-q{- zsHK_B`sclEeJ7M6g`y%!ElR%=?vkS=Hx2d#Y2s)=@4t&yi(C~BOWZFso0XT7^CiUd zJ3+`U#U&<&RYWkRQQ6p@d*a+QI4P;T zByODSEBlzSx1ce5v$3?#Y;1-LStK_{$*$g1a%uFD@md?OX?q5P$*DoGr@9s;5#&@W z%T~bdE{W8_QeAx-m8phRr_a6npZ?J1h}91?Tc51f0-T^NUJ8~_Hcd_M*P!C)FaRL|1H-ugW4&Rumr_vVRnafKOfXoz8`Q)v+|ej3Ja+j?LT*IlL%)qQ zFK5!`h)lIfHvDIMDct{oR;tf_&!ir=+f7!9VN%>+C*W0nxN$^mJE@52c6hspx0)=V ze}~=G^PZ>ct=K9jwUYphK)?3i=~|$c2!nEC|osT%lu?st>91@ z^(mItLnwr%7URzHM_y|mS~PjO>^|I1WOH%h9I(@PH|)cAXI~rq=cL|N!#7uzRrV<7 zu2-@^O=m~?Igiw$nu(g1LwC846A5rzj%e~CnV8{iaLrCNN)i|-P;A?+=s!8NSjwq3 zCgXBNtw|;wXs`!+x%dq9@v!<9 zaiD_#`A|UjR+(srltG?cM zuak1Va|pZhT9ktMHfg}t2&D=Mb;4F$!flW#(UI`(mD{*Ur&3WyM440M{|>9&vS<`4 z2r_V+Z1It6yU!kYYkyzZ$J@L4EISdf@~Y*qefeD!z345cv3f6&C|%tl|0jm8wZU*X zm6rW{q}Ka3gI1B+&dx6(#W>M*Ay^O&iLR{7tHH_096tsH8Rz-DO{N`prj!R=So^%_ zlXlCjNoR2grdSery7QwB^Ol&Xd`M~!bfu(Lx}P{GY68QD`qNL5wzc_*i;p$FY{I7**FgJaJv%?od zh!GDy-Lo3V`uHd4vBuIYFf%QKV|>OhM4UnT;MOXlUWu&T?e9N<9i`;^mAphs`;|;# zL330D0s`zYvBX~hzGEfI6qAq;3b^+fzzgK`(`zO4h_w{A^6ncBP-2WpOqy^cSD+z6)D31;4&6WyY6?;Kf|O?DcGl|U%ys`0)MI+0xH9FVnD0+Cbw$4<4+N zb5bTQyM04-K&=SV0pI=b=eq@b`=x%WZ&E_h&L-4H`1tsX^?rx-j80lYAv0iNb?!-MW=A zN*(9N((a^i1>p{gySRhRdIqE{|2x*Ymxl)rI2O=gM&YW#KYrkWZ|@=^J~=dLfZ|4x z?@7mKj6bM9bLGssM^Rf)DCB6_S%RK}BJnzJqd`|uTwJ6}`^##O0wW}6i91=|$KvD^x6ky>z7TBIj{+WdH&PzkU05f$P2|axG8Ts9VN)E=lyq zxH~CDjp>n?zpzCCyUnv_RUU)P{WG&@u(W}f(Q)-l(mxdkvvnQ2)mu&4N$&Oy?2kc^>okT zXc^2`xYtD$%Ap8SRa2t_(#vo2Z?j=HZ@&Ky13hxMF-lEEMFkvEv>RxzSHrlu>2{Dv zHblS$n?<>6S?1MZM{A)gegaP8IKB7^fH-&gPdtJMB0);f?WR_wbREI;}d&-fh! z*to!WC7}%%HBG*dOEpr0MD1aaplO|MVthPLFlDw%4DH1mg14X(X~1nu(RFESH9v$i zM?!N4y-0gnKky^Z_lu;av$&=jA>29wUK%UavQYmnfalI6HH_b;UVv?hRsoV13c%&t zj6vyUzIXs8h($R7=z??4Bn=D-iA&1Mq4ldf;e4_YZ=+e~?SLHYJ(0z~u7GbRjyMgo za=M&aXk#A#hPf?2bOi<<*8qxMT3Y(S4A(OtQ@K@mk)#}!xjMy?y+SClA|fn&5BQNe z9P{zm(99jb7jp4XV|89#w4L+3|24WF;u(%Ti8}vpcaN4K+o7!ZU6o`RH{MH>l&WTC z>_AYE=_Xwn)b~63a|^ZV2d>sN>dwLjXUS@1Qc4u(H^v{^#sqg&~FRw)<+|z38LWegcpj zOP;NzvVn*k*M-cqpI$~q^B6SZr~?0*V-`@3_q_YFglyeZeO1Ih=1+2mpjt(h z^VfRtbzno9FDHmV4EgVF!0ko#!+{Gs9waRz^W)IR%=LWDITdUZbREufQ{i7|NGzE~ zf@}UCS&l-K!w(bEuWzmk2xyFSJxe+V`&!VS{0qzmX>nCbwZ^1Snrsk=!~Rw8x-H$H zIvu^K{ZG%psf@pHU6+zdd;oF`TVbfnIqxypWu7$Pp>>saE6M7 z4O^5&IA^eBgq#{_NFA=9sph{RUawQD>2X&WaELS@V->t9QqVBOPX%iryAC_g&cE;2 z!SPexou)R7Y9V5Jl72Sy-8<6#aV#o;NzRr665T)>W+WUB{_f=#x6OTt1+m1C$?FWR z`#qd(br{wtros1NYhIwBxXEP6;CEwr0_M7SpRE}B6#?J65|l3A=R23SDi6PQi@f+o z6D63|u1PTT^z;~jg-n=}GjMTnQB+PrU`=Wi&eQO%?>}d8#M(PD7^o#tq<#8iJ?uX< zD~1`dGQw!r?OU$v$OcP{!p-nvsRw^+ZD+iF??h1OHzn?JO~I&~*xYX~WMPssw7n#; z1_QD=Tcb1W!IDI#cKtV$%=D2*8}U^$yti(}7MS-66JEu1^SQXWJ$z20vD5qO*Nc5O zl{$yRJ9|6Eat{FXc%I!d6>OcYOmmb(RnLF5<+FW&F9$I>d$E+Sq82Q!t>TgLQjQ(9 z1!qi+uUt?=gEoH9cKGbgG6y7^+`R<8>zw=FbD%)ai_hOr4WAWQu}^)#cCMg zy?k-n`MC!H!V|FI{g;A}Jr|K1W^3Fp7WUd)%!LhV20mAcfq)aD*5 zUWjHD5qx|b?M*_IkySw-SbCs8SrU<&WRIjZd(!3xA8Nodu;71^^LyStYod13hL44V zBO0J2muA=e)z6Iof(?zHmrXBhRfmjJncqZ842_I%o~03f9a?lQnn5w!Kn-Uk%<3nI@a{lZx?*H9ev^Dvw;Prts7^OK; z?CpTF>`}0?#6P~5Vvp2u6kGVM5!uA#vSB!oqhh0uE%6~2Wt#WJtF7r!7_{vjXL!YRL zW8`(b`%`D-_tV*iXRDoNdBem9&v?JV1}CkICIKu{2G^kT5b{4Ux8}s0K&ri-Aw890 zyRz0NatnWcQrt0;p@eq3M37d4%iXa~D&D`Z-CwhtzKDxM{95g|b{~N!T4PgGSOeP_ z(wE^^xb>l1g=nZ_Jvw%NCCmxQ^NHj2-Bq-?X<62|)`hEAvruEx5Pa~soC>z(fUSN_ zT96q^EpaO3o=W&!_EMr}oz7F{8goOyh>8&PSbw_+PTZpMgyXtz%9#TAe!Z&7^6g8= zwUEgRJ5)X&@0jtkNWgP!hZqW`_WyYkTff5_Bf?@f`FZC=fwK_dfnpSivRs!6dvTPL zQgnvs)(c!@A}hbWneD!0x0&_eTN<+cW%5KGwL`lyilH-Wf3WB{Amyean~Y!Xc=6~) zcrDpoomli=%#;Y&?{kY`aau1wQfieeD_OrDsFmv)ESpVd2|~n8T{6BjjE^EWP?K(Y z4{=i*&fjieuvc`C;MHVt98wFR0CxV2c!X6G)C+jHBhKj8cK>SNkm%;oULEz%h=?mv z>*)BRrm^#Lo%8NW^Y^2b3@)cHqESdahA$vxvSNa<7qolcw-TKW{LX==*0?szqz=eLlWMF2;}UtR{Ox$?iZnN-!iG6$E+JT?M67N|xo zL}>_93=fgmU+Q`mV%NIz-l1V)DRsh@XPStx56rw9uFiuaPTU+RMf%Le*N?er5)Df+A zK3;y~#*N2Qy=!BFA28M1Z$d9Mjri@%l(Z>~T|MQ_q~WB@l&n*S6wPLMpbo0h(a{{$ z0I(l!fk!l}jLv9d26y7`-a3qiHMX$6*fOYGVzyo+>f?|T&LSZ3<*Fi(b zl&=Kn983lyqIcJ~-%gzH%*sEPo20K_RNIkK=J17dw#AP(T!hZ7U3X*U!F1r&X26PEZAD`g_=Rd)GjGMeQMcMrO6_U~{<_d=hJYVlhTM~SMveYdA`6Sh zX?xXFKkmOLgPP~$wwv2E5_cz3oXFxizAR<%$q2x7&Ws!A@2OD(9hq%6^E8kvM1vIr zmkG(Vu8)>%7a4GIurii=OjK3#RM6!y0e6+4l0XZ*S**FJ#yjkK~x; zIthpXn@@&tT)605rgx{`!VK-D!*)qrqo?~_0n9-J|4n+j`z^zoaSn!EvADd4 z6KpP?)zFYSUaH>(_x~yTmD;egX9rF3!U=_V0LiAs^K4K}02kJ4d2_S>;{pJ*LtB;9 zsK%LV(7|HM?!(BWlR6Nbtc?`1%<{4R;ujI&184{-85ynYS0G+#m%UqvI^jqQIs9ty zko}0CC7}OiMKz}Q^wueQ!OC@gRhwdaRO?0MLSK&;D2d+fi6Ac9O-zt%lJux{oYs}1 z>hZzme*i9%t}AI?WiydfkHGX{SnbGGjD1Ogb~qtZ4!tXH+Q za4~ry)Qf12VvG9hfE0qOu0Ux$zptsMErvgRLq>a}fRJKx&fO&PeinWygA;(9LU zv3?ht1M-0uf1uc461RKYmn^&E@W!O|dTPyzw_8+_;n+g*dR z_B1V^$9E0@o3~q8S;ZtLUxI-9Z9A~=;F;x3z;$aGc2zL95F>W-s$-M-p4Ta6_9@Z| zByszhx~Iu4`Fwz00NUN(Zvi$n1$h|^ zBPeqruq8Wi-3`6L3_pH!_P$Sq$FBf$+Yf3EA|eNz3H-aNs{A9KPcK0C<14QEwY0PZ z7Xw!TLGvb7aBFbZyaone>3P*joNb;4m21Dt` z11I}zOyORa8J?B)S;jmG1e%&}t$pd%=o!8uaL{1*e)q;aV;D!X0g(>5dPP8UAl_ZH zsROPD`}!UOo)*u78R_SD6!r7uP+e*|ueg>K z;y6RLS&Iq}7Xv8w}wqPC>APQx|uT1D#IIL4AAf~0B3a81hr4aUYEETaWUp-Z~c@~DrtHNippD&wMcpb-%@N->Oj=rm*!|pwoS9PA8+&VGqRV$VoG2xn5$zV#s;6)*XLdZ@j*;Dy zibq8N5f2!rt>IeDn}I(U107h%Qv;upNsabB6_Y=Yx?4N%{rQm)Bf`eWo9xd~9f2ew3zJ3=qN_a{XU}9mpY!tBk88FZDPRDt9c_{`2 z20mUz4z1I@Ti#1M$YF-dALgt5qGAIvS-~*3#eM5js1wJjkN7MliN%{3I64#L8nOXl zSidWto&5@Z|4jPn^ySzMQ5}g(r&2>OfG3v^bvhWfcwc&2OOGa!Pt~!cl#P?~X7242 zK?mUCiy(F!PG3`Xxx8^a)3m0(!)Xv#rsYRY+MAo)MlwD@%b0!zmL+TtRoLVCww?2# zD#Qz2Kr!5bCGSc+Rg1%Jfpe_v6uyJn}KK=73v9P+1k)L zhcY2hS#_jWXJgaYp(*4;xru{EaV^6W#n#j@r8uXz=&l;M*poG7tw2=yGx>9J1-enR z)a%pqRhxchTGN^jO>}7zgv3TrJoZ3j3!aLLiLrKxjAEm@ARzGS2-c$y$0oh-_gnx* zI-r^rrUx5N@n7%RGnHwpNMP_8T5&c!0ux8FR$$^HadXo)=q6;tiCR)Zo2LGe^n_-n zL;dh2Ioksc6^6nJ^}L=l2y3uL?A_0+ zp1$a&#pzEbl)Zu)(kR*-mZ70AKGaWE)ZX6_p{|5FpcqfsF*B##LxR*kvx{^G<$p1h z8!$BbX0yKT=H>?}lZwgVVQ%W#-}JesAf3E%@Ymm5oDCaPNjxainE-%sF57&k zSJ~;;2oJ$LjOrEiJXy`uF%Or>d5{3JS6K{sjJKYLSJ<2y*}Q%}i#y@LH{2|+mvl{@ zhkE`$$wR~IjR!u9`|@K$g0<3OXbwis(H@I;<=*i@Tw$&L_KnR&&K^*g#ilc)hVVo9 z?ffF3PkA6Ry_ghNDV_6zd2xDvC8wR0!wKGBwcX^}Br5^8a75M@@PM`DJwBZUR9K0$ zm+OfHYK!N+e@DIpfk&>qyvl2t4Qh}#y>$^?2i@C_0K_`++uLp0?9pUL6F z53PF&=_gKsYz)}~tf!#stm4wr!`jsuO2O*d#dOhsUtOi{wQjN)eC>57+B~eeE$^z@ z5S9Ivx$H-H>uI_-)3d!-H@*YXE?{&|+iV_i_KB^I^HzvA0EQKb8RwU5?kJZ_Vq#K; zPe0X&&FDml=>)+70i*qGah3woFTr7&rM&@ExLkho)5i-BsH^k2H+9umr)Y^~-)Rl^ zRl>1 zitwrD&aqyz)AN0bO`o<~O=>NcU3Z`+h~uv!_epX1uDat7#jj!3@edSMe>@B_JQ_K_ zLxHaigeP_zrgzu8B0s-#blTE~YXVWJL6c9R0UtbAk>B+bL_|d;P_JphFtV>B`4cvU zTIM*bh8-Y_XjqxD zz-KzCBaRwO!hi)`Ub&~+1INfmr(bU{#}gI@k8cl&)=eubQn9b6UL0dhPRwGDL<8PB z$Vz@XB^ZY&`gZ0*p@!zDT7YI_fF~a{1ojm=C_EHQrz3#PSsxl4EcR&uxO^-)V=31Bp+{OzZ!>vryA`T)A-4so4a8iZ+6O6^IshwKK zb^0IE6R;xP*oGchTHfjG>PpGb2s$iZ9S$uJI*9i8OYH4qmD)hd;X%+-o9X$J>R?|# z3epD;n=p?auCj!LP~ z*ETs+k`j6dy{E&1z0MXSDFD(19pEtx+9kQdXuvY}36>&yWgI1SAa-e^O?J(_3gL|% ztVT6`cPT+2vFZJgkt%=0U3mWHhZZc_j2L@6fD1+h+gNF_=yj}K_u$}M!^f|g?A4}j~>>xBVb3zqod zZt)I?g^ugUWMVZ}E{{P2ME}%VEeG7M5J1~0I!Hh^R}kFuCj2dMC#MX^4JHH8sLkN8 zWr)98Tm#fKZd}t*@?idp)+gX}$%8%5`R?Rdc~+mz4`F>v7wYRZ0hPqS;Gl3_FCCb) z;DgQj$QuBHXhiD@6ZV{jq}A|-90)q7YG;WVQYo32H9)7v9HK_Bp z%4X1L$>|>4gL+|1I`r(_;eY%q79drSvh^FtcLZW^8HaK2?A){InvSX|B61wS+V+l) znCo36@x8Wg-TQx7Akgi}Dl7Lt;6mwp*3pG{Jr-1UvDT5Wfw@tQP$fHfUbVrs-f!vy zx8@)k0EtDhP3te^(IjVg%8V4AiZH4_X6r!dTFd*md{om%qSvp~r8+%%lKVoYySw`a zVCs|+wE_}PY#;3u%7XyFbc`iOeia%FEN~KGrT^h--36n#Lxe6ld4E>BA=`yN_-4Pi zHzVD(jHEn4Pzp>ThI!-ZgqM!oT9nu2?@xtw7%1st6HWE~O_M-|?l{JpL9fm7a)+P( zG&~`KB>?DGF8~e?;qfzniqx<Coebd;Y=B%aa_x#HR-C-Cs^WbM|24|`oBFWpe@NJL7K zBy`TKBR0zt){v4Y2+6{OVWUQwlCx*BT%3Wgd!TFvoC7XksuH4V{9xo(0F|&$4e_!^F zeqBxVEsN2g_&S=R$7qYF)e=hhp`y{rHH>V_SuCucA1{=7qH*DE-i!Lqb9uOxM>sP_ z$_Cb=1w7@($Y2%vPB!(_oh0mW!j$4%uY*$qAEl8K+EG#Rp!4|*yb97hl{Emu%0frKI zI2MKO!ioJWI$#dxl_>gPOkW@fQA2lQ5x7?SmBng zwx&0w95~3}P?Emnn~?q9lqlViCsC0ewww+dxxnq|==c=-Qi6Zp!rZ@@Yl^qKPVRfEVD<7FV&mdc_<;90*znTGkzSkS z!JutSzCB6(JOwe`NJ9+gYypA)*mx%z)gYqO!psz#k?Oy5u{g9Ve)r_bT zfa8m(wF9c%+g0gcZfz7Q{+t#;y2AG>>9DaIaBF%=ph5y00mN@!XQus6*=mj!V+JUf zUCsHh?hLTI9ToWO23!CFMrp>$=BB2(J^5GvG}&_AW6uVY?c)vEfAvW&Kc0r1`v|id zJGy0IEhWg%3izoE{Rl*WjbKLf>VJzen|DWjBvAT4UAU6p2pnM|AM~>+=Ktr*zW-GY zTW_fxd2S2Q^+}SrQ%-$p`w_=o@@T=l1@2P1=N7M7G|cX{FDfj&g~f(UAEBWOP3-sj zPC-jE^Bw1rA3Ine*{0p((=Ve$l&6YzJ)pK^iXj^7b1aC#2j*JnC_rt3E zU`5TW^F`5}@D!5XqlMH{k3Fxm)F--}$z!YFzwEBq8cE+0Z3nxiZ)}`?uc7ZA9Qdss z?ZNGDK81m4P)siVeIvJK%*cZ>`htR`$)f`RDdy}?82Z8t*%VE;8iD+xeA!mz5~^ z)X*S%oTz@H8Pn9uc{`g7)kHp}AZI$M)*9TTIwoTHZB*A%ZUQEdAx);Dmv412a<*#N z*6RsL9MAP9dfd4)Lb|qLr>dEhNQ$KaQ9EY$kmopCS}Ain!B9Eg6ZWaGtma(LfddqM znZAF8rI2{H@)O1=AUmD?{Bup+LyXbK>`enu4O`rh_|tmR;OtdGkI^d?P`7wK@h;$| zg4eyL1U1X6obwDUl?6d* zZ*}X|wB6u>{^rOp{YQl-6h0GAtiP@O&E3D;4)5{OaUCL1dAWu+~!Oz&nt+A2N^Axk$H7jAEH5&e;Cd21zQl$>odMtQfs zCymekzOZJ#JFtuViuJub!x`(Y3PZzv8BeKZaGtpcs@$$}A{4LN!TRWTuIOpzFl_vZ zYcrj6!tK}cOcsltQ0&a$KcrHWj71k&c~8Q%L(yDKK;H(}>Uogbn_k$4 zKce*}_zvA1wCHV1euFUg52s?^9L$9EFYn9Z@kChi&hs}j(~Ef^r<9`m#~CKMD(qu) z8g>5>sJruwVz5MXx6B0S5gg$`ta5mV-y^kK@lyc2aj1=W=ENW(m6USVj&O2>z+J~9 z@TKf_i0hO>oEC@gDA0v7+vfwnA+Z zFt0uKv|K*7G>1%|#^LgoY8HLd_dGO3X~p673~4WnGgR*=|C)K*UvfHri`s|e#bHMJCrpMrFIy;xZ`gAJEod$Gb`TJmACz)ZwU}q>>1~!$XbJF)M?*=X`o<<#Rz z-s`#4GOOdmF~EYmpT<9O-hPx}AirZk{KShI_IPUL#+S>f4p#GYrqAK$*kYZ|N)iBi zbeK!Dy?(jBiUdZW<4^LhGS&BavfsQ97!ipJger=M3Zy<+XZ*ps7|g5W%k$Tcz5*3u zJ>2sY`Jx7UXwW-xS@mu@uh;Uk#w% zIu)sKz>}Q(@pq%uQWmPg<;sB$(!4$pzIrsIazg5EFA|q1D#s%dYjg4GFy(0igFd{Q z@}x*G0TTyXZ(H32Xop45>3vxf z@{FJbP&j07^j{12BUkrh6 zx2f+N&Nz^AQZ~QNex3oE82$G8_KCPQSe=zXmK$;j^bSq#jrq1`@`@d}LS)6>?a7L0V+5>Tl*`xn#03F`1WDef7tWYnNH>GBQ-}-s^Z+7;iIsV@J?HkQ3b!EW=Zm2&C*jHO0LKu=N z?gAg3UCiV+wqZ2kTn+-(Xu;V)>pLaR0{`PI{0-Rf(sG8VOX&-1t^BnBaQKroyDeQR#_`y4-gXM)gChzON(eC!Gzo~lde`*1Uk!mJ7@7a95od9zgYWs;W zH4pb@UvD0OQ9?uU#oYU2{(8W5@zcXWLRYXb*1#BESx)+28EViJb`tJB!E)1l`4YDe zn4&+ID!hJ2d^s+ms}|{$Z~05xd)KQl2xFH>j`$-fdEFvq>oW7#HSyGQFJr-gNJCd! z+>B_pbbWw&&nj5X=$Im0rZY8e&lis=lgrGw-d$ zrP6}SIz;Oc`7wje4e<8V}50QiYn-N@H{cJi7j6WlYhjtPC967a2* z@lByLH(yc>&RH5&Ev_HGM9aib7k{%o!&6?l`Rku%%d%};v6;U4#)H>xZFgqNimv*Q zGb1x6)SA3ea|Og5UppZY_vOg58J}a&cm?A05a-@&vA0A6LsUHp>)S!<^K;(vm|G`S zBb2auaqkSr2ny*7n(K_wj$_WWj?8fNW^eTIw=KSQr9DQzOVPh5b{pNZ?>`^F$<>mK z;bHB9?}KJSOdV2FRTq+z6I2~?ZdF%&9GQ(But|+Fbs%2(S!%wre6KV3XSe)d6EChq zZl5;i!Hv7DG>?*6aEG>v%^6+YGSvH-vNDS}pDlxsc|U2l#ms$V-+pF(ynuK7piLon zuR2Ak>$Zdr+_HI;`B}*q;Gkd^gK7UU8|D<(DiVlQG2O5I$yuLza(LsWv;ZXI`Ri;e zDY{GFAB)M|##!#L#ZDet5-0sN&%Z03T>K@aPu!LC-E`@0xcrB>$F1IK>^7RO`_bB@_S@{efrG6KwsXw7r)N4vJ~l}(R(TJR8QC$|W)+=b`$NCBJon&!omN(aRxowlhT6rP}x zSw3^H)dqWmb*jbECHA6**4I3_2w5P6PXeYPZY0mQYTA&!*z$wmXq-6_VR^m&v>ThW zpNI59ua8EozAr8QaQfD*Tmbev(ef^mWakE<0f|YJULstcL;COJ$s}6Y%|6WTZ@B2K zubnLO*da$@5q9V01ZS)WF7%}44k6BKEm{h75O;<=KW>eD;{bX z$-eB);2}%)rP!!8CBc3oN81G9pH5HvCArN;t$ES4n{~g1J9Ok-Fip(knG^vI_NYNS z)gaozieLGS#+wA(@=U01m(Cj|?t!8VK8o@^%dN*^+ho`QY3GviIr+gtsxRqRpI1Uk zABg=|li^;XlO|jJj+;eJ$imz@`wy}S8E4O=+|H}J0RLbnipMBA&tX=F&XB`9qqtPz z@v!;>zw@=*%+IT!b5}i@3eh|M8ylfN3aVjy+>e^)vAS0_2-To_O1(6%))MfORp=5v z&557ZPrK}&_(_Wpvu$`8cBfilV_VGiwNp+1)WihWihVF|ETMiKMT6l)FT78f3;wSA z#M$_oF#Shy`JtPZ_e{qci`Cyn!&b8b;m`IS%i)JP>whJPHt-*t#AZf{!cY9H-L_>x zbY;RKklYPL)zYblbo?b$SMrI;T|dR{wu~QzO*hY1z8b{D-oD+kqOIKoXsE+4%9J z+ft0t$W3Z1^SP*&1fxWJ?QwLYU0-GV;4GrjNBUCv#{T@9Z4DvJu^Bo1RdLMCx%<|$ z-$aG>yy!j2zw_y?6cHb^*%=a2)E+ZBtgJ>X)Ym7IN1XXCLH&z?(tha)H1uj`Q3+<&-IgWE__7l+s9xJmPUPbJRh@JT6b~(adhU+u z`t#T~F5_|2quC`>PRaZ4lURKiYF#(!K>eaoEG%k-U9^`|pY$KRFUFfTKiganziq;U zbnrS!4k%Kayy0)(Ea5M{|I+5vKLx(g9tGRJXnF0=fr&%!XVrlc?5hNQ%Z8>7>MJm0 zdIBaUGsIzk{mPxgv6WU>0MW@B;;W z1qzwx?$P$)(PAh4Q02AF5!~DRR$q-JMW7ePl`o#3tPI$wMQV0B71gSHb+G8yyKd%1 z4ZcEyd#65+`ic|Mj1r=w6!H2^xRxsp-K4!1F=nYKi#~`Kn_HEoP(dnVyN{7O3}eF+ zJ79bbXY`%MFMe~K#-zs3LsfPU=`Iqny*$rt#_PK-KS3%qxQ_--G^P!gMND0VP5vlF z?wWo%>`PzMtuSZLsa7(?wWscNDK`r2FzHr{9EfAZXkzuq+B9PNv?niak-AMu3JWHgRtR@H|uWx)I&)-pP_0VqxZPlJ`=$joJM;CoK`sy`OQ39cs zaWl_P5Y(mNOks3@vk1&@`}NM>_#_4r{C-soJXiw#jeR@+_Ny(P|6K2~<97Zd_Zpg6 z;a?v_usTL+-c%SQnyx@xy0<%T_0R3m{_thPCjrueHf?ncK&!rjII&ToC%wfdM=%^X z!;rdPOOF!9>JERAQJ5FP3kr_5h|$>Bds1$3VH0aAqvtC&wa^*8Gvtk-)m6X@9(G~y z{Y|FW#KhKT4LL-z^054_5LYja58j?%60BJZUROpL{kb$X`jHcZ*=>K^%6XitW&kPLW2~)hcxh#liCa zpiiB846+$rLFj%AAv$wn;4KCL@kjT(FG<%(R?g^{d|YJNSEju97x@B3PC&{ilk*E` zED-ObVe@pW&&~)^tQsG*vka4t?iYhDUWf>TjuXPd!(Fw=51_{*Y4;NA&YV5L;>To? zDR}qleX8gi&fpNjaHeg~ChCNliCa^DW~5>wA0MG(QQIIbl887(atcGgKf7qMYF4BO zynp~+R0x8C{-2N_5%9@^L-f;m^!U^B&i2Jf;h?_Qlh8cK&C+LjY;L+`v5r!U zLH}xlvs0y-y7<8(OrkM;5^Bk}MBv?cuDQ@Ia@Sla(`41TJn0u1dsZney4-OJ6JCL} zTdil!_fixl%~w*1Drt3RAYX>}fs?q`6Cy~XK>l+?izJ;}^|IQm|1G5)_q$K`bEBWs z*PGfxCW#o;@sz2AF&kdu!!4;!7Hc%~r5G49a`t6XJi;Z4Ii%~mj)oJuLZd>MB%iGjo)pKt1_Y&HaXZNIkmTFyz@wA!F+ zxPILW*VFQ2KD6}@zxpIC_0tIk3A$^hw9&YSI;@2Ock91u16t#wqNtGN%;>b*qoQFG z+Zy?B$e+`xcz->zd)O~)2qrb6+}yDx4Xwe@+b$}<52+w^U@fPDz)LeMlzFNrWwccz+#?s{F{J-2^2w4`rLzeKRW;>=tlHvNta! z^?`p7k)ALu1@C1`L2|6@`Yfkxc&hxc>HWhXwa}gM0(c05tS-!?!`Xd(vL*Vq*mcM4o}7;#!)k$NCWP;ZN<|7>3!!IVW6Pf3Dhh`f2&cAZ zi?^Gz4uq0mJ2ZKgcflK$l({ROGpEHCUXh6BQBWcjNo}K zt!Se*bm70%2~B`mj=j6*CR2qf!#aQU z15L>s=(%z=vXd}CH9)+hZ6!f}Jn!7B1#}JNm;ZQGho)ZDWXlWlL`V)sL#2VHXHvUscns1&pGfe(kP%rt^S^iJqdduL_(+689PcLG zRrqUu=Gqb7s(rb4T$b=D48)$^3?f3O9ZYn)r*Ql$MshARTz%eelyi zTjE+BS>E}z+ZD^ZwCVGw;iPKDygOOYQx;jsdMN%>cW{;9X- z>3ZUULm30 zwp+@q^Y7woZWo=w?Tw!R2IKEtT}%W50XWAWg9^+lBR>&0=Zv}M`8R1TTWynHDQMYE zVJ(*k2Ctq_7a`AjZNJiCU_~zakez%%BXRx)xLfP%lb+&QU5IPLo=*8(?u^=lG%R@} zJc4L4^EDQhYSh?hKG(*Ex%rPbzM}7*ot1wYy&gqMe;5^|L|!ez_7W#7 zC7aWVP88Mx=DV{nTPd(jR(${C-q2?fB0Qip=f2&7L0mENTWR?tzd1*pJ9sT%L!}S> zEDD{^`ED8PT)Fl2_Uo$=%SCk}7UNqNtRGUDf*3)Eky2q4Y1bHfI`Qi2Ww@~1E`v-w z%ncb@RcWkx;;1DQl7J^%9EfK6Q{_Sp6??22fRS|FiWJbAe?Xry83&d@mo>KLJzlqpiM#UqEjO`@mnVV_zQEIe1#H# zcS1c6R`!UYes(KEQ9;od;f0%CS<#u>rfM*G<#HUQr=d?STg+P2r45Us7<}_~laITm zwMmP;xUENH4?t|TG9fFe-N}&Fz=*HRioSk!J44Q=&T>W!R!i{DS`l=X@9pbr`{uEF z-e`z>f|x3Dt0+m3uhw+_5_J2xz`uprgY*E@zJ1lnD-Y+A1R-}iM9nRC-0L_kf#8Ij zk5II;AEJdAnV4cQ7?7pswF2Vpjb-=m1Dw*QPT6YX79w8Y7M^}@LyI|-TrZwtLRriL zir?2^VT#58rO`vcG0i20Wddmw!*cBA8JJ_Ka>>z;W@>&&qrj~OcEZgp=m{WSxpe|? zUG`rbGO8sMuz{|)K}SUxx*Q;l@9p%ALkF_Lz;6;`5RSbXDqgsUB070OIfyIAWf+uz zNYm))Q=hhb^ovtJE?Qbzn$HI+U_6$dQTZRspA^LhY@9%*Jr5QDya7iK1B}61p$tI1 z|2$UA93|C{bROVo?V~SqbdjTnoIyTg|JfqF;As9+-m$T9%i0#|OZi=Y`{-)6 z+a{9|aWsfcT5wEWBglP$#@pL(-16aJrg)c)>hzbLx%X)F&4qiy1BzcKGKG75d=q!3 zW9?_wcIm!iE6q^@opCWnnILL(h@T574sUc|Vok4rAocuflK7pL%6EhEl1pVLfgR#) z@tkICaM*dK>a9b|l%RTJ5tmsyGS{nyNx`ZuX{o1gbeYb&&BD?x{R0P?sLB;7)R|ksFPBb*@8Xs<@fxtOe zg0_~H3>nsM84$m%K`1z{B?gc0(>eOk3|>WHJVtEUDujVzWd@1)(2xWgF*-PpQQ$iu z=vfO>E#PVR%n93+tAn_{!(C%QHu#qI!gId@cfbJyQbKi~p}`*txb7$(JDn{G-Y!48 z#o@gX!;V*)S3}lH-nt!isk52de3P)a^}A+YCXzQL>l00INQe$-eL4tu$Mb`L5kSeI zrKM#hcr7a{YXJ1b6N2RV3n0VIdue%LOfXFcp|d!e-wDaZ_ULWj%=xEp`>qERY&|>? z@Cz?_zJV8U{gScTNoTcqF`6a#z~I)lG93p82J&w#OgSTRN&LBU-{o3D;>?floPhnIVZ2aav_kCKn7nYR ztcHe0&g5RgOr>AW>3)ym5=J&YzUKur4n5fNHQ7I-duM9tzNc(D2x~c!zdoefeEN&( zySCeHd>PyKSm`K(u(E|Tr(2IY@&RnM1IV{uCer;_v zyt*4}S(N0id=5m*Nu^wI5CqTn3)hDYtqO&2Qo4;XMidaR&f^isB``e=5$ zO|CBe+5dJgz@9VcR@WyPB@6)NMe`4m)AfaKbPShrJdG?mp6*R$Px}@w!fI%>)aE&XLMI#yi~dfQ!aWs&H9usv_RVCr?>AO= zD6gRG1jJgZqj}wEh2ERNWR)i(Q`P6aT~sEJeYiVxrRzSXgl#UQ=^B|bhDXTWH8@^X zQ{52E(HP9((0LoINr7}MU$zyW(LbN&Ii_ACR=k4$B(*@*|D3TvnOuw0r|$nk^Ypd; zhg}X64`g9p!{=Eii+~~^!~FYM9eh@$J&%tBf-Z{m@G9l^IQ5OkA)S3y>m5fEX?BPE zS(yAVJ57TlydRK(b&yGP6RTtUuc|AEH9yw?En5XuN?fggx10hwlMe0ZA7shB`SiyN zs3ArOZL{{xV~9MRbn&hCu+huOdY0~UC@=Y+bHnn_sDH&Ku9@F6;ED(!a#f|qMr3!9 zsG#~D#PHK~L)7L`ULP|3YKP2jF z;VN%8M(YDM5c-<_SXghSl5GJ^hwu^@%aGe9(&l6O+~NKullCmkIpnGl}|_W8u%o*9%`_5<&U7ODCUoVI8-f zQl}c++5S>Q9ma{iGwd|H%C0>8mYX=1?+Lnm(d_~Y+tkUH=UPos7cl>M>s9@V`frg@ zyF?xf+FKIv$#Oqh@2kTNy)0Hb9QuMBvn<5}R=OnzVyu>*4 z{M2kO!aFx?{WkyGzEj3+ML#%BbEeCEFeq-HmE!QjcHOW)S}rWUy}SwbC1|R|sEyTE zg~sVt&eUvN)O+JTKKybX<_%nfcCJ4wP~KS+u!8Z_U1jy#X=o&?r@fSVGjpBM^~cQW z^`M}jDL`Ej4_W>FU2ZvH zKJrQ_fA7Jo!Hu=$dg_uk4RzmOr~zI~YwB^Lkx5_G$$+-At4_`{itKr!UzNs;+{1U` z${6imG^_ntE+`gb_gkz;#C~i3WSJx92?+4*1a|B#yDQt=38<%#L#r!F>6ZAKDnhXN ztwk5(1Tmr$^%I+Wj&FQn-Pii~#J~m}S$g&P=GMhm5xNWmgDNln_APkryf*1!Zar}S zpVr20KVy4$gdu7>yf-a6v>dh-FAoeB$jc3eI8+lM^;?y9eiJ5z+b7aYZeIWEaQ(|t z+qGZnQ@7P1gG)}rR3zq<=el?AGR;zo5c;xa!6lUphld;JaI8!ZCaQ(gEhB5vOm_utsj=KL^p#CJCbYSh4-#jjM9ECvgz( zG|Ke*T{I^3d!|mfYgW_f-t6X7pVzLTEASZ1lG?aFUH*WW;m7WTCZ}{znZQ_3|GAW> zWxd|9^GV66eCg;>PxmSc4x-bQ+}M_bZ*EG7wMuad{-1wx zm6%c;sn79r6*8SH7Nmz#Ra(0#tb`K`J6lZ+T-SbQygav2^kjb@5J(9M2!t4>H#av| z^Ai7O@R)7_D?l`?O>5_;aoFP8^+#1&Rr?b)muyPqBY4Dx;CP5%NhxKl@xOpEph&*$ zFGuo!{-M?N{@Bj$_Hjr6{y$d~&k6qrC4npxf^%(DvGLyQhh6ks7`-6hxZJ17+S+p< zy1li}`~MaKH$I2COUP2V$hqAt{q@Oh;pR}a$oFHFCDWl6$(3`qJs3Lg<`Ka4eTs{# zwQySRzbyx3mer%A;iu>9w&<8jT!$PXplo>3jf=^)@R!z!V7zN!8sOK~_aqVqN4-w$ z2JrS61Y(yr_xCl8jJR-t{{m3(is76*3H@VTL#v+iv&?){8&MMtLsciFMZC-^e1Yi8 zYzG3D{-ebMJs%g|hd9KuOm;g7U&j9|&r>zE+;u_7OAF_XT5@LAMLMpdc=pZm1cJS+Y3sBi|4Ik2 z0adF;l>^GG!h#f#gy(A>9^N_<2x1Nr$f57s3%^V#{_x^U3Wu|x_Wfx|R7=oO9lAXO zou99eEuSonPc06iK8+%_)@pGP*QuBevhsth|LY3?!(MWB_L#Jdl%@e<1Iw*ddZ@O} zvTy!-dz!QXnk`cypr3+KNsKy9LL03mA3R+?b_`;->y49lp16H0l2`0U8`3n`cCI~! z*_*fr=eT)%!y&3%fO_kDBVr0Zu7r2r#E}I&$y2Ipr#M{UzpNEp$GpB;vCu^~rb;KV zCLSULgv_X(CctxrDfqYEz7nU&oPWi~9=$F);4m>TV4EJ&n*U48RUQwRg}zZoQ<&b- zL1ja$nBtwUUvI5wX&5&&4{a(rF--Sxe}XggoxMhECPL5|ZdsRY6E*0g;5Cetg*xBa zY>m-!4qo`7f=F+=s9%LU)$&A6#ug6Hi^rwM)7(z)$ZN8>NMgAev&Bs}^C3&KAN5qQ z0{QjTas>cJbZ{lghTl2&A;Xgo=JfZ-0{agmkv)6ZZz~ajM3U5Y&jeFJt~QvLKpqMN zBlCLAw}-8#@D^=ri%r<ou8>YEQNqd+Fmf|j*!7R($; zXUY2cmw+MyF^n5(W??^=euIQAj2xb(Ix&Mgd~0ZMJx+=Z`s0h?=3`7YpIsZR_5NDY zgu@m2u#coDx@Bg^W7aK3&q&;?6-tVVkvLYeEr8Ag1;>JOm)RaJe{1C+?e@niT>*%D zyPw#k_Ob5|F4y`t1SslGe&C!OAdfa17aqGYuaODFs!KHmJ^z$m!Tay+r-&Xx*~*|% z5yp6Y|4y3xTfm)cO*+_pQu*4*8d6I*JwZH(c0SI0emOSCcy_j$IPSIGjJw6EJH#gr z$4;6&9)6jUwqCls$eY7c_Hvf_x;~j*rLP%%;F>$u6o}aBg+cCviIZ^5w&zJaQ#EBL zlSRGAkT&2BoXVsnbyC&pTKdybrUF9gT7SH=?ffUxdU3EcM`@L^Lad@aR1LST&k<=e zDyX1^40Ng(rc?h#YSt&!^590zvRHo8t!3t^#4`jX+;?^+p7}*;&OrnuGuUPqmXyQ| znI!OoOycKS(hx~s-5Q|nrA*a`?uY$u!P?2{WT4NQk?>W!Kp{rtNYbIt zRlgw!8Po6(LKA%kS`i}1iNhk{$y8{hxu_gUiG%TQv4Q>rVWdH!q10AF*K`OmxbpaS zy2|6n+zc^@5O@s)nR0QzTaAME#`(chmS+Rsz2o1})oQ@`bQv_70VBhz%}3+~7~;&S zYaoxeOiw4y0*3c@*kIv2=VSIH3o%>92tC^}` zrhS;&e1CEpU%pPoc=BYzfo000J*Nj@_J}*r9u94NLLP~o0s@UDCkIl|A{9js)e4XQ zsOOif9`8}PHNO~Bi0F&!Vu6S?h7#r-UCn117>3B}OFbct;l}#R&M1orVFKPHPe?NN zy%)}bE%j;4<%>^JRTf|ip}rfq%bog7Zu&7z9YRl)6YZT@NNByvh)$L@p#kMu-{)R9 zsU74})MRpH(#_dIVKx84bV8i>wl{^;vt{TIkjus<+H=Xs&)!7mZu*rrlie)C(?&$u z62ECx{$B8j_(wG)hxKRtyIj!o+Z;wJd+oo9@*f1>isE}Vm-(V(O>9w0pqAhv{A(`I z$fB&EesiG1h?LeR_NlRTQ8Amfr_S#hUHUQ?KSI3fXBs^0vl4D8()an; zc{l;LM3`>e;46Ly_uH3V?DA2o!u43Z?@5L^IL#Jw5yLmW{+8?sD0<^){OU-I?|X8t z0Z5dRI?;*uwjDS{R8;<$Fo0%t8ndecqNf*2nC$eRPIT&}!TrJJbbz2xD79WoY$Uw1 z>&IH}xwJD2=d=B`d_X$DSOm9$)h+w^?Q8Hn*zQ~XqjJZ(Tw(`n-?h9(sQJO~4qL)I zRrOO>QHf0oeok&eZg+I97ZxqNue)K2;QU3o%khw-E){uYhu)`8(&i$(7GLVXLJCWH zg%iWLG&^TZ4c}^#v{-w*NY20bs+@3`FQWc`B+Z8m$I1$ul4l3SPdKS1@Czd%J{7MR zm|R*S-m{F%xOmY7)u;NBRBL`+?ZAt7n^Ct~o0X;h!(z&KP0rDBF?oVl%jnjP=-^YQ zZaj2J$sN}9e*V1O?LK<^{sfXoT-kq`4Ig$YtF=gOiSjkR|53z)xZ_J;jr-)P?K@f? zKT7l}&l8 zI@Z7hRj3j-1DN0P>_Yxu;AH!kubQuCM5=7eQiH5_w|BOi?8L;pc7BS1Zvq$BrZk;@ z=~{|8y`fgOv=$q+RV8%UUWsFubgox@J$XR&`ZQU7aP2*t&GIUfkYIZ{-pj%IMv%x% zdzwD`B|(lYv)C74k!i0=e*NMNf2Tx~W9+OQr8W4cInZ04sf3G&v@T4p4vO=(^*MTq zA%`;4o=V9ucNLA|Vj|4nwuH)}6(1#Va3P(rU6%EQh8KLqd_5)+aU?KAt?*lCaTUKz z{o?~Efa;f(=X275h;U3*jd;F#!Q2N%iVBRJ^85UNhdHl-1Ye~g3WQTVxvW)-Fxq3c zicbIX@h`>upmK77ssXePf3Wg+6w9}71BMu?2S%vGg}JFo&TZM%Oy3tPKgIArt>8E_ zz1Zl|5HC4L$iFUVb73qtU3b#$)9p208UJ@S;-6DyoF1q_R*|<(cGMEo=Q&PWRqeLa z?Ry*kIrd=zE4yaOID=9;4lM!dX8?mB2rwAkI#YW7dZ(GK{Rp&HpbF=tJf6S_)zsbO zBrkkgZh!Vc3Q^t5Qj{rLNT~RNyRU&dez!|Z1{a96j!r^kv>2)JhUynFf7LuHt<}@# z7jG@hH2_=0MpgC6dq!S!Bse&;&Wq@Zu1URe=JM0uGDlC>PuEdA%ew|U{Sr@L#u=zXC+^`>X%^ zYRPcRXM3mZhTLZDk-t+mf9vtt48A+AF|D zWAg@_TKhpY=!I!_!X+uuLm@a|UVZ+i5%nk^n+DDpb%+v2d1NFM95lnWjUMV(9UH3m z643T~28QN^NwzQffP3&!M7hex8ZQUr*iIkYE%bOieexs>Fdef4A;`=(Q$Hb>bU5=s zBMj7=qUB9%dQRU37w^LC{yKBO-n;KDM;~|`PAARtM0J;R z1a^ftPyl#&ve3ncaQX-3ulL48fKyvuU7tneV`R)dmg!>9=tyKsUY}8Ntp8{E0JS?c zc?UydMeK4?j6zvi^Q}z^-@Y+wHlX#8XUVD-vZep*$b=7Rj5ltU@|c&d^FplubgEE7{-Pm#PN8T}Z=ITdCfs$Z485+x$z!4Z=;~z7x4BO;ZYB44uT~BX z?Ym9=+^g|oYDJ;2XPDQQ2J{n=9*l=<==)f^8@_bkkH@s+owU?qex*RIO>x11OsKqZ z6&OTYI~r(dUm7`BRzisVWO*l69CtkB?2=Ru(wp?_Ll^93TAFR~-D*8Mh2l1U?vz?; zl>ZJDnx(jp9;q)RZnVUQi2MJ{<9cbb)d&!oR?$$^`C~cP9x1M$IyoG`%md!sHT_lQ zY!xQuM!{odRw8!ihDA*5K8Md(ky-OX1k1jQ3hPeccgoMvXc9<7UUh1nz&%-P-BO_2 z@+V2yKoN!mT0a?jZYQW0v=n_kmY$EVzoogpaS?1br0L0jmX`iuF?StS?6G`zGh%Oh zb;zb_V`sXEZZ=_jn0VVBGk+E70UtP^3m*i8OHyA_LJf9ys;HR5EE9zmfpH?&x5J~u znHG_n6f>GQCYH&HGWV=R4C@P9<%uNxLifds4^!6?z!3&j$LUBgmt7j9F>dfF%6g3s zfr;E`Q0)c|L4);!`&9?aXRZ4jr*92Yw4Wr4g-+OQWewENbxKxV)Ti+$_uK9<;FAwV z#@i-$8naqy^HT|(RdCOHVG{O?xjTOjf!0;*MRpDmB%#7ScxXAmQ)ww>ef{R=SlwlZ z-9z&i;XTue=7sKA1?;+(4hyafZiP4|%NuN7ZHx&9&nNbJOy%7V2wchg6SA8i3Nyra zBS&?ghh`YYrbkiY54W$_|9x4Ne-dP+LIv6TXZL1nY*55ba)+%BTX&4u+muKA{GJgk zY)qF9E=qtRiLmLdvlc66{s!{H=i2*D1pLdZi>}*mZwvJ+e=v8k!wzwnHcG6oPkbLb zy9(*w{HF!6UkbDweWbMfDUsNwbynB{!dK7H{Pz>vUbE^)yzq_9{Q9+vWf6-V{*n!p zN_dhCu3L%J@9wPLS+a>v7gQbKqZsffy_LKQm}VFRbk$aO28!;jC(93s7A@NH-P81q zzi|r?%7Gor#CGIAY`o!pqzch~tlhdT8u2H;!KeeO;T4-sE_#I$Qe~_>_jiD#x^nmV zr0w=`U?p;Ig~)!n-K2ml${=@VssHxqlIrH(jCXVK1+q*|p^qCD(F9oYYvX_`i^Tg6 zOg{S+-AvSZ`6m>J@=a9zFvbz&g+MicJ`2(>5D!_|UFa+2d8E-`hRQA+ReyjQZ`D}} zRN0h=dALL6eL(iB_yW&Q2G|c;mJVxGAA4}+`3-kG6dt^5xinZjcJhw;Ii_2lhcL5! zaXJfQZMkr%s||B9G_$;yaU(J0!Gi}?;>L4GV z>WY?WVd6)8Nq_tok#95a?%>w{*1E-?qXGFyirHC~L2}ikD~9yOuFvvr(0{6w4M_#W zEcEnOAk$a8tyafYl|j5#PF?n#M&`#`+J*|G=vy|mRbEiA#(Z=);IP@n)fC}(OD-&Ft zuqpK~DqLS$W2JZBzuo?nUEbUZ$e^eFIL3?a&(Q2?;52SYWMwSjE{Kg@-W`M~$AWS# z%f_qO5qP+n`gykv_+2l=9S&8^wAHQ)o7F20NYxP2f3q*;eolv1JcxNe17GTc)#b?f zJ=kfN-coU3+MQh3?)t71aKDwD30qLGv4y0f8vXq5=nfe~-i%sf=Gg$W8V7wRcT{_l z6D<-MpxNzqv~~LOCfMBO@J-cfjA)U^yLlNCF*IRpcRx0i=!b``v@5cV0NmSwarFcj9$Gu9jFp6;WWj-4K)zG4mP)dux7QZM_3-@!dc zR%bCBA5kSs0$G6-#C%yfMd}0p@49L;60w9i7(*C3wbx!BdQ2OS?$iwc^Y@Iy9GoB9 zkAZTh+psma0-YQjWYm^0Wp+)7jcVmFwY79{Mokv`pB(C@y*&h=8nXcs`O!@RHN9}q6;@w9wgrLkh zYueuNUIC^m;a)TgIw1hMyy$X=$9p- zCcb3lz@ciTcl9ppw82Ql)$91Wr2KAxogGqx_>shR7VMtr+jCW=$aIjBpuzpfa{K<1 zZ+N}qaoOamNXl z>Lc|=SKhm|;TcmN)@v)iO_pEXzn>9n;6Re1eYQo{nJn)%9D^+{D5GBb|CCvuR7J4C zfwy6p{mj(BqKE?5t-0CKASSe1OQkp}oVa5$9B<#Zc}T+Qw56m`BIzMSOqGHr-YXuiz! z-&+|4)56bBPRrYfzAlQ-o5rBS7H;1W>S+FwGA16+7K({=CifC7P(Uj=j;WDh-p*?L zyeNS>))T}f)UXNE-YhLmvFURm#?2e!=KsF!1&K|(e!XX0#y@2|{z?_gU-6nGd;^WJb^K$ibR^9=@0fqwmiQ4{v>Ia&2?_ znTL&Bp-+j?e$xaFGi@{S=}T3DKVF^B2sf*rBy!$qa?v*(L;X?ACTIa8g7t83>Te<1 z%_zKPYG)b}axk|_6BbkYQtZtzvj&rRd*(AM+b+E>ElW%8y1F{yRc<0O!n_XX@)#GH zEmKKC`PODgOevCfiFE!?>nkx^`=LLt#UnU6Ngd@OwAofI{_`O`k8v-0-M~)BZ7*L< zJem4sm_^#5^8vLs*JwM!-#InKbm*`DC3W^&JG>*+;91ATx!V<7;#2LYm>|AIbopj$ z;{KY#tG|L0UIpJN6G_*jz9f=Dc#D!KHV1}DZFV2VR#iz+Gce>#mOoK+is)SRAd#dR z7GU~(KOn?J?emLM6A5U`&&dA#wj7aIm)h|R{3Sq)U$JyuxFY*^m`JR1Zw6kw0V&-x znEl(2Xh^N(0{zY9U^iHc^6grOjgNbCKb+D~&_m&N;M^46MdJ#IY7}%v4*m}JqMjUM z)3}C9NjhM8h+_7I`rstSyNmzTH^s^Q$F&_<(Ci;KP#owuWhIVcL~Vz8Upk~;Dj>B* z!isS)MCU~wf)J9f3VrUVx}Af*vroWw;UO`E089crW%Q_kwnAwL#KG$C?@zj?+w_1X zZZQ)6C{XUg-%$xFW@nw$0{0NTzMXQC9-R`Z<0ETEPSW$+^gb^uO)x0S-xd)m9X+i@ zby6)Xulu*L%3Ck26G5uJ0N5dN>etZUAzlS?{_OUmzjZ;UX@}b3=p%En{o}pg;Q8w9 z3dA3L>h*^(<~_94_kVAvONKPb=>J-ozYNwcf})FXx(L#4Pe6eIoH40`NXwc!cP7 zt14>ke5%VZi}2NpRM;6vYaErvp_4v#`Jw~yfJ5?aJnTLU)ZjHf;Qhng*{?BNG(d`f z7TR*bA(Tu^p+L?h9HVRG+Rh7C$ij9AD6%42zXC3Q?M&)}Qv{l$kr#aDVb((x907l; zsyUhTk=GAfJiW7(+RfI7M&q&_dB}msl<7NkFHac!xR%UXnrC2o-Mh|fZ(5*Ewh$p9_fbvCEn`Lc`Any8)f zjRyt|7X)d+0Je_h7NmznO_g(5!bgU7-lT7C0Z;zYA#`+YU^u7im^LwS{)qwohZ9nW zu%1uDb?boW`YxyZiN%V%-yJw28O?{?D=+jLa=sNXc1reTKI?XX+@MPj3-HvL`M}A# ziwyPPT6?`^;y*N?6mj+viXS*2lF*%ChkAz?@w3s zuj-7vFRu>Jq@?4W{r3`|XX2;t@cu^F7_Yf&A-1L(b5wv=M0*{5Rvw%15ZdOac3fsF zt7820+6|)D)!*Ar%6^Y$N*PrXfXlQwiZI!Z{nB{)>zY}u2UX=Y$Bgg8dwqUgTI2Ap zw|5URg7MhE_pK4m8u{$wtfu^mdMJ?)@~QS3>Q`~-FLnX21#L3v4~M+c^jNmt<02_f zL1G@QkMm2N&za#fRGppvAMw^b449Ti%wi8l>9~;_nUdX9WTyFRGF;}W_NfcXzTUI0 z;u7M@Ed9O%d*2SHgE8)oaJ`VEy=_CynJ`X zG(9TpCdkL2l0=+I9Z-nab=yFuv>KEB=Kr@PUT@wV$x(37QoGvzdAU8h=qOKM z$Q(01O)Lj4>F>Y=)ZRE*9x-vCtAiW-QT0b%9$O5Epp#`iL1Oja%qk{3F4lJv+LH(x?7x)zzMC9Jm{r_TKph zNROqV7XZyF$OF#ITxq&1Nt%J6%5wkW`7TrpeJvA1bL&1lMYinpQ^r zL{*yW1D4j(j=6Kq7mxb-NwxJ+ee|v^o!8ePH!!-$Qwm^*ix=E(`6F)xmhdu)R?!~a zZ*SH;Mb(JQg&qp&~?shh`g`_1L{i5$@Rp8pe>LIO0ntk>seW~peY{Wd#0dpuEb{%~?3 z+zYRYL||{`sI-$YXDJlhpfYOoj=RRe8{qDZvsQC7)Dh)wG9eLPd*=rSv{^9L)=~v$ zDIU2Wrow=G|NiG-k!kpfYT0C_M|Z?qXGy(4Gc)pw4>)XQchs@|{7|ki#9+C7T3WGU z!s-@S>!v(>k}1z4a{2J;E-h>8@5xv5orrNxM>axMjg#GAy2DFT`UL#{&8;xO88C}T zu=rND&+*#@Iw;q(A1T3jWfhgjq)=An{iH{+Y3PNi*W8y28L@9g=p{FpxvtiebdLxy zy;sz{Az7Y1{uZd#uVrlWBN#u;S8Vosa7bo=SFvK5NS~`C25GHN_Vlr~3$o%rj%qEL zGr6N5NYGr(=Mz?qg6ZI>;z7SntLLP;MnkWh!v9|qz$VVBKahnXm!A<-8 z((M$Srr8@a`D<0Oh;{Wx3#f@VOy*|?16f6{lS9R!Ef#L-S3%9ILWu4M?o6PI$F3#2 zqld4p*OX*$YtO8(G9Kgh#m@FNkb-!+?|OUl%H+g?3{oUJ;q<+aa=q;@RASiHGDyN-l$5JX@1C(0pmN9^ty`^GrZxOdG&v0dMS7Ea-{h zwi%0!N^>80i{NX+%v&QNPb!;rutNVXPozYU&)YqJEhEuzz<%&%AO=x*SsL~De)SD5 zdtQ$2DH0G;0K;~da)At&_mB-Q}Dd8UP!Ke$CXbC2rU}2jY+;6mFhQ!UXU|Y zv5Dp|=3t~Vfj)bl!*LhO#P9BQSlWy)H#P0(4}qPySwLh}C81Y;O*ykpI_x!Q2>#%b zG#SsP8jWVMxyc(Cc$st<<7gORP2X4|6%L9l#1M_UB81uDpeq=%w;y=@TO{0!?S2U0 zp)k$YoLejOj84;kwMAq*dn8DM8x-OB+4u?n%es3Pmhak|%8l}q5>yvZ$2>6d-zqgakkJ z{j5o@DpcrSezcR5KEq4M&E3nsFGhgs`N7fnrd8(RerV%RR}{i1Ab@;&pv3n&7>!*t z$A|`;SN-RQ6kYGmRgBf-r>B{IwN|iqJ$B5Lg6leiOjR*)XHMgA0xbRHF_7Br9$e1R z-Lx;-MUtR}yk{A@iD%WY`o!O*aUSgNXura29l&Q}hl>Y;k2VCK_8$&2h4|F{5_vTv za(D*Xi&n6n4PPQmZw9ndcRQ?pmC{OSfT{m@zbkzAgLhBspZb=q^|Spy>m9!{@u2tT zTG7e^rU5;OOY`;H{Rl)~XnSsrD($8EAYNay$6oaxH;O@5IK@W_z>95|6;at@Muy8y z2^fnA=vCYgam#L#U0ocpLl!+2b#iy3-p8Kv}{GALm7l( zJP{wrP*H<(S^@C${tyX$ZJo?2?%dcD}$;K z%K8NHJ&rN~0L>R?%y0jbJhT;JB_!4>qSH%-yqT$+aG$>ZpT-2b{7r~Xr_DgQc&SM7 zukLz`ovm7AP*9L_pn>;CJa|zO)?4|D@;?bCkYW-Vbd8t#?>|UGGz}|epYElKKJfWZ zv%#0!pa}4Ob2L1Kc$nuLRJJzj9+44HU;cFNn8$X-SdeB82Mza=CO|jFHA?*yhTGZM zaWVJ%c-c`A6f`_lcQk3Y1kUm#;TMD;+rdR3{1^g7U0j#OplNuTLZJ}bJufdWcB-d2 zT#v3sUFq!WSwbn;hlegUkc0KOEo>{Aq&sEO|~@luHxU8X=PMe8U-8udOU&EK#WGnU5nRxWRWno)s>WLIBGuJCz@xd7X4DAy<>#MG%e%^eV(E2-Sf3Z#RobY`%!TQ{MOV8(t&!3mT@BykSkAZH5*%hDR zVZDOTa1toNx6?*(!3a~5h%VTsS5EO=bN?a?Iq7Z}QGc_y@sk_tw5$|qm}Z#=sPaUZ z&o-^mD1zouV;Z@BB3mfo`?9ONcSx>1_Nn1i?0)(35!$>yPqr`bR{0(706>NN%Z%>h zh)mgNbF(E*%%tPFC>*}-i#p?kR3#_p9Tivd8S3iZyA%@-DYYhrn)S zwf&1$J_Y8@i%xH_Y_WV+=5*Rkdnc=%q<41Vp_0Cbcq_p>=iDH3-2rWKxOQiMz#22> zCCF=iB0ijuZkyAzkwL6O@Kvbl^}Tz^_MR?6*7v>b%$PmEo2hNOSz^569cN3E7`e|b zCw-Mp_&%aH7M(RK4yKZU|GU5cgw*$dj7$Xu=o23{7)-FhB1upF#V!%{eV}w$S>@3o znByXlJw~&7#tC`%{s0|Z)W0)@v^~z{N*rOad5l{B4asAm*1)Z&@Rj^l%2W%S+05Eq zxISQV>(iAThrGv8Bc?C#o?jxwj>Yhe(J}*71bfIZi|KDe(_)EuVm2TnBV5jV9X4JW zIgl54U%v3;;rfWW{`qK?RK_$9zkJtOV{&o`SlGM^^0m%4Zb;e7on85T8%QdULSYgZ z09qwCU0ev0Gk-rv(z6RspK&e`7wp?oco|$g^26#nn$C1jpwgFQ_781J^??@HxG3<3 zzEo6s4M*#rSV#LB<7W9}nIU4{tZUMebxW#!tL*Tuv$3Zrpzylft>%D|2VBaR!lxtN z-V0v`mj(loB?rgM(~@sxBDPrrzEXupBOq~c2`^^Rm4jc8PfVQh>luC>djc@5EcrH73;S0LuY!H_-0!d1Yz&tG^$5u+pm=AMI`}<}v>{Tk11S zsL!AMWVI*_NhKFOvJArXFV3SUt;N$ym^Y8V$ve>ltLkx3dYz1E+rk;=|p>3;F8kgn)uC%moNU?c=a&ZL+Sr zZ#U00$2Ytm}4pLtrGfFJK7<tmw0WK zN*M7k?!c|RJs%}I-jYKnQfN*@dbFMYXRrI+US*qG+(G0h6{bZM<uu-mqgmGUs)Vrr7H{$AHxzBAQg>mh~S)7G3Y4vevceChPXO&y&WjIF5pf4oU& zR`WX)c-nJy-K9+O@sdA|JbU&>;?Q7=KNg8r$jHxZY!(BbD<*vV7q;4@cNKcL$x|2_ zUJ-n0bmch1(2D9fLneNl?ZMW(U9SWKJG_qro~ati9e9DK0RrBVs3MXarQLsjCLJA` zYn^W1l6=>i3B}Eg@4TpToO4AoARxtU-}NoZpIv94^of01+_1sqXZBvT;p4BV1RuUp~nSmCmVt8sWB zB6TAysHwRnFg$VD)&nNtd}-Gf&+P9co>{S|E)A{vwEl`sz!sdK3ig3(e{6efs^1P2 z%q0ZN*g%Wu6%cus;Rn7yBg-4<)sCl*(@C$#i(TFLI9qh9s{x#z!r;`*kR*708NjRo_*uCgvQ3nGlP+RnNNWhz}tLV%Dv{#XU-oTpt zzJ0t6NLjMBngE-%Q*>0Q4yLN29$JQW}+9kay9ZrdX5q zo0qtZwSOOiDexRBVNF1{Cf!PG+;oBF^Px2#eDr8m2ZX<%a@`3!hzIixg+N!z+X;F* z0hOsz!6;io;_pI=T@0{phW~@Kmo0C;m;O^|D)~jqiO~NFxgS89{Is9&BctJ-JLrdx znfc`{(pf9bGy5{j?7qs z=L6}ASfEfYUQ9gE#dE70T6|V18F5J9-u}L#rX~gC`LB;;;57lnexj5Fs4BJC71w90H6|9~ck*j&d{-MTT<)`cC{A1p zl!NDdAPHKTUA%FS*emC|p8rO26hwdTG z9Q?&EVt{g)yKJ`pjCnCgwA6#N3f<6sZp$^^bB6j~*J~pRv;KM(tm`kJj+%5_DMgR3;@Kb5;*Z+Jv<{sux{`cQtI%u5Q zoq4iQfKUFW6iBgDyW7wLKs10!er;gNmhXRod)34H*fLgZYAPZHV6c3L zK_2RVG5K4HH|NWj73s!oDnibuIe%uI?@gE^LqgT&- za{9w^b?^RG<@4gpkX|&)iUC@*DnElXUuVp&)jDtXcig#n;PZv^@p>+ldU#e~L(A&# zmHUauv%ZoD<=+X1TMLR$h0a&X@is%*(XF8q=;PeCH(1~G4<>TUq4drf9UF9fKotm} zt_@RRw06lSbr%V*E-SJ#_@uNtl8<8&+78~u-{HqA?T4>Avrh#sm`ausYa}_dcF2RX z`X4ICtxvT2FIlBJnVgr~40Uwoctyv=5GGJ_CPKS%sDGu*-h1MP*73m{9B={Zt^$d^ z=%py?9o;FItvsW)iFE~c;2*nF0;B!T1*mI!h z-#vVejIfmZ%j93=k<_|Yb-KmowYN9EId^Eppk1z1mO9RTa6+t{Mb_c;P*>J`BR#9w zxwt-&8S=p*IwWOC=xJ$}hIwL6N-s=0pu0fpX*LBm6w83V2I)Ur?aUeBG5F6&tmfgM zlf*n+*riS8_q2tHgMa-^QOg^A{CaqHj|TLWMge^}ViFQ6RT|&QcJh(;p3Ghs+rb8y z7H7!Q(w)z96aGBj8)0r8ax}7@03`Bn799tquPK=2{W1!kxVww{1sZbDzJ9&XG|$03 zyLi=UOf_aC)AtLZUtCHh)y4jh%MtP^LuMRw6g2K5oiU=`=g1;V*}6czjy$t3B8yO& zYi_c_PK9c~R%yF)YB7MiUnTsrT2$tCquk&F6y%&JYEDKH1~I9ic<{7K-~Hs|ChXww zpAvXoZp-qGZz?V9rf9&@w5zHnL34p4*AiVkM1fQ@6*KaD$xoszO(y1fdj%V z*v%yyFw{qbLVxWqh^xB^)ouM3(jK?Kw~`(F@9G$!+WsPZru|+bJ&W$|YB(oPqxOvn zA;CL>Rc3tSCb?&3H@Z&9RWrymVYB5*Y zIfaCvK3|1afEr{EQ`HQsWE_(6H1+VP>g&>9L)GT`AC;YpCQdA`!48dLlt|N$j%Uar zSeg0Tgwk=%QxzN#YkA1$MIwdTJ>h0ILV1m@kxkyNuHSJ#TdPEB2q+3=KGiXuCp*9C zOuA>$u`w7Qk9Im{;)mZSAx3!pi&sL!H`E)vaUOrNAING`$l}o9PcC-qa868;)Tx?* zx;dKnicffPda@ALc$g=ap5*GB5P@SP(_?%x?s6GBLmXW#HbjZ$VdI39)OuVd-I20& zq-oHa+%v47q>+lvKaZ{JEdJ( z)jt3eP@slcC<@S{RhzvOWQ}NwoT`e(d#zIgI6%pVf3~D^hR?hvqOl`_A9>ZN6W7_K zx5anivrWaHq1p1HlmZPC;bDhjV(;1CI&Ss8k`5a?pQ1^?5Ot8UA(+i~eY44T6`ka3 zS9H5B(sN-bd?mx=tY_t%3|V7URmZs1()`HP>aiP+0gu$!LqbE7?ER~aHgGy+n1i$K z{@7gvDs2iEDA`z3MSu2$TFA0#8664eGSZOku`qx4y;$@H&OksxOqQP=V^b^>p4_sNz0Rcupm+G%p8TXfK?hcri(A^-6E(2DAjIznl z4^L#8ZuUq!z9a$WJTC_!YdF{&b2Q=!XBD4$+hg_mf$hLmZyqzTE<-0Ls!>Oy_CxVp z%l`g#wf^X4x1A)}gtd*Aw_(6|74B1xwl(mrD!Vb<5WK6JQF~oF?iORMnVC~1(Z1cS z8IU$c7y>jX)^(OVjq(8`9e_Wq|9m5ZJl{R?Z7X(Lt2h4#_tEj2mYsi^6yCGNOhZ=V zrTV}|Q847%_4F)af|X-$JV$*PVk}v!MIEIQc}q)HRu%}0kmdS*C2V>Jp+>H*G~)lf zdTM4y425cm*-hMnM0suVuUTB+UecA(l+?=qk=l-bckt6`GG9UtUWq^^aMl>Qa%9S9|tyuOEI}4a#Dr157C`mq5hLQlu2&>`uy8e$SUj4zQrpD-P|V zkrM6K`^6qEu>UiZJ!3nP;p31aFM9njU|pDWgY4GDBcTGUr6kgS3Ut$cT0#0aF1NR| z3`va-x;zg~Q%lFq+>4H6Xk;|a z=i@jjb1RbLgC!tcTAN&oa@ymqbINrWdMpZJFfPteZA zu+^0X|MA^#x&1djs=tspuYc#iLjBuwGV~b2iS6snVSf75+P*HEj+dVBiVjEU6&Zjna$jzEq*J5B}2-q`%T%X<|k>n#oG zLvx{Ryc{V()?&2jT$_g9nzZ5_PU|D=3#0vB2a8=DOIHf|I{b~Uzp~=7Y%>_5Js5D7 z)wnpNoY|0r>lcmn&d|Jtt^;gkux1$Jr%;k=!dt35)Oy+6YuN8k1vl1~zQ#p;w|i?b z&1TJBo29RmCDCbOu?>5bxRsFI!>M`Gg0bz@!!D_|-{Jdt6||acGKz{CuP6y_rcryW zbH6=`+_b6L^|w04RUG0B{yx~*99pHudSPU8w3Kk2?k@-}RaSZ0JW}ts)BQNCdxB&= zq{Pzd=|}qgJ~sc;8R%>lJ@0M!9j8QIfz&=w4MiIH!tDtNeFlmcqRVd^@hG}(5T7}# zAtCip&7x(`DH34dSZXE*^*i+?HxHxW!{QkmwFPbenmCgH<*v2hPLKILI z4|YZvr?TaDKov}VI1v~EHZj@9Zr9GM$QG$?vA)jp`CQSj)!HlkuOjwAi#`<@?YVo{ zfploJ@5V;?0TGiU`pv|+zs;WGAS3l)PQ8ijoiNt4{ zyO=PQlfBwn>HQQ9ySK%L`%`2hfZ>A^E=x=D2*U;Qslg^Au5UT0GGt_=)-2xRP;o<* z&p%^i6})}!Fga$uysWqaChn}v;a`#qyRPta@_YPT7Kd!M9}%J#ZBr)IbC zHhfYyoxu5e)RKCGTY1jB^Us@`k#)=MZ+)ILT`tWm1=xUw7lgzE>Xa^Facqg*t zYq!QC_bDpq!Ck-zJcENMOnUsd?%lzl#A4r2pVXDmX4bOnlWt+md-u3XqC1I+|a(#R_9H{SHRr+J35|UiY{VaST)#O?wY*f#eqw4;S%YTGP8#ilu-0JHDTFv!tD!=|Ne}M3i2aaR2Wa0WmQ^@!TNb} z2suRWKXR@&?YP+AIM1mIvIrmdXeM^QUYUY2ekD{k_PL)Dc*3ZBP$-7tdoFT2hVpb| z!kO-XCO1+^r7chG(Nxcow;K^ecpFQ0N9)OeoV=iXAIN6Xvki?w_Ir}&KiH0xIb~f$ zoj=KSyGP%A1=2S*!-HY`jx}Ivw3g5UJ@B3fy-f^B@%QA(ejrl07H8z2{K~mi)@ZbD z^r|3rQa>S;WD?{Or#r#2!|?lT|*bZxvua$}iJf@(@%JsY7yydF;> zeOpdgF5%KMysK8D|6hHYeOza$dkFXtDV?LN{_*WX3yj~}`ZMEUeY3T0#RFwKE4{4k z8~ly5!JLbm(rlGbqWHx@UD)nM#7TVj*6o7rnzs@%&Q$fyQ=j(gCoHUzBVC?$L?;g&wHLT_jFp4-J3U(lDyLW@|U)`fiI>s#HaXg znP}>pzva<)!$MVFDPR5RFu69AYn+zkbJuNWZIn28N%wa-#Xz3b;^SvUQgQL|&3{ig z=S1)kzUPD^?#gG>QutzTM=D0lN-r`@{3DRX%zu8Dti}z`z}E03#$-0# zh{PY_v#1p*4UzR@O#jSt8QPBN6{r@+?#&y!7cL*u-)cb%zwooVR*~64Jw)e9&JcnL z$v3KGpBQ>WP=dNg$sl<>yn>a$#@_l0M6ANSVQ1p=&U)1}Lu5^W#ITqL@lrkXUd-j;_c{URJ zK20nYMukM(sPbaO7YI=;9$OL$#|v=<$zNb59VE5~9SS@?a2J?J2B z567wgt(LhAsnF*}N6rU9#SzLkOo`vDF7SZJZOWz>8rewF?^9*N6UY3|c<)X~@%3ix zNPWQLh~&Q2RP@o2t5mAy%6sX%Kio$s=B3yihCM3x?+!U;}5s(Z>lTV{C9*%@TR3O86nogy4nry&WkmmO$&TQ^}C z@mrk{`1cDtbGj834%1%FsT?qqPfP~__PMuT4SRND1JG~u&7J73uePxXI?z+U{N>U= zk7=$I?0#CA-*_Od;brH*s@%{KX7s;t@C_vGX?#32B>lwr)7HvWp?HW-8ivAYNYh6X zHU9n{a#w@(VFdir>z*&=SC%~Oz*c}`QE>$fRN`=<#h}ymTQTQOn z#thzT7GV{Yp7iB2)y{z$U!(^AknAXiG?o=Y<}x#E|Apn3mfG<)yq_#aLRi;>m@UCj zTG21AN2P|Mm!?vLmfI__p=&3HhWJn0PK!AQuO$%Aum#2kO5&(OVmeyd=O>5jJJsz)SD?trAUjgO!$6(~5f^Hxy$Gd0 zyOlpy=aZLb3u#(b_cnkzS{Et)Ub2?@1^SA0vk&PO)`#RS{yRgKd5|Bx-KjDWMeWEU&*`d5S&bl9WsYPPzQ&&)f3ytQ_&_6?}Ym-fS{; zz)xJ8R^NmF9-miGP8wSM5s>iVckc;TSJ9dAcGKRfMEP)LSw+Px#SN|t+!DFHtNjhj z_STHLf(L&QRh(toh(x zt%zJgoBf=HDHptNsoj|1GJtjjg@-@1vEiF`I1XY^Ifw|h+qbnx%Inrw(-#|%+`iot z%wyGiQ2p@Z{gEB@7mB{WUUR&BSN;J_hpRh%YVM?8xnr45(k@HtTso1QW0;Zsm4yUB z3tiCtNskusr)QI4fOl3(UVPor-SSqy=7ebseY9kS(&pAJs`E2B7U0AeU0JCaJGdzr z&iC;t3G$@R=4)%>K{raJyK)nIs_x)W^i)TOAh6{{5-~yAdX}iw_2CqWZW`UU%O+$d zrniQf&=CuSnc=RFyF!o+N0xsBUj03r*U{bY%x$_J;VX}`E~pO{*D6-l(DiH*mo`|| zwbUm3&+`!jh0GdA%s+lz{1!LYY5L)6ybIf^E*+vXXZe^xR7O$dmDDa5Q0BD&r^c31 zB{6Uz&A=sU8@PhxP5}kBMownP`2FupFhFis*IRm4-2*<+bchZE4BVI=pV!~ryqO3K znVu?5dshAKCc9%#88PDhemv6T{JDhzPY1k~S*jRg)|jFuqW!e9Fwjh?Um*4yQ*mhy>}Xm#~sidOlKvjA21 zbc_%#A^azep1wX8mvxlKYSUC#xoYjm^^hHbI}a_kHDt@!_SRQs7;dp_WLOF8^U>^y zx&P;lz0ZohTS4JP1rypDss%3C;*AWmqw&bjoF%Aab#?V)2M57gmrZ(e6mFSxr7E|K zOGb5~#)jh*oCbTn;g(whcmA1KsULg+kLe8veP1{M1i!%7Up}+c4I|UFwxn-_MjW9^~wi0F3yl{+qtV04}h!o zkza>EP{|(d!+I{NmYLE*!^k}BRmuyvqfZf5tzk*GvtQ|N&ZE%CCG~UvJ8+fd(L!4g zLl1iOJFnOiqx`J5*cQrl$c+B zEEIDl!TpQmcmL(t@2Q&*%P?D87wPOWSqri8qcn77v{yXLUnvLi!&(!IGK#*E^9h`! z9fNr7sFvyA?StN&kQ$ouc%aU%F5igwcJ*u>%YFrt!62jFuw?41#X^;s!D!86{rG%T zgGtWLenok6N2tNf|C4iu#%`7NUap&;!L);XGb fJW~2gaN@n3&P3!-olFA(Uuq9Em5Uxc_xpbU*VXok literal 81769 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 XqB8R5=wyvN00000NkvXXu0mjf7I(1& literal 5045 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 diff --git a/maps/away_missions/140x140/zoo.dmm b/maps/away_missions/140x140/zoo.dmm index 84e18f701d06..e87147a3208a 100644 --- a/maps/away_missions/140x140/zoo.dmm +++ b/maps/away_missions/140x140/zoo.dmm @@ -73,7 +73,7 @@ /obj/item/stolenpackage, /obj/item/plastique, /obj/item/plastique, -/obj/item/melee/energy/sword/pirate, +/obj/item/melee/transforming/energy/sword/cutlass, /turf/space, /area/awaymission/zoo) "aj" = ( diff --git a/maps/away_missions/archive/spacebattle.dmm b/maps/away_missions/archive/spacebattle.dmm index 48106f306f10..0c972979b977 100644 --- a/maps/away_missions/archive/spacebattle.dmm +++ b/maps/away_missions/archive/spacebattle.dmm @@ -270,7 +270,7 @@ /area/awaymission/spacebattle/syndicate3) "bw" = ( /obj/structure/table/reinforced, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, /turf/simulated/floor, /area/awaymission/spacebattle/syndicate1) "bx" = ( @@ -888,7 +888,7 @@ /turf/simulated/floor, /area/awaymission/spacebattle/cruiser) "eR" = ( -/obj/item/shield/energy, +/obj/item/shield/transforming/energy, /turf/simulated/floor, /area/awaymission/spacebattle/cruiser) "eT" = ( @@ -929,7 +929,7 @@ /area/awaymission/spacebattle/cruiser) "fa" = ( /obj/spawner/corpse/syndicatesoldier, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, /obj/effect/debris/cleanable/blood, /turf/simulated/floor, /area/awaymission/spacebattle/cruiser) diff --git a/maps/away_missions/archive/stationCollision.dmm b/maps/away_missions/archive/stationCollision.dmm index 74a28eeb4968..94520c133f68 100644 --- a/maps/away_missions/archive/stationCollision.dmm +++ b/maps/away_missions/archive/stationCollision.dmm @@ -2777,7 +2777,7 @@ /turf/simulated/floor, /area/awaymission/arrivalblock) "kN" = ( -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, /obj/item/clothing/shoes/syndigaloshes, /obj/item/clothing/under/syndicate, /obj/effect/decal/remains/human{ diff --git a/maps/rift/levels/rift-11-orbital.dmm b/maps/rift/levels/rift-11-orbital.dmm index 90f246634f87..fc113d7d0de8 100644 --- a/maps/rift/levels/rift-11-orbital.dmm +++ b/maps/rift/levels/rift-11-orbital.dmm @@ -6284,7 +6284,7 @@ /obj/item/clothing/suit/armor/tdome/red, /obj/item/clothing/head/helmet/thunderdome, /obj/item/melee/baton/loaded, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, /turf/unsimulated/floor/dark, /area/tdome/tdome1) "tj" = ( @@ -6447,7 +6447,7 @@ /obj/structure/table/rack, /obj/item/clothing/under/color/green, /obj/item/clothing/shoes/brown, -/obj/item/melee/energy/axe, +/obj/item/melee/transforming/energy/axe, /turf/unsimulated/floor/dark, /area/tdome/tdome1) "vT" = ( @@ -6570,7 +6570,7 @@ /obj/item/clothing/suit/armor/tdome/green, /obj/item/clothing/head/helmet/thunderdome, /obj/item/melee/baton/loaded, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, /turf/unsimulated/floor/dark, /area/tdome/tdome1) "xw" = ( @@ -9484,7 +9484,7 @@ /obj/structure/table/rack, /obj/item/clothing/under/color/red, /obj/item/clothing/shoes/brown, -/obj/item/melee/energy/axe, +/obj/item/melee/transforming/energy/axe, /turf/unsimulated/floor/dark, /area/tdome/tdome1) "Uk" = ( diff --git a/maps/sectors/piratebase_192/levels/piratebase_192.dmm b/maps/sectors/piratebase_192/levels/piratebase_192.dmm index adb8f29930a7..59da0041ba63 100644 --- a/maps/sectors/piratebase_192/levels/piratebase_192.dmm +++ b/maps/sectors/piratebase_192/levels/piratebase_192.dmm @@ -5314,14 +5314,14 @@ /area/piratebase/captain) "Wc" = ( /obj/structure/table/rack/shelf/steel, -/obj/item/melee/energy/sword/pirate{ +/obj/item/melee/transforming/energy/sword/cutlass{ pixel_x = 5 }, -/obj/item/melee/energy/sword/pirate{ +/obj/item/melee/transforming/energy/sword/cutlass{ pixel_x = 5; pixel_y = 5 }, -/obj/item/melee/energy/sword/pirate{ +/obj/item/melee/transforming/energy/sword/cutlass{ pixel_x = 5; pixel_y = 10 }, diff --git a/maps/sectors/tradeport_140/levels/tradeport_140.dmm b/maps/sectors/tradeport_140/levels/tradeport_140.dmm index 2684410d2157..04dd84918912 100644 --- a/maps/sectors/tradeport_140/levels/tradeport_140.dmm +++ b/maps/sectors/tradeport_140/levels/tradeport_140.dmm @@ -471,7 +471,7 @@ req_access = list(160) }, /obj/structure/table/marble, -/obj/item/melee/energy/hfmachete, +/obj/item/melee/transforming/hfmachete, /turf/simulated/floor/carpet/bcarpet, /area/tradeport/cyndishow) "bU" = ( diff --git a/maps/sectors/tradeport_192/levels/tradeport_192.dmm b/maps/sectors/tradeport_192/levels/tradeport_192.dmm index 596ac6f8df30..53e25968b662 100644 --- a/maps/sectors/tradeport_192/levels/tradeport_192.dmm +++ b/maps/sectors/tradeport_192/levels/tradeport_192.dmm @@ -3061,7 +3061,7 @@ req_access = list(160) }, /obj/structure/table/marble, -/obj/item/melee/energy/hfmachete, +/obj/item/melee/transforming/hfmachete, /turf/simulated/floor/carpet/bcarpet, /area/tradeport/cyndishow) "kD" = ( diff --git a/maps/templates/admin/dhael_centcom.dmm b/maps/templates/admin/dhael_centcom.dmm index 247c12e9fe5e..91ea585fafb8 100644 --- a/maps/templates/admin/dhael_centcom.dmm +++ b/maps/templates/admin/dhael_centcom.dmm @@ -229,17 +229,17 @@ /area/centcom/specops) "ay" = ( /obj/structure/table/rack, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, /obj/effect/floor_decal/industrial/outline/yellow, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, /obj/effect/floor_decal/industrial/outline/yellow, /obj/structure/window/reinforced{ dir = 1 @@ -10688,17 +10688,17 @@ /area/centcom/specops) "GH" = ( /obj/structure/table/rack, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, /obj/effect/floor_decal/industrial/outline/yellow, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, /turf/unsimulated/floor{ icon_state = "dark" }, @@ -11332,7 +11332,7 @@ /obj/structure/table/rack, /obj/item/clothing/under/color/red, /obj/item/clothing/shoes/brown, -/obj/item/melee/energy/axe, +/obj/item/melee/transforming/energy/axe, /turf/unsimulated/floor{ icon_state = "dark" }, @@ -12059,7 +12059,7 @@ /obj/structure/table/rack, /obj/item/clothing/under/color/green, /obj/item/clothing/shoes/brown, -/obj/item/melee/energy/axe, +/obj/item/melee/transforming/energy/axe, /turf/unsimulated/floor{ icon_state = "dark" }, @@ -12076,7 +12076,7 @@ /obj/item/clothing/suit/armor/tdome/green, /obj/item/clothing/head/helmet/thunderdome, /obj/item/melee/baton/loaded, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, /turf/unsimulated/floor{ icon_state = "dark" }, @@ -13315,7 +13315,7 @@ /obj/item/clothing/suit/armor/tdome/red, /obj/item/clothing/head/helmet/thunderdome, /obj/item/melee/baton/loaded, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, /turf/unsimulated/floor{ icon_state = "dark" }, diff --git a/maps/templates/admin/ert_base.dmm b/maps/templates/admin/ert_base.dmm index 96879d65dce4..5844bc5022bf 100644 --- a/maps/templates/admin/ert_base.dmm +++ b/maps/templates/admin/ert_base.dmm @@ -1357,16 +1357,16 @@ /area/shuttle/specops/centcom) "cL" = ( /obj/structure/table/rack/steel, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, /turf/simulated/shuttle/floor/black, /area/shuttle/specops/centcom) "cM" = ( diff --git a/maps/templates/admin/kk_mercship.dmm b/maps/templates/admin/kk_mercship.dmm index 16b08549308f..f889b1fa0f65 100644 --- a/maps/templates/admin/kk_mercship.dmm +++ b/maps/templates/admin/kk_mercship.dmm @@ -9345,18 +9345,18 @@ /turf/simulated/floor/tiled/techmaint, /area/ship/manta/bridge) "VF" = ( -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, /obj/structure/table/steel_reinforced, /obj/machinery/atmospherics/component/unary/vent_scrubber/on{ dir = 4 diff --git a/maps/templates/admin/mercbase.dmm b/maps/templates/admin/mercbase.dmm index 59eb33bbafb5..bc8c94c333a0 100644 --- a/maps/templates/admin/mercbase.dmm +++ b/maps/templates/admin/mercbase.dmm @@ -489,18 +489,18 @@ /area/antag/antag_base) "aP" = ( /obj/structure/table/rack, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, /obj/machinery/recharger/wallcharger{ pixel_x = 5; pixel_y = 32 diff --git a/maps/templates/admin/skipjack.dmm b/maps/templates/admin/skipjack.dmm index caad2f925597..e757e7953c50 100644 --- a/maps/templates/admin/skipjack.dmm +++ b/maps/templates/admin/skipjack.dmm @@ -1099,7 +1099,7 @@ /area/shuttle/skipjack) "cK" = ( /obj/structure/table/rack, -/obj/item/melee/energy/sword/pirate, +/obj/item/melee/transforming/energy/sword/cutlass, /obj/item/clothing/suit/space/pirate, /obj/item/clothing/suit/space/pirate, /obj/item/tank/oxygen, diff --git a/maps/templates/admin/thunderdome.dmm b/maps/templates/admin/thunderdome.dmm index 31f85fdaa411..8cb99cbba953 100644 --- a/maps/templates/admin/thunderdome.dmm +++ b/maps/templates/admin/thunderdome.dmm @@ -26,7 +26,7 @@ /obj/structure/table/rack, /obj/item/clothing/under/color/red, /obj/item/clothing/shoes/brown, -/obj/item/melee/energy/axe, +/obj/item/melee/transforming/energy/axe, /turf/unsimulated/floor{ icon_state = "dark"; dir = 5 @@ -43,7 +43,7 @@ /obj/structure/table/rack, /obj/item/clothing/under/color/green, /obj/item/clothing/shoes/brown, -/obj/item/melee/energy/axe, +/obj/item/melee/transforming/energy/axe, /turf/unsimulated/floor{ icon_state = "dark"; dir = 5 @@ -77,7 +77,7 @@ /obj/item/clothing/suit/armor/tdome/red, /obj/item/clothing/head/helmet/thunderdome, /obj/item/melee/baton/loaded, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, /turf/unsimulated/floor{ icon_state = "dark"; dir = 5 @@ -128,7 +128,7 @@ /obj/item/clothing/suit/armor/tdome/green, /obj/item/clothing/head/helmet/thunderdome, /obj/item/melee/baton/loaded, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, /turf/unsimulated/floor{ icon_state = "dark"; dir = 5 diff --git a/maps/templates/shuttles/overmaps/generic/cruiser.dmm b/maps/templates/shuttles/overmaps/generic/cruiser.dmm index 5472b593c6ca..071f0063aa0b 100644 --- a/maps/templates/shuttles/overmaps/generic/cruiser.dmm +++ b/maps/templates/shuttles/overmaps/generic/cruiser.dmm @@ -5404,16 +5404,16 @@ /obj/item/melee/baton/loaded, /obj/item/melee/baton/loaded, /obj/item/melee/baton/loaded, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, /obj/item/material/knife/tacknife/survival, /obj/item/material/knife/tacknife/survival, /obj/item/material/knife/tacknife/survival, diff --git a/maps/templates/shuttles/overmaps/generic/shelter_6.dmm b/maps/templates/shuttles/overmaps/generic/shelter_6.dmm index 242fc42c3824..e0a277ddfda5 100644 --- a/maps/templates/shuttles/overmaps/generic/shelter_6.dmm +++ b/maps/templates/shuttles/overmaps/generic/shelter_6.dmm @@ -65,14 +65,14 @@ /obj/item/material/knife/machete, /obj/item/material/knife/machete, /obj/item/material/knife/machete, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, /obj/item/melee/baton/loaded, /obj/item/melee/baton/loaded, /obj/item/melee/baton/loaded, diff --git a/maps/triumph/levels/flagship.dmm b/maps/triumph/levels/flagship.dmm index e79f15c02043..9f62a245cd19 100644 --- a/maps/triumph/levels/flagship.dmm +++ b/maps/triumph/levels/flagship.dmm @@ -1399,7 +1399,7 @@ /obj/structure/table/rack, /obj/item/clothing/under/color/red, /obj/item/clothing/shoes/brown, -/obj/item/melee/energy/axe, +/obj/item/melee/transforming/energy/axe, /turf/unsimulated/floor{ icon_state = "dark" }, @@ -1851,17 +1851,17 @@ /area/centcom/specops) "fK" = ( /obj/structure/table/rack, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, /obj/effect/floor_decal/industrial/outline/yellow, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, /obj/effect/floor_decal/industrial/outline/yellow, /obj/structure/window/reinforced{ dir = 1 @@ -4455,17 +4455,17 @@ /area/centcom/security) "nz" = ( /obj/structure/table/rack, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, /obj/effect/floor_decal/industrial/outline/yellow, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, /turf/unsimulated/floor{ icon_state = "dark" }, @@ -7634,7 +7634,7 @@ /obj/item/clothing/suit/armor/tdome/red, /obj/item/clothing/head/helmet/thunderdome, /obj/item/melee/baton/loaded, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, /turf/unsimulated/floor{ icon_state = "dark" }, @@ -8273,7 +8273,7 @@ /obj/structure/table/rack, /obj/item/clothing/under/color/green, /obj/item/clothing/shoes/brown, -/obj/item/melee/energy/axe, +/obj/item/melee/transforming/energy/axe, /turf/unsimulated/floor{ icon_state = "dark" }, @@ -10908,7 +10908,7 @@ /obj/item/clothing/suit/armor/tdome/green, /obj/item/clothing/head/helmet/thunderdome, /obj/item/melee/baton/loaded, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, /turf/unsimulated/floor{ icon_state = "dark" }, diff --git a/sound/soundbytes/effects/combat/block-metal-on-metal-1.ogg b/sound/soundbytes/effects/combat/block-metal-on-metal-1.ogg new file mode 100644 index 0000000000000000000000000000000000000000..10ed875b8d0f210b0b4081a595bff20b1ffaa552 GIT binary patch literal 10382 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 CYTPCO literal 0 HcmV?d00001 diff --git a/sound/soundbytes/effects/combat/block-metal-on-metal-2.ogg b/sound/soundbytes/effects/combat/block-metal-on-metal-2.ogg new file mode 100644 index 0000000000000000000000000000000000000000..b7ae0e247d013778098b5b246fbc7769d03eea51 GIT binary patch literal 12604 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 Be3SqH literal 0 HcmV?d00001 diff --git a/sound/soundbytes/effects/combat/block-metal-on-wood-2.ogg b/sound/soundbytes/effects/combat/block-metal-on-wood-2.ogg new file mode 100644 index 0000000000000000000000000000000000000000..25694ec5578570fe1311c7556dd548ecb46269ea GIT binary patch literal 6631 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 literal 0 HcmV?d00001 diff --git a/sound/soundbytes/effects/combat/block-wood-on-wood-1.ogg b/sound/soundbytes/effects/combat/block-wood-on-wood-1.ogg new file mode 100644 index 0000000000000000000000000000000000000000..d2829db00c372e256c1cbcff5eb6a9ea4b06ae3b GIT binary patch literal 12163 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 literal 0 HcmV?d00001 diff --git a/sound/soundbytes/effects/combat/parry-wood1.ogg b/sound/soundbytes/effects/combat/parry-wood1.ogg new file mode 100644 index 0000000000000000000000000000000000000000..a01f3dbe0161d2c7d859aeeb860b43cb8aabbe61 GIT binary patch literal 10321 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 T=}*8Qh*Kb(>RUpW5%7NiMs8yq literal 0 HcmV?d00001 diff --git a/sound/soundbytes/effects/combat/parry-wood2.ogg b/sound/soundbytes/effects/combat/parry-wood2.ogg new file mode 100644 index 0000000000000000000000000000000000000000..398a59e24863d4c3b872300dc91b86cab33d0798 GIT binary patch literal 11514 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 z Date: Mon, 2 Sep 2024 11:48:42 -0700 Subject: [PATCH 18/35] shrapnel is no longer rng + u can pick what to grab (#6712) ## About The Pull Request shrapnel is no longer rng + u can pick what to grab. theres also now a proper proc for removing something surgically. ## Why It's Good For The Game no reason for rng in ideal circumstances. u already get penalized for improper tools so like why have more rng. also a needed proc for slightly less shitcodedness. ## Changelog :cl: add: shrapnel / etc removal now goes through a surgically_remove proc. qol: shrapnel removal is no longer rng qol: shrapnel removal now lets u pick what item to remove /:cl: --- .../objects/items/weapons/implants/implant.dm | 4 ++ .../simple_mob/subtypes/animal/borer/borer.dm | 7 ++- code/modules/resleeving/mirror.dm | 4 ++ code/modules/surgery/implant.dm | 61 ++++++------------- 4 files changed, 34 insertions(+), 42 deletions(-) diff --git a/code/game/objects/items/weapons/implants/implant.dm b/code/game/objects/items/weapons/implants/implant.dm index 0bfe624f50f9..92769892f314 100644 --- a/code/game/objects/items/weapons/implants/implant.dm +++ b/code/game/objects/items/weapons/implants/implant.dm @@ -92,6 +92,10 @@ else ..() +/obj/item/implant/surgically_remove(mob/living/carbon/human/target, obj/item/organ/external/chest/removing_from) + . = ..() + imp_in = null + implanted = 0 ////////////////////////////// // Tracking Implant ////////////////////////////// diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm index 0944f22c199e..1aa2a52144f3 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm @@ -51,7 +51,6 @@ var/roundstart = FALSE // If true, spawning won't try to pull a ghost. var/used_dominate // world.time when the dominate power was last used. - /mob/living/simple_mob/animal/borer/roundstart roundstart = TRUE @@ -267,3 +266,9 @@ continue else if(M.stat == DEAD && M.get_preference_toggle(/datum/game_preference_toggle/observer/ghost_ears)) to_chat(M, "[src.true_name] whispers to [host], \"[message]\"") + +/mob/living/simple_mob/animal/borer/proc/surgically_remove(mob/living/carbon/human/target, obj/item/organ/external/chest/removing_from) + if(controlling) + target.release_control() + detatch() + leave_host() diff --git a/code/modules/resleeving/mirror.dm b/code/modules/resleeving/mirror.dm index b68cb6791991..92bebfefa35a 100644 --- a/code/modules/resleeving/mirror.dm +++ b/code/modules/resleeving/mirror.dm @@ -81,6 +81,10 @@ forceMove(MT) MT.imp = src +/obj/item/implant/mirror/surgically_remove(mob/living/carbon/human/target, obj/item/organ/external/chest/removing_from) + . = ..() + target.mirror = null + /obj/item/implant/mirror/positronic name = "Synthetic Mirror" desc = "An altered form of the common mirror designed to work with synthetic brains." diff --git a/code/modules/surgery/implant.dm b/code/modules/surgery/implant.dm index 4f5bff255204..e0f11fce24b9 100644 --- a/code/modules/surgery/implant.dm +++ b/code/modules/surgery/implant.dm @@ -158,6 +158,15 @@ // IMPLANT/ITEM REMOVAL SURGERY ////////////////////////////////////////////////////////////////// +/obj/item/proc/surgically_remove(mob/living/carbon/human/target, obj/item/organ/external/chest/removing_from) + removing_from.implants -= src + + target.update_hud_sec_implants() + + loc = get_turf(target) + add_blood(target) + update_icon() + /datum/surgery_step/cavity/implant_removal allowed_tools = list( /obj/item/surgical/hemostat = 100, \ @@ -190,49 +199,19 @@ /datum/surgery_step/cavity/implant_removal/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/chest/affected = target.get_organ(target_zone) - var/find_prob = 0 - if (affected.implants.len) - var/obj/item/obj = pick(affected.implants) - - if(istype(obj,/obj/item/implant)) - var/obj/item/implant/imp = obj - if (imp.islegal()) - find_prob +=60 - else - find_prob +=40 - else - find_prob +=50 - - if (prob(find_prob)) - user.visible_message("[user] takes something out of incision on [target]'s [affected.name] with \the [tool]!", \ - "You take [obj] out of incision on [target]'s [affected.name]s with \the [tool]!" ) - affected.implants -= obj - - target.update_hud_sec_implants() - - //Handle possessive brain borers. - if(istype(obj,/mob/living/simple_mob/animal/borer)) - var/mob/living/simple_mob/animal/borer/worm = obj - if(worm.controlling) - target.release_control() - worm.detatch() - worm.leave_host() - else - obj.loc = get_turf(target) - obj.add_blood(target) - obj.update_icon() - if(istype(obj,/obj/item/implant)) - var/obj/item/implant/imp = obj - imp.imp_in = null - imp.implanted = 0 - if(istype(obj, /obj/item/implant/mirror)) - target.mirror = null - else if(istype(tool,/obj/item/nif)){var/obj/item/nif/N = tool;N.unimplant(target)} - else - user.visible_message("[user] removes \the [tool] from [target]'s [affected.name].", \ - "There's something inside [target]'s [affected.name], but you just missed it this time." ) + var/obj/item/obj = input("What do you want to extract?") in affected.implants + + user.visible_message("[user] takes something out of incision on [target]'s [affected.name] with \the [tool]!", \ + "You take [obj] out of incision on [target]'s [affected.name]s with \the [tool]!" ) + + obj.surgically_remove(target, affected) + + if(istype(tool, /obj/item/nif)) + var/obj/item/nif/N = tool + N.unimplant(target) + else user.visible_message("[user] could not find anything inside [target]'s [affected.name], and pulls \the [tool] out.", \ "You could not find anything inside [target]'s [affected.name]." ) From 50b7af466c41d7a44563bbc06a8b69a188a32ff3 Mon Sep 17 00:00:00 2001 From: Niezan Date: Mon, 2 Sep 2024 11:49:34 -0700 Subject: [PATCH 19/35] fixs sign language circuit (#6722) ## About The Pull Request fixes sign language circuit to hear sign language ## Why It's Good For The Game fix bug ## Changelog :cl: fix: sign language circuit now picks up sign language /:cl: --- code/modules/integrated_electronics/subtypes/input.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/integrated_electronics/subtypes/input.dm b/code/modules/integrated_electronics/subtypes/input.dm index 28db56a4ff61..94a68fb24cbd 100644 --- a/code/modules/integrated_electronics/subtypes/input.dm +++ b/code/modules/integrated_electronics/subtypes/input.dm @@ -1080,7 +1080,7 @@ GLOBAL_DATUM_INIT(circuit_translation_context, /datum/translation_context/simple if(signlang) activate_pin(2) -/obj/item/integrated_circuit/input/microphone/sign/hear_signlang(text, verb, datum/language/speaking, mob/M as mob) +/obj/item/integrated_circuit/input/microphone/sign/hear_signlang(mob/M as mob, text, verb, datum/language/speaking) hear_talk(M, text, verb, speaking) return From 9e30d9e47fa985e98e13950ea4f4b99ddb090f77 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Mon, 2 Sep 2024 14:50:01 -0400 Subject: [PATCH 20/35] more loadout slots (#6715) @AlphaM01 asked --- code/__DEFINES/loadout.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/__DEFINES/loadout.dm b/code/__DEFINES/loadout.dm index 97541ba9432d..e8fcbfcf7f4a 100644 --- a/code/__DEFINES/loadout.dm +++ b/code/__DEFINES/loadout.dm @@ -33,9 +33,9 @@ DEFINE_BITFIELD(loadout_customize_flags, list( )) // *DO NOT RAISE THIS. This is for performance reasons.* // -#define LOADOUT_MAX_SLOTS 10 +#define LOADOUT_MAX_SLOTS 16 #define LOADOUT_SLOT_NAME_LENGTH 32 -#define LOADOUT_MAX_ITEMS 30 +#define LOADOUT_MAX_ITEMS 32 /// Used in chargen for accessory loadout limit. #define LOADOUT_MAX_COST 20 /// Used in chargen for accessory loadout limit on holidays. From e6e8ce042efc3f9b7e474db45109e40e4095b154 Mon Sep 17 00:00:00 2001 From: Timothy Teakettle <59849408+timothyteakettle@users.noreply.github.com> Date: Mon, 2 Sep 2024 20:05:33 +0100 Subject: [PATCH 21/35] snowflakes tts more so it works with headsets (#6686) ## About The Pull Request title ## Why It's Good For The Game you cant use tts with headsets right now ## Changelog :cl: fix: snowflakes tts more so it works with headsets /:cl: --------- Co-authored-by: BlueWildrose <57083662+BlueWildrose@users.noreply.github.com> --- code/game/objects/items/devices/text_to_speech.dm | 14 +++++++++++++- code/modules/mob/living/say.dm | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/code/game/objects/items/devices/text_to_speech.dm b/code/game/objects/items/devices/text_to_speech.dm index 71c444de8c53..07aa8e7721a6 100644 --- a/code/game/objects/items/devices/text_to_speech.dm +++ b/code/game/objects/items/devices/text_to_speech.dm @@ -51,8 +51,20 @@ if(activated) var/message = message_args["message"] var/whispering = message_args["whispering"] + var/message_mode = message_args["message_mode"] + var/speech_verb = whispering ? "quietly states" : "states" message_args["cancelled"] = TRUE - audible_message("[icon2html(thing = src, target = world)] \The [name] [whispering ? "quietly states" : "states"], \"[message]\"", null, whispering ? 2 : world.view) + audible_message("[icon2html(thing = src, target = world)] \The [name] [speech_verb], \"[message]\"", null, whispering ? 2 : world.view) if(!whispering) linked_user.say_overhead(message, FALSE, MESSAGE_RANGE_COMBAT_LOUD) + + if(ishuman(source) && message_mode != null) + var/mob/living/carbon/human/H = source + var/obj/item/radio/headset/left_radio = H.l_ear + var/obj/item/radio/headset/right_radio = H.r_ear + if(istype(left_radio)) + left_radio.talk_into(source, message, message_mode, speech_verb, null) + if(istype(right_radio)) + right_radio.talk_into(source, message, message_mode, speech_verb, null) + playsound(src, 'sound/items/tts/stopped_type.ogg', 25, TRUE) diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 8b7ac8908cc5..cac79cb47bed 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -236,7 +236,7 @@ var/list/channel_to_radio_key = new verb = speaking.speech_verb w_not_heard = "[speaking.speech_verb] something [w_adverb]" - var/list/message_args = list("message" = message, "whispering" = whispering, "cancelled" = FALSE) + var/list/message_args = list("message" = message, "whispering" = whispering, "cancelled" = FALSE, "message_mode" = message_mode) SEND_SIGNAL(src, COMSIG_MOB_SAY, message_args) From 1d2ea7819f24081af29bb16d004d0679abb37b67 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Tue, 3 Sep 2024 21:31:26 -0400 Subject: [PATCH 22/35] mob ai: movable tracking (#6703) adds predictive tracker. --- citadel.dme | 3 +- code/__HELPERS/lists/string.dm | 20 ++- code/controllers/subsystem/air.dm | 4 +- .../{movement.dm => movable-movement.dm} | 148 ++++++++++------- code/game/atoms/movable/movable-throw.dm | 2 +- code/game/atoms/movable/movable.dm | 13 ++ code/modules/ai/ai_pathing.dm | 2 +- code/modules/ai/ai_tracking.dm | 156 ++++++++++++++++++ code/modules/mob/mob_defines.dm | 4 +- code/modules/mob/movement.dm | 14 +- code/modules/movespeed/movespeed_modifier.dm | 4 +- 11 files changed, 289 insertions(+), 81 deletions(-) rename code/game/atoms/movable/{movement.dm => movable-movement.dm} (87%) create mode 100644 code/modules/ai/ai_tracking.dm diff --git a/citadel.dme b/citadel.dme index ff216dc4a2ad..1fc66a0118f8 100644 --- a/citadel.dme +++ b/citadel.dme @@ -1030,9 +1030,9 @@ #include "code\game\atoms\movement.dm" #include "code\game\atoms\say.dm" #include "code\game\atoms\vv.dm" +#include "code\game\atoms\movable\movable-movement.dm" #include "code\game\atoms\movable\movable-throw.dm" #include "code\game\atoms\movable\movable.dm" -#include "code\game\atoms\movable\movement.dm" #include "code\game\atoms\movable\pulling.dm" #include "code\game\atoms\movable\vv.dm" #include "code\game\atoms\movable\special\overlay.dm" @@ -2198,6 +2198,7 @@ #include "code\modules\admin\view_variables\topic_list.dm" #include "code\modules\admin\view_variables\view_variables.dm" #include "code\modules\ai\ai_pathing.dm" +#include "code\modules\ai\ai_tracking.dm" #include "code\modules\ai\holders\ai_holder-movement.dm" #include "code\modules\ai\holders\ai_holder-pathfinding.dm" #include "code\modules\ai\holders\ai_holder-scheduling.dm" diff --git a/code/__HELPERS/lists/string.dm b/code/__HELPERS/lists/string.dm index 2acdf2a131b7..f19028b98fad 100644 --- a/code/__HELPERS/lists/string.dm +++ b/code/__HELPERS/lists/string.dm @@ -1,10 +1,22 @@ /** * Returns a list in plain english as a string. * - * * input - (optional) list or null; if null, we use empty_text + * @params + * * input - the list to interpolate into strings + * * nothing_text - the text to emit if the list is empty + * * and_text - the text to emit on the last element instead of a comma + * * comma_text - the glue between elements + * * final_comma_text - the glue between the last two elements; usually empty for use with 'and_text' + * * limit - limit the entries processed. if the limit was reached, the last two elements will not use the usual glue text. + * * limit_text - text to append at the end if we were limited. defaults to "..." */ -/proc/english_list(list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" ) +/proc/english_list(list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "", limit, limit_text = "...") var/total = length(input) + var/limited = FALSE + if(!isnull(limit)) + if(total > limit) + total = limit + limited = TRUE if (!total) return "[nothing_text]" else if (total == 1) @@ -15,13 +27,13 @@ var/output = "" var/index = 1 while (index < total) - if (index == total - 1) + if ((index == (total - 1)) && !limited) comma_text = final_comma_text output += "[input[index]][comma_text]" index++ - return "[output][and_text][input[index]]" + return "[output][limited ? "" : and_text][input[index]][limited? limit_text : ""]" /** * Removes a string from a list. diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm index 8609e1f99b1a..a7b59ec711b0 100644 --- a/code/controllers/subsystem/air.dm +++ b/code/controllers/subsystem/air.dm @@ -137,7 +137,7 @@ SUBSYSTEM_DEF(air) var/timer if(!resumed) if(LAZYLEN(currentrun) != 0) - stack_trace("Currentrun not empty before processing cycle when it should be. [english_list(currentrun)]") + stack_trace("Currentrun not empty before processing cycle when it should be. [english_list(currentrun, limit = 5)]") currentrun = list() if(current_step != null) stack_trace("current_step before processing cycle was [current_step] instead of null") @@ -152,7 +152,7 @@ SUBSYSTEM_DEF(air) // Okay, we're done! Woo! Got thru a whole SSair cycle! if(LAZYLEN(currentrun) != 0) - stack_trace("Currentrun not empty after processing cycle when it should be. [english_list(currentrun.Copy(1, min(currentrun.len, 5)))]") + stack_trace("Currentrun not empty after processing cycle when it should be. [english_list(currentrun, limit = 5)]") currentrun = null if(current_step != SSAIR_DONE) stack_trace("current_step after processing cycle was [current_step] instead of [SSAIR_DONE]") diff --git a/code/game/atoms/movable/movement.dm b/code/game/atoms/movable/movable-movement.dm similarity index 87% rename from code/game/atoms/movable/movement.dm rename to code/game/atoms/movable/movable-movement.dm index a0d0b0c016bf..412d21532059 100644 --- a/code/game/atoms/movable/movement.dm +++ b/code/game/atoms/movable/movable-movement.dm @@ -208,77 +208,96 @@ if(glide_size_override) set_glide_size(glide_size_override, FALSE) - if(loc != newloc) - if (!(direct & (direct - 1))) //Cardinal move - . = ..() - else //Diagonal move, split it into cardinal moves - moving_diagonally = FIRST_DIAG_STEP - var/first_step_dir - // The `&& moving_diagonally` checks are so that a force_move taking - // place due to a Crossed, Bumped, etc. call will interrupt - // the second half of the diagonal movement, or the second attempt - // at a first half if step() fails because we hit something. - if (direct & NORTH) - if (direct & EAST) - if (step(src, NORTH) && moving_diagonally) - first_step_dir = NORTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, EAST) - else if (moving_diagonally && step(src, EAST)) - first_step_dir = EAST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, NORTH) - else if (direct & WEST) - if (step(src, NORTH) && moving_diagonally) - first_step_dir = NORTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, WEST) - else if (moving_diagonally && step(src, WEST)) - first_step_dir = WEST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, NORTH) - else if (direct & SOUTH) - if (direct & EAST) - if (step(src, SOUTH) && moving_diagonally) - first_step_dir = SOUTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, EAST) - else if (moving_diagonally && step(src, EAST)) - first_step_dir = EAST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, SOUTH) - else if (direct & WEST) - if (step(src, SOUTH) && moving_diagonally) - first_step_dir = SOUTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, WEST) - else if (moving_diagonally && step(src, WEST)) - first_step_dir = WEST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, SOUTH) - if(moving_diagonally == SECOND_DIAG_STEP) - if(!.) - setDir(first_step_dir) - else if (!inertia_moving) - inertia_next_move = world.time + inertia_move_delay - newtonian_move(direct) - moving_diagonally = NOT_IN_DIAG_STEP - --in_move - return - else // trying to move to the same place + var/time_since_last_move = world.time - last_move + last_move = world.time + + // trying to move to the same place + if(loc == newloc) if(direct) last_move_dir = direct setDir(direct) --in_move - return TRUE // not moving is technically success + ai_tracking?.track_movement(time_since_last_move, NONE) + // not moving is technically success + return TRUE + + //Cardinal move + if (!(direct & (direct - 1))) + . = ..() + //Diagonal move, split it into cardinal moves + else + moving_diagonally = FIRST_DIAG_STEP + var/first_step_dir + // The `&& moving_diagonally` checks are so that a force_move taking + // place due to a Crossed, Bumped, etc. call will interrupt + // the second half of the diagonal movement, or the second attempt + // at a first half if step() fails because we hit something. + if (direct & NORTH) + if (direct & EAST) + if (step(src, NORTH) && moving_diagonally) + first_step_dir = NORTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, EAST) + else if (moving_diagonally && step(src, EAST)) + first_step_dir = EAST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, NORTH) + else if (direct & WEST) + if (step(src, NORTH) && moving_diagonally) + first_step_dir = NORTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, WEST) + else if (moving_diagonally && step(src, WEST)) + first_step_dir = WEST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, NORTH) + else if (direct & SOUTH) + if (direct & EAST) + if (step(src, SOUTH) && moving_diagonally) + first_step_dir = SOUTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, EAST) + else if (moving_diagonally && step(src, EAST)) + first_step_dir = EAST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, SOUTH) + else if (direct & WEST) + if (step(src, SOUTH) && moving_diagonally) + first_step_dir = SOUTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, WEST) + else if (moving_diagonally && step(src, WEST)) + first_step_dir = WEST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, SOUTH) + if(moving_diagonally == SECOND_DIAG_STEP) + if(!.) + setDir(first_step_dir) + else if (!inertia_moving) + inertia_next_move = world.time + inertia_move_delay + newtonian_move(direct) + moving_diagonally = NOT_IN_DIAG_STEP + --in_move + // track movement if we're no longer in a move; this way this fires only once for diag steps + if(ai_tracking && !in_move) + ai_tracking.track_movement(time_since_last_move, . ? direct : (moving_diagonally == SECOND_DIAG_STEP ? first_step_dir : NONE)) + return if(!loc || (loc == oldloc && oldloc != newloc)) last_move_dir = NONE --in_move - return + // track movement if we're no longer in a move; this way this fires only once for diag steps + if(ai_tracking && !in_move) + ai_tracking.track_movement(time_since_last_move, NONE) + return FALSE - if(. && has_buckled_mobs() && !handle_buckled_mob_movement(loc, direct, glide_size_override)) //movement failed due to buckled mob(s) + //movement failed due to buckled mob(s) + if(. && has_buckled_mobs() && !handle_buckled_mob_movement(loc, direct, glide_size_override)) + last_move_dir = NONE --in_move + // track movement if we're no longer in a move; this way this fires only once for diag steps + if(ai_tracking && !in_move) + ai_tracking.track_movement(time_since_last_move, NONE) return FALSE if(.) @@ -311,6 +330,10 @@ --in_move + // track movement if we're no longer in a move; this way this fires only once for diag steps + if(ai_tracking && !in_move) + ai_tracking.track_movement(time_since_last_move, direct) + // legacy move_speed = world.time - l_move_time l_move_time = world.time @@ -579,6 +602,9 @@ /atom/movable/proc/doMove(atom/destination) . = FALSE + // completely reset ai tracking + ai_tracking?.reset_movement() + ++in_move var/atom/oldloc = loc diff --git a/code/game/atoms/movable/movable-throw.dm b/code/game/atoms/movable/movable-throw.dm index f329f1ab5bd2..742afd66cd8f 100644 --- a/code/game/atoms/movable/movable-throw.dm +++ b/code/game/atoms/movable/movable-throw.dm @@ -186,7 +186,7 @@ // user momentum var/user_speed = L.movement_delay() // 1 decisecond of margin - if(L.last_move_dir && (L.last_move_time >= (world.time - user_speed + 1))) + if(L.last_move_dir && (L.last_self_move >= (world.time - user_speed + 1))) user_speed = max(user_speed, world.tick_lag) // convert to tiles per **decisecond** user_speed = 1/user_speed diff --git a/code/game/atoms/movable/movable.dm b/code/game/atoms/movable/movable.dm index d2e4a5f95cf7..9be084a15cc5 100644 --- a/code/game/atoms/movable/movable.dm +++ b/code/game/atoms/movable/movable.dm @@ -25,6 +25,8 @@ //* AI Holders /// AI holder bound to us var/datum/ai_holder/ai_holder + /// AI tracking datum. Handled by procs in [code/modules/ai/ai_tracking.dm]. + var/datum/ai_tracking/ai_tracking //? Intrinsics /// movable flags - see [code/__DEFINES/_flags/atoms.dm] @@ -36,28 +38,39 @@ /// Set this to TRUE if we are not a [TILE_MOVER]! var/pixel_movement = FALSE /// Whatever we're pulling. + /// /// * this variable is not visible and should not be edited in the map editor. var/tmp/atom/movable/pulling /// Who's currently pulling us + /// /// * this variable is not visible and should not be edited in the map editor. var/tmp/atom/movable/pulledby /// If false makes [CanPass][/atom/proc/CanPass] call [CanPassThrough][/atom/movable/proc/CanPassThrough] on this type instead of using default behaviour + /// /// * this variable is not visible and should not be edited in the map editor. var/tmp/generic_canpass = TRUE /// Pass flags. var/pass_flags = NONE /// movement calls we're in + /// /// * this variable is not visible and should not be edited in the map editor. var/tmp/in_move = 0 /// a direction, or null + /// /// * this variable is not visible and should not be edited in the map editor. var/tmp/moving_diagonally = NOT_IN_DIAG_STEP /// attempt to resume grab after moving instead of before. This is what atom/movable is pulling us during move-from-pulling. + /// /// * this variable is not visible and should not be edited in the map editor. var/tmp/atom/movable/moving_from_pull /// Direction of our last move. + /// /// * this variable is not visible and should not be edited in the map editor. var/tmp/last_move_dir = NONE + /// world.time of our last move + /// + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/last_move /// Our default glide_size. Null to use global default. var/default_glide_size /// Movement types, see [code/__DEFINES/flags/movement.dm] diff --git a/code/modules/ai/ai_pathing.dm b/code/modules/ai/ai_pathing.dm index 9fe14ef0f440..aa949e6b6f3f 100644 --- a/code/modules/ai/ai_pathing.dm +++ b/code/modules/ai/ai_pathing.dm @@ -1,5 +1,5 @@ //* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2024 silicons *// +//* Copyright (c) 2024 Citadel Station Developers *// /** * Pathfinding results. diff --git a/code/modules/ai/ai_tracking.dm b/code/modules/ai/ai_tracking.dm new file mode 100644 index 000000000000..fc962abb4066 --- /dev/null +++ b/code/modules/ai/ai_tracking.dm @@ -0,0 +1,156 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +/** + * Datum to track a movable entity for things like motion and other predictive qualities. + * + * todo: either this or spatial grid signals need to allow for signal on projectile fire / attack / etc. + */ +/datum/ai_tracking + //* System *// + /// last time we were requested + var/last_requested + /// time since last requested we should delete at + var/gc_timeout = 30 SECONDS + /// timerid + var/gc_timerid + + //* Movement *// + + // last movement record + var/movement_record_last + + // basic vel x / y immediate ; tracks within a second or two. only useful for current velocity. // + + /// in tiles / second + var/imm_vel_x = 0 + /// in tiles / second + var/imm_vel_y = 0 + + var/static/imm_vel_multiplier = 0.1 + + // average vel x / y ; uses a fast rolling average. use to suppress attempts at baiting shots. // + + /// in tiles / second + var/fast_vel_x = 0 + /// in tiles / second + var/fast_vel_y = 0 + + var/static/fast_vel_multiplier = 2.5 + +/datum/ai_tracking/Destroy() + if(gc_timerid) + deltimer(gc_timerid) + return ..() + +/** + * Notifies us that we've been requested. + */ +/datum/ai_tracking/proc/keep_alive() + src.last_requested = world.time + +//* Movement *// + +/** + * Tracks movement state + * + * todo: this doesn't support forced movement or anything like that that is faster than a tile second + * + * * time - time since last move + * * dir - direction of move. if it's just a Move() or otherwie standing still, this is NONE. + */ +/datum/ai_tracking/proc/track_movement(time, dir) + var/elapsed = max(world.time - movement_record_last, world.tick_lag) + // flushing changes last record + flush_movement() + + var/sx + var/sy + switch(dir) + if(NORTH) + sy = 1 + if(SOUTH) + sy = -1 + if(EAST) + sx = 1 + if(WEST) + sx = -1 + if(NORTHWEST) + sy = 1 + sx = -1 + if(NORTHEAST) + sy = 1 + sx = 1 + if(SOUTHEAST) + sy = -1 + sx = 1 + if(SOUTHWEST) + sy = -1 + sx = -1 + + /// tiles / ds + var/immediate_speed = 10 / time + var/imm_vel_inverse = 1 - imm_vel_multiplier + imm_vel_x = min(immediate_speed, imm_vel_x * imm_vel_multiplier + immediate_speed * imm_vel_inverse * sx) + imm_vel_y = min(immediate_speed, imm_vel_y * imm_vel_multiplier + immediate_speed * imm_vel_inverse * sy) + + var/fast_new_multiplier = clamp(fast_vel_multiplier * (time / 10), 0, 1) + var/fast_old_multiplier = clamp(1 - fast_new_multiplier, 0, 1) + fast_vel_x = (fast_vel_x) * fast_old_multiplier + imm_vel_x * fast_new_multiplier + fast_vel_y = (fast_vel_y) * fast_old_multiplier + imm_vel_y * fast_new_multiplier + +/** + * Tells us to completely drop movement state. + */ +/datum/ai_tracking/proc/reset_movement() + movement_record_last = world.time + imm_vel_x = imm_vel_y = 0 + fast_vel_x = fast_vel_y = 0 + +/** + * Tells us to flush movement state. + * + * * Always call this before checking movement vars. + */ +/datum/ai_tracking/proc/flush_movement() + if(movement_record_last == world.time) + return + + var/elapsed = world.time - movement_record_last + movement_record_last = world.time + + // penalize immediate speed + imm_vel_x = min(imm_vel_x, 10 / elapsed) + imm_vel_y = min(imm_vel_y, 10 / elapsed) + // penalize fast speed + fast_vel_x = min(fast_vel_x, 20 / elapsed) + fast_vel_y = min(fast_vel_y, 20 / elapsed) + +// todo: get projected tile location (): list(center, radius) +// todo: calculate immediate intercept (atom/source, speed (pixels per second)): list(x, y) +// todo: calculate fast intercept (atom/source, speed (pixels per second)): list(x, y) + +//* /atom/movable API *// + +/** + * Requests our AI tracking datum. + * + * * Will make one if it's not there. + * * Will keep an existing one alive. + */ +/atom/movable/proc/request_ai_tracking() + RETURN_TYPE(/datum/ai_tracking) + if(src.ai_tracking) + return src.ai_tracking + src.ai_tracking = new + src.ai_tracking.keep_alive() + src.ai_tracking.gc_timerid = addtimer(CALLBACK(src, PROC_REF(expire_ai_tracking)), src.ai_tracking.gc_timeout, TIMER_LOOP | TIMER_STOPPABLE) + return src.ai_tracking + +/atom/movable/proc/expire_ai_tracking() + if(!ai_tracking) + return + if(ai_tracking.last_requested + ai_tracking.gc_timeout > world.time) + return + deltimer(ai_tracking.gc_timerid) + QDEL_NULL(ai_tracking) diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index f8d9fc7ab7f9..3c939862f217 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -79,9 +79,9 @@ /// Next world.time we will be able to move. var/move_delay = 0 /// Last world.time we finished a normal, non relay/intercepted move - var/last_move_time = 0 + var/last_self_move = 0 /// Last world.time we turned in our spot without moving (see: facing directions) - var/last_turn = 0 + var/last_self_turn = 0 /// Tracks if we have gravity from environment right now. var/in_gravity diff --git a/code/modules/mob/movement.dm b/code/modules/mob/movement.dm index c02dd0d4d7ec..e9a8621df515 100644 --- a/code/modules/mob/movement.dm +++ b/code/modules/mob/movement.dm @@ -352,14 +352,14 @@ // preserve momentum: for non-evenly-0.5-multiple movespeeds (HELLO, DIAGONAL MOVES), // we need to store how much we're cheated out of our tick and carry it through // make an intelligent guess at if they're trying to keep moving, tho! - if(mob.last_move_time > (world.time - add_delay * 1.25)) + if(mob.last_self_move > (world.time - add_delay * 1.25)) mob.move_delay = old_delay + add_delay else mob.move_delay = world.time + add_delay SMOOTH_GLIDE_SIZE(mob, DELAY_TO_GLIDE_SIZE(add_delay)) - mob.last_move_time = world.time + mob.last_self_move = world.time /mob/proc/SelfMove(turf/T, dir) in_selfmove = TRUE @@ -537,7 +537,7 @@ * * we are not restrained */ /mob/proc/canface() - if(world.time <= last_turn) + if(world.time <= last_self_turn) return FALSE if(stat == DEAD || stat == UNCONSCIOUS) return FALSE @@ -554,7 +554,7 @@ if(!canface()) return FALSE setDir(EAST) - last_turn = world.time + last_self_turn = world.time return TRUE ///Hidden verb to turn west @@ -564,7 +564,7 @@ if(!canface()) return FALSE setDir(WEST) - last_turn = world.time + last_self_turn = world.time return TRUE ///Hidden verb to turn north @@ -574,7 +574,7 @@ if(!canface()) return FALSE setDir(NORTH) - last_turn = world.time + last_self_turn = world.time return TRUE ///Hidden verb to turn south @@ -584,7 +584,7 @@ if(!canface()) return FALSE setDir(SOUTH) - last_turn = world.time + last_self_turn = world.time return TRUE //! Pixel Shifting diff --git a/code/modules/movespeed/movespeed_modifier.dm b/code/modules/movespeed/movespeed_modifier.dm index 0cc2c6291271..97be21d22456 100644 --- a/code/modules/movespeed/movespeed_modifier.dm +++ b/code/modules/movespeed/movespeed_modifier.dm @@ -290,14 +290,14 @@ GLOBAL_LIST_EMPTY(movespeed_modification_cache) cached_multiplicative_slowdown = min(., 10 / MOVESPEED_ABSOLUTE_MINIMUM_TILES_PER_SECOND) if(!client) return - var/diff = (last_move_time - move_delay) - cached_multiplicative_slowdown + var/diff = (last_self_move - move_delay) - cached_multiplicative_slowdown if(diff > 0) // your delay decreases, "give" the delay back to the client if(move_delay > world.time + 1.5) move_delay -= diff #ifdef SMOOTH_MOVEMENT var/timeleft = world.time - move_delay - var/elapsed = world.time - last_move_time + var/elapsed = world.time - last_self_move var/glide_size_current = glide_size if((timeleft <= 0) || (elapsed > 20)) SMOOTH_GLIDE_SIZE(src, 16, TRUE) From c8cef909f782df0f10e6df1bbb7da45b4cd8d3bd Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Tue, 3 Sep 2024 21:32:17 -0400 Subject: [PATCH 23/35] preliminary reorganization of conveyors, disposals, packaging, etc (#6706) factorio gaming --------- Co-authored-by: LordME <58342752+TheLordME@users.noreply.github.com> --- citadel.dme | 26 +- code/__DEFINES/turfs/turfs.dm | 12 + code/modules/industry/README.md | 9 + .../conveyors/conveyor.dm} | 197 +-- .../industry/conveyors/conveyor_switch.dm | 128 ++ code/modules/industry/destination_tagger.dm | 45 + code/modules/industry/disposals/disposal.dm | 28 + .../industry/disposals/disposal/chute.dm | 513 ++++++ .../industry/disposals/disposal/intake.dm | 105 ++ .../industry/disposals/disposal/outlet.dm | 74 + .../disposals/disposal_frame.dm} | 0 .../industry/disposals/disposal_holder.dm | 150 ++ .../industry/disposals/disposal_pipe.dm | 265 +++ .../disposals/disposal_pipe/broken.dm | 13 + .../industry/disposals/disposal_pipe/down.dm | 48 + .../disposals/disposal_pipe/junction.dm | 45 + .../disposals/disposal_pipe/segment.dm | 10 + .../disposals/disposal_pipe/sortjunction.dm | 116 ++ .../disposals/disposal_pipe/tagger.dm | 50 + .../industry/disposals/disposal_pipe/trunk.dm | 99 ++ .../industry/disposals/disposal_pipe/up.dm | 48 + code/modules/industry/package_wrapper.dm | 93 + .../modules/industry/packages/large_parcel.dm | 118 ++ .../modules/industry/packages/small_parcel.dm | 107 ++ code/modules/mining/machine_unloading.dm | 2 +- code/modules/recycling/disposal.dm | 1496 ----------------- code/modules/recycling/sortingmachinery.dm | 469 ------ 27 files changed, 2135 insertions(+), 2131 deletions(-) create mode 100644 code/__DEFINES/turfs/turfs.dm create mode 100644 code/modules/industry/README.md rename code/modules/{recycling/conveyor2.dm => industry/conveyors/conveyor.dm} (53%) create mode 100644 code/modules/industry/conveyors/conveyor_switch.dm create mode 100644 code/modules/industry/destination_tagger.dm create mode 100644 code/modules/industry/disposals/disposal.dm create mode 100644 code/modules/industry/disposals/disposal/chute.dm create mode 100644 code/modules/industry/disposals/disposal/intake.dm create mode 100644 code/modules/industry/disposals/disposal/outlet.dm rename code/modules/{recycling/disposal-construction.dm => industry/disposals/disposal_frame.dm} (100%) create mode 100644 code/modules/industry/disposals/disposal_holder.dm create mode 100644 code/modules/industry/disposals/disposal_pipe.dm create mode 100644 code/modules/industry/disposals/disposal_pipe/broken.dm create mode 100644 code/modules/industry/disposals/disposal_pipe/down.dm create mode 100644 code/modules/industry/disposals/disposal_pipe/junction.dm create mode 100644 code/modules/industry/disposals/disposal_pipe/segment.dm create mode 100644 code/modules/industry/disposals/disposal_pipe/sortjunction.dm create mode 100644 code/modules/industry/disposals/disposal_pipe/tagger.dm create mode 100644 code/modules/industry/disposals/disposal_pipe/trunk.dm create mode 100644 code/modules/industry/disposals/disposal_pipe/up.dm create mode 100644 code/modules/industry/package_wrapper.dm create mode 100644 code/modules/industry/packages/large_parcel.dm create mode 100644 code/modules/industry/packages/small_parcel.dm delete mode 100644 code/modules/recycling/disposal.dm delete mode 100644 code/modules/recycling/sortingmachinery.dm diff --git a/citadel.dme b/citadel.dme index 1fc66a0118f8..faa460e3c9a6 100644 --- a/citadel.dme +++ b/citadel.dme @@ -338,6 +338,7 @@ #include "code\__DEFINES\traits\sources.dm" #include "code\__DEFINES\traits\unsorted.dm" #include "code\__DEFINES\turfs\change_turf.dm" +#include "code\__DEFINES\turfs\turfs.dm" #include "code\__DEFINES\turfs\type_generation.dm" #include "code\__HELPERS\_auxtools_api.dm" #include "code\__HELPERS\_global_objects.dm" @@ -2931,6 +2932,27 @@ #include "code\modules\hydroponics\trays\tray_update_icons.dm" #include "code\modules\identification\identification.dm" #include "code\modules\identification\item_procs.dm" +#include "code\modules\industry\destination_tagger.dm" +#include "code\modules\industry\package_wrapper.dm" +#include "code\modules\industry\conveyors\conveyor.dm" +#include "code\modules\industry\conveyors\conveyor_switch.dm" +#include "code\modules\industry\disposals\disposal.dm" +#include "code\modules\industry\disposals\disposal_frame.dm" +#include "code\modules\industry\disposals\disposal_holder.dm" +#include "code\modules\industry\disposals\disposal_pipe.dm" +#include "code\modules\industry\disposals\disposal\chute.dm" +#include "code\modules\industry\disposals\disposal\intake.dm" +#include "code\modules\industry\disposals\disposal\outlet.dm" +#include "code\modules\industry\disposals\disposal_pipe\broken.dm" +#include "code\modules\industry\disposals\disposal_pipe\down.dm" +#include "code\modules\industry\disposals\disposal_pipe\junction.dm" +#include "code\modules\industry\disposals\disposal_pipe\segment.dm" +#include "code\modules\industry\disposals\disposal_pipe\sortjunction.dm" +#include "code\modules\industry\disposals\disposal_pipe\tagger.dm" +#include "code\modules\industry\disposals\disposal_pipe\trunk.dm" +#include "code\modules\industry\disposals\disposal_pipe\up.dm" +#include "code\modules\industry\packages\large_parcel.dm" +#include "code\modules\industry\packages\small_parcel.dm" #include "code\modules\instruments\instrument_data\_instrument_data.dm" #include "code\modules\instruments\instrument_data\_instrument_key.dm" #include "code\modules\instruments\instrument_data\brass.dm" @@ -4572,10 +4594,6 @@ #include "code\modules\reagents\reagent_containers\syringes_vr.dm" #include "code\modules\reagents\reagent_containers\unidentified_hypospray.dm" #include "code\modules\reagents\reagent_containers\glass\bottle_vr.dm" -#include "code\modules\recycling\conveyor2.dm" -#include "code\modules\recycling\disposal-construction.dm" -#include "code\modules\recycling\disposal.dm" -#include "code\modules\recycling\sortingmachinery.dm" #include "code\modules\research\design.dm" #include "code\modules\research\research.dm" #include "code\modules\research\designs\ai_holders.dm" diff --git a/code/__DEFINES/turfs/turfs.dm b/code/__DEFINES/turfs/turfs.dm new file mode 100644 index 000000000000..5ec000ffce90 --- /dev/null +++ b/code/__DEFINES/turfs/turfs.dm @@ -0,0 +1,12 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +/// Turf crowding limit. +/// +/// Modules that move a lot of objects should respect this, +/// as performance falls quadratically as turfs get more full. +#define TURF_CROWDING_SOFT_LIMIT 15 +/// Turf catastrophic crowding limit. +/// +/// Modules that move a lot of objects should flat out refuse to operate on more objects than this. +#define TURF_CROWDING_HARD_LIMIT 75 diff --git a/code/modules/industry/README.md b/code/modules/industry/README.md new file mode 100644 index 000000000000..cbfb13208edd --- /dev/null +++ b/code/modules/industry/README.md @@ -0,0 +1,9 @@ +# Industry Module + +tl;dr if it belongs in a game of factorio it belongs here + +contains: + +- disposals +- conveyors +- general package code; while this is equally belonging in cargo and here, it's used here more. diff --git a/code/modules/recycling/conveyor2.dm b/code/modules/industry/conveyors/conveyor.dm similarity index 53% rename from code/modules/recycling/conveyor2.dm rename to code/modules/industry/conveyors/conveyor.dm index fb9373ecc1ae..1f7c9b381b16 100644 --- a/code/modules/recycling/conveyor2.dm +++ b/code/modules/industry/conveyors/conveyor.dm @@ -4,6 +4,7 @@ //conveyor2 is pretty much like the original, except it supports corners, but not diverters. //note that corner pieces transfer stuff clockwise when running forward, and anti-clockwise backwards. +// todo: main-like stacks of conveyor belts. /obj/machinery/conveyor icon = 'icons/obj/recycling.dmi' @@ -26,11 +27,6 @@ var/id = "" // the control ID - must match controller ID -/obj/machinery/conveyor/centcom_auto - id = "round_end_belt" - - - // create a conveyor /obj/machinery/conveyor/Initialize(mapload, newdir, on = 0) . = ..() @@ -52,6 +48,12 @@ component_parts += new /obj/item/stack/cable_coil(src,5) RefreshParts() +/obj/machinery/conveyor/examine(mob/user, dist) + . = ..() + // give a hint about catastrophic crowding + if(length(loc?.contents) > TURF_CROWDING_HARD_LIMIT) + . += SPAN_WARNING("There's far too many things on [src] for it to move!") + /obj/machinery/conveyor/proc/setmove() if(operating == FORWARDS) movedir = forwards @@ -97,36 +99,43 @@ AM.set_glide_size(conveyor_glide_size) icon_state = "conveyor[operating]" - // machine process - // move items to the target location +// machine process +// move items to the target location /obj/machinery/conveyor/process(delta_time) if(machine_stat & (BROKEN | NOPOWER)) return if(!operating) return use_power(10) - var/list/affecting = loc.contents - src // moved items will be all in loc - addtimer(CALLBACK(src, PROC_REF(convey), affecting), 1) - -/obj/machinery/conveyor/proc/convey(list/affecting) - var/turf/T = get_step(src, movedir) - if(!T) + // check catastrophic crowding + if(length(loc.contents) > TURF_CROWDING_HARD_LIMIT) + return + // todo: this is still kind of tick-dependent, and will result in issues + // todo: conveyors should be on their own subsystem that lets it run a collect-sweep cycle? + addtimer(CALLBACK(src, PROC_REF(convey), loc.contents.Copy()), 1) + +/** + * Conveys a list of movables. + * + * * This does filter to make sure the movables in question are still in us. + * * This is done in a separate proc so that order of operations from process() is canonical. + */ +/obj/machinery/conveyor/proc/convey(list/atom/movable/to_convey) + var/turf/target_turf = get_step(src, movedir) + if(!target_turf) return - affecting.len = max(min(affecting.len, 150 - T.contents.len), 0) - if(!affecting.len) + // limit items to soft crowding limit + to_convey.len = clamp(TURF_CROWDING_SOFT_LIMIT - length(target_turf.contents), 0, length(to_convey)) + if(!length(to_convey)) return - var/items_moved = 0 - for(var/atom/movable/A in affecting) - if(!A.anchored) - if(A.loc == src.loc) // prevents the object from being affected if it's not currently here. - step(A,movedir) - ++items_moved - if(items_moved >= 50) - break -/* - if((A.loc == loc) && A.has_gravity()) - A.ConveyorMove(movedir) -*/ + // move items + for(var/atom/movable/AM in to_convey) + // todo: movement force check? + if(AM.anchored) + continue + if(AM.loc != loc) + continue + step(AM, movedir) // attack with item, place item on conveyor /obj/machinery/conveyor/attackby(var/obj/item/I, mob/user) @@ -200,137 +209,3 @@ /obj/machinery/conveyor/power_change() ..() update() - -// the conveyor control switch -// -// - -/obj/machinery/conveyor_switch - - name = "conveyor switch" - desc = "A conveyor control switch." - icon = 'icons/obj/recycling.dmi' - icon_state = "switch-off" - var/position = 0 // 0 off, -1 reverse, 1 forward - var/last_pos = -1 // last direction setting - var/operated = 1 // true if just operated - - var/id = "" // must match conveyor IDs to control them - - var/list/conveyors // the list of converyors that are controlled by this switch - anchored = 1 - -/obj/machinery/conveyor_switch/two_way_on - position = 1 - - -/obj/machinery/conveyor_switch/Initialize(mapload) - . = ..() - update() - return INITIALIZE_HINT_LATELOAD - -/obj/machinery/conveyor_switch/LateInitialize() - conveyors = list() - for(var/obj/machinery/conveyor/C in GLOB.machines) - if(C.id == id) - conveyors += C - -// update the icon depending on the position - -/obj/machinery/conveyor_switch/proc/update() - if(position<0) - icon_state = "switch-rev" - else if(position>0) - icon_state = "switch-fwd" - else - icon_state = "switch-off" - - -// timed process -// if the switch changed, update the linked conveyors - -/obj/machinery/conveyor_switch/process(delta_time) - if(!operated) - return - operated = 0 - - for(var/obj/machinery/conveyor/C in conveyors) - C.operating = position - C.setmove() - -// attack with hand, switch position -/obj/machinery/conveyor_switch/attack_hand(mob/user, list/params) - if(!allowed(user)) - to_chat(user, "Access denied.") - return - - if(position == 0) - if(last_pos < 0) - position = 1 - last_pos = 0 - else - position = -1 - last_pos = 0 - else - last_pos = position - position = 0 - - operated = 1 - update() - - // find any switches with same id as this one, and set their positions to match us - for(var/obj/machinery/conveyor_switch/S in GLOB.machines) - if(S.id == src.id) - S.position = position - S.update() - -/obj/machinery/conveyor_switch/attackby(var/obj/item/I, mob/user) - if(default_deconstruction_screwdriver(user, I)) - return - - if(istype(I, /obj/item/weldingtool)) - if(panel_open) - var/obj/item/weldingtool/WT = I - if(!WT.remove_fuel(0, user)) - to_chat(user, "The welding tool must be on to complete this task.") - return - playsound(src, WT.tool_sound, 50, 1) - if(do_after(user, 20 * WT.tool_speed)) - if(!src || !WT.isOn()) return - to_chat(user, "You deconstruct the frame.") - new /obj/item/stack/material/steel( src.loc, 2 ) - qdel(src) - return - - if(istype(I, /obj/item/multitool)) - if(panel_open) - var/input = sanitize(input(usr, "What id would you like to give this conveyor switch?", "Multitool-Conveyor interface", id)) - if(!input) - to_chat(user, "No input found. Please hang up and try your call again.") - return - id = input - conveyors = list() // Clear list so they aren't double added. - for(var/obj/machinery/conveyor/C in GLOB.machines) - if(C.id == id) - conveyors += C - return - -/obj/machinery/conveyor_switch/oneway - var/convdir = 1 //Set to 1 or -1 depending on which way you want the convayor to go. (In other words keep at 1 and set the proper dir on the belts.) - desc = "A conveyor control switch. It appears to only go in one direction." - -// attack with hand, switch position -/obj/machinery/conveyor_switch/oneway/attack_hand(mob/user, list/params) - if(position == 0) - position = convdir - else - position = 0 - - operated = 1 - update() - - // find any switches with same id as this one, and set their positions to match us - for(var/obj/machinery/conveyor_switch/S in GLOB.machines) - if(S.id == src.id) - S.position = position - S.update() diff --git a/code/modules/industry/conveyors/conveyor_switch.dm b/code/modules/industry/conveyors/conveyor_switch.dm new file mode 100644 index 000000000000..6992ecf1ed2b --- /dev/null +++ b/code/modules/industry/conveyors/conveyor_switch.dm @@ -0,0 +1,128 @@ +/obj/machinery/conveyor_switch + + name = "conveyor switch" + desc = "A conveyor control switch." + icon = 'icons/obj/recycling.dmi' + icon_state = "switch-off" + var/position = 0 // 0 off, -1 reverse, 1 forward + var/last_pos = -1 // last direction setting + var/operated = 1 // true if just operated + + var/id = "" // must match conveyor IDs to control them + + var/list/conveyors // the list of converyors that are controlled by this switch + anchored = 1 + +/obj/machinery/conveyor_switch/two_way_on + position = 1 + + +/obj/machinery/conveyor_switch/Initialize(mapload) + . = ..() + update() + return INITIALIZE_HINT_LATELOAD + +/obj/machinery/conveyor_switch/LateInitialize() + conveyors = list() + for(var/obj/machinery/conveyor/C in GLOB.machines) + if(C.id == id) + conveyors += C + +// update the icon depending on the position + +/obj/machinery/conveyor_switch/proc/update() + if(position<0) + icon_state = "switch-rev" + else if(position>0) + icon_state = "switch-fwd" + else + icon_state = "switch-off" + +// timed process +// if the switch changed, update the linked conveyors + +/obj/machinery/conveyor_switch/process(delta_time) + if(!operated) + return + operated = 0 + + for(var/obj/machinery/conveyor/C in conveyors) + C.operating = position + C.setmove() + +// attack with hand, switch position +/obj/machinery/conveyor_switch/attack_hand(mob/user, list/params) + if(!allowed(user)) + to_chat(user, "Access denied.") + return + + if(position == 0) + if(last_pos < 0) + position = 1 + last_pos = 0 + else + position = -1 + last_pos = 0 + else + last_pos = position + position = 0 + + operated = 1 + update() + + // find any switches with same id as this one, and set their positions to match us + for(var/obj/machinery/conveyor_switch/S in GLOB.machines) + if(S.id == src.id) + S.position = position + S.update() + +/obj/machinery/conveyor_switch/attackby(var/obj/item/I, mob/user) + if(default_deconstruction_screwdriver(user, I)) + return + + if(istype(I, /obj/item/weldingtool)) + if(panel_open) + var/obj/item/weldingtool/WT = I + if(!WT.remove_fuel(0, user)) + to_chat(user, "The welding tool must be on to complete this task.") + return + playsound(src, WT.tool_sound, 50, 1) + if(do_after(user, 20 * WT.tool_speed)) + if(!src || !WT.isOn()) return + to_chat(user, "You deconstruct the frame.") + new /obj/item/stack/material/steel( src.loc, 2 ) + qdel(src) + return + + if(istype(I, /obj/item/multitool)) + if(panel_open) + var/input = sanitize(input(usr, "What id would you like to give this conveyor switch?", "Multitool-Conveyor interface", id)) + if(!input) + to_chat(user, "No input found. Please hang up and try your call again.") + return + id = input + conveyors = list() // Clear list so they aren't double added. + for(var/obj/machinery/conveyor/C in GLOB.machines) + if(C.id == id) + conveyors += C + return + +/obj/machinery/conveyor_switch/oneway + var/convdir = 1 //Set to 1 or -1 depending on which way you want the convayor to go. (In other words keep at 1 and set the proper dir on the belts.) + desc = "A conveyor control switch. It appears to only go in one direction." + +// attack with hand, switch position +/obj/machinery/conveyor_switch/oneway/attack_hand(mob/user, list/params) + if(position == 0) + position = convdir + else + position = 0 + + operated = 1 + update() + + // find any switches with same id as this one, and set their positions to match us + for(var/obj/machinery/conveyor_switch/S in GLOB.machines) + if(S.id == src.id) + S.position = position + S.update() diff --git a/code/modules/industry/destination_tagger.dm b/code/modules/industry/destination_tagger.dm new file mode 100644 index 000000000000..afa4406dde4a --- /dev/null +++ b/code/modules/industry/destination_tagger.dm @@ -0,0 +1,45 @@ +/obj/item/destTagger + name = "destination tagger" + desc = "Used to set the destination of properly wrapped packages." + icon = 'icons/obj/device.dmi' + icon_state = "dest_tagger" + var/currTag = 0 + + w_class = WEIGHT_CLASS_SMALL + item_state = "electronic" + slot_flags = SLOT_BELT + +/obj/item/destTagger/ui_state() + return GLOB.inventory_state + +/obj/item/destTagger/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "DestinationTagger", name) + ui.open() + +/obj/item/destTagger/ui_data(mob/user, datum/tgui/ui) + var/list/data = ..() + + data["currTag"] = currTag + data["taggerLocs"] = GLOB.tagger_locations + + return data + +/obj/item/destTagger/attack_self(mob/user) + . = ..() + if(.) + return + ui_interact(user) + +/obj/item/destTagger/ui_act(action, list/params, datum/tgui/ui) + if(..()) + return TRUE + add_fingerprint(usr) + switch(action) + if("set_tag") + var/new_tag = params["tag"] + if(!(new_tag in GLOB.tagger_locations)) + return FALSE + currTag = new_tag + . = TRUE diff --git a/code/modules/industry/disposals/disposal.dm b/code/modules/industry/disposals/disposal.dm new file mode 100644 index 000000000000..d449a288ed28 --- /dev/null +++ b/code/modules/industry/disposals/disposal.dm @@ -0,0 +1,28 @@ +// todo: /machinery/disposal that trunks connect to + +// called when movable is expelled from a disposal pipe or outlet +// by default does nothing, override for special behaviour +/atom/movable/proc/pipe_eject(direction) + return + +// check if mob has client, if so restore client view on eject +/mob/pipe_eject(direction) + update_perspective() + +/obj/effect/debris/cleanable/blood/gibs/pipe_eject(direction) + var/list/dirs + if(direction) + dirs = list( direction, turn(direction, -45), turn(direction, 45)) + else + dirs = GLOB.alldirs.Copy() + + src.streak(dirs) + +/obj/effect/debris/cleanable/blood/gibs/robot/pipe_eject(direction) + var/list/dirs + if(direction) + dirs = list( direction, turn(direction, -45), turn(direction, 45)) + else + dirs = GLOB.alldirs.Copy() + + src.streak(dirs) diff --git a/code/modules/industry/disposals/disposal/chute.dm b/code/modules/industry/disposals/disposal/chute.dm new file mode 100644 index 000000000000..572b165c5b28 --- /dev/null +++ b/code/modules/industry/disposals/disposal/chute.dm @@ -0,0 +1,513 @@ +// Disposal bin +// Holds items for disposal into pipe system +// Draws air from turf, gradually charges internal reservoir +// Once full (~1 atm), uses air resv to flush items into the pipes +// Automatically recharges air (unless off), will flush when ready if pre-set +// Can hold items and human size things, no other draggables +// Toilets are a type of disposal bin for small objects only and work on magic. By magic, I mean torque rotation +///kPa - assume the inside of a dispoal pipe is 1 atm, so that needs to be added. +#define SEND_PRESSURE (700 + ONE_ATMOSPHERE) +///L +#define PRESSURE_TANK_VOLUME 150 +///L/s - 4 m/s using a 15 cm by 15 cm inlet +#define PUMP_MAX_FLOW_RATE 90 +// todo: /obj/machinery/disposal/chute +/obj/machinery/disposal + name = "disposal unit" + desc = "A pneumatic waste disposal unit." + icon = 'icons/obj/pipes/disposal.dmi' + icon_state = "disposal" + atom_colouration_system = FALSE + anchored = TRUE + density = TRUE + pass_flags_self = ATOM_PASS_OVERHEAD_THROW + var/datum/gas_mixture/air_contents // internal reservoir + var/mode = 1 // item mode 0=off 1=charging 2=charged + var/flush = FALSE // true if flush handle is pulled + var/obj/structure/disposalpipe/trunk/trunk = null // the attached pipe trunk + var/flushing = FALSE // true if flushing in progress + var/flush_every_ticks = 30 //Every 30 ticks it will look whether it is ready to flush + var/flush_count = 0 //this var adds 1 once per tick. When it reaches flush_every_ticks it resets and tries to flush. + var/last_sound = 0 + active_power_usage = 2200 //the pneumatic pump power. 3 HP ~ 2200W + idle_power_usage = 100 + +// create a new disposal +// find the attached trunk (if present) and init gas resvr. +/obj/machinery/disposal/Initialize(mapload, newdir) + . = ..() + return INITIALIZE_HINT_LATELOAD + +/obj/machinery/disposal/LateInitialize() + . = ..() + trunk = locate() in src.loc + if(!trunk) + mode = 0 + flush = FALSE + else + trunk.linked = src // link the pipe trunk to self + + air_contents = new/datum/gas_mixture(PRESSURE_TANK_VOLUME) + update() + +/obj/machinery/disposal/Destroy() + eject() + if(trunk) + trunk.linked = null + return ..() + +// attack by item places it in to disposal +/obj/machinery/disposal/attackby(var/obj/item/I, var/mob/user) + if(user.a_intent != INTENT_HELP) + return ..() + + . = CLICKCHAIN_DO_NOT_PROPAGATE + + if(machine_stat & BROKEN || !I || !user) + return + + add_fingerprint(user, 0, I) + if(mode<=0) // It's off + if(I.is_screwdriver()) + if(contents.len > 0) + to_chat(user, "Eject the items first!") + return + if(mode==0) // It's off but still not unscrewed + mode=-1 // Set it to doubleoff l0l + playsound(src, I.tool_sound, 50, 1) + to_chat(user, "You remove the screws around the power connection.") + return + else if(mode==-1) + mode=0 + playsound(src, I.tool_sound, 50, 1) + to_chat(user, "You attach the screws around the power connection.") + return + else if(istype(I, /obj/item/weldingtool) && mode==-1) + if(contents.len > 0) + to_chat(user, "Eject the items first!") + return + var/obj/item/weldingtool/W = I + if(W.remove_fuel(0,user)) + playsound(src, W.tool_sound, 100, 1) + to_chat(user, "You start slicing the floorweld off the disposal unit.") + + if(do_after(user,20 * W.tool_speed)) + if(!src || !W.isOn()) return + to_chat(user, "You sliced the floorweld off the disposal unit.") + var/obj/structure/disposalconstruct/C = new (src.loc) + src.transfer_fingerprints_to(C) + C.ptype = 6 // 6 = disposal unit + C.anchored = 1 + C.density = 1 + C.update() + qdel(src) + return + else + to_chat(user, "You need more welding fuel to complete this task.") + return + + if(istype(I, /obj/item/storage/bag/trash)) + var/obj/item/storage/bag/trash/T = I + to_chat(user, "You empty the bag.") + for(var/obj/item/O in T.contents) + T.obj_storage.remove(O, src) + T.update_icon() + update() + return + + if(istype(I, /obj/item/material/ashtray)) + var/obj/item/material/ashtray/A = I + if(A.contents.len > 0) + user.visible_message("\The [user] empties \the [A.name] into [src].") + for(var/obj/item/O in A.contents) + O.forceMove(src) + A.update_icon() + update() + return + + var/obj/item/grab/G = I + if(istype(G)) // handle grabbed mob + if(ismob(G.affecting)) + var/mob/GM = G.affecting + for (var/mob/V in viewers(usr)) + V.show_message("[usr] starts putting [GM.name] into the disposal.", 3) + if(do_after(usr, 20)) + GM.forceMove(src) + GM.update_perspective() + for (var/mob/C in viewers(src)) + C.show_message("[GM.name] has been placed in \the [src] by [user].", 3) + qdel(G) + + add_attack_logs(user,GM,"Disposals dunked") + return + + if(!user.attempt_insert_item_for_installation(I, src)) + return + + to_chat(user, "You place \the [I] into \the [src].") + for(var/mob/M in viewers(src)) + if(M == user) + continue + M.show_message("[user.name] places \the [I] into \the [src].", 3) + + update() + +// mouse drop another mob or self +// +/obj/machinery/disposal/MouseDroppedOnLegacy(mob/target, mob/user) + if(!CHECK_MOBILITY(user, MOBILITY_CAN_USE) || !istype(target)) + return + if(target.buckled || get_dist(user, src) > 1 || get_dist(user, target) > 1) + return + + //animals cannot put mobs other than themselves into disposal + if(isanimal(user) && target != user) + return + + src.add_fingerprint(user) + var/target_loc = target.loc + var/msg + for (var/mob/V in viewers(usr)) + if(target == user && !user.stat && CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE)) + V.show_message("[usr] starts climbing into the disposal.", 3) + if(target != user && !user.restrained() && !user.stat && CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE)) + if(target.anchored) return + V.show_message("[usr] starts stuffing [target.name] into the disposal.", 3) + if(!do_after(usr, 20)) + return + if(target_loc != target.loc) + return + if(target == user && !user.stat && CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE)) // if drop self, then climbed in + // must be awake, not stunned or whatever + msg = "[user.name] climbs into \the [src]." + to_chat(user, "You climb into \the [src].") + else if(target != user && !user.restrained() && !user.stat && CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE)) + msg = "[user.name] stuffs [target.name] into \the [src]!" + to_chat(user, "You stuff [target.name] into \the [src]!") + + add_attack_logs(user,target,"Disposals dunked") + else + return + target.forceMove(src) + target.update_perspective() + + for (var/mob/C in viewers(src)) + if(C == user) + continue + C.show_message(msg, 3) + + update() + return + +// attempt to move while inside +/obj/machinery/disposal/relaymove(mob/user as mob) + if(user.stat || src.flushing) + return + if(user.loc == src) + src.go_out(user) + return + +// leave the disposal +/obj/machinery/disposal/proc/go_out(mob/user) + user.forceMove(loc) + user.update_perspective() + update() + return + +// ai as human but can't flush +/obj/machinery/disposal/attack_ai(mob/user as mob) + interact(user, 1) + +// human interact with machine +/obj/machinery/disposal/attack_hand(mob/user, list/params) + + if(machine_stat & BROKEN) + return + + if(user && user.loc == src) + to_chat(user, "You cannot reach the controls from inside.") + return + + // Clumsy folks can only flush it. + if(user.IsAdvancedToolUser(1)) + interact(user, 0) + else + flush = !flush + update() + return + +// user interaction +/obj/machinery/disposal/interact(mob/user, var/ai=0) + + src.add_fingerprint(user) + if(machine_stat & BROKEN) + user.unset_machine() + return + + var/dat = "Waste Disposal UnitWaste Disposal Unit


" + + if(!ai) // AI can't pull flush handle + if(flush) + dat += "Disposal handle: Disengage Engaged" + else + dat += "Disposal handle: Disengaged Engage" + + dat += "

Eject contents
" + + if(mode <= 0) + dat += "Pump: Off On
" + else if(mode == 1) + dat += "Pump: Off On (pressurizing)
" + else + dat += "Pump: Off On (idle)
" + + var/per = 100* air_contents.return_pressure() / (SEND_PRESSURE) + + dat += "Pressure: [round(per, 1)]%
" + + + user.set_machine(src) + user << browse(dat, "window=disposal;size=360x170") + onclose(user, "disposal") + +// handle machine interaction + +/obj/machinery/disposal/Topic(href, href_list) + if(usr.loc == src) + to_chat(usr, "You cannot reach the controls from inside.") + return + + if(mode==-1 && !href_list["eject"]) // only allow ejecting if mode is -1 + to_chat(usr, "The disposal units power is disabled.") + return + if(..()) + return + + if(machine_stat & BROKEN) + return + if(usr.stat || usr.restrained() || src.flushing) + return + + if(istype(src.loc, /turf)) + usr.set_machine(src) + + if(href_list["close"]) + usr.unset_machine() + usr << browse(null, "window=disposal") + return + + if(href_list["pump"]) + if(text2num(href_list["pump"])) + mode = 1 + else + mode = 0 + update() + + if(!isAI(usr)) + if(href_list["handle"]) + flush = text2num(href_list["handle"]) + update() + + if(href_list["eject"]) + eject() + else + usr << browse(null, "window=disposal") + usr.unset_machine() + return + return + +// eject the contents of the disposal unit +/obj/machinery/disposal/proc/eject() + for(var/atom/movable/AM in src) + AM.forceMove(src.loc) + AM.pipe_eject(0) + update() + +// update the icon & overlays to reflect mode & status +/obj/machinery/disposal/proc/update() + cut_overlays() + if(machine_stat & BROKEN) + icon_state = "disposal-broken" + mode = 0 + flush = 0 + return + + var/list/overlays_to_add = list() + + // flush handle + if(flush) + overlays_to_add += image('icons/obj/pipes/disposal.dmi', "dispover-handle") + + // only handle is shown if no power + if(machine_stat & NOPOWER || mode == -1) + add_overlay(overlays_to_add) + return + + // check for items in disposal - occupied light + if(contents.len > 0) + overlays_to_add += image('icons/obj/pipes/disposal.dmi', "dispover-full") + + // charging and ready light + if(mode == 1) + overlays_to_add += image('icons/obj/pipes/disposal.dmi', "dispover-charge") + else if(mode == 2) + overlays_to_add += image('icons/obj/pipes/disposal.dmi', "dispover-ready") + + add_overlay(overlays_to_add) + +// timed process +// charge the gas reservoir and perform flush if ready +/obj/machinery/disposal/process(delta_time) + if(!air_contents || (machine_stat & BROKEN)) // nothing can happen if broken + update_use_power(USE_POWER_OFF) + return + + flush_count++ + if( flush_count >= flush_every_ticks ) + if( contents.len ) + if(mode == 2) + spawn(0) + feedback_inc("disposal_auto_flush",1) + flush() + flush_count = 0 + + src.updateDialog() + + if(flush && air_contents.return_pressure() >= SEND_PRESSURE ) // flush can happen even without power + flush() + + if(mode != 1) //if off or ready, no need to charge + update_use_power(USE_POWER_IDLE) + else if(air_contents.return_pressure() >= SEND_PRESSURE) + mode = 2 //if full enough, switch to ready mode + update() + else + src.pressurize() //otherwise charge + +/obj/machinery/disposal/proc/pressurize() + if(machine_stat & NOPOWER) // won't charge if no power + update_use_power(USE_POWER_OFF) + return + + var/atom/L = loc // recharging from loc turf + var/datum/gas_mixture/env = L.return_air() + + var/power_draw = -1 + if(env && env.temperature > 0) + var/transfer_moles = (PUMP_MAX_FLOW_RATE/env.volume)*env.total_moles //group_multiplier is divided out here + power_draw = pump_gas(src, env, air_contents, transfer_moles, active_power_usage) + + if (power_draw > 0) + use_power(power_draw) + +// perform a flush +/obj/machinery/disposal/proc/flush() + + flushing = 1 + flick("[icon_state]-flush", src) + + var/wrapcheck = 0 + var/obj/structure/disposalholder/H = new() // virtual holder object which actually + // travels through the pipes. + //Hacky test to get drones to mail themselves through disposals. + for(var/mob/living/silicon/robot/drone/D in src) + wrapcheck = 1 + + for(var/obj/item/smallDelivery/O in src) + wrapcheck = 1 + + if(wrapcheck == 1) + H.tomail = 1 + + + sleep(10) + if(last_sound < world.time + 1) + playsound(src, 'sound/machines/disposalflush.ogg', 50, 0, 0) + last_sound = world.time + sleep(5) // wait for animation to finish + + + H.init(src, air_contents) // copy the contents of disposer to holder + air_contents = new(PRESSURE_TANK_VOLUME) // new empty gas resv. + + H.start(src) // start the holder processing movement + flushing = 0 + // now reset disposal state + flush = 0 + if(mode == 2) // if was ready, + mode = 1 // switch to charging + update() + return + + +// called when area power changes +/obj/machinery/disposal/power_change() + ..() // do default setting/reset of stat NOPOWER bit + update() // update icon + return + + +/// Called when holder is expelled from a disposal. +/// Should usually only occur if the pipe network is modified. +/obj/machinery/disposal/proc/expel(obj/structure/disposalholder/H) + + var/turf/target + playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) + if(H) // Somehow, someone managed to flush a window which broke mid-transit and caused the disposal to go in an infinite loop trying to expel null, hopefully this fixes it + for(var/atom/movable/AM in H) + target = get_offset_target_turf(src.loc, rand(5)-rand(5), rand(5)-rand(5)) + + AM.forceMove(src.loc) + AM.pipe_eject(0) + if(!istype(AM,/mob/living/silicon/robot/drone)) //Poor drones kept smashing windows and taking system damage being fired out of disposals. ~Z + spawn(1) + if(AM) + AM.throw_at_old(target, 5, 1) + + H.vent_gas(loc) + qdel(H) + +/obj/machinery/disposal/throw_impacted(atom/movable/AM, datum/thrownthing/TT) + if(istype(AM, /obj/item) && !istype(AM, /obj/projectile)) + if(prob(75)) + AM.forceMove(src) + visible_message("\The [AM] lands in \the [src].") + return COMPONENT_THROW_HIT_TERMINATE + else + visible_message("\The [AM] bounces off of \the [src]'s rim!") + return ..() + return ..() + +/obj/machinery/disposal/CanAllowThrough(atom/movable/mover, turf/target) + if(istype(mover, /obj/projectile)) + return TRUE + return ..() + +/obj/machinery/disposal/wall + name = "inset disposal unit" + icon_state = "wall" + + density = FALSE + +/obj/machinery/disposal/wall/Initialize() + . = ..() + + spawn(1 SECOND) // Fixfix for weird interaction with buildmode or other late-spawning. + update() + +/obj/machinery/disposal/wall/update() + ..() + + switch(dir) + if(1) + pixel_x = 0 + pixel_y = -32 + if(2) + pixel_x = 0 + pixel_y = 32 + if(4) + pixel_x = -32 + pixel_y = 0 + if(8) + pixel_x = 32 + pixel_y = 0 diff --git a/code/modules/industry/disposals/disposal/intake.dm b/code/modules/industry/disposals/disposal/intake.dm new file mode 100644 index 000000000000..6b1ba1c81720 --- /dev/null +++ b/code/modules/industry/disposals/disposal/intake.dm @@ -0,0 +1,105 @@ + +// todo: /obj/machinery/disposal/chute/intake +/obj/machinery/disposal/deliveryChute + name = "Delivery chute" + desc = "A chute for big and small packages alike!" + density = 1 + icon_state = "intake" + + var/c_mode = 0 + +/obj/machinery/disposal/deliveryChute/Initialize(mapload, newdir) + . = ..() + spawn(5) + trunk = locate() in src.loc + if(trunk) + trunk.linked = src // link the pipe trunk to self + +/obj/machinery/disposal/deliveryChute/interact() + return + +/obj/machinery/disposal/deliveryChute/update() + return + +/obj/machinery/disposal/deliveryChute/Bumped(var/atom/movable/AM) //Go straight into the chute + if(istype(AM, /obj/projectile) || istype(AM, /obj/effect) || istype(AM, /obj/vehicle/sealed/mecha)) return + switch(dir) + if(NORTH) + if(AM.loc.y != src.loc.y+1) return + if(EAST) + if(AM.loc.x != src.loc.x+1) return + if(SOUTH) + if(AM.loc.y != src.loc.y-1) return + if(WEST) + if(AM.loc.x != src.loc.x-1) return + +// if(istype(AM, has_buckled_mobs()) return // I dont know what im doing @ktoma36 + + if(istype(AM, /obj)) + var/obj/O = AM + O.loc = src + else if(istype(AM, /mob)) + var/mob/M = AM + M.loc = src + src.flush() + +/obj/machinery/disposal/deliveryChute/flush() + flushing = 1 + flick("intake-closing", src) + var/obj/structure/disposalholder/H = new() // virtual holder object which actually + // travels through the pipes. + air_contents = new() // new empty gas resv. + + sleep(10) + playsound(src, 'sound/machines/disposalflush.ogg', 50, 0, 0) + sleep(5) // wait for animation to finish + + H.init(src) // copy the contents of disposer to holder + + H.start(src) // start the holder processing movement + flushing = 0 + // now reset disposal state + flush = 0 + if(mode == 2) // if was ready, + mode = 1 // switch to charging + update() + return + +/obj/machinery/disposal/deliveryChute/attackby(var/obj/item/I, var/mob/user) + if(!I || !user) + return + + if(I.is_screwdriver()) + if(c_mode==0) + c_mode=1 + playsound(src.loc, I.tool_sound, 50, 1) + to_chat(user, "You remove the screws around the power connection.") + return + else if(c_mode==1) + c_mode=0 + playsound(src.loc, I.tool_sound, 50, 1) + to_chat(user, "You attach the screws around the power connection.") + return + else if(istype(I, /obj/item/weldingtool) && c_mode==1) + var/obj/item/weldingtool/W = I + if(W.remove_fuel(0,user)) + playsound(src.loc, W.tool_sound, 50, 1) + to_chat(user, "You start slicing the floorweld off the delivery chute.") + if(do_after(user,20 * W.tool_speed)) + if(!src || !W.isOn()) return + to_chat(user, "You sliced the floorweld off the delivery chute.") + var/obj/structure/disposalconstruct/C = new (src.loc) + C.ptype = 8 // 8 = Delivery chute + C.update() + C.anchored = 1 + C.density = 1 + qdel(src) + return + else + to_chat(user, "You need more welding fuel to complete this task.") + return + +/obj/machinery/disposal/deliveryChute/Destroy() + if(trunk) + trunk.linked = null + ..() diff --git a/code/modules/industry/disposals/disposal/outlet.dm b/code/modules/industry/disposals/disposal/outlet.dm new file mode 100644 index 000000000000..f1a389f60ea8 --- /dev/null +++ b/code/modules/industry/disposals/disposal/outlet.dm @@ -0,0 +1,74 @@ +// the disposal outlet machine +// todo: /obj/machinery/disposal/outlet + +/obj/structure/disposaloutlet + name = "disposal outlet" + desc = "An outlet for the pneumatic disposal system." + icon = 'icons/obj/pipes/disposal.dmi' + icon_state = "outlet" + density = 1 + anchored = 1 + var/active = 0 + var/turf/target // this will be where the output objects are 'thrown' to. + var/mode = 0 + +/obj/structure/disposaloutlet/LateInitialize() + target = get_ranged_target_turf(src, dir, 10) + var/obj/structure/disposalpipe/trunk/trunk = locate() in loc + if(trunk) + trunk.linked = src // link the pipe trunk to self + +// expel the contents of the holder object, then delete it +// called when the holder exits the outlet +/obj/structure/disposaloutlet/proc/expel(obj/structure/disposalholder/H) + + target = get_ranged_target_turf(src, dir, 10) + flick("outlet-open", src) + playsound(src, 'sound/machines/warning-buzzer.ogg', 50, 0, 0) + sleep(20) //wait until correct animation frame + playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) + + if(H) + for(var/atom/movable/AM in H) + AM.forceMove(src.loc) + AM.pipe_eject(dir) + if(!istype(AM,/mob/living/silicon/robot/drone)) //Drones keep smashing windows from being fired out of chutes. Bad for the station. ~Z + spawn(5) + AM.throw_at_old(target, 3, 1) + H.vent_gas(src.loc) + qdel(H) + +/obj/structure/disposaloutlet/attackby(obj/item/I, mob/user) + if(!I || !user) + return + src.add_fingerprint(user, 0, I) + if(I.is_screwdriver()) + if(mode==0) + mode=1 + to_chat(user, "You remove the screws around the power connection.") + playsound(src, I.tool_sound, 50, 1) + return + else if(mode==1) + mode=0 + to_chat(user, "You attach the screws around the power connection.") + playsound(src, I.tool_sound, 50, 1) + return + else if(istype(I, /obj/item/weldingtool) && mode==1) + var/obj/item/weldingtool/W = I + if(W.remove_fuel(0,user)) + playsound(src, W.tool_sound, 100, 1) + to_chat(user, "You start slicing the floorweld off the disposal outlet.") + if(do_after(user,20 * W.tool_speed)) + if(!src || !W.isOn()) return + to_chat(user, "You sliced the floorweld off the disposal outlet.") + var/obj/structure/disposalconstruct/C = new (src.loc) + src.transfer_fingerprints_to(C) + C.ptype = 7 // 7 = outlet + C.update() + C.anchored = 1 + C.density = 1 + qdel(src) + return + else + to_chat(user, "You need more welding fuel to complete this task.") + return diff --git a/code/modules/recycling/disposal-construction.dm b/code/modules/industry/disposals/disposal_frame.dm similarity index 100% rename from code/modules/recycling/disposal-construction.dm rename to code/modules/industry/disposals/disposal_frame.dm diff --git a/code/modules/industry/disposals/disposal_holder.dm b/code/modules/industry/disposals/disposal_holder.dm new file mode 100644 index 000000000000..a89da28a3d27 --- /dev/null +++ b/code/modules/industry/disposals/disposal_holder.dm @@ -0,0 +1,150 @@ +// virtual disposal object +// travels through pipes in lieu of actual items +// contents will be items flushed by the disposal +// this allows the gas flushed to be tracked + +// todo: /atom/movable/disposal_holder +/obj/structure/disposalholder + invisibility = 101 + var/datum/gas_mixture/gas = null // gas used to flush, will appear at exit point + var/active = 0 // true if the holder is moving, otherwise inactive + dir = 0 + var/count = 2048 //*** can travel 2048 steps before going inactive (in case of loops) + var/destinationTag = "" // changes if contains a delivery container + var/tomail = 0 //changes if contains wrapped package + var/hasmob = 0 //If it contains a mob + + var/partialTag = "" //set by a partial tagger the first time round, then put in destinationTag if it goes through again. + +/obj/structure/disposalholder/Destroy() + QDEL_NULL(gas) + active = 0 + return ..() + +// initialize a holder from the contents of a disposal unit +/obj/structure/disposalholder/proc/init(var/obj/machinery/disposal/D, var/datum/gas_mixture/flush_gas) + gas = flush_gas// transfer gas resv. into holder object -- let's be explicit about the data this proc consumes, please. + + //Check for any living mobs trigger hasmob. + //hasmob effects whether the package goes to cargo or its tagged destination. + for(var/mob/living/M in D) + if(M && M.stat != 2 && !istype(M,/mob/living/silicon/robot/drone)) + hasmob = 1 + + //Checks 1 contents level deep. This means that players can be sent through disposals... + //...but it should require a second person to open the package. (i.e. person inside a wrapped locker) + for(var/obj/O in D) + if(O.contents) + for(var/mob/living/M in O.contents) + if(M && M.stat != 2 && !istype(M,/mob/living/silicon/robot/drone)) + hasmob = 1 + + // now everything inside the disposal gets put into the holder + // note AM since can contain mobs or objs + for(var/atom/movable/AM in D) + AM.forceMove(src) + if(istype(AM, /obj/structure/bigDelivery) && !hasmob) + var/obj/structure/bigDelivery/T = AM + src.destinationTag = T.sortTag + if(istype(AM, /obj/item/smallDelivery) && !hasmob) + var/obj/item/smallDelivery/T = AM + src.destinationTag = T.sortTag + //Drones can mail themselves through maint. + if(istype(AM, /mob/living/silicon/robot/drone)) + var/mob/living/silicon/robot/drone/drone = AM + src.destinationTag = drone.mail_destination + +// start the movement process +// argument is the disposal unit the holder started in +/obj/structure/disposalholder/proc/start(var/obj/machinery/disposal/D) + if(!D.trunk) + D.expel(src) // no trunk connected, so expel immediately + return + + forceMove(D.trunk) + active = 1 + setDir(DOWN) + spawn(1) + move() // spawn off the movement process + +// movement process, persists while holder is moving through pipes +/obj/structure/disposalholder/proc/move() + var/obj/structure/disposalpipe/last + // todo: while this is fucking awful? + while(active) + sleep(1) // was 1 + if(!loc) return // check if we got GC'd + + if(hasmob && prob(3)) + for(var/mob/living/H in src) + if(!istype(H,/mob/living/silicon/robot/drone)) //Drones use the mailing code to move through the disposal system, + H.take_overall_damage(20, 0, weapon_descriptor = "blunt trauma")//horribly maim any living creature jumping down disposals. c'est la vie + + var/obj/structure/disposalpipe/curr = loc + last = curr + curr = curr.transfer(src) + + if(!loc) return //side effects + + if(!curr) + last.expel(src, loc, dir) + + // + if(!(count--)) + active = 0 + +// find the turf which should contain the next pipe +/obj/structure/disposalholder/proc/nextloc() + return get_step(loc,dir) + +// find a matching pipe on a turf +/obj/structure/disposalholder/proc/findpipe(var/turf/T) + + if(!T) + return null + + var/fdir = turn(dir, 180) // flip the movement direction + for(var/obj/structure/disposalpipe/P in T) + if(fdir & P.dpdir) // find pipe direction mask that matches flipped dir + return P + // if no matching pipe, return null + return null + +// merge two holder objects +// used when a a holder meets a stuck holder +/obj/structure/disposalholder/proc/merge(var/obj/structure/disposalholder/other) + for(var/atom/movable/AM in other) + AM.forceMove(src) // move everything in other holder to this one + if(ismob(AM)) + var/mob/M = AM + M.update_perspective() + qdel(other) + +/obj/structure/disposalholder/proc/settag(var/new_tag) + destinationTag = new_tag + +/obj/structure/disposalholder/proc/setpartialtag(var/new_tag) + if(partialTag == new_tag) + destinationTag = new_tag + partialTag = "" + else + partialTag = new_tag + +// called when player tries to move while in a pipe +/obj/structure/disposalholder/relaymove(mob/user as mob) + if(!istype(user,/mob/living)) + return + + var/mob/living/U = user + if (U.stat || U.last_special <= world.time) + return + U.last_special = world.time+100 + + if(loc) + for (var/mob/M in hearers(src.loc.loc)) + to_chat(M, "CLONG, clong!") + playsound(src, 'sound/effects/clang.ogg', 50, 0, 0) + +// called to vent all gas in holder to a location +/obj/structure/disposalholder/proc/vent_gas(var/atom/location) + location.assume_air(gas) // vent all gas to turf diff --git a/code/modules/industry/disposals/disposal_pipe.dm b/code/modules/industry/disposals/disposal_pipe.dm new file mode 100644 index 000000000000..ec16874fdf64 --- /dev/null +++ b/code/modules/industry/disposals/disposal_pipe.dm @@ -0,0 +1,265 @@ +// Disposal pipes +/// todo: /obj/structure/disposal_pipe +/obj/structure/disposalpipe + icon = 'icons/obj/pipes/disposal.dmi' + name = "disposal pipe" + desc = "An underfloor disposal pipe." + anchored = 1 + density = 0 + hides_underfloor = OBJ_UNDERFLOOR_ALWAYS + dir = 0 // dir will contain dominant direction for junction pipes + plane = TURF_PLANE + layer = DISPOSAL_LAYER // slightly lower than wires and other pipes. + + integrity = 100 + integrity_max = 100 + + #ifdef IN_MAP_EDITOR // Display disposal pipes etc. above walls in map editors. + alpha = 128 // Set for the benefit of mapping. + #endif + + /// Bitmask of pipe directions. + var/dpdir = 0 + var/sortType = "" + var/subtype = 0 + // new pipe, set the icon_state as on map + +/obj/structure/disposalpipe/Initialize(mapload, dir) + . = ..() + base_icon_state = icon_state + if(!isnull(dir)) + setDir(dir) + +// pipe is deleted +// ensure if holder is present, it is expelled +/obj/structure/disposalpipe/Destroy() + var/obj/structure/disposalholder/H = locate() in src + if(H) + // holder was present + H.active = 0 + var/turf/T = src.loc + if(T.density) + // deleting pipe is inside a dense turf (wall) + // this is unlikely, but just dump out everything into the turf in case + + for(var/atom/movable/AM in H) + AM.forceMove(T) + AM.pipe_eject(0) + qdel(H) + ..() + return + + // otherwise, do normal expel from turf + if(H) + expel(H, T, 0) + ..() + +// returns the direction of the next pipe object, given the entrance dir +// by default, returns the bitmask of remaining directions +/obj/structure/disposalpipe/proc/nextdir(var/fromdir) + return dpdir & (~turn(fromdir, 180)) + +// transfer the holder through this pipe segment +// overriden for special behaviour +// +/obj/structure/disposalpipe/proc/transfer(var/obj/structure/disposalholder/H) + var/nextdir = nextdir(H.dir) + H.setDir(nextdir) + var/turf/T = H.nextloc() + var/obj/structure/disposalpipe/P = H.findpipe(T) + + if(P) + // find other holder in next loc, if inactive merge it with current + var/obj/structure/disposalholder/H2 = locate() in P + if(H2 && !H2.active) + H.merge(H2) + + H.forceMove(P) + else // if wasn't a pipe, then set loc to turf + H.forceMove(T) + return null + + return P + +// update actual icon_state depending on visibility +// if invisible, append "f" to icon_state to show faded version +// this will be revealed if a T-scanner is used +// if visible, use regular icon_state +/obj/structure/disposalpipe/proc/updateicon() + icon_state = base_icon_state + +// expel the held objects into a turf +// called when there is a break in the pipe +/obj/structure/disposalpipe/proc/expel(var/obj/structure/disposalholder/H, var/turf/T, var/direction) + if(!istype(H)) + return + + // Empty the holder if it is expelled into a dense turf. + // Leaving it intact and sitting in a wall is stupid. + if(T.density) + for(var/atom/movable/AM in H) + AM.loc = T + AM.pipe_eject(0) + qdel(H) + return + + if(!T.is_plating() && istype(T,/turf/simulated/floor)) //intact floor, pop the tile + var/turf/simulated/floor/F = T + F.break_tile() + new /obj/item/stack/tile(H) // add to holder so it will be thrown with other stuff + + var/turf/target + if(direction) // direction is specified + if(istype(T, /turf/space)) // if ended in space, then range is unlimited + target = get_edge_target_turf(T, direction) + else // otherwise limit to 10 tiles + target = get_ranged_target_turf(T, direction, 10) + + playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) + if(H) + for(var/atom/movable/AM in H) + AM.forceMove(T) + AM.pipe_eject(direction) + spawn(1) + if(AM) + AM.throw_at_old(target, 100, 1) + H.vent_gas(T) + qdel(H) + + else // no specified direction, so throw in random direction + + playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) + if(H) + for(var/atom/movable/AM in H) + target = get_offset_target_turf(T, rand(5)-rand(5), rand(5)-rand(5)) + + AM.forceMove(T) + AM.pipe_eject(0) + spawn(1) + if(AM) + AM.throw_at_old(target, 5, 1) + + H.vent_gas(T) // all gas vent to turf + qdel(H) + +/obj/structure/disposalpipe/deconstructed(method) + . = ..() + src.invisibility = 101 // make invisible (since we won't delete the pipe immediately) + var/obj/structure/disposalholder/H = locate() in src + if(H) + // holder was present + H.active = 0 + var/turf/T = src.loc + if(T.density) + // broken pipe is inside a dense turf (wall) + // this is unlikely, but just dump out everything into the turf in case + + for(var/atom/movable/AM in H) + AM.forceMove(T) + AM.pipe_eject(0) + qdel(H) + return + + // otherwise, do normal expel from turf + if(H) + expel(H, T, 0) + return ..() + +/obj/structure/disposalpipe/drop_products(method, atom/where) + . = ..() + if(method != ATOM_DECONSTRUCT_DISASSEMBLED) + new /obj/structure/disposalpipe/broken(where, dir) + +//attack by item +//weldingtool: unfasten and convert to obj/disposalconstruct + +/obj/structure/disposalpipe/attackby(var/obj/item/I, var/mob/user) + + var/turf/T = src.loc + if(!T.is_plating()) + return // prevent interaction with T-scanner revealed pipes + src.add_fingerprint(user, 0, I) + if(istype(I, /obj/item/weldingtool)) + var/obj/item/weldingtool/W = I + + if(W.remove_fuel(0,user)) + playsound(src, W.tool_sound, 50, 1) + // check if anything changed over 2 seconds + var/turf/uloc = user.loc + var/atom/wloc = W.loc + to_chat(user, "Slicing the disposal pipe.") + sleep(30) + if(!W.isOn()) return + if(user.loc == uloc && wloc == W.loc) + welded() + else + to_chat(user, "You must stay still while welding the pipe.") + else + to_chat(user, "You need more welding fuel to cut the pipe.") + return + +// called when pipe is cut with welder +/obj/structure/disposalpipe/proc/welded() + + var/obj/structure/disposalconstruct/C = new (src.loc) + switch(base_icon_state) + if("pipe-s") + C.ptype = 0 + if("pipe-c") + C.ptype = 1 + if("pipe-j1") + C.ptype = 2 + if("pipe-j2") + C.ptype = 3 + if("pipe-y") + C.ptype = 4 + if("pipe-t") + C.ptype = 5 + if("pipe-j1s") + C.ptype = 9 + C.sortType = sortType + if("pipe-j2s") + C.ptype = 10 + C.sortType = sortType +///// Z-Level stuff + if("pipe-u") + C.ptype = 11 + if("pipe-d") + C.ptype = 12 +///// Z-Level stuff + if("pipe-tagger") + C.ptype = 13 + if("pipe-tagger-partial") + C.ptype = 14 + C.subtype = src.subtype + src.transfer_fingerprints_to(C) + C.setDir(dir) + C.density = 0 + C.anchored = 1 + C.update() + + qdel(src) + +// pipe is deleted +// ensure if holder is present, it is expelled +/obj/structure/disposalpipe/Destroy() + var/obj/structure/disposalholder/H = locate() in src + if(H) + // holder was present + H.active = 0 + var/turf/T = src.loc + if(T.density) + // deleting pipe is inside a dense turf (wall) + // this is unlikely, but just dump out everything into the turf in case + + for(var/atom/movable/AM in H) + AM.forceMove(T) + AM.pipe_eject(0) + qdel(H) + ..() + return + + // otherwise, do normal expel from turf + if(H) + expel(H, T, 0) + ..() diff --git a/code/modules/industry/disposals/disposal_pipe/broken.dm b/code/modules/industry/disposals/disposal_pipe/broken.dm new file mode 100644 index 000000000000..2914d4da38e4 --- /dev/null +++ b/code/modules/industry/disposals/disposal_pipe/broken.dm @@ -0,0 +1,13 @@ +// a broken pipe +/obj/structure/disposalpipe/broken + icon_state = "pipe-b" + dpdir = 0 // broken pipes have dpdir=0 so they're not found as 'real' pipes + // i.e. will be treated as an empty turf + desc = "A broken piece of disposal pipe." + +// called when welded +// for broken pipe, remove and turn into scrap +/obj/structure/disposalpipe/broken/welded() +// var/obj/item/scrap/S = new(src.loc) +// S.set_components(200,0,0) + qdel(src) diff --git a/code/modules/industry/disposals/disposal_pipe/down.dm b/code/modules/industry/disposals/disposal_pipe/down.dm new file mode 100644 index 000000000000..5bc47b24e8a1 --- /dev/null +++ b/code/modules/industry/disposals/disposal_pipe/down.dm @@ -0,0 +1,48 @@ +/obj/structure/disposalpipe/down + icon_state = "pipe-d" + +/obj/structure/disposalpipe/down/New() + ..() + dpdir = dir + return + +/obj/structure/disposalpipe/down/nextdir(fromdir) + var/nextdir + if(fromdir == 12) + nextdir = dir + else + nextdir = 11 + return nextdir + +/obj/structure/disposalpipe/down/transfer(obj/structure/disposalholder/H) + var/nextdir = nextdir(H.dir) + H.dir = nextdir + + var/turf/T + var/obj/structure/disposalpipe/P + + if(nextdir == 11) + T = get_vertical_step(src, DOWN) + if(!T) + H.forceMove(src.loc) + return + else + for(var/obj/structure/disposalpipe/up/F in T) + P = F + + else + T = get_step(src.loc, H.dir) + P = H.findpipe(T) + + if(P) + // find other holder in next loc, if inactive merge it with current + var/obj/structure/disposalholder/H2 = locate() in P + if(H2 && !H2.active) + H.merge(H2) + + H.forceMove(P) + else // if wasn't a pipe, then set loc to turf + H.forceMove(T) + return null + + return P diff --git a/code/modules/industry/disposals/disposal_pipe/junction.dm b/code/modules/industry/disposals/disposal_pipe/junction.dm new file mode 100644 index 000000000000..0d1937f21107 --- /dev/null +++ b/code/modules/industry/disposals/disposal_pipe/junction.dm @@ -0,0 +1,45 @@ +/obj/structure/disposalpipe/junction/yjunction + icon_state = "pipe-y" + +//a three-way junction with dir being the dominant direction +/obj/structure/disposalpipe/junction + icon_state = "pipe-j1" + +/obj/structure/disposalpipe/junction/New() + ..() + if(icon_state == "pipe-j1") + dpdir = dir | turn(dir, -90) | turn(dir,180) + else if(icon_state == "pipe-j2") + dpdir = dir | turn(dir, 90) | turn(dir,180) + else // pipe-y + dpdir = dir | turn(dir,90) | turn(dir, -90) + +// next direction to move +// if coming in from secondary dirs, then next is primary dir +// if coming in from primary dir, then next is equal chance of other dirs +/obj/structure/disposalpipe/junction/nextdir(var/fromdir) + var/flipdir = turn(fromdir, 180) + if(flipdir != dir) // came from secondary dir + return dir // so exit through primary + else // came from primary + // so need to choose either secondary exit + var/mask = ..(fromdir) + + // find a bit which is set + var/setbit = 0 + if(mask & NORTH) + setbit = NORTH + else if(mask & SOUTH) + setbit = SOUTH + else if(mask & EAST) + setbit = EAST + else + setbit = WEST + + if(prob(50)) // 50% chance to choose the found bit or the other one + return setbit + else + return mask & (~setbit) + +/obj/structure/disposalpipe/junction/flipped //for easier and cleaner mapping + icon_state = "pipe-j2" diff --git a/code/modules/industry/disposals/disposal_pipe/segment.dm b/code/modules/industry/disposals/disposal_pipe/segment.dm new file mode 100644 index 000000000000..eb3bad08b814 --- /dev/null +++ b/code/modules/industry/disposals/disposal_pipe/segment.dm @@ -0,0 +1,10 @@ +// a straight or bent segment +/obj/structure/disposalpipe/segment + icon_state = "pipe-s" + +/obj/structure/disposalpipe/segment/New() + ..() + if(icon_state == "pipe-s") + dpdir = dir | turn(dir, 180) + else + dpdir = dir | turn(dir, -90) diff --git a/code/modules/industry/disposals/disposal_pipe/sortjunction.dm b/code/modules/industry/disposals/disposal_pipe/sortjunction.dm new file mode 100644 index 000000000000..02b2f813936d --- /dev/null +++ b/code/modules/industry/disposals/disposal_pipe/sortjunction.dm @@ -0,0 +1,116 @@ +//a three-way junction that sorts objects +/obj/structure/disposalpipe/sortjunction + name = "sorting junction" + icon_state = "pipe-j1s" + desc = "An underfloor disposal pipe with a package sorting mechanism." + + var/posdir = 0 + var/negdir = 0 + var/sortdir = 0 + +/obj/structure/disposalpipe/sortjunction/proc/updatedesc() + desc = initial(desc) + if(sortType) + desc += "\nIt's filtering objects with the '[sortType]' tag." + +/obj/structure/disposalpipe/sortjunction/proc/updatename() + if(sortType) + name = "[initial(name)] ([sortType])" + else + name = initial(name) + +/obj/structure/disposalpipe/sortjunction/proc/updatedir() + posdir = dir + negdir = turn(posdir, 180) + + if(icon_state == "pipe-j1s") + sortdir = turn(posdir, -90) + else if(icon_state == "pipe-j2s") + sortdir = turn(posdir, 90) + + dpdir = sortdir | posdir | negdir + +/obj/structure/disposalpipe/sortjunction/New() + . = ..() + if(sortType) GLOB.tagger_locations |= sortType + + updatedir() + updatename() + updatedesc() + +/obj/structure/disposalpipe/sortjunction/attackby(obj/item/I, mob/user) + if(..()) + return + + if(istype(I, /obj/item/destTagger)) + var/obj/item/destTagger/O = I + + if(O.currTag)// Tag set + sortType = O.currTag + playsound(src.loc, 'sound/machines/twobeep.ogg', 100, 1) + to_chat(user, "Changed filter to '[sortType]'.") + updatename() + updatedesc() + +/obj/structure/disposalpipe/sortjunction/proc/divert_check(checkTag) + return sortType == checkTag + +// next direction to move +// if coming in from negdir, then next is primary dir or sortdir +// if coming in from posdir, then flip around and go back to posdir +// if coming in from sortdir, go to posdir +/obj/structure/disposalpipe/sortjunction/nextdir(fromdir, sortTag) + if(fromdir != sortdir) // probably came from the negdir + if(divert_check(sortTag)) + return sortdir + else + return posdir + else // came from sortdir + // so go with the flow to positive direction + return posdir + +/obj/structure/disposalpipe/sortjunction/transfer(var/obj/structure/disposalholder/H) + var/nextdir = nextdir(H.dir, H.destinationTag) + H.setDir(nextdir) + var/turf/T = H.nextloc() + var/obj/structure/disposalpipe/P = H.findpipe(T) + + if(P) + // find other holder in next loc, if inactive merge it with current + var/obj/structure/disposalholder/H2 = locate() in P + if(H2 && !H2.active) + H.merge(H2) + + H.forceMove(P) + else // if wasn't a pipe, then set loc to turf + H.forceMove(T) + return null + + return P + +//a three-way junction that filters all wrapped and tagged items +/obj/structure/disposalpipe/sortjunction/wildcard + name = "wildcard sorting junction" + desc = "An underfloor disposal pipe which filters all wrapped and tagged items." + subtype = 1 + +/obj/structure/disposalpipe/sortjunction/wildcard/divert_check(checkTag) + return checkTag != "" + +//junction that filters all untagged items +/obj/structure/disposalpipe/sortjunction/untagged + name = "untagged sorting junction" + desc = "An underfloor disposal pipe which filters all untagged items." + subtype = 2 + +/obj/structure/disposalpipe/sortjunction/untagged/divert_check(checkTag) + return checkTag == "" + +/obj/structure/disposalpipe/sortjunction/flipped //for easier and cleaner mapping + icon_state = "pipe-j2s" + +/obj/structure/disposalpipe/sortjunction/wildcard/flipped + icon_state = "pipe-j2s" + +/obj/structure/disposalpipe/sortjunction/untagged/flipped + icon_state = "pipe-j2s" diff --git a/code/modules/industry/disposals/disposal_pipe/tagger.dm b/code/modules/industry/disposals/disposal_pipe/tagger.dm new file mode 100644 index 000000000000..8a24e6badf7c --- /dev/null +++ b/code/modules/industry/disposals/disposal_pipe/tagger.dm @@ -0,0 +1,50 @@ +/obj/structure/disposalpipe/tagger + name = "package tagger" + icon_state = "pipe-tagger" + var/sort_tag = "" + var/partial = 0 + +/obj/structure/disposalpipe/tagger/proc/updatedesc() + desc = initial(desc) + if(sort_tag) + desc += "\nIt's tagging objects with the '[sort_tag]' tag." + +/obj/structure/disposalpipe/tagger/proc/updatename() + if(sort_tag) + name = "[initial(name)] ([sort_tag])" + else + name = initial(name) + +/obj/structure/disposalpipe/tagger/New() + . = ..() + dpdir = dir | turn(dir, 180) + if(sort_tag) GLOB.tagger_locations |= sort_tag + updatename() + updatedesc() + +/obj/structure/disposalpipe/tagger/attackby(obj/item/I, mob/user) + if(..()) + return + + if(istype(I, /obj/item/destTagger)) + var/obj/item/destTagger/O = I + + if(O.currTag)// Tag set + sort_tag = O.currTag + playsound(src.loc, 'sound/machines/twobeep.ogg', 100, 1) + to_chat(user, "Changed tag to '[sort_tag]'.") + updatename() + updatedesc() + +/obj/structure/disposalpipe/tagger/transfer(obj/structure/disposalholder/H) + if(sort_tag) + if(partial) + H.setpartialtag(sort_tag) + else + H.settag(sort_tag) + return ..() + +/obj/structure/disposalpipe/tagger/partial //needs two passes to tag + name = "partial package tagger" + icon_state = "pipe-tagger-partial" + partial = 1 diff --git a/code/modules/industry/disposals/disposal_pipe/trunk.dm b/code/modules/industry/disposals/disposal_pipe/trunk.dm new file mode 100644 index 000000000000..3d0b03a73a16 --- /dev/null +++ b/code/modules/industry/disposals/disposal_pipe/trunk.dm @@ -0,0 +1,99 @@ +//a trunk joining to a disposal bin or outlet on the same turf +/obj/structure/disposalpipe/trunk + icon_state = "pipe-t" + var/obj/linked // the linked obj/machinery/disposal or obj/disposaloutlet + +/obj/structure/disposalpipe/trunk/Initialize(mapload) + . = ..() + dpdir = dir + return INITIALIZE_HINT_LATELOAD + +/obj/structure/disposalpipe/trunk/LateInitialize() + . = ..() + getlinked() +/obj/structure/disposalpipe/trunk/proc/getlinked() + linked = null + var/obj/machinery/disposal/D = locate() in src.loc + if(D) + linked = D + if (!D.trunk) + D.trunk = src + + var/obj/structure/disposaloutlet/O = locate() in src.loc + if(O) + linked = O + return + + // Override attackby so we disallow trunkremoval when somethings ontop +/obj/structure/disposalpipe/trunk/attackby(obj/item/I, mob/user) + + //Disposal bins or chutes + /* + These shouldn't be required + var/obj/machinery/disposal/D = locate() in src.loc + if(D && D.anchored) + return + + //Disposal outlet + var/obj/structure/disposaloutlet/O = locate() in src.loc + if(O && O.anchored) + return + */ + + //Disposal constructors + var/obj/structure/disposalconstruct/C = locate() in src.loc + if(C && C.anchored) + return + + var/turf/T = src.loc + if(!T.is_plating()) + return // prevent interaction with T-scanner revealed pipes + src.add_fingerprint(user, 0, I) + if(istype(I, /obj/item/weldingtool)) + var/obj/item/weldingtool/W = I + + if(W.remove_fuel(0,user)) + playsound(src, W.tool_sound, 100, 1) + // check if anything changed over 2 seconds + var/turf/uloc = user.loc + var/atom/wloc = W.loc + to_chat(user, "Slicing the disposal pipe.") + sleep(30) + if(!W.isOn()) return + if(user.loc == uloc && wloc == W.loc) + welded() + else + to_chat(user, "You must stay still while welding the pipe.") + else + to_chat(user, "You need more welding fuel to cut the pipe.") + return + + // would transfer to next pipe segment, but we are in a trunk + // if not entering from disposal bin, + // transfer to linked object (outlet or bin) + +/obj/structure/disposalpipe/trunk/transfer(var/obj/structure/disposalholder/H) + + if(H.dir == DOWN) // we just entered from a disposer + return ..() // so do base transfer proc + // otherwise, go to the linked object + if(linked) + var/obj/structure/disposaloutlet/O = linked + if(istype(O) && (H)) + O.expel(H) // expel at outlet + else + var/obj/machinery/disposal/D = linked + if(H) + D.expel(H) // expel at disposal + else + if(H) + src.expel(H, src.loc, 0) // expel at turf + return null + + // nextdir + +/obj/structure/disposalpipe/trunk/nextdir(var/fromdir) + if(fromdir == DOWN) + return dir + else + return 0 diff --git a/code/modules/industry/disposals/disposal_pipe/up.dm b/code/modules/industry/disposals/disposal_pipe/up.dm new file mode 100644 index 000000000000..31816509527d --- /dev/null +++ b/code/modules/industry/disposals/disposal_pipe/up.dm @@ -0,0 +1,48 @@ +/obj/structure/disposalpipe/up + icon_state = "pipe-u" + +/obj/structure/disposalpipe/up/New() + ..() + dpdir = dir + return + +/obj/structure/disposalpipe/up/nextdir(fromdir) + var/nextdir + if(fromdir == 11) + nextdir = dir + else + nextdir = 12 + return nextdir + +/obj/structure/disposalpipe/up/transfer(var/obj/structure/disposalholder/H) + var/nextdir = nextdir(H.dir) + H.setDir(nextdir) + + var/turf/T + var/obj/structure/disposalpipe/P + + if(nextdir == 12) + T = get_vertical_step(src, UP) + if(!T) + H.forceMove(loc) + return + else + for(var/obj/structure/disposalpipe/down/F in T) + P = F + + else + T = get_step(src.loc, H.dir) + P = H.findpipe(T) + + if(P) + // find other holder in next loc, if inactive merge it with current + var/obj/structure/disposalholder/H2 = locate() in P + if(H2 && !H2.active) + H.merge(H2) + + H.forceMove(P) + else // if wasn't a pipe, then set loc to turf + H.forceMove(T) + return null + + return P diff --git a/code/modules/industry/package_wrapper.dm b/code/modules/industry/package_wrapper.dm new file mode 100644 index 000000000000..6d18bf75cbef --- /dev/null +++ b/code/modules/industry/package_wrapper.dm @@ -0,0 +1,93 @@ +// todo: /obj/item/package_wrapper +/obj/item/packageWrap + name = "package wrapper" + icon = 'icons/obj/items.dmi' + icon_state = "deliveryPaper" + w_class = WEIGHT_CLASS_NORMAL + var/amount = 25 + +/obj/item/packageWrap/afterattack(atom/movable/target, mob/user, clickchain_flags, list/params) + if(!(clickchain_flags & CLICKCHAIN_HAS_PROXIMITY)) return + if(!istype(target)) //this really shouldn't be necessary (but it is). -Pete + return + if(istype(target, /obj/item/smallDelivery) || istype(target,/obj/structure/bigDelivery) \ + || istype(target, /obj/item/gift) || istype(target, /obj/item/evidencebag)) + return + if(target.anchored) + return + if(target in user) + return + if(user in target) //no wrapping closets that you are inside - it's not physically possible + return + + user.attack_log += "\[[time_stamp()]\] Has used [name] on \ref[target]" + + + if (istype(target, /obj/item) && !(istype(target, /obj/item/storage) && !istype(target,/obj/item/storage/box))) + var/obj/item/O = target + if (src.amount > 1) + var/obj/item/smallDelivery/P = new /obj/item/smallDelivery(get_turf(O.loc)) //Aaannd wrap it up! + if(!istype(O.loc, /turf)) + if(user.client) + user.client.screen -= O + P.wrapped = O + O.forceMove(P) + P.set_weight_class(O.w_class) + var/i = round(O.get_weight_class()) + if(i in list(1,2,3,4,5)) + P.icon_state = "deliverycrate[i]" + switch(i) + if(1) P.name = "tiny parcel" + if(3) P.name = "normal-sized parcel" + if(4) P.name = "large parcel" + if(5) P.name = "huge parcel" + if(i < 1) + P.icon_state = "deliverycrate1" + P.name = "tiny parcel" + if(i > 5) + P.icon_state = "deliverycrate5" + P.name = "huge parcel" + P.add_fingerprint(usr) + O.add_fingerprint(usr) + src.add_fingerprint(usr) + src.amount -= 1 + user.visible_message("\The [user] wraps \a [target] with \a [src].",\ + "You wrap \the [target], leaving [amount] units of paper on \the [src].",\ + "You hear someone taping paper around a small object.") + else if (istype(target, /obj/structure/closet/crate)) + var/obj/structure/closet/crate/O = target + if (src.amount > 3 && !O.opened) + var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc)) + P.icon_state = "deliverycrate" + P.wrapped = O + O.loc = P + src.amount -= 3 + user.visible_message("\The [user] wraps \a [target] with \a [src].",\ + "You wrap \the [target], leaving [amount] units of paper on \the [src].",\ + "You hear someone taping paper around a large object.") + else if(src.amount < 3) + to_chat(user, "You need more paper.") + else if (istype (target, /obj/structure/closet)) + var/obj/structure/closet/O = target + if (src.amount > 3 && !O.opened) + var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc)) + P.wrapped = O + O.sealed = 1 + O.loc = P + src.amount -= 3 + user.visible_message("\The [user] wraps \a [target] with \a [src].",\ + "You wrap \the [target], leaving [amount] units of paper on \the [src].",\ + "You hear someone taping paper around a large object.") + else if(src.amount < 3) + to_chat(user, "You need more paper.") + else + to_chat(user, "The object you are trying to wrap is unsuitable for the sorting machinery!") + if (src.amount <= 0) + new /obj/item/c_tube( src.loc ) + qdel(src) + return + return + +/obj/item/packageWrap/examine(mob/user, dist) + . = ..() + . += "There are [amount] units of package wrap left!" diff --git a/code/modules/industry/packages/large_parcel.dm b/code/modules/industry/packages/large_parcel.dm new file mode 100644 index 000000000000..268d5a6f94ba --- /dev/null +++ b/code/modules/industry/packages/large_parcel.dm @@ -0,0 +1,118 @@ +// todo: /obj/structure/large_parcel +/obj/structure/bigDelivery + desc = "A big wrapped package." + name = "large parcel" + icon = 'icons/obj/storage.dmi' + icon_state = "deliverycloset" + var/obj/wrapped = null + density = 1 + var/sortTag = null + mouse_drag_pointer = MOUSE_ACTIVE_POINTER + var/examtext = null + var/nameset = 0 + var/label_y + var/label_x + var/tag_x + +/obj/structure/bigDelivery/Destroy() + if(wrapped) //sometimes items can disappear. For example, bombs. --rastaf0 + wrapped.forceMove(get_turf(src)) + if(istype(wrapped, /obj/structure/closet)) + var/obj/structure/closet/O = wrapped + O.sealed = 0 + wrapped = null + var/turf/T = get_turf(src) + for(var/atom/movable/AM in contents) + AM.forceMove(T) + return ..() + +/obj/structure/bigDelivery/attack_hand(mob/user, list/params) + unwrap() + +/obj/structure/bigDelivery/proc/unwrap() + // Destroy will drop our wrapped object on the turf, so let it. + qdel(src) + +/obj/structure/bigDelivery/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W, /obj/item/destTagger)) + var/obj/item/destTagger/O = W + if(O.currTag) + if(src.sortTag != O.currTag) + to_chat(user, "You have labeled the destination as [O.currTag].") + if(!src.sortTag) + src.sortTag = O.currTag + update_icon() + else + src.sortTag = O.currTag + playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 1) + else + to_chat(user, "The package is already labeled for [O.currTag].") + else + to_chat(user, "You need to set a destination first!") + + else if(istype(W, /obj/item/pen)) + switch(alert("What would you like to alter?",,"Title","Description", "Cancel")) + if("Title") + var/str = sanitizeSafe(input(usr,"Label text?","Set label",""), MAX_NAME_LEN) + if(!str || !length(str)) + to_chat(user, " Invalid text.") + return + user.visible_message("\The [user] titles \the [src] with \a [W], marking down: \"[str]\"",\ + "You title \the [src]: \"[str]\"",\ + "You hear someone scribbling a note.") + name = "[name] ([str])" + if(!examtext && !nameset) + nameset = 1 + update_icon() + else + nameset = 1 + if("Description") + var/str = sanitize(input(usr,"Label text?","Set label","")) + if(!str || !length(str)) + to_chat(user, "Invalid text.") + return + if(!examtext && !nameset) + examtext = str + update_icon() + else + examtext = str + user.visible_message("\The [user] labels \the [src] with \a [W], scribbling down: \"[examtext]\"",\ + "You label \the [src]: \"[examtext]\"",\ + "You hear someone scribbling a note.") + return + +/obj/structure/bigDelivery/update_icon() + cut_overlays() + if(nameset || examtext) + var/image/I = new/image('icons/obj/storage.dmi',"delivery_label") + if(icon_state == "deliverycloset") + I.pixel_x = 2 + if(label_y == null) + label_y = rand(-6, 11) + I.pixel_y = label_y + else if(icon_state == "deliverycrate") + if(label_x == null) + label_x = rand(-8, 6) + I.pixel_x = label_x + I.pixel_y = -3 + add_overlay(I) + if(sortTag) + var/image/I = new/image('icons/obj/storage.dmi',"delivery_tag") + if(icon_state == "deliverycloset") + if(tag_x == null) + tag_x = rand(-2, 3) + I.pixel_x = tag_x + I.pixel_y = 9 + else if(icon_state == "deliverycrate") + if(tag_x == null) + tag_x = rand(-8, 6) + I.pixel_x = tag_x + I.pixel_y = -3 + add_overlay(I) + +/obj/structure/bigDelivery/examine(mob/user, dist) + . = ..() + if(sortTag) + . += "It is labeled \"[sortTag]\"" + if(examtext) + . += "It has a note attached which reads, \"[examtext]\"" diff --git a/code/modules/industry/packages/small_parcel.dm b/code/modules/industry/packages/small_parcel.dm new file mode 100644 index 000000000000..433b8444952e --- /dev/null +++ b/code/modules/industry/packages/small_parcel.dm @@ -0,0 +1,107 @@ +// todo: /obj/item/small_parcel +/obj/item/smallDelivery + desc = "A small wrapped package." + name = "small parcel" + icon = 'icons/obj/storage.dmi' + icon_state = "deliverycrate3" + drop_sound = 'sound/items/drop/cardboardbox.ogg' + pickup_sound = 'sound/items/pickup/cardboardbox.ogg' + var/obj/item/wrapped = null + var/sortTag = null + var/examtext = null + var/nameset = 0 + var/tag_x + +/obj/item/smallDelivery/attack_self(mob/user) + . = ..() + if(.) + return + if (wrapped) //sometimes items can disappear. For example, bombs. --rastaf0 + if(ishuman(user)) + user.put_in_hands_or_drop(wrapped) + else + wrapped.forceMove(drop_location()) + wrapped = null + + qdel(src) + +/obj/item/smallDelivery/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W, /obj/item/destTagger)) + var/obj/item/destTagger/O = W + if(O.currTag) + if(src.sortTag != O.currTag) + to_chat(user, "You have labeled the destination as [O.currTag].") + if(!src.sortTag) + src.sortTag = O.currTag + update_icon() + else + src.sortTag = O.currTag + playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 1) + else + to_chat(user, "The package is already labeled for [O.currTag].") + else + to_chat(user, "You need to set a destination first!") + + else if(istype(W, /obj/item/pen)) + switch(alert("What would you like to alter?",,"Title","Description", "Cancel")) + if("Title") + var/str = sanitizeSafe(input(usr,"Label text?","Set label",""), MAX_NAME_LEN) + if(!str || !length(str)) + to_chat(user, " Invalid text.") + return + user.visible_message("\The [user] titles \the [src] with \a [W], marking down: \"[str]\"",\ + "You title \the [src]: \"[str]\"",\ + "You hear someone scribbling a note.") + name = "[name] ([str])" + if(!examtext && !nameset) + nameset = 1 + update_icon() + else + nameset = 1 + + if("Description") + var/str = sanitize(input(usr,"Label text?","Set label","")) + if(!str || !length(str)) + to_chat(user, "Invalid text.") + return + if(!examtext && !nameset) + examtext = str + update_icon() + else + examtext = str + user.visible_message("\The [user] labels \the [src] with \a [W], scribbling down: \"[examtext]\"",\ + "You label \the [src]: \"[examtext]\"",\ + "You hear someone scribbling a note.") + return + +/obj/item/smallDelivery/update_icon() + cut_overlays() + if((nameset || examtext) && icon_state != "deliverycrate1") + var/image/I = new/image('icons/obj/storage.dmi',"delivery_label") + if(icon_state == "deliverycrate5") + I.pixel_y = -1 + add_overlay(I) + if(sortTag) + var/image/I = new/image('icons/obj/storage.dmi',"delivery_tag") + switch(icon_state) + if("deliverycrate1") + I.pixel_y = -5 + if("deliverycrate2") + I.pixel_y = -2 + if("deliverycrate3") + I.pixel_y = 0 + if("deliverycrate4") + if(tag_x == null) + tag_x = rand(0,5) + I.pixel_x = tag_x + I.pixel_y = 3 + if("deliverycrate5") + I.pixel_y = -3 + add_overlay(I) + +/obj/item/smallDelivery/examine(mob/user, dist) + . = ..() + if(sortTag) + . += "It is labeled \"[sortTag]\"" + if(examtext) + . += "It has a note attached which reads, \"[examtext]\"" diff --git a/code/modules/mining/machine_unloading.dm b/code/modules/mining/machine_unloading.dm index 0f281c96052c..382d641d0c6c 100644 --- a/code/modules/mining/machine_unloading.dm +++ b/code/modules/mining/machine_unloading.dm @@ -27,7 +27,7 @@ /obj/machinery/mineral/unloading_machine/process(delta_time) if(output && input) - if(length(output.loc.contents) > 100) // let's not! + if(length(output.loc.contents) > TURF_CROWDING_SOFT_LIMIT) // let's not! return var/obj/structure/ore_box/O = locate() in input.loc if(O) diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm deleted file mode 100644 index 885b1df83216..000000000000 --- a/code/modules/recycling/disposal.dm +++ /dev/null @@ -1,1496 +0,0 @@ -// Disposal bin -// Holds items for disposal into pipe system -// Draws air from turf, gradually charges internal reservoir -// Once full (~1 atm), uses air resv to flush items into the pipes -// Automatically recharges air (unless off), will flush when ready if pre-set -// Can hold items and human size things, no other draggables -// Toilets are a type of disposal bin for small objects only and work on magic. By magic, I mean torque rotation -///kPa - assume the inside of a dispoal pipe is 1 atm, so that needs to be added. -#define SEND_PRESSURE (700 + ONE_ATMOSPHERE) -///L -#define PRESSURE_TANK_VOLUME 150 -///L/s - 4 m/s using a 15 cm by 15 cm inlet -#define PUMP_MAX_FLOW_RATE 90 -/obj/machinery/disposal - name = "disposal unit" - desc = "A pneumatic waste disposal unit." - icon = 'icons/obj/pipes/disposal.dmi' - icon_state = "disposal" - atom_colouration_system = FALSE - anchored = TRUE - density = TRUE - pass_flags_self = ATOM_PASS_OVERHEAD_THROW - var/datum/gas_mixture/air_contents // internal reservoir - var/mode = 1 // item mode 0=off 1=charging 2=charged - var/flush = FALSE // true if flush handle is pulled - var/obj/structure/disposalpipe/trunk/trunk = null // the attached pipe trunk - var/flushing = FALSE // true if flushing in progress - var/flush_every_ticks = 30 //Every 30 ticks it will look whether it is ready to flush - var/flush_count = 0 //this var adds 1 once per tick. When it reaches flush_every_ticks it resets and tries to flush. - var/last_sound = 0 - active_power_usage = 2200 //the pneumatic pump power. 3 HP ~ 2200W - idle_power_usage = 100 - -// create a new disposal -// find the attached trunk (if present) and init gas resvr. -/obj/machinery/disposal/Initialize(mapload, newdir) - . = ..() - return INITIALIZE_HINT_LATELOAD - -/obj/machinery/disposal/LateInitialize() - . = ..() - trunk = locate() in src.loc - if(!trunk) - mode = 0 - flush = FALSE - else - trunk.linked = src // link the pipe trunk to self - - air_contents = new/datum/gas_mixture(PRESSURE_TANK_VOLUME) - update() - -/obj/machinery/disposal/Destroy() - eject() - if(trunk) - trunk.linked = null - return ..() - -// attack by item places it in to disposal -/obj/machinery/disposal/attackby(var/obj/item/I, var/mob/user) - if(user.a_intent != INTENT_HELP) - return ..() - - . = CLICKCHAIN_DO_NOT_PROPAGATE - - if(machine_stat & BROKEN || !I || !user) - return - - add_fingerprint(user, 0, I) - if(mode<=0) // It's off - if(I.is_screwdriver()) - if(contents.len > 0) - to_chat(user, "Eject the items first!") - return - if(mode==0) // It's off but still not unscrewed - mode=-1 // Set it to doubleoff l0l - playsound(src, I.tool_sound, 50, 1) - to_chat(user, "You remove the screws around the power connection.") - return - else if(mode==-1) - mode=0 - playsound(src, I.tool_sound, 50, 1) - to_chat(user, "You attach the screws around the power connection.") - return - else if(istype(I, /obj/item/weldingtool) && mode==-1) - if(contents.len > 0) - to_chat(user, "Eject the items first!") - return - var/obj/item/weldingtool/W = I - if(W.remove_fuel(0,user)) - playsound(src, W.tool_sound, 100, 1) - to_chat(user, "You start slicing the floorweld off the disposal unit.") - - if(do_after(user,20 * W.tool_speed)) - if(!src || !W.isOn()) return - to_chat(user, "You sliced the floorweld off the disposal unit.") - var/obj/structure/disposalconstruct/C = new (src.loc) - src.transfer_fingerprints_to(C) - C.ptype = 6 // 6 = disposal unit - C.anchored = 1 - C.density = 1 - C.update() - qdel(src) - return - else - to_chat(user, "You need more welding fuel to complete this task.") - return - - if(istype(I, /obj/item/storage/bag/trash)) - var/obj/item/storage/bag/trash/T = I - to_chat(user, "You empty the bag.") - for(var/obj/item/O in T.contents) - T.obj_storage.remove(O, src) - T.update_icon() - update() - return - - if(istype(I, /obj/item/material/ashtray)) - var/obj/item/material/ashtray/A = I - if(A.contents.len > 0) - user.visible_message("\The [user] empties \the [A.name] into [src].") - for(var/obj/item/O in A.contents) - O.forceMove(src) - A.update_icon() - update() - return - - var/obj/item/grab/G = I - if(istype(G)) // handle grabbed mob - if(ismob(G.affecting)) - var/mob/GM = G.affecting - for (var/mob/V in viewers(usr)) - V.show_message("[usr] starts putting [GM.name] into the disposal.", 3) - if(do_after(usr, 20)) - GM.forceMove(src) - GM.update_perspective() - for (var/mob/C in viewers(src)) - C.show_message("[GM.name] has been placed in \the [src] by [user].", 3) - qdel(G) - - add_attack_logs(user,GM,"Disposals dunked") - return - - if(!user.attempt_insert_item_for_installation(I, src)) - return - - to_chat(user, "You place \the [I] into \the [src].") - for(var/mob/M in viewers(src)) - if(M == user) - continue - M.show_message("[user.name] places \the [I] into \the [src].", 3) - - update() - -// mouse drop another mob or self -// -/obj/machinery/disposal/MouseDroppedOnLegacy(mob/target, mob/user) - if(!CHECK_MOBILITY(user, MOBILITY_CAN_USE) || !istype(target)) - return - if(target.buckled || get_dist(user, src) > 1 || get_dist(user, target) > 1) - return - - //animals cannot put mobs other than themselves into disposal - if(isanimal(user) && target != user) - return - - src.add_fingerprint(user) - var/target_loc = target.loc - var/msg - for (var/mob/V in viewers(usr)) - if(target == user && !user.stat && CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE)) - V.show_message("[usr] starts climbing into the disposal.", 3) - if(target != user && !user.restrained() && !user.stat && CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE)) - if(target.anchored) return - V.show_message("[usr] starts stuffing [target.name] into the disposal.", 3) - if(!do_after(usr, 20)) - return - if(target_loc != target.loc) - return - if(target == user && !user.stat && CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE)) // if drop self, then climbed in - // must be awake, not stunned or whatever - msg = "[user.name] climbs into \the [src]." - to_chat(user, "You climb into \the [src].") - else if(target != user && !user.restrained() && !user.stat && CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE)) - msg = "[user.name] stuffs [target.name] into \the [src]!" - to_chat(user, "You stuff [target.name] into \the [src]!") - - add_attack_logs(user,target,"Disposals dunked") - else - return - target.forceMove(src) - target.update_perspective() - - for (var/mob/C in viewers(src)) - if(C == user) - continue - C.show_message(msg, 3) - - update() - return - -// attempt to move while inside -/obj/machinery/disposal/relaymove(mob/user as mob) - if(user.stat || src.flushing) - return - if(user.loc == src) - src.go_out(user) - return - -// leave the disposal -/obj/machinery/disposal/proc/go_out(mob/user) - user.forceMove(loc) - user.update_perspective() - update() - return - -// ai as human but can't flush -/obj/machinery/disposal/attack_ai(mob/user as mob) - interact(user, 1) - -// human interact with machine -/obj/machinery/disposal/attack_hand(mob/user, list/params) - - if(machine_stat & BROKEN) - return - - if(user && user.loc == src) - to_chat(user, "You cannot reach the controls from inside.") - return - - // Clumsy folks can only flush it. - if(user.IsAdvancedToolUser(1)) - interact(user, 0) - else - flush = !flush - update() - return - -// user interaction -/obj/machinery/disposal/interact(mob/user, var/ai=0) - - src.add_fingerprint(user) - if(machine_stat & BROKEN) - user.unset_machine() - return - - var/dat = "Waste Disposal UnitWaste Disposal Unit
" - - if(!ai) // AI can't pull flush handle - if(flush) - dat += "Disposal handle: Disengage Engaged" - else - dat += "Disposal handle: Disengaged Engage" - - dat += "

Eject contents
" - - if(mode <= 0) - dat += "Pump: Off On
" - else if(mode == 1) - dat += "Pump: Off On (pressurizing)
" - else - dat += "Pump: Off On (idle)
" - - var/per = 100* air_contents.return_pressure() / (SEND_PRESSURE) - - dat += "Pressure: [round(per, 1)]%
" - - - user.set_machine(src) - user << browse(dat, "window=disposal;size=360x170") - onclose(user, "disposal") - -// handle machine interaction - -/obj/machinery/disposal/Topic(href, href_list) - if(usr.loc == src) - to_chat(usr, "You cannot reach the controls from inside.") - return - - if(mode==-1 && !href_list["eject"]) // only allow ejecting if mode is -1 - to_chat(usr, "The disposal units power is disabled.") - return - if(..()) - return - - if(machine_stat & BROKEN) - return - if(usr.stat || usr.restrained() || src.flushing) - return - - if(istype(src.loc, /turf)) - usr.set_machine(src) - - if(href_list["close"]) - usr.unset_machine() - usr << browse(null, "window=disposal") - return - - if(href_list["pump"]) - if(text2num(href_list["pump"])) - mode = 1 - else - mode = 0 - update() - - if(!isAI(usr)) - if(href_list["handle"]) - flush = text2num(href_list["handle"]) - update() - - if(href_list["eject"]) - eject() - else - usr << browse(null, "window=disposal") - usr.unset_machine() - return - return - -// eject the contents of the disposal unit -/obj/machinery/disposal/proc/eject() - for(var/atom/movable/AM in src) - AM.forceMove(src.loc) - AM.pipe_eject(0) - update() - -// update the icon & overlays to reflect mode & status -/obj/machinery/disposal/proc/update() - cut_overlays() - if(machine_stat & BROKEN) - icon_state = "disposal-broken" - mode = 0 - flush = 0 - return - - var/list/overlays_to_add = list() - - // flush handle - if(flush) - overlays_to_add += image('icons/obj/pipes/disposal.dmi', "dispover-handle") - - // only handle is shown if no power - if(machine_stat & NOPOWER || mode == -1) - add_overlay(overlays_to_add) - return - - // check for items in disposal - occupied light - if(contents.len > 0) - overlays_to_add += image('icons/obj/pipes/disposal.dmi', "dispover-full") - - // charging and ready light - if(mode == 1) - overlays_to_add += image('icons/obj/pipes/disposal.dmi', "dispover-charge") - else if(mode == 2) - overlays_to_add += image('icons/obj/pipes/disposal.dmi', "dispover-ready") - - add_overlay(overlays_to_add) - -// timed process -// charge the gas reservoir and perform flush if ready -/obj/machinery/disposal/process(delta_time) - if(!air_contents || (machine_stat & BROKEN)) // nothing can happen if broken - update_use_power(USE_POWER_OFF) - return - - flush_count++ - if( flush_count >= flush_every_ticks ) - if( contents.len ) - if(mode == 2) - spawn(0) - feedback_inc("disposal_auto_flush",1) - flush() - flush_count = 0 - - src.updateDialog() - - if(flush && air_contents.return_pressure() >= SEND_PRESSURE ) // flush can happen even without power - flush() - - if(mode != 1) //if off or ready, no need to charge - update_use_power(USE_POWER_IDLE) - else if(air_contents.return_pressure() >= SEND_PRESSURE) - mode = 2 //if full enough, switch to ready mode - update() - else - src.pressurize() //otherwise charge - -/obj/machinery/disposal/proc/pressurize() - if(machine_stat & NOPOWER) // won't charge if no power - update_use_power(USE_POWER_OFF) - return - - var/atom/L = loc // recharging from loc turf - var/datum/gas_mixture/env = L.return_air() - - var/power_draw = -1 - if(env && env.temperature > 0) - var/transfer_moles = (PUMP_MAX_FLOW_RATE/env.volume)*env.total_moles //group_multiplier is divided out here - power_draw = pump_gas(src, env, air_contents, transfer_moles, active_power_usage) - - if (power_draw > 0) - use_power(power_draw) - -// perform a flush -/obj/machinery/disposal/proc/flush() - - flushing = 1 - flick("[icon_state]-flush", src) - - var/wrapcheck = 0 - var/obj/structure/disposalholder/H = new() // virtual holder object which actually - // travels through the pipes. - //Hacky test to get drones to mail themselves through disposals. - for(var/mob/living/silicon/robot/drone/D in src) - wrapcheck = 1 - - for(var/obj/item/smallDelivery/O in src) - wrapcheck = 1 - - if(wrapcheck == 1) - H.tomail = 1 - - - sleep(10) - if(last_sound < world.time + 1) - playsound(src, 'sound/machines/disposalflush.ogg', 50, 0, 0) - last_sound = world.time - sleep(5) // wait for animation to finish - - - H.init(src, air_contents) // copy the contents of disposer to holder - air_contents = new(PRESSURE_TANK_VOLUME) // new empty gas resv. - - H.start(src) // start the holder processing movement - flushing = 0 - // now reset disposal state - flush = 0 - if(mode == 2) // if was ready, - mode = 1 // switch to charging - update() - return - - -// called when area power changes -/obj/machinery/disposal/power_change() - ..() // do default setting/reset of stat NOPOWER bit - update() // update icon - return - - -/// Called when holder is expelled from a disposal. -/// Should usually only occur if the pipe network is modified. -/obj/machinery/disposal/proc/expel(obj/structure/disposalholder/H) - - var/turf/target - playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) - if(H) // Somehow, someone managed to flush a window which broke mid-transit and caused the disposal to go in an infinite loop trying to expel null, hopefully this fixes it - for(var/atom/movable/AM in H) - target = get_offset_target_turf(src.loc, rand(5)-rand(5), rand(5)-rand(5)) - - AM.forceMove(src.loc) - AM.pipe_eject(0) - if(!istype(AM,/mob/living/silicon/robot/drone)) //Poor drones kept smashing windows and taking system damage being fired out of disposals. ~Z - spawn(1) - if(AM) - AM.throw_at_old(target, 5, 1) - - H.vent_gas(loc) - qdel(H) - -/obj/machinery/disposal/throw_impacted(atom/movable/AM, datum/thrownthing/TT) - if(istype(AM, /obj/item) && !istype(AM, /obj/projectile)) - if(prob(75)) - AM.forceMove(src) - visible_message("\The [AM] lands in \the [src].") - return COMPONENT_THROW_HIT_TERMINATE - else - visible_message("\The [AM] bounces off of \the [src]'s rim!") - return ..() - return ..() - -/obj/machinery/disposal/CanAllowThrough(atom/movable/mover, turf/target) - if(istype(mover, /obj/projectile)) - return TRUE - return ..() - -/obj/machinery/disposal/wall - name = "inset disposal unit" - icon_state = "wall" - - density = FALSE - -/obj/machinery/disposal/wall/Initialize() - . = ..() - - spawn(1 SECOND) // Fixfix for weird interaction with buildmode or other late-spawning. - update() - -/obj/machinery/disposal/wall/update() - ..() - - switch(dir) - if(1) - pixel_x = 0 - pixel_y = -32 - if(2) - pixel_x = 0 - pixel_y = 32 - if(4) - pixel_x = -32 - pixel_y = 0 - if(8) - pixel_x = 32 - pixel_y = 0 - - -// virtual disposal object -// travels through pipes in lieu of actual items -// contents will be items flushed by the disposal -// this allows the gas flushed to be tracked - -/obj/structure/disposalholder - invisibility = 101 - var/datum/gas_mixture/gas = null // gas used to flush, will appear at exit point - var/active = 0 // true if the holder is moving, otherwise inactive - dir = 0 - var/count = 2048 //*** can travel 2048 steps before going inactive (in case of loops) - var/destinationTag = "" // changes if contains a delivery container - var/tomail = 0 //changes if contains wrapped package - var/hasmob = 0 //If it contains a mob - - var/partialTag = "" //set by a partial tagger the first time round, then put in destinationTag if it goes through again. - - -// initialize a holder from the contents of a disposal unit -/obj/structure/disposalholder/proc/init(var/obj/machinery/disposal/D, var/datum/gas_mixture/flush_gas) - gas = flush_gas// transfer gas resv. into holder object -- let's be explicit about the data this proc consumes, please. - - //Check for any living mobs trigger hasmob. - //hasmob effects whether the package goes to cargo or its tagged destination. - for(var/mob/living/M in D) - if(M && M.stat != 2 && !istype(M,/mob/living/silicon/robot/drone)) - hasmob = 1 - - //Checks 1 contents level deep. This means that players can be sent through disposals... - //...but it should require a second person to open the package. (i.e. person inside a wrapped locker) - for(var/obj/O in D) - if(O.contents) - for(var/mob/living/M in O.contents) - if(M && M.stat != 2 && !istype(M,/mob/living/silicon/robot/drone)) - hasmob = 1 - - // now everything inside the disposal gets put into the holder - // note AM since can contain mobs or objs - for(var/atom/movable/AM in D) - AM.forceMove(src) - if(istype(AM, /obj/structure/bigDelivery) && !hasmob) - var/obj/structure/bigDelivery/T = AM - src.destinationTag = T.sortTag - if(istype(AM, /obj/item/smallDelivery) && !hasmob) - var/obj/item/smallDelivery/T = AM - src.destinationTag = T.sortTag - //Drones can mail themselves through maint. - if(istype(AM, /mob/living/silicon/robot/drone)) - var/mob/living/silicon/robot/drone/drone = AM - src.destinationTag = drone.mail_destination - - -// start the movement process -// argument is the disposal unit the holder started in -/obj/structure/disposalholder/proc/start(var/obj/machinery/disposal/D) - if(!D.trunk) - D.expel(src) // no trunk connected, so expel immediately - return - - forceMove(D.trunk) - active = 1 - setDir(DOWN) - spawn(1) - move() // spawn off the movement process - - return - -// movement process, persists while holder is moving through pipes -/obj/structure/disposalholder/proc/move() - var/obj/structure/disposalpipe/last - // todo: while this is fucking awful? - while(active) - sleep(1) // was 1 - if(!loc) return // check if we got GC'd - - if(hasmob && prob(3)) - for(var/mob/living/H in src) - if(!istype(H,/mob/living/silicon/robot/drone)) //Drones use the mailing code to move through the disposal system, - H.take_overall_damage(20, 0, weapon_descriptor = "blunt trauma")//horribly maim any living creature jumping down disposals. c'est la vie - - var/obj/structure/disposalpipe/curr = loc - last = curr - curr = curr.transfer(src) - - if(!loc) return //side effects - - if(!curr) - last.expel(src, loc, dir) - - // - if(!(count--)) - active = 0 - return - - - -// find the turf which should contain the next pipe -/obj/structure/disposalholder/proc/nextloc() - return get_step(loc,dir) - -// find a matching pipe on a turf -/obj/structure/disposalholder/proc/findpipe(var/turf/T) - - if(!T) - return null - - var/fdir = turn(dir, 180) // flip the movement direction - for(var/obj/structure/disposalpipe/P in T) - if(fdir & P.dpdir) // find pipe direction mask that matches flipped dir - return P - // if no matching pipe, return null - return null - -// merge two holder objects -// used when a a holder meets a stuck holder -/obj/structure/disposalholder/proc/merge(var/obj/structure/disposalholder/other) - for(var/atom/movable/AM in other) - AM.forceMove(src) // move everything in other holder to this one - if(ismob(AM)) - var/mob/M = AM - M.update_perspective() - - qdel(other) - - -/obj/structure/disposalholder/proc/settag(var/new_tag) - destinationTag = new_tag - -/obj/structure/disposalholder/proc/setpartialtag(var/new_tag) - if(partialTag == new_tag) - destinationTag = new_tag - partialTag = "" - else - partialTag = new_tag - - -// called when player tries to move while in a pipe -/obj/structure/disposalholder/relaymove(mob/user as mob) - - if(!istype(user,/mob/living)) - return - - var/mob/living/U = user - - if (U.stat || U.last_special <= world.time) - return - - U.last_special = world.time+100 - - if (src.loc) - for (var/mob/M in hearers(src.loc.loc)) - to_chat(M, "CLONG, clong!") - - playsound(src.loc, 'sound/effects/clang.ogg', 50, 0, 0) - -// called to vent all gas in holder to a location -/obj/structure/disposalholder/proc/vent_gas(var/atom/location) - location.assume_air(gas) // vent all gas to turf - return - -/obj/structure/disposalholder/Destroy() - QDEL_NULL(gas) - active = 0 - return ..() - -// Disposal pipes - -/obj/structure/disposalpipe - icon = 'icons/obj/pipes/disposal.dmi' - name = "disposal pipe" - desc = "An underfloor disposal pipe." - anchored = 1 - density = 0 - hides_underfloor = OBJ_UNDERFLOOR_ALWAYS - dir = 0 // dir will contain dominant direction for junction pipes - plane = TURF_PLANE - layer = DISPOSAL_LAYER // slightly lower than wires and other pipes. - - integrity = 100 - integrity_max = 100 - - #ifdef IN_MAP_EDITOR // Display disposal pipes etc. above walls in map editors. - alpha = 128 // Set for the benefit of mapping. - #endif - - /// Bitmask of pipe directions. - var/dpdir = 0 - var/sortType = "" - var/subtype = 0 - // new pipe, set the icon_state as on map - -/obj/structure/disposalpipe/Initialize(mapload, dir) - . = ..() - base_icon_state = icon_state - if(!isnull(dir)) - setDir(dir) - -// pipe is deleted -// ensure if holder is present, it is expelled -/obj/structure/disposalpipe/Destroy() - var/obj/structure/disposalholder/H = locate() in src - if(H) - // holder was present - H.active = 0 - var/turf/T = src.loc - if(T.density) - // deleting pipe is inside a dense turf (wall) - // this is unlikely, but just dump out everything into the turf in case - - for(var/atom/movable/AM in H) - AM.forceMove(T) - AM.pipe_eject(0) - qdel(H) - ..() - return - - // otherwise, do normal expel from turf - if(H) - expel(H, T, 0) - ..() - -// returns the direction of the next pipe object, given the entrance dir -// by default, returns the bitmask of remaining directions -/obj/structure/disposalpipe/proc/nextdir(var/fromdir) - return dpdir & (~turn(fromdir, 180)) - -// transfer the holder through this pipe segment -// overriden for special behaviour -// -/obj/structure/disposalpipe/proc/transfer(var/obj/structure/disposalholder/H) - var/nextdir = nextdir(H.dir) - H.setDir(nextdir) - var/turf/T = H.nextloc() - var/obj/structure/disposalpipe/P = H.findpipe(T) - - if(P) - // find other holder in next loc, if inactive merge it with current - var/obj/structure/disposalholder/H2 = locate() in P - if(H2 && !H2.active) - H.merge(H2) - - H.forceMove(P) - else // if wasn't a pipe, then set loc to turf - H.forceMove(T) - return null - - return P - -// update actual icon_state depending on visibility -// if invisible, append "f" to icon_state to show faded version -// this will be revealed if a T-scanner is used -// if visible, use regular icon_state -/obj/structure/disposalpipe/proc/updateicon() -/* if(invisibility) //we hide things with alpha now, no need for transparent icons - icon_state = "[base_icon_state]f" - else - icon_state = base_icon_state*/ - icon_state = base_icon_state - return - - -// expel the held objects into a turf -// called when there is a break in the pipe -/obj/structure/disposalpipe/proc/expel(var/obj/structure/disposalholder/H, var/turf/T, var/direction) - if(!istype(H)) - return - - // Empty the holder if it is expelled into a dense turf. - // Leaving it intact and sitting in a wall is stupid. - if(T.density) - for(var/atom/movable/AM in H) - AM.loc = T - AM.pipe_eject(0) - qdel(H) - return - - - if(!T.is_plating() && istype(T,/turf/simulated/floor)) //intact floor, pop the tile - var/turf/simulated/floor/F = T - F.break_tile() - new /obj/item/stack/tile(H) // add to holder so it will be thrown with other stuff - - var/turf/target - if(direction) // direction is specified - if(istype(T, /turf/space)) // if ended in space, then range is unlimited - target = get_edge_target_turf(T, direction) - else // otherwise limit to 10 tiles - target = get_ranged_target_turf(T, direction, 10) - - playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) - if(H) - for(var/atom/movable/AM in H) - AM.forceMove(T) - AM.pipe_eject(direction) - spawn(1) - if(AM) - AM.throw_at_old(target, 100, 1) - H.vent_gas(T) - qdel(H) - - else // no specified direction, so throw in random direction - - playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) - if(H) - for(var/atom/movable/AM in H) - target = get_offset_target_turf(T, rand(5)-rand(5), rand(5)-rand(5)) - - AM.forceMove(T) - AM.pipe_eject(0) - spawn(1) - if(AM) - AM.throw_at_old(target, 5, 1) - - H.vent_gas(T) // all gas vent to turf - qdel(H) - -/obj/structure/disposalpipe/deconstructed(method) - . = ..() - src.invisibility = 101 // make invisible (since we won't delete the pipe immediately) - var/obj/structure/disposalholder/H = locate() in src - if(H) - // holder was present - H.active = 0 - var/turf/T = src.loc - if(T.density) - // broken pipe is inside a dense turf (wall) - // this is unlikely, but just dump out everything into the turf in case - - for(var/atom/movable/AM in H) - AM.forceMove(T) - AM.pipe_eject(0) - qdel(H) - return - - // otherwise, do normal expel from turf - if(H) - expel(H, T, 0) - return ..() - -/obj/structure/disposalpipe/drop_products(method, atom/where) - . = ..() - if(method != ATOM_DECONSTRUCT_DISASSEMBLED) - new /obj/structure/disposalpipe/broken(where, dir) - -//attack by item -//weldingtool: unfasten and convert to obj/disposalconstruct - -/obj/structure/disposalpipe/attackby(var/obj/item/I, var/mob/user) - - var/turf/T = src.loc - if(!T.is_plating()) - return // prevent interaction with T-scanner revealed pipes - src.add_fingerprint(user, 0, I) - if(istype(I, /obj/item/weldingtool)) - var/obj/item/weldingtool/W = I - - if(W.remove_fuel(0,user)) - playsound(src, W.tool_sound, 50, 1) - // check if anything changed over 2 seconds - var/turf/uloc = user.loc - var/atom/wloc = W.loc - to_chat(user, "Slicing the disposal pipe.") - sleep(30) - if(!W.isOn()) return - if(user.loc == uloc && wloc == W.loc) - welded() - else - to_chat(user, "You must stay still while welding the pipe.") - else - to_chat(user, "You need more welding fuel to cut the pipe.") - return - -// called when pipe is cut with welder -/obj/structure/disposalpipe/proc/welded() - - var/obj/structure/disposalconstruct/C = new (src.loc) - switch(base_icon_state) - if("pipe-s") - C.ptype = 0 - if("pipe-c") - C.ptype = 1 - if("pipe-j1") - C.ptype = 2 - if("pipe-j2") - C.ptype = 3 - if("pipe-y") - C.ptype = 4 - if("pipe-t") - C.ptype = 5 - if("pipe-j1s") - C.ptype = 9 - C.sortType = sortType - if("pipe-j2s") - C.ptype = 10 - C.sortType = sortType -///// Z-Level stuff - if("pipe-u") - C.ptype = 11 - if("pipe-d") - C.ptype = 12 -///// Z-Level stuff - if("pipe-tagger") - C.ptype = 13 - if("pipe-tagger-partial") - C.ptype = 14 - C.subtype = src.subtype - src.transfer_fingerprints_to(C) - C.setDir(dir) - C.density = 0 - C.anchored = 1 - C.update() - - qdel(src) - -// pipe is deleted -// ensure if holder is present, it is expelled -/obj/structure/disposalpipe/Destroy() - var/obj/structure/disposalholder/H = locate() in src - if(H) - // holder was present - H.active = 0 - var/turf/T = src.loc - if(T.density) - // deleting pipe is inside a dense turf (wall) - // this is unlikely, but just dump out everything into the turf in case - - for(var/atom/movable/AM in H) - AM.forceMove(T) - AM.pipe_eject(0) - qdel(H) - ..() - return - - // otherwise, do normal expel from turf - if(H) - expel(H, T, 0) - ..() - -// a straight or bent segment -/obj/structure/disposalpipe/segment - icon_state = "pipe-s" - -/obj/structure/disposalpipe/segment/New() - ..() - if(icon_state == "pipe-s") - dpdir = dir | turn(dir, 180) - else - dpdir = dir | turn(dir, -90) - return - -///// Z-Level stuff -/obj/structure/disposalpipe/up - icon_state = "pipe-u" - -/obj/structure/disposalpipe/up/New() - ..() - dpdir = dir - return - -/obj/structure/disposalpipe/up/nextdir(fromdir) - var/nextdir - if(fromdir == 11) - nextdir = dir - else - nextdir = 12 - return nextdir - -/obj/structure/disposalpipe/up/transfer(var/obj/structure/disposalholder/H) - var/nextdir = nextdir(H.dir) - H.setDir(nextdir) - - var/turf/T - var/obj/structure/disposalpipe/P - - if(nextdir == 12) - T = get_vertical_step(src, UP) - if(!T) - H.forceMove(loc) - return - else - for(var/obj/structure/disposalpipe/down/F in T) - P = F - - else - T = get_step(src.loc, H.dir) - P = H.findpipe(T) - - if(P) - // find other holder in next loc, if inactive merge it with current - var/obj/structure/disposalholder/H2 = locate() in P - if(H2 && !H2.active) - H.merge(H2) - - H.forceMove(P) - else // if wasn't a pipe, then set loc to turf - H.forceMove(T) - return null - - return P - -/obj/structure/disposalpipe/down - icon_state = "pipe-d" - -/obj/structure/disposalpipe/down/New() - ..() - dpdir = dir - return - -/obj/structure/disposalpipe/down/nextdir(fromdir) - var/nextdir - if(fromdir == 12) - nextdir = dir - else - nextdir = 11 - return nextdir - -/obj/structure/disposalpipe/down/transfer(obj/structure/disposalholder/H) - var/nextdir = nextdir(H.dir) - H.dir = nextdir - - var/turf/T - var/obj/structure/disposalpipe/P - - if(nextdir == 11) - T = get_vertical_step(src, DOWN) - if(!T) - H.forceMove(src.loc) - return - else - for(var/obj/structure/disposalpipe/up/F in T) - P = F - - else - T = get_step(src.loc, H.dir) - P = H.findpipe(T) - - if(P) - // find other holder in next loc, if inactive merge it with current - var/obj/structure/disposalholder/H2 = locate() in P - if(H2 && !H2.active) - H.merge(H2) - - H.forceMove(P) - else // if wasn't a pipe, then set loc to turf - H.forceMove(T) - return null - - return P -///// Z-Level stuff - -/obj/structure/disposalpipe/junction/yjunction - icon_state = "pipe-y" - -//a three-way junction with dir being the dominant direction -/obj/structure/disposalpipe/junction - icon_state = "pipe-j1" - -/obj/structure/disposalpipe/junction/New() - ..() - if(icon_state == "pipe-j1") - dpdir = dir | turn(dir, -90) | turn(dir,180) - else if(icon_state == "pipe-j2") - dpdir = dir | turn(dir, 90) | turn(dir,180) - else // pipe-y - dpdir = dir | turn(dir,90) | turn(dir, -90) - return - - -// next direction to move -// if coming in from secondary dirs, then next is primary dir -// if coming in from primary dir, then next is equal chance of other dirs -/obj/structure/disposalpipe/junction/nextdir(var/fromdir) - var/flipdir = turn(fromdir, 180) - if(flipdir != dir) // came from secondary dir - return dir // so exit through primary - else // came from primary - // so need to choose either secondary exit - var/mask = ..(fromdir) - - // find a bit which is set - var/setbit = 0 - if(mask & NORTH) - setbit = NORTH - else if(mask & SOUTH) - setbit = SOUTH - else if(mask & EAST) - setbit = EAST - else - setbit = WEST - - if(prob(50)) // 50% chance to choose the found bit or the other one - return setbit - else - return mask & (~setbit) - -/obj/structure/disposalpipe/junction/flipped //for easier and cleaner mapping - icon_state = "pipe-j2" - -/obj/structure/disposalpipe/tagger - name = "package tagger" - icon_state = "pipe-tagger" - var/sort_tag = "" - var/partial = 0 - -/obj/structure/disposalpipe/tagger/proc/updatedesc() - desc = initial(desc) - if(sort_tag) - desc += "\nIt's tagging objects with the '[sort_tag]' tag." - -/obj/structure/disposalpipe/tagger/proc/updatename() - if(sort_tag) - name = "[initial(name)] ([sort_tag])" - else - name = initial(name) - -/obj/structure/disposalpipe/tagger/New() - . = ..() - dpdir = dir | turn(dir, 180) - if(sort_tag) GLOB.tagger_locations |= sort_tag - updatename() - updatedesc() - -/obj/structure/disposalpipe/tagger/attackby(obj/item/I, mob/user) - if(..()) - return - - if(istype(I, /obj/item/destTagger)) - var/obj/item/destTagger/O = I - - if(O.currTag)// Tag set - sort_tag = O.currTag - playsound(src.loc, 'sound/machines/twobeep.ogg', 100, 1) - to_chat(user, "Changed tag to '[sort_tag]'.") - updatename() - updatedesc() - -/obj/structure/disposalpipe/tagger/transfer(obj/structure/disposalholder/H) - if(sort_tag) - if(partial) - H.setpartialtag(sort_tag) - else - H.settag(sort_tag) - return ..() - -/obj/structure/disposalpipe/tagger/partial //needs two passes to tag - name = "partial package tagger" - icon_state = "pipe-tagger-partial" - partial = 1 - -//a three-way junction that sorts objects -/obj/structure/disposalpipe/sortjunction - name = "sorting junction" - icon_state = "pipe-j1s" - desc = "An underfloor disposal pipe with a package sorting mechanism." - - var/posdir = 0 - var/negdir = 0 - var/sortdir = 0 - -/obj/structure/disposalpipe/sortjunction/proc/updatedesc() - desc = initial(desc) - if(sortType) - desc += "\nIt's filtering objects with the '[sortType]' tag." - -/obj/structure/disposalpipe/sortjunction/proc/updatename() - if(sortType) - name = "[initial(name)] ([sortType])" - else - name = initial(name) - -/obj/structure/disposalpipe/sortjunction/proc/updatedir() - posdir = dir - negdir = turn(posdir, 180) - - if(icon_state == "pipe-j1s") - sortdir = turn(posdir, -90) - else if(icon_state == "pipe-j2s") - sortdir = turn(posdir, 90) - - dpdir = sortdir | posdir | negdir - -/obj/structure/disposalpipe/sortjunction/New() - . = ..() - if(sortType) GLOB.tagger_locations |= sortType - - updatedir() - updatename() - updatedesc() - -/obj/structure/disposalpipe/sortjunction/attackby(obj/item/I, mob/user) - if(..()) - return - - if(istype(I, /obj/item/destTagger)) - var/obj/item/destTagger/O = I - - if(O.currTag)// Tag set - sortType = O.currTag - playsound(src.loc, 'sound/machines/twobeep.ogg', 100, 1) - to_chat(user, "Changed filter to '[sortType]'.") - updatename() - updatedesc() - -/obj/structure/disposalpipe/sortjunction/proc/divert_check(checkTag) - return sortType == checkTag - -// next direction to move -// if coming in from negdir, then next is primary dir or sortdir -// if coming in from posdir, then flip around and go back to posdir -// if coming in from sortdir, go to posdir -/obj/structure/disposalpipe/sortjunction/nextdir(fromdir, sortTag) - if(fromdir != sortdir) // probably came from the negdir - if(divert_check(sortTag)) - return sortdir - else - return posdir - else // came from sortdir - // so go with the flow to positive direction - return posdir - -/obj/structure/disposalpipe/sortjunction/transfer(var/obj/structure/disposalholder/H) - var/nextdir = nextdir(H.dir, H.destinationTag) - H.setDir(nextdir) - var/turf/T = H.nextloc() - var/obj/structure/disposalpipe/P = H.findpipe(T) - - if(P) - // find other holder in next loc, if inactive merge it with current - var/obj/structure/disposalholder/H2 = locate() in P - if(H2 && !H2.active) - H.merge(H2) - - H.forceMove(P) - else // if wasn't a pipe, then set loc to turf - H.forceMove(T) - return null - - return P - -//a three-way junction that filters all wrapped and tagged items -/obj/structure/disposalpipe/sortjunction/wildcard - name = "wildcard sorting junction" - desc = "An underfloor disposal pipe which filters all wrapped and tagged items." - subtype = 1 - -/obj/structure/disposalpipe/sortjunction/wildcard/divert_check(checkTag) - return checkTag != "" - -//junction that filters all untagged items -/obj/structure/disposalpipe/sortjunction/untagged - name = "untagged sorting junction" - desc = "An underfloor disposal pipe which filters all untagged items." - subtype = 2 - -/obj/structure/disposalpipe/sortjunction/untagged/divert_check(checkTag) - return checkTag == "" - -/obj/structure/disposalpipe/sortjunction/flipped //for easier and cleaner mapping - icon_state = "pipe-j2s" - -/obj/structure/disposalpipe/sortjunction/wildcard/flipped - icon_state = "pipe-j2s" - -/obj/structure/disposalpipe/sortjunction/untagged/flipped - icon_state = "pipe-j2s" - -//a trunk joining to a disposal bin or outlet on the same turf -/obj/structure/disposalpipe/trunk - icon_state = "pipe-t" - var/obj/linked // the linked obj/machinery/disposal or obj/disposaloutlet - -/obj/structure/disposalpipe/trunk/Initialize(mapload) - . = ..() - dpdir = dir - return INITIALIZE_HINT_LATELOAD - -/obj/structure/disposalpipe/trunk/LateInitialize() - . = ..() - getlinked() -/obj/structure/disposalpipe/trunk/proc/getlinked() - linked = null - var/obj/machinery/disposal/D = locate() in src.loc - if(D) - linked = D - if (!D.trunk) - D.trunk = src - - var/obj/structure/disposaloutlet/O = locate() in src.loc - if(O) - linked = O - return - - // Override attackby so we disallow trunkremoval when somethings ontop -/obj/structure/disposalpipe/trunk/attackby(obj/item/I, mob/user) - - //Disposal bins or chutes - /* - These shouldn't be required - var/obj/machinery/disposal/D = locate() in src.loc - if(D && D.anchored) - return - - //Disposal outlet - var/obj/structure/disposaloutlet/O = locate() in src.loc - if(O && O.anchored) - return - */ - - //Disposal constructors - var/obj/structure/disposalconstruct/C = locate() in src.loc - if(C && C.anchored) - return - - var/turf/T = src.loc - if(!T.is_plating()) - return // prevent interaction with T-scanner revealed pipes - src.add_fingerprint(user, 0, I) - if(istype(I, /obj/item/weldingtool)) - var/obj/item/weldingtool/W = I - - if(W.remove_fuel(0,user)) - playsound(src, W.tool_sound, 100, 1) - // check if anything changed over 2 seconds - var/turf/uloc = user.loc - var/atom/wloc = W.loc - to_chat(user, "Slicing the disposal pipe.") - sleep(30) - if(!W.isOn()) return - if(user.loc == uloc && wloc == W.loc) - welded() - else - to_chat(user, "You must stay still while welding the pipe.") - else - to_chat(user, "You need more welding fuel to cut the pipe.") - return - - // would transfer to next pipe segment, but we are in a trunk - // if not entering from disposal bin, - // transfer to linked object (outlet or bin) - -/obj/structure/disposalpipe/trunk/transfer(var/obj/structure/disposalholder/H) - - if(H.dir == DOWN) // we just entered from a disposer - return ..() // so do base transfer proc - // otherwise, go to the linked object - if(linked) - var/obj/structure/disposaloutlet/O = linked - if(istype(O) && (H)) - O.expel(H) // expel at outlet - else - var/obj/machinery/disposal/D = linked - if(H) - D.expel(H) // expel at disposal - else - if(H) - src.expel(H, src.loc, 0) // expel at turf - return null - - // nextdir - -/obj/structure/disposalpipe/trunk/nextdir(var/fromdir) - if(fromdir == DOWN) - return dir - else - return 0 - -// a broken pipe -/obj/structure/disposalpipe/broken - icon_state = "pipe-b" - dpdir = 0 // broken pipes have dpdir=0 so they're not found as 'real' pipes - // i.e. will be treated as an empty turf - desc = "A broken piece of disposal pipe." - -// called when welded -// for broken pipe, remove and turn into scrap -/obj/structure/disposalpipe/broken/welded() -// var/obj/item/scrap/S = new(src.loc) -// S.set_components(200,0,0) - qdel(src) - -// the disposal outlet machine - -/obj/structure/disposaloutlet - name = "disposal outlet" - desc = "An outlet for the pneumatic disposal system." - icon = 'icons/obj/pipes/disposal.dmi' - icon_state = "outlet" - density = 1 - anchored = 1 - var/active = 0 - var/turf/target // this will be where the output objects are 'thrown' to. - var/mode = 0 - -/obj/structure/disposaloutlet/LateInitialize() - target = get_ranged_target_turf(src, dir, 10) - var/obj/structure/disposalpipe/trunk/trunk = locate() in loc - if(trunk) - trunk.linked = src // link the pipe trunk to self - -// expel the contents of the holder object, then delete it -// called when the holder exits the outlet -/obj/structure/disposaloutlet/proc/expel(obj/structure/disposalholder/H) - - target = get_ranged_target_turf(src, dir, 10) - flick("outlet-open", src) - playsound(src, 'sound/machines/warning-buzzer.ogg', 50, 0, 0) - sleep(20) //wait until correct animation frame - playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) - - if(H) - for(var/atom/movable/AM in H) - AM.forceMove(src.loc) - AM.pipe_eject(dir) - if(!istype(AM,/mob/living/silicon/robot/drone)) //Drones keep smashing windows from being fired out of chutes. Bad for the station. ~Z - spawn(5) - AM.throw_at_old(target, 3, 1) - H.vent_gas(src.loc) - qdel(H) - - return - -/obj/structure/disposaloutlet/attackby(obj/item/I, mob/user) - if(!I || !user) - return - src.add_fingerprint(user, 0, I) - if(I.is_screwdriver()) - if(mode==0) - mode=1 - to_chat(user, "You remove the screws around the power connection.") - playsound(src, I.tool_sound, 50, 1) - return - else if(mode==1) - mode=0 - to_chat(user, "You attach the screws around the power connection.") - playsound(src, I.tool_sound, 50, 1) - return - else if(istype(I, /obj/item/weldingtool) && mode==1) - var/obj/item/weldingtool/W = I - if(W.remove_fuel(0,user)) - playsound(src, W.tool_sound, 100, 1) - to_chat(user, "You start slicing the floorweld off the disposal outlet.") - if(do_after(user,20 * W.tool_speed)) - if(!src || !W.isOn()) return - to_chat(user, "You sliced the floorweld off the disposal outlet.") - var/obj/structure/disposalconstruct/C = new (src.loc) - src.transfer_fingerprints_to(C) - C.ptype = 7 // 7 = outlet - C.update() - C.anchored = 1 - C.density = 1 - qdel(src) - return - else - to_chat(user, "You need more welding fuel to complete this task.") - return - -// called when movable is expelled from a disposal pipe or outlet -// by default does nothing, override for special behaviour -/atom/movable/proc/pipe_eject(direction) - return - -// check if mob has client, if so restore client view on eject -/mob/pipe_eject(direction) - update_perspective() - -/obj/effect/debris/cleanable/blood/gibs/pipe_eject(direction) - var/list/dirs - if(direction) - dirs = list( direction, turn(direction, -45), turn(direction, 45)) - else - dirs = GLOB.alldirs.Copy() - - src.streak(dirs) - -/obj/effect/debris/cleanable/blood/gibs/robot/pipe_eject(direction) - var/list/dirs - if(direction) - dirs = list( direction, turn(direction, -45), turn(direction, 45)) - else - dirs = GLOB.alldirs.Copy() - - src.streak(dirs) diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm deleted file mode 100644 index 8b793c7df654..000000000000 --- a/code/modules/recycling/sortingmachinery.dm +++ /dev/null @@ -1,469 +0,0 @@ -/obj/structure/bigDelivery - desc = "A big wrapped package." - name = "large parcel" - icon = 'icons/obj/storage.dmi' - icon_state = "deliverycloset" - var/obj/wrapped = null - density = 1 - var/sortTag = null - mouse_drag_pointer = MOUSE_ACTIVE_POINTER - var/examtext = null - var/nameset = 0 - var/label_y - var/label_x - var/tag_x - -/obj/structure/bigDelivery/attack_hand(mob/user, list/params) - unwrap() - -/obj/structure/bigDelivery/proc/unwrap() - // Destroy will drop our wrapped object on the turf, so let it. - qdel(src) - -/obj/structure/bigDelivery/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W, /obj/item/destTagger)) - var/obj/item/destTagger/O = W - if(O.currTag) - if(src.sortTag != O.currTag) - to_chat(user, "You have labeled the destination as [O.currTag].") - if(!src.sortTag) - src.sortTag = O.currTag - update_icon() - else - src.sortTag = O.currTag - playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 1) - else - to_chat(user, "The package is already labeled for [O.currTag].") - else - to_chat(user, "You need to set a destination first!") - - else if(istype(W, /obj/item/pen)) - switch(alert("What would you like to alter?",,"Title","Description", "Cancel")) - if("Title") - var/str = sanitizeSafe(input(usr,"Label text?","Set label",""), MAX_NAME_LEN) - if(!str || !length(str)) - to_chat(user, " Invalid text.") - return - user.visible_message("\The [user] titles \the [src] with \a [W], marking down: \"[str]\"",\ - "You title \the [src]: \"[str]\"",\ - "You hear someone scribbling a note.") - name = "[name] ([str])" - if(!examtext && !nameset) - nameset = 1 - update_icon() - else - nameset = 1 - if("Description") - var/str = sanitize(input(usr,"Label text?","Set label","")) - if(!str || !length(str)) - to_chat(user, "Invalid text.") - return - if(!examtext && !nameset) - examtext = str - update_icon() - else - examtext = str - user.visible_message("\The [user] labels \the [src] with \a [W], scribbling down: \"[examtext]\"",\ - "You label \the [src]: \"[examtext]\"",\ - "You hear someone scribbling a note.") - return - -/obj/structure/bigDelivery/update_icon() - cut_overlays() - if(nameset || examtext) - var/image/I = new/image('icons/obj/storage.dmi',"delivery_label") - if(icon_state == "deliverycloset") - I.pixel_x = 2 - if(label_y == null) - label_y = rand(-6, 11) - I.pixel_y = label_y - else if(icon_state == "deliverycrate") - if(label_x == null) - label_x = rand(-8, 6) - I.pixel_x = label_x - I.pixel_y = -3 - add_overlay(I) - if(sortTag) - var/image/I = new/image('icons/obj/storage.dmi',"delivery_tag") - if(icon_state == "deliverycloset") - if(tag_x == null) - tag_x = rand(-2, 3) - I.pixel_x = tag_x - I.pixel_y = 9 - else if(icon_state == "deliverycrate") - if(tag_x == null) - tag_x = rand(-8, 6) - I.pixel_x = tag_x - I.pixel_y = -3 - add_overlay(I) - -/obj/structure/bigDelivery/examine(mob/user, dist) - . = ..() - if(sortTag) - . += "It is labeled \"[sortTag]\"" - if(examtext) - . += "It has a note attached which reads, \"[examtext]\"" - -/obj/structure/bigDelivery/Destroy() - if(wrapped) //sometimes items can disappear. For example, bombs. --rastaf0 - wrapped.forceMove(get_turf(src)) - if(istype(wrapped, /obj/structure/closet)) - var/obj/structure/closet/O = wrapped - O.sealed = 0 - wrapped = null - var/turf/T = get_turf(src) - for(var/atom/movable/AM in contents) - AM.forceMove(T) - return ..() - -/obj/item/smallDelivery - desc = "A small wrapped package." - name = "small parcel" - icon = 'icons/obj/storage.dmi' - icon_state = "deliverycrate3" - drop_sound = 'sound/items/drop/cardboardbox.ogg' - pickup_sound = 'sound/items/pickup/cardboardbox.ogg' - var/obj/item/wrapped = null - var/sortTag = null - var/examtext = null - var/nameset = 0 - var/tag_x - -/obj/item/smallDelivery/attack_self(mob/user) - . = ..() - if(.) - return - if (src.wrapped) //sometimes items can disappear. For example, bombs. --rastaf0 - wrapped.loc = user.loc - if(ishuman(user)) - user.put_in_hands(wrapped) - else - wrapped.loc = get_turf(src) - - qdel(src) - return - -/obj/item/smallDelivery/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W, /obj/item/destTagger)) - var/obj/item/destTagger/O = W - if(O.currTag) - if(src.sortTag != O.currTag) - to_chat(user, "You have labeled the destination as [O.currTag].") - if(!src.sortTag) - src.sortTag = O.currTag - update_icon() - else - src.sortTag = O.currTag - playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 1) - else - to_chat(user, "The package is already labeled for [O.currTag].") - else - to_chat(user, "You need to set a destination first!") - - else if(istype(W, /obj/item/pen)) - switch(alert("What would you like to alter?",,"Title","Description", "Cancel")) - if("Title") - var/str = sanitizeSafe(input(usr,"Label text?","Set label",""), MAX_NAME_LEN) - if(!str || !length(str)) - to_chat(user, " Invalid text.") - return - user.visible_message("\The [user] titles \the [src] with \a [W], marking down: \"[str]\"",\ - "You title \the [src]: \"[str]\"",\ - "You hear someone scribbling a note.") - name = "[name] ([str])" - if(!examtext && !nameset) - nameset = 1 - update_icon() - else - nameset = 1 - - if("Description") - var/str = sanitize(input(usr,"Label text?","Set label","")) - if(!str || !length(str)) - to_chat(user, "Invalid text.") - return - if(!examtext && !nameset) - examtext = str - update_icon() - else - examtext = str - user.visible_message("\The [user] labels \the [src] with \a [W], scribbling down: \"[examtext]\"",\ - "You label \the [src]: \"[examtext]\"",\ - "You hear someone scribbling a note.") - return - -/obj/item/smallDelivery/update_icon() - cut_overlays() - if((nameset || examtext) && icon_state != "deliverycrate1") - var/image/I = new/image('icons/obj/storage.dmi',"delivery_label") - if(icon_state == "deliverycrate5") - I.pixel_y = -1 - add_overlay(I) - if(sortTag) - var/image/I = new/image('icons/obj/storage.dmi',"delivery_tag") - switch(icon_state) - if("deliverycrate1") - I.pixel_y = -5 - if("deliverycrate2") - I.pixel_y = -2 - if("deliverycrate3") - I.pixel_y = 0 - if("deliverycrate4") - if(tag_x == null) - tag_x = rand(0,5) - I.pixel_x = tag_x - I.pixel_y = 3 - if("deliverycrate5") - I.pixel_y = -3 - add_overlay(I) - -/obj/item/smallDelivery/examine(mob/user, dist) - . = ..() - if(sortTag) - . += "It is labeled \"[sortTag]\"" - if(examtext) - . += "It has a note attached which reads, \"[examtext]\"" - -/obj/item/packageWrap - name = "package wrapper" - icon = 'icons/obj/items.dmi' - icon_state = "deliveryPaper" - w_class = WEIGHT_CLASS_NORMAL - var/amount = 25.0 - - -/obj/item/packageWrap/afterattack(atom/movable/target, mob/user, clickchain_flags, list/params) - if(!(clickchain_flags & CLICKCHAIN_HAS_PROXIMITY)) return - if(!istype(target)) //this really shouldn't be necessary (but it is). -Pete - return - if(istype(target, /obj/item/smallDelivery) || istype(target,/obj/structure/bigDelivery) \ - || istype(target, /obj/item/gift) || istype(target, /obj/item/evidencebag)) - return - if(target.anchored) - return - if(target in user) - return - if(user in target) //no wrapping closets that you are inside - it's not physically possible - return - - user.attack_log += "\[[time_stamp()]\] Has used [name] on \ref[target]" - - - if (istype(target, /obj/item) && !(istype(target, /obj/item/storage) && !istype(target,/obj/item/storage/box))) - var/obj/item/O = target - if (src.amount > 1) - var/obj/item/smallDelivery/P = new /obj/item/smallDelivery(get_turf(O.loc)) //Aaannd wrap it up! - if(!istype(O.loc, /turf)) - if(user.client) - user.client.screen -= O - P.wrapped = O - O.forceMove(P) - P.set_weight_class(O.w_class) - var/i = round(O.get_weight_class()) - if(i in list(1,2,3,4,5)) - P.icon_state = "deliverycrate[i]" - switch(i) - if(1) P.name = "tiny parcel" - if(3) P.name = "normal-sized parcel" - if(4) P.name = "large parcel" - if(5) P.name = "huge parcel" - if(i < 1) - P.icon_state = "deliverycrate1" - P.name = "tiny parcel" - if(i > 5) - P.icon_state = "deliverycrate5" - P.name = "huge parcel" - P.add_fingerprint(usr) - O.add_fingerprint(usr) - src.add_fingerprint(usr) - src.amount -= 1 - user.visible_message("\The [user] wraps \a [target] with \a [src].",\ - "You wrap \the [target], leaving [amount] units of paper on \the [src].",\ - "You hear someone taping paper around a small object.") - else if (istype(target, /obj/structure/closet/crate)) - var/obj/structure/closet/crate/O = target - if (src.amount > 3 && !O.opened) - var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc)) - P.icon_state = "deliverycrate" - P.wrapped = O - O.loc = P - src.amount -= 3 - user.visible_message("\The [user] wraps \a [target] with \a [src].",\ - "You wrap \the [target], leaving [amount] units of paper on \the [src].",\ - "You hear someone taping paper around a large object.") - else if(src.amount < 3) - to_chat(user, "You need more paper.") - else if (istype (target, /obj/structure/closet)) - var/obj/structure/closet/O = target - if (src.amount > 3 && !O.opened) - var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc)) - P.wrapped = O - O.sealed = 1 - O.loc = P - src.amount -= 3 - user.visible_message("\The [user] wraps \a [target] with \a [src].",\ - "You wrap \the [target], leaving [amount] units of paper on \the [src].",\ - "You hear someone taping paper around a large object.") - else if(src.amount < 3) - to_chat(user, "You need more paper.") - else - to_chat(user, "The object you are trying to wrap is unsuitable for the sorting machinery!") - if (src.amount <= 0) - new /obj/item/c_tube( src.loc ) - qdel(src) - return - return - -/obj/item/packageWrap/examine(mob/user, dist) - . = ..() - . += "There are [amount] units of package wrap left!" - -/obj/item/destTagger - name = "destination tagger" - desc = "Used to set the destination of properly wrapped packages." - icon = 'icons/obj/device.dmi' - icon_state = "dest_tagger" - var/currTag = 0 - - w_class = WEIGHT_CLASS_SMALL - item_state = "electronic" - slot_flags = SLOT_BELT - -/obj/item/destTagger/ui_state() - return GLOB.inventory_state - -/obj/item/destTagger/ui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "DestinationTagger", name) - ui.open() - -/obj/item/destTagger/ui_data(mob/user, datum/tgui/ui) - var/list/data = ..() - - data["currTag"] = currTag - data["taggerLocs"] = GLOB.tagger_locations - - return data - -/obj/item/destTagger/attack_self(mob/user) - . = ..() - if(.) - return - ui_interact(user) - -/obj/item/destTagger/ui_act(action, list/params, datum/tgui/ui) - if(..()) - return TRUE - add_fingerprint(usr) - switch(action) - if("set_tag") - var/new_tag = params["tag"] - if(!(new_tag in GLOB.tagger_locations)) - return FALSE - currTag = new_tag - . = TRUE - -/obj/machinery/disposal/deliveryChute - name = "Delivery chute" - desc = "A chute for big and small packages alike!" - density = 1 - icon_state = "intake" - - var/c_mode = 0 - -/obj/machinery/disposal/deliveryChute/Initialize(mapload, newdir) - . = ..() - spawn(5) - trunk = locate() in src.loc - if(trunk) - trunk.linked = src // link the pipe trunk to self - -/obj/machinery/disposal/deliveryChute/interact() - return - -/obj/machinery/disposal/deliveryChute/update() - return - -/obj/machinery/disposal/deliveryChute/Bumped(var/atom/movable/AM) //Go straight into the chute - if(istype(AM, /obj/projectile) || istype(AM, /obj/effect) || istype(AM, /obj/vehicle/sealed/mecha)) return - switch(dir) - if(NORTH) - if(AM.loc.y != src.loc.y+1) return - if(EAST) - if(AM.loc.x != src.loc.x+1) return - if(SOUTH) - if(AM.loc.y != src.loc.y-1) return - if(WEST) - if(AM.loc.x != src.loc.x-1) return - -// if(istype(AM, has_buckled_mobs()) return // I dont know what im doing @ktoma36 - - if(istype(AM, /obj)) - var/obj/O = AM - O.loc = src - else if(istype(AM, /mob)) - var/mob/M = AM - M.loc = src - src.flush() - -/obj/machinery/disposal/deliveryChute/flush() - flushing = 1 - flick("intake-closing", src) - var/obj/structure/disposalholder/H = new() // virtual holder object which actually - // travels through the pipes. - air_contents = new() // new empty gas resv. - - sleep(10) - playsound(src, 'sound/machines/disposalflush.ogg', 50, 0, 0) - sleep(5) // wait for animation to finish - - H.init(src) // copy the contents of disposer to holder - - H.start(src) // start the holder processing movement - flushing = 0 - // now reset disposal state - flush = 0 - if(mode == 2) // if was ready, - mode = 1 // switch to charging - update() - return - -/obj/machinery/disposal/deliveryChute/attackby(var/obj/item/I, var/mob/user) - if(!I || !user) - return - - if(I.is_screwdriver()) - if(c_mode==0) - c_mode=1 - playsound(src.loc, I.tool_sound, 50, 1) - to_chat(user, "You remove the screws around the power connection.") - return - else if(c_mode==1) - c_mode=0 - playsound(src.loc, I.tool_sound, 50, 1) - to_chat(user, "You attach the screws around the power connection.") - return - else if(istype(I, /obj/item/weldingtool) && c_mode==1) - var/obj/item/weldingtool/W = I - if(W.remove_fuel(0,user)) - playsound(src.loc, W.tool_sound, 50, 1) - to_chat(user, "You start slicing the floorweld off the delivery chute.") - if(do_after(user,20 * W.tool_speed)) - if(!src || !W.isOn()) return - to_chat(user, "You sliced the floorweld off the delivery chute.") - var/obj/structure/disposalconstruct/C = new (src.loc) - C.ptype = 8 // 8 = Delivery chute - C.update() - C.anchored = 1 - C.density = 1 - qdel(src) - return - else - to_chat(user, "You need more welding fuel to complete this task.") - return - -/obj/machinery/disposal/deliveryChute/Destroy() - if(trunk) - trunk.linked = null - ..() From d3bcbe2c10391c87b3f9232e468a504334385917 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Tue, 3 Sep 2024 21:33:06 -0400 Subject: [PATCH 24/35] fixes compile on latest byond (#6709) --- code/datums/mutable_appearance.dm | 9 ++++----- code/game/rendering/legacy/radial.dm | 2 -- code/game/rendering/parallax/parallax_object.dm | 2 ++ code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm | 2 ++ code/modules/overmap/bounds.dm | 4 ++++ 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/code/datums/mutable_appearance.dm b/code/datums/mutable_appearance.dm index 1f08f52c6c2c..d7e04d7a14cf 100644 --- a/code/datums/mutable_appearance.dm +++ b/code/datums/mutable_appearance.dm @@ -4,11 +4,10 @@ // Mutable appearances are children of images, just so you know. -/mutable_appearance/New(copy_from, ...) - ..() - if(!copy_from) - plane = FLOAT_PLANE // No clue why this is 0 by default yet images are on FLOAT_PLANE - // And yes this does have to be in the constructor, BYOND ignores it if you set it as a normal var +/** + * * Mutable appearances do **not** have FLOAT_PLANE by default. Set it manually if you aren't using [mutable_appearance()] + */ +/mutable_appearance // Helper similar to image() /proc/mutable_appearance(icon, icon_state = "", layer = FLOAT_LAYER, plane = FLOAT_PLANE, alpha = 255, appearance_flags = NONE) diff --git a/code/game/rendering/legacy/radial.dm b/code/game/rendering/legacy/radial.dm index 31b2425da037..39da472582e4 100644 --- a/code/game/rendering/legacy/radial.dm +++ b/code/game/rendering/legacy/radial.dm @@ -256,7 +256,6 @@ GLOBAL_LIST_EMPTY(radial_menus) choices_icons[id] = I setup_menu(use_tooltips) - /datum/radial_menu/proc/extract_image(E) var/mutable_appearance/MA = new /mutable_appearance(E) if(MA) @@ -264,7 +263,6 @@ GLOBAL_LIST_EMPTY(radial_menus) MA.appearance_flags |= RESET_TRANSFORM return MA - /datum/radial_menu/proc/next_page() if(pages > 1) current_page = WRAP(current_page + 1,1,pages+1) diff --git a/code/game/rendering/parallax/parallax_object.dm b/code/game/rendering/parallax/parallax_object.dm index ac298821a082..b998484e9fee 100644 --- a/code/game/rendering/parallax/parallax_object.dm +++ b/code/game/rendering/parallax/parallax_object.dm @@ -84,6 +84,8 @@ clone.icon = icon clone.icon_state = icon_state clone.overlays = GetOverlays() + clone.layer = FLOAT_LAYER + clone.plane = FLOAT_PLANE // do NOT inherit our overlays! parallax layers should never have overlays, // because if it inherited us it'll result in exponentially increasing overlays // due to cut_overlays() above over there being a queue operation and not instant! diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm b/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm index 94089a1a4213..77f23e97c33e 100644 --- a/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm +++ b/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm @@ -5,6 +5,8 @@ /proc/_animate(atom/A, set_vars, time = 10, loop = 1, easing = LINEAR_EASING, flags = null) var/mutable_appearance/MA = new() + // mutable appearance is not FLOAT_PLANE by default + MA.plane = FLOAT_PLANE for(var/v in set_vars) MA.vars[v] = set_vars[v] animate(A, appearance = MA, time, loop, easing, flags) diff --git a/code/modules/overmap/bounds.dm b/code/modules/overmap/bounds.dm index 4ba1da1d2983..b373fff61c76 100644 --- a/code/modules/overmap/bounds.dm +++ b/code/modules/overmap/bounds.dm @@ -20,6 +20,8 @@ var/mutable_appearance/MA = new . = MA MA.appearance_flags = KEEP_APART | RESET_TRANSFORM + // mutable appearance is not FLOAT_PLANE by default + MA.plane = FLOAT_PLANE MA.icon = I MA.color = color MA.alpha = 160 @@ -59,6 +61,8 @@ var/mutable_appearance/MA = new . = MA MA.appearance_flags = KEEP_APART | RESET_TRANSFORM + // mutable appearance is not FLOAT_PLANE by default + MA.plane = FLOAT_PLANE MA.icon = I MA.alpha = 160 MA.filters = filter( From f26c79e34d2192497d9882a5857c6af8371e3722 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Tue, 3 Sep 2024 21:34:30 -0400 Subject: [PATCH 25/35] item interaction API (#6721) the start of the death of attackby() **There's no support for not meleeing with this system, yet.** The idea is eventually as more things use it, melee will be at a higher level to skip needing to check user's intent entirely. --- citadel.dme | 3 + .../signals_atom/signals_atom-clickchain.dm | 7 ++ .../signals_item/signals_item-clickchain.dm | 7 ++ code/datums/event_args/clickchain.dm | 1 + code/game/click/items-item_attack_chain.dm | 70 +++++++++++++++++++ code/game/click/items.dm | 5 ++ 6 files changed, 93 insertions(+) create mode 100644 code/__DEFINES/dcs/signals/signals_atom/signals_atom-clickchain.dm create mode 100644 code/__DEFINES/dcs/signals/signals_item/signals_item-clickchain.dm create mode 100644 code/game/click/items-item_attack_chain.dm diff --git a/citadel.dme b/citadel.dme index faa460e3c9a6..2e7d1b2a717b 100644 --- a/citadel.dme +++ b/citadel.dme @@ -178,6 +178,7 @@ #include "code\__DEFINES\dcs\signals\items\signals_inducer.dm" #include "code\__DEFINES\dcs\signals\modules\signals_module_fishing.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-buckling.dm" +#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-clickchain.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-context_system.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-defense.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-reachability.dm" @@ -191,6 +192,7 @@ #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_movement.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_radiation.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_x_act.dm" +#include "code\__DEFINES\dcs\signals\signals_item\signals_item-clickchain.dm" #include "code\__DEFINES\dcs\signals\signals_item\signals_item-interaction.dm" #include "code\__DEFINES\dcs\signals\signals_item\signals_item_economy.dm" #include "code\__DEFINES\dcs\signals\signals_item\signals_item_inventory.dm" @@ -1048,6 +1050,7 @@ #include "code\game\click\cyborg.dm" #include "code\game\click\drag_drop.dm" #include "code\game\click\item_attack.dm" +#include "code\game\click\items-item_attack_chain.dm" #include "code\game\click\items.dm" #include "code\game\click\mobs.dm" #include "code\game\click\observer.dm" diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom-clickchain.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-clickchain.dm new file mode 100644 index 000000000000..989eb5321ab8 --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-clickchain.dm @@ -0,0 +1,7 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +//* All of these return CLICKCHAIN_* flags. *// + +/// from /obj/item/proc/item_attack_chain(): (obj/item/using, datum/event_args/actor/clickchain/e_args, clickchain_flags, datum/callback/reachability_check) +#define COMSIG_ATOM_USING_ITEM_ON "using_item_on" diff --git a/code/__DEFINES/dcs/signals/signals_item/signals_item-clickchain.dm b/code/__DEFINES/dcs/signals/signals_item/signals_item-clickchain.dm new file mode 100644 index 000000000000..a6813cd46db2 --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_item/signals_item-clickchain.dm @@ -0,0 +1,7 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +//* All of these return CLICKCHAIN_* flags. *// + +/// from /obj/item/proc/item_attack_chain(): (atom/target, datum/event_args/actor/clickchain/e_args, clickchain_flags, datum/callback/reachability_check) +#define COMSIG_ITEM_USING_AS_ITEM "using_as_item" diff --git a/code/datums/event_args/clickchain.dm b/code/datums/event_args/clickchain.dm index 66fc14937bdf..32ec7f0bdae5 100644 --- a/code/datums/event_args/clickchain.dm +++ b/code/datums/event_args/clickchain.dm @@ -3,6 +3,7 @@ * * the click may be real or fake. * + * * clickchain flags are deliberately not stored in here; you're supposed to modify and return them a ton, so it's inefficient to put it in here. * * This is required for item swings / interaction, usually, not just base /event_args/actor. */ /datum/event_args/actor/clickchain diff --git a/code/game/click/items-item_attack_chain.dm b/code/game/click/items-item_attack_chain.dm new file mode 100644 index 000000000000..8812171abc2d --- /dev/null +++ b/code/game/click/items-item_attack_chain.dm @@ -0,0 +1,70 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station developers. *// + +//* Code for interacting as an item. *// + +/** + * Called to initiate a usage as item. + * + * * Called before tool_attack_chain() + * * Called before melee_attack_chain() + * + * @params + * * target - thing we're being used on + * * e_args - the clickchain data, including who's doing the interaction + * * clickchain_flags - the clickchain flags given + * * reachability_check - a callback used for reachability checks. if none, defaults to mob.Reachability when in clickcode, can always reach otherwise. + * + * @return CLICKCHAIN_* flags. These are added / interpreted the caller. + */ +/obj/item/proc/item_attack_chain(atom/target, datum/event_args/actor/clickchain/e_args, clickchain_flags, datum/callback/reachability_check) + SHOULD_NOT_OVERRIDE(TRUE) + + . = clickchain_flags + + . |= SEND_SIGNAL(src, COMSIG_ITEM_USING_AS_ITEM, target, e_args, clickchain_flags, reachability_check) + if(. & CLICKCHAIN_DO_NOT_PROPAGATE) + return + . |= using_as_item(target, e_args, clickchain_flags, reachability_check) + if(. & CLICKCHAIN_DO_NOT_PROPAGATE) + return + + . |= SEND_SIGNAL(src, COMSIG_ATOM_USING_ITEM_ON, src, e_args, clickchain_flags, reachability_check) + if(. & CLICKCHAIN_DO_NOT_PROPAGATE) + return + . |= target.using_item_on(src, e_args, clickchain_flags, reachability_check) + if(. & CLICKCHAIN_DO_NOT_PROPAGATE) + return + +/** + * Called when being used as an item. + * + * * If handled, return CLICKCHAIN_DO_NOT_PROPAGATE + * + * @params + * * target - thing we're being used on + * * e_args - the clickchain data, including who's doing the interaction + * * clickchain_flags - the clickchain flags given + * * reachability_check - a callback used for reachability checks. if none, defaults to mob.Reachability when in clickcode, can always reach otherwise. + * + * @return CLICKCHAIN_* flags. These are added / interpreted by the caller. + */ +/obj/item/proc/using_as_item(atom/target, datum/event_args/actor/clickchain/e_args, clickchain_flags, datum/callback/reachability_check) + return NONE + +/** + * Called when an item is being used on us as an item. + * + * * If handled, return CLICKCHAIN_DO_NOT_PROPAGATE + * * item's `item_attack_chain`, and then `used_as_item` are called first. + * + * @params + * * using - the item + * * e_args - the clickchain data, including who's doing the interaction + * * clickchain_flags - the clickchain flags given + * * reachability_check - a callback used for reachability checks. if none, defaults to mob.Reachability when in clickcode, can always reach otherwise. + * + * @return CLICKCHAIN_* flags. These are added / interpreted by the caller. + */ +/atom/proc/using_item_on(obj/item/using, datum/event_args/actor/clickchain/e_args, clickchain_flags, datum/callback/reachability_check) + return NONE diff --git a/code/game/click/items.dm b/code/game/click/items.dm index 607c9c2a1745..2e3111c6c0e3 100644 --- a/code/game/click/items.dm +++ b/code/game/click/items.dm @@ -27,6 +27,11 @@ // todo: inject something here for 'used as item' much like /tg/, to get rid of attackby pattern + var/datum/event_args/actor/clickchain/e_args = new(user) + + if((. |= item_attack_chain(target, e_args, ., params)) & CLICKCHAIN_DO_NOT_PROPAGATE) + return + if((. |= tool_attack_chain(target, user, ., params)) & CLICKCHAIN_DO_NOT_PROPAGATE) return From 710a7cc8712ccbd64dbbb570d57c9c176bee306f Mon Sep 17 00:00:00 2001 From: Niezan Date: Tue, 3 Sep 2024 18:52:14 -0700 Subject: [PATCH 26/35] lets mechs open blast doors (#6729) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## About The Pull Request it lets mechs interact with blast door buttons ## Why It's Good For The Game they can already interact with airlocks and stuff. its kind of stupid you have to eject like four times and get back in four times to get a mech out properly. 🥺 ## Changelog :cl: qol: mechs can now operate blast door controls /:cl: --- code/modules/vehicles/sealed/mecha/mecha.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/vehicles/sealed/mecha/mecha.dm b/code/modules/vehicles/sealed/mecha/mecha.dm index f5e86771f096..5af90412324d 100644 --- a/code/modules/vehicles/sealed/mecha/mecha.dm +++ b/code/modules/vehicles/sealed/mecha/mecha.dm @@ -624,7 +624,7 @@ return /obj/vehicle/sealed/mecha/proc/interface_action(obj/machinery/target) - if(istype(target, /obj/machinery/access_button)) + if(istype(target, /obj/machinery/access_button) || istype(target, /obj/machinery/button/remote/blast_door)) src.occupant_message("Interfacing with [target].") src.log_message("Interfaced with [target].") target.attack_hand(src.occupant_legacy) From 2969f977b461e2c123843999506f97d5eb18fd5a Mon Sep 17 00:00:00 2001 From: Niezan Date: Tue, 3 Sep 2024 18:52:54 -0700 Subject: [PATCH 27/35] atmosphere retention field touch message is now toucher only (#6726) ## About The Pull Request atmosphere retention field touch message is now toucher only ## Why It's Good For The Game it was an aoe msg with first person structure ## Changelog :cl: fix: atmos field touching msg is now toucher only /:cl: --- code/game/machinery/atm_ret_field.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/machinery/atm_ret_field.dm b/code/game/machinery/atm_ret_field.dm index 296cb80b75b1..5190b86c6bb4 100644 --- a/code/game/machinery/atm_ret_field.dm +++ b/code/game/machinery/atm_ret_field.dm @@ -223,9 +223,9 @@ /obj/structure/atmospheric_retention_field/attack_hand(mob/user, list/params) if(density) - visible_message("You touch the retention field, and it crackles faintly. Tingly!") + to_chat(user, "You touch the retention field, and it crackles faintly. Tingly!") else - visible_message("You try to touch the retention field, but pass through it like it isn't even there.") + to_chat(user, "You try to touch the retention field, but pass through it like it isn't even there.") /obj/structure/atmospheric_retention_field/legacy_ex_act() return From 784368f1a1fa18eea8581bc78489cd1270ee9c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AD=BB=E7=83=8F?= <85062773+washikarasu@users.noreply.github.com> Date: Wed, 4 Sep 2024 02:54:10 +0100 Subject: [PATCH 28/35] Nebula Air Supply (#6723) ## About The Pull Request Changes the Nebula Air Supply ## Why It's Good For The Game Because it works without having to break into the super pressure chamber to fix it. ## Changelog Removes two useless pumps, replacing it with one high power pump and activates the vent. :cl: add: added a high power pump del: removed two regular pumps tweak: activates the vent in the Nebula air supply --- .../tradeport_192/levels/tradeport_192.dmm | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/maps/sectors/tradeport_192/levels/tradeport_192.dmm b/maps/sectors/tradeport_192/levels/tradeport_192.dmm index 53e25968b662..14ff915c4c12 100644 --- a/maps/sectors/tradeport_192/levels/tradeport_192.dmm +++ b/maps/sectors/tradeport_192/levels/tradeport_192.dmm @@ -6033,18 +6033,8 @@ /turf/simulated/floor/tiled/dark, /area/shuttle/trade_ship/cockpit) "vA" = ( -/obj/machinery/atmospherics/component/unary/vent_pump{ - dir = 4; - external_pressure_bound = 0; - external_pressure_bound_default = 0; - frequency = null; - icon_state = "map_vent_in"; - initialize_directions = 1; - internal_pressure_bound = 4000; - internal_pressure_bound_default = 4000; - pressure_checks = 2; - pressure_checks_default = 2; - pump_direction = 0 +/obj/machinery/atmospherics/component/unary/vent_pump/siphon/on{ + dir = 4 }, /turf/simulated/floor/reinforced/airmix, /area/tradeport/atmospherics) @@ -14149,9 +14139,10 @@ /turf/simulated/floor/outdoors/beach/sand/desert/indoors, /area/tradeport/safarizoo) "YT" = ( -/obj/machinery/atmospherics/component/binary/pump/on{ +/obj/machinery/atmospherics/component/binary/pump/high_power/on{ dir = 1; - name = "Airmix to Distro" + name = "Air Supply"; + target_pressure = 1000 }, /turf/simulated/floor/plating, /area/tradeport/atmospherics) @@ -14294,14 +14285,14 @@ /turf/simulated/floor/wmarble, /area/tradeport/cafeteria) "Zu" = ( -/obj/machinery/atmospherics/component/binary/pump/on{ - dir = 4 - }, /obj/effect/floor_decal/industrial/warning{ dir = 8 }, /obj/effect/debris/cleanable/blood/gibs/robot/limb, /obj/effect/floor_decal/rust, +/obj/machinery/atmospherics/pipe/simple/visible/cyan{ + dir = 4 + }, /turf/simulated/floor/plating, /area/tradeport/atmospherics) "Zv" = ( From 8b408bd95c7835aff90e06b3615febc7e2c68f5b Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 4 Sep 2024 06:01:41 -0400 Subject: [PATCH 29/35] fixes floor underlays (#6731) sigh WHY ARE WE USING MUTABLE APPEARANCES FOR THIS --- code/game/turfs/simulated/floor/floor_types.dm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/game/turfs/simulated/floor/floor_types.dm b/code/game/turfs/simulated/floor/floor_types.dm index 4b555d2589fb..5bb299a9a46c 100644 --- a/code/game/turfs/simulated/floor/floor_types.dm +++ b/code/game/turfs/simulated/floor/floor_types.dm @@ -81,7 +81,9 @@ var/mutable_appearance/under_ma if(ispath(under)) // It's just a mapper-specified path - under_ma = new() + under_ma = new + under_ma.layer = FLOAT_LAYER + under_ma.plane = FLOAT_PLANE under_ma.icon = initial(under.icon) under_ma.icon_state = initial(under.icon_state) under_ma.color = initial(under.color) From 2f7ea576067e980bbf763c23ca2a57019539a42c Mon Sep 17 00:00:00 2001 From: Niezan Date: Wed, 4 Sep 2024 03:27:03 -0700 Subject: [PATCH 30/35] fixs point defense registering (#6727) ## About The Pull Request allows point defense control to be properly registered by checking if the new indent is taken, instead of the current tag. also makes sure that the batteries themselves dont check for id taged controllers with their own tag???? idk why that was a thing. im guessing copy paste. ## Why It's Good For The Game fixes bug where both were unregisterable. ## Changelog :cl: fix: point defense control now can be set. fix: point defense battery can be set to a new ident tag. /:cl: --- code/game/machinery/pointdefense.dm | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/code/game/machinery/pointdefense.dm b/code/game/machinery/pointdefense.dm index 4d71af4a6196..9fb3e3b69396 100644 --- a/code/game/machinery/pointdefense.dm +++ b/code/game/machinery/pointdefense.dm @@ -96,7 +96,7 @@ GLOBAL_LIST_BOILERPLATE(pointdefense_turrets, /obj/machinery/power/pointdefense) // Check for duplicate controllers with this ID for(var/thing in GLOB.pointdefense_controllers) var/obj/machinery/pointdefense_control/PC = thing - if(PC != src && PC.id_tag == id_tag) + if(PC != src && PC.id_tag == new_ident) to_chat(user, "The [new_ident] network already has a controller.") return to_chat(user, "You register [src] with the [new_ident] network.") @@ -208,11 +208,6 @@ GLOBAL_LIST_BOILERPLATE(pointdefense_turrets, /obj/machinery/power/pointdefense) if(W?.is_multitool()) var/new_ident = input(user, "Enter a new ident tag.", "[src]", id_tag) as null|text if(new_ident && new_ident != id_tag && user.Adjacent(src) && CanInteract(user, GLOB.physical_state)) - // Check for duplicate controllers with this ID - for(var/obj/machinery/pointdefense_control/PC as anything in GLOB.pointdefense_controllers) - if(PC != src && PC.id_tag == id_tag) - to_chat(user, SPAN_WARNING("\The [new_ident] network already has a controller!")) - return to_chat(user, SPAN_NOTICE("You register [src] with \the [new_ident] network.")) id_tag = new_ident return From 4224dff3d49a77118c44f5050afc5238879464e8 Mon Sep 17 00:00:00 2001 From: IrkallaEpsilon <42441793+IrkallaEpsilon@users.noreply.github.com> Date: Wed, 4 Sep 2024 12:27:54 +0200 Subject: [PATCH 31/35] Enigma wills it, Phase 4 Enigma Units Part 1: Sprites (#6724) Hi. I am nerding out and have been hyperobsessing with the Engima plotpoint we have going. For better or worse. Here are some sprites for Athena (The Eventmanager) ## About The Pull Request Athena said on Discord I am working on truly horrific Phase 4 units of Enigma. A bit of a background for those out of the loop: Enigma being a bad sapient AI which may or me not go singularity due to figuring out abductor tech fully and being able to improve upon it. So far it introduced 3 phases of troops: Phase 1: Hivebots augmented with abductor tech Phase 2: Original designs (do not steel) with specialized purposes Phase 3: Bipedal combat units, think Xcoms Advent troopers or your generic soldiers This introduces the spritework for phase 4. Not any code yet, I do this over the course of the week, might put that into a separate PR. Phase 4 will be specialized elite units utilizing nanotechnology (TM) and more exotic findings Enigma found. Basically they are experimental plattforms. I dont want to spoil too much but we got basically: A squadlead type of unit for P3 units armed with an enhanced abductor beam rifle, mild regeneration and superior reflexes (some dodge chance), all subject to change. Think of Xcoms Spectre with a good bit Xcoms Hunter and a certain unit from RA2 MO from a certain purple clad faction. Called a Wraith. An infiltrator, assassin and asset acquisition unit, bladed, lightning fast, able to apply stamloss and able to blink (thats this entire gimmick really, they may or may not get reactive telearmor blink chance. Will see. I want them to be minibosses and elites not full bosses). These could be used for a major plotpoint or not.. Its inpsired by Xcoms Spectre with a good bit of Xcoms Assassin, Ceph Guardian Units, and a certain other unit from RA2 MO from a certain purple clad faction. Hmm.. Wonder what Irkalla had in mind with those. Its called a Banshee. A lets call it antigrav plattform. Basically a siege and construction unit of sorts. Also inspired by Xcoms Spectre paired with the Warlock (who would have thought) and the pose is basically your standard "I am levitating :)" pose. Oh right it also has some inspiration from RA2 MO. Also very fragile. Currently named "Revenant". All of them are concipated as "Specials" or "Minibosses". Code will follow for at least the Wraith and maybe Banshee this week. Revenant may take a bit longer. That thing is called a Revenant, why not Poltergeist? Well you see there is an easy explanation for that. *Refuses to elaborate*. 1 Sprite definitely human in appearance, one looking like your generic banshee or well a Ceph depending how you look at it and 1 sprite looking non-human to meet a quota. I really need better sources for Xcoms Warlock crown though. Here a sample for the wraith, yeah all units are animated and have an uncanny stutter to their animation cycle. Some moreso than others. Especially the banshee.. : ![image](https://github.com/user-attachments/assets/86187e4f-431a-41a7-bfd0-86b6c38872e6) ## Why It's Good For The Game ~~Its not~~ I am just packed by the Enigma plot and I think it can have a bit more gravitas on the crew. Taking out the teleportation hub was a great event. Enigma learns and adapts. ## Changelog :cl: add: ..My vision has permutated.. Your.. my.. plan has taken a path unpredicted by the assault on the translocation hub.. Our directives must be reassested.. Phase four.. Phase four.. -- Adds Spritework for Enigma Phase 4 units. /:cl: --- icons/mob/enigma.dmi | Bin 25816 -> 62897 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/icons/mob/enigma.dmi b/icons/mob/enigma.dmi index 8543603825c4a793cb245375891a696273dbc096..689b6257c634f3b3b8675527c9846966f5333d64 100644 GIT binary patch literal 62897 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

;(Z_6B! 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

@;b3Vtd2IH3F(<~YZM&NnUJEU(->{Q- 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 Wk|>;O8vLme;Gvd*X1Tg;$o~L(Cl=HI From 701ebc443f3c167d9a7393cfe6f8fceea276bab0 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 4 Sep 2024 06:28:46 -0400 Subject: [PATCH 32/35] reorganizes gateway into its own module (#6718) it's stargate time --- citadel.dme | 5 +- code/modules/awaymissions/gateway.dm | 257 ------------------ code/modules/gateway/README.md | 11 + .../modules/gateway/stargate/stargate-away.dm | 107 ++++++++ .../gateway/stargate/stargate-destination.dm | 7 + .../gateway/stargate/stargate-station.dm | 127 +++++++++ code/modules/gateway/stargate/stargate.dm | 24 ++ 7 files changed, 280 insertions(+), 258 deletions(-) delete mode 100644 code/modules/awaymissions/gateway.dm create mode 100644 code/modules/gateway/README.md create mode 100644 code/modules/gateway/stargate/stargate-away.dm create mode 100644 code/modules/gateway/stargate/stargate-destination.dm create mode 100644 code/modules/gateway/stargate/stargate-station.dm create mode 100644 code/modules/gateway/stargate/stargate.dm diff --git a/citadel.dme b/citadel.dme index 2e7d1b2a717b..e8292eaa5c98 100644 --- a/citadel.dme +++ b/citadel.dme @@ -2387,7 +2387,6 @@ #include "code\modules\awaymissions\bluespaceartillery.dm" #include "code\modules\awaymissions\corpse.dm" #include "code\modules\awaymissions\exile.dm" -#include "code\modules\awaymissions\gateway.dm" #include "code\modules\awaymissions\loot.dm" #include "code\modules\awaymissions\loot_vr.dm" #include "code\modules\awaymissions\pamphlet.dm" @@ -2857,6 +2856,10 @@ #include "code\modules\games\spaceball_cards.dm" #include "code\modules\games\tarot.dm" #include "code\modules\games\unus.dm" +#include "code\modules\gateway\stargate\stargate-away.dm" +#include "code\modules\gateway\stargate\stargate-destination.dm" +#include "code\modules\gateway\stargate\stargate-station.dm" +#include "code\modules\gateway\stargate\stargate.dm" #include "code\modules\genetics\side_effects.dm" #include "code\modules\ghostroles\instantiator.dm" #include "code\modules\ghostroles\menu.dm" diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm deleted file mode 100644 index ade64d30058a..000000000000 --- a/code/modules/awaymissions/gateway.dm +++ /dev/null @@ -1,257 +0,0 @@ -/obj/machinery/gateway - name = "gateway" - desc = "A mysterious gateway built by unknown hands. It allows for faster than light travel to far-flung locations and even alternate realities." - icon = 'icons/obj/machines/gateway.dmi' - // todo: temporary, as this is unbuildable - integrity_flags = INTEGRITY_INDESTRUCTIBLE - icon_state = "off" - density = 1 - anchored = 1 - var/active = 0 - - -/obj/machinery/gateway/Initialize(mapload) - update_icon() - if(dir == SOUTH) - density = 0 - . = ..() - -/obj/machinery/gateway/update_icon() - if(active) - icon_state = "on" - return - icon_state = "off" - - - -//this is da important part wot makes things go -/obj/machinery/gateway/centerstation - density = 1 - icon_state = "offcenter" - use_power = USE_POWER_IDLE - - //warping vars - var/list/linked = list() - var/ready = 0 //have we got all the parts for a gateway? - var/wait = 0 //this just grabs world.time at world start - var/obj/machinery/gateway/centeraway/awaygate = null - -/obj/machinery/gateway/centerstation/Initialize(mapload) - update_icon() - wait = world.time + config_legacy.gateway_delay //+ thirty minutes default - awaygate = locate(/obj/machinery/gateway/centeraway) - . = ..() - density = TRUE - -/obj/machinery/gateway/centerstation/update_icon() - if(active) - icon_state = "oncenter" - return - icon_state = "offcenter" - -/obj/machinery/gateway/centerstation/process(delta_time) - if(machine_stat & (NOPOWER)) - if(active) toggleoff() - return - - if(active) - use_power(5000) - - -/obj/machinery/gateway/centerstation/proc/detect() - linked = list() //clear the list - var/turf/T = loc - - for(var/i in GLOB.alldirs) - T = get_step(loc, i) - var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T - if(G) - linked.Add(G) - continue - - //this is only done if we fail to find a part - ready = 0 - toggleoff() - break - - if(linked.len == 8) - ready = 1 - - -/obj/machinery/gateway/centerstation/proc/toggleon(mob/user as mob) - if(!ready) return - if(linked.len != 8) return - if(!powered()) return - if(!awaygate) - to_chat(user, "Error: No destination found. Please program gateway.") - return - if(world.time < wait) - to_chat(user, "Error: Warpspace triangulation in progress. Estimated time to completion: [round(((wait - world.time) / 10) / 60)] minutes.") - return - if(!awaygate.calibrated && !LAZYLEN(awaydestinations)) - to_chat(user, SPAN_NOTICE("Error: Destination gate uncalibrated. Gateway unsafe to use without far-end calibration update.")) - return - - for(var/obj/machinery/gateway/G in linked) - G.active = 1 - G.update_icon() - active = 1 - update_icon() - - -/obj/machinery/gateway/centerstation/proc/toggleoff() - for(var/obj/machinery/gateway/G in linked) - G.active = 0 - G.update_icon() - active = 0 - update_icon() - - -/obj/machinery/gateway/centerstation/attack_hand(mob/user, list/params) - if(!ready) - detect() - return - if(!active) - toggleon(user) - return - toggleoff() - - -//okay, here's the good teleporting stuff -/obj/machinery/gateway/centerstation/Bumped(atom/movable/M as mob|obj) - if(!ready) return - if(!active) return - if(!awaygate) return - - use_power(5000) - SEND_SOUND(M, sound('sound/effects/phasein.ogg')) - playsound(src, 'sound/effects/phasein.ogg', 100, 1) - if(awaygate.calibrated) - M.forceMove(get_step(awaygate.loc, SOUTH)) - M.setDir(SOUTH) - return - else - var/obj/landmark/dest = pick(awaydestinations) - if(dest) - M.forceMove(dest.loc) - M.setDir(SOUTH) - return - -/obj/machinery/gateway/centerstation/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W,/obj/item/multitool)) - if(!awaygate) - awaygate = locate(/obj/machinery/gateway/centeraway) - if(!awaygate) // We still can't find the damn thing because there is no destination. - to_chat(user, "Error: Programming failed. No destination found.") - return - to_chat(user, "Startup programming successful!: A destination in another point of space and time has been detected.") - else - to_chat(user, "The gate is already calibrated, there is no work for you to do here.") - return - -/////////////////////////////////////Away//////////////////////// - - -/obj/machinery/gateway/centeraway - density = 1 - icon_state = "offcenter" - use_power = USE_POWER_OFF - var/calibrated = 1 - var/list/linked = list() //a list of the connected gateway chunks - var/ready = 0 - var/obj/machinery/gateway/centeraway/stationgate = null - - -/obj/machinery/gateway/centeraway/Initialize(mapload) - update_icon() - stationgate = locate(/obj/machinery/gateway/centerstation) - . = ..() - density = 1 - - -/obj/machinery/gateway/centeraway/update_icon() - if(active) - icon_state = "oncenter" - return - icon_state = "offcenter" - -/obj/machinery/gateway/centeraway/proc/detect() - linked = list() //clear the list - var/turf/T = loc - - for(var/i in GLOB.alldirs) - T = get_step(loc, i) - var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T - if(G) - linked.Add(G) - continue - - //this is only done if we fail to find a part - ready = 0 - toggleoff() - break - - if(linked.len == 8) - ready = 1 - - -/obj/machinery/gateway/centeraway/proc/toggleon(mob/user as mob) - if(!ready) return - if(linked.len != 8) return - if(!stationgate || !calibrated) - to_chat(user, SPAN_NOTICE("Error: No destination found. Please calibrate gateway.")) - return - - for(var/obj/machinery/gateway/G in linked) - G.active = 1 - G.update_icon() - active = 1 - update_icon() - - -/obj/machinery/gateway/centeraway/proc/toggleoff() - for(var/obj/machinery/gateway/G in linked) - G.active = 0 - G.update_icon() - active = 0 - update_icon() - - -/obj/machinery/gateway/centeraway/attack_hand(mob/user, list/params) - if(!ready) - detect() - return - if(!active) - toggleon(user) - return - toggleoff() - - -/obj/machinery/gateway/centeraway/Bumped(atom/movable/M as mob|obj) - if(!ready) return - if(!active) return - if(istype(M, /mob/living/carbon)) - for(var/obj/item/implant/exile/E in M)//Checking that there is an exile implant in the contents - if(E.imp_in == M)//Checking that it's actually implanted vs just in their pocket - to_chat(M, "The station gate has detected your exile implant and is blocking your entry.") - return - M.forceMove(get_step(stationgate.loc, SOUTH)) - M.setDir(SOUTH) - SEND_SOUND(M, sound('sound/effects/phasein.ogg')) - playsound(src, 'sound/effects/phasein.ogg', 100, 1) - - -/obj/machinery/gateway/centeraway/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W,/obj/item/multitool)) - if(calibrated && stationgate) - to_chat(user, "The gate is already calibrated, there is no work for you to do here.") - return - else - stationgate = locate(/obj/machinery/gateway/centerstation) - if(!stationgate) - to_chat(user, "Error: Recalibration failed. No destination found... That can't be good.") - return - else - to_chat(user, "Recalibration successful!: This gate's systems have been fine tuned. Travel to this gate will now be on target.") - calibrated = 1 - return diff --git a/code/modules/gateway/README.md b/code/modules/gateway/README.md new file mode 100644 index 000000000000..9acc351a8253 --- /dev/null +++ b/code/modules/gateway/README.md @@ -0,0 +1,11 @@ +# Gateway Module + +WIP module for a series of disjunct mechanics for shunting people across spaces. + +- These are unrelated to telescience module, and therefore will not be balanced by it nor will it be constructible. +- These are mostly for map design, admin spawn, and events. + +## Contains + +- Stargates: Classic SS13 gateways with rudimentary accuracy/precision and destination settings +- Quantum Archways: See-through & Walk-through 'line' gateways diff --git a/code/modules/gateway/stargate/stargate-away.dm b/code/modules/gateway/stargate/stargate-away.dm new file mode 100644 index 000000000000..424ad7fb9294 --- /dev/null +++ b/code/modules/gateway/stargate/stargate-away.dm @@ -0,0 +1,107 @@ +/** + * Preset for away destinations for the stargate system + */ + +/obj/machinery/gateway/centeraway + density = 1 + icon_state = "offcenter" + use_power = USE_POWER_OFF + var/calibrated = 1 + var/list/linked = list() //a list of the connected gateway chunks + var/ready = 0 + var/obj/machinery/gateway/centeraway/stationgate = null + + +/obj/machinery/gateway/centeraway/Initialize(mapload) + update_icon() + stationgate = locate(/obj/machinery/gateway/centerstation) + . = ..() + density = 1 + + +/obj/machinery/gateway/centeraway/update_icon() + if(active) + icon_state = "oncenter" + return + icon_state = "offcenter" + +/obj/machinery/gateway/centeraway/proc/detect() + linked = list() //clear the list + var/turf/T = loc + + for(var/i in GLOB.alldirs) + T = get_step(loc, i) + var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T + if(G) + linked.Add(G) + continue + + //this is only done if we fail to find a part + ready = 0 + toggleoff() + break + + if(linked.len == 8) + ready = 1 + + +/obj/machinery/gateway/centeraway/proc/toggleon(mob/user as mob) + if(!ready) return + if(linked.len != 8) return + if(!stationgate || !calibrated) + to_chat(user, SPAN_NOTICE("Error: No destination found. Please calibrate gateway.")) + return + + for(var/obj/machinery/gateway/G in linked) + G.active = 1 + G.update_icon() + active = 1 + update_icon() + + +/obj/machinery/gateway/centeraway/proc/toggleoff() + for(var/obj/machinery/gateway/G in linked) + G.active = 0 + G.update_icon() + active = 0 + update_icon() + + +/obj/machinery/gateway/centeraway/attack_hand(mob/user, list/params) + if(!ready) + detect() + return + if(!active) + toggleon(user) + return + toggleoff() + + +/obj/machinery/gateway/centeraway/Bumped(atom/movable/M as mob|obj) + if(!ready) return + if(!active) return + if(istype(M, /mob/living/carbon)) + for(var/obj/item/implant/exile/E in M)//Checking that there is an exile implant in the contents + if(E.imp_in == M)//Checking that it's actually implanted vs just in their pocket + to_chat(M, "The station gate has detected your exile implant and is blocking your entry.") + return + M.forceMove(get_step(stationgate.loc, SOUTH)) + M.setDir(SOUTH) + SEND_SOUND(M, sound('sound/effects/phasein.ogg')) + playsound(src, 'sound/effects/phasein.ogg', 100, 1) + + +/obj/machinery/gateway/centeraway/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W,/obj/item/multitool)) + if(calibrated && stationgate) + to_chat(user, "The gate is already calibrated, there is no work for you to do here.") + return + else + stationgate = locate(/obj/machinery/gateway/centerstation) + if(!stationgate) + to_chat(user, "Error: Recalibration failed. No destination found... That can't be good.") + return + else + to_chat(user, "Recalibration successful!: This gate's systems have been fine tuned. Travel to this gate will now be on target.") + calibrated = 1 + return diff --git a/code/modules/gateway/stargate/stargate-destination.dm b/code/modules/gateway/stargate/stargate-destination.dm new file mode 100644 index 000000000000..be34508c68e2 --- /dev/null +++ b/code/modules/gateway/stargate/stargate-destination.dm @@ -0,0 +1,7 @@ +/** + * Stargate destination override; allows you to kick people to those destinations instead of an away gate. + */ +/obj/landmark/stargate_destination_marker + delete_on_roundstart = FALSE + +// todo: impl diff --git a/code/modules/gateway/stargate/stargate-station.dm b/code/modules/gateway/stargate/stargate-station.dm new file mode 100644 index 000000000000..9708760a87b8 --- /dev/null +++ b/code/modules/gateway/stargate/stargate-station.dm @@ -0,0 +1,127 @@ +/** + * Preset for station-side stargate + */ +//this is da important part wot makes things go +/obj/machinery/gateway/centerstation + density = 1 + icon_state = "offcenter" + use_power = USE_POWER_IDLE + + //warping vars + var/list/linked = list() + var/ready = 0 //have we got all the parts for a gateway? + var/wait = 0 //this just grabs world.time at world start + var/obj/machinery/gateway/centeraway/awaygate = null + +/obj/machinery/gateway/centerstation/Initialize(mapload) + update_icon() + wait = world.time + config_legacy.gateway_delay //+ thirty minutes default + awaygate = locate(/obj/machinery/gateway/centeraway) + . = ..() + density = TRUE + +/obj/machinery/gateway/centerstation/update_icon() + if(active) + icon_state = "oncenter" + return + icon_state = "offcenter" + +/obj/machinery/gateway/centerstation/process(delta_time) + if(machine_stat & (NOPOWER)) + if(active) toggleoff() + return + + if(active) + use_power(5000) + + +/obj/machinery/gateway/centerstation/proc/detect() + linked = list() //clear the list + var/turf/T = loc + + for(var/i in GLOB.alldirs) + T = get_step(loc, i) + var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T + if(G) + linked.Add(G) + continue + + //this is only done if we fail to find a part + ready = 0 + toggleoff() + break + + if(linked.len == 8) + ready = 1 + + +/obj/machinery/gateway/centerstation/proc/toggleon(mob/user as mob) + if(!ready) return + if(linked.len != 8) return + if(!powered()) return + if(!awaygate) + to_chat(user, "Error: No destination found. Please program gateway.") + return + if(world.time < wait) + to_chat(user, "Error: Warpspace triangulation in progress. Estimated time to completion: [round(((wait - world.time) / 10) / 60)] minutes.") + return + if(!awaygate.calibrated && !LAZYLEN(awaydestinations)) + to_chat(user, SPAN_NOTICE("Error: Destination gate uncalibrated. Gateway unsafe to use without far-end calibration update.")) + return + + for(var/obj/machinery/gateway/G in linked) + G.active = 1 + G.update_icon() + active = 1 + update_icon() + + +/obj/machinery/gateway/centerstation/proc/toggleoff() + for(var/obj/machinery/gateway/G in linked) + G.active = 0 + G.update_icon() + active = 0 + update_icon() + + +/obj/machinery/gateway/centerstation/attack_hand(mob/user, list/params) + if(!ready) + detect() + return + if(!active) + toggleon(user) + return + toggleoff() + + +//okay, here's the good teleporting stuff +/obj/machinery/gateway/centerstation/Bumped(atom/movable/M as mob|obj) + if(!ready) return + if(!active) return + if(!awaygate) return + + use_power(5000) + SEND_SOUND(M, sound('sound/effects/phasein.ogg')) + playsound(src, 'sound/effects/phasein.ogg', 100, 1) + if(awaygate.calibrated) + M.forceMove(get_step(awaygate.loc, SOUTH)) + M.setDir(SOUTH) + return + else + var/obj/landmark/dest = pick(awaydestinations) + if(dest) + M.forceMove(dest.loc) + M.setDir(SOUTH) + return + +/obj/machinery/gateway/centerstation/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W,/obj/item/multitool)) + if(!awaygate) + awaygate = locate(/obj/machinery/gateway/centeraway) + if(!awaygate) // We still can't find the damn thing because there is no destination. + to_chat(user, "Error: Programming failed. No destination found.") + return + to_chat(user, "Startup programming successful!: A destination in another point of space and time has been detected.") + else + to_chat(user, "The gate is already calibrated, there is no work for you to do here.") + return diff --git a/code/modules/gateway/stargate/stargate.dm b/code/modules/gateway/stargate/stargate.dm new file mode 100644 index 000000000000..41224803ae60 --- /dev/null +++ b/code/modules/gateway/stargate/stargate.dm @@ -0,0 +1,24 @@ +// todo: /obj/machinery/stargate +/obj/machinery/gateway + name = "gateway" + desc = "A mysterious gateway built by unknown hands. It allows for faster than light travel to far-flung locations and even alternate realities." + icon = 'icons/obj/machines/gateway.dmi' + // todo: temporary, as this is unbuildable + integrity_flags = INTEGRITY_INDESTRUCTIBLE + icon_state = "off" + density = 1 + anchored = 1 + var/active = 0 + + +/obj/machinery/gateway/Initialize(mapload) + update_icon() + if(dir == SOUTH) + density = 0 + . = ..() + +/obj/machinery/gateway/update_icon() + if(active) + icon_state = "on" + return + icon_state = "off" From 6dbb18ede617f0e4fd4f1b2269d93740a74451c0 Mon Sep 17 00:00:00 2001 From: GySgtMurphy <79298976+GySgtMurphy@users.noreply.github.com> Date: Fri, 6 Sep 2024 11:54:15 -0400 Subject: [PATCH 33/35] Adds Gauss Rifle and AC10 to the Exosuit fab (#6716) ## About The Pull Request Adds more ballistic options to the Exosuit fabricator, and a new projectile that should be pretty powerful, but not too OP like the AMR round. ## Why It's Good For The Game Gives more options to the ballistic side of things for weapons on exosuits. Not everything has to be a laser. ## Changelog :cl: add: Added Gauss Rifle, AC10 and bullet for the Gauss to the code /:cl: --- .../projectile/subtypes/bullets.dm | 6 +++++ .../research/designs/mechfab_designs.dm | 10 ++++++++ .../equipment/weapons/ballistic/automatic.dm | 24 +++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/code/modules/projectiles/projectile/subtypes/bullets.dm b/code/modules/projectiles/projectile/subtypes/bullets.dm index 5f5b67065805..11458f9cd56a 100644 --- a/code/modules/projectiles/projectile/subtypes/bullets.dm +++ b/code/modules/projectiles/projectile/subtypes/bullets.dm @@ -308,6 +308,12 @@ armor_penetration = 80 hitscan = 1 //so the PTR isn't useless as a sniper weapon +/obj/projectile/bullet/mecha/a12mm_gauss //Mecha gauss rifle round. + fire_sound = 'sound/weapons/Gunshot_cannon.ogg' // This is literally an anti-tank rifle caliber. It better sound like a fucking cannon. + damage = 60 + legacy_penetrating = 1 + armor_penetration = 60 + /* Miscellaneous */ /obj/projectile/bullet/suffocationbullet//How does this even work? diff --git a/code/modules/research/designs/mechfab_designs.dm b/code/modules/research/designs/mechfab_designs.dm index 6ab674d5452f..7ae1195b9106 100644 --- a/code/modules/research/designs/mechfab_designs.dm +++ b/code/modules/research/designs/mechfab_designs.dm @@ -514,6 +514,16 @@ id = "mech_lmg" build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg +/datum/design/science/mecha/lmg_heavy + design_name = "AC 10" + id = "mech_lmg_heavy" + build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg_heavy + +/datum/design/science/mecha/gauss_rifle + design_name = "Gauss Rifle" + id = "mech_gauss_rifle" + build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/gauss_rifle + /datum/design/science/mecha/rigged_lmg design_name = "Jury-Rigged Machinegun" id = "mech_lmg-r" diff --git a/code/modules/vehicles/sealed/mecha/equipment/weapons/ballistic/automatic.dm b/code/modules/vehicles/sealed/mecha/equipment/weapons/ballistic/automatic.dm index 7bc1b1c89d12..6686d75d011f 100644 --- a/code/modules/vehicles/sealed/mecha/equipment/weapons/ballistic/automatic.dm +++ b/code/modules/vehicles/sealed/mecha/equipment/weapons/ballistic/automatic.dm @@ -11,6 +11,30 @@ projectile_energy_cost = 20 fire_cooldown = 2 +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg_heavy + name = "\improper AC 10" + desc = "The original in anti-mech firepower, the standard Hephaestus Autocannon MK10 design fires AP slugs in order to damage other heavy armor suits. This does mean its rate between bursts is longer than most." + icon_state = "mecha_uac2" + equip_cooldown = 1 SECONDS + projectile = /obj/projectile/bullet/rifle/a762/ap + fire_sound = 'sound/weapons/Gunshot_deagle.ogg' + projectiles = 20 //Mag size + projectiles_per_shot = 2 + deviation = 0.3 + projectile_energy_cost = 60 + fire_cooldown = 2 + +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/gauss_rifle + name = "\improper gauss rifle" + desc = "The current standard in non-laser, anti-armor firepower, this weapon is the same as those mounted on light tanks for their primary weapon. Fires a single nickle-iron slug at high speed. Requires a long charge time between shots. " + icon_state = "mecha_uac2-rig" + equip_cooldown = 3 SECONDS + projectile = /obj/projectile/bullet/mecha/a12mm_gauss + fire_sound = 'sound/weapons/Gunshot_cannon.ogg' + projectiles = 10 //Mag size + projectiles_per_shot = 1 + projectile_energy_cost = 100 + /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg/rigged name = "jury-rigged machinegun" desc = "The cross between a jackhammer and a whole lot of zipguns." From 6b8a74e682779b17c738ed33010966f9a72005ea Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Fri, 6 Sep 2024 15:23:38 -0400 Subject: [PATCH 34/35] mob ai: iff factions, small turbolift/maploader fix (#6702) replaces old faction system with one that supports lists --- citadel.dme | 5 +- code/__DEFINES/mobs/iff.dm | 109 +++++++++ code/game/machinery/computer/arcade/orion.dm | 1 - code/game/machinery/turrets/turret.dm | 2 +- code/game/objects/items/weapons/nullrod.dm | 2 +- code/game/objects/items/weapons/other.dm | 4 +- code/game/objects/mob_spawner.dm | 6 +- code/game/objects/random/mob.dm | 2 +- code/game/objects/structures/ashlander.dm | 8 +- code/game/objects/structures/props/nest.dm | 2 +- code/game/objects/structures/props/swarm.dm | 8 +- code/game/turfs/change_turf.dm | 7 + .../holders/polaris/ai_holder_cooperation.dm | 3 +- .../ai/holders/polaris/ai_holder_targeting.dm | 2 +- code/modules/ai/holders/polaris/interfaces.dm | 2 +- code/modules/blob2/blobs/base_blob.dm | 14 +- code/modules/blob2/blobs/factory.dm | 2 +- code/modules/blob2/overmind/overmind.dm | 3 +- code/modules/blob2/overmind/powers.dm | 2 +- code/modules/blob2/overmind/types.dm | 8 +- code/modules/ghostroles/roles/ashlander.dm | 2 +- code/modules/ghostroles/roles/pirate.dm | 2 +- code/modules/holodeck/HolodeckObjects.dm | 4 +- code/modules/mapping/map.dm | 4 +- code/modules/mapping/map_level.dm | 82 ++++--- code/modules/maps/_shim/away_spawner.dm | 2 +- .../maps/away_missions/140x140/snowfield.dm | 6 +- .../mob/living/carbon/alien/larva/larva.dm | 2 +- .../mob/living/carbon/alien/progression.dm | 3 +- .../human/ai_controlled/ai_controlled.dm | 2 - .../mob/living/living-defense-legacy.dm | 2 +- code/modules/mob/living/living_defines.dm | 4 +- code/modules/mob/living/movement.dm | 2 +- .../mob/living/silicon/robot/drone/swarm.dm | 6 +- .../mob/living/simple_mob/simple_mob.dm | 2 + .../subtypes/animal/farm animals/goat.dm | 4 +- .../subtypes/animal/farm animals/lythios.dm | 7 +- .../animal/giant_spider/_giant_spider.dm | 3 +- .../subtypes/animal/giant_spider/carrier.dm | 2 +- .../subtypes/animal/passive/crab.dm | 4 +- .../subtypes/animal/passive/passive.dm | 4 + .../simple_mob/subtypes/animal/pets/fox_vr.dm | 2 +- .../simple_mob/subtypes/animal/roach/roach.dm | 5 +- .../simple_mob/subtypes/animal/sif/diyaab.dm | 2 +- .../simple_mob/subtypes/animal/sif/duck.dm | 2 +- .../subtypes/animal/sif/frostfly.dm | 2 +- .../subtypes/animal/sif/glitterfly.dm | 2 +- .../subtypes/animal/sif/hooligan_crab.dm | 2 +- .../simple_mob/subtypes/animal/sif/kururak.dm | 4 +- .../simple_mob/subtypes/animal/sif/leech.dm | 2 +- .../simple_mob/subtypes/animal/sif/racoon.dm | 4 +- .../simple_mob/subtypes/animal/sif/savik.dm | 2 +- .../simple_mob/subtypes/animal/sif/shantak.dm | 6 +- .../simple_mob/subtypes/animal/space/alien.dm | 12 +- .../simple_mob/subtypes/animal/space/bats.dm | 4 +- .../simple_mob/subtypes/animal/space/bear.dm | 2 +- .../simple_mob/subtypes/animal/space/carp.dm | 3 +- .../subtypes/animal/space/gaslamp_vr.dm | 3 +- .../simple_mob/subtypes/animal/space/goose.dm | 5 +- .../subtypes/animal/space/horing.dm | 2 +- .../subtypes/animal/space/mouse_army.dm | 3 +- .../simple_mob/subtypes/animal/space/worm.dm | 6 +- .../living/simple_mob/subtypes/blob/blob.dm | 2 +- .../living/simple_mob/subtypes/horror/Eddy.dm | 1 - .../simple_mob/subtypes/horror/Master.dm | 1 - .../simple_mob/subtypes/horror/Rickey.dm | 1 - .../simple_mob/subtypes/horror/Smiley.dm | 1 - .../simple_mob/subtypes/horror/Steve.dm | 1 - .../simple_mob/subtypes/horror/Willy.dm | 1 - .../simple_mob/subtypes/horror/bradley.dm | 1 - .../simple_mob/subtypes/horror/horror .dm | 2 +- .../simple_mob/subtypes/horror/sally.dm | 1 - .../simple_mob/subtypes/horror/shittytim.dm | 1 - .../simple_mob/subtypes/horror/timling.dm | 1 - .../simple_mob/subtypes/humanoid/clown.dm | 4 +- .../simple_mob/subtypes/humanoid/cultist.dm | 17 +- .../subtypes/humanoid/humanoid_vr.dm | 6 - .../subtypes/humanoid/mercs/mercs.dm | 6 +- .../simple_mob/subtypes/humanoid/pirates.dm | 2 +- .../simple_mob/subtypes/humanoid/possessed.dm | 2 +- .../simple_mob/subtypes/humanoid/russian.dm | 48 ---- .../simple_mob/subtypes/lavaland/goliath.dm | 3 +- .../simple_mob/subtypes/lavaland/gutshank.dm | 11 +- .../subtypes/lavaland/stormdrifter.dm | 8 +- .../subtypes/mechanical/combat_drone.dm | 2 +- .../mechanical/corrupt_maint_drone_vr.dm | 2 +- .../mechanical/cyber_horror/cyber_horror.dm | 4 +- .../subtypes/mechanical/disbot_vr.dm | 4 +- .../simple_mob/subtypes/mechanical/golem.dm | 2 +- .../mechanical/hivebot/enigma_hivebot.dm | 13 +- .../subtypes/mechanical/hivebot/hivebot.dm | 2 +- .../subtypes/mechanical/hivebot/support.dm | 2 +- .../subtypes/mechanical/mecha/mecha.dm | 5 +- .../subtypes/mechanical/mecha/micro.dm | 3 +- .../subtypes/mechanical/mecha/ripley.dm | 2 +- .../subtypes/mechanical/mechanical.dm | 2 +- .../subtypes/mechanical/viscerator.dm | 2 +- .../subtypes/mechanical/ward/monitor_ward.dm | 2 +- .../subtypes/mechanical/ward/ward.dm | 3 +- .../subtypes/occult/constructs/_construct.dm | 2 +- .../simple_mob/subtypes/occult/creature.dm | 4 +- .../simple_mob/subtypes/occult/faithless.dm | 4 +- .../subtypes/occult/living_statue.dm | 2 +- .../simple_mob/subtypes/plant/tomato.dm | 2 +- .../living/simple_mob/subtypes/plant/tree.dm | 6 +- .../living/simple_mob/subtypes/slime/slime.dm | 2 +- .../subtypes/slime/xenobio/xenobio.dm | 5 +- .../living/simple_mob/subtypes/vore/bee.dm | 2 +- .../subtypes/vore/corrupt_hounds.dm | 2 +- .../simple_mob/subtypes/vore/deathclaw.dm | 2 +- .../simple_mob/subtypes/vore/demon/demon.dm | 11 +- .../living/simple_mob/subtypes/vore/dino.dm | 2 +- .../living/simple_mob/subtypes/vore/dragon.dm | 4 +- .../living/simple_mob/subtypes/vore/fennec.dm | 3 +- .../living/simple_mob/subtypes/vore/fennix.dm | 3 +- .../living/simple_mob/subtypes/vore/horse.dm | 3 +- .../living/simple_mob/subtypes/vore/jelly.dm | 1 - .../living/simple_mob/subtypes/vore/mimic.dm | 8 +- .../living/simple_mob/subtypes/vore/otie.dm | 14 +- .../simple_mob/subtypes/vore/panther.dm | 3 +- .../living/simple_mob/subtypes/vore/rat.dm | 2 +- .../simple_mob/subtypes/vore/redpanda.dm | 3 +- .../living/simple_mob/subtypes/vore/snake.dm | 3 +- .../simple_mob/subtypes/vore/solargrub.dm | 3 +- .../subtypes/vore/solargrub_larva.dm | 2 +- .../vore/{solarmoth_ch.dm => solarmoth.dm} | 3 +- .../living/simple_mob/subtypes/vore/wolf.dm | 2 +- .../simple_mob/subtypes/vore/wolfgirl.dm | 1 - .../subtypes/vore/zz_vore_overrides.dm | 3 - code/modules/mob/mob-iff.dm | 117 ++++++++++ code/modules/mob/mob.dm | 2 + code/modules/mob/mob_defines.dm | 9 +- code/modules/mob/mob_helpers.dm | 2 +- code/modules/power/fission/engine.dm | 5 +- code/modules/rogueminer_vr/roguemines_mobs.dm | 16 +- code/modules/rogueminer_vr/zonemaster.dm | 2 +- code/modules/species/protean/protean_blob.dm | 3 +- code/modules/species/species.dm | 13 ++ .../species/xenomorphs/alien_species.dm | 4 + code/modules/species/xenomorphs/xenomorphs.dm | 8 +- code/modules/tension/tension.dm | 2 +- code/modules/turbolift/turbolift_map.dm | 2 +- code/modules/xenobio/items/slimepotions.dm | 5 +- maps/away_missions/140x140/listeningpost.dmm | 3 +- maps/away_missions/140x140/zoo.dmm | 206 +++++++++--------- maps/sectors/frozen_140/levels/frozen_140.dmm | 2 +- maps/sectors/frozen_192/levels/frozen_192.dmm | 2 +- .../piratebase_140/levels/piratebase_140.dmm | 2 +- .../piratebase_192/levels/piratebase_192.dmm | 2 +- .../level_specific/class_h/covert_post.dmm | 2 +- .../level_specific/debrisfield/derelict.dmm | 10 +- .../debrisfield_vr/derelict.dmm | 10 +- .../debrisfield_vr/mining_drones.dmm | 2 +- .../underdark/abandonded_outpost.dmm | 2 +- .../level_specific/underdark/wolf_den.dmm | 2 +- .../level_specific/virgo2/Rockybase.dmm | 8 +- maps/submaps/plains/Oldhouse.dmm | 2 +- maps/submaps/plains/Oldhouse_vr.dmm | 2 +- maps/submaps/wilderness/Rockybase.dmm | 8 +- maps/tether/levels/plains.dmm | 2 +- 160 files changed, 685 insertions(+), 466 deletions(-) create mode 100644 code/__DEFINES/mobs/iff.dm delete mode 100644 code/modules/mob/living/simple_mob/subtypes/humanoid/russian.dm rename code/modules/mob/living/simple_mob/subtypes/vore/{solarmoth_ch.dm => solarmoth.dm} (99%) create mode 100644 code/modules/mob/mob-iff.dm diff --git a/citadel.dme b/citadel.dme index e8292eaa5c98..37a4d4c956ec 100644 --- a/citadel.dme +++ b/citadel.dme @@ -263,6 +263,7 @@ #include "code\__DEFINES\mobs\biology.dm" #include "code\__DEFINES\mobs\characteristics.dm" #include "code\__DEFINES\mobs\grab.dm" +#include "code\__DEFINES\mobs\iff.dm" #include "code\__DEFINES\mobs\intent.dm" #include "code\__DEFINES\mobs\life.dm" #include "code\__DEFINES\mobs\mobility.dm" @@ -3496,6 +3497,7 @@ #include "code\modules\mob\mob-keybind-triggers.dm" #include "code\modules\mob\mob.dm" #include "code\modules\mob\mob_defines.dm" +#include "code\modules\mob\mob-iff.dm" #include "code\modules\mob\mob_helpers.dm" #include "code\modules\mob\mob_transformation_simple.dm" #include "code\modules\mob\mobility.dm" @@ -3903,7 +3905,6 @@ #include "code\modules\mob\living\simple_mob\subtypes\humanoid\humanoid_vr.dm" #include "code\modules\mob\living\simple_mob\subtypes\humanoid\pirates.dm" #include "code\modules\mob\living\simple_mob\subtypes\humanoid\possessed.dm" -#include "code\modules\mob\living\simple_mob\subtypes\humanoid\russian.dm" #include "code\modules\mob\living\simple_mob\subtypes\humanoid\mercs\mercs.dm" #include "code\modules\mob\living\simple_mob\subtypes\humanoid\mercs\mercs_vr.dm" #include "code\modules\mob\living\simple_mob\subtypes\illusion\illusion.dm" @@ -3983,7 +3984,7 @@ #include "code\modules\mob\living\simple_mob\subtypes\vore\snake.dm" #include "code\modules\mob\living\simple_mob\subtypes\vore\solargrub.dm" #include "code\modules\mob\living\simple_mob\subtypes\vore\solargrub_larva.dm" -#include "code\modules\mob\living\simple_mob\subtypes\vore\solarmoth_ch.dm" +#include "code\modules\mob\living\simple_mob\subtypes\vore\solarmoth.dm" #include "code\modules\mob\living\simple_mob\subtypes\vore\vore.dm" #include "code\modules\mob\living\simple_mob\subtypes\vore\wolf.dm" #include "code\modules\mob\living\simple_mob\subtypes\vore\wolfgirl.dm" diff --git a/code/__DEFINES/mobs/iff.dm b/code/__DEFINES/mobs/iff.dm new file mode 100644 index 000000000000..4edc9d365aa2 --- /dev/null +++ b/code/__DEFINES/mobs/iff.dm @@ -0,0 +1,109 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station developers. *// + +//* IFF Factions *// + +//? -- Special; These must start with '!' -- ?// +//? ?// +//? These are only valid syntax in places ?// +//? where you should be using them, aka ?// +//? initializers. ?// + +/// get an arbitrary faction that's the same on a given /datum/map_level +#define MOB_IFF_FACTION_BIND_TO_LEVEL "!bind-level" +/// get an arbitrary faction that's the same on a given /datum/map +/// +/// * acts like BIND_TO_LEVEL if there's no parent /datum/map for a /datum/map_level +#define MOB_IFF_FACTION_BIND_TO_MAP "!bind-map" +/// get an arbitrary faction that's the same on a given /datum/map_level for a given key +/// +/// * GROUP must be a string +#define MOB_IFF_FACTION_BIND_TO_LEVEL_GROUP(GROUP) list(MOB_IFF_FACTION_BIND_TO_LEVEL = GROUP) +/// get an arbitrary faction that's the same on a given /datum/map for a given key +/// +/// * GROUP must be a string +/// * acts like BIND_TO_LEVEL if there's no parent /datum/map for a /datum/map_level +#define MOB_IFF_FACTION_BIND_TO_MAP_GROUP list(MOB_IFF_FACTION_BIND_TO_MAP = GROUP) + +// todo: "bind to map template" (?) +// todo: "bind to /area" (?) +// todo: "bind to specific type" handling; maybe just input typepath? + +/// automatically detect what we should bind to +/// +/// * submap +/// * map +/// todo: impl +#define MOB_IFF_FACTION_BIND_AUTO MOB_IFF_FACTION_BIND_TO_MAP +/// automatically detect what we should bind to, and use a specific separated group +/// +/// * submap +/// * map +/// todo: impl +#define MOB_IFF_FACTION_BIND_AUTO_GROUP(GROUP) list(MOB_IFF_FACTION_BIND_TO_MAP = GROUP) + +//? Default factions *// + +/// mobs have this by default +/// +/// * this makes a lot of things assume that the mob is nonhostile. +/// * this should be removed for hostile mobs +#define MOB_IFF_FACTION_NEUTRAL "neutral" +/// generic hostile mob faction +/// +/// * do not check for this; most hostile mobs do not have this +/// * having FACTION_NEUTRAL is an effect; having this is not, this is just a generic one so the mob has a faction. +/// * you probably shouldn't even be using this unless you're doing BIND_TO_LEVEL/MAP_GROUP with this. +#define MOB_IFF_FACTION_HOSTILE "hostile" + +//? AI / machine intelligence factions ?// + +#define MOB_IFF_FACTION_HIVEBOT "ai-hivebot" +#define MOB_IFF_FACTION_SWARMER "ai-swarmer" + +//? Alien factions + +#define MOB_IFF_FACTION_BLOB "alien-blob" +#define MOB_IFF_FACTION_CHIMERIC "alien-chimeric" +#define MOB_IFF_FACTION_SLIME "alien-slime" +#define MOB_IFF_FACTION_STATUE "alien-statue" +#define MOB_IFF_FACTION_XENOMORPH "alien-xenomorph" + +//? Animal factions ?// +//* Farm refers to 'this would not be out of place in a normal earth farm that isn't in a horror series' + +/// goats, cows, sheep +#define MOB_IFF_FACTION_FARM_ANIMAL "farm-animal" +/// ducks, other 'non producing' (canonically, anyways) +#define MOB_IFF_FACTION_FARM_NEUTRAL "farm-neutral" +/// mice and similar +#define MOB_IFF_FACTION_FARM_PEST "farm-pest" +/// cats, dogs +#define MOB_IFF_FACTION_FARM_PET "pet" + +/// man's worst enemy (spiders) +#define MOB_IFF_FACTION_SPIDER "spider" +/// fallout gone wrong - wait what?! (cockroaches) +#define MOB_IFF_FACTION_ROACH "roach" +/// biotech gone wrong (genetic horrors) +#define MOB_IFF_FACTION_MUTANT "mutant" +/// hydroponics gone wrong (literally any hostile plane) +#define MOB_IFF_FACTION_PLANT "plant" +/// is this a dune reference??? (space worms) +#define MOB_IFF_FACTION_WORM "worm" +/// we're going whaling! (space carps) +#define MOB_IFF_FACTION_CARP "carp" +/// the bane of engineering (solargrubs) +#define MOB_IFF_FACTION_GRUB "grubs" + +//? Human factions ?// + +#define MOB_IFF_FACTION_MERCENARY "mercenary" +#define MOB_IFF_FACTION_MERCENARY_GROUP(GROUP) ("mercenary-" + GROUP) +#define MOB_IFF_FACTION_PIRATE "mercenary" +#define MOB_IFF_FACTION_PIRATE_GROUP(GROUP) ("mercenary-" + GROUP) + +//? Paracausal factions ?// + +#define MOB_IFF_FACTION_CLOCKWORK_CULT "clock-cult" +#define MOB_IFF_FACTION_SANGUINE_CULT "blood-cult" diff --git a/code/game/machinery/computer/arcade/orion.dm b/code/game/machinery/computer/arcade/orion.dm index c1c365ded1b6..77d2a6c5f98a 100644 --- a/code/game/machinery/computer/arcade/orion.dm +++ b/code/game/machinery/computer/arcade/orion.dm @@ -487,7 +487,6 @@ GLOBAL_LIST_INIT(orion_events, generate_orion_events()) /mob/living/simple_mob/hostile/humanoid/orion name = "spaceport security" desc = "Premier corporate security forces for all spaceports found along the Orion Trail." - faction = "orion" loot_list = list() //del_on_death = TRUE diff --git a/code/game/machinery/turrets/turret.dm b/code/game/machinery/turrets/turret.dm index 02a1860c9e10..c469d5d39eb6 100644 --- a/code/game/machinery/turrets/turret.dm +++ b/code/game/machinery/turrets/turret.dm @@ -543,7 +543,7 @@ if(L.invisibility >= INVISIBILITY_LEVEL_ONE) // Cannot see him. see_invisible is a mob-var return TURRET_NOT_TARGET - if(faction && L.faction == faction) + if(faction && L.has_iff_faction(faction)) return TURRET_NOT_TARGET if(!emagged && issilicon(L) && check_all == FALSE) // Don't target silica, unless told to neutralize everything. diff --git a/code/game/objects/items/weapons/nullrod.dm b/code/game/objects/items/weapons/nullrod.dm index b70acf30e7b3..ce93b1ee66b2 100644 --- a/code/game/objects/items/weapons/nullrod.dm +++ b/code/game/objects/items/weapons/nullrod.dm @@ -359,7 +359,7 @@ if(used_blessing) else if(user.mind && (user.mind.isholy)) to_chat(user, "You are blessed by Carp-Sie. Wild space carp will no longer attack you.") - user.faction |= "carp" + user.add_iff_faction(MOB_IFF_FACTION_CARP) used_blessing = TRUE /obj/item/nullrod/claymore/bostaff //May as well make it a "claymore" and inherit the blocking diff --git a/code/game/objects/items/weapons/other.dm b/code/game/objects/items/weapons/other.dm index 9b5c907b0eb2..9cd042fb58f6 100644 --- a/code/game/objects/items/weapons/other.dm +++ b/code/game/objects/items/weapons/other.dm @@ -427,11 +427,11 @@ . = CLICKCHAIN_DO_NOT_PROPAGATE if(!target.mind) return - if(target.faction == user.faction) + if(target.shares_iff_faction(user)) to_chat(target, "You are graced by the familiar gaze of the Mother for a brief moment.") to_chat(user, "You smear the Mark of the Mother on [target]'s forehead using the [src].") to_chat(target, "You sense an unfamiliar presence looming over you. It encases you in a gentle, all-encompassing warmth.") - target.faction = user.faction + target.copy_iff_factions(user) playsound(src, pick(use_sound), 25) qdel(src) diff --git a/code/game/objects/mob_spawner.dm b/code/game/objects/mob_spawner.dm index 17005e489806..a7750f0b37de 100644 --- a/code/game/objects/mob_spawner.dm +++ b/code/game/objects/mob_spawner.dm @@ -64,7 +64,7 @@ if(total_spawns > 0) total_spawns-- if(mob_faction) - L.faction = mob_faction + L.set_iff_factions(mob_faction) return L /obj/structure/mob_spawner/proc/get_death_report(var/mob/living/L) @@ -102,7 +102,7 @@ It also makes it so a ghost wont know where all the goodies/mobs are. if(world.time > last_spawn + spawn_delay) var/turf/mainloc = get_turf(src) for(var/mob/living/A in range(range,mainloc)) - if ((A.faction != mob_faction) && (A.move_speed < 12)) + if (((!mob_faction || !A.has_iff_faction(mob_faction))) && (A.move_speed < 12)) var/chosen_mob = choose_spawn() if(chosen_mob) do_spawn(chosen_mob) @@ -279,7 +279,7 @@ It also makes it so a ghost wont know where all the goodies/mobs are. my_mob.low_priority = TRUE if(faction) - my_mob.faction = faction + my_mob.set_iff_factions(faction) if(atmos_comp) var/turf/T = get_turf(src) diff --git a/code/game/objects/random/mob.dm b/code/game/objects/random/mob.dm index fce0aa645816..006dfdbeb277 100644 --- a/code/game/objects/random/mob.dm +++ b/code/game/objects/random/mob.dm @@ -227,7 +227,7 @@ . = ..() if(istype(., /mob/living/simple_mob)) var/mob/living/simple_mob/this_mob = . - this_mob.faction = src.faction + this_mob.copy_iff_factions(src) if (this_mob.minbodytemp > 200) // Temporary hotfix. Eventually I'll add code to change all mob vars to fit the environment they are spawned in. this_mob.minbodytemp = 200 //wander the mobs around so they aren't always in the same spots diff --git a/code/game/objects/structures/ashlander.dm b/code/game/objects/structures/ashlander.dm index f84c552bd6ad..58902f3eb82d 100644 --- a/code/game/objects/structures/ashlander.dm +++ b/code/game/objects/structures/ashlander.dm @@ -397,10 +397,10 @@ /obj/structure/ashlander/statue/proc/Bless(mob/user) var/mob/living/carbon/human/H = usr - if(!H.faction == "lavaland") - to_chat(user, "You feel as if an eye briefly regards you, and then turns away.") - else - H.add_modifier(/datum/modifier/ashlander_blessing, 15 MINUTES) + // if(!H.faction == "lavaland") + // to_chat(user, "You feel as if an eye briefly regards you, and then turns away.") + // else + H.add_modifier(/datum/modifier/ashlander_blessing, 15 MINUTES) /datum/modifier/ashlander_blessing name = "The Mother's Blessing" diff --git a/code/game/objects/structures/props/nest.dm b/code/game/objects/structures/props/nest.dm index 2d968a94671d..2e494b42e30c 100644 --- a/code/game/objects/structures/props/nest.dm +++ b/code/game/objects/structures/props/nest.dm @@ -53,7 +53,7 @@ var/spawn_choice = pick(creature_types) var/mob/living/L = new spawn_choice(spawnpoint) if(den_faction) - L.faction = den_faction + L.set_iff_factions(den_faction) visible_message("\The [L] crawls out of \the [src].") den_mobs += L tally++ diff --git a/code/game/objects/structures/props/swarm.dm b/code/game/objects/structures/props/swarm.dm index 0941273d10bd..5f46aa887112 100644 --- a/code/game/objects/structures/props/swarm.dm +++ b/code/game/objects/structures/props/swarm.dm @@ -14,12 +14,14 @@ /obj/structure/cult/pylon/swarm/CanAllowThrough(atom/movable/mover, turf/target) if(istype(mover, /mob/living)) var/mob/living/L = mover - if(L.faction == "swarmer") + if(L.has_iff_faction(MOB_IFF_FACTION_SWARMER)) return TRUE else if(istype(mover, /obj/projectile)) var/obj/projectile/P = mover - if(istype(P.firer) && P.firer.faction == "swarmer") - return TRUE + if(isliving(P.firer)) + var/mob/living/L = P.firer + if(L.has_iff_faction(MOB_IFF_FACTION_SWARMER)) + return TRUE return ..() /obj/structure/cult/pylon/swarm/Initialize(mapload) diff --git a/code/game/turfs/change_turf.dm b/code/game/turfs/change_turf.dm index 94367ea22ca8..aa5a0dbbe86b 100644 --- a/code/game/turfs/change_turf.dm +++ b/code/game/turfs/change_turf.dm @@ -43,6 +43,8 @@ GLOBAL_LIST_INIT(multiz_hole_baseturfs, typecacheof(list( /** * get what /turf/baseturf_bottom should be + * + * todo: using a proc is inefficient. is there a better way? like a define? */ /turf/proc/baseturf_core() // todo: this is shitcode, pull it out on maploader refactor. @@ -58,6 +60,8 @@ GLOBAL_LIST_INIT(multiz_hole_baseturfs, typecacheof(list( . = /turf/simulated/open /** * get baseturf on bottom + * + * todo: using a proc is inefficient. is there a better way? like a define? */ /turf/proc/baseturf_bottom() . = islist(baseturfs)? baseturfs[1] : baseturfs @@ -65,6 +69,8 @@ GLOBAL_LIST_INIT(multiz_hole_baseturfs, typecacheof(list( /** * get baseturf underneath + * + * todo: using a proc is inefficient. is there a better way? like a define? */ /turf/proc/baseturf_underneath() . = islist(baseturfs)? baseturfs[length(baseturfs)] : baseturfs @@ -82,6 +88,7 @@ GLOBAL_LIST_INIT(multiz_hole_baseturfs, typecacheof(list( // then we can skip all this bullshit and have proper space zmimic // as long as zm overhead isn't too high. //* THIS CANNOT CALL ANY PROCS UP UNTIL 'new path'! *// + // todo: this entire switch section is kind of ass switch(path) if(null) return diff --git a/code/modules/ai/holders/polaris/ai_holder_cooperation.dm b/code/modules/ai/holders/polaris/ai_holder_cooperation.dm index 3969afff4e34..a34408231de9 100644 --- a/code/modules/ai/holders/polaris/ai_holder_cooperation.dm +++ b/code/modules/ai/holders/polaris/ai_holder_cooperation.dm @@ -32,8 +32,9 @@ // Find another AI-controlled mob in the same faction if possible. var/mob/living/first_friend + // todo: this isn't good because we now use factions for different reasons than binding, so all of the same map is often just friends under this defintion for(var/mob/living/L in living_mob_list) - if(L.faction == holder.faction && L.ai_holder) + if(L.shares_iff_faction(holder) && L.ai_holder) first_friend = L break diff --git a/code/modules/ai/holders/polaris/ai_holder_targeting.dm b/code/modules/ai/holders/polaris/ai_holder_targeting.dm index db99ba0e3f3b..bb119d77f86b 100644 --- a/code/modules/ai/holders/polaris/ai_holder_targeting.dm +++ b/code/modules/ai/holders/polaris/ai_holder_targeting.dm @@ -143,7 +143,7 @@ var/obj/machinery/porta_turret/P = the_target if(P.machine_stat & BROKEN) return FALSE // Already dead. - if(P.faction == holder.faction) + if(holder.has_iff_faction(P.faction)) return FALSE // Don't shoot allied turrets. if(!P.raised && !P.raising) return FALSE // Turrets won't get hurt if they're still in their cover. diff --git a/code/modules/ai/holders/polaris/interfaces.dm b/code/modules/ai/holders/polaris/interfaces.dm index cc6d1943ef7f..61897499e1ec 100644 --- a/code/modules/ai/holders/polaris/interfaces.dm +++ b/code/modules/ai/holders/polaris/interfaces.dm @@ -47,7 +47,7 @@ return say(message) /mob/living/proc/IIsAlly(mob/living/L) - return src.faction == L.faction + return shares_iff_faction(L) /mob/living/simple_mob/IIsAlly(mob/living/L) . = ..() diff --git a/code/modules/blob2/blobs/base_blob.dm b/code/modules/blob2/blobs/base_blob.dm index 6b44bb0e4828..09cdcd04c83f 100644 --- a/code/modules/blob2/blobs/base_blob.dm +++ b/code/modules/blob2/blobs/base_blob.dm @@ -53,12 +53,14 @@ var/list/blobs = list() return TRUE else if(istype(mover, /mob/living)) var/mob/living/L = mover - if(L.faction == "blob") + if(L.has_iff_faction(MOB_IFF_FACTION_BLOB)) return TRUE else if(istype(mover, /obj/projectile)) var/obj/projectile/P = mover - if(istype(P.firer) && P.firer.faction == "blob") - return TRUE + if(isliving(P.firer)) + var/mob/living/L = P.firer + if(L.has_iff_faction(MOB_IFF_FACTION_BLOB)) + return TRUE return FALSE /obj/structure/blob/examine(mob/user, dist) @@ -253,8 +255,10 @@ var/list/blobs = list() /obj/structure/blob/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) . = ..() - if(istype(proj.firer) && proj.firer.faction == "blob") - return + if(isliving(proj.firer)) + var/mob/living/L = proj.firer + if(L.has_iff_faction(MOB_IFF_FACTION_BLOB)) + return TRUE var/damage = proj.get_structure_damage() // So tasers don't hurt the blob. if(!damage) diff --git a/code/modules/blob2/blobs/factory.dm b/code/modules/blob2/blobs/factory.dm index 83f5ea4c5a10..12f8fac4b0a5 100644 --- a/code/modules/blob2/blobs/factory.dm +++ b/code/modules/blob2/blobs/factory.dm @@ -35,7 +35,7 @@ var/mob/living/simple_mob/blob/spore/S = null if(overmind) S = new overmind.blob_type.spore_type(src.loc, src) - S.faction = "blob" + S.set_iff_factions(MOB_IFF_FACTION_BLOB) if(istype(S)) S.overmind = overmind overmind.blob_mobs.Add(S) diff --git a/code/modules/blob2/overmind/overmind.dm b/code/modules/blob2/overmind/overmind.dm index 12f4b1763023..bee62d99dfbc 100644 --- a/code/modules/blob2/overmind/overmind.dm +++ b/code/modules/blob2/overmind/overmind.dm @@ -10,7 +10,8 @@ var/list/overminds = list() see_in_dark = 8 invisibility = INVISIBILITY_OBSERVER - faction = "blob" + iff_factions = MOB_IFF_FACTION_BLOB + var/obj/structure/blob/core/blob_core = null // The blob overmind's core var/blob_points = 0 var/max_blob_points = 200 diff --git a/code/modules/blob2/overmind/powers.dm b/code/modules/blob2/overmind/powers.dm index 67429f3e8f76..e94fd9a0a589 100644 --- a/code/modules/blob2/overmind/powers.dm +++ b/code/modules/blob2/overmind/powers.dm @@ -207,7 +207,7 @@ for(var/mob/living/L in view(src)) if(L.stat == DEAD) continue // Already dying or dead. - if(L.faction == "blob") + if(L.has_iff_faction(MOB_IFF_FACTION_BLOB)) continue // No friendly fire. if(locate(/obj/structure/blob) in L.loc) continue // Already has a blob over them. diff --git a/code/modules/blob2/overmind/types.dm b/code/modules/blob2/overmind/types.dm index 9b01a843fb5a..42a56f5df373 100644 --- a/code/modules/blob2/overmind/types.dm +++ b/code/modules/blob2/overmind/types.dm @@ -266,8 +266,7 @@ if(istype(S)) S.overmind = O O.blob_mobs.Add(S) - else - S.faction = "blob" + S.add_iff_faction(MOB_IFF_FACTION_BLOB) S.update_icons() /datum/blob_type/fulminant_organism/on_death(obj/structure/blob/B) @@ -277,8 +276,7 @@ if(istype(S)) S.overmind = B.overmind B.overmind.blob_mobs.Add(S) - else - S.faction = "blob" + S.add_iff_faction(MOB_IFF_FACTION_BLOB) S.update_icons() @@ -500,7 +498,7 @@ for(var/mob/living/L in range(get_turf(victim), 1)) // We don't use orange(), in case there is more than one mob on the target tile. if(L == victim) // Already hit. continue - if(L.faction == "blob") // No friendly fire + if(L.has_iff_faction(MOB_IFF_FACTION_BLOB)) continue L.blob_act() diff --git a/code/modules/ghostroles/roles/ashlander.dm b/code/modules/ghostroles/roles/ashlander.dm index b228bc81ce00..e0f0be05ccf1 100644 --- a/code/modules/ghostroles/roles/ashlander.dm +++ b/code/modules/ghostroles/roles/ashlander.dm @@ -112,7 +112,7 @@ /datum/ghostrole_instantiator/human/player_static/ashlander/AfterSpawn(mob/created, list/params) . = ..() - created.faction = "lavaland" + created.set_iff_factions(MOB_IFF_FACTION_BIND_TO_MAP) created.mind.teach_crafting_recipe(ashlander_crafting) created.remove_language(/datum/language/common) diff --git a/code/modules/ghostroles/roles/pirate.dm b/code/modules/ghostroles/roles/pirate.dm index d33d16700f5e..93bfa9ee038d 100644 --- a/code/modules/ghostroles/roles/pirate.dm +++ b/code/modules/ghostroles/roles/pirate.dm @@ -50,7 +50,7 @@ /datum/ghostrole_instantiator/human/player_static/pirate/GetOutfit(client/C, mob/M, list/params) var/datum/outfit/outfit = ..() //var/mob/M = /mob/living/carbon/human/H - M.faction = "pirate" + M.set_iff_factions(MOB_IFF_FACTION_PIRATE) switch(params["fluff"]) if("immigrant") return /datum/outfit/pirate/immigrant diff --git a/code/modules/holodeck/HolodeckObjects.dm b/code/modules/holodeck/HolodeckObjects.dm index 0afe78ce86f7..dfe40dcf534f 100644 --- a/code/modules/holodeck/HolodeckObjects.dm +++ b/code/modules/holodeck/HolodeckObjects.dm @@ -428,11 +428,11 @@ /mob/living/simple_mob/animal/space/carp/holodeck/proc/set_safety(var/safe) if (safe) - faction = "neutral" + set_iff_factions(MOB_IFF_FACTION_NEUTRAL) legacy_melee_damage_lower = 0 legacy_melee_damage_upper = 0 else - faction = "carp" + set_iff_factions(MOB_IFF_FACTION_CARP) legacy_melee_damage_lower = initial(legacy_melee_damage_lower) legacy_melee_damage_upper = initial(legacy_melee_damage_upper) diff --git a/code/modules/mapping/map.dm b/code/modules/mapping/map.dm index 77d3d456985d..cc0283611191 100644 --- a/code/modules/mapping/map.dm +++ b/code/modules/mapping/map.dm @@ -129,8 +129,8 @@ for(var/i in 1 to length(levels)) if(ispath(levels[i])) var/datum/map_level/level_path = levels[i] - var/datum/map_level/level_instance = new level_path - level_instance.hardcoded = TRUE + var/datum/map_level/level_instance = new level_path(src) + level_instance.hardcoded = TRUE // todo: map can just also not be hardcoded levels[i] = level_instance if(levels_match_mangling_id) level_instance.mangling_id = mangling_id || id diff --git a/code/modules/mapping/map_level.dm b/code/modules/mapping/map_level.dm index 38935b04e44d..9c8436799c1c 100644 --- a/code/modules/mapping/map_level.dm +++ b/code/modules/mapping/map_level.dm @@ -45,23 +45,41 @@ var/base_turf = /turf/space /// base area typepath for this level var/base_area = /area/space - /// id of north zlevel - overrides linkage if set. can be set to path, autoconverts to id on new. - /// can also be set to instance - used for structs. + /// id of north zlevel - overrides linkage if set. + /// + /// * can also be set to path + /// * can also be set to instance - used for structs + /// * do not manually set it to levelpath::id, map levels generate ids dynamically! var/link_north - /// id of south zlevel - overrides linkage if set. can be set to path, autoconverts to id on new. - /// can also be set to instance - used for structs. + /// id of south zlevel - overrides linkage if set. + /// + /// * can also be set to path + /// * can also be set to instance - used for structs + /// * do not manually set it to levelpath::id, map levels generate ids dynamically! can also be set to instance - used for structs. var/link_south - /// id of west zlevel - overrides linkage if set. can be set to path, autoconverts to id on new. - /// can also be set to instance - used for structs. + /// id of west zlevel - overrides linkage if set. + /// + /// * can also be set to path + /// * can also be set to instance - used for structs + /// * do not manually set it to levelpath::id, map levels generate ids dynamically! can also be set to instance - used for structs. var/link_west - /// id of east zlevel - overrides linkage if set. can be set to path, autoconverts to id on new. - /// can also be set to instance - used for structs. + /// id of east zlevel - overrides linkage if set. + /// + /// * can also be set to path + /// * can also be set to instance - used for structs + /// * do not manually set it to levelpath::id, map levels generate ids dynamically! can also be set to instance - used for structs. var/link_east - /// id of below zlevel - overrides linkage if set. can be set to path, autoconverts to id on new. - /// can also be set to instance - used for structs. + /// id of below zlevel - overrides linkage if set. + /// + /// * can also be set to path + /// * can also be set to instance - used for structs + /// * do not manually set it to levelpath::id, map levels generate ids dynamically! can also be set to instance - used for structs. var/link_below - /// id of above zlevel - overrides linkage if set. can be set to path, autoconverts to id on new. - /// can also be set to instance - used for structs. + /// id of above zlevel - overrides linkage if set. + /// + /// * can also be set to path + /// * can also be set to instance - used for structs + /// * do not manually set it to levelpath::id, map levels generate ids dynamically! can also be set to instance - used for structs. var/link_above /// gas string / atmosphere path / atmosphere id for indoors air /// if atmosphere path, it'll be automatically packed to ID on serialize, as we don't want to serialize paths to disk. @@ -152,16 +170,6 @@ if(!isnull(parent_map)) id = "[parent_map.id]-[id]" - #define UNPACK_LINK(vname) if(ispath(vname, /datum/map_level)) { var/datum/map_level/cast_##vname = vname; vname = initial(cast_##vname.id) ; } - UNPACK_LINK(link_north) - UNPACK_LINK(link_south) - UNPACK_LINK(link_east) - UNPACK_LINK(link_west) - UNPACK_LINK(link_below) - UNPACK_LINK(link_above) - BLOCK_BYOND_BUG_2072419 - #undef UNPACK_LINK - /datum/map_level/Destroy() if(loaded) . = QDEL_HINT_LETMELIVE @@ -236,18 +244,36 @@ transition = data["transition"] if(!isnull(data["base_turf"])) base_turf = text2path(data["base_turf"]) + + // Resolve links, including if they got serlalized as typepaths. + // todo: typepaths should be trampled into ids on save instead. + var/resolving_link + var/maybe_link_path if(!isnull(data["link_north"])) - link_north = data["link_north"] + resolving_link = data["north"] + maybe_link_path = text2path(resolving_link) + link_north = ispath(maybe_link_path) ? maybe_link_path : resolving_link if(!isnull(data["link_south"])) - link_south = data["link_south"] + resolving_link = data["south"] + maybe_link_path = text2path(resolving_link) + link_south = ispath(maybe_link_path) ? maybe_link_path : resolving_link if(!isnull(data["link_above"])) - link_above = data["link_above"] + resolving_link = data["above"] + maybe_link_path = text2path(resolving_link) + link_above = ispath(maybe_link_path) ? maybe_link_path : resolving_link if(!isnull(data["link_below"])) - link_below = data["link_below"] + resolving_link = data["below"] + maybe_link_path = text2path(resolving_link) + link_below = ispath(maybe_link_path) ? maybe_link_path : resolving_link if(!isnull(data["link_west"])) - link_west = data["link_west"] + resolving_link = data["west"] + maybe_link_path = text2path(resolving_link) + link_west = ispath(maybe_link_path) ? maybe_link_path : resolving_link if(!isnull(data["link_east"])) - link_east = data["link_east"] + resolving_link = data["east"] + maybe_link_path = text2path(resolving_link) + link_east = ispath(maybe_link_path) ? maybe_link_path : resolving_link + if(!isnull(data["air_indoors"])) air_indoors = data["air_indoors"] if(!isnull(data["air_outdoors"])) diff --git a/code/modules/maps/_shim/away_spawner.dm b/code/modules/maps/_shim/away_spawner.dm index 08ed95275c1c..861b714f3aaa 100644 --- a/code/modules/maps/_shim/away_spawner.dm +++ b/code/modules/maps/_shim/away_spawner.dm @@ -58,7 +58,7 @@ For now this is still usable but bad. my_mob.low_priority = TRUE if(faction) - my_mob.faction = faction + my_mob.set_iff_factions(faction) if(atmos_comp) var/turf/T = get_turf(src) diff --git a/code/modules/maps/away_missions/140x140/snowfield.dm b/code/modules/maps/away_missions/140x140/snowfield.dm index 77ae30c9e42f..8c1d71f1ca11 100644 --- a/code/modules/maps/away_missions/140x140/snowfield.dm +++ b/code/modules/maps/away_missions/140x140/snowfield.dm @@ -61,7 +61,8 @@ icon_gib = "bear-gib" say_list_type = /datum/say_list/polar_bear - faction = "polar" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP + maxHealth = 80 health = 80 // Polar bear will fuck you up. @@ -89,13 +90,10 @@ ..() /mob/living/simple_mob/animal/sif/sakimm/polar - faction = "polar" /mob/living/simple_mob/animal/sif/diyaab/polar - faction = "polar" /mob/living/simple_mob/animal/sif/shantak/polar - faction = "polar" // -- Items -- // diff --git a/code/modules/mob/living/carbon/alien/larva/larva.dm b/code/modules/mob/living/carbon/alien/larva/larva.dm index b542ea2d1d49..64741a5c14fc 100644 --- a/code/modules/mob/living/carbon/alien/larva/larva.dm +++ b/code/modules/mob/living/carbon/alien/larva/larva.dm @@ -16,7 +16,7 @@ language = "Hivemind" maxHealth = 25 health = 25 - faction = "xeno" + iff_factions = MOB_IFF_FACTION_XENOMORPH catalogue_data = list(/datum/category_item/catalogue/fauna/alien/larva) /mob/living/carbon/alien/larva/Initialize(mapload) diff --git a/code/modules/mob/living/carbon/alien/progression.dm b/code/modules/mob/living/carbon/alien/progression.dm index a76fc409c230..c082f7d2e515 100644 --- a/code/modules/mob/living/carbon/alien/progression.dm +++ b/code/modules/mob/living/carbon/alien/progression.dm @@ -30,8 +30,7 @@ transfer_languages(src, adult) - if(src.faction != "neutral") - adult.faction = src.faction + adult.copy_iff_factions(src) if(mind) mind.transfer(adult) diff --git a/code/modules/mob/living/carbon/human/ai_controlled/ai_controlled.dm b/code/modules/mob/living/carbon/human/ai_controlled/ai_controlled.dm index c43d9c05500a..d0275e768bdc 100644 --- a/code/modules/mob/living/carbon/human/ai_controlled/ai_controlled.dm +++ b/code/modules/mob/living/carbon/human/ai_controlled/ai_controlled.dm @@ -117,8 +117,6 @@ generate_gender = TRUE identifying_gender = NEUTER - faction = "xeno" - to_wear_helmet = /obj/item/clothing/head/helmet/dermal to_wear_glasses = /obj/item/clothing/glasses/goggles to_wear_mask = /obj/item/clothing/mask/gas/half diff --git a/code/modules/mob/living/living-defense-legacy.dm b/code/modules/mob/living/living-defense-legacy.dm index 6f88e783acd2..ed9609f7d8f8 100644 --- a/code/modules/mob/living/living-defense-legacy.dm +++ b/code/modules/mob/living/living-defense-legacy.dm @@ -136,7 +136,7 @@ ..() /mob/living/blob_act(var/obj/structure/blob/B) - if(stat == DEAD || faction == "blob") + if(stat == DEAD || has_iff_faction(MOB_IFF_FACTION_BLOB)) return var/damage = rand(30, 40) diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index d65c614a6c67..9312e6a33a18 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -73,7 +73,9 @@ /// This is used to determine if the mob failed a breath. If they did fail a brath, they will attempt to breathe each tick, otherwise just once per 4 ticks. var/failed_last_breath = 0 - var/lastpuke = 0 + var/lastpuke = 0//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station developers. *// + /// Makes attacks harder to land. Negative numbers increase hit chance. var/evasion = 0 diff --git a/code/modules/mob/living/movement.dm b/code/modules/mob/living/movement.dm index c67d50f33fa6..f75f2182feb7 100644 --- a/code/modules/mob/living/movement.dm +++ b/code/modules/mob/living/movement.dm @@ -396,7 +396,7 @@ // riding same thing, don't block each other return TRUE // can't throw blob stuff through blob stuff - if(istype(mover, /obj/structure/blob) && faction == "blob" && !mover.throwing) //Blobs should ignore things on their faction. + if(istype(mover, /obj/structure/blob) && has_iff_faction(MOB_IFF_FACTION_BLOB) && !mover.throwing) //Blobs should ignore things on their faction. return TRUE /mob/living/CheckExit(atom/movable/AM, atom/newLoc) diff --git a/code/modules/mob/living/silicon/robot/drone/swarm.dm b/code/modules/mob/living/silicon/robot/drone/swarm.dm index 5cb7268301db..8a45b86d4181 100644 --- a/code/modules/mob/living/silicon/robot/drone/swarm.dm +++ b/code/modules/mob/living/silicon/robot/drone/swarm.dm @@ -3,7 +3,7 @@ real_name = "drone" icon = 'icons/mob/swarmbot.dmi' icon_state = "swarmer" - faction = "swarmer" + iff_factions = MOB_IFF_FACTION_SWARMER maxHealth = 75 health = 75 cell_emp_mult = 0.5 @@ -69,7 +69,7 @@ real_name = "drone" icon = 'icons/mob/swarmbot.dmi' icon_state = "swarmer_ranged" - faction = "swarmer" + iff_factions = MOB_IFF_FACTION_SWARMER law_type = /datum/ai_laws/swarm_drone/soldier module_type = /obj/item/robot_module/drone/swarm/ranged @@ -85,7 +85,7 @@ real_name = "drone" icon = 'icons/mob/swarmbot.dmi' icon_state = "swarmer_melee" - faction = "swarmer" + iff_factions = MOB_IFF_FACTION_SWARMER law_type = /datum/ai_laws/swarm_drone/soldier module_type = /obj/item/robot_module/drone/swarm/melee diff --git a/code/modules/mob/living/simple_mob/simple_mob.dm b/code/modules/mob/living/simple_mob/simple_mob.dm index af9e9d3dd7bc..909771c1d520 100644 --- a/code/modules/mob/living/simple_mob/simple_mob.dm +++ b/code/modules/mob/living/simple_mob/simple_mob.dm @@ -14,6 +14,8 @@ mob_swap_flags = ~HEAVY mob_push_flags = ~HEAVY + iff_factions = MOB_IFF_FACTION_BIND_AUTO + //? Attacks - Basic /// melee style var/datum/unarmed_attack/melee_style diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/goat.dm b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/goat.dm index bc4762728287..9eb1d0be798c 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/goat.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/goat.dm @@ -16,7 +16,9 @@ icon_dead = "goat_dead" catalogue_data = list(/datum/category_item/catalogue/fauna/livestock/goat) - faction = "goat" + iff_factions = list( + MOB_IFF_FACTION_FARM_ANIMAL, + ) health = 40 maxHealth = 40 diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/lythios.dm b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/lythios.dm index b6000dcdd3c3..79981b31e3d7 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/lythios.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/lythios.dm @@ -22,7 +22,7 @@ icon_dead = "icegoat_dead" catalogue_data = list(/datum/category_item/catalogue/fauna/livestock/icegoat) - faction = "goat" + iff_factions = MOB_IFF_FACTION_FARM_ANIMAL minbodytemp = 180 maxbodytemp = 275 @@ -117,7 +117,7 @@ icon_dead = "woolie_dead" catalogue_data = list(/datum/category_item/catalogue/fauna/livestock/woolie) - faction = "goat" + iff_factions = MOB_IFF_FACTION_FARM_ANIMAL minbodytemp = 180 maxbodytemp = 300 @@ -189,7 +189,8 @@ minbodytemp = 180 maxbodytemp = 350 - faction = "grubs" + iff_factions = MOB_IFF_FACTION_FARM_ANIMAL + maxHealth = 50 health = 50 diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/_giant_spider.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/_giant_spider.dm index ecf24ffe0e56..a59f1056122f 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/_giant_spider.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/_giant_spider.dm @@ -69,7 +69,8 @@ icon_dead = "guard_dead" has_eye_glow = TRUE - faction = "spiders" + iff_factions = MOB_IFF_FACTION_SPIDER + maxHealth = 200 health = 200 randomized = TRUE diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/carrier.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/carrier.dm index e3aca574fd8e..f59fa86255d4 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/carrier.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/carrier.dm @@ -60,7 +60,7 @@ swarmling.health = swarm_health swarmling.legacy_melee_damage_lower = swarm_dam_lower swarmling.legacy_melee_damage_upper = swarm_dam_upper - swarmling.faction = swarmling_faction + swarmling.set_iff_factions(swarmling_faction) swarmling.adjust_scale(0.75) new_spiders += swarmling else if(src) diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/crab.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/crab.dm index d4188d3fbd5b..f14c48c6dbb1 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/crab.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/crab.dm @@ -10,7 +10,9 @@ name = "crab" desc = "A hard-shelled crustacean. Seems quite content to lounge around all the time." tt_desc = "E Cancer bellianus" - faction = "crabs" + + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP + catalogue_data = list(/datum/category_item/catalogue/fauna/crab) icon_state = "crab" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/passive.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/passive.dm index 1a3aff961f17..1dd44c59b331 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/passive.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/passive.dm @@ -3,3 +3,7 @@ /mob/living/simple_mob/animal/passive ai_holder_type = /datum/ai_holder/polaris/simple_mob/passive mob_bump_flag = 0 + iff_factions = list( + MOB_IFF_FACTION_NEUTRAL, + MOB_IFF_FACTION_FARM_PET, + ) diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/fox_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/fox_vr.dm index bd25ba380764..81fd2f00028b 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/fox_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/fox_vr.dm @@ -24,7 +24,7 @@ mob_size = MOB_SMALL //Foxes are not smaller than cats so bumping them up to small randomized = TRUE - faction = "fox" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP response_help = "scritches" response_disarm = "gently pushes aside" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/roach/roach.dm b/code/modules/mob/living/simple_mob/subtypes/animal/roach/roach.dm index baeb4b6a8c18..649c396a415b 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/roach/roach.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/roach/roach.dm @@ -62,7 +62,8 @@ see_in_dark = 6 universal_understand = 1 - faction = "roaches" + + iff_factions = MOB_IFF_FACTION_ROACH mob_size = MOB_SMALL pass_flags = ATOM_PASS_TABLE @@ -377,7 +378,7 @@ item_state = "uberfallen" icon_living = "uberfallen" icon_dead = "uberfallen_dead" - faction = "synthtide" + catalogue_data = list(/datum/category_item/catalogue/fauna/roach/uberfallen) maxHealth = 30 health = 30 diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/diyaab.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/diyaab.dm index 0e68b5967b9a..d1f50ecf6464 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/diyaab.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/diyaab.dm @@ -19,7 +19,7 @@ tt_desc = "S Choeros hirtus" //diyaab and shantak are technically reletives! catalogue_data = list(/datum/category_item/catalogue/fauna/diyaab) - faction = "diyaab" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP icon_state = "diyaab" icon_living = "diyaab" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/duck.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/duck.dm index 34b8aef5de3b..5a3124821ec5 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/duck.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/duck.dm @@ -19,7 +19,7 @@ tt_desc = "S Anatidae vitriae" catalogue_data = list(/datum/category_item/catalogue/fauna/crystalduck) - faction = "duck" + iff_factions = MOB_IFF_FACTION_FARM_NEUTRAL icon_state = "duck" icon_living = "duck" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/frostfly.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/frostfly.dm index d09429d0ead5..0369de91df86 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/frostfly.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/frostfly.dm @@ -30,7 +30,7 @@ tt_desc = "S Carabidae glacios" catalogue_data = list(/datum/category_item/catalogue/fauna/frostfly) - faction = "diyaab" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP icon_state = "firefly" icon_living = "firefly" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/glitterfly.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/glitterfly.dm index d37f68a4758b..3664d5976761 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/glitterfly.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/glitterfly.dm @@ -28,7 +28,7 @@ tt_desc = "S Lepidoptera adamas" catalogue_data = list(/datum/category_item/catalogue/fauna/glitterfly) - faction = "neutral" + iff_factions = MOB_IFF_FACTION_NEUTRAL icon_state = "butterfly" icon_living = "butterfly" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm index 09bc573d8298..1afaf0aec5be 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm @@ -36,7 +36,7 @@ mod_min = 100 mod_max = 150 - faction = "crabs" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP maxHealth = 200 health = 200 diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/kururak.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/kururak.dm index aa8b855bdc03..9874709f3121 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/kururak.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/kururak.dm @@ -23,7 +23,7 @@ tt_desc = "S Felidae fluctursora" catalogue_data = list(/datum/category_item/catalogue/fauna/kururak) - faction = "kururak" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP icon_state = "bigcat" icon_living = "bigcat" @@ -292,7 +292,7 @@ continue if(!K.ai_holder) continue - if(K.faction != src.faction) + if(!K.shares_iff_faction(src)) continue var/datum/ai_holder/polaris/AI = K.ai_holder to_chat(K, SPAN_NOTICE("The pack leader wishes for you to follow them.")) diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/leech.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/leech.dm index d031352454d9..1306b7868f26 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/leech.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/leech.dm @@ -33,7 +33,7 @@ tt_desc = "S Hirudinea phorus" catalogue_data = list(/datum/category_item/catalogue/fauna/iceleech) - faction = "leech" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP icon_state = "leech" item_state = "brainslug" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/racoon.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/racoon.dm index 293d140e762a..f607f5267a71 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/racoon.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/racoon.dm @@ -17,7 +17,7 @@ tt_desc = "S Procyon cogitae" catalogue_data = list(/datum/category_item/catalogue/fauna/sakimm) - faction = "sakimm" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP icon_state = "raccoon" icon_living = "raccoon" @@ -349,4 +349,4 @@ /mob/living/simple_mob/animal/sif/sakimm/dexter name = "Dexter" desc = "A tame, oversized rodent with hands. It seems really friendly." - faction = "neutral" + iff_factions = MOB_IFF_FACTION_NEUTRAL diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/savik.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/savik.dm index b5f2bc6d75dd..5ac610fd67e2 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/savik.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/savik.dm @@ -21,7 +21,7 @@ name = "savik" tt_desc = "S Pistris tellus" //landshark catalogue_data = list(/datum/category_item/catalogue/fauna/savik) - faction = "savik" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP icon_state = "savik" icon_living = "savik" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm index 73cd15895dab..f715b466daf4 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm @@ -25,7 +25,7 @@ tt_desc = "S Choeros shantak" catalogue_data = list(/datum/category_item/catalogue/fauna/shantak) - faction = "shantak" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP icon_state = "shantak" icon_living = "shantak" @@ -74,7 +74,7 @@ continue if(!S.ai_holder) continue - if(S.faction != src.faction) + if(!S.shares_iff_faction(src)) continue var/datum/ai_holder/polaris/AI = S.ai_holder AI.set_follow(src) @@ -94,4 +94,4 @@ name = "Scruffy" ai_holder_type = /datum/ai_holder/polaris/simple_mob/passive makes_dirt = 0 - faction = "neutral" + iff_factions = MOB_IFF_FACTION_NEUTRAL diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/alien.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/alien.dm index 405b220f8374..0aeff455be7b 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/alien.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/alien.dm @@ -26,6 +26,9 @@ desc = "Warriors serve as the primary combat caste within a Hive Structure, while having fewer numbers than the endless drone hordes, they are none-the-less extremely formidable. " value = CATALOGUER_REWARD_MEDIUM +/mob/living/simple_mob/animal/space/alien + iff_factions = MOB_IFF_FACTION_XENOMORPH + /mob/living/simple_mob/animal/space/alien/warrior name = "xenomorph warrior" desc = "A feral Xenomorph that plays the part of the Hive Structures main fighter. Standing at an even larger stance than a drone, its exoskeleton is fully militarized, intended to take hits from both melee and ranged alike. Its claws can easily tear through armor and flesh, while its acid does the rest." @@ -46,7 +49,6 @@ catalogue_data = list(/datum/category_item/catalogue/fauna/feral_alien/warrior) ai_holder_type = /datum/ai_holder/polaris/simple_mob/melee/evasive - faction = "xeno" mob_class = MOB_CLASS_ABERRATION @@ -97,7 +99,6 @@ legacy_melee_damage_lower = 20 legacy_melee_damage_upper = 20 base_attack_cooldown = 6 - faction = "xeno" attack_sound = 'sound/mobs/biomorphs/drone_attack.ogg' movement_sound = 'sound/mobs/biomorphs/drone_move.ogg' catalogue_data = list(/datum/category_item/catalogue/fauna/feral_alien/drone) @@ -128,7 +129,6 @@ health = 200 legacy_melee_damage_lower = 10 legacy_melee_damage_upper = 10 - faction = "xeno" base_pixel_x = -8 movement_cooldown = 3 projectiletype = /obj/projectile/energy/neurotoxin @@ -153,7 +153,6 @@ health = 250 legacy_melee_damage_lower = 20 legacy_melee_damage_upper = 20 - faction = "xeno" movement_cooldown = 2 base_pixel_x = -8 base_pixel_y = 1 @@ -190,7 +189,6 @@ ) legacy_melee_damage_lower = 50 legacy_melee_damage_upper = 50 - faction = "xeno" movement_cooldown = 2 base_pixel_x = -17 base_pixel_y = 6 @@ -332,7 +330,6 @@ legacy_melee_damage_lower = 40 legacy_melee_damage_upper = 40 attack_armor_type = DAMAGE_MODE_PIERCE | DAMAGE_MODE_SHARP - faction = "xeno" movement_cooldown = 3 base_pixel_x = -18 base_pixel_y = 2 @@ -381,7 +378,6 @@ legacy_melee_damage_lower = 70 legacy_melee_damage_upper = 50 attack_armor_pen = 60 - faction = "xeno" movement_cooldown = 4 base_pixel_x = -15 base_pixel_y = 6 @@ -515,7 +511,6 @@ health = 300 legacy_melee_damage_lower = 35 legacy_melee_damage_upper = 40 - faction = "xeno" movement_cooldown = 0 icon_scale_x = 0.7 icon_scale_y = 0.7 @@ -687,7 +682,6 @@ health = 250 legacy_melee_damage_lower = 10 legacy_melee_damage_upper = 10 - faction = "xeno" movement_cooldown = 4 base_pixel_x = -8 base_pixel_y = 1 diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/bats.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/bats.dm index e325e4fbdb77..cb3f6064c4b5 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/bats.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/bats.dm @@ -17,7 +17,7 @@ icon_gib = "bat_dead" catalogue_data = list(/datum/category_item/catalogue/fauna/bats) - faction = "scarybat" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP maxHealth = 20 health = 20 @@ -57,7 +57,7 @@ // Spookiest of bats /mob/living/simple_mob/animal/space/bats/cult - faction = "cult" + iff_factions = MOB_IFF_FACTION_SANGUINE_CULT supernatural = TRUE /mob/living/simple_mob/animal/space/bats/cult/cultify() diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/bear.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/bear.dm index b9c903035ee9..777ad7974b70 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/bear.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/bear.dm @@ -19,7 +19,7 @@ icon_gib = "bear_gib" catalogue_data = list(/datum/category_item/catalogue/fauna/space_bear) - faction = "russian" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP maxHealth = 125 health = 125 diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm index b15d52832f90..59c791db93c9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm @@ -34,7 +34,8 @@ icon_dead = "carp_dead" icon_gib = "carp_gib" - faction = "carp" + iff_factions = MOB_IFF_FACTION_CARP + maxHealth = 25 health = 25 randomized = TRUE diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/gaslamp_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/gaslamp_vr.dm index 1d97e5bc9a52..f9213088c026 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/gaslamp_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/gaslamp_vr.dm @@ -32,7 +32,8 @@ TODO: Make them light up and heat the air when exposed to oxygen. icon_dead = "gaslamp-dead" icon = 'icons/mob/vore32x64.dmi' - faction = "virgo3b" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP + maxHealth = 100 health = 100 randomized = TRUE diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/goose.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/goose.dm index c6f00f33cf26..725f4d1a47e7 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/goose.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/goose.dm @@ -14,7 +14,7 @@ icon_dead = "goose_dead" catalogue_data = list(/datum/category_item/catalogue/fauna/goose) - faction = "geese" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP maxHealth = 30 health = 30 @@ -52,6 +52,3 @@ set category = "Abilities" add_modifier(/datum/modifier/berserk, 30 SECONDS) - -/mob/living/simple_mob/animal/space/goose/virgo3b - faction = "virgo3b" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/horing.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/horing.dm index bf95597dc568..d490b5426318 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/horing.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/horing.dm @@ -9,7 +9,7 @@ icon_rest = "thrumbo_rest" maxHealth = 500 health = 500 - faction = "horing" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP pixel_x = -16 special_attack_min_range = 3 special_attack_max_range = 8 diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/mouse_army.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/mouse_army.dm index f76c3a8289a1..123116061f79 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/mouse_army.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/mouse_army.dm @@ -35,7 +35,8 @@ icon_living = "mouse_gray" icon_dead = "mouse_gray_dead" icon_rest = "mouse_gray_sleep" - faction = "mouse_army" + + iff_factions = MOB_IFF_FACTION_FARM_PEST maxHealth = 50 health = 50 diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/worm.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/worm.dm index 1cf183f2550f..9ee40ccb70c4 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/worm.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/worm.dm @@ -15,7 +15,7 @@ movement_cooldown = 0 movement_sound = 'sound/effects/sand_step.ogg' - faction = "worm" + iff_factions = MOB_IFF_FACTION_WORM status_flags = 0 universal_speak = 0 @@ -143,7 +143,7 @@ var/mob/living/simple_mob/animal/space/space_worm/newSegment = new segment_type(loc) current.Attach(newSegment) current = newSegment - current.faction = faction + current.copy_iff_factions(src) /mob/living/simple_mob/animal/space/space_worm/head/verb/toggle_devour() set name = "Toggle Feeding" @@ -404,7 +404,7 @@ /mob/living/simple_mob/animal/space/space_worm/proc/update_body_faction() if(next) // Keep us on the same page, here. - faction = next.faction + copy_iff_factions(next) if(previous) previous.update_body_faction() return 1 diff --git a/code/modules/mob/living/simple_mob/subtypes/blob/blob.dm b/code/modules/mob/living/simple_mob/subtypes/blob/blob.dm index 361a27500e45..9d815960f40d 100644 --- a/code/modules/mob/living/simple_mob/subtypes/blob/blob.dm +++ b/code/modules/mob/living/simple_mob/subtypes/blob/blob.dm @@ -33,7 +33,7 @@ /mob/living/simple_mob/blob icon = 'icons/mob/blob.dmi' pass_flags = ATOM_PASS_BLOB | ATOM_PASS_TABLE - faction = "blob" + iff_factions = MOB_IFF_FACTION_BLOB catalogue_data = list(/datum/category_item/catalogue/fauna/blob) heat_damage_per_tick = 0 diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Eddy.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Eddy.dm index af439b298ee2..f1b32eb16bcd 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Eddy.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Eddy.dm @@ -12,7 +12,6 @@ icon_living = "Eddy" icon_dead = "e_head" icon_rest = "Eddy" - faction = "horror" icon = 'icons/mob/horror_show/GHPS.dmi' icon_gib = "generic_gib" catalogue_data = list(/datum/category_item/catalogue/fauna/horror/Eddy) diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Master.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Master.dm index d49a957e3d42..4c43aea4205e 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Master.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Master.dm @@ -12,7 +12,6 @@ icon_living = "Helix" icon_dead = "m_dead" icon_rest = "Helix" - faction = "horror" icon = 'icons/mob/horror_show/master.dmi' icon_gib = "generic_gib" anchored = 1 diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Rickey.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Rickey.dm index c949dafb96e2..94281d20ad83 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Rickey.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Rickey.dm @@ -12,7 +12,6 @@ icon_living = "Rickey" icon_dead = "r_head" icon_rest = "Rickey" - faction = "horror" icon = 'icons/mob/horror_show/GHPS.dmi' icon_gib = "generic_gib" catalogue_data = list(/datum/category_item/catalogue/fauna/horror/Rickey) diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Smiley.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Smiley.dm index c5f532332c85..4cb631309dfb 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Smiley.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Smiley.dm @@ -12,7 +12,6 @@ icon_living = "Smiley" icon_dead = "s_head" icon_rest = "Smiley" - faction = "horror" icon = 'icons/mob/horror_show/GHPS.dmi' icon_gib = "generic_gib" catalogue_data = list(/datum/category_item/catalogue/fauna/horror/Smiley) diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Steve.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Steve.dm index 507441749a72..9b9c28a3a21a 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Steve.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Steve.dm @@ -12,7 +12,6 @@ icon_living = "Steve" icon_dead = "sg_head" icon_rest = "Steve" - faction = "horror" icon = 'icons/mob/horror_show/GHPS.dmi' icon_gib = "generic_gib" catalogue_data = list(/datum/category_item/catalogue/fauna/horror/Steve) diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Willy.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Willy.dm index 8e7fe09304d5..63ec0ecd54b7 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Willy.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Willy.dm @@ -12,7 +12,6 @@ icon_living = "Willy" icon_dead = "w_head" icon_rest = "Willy" - faction = "horror" icon = 'icons/mob/horror_show/GHPS.dmi' icon_gib = "generic_gib" catalogue_data = list(/datum/category_item/catalogue/fauna/horror/Willy) diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/bradley.dm b/code/modules/mob/living/simple_mob/subtypes/horror/bradley.dm index e10d0e557053..dd8a6f962a37 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/bradley.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/bradley.dm @@ -12,7 +12,6 @@ icon_living = "Bradley" icon_dead = "b_head" icon_rest = "Bradley" - faction = "horror" icon = 'icons/mob/horror_show/GHPS.dmi' icon_gib = "generic_gib" catalogue_data = list(/datum/category_item/catalogue/fauna/horror/bradley) diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/horror .dm b/code/modules/mob/living/simple_mob/subtypes/horror/horror .dm index f14bd678effe..e543b849daa3 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/horror .dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/horror .dm @@ -27,7 +27,7 @@ /mob/living/simple_mob/horror tt_desc = "Homo Horrificus" - faction = "horror" + iff_factions = MOB_IFF_FACTION_MUTANT icon = 'icons/mob/horror_show/GHPS.dmi' icon_gib = "generic_gib" taser_kill = 0 diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/sally.dm b/code/modules/mob/living/simple_mob/subtypes/horror/sally.dm index b051923756db..64ec477eddc7 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/sally.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/sally.dm @@ -12,7 +12,6 @@ icon_living = "Sally" icon_dead = "ws_head" icon_rest = "Sally" - faction = "horror" icon = 'icons/mob/horror_show/widehorror.dmi' icon_gib = "generic_gib" catalogue_data = list(/datum/category_item/catalogue/fauna/horror/Sally) diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/shittytim.dm b/code/modules/mob/living/simple_mob/subtypes/horror/shittytim.dm index 2c051b4d6301..4b79e05eed18 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/shittytim.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/shittytim.dm @@ -12,7 +12,6 @@ icon_living = "shitty_tim" icon_dead = "tst_head" icon_rest = "shitty_tim" - faction = "horror" icon = 'icons/mob/horror_show/tallhorror.dmi' icon_gib = "generic_gib" catalogue_data = list(/datum/category_item/catalogue/fauna/horror/BigTim) diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/timling.dm b/code/modules/mob/living/simple_mob/subtypes/horror/timling.dm index 60fe08909b2a..41bfde71be47 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/timling.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/timling.dm @@ -12,7 +12,6 @@ icon_living = "timling" icon_dead = "tt_head" icon_rest = "timling" - faction = "horror" icon = 'icons/mob/horror_show/tallhorror.dmi' icon_gib = "generic_gib" catalogue_data = list(/datum/category_item/catalogue/fauna/horror/TinyTim) diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/clown.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/clown.dm index e9e9ee7e338a..f659511aa4b8 100644 --- a/code/modules/mob/living/simple_mob/subtypes/humanoid/clown.dm +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/clown.dm @@ -30,7 +30,7 @@ icon_gib = "clown_gib" catalogue_data = list(/datum/category_item/catalogue/fauna/clown) - faction = "clown" + iff_factions = MOB_IFF_FACTION_MERCENARY_GROUP("clown") loot_list = list(/obj/item/bikehorn = 100) @@ -145,7 +145,7 @@ var/mob_count = 0 // Are there enough mobs to consider grenading? var/turf/T = get_turf(A) for(var/mob/M in range(T, 2)) - if(M.faction == faction) // Don't grenade our friends + if(shares_iff_faction(M)) return FALSE if(M in oview(src, special_attack_max_range)) // And lets check if we can actually see at least two people before we throw a grenade if(!M.stat) // Dead things don't warrant a grenade diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/cultist.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/cultist.dm index bf0caab9bcbd..cb3ef4c86221 100644 --- a/code/modules/mob/living/simple_mob/subtypes/humanoid/cultist.dm +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/cultist.dm @@ -38,7 +38,7 @@ tt_desc = "NULL" icon = 'icons/mob/cult.dmi' icon_state = "initiate" - faction = "cult" + iff_factions = MOB_IFF_FACTION_SANGUINE_CULT mob_class = MOB_CLASS_DEMONIC /datum/category_item/catalogue/fauna/cultist/human @@ -222,8 +222,6 @@ health = 75 catalogue_data = list(/datum/category_item/catalogue/fauna/cultist/tesh) - faction = "cult" - status_flags = 0 response_help = "pokes" @@ -273,8 +271,6 @@ health = 200 catalogue_data = list(/datum/category_item/catalogue/fauna/cultist/lizard) - faction = "cult" - status_flags = 0 response_help = "pokes" @@ -324,8 +320,6 @@ health = 150 catalogue_data = list(/datum/category_item/catalogue/fauna/cultist/caster) - faction = "cult" - status_flags = 0 response_help = "pokes" @@ -375,8 +369,6 @@ health = 150 catalogue_data = list(/datum/category_item/catalogue/fauna/cultist/initiate) - faction = "cult" - status_flags = 0 response_help = "pokes" @@ -423,8 +415,6 @@ health = 75 catalogue_data = list(/datum/category_item/catalogue/fauna/cultist/castertesh) - faction = "cult" - status_flags = 0 response_help = "pokes" @@ -472,7 +462,6 @@ desc = "A heavily armed cultist with a mirror shield that hurts to look at." icon_state = "cult_elite" icon_living = "cult_elite" - faction = "cult" catalogue_data = list(/datum/category_item/catalogue/fauna/cultist/elite) status_flags = 0 @@ -550,8 +539,6 @@ health = 300 catalogue_data = list(/datum/category_item/catalogue/fauna/cultist/magus) - faction = "cult" - status_flags = 0 response_help = "pokes" @@ -623,8 +610,6 @@ health = 300 catalogue_data = list(/datum/category_item/catalogue/fauna/cultist/hunter) - faction = "cult" - status_flags = 0 response_help = "pokes" diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/humanoid_vr.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/humanoid_vr.dm index f21bd3b164c7..24322713677a 100644 --- a/code/modules/mob/living/simple_mob/subtypes/humanoid/humanoid_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/humanoid_vr.dm @@ -3,9 +3,3 @@ /mob/living/simple_mob/humanoid/pirate/ranged ai_holder_type = /datum/ai_holder/polaris/simple_mob/ranged - -/mob/living/simple_mob/humanoid/russian - ai_holder_type = /datum/ai_holder/polaris/simple_mob/melee - -/mob/living/simple_mob/humanoid/russian/ranged - ai_holder_type = /datum/ai_holder/polaris/simple_mob/ranged diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs.dm index 344444ff8224..d4e4f8e79968 100644 --- a/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs.dm +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs.dm @@ -54,7 +54,7 @@ icon_gib = "syndicate_gib" catalogue_data = list(/datum/category_item/catalogue/fauna/mercenary/human) - faction = "syndicate" + iff_factions = MOB_IFF_FACTION_MERCENARY movement_cooldown = 2 status_flags = 0 @@ -95,7 +95,7 @@ var/mob_count = 0 // Are there enough mobs to consider grenading? var/turf/T = get_turf(A) for(var/mob/M in range(T, 2)) - if(M.faction == faction) // Don't grenade our friends + if(shares_iff_faction(M)) return FALSE if(M in oview(src, special_attack_max_range)) // And lets check if we can actually see at least two people before we throw a grenade if(!M.stat) // Dead things don't warrant a grenade @@ -607,7 +607,7 @@ icon_living = "voxpirate" icon_dead = "voxpirate_dead" - faction = "voxpirate" + iff_factions = MOB_IFF_FACTION_PIRATE movement_cooldown = 4 status_flags = 0 diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/pirates.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/pirates.dm index 640560545917..2a8d48e8be01 100644 --- a/code/modules/mob/living/simple_mob/subtypes/humanoid/pirates.dm +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/pirates.dm @@ -25,7 +25,7 @@ icon_dead = "piratemelee_dead" catalogue_data = list(/datum/category_item/catalogue/fauna/pirate) - faction = "pirate" + iff_factions = MOB_IFF_FACTION_PIRATE response_help = "pushes" response_disarm = "shoves" diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/possessed.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/possessed.dm index c3e1564fca60..e539869578ab 100644 --- a/code/modules/mob/living/simple_mob/subtypes/humanoid/possessed.dm +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/possessed.dm @@ -8,7 +8,7 @@ icon = 'icons/mob/possessed.dmi' icon_state = "eva-rig" - faction = "Possessed" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP movement_cooldown = 10 health = 200 diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/russian.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/russian.dm deleted file mode 100644 index 7de7e01468eb..000000000000 --- a/code/modules/mob/living/simple_mob/subtypes/humanoid/russian.dm +++ /dev/null @@ -1,48 +0,0 @@ -/datum/category_item/catalogue/fauna/russian - name = "Russians" - desc = "After the Human Diaspora, but before the Final War, many \ - nations hosted their own colonial efforts out into the stars. Although \ - most human settlers live in culturally diverse environments, some of \ - those habitats which trace their lineage back to the Diaspora have remained \ - culturally homogenous. Sometimes xenophobic, and sometimes simply \ - nationalistic, these cultures function just as well as their counterparts. \ - Many of these communities draw their ancestry directly back to Old Russia. \ - Due to this, ethnic and genetic Russians remain heavily represented on the \ - Galactic stage." - value = CATALOGUER_REWARD_TRIVIAL - -/mob/living/simple_mob/humanoid/russian - name = "russian" - desc = "For the Motherland!" - tt_desc = "E Homo sapiens" - icon_state = "russianmelee" - icon_living = "russianmelee" - icon_dead = "russianmelee_dead" - icon_gib = "syndicate_gib" - catalogue_data = list(/datum/category_item/catalogue/fauna/russian) - - faction = "russian" - - response_help = "pokes" - response_disarm = "shoves" - response_harm = "hits" - - harm_intent_damage = 5 - legacy_melee_damage_lower = 15 - legacy_melee_damage_upper = 15 - attacktext = list("punched") - - loot_list = list(/obj/item/material/knife = 100) - - corpse = /obj/spawner/corpse/russian - -/mob/living/simple_mob/humanoid/russian/ranged - icon_state = "russianranged" - icon_living = "russianranged" - - projectiletype = /obj/projectile/bullet - projectilesound = 'sound/weapons/Gunshot4.ogg' - - loot_list = list(/obj/item/gun/ballistic/revolver/mateba = 100) - - corpse = /obj/spawner/corpse/russian/ranged diff --git a/code/modules/mob/living/simple_mob/subtypes/lavaland/goliath.dm b/code/modules/mob/living/simple_mob/subtypes/lavaland/goliath.dm index c9cc8d269a2b..ff11f5cdb894 100644 --- a/code/modules/mob/living/simple_mob/subtypes/lavaland/goliath.dm +++ b/code/modules/mob/living/simple_mob/subtypes/lavaland/goliath.dm @@ -74,7 +74,8 @@ hide_amount = 10 exotic_amount = 10 - faction = "lavaland" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP + speak_emote = list("bellows") say_list_type = /datum/say_list/goliath ai_holder_type = /datum/ai_holder/polaris/simple_mob/melee/goliath diff --git a/code/modules/mob/living/simple_mob/subtypes/lavaland/gutshank.dm b/code/modules/mob/living/simple_mob/subtypes/lavaland/gutshank.dm index 6efce8ef4378..efea68a50c1c 100644 --- a/code/modules/mob/living/simple_mob/subtypes/lavaland/gutshank.dm +++ b/code/modules/mob/living/simple_mob/subtypes/lavaland/gutshank.dm @@ -66,7 +66,8 @@ bone_type = /obj/item/stack/material/chitin bone_amount = 5 - faction = "lavaland" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP + speak_emote = list("chatters") say_list_type = /datum/say_list/gutshank ai_holder_type = /datum/ai_holder/polaris/simple_mob/melee @@ -187,7 +188,8 @@ bone_type = /obj/item/stack/material/chitin bone_amount = 5 - faction = "lavaland" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP + speak_emote = list("chatters") say_list_type = /datum/say_list/gutshank //I changed the ai_holder from simple/melee to retaliate/coop because when riding a Shank, it would override user inputs to charge non-faction mobs. Which is annoying. @@ -219,8 +221,9 @@ /mob/living/simple_mob/animal/shank/proc/RenameMount() var/mob/M = usr - if(!M.mind) return 0 - if(!M.faction == src.faction) + if(!M.mind) + return 0 + if(!shares_iff_faction(M)) to_chat(M, "You don't feel familiar enough with this beast to name it.") return 0 diff --git a/code/modules/mob/living/simple_mob/subtypes/lavaland/stormdrifter.dm b/code/modules/mob/living/simple_mob/subtypes/lavaland/stormdrifter.dm index 3e7d894f96e7..7538a59ae6af 100644 --- a/code/modules/mob/living/simple_mob/subtypes/lavaland/stormdrifter.dm +++ b/code/modules/mob/living/simple_mob/subtypes/lavaland/stormdrifter.dm @@ -67,7 +67,8 @@ bone_amount = 0 exotic_amount = 1 - faction = "lavaland" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP + speak_emote = list("rumbles") say_list_type = /datum/say_list/stormdrifter ai_holder_type = /datum/ai_holder/polaris/simple_mob/stormdrifter @@ -172,8 +173,9 @@ /mob/living/simple_mob/animal/stormdrifter/bull/proc/RenameMount() var/mob/M = usr - if(!M.mind) return 0 - if(!M.faction == src.faction) + if(!M.mind) + return 0 + if(!shares_iff_faction(M)) to_chat(M, "You don't feel familiar enough with this beast to name it.") return 0 diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/combat_drone.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/combat_drone.dm index 1b69c4182085..ccc4a81b2d33 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/combat_drone.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/combat_drone.dm @@ -39,7 +39,7 @@ icon_dead = "drone_dead" has_eye_glow = TRUE - faction = "malf_drone" + iff_factions = MOB_IFF_FACTION_MERCENARY maxHealth = 50 // Shield has 150 for total of 200. health = 50 diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/corrupt_maint_drone_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/corrupt_maint_drone_vr.dm index 289d3f1f124e..782fa602fdd9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/corrupt_maint_drone_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/corrupt_maint_drone_vr.dm @@ -26,7 +26,7 @@ icon_living = "corrupt-repairbot" hovering = FALSE // Can trigger landmines. - faction = "hivebot" + iff_factions = MOB_IFF_FACTION_HIVEBOT maxHealth = 25 health = 25 movement_cooldown = 0 diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/cyber_horror/cyber_horror.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/cyber_horror/cyber_horror.dm index 1f2841dc4cd9..ffc0042e0b93 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/cyber_horror/cyber_horror.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/cyber_horror/cyber_horror.dm @@ -22,7 +22,7 @@ icon_gib = "cyber_horror_dead" catalogue_data = list(/datum/category_item/catalogue/fauna/cyberhorror) - faction = "synthtide" + iff_factions = MOB_IFF_FACTION_MUTANT ai_holder_type = /datum/ai_holder/polaris/simple_mob/melee/evasive @@ -462,7 +462,7 @@ var/mob_count = 0 // Are there enough mobs? var/turf/T = get_turf(A) for(var/mob/M in range(T, 2)) - if(M.faction == faction) // Don't grenade our friends + if(shares_iff_faction(M)) return FALSE if(M in oview(src, special_attack_max_range)) if(!M.stat) diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/disbot_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/disbot_vr.dm index 5b2946eed9f5..8ed30cebead3 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/disbot_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/disbot_vr.dm @@ -35,7 +35,9 @@ response_help = "pets the" response_disarm = "gently pushes aside the" response_harm = "hits the" - faction = "vagrant" + + iff_factions = MOB_IFF_FACTION_SWARMER + harm_intent_damage = 3 legacy_melee_damage_lower = 6 legacy_melee_damage_upper = 9 diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/golem.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/golem.dm index 1ae4a5474156..d13630487081 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/golem.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/golem.dm @@ -20,7 +20,7 @@ maxHealth = 300 catalogue_data = list(/datum/category_item/catalogue/technology/drone/technomancer_golem) - faction = "golem" + iff_factions = MOB_IFF_FACTION_NEUTRAL // bad faction to use but whatever response_help = "pets" response_disarm = "pushes away" diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/enigma_hivebot.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/enigma_hivebot.dm index a495df25563b..3b4b35fd9261 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/enigma_hivebot.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/enigma_hivebot.dm @@ -2,6 +2,9 @@ // Enigma hivebots are usually significantly tougher than baseline Hivebots, and are modified with salvaged abductor parts. // These Hivebots are often meant for Event purposes, and carry a different faction than baseline Hivebots. Use these carefully! +/mob/living/simple_mob/mechanical/hivebot/enigma + iff_factions = MOB_IFF_FACTION_HIVEBOT + // Code Stuff /mob/living/simple_mob/mechanical/hivebot/enigma/death() ..() @@ -85,7 +88,6 @@ movement_cooldown = 4 icon_scale_x = 1.4 icon_scale_y = 1.4 - faction = "enigma" attack_sound = 'sound/enigma/enigma_hit2.ogg' movement_sound = 'sound/enigma/enigma_move2.ogg' ai_holder_type = /datum/ai_holder/polaris/simple_mob/hivebot @@ -126,7 +128,6 @@ legacy_melee_damage_upper = 20 base_attack_cooldown = 6 movement_cooldown = 1 - faction = "enigma" attack_sound = 'sound/enigma/enigma_hit.ogg' movement_sound = 'sound/enigma/enigma_move.ogg' ai_holder_type = /datum/ai_holder/polaris/simple_mob/hivebot @@ -156,7 +157,6 @@ movement_cooldown = 5 base_pixel_x = 1.5 base_pixel_y = 1.5 - faction = "enigma" attack_sound = 'sound/weapons/slash.ogg' movement_sound = 'sound/enigma/enigma_move.ogg' ai_holder_type = /datum/ai_holder/polaris/simple_mob/event @@ -182,7 +182,6 @@ legacy_melee_damage_lower = 10 legacy_melee_damage_upper = 10 movement_cooldown = 2 - faction = "enigma" attack_sound = 'sound/items/drill_hit.ogg' movement_sound = 'sound/enigma/enigma_move.ogg' ai_holder_type = /datum/ai_holder/polaris/simple_mob/event @@ -213,7 +212,6 @@ legacy_melee_damage_lower = 0 legacy_melee_damage_upper = 0 movement_cooldown = 6 - faction = "enigma" movement_sound = 'sound/enigma/enigma_move.ogg' ai_holder_type = /datum/ai_holder/polaris/simple_mob/ranged/kiting projectiletype = /obj/projectile/beam/cyan/hivebot @@ -253,7 +251,6 @@ legacy_melee_damage_lower = 20 legacy_melee_damage_upper = 20 movement_cooldown = 6 - faction = "enigma" attack_sound = 'sound/enigma/enigma_hit2.ogg' movement_sound = 'sound/enigma/enigma_move2.ogg' icon_scale_x = 1.1 @@ -301,7 +298,6 @@ legacy_melee_damage_lower = 5 legacy_melee_damage_upper = 5 movement_cooldown = 2 - faction = "enigma" movement_sound = 'sound/enigma/enigma_move.ogg' ai_holder_type = /datum/ai_holder/polaris/simple_mob/hivebot projectiletype = /obj/projectile/beam/weak @@ -327,7 +323,6 @@ legacy_melee_damage_lower = 5 legacy_melee_damage_upper = 5 movement_cooldown = 4 - faction = "enigma" movement_sound = 'sound/enigma/enigma_move.ogg' ai_holder_type = /datum/ai_holder/polaris/simple_mob/ranged/sniper projectiletype = /obj/projectile/beam/xray @@ -353,7 +348,6 @@ legacy_melee_damage_lower = 0 legacy_melee_damage_upper = 0 movement_cooldown = 2 - faction = "enigma" movement_sound = 'sound/enigma/enigma_move.ogg' ai_holder_type = /datum/ai_holder/polaris/simple_mob/ranged/kiting projectiletype = /obj/projectile/ion @@ -370,7 +364,6 @@ attack_sound = 'sound/enigma/enigma_hit2.ogg' icon_state = "mimir" icon_living = "mimir" - faction = "enigma" icon_scale_x = 1.3 icon_scale_y = 1.3 movement_cooldown = 3 diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot.dm index 5f5f37fc1c0f..f16bc3c8923f 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot.dm @@ -29,7 +29,7 @@ icon_living = "basic" catalogue_data = list(/datum/category_item/catalogue/technology/drone/hivebot/basic) - faction = "hivebot" + iff_factions = MOB_IFF_FACTION_HIVEBOT maxHealth = 3 LASERS_TO_KILL health = 3 LASERS_TO_KILL diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support.dm index c6a5eb97c478..55356b748a8c 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support.dm @@ -54,7 +54,7 @@ for(var/mob/living/L in hearers(7, src)) if(!L.ai_holder) continue - if(L.faction != src.faction) + if(!L.shares_iff_faction(src)) continue var/datum/ai_holder/polaris/AI = L.ai_holder AI.set_follow(src) diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/mecha.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/mecha.dm index 3ec87c9c34f2..560154dc30d1 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/mecha.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/mecha.dm @@ -7,7 +7,8 @@ desc = "A big stompy mech!" icon = 'icons/mecha/mecha.dmi' - faction = "syndicate" + iff_factions = MOB_IFF_FACTION_MERCENARY + movement_cooldown = 5 movement_sound = "mechstep" // This gets fed into playsound(), which can also take strings as a 'group' of sound files. turn_sound = 'sound/mecha/mechturn.ogg' @@ -70,7 +71,7 @@ // 'Eject' our pilot, if one exists. if(pilot_type) var/mob/living/L = new pilot_type(loc) - L.faction = src.faction + L.copy_iff_factions(src) new wreckage(loc) // Leave some wreckage. diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/micro.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/micro.dm index e1db2ddcf482..2c0974106082 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/micro.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/micro.dm @@ -19,7 +19,8 @@ icon = 'icons/mecha/micro.dmi' icon_state = "whisker" wreckage = /obj/structure/loot_pile/mecha/mouse_tank - faction = "mouse_army" + + iff_factions = MOB_IFF_FACTION_FARM_PEST maxHealth = 150 armor_legacy_mob = list( diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/ripley.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/ripley.dm index 5a39727e5990..af36e6f16cfd 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/ripley.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/ripley.dm @@ -81,7 +81,7 @@ desc = "A Ripley modified by pirates. Sports additional riveted armor plating and a jury rigged machine gun in addition to its hull piercing drill." catalogue_data = list(/datum/category_item/catalogue/technology/ripley) icon_state = "pirate" - faction = "pirate" + iff_factions = MOB_IFF_FACTION_PIRATE wreckage = /obj/structure/loot_pile/mecha/ripley/pirate maxHealth = 250 diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mechanical.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mechanical.dm index 6dff71602413..2ff147313e01 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mechanical.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mechanical.dm @@ -22,7 +22,7 @@ return TRUE /mob/living/simple_mob/mechanical/speech_bubble_appearance() - return faction != "neutral" ? "synthetic_evil" : "machine" + return !has_iff_faction(MOB_IFF_FACTION_NEUTRAL) ? "synthetic_evil" : "machine" // Fix for Virgo 2's Surface /mob/living/simple_mob/mechanical diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/viscerator.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/viscerator.dm index e813fda8edb6..1aaa2b0cfae5 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/viscerator.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/viscerator.dm @@ -27,7 +27,7 @@ icon_living = "viscerator_attack" hovering = TRUE // Won't trigger landmines. - faction = "syndicate" + iff_factions = MOB_IFF_FACTION_MERCENARY maxHealth = 15 health = 15 movement_cooldown = 0 diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm index 0d8dfd044783..00646d8cf5d5 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm @@ -40,7 +40,7 @@ // For PoIs. /mob/living/simple_mob/mechanical/ward/monitor/syndicate - faction = "syndicate" + iff_factions = MOB_IFF_FACTION_MERCENARY /mob/living/simple_mob/mechanical/ward/monitor/crew icon_state = "ward-nt" diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/ward.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/ward.dm index 4d5f2614f1b1..faed7952ab98 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/ward.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/ward.dm @@ -13,7 +13,8 @@ response_help = "pets" response_disarm = "swats away" response_harm = "punches" - faction = "wards" // Needed as most human mobs are in neutral faction. The owner is generally except from any ward hostility regardless. + + iff_factions = MOB_IFF_FACTION_HOSTILE // Needed as most human mobs are in neutral faction. The owner is generally except from any ward hostility regardless. maxHealth = 15 health = 15 diff --git a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/_construct.dm b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/_construct.dm index 03085a303469..b191aaf8ba44 100644 --- a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/_construct.dm +++ b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/_construct.dm @@ -81,7 +81,7 @@ "rad" = 100) can_be_antagged = TRUE - faction = "cult" + iff_factions = MOB_IFF_FACTION_SANGUINE_CULT supernatural = TRUE diff --git a/code/modules/mob/living/simple_mob/subtypes/occult/creature.dm b/code/modules/mob/living/simple_mob/subtypes/occult/creature.dm index 31c898afc523..39252ebffce2 100644 --- a/code/modules/mob/living/simple_mob/subtypes/occult/creature.dm +++ b/code/modules/mob/living/simple_mob/subtypes/occult/creature.dm @@ -15,7 +15,7 @@ mob_class = MOB_CLASS_ABERRATION - faction = "creature" + iff_factions = MOB_IFF_FACTION_MUTANT maxHealth = 40 health = 40 @@ -61,7 +61,7 @@ /mob/living/simple_mob/creature/cult mob_class = MOB_CLASS_DEMONIC - faction = "cult" + iff_factions = MOB_IFF_FACTION_SANGUINE_CULT min_oxy = 0 max_oxy = 0 diff --git a/code/modules/mob/living/simple_mob/subtypes/occult/faithless.dm b/code/modules/mob/living/simple_mob/subtypes/occult/faithless.dm index 21bd5de9e9e6..c48e9849195d 100644 --- a/code/modules/mob/living/simple_mob/subtypes/occult/faithless.dm +++ b/code/modules/mob/living/simple_mob/subtypes/occult/faithless.dm @@ -12,7 +12,7 @@ icon_dead = "faithless_dead" catalogue_data = list(/datum/category_item/catalogue/fauna/horror) - faction = "faithless" + iff_factions = MOB_IFF_FACTION_MUTANT mob_class = MOB_CLASS_DEMONIC @@ -74,7 +74,7 @@ // Cult Variant /mob/living/simple_mob/faithless/cult - faction = "cult" + iff_factions = MOB_IFF_FACTION_SANGUINE_CULT supernatural = TRUE /mob/living/simple_mob/faithless/cult/cultify() diff --git a/code/modules/mob/living/simple_mob/subtypes/occult/living_statue.dm b/code/modules/mob/living/simple_mob/subtypes/occult/living_statue.dm index 55c7f5b6b9a3..52506c83b6b3 100644 --- a/code/modules/mob/living/simple_mob/subtypes/occult/living_statue.dm +++ b/code/modules/mob/living/simple_mob/subtypes/occult/living_statue.dm @@ -7,7 +7,7 @@ icon_state = "human_male" gender = NEUTER - faction = list("statue") + iff_factions = MOB_IFF_FACTION_STATUE mob_class = MOB_CLASS_ABERRATION diff --git a/code/modules/mob/living/simple_mob/subtypes/plant/tomato.dm b/code/modules/mob/living/simple_mob/subtypes/plant/tomato.dm index c90f7124de0a..9501930ae69b 100644 --- a/code/modules/mob/living/simple_mob/subtypes/plant/tomato.dm +++ b/code/modules/mob/living/simple_mob/subtypes/plant/tomato.dm @@ -15,7 +15,7 @@ mob_class = MOB_CLASS_PLANT - faction = "plants" + iff_factions = MOB_IFF_FACTION_PLANT maxHealth = 15 health = 15 poison_resist = 1.0 diff --git a/code/modules/mob/living/simple_mob/subtypes/plant/tree.dm b/code/modules/mob/living/simple_mob/subtypes/plant/tree.dm index d9bfb0a66cec..c68277f1d10e 100644 --- a/code/modules/mob/living/simple_mob/subtypes/plant/tree.dm +++ b/code/modules/mob/living/simple_mob/subtypes/plant/tree.dm @@ -18,7 +18,11 @@ mob_class = MOB_CLASS_PLANT - faction = "plants" + iff_factions = list( + MOB_IFF_FACTION_BIND_TO_MAP, + MOB_IFF_FACTION_PLANT, + ) + maxHealth = 250 health = 250 poison_resist = 1.0 diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/slime.dm b/code/modules/mob/living/simple_mob/subtypes/slime/slime.dm index 228f3d154988..2218f6ab57f4 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/slime.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/slime.dm @@ -24,7 +24,7 @@ gender = NEUTER catalogue_data = list(/datum/category_item/catalogue/fauna/slime) - faction = "slime" // Note that slimes are hostile to other slimes of different color regardless of faction (unless Unified). + iff_factions = MOB_IFF_FACTION_SLIME // Note that slimes are hostile to other slimes of different color regardless of faction (unless Unified). maxHealth = 150 movement_cooldown = 0 pass_flags = ATOM_PASS_TABLE diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm index 9bbc247c3f5c..be5220876445 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm @@ -175,7 +175,7 @@ var/datum/ai_holder/polaris/simple_mob/xenobio_slime/AI = ai_holder AI.pacify() - faction = "neutral" + set_iff_factions(MOB_IFF_FACTION_NEUTRAL) // If for whatever reason the mob AI (or player) decides to try to attack something anyways. legacy_melee_damage_upper = 0 @@ -183,7 +183,6 @@ update_mood() - // These are verbs so that player slimes can evolve/split. /mob/living/simple_mob/slime/xenobio/verb/evolve() set category = "Slime" @@ -279,7 +278,7 @@ if(untamable_inherit) baby.untamable = untamable baby.untamable_inherit = untamable_inherit - baby.faction = faction + baby.copy_iff_factions(src) baby.friends = friends.Copy() if(no_step != 1) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/bee.dm b/code/modules/mob/living/simple_mob/subtypes/vore/bee.dm index 28d0fb60f015..106cc664ccc5 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/bee.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/bee.dm @@ -44,7 +44,7 @@ max_n2 = 0 minbodytemp = 0 - faction = "bee" + iff_factions = MOB_IFF_FACTION_FARM_ANIMAL var/poison_type = "spidertoxin" // The reagent that gets injected when it attacks, can be changed to different toxin. var/poison_chance = 10 // Chance for injection to occur. diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm b/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm index 0313e5be44b2..406dc242ca98 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm @@ -22,7 +22,7 @@ icon = 'icons/mob/vore64x32.dmi' has_eye_glow = TRUE - faction = "hivebot" + iff_factions = MOB_IFF_FACTION_HIVEBOT maxHealth = 200 health = 200 diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm b/code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm index 60bc6d985d26..5f90d6cc94ae 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm @@ -17,7 +17,7 @@ attacktext = list("mauled") - faction = "deathclaw" + iff_factions = MOB_IFF_FACTION_MUTANT maxHealth = 200 health = 200 diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon.dm b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon.dm index ff6d8c6ae4b1..ffdace5c0602 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon.dm @@ -11,9 +11,10 @@ icon_rest = "boxfox_rest" icon = 'icons/mob/demon_vr.dmi' - faction = "demon" - maxHealth = 30 - health = 30 + iff_factions = MOB_IFF_FACTION_SANGUINE_CULT + + maxHealth = 300 + health = 300 movement_cooldown = 0 vision_innate = /datum/vision/baseline/demon @@ -33,8 +34,8 @@ response_disarm = "pushes" response_harm = "hits" - legacy_melee_damage_lower = 3 - legacy_melee_damage_upper = 1 + legacy_melee_damage_lower = 10 + legacy_melee_damage_upper = 20 attacktext = list("clawed") diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/dino.dm b/code/modules/mob/living/simple_mob/subtypes/vore/dino.dm index 3db981881bd6..3483b9ed344a 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/dino.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/dino.dm @@ -12,6 +12,7 @@ randomized = TRUE ai_holder_type = /datum/ai_holder/polaris/simple_mob/melee + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP // By default, this is what most vore mobs are capable of. response_help = "pets" @@ -45,4 +46,3 @@ swallowTime = 1 SECOND // Hungry little bastards. /mob/living/simple_mob/vore/aggressive/dino/virgo3b - faction = "virgo3b" diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/dragon.dm b/code/modules/mob/living/simple_mob/subtypes/vore/dragon.dm index 8581053f9ef5..6f282cb1e882 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/dragon.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/dragon.dm @@ -7,7 +7,8 @@ icon_state = "reddragon" icon = 'icons/mob/vore64x64.dmi' - faction = "dragon" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP + maxHealth = 500 // Boss health = 500 randomized = TRUE @@ -42,7 +43,6 @@ /mob/living/simple_mob/vore/aggressive/dragon/virgo3b maxHealth = 200 health = 200 - faction = "virgo3b" /datum/say_list/dragonboss say_got_target = list("roars and snaps it jaws!") diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/fennec.dm b/code/modules/mob/living/simple_mob/subtypes/vore/fennec.dm index e4fdab4758be..d0fb4cf782dd 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/fennec.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/fennec.dm @@ -17,7 +17,8 @@ icon_rest = "fennec_rest" icon = 'icons/mob/vore.dmi' - faction = "fennec" + iff_factions = MOB_IFF_FACTION_FARM_NEUTRAL + maxHealth = 30 health = 30 randomized = TRUE diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/fennix.dm b/code/modules/mob/living/simple_mob/subtypes/vore/fennix.dm index 6273c8adc28b..8a3bbd6545a7 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/fennix.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/fennix.dm @@ -8,7 +8,8 @@ icon_dead = "fennix_dead" icon = 'icons/mob/vore.dmi' - faction = "fennec" // Will protec other fenfens + iff_factions = MOB_IFF_FACTION_FARM_NEUTRAL + maxHealth = 60 health = 60 diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/horse.dm b/code/modules/mob/living/simple_mob/subtypes/vore/horse.dm index 99a1e2098356..65e1f008a2bf 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/horse.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/horse.dm @@ -19,7 +19,8 @@ icon_dead = "horse-dead" icon = 'icons/mob/animal.dmi' - faction = "horse" + iff_factions = MOB_IFF_FACTION_FARM_ANIMAL + maxHealth = 60 health = 60 randomized = TRUE diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/jelly.dm b/code/modules/mob/living/simple_mob/subtypes/vore/jelly.dm index 5f454b39b521..84be80f16999 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/jelly.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/jelly.dm @@ -16,7 +16,6 @@ icon_state = "jelly" icon = 'icons/mob/vore.dmi' - faction = "virgo2" maxHealth = 50 health = 50 randomized = TRUE diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/mimic.dm b/code/modules/mob/living/simple_mob/subtypes/vore/mimic.dm index d605984192c9..c6739e6c7143 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/mimic.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/mimic.dm @@ -63,7 +63,7 @@ icon_living = "mimicopen" icon = 'icons/mob/animal.dmi' - faction = "mimic" + iff_factions = MOB_IFF_FACTION_CHIMERIC maxHealth = 125 health = 125 @@ -351,8 +351,6 @@ icon_state = "wmimicopen" icon_living = "wmimicopen" - faction = "mimic" - maxHealth = 100 health = 100 movement_cooldown = 5 @@ -395,8 +393,6 @@ icon_state = "tmimicopen" icon_living = "tmimicopen" - faction = "mimic" - maxHealth = 125 health = 125 movement_cooldown = 7 @@ -436,8 +432,6 @@ icon_state = "pmimicopen" icon_living = "pmimicopen" - faction = "mimic" - maxHealth = 150 health = 150 movement_cooldown = 7 diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/otie.dm b/code/modules/mob/living/simple_mob/subtypes/vore/otie.dm index 7d28db4ad0e5..29ea3126cd4e 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/otie.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/otie.dm @@ -15,7 +15,6 @@ icon_living = "otie" icon_dead = "otie-dead" icon_rest = "otie_rest" - faction = "otie" maxHealth = 150 health = 150 randomized = TRUE @@ -53,7 +52,6 @@ icon_living = "photie" icon_dead = "photie-dead" icon_rest = "photie_rest" - faction = "virgo3b" tame_chance = 5 // Only a 1 in 20 chance of success. It's feral. What do you expect? // Lazy way of making sure this otie survives outside. min_oxy = 0 @@ -74,7 +72,6 @@ icon_living = "hotie" icon_dead = "hotie-dead" icon_rest = "hotie_rest" - faction = "cult" tame_chance = 20 // Lazy way of making sure this otie survives outside. min_oxy = 0 @@ -91,13 +88,13 @@ /mob/living/simple_mob/otie/red/friendly //gets the pet2tame feature and doesn't kill you right away name = "red otie" desc = "Seems this ominous looking longdog has been infused with wicked infernal forces. This one seems rather peaceful though." - faction = "neutral" + iff_factions = MOB_IFF_FACTION_NEUTRAL tamed = 1 /mob/living/simple_mob/otie/friendly //gets the pet2tame feature and doesn't kill you right away name = "otie" desc = "The classic bioengineered longdog. This one might even tolerate you!" - faction = "neutral" + iff_factions = MOB_IFF_FACTION_NEUTRAL tamed = 1 /mob/living/simple_mob/otie/cotie //same as above but has a little collar :v @@ -106,7 +103,7 @@ icon_state = "cotie" icon_living = "cotie" icon_rest = "cotie_rest" - faction = "neutral" + iff_factions = MOB_IFF_FACTION_NEUTRAL tamed = 1 /mob/living/simple_mob/otie/cotie/phoron //friendly phoron pup with collar @@ -134,12 +131,11 @@ icon_living = "sotie" icon_rest = "sotie_rest" icon_dead = "sotie-dead" - faction = "neutral" + iff_factions = MOB_IFF_FACTION_NEUTRAL maxHealth = 200 //armored or something health = 200 tamed = 1 has_eye_glow = TRUE - loot_list = list(/obj/item/clothing/glasses/sunglasses/sechud,/obj/item/clothing/suit/armor/vest/alt) /mob/living/simple_mob/otie/security/phoron name = "mutated guard otie" @@ -194,7 +190,7 @@ AI.set_follow(friend) if(tamed != 1) tamed = 1 - faction = M.faction + copy_iff_factions(M) sleep(1 SECOND) if(INTENT_GRAB) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/panther.dm b/code/modules/mob/living/simple_mob/subtypes/vore/panther.dm index 196981a2436d..d2fdfb753142 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/panther.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/panther.dm @@ -17,7 +17,8 @@ icon_dead = "panther-dead" icon = 'icons/mob/vore64x64.dmi' - faction = "panther" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP + maxHealth = 200 health = 200 randomized = TRUE diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/rat.dm b/code/modules/mob/living/simple_mob/subtypes/vore/rat.dm index 2101732ed4d8..a7fafe8a6de9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/rat.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/rat.dm @@ -15,7 +15,7 @@ icon_living = "rous" icon_dead = "rous-dead" icon_rest = "rous_rest" - faction = "mouse" + iff_factions = MOB_IFF_FACTION_FARM_PEST icon = 'icons/mob/vore64x32.dmi' maxHealth = 150 diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/redpanda.dm b/code/modules/mob/living/simple_mob/subtypes/vore/redpanda.dm index 96d20383ec1a..02948915e3b6 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/redpanda.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/redpanda.dm @@ -18,7 +18,8 @@ icon_rest = "wah_rest" icon = 'icons/mob/vore.dmi' - faction = "redpanda" //stop naming stuff vaguely + iff_factions = MOB_IFF_FACTION_NEUTRAL + maxHealth = 30 health = 30 randomized = TRUE diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/snake.dm b/code/modules/mob/living/simple_mob/subtypes/vore/snake.dm index 1c08dc55d987..52c594aab27a 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/snake.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/snake.dm @@ -19,7 +19,8 @@ icon_state = "snake" icon = 'icons/mob/vore64x64.dmi' - faction = "snake" + iff_factions = MOB_IFF_FACTION_FARM_PEST + maxHealth = 200 health = 200 randomized = TRUE diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/solargrub.dm b/code/modules/mob/living/simple_mob/subtypes/vore/solargrub.dm index 4ec007004a1f..c5548a658e13 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/solargrub.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/solargrub.dm @@ -35,7 +35,8 @@ GLOBAL_LIST_EMPTY(solargrubs) //var/adult_forms = "/mob/living/simple_mob/vore/solarmoth" // CHOMPEDIT VAR that decides what mob the queen form is. ex /mob/living/simple_mob/subtypes/vore/solarmoth; CitRP: Without lunarmoth, quoted out for fun; // CHOMPEDIT End, Rykka waz here. *pawstamp* - faction = "grubs" + iff_factions = MOB_IFF_FACTION_GRUB + maxHealth = 50 //grubs can take a lot of harm health = 50 diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/solargrub_larva.dm b/code/modules/mob/living/simple_mob/subtypes/vore/solargrub_larva.dm index 16fa82ea6908..e2a65637aa01 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/solargrub_larva.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/solargrub_larva.dm @@ -19,7 +19,7 @@ var/global/list/grub_machine_overlays = list() meat_amount = 2 meat_type = /obj/item/reagent_containers/food/snacks/meat/grubmeat - faction = "grubs" + iff_factions = MOB_IFF_FACTION_GRUB response_help = "pats" response_disarm = "nudges" diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/solarmoth_ch.dm b/code/modules/mob/living/simple_mob/subtypes/vore/solarmoth.dm similarity index 99% rename from code/modules/mob/living/simple_mob/subtypes/vore/solarmoth_ch.dm rename to code/modules/mob/living/simple_mob/subtypes/vore/solarmoth.dm index 06cd05802403..0f07603df036 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/solarmoth_ch.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/solarmoth.dm @@ -30,7 +30,8 @@ var/emp_light = 7 var/emp_long = 10 - faction = "grubs" + iff_factions = MOB_IFF_FACTION_GRUB + maxHealth = 200 // Tanky fuckers. health = 200 // Tanky fuckers. diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/wolf.dm b/code/modules/mob/living/simple_mob/subtypes/vore/wolf.dm index f87e3aac491e..5eca2385fca7 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/wolf.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/wolf.dm @@ -34,8 +34,8 @@ // Adds Phoron Wolf /mob/living/simple_mob/animal/wolf/phoron + iff_factions = MOB_IFF_FACTION_BIND_TO_LEVEL - faction = "underdark" movement_cooldown = 0 harm_intent_damage = 5 diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/wolfgirl.dm b/code/modules/mob/living/simple_mob/subtypes/vore/wolfgirl.dm index fc2c1605e284..10ed8ab01633 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/wolfgirl.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/wolfgirl.dm @@ -8,7 +8,6 @@ icon_dead = "wolfgirl-dead" icon = 'icons/mob/vore.dmi' - faction = "wolfgirl" maxHealth = 30 health = 30 diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/zz_vore_overrides.dm b/code/modules/mob/living/simple_mob/subtypes/vore/zz_vore_overrides.dm index 9e74a4a7a9c7..4b1aa4a4c0d0 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/zz_vore_overrides.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/zz_vore_overrides.dm @@ -24,6 +24,3 @@ /mob/living/simple_mob/animal/space/carp/holographic /mob/living/simple_mob/animal/space/carp/holographic - -/mob/living/simple_mob/animal/passive/mouse - faction = "mouse" //Giving mice a faction so certain mobs can get along with them. diff --git a/code/modules/mob/mob-iff.dm b/code/modules/mob/mob-iff.dm new file mode 100644 index 000000000000..b4d9ec466f74 --- /dev/null +++ b/code/modules/mob/mob-iff.dm @@ -0,0 +1,117 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station developers. *// + +//* Basic mob-level IFF system. *// +//* *// +//* This is not on mob AI holder so non-AI holders can access it too. *// + +// todo: typelist them where possible + +//* Init *// + +/mob/proc/init_iff() + if(!iff_factions) + return + set_iff_factions(iff_factions) + +/** + * * descriptor can be a string or a list + * + * @return string faction + */ +/mob/proc/process_iff_faction(descriptor) + if(islist(descriptor)) + switch(descriptor[1]) + if(MOB_IFF_FACTION_BIND_TO_LEVEL) + // if get z returns null it works as an assoc lookup returning null + var/prepend = SSmapping.ordered_levels[get_z(src)]?.id || "unkw" + return "[prepend]-[descriptor[descriptor[1]]]" + if(MOB_IFF_FACTION_BIND_TO_MAP) + // if get z returns null it works as an assoc lookup returning null + var/datum/map_level/level = SSmapping.ordered_levels[get_z(src)] + var/prepend = level?.parent_map?.id || level?.id || "unkw" + return "bind-[prepend]-[descriptor[descriptor[1]]]" + switch(descriptor) + if(MOB_IFF_FACTION_BIND_TO_LEVEL) + // if get z returns null it works as an assoc lookup returning null + var/prepend = SSmapping.ordered_levels[get_z(src)]?.id || "unkw" + return "bind-[prepend]" + if(MOB_IFF_FACTION_BIND_TO_MAP) + // if get z returns null it works as an assoc lookup returning null + var/datum/map_level/level = SSmapping.ordered_levels[get_z(src)] + var/prepend = level?.parent_map?.id || level?.id || "unkw" + return "bind-[prepend]" + else + return descriptor + +//* Direct Operations *// + +/mob/proc/add_iff_faction(string) + if(islist(iff_factions)) + iff_factions |= string + else if(iff_factions) + iff_factions = list(iff_factions, string) + else + iff_factions = string + +/mob/proc/remove_iff_faction(string) + if(islist(iff_factions)) + iff_factions -= string + if(!length(iff_factions)) + iff_factions = null + else if(iff_factions == string) + iff_factions = null + +/mob/proc/clear_iff_factions() + iff_factions = null + +/mob/proc/set_iff_factions(new_factions) + if(islist(new_factions)) + for(var/i in 1 to length(new_factions)) + var/faction = new_factions[i] + if(islist(faction) || ispath(faction) || faction[1] == "!") + faction = process_iff_faction(faction) + new_factions[i] = faction + continue // nothing otherwise to do yet + else if(islist(new_factions) || ispath(new_factions) || new_factions[1] == "!") + new_factions = process_iff_faction(new_factions) + + iff_factions = new_factions + +/mob/proc/has_iff_faction(string) + return islist(iff_factions) ? (string in iff_factions) : (iff_factions == string) + +//* Generation / Resolution *// + +/** + * Gets, or generates an unique IFF faction for ourselves. + * + * * Behavior depends on DF_USE_TAG being in use for us. + */ +/mob/proc/unique_iff_faction() + var/unique_faction = "mob-[tag]" + if(!has_iff_faction(unique_faction)) + add_iff_faction(unique_faction) + return unique_faction + +//* Operations - With Other Mob *// + +/mob/proc/copy_iff_factions(mob/other) + iff_factions = islist(other.iff_factions)? other.iff_factions:Copy() : other.iff_factions + +/** + * Returns a truthy value if we share atleast one IFF faction string with another mob, falsy otherwise. + * + * * does not necessarily return TRUE or FALSE! + */ +/mob/proc/shares_iff_faction(mob/other) + if(islist(iff_factions)) + if(islist(other.iff_factions)) + return length(iff_factions & other.iff_factions) + else if(other.iff_factions) + return other.iff_factions in iff_factions + else if(iff_factions) + if(islist(other.iff_factions)) + return iff_factions in other.iff_factions + else if(other.iff_factions) + return iff_factions == other.iff_factions diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 6c4eccc05ae3..359eaaac1a18 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -47,6 +47,8 @@ initialize_actionspeed() // ssd overlay update_ssd_overlay() + // iff factions + init_iff() return ..() /mob/Destroy() diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 3c939862f217..5484c23990ae 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -117,6 +117,13 @@ /// our inventory datum, if any. var/datum/inventory/inventory + //* IFF *// + /// our IFF factions + /// + /// * Do not read directly, use [code/modules/mob/mob-iff.dm] helpers. + /// * can be set to a string, or a list of strings. + var/iff_factions = MOB_IFF_FACTION_NEUTRAL + //! Size //! todo kill this with fire it should just be part of icon_scale_x/y. /// our size multiplier @@ -293,7 +300,7 @@ var/voice_name = "unidentifiable voice" ///Used for checking whether hostile simple animals will attack you, possibly more stuff later. - var/faction = "neutral" + var/_faction = "neutral" /// To prevent pAIs/mice/etc from getting antag in autotraitor and future auto- modes. Uses inheritance instead of a bunch of typechecks. // todo: what the fuck var/can_be_antagged = FALSE diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 47ea91b24636..2696567e57ef 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -500,7 +500,7 @@ var/list/intents = list(INTENT_HELP,INTENT_DISARM,INTENT_GRAB,INTENT_HARM) if(!istype(src.ai_holder, /datum/ai_holder/polaris)) return SAFE_PERP var/datum/ai_holder/polaris/ai_holder = src.ai_holder - if(has_polaris_AI() && ai_holder.hostile && faction != "neutral") + if(has_polaris_AI() && ai_holder.hostile && !has_iff_faction(MOB_IFF_FACTION_NEUTRAL)) threatcount += 4 return threatcount diff --git a/code/modules/power/fission/engine.dm b/code/modules/power/fission/engine.dm index 07109bcc683a..8e64670dd636 100644 --- a/code/modules/power/fission/engine.dm +++ b/code/modules/power/fission/engine.dm @@ -479,7 +479,7 @@ I'm commenting this out until I have time to make this less stupid. // fuck this /obj/nuclear_mistake_spawner name = "the Underdark's revenge" - desc = "hardcoded piece of that that should never be seen PLEASE report this if you do" + desc = "hardcoded piece of trash that should never be seen PLEASE report this if you do" icon = 'icons/mob/screen1.dmi' icon_state = "x" invisibility = 101 @@ -536,7 +536,8 @@ I'm commenting this out until I have time to make this less stupid. my_mob.low_priority = TRUE if(faction) - my_mob.faction = faction + my_mob.clear_iff_factions() + my_mob.add_iff_faction(faction) if(atmos_comp) var/turf/T = get_turf(src) diff --git a/code/modules/rogueminer_vr/roguemines_mobs.dm b/code/modules/rogueminer_vr/roguemines_mobs.dm index 14389fb38efe..25ecd8c57a95 100644 --- a/code/modules/rogueminer_vr/roguemines_mobs.dm +++ b/code/modules/rogueminer_vr/roguemines_mobs.dm @@ -1,23 +1,23 @@ /mob/living/simple_mob/animal/space/bats/roguemines - faction = "roguemines" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP /mob/living/simple_mob/animal/space/carp/roguemines - faction = "roguemines" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP /mob/living/simple_mob/animal/space/goose/roguemines - faction = "roguemines" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP /mob/living/simple_mob/animal/wolf/space/roguemines - faction = "roguemines" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP /mob/living/simple_mob/animal/space/carp/large/roguemines - faction = "roguemines" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP /mob/living/simple_mob/animal/space/bear/roguemines - faction = "roguemines" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP /mob/living/simple_mob/vore/aggressive/corrupthound/space/roguemines - faction = "roguemines" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP /mob/living/simple_mob/animal/space/carp/large/huge/roguemines - faction = "roguemines" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP diff --git a/code/modules/rogueminer_vr/zonemaster.dm b/code/modules/rogueminer_vr/zonemaster.dm index 8bede43f5d87..aa9d20d5cc82 100644 --- a/code/modules/rogueminer_vr/zonemaster.dm +++ b/code/modules/rogueminer_vr/zonemaster.dm @@ -295,7 +295,7 @@ var/mobchoice = pickweight(rm_controller.mobs["tier[rm_controller.diffstep]"]) rm_controller.dbg("ZM(p): Picked [mobchoice] to spawn.") var/mob/living/newmob = new mobchoice(get_turf(SP)) - newmob.faction = "asteroid_belt" + newmob.set_iff_factions(MOB_IFF_FACTION_BIND_AUTO) spawned_mobs += newmob if(delay) sleep(delay) diff --git a/code/modules/species/protean/protean_blob.dm b/code/modules/species/protean/protean_blob.dm index cf3f64c32e14..165146f9288f 100644 --- a/code/modules/species/protean/protean_blob.dm +++ b/code/modules/species/protean/protean_blob.dm @@ -9,7 +9,8 @@ icon_rest = "rest" icon_dead = "puddle" - faction = "neutral" + iff_factions = MOB_IFF_FACTION_NEUTRAL + maxHealth = 250 health = 250 say_list_type = /datum/say_list/protean_blob diff --git a/code/modules/species/species.dm b/code/modules/species/species.dm index a675e88c3243..49f83c734ec3 100644 --- a/code/modules/species/species.dm +++ b/code/modules/species/species.dm @@ -84,6 +84,13 @@ /// do we have a species statpanel? var/species_statpanel = FALSE + //* IFF *// + + /// Inherent IFF factions + /// + /// * This isn't the best way to do this, but it works. + var/list/iff_factions_inherent + //? Icons /// Normal icon set. var/icobase = 'icons/mob/species/human/body.dmi' @@ -545,6 +552,9 @@ for(var/datum/ability/ability as anything in abilities) ability.associate(H) + for(var/faction in iff_factions_inherent) + H.add_iff_faction(faction) + /** * called when we are removed from a mob */ @@ -572,6 +582,9 @@ for(var/datum/ability/ability as anything in abilities) ability.disassociate(H) + for(var/faction in iff_factions_inherent) + H.remove_iff_faction(faction) + /datum/species/proc/sanitize_species_name(var/name) return sanitizeName(name, MAX_NAME_LEN) diff --git a/code/modules/species/xenomorphs/alien_species.dm b/code/modules/species/xenomorphs/alien_species.dm index eb33fdd386b0..e8eaf1b9b056 100644 --- a/code/modules/species/xenomorphs/alien_species.dm +++ b/code/modules/species/xenomorphs/alien_species.dm @@ -85,6 +85,10 @@ BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right/unseverable/xeno) ) + iff_factions_inherent = list( + MOB_IFF_FACTION_XENOMORPH, + ) + /datum/species/xenos/get_bodytype_legacy() return SPECIES_XENO diff --git a/code/modules/species/xenomorphs/xenomorphs.dm b/code/modules/species/xenomorphs/xenomorphs.dm index 2c44851e4fab..ce9a6e4f5481 100644 --- a/code/modules/species/xenomorphs/xenomorphs.dm +++ b/code/modules/species/xenomorphs/xenomorphs.dm @@ -10,22 +10,22 @@ /mob/living/carbon/human/xdrone species = /datum/species/xenos/drone h_style = "Bald" - faction = "xeno" + iff_factions = MOB_IFF_FACTION_XENOMORPH /mob/living/carbon/human/xsentinel species = /datum/species/xenos/sentinel h_style = "Bald" - faction = "xeno" + iff_factions = MOB_IFF_FACTION_XENOMORPH /mob/living/carbon/human/xhunter species = /datum/species/xenos/hunter h_style = "Bald" - faction = "xeno" + iff_factions = MOB_IFF_FACTION_XENOMORPH /mob/living/carbon/human/xqueen species = /datum/species/xenos/queen h_style = "Bald" - faction = "xeno" + iff_factions = MOB_IFF_FACTION_XENOMORPH // I feel like we should generalize/condense down all the various icon-rendering antag procs. /*---------------------------------------- diff --git a/code/modules/tension/tension.dm b/code/modules/tension/tension.dm index 8886f777f6de..7c4fd2bff7db 100644 --- a/code/modules/tension/tension.dm +++ b/code/modules/tension/tension.dm @@ -69,7 +69,7 @@ if(incapacitated(INCAPACITATION_DISABLED)) return 0 // Can't currently hurt you if it's stunned. - var/friendly = threatened.faction == faction + var/friendly = threatened.shares_iff_faction(src) var/threat = guess_threat_level(threatened) diff --git a/code/modules/turbolift/turbolift_map.dm b/code/modules/turbolift/turbolift_map.dm index 0f0dd99deecb..c2609f274039 100644 --- a/code/modules/turbolift/turbolift_map.dm +++ b/code/modules/turbolift/turbolift_map.dm @@ -162,7 +162,7 @@ return // Update path appropriately if needed. - var/swap_to = /turf/simulated/open + var/swap_to = /turf/baseturf_bottom if(cz == uz) // Elevator. if(wall_type && (tx == ux || ty == uy || tx == ex || ty == ey) && !(tx >= door_x1 && tx <= door_x2 && ty >= door_y1 && ty <= door_y2)) swap_to = wall_type diff --git a/code/modules/xenobio/items/slimepotions.dm b/code/modules/xenobio/items/slimepotions.dm index fcd47d828ed8..8e663c9f84e4 100644 --- a/code/modules/xenobio/items/slimepotions.dm +++ b/code/modules/xenobio/items/slimepotions.dm @@ -204,6 +204,7 @@ qdel(src) // Makes slimes not kill (most) humanoids but still fight spiders/carp/bears/etc. +// todo: this is janky as shit /obj/item/slimepotion/loyalty name = "slime loyalty agent" desc = "A potent chemical mix that makes an animal deeply loyal to the species of whoever applies this, and will attack threats to them." @@ -225,7 +226,7 @@ if(S.stat == DEAD) to_chat(user, SPAN_WARNING("The animal is dead!")) return - if(S.faction == user.faction) + if(S.shares_iff_faction(user)) to_chat(user, SPAN_WARNING("\The [S] is already loyal to your species!")) return if(!S.has_polaris_AI()) @@ -239,7 +240,7 @@ to_chat(user, SPAN_NOTICE("You feed \the [S] the agent. It will now try to murder things that want to murder you instead.")) to_chat(S, SPAN_NOTICE("\The [user] feeds you \the [src], and feel that the others will regard you as an outsider now.")) - S.faction = user.faction + S.copy_iff_factions() AI.lost_target() // So hostile things stop attacking people even if not hostile anymore. playsound(src, 'sound/effects/bubbles.ogg', 50, 1) qdel(src) diff --git a/maps/away_missions/140x140/listeningpost.dmm b/maps/away_missions/140x140/listeningpost.dmm index 1789fec7c818..b9abaf16d1bd 100644 --- a/maps/away_missions/140x140/listeningpost.dmm +++ b/maps/away_missions/140x140/listeningpost.dmm @@ -61,8 +61,7 @@ dir = 4 }, /mob/living/simple_mob/humanoid/merc{ - desc = "A weary looking syndicate operative."; - faction = "syndicate" + desc = "A weary looking syndicate operative." }, /turf/simulated/floor, /area/awaymission/listeningpost) diff --git a/maps/away_missions/140x140/zoo.dmm b/maps/away_missions/140x140/zoo.dmm index e87147a3208a..e69738ee3168 100644 --- a/maps/away_missions/140x140/zoo.dmm +++ b/maps/away_missions/140x140/zoo.dmm @@ -1539,7 +1539,7 @@ /area/awaymission/zoo/pirateship) "eB" = ( /mob/living/simple_mob/animal/space/alien{ - faction = "pirate" + iff_factions = "pirate" }, /turf/simulated/floor/plating, /area/awaymission/zoo/pirateship) @@ -2843,7 +2843,7 @@ "ik" = ( /mob/living/simple_mob/humanoid/merc/ranged{ desc = "He doesn't seem like he's looking for a fight."; - faction = "neutral"; + iff_factions = "neutral"; friendly = "hugs"; name = "Mercenary" }, @@ -3606,7 +3606,7 @@ /area/awaymission/zoo) "jT" = ( /mob/living/simple_mob/animal/passive/chicken{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass0" @@ -3620,7 +3620,7 @@ /area/awaymission/zoo) "jV" = ( /mob/living/simple_mob/animal/passive/dog/corgi/puppy{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass0" @@ -3847,7 +3847,7 @@ "kH" = ( /obj/structure/flora/ausbushes/ywflowers, /mob/living/simple_mob/animal/passive/bird/parrot/kea{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) @@ -3862,7 +3862,7 @@ "kK" = ( /obj/structure/flora/ausbushes/pointybush, /mob/living/simple_mob/animal/passive/bird/parrot/pink_cockatoo{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) @@ -3933,7 +3933,7 @@ /area/awaymission/zoo) "kV" = ( /mob/living/simple_mob/animal/passive/cat{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/wood, /area/awaymission/zoo) @@ -3981,7 +3981,7 @@ /area/awaymission/zoo) "le" = ( /mob/living/simple_mob/animal/passive/bird/parrot/white_cockatoo{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) @@ -3992,7 +3992,7 @@ "lg" = ( /obj/structure/flora/ausbushes/fullgrass, /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/grey{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) @@ -4003,14 +4003,14 @@ "li" = ( /obj/structure/flora/ausbushes/brflowers, /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/yellowish{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) "lj" = ( /obj/structure/flora/ausbushes/ppflowers, /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/bluegreen{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) @@ -4028,7 +4028,7 @@ /area/awaymission/zoo) "lm" = ( /mob/living/simple_mob/horse{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/wood, /area/awaymission/zoo) @@ -4167,14 +4167,14 @@ /area/awaymission/zoo) "lI" = ( /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) "lJ" = ( /obj/structure/flora/ausbushes/sunnybush, /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) @@ -4188,7 +4188,7 @@ /area/awaymission/zoo) "lM" = ( /mob/living/simple_mob/animal/passive/cow{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass0" @@ -4280,13 +4280,13 @@ /area/awaymission/zoo) "mb" = ( /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/yellowish{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) "mc" = ( /mob/living/simple_mob/animal/passive/bird/parrot/white_caique{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) @@ -4798,7 +4798,7 @@ /area/awaymission/zoo) "nt" = ( /mob/living/simple_mob/animal/passive/tindalos{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "fullgrass0" @@ -5073,7 +5073,7 @@ /area/awaymission/zoo) "of" = ( /mob/living/simple_mob/vore/aggressive/frog{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass{ icon_state = "grass2" @@ -5104,7 +5104,7 @@ /area/awaymission/zoo) "oj" = ( /mob/living/simple_mob/animal/passive/tindalos{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "fullgrass1" @@ -5173,7 +5173,7 @@ /area/awaymission/zoo) "ov" = ( /mob/living/simple_mob/animal/passive/dog/corgi{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/lino, /area/awaymission/zoo) @@ -5204,7 +5204,7 @@ "oA" = ( /obj/structure/flora/ausbushes/lavendergrass, /mob/living/simple_mob/animal/passive/cat{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass4" @@ -5212,7 +5212,7 @@ /area/awaymission/zoo) "oB" = ( /mob/living/simple_mob/vore/bee{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass{ icon_state = "hive"; @@ -5280,7 +5280,7 @@ /area/awaymission/zoo) "oL" = ( /mob/living/simple_mob/vore/aggressive/giant_snake{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass3" @@ -5336,7 +5336,7 @@ /area/awaymission/zoo) "oU" = ( /mob/living/simple_mob/animal/space/bats{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) @@ -5435,13 +5435,13 @@ /area/awaymission/zoo) "pg" = ( /mob/living/carbon/human/sharkm{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/water, /area/awaymission/zoo) "ph" = ( /mob/living/simple_mob/animal/passive/tindalos{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass2" @@ -5461,7 +5461,7 @@ /area/awaymission/zoo) "pk" = ( /mob/living/simple_mob/vore/aggressive/giant_snake{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass0" @@ -5475,7 +5475,7 @@ /area/awaymission/zoo) "pm" = ( /mob/living/simple_mob/animal/passive/cat{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "fullgrass3" @@ -5568,7 +5568,7 @@ /area/awaymission/zoo) "pB" = ( /mob/living/simple_mob/animal/passive/tindalos{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass0" @@ -5588,7 +5588,7 @@ /area/awaymission/zoo) "pE" = ( /mob/living/simple_mob/animal/passive/cat{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass2" @@ -6005,7 +6005,7 @@ /area/awaymission/zoo) "qJ" = ( /mob/living/carbon/human/sparram{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) @@ -6044,7 +6044,7 @@ /area/awaymission/zoo) "qR" = ( /mob/living/simple_mob/animal/passive/mouse{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/plating, /area/awaymission/zoo) @@ -6092,7 +6092,7 @@ /area/awaymission/zoo) "rb" = ( /mob/living/carbon/human/neaera{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass{ icon_state = "grass2" @@ -6129,7 +6129,7 @@ /area/awaymission/zoo) "rf" = ( /mob/living/simple_mob/animal/sif/diyaab{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/snow, /area/awaymission/zoo) @@ -6374,7 +6374,7 @@ /area/awaymission/zoo) "rL" = ( /mob/living/simple_mob/animal/space/alien{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert{ icon_state = "asteroidplating"; @@ -6434,7 +6434,7 @@ /area/awaymission/zoo) "rS" = ( /mob/living/simple_mob/animal/space/alien/drone{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert{ icon_state = "asteroidplating"; @@ -6475,7 +6475,7 @@ /area/awaymission/zoo) "rW" = ( /mob/living/simple_mob/animal/passive/penguin{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/snow, /area/awaymission/zoo) @@ -6499,7 +6499,7 @@ /area/awaymission/zoo) "rZ" = ( /mob/living/simple_mob/animal/passive/snake{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "fullgrass1" @@ -6515,7 +6515,7 @@ name = "rocky edge" }, /mob/living/simple_mob/animal/space/alien/drone{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert{ icon_state = "asteroidplating"; @@ -6535,28 +6535,28 @@ "sc" = ( /obj/effect/floor_decal/industrial/outline/yellow, /mob/living/simple_mob/vore/aggressive/mimic{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/tiled/neutral, /area/awaymission/zoo) "sd" = ( /obj/structure/flora/ausbushes/fullgrass, /mob/living/simple_mob/animal/passive/penguin{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/snow, /area/awaymission/zoo) "se" = ( /obj/effect/floor_decal/asteroid, /mob/living/simple_mob/animal/space/bear{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) "sf" = ( /obj/structure/flora/grass/both, /mob/living/simple_mob/animal/passive/penguin{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/snow, /area/awaymission/zoo) @@ -6584,7 +6584,7 @@ /area/awaymission/zoo) "sl" = ( /mob/living/simple_mob/animal/space/carp{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/space, /area/awaymission/zoo) @@ -6622,7 +6622,7 @@ name = "rocky edge" }, /mob/living/simple_mob/animal/space/alien/drone{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert{ icon_state = "asteroidplating"; @@ -6646,7 +6646,7 @@ "sq" = ( /obj/effect/spider/stickyweb, /mob/living/simple_mob/animal/giant_spider/hunter{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) @@ -6655,14 +6655,14 @@ icon_state = "cocoon_large2" }, /mob/living/simple_mob/animal/giant_spider/hunter{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) "ss" = ( /obj/effect/spider/stickyweb, /mob/living/simple_mob/animal/giant_spider{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) @@ -6744,7 +6744,7 @@ name = "rocky edge" }, /mob/living/simple_mob/animal/space/alien{ - faction = "zoo"; + iff_factions = "!bind-map"; name = "invisible alien hunter" }, /turf/simulated/floor/holofloor/desert{ @@ -6760,7 +6760,7 @@ icon_state = "cocoon3" }, /mob/living/simple_mob/animal/giant_spider/hunter{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) @@ -6769,7 +6769,7 @@ icon_state = "cobweb1" }, /mob/living/simple_mob/animal/giant_spider/hunter{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) @@ -6778,7 +6778,7 @@ icon_state = "cocoon2" }, /mob/living/simple_mob/animal/giant_spider/nurse{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) @@ -6787,7 +6787,7 @@ icon_state = "cobweb2" }, /mob/living/simple_mob/animal/giant_spider{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) @@ -6799,7 +6799,7 @@ icon_state = "cocoon_large1" }, /mob/living/simple_mob/animal/giant_spider{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) @@ -6812,19 +6812,19 @@ icon_state = "cocoon_large3" }, /mob/living/simple_mob/animal/giant_spider/hunter{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) "sI" = ( /mob/living/simple_mob/animal/giant_spider{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) "sJ" = ( /mob/living/simple_mob/animal/passive/bird/parrot/polly{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) @@ -6834,13 +6834,13 @@ /area/awaymission/zoo) "sL" = ( /mob/living/simple_mob/animal/space/bear{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) "sM" = ( /mob/living/simple_mob/animal/space/alien{ - faction = "zoo"; + iff_factions = "!bind-map"; name = "invisible alien hunter" }, /turf/simulated/floor/holofloor/desert{ @@ -6859,7 +6859,7 @@ /area/awaymission/zoo) "sP" = ( /mob/living/simple_mob/animal/passive/snake{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass4" @@ -6946,7 +6946,7 @@ "ta" = ( /obj/effect/floor_decal/asteroid, /mob/living/simple_mob/animal/space/alien{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert{ icon_state = "asteroidplating"; @@ -7020,7 +7020,7 @@ /area/awaymission/zoo) "ti" = ( /mob/living/simple_mob/animal/passive/yithian{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/wood{ icon_state = "diona" @@ -7046,7 +7046,7 @@ "tn" = ( /obj/effect/floor_decal/asteroid, /mob/living/carbon/human/stok{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) @@ -7064,7 +7064,7 @@ /area/awaymission/zoo) "tq" = ( /mob/living/carbon/human/farwa{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/snow, /area/awaymission/zoo) @@ -7074,7 +7074,7 @@ /area/awaymission/zoo) "ts" = ( /mob/living/carbon/human/stok{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) @@ -7103,7 +7103,7 @@ "tx" = ( /obj/structure/flora/ausbushes/sunnybush, /mob/living/simple_mob/animal/passive/yithian{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/wood{ icon_state = "diona" @@ -7123,7 +7123,7 @@ /area/awaymission/zoo) "tA" = ( /mob/living/simple_mob/creature{ - faction = "zoo"; + iff_factions = "!bind-map"; name = "redspace abomination" }, /turf/simulated/floor/cult, @@ -7220,7 +7220,7 @@ "tO" = ( /obj/effect/floor_decal/asteroid, /mob/living/simple_mob/vore/aggressive/dino{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) @@ -7308,7 +7308,7 @@ /area/awaymission/zoo) "tX" = ( /mob/living/simple_mob/vore/aggressive/dragon{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert{ icon_state = "asteroidplating"; @@ -7317,19 +7317,19 @@ /area/awaymission/zoo) "tY" = ( /mob/living/simple_mob/animal/passive/lizard{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) "tZ" = ( /mob/living/simple_mob/tomato{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) "ua" = ( /mob/living/simple_mob/animal/passive/fox{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/snow, /area/awaymission/zoo) @@ -7350,7 +7350,7 @@ "uc" = ( /obj/effect/floor_decal/asteroid, /mob/living/simple_mob/vore/aggressive/deathclaw{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) @@ -7370,7 +7370,7 @@ /area/awaymission/zoo) "ue" = ( /mob/living/simple_mob/animal/space/goose{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/outdoors/beach, /area/awaymission/zoo) @@ -7418,46 +7418,46 @@ /area/awaymission/zoo) "ui" = ( /mob/living/simple_mob/vore/aggressive/deathclaw{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) "uj" = ( /mob/living/simple_mob/slime/feral/dark_blue{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) "uk" = ( /obj/structure/flora/ausbushes/sparsegrass, /mob/living/simple_mob/vore/aggressive/dino{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) "ul" = ( /obj/effect/floor_decal/asteroid, /mob/living/simple_mob/animal/passive/lizard{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) "um" = ( /obj/landmark/away, /mob/living/simple_mob/animal/space/goose{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/wood, /area/awaymission/zoo) "un" = ( /mob/living/simple_mob/animal/wolf{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/snow, /area/awaymission/zoo) "uo" = ( /mob/living/simple_mob/animal/space/goose{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/wood, /area/awaymission/zoo) @@ -7538,7 +7538,7 @@ "uz" = ( /obj/effect/floor_decal/asteroid, /mob/living/carbon/human/sergallingm{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass2" @@ -7546,7 +7546,7 @@ /area/awaymission/zoo) "uA" = ( /mob/living/simple_mob/animal/space/bear/brown{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) @@ -7558,7 +7558,7 @@ /area/awaymission/zoo) "uC" = ( /mob/living/carbon/human/sergallingm{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass4" @@ -7578,7 +7578,7 @@ /area/awaymission/zoo) "uF" = ( /mob/living/simple_mob/animal/passive/dog/tamaskan{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/snow, /area/awaymission/zoo) @@ -7615,7 +7615,7 @@ /area/awaymission/zoo) "uL" = ( /mob/living/carbon/human/sergallingm{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "fullgrass1" @@ -7623,7 +7623,7 @@ /area/awaymission/zoo) "uM" = ( /mob/living/carbon/human/sergallingm{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass2" @@ -7632,7 +7632,7 @@ "uN" = ( /obj/structure/flora/ausbushes/genericbush, /mob/living/simple_mob/animal/space/bear/brown{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) @@ -7736,14 +7736,14 @@ /area/awaymission/zoo) "uZ" = ( /mob/living/simple_mob/animal/space/carp/large{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) "va" = ( /obj/structure/flora/ausbushes/fernybush, /mob/living/carbon/human/monkey{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "fullgrass1" @@ -7763,13 +7763,13 @@ /area/awaymission/zoo) "vd" = ( /mob/living/simple_mob/animal/passive/crab{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand, /area/awaymission/zoo) "ve" = ( /mob/living/carbon/human/monkey{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "fullgrass3" @@ -7777,7 +7777,7 @@ /area/awaymission/zoo) "vf" = ( /mob/living/simple_mob/vore/aggressive/panther{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass{ icon_state = "grass2" @@ -7845,7 +7845,7 @@ /area/awaymission/zoo) "vl" = ( /mob/living/simple_mob/animal/passive/crab{ - faction = "zoo"; + iff_factions = "!bind-map"; icon_dead = "evilcrab_dead"; icon_living = "evilcrab"; icon_state = "evilcrab" @@ -7861,13 +7861,13 @@ /area/awaymission/zoo) "vn" = ( /mob/living/simple_mob/animal/sif/shantak{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/snow, /area/awaymission/zoo) "vo" = ( /mob/living/carbon/human/monkey{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass2" @@ -7876,7 +7876,7 @@ "vp" = ( /mob/living/simple_mob/animal/sif/shantak{ color = ""; - faction = "zoo"; + iff_factions = "!bind-map"; health = 100; maxHealth = 100; name = "alpha shantak" @@ -7885,20 +7885,20 @@ /area/awaymission/zoo) "vq" = ( /mob/living/simple_mob/animal/space/tree{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/snow, /area/awaymission/zoo) "vr" = ( /obj/structure/flora/bush, /mob/living/simple_mob/animal/sif/shantak{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/snow, /area/awaymission/zoo) "vs" = ( /mob/living/carbon/human/monkey{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass0" @@ -7907,7 +7907,7 @@ "vt" = ( /obj/structure/flora/ausbushes/lavendergrass, /mob/living/carbon/human/monkey{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "fullgrass1" @@ -7949,7 +7949,7 @@ /area/awaymission/zoo) "vz" = ( /mob/living/simple_mob/animal/space/bear/polar{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/outdoors/beach, /area/awaymission/zoo) diff --git a/maps/sectors/frozen_140/levels/frozen_140.dmm b/maps/sectors/frozen_140/levels/frozen_140.dmm index c598c1ec59f2..d1fbb4504dff 100644 --- a/maps/sectors/frozen_140/levels/frozen_140.dmm +++ b/maps/sectors/frozen_140/levels/frozen_140.dmm @@ -535,7 +535,7 @@ /area/class_p/facility) "wQ" = ( /mob/living/simple_mob/animal/space/tree{ - faction = "frozen" + iff_factions = "frozen" }, /turf/simulated/floor/outdoors/snow/classp, /area/class_p/unexplored) diff --git a/maps/sectors/frozen_192/levels/frozen_192.dmm b/maps/sectors/frozen_192/levels/frozen_192.dmm index 96384fcd6485..f2d2b32fe180 100644 --- a/maps/sectors/frozen_192/levels/frozen_192.dmm +++ b/maps/sectors/frozen_192/levels/frozen_192.dmm @@ -242,7 +242,7 @@ /area/class_p/unexplored) "lW" = ( /mob/living/simple_mob/animal/space/tree{ - faction = "frozen" + iff_factions = "frozen" }, /turf/simulated/floor/outdoors/snow/classp, /area/class_p/unexplored) diff --git a/maps/sectors/piratebase_140/levels/piratebase_140.dmm b/maps/sectors/piratebase_140/levels/piratebase_140.dmm index e69c9fe43ed1..0b9aa0ae3387 100644 --- a/maps/sectors/piratebase_140/levels/piratebase_140.dmm +++ b/maps/sectors/piratebase_140/levels/piratebase_140.dmm @@ -469,7 +469,7 @@ /area/piratebase/halls) "vW" = ( /mob/living/simple_mob/mechanical/mecha/combat/marauder/mauler{ - faction = "voxpirate" + iff_factions = "voxpirate" }, /turf/simulated/floor/reinforced, /area/piratebase/halls) diff --git a/maps/sectors/piratebase_192/levels/piratebase_192.dmm b/maps/sectors/piratebase_192/levels/piratebase_192.dmm index 59da0041ba63..022055adef19 100644 --- a/maps/sectors/piratebase_192/levels/piratebase_192.dmm +++ b/maps/sectors/piratebase_192/levels/piratebase_192.dmm @@ -4410,7 +4410,7 @@ dir = 8 }, /mob/living/simple_mob/animal/giant_spider/pepper{ - faction = "pirate"; + iff_factions = "pirate"; name = "First Mate Lil Peppies" }, /turf/simulated/floor/wood, diff --git a/maps/submaps/level_specific/class_h/covert_post.dmm b/maps/submaps/level_specific/class_h/covert_post.dmm index 2d5d8a9390ad..b9108d9ba56c 100644 --- a/maps/submaps/level_specific/class_h/covert_post.dmm +++ b/maps/submaps/level_specific/class_h/covert_post.dmm @@ -226,7 +226,7 @@ /area/class_h/POIs/covert_post) "V" = ( /mob/living/simple_mob/animal/passive/honkpet{ - faction = "clown"; + iff_factions = "clown"; name = "Hunkle" }, /turf/simulated/floor/tiled/white, diff --git a/maps/submaps/level_specific/debrisfield/derelict.dmm b/maps/submaps/level_specific/debrisfield/derelict.dmm index fd2f6abe714e..58cfe5408344 100644 --- a/maps/submaps/level_specific/debrisfield/derelict.dmm +++ b/maps/submaps/level_specific/debrisfield/derelict.dmm @@ -25,7 +25,7 @@ /area/debrisfield/derelict) "ap" = ( /mob/living/simple_mob/mechanical/infectionbot{ - faction = "derelict" + iff_factions = "derelict" }, /turf/simulated/floor/tiled/asteroid_steel/airless, /area/debrisfield/derelict) @@ -82,7 +82,7 @@ /area/debrisfield/derelict) "aA" = ( /mob/living/simple_mob/mechanical/infectionbot{ - faction = "derelict" + iff_factions = "derelict" }, /turf/simulated/floor/tiled/airless, /area/debrisfield/derelict) @@ -139,7 +139,7 @@ "aR" = ( /obj/mob_spawner/derelict/corrupt_maint_swarm, /mob/living/simple_mob/mechanical/corrupt_maint_drone{ - faction = "derelict" + iff_factions = "derelict" }, /turf/simulated/floor/tiled/airless, /area/debrisfield/derelict) @@ -246,7 +246,7 @@ "bo" = ( /obj/mob_spawner/derelict/corrupt_maint_swarm, /mob/living/simple_mob/mechanical/corrupt_maint_drone{ - faction = "derelict" + iff_factions = "derelict" }, /turf/simulated/floor/tiled/asteroid_steel/airless, /area/debrisfield/derelict) @@ -283,7 +283,7 @@ /area/debrisfield/derelict) "bu" = ( /mob/living/simple_mob/mechanical/infectionbot{ - faction = "derelict" + iff_factions = "derelict" }, /turf/simulated/floor/tiled/white/airless, /area/debrisfield/derelict) diff --git a/maps/submaps/level_specific/debrisfield_vr/derelict.dmm b/maps/submaps/level_specific/debrisfield_vr/derelict.dmm index 29e0a3e15a2a..13c4ed44be15 100644 --- a/maps/submaps/level_specific/debrisfield_vr/derelict.dmm +++ b/maps/submaps/level_specific/debrisfield_vr/derelict.dmm @@ -292,7 +292,7 @@ /area/submap/debrisfield_vr/derelict/interior) "aZ" = ( /mob/living/simple_mob/mechanical/infectionbot{ - faction = "derelict" + iff_factions = "derelict" }, /turf/simulated/floor/tiled/dark, /area/submap/debrisfield_vr/derelict/interior) @@ -356,7 +356,7 @@ /area/submap/debrisfield_vr/derelict/interior) "bn" = ( /mob/living/simple_mob/mechanical/infectionbot{ - faction = "derelict" + iff_factions = "derelict" }, /turf/simulated/floor/tiled/steel_grid, /area/submap/debrisfield_vr/derelict/interior) @@ -406,7 +406,7 @@ "bx" = ( /obj/mob_spawner/derelict/corrupt_maint_swarm, /mob/living/simple_mob/mechanical/corrupt_maint_drone{ - faction = "derelict" + iff_factions = "derelict" }, /turf/simulated/floor/tiled/steel_grid, /area/submap/debrisfield_vr/derelict/interior) @@ -546,7 +546,7 @@ "bX" = ( /obj/mob_spawner/derelict/corrupt_maint_swarm, /mob/living/simple_mob/mechanical/corrupt_maint_drone{ - faction = "derelict" + iff_factions = "derelict" }, /turf/simulated/floor/tiled/dark, /area/submap/debrisfield_vr/derelict/interior) @@ -609,7 +609,7 @@ /area/submap/debrisfield_vr/derelict/interior) "ci" = ( /mob/living/simple_mob/mechanical/infectionbot{ - faction = "derelict" + iff_factions = "derelict" }, /turf/simulated/floor/tiled/white, /area/submap/debrisfield_vr/derelict/interior) diff --git a/maps/submaps/level_specific/debrisfield_vr/mining_drones.dmm b/maps/submaps/level_specific/debrisfield_vr/mining_drones.dmm index 27c88e83d9d7..006b3cc94f96 100644 --- a/maps/submaps/level_specific/debrisfield_vr/mining_drones.dmm +++ b/maps/submaps/level_specific/debrisfield_vr/mining_drones.dmm @@ -7,7 +7,7 @@ /area/space) "c" = ( /mob/living/simple_mob/mechanical/corrupt_maint_drone{ - faction = "poi_mining_drones" + iff_factions = "poi_mining_drones" }, /turf/simulated/floor/airless, /area/submap/debrisfield_vr/mining_outpost) diff --git a/maps/submaps/level_specific/underdark/abandonded_outpost.dmm b/maps/submaps/level_specific/underdark/abandonded_outpost.dmm index 81854381bf99..d6a54d376107 100644 --- a/maps/submaps/level_specific/underdark/abandonded_outpost.dmm +++ b/maps/submaps/level_specific/underdark/abandonded_outpost.dmm @@ -354,7 +354,7 @@ /area/mine/explored/underdark) "bB" = ( /mob/living/simple_mob/mechanical/infectionbot{ - faction = "underdark" + iff_factions = "underdark" }, /turf/simulated/floor/tiled/steel_dirty/virgo3b, /area/mine/explored/underdark) diff --git a/maps/submaps/level_specific/underdark/wolf_den.dmm b/maps/submaps/level_specific/underdark/wolf_den.dmm index 3d43dc486d23..6122e62fce0a 100644 --- a/maps/submaps/level_specific/underdark/wolf_den.dmm +++ b/maps/submaps/level_specific/underdark/wolf_den.dmm @@ -37,7 +37,7 @@ /area/mine/explored/underdark) "j" = ( /mob/living/simple_mob/otie/feral{ - faction = "underdark" + iff_factions = "underdark" }, /turf/simulated/mineral/floor/ignore_cavegen/virgo3b, /area/mine/explored/underdark) diff --git a/maps/submaps/level_specific/virgo2/Rockybase.dmm b/maps/submaps/level_specific/virgo2/Rockybase.dmm index f0642ac9f246..ef2d0f990489 100644 --- a/maps/submaps/level_specific/virgo2/Rockybase.dmm +++ b/maps/submaps/level_specific/virgo2/Rockybase.dmm @@ -24,7 +24,7 @@ /mob/living/simple_mob/mechanical/combat_drone{ ai_holder_type = /datum/ai_holder/polaris/simple_mob/ranged/kiting/threatening/returnhome; desc = "An automated combat drone with an aged apperance."; - faction = "syndicate"; + iff_factions = "syndicate"; maxbodytemp = 900 }, /turf/simulated/mineral/floor/ignore_mapgen/virgo2, @@ -114,7 +114,7 @@ /area/submap/virgo2/Rockybase) "ax" = ( /mob/living/bot/cleanbot{ - faction = "malf_drone" + iff_factions = "malf_drone" }, /turf/simulated/floor/tiled/techfloor/virgo2, /area/submap/virgo2/Rockybase) @@ -307,7 +307,7 @@ /area/submap/virgo2/Rockybase) "bf" = ( /mob/living/bot/farmbot{ - faction = "malf_drone" + iff_factions = "malf_drone" }, /turf/simulated/floor/tiled/techfloor/virgo2, /area/submap/virgo2/Rockybase) @@ -341,7 +341,7 @@ /mob/living/simple_mob/mechanical/combat_drone{ ai_holder_type = /datum/ai_holder/polaris/simple_mob/ranged/kiting/threatening/returnhome; desc = "An automated combat drone with an aged apperance."; - faction = "syndicate"; + iff_factions = "syndicate"; maxbodytemp = 900 }, /turf/simulated/floor/tiled/techfloor/virgo2, diff --git a/maps/submaps/plains/Oldhouse.dmm b/maps/submaps/plains/Oldhouse.dmm index df7623b7f23b..ee2c21772cef 100644 --- a/maps/submaps/plains/Oldhouse.dmm +++ b/maps/submaps/plains/Oldhouse.dmm @@ -183,7 +183,7 @@ attack_armor_pen = 100; attacktext = list("lightly bonked"); desc = "Furry and brown, this spider is so goddamn fat you're surprised it even moves around."; - faction = "neutral"; + iff_factions = "neutral"; health = 400; legacy_melee_damage_lower = 1; legacy_melee_damage_upper = 3; diff --git a/maps/submaps/plains/Oldhouse_vr.dmm b/maps/submaps/plains/Oldhouse_vr.dmm index 4c3de3e3c55e..089de09d6358 100644 --- a/maps/submaps/plains/Oldhouse_vr.dmm +++ b/maps/submaps/plains/Oldhouse_vr.dmm @@ -183,7 +183,7 @@ attack_armor_pen = 100; attacktext = list("lightly bonked"); desc = "Furry and brown, this spider is so goddamn fat you're surprised it even moves around."; - faction = "neutral"; + iff_factions = "neutral"; health = 400; maxHealth = 400; legacy_melee_damage_lower = 1; diff --git a/maps/submaps/wilderness/Rockybase.dmm b/maps/submaps/wilderness/Rockybase.dmm index 1571a38f7ae7..e7810469bd03 100644 --- a/maps/submaps/wilderness/Rockybase.dmm +++ b/maps/submaps/wilderness/Rockybase.dmm @@ -167,7 +167,7 @@ /area/submap/Rockybase) "aK" = ( /mob/living/bot/cleanbot{ - faction = "malf_drone" + iff_factions = "malf_drone" }, /turf/simulated/floor/tiled, /area/submap/Rockybase) @@ -435,7 +435,7 @@ dir = 8 }, /mob/living/bot/farmbot{ - faction = "malf_drone"; + iff_factions = "malf_drone"; name = "Mr. Weddleton" }, /turf/simulated/floor/tiled, @@ -790,7 +790,7 @@ /area/submap/Rockybase) "cL" = ( /mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced{ - faction = "malf_drone" + iff_factions = "malf_drone" }, /turf/simulated/floor/tiled, /area/submap/Rockybase) @@ -1035,7 +1035,7 @@ "dB" = ( /obj/effect/debris/cleanable/dirt, /mob/living/bot/medibot{ - faction = "malf_drone" + iff_factions = "malf_drone" }, /turf/simulated/floor/tiled, /area/submap/Rockybase) diff --git a/maps/tether/levels/plains.dmm b/maps/tether/levels/plains.dmm index a71bc8e6ad49..a60e9f1db8af 100644 --- a/maps/tether/levels/plains.dmm +++ b/maps/tether/levels/plains.dmm @@ -20,7 +20,7 @@ /turf/simulated/floor/tiled/steel_dirty/virgo3b, /area/mine/unexplored) "i" = ( -/mob/living/simple_mob/animal/space/goose/virgo3b, +/mob/living/simple_mob/animal/space/goose, /turf/simulated/floor/outdoors/grass/sif/virgo3b, /area/tether/outpost/exploration_plains) "j" = ( From 0c8dd521c2099aa14be722e1ff5af0638efa368f Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Fri, 6 Sep 2024 15:36:10 -0400 Subject: [PATCH 35/35] refactors attack hand proc header (#6720) needed for #5677 this is to make that PR slightly easier to review. --- .../dcs/signals/signals_mob/signals_mob_legacy.dm | 2 +- code/game/atoms/buckling.dm | 2 +- code/game/atoms/movable/special/overlay.dm | 10 ++++------ code/game/click/other_mobs.dm | 12 ++++++++---- code/game/dna/dna_modifier.dm | 2 +- code/game/gamemodes/cult/cult_structures.dm | 2 +- code/game/gamemodes/cult/ritual.dm | 2 +- code/game/machinery/CableLayer.dm | 2 +- code/game/machinery/OpTable.dm | 2 +- code/game/machinery/Sleeper.dm | 2 +- code/game/machinery/_machinery.dm | 2 +- code/game/machinery/adv_med.dm | 2 +- code/game/machinery/ai_slipper.dm | 2 +- code/game/machinery/atm_ret_field.dm | 2 +- code/game/machinery/atmo_control.dm | 2 +- code/game/machinery/bioprinter.dm | 2 +- code/game/machinery/bomb_tester.dm | 2 +- code/game/machinery/buttons.dm | 4 ++-- code/game/machinery/camera/camera.dm | 2 +- code/game/machinery/camera/camera_assembly.dm | 2 +- code/game/machinery/cell_charger.dm | 2 +- code/game/machinery/cloning.dm | 2 +- code/game/machinery/computer/Operating.dm | 2 +- code/game/machinery/computer/RCON_Console.dm | 2 +- code/game/machinery/computer/aifixer.dm | 2 +- code/game/machinery/computer/arcade/amputation.dm | 2 +- code/game/machinery/computer/arcade/claw_machine.dm | 2 +- code/game/machinery/computer/atmos_alert.dm | 2 +- code/game/machinery/computer/atmos_control.dm | 2 +- code/game/machinery/computer/camera.dm | 2 +- code/game/machinery/computer/cloning.dm | 2 +- code/game/machinery/computer/command/card.dm | 2 +- code/game/machinery/computer/communications.dm | 2 +- code/game/machinery/computer/crew.dm | 2 +- code/game/machinery/computer/law.dm | 4 ++-- code/game/machinery/computer/medical.dm | 2 +- code/game/machinery/computer/message.dm | 2 +- code/game/machinery/computer/pod.dm | 4 ++-- code/game/machinery/computer/prisoner.dm | 2 +- code/game/machinery/computer/prisonshuttle.dm | 2 +- code/game/machinery/computer/robot.dm | 2 +- code/game/machinery/computer/security.dm | 2 +- code/game/machinery/computer/shutoff_monitor.dm | 2 +- code/game/machinery/computer/skills.dm | 2 +- code/game/machinery/computer/specops_shuttle.dm | 2 +- code/game/machinery/computer/station_alert.dm | 2 +- .../machinery/computer/supermatter_monitor_tgui.dm | 2 +- code/game/machinery/computer/supply.dm | 2 +- .../machinery/computer/syndicate_specops_shuttle.dm | 2 +- code/game/machinery/computer/timeclock_vr.dm | 2 +- code/game/machinery/cryo.dm | 2 +- code/game/machinery/cryopod.dm | 2 +- code/game/machinery/deployable_vr.dm | 2 +- code/game/machinery/door_control.dm | 2 +- code/game/machinery/doorbell_vr.dm | 2 +- code/game/machinery/doors/airlock/airlock.dm | 2 +- code/game/machinery/doors/airlock/airlock_control.dm | 4 ++-- code/game/machinery/doors/blast_door.dm | 2 +- code/game/machinery/doors/door.dm | 2 +- code/game/machinery/doors/door_timer.dm | 2 +- code/game/machinery/doors/firedoor.dm | 2 +- code/game/machinery/doors/windowdoor.dm | 2 +- .../embedded_controller/embedded_controller_base.dm | 2 +- code/game/machinery/event.dm | 4 ++-- code/game/machinery/exonet_node.dm | 2 +- code/game/machinery/fire_alarm.dm | 2 +- code/game/machinery/flasher.dm | 2 +- code/game/machinery/floodlight.dm | 2 +- code/game/machinery/floor_inflatables.dm | 2 +- code/game/machinery/floor_light.dm | 2 +- code/game/machinery/floorlayer.dm | 2 +- code/game/machinery/holosign.dm | 2 +- code/game/machinery/igniter.dm | 4 ++-- code/game/machinery/iv_drip.dm | 2 +- code/game/machinery/jukebox.dm | 2 +- code/game/machinery/lightswitch.dm | 2 +- code/game/machinery/magnet.dm | 2 +- code/game/machinery/misc/bioscan_antenna.dm | 2 +- code/game/machinery/navbeacon.dm | 2 +- code/game/machinery/newscaster.dm | 2 +- code/game/machinery/nuclear_bomb.dm | 2 +- code/game/machinery/oxygen_pump.dm | 2 +- code/game/machinery/pda_multicaster.dm | 2 +- code/game/machinery/pipe/pipe_dispenser.dm | 2 +- code/game/machinery/pointdefense.dm | 2 +- code/game/machinery/recharger.dm | 2 +- code/game/machinery/recipe_lookup.dm | 2 +- code/game/machinery/records_scanner.dm | 2 +- code/game/machinery/requests_console.dm | 2 +- code/game/machinery/spaceheater.dm | 4 ++-- code/game/machinery/suit_storage/suit_cycler.dm | 2 +- .../machinery/suit_storage/suit_storage_massive.dm | 2 +- .../game/machinery/suit_storage/suit_storage_unit.dm | 2 +- code/game/machinery/supplybeacon.dm | 2 +- code/game/machinery/syndicatebeacon.dm | 6 +++--- code/game/machinery/telecomms/_telecomms.dm | 2 +- code/game/machinery/telecomms/logbrowser.dm | 2 +- code/game/machinery/telecomms/message_server.dm | 2 +- code/game/machinery/telecomms/telemonitor.dm | 2 +- code/game/machinery/teleporter/console.dm | 2 +- code/game/machinery/teleporter/projector.dm | 2 +- code/game/machinery/turrets/turret.dm | 2 +- code/game/machinery/turrets/turret_control.dm | 2 +- code/game/machinery/turrets/turret_frame.dm | 6 +++--- code/game/machinery/vending/vending.dm | 2 +- code/game/machinery/washing_machine.dm | 2 +- code/game/machinery/wishgranter.dm | 2 +- code/game/objects/effects/debris/cleanable/blood.dm | 2 +- code/game/objects/effects/debris/cleanable/misc.dm | 2 +- code/game/objects/effects/decals/remains.dm | 2 +- code/game/objects/effects/portals.dm | 2 +- code/game/objects/items/bells.dm | 2 +- code/game/objects/items/devices/chameleonproj.dm | 2 +- code/game/objects/items/devices/defib.dm | 2 +- code/game/objects/items/devices/flashlight.dm | 2 +- code/game/objects/items/devices/powersink.dm | 2 +- code/game/objects/items/devices/radio/electropack.dm | 2 +- code/game/objects/items/devices/radio/intercom.dm | 2 +- code/game/objects/items/devices/radio/jammer.dm | 2 +- code/game/objects/items/devices/radio/radio.dm | 2 +- .../items/devices/tape_recorder/tape_recorder.dm | 2 +- code/game/objects/items/devices/taperecorder.dm | 2 +- code/game/objects/items/shooting_range.dm | 2 +- code/game/objects/items/stacks/marker_beacons.dm | 2 +- code/game/objects/items/stacks/stack.dm | 2 +- code/game/objects/items/storage/quickdraw.dm | 2 +- code/game/objects/items/storage/secure.dm | 2 +- .../game/objects/items/stream_projector/medichine.dm | 2 +- code/game/objects/items/tools/weldingtool.dm | 2 +- code/game/objects/items/toys.dm | 4 ++-- code/game/objects/items/uav.dm | 2 +- code/game/objects/items/weapons/barrier_tape.dm | 4 ++-- code/game/objects/items/weapons/duct_tape.dm | 2 +- code/game/objects/items/weapons/game_kit.dm | 2 +- code/game/objects/items/weapons/grenades/grenade.dm | 2 +- .../objects/items/weapons/implants/implantchair.dm | 2 +- .../objects/items/weapons/implants/implantpad.dm | 2 +- code/game/objects/items/weapons/stunbaton.dm | 2 +- code/game/objects/items/weapons/traps.dm | 2 +- code/game/objects/items/weapons/weldbackpack.dm | 2 +- code/game/objects/misc.dm | 2 +- code/game/objects/obj.dm | 2 +- code/game/objects/structures.dm | 2 +- code/game/objects/structures/aliens.dm | 4 ++-- code/game/objects/structures/ashlander.dm | 4 ++-- code/game/objects/structures/bedsheet_bin.dm | 2 +- code/game/objects/structures/bonfire.dm | 4 ++-- code/game/objects/structures/catwalk.dm | 2 +- code/game/objects/structures/charge_pylon.dm | 2 +- code/game/objects/structures/coathanger.dm | 2 +- .../objects/structures/crates_lockers/__closet.dm | 2 +- .../structures/crates_lockers/closets/coffin.dm | 2 +- .../structures/crates_lockers/closets/fireaxe.dm | 2 +- .../crates_lockers/closets/secure/genpop.dm | 2 +- .../structures/crates_lockers/closets/statue.dm | 2 +- .../structures/crates_lockers/closets/walllocker.dm | 2 +- .../game/objects/structures/crates_lockers/crates.dm | 2 +- .../objects/structures/crates_lockers/largecrate.dm | 2 +- .../objects/structures/crates_lockers/vehiclecage.dm | 2 +- code/game/objects/structures/curtains.dm | 2 +- code/game/objects/structures/displaycase.dm | 2 +- code/game/objects/structures/extinguisher.dm | 2 +- code/game/objects/structures/fence.dm | 2 +- code/game/objects/structures/fireaxe.dm | 2 +- code/game/objects/structures/fitness.dm | 4 ++-- code/game/objects/structures/flora.dm | 4 ++-- code/game/objects/structures/flora/trees.dm | 2 +- .../game/objects/structures/ghost_pods/ghost_pods.dm | 2 +- code/game/objects/structures/holoplant.dm | 2 +- code/game/objects/structures/inflatable.dm | 2 +- code/game/objects/structures/janicart.dm | 4 ++-- code/game/objects/structures/kitchen_foodcart.dm | 2 +- code/game/objects/structures/kitchen_spike.dm | 2 +- code/game/objects/structures/loot_piles.dm | 2 +- code/game/objects/structures/medical_stand_vr.dm | 2 +- code/game/objects/structures/mineral_bath.dm | 2 +- code/game/objects/structures/mirror.dm | 2 +- code/game/objects/structures/morgue.dm | 8 ++++---- code/game/objects/structures/noticeboard.dm | 2 +- code/game/objects/structures/poster.dm | 2 +- code/game/objects/structures/props/beam_prism.dm | 4 ++-- code/game/objects/structures/props/nest.dm | 2 +- code/game/objects/structures/props/prop.dm | 2 +- code/game/objects/structures/props/puzzledoor.dm | 2 +- code/game/objects/structures/safe.dm | 2 +- code/game/objects/structures/simple_doors.dm | 2 +- code/game/objects/structures/snowman.dm | 2 +- code/game/objects/structures/stasis_cage.dm | 2 +- code/game/objects/structures/statues.dm | 2 +- .../structures/stool_bed_chair_nest/chairs.dm | 2 +- .../structures/stool_bed_chair_nest/wheelchair.dm | 2 +- code/game/objects/structures/tank_dispenser.dm | 2 +- code/game/objects/structures/target_stake.dm | 2 +- code/game/objects/structures/transit_tubes.dm | 2 +- code/game/objects/structures/trash_pile.dm | 2 +- code/game/objects/structures/under_wardrobe.dm | 2 +- code/game/objects/structures/watercloset.dm | 10 +++++----- code/game/objects/structures/window.dm | 2 +- code/game/rendering/screen.dm | 2 +- code/game/turfs/simulated/floor_types/snow.dm | 2 +- code/game/turfs/simulated/flooring/flooring_traps.dm | 2 +- code/game/turfs/simulated/wall/wall_attacks.dm | 2 +- code/game/turfs/turf.dm | 2 +- code/modules/assembly/holder.dm | 2 +- code/modules/assembly/mousetrap.dm | 2 +- code/modules/atmospherics/machinery/air_alarm.dm | 2 +- .../components/binary_devices/algae_generator_vr.dm | 2 +- .../machinery/components/binary_devices/heat_pump.dm | 2 +- .../components/binary_devices/massive_gas_pump.dm | 2 +- .../components/binary_devices/massive_heat_pump.dm | 2 +- .../components/binary_devices/passive_gate.dm | 2 +- .../machinery/components/binary_devices/pump.dm | 2 +- .../machinery/components/binary_devices/valve.dm | 4 ++-- .../machinery/components/omni_devices/omni_base.dm | 2 +- .../machinery/components/trinary_devices/mixer.dm | 2 +- .../machinery/components/trinary_devices/tvalve.dm | 4 ++-- .../machinery/components/unary/gas_freezer.dm | 2 +- .../machinery/components/unary/gas_heater.dm | 2 +- .../machinery/components/unary/outlet_injector.dm | 2 +- .../machinery/portable/area_atmos_computer.dm | 2 +- .../atmospherics/machinery/portable/canister.dm | 2 +- code/modules/atmospherics/machinery/portable/pump.dm | 2 +- code/modules/awaymissions/bluespaceartillery.dm | 2 +- code/modules/clothing/masks/miscellaneous.dm | 2 +- code/modules/clothing/shoes/_shoes.dm | 2 +- code/modules/clothing/under/accessories/accessory.dm | 2 +- code/modules/clothing/under/accessories/holster.dm | 2 +- code/modules/detectivework/microscope/dnascanner.dm | 2 +- code/modules/detectivework/microscope/microscope.dm | 2 +- code/modules/economy/machines/ATM.dm | 2 +- code/modules/economy/machines/Accounts_DB.dm | 2 +- code/modules/economy/machines/cash_register.dm | 2 +- code/modules/economy/machines/mint.dm | 4 ++-- code/modules/economy/money_bag.dm | 2 +- code/modules/food/drinkingglass/extras.dm | 2 +- code/modules/food/food/snacks.dm | 4 ++-- code/modules/food/glass/bottle.dm | 2 +- code/modules/food/machinery/appliance/_appliance.dm | 2 +- code/modules/food/machinery/gibber.dm | 2 +- code/modules/food/machinery/microwave.dm | 2 +- code/modules/food/machinery/smartfridge.dm | 2 +- code/modules/games/cards.dm | 2 +- code/modules/gateway/stargate/stargate-away.dm | 2 +- code/modules/gateway/stargate/stargate-station.dm | 2 +- code/modules/hardsuits/pieces.dm | 2 +- code/modules/hardsuits/rig_attackby.dm | 2 +- code/modules/hardsuits/rig_pieces.dm | 2 +- code/modules/holodeck/HolodeckControl.dm | 2 +- code/modules/holodeck/HolodeckObjects.dm | 2 +- code/modules/holomap/station_holomap.dm | 2 +- code/modules/hydroponics/beekeeping/beehive.dm | 2 +- code/modules/hydroponics/seed_machines.dm | 2 +- code/modules/hydroponics/seed_storage.dm | 2 +- code/modules/hydroponics/spreading/spreading.dm | 2 +- .../hydroponics/spreading/spreading_response.dm | 2 +- code/modules/hydroponics/trays/tray.dm | 2 +- code/modules/industry/conveyors/conveyor.dm | 2 +- code/modules/industry/conveyors/conveyor_switch.dm | 4 ++-- code/modules/industry/disposals/disposal/chute.dm | 2 +- code/modules/industry/packages/large_parcel.dm | 2 +- code/modules/instruments/instruments/stationary.dm | 2 +- .../integrated_electronics/core/assemblies.dm | 2 +- code/modules/library/lib_items.dm | 2 +- code/modules/library/lib_machines.dm | 6 +++--- code/modules/maps/away_missions/archive/wildwest.dm | 2 +- code/modules/maps/misc_maps/lavaland/_lavaland.dm | 4 ++-- code/modules/materials/material_sheets.dm | 2 +- code/modules/mining/drilling/drill.dm | 2 +- code/modules/mining/machine_processing.dm | 2 +- code/modules/mining/machine_stacking.dm | 2 +- code/modules/mining/mine_items.dm | 2 +- .../ore_redemption_machine/equipment_vendor.dm | 2 +- code/modules/mining/shelter_atoms.dm | 4 ++-- code/modules/mob/living/bot/cleanbot.dm | 2 +- code/modules/mob/living/bot/farmbot.dm | 4 ++-- code/modules/mob/living/bot/floorbot.dm | 2 +- code/modules/mob/living/bot/medibot.dm | 2 +- code/modules/mob/living/bot/secbot.dm | 2 +- .../modules/mob/living/carbon/alien/alien_attacks.dm | 2 +- code/modules/mob/living/carbon/carbon.dm | 2 +- .../mob/living/carbon/human/human_attackhand.dm | 2 +- .../mob/living/carbon/human/traits/weaver_objs.dm | 4 ++-- code/modules/mob/living/living-defense-legacy.dm | 4 ++-- code/modules/mob/living/silicon/pai/defense.dm | 2 +- .../mob/living/silicon/pai/hologram_effect.dm | 2 +- .../mob/living/silicon/robot/drone/drone_console.dm | 2 +- .../living/silicon/robot/drone/drone_manufacturer.dm | 2 +- code/modules/mob/living/silicon/robot/robot.dm | 2 +- code/modules/mob/living/simple_mob/defense.dm | 2 +- .../simple_mob/subtypes/animal/farm animals/cow.dm | 2 +- .../simple_mob/subtypes/animal/passive/fish.dm | 2 +- .../simple_mob/subtypes/animal/passive/mouse.dm | 2 +- .../living/simple_mob/subtypes/animal/pets/parrot.dm | 2 +- .../mob/living/simple_mob/subtypes/horror/Eddy.dm | 2 +- .../mob/living/simple_mob/subtypes/horror/Master.dm | 2 +- .../mob/living/simple_mob/subtypes/horror/Rickey.dm | 2 +- .../mob/living/simple_mob/subtypes/horror/Smiley.dm | 2 +- .../mob/living/simple_mob/subtypes/horror/Steve.dm | 2 +- .../mob/living/simple_mob/subtypes/horror/Willy.dm | 2 +- .../mob/living/simple_mob/subtypes/horror/bradley.dm | 2 +- .../mob/living/simple_mob/subtypes/horror/sally.dm | 2 +- .../living/simple_mob/subtypes/horror/shittytim.dm | 2 +- .../mob/living/simple_mob/subtypes/horror/timling.dm | 2 +- .../living/simple_mob/subtypes/illusion/illusion.dm | 2 +- .../mob/living/simple_mob/subtypes/slime/slime.dm | 2 +- .../simple_mob/subtypes/slime/xenobio/defense.dm | 2 +- .../mob/living/simple_mob/subtypes/vore/c_pet.dm | 4 ++-- .../mob/living/simple_mob/subtypes/vore/otie.dm | 2 +- code/modules/modular_computers/NTNet/NTNet_relay.dm | 2 +- .../computers/modular_computer/interaction.dm | 2 +- code/modules/modular_computers/laptop_vendor.dm | 2 +- code/modules/multiz/structures/hoist.dm | 4 ++-- code/modules/multiz/structures/ladder.dm | 2 +- .../modules/multiz/structures/vorestation_portals.dm | 2 +- code/modules/multiz/zmimic/mimic_movable.dm | 6 +++--- code/modules/overmap/legacy/overmap_shuttle.dm | 2 +- .../overmap/legacy/ships/computers/computer_shims.dm | 2 +- code/modules/overmap/legacy/ships/panicbutton.dm | 2 +- code/modules/paperwork/faxmachine.dm | 2 +- code/modules/paperwork/filingcabinet.dm | 6 +++--- code/modules/paperwork/paperbin.dm | 4 ++-- code/modules/paperwork/photocopier.dm | 2 +- code/modules/power/antimatter/computer.dm | 2 +- code/modules/power/antimatter/control.dm | 2 +- code/modules/power/apc.dm | 2 +- code/modules/power/breaker_box.dm | 2 +- code/modules/power/fission/computer.dm | 2 +- code/modules/power/fission/engine.dm | 2 +- code/modules/power/fusion/core/_core.dm | 2 +- code/modules/power/fusion/core/core_control.dm | 2 +- .../power/fusion/fuel_assembly/fuel_control.dm | 2 +- .../power/fusion/fuel_assembly/fuel_injector.dm | 2 +- .../power/fusion/gyrotron/gyrotron_control.dm | 2 +- code/modules/power/generator.dm | 2 +- code/modules/power/generator_type2.dm | 2 +- code/modules/power/gravitygenerator.dm | 4 ++-- code/modules/power/grid_checker.dm | 2 +- code/modules/power/lighting/lighting.dm | 6 +++--- code/modules/power/port_gen.dm | 6 +++--- code/modules/power/sensors/sensor_monitoring.dm | 2 +- code/modules/power/singularity/collector.dm | 2 +- code/modules/power/singularity/containment_field.dm | 2 +- code/modules/power/singularity/emitter.dm | 2 +- code/modules/power/singularity/field_generator.dm | 2 +- .../particle_accelerator/particle_control.dm | 2 +- code/modules/power/singularity/singularity.dm | 2 +- code/modules/power/smes/smes.dm | 2 +- code/modules/power/smes/smes_construction.dm | 2 +- code/modules/power/solar/solar_control.dm | 2 +- code/modules/power/supermatter/supermatter.dm | 2 +- code/modules/power/tesla/coil.dm | 4 ++-- code/modules/power/turbine.dm | 4 ++-- code/modules/projectiles/guns/ballistic.dm | 2 +- code/modules/projectiles/guns/energy.dm | 2 +- .../projectiles/guns/launcher/grenade_launcher.dm | 2 +- code/modules/projectiles/guns/launcher/pneumatic.dm | 2 +- .../modules/projectiles/guns/launcher/syringe_gun.dm | 2 +- code/modules/projectiles/guns/magnetic/bore.dm | 2 +- code/modules/projectiles/guns/magnetic/magnetic.dm | 2 +- .../modules/projectiles/guns/projectile/automatic.dm | 4 ++-- code/modules/projectiles/guns/projectile/bow.dm | 2 +- .../modules/projectiles/guns/projectile/contender.dm | 2 +- code/modules/projectiles/guns/projectile/pistol.dm | 6 +++--- code/modules/projectiles/guns/projectile/rocket.dm | 6 +++--- code/modules/random_map/drop/droppod_doors.dm | 2 +- code/modules/reagents/chemistry/machinery.dm | 2 +- code/modules/reagents/distilling/distilling.dm | 2 +- code/modules/reagents/items/hypospray.dm | 2 +- code/modules/reagents/machinery/chem_master.dm | 2 +- .../reagents/machinery/reagent_dispenser/fuel.dm | 2 +- .../machinery/reagent_dispenser/watercooler.dm | 2 +- code/modules/reagents/reagent_containers/glass.dm | 2 +- .../modules/reagents/reagent_containers/hypospray.dm | 2 +- code/modules/reagents/reagent_containers/organic.dm | 2 +- code/modules/reagents/reagent_containers/syringes.dm | 2 +- code/modules/research/machinery/rdconsole.dm | 2 +- code/modules/research/machinery/rdmachines.dm | 2 +- code/modules/research/machinery/server.dm | 2 +- code/modules/resleeving/computers.dm | 2 +- code/modules/resleeving/infomorph.dm | 2 +- code/modules/resleeving/machines.dm | 4 ++-- code/modules/resleeving/mirror.dm | 2 +- code/modules/rogueminer_vr/zone_console.dm | 2 +- .../security levels/keycard authentication.dm | 2 +- code/modules/shieldgen/emergency_shield.dm | 2 +- code/modules/shieldgen/energy_field.dm | 2 +- code/modules/shieldgen/energy_shield.dm | 2 +- code/modules/shieldgen/handheld_defuser.dm | 2 +- code/modules/shieldgen/sheldwallgen.dm | 4 ++-- code/modules/shieldgen/shield_capacitor.dm | 2 +- code/modules/shieldgen/shield_diffuser.dm | 2 +- code/modules/shieldgen/shield_gen.dm | 2 +- code/modules/shieldgen/shield_generator.dm | 2 +- code/modules/shuttles/shuttle_console.dm | 2 +- code/modules/species/protean/protean_blob.dm | 2 +- .../station/xenomorph_hybrids/hybrid_resin.dm | 2 +- code/modules/species/xenomorphs/alien_facehugger.dm | 4 ++-- code/modules/stockmarket/computer.dm | 2 +- code/modules/telesci/hyper_pad.dm | 4 ++-- code/modules/telesci/quantum_pad.dm | 2 +- code/modules/telesci/telesci_computer.dm | 2 +- code/modules/turbolift/turbolift_console.dm | 2 +- .../modules/vehicles/sealed/mecha/mech_fabricator.dm | 2 +- code/modules/vehicles/sealed/mecha/mecha.dm | 2 +- .../vehicles/sealed/mecha/mecha_control_console.dm | 2 +- code/modules/vehicles/sealed/mecha/mecha_parts.dm | 4 ++-- .../sealed/mecha/subtypes/micro/mecha_parts_vr.dm | 2 +- code/modules/vehicles_legacy/bike.dm | 2 +- code/modules/vehicles_legacy/skateboard.dm | 2 +- code/modules/vehicles_legacy/train.dm | 2 +- code/modules/virus2/curer.dm | 2 +- code/modules/virus2/diseasesplicer.dm | 2 +- code/modules/virus2/dishincubator.dm | 2 +- code/modules/virus2/isolator.dm | 2 +- code/modules/vore/fluffstuff/custom_items.dm | 4 ++-- code/modules/vore/weight/fitness_machines_vr.dm | 6 +++--- code/modules/xenoarcheaology/anomaly_container.dm | 2 +- code/modules/xenoarcheaology/artifacts/artifact.dm | 2 +- code/modules/xenoarcheaology/artifacts/gigadrill.dm | 2 +- code/modules/xenoarcheaology/artifacts/replicator.dm | 2 +- .../xenoarcheaology/tools/artifact_analyser.dm | 2 +- .../xenoarcheaology/tools/artifact_harvester.dm | 2 +- .../xenoarcheaology/tools/geosample_scanner.dm | 2 +- .../xenoarcheaology/tools/suspension_generator.dm | 2 +- code/modules/xenobio/machinery/processor.dm | 2 +- code/modules/xenobio2/machinery/core_extractor.dm | 2 +- code/modules/xenobio2/machinery/gene_manipulators.dm | 2 +- code/modules/xenobio2/machinery/injector_computer.dm | 2 +- code/modules/xenobio2/machinery/slime_replicator.dm | 2 +- maps/generic/misc.dm | 2 +- 430 files changed, 506 insertions(+), 504 deletions(-) diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_legacy.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_legacy.dm index 7e43f572821d..374ffdcdbb66 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_legacy.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_legacy.dm @@ -8,6 +8,6 @@ /// for now, this is a signal with (item, user, hit_zone) #define COMSIG_MOB_LEGACY_RESOLVE_ITEM_ATTACK "legacy-mob-item-resolve-attack" /// used by passive parry to detect -/// signal with (user, list/params) +/// signal with (user, datum/event_args/actor/clickchain/e_args) /// :skull: #define COMSIG_MOB_LEGACY_ATTACK_HAND_INTERCEPT "legacy-mob-legacy-attack-hand" diff --git a/code/game/atoms/buckling.dm b/code/game/atoms/buckling.dm index b286d57f88ef..0068578f2ab7 100644 --- a/code/game/atoms/buckling.dm +++ b/code/game/atoms/buckling.dm @@ -6,7 +6,7 @@ return CLICKCHAIN_DO_NOT_PROPAGATE return ..() -/atom/movable/attack_hand(mob/user, list/params) +/atom/movable/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_HARM) return ..() . = ..() diff --git a/code/game/atoms/movable/special/overlay.dm b/code/game/atoms/movable/special/overlay.dm index 56889c02b356..be8a448f50dd 100644 --- a/code/game/atoms/movable/special/overlay.dm +++ b/code/game/atoms/movable/special/overlay.dm @@ -5,10 +5,8 @@ // todo: nuke this shit from orbit, this is awful. var/atom/master = null -/atom/movable/overlay/attackby(a, b) - if (src.master) - return src.master.attackby(a, b) +/atom/movable/overlay/attackby(obj/item/I, mob/user, list/params, clickchain_flags, damage_multiplier) + return master.attackby(arglist(args)) -/atom/movable/overlay/attack_hand(a, b, c) - if (src.master) - return src.master.attack_hand(a, b, c) +/atom/movable/overlay/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) + return master.attack_hand(arglist(args)) diff --git a/code/game/click/other_mobs.dm b/code/game/click/other_mobs.dm index 8cbc452f270a..55a50d5cb69d 100644 --- a/code/game/click/other_mobs.dm +++ b/code/game/click/other_mobs.dm @@ -25,12 +25,16 @@ if(istype(G) && G.Touch(A,1)) return - A.attack_hand(src) + A.attack_hand(src, new /datum/event_args/actor/clickchain(src)) /// Return TRUE to cancel other attack hand effects that respect it. -// todo: /datum/event_args/actor/clickchain -/atom/proc/attack_hand(mob/user, list/params) - var/datum/event_args/actor/clickchain/e_args = new(user, target = src, intent = user.a_intent, params = params) +// todo: better desc +// todo: e_args is not specified all the time, yet. +/atom/proc/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) + // todo: remove + if(isnull(e_args)) + e_args = new(user) + // end if(on_attack_hand(e_args)) return TRUE if(user.a_intent == INTENT_HARM) diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm index 3f2835b7d3dd..7fd8de7c9530 100644 --- a/code/game/dna/dna_modifier.dm +++ b/code/game/dna/dna_modifier.dm @@ -300,7 +300,7 @@ src.add_hiddenprint(user) nano_ui_interact(user) -/obj/machinery/computer/scan_consolenew/attack_hand(mob/user, list/params) +/obj/machinery/computer/scan_consolenew/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!..()) nano_ui_interact(user) diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm index c9e899bd85c5..4c10d5b86868 100644 --- a/code/game/gamemodes/cult/cult_structures.dm +++ b/code/game/gamemodes/cult/cult_structures.dm @@ -37,7 +37,7 @@ . = ..() START_PROCESSING(SSobj, src) -/obj/structure/cult/pylon/attack_hand(mob/user, list/params) +/obj/structure/cult/pylon/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) attackpylon(user, 5) /obj/structure/cult/pylon/attack_generic(var/mob/user, var/damage) diff --git a/code/game/gamemodes/cult/ritual.dm b/code/game/gamemodes/cult/ritual.dm index e2e982ddfe11..5f9c08ac0772 100644 --- a/code/game/gamemodes/cult/ritual.dm +++ b/code/game/gamemodes/cult/ritual.dm @@ -101,7 +101,7 @@ var/global/list/rnwords = list("ire","ego","nahlizet","certum","veri","jatkaa"," return -/obj/effect/rune/attack_hand(mob/user, list/params) +/obj/effect/rune/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!ishuman(user)) return var/mob/living/carbon/human/H = user diff --git a/code/game/machinery/CableLayer.dm b/code/game/machinery/CableLayer.dm index 0156cb84f94c..c70604f40cd3 100644 --- a/code/game/machinery/CableLayer.dm +++ b/code/game/machinery/CableLayer.dm @@ -17,7 +17,7 @@ . = ..() layCable(new_turf,M_Dir) -/obj/machinery/cablelayer/attack_hand(mob/user, list/params) +/obj/machinery/cablelayer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!cable&&!on) to_chat(user, SPAN_WARNING("\The [src] doesn't have any cable loaded.")) return diff --git a/code/game/machinery/OpTable.dm b/code/game/machinery/OpTable.dm index dea856fe44ae..839932093016 100644 --- a/code/game/machinery/OpTable.dm +++ b/code/game/machinery/OpTable.dm @@ -41,7 +41,7 @@ if(prob(25)) density = 0 -/obj/machinery/optable/attack_hand(mob/user, list/params) +/obj/machinery/optable/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(MUTATION_HULK in usr.mutations) visible_message(SPAN_DANGER("\The [usr] destroys \the [src]!")) density = FALSE diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index c894c4e47efd..88b51467ac46 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -37,7 +37,7 @@ /obj/machinery/sleep_console/attack_ai(mob/user) return attack_hand(user) -/obj/machinery/sleep_console/attack_hand(mob/user, list/params) +/obj/machinery/sleep_console/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return 1 diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index 3c8879162bf9..999f424386a8 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -288,7 +288,7 @@ return attack_hand(user) // todo: refactor -/obj/machinery/attack_hand(mob/user, list/params) +/obj/machinery/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_HARM) return ..() if(IsAdminGhost(user)) diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm index c694e19bbdd0..f5780aa48c7d 100644 --- a/code/game/machinery/adv_med.dm +++ b/code/game/machinery/adv_med.dm @@ -240,7 +240,7 @@ . = ..() return attack_hand(user) -/obj/machinery/body_scanconsole/attack_hand(mob/user, list/params) +/obj/machinery/body_scanconsole/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (NOPOWER|BROKEN)) return diff --git a/code/game/machinery/ai_slipper.dm b/code/game/machinery/ai_slipper.dm index 477852c0229c..d7b7a582f8dc 100644 --- a/code/game/machinery/ai_slipper.dm +++ b/code/game/machinery/ai_slipper.dm @@ -57,7 +57,7 @@ /obj/machinery/ai_slipper/attack_ai(mob/user as mob) return attack_hand(user) -/obj/machinery/ai_slipper/attack_hand(mob/user, list/params) +/obj/machinery/ai_slipper/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (NOPOWER|BROKEN)) return if((get_dist(src, user) > 1)) diff --git a/code/game/machinery/atm_ret_field.dm b/code/game/machinery/atm_ret_field.dm index 5190b86c6bb4..c49273e59647 100644 --- a/code/game/machinery/atm_ret_field.dm +++ b/code/game/machinery/atm_ret_field.dm @@ -221,7 +221,7 @@ update_nearby_tiles() //Force ZAS update . = ..() -/obj/structure/atmospheric_retention_field/attack_hand(mob/user, list/params) +/obj/structure/atmospheric_retention_field/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(density) to_chat(user, "You touch the retention field, and it crackles faintly. Tingly!") else diff --git a/code/game/machinery/atmo_control.dm b/code/game/machinery/atmo_control.dm index 44618bf10b65..5e548e8bb2ea 100644 --- a/code/game/machinery/atmo_control.dm +++ b/code/game/machinery/atmo_control.dm @@ -86,7 +86,7 @@ radio_controller.remove_object(src, frequency) ..() -/obj/machinery/computer/general_air_control/attack_hand(mob/user, list/params) +/obj/machinery/computer/general_air_control/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..(user)) return diff --git a/code/game/machinery/bioprinter.dm b/code/game/machinery/bioprinter.dm index 2b89e6e6b2e8..b9a8ce78f739 100644 --- a/code/game/machinery/bioprinter.dm +++ b/code/game/machinery/bioprinter.dm @@ -125,7 +125,7 @@ . = ..() -/obj/machinery/organ_printer/attack_hand(mob/user, list/params) +/obj/machinery/organ_printer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (BROKEN|NOPOWER)) return diff --git a/code/game/machinery/bomb_tester.dm b/code/game/machinery/bomb_tester.dm index 070d81a1fff5..935abaed349e 100644 --- a/code/game/machinery/bomb_tester.dm +++ b/code/game/machinery/bomb_tester.dm @@ -107,7 +107,7 @@ return ..() -/obj/machinery/bomb_tester/attack_hand(mob/user, list/params) +/obj/machinery/bomb_tester/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) ui_interact(user) diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index 2918472e4a30..9f7585241cac 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -18,7 +18,7 @@ /obj/machinery/button/attackby(obj/item/W, mob/user) return attack_hand(user) -/obj/machinery/button/attack_hand(obj/item/W, mob/user) +/obj/machinery/button/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return TRUE playsound(src, 'sound/machines/button.ogg', 100, TRUE) @@ -30,7 +30,7 @@ desc = "A remote control switch for polarized windows." var/range = 7 -/obj/machinery/button/windowtint/attack_hand(mob/user, list/params) +/obj/machinery/button/windowtint/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (..()) return TRUE else diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 26fa71139dcf..673b3de996f2 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -110,7 +110,7 @@ CREATE_WALL_MOUNTING_TYPES(/obj/machinery/camera) src.view_range = num GLOB.cameranet.updateVisibility(src, 0) -/obj/machinery/camera/attack_hand(mob/user, list/params) +/obj/machinery/camera/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/carbon/human/L = user if(!istype(L)) return diff --git a/code/game/machinery/camera/camera_assembly.dm b/code/game/machinery/camera/camera_assembly.dm index a851040377c9..2da823206971 100644 --- a/code/game/machinery/camera/camera_assembly.dm +++ b/code/game/machinery/camera/camera_assembly.dm @@ -145,7 +145,7 @@ else icon_state = "cameracase" -/obj/item/camera_assembly/attack_hand(mob/user, list/params) +/obj/item/camera_assembly/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!anchored) ..() diff --git a/code/game/machinery/cell_charger.dm b/code/game/machinery/cell_charger.dm index 813b3c83f07b..0beca8c9766c 100644 --- a/code/game/machinery/cell_charger.dm +++ b/code/game/machinery/cell_charger.dm @@ -81,7 +81,7 @@ else if(default_part_replacement(user, W)) return -/obj/machinery/cell_charger/attack_hand(mob/user, list/params) +/obj/machinery/cell_charger/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) if(charging) diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index f099b8ffa241..3f04bf3cb143 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -60,7 +60,7 @@ add_hiddenprint(user) return attack_hand(user) -/obj/machinery/clonepod/attack_hand(mob/user, list/params) +/obj/machinery/clonepod/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((isnull(occupant)) || (machine_stat & NOPOWER)) return if((!isnull(occupant)) && (occupant.stat != 2)) diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm index e73cb842326b..853f74f96f03 100644 --- a/code/game/machinery/computer/Operating.dm +++ b/code/game/machinery/computer/Operating.dm @@ -48,7 +48,7 @@ return ui_interact(user) -/obj/machinery/computer/operating/attack_hand(mob/user, list/params) +/obj/machinery/computer/operating/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (BROKEN|NOPOWER)) return ui_interact(user) diff --git a/code/game/machinery/computer/RCON_Console.dm b/code/game/machinery/computer/RCON_Console.dm index 6dae0a2772ec..68d751075a29 100644 --- a/code/game/machinery/computer/RCON_Console.dm +++ b/code/game/machinery/computer/RCON_Console.dm @@ -26,7 +26,7 @@ // Proc: attack_hand() // Parameters: 1 (user - Person which clicked this computer) // Description: Opens UI of this machine. -/obj/machinery/computer/rcon/attack_hand(mob/user, list/params) +/obj/machinery/computer/rcon/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() ui_interact(user) diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm index ff694a8bba40..43b425dfcfa8 100644 --- a/code/game/machinery/computer/aifixer.dm +++ b/code/game/machinery/computer/aifixer.dm @@ -48,7 +48,7 @@ to_chat(user, SPAN_NOTICE("There is no AI loaded onto this computer, and no AI loaded onto [I]. What exactly are you trying to do here?")) return ..() -/obj/machinery/computer/aifixer/attack_hand(mob/user, list/params) +/obj/machinery/computer/aifixer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (NOPOWER|BROKEN)) return ui_interact(user) diff --git a/code/game/machinery/computer/arcade/amputation.dm b/code/game/machinery/computer/arcade/amputation.dm index 3725192b5169..a978a40bdf79 100644 --- a/code/game/machinery/computer/arcade/amputation.dm +++ b/code/game/machinery/computer/arcade/amputation.dm @@ -7,7 +7,7 @@ icon_screen = null circuit = /obj/item/circuitboard/arcade/amputation -/obj/machinery/computer/arcade/amputation/attack_hand(mob/user, list/modifiers) +/obj/machinery/computer/arcade/amputation/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!iscarbon(user)) return var/mob/living/carbon/c_user = user diff --git a/code/game/machinery/computer/arcade/claw_machine.dm b/code/game/machinery/computer/arcade/claw_machine.dm index 9d0e3bc741ff..30b76f6a3f66 100644 --- a/code/game/machinery/computer/arcade/claw_machine.dm +++ b/code/game/machinery/computer/arcade/claw_machine.dm @@ -160,7 +160,7 @@ /obj/machinery/computer/arcade/clawmachine/Initialize(mapload) . = ..() -/obj/machinery/computer/arcade/clawmachine/attack_hand(mob/user, list/params) +/obj/machinery/computer/arcade/clawmachine/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return ui_interact(user) diff --git a/code/game/machinery/computer/atmos_alert.dm b/code/game/machinery/computer/atmos_alert.dm index d22cfb98a8c2..d0b1000e0724 100644 --- a/code/game/machinery/computer/atmos_alert.dm +++ b/code/game/machinery/computer/atmos_alert.dm @@ -19,7 +19,7 @@ var/global/list/minor_air_alarms = list() atmosphere_alarm.unregister_alarm(src) return ..() -/obj/machinery/computer/atmos_alert/attack_hand(mob/user, list/params) +/obj/machinery/computer/atmos_alert/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /obj/machinery/computer/atmos_alert/ui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) diff --git a/code/game/machinery/computer/atmos_control.dm b/code/game/machinery/computer/atmos_control.dm index 4bda92467aba..edf5ecf561a9 100644 --- a/code/game/machinery/computer/atmos_control.dm +++ b/code/game/machinery/computer/atmos_control.dm @@ -33,7 +33,7 @@ /obj/machinery/computer/atmoscontrol/attack_ai(mob/user) ui_interact(user) -/obj/machinery/computer/atmoscontrol/attack_hand(mob/user, list/params) +/obj/machinery/computer/atmoscontrol/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return TRUE ui_interact(user) diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm index 3748b3970ce3..b7630fc39d64 100644 --- a/code/game/machinery/computer/camera.dm +++ b/code/game/machinery/computer/camera.dm @@ -28,7 +28,7 @@ /obj/machinery/computer/security/ui_interact(mob/user, datum/tgui/ui = null) camera.ui_interact(user, ui) -/obj/machinery/computer/security/attack_hand(mob/user, list/params) +/obj/machinery/computer/security/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) if(machine_stat & (BROKEN|NOPOWER)) return diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index cc7b57599f99..906da036bf0f 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -94,7 +94,7 @@ /obj/machinery/computer/cloning/attack_ai(mob/user as mob) return attack_hand(user) -/obj/machinery/computer/cloning/attack_hand(mob/user, list/params) +/obj/machinery/computer/cloning/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.set_machine(src) add_fingerprint(user) diff --git a/code/game/machinery/computer/command/card.dm b/code/game/machinery/computer/command/card.dm index 2428138350ee..d306d3bd22d5 100644 --- a/code/game/machinery/computer/command/card.dm +++ b/code/game/machinery/computer/command/card.dm @@ -97,7 +97,7 @@ /obj/machinery/computer/card/attack_ai(var/mob/user as mob) return attack_hand(user) -/obj/machinery/computer/card/attack_hand(mob/user, list/params) +/obj/machinery/computer/card/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return if(machine_stat & (NOPOWER|BROKEN)) diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index 401779e66468..afe0b69ef650 100644 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -296,7 +296,7 @@ /obj/machinery/computer/communications/attack_ai(var/mob/user as mob) return src.attack_hand(user) -/obj/machinery/computer/communications/attack_hand(mob/user, list/params) +/obj/machinery/computer/communications/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return if ((LEGACY_MAP_DATUM) && !(src.z in (LEGACY_MAP_DATUM).contact_levels)) diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index 72bdbcf6831e..8c1265ef08e6 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -23,7 +23,7 @@ /obj/machinery/computer/crew/attack_ai(mob/user) attack_hand(user) -/obj/machinery/computer/crew/attack_hand(mob/user, list/params) +/obj/machinery/computer/crew/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() if(machine_stat & (BROKEN|NOPOWER)) return diff --git a/code/game/machinery/computer/law.dm b/code/game/machinery/computer/law.dm index f6d34ea319fd..63601b4ab0e3 100644 --- a/code/game/machinery/computer/law.dm +++ b/code/game/machinery/computer/law.dm @@ -35,7 +35,7 @@ ..() -/obj/machinery/computer/aiupload/attack_hand(mob/user, list/params) +/obj/machinery/computer/aiupload/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & NOPOWER) to_chat(user, "The upload computer has no power!") return @@ -71,7 +71,7 @@ return ..() -/obj/machinery/computer/borgupload/attack_hand(mob/user, list/params) +/obj/machinery/computer/borgupload/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat& NOPOWER) to_chat(user, "The upload computer has no power!") return diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm index 40f65b2fb923..5705c2a9dd3a 100644 --- a/code/game/machinery/computer/medical.dm +++ b/code/game/machinery/computer/medical.dm @@ -101,7 +101,7 @@ /obj/machinery/computer/med_data/attack_ai(user as mob) return attack_hand(user) -/obj/machinery/computer/med_data/attack_hand(mob/user, list/params) +/obj/machinery/computer/med_data/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return diff --git a/code/game/machinery/computer/message.dm b/code/game/machinery/computer/message.dm index d237215ad181..b14ff4229d32 100644 --- a/code/game/machinery/computer/message.dm +++ b/code/game/machinery/computer/message.dm @@ -78,7 +78,7 @@ linkedServer = message_servers[1] return ..() -/obj/machinery/computer/message_monitor/attack_hand(mob/user, list/params) +/obj/machinery/computer/message_monitor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (NOPOWER|BROKEN)) return if(!istype(user)) diff --git a/code/game/machinery/computer/pod.dm b/code/game/machinery/computer/pod.dm index ebeb9a383006..ee3abe342b34 100644 --- a/code/game/machinery/computer/pod.dm +++ b/code/game/machinery/computer/pod.dm @@ -46,7 +46,7 @@ /obj/machinery/computer/pod/attack_ai(var/mob/user as mob) return attack_hand(user) -/obj/machinery/computer/pod/attack_hand(mob/user, list/params) +/obj/machinery/computer/pod/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return @@ -142,7 +142,7 @@ title = "External Airlock Controls" req_access = list(ACCESS_FACTION_SYNDICATE) -/obj/machinery/computer/pod/old/syndicate/attack_hand(mob/user, list/params) +/obj/machinery/computer/pod/old/syndicate/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!allowed(user)) to_chat(user, "Access Denied") return diff --git a/code/game/machinery/computer/prisoner.dm b/code/game/machinery/computer/prisoner.dm index 9171a6cf6115..e1acca8293c5 100644 --- a/code/game/machinery/computer/prisoner.dm +++ b/code/game/machinery/computer/prisoner.dm @@ -16,7 +16,7 @@ /obj/machinery/computer/prisoner/attack_ai(var/mob/user as mob) return src.attack_hand(user) -/obj/machinery/computer/prisoner/attack_hand(mob/user, list/params) +/obj/machinery/computer/prisoner/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return ui_interact(user) diff --git a/code/game/machinery/computer/prisonshuttle.dm b/code/game/machinery/computer/prisonshuttle.dm index 398b877bec3e..04726d584174 100644 --- a/code/game/machinery/computer/prisonshuttle.dm +++ b/code/game/machinery/computer/prisonshuttle.dm @@ -27,7 +27,7 @@ var/prison_shuttle_timeleft = 0 /obj/machinery/computer/prison_shuttle/attack_ai(var/mob/user as mob) return src.attack_hand(user) -/obj/machinery/computer/prison_shuttle/attack_hand(mob/user, list/params) +/obj/machinery/computer/prison_shuttle/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!src.allowed(user) && (!hacked)) to_chat(user, "Access Denied.") return diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm index 477ab3ec77a0..db382db147eb 100644 --- a/code/game/machinery/computer/robot.dm +++ b/code/game/machinery/computer/robot.dm @@ -11,7 +11,7 @@ /obj/machinery/computer/robotics/attack_ai(var/mob/user as mob) ui_interact(user) -/obj/machinery/computer/robotics/attack_hand(mob/user, list/params) +/obj/machinery/computer/robotics/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return if(machine_stat & (NOPOWER|BROKEN)) diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm index 981fc2fdfd99..70c3c201074d 100644 --- a/code/game/machinery/computer/security.dm +++ b/code/game/machinery/computer/security.dm @@ -87,7 +87,7 @@ /obj/machinery/computer/secure_data/attack_ai(mob/user as mob) return attack_hand(user) -/obj/machinery/computer/secure_data/attack_hand(mob/user, list/params) +/obj/machinery/computer/secure_data/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return add_fingerprint(user) diff --git a/code/game/machinery/computer/shutoff_monitor.dm b/code/game/machinery/computer/shutoff_monitor.dm index aca1c4988522..d362c764390e 100644 --- a/code/game/machinery/computer/shutoff_monitor.dm +++ b/code/game/machinery/computer/shutoff_monitor.dm @@ -15,7 +15,7 @@ QDEL_NULL(monitor) ..() -/obj/machinery/computer/shutoff_monitor/attack_hand(mob/user, list/params) +/obj/machinery/computer/shutoff_monitor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() monitor.ui_interact(user) diff --git a/code/game/machinery/computer/skills.dm b/code/game/machinery/computer/skills.dm index e026411a300e..de865888aaab 100644 --- a/code/game/machinery/computer/skills.dm +++ b/code/game/machinery/computer/skills.dm @@ -65,7 +65,7 @@ return attack_hand(user) //Someone needs to break down the dat += into chunks instead of long ass lines. -/obj/machinery/computer/skills/attack_hand(mob/user, list/params) +/obj/machinery/computer/skills/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return if ((LEGACY_MAP_DATUM) && !(src.z in (LEGACY_MAP_DATUM).contact_levels)) diff --git a/code/game/machinery/computer/specops_shuttle.dm b/code/game/machinery/computer/specops_shuttle.dm index 4b565b2ae2e2..5814dbec0544 100644 --- a/code/game/machinery/computer/specops_shuttle.dm +++ b/code/game/machinery/computer/specops_shuttle.dm @@ -254,7 +254,7 @@ var/specops_shuttle_timeleft = 0 /obj/machinery/computer/specops_shuttle/emag_act(var/remaining_charges, var/mob/user) to_chat(user, "The electronic systems in this console are far too advanced for your primitive hacking peripherals.") -/obj/machinery/computer/specops_shuttle/attack_hand(mob/user, list/params) +/obj/machinery/computer/specops_shuttle/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!allowed(user)) to_chat(user, "Access Denied.") return diff --git a/code/game/machinery/computer/station_alert.dm b/code/game/machinery/computer/station_alert.dm index 35b4b3187a4e..4f0ad22ba130 100644 --- a/code/game/machinery/computer/station_alert.dm +++ b/code/game/machinery/computer/station_alert.dm @@ -34,7 +34,7 @@ ui_interact(user) return -/obj/machinery/computer/station_alert/attack_hand(mob/user, list/params) +/obj/machinery/computer/station_alert/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) if(machine_stat & (BROKEN|NOPOWER)) return diff --git a/code/game/machinery/computer/supermatter_monitor_tgui.dm b/code/game/machinery/computer/supermatter_monitor_tgui.dm index c033af582418..6aaf789af6bc 100644 --- a/code/game/machinery/computer/supermatter_monitor_tgui.dm +++ b/code/game/machinery/computer/supermatter_monitor_tgui.dm @@ -17,7 +17,7 @@ /obj/machinery/computer/sm_monitor/attack_ai(mob/user) ui_interact(user) -/obj/machinery/computer/sm_monitor/attack_hand(mob/user, list/params) +/obj/machinery/computer/sm_monitor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return TRUE ui_interact(user) diff --git a/code/game/machinery/computer/supply.dm b/code/game/machinery/computer/supply.dm index 00e2d24145e2..840549abf934 100644 --- a/code/game/machinery/computer/supply.dm +++ b/code/game/machinery/computer/supply.dm @@ -35,7 +35,7 @@ /obj/machinery/computer/supplycomp/attack_ai(var/mob/user as mob) return attack_hand(user) -/obj/machinery/computer/supplycomp/attack_hand(mob/user, list/params) +/obj/machinery/computer/supplycomp/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return if(!allowed(user)) diff --git a/code/game/machinery/computer/syndicate_specops_shuttle.dm b/code/game/machinery/computer/syndicate_specops_shuttle.dm index 1e681eb33c87..306a44b3afff 100644 --- a/code/game/machinery/computer/syndicate_specops_shuttle.dm +++ b/code/game/machinery/computer/syndicate_specops_shuttle.dm @@ -191,7 +191,7 @@ var/syndicate_elite_shuttle_timeleft = 0 /obj/machinery/computer/syndicate_elite_shuttle/emag_act(var/remaining_charges, var/mob/user) to_chat(user, "The electronic systems in this console are far too advanced for your primitive hacking peripherals.") -/obj/machinery/computer/syndicate_elite_shuttle/attack_hand(mob/user, list/params) +/obj/machinery/computer/syndicate_elite_shuttle/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!allowed(user)) to_chat(user, "Access Denied.") return diff --git a/code/game/machinery/computer/timeclock_vr.dm b/code/game/machinery/computer/timeclock_vr.dm index 79bc9bbfb951..55f5bf30f726 100644 --- a/code/game/machinery/computer/timeclock_vr.dm +++ b/code/game/machinery/computer/timeclock_vr.dm @@ -65,7 +65,7 @@ return . = ..() -/obj/machinery/computer/timeclock/attack_hand(mob/user, list/params) +/obj/machinery/computer/timeclock/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return user.set_machine(src) diff --git a/code/game/machinery/cryo.dm b/code/game/machinery/cryo.dm index 237dd9408a77..6f4ef4ab70b5 100644 --- a/code/game/machinery/cryo.dm +++ b/code/game/machinery/cryo.dm @@ -82,7 +82,7 @@ if(occupant == user && !user.stat) go_out() -/obj/machinery/atmospherics/component/unary/cryo_cell/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/component/unary/cryo_cell/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) nano_ui_interact(user) /** diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index afed66b803fb..4b803e871166 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -99,7 +99,7 @@ /obj/machinery/computer/cryopod/attack_ai() attack_hand() -/obj/machinery/computer/cryopod/attack_hand(mob/user = usr) +/obj/machinery/computer/cryopod/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (NOPOWER|BROKEN)) return ..() diff --git a/code/game/machinery/deployable_vr.dm b/code/game/machinery/deployable_vr.dm index 55b62ce1844e..3b50fc26d7c7 100644 --- a/code/game/machinery/deployable_vr.dm +++ b/code/game/machinery/deployable_vr.dm @@ -63,7 +63,7 @@ . = ..() untopple() -/obj/structure/barricade/cutout/attack_hand(mob/user, list/params) +/obj/structure/barricade/cutout/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((. = ..())) return diff --git a/code/game/machinery/door_control.dm b/code/game/machinery/door_control.dm index 648181e273e4..7280a965dcc8 100644 --- a/code/game/machinery/door_control.dm +++ b/code/game/machinery/door_control.dm @@ -34,7 +34,7 @@ playsound(src.loc, /datum/soundbyte/grouped/sparks, 100, TRUE) return 1 -/obj/machinery/button/remote/attack_hand(mob/user, list/params) +/obj/machinery/button/remote/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return diff --git a/code/game/machinery/doorbell_vr.dm b/code/game/machinery/doorbell_vr.dm index 1379236cf3d4..c2bd4e3c45ed 100644 --- a/code/game/machinery/doorbell_vr.dm +++ b/code/game/machinery/doorbell_vr.dm @@ -111,7 +111,7 @@ else icon_state = "doorbell-standby" -/obj/machinery/button/doorbell/attack_hand(mob/user, list/params) +/obj/machinery/button/doorbell/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) if(..()) return diff --git a/code/game/machinery/doors/airlock/airlock.dm b/code/game/machinery/doors/airlock/airlock.dm index 541fb37f2597..7fd4d8f73dfe 100644 --- a/code/game/machinery/doors/airlock/airlock.dm +++ b/code/game/machinery/doors/airlock/airlock.dm @@ -646,7 +646,7 @@ About the new airlock wires panel: last_spark = world.time return ..() -/obj/machinery/door/airlock/attack_hand(mob/user, list/params) +/obj/machinery/door/airlock/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!istype(usr, /mob/living/silicon)) if(src.isElectrified()) if(src.shock(user, 100)) diff --git a/code/game/machinery/doors/airlock/airlock_control.dm b/code/game/machinery/doors/airlock/airlock_control.dm index c71c236d47e5..9fffbae73405 100644 --- a/code/game/machinery/doors/airlock/airlock_control.dm +++ b/code/game/machinery/doors/airlock/airlock_control.dm @@ -177,7 +177,7 @@ else icon_state = "airlock_sensor_off" -/obj/machinery/airlock_sensor/attack_hand(mob/user, list/params) +/obj/machinery/airlock_sensor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/datum/signal/signal = new signal.transmission_method = TRANSMISSION_RADIO //radio signal signal.data["tag"] = master_tag @@ -281,7 +281,7 @@ return ..() -/obj/machinery/access_button/attack_hand(mob/user, list/params) +/obj/machinery/access_button/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() if(!allowed(user)) to_chat(user, "Access Denied") diff --git a/code/game/machinery/doors/blast_door.dm b/code/game/machinery/doors/blast_door.dm index b9041727b7d7..0e7fa26bc677 100644 --- a/code/game/machinery/doors/blast_door.dm +++ b/code/game/machinery/doors/blast_door.dm @@ -116,7 +116,7 @@ //Proc: attack_hand //Description: Attacked with empty hand. Only to allow special attack_bys. -/obj/machinery/door/blast/attack_hand(mob/user, list/params) +/obj/machinery/door/blast/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(istype(user, /mob/living/carbon/human)) var/mob/living/carbon/human/X = user if(istype(X.species, /datum/species/xenos)) diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 9dab9b833db9..0042f37be31e 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -155,7 +155,7 @@ /obj/machinery/door/attack_ai(mob/user as mob) return src.attack_hand(user) -/obj/machinery/door/attack_hand(mob/user, list/params) +/obj/machinery/door/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_HARM) return ..() return src.attackby(user, user) diff --git a/code/game/machinery/doors/door_timer.dm b/code/game/machinery/doors/door_timer.dm index e45245f39a8a..cd5690419e7b 100644 --- a/code/game/machinery/doors/door_timer.dm +++ b/code/game/machinery/doors/door_timer.dm @@ -160,7 +160,7 @@ /obj/machinery/door_timer/attack_ai(mob/user) return src.attack_hand(user) -/obj/machinery/door_timer/attack_hand(mob/user, list/params) +/obj/machinery/door_timer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return TRUE ui_interact(user) diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index 0000e5c416a8..ebe353bb3e0d 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -170,7 +170,7 @@ GLOBAL_LIST_INIT(firelock_align_types, typecacheof(list( attack_hand(M) return 0 -/obj/machinery/door/firedoor/attack_hand(mob/user, list/params) +/obj/machinery/door/firedoor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) if(operating) return//Already doing something. diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 95921c715c2f..bf8007ae0194 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -145,7 +145,7 @@ /obj/machinery/door/window/attack_ai(mob/user as mob) return src.attack_hand(user) -/obj/machinery/door/window/attack_hand(mob/user, list/params) +/obj/machinery/door/window/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_HARM) return ..() src.add_fingerprint(user) diff --git a/code/game/machinery/embedded_controller/embedded_controller_base.dm b/code/game/machinery/embedded_controller/embedded_controller_base.dm index 5cc1846c54aa..137e78cdbbbf 100644 --- a/code/game/machinery/embedded_controller/embedded_controller_base.dm +++ b/code/game/machinery/embedded_controller/embedded_controller_base.dm @@ -48,7 +48,7 @@ /obj/machinery/embedded_controller/attack_ai(mob/user as mob) ui_interact(user) -/obj/machinery/embedded_controller/attack_hand(mob/user, list/params) +/obj/machinery/embedded_controller/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!user.IsAdvancedToolUser()) return 0 diff --git a/code/game/machinery/event.dm b/code/game/machinery/event.dm index aabe77a9a12f..8fc316d2498f 100644 --- a/code/game/machinery/event.dm +++ b/code/game/machinery/event.dm @@ -65,7 +65,7 @@ The goal here is to create esoteric or niche, specialized machines that follow t else return -/obj/machinery/magma_pump/attack_hand(mob/user, list/params) +/obj/machinery/magma_pump/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) interact(user) /obj/machinery/magma_pump/interact(mob/user) @@ -211,7 +211,7 @@ The goal here is to create esoteric or niche, specialized machines that follow t else return -/obj/machinery/magma_reservoir/attack_hand(mob/user, list/params) +/obj/machinery/magma_reservoir/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) interact(user) /obj/machinery/magma_reservoir/interact(mob/user) diff --git a/code/game/machinery/exonet_node.dm b/code/game/machinery/exonet_node.dm index fb6c5c49b8a2..ac1e50634956 100644 --- a/code/game/machinery/exonet_node.dm +++ b/code/game/machinery/exonet_node.dm @@ -103,7 +103,7 @@ GLOBAL_LIST_EMPTY(exonet_nodes) // Proc: attack_hand() // Parameters: 1 (user - the person clicking on the machine) // Description: Opens the TGUI interface with ui_interact() -/obj/machinery/exonet_node/attack_hand(mob/user, list/params) +/obj/machinery/exonet_node/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) // Proc: ui_interact() diff --git a/code/game/machinery/fire_alarm.dm b/code/game/machinery/fire_alarm.dm index 40757b9a3aae..edce90384ca9 100644 --- a/code/game/machinery/fire_alarm.dm +++ b/code/game/machinery/fire_alarm.dm @@ -178,7 +178,7 @@ CREATE_WALL_MOUNTING_TYPES_SHIFTED(/obj/machinery/fire_alarm/alarms_hidden, 21) spawn(rand(0,15)) update_icon() -/obj/machinery/fire_alarm/attack_hand(mob/user, list/params) +/obj/machinery/fire_alarm/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.stat || machine_stat & (NOPOWER | BROKEN)) return diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm index 0dc55edd94e7..fd2df07fd4a6 100644 --- a/code/game/machinery/flasher.dm +++ b/code/game/machinery/flasher.dm @@ -130,7 +130,7 @@ name = "flasher button" desc = "A remote control switch for a mounted flasher." -/obj/machinery/button/flasher/attack_hand(mob/user, list/params) +/obj/machinery/button/flasher/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return diff --git a/code/game/machinery/floodlight.dm b/code/game/machinery/floodlight.dm index fd2ba4b08a02..8548bc99021d 100644 --- a/code/game/machinery/floodlight.dm +++ b/code/game/machinery/floodlight.dm @@ -67,7 +67,7 @@ if(!turn_on(TRUE)) to_chat(user, "You try to turn on \the [src] but it does not work.") -/obj/machinery/floodlight/attack_hand(mob/user, list/params) +/obj/machinery/floodlight/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(open && cell) user.grab_item_from_interacted_with(cell, src) diff --git a/code/game/machinery/floor_inflatables.dm b/code/game/machinery/floor_inflatables.dm index 7e87f68451a8..2a79c18e4d31 100644 --- a/code/game/machinery/floor_inflatables.dm +++ b/code/game/machinery/floor_inflatables.dm @@ -47,7 +47,7 @@ else to_chat(user, "[C] is to large for [src]") -/obj/machinery/floor_inflatables/attack_hand(mob/user, list/params) +/obj/machinery/floor_inflatables/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() var/confirm = input(user, "Do you want to trigger [src]'s deployment?","Trigger Floormount") as null|anything in list("Yes","No") if(confirm && confirm == "Yes") diff --git a/code/game/machinery/floor_light.dm b/code/game/machinery/floor_light.dm index 510b838e4900..6975c0a4f182 100644 --- a/code/game/machinery/floor_light.dm +++ b/code/game/machinery/floor_light.dm @@ -62,7 +62,7 @@ var/list/floor_light_cache = list() attack_hand(user) return -/obj/machinery/floor_light/attack_hand(mob/user, list/params) +/obj/machinery/floor_light/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_HARM && !issmall(user)) if(!isnull(damaged) && !(machine_stat & BROKEN)) diff --git a/code/game/machinery/floorlayer.dm b/code/game/machinery/floorlayer.dm index f2cb0f8e2c78..c52162902c1c 100644 --- a/code/game/machinery/floorlayer.dm +++ b/code/game/machinery/floorlayer.dm @@ -32,7 +32,7 @@ old_turf = isturf(loc)? loc : null -/obj/machinery/floorlayer/attack_hand(mob/user, list/params) +/obj/machinery/floorlayer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) on=!on user.visible_message( \ SPAN_NOTICE("[user] has [!on?"de":""]activated \the [src]."), \ diff --git a/code/game/machinery/holosign.dm b/code/game/machinery/holosign.dm index 4208e4973304..7b28e9b6766d 100644 --- a/code/game/machinery/holosign.dm +++ b/code/game/machinery/holosign.dm @@ -59,7 +59,7 @@ icon = 'icons/obj/power.dmi' icon_state = "crema_switch" -/obj/machinery/button/holosign/attack_hand(mob/user, list/params) +/obj/machinery/button/holosign/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return diff --git a/code/game/machinery/igniter.dm b/code/game/machinery/igniter.dm index a4d038ddb312..21b86283ba57 100644 --- a/code/game/machinery/igniter.dm +++ b/code/game/machinery/igniter.dm @@ -13,7 +13,7 @@ /obj/machinery/igniter/attack_ai(mob/user) return attack_hand(user) -/obj/machinery/igniter/attack_hand(mob/user, list/params) +/obj/machinery/igniter/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return @@ -121,7 +121,7 @@ name = "ignition switch" desc = "A remote control switch for a mounted igniter." -/obj/machinery/button/ignition/attack_hand(mob/user, list/params) +/obj/machinery/button/ignition/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm index c2d4b3020add..c75979412d70 100644 --- a/code/game/machinery/iv_drip.dm +++ b/code/game/machinery/iv_drip.dm @@ -166,7 +166,7 @@ else return ..() -/obj/machinery/iv_drip/attack_hand(mob/user, list/params) +/obj/machinery/iv_drip/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(reagent_container) reagent_container.loc = get_turf(src) reagent_container = null diff --git a/code/game/machinery/jukebox.dm b/code/game/machinery/jukebox.dm index 25758ac5be7d..0d687a25a999 100644 --- a/code/game/machinery/jukebox.dm +++ b/code/game/machinery/jukebox.dm @@ -273,7 +273,7 @@ /obj/machinery/media/jukebox/attack_ai(mob/user) return src.attack_hand(user) -/obj/machinery/media/jukebox/attack_hand(mob/user, list/params) +/obj/machinery/media/jukebox/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) interact(user) /obj/machinery/media/jukebox/proc/explode() diff --git a/code/game/machinery/lightswitch.dm b/code/game/machinery/lightswitch.dm index 21a0dddf6323..4a44849248b9 100644 --- a/code/game/machinery/lightswitch.dm +++ b/code/game/machinery/lightswitch.dm @@ -52,7 +52,7 @@ /obj/machinery/light_switch/examine(mob/user, dist) . += SPAN_NOTICE("A light switch. It is [on? "on" : "off"].") -/obj/machinery/light_switch/attack_hand(mob/user, list/params) +/obj/machinery/light_switch/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) on = !on diff --git a/code/game/machinery/magnet.dm b/code/game/machinery/magnet.dm index 00c9426b463c..a9f4e837fa54 100644 --- a/code/game/machinery/magnet.dm +++ b/code/game/machinery/magnet.dm @@ -248,7 +248,7 @@ /obj/machinery/magnetic_controller/attack_ai(mob/user) return attack_hand(user) -/obj/machinery/magnetic_controller/attack_hand(mob/user, list/params) +/obj/machinery/magnetic_controller/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (BROKEN|NOPOWER)) return user.set_machine(src) diff --git a/code/game/machinery/misc/bioscan_antenna.dm b/code/game/machinery/misc/bioscan_antenna.dm index 15dab56fba6a..22da7d056c67 100644 --- a/code/game/machinery/misc/bioscan_antenna.dm +++ b/code/game/machinery/misc/bioscan_antenna.dm @@ -58,7 +58,7 @@ GLOBAL_LIST_EMPTY(bioscan_antenna_list) .[TOOL_MULTITOOL] = "change network" return merge_double_lazy_assoc_list(., ..()) -/obj/machinery/bioscan_antenna/attack_hand(mob/user, list/params) +/obj/machinery/bioscan_antenna/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) // todo: better xenomorphs if(ishuman(user)) var/mob/living/carbon/human/H = user diff --git a/code/game/machinery/navbeacon.dm b/code/game/machinery/navbeacon.dm index 2f46990e8c84..5bc31cd85761 100644 --- a/code/game/machinery/navbeacon.dm +++ b/code/game/machinery/navbeacon.dm @@ -92,7 +92,7 @@ var/global/list/navbeacons = list() // no I don't like putting this in, but it w /obj/machinery/navbeacon/attack_ai(var/mob/user) interact(user, 1) -/obj/machinery/navbeacon/attack_hand(mob/user, list/params) +/obj/machinery/navbeacon/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!user.IsAdvancedToolUser()) return FALSE diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index 9846a08a5260..45445346e415 100644 --- a/code/game/machinery/newscaster.dm +++ b/code/game/machinery/newscaster.dm @@ -250,7 +250,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co /obj/machinery/newscaster/attack_ai(mob/user) return attack_hand(user) -/obj/machinery/newscaster/attack_hand(mob/user, list/params) //########### THE MAIN BEEF IS HERE! And in the proc below this...############ +/obj/machinery/newscaster/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!ispowered || isbroken) return diff --git a/code/game/machinery/nuclear_bomb.dm b/code/game/machinery/nuclear_bomb.dm index d7f7a571d1d5..836768f4b54a 100644 --- a/code/game/machinery/nuclear_bomb.dm +++ b/code/game/machinery/nuclear_bomb.dm @@ -162,7 +162,7 @@ var/bomb_set return ..() -/obj/machinery/nuclearbomb/attack_hand(mob/user, list/params) +/obj/machinery/nuclearbomb/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(extended) if(!ishuman(user)) to_chat(user, "You don't have the dexterity to do this!") diff --git a/code/game/machinery/oxygen_pump.dm b/code/game/machinery/oxygen_pump.dm index 05cdf8d22a0d..6c7cd305216f 100644 --- a/code/game/machinery/oxygen_pump.dm +++ b/code/game/machinery/oxygen_pump.dm @@ -51,7 +51,7 @@ attach_mask(target) src.add_fingerprint(usr) -/obj/machinery/oxygen_pump/attack_hand(mob/user, list/params) +/obj/machinery/oxygen_pump/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((machine_stat & MAINT) && tank) user.visible_message( \ SPAN_NOTICE("\The [user] removes \the [tank] from \the [src]."), \ diff --git a/code/game/machinery/pda_multicaster.dm b/code/game/machinery/pda_multicaster.dm index 324fc042db0d..7273710bbf41 100644 --- a/code/game/machinery/pda_multicaster.dm +++ b/code/game/machinery/pda_multicaster.dm @@ -46,7 +46,7 @@ /obj/machinery/pda_multicaster/attack_ai(mob/user) attack_hand(user) -/obj/machinery/pda_multicaster/attack_hand(mob/user, list/params) +/obj/machinery/pda_multicaster/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) toggle_power(user) /obj/machinery/pda_multicaster/proc/toggle_power(mob/user) diff --git a/code/game/machinery/pipe/pipe_dispenser.dm b/code/game/machinery/pipe/pipe_dispenser.dm index 57c717216504..d20efd83cccc 100644 --- a/code/game/machinery/pipe/pipe_dispenser.dm +++ b/code/game/machinery/pipe/pipe_dispenser.dm @@ -12,7 +12,7 @@ var/disposals = FALSE -/obj/machinery/pipedispenser/attack_hand(mob/user, list/params) +/obj/machinery/pipedispenser/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((. = ..())) return ui_interact(user) diff --git a/code/game/machinery/pointdefense.dm b/code/game/machinery/pointdefense.dm index 9fb3e3b69396..7be2b8d14300 100644 --- a/code/game/machinery/pointdefense.dm +++ b/code/game/machinery/pointdefense.dm @@ -39,7 +39,7 @@ GLOBAL_LIST_BOILERPLATE(pointdefense_turrets, /obj/machinery/power/pointdefense) ui = new(user, src, "PointDefenseControl") // 400, 600 ui.open() -/obj/machinery/pointdefense_control/attack_hand(mob/user, list/params) +/obj/machinery/pointdefense_control/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return TRUE ui_interact(user) diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm index b016f4d0a5c3..5bddf8fc4db8 100644 --- a/code/game/machinery/recharger.dm +++ b/code/game/machinery/recharger.dm @@ -101,7 +101,7 @@ else if(default_part_replacement(user, G)) return -/obj/machinery/recharger/attack_hand(mob/user, list/params) +/obj/machinery/recharger/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(istype(user,/mob/living/silicon)) return diff --git a/code/game/machinery/recipe_lookup.dm b/code/game/machinery/recipe_lookup.dm index 3116a8e8ea5c..8fe6954bd83a 100644 --- a/code/game/machinery/recipe_lookup.dm +++ b/code/game/machinery/recipe_lookup.dm @@ -9,7 +9,7 @@ /obj/machinery/recipe_lookup/proc/display_options() return tgui_input_list(usr, "Pick a [result_type_name] to view its recipe.", "Select [result_type_name]", recipe_list) -/obj/machinery/recipe_lookup/attack_hand(mob/user) +/obj/machinery/recipe_lookup/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) // regular use checks if (!isliving(user)) return FALSE diff --git a/code/game/machinery/records_scanner.dm b/code/game/machinery/records_scanner.dm index 5ba7d9db0a76..6c1ac78a9fe3 100644 --- a/code/game/machinery/records_scanner.dm +++ b/code/game/machinery/records_scanner.dm @@ -36,7 +36,7 @@ obj/machinery/scanner else icon_state = "scanner_idle" -obj/machinery/scanner/attack_hand(mob/user, list/params) +obj/machinery/scanner/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(stat & NOPOWER) return if(!ishuman(user) || lastuser == user.real_name) diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm index 3c24ce2293e7..3f7b01c32662 100644 --- a/code/game/machinery/requests_console.dm +++ b/code/game/machinery/requests_console.dm @@ -119,7 +119,7 @@ var/list/obj/machinery/requests_console/allConsoles = list() req_console_information -= department return ..() -/obj/machinery/requests_console/attack_hand(mob/user, list/params) +/obj/machinery/requests_console/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..(user)) return nano_ui_interact(user) diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm index 6dad1ded03ec..4f2fd5f44d85 100644 --- a/code/game/machinery/spaceheater.dm +++ b/code/game/machinery/spaceheater.dm @@ -74,7 +74,7 @@ ..() return -/obj/machinery/space_heater/attack_hand(mob/user, list/params) +/obj/machinery/space_heater/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) interact(user) /obj/machinery/space_heater/interact(mob/user as mob) @@ -232,7 +232,7 @@ return ..() -/obj/machinery/power/thermoregulator/attack_hand(mob/user, list/params) +/obj/machinery/power/thermoregulator/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) interact(user) diff --git a/code/game/machinery/suit_storage/suit_cycler.dm b/code/game/machinery/suit_storage/suit_cycler.dm index dbdc44a1ea6a..e17e8357b43f 100644 --- a/code/game/machinery/suit_storage/suit_cycler.dm +++ b/code/game/machinery/suit_storage/suit_cycler.dm @@ -260,7 +260,7 @@ updateUsrDialog() return 1 -/obj/machinery/suit_cycler/attack_hand(mob/user, list/params) +/obj/machinery/suit_cycler/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) diff --git a/code/game/machinery/suit_storage/suit_storage_massive.dm b/code/game/machinery/suit_storage/suit_storage_massive.dm index dd5dc3cf21c6..773e6ab6e280 100644 --- a/code/game/machinery/suit_storage/suit_storage_massive.dm +++ b/code/game/machinery/suit_storage/suit_storage_massive.dm @@ -65,7 +65,7 @@ dump_everything() qdel(src) -/obj/machinery/suit_storage_closet/attack_hand(mob/user, list/params) +/obj/machinery/suit_storage_closet/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return if(machine_stat & NOPOWER) diff --git a/code/game/machinery/suit_storage/suit_storage_unit.dm b/code/game/machinery/suit_storage/suit_storage_unit.dm index a1b8ff57cca6..cb80c6ff01f4 100644 --- a/code/game/machinery/suit_storage/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage/suit_storage_unit.dm @@ -78,7 +78,7 @@ dump_everything() qdel(src) -/obj/machinery/suit_storage_unit/attack_hand(mob/user, list/params) +/obj/machinery/suit_storage_unit/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return if(machine_stat & NOPOWER) diff --git a/code/game/machinery/supplybeacon.dm b/code/game/machinery/supplybeacon.dm index f3337336291c..7b7c5bbf476e 100644 --- a/code/game/machinery/supplybeacon.dm +++ b/code/game/machinery/supplybeacon.dm @@ -57,7 +57,7 @@ return return ..() -/obj/machinery/power/supply_beacon/attack_hand(mob/user, list/params) +/obj/machinery/power/supply_beacon/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(expended) update_use_power(USE_POWER_OFF) diff --git a/code/game/machinery/syndicatebeacon.dm b/code/game/machinery/syndicatebeacon.dm index 6148d58e0681..481d8d39008f 100644 --- a/code/game/machinery/syndicatebeacon.dm +++ b/code/game/machinery/syndicatebeacon.dm @@ -15,7 +15,7 @@ var/selfdestructing = FALSE var/charges = 1 -/obj/machinery/syndicate_beacon/attack_hand(mob/user, list/params) +/obj/machinery/syndicate_beacon/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) usr.set_machine(src) var/dat = "Scanning [pick("retina pattern", "voice print", "fingerprints", "dna sequence")]...
Identity confirmed,
" if(istype(user, /mob/living/carbon/human) || istype(user, /mob/living/silicon/ai)) @@ -108,7 +108,7 @@ /obj/machinery/power/singularity_beacon/attack_ai(mob/user) return -/obj/machinery/power/singularity_beacon/attack_hand(mob/user, list/params) +/obj/machinery/power/singularity_beacon/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(anchored) return active ? Deactivate(user) : Activate(user) else @@ -159,7 +159,7 @@ //! ## VR FILE MERGE ## !// // Virgo modified syndie beacon, does not give objectives -/obj/machinery/syndicate_beacon/virgo/attack_hand(mob/user, list/params) +/obj/machinery/syndicate_beacon/virgo/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) usr.set_machine(src) var/dat = "Scanning [pick("retina pattern", "voice print", "fingerprints", "dna sequence")]...
Identity confirmed,
" if(istype(user, /mob/living/carbon/human) || istype(user, /mob/living/silicon/ai)) diff --git a/code/game/machinery/telecomms/_telecomms.dm b/code/game/machinery/telecomms/_telecomms.dm index 7c9f141ca39e..65a6994ea967 100644 --- a/code/game/machinery/telecomms/_telecomms.dm +++ b/code/game/machinery/telecomms/_telecomms.dm @@ -363,7 +363,7 @@ return UI_CLOSE . = ..() -/obj/machinery/telecomms/attack_hand(mob/user, list/params) +/obj/machinery/telecomms/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /obj/machinery/telecomms/ui_interact(mob/user, datum/tgui/ui) diff --git a/code/game/machinery/telecomms/logbrowser.dm b/code/game/machinery/telecomms/logbrowser.dm index 94977779139f..a6673e2ed6de 100644 --- a/code/game/machinery/telecomms/logbrowser.dm +++ b/code/game/machinery/telecomms/logbrowser.dm @@ -62,7 +62,7 @@ return data -/obj/machinery/computer/telecomms/server/attack_hand(mob/user, list/params) +/obj/machinery/computer/telecomms/server/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (BROKEN|NOPOWER)) return ui_interact(user) diff --git a/code/game/machinery/telecomms/message_server.dm b/code/game/machinery/telecomms/message_server.dm index 56b0aec8744a..3f5bcda9869d 100644 --- a/code/game/machinery/telecomms/message_server.dm +++ b/code/game/machinery/telecomms/message_server.dm @@ -140,7 +140,7 @@ var/global/list/obj/machinery/message_server/message_servers = list() Console.set_light(2) -/obj/machinery/message_server/attack_hand(mob/user, list/params) +/obj/machinery/message_server/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) // to_chat(user, "There seem to be some parts missing from this server. They should arrive on the station in a few days, give or take a few CentCom delays.") to_chat(user, "You toggle PDA message passing from [active ? "On" : "Off"] to [active ? "Off" : "On"]") active = !active diff --git a/code/game/machinery/telecomms/telemonitor.dm b/code/game/machinery/telecomms/telemonitor.dm index eff16361e954..ddf0bc204dd3 100644 --- a/code/game/machinery/telecomms/telemonitor.dm +++ b/code/game/machinery/telecomms/telemonitor.dm @@ -50,7 +50,7 @@ return data -/obj/machinery/computer/telecomms/monitor/attack_hand(mob/user, list/params) +/obj/machinery/computer/telecomms/monitor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (BROKEN|NOPOWER)) return ui_interact(user) diff --git a/code/game/machinery/teleporter/console.dm b/code/game/machinery/teleporter/console.dm index d2c7551f9240..71aa5f335f4c 100644 --- a/code/game/machinery/teleporter/console.dm +++ b/code/game/machinery/teleporter/console.dm @@ -86,7 +86,7 @@ return -/obj/machinery/computer/teleporter/attack_hand(mob/user, list/params) +/obj/machinery/computer/teleporter/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /obj/machinery/computer/teleporter/ui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) diff --git a/code/game/machinery/teleporter/projector.dm b/code/game/machinery/teleporter/projector.dm index d304f36e6d99..5d2922237cc6 100644 --- a/code/game/machinery/teleporter/projector.dm +++ b/code/game/machinery/teleporter/projector.dm @@ -182,7 +182,7 @@ /obj/machinery/tele_projector/attack_ai() attack_hand() -/obj/machinery/tele_projector/attack_hand(mob/user, list/params) +/obj/machinery/tele_projector/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(engaged) disengage() else diff --git a/code/game/machinery/turrets/turret.dm b/code/game/machinery/turrets/turret.dm index c469d5d39eb6..a22964f65fd1 100644 --- a/code/game/machinery/turrets/turret.dm +++ b/code/game/machinery/turrets/turret.dm @@ -288,7 +288,7 @@ nano_ui_interact(user) -/obj/machinery/porta_turret/attack_hand(mob/user, list/params) +/obj/machinery/porta_turret/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(isLocked(user)) return diff --git a/code/game/machinery/turrets/turret_control.dm b/code/game/machinery/turrets/turret_control.dm index e01425d540a2..b163c6a66656 100644 --- a/code/game/machinery/turrets/turret_control.dm +++ b/code/game/machinery/turrets/turret_control.dm @@ -118,7 +118,7 @@ nano_ui_interact(user) -/obj/machinery/turretid/attack_hand(mob/user, list/params) +/obj/machinery/turretid/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(isLocked(user)) return diff --git a/code/game/machinery/turrets/turret_frame.dm b/code/game/machinery/turrets/turret_frame.dm index 220d30456661..8a3c97076fcb 100644 --- a/code/game/machinery/turrets/turret_frame.dm +++ b/code/game/machinery/turrets/turret_frame.dm @@ -103,7 +103,7 @@ to_chat(user, "You add the prox sensor to the turret.") return - //attack_hand() removes the gun + //attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(5) if(I.is_screwdriver()) @@ -112,7 +112,7 @@ to_chat(user, "You close the internal access hatch.") return - //attack_hand() removes the prox sensor + //attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(6) if(I.is_material_stack_of(/datum/material/steel)) @@ -173,7 +173,7 @@ ..() -/obj/machinery/porta_turret_construct/attack_hand(mob/user, list/params) +/obj/machinery/porta_turret_construct/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) switch(build_step) if(4) if(!installation) diff --git a/code/game/machinery/vending/vending.dm b/code/game/machinery/vending/vending.dm index a47f2cf8da65..999a8526eb4d 100644 --- a/code/game/machinery/vending/vending.dm +++ b/code/game/machinery/vending/vending.dm @@ -283,7 +283,7 @@ /obj/machinery/vending/attack_ai(mob/user as mob) return attack_hand(user) -/obj/machinery/vending/attack_hand(mob/user, list/params) +/obj/machinery/vending/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (BROKEN|NOPOWER)) return diff --git a/code/game/machinery/washing_machine.dm b/code/game/machinery/washing_machine.dm index db2d8c8b4d29..7c0232aa5b14 100644 --- a/code/game/machinery/washing_machine.dm +++ b/code/game/machinery/washing_machine.dm @@ -120,7 +120,7 @@ ..() update_icon() -/obj/machinery/washing_machine/attack_hand(mob/user, list/params) +/obj/machinery/washing_machine/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) switch(state) if(1) state = 2 diff --git a/code/game/machinery/wishgranter.dm b/code/game/machinery/wishgranter.dm index cd2ffb6a64d4..c233bd51cdf3 100644 --- a/code/game/machinery/wishgranter.dm +++ b/code/game/machinery/wishgranter.dm @@ -9,7 +9,7 @@ var/charges = 1 var/insisting = 0 -/obj/machinery/wish_granter/attack_hand(mob/user, list/params) +/obj/machinery/wish_granter/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) usr.set_machine(src) if(charges <= 0) diff --git a/code/game/objects/effects/debris/cleanable/blood.dm b/code/game/objects/effects/debris/cleanable/blood.dm index 095f46374693..220680d47bd7 100644 --- a/code/game/objects/effects/debris/cleanable/blood.dm +++ b/code/game/objects/effects/debris/cleanable/blood.dm @@ -132,7 +132,7 @@ var/global/list/image/splatter_cache=list() add_atom_colour(newcolor, FIXED_COLOUR_PRIORITY) amount = 0 -/obj/effect/debris/cleanable/blood/attack_hand(mob/user, list/params) +/obj/effect/debris/cleanable/blood/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return diff --git a/code/game/objects/effects/debris/cleanable/misc.dm b/code/game/objects/effects/debris/cleanable/misc.dm index 8a9ead843099..41866828989a 100644 --- a/code/game/objects/effects/debris/cleanable/misc.dm +++ b/code/game/objects/effects/debris/cleanable/misc.dm @@ -15,7 +15,7 @@ icon_state = "ash" anchored = 1 -/obj/effect/debris/cleanable/ash/attack_hand(mob/user, list/params) +/obj/effect/debris/cleanable/ash/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) to_chat(user, "[src] sifts through your fingers.") var/turf/simulated/floor/F = get_turf(src) if (istype(F)) diff --git a/code/game/objects/effects/decals/remains.dm b/code/game/objects/effects/decals/remains.dm index b8e7644be70c..95ea5bd8b49b 100644 --- a/code/game/objects/effects/decals/remains.dm +++ b/code/game/objects/effects/decals/remains.dm @@ -55,7 +55,7 @@ desc = "They look like human remains. They've been here a long time." icon_state = "mummified2" -/obj/effect/decal/remains/attack_hand(mob/user, list/params) +/obj/effect/decal/remains/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) to_chat(user, "[src] sinks together into a pile of ash.") var/turf/simulated/floor/F = get_turf(src) if (istype(F)) diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm index b6e81d12bebe..4216510e7dda 100644 --- a/code/game/objects/effects/portals.dm +++ b/code/game/objects/effects/portals.dm @@ -20,7 +20,7 @@ GLOBAL_LIST_BOILERPLATE(all_portals, /obj/effect/portal) . = ..() teleport(AM) -/obj/effect/portal/attack_hand(mob/user, list/params) +/obj/effect/portal/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(istype(user) && !(istype(user,/mob/living))) return //do not send ghosts, zshadows, ai eyes, etc spawn(0) diff --git a/code/game/objects/items/bells.dm b/code/game/objects/items/bells.dm index 3eeb418f2f73..39ebae855cbc 100644 --- a/code/game/objects/items/bells.dm +++ b/code/game/objects/items/bells.dm @@ -18,7 +18,7 @@ if(broken) . += "It looks damaged, the ringer is stuck firmly inside." -/obj/item/deskbell/attack_hand(mob/user, list/params) +/obj/item/deskbell/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) //This defines the radials and what call we're assiging to them. var/list/options = list() diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm index 366b6f29a8ae..7b0616c46b66 100644 --- a/code/game/objects/items/devices/chameleonproj.dm +++ b/code/game/objects/items/devices/chameleonproj.dm @@ -110,7 +110,7 @@ to_chat(M, "Your chameleon-projector deactivates.") master.disrupt() -/obj/effect/dummy/chameleon/attack_hand(mob/user, list/params) +/obj/effect/dummy/chameleon/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) for(var/mob/M in src) to_chat(M, "Your chameleon-projector deactivates.") master.disrupt() diff --git a/code/game/objects/items/devices/defib.dm b/code/game/objects/items/devices/defib.dm index 18afd34b01ff..b657b547ca10 100644 --- a/code/game/objects/items/devices/defib.dm +++ b/code/game/objects/items/devices/defib.dm @@ -67,7 +67,7 @@ /obj/item/defib_kit/ui_action_click(datum/action/action, datum/event_args/actor/actor) toggle_paddles() -/obj/item/defib_kit/attack_hand(mob/user, list/params) +/obj/item/defib_kit/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(loc == user) toggle_paddles() else diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm index 0c5736fac96a..0e34f154c131 100644 --- a/code/game/objects/items/devices/flashlight.dm +++ b/code/game/objects/items/devices/flashlight.dm @@ -189,7 +189,7 @@ return CLICKCHAIN_DO_NOT_PROPAGATE return ..() -/obj/item/flashlight/attack_hand(mob/user, list/params) +/obj/item/flashlight/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) if(cell) cell.update_appearance() diff --git a/code/game/objects/items/devices/powersink.dm b/code/game/objects/items/devices/powersink.dm index d4d8b82e28b2..d2d971bd8b08 100644 --- a/code/game/objects/items/devices/powersink.dm +++ b/code/game/objects/items/devices/powersink.dm @@ -70,7 +70,7 @@ /obj/item/powersink/attack_ai() return -/obj/item/powersink/attack_hand(mob/user, list/params) +/obj/item/powersink/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) switch(mode) if(0) ..() diff --git a/code/game/objects/items/devices/radio/electropack.dm b/code/game/objects/items/devices/radio/electropack.dm index a4a142c7017a..f835513866fb 100644 --- a/code/game/objects/items/devices/radio/electropack.dm +++ b/code/game/objects/items/devices/radio/electropack.dm @@ -15,7 +15,7 @@ var/code = 2 -/obj/item/radio/electropack/attack_hand(mob/user, list/params) +/obj/item/radio/electropack/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(src == user.item_by_slot_id(SLOT_ID_BACK)) to_chat(user, "You need help taking this off!") return diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm index 4c96ed809894..198d2981da3b 100644 --- a/code/game/objects/items/devices/radio/intercom.dm +++ b/code/game/objects/items/devices/radio/intercom.dm @@ -151,7 +151,7 @@ CREATE_WALL_MOUNTING_TYPES_SHIFTED(/obj/item/radio/intercom, 28) spawn (0) attack_self(user) -/obj/item/radio/intercom/attack_hand(mob/user, list/params) +/obj/item/radio/intercom/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) src.add_fingerprint(user) spawn (0) attack_self(user) diff --git a/code/game/objects/items/devices/radio/jammer.dm b/code/game/objects/items/devices/radio/jammer.dm index 69ef1cf538f8..80f9f6431053 100644 --- a/code/game/objects/items/devices/radio/jammer.dm +++ b/code/game/objects/items/devices/radio/jammer.dm @@ -69,7 +69,7 @@ var/global/list/active_radio_jammers = list() update_icon() -/obj/item/radio_jammer/attack_hand(mob/user, list/params) +/obj/item/radio_jammer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src && power_source) to_chat(user,"You eject \the [power_source] from \the [src].") user.put_in_hands(power_source) diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 6bedeea1a533..ef0cea675388 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -813,7 +813,7 @@ GLOBAL_DATUM_INIT(virtual_announcer_ai, /mob/living/silicon/ai/announcer, new(nu /obj/item/bluespace_radio/ui_action_click(datum/action/action, datum/event_args/actor/actor) toggle_handset() -/obj/item/bluespace_radio/attack_hand(mob/user, list/params) +/obj/item/bluespace_radio/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(loc == user) toggle_handset() else diff --git a/code/game/objects/items/devices/tape_recorder/tape_recorder.dm b/code/game/objects/items/devices/tape_recorder/tape_recorder.dm index 076367dc8a25..7dfd403748f8 100644 --- a/code/game/objects/items/devices/tape_recorder/tape_recorder.dm +++ b/code/game/objects/items/devices/tape_recorder/tape_recorder.dm @@ -62,7 +62,7 @@ tape.ruin() //Fires destroy the tape return ..() -/obj/item/tape_recorder/attack_hand(mob/user, list/params) +/obj/item/tape_recorder/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) if(tape) eject() diff --git a/code/game/objects/items/devices/taperecorder.dm b/code/game/objects/items/devices/taperecorder.dm index 3a38f6b4f221..a35e5ec5b34c 100644 --- a/code/game/objects/items/devices/taperecorder.dm +++ b/code/game/objects/items/devices/taperecorder.dm @@ -57,7 +57,7 @@ return ..() -/obj/item/tape_recorder/attack_hand(mob/user, list/params) +/obj/item/tape_recorder/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) if(mytape) eject() diff --git a/code/game/objects/items/shooting_range.dm b/code/game/objects/items/shooting_range.dm index 3f2ad6ff22e5..8925f463c055 100644 --- a/code/game/objects/items/shooting_range.dm +++ b/code/game/objects/items/shooting_range.dm @@ -29,7 +29,7 @@ return ..() -/obj/item/target/attack_hand(mob/user, list/params) +/obj/item/target/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) // taking pinned targets off! var/obj/structure/target_stake/stake for(var/obj/structure/target_stake/T in view(3,src)) diff --git a/code/game/objects/items/stacks/marker_beacons.dm b/code/game/objects/items/stacks/marker_beacons.dm index 9be024a7645a..9c923e7c75ce 100644 --- a/code/game/objects/items/stacks/marker_beacons.dm +++ b/code/game/objects/items/stacks/marker_beacons.dm @@ -106,7 +106,7 @@ var/list/marker_beacon_colors = list( icon_state = "[initial(icon_state)][lowertext(picked_color)]-on" set_light(light_range, light_power, marker_beacon_colors[picked_color]) -/obj/structure/marker_beacon/attack_hand(mob/user, list/params) +/obj/structure/marker_beacon/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) to_chat(user, "You start picking [src] up...") if(do_after(user, remove_speed, target = src)) var/obj/item/stack/marker_beacon/M = new(loc) diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index bde4383c2036..65a4985587b5 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -304,7 +304,7 @@ if(!amount) break -/obj/item/stack/attack_hand(mob/user, list/params) +/obj/item/stack/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) change_stack(user, 1) else diff --git a/code/game/objects/items/storage/quickdraw.dm b/code/game/objects/items/storage/quickdraw.dm index 11575db69b74..3c64736e68ca 100644 --- a/code/game/objects/items/storage/quickdraw.dm +++ b/code/game/objects/items/storage/quickdraw.dm @@ -17,7 +17,7 @@ //When set to 1, a click while it is equipped will instead move the first item inside it to your hand // var/quickmode = 0 -// /obj/item/storage/quickdraw/attack_hand(mob/user, list/params) +// /obj/item/storage/quickdraw/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) // if(src.loc == user) //If they aren't holding us, we do nothing special // if(ishuman(user)) // var/mob/living/carbon/human/H = user diff --git a/code/game/objects/items/storage/secure.dm b/code/game/objects/items/storage/secure.dm index 1c4f05bca68a..9d249922cbf5 100644 --- a/code/game/objects/items/storage/secure.dm +++ b/code/game/objects/items/storage/secure.dm @@ -198,5 +198,5 @@ /obj/item/pen ) -/obj/item/storage/secure/safe/attack_hand(mob/user, list/params) +/obj/item/storage/secure/safe/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return attack_self(user) diff --git a/code/game/objects/items/stream_projector/medichine.dm b/code/game/objects/items/stream_projector/medichine.dm index fb87b5393695..5ab66a2b6ad3 100644 --- a/code/game/objects/items/stream_projector/medichine.dm +++ b/code/game/objects/items/stream_projector/medichine.dm @@ -58,7 +58,7 @@ GLOBAL_LIST_EMPTY(medichine_cell_datums) /obj/item/stream_projector/medichine/valid_target(atom/entity) return isliving(entity) -/obj/item/stream_projector/medichine/attack_hand(mob/user, list/params) +/obj/item/stream_projector/medichine/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.is_holding_inactive(src)) if(isnull(inserted_cartridge)) user.action_feedback(SPAN_WARNING("[src] has no vial loaded."), src) diff --git a/code/game/objects/items/tools/weldingtool.dm b/code/game/objects/items/tools/weldingtool.dm index b564a95919b9..9811dbc5c85c 100644 --- a/code/game/objects/items/tools/weldingtool.dm +++ b/code/game/objects/items/tools/weldingtool.dm @@ -651,7 +651,7 @@ update_icon() return 0 -/obj/item/weldingtool/electric/attack_hand(mob/user, list/params) +/obj/item/weldingtool/electric/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) if(power_supply) power_supply.update_icon() diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index 83ebd8e9c1b2..4ef067b847a6 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -518,7 +518,7 @@ playsound(user, 'sound/mecha/mechstep.ogg', 20, 1) cooldown = world.time -/obj/item/toy/prize/attack_hand(mob/user, list/params) +/obj/item/toy/prize/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(loc == user) if(cooldown < world.time - 8) to_chat(user, "You play with [src].") @@ -926,7 +926,7 @@ density = 1 var/phrase = "I don't want to exist anymore!" -/obj/structure/plushie/attack_hand(mob/user, list/params) +/obj/structure/plushie/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) if(user.a_intent == INTENT_HELP) user.visible_message("\The [user] hugs [src]!","You hug [src]!") diff --git a/code/game/objects/items/uav.dm b/code/game/objects/items/uav.dm index 852a1a8dac98..27ef0ae3ee6f 100644 --- a/code/game/objects/items/uav.dm +++ b/code/game/objects/items/uav.dm @@ -64,7 +64,7 @@ STOP_PROCESSING(SSobj, src) return ..() -/obj/item/uav/attack_hand(mob/user, list/params) +/obj/item/uav/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) //Has to be on the ground to work with it properly if(!isturf(loc)) return ..() diff --git a/code/game/objects/items/weapons/barrier_tape.dm b/code/game/objects/items/weapons/barrier_tape.dm index 8eb40321c06d..c82c16b019ca 100644 --- a/code/game/objects/items/weapons/barrier_tape.dm +++ b/code/game/objects/items/weapons/barrier_tape.dm @@ -133,7 +133,7 @@ var/list/tape_roll_applications = list() update_icon() return ..() -/obj/item/barrier_tape_roll/attack_hand(mob/user, list/params) +/obj/item/barrier_tape_roll/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) update_icon() return ..() @@ -334,7 +334,7 @@ var/list/tape_roll_applications = list() /obj/item/barrier_tape_segment/attackby(obj/item/W as obj, mob/user as mob) breaktape(user) -/obj/item/barrier_tape_segment/attack_hand(mob/user, list/params) +/obj/item/barrier_tape_segment/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (user.a_intent == INTENT_HELP && src.allowed(user)) user.show_viewers("\The [user] lifts \the [src], allowing passage.") for(var/obj/item/barrier_tape_segment/T in gettapeline()) diff --git a/code/game/objects/items/weapons/duct_tape.dm b/code/game/objects/items/weapons/duct_tape.dm index 5fbb2b0d3199..6e05e576feae 100644 --- a/code/game/objects/items/weapons/duct_tape.dm +++ b/code/game/objects/items/weapons/duct_tape.dm @@ -202,7 +202,7 @@ qdel(I) to_chat(user, "You place \the [I] back into \the [src].") -/obj/item/duct_tape_piece/attack_hand(mob/user, list/params) +/obj/item/duct_tape_piece/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) anchored = FALSE return ..() // Pick it up now that it's unanchored. diff --git a/code/game/objects/items/weapons/game_kit.dm b/code/game/objects/items/weapons/game_kit.dm index 190f0364ad28..fafb6e8281ab 100644 --- a/code/game/objects/items/weapons/game_kit.dm +++ b/code/game/objects/items/weapons/game_kit.dm @@ -57,7 +57,7 @@ THAT STUPID GAME KIT /obj/item/game_kit/attack_ai(mob/user as mob, unused, flag) return src.attack_hand(user, unused, flag) -/obj/item/game_kit/attack_hand(mob/user as mob, unused, flag) +/obj/item/game_kit/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (flag) return ..() diff --git a/code/game/objects/items/weapons/grenades/grenade.dm b/code/game/objects/items/weapons/grenades/grenade.dm index 795f743c060b..a2bf2996ee9f 100644 --- a/code/game/objects/items/weapons/grenades/grenade.dm +++ b/code/game/objects/items/weapons/grenades/grenade.dm @@ -111,7 +111,7 @@ ..() return -/obj/item/grenade/attack_hand(mob/user, list/params) +/obj/item/grenade/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) walk(src, null, null) ..() return diff --git a/code/game/objects/items/weapons/implants/implantchair.dm b/code/game/objects/items/weapons/implants/implantchair.dm index fe80c61ab876..e997a571d9be 100644 --- a/code/game/objects/items/weapons/implants/implantchair.dm +++ b/code/game/objects/items/weapons/implants/implantchair.dm @@ -24,7 +24,7 @@ ..() add_implants() -/obj/machinery/implantchair/attack_hand(mob/user, list/params) +/obj/machinery/implantchair/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.set_machine(src) var/health_text = "" if(src.occupant) diff --git a/code/game/objects/items/weapons/implants/implantpad.dm b/code/game/objects/items/weapons/implants/implantpad.dm index cec946b04872..5a1a4076d171 100644 --- a/code/game/objects/items/weapons/implants/implantpad.dm +++ b/code/game/objects/items/weapons/implants/implantpad.dm @@ -21,7 +21,7 @@ return -/obj/item/implantpad/attack_hand(mob/user, list/params) +/obj/item/implantpad/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if ((src.case && user.is_holding(src))) user.put_in_active_hand(case) diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm index ac7800a0ec4c..add625fde68d 100644 --- a/code/game/objects/items/weapons/stunbaton.dm +++ b/code/game/objects/items/weapons/stunbaton.dm @@ -100,7 +100,7 @@ else to_chat(user, "This cell is not fitted for [src].") -/obj/item/melee/baton/attack_hand(mob/user, list/params) +/obj/item/melee/baton/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) if(bcell && !integrated_cell) bcell.update_icon() diff --git a/code/game/objects/items/weapons/traps.dm b/code/game/objects/items/weapons/traps.dm index d447853fecb3..100a837531d8 100644 --- a/code/game/objects/items/weapons/traps.dm +++ b/code/game/objects/items/weapons/traps.dm @@ -85,7 +85,7 @@ if(!has_buckled_mobs()) anchored = FALSE -/obj/item/beartrap/attack_hand(mob/user, list/params) +/obj/item/beartrap/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) // check unbuckle first if(click_unbuckle_interaction(user)) return CLICKCHAIN_DO_NOT_PROPAGATE diff --git a/code/game/objects/items/weapons/weldbackpack.dm b/code/game/objects/items/weapons/weldbackpack.dm index 8c1a5be5dc90..38a495269b72 100644 --- a/code/game/objects/items/weapons/weldbackpack.dm +++ b/code/game/objects/items/weapons/weldbackpack.dm @@ -84,7 +84,7 @@ to_chat(user,"The tank scoffs at your insolence. It only provides services to welders.") return -/obj/item/weldpack/attack_hand(mob/user, list/params) +/obj/item/weldpack/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(istype(user, /mob/living/carbon/human)) var/mob/living/carbon/human/wearer = user if(wearer.back == src) diff --git a/code/game/objects/misc.dm b/code/game/objects/misc.dm index 0a4d51c5a0fa..04856ddf13ac 100644 --- a/code/game/objects/misc.dm +++ b/code/game/objects/misc.dm @@ -7,7 +7,7 @@ /obj/structure/signpost/attackby(obj/item/W, mob/user) return attack_hand(user) -/obj/structure/signpost/attack_hand(mob/user, list/params) +/obj/structure/signpost/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) switch(tgui_alert(user, "Do you want to go to SS13?", "Travel", list("Yes", "No"))) if("Yes") user.forceMove(SSjob.get_latejoin_spawnpoint(faction = JOB_FACTION_STATION)) diff --git a/code/game/objects/obj.dm b/code/game/objects/obj.dm index 828bbda90e8a..f58e08f536e6 100644 --- a/code/game/objects/obj.dm +++ b/code/game/objects/obj.dm @@ -529,7 +529,7 @@ /obj/proc/do_climb_target(mob/living/climber) return get_turf(src) -/obj/attack_hand(mob/user, list/params) +/obj/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_HARM) return ..() . = ..() diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index 87a0afecc2d1..08af691efc11 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -31,7 +31,7 @@ return ..() -/obj/structure/attack_hand(mob/user, list/params) +/obj/structure/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(breakable) if(MUTATION_HULK in user.mutations) user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) diff --git a/code/game/objects/structures/aliens.dm b/code/game/objects/structures/aliens.dm index 4adaffbe1e29..ad8aa2d2cd0e 100644 --- a/code/game/objects/structures/aliens.dm +++ b/code/game/objects/structures/aliens.dm @@ -55,7 +55,7 @@ T.thermal_conductivity = initial(T.thermal_conductivity) ..() -/obj/structure/alien/resin/attack_hand(mob/user, list/params) +/obj/structure/alien/resin/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(iscarbon(user)) var/mob/living/carbon/C = user if(locate(/obj/item/organ/internal/xenos/hivenode) in C.internal_organs) @@ -344,7 +344,7 @@ Alien plants should do something if theres a lot of poison if((status == GROWING) && (BURST == 0)) Grow() -/obj/structure/alien/egg/attack_hand(mob/user, list/params) +/obj/structure/alien/egg/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/carbon/M = user if(!istype(M) || !(locate(/obj/item/organ/internal/xenos/hivenode) in M.internal_organs)) diff --git a/code/game/objects/structures/ashlander.dm b/code/game/objects/structures/ashlander.dm index 58902f3eb82d..4c9a0d39b7da 100644 --- a/code/game/objects/structures/ashlander.dm +++ b/code/game/objects/structures/ashlander.dm @@ -299,7 +299,7 @@ src.updateUsrDialog() return 0 -/obj/structure/ashlander/calcinator/attack_hand(mob/user, list/params) +/obj/structure/ashlander/calcinator/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) interact(user) /obj/structure/ashlander/calcinator/AltClick(mob/user) @@ -387,7 +387,7 @@ . = ..() set_light(3, 2, "#9463bb") -/obj/structure/ashlander/statue/attack_hand(mob/user, list/params) +/obj/structure/ashlander/statue/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/choice = tgui_alert(user, "Do you wish to pray to the statue?", "Interact With the Statue", list("Yes", "No")) if(choice != "Yes") return diff --git a/code/game/objects/structures/bedsheet_bin.dm b/code/game/objects/structures/bedsheet_bin.dm index 23bb25c13752..554be30604dd 100644 --- a/code/game/objects/structures/bedsheet_bin.dm +++ b/code/game/objects/structures/bedsheet_bin.dm @@ -216,7 +216,7 @@ LINEN BINS hidden = I to_chat(user, "You hide [I] among the sheets.") -/obj/structure/bedsheetbin/attack_hand(mob/user, list/params) +/obj/structure/bedsheetbin/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(amount >= 1) amount-- diff --git a/code/game/objects/structures/bonfire.dm b/code/game/objects/structures/bonfire.dm index a4d652be4679..f43c1aebf1ba 100644 --- a/code/game/objects/structures/bonfire.dm +++ b/code/game/objects/structures/bonfire.dm @@ -81,7 +81,7 @@ else return ..() -/obj/structure/bonfire/attack_hand(mob/user, list/params) +/obj/structure/bonfire/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(has_buckled_mobs()) return ..() @@ -288,7 +288,7 @@ else return ..() -/obj/structure/fireplace/attack_hand(mob/user, list/params) +/obj/structure/fireplace/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(get_fuel_amount()) remove_fuel(user) diff --git a/code/game/objects/structures/catwalk.dm b/code/game/objects/structures/catwalk.dm index f34b8a2dfba4..4203261cf627 100644 --- a/code/game/objects/structures/catwalk.dm +++ b/code/game/objects/structures/catwalk.dm @@ -135,7 +135,7 @@ . = ..() activate() -/obj/effect/catwalk_plated/attack_hand(mob/user, list/params) +/obj/effect/catwalk_plated/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) attack_generic() /obj/effect/catwalk_plated/attack_ghost() diff --git a/code/game/objects/structures/charge_pylon.dm b/code/game/objects/structures/charge_pylon.dm index 6a86d5b85f8e..0225508a2b7e 100644 --- a/code/game/objects/structures/charge_pylon.dm +++ b/code/game/objects/structures/charge_pylon.dm @@ -12,7 +12,7 @@ if(Adjacent(user)) attack_hand(user) -/obj/structure/adherent_pylon/attack_hand(mob/user, list/params) +/obj/structure/adherent_pylon/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) charge_user(user) /obj/structure/adherent_pylon/proc/charge_user(var/mob/living/user) diff --git a/code/game/objects/structures/coathanger.dm b/code/game/objects/structures/coathanger.dm index 0b2e643c2f98..3c7e21c9f3a4 100644 --- a/code/game/objects/structures/coathanger.dm +++ b/code/game/objects/structures/coathanger.dm @@ -6,7 +6,7 @@ var/obj/item/clothing/suit/coat var/list/allowed = list(/obj/item/clothing/suit/storage/toggle/labcoat, /obj/item/clothing/suit/storage/det_trench) -/obj/structure/coatrack/attack_hand(mob/user, list/params) +/obj/structure/coatrack/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.visible_message("[user] takes [coat] off \the [src].", "You take [coat] off the \the [src]") if(!user.put_in_active_hand(coat)) coat.loc = get_turf(user) diff --git a/code/game/objects/structures/crates_lockers/__closet.dm b/code/game/objects/structures/crates_lockers/__closet.dm index d44188937940..d1c3e2fcb82b 100644 --- a/code/game/objects/structures/crates_lockers/__closet.dm +++ b/code/game/objects/structures/crates_lockers/__closet.dm @@ -412,7 +412,7 @@ if(!open()) to_chat(user, "It won't budge!") -/obj/structure/closet/attack_hand(mob/user, list/params) +/obj/structure/closet/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) if(locked && secure) togglelock(user) diff --git a/code/game/objects/structures/crates_lockers/closets/coffin.dm b/code/game/objects/structures/crates_lockers/closets/coffin.dm index fcf23cf8bdeb..44e4f8f46529 100644 --- a/code/game/objects/structures/crates_lockers/closets/coffin.dm +++ b/code/game/objects/structures/crates_lockers/closets/coffin.dm @@ -34,7 +34,7 @@ use_old_icon_update = TRUE obj_flags = OBJ_MELEE_TARGETABLE -/obj/structure/closet/grave/attack_hand(mob/user, list/params) +/obj/structure/closet/grave/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(opened) visible_message("[user] starts to climb into \the [src.name].", \ "You start to lower yourself into \the [src.name].") diff --git a/code/game/objects/structures/crates_lockers/closets/fireaxe.dm b/code/game/objects/structures/crates_lockers/closets/fireaxe.dm index e7e320a32367..b58927665393 100644 --- a/code/game/objects/structures/crates_lockers/closets/fireaxe.dm +++ b/code/game/objects/structures/crates_lockers/closets/fireaxe.dm @@ -109,7 +109,7 @@ update_icon() -/obj/structure/closet/fireaxecabinet/attack_hand(mob/user, list/params) +/obj/structure/closet/fireaxecabinet/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/hasaxe = 0 if(fireaxe) hasaxe = 1 diff --git a/code/game/objects/structures/crates_lockers/closets/secure/genpop.dm b/code/game/objects/structures/crates_lockers/closets/secure/genpop.dm index d3410971f156..6ce0bf5f5e37 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/genpop.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/genpop.dm @@ -95,7 +95,7 @@ locked = TRUE return ..() -/obj/structure/closet/secure_closet/genpop/attack_hand(mob/user) +/obj/structure/closet/secure_closet/genpop/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.lying && get_dist(src, user) > 0) return diff --git a/code/game/objects/structures/crates_lockers/closets/statue.dm b/code/game/objects/structures/crates_lockers/closets/statue.dm index 76fde00e4861..6de122885a82 100644 --- a/code/game/objects/structures/crates_lockers/closets/statue.dm +++ b/code/game/objects/structures/crates_lockers/closets/statue.dm @@ -90,7 +90,7 @@ /obj/structure/closet/statue/relaymove() return -/obj/structure/closet/statue/attack_hand(mob/user, list/params) +/obj/structure/closet/statue/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return /obj/structure/closet/statue/verb_toggleopen() diff --git a/code/game/objects/structures/crates_lockers/closets/walllocker.dm b/code/game/objects/structures/crates_lockers/closets/walllocker.dm index 2554fcc6496a..3c3b8db1ba14 100644 --- a/code/game/objects/structures/crates_lockers/closets/walllocker.dm +++ b/code/game/objects/structures/crates_lockers/closets/walllocker.dm @@ -28,7 +28,7 @@ /obj/structure/closet/walllocker/emerglocker/attackby(obj/item/W as obj, mob/user as mob) return -/obj/structure/closet/walllocker/emerglocker/attack_hand(mob/user, list/params) +/obj/structure/closet/walllocker/emerglocker/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (istype(user, /mob/living/silicon/ai)) //Added by Strumpetplaya - AI shouldn't be able to return //activate emergency lockers. This fixes that. (Does this make sense, the AI can't call attack_hand, can it? --Mloc) if(!amount) diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index 04e971a25f02..33e09af3771c 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -201,7 +201,7 @@ else to_chat(usr, "This mob type can't use this verb.") -/obj/structure/closet/crate/secure/attack_hand(mob/user, list/params) +/obj/structure/closet/crate/secure/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) src.add_fingerprint(user) if(locked) src.togglelock(user) diff --git a/code/game/objects/structures/crates_lockers/largecrate.dm b/code/game/objects/structures/crates_lockers/largecrate.dm index 0a77584a74a1..47eb371602f8 100644 --- a/code/game/objects/structures/crates_lockers/largecrate.dm +++ b/code/game/objects/structures/crates_lockers/largecrate.dm @@ -62,7 +62,7 @@ */ -/obj/structure/largecrate/attack_hand(mob/user, list/params) +/obj/structure/largecrate/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) to_chat(user, "You need a crowbar to pry this open!") return diff --git a/code/game/objects/structures/crates_lockers/vehiclecage.dm b/code/game/objects/structures/crates_lockers/vehiclecage.dm index c4987c4f0600..f255d50a6274 100644 --- a/code/game/objects/structures/crates_lockers/vehiclecage.dm +++ b/code/game/objects/structures/crates_lockers/vehiclecage.dm @@ -23,7 +23,7 @@ load_vehicle(I) update_icon() -/obj/structure/vehiclecage/attack_hand(mob/user, list/params) +/obj/structure/vehiclecage/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) to_chat(user, "You need a wrench to take this apart!") return diff --git a/code/game/objects/structures/curtains.dm b/code/game/objects/structures/curtains.dm index b3f82578d8af..96379a2ea4d0 100644 --- a/code/game/objects/structures/curtains.dm +++ b/code/game/objects/structures/curtains.dm @@ -19,7 +19,7 @@ layer = 3.3 //3.3 so its above windows, not the same as them. anything below 3.3 puts the curtain beneath the window sprite in current build opacity = 0 -/obj/structure/curtain/attack_hand(mob/user, list/params) +/obj/structure/curtain/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) playsound(get_turf(loc), "rustle", 15, 1, -5) toggle() ..() diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm index 0ef8330e5c20..21b2b9d7ea3f 100644 --- a/code/game/objects/structures/displaycase.dm +++ b/code/game/objects/structures/displaycase.dm @@ -23,7 +23,7 @@ else src.icon_state = "glassbox[src.occupied]" -/obj/structure/displaycase/attack_hand(mob/user, list/params) +/obj/structure/displaycase/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((atom_flags & ATOM_BROKEN) && occupied) new /obj/item/gun/energy/captain( src.loc ) to_chat(user, "You deactivate the hover field built into the case.") diff --git a/code/game/objects/structures/extinguisher.dm b/code/game/objects/structures/extinguisher.dm index c1897ea9bca8..67e93490a3a9 100644 --- a/code/game/objects/structures/extinguisher.dm +++ b/code/game/objects/structures/extinguisher.dm @@ -45,7 +45,7 @@ update_icon() -/obj/structure/extinguisher_cabinet/attack_hand(mob/user, list/params) +/obj/structure/extinguisher_cabinet/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(isrobot(user)) return if (ishuman(user)) diff --git a/code/game/objects/structures/fence.dm b/code/game/objects/structures/fence.dm index 82c954d2d326..067a553e9243 100644 --- a/code/game/objects/structures/fence.dm +++ b/code/game/objects/structures/fence.dm @@ -144,7 +144,7 @@ desc = "It looks like it has a strong padlock attached." locked = TRUE -/obj/structure/fence/door/attack_hand(mob/user, list/params) +/obj/structure/fence/door/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(can_open(user)) toggle(user) else diff --git a/code/game/objects/structures/fireaxe.dm b/code/game/objects/structures/fireaxe.dm index 5b21c9d390de..994b2e183976 100644 --- a/code/game/objects/structures/fireaxe.dm +++ b/code/game/objects/structures/fireaxe.dm @@ -92,7 +92,7 @@ toggle_close_open() -/obj/structure/fireaxecabinet/attack_hand(mob/user, list/params) +/obj/structure/fireaxecabinet/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) //var/hasaxe = 0 //Fuck this. Fuck everything about this. Who wrote this. Why. //if(fireaxe) // hasaxe = 1 diff --git a/code/game/objects/structures/fitness.dm b/code/game/objects/structures/fitness.dm index 355d2f9f1db6..ea926dc26e1c 100644 --- a/code/game/objects/structures/fitness.dm +++ b/code/game/objects/structures/fitness.dm @@ -10,7 +10,7 @@ density = 1 var/list/hit_message = list("hit", "punch", "kick", "robust") -/obj/structure/fitness/punchingbag/attack_hand(mob/user, list/params) +/obj/structure/fitness/punchingbag/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!istype(user)) ..() return @@ -38,7 +38,7 @@ weight = ((weight) % qualifiers.len) + 1 to_chat(user, "You set the machine's weight level to [weight].") -/obj/structure/fitness/weightlifter/attack_hand(mob/user, list/params) +/obj/structure/fitness/weightlifter/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!istype(user)) return if(user.loc != src.loc) diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm index 81d9f24f58aa..9b166f6170d6 100644 --- a/code/game/objects/structures/flora.dm +++ b/code/game/objects/structures/flora.dm @@ -397,7 +397,7 @@ /obj/structure/flora/sif icon = 'icons/obj/flora/sifflora.dmi' -/obj/structure/flora/sif/attack_hand(mob/user, list/params) +/obj/structure/flora/sif/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (user.a_intent == INTENT_HARM) if(do_after(user, 5 SECONDS)) user.visible_message("\The [user] digs up \the [src.name].", "You dig up \the [src.name].") @@ -518,7 +518,7 @@ var/gift_type = /obj/item/b_gift var/list/ckeys_that_took = list() -/obj/structure/flora/pumpkin/pumpkin_patch/presents/attack_hand(mob/user, list/params) +/obj/structure/flora/pumpkin/pumpkin_patch/presents/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return diff --git a/code/game/objects/structures/flora/trees.dm b/code/game/objects/structures/flora/trees.dm index 75898d9168ce..fe7ac42a99f0 100644 --- a/code/game/objects/structures/flora/trees.dm +++ b/code/game/objects/structures/flora/trees.dm @@ -133,7 +133,7 @@ /obj/structure/flora/tree/pine/xmas/presents/choose_icon_state() return "pinepresents" -/obj/structure/flora/tree/pine/xmas/presents/attack_hand(mob/user, list/params) +/obj/structure/flora/tree/pine/xmas/presents/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return diff --git a/code/game/objects/structures/ghost_pods/ghost_pods.dm b/code/game/objects/structures/ghost_pods/ghost_pods.dm index ebfc4daa2afc..c8ae98d84a06 100644 --- a/code/game/objects/structures/ghost_pods/ghost_pods.dm +++ b/code/game/objects/structures/ghost_pods/ghost_pods.dm @@ -43,7 +43,7 @@ /obj/structure/ghost_pod/manual var/confirm_before_open = FALSE // Recommended to be TRUE if the pod contains a surprise. -/obj/structure/ghost_pod/manual/attack_hand(mob/user, list/params) +/obj/structure/ghost_pod/manual/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!used) if(confirm_before_open) if(alert(user, "Are you sure you want to touch \the [src]?", "Confirm", "No", "Yes") == "No") diff --git a/code/game/objects/structures/holoplant.dm b/code/game/objects/structures/holoplant.dm index d239087db652..714980a4d26d 100644 --- a/code/game/objects/structures/holoplant.dm +++ b/code/game/objects/structures/holoplant.dm @@ -21,7 +21,7 @@ . = ..() activate() -/obj/machinery/holoplant/attack_hand(mob/user, list/params) +/obj/machinery/holoplant/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!istype(user) || interference) return diff --git a/code/game/objects/structures/inflatable.dm b/code/game/objects/structures/inflatable.dm index 516693d6f557..3a40b5c873ef 100644 --- a/code/game/objects/structures/inflatable.dm +++ b/code/game/objects/structures/inflatable.dm @@ -92,7 +92,7 @@ if(get_dist(user,src) <= 1) //not remotely though return TryToSwitchState(user) -/obj/structure/inflatable/door/attack_hand(mob/user, list/params) +/obj/structure/inflatable/door/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return TryToSwitchState(user) /obj/structure/inflatable/door/CanAllowThrough(atom/movable/mover, turf/target) diff --git a/code/game/objects/structures/janicart.dm b/code/game/objects/structures/janicart.dm index cd4858dd972a..57e3f2541921 100644 --- a/code/game/objects/structures/janicart.dm +++ b/code/game/objects/structures/janicart.dm @@ -89,7 +89,7 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) mybag.attackby(I, user) -/obj/structure/janitorialcart/attack_hand(mob/user, list/params) +/obj/structure/janitorialcart/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) nano_ui_interact(user) return @@ -211,7 +211,7 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) to_chat(user, "You hook the trashbag onto the [callme].") mybag = I -/obj/structure/bed/chair/janicart/attack_hand(mob/user, list/params) +/obj/structure/bed/chair/janicart/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(mybag) user.grab_item_from_interacted_with(mybag, src) mybag = null diff --git a/code/game/objects/structures/kitchen_foodcart.dm b/code/game/objects/structures/kitchen_foodcart.dm index 968d990be4c5..963becea89b1 100644 --- a/code/game/objects/structures/kitchen_foodcart.dm +++ b/code/game/objects/structures/kitchen_foodcart.dm @@ -22,7 +22,7 @@ return return ..() -/obj/structure/foodcart/attack_hand(mob/user, list/params) +/obj/structure/foodcart/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(contents.len) var/obj/item/reagent_containers/food/choice = input("What would you like to grab from the cart?") as null|obj in contents if(choice) diff --git a/code/game/objects/structures/kitchen_spike.dm b/code/game/objects/structures/kitchen_spike.dm index ecc3dffbb52f..eb239b4e9623 100644 --- a/code/game/objects/structures/kitchen_spike.dm +++ b/code/game/objects/structures/kitchen_spike.dm @@ -50,7 +50,7 @@ meat = 5 return 1 -/obj/structure/kitchenspike/attack_hand(mob/user, list/params) +/obj/structure/kitchenspike/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..() || !occupied) return meat-- diff --git a/code/game/objects/structures/loot_piles.dm b/code/game/objects/structures/loot_piles.dm index 95978d8a76fa..25738b913faa 100644 --- a/code/game/objects/structures/loot_piles.dm +++ b/code/game/objects/structures/loot_piles.dm @@ -42,7 +42,7 @@ Loot piles can be depleted, if loot_depleted is turned on. Note that players wh var/list/rare_loot = list() // Rare is powerful, or somewhat unique items. var/list/very_rare_loot = list()// Very Rare really powerful, or at least unique items. -/obj/structure/loot_pile/attack_hand(mob/user, list/params) +/obj/structure/loot_pile/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) //Human mob if(isliving(user)) var/mob/living/L = user diff --git a/code/game/objects/structures/medical_stand_vr.dm b/code/game/objects/structures/medical_stand_vr.dm index 0eed1c210f1a..ee4ca93c4d39 100644 --- a/code/game/objects/structures/medical_stand_vr.dm +++ b/code/game/objects/structures/medical_stand_vr.dm @@ -164,7 +164,7 @@ update_icon() -/obj/structure/medical_stand/attack_hand(mob/user, list/params) +/obj/structure/medical_stand/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/list/available_options = list() if (tank) available_options += "Toggle valve" diff --git a/code/game/objects/structures/mineral_bath.dm b/code/game/objects/structures/mineral_bath.dm index bf572a66f100..f729e46cd33f 100644 --- a/code/game/objects/structures/mineral_bath.dm +++ b/code/game/objects/structures/mineral_bath.dm @@ -66,7 +66,7 @@ START_PROCESSING(SSobj, src) return TRUE -/obj/structure/adherent_bath/attack_hand(mob/user, list/params) +/obj/structure/adherent_bath/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) eject_occupant() /obj/structure/adherent_bath/proc/eject_occupant() diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm index 9e12dc21cec7..d120edc51470 100644 --- a/code/game/objects/structures/mirror.dm +++ b/code/game/objects/structures/mirror.dm @@ -18,7 +18,7 @@ pixel_x = (dir & 3)? 0 : (dir == 4 ? -28 : 28) pixel_y = (dir & 3)? (dir == 1 ? -30 : 30) : 0 -/obj/structure/mirror/attack_hand(mob/user, list/params) +/obj/structure/mirror/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!glass) return if(shattered) return diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm index 18b979d9bbd8..c73ca39802fa 100644 --- a/code/game/objects/structures/morgue.dm +++ b/code/game/objects/structures/morgue.dm @@ -82,7 +82,7 @@ if(Adjacent(user)) attack_hand(user) -/obj/structure/morgue/attack_hand(mob/user, list/params) +/obj/structure/morgue/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (src.connected) close() else @@ -173,7 +173,7 @@ if(Adjacent(user)) attack_hand(user) -/obj/structure/m_tray/attack_hand(mob/user, list/params) +/obj/structure/m_tray/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (src.connected) for(var/atom/movable/A as mob|obj in src.loc) if (!( A.anchored )) @@ -227,7 +227,7 @@ GLOBAL_LIST_BOILERPLATE(all_crematoriums, /obj/structure/morgue/crematorium) src.icon_state = "crema1" return -/obj/structure/morgue/crematorium/attack_hand(mob/user, list/params) +/obj/structure/morgue/crematorium/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (cremating) to_chat(usr, "It's locked.") return @@ -350,7 +350,7 @@ GLOBAL_LIST_BOILERPLATE(all_crematoriums, /obj/structure/morgue/crematorium) req_access = list(ACCESS_GENERAL_CREMATOR) id = 1 -/obj/machinery/button/crematorium/attack_hand(mob/user, list/params) +/obj/machinery/button/crematorium/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return if(src.allowed(user)) diff --git a/code/game/objects/structures/noticeboard.dm b/code/game/objects/structures/noticeboard.dm index 9376814056a7..9a710c58a5f2 100644 --- a/code/game/objects/structures/noticeboard.dm +++ b/code/game/objects/structures/noticeboard.dm @@ -43,7 +43,7 @@ new /obj/item/frame/noticeboard( src.loc ) qdel(src) -/obj/structure/noticeboard/attack_hand(mob/user, list/params) +/obj/structure/noticeboard/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.do_examinate(src) // Since Topic() never seems to interact with usr on more than a superficial diff --git a/code/game/objects/structures/poster.dm b/code/game/objects/structures/poster.dm index fa5c8a1d0729..4c7978e4a6da 100644 --- a/code/game/objects/structures/poster.dm +++ b/code/game/objects/structures/poster.dm @@ -158,7 +158,7 @@ roll_and_drop(user.loc) return -/obj/structure/sign/poster/attack_hand(mob/user, list/params) +/obj/structure/sign/poster/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(ruined) return diff --git a/code/game/objects/structures/props/beam_prism.dm b/code/game/objects/structures/props/beam_prism.dm index 8a2d8b095fde..11ca14327755 100644 --- a/code/game/objects/structures/props/beam_prism.dm +++ b/code/game/objects/structures/props/beam_prism.dm @@ -48,7 +48,7 @@ var/degrees_to_rotate = -1 * degrees_from_north animate(src, transform = turn(src.transform, degrees_to_rotate), time = 2) -/obj/structure/prop/prism/attack_hand(mob/user, list/params) +/obj/structure/prop/prism/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() if(rotation_lock) @@ -160,7 +160,7 @@ var/list/my_turrets = list() var/dialID = null -/obj/structure/prop/prismcontrol/attack_hand(mob/user, list/params) +/obj/structure/prop/prismcontrol/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() var/confirm = input("Do you want to try to rotate \the [src]?", "[name]") in list("Yes", "No") diff --git a/code/game/objects/structures/props/nest.dm b/code/game/objects/structures/props/nest.dm index 2e494b42e30c..190e7f78c182 100644 --- a/code/game/objects/structures/props/nest.dm +++ b/code/game/objects/structures/props/nest.dm @@ -34,7 +34,7 @@ STOP_PROCESSING(SSobj, src) ..() -/obj/structure/prop/nest/attack_hand(mob/user, list/params) // Used to tell the player that this isn't useful for anything. +/obj/structure/prop/nest/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() if(user && prob(disturbance_spawn_chance)) spawn_creature(get_turf(src)) diff --git a/code/game/objects/structures/props/prop.dm b/code/game/objects/structures/props/prop.dm index c82b4b4dcdef..8471e5fde491 100644 --- a/code/game/objects/structures/props/prop.dm +++ b/code/game/objects/structures/props/prop.dm @@ -9,7 +9,7 @@ anchored = TRUE var/interaction_message = null -/obj/structure/prop/attack_hand(mob/user, list/params) // Used to tell the player that this isn't useful for anything. +/obj/structure/prop/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!istype(user)) return FALSE if(!interaction_message) diff --git a/code/game/objects/structures/props/puzzledoor.dm b/code/game/objects/structures/props/puzzledoor.dm index 8f429ac12f7b..d3e25d3acf8b 100644 --- a/code/game/objects/structures/props/puzzledoor.dm +++ b/code/game/objects/structures/props/puzzledoor.dm @@ -52,7 +52,7 @@ locks -= L ..() -/obj/machinery/door/blast/puzzle/attack_hand(mob/user, list/params) +/obj/machinery/door/blast/puzzle/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(check_locks()) force_toggle(1, user) else diff --git a/code/game/objects/structures/safe.dm b/code/game/objects/structures/safe.dm index f171fb7bfba4..7f4641ea6de5 100644 --- a/code/game/objects/structures/safe.dm +++ b/code/game/objects/structures/safe.dm @@ -70,7 +70,7 @@ FLOOR SAFES icon_state = initial(icon_state) -/obj/structure/safe/attack_hand(mob/user, list/params) +/obj/structure/safe/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.set_machine(src) var/dat = "

" dat += "[open ? "Close" : "Open"] [src] | - [dial * 5] +" diff --git a/code/game/objects/structures/simple_doors.dm b/code/game/objects/structures/simple_doors.dm index 9797dfd7ef23..12f948c593c3 100644 --- a/code/game/objects/structures/simple_doors.dm +++ b/code/game/objects/structures/simple_doors.dm @@ -49,7 +49,7 @@ if(get_dist(user,src) <= 1) //not remotely though return TryToSwitchState(user) -/obj/structure/simple_door/attack_hand(mob/user, list/params) +/obj/structure/simple_door/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_HARM) return ..() return TryToSwitchState(user) diff --git a/code/game/objects/structures/snowman.dm b/code/game/objects/structures/snowman.dm index 702d870a15ad..d3e5539eda5e 100644 --- a/code/game/objects/structures/snowman.dm +++ b/code/game/objects/structures/snowman.dm @@ -5,7 +5,7 @@ desc = "A happy little snowman smiles back at you!" anchored = 1 -/obj/structure/snowman/attack_hand(mob/user, list/params) +/obj/structure/snowman/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_HARM) to_chat(user, "In one hit, [src] easily crumples into a pile of snow. You monster.") var/turf/simulated/floor/F = get_turf(src) diff --git a/code/game/objects/structures/stasis_cage.dm b/code/game/objects/structures/stasis_cage.dm index e61d37956543..a4e3b3988a8b 100644 --- a/code/game/objects/structures/stasis_cage.dm +++ b/code/game/objects/structures/stasis_cage.dm @@ -14,7 +14,7 @@ if(A) contain(A) -/obj/structure/stasis_cage/attack_hand(mob/user, list/params) +/obj/structure/stasis_cage/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) release() /obj/structure/stasis_cage/attack_robot(var/mob/user) diff --git a/code/game/objects/structures/statues.dm b/code/game/objects/structures/statues.dm index 475ebfcbb9a2..30784dac7950 100644 --- a/code/game/objects/structures/statues.dm +++ b/code/game/objects/structures/statues.dm @@ -51,7 +51,7 @@ deconstruct(ATOM_DECONSTRUCT_DISASSEMBLED) return return ..() -/obj/structure/statue/attack_hand(mob/user, list/params) +/obj/structure/statue/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) user.visible_message("[user] rubs some dust off from the [name]'s surface.", \ "You rub some dust off from the [name]'s surface.") diff --git a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm index 26cece5c48f0..ebae37616894 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm @@ -51,7 +51,7 @@ qdel(src) return CLICKCHAIN_DO_NOT_PROPAGATE -/obj/structure/bed/chair/attack_hand(mob/user) +/obj/structure/bed/chair/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!stacked_size) return ..() var/obj/item/material/twohanded/folded_metal_chair/F = new(loc) diff --git a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm index bade56588a11..6f38a4dd187c 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm @@ -97,7 +97,7 @@ create_track() driving = 0 -/obj/structure/bed/chair/wheelchair/attack_hand(mob/user, list/params) +/obj/structure/bed/chair/wheelchair/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (pulling_along) MouseDrop(usr) return ..() diff --git a/code/game/objects/structures/tank_dispenser.dm b/code/game/objects/structures/tank_dispenser.dm index dc0ee1691ed6..4fadf95503c6 100644 --- a/code/game/objects/structures/tank_dispenser.dm +++ b/code/game/objects/structures/tank_dispenser.dm @@ -44,7 +44,7 @@ return attack_hand(user) ..() -/obj/structure/dispenser/attack_hand(mob/user, list/params) +/obj/structure/dispenser/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.set_machine(src) var/dat = "[src]

" dat += "Oxygen tanks: [oxygentanks] - [oxygentanks ? "Dispense" : "empty"]
" diff --git a/code/game/objects/structures/target_stake.dm b/code/game/objects/structures/target_stake.dm index 136ba2bc219c..73521b73b830 100644 --- a/code/game/objects/structures/target_stake.dm +++ b/code/game/objects/structures/target_stake.dm @@ -29,7 +29,7 @@ else return ..() -/obj/structure/target_stake/attack_hand(mob/user, list/params) +/obj/structure/target_stake/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) // taking pinned targets off! if(pinned_target) pinned_target.layer = OBJ_LAYER diff --git a/code/game/objects/structures/transit_tubes.dm b/code/game/objects/structures/transit_tubes.dm index 8c2052ee6146..3a85ad813067 100644 --- a/code/game/objects/structures/transit_tubes.dm +++ b/code/game/objects/structures/transit_tubes.dm @@ -102,7 +102,7 @@ return -/obj/structure/transit_tube/station/attack_hand(mob/user, list/params) +/obj/structure/transit_tube/station/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!pod_moving) for(var/obj/structure/transit_tube_pod/pod in loc) if(!pod.moving && (pod.dir in directions())) diff --git a/code/game/objects/structures/trash_pile.dm b/code/game/objects/structures/trash_pile.dm index 6fd09fa29026..ce0607b10237 100644 --- a/code/game/objects/structures/trash_pile.dm +++ b/code/game/objects/structures/trash_pile.dm @@ -79,7 +79,7 @@ else return ..() -/obj/structure/trash_pile/attack_hand(mob/user, list/params) +/obj/structure/trash_pile/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) //Human mob if(ishuman(user)) var/mob/living/carbon/human/H = user diff --git a/code/game/objects/structures/under_wardrobe.dm b/code/game/objects/structures/under_wardrobe.dm index 3436f3cda468..65fe0be1a86a 100644 --- a/code/game/objects/structures/under_wardrobe.dm +++ b/code/game/objects/structures/under_wardrobe.dm @@ -5,7 +5,7 @@ icon_state = "cabinet_closed" density = 1 -/obj/structure/undies_wardrobe/attack_hand(mob/user, list/params) +/obj/structure/undies_wardrobe/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!human_who_can_use_underwear(user)) to_chat(user, "Sadly there's nothing in here for you to wear.") return diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index a4939c7520a7..d94c0aa091ea 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -17,7 +17,7 @@ open = round(rand(0, 1)) update_icon() -/obj/structure/toilet/attack_hand(mob/user, list/params) +/obj/structure/toilet/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(swirlie) usr.setClickCooldown(user.get_attack_speed()) usr.visible_message("[user] slams the toilet seat onto [swirlie.name]'s head!", "You slam the toilet seat onto [swirlie.name]'s head!", "You hear reverberating porcelain.") @@ -172,7 +172,7 @@ anchored = 1 mouse_opacity = 0 -/obj/machinery/shower/attack_hand(mob/user, list/params) +/obj/machinery/shower/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/M = user if(!istype(M)) return @@ -388,7 +388,7 @@ thing.reagents.clear_reagents() thing.update_icon() -/obj/structure/sink/attack_hand(mob/user, list/params) +/obj/structure/sink/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (ishuman(user)) var/mob/living/carbon/human/H = user var/obj/item/organ/external/temp = H.organs_by_name["r_hand"] @@ -497,7 +497,7 @@ icon_state = "puddle" desc = "A small pool of some liquid, ostensibly water." -/obj/structure/sink/puddle/attack_hand(mob/user, list/params) +/obj/structure/sink/puddle/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) icon_state = "puddle-splash" ..() icon_state = "puddle" @@ -521,7 +521,7 @@ reagents.add_reagent(dispensedreagent, 20) /* Okay, just straight up, I tried to code this like blood overlays, but I just do NOT understand the system. If someone wants to sort it, enable this too. -/obj/structure/sink/oil_well/attack_hand(mob/user, list/params) +/obj/structure/sink/oil_well/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) flick("puddle-oil-splash",src) reagents.reaction(M, 20) //Covers target in 20u of oil. to_chat(M, "You touch the pool of oil, only to get oil all over yourself. It would be wise to wash this off with water.") diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 4f5081b13d04..efe8c3688804 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -207,7 +207,7 @@ user.visible_message(SPAN_NOTICE("Something knocks on [src].")) playsound(loc, 'sound/effects/Glasshit.ogg', 50, TRUE) -/obj/structure/window/attack_hand(mob/user, list/params) +/obj/structure/window/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_HARM) return ..() diff --git a/code/game/rendering/screen.dm b/code/game/rendering/screen.dm index 77bd443fe473..b379d64f7323 100644 --- a/code/game/rendering/screen.dm +++ b/code/game/rendering/screen.dm @@ -36,7 +36,7 @@ G.s_click(src) return 1 -/atom/movable/screen/grab/attack_hand(mob/user, list/params) +/atom/movable/screen/grab/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return /atom/movable/screen/grab/attackby() diff --git a/code/game/turfs/simulated/floor_types/snow.dm b/code/game/turfs/simulated/floor_types/snow.dm index 8d491ae4ceda..24df6a6e6f2d 100644 --- a/code/game/turfs/simulated/floor_types/snow.dm +++ b/code/game/turfs/simulated/floor_types/snow.dm @@ -41,7 +41,7 @@ CREATE_STANDARD_TURFS(/turf/simulated/floor/outdoors/snow) else ..() -/turf/simulated/floor/outdoors/snow/attack_hand(mob/user, list/params) +/turf/simulated/floor/outdoors/snow/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) visible_message("[user] starts scooping up some snow.", "You start scooping up some snow.") if(do_after(user, 1 SECOND)) user.put_in_hands_or_drop(new /obj/item/stack/material/snow) diff --git a/code/game/turfs/simulated/flooring/flooring_traps.dm b/code/game/turfs/simulated/flooring/flooring_traps.dm index 659166ce710f..944abc1e4cb7 100644 --- a/code/game/turfs/simulated/flooring/flooring_traps.dm +++ b/code/game/turfs/simulated/flooring/flooring_traps.dm @@ -31,7 +31,7 @@ else if (tripped) icon_state = "[initial(icon_state)]_tripped" -/turf/simulated/floor/trap/attack_hand(mob/user, list/params) +/turf/simulated/floor/trap/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(tripped) to_chat(usr, "You reset the triggered mechanism.") tripped = 0 diff --git a/code/game/turfs/simulated/wall/wall_attacks.dm b/code/game/turfs/simulated/wall/wall_attacks.dm index 9737790e6ac2..210112eb624c 100644 --- a/code/game/turfs/simulated/wall/wall_attacks.dm +++ b/code/game/turfs/simulated/wall/wall_attacks.dm @@ -82,7 +82,7 @@ return 0 -/turf/simulated/wall/attack_hand(mob/user, list/params) +/turf/simulated/wall/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) user.setClickCooldown(user.get_attack_speed()) var/rotting = (locate(/obj/effect/overlay/wallrot) in src) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index d8c34019b0ee..0b4c7320d100 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -254,7 +254,7 @@ /turf/proc/is_solid_structure() return TRUE -/turf/attack_hand(mob/user, list/params) +/turf/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() //QOL feature, clicking on turf can toggle doors, unless pulling something if(!user.pulling) diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index 5f83a4fa06b9..b9acfc00dc7e 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -85,7 +85,7 @@ a_right.holder_movement() -/obj/item/assembly_holder/attack_hand(mob/user, list/params)//Perhapse this should be a holder_pickup proc instead, can add if needbe I guess +/obj/item/assembly_holder/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(a_left && a_right) a_left.holder_movement() a_right.holder_movement() diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm index 891d109980bc..f06c66e968d7 100644 --- a/code/modules/assembly/mousetrap.dm +++ b/code/modules/assembly/mousetrap.dm @@ -68,7 +68,7 @@ playsound(user.loc, 'sound/weapons/handcuffs.ogg', 30, 1, -3) -/obj/item/assembly/mousetrap/attack_hand(mob/user, list/params) +/obj/item/assembly/mousetrap/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/L = user if(!istype(L)) return diff --git a/code/modules/atmospherics/machinery/air_alarm.dm b/code/modules/atmospherics/machinery/air_alarm.dm index 96a13304af28..e79db8b4533a 100644 --- a/code/modules/atmospherics/machinery/air_alarm.dm +++ b/code/modules/atmospherics/machinery/air_alarm.dm @@ -514,7 +514,7 @@ CREATE_WALL_MOUNTING_TYPES_SHIFTED(/obj/machinery/air_alarm, 26) /obj/machinery/air_alarm/attack_ai(mob/user) ui_interact(user) -/obj/machinery/air_alarm/attack_hand(mob/user, list/params) +/obj/machinery/air_alarm/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return diff --git a/code/modules/atmospherics/machinery/components/binary_devices/algae_generator_vr.dm b/code/modules/atmospherics/machinery/components/binary_devices/algae_generator_vr.dm index 799768392e2d..fb6a8bf7103e 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/algae_generator_vr.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/algae_generator_vr.dm @@ -137,7 +137,7 @@ to_chat(user, SPAN_NOTICE("You cannot insert this item into \the [src]!")) return -/obj/machinery/atmospherics/component/binary/algae_farm/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/component/binary/algae_farm/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return TRUE ui_interact(user) diff --git a/code/modules/atmospherics/machinery/components/binary_devices/heat_pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/heat_pump.dm index f9fa8cb4c6a7..5e4bc19db5cd 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/heat_pump.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/heat_pump.dm @@ -109,7 +109,7 @@ "You hear ratchet.") deconstruct() -/obj/machinery/atmospherics/component/binary/heat_pump/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/component/binary/heat_pump/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return src.add_fingerprint(usr) diff --git a/code/modules/atmospherics/machinery/components/binary_devices/massive_gas_pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/massive_gas_pump.dm index 0ac892f84e8f..02f90da33167 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/massive_gas_pump.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/massive_gas_pump.dm @@ -147,7 +147,7 @@ return data -/obj/machinery/atmospherics/component/binary/massive_gas_pump/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/component/binary/massive_gas_pump/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return add_fingerprint(usr) diff --git a/code/modules/atmospherics/machinery/components/binary_devices/massive_heat_pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/massive_heat_pump.dm index 6704cfe860e7..bc6706dd316b 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/massive_heat_pump.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/massive_heat_pump.dm @@ -171,7 +171,7 @@ return data -/obj/machinery/atmospherics/component/binary/massive_heat_pump/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/component/binary/massive_heat_pump/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return add_fingerprint(usr) diff --git a/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm b/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm index 7cbe517906e0..66b1368d7eba 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm @@ -165,7 +165,7 @@ update_icon() return -/obj/machinery/atmospherics/component/binary/passive_gate/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/component/binary/passive_gate/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return add_fingerprint(usr) diff --git a/code/modules/atmospherics/machinery/components/binary_devices/pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/pump.dm index 3ad9ead3179c..f800ce95d03e 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/pump.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/pump.dm @@ -228,7 +228,7 @@ Thus, the two variables affect pump operation are set in New(): . = ..() ui_interact(user) -/obj/machinery/atmospherics/component/binary/pump/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/component/binary/pump/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return add_fingerprint(usr) diff --git a/code/modules/atmospherics/machinery/components/binary_devices/valve.dm b/code/modules/atmospherics/machinery/components/binary_devices/valve.dm index b7259bbe650e..be7fc2a85e66 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/valve.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/valve.dm @@ -127,7 +127,7 @@ /obj/machinery/atmospherics/valve/attack_ai(mob/user as mob) return -/obj/machinery/atmospherics/valve/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/valve/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) src.add_fingerprint(usr) update_icon() sleep(10) @@ -231,7 +231,7 @@ /obj/machinery/atmospherics/valve/digital/attack_ai(mob/user as mob) return src.attack_hand(user) -/obj/machinery/atmospherics/valve/digital/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/valve/digital/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!powered()) return if(!src.allowed(user)) diff --git a/code/modules/atmospherics/machinery/components/omni_devices/omni_base.dm b/code/modules/atmospherics/machinery/components/omni_devices/omni_base.dm index 2fb778a832bf..d0b30901194f 100644 --- a/code/modules/atmospherics/machinery/components/omni_devices/omni_base.dm +++ b/code/modules/atmospherics/machinery/components/omni_devices/omni_base.dm @@ -103,7 +103,7 @@ return TRUE return FALSE -/obj/machinery/atmospherics/component/quaternary/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/component/quaternary/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return diff --git a/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm b/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm index f5c4892d1606..1af33310211e 100644 --- a/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm +++ b/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm @@ -94,7 +94,7 @@ data["node2_dir"] = dir_name(node_connects[2],TRUE) return data -/obj/machinery/atmospherics/component/trinary/mixer/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/component/trinary/mixer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return ui_interact(user) diff --git a/code/modules/atmospherics/machinery/components/trinary_devices/tvalve.dm b/code/modules/atmospherics/machinery/components/trinary_devices/tvalve.dm index d9cc0b620281..6df7bc5a6a39 100644 --- a/code/modules/atmospherics/machinery/components/trinary_devices/tvalve.dm +++ b/code/modules/atmospherics/machinery/components/trinary_devices/tvalve.dm @@ -157,7 +157,7 @@ /obj/machinery/atmospherics/tvalve/attack_ai(mob/user as mob) return -/obj/machinery/atmospherics/tvalve/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/tvalve/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) src.add_fingerprint(usr) animation() sleep(10) @@ -281,7 +281,7 @@ /obj/machinery/atmospherics/tvalve/digital/attack_ai(mob/user as mob) return src.attack_hand(user) -/obj/machinery/atmospherics/tvalve/digital/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/tvalve/digital/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!powered()) return if(!src.allowed(user)) diff --git a/code/modules/atmospherics/machinery/components/unary/gas_freezer.dm b/code/modules/atmospherics/machinery/components/unary/gas_freezer.dm index e0df0c43d522..e7c576661f2c 100644 --- a/code/modules/atmospherics/machinery/components/unary/gas_freezer.dm +++ b/code/modules/atmospherics/machinery/components/unary/gas_freezer.dm @@ -59,7 +59,7 @@ /obj/machinery/atmospherics/component/unary/freezer/attack_ai(mob/user as mob) ui_interact(user) -/obj/machinery/atmospherics/component/unary/freezer/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/component/unary/freezer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /obj/machinery/atmospherics/component/unary/freezer/ui_interact(mob/user, datum/tgui/ui) diff --git a/code/modules/atmospherics/machinery/components/unary/gas_heater.dm b/code/modules/atmospherics/machinery/components/unary/gas_heater.dm index 1e3a14a325c5..022f5157f20b 100644 --- a/code/modules/atmospherics/machinery/components/unary/gas_heater.dm +++ b/code/modules/atmospherics/machinery/components/unary/gas_heater.dm @@ -73,7 +73,7 @@ /obj/machinery/atmospherics/component/unary/heater/attack_ai(mob/user as mob) ui_interact(user) -/obj/machinery/atmospherics/component/unary/heater/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/component/unary/heater/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /obj/machinery/atmospherics/component/unary/heater/ui_interact(mob/user, datum/tgui/ui) diff --git a/code/modules/atmospherics/machinery/components/unary/outlet_injector.dm b/code/modules/atmospherics/machinery/components/unary/outlet_injector.dm index 9e3fccd31a00..ca0498f07b66 100644 --- a/code/modules/atmospherics/machinery/components/unary/outlet_injector.dm +++ b/code/modules/atmospherics/machinery/components/unary/outlet_injector.dm @@ -193,7 +193,7 @@ broadcast_status() update_icon() -/obj/machinery/atmospherics/component/unary/outlet_injector/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/component/unary/outlet_injector/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /obj/machinery/atmospherics/component/unary/outlet_injector/proc/toggle_injecting() diff --git a/code/modules/atmospherics/machinery/portable/area_atmos_computer.dm b/code/modules/atmospherics/machinery/portable/area_atmos_computer.dm index b7f1e566ce16..6b799e21fdce 100644 --- a/code/modules/atmospherics/machinery/portable/area_atmos_computer.dm +++ b/code/modules/atmospherics/machinery/portable/area_atmos_computer.dm @@ -23,7 +23,7 @@ /obj/machinery/computer/area_atmos/attack_ai(mob/user) return src.attack_hand(user) -/obj/machinery/computer/area_atmos/attack_hand(mob/user, list/params) +/obj/machinery/computer/area_atmos/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..(user)) return ui_interact(user) diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm index c70bc68b6aa6..216eae7ccc77 100644 --- a/code/modules/atmospherics/machinery/portable/canister.dm +++ b/code/modules/atmospherics/machinery/portable/canister.dm @@ -307,7 +307,7 @@ update_flag /obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user as mob) return src.attack_hand(user) -/obj/machinery/portable_atmospherics/canister/attack_hand(mob/user, list/params) +/obj/machinery/portable_atmospherics/canister/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return src.ui_interact(user) /obj/machinery/portable_atmospherics/canister/ui_state() diff --git a/code/modules/atmospherics/machinery/portable/pump.dm b/code/modules/atmospherics/machinery/portable/pump.dm index dc3fa0c1134e..1e314a16cb8f 100644 --- a/code/modules/atmospherics/machinery/portable/pump.dm +++ b/code/modules/atmospherics/machinery/portable/pump.dm @@ -105,7 +105,7 @@ . = ..() return src.attack_hand(user) -/obj/machinery/portable_atmospherics/powered/pump/attack_hand(mob/user, list/params) +/obj/machinery/portable_atmospherics/powered/pump/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /obj/machinery/portable_atmospherics/powered/pump/ui_interact(mob/user, datum/tgui/ui) diff --git a/code/modules/awaymissions/bluespaceartillery.dm b/code/modules/awaymissions/bluespaceartillery.dm index 70aa1e5bd534..466351a52353 100644 --- a/code/modules/awaymissions/bluespaceartillery.dm +++ b/code/modules/awaymissions/bluespaceartillery.dm @@ -20,7 +20,7 @@ /obj/structure/artilleryplaceholder/decorative density = 0 -/obj/machinery/artillerycontrol/attack_hand(mob/user, list/params) +/obj/machinery/artillerycontrol/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.set_machine(src) var/dat = "Bluespace Artillery Control:
" dat += "Locked on
" diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm index fa2f3fdbdd92..6617f54e03e5 100644 --- a/code/modules/clothing/masks/miscellaneous.dm +++ b/code/modules/clothing/masks/miscellaneous.dm @@ -21,7 +21,7 @@ say_verbs = list("mumbles", "says") // Clumsy folks can't take the mask off themselves. -/obj/item/clothing/mask/muzzle/attack_hand(mob/user, list/params) +/obj/item/clothing/mask/muzzle/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.item_by_slot_id(SLOT_ID_MASK) == src && !user.IsAdvancedToolUser()) return 0 ..() diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm index db94bf4b60fe..8d43ec80d2b9 100644 --- a/code/modules/clothing/shoes/_shoes.dm +++ b/code/modules/clothing/shoes/_shoes.dm @@ -66,7 +66,7 @@ update_icon() -/obj/item/clothing/shoes/attack_hand(mob/user, list/params) +/obj/item/clothing/shoes/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(can_hold_knife == 1 && holding && src.loc == user) draw_knife() return diff --git a/code/modules/clothing/under/accessories/accessory.dm b/code/modules/clothing/under/accessories/accessory.dm index ca0607567040..a9c28b8385af 100644 --- a/code/modules/clothing/under/accessories/accessory.dm +++ b/code/modules/clothing/under/accessories/accessory.dm @@ -133,7 +133,7 @@ ..() //default attack_hand behaviour -/obj/item/clothing/accessory/attack_hand(mob/user, list/params) +/obj/item/clothing/accessory/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(accessory_host) return //we aren't an object on the ground so don't call parent ..() diff --git a/code/modules/clothing/under/accessories/holster.dm b/code/modules/clothing/under/accessories/holster.dm index 20d169cc85ea..339fda618d4a 100644 --- a/code/modules/clothing/under/accessories/holster.dm +++ b/code/modules/clothing/under/accessories/holster.dm @@ -67,7 +67,7 @@ set_weight_class(initial(w_class)) clear_holster() -/obj/item/clothing/accessory/holster/attack_hand(mob/user, list/params) +/obj/item/clothing/accessory/holster/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (accessory_host && (slot & ACCESSORY_SLOT_UTILITY)) if(holstered) unholster(user) diff --git a/code/modules/detectivework/microscope/dnascanner.dm b/code/modules/detectivework/microscope/dnascanner.dm index 58699c567182..281fdbb77ee9 100644 --- a/code/modules/detectivework/microscope/dnascanner.dm +++ b/code/modules/detectivework/microscope/dnascanner.dm @@ -142,7 +142,7 @@ /obj/machinery/dnaforensics/attack_ai(mob/user) ui_interact(user) -/obj/machinery/dnaforensics/attack_hand(mob/user, list/params) +/obj/machinery/dnaforensics/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /obj/machinery/dnaforensics/update_icon() diff --git a/code/modules/detectivework/microscope/microscope.dm b/code/modules/detectivework/microscope/microscope.dm index 5be88931cf1d..696f0910d0c5 100644 --- a/code/modules/detectivework/microscope/microscope.dm +++ b/code/modules/detectivework/microscope/microscope.dm @@ -25,7 +25,7 @@ return return ..() -/obj/machinery/microscope/attack_hand(mob/user, list/params) +/obj/machinery/microscope/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!sample) to_chat(user, "The microscope has no sample to examine.") diff --git a/code/modules/economy/machines/ATM.dm b/code/modules/economy/machines/ATM.dm index e2967158e2c9..3fdd838f12de 100644 --- a/code/modules/economy/machines/ATM.dm +++ b/code/modules/economy/machines/ATM.dm @@ -308,7 +308,7 @@ GLOBAL_LIST_INIT(atm_sounds, list('sound/items/polaroid1.ogg', 'sound/items/pola authenticated_account = null account_security_level = 0 -/obj/machinery/atm/attack_hand(mob/user, list/params) +/obj/machinery/atm/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(istype(user, /mob/living/silicon)) to_chat (user, SPAN_WARNING("A firewall prevents you from interfacing with this device!")) return diff --git a/code/modules/economy/machines/Accounts_DB.dm b/code/modules/economy/machines/Accounts_DB.dm index 08cfd60644de..7eef108386f4 100644 --- a/code/modules/economy/machines/Accounts_DB.dm +++ b/code/modules/economy/machines/Accounts_DB.dm @@ -57,7 +57,7 @@ attack_hand(user) -/obj/machinery/account_database/attack_hand(mob/user, list/params) +/obj/machinery/account_database/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (NOPOWER|BROKEN)) return nano_ui_interact(user) diff --git a/code/modules/economy/machines/cash_register.dm b/code/modules/economy/machines/cash_register.dm index 99da9607305b..893f02028bb6 100644 --- a/code/modules/economy/machines/cash_register.dm +++ b/code/modules/economy/machines/cash_register.dm @@ -39,7 +39,7 @@ . += "It's completely empty." -/obj/machinery/cash_register/attack_hand(mob/user, list/params) +/obj/machinery/cash_register/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) // Don't be accessible from the wrong side of the machine if(get_dir(src, user) & global.reverse_dir[src.dir]) return diff --git a/code/modules/economy/machines/mint.dm b/code/modules/economy/machines/mint.dm index 858b40fa4ed5..daacc66ee439 100644 --- a/code/modules/economy/machines/mint.dm +++ b/code/modules/economy/machines/mint.dm @@ -81,7 +81,7 @@ if(processed) qdel(O) -/obj/machinery/mineral/mint/attack_hand(mob/user, list/params) +/obj/machinery/mineral/mint/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/dat = "Coin Press
" @@ -474,7 +474,7 @@ /obj/machinery/coinbank/attack_ai(mob/user as mob) attack_hand(user) -/obj/machinery/coinbank/attack_hand(mob/user, list/params) +/obj/machinery/coinbank/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (NOPOWER|BROKEN)) return wires.Interact(user) diff --git a/code/modules/economy/money_bag.dm b/code/modules/economy/money_bag.dm index 04a467c8cff1..334bf4051b5f 100644 --- a/code/modules/economy/money_bag.dm +++ b/code/modules/economy/money_bag.dm @@ -8,7 +8,7 @@ throw_force = 2.0 w_class = WEIGHT_CLASS_BULKY -/obj/item/moneybag/attack_hand(mob/user, list/params) +/obj/item/moneybag/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/amt_supermatter = 0 var/amt_bananium = 0 var/amt_mhydrogen = 0 diff --git a/code/modules/food/drinkingglass/extras.dm b/code/modules/food/drinkingglass/extras.dm index d51297c70588..94b0d7690eb9 100644 --- a/code/modules/food/drinkingglass/extras.dm +++ b/code/modules/food/drinkingglass/extras.dm @@ -26,7 +26,7 @@ else return ..() -/obj/item/reagent_containers/food/drinks/glass2/attack_hand(mob/user, list/params) +/obj/item/reagent_containers/food/drinks/glass2/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(src != user.get_inactive_held_item()) return ..() diff --git a/code/modules/food/food/snacks.dm b/code/modules/food/food/snacks.dm index f0b26d356190..ab89a235a24c 100644 --- a/code/modules/food/food/snacks.dm +++ b/code/modules/food/food/snacks.dm @@ -3273,7 +3273,7 @@ add_overlay(overlays_to_add) -/obj/item/pizzabox/attack_hand(mob/user, list/params) +/obj/item/pizzabox/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if( open && pizza ) user.put_in_hands( pizza ) @@ -4807,7 +4807,7 @@ END CITADEL CHANGE */ bitesize = 1 nutriment_amt = 10 -/obj/item/reagent_containers/food/snacks/chipplate/attack_hand(mob/user, list/params) +/obj/item/reagent_containers/food/snacks/chipplate/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) // todo: sigh, no ..(); shift over to on_attack_hand var/obj/item/reagent_containers/food/snacks/returningitem = new vendingobject(loc) returningitem.reagents.clear_reagents() diff --git a/code/modules/food/glass/bottle.dm b/code/modules/food/glass/bottle.dm index 76736ada9951..7e47c25b1cee 100644 --- a/code/modules/food/glass/bottle.dm +++ b/code/modules/food/glass/bottle.dm @@ -26,7 +26,7 @@ ..() update_icon() -/obj/item/reagent_containers/glass/bottle/attack_hand(mob/user, list/params) +/obj/item/reagent_containers/glass/bottle/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() update_icon() diff --git a/code/modules/food/machinery/appliance/_appliance.dm b/code/modules/food/machinery/appliance/_appliance.dm index 8bf657ddface..641141ac8286 100644 --- a/code/modules/food/machinery/appliance/_appliance.dm +++ b/code/modules/food/machinery/appliance/_appliance.dm @@ -541,7 +541,7 @@ smoke.set_up(10, 0, get_turf(src), 300) smoke.start() -/obj/machinery/appliance/attack_hand(mob/user, list/params) +/obj/machinery/appliance/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (cooking_objs.len) if (removal_menu(user)) return diff --git a/code/modules/food/machinery/gibber.dm b/code/modules/food/machinery/gibber.dm index 128eed53e599..c5090d677078 100644 --- a/code/modules/food/machinery/gibber.dm +++ b/code/modules/food/machinery/gibber.dm @@ -76,7 +76,7 @@ src.go_out() return -/obj/machinery/gibber/attack_hand(mob/user, list/params) +/obj/machinery/gibber/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (NOPOWER|BROKEN)) return if(operating) diff --git a/code/modules/food/machinery/microwave.dm b/code/modules/food/machinery/microwave.dm index fbd660f080f2..d31dbd2e4ae9 100644 --- a/code/modules/food/machinery/microwave.dm +++ b/code/modules/food/machinery/microwave.dm @@ -162,7 +162,7 @@ if(istype(user, /mob/living/silicon/robot) && Adjacent(user)) attack_hand(user) -/obj/machinery/microwave/attack_hand(mob/user, list/params) +/obj/machinery/microwave/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.set_machine(src) interact(user) diff --git a/code/modules/food/machinery/smartfridge.dm b/code/modules/food/machinery/smartfridge.dm index bfa4a59d7a46..d75dd5222df9 100644 --- a/code/modules/food/machinery/smartfridge.dm +++ b/code/modules/food/machinery/smartfridge.dm @@ -202,7 +202,7 @@ /obj/machinery/smartfridge/attack_ai(mob/user as mob) attack_hand(user) -/obj/machinery/smartfridge/attack_hand(mob/user, list/params) +/obj/machinery/smartfridge/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (NOPOWER|BROKEN)) return wires.Interact(user) diff --git a/code/modules/games/cards.dm b/code/modules/games/cards.dm index 7b76176cb2ae..1fb2c99dffed 100644 --- a/code/modules/games/cards.dm +++ b/code/modules/games/cards.dm @@ -67,7 +67,7 @@ return ..() -/obj/item/deck/attack_hand(mob/user, list/params) +/obj/item/deck/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/carbon/human/H = user if(istype(src.loc, /obj/item/storage) || src == H.r_store || src == H.l_store || src.loc == user) // so objects can be removed from storage containers or pockets. also added a catch-all, so if it's in the mob you'll pick it up. ..() diff --git a/code/modules/gateway/stargate/stargate-away.dm b/code/modules/gateway/stargate/stargate-away.dm index 424ad7fb9294..9a1e694cf287 100644 --- a/code/modules/gateway/stargate/stargate-away.dm +++ b/code/modules/gateway/stargate/stargate-away.dm @@ -67,7 +67,7 @@ update_icon() -/obj/machinery/gateway/centeraway/attack_hand(mob/user, list/params) +/obj/machinery/gateway/centeraway/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!ready) detect() return diff --git a/code/modules/gateway/stargate/stargate-station.dm b/code/modules/gateway/stargate/stargate-station.dm index 9708760a87b8..b7e780051a44 100644 --- a/code/modules/gateway/stargate/stargate-station.dm +++ b/code/modules/gateway/stargate/stargate-station.dm @@ -84,7 +84,7 @@ update_icon() -/obj/machinery/gateway/centerstation/attack_hand(mob/user, list/params) +/obj/machinery/gateway/centerstation/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!ready) detect() return diff --git a/code/modules/hardsuits/pieces.dm b/code/modules/hardsuits/pieces.dm index 6615b4240656..84688d96acc5 100644 --- a/code/modules/hardsuits/pieces.dm +++ b/code/modules/hardsuits/pieces.dm @@ -85,7 +85,7 @@ hardsuit = null return ..() -/obj/item/clothing/suit/space/hardsuit/attack_hand(mob/user, list/params) +/obj/item/clothing/suit/space/hardsuit/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(tacknife) tacknife.loc = get_turf(src) if(user.put_in_active_hand(tacknife)) diff --git a/code/modules/hardsuits/rig_attackby.dm b/code/modules/hardsuits/rig_attackby.dm index 52330ed2c0f6..2e85da1c35e7 100644 --- a/code/modules/hardsuits/rig_attackby.dm +++ b/code/modules/hardsuits/rig_attackby.dm @@ -172,7 +172,7 @@ ..() -/obj/item/hardsuit/attack_hand(mob/user, list/params) +/obj/item/hardsuit/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(electrified != 0) if(shock(user)) //Handles removing charge from the cell, as well. No need to do that here. diff --git a/code/modules/hardsuits/rig_pieces.dm b/code/modules/hardsuits/rig_pieces.dm index 31cea56b0346..471a9793cdc9 100644 --- a/code/modules/hardsuits/rig_pieces.dm +++ b/code/modules/hardsuits/rig_pieces.dm @@ -178,7 +178,7 @@ SPECIES_ZORREN_HIGH, ) -/obj/item/clothing/suit/space/hardsuit/attack_hand(mob/user, list/params) +/obj/item/clothing/suit/space/hardsuit/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(tacknife) tacknife.loc = get_turf(src) if(user.put_in_active_hand(tacknife)) diff --git a/code/modules/holodeck/HolodeckControl.dm b/code/modules/holodeck/HolodeckControl.dm index b5697ff0b7fe..95b91e324001 100644 --- a/code/modules/holodeck/HolodeckControl.dm +++ b/code/modules/holodeck/HolodeckControl.dm @@ -75,7 +75,7 @@ /obj/machinery/computer/HolodeckControl/attack_ai(var/mob/user as mob) return src.attack_hand(user) -/obj/machinery/computer/HolodeckControl/attack_hand(mob/user, list/params) +/obj/machinery/computer/HolodeckControl/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return user.set_machine(src) diff --git a/code/modules/holodeck/HolodeckObjects.dm b/code/modules/holodeck/HolodeckObjects.dm index dfe40dcf534f..73ca10c2a5fb 100644 --- a/code/modules/holodeck/HolodeckObjects.dm +++ b/code/modules/holodeck/HolodeckObjects.dm @@ -359,7 +359,7 @@ /obj/machinery/readybutton/attackby(obj/item/W as obj, mob/user as mob) to_chat(user, "The device is a solid button, there's nothing you can do with it!") -/obj/machinery/readybutton/attack_hand(mob/user, list/params) +/obj/machinery/readybutton/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.stat || machine_stat & (NOPOWER|BROKEN)) to_chat(user, "This device is not powered.") diff --git a/code/modules/holomap/station_holomap.dm b/code/modules/holomap/station_holomap.dm index d89f025a7aef..1a68c7e39f6f 100644 --- a/code/modules/holomap/station_holomap.dm +++ b/code/modules/holomap/station_holomap.dm @@ -70,7 +70,7 @@ // floor_markings.layer = FLOOR_DECAL_LAYER update_icon() -/obj/machinery/station_map/attack_hand(mob/user, list/params) +/obj/machinery/station_map/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(watching_mob && (watching_mob != user)) to_chat(user, "Someone else is currently watching the holomap.") return diff --git a/code/modules/hydroponics/beekeeping/beehive.dm b/code/modules/hydroponics/beekeeping/beehive.dm index 5b6cd3714752..7e3e2e941fe0 100644 --- a/code/modules/hydroponics/beekeeping/beehive.dm +++ b/code/modules/hydroponics/beekeeping/beehive.dm @@ -122,7 +122,7 @@ qdel(src) return -/obj/machinery/beehive/attack_hand(mob/user, list/params) +/obj/machinery/beehive/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!closed) if(honeycombs < 100) to_chat(user, "There are no filled honeycombs.") diff --git a/code/modules/hydroponics/seed_machines.dm b/code/modules/hydroponics/seed_machines.dm index b649fc10f1f5..11f538991210 100644 --- a/code/modules/hydroponics/seed_machines.dm +++ b/code/modules/hydroponics/seed_machines.dm @@ -63,7 +63,7 @@ /obj/machinery/botany/attack_ai(mob/user as mob) return attack_hand(user) -/obj/machinery/botany/attack_hand(mob/user, list/params) +/obj/machinery/botany/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /obj/machinery/botany/proc/finished_task() diff --git a/code/modules/hydroponics/seed_storage.dm b/code/modules/hydroponics/seed_storage.dm index 2d8ddb5c6885..1c05181dcd54 100644 --- a/code/modules/hydroponics/seed_storage.dm +++ b/code/modules/hydroponics/seed_storage.dm @@ -206,7 +206,7 @@ /obj/item/seeds/whitebeetseed = 3 ) -/obj/machinery/seed_storage/attack_hand(mob/user, list/params) +/obj/machinery/seed_storage/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (BROKEN|NOPOWER)) return diff --git a/code/modules/hydroponics/spreading/spreading.dm b/code/modules/hydroponics/spreading/spreading.dm index 08d3b1d3ad03..49a80bab7d2c 100644 --- a/code/modules/hydroponics/spreading/spreading.dm +++ b/code/modules/hydroponics/spreading/spreading.dm @@ -36,7 +36,7 @@ integrity_enabled = TRUE obj_flags = OBJ_MELEE_TARGETABLE | OBJ_RANGE_TARGETABLE -/obj/effect/dead_plant/attack_hand(mob/user, list/params) +/obj/effect/dead_plant/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) qdel(src) /obj/effect/dead_plant/attackby() diff --git a/code/modules/hydroponics/spreading/spreading_response.dm b/code/modules/hydroponics/spreading/spreading_response.dm index 88657e388d31..016ae848fa3a 100644 --- a/code/modules/hydroponics/spreading/spreading_response.dm +++ b/code/modules/hydroponics/spreading/spreading_response.dm @@ -34,7 +34,7 @@ if(!M.apply_damage(base_damage, BRUTE, target_zone, blocked, soaked, used_weapon=src)) return 0 -/obj/effect/plant/attack_hand(mob/user, list/params) +/obj/effect/plant/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) manual_unbuckle(user) /obj/effect/plant/attack_generic(var/mob/user) diff --git a/code/modules/hydroponics/trays/tray.dm b/code/modules/hydroponics/trays/tray.dm index 6003b3b9eabb..707cf82533f7 100644 --- a/code/modules/hydroponics/trays/tray.dm +++ b/code/modules/hydroponics/trays/tray.dm @@ -576,7 +576,7 @@ else if(harvest) harvest(user) -/obj/machinery/portable_atmospherics/hydroponics/attack_hand(mob/user, list/params) +/obj/machinery/portable_atmospherics/hydroponics/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(istype(usr,/mob/living/silicon)) return diff --git a/code/modules/industry/conveyors/conveyor.dm b/code/modules/industry/conveyors/conveyor.dm index 1f7c9b381b16..d0510a97b8ac 100644 --- a/code/modules/industry/conveyors/conveyor.dm +++ b/code/modules/industry/conveyors/conveyor.dm @@ -162,7 +162,7 @@ return ..() // attack with hand, move pulled object onto conveyor -/obj/machinery/conveyor/attack_hand(mob/user, list/params) +/obj/machinery/conveyor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE)) return if(isnull(user.pulling) || user.pulling.anchored) diff --git a/code/modules/industry/conveyors/conveyor_switch.dm b/code/modules/industry/conveyors/conveyor_switch.dm index 6992ecf1ed2b..19d3acdef2ad 100644 --- a/code/modules/industry/conveyors/conveyor_switch.dm +++ b/code/modules/industry/conveyors/conveyor_switch.dm @@ -51,7 +51,7 @@ C.setmove() // attack with hand, switch position -/obj/machinery/conveyor_switch/attack_hand(mob/user, list/params) +/obj/machinery/conveyor_switch/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!allowed(user)) to_chat(user, "Access denied.") return @@ -112,7 +112,7 @@ desc = "A conveyor control switch. It appears to only go in one direction." // attack with hand, switch position -/obj/machinery/conveyor_switch/oneway/attack_hand(mob/user, list/params) +/obj/machinery/conveyor_switch/oneway/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(position == 0) position = convdir else diff --git a/code/modules/industry/disposals/disposal/chute.dm b/code/modules/industry/disposals/disposal/chute.dm index 572b165c5b28..7fd8681bffb5 100644 --- a/code/modules/industry/disposals/disposal/chute.dm +++ b/code/modules/industry/disposals/disposal/chute.dm @@ -219,7 +219,7 @@ interact(user, 1) // human interact with machine -/obj/machinery/disposal/attack_hand(mob/user, list/params) +/obj/machinery/disposal/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & BROKEN) return diff --git a/code/modules/industry/packages/large_parcel.dm b/code/modules/industry/packages/large_parcel.dm index 268d5a6f94ba..c1c40307b0ba 100644 --- a/code/modules/industry/packages/large_parcel.dm +++ b/code/modules/industry/packages/large_parcel.dm @@ -26,7 +26,7 @@ AM.forceMove(T) return ..() -/obj/structure/bigDelivery/attack_hand(mob/user, list/params) +/obj/structure/bigDelivery/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) unwrap() /obj/structure/bigDelivery/proc/unwrap() diff --git a/code/modules/instruments/instruments/stationary.dm b/code/modules/instruments/instruments/stationary.dm index 390f10f10f11..1adf54e84ac3 100644 --- a/code/modules/instruments/instruments/stationary.dm +++ b/code/modules/instruments/instruments/stationary.dm @@ -23,7 +23,7 @@ return (usr.default_can_use_topic(src) < UI_UPDATE) //can play with MUTATION_TELEKINESIS and while resting because fun. /// CITRP EDIT UNTIL INTERACTION REFACTOR PORT! -/obj/structure/musician/attack_hand(mob/user, list/params) +/obj/structure/musician/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return diff --git a/code/modules/integrated_electronics/core/assemblies.dm b/code/modules/integrated_electronics/core/assemblies.dm index d9765357ef40..43cfa1a3546b 100644 --- a/code/modules/integrated_electronics/core/assemblies.dm +++ b/code/modules/integrated_electronics/core/assemblies.dm @@ -609,7 +609,7 @@ return ..() -/obj/item/electronic_assembly/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags) +/obj/item/electronic_assembly/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(anchored) attack_self(user) return diff --git a/code/modules/library/lib_items.dm b/code/modules/library/lib_items.dm index 28d1d9450f14..5df9c106a829 100644 --- a/code/modules/library/lib_items.dm +++ b/code/modules/library/lib_items.dm @@ -54,7 +54,7 @@ else ..() -/obj/structure/bookcase/attack_hand(mob/user, list/params) +/obj/structure/bookcase/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(contents.len) var/obj/item/book/choice = input("Which book would you like to remove from the shelf?") as null|obj in contents if(choice) diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm index 7a2d668fff97..3980cecb08d0 100644 --- a/code/modules/library/lib_machines.dm +++ b/code/modules/library/lib_machines.dm @@ -31,7 +31,7 @@ var/category = "Any" var/author -/obj/machinery/librarypubliccomp/attack_hand(mob/user, list/params) +/obj/machinery/librarypubliccomp/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) usr.set_machine(src) var/dat = "Library Visitor\n" // switch(screenstate) @@ -160,7 +160,7 @@ var/obj/item/book/M = new path(null) all_books[M.title] = M -/obj/machinery/librarycomp/attack_hand(mob/user, list/params) +/obj/machinery/librarycomp/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) usr.set_machine(src) var/dat = "Book Inventory Management\n" // switch(screenstate) @@ -476,7 +476,7 @@ if(!user.attempt_insert_item_for_installation(I, src)) return -/obj/machinery/libraryscanner/attack_hand(mob/user, list/params) +/obj/machinery/libraryscanner/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) usr.set_machine(src) var/dat = "Scanner Control Interface\n" // if(cache) diff --git a/code/modules/maps/away_missions/archive/wildwest.dm b/code/modules/maps/away_missions/archive/wildwest.dm index f343556da93c..bb760ae21012 100644 --- a/code/modules/maps/away_missions/archive/wildwest.dm +++ b/code/modules/maps/away_missions/archive/wildwest.dm @@ -20,7 +20,7 @@ var/chargesa = 1 var/insistinga = 0 -/obj/machinery/wish_granter_dark/attack_hand(mob/user, list/params) +/obj/machinery/wish_granter_dark/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) usr.set_machine(src) if(chargesa <= 0) diff --git a/code/modules/maps/misc_maps/lavaland/_lavaland.dm b/code/modules/maps/misc_maps/lavaland/_lavaland.dm index 3b85aae9b434..6c5eb869c54c 100644 --- a/code/modules/maps/misc_maps/lavaland/_lavaland.dm +++ b/code/modules/maps/misc_maps/lavaland/_lavaland.dm @@ -22,7 +22,7 @@ use_power = USE_POWER_IDLE interaction_flags_machine = INTERACT_MACHINE_OFFLINE | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OFFLINE_SILICON -/obj/machinery/lavaland_entryportal/attack_hand(mob/user, list/params) +/obj/machinery/lavaland_entryportal/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(istype(user, /mob/living/silicon/ai)) // lets not teleport AI cores return if(inoperable(MAINT)) @@ -58,7 +58,7 @@ anchored = 1 -/obj/effect/lavaland_exitportal/attack_hand(mob/user, list/params) +/obj/effect/lavaland_exitportal/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(istype(usr, /mob/living/silicon/ai)) return if(do_after(user, 10)) diff --git a/code/modules/materials/material_sheets.dm b/code/modules/materials/material_sheets.dm index 950c64e4e9d0..0b9c51f98d69 100644 --- a/code/modules/materials/material_sheets.dm +++ b/code/modules/materials/material_sheets.dm @@ -299,7 +299,7 @@ update_mass() return -/obj/item/stack/material/supermatter/attack_hand(mob/user, list/params) +/obj/item/stack/material/supermatter/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() update_mass() diff --git a/code/modules/mining/drilling/drill.dm b/code/modules/mining/drilling/drill.dm index dd1dce7bbf6a..3fc9105b9452 100644 --- a/code/modules/mining/drilling/drill.dm +++ b/code/modules/mining/drilling/drill.dm @@ -190,7 +190,7 @@ return ..() -/obj/machinery/mining/drill/attack_hand(mob/user, list/params) +/obj/machinery/mining/drill/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) check_supports() if (panel_open && cell && user.Adjacent(src)) diff --git a/code/modules/mining/machine_processing.dm b/code/modules/mining/machine_processing.dm index a39668372302..1de34552fa6f 100644 --- a/code/modules/mining/machine_processing.dm +++ b/code/modules/mining/machine_processing.dm @@ -30,7 +30,7 @@ inserted_id.forceMove(loc) //Prevents deconstructing from deleting whatever ID was inside it. . = ..() -/obj/machinery/mineral/processing_unit_console/attack_hand(mob/user, list/params) +/obj/machinery/mineral/processing_unit_console/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return interact(user) diff --git a/code/modules/mining/machine_stacking.dm b/code/modules/mining/machine_stacking.dm index 8f93efbe270c..ec39ab65a50a 100644 --- a/code/modules/mining/machine_stacking.dm +++ b/code/modules/mining/machine_stacking.dm @@ -24,7 +24,7 @@ stack_trace("Stacking machine console at [COORD(src)] could not find its machine!") qdel(src) -/obj/machinery/mineral/stacking_unit_console/attack_hand(mob/user, list/params) +/obj/machinery/mineral/stacking_unit_console/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) interact(user) diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm index 4fc69fa899a7..8b8f630d436a 100644 --- a/code/modules/mining/mine_items.dm +++ b/code/modules/mining/mine_items.dm @@ -391,7 +391,7 @@ else ..() -/obj/item/stack/flag/attack_hand(mob/user, list/params) +/obj/item/stack/flag/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(upright) upright = 0 icon_state = base_state diff --git a/code/modules/mining/ore_redemption_machine/equipment_vendor.dm b/code/modules/mining/ore_redemption_machine/equipment_vendor.dm index 16bacb7da61e..5b131d329524 100644 --- a/code/modules/mining/ore_redemption_machine/equipment_vendor.dm +++ b/code/modules/mining/ore_redemption_machine/equipment_vendor.dm @@ -119,7 +119,7 @@ else icon_state = "[initial(icon_state)]-off" -/obj/machinery/mineral/equipment_vendor/attack_hand(mob/user, list/params) +/obj/machinery/mineral/equipment_vendor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return interact(user) diff --git a/code/modules/mining/shelter_atoms.dm b/code/modules/mining/shelter_atoms.dm index 8cdb65a886d4..619a349bdb74 100644 --- a/code/modules/mining/shelter_atoms.dm +++ b/code/modules/mining/shelter_atoms.dm @@ -128,7 +128,7 @@ id = "placeholder_id_do_not_use" //This has to be this way, otherwise it will control ALL doors if left blank. var/obj/machinery/door/airlock/voidcraft/survival_pod/door -/obj/machinery/button/remote/airlock/survival_pod/attack_hand(obj/item/W, mob/user as mob) +/obj/machinery/button/remote/airlock/survival_pod/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return 1 //1 is failure on machines (for whatever reason) if(!door) var/turf/dT = get_step(src,dir) @@ -209,7 +209,7 @@ return FALSE -/obj/item/gps/computer/attack_hand(mob/user, list/params) +/obj/item/gps/computer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) attack_self(user) //Bed diff --git a/code/modules/mob/living/bot/cleanbot.dm b/code/modules/mob/living/bot/cleanbot.dm index d019d087d085..7e1da92fc895 100644 --- a/code/modules/mob/living/bot/cleanbot.dm +++ b/code/modules/mob/living/bot/cleanbot.dm @@ -140,7 +140,7 @@ else icon_state = "cleanbot[on]" -/mob/living/bot/cleanbot/attack_hand(mob/user, list/params) +/mob/living/bot/cleanbot/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /mob/living/bot/cleanbot/ui_interact(mob/user, datum/tgui/ui) diff --git a/code/modules/mob/living/bot/farmbot.dm b/code/modules/mob/living/bot/farmbot.dm index bbccdd32b11d..be45888d51a6 100644 --- a/code/modules/mob/living/bot/farmbot.dm +++ b/code/modules/mob/living/bot/farmbot.dm @@ -69,7 +69,7 @@ return data -/mob/living/bot/farmbot/attack_hand(mob/user, list/params) +/mob/living/bot/farmbot/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return @@ -403,5 +403,5 @@ created_name = t -/obj/item/farmbot_arm_assembly/attack_hand(mob/user, list/params) +/obj/item/farmbot_arm_assembly/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return //it's a converted watertank, no you cannot pick it up and put it in your backpack diff --git a/code/modules/mob/living/bot/floorbot.dm b/code/modules/mob/living/bot/floorbot.dm index 81e324c9c20e..a2bd72cba7eb 100644 --- a/code/modules/mob/living/bot/floorbot.dm +++ b/code/modules/mob/living/bot/floorbot.dm @@ -89,7 +89,7 @@ return data -/mob/living/bot/floorbot/attack_hand(mob/user, list/params) +/mob/living/bot/floorbot/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /mob/living/bot/floorbot/emag_act(var/remaining_charges, var/mob/user) diff --git a/code/modules/mob/living/bot/medibot.dm b/code/modules/mob/living/bot/medibot.dm index bc25e6d098d8..895abfebedcc 100644 --- a/code/modules/mob/living/bot/medibot.dm +++ b/code/modules/mob/living/bot/medibot.dm @@ -221,7 +221,7 @@ busy = FALSE update_appearance() -/mob/living/bot/medibot/attack_hand(mob/user, list/params) +/mob/living/bot/medibot/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return diff --git a/code/modules/mob/living/bot/secbot.dm b/code/modules/mob/living/bot/secbot.dm index 48e5a56b50a8..7047005d3290 100644 --- a/code/modules/mob/living/bot/secbot.dm +++ b/code/modules/mob/living/bot/secbot.dm @@ -163,7 +163,7 @@ return data -/mob/living/bot/secbot/attack_hand(mob/user, list/params) +/mob/living/bot/secbot/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /mob/living/bot/secbot/ui_act(action, list/params, datum/tgui/ui) diff --git a/code/modules/mob/living/carbon/alien/alien_attacks.dm b/code/modules/mob/living/carbon/alien/alien_attacks.dm index bac33078a131..41b16b48f56a 100644 --- a/code/modules/mob/living/carbon/alien/alien_attacks.dm +++ b/code/modules/mob/living/carbon/alien/alien_attacks.dm @@ -3,7 +3,7 @@ /mob/living/carbon/alien/attack_ui(slot_id) return -/mob/living/carbon/alien/attack_hand(mob/user, list/params) +/mob/living/carbon/alien/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 366bba485291..4a47c832a90c 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -38,7 +38,7 @@ N.show_message("[M] bursts out of [src]!", 2) ..() -/mob/living/carbon/attack_hand(mob/user, list/params) +/mob/living/carbon/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/carbon/M = user if(!istype(M)) return ..() diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm index 0667987fb4fa..99fe7baf87c9 100644 --- a/code/modules/mob/living/carbon/human/human_attackhand.dm +++ b/code/modules/mob/living/carbon/human/human_attackhand.dm @@ -25,7 +25,7 @@ return u_attack return null -/mob/living/carbon/human/attack_hand(mob/user, list/params) +/mob/living/carbon/human/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/datum/gender/TT = GLOB.gender_datums[user.get_visible_gender()] var/mob/living/carbon/human/H = user if(istype(H)) diff --git a/code/modules/mob/living/carbon/human/traits/weaver_objs.dm b/code/modules/mob/living/carbon/human/traits/weaver_objs.dm index 45211ebf7d6a..19bf346ed2ef 100644 --- a/code/modules/mob/living/carbon/human/traits/weaver_objs.dm +++ b/code/modules/mob/living/carbon/human/traits/weaver_objs.dm @@ -34,7 +34,7 @@ var/global/list/weavable_items = list() if(damage) qdel(src) -/obj/effect/weaversilk/attack_hand(mob/user, list/params) +/obj/effect/weaversilk/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() if(user.a_intent == INTENT_HARM) to_chat(user,"You easily tear down [name].") @@ -81,7 +81,7 @@ var/global/list/weavable_items = list() return ..() -/obj/structure/bed/double/weaversilk_nest/attack_hand(mob/user, list/params) +/obj/structure/bed/double/weaversilk_nest/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() if(user.a_intent == INTENT_HARM && !has_buckled_mobs()) to_chat(user,"You easily tear down [name].") diff --git a/code/modules/mob/living/living-defense-legacy.dm b/code/modules/mob/living/living-defense-legacy.dm index ed9609f7d8f8..1e994c49ede8 100644 --- a/code/modules/mob/living/living-defense-legacy.dm +++ b/code/modules/mob/living/living-defense-legacy.dm @@ -92,11 +92,11 @@ return 0 // Clicking with an empty hand -/mob/living/attack_hand(mob/user, list/params) +/mob/living/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return - SEND_SIGNAL(src, COMSIG_MOB_LEGACY_ATTACK_HAND_INTERCEPT, user, params) + SEND_SIGNAL(src, COMSIG_MOB_LEGACY_ATTACK_HAND_INTERCEPT, user, e_args) var/mob/living/L = user if(!istype(L)) return diff --git a/code/modules/mob/living/silicon/pai/defense.dm b/code/modules/mob/living/silicon/pai/defense.dm index 2811411b7799..d6faec813e36 100644 --- a/code/modules/mob/living/silicon/pai/defense.dm +++ b/code/modules/mob/living/silicon/pai/defense.dm @@ -36,7 +36,7 @@ else . = ..() -/mob/living/silicon/pai/attack_hand(mob/user, list/params) +/mob/living/silicon/pai/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(. & CLICKCHAIN_DO_NOT_PROPAGATE) return diff --git a/code/modules/mob/living/silicon/pai/hologram_effect.dm b/code/modules/mob/living/silicon/pai/hologram_effect.dm index 8c84bf80b10c..8bc6cc288ab8 100644 --- a/code/modules/mob/living/silicon/pai/hologram_effect.dm +++ b/code/modules/mob/living/silicon/pai/hologram_effect.dm @@ -10,7 +10,7 @@ /obj/effect/pai_hologram/attackby(obj/item/W, mob/user) hologram_destroy(user) -/obj/effect/pai_hologram/attack_hand(mob/user, list/params) +/obj/effect/pai_hologram/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) hologram_destroy(user) /obj/effect/pai_hologram/proc/hologram_destroy(mob/user) diff --git a/code/modules/mob/living/silicon/robot/drone/drone_console.dm b/code/modules/mob/living/silicon/robot/drone/drone_console.dm index e11d2eab337b..5687625be2ef 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_console.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_console.dm @@ -19,7 +19,7 @@ return UI_CLOSE return ..() -/obj/machinery/computer/drone_control/attack_hand(mob/user, list/params) +/obj/machinery/computer/drone_control/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return diff --git a/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm b/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm index 3e7214da92d6..0f453fe6feac 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm @@ -184,7 +184,7 @@ var/obj/machinery/drone_fabricator/chosen_fabricator = all_fabricators[choice] chosen_fabricator.create_drone(src.client) -/obj/machinery/drone_fabricator/attack_hand(mob/user, list/params) +/obj/machinery/drone_fabricator/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!is_spawn_safe) is_spawn_safe = TRUE to_chat(user, "You inform the fabricator that it is safe for drones to roam around.") diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index d141d84f0076..89781e21ebb3 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -832,7 +832,7 @@ module = null updatename("Default") -/mob/living/silicon/robot/attack_hand(mob/user, list/params) +/mob/living/silicon/robot/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(. & CLICKCHAIN_DO_NOT_PROPAGATE) return diff --git a/code/modules/mob/living/simple_mob/defense.dm b/code/modules/mob/living/simple_mob/defense.dm index 5bb0b7f077ec..af337354f3ea 100644 --- a/code/modules/mob/living/simple_mob/defense.dm +++ b/code/modules/mob/living/simple_mob/defense.dm @@ -1,5 +1,5 @@ // When someone clicks us with an empty hand -/mob/living/simple_mob/attack_hand(mob/user, list/params) +/mob/living/simple_mob/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/cow.dm b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/cow.dm index dd1534a2565c..4c253e499ba0 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/cow.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/cow.dm @@ -62,7 +62,7 @@ if(udder && prob(5)) udder.add_reagent("milk", rand(5, 10)) -/mob/living/simple_mob/animal/passive/cow/attack_hand(mob/user, list/params) +/mob/living/simple_mob/animal/passive/cow/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/M = user if(!istype(M)) return diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish.dm index e6c26cb9f8bb..96d69d7ed51c 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish.dm @@ -110,7 +110,7 @@ var/obj/belly/B = loc sting(B.owner) -/mob/living/simple_mob/animal/passive/fish/koi/poisonous/attack_hand(mob/user, list/params) +/mob/living/simple_mob/animal/passive/fish/koi/poisonous/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm index cdfebcaf0d36..c555b146fa42 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm @@ -145,7 +145,7 @@ emote_hear = list("squeeks","squeaks","squiks") emote_see = list("runs in a circle", "shakes", "scritches at something") -/mob/living/simple_mob/animal/passive/mouse/attack_hand(mob/user, list/params) +/mob/living/simple_mob/animal/passive/mouse/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/hander = user if(!istype(hander)) return diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/parrot.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/parrot.dm index 93b952241395..a58cfae66a3f 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/parrot.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/parrot.dm @@ -72,7 +72,7 @@ return ..() // Clicked on by empty hand. -/mob/living/simple_mob/animal/passive/bird/parrot/attack_hand(mob/user, list/params) +/mob/living/simple_mob/animal/passive/bird/parrot/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/L = user if(!istype(L)) return diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Eddy.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Eddy.dm index f1b32eb16bcd..256157269dea 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Eddy.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Eddy.dm @@ -51,7 +51,7 @@ . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) -/mob/living/simple_mob/horror/Eddy/attack_hand(mob/user, list/params) +/mob/living/simple_mob/horror/Eddy/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Master.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Master.dm index 4c43aea4205e..749a490f3304 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Master.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Master.dm @@ -52,7 +52,7 @@ . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) -/mob/living/simple_mob/horror/Master/attack_hand(mob/user, list/params) +/mob/living/simple_mob/horror/Master/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Rickey.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Rickey.dm index 94281d20ad83..c38bbbc1d272 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Rickey.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Rickey.dm @@ -53,7 +53,7 @@ . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) -/mob/living/simple_mob/horror/Rickey/attack_hand(mob/user, list/params) +/mob/living/simple_mob/horror/Rickey/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Smiley.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Smiley.dm index 4cb631309dfb..3faa6b9225dd 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Smiley.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Smiley.dm @@ -52,7 +52,7 @@ . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) -/mob/living/simple_mob/horror/Helix/attack_hand(mob/user, list/params) +/mob/living/simple_mob/horror/Helix/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Steve.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Steve.dm index 9b9c28a3a21a..775510d1c5e5 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Steve.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Steve.dm @@ -57,7 +57,7 @@ . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) -/mob/living/simple_mob/horror/Steve/attack_hand(mob/user, list/params) +/mob/living/simple_mob/horror/Steve/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Willy.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Willy.dm index 63ec0ecd54b7..6d8e707a5130 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Willy.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Willy.dm @@ -53,7 +53,7 @@ . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) -/mob/living/simple_mob/horror/Willy/attack_hand(mob/user, list/params) +/mob/living/simple_mob/horror/Willy/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/bradley.dm b/code/modules/mob/living/simple_mob/subtypes/horror/bradley.dm index dd8a6f962a37..7fe387538841 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/bradley.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/bradley.dm @@ -51,7 +51,7 @@ . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) -/mob/living/simple_mob/horror/bradley/attack_hand(mob/user, list/params) +/mob/living/simple_mob/horror/bradley/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/sally.dm b/code/modules/mob/living/simple_mob/subtypes/horror/sally.dm index 64ec477eddc7..429cdc27508e 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/sally.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/sally.dm @@ -50,7 +50,7 @@ . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) -/mob/living/simple_mob/horror/Sally/attack_hand(mob/user, list/params) +/mob/living/simple_mob/horror/Sally/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/shittytim.dm b/code/modules/mob/living/simple_mob/subtypes/horror/shittytim.dm index 4b79e05eed18..eb0140b42200 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/shittytim.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/shittytim.dm @@ -51,7 +51,7 @@ . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) -/mob/living/simple_mob/horror/BigTim/attack_hand(mob/user, list/params) +/mob/living/simple_mob/horror/BigTim/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/timling.dm b/code/modules/mob/living/simple_mob/subtypes/horror/timling.dm index 41bfde71be47..68218e0440ee 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/timling.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/timling.dm @@ -51,7 +51,7 @@ . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) -/mob/living/simple_mob/horror/TinyTim/attack_hand(mob/user, list/params) +/mob/living/simple_mob/horror/TinyTim/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/illusion/illusion.dm b/code/modules/mob/living/simple_mob/subtypes/illusion/illusion.dm index 9e81d5932b36..721c948243db 100644 --- a/code/modules/mob/living/simple_mob/subtypes/illusion/illusion.dm +++ b/code/modules/mob/living/simple_mob/subtypes/illusion/illusion.dm @@ -50,7 +50,7 @@ return ..() return PROJECTILE_IMPACT_PHASE -/mob/living/simple_mob/illusion/attack_hand(mob/user, list/params) +/mob/living/simple_mob/illusion/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/M = user if(!istype(M)) return diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/slime.dm b/code/modules/mob/living/simple_mob/subtypes/slime/slime.dm index 2218f6ab57f4..227fb8b5be9c 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/slime.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/slime.dm @@ -159,7 +159,7 @@ adjustBruteLoss(-1) // Clicked on by empty hand. -/mob/living/simple_mob/slime/attack_hand(mob/user, list/params) +/mob/living/simple_mob/slime/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/L = user if(!istype(L)) return diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/defense.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/defense.dm index 7d0d0eccb655..dcf8e437aa8c 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/defense.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/defense.dm @@ -2,7 +2,7 @@ // Clicked on by empty hand. // Handles trying to wrestle a slime off of someone being eatten. -/mob/living/simple_mob/slime/xenobio/attack_hand(mob/user, list/params) +/mob/living/simple_mob/slime/xenobio/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/L = user if(!istype(L)) return diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/c_pet.dm b/code/modules/mob/living/simple_mob/subtypes/vore/c_pet.dm index 55598d1e9c19..666d3f0f6fd8 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/c_pet.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/c_pet.dm @@ -40,7 +40,7 @@ has_langs = list("Coulrian") -/mob/living/simple_mob/animal/passive/honkpet/attack_hand(mob/user, list/params) +/mob/living/simple_mob/animal/passive/honkpet/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_DISARM) return icon_state = pick("c_pet", "m_pet") .=..() @@ -78,7 +78,7 @@ "rad" = 0 ) -/mob/living/simple_mob/animal/passive/mimepet/attack_hand(mob/user, list/params) +/mob/living/simple_mob/animal/passive/mimepet/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_DISARM) icon_state = pick("dave1", "dave2", "dave3", "dave5" , "dave6" , "dave7" , "dave8" , "dave9" , "dave10") .=..() diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/otie.dm b/code/modules/mob/living/simple_mob/subtypes/vore/otie.dm index 29ea3126cd4e..4caa88ad60a4 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/otie.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/otie.dm @@ -172,7 +172,7 @@ //Pet 4 friendly -/mob/living/simple_mob/otie/attack_hand(mob/user, list/params) +/mob/living/simple_mob/otie/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/M = user if(!istype(M)) diff --git a/code/modules/modular_computers/NTNet/NTNet_relay.dm b/code/modules/modular_computers/NTNet/NTNet_relay.dm index c7e503ee6c59..0e6a94718880 100644 --- a/code/modules/modular_computers/NTNet/NTNet_relay.dm +++ b/code/modules/modular_computers/NTNet/NTNet_relay.dm @@ -70,7 +70,7 @@ ui.open() ui.set_auto_update(1) -/obj/machinery/ntnet_relay/attack_hand(mob/user, list/params) +/obj/machinery/ntnet_relay/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) nano_ui_interact(user) /obj/machinery/ntnet_relay/Topic(href, href_list) diff --git a/code/modules/modular_computers/computers/modular_computer/interaction.dm b/code/modules/modular_computers/computers/modular_computer/interaction.dm index 28fc9219d1e6..9207f187b5de 100644 --- a/code/modules/modular_computers/computers/modular_computer/interaction.dm +++ b/code/modules/modular_computers/computers/modular_computer/interaction.dm @@ -109,7 +109,7 @@ /obj/item/modular_computer/attack_ai(mob/user) return attack_self(user) -/obj/item/modular_computer/attack_hand(mob/user, list/params) +/obj/item/modular_computer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(anchored) return attack_self(user) return ..() diff --git a/code/modules/modular_computers/laptop_vendor.dm b/code/modules/modular_computers/laptop_vendor.dm index fc48f029d79f..ddc02dbf3030 100644 --- a/code/modules/modular_computers/laptop_vendor.dm +++ b/code/modules/modular_computers/laptop_vendor.dm @@ -214,7 +214,7 @@ return 1 return 0 -/obj/machinery/lapvend/attack_hand(mob/user, list/params) +/obj/machinery/lapvend/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) nano_ui_interact(user) /obj/machinery/lapvend/nano_ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) diff --git a/code/modules/multiz/structures/hoist.dm b/code/modules/multiz/structures/hoist.dm index f2459acaf4bf..aceb2dfcde03 100644 --- a/code/modules/multiz/structures/hoist.dm +++ b/code/modules/multiz/structures/hoist.dm @@ -28,7 +28,7 @@ var/obj/structure/hoist/source_hoist description_info = "Click and drag someone (or any object) to this to attach them to the clamp. If you are within reach, when you click and drag this to a turf adjacent to you, it will move the attached object there and release it." -/obj/effect/hoist_hook/attack_hand(mob/user, list/params) +/obj/effect/hoist_hook/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return // This has to be overridden so that it works properly. /obj/effect/hoist_hook/MouseDroppedOnLegacy(atom/movable/AM,mob/user) @@ -175,7 +175,7 @@ return -/obj/structure/hoist/attack_hand(mob/user, list/params) +/obj/structure/hoist/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (!ishuman(user)) return diff --git a/code/modules/multiz/structures/ladder.dm b/code/modules/multiz/structures/ladder.dm index 0ac65d6d7c9c..96ebae23d1e7 100644 --- a/code/modules/multiz/structures/ladder.dm +++ b/code/modules/multiz/structures/ladder.dm @@ -39,7 +39,7 @@ attack_hand(user) return -/obj/structure/ladder/attack_hand(mob/user, list/params) +/obj/structure/ladder/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return diff --git a/code/modules/multiz/structures/vorestation_portals.dm b/code/modules/multiz/structures/vorestation_portals.dm index 9e1989fbfce7..3150788bf837 100644 --- a/code/modules/multiz/structures/vorestation_portals.dm +++ b/code/modules/multiz/structures/vorestation_portals.dm @@ -27,7 +27,7 @@ return //do not send ghosts, zshadows, ai eyes, etc teleport(AM) -/obj/structure/portal_subtle/attack_hand(mob/user, list/params) +/obj/structure/portal_subtle/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(istype(user) && !(istype(user,/mob/living))) return //do not send ghosts, zshadows, ai eyes, etc spawn(0) diff --git a/code/modules/multiz/zmimic/mimic_movable.dm b/code/modules/multiz/zmimic/mimic_movable.dm index 95f6cf27cd4d..adb074785f54 100644 --- a/code/modules/multiz/zmimic/mimic_movable.dm +++ b/code/modules/multiz/zmimic/mimic_movable.dm @@ -170,7 +170,7 @@ /atom/movable/openspace/mimic/attackby(obj/item/W, mob/user) to_chat(user, SPAN_NOTICE("\The [src] is too far away.")) -/atom/movable/openspace/mimic/attack_hand(mob/user) +/atom/movable/openspace/mimic/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) to_chat(user, SPAN_NOTICE("You cannot reach \the [src] from here.")) /atom/movable/openspace/mimic/examine(...) @@ -216,7 +216,7 @@ /atom/movable/openspace/turf_proxy/attackby(obj/item/W, mob/user) loc.attackby(W, user) -/atom/movable/openspace/turf_proxy/attack_hand(mob/user, list/params) +/atom/movable/openspace/turf_proxy/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) loc.attack_hand(user) /atom/movable/openspace/turf_proxy/attack_generic(mob/user as mob) @@ -243,7 +243,7 @@ /atom/movable/openspace/turf_mimic/attackby(obj/item/W, mob/user) loc.attackby(W, user) -/atom/movable/openspace/turf_mimic/attack_hand(mob/user, list/params) +/atom/movable/openspace/turf_mimic/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) to_chat(user, SPAN_NOTICE("You cannot reach \the [src] from here.")) /atom/movable/openspace/turf_mimic/attack_generic(mob/user as mob) diff --git a/code/modules/overmap/legacy/overmap_shuttle.dm b/code/modules/overmap/legacy/overmap_shuttle.dm index 325e06299cc1..441aeb93949c 100644 --- a/code/modules/overmap/legacy/overmap_shuttle.dm +++ b/code/modules/overmap/legacy/overmap_shuttle.dm @@ -138,7 +138,7 @@ opened = 1 //shows open so you can diagnose 'oops, no gas' easily icon_state = "fuel_port_empty" //set the default state just to be safe -/obj/structure/fuel_port/attack_hand(mob/user, list/params) +/obj/structure/fuel_port/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!opened) to_chat(user, "The door is secured tightly. You'll need a crowbar to open it.") return diff --git a/code/modules/overmap/legacy/ships/computers/computer_shims.dm b/code/modules/overmap/legacy/ships/computers/computer_shims.dm index 616cb4f694f7..0c25c5edf1b6 100644 --- a/code/modules/overmap/legacy/ships/computers/computer_shims.dm +++ b/code/modules/overmap/legacy/ships/computers/computer_shims.dm @@ -80,7 +80,7 @@ // If you don't call parent in this proc, you must make all appropriate checks yourself. // If you do, you must respect the return value. -/obj/machinery/computer/ship/attack_hand(mob/user, list/params) +/obj/machinery/computer/ship/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((. = ..())) return if(!allowed(user)) diff --git a/code/modules/overmap/legacy/ships/panicbutton.dm b/code/modules/overmap/legacy/ships/panicbutton.dm index 0cd9d8c53f2e..33d0cdb90642 100644 --- a/code/modules/overmap/legacy/ships/panicbutton.dm +++ b/code/modules/overmap/legacy/ships/panicbutton.dm @@ -23,7 +23,7 @@ else icon_state = "[initial(icon_state)]" -/obj/structure/panic_button/attack_hand(mob/user, list/params) +/obj/structure/panic_button/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!istype(user)) return ..() diff --git a/code/modules/paperwork/faxmachine.dm b/code/modules/paperwork/faxmachine.dm index e6c91e650d3a..0e2e66eff904 100644 --- a/code/modules/paperwork/faxmachine.dm +++ b/code/modules/paperwork/faxmachine.dm @@ -32,7 +32,7 @@ var/list/adminfaxes = list() //cache for faxes that have been sent to admins if(!(("[department]" in alldepartments) || ("[department]" in admin_departments())) ) alldepartments |= department -/obj/machinery/photocopier/faxmachine/attack_hand(mob/user, list/params) +/obj/machinery/photocopier/faxmachine/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.set_machine(src) nano_ui_interact(user) diff --git a/code/modules/paperwork/filingcabinet.dm b/code/modules/paperwork/filingcabinet.dm index b9ecb5dd6704..d6361a68ddce 100644 --- a/code/modules/paperwork/filingcabinet.dm +++ b/code/modules/paperwork/filingcabinet.dm @@ -61,7 +61,7 @@ else to_chat(user, SPAN_NOTICE("You can't put [P] in [src]!")) -/obj/structure/filingcabinet/attack_hand(mob/user, list/params) +/obj/structure/filingcabinet/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(contents.len <= 0) to_chat(user, SPAN_NOTICE("\The [src] is empty.")) return @@ -150,7 +150,7 @@ virgin = 0 //tabbing here is correct- it's possible for people to try and use it //before the records have been generated, so we do this inside the loop. -/obj/structure/filingcabinet/security/attack_hand(mob/user, list/params) +/obj/structure/filingcabinet/security/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) populate() ..() @@ -187,7 +187,7 @@ virgin = 0 //tabbing here is correct- it's possible for people to try and use it //before the records have been generated, so we do this inside the loop. -/obj/structure/filingcabinet/medical/attack_hand(mob/user, list/params) +/obj/structure/filingcabinet/medical/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) populate() ..() diff --git a/code/modules/paperwork/paperbin.dm b/code/modules/paperwork/paperbin.dm index 1076fc731af3..b2bcd9a135a1 100644 --- a/code/modules/paperwork/paperbin.dm +++ b/code/modules/paperwork/paperbin.dm @@ -38,7 +38,7 @@ return -/obj/item/paper_bin/attack_hand(mob/user, list/params) +/obj/item/paper_bin/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(ishuman(user)) var/mob/living/carbon/human/H = user var/obj/item/organ/external/temp = H.organs_by_name["r_hand"] @@ -116,7 +116,7 @@ icon_state = "paper_bundle" papers = /obj/item/paper/natural -/obj/item/paper_bin/bundlenatural/attack_hand(mob/user, list/params) +/obj/item/paper_bin/bundlenatural/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(amount < 1) qdel(src) return ..() diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm index 632c340da4a9..7b943d43c9a2 100644 --- a/code/modules/paperwork/photocopier.dm +++ b/code/modules/paperwork/photocopier.dm @@ -27,7 +27,7 @@ /obj/machinery/photocopier/attack_ai(mob/user as mob) return attack_hand(user) -/obj/machinery/photocopier/attack_hand(mob/user, list/params) +/obj/machinery/photocopier/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.set_machine(src) nano_ui_interact(user) diff --git a/code/modules/power/antimatter/computer.dm b/code/modules/power/antimatter/computer.dm index 23d25beed55c..5996d5abc212 100644 --- a/code/modules/power/antimatter/computer.dm +++ b/code/modules/power/antimatter/computer.dm @@ -64,7 +64,7 @@ /obj/machinery/computer/am_engine/attack_paw(var/mob/user as mob) return src.attack_hand(user) -/obj/machinery/computer/am_engine/attack_hand(mob/user, list/params) +/obj/machinery/computer/am_engine/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return user.machine = src diff --git a/code/modules/power/antimatter/control.dm b/code/modules/power/antimatter/control.dm index f65362e5f7ef..e9cae20586ad 100644 --- a/code/modules/power/antimatter/control.dm +++ b/code/modules/power/antimatter/control.dm @@ -176,7 +176,7 @@ return -/obj/machinery/power/am_control_unit/attack_hand(mob/user, list/params) +/obj/machinery/power/am_control_unit/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(anchored) interact(user) return diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 92209ce67943..e42290cc3c60 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -787,7 +787,7 @@ CREATE_WALL_MOUNTING_TYPES_SHIFTED(/obj/machinery/power/apc, 22) wires.cut_all() update_icon() -/obj/machinery/power/apc/attack_hand(mob/user, list/params) +/obj/machinery/power/apc/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) // if (!can_use(user)) This already gets called in interact() and in topic() // return if(!user) diff --git a/code/modules/power/breaker_box.dm b/code/modules/power/breaker_box.dm index 591cc56f449f..8314a7a6f63a 100644 --- a/code/modules/power/breaker_box.dm +++ b/code/modules/power/breaker_box.dm @@ -72,7 +72,7 @@ busy = 0 -/obj/machinery/power/breakerbox/attack_hand(mob/user, list/params) +/obj/machinery/power/breakerbox/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(update_locked) to_chat(user, "System locked. Please try again later.") return diff --git a/code/modules/power/fission/computer.dm b/code/modules/power/fission/computer.dm index 6c7b62a33a58..4916174d0246 100644 --- a/code/modules/power/fission/computer.dm +++ b/code/modules/power/fission/computer.dm @@ -37,7 +37,7 @@ /obj/machinery/computer/fission_monitor/attack_ai(mob/user) attack_hand(user) -/obj/machinery/computer/fission_monitor/attack_hand(mob/user, list/params) +/obj/machinery/computer/fission_monitor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) if(machine_stat & (BROKEN|NOPOWER)) return diff --git a/code/modules/power/fission/engine.dm b/code/modules/power/fission/engine.dm index 8e64670dd636..49d976e0af4f 100644 --- a/code/modules/power/fission/engine.dm +++ b/code/modules/power/fission/engine.dm @@ -108,7 +108,7 @@ var/power = (decay_heat / REACTOR_RADS_TO_MJ) * max(healthmul, 0.1) radiation_pulse(src, max(power * REACTOR_RADIATION_MULTIPLIER, 0), RAD_FALLOFF_ENGINE_FISSION) -/obj/machinery/power/fission/attack_hand(mob/user, list/params) +/obj/machinery/power/fission/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) nano_ui_interact(user) /obj/machinery/power/fission/attack_robot(mob/user) diff --git a/code/modules/power/fusion/core/_core.dm b/code/modules/power/fusion/core/_core.dm index 2ff85f772b77..207915ae7461 100644 --- a/code/modules/power/fusion/core/_core.dm +++ b/code/modules/power/fusion/core/_core.dm @@ -99,7 +99,7 @@ var/list/fusion_cores = list() if(owned_field) owned_field.ChangeFieldStrength(value) -/obj/machinery/power/fusion_core/attack_hand(mob/user, list/params) +/obj/machinery/power/fusion_core/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!Adjacent(user)) // As funny as it was for the AI to hug-kill the tokamak field from a distance... return visible_message("\The [user] hugs \the [src] to make it feel better!") diff --git a/code/modules/power/fusion/core/core_control.dm b/code/modules/power/fusion/core/core_control.dm index 08f267e39747..488c92cf2113 100644 --- a/code/modules/power/fusion/core/core_control.dm +++ b/code/modules/power/fusion/core/core_control.dm @@ -21,7 +21,7 @@ /obj/machinery/computer/fusion_core_control/attack_ai(mob/user) attack_hand(user) -/obj/machinery/computer/fusion_core_control/attack_hand(mob/user, list/params) +/obj/machinery/computer/fusion_core_control/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) interact(user) diff --git a/code/modules/power/fusion/fuel_assembly/fuel_control.dm b/code/modules/power/fusion/fuel_assembly/fuel_control.dm index 668c1ee7d7cf..dc3b35787ad8 100644 --- a/code/modules/power/fusion/fuel_assembly/fuel_control.dm +++ b/code/modules/power/fusion/fuel_assembly/fuel_control.dm @@ -10,7 +10,7 @@ /obj/machinery/computer/fusion_fuel_control/attack_ai(mob/user) attack_hand(user) -/obj/machinery/computer/fusion_fuel_control/attack_hand(mob/user, list/params) +/obj/machinery/computer/fusion_fuel_control/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) interact(user) diff --git a/code/modules/power/fusion/fuel_assembly/fuel_injector.dm b/code/modules/power/fusion/fuel_assembly/fuel_injector.dm index 99856dd63b44..53951245d996 100644 --- a/code/modules/power/fusion/fuel_assembly/fuel_injector.dm +++ b/code/modules/power/fusion/fuel_assembly/fuel_injector.dm @@ -79,7 +79,7 @@ var/list/fuel_injectors = list() return ..() -/obj/machinery/fusion_fuel_injector/attack_hand(mob/user, list/params) +/obj/machinery/fusion_fuel_injector/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(injecting) to_chat(user, "Shut \the [src] off before playing with the fuel rod!") diff --git a/code/modules/power/fusion/gyrotron/gyrotron_control.dm b/code/modules/power/fusion/gyrotron/gyrotron_control.dm index 8cff0d855e12..d51841206cdb 100644 --- a/code/modules/power/fusion/gyrotron/gyrotron_control.dm +++ b/code/modules/power/fusion/gyrotron/gyrotron_control.dm @@ -11,7 +11,7 @@ /obj/machinery/computer/gyrotron_control/attack_ai(var/mob/user) attack_hand(user) -/obj/machinery/computer/gyrotron_control/attack_hand(mob/user, list/params) +/obj/machinery/computer/gyrotron_control/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) interact(user) diff --git a/code/modules/power/generator.dm b/code/modules/power/generator.dm index 5315b5560e40..30b91be24149 100644 --- a/code/modules/power/generator.dm +++ b/code/modules/power/generator.dm @@ -188,7 +188,7 @@ GLOBAL_LIST_EMPTY(all_turbines) else ..() -/obj/machinery/power/generator/attack_hand(mob/user, list/params) +/obj/machinery/power/generator/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) if(machine_stat & (BROKEN|NOPOWER) || !anchored) return diff --git a/code/modules/power/generator_type2.dm b/code/modules/power/generator_type2.dm index 1bf94294a438..bbfb70e642f0 100644 --- a/code/modules/power/generator_type2.dm +++ b/code/modules/power/generator_type2.dm @@ -94,7 +94,7 @@ interact(user) -/obj/machinery/power/generator_type2/attack_hand(mob/user, list/params) +/obj/machinery/power/generator_type2/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) if(machine_stat & (BROKEN|NOPOWER)) return diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm index d4bcc7eebf22..c602f9b35e71 100644 --- a/code/modules/power/gravitygenerator.dm +++ b/code/modules/power/gravitygenerator.dm @@ -79,7 +79,7 @@ GLOBAL_LIST_EMPTY(gravity_generators) /obj/machinery/gravity_generator/part/get_status() return main_part?.get_status() -/obj/machinery/gravity_generator/part/attack_hand(mob/user, list/params) +/obj/machinery/gravity_generator/part/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return main_part.attack_hand(user) /obj/machinery/gravity_generator/part/set_broken() @@ -239,7 +239,7 @@ GLOBAL_LIST_EMPTY(gravity_generators) return return ..() -/obj/machinery/gravity_generator/main/attack_hand(mob/user, list/params) +/obj/machinery/gravity_generator/main/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((. = ..())) return ui_interact(user) diff --git a/code/modules/power/grid_checker.dm b/code/modules/power/grid_checker.dm index 4da2807a9e53..9489bb531bf9 100644 --- a/code/modules/power/grid_checker.dm +++ b/code/modules/power/grid_checker.dm @@ -51,7 +51,7 @@ else if(istype(W, /obj/item/multitool) || W.is_wirecutter()) attack_hand(user) -/obj/machinery/power/grid_checker/attack_hand(mob/user, list/params) +/obj/machinery/power/grid_checker/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!user) return add_fingerprint(user) diff --git a/code/modules/power/lighting/lighting.dm b/code/modules/power/lighting/lighting.dm index 0d8144581aee..61515e6b5059 100644 --- a/code/modules/power/lighting/lighting.dm +++ b/code/modules/power/lighting/lighting.dm @@ -77,7 +77,7 @@ var/global/list/light_type_cache = list() else to_chat(user, "This casing doesn't support power cells for backup power.") -/obj/machinery/light_construct/attack_hand(mob/user, list/params) +/obj/machinery/light_construct/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return . // obj/machinery/attack_hand returns 1 if user can't use the machine @@ -861,7 +861,7 @@ var/global/list/light_type_cache = list() // attack with hand - remove tube/bulb // if hands aren't protected and the light is on, burn the player -/obj/machinery/light/attack_hand(mob/user, list/params) +/obj/machinery/light/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) @@ -907,7 +907,7 @@ var/global/list/light_type_cache = list() // create a light tube/bulb item and put it in the user's hand user.put_in_active_hand(remove_bulb()) //puts it in our active hand -/obj/machinery/light/flamp/attack_hand(mob/user, list/params) +/obj/machinery/light/flamp/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(lamp_shade) if(status == LIGHT_EMPTY) to_chat(user, "There is no [get_fitting_name()] in this light.") diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm index fc548866d6b2..6c1603b29ed7 100644 --- a/code/modules/power/port_gen.dm +++ b/code/modules/power/port_gen.dm @@ -42,7 +42,7 @@ /obj/machinery/power/powered() return 1 //doesn't require an external power source -/obj/machinery/power/port_gen/attack_hand(mob/user, list/params) +/obj/machinery/power/port_gen/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return if(!anchored) @@ -302,7 +302,7 @@ return return ..() -/obj/machinery/power/port_gen/pacman/attack_hand(mob/user, list/params) +/obj/machinery/power/port_gen/pacman/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() if (!anchored) return @@ -613,7 +613,7 @@ log_and_message_admins("[ADMIN_LOOKUPFLW(proj.firer)] triggered an Abductor Core explosion at [x],[y],[z] via projectile.") asplod() -/obj/machinery/power/rtg/abductor/attack_hand(mob/user, list/params) +/obj/machinery/power/rtg/abductor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!istype(user) || (. = ..())) return diff --git a/code/modules/power/sensors/sensor_monitoring.dm b/code/modules/power/sensors/sensor_monitoring.dm index fc9f49505e39..85f33951033c 100644 --- a/code/modules/power/sensors/sensor_monitoring.dm +++ b/code/modules/power/sensors/sensor_monitoring.dm @@ -41,7 +41,7 @@ ..() // On user click opens the UI of this computer. -/obj/machinery/computer/power_monitor/attack_hand(mob/user, list/params) +/obj/machinery/computer/power_monitor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) if(machine_stat & (BROKEN|NOPOWER)) diff --git a/code/modules/power/singularity/collector.dm b/code/modules/power/singularity/collector.dm index 1e5de40b4b86..6fdfc767bb8b 100644 --- a/code/modules/power/singularity/collector.dm +++ b/code/modules/power/singularity/collector.dm @@ -48,7 +48,7 @@ rad_insulation = active? rad_insulation_active : rad_insulation_inactive -/obj/machinery/power/rad_collector/attack_hand(mob/user, list/params) +/obj/machinery/power/rad_collector/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(anchored) if(!src.locked) toggle_power() diff --git a/code/modules/power/singularity/containment_field.dm b/code/modules/power/singularity/containment_field.dm index ebbfc93fe055..1e957093359c 100644 --- a/code/modules/power/singularity/containment_field.dm +++ b/code/modules/power/singularity/containment_field.dm @@ -21,7 +21,7 @@ FG2.cleanup() . = ..() -/obj/machinery/containment_field/attack_hand(mob/user, list/params) +/obj/machinery/containment_field/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(get_dist(src, user) > 1) return 0 else diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index 7da6c2e6c880..a62c8516fdc9 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -58,7 +58,7 @@ else icon_state = "emitter" -/obj/machinery/power/emitter/attack_hand(mob/user, list/params) +/obj/machinery/power/emitter/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) src.add_fingerprint(user) activate(user) diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/power/singularity/field_generator.dm index 6a80497c66b0..ebd211036a73 100644 --- a/code/modules/power/singularity/field_generator.dm +++ b/code/modules/power/singularity/field_generator.dm @@ -75,7 +75,7 @@ field_generator power level display return -/obj/machinery/field_generator/attack_hand(mob/user, list/params) +/obj/machinery/field_generator/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(state == 2) if(get_dist(src, user) <= 1)//Need to actually touch the thing to turn it on if(src.active >= 1) diff --git a/code/modules/power/singularity/particle_accelerator/particle_control.dm b/code/modules/power/singularity/particle_accelerator/particle_control.dm index d87f82a0621b..51efce1bc5fd 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_control.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_control.dm @@ -34,7 +34,7 @@ wires = null return ..() -/obj/machinery/particle_accelerator/control_box/attack_hand(mob/user, list/params) +/obj/machinery/particle_accelerator/control_box/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(construction_state >= 3) interact(user) else if(construction_state == 2) // Wires exposed diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index 71ed027c49ac..00b55157c46f 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -46,7 +46,7 @@ GLOBAL_LIST_BOILERPLATE(all_singularities, /obj/singularity) STOP_PROCESSING(SSobj, src) return ..() -/obj/singularity/attack_hand(mob/user, list/params) +/obj/singularity/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) consume(user) return 1 diff --git a/code/modules/power/smes/smes.dm b/code/modules/power/smes/smes.dm index 9d2082dd458d..bde57b6dd623 100644 --- a/code/modules/power/smes/smes.dm +++ b/code/modules/power/smes/smes.dm @@ -241,7 +241,7 @@ GLOBAL_LIST_EMPTY(smeses) add_hiddenprint(user) ui_interact(user) -/obj/machinery/power/smes/attack_hand(mob/user, list/params) +/obj/machinery/power/smes/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) ui_interact(user) diff --git a/code/modules/power/smes/smes_construction.dm b/code/modules/power/smes/smes_construction.dm index c60612be15ac..2dde0f3ad5b6 100644 --- a/code/modules/power/smes/smes_construction.dm +++ b/code/modules/power/smes/smes_construction.dm @@ -111,7 +111,7 @@ // Proc: attack_hand() // Parameters: None // Description: Opens the UI as usual, and if cover is removed opens the wiring panel. -/obj/machinery/power/smes/buildable/attack_hand(mob/user, list/params) +/obj/machinery/power/smes/buildable/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() if(open_hatch) wires.Interact(usr) diff --git a/code/modules/power/solar/solar_control.dm b/code/modules/power/solar/solar_control.dm index cd503e9daa74..d2738102c702 100644 --- a/code/modules/power/solar/solar_control.dm +++ b/code/modules/power/solar/solar_control.dm @@ -116,7 +116,7 @@ add_overlay(image('icons/obj/computer.dmi', "solcon-o", FLY_LAYER, angle2dir(cdir))) return -/obj/machinery/power/solar_control/attack_hand(mob/user, list/params) +/obj/machinery/power/solar_control/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!..()) interact(user) diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm index 63c73826cfaf..19854c1cd771 100644 --- a/code/modules/power/supermatter/supermatter.dm +++ b/code/modules/power/supermatter/supermatter.dm @@ -392,7 +392,7 @@ /obj/machinery/power/supermatter/attack_ai(mob/user as mob) ui_interact(user) -/obj/machinery/power/supermatter/attack_hand(mob/user, list/params) +/obj/machinery/power/supermatter/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/datum/gender/TU = GLOB.gender_datums[user.get_visible_gender()] user.visible_message("\The [user] reaches out and touches \the [src], inducing a resonance... [TU.his] body starts to glow and bursts into flames before flashing into ash.",\ "You reach out and touch \the [src]. Everything starts burning and all you can hear is ringing. Your last thought is \"That was not a wise decision.\"",\ diff --git a/code/modules/power/tesla/coil.dm b/code/modules/power/tesla/coil.dm index 0121fcb4dc42..ee0741ed89ae 100644 --- a/code/modules/power/tesla/coil.dm +++ b/code/modules/power/tesla/coil.dm @@ -59,7 +59,7 @@ return wires.Interact(user) return ..() -/obj/machinery/power/tesla_coil/attack_hand(mob/user, list/params) +/obj/machinery/power/tesla_coil/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_GRAB && user_buckle_mob(user.pulling, user)) return ..() @@ -125,7 +125,7 @@ return return ..() -/obj/machinery/power/grounding_rod/attack_hand(mob/user, list/params) +/obj/machinery/power/grounding_rod/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_GRAB && user_buckle_mob(user.pulling, user)) return ..() diff --git a/code/modules/power/turbine.dm b/code/modules/power/turbine.dm index e8d7410e77e0..4d52ad696e82 100644 --- a/code/modules/power/turbine.dm +++ b/code/modules/power/turbine.dm @@ -273,7 +273,7 @@ updateDialog() -/obj/machinery/power/turbine/attack_hand(mob/user, list/params) +/obj/machinery/power/turbine/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((. = ..())) return src.interact(user) @@ -340,7 +340,7 @@ id = new_ident return -/obj/machinery/computer/turbine_computer/attack_hand(mob/user, list/params) +/obj/machinery/computer/turbine_computer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((. = ..())) return src.interact(user) diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm index 583256d45132..122b6cf69724 100644 --- a/code/modules/projectiles/guns/ballistic.dm +++ b/code/modules/projectiles/guns/ballistic.dm @@ -292,7 +292,7 @@ unload_ammo(user) update_icon() -/obj/item/gun/ballistic/attack_hand(mob/user, list/params) +/obj/item/gun/ballistic/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) unload_ammo(user, allow_dump=0) else diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index 33e3694cab93..89db582917b5 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -155,7 +155,7 @@ ..() load_ammo(A, user) -/obj/item/gun/energy/attack_hand(mob/user, list/params) +/obj/item/gun/energy/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) unload_ammo(user) else diff --git a/code/modules/projectiles/guns/launcher/grenade_launcher.dm b/code/modules/projectiles/guns/launcher/grenade_launcher.dm index 747c7eb52156..319caebd3244 100644 --- a/code/modules/projectiles/guns/launcher/grenade_launcher.dm +++ b/code/modules/projectiles/guns/launcher/grenade_launcher.dm @@ -77,7 +77,7 @@ else ..() -/obj/item/gun/launcher/grenade/attack_hand(mob/user, list/params) +/obj/item/gun/launcher/grenade/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) unload(user) else diff --git a/code/modules/projectiles/guns/launcher/pneumatic.dm b/code/modules/projectiles/guns/launcher/pneumatic.dm index 9d43fcbb3fac..cd73e8f1bfb8 100644 --- a/code/modules/projectiles/guns/launcher/pneumatic.dm +++ b/code/modules/projectiles/guns/launcher/pneumatic.dm @@ -61,7 +61,7 @@ else to_chat(user, "There is nothing to remove in \the [src].") -/obj/item/gun/launcher/pneumatic/attack_hand(mob/user, list/params) +/obj/item/gun/launcher/pneumatic/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) unload_hopper(user) else diff --git a/code/modules/projectiles/guns/launcher/syringe_gun.dm b/code/modules/projectiles/guns/launcher/syringe_gun.dm index 9393ff5c7233..4461b6a2c85e 100644 --- a/code/modules/projectiles/guns/launcher/syringe_gun.dm +++ b/code/modules/projectiles/guns/launcher/syringe_gun.dm @@ -108,7 +108,7 @@ next = darts[1] add_fingerprint(user) -/obj/item/gun/launcher/syringe/attack_hand(mob/user, list/params) +/obj/item/gun/launcher/syringe/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) if(!darts.len) to_chat(user, "[src] is empty.") diff --git a/code/modules/projectiles/guns/magnetic/bore.dm b/code/modules/projectiles/guns/magnetic/bore.dm index f59f7d47c8be..f6bf34c5d9d1 100644 --- a/code/modules/projectiles/guns/magnetic/bore.dm +++ b/code/modules/projectiles/guns/magnetic/bore.dm @@ -48,7 +48,7 @@ if(mat_storage) . += image(icon, "[icon_state]_loaded") -/obj/item/gun/magnetic/matfed/attack_hand(mob/user, list/params) // It doesn't keep a loaded item inside. +/obj/item/gun/magnetic/matfed/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) var/obj/item/removing diff --git a/code/modules/projectiles/guns/magnetic/magnetic.dm b/code/modules/projectiles/guns/magnetic/magnetic.dm index 6a85481a0084..8dffab58d8e4 100644 --- a/code/modules/projectiles/guns/magnetic/magnetic.dm +++ b/code/modules/projectiles/guns/magnetic/magnetic.dm @@ -150,7 +150,7 @@ return . = ..() -/obj/item/gun/magnetic/attack_hand(mob/user, list/params) +/obj/item/gun/magnetic/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) var/obj/item/removing diff --git a/code/modules/projectiles/guns/projectile/automatic.dm b/code/modules/projectiles/guns/projectile/automatic.dm index 3e9afa3f3fe4..f8b7dcbd6c23 100644 --- a/code/modules/projectiles/guns/projectile/automatic.dm +++ b/code/modules/projectiles/guns/projectile/automatic.dm @@ -166,7 +166,7 @@ else ..() -/obj/item/gun/ballistic/automatic/z8/attack_hand(mob/user, list/params) +/obj/item/gun/ballistic/automatic/z8/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src && use_launcher) launcher.unload(user) else @@ -255,7 +255,7 @@ else return ..() //once closed, behave like normal -/obj/item/gun/ballistic/automatic/lmg/attack_hand(mob/user, list/params) +/obj/item/gun/ballistic/automatic/lmg/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!cover_open && user.get_inactive_held_item() == src) toggle_cover(user) //open the cover else diff --git a/code/modules/projectiles/guns/projectile/bow.dm b/code/modules/projectiles/guns/projectile/bow.dm index ec2f2da81eb1..b430fb5dceff 100644 --- a/code/modules/projectiles/guns/projectile/bow.dm +++ b/code/modules/projectiles/guns/projectile/bow.dm @@ -54,7 +54,7 @@ else return -/obj/item/gun/ballistic/bow/attack_hand(mob/user, list/params) +/obj/item/gun/ballistic/bow/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) unload_ammo(user, allow_dump=0) else diff --git a/code/modules/projectiles/guns/projectile/contender.dm b/code/modules/projectiles/guns/projectile/contender.dm index bd61be4f2683..0f3b1290d361 100644 --- a/code/modules/projectiles/guns/projectile/contender.dm +++ b/code/modules/projectiles/guns/projectile/contender.dm @@ -129,7 +129,7 @@ to_chat(user, "\The [src] is completely inoperable!") handle_click_empty() -/obj/item/gun/ballistic/contender/pipegun/attack_hand(mob/user, list/params) +/obj/item/gun/ballistic/contender/pipegun/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src && destroyed) to_chat(user, "\The [src]'s chamber is too warped to extract the casing!") return diff --git a/code/modules/projectiles/guns/projectile/pistol.dm b/code/modules/projectiles/guns/projectile/pistol.dm index c188758bb3ca..b0ea5a040444 100644 --- a/code/modules/projectiles/guns/projectile/pistol.dm +++ b/code/modules/projectiles/guns/projectile/pistol.dm @@ -74,7 +74,7 @@ icon_state = "colt-taj" /*//apart of reskins that have two sprites, touching may result in frustration and breaks -/obj/item/gun/ballistic/colt/detective/attack_hand(mob/user, list/params) +/obj/item/gun/ballistic/colt/detective/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!unique_reskin && loc == user) reskin_gun(user) return @@ -273,7 +273,7 @@ to_chat(user, "\The [src] is completely inoperable!") handle_click_empty() -/obj/item/gun/ballistic/pirate/attack_hand(mob/user, list/params) +/obj/item/gun/ballistic/pirate/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src && destroyed) to_chat(user, "\The [src]'s chamber is too warped to extract the casing!") return @@ -403,7 +403,7 @@ else ..() -/obj/item/gun/ballistic/konigin/attack_hand(mob/user, list/params) +/obj/item/gun/ballistic/konigin/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src && use_shotgun) shotgun.unload_ammo(user) else diff --git a/code/modules/projectiles/guns/projectile/rocket.dm b/code/modules/projectiles/guns/projectile/rocket.dm index ff201c946ef5..12dac8984e58 100644 --- a/code/modules/projectiles/guns/projectile/rocket.dm +++ b/code/modules/projectiles/guns/projectile/rocket.dm @@ -45,7 +45,7 @@ else ..() -/obj/item/gun/ballistic/rocket/attack_hand(mob/user, list/params) +/obj/item/gun/ballistic/rocket/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) unload(user) else @@ -86,7 +86,7 @@ to_chat(user, "You cannot reload the [src]!") return -/obj/item/gun/ballistic/rocket/collapsible/attack_hand(mob/user, list/params) +/obj/item/gun/ballistic/rocket/collapsible/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) to_chat(user, "You cannot unload the [src]'s munition!") return @@ -167,7 +167,7 @@ to_chat(user, "\The [src] is completely inoperable!") handle_click_empty() -/obj/item/gun/ballistic/rocket/tyrmalin/attack_hand(mob/user, list/params) +/obj/item/gun/ballistic/rocket/tyrmalin/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src && destroyed) to_chat(user, "\The [src]'s chamber is too warped to extract the casing!") return diff --git a/code/modules/random_map/drop/droppod_doors.dm b/code/modules/random_map/drop/droppod_doors.dm index e6b6c02f0724..619144a6e58b 100644 --- a/code/modules/random_map/drop/droppod_doors.dm +++ b/code/modules/random_map/drop/droppod_doors.dm @@ -23,7 +23,7 @@ /obj/structure/droppod_door/attack_generic(var/mob/user) attack_hand(user) -/obj/structure/droppod_door/attack_hand(mob/user, list/params) +/obj/structure/droppod_door/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(deploying) return to_chat(user, "You prime the explosive bolts. Better get clear!") sleep(30) diff --git a/code/modules/reagents/chemistry/machinery.dm b/code/modules/reagents/chemistry/machinery.dm index dd4191774414..731bb44d72a0 100644 --- a/code/modules/reagents/chemistry/machinery.dm +++ b/code/modules/reagents/chemistry/machinery.dm @@ -160,7 +160,7 @@ if(AM in holdingitems) holdingitems -= AM -/obj/machinery/reagentgrinder/attack_hand(mob/user, list/params) +/obj/machinery/reagentgrinder/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) interact(user) /obj/machinery/reagentgrinder/interact(mob/user as mob) // The microwave Menu //I am reasonably certain that this is not a microwave diff --git a/code/modules/reagents/distilling/distilling.dm b/code/modules/reagents/distilling/distilling.dm index 04436f57fb1f..2384c3b21b59 100644 --- a/code/modules/reagents/distilling/distilling.dm +++ b/code/modules/reagents/distilling/distilling.dm @@ -107,7 +107,7 @@ ..() -/obj/machinery/portable_atmospherics/powered/reagent_distillery/attack_hand(mob/user, list/params) +/obj/machinery/portable_atmospherics/powered/reagent_distillery/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/list/options = list() options["examine"] = radial_examine options["use"] = radial_use diff --git a/code/modules/reagents/items/hypospray.dm b/code/modules/reagents/items/hypospray.dm index 8986f462573f..c3b990c67d25 100644 --- a/code/modules/reagents/items/hypospray.dm +++ b/code/modules/reagents/items/hypospray.dm @@ -68,7 +68,7 @@ if(HYPOSPRAY_MODE_SPRAY) . += "spray" -/obj/item/hypospray/attack_hand(mob/user, list/params) +/obj/item/hypospray/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.is_holding_inactive(src)) if(isnull(loaded)) user.action_feedback(SPAN_WARNING("[src] has no vial loaded."), src) diff --git a/code/modules/reagents/machinery/chem_master.dm b/code/modules/reagents/machinery/chem_master.dm index 691d9a521137..81d7a77327bd 100644 --- a/code/modules/reagents/machinery/chem_master.dm +++ b/code/modules/reagents/machinery/chem_master.dm @@ -148,7 +148,7 @@ pill_bottle = null return ..() -/obj/machinery/chem_master/attack_hand(mob/user, list/params) +/obj/machinery/chem_master/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & BROKEN) return user.set_machine(src) diff --git a/code/modules/reagents/machinery/reagent_dispenser/fuel.dm b/code/modules/reagents/machinery/reagent_dispenser/fuel.dm index e7e9faab4139..b4be4914556a 100644 --- a/code/modules/reagents/machinery/reagent_dispenser/fuel.dm +++ b/code/modules/reagents/machinery/reagent_dispenser/fuel.dm @@ -39,7 +39,7 @@ if(rig) . += "There is some kind of device rigged to the tank." -/obj/structure/reagent_dispensers/fueltank/attack_hand() +/obj/structure/reagent_dispensers/fueltank/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (rig) usr.visible_message("[usr] begins to detach [rig] from \the [src].", "You begin to detach [rig] from \the [src]") if(do_after(usr, 20)) diff --git a/code/modules/reagents/machinery/reagent_dispenser/watercooler.dm b/code/modules/reagents/machinery/reagent_dispenser/watercooler.dm index 9926b6417d6d..95e3b73397d5 100644 --- a/code/modules/reagents/machinery/reagent_dispenser/watercooler.dm +++ b/code/modules/reagents/machinery/reagent_dispenser/watercooler.dm @@ -122,7 +122,7 @@ to_chat(user, "There is already a cup dispenser there!") return -/obj/structure/reagent_dispensers/water_cooler/attack_hand(mob/user) +/obj/structure/reagent_dispensers/water_cooler/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(cups) new /obj/item/reagent_containers/food/drinks/sillycup(src.loc) cups-- diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm index 16fef9d00e2b..93760ce61848 100644 --- a/code/modules/reagents/reagent_containers/glass.dm +++ b/code/modules/reagents/reagent_containers/glass.dm @@ -172,7 +172,7 @@ . = ..() update_icon() -/obj/item/reagent_containers/glass/beaker/attack_hand(mob/user, list/params) +/obj/item/reagent_containers/glass/beaker/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() update_icon() diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm index 57af42c64645..79621a907c05 100644 --- a/code/modules/reagents/reagent_containers/hypospray.dm +++ b/code/modules/reagents/reagent_containers/hypospray.dm @@ -93,7 +93,7 @@ volume = loaded_vial.volume reagents.maximum_volume = loaded_vial.reagents.maximum_volume -/obj/item/reagent_containers/hypospray/vial/attack_hand(mob/user, list/params) +/obj/item/reagent_containers/hypospray/vial/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) if(loaded_vial) reagents.trans_to_holder(loaded_vial.reagents,volume) diff --git a/code/modules/reagents/reagent_containers/organic.dm b/code/modules/reagents/reagent_containers/organic.dm index 71e56dfe8d30..5e6f99728893 100644 --- a/code/modules/reagents/reagent_containers/organic.dm +++ b/code/modules/reagents/reagent_containers/organic.dm @@ -51,7 +51,7 @@ . = ..() update_icon() -/obj/item/reagent_containers/organic/attack_hand(mob/user, list/params) +/obj/item/reagent_containers/organic/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() update_icon() diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index 586eea6f7c45..0d68bfa56c6f 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -47,7 +47,7 @@ return update_icon() -/obj/item/reagent_containers/syringe/attack_hand(mob/user, list/params) +/obj/item/reagent_containers/syringe/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() update_icon() diff --git a/code/modules/research/machinery/rdconsole.dm b/code/modules/research/machinery/rdconsole.dm index 5caf77516ca8..4aed6ff2db36 100644 --- a/code/modules/research/machinery/rdconsole.dm +++ b/code/modules/research/machinery/rdconsole.dm @@ -165,7 +165,7 @@ won't update every console in existence) but it's more of a hassle to do. Also, dat += "" return dat.Join() -/obj/machinery/computer/rdconsole/attack_hand(mob/user, list/params) +/obj/machinery/computer/rdconsole/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (BROKEN|NOPOWER)) return ui_interact(user) diff --git a/code/modules/research/machinery/rdmachines.dm b/code/modules/research/machinery/rdmachines.dm index a0c8040fec25..5d31c7a76cf5 100644 --- a/code/modules/research/machinery/rdmachines.dm +++ b/code/modules/research/machinery/rdmachines.dm @@ -14,7 +14,7 @@ var/list/stored_materials = list() // Materials this machine can accept. var/list/hidden_materials = list() // Materials this machine will not display, unless it contains them. Must be in the materials list as well. -/obj/machinery/r_n_d/attack_hand(mob/user, list/params) +/obj/machinery/r_n_d/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_HARM) return ..() return diff --git a/code/modules/research/machinery/server.dm b/code/modules/research/machinery/server.dm index 987d9d65976b..734343e239b1 100644 --- a/code/modules/research/machinery/server.dm +++ b/code/modules/research/machinery/server.dm @@ -285,7 +285,7 @@ target.files.known_tech |= from.files.known_tech return TRUE -/obj/machinery/computer/rdservercontrol/attack_hand(mob/user, list/params) +/obj/machinery/computer/rdservercontrol/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (BROKEN|NOPOWER)) return ui_interact(user) diff --git a/code/modules/resleeving/computers.dm b/code/modules/resleeving/computers.dm index a539e8c311a1..3101f93ae255 100644 --- a/code/modules/resleeving/computers.dm +++ b/code/modules/resleeving/computers.dm @@ -135,7 +135,7 @@ /obj/machinery/computer/transhuman/resleeving/attack_ai(mob/user as mob) return attack_hand(user) -/obj/machinery/computer/transhuman/resleeving/attack_hand(mob/user, list/params) +/obj/machinery/computer/transhuman/resleeving/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.set_machine(src) add_fingerprint(user) diff --git a/code/modules/resleeving/infomorph.dm b/code/modules/resleeving/infomorph.dm index 13b11d6e4f93..10ddd357b16b 100644 --- a/code/modules/resleeving/infomorph.dm +++ b/code/modules/resleeving/infomorph.dm @@ -343,7 +343,7 @@ var/list/infomorph_emotions = list( if(stat != 2) close_up() return -/mob/living/silicon/infomorph/attack_hand(mob/user, list/params) +/mob/living/silicon/infomorph/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) visible_message("[user.name] boops [src] on the head.") close_up() diff --git a/code/modules/resleeving/machines.dm b/code/modules/resleeving/machines.dm index 6eb21e0d84f8..145205f7d834 100644 --- a/code/modules/resleeving/machines.dm +++ b/code/modules/resleeving/machines.dm @@ -341,7 +341,7 @@ return 1 -/obj/machinery/transhuman/synthprinter/attack_hand(mob/user, list/params) +/obj/machinery/transhuman/synthprinter/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((busy == 0) || (machine_stat & NOPOWER)) return to_chat(user, "Current print cycle is [busy]% complete.") @@ -430,7 +430,7 @@ manip_rating += M.rating blur_amount = (48 - manip_rating * 8) -/obj/machinery/transhuman/resleever/attack_hand(mob/user, list/params) +/obj/machinery/transhuman/resleever/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.set_machine(src) var/health_text = "" var/mind_text = "" diff --git a/code/modules/resleeving/mirror.dm b/code/modules/resleeving/mirror.dm index 92bebfefa35a..c084fa6aa17b 100644 --- a/code/modules/resleeving/mirror.dm +++ b/code/modules/resleeving/mirror.dm @@ -186,7 +186,7 @@ imp = null update_icon() -/obj/item/mirrortool/attack_hand(mob/user as mob) +/obj/item/mirrortool/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) user.put_in_hands_or_drop(imp) imp = null diff --git a/code/modules/rogueminer_vr/zone_console.dm b/code/modules/rogueminer_vr/zone_console.dm index 633bdd2b0232..4524216bb76c 100644 --- a/code/modules/rogueminer_vr/zone_console.dm +++ b/code/modules/rogueminer_vr/zone_console.dm @@ -32,7 +32,7 @@ /obj/machinery/computer/roguezones/attack_ai(mob/user as mob) return attack_hand(user) -/obj/machinery/computer/roguezones/attack_hand(mob/user, list/params) +/obj/machinery/computer/roguezones/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) if(machine_stat & (BROKEN|NOPOWER)) return diff --git a/code/modules/security levels/keycard authentication.dm b/code/modules/security levels/keycard authentication.dm index 526f672973d5..1506cffaca37 100644 --- a/code/modules/security levels/keycard authentication.dm +++ b/code/modules/security levels/keycard authentication.dm @@ -68,7 +68,7 @@ if(machine_stat & NOPOWER) icon_state = "auth_off" -/obj/machinery/keycard_auth/attack_hand(mob/user, list/params) +/obj/machinery/keycard_auth/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.stat || machine_stat & (NOPOWER|BROKEN)) to_chat(user, "This device is not powered.") return diff --git a/code/modules/shieldgen/emergency_shield.dm b/code/modules/shieldgen/emergency_shield.dm index b6ae9691d5ed..191f4024fc1b 100644 --- a/code/modules/shieldgen/emergency_shield.dm +++ b/code/modules/shieldgen/emergency_shield.dm @@ -149,7 +149,7 @@ else check_delay-- -/obj/machinery/shieldgen/attack_hand(mob/user, list/params) +/obj/machinery/shieldgen/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(locked) to_chat(user, "The machine is locked, you are unable to use it.") return diff --git a/code/modules/shieldgen/energy_field.dm b/code/modules/shieldgen/energy_field.dm index 666014f6393f..abb6b3f07911 100644 --- a/code/modules/shieldgen/energy_field.dm +++ b/code/modules/shieldgen/energy_field.dm @@ -62,7 +62,7 @@ adjust_strength(damage / 20) return damage -/obj/effect/energy_field/attack_hand(mob/user, list/params) +/obj/effect/energy_field/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) impact_effect(3) // Harmless, but still produces the 'impact' effect. ..() diff --git a/code/modules/shieldgen/energy_shield.dm b/code/modules/shieldgen/energy_shield.dm index 434a484ad33d..009e9bbb5b8e 100644 --- a/code/modules/shieldgen/energy_shield.dm +++ b/code/modules/shieldgen/energy_shield.dm @@ -162,7 +162,7 @@ animate(src, alpha = initial(alpha), time = 1 SECOND) // Just for fun -/obj/effect/shield/attack_hand(mob/user, list/params) +/obj/effect/shield/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) flash_adjacent_segments(3) /obj/effect/shield/proc/take_damage_legacy(var/damage, var/damtype, var/hitby) diff --git a/code/modules/shieldgen/handheld_defuser.dm b/code/modules/shieldgen/handheld_defuser.dm index 2155a0220615..ae075de65698 100644 --- a/code/modules/shieldgen/handheld_defuser.dm +++ b/code/modules/shieldgen/handheld_defuser.dm @@ -61,7 +61,7 @@ to_chat(user, "The charge meter reads [cell ? cell.percent() : 0]%") to_chat(user, "It is [enabled ? "enabled" : "disabled"].") -/obj/item/shield_diffuser/attack_hand(mob/user, list/params) +/obj/item/shield_diffuser/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) if(cell) cell.update_icon() diff --git a/code/modules/shieldgen/sheldwallgen.dm b/code/modules/shieldgen/sheldwallgen.dm index 7220b26436b0..93aaed7bc407 100644 --- a/code/modules/shieldgen/sheldwallgen.dm +++ b/code/modules/shieldgen/sheldwallgen.dm @@ -26,7 +26,7 @@ var/max_stored_power = 50000 //50 kW use_power = USE_POWER_OFF //Draws directly from power net. Does not use APC power. -/obj/machinery/shieldwallgen/attack_hand(mob/user, list/params) +/obj/machinery/shieldwallgen/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(state != 1) to_chat(user, "The shield generator needs to be firmly secured to the floor first.") return 1 @@ -256,7 +256,7 @@ update_nearby_tiles() ..() -/obj/machinery/shieldwall/attack_hand(mob/user, list/params) +/obj/machinery/shieldwall/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return diff --git a/code/modules/shieldgen/shield_capacitor.dm b/code/modules/shieldgen/shield_capacitor.dm index 1d6e73970352..739d6cf27ce5 100644 --- a/code/modules/shieldgen/shield_capacitor.dm +++ b/code/modules/shieldgen/shield_capacitor.dm @@ -67,7 +67,7 @@ else ..() -/obj/machinery/shield_capacitor/attack_hand(mob/user, list/params) +/obj/machinery/shield_capacitor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (BROKEN)) return interact(user) diff --git a/code/modules/shieldgen/shield_diffuser.dm b/code/modules/shieldgen/shield_diffuser.dm index c3b2636463e6..67a4f842f17c 100644 --- a/code/modules/shieldgen/shield_diffuser.dm +++ b/code/modules/shieldgen/shield_diffuser.dm @@ -41,7 +41,7 @@ else icon_state = "fdiffuser_on" -/obj/machinery/shield_diffuser/attack_hand(mob/user, list/params) +/obj/machinery/shield_diffuser/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((. = ..())) return if(alarm) diff --git a/code/modules/shieldgen/shield_gen.dm b/code/modules/shieldgen/shield_gen.dm index 1048bab6de77..4c9e84b79e00 100644 --- a/code/modules/shieldgen/shield_gen.dm +++ b/code/modules/shieldgen/shield_gen.dm @@ -94,7 +94,7 @@ /obj/machinery/shield_gen/attack_ai(user as mob) return src.attack_hand(user) -/obj/machinery/shield_gen/attack_hand(mob/user, list/params) +/obj/machinery/shield_gen/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (BROKEN)) return interact(user) diff --git a/code/modules/shieldgen/shield_generator.dm b/code/modules/shieldgen/shield_generator.dm index 65caacedfc4e..efcd593b77b5 100644 --- a/code/modules/shieldgen/shield_generator.dm +++ b/code/modules/shieldgen/shield_generator.dm @@ -431,7 +431,7 @@ return data -/obj/machinery/power/shield_generator/attack_hand(mob/user, list/params) +/obj/machinery/power/shield_generator/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((. = ..())) return if(panel_open && Adjacent(user)) diff --git a/code/modules/shuttles/shuttle_console.dm b/code/modules/shuttles/shuttle_console.dm index 9a639edbff8b..258d6f7d761c 100644 --- a/code/modules/shuttles/shuttle_console.dm +++ b/code/modules/shuttles/shuttle_console.dm @@ -11,7 +11,7 @@ var/skip_act = FALSE var/tgui_subtemplate = "ShuttleControlConsoleDefault" -/obj/machinery/computer/shuttle_control/attack_hand(mob/user, list/params) +/obj/machinery/computer/shuttle_control/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..(user)) return if(!allowed(user)) diff --git a/code/modules/species/protean/protean_blob.dm b/code/modules/species/protean/protean_blob.dm index 165146f9288f..336209c011b4 100644 --- a/code/modules/species/protean/protean_blob.dm +++ b/code/modules/species/protean/protean_blob.dm @@ -258,7 +258,7 @@ else return ..() -/mob/living/simple_mob/protean_blob/attack_hand(mob/user, list/params) +/mob/living/simple_mob/protean_blob/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/L = user if(!istype(L)) return diff --git a/code/modules/species/station/xenomorph_hybrids/hybrid_resin.dm b/code/modules/species/station/xenomorph_hybrids/hybrid_resin.dm index a4ca77512b9d..9af0aa0221b3 100644 --- a/code/modules/species/station/xenomorph_hybrids/hybrid_resin.dm +++ b/code/modules/species/station/xenomorph_hybrids/hybrid_resin.dm @@ -178,7 +178,7 @@ T.thermal_conductivity = initial(T.thermal_conductivity) ..() -/obj/structure/alien/hybrid_resin/attack_hand(mob/user, list/params) +/obj/structure/alien/hybrid_resin/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(iscarbon(user)) var/mob/living/carbon/C = user if(locate(/obj/item/organ/internal/xenos/hivenode) in C.internal_organs) diff --git a/code/modules/species/xenomorphs/alien_facehugger.dm b/code/modules/species/xenomorphs/alien_facehugger.dm index 159d148bed6f..5eb271e11ce5 100644 --- a/code/modules/species/xenomorphs/alien_facehugger.dm +++ b/code/modules/species/xenomorphs/alien_facehugger.dm @@ -30,7 +30,7 @@ var/const/MAX_ACTIVE_TIME = 400 var/strength = 5 var/attached = 0 -/obj/item/clothing/mask/facehugger/attack_hand(mob/user, list/params) +/obj/item/clothing/mask/facehugger/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((stat == CONSCIOUS && !sterile)) if(Attach(user)) return @@ -315,7 +315,7 @@ var/const/MAX_ACTIVE_TIME = 400 var/strength = 5 var/attached = 0 -/mob/living/simple_mob/animal/space/alien/facehugger/attack_hand(mob/user, list/params) +/mob/living/simple_mob/animal/space/alien/facehugger/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((stat == CONSCIOUS && !sterile)) if(Attach(user)) diff --git a/code/modules/stockmarket/computer.dm b/code/modules/stockmarket/computer.dm index 666891005f5d..e30228b5a8cb 100644 --- a/code/modules/stockmarket/computer.dm +++ b/code/modules/stockmarket/computer.dm @@ -13,7 +13,7 @@ . = ..() logged_in = "Cargo Department" -/obj/machinery/computer/stockexchange/attack_hand(mob/user, list/params) +/obj/machinery/computer/stockexchange/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..(user)) return diff --git a/code/modules/telesci/hyper_pad.dm b/code/modules/telesci/hyper_pad.dm index cd5c759375cf..e3c129750dc6 100644 --- a/code/modules/telesci/hyper_pad.dm +++ b/code/modules/telesci/hyper_pad.dm @@ -56,7 +56,7 @@ if(primary) primary.attack_ghost(ghost) -/obj/machinery/hyperpad/centre/attack_hand(mob/user, list/params) +/obj/machinery/hyperpad/centre/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return @@ -77,7 +77,7 @@ src.add_fingerprint(user) startteleport(user) -/obj/machinery/hyperpad/attack_hand(mob/user, list/params) +/obj/machinery/hyperpad/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(primary) primary.attack_hand(user) diff --git a/code/modules/telesci/quantum_pad.dm b/code/modules/telesci/quantum_pad.dm index 3aabd75950bc..ee060a8bdfab 100644 --- a/code/modules/telesci/quantum_pad.dm +++ b/code/modules/telesci/quantum_pad.dm @@ -78,7 +78,7 @@ else icon_state = initial(icon_state) -/obj/machinery/power/quantumpad/attack_hand(mob/user, list/params) +/obj/machinery/power/quantumpad/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return diff --git a/code/modules/telesci/telesci_computer.dm b/code/modules/telesci/telesci_computer.dm index 7445b287c5ca..3123dae91893 100644 --- a/code/modules/telesci/telesci_computer.dm +++ b/code/modules/telesci/telesci_computer.dm @@ -77,7 +77,7 @@ /obj/machinery/computer/telescience/attack_ai(mob/user) src.attack_hand(user) -/obj/machinery/computer/telescience/attack_hand(mob/user, list/params) +/obj/machinery/computer/telescience/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return nano_ui_interact(user) diff --git a/code/modules/turbolift/turbolift_console.dm b/code/modules/turbolift/turbolift_console.dm index 782afd39a580..129ddc9d3bb2 100644 --- a/code/modules/turbolift/turbolift_console.dm +++ b/code/modules/turbolift/turbolift_console.dm @@ -39,7 +39,7 @@ /obj/structure/lift/attack_generic(var/mob/user) return attack_hand(user) -/obj/structure/lift/attack_hand(mob/user, list/params) +/obj/structure/lift/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return interact(user) /obj/structure/lift/interact(var/mob/user) diff --git a/code/modules/vehicles/sealed/mecha/mech_fabricator.dm b/code/modules/vehicles/sealed/mecha/mech_fabricator.dm index 1a6fe4513e80..f9c009710b78 100644 --- a/code/modules/vehicles/sealed/mecha/mech_fabricator.dm +++ b/code/modules/vehicles/sealed/mecha/mech_fabricator.dm @@ -482,7 +482,7 @@ immediate += /datum/asset_pack/spritesheet/materials return ..() -/obj/machinery/mecha_part_fabricator/attack_hand(mob/user, list/params) +/obj/machinery/mecha_part_fabricator/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return if(!allowed(user)) diff --git a/code/modules/vehicles/sealed/mecha/mecha.dm b/code/modules/vehicles/sealed/mecha/mecha.dm index 5af90412324d..a7107c0848fa 100644 --- a/code/modules/vehicles/sealed/mecha/mecha.dm +++ b/code/modules/vehicles/sealed/mecha/mecha.dm @@ -1032,7 +1032,7 @@ qdel(src) return -/obj/vehicle/sealed/mecha/attack_hand(mob/user, list/params) +/obj/vehicle/sealed/mecha/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user == occupant_legacy) show_radial_occupant(user) return diff --git a/code/modules/vehicles/sealed/mecha/mecha_control_console.dm b/code/modules/vehicles/sealed/mecha/mecha_control_console.dm index 2d1483ab79d0..b9c9ee11c8e4 100644 --- a/code/modules/vehicles/sealed/mecha/mecha_control_console.dm +++ b/code/modules/vehicles/sealed/mecha/mecha_control_console.dm @@ -13,7 +13,7 @@ /obj/machinery/computer/mecha/attack_ai(mob/user) return attack_hand(user) -/obj/machinery/computer/mecha/attack_hand(mob/user, list/params) +/obj/machinery/computer/mecha/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return ui_interact(user) diff --git a/code/modules/vehicles/sealed/mecha/mecha_parts.dm b/code/modules/vehicles/sealed/mecha/mecha_parts.dm index 652ff65aee8b..22f7f2c7407c 100644 --- a/code/modules/vehicles/sealed/mecha/mecha_parts.dm +++ b/code/modules/vehicles/sealed/mecha/mecha_parts.dm @@ -22,7 +22,7 @@ ..() return -/obj/item/mecha_parts/chassis/attack_hand(mob/user, list/params) +/obj/item/mecha_parts/chassis/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return //! Ripley @@ -467,7 +467,7 @@ ..() return -/obj/item/mecha_parts/fighter/chassis/attack_hand(mob/user, list/params) +/obj/item/mecha_parts/fighter/chassis/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return diff --git a/code/modules/vehicles/sealed/mecha/subtypes/micro/mecha_parts_vr.dm b/code/modules/vehicles/sealed/mecha/subtypes/micro/mecha_parts_vr.dm index 7c49970beadb..6b361fa23c4d 100644 --- a/code/modules/vehicles/sealed/mecha/subtypes/micro/mecha_parts_vr.dm +++ b/code/modules/vehicles/sealed/mecha/subtypes/micro/mecha_parts_vr.dm @@ -17,7 +17,7 @@ ..() return -/obj/item/mecha_parts/micro/chassis/attack_hand(mob/user, list/params) +/obj/item/mecha_parts/micro/chassis/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return //Gopher diff --git a/code/modules/vehicles_legacy/bike.dm b/code/modules/vehicles_legacy/bike.dm index 9e5ee11e95c7..d977de210b81 100644 --- a/code/modules/vehicles_legacy/bike.dm +++ b/code/modules/vehicles_legacy/bike.dm @@ -120,7 +120,7 @@ return CLICKCHAIN_DO_NOT_PROPAGATE return CLICKCHAIN_DO_NOT_PROPAGATE -/obj/vehicle_old/bike/attack_hand(mob/user, list/params) +/obj/vehicle_old/bike/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user == load) unload(load, user) to_chat(user, "You unbuckle yourself from \the [src].") diff --git a/code/modules/vehicles_legacy/skateboard.dm b/code/modules/vehicles_legacy/skateboard.dm index 1b500e707aed..7b481283a238 100644 --- a/code/modules/vehicles_legacy/skateboard.dm +++ b/code/modules/vehicles_legacy/skateboard.dm @@ -47,7 +47,7 @@ return CLICKCHAIN_DO_NOT_PROPAGATE return CLICKCHAIN_DO_NOT_PROPAGATE -/obj/vehicle_old/skateboard/attack_hand(mob/user, list/params) +/obj/vehicle_old/skateboard/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user == load) unbuckle_mob(load, user) to_chat(user, "You unbuckle yourself from \the [src].") diff --git a/code/modules/vehicles_legacy/train.dm b/code/modules/vehicles_legacy/train.dm index 32f45a77dabe..a957a1c6776e 100644 --- a/code/modules/vehicles_legacy/train.dm +++ b/code/modules/vehicles_legacy/train.dm @@ -109,7 +109,7 @@ return CLICKCHAIN_DO_NOT_PROPAGATE return CLICKCHAIN_DO_NOT_PROPAGATE -/obj/vehicle_old/train/attack_hand(mob/user, list/params) +/obj/vehicle_old/train/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.stat || user.restrained() || !Adjacent(user)) return 0 diff --git a/code/modules/virus2/curer.dm b/code/modules/virus2/curer.dm index 4263e11d7450..4735ccdf43ce 100644 --- a/code/modules/virus2/curer.dm +++ b/code/modules/virus2/curer.dm @@ -36,7 +36,7 @@ /obj/machinery/computer/curer/attack_ai(var/mob/user as mob) return src.attack_hand(user) -/obj/machinery/computer/curer/attack_hand(mob/user, list/params) +/obj/machinery/computer/curer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return user.machine = src diff --git a/code/modules/virus2/diseasesplicer.dm b/code/modules/virus2/diseasesplicer.dm index b2a1e2ef5d2b..9d9771d2a389 100644 --- a/code/modules/virus2/diseasesplicer.dm +++ b/code/modules/virus2/diseasesplicer.dm @@ -38,7 +38,7 @@ /obj/machinery/computer/diseasesplicer/attack_ai(var/mob/user as mob) return src.attack_hand(user) -/obj/machinery/computer/diseasesplicer/attack_hand(mob/user, list/params) +/obj/machinery/computer/diseasesplicer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return TRUE ui_interact(user) diff --git a/code/modules/virus2/dishincubator.dm b/code/modules/virus2/dishincubator.dm index df4e6ab94357..eb32bee10fb0 100644 --- a/code/modules/virus2/dishincubator.dm +++ b/code/modules/virus2/dishincubator.dm @@ -45,7 +45,7 @@ src.attack_hand(user) -/obj/machinery/disease2/incubator/attack_hand(mob/user, list/params) +/obj/machinery/disease2/incubator/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (NOPOWER|BROKEN)) return nano_ui_interact(user) diff --git a/code/modules/virus2/isolator.dm b/code/modules/virus2/isolator.dm index ea48c01a5ede..80093b43806d 100644 --- a/code/modules/virus2/isolator.dm +++ b/code/modules/virus2/isolator.dm @@ -41,7 +41,7 @@ src.attack_hand(user) -/obj/machinery/disease2/isolator/attack_hand(mob/user, list/params) +/obj/machinery/disease2/isolator/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (NOPOWER|BROKEN)) return ui_interact(user) diff --git a/code/modules/vore/fluffstuff/custom_items.dm b/code/modules/vore/fluffstuff/custom_items.dm index 531e04b41d11..2266c6708b9f 100644 --- a/code/modules/vore/fluffstuff/custom_items.dm +++ b/code/modules/vore/fluffstuff/custom_items.dm @@ -597,7 +597,7 @@ ..() -/obj/item/perfect_tele/attack_hand(mob/user, list/params) +/obj/item/perfect_tele/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src && power_source) to_chat(user,"You eject \the [power_source] from \the [src].") user.put_in_hands(power_source) @@ -859,7 +859,7 @@ tele_hand = null return ..() -/obj/item/perfect_tele_beacon/attack_hand(mob/user, list/params) +/obj/item/perfect_tele_beacon/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((user.ckey != creator) && !(user.ckey in warned_users)) warned_users |= user.ckey var/choice = alert(user,"This device is a translocator beacon. Having it on your person may mean that anyone \ diff --git a/code/modules/vore/weight/fitness_machines_vr.dm b/code/modules/vore/weight/fitness_machines_vr.dm index 1af486035e79..5e27217d991d 100644 --- a/code/modules/vore/weight/fitness_machines_vr.dm +++ b/code/modules/vore/weight/fitness_machines_vr.dm @@ -11,7 +11,7 @@ var/cooldown = 10 var/weightloss_power = 1 -/obj/machinery/fitness/attack_hand(mob/user, list/params) +/obj/machinery/fitness/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return @@ -68,7 +68,7 @@ playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) return -/obj/machinery/fitness/heavy/attack_hand(mob/user, list/params) +/obj/machinery/fitness/heavy/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!anchored) to_chat(user, "For safety reasons, you are required to have this equipment wrenched down before using it!") return @@ -98,7 +98,7 @@ idle_power_usage = 0 active_power_usage = 0 -/obj/machinery/scale/attack_hand(mob/user, list/params) +/obj/machinery/scale/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return diff --git a/code/modules/xenoarcheaology/anomaly_container.dm b/code/modules/xenoarcheaology/anomaly_container.dm index fc2efc0ad1a6..85d76cf6521f 100644 --- a/code/modules/xenoarcheaology/anomaly_container.dm +++ b/code/modules/xenoarcheaology/anomaly_container.dm @@ -14,7 +14,7 @@ if(A) contain(A) -/obj/structure/anomaly_container/attack_hand(mob/user, list/params) +/obj/structure/anomaly_container/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) release() /obj/structure/anomaly_container/attack_robot(var/mob/user) diff --git a/code/modules/xenoarcheaology/artifacts/artifact.dm b/code/modules/xenoarcheaology/artifacts/artifact.dm index a55ec3da15ef..54ffea27f32b 100644 --- a/code/modules/xenoarcheaology/artifacts/artifact.dm +++ b/code/modules/xenoarcheaology/artifacts/artifact.dm @@ -175,7 +175,7 @@ if(secondary_effect && secondary_effect.trigger == TRIGGER_NITRO && !secondary_effect.activated) secondary_effect.ToggleActivate(0) -/obj/machinery/artifact/attack_hand(mob/user, list/params) +/obj/machinery/artifact/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (get_dist(user, src) > 1) to_chat(user, "You can't reach [src] from here.") return diff --git a/code/modules/xenoarcheaology/artifacts/gigadrill.dm b/code/modules/xenoarcheaology/artifacts/gigadrill.dm index 7f54c8337021..73a9c119aef5 100644 --- a/code/modules/xenoarcheaology/artifacts/gigadrill.dm +++ b/code/modules/xenoarcheaology/artifacts/gigadrill.dm @@ -9,7 +9,7 @@ density = 1 layer = ABOVE_JUNK_LAYER -/obj/machinery/giga_drill/attack_hand(mob/user, list/params) +/obj/machinery/giga_drill/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(active) active = 0 icon_state = "gigadrill" diff --git a/code/modules/xenoarcheaology/artifacts/replicator.dm b/code/modules/xenoarcheaology/artifacts/replicator.dm index 8b9f81e9e511..726c86594f69 100644 --- a/code/modules/xenoarcheaology/artifacts/replicator.dm +++ b/code/modules/xenoarcheaology/artifacts/replicator.dm @@ -138,7 +138,7 @@ last_process_time = world.time -/obj/machinery/replicator/attack_hand(mob/user, list/params) +/obj/machinery/replicator/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /obj/machinery/replicator/ui_interact(mob/user, datum/tgui/ui) diff --git a/code/modules/xenoarcheaology/tools/artifact_analyser.dm b/code/modules/xenoarcheaology/tools/artifact_analyser.dm index b10e28b38bf5..40bbe0f242a5 100644 --- a/code/modules/xenoarcheaology/tools/artifact_analyser.dm +++ b/code/modules/xenoarcheaology/tools/artifact_analyser.dm @@ -24,7 +24,7 @@ if(!owned_scanner) owned_scanner = locate(/obj/machinery/artifact_scanpad) in orange(1, src) -/obj/machinery/artifact_analyser/attack_hand(mob/user, list/params) +/obj/machinery/artifact_analyser/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) if(machine_stat & (NOPOWER|BROKEN) || get_dist(src, user) > 1) return diff --git a/code/modules/xenoarcheaology/tools/artifact_harvester.dm b/code/modules/xenoarcheaology/tools/artifact_harvester.dm index b036b06a210d..61bd66103705 100644 --- a/code/modules/xenoarcheaology/tools/artifact_harvester.dm +++ b/code/modules/xenoarcheaology/tools/artifact_harvester.dm @@ -35,7 +35,7 @@ else return..() -/obj/machinery/artifact_harvester/attack_hand(mob/user, list/params) +/obj/machinery/artifact_harvester/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) if(machine_stat & (NOPOWER|BROKEN)) return diff --git a/code/modules/xenoarcheaology/tools/geosample_scanner.dm b/code/modules/xenoarcheaology/tools/geosample_scanner.dm index 8fa28dece263..99d9a99d3095 100644 --- a/code/modules/xenoarcheaology/tools/geosample_scanner.dm +++ b/code/modules/xenoarcheaology/tools/geosample_scanner.dm @@ -115,7 +115,7 @@ if(total_purity && fresh_coolant) coolant_purity = total_purity / fresh_coolant -/obj/machinery/radiocarbon_spectrometer/attack_hand(mob/user, list/params) +/obj/machinery/radiocarbon_spectrometer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /obj/machinery/radiocarbon_spectrometer/ui_interact(mob/user, datum/tgui/ui) diff --git a/code/modules/xenoarcheaology/tools/suspension_generator.dm b/code/modules/xenoarcheaology/tools/suspension_generator.dm index 555e3fb8b71b..9d94bddb67ca 100644 --- a/code/modules/xenoarcheaology/tools/suspension_generator.dm +++ b/code/modules/xenoarcheaology/tools/suspension_generator.dm @@ -35,7 +35,7 @@ else deactivate() -/obj/machinery/suspension_gen/attack_hand(mob/user, list/params) +/obj/machinery/suspension_gen/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(panel_open) if(cell) to_chat(user, SPAN_NOTICE("You remove [cell].")) diff --git a/code/modules/xenobio/machinery/processor.dm b/code/modules/xenobio/machinery/processor.dm index 600a748c01c6..3e0f9474ee46 100644 --- a/code/modules/xenobio/machinery/processor.dm +++ b/code/modules/xenobio/machinery/processor.dm @@ -29,7 +29,7 @@ . += SPAN_NOTICE("It looks slick enough to let slimes glide over it.") . += SPAN_BOLDNOTICE("The automatic intake switch is in the [auto_mode? "On" : "Off"] position.") -/obj/machinery/processor/attack_hand(mob/user, list/params) +/obj/machinery/processor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(processing) to_chat(user, SPAN_WARNING("The processor is in the process of processing!")) return diff --git a/code/modules/xenobio2/machinery/core_extractor.dm b/code/modules/xenobio2/machinery/core_extractor.dm index c7ae4f1dc3fd..7126bf529979 100644 --- a/code/modules/xenobio2/machinery/core_extractor.dm +++ b/code/modules/xenobio2/machinery/core_extractor.dm @@ -145,7 +145,7 @@ eject_slime() //Here lies the UI -/obj/machinery/slime/extractor/attack_hand(mob/user, list/params) +/obj/machinery/slime/extractor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.set_machine(src) interact(user) diff --git a/code/modules/xenobio2/machinery/gene_manipulators.dm b/code/modules/xenobio2/machinery/gene_manipulators.dm index 5599d09ff8d7..548fdfae7b89 100644 --- a/code/modules/xenobio2/machinery/gene_manipulators.dm +++ b/code/modules/xenobio2/machinery/gene_manipulators.dm @@ -59,7 +59,7 @@ /obj/machinery/xenobio/attack_ai(mob/user as mob) return attack_hand(user) -/obj/machinery/xenobio/attack_hand(mob/user, list/params) +/obj/machinery/xenobio/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) nano_ui_interact(user) /obj/machinery/xenobio/attackby(obj/item/W as obj, mob/user as mob) diff --git a/code/modules/xenobio2/machinery/injector_computer.dm b/code/modules/xenobio2/machinery/injector_computer.dm index da929dd1257a..2da0a6d0573c 100644 --- a/code/modules/xenobio2/machinery/injector_computer.dm +++ b/code/modules/xenobio2/machinery/injector_computer.dm @@ -23,7 +23,7 @@ injector.computer = null ..() -/obj/machinery/computer/xenobio2/attack_hand(mob/user, list/params) +/obj/machinery/computer/xenobio2/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return 1 nano_ui_interact(user) diff --git a/code/modules/xenobio2/machinery/slime_replicator.dm b/code/modules/xenobio2/machinery/slime_replicator.dm index edc019043cfa..a36c97634b01 100644 --- a/code/modules/xenobio2/machinery/slime_replicator.dm +++ b/code/modules/xenobio2/machinery/slime_replicator.dm @@ -99,7 +99,7 @@ eject_core() //Here lies the UI -/obj/machinery/slime/replicator/attack_hand(mob/user, list/params) +/obj/machinery/slime/replicator/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.set_machine(src) interact(user) diff --git a/maps/generic/misc.dm b/maps/generic/misc.dm index 3af8d819c540..b511fd2712a1 100644 --- a/maps/generic/misc.dm +++ b/maps/generic/misc.dm @@ -68,7 +68,7 @@ /turf/simulated/floor/maglev/Entered(var/atom/movable/AM, var/atom/old_loc) if(isliving(AM) && prob(50)) track_zap(AM) -/turf/simulated/floor/maglev/attack_hand(mob/user, list/params) +/turf/simulated/floor/maglev/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(prob(75)) track_zap(user) /turf/simulated/floor/maglev/proc/track_zap(var/mob/living/user)