diff --git a/code/__DEFINES/basic_mobs.dm b/code/__DEFINES/basic_mobs.dm index e519fb25240..6696da857c8 100644 --- a/code/__DEFINES/basic_mobs.dm +++ b/code/__DEFINES/basic_mobs.dm @@ -33,6 +33,9 @@ ///keeps track of how many gutlunches are born GLOBAL_VAR_INIT(gutlunch_count, 0) +///Pet customization settings saved for every client +GLOBAL_LIST_EMPTY(customized_pets) + //raptor defines #define RAPTOR_RED "Red" diff --git a/code/datums/greyscale/config_types/greyscale_configs/greyscale_items.dm b/code/datums/greyscale/config_types/greyscale_configs/greyscale_items.dm index 2137aea08f3..62f30855abb 100644 --- a/code/datums/greyscale/config_types/greyscale_configs/greyscale_items.dm +++ b/code/datums/greyscale/config_types/greyscale_configs/greyscale_items.dm @@ -229,6 +229,21 @@ icon_file = 'icons/obj/toys/plushes.dmi' json_config = 'code/datums/greyscale/json_configs/plushie_carp.json' +/datum/greyscale_config/pet_carrier + name = "Pet Carrier" + icon_file = 'icons/obj/pet_carrier.dmi' + json_config = 'code/datums/greyscale/json_configs/pet_carrier.json' + +/datum/greyscale_config/pet_carrier_inhands_left + name = "Pet Carrier Left" + icon_file = 'icons/mob/inhands/items_lefthand.dmi' + json_config = 'code/datums/greyscale/json_configs/pet_carrier_inhands.json' + +/datum/greyscale_config/pet_carrier_inhands_right + name = "Pet Carrier Right" + icon_file = 'icons/mob/inhands/items_righthand.dmi' + json_config = 'code/datums/greyscale/json_configs/pet_carrier_inhands.json' + /datum/greyscale_config/plush_lizard name = "Plushie Lizard" icon_file = 'icons/obj/toys/plushes.dmi' diff --git a/code/datums/greyscale/json_configs/bandanaskull_inhands.json b/code/datums/greyscale/json_configs/bandanaskull_inhands.json index b7067cf3c2e..40fade07973 100644 --- a/code/datums/greyscale/json_configs/bandanaskull_inhands.json +++ b/code/datums/greyscale/json_configs/bandanaskull_inhands.json @@ -14,3 +14,4 @@ } ] } + diff --git a/code/datums/greyscale/json_configs/pet_carrier.json b/code/datums/greyscale/json_configs/pet_carrier.json new file mode 100644 index 00000000000..1674765268f --- /dev/null +++ b/code/datums/greyscale/json_configs/pet_carrier.json @@ -0,0 +1,87 @@ +{ + "pet_carrier_open": [ + { + "type": "icon_state", + "icon_state": "pet_carrier_open", + "blend_mode": "overlay" + }, + { + "type": "icon_state", + "icon_state": "pet_carrier_gags", + "blend_mode": "overlay", + "color_ids": [ 1 ] + } + ], + "pet_carrier_closed_unlocked": [ + { + "type": "icon_state", + "icon_state": "pet_carrier_unlocked", + "blend_mode": "overlay" + }, + { + "type": "icon_state", + "icon_state": "pet_carrier_closed", + "blend_mode": "overlay" + }, + { + "type": "icon_state", + "icon_state": "pet_carrier_gags", + "blend_mode": "overlay", + "color_ids": [ 1 ] + } + ], + "pet_carrier_closed_locked": [ + { + "type": "icon_state", + "icon_state": "pet_carrier_locked", + "blend_mode": "overlay" + }, + { + "type": "icon_state", + "icon_state": "pet_carrier_closed", + "blend_mode": "overlay" + }, + { + "type": "icon_state", + "icon_state": "pet_carrier_gags", + "blend_mode": "overlay", + "color_ids": [ 1 ] + } + ], + "pet_carrier_occupied_unlocked": [ + { + "type": "icon_state", + "icon_state": "pet_carrier_unlocked", + "blend_mode": "overlay" + }, + { + "type": "icon_state", + "icon_state": "pet_carrier_occupied", + "blend_mode": "overlay" + }, + { + "type": "icon_state", + "icon_state": "pet_carrier_gags", + "blend_mode": "overlay", + "color_ids": [ 1 ] + } + ], + "pet_carrier_occupied_locked": [ + { + "type": "icon_state", + "icon_state": "pet_carrier_locked", + "blend_mode": "overlay" + }, + { + "type": "icon_state", + "icon_state": "pet_carrier_occupied", + "blend_mode": "overlay" + }, + { + "type": "icon_state", + "icon_state": "pet_carrier_gags", + "blend_mode": "overlay", + "color_ids": [ 1 ] + } + ] +} diff --git a/code/datums/greyscale/json_configs/pet_carrier_inhands.json b/code/datums/greyscale/json_configs/pet_carrier_inhands.json new file mode 100644 index 00000000000..05978e3c829 --- /dev/null +++ b/code/datums/greyscale/json_configs/pet_carrier_inhands.json @@ -0,0 +1,15 @@ +{ + "pet_carrier": [ + { + "type": "icon_state", + "icon_state": "pet_carrier", + "blend_mode": "overlay" + }, + { + "type": "icon_state", + "icon_state": "pet_carrier_gags", + "blend_mode": "overlay", + "color_ids": [ 1 ] + } + ] +} diff --git a/code/datums/station_traits/neutral_traits.dm b/code/datums/station_traits/neutral_traits.dm index 74a8e542400..2fb7072f55d 100644 --- a/code/datums/station_traits/neutral_traits.dm +++ b/code/datums/station_traits/neutral_traits.dm @@ -544,3 +544,47 @@ dynamic_category = RULESET_CATEGORY_NO_WITTING_CREW_ANTAGONISTS threat_reduction = 15 dynamic_threat_id = "Background Checks" + + +/datum/station_trait/pet_day + name = "Bring Your Pet To Work Day" + trait_type = STATION_TRAIT_NEUTRAL + show_in_report = FALSE + weight = 2 + sign_up_button = TRUE + +/datum/station_trait/pet_day/New() + . = ..() + RegisterSignal(SSdcs, COMSIG_GLOB_JOB_AFTER_SPAWN, PROC_REF(on_job_after_spawn)) + +/datum/station_trait/pet_day/setup_lobby_button(atom/movable/screen/lobby/button/sign_up/lobby_button) + lobby_button.desc = "Want to bring your innocent pet to a giant metal deathtrap? Click here to customize it!" + RegisterSignal(lobby_button, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_lobby_button_update_overlays)) + return ..() + +/datum/station_trait/pet_day/can_display_lobby_button(client/player) + return sign_up_button + +/datum/station_trait/pet_day/on_round_start() + return + +/datum/station_trait/pet_day/on_lobby_button_click(atom/movable/screen/lobby/button/sign_up/lobby_button, updates) + var/mob/our_player = lobby_button.get_mob() + var/client/player_client = our_player.client + if(isnull(player_client)) + return + var/datum/pet_customization/customization = GLOB.customized_pets[REF(player_client)] + if(isnull(customization)) + customization = new(player_client) + INVOKE_ASYNC(customization, TYPE_PROC_REF(/datum, ui_interact), our_player) + +/datum/station_trait/pet_day/proc/on_job_after_spawn(datum/source, datum/job/job, mob/living/spawned, client/player_client) + SIGNAL_HANDLER + + var/datum/pet_customization/customization = GLOB.customized_pets[REF(player_client)] + if(isnull(customization)) + return + INVOKE_ASYNC(customization, TYPE_PROC_REF(/datum/pet_customization, create_pet), spawned, player_client) + +/datum/station_trait/pet_day/proc/on_lobby_button_update_overlays(atom/movable/screen/lobby/button/sign_up/lobby_button, list/overlays) + overlays += "select_pet" diff --git a/code/game/objects/items/pet_carrier.dm b/code/game/objects/items/pet_carrier.dm index 8be08a876d7..336d177f2a2 100644 --- a/code/game/objects/items/pet_carrier.dm +++ b/code/game/objects/items/pet_carrier.dm @@ -11,6 +11,10 @@ inhand_icon_state = "pet_carrier" lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' righthand_file = 'icons/mob/inhands/items_righthand.dmi' + greyscale_config = /datum/greyscale_config/pet_carrier + greyscale_config_inhand_left = /datum/greyscale_config/pet_carrier_inhands_left + greyscale_config_inhand_right = /datum/greyscale_config/pet_carrier_inhands_right + greyscale_colors = COLOR_BLUE force = 5 attack_verb_continuous = list("bashes", "carries") attack_verb_simple = list("bash", "carry") @@ -19,7 +23,6 @@ throw_range = 3 custom_materials = list(/datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT * 7.5, /datum/material/glass = SMALL_MATERIAL_AMOUNT) interaction_flags_mouse_drop = NEED_DEXTERITY - var/open = TRUE var/locked = FALSE var/list/occupants = list() @@ -147,14 +150,9 @@ if(open) icon_state = initial(icon_state) return ..() - icon_state = "[base_icon_state]_[!occupants.len ? "closed" : "occupied"]" + icon_state = "[base_icon_state]_[!occupants.len ? "closed" : "occupied"]_[locked ? "locked" : "unlocked"]" return ..() -/obj/item/pet_carrier/update_overlays() - . = ..() - if(!open) - . += "[base_icon_state]_[locked ? "" : "un"]locked" - /obj/item/pet_carrier/mouse_drop_dragged(atom/over_atom, mob/user, src_location, over_location, params) if(isopenturf(over_atom) && open && occupants.len) user.visible_message(span_notice("[user] unloads [src]."), \ @@ -202,5 +200,9 @@ base_icon_state = "biopod" icon_state = "biopod_open" inhand_icon_state = "biopod" + greyscale_config = null + greyscale_config_inhand_left = null + greyscale_config_inhand_right = null + greyscale_colors = null #undef pet_carrier_full diff --git a/code/modules/mob/living/basic/pets/cat/cat.dm b/code/modules/mob/living/basic/pets/cat/cat.dm index 7394d1fd5c5..c911fa64c20 100644 --- a/code/modules/mob/living/basic/pets/cat/cat.dm +++ b/code/modules/mob/living/basic/pets/cat/cat.dm @@ -45,6 +45,14 @@ /obj/item/food/deadmouse, /obj/item/food/fishmeat, ) + ///list of pet commands we follow + var/static/list/pet_commands = list( + /datum/pet_command/idle, + /datum/pet_command/free, + /datum/pet_command/follow, + /datum/pet_command/perform_trick_sequence, + ) + ///item we are currently holding var/obj/item/held_food ///mutable appearance for held item @@ -74,6 +82,7 @@ /mob/living/basic/pet/cat/Initialize(mapload) . = ..() + AddComponent(/datum/component/obeys_commands, pet_commands) AddElement(/datum/element/cultist_pet, pet_cult_icon_state = cult_icon_state) AddElement(/datum/element/wears_collar, collar_icon_state = collar_icon_state, collar_resting_icon_state = TRUE) AddElement(/datum/element/ai_retaliate) @@ -224,3 +233,9 @@ name = "Jerry" desc = "Tom is VERY amused." gender = MALE + +/mob/living/basic/pet/cat/tabby + icon_state = "cat" + icon_living = "cat" + icon_dead = "cat_dead" + held_state = "cat" diff --git a/code/modules/mob/living/basic/pets/cat/cat_ai.dm b/code/modules/mob/living/basic/pets/cat/cat_ai.dm index 8dde1049568..2a2d3204bad 100644 --- a/code/modules/mob/living/basic/pets/cat/cat_ai.dm +++ b/code/modules/mob/living/basic/pets/cat/cat_ai.dm @@ -1,6 +1,7 @@ /datum/ai_controller/basic_controller/cat blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, BB_HOSTILE_MEOWS = list("Mawwww", "Mrewwww", "mhhhhng..."), BB_BABIES_PARTNER_TYPES = list(/mob/living/basic/pet/cat), BB_BABIES_CHILD_TYPES = list(/mob/living/basic/pet/cat/kitten), @@ -9,6 +10,7 @@ ai_movement = /datum/ai_movement/basic_avoidance idle_behavior = /datum/idle_behavior/idle_random_walk planning_subtrees = list( + /datum/ai_planning_subtree/pet_planning, /datum/ai_planning_subtree/reside_in_home, /datum/ai_planning_subtree/flee_target/from_flee_key/cat_struggle, /datum/ai_planning_subtree/find_and_hunt_target/hunt_mice, diff --git a/code/modules/mob/living/basic/pets/cat/kitten_ai.dm b/code/modules/mob/living/basic/pets/cat/kitten_ai.dm index b694235a39e..4136aa0aa01 100644 --- a/code/modules/mob/living/basic/pets/cat/kitten_ai.dm +++ b/code/modules/mob/living/basic/pets/cat/kitten_ai.dm @@ -3,10 +3,12 @@ blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_HUNGRY_MEOW = list("mrrp...", "mraw..."), + BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, BB_MAX_DISTANCE_TO_FOOD = 2, ) planning_subtrees = list( + /datum/ai_planning_subtree/pet_planning, /datum/ai_planning_subtree/target_retaliate, /datum/ai_planning_subtree/flee_target, /datum/ai_planning_subtree/beg_human, diff --git a/code/modules/mob/living/basic/pets/dog/_dog.dm b/code/modules/mob/living/basic/pets/dog/_dog.dm index 1ff991941d3..35996d4d77c 100644 --- a/code/modules/mob/living/basic/pets/dog/_dog.dm +++ b/code/modules/mob/living/basic/pets/dog/_dog.dm @@ -40,6 +40,7 @@ /datum/pet_command/free, /datum/pet_command/good_boy/dog, /datum/pet_command/follow/dog, + /datum/pet_command/perform_trick_sequence, /datum/pet_command/point_targeting/attack/dog, /datum/pet_command/point_targeting/fetch, /datum/pet_command/play_dead, diff --git a/code/modules/mob/living/basic/pets/fox.dm b/code/modules/mob/living/basic/pets/fox.dm index a442f607158..c45ff27bbc3 100644 --- a/code/modules/mob/living/basic/pets/fox.dm +++ b/code/modules/mob/living/basic/pets/fox.dm @@ -28,9 +28,19 @@ attack_sound = 'sound/items/weapons/bite.ogg' attack_vis_effect = ATTACK_EFFECT_BITE ai_controller = /datum/ai_controller/basic_controller/fox + ///list of our pet commands we follow + var/static/list/pet_commands = list( + /datum/pet_command/idle, + /datum/pet_command/free, + /datum/pet_command/follow, + /datum/pet_command/point_targeting/attack, + /datum/pet_command/perform_trick_sequence, + ) + /mob/living/basic/pet/fox/Initialize(mapload) . = ..() + AddComponent(/datum/component/obeys_commands, pet_commands) AddElement(/datum/element/cultist_pet) AddElement(/datum/element/wears_collar) AddElement(/datum/element/pet_bonus, "pants and yaps happily!") @@ -42,12 +52,14 @@ blackboard = list( BB_ALWAYS_IGNORE_FACTION = TRUE, BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/of_size/ours_or_smaller, + BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, BB_FLEE_TARGETING_STRATEGY = /datum/targeting_strategy/basic, ) ai_movement = /datum/ai_movement/basic_avoidance idle_behavior = /datum/idle_behavior/idle_random_walk planning_subtrees = list( + /datum/ai_planning_subtree/pet_planning, /datum/ai_planning_subtree/target_retaliate/to_flee, /datum/ai_planning_subtree/flee_target/from_flee_key, /datum/ai_planning_subtree/simple_find_target/not_while_observed, diff --git a/code/modules/mob/living/basic/pets/parrot/_parrot.dm b/code/modules/mob/living/basic/pets/parrot/_parrot.dm index e61b0522179..e22948848eb 100644 --- a/code/modules/mob/living/basic/pets/parrot/_parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/_parrot.dm @@ -76,6 +76,13 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( var/static/list/edibles = list( /obj/item/food/cracker, ) + ///list of commands we follow + var/static/list/pet_commands = list( + /datum/pet_command/idle, + /datum/pet_command/free, + /datum/pet_command/follow, + /datum/pet_command/perform_trick_sequence, + ) /// Tracks the times when we send a phrase through either being pet or attack to ensure it's not abused to flood COOLDOWN_DECLARE(forced_speech_cooldown) @@ -92,7 +99,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( AddElement(/datum/element/simple_flying) AddComponent(/datum/component/listen_and_repeat, desired_phrases = get_static_list_of_phrases(), blackboard_key = BB_PARROT_REPEAT_STRING) AddComponent(/datum/component/tameable, food_types = edibles, tame_chance = 100, bonus_tame_chance = 0) - + AddComponent(/datum/component/obeys_commands, pet_commands) RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(pre_attacking)) RegisterSignal(src, COMSIG_MOB_CLICKON, PROC_REF(on_click)) RegisterSignal(src, COMSIG_ATOM_ATTACKBY_SECONDARY, PROC_REF(on_attacked)) // this means we could have a peaceful interaction, like getting a cracker diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/_parrot_controller.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/_parrot_controller.dm index 8a0fe5e157d..2d6bccbc703 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_ai/_parrot_controller.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/_parrot_controller.dm @@ -1,6 +1,7 @@ /datum/ai_controller/basic_controller/parrot blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/allow_items, + BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, BB_HOARD_LOCATION_RANGE = 9, ) @@ -9,11 +10,12 @@ idle_behavior = /datum/idle_behavior/idle_random_walk/parrot planning_subtrees = list( + /datum/ai_planning_subtree/parrot_as_in_repeat, // always get a witty oneliner in when you can + /datum/ai_planning_subtree/pet_planning, /datum/ai_planning_subtree/target_retaliate, /datum/ai_planning_subtree/perch_on_target, /datum/ai_planning_subtree/basic_melee_attack_subtree, /datum/ai_planning_subtree/hoard_items, - /datum/ai_planning_subtree/parrot_as_in_repeat, // always get a witty oneliner in when you can ) /datum/idle_behavior/idle_random_walk/parrot diff --git a/code/modules/mob/living/basic/pets/pet_designer.dm b/code/modules/mob/living/basic/pets/pet_designer.dm new file mode 100644 index 00000000000..a0cc7ca4018 --- /dev/null +++ b/code/modules/mob/living/basic/pets/pet_designer.dm @@ -0,0 +1,205 @@ +#define PET_OPTION_DOG "Dog" +#define PET_OPTION_CAT "Cat" +#define PET_OPTION_FOX "Fox" +#define PET_OPTION_VERMIN "Vermin" +#define PET_OPTION_BIRD "Bird" + +GLOBAL_LIST_INIT(pet_options, list( + PET_OPTION_DOG = list( + /mob/living/basic/pet/dog/corgi, + /mob/living/basic/pet/dog/pug, + /mob/living/basic/pet/dog/bullterrier, + /mob/living/basic/pet/dog/corgi/puppy, + /mob/living/basic/pet/dog/corgi/exoticcorgi, + ), + PET_OPTION_CAT = list( + /mob/living/basic/pet/cat/tabby, + /mob/living/basic/pet/cat, + /mob/living/basic/pet/cat/kitten, + ), + PET_OPTION_FOX = list( + /mob/living/basic/pet/fox, + ), + PET_OPTION_VERMIN = list( + /mob/living/basic/mothroach, + /mob/living/basic/spider/maintenance, + /mob/living/basic/mouse, + ), + PET_OPTION_BIRD = list( + /mob/living/basic/parrot, + ), +)) + +/datum/pet_customization + ///our selected path type + var/mob/living/basic/selected_path = /mob/living/basic/pet/dog/corgi + ///current selected specie + var/pet_specie = PET_OPTION_DOG + ///custom name to apply to our pet + var/custom_name + ///type of pet carrier to give our pet + var/pet_carrier_path + ///pet collar color to give our pet + var/pet_collar_type + ///our pet's gender + var/pet_gender = MALE + ///trick name of our pet! + var/pet_trick_name = "Trick" + ///our pet's trick moves + var/list/pet_trick_moves + ///our selected carrier + var/pet_carrier + ///our cached pet carrier icons + var/static/list/custom_pet_carriers + ///possible emotes our pet can run + var/static/list/pet_possible_emotes = list( + /datum/emote/flip, + /datum/emote/jump, + /datum/emote/spin, + ) + ///list of carrier colors we can pick from + var/static/list/possible_colors = list( + "Blue" = COLOR_BLUE, + "Red" = COLOR_RED, + "Yellow" = COLOR_YELLOW, + "Green" = COLOR_GREEN, + ) + +/datum/pet_customization/New(client/player_client) + . = ..() + if(isnull(custom_pet_carriers)) + custom_pet_carriers = setup_pet_carriers() + pet_carrier = possible_colors[1] + GLOB.customized_pets[REF(player_client)] = src + +/datum/pet_customization/proc/setup_pet_carriers() + var/list/list_to_return = list() + + var/obj/item/pet_carrier/demo_carrier = new() + demo_carrier.open = FALSE + demo_carrier.update_appearance() + + for(var/color in possible_colors) + demo_carrier.set_greyscale(possible_colors[color]) + list_to_return[color] = icon2base64(getFlatIcon(demo_carrier)) + qdel(demo_carrier) + return list_to_return + +/datum/pet_customization/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "PetBuilder") + ui.open() + +/datum/pet_customization/ui_data(mob/user) + var/list/data = list() + data["pet_name"] = custom_name + data["pet_path"] = selected_path + data["pet_gender"] = pet_gender + data["pet_trick_name"] = pet_trick_name + data["pet_specie"] = pet_specie + data["pet_types"] = list() + data["pet_options"] = list() + for(var/pet_option in GLOB.pet_options) + data["pet_types"] += pet_option + data["pet_options"] += retrieve_pet_options(pet_option, GLOB.pet_options[pet_option]) + data["pet_carrier"] = pet_carrier + + data["carrier_options"] = list() + for(var/carrier_option in custom_pet_carriers) + data["carrier_options"] += list(list( + "carrier_color" = carrier_option, + "carrier_icon" = custom_pet_carriers[carrier_option], + )) + + data["pet_possible_emotes"] = list() + for(var/datum/emote/emote as anything in pet_possible_emotes) + data["pet_possible_emotes"] += emote.key + + return data + +/datum/pet_customization/ui_state(mob/user) + return GLOB.always_state + +/datum/pet_customization/proc/retrieve_pet_options(pet_specie, list/input_list) + var/list/pet_options = list() + for(var/mob/living/pet_type as anything in input_list) + pet_options += list(list( + "pet_specie" = pet_specie, + "pet_name" = pet_type::name, + "pet_icon" = pet_type:icon, + "pet_path" = pet_type, + "pet_icon_state" = pet_type::icon_state, + )) + return pet_options + +/datum/pet_customization/ui_act(action, params, datum/tgui/ui) + . = ..() + switch(action) + if("finalize_pet") + + var/pet_type = text2path(params["selected_path"]) + for(var/pet_category in GLOB.pet_options) + var/list/pet_list = GLOB.pet_options[pet_category] + if(pet_list.Find(pet_type)) + selected_path = pet_type + break + + var/trick_name = params["selected_trick_name"] + if(trick_name && sanitize_name(trick_name)) + pet_trick_name = trick_name + + var/pet_name = params["selected_pet_name"] + if(pet_name && sanitize_name(pet_name)) + custom_name = pet_name + + switch(params["selected_gender"]) + if("male") + pet_gender = MALE + if("female") + pet_gender = FEMALE + if("neuter") + pet_gender = NEUTER + + var/list/trick_moves = params["selected_trick_moves"] + if(length(trick_moves)) + pet_trick_moves = trick_moves + + var/selected_color = params["selected_carrier"] + if(!isnull(selected_color)) + pet_carrier = selected_color + + var/selected_specie = params["selected_specie"] + if(!isnull(selected_specie)) + pet_specie = selected_specie + ui.close() + + return TRUE + +/datum/pet_customization/proc/create_pet(mob/living/spawned, client/player_client) + var/obj/item/pet_carrier/carrier = new(get_turf(spawned)) + carrier.open = FALSE + carrier.update_appearance() + var/final_color = possible_colors[pet_carrier] || COLOR_BLUE + carrier.set_greyscale(final_color) + var/mob/living/our_pet = new selected_path(get_turf(spawned)) //spawning these in nullspace leads to runtimes + our_pet.gender = pet_gender + + if(custom_name) + our_pet.fully_replace_character_name(our_pet.name, custom_name) + + if(pet_trick_name) + our_pet.ai_controller.set_blackboard_key(BB_TRICK_NAME, pet_trick_name) + if(pet_trick_moves) + our_pet.ai_controller.override_blackboard_key(BB_TRICK_SEQUENCE, pet_trick_moves) + our_pet.befriend(spawned) + carrier.add_occupant(our_pet) + spawned.put_in_hands(carrier, forced = TRUE) + GLOB.customized_pets -= REF(player_client) + qdel(src) + +#undef PET_OPTION_DOG +#undef PET_OPTION_CAT +#undef PET_OPTION_FOX +#undef PET_OPTION_VERMIN +#undef PET_OPTION_BIRD diff --git a/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_ai.dm b/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_ai.dm index ae971fd2558..18ae6fc3e75 100644 --- a/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_ai.dm +++ b/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_ai.dm @@ -44,11 +44,13 @@ /datum/ai_controller/basic_controller/giant_spider/pest blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/of_size/ours_or_smaller, // Hunt mobs our size + BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, BB_FLEE_TARGETING_STRATEGY = /datum/targeting_strategy/basic/of_size/larger, // Run away from mobs bigger than we are ) idle_behavior = /datum/idle_behavior/idle_random_walk planning_subtrees = list( + /datum/ai_planning_subtree/pet_planning, /datum/ai_planning_subtree/target_retaliate/to_flee, /datum/ai_planning_subtree/flee_target/from_flee_key, /datum/ai_planning_subtree/simple_find_target, diff --git a/code/modules/mob/living/basic/space_fauna/spider/spider.dm b/code/modules/mob/living/basic/space_fauna/spider/spider.dm index 118487f0383..9f94d839a76 100644 --- a/code/modules/mob/living/basic/space_fauna/spider/spider.dm +++ b/code/modules/mob/living/basic/space_fauna/spider/spider.dm @@ -171,10 +171,18 @@ response_harm_simple = "splat" ai_controller = /datum/ai_controller/basic_controller/giant_spider/pest apply_spider_antag = FALSE + ///list of pet commands we follow + var/static/list/pet_commands = list( + /datum/pet_command/idle, + /datum/pet_command/free, + /datum/pet_command/follow, + /datum/pet_command/perform_trick_sequence, + ) /mob/living/basic/spider/maintenance/Initialize(mapload) . = ..() ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT) AddElement(/datum/element/web_walker, /datum/movespeed_modifier/average_web) AddElement(/datum/element/ai_retaliate) + AddComponent(/datum/component/obeys_commands, pet_commands) AddElement(/datum/element/tiny_mob_hunter) diff --git a/code/modules/mob/living/basic/vermin/mothroach/mothroach.dm b/code/modules/mob/living/basic/vermin/mothroach/mothroach.dm index 9659408b8b1..d0605d4dc93 100644 --- a/code/modules/mob/living/basic/vermin/mothroach/mothroach.dm +++ b/code/modules/mob/living/basic/vermin/mothroach/mothroach.dm @@ -33,11 +33,19 @@ faction = list(FACTION_NEUTRAL) ai_controller = /datum/ai_controller/basic_controller/mothroach + ///list of pet commands we follow + var/static/list/pet_commands = list( + /datum/pet_command/idle, + /datum/pet_command/free, + /datum/pet_command/follow, + /datum/pet_command/perform_trick_sequence, + ) /mob/living/basic/mothroach/Initialize(mapload) . = ..() var/static/list/food_types = list(/obj/item/clothing) AddElement(/datum/element/basic_eating, food_types = food_types) + AddComponent(/datum/component/obeys_commands, pet_commands) ai_controller.set_blackboard_key(BB_BASIC_FOODS, typecacheof(food_types)) AddElement(/datum/element/ai_retaliate) AddElement(/datum/element/pet_bonus, "squeaks happily!") diff --git a/code/modules/mob/living/basic/vermin/mothroach/mothroach_ai.dm b/code/modules/mob/living/basic/vermin/mothroach/mothroach_ai.dm index 5801d183215..bed72a98239 100644 --- a/code/modules/mob/living/basic/vermin/mothroach/mothroach_ai.dm +++ b/code/modules/mob/living/basic/vermin/mothroach/mothroach_ai.dm @@ -3,6 +3,7 @@ /datum/ai_controller/basic_controller/mothroach blackboard = list( BB_FLEE_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/allow_items, ) @@ -10,6 +11,7 @@ ai_movement = /datum/ai_movement/basic_avoidance idle_behavior = /datum/idle_behavior/idle_random_walk planning_subtrees = list( + /datum/ai_planning_subtree/pet_planning, /datum/ai_planning_subtree/find_food/mothroach, /datum/ai_planning_subtree/target_retaliate/to_flee, /datum/ai_planning_subtree/flee_target/from_flee_key, diff --git a/code/modules/mob/living/basic/vermin/mouse.dm b/code/modules/mob/living/basic/vermin/mouse.dm index cb62ad956f9..aba7c00d4c7 100644 --- a/code/modules/mob/living/basic/vermin/mouse.dm +++ b/code/modules/mob/living/basic/vermin/mouse.dm @@ -36,6 +36,13 @@ var/contributes_to_ratcap = TRUE /// Probability that, if we successfully bite a shocked cable, that we will die to it. var/cable_zap_prob = 85 + ///list of pet commands we follow + var/static/list/pet_commands = list( + /datum/pet_command/idle, + /datum/pet_command/free, + /datum/pet_command/follow, + /datum/pet_command/perform_trick_sequence, + ) /mob/living/basic/mouse/Initialize(mapload, tame = FALSE, new_body_color) . = ..() @@ -55,6 +62,7 @@ var/static/list/loc_connections = list( COMSIG_ATOM_ENTERED = PROC_REF(on_entered), ) + AddComponent(/datum/component/obeys_commands, pet_commands) AddElement(/datum/element/connect_loc, loc_connections) make_tameable() AddComponent(/datum/component/swarming, 16, 16) //max_x, max_y @@ -383,9 +391,8 @@ /// The mouse AI controller /datum/ai_controller/basic_controller/mouse blackboard = list( // Always cowardly - BB_CURRENT_HUNTING_TARGET = null, // cheese - BB_LOW_PRIORITY_HUNTING_TARGET = null, // cable BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, // Use this to find people to run away from + BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, BB_BASIC_MOB_FLEE_DISTANCE = 3, ) @@ -393,6 +400,7 @@ ai_movement = /datum/ai_movement/basic_avoidance idle_behavior = /datum/idle_behavior/idle_random_walk planning_subtrees = list( + /datum/ai_planning_subtree/pet_planning, // Top priority is to look for and execute hunts for cheese even if someone is looking at us /datum/ai_planning_subtree/find_and_hunt_target/look_for_cheese, // Next priority is see if anyone is looking at us diff --git a/icons/hud/lobby/signup_button.dmi b/icons/hud/lobby/signup_button.dmi index e015fa71948..26d32e9af83 100644 Binary files a/icons/hud/lobby/signup_button.dmi and b/icons/hud/lobby/signup_button.dmi differ diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi index ebcfea59675..f38ce001619 100644 Binary files a/icons/mob/inhands/items_lefthand.dmi and b/icons/mob/inhands/items_lefthand.dmi differ diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi index b8fd65ec072..4639b290fe3 100644 Binary files a/icons/mob/inhands/items_righthand.dmi and b/icons/mob/inhands/items_righthand.dmi differ diff --git a/icons/obj/pet_carrier.dmi b/icons/obj/pet_carrier.dmi index 0a2b4f6c430..366fddd4144 100644 Binary files a/icons/obj/pet_carrier.dmi and b/icons/obj/pet_carrier.dmi differ diff --git a/tgstation.dme b/tgstation.dme index f7f1ba6df14..7cd2ddfbf85 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -5079,6 +5079,7 @@ #include "code\modules\mob\living\basic\pets\fox.dm" #include "code\modules\mob\living\basic\pets\penguin.dm" #include "code\modules\mob\living\basic\pets\pet.dm" +#include "code\modules\mob\living\basic\pets\pet_designer.dm" #include "code\modules\mob\living\basic\pets\sloth.dm" #include "code\modules\mob\living\basic\pets\cat\bread_cat_ai.dm" #include "code\modules\mob\living\basic\pets\cat\cat.dm" diff --git a/tgui/packages/tgui/interfaces/LootPanel/IconDisplay.tsx b/tgui/packages/tgui/interfaces/LootPanel/IconDisplay.tsx index fb792f907f2..a214a0a969e 100644 --- a/tgui/packages/tgui/interfaces/LootPanel/IconDisplay.tsx +++ b/tgui/packages/tgui/interfaces/LootPanel/IconDisplay.tsx @@ -4,11 +4,18 @@ import { SearchItem } from './types'; type Props = { item: SearchItem; + size: Size; +}; + +type Size = { + height: number; + width: number; }; export function IconDisplay(props: Props) { const { item: { icon, icon_state }, + size: { height, width }, } = props; const fallback = ; @@ -27,8 +34,8 @@ export function IconDisplay(props: Props) { fallback={fallback} icon={icon} icon_state={icon_state} - height={3} - width={3} + height={height} + width={width} /> ); } diff --git a/tgui/packages/tgui/interfaces/LootPanel/LootBox.tsx b/tgui/packages/tgui/interfaces/LootPanel/LootBox.tsx index 3fb94324b6a..b4fafcf5703 100644 --- a/tgui/packages/tgui/interfaces/LootPanel/LootBox.tsx +++ b/tgui/packages/tgui/interfaces/LootPanel/LootBox.tsx @@ -56,7 +56,7 @@ export function LootBox(props: Props) { }); }} > - + {amount > 1 &&
{amount}
} {!is_blind && {name}} diff --git a/tgui/packages/tgui/interfaces/PetBuilder.tsx b/tgui/packages/tgui/interfaces/PetBuilder.tsx new file mode 100644 index 00000000000..289281ade7f --- /dev/null +++ b/tgui/packages/tgui/interfaces/PetBuilder.tsx @@ -0,0 +1,431 @@ +import { useState } from 'react'; + +import { useBackend } from '../backend'; +import { + Box, + Button, + Dropdown, + Flex, + Icon, + Image, + Input, + LabeledList, + Section, + Stack, +} from '../components'; +import { Window } from '../layouts'; +import { IconDisplay } from './LootPanel/IconDisplay'; +type Data = { + pet_name: string | null; + pet_specie: string; + pet_path: string; + pet_gender: string; + pet_types: string[]; + pet_trick_name: string; + pet_options: PetOptions[]; + pet_carrier: string; + carrier_options: CarrierOptions[]; + pet_possible_emotes: String[]; +}; + +enum PetGender { + male = 'male', + female = 'female', + neuter = 'neuter', +} + +type CarrierOptions = { + carrier_color: string; + carrier_icon: string; +}; + +type PetOptions = { + pet_specie: string; + pet_name: string; + pet_icon: string; + pet_path: string; + pet_icon_state: string; +}; + +const RetrievePet = (pet_path: string, pet_options: PetOptions[]) => { + return pet_options.find((option) => option.pet_path === pet_path); +}; + +const RetrieveCarrier = (color: string, carrier_options: CarrierOptions[]) => { + return carrier_options.find((option) => option.carrier_color === color); +}; + +const FilterPetList = (pet_specie: string, pet_options: PetOptions[]) => { + return pet_options.filter((pet) => pet.pet_specie === pet_specie); +}; + +export const PetBuilder = (props) => { + const { act, data } = useBackend(); + const { + pet_name, + pet_path, + pet_gender, + pet_options, + pet_types, + pet_specie, + pet_trick_name, + carrier_options, + pet_carrier, + pet_possible_emotes, + } = data; + + const [selectedCarrier, setSelectedCarrier] = useState( + () => RetrieveCarrier(pet_carrier, carrier_options) || null, + ); + const [sequences, setSequences] = useState(['none', 'none', 'none']); + const [TrickName, setTrickName] = useState(pet_trick_name); + + const UpdateSequence = (Index: number, Trick: string) => { + const NewSequence = [...sequences]; + NewSequence[Index] = Trick; + setSequences(NewSequence); + }; + + const [selectedPath, setSelectedPath] = useState(pet_path); + const [selectedPet, setSelectedPet] = useState( + () => RetrievePet(selectedPath, pet_options) || null, + ); + const [selectedSpecie, setSelectedSpecie] = useState(pet_specie); + const [filteredPetList, setFilteredPetList] = useState( + () => FilterPetList(selectedSpecie, pet_options) || [], + ); + const [selectedName, setSelectedName] = useState(pet_name); + const [selectedGender, setSelectedGender] = useState(pet_gender); + + const ScrollPetSpecies = (direction: string) => { + let dir = direction === 'next' ? 1 : -1; + let currindex = pet_types.indexOf(selectedSpecie); + const newSpecie = + pet_types[(currindex + dir + pet_types.length) % pet_types.length]; + + const newFilteredPetList = FilterPetList(newSpecie, pet_options); + setSelectedSpecie(newSpecie); + setFilteredPetList(newFilteredPetList); + setSelectedPet(newFilteredPetList[0]); + }; + + const ScrollPetOptions = (direction: string) => { + if (!selectedPet) { + return; + } + let dir = direction === 'next' ? 1 : -1; + let currindex = filteredPetList.indexOf(selectedPet); + setSelectedPet( + filteredPetList[ + (currindex + dir + filteredPetList.length) % filteredPetList.length + ], + ); + }; + + return ( + + + + + + + + + +
+ +
+
+
+
+ +
+ + + + + + + + + +
+
+
+
+
+ ); +}; + +const PetSelector = ({ + selectedSpecie, + selectedPet, + pet_types, + ScrollPetSpecies, + ScrollPetOptions, +}: any) => ( +
+ + + + +
+); + +const PetDetails = ({ + selectedName, + setSelectedName, + selectedGender, + setSelectedGender, +}: any) => ( + + + + + Pet Name + + setSelectedName(value)} + style={{ borderRadius: '1em' }} + /> + + + + + + + + + + Pet Gender + + + +