From 973542bc1d8366350faabef1a4534acae7b27306 Mon Sep 17 00:00:00 2001
From: Contrabang <91113370+Contrabang@users.noreply.github.com>
Date: Mon, 16 Oct 2023 14:01:35 -0400
Subject: [PATCH] Adds a magic monkey to the wizard's den (#22677)
* merlin time
* bam
* boom
* oops
* Apply suggestions from code review
Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com>
* autodoc
* Update code/modules/mob/living/carbon/human/npcs.dm
Co-authored-by: Henri215 <77684085+Henri215@users.noreply.github.com>
---------
Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com>
Co-authored-by: Henri215 <77684085+Henri215@users.noreply.github.com>
---
_maps/map_files/generic/centcomm.dmm | 38 ++++----
code/datums/spells/mind_transfer.dm | 2 +-
.../mob/living/carbon/human/human_mob.dm | 3 +-
code/modules/mob/living/carbon/human/npcs.dm | 88 +++++++++++++++++++
.../mob/living/carbon/human/species/monkey.dm | 8 +-
.../living/simple_animal/hostile/hostile.dm | 3 +-
6 files changed, 118 insertions(+), 24 deletions(-)
diff --git a/_maps/map_files/generic/centcomm.dmm b/_maps/map_files/generic/centcomm.dmm
index 2c6f94f0e0e..03c69d2aa3d 100644
--- a/_maps/map_files/generic/centcomm.dmm
+++ b/_maps/map_files/generic/centcomm.dmm
@@ -1433,10 +1433,6 @@
icon_state = "cmo"
},
/area/shuttle/escape)
-"fz" = (
-/mob/living/carbon/human/monkey,
-/turf/simulated/floor/wood,
-/area/wizard_station)
"fE" = (
/obj/structure/table,
/obj/item/storage/box/handcuffs,
@@ -3561,19 +3557,6 @@
},
/turf/simulated/floor/plasteel,
/area/admin)
-"mh" = (
-/obj/structure/rack,
-/obj/item/clothing/under/rank/centcom/captain{
- pixel_x = -3;
- pixel_y = 3
- },
-/obj/item/clothing/under/rank/centcom/officer,
-/obj/item/clothing/under/rank/procedure/representative{
- pixel_x = 3;
- pixel_y = -3
- },
-/turf/simulated/floor/plasteel,
-/area/admin)
"mi" = (
/obj/structure/rack,
/obj/item/clothing/suit/pirate_black{
@@ -7402,6 +7385,19 @@
},
/turf/simulated/floor/plasteel,
/area/tdome/arena_source)
+"Ar" = (
+/obj/structure/rack,
+/obj/item/clothing/under/rank/centcom/captain{
+ pixel_x = -3;
+ pixel_y = 3
+ },
+/obj/item/clothing/under/rank/centcom/officer,
+/obj/item/clothing/under/rank/procedure/representative{
+ pixel_x = 3;
+ pixel_y = -3
+ },
+/turf/simulated/floor/plasteel,
+/area/admin)
"As" = (
/obj/structure/table/wood,
/obj/item/reagent_containers/food/drinks/drinkingglass/devilskiss,
@@ -12581,6 +12577,10 @@
/obj/structure/filingcabinet/security,
/turf/simulated/floor/wood,
/area/centcom/control)
+"Sv" = (
+/mob/living/carbon/human/monkey/magic,
+/turf/simulated/floor/wood,
+/area/wizard_station)
"Sw" = (
/obj/structure/sign/poster/official/high_class_martini,
/turf/simulated/wall/indestructible/riveted,
@@ -61499,7 +61499,7 @@ BX
UJ
GN
BR
-fz
+Sv
ed
fx
GN
@@ -68522,7 +68522,7 @@ wk
lI
lY
mb
-mh
+Ar
mx
mU
nm
diff --git a/code/datums/spells/mind_transfer.dm b/code/datums/spells/mind_transfer.dm
index 6cc0e82d554..c8cd5aa2ab0 100644
--- a/code/datums/spells/mind_transfer.dm
+++ b/code/datums/spells/mind_transfer.dm
@@ -10,7 +10,7 @@
selection_activated_message = "You prepare to transfer your mind. Click on a target to cast the spell."
selection_deactivated_message = "You decide that your current form is good enough."
cooldown_min = 200 //100 deciseconds reduction per rank
- var/list/protected_roles = list("Wizard","Changeling","Cultist") //which roles are immune to the spell
+ var/list/protected_roles = list(SPECIAL_ROLE_WIZARD, SPECIAL_ROLE_CHANGELING, SPECIAL_ROLE_CULTIST) //which roles are immune to the spell
var/paralysis_amount_caster = 40 SECONDS //how much the caster is paralysed for after the spell
var/paralysis_amount_victim = 40 SECONDS //how much the victim is paralysed for after the spell
action_icon_state = "mindswap"
diff --git a/code/modules/mob/living/carbon/human/human_mob.dm b/code/modules/mob/living/carbon/human/human_mob.dm
index c72ca00faaf..7673794a69b 100644
--- a/code/modules/mob/living/carbon/human/human_mob.dm
+++ b/code/modules/mob/living/carbon/human/human_mob.dm
@@ -28,9 +28,10 @@
/mob/living/carbon/human/proc/mind_checks()
if(!mind)
- return
+ return FALSE
if(mind.miming)
qdel(GetComponent(/datum/component/footstep))
+ return TRUE
/**
* Sets up DNA and species.
diff --git a/code/modules/mob/living/carbon/human/npcs.dm b/code/modules/mob/living/carbon/human/npcs.dm
index e291abe72ce..bfb37b40a4a 100644
--- a/code/modules/mob/living/carbon/human/npcs.dm
+++ b/code/modules/mob/living/carbon/human/npcs.dm
@@ -21,3 +21,91 @@
real_name = name
resize = 0.8
update_transform()
+
+/mob/living/carbon/human/monkey/magic
+ /// Stores the timer ID of the timer that happens in Life() to prevent multiple of the same timer happening.
+ var/return_timer
+
+/mob/living/carbon/human/monkey/magic/Initialize(mapload)
+ . = ..()
+ var/headwear = pick(/obj/item/clothing/head/wizard, /obj/item/clothing/head/wizard/red, /obj/item/clothing/head/wizard/black)
+
+ name = pick(GLOB.wizard_first)
+ if(prob(20))
+ name = "[name] [pick(GLOB.wizard_second)]"
+ if(prob(0.1))
+ name = "MERLIN THE MAGNIFICENT"
+ headwear = /obj/item/clothing/head/wizard // he's gotta wear blue, ya know?
+ resize = 2
+ update_transform()
+ real_name = name
+
+ equip_to_slot_or_del(new headwear(src), SLOT_HUD_HEAD)
+ RegisterSignal(src, list(COMSIG_HUMAN_ATTACKED, COMSIG_HOSTILE_ATTACKINGTARGET), PROC_REF(ouch))
+
+ for(var/trait in list(TRAIT_RESISTHEAT, TRAIT_NOBREATH, TRAIT_RESISTCOLD, TRAIT_RESISTHIGHPRESSURE, TRAIT_RESISTLOWPRESSURE))
+ ADD_TRAIT(src, trait, MAGIC_TRAIT)
+
+/mob/living/carbon/human/monkey/magic/proc/ouch(mob/living/victim, mob/living/attacker)
+ SIGNAL_HANDLER
+ if(src == attacker)
+ return // I'm afraid you do not get a free blink
+ if(blink_away())
+ return COMPONENT_CANCEL_ATTACK_CHAIN // prevent people from attacking the monkey
+
+/mob/living/carbon/human/monkey/magic/proc/blink_away()
+ if(HAS_TRAIT(src, TRAIT_HANDS_BLOCKED))
+ return
+
+ // A monkey, with a blink spell? Preposterous
+ playsound(get_turf(src), 'sound/magic/blink.ogg', 50, TRUE)
+
+ var/list/turfs = list()
+ var/list/target_turfs = range(src, 5) - range(src, 1)
+ for(var/turf/T in target_turfs)
+ if(isspaceturf(T))
+ continue
+ if(is_blocked_turf(T))
+ continue
+ if(T.x > world.maxx - 5 || T.x < 5)
+ continue //putting them at the edge is dumb
+ if(T.y > world.maxy - 5 || T.y < 5)
+ continue
+ if(islava(T) || ischasm(T))
+ continue
+ turfs += T
+
+ var/turf/picked = pick(turfs)
+ if(!picked || !isturf(picked))
+ return
+
+ visible_message("[src] blinks away!", "Your instincts kick in, and you blink away!")
+ INVOKE_ASYNC(src, PROC_REF(after_the_attack), picked)
+
+ playsound(get_turf(src), 'sound/magic/blink.ogg', 50, TRUE)
+ return_timer = null
+ return TRUE
+
+/mob/living/carbon/human/monkey/magic/proc/after_the_attack(turf/picked)
+ forceMove(picked)
+
+ var/datum/effect_system/smoke_spread/smoke = new /datum/effect_system/smoke_spread()
+ smoke.set_up(1, FALSE, src)
+ smoke.start()
+
+/mob/living/carbon/human/monkey/magic/proc/i_want_to_go_home()
+ if(client || !istype(loc, /turf/simulated/floor/plating/asteroid))
+ return
+ blink_away()
+
+/mob/living/carbon/human/monkey/magic/Life(seconds, times_fired)
+ . = ..()
+ if(!client && !return_timer && prob(10) && istype(loc, /turf/simulated/floor/plating/asteroid))
+ return_timer = addtimer(CALLBACK(src, PROC_REF(i_want_to_go_home)), 5 MINUTES)
+ // I'm out out the wizard's den, I want to go back inside!
+
+/mob/living/carbon/human/monkey/magic/mind_checks()
+ if(!..())
+ return FALSE
+ mind.AddSpell(new /obj/effect/proc_holder/spell/turf_teleport/blink(null))
+ mind.special_role = SPECIAL_ROLE_WIZARD
diff --git a/code/modules/mob/living/carbon/human/species/monkey.dm b/code/modules/mob/living/carbon/human/species/monkey.dm
index d65bfb4fde7..0ab9e22ebef 100644
--- a/code/modules/mob/living/carbon/human/species/monkey.dm
+++ b/code/modules/mob/living/carbon/human/species/monkey.dm
@@ -48,10 +48,14 @@
/datum/species/monkey/handle_npc(mob/living/carbon/human/H)
if(H.stat != CONSCIOUS)
return
- if(prob(33) && (H.mobility_flags & MOBILITY_MOVE) && isturf(H.loc) && !H.pulledby) //won't move if being pulled
- step(H, pick(GLOB.cardinal))
if(prob(1))
H.emote(pick("scratch","jump","roll","tail"))
+ if(prob(33) && (H.mobility_flags & MOBILITY_MOVE) && isturf(H.loc) && !H.pulledby) //won't move if being pulled
+ var/dir_to_go = pick(GLOB.cardinal)
+ var/turf/to_go = get_step(H, dir_to_go)
+ if(islava(to_go) || ischasm(to_go))
+ return
+ step(H, dir_to_go)
/datum/species/monkey/get_random_name()
return "[lowertext(name)] ([rand(100,999)])"
diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm
index a429d1c2295..54685786b5f 100644
--- a/code/modules/mob/living/simple_animal/hostile/hostile.dm
+++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm
@@ -326,7 +326,8 @@
FindTarget()
/mob/living/simple_animal/hostile/proc/AttackingTarget()
- SEND_SIGNAL(src, COMSIG_HOSTILE_ATTACKINGTARGET, target)
+ if(SEND_SIGNAL(target, COMSIG_HOSTILE_ATTACKINGTARGET, src) & COMPONENT_CANCEL_ATTACK_CHAIN)
+ return FALSE
in_melee = TRUE
return target.attack_animal(src)