diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm
index 97dfe678bc1..c8cf8373e36 100644
--- a/code/modules/mob/living/simple_animal/friendly/mouse.dm
+++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm
@@ -30,6 +30,7 @@
universal_understand = 1
mob_size = 1
holder_type = /obj/item/weapon/holder/mouse
+ digest_factor = 0.05
/mob/living/simple_animal/mouse/Life()
..()
@@ -75,6 +76,8 @@
holder_type = /obj/item/weapon/holder/mouse/white
+
+
/mob/living/simple_animal/mouse/attack_hand(mob/living/carbon/human/M as mob)
if (src.stat == DEAD)//If the mouse is dead, we don't pet it, we just pickup the corpse on click
get_scooped(M)
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index 59e2e256910..f2b76e665cf 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -66,9 +66,29 @@
var/supernatural = 0
var/purge = 0
+ var/max_nutrition = 75
+ var/nutrition_step = 0.2 //nutrition lost per tick and per step, calculated from mob_size, 0.4 is a fallback
+ var/bite_factor = 0.4
+ var/digest_factor = 0.2 //A multiplier on how quickly reagents are digested
+
/mob/living/simple_animal/New()
..()
verbs -= /mob/verb/observe
+ if (mob_size)
+ nutrition_step = mob_size * 0.05
+ bite_factor = mob_size * 0.3
+ max_nutrition *= 1 + (nutrition_step*3)//Max nutrition scales faster than costs, so bigger creatures eat less often
+ reagents = new/datum/reagents(5*mob_size, src)
+ else
+ reagents = new/datum/reagents(20, src)
+ nutrition = max_nutrition
+
+/mob/living/simple_animal/Move(NewLoc, direct)
+ . = ..()
+ if(.)
+ if(src.nutrition && src.stat != DEAD)
+ src.nutrition -= nutrition_step
+
/mob/living/simple_animal/Login()
if(src && src.client)
@@ -78,6 +98,15 @@
/mob/living/simple_animal/updatehealth()
return
+/mob/living/simple_animal/examine(mob/user)
+ ..()
+ if (!nutrition)
+ user << "It looks starving!"
+ else if (nutrition < max_nutrition *0.5)
+ user << "It looks hungry."
+ else if ((reagents.total_volume > 0 && nutrition > max_nutrition *0.75) || nutrition > max_nutrition *0.9)
+ user << "It looks full and contented."
+
/mob/living/simple_animal/Life()
..()
@@ -104,7 +133,7 @@
handle_paralysed()
update_canmove()
handle_supernatural()
-
+ process_food()
//Movement
if(!client && !stop_automated_movement && wander && !anchored)
if(isturf(src.loc) && !resting && !buckled && canmove) //This is so it only moves if it's not inside a closet, gentics machine, etc.
@@ -210,6 +239,40 @@
if(purge)
purge -= 1
+
+
+//Simple reagent processing for simple animals
+//This allows animals to digest food, and only food
+//Most drugs, poisons etc, are designed to work on carbons and affect many values a simple animal doesnt have
+/mob/living/simple_animal/proc/process_food()
+
+ if (nutrition)
+ nutrition -= nutrition_step//Bigger animals get hungry faster
+ nutrition = max(0,min(nutrition, max_nutrition))//clamp the value
+ else
+ if (prob(3))
+ src << "You feel hungry..."
+
+
+ if (!reagents || !reagents.total_volume)
+ return
+
+ for(var/datum/reagent/current in reagents.reagent_list)
+ var/removed = min(current.metabolism*digest_factor, current.volume)
+ if (istype(current, /datum/reagent/nutriment))//If its food, it feeds us
+ var/datum/reagent/nutriment/N = current
+ nutrition += removed*N.nutriment_factor
+ current.remove_self(removed)//If its not food, it just does nothing. no fancy effects
+
+/mob/living/simple_animal/proc/can_eat()
+ if (nutrition > max_nutrition * 0.9)
+ return 0//full
+
+ else if (nutrition > max_nutrition * 0.8)
+ return 1//content
+
+ else return 2//hungry
+
/mob/living/simple_animal/gib()
..(icon_gib,1)
@@ -284,7 +347,10 @@
M.show_message("[user] applies the [MED] on [src].")
else
user << "\The [src] is dead, medical items won't bring \him back to life."
- if(meat_type && (stat == DEAD)) //if the animal has a meat, and if it is dead.
+ else if(istype(O, /obj/item/weapon/reagent_containers))
+ ..()
+
+ else if(meat_type && (stat == DEAD)) //if the animal has a meat, and if it is dead.
if(istype(O, /obj/item/weapon/material/knife) || istype(O, /obj/item/weapon/material/knife/butch))
harvest(user)
else
@@ -320,6 +386,9 @@
tally = 1
tally *= purge
+ if (!nutrition)
+ tally += 4
+
return tally+config.animal_delay
/mob/living/simple_animal/Stat()
diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm
index 85d7191048b..f8aa92964f4 100644
--- a/code/modules/reagents/Chemistry-Holder.dm
+++ b/code/modules/reagents/Chemistry-Holder.dm
@@ -391,9 +391,13 @@
var/datum/reagents/R = C.touching
return trans_to_holder(R, amount, multiplier, copy)
else
- var/datum/reagents/R = new /datum/reagents(amount)
- . = trans_to_holder(R, amount, multiplier, copy)
- R.touch_mob(target)
+ //If the target has a reagent holder, we'll try to put it there instead. This allows feeding simple animals
+ if(target.reagents && type == CHEM_BLOOD || type == CHEM_INGEST)
+ return trans_to_holder(target.reagents, amount, multiplier, copy)
+ else
+ var/datum/reagents/R = new /datum/reagents(amount)
+ . = trans_to_holder(R, amount, multiplier, copy)
+ R.touch_mob(target)
/datum/reagents/proc/trans_to_turf(var/turf/target, var/amount = 1, var/multiplier = 1, var/copy = 0) // Turfs don't have any reagents (at least, for now). Just touch it.
if(!target || !target.simulated)
diff --git a/code/modules/reagents/reagent_containers/food/snacks.dm b/code/modules/reagents/reagent_containers/food/snacks.dm
index 950b17a13ee..c4c7bd93d53 100644
--- a/code/modules/reagents/reagent_containers/food/snacks.dm
+++ b/code/modules/reagents/reagent_containers/food/snacks.dm
@@ -108,6 +108,32 @@
On_Consume(M)
return 1
+ else if (isanimal(M))
+ var/mob/living/simple_animal/SA = M
+ var/m_bitesize = bitesize * SA.bite_factor//Modified bitesize based on creature size
+ var/amount_eaten = m_bitesize
+
+ if(reagents && SA.reagents)
+ m_bitesize = min(m_bitesize, reagents.total_volume)
+ //If the creature can't even stomach half a bite, then it eats nothing
+ if (!SA.can_eat() || ((user.reagents.maximum_volume - user.reagents.total_volume) < m_bitesize * 0.5))
+ amount_eaten = 0
+ else
+ amount_eaten = reagents.trans_to_mob(SA, m_bitesize, CHEM_INGEST)
+ else
+ return 0//The target creature can't eat
+
+ if (amount_eaten)
+ bitecount++
+ if (amount_eaten >= m_bitesize)
+ user.visible_message("[user] feeds [M] [src].")
+ else
+ user.visible_message("[user] feeds [M] a tiny bit of [src]. It looks full.")
+ On_Consume(M)
+ return 1
+ else
+ user << "[M.name] can't stomach anymore food!"
+
return 0
/obj/item/weapon/reagent_containers/food/snacks/examine(mob/user)
@@ -206,10 +232,34 @@
/obj/item/weapon/reagent_containers/food/snacks/attack_generic(var/mob/living/user)
if(!isanimal(user) && !isalien(user))
return
- user.visible_message("[user] nibbles away at \the [src].","You nibble away at \the [src].")
- bitecount++
+
+ var/amount_eaten = bitesize
+ var/m_bitesize = bitesize
+
+ if (isanimal(user))
+ var/mob/living/simple_animal/SA = user
+ m_bitesize = bitesize * SA.bite_factor//Modified bitesize based on creature size
+ amount_eaten = m_bitesize
+ if (!SA.can_eat())
+ user << "You're too full to eat anymore!"
+ return
+
if(reagents && user.reagents)
- reagents.trans_to_mob(user, bitesize, CHEM_INGEST)
+ m_bitesize = min(m_bitesize, reagents.total_volume)
+ //If the creature can't even stomach half a bite, then it eats nothing
+ if (((user.reagents.maximum_volume - user.reagents.total_volume) < m_bitesize * 0.5))
+ amount_eaten = 0
+ else
+ amount_eaten = reagents.trans_to_mob(user, m_bitesize, CHEM_INGEST)
+ if (amount_eaten)
+ bitecount++
+ if (amount_eaten < m_bitesize)
+ user.visible_message("[user] reluctantly nibbles a tiny part of \the [src].","You reluctantly nibble a tiny part of \the [src]. You can't stomach much more!.")
+ else
+ user.visible_message("[user] nibbles away at \the [src].","You nibble away at \the [src].")
+ else
+ user << "You're too full to eat anymore!"
+
spawn(5)
if(!src && !user.client)
user.custom_emote(1,"[pick("burps", "cries for more", "burps twice", "looks at the area where the food was")]")
diff --git a/html/changelogs/Nanako-AnimalFeeding.yml b/html/changelogs/Nanako-AnimalFeeding.yml
new file mode 100644
index 00000000000..4c444d2b653
--- /dev/null
+++ b/html/changelogs/Nanako-AnimalFeeding.yml
@@ -0,0 +1,40 @@
+################################
+# Example Changelog File
+#
+# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
+#
+# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
+# When it is, any changes listed below will disappear.
+#
+# Valid Prefixes:
+# bugfix
+# wip (For works in progress)
+# tweak
+# soundadd
+# sounddel
+# rscadd (general adding of nice things)
+# rscdel (general deleting of nice things)
+# imageadd
+# imagedel
+# maptweak
+# spellcheck (typo fixes)
+# experiment
+#################################
+
+# Your name.
+author: Nanako
+
+# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
+delete-after: True
+
+# Any changes you've made. See valid prefix list above.
+# INDENT WITH TWO SPACES. NOT TABS. SPACES.
+# SCREW THIS UP AND IT WON'T WORK.
+# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
+# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
+changes:
+ - rscadd: "Added hunger and feeding system for simple animals, this includes cats, dogs, mice, lizards, chickens, cows, etc"
+ - rscadd: "Animals can now actually consume food instead of nibbling them eternally."
+ - rscadd: "Animals can now be hand-fed by using food on them."
+ - rscadd: "Animals will move more slowly when starving. Examining an animal will show if its hungry."
+
\ No newline at end of file