Merge pull request #8904 from MistakeNot4892/drakes3

Drake harnesses are removable.
This commit is contained in:
Atermonera
2023-03-08 18:04:45 -08:00
committed by GitHub
7 changed files with 224 additions and 126 deletions
@@ -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 ..()
@@ -97,12 +97,20 @@ You can eat glowing tree fruit to fuel your <b>ranged spitting attack</b> and <b
var/trained_drake = FALSE
var/list/understands_languages
// universal_understand is buggy and produces double lines, so we'll just do this hack instead.
/mob/living/simple_mob/animal/sif/grafadreka/say_understands(mob/other, datum/language/speaking)
if (!speaking || (speaking.name in understands_languages))
return TRUE
/// 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/Destroy()
if (istype(harness))
QDEL_NULL(harness)
return ..()
/mob/living/simple_mob/animal/sif/grafadreka/Initialize()
if(is_baby)
verbs |= /mob/living/proc/hide
@@ -119,6 +127,13 @@ You can eat glowing tree fruit to fuel your <b>ranged spitting attack</b> and <b
update_icon()
// universal_understand is buggy and produces double lines, so we'll just do this hack instead.
/mob/living/simple_mob/animal/sif/grafadreka/say_understands(mob/other, datum/language/speaking)
if (!speaking || (speaking.name in understands_languages))
return TRUE
return ..()
/mob/living/simple_mob/animal/sif/grafadreka/Stat()
. = ..()
if (statpanel("Status"))
@@ -129,6 +144,9 @@ You can eat glowing tree fruit to fuel your <b>ranged spitting attack</b> and <b
/mob/living/simple_mob/animal/sif/grafadreka/Login()
. = ..()
reset_charisma()
SetSleeping(0)
update_icon()
QDEL_NULL(ai_holder) // Disable AI so player drakes don't maul prometheans to death when aghosted
/mob/living/simple_mob/animal/sif/grafadreka/proc/reset_charisma()
@@ -231,7 +249,19 @@ You can eat glowing tree fruit to fuel your <b>ranged spitting attack</b> and <b
/mob/living/simple_mob/animal/sif/grafadreka/Life()
// First up we knock ourselves out if we're expected to have a client and don't -
// this is to avoid aghosted or logged out drakes going wild on Teshari on the station.
if (stat != DEAD && key && !client)
if(!lying || !resting || sitting)
lying = TRUE
resting = TRUE
sitting = FALSE
update_icon()
Sleeping(2)
. = ..()
if (stat == CONSCIOUS)
// Don't make clientless drakes lose nutrition or they'll all go feral.
if (!resting && client)
@@ -264,6 +294,7 @@ You can eat glowing tree fruit to fuel your <b>ranged spitting attack</b> and <b
. = ..()
if (sitting && stat == CONSCIOUS)
icon_state = "[initial(icon_state)]_sitting"
var/list/add_images = list()
var/image/I = image(icon, "[icon_state]")
I.color = base_colour
@@ -274,19 +305,34 @@ You can eat glowing tree fruit to fuel your <b>ranged spitting attack</b> and <b
I = image(icon, "[icon_state]-claws")
I.color = claw_colour
add_images += I
if (stat == CONSCIOUS && !sleeping)
I = image(icon, "[icon_state]-eye_overlay")
I.color = eye_colour
add_images += I
if (stat != DEAD)
var/glow = add_glow()
var/image/glow = image(icon, "[icon_state]-glow")
glow.color = glow_colour
glow.plane = PLANE_LIGHTING_ABOVE
glow.alpha = 35 + round(220 * clamp(stored_sap/max_stored_sap, 0, 1))
if (harness)
glow.icon_state = "[glow.icon_state]-[harness.icon_state]"
if (glow)
add_images += glow
if (harness)
I = image(harness.icon, "[current_icon_state]-[harness.icon_state]")
I.color = harness.color
I.appearance_flags |= (RESET_COLOR|PIXEL_SCALE|KEEP_APART)
add_images += I
for (var/image/adding in add_images)
adding.appearance_flags |= (RESET_COLOR|PIXEL_SCALE|KEEP_APART)
if (offset_compiled_icon)
adding.pixel_x = offset_compiled_icon // Offset here so that things like modifiers, runechat text, etc. are centered
add_overlay(adding)
// We do this last so the default mob icon_state can be used for the overlays.
current_icon_state = icon_state
icon_state = "blank"
@@ -405,15 +451,6 @@ You can eat glowing tree fruit to fuel your <b>ranged spitting attack</b> and <b
/mob/living/simple_mob/animal/sif/grafadreka/has_appetite()
return reagents && abs(reagents.total_volume - reagents.maximum_volume) >= 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("<b>The pack leader wishes for you to follow them.</b>"))
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 ..()
@@ -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
@@ -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))
@@ -9,84 +9,13 @@ Using <font color='#e0a000'>grab intent</font> 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."))