squad-proto/engine/progression.cpp
z.kirill ef103050b1 Правки по игре: начало, ставка, наёмники (M29)
Семь изменений по итогам игры вживую.

НАЧАЛО. Первый этаж встречал одинокого оперативника с ножом тремя тварями,
из которых одна быстрая: бюджет 3.0 и бегуны с первого этажа. Стало 2.2
(ровно два бредущих), бегуны с четвёртого. Света прибавлено: обзор
начинается с 1.30 вместо 1.0, у этажа появилась своя яркость пройденного
(0.42 наверху против 0.13 на дне). Наверху ещё работает освещение, и
хоррор первого этажа в том, что игрок один, а не в том, что не видно
стен. Темнота это инструмент глубины, и тратить её на первый этаж значит
не иметь запаса дальше.

СТАВКА. Опыт платится за убийства и сразу: выносить надо ВЕЩИ. Вайп
больше не отнимает всё - доезжает около половины добычи, и чем вещь реже,
тем охотнее теряется. Прежнее "вайп не возвращает ничего" делало
единственным разумным поведением уход наверх при первой царапине.

ВЫХОД. Аномалия возврата переехала из точки входа к спуску: развилка
"вниз или наверх" теперь там же, где этаж кончился. Обратный путь через
растревоженный этаж ничего не решал - это был ещё раз тот же этаж.

ЧЕКПОИНТЫ через 50 этажей вместо пяти: забег это долгий спуск, а не
перебежка между сохранениями.

НАЁМНИКИ. Кнопка "нанять за опыт" убрана, оперативники находятся в
капсулах на этажах: в среднем раз в сотню этажей и не чаще раза в
двадцать. У каждого свои три прибавки из одного бюджета - разница в
характере, а не в ранге. Покупка была скучной ровно в том смысле, в каком
скучна любая кнопка "стать сильнее": ничего не рассказывает и никуда не
зовёт.

ЗАМЕРЫ. Вайп на сотне повторов: доезжает 52% (обычное 69%, реликты 25%).
Первая версия проверки считала одну сумку и выдала "обычного доехало
112%" - спасение забирает ещё и надетое. Опыт: банк 558 -> 578 за
убийство без эвакуации. Капсула поднимает отряд до двух и даёт наёмнику
его числа.

Формат профиля 6: строки merc и rescued.

Не сделано и обсуждается: ростер сверх пятерых, задания для свободных,
пассивные бонусы от сидящих на базе.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 04:09:26 +03:00

75 lines
4.0 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "engine/progression.h"
#include <algorithm>
#include <cstdio>
namespace
{
// --- отделка -----------------------------------------------------------------
// Полосы этажей по темам. Порядок — это порядок погружения: обжитая сталь,
// заброшенная ржавчина, заражённая плоть, мёртвая наледь. Границы полос —
// данные проекта (FloorCurve::themeFrom), сам порядок — нет: тема с номером
// больше обязана быть «глубже», иначе спуск станет чересполосицей.
uint8_t ThemeAtDepth(int depth)
{
uint8_t theme = 0;
for (int i = 0; i < THEME_COUNT; ++i)
if (depth >= g_curve.themeFrom[i]) theme = uint8_t(i);
return theme;
}
} // namespace
FloorSpec DescribeFloor(int depth)
{
const FloorCurve& c = g_curve;
FloorSpec s;
s.depth = std::max(1, depth);
const float d = float(s.depth);
const float over = d - 1.0f; // насколько глубже первого
s.theme = ThemeAtDepth(s.depth);
// Опасность: линейный рост плюс небольшой квадратичный хвост. Чистая
// линейка на тридцатом этаже читается как «то же самое, только больше»;
// чистая экспонента убивает забег ровно там, где игрок начал понимать
// правила. Хвост даёт ускорение, которое ЗАМЕТНО, но не обрывает спуск.
s.dangerBudget = c.dangerBase + c.dangerLinear * over + c.dangerQuad * over * over;
// Плотность растёт до упора и там встаёт. Дальше расти нельзя: точки
// спавна в комнатах конечны, и множитель выше упора просто означал бы
// «все точки сработали всегда», то есть одинаковые этажи.
s.spawnChanceScale = std::min(c.densityCap, 1.0f + c.densityPerFloor * over);
// Лут. Тир поднимается медленнее опасности, поэтому спуск всегда чуть
// опережает снаряжение — иначе отряд к десятому этажу одет так, что
// дальше можно не смотреть.
s.lootTier = (c.floorsPerTier > 0) ? (s.depth - 1) / c.floorsPerTier : 0;
s.lootDrops = c.lootBase + ((c.floorsPerDrop > 0) ? s.depth / c.floorsPerDrop : 0);
// Обзор сжимается до упора и там встаёт. Ниже — это уже не «страшно», а
// «не видно, куда идти»: игрок перестаёт различать своих, и хоррор
// превращается в неудобство.
s.sightScale = std::max(c.sightFloorAt, c.sightStart - c.sightPerFloor * over);
// Свет этажа гаснет своей кривой: обзор — это «далеко ли видно», а ambient —
// «светло ли там, где уже прошли». Две разные вещи, и первый этаж обязан
// быть светлым по обеим.
s.ambient = std::max(c.ambientFloorAt, c.ambientStart - c.ambientPerFloor * over);
// Опыт растёт медленнее опасности намеренно: спускаться должно быть
// выгодно, но не настолько, чтобы прыгать через этажи было единственной
// разумной игрой.
s.xpScale = 1.0f + c.xpPerFloor * over;
return s;
}
void FloorLabel(const FloorSpec& spec, char* buf, int cap)
{
if (!buf || cap <= 0) return;
std::snprintf(buf, size_t(cap), "D%d %s", spec.depth, ThemeName(spec.theme));
}