mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-23 05:00:55 +01:00
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:
@@ -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
|
||||
Reference in New Issue
Block a user