Bring your pet to work day! (#86879)

## About The Pull Request
Adds a new neutral station trait where people are able to bring their
personal pets to show off to the rest of the crew (or to donate to the
chef). Before joining, players will be able to customize their pets



https://github.com/user-attachments/assets/5a5cda18-e551-4f87-b649-67a6940ffc6a

u can set their name, gender, pet carrier color, and teach them give
them a unique dance sequence trick. if players dont wish to opt in, they
also have the choice not to

## Why It's Good For The Game
adds a new station trait which people can RP around, whether its by
competitively comparing pets, or resolving conflicts around pets being
killed/kidnapped. Ive also GAGsified pet carriers, so people can now
have the option to recolor those too

## Changelog
🆑
add: adds a new station trait, "bring your pet to work" day
/🆑
This commit is contained in:
Ben10Omintrix
2024-11-14 15:35:33 -08:00
committed by lessthanthree
parent fe3efe2b47
commit f380bd4c6e
28 changed files with 894 additions and 14 deletions
+3
View File
@@ -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"
@@ -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'
@@ -14,3 +14,4 @@
}
]
}
@@ -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 ]
}
]
}
@@ -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 ]
}
]
}
@@ -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"
+9 -7
View File
@@ -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
@@ -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"
@@ -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,
@@ -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,
@@ -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,
+12
View File
@@ -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,
@@ -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
@@ -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
@@ -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
@@ -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,
@@ -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)
@@ -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!")
@@ -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,
+10 -2
View File
@@ -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
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

+1
View File
@@ -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"
@@ -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 = <Icon name="spinner" size={2.2} spin color="gray" />;
@@ -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}
/>
);
}
@@ -56,7 +56,7 @@ export function LootBox(props: Props) {
});
}}
>
<IconDisplay item={item} />
<IconDisplay item={item} size={{ height: 3, width: 3 }} />
{amount > 1 && <div className="SearchItem--amount">{amount}</div>}
</div>
{!is_blind && <span className="SearchItem--text">{name}</span>}
@@ -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<Data>();
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 (
<Window title="Create Your Pet!" width={665} height={325}>
<Window.Content>
<Flex width="50%">
<Flex.Item>
<Stack vertical>
<Stack.Item>
<PetSelector
selectedSpecie={selectedSpecie}
selectedPet={selectedPet}
pet_types={pet_types}
ScrollPetSpecies={ScrollPetSpecies}
ScrollPetOptions={ScrollPetOptions}
/>
</Stack.Item>
<Stack.Item mt={1.5}>
<Section
textAlign="center"
style={{
borderRadius: '1em',
}}
>
<Button
color="green"
onClick={() =>
act('finalize_pet', {
selected_path: selectedPet?.pet_path,
selected_trick_name: TrickName,
selected_trick_moves: sequences,
selected_pet_name: selectedName,
selected_gender: selectedGender,
selected_specie: selectedSpecie,
selected_carrier: selectedCarrier?.carrier_color,
})
}
style={{
fontSize: '27px',
borderRadius: '1em',
}}
>
<Flex>
<Flex.Item>
<Icon name="paw" />
</Flex.Item>
<Flex.Item ml={1}>Finalize Pet!</Flex.Item>
<Flex.Item>
<Icon ml={1} name="paw" />
</Flex.Item>
</Flex>
</Button>
</Section>
</Stack.Item>
</Stack>
</Flex.Item>
<Flex.Item ml={1}>
<Section width="350px">
<PetDetails
selectedName={selectedName}
setSelectedName={setSelectedName}
selectedGender={selectedGender}
setSelectedGender={setSelectedGender}
/>
<Flex>
<Flex.Item width="70px">
<CarrierSelector
selectedCarrier={selectedCarrier}
carrier_options={carrier_options}
setSelectedCarrier={setSelectedCarrier}
/>
</Flex.Item>
<Flex.Item ml={5} grow>
<TrickSequence
ml={5}
TrickName={TrickName}
pet_possible_emotes={pet_possible_emotes}
setTrickName={setTrickName}
sequences={sequences}
UpdateSequence={UpdateSequence}
carrier_options={carrier_options}
/>
</Flex.Item>
</Flex>
</Section>
</Flex.Item>
</Flex>
</Window.Content>
</Window>
);
};
const PetSelector = ({
selectedSpecie,
selectedPet,
pet_types,
ScrollPetSpecies,
ScrollPetOptions,
}: any) => (
<Section>
<Stack vertical>
<Stack.Item>
<Flex style={{ borderBottom: '2px solid #6f1d94' }}>
<Flex.Item>
<Button
color="transparent"
icon="arrow-left"
style={{ fontSize: '16px' }}
onClick={() => ScrollPetSpecies('previous')}
/>
</Flex.Item>
<Flex.Item grow textAlign="center" style={{ fontSize: '16px' }}>
{selectedSpecie}
</Flex.Item>
<Flex.Item>
<Button
color="transparent"
icon="arrow-right"
style={{ fontSize: '16px' }}
onClick={() => ScrollPetSpecies('next')}
/>
</Flex.Item>
</Flex>
</Stack.Item>
{selectedPet && (
<Stack.Item mt={5}>
<Flex>
<Flex.Item>
<Button
mt={5}
color="transparent"
icon="arrow-left"
style={{ fontSize: '20px' }}
onClick={() => ScrollPetOptions('previous')}
/>
</Flex.Item>
<Flex.Item grow textAlign="center">
<IconDisplay
item={{
icon: selectedPet.pet_icon,
icon_state: selectedPet.pet_icon_state,
name: selectedPet.pet_name,
path: selectedPet.pet_path,
ref: selectedPet.pet_ref,
}}
size={{
height: 11,
width: 11,
}}
/>
</Flex.Item>
<Flex.Item>
<Button
mt={5}
color="transparent"
style={{ fontSize: '20px' }}
icon="arrow-right"
onClick={() => ScrollPetOptions('next')}
/>
</Flex.Item>
</Flex>
</Stack.Item>
)}
</Stack>
</Section>
);
const PetDetails = ({
selectedName,
setSelectedName,
selectedGender,
setSelectedGender,
}: any) => (
<Stack vertical width="85%">
<Flex style={{ borderBottom: '1px solid gray' }}>
<Flex.Item>
<Stack vertical>
<Stack.Item style={{ fontSize: '16px' }}>Pet Name</Stack.Item>
<Stack.Item>
<Input
mb={2}
ml={2}
mt={0.5}
width="220px"
maxLength={30}
value={selectedName}
onInput={(_, value) => setSelectedName(value)}
style={{ borderRadius: '1em' }}
/>
</Stack.Item>
</Stack>
</Flex.Item>
<Flex.Item>
<Icon
style={{ transform: 'rotate(30deg)', fontSize: '30px' }}
ml={8}
mt={1}
name="paw"
/>
</Flex.Item>
</Flex>
<Stack.Item>
<Stack vertical>
<Stack.Item style={{ fontSize: '16px' }}>Pet Gender</Stack.Item>
<Stack.Item>
<Stack mt={0.5} style={{ borderBottom: '1px solid gray' }}>
<Stack.Item grow ml={2} mb={2}>
<Button
selected={selectedGender === PetGender.male}
icon="mars"
onClick={() => setSelectedGender(PetGender.male)}
/>
</Stack.Item>
<Stack.Item grow>
<Button
selected={selectedGender === PetGender.female}
icon="venus"
onClick={() => setSelectedGender(PetGender.female)}
/>
</Stack.Item>
<Stack.Item grow>
<Button
selected={selectedGender === PetGender.neuter}
icon="neuter"
onClick={() => setSelectedGender(PetGender.neuter)}
/>
</Stack.Item>
</Stack>
</Stack.Item>
</Stack>
</Stack.Item>
</Stack>
);
const TrickSequence = ({
TrickName,
setTrickName,
sequences,
pet_possible_emotes,
UpdateSequence,
carrier_options,
}: any) => (
<Section
mt={1}
width="80%"
style={{ borderBottom: '0px solid #6f1d94' }}
title={TrickName}
buttons={
<Button.Input
color="transparent"
onCommit={(_, value) => setTrickName(value)}
>
Rename
</Button.Input>
}
>
<Box ml={2}>
<LabeledList>
{sequences.map((sequence: string, index: number) => (
<LabeledList.Item key={index} label={`Move ${index + 1}`}>
<Dropdown
width="80%"
selected={sequences[index]}
options={pet_possible_emotes}
onSelected={(selected: string) => UpdateSequence(index, selected)}
/>
</LabeledList.Item>
))}
</LabeledList>
</Box>
</Section>
);
const CarrierSelector = ({
selectedCarrier,
carrier_options,
setSelectedCarrier,
}: any) => (
<Flex grow>
<Flex.Item width="30%">
<Stack vertical>
<Stack.Item>
<Image
m={1}
src={`data:image/jpeg;base64,${selectedCarrier?.carrier_icon}`}
height="64px"
width="64px"
style={{ verticalAlign: 'middle' }}
/>
</Stack.Item>
<Stack.Item>
<Dropdown
width="70px"
selected={selectedCarrier?.carrier_color}
options={carrier_options.map(
(carrier: any) => carrier.carrier_color,
)}
onSelected={(selected: string) => {
const new_carrier = RetrieveCarrier(selected, carrier_options);
if (new_carrier) {
setSelectedCarrier(new_carrier);
}
}}
/>
</Stack.Item>
</Stack>
</Flex.Item>
</Flex>
);