From 36e89191dfaa5c162fa5431160bfbbe2d81ee93d Mon Sep 17 00:00:00 2001
From: SkyratBot <59378654+SkyratBot@users.noreply.github.com>
Date: Fri, 11 Dec 2020 01:39:56 +0100
Subject: [PATCH] [MIRROR] Refactors smiting, adds smite build mode (#2126)
* Refactors smiting, adds smite build mode (#55327)
Refactors smiting out from being a large switch/case into datums.
Adds a new smite option to build mode. This lets you choose a smite and rapidly apply it to a lot of people, presumably for EORG. Requested for by...one of the admins, I forget which.
* Refactors smiting, adds smite build mode
Co-authored-by: Jared-Fogle <35135081+Jared-Fogle@users.noreply.github.com>
---
code/__DEFINES/admin.dm | 24 --
code/_globalvars/lists/admin.dm | 9 +
code/modules/admin/smites/bad_luck.dm | 17 ++
code/modules/admin/smites/berforate.dm | 53 ++++
code/modules/admin/smites/bloodless.dm | 18 ++
code/modules/admin/smites/boneless.dm | 16 ++
code/modules/admin/smites/brain_damage.dm | 7 +
code/modules/admin/smites/bread.dm | 14 ++
code/modules/admin/smites/bsa.dm | 35 +++
code/modules/admin/smites/dock_pay.dm | 31 +++
code/modules/admin/smites/fake_bwoink.dm | 7 +
code/modules/admin/smites/fat.dm | 7 +
code/modules/admin/smites/fireball.dm | 7 +
code/modules/admin/smites/gib.dm | 7 +
code/modules/admin/smites/immerse.dm | 7 +
code/modules/admin/smites/knot_shoes.dm | 15 ++
code/modules/admin/smites/lightning.dm | 19 ++
code/modules/admin/smites/nugget.dm | 21 ++
code/modules/admin/smites/puzzle.dm | 8 +
code/modules/admin/smites/rod.dm | 11 +
code/modules/admin/smites/scarify.dm | 12 +
code/modules/admin/smites/smite.dm | 17 ++
code/modules/admin/smites/supply_pod.dm | 21 ++
code/modules/admin/smites/supply_pod_quick.dm | 49 ++++
code/modules/admin/verbs/bluespacearty.dm | 26 --
code/modules/admin/verbs/randomverbs.dm | 232 +-----------------
code/modules/buildmode/submodes/smite.dm | 40 +++
icons/misc/buildmode.dmi | Bin 1818 -> 2856 bytes
tgstation.dme | 24 +-
29 files changed, 480 insertions(+), 274 deletions(-)
create mode 100644 code/modules/admin/smites/bad_luck.dm
create mode 100644 code/modules/admin/smites/berforate.dm
create mode 100644 code/modules/admin/smites/bloodless.dm
create mode 100644 code/modules/admin/smites/boneless.dm
create mode 100644 code/modules/admin/smites/brain_damage.dm
create mode 100644 code/modules/admin/smites/bread.dm
create mode 100644 code/modules/admin/smites/bsa.dm
create mode 100644 code/modules/admin/smites/dock_pay.dm
create mode 100644 code/modules/admin/smites/fake_bwoink.dm
create mode 100644 code/modules/admin/smites/fat.dm
create mode 100644 code/modules/admin/smites/fireball.dm
create mode 100644 code/modules/admin/smites/gib.dm
create mode 100644 code/modules/admin/smites/immerse.dm
create mode 100644 code/modules/admin/smites/knot_shoes.dm
create mode 100644 code/modules/admin/smites/lightning.dm
create mode 100644 code/modules/admin/smites/nugget.dm
create mode 100644 code/modules/admin/smites/puzzle.dm
create mode 100644 code/modules/admin/smites/rod.dm
create mode 100644 code/modules/admin/smites/scarify.dm
create mode 100644 code/modules/admin/smites/smite.dm
create mode 100644 code/modules/admin/smites/supply_pod.dm
create mode 100644 code/modules/admin/smites/supply_pod_quick.dm
delete mode 100644 code/modules/admin/verbs/bluespacearty.dm
create mode 100644 code/modules/buildmode/submodes/smite.dm
diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm
index 8ecaa7c3645..9bb64c21703 100644
--- a/code/__DEFINES/admin.dm
+++ b/code/__DEFINES/admin.dm
@@ -84,30 +84,6 @@
/turf/Safe_COORD_Location()
return src
-
-#define ADMIN_PUNISHMENT_LIGHTNING "Lightning bolt"
-#define ADMIN_PUNISHMENT_BRAINDAMAGE "Brain damage"
-#define ADMIN_PUNISHMENT_GIB "Gib"
-#define ADMIN_PUNISHMENT_BSA "Bluespace Artillery Device"
-#define ADMIN_PUNISHMENT_FIREBALL "Fireball"
-#define ADMIN_PUNISHMENT_ROD "Immovable Rod"
-#define ADMIN_PUNISHMENT_SUPPLYPOD_QUICK "Supply Pod (Quick)"
-#define ADMIN_PUNISHMENT_SUPPLYPOD "Supply Pod"
-#define ADMIN_PUNISHMENT_MAZING "Puzzle"
-#define ADMIN_PUNISHMENT_IMMERSE "Fully Immerse"
-#define ADMIN_PUNISHMENT_FAT "Fatten up"
-#define ADMIN_PUNISHMENT_FAKEBWOINK "Fake bwoink"
-#define ADMIN_PUNISHMENT_NUGGET "Nugget"
-#define ADMIN_PUNISHMENT_CRACK ":B:oneless"
-#define ADMIN_PUNISHMENT_BLEED ":B:loodless"
-#define ADMIN_PUNISHMENT_PERFORATE ":B:erforate"
-#define ADMIN_PUNISHMENT_SCARIFY "Scarify"
-#define ADMIN_PUNISHMENT_SHOES "Knot Shoes"
-#define ADMIN_PUNISHMENT_DOCK "Dock Pay"
-#define ADMIN_PUNISHMENT_BREAD "Bread"
-/// Applies [/datum/component/omen] to the target, optionally permanently.
-#define ADMIN_PUNISHMENT_BADLUCK "Bad Luck"
-
#define AHELP_ACTIVE 1
#define AHELP_CLOSED 2
#define AHELP_RESOLVED 3
diff --git a/code/_globalvars/lists/admin.dm b/code/_globalvars/lists/admin.dm
index be9f1ff7292..22e4f293468 100644
--- a/code/_globalvars/lists/admin.dm
+++ b/code/_globalvars/lists/admin.dm
@@ -1,3 +1,12 @@
GLOBAL_LIST_EMPTY(stickybanadminexemptions) //stores a list of ckeys exempted from a stickyban (workaround for a bug)
GLOBAL_LIST_EMPTY(stickybanadmintexts) //stores the entire stickyban list temporarily
GLOBAL_VAR(stickbanadminexemptiontimerid) //stores the timerid of the callback that restores all stickybans after an admin joins
+
+/proc/init_smites()
+ var/list/smites = list()
+ for (var/_smite_path in subtypesof(/datum/smite))
+ var/datum/smite/smite_path = _smite_path
+ smites[initial(smite_path.name)] = smite_path
+ return smites
+
+GLOBAL_LIST_INIT_TYPED(smites, /datum/smite, init_smites())
diff --git a/code/modules/admin/smites/bad_luck.dm b/code/modules/admin/smites/bad_luck.dm
new file mode 100644
index 00000000000..b8bac94b1ca
--- /dev/null
+++ b/code/modules/admin/smites/bad_luck.dm
@@ -0,0 +1,17 @@
+/// Gives the target bad luck, optionally permanently
+/datum/smite/bad_luck
+ name = "Bad Luck"
+
+ /// Should the target know they've received bad luck?
+ var/silent
+
+ /// Is this permanent?
+ var/permanent
+
+/datum/smite/bad_luck/configure(client/user)
+ silent = alert(user, "Do you want to apply the omen with a player notification?", "Notify Player?", "Notify", "Silent") == "Silent"
+ permanent = alert(user, "Would you like this to be permanent or removed automatically after the first accident?", "Permanent?", "Permanent", "Temporary") == "Permanent"
+
+/datum/smite/bad_luck/effect(client/user, mob/living/target)
+ . = ..()
+ target.AddComponent(/datum/component/omen, silent, null, permanent)
diff --git a/code/modules/admin/smites/berforate.dm b/code/modules/admin/smites/berforate.dm
new file mode 100644
index 00000000000..ce573237565
--- /dev/null
+++ b/code/modules/admin/smites/berforate.dm
@@ -0,0 +1,53 @@
+/// Fires an absurd amount of bullets at the target
+/datum/smite/berforate
+ name = ":B:erforate"
+
+ /// Determines how fucked the target is
+ var/hatred
+
+/datum/smite/berforate/configure(client/user)
+ var/static/list/how_fucked_is_this_dude = list("A little", "A lot", "So fucking much", "FUCK THIS DUDE")
+ hatred = input(user, "How much do you hate this guy?") in how_fucked_is_this_dude
+
+/datum/smite/berforate/effect(client/user, mob/living/target)
+ . = ..()
+ if (!iscarbon(target))
+ to_chat(user, "This must be used on a carbon mob.", confidential = TRUE)
+ return
+
+ var/repetitions
+ var/shots_per_limb_per_rep = 2
+ var/damage
+ switch (hatred)
+ if ("A little")
+ repetitions = 1
+ damage = 5
+ if ("A lot")
+ repetitions = 2
+ damage = 8
+ if ("So fucking much")
+ repetitions = 3
+ damage = 10
+ if ("FUCK THIS DUDE")
+ repetitions = 4
+ damage = 10
+
+ var/mob/living/carbon/dude = target
+ var/list/open_adj_turfs = get_adjacent_open_turfs(dude)
+ var/list/wound_bonuses = list(15, 70, 110, 250)
+
+ var/delay_per_shot = 1
+ var/delay_counter = 1
+
+ dude.Immobilize(5 SECONDS)
+ for (var/wound_bonus_rep in 1 to repetitions)
+ for (var/_limb in dude.bodyparts)
+ var/obj/item/bodypart/limb = _limb
+ var/shots_this_limb = 0
+ for (var/_iter_turf in shuffle(open_adj_turfs))
+ var/turf/iter_turf = _iter_turf
+ addtimer(CALLBACK(GLOBAL_PROC, .proc/firing_squad, dude, iter_turf, limb.body_zone, wound_bonuses[wound_bonus_rep], damage), delay_counter)
+ delay_counter += delay_per_shot
+ shots_this_limb += 1
+ if (shots_this_limb > shots_per_limb_per_rep)
+ break
diff --git a/code/modules/admin/smites/bloodless.dm b/code/modules/admin/smites/bloodless.dm
new file mode 100644
index 00000000000..cc6e0c7d920
--- /dev/null
+++ b/code/modules/admin/smites/bloodless.dm
@@ -0,0 +1,18 @@
+/// Slashes up the target
+/datum/smite/bloodless
+ name = ":B:loodless"
+
+/datum/smite/bloodless/effect(client/user, mob/living/target)
+ . = ..()
+ if (!iscarbon(target))
+ to_chat(user, "This must be used on a carbon mob.", confidential = TRUE)
+ return
+ var/mob/living/carbon/carbon_target = target
+ for(var/_limb in carbon_target.bodyparts)
+ var/obj/item/bodypart/limb = _limb
+ var/type_wound = pick(list(/datum/wound/slash/severe, /datum/wound/slash/moderate))
+ limb.force_wound_upwards(type_wound, smited = TRUE)
+ type_wound = pick(list(/datum/wound/slash/critical, /datum/wound/slash/severe, /datum/wound/slash/moderate))
+ limb.force_wound_upwards(type_wound, smited = TRUE)
+ type_wound = pick(list(/datum/wound/slash/critical, /datum/wound/slash/severe))
+ limb.force_wound_upwards(type_wound, smited = TRUE)
diff --git a/code/modules/admin/smites/boneless.dm b/code/modules/admin/smites/boneless.dm
new file mode 100644
index 00000000000..e622f2c4f1e
--- /dev/null
+++ b/code/modules/admin/smites/boneless.dm
@@ -0,0 +1,16 @@
+/// Gives the target critically bad wounds
+/datum/smite/boneless
+ name = ":B:oneless"
+
+/datum/smite/boneless/effect(client/user, mob/living/target)
+ . = ..()
+
+ if (!iscarbon(target))
+ to_chat(user, "This must be used on a carbon mob.", confidential = TRUE)
+ return
+
+ var/mob/living/carbon/carbon_target = target
+ for(var/_limb in carbon_target.bodyparts)
+ var/obj/item/bodypart/limb = _limb
+ var/type_wound = pick(list(/datum/wound/blunt/critical, /datum/wound/blunt/severe, /datum/wound/blunt/critical, /datum/wound/blunt/severe, /datum/wound/blunt/moderate))
+ limb.force_wound_upwards(type_wound, smited = TRUE)
diff --git a/code/modules/admin/smites/brain_damage.dm b/code/modules/admin/smites/brain_damage.dm
new file mode 100644
index 00000000000..ceafc49df24
--- /dev/null
+++ b/code/modules/admin/smites/brain_damage.dm
@@ -0,0 +1,7 @@
+/// Inflicts crippling brain damage on the target
+/datum/smite/brain_damage
+ name = "Brain damage"
+
+/datum/smite/brain_damage/effect(client/user, mob/living/target)
+ . = ..()
+ target.adjustOrganLoss(ORGAN_SLOT_BRAIN, BRAIN_DAMAGE_DEATH - 1, BRAIN_DAMAGE_DEATH - 1)
diff --git a/code/modules/admin/smites/bread.dm b/code/modules/admin/smites/bread.dm
new file mode 100644
index 00000000000..2bdd34155e1
--- /dev/null
+++ b/code/modules/admin/smites/bread.dm
@@ -0,0 +1,14 @@
+#define BREADIFY_TIME (5 SECONDS)
+
+/// Turns the target into bread
+/datum/smite/bread
+ name = "Bread"
+
+/datum/smite/bread/effect(client/user, mob/living/target)
+ . = ..()
+ var/mutable_appearance/bread_appearance = mutable_appearance('icons/obj/food/burgerbread.dmi', "bread")
+ var/mutable_appearance/transform_scanline = mutable_appearance('icons/effects/effects.dmi', "transform_effect")
+ target.transformation_animation(bread_appearance,time = BREADIFY_TIME, transform_overlay=transform_scanline, reset_after=TRUE)
+ addtimer(CALLBACK(GLOBAL_PROC, .proc/breadify, target), BREADIFY_TIME)
+
+#undef BREADIFY_TIME
diff --git a/code/modules/admin/smites/bsa.dm b/code/modules/admin/smites/bsa.dm
new file mode 100644
index 00000000000..7df6bc8ad42
--- /dev/null
+++ b/code/modules/admin/smites/bsa.dm
@@ -0,0 +1,35 @@
+#define BSA_CHANCE_TO_BREAK_TILE_TO_PLATING 80
+#define BSA_MAX_DAMAGE 99
+#define BSA_PARALYZE_TIME (40 SECONDS)
+#define BSA_STUTTER_TIME 20
+
+/// Fires the BSA at the target
+/datum/smite/bsa
+ name = "Bluespace Artillery Device"
+
+/datum/smite/bsa/effect(client/user, mob/living/target)
+ . = ..()
+
+ explosion(target.loc, 0, 0, 0, 0)
+
+ var/turf/open/floor/target_turf = get_turf(target)
+ if (istype(target_turf))
+ if (prob(BSA_CHANCE_TO_BREAK_TILE_TO_PLATING))
+ target_turf.break_tile_to_plating()
+ else
+ target_turf.break_tile()
+
+ if (target.health <= 1)
+ target.gib(
+ /* no_brain = */ TRUE,
+ /* no_organs = */ TRUE,
+ )
+ else
+ target.adjustBruteLoss(min(BSA_MAX_DAMAGE, target.health - 1))
+ target.Paralyze(BSA_PARALYZE_TIME)
+ target.stuttering = BSA_STUTTER_TIME
+
+#undef BSA_CHANCE_TO_BREAK_TILE_TO_PLATING
+#undef BSA_MAX_DAMAGE
+#undef BSA_PARALYZE_TIME
+#undef BSA_STUTTER_TIME
diff --git a/code/modules/admin/smites/dock_pay.dm b/code/modules/admin/smites/dock_pay.dm
new file mode 100644
index 00000000000..8c220e25c9f
--- /dev/null
+++ b/code/modules/admin/smites/dock_pay.dm
@@ -0,0 +1,31 @@
+/// Docks the target's pay
+/datum/smite/dock_pay
+ name = "Dock Pay"
+
+/datum/smite/dock_pay/effect(client/user, mob/living/target)
+ . = ..()
+ if (!iscarbon(target))
+ to_chat(user, "This must be used on a carbon mob.", confidential = TRUE)
+ return
+ var/mob/living/carbon/dude = target
+ var/obj/item/card/id/card = dude.get_idcard(TRUE)
+ if (!card)
+ to_chat(user, "[dude] does not have an ID card on!", confidential = TRUE)
+ return
+ if (!card.registered_account)
+ to_chat(user, "[dude] does not have an ID card with an account!", confidential = TRUE)
+ return
+ if (card.registered_account.account_balance == 0)
+ to_chat(user, "ID Card lacks any funds. No pay to dock.")
+ return
+ var/new_cost = input("How much pay are we docking? Current balance: [card.registered_account.account_balance] credits.", "BUDGET CUTS") as num|null
+ if (!new_cost)
+ return
+ if (!(card.registered_account.has_money(new_cost)))
+ to_chat(user, "ID Card lacked funds. Emptying account.")
+ card.registered_account.bank_card_talk("[new_cost] credits deducted from your account based on performance review.")
+ card.registered_account.account_balance = 0
+ else
+ card.registered_account.account_balance = card.registered_account.account_balance - new_cost
+ card.registered_account.bank_card_talk("[new_cost] credits deducted from your account based on performance review.")
+ SEND_SOUND(target, 'sound/machines/buzz-sigh.ogg')
diff --git a/code/modules/admin/smites/fake_bwoink.dm b/code/modules/admin/smites/fake_bwoink.dm
new file mode 100644
index 00000000000..a4179ff5ce8
--- /dev/null
+++ b/code/modules/admin/smites/fake_bwoink.dm
@@ -0,0 +1,7 @@
+/// Sends the target a fake adminhelp sound
+/datum/smite/fake_bwoink
+ name = "Fake bwoink"
+
+/datum/smite/fake_bwoink/effect(client/user, mob/living/target)
+ . = ..()
+ SEND_SOUND(target, 'sound/effects/adminhelp.ogg')
diff --git a/code/modules/admin/smites/fat.dm b/code/modules/admin/smites/fat.dm
new file mode 100644
index 00000000000..4eba39f466e
--- /dev/null
+++ b/code/modules/admin/smites/fat.dm
@@ -0,0 +1,7 @@
+/// Fattens the target
+/datum/smite/fat
+ name = "Fatten up"
+
+/datum/smite/fat/effect(client/user, mob/living/target)
+ . = ..()
+ target.set_nutrition(NUTRITION_LEVEL_FAT * 2)
diff --git a/code/modules/admin/smites/fireball.dm b/code/modules/admin/smites/fireball.dm
new file mode 100644
index 00000000000..98dad65d825
--- /dev/null
+++ b/code/modules/admin/smites/fireball.dm
@@ -0,0 +1,7 @@
+/// Throws a fireball down at the target
+/datum/smite/fireball
+ name = "Fireball"
+
+/datum/smite/fireball/effect(client/user, mob/living/target)
+ . = ..()
+ new /obj/effect/temp_visual/target(get_turf(target))
diff --git a/code/modules/admin/smites/gib.dm b/code/modules/admin/smites/gib.dm
new file mode 100644
index 00000000000..471479e7645
--- /dev/null
+++ b/code/modules/admin/smites/gib.dm
@@ -0,0 +1,7 @@
+/// Gibs the target
+/datum/smite/gib
+ name = "Gib"
+
+/datum/smite/gib/effect(client/user, mob/living/target)
+ . = ..()
+ target.gib(/* no_brain = */ FALSE)
diff --git a/code/modules/admin/smites/immerse.dm b/code/modules/admin/smites/immerse.dm
new file mode 100644
index 00000000000..84f5aeb6b9b
--- /dev/null
+++ b/code/modules/admin/smites/immerse.dm
@@ -0,0 +1,7 @@
+/// "Fully immerses" the player, making them manually breathe and blink
+/datum/smite/immerse
+ name = "Fully Immerse"
+
+/datum/smite/immerse/effect(client/user, mob/living/target)
+ . = ..()
+ immerse_player(target)
diff --git a/code/modules/admin/smites/knot_shoes.dm b/code/modules/admin/smites/knot_shoes.dm
new file mode 100644
index 00000000000..42d41e238e9
--- /dev/null
+++ b/code/modules/admin/smites/knot_shoes.dm
@@ -0,0 +1,15 @@
+/// Ties the target's shoes
+/datum/smite/knot_shoes
+ name = "Knot Shoes"
+
+/datum/smite/knot_shoes/effect(client/user, mob/living/target)
+ . = ..()
+ if (!iscarbon(target))
+ to_chat(user, "This must be used on a carbon mob.", confidential = TRUE)
+ return
+ var/mob/living/carbon/dude = target
+ var/obj/item/clothing/shoes/sick_kicks = dude.shoes
+ if (!sick_kicks?.can_be_tied)
+ to_chat(user, "[dude] does not have knottable shoes!", confidential = TRUE)
+ return
+ sick_kicks.adjust_laces(SHOES_KNOTTED)
diff --git a/code/modules/admin/smites/lightning.dm b/code/modules/admin/smites/lightning.dm
new file mode 100644
index 00000000000..3148ae7d4f5
--- /dev/null
+++ b/code/modules/admin/smites/lightning.dm
@@ -0,0 +1,19 @@
+#define LIGHTNING_BOLT_DAMAGE 75
+#define LIGHTNING_BOLT_ELECTROCUTION_ANIMATION_LENGTH 40
+
+/// Strikes the target with a lightning bolt
+/datum/smite/lightning
+ name = "Lightning bolt"
+
+/datum/smite/lightning/effect(client/user, mob/living/target)
+ . = ..()
+ var/turf/lightning_source = get_step(get_step(target, NORTH), NORTH)
+ lightning_source.Beam(target, icon_state="lightning[rand(1,12)]", time = 5)
+ target.adjustFireLoss(LIGHTNING_BOLT_DAMAGE)
+ if(ishuman(target))
+ var/mob/living/carbon/human/human_target = target
+ human_target.electrocution_animation(LIGHTNING_BOLT_ELECTROCUTION_ANIMATION_LENGTH)
+ to_chat(target, "The gods have punished you for your sins!", confidential = TRUE)
+
+#undef LIGHTNING_BOLT_DAMAGE
+#undef LIGHTNING_BOLT_ELECTROCUTION_ANIMATION_LENGTH
diff --git a/code/modules/admin/smites/nugget.dm b/code/modules/admin/smites/nugget.dm
new file mode 100644
index 00000000000..77f92536ff5
--- /dev/null
+++ b/code/modules/admin/smites/nugget.dm
@@ -0,0 +1,21 @@
+/// Rips off all the limbs of the target
+/datum/smite/nugget
+ name = "Nugget"
+
+/datum/smite/nugget/effect(client/user, mob/living/target)
+ . = ..()
+
+ if (!iscarbon(target))
+ to_chat(user, "This must be used on a carbon mob.", confidential = TRUE)
+ return
+
+ var/mob/living/carbon/carbon_target = target
+ var/timer = 2 SECONDS
+ for (var/_limb in carbon_target.bodyparts)
+ var/obj/item/bodypart/limb = _limb
+ if (limb.body_part == HEAD || limb.body_part == CHEST)
+ continue
+ addtimer(CALLBACK(limb, /obj/item/bodypart/.proc/dismember), timer)
+ addtimer(CALLBACK(GLOBAL_PROC, .proc/playsound, carbon_target, 'sound/effects/cartoon_pop.ogg', 70), timer)
+ addtimer(CALLBACK(carbon_target, /mob/living/.proc/spin, 4, 1), timer - 0.4 SECONDS)
+ timer += 2 SECONDS
diff --git a/code/modules/admin/smites/puzzle.dm b/code/modules/admin/smites/puzzle.dm
new file mode 100644
index 00000000000..ffaee144120
--- /dev/null
+++ b/code/modules/admin/smites/puzzle.dm
@@ -0,0 +1,8 @@
+/// Turns the user into a sliding puzzle
+/datum/smite/puzzle
+ name = "Puzzle"
+
+/datum/smite/puzzle/effect(client/user, mob/living/target)
+ . = ..()
+ if(!puzzle_imprison(target))
+ to_chat(user, "Imprisonment failed!", confidential = TRUE)
diff --git a/code/modules/admin/smites/rod.dm b/code/modules/admin/smites/rod.dm
new file mode 100644
index 00000000000..6c4b50b2688
--- /dev/null
+++ b/code/modules/admin/smites/rod.dm
@@ -0,0 +1,11 @@
+/// Throw an immovable rod at the target
+/datum/smite/rod
+ name = "Immovable Rod"
+
+/datum/smite/rod/effect(client/user, mob/living/target)
+ . = ..()
+ var/turf/target_turf = get_turf(target)
+ var/startside = pick(GLOB.cardinals)
+ var/turf/start_turf = spaceDebrisStartLoc(startside, target_turf.z)
+ var/turf/end_turf = spaceDebrisFinishLoc(startside, target_turf.z)
+ new /obj/effect/immovablerod(start_turf, end_turf, target)
diff --git a/code/modules/admin/smites/scarify.dm b/code/modules/admin/smites/scarify.dm
new file mode 100644
index 00000000000..d2d445f23e3
--- /dev/null
+++ b/code/modules/admin/smites/scarify.dm
@@ -0,0 +1,12 @@
+/// Gives the target fake scars
+/datum/smite/scarify
+ name = "Scarify"
+
+/datum/smite/scarify/effect(client/user, mob/living/target)
+ . = ..()
+ if(!iscarbon(target))
+ to_chat(user, "This must be used on a carbon mob.", confidential = TRUE)
+ return
+ var/mob/living/carbon/dude = target
+ dude.generate_fake_scars(rand(1, 4))
+ to_chat(dude, "You feel your body grow jaded and torn...")
diff --git a/code/modules/admin/smites/smite.dm b/code/modules/admin/smites/smite.dm
new file mode 100644
index 00000000000..b767f47f8fe
--- /dev/null
+++ b/code/modules/admin/smites/smite.dm
@@ -0,0 +1,17 @@
+/// A smite, used by admins to punish players, or for their own amusement
+/datum/smite
+ /// The name of the smite, shown in the menu
+ var/name
+
+ /// Should this smite write to logs?
+ var/should_log = TRUE
+
+/// Called once after either choosing the option to smite a player, or when selected in smite build mode.
+/// Use this to prompt the user configuration options.
+/// Return FALSE if the smite should not be used.
+/datum/smite/proc/configure(client/user)
+
+/// The effect of the smite, make sure to call this in your own smites
+/datum/smite/proc/effect(client/user, mob/living/target)
+ if (should_log)
+ user.punish_log(target, name)
diff --git a/code/modules/admin/smites/supply_pod.dm b/code/modules/admin/smites/supply_pod.dm
new file mode 100644
index 00000000000..187507e9bb4
--- /dev/null
+++ b/code/modules/admin/smites/supply_pod.dm
@@ -0,0 +1,21 @@
+#define SUPPLY_POD_FIRE_RANGE
+
+/// Throws a supply pod at the target, with no item inside
+/datum/smite/supply_pod
+ name = "Supply Pod"
+
+ // punish_log() is handled by the centcom_podlauncher datum
+ should_log = FALSE
+
+/datum/smite/supply_pod/effect(client/user, mob/living/target)
+ . = ..()
+ var/datum/centcom_podlauncher/plaunch = new(user)
+ plaunch.specificTarget = target
+ plaunch.launchChoice = 0
+ plaunch.damageChoice = 1
+ plaunch.explosionChoice = 1
+ plaunch.temp_pod.damage = 40 // bring the mother fuckin ruckus
+ plaunch.temp_pod.explosionSize = list(0, 0, 0, SUPPLY_POD_FIRE_RANGE)
+ plaunch.temp_pod.effectStun = TRUE
+
+#undef SUPPLY_POD_FIRE_RANGE
diff --git a/code/modules/admin/smites/supply_pod_quick.dm b/code/modules/admin/smites/supply_pod_quick.dm
new file mode 100644
index 00000000000..0af70e715f9
--- /dev/null
+++ b/code/modules/admin/smites/supply_pod_quick.dm
@@ -0,0 +1,49 @@
+#define SUPPLY_POD_QUICK_DAMAGE 40
+#define SUPPLY_POD_QUICK_FIRE_RANGE 2
+
+/// Quickly throws a supply pod at the target, optionally with an item
+/datum/smite/supply_pod_quick
+ name = "Supply Pod (Quick)"
+
+ /// What is sent down with the pod
+ var/target_path
+
+/datum/smite/supply_pod_quick/configure(client/user)
+ var/attempted_target_path = input(
+ user,
+ "Enter typepath of an atom you'd like to send with the pod (type \"empty\" to send an empty pod):",
+ "Typepath",
+ "/obj/item/food/grown/harebell",
+ ) as null|text
+
+ if (isnull(attempted_target_path)) //The user pressed "Cancel"
+ return FALSE
+
+ if (attempted_target_path == "empty")
+ target_path = null
+ return
+
+ // if you didn't type empty, we want to load the pod with a delivery
+ var/delivery = text2path(attempted_target_path)
+ if(!ispath(delivery))
+ delivery = pick_closest_path(attempted_target_path)
+ if(!delivery)
+ alert(user, "ERROR: Incorrect / improper path given.")
+ return FALSE
+ target_path = delivery
+
+/datum/smite/supply_pod_quick/effect(client/user, mob/living/target)
+ . = ..()
+
+ var/obj/structure/closet/supplypod/centcompod/pod = new
+ pod.damage = SUPPLY_POD_QUICK_DAMAGE
+ pod.explosionSize = list(0, 0, 0, SUPPLY_POD_QUICK_FIRE_RANGE)
+ pod.effectStun = TRUE
+
+ if (!isnull(target_path))
+ new target_path(pod)
+
+ new /obj/effect/pod_landingzone(get_turf(target), pod)
+
+#undef SUPPLY_POD_QUICK_DAMAGE
+#undef SUPPLY_POD_QUICK_FIRE_RANGE
diff --git a/code/modules/admin/verbs/bluespacearty.dm b/code/modules/admin/verbs/bluespacearty.dm
deleted file mode 100644
index b257581e796..00000000000
--- a/code/modules/admin/verbs/bluespacearty.dm
+++ /dev/null
@@ -1,26 +0,0 @@
-/client/proc/bluespace_artillery(mob/M in GLOB.mob_list)
- if(!holder || !check_rights(R_FUN))
- return
-
- var/mob/living/target = M
-
- if(!isliving(target))
- to_chat(usr, "This can only be used on instances of type /mob/living", confidential = TRUE)
- return
-
- explosion(target.loc, 0, 0, 0, 0)
-
- var/turf/open/floor/T = get_turf(target)
- if(istype(T))
- if(prob(80))
- T.break_tile_to_plating()
- else
- T.break_tile()
-
- if(target.health <= 1)
- target.gib(1, 1)
- else
- target.adjustBruteLoss(min(99,(target.health - 1)))
- target.Paralyze(400)
- target.stuttering = 20
-
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index 2a89c6c2342..1df904a4064 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -1117,232 +1117,18 @@ Traitors and the like can also be revived with the previous role mostly intact.
if(!check_rights(R_ADMIN) || !check_rights(R_FUN))
return
- var/list/punishment_list = list(ADMIN_PUNISHMENT_LIGHTNING,
- ADMIN_PUNISHMENT_BRAINDAMAGE,
- ADMIN_PUNISHMENT_GIB,
- ADMIN_PUNISHMENT_BSA,
- ADMIN_PUNISHMENT_FIREBALL,
- ADMIN_PUNISHMENT_ROD,
- ADMIN_PUNISHMENT_SUPPLYPOD_QUICK,
- ADMIN_PUNISHMENT_SUPPLYPOD,
- ADMIN_PUNISHMENT_MAZING,
- ADMIN_PUNISHMENT_IMMERSE,
- ADMIN_PUNISHMENT_FAT,
- ADMIN_PUNISHMENT_FAKEBWOINK,
- ADMIN_PUNISHMENT_NUGGET,
- ADMIN_PUNISHMENT_CRACK,
- ADMIN_PUNISHMENT_BLEED,
- ADMIN_PUNISHMENT_PERFORATE,
- ADMIN_PUNISHMENT_SCARIFY,
- ADMIN_PUNISHMENT_SHOES,
- ADMIN_PUNISHMENT_DOCK,
- ADMIN_PUNISHMENT_BREAD,
- ADMIN_PUNISHMENT_BADLUCK
- )
- var/punishment = input("Choose a punishment", "DIVINE SMITING") as null|anything in sortList(punishment_list)
+ var/punishment = input("Choose a punishment", "DIVINE SMITING") as null|anything in GLOB.smites
if(QDELETED(target) || !punishment)
return
- switch(punishment)
- if(ADMIN_PUNISHMENT_LIGHTNING)
- var/turf/T = get_step(get_step(target, NORTH), NORTH)
- T.Beam(target, icon_state="lightning[rand(1,12)]", time = 5)
- target.adjustFireLoss(75)
- if(ishuman(target))
- var/mob/living/carbon/human/H = target
- H.electrocution_animation(40)
- to_chat(target, "The gods have punished you for your sins!", confidential = TRUE)
- if(ADMIN_PUNISHMENT_BRAINDAMAGE)
- target.adjustOrganLoss(ORGAN_SLOT_BRAIN, 199, 199)
- if(ADMIN_PUNISHMENT_GIB)
- target.gib(FALSE)
- if(ADMIN_PUNISHMENT_BSA)
- bluespace_artillery(target)
- if(ADMIN_PUNISHMENT_FIREBALL)
- new /obj/effect/temp_visual/target(get_turf(target))
- if(ADMIN_PUNISHMENT_ROD)
- var/turf/T = get_turf(target)
- var/startside = pick(GLOB.cardinals)
- var/turf/startT = spaceDebrisStartLoc(startside, T.z)
- var/turf/endT = spaceDebrisFinishLoc(startside, T.z)
- new /obj/effect/immovablerod(startT, endT,target)
- if(ADMIN_PUNISHMENT_SUPPLYPOD_QUICK)
- var/target_path = input(usr,"Enter typepath of an atom you'd like to send with the pod (type \"empty\" to send an empty pod):" ,"Typepath","/obj/item/food/grown/harebell") as null|text
- var/obj/structure/closet/supplypod/centcompod/pod = new()
- pod.damage = 40
- pod.explosionSize = list(0,0,0,2)
- pod.effectStun = TRUE
- if (isnull(target_path)) //The user pressed "Cancel"
- return
- if (target_path != "empty")//if you didn't type empty, we want to load the pod with a delivery
- var/delivery = text2path(target_path)
- if(!ispath(delivery))
- delivery = pick_closest_path(target_path)
- if(!delivery)
- alert("ERROR: Incorrect / improper path given.")
- return
- new delivery(pod)
- new /obj/effect/pod_landingzone(get_turf(target), pod)
- if(ADMIN_PUNISHMENT_SUPPLYPOD)
- var/datum/centcom_podlauncher/plaunch = new(usr)
- if(!holder)
- return
- plaunch.specificTarget = target
- plaunch.launchChoice = 0
- plaunch.damageChoice = 1
- plaunch.explosionChoice = 1
- plaunch.temp_pod.damage = 40//bring the mother fuckin ruckus
- plaunch.temp_pod.explosionSize = list(0,0,0,2)
- plaunch.temp_pod.effectStun = TRUE
- return //We return here because punish_log() is handled by the centcom_podlauncher datum
- if(ADMIN_PUNISHMENT_MAZING)
- if(!puzzle_imprison(target))
- to_chat(usr,"Imprisonment failed!", confidential = TRUE)
- return
- if(ADMIN_PUNISHMENT_IMMERSE)
- immerse_player(target)
- if(ADMIN_PUNISHMENT_FAT)
- target.set_nutrition(NUTRITION_LEVEL_FAT*2)
- if(ADMIN_PUNISHMENT_FAKEBWOINK)
- SEND_SOUND(target, 'sound/effects/adminhelp.ogg')
- if(ADMIN_PUNISHMENT_NUGGET)
- if(!iscarbon(target))
- to_chat(usr,"This must be used on a carbon mob.", confidential = TRUE)
- return
- var/mob/living/carbon/C = target
- var/timer = 2 SECONDS
- for(var/obj/item/bodypart/thing in C.bodyparts)
- if(thing.body_part == HEAD || thing.body_part == CHEST)
- continue
- addtimer(CALLBACK(thing, /obj/item/bodypart/.proc/dismember), timer)
- addtimer(CALLBACK(GLOBAL_PROC, .proc/playsound, C, 'sound/effects/cartoon_pop.ogg', 70), timer)
- addtimer(CALLBACK(C, /mob/living/.proc/spin, 4, 1), timer - 0.4 SECONDS)
- timer += 2 SECONDS
- if(ADMIN_PUNISHMENT_CRACK)
- if(!iscarbon(target))
- to_chat(usr,"This must be used on a carbon mob.", confidential = TRUE)
- return
- var/mob/living/carbon/C = target
- for(var/i in C.bodyparts)
- var/obj/item/bodypart/squish_part = i
- var/type_wound = pick(list(/datum/wound/blunt/critical, /datum/wound/blunt/severe, /datum/wound/blunt/critical, /datum/wound/blunt/severe, /datum/wound/blunt/moderate))
- squish_part.force_wound_upwards(type_wound, smited=TRUE)
- if(ADMIN_PUNISHMENT_BLEED)
- if(!iscarbon(target))
- to_chat(usr,"This must be used on a carbon mob.", confidential = TRUE)
- return
- var/mob/living/carbon/C = target
- for(var/i in C.bodyparts)
- var/obj/item/bodypart/slice_part = i
- var/type_wound = pick(list(/datum/wound/slash/severe, /datum/wound/slash/moderate))
- slice_part.force_wound_upwards(type_wound, smited=TRUE)
- type_wound = pick(list(/datum/wound/slash/critical, /datum/wound/slash/severe, /datum/wound/slash/moderate))
- slice_part.force_wound_upwards(type_wound, smited=TRUE)
- type_wound = pick(list(/datum/wound/slash/critical, /datum/wound/slash/severe))
- slice_part.force_wound_upwards(type_wound, smited=TRUE)
- if(ADMIN_PUNISHMENT_PERFORATE)
- if(!iscarbon(target))
- to_chat(usr,"This must be used on a carbon mob.", confidential = TRUE)
- return
-
- var/list/how_fucked_is_this_dude = list("A little", "A lot", "So fucking much", "FUCK THIS DUDE")
- var/hatred = input("How much do you hate this guy?") in how_fucked_is_this_dude
- var/repetitions
- var/shots_per_limb_per_rep = 2
- var/damage
- switch(hatred)
- if("A little")
- repetitions = 1
- damage = 5
- if("A lot")
- repetitions = 2
- damage = 8
- if("So fucking much")
- repetitions = 3
- damage = 10
- if("FUCK THIS DUDE")
- repetitions = 4
- damage = 10
-
- var/mob/living/carbon/dude = target
- var/list/open_adj_turfs = get_adjacent_open_turfs(dude)
- var/list/wound_bonuses = list(15, 70, 110, 250)
-
- var/delay_per_shot = 1
- var/delay_counter = 1
-
- dude.Immobilize(5 SECONDS)
- for(var/wound_bonus_rep in 1 to repetitions)
- for(var/i in dude.bodyparts)
- var/obj/item/bodypart/slice_part = i
- var/shots_this_limb = 0
- for(var/t in shuffle(open_adj_turfs))
- var/turf/iter_turf = t
- addtimer(CALLBACK(GLOBAL_PROC, .proc/firing_squad, dude, iter_turf, slice_part.body_zone, wound_bonuses[wound_bonus_rep], damage), delay_counter)
- delay_counter += delay_per_shot
- shots_this_limb++
- if(shots_this_limb > shots_per_limb_per_rep)
- break
-
- if(ADMIN_PUNISHMENT_SCARIFY)
- if(!iscarbon(target))
- to_chat(usr,"This must be used on a carbon mob.", confidential = TRUE)
- return
- var/mob/living/carbon/dude = target
- dude.generate_fake_scars(rand(1, 4))
- to_chat(dude, "You feel your body grow jaded and torn...")
- if(ADMIN_PUNISHMENT_SHOES)
- if(!iscarbon(target))
- to_chat(usr,"This must be used on a carbon mob.", confidential = TRUE)
- return
- var/mob/living/carbon/dude = target
- var/obj/item/clothing/shoes/sick_kicks = dude.shoes
- if(!sick_kicks?.can_be_tied)
- to_chat(usr,"[dude] does not have knottable shoes!", confidential = TRUE)
- return
- sick_kicks.adjust_laces(SHOES_KNOTTED)
- if(ADMIN_PUNISHMENT_DOCK)
- if(!iscarbon(target))
- to_chat(usr,"This must be used on a carbon mob.", confidential = TRUE)
- return
- var/mob/living/carbon/dude = target
- var/obj/item/card/id/card = dude.get_idcard(TRUE)
- if(!card)
- to_chat(usr,"[dude] does not have an ID card on!", confidential = TRUE)
- return
- if(!card.registered_account)
- to_chat(usr,"[dude] does not have an ID card with an account!", confidential = TRUE)
- return
- if(card.registered_account.account_balance == 0)
- to_chat(usr, "ID Card lacks any funds. No pay to dock.")
- return
- var/new_cost = input("How much pay are we docking? Current balance: [card.registered_account.account_balance] credits.","BUDGET CUTS") as num|null
- if(!new_cost)
- return
- if(!(card.registered_account.has_money(new_cost)))
- to_chat(usr, "ID Card lacked funds. Emptying account.")
- card.registered_account.bank_card_talk("[new_cost] credits deducted from your account based on performance review.")
- card.registered_account.account_balance = 0
- else
- card.registered_account.account_balance = card.registered_account.account_balance - new_cost
- card.registered_account.bank_card_talk("[new_cost] credits deducted from your account based on performance review.")
- SEND_SOUND(target, 'sound/machines/buzz-sigh.ogg')
- if(ADMIN_PUNISHMENT_BREAD)
- var/mutable_appearance/bread_appearance = mutable_appearance('icons/obj/food/burgerbread.dmi',"bread")
- var/mutable_appearance/transform_scanline = mutable_appearance('icons/effects/effects.dmi',"transform_effect")
- target.transformation_animation(bread_appearance,time= 5 SECONDS,transform_overlay=transform_scanline,reset_after=TRUE)
- addtimer(CALLBACK(GLOBAL_PROC, .proc/breadify, target), 5 SECONDS)
- if(ADMIN_PUNISHMENT_BADLUCK)
- if(!isliving(target))
- to_chat(usr, "This must be used on a /mob/living type of mob.", confidential = TRUE)
- return
- var/silent = alert("Do you want to apply the omen with a player notification?", "Notify Player?", "Notify", "Silent") == "Silent"
- var/permanent = alert("Would you like this to be permanent or removed automatically after the first accident?", "Permanent?", "Permanent", "Temporary") == "Permanent"
- target.AddComponent(/datum/component/omen, silent, null, permanent)
-
- punish_log(target, punishment)
+ var/smite_path = GLOB.smites[punishment]
+ var/datum/smite/smite = new smite_path
+ var/configuration_success = smite.configure(usr)
+ if (configuration_success == FALSE)
+ return
+ smite.effect(src, target)
/proc/breadify(atom/movable/target)
var/obj/item/food/bread/plain/bread = new(get_turf(target))
@@ -1374,10 +1160,10 @@ Traitors and the like can also be revived with the previous role mostly intact.
divine_wrath.fire()
/client/proc/punish_log(whom, punishment)
- var/msg = "[key_name_admin(usr)] punished [key_name_admin(whom)] with [punishment]."
+ var/msg = "[key_name_admin(src)] punished [key_name_admin(whom)] with [punishment]."
message_admins(msg)
admin_ticket_log(whom, msg)
- log_admin("[key_name(usr)] punished [key_name(whom)] with [punishment].")
+ log_admin("[key_name(src)] punished [key_name(whom)] with [punishment].")
/client/proc/trigger_centcom_recall()
if(!check_rights(R_ADMIN))
diff --git a/code/modules/buildmode/submodes/smite.dm b/code/modules/buildmode/submodes/smite.dm
new file mode 100644
index 00000000000..5f5050b41f3
--- /dev/null
+++ b/code/modules/buildmode/submodes/smite.dm
@@ -0,0 +1,40 @@
+/datum/buildmode_mode/smite
+ key = "smite"
+ var/datum/smite/selected_smite
+
+/datum/buildmode_mode/smite/Destroy()
+ selected_smite = null
+ return ..()
+
+/datum/buildmode_mode/smite/show_help(client/user)
+ to_chat(user, "***********************************************************\n\
+ Right Mouse Button on buildmode button = Select smite to use.\n\
+ Left Mouse Button on mob/living = Smite the mob.\n\
+ ***********************************************************")
+
+/datum/buildmode_mode/smite/change_settings(client/user)
+ var/punishment = input(user, "Choose a punishment", "DIVINE SMITING") as null|anything in GLOB.smites
+ var/smite_path = GLOB.smites[punishment]
+ var/datum/smite/picking_smite = new smite_path
+ var/configuration_success = picking_smite.configure(user)
+ if (configuration_success == FALSE)
+ return
+ selected_smite = picking_smite
+
+/datum/buildmode_mode/smite/handle_click(client/user, params_string, object)
+ var/list/params = params2list(params_string)
+
+ if (!check_rights(R_ADMIN | R_FUN))
+ return
+
+ if (!params.Find("left"))
+ return
+
+ if (!isliving(object))
+ return
+
+ if (selected_smite == null)
+ to_chat(user, "No smite selected.")
+ return
+
+ selected_smite.effect(user, object)
diff --git a/icons/misc/buildmode.dmi b/icons/misc/buildmode.dmi
index fb51ad68116039b29ec6659190f15789f8b805c9..edb635e7f2e7875b729d01de7bafebfacb61d69d 100644
GIT binary patch
delta 2811
zcmViCMkC$14|efVE}Z_7z)$45SuJ6^a%F^4iv7sKT4uLTeL-I#6fV(N`l^8
zo8@nx<#K`DTUW40GKeq+PZ=O>C0VRObj*KEZ^uZ4+gyd*UW(yjrR8^|<#v(fXmpNWV?eBXnZZ`~|U!CHKQloPTxWnd{fR{u*0gt+ely%utOyH^GI`bh6
zIN;IQUaHaKxH(nDetrGTe>txu$PFD1`v3q3=}AOERCt{2n}wg-HWbG%tIw~4wqu44
zI%eh@b35u?$CxoQuiLI;W`^vpyMO+fOKib=(vxJzimiS=QI4Entk~Z?!TU)!Gt={y
zneO#nyrtK*KDt2n^_LZNst+%)w<>d#RlQLK-Rh$YoSn_tGCNyVfOP=B9y;HzLs5u6
zxr3!R;%iVv&KvYb3K?$g5Y?%W`_{-{eR%3##+D50_OX{jtS!TPl+Ih5ct_lHv}_dFp*EBL(pOf
zQl|vXo~k{9#*7A(Ze(>d<%piOd3E*H@((tv>naB2yIY@Nk+{2JL?+f<>N|t=(XBZY
zG>^Ks*8+Oy7ZB7t{s^c3;G}grk}&>URNWZrtBf>gB;ozJczR~pi+@!{8aar~8ur}2hLRAP({lSLTN4MtC)E^A#tNr@w
zsXsXN2SYu1*LvMuACuAP2wfH+-i+IyYdRaLq|?nHMWORSk3ZKAVK5om-i*s#YK35K
zG6ch6_4spb#eXPaC=c2sSUOgqeJt~Q?2E|>C5l0(KNpRV_8U~vR%MkywqmD0hhn5`
z>w*K(McXf5QXYVj7mMfI1Q`>%PCuxrDi;OopgslsFjoP9;IlsgYZ;)2vbs1cP)?E^)V~y03u-$#I4vS7>r^04zjtk=gTI}
z5ft}V-krLOm?MeoYjHEC=Yw1&z^$CD)q(1Y%IQqhu~^5TgP`S1umnNfZL6~x3J!mQ
zb@x$EOn)cM7mYM&%&4sip*T#3pf#li*#^=eXiccaQ@vEOIy)s9F-$0{^XOB5aOw|s
zZoh>p=vq(zBObnBSLgM&(@4DUKlKNj>UbmpGbKo6Br*0@ar;{2bQu3=XST!cAG9-n
zZQjV_EY^B(Rf2RnVdV_k{xWxEM4M6P%DUkQnt#(V%Q+~V1@S{001*jVz6~X)uN#m*
zVi82fLUe*6>E;RId8dt{z!6-~Wn0M3<
z0s|2LcPsAw#&x%v6Ev?6fV5(~24MSxdXUlb7|w$FQT#UgLHh#De$eZ%%36?IuN9-Q
zN`HEkv>$9lFqhAcO%RV&!m2ggtuR47h_EA#hoCh6FeHPZTI@nbA{vXuK1p>1EEV;?jZLx_<2;fPUb<8lP`QiDy93Bs7}6(TI%?z+3*y!~Lf6;>du
zJ$|`kxN<+pB8=?+>3&j;xpfIrhP4PyPJd_qzgY51FRMck+apwk;M5;%qNi^jb#1Q&
z?Y9aQfVTqw`4J4xjz7Yk!5{SMiTH!N4Ucwb^+f!^J=Uzv*X_yaiL5m3mF>Mxw(ow7
zp2lkD{s$a*(7}fsdRTtArPtVb1acy$roNE~9~t{srR^#5otJEe+MzgNI+a_#iU$yVNhd=;BK*
zotwMt@++>KyXtD5AP`^9Lr@z~b!OaVx@UX>*Iawu^*7vj)5eXPHr;&7T9%--;2nw~
zJ8LVmHISi7+%dH4h#{C|x88R9hJVfT^9x%R?zrxKNqmtKD5)z|v3Z+YX*w^)K%)`*~rFCf3F63nu<-+$Ts?$-JD
z-v3~6+lPD%;=OP635E%KV{rKAM<0Lk>1UsR@#R-vf8!wNC)7|6wc=s<_Pg(Y`0=No
zfBDrozn!r-&<_>|YkvFvk3awV`=8=qFc`E=P`fic!Ls~Y{#sgIUS4XIpfwhI{l~%f
zQ5ht(cZ-%GzyF4hA;Z=P)?m{IE8C05AXS3d(&CP7I~JFcnNiDs0+OQ{3>mJh4AR*@
zSPtNHAx7ybWOZtmE=Mw~r$A6Gi?|4O?GK_DBm}4aU=+QvKR9Xo?SIHr)l7C-D8v8&
N002ovPDHLkV1kceQNaKJ
delta 1757
zcmV<31|s>W7Mc!_7!3pi0001a;d&kb001G8B_bFFG80{*9Jc@f00DGTPE!Ct=GbNc
z0085YIRYSmQE3^vuv9@6LwSYDi7)Y~b}qFqLA*Vo3<#8<+S~o4@6-LUyi^~%&;3Ca
zUwb(~h)Av4s?F*}iX$4fV|aU&!U8syuOYH#7|TW3qcaOB42C%7cPZ+iJzVPb17*Q=
zH*$SP3P;pUIGzlAmpWpVWok3%wgQtSgF(&`*#}h4YYIWz3|&-j#p84`d^Fd
z4Fl+BrUPE@%KB=F|!u*
zED?*O000HJNkl91wkI#%xV<{x#uy{o6kCv`mU>gn+;Kj87atEb1ixj4Q&@9OFC
z;ro(~|Gzz>wqEP{^-5a+&w6^iFF)byGim^>>({jgH_rx02g0Q|@DrE(iAmS5Yi(J0
z)O&=+ogn0MXLCRpW7tP%<$OPg(1|i*2yH%p-wz=)S-NqA0OJVpOrW3HoFufYy*}T^
zM8tpl2@T-kC}#XL6B=XieCJ$2SQD)@T?J6{v;4ZZ*5diz`+~3)vlbx^@gd>nsP4va
z0s2`^5VrD=S0{Ip@E{NQ@Z$*i{bLAoOzkp+5Z6I|znjMp3ZgAmY!egf65$-z8y8o9
zY^q|sT69;rO5?n-psL=T7^-g=3Vj(9EZq0>Z?Gbx&C+l?_wt9M{9*QYeVW$i&6zXR
z{;o?0u{X1(+TZnPocfSf8!UL%0#&|9So*sjjkI}n3N_EsPxxOZRQ|3*)9PX&5r&E!
z{sclBYl`xB9h$>Qh+LMef
zW$o|US^y=6t31^HuB{8ejVd_R{;sVHz#T&6@7lTml$k=QiH}S;&zk5!b4up9jIRcM
zOv4nz!r!&^lJipXkRDccQrh3O^@f+2oA?dF
z6Gcdfw2|4)Lt-_#h9FHmWLH3h#Wx7P0p>YXy?WFznl|rCybZXBl?XZNMT~Rbo2#Ci
z5lLcVfCIEkxDn&jndiD}Lj-K9yK6#<8afymRkR7V%!0JD;++)Q6XRfiCx$B`HiUZb
zm%Ab#kcVR87d6V|-cILXcmv=&ioKr~&mFyubG&=&?IbyJTjkzQb_H>-=W+KIOlsdu
zp%|8g;w%b6z|Xz7+VCWa-*B&weM)lxioNU-sZX
z#>7zRmp>d}=js#g>fhjh4?mY5aA8oY_x~;a5N|ULn<>)E%6%C05uX0Ku=*7J!{h{<
zp`u~B_QzX8%Db}X@d}aSosvdv=hi;{pP@Iksa%Tu2I|W2MK?sW?
zjC~mz=o~|U{uow-)MAbIJFite3c4ob8>m<7ay7~}-J1oU@^HC-k%va^&BnX4+-N&;
zUu{B=BX*K#Q;^&j!TQDS5{9o51{xivh-iWrF$H5%
z769!cTqdmYu$6azOpHc+Z#}H?u$6Z_>R`<7ik%X%(+^T!+U?aScjxGL7lzE`?+I~e
zSA^@{%O74bOT+El>V$u_ZZ;t6
z#2ZX#S4`LalMX^peH`yMSLE2y#I?!y5<+Yx?GT#Tv)y%ndxYTEgw*t~+k^|(?0rI4
z5y~Nic