Files
Bubberstation/code/modules/fishing/fish/_fish.dm
SkyratBot 0ca2c0b527 [MIRROR] Gives bread and cake slice_types and adds screentip verbs to proccessed foods [MDB IGNORE] (#17721)
* 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>
2022-11-28 17:16:01 +00:00

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])