Raptor Rework - Ranching and Companionship (#93564)

This commit is contained in:
SmArtKar
2025-11-01 22:13:29 +11:00
committed by GitHub
parent 30151ccc20
commit 1c6c506936
58 changed files with 2608 additions and 1621 deletions
File diff suppressed because it is too large Load Diff
+1
View File
@@ -81,6 +81,7 @@
#define BB_BASIC_MOB_CURRENT_TARGET "BB_basic_current_target"
#define BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION "BB_basic_current_target_hiding_location"
#define BB_TARGETING_STRATEGY "targeting_strategy"
#define BB_HUNT_TARGETING_STRATEGY "hunt_targeting_strategy"
///some behaviors that check current_target also set this on deep crit mobs
#define BB_BASIC_MOB_EXECUTION_TARGET "BB_basic_execution_target"
///Blackboard key for a whitelist typecache of "things we can target while trying to move"
+61 -18
View File
@@ -39,40 +39,83 @@
#define MOOK_ATTACK_ACTIVE 2
#define MOOK_ATTACK_STRIKE 3
///keeps track of how many gutlunches are born
/// Keeps track of how many gutlunches are born
GLOBAL_VAR_INIT(gutlunch_count, 0)
///Pet customization settings saved for every client
/// Pet customization settings saved for every client
GLOBAL_LIST_EMPTY(customized_pets)
//raptor defines
// Raptor defines
/// Raptor chick, tiny and very frail
#define RAPTOR_BABY "baby"
/// Raptor youngling, cannot be ridden and has reduced stats, but can hunt by itself just fine
#define RAPTOR_YOUNG "young"
/// Fully grown adult raptor
#define RAPTOR_ADULT "adult"
#define RAPTOR_RED "Red"
#define RAPTOR_GREEN "Green"
#define RAPTOR_PURPLE "Purple"
#define RAPTOR_WHITE "White"
#define RAPTOR_YELLOW "Yellow"
#define RAPTOR_BLACK "Black"
#define RAPTOR_BLUE "Blue"
/// Innate raptor offsets
#define RAPTOR_INNATE_SOURCE "raptor_innate"
// Growth values
/// How much does meal complexity affect our growth?
#define RAPTOR_MEAL_COMPLEXITY_GROWTH_FACTOR 5
/// Base value for raptor growth from meat
#define RAPTOR_GROWTH_BASE_MEAT 10
/// Base value for raptor growth from ash flora
#define RAPTOR_GROWTH_BASE_PLANT 5
/// How much growth progress raptors need to accumulate to fully grow into an adult
#define RAPTOR_GROWTH_REQUIRED 100
/// Minimum random growth value a baby raptor can gain per second
#define RAPTOR_BABY_GROWTH_LOWER 0.5
/// Maximum random growth value a baby raptor can gain per second
#define RAPTOR_BABY_GROWTH_UPPER 0.8
/// How long it takes for a raptor egg to grow up, in seconds
#define RAPTOR_EGG_GROWTH_PROGRESS 100
/// How much happiness percentage affects our growth speed
#define RAPTOR_GROWTH_HAPPINESS_MULTIPLIER 0.005 // Full happiness increases growth rate by 50%
/// Damage boost per happiness percent
#define RAPTOR_HAPPINESS_DAMAGE_BOOST 0.05
// Raptor inheritance stats
/// Maximum amount of traits a raptor can inherit
#define RAPTOR_TRAIT_INHERIT_AMOUNT 2
/// Minimum modifier to base attack values a raptor can get
#define RAPTOR_INHERIT_MIN_ATTACK -5
/// Maximum modifier to base attack values a raptor can get
#define RAPTOR_INHERIT_MAX_ATTACK 5
/// Minimum modifier to the base health value a raptor can get
#define RAPTOR_INHERIT_MIN_HEALTH -30
/// Maximum modifier to the base health value a raptor can get
#define RAPTOR_INHERIT_MAX_HEALTH 30
/// Minimum modifier to base speed values a raptor can get
#define RAPTOR_INHERIT_MIN_SPEED -0.66
/// Maximum modifier to base speed values a raptor can get
#define RAPTOR_INHERIT_MAX_SPEED 0.66
/// Minimum modifier a raptor can get to their modifiers, such as ability effect and growth speed
#define RAPTOR_INHERIT_MIN_MODIFIER -0.25
/// Maximum modifier a raptor can get to their modifiers, such as ability effect and growth speed
#define RAPTOR_INHERIT_MAX_MODIFIER 0.25
/// Genetic drift for raptors, aka min/max value from the cap that stats can receive when breeding
#define RAPTOR_GENETIC_DRIFT 0.2
///this mob suffers depression
/// This mob suffers depression
#define BB_BASIC_DEPRESSED "basic_depressed"
///this mob will care for its young
/// This mob will care for its young
#define BB_RAPTOR_MOTHERLY "raptor_motherly"
///this mob will be playful around their owners
/// This mob will be playful around their owners
#define BB_RAPTOR_PLAYFUL "raptor_playful"
///this mob will flee combat when it feels threatened
/// This mob will flee combat when it feels threatened
#define BB_RAPTOR_COWARD "raptor_coward"
///our raptor baby target we will take care of
/// Our raptor baby target we will take care of
#define BB_RAPTOR_BABY "raptor_baby"
///the raptor we will heal up
/// The raptor we will heal up
#define BB_INJURED_RAPTOR "injured_raptor"
///the cooldown for next time we eat
/// The cooldown for next time we eat
#define BB_RAPTOR_EAT_COOLDOWN "raptor_eat_cooldown"
///our trough target
/// Our trough target
#define BB_RAPTOR_TROUGH_TARGET "raptor_trough_target"
/// HP level at which we'll flee from attackers
#define BB_RAPTOR_FLEE_THRESHOLD "raptor_flee_threshold"
#define MAX_RAPTOR_POP 64
@@ -74,6 +74,9 @@
///Sent to the newly spawned object when it's baked in an oven.
#define COMSIG_ITEM_BAKED_RESULT "item_baked_result"
/// From /datum/element/basic_eating/finish_eating() : (mob/living/eater, mob/living/feeder)
#define COMSIG_ITEM_EATEN_BY_BASIC_MOB "item_eaten_by_basic_mob"
///Called on the result spawned during decomposition: (obj/decomposed)
#define COMSIG_OBJ_DECOMPOSITION_RESULT "obj_decomposition_result"
@@ -28,3 +28,8 @@
#define COMSIG_BASICMOB_MUTATED "basicmob_mutated"
///cancel further mutation modifications to the mob such as shiny mutation.
#define MUTATED_NO_FURTHER_MUTATIONS (1 << 0)
/// from /mob/living/basic/raptor/proc/change_growth_stage() : (new_stage, prev_stage)
#define COMSIG_RAPTOR_GROWTH_STAGE_CHANGE "raptor_growth_stage_change"
/// Cancel raptor growth
#define COMPONENT_CANCEL_RAPTOR_GROWTH (1<<0)
@@ -182,3 +182,6 @@
//from base of [/obj/effect/particle_effect/fluid/smoke/proc/smoke_mob]: (seconds_per_tick)
#define COMSIG_CARBON_EXPOSED_TO_SMOKE "carbon_exposed_to_smoke"
/// From /mob/living/carbon/human/proc/update_mob_height() : (old_height)
#define COMSIG_HUMAN_HEIGHT_UPDATED "human_height_updated"
@@ -365,3 +365,6 @@
#define COMSIG_LIVING_DROP_LOOT "living_drop_loot"
/// Prevent loot from being dropped
#define COMPONENT_NO_LOOT_DROP (1<<0)
/// From /mob/living/proc/mob_pickup() : (mob/living/user, obj/item/mob_holder/holder)
#define COMSIG_LIVING_SCOOPED_UP "living_scooped_up"
@@ -637,3 +637,9 @@
/// Sent from /obj/machinert/console/camera_advanced/attack_hand() : (mob/eye/camera/remote/new_camera)
#define COMSIG_ADVANCED_CAMERA_EYE_CREATED "advanced_camera_eye_created"
/// Sent from /obj/item/mob_holder/purple_raptor/proc/toggle_wings() : (mob/living/carbon/human/user)
#define COMSIG_RAPTOR_WINGS_OPENED "raptor_wings_opened"
/// Sent from /obj/item/mob_holder/purple_raptor/proc/toggle_wings() : (mob/living/carbon/human/user)
#define COMSIG_RAPTOR_WINGS_CLOSED "raptor_wings_closed"
+3 -3
View File
@@ -370,14 +370,14 @@ rough example of the "cone" made by the 3 dirs checked
/// Returns an x and y value require to reverse the transformations made to center an oversized icon
/atom/proc/get_oversized_icon_offsets()
if (pixel_x == 0 && pixel_y == 0)
if (!base_pixel_x && !base_pixel_y && !base_pixel_w && !base_pixel_z)
return list("x" = 0, "y" = 0)
var/list/icon_dimensions = get_icon_dimensions(icon)
var/icon_width = icon_dimensions["width"]
var/icon_height = icon_dimensions["height"]
return list(
"x" = icon_width > ICON_SIZE_X && pixel_x != 0 ? (icon_width - ICON_SIZE_X) * 0.5 : 0,
"y" = icon_height > ICON_SIZE_Y && pixel_y != 0 ? (icon_height - ICON_SIZE_Y) * 0.5 : 0,
"x" = icon_width > ICON_SIZE_X && (base_pixel_x || base_pixel_w) ? (icon_width - ICON_SIZE_X) * 0.5 : 0,
"y" = icon_height > ICON_SIZE_Y && (base_pixel_y || base_pixel_z) ? (icon_height - ICON_SIZE_Y) * 0.5 : 0,
)
/// Helper for easily adding blood from INSIDE a mob to an atom (NOT blood ON the mob)
+6 -4
View File
@@ -205,8 +205,10 @@ GLOBAL_LIST_INIT(skin_tone_names, list(
* @param {icon} icon - The icon file of the cog. Default: 'icons/effects/progressbar.dmi'
*
* @param {iconstate} iconstate - The icon state of the cog. Default: "Cog"
*
* @param {mob} bar_override - Mob which should see the bar instead of the user
*/
/proc/do_after(mob/user, delay, atom/target, timed_action_flags = NONE, progress = TRUE, datum/callback/extra_checks, interaction_key, max_interact_count = 1, hidden = FALSE, icon = 'icons/effects/progressbar.dmi', iconstate = "cog")
/proc/do_after(mob/user, delay, atom/target, timed_action_flags = NONE, progress = TRUE, datum/callback/extra_checks, interaction_key, max_interact_count = 1, hidden = FALSE, icon = 'icons/effects/progressbar.dmi', iconstate = "cog", mob/bar_override = null)
if(!user)
return FALSE
if(!isnum(delay))
@@ -240,11 +242,11 @@ GLOBAL_LIST_INIT(skin_tone_names, list(
var/datum/cogbar/cog
if(progress)
if(user.client)
progbar = new(user, delay, target || user)
if(user.client || bar_override?.client)
progbar = new(bar_override || user, delay, target || user)
if(!hidden && delay >= 1 SECONDS)
cog = new(user, icon, iconstate)
cog = new(bar_override || user, icon, iconstate)
SEND_SIGNAL(user, COMSIG_DO_AFTER_BEGAN)
+5 -3
View File
@@ -3,10 +3,12 @@
*/
/datum/ai_planning_subtree/make_babies
operational_datums = list(/datum/component/breed)
///chance to make babies
/// Chance to make babies
var/chance = 5
///make babies behavior we will use
/// Make babies behavior we will use
var/datum/ai_behavior/reproduce_behavior = /datum/ai_behavior/make_babies
/// Find partner behavior we will use
var/datum/ai_behavior/partner_behavior = /datum/ai_behavior/find_partner
/datum/ai_planning_subtree/make_babies/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
. = ..()
@@ -25,4 +27,4 @@
return
// Find target
controller.queue_behavior(/datum/ai_behavior/find_partner, BB_BABIES_TARGET, BB_BABIES_PARTNER_TYPES, BB_BABIES_CHILD_TYPES)
controller.queue_behavior(partner_behavior, BB_BABIES_TARGET, BB_BABIES_PARTNER_TYPES, BB_BABIES_CHILD_TYPES)
@@ -4,21 +4,32 @@
/datum/ai_behavior/find_mom/perform(seconds_per_tick, datum/ai_controller/controller, mom_key, ignore_mom_key, found_mom)
var/mob/living_pawn = controller.pawn
var/list/mom_types = controller.blackboard[mom_key]
var/list/all_moms = list()
var/list/mom_types = controller.blackboard[mom_key]
var/list/ignore_types = controller.blackboard[ignore_mom_key]
if(!length(mom_types))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
for(var/mob/mother in oview(look_range, living_pawn))
if(!is_type_in_list(mother, mom_types))
continue
if(is_type_in_list(mother, ignore_types)) //so the not permanent baby and the permanent baby subtype dont followed each other
continue
all_moms += mother
if (is_possible_mom(mother, mom_types, ignore_types))
all_moms += mother
if(length(all_moms))
controller.set_blackboard_key(found_mom, pick(all_moms))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
/datum/ai_behavior/find_mom/proc/is_possible_mom(mob/mother, list/mom_types, list/ignore_types)
if(!is_type_in_list(mother, mom_types))
return FALSE
if(is_type_in_list(mother, ignore_types)) // so the not permanent baby and the permanent baby subtype dont followed each other
return FALSE
return TRUE
/datum/ai_behavior/find_mom/raptor/is_possible_mom(mob/mother, list/mom_types, list/ignore_types)
. = ..()
if (!. || !istype(mother, /mob/living/basic/raptor))
return FALSE
var/mob/living/basic/raptor/raptor = mother
return raptor.growth_stage == RAPTOR_ADULT
@@ -7,7 +7,7 @@
var/mob/baby = controller.pawn
if(QDELETED(target))
controller.queue_behavior(/datum/ai_behavior/find_mom, BB_FIND_MOM_TYPES, BB_IGNORE_MOM_TYPES, BB_FOUND_MOM)
find_mom(controller)
return
if(get_dist(target, baby) > minimum_distance)
@@ -23,3 +23,9 @@
controller.queue_behavior(/datum/ai_behavior/perform_emote, "dances around their parent!")
return SUBTREE_RETURN_FINISH_PLANNING
/datum/ai_planning_subtree/look_for_adult/proc/find_mom(datum/ai_controller/controller)
controller.queue_behavior(/datum/ai_behavior/find_mom, BB_FIND_MOM_TYPES, BB_IGNORE_MOM_TYPES, BB_FOUND_MOM)
/datum/ai_planning_subtree/look_for_adult/raptor/find_mom(datum/ai_controller/controller)
controller.queue_behavior(/datum/ai_behavior/find_mom/raptor, BB_FIND_MOM_TYPES, BB_FOUND_MOM)
@@ -1,10 +1,12 @@
/datum/ai_planning_subtree/simple_find_target
/// Variable to store target in
var/target_key = BB_BASIC_MOB_CURRENT_TARGET
/// Targeting strategy key to use
var/strategy_key = BB_TARGETING_STRATEGY
/datum/ai_planning_subtree/simple_find_target/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
. = ..()
controller.queue_behavior(/datum/ai_behavior/find_potential_targets, target_key, BB_TARGETING_STRATEGY, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION)
controller.queue_behavior(/datum/ai_behavior/find_potential_targets, target_key, strategy_key, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION)
// Prevents finding a target if a human is nearby
/datum/ai_planning_subtree/simple_find_target/not_while_observed
@@ -21,4 +23,7 @@
/datum/ai_planning_subtree/simple_find_target/increased_range
/datum/ai_planning_subtree/simple_find_target/increased_range/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
controller.queue_behavior(/datum/ai_behavior/find_potential_targets/bigger_range, target_key, BB_TARGETING_STRATEGY, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION)
controller.queue_behavior(/datum/ai_behavior/find_potential_targets/bigger_range, target_key, strategy_key, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION)
/datum/ai_planning_subtree/simple_find_target/hunt
strategy_key = BB_HUNT_TARGETING_STRATEGY
@@ -154,3 +154,6 @@
if(!istype(target) || target.has_status_effect(/datum/status_effect/gutted))
return FALSE
return ..()
/datum/targeting_strategy/basic/exact_match
check_factions_exactly = TRUE
@@ -16,7 +16,7 @@
if(!(target.resistance_flags & INDESTRUCTIBLE))
return TRUE
if(target in living_mob.ai_controller.blackboard[BB_FRIENDS_LIST])
if(isliving(target) && (target in living_mob.ai_controller.blackboard[BB_FRIENDS_LIST]))
return FALSE
return ..()
+1 -1
View File
@@ -25,7 +25,7 @@
// With tongs in hand!
BB_TARGET_HELD_ITEM = /obj/item/kitchen/tongs,
BB_BABIES_PARTNER_TYPES = list(/mob/living/basic/pet/dog),
BB_BABIES_CHILD_TYPES = list(/mob/living/basic/pet/dog/corgi),
BB_BABIES_CHILD_TYPES = list(/mob/living/basic/pet/dog/corgi/puppy),
)
planning_subtrees = list(
+6 -7
View File
@@ -2,17 +2,17 @@
* A component to allow us to breed
*/
/datum/component/breed
/// additional mobs we can breed with
/// Additional mobs we can breed with
var/list/can_breed_with
///weighted list of the possible baby types
/// Weighted list of the possible baby types
var/list/baby_paths
///time to wait after breeding
/// Time to wait after breeding
var/breed_timer
///AI key we set when we're ready to breed
/// AI key we set when we're ready to breed
var/breed_key = BB_BREED_READY
///are we ready to breed?
/// Are we ready to breed?
var/ready_to_breed = TRUE
///callback after we give birth to the child
/// Callback after we give birth to the child
var/datum/callback/post_birth
/datum/component/breed/Initialize(list/can_breed_with = list(), breed_timer = 40 SECONDS, baby_paths = list(), post_birth)
@@ -44,7 +44,6 @@
REMOVE_TRAIT(parent, TRAIT_MOB_BREEDER, REF(src))
post_birth = null
/datum/component/breed/proc/breed_with_partner(mob/living/source, mob/living/target)
SIGNAL_HANDLER
+32 -19
View File
@@ -62,7 +62,7 @@
/datum/component/happiness/proc/on_eat(datum/source)
SIGNAL_HANDLER
increase_happiness_level(on_eat_change)
change_happiness_level(on_eat_change)
/datum/component/happiness/proc/on_clean(mob/living/source)
SIGNAL_HANDLER
@@ -74,7 +74,7 @@
return NONE
COOLDOWN_START(src, groom_cooldown, GROOM_COOLDOWN)
increase_happiness_level(on_groom_change)
change_happiness_level(on_groom_change)
return COMPONENT_CLEANED|COMPONENT_CLEANED_GAIN_XP
/datum/component/happiness/proc/on_petted(datum/source, mob/living/petter, list/modifiers)
@@ -91,42 +91,55 @@
/datum/component/happiness/proc/pet_animal()
if(!COOLDOWN_FINISHED(src, pet_cooldown))
return
increase_happiness_level(on_petted_change)
change_happiness_level(on_petted_change)
COOLDOWN_START(src, pet_cooldown, PET_COOLDOWN)
/datum/component/happiness/proc/increase_happiness_level(amount)
happiness_level = min(happiness_level + amount, maximum_happiness)
if(!HAS_TRAIT(parent, TRAIT_MOB_HIDE_HAPPINESS))
var/mob/living/living_parent = parent
/datum/component/happiness/proc/change_happiness_level(amount)
var/old_happiness = happiness_level
happiness_level = clamp(happiness_level + amount, 0, maximum_happiness)
var/happiness_percentage = happiness_level / maximum_happiness
var/old_percentage = old_happiness / maximum_happiness
var/old_threshold = 0
var/new_threshold = 0
for(var/check_percentage in callback_percentages)
if (check_percentage <= old_percentage)
old_threshold = check_percentage
if (check_percentage <= happiness_percentage)
new_threshold = check_percentage
if (old_threshold != new_threshold)
SEND_SIGNAL(parent, COMSIG_MOB_HAPPINESS_CHANGE, happiness_percentage)
happiness_callback?.Invoke(happiness_percentage)
var/mob/living/living_parent = parent
living_parent.ai_controller?.set_blackboard_key(blackboard_key, happiness_percentage)
if(!HAS_TRAIT(parent, TRAIT_MOB_HIDE_HAPPINESS) && amount > 0)
new /obj/effect/temp_visual/heart(living_parent.loc)
living_parent.spin(spintime = 2 SECONDS, speed = 1)
START_PROCESSING(SSprocessing, src)
if (happiness_level > 0)
START_PROCESSING(SSprocessing, src)
/datum/component/happiness/proc/view_happiness(mob/living/source, mob/living/clicker)
if(HAS_TRAIT(source, TRAIT_MOB_HIDE_HAPPINESS) || !istype(clicker) || !COOLDOWN_FINISHED(src, happiness_inspect) || !source.IsReachableBy(clicker))
return
var/y_position = source.get_cached_height() + 1
var/obj/effect/overlay/happiness_overlay/hearts = new
hearts.pixel_w -= source.base_pixel_x + source.base_pixel_w
hearts.pixel_y = y_position
hearts.set_hearts(happiness_level/maximum_happiness)
hearts.set_hearts(happiness_level / maximum_happiness)
source.vis_contents += hearts
COOLDOWN_START(src, happiness_inspect, INSPECT_TIMER)
/datum/component/happiness/process()
var/mob/living/living_parent = parent
var/happiness_percentage = happiness_level/maximum_happiness
living_parent.ai_controller?.set_blackboard_key(blackboard_key, happiness_percentage)
var/check_percentage_in_list = round(happiness_percentage * 100, 1)
if(check_percentage_in_list in callback_percentages)
SEND_SIGNAL(parent, COMSIG_MOB_HAPPINESS_CHANGE, happiness_percentage)
happiness_callback?.Invoke(happiness_percentage)
if(happiness_level <= 0)
return PROCESS_KILL
var/mob/living/living_parent = parent
var/modifier = living_parent.ai_controller?.blackboard[BB_BASIC_DEPRESSED] ? 2 : 1
happiness_level = max(0, happiness_level - modifier)
change_happiness_level(-modifier)
/obj/effect/overlay/happiness_overlay
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+15 -5
View File
@@ -45,6 +45,8 @@
var/required_modifier
/// Callback to run after healing a mob
var/datum/callback/after_healed
/// Callback to run to get a multiplier for our healing value
var/datum/callback/healing_multiplier
/datum/component/healing_touch/Initialize(
heal_brute = 20,
@@ -65,6 +67,7 @@
heal_color = COLOR_HEALING_CYAN,
required_modifier = null,
datum/callback/after_healed = null,
datum/callback/healing_multiplier = null,
)
if (!isliving(parent))
return COMPONENT_INCOMPATIBLE
@@ -87,6 +90,7 @@
src.heal_color = heal_color
src.required_modifier = required_modifier
src.after_healed = after_healed
src.healing_multiplier = healing_multiplier
RegisterSignal(parent, COMSIG_LIVING_UNARMED_ATTACK, PROC_REF(try_healing)) // Players
RegisterSignal(parent, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(try_healing)) // NPCs
@@ -190,15 +194,21 @@
if (complete_text)
healer.visible_message(span_notice("[format_string(complete_text, healer, target)]"))
var/mult = 1
if (healing_multiplier)
mult = healing_multiplier.Invoke(healer, target)
if (mult == 0)
return
var/healed = target.heal_overall_damage(
brute = heal_brute,
burn = heal_burn,
stamina = heal_stamina,
brute = heal_brute * mult,
burn = heal_burn * mult,
stamina = heal_stamina * mult,
required_bodytype = required_bodytype,
updating_health = FALSE,
)
healed += target.adjustOxyLoss(-heal_oxy, updating_health = FALSE, required_biotype = valid_biotypes)
healed += target.adjustToxLoss(-heal_tox, updating_health = FALSE, required_biotype = valid_biotypes)
healed += target.adjustOxyLoss(-heal_oxy * mult, updating_health = FALSE, required_biotype = valid_biotypes)
healed += target.adjustToxLoss(-heal_tox * mult, updating_health = FALSE, required_biotype = valid_biotypes)
if (healed <= 0)
return
+39 -8
View File
@@ -1,11 +1,17 @@
/// Component given to mobs that can mine when moving
/datum/component/proficient_miner
/// Toolspeed for mining, 0 will cause it to instamine rock
var/mining_speed = 0
/// Should we pass the do_after visuals to our rider if we are a mob?
var/pass_driver = FALSE
/// Last tick when we bumpmined. Prevents diagonal bumpnining being thrice as fast as normal
var/last_bumpmine_tick = -1
/datum/component/proficient_miner/Initialize()
/datum/component/proficient_miner/Initialize(mining_speed = 0, pass_driver = FALSE)
if (!ismovable(parent))
return COMPONENT_INCOMPATIBLE
src.mining_speed = mining_speed
src.pass_driver = pass_driver
/datum/component/proficient_miner/RegisterWithParent()
RegisterSignal(parent, COMSIG_MOVABLE_BUMP, PROC_REF(on_bump))
@@ -26,12 +32,37 @@
return
var/turf/closed/mineral/mineral_wall = target
if(!istype(mineral_wall, /turf/closed/mineral/gibtonite))
last_bumpmine_tick = world.time
mineral_wall.gets_drilled(source)
if(istype(mineral_wall, /turf/closed/mineral/gibtonite))
var/turf/closed/mineral/gibtonite/gibtonite_wall = mineral_wall
if(gibtonite_wall.stage != GIBTONITE_UNSTRUCK)
return
if(user && mining_speed > 0)
INVOKE_ASYNC(src, PROC_REF(slow_mine), user, target)
return
var/turf/closed/mineral/gibtonite/gibtonite_wall = mineral_wall
if(gibtonite_wall.stage == GIBTONITE_UNSTRUCK)
last_bumpmine_tick = world.time
mineral_wall.gets_drilled(source)
last_bumpmine_tick = world.time
mineral_wall.gets_drilled(source)
/datum/component/proficient_miner/proc/slow_mine(mob/living/user, turf/closed/mineral/mineral_wall)
if(TIMER_COOLDOWN_RUNNING(mineral_wall, REF(user))) //prevents mining turfs in progress
return
var/mining_delay = mineral_wall.tool_mine_speed * mining_speed
TIMER_COOLDOWN_START(mineral_wall, REF(user), mining_delay)
var/static/list/mine_sounds = list('sound/effects/pickaxe/picaxe1.ogg', 'sound/effects/pickaxe/picaxe2.ogg', 'sound/effects/pickaxe/picaxe3.ogg')
playsound(user, pick(mine_sounds), 50)
var/mob/living/driver = null
if (pass_driver && length(user.buckled_mobs))
driver = user.buckled_mobs[1]
if(!do_after(user, mining_delay, mineral_wall, bar_override = driver))
TIMER_COOLDOWN_END(mineral_wall, REF(user)) //if we fail we can start again immediately
return
if (mining_delay > MIN_TOOL_SOUND_DELAY)
playsound(user, pick(mine_sounds), 50)
if(istype(mineral_wall))
mineral_wall.gets_drilled(driver || user)
+47 -13
View File
@@ -119,8 +119,7 @@
return COMPONENT_DRIVER_BLOCK_MOVE
var/mob/living/living_parent = parent
step(living_parent, direction)
var/datum/movespeed_modifier/slowdown = get_cached_movespeed_modifier(living_parent.get_move_intent_slowdown())
var/modified_move_delay = uses_native_speed ? living_parent.cached_multiplicative_slowdown - slowdown?.multiplicative_slowdown : vehicle_move_delay
var/modified_move_delay = uses_native_speed ? living_parent.cached_multiplicative_slowdown : vehicle_move_delay
if(HAS_TRAIT(user, TRAIT_ROUGHRIDER)) // YEEHAW!
switch(HAS_TRAIT(user, TRAIT_PRIMITIVE) ? SANITY_LEVEL_GREAT : user.mob_mood?.sanity_level)
if(SANITY_LEVEL_GREAT)
@@ -133,6 +132,8 @@
modified_move_delay *= 1.1
if(SANITY_LEVEL_INSANE)
modified_move_delay *= 1.2
if(NSCOMPONENT(direction) && EWCOMPONENT(direction))
modified_move_delay = FLOOR(modified_move_delay * sqrt(2), world.tick_lag)
COOLDOWN_START(src, vehicle_move_cooldown, modified_move_delay)
return ..()
@@ -675,18 +676,11 @@
force_dismount(buckled_mob, throw_range = 2, gentle = TRUE)
/datum/component/riding/creature/raptor/get_rider_offsets_and_layers(pass_index, mob/offsetter)
if(!SSmapping.is_planetary())
return list(
TEXT_NORTH = list(-1, 7),
TEXT_SOUTH = list(2, 10),
TEXT_EAST = list(0, 7),
TEXT_WEST = list(0, 7),
)
return list(
TEXT_NORTH = list(0, 7),
TEXT_SOUTH = list(0, 10),
TEXT_EAST = list(-3, 9),
TEXT_WEST = list(3, 9),
TEXT_NORTH = list(-1, 7),
TEXT_SOUTH = list(2, 10),
TEXT_EAST = list(0, 7),
TEXT_WEST = list(0, 7),
)
/datum/component/riding/creature/raptor/get_parent_offsets_and_layers()
@@ -705,3 +699,43 @@
/datum/component/riding/creature/raptor/combat
ai_behavior_while_ridden = RIDING_PAUSE_AI_MOVEMENT
/datum/component/riding/creature/raptor/healer/vehicle_mob_buckle(mob/living/ridden, mob/living/rider, force)
RegisterSignal(rider, COMSIG_MOB_STATCHANGE, PROC_REF(on_buckled_stat_change))
return ..()
/datum/component/riding/creature/raptor/healer/vehicle_mob_unbuckle(mob/living/formerly_ridden, mob/living/former_rider, force)
UnregisterSignal(former_rider, COMSIG_MOB_STATCHANGE)
return ..()
/datum/component/riding/creature/raptor/healer/proc/on_buckled_stat_change(mob/living/source, new_stat, old_stat)
SIGNAL_HANDLER
var/mob/living/basic/raptor/raptor = source.buckled
if (!istype(raptor)) // what
UnregisterSignal(source, COMSIG_MOB_STATCHANGE)
return
// Heal the owner and flee whatever might've attacked them
if (new_stat == CONSCIOUS || new_stat == DEAD || old_stat != CONSCIOUS || !raptor.ai_controller)
ADD_TRAIT(raptor, TRAIT_AI_PAUSED, REF(src))
return
REMOVE_TRAIT(raptor, TRAIT_AI_PAUSED, REF(src))
// Rip bozo, but you're not our friend
if (source in raptor.ai_controller.blackboard[BB_FRIENDS_LIST])
raptor.ai_controller.set_blackboard_key(BB_INJURED_RAPTOR, source)
for (var/mob/living/possible_hostile in view(5, raptor))
if (possible_hostile.stat || possible_hostile.invisibility > raptor.see_invisible || source.faction_check_atom(possible_hostile))
continue
raptor.ai_controller.set_blackboard_key(BB_BASIC_MOB_FLEE_TARGET, possible_hostile)
break
/datum/component/riding/creature/raptor/small/get_rider_offsets_and_layers(pass_index, mob/offsetter)
return list(
TEXT_NORTH = list(-1, 5),
TEXT_SOUTH = list(2, 8),
TEXT_EAST = list(0, 5),
TEXT_WEST = list(0, 5),
)
@@ -97,7 +97,10 @@
return
step(movable_parent, direction)
COOLDOWN_START(src, vehicle_move_cooldown, vehicle_move_delay)
var/move_delay = vehicle_move_delay
if(NSCOMPONENT(direction) && EWCOMPONENT(direction))
move_delay = FLOOR(move_delay * sqrt(2), world.tick_lag)
COOLDOWN_START(src, vehicle_move_cooldown, move_delay)
if(QDELETED(src))
return
+43 -15
View File
@@ -16,10 +16,12 @@
var/drinking
/// If true, we put food in our tummy instead of deleting it
var/add_to_contents
/// Types the animal can eat.
/// If true, when add_to_contents would put the item into contents but when used for healing, the item is consumed instead
var/consume_healing
/// Types the animal can eat. Can be an assoc list with amount to heal/damage the mob by
var/list/food_types
/datum/element/basic_eating/Attach(datum/target, heal_amt = 0, damage_amount = 0, damage_type = null, drinking = FALSE, add_to_contents = FALSE, food_types = list())
/datum/element/basic_eating/Attach(datum/target, heal_amt = 0, damage_amount = 0, damage_type = null, drinking = FALSE, add_to_contents = FALSE, consume_healing = TRUE, food_types = list())
. = ..()
if(!isliving(target))
@@ -31,6 +33,7 @@
src.damage_type = damage_type
src.drinking = drinking
src.add_to_contents = add_to_contents
src.consume_healing = consume_healing
src.food_types = food_types
RegisterSignal(target, COMSIG_ATOM_ITEM_INTERACTION, PROC_REF(try_feed))
@@ -66,7 +69,8 @@
/datum/element/basic_eating/proc/try_eating(mob/living/eater, atom/target, mob/living/feeder)
if(!is_type_in_list(target, food_types))
return FALSE
if(SEND_SIGNAL(eater, COMSIG_MOB_PRE_EAT, target, feeder) & COMSIG_MOB_CANCEL_EAT)
var/list/effect_mult = list()
if(SEND_SIGNAL(eater, COMSIG_MOB_PRE_EAT, target, feeder, effect_mult) & COMSIG_MOB_CANCEL_EAT)
return FALSE
if(add_to_contents && !ismovable(target))
return FALSE
@@ -76,22 +80,44 @@
else
eat_verb = pick("bite","chew","nibble","gnaw","gobble","chomp")
if (heal_amt > 0)
var/healed = heal_amt && eater.health < eater.maxHealth
eater.heal_overall_damage(heal_amt)
var/best_match = null
var/best_value = 0
for (var/food_path in food_types)
// Not an assoc list
if (isnull(food_types[food_path]))
break
if (istype(target, food_path) && (!best_match || ispath(food_path, best_match)))
best_match = food_path
best_value = food_types[food_path]
var/to_heal = heal_amt
var/to_damage = damage_amount
if (best_match)
to_heal = 0
to_damage = 0
if (best_value > 0)
to_heal = best_value
else if (best_value < 0)
to_damage = -best_value
for (var/mult in effect_mult)
to_heal *= mult
to_damage *= mult
if (to_heal > 0)
var/healed = eater.heal_overall_damage(to_heal)
eater.visible_message(span_notice("[eater] [eat_verb]s [target]."), span_notice("You [eat_verb] [target][healed ? ", restoring some health" : ""]."))
else if (damage_amount > 0 && damage_type)
eater.apply_damage(damage_amount, damage_type)
eater.visible_message(span_notice("[eater] [eat_verb]s [target], and seems to hurt itself."), span_notice("You [eat_verb] [target], hurting yourself in the process."))
else if (to_damage > 0 && damage_type)
var/damaged = eater.apply_damage(to_damage, damage_type)
eater.visible_message(span_notice("[eater] [eat_verb]s [target][damaged ? ", and seems to hurt [eater.p_themselves()]!" : "."]"), span_notice("You [eat_verb] [target][damaged ? ", hurting yourself in the process" : ""]."))
else
eater.visible_message(span_notice("[eater] [eat_verb]s [target]."), span_notice("You [eat_verb] [target]."))
finish_eating(eater, target, feeder)
finish_eating(eater, target, feeder, to_heal)
return TRUE
/datum/element/basic_eating/proc/finish_eating(mob/living/eater, atom/target, mob/living/feeder)
/datum/element/basic_eating/proc/finish_eating(mob/living/eater, atom/target, mob/living/feeder, to_heal)
set waitfor = FALSE
if(drinking)
playsound(eater.loc,'sound/items/drink.ogg', rand(10,50), TRUE)
@@ -104,10 +130,12 @@
var/obj/item/stack/food_stack = target
final_target = food_stack.split_stack(1)
eater.log_message("has eaten [target], [add_to_contents ? "swallowing it" : "destroying it"]!", LOG_ATTACK)
var/devour = add_to_contents && !to_heal
eater.log_message("has eaten [target], [devour ? "swallowing it" : "destroying it"]!", LOG_ATTACK)
if (add_to_contents)
if (devour)
var/atom/movable/movable_target = final_target
movable_target.forceMove(eater)
else
qdel(final_target)
+87
View File
@@ -0,0 +1,87 @@
/// Element which influences raptor children upon owner's consumption as food
/datum/element/raptor_food
element_flags = ELEMENT_BESPOKE
argument_hash_start_idx = 2
/// Flat damage modifier
var/attack_modifier = null
/// Flat health modifier
var/health_modifier = null
/// Speed modifier
var/speed_modifier = null
/// Primary ability stat modifier
/// Multiplier equates to 1 + this
var/ability_modifier = null
/// Growth rate modifier
/// Multiplier equates to 1 + this
var/growth_modifier = null
/// Personality traits the child may get
var/list/personality_traits = null
/// Offspring color probability modifiers
var/list/color_chances = null
/datum/element/raptor_food/Attach(obj/item/target, attack_modifier, health_modifier, speed_modifier, ability_modifier, growth_modifier, list/personality_traits, list/color_chances)
. = ..()
if (!isitem(target))
return ELEMENT_INCOMPATIBLE
src.attack_modifier = attack_modifier
src.health_modifier = health_modifier
src.speed_modifier = speed_modifier
src.ability_modifier = ability_modifier
src.growth_modifier = growth_modifier
src.personality_traits = personality_traits
src.color_chances = color_chances
RegisterSignal(target, COMSIG_ITEM_EATEN_BY_BASIC_MOB, PROC_REF(on_eaten))
RegisterSignal(target, COMSIG_ATOM_EXAMINE_MORE, PROC_REF(on_examine_more))
/datum/element/raptor_food/Detach(datum/source, ...)
UnregisterSignal(source, list(COMSIG_ITEM_EATEN_BY_BASIC_MOB, COMSIG_ATOM_EXAMINE_MORE))
return ..()
/datum/element/raptor_food/proc/on_examine_more(obj/item/source, mob/examiner, list/examine_list)
SIGNAL_HANDLER
if (!istype(examiner.buckled, /mob/living/basic/raptor))
return
// Just to check for priority
var/strognest_effect = max(attack_modifier, health_modifier, speed_modifier, ability_modifier, growth_modifier, 0)
// Only personality or color probability effects, or just pure negatives
if (strognest_effect == 0)
examine_list += span_notice("You reckon this would have unique effects on your raptor's offspring if you fed it to them...")
return
if (strognest_effect == attack_modifier)
examine_list += span_notice("You reckon this would have make your raptor's offspring stronger if you fed it to them...")
else if (strognest_effect == health_modifier)
examine_list += span_notice("You reckon this would have make your raptor's offspring tougher if you fed it to them...")
else if (strognest_effect == speed_modifier)
examine_list += span_notice("You reckon this would have make your raptor's offspring faster if you fed it to them...")
else if (strognest_effect == ability_modifier)
examine_list += span_notice("You reckon this would have make your raptor's offspring more capable if you fed it to them...")
else if (strognest_effect == growth_modifier)
examine_list += span_notice("You reckon this would have make your raptor's offspring grow faster if you fed it to them...")
/datum/element/raptor_food/proc/on_eaten(obj/item/source, mob/living/eater, mob/living/feeder)
SIGNAL_HANDLER
if (!istype(eater, /mob/living/basic/raptor))
return
var/mob/living/basic/raptor/raptor = eater
var/list/our_food = raptor.inherited_stats.foods_eaten[source.type]
if (our_food)
our_food["amount"] += 1
return
raptor.inherited_stats.foods_eaten[source.type] = list(
"amount" = 1,
"attack" = attack_modifier,
"health" = health_modifier,
"speed" = speed_modifier,
"ability" = ability_modifier,
"growth" = growth_modifier,
"traits" = personality_traits?.Copy(),
"color_chances" = color_chances?.Copy(),
)
+10 -4
View File
@@ -791,8 +791,9 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
return NONE
if(istype(over_object, /atom/movable/screen/inventory/hand))
if(real_location.loc != user || !user.can_perform_action(parent, FORBID_TELEKINESIS_REACH | ALLOW_RESTING))
if(parent.loc != user || !user.can_perform_action(parent, FORBID_TELEKINESIS_REACH | ALLOW_RESTING))
return NONE
if(isitem(parent))
var/obj/item/item_parent = parent
if(!item_parent.can_mob_unequip(user))
@@ -803,15 +804,20 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
parent.add_fingerprint(user)
return COMPONENT_CANCEL_MOUSEDROP_ONTO
if(ismob(over_object))
if(over_object != user || !user.can_perform_action(parent, FORBID_TELEKINESIS_REACH | ALLOW_RESTING))
if(over_object == user)
if(!user.can_perform_action(parent, FORBID_TELEKINESIS_REACH | ALLOW_RESTING))
return NONE
if(isliving(parent) && user.pulling == parent)
var/mob/living/as_living = parent
if(as_living.can_be_held)
return
parent.add_fingerprint(user)
INVOKE_ASYNC(src, PROC_REF(open_storage), user)
return COMPONENT_CANCEL_MOUSEDROP_ONTO
if(istype(over_object, /atom/movable/screen))
if(istype(over_object, /atom/movable/screen) || ismob(over_object))
return NONE
if(!user.can_perform_action(over_object, FORBID_TELEKINESIS_REACH))
@@ -55,11 +55,41 @@
desc = "Chance to spawn a rare shiny version."
icon = 'icons/mob/simple/lavaland/raptor_big.dmi'
icon_state = "raptor_red"
pixel_x = -12
loot = list(
/mob/living/basic/raptor/red = 20,
/mob/living/basic/raptor/white = 20,
/mob/living/basic/raptor/purple = 20,
/mob/living/basic/raptor/green = 20,
/mob/living/basic/raptor/yellow = 20,
/mob/living/basic/raptor/red = 25,
/mob/living/basic/raptor/white = 25,
/mob/living/basic/raptor/purple = 25,
/mob/living/basic/raptor/green = 25,
/mob/living/basic/raptor/yellow = 25,
/mob/living/basic/raptor/blue = 25,
/mob/living/basic/raptor/black = 1,
)
/obj/effect/spawner/random/lavaland_mob/raptor/young
name = "random raptor youngling"
icon_state = "young_red"
loot = list(
/mob/living/basic/raptor/young/red = 25,
/mob/living/basic/raptor/young/white = 25,
/mob/living/basic/raptor/young/purple = 25,
/mob/living/basic/raptor/young/green = 25,
/mob/living/basic/raptor/young/yellow = 25,
/mob/living/basic/raptor/young/blue = 25,
/mob/living/basic/raptor/young/black = 1,
)
/obj/effect/spawner/random/lavaland_mob/raptor/baby
name = "random raptor chick"
icon = 'icons/mob/simple/lavaland/raptor_baby.dmi'
icon_state = "baby_red"
pixel_x = 0
loot = list(
/mob/living/basic/raptor/baby/red = 25,
/mob/living/basic/raptor/baby/white = 25,
/mob/living/basic/raptor/baby/purple = 25,
/mob/living/basic/raptor/baby/green = 25,
/mob/living/basic/raptor/baby/yellow = 25,
/mob/living/basic/raptor/baby/blue = 25,
/mob/living/basic/raptor/baby/black = 1,
)
+12
View File
@@ -211,6 +211,10 @@
foodtypes = RAW | MEAT | BUGS
blood_decal_type = /obj/effect/decal/cleanable/insectguts
/obj/item/food/meat/slab/bugmeat/Initialize(mapload)
. = ..()
AddElement(/datum/element/raptor_food, health_modifier = 2, color_chances = string_list(list(/datum/raptor_color/green = 2)))
/obj/item/food/meat/slab/mouse
name = "mouse meat"
desc = "A slab of mouse meat. Best not eat it raw."
@@ -337,6 +341,10 @@
tastes = list("meat" = 1)
foodtypes = RAW | MEAT | TOXIC
/obj/item/food/meat/slab/goliath/Initialize(mapload)
. = ..()
AddElement(/datum/element/raptor_food, attack_modifier = 0.5, speed_modifier = -0.05, color_chances = string_list(list(/datum/raptor_color/red = 5)))
/obj/item/food/meat/slab/goliath/burn()
visible_message(span_notice("[src] finishes cooking!"))
new /obj/item/food/meat/steak/goliath(loc)
@@ -569,6 +577,10 @@
trash_type = null
tastes = list("meat" = 1, "rock" = 1)
/obj/item/food/meat/steak/goliath/Initialize(mapload)
. = ..()
AddElement(/datum/element/raptor_food, attack_modifier = 1, speed_modifier = -0.075, color_chances = string_list(list(/datum/raptor_color/red = 7)))
/obj/item/food/meat/steak/gondola
name = "gondola steak"
tastes = list("meat" = 1, "tranquility" = 1)
@@ -28,6 +28,7 @@
. = ..()
pixel_x = rand(-5, 5)
pixel_y = rand(-5, 5)
AddElement(/datum/element/raptor_food, ability_modifier = 0.05, attack_modifier = 0.5, color_chances = string_list(list(/datum/raptor_color/black = 1)))
/obj/item/stack/ore/bluespace_crystal/get_part_rating()
return 1
@@ -205,6 +205,7 @@
sound_on_success = 'sound/items/weapons/parry.ogg',\
effect_on_success = /obj/effect/temp_visual/guardian/phase,\
)
AddElement(/datum/element/raptor_food, attack_modifier = 1.5, growth_modifier = -0.075)
/obj/item/fish/lavaloop/get_fish_taste()
return list("chewy fish" = 2)
+14 -1
View File
@@ -230,6 +230,10 @@
wine_power = 50
foodtypes = FRUIT
/obj/item/food/grown/ash_flora/cactus_fruit/Initialize(mapload)
. = ..()
AddElement(/datum/element/raptor_food, growth_modifier = 0.1, ability_modifier = -0.05)
/obj/item/food/grown/ash_flora/seraka
name = "seraka cap"
desc = "Small, deeply flavourful mushrooms originally native to Tizira."
@@ -237,6 +241,10 @@
seed = /obj/item/seeds/lavaland/seraka
wine_power = 40
/obj/item/food/grown/ash_flora/seraka/Initialize(mapload)
. = ..()
AddElement(/datum/element/raptor_food, ability_modifier = 0.1)
/obj/item/food/grown/ash_flora/fireblossom
name = "fire blossom"
desc = "A flower from a fire blossom."
@@ -245,7 +253,12 @@
seed = /obj/item/seeds/lavaland/fireblossom
wine_power = 40
//SEEDS
/obj/item/food/grown/ash_flora/fireblossom/Initialize(mapload)
. = ..()
// Fire flowers make fireproof raptors
AddElement(/datum/element/raptor_food, color_chances = string_list(list(/datum/raptor_color/blue = 5)))
// SEEDS
/obj/item/seeds/lavaland
name = "lavaland seeds"
+4
View File
@@ -215,6 +215,10 @@ GLOBAL_LIST_INIT(sand_recipes, list(\
scan_state = "rock_diamond"
merge_type = /obj/item/stack/ore/diamond
/obj/item/stack/ore/diamond/Initialize(mapload)
. = ..()
AddElement(/datum/element/raptor_food, speed_modifier = 0.05, health_modifier = -1, color_chances = string_list(list(/datum/raptor_color/yellow = 3)))
/obj/item/stack/ore/diamond/five
amount = 5
@@ -48,6 +48,7 @@
lighting_cutoff_blue = 25
can_be_held = TRUE
worn_slot_flags = ITEM_SLOT_HEAD
inhand_holder_type = /obj/item/mob_holder/drone
/// `TRUE` if we have picked our visual appearance, `FALSE` otherwise (default)
var/picked = FALSE
/// Stored drone color, restored when unhacked
@@ -80,6 +80,7 @@
if(beegent)
reee[beegent.type] = 5
holder.AddComponentFrom(SOURCE_EDIBLE_INNATE, /datum/component/edible, reee, null, BEE_FOODGROUPS, 10, 0, list("bee"), null, 10)
SEND_SIGNAL(src, COMSIG_LIVING_SCOOPED_UP, picker, holder)
picker.visible_message(span_warning("[picker] scoops up [src]!"))
picker.put_in_hands(holder)
@@ -1,12 +1,3 @@
GLOBAL_LIST_INIT(raptor_growth_paths, list(
/mob/living/basic/raptor/baby_raptor/red = list(RAPTOR_PURPLE, RAPTOR_WHITE),
/mob/living/basic/raptor/baby_raptor/white = list(RAPTOR_GREEN, RAPTOR_PURPLE),
/mob/living/basic/raptor/baby_raptor/purple = list(RAPTOR_GREEN, RAPTOR_WHITE),
/mob/living/basic/raptor/baby_raptor/yellow = list(RAPTOR_GREEN, RAPTOR_RED),
/mob/living/basic/raptor/baby_raptor/green = list(RAPTOR_RED, RAPTOR_YELLOW),
/mob/living/basic/raptor/baby_raptor/blue = list(RAPTOR_RED, RAPTOR_PURPLE)
))
GLOBAL_LIST_INIT(raptor_inherit_traits, list(
BB_BASIC_DEPRESSED = "Depressed",
BB_RAPTOR_MOTHERLY = "Motherly",
@@ -16,23 +7,25 @@ GLOBAL_LIST_INIT(raptor_inherit_traits, list(
GLOBAL_LIST_EMPTY(raptor_population)
#define HAPPINESS_BOOST_DAMPENER 0.3
/// Innate raptor offsets
#define RAPTOR_INNATE_SOURCE "raptor_innate"
/mob/living/basic/raptor
name = "raptor"
desc = "A trusty, powerful steed. Taming it might prove difficult..."
icon = 'icons/mob/simple/lavaland/raptor_big.dmi'
speed = 2
icon_state = "raptor_red"
base_icon_state = "raptor"
pixel_w = -12
base_pixel_w = -12
speed = 0.5
mob_biotypes = MOB_ORGANIC|MOB_BEAST
maxHealth = 270
health = 270
maxHealth = 200
health = 200
melee_damage_lower = 10
melee_damage_upper = 15
combat_mode = TRUE
mob_size = MOB_SIZE_LARGE
head_icon = 'icons/mob/clothing/back/pets_back.dmi'
worn_slot_flags = ITEM_SLOT_BACK
held_w_class = WEIGHT_CLASS_BULKY
unsuitable_atmos_damage = 0
minimum_survivable_temperature = BODYTEMP_COLD_ICEBOX_SAFE
maximum_survivable_temperature = INFINITY
@@ -45,14 +38,13 @@ GLOBAL_LIST_EMPTY(raptor_population)
/obj/item/food/meat/slab/chicken = 4,
/obj/item/stack/sheet/bone = 2,
)
ai_controller = /datum/ai_controller/basic_controller/raptor
///can this mob breed
// AI controller is set by our color
ai_controller = null
/// Can this raptor breed?
var/can_breed = TRUE
///should we change offsets on direction change?
/// Should we change offsets on direction change?
var/change_offsets = TRUE
///can we ride this mob
var/ridable_component = /datum/component/riding/creature/raptor
//pet commands when we tame the raptor
/// Pet commands when we tame the raptor
var/static/list/pet_commands = list(
/datum/pet_command/breed,
/datum/pet_command/idle,
@@ -62,24 +54,47 @@ GLOBAL_LIST_EMPTY(raptor_population)
/datum/pet_command/follow,
/datum/pet_command/fetch,
)
///things we inherited from our parent
var/datum/raptor_inheritance/inherited_stats
///our color
var/raptor_color
///the description that appears in the dex
var/dex_description
///path of our child
var/child_path
/// Can we wear a collar? If so, what is our icon state prefix for it?
var/collar_state = "raptor"
/// Raptor color datum assigned to this raptor, this is a singleton
var/datum/raptor_color/raptor_color = null
/// Are we an adult, youngling or baby?
var/growth_stage = RAPTOR_ADULT
/// Our current growth progress towards the next stage if we're a youngling or a baby
var/growth_progress = 0
/// Probability of getting progress in the baby phase each second
var/growth_probability = 80
/// Food types that we can consume
var/static/list/food_types = list(
/obj/item/stack/ore = 0,
/obj/item/food/meat = 15,
/obj/item/food/meat/slab = 25,
/obj/item/food/meat/slab/spider = -15, // Toxic meats
/obj/item/food/meat/slab/xeno = -15,
/obj/item/food/meat/steak = 50,
/obj/item/food/grown/ash_flora = 10,
/obj/item/fish = 15,
/obj/item/organ = 25,
)
/// Inheritance datum we store our genetic data in
var/datum/raptor_inheritance/inherited_stats = null
/// Current happiness value of the raptor
var/happiness_percentage = 0
/mob/living/basic/raptor/Initialize(mapload)
/mob/living/basic/raptor/Initialize(mapload, datum/raptor_color/color_type, datum/raptor_inheritance/passed_stats)
. = ..()
if(SSmapping.is_planetary())
change_offsets = FALSE
icon = 'icons/mob/simple/lavaland/raptor_icebox.dmi'
inherited_stats = passed_stats || new(src)
// First thing as to go before tameable in change_growth_stage()
AddElement(/datum/element/basic_eating, food_types = food_types)
raptor_color = GLOB.raptor_colors[color_type || raptor_color || pick(GLOB.raptor_colors)]
raptor_color.setup_raptor(src)
AddElement(/datum/element/wears_collar)
add_traits(list(TRAIT_LAVA_IMMUNE, TRAIT_ASHSTORM_IMMUNE, TRAIT_SNOWSTORM_IMMUNE), INNATE_TRAIT)
if (growth_stage == RAPTOR_ADULT)
raptor_color.setup_adult(src)
else
change_growth_stage(growth_stage, RAPTOR_ADULT)
add_traits(list(TRAIT_ASHSTORM_IMMUNE, TRAIT_SNOWSTORM_IMMUNE, TRAIT_MINING_AOE_IMMUNE), INNATE_TRAIT)
AddElement(\
/datum/element/crusher_loot,\
trophy_type = /obj/item/crusher_trophy/raptor_feather,\
@@ -87,15 +102,364 @@ GLOBAL_LIST_EMPTY(raptor_population)
drop_immediately = FALSE,\
)
if(!mapload)
if (!mapload)
GLOB.raptor_population += REF(src)
AddComponent(/datum/component/obeys_commands, pet_commands)
AddComponent(/datum/component/obeys_commands, pet_commands, list(0, -base_pixel_w))
AddElement(\
/datum/element/change_force_on_death,\
move_resist = MOVE_RESIST_DEFAULT,\
)
RegisterSignal(src, COMSIG_MOB_ATE, PROC_REF(on_eat))
RegisterSignal(src, COMSIG_MOB_PRE_EAT, PROC_REF(on_pre_eat))
AddElement(/datum/element/ai_retaliate)
AddElement(/datum/element/ai_flee_while_injured, stop_fleeing_at = 0.5, start_fleeing_below = 0.2)
if (can_breed)
add_breeding_component()
// Babies handle it in their change_growth_stage
if (growth_stage != RAPTOR_BABY)
update_blackboard()
AddElement(/datum/element/footstep, footstep_type = FOOTSTEP_MOB_CLAW)
RegisterSignal(src, COMSIG_ATOM_DIR_CHANGE, PROC_REF(on_dir_change))
RegisterSignal(src, COMSIG_LIVING_SCOOPED_UP, PROC_REF(on_picked_up))
adjust_offsets(dir)
add_happiness_component()
/mob/living/basic/raptor/Destroy()
raptor_color = null
GLOB.raptor_population -= REF(src)
return ..()
/mob/living/basic/raptor/death(gibbed)
. = ..()
GLOB.raptor_population -= REF(src)
/mob/living/basic/raptor/buckle_mob(mob/living/target, force = FALSE, check_loc = TRUE, buckle_mob_flags= NONE)
if(!iscarbon(target))
return
return ..()
/mob/living/basic/raptor/proc/on_dir_change(datum/source, old_dir, new_dir)
SIGNAL_HANDLER
adjust_offsets(new_dir)
/mob/living/basic/raptor/proc/adjust_offsets(direction)
if (!change_offsets)
return
switch (direction)
if (NORTH, SOUTH)
add_offsets(RAPTOR_INNATE_SOURCE, w_add = 0, animate = FALSE)
if (EAST, SOUTHEAST, NORTHEAST)
add_offsets(RAPTOR_INNATE_SOURCE, w_add = -8, animate = FALSE)
if (WEST, SOUTHWEST, NORTHWEST)
add_offsets(RAPTOR_INNATE_SOURCE, w_add = 8, animate = FALSE)
/mob/living/basic/raptor/examine(mob/user)
. = ..()
if (stat == DEAD)
return
switch (health / maxHealth)
if (0 to 0.2)
. += span_italics(span_bolddanger("[p_They()] [p_are()] gruesomly wounded, barely staying up on [p_their()] feet!"))
if (0.2 to 0.4)
. += span_danger("[p_They()] [p_have()] heavy injuries and open wounds all around [p_their()] body!")
if (0.4 to 0.6)
. += span_warning("[p_They()] [p_are()] noticeably hurt, limping from [p_their()] cuts and bruises.")
if (0.6 to 0.8)
. += span_warning("[p_They()] [p_are()] visibly injured, a few bruises and cuts showing between [p_their()] feathers.")
if (0.8 to 0.999)
. += span_notice("[p_They()] [p_have()] a few minor bruises and scratches.")
/mob/living/basic/raptor/Life(seconds_per_tick, times_fired)
. = ..()
if (growth_stage != RAPTOR_BABY || HAS_TRAIT(src, TRAIT_STASIS) || stat == DEAD)
return
if (!SPT_PROB(growth_probability * (1 + happiness_percentage * RAPTOR_GROWTH_HAPPINESS_MULTIPLIER), seconds_per_tick))
return
growth_progress += rand(RAPTOR_BABY_GROWTH_LOWER, RAPTOR_BABY_GROWTH_UPPER)
if (growth_progress >= RAPTOR_GROWTH_REQUIRED)
change_growth_stage(RAPTOR_YOUNG)
growth_progress = 0
/mob/living/basic/raptor/early_melee_attack(atom/target, list/modifiers, ignore_cooldown)
. = ..()
if(!.)
return FALSE
if(!istype(target, /obj/structure/ore_container/food_trough/raptor_trough))
return TRUE
var/obj/ore_food = locate(/obj/item/stack/ore) in target
if(isnull(ore_food))
balloon_alert(src, "no food!")
else
UnarmedAttack(ore_food, TRUE, modifiers)
return FALSE
/mob/living/basic/raptor/melee_attack(mob/living/target, list/modifiers, ignore_cooldown)
if (!combat_mode && istype(target, /mob/living/basic/raptor))
var/mob/living/basic/raptor/possible_baby = target
if (possible_baby.growth_stage == RAPTOR_BABY)
return target.attack_hand(src, list(LEFT_CLICK = TRUE))
return ..()
/mob/living/basic/raptor/proc/add_breeding_component()
var/static/list/partner_types = typecacheof(list(/mob/living/basic/raptor))
var/static/list/baby_types = list(/obj/item/food/egg = 1) ///raptor_egg = 1)
AddComponent(\
/datum/component/breed, \
can_breed_with = partner_types, \
baby_paths = baby_types, \
breed_timer = 3 MINUTES, \
post_birth = CALLBACK(src, PROC_REF(egg_inherit)), \
)
/mob/living/basic/raptor/proc/add_happiness_component()
var/static/list/percentage_callbacks = list(0, 15, 25, 35, 50, 75, 90, 100)
// Higher happiness cap so it decays slower, about 15 minutes from full to zero
AddComponent(\
/datum/component/happiness, \
maximum_happiness = 900, \
on_petted_change = 50, \
on_groom_change = 200, \
on_eat_change = 150, \
callback_percentages = percentage_callbacks,\
happiness_callback = CALLBACK(src, PROC_REF(happiness_change)),\
)
/mob/living/basic/raptor/proc/happiness_change(percent_value)
var/attack_boost = round((percent_value - happiness_percentage) * RAPTOR_HAPPINESS_DAMAGE_BOOST, 1)
melee_damage_lower += attack_boost
melee_damage_upper += attack_boost
happiness_percentage = percent_value
/mob/living/basic/raptor/projectile_hit(obj/projectile/hitting_projectile, def_zone, piercing_hit, blocked)
// Most colors will redirect shots to their rider as to increase their own survivability, and only tank melee attacks
if (raptor_color.redirect_shots && length(buckled_mobs))
return buckled_mobs[1].projectile_hit(hitting_projectile, def_zone, piercing_hit, blocked)
return ..()
/// Pass our genetic data to the egg
/mob/living/basic/raptor/proc/egg_inherit(obj/item/food/egg/raptor_egg/baby_egg, mob/living/basic/raptor/partner)
var/datum/raptor_inheritance/child_genes = new()
child_genes.set_parents(src, partner)
baby_egg.inherited_stats = child_genes
baby_egg.child_color = get_child_color(partner)
// Halve our food modifiers every time we breed
for (var/food_type in inherited_stats.foods_eaten)
var/list/stat_mods = inherited_stats.foods_eaten[food_type]
stat_mods["amount"] /= 2
stat_mods["attack"] /= 2
stat_mods["health"] /= 2
stat_mods["speed"] /= 2
stat_mods["ability"] /= 2
stat_mods["growth"] /= 2
var/list/trait_list = stat_mods["traits"]
for (var/i in 1 to ceil(length(trait_list) / 2))
trait_list -= pick(trait_list)
var/list/color_chances = stat_mods["color_chances"]
for (var/datum/raptor_color/color_type as anything in color_chances)
color_chances[color_type] = floor(color_chances[color_type] / 2)
if (!color_chances[color_type])
color_chances -= color_type
/mob/living/basic/raptor/proc/get_child_color(mob/living/basic/raptor/partner)
if (raptor_color == partner.raptor_color)
return raptor_color.type
if (raptor_color.guaranteed_crossbreeds[partner.raptor_color.type])
return raptor_color.guaranteed_crossbreeds[partner.raptor_color.type]
// This should be redundant as they should be mirroring eachother, but just in case
if (partner.raptor_color.guaranteed_crossbreeds[raptor_color.type])
return partner.raptor_color.guaranteed_crossbreeds[raptor_color.type]
// We've got all the colors in our family tree and aren't rolling a guarantee, bingo
if (length(inherited_stats.parent_colors | partner.inherited_stats.parent_colors | raptor_color.type | partner.raptor_color.type) == length(GLOB.raptor_colors))
return /datum/raptor_color/black
var/list/prob_list = list()
for (var/datum/raptor_color/color_type as anything in GLOB.raptor_colors)
prob_list[color_type] = color_type::spawn_chance
var/amount_eaten = 0
for (var/food_type in inherited_stats.foods_eaten)
var/list/stat_mods = inherited_stats.foods_eaten[food_type]
amount_eaten += stat_mods["amount"]
for (var/food_type in inherited_stats.foods_eaten)
var/list/stat_mods = inherited_stats.foods_eaten[food_type]
var/list/color_chances = stat_mods["color_chances"]
for (var/datum/raptor_color/color_type as anything in color_chances)
prob_list[color_type] += floor(color_chances[color_type] * stat_mods["amount"])
return pick_weight(prob_list)
/mob/living/basic/raptor/proc/on_picked_up(mob/living/basic/raptor/source, mob/living/user, obj/item/mob_holder/holder)
SIGNAL_HANDLER
// Our inventory code sucks so we have to do this
holder.icon = 'icons/mob/simple/lavaland/raptor_baby.dmi'
holder.icon_state = icon_state
holder.alternate_worn_layer = HEAD_LAYER
holder.pixel_w = 0
holder.pixel_z = 0
holder.base_pixel_w = 0
holder.base_pixel_z = 0
/mob/living/basic/raptor/proc/on_pre_eat(datum/source, obj/item/potential_food, list/effect_mult)
SIGNAL_HANDLER
if (isorgan(potential_food))
var/obj/item/organ/guts = potential_food
if (!(guts.organ_flags & ORGAN_EDIBLE) || !(guts.organ_flags & ORGAN_ORGANIC))
return COMSIG_MOB_CANCEL_EAT
if (happiness_percentage)
effect_mult += happiness_percentage * RAPTOR_GROWTH_HAPPINESS_MULTIPLIER
/mob/living/basic/raptor/proc/on_eat(datum/source, atom/food, mob/living/feeder)
SIGNAL_HANDLER
if (!istype(food, /obj/item/food))
return
var/obj/item/food/meal = food
var/is_flora = istype(meal, /obj/item/food/grown/ash_flora)
var/is_meat = (meal.foodtypes & (MEAT|GORE))
// Babies cannot gain growth from eating meat, only plants, but they get some passively
if ((!is_meat || growth_stage == RAPTOR_BABY) && !is_flora)
return
if (growth_stage == RAPTOR_ADULT)
return
// Better meals make your raptor grow faster
var/growth_value = meal.crafting_complexity * RAPTOR_MEAL_COMPLEXITY_GROWTH_FACTOR + (is_flora ? RAPTOR_GROWTH_BASE_PLANT : RAPTOR_GROWTH_BASE_MEAT)
growth_progress += growth_value * (1 + inherited_stats.growth_modifier) * (1 + happiness_percentage * RAPTOR_GROWTH_HAPPINESS_MULTIPLIER)
if (growth_progress >= RAPTOR_GROWTH_REQUIRED)
change_growth_stage(growth_stage == RAPTOR_BABY ? RAPTOR_YOUNG : RAPTOR_ADULT)
growth_progress = 0
/// Changes the raptor to a new growth stage. Only should be done forwards, or on raptor init as the first thing before everything else
/// Sorry for the monolith, but splitting it up results in even worse looking code with a ton of duplicate calls and assignments
/// And making a *second* datum is just insanity
/mob/living/basic/raptor/proc/change_growth_stage(new_stage, prev_stage = growth_stage)
if (new_stage == prev_stage)
return FALSE
if (SEND_SIGNAL(src, COMSIG_RAPTOR_GROWTH_STAGE_CHANGE, new_stage, prev_stage) & COMPONENT_CANCEL_RAPTOR_GROWTH)
return FALSE
growth_stage = new_stage
// Visuals
switch (new_stage)
if (RAPTOR_BABY)
name = "baby raptor"
desc = "Will this grow into something useful?"
icon = 'icons/mob/simple/lavaland/raptor_baby.dmi'
base_icon_state = "baby"
base_pixel_w = 0
mob_size = MOB_SIZE_TINY
if (RAPTOR_YOUNG)
name = "raptor youngling"
desc = "A young raptor that can grow into a robust, trusty steed. Rather naive at such an age, it shouldn't be too hard to tame."
icon = 'icons/mob/simple/lavaland/raptor_big.dmi'
base_icon_state = "young"
base_pixel_w = initial(base_pixel_w)
mob_size = MOB_SIZE_HUMAN
if (RAPTOR_ADULT)
name = "raptor"
desc = initial(desc)
icon = 'icons/mob/simple/lavaland/raptor_big.dmi'
base_icon_state = "raptor"
base_pixel_w = initial(base_pixel_w)
mob_size = initial(mob_size)
can_be_held = initial(density)
density = initial(density)
move_resist = initial(move_resist)
can_breed = initial(can_breed)
change_offsets = initial(change_offsets)
if (new_stage == RAPTOR_ADULT)
// Adults need to be tamed with skill rather than snacks
qdel(GetComponent(/datum/component/tameable))
else // Make us teeny-tiny
can_be_held = TRUE
density = FALSE
can_breed = FALSE
move_resist = MOVE_RESIST_DEFAULT
change_offsets = FALSE
if (prev_stage == RAPTOR_ADULT)
AddComponent(/datum/component/tameable, food_types = food_types, tame_chance = 25, bonus_tame_chance = 15, unique = TRUE)
if (change_offsets)
adjust_offsets(dir)
else
remove_offsets(RAPTOR_INNATE_SOURCE, FALSE)
if (can_breed)
add_breeding_component()
else
qdel(GetComponent(/datum/component/breed))
var/obj/item/mob_holder/holder = null
if (istype(loc, /obj/item/mob_holder))
holder = loc
if (!can_be_held)
holder.release()
holder = null
if (collar_state)
RemoveElement(/datum/element/wears_collar, collar_icon = 'icons/mob/simple/lavaland/raptor_big.dmi', collar_icon_state = "[collar_state]_")
if (new_stage == RAPTOR_BABY)
collar_state = null
var/list/friends = ai_controller?.blackboard[BB_FRIENDS_LIST]
if (friends)
friends = friends.Copy()
QDEL_NULL(ai_controller)
ai_controller = new /datum/ai_controller/basic_controller/baby_raptor(src)
for (var/old_friend in friends)
ai_controller.insert_blackboard_key_lazylist(BB_FRIENDS_LIST, old_friend)
update_blackboard()
held_w_class = WEIGHT_CLASS_SMALL
worn_slot_flags = NONE
holder?.update_weight_class(held_w_class)
else
collar_state = base_icon_state
AddElement(/datum/element/wears_collar, collar_icon = 'icons/mob/simple/lavaland/raptor_big.dmi', collar_icon_state = "[collar_state]_")
if (prev_stage == RAPTOR_BABY)
var/list/friends = ai_controller?.blackboard[BB_FRIENDS_LIST]
if (friends)
friends = friends.Copy()
QDEL_NULL(ai_controller)
ai_controller = new raptor_color.ai_controller(src)
for (var/old_friend in friends)
ai_controller.insert_blackboard_key_lazylist(BB_FRIENDS_LIST, old_friend)
update_blackboard()
held_w_class = WEIGHT_CLASS_BULKY
worn_slot_flags = ITEM_SLOT_BACK
holder?.update_weight_class(held_w_class)
// And finish the setup on our color's side
switch (new_stage)
if (RAPTOR_BABY)
raptor_color.setup_baby(src)
if (RAPTOR_YOUNG)
raptor_color.setup_young(src)
if (RAPTOR_ADULT)
raptor_color.setup_adult(src)
return TRUE
/mob/living/basic/raptor/proc/update_blackboard()
var/static/list/display_emote = list(
BB_EMOTE_SAY = list("Chirp chirp chirp!", "Kweh!", "Bwark!"),
BB_EMOTE_SEE = list("shakes its feathers!", "stretches!", "flaps its wings!", "pecks at the ground!"),
@@ -110,235 +474,110 @@ GLOBAL_LIST_EMPTY(raptor_population)
)
ai_controller.set_blackboard_key(BB_BASIC_MOB_SPEAK_LINES, display_emote)
inherited_stats = new
inherit_properties()
var/list/my_food = string_list(list(/obj/item/stack/ore))
AddElement(/datum/element/basic_eating, food_types = my_food)
AddElement(/datum/element/ai_retaliate)
AddElement(/datum/element/ai_flee_while_injured, stop_fleeing_at = 0.5, start_fleeing_below = 0.2)
if(ridable_component)
AddElement(/datum/element/ridable, ridable_component)
var/static/list/preferred_foods = typecacheof(list(
/obj/item/food/meat,
/obj/item/food/grown/ash_flora,
)) - typecacheof(list( // Don't seek out toxic foods
/obj/item/food/meat/slab/spider,
/obj/item/food/meat/slab/xeno,
))
if(can_breed)
add_breeding_component()
ai_controller.set_blackboard_key(BB_BASIC_FOODS, preferred_foods)
AddElement(/datum/element/footstep, footstep_type = FOOTSTEP_MOB_CLAW)
RegisterSignal(src, COMSIG_ATOM_DIR_CHANGE, PROC_REF(on_dir_change))
adjust_offsets(dir)
add_happiness_component()
/mob/living/basic/raptor/buckle_mob(mob/living/target, force = FALSE, check_loc = TRUE, buckle_mob_flags= NONE)
if(!iscarbon(target))
return
return ..()
/mob/living/basic/raptor/proc/add_happiness_component()
var/static/list/percentage_callbacks = list(0, 15, 25, 35, 50, 75, 90, 100)
AddComponent(\
/datum/component/happiness,\
on_petted_change = 100,\
on_groom_change = 100,\
on_eat_change = 400,\
callback_percentages = percentage_callbacks,\
happiness_callback = CALLBACK(src, PROC_REF(happiness_change)),\
)
/mob/living/basic/raptor/proc/on_dir_change(datum/source, old_dir, new_dir)
SIGNAL_HANDLER
adjust_offsets(new_dir)
/mob/living/basic/raptor/proc/adjust_offsets(direction)
if (!change_offsets)
return
switch (direction)
if (NORTH)
add_offsets(RAPTOR_INNATE_SOURCE, w_add = -8, animate = FALSE)
if (SOUTH)
add_offsets(RAPTOR_INNATE_SOURCE, w_add = 0, animate = FALSE)
if (EAST, SOUTHEAST, NORTHEAST)
add_offsets(RAPTOR_INNATE_SOURCE, w_add = -20, animate = FALSE)
if (WEST, SOUTHWEST, NORTHWEST)
add_offsets(RAPTOR_INNATE_SOURCE, w_add = -5, animate = FALSE)
/mob/living/basic/raptor/early_melee_attack(atom/target, list/modifiers, ignore_cooldown)
. = ..()
if(!.)
return FALSE
if(!istype(target, /obj/structure/ore_container/food_trough/raptor_trough))
return TRUE
var/obj/ore_food = locate(/obj/item/stack/ore) in target
if(isnull(ore_food))
balloon_alert(src, "no food!")
else
UnarmedAttack(ore_food, TRUE, modifiers)
return FALSE
/mob/living/basic/raptor/melee_attack(mob/living/target, list/modifiers, ignore_cooldown)
if(!combat_mode && istype(target, /mob/living/basic/raptor/baby_raptor))
target.attack_hand(src, list(LEFT_CLICK = TRUE))
return
return ..()
/mob/living/basic/raptor/death(gibbed)
. = ..()
GLOB.raptor_population -= REF(src)
/mob/living/basic/raptor/proc/happiness_change(percent_value)
var/attack_boost = round(initial(melee_damage_lower) * percent_value * HAPPINESS_BOOST_DAMPENER, 1)
melee_damage_lower = initial(melee_damage_lower) + attack_boost
melee_damage_upper = melee_damage_lower + 5
///pass down our inheritance to the egg
/mob/living/basic/raptor/proc/egg_inherit(obj/item/food/egg/raptor_egg/baby_egg, mob/living/basic/raptor/partner)
var/datum/raptor_inheritance/inherit = new
inherit.set_parents(inherited_stats, partner.inherited_stats)
baby_egg.inherited_stats = inherit
baby_egg.determine_growth_path(src, partner)
/mob/living/basic/raptor/proc/inherit_properties()
if(isnull(inherited_stats))
return
for(var/trait in GLOB.raptor_inherit_traits) // done this way to allow overriding of traits when assigned new inherit datum
var/should_inherit = (trait in inherited_stats.inherit_traits)
for(var/trait in GLOB.raptor_inherit_traits)
var/should_inherit = (trait in inherited_stats.personality_traits)
ai_controller?.set_blackboard_key(trait, should_inherit)
melee_damage_lower += inherited_stats.attack_modifier
melee_damage_upper += melee_damage_lower + 5
maxHealth += inherited_stats.health_modifier
heal_overall_damage(maxHealth)
/mob/living/basic/raptor/proc/add_breeding_component()
var/static/list/partner_types = typecacheof(list(/mob/living/basic/raptor))
var/static/list/baby_types = list(/obj/item/food/egg/raptor_egg = 1)
AddComponent(\
/datum/component/breed,\
can_breed_with = typecacheof(list(/mob/living/basic/raptor)),\
baby_paths = baby_types,\
post_birth = CALLBACK(src, PROC_REF(egg_inherit)),\
breed_timer = 3 MINUTES,\
)
/mob/living/basic/raptor/Destroy()
QDEL_NULL(inherited_stats)
return ..()
// Raptor types for mappers to use
/mob/living/basic/raptor/red
name = "red raptor"
icon_state = "raptor_red"
icon_living = "raptor_red"
icon_dead = "raptor_red_dead"
melee_damage_lower = 15
melee_damage_upper = 20
raptor_color = RAPTOR_RED
ridable_component = /datum/component/riding/creature/raptor/combat
dex_description = "A resilient breed of raptors, battle-tested and bred for the purpose of humbling its foes in combat, \
This breed demonstrates higher combat capabilities than its peers and oozes ruthless aggression."
child_path = /mob/living/basic/raptor/baby_raptor/red
raptor_color = /datum/raptor_color/red
/mob/living/basic/raptor/purple
name = "purple raptor"
icon_state = "raptor_purple"
icon_living = "raptor_purple"
icon_dead = "raptor_purple_dead"
raptor_color = RAPTOR_PURPLE
dex_description = "A dependable mount, bred for the purpose of long distance pilgrimages. This breed is also able to store its rider's possessions."
child_path = /mob/living/basic/raptor/baby_raptor/purple
/mob/living/basic/raptor/purple/Initialize(mapload)
. = ..()
create_storage(
max_specific_storage = WEIGHT_CLASS_NORMAL,
max_total_storage = 10,
storage_type = /datum/storage/raptor_storage,
)
raptor_color = /datum/raptor_color/purple
/mob/living/basic/raptor/green
name = "green raptor"
icon_state = "raptor_green"
icon_living = "raptor_green"
icon_dead = "raptor_green_dead"
maxHealth = 400
health = 400
raptor_color = RAPTOR_GREEN
dex_description = "A tough breed of raptor, made to withstand the harshest of punishment and to laugh in the face of pain, \
this breed is able to withstand more punishment than its peers."
child_path = /mob/living/basic/raptor/baby_raptor/green
/mob/living/basic/raptor/green/Initialize(mapload)
. = ..()
AddComponent(/datum/component/proficient_miner)
raptor_color = /datum/raptor_color/green
/mob/living/basic/raptor/white
name = "white raptor"
icon_state = "raptor_white"
icon_living = "raptor_white"
icon_dead = "raptor_white_dead"
raptor_color = RAPTOR_WHITE
dex_description = "A loving sort, it cares for it peers and rushes to their aid with reckless abandon. It is able to heal any raptors' ailments."
child_path = /mob/living/basic/raptor/baby_raptor/white
/mob/living/basic/raptor/white/Initialize(mapload)
. = ..()
AddComponent(\
/datum/component/healing_touch,\
heal_brute = melee_damage_upper,\
heal_burn = melee_damage_upper,\
heal_time = 0,\
valid_targets_typecache = typecacheof(list(/mob/living/basic/raptor)),\
)
raptor_color = /datum/raptor_color/white
/mob/living/basic/raptor/black
name = "black raptor"
icon_state = "raptor_black"
icon_living = "raptor_black"
icon_dead = "raptor_black_dead"
maxHealth = 400
health = 400
speed = 1.5
melee_damage_lower = 20
melee_damage_upper = 25
raptor_color = RAPTOR_BLACK
dex_description = "An ultra rare breed. Due to its sparse nature, not much is known about this sort. However it is said to possess many of its peers' abilities."
child_path = /mob/living/basic/raptor/baby_raptor/black
raptor_color = /datum/raptor_color/black
/mob/living/basic/raptor/yellow
name = "yellow raptor"
icon_state = "raptor_yellow"
icon_living = "raptor_yellow"
icon_dead = "raptor_yellow_dead"
speed = 1.5
raptor_color = RAPTOR_YELLOW
dex_description = "This breed possesses greasy fast speed, DEMON speed, making light work of long pilgrimages. It's said that a thunderclap could be heard when this breed reaches its maximum speed."
child_path = /mob/living/basic/raptor/baby_raptor/yellow
raptor_color = /datum/raptor_color/yellow
/mob/living/basic/raptor/blue
name = "blue raptor"
icon_state = "raptor_blue"
icon_living = "raptor_blue"
icon_dead = "raptor_blue_dead"
raptor_color = RAPTOR_BLUE
dex_description = "Known to produce nutritous and equally delicious milk, which is also said to possess healing properties."
child_path = /mob/living/basic/raptor/baby_raptor/blue
raptor_color = /datum/raptor_color/blue
/mob/living/basic/raptor/blue/Initialize(mapload)
. = ..()
AddComponent(\
/datum/component/udder,\
udder_type = /obj/item/udder/raptor,\
)
/mob/living/basic/raptor/young
growth_stage = RAPTOR_YOUNG
/datum/storage/raptor_storage
animated = FALSE
insert_on_attack = FALSE
/mob/living/basic/raptor/young/red
icon_state = "young_red"
raptor_color = /datum/raptor_color/red
/datum/storage/raptor_storage/on_mousedropped_onto(datum/source, obj/item/dropping, mob/user)
..()
return NONE
/mob/living/basic/raptor/young/purple
icon_state = "young_purple"
raptor_color = /datum/raptor_color/purple
#undef HAPPINESS_BOOST_DAMPENER
#undef RAPTOR_INNATE_SOURCE
/mob/living/basic/raptor/young/green
icon_state = "young_green"
raptor_color = /datum/raptor_color/green
/mob/living/basic/raptor/young/white
icon_state = "young_white"
raptor_color = /datum/raptor_color/white
/mob/living/basic/raptor/young/black
icon_state = "young_black"
raptor_color = /datum/raptor_color/black
/mob/living/basic/raptor/young/yellow
icon_state = "young_yellow"
raptor_color = /datum/raptor_color/yellow
/mob/living/basic/raptor/young/blue
icon_state = "young_blue"
raptor_color = /datum/raptor_color/blue
/mob/living/basic/raptor/baby
icon = 'icons/mob/simple/lavaland/raptor_baby.dmi'
growth_stage = RAPTOR_BABY
/mob/living/basic/raptor/baby/red
icon_state = "baby_red"
raptor_color = /datum/raptor_color/red
/mob/living/basic/raptor/baby/purple
icon_state = "baby_purple"
raptor_color = /datum/raptor_color/purple
/mob/living/basic/raptor/baby/green
icon_state = "baby_green"
raptor_color = /datum/raptor_color/green
/mob/living/basic/raptor/baby/white
icon_state = "baby_white"
raptor_color = /datum/raptor_color/white
/mob/living/basic/raptor/baby/black
icon_state = "baby_black"
raptor_color = /datum/raptor_color/black
/mob/living/basic/raptor/baby/yellow
icon_state = "baby_yellow"
raptor_color = /datum/raptor_color/yellow
/mob/living/basic/raptor/baby/blue
icon_state = "baby_blue"
raptor_color = /datum/raptor_color/blue
@@ -1,102 +0,0 @@
/mob/living/basic/raptor/baby_raptor
name = "baby raptor"
desc = "Will this grow into something useful?"
icon = 'icons/mob/simple/lavaland/raptor_baby.dmi'
speed = 5
maxHealth = 25
health = 25
melee_damage_lower = 3
melee_damage_upper = 5
density = FALSE
can_breed = FALSE
move_resist = MOVE_RESIST_DEFAULT
ai_controller = /datum/ai_controller/basic_controller/baby_raptor
ridable_component = null
change_offsets = FALSE
dex_description = "A cute baby raptor, Having it near a parent or a birds-nest could encourage it to grow faster, \
grooming it and feeding it could also ensure that it grows up quicker!"
///what do we grow into
var/growth_path
///probability we are to be rolled
var/roll_rate = 100
/mob/living/basic/raptor/baby_raptor/Initialize(mapload)
. = ..()
if(isnull(growth_path))
return
AddComponent(\
/datum/component/growth_and_differentiation,\
growth_time = null,\
growth_path = growth_path,\
growth_probability = 80,\
lower_growth_value = 0.5,\
upper_growth_value = 0.8,\
signals_to_kill_on = list(COMSIG_MOB_CLIENT_LOGIN),\
optional_checks = CALLBACK(src, PROC_REF(check_grow)),\
optional_grow_behavior = CALLBACK(src, PROC_REF(ready_to_grow)),\
)
/mob/living/basic/raptor/baby_raptor/add_happiness_component()
AddComponent(/datum/component/happiness, on_petted_change = 100)
/mob/living/basic/raptor/baby_raptor/proc/check_grow()
return (stat != DEAD)
/mob/living/basic/raptor/baby_raptor/proc/ready_to_grow()
var/mob/living/basic/raptor/grown_mob = new growth_path(get_turf(src))
QDEL_NULL(grown_mob.inherited_stats)
grown_mob.inherited_stats = inherited_stats
inherited_stats = null
grown_mob.inherit_properties()
ADD_TRAIT(grown_mob, TRAIT_MOB_HATCHED, INNATE_TRAIT) //pass on the hatched trait
qdel(src)
/mob/living/basic/raptor/baby_raptor/black
name = "baby black raptor"
icon_state = "baby_black"
icon_living = "baby_black"
icon_dead = "baby_black_dead"
growth_path = /mob/living/basic/raptor/black
roll_rate = 10
/mob/living/basic/raptor/baby_raptor/red
name = "baby red raptor"
icon_state = "baby_red"
icon_living = "baby_red"
icon_dead = "baby_red_dead"
growth_path = /mob/living/basic/raptor/red
/mob/living/basic/raptor/baby_raptor/purple
name = "baby purple raptor"
icon_state = "baby_purple"
icon_living = "baby_purple"
icon_dead = "baby_purple_dead"
growth_path = /mob/living/basic/raptor/purple
/mob/living/basic/raptor/baby_raptor/white
name = "baby white raptor"
icon_state = "baby_white"
icon_living = "baby_white"
icon_dead = "baby_white_dead"
growth_path = /mob/living/basic/raptor/white
/mob/living/basic/raptor/baby_raptor/yellow
name = "baby yellow raptor"
icon_state = "baby_yellow"
icon_living = "baby_yellow"
icon_dead = "baby_yellow_dead"
growth_path = /mob/living/basic/raptor/yellow
/mob/living/basic/raptor/baby_raptor/green
name = "baby green raptor"
icon_state = "baby_green"
icon_living = "baby_green"
icon_dead = "baby_green_dead"
growth_path = /mob/living/basic/raptor/green
/mob/living/basic/raptor/baby_raptor/blue
name = "baby blue raptor"
icon_state = "baby_blue"
icon_living = "baby_blue"
icon_dead = "baby_blue_dead"
growth_path = /mob/living/basic/raptor/blue
@@ -8,7 +8,10 @@
return (source != target && target.health < target.maxHealth)
/datum/ai_behavior/find_hunt_target/raptor_baby/valid_dinner(mob/living/source, mob/living/target, radius)
return can_see(source, target, radius) && target.stat != DEAD
if (!can_see(source, target, radius) || target.stat == DEAD || !istype(target, /mob/living/basic/raptor))
return FALSE
var/mob/living/basic/raptor/raptor = target
return raptor.growth_stage == RAPTOR_BABY
/datum/ai_behavior/hunt_target/interact_with_target/reset_target_combat_mode_off/care_for_young
@@ -21,3 +24,15 @@
/datum/ai_behavior/find_hunt_target/raptor_trough/valid_dinner(mob/living/source, atom/movable/trough, radius)
return !!(locate(/obj/item/stack/ore) in trough.contents)
/datum/ai_behavior/find_injured_rider/perform(seconds_per_tick, datum/ai_controller/controller, hunting_target_key, types_to_hunt, hunt_range)
var/mob/living/living_mob = controller.pawn
if (!length(living_mob.buckled_mobs) || !isliving(living_mob.buckled_mobs[1]))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
var/mob/living/rider = living_mob.buckled_mobs[1]
if (rider.stat == CONSCIOUS || rider.stat == DEAD || rider.health >= rider.maxHealth)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
controller.set_blackboard_key(hunting_target_key, rider)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
@@ -9,8 +9,8 @@
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
BB_BABIES_PARTNER_TYPES = list(/mob/living/basic/raptor),
BB_BABIES_CHILD_TYPES = list(/mob/living/basic/raptor/baby_raptor),
BB_MAX_CHILDREN = 5,
BB_RAPTOR_FLEE_THRESHOLD = 0.25,
)
ai_movement = /datum/ai_movement/basic_avoidance
@@ -19,6 +19,7 @@
/datum/ai_planning_subtree/escape_captivity,
/datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee/raptor,
/datum/ai_planning_subtree/flee_target/from_flee_key,
/datum/ai_planning_subtree/find_and_hunt_target/heal_rider,
/datum/ai_planning_subtree/find_and_hunt_target/heal_raptors,
/datum/ai_planning_subtree/random_speech/blackboard,
/datum/ai_planning_subtree/pet_planning,
@@ -26,6 +27,42 @@
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/basic_melee_attack_subtree,
/datum/ai_planning_subtree/find_and_hunt_target/raptor_trough,
/datum/ai_planning_subtree/find_food,
/datum/ai_planning_subtree/find_and_hunt_target/care_for_young,
/datum/ai_planning_subtree/make_babies,
/datum/ai_planning_subtree/express_happiness,
/datum/ai_planning_subtree/find_and_hunt_target/play_with_owner/raptor,
)
/// Angry raptors with no faction check on retaliation
/datum/ai_controller/basic_controller/raptor/aggressive
blackboard = list(
BB_INTERACTIONS_WITH_OWNER = list(
"pecks",
"nuzzles",
"wags their tail against",
"playfully leans against"
),
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/exact_match,
BB_HUNT_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
BB_BABIES_PARTNER_TYPES = list(/mob/living/basic/raptor),
BB_MAX_CHILDREN = 5,
BB_RAPTOR_FLEE_THRESHOLD = 0.1,
)
planning_subtrees = list(
/datum/ai_planning_subtree/escape_captivity,
/datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee/raptor,
/datum/ai_planning_subtree/flee_target/from_flee_key,
/datum/ai_planning_subtree/find_and_hunt_target/heal_raptors,
/datum/ai_planning_subtree/random_speech/blackboard,
/datum/ai_planning_subtree/pet_planning,
/datum/ai_planning_subtree/target_retaliate,
/datum/ai_planning_subtree/simple_find_target/hunt,
/datum/ai_planning_subtree/basic_melee_attack_subtree,
/datum/ai_planning_subtree/find_and_hunt_target/raptor_trough,
/datum/ai_planning_subtree/find_food,
/datum/ai_planning_subtree/find_and_hunt_target/care_for_young,
/datum/ai_planning_subtree/make_babies,
/datum/ai_planning_subtree/express_happiness,
@@ -55,7 +92,6 @@
blackboard = list(
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
BB_FIND_MOM_TYPES = list(/mob/living/basic/raptor),
BB_IGNORE_MOM_TYPES = list(/mob/living/basic/raptor/baby_raptor),
)
ai_movement = /datum/ai_movement/basic_avoidance
@@ -67,5 +103,5 @@
/datum/ai_planning_subtree/random_speech/blackboard,
/datum/ai_planning_subtree/find_and_hunt_target/raptor_trough,
/datum/ai_planning_subtree/express_happiness,
/datum/ai_planning_subtree/look_for_adult,
/datum/ai_planning_subtree/look_for_adult/raptor,
)
@@ -11,19 +11,41 @@
return
return ..()
/datum/ai_planning_subtree/find_and_hunt_target/heal_rider
target_key = BB_INJURED_RAPTOR
hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/heal_raptor
finding_behavior = /datum/ai_behavior/find_injured_rider
hunt_targets = list(/mob/living/carbon/human)
/datum/ai_planning_subtree/find_and_hunt_target/heal_rider/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
if(!controller.blackboard[BB_BASIC_MOB_HEALER])
return
return ..()
/datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee/raptor
target_key = BB_BASIC_MOB_FLEE_TARGET
/datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee/raptor/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
if(!controller.blackboard[BB_RAPTOR_COWARD])
if(controller.blackboard[BB_RAPTOR_COWARD])
return ..()
var/mob/living/basic/raptor/raptor = controller.pawn
if (raptor.health <= raptor.maxHealth * controller.blackboard[BB_RAPTOR_FLEE_THRESHOLD])
return ..()
if (!length(raptor.buckled_mobs))
return
return ..()
var/mob/living/buckled_to = raptor.buckled_mobs[1]
// Flee if our owner is badly injured and out
if (buckled_to.stat != CONSCIOUS && buckled_to.stat != DEAD)
return ..()
/datum/ai_planning_subtree/find_and_hunt_target/care_for_young
target_key = BB_RAPTOR_BABY
hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/reset_target_combat_mode_off/care_for_young
finding_behavior = /datum/ai_behavior/find_hunt_target/raptor_baby
hunt_targets = list(/mob/living/basic/raptor/baby_raptor)
hunt_targets = list(/mob/living/basic/raptor)
hunt_chance = 75
hunt_range = 9
@@ -0,0 +1,439 @@
GLOBAL_LIST_INIT(raptor_colors, init_raptor_colors())
/proc/init_raptor_colors()
var/list/colors = list()
for (var/datum/raptor_color/color_type as anything in subtypesof(/datum/raptor_color))
colors[color_type] = new color_type()
return colors
/datum/raptor_color
/// Color name used for raptor name and icon
var/color = "error"
/// RaptorDex description for the raptor
var/description = "Report this!"
/// Raptor's health
var/health = 220
/// Raptor's speed
var/speed = 0.5
// Minimal and maximal damage for the raptor
var/melee_damage_lower = 10
var/melee_damage_upper = 15
/// Does this raptor redirect projectile hits onto its rider?
var/redirect_shots = TRUE
/// Rideable component type to assign to the raptor
var/rideable_component = /datum/component/riding/creature/raptor
/// Guaranteed cross-breeding colors, other parent color -> child color
var/guaranteed_crossbreeds = list()
/// Type of AI controller the raptor uses
var/datum/ai_controller/ai_controller = /datum/ai_controller/basic_controller/raptor
/// Chance that a newborn baby raptor will be of this color
var/spawn_chance = 33
/// Shared proc, only called once on raptor init for color-specific traits and properties
/datum/raptor_color/proc/setup_raptor(mob/living/basic/raptor/raptor)
if (raptor.ai_controller)
CRASH("setup_raptor called on a raptor ([raptor]) with a present AI controller! This is most likely a result of a second call to setup_raptor.")
raptor.ai_controller = new ai_controller(raptor)
/datum/raptor_color/proc/setup_adult(mob/living/basic/raptor/raptor)
var/datum/raptor_inheritance/stats = raptor.inherited_stats
var/real_health = health + stats.health_modifier
// If we grow up while damaged, keep the damage percentage the same
raptor.health *= real_health / raptor.maxHealth
raptor.maxHealth = real_health
// -0.33 ~ 0.33 gets rounded to 0 rather than +-0.5
var/speed_mod = clamp(round(abs(stats.speed_modifier - 0.08), 0.5) * sign(stats.speed_modifier), -0.5, 0.5)
raptor.set_varspeed(speed - speed_mod)
raptor.melee_damage_lower = melee_damage_lower + stats.attack_modifier
raptor.melee_damage_upper = melee_damage_upper + stats.attack_modifier
if (rideable_component)
raptor.AddElement(/datum/element/ridable, rideable_component)
setup_appearance(raptor)
/datum/raptor_color/proc/setup_young(mob/living/basic/raptor/raptor)
var/datum/raptor_inheritance/stats = raptor.inherited_stats
var/real_health = health + stats.health_modifier
raptor.health *= real_health / 2 / raptor.maxHealth
raptor.maxHealth = real_health / 2
var/speed_mod = clamp(round(abs(stats.speed_modifier - 0.08), 0.5) * sign(stats.speed_modifier), -0.5, 0.5)
raptor.set_varspeed(speed - speed_mod)
raptor.melee_damage_lower = floor((melee_damage_lower + stats.attack_modifier) / 2)
raptor.melee_damage_upper = floor((melee_damage_upper + stats.attack_modifier) / 2)
setup_appearance(raptor)
/datum/raptor_color/proc/setup_baby(mob/living/basic/raptor/raptor)
var/datum/raptor_inheritance/stats = raptor.inherited_stats
var/real_health = health + stats.health_modifier
raptor.health *= real_health / 8 / raptor.maxHealth
raptor.maxHealth = real_health / 8
var/speed_mod = clamp(round(abs(stats.speed_modifier - 0.08), 0.5) * sign(stats.speed_modifier), -0.5, 0.5)
raptor.set_varspeed(speed + 4.5 - speed_mod)
raptor.melee_damage_lower = floor((melee_damage_lower + stats.attack_modifier) / 3)
raptor.melee_damage_upper = floor((melee_damage_upper + stats.attack_modifier) / 3)
setup_appearance(raptor)
/datum/raptor_color/proc/setup_appearance(mob/living/basic/raptor/raptor)
raptor.name = "[color] [raptor.name]"
raptor.icon_state = "[raptor.base_icon_state]_[color]"
raptor.held_state = "[raptor.base_icon_state]_[color]"
raptor.icon_living = "[raptor.base_icon_state]_[color]"
raptor.icon_dead = "[raptor.base_icon_state]_[color]_dead"
raptor.update_appearance()
/datum/raptor_color/red
color = "red"
description = "A resilient breed of raptors, battle-tested and bred for the purpose of humbling its foes in combat, \
This breed demonstrates higher combat capabilities than its peers and oozes ruthless aggression."
melee_damage_lower = 15
melee_damage_upper = 20
health = 300
rideable_component = /datum/component/riding/creature/raptor/combat
redirect_shots = FALSE
guaranteed_crossbreeds = list(
/datum/raptor_color/green = /datum/raptor_color/yellow,
/datum/raptor_color/blue = /datum/raptor_color/purple,
)
// Doesn't care for your excuses for friendly fire
ai_controller = /datum/ai_controller/basic_controller/raptor/aggressive
/datum/raptor_color/purple
color = "purple"
description = "A small, nimble breed, these raptors have been bred as travel companions rather than mounts, capable of storing the owner's possessions and helping them escape from danger unscathed."
health = 140 // smol
rideable_component = /datum/component/riding/creature/raptor/small
guaranteed_crossbreeds = list(
/datum/raptor_color/green = /datum/raptor_color/white,
/datum/raptor_color/yellow = /datum/raptor_color/blue,
)
/datum/raptor_color/purple/setup_raptor(mob/living/basic/raptor/raptor)
. = ..()
RegisterSignal(raptor, COMSIG_MOVABLE_PREBUCKLE, PROC_REF(on_pre_buckle))
raptor.inhand_holder_type = /obj/item/mob_holder/purple_raptor
/datum/raptor_color/purple/proc/on_pre_buckle(mob/living/basic/raptor/source, mob/living/potential_rider, force = FALSE, ride_check_flags = NONE)
SIGNAL_HANDLER
if (!ishuman(potential_rider))
return COMPONENT_BLOCK_BUCKLE
var/mob/living/carbon/human/rider = potential_rider
if (rider.mob_height > HUMAN_HEIGHT_SHORTEST)
to_chat(rider, span_warning("Your tall stature will crush [source] were you attempt to ride [source.p_them()]!"))
return COMPONENT_BLOCK_BUCKLE
// Purple raptors never "fully" grow up, and remain usable as backpacks
/datum/raptor_color/purple/setup_adult(mob/living/basic/raptor/raptor)
raptor.base_pixel_w = initial(raptor.base_pixel_w)
raptor.can_be_held = TRUE
raptor.density = FALSE
raptor.move_resist = MOVE_RESIST_DEFAULT
raptor.change_offsets = FALSE
raptor.remove_offsets(RAPTOR_INNATE_SOURCE, FALSE)
raptor.held_w_class = WEIGHT_CLASS_BULKY
. = ..()
// Non-shorties cannot ride these, so we gotta keep em tameable through food
raptor.AddComponent(/datum/component/tameable, food_types = raptor.food_types, tame_chance = 25, bonus_tame_chance = 15, unique = TRUE)
if (raptor.atom_storage)
return
// A bit bigger (23 vs 21) than a backpack at max size, a bit less by default
var/storage_volume = floor(19 * (1 + raptor.inherited_stats.ability_modifier))
raptor.create_storage(
max_total_storage = storage_volume,
max_slots = storage_volume,
storage_type = /datum/storage/raptor_storage,
)
/datum/raptor_color/purple/setup_young(mob/living/basic/raptor/raptor)
. = ..()
if (raptor.atom_storage)
return
var/storage_volume = floor(19 * (1 + raptor.inherited_stats.ability_modifier))
raptor.create_storage(
max_total_storage = storage_volume,
max_slots = storage_volume,
storage_type = /datum/storage/raptor_storage,
)
/obj/item/mob_holder/purple_raptor
/// Wings action granted to whoever is wearing us on their back
var/datum/action/innate/raptor_wings/flight_action = null
/// Are our wings open?
var/wings_open = FALSE
/// Wings underlay added to the owner, because human rendering code is a mess
var/mutable_appearance/wings_underlay = null
/// Our drift force
var/drift_force = 2 NEWTONS
/// Our stabilizing force
var/stabilizer_force = 4.5 NEWTONS
/obj/item/mob_holder/purple_raptor/Initialize(mapload, mob/living/held_mob, worn_state, head_icon, lh_icon, rh_icon, worn_slot_flags)
. = ..()
var/mob/living/basic/raptor/raptor = held_mob
if (raptor.growth_stage == RAPTOR_BABY)
return
// Create a mirror storage for our raptor when picked up to handle interactions
var/datum/storage/raptor_storage = create_storage(
max_total_storage = held_mob.atom_storage.max_total_storage,
max_slots = held_mob.atom_storage.max_slots,
storage_type = /datum/storage/raptor_storage,
)
raptor_storage.set_real_location(held_mob)
raptor_storage.insert_on_attack = TRUE
if (raptor.growth_stage != RAPTOR_ADULT)
return
flight_action = new(src)
AddComponent( \
/datum/component/jetpack, \
TRUE, \
drift_force, \
stabilizer_force, \
COMSIG_RAPTOR_WINGS_OPENED, \
COMSIG_RAPTOR_WINGS_CLOSED, \
null, \
CALLBACK(src, PROC_REF(can_fly)), \
CALLBACK(src, PROC_REF(can_fly)), \
)
/obj/item/mob_holder/purple_raptor/Destroy()
if (ishuman(loc) && wings_open)
toggle_wings(loc)
QDEL_NULL(flight_action)
return ..()
/obj/item/mob_holder/purple_raptor/equipped(mob/user, slot, initial)
. = ..()
if ((slot & ITEM_SLOT_BACK) && ishuman(user) && flight_action)
flight_action.Grant(held_mob)
flight_action.GiveAction(user)
/obj/item/mob_holder/purple_raptor/dropped(mob/user, silent)
. = ..()
if (wings_open)
toggle_wings(user)
// Removed in Destroy()
if (flight_action)
flight_action.Remove(held_mob)
flight_action.HideFrom(user)
/obj/item/mob_holder/purple_raptor/proc/on_weight_updated(mob/living/carbon/human/source)
SIGNAL_HANDLER
if (source.mob_height <= HUMAN_HEIGHT_SHORTEST && !HAS_TRAIT(source, TRAIT_FAT))
source.remove_movespeed_modifier(/datum/movespeed_modifier/jetpack/raptor/slow)
source.add_movespeed_modifier(/datum/movespeed_modifier/jetpack/raptor)
else
source.remove_movespeed_modifier(/datum/movespeed_modifier/jetpack/raptor)
source.add_movespeed_modifier(/datum/movespeed_modifier/jetpack/raptor/slow)
/obj/item/mob_holder/purple_raptor/proc/can_fly()
var/mob/living/carbon/human/user = loc
if (!istype(user) || user.stat || user.body_position == LYING_DOWN || isnull(user.client))
return FALSE
if (user.get_organ_slot(ORGAN_SLOT_EXTERNAL_WINGS))
return FALSE
var/turf/location = get_turf(user)
if (!istype(location))
return FALSE
var/datum/gas_mixture/environment = location.return_air()
if (environment?.return_pressure() >= HAZARD_LOW_PRESSURE + 10)
return TRUE
to_chat(user, span_warning("The atmosphere is too thin for you to fly!"))
return FALSE
/obj/item/mob_holder/purple_raptor/proc/toggle_wings(mob/living/carbon/human/user)
// In case something goes wrong
if (!istype(user))
wings_open = FALSE
worn_icon_state = icon_state
SEND_SIGNAL(src, COMSIG_RAPTOR_WINGS_CLOSED, user)
STOP_PROCESSING(SSprocessing, src)
return
if (!wings_open && !can_fly())
return
wings_open = !wings_open
worn_icon_state = "[icon_state][wings_open ? "_wings_out" : ""]"
user.update_worn_back()
// Raptors won't have the best of times keeping up tall humans or fatties up in the air
var/struggling = HAS_TRAIT(user, TRAIT_FAT) || user.mob_height > HUMAN_HEIGHT_SHORTEST
if (wings_open)
wings_underlay = user.apply_height_offsets(mutable_appearance(worn_icon, "raptor_purple_wings", -BODY_BEHIND_LAYER, user), UPPER_BODY)
user.add_overlay(wings_underlay)
user.physiology.stun_mod *= 2
user.add_traits(list(TRAIT_MOVE_FLOATING, TRAIT_IGNORING_GRAVITY, TRAIT_NOGRAV_ALWAYS_DRIFT), REF(src))
if (struggling)
user.add_movespeed_modifier(/datum/movespeed_modifier/jetpack/raptor/slow)
else
user.add_movespeed_modifier(/datum/movespeed_modifier/jetpack/raptor)
user.AddElement(/datum/element/forced_gravity, 0)
passtable_on(user, REF(src))
to_chat(user, span_notice("You begin gently hovering above ground as [held_mob] on your back starts furiously flapping [held_mob.p_their()] wings[struggling ? ", struggling to keep you up in the air" : ""]!"))
user.set_resting(FALSE, TRUE)
user.refresh_gravity()
START_PROCESSING(SSprocessing, src)
RegisterSignals(user, list(COMSIG_HUMAN_HEIGHT_UPDATED, SIGNAL_ADDTRAIT(TRAIT_FAT), SIGNAL_REMOVETRAIT(TRAIT_FAT)), PROC_REF(on_weight_updated))
SEND_SIGNAL(src, COMSIG_RAPTOR_WINGS_OPENED, user)
return
user.cut_overlay(wings_underlay)
QDEL_NULL(wings_underlay)
user.physiology.stun_mod *= 0.5
user.remove_traits(list(TRAIT_MOVE_FLOATING, TRAIT_IGNORING_GRAVITY, TRAIT_NOGRAV_ALWAYS_DRIFT), REF(src))
user.remove_movespeed_modifier(/datum/movespeed_modifier/jetpack/raptor/slow)
user.remove_movespeed_modifier(/datum/movespeed_modifier/jetpack/raptor)
user.RemoveElement(/datum/element/forced_gravity, 0)
passtable_off(user, REF(src))
to_chat(user, span_notice("You settle gently back onto the ground[struggling ? ", [held_mob] on your back breathing out a sigh of releif" : ""]..."))
user.refresh_gravity()
STOP_PROCESSING(SSprocessing, src)
UnregisterSignal(user, list(COMSIG_HUMAN_HEIGHT_UPDATED, SIGNAL_ADDTRAIT(TRAIT_FAT), SIGNAL_REMOVETRAIT(TRAIT_FAT)))
SEND_SIGNAL(src, COMSIG_RAPTOR_WINGS_CLOSED, user)
/obj/item/mob_holder/purple_raptor/process(seconds_per_tick)
if (!can_fly())
toggle_wings(loc)
return PROCESS_KILL
/datum/storage/raptor_storage
animated = FALSE
insert_on_attack = FALSE // should flip when worn on the back
/datum/storage/raptor_storage/on_mousedropped_onto(datum/source, obj/item/dropping, mob/user)
return NONE
/datum/action/innate/raptor_wings
name = "Toggle Flight"
check_flags = AB_CHECK_CONSCIOUS | AB_CHECK_IMMOBILE | AB_CHECK_INCAPACITATED
button_icon = 'icons/mob/actions/actions_animal.dmi'
button_icon_state = "raptor_wings"
/datum/action/innate/raptor_wings/Activate()
var/obj/item/mob_holder/purple_raptor/holder = target
var/mob/living/carbon/human/user = holder.loc
if (istype(user) && user.get_item_by_slot(ITEM_SLOT_BACK) == holder)
holder.toggle_wings(user)
/datum/raptor_color/green
color = "green"
description = "A tough breed of raptor, made to withstand the harshest of punishment and to laugh in the face of pain, \
this breed is able to withstand more punishment than its peers."
health = 400
// redirect_shots = FALSE // Need to figure out if I want this or not here
guaranteed_crossbreeds = list(
/datum/raptor_color/purple = /datum/raptor_color/white,
/datum/raptor_color/red = /datum/raptor_color/yellow,
)
/datum/raptor_color/green/setup_adult(mob/living/basic/raptor/raptor)
. = ..()
var/ability_scale = 1 - INVERSE_LERP(RAPTOR_INHERIT_MIN_MODIFIER, RAPTOR_INHERIT_MAX_MODIFIER, raptor.inherited_stats.ability_modifier)
var/mining_mod = round(ability_scale * 0.1, 0.025)
if (ability_scale >= 0.75)
mining_mod = 0
raptor.AddComponent(/datum/component/proficient_miner, mining_mod, TRUE)
/datum/raptor_color/white
color = "white"
description = "A loving sort, it cares for it peers and rushes to their aid with reckless abandon. It is able to heal any raptors' ailments, and rescue its owner in case of an emergency."
rideable_component = /datum/component/riding/creature/raptor/healer
guaranteed_crossbreeds = list(
/datum/raptor_color/blue = /datum/raptor_color/green,
/datum/raptor_color/yellow = /datum/raptor_color/red,
)
/datum/raptor_color/white/setup_young(mob/living/basic/raptor/raptor)
. = ..()
raptor.AddComponent( \
/datum/component/healing_touch, \
heal_brute = melee_damage_upper * 0.75 * (1 + raptor.inherited_stats.ability_modifier), \
heal_burn = melee_damage_upper * 0.75 * (1 + raptor.inherited_stats.ability_modifier), \
heal_time = 0, \
valid_targets_typecache = typecacheof(list(/mob/living/basic/raptor)), \
)
/datum/raptor_color/white/setup_adult(mob/living/basic/raptor/raptor)
. = ..()
qdel(raptor.GetComponent(/datum/component/healing_touch))
raptor.AddComponent( \
/datum/component/healing_touch, \
heal_brute = melee_damage_upper * (1 + raptor.inherited_stats.ability_modifier), \
heal_burn = melee_damage_upper * (1 + raptor.inherited_stats.ability_modifier), \
heal_time = 0, \
valid_targets_typecache = typecacheof(list(/mob/living/basic/raptor, /mob/living/carbon/human)), \
extra_checks = CALLBACK(src, PROC_REF(heal_checks)), \
healing_multiplier = CALLBACK(src, PROC_REF(heal_multiplier)), \
)
/datum/raptor_color/white/proc/heal_checks(mob/living/healer, mob/living/target)
if (istype(target, /mob/living/basic/raptor))
return TRUE
// Only heal raptors, or critted rider
if (target.stat == CONSCIOUS || target.stat == DEAD)
return FALSE
return target.buckled == healer
/datum/raptor_color/white/proc/heal_multiplier(mob/living/healer, mob/living/target)
if (istype(target, /mob/living/basic/raptor))
return 1
// The healing is slow so this is fine
return 0.67
/datum/raptor_color/yellow
color = "yellow"
description = "This breed possesses greasy fast speed, DEMON speed, making light work of long pilgrimages. \
It's said that a thunderclap could be heard when this breed reaches its maximum speed."
speed = 0
guaranteed_crossbreeds = list(
/datum/raptor_color/purple = /datum/raptor_color/blue,
/datum/raptor_color/white = /datum/raptor_color/red,
)
/datum/raptor_color/blue
color = "blue"
description = "Covered in tough, lava-resistant feathers with thick insulated fur underneath, this breed is capable of marching through lava and fire alike."
guaranteed_crossbreeds = list(
/datum/raptor_color/red = /datum/raptor_color/purple,
/datum/raptor_color/white = /datum/raptor_color/green,
)
/datum/raptor_color/blue/setup_raptor(mob/living/basic/raptor/raptor)
. = ..()
raptor.add_traits(list(TRAIT_LAVA_IMMUNE, TRAIT_NOFIRE_SPREAD), INNATE_TRAIT)
/datum/raptor_color/black
color = "black"
description = "An ultra rare breed. Due to its sparse nature, not much is known about this sort. However it is said to possess many of its peers' abilities."
health = 400
speed = 0
melee_damage_lower = 20
melee_damage_upper = 25
redirect_shots = FALSE
rideable_component = /datum/component/riding/creature/raptor/combat
ai_controller = /datum/ai_controller/basic_controller/raptor/aggressive
spawn_chance = 1 // 1 in 150 chance without modifiers
/datum/raptor_color/black/setup_raptor(mob/living/basic/raptor/raptor)
. = ..()
raptor.add_traits(list(TRAIT_LAVA_IMMUNE, TRAIT_NOFIRE_SPREAD), INNATE_TRAIT)
/datum/raptor_color/black/setup_adult(mob/living/basic/raptor/raptor)
. = ..()
// Slightly worse than greens at this
var/ability_scale = 1 - INVERSE_LERP(RAPTOR_INHERIT_MIN_MODIFIER, RAPTOR_INHERIT_MAX_MODIFIER, raptor.inherited_stats.ability_modifier)
var/mining_mod = round(ability_scale * 0.2, 0.025)
if (ability_scale >= 0.8)
mining_mod = 0
raptor.AddComponent(/datum/component/proficient_miner, mining_mod, TRUE)
@@ -1,62 +1,64 @@
/obj/item/raptor_dex
name = "raptor Dex"
name = "RaptorDex"
desc = "A device used to analyze lavaland raptors!"
icon = 'icons/obj/devices/scanner.dmi'
icon_state = "raptor_dex"
item_flags = NOBLUDGEON
///current raptor we are analyzing
var/datum/weakref/raptor
/// Raptor scan data we have stored
var/list/scan_data = list("raptor_scan" = FALSE)
/obj/item/raptor_dex/ui_interact(mob/user, datum/tgui/ui)
if(isnull(raptor?.resolve()))
balloon_alert(user, "no specimen data!")
return TRUE
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "RaptorDex")
ui.open()
/obj/item/raptor_dex/ui_static_data(mob/user)
var/list/data = list()
var/mob/living/basic/raptor/my_raptor = raptor.resolve()
data["raptor_image"] = icon2base64(icon(icon = my_raptor.icon, icon_state = my_raptor.icon_state, frame = 1))
data["raptor_attack"] = my_raptor.melee_damage_lower
data["raptor_health"] = my_raptor.maxHealth
data["raptor_speed"] = my_raptor.speed
data["raptor_color"] = my_raptor.name
data["raptor_gender"] = my_raptor.gender
data["raptor_description"] = my_raptor.dex_description
var/happiness_percentage = my_raptor.ai_controller?.blackboard[BB_BASIC_HAPPINESS]
var/obj/effect/overlay/happiness_overlay/display = new
display.set_hearts(happiness_percentage)
display.pixel_y = ICON_SIZE_Y * 0.5
data["raptor_happiness"] = icon2base64(getFlatIcon(display, no_anim = TRUE))
qdel(display)
var/datum/raptor_inheritance/inherit = my_raptor.inherited_stats
if(isnull(inherit))
return data
data["inherited_attack"] = inherit.attack_modifier
data["inherited_attack_max"] = RAPTOR_INHERIT_MAX_ATTACK
data["inherited_health"] = inherit.health_modifier
data["inherited_health_max"] = RAPTOR_INHERIT_MAX_HEALTH
data["inherited_traits"] = list()
for(var/index in inherit.inherit_traits)
data["inherited_traits"] += GLOB.raptor_inherit_traits[index]
return data
/obj/item/raptor_dex/ui_data(mob/user)
return scan_data
/obj/item/raptor_dex/interact_with_atom(atom/attacked_atom, mob/living/user)
if(!istype(attacked_atom, /mob/living/basic/raptor))
return NONE
raptor = WEAKREF(attacked_atom)
var/mob/living/basic/raptor/my_raptor = attacked_atom
var/datum/movespeed_modifier/intent_mod = my_raptor.get_move_intent_slowdown()
scan_data = list()
scan_data["raptor_scan"] = TRUE
scan_data["raptor_image"] = icon2base64(icon(icon = my_raptor.icon, icon_state = my_raptor.icon_state, dir = SOUTH, frame = 1))
scan_data["raptor_attack"] = my_raptor.melee_damage_lower
scan_data["raptor_health"] = my_raptor.health
scan_data["raptor_max_health"] = my_raptor.maxHealth
scan_data["raptor_speed"] = my_raptor.speed + intent_mod?.multiplicative_slowdown
scan_data["raptor_color"] = my_raptor.name
scan_data["raptor_gender"] = my_raptor.gender
scan_data["raptor_description"] = my_raptor.raptor_color.description
var/happiness_percentage = my_raptor.ai_controller?.blackboard[BB_BASIC_HAPPINESS]
var/obj/effect/overlay/happiness_overlay/display = new()
display.set_hearts(happiness_percentage)
display.pixel_y = ICON_SIZE_Y * 0.5
scan_data["raptor_happiness"] = icon2base64(getFlatIcon(display, no_anim = TRUE))
qdel(display)
var/datum/raptor_inheritance/inherit = my_raptor.inherited_stats
if(!isnull(inherit))
scan_data["inherited_attack"] = inherit.attack_modifier
scan_data["inherited_attack_max"] = RAPTOR_INHERIT_MAX_ATTACK
scan_data["inherited_health"] = inherit.health_modifier
scan_data["inherited_health_max"] = RAPTOR_INHERIT_MAX_HEALTH
scan_data["inherited_speed"] = inherit.speed_modifier
scan_data["inherited_speed_max"] = RAPTOR_INHERIT_MAX_SPEED
scan_data["inherited_ability"] = inherit.ability_modifier
scan_data["inherited_ability_max"] = RAPTOR_INHERIT_MAX_MODIFIER
scan_data["inherited_growth"] = inherit.growth_modifier
scan_data["inherited_growth_max"] = RAPTOR_INHERIT_MAX_MODIFIER
scan_data["inherited_traits"] = list()
for(var/index in inherit.personality_traits)
scan_data["inherited_traits"] += GLOB.raptor_inherit_traits[index]
playsound(src, 'sound/mobs/non-humanoids/orbie/orbie_send_out.ogg', 20)
balloon_alert(user, "scanned")
balloon_alert(my_raptor, "scanned")
ui_interact(user)
return ITEM_INTERACT_SUCCESS
@@ -1,58 +1,52 @@
/obj/item/food/egg/raptor_egg
name = "raptor egg"
desc = "An uneven egg with a rough, thick shell."
icon = 'icons/mob/simple/lavaland/raptor_baby.dmi'
icon_state = "raptor_egg"
///inheritance datum to pass on to the child
var/datum/raptor_inheritance/inherited_stats
resistance_flags = LAVA_PROOF|FIRE_PROOF
/// Color typepath of the child we spawn
var/datum/raptor_color/child_color = /datum/raptor_color
/// Inheritance data to pass onto the child
var/datum/raptor_inheritance/inherited_stats = null
/// Current growth progress
var/growth_progress = 0
/// Minimum growth progress per second
var/min_growth_rate = 0.5
/// Maximum growth progress per second
var/max_growth_rate = 1
/obj/item/food/egg/raptor_egg/Initialize(mapload)
. = ..()
if(SSmapping.is_planetary())
icon = 'icons/mob/simple/lavaland/raptor_icebox.dmi'
/obj/item/food/egg/raptor_egg/proc/determine_growth_path(mob/living/basic/raptor/dad, mob/living/basic/raptor/mom)
if(dad.type == mom.type)
add_growth_component(dad.child_path)
return
var/dad_color = dad.raptor_color
var/mom_color = mom.raptor_color
var/list/my_colors = list(dad_color, mom_color)
sortTim(my_colors, GLOBAL_PROC_REF(cmp_text_asc))
for(var/path in GLOB.raptor_growth_paths) //guaranteed spawns
var/list/required_colors = GLOB.raptor_growth_paths[path]
if(!compare_list(my_colors, required_colors))
continue
add_growth_component(path)
return
var/list/valid_subtypesof = list()
var/static/list/all_subtypes = subtypesof(/mob/living/basic/raptor/baby_raptor)
for(var/path in all_subtypes)
var/mob/living/basic/raptor/baby_raptor/raptor_path = path
if(!prob(initial(raptor_path.roll_rate)))
continue
valid_subtypesof += raptor_path
add_growth_component(pick(valid_subtypesof))
/obj/item/food/egg/raptor_egg/proc/add_growth_component(growth_path)
if(length(GLOB.raptor_population) >= MAX_RAPTOR_POP)
return
AddComponent(\
/datum/component/fertile_egg,\
embryo_type = growth_path,\
minimum_growth_rate = 0.5,\
maximum_growth_rate = 1,\
total_growth_required = 100,\
current_growth = 0,\
location_allowlist = typecacheof(list(/turf)),\
post_hatch = CALLBACK(src, PROC_REF(post_hatch)),\
)
/obj/item/food/egg/raptor_egg/proc/post_hatch(mob/living/basic/raptor/baby)
if(!istype(baby))
return
QDEL_NULL(baby.inherited_stats)
baby.inherited_stats = inherited_stats
inherited_stats = null
START_PROCESSING(SSobj, src)
/obj/item/food/egg/raptor_egg/Destroy()
STOP_PROCESSING(SSobj, src)
QDEL_NULL(inherited_stats)
return ..()
/obj/item/food/egg/raptor_egg/examine(mob/user)
. = ..()
if (growth_progress >= RAPTOR_EGG_GROWTH_PROGRESS)
. += span_boldnotice("Its noticeably shaking, ready to hatch!")
/obj/item/food/egg/raptor_egg/process(seconds_per_tick)
if (!isturf(loc) || length(GLOB.raptor_population) >= MAX_RAPTOR_POP)
return
var/growth_value = rand(min_growth_rate, max_growth_rate) * seconds_per_tick * (1 + inherited_stats.growth_modifier)
// Slower growth off hot lavaland
if (!SSmapping.level_trait(z, ZTRAIT_ASHSTORM))
growth_value *= 0.75
// Faster growth in hot hot lava
if (islava(loc))
growth_value *= 1.5
growth_progress += growth_value
// Don't hatch on lava though, or the chick will die instantly
if (growth_progress < RAPTOR_EGG_GROWTH_PROGRESS || islava(loc))
return
visible_message(span_notice("[src] hatches with a quiet cracking sound."))
new /mob/living/basic/raptor(loc, child_color, inherited_stats)
inherited_stats = null
qdel(src)
@@ -1,31 +1,97 @@
#define RANDOM_INHERIT_AMOUNT 2
/datum/raptor_inheritance
///list of traits we inherit
var/list/inherit_traits = list()
///attack modifier
var/attack_modifier
///health_modifier
var/health_modifier
/// Inherited parent personality traits
var/list/personality_traits = list()
/// Flat damage modifier
var/attack_modifier = 0
/// Flat health modifier
var/health_modifier = 0
/// Speed modifier, not randomized by default
var/speed_modifier = 0
/// Primary ability stat modifier, not randomized by default
/// Multiplier equates to 1 + this
var/ability_modifier = 0
/// Growth rate modifier, not randomized by default
/// Multiplier equates to 1 + this
var/growth_modifier = 0
/// Foods eaten that can affect our child's stats -> stats they modify
var/list/foods_eaten = list()
/// List of all colors in our family tree
var/list/parent_colors = list()
/datum/raptor_inheritance/New(datum/raptor_inheritance/father, datum/raptor_inheritance/mother)
// Owner being set will randomize the stats as it means we're spawned not via an egg
// otherwise wait for set_parents call to inherit genetics
/datum/raptor_inheritance/New(mob/living/basic/raptor/owner)
. = ..()
randomize_stats()
/datum/raptor_inheritance/proc/randomize_stats()
attack_modifier = rand(0, RAPTOR_INHERIT_MAX_ATTACK)
health_modifier = rand(0, RAPTOR_INHERIT_MAX_HEALTH)
var/list/traits_to_pick = GLOB.raptor_inherit_traits.Copy()
for(var/i in 1 to RANDOM_INHERIT_AMOUNT)
inherit_traits += pick_n_take(traits_to_pick)
/datum/raptor_inheritance/proc/set_parents(datum/raptor_inheritance/father, datum/raptor_inheritance/mother)
if(isnull(father) || isnull(mother))
if (!owner)
return
if(length(father.inherit_traits))
inherit_traits += pick(father.inherit_traits)
if(length(mother.inherit_traits))
inherit_traits += pick(mother.inherit_traits)
attack_modifier = rand(min(father.attack_modifier, mother.attack_modifier), max(father.attack_modifier, mother.attack_modifier))
health_modifier = rand(min(father.health_modifier, mother.health_modifier), max(father.health_modifier, mother.health_modifier))
#undef RANDOM_INHERIT_AMOUNT
attack_modifier = rand(RAPTOR_INHERIT_MIN_ATTACK * 0.5, RAPTOR_INHERIT_MAX_ATTACK * 0.5)
health_modifier = rand(RAPTOR_INHERIT_MIN_HEALTH * 0.5, RAPTOR_INHERIT_MAX_HEALTH * 0.5)
var/list/traits_to_pick = GLOB.raptor_inherit_traits.Copy()
for(var/i in 1 to rand(1, RAPTOR_TRAIT_INHERIT_AMOUNT))
personality_traits += pick_n_take(traits_to_pick)
/datum/raptor_inheritance/proc/set_parents(mob/living/basic/raptor/mother, mob/living/basic/raptor/father)
var/datum/raptor_inheritance/mom_stats = mother.inherited_stats
var/datum/raptor_inheritance/dad_stats = father.inherited_stats
parent_colors = mom_stats.parent_colors | dad_stats.parent_colors | mother.raptor_color.type | father.raptor_color.type
var/list/traits_to_pick = list()
// + so shared traits have double the weight
for (var/raptor_trait in mom_stats.personality_traits + dad_stats.personality_traits)
if (!traits_to_pick[raptor_trait])
traits_to_pick[raptor_trait] = 0
traits_to_pick[raptor_trait] += 1
var/attack_mod = 0
var/health_mod = 0
var/speed_mod = 0
var/ability_mod = 0
var/growth_mod = 0
var/amount_eaten = 0
for (var/food_type in foods_eaten)
var/list/stat_mods = foods_eaten[food_type]
amount_eaten += stat_mods["amount"]
for (var/food_type in foods_eaten)
var/list/stat_mods = foods_eaten[food_type]
// First multiply stats themselves, then multiply to get a proportion of this food from amount_eaten
var/amount = stat_mods["amount"] ** 2
// Eating other foods reduces the effects of a specific one
attack_mod += stat_mods["attack"] / amount_eaten * amount
health_mod += stat_mods["health"] / amount_eaten * amount
speed_mod += stat_mods["speed"] / amount_eaten * amount
ability_mod += stat_mods["ability"] / amount_eaten * amount
growth_mod += stat_mods["growth"] / amount_eaten * amount
var/list/trait_list = stat_mods["traits"]
for (var/raptor_trait in trait_list)
if (!traits_to_pick[raptor_trait])
traits_to_pick[raptor_trait] = 0
traits_to_pick[raptor_trait] += trait_list[raptor_trait] * stat_mods["amount"]
// If we don't clamp these, RNG and drift can get wildly out of control and result in polar values
// and this way we at most get twice the minimum/maximum value, so at least half the rand spread is within the clamp values
attack_mod = clamp(attack_mod, RAPTOR_INHERIT_MIN_ATTACK, RAPTOR_INHERIT_MAX_ATTACK)
health_mod = clamp(health_mod, RAPTOR_INHERIT_MIN_HEALTH, RAPTOR_INHERIT_MAX_HEALTH)
speed_mod = clamp(speed_mod, RAPTOR_INHERIT_MIN_SPEED, RAPTOR_INHERIT_MAX_SPEED)
ability_mod = clamp(ability_mod, RAPTOR_INHERIT_MIN_MODIFIER, RAPTOR_INHERIT_MAX_MODIFIER)
growth_mod = clamp(growth_mod, RAPTOR_INHERIT_MIN_MODIFIER, RAPTOR_INHERIT_MAX_MODIFIER)
attack_modifier = rand((min(mom_stats.attack_modifier, dad_stats.attack_modifier) + min(0, attack_mod)) + RAPTOR_GENETIC_DRIFT * RAPTOR_INHERIT_MIN_ATTACK, (max(mom_stats.attack_modifier, dad_stats.attack_modifier) + max(0, attack_mod)) + RAPTOR_GENETIC_DRIFT * RAPTOR_INHERIT_MAX_ATTACK)
health_modifier = rand((min(mom_stats.health_modifier, dad_stats.health_modifier) + min(0, health_mod)) + RAPTOR_GENETIC_DRIFT * RAPTOR_INHERIT_MIN_HEALTH, (max(mom_stats.health_modifier, dad_stats.health_modifier) + max(0, health_mod)) + RAPTOR_GENETIC_DRIFT * RAPTOR_INHERIT_MAX_HEALTH)
speed_modifier = rand((min(mom_stats.speed_modifier, dad_stats.speed_modifier) + min(0, speed_mod)) + RAPTOR_GENETIC_DRIFT * RAPTOR_INHERIT_MIN_SPEED, (max(mom_stats.speed_modifier, dad_stats.speed_modifier) + max(0, speed_mod)) + RAPTOR_GENETIC_DRIFT * RAPTOR_INHERIT_MAX_SPEED)
ability_modifier = rand((min(mom_stats.ability_modifier, dad_stats.ability_modifier) + min(0, ability_mod)) + RAPTOR_GENETIC_DRIFT * RAPTOR_INHERIT_MIN_MODIFIER, (max(mom_stats.ability_modifier, dad_stats.ability_modifier) + max(0, ability_mod)) + RAPTOR_GENETIC_DRIFT * RAPTOR_INHERIT_MAX_MODIFIER)
growth_modifier = rand((min(mom_stats.growth_modifier, dad_stats.growth_modifier) + min(0, growth_mod)) + RAPTOR_GENETIC_DRIFT * RAPTOR_INHERIT_MIN_MODIFIER, (max(mom_stats.growth_modifier, dad_stats.growth_modifier) + max(0, growth_mod)) + RAPTOR_GENETIC_DRIFT * RAPTOR_INHERIT_MAX_MODIFIER)
for(var/i in 1 to min(length(traits_to_pick), RAPTOR_TRAIT_INHERIT_AMOUNT))
var/chosen_trait = pick_weight(traits_to_pick)
traits_to_pick -= chosen_trait
personality_traits += chosen_trait
attack_modifier = clamp(attack_modifier, RAPTOR_INHERIT_MIN_ATTACK, RAPTOR_INHERIT_MAX_ATTACK)
health_modifier = clamp(health_modifier, RAPTOR_INHERIT_MIN_HEALTH, RAPTOR_INHERIT_MAX_HEALTH)
speed_modifier = clamp(speed_modifier, RAPTOR_INHERIT_MIN_SPEED, RAPTOR_INHERIT_MAX_SPEED)
ability_modifier = clamp(ability_modifier, RAPTOR_INHERIT_MIN_MODIFIER, RAPTOR_INHERIT_MAX_MODIFIER)
growth_modifier = clamp(growth_modifier, RAPTOR_INHERIT_MIN_MODIFIER, RAPTOR_INHERIT_MAX_MODIFIER)
@@ -81,6 +81,7 @@
/mob/living/basic/snail/mob_pickup(mob/living/user)
var/obj/item/mob_holder/snail/holder = new(get_turf(src), src, held_state, head_icon, held_lh, held_rh, worn_slot_flags)
SEND_SIGNAL(src, COMSIG_LIVING_SCOOPED_UP, user, holder)
var/display_message = "[user] [HAS_TRAIT(src, TRAIT_MOVE_FLOATING) ? "scoops up [src]" : "peels [src] off the ground"]!"
user.visible_message(span_warning(display_message))
user.put_in_hands(holder)
@@ -280,6 +280,7 @@
mob_height = dna?.species?.update_species_heights(src) || base_mob_height
if(old_height != mob_height)
regenerate_icons()
SEND_SIGNAL(src, COMSIG_HUMAN_HEIGHT_UPDATED, old_height)
/**
* Makes a full copy of src and returns it.
+7 -7
View File
@@ -8,8 +8,6 @@
slot_flags = NONE
/// Mob inside of us
var/mob/living/held_mob
/// True if we've started being destroyed
var/destroying = FALSE
lefthand_file = 'icons/mob/inhands/clothing/hats_lefthand.dmi'
righthand_file = 'icons/mob/inhands/clothing/hats_righthand.dmi'
body_parts_covered = HEAD
@@ -31,9 +29,9 @@
return ..()
/obj/item/mob_holder/Destroy()
destroying = TRUE
if(held_mob)
if(held_mob?.loc == src)
release()
held_mob = null
return ..()
/obj/item/mob_holder/proc/insert_mob(mob/living/new_prisoner)
@@ -78,11 +76,10 @@
/obj/item/mob_holder/proc/release(display_messages = TRUE)
if(!held_mob)
if(!destroying)
if(!QDELETED(src))
qdel(src)
return FALSE
var/mob/living/released_mob = held_mob
held_mob = null // stops the held mob from being release()'d twice.
if(isliving(loc))
var/mob/living/captor = loc
if(display_messages)
@@ -93,7 +90,7 @@
released_mob.setDir(SOUTH)
if(display_messages)
released_mob.visible_message(span_warning("[released_mob] uncurls!"))
if(!destroying)
if(!QDELETED(src))
qdel(src)
return TRUE
@@ -159,3 +156,6 @@
if(. || !held_mob) // Another interaction was performed
return
tool.melee_attack_chain(user, held_mob, modifiers) //Interact with the mob with our tool
/obj/item/mob_holder/IsContainedAtomAccessible(atom/contained, atom/movable/user)
return TRUE
+2 -1
View File
@@ -1965,7 +1965,8 @@ GLOBAL_LIST_EMPTY(fire_appearances)
return ..()
/mob/living/proc/mob_pickup(mob/living/user)
var/obj/item/mob_holder/holder = new(get_turf(src), src, held_state, head_icon, held_lh, held_rh, worn_slot_flags)
var/obj/item/mob_holder/holder = new inhand_holder_type(get_turf(src), src, held_state, head_icon, held_lh, held_rh, worn_slot_flags)
SEND_SIGNAL(src, COMSIG_LIVING_SCOOPED_UP, user, holder)
user.visible_message(span_warning("[user] scoops up [src]!"))
user.put_in_hands(holder)
@@ -204,6 +204,8 @@
var/icon/head_icon = 'icons/mob/clothing/head/pets_head.dmi'
/// icon_state for holding mobs.
var/held_state = ""
/// Typepath of the holder created when we're picked up
var/inhand_holder_type = /obj/item/mob_holder
///If combat mode is on or not
var/combat_mode = FALSE
@@ -259,6 +259,7 @@
greyscale_colors = "#927444#8b4c31#4c202d"
starting_programs = list(
/datum/computer_file/program/skill_tracker,
/datum/computer_file/program/raptordex,
)
/obj/item/modular_computer/pda/bitrunner
@@ -0,0 +1,61 @@
/datum/computer_file/program/raptordex
filename = "raptordex"
filedesc = "RaptorDex"
downloader_category = PROGRAM_CATEGORY_DEVICE
program_open_overlay = "bountyboard"
extended_desc = "Scan and analyze raptor genes."
size = 2
tgui_id = "RaptorDex"
program_icon = "crow"
can_run_on_flags = PROGRAM_PDA
/// Raptor scan data we have stored
var/list/scan_data = list("raptor_scan" = FALSE)
/datum/computer_file/program/raptordex/tap(atom/tapped_atom, mob/living/user, list/modifiers)
if(!istype(tapped_atom, /mob/living/basic/raptor))
return FALSE
var/mob/living/basic/raptor/my_raptor = tapped_atom
var/datum/movespeed_modifier/intent_mod = my_raptor.get_move_intent_slowdown()
scan_data = list()
scan_data["raptor_scan"] = TRUE
scan_data["raptor_image"] = icon2base64(icon(icon = my_raptor.icon, icon_state = my_raptor.icon_state, dir = SOUTH, frame = 1))
scan_data["raptor_attack"] = my_raptor.melee_damage_lower
scan_data["raptor_health"] = my_raptor.health
scan_data["raptor_max_health"] = my_raptor.maxHealth
scan_data["raptor_speed"] = my_raptor.speed + intent_mod?.multiplicative_slowdown
scan_data["raptor_color"] = my_raptor.name
scan_data["raptor_gender"] = my_raptor.gender
scan_data["raptor_description"] = my_raptor.raptor_color.description
var/happiness_percentage = my_raptor.ai_controller?.blackboard[BB_BASIC_HAPPINESS]
var/obj/effect/overlay/happiness_overlay/display = new()
display.set_hearts(happiness_percentage)
display.pixel_y = ICON_SIZE_Y * 0.5
scan_data["raptor_happiness"] = icon2base64(getFlatIcon(display, no_anim = TRUE))
qdel(display)
var/datum/raptor_inheritance/inherit = my_raptor.inherited_stats
if(!isnull(inherit))
scan_data["inherited_attack"] = inherit.attack_modifier
scan_data["inherited_attack_max"] = RAPTOR_INHERIT_MAX_ATTACK
scan_data["inherited_health"] = inherit.health_modifier
scan_data["inherited_health_max"] = RAPTOR_INHERIT_MAX_HEALTH
scan_data["inherited_speed"] = inherit.speed_modifier
scan_data["inherited_speed_max"] = RAPTOR_INHERIT_MAX_SPEED
scan_data["inherited_ability"] = inherit.ability_modifier
scan_data["inherited_ability_max"] = RAPTOR_INHERIT_MAX_MODIFIER
scan_data["inherited_growth"] = inherit.growth_modifier
scan_data["inherited_growth_max"] = RAPTOR_INHERIT_MAX_MODIFIER
scan_data["inherited_traits"] = list()
for(var/index in inherit.personality_traits)
scan_data["inherited_traits"] += GLOB.raptor_inherit_traits[index]
playsound(computer, 'sound/mobs/non-humanoids/orbie/orbie_send_out.ogg', 20)
my_raptor.balloon_alert(user, "scanned")
return TRUE
/datum/computer_file/program/raptordex/ui_data(mob/user)
return scan_data
@@ -11,6 +11,12 @@
/datum/movespeed_modifier/jetpack/wings
multiplicative_slowdown = -0.3
/datum/movespeed_modifier/jetpack/raptor
multiplicative_slowdown = 0.2
/datum/movespeed_modifier/jetpack/raptor/slow
multiplicative_slowdown = 1
/datum/movespeed_modifier/die_of_fate
multiplicative_slowdown = 1
+2 -2
View File
@@ -163,10 +163,10 @@
variable = TRUE
/datum/movespeed_modifier/goliath_mount
multiplicative_slowdown = -26
multiplicative_slowdown = -27.5
/datum/movespeed_modifier/goldgrub_mount
multiplicative_slowdown = -3
multiplicative_slowdown = -4.5
/datum/movespeed_modifier/settler
multiplicative_slowdown = 0.2
Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 33 KiB

+3 -1
View File
@@ -1625,6 +1625,7 @@
#include "code\datums\elements\radiation_protected_clothing.dm"
#include "code\datums\elements\radioactive.dm"
#include "code\datums\elements\ranged_armour.dm"
#include "code\datums\elements\raptor_food.dm"
#include "code\datums\elements\reagents_heated_on_fire.dm"
#include "code\datums\elements\reagents_item_heatable.dm"
#include "code\datums\elements\regal_rat_minion.dm"
@@ -5199,10 +5200,10 @@
#include "code\modules\mob\living\basic\lavaland\mook\mook_village.dm"
#include "code\modules\mob\living\basic\lavaland\node_drone\node_drone.dm"
#include "code\modules\mob\living\basic\lavaland\raptor\_raptor.dm"
#include "code\modules\mob\living\basic\lavaland\raptor\baby_raptor.dm"
#include "code\modules\mob\living\basic\lavaland\raptor\raptor_ai_behavior.dm"
#include "code\modules\mob\living\basic\lavaland\raptor\raptor_ai_controller.dm"
#include "code\modules\mob\living\basic\lavaland\raptor\raptor_ai_subtrees.dm"
#include "code\modules\mob\living\basic\lavaland\raptor\raptor_color.dm"
#include "code\modules\mob\living\basic\lavaland\raptor\raptor_dex.dm"
#include "code\modules\mob\living\basic\lavaland\raptor\raptor_egg.dm"
#include "code\modules\mob\living\basic\lavaland\raptor\raptor_food_trough.dm"
@@ -5656,6 +5657,7 @@
#include "code\modules\modular_computers\file_system\programs\portrait_printer.dm"
#include "code\modules\modular_computers\file_system\programs\powermonitor.dm"
#include "code\modules\modular_computers\file_system\programs\radar.dm"
#include "code\modules\modular_computers\file_system\programs\raptordex.dm"
#include "code\modules\modular_computers\file_system\programs\records.dm"
#include "code\modules\modular_computers\file_system\programs\restock_tracker.dm"
#include "code\modules\modular_computers\file_system\programs\robocontrol.dm"
+80 -24
View File
@@ -1,17 +1,20 @@
import {
Image,
LabeledList,
Modal,
ProgressBar,
Section,
Stack,
} from 'tgui-core/components';
import { capitalizeAll, capitalizeFirst } from 'tgui-core/string';
import { useBackend } from '../backend';
import { Window } from '../layouts';
type Data = {
raptor_scan: boolean;
raptor_attack: number;
raptor_health: number;
raptor_max_health: number;
raptor_speed: number;
raptor_color: string;
raptor_image: string;
@@ -22,14 +25,22 @@ type Data = {
inherited_attack_max: number;
inherited_health: number;
inherited_health_max: number;
inherited_speed: number;
inherited_speed_max: number;
inherited_ability: number;
inherited_ability_max: number;
inherited_growth: number;
inherited_growth_max: number;
inherited_traits: string[];
};
export const RaptorDex = (props) => {
const { act, data } = useBackend<Data>();
const {
raptor_scan,
raptor_attack,
raptor_health,
raptor_max_health,
raptor_speed,
raptor_image,
raptor_gender,
@@ -37,21 +48,39 @@ export const RaptorDex = (props) => {
inherited_attack_max,
inherited_health,
inherited_health_max,
inherited_speed,
inherited_speed_max,
inherited_ability,
inherited_ability_max,
inherited_growth,
inherited_growth_max,
raptor_happiness,
inherited_traits,
raptor_description,
raptor_color,
} = data;
if (!raptor_scan) {
return (
<Window title="Raptor Data" width={625} height={420} theme="hackerman">
<Window.Content>
<Modal textAlign="center">No scan data present.</Modal>
</Window.Content>
</Window>
);
}
return (
<Window title="Raptor Data" width={625} height={370} theme="hackerman">
<Window title="Raptor Data" width={770} height={370} theme="hackerman">
<Window.Content>
<Stack>
{' '}
<Stack.Item width="33%">
<Section textAlign="center" title={raptor_color}>
<Section textAlign="center" title={capitalizeAll(raptor_color)}>
<Image
src={`data:image/jpeg;base64,${raptor_image}`}
height="160px"
width="160px"
height="128px"
width="224px"
style={{
verticalAlign: 'middle',
borderRadius: '1em',
@@ -59,21 +88,24 @@ export const RaptorDex = (props) => {
}}
/>
</Section>
<Section fill title="Description">
{raptor_description}
</Section>
</Stack.Item>
<Stack.Item width="33%" textAlign="center">
<Section title="Stats">
<LabeledList>
<LabeledList.Item label="Health">
{raptor_health}
{raptor_health} / {raptor_max_health}
</LabeledList.Item>
<LabeledList.Item label="Attack">
{raptor_attack}
</LabeledList.Item>
<LabeledList.Item label="Speed">
{10 - raptor_speed}
{Math.round(10 / Math.max(raptor_speed, 0.5))}
</LabeledList.Item>
<LabeledList.Item label="Gender">
{raptor_gender}
{capitalizeFirst(raptor_gender)}
</LabeledList.Item>
</LabeledList>
</Section>
@@ -84,12 +116,9 @@ export const RaptorDex = (props) => {
value={inherited_health}
maxValue={inherited_health_max}
ranges={{
good: [0.7 * inherited_health_max, inherited_health_max],
average: [
0.4 * inherited_health_max,
inherited_health_max,
],
bad: [-Infinity, inherited_health_max],
good: [0.5 * inherited_health_max, Infinity],
average: [0, inherited_health_max * 0.5],
bad: [-Infinity, 0],
}}
/>
</LabeledList.Item>
@@ -98,12 +127,42 @@ export const RaptorDex = (props) => {
value={inherited_attack}
maxValue={inherited_attack_max}
ranges={{
good: [0.7 * inherited_attack_max, inherited_attack_max],
average: [
0.4 * inherited_attack_max,
inherited_attack_max,
],
bad: [-Infinity, inherited_attack_max],
good: [0.5 * inherited_attack_max, Infinity],
average: [0, inherited_attack_max * 0.5],
bad: [-Infinity, 0],
}}
/>
</LabeledList.Item>
<LabeledList.Item label="Speed">
<ProgressBar
value={inherited_speed}
maxValue={inherited_speed_max}
ranges={{
good: [0.5 * inherited_speed_max, Infinity],
average: [0, inherited_speed_max * 0.5],
bad: [-Infinity, 0],
}}
/>
</LabeledList.Item>
<LabeledList.Item label="Capability">
<ProgressBar
value={inherited_ability}
maxValue={inherited_ability_max}
ranges={{
good: [0.5 * inherited_ability_max, Infinity],
average: [0, inherited_ability_max * 0.5],
bad: [-Infinity, 0],
}}
/>
</LabeledList.Item>
<LabeledList.Item label="Growth Speed">
<ProgressBar
value={inherited_growth}
maxValue={inherited_growth_max}
ranges={{
good: [0.5 * inherited_growth_max, Infinity],
average: [0, inherited_growth_max * 0.5],
bad: [-Infinity, 0],
}}
/>
</LabeledList.Item>
@@ -113,7 +172,7 @@ export const RaptorDex = (props) => {
<Stack.Item width="33%">
<Section textAlign="center" title="Friendship bond">
<Image
mt={-7}
mt={-9.5}
src={`data:image/jpeg;base64,${raptor_happiness}`}
height="72px"
width="72px"
@@ -128,9 +187,6 @@ export const RaptorDex = (props) => {
</Section>
</Stack.Item>
</Stack>
<Section fill title="Desc">
{raptor_description}
</Section>
</Window.Content>
</Window>
);