Refactors Fish

Cleans up fish code to not be (as) reliant on some ugly associated lists
and strings.

Tweaks fish breeding:
- Breeding now only requires an additional 0.2 food level, instead of
the previous 2.0 food level
- Fish may refuse to cross-breed with other fish, resulting in dud eggs
unless another member of their species is present. Currently, shrimp and
eels refuse to breed with other types of fish.
- When breeding, fish will attempt to pick a partner and base their
success off the pair
- If the first parent will not cross-breed, they will successfully lay
an egg of their type if at least one other fish of their species is
present, otherwise they will always lay a dud
- If the first parent will cross-breed but the second parent will not,
the egg will always be a dud
- If both fish are the same type of cross-breeders, there is a 90%
chance of them successfully laying an egg of their type, otherwise the
result is a dud egg
- If the fish are different types of cross-breeders, there is a 30%
chance of them laying a dud egg, otherwise they will lay an egg of the
same type as one of the parents

Code refactor includes making adding more fish with their own special
interactions with the tank (like the catfish and feeder fish
interactions) cleaner and hopefully easier.
- New "fish" are on the way, hopefully coming soon once sprites are
available.
- Fish mutating will be added at such time to accomodate the obtaining
of new and exciting sea-life.

🆑
tweak: Fish breeding has been tweaked to support fish refusing to
cross-breed and improved egg chances with same-species parents.
/🆑
This commit is contained in:
FalseIncarnate
2017-03-06 04:16:09 -05:00
parent 110bdad1d3
commit b5cfca5ea8
5 changed files with 166 additions and 103 deletions
+13 -24
View File
@@ -5,81 +5,70 @@
icon = 'icons/obj/fish_items.dmi'
icon_state = "eggs"
w_class = 2
var/fish_type = null //Holds the name of the fish that the egg is for
var/datum/fish/fish_type = null //Holds the datum of the fish that the egg is for, null means dud eggs
/obj/item/fish_eggs/New()
..()
var/global/list/fish_eggs_list = list("dud" = /obj/item/fish_eggs,
"goldfish" = /obj/item/fish_eggs/goldfish,
"clownfish" = /obj/item/fish_eggs/clownfish,
"shark" = /obj/item/fish_eggs/shark,
"baby space carp" = /obj/item/fish_eggs/babycarp,
"catfish" = /obj/item/fish_eggs/catfish,
"feederfish" = /obj/item/fish_eggs/feederfish,
"salmon" = /obj/item/fish_eggs/salmon,
"shrimp" = /obj/item/fish_eggs/shrimp,
"electric eel" = /obj/item/fish_eggs/electric_eel,
"glofish" = /obj/item/fish_eggs/glofish,
)
if(fish_type)
fish_type = new fish_type
/obj/item/fish_eggs/goldfish
name = "goldfish eggs"
desc = "Goldfish eggs, surprisingly, don't contain actual gold."
icon_state = "gold_eggs"
fish_type = "goldfish"
fish_type = /datum/fish/goldfish
/obj/item/fish_eggs/clownfish
name = "clownfish eggs"
desc = "Even as eggs, they are funnier than the clown. HONK!"
icon_state = "clown_eggs"
fish_type = "clownfish"
fish_type = /datum/fish/clownfish
/obj/item/fish_eggs/shark
name = "shark eggs"
desc = "We're gonna need a- Oh wait, they're still eggs."
icon_state = "shark_eggs"
fish_type = "shark"
fish_type = /datum/fish/shark
/obj/item/fish_eggs/babycarp
name = "baby space carp eggs"
desc = "Eggs from the substantially smaller form of the intergalactic terror."
icon_state = "babycarp_eggs"
fish_type = "baby space carp"
fish_type = /datum/fish/babycarp
/obj/item/fish_eggs/catfish
name = "catfish eggs"
desc = "A bottom-feeding species noted for their similarity to cats and Tajaran."
icon_state = "catfish_eggs"
fish_type = "catfish"
fish_type = /datum/fish/catfish
/obj/item/fish_eggs/feederfish
name = "feeder fish eggs"
desc = "These generic fish are commonly found being eaten by larger fish."
icon_state = "feederfish_eggs"
fish_type = "feederfish"
fish_type = /datum/fish/feederfish
/obj/item/fish_eggs/salmon
name = "salmon eggs"
desc = "A collection of salmon eggs."
icon_state = "salmon_eggs"
fish_type = "salmon"
fish_type = /datum/fish/salmon
/obj/item/fish_eggs/shrimp
name = "shrimp eggs"
desc = "Eggs for shrimp. You figured they'd be smaller though..."
icon_state = "shrimp_eggs"
fish_type = "shrimp"
fish_type = /datum/fish/shrimp
/obj/item/fish_eggs/electric_eel
name = "electric eel eggs"
desc = "A pile of eggs for a slimy, shocking, sea-serpent."
icon_state = "electric_eel_eggs"
fish_type = "electric eel"
fish_type = /datum/fish/electric_eel
/obj/item/fish_eggs/glofish
name = "glofish eggs"
desc = "A cluster of bright neon eggs belonging to a bio-luminescent species of fish."
icon_state = "glofish_eggs"
fish_type = "glofish"
fish_type = /datum/fish/glofish
-13
View File
@@ -1,17 +1,4 @@
var/global/list/fish_items_list = list("goldfish" = /obj/item/weapon/fish/goldfish,
"clownfish" = /obj/item/weapon/grown/bananapeel/clownfish,
"shark" = /obj/item/weapon/fish/shark,
"baby space carp" = /obj/item/weapon/fish/babycarp,
"catfish" = /obj/item/weapon/fish/catfish,
"feederfish" = /obj/item/weapon/reagent_containers/food/snacks/feederfish,
"salmon" = /obj/item/weapon/fish/salmon,
"shrimp" = /obj/item/weapon/reagent_containers/food/snacks/shrimp,
"electric eel" = /obj/item/weapon/fish/electric_eel,
"glofish" = /obj/item/weapon/fish/glofish
,
)
//////////////////////////////////////////////
// Aquarium Supplies //
//////////////////////////////////////////////
+76
View File
@@ -0,0 +1,76 @@
/datum/fish
var/fish_name = "generic fish"
var/egg_item = /obj/item/fish_eggs
var/fish_item = /obj/item/weapon/fish
var/crossbreeder = 1 //determines if the fish will attempt to breed with other types, set to 0 if you want the fish to only lay eggs with its own species
/datum/fish/proc/special_interact(obj/machinery/fishtank/my_tank)
return
/datum/fish/goldfish
fish_name = "goldfish"
egg_item = /obj/item/fish_eggs/goldfish
fish_item = /obj/item/weapon/fish/goldfish
/datum/fish/glofish
fish_name = "glofish"
egg_item = /obj/item/fish_eggs/glofish
fish_item = /obj/item/weapon/fish/glofish
/datum/fish/clownfish
fish_name = "clownfish"
egg_item = /obj/item/fish_eggs/clownfish
fish_item = /obj/item/weapon/grown/bananapeel/clownfish
/datum/fish/shark
fish_name = "shark"
egg_item = /obj/item/fish_eggs/shark
fish_item = /obj/item/weapon/fish/shark
/datum/fish/babycarp
fish_name = "baby space carp"
egg_item = /obj/item/fish_eggs/babycarp
fish_item = /obj/item/weapon/fish/babycarp
/datum/fish/catfish
fish_name = "catfish"
egg_item = /obj/item/fish_eggs/catfish
fish_item = /obj/item/weapon/fish/catfish
/datum/fish/catfish/special_interact(obj/machinery/fishtank/my_tank)
if(!my_tank || !istype(my_tank))
return
if(my_tank.filth_level > 0 && prob(33))
my_tank.filth_level -= 0.1
/datum/fish/salmon
fish_name = "salmon"
egg_item = /obj/item/fish_eggs/salmon
fish_item = /obj/item/weapon/fish/salmon
/datum/fish/shrimp
fish_name = "shrimp"
egg_item = /obj/item/fish_eggs/shrimp
fish_item = /obj/item/weapon/reagent_containers/food/snacks/shrimp
crossbreeder = 0
/datum/fish/feederfish
fish_name = "feeder fish"
egg_item = /obj/item/fish_eggs/feederfish
fish_item = /obj/item/weapon/reagent_containers/food/snacks/feederfish
/datum/fish/feederfish/special_interact(obj/machinery/fishtank/my_tank)
if(!my_tank || !istype(my_tank))
return
if(my_tank.fish_count < 2)
return
if(my_tank.food_level <= 5 && prob(25))
my_tank.food_level += 1
my_tank.kill_fish(src)
/datum/fish/electric_eel
fish_name = "electric eel"
egg_item = /obj/item/fish_eggs/electric_eel
fish_item = /obj/item/weapon/fish/electric_eel
crossbreeder = 0
+76 -66
View File
@@ -112,7 +112,7 @@
if(light_switch)
set_light(2,2,"#a0a080")
else
set_light(0)
adjust_light()
//////////////////////////////
// NEW() PROCS //
@@ -176,6 +176,7 @@
/obj/machinery/fishtank/process()
//Start by counting fish in the tank
fish_count = 0
var/ate_food = 0
for(var/fish in fish_list)
if(fish)
fish_count++
@@ -194,15 +195,14 @@
//Check breeding conditions
if(fish_count >=2 && egg_count < max_fish) //Need at least 2 fish to breed, but won't breed if there are as many eggs as max_fish
if(food_level > 2 && filth_level <=5) //Breeding is going to use extra food, and the filth_level shouldn't be too high
if(food_level >= 0.2 && filth_level <=5) //Breeding is going to use extra food, and the filth_level shouldn't be too high
if(prob(((fish_count - 2) * 5)+10)) //Chances increase with each additional fish, 10% base + 5% per additional fish
egg_count++ //A new set of eggs were laid, increase egg_count
egg_list.Add(select_egg_type()) //Add the new egg to the egg_list for storage
food_level -= 2 //Remove extra food for the breeding process
breed_fish()
food_level -= 0.2 //Remove extra food for the breeding process
ate_food = 1
//Handle standard food and filth adjustments
check_food_level()
var/ate_food = 0
if(food_level > 0 && prob(50)) //Chance for the fish to eat some food
if(food_level >= (fish_count * 0.1)) //If there is at least enough food to go around, feed all the fish
food_level -= fish_count * 0.1
@@ -238,44 +238,35 @@
//////////////////////////////
/obj/machinery/fishtank/proc/handle_special_interactions()
var/glo_light = 0
for(var/fish in fish_list)
switch(fish)
if("catfish") //Catfish have a small chance of cleaning some filth since they are a bottom-feeder
if((filth_level > 0) && prob(30))
filth_level -= 0.1
if("feederfish") //Feeder fish have a small chance of sacrificing themselves to produce some food
if(fish_count < 2) //Don't sacrifice the last fish, there's nothing to eat it
continue
if(food_level <= 5 && prob(25))
kill_fish("feederfish") //Kill the fish to reflect it's sacrifice, but don't increase the filth_level
food_level += 1 //The corpse became food for the other fish, ecology at it's finest
if("glofish")
glo_light++
if(!light_switch && (glo_light > 0))
set_light(2,glo_light,"#99FF66")
for(var/datum/fish/fish in fish_list)
fish.special_interact(src)
check_food_level()
check_filth_level()
check_water_level()
adjust_light()
/obj/machinery/fishtank/proc/adjust_light()
if(light_switch) //tank light overrides fish lights
return
else
var/glo_light = 0
for(var/datum/fish/fish in fish_list)
if(istype(fish, /datum/fish/glofish))
glo_light ++
if(glo_light)
set_light(2, glo_light, "#99FF66")
else
set_light(0, 0)
/obj/machinery/fishtank/proc/check_water_level()
if(water_level < 0) //Water_level cannot be negative, set it to 0 if it is
water_level = 0
if(water_level > water_capacity) //Water_level cannot exceed capacity, set it to capacity if it does
water_level = water_capacity
water_level = min(water_capacity, max(0, water_level))
update_icon()
/obj/machinery/fishtank/proc/check_filth_level()
if(filth_level < 0) //Filth_level cannot be negative, set it to 0 if it is
filth_level = 0
if(filth_level > 10) //Filth_level cannot exceed 10 (cap), set it to 10 if it does
filth_level = 10
filth_level = min(10, max(0, filth_level))
/obj/machinery/fishtank/proc/check_food_level()
if(food_level < 0) //Food_level cannot be negative, set it to 0 if it is
food_level = 0
if(food_level > 10) //Food_level cannot exceed 10 (cap), set it to 10 if it does
food_level = 10
food_level = min(10, max(0, food_level))
/obj/machinery/fishtank/proc/check_health()
//Max value check
@@ -292,37 +283,26 @@
if(cur_health <= 0) //The tank is broken, destroy it
destroy()
/obj/machinery/fishtank/proc/kill_fish(var/type = null)
/obj/machinery/fishtank/proc/kill_fish(datum/fish/fish_type = null)
//Check if we were passed a fish to kill, otherwise kill a random one
if(type)
fish_list.Remove(type) //Kill a fish of the specified type
fish_count -- //Lower fish_count to reflect the death of a fish, so the everything else works fine
else
fish_list.Remove(pick(fish_list)) //Kill a random fish
fish_count -- //Lower fish_count to reflect the death of a fish, so the everything else works fine
if(!fish_type)
fish_type = pick(fish_list)
fish_list.Remove(fish_type) //Kill a fish of the specified type
fish_count -- //Lower fish_count to reflect the death of a fish, so the everything else works fine
if(istype(type, /datum/fish/glofish))
adjust_light()
/obj/machinery/fishtank/proc/add_fish(var/type = null)
/obj/machinery/fishtank/proc/add_fish(datum/fish/fish_type = null)
//Check if we were passed a fish type
if(type)
fish_list.Add("[type]") //Add a fish of the specified type
if(fish_type)
fish_list.Add(fish_type) //Add a fish of the specified type
fish_count++ //Increase fish_count to reflect the introduction of a fish, so the everything else works fine
//Announce the new fish
src.visible_message("A new [type] has hatched in \the [src]!")
src.visible_message("A new [fish_type.fish_name] has hatched in \the [src]!")
//Null type fish are dud eggs, give a message to inform the player
else
to_chat(usr, "The eggs disolve in the water. They were duds!")
/obj/machinery/fishtank/proc/select_egg_type()
var/fish = pick(fish_list) //Select a fish from the fish in the tank
if(prob(25)) //25% chance to be a dud (blank) egg
fish = "dud"
var/obj/item/fish_eggs/egg_path = null //Create empty variable to receive the egg_path
egg_path = fish_eggs_list[fish] //Locate the corresponding path from fish_eggs_list that matches the fish
if(!egg_path) //The fish wasn't located in the fish_eggs_list, potentially due to a typo, so return a dud egg
return /obj/item/fish_eggs
else //The fish was located in the fish_eggs_list, so return the proper egg
return egg_path
/obj/machinery/fishtank/proc/harvest_eggs(var/mob/user)
if(!egg_count) //Can't harvest non-existant eggs
return
@@ -342,17 +322,17 @@
if(!fish_count) //Can't catch non-existant fish!
to_chat(usr, "There are no fish in \the [src] to catch!")
return
var/caught_fish
caught_fish = input("Select a fish to catch.", "Fishing", caught_fish) in fish_list //Select a fish from the tank
var/list/fish_names_list = list()
for(var/datum/fish/fish_type in fish_list)
fish_names_list += list("[fish_type.fish_name]" = fish_type)
var/caught_fish = input("Select a fish to catch.", "Fishing") as null|anything in fish_names_list //Select a fish from the tank
if(caught_fish)
var/dead_fish = null
dead_fish = fish_items_list[caught_fish] //Locate the appropriate fish_item for the caught fish
if(!dead_fish) //No fish_item found, possibly due to typo or not being listed. Do nothing.
return
kill_fish(caught_fish) //Kill the caught fish from the tank
user.visible_message("[user.name] harvests \a [caught_fish] from \the [src].", "You scoop \a [caught_fish] out of \the [src].")
new dead_fish(get_turf(user)) //Spawn the appropriate fish_item at the user's feet.
var/datum/fish/fish_type = fish_names_list[caught_fish]
var/fish_item = fish_type.fish_item
if(fish_item)
new fish_item(get_turf(user)) //Spawn the appropriate fish_item at the user's feet.
kill_fish(fish_type) //Kill the caught fish from the tank
/obj/machinery/fishtank/proc/destroy(var/deconstruct = 0)
var/turf/T = get_turf(src) //Store the tank's turf for atmos updating after deletion of tank
@@ -392,6 +372,34 @@
var/turf/simulated/S = T
S.MakeSlippery()
/obj/machinery/fishtank/proc/breed_fish()
var/list/breed_candidates = fish_list.Copy()
var/datum/fish/parent1 = pick_n_take(breed_candidates)
var/datum/fish/parent2 = null
if(!parent1.crossbreeder) //fish with crossbreed = 0 will only breed with their own species, and only leave duds if they can't breed
if(parent1 in breed_candidates)
egg_list.Add(parent1.egg_item)
else
egg_list.Add(/obj/item/fish_eggs)
else
parent2 = pick(breed_candidates)
if(!parent2.crossbreeder) //second fish refuses to crossbreed, spawn a dud
egg_list.Add(/obj/item/fish_eggs)
else if(parent1 == parent2) //both fish are the same type
if(prob(90)) //90% chance to get that type of egg
egg_list.Add(parent1.egg_item)
else //10% chance to get a dud
egg_list.Add(/obj/item/fish_eggs)
else //different types of fish
if(prob(30)) //30% chance to get dud
egg_list.Add(/obj/item/fish_eggs)
else
if(prob(50)) //chance to get egg for either parent type (50/50 for either parent, 35% overall each)
egg_list.Add(parent1.egg_item)
else
egg_list.Add(parent2.egg_item)
egg_count++
////////////////////////////// Note from FalseIncarnate:
// EXAMINE PROC // This proc is massive, messy, and probably could be handled better.
////////////////////////////// Feel free to try cleaning it up if you think of a better way to do it.
@@ -464,9 +472,11 @@
var/fish_num = fish_count
var/message = "You spot "
while(fish_num > 0)
var/datum/fish/fish_type = fish_list[fish_num]
var/fish_name = fish_type.fish_name
if(fish_count > 1 && fish_num == 1) //If there were at least 2 fish, and this is the last one, add "and" to the message
message += "and "
message += "a [fish_list[fish_num]]"
message += "\an [fish_name]"
fish_num --
if(fish_num > 0) //There's more fish, add a comma to the message
message +=", "
+1
View File
@@ -1325,6 +1325,7 @@
#include "code\modules\ext_scripts\python.dm"
#include "code\modules\fish\fish_eggs.dm"
#include "code\modules\fish\fish_items.dm"
#include "code\modules\fish\fish_types.dm"
#include "code\modules\fish\fishtank.dm"
#include "code\modules\flufftext\Dreaming.dm"
#include "code\modules\flufftext\Hallucination.dm"