Files
Bubberstation/code/modules/modular_computers/file_system/programs/virtual_pet.dm
DATA e4682c04a4 Adds a new smite: Retcon, also refactors the temporary_atom component into an atom level proc (#90016)
## About The Pull Request

Adds a new Retcon smite, it makes the person fade out into nothingness
with a configurable timer, deletes their records and reopens their job
slot, as if they were never there at all.

I was also annoyed that to play around with temporary_atom I had to
slowy add a component, and it doesn't really have much of a reason to BE
a component, so I refactored it into an atom level proc called
fade_into_nothing

## Why It's Good For The Game

The smite is useful for when you wanna get rid of someone who had to
leave roundstart and whatnot, on top of just being funny. the refactor
is also good because i can now put that proc on build mode and go to
town.

## Changelog

🆑
add: Added new mechanics or gameplay changes
add: Added more things
del: Removed old things
qol: made something easier to use
balance: rebalanced something
fix: fixed a few things
sound: added/modified/removed audio or sound effects
image: added/modified/removed some icons or images
map: added/modified/removed map content
spellcheck: fixed a few typos
code: changed some code
refactor: refactored some code
config: changed some config setting
admin: messed with admin stuff
server: something server ops should know
/🆑

---------

Co-authored-by: Jacquerel <hnevard@gmail.com>
2025-03-12 22:26:06 -04:00

620 lines
22 KiB
Plaintext

GLOBAL_LIST_EMPTY(global_pet_updates)
GLOBAL_LIST_EMPTY(virtual_pets_list)
#define MAX_UPDATE_LENGTH 50
#define PET_MAX_LEVEL 3
#define PET_MAX_STEPS_RECORD 50000
#define PET_EAT_BONUS 500
#define PET_CLEAN_BONUS 250
#define PET_PLAYMATE_BONUS 500
#define PET_STATE_HUNGRY "hungry"
#define PET_STATE_ASLEEP "asleep"
#define PET_STATE_HAPPY "happy"
#define PET_STATE_NEUTRAL "neutral"
/datum/computer_file/program/virtual_pet
filename = "virtualpet"
filedesc = "Virtual Pet"
downloader_category = PROGRAM_CATEGORY_GAMES
extended_desc = "Download your very own Orbie today!"
program_open_overlay = "generic"
program_flags = PROGRAM_ON_NTNET_STORE
size = 3
tgui_id = "NtosVirtualPet"
program_icon = "paw"
can_run_on_flags = PROGRAM_PDA
detomatix_resistance = DETOMATIX_RESIST_MALUS
///how many steps have we walked
var/steps_counter = 0
///the pet hologram
var/mob/living/pet
///the type of our pet
var/pet_type = /mob/living/basic/orbie
///our current happiness
var/happiness = 0
///our max happiness
var/max_happiness = 1750
///our current level
var/level = 1
///required exp to get to next level
var/to_next_level = 1000
///how much exp we currently have
var/current_level_progress = 0
///our current hunger
var/hunger = 0
///maximum hunger threshold
var/max_hunger = 500
///pet icon for each state
var/static/list/pet_state_icons = list(
PET_STATE_HUNGRY = list("icon" = 'icons/ui/virtualpet/pet_state.dmi', "icon_state" = "pet_hungry"),
PET_STATE_HAPPY = list("icon" = 'icons/ui/virtualpet/pet_state.dmi', "icon_state" = "pet_happy"),
PET_STATE_ASLEEP = list("icon" = 'icons/ui/virtualpet/pet_state.dmi', "icon_state" = "pet_asleep"),
PET_STATE_NEUTRAL = list("icon" = 'icons/ui/virtualpet/pet_state.dmi', "icon_state" = "pet_neutral"),
)
///hat options and what level they will be unlocked at
var/static/list/hat_selections = list(
/obj/item/clothing/head/hats/tophat = 1,
/obj/item/clothing/head/fedora = 1,
/obj/item/clothing/head/soft/fishing_hat = 1,
/obj/item/cigarette/dart = 1,
/obj/item/clothing/head/hats/bowler = 2,
/obj/item/clothing/head/hats/warden/police = 2,
/obj/item/clothing/head/wizard/tape = 2,
/obj/item/clothing/head/utility/hardhat/cakehat/energycake = 2,
/obj/item/clothing/head/cowboy/bounty = 2,
/obj/item/clothing/head/hats/warden/red = 3,
/obj/item/clothing/head/hats/caphat = 3,
/obj/item/clothing/head/costume/crown/fancy = 3,
)
///hat options that are locked behind achievements
var/static/list/cheevo_hats = list(
/obj/item/clothing/head/soft/fishing_hat = /datum/award/achievement/skill/legendary_fisher,
/obj/item/cigarette/dart = /datum/award/achievement/misc/cigarettes,
/obj/item/clothing/head/wizard/tape = /datum/award/achievement/misc/grand_ritual_finale,
/obj/item/clothing/head/utility/hardhat/cakehat/energycake = /datum/award/achievement/misc/cayenne_disk,
/obj/item/clothing/head/cowboy/bounty = /datum/award/achievement/misc/hot_damn,
/obj/item/clothing/head/costume/crown/fancy = /datum/award/achievement/misc/debt_extinguished,
)
///A list of hats that override the hat offsets and transform variable
var/static/list/special_hat_placement = list(
/obj/item/cigarette/dart = list(
"west" = list(2,-1),
"east" = list(-2,-1),
"north" = list(0,0),
"south" = list(0, -3),
"transform" = list(1, 1),
),
)
///hologram hat we have selected for our pet
var/list/selected_hat = list()
///manage hat offsets for when we turn directions
var/static/list/hat_offsets = list(
"west" = list(0,1),
"east" = list(0,1),
"north" = list(1,1),
"south" = list(1,1),
)
///area we have picked as dropoff location for petfeed
var/area/selected_area
///possible colors our pet can have
var/static/list/possible_colors= list(
"white" = null, //default color state
"light blue" = "#c3ecf3",
"light green" = "#b1ffe8",
)
///areas we wont drop the chocolate in
var/static/list/restricted_areas = typecacheof(list(
/area/station/security,
/area/station/command,
/area/station/ai_monitored,
/area/station/maintenance,
/area/station/solars,
))
///our profile picture
var/icon/profile_picture
///cooldown till we can reroll the pet feed dropzone
COOLDOWN_DECLARE(area_reroll)
///cooldown till our pet gains happiness again from being cleaned
COOLDOWN_DECLARE(on_clean_cooldown)
///cooldown till we can release/recall our pet
COOLDOWN_DECLARE(summon_cooldown)
///cooldown till we can alter our pet's appearance again
COOLDOWN_DECLARE(alter_appearance_cooldown)
/datum/computer_file/program/virtual_pet/on_install()
. = ..()
profile_picture = getFlatIcon(image(icon = 'icons/ui/virtualpet/pet_state.dmi', icon_state = "pet_preview"))
GLOB.virtual_pets_list += src
pet = new pet_type(computer)
pet.forceMove(computer)
pet.AddComponent(/datum/component/leash, computer, 9, force_teleport_out_effect = /obj/effect/temp_visual/guardian/phase/out)
RegisterSignal(pet, COMSIG_QDELETING, PROC_REF(remove_pet))
RegisterSignal(pet, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_overlays_updated)) //hologramic hat management
RegisterSignal(pet, COMSIG_ATOM_DIR_CHANGE, PROC_REF(on_change_dir))
RegisterSignal(pet, COMSIG_MOVABLE_MOVED, PROC_REF(after_pet_move))
RegisterSignal(pet, COMSIG_MOB_ATE, PROC_REF(after_pet_eat)) // WE ATEEE
RegisterSignal(pet, COMSIG_ATOM_PRE_CLEAN, PROC_REF(pet_pre_clean))
RegisterSignal(pet, COMSIG_LIVING_DEATH, PROC_REF(on_death))
RegisterSignal(pet, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(post_cleaned))
RegisterSignal(pet, COMSIG_AI_BLACKBOARD_KEY_SET(BB_NEARBY_PLAYMATE), PROC_REF(on_playmate_find))
RegisterSignal(computer, COMSIG_ATOM_ENTERED, PROC_REF(on_pet_entered))
RegisterSignal(computer, COMSIG_ATOM_EXITED, PROC_REF(on_pet_exit))
/datum/computer_file/program/virtual_pet/Destroy()
GLOB.virtual_pets_list -= src
if(!QDELETED(pet))
QDEL_NULL(pet)
STOP_PROCESSING(SSprocessing, src)
return ..()
/datum/computer_file/program/virtual_pet/proc/on_death(datum/source)
SIGNAL_HANDLER
pet.forceMove(computer)
/datum/computer_file/program/virtual_pet/proc/on_message_receive(datum/source, sender_title, inbound_message, photo_message)
SIGNAL_HANDLER
var/message_to_display = "[sender_title] has sent you a message [photo_message ? "with a photo attached" : ""]: [inbound_message]!"
pet.ai_controller?.set_blackboard_key(BB_LAST_RECEIVED_MESSAGE, message_to_display)
/datum/computer_file/program/virtual_pet/proc/pet_pre_clean(atom/source, mob/user)
SIGNAL_HANDLER
if(!COOLDOWN_FINISHED(src, on_clean_cooldown))
source.balloon_alert(user, "already clean!")
return COMSIG_ATOM_CANCEL_CLEAN
/datum/computer_file/program/virtual_pet/proc/on_playmate_find(datum/source)
SIGNAL_HANDLER
happiness = min(happiness + PET_PLAYMATE_BONUS, max_happiness)
START_PROCESSING(SSprocessing, src)
/datum/computer_file/program/virtual_pet/proc/post_cleaned(mob/source, mob/user)
SIGNAL_HANDLER
source.spin(spintime = 2 SECONDS, speed = 1) //celebrate!
happiness = min(happiness + PET_CLEAN_BONUS, max_happiness)
COOLDOWN_START(src, on_clean_cooldown, 1 MINUTES)
START_PROCESSING(SSprocessing, src)
///manage the pet's hat offsets when he changes direction
/datum/computer_file/program/virtual_pet/proc/on_change_dir(datum/source, old_dir, new_dir)
SIGNAL_HANDLER
if(!length(selected_hat))
return
set_hat_offsets(new_dir)
/datum/computer_file/program/virtual_pet/proc/on_photo_captured(datum/source, atom/target, atom/user, datum/picture/photo)
SIGNAL_HANDLER
if(isnull(photo))
return
computer.store_file(new /datum/computer_file/picture(photo))
/datum/computer_file/program/virtual_pet/proc/set_hat_offsets(new_dir)
var/direction_text = dir2text(new_dir)
var/hat_type = selected_hat["type"]
var/list/offsets_list = special_hat_placement[hat_type]?[direction_text] || hat_offsets[direction_text]
var/mutable_appearance/hat_appearance = selected_hat["appearance"]
hat_appearance.pixel_w = offsets_list[1]
hat_appearance.pixel_z = offsets_list[2] + selected_hat["worn_offset"]
pet.update_appearance(UPDATE_OVERLAYS)
///give our pet his hologram hat
/datum/computer_file/program/virtual_pet/proc/on_overlays_updated(atom/source, list/overlays)
SIGNAL_HANDLER
if(!length(selected_hat))
return
overlays += selected_hat["appearance"]
/datum/computer_file/program/virtual_pet/proc/alter_profile_picture()
var/image/pet_preview = image(icon = 'icons/ui/virtualpet/pet_state.dmi', icon_state = "pet_preview")
if(LAZYACCESS(pet.atom_colours, FIXED_COLOUR_PRIORITY))
pet_preview.color = pet.atom_colours[FIXED_COLOUR_PRIORITY]
if(length(selected_hat))
var/mutable_appearance/our_selected_hat = selected_hat["appearance"]
var/mutable_appearance/hat_preview = mutable_appearance(our_selected_hat.icon, our_selected_hat.icon_state)
hat_preview.pixel_y = -9 + selected_hat["worn_offset"]
var/list/spec_hat = special_hat_placement[selected_hat["type"]]?["south"]
if(spec_hat)
hat_preview.pixel_w += spec_hat[1]
hat_preview.pixel_z += spec_hat[2]
hat_preview.appearance_flags = RESET_COLOR
pet_preview.add_overlay(hat_preview)
profile_picture = getFlatIcon(pet_preview, no_anim = TRUE)
COOLDOWN_START(src, alter_appearance_cooldown, 10 SECONDS)
///decrease the pet's hunger after it eats
/datum/computer_file/program/virtual_pet/proc/after_pet_eat(datum/source)
SIGNAL_HANDLER
hunger = min(hunger + PET_EAT_BONUS, max_hunger)
happiness = min(happiness + PET_EAT_BONUS, max_happiness)
START_PROCESSING(SSprocessing, src)
///start processing if we enter the pda and need healing
/datum/computer_file/program/virtual_pet/proc/on_pet_entered(atom/movable/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
SIGNAL_HANDLER
if(arrived != pet)
return
ADD_TRAIT(pet, TRAIT_AI_PAUSED, REF(src))
if((datum_flags & DF_ISPROCESSING))
return
if(pet.health < pet.maxHealth) //if we're in the pda, heal up
START_PROCESSING(SSprocessing, src)
/datum/computer_file/program/virtual_pet/proc/on_pet_exit(atom/movable/source, atom/movable/exited)
SIGNAL_HANDLER
if(exited != pet)
return
REMOVE_TRAIT(pet, TRAIT_AI_PAUSED, REF(src))
if((datum_flags & DF_ISPROCESSING))
return
if(hunger > 0 || happiness > 0) //if were outside the pda, we become hungry and happiness decreases
START_PROCESSING(SSprocessing, src)
/datum/computer_file/program/virtual_pet/process()
if(pet.loc == computer)
if(pet.health >= pet.maxHealth)
return PROCESS_KILL
if(pet.stat == DEAD)
pet.revive(ADMIN_HEAL_ALL)
pet.heal_overall_damage(5)
return
if(hunger > 0)
hunger--
if(happiness > 0)
happiness--
if(hunger <=0 && happiness <= 0)
return PROCESS_KILL
/datum/computer_file/program/virtual_pet/proc/after_pet_move(atom/movable/movable, atom/old_loc)
SIGNAL_HANDLER
if(!isturf(pet.loc) || !isturf(old_loc))
return
steps_counter = min(steps_counter + 1, PET_MAX_STEPS_RECORD)
increment_exp()
if(steps_counter % 2000 == 0) //every 2000 steps, announce the milestone to the world!
announce_global_updates(message = "has walked [steps_counter] steps!")
/datum/computer_file/program/virtual_pet/proc/increment_exp()
var/modifier = 1
var/hunger_happiness = hunger + happiness
var/max_hunger_happiness = max_hunger + max_happiness
switch(hunger_happiness / max_hunger_happiness)
if(0.8 to 1)
modifier = 3
if(0.5 to 0.8)
modifier = 2
current_level_progress = min(current_level_progress + modifier, to_next_level)
if(current_level_progress >= to_next_level)
handle_level_up()
/datum/computer_file/program/virtual_pet/proc/handle_level_up()
current_level_progress = 0
level++
grant_level_abilities()
pet.ai_controller?.set_blackboard_key(BB_VIRTUAL_PET_LEVEL, level)
playsound(computer.loc, 'sound/mobs/non-humanoids/orbie/orbie_level_up.ogg', 50)
to_next_level += (level**2) + 500
SEND_SIGNAL(pet, COMSIG_VIRTUAL_PET_LEVEL_UP, level) //its a signal so different path types of virtual pets can handle leveling up differently
announce_global_updates(message = "has reached level [level]!")
/datum/computer_file/program/virtual_pet/proc/grant_level_abilities()
switch(level)
if(2)
RegisterSignal(computer, COMSIG_COMPUTER_RECEIVED_MESSAGE, PROC_REF(on_message_receive)) // we will now read out PDA messages
var/datum/action/cooldown/mob_cooldown/lights/lights = new(pet)
lights.Grant(pet)
pet.ai_controller?.set_blackboard_key(BB_LIGHTS_ABILITY, lights)
if(3)
var/datum/action/cooldown/mob_cooldown/capture_photo/photo_ability = new(pet)
photo_ability.Grant(pet)
pet.ai_controller?.set_blackboard_key(BB_PHOTO_ABILITY, photo_ability)
RegisterSignal(photo_ability.ability_camera, COMSIG_CAMERA_IMAGE_CAPTURED, PROC_REF(on_photo_captured))
/datum/computer_file/program/virtual_pet/proc/announce_global_updates(message)
if(isnull(message))
return
var/list/message_to_announce = list(
"name" = pet.name,
"pet_picture" = icon2base64(profile_picture),
"message" = message,
"likers" = list(REF(src))
)
if(length(GLOB.global_pet_updates) >= MAX_UPDATE_LENGTH)
GLOB.global_pet_updates.Cut(1,2)
GLOB.global_pet_updates += list(message_to_announce)
playsound(computer.loc, 'sound/mobs/non-humanoids/orbie/orbie_notification_sound.ogg', 50)
/datum/computer_file/program/virtual_pet/proc/remove_pet(datum/source)
SIGNAL_HANDLER
pet = null
if(QDELETED(src))
return
computer.remove_file(src) //all is lost we no longer have a reason to exist
/datum/computer_file/program/virtual_pet/kill_program(mob/user)
if(pet && pet.loc != computer)
pet.forceMove(computer) //recall the hologram back to the pda
STOP_PROCESSING(SSprocessing, src)
return ..()
/datum/computer_file/program/virtual_pet/proc/get_pet_state()
if(isnull(pet))
return
if(pet.loc == computer)
return PET_STATE_ASLEEP
if(happiness/max_happiness > 0.8)
return PET_STATE_HAPPY
if(hunger/max_hunger < 0.5)
return PET_STATE_HUNGRY
return PET_STATE_NEUTRAL
/datum/computer_file/program/virtual_pet/ui_data(mob/user)
var/list/data = list()
var/obj/item/hat_type = selected_hat?["type"]
data["currently_summoned"] = (pet.loc != computer)
data["selected_area"] = (selected_area ? selected_area.name : "No location set")
data["pet_state"] = get_pet_state()
data["hunger"] = hunger
data["maximum_hunger"] = max_hunger
data["pet_hat"] = (hat_type ? initial(hat_type.name) : "none")
data["can_reroll"] = COOLDOWN_FINISHED(src, area_reroll)
data["can_summon"] = COOLDOWN_FINISHED(src, summon_cooldown)
data["can_alter_appearance"] = COOLDOWN_FINISHED(src, alter_appearance_cooldown)
data["pet_name"] = pet.name
data["steps_counter"] = steps_counter
data["in_dropzone"] = (istype(get_area(computer), selected_area))
data["pet_area"] = (pet.loc != computer ? get_area_name(pet) : "Sleeping in PDA")
data["current_exp"] = current_level_progress
data["required_exp"] = to_next_level
data["happiness"] = happiness
data["maximum_happiness"] = max_happiness
data["level"] = level
data["pet_color"] = ""
var/color_value = LAZYACCESS(pet.atom_colours, FIXED_COLOUR_PRIORITY)
for(var/index in possible_colors)
if(possible_colors[index] == color_value)
data["pet_color"] = index
break
data["pet_gender"] = pet.gender
data["pet_updates"] = list()
for(var/i in length(GLOB.global_pet_updates) to 1 step -1)
var/list/update = GLOB.global_pet_updates[i]
if(isnull(update))
continue
data["pet_updates"] += list(list(
"update_id" = i,
"update_name" = update["name"],
"update_picture" = update["pet_picture"],
"update_message" = update["message"],
"update_likers" = length(update["likers"]),
"update_already_liked" = ((REF(src)) in update["likers"]),
))
data["all_pets"] = list()
for(var/datum/computer_file/program/virtual_pet/program as anything in GLOB.virtual_pets_list)
data["all_pets"] += list(list(
"other_pet_name" = program.pet.name,
"other_pet_picture" = icon2base64(program.profile_picture),
))
return data
/datum/computer_file/program/virtual_pet/ui_static_data(mob/user)
var/list/data = list()
data["pet_state_icons"] = list()
for(var/list_index as anything in pet_state_icons)
var/list/sprite_location = pet_state_icons[list_index]
data["pet_state_icons"] += list(list(
"name" = list_index,
"icon" = icon2base64(getFlatIcon(image(icon = sprite_location["icon"], icon_state = sprite_location["icon_state"]), no_anim=TRUE))
))
data["hat_selections"] = list(list(
"hat_id" = null,
"hat_name" = "none",
))
for(var/type_index as anything in hat_selections)
if(level >= hat_selections[type_index])
var/obj/item/hat = type_index
var/hat_name = initial(hat.name)
if(length(SSachievements.achievements)) // The Achievements subsystem is active.
var/datum/award/required_cheevo = cheevo_hats[hat]
if(required_cheevo && !user.client.get_award_status(required_cheevo))
hat_name = "LOCKED"
data["hat_selections"] += list(list(
"hat_id" = type_index,
"hat_name" = hat_name,
))
data["possible_colors"] = list()
for(var/color in possible_colors)
data["possible_colors"] += list(list(
"color_name" = color,
"color_value" = possible_colors[color],
))
var/static/list/possible_emotes = list(
/datum/emote/flip,
/datum/emote/jump,
/datum/emote/living/shiver,
/datum/emote/spin,
/datum/emote/silicon/beep,
)
data["possible_emotes"] = list("none")
for(var/datum/emote/target_emote as anything in possible_emotes)
data["possible_emotes"] += target_emote.key
data["preview_icon"] = icon2base64(profile_picture)
return data
/datum/computer_file/program/virtual_pet/ui_act(action, params, datum/tgui/ui)
. = ..()
switch(action)
if("summon_pet")
if(!COOLDOWN_FINISHED(src, summon_cooldown))
return TRUE
if(pet.loc == computer)
release_pet(ui.user)
else
recall_pet(ui.user)
COOLDOWN_START(src, summon_cooldown, 10 SECONDS)
if("apply_customization")
if(!COOLDOWN_FINISHED(src, alter_appearance_cooldown))
return TRUE
var/obj/item/chosen_type = text2path(params["chosen_hat"])
if(isnull(chosen_type))
selected_hat.Cut()
else if(hat_selections[chosen_type])
var/datum/award/required_cheevo = cheevo_hats[chosen_type]
if(length(SSachievements.achievements) && required_cheevo && !ui.user.client.get_award_status(required_cheevo))
to_chat(ui.user, span_info("This customization requires the \"[span_bold(initial(required_cheevo.name))]\ achievement to be unlocked."))
else
selected_hat["type"] = chosen_type
var/state_to_use = initial(chosen_type.worn_icon_state) || initial(chosen_type.icon_state)
var/mutable_appearance/selected_hat_appearance = mutable_appearance(initial(chosen_type.worn_icon), state_to_use, appearance_flags = RESET_COLOR)
selected_hat["worn_offset"] = initial(chosen_type.worn_y_offset)
var/list/scale_list = special_hat_placement[chosen_type]?["scale"]
if(scale_list)
selected_hat_appearance.transform = selected_hat_appearance.transform.Scale(scale_list[1], scale_list[2])
else
selected_hat_appearance.transform = selected_hat_appearance.transform.Scale(0.8, 1)
selected_hat["appearance"] = selected_hat_appearance
set_hat_offsets(pet.dir)
var/chosen_color = params["chosen_color"]
if(isnull(chosen_color))
pet.remove_atom_colour(FIXED_COLOUR_PRIORITY)
else
pet.add_atom_colour(chosen_color, FIXED_COLOUR_PRIORITY)
var/input_name = sanitize_name(params["chosen_name"], allow_numbers = TRUE)
pet.name = (input_name ? input_name : initial(pet.name))
new /obj/effect/temp_visual/guardian/phase(pet.loc)
switch(params["chosen_gender"])
if("male")
pet.gender = MALE
if("female")
pet.gender = FEMALE
if("neuter")
pet.gender = NEUTER
pet.update_appearance()
alter_profile_picture()
update_static_data(ui.user, ui)
if("get_feed_location")
generate_petfeed_area()
if("drop_feed")
drop_feed()
if("like_update")
var/index = params["update_reference"]
var/list/update_message = GLOB.global_pet_updates[index]
if(isnull(update_message))
return TRUE
var/our_reference = REF(src)
if(our_reference in update_message["likers"])
update_message["likers"] -= our_reference
else
update_message["likers"] += our_reference
if("teach_tricks")
var/trick_name = params["trick_name"]
var/list/trick_sequence = params["tricks"]
if(isnull(pet.ai_controller))
return TRUE
if(!isnull(trick_name))
pet.ai_controller.set_blackboard_key(BB_TRICK_NAME, trick_name)
for (var/trick_move in trick_sequence)
if (!length(GLOB.emote_list[LOWER_TEXT(trick_move)]))
trick_sequence -= trick_move
pet.ai_controller.override_blackboard_key(BB_TRICK_SEQUENCE, trick_sequence)
playsound(computer.loc, 'sound/mobs/non-humanoids/orbie/orbie_trick_learned.ogg', 50)
return TRUE
/datum/computer_file/program/virtual_pet/proc/generate_petfeed_area()
if(!COOLDOWN_FINISHED(src, area_reroll))
return
var/list/filter_area_list = typecache_filter_list(GLOB.the_station_areas, restricted_areas)
var/list/target_area_list = GLOB.the_station_areas.Copy() - filter_area_list
if(!length(target_area_list))
return
selected_area = pick(target_area_list)
COOLDOWN_START(src, area_reroll, 2 MINUTES)
/datum/computer_file/program/virtual_pet/proc/drop_feed()
if(!istype(get_area(computer), selected_area))
return
announce_global_updates(message = "has found a chocolate at [selected_area.name]")
selected_area = null
var/obj/item/food/virtual_chocolate/chocolate = new(get_turf(computer))
chocolate.fade_into_nothing(life_time = 30 SECONDS) //we cant maintain its existence for too long!
/datum/computer_file/program/virtual_pet/proc/recall_pet(mob/living/friend)
animate(pet, transform = matrix().Scale(0.3, 0.3), time = 1.5 SECONDS)
addtimer(CALLBACK(pet, TYPE_PROC_REF(/atom/movable, forceMove), computer), 1.5 SECONDS)
SEND_SIGNAL(pet, COMSIG_VIRTUAL_PET_RECALLED, friend)
/datum/computer_file/program/virtual_pet/proc/release_pet(mob/living/our_user)
var/turf/drop_zone
var/list/turfs_list = get_adjacent_open_turfs(computer.drop_location())
for(var/turf/possible_turf as anything in turfs_list)
if(possible_turf.is_blocked_turf())
continue
drop_zone = possible_turf
break
var/turf/final_turf = isnull(drop_zone) ? computer.drop_location() : drop_zone
pet.befriend(our_user) //befriend whoever set us out
animate(pet, transform = matrix(), time = 1.5 SECONDS)
pet.forceMove(final_turf)
playsound(computer.loc, 'sound/mobs/non-humanoids/orbie/orbie_send_out.ogg', 20)
SEND_SIGNAL(pet, COMSIG_VIRTUAL_PET_SUMMONED, our_user)
new /obj/effect/temp_visual/guardian/phase(pet.loc)
#undef PET_MAX_LEVEL
#undef PET_MAX_STEPS_RECORD
#undef PET_EAT_BONUS
#undef PET_CLEAN_BONUS
#undef PET_PLAYMATE_BONUS
#undef PET_STATE_HUNGRY
#undef PET_STATE_ASLEEP
#undef PET_STATE_HAPPY
#undef PET_STATE_NEUTRAL
#undef MAX_UPDATE_LENGTH