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"