Skip to content

Commit

Permalink
Adds hunger to mouse (#1753)
Browse files Browse the repository at this point in the history
<!-- Пишите **НИЖЕ** заголовков и **ВЫШЕ** комментариев, иначе что то
может пойти не так. -->
<!-- Вы можете прочитать Contributing.MD, если хотите узнать больше. -->

## Что этот PR делает

Добавляет мышкам под контролем игрока систему голода и возможность
взаимодействовать с едой.

- При переедании мышку ждёт жестокая смерть от разрыва желудка
- При употребления сыра накладывается эффект наркотиков на 2 секунды
- Систему голода не удалось полностью скопировать с людей, поэтому
параметры пришлось подпирать самому, приближенно к людям, но мыши
голодают чуть быстрее и получают чуть больше веществ с еды.
- Добавляет мышкам кастомный hud голода

## Почему это хорошо для игры
Скорее всего это плохо. Я уде вижу нашествие мышей на кухню с желанием
съесть всё что можно.

## Изображения изменений

![image](https://github.com/user-attachments/assets/1183b495-7159-4a41-b597-650bf67d390f)

![image](https://github.com/user-attachments/assets/66a372eb-e56c-4c9e-bb21-e79d7f1162e9)

![image](https://github.com/user-attachments/assets/a13d8c2b-869a-4d7d-a884-a2a8290cec5c)

![image](https://github.com/user-attachments/assets/6cf2632d-59d7-4215-a79e-72015be2f158)


## TODO

- [x] Доделать иконки для шкалы голода
- [x] Придумать, что случается с очень голодными мышами (Теряет здоровье
в тик?)
- [x] Убрать все магические числа и добавить параметр голода мыши в виде
блюда/час
- [x] Менять скорость поедание еды прямо пропорционально голоду.
- [x] Придумать и добавить особенное взаимодействие с сыром (Квестовую
цепочку? Открыть прыжок после поедания 4 кусков? Просто особенные
реплики в чате?)
- [x] Убрать log_debug()
- [x] Пройтись по всему тексту

## Тестирование

<!-- Как вы тестировали свой PR, если делали это вовсе? -->
- Убедился, что мыши не под управление игрока не получают статуса голода
- Убедился, что мышь может есть только одно блюдо за раз
- Убедился, что другие мышиные механики не затронуты.
- Убедился, что мышь получает нужное количество нутриентов при поедании
мяса.
- Убедился, что мышь теряет ожидаемое число реагентов в минуту. 
- Убедился, что скорость поедания еды тем медленнее, чем больше сытость.
- Убедился, что при поедание разных типов сыра, мышка получает нужный
эффект.

## Changelog

:cl:
add: Мыши и крысы под управлением игроков теперь имеют шкалу голода. Для
поедания доступна любая еда, а скорость поедания зависит от уровня
голода. Для взаимодействия с едой нужно просто кликнуть на неё. Также
добавлена уникальная шкала голода для них и появилась возможность тянуть
не только сыр, но и другую еду.
/:cl:

## Summary by Sourcery

Implement a hunger system for player-controlled mice, allowing them to
interact with food and suffer consequences for overeating.

New Features:
- A hunger system for player-controlled mice has been added.

Tests:
- Verified that non-player mice are unaffected by hunger.
- Confirmed that a mouse can only eat one item at a time.
- Checked that other mouse mechanics remain untouched.
- Ensured correct nutrient gain from meat consumption.
- Validated the expected reagent loss per minute.

## Summary by Sourcery

Add a hunger system for player-controlled mice, including a hunger meter
and the ability to eat food. Overeating will cause the mouse to explode.

New Features:
- Mice now have a hunger system.

Tests:
- Verify that non-player mice are unaffected by the hunger system.
- Confirm that mice can only eat one food item at a time.
- Ensure other mouse mechanics are not impacted.

---------

Co-authored-by: Aylong <[email protected]>
Co-authored-by: Mikhail Dzianishchyts <[email protected]>
  • Loading branch information
3 people authored Jan 27, 2025
1 parent eb1694a commit 2071889
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 5 deletions.
3 changes: 3 additions & 0 deletions code/modules/food_and_drinks/food_base.dm
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,9 @@
N.visible_message("[N] nibbles away at [src].", "")
N.adjustHealth(-2)
N.taste(reagents)
// SS220 EDIT - START
N.consume(src)
// SS220 EDIT - END
else if(iscaterpillar(M))
var/mob/living/simple_animal/nian_caterpillar/W = M
W.taste(reagents)
Expand Down
178 changes: 173 additions & 5 deletions modular_ss220/mobs/code/simple_animal/friendly/mouse.dm
Original file line number Diff line number Diff line change
@@ -1,3 +1,38 @@

// Коэфицент питательности еды, чтобы полностью не копировать сложную систему питание людей.
#define NUTRITION_COEF 20
#define MAX_FEADING_TIME 7 SECONDS
#define MIN_FEADING_TIME 3 SECONDS
// Сколько нутриентов должно быть в мыше, перед тем как мы её гибнем
#define GIB_FEED_LEVEL NUTRITION_LEVEL_FULL * 1.35
#define STATUS_FAT 0
#define STATUS_FULL 1
#define STATUS_WELL_FED 2
#define STATUS_FED 3
#define STATUS_HUNGRY 4
#define STATUS_STARVING 5

/datum/hud/simple_animal_mouse/New(mob/user)
..()

mymob.healths = new /atom/movable/screen/healths()
infodisplay += mymob.healths

mymob.nutrition_display = new /atom/movable/screen/nutrition()
mymob.nutrition_display.screen_loc = "EAST-1:26,CENTER-1:15"
// "EAST-2:32,CENTER-1:13"
mymob.nutrition_display.icon = 'modular_ss220/mobs/code/simple_animal/friendly/screen_hunger_ui_for_mouse.dmi'

infodisplay += mymob.nutrition_display

var/atom/movable/screen/using
using = new /atom/movable/screen/act_intent/simple_animal()
using.icon_state = mymob.a_intent
static_inventory += using
action_intent = using

user.overlay_fullscreen("see_through_darkness", /atom/movable/screen/fullscreen/stretch/see_through_darkness)

/mob/living/simple_animal/mouse
var/non_standard = FALSE // for no "mouse_" with mouse_color
icon = 'modular_ss220/mobs/icons/mob/animal.dmi'
Expand All @@ -6,18 +41,35 @@
damaged_sound = list('modular_ss220/mobs/sound/creatures/rat_wound.ogg')
blood_volume = BLOOD_VOLUME_SURVIVE
butcher_results = list(/obj/item/food/meat/mouse = 1)
// hungry mouse
hud_type = /datum/hud/simple_animal_mouse
// Стартовый уровень голода
nutrition = NUTRITION_LEVEL_HUNGRY + 10
// Скорость с которой снижается наш голод
// Мышка тратит 1800 nutrition в час, при hunger_drain = 1. Одно блюдо восполняет где-то 100-200 nutrition
hunger_drain = HUNGER_FACTOR * 1.66

var/const/bitesize = 2
var/previous_status
var/busy = FALSE

/mob/living/simple_animal/mouse/Initialize(mapload)
. = ..()
AddComponent(/datum/component/squeak, list("[squeak_sound]" = 1), 100, extrarange = SHORT_RANGE_SOUND_EXTRARANGE) //as quiet as a mouse or whatever

pixel_x = rand(-6, 6)
pixel_y = rand(0, 10)

mouse_color = initial(mouse_color) // сбрасываем из-за наследования чтобы своим проком переписать
color_pick()
update_appearance(UPDATE_ICON_STATE|UPDATE_DESC)

// Отслеживаем, что призрак попал в мышку.
/mob/living/simple_animal/mouse/Login()
. = ..()
// Теперь мышка будет обрабатыватся в цикле life, обычные мышки не будут обрабатывать голод.
reagents = new()

/mob/living/simple_animal/mouse/proc/color_pick()
if(!mouse_color)
mouse_color = pick( list("brown","gray","white") )
Expand Down Expand Up @@ -52,12 +104,117 @@
remains.pixel_x = pixel_x
remains.pixel_y = pixel_y

// Вызывается цикилически из модуля live. Отвечает за обработку голода
/mob/living/simple_animal/mouse/handle_chemicals_in_body()
var/new_status
adjust_nutrition(-hunger_drain)

switch(nutrition)
if(GIB_FEED_LEVEL to INFINITY)
visible_message("[src] разорвало от обжорства!", "Ваши внутренности не выдерживают и лопаются!")
src.gib()
if(NUTRITION_LEVEL_FULL to GIB_FEED_LEVEL)
nutrition_display.icon_state = "fat"
new_status = STATUS_FAT
if(NUTRITION_LEVEL_WELL_FED to NUTRITION_LEVEL_FULL)
nutrition_display.icon_state = "full"
new_status = STATUS_FULL
if(NUTRITION_LEVEL_FED to NUTRITION_LEVEL_WELL_FED)
nutrition_display.icon_state = "well_fed"
new_status = STATUS_WELL_FED
if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FED)
nutrition_display.icon_state = "fed"
new_status = STATUS_FED
if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY)
nutrition_display.icon_state = "hungry"
new_status = STATUS_HUNGRY
if(NUTRITION_LEVEL_HYPOGLYCEMIA to NUTRITION_LEVEL_STARVING)
nutrition_display.icon_state = "starving"
new_status = STATUS_STARVING
adjustHealth(0.02)
else
// we are below 0 that's realy bad. Let's kill us
adjustHealth(0.05)

if(previous_status == new_status)
return

previous_status = new_status
switch(new_status)
if(STATUS_FAT)
name = "жирная [initial(name)]" // Мешаем англиский с русским
desc = "[initial(desc)] Господи! Она же огромная!"
to_chat(src, span_userdanger("Ты чувствуешь, что в тебя больше не влезет и кусочка"))
if(STATUS_FULL)
name = initial(name)
desc = initial(desc)
if(STATUS_WELL_FED)
to_chat(src, span_notice("Ты чувствуешь себя превосходно!"))
if(STATUS_FED)
name = initial(name)
desc = initial(desc)
if(STATUS_HUNGRY)
name = "костлявая [initial(name)]"
desc = "[initial(desc)] Вы можете видеть рёбра через кожу."
to_chat(src, span_warning("Твой живот угрюмо урчит, лучше найти что-то поесть"))
if(STATUS_STARVING)
to_chat(src, span_userdanger("Ты смертельно голоден!"))
else
CRASH("Unknown status: [new_status]")

//Prevents mouse from pulling things
/mob/living/simple_animal/mouse/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE)
if(istype(AM, /obj/item/food))
// This is stolen from mob/living/start_pulling
// because i can't call it from here ..()
// insted it will call upstream version of mouse/start_pulling
// Let me know if you know how to do it better
if(!(AM.can_be_pulled(src, state, force, show_message)))
return FALSE
if(incapacitated())
return
if(SEND_SIGNAL(src, COMSIG_LIVING_TRY_PULL, AM, force) & COMSIG_LIVING_CANCEL_PULL)
return FALSE
return
if(show_message)
to_chat(src, span_warning("Ты слишком крохотный, чтобы тянуть что-то кроме еды."))
return

// Вызывается, когда мышка кликает на еду, можно кушать только одну еду за раз.
/mob/living/simple_animal/mouse/proc/consume(obj/item/food/F)
if(busy)
to_chat(src, span_warning("Сначала доешь, то что уже жуёшь."))
return

// /mob/living/simple_animal/mouse/emote(act, m_type = 1, message = null, force)
busy = TRUE
// liniar scale from (MIN_FEADING_TIME, to MAX_FEADING_TIME)
var/eat_time = MIN_FEADING_TIME + (MAX_FEADING_TIME - MIN_FEADING_TIME) * (nutrition / GIB_FEED_LEVEL)
to_chat(src, span_notice("Ты начинаешь употреблять [F]."))
if(!do_after_once(src, eat_time, target = F, needhand = FALSE))
to_chat(src, span_notice("Не доедая, ты перестаёшь жевать [F]!"))
busy = FALSE
return

busy = FALSE
playsound(loc, 'sound/items/eatfood.ogg', 30, FALSE, frequency = 1.5)
var/nutriment = F.reagents.get_reagent_amount("nutriment")
// Добовляю только нутриенты т.к. яды и другие вещества не обрабатываются по умолчанию.

if(istype(F, /obj/item/food/sliced/cheesewedge) || istype(F, /obj/item/food/sliceable/cheesewheel))
Druggy(2 SECONDS)
emote(pick("дёргается","быстро вертит хвостиком","издаёт продолжительный писк"))

if(nutriment > bitesize)
F.reagents.remove_reagent("nutriment", bitesize, TRUE)
visible_message("[src] съедает часть [F].", "Ты съедаешь часть [F].")
adjust_nutrition(bitesize * NUTRITION_COEF)
else
visible_message("[src] заканчивает есть [F].", "Ты доедаешь [F].")
F.reagents.remove_reagent("nutriment", nutriment, TRUE)
adjust_nutrition(nutriment * NUTRITION_COEF)
F.generate_trash(F)
qdel(F)

// if("help")
// to_chat(src, "scream, squeak")
// playsound(src, damaged_sound, 40, 1)

/mob/living/simple_animal/mouse/brown/tom
maxHealth = 10
Expand All @@ -76,3 +233,14 @@
butcher_results = list(/obj/item/stack/sheet/metal = 1)
maxHealth = 20
health = 20

#undef NUTRITION_COEF
#undef MAX_FEADING_TIME
#undef MIN_FEADING_TIME
#undef GIB_FEED_LEVEL
#undef STATUS_FAT
#undef STATUS_FULL
#undef STATUS_WELL_FED
#undef STATUS_FED
#undef STATUS_HUNGRY
#undef STATUS_STARVING
Binary file not shown.

0 comments on commit 2071889

Please sign in to comment.