diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/_animal_harness.dm b/code/modules/mob/living/simple_mob/subtypes/animal/_animal_harness.dm
index 2833c3caf9..f60f8ed04f 100644
--- a/code/modules/mob/living/simple_mob/subtypes/animal/_animal_harness.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/_animal_harness.dm
@@ -1,11 +1,22 @@
-/obj/item/storage/internal/animal_harness
- abstract_type = /obj/item/storage/internal/animal_harness
+/obj/item/storage/animal_harness
+ abstract_type = /obj/item/storage/animal_harness
name = "animal harness"
color = COLOR_BEASTY_BROWN
max_w_class = ITEMSIZE_LARGE
max_storage_space = INVENTORY_STANDARD_SPACE
icon_state = "harness"
icon = 'icons/mob/drake_harness.dmi'
+ preserve_item = TRUE
+
+ // keys for animal_harness/attachable_type
+ var/const/ATTACHED_GPS = "gps"
+ var/const/ATTACHED_RADIO = "radio"
+ var/const/ATTACHED_ARMOR = "armor plate"
+ var/const/ATTACHED_LIGHT = "light"
+ var/const/ATTACHED_ID = "access card"
+
+ /// Contains valid types that the harness will attach to.
+ var/list/harnessable_types
/// Null, or a list of (/obj/item/foo = "item key"), or (/path = ("item key", proc/handler)).
var/list/attachable_types
@@ -20,10 +31,10 @@
var/other_attach_delay = 3 SECONDS
-/obj/item/storage/internal/animal_harness/Destroy()
+/obj/item/storage/animal_harness/Destroy()
var/mob/living/simple_mob/animal/wearer = loc
if (istype(wearer) && wearer.harness == src)
- wearer.harness = null
+ wearer.clear_harness()
for (var/key in attached_items)
var/obj/item/item = attached_items[key]
qdel(item)
@@ -32,7 +43,7 @@
return ..()
-/obj/item/storage/internal/animal_harness/Initialize()
+/obj/item/storage/animal_harness/Initialize()
. = ..()
if (!attachable_types)
return
@@ -43,11 +54,15 @@
CreateAttachments()
-/obj/item/storage/internal/animal_harness/LateInitializeName()
+/obj/item/storage/animal_harness/GetIdCard()
+ return attached_items[ATTACHED_ID]
+
+
+/obj/item/storage/animal_harness/LateInitializeName()
return
-/obj/item/storage/internal/animal_harness/attackby(obj/item/item, mob/living/user, silent)
+/obj/item/storage/animal_harness/attackby(obj/item/item, mob/living/user, silent)
. = TRUE
if (user == loc) // Animals can only shove stuff in their storage.
return ..()
@@ -78,7 +93,7 @@
return ..()
-/obj/item/storage/internal/animal_harness/proc/GetAttachedKeys()
+/obj/item/storage/animal_harness/proc/GetAttachedKeys()
var/list/obj/item/result = list()
for (var/key in attached_items)
if (attached_items[key])
@@ -86,7 +101,7 @@
return result
-/obj/item/storage/internal/animal_harness/proc/GetAttachedItems()
+/obj/item/storage/animal_harness/proc/GetAttachedItems()
var/list/obj/item/result = list()
for (var/key in attached_items)
var/obj/item/item = attached_items[key]
@@ -95,7 +110,7 @@
return result
-/obj/item/storage/internal/animal_harness/proc/RemoveAttachment(obj/item/attachment)
+/obj/item/storage/animal_harness/proc/RemoveAttachment(obj/item/attachment)
if (istext(attachment))
var/obj/item/item = attached_items[attachment]
if (!item)
@@ -110,12 +125,29 @@
return attachment
-/obj/item/storage/internal/animal_harness/proc/CreateAttachments()
+/obj/item/storage/animal_harness/proc/CreateAttachments()
return
/mob/living/simple_mob/animal
- var/obj/item/storage/internal/animal_harness/harness
+ var/obj/item/storage/animal_harness/harness
+
+
+/mob/living/simple_mob/animal/attackby(obj/item/O, mob/user)
+ if(istype(O, /obj/item/storage/animal_harness))
+ if(harness)
+ to_chat(user, SPAN_WARNING("\The [src] is already wearing \the [harness]."))
+ return TRUE
+ var/obj/item/storage/animal_harness/new_harness = O
+ if(!is_type_in_list(src, new_harness.harnessable_types))
+ to_chat(user, SPAN_WARNING("\The [new_harness] does not fit on \the [src]."))
+ return TRUE
+ if(user.unEquip(new_harness, src))
+ set_harness(new_harness)
+ user.visible_message(SPAN_NOTICE("\The [user] slips \the [harness] over \the [src] and buckles it securely."))
+ update_icon()
+ return TRUE
+ return ..()
/mob/living/simple_mob/animal/Destroy()
@@ -126,10 +158,44 @@
/mob/living/simple_mob/animal/Initialize()
. = ..()
- if (ispath(harness))
- harness = new harness (src)
- verbs += /mob/living/simple_mob/animal/proc/RemoveAttachmentVerb
+ if(harness)
+ if(ispath(harness))
+ set_harness(new harness(src))
+ else if(!istype(harness))
+ clear_harness()
+/mob/living/simple_mob/animal/proc/clear_harness()
+ if(!harness)
+ return FALSE
+ if(istype(harness))
+ var/turf/drop_loc = get_turf(src)
+ for (var/key in harness.attached_items)
+ var/obj/item/item = harness.attached_items[key]
+ if(item)
+ harness.RemoveAttachment(item)
+ item.dropInto(drop_loc)
+ harness.dropInto(drop_loc)
+ harness = null
+ verbs -= /mob/living/simple_mob/animal/proc/RemoveAttachmentVerb
+ verbs -= /mob/living/simple_mob/animal/proc/RemoveHarnessVerb
+ update_icon()
+ return TRUE
+
+/mob/living/simple_mob/animal/proc/set_harness(var/obj/item/storage/animal_harness/new_harness)
+ if(!istype(new_harness) || new_harness == harness)
+ return FALSE
+ if(istype(harness))
+ QDEL_NULL(harness)
+ harness = new_harness
+ if(istype(harness))
+ harness.forceMove(src)
+ verbs |= /mob/living/simple_mob/animal/proc/RemoveAttachmentVerb
+ verbs |= /mob/living/simple_mob/animal/proc/RemoveHarnessVerb
+ else
+ verbs -= /mob/living/simple_mob/animal/proc/RemoveAttachmentVerb
+ verbs -= /mob/living/simple_mob/animal/proc/RemoveHarnessVerb
+ update_icon()
+ return TRUE
/mob/living/simple_mob/animal/examine(mob/living/user)
. = ..()
@@ -138,6 +204,31 @@
for (var/obj/item/item in harness.GetAttachedItems())
. += "There is \a [item] attached."
+/mob/living/simple_mob/animal/proc/RemoveHarnessVerb()
+ set name = "Remove Harness"
+ set category = "IC"
+ set src in view(1)
+
+ var/mob/living/user = usr
+ if (!istype(user))
+ return
+ if (!Adjacent(user) || user.incapacitated())
+ to_chat(user, SPAN_WARNING("You are in no condition to do that."))
+ return
+ if (!user.check_dexterity(MOB_DEXTERITY_SIMPLE_MACHINES))
+ return
+ if(user == src)
+ to_chat(usr, SPAN_WARNING("You cannot remove your own harness!"))
+ return
+
+ var/removing_harness = harness
+ user.visible_message(SPAN_NOTICE("\The [user] begins unbuckling \the [harness] from \the [src]."))
+ if(!do_after(user, 3 SECONDS, src) || removing_harness != harness || !Adjacent(user) || user.incapacitated())
+ return
+ user.visible_message(SPAN_NOTICE("\The [user] unbuckles and removes \the [harness] from \the [src]."))
+ harness.forceMove(get_turf(src))
+ clear_harness()
+
/mob/living/simple_mob/animal/proc/RemoveAttachmentVerb()
set name = "Remove Harness Attachment"
set category = "IC"
@@ -178,3 +269,29 @@
return
var/obj/item/removed = harness.RemoveAttachment(response)
user.put_in_hands(removed)
+
+// The below logic is adapted from /obj/item/storage/internal.
+/obj/item/storage/animal_harness/attack_hand(mob/user)
+ if(isanimal(loc))
+ var/mob/living/simple_mob/animal/critter = loc
+ if(critter.harness == src)
+ if(loc == user)
+ add_fingerprint(user)
+ open(user)
+ return FALSE
+ if(user.Adjacent(loc))
+ add_fingerprint(user)
+ open(user)
+ return FALSE
+ for(var/mob/M in range(1, get_turf(loc)))
+ if (M.s_active == src)
+ src.close(M)
+ return TRUE
+ return ..()
+
+/obj/item/storage/animal_harness/Adjacent(var/atom/neighbor)
+ if(isanimal(loc))
+ var/mob/living/simple_mob/animal/critter = loc
+ if(critter.harness == src)
+ return critter.Adjacent(neighbor)
+ return ..()
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka/grafadreka.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka/grafadreka.dm
index 2eab15c14b..87f8f7a184 100644
--- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka/grafadreka.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka/grafadreka.dm
@@ -97,12 +97,20 @@ You can eat glowing tree fruit to fuel your ranged spitting attack and ranged spitting attack and ranged spitting attack and ranged spitting attack and ranged spitting attack and ranged spitting attack and ranged spitting attack and = 10
-
-/mob/living/simple_mob/animal/sif/grafadreka/proc/add_glow()
- var/image/I = image(icon, "[icon_state]-glow")
- I.color = glow_colour
- I.plane = PLANE_LIGHTING_ABOVE
- I.alpha = 35 + round(220 * clamp(stored_sap/max_stored_sap, 0, 1))
- return I
-
-
/mob/living/simple_mob/animal/sif/grafadreka/proc/has_sap(amount)
return stored_sap >= amount
@@ -550,3 +587,24 @@ var/global/list/wounds_being_tended_by_drakes = list()
to_chat(drake, SPAN_NOTICE("The pack leader wishes for you to follow them."))
else if (drake.ai_holder)
drake.ai_holder.set_follow(src)
+
+
+/mob/living/simple_mob/animal/sif/grafadreka/GetIdCard()
+ return harness?.GetIdCard()
+
+
+/mob/living/simple_mob/animal/sif/grafadreka/attack_hand(mob/living/user)
+ // Permit headpats/smacks
+ if (!harness || user.a_intent == I_HURT || (user.a_intent == I_HELP && user.zone_sel?.selecting == BP_HEAD))
+ return ..()
+ return harness.attack_hand(user)
+
+
+/mob/living/simple_mob/animal/sif/grafadreka/attackby(obj/item/item, mob/user)
+ if (user.a_intent == I_HURT)
+ return ..()
+ if (user.a_intent == I_HELP && is_type_in_list(item, allow_type_to_pass))
+ return ..()
+ if (harness?.attackby(item, user))
+ return TRUE
+ return ..()
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka/grafadreka_harness.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka/grafadreka_harness.dm
index 039e29964a..b8a953fbd8 100644
--- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka/grafadreka_harness.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka/grafadreka_harness.dm
@@ -1,46 +1,39 @@
-/obj/item/storage/internal/animal_harness/grafadreka
- abstract_type = /obj/item/storage/internal/animal_harness/grafadreka
+/obj/item/storage/animal_harness/grafadreka
+ abstract_type = /obj/item/storage/animal_harness/grafadreka
name = "grafadreka harness"
self_attach_delay = 3 SECONDS
-
- // keys for animal_harness/attachable_type
- var/const/ATTACHED_GPS = "gps"
- var/const/ATTACHED_RADIO = "radio"
- var/const/ATTACHED_ARMOR = "armor plate"
- var/const/ATTACHED_LIGHT = "light"
- var/const/ATTACHED_ID = "access card"
-
+ harnessable_types = list(
+ /mob/living/simple_mob/animal/sif/grafadreka,
+ /mob/living/simple_mob/animal/sif/grafadreka/trained
+ )
/// An attachable_types list shared between drake harness instances.
var/static/list/grafadreka_attachable_types = list(
/obj/item/gps = ATTACHED_GPS,
/obj/item/radio = ATTACHED_RADIO,
/obj/item/clothing/accessory/armor = list(
ATTACHED_ARMOR,
- /obj/item/storage/internal/animal_harness/grafadreka/proc/UpdateArmor
+ /obj/item/storage/animal_harness/grafadreka/proc/UpdateArmor
),
/obj/item/clothing/accessory/material/makeshift = list(
ATTACHED_ARMOR,
- /obj/item/storage/internal/animal_harness/grafadreka/proc/UpdateArmor
+ /obj/item/storage/animal_harness/grafadreka/proc/UpdateArmor
),
/obj/item/card/id = ATTACHED_ID,
/obj/item/flashlight = ATTACHED_LIGHT
)
-/obj/item/storage/internal/animal_harness/grafadreka/GetIdCard()
- return attached_items[ATTACHED_ID]
-
-/obj/item/storage/internal/animal_harness/grafadreka/Destroy()
+/obj/item/storage/animal_harness/grafadreka/Destroy()
attachable_types = null
return ..()
-/obj/item/storage/internal/animal_harness/grafadreka/Initialize(mapload)
+/obj/item/storage/animal_harness/grafadreka/Initialize(mapload)
attachable_types = grafadreka_attachable_types
. = ..()
-/obj/item/storage/internal/animal_harness/grafadreka/proc/UpdateArmor()
+/obj/item/storage/animal_harness/grafadreka/proc/UpdateArmor()
var/mob/living/simple_mob/animal/sif/grafadreka/drake = loc
if (!istype(drake))
return
@@ -53,7 +46,7 @@
// Basic trained drake harness contents on spawn
-/obj/item/storage/internal/animal_harness/grafadreka/trained/CreateAttachments()
+/obj/item/storage/animal_harness/grafadreka/trained/CreateAttachments()
var/mob/living/owner = loc
if (!istype(owner))
return
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka/grafadreka_misc.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka/grafadreka_misc.dm
index c22a60583e..db1881acb5 100644
--- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka/grafadreka_misc.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka/grafadreka_misc.dm
@@ -63,6 +63,7 @@ Field studies suggest analytical abilities on par with some species of cepholapo
)
+
/obj/item/projectile/drake_spit
name = "drake spittle"
icon_state = "ice_1"
@@ -77,6 +78,7 @@ Field studies suggest analytical abilities on par with some species of cepholapo
eyeblur = 5
fire_sound = 'sound/voice/drakes/drake_spit.ogg'
+
/obj/item/projectile/drake_spit/on_hit(atom/target, blocked, def_zone)
// Stun is needed to effectively hunt simplemobs, but it's OP against humans.
if(ishuman(target))
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka/grafadreka_trained.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka/grafadreka_trained.dm
index f8e5a4c8a7..434b3d7355 100644
--- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka/grafadreka_trained.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka/grafadreka_trained.dm
@@ -9,84 +9,13 @@ Using grab intent you can pick up and drop items by
gender = PLURAL // Will take gender from prefs = set to non-NEUTER here to avoid randomizing in Initialize().
movement_cooldown = 1.5 // ~~Red~~ trained ones go faster.
dexterity = MOB_DEXTERITY_SIMPLE_MACHINES
- harness = /obj/item/storage/internal/animal_harness/grafadreka/trained
+ harness = /obj/item/storage/animal_harness/grafadreka/trained
trained_drake = TRUE
understands_languages = list(
LANGUAGE_GALCOM,
LANGUAGE_SIVIAN
)
- /// On clicking with an item, stuff that should use behaviors instead of being placed in storage.
- var/static/list/allow_type_to_pass = list(
- /obj/item/healthanalyzer,
- /obj/item/stack/medical,
- /obj/item/reagent_containers/syringe,
- /obj/item/shockpaddles
- )
-
-/mob/living/simple_mob/animal/sif/grafadreka/trained/Destroy()
- if (istype(harness))
- QDEL_NULL(harness)
- return ..()
-
-
-/mob/living/simple_mob/animal/sif/grafadreka/trained/GetIdCard()
- return harness?.GetIdCard()
-
-
-/mob/living/simple_mob/animal/sif/grafadreka/trained/add_glow()
- . = ..()
- if (. && harness)
- var/image/I = .
- I.icon_state = "[I.icon_state]-[harness.icon_state]"
-
-
-/mob/living/simple_mob/animal/sif/grafadreka/trained/Logout()
- ..()
- if (stat != DEAD)
- lying = TRUE
- resting = TRUE
- sitting = FALSE
- Sleeping(2)
- update_icon()
-
-
-/mob/living/simple_mob/animal/sif/grafadreka/trained/Login()
- ..()
- SetSleeping(0)
- update_icon()
-
-
-/mob/living/simple_mob/animal/sif/grafadreka/trained/attack_hand(mob/living/user)
- // Permit headpats/smacks
- if (!harness || user.a_intent == I_HURT || (user.a_intent == I_HELP && user.zone_sel?.selecting == BP_HEAD))
- return ..()
- return harness.handle_attack_hand(user)
-
-
-/mob/living/simple_mob/animal/sif/grafadreka/trained/update_icon()
- . = ..()
- if (harness)
- var/image/I = image(harness.icon, "[current_icon_state]-[harness.icon_state]")
- I.color = harness.color
- I.appearance_flags |= (RESET_COLOR|PIXEL_SCALE|KEEP_APART)
- if (offset_compiled_icon)
- I.pixel_x = offset_compiled_icon
- add_overlay(I)
-
-
-/mob/living/simple_mob/animal/sif/grafadreka/trained/attackby(obj/item/item, mob/user)
- if (user.a_intent == I_HURT)
- return ..()
- if (user.a_intent == I_HELP)
- for (var/pass_type in allow_type_to_pass)
- if (istype(item, pass_type))
- return ..()
- if (harness?.attackby(item, user))
- return TRUE
- return ..()
-
-
/mob/living/simple_mob/animal/sif/grafadreka/trained/proc/DropItem()
if (!length(harness?.contents))
to_chat(src, SPAN_WARNING("You have nothing to drop."))
diff --git a/icons/mob/drake_harness.dmi b/icons/mob/drake_harness.dmi
index a9de55e0c7..1ceb8653b8 100644
Binary files a/icons/mob/drake_harness.dmi and b/icons/mob/drake_harness.dmi differ
diff --git a/maps/cynosure/cynosure_jobs.dm b/maps/cynosure/cynosure_jobs.dm
index 662763626c..2c59581cca 100644
--- a/maps/cynosure/cynosure_jobs.dm
+++ b/maps/cynosure/cynosure_jobs.dm
@@ -285,9 +285,8 @@ var/global/const/access_explorer = 43
for(var/language in P.alternate_languages)
LAZYDISTINCTADD(drake.understands_languages, language)
- var/obj/item/storage/internal/animal_harness/grafadreka/harness = drake.harness
- if (istype(harness))
- var/obj/item/gps/gps = harness.attached_items[harness.ATTACHED_GPS]
+ if (drake.harness?.attached_items)
+ var/obj/item/gps/gps = drake.harness.attached_items[drake.harness.ATTACHED_GPS]
if (gps)
gps.SetTag(drake.name)
@@ -327,7 +326,7 @@ var/global/const/access_explorer = 43
access = list(access_explorer, access_research)
-/obj/item/storage/internal/animal_harness/grafadreka/expedition
+/obj/item/storage/animal_harness/grafadreka/expedition
name = "expedition harness"
color = null
icon_state = "explorer"
@@ -351,7 +350,7 @@ var/global/const/access_explorer = 43
// Station/Science drake harness contents on spawn
-/obj/item/storage/internal/animal_harness/grafadreka/expedition/CreateAttachments()
+/obj/item/storage/animal_harness/grafadreka/expedition/CreateAttachments()
var/mob/living/owner = loc
if(!istype(owner))
return
@@ -371,7 +370,7 @@ var/global/const/access_explorer = 43
You have been trained by the Xenobiology department to assist with expeditions and field work, and are an official part of the Exploration staff."
You can eat glowing tree fruit to fuel your ranged spitting attack and poisonous bite (on harm intent), as well as healing saliva (on help intent).
Using grab intent you can pick up and drop items by clicking them or yourself, and can interact with some simple machines like buttons and levers."}
- harness = /obj/item/storage/internal/animal_harness/grafadreka/expedition
+ harness = /obj/item/storage/animal_harness/grafadreka/expedition
/obj/item/book/manual/drake_handling
name = "Care And Handling Of The Sivian Snow Drake v0.1.12b"