virtual pets (#81342)

players can now download their very own virtual pet through a new PDA
app. this pet is called orbie and u can interact with him in alot of
unique ways

happiness can be increased by regularly grooming ur pet, feeding him, or
u can arrange with other players playdates for ur pets, they can play
with each other and both their happiness will increase

u can get food from ur pet through ur pda, it will assign u a random
drop zone location in the station u need to go to, after which u can
obtain ur pet's food then ur PDA will spawn a virtual chocolate bar that
ur pet loves to eat. it wont assign u dropzone locations that are
restricted or hard to reach, however if the area it assigns u is a bit
difficult to get to, u can reroll the location after a small cooldown

u can also level up ur pet to make it gain new helpful abilities and
more cosmetic options. the main way to level up ur pet is by walking it,
so u can have it follow u while u are doing ur job on the station and it
will passively get exp. u get an increased exp modifier per step if ur
pet is happy and not hungry. At level 2, ur pet will gain an ability to
toggle lights and will also read outloud to u any PDA messages u
recieve. at level 3, ur pet gains a camera ability. u can command it to
take a photo afterwhich the picture will be saved directly in ur pda

u also have alot of customization options for ur pet!

u can change its color, name, gender, and u can make it wear hats! u can
unlock more hats for ur pet if u level it up further. these
customizations change ur pet's hologram appearance as well as its
profile picture on the pet network.

u can view how other player's virtual pets are progressing through the
pet network. each time ur pet reaches a new milestone, an update will
automatically be sent out on the network

if ur pet's milestones gets likes from other players, it will become
happier

this app also allows u to program new tricks for ur pet. U can create a
custom trick sequence, and change the trick's name. If u say the trick's
name outloud to ur pet it will do the sequence u programmed.
This commit is contained in:
Ben10Omintrix
2024-02-29 04:24:10 +00:00
committed by GitHub
parent b3e8ce3cea
commit fcc87b4801
26 changed files with 1500 additions and 20 deletions
@@ -0,0 +1,112 @@
#define ORBIE_MAXIMUM_HEALTH 300
/mob/living/basic/orbie
name = "Orbie"
desc = "An orb shaped hologram."
icon = 'icons/mob/simple/pets.dmi'
icon_state = "orbie"
icon_living = "orbie"
speed = 0
maxHealth = 100
light_on = FALSE
light_system = OVERLAY_LIGHT
light_range = 6
light_color = "#64bee1"
health = 100
habitable_atmos = list("min_oxy" = 0, "max_oxy" = 0, "min_plas" = 0, "max_plas" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
unsuitable_atmos_damage = 0
can_buckle_to = FALSE
density = FALSE
pass_flags = PASSMOB
move_force = 0
move_resist = 0
pull_force = 0
minimum_survivable_temperature = TCMB
maximum_survivable_temperature = INFINITY
death_message = "fades out of existence!"
ai_controller = /datum/ai_controller/basic_controller/orbie
///are we happy or not?
var/happy_state = FALSE
///overlay for our neutral eyes
var/static/mutable_appearance/eyes_overlay = mutable_appearance('icons/mob/simple/pets.dmi', "orbie_eye_overlay")
///overlay for when our eyes are emitting light
var/static/mutable_appearance/orbie_light_overlay = mutable_appearance('icons/mob/simple/pets.dmi', "orbie_light_overlay")
///overlay for the flame propellar
var/static/mutable_appearance/flame_overlay = mutable_appearance('icons/mob/simple/pets.dmi', "orbie_flame_overlay")
///overlay for our happy eyes
var/static/mutable_appearance/happy_eyes_overlay = mutable_appearance('icons/mob/simple/pets.dmi', "orbie_happy_eye_overlay")
///commands we can give orbie
var/list/pet_commands = list(
/datum/pet_command/idle,
/datum/pet_command/free,
/datum/pet_command/untargeted_ability/pet_lights,
/datum/pet_command/point_targeting/use_ability/take_photo,
/datum/pet_command/follow/orbie,
/datum/pet_command/perform_trick_sequence,
)
/mob/living/basic/orbie/Initialize(mapload)
. = ..()
var/static/list/food_types = list(/obj/item/food/virtual_chocolate)
AddComponent(/datum/component/obeys_commands, pet_commands)
AddElement(/datum/element/basic_eating, food_types = food_types)
RegisterSignal(src, COMSIG_ATOM_CAN_BE_PULLED, PROC_REF(on_pulled))
RegisterSignal(src, COMSIG_VIRTUAL_PET_LEVEL_UP, PROC_REF(on_level_up))
RegisterSignal(src, COMSIG_MOB_CLICKON, PROC_REF(on_click))
RegisterSignal(src, COMSIG_ATOM_UPDATE_LIGHT_ON, PROC_REF(on_lights))
ai_controller.set_blackboard_key(BB_BASIC_FOODS, typecacheof(food_types))
update_appearance()
/mob/living/basic/orbie/proc/on_click(mob/living/basic/source, atom/target, params)
SIGNAL_HANDLER
if(!CanReach(target))
return
if(src == target || happy_state || !istype(target))
return
toggle_happy_state()
addtimer(CALLBACK(src, PROC_REF(toggle_happy_state)), 30 SECONDS)
/mob/living/basic/orbie/proc/on_lights(datum/source)
SIGNAL_HANDLER
update_appearance()
/mob/living/basic/orbie/proc/toggle_happy_state()
happy_state = !happy_state
update_appearance()
/mob/living/basic/orbie/proc/on_pulled(datum/source) //i need move resist at 0, but i also dont want him to be pulled
SIGNAL_HANDLER
return COMSIG_ATOM_CANT_PULL
/mob/living/basic/orbie/proc/on_level_up(datum/source, new_level)
SIGNAL_HANDLER
if(maxHealth >= ORBIE_MAXIMUM_HEALTH)
UnregisterSignal(src, COMSIG_VIRTUAL_PET_LEVEL_UP)
return
maxHealth += 100
heal_overall_damage(maxHealth - health)
/mob/living/basic/orbie/update_overlays()
. = ..()
if(stat == DEAD)
return
. += flame_overlay
if(happy_state)
. += happy_eyes_overlay
else if(light_on)
. += orbie_light_overlay
else
. += eyes_overlay
/mob/living/basic/orbie/gib()
death(TRUE)
#undef ORBIE_MAXIMUM_HEALTH
@@ -0,0 +1,46 @@
/datum/action/cooldown/mob_cooldown/lights
name = "Toggle Lights"
button_icon = 'icons/mob/simple/pets.dmi'
button_icon_state = "orbie_light_action"
background_icon_state = "bg_default"
overlay_icon_state = "bg_default_border"
click_to_activate = FALSE
/datum/action/cooldown/mob_cooldown/lights/Activate()
owner.set_light_on(!owner.light_on)
return TRUE
/datum/action/cooldown/mob_cooldown/capture_photo
name = "Camera"
button_icon = 'icons/mob/simple/pets.dmi'
button_icon_state = "orbie_light_action"
background_icon_state = "bg_default"
overlay_icon_state = "bg_default_border"
cooldown_time = 30 SECONDS
///camera we use to take photos
var/obj/item/camera/ability_camera
/datum/action/cooldown/mob_cooldown/capture_photo/Grant(mob/grant_to)
. = ..()
if(isnull(owner))
return
ability_camera = new(owner)
ability_camera.print_picture_on_snap = FALSE
RegisterSignal(ability_camera, COMSIG_PREQDELETED, PROC_REF(on_camera_delete))
/datum/action/cooldown/mob_cooldown/capture_photo/Activate(atom/target)
if(isnull(ability_camera))
return FALSE
ability_camera.captureimage(target, owner)
StartCooldown()
return TRUE
/datum/action/cooldown/mob_cooldown/capture_photo/proc/on_camera_delete(datum/source)
SIGNAL_HANDLER
UnregisterSignal(ability_camera, COMSIG_PREQDELETED)
ability_camera = null
/datum/action/cooldown/mob_cooldown/capture_photo/Destroy()
QDEL_NULL(ability_camera)
return ..()
@@ -0,0 +1,169 @@
#define PET_PLAYTIME_COOLDOWN (2 MINUTES)
#define MESSAGE_EXPIRY_TIME (30 SECONDS)
/datum/ai_controller/basic_controller/orbie
blackboard = list(
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/allow_items,
BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends,
BB_TRICK_NAME = "Trick",
)
ai_movement = /datum/ai_movement/basic_avoidance
idle_behavior = /datum/idle_behavior/idle_random_walk
planning_subtrees = list(
/datum/ai_planning_subtree/find_food,
/datum/ai_planning_subtree/find_playmates,
/datum/ai_planning_subtree/basic_melee_attack_subtree,
/datum/ai_planning_subtree/relay_pda_message,
/datum/ai_planning_subtree/pet_planning,
)
/datum/ai_controller/basic_controller/orbie/TryPossessPawn(atom/new_pawn)
. = ..()
if(. & AI_CONTROLLER_INCOMPATIBLE)
return
RegisterSignal(new_pawn, COMSIG_AI_BLACKBOARD_KEY_SET(BB_LAST_RECIEVED_MESSAGE), PROC_REF(on_set_message))
/datum/ai_controller/basic_controller/orbie/proc/on_set_message(datum/source)
SIGNAL_HANDLER
addtimer(CALLBACK(src, PROC_REF(clear_blackboard_key), BB_LAST_RECIEVED_MESSAGE), MESSAGE_EXPIRY_TIME)
///ai behavior that lets us search for other orbies to play with
/datum/ai_planning_subtree/find_playmates
/datum/ai_planning_subtree/find_playmates/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
if(controller.blackboard[BB_NEXT_PLAYDATE] > world.time)
return
if(controller.blackboard_key_exists(BB_NEARBY_PLAYMATE))
controller.queue_behavior(/datum/ai_behavior/interact_with_playmate, BB_NEARBY_PLAYMATE)
return SUBTREE_RETURN_FINISH_PLANNING
controller.queue_behavior(/datum/ai_behavior/find_and_set/find_playmate, BB_NEARBY_PLAYMATE, /mob/living/basic/orbie)
/datum/ai_behavior/find_and_set/find_playmate
/datum/ai_behavior/find_and_set/find_playmate/search_tactic(datum/ai_controller/controller, locate_path, search_range)
for(var/mob/living/basic/orbie/playmate in oview(search_range, controller.pawn))
if(playmate == controller.pawn || playmate.stat == DEAD || isnull(playmate.ai_controller))
continue
if(playmate.ai_controller.blackboard[BB_NEARBY_PLAYMATE] || playmate.ai_controller.blackboard[BB_NEXT_PLAYDATE] > world.time) //they already have a playmate...
continue
playmate.ai_controller.set_blackboard_key(BB_NEARBY_PLAYMATE, controller.pawn)
return playmate
return null
/datum/ai_behavior/interact_with_playmate
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
/datum/ai_behavior/interact_with_playmate/setup(datum/ai_controller/controller, target_key)
. = ..()
var/turf/target = controller.blackboard[target_key]
if(isnull(target))
return FALSE
set_movement_target(controller, target)
/datum/ai_behavior/interact_with_playmate/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
. = ..()
var/mob/living/basic/living_pawn = controller.pawn
var/atom/target = controller.blackboard[target_key]
if(QDELETED(target))
finish_action(controller, FALSE, target_key)
return
living_pawn.manual_emote("plays with [target]!")
living_pawn.spin(spintime = 4, speed = 1)
living_pawn.ClickOn(target)
finish_action(controller, TRUE, target_key)
/datum/ai_behavior/interact_with_playmate/finish_action(datum/ai_controller/controller, success, target_key)
. = ..()
controller.clear_blackboard_key(target_key)
controller.set_blackboard_key(BB_NEXT_PLAYDATE, world.time + PET_PLAYTIME_COOLDOWN)
/datum/ai_planning_subtree/relay_pda_message
/datum/ai_planning_subtree/relay_pda_message/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
if(controller.blackboard[BB_VIRTUAL_PET_LEVEL] < 2 || isnull(controller.blackboard[BB_LAST_RECIEVED_MESSAGE]))
return
controller.queue_behavior(/datum/ai_behavior/relay_pda_message, BB_LAST_RECIEVED_MESSAGE)
/datum/ai_behavior/relay_pda_message/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
. = ..()
var/mob/living/basic/living_pawn = controller.pawn
var/text_to_say = controller.blackboard[target_key]
if(isnull(text_to_say))
finish_action(controller, FALSE, target_key)
return
living_pawn.say(text_to_say, forced = "AI controller")
living_pawn.spin(spintime = 4, speed = 1)
finish_action(controller, TRUE, target_key)
/datum/ai_behavior/relay_pda_message/finish_action(datum/ai_controller/controller, success, target_key)
. = ..()
controller.clear_blackboard_key(target_key)
/datum/pet_command/follow/orbie
follow_behavior = /datum/ai_behavior/pet_follow_friend/orbie
/datum/ai_behavior/pet_follow_friend/orbie
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
///command to make our pet turn its lights on, we need to be level 2 to activate this ability
/datum/pet_command/untargeted_ability/pet_lights
command_name = "Lights"
command_desc = "Toggle your pet's lights!"
radial_icon = 'icons/mob/simple/pets.dmi'
radial_icon_state = "orbie_lights_action"
speech_commands = list("lights", "light", "toggle")
ability_key = BB_LIGHTS_ABILITY
/datum/pet_command/untargeted_ability/pet_lights/execute_action(datum/ai_controller/controller)
if(controller.blackboard[BB_VIRTUAL_PET_LEVEL] < 2)
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
return SUBTREE_RETURN_FINISH_PLANNING
return ..()
/datum/pet_command/point_targeting/use_ability/take_photo
command_name = "Photo"
command_desc = "Make your pet take a photo!"
radial_icon = 'icons/mob/simple/pets.dmi'
radial_icon_state = "orbie_lights_action"
speech_commands = list("photo", "picture", "image")
command_feedback = "Readys camera mode"
pet_ability_key = BB_PHOTO_ABILITY
targeting_strategy_key = BB_TARGETING_STRATEGY
/datum/pet_command/point_targeting/use_ability/take_photo/execute_action(datum/ai_controller/controller)
if(controller.blackboard[BB_VIRTUAL_PET_LEVEL] < 3)
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
return SUBTREE_RETURN_FINISH_PLANNING
return ..()
/datum/pet_command/perform_trick_sequence
command_name = "Trick Sequence"
command_desc = "A trick sequence programmable through your PDA!"
/datum/pet_command/perform_trick_sequence/find_command_in_text(spoken_text, check_verbosity = FALSE)
var/mob/living/living_pawn = weak_parent.resolve()
if(isnull(living_pawn?.ai_controller))
return FALSE
var/text_command = living_pawn.ai_controller.blackboard[BB_TRICK_NAME]
if(isnull(text_command))
return FALSE
return findtext(spoken_text, text_command)
/datum/pet_command/perform_trick_sequence/execute_action(datum/ai_controller/controller)
var/mob/living/living_pawn = controller.pawn
var/list/trick_sequence = controller.blackboard[BB_TRICK_SEQUENCE]
for(var/index in 1 to length(trick_sequence))
addtimer(CALLBACK(living_pawn, TYPE_PROC_REF(/mob, emote), trick_sequence[index], index * 0.5 SECONDS))
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
return SUBTREE_RETURN_FINISH_PLANNING
#undef PET_PLAYTIME_COOLDOWN
#undef MESSAGE_EXPIRY_TIME