From f4fe24f34ad8288a7225b582e98de44241fdcead Mon Sep 17 00:00:00 2001
From: Tsar-Salat <62388554+Tsar-Salat@users.noreply.github.com>
Date: Tue, 5 Mar 2024 15:42:28 -0500
Subject: [PATCH] Fixes moths not being able to eat clothes (#10699)
* https://github.com/tgstation/tgstation/pull/55356
* https://github.com/tgstation/tgstation/pull/61082
* final touchups
* bad vars oopsies
* fix bad armor var
* dumb span
---
code/datums/armor.dm | 109 +++++++++---------
code/modules/clothing/clothing.dm | 60 ++++++++--
.../unit_tests/food_edibility_check.dm | 4 +-
3 files changed, 109 insertions(+), 64 deletions(-)
diff --git a/code/datums/armor.dm b/code/datums/armor.dm
index 6ad80052956c3..3a11186a292e9 100644
--- a/code/datums/armor.dm
+++ b/code/datums/armor.dm
@@ -1,73 +1,76 @@
-#define ARMORID "armor-[melee]-[bullet]-[laser]-[energy]-[bomb]-[bio]-[rad]-[fire]-[acid]-[magic]-[stamina]"
+#define ARMORID "armor-[melee]-[bullet]-[laser]-[energy]-[bomb]-[bio]-[rad]-[fire]-[acid]-[magic]-[stamina]-[consume]"
-/proc/getArmor(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0, magic = 0, stamina = 0)
- . = locate(ARMORID)
- if (!.)
- . = new /datum/armor(melee, bullet, laser, energy, bomb, bio, rad, fire, acid, magic, stamina)
+/proc/getArmor(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0, magic = 0, stamina = 0, consume = 0)
+ . = locate(ARMORID)
+ if (!.)
+ . = new /datum/armor(melee, bullet, laser, energy, bomb, bio, rad, fire, acid, magic, stamina, consume)
/datum/armor
- datum_flags = DF_USE_TAG
- var/melee
- var/bullet
- var/laser
- var/energy
- var/bomb
- var/bio
- var/rad
- var/fire
- var/acid
- var/magic
- var/stamina
+ datum_flags = DF_USE_TAG
+ var/melee
+ var/bullet
+ var/laser
+ var/energy
+ var/bomb
+ var/bio
+ var/rad
+ var/fire
+ var/acid
+ var/magic
+ var/stamina
+ var/consume
-/datum/armor/New(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0, magic = 0, stamina = 0)
- src.melee = melee
- src.bullet = bullet
- src.laser = laser
- src.energy = energy
- src.bomb = bomb
- src.bio = bio
- src.rad = rad
- src.fire = fire
- src.acid = acid
- src.magic = magic
- src.stamina = stamina
- tag = ARMORID
+/datum/armor/New(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0, magic = 0, stamina = 0, consume = 0)
+ src.melee = melee
+ src.bullet = bullet
+ src.laser = laser
+ src.energy = energy
+ src.bomb = bomb
+ src.bio = bio
+ src.rad = rad
+ src.fire = fire
+ src.acid = acid
+ src.magic = magic
+ src.stamina = stamina
+ src.consume = consume
+ tag = ARMORID
-/datum/armor/proc/modifyRating(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0, magic = 0, stamina = 0)
- return getArmor(src.melee+melee, src.bullet+bullet, src.laser+laser, src.energy+energy, src.bomb+bomb, src.bio+bio, src.rad+rad, src.fire+fire, src.acid+acid, src.magic+magic, src.stamina+stamina)
+/datum/armor/proc/modifyRating(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0, magic = 0, stamina = 0, consume = 0)
+ return getArmor(src.melee+melee, src.bullet+bullet, src.laser+laser, src.energy+energy, src.bomb+bomb, src.bio+bio, src.rad+rad, src.fire+fire, src.acid+acid, src.magic+magic, src.stamina+stamina, src.consume+consume)
/datum/armor/proc/modifyAllRatings(modifier = 0)
- return getArmor(melee+modifier, bullet+modifier, laser+modifier, energy+modifier, bomb+modifier, bio+modifier, rad+modifier, fire+modifier, acid+modifier, magic+modifier, stamina+modifier)
+ return getArmor(melee+modifier, bullet+modifier, laser+modifier, energy+modifier, bomb+modifier, bio+modifier, rad+modifier, fire+modifier, acid+modifier, magic+modifier, stamina+modifier, consume+modifier)
-/datum/armor/proc/setRating(melee, bullet, laser, energy, bomb, bio, rad, fire, acid, magic)
- return getArmor((isnull(melee) ? src.melee : melee),\
- (isnull(bullet) ? src.bullet : bullet),\
- (isnull(laser) ? src.laser : laser),\
- (isnull(energy) ? src.energy : energy),\
- (isnull(bomb) ? src.bomb : bomb),\
- (isnull(bio) ? src.bio : bio),\
- (isnull(rad) ? src.rad : rad),\
- (isnull(fire) ? src.fire : fire),\
- (isnull(acid) ? src.acid : acid),\
- (isnull(magic) ? src.magic : magic),\
- (isnull(stamina) ? src.stamina : stamina))
+/datum/armor/proc/setRating(melee, bullet, laser, energy, bomb, bio, rad, fire, acid, magic, consume)
+ return getArmor((isnull(melee) ? src.melee : melee),\
+ (isnull(bullet) ? src.bullet : bullet),\
+ (isnull(laser) ? src.laser : laser),\
+ (isnull(energy) ? src.energy : energy),\
+ (isnull(bomb) ? src.bomb : bomb),\
+ (isnull(bio) ? src.bio : bio),\
+ (isnull(rad) ? src.rad : rad),\
+ (isnull(fire) ? src.fire : fire),\
+ (isnull(acid) ? src.acid : acid),\
+ (isnull(magic) ? src.magic : magic),\
+ (isnull(stamina) ? src.stamina : stamina),\
+ (isnull(consume) ? src.consume : consume))
/datum/armor/proc/getRating(rating)
- return vars[rating]
+ return vars[rating]
/datum/armor/proc/getList()
- return list(MELEE = melee, BULLET = bullet, LASER = laser, ENERGY = energy, BOMB = bomb, BIO = bio, RAD = rad, FIRE = fire, ACID = acid, MAGIC = magic, STAMINA = stamina)
+ return list(MELEE = melee, BULLET = bullet, LASER = laser, ENERGY = energy, BOMB = bomb, BIO = bio, RAD = rad, FIRE = fire, ACID = acid, MAGIC = magic, STAMINA = stamina, CONSUME = consume)
/datum/armor/proc/attachArmor(datum/armor/AA)
- return getArmor(melee+AA.melee, bullet+AA.bullet, laser+AA.laser, energy+AA.energy, bomb+AA.bomb, bio+AA.bio, rad+AA.rad, fire+AA.fire, acid+AA.acid, magic+AA.magic, stamina+AA.stamina)
+ return getArmor(melee+AA.melee, bullet+AA.bullet, laser+AA.laser, energy+AA.energy, bomb+AA.bomb, bio+AA.bio, rad+AA.rad, fire+AA.fire, acid+AA.acid, magic+AA.magic, stamina+AA.stamina, consume+AA.consume)
/datum/armor/proc/detachArmor(datum/armor/AA)
- return getArmor(melee-AA.melee, bullet-AA.bullet, laser-AA.laser, energy-AA.energy, bomb-AA.bomb, bio-AA.bio, rad-AA.rad, fire-AA.fire, acid-AA.acid, magic-AA.magic, stamina-AA.stamina)
+ return getArmor(melee-AA.melee, bullet-AA.bullet, laser-AA.laser, energy-AA.energy, bomb-AA.bomb, bio-AA.bio, rad-AA.rad, fire-AA.fire, acid-AA.acid, magic-AA.magic, stamina-AA.stamina, consume+AA.consume)
/datum/armor/vv_edit_var(var_name, var_value)
- if (var_name == NAMEOF(src, tag))
- return FALSE
- . = ..()
- tag = ARMORID // update tag in case armor values were edited
+ if (var_name == NAMEOF(src, tag))
+ return FALSE
+ . = ..()
+ tag = ARMORID // update tag in case armor values were edited
#undef ARMORID
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index d12e5834d727e..f789c34fa30eb 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -4,6 +4,8 @@
#define SENSORS_TRACKING 3
#define SENSOR_CHANGE_DELAY 1.5 SECONDS
+#define MOTH_EATING_CLOTHING_DAMAGE 15
+
/obj/item/clothing
name = "clothing"
resistance_flags = FLAMMABLE
@@ -52,6 +54,9 @@
var/high_pressure_multiplier = 1
var/static/list/high_pressure_multiplier_types = list(MELEE, BULLET, LASER, ENERGY, BOMB)
+ /// A lazily initiated "food" version of the clothing for moths
+ var/obj/item/food/clothing/moth_snack
+
/obj/item/clothing/Initialize(mapload)
if(CHECK_BITFIELD(clothing_flags, VOICEBOX_TOGGLABLE))
actions_types += /datum/action/item_action/toggle_voice_box
@@ -76,17 +81,45 @@
/obj/item/food/clothing // fuck you
name = "temporary moth clothing snack item"
desc = "If you're reading this it means I messed up. This is related to moths eating clothes and I didn't know a better way to do it than making a new food object." // die
- food_reagents = list(/datum/reagent/consumable/nutriment = 1)
+ bite_consumption = 1
+ // sigh, ok, so it's not ACTUALLY infinite nutrition. this is so you can eat clothes more than...once.
+ // bite_consumption limits how much you actually get, and the take_damage in after eat makes sure you can't abuse this.
+ // ...maybe this was a mistake after all.
+ food_reagents = list(/datum/reagent/consumable/nutriment = INFINITY)
tastes = list("dust" = 1, "lint" = 1)
foodtypes = CLOTH
-/obj/item/clothing/attack(mob/M, mob/user, def_zone)
- if(user.a_intent != INTENT_HARM && (ismoth(M) || ispsyphoza(M)) && !(clothing_flags & NOTCONSUMABLE) && !(resistance_flags & INDESTRUCTIBLE) && (armor.getRating(MELEE) == 0))
- var/obj/item/food/clothing/clothing_as_food = new // I shall send you to hell in the next PR
- clothing_as_food.name = name
- if(clothing_as_food.attack(M, user, def_zone))
- take_damage(15, sound_effect=FALSE)
- qdel(clothing_as_food)
+ /// A weak reference to the clothing that created us
+ var/datum/weakref/clothing
+
+/obj/item/food/clothing/make_edible()
+ AddComponent(/datum/component/edible,\
+ initial_reagents = food_reagents,\
+ food_flags = food_flags,\
+ foodtypes = foodtypes,\
+ volume = max_volume,\
+ eat_time = eat_time,\
+ tastes = tastes,\
+ eatverbs = eatverbs,\
+ bite_consumption = bite_consumption,\
+ microwaved_type = microwaved_type,\
+ junkiness = junkiness,\
+ after_eat = CALLBACK(src, PROC_REF(after_eat)))
+
+/obj/item/food/clothing/proc/after_eat(mob/eater)
+ var/obj/item/clothing/resolved_clothing = clothing.resolve()
+ if (resolved_clothing)
+ resolved_clothing.take_damage(MOTH_EATING_CLOTHING_DAMAGE, sound_effect = FALSE, damage_flag = CONSUME)
+ else
+ qdel(src)
+
+/obj/item/clothing/attack(mob/attacker, mob/user, def_zone)
+ if(user.a_intent != INTENT_HARM && (ismoth(attacker) || ispsyphoza(attacker)) && !(clothing_flags & NOTCONSUMABLE) && !(resistance_flags & INDESTRUCTIBLE) && (armor.getRating(MELEE) == 0))
+ if (isnull(moth_snack))
+ moth_snack = new
+ moth_snack.name = name
+ moth_snack.clothing = WEAKREF(src)
+ moth_snack.attack(attacker, user, def_zone)
else
return ..()
@@ -102,6 +135,7 @@
/obj/item/clothing/Destroy()
user_vars_remembered = null //Oh god somebody put REFERENCES in here? not to worry, we'll clean it up
+ QDEL_NULL(moth_snack)
return ..()
/obj/item/clothing/dropped(mob/user)
@@ -449,7 +483,6 @@ BLIND // can't see anything
return 1
return 0
-
/obj/item/clothing/obj_destruction(damage_flag)
if(damage_flag == BOMB || damage_flag == MELEE)
var/turf/T = get_turf(src)
@@ -457,6 +490,13 @@ BLIND // can't see anything
var/obj/effect/decal/cleanable/shreds/Shreds = new(T)
Shreds.desc = "The sad remains of what used to be [name]."
deconstruct(FALSE)
+ if(damage_flag == CONSUME) //This allows for moths to fully consume clothing, rather than damaging it like other sources like brute
+ var/turf/current_position = get_turf(src)
+ new /obj/effect/decal/cleanable/shreds(current_position, name)
+ if(isliving(loc))
+ var/mob/living/possessing_mob = loc
+ possessing_mob.visible_message("[src] is consumed until naught but shreds remains!", "[src] falls apart into little bits!")
+ deconstruct(FALSE)
else
..()
@@ -475,3 +515,5 @@ BLIND // can't see anything
#undef SENSORS_VITALS
#undef SENSORS_TRACKING
#undef SENSOR_CHANGE_DELAY
+
+#undef MOTH_EATING_CLOTHING_DAMAGE
diff --git a/code/modules/unit_tests/food_edibility_check.dm b/code/modules/unit_tests/food_edibility_check.dm
index a5528c90dd530..8eae05e3330f1 100644
--- a/code/modules/unit_tests/food_edibility_check.dm
+++ b/code/modules/unit_tests/food_edibility_check.dm
@@ -8,8 +8,8 @@
/obj/item/food/grown/mushroom,
/obj/item/food/donkpocket/random,
/obj/item/food/deepfryholder,
- //obj/item/food/clothing,
- //obj/item/food/meat/slab/human/mutant,
+ /obj/item/food/clothing,
+ /obj/item/food/meat/slab/human/mutant,
/obj/item/food/grown/shell)
var/list/food_paths = subtypesof(/obj/item/food) - not_food