diff --git a/code/__DEFINES/dcs/mob_signals.dm b/code/__DEFINES/dcs/mob_signals.dm index 7ade7b633f2..fa194f0ef62 100644 --- a/code/__DEFINES/dcs/mob_signals.dm +++ b/code/__DEFINES/dcs/mob_signals.dm @@ -196,6 +196,7 @@ ///from of mob/MouseDrop(): (/atom/over, /mob/user) #define COMSIG_DO_MOB_STRIP "do_mob_strip" +#define COMSIG_STRIPPABLE_REQUEST_ITEMS "strippable_request_items" // Sent when a mob spawner is attacked directly or via projectile. #define COMSIG_SPAWNER_SET_TARGET "spawner_set_target" @@ -239,3 +240,5 @@ #define MOVE_ARG_NEW_LOC 1 /// The arugment of move_args which dictates our movement direction #define MOVE_ARG_DIRECTION 2 + +#define COMSIG_LIVING_RESTING "living_resting" diff --git a/code/datums/elements/strippable.dm b/code/datums/elements/strippable.dm index 11a88c871d2..d0283e3897a 100644 --- a/code/datums/elements/strippable.dm +++ b/code/datums/elements/strippable.dm @@ -313,8 +313,11 @@ var/list/items = list() - for(var/strippable_key in strippable.items) - var/datum/strippable_item/item_data = strippable.items[strippable_key] + var/list/unfiltered_items = strippable.items.Copy() + SEND_SIGNAL(owner, COMSIG_STRIPPABLE_REQUEST_ITEMS, unfiltered_items) + + for(var/strippable_key in unfiltered_items) + var/datum/strippable_item/item_data = unfiltered_items[strippable_key] if(!item_data.should_show(owner, user)) continue @@ -389,11 +392,14 @@ if(!isliving(ui.user) || !HAS_TRAIT(user, TRAIT_CAN_STRIP)) return + var/list/unfiltered_items = strippable.items.Copy() + SEND_SIGNAL(owner, COMSIG_STRIPPABLE_REQUEST_ITEMS, unfiltered_items) + . = TRUE switch(action) if("use") var/key = params["key"] - var/datum/strippable_item/strippable_item = strippable.items[key] + var/datum/strippable_item/strippable_item = unfiltered_items[key] if(isnull(strippable_item)) return diff --git a/code/datums/elements/wears_collar.dm b/code/datums/elements/wears_collar.dm new file mode 100644 index 00000000000..ee89ca6a18a --- /dev/null +++ b/code/datums/elements/wears_collar.dm @@ -0,0 +1,122 @@ +/datum/element/wears_collar + element_flags = ELEMENT_BESPOKE + argument_hash_start_idx = 2 + ///our icon's pathfile + var/collar_icon + ///our collar's icon state + var/collar_icon_state + ///iconstate of our collar while resting + var/collar_resting_icon_state + + var/datum/strippable_item/pet_collar/pet_collar + +/datum/element/wears_collar/Attach(datum/target, collar_icon_ = 'icons/mob/pets.dmi', collar_icon_state_, collar_resting_icon_state_ = FALSE) + . = ..() + + if(!isliving(target)) + return ELEMENT_INCOMPATIBLE + + collar_icon = collar_icon_ + collar_icon_state = collar_icon_state_ + collar_resting_icon_state = collar_resting_icon_state_ + pet_collar = new + + RegisterSignal(target, COMSIG_ATTACK_BY, PROC_REF(attach_collar)) + RegisterSignal(target, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_overlays_updated)) + RegisterSignal(target, COMSIG_ATOM_EXITED, PROC_REF(on_content_exit)) + RegisterSignal(target, COMSIG_ATOM_ENTERED, PROC_REF(on_content_enter)) + RegisterSignal(target, COMSIG_LIVING_RESTING, PROC_REF(on_rest)) + RegisterSignal(target, COMSIG_MOB_STATCHANGE, PROC_REF(on_stat_change)) + RegisterSignal(target, COMSIG_STRIPPABLE_REQUEST_ITEMS, PROC_REF(on_strippable_request_items)) + +/datum/element/wears_collar/Detach(datum/target) + . = ..() + UnregisterSignal(target, list( + COMSIG_ATTACK_BY, + COMSIG_ATOM_UPDATE_OVERLAYS, + COMSIG_ATOM_EXITED, + COMSIG_ATOM_ENTERED, + COMSIG_LIVING_RESTING, + COMSIG_MOB_STATCHANGE, + COMSIG_STRIPPABLE_REQUEST_ITEMS, + )) + +/datum/element/wears_collar/proc/on_strippable_request_items(datum/source, list/items) + SIGNAL_HANDLER // COMSIG_STRIPPABLE_REQUEST_ITEMS + items[STRIPPABLE_ITEM_PET_COLLAR] = pet_collar + +/datum/element/wears_collar/proc/on_stat_change(mob/living/source) + SIGNAL_HANDLER // COMSIG_MOB_STATCHANGE + + if(collar_icon_state) + source.update_icon(UPDATE_OVERLAYS) + +/datum/element/wears_collar/proc/on_content_exit(mob/living/source, atom/moved) + SIGNAL_HANDLER // COMSIG_ATOM_EXITED + + var/obj/item/petcollar/collar = moved + if(!istype(moved)) + return + + if(collar.tagname) + source.name = collar.original_name + source.real_name = collar.original_real_name + + collar.original_name = null + collar.original_real_name = null + + if(collar_icon_state) + source.update_appearance() + +/datum/element/wears_collar/proc/on_content_enter(mob/living/source, obj/item/petcollar/new_collar) + SIGNAL_HANDLER // COMSIG_ATOM_ENTERED + + if(!istype(new_collar)) + return + + if(new_collar.tagname) + new_collar.original_name = source.name + new_collar.original_real_name = source.real_name + source.name = new_collar.tagname + source.real_name = new_collar.tagname + + if(collar_icon_state) + source.update_appearance() + +/datum/element/wears_collar/proc/attach_collar(atom/source, atom/movable/attacking_item, mob/user, params) + SIGNAL_HANDLER // COMSIG_ATTACK_BY + + if(!istype(attacking_item, /obj/item/petcollar)) + return NONE + if(locate(/obj/item/petcollar) in source) + to_chat(user, "[source] is already wearing a collar!") + return COMPONENT_SKIP_AFTERATTACK + + if(user.drop_item()) + attacking_item.forceMove(source) + to_chat(user, "You put [attacking_item] around [source]'s neck.") + else + to_chat(user, "[attacking_item] is stuck to your hand!") + + return COMPONENT_SKIP_AFTERATTACK + +/datum/element/wears_collar/proc/on_overlays_updated(mob/living/source, list/overlays) + SIGNAL_HANDLER // COMSIG_ATOM_UPDATE_OVERLAYS + + if(!locate(/obj/item/petcollar) in source) + return + + var/icon_tag = "" + + if(source.stat == DEAD || HAS_TRAIT(source, TRAIT_FAKEDEATH)) + icon_tag = "_dead" + else if(collar_resting_icon_state && source.resting) + icon_tag = "_rest" + + overlays += mutable_appearance(collar_icon, "[collar_icon_state][icon_tag]collar") + overlays += mutable_appearance(collar_icon, "[collar_icon_state][icon_tag]tag") + +/datum/element/wears_collar/proc/on_rest(atom/movable/source) + SIGNAL_HANDLER // COMSIG_LIVING_RESTING + + source.update_icon(UPDATE_OVERLAYS) diff --git a/code/game/objects/items/collar.dm b/code/game/objects/items/collar.dm index 9bb839442c8..6eb50053982 100644 --- a/code/game/objects/items/collar.dm +++ b/code/game/objects/items/collar.dm @@ -4,6 +4,8 @@ icon_state = "petcollar" item_color = "petcollar" var/tagname = null + var/original_name + var/original_real_name var/obj/item/card/id/access_id /obj/item/petcollar/Destroy() @@ -62,7 +64,7 @@ /obj/item/petcollar/process() var/mob/living/simple_animal/M = loc // if it wasn't intentionally unequipped but isn't being worn, possibly gibbed - if(istype(M) && src == M.pcollar && M.stat != DEAD) + if(istype(M) && M.stat != DEAD) return var/area/pet_death_area = get_area(M) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index afb9caeee19..cc0a9ca7da5 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -2093,10 +2093,9 @@ P.master_commander = H P.universal_speak = TRUE P.universal_understand = TRUE - P.set_can_collar(TRUE) + P.AddElement(/datum/element/wears_collar) P.faction = list("neutral") - var/obj/item/petcollar/C = new - P.add_collar(C) + var/obj/item/petcollar/C = new(P) var/obj/item/card/id/I = H.wear_id if(I) var/obj/item/card/id/D = new /obj/item/card/id(C) diff --git a/code/modules/events/sentience.dm b/code/modules/events/sentience.dm index ada9e98b3f7..e4a4ae4b39a 100644 --- a/code/modules/events/sentience.dm +++ b/code/modules/events/sentience.dm @@ -27,7 +27,7 @@ dust_if_respawnable(SG) SA.universal_speak = TRUE SA.sentience_act() - SA.set_can_collar(TRUE) + SA.AddElement(/datum/element/wears_collar) SA.maxHealth = max(SA.maxHealth, 200) SA.health = SA.maxHealth SA.del_on_death = FALSE diff --git a/code/modules/mining/equipment/lazarus_injector.dm b/code/modules/mining/equipment/lazarus_injector.dm index 0ebd211169b..ebc0a03cce7 100644 --- a/code/modules/mining/equipment/lazarus_injector.dm +++ b/code/modules/mining/equipment/lazarus_injector.dm @@ -27,7 +27,7 @@ if(M.stat == DEAD) M.faction = list("neutral") M.revive() - M.set_can_collar(TRUE) + M.AddElement(/datum/element/wears_collar) if(ishostile(target)) var/mob/living/simple_animal/hostile/H = M if(isretaliate(target)) diff --git a/code/modules/mob/living/living_status_procs.dm b/code/modules/mob/living/living_status_procs.dm index e83bab19b03..c44fa0c63b4 100644 --- a/code/modules/mob/living/living_status_procs.dm +++ b/code/modules/mob/living/living_status_procs.dm @@ -120,6 +120,7 @@ STATUS EFFECTS layer = LYING_MOB_LAYER //so mob lying always appear behind standing mobs ADD_TRAIT(src, TRAIT_UI_BLOCKED, LYING_DOWN_TRAIT) ADD_TRAIT(src, TRAIT_CANNOT_PULL, LYING_DOWN_TRAIT) + SEND_SIGNAL(src, COMSIG_LIVING_RESTING, TRUE) RegisterSignal(src, COMSIG_ATOM_DIR_CHANGE, PROC_REF(orient_crawling)) set_density(FALSE) set_lying_angle(pick(90, 270)) @@ -129,6 +130,7 @@ STATUS EFFECTS layer = initial(layer) set_density(initial(density)) REMOVE_TRAITS_IN(src, LYING_DOWN_TRAIT) + SEND_SIGNAL(src, COMSIG_LIVING_RESTING, FALSE) UnregisterSignal(src, COMSIG_ATOM_DIR_CHANGE) set_lying_angle(0) pixel_y = 0 diff --git a/code/modules/mob/living/simple_animal/animal_defense.dm b/code/modules/mob/living/simple_animal/animal_defense.dm index 95c22172bf9..5b919326889 100644 --- a/code/modules/mob/living/simple_animal/animal_defense.dm +++ b/code/modules/mob/living/simple_animal/animal_defense.dm @@ -1,8 +1,3 @@ -/mob/living/simple_animal/item_interaction(mob/living/user, obj/item/O, list/modifiers) - if(can_collar && istype(O, /obj/item/petcollar) && !pcollar) - add_collar(O, user) - return ITEM_INTERACT_COMPLETE - /mob/living/simple_animal/attack_hand(mob/living/carbon/human/M) ..() switch(M.a_intent) diff --git a/code/modules/mob/living/simple_animal/friendly/bunny.dm b/code/modules/mob/living/simple_animal/friendly/bunny.dm index 881e014f147..ff96383f425 100644 --- a/code/modules/mob/living/simple_animal/friendly/bunny.dm +++ b/code/modules/mob/living/simple_animal/friendly/bunny.dm @@ -25,10 +25,13 @@ maxbodytemp = 323 //Above 50 Degrees Celcius can_hide = TRUE holder_type = /obj/item/holder/bunny - can_collar = TRUE gold_core_spawnable = FRIENDLY_SPAWN ventcrawler = VENTCRAWLER_ALWAYS +/mob/living/simple_animal/bunny/Initialize(mapload) + . = ..() + AddElement(/datum/element/wears_collar) + /mob/living/simple_animal/bunny/attack_hand(mob/living/carbon/human/M) if(M.a_intent == INTENT_HELP) get_scooped(M, TRUE) diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm index 83696196b72..ae3c7e47bd3 100644 --- a/code/modules/mob/living/simple_animal/friendly/cat.dm +++ b/code/modules/mob/living/simple_animal/friendly/cat.dm @@ -22,12 +22,16 @@ response_disarm = "gently pushes aside" response_harm = "kicks" gold_core_spawnable = FRIENDLY_SPAWN - collar_type = "cat" var/turns_since_scan = 0 var/mob/living/simple_animal/mouse/movement_target var/eats_mice = 1 + var/collar_icon_state = "cat" footstep_type = FOOTSTEP_MOB_CLAW +/mob/living/simple_animal/pet/cat/Initialize(mapload) + . = ..() + AddElement(/datum/element/wears_collar, collar_icon_state_ = collar_icon_state, collar_resting_icon_state_ = TRUE) + //RUNTIME IS ALIVE! SQUEEEEEEEE~ /mob/living/simple_animal/pet/cat/runtime name = "Runtime" @@ -118,7 +122,6 @@ resting = TRUE custom_emote(EMOTE_VISIBLE, pick("sits down.", "crouches on its hind legs.", "looks alert.")) icon_state = "[icon_living]_sit" - collar_type = "[initial(collar_type)]_sit" /mob/living/simple_animal/pet/cat/handle_automated_action() if(stat == CONSCIOUS && !buckled) @@ -195,7 +198,7 @@ gender = NEUTER density = FALSE pass_flags = PASSMOB - collar_type = "kitten" + collar_icon_state = "kitten" /mob/living/simple_animal/pet/cat/syndi name = "SyndiCat" diff --git a/code/modules/mob/living/simple_animal/friendly/corgi_stripping.dm b/code/modules/mob/living/simple_animal/friendly/corgi_stripping.dm index 863dbb2e081..01858b3968f 100644 --- a/code/modules/mob/living/simple_animal/friendly/corgi_stripping.dm +++ b/code/modules/mob/living/simple_animal/friendly/corgi_stripping.dm @@ -2,8 +2,7 @@ GLOBAL_LIST_INIT(strippable_corgi_items, create_strippable_list(list( /datum/strippable_item/corgi_head, - /datum/strippable_item/corgi_back, - /datum/strippable_item/pet_collar + /datum/strippable_item/corgi_back ))) /datum/strippable_item/corgi_head @@ -41,7 +40,7 @@ GLOBAL_LIST_INIT(strippable_corgi_items, create_strippable_list(list( if(!istype(pet_source)) return - return pet_source.pcollar + return (locate(/obj/item/petcollar) in source) /datum/strippable_item/pet_collar/try_equip(atom/source, obj/item/equipping, mob/user) . = ..() @@ -59,15 +58,15 @@ GLOBAL_LIST_INIT(strippable_corgi_items, create_strippable_list(list( if(!istype(pet_source)) return - INVOKE_ASYNC(source, TYPE_PROC_REF(/mob/living/simple_animal, add_collar), equipping, user) + user.transfer_item_to(equipping, source) /datum/strippable_item/pet_collar/finish_unequip(atom/source, mob/user) var/mob/living/simple_animal/pet_source = source if(!istype(pet_source)) return - INVOKE_ASYNC(pet_source, TYPE_PROC_REF(/mob/living/simple_animal, remove_collar), user.drop_location(), user) - + var/obj/item/petcollar/collar = locate() in source + user.put_in_hands(collar) /datum/strippable_item/corgi_back key = STRIPPABLE_ITEM_BACK diff --git a/code/modules/mob/living/simple_animal/friendly/crab.dm b/code/modules/mob/living/simple_animal/friendly/crab.dm index 8be2a37fdf1..2fd41ecf05d 100644 --- a/code/modules/mob/living/simple_animal/friendly/crab.dm +++ b/code/modules/mob/living/simple_animal/friendly/crab.dm @@ -18,9 +18,12 @@ friendly = "pinches" ventcrawler = VENTCRAWLER_ALWAYS can_hide = TRUE - can_collar = TRUE gold_core_spawnable = FRIENDLY_SPAWN +/mob/living/simple_animal/crab/Initialize(mapload) + . = ..() + AddElement(/datum/element/wears_collar) + /mob/living/simple_animal/crab/handle_automated_movement() //CRAB movement if(stat == CONSCIOUS || !isturf(loc) || IS_HORIZONTAL(src) || buckled) diff --git a/code/modules/mob/living/simple_animal/friendly/deer.dm b/code/modules/mob/living/simple_animal/friendly/deer.dm index 3bba3bfeeba..4e79d504a84 100644 --- a/code/modules/mob/living/simple_animal/friendly/deer.dm +++ b/code/modules/mob/living/simple_animal/friendly/deer.dm @@ -15,6 +15,9 @@ response_help = "pets" response_disarm = "gently pushes aside" response_harm = "kicks" - can_collar = TRUE mob_biotypes = MOB_ORGANIC | MOB_BEAST gold_core_spawnable = FRIENDLY_SPAWN + +/mob/living/simple_animal/deer/Initialize(mapload) + . = ..() + AddElement(/datum/element/wears_collar) diff --git a/code/modules/mob/living/simple_animal/friendly/diona_nymph.dm b/code/modules/mob/living/simple_animal/friendly/diona_nymph.dm index fd3e170fc39..3d22c9dde18 100644 --- a/code/modules/mob/living/simple_animal/friendly/diona_nymph.dm +++ b/code/modules/mob/living/simple_animal/friendly/diona_nymph.dm @@ -43,7 +43,6 @@ var/list/donors = list() holder_type = /obj/item/holder/diona - can_collar = TRUE a_intent = INTENT_HELP var/evolve_donors = 5 //amount of blood donors needed before evolving @@ -54,6 +53,10 @@ var/datum/action/innate/diona/evolve/evolve_action = new() var/datum/action/innate/diona/steal_blood/steal_blood_action = new() +/mob/living/simple_animal/diona/Initialize(mapload) + . = ..() + AddElement(/datum/element/wears_collar) + /datum/action/innate/diona/merge name = "Merge with gestalt" button_overlay_icon = 'icons/mob/human_races/r_diona.dmi' diff --git a/code/modules/mob/living/simple_animal/friendly/dog.dm b/code/modules/mob/living/simple_animal/friendly/dog.dm index 9bac2802d85..1f599885b55 100644 --- a/code/modules/mob/living/simple_animal/friendly/dog.dm +++ b/code/modules/mob/living/simple_animal/friendly/dog.dm @@ -73,7 +73,6 @@ butcher_results = list(/obj/item/food/meat/corgi = 3, /obj/item/stack/sheet/animalhide/corgi = 1) childtype = list(/mob/living/simple_animal/pet/dog/corgi/puppy = 95, /mob/living/simple_animal/pet/dog/corgi/puppy/void = 5) animal_species = /mob/living/simple_animal/pet/dog - collar_type = "corgi" hud_type = /datum/hud/corgi ///Currently worn item on the head slot var/obj/item/inventory_head = null @@ -84,13 +83,14 @@ var/shaved = FALSE var/nofur = FALSE //Corgis that have risen past the material plane of existence. var/razor_shave_delay = 5 SECONDS + var/collar_icon_state = "corgi" + /mob/living/simple_animal/pet/dog/corgi/Initialize(mapload) . = ..() - regenerate_icons() - -/mob/living/simple_animal/pet/dog/corgi/add_strippable_element() + AddElement(/datum/element/wears_collar, collar_icon_state_ = collar_icon_state) AddElement(/datum/element/strippable, length(strippable_inventory_slots) ? create_strippable_list(strippable_inventory_slots) : GLOB.strippable_corgi_items) + regenerate_icons() /mob/living/simple_animal/pet/dog/corgi/Destroy() QDEL_NULL(inventory_head) @@ -246,7 +246,7 @@ item_to_add.forceMove(src) inventory_head = item_to_add update_corgi_fluff() - regenerate_icons() + update_appearance() else to_chat(user, "You set [item_to_add] on [src]'s head, but it falls off!") item_to_add.forceMove(drop_location()) @@ -276,8 +276,9 @@ var/datum/dog_fashion/DF = new inventory_back.dog_fashion(src) DF.apply(src) -/mob/living/simple_animal/pet/dog/corgi/regenerate_icons() - ..() +/mob/living/simple_animal/pet/dog/corgi/update_overlays() + . = ..() + if(inventory_head) var/image/head_icon var/datum/dog_fashion/DF = new inventory_head.dog_fashion(src) @@ -296,7 +297,7 @@ else head_icon = DF.get_overlay() - add_overlay(head_icon) + . += head_icon if(inventory_back) var/image/back_icon @@ -315,7 +316,8 @@ back_icon.transform = turn(back_icon.transform, 180) else back_icon = DF.get_overlay() - add_overlay(back_icon) + + . += back_icon //IAN! SQUEEEEEEEEE~ /mob/living/simple_animal/pet/dog/corgi/ian @@ -519,9 +521,8 @@ density = FALSE pass_flags = PASSMOB mob_size = MOB_SIZE_SMALL - collar_type = "puppy" - strippable_inventory_slots = list(/datum/strippable_item/pet_collar) // Puppies do not have a head or back equipment slot. - + strippable_inventory_slots = list() // Puppies do not have a head or back equipment slot. + collar_icon_state = "puppy" /// Tribute to the corgis born in nullspace /mob/living/simple_animal/pet/dog/corgi/puppy/void @@ -553,7 +554,7 @@ response_help = "pets" response_disarm = "bops" response_harm = "kicks" - strippable_inventory_slots = list(/datum/strippable_item/corgi_back, /datum/strippable_item/pet_collar) //Lisa already has a cute bow! + strippable_inventory_slots = list(/datum/strippable_item/corgi_back) //Lisa already has a cute bow! var/turns_since_scan = 0 /mob/living/simple_animal/pet/dog/corgi/lisa/Life() @@ -655,7 +656,10 @@ icon_living = "pug" icon_dead = "pug_dead" butcher_results = list(/obj/item/food/meat/pug = 3) - collar_type = "pug" + +/mob/living/simple_animal/pet/dog/pug/Initialize(mapload) + . = ..() + AddElement(/datum/element/wears_collar, collar_icon_state_ = "pug") /mob/living/simple_animal/pet/dog/pug/handle_automated_movement() . = ..() diff --git a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm index 54837024ea3..e7cfc84a62e 100644 --- a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm +++ b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm @@ -26,7 +26,6 @@ melee_damage_lower = 1 melee_damage_upper = 2 stop_automated_movement_when_pulled = TRUE - can_collar = TRUE blood_volume = BLOOD_VOLUME_NORMAL var/obj/item/udder/cow/udder = null gender = FEMALE @@ -35,6 +34,7 @@ /mob/living/simple_animal/hostile/retaliate/goat/Initialize(mapload) . = ..() udder = new() + AddElement(/datum/element/wears_collar) /mob/living/simple_animal/hostile/retaliate/goat/Destroy() QDEL_NULL(udder) @@ -135,12 +135,12 @@ mob_size = MOB_SIZE_TINY holder_type = /obj/item/holder/chicken can_hide = TRUE - can_collar = TRUE gold_core_spawnable = FRIENDLY_SPAWN footstep_type = FOOTSTEP_MOB_CLAW /mob/living/simple_animal/chick/Initialize(mapload) . = ..() + AddElement(/datum/element/wears_collar) scatter_atom() /mob/living/simple_animal/chick/scatter_atom(x_offset, y_offset) @@ -157,10 +157,10 @@ C.name = name if(mind) mind.transfer_to(C) - if(pcollar) - var/the_collar = pcollar - drop_item_to_ground(pcollar) - C.add_collar(the_collar) + + for(var/atom/movable/AM in contents) + AM.forceMove(C) + qdel(src) @@ -208,7 +208,6 @@ GLOBAL_VAR_INIT(chicken_count, 0) mob_size = MOB_SIZE_SMALL holder_type = /obj/item/holder/chicken can_hide = TRUE - can_collar = TRUE var/list/feedMessages = list("It clucks happily.","It clucks happily.") var/list/layMessage = EGG_LAYING_MESSAGES var/list/validColors = list("brown","black","white") @@ -222,6 +221,7 @@ GLOBAL_VAR_INIT(chicken_count, 0) icon_state = "[icon_prefix]_[body_color]" icon_living = "[icon_prefix]_[body_color]" icon_dead = "[icon_prefix]_[body_color]_dead" + AddElement(/datum/element/wears_collar) scatter_atom() update_appearance(UPDATE_ICON_STATE) GLOB.chicken_count += 1 @@ -325,11 +325,14 @@ GLOBAL_VAR_INIT(chicken_count, 0) attacktext = "pecks" health = 50 maxHealth = 50 - can_collar = TRUE mob_biotypes = MOB_ORGANIC | MOB_BEAST gold_core_spawnable = FRIENDLY_SPAWN footstep_type = FOOTSTEP_MOB_CLAW +/mob/living/simple_animal/turkey/Initialize(mapload) + . = ..() + AddElement(/datum/element/wears_collar) + /mob/living/simple_animal/goose name = "goose" desc = "A pretty goose. Would make a nice comforter." @@ -350,11 +353,14 @@ GLOBAL_VAR_INIT(chicken_count, 0) attacktext = "kicks" health = 50 maxHealth = 50 - can_collar = TRUE mob_biotypes = MOB_ORGANIC | MOB_BEAST gold_core_spawnable = FRIENDLY_SPAWN footstep_type = FOOTSTEP_MOB_CLAW +/mob/living/simple_animal/goose/Initialize(mapload) + . = ..() + AddElement(/datum/element/wears_collar) + /mob/living/simple_animal/seal name = "seal" desc = "A beautiful white seal." @@ -375,11 +381,14 @@ GLOBAL_VAR_INIT(chicken_count, 0) attacktext = "kicks" health = 50 maxHealth = 50 - can_collar = TRUE mob_biotypes = MOB_ORGANIC | MOB_BEAST gold_core_spawnable = FRIENDLY_SPAWN blood_volume = BLOOD_VOLUME_NORMAL +/mob/living/simple_animal/seal/Initialize(mapload) + . = ..() + AddElement(/datum/element/wears_collar) + /mob/living/simple_animal/walrus name = "walrus" desc = "A big brown walrus." @@ -400,11 +409,14 @@ GLOBAL_VAR_INIT(chicken_count, 0) attacktext = "kicks" health = 50 maxHealth = 50 - can_collar = TRUE mob_biotypes = MOB_ORGANIC | MOB_BEAST gold_core_spawnable = FRIENDLY_SPAWN blood_volume = BLOOD_VOLUME_NORMAL +/mob/living/simple_animal/walrus/Initialize(mapload) + . = ..() + AddElement(/datum/element/wears_collar) + /obj/item/udder name = "udder" diff --git a/code/modules/mob/living/simple_animal/friendly/lizard.dm b/code/modules/mob/living/simple_animal/friendly/lizard.dm index 4932f70e997..4119e34aec3 100644 --- a/code/modules/mob/living/simple_animal/friendly/lizard.dm +++ b/code/modules/mob/living/simple_animal/friendly/lizard.dm @@ -23,13 +23,16 @@ can_hide = TRUE pass_door_while_hidden = TRUE butcher_results = list(/obj/item/food/meat = 1) - can_collar = TRUE mob_biotypes = MOB_ORGANIC | MOB_BEAST | MOB_REPTILE gold_core_spawnable = FRIENDLY_SPAWN var/eating_sound = 'sound/weapons/bite.ogg' /// Lizards start with a tail var/has_tail = TRUE +/mob/living/simple_animal/lizard/Initialize(mapload) + . = ..() + AddElement(/datum/element/wears_collar) + /mob/living/simple_animal/lizard/handle_automated_action() if(stat == CONSCIOUS && !buckled) if(prob(1)) diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm index c2069cc539f..bab3b8564f0 100644 --- a/code/modules/mob/living/simple_animal/friendly/mouse.dm +++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm @@ -34,12 +34,12 @@ can_hide = TRUE pass_door_while_hidden = TRUE holder_type = /obj/item/holder/mouse - can_collar = TRUE gold_core_spawnable = FRIENDLY_SPAWN var/chew_probability = 1 /mob/living/simple_animal/mouse/Initialize(mapload) . = ..() + AddElement(/datum/element/wears_collar) AddComponent(/datum/component/squeak, list('sound/creatures/mousesqueak.ogg' = 1), 100, extrarange = SHORT_RANGE_SOUND_EXTRARANGE) //as quiet as a mouse or whatever /mob/living/simple_animal/mouse/handle_automated_action() diff --git a/code/modules/mob/living/simple_animal/friendly/nian_caterpillar.dm b/code/modules/mob/living/simple_animal/friendly/nian_caterpillar.dm index 591b9d9ba84..74a9769dfc3 100644 --- a/code/modules/mob/living/simple_animal/friendly/nian_caterpillar.dm +++ b/code/modules/mob/living/simple_animal/friendly/nian_caterpillar.dm @@ -41,7 +41,6 @@ attack_sound = 'sound/weapons/bite.ogg' holder_type = /obj/item/holder/nian_caterpillar - can_collar = TRUE /// Evolution action. var/datum/action/innate/nian_caterpillar_emerge/evolve_action = new() @@ -51,6 +50,7 @@ /mob/living/simple_animal/nian_caterpillar/Initialize(mapload) . = ..() real_name = name + AddElement(/datum/element/wears_collar) add_language("Tkachi") evolve_action.Grant(src) diff --git a/code/modules/mob/living/simple_animal/friendly/penguin.dm b/code/modules/mob/living/simple_animal/friendly/penguin.dm index 1f554d3552f..c2f16d8f348 100644 --- a/code/modules/mob/living/simple_animal/friendly/penguin.dm +++ b/code/modules/mob/living/simple_animal/friendly/penguin.dm @@ -19,6 +19,7 @@ /mob/living/simple_animal/pet/penguin/Initialize(mapload) . = ..() + AddElement(/datum/element/wears_collar) AddElement(/datum/element/waddling) /mob/living/simple_animal/pet/penguin/emperor diff --git a/code/modules/mob/living/simple_animal/friendly/pet.dm b/code/modules/mob/living/simple_animal/friendly/pet.dm index 45ace13f41c..1697c08a850 100644 --- a/code/modules/mob/living/simple_animal/friendly/pet.dm +++ b/code/modules/mob/living/simple_animal/friendly/pet.dm @@ -3,7 +3,6 @@ mob_size = MOB_SIZE_SMALL mob_biotypes = MOB_ORGANIC | MOB_BEAST blood_volume = BLOOD_VOLUME_NORMAL - can_collar = TRUE speed = 0 // same speed as a person. hud_type = /datum/hud/corgi diff --git a/code/modules/mob/living/simple_animal/friendly/sloth.dm b/code/modules/mob/living/simple_animal/friendly/sloth.dm index 1f911f1a10d..c30f73aa5bf 100644 --- a/code/modules/mob/living/simple_animal/friendly/sloth.dm +++ b/code/modules/mob/living/simple_animal/friendly/sloth.dm @@ -25,6 +25,9 @@ speed = 2 footstep_type = FOOTSTEP_MOB_CLAW +/mob/living/simple_animal/pet/sloth/Initialize(mapload) + . = ..() + AddElement(/datum/element/wears_collar) //IAA Sloth /mob/living/simple_animal/pet/sloth/paperwork diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index a510384adbf..f87fe41d9a8 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -33,7 +33,6 @@ icon_dead = "parrot_dead" icon_resting = "parrot_sit" pass_flags = PASSTABLE - can_collar = TRUE blocks_emissive = EMISSIVE_BLOCK_UNIQUE faction = list("neutral", "jungle") @@ -127,7 +126,7 @@ /obj/machinery/computer/guestpass )) -/mob/living/simple_animal/parrot/add_strippable_element() + AddElement(/datum/element/wears_collar) AddElement(/datum/element/strippable, GLOB.strippable_parrot_items) /mob/living/simple_animal/parrot/Destroy() diff --git a/code/modules/mob/living/simple_animal/parrot_stripping.dm b/code/modules/mob/living/simple_animal/parrot_stripping.dm index 8de380b3a9f..f1be160dad4 100644 --- a/code/modules/mob/living/simple_animal/parrot_stripping.dm +++ b/code/modules/mob/living/simple_animal/parrot_stripping.dm @@ -1,6 +1,5 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /datum/strippable_item/parrot_headset, - /datum/strippable_item/pet_collar ))) /datum/strippable_item/parrot_headset diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 349c82e35da..158a24e09ae 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -87,13 +87,8 @@ /// Allows a mob to pass unbolted doors while hidden var/pass_door_while_hidden = FALSE - var/obj/item/petcollar/pcollar = null - /// If the mob has collar sprites, define them. - var/collar_type /// If the mob can be renamed var/unique_pet = FALSE - /// Can add collar to mob or not, use the set_can_collar if you want to change this on runtime - var/can_collar = FALSE /// Hot simple_animal baby making vars var/list/childtype = null @@ -152,12 +147,8 @@ if(can_hide) var/datum/action/innate/hide/hide = new() hide.Grant(src) - if(pcollar) - pcollar = new(src) - regenerate_icons() if(footstep_type) AddComponent(/datum/component/footstep, footstep_type) - add_strippable_element() apply_atmos_requirements() AddElement(/datum/element/body_temperature, minbodytemp, maxbodytemp, cold_damage_per_tick, heat_damage_per_tick) @@ -165,7 +156,6 @@ /mob/living/simple_animal/Destroy() /// We need to clear the reference to where we're walking to properly GC GLOB.move_manager.stop_looping(src) - QDEL_NULL(pcollar) for(var/datum/action/innate/hide/hide in actions) hide.Remove(src) master_commander = null @@ -200,11 +190,6 @@ /mob/living/simple_animal/proc/remove_atmos_requirements() RemoveElement(/datum/element/atmos_requirements) -/mob/living/simple_animal/handle_atom_del(atom/A) - if(A == pcollar) - pcollar = null - return ..() - /mob/living/simple_animal/examine(mob/user) . = ..() if(stat == DEAD) @@ -222,9 +207,6 @@ ..() if(icon_resting && stat != DEAD) icon_state = icon_resting - if(collar_type) - collar_type = "[initial(collar_type)]_rest" - regenerate_icons() if(!can_crawl) ADD_TRAIT(src, TRAIT_IMMOBILIZED, LYING_DOWN_TRAIT) //simple mobs cannot crawl (unless they can) @@ -232,9 +214,6 @@ ..() if(icon_resting && stat != DEAD) icon_state = icon_living - if(collar_type) - collar_type = "[initial(collar_type)]" - regenerate_icons() /mob/living/simple_animal/update_stat(reason = "none given") if(status_flags & GODMODE) @@ -316,10 +295,8 @@ for(var/path in butcher_results) for(var/i in 1 to butcher_results[path]) new path(Tsec) - if(pcollar) - pcollar.forceMove(drop_location()) - pcollar = null ..() + /mob/living/simple_animal/say_quote(message) var/verb = "says" @@ -353,9 +330,6 @@ density = initial(density) if(TRAIT_FLYING in initial_traits) ADD_TRAIT(src, TRAIT_FLYING, INNATE_TRAIT) - if(collar_type) - collar_type = "[initial(collar_type)]" - regenerate_icons() /mob/living/simple_animal/death(gibbed) // Only execute the below if we successfully died @@ -389,9 +363,6 @@ if(flip_on_death) transform = transform.Turn(180) density = FALSE - if(collar_type) - collar_type = "[initial(collar_type)]_dead" - regenerate_icons() /mob/living/simple_animal/proc/CanAttack(atom/the_target) if(see_invisible < the_target.invisibility) @@ -472,20 +443,11 @@ /mob/living/simple_animal/get_item_by_slot(slot_id) switch(slot_id) if(ITEM_SLOT_COLLAR) - return pcollar + return (locate(/obj/item/petcollar) in src) . = ..() /mob/living/simple_animal/can_equip(obj/item/I, slot, disable_warning = 0) - // . = ..() // Do not call parent. We do not want animals using their hand slots. - switch(slot) - if(ITEM_SLOT_COLLAR) - if(pcollar) - return FALSE - if(!can_collar) - return FALSE - if(!istype(I, /obj/item/petcollar)) - return FALSE - return TRUE + return FALSE /mob/living/simple_animal/equip_to_slot(obj/item/W, slot, initial = FALSE) if(!istype(W)) @@ -497,23 +459,15 @@ W.layer = ABOVE_HUD_LAYER W.plane = ABOVE_HUD_PLANE - switch(slot) - if(ITEM_SLOT_COLLAR) - add_collar(W) - /mob/living/simple_animal/unequip_to(obj/item/target, atom/destination, force = FALSE, silent = FALSE, drop_inventory = TRUE, no_move = FALSE) . = ..() if(!. || !target) return - if(target == pcollar) - pcollar = null - regenerate_icons() - /mob/living/simple_animal/get_access() . = ..() - if(pcollar) - . |= pcollar.GetAccess() + for(var/obj/item/petcollar/collar in contents) + . |= collar.GetAccess() /mob/living/simple_animal/update_transform() var/matrix/ntransform = matrix(transform) //aka transform.Copy() @@ -587,41 +541,6 @@ LAZYREMOVE(idle_mobs_on_old_z, src) toggle_ai(initial(AIStatus)) -/mob/living/simple_animal/proc/add_collar(obj/item/petcollar/P, mob/user) - if(!istype(P) || QDELETED(P) || pcollar) - return - if(user && !user.drop_item_to_ground(P)) - return - P.forceMove(src) - P.equipped(src) - pcollar = P - regenerate_icons() - if(user) - to_chat(user, "You put [P] around [src]'s neck.") - if(P.tagname && !unique_pet) - name = P.tagname - real_name = P.tagname - -/mob/living/simple_animal/proc/remove_collar(atom/new_loc, mob/user) - if(!pcollar) - return - - var/obj/old_collar = pcollar - - drop_item_to_ground(pcollar) - - if(user) - user.put_in_hands(old_collar) - - return old_collar - - -/mob/living/simple_animal/regenerate_icons() - cut_overlays() - if(pcollar && collar_type) - add_overlay("[collar_type]collar") - add_overlay("[collar_type]tag") - /mob/living/simple_animal/Login() ..() GLOB.move_manager.stop_looping(src) // if mob is moving under ai control, then stop AI movement @@ -639,16 +558,3 @@ /mob/living/simple_animal/proc/end_dchat_plays() stop_automated_movement = FALSE - -/mob/living/simple_animal/proc/set_can_collar(new_value) - can_collar = (new_value ? TRUE : FALSE) - if(can_collar) - add_strippable_element() - return - remove_collar(drop_location()) - RemoveElement(/datum/element/strippable) - -/mob/living/simple_animal/proc/add_strippable_element() - if(!can_collar) - return - AddElement(/datum/element/strippable, create_strippable_list(list(/datum/strippable_item/pet_collar))) diff --git a/code/modules/mob/mob_misc_procs.dm b/code/modules/mob/mob_misc_procs.dm index 2ed23918833..c19669ec952 100644 --- a/code/modules/mob/mob_misc_procs.dm +++ b/code/modules/mob/mob_misc_procs.dm @@ -5,13 +5,6 @@ return 1 return 0 -/proc/ispet(A) - if(isanimal(A)) - var/mob/living/simple_animal/SA = A - if(SA.can_collar) - return 1 - return 0 - /mob/proc/get_screen_colour() SHOULD_CALL_PARENT(TRUE) // OOC Colourblind setting takes priority over everything else. diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index 91077b379f9..be67faed004 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -253,7 +253,7 @@ SM.faction = user.faction SM.master_commander = user SM.sentience_act() - SM.set_can_collar(TRUE) + SM.AddElement(/datum/element/wears_collar) to_chat(SM, "All at once it makes sense: you know what you are and who you are! Self awareness is yours!") to_chat(SM, "You are grateful to be self aware and owe [user] a great debt. Serve [user], and assist [user.p_them()] in completing [user.p_their()] goals at any cost.") if(SM.flags_2 & HOLOGRAM_2) //Check to see if it's a holodeck creature @@ -322,7 +322,7 @@ SM.universal_speak = TRUE SM.faction = user.faction SM.sentience_act() //Same deal here as with sentience - SM.set_can_collar(TRUE) + SM.AddElement(/datum/element/wears_collar) user.death() to_chat(SM, "In a quick flash, you feel your consciousness flow into [SM]!") to_chat(SM, "You are now [SM]. Your allegiances, alliances, and roles are still the same as they were prior to consciousness transfer!") diff --git a/code/tests/game_tests.dm b/code/tests/game_tests.dm index 7da27df4b8d..38fa65a7be4 100644 --- a/code/tests/game_tests.dm +++ b/code/tests/game_tests.dm @@ -47,6 +47,7 @@ #include "test_subsystem_init.dm" #include "test_subsystem_metric_sanity.dm" #include "test_timer_sanity.dm" +#include "test_wears_collar.dm" #endif #ifdef MAP_TESTS diff --git a/code/tests/test_wears_collar.dm b/code/tests/test_wears_collar.dm new file mode 100644 index 00000000000..46a473d89e7 --- /dev/null +++ b/code/tests/test_wears_collar.dm @@ -0,0 +1,24 @@ +/datum/game_test/wears_collar/Run() + var/datum/test_puppeteer/player = new(src) + + var/obj/chair = player.spawn_obj_nearby(/obj/structure/chair) + var/mob/corgi = player.spawn_mob_nearby(/mob/living/simple_animal/pet/dog/corgi) + chair.buckle_mob(corgi) // So it doesn't wander off + + var/obj/item/petcollar/collar = player.spawn_obj_in_hand(/obj/item/petcollar) + player.click_on(corgi) + TEST_ASSERT(collar in corgi, "Collar not placed on corgi") + TEST_ASSERT_EQUAL(corgi.name, "corgi", "animal name not preserved") + + qdel(collar) + qdel(corgi) + + corgi = player.spawn_mob_nearby(/mob/living/simple_animal/pet/dog/corgi) + chair.buckle_mob(corgi) // So it doesn't wander off + + collar = player.spawn_obj_in_hand(/obj/item/petcollar) + collar.tagname = "Bucephalus" + player.click_on(corgi) + TEST_ASSERT(collar in corgi, "Collar not placed on corgi") + TEST_ASSERT_EQUAL(corgi.name, "Bucephalus", "animal name not changed") + diff --git a/icons/mob/pets.dmi b/icons/mob/pets.dmi index 66e406c42c5..9352135ba5f 100644 Binary files a/icons/mob/pets.dmi and b/icons/mob/pets.dmi differ diff --git a/paradise.dme b/paradise.dme index 4da7f497ee5..c69c1fef5cd 100644 --- a/paradise.dme +++ b/paradise.dme @@ -601,6 +601,7 @@ #include "code\datums\elements\shatters_when_thrown.dm" #include "code\datums\elements\strippable.dm" #include "code\datums\elements\waddling.dm" +#include "code\datums\elements\wears_collar.dm" #include "code\datums\helper_datums\async_input.dm" #include "code\datums\helper_datums\construction_datum.dm" #include "code\datums\helper_datums\icon_snapshot.dm"