mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-28 18:11:16 +00:00
* Gives bread and cake slice_types and adds screentip verbs to proccessed foods (#71449) ## About The Pull Request A side effect of my pizza PR #71202 I added contextual screentips as part of processable.dm. In doing this, I noticed that with a few exceptions, almost every single bread and cake type copies the proc exactly the same for every single child of cake or bread, so I put the proc on the parent of bread and cake and gave them slice_types, making them more similar to pizza.dm For everything else I've changed the default that I put in processable.dm into "slice" or "cut" for things that use the knife and "flatten" for things that use the rolling pin. Finally, you can slice bread with saws now, because I think its silly that only pizza gets this luxury. ## Why It's Good For The Game Because it wasnt the focus of #71202 I didn't mess with screentips outside of the pizza file a lot, but now that it's merged I figure I should go and do that. As Bread and Cake's processables are almost fully standardized it seems silly for them to call on the proc 12 times in the same document so I did this, which also allows for more versatility in editing how they work as well allow people to, if they want to, add more tool behaviours in the future without adding in 12 lines of code. Also means that people who want to add new cake or bread have one less thing to do. ## Changelog 🆑 add: you can saw bread with a saw into bread slices qol: added screentip verbs to a bunch of food files code: bread and cake now have slice types and all only have one call on the processable.dm proc /🆑 * Gives bread and cake slice_types and adds screentip verbs to proccessed foods * sco'ish * fuck me ig Co-authored-by: Sol N <116288367+flowercuco@users.noreply.github.com> Co-authored-by: John Doe <gamingskeleton3@gmail.com>
371 lines
12 KiB
Plaintext
371 lines
12 KiB
Plaintext
// Fish path used for autogenerated fish
|
|
/obj/item/fish
|
|
name = "generic looking aquarium fish"
|
|
desc = "very bland"
|
|
icon = 'icons/obj/aquarium.dmi'
|
|
icon_state = "bugfish"
|
|
|
|
w_class = WEIGHT_CLASS_TINY
|
|
|
|
/// Resulting width of aquarium visual icon - default size of "fish_greyscale" state
|
|
var/sprite_width = 3
|
|
/// Resulting height of aquarium visual icon - default size of "fish_greyscale" state
|
|
var/sprite_height = 3
|
|
|
|
/// Original width of aquarium visual icon - used to calculate scaledown factor
|
|
var/source_width = 32
|
|
/// Original height of aquarium visual icon - used to calculate scaledown factor
|
|
var/source_height = 32
|
|
|
|
/// If present this icon will be used for in-aquarium visual for the fish instead of icon_state
|
|
var/dedicated_in_aquarium_icon_state
|
|
|
|
/// If present aquarium visual will be this color
|
|
var/aquarium_vc_color
|
|
|
|
/// Required fluid type for this fish to live.
|
|
var/required_fluid_type = AQUARIUM_FLUID_FRESHWATER
|
|
/// Required minimum temperature for the fish to live.
|
|
var/required_temperature_min = MIN_AQUARIUM_TEMP
|
|
/// Maximum possible temperature for the fish to live.
|
|
var/required_temperature_max = MAX_AQUARIUM_TEMP
|
|
|
|
/// What type of reagent this fish needs to be fed.
|
|
var/food = /datum/reagent/consumable/nutriment
|
|
/// How often the fish needs to be fed
|
|
var/feeding_frequency = 5 MINUTES
|
|
/// Time of last feedeing
|
|
var/last_feeding
|
|
|
|
/// Fish status
|
|
var/status = FISH_ALIVE
|
|
|
|
/// Current fish health. Dies at 0.
|
|
var/health = 100
|
|
|
|
/// Should this fish type show in fish catalog
|
|
var/show_in_catalog = TRUE
|
|
/// Should this fish spawn in random fish cases
|
|
var/available_in_random_cases = TRUE
|
|
/// How rare this fish is in the random cases
|
|
var/random_case_rarity = FISH_RARITY_BASIC
|
|
|
|
/// Fish autogenerated from this behaviour will be processable into this
|
|
var/fillet_type = /obj/item/food/fishmeat
|
|
|
|
/// Won't breed more than this amount in single aquarium.
|
|
var/stable_population = 1
|
|
/// Last time new fish was created
|
|
var/last_breeding
|
|
/// How long it takes to produce new fish
|
|
var/breeding_timeout = 2 MINUTES
|
|
|
|
var/flopping = FALSE
|
|
|
|
var/in_stasis = FALSE
|
|
|
|
// Fishing related properties
|
|
|
|
/// List of fishing trait types, these modify probabilty/difficulty depending on rod/user properties
|
|
var/list/fishing_traits = list()
|
|
|
|
/// Fishing behaviour
|
|
var/fish_ai_type = FISH_AI_DUMB
|
|
|
|
/// Base additive modifier to fishing difficulty
|
|
var/fishing_difficulty_modifier = 0
|
|
|
|
/**
|
|
* Bait identifiers that make catching this fish easier and more likely
|
|
* Bait identifiers: Path | Trait | list("Type"="Foodtype","Value"= Food Type Flag like [MEAT])
|
|
*/
|
|
var/list/favorite_bait = list()
|
|
|
|
/**
|
|
* Bait identifiers that make catching this fish harder and less likely
|
|
* Bait identifiers: Path | Trait | list("Type"="Foodtype","Value"= Food Type Flag like [MEAT])
|
|
*/
|
|
var/list/disliked_bait = list()
|
|
|
|
/// Size in centimeters
|
|
var/size = 50
|
|
/// Average size for this fish type in centimeters. Will be used as gaussian distribution with 20% deviation for fishing, bought fish are always standard size
|
|
var/average_size = 50
|
|
|
|
/// Weight in grams
|
|
var/weight = 1000
|
|
/// Average weight for this fish type in grams
|
|
var/average_weight = 1000
|
|
|
|
|
|
|
|
/obj/item/fish/Initialize(mapload)
|
|
. = ..()
|
|
if(fillet_type)
|
|
AddElement(/datum/element/processable, TOOL_KNIFE, fillet_type, 1, 5, screentip_verb = "Cut")
|
|
AddComponent(/datum/component/aquarium_content, PROC_REF(get_aquarium_animation), list(COMSIG_FISH_STATUS_CHANGED,COMSIG_FISH_STIRRED))
|
|
RegisterSignal(src, COMSIG_ATOM_TEMPORARY_ANIMATION_START, PROC_REF(on_temp_animation))
|
|
|
|
check_environment_after_movement()
|
|
if(status != FISH_DEAD)
|
|
START_PROCESSING(SSobj, src)
|
|
|
|
size = average_size
|
|
weight = average_weight
|
|
|
|
/obj/item/fish/examine(mob/user)
|
|
. = ..()
|
|
// All spacemen have magic eyes of fish weight perception until fish scale (get it?) is implemented.
|
|
. += span_notice("It's [size] cm long.")
|
|
. += span_notice("It weighs [weight] g.")
|
|
|
|
/obj/item/fish/proc/randomize_weight_and_size(modifier = 0)
|
|
var/size_deviation = 0.2 * average_size
|
|
var/size_mod = modifier * average_size
|
|
size = max(1,gaussian(average_size + size_mod, size_deviation))
|
|
|
|
var/weight_deviation = 0.2 * average_weight
|
|
var/weight_mod = modifier * average_weight
|
|
weight = max(1,gaussian(average_weight + weight_mod, weight_deviation))
|
|
|
|
/obj/item/fish/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE)
|
|
. = ..()
|
|
check_environment_after_movement()
|
|
|
|
/obj/item/fish/proc/enter_stasis()
|
|
in_stasis = TRUE
|
|
// Stop processing until inserted into aquarium again.
|
|
stop_flopping()
|
|
STOP_PROCESSING(SSobj, src)
|
|
|
|
/obj/item/fish/proc/exit_stasis()
|
|
in_stasis = FALSE
|
|
if(status != FISH_DEAD)
|
|
START_PROCESSING(SSobj, src)
|
|
|
|
/obj/item/fish/proc/on_aquarium_insertion(obj/structure/aquarium)
|
|
if(isnull(last_feeding)) //Fish start fed.
|
|
last_feeding = world.time
|
|
RegisterSignal(aquarium, COMSIG_ATOM_EXITED, PROC_REF(aquarium_exited))
|
|
RegisterSignal(aquarium, COMSIG_PARENT_ATTACKBY, PROC_REF(attack_reaction))
|
|
|
|
/obj/item/fish/proc/aquarium_exited(datum/source, atom/movable/gone, direction)
|
|
SIGNAL_HANDLER
|
|
if(src != gone)
|
|
return
|
|
UnregisterSignal(source,list(COMSIG_ATOM_EXITED,COMSIG_PARENT_ATTACKBY))
|
|
|
|
/// Our aquarium is hit with stuff
|
|
/obj/item/fish/proc/attack_reaction(datum/source, obj/item/thing, mob/user, params)
|
|
SIGNAL_HANDLER
|
|
if(is_food(thing))
|
|
on_feeding(thing.reagents)
|
|
return COMPONENT_NO_AFTERATTACK
|
|
else
|
|
//stirred effect
|
|
SEND_SIGNAL(src, COMSIG_FISH_STIRRED)
|
|
|
|
/obj/item/fish/proc/is_food(obj/item/thing)
|
|
return istype(thing, /obj/item/fish_feed)
|
|
|
|
/obj/item/fish/proc/on_feeding(datum/reagents/feed_reagents)
|
|
if(feed_reagents.has_reagent(food))
|
|
last_feeding = world.time
|
|
|
|
/obj/item/fish/proc/check_environment_after_movement()
|
|
if(QDELETED(src)) //we don't care anymore
|
|
return
|
|
// Apply/remove stasis as needed
|
|
if(loc && HAS_TRAIT(loc, TRAIT_FISH_SAFE_STORAGE))
|
|
enter_stasis()
|
|
else if(in_stasis)
|
|
exit_stasis()
|
|
|
|
// Do additional stuff
|
|
var/in_aquarium = istype(loc,/obj/structure/aquarium)
|
|
if(in_aquarium)
|
|
on_aquarium_insertion(loc)
|
|
|
|
// Start flopping if outside of fish container
|
|
var/should_be_flopping = status == FISH_ALIVE && loc && !HAS_TRAIT(loc,TRAIT_FISH_SAFE_STORAGE) && !in_aquarium
|
|
|
|
if(should_be_flopping)
|
|
start_flopping()
|
|
else
|
|
stop_flopping()
|
|
|
|
/obj/item/fish/process(delta_time)
|
|
if(in_stasis || status != FISH_ALIVE)
|
|
return
|
|
|
|
process_health(delta_time)
|
|
if(ready_to_reproduce())
|
|
try_to_reproduce()
|
|
|
|
/obj/item/fish/proc/set_status(new_status)
|
|
switch(new_status)
|
|
if(FISH_ALIVE)
|
|
status = FISH_ALIVE
|
|
health = initial(health) // this is admin option anyway
|
|
START_PROCESSING(SSobj, src)
|
|
if(FISH_DEAD)
|
|
status = FISH_DEAD
|
|
STOP_PROCESSING(SSobj, src)
|
|
stop_flopping()
|
|
var/message = span_notice("\The [name] dies.")
|
|
if(istype(loc,/obj/structure/aquarium))
|
|
loc.visible_message(message)
|
|
else
|
|
visible_message(message)
|
|
SEND_SIGNAL(src, COMSIG_FISH_STATUS_CHANGED)
|
|
|
|
/obj/item/fish/proc/get_aquarium_animation()
|
|
var/obj/structure/aquarium/aquarium = loc
|
|
if(!istype(aquarium) || aquarium.fluid_type == AQUARIUM_FLUID_AIR || status == FISH_DEAD)
|
|
return AQUARIUM_ANIMATION_FISH_DEAD
|
|
else
|
|
return AQUARIUM_ANIMATION_FISH_SWIM
|
|
|
|
/// Checks if our current environment lets us live.
|
|
/obj/item/fish/proc/proper_environment()
|
|
var/obj/structure/aquarium/aquarium = loc
|
|
if(!istype(aquarium))
|
|
return FALSE
|
|
|
|
if(required_fluid_type != AQUARIUM_FLUID_ANADROMOUS)
|
|
if(aquarium.fluid_type != required_fluid_type)
|
|
return FALSE
|
|
else
|
|
if(aquarium.fluid_type != AQUARIUM_FLUID_SALTWATER && aquarium.fluid_type != AQUARIUM_FLUID_FRESHWATER)
|
|
return FALSE
|
|
if(aquarium.fluid_temp < required_temperature_min || aquarium.fluid_temp > required_temperature_max)
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/obj/item/fish/proc/process_health(delta_time)
|
|
var/health_change_per_second = 0
|
|
if(!proper_environment())
|
|
health_change_per_second -= 3 //Dying here
|
|
if(world.time - last_feeding >= feeding_frequency)
|
|
health_change_per_second -= 0.5 //Starving
|
|
else
|
|
health_change_per_second += 0.5 //Slowly healing
|
|
adjust_health(health + health_change_per_second * delta_time)
|
|
|
|
/obj/item/fish/proc/adjust_health(amt)
|
|
health = clamp(amt, 0, initial(health))
|
|
if(health <= 0)
|
|
set_status(FISH_DEAD)
|
|
|
|
|
|
/obj/item/fish/proc/ready_to_reproduce()
|
|
var/obj/structure/aquarium/aquarium = loc
|
|
if(!istype(aquarium))
|
|
return FALSE
|
|
return aquarium.allow_breeding && health == initial(health) && stable_population > 1 && world.time - last_breeding >= breeding_timeout
|
|
|
|
//Fish breeding stops if fish count exceeds this.
|
|
#define AQUARIUM_MAX_BREEDING_POPULATION 20
|
|
/obj/item/fish/proc/try_to_reproduce()
|
|
var/obj/structure/aquarium/aquarium = loc
|
|
if(!istype(aquarium))
|
|
return
|
|
if(length(aquarium.tracked_fish) >= AQUARIUM_MAX_BREEDING_POPULATION) //so aquariums full of fish don't need to do these expensive checks
|
|
return
|
|
var/list/other_fish_of_same_type = list()
|
|
for(var/obj/item/fish/fish_in_aquarium in aquarium)
|
|
if(fish_in_aquarium == src || fish_in_aquarium.type != type)
|
|
continue
|
|
other_fish_of_same_type += fish_in_aquarium
|
|
if(length(other_fish_of_same_type) >= stable_population)
|
|
return
|
|
var/obj/item/fish/second_fish
|
|
for(var/obj/item/fish/other_fish in other_fish_of_same_type)
|
|
if(other_fish.ready_to_reproduce())
|
|
second_fish = other_fish
|
|
break
|
|
if(second_fish)
|
|
new type(loc) //could use child_type var
|
|
last_breeding = world.time
|
|
second_fish.last_breeding = world.time
|
|
#undef AQUARIUM_MAX_BREEDING_POPULATION
|
|
|
|
#define PAUSE_BETWEEN_PHASES 15
|
|
#define PAUSE_BETWEEN_FLOPS 2
|
|
#define FLOP_COUNT 2
|
|
#define FLOP_DEGREE 20
|
|
#define FLOP_SINGLE_MOVE_TIME 1.5
|
|
#define JUMP_X_DISTANCE 5
|
|
#define JUMP_Y_DISTANCE 6
|
|
/// This animation should be applied to actual parent atom instead of vc_object.
|
|
/proc/flop_animation(atom/movable/animation_target)
|
|
var/pause_between = PAUSE_BETWEEN_PHASES + rand(1, 5) //randomized a bit so fish are not in sync
|
|
animate(animation_target, time = pause_between, loop = -1)
|
|
//move nose down and up
|
|
for(var/_ in 1 to FLOP_COUNT)
|
|
var/matrix/up_matrix = matrix()
|
|
up_matrix.Turn(FLOP_DEGREE)
|
|
var/matrix/down_matrix = matrix()
|
|
down_matrix.Turn(-FLOP_DEGREE)
|
|
animate(transform = down_matrix, time = FLOP_SINGLE_MOVE_TIME, loop = -1)
|
|
animate(transform = up_matrix, time = FLOP_SINGLE_MOVE_TIME, loop = -1)
|
|
animate(transform = matrix(), time = FLOP_SINGLE_MOVE_TIME, loop = -1, easing = BOUNCE_EASING | EASE_IN)
|
|
animate(time = PAUSE_BETWEEN_FLOPS, loop = -1)
|
|
//bounce up and down
|
|
animate(time = pause_between, loop = -1, flags = ANIMATION_PARALLEL)
|
|
var/jumping_right = FALSE
|
|
var/up_time = 3 * FLOP_SINGLE_MOVE_TIME / 2
|
|
for(var/_ in 1 to FLOP_COUNT)
|
|
jumping_right = !jumping_right
|
|
var/x_step = jumping_right ? JUMP_X_DISTANCE/2 : -JUMP_X_DISTANCE/2
|
|
animate(time = up_time, pixel_y = JUMP_Y_DISTANCE , pixel_x=x_step, loop = -1, flags= ANIMATION_RELATIVE, easing = BOUNCE_EASING | EASE_IN)
|
|
animate(time = up_time, pixel_y = -JUMP_Y_DISTANCE, pixel_x=x_step, loop = -1, flags= ANIMATION_RELATIVE, easing = BOUNCE_EASING | EASE_OUT)
|
|
animate(time = PAUSE_BETWEEN_FLOPS, loop = -1)
|
|
#undef PAUSE_BETWEEN_PHASES
|
|
#undef PAUSE_BETWEEN_FLOPS
|
|
#undef FLOP_COUNT
|
|
#undef FLOP_DEGREE
|
|
#undef FLOP_SINGLE_MOVE_TIME
|
|
#undef JUMP_X_DISTANCE
|
|
#undef JUMP_Y_DISTANCE
|
|
|
|
/// Starts flopping animation
|
|
/obj/item/fish/proc/start_flopping()
|
|
if(!flopping) //Requires update_transform/animate_wrappers to be less restrictive.
|
|
flopping = TRUE
|
|
flop_animation(src)
|
|
|
|
/// Stops flopping animation
|
|
/obj/item/fish/proc/stop_flopping()
|
|
if(flopping)
|
|
flopping = FALSE
|
|
animate(src, transform = matrix()) //stop animation
|
|
|
|
/// Refreshes flopping animation after temporary animation finishes
|
|
/obj/item/fish/proc/on_temp_animation(datum/source, animation_duration)
|
|
if(animation_duration > 0)
|
|
addtimer(CALLBACK(src, PROC_REF(refresh_flopping)), animation_duration)
|
|
|
|
/obj/item/fish/proc/refresh_flopping()
|
|
if(flopping)
|
|
flop_animation(src)
|
|
|
|
/// Returns random fish, using random_case_rarity probabilities.
|
|
/proc/random_fish_type(case_fish_only=TRUE, required_fluid)
|
|
var/static/probability_table
|
|
var/argkey = "fish_[required_fluid]_[case_fish_only]" //If this expands more extract bespoke element arg generation to some common helper.
|
|
if(!probability_table || !probability_table[argkey])
|
|
if(!probability_table)
|
|
probability_table = list()
|
|
var/chance_table = list()
|
|
for(var/_fish_type in subtypesof(/obj/item/fish))
|
|
var/obj/item/fish/fish = _fish_type
|
|
if(required_fluid && initial(fish.required_fluid_type) != required_fluid)
|
|
continue
|
|
if(initial(fish.available_in_random_cases) || !case_fish_only)
|
|
chance_table[fish] = initial(fish.random_case_rarity)
|
|
probability_table[argkey] = chance_table
|
|
return pick_weight(probability_table[argkey])
|
|
|
|
|