Полноценная мини-игра в стиле Doom, встроенная в игровой мир SS14 в виде аркадного автомата. Реализует raycasting-рендер с текстурами, волновой спавн врагов, пикапы аптечек и патронов, прогрессию сложности и сохранение рекорда на сервере.
Goob-Station/
├── Content.Goobstation.Shared/
│ └── DoomArcade/
│ ├── SharedDoomArcadeComponent.cs # Компонент + UI enum
│ └── DoomArcadeMessages.cs # Сетевые сообщения (счёт)
│
├── Content.Goobstation.Server/
│ └── DoomArcade/
│ └── DoomArcadeSystem.cs # Серверная система (хранит рекорд)
│
├── Content.Goobstation.Client/
│ └── DoomArcade/
│ ├── DoomArcadeBui.cs # BoundUserInterface (открытие/закрытие окна)
│ ├── DoomArcadeMap.cs # Загрузчик карты из YAML (DoomArcadeMapLoader)
│ ├── DoomArcadeGame.cs # Игровая логика (спавн, враги, пикапы, прогрессия)
│ ├── DoomArcadeControl.cs # UI-контрол: raycasting, текстуры, ввод
│ ├── DoomArcadeMenu.xaml # Разметка окна (Score, Wave, HP, Ammo)
│ └── DoomArcadeMenu.xaml.cs # Код окна (обновление лейблов)
│
└── Resources/
├── Maps/
│ └── DoomArcade/
│ └── map_01.yml # Карта в YAML-формате
├── Textures/
│ └── DoomArcade/
│ ├── vulp_enemy.png # Текстура врага (вульпа)
│ ├── wall_1.png # Серый кирпич
│ ├── wall_2.png # Красный кирпич
│ ├── wall_3.png # Зелёный металл
│ ├── wall_4.png # Синяя решётка
│ ├── floor.png # Каменный пол
│ ├── ceiling.png # Тёмный потолок
│ ├── medkit.png # Аптечка
│ └── ammo.png # Коробка патронов
└── Prototypes/
└── Entities/Structures/Machines/Computers/
└── doom_arcade.yml # Прототип энтити автомата
- Вид от первого лица с raycasting-рендером (как оригинальный Doom/Wolfenstein 3D)
- Цель: выживать как можно дольше, уничтожая волны врагов
- За каждого убитого врага — +100 очков, за полную зачистку волны — +500 очков
- Новая волна спавнится каждые 8 секунд или сразу после зачистки всех врагов
- С каждой волной враги становятся толще и больнее (см. таблицу прогрессии)
- Вместе с волной случайно появляются аптечки и патроны — подбираются автоматически при наступлении
- При смерти — экран Game Over, любая клавиша перезапускает игру
- Лучший результат сохраняется на сервере в компоненте автомата
| Клавиша | Действие |
|---|---|
W |
Движение вперёд |
S |
Движение назад |
A |
Стрейф влево |
D |
Стрейф вправо |
← |
Поворот влево |
→ |
Поворот вправо |
↑ (TextCursorUp) |
Выстрел |
| Иконка на миникарте | Тип | Эффект |
|---|---|---|
| 🟢 Зелёный | Аптечка | +30 HP (макс 100) |
| 🟡 Жёлтый | Патроны | +15 патронов |
С каждой волной характеристики всех новых врагов масштабируются:
| Волна | HP врага (база + разброс) | Урон в секунду |
|---|---|---|
| 1 | 25–35 | 5 |
| 2 | 40–55 | 5 |
| 3 | 55–75 | 7 |
| 4 | 70–95 | 7 |
| 5 | 85–115 | 9 |
| 10 | 160–210 | 19 |
| 15+ | 235–310 | 25 (макс) |
Формулы в DoomArcadeGame.cs:
// HP: +15 базы и +5 разброса за каждую волну
int baseHp = 25 + (wave - 1) * 15;
int spread = 10 + (wave - 1) * 5;
// Урон: +2 каждые 2 волны, максимум 25
int damage = Math.Min(5 + (wave - 1) / 2 * 2, 25);Карта хранится в YAML-файле и загружается при старте через IResourceManager.ContentFileReadAllText. При ошибке загрузки используется встроенная fallback-карта в DoomArcadeGame.cs.
Формат YAML:
name: "UAC Research Facility"
width: 16
height: 16
player_start:
x: 2.5
y: 2.5
angle: 0 # в градусах
tiles:
- [1, 1, 1, ...] # строка Y=0
- [1, 0, 0, ...] # строка Y=1
...Значения тайлов:
| Значение | Текстура |
|---|---|
0 |
Пустое пространство |
1 |
wall_1.png — серый кирпич |
2 |
wall_2.png — красный кирпич |
3 |
wall_3.png — зелёный металл |
4 |
wall_4.png — синяя решётка |
Рендер реализован через DrawingHandleScreen (Robust UI) без шейдеров или 3D-движка:
- Raycasting (DDA-алгоритм): для каждого столбца экрана пускается луч, вычисляется расстояние до стены, высота полосы и U-координата попадания на стену
- Текстурированные стены: каждый вертикальный столбец рисуется через
DrawTextureRectRegionс нужной полосой текстуры по U-координате - Тайловые пол/потолок: текстуры повторяются блоками через
DrawTextureRectRegion - Depth buffer: защищает спрайты врагов и пикапов от отрисовки сквозь стены
- Затемнение: цвет умножается на
1 / (1 + dist * k), боковые стены дополнительно × 0.7 - Спрайты пикапов: рисуются у пола, масштабируются по расстоянию
- Миникарта: стены (серые), игрок (зелёный), живые враги (красные), аптечки (ярко-зелёные), патроны (жёлтые)
- HUD: прицел, пистолет, вспышка выстрела, лейблы Score / Wave / HP / Ammo
GetFreePositions()— собирает все пустые клетки карты, исключает стены, позицию игрока (радиус 4–5 ед.) и клетки занятые живыми врагами- Список перемешивается через
Shuffle()(Fisher-Yates), берутся первые N позиций — спавн гарантированно вне стен - Каждые 8 секунд или после зачистки вызывается
SpawnWave(): добавляет 3+ врагов и 1–2 пикапа - Максимум живых врагов одновременно: 8
- При закрытии окна BUI отправляет
DoomArcadeScoreMessageс финальным счётом - Сервер (
DoomArcadeSystem) обновляетHighScoreв компоненте, если новый счёт выше
- Скопировать все файлы согласно структуре выше
- Положить PNG-текстуры в
Resources/Textures/DoomArcade/ - Положить
map_01.ymlвResources/Maps/DoomArcade/ - Убедиться, что
doom_arcade.ymlнаходится в папке прототипов и подключён к сборке - Заспавнить через
spawn DoomArcadeMachineили разместить на карте - Собрать проект
- Robust Toolbox — UI, input, IoC, рендер,
IResourceManager,IResourceCache - Пространства имён:
Content.Goobstation.Shared,Content.Goobstation.Client,Content.Goobstation.Server - Спрайты корпуса автомата:
Structures/Machines/arcade.rsi(базовый ресурс SS14) - Python + Pillow — только для регенерации PNG-текстур (не нужен в рантайме)