mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-16 12:43:09 +00:00
## About The Pull Request Lobstrosities can now be raised from aquarium icemoon/lavaland chrabs. First of all, you've to get a live chrab, an aquarium, and some fish feed. Second, you place the chrab inside the aquarium and turn the 'allow breeding' settting on (should probably rename it to a more apt name now). Keep the chrab well fed, and possibly with some friends and props in the same aquarium until it develops into a hopefully calm juveline lobstrosity and plops out of the aquarium (it can take some time). From there you can tame it by feeding it its favorite food: arms and lavaloop fish, and wait another dozen minutes for it to mature into a fully grown lobstrosity. Juveline lobstrosities are basically smaller and weaker lobstrosities, if not slightly faster in some ways. Unlike their taller counterparts, they can be tamed. Once done so, they'll retain their tamedness once grown up. Regardless, tamed lobstrosities can be given the pet command to fish for things by pointing at them. Thanks BenMatrix for the profound fisher component, woo. The chrab's weigth and size influence the growth speed of the first stage faster, meaning bigger chrabs (may require crossbreeding) will turn into juveline lobstrosities quickly. Amongst other things influencing the resulting mob are fish traits: Several traits have been given effects that apply to the mob, such as nocturnal regeneration, being venomous or being able to fly akin space carps. Also a new one that prevents the resulting lobstrosity from fully developing Now tested. ## Why It's Good For The Game I'm building upon fishing and aquarium stuff, which has been an interest of mine in a good while, though most of it doesn't have that many practical uses, I'm slowly trying to make it cooler, and chasm chrabs growing into lobstrosities is pretty much in line with the fluff texts for the fish. Eventually I'll have to add tips inside fishing toolboxes, otherwise people won't know even half of it. ## Changelog 🆑 add: You can raise lobstrosities from chasm chrabs inside an aquarium with the 'allow breeding' setting on. Keep the fish well fed, healthy and not lonely if you don't want an hostile one. add: Juveline lobstrosities (from chasms, plasma rivers, or aquariums, xenobio too) can be tamed with arms and lavaloop fishes. add: For lobstrosities grown from aquariums, they can have additional effects based on the fish traits they had in the aquarium, like being venomous or even flying. /🆑
297 lines
12 KiB
Plaintext
297 lines
12 KiB
Plaintext
// None of these are really complex enough to merit their own file
|
|
|
|
/**
|
|
* # Pet Command: Idle
|
|
* Tells a pet to resume its idle behaviour, usually staying put where you leave it
|
|
*/
|
|
/datum/pet_command/idle
|
|
command_name = "Stay"
|
|
command_desc = "Command your pet to stay idle in this location."
|
|
radial_icon = 'icons/obj/bed.dmi'
|
|
radial_icon_state = "dogbed"
|
|
speech_commands = list("sit", "stay", "stop")
|
|
command_feedback = "sits"
|
|
|
|
/datum/pet_command/idle/execute_action(datum/ai_controller/controller)
|
|
return SUBTREE_RETURN_FINISH_PLANNING // This cancels further AI planning
|
|
|
|
/**
|
|
* # Pet Command: Stop
|
|
* Tells a pet to exit command mode and resume its normal behaviour, which includes regular target-seeking and what have you
|
|
*/
|
|
/datum/pet_command/free
|
|
command_name = "Loose"
|
|
command_desc = "Allow your pet to resume its natural behaviours."
|
|
radial_icon = 'icons/mob/actions/actions_spells.dmi'
|
|
radial_icon_state = "repulse"
|
|
speech_commands = list("free", "loose")
|
|
command_feedback = "relaxes"
|
|
|
|
/datum/pet_command/free/execute_action(datum/ai_controller/controller)
|
|
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
|
|
return // Just move on to the next planning subtree.
|
|
|
|
/**
|
|
* # Pet Command: Follow
|
|
* Tells a pet to follow you until you tell it to do something else
|
|
*/
|
|
/datum/pet_command/follow
|
|
command_name = "Follow"
|
|
command_desc = "Command your pet to accompany you."
|
|
radial_icon = 'icons/testing/turf_analysis.dmi'
|
|
radial_icon_state = "red_arrow"
|
|
speech_commands = list("heel", "follow")
|
|
///the behavior we use to follow
|
|
var/follow_behavior = /datum/ai_behavior/pet_follow_friend
|
|
|
|
/datum/pet_command/follow/set_command_active(mob/living/parent, mob/living/commander)
|
|
. = ..()
|
|
set_command_target(parent, commander)
|
|
|
|
/datum/pet_command/follow/execute_action(datum/ai_controller/controller)
|
|
controller.queue_behavior(follow_behavior, BB_CURRENT_PET_TARGET)
|
|
return SUBTREE_RETURN_FINISH_PLANNING
|
|
|
|
/**
|
|
* # Pet Command: Play Dead
|
|
* Pretend to be dead for a random period of time
|
|
*/
|
|
/datum/pet_command/play_dead
|
|
command_name = "Play Dead"
|
|
command_desc = "Play a macabre trick."
|
|
radial_icon = 'icons/mob/simple/pets.dmi'
|
|
radial_icon_state = "puppy_dead"
|
|
speech_commands = list("play dead") // Don't get too creative here, people talk about dying pretty often
|
|
|
|
/datum/pet_command/play_dead/execute_action(datum/ai_controller/controller)
|
|
controller.queue_behavior(/datum/ai_behavior/play_dead)
|
|
return SUBTREE_RETURN_FINISH_PLANNING
|
|
|
|
/**
|
|
* # Pet Command: Good Boy
|
|
* React if complimented
|
|
*/
|
|
/datum/pet_command/good_boy
|
|
command_name = "Good Boy"
|
|
command_desc = "Give your pet a compliment."
|
|
hidden = TRUE
|
|
|
|
/datum/pet_command/good_boy/New(mob/living/parent)
|
|
. = ..()
|
|
speech_commands += "good [parent.name]"
|
|
switch (parent.gender)
|
|
if (MALE)
|
|
speech_commands += "good boy"
|
|
return
|
|
if (FEMALE)
|
|
speech_commands += "good girl"
|
|
return
|
|
// If we get past this point someone has finally added a non-binary dog
|
|
|
|
/datum/pet_command/good_boy/execute_action(datum/ai_controller/controller)
|
|
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
|
|
var/mob/living/parent = weak_parent.resolve()
|
|
if (!parent)
|
|
return SUBTREE_RETURN_FINISH_PLANNING
|
|
|
|
new /obj/effect/temp_visual/heart(parent.loc)
|
|
parent.emote("spin")
|
|
return SUBTREE_RETURN_FINISH_PLANNING
|
|
|
|
/**
|
|
* # Pet Command: Use ability
|
|
* Use an an ability that does not require any targets
|
|
*/
|
|
/datum/pet_command/untargeted_ability
|
|
///untargeted ability we will use
|
|
var/ability_key
|
|
|
|
/datum/pet_command/untargeted_ability/execute_action(datum/ai_controller/controller)
|
|
var/datum/action/cooldown/ability = controller.blackboard[ability_key]
|
|
if(!ability?.IsAvailable())
|
|
return
|
|
controller.queue_behavior(/datum/ai_behavior/use_mob_ability, ability_key)
|
|
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
|
|
return SUBTREE_RETURN_FINISH_PLANNING
|
|
|
|
/**
|
|
* # Pet Command: Attack
|
|
* Tells a pet to chase and bite the next thing you point at
|
|
*/
|
|
/datum/pet_command/point_targeting/attack
|
|
command_name = "Attack"
|
|
command_desc = "Command your pet to attack things that you point out to it."
|
|
radial_icon = 'icons/effects/effects.dmi'
|
|
radial_icon_state = "bite"
|
|
|
|
speech_commands = list("attack", "sic", "kill")
|
|
command_feedback = "growl"
|
|
pointed_reaction = "and growls"
|
|
/// Balloon alert to display if providing an invalid target
|
|
var/refuse_reaction = "shakes head"
|
|
/// Attack behaviour to use
|
|
var/attack_behaviour = /datum/ai_behavior/basic_melee_attack
|
|
|
|
// Refuse to target things we can't target, chiefly other friends
|
|
/datum/pet_command/point_targeting/attack/set_command_target(mob/living/parent, atom/target)
|
|
if (!target)
|
|
return
|
|
var/mob/living/living_parent = parent
|
|
if (!living_parent.ai_controller)
|
|
return
|
|
var/datum/targeting_strategy/targeter = GET_TARGETING_STRATEGY(living_parent.ai_controller.blackboard[targeting_strategy_key])
|
|
if (!targeter)
|
|
return
|
|
if (!targeter.can_attack(living_parent, target))
|
|
refuse_target(parent, target)
|
|
return
|
|
return ..()
|
|
|
|
/// Display feedback about not targeting something
|
|
/datum/pet_command/point_targeting/attack/proc/refuse_target(mob/living/parent, atom/target)
|
|
var/mob/living/living_parent = parent
|
|
living_parent.balloon_alert_to_viewers("[refuse_reaction]")
|
|
living_parent.visible_message(span_notice("[living_parent] refuses to attack [target]."))
|
|
|
|
/datum/pet_command/point_targeting/attack/execute_action(datum/ai_controller/controller)
|
|
controller.queue_behavior(attack_behaviour, BB_CURRENT_PET_TARGET, targeting_strategy_key)
|
|
return SUBTREE_RETURN_FINISH_PLANNING
|
|
|
|
/**
|
|
* # Breed command. breed with a partner!
|
|
*/
|
|
/datum/pet_command/point_targeting/breed
|
|
command_name = "Breed"
|
|
command_desc = "Command your pet to attempt to breed with a partner."
|
|
radial_icon = 'icons/mob/simple/animal.dmi'
|
|
radial_icon_state = "heart"
|
|
speech_commands = list("breed", "consummate")
|
|
var/datum/ai_behavior/reproduce_behavior = /datum/ai_behavior/make_babies
|
|
|
|
/datum/pet_command/point_targeting/breed/set_command_target(mob/living/parent, atom/target)
|
|
if(isnull(target) || !isliving(target))
|
|
return
|
|
if(!HAS_TRAIT(parent, TRAIT_MOB_BREEDER) || !HAS_TRAIT(target, TRAIT_MOB_BREEDER))
|
|
return
|
|
if(isnull(parent.ai_controller))
|
|
return
|
|
if(!parent.ai_controller.blackboard[BB_BREED_READY] || isnull(parent.ai_controller.blackboard[BB_BABIES_PARTNER_TYPES]))
|
|
return
|
|
var/mob/living/living_target = target
|
|
if(!living_target.ai_controller?.blackboard[BB_BREED_READY])
|
|
return
|
|
return ..()
|
|
|
|
/datum/pet_command/point_targeting/breed/execute_action(datum/ai_controller/controller)
|
|
if(is_type_in_list(controller.blackboard[BB_CURRENT_PET_TARGET], controller.blackboard[BB_BABIES_PARTNER_TYPES]))
|
|
controller.queue_behavior(reproduce_behavior, BB_CURRENT_PET_TARGET)
|
|
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
|
|
return SUBTREE_RETURN_FINISH_PLANNING
|
|
|
|
/**
|
|
* # Pet Command: Targetted Ability
|
|
* Tells a pet to use some kind of ability on the next thing you point at
|
|
*/
|
|
/datum/pet_command/point_targeting/use_ability
|
|
command_name = "Use ability"
|
|
command_desc = "Command your pet to use one of its special skills on something that you point out to it."
|
|
radial_icon = 'icons/mob/actions/actions_spells.dmi'
|
|
radial_icon_state = "projectile"
|
|
speech_commands = list("shoot", "blast", "cast")
|
|
command_feedback = "growl"
|
|
pointed_reaction = "and growls"
|
|
/// Blackboard key where a reference to some kind of mob ability is stored
|
|
var/pet_ability_key
|
|
/// The AI behavior to use for the ability
|
|
var/ability_behavior = /datum/ai_behavior/pet_use_ability
|
|
|
|
/datum/pet_command/point_targeting/use_ability/execute_action(datum/ai_controller/controller)
|
|
if (!pet_ability_key)
|
|
return
|
|
var/datum/action/cooldown/using_action = controller.blackboard[pet_ability_key]
|
|
if (QDELETED(using_action))
|
|
return
|
|
// We don't check if the target exists because we want to 'sit attentively' if we've been instructed to attack but not given one yet
|
|
// We also don't check if the cooldown is over because there's no way a pet owner can know that, the behaviour will handle it
|
|
controller.queue_behavior(ability_behavior, pet_ability_key, BB_CURRENT_PET_TARGET)
|
|
return SUBTREE_RETURN_FINISH_PLANNING
|
|
|
|
/datum/pet_command/protect_owner
|
|
command_name = "Protect owner"
|
|
command_desc = "Your pet will run to your aid."
|
|
hidden = TRUE
|
|
///the range our owner needs to be in for us to protect him
|
|
var/protect_range = 9
|
|
///the behavior we will use when he is attacked
|
|
var/protect_behavior = /datum/ai_behavior/basic_melee_attack
|
|
///message cooldown to prevent too many people from telling you not to commit suicide
|
|
COOLDOWN_DECLARE(self_harm_message_cooldown)
|
|
|
|
/datum/pet_command/protect_owner/add_new_friend(mob/living/tamer)
|
|
RegisterSignal(tamer, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(set_attacking_target))
|
|
if(!HAS_TRAIT(tamer, TRAIT_RELAYING_ATTACKER))
|
|
tamer.AddElement(/datum/element/relay_attackers)
|
|
|
|
/datum/pet_command/protect_owner/remove_friend(mob/living/unfriended)
|
|
UnregisterSignal(unfriended, COMSIG_ATOM_WAS_ATTACKED)
|
|
|
|
/datum/pet_command/protect_owner/execute_action(datum/ai_controller/controller)
|
|
var/mob/living/victim = controller.blackboard[BB_CURRENT_PET_TARGET]
|
|
if(QDELETED(victim))
|
|
return
|
|
// cancel the action if they're below our given crit stat, OR if we're trying to attack ourselves (this can happen on tamed mobs w/ protect subtree rarely)
|
|
if(victim.stat > controller.blackboard[BB_TARGET_MINIMUM_STAT] || victim == controller.pawn)
|
|
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
|
|
return
|
|
controller.queue_behavior(protect_behavior, BB_CURRENT_PET_TARGET, BB_PET_TARGETING_STRATEGY)
|
|
return SUBTREE_RETURN_FINISH_PLANNING
|
|
|
|
/datum/pet_command/protect_owner/set_command_active(mob/living/parent, mob/living/victim)
|
|
. = ..()
|
|
set_command_target(parent, victim)
|
|
|
|
/datum/pet_command/protect_owner/proc/set_attacking_target(atom/source, mob/living/attacker)
|
|
SIGNAL_HANDLER
|
|
|
|
var/mob/living/basic/owner = weak_parent.resolve()
|
|
if(isnull(owner))
|
|
return
|
|
if(source == attacker)
|
|
var/list/interventions = owner.ai_controller?.blackboard[BB_OWNER_SELF_HARM_RESPONSES] || list()
|
|
if (length(interventions) && COOLDOWN_FINISHED(src, self_harm_message_cooldown) && prob(30))
|
|
COOLDOWN_START(src, self_harm_message_cooldown, 5 SECONDS)
|
|
var/chosen_statement = pick(interventions)
|
|
INVOKE_ASYNC(owner, TYPE_PROC_REF(/atom/movable, say), chosen_statement)
|
|
return
|
|
var/mob/living/current_target = owner.ai_controller?.blackboard[BB_CURRENT_PET_TARGET]
|
|
if(attacker == current_target) //we are already dealing with this target
|
|
return
|
|
if(isliving(attacker) && can_see(owner, attacker, protect_range))
|
|
set_command_active(owner, attacker)
|
|
|
|
/**
|
|
* # Fish command: command the mob to fish at the next fishing spot you point at. Requires the profound fisher component
|
|
*/
|
|
/datum/pet_command/point_targeting/fish
|
|
command_name = "Fish"
|
|
command_desc = "Command your pet to try fishing at a nearby fishing spot."
|
|
radial_icon = 'icons/obj/aquarium/fish.dmi'
|
|
radial_icon_state = "goldfish"
|
|
speech_commands = list("fish")
|
|
|
|
// Refuse to target things we can't target, chiefly other friends
|
|
/datum/pet_command/point_targeting/fish/set_command_target(mob/living/parent, atom/target)
|
|
if (!target)
|
|
return
|
|
if(!parent.ai_controller || !HAS_TRAIT(parent, TRAIT_PROFOUND_FISHER))
|
|
return
|
|
var/datum/targeting_strategy/targeter = GET_TARGETING_STRATEGY(/datum/targeting_strategy/fishing)
|
|
if (!targeter?.can_attack(parent, target))
|
|
parent.balloon_alert_to_viewers("shakes head!")
|
|
return
|
|
return ..()
|
|
|
|
/datum/pet_command/point_targeting/fish/execute_action(datum/ai_controller/controller)
|
|
controller.queue_behavior(/datum/ai_behavior/hunt_target/unarmed_attack_target/reset_target_combat_mode, BB_CURRENT_PET_TARGET)
|
|
return SUBTREE_RETURN_FINISH_PLANNING
|