diff --git a/code/modules/mob/holder.dm b/code/modules/mob/holder.dm
index fcc27fcb8d2..94c20f97016 100644
--- a/code/modules/mob/holder.dm
+++ b/code/modules/mob/holder.dm
@@ -12,6 +12,9 @@
var/name_dead
var/isalive
+ var/last_loc_general//This stores a general location of the object. Ie, a container or a mob
+ var/last_loc_specific//This stores specific extra information about the location, pocket, hand, worn on head, etc. Only relevant to mobs
+
/obj/item/weapon/holder/New()
if (!item_state)
item_state = icon_state
@@ -30,18 +33,18 @@
return
- for(var/mob/M in contents)
-
- var/atom/movable/mob_container
- mob_container = M
- mob_container.forceMove(src.loc)//if the holder was placed into a disposal, this should place the animal in the disposal
- M.reset_view()
+ for(var/mob/M in contents)
+ var/atom/movable/mob_container
+ mob_container = M
+ mob_container.forceMove(src.loc)//if the holder was placed into a disposal, this should place the animal in the disposal
+ M.reset_view()
var/mob/L = get_holding_mob()
if (L)
L.drop_from_inventory(src)
- qdel(src)
+ qdel(src)
+ return
if (isalive && contained.stat == DEAD)
held_death(1)//If we get here, it means the mob died sometime after we picked it up. We pass in 1 so that we can play its deathmessage
@@ -61,6 +64,33 @@
for(var/mob/M in src.contents)
M.attackby(W,user)
+/obj/item/weapon/holder/dropped(mob/user)
+
+ ///When an object is put into a container, drop fires twice.
+ //once with it on the floor, and then once in the container
+ //This conditional allows us to ignore that first one. Handling of mobs dropped on the floor is done in process
+ if (istype(loc, /turf))
+ return
+
+ if (istype(loc, /obj/item/weapon/storage)) //The second drop reads the container its placed into as the location
+ update_location()
+
+
+/obj/item/weapon/holder/equipped(var/mob/user, var/slot)
+ ..()
+ update_location(slot)
+
+/obj/item/weapon/holder/proc/update_location(var/slotnumber = null)
+ if (!slotnumber)
+ if (istype(loc, /mob))
+ slotnumber = get_equip_slot()
+
+ report_onmob_location(1, slotnumber, contained)
+
+
+
+
+
/obj/item/weapon/holder/attack_self(mob/M as mob)
if (contained && !(contained.stat & DEAD))
@@ -115,26 +145,47 @@
grabber << "Your hand is full!"
return
+ src.verbs += /mob/living/proc/get_holder_location//This has to be before we move the mob into the holder
- var/obj/item/weapon/holder/H = new holder_type(loc)
- src.forceMove(H)
- H.name = loc.name
- H.attack_hand(grabber)
- H.contained = src
- if (src.stat == DEAD)
- H.held_death()//We've scooped up an animal that's already dead. use the proper dead icons
- else
- H.isalive = 1//We note that the mob is alive when picked up. If it dies later, we can know that its death happened while held, and play its deathmessage for it
+ spawn(2)
+ var/obj/item/weapon/holder/H = new holder_type(loc)
+ H.name = loc.name
+ src.forceMove(H)
- if (user == src)
- grabber << "[src.name] climbs up onto you."
- src << "You climb up onto [grabber]."
- else
- grabber << "You scoop up [src]."
- src << "[grabber] scoops you up."
- grabber.status_flags |= PASSEMOTES
- return
+
+ H.contained = src
+
+
+
+ if (src.stat == DEAD)
+ H.held_death()//We've scooped up an animal that's already dead. use the proper dead icons
+ else
+ H.isalive = 1//We note that the mob is alive when picked up. If it dies later, we can know that its death happened while held, and play its deathmessage for it
+
+ if (user == src)
+ grabber << "[src.name] climbs up onto you."
+ src << "You climb up onto [grabber]."
+ else
+ grabber << "You scoop up [src]."
+ src << "[grabber] scoops you up."
+ grabber.status_flags |= PASSEMOTES
+ H.attack_hand(grabber)//We put this last to prevent some race conditions
+ return
+
+
+/mob/living/proc/get_holder_location()
+ set category = "Abilities"
+ set name = "Check held location"
+ set desc = "Find out where on their person, someone is holding you."
+
+ if (!usr.get_holding_mob())
+ src << "Nobody is holding you!"
+ return
+
+ if (istype(usr.loc, /obj/item/weapon/holder))
+ var/obj/item/weapon/holder/H = usr.loc
+ H.report_onmob_location(0, H.get_equip_slot(), src)
//Mob specific holders.
//w_class mainly determines whether they can fit in trashbags. <=2 can, >=3 cannot
@@ -231,7 +282,6 @@
origin_tech = "biotech=2"
w_class = 1
-
/obj/item/weapon/holder/mouse/white
icon_state = "mouse_white"
icon_state_dead = "mouse_white_dead"
diff --git a/code/modules/mob/living/simple_animal/constructs/constructs.dm b/code/modules/mob/living/simple_animal/constructs/constructs.dm
index e79b7dea738..50eea50f0d3 100644
--- a/code/modules/mob/living/simple_animal/constructs/constructs.dm
+++ b/code/modules/mob/living/simple_animal/constructs/constructs.dm
@@ -32,7 +32,7 @@
mob_swap_flags = HUMAN|SIMPLE_ANIMAL|SLIME|MONKEY
mob_push_flags = ALLMOBS
-
+ hunger_enabled = 0
var/list/construct_spells = list()
/mob/living/simple_animal/construct/cultify()
diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm
index 7d45fabbaf5..5f4cd032eca 100644
--- a/code/modules/mob/living/simple_animal/friendly/mouse.dm
+++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm
@@ -2,6 +2,7 @@
name = "mouse"
real_name = "mouse"
desc = "It's a small rodent."
+ icon = 'icons/mob/mouse.dmi'
icon_state = "mouse_gray"
icon_living = "mouse_gray"
icon_dead = "mouse_gray_dead"
@@ -14,6 +15,9 @@
'sound/effects/creatures/mouse_squeaks_3.ogg',
'sound/effects/creatures/mouse_squeaks_4.ogg')
var/last_softsqueak = null//Used to prevent the same soft squeak twice in a row
+ var/squeals = 5//Spam control. You people are why we cant have nice things >:(
+ var/maxSqueals = 5//SPAM PROTECTION
+ var/last_squealgain = 0// #TODO-FUTURE: Remove from life() once something else is created
pass_flags = PASSTABLE
small = 1
@@ -40,10 +44,19 @@
/mob/living/simple_animal/mouse/Life()
..()
- //Player-animals don't do random speech normally, so this is here
+
+ if(client)
+ //Player-animals don't do random speech normally, so this is here
//Player-controlled mice will still squeak, but less often than NPC mice
- if(client && stat == CONSCIOUS && prob(speak_chance*0.4))
- squeak_soft()
+ if (stat == CONSCIOUS && prob(speak_chance*0.1))
+ squeak_soft(0)
+
+ if (squeals < maxSqueals)
+ var/diff = world.time - last_squealgain
+ if (diff > 600)
+ world << "+1 squeak"
+ squeals++
+ last_squealgain = world.time
if(!ckey && stat == CONSCIOUS && prob(0.5))
stat = UNCONSCIOUS
@@ -60,9 +73,12 @@
else if(prob(5))
audible_emote("snuffles.")
+
+
/mob/living/simple_animal/mouse/New()
..()
+ nutrition = rand(max_nutrition*0.25, max_nutrition*0.75)
verbs += /mob/living/proc/ventcrawl
verbs += /mob/living/proc/hide
@@ -83,12 +99,12 @@
if (body_color == "white")
holder_type = /obj/item/weapon/holder/mouse/white
- verbs += /mob/living/simple_animal/mouse/proc/squeak
- verbs += /mob/living/simple_animal/mouse/proc/squeak_soft
- verbs += /mob/living/simple_animal/mouse/proc/squeak_loud
+ //verbs += /mob/living/simple_animal/mouse/proc/squeak
+ //verbs += /mob/living/simple_animal/mouse/proc/squeak_soft
+ //verbs += /mob/living/simple_animal/mouse/proc/squeak_loud(1)
/mob/living/simple_animal/mouse/speak_audio()
- squeak_soft()
+ squeak_soft(0)
@@ -116,34 +132,54 @@
//Plays a sound.
//This is triggered when a mob steps on an NPC mouse, or manually by a playermouse
-/mob/living/simple_animal/mouse/proc/squeak()
- set name = "Squeak"
- set category = "Abilities"
+/mob/living/simple_animal/mouse/proc/squeak(var/manual = 1)
playsound(src, 'sound/effects/mousesqueek.ogg', 70, 1)
-
+ if (manual)
+ log_say("[key_name(src)] squeaks! ")
//Plays a random selection of four sounds, at a low volume
//This is triggered randomly periodically by any mouse, or manually
-/mob/living/simple_animal/mouse/proc/squeak_soft()
- set name = "Soft Squeaking"
- set category = "Abilities"
-
+/mob/living/simple_animal/mouse/proc/squeak_soft(var/manual = 1)
var/list/new_squeaks = last_softsqueak ? soft_squeaks - last_softsqueak : soft_squeaks
var/sound = pick(new_squeaks)
last_softsqueak = sound
playsound(src, sound, 6, 1)
+ if (manual)
+ log_say("[key_name(src)] squeaks softly! ")
+
//Plays a loud sound
//Triggered manually, when a mouse dies, or rarely when its stepped on
-/mob/living/simple_animal/mouse/proc/squeak_loud()
+/mob/living/simple_animal/mouse/proc/squeak_loud(var/manual = 0)
+ if (squeals > 0 || !manual)
+ playsound(src, 'sound/effects/creatures/mouse_squeak_loud.ogg', 50, 1)
+ squeals --
+ log_say("[key_name(src)] squeals! ")
+ else
+ src << "\red Your hoarse mousey throat can't squeal just now, stop and take a breath!"
+
+
+//Wrapper verbs for the squeak functions
+/mob/living/simple_animal/mouse/verb/squeak_loud_verb()
set name = "Squeal!"
set category = "Abilities"
- playsound(src, 'sound/effects/creatures/mouse_squeak_loud.ogg', 50, 1)
+ squeak_loud(1)
+
+/mob/living/simple_animal/mouse/verb/squeak_soft_verb()
+ set name = "Soft Squeaking"
+ set category = "Abilities"
+ squeak_soft(1)
+
+/mob/living/simple_animal/mouse/verb/squeak_verb()
+ set name = "Squeak"
+ set category = "Abilities"
+ squeak(1)
+
/mob/living/simple_animal/mouse/Crossed(AM as mob|obj)
if( ishuman(AM) )
@@ -151,15 +187,15 @@
var/mob/M = AM
M << "\blue \icon[src] Squeek!"
if (prob(95))
- squeak()
+ squeak(0)
else
- squeak_loud()//You trod on its tail
+ squeak_loud(0)//You trod on its tail
..()
/mob/living/simple_animal/mouse/death()
layer = MOB_LAYER
if (ckey || prob(50))
- squeak_loud()//deathgasp
+ squeak_loud(0)//deathgasp
if(client)
client.time_died_as_mouse = world.time
diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm
index 2016ebd155b..4931e5d1eae 100644
--- a/code/modules/mob/living/simple_animal/hostile/hostile.dm
+++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm
@@ -13,7 +13,7 @@
var/break_stuff_probability = 10
stop_automated_movement_when_pulled = 0
var/destroy_surroundings = 1
-
+ hunger_enabled = 0//Until automated eating mechanics are enabled, disable hunger for hostile mobs
var/shuttletarget = null
var/enroute = 0
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index 162577ae187..1642a1cf60d 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -66,6 +66,9 @@
var/supernatural = 0
var/purge = 0
+
+ //Hunger/feeding vars
+ var/hunger_enabled = 1//If set to 0, a creature ignores hunger
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
@@ -87,7 +90,7 @@
/mob/living/simple_animal/Move(NewLoc, direct)
. = ..()
if(.)
- if(src.nutrition && src.stat != DEAD)
+ if(src.nutrition && src.stat != DEAD && hunger_enabled)
src.nutrition -= nutrition_step
@@ -101,12 +104,14 @@
/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."
+
+ if (hunger_enabled)
+ 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()
..()
@@ -247,28 +252,28 @@
//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 (hunger_enabled)
+ 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
+ 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
- health = min(health+(removed*N.regen_factor), maxHealth)
- current.remove_self(removed)//If its not food, it just does nothing. no fancy effects
+ 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
+ health = min(health+(removed*N.regen_factor), maxHealth)
+ 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)
+ if (!hunger_enabled || nutrition > max_nutrition * 0.9)
return 0//full
else if ((nutrition > max_nutrition * 0.8) || health < maxHealth)
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 0bd3306a938..53e6c5114f3 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -931,6 +931,9 @@ proc/is_blind(A)
var/atom/a = src
while (x < 5)
x++
+ if (isnull(a))
+ return null
+
a = a.loc
if (istype(a, /turf))
return null//We must be on a table or a floor, or maybe in a wall. Either way we're not held.
diff --git a/html/changelogs/Nanako-HungerMouseFix.yml b/html/changelogs/Nanako-HungerMouseFix.yml
new file mode 100644
index 00000000000..d522090f540
--- /dev/null
+++ b/html/changelogs/Nanako-HungerMouseFix.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:
+ - bugfix: "Fixed Check Held Location verb for held mobs not being there."
+ - tweak: "Mouse starting nutrition randomised a little."
+ - bugfix: "Fixed hungry constructs."
+ - tweak: "Nerfed mice."
+
\ No newline at end of file
diff --git a/icons/mob/mouse.dmi b/icons/mob/mouse.dmi
new file mode 100644
index 00000000000..ec4ff5c0d50
Binary files /dev/null and b/icons/mob/mouse.dmi differ