Прототипы - это фундаментальный механизм Space Station 14 для определения данных игры в YAML-файлах, которые затем загружаются и используются движком Robust Toolbox. Они помогают отделить логику от данных: например, если вы хотите изменить внешний вид или характеристики предмета, вам не нужно перекомпилировать весь код - достаточно изменить соответствующий YAML-прототип.
Если вы рассматриваете возможность внести свой вклад в Lost Paradise, руководство Wizard's Den по PR является хорошей отправной точкой для понимания качества кода и этикета отслеживания версий. Обратите внимание, что у нас нет такого разделения на ветки master/stable.
Важно: не вносите изменения через веб-редактор GitHub. PR, отправленные через веб-редактор, могут быть закрыты без рассмотрения.
"Upstream" относится к репозиторию Simple-Station/Einstein-Engines, от которого был создан этот форк.
В целом, всё, что вы создаёте с нуля (в отличие от изменения существующего контента из upstream), должно находиться в специфичной для Lost Paradise подпапке _LP.
Примеры структуры файлов:
| Тип файла | Путь |
|---|---|
| C# код | Content.Server/_LP/Shipyard/Systems/ShipyardSystem.cs |
| Прототипы | Resources/Prototypes/_LP/Loadouts/role_loadouts.yml |
| Звуки | Resources/Audio/_LP/Voice/Goblin/goblin-scream-03.ogg |
| Текстуры | Resources/Textures/_LP/Tips/clippy.rsi/left.png |
| Локализация | Resources/Locale/en-US/_LP/devices/pda.ftl |
| Гайдбук | Resources/ServerInfo/_LP/Guidebook/Medical/Doc.xml |
Если вы вносите изменения в C# или YAML-файл из upstream, вы обязаны добавить комментарии на изменённых строках или рядом с ними.
Комментарии должны пояснять, что именно было изменено, чтобы упростить разрешение конфликтов при изменении файла в upstream.
Правила комментариев:
LP edit OLD<NEWОднострочный комментарий к изменённому полю YAML:
- type: entity
id: TorsoHarpy
name: "harpy torso"
parent: [PartHarpy, BaseTorso] # LP edit: add BaseTorso
Изменение значения (формат OLD<NEW):
- type: Gun
fireRate: 4 # LP edit 3<4
availableModes:
- SemiAuto
Блочный комментарий для добавленного кода:
- type: ItemBorgModule
moduleId: Gardening # LP edit
items:
- HydroponicsToolMiniHoe
- HydroponicsToolSpade
- HydroponicsToolClippers
# - Bucket # LP edit
# LP edit start: droppable borg items
- type: DroppableBorgModule
moduleId: Gardening
items:
- id: Bucket
whitelist:
tags:
- Bucket
# LP edit end
Комментарий к новому импорту в C#:
using Content.Client._NF.Emp.Overlays; // LP edit
Блочный комментарий в C#:
component.Capacity = state.Capacity;
component.UIUpdateNeeded = true;
// LP edit start: ensure signature colour is consistent
if (TryComp<StampComponent>(uid, out var stamp))
{
stamp.StampedColor = state.Color;
}
// LP edit end
Код, спрайты и любой другой контент, сгенерированный ИИ, не разрешается отправлять в репозиторий.
Попытка отправить PR с контентом, сгенерированным ИИ, может привести к блокировке вашей возможности вносить вклад.
Прототип - это шаблон или описание игрового объекта (или другого элемента игры), написанный в YAML-формате. Каждый прототип имеет:
Компонент - это модуль функционала для сущностей. Каждый компонент добавляет определённую способность сущности:
Sprite - добавляет спрайт (графику)Physics - делает сущность физическим объектом (с массой, гравитацией)Item - делает объект поднимаемым и переносимымDamageable - позволяет сущности получать уронСущность - это собрание компонентов. Без компонентов сущность не будет ни видно, ни взаимодействовать с миром.
В Robust Toolbox существуют два основных интерфейса для работы с прототипами:
Базовый интерфейс для всех прототипов. Определяет обязательное свойство ID - уникальный идентификатор прототипа.
public interface IPrototype
{
string ID { get; }
}
Расширение IPrototype, позволяющее прототипу наследовать свойства от родительских прототипов. Добавляет свойства:
Parents - массив идентификаторов родительских прототиповAbstract - флаг, указывающий, что прототип абстрактный (не может быть использован напрямую, только наследован)public interface IInheritingPrototype
{
string[]? Parents { get; }
bool Abstract { get; }
}
Каждый прототип в YAML-файле имеет следующую структуру:
- type: <тип_прототипа>
id: <уникальный_идентификатор>
<свойство1>: <значение1>
<свойство2>: <значение2>
...
Сущности - это основные игровые объекты. Они описываются с помощью компонентов.
- type: entity
id: BaseControllable
abstract: true
save: false
components:
- type: Sprite
noRot: true
drawdepth: Mobs
- type: GravityAffected
- type: Physics
- type: Fixtures
fixtures:
fix1:
shape: !type:PhysShapeCircle
radius: 0.35
density: 50
mask: [MobMask]
layer: [MobLayer]
Описывает параметры взрыва: урон, шанс разрушения тайлов, цвет и т.д.
- type: explosion
id: Default
damagePerIntensity:
types:
Heat: 5
Blunt: 5
Piercing: 5
tileBreakChance: [0, 0.5, 1]
tileBreakIntensity: [0, 10, 30]
tileBreakRerollReduction: 20
lightColor: Orange
texturePath: /Textures/Effects/fire.rsi
fireStates: 3
temperature: 800
Теги используются для классификации сущностей и создания белых/чёрных списков.
- type: Tag
id: Airlock
В SS14 существует множество разных прототипов.
Сущность - это основной игровой объект: моб, предмет, устройство, стены и т.д. Все сущности состоят из компонентов.
# /Resources/Prototypes/Entities/Objects/Tools/tools.yml
- type: entity
name: wrench
parent: BaseItem
id: Wrench
description: "A common tool for assembly and disassembly."
components:
- type: Sprite
sprite: Objects/Tools/wrench.rsi
state: icon
- type: Item
size: Small
- type: Tag
tags:
- Wrench
- type: MeleeWeapon
wideAnimationRotation: 135
attackRate: 1.5
damage:
types:
Blunt: 4.5
soundHit:
collection: MetalThud
- type: Tool
qualities:
- Anchoring
useSound:
path: /Audio/Items/ratchet.ogg
# /Resources/Prototypes/Entities/Structures/Walls/walls.yml
- type: entity
parent: BaseWall
id: WallSolid
name: solid wall
components:
- type: Sprite
sprite: Structures/Walls/solid.rsi
- type: Icon
sprite: Structures/Walls/solid.rsi
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 400
behaviors:
- !type:DoActsBehavior
acts: ["Destruction"]
- type: entity
id: SimpleNPC
name: simple-npc
parent: BaseMob
components:
- type: Sprite
sprite: Mobs/SimpleNPC.rsi
state: idle
- type: MovementSpeedModifier
baseWalkSpeed: 2
baseSprintSpeed: 3
- type: HTN # Hierarchical Task Network - система ИИ для NPC
rootTask: SimpleNPCBrain # Корневая задача (определяется в HTN прототипах)
Используется для создания групп звуков, из которых случайно выбирается один для воспроизведения.
# /Resources/Prototypes/SoundCollections/hit_impacts.yml
- type: soundCollection
id: MetalThud
files:
- /Audio/Effects/metal_thud1.ogg
- /Audio/Effects/metal_thud2.ogg
- /Audio/Effects/metal_thud3.ogg
Описывает параметры взрыва: урон, шанс разрушения тайлов, цвет и т.д.
- type: explosion
id: GrenadeExplosion
damagePerIntensity:
types:
Heat: 10
Blunt: 15
Piercing: 12
tileBreakChance: [0, 0.8, 1]
tileBreakIntensity: [0, 15, 40]
lightColor: Red
texturePath: /Textures/Effects/explosion.rsi
fireStates: 5
temperature: 1000
Теги используются для классификации сущностей и создания фильтров (белых/чёрных списков).
- type: Tag
id: Tool
# Оборудование, которое считается инструментом
- type: Tag
id: Explosive
# Предметы, которые могут взорваться
Описывает химические реагенты для системы химии.
# Resources\Prototypes\Reagents\Consumable\Drink\alcohol.yml
- type: reagent
id: Beer
name: reagent-name-beer
parent: BaseAlcohol
desc: reagent-desc-beer
physicalDesc: reagent-physical-desc-bubbly
flavor: beer
color: "#e3e77b"
recognizable: true
metamorphicSprite:
sprite: Objects/Consumable/Drinks/beerglass.rsi
state: icon_empty
metamorphicMaxFillLevels: 6
metamorphicFillBaseName: fill-
metamorphicChangeColor: true
fizziness: 0.6
Если вам нужен новый тип прототипа, создайте класс в C#:
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Shared.MyModule
{
// Атрибут указывает тип прототипа для YAML-файлов
[Prototype("customItem")]
public sealed class CustomItemPrototype : IPrototype
{
// Обязательное поле ID (уникальный идентификатор)
[IdDataField]
public string ID { get; private set; } = default!;
// Поле name с значением по умолчанию
[DataField("name")]
public string Name { get; private set; } = "Неизвестный предмет";
// Числовое поле с значением по умолчанию
[DataField("price")]
public int Price { get; private set; } = 0;
// Вложенный объект для модификаторов (структурированные данные)
[DataField("modifiers")]
public ItemModifiers Modifiers { get; private set; } = new();
// Вложенный объект (с поддержкой сериализации)
[DataField("stats")]
public ItemStats Stats { get; private set; } = new();
}
// Класс для модификаторов предмета
public sealed class ItemModifiers
{
[DataField("fireDamage")]
public int FireDamage { get; private set; } = 0;
[DataField("attackSpeedBonus")]
public float AttackSpeedBonus { get; private set; } = 0f;
[DataField("damageBlockPercent")]
public float DamageBlockPercent { get; private set; } = 0f;
[DataField("speedPenalty")]
public float SpeedPenalty { get; private set; } = 0f;
}
// Вложенный класс для сложных данных
public sealed class ItemStats
{
[DataField("damage")]
public int Damage { get; private set; } = 10;
[DataField("durability")]
public int Durability { get; private set; } = 100;
}
}
Теперь создайте YAML-файл с прототипами:
# /Resources/Prototypes/MyModule/customItems.yml
# Кастомный меч
- type: customItem
id: CustomSword
name: custom-sword-test
price: 50
modifiers: # Используем modifiers вместо effects
fireDamage: 10
attackSpeedBonus: 0.2 # +20% к скорости атаки
stats:
damage: 25
durability: 150
# Кастомный щит
- type: customItem
id: CustomShield
name: custom-shield-test
price: 75
modifiers:
damageBlockPercent: 0.5 # Блокирует 50% урона
speedPenalty: 0.1 # -10% к скорости
stats:
damage: 0
durability: 200
Важно: В этом примере мы используем поле modifiers вместо effects. Это связано с тем, что effects в SS14 - это специальный термин для типизированных объектов (см. главу "Разбор Effects от нуля до понимания"). Для кастомных прототипов лучше использовать другие названия полей.
Обновлённый C# класс:
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Shared.MyModule
{
[Prototype("customItem")]
public sealed class CustomItemPrototype : IPrototype
{
[IdDataField]
public string ID { get; private set; } = default!;
[DataField("name")]
public string Name { get; private set; } = "Неизвестный предмет";
[DataField("price")]
public int Price { get; private set; } = 0;
// Используем структурированные данные вместо строк
[DataField("modifiers")]
public ItemModifiers Modifiers { get; private set; } = new();
[DataField("stats")]
public ItemStats Stats { get; private set; } = new();
}
// Структура для модификаторов
public sealed class ItemModifiers
{
[DataField("fireDamage")]
public int FireDamage { get; private set; } = 0;
[DataField("attackSpeedBonus")]
public float AttackSpeedBonus { get; private set; } = 0f;
[DataField("damageBlockPercent")]
public float DamageBlockPercent { get; private set; } = 0f;
[DataField("speedPenalty")]
public float SpeedPenalty { get; private set; } = 0f;
}
public sealed class ItemStats
{
[DataField("damage")]
public int Damage { get; private set; } = 10;
[DataField("durability")]
public int Durability { get; private set; } = 100;
}
}
Использование в системе:
using Robust.Shared.Prototypes;
public class MySystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public void ShowItemInfo(string itemId)
{
if (_prototypeManager.TryIndex<CustomItemPrototype>(itemId, out var prototype))
{
Console.WriteLine($"Название: {prototype.Name}");
Console.WriteLine($"Цена: {prototype.Price} кредитов");
Console.WriteLine($"Урон: {prototype.Stats.Damage}");
Console.WriteLine($"Прочность: {prototype.Stats.Durability}");
// Вывод модификаторов
var mods = prototype.Modifiers;
if (mods.FireDamage > 0)
Console.WriteLine($" - Огненный урон: +{mods.FireDamage}");
if (mods.AttackSpeedBonus > 0)
Console.WriteLine($" - Скорость атаки: +{mods.AttackSpeedBonus * 100}%");
if (mods.DamageBlockPercent > 0)
Console.WriteLine($" - Блок урона: {mods.DamageBlockPercent * 100}%");
if (mods.SpeedPenalty > 0)
Console.WriteLine($" - Штраф скорости: -{mods.SpeedPenalty * 100}%");
}
else
{
Console.WriteLine($"Прототип '{itemId}' не найден!");
}
}
}
Сравнение подходов:
| Подход | Преимущества | Недостатки |
|---|---|---|
| Строки (неправильно) | Просто написать | Нет типизации, сложно обрабатывать |
| Структурированные данные | Типизация, валидация, IDE подсказки | Требует больше кода |
Примечание: Если вам нужны реальные эффекты, которые взаимодействуют с игровыми системами SS14 (здоровье, статус-эффекты, взрывы и т.д.), используйте EntityEffect. Подробнее см. главу "Разбор Effects от нуля до понимания".
Прототипы могут наследовать свойства от других прототипов, используя поле parent или parents.
- type: entity
id: BaseMob
abstract: true
parent: BaseControllable
components:
- type: MobCollision
- type: Physics
bodyType: KinematicController
- type: InputMover
Для множественного наследования используется массив в parent.
- type: entity
id: ComplexMob
abstract: true
parent: [BaseMob, MobDamageable, MobCombat ]
components:
- type: CustomComponent
customProperty: "value"
Дочерние прототипы могут переопределять свойства родителей:
- type: entity
id: PlayerMob
parent: BaseMob
components:
- type: Physics
# Переопределение свойства из родителя
bodyType: Dynamic
- type: InputMover
- type: PlayerComponent
Абстрактные прототипы не могут быть использованы напрямую, только наследованы. Они используются для создания базовых шаблонов.
- type: entity
id: BaseControllable
abstract: true
save: false
components:
- type: Sprite
- type: Input
Компонент - это минимальная единица функциональности для сущности. Каждый компонент отвечает за одно конкретное поведение:
Sprite - отвечает за графикуPhysics - отвечает за физику (гравитация, столкновения)Item - делает объект поднимаемымDamageable - позволяет получить уронStorage - добавляет возможность хранить другие предметыHTN (Hierarchical Task Network) - добавляет ИИ-управление для NPCБез компонентов сущность - это просто "пустой контейнер" без внешнего вида.
Когда вы создаёте сущность через прототип, движок:
Добавляет спрайт (графику) сущности:
- type: Sprite
sprite: Objects/Tools/wrench.rsi
state: icon
drawdepth: Items
noRot: false
Делает объект поднимаемым и переносимым:
- type: Item
size: Small # Размер: Tiny, Small, Normal, Large, Huge
shape: # Форма в инвентаре (опционально)
- 0,0,0,0 # Список прямоугольников (Box2i)
heldPrefix: "held_" # Префикс для спрайта в руках
storedRotation: 90 # Поворот при хранении
Определяют физические свойства сущности:
- type: Physics
bodyType: Dynamic
- type: Fixtures
fixtures:
fix1:
shape: !type:PhysShapeAabb
bounds: "-0.25,-0.25,0.25,0.25"
density: 10
mask: [ItemMask]
layer: [ItemLayer]
Позволяют сущности получать урон и разрушаться:
- type: Damageable
damageContainer: Biological
- type: Destructible
thresholds:
- trigger:
!type:DamageTypeTrigger
damageType: Blunt
damage: 100
behaviors:
- !type:SpawnEntitiesBehavior
spawn:
Meat:
min: 1
max: 2
Для спавна сущностей используйте IEntityManager:
using Robust.Shared.Prototypes;
using Robust.Shared.Map;
public class MySystem : EntitySystem
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
public void SpawnItemAtLocation(string prototypeId, EntityUid targetEntity)
{
// Получаем координаты целевой сущности
var transform = _entityManager.GetComponent<TransformComponent>(targetEntity);
// Спавн сущности по ID прототипа
var item = _entityManager.SpawnEntity(prototypeId, transform.Coordinates);
Console.WriteLine($"Создан предмет: {_entityManager.GetComponent<MetaDataComponent>(item).EntityPrototype?.ID}");
}
public void SpawnItemAtPosition(string prototypeId, Vector2 worldPosition)
{
// Создаём координаты из мировых координат
var coordinates = new EntityCoordinates(EntityUid.Invalid, worldPosition);
// Спавн через EntityPrototype
if (_prototypeManager.TryIndex<EntityPrototype>(prototypeId, out var prototype))
{
var item = _entityManager.SpawnEntity(prototype, coordinates);
}
}
}
Вы можете динамически добавлять и удалять компоненты из сущностей:
using Robust.Shared.GameObjects;
public class MySystem : EntitySystem
{
public void MakeItemIndestructible(EntityUid item)
{
// Проверяем, есть ли у сущности компонент Damageable
if (_entityManager.TryGetComponent(item, out DamageableComponent? damageable))
{
// Удаляем компонент
_entityManager.RemoveComponent<DamageableComponent>(item);
}
// Проверяем, есть ли Destructible
if (_entityManager.TryGetComponent(item, out DestructibleComponent? destructible))
{
_entityManager.RemoveComponent<DestructibleComponent>(item);
}
}
public void AddLightToEntity(EntityUid entity, Color color, float radius)
{
// Добавляем компонент PointLight
_entityManager.AddComponent<PointLightComponent>(entity);
// Настраиваем компонент
var light = _entityManager.GetComponent<PointLightComponent>(entity);
light.Color = color;
light.Radius = radius;
light.Energy = 1f;
}
}
/Resources/Prototypes/ - Основная директория с прототипами игры/Resources/Prototypes/Entities/ - Прототипы сущностей/Resources/Prototypes/Entities/Mobs/ - Прототипы мобов/Resources/Prototypes/Entities/Objects/ - Прототипы объектов/RobustToolbox/Robust.Shared/Prototypes/ - Исходный код системы прототиповЕсли вы новичок, начните с простого:
/Resources/Prototypes//Resources/Prototypes/Entities/Objects/Tools/)Пример создания простого предмета - "Моя Лампа":
# /Resources/Prototypes/Entities/Objects/MyLamp.yml
- type: entity
id: MyLamp
name: "Моя Лампа"
description: "Простая лампа, которую я создал!"
components:
- type: Sprite
sprite: Objects/Lights/table_lamp.rsi
state: icon
- type: Item
size: 1
- type: Physics
bodyType: Dynamic
- type: Fixtures
fixtures:
fix1:
shape: !type:PhysShapeAabb
bounds: "-0.25,-0.25,0.25,0.25"
density: 5
mask: [ItemMask]
layer: [ItemLayer]
- type: PointLight
color: "#ffffaa"
radius: 3
energy: 0.8
- type: Damageable
damageContainer: Inorganic
- type: Destructible
thresholds:
- trigger:
!type:DamageTypeTrigger
damageType: Blunt
damage: 5
behaviors:
- !type:SpawnEntitiesBehavior
spawn:
GlassSheet:
min: 1
max: 1
Для сервера Lost Paradise новые прототипы должны быть размещены в папке /Resources/Prototypes/_LP/. Это позволяет легко различать контент, специфичный для данного сервера.
Структура папки _LP:
_LP/...
Catalog/ // Каталог для торговых автоматов
Datasets/ // Локализация и наборы данных
Entities/ // Сущности
- Clothing/ // Одежда
- Markers/ // Маркеры для спавна
- Objects/ // Объекты
- Structures/ // Структуры
Guidebook/ // Гид по игре
Loadouts/ // Наборы оборудования для ролей
Objectives/ // Объективы для раундов
Reagents/ // Реагенты для химии
Recipes/ // Рецепты для станков и микроволновок
SoundCollections/ // Звуковые коллекции
Пример: Если вы создаёте новый предмет для Lost Paradise, разместите его в /Resources/Prototypes/_LP/Entities/Objects/.
Уникальные ID: Всегда используйте уникальные имена. Лучше всего использовать описательные названия:
Wrench или TableLampItem1 или Obj2Абстрактные прототипы: Используйте абстрактные прототипы для шаблонов:
- type: entity
id: BaseLight
abstract: true
components:
- type: Sprite
- type: PointLight
- type: Item
- type: Physics
Наследование: Максимально используйте наследование для уменьшения дублирования кода:
- type: entity
id: DeskLamp
parent: BaseLight
name: "Настольная лампа"
components:
- type: Sprite
sprite: Objects/Lights/desk_lamp.rsi
state: icon
- type: PointLight
color: "#ffcc88"
radius: 2.5
Комментарии: Добавляйте комментарии для пояснения:
# LP edit start
- type: entity
id: ExplosiveBarrel
name: "Взрывоопасный бочка"
description: "Бочка с нефтью. Может взорваться от открытого огня."
# Доп. комментарий: Не спавнить в техах
# LP edit end
Ответ: Используйте SpriteComponent и укажите путь к RSI-файлу:
- type: Sprite
sprite: Objects/MyCustomItem.rsi
state: icon
Ответ: Добавьте ItemComponent:
- type: Item
size: Small # Размер: Tiny, Small, Normal, Large, Huge
Ответ: Добавьте PhysicsComponent и FixturesComponent:
- type: Physics
bodyType: Dynamic
- type: Fixtures
fixtures:
fix1:
shape: !type:PhysShapeAabb
bounds: "-0.25,-0.25,0.25,0.25"
density: 10
Ответ: Используйте PointLightComponent:
- type: PointLight
color: "#ff0000" # это HEX цвет "#ff0000"
radius: 4
energy: 1.0
Ответ: Создайте или отредактируйте FTL-файл в /Resources/Locale/:
# /Resources/Locale/ru-RU/ss14-ru/prototypes/_lp/entities/objects/myitem.ftl
ent-MyCustomItem = Мой Кастомный Предмет
.desc = Это очень полезный предмет, который я создал для Lost Paradise
.suffix = DEBUG
Для поддержки разных языков в SS14 используются FTL-файлы (Fluent Localization). Названия и описания прототипов задаются в папке /Resources/Locale/.
Для сервера Lost Paradise локализация размещается в:
/Resources/Locale/ru-RU/ss14-ru/prototypes/_lp//Resources/Locale/en-US/ss14-ru/prototypes/_lp/Каждый прототип имеет уникальный ключ в формате:
ent-<PrototypeID> = Название предмета
.desc = Описание предмета
.suffix = Суффикс для меню спавна (опционально)
Пример:
# /Resources/Locale/ru-RU/ss14-ru/prototypes/_lp/entities/objects/fun/pillows.ftl
ent-LPPAuroraPillow = дакимакура Авроры
.desc = Красивая подушка с изображением Авроры
ent-LPPCalliePillow = дакимакура Келли
.desc = Подушка с милой Келли
.suffix = Limited Edition
Когда игра запускается, она:
ent- для сущностейtag-reagent-sound-.desc для описания и .suffix для суффиксаВ SS14 все спрайты хранятся в формате RSI (Resource Sprite Image) - это специальный формат, который объединяет все состояния спрайта и его метаданные в одном каталоге.
Каждый RSI - это папка с расширением .rsi, содержащая:
Пример структуры RSI для игрушки (ToyKatana):
ToyKatana.rsi/...
- meta.json # метаданные
- icon.png # основное состояние (32x32)
- equipped-BACKPACK.png # когда надето на слот рюкзака
- equipped-BELT.png # когда надето на слот пояса
- inhand-left.png # в левой руке
- inhand-right.png # в правой руке
Meta.json описывает структуру спрайта:
{
"version": 1, // версия формата (всегда 1)
"license": "CC-BY-SA-3.0", // лицензия на спрайт
"copyright": "", // авторские права (опционально)
"size": { // размер одного спрайта в пикселях
"x": 32,
"y": 32
},
"states": [ // список всех состояний спрайта
{
"name": "icon" // основное состояние
},
{
"name": "equipped-BACKPACK", // состояние: надето на слот рюкзака
"directions": 4 // число направлений (0-3 = север/восток/юг/запад)
},
{
"name": "normal-unshaded", // состояние с анимацией
"delays": [ // задержки между кадрами анимации (в секундах)
[1.5, 0.5, 1.5, 0.5]
]
}
]
}
Для использования спрайта в прототипе используйте SpriteComponent:
- type: entity
id: ToyKatana
name: "Игрушечный клинок"
description: "Маленький игрушный клинок для детей."
components:
- type: Sprite
sprite: _LP/Objects/Fun/ToyKatana.rsi # путь к RSI
state: icon # начальное состояние
- type: Item
size: Small
- type: Physics
bodyType: Dynamic
Для создания анимации добавьте поле delays в meta.json:
{
"states": [
{
"name": "blinking",
"delays": [
[0.1, 0.1, 0.5, 0.1, 0.1] // задержки для каждого кадра
]
}
]
}
А затем в коде или прототипе можно управлять анимацией:
- type: entity
id: AnimatedLight
name: "Анимированная лампа"
components:
- type: Sprite
sprite: Objects/Lights/animated.rsi
state: off
- type: PointLight
color: "#ffff00"
radius: 3
- type: LightController
animationStates:
off: { state: "off" }
on: { state: "blinking" }
Для объектов, которые могут быть повернуты, используйте directions:
{
"states": [
{
"name": "icon",
"directions": 4 // 4 направления (север, восток, юг, запад)
}
]
}
Каждая направление соответствует индексу:
.rsi/Resources/Textures/_LP//Resources/Textures/_LP/Objects//Resources/Textures/_LP/Clothing//Resources/Textures/_LP/Structures/В файле meta.json для RSI спрайтов указывайте авторские права:
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Создано YourName для Lost Paradise",
"size": { "x": 32, "y": 32 },
"states": [...]
}
Важно: Указывайте авторские права только если контент оригинальный (создан вами с нуля). Если вы используете чужой контент:
Пример для заимствованного контента:
{
"license": "CC-BY-SA-3.0",
"copyright": "Taken from BlueMoon github at commit https://github.com/BlueMoon-Labs/...",
"size": { "x": 32, "y": 32 }
}
Для звуков создайте файл attributions.yml в той же папке, где находятся звуковые файлы:
# /Resources/Audio/_LP/Effects/Admin/attributions.yml
- files: ["Effect_CHelp.ogg"]
license: "CC0-1.0"
copyright: "by BitBoxxxer on GitHub"
source: "https://github.com/BitBoxxxer"
Поля attributions.yml:
files - список звуковых файловlicense - лицензия (CC0-1.0, CC-BY-SA-3.0, CC-BY-NC-SA-3.0 и т.д.)copyright - автор или источникsource - ссылка на оригинал (опционально)ВАЖНО: Все звуки в SS14 должны быть в формате OGG (Ogg Vorbis). Другие форматы (MP3, WAV) не поддерживаются!
/Resources/Audio/_LP/
├── Effects/ # Звуковые эффекты
│ ├── Admin/ # Админские звуки
│ │ ├── attributions.yml
│ │ └── Effect_CHelp.ogg
│ └── whip.ogg
├── Lobby/ # Музыка для лобби
│ ├── attributions.yml
│ └── *.ogg
├── StationEvents/ # Звуки событий станции
└── Weapons/ # Звуки оружия
# Пример использования звука в прототипе
- type: entity
id: MyItem
components:
- type: EmitSoundOnUse
sound:
path: /Audio/_LP/Effects/whip.ogg
params:
volume: -5
- type: EmitSoundOnLand
sound:
path: /Audio/_LP/Effects/whip.ogg
Для каждого каталога со звуками создайте attributions.yml:
# Пример для нескольких файлов
- files: ["sound1.ogg", "sound2.ogg"]
license: "CC-BY-SA-3.0"
copyright: "Created by YourName for Lost Paradise"
source: "https://github.com/Lost-Paradise"
# Для оригинального контента (ваши звуки)
- files: ["my_original_sound.ogg"]
license: "CC0-1.0"
copyright: "by YourName on GitHub"
source: "https://github.com/YourName"
# Для заимствованного контента
- files: ["borrowed_sound.ogg"]
license: "CC-BY-SA-3.0"
copyright: "Original author name"
source: "https://example.com/original-source"
/Resources/Audio/_LP//Resources/Audio/_LP/Effects//Resources/Audio/_LP/Lobby//Resources/Audio/_LP/Weapons//proto команды для отладкиEffects (Эффекты) - это типизированные объекты, которые выполняют определённые действия при наступлении событий в игре. В отличие от простых строк, effects в SS14 имеют структуру, параметры и связь с C# кодом.
Важно понимать: Effects НЕ являются просто текстовым описанием. Каждый effect - это реальный C# класс, который выполняет конкретное действие в игре.
Effects применяются в нескольких контекстах:
EntityEffect - базовый класс для всех эффектов, применяемых к сущностям. Рассмотрим реальные примеры из игры.
YAML прототип реагента:
# /Resources/Prototypes/Reagents/medicine.yml
- type: reagent
id: Dylovene
name: reagent-name-dylovene
group: Medicine
desc: reagent-desc-dylovene
color: "#3a1d8a"
metabolisms:
Medicine:
effects:
- !type:HealthChange # Тип эффекта
damage: # Параметры урона/лечения
types:
Poison: -1 # -1 означает лечение яда
C# код эффекта:
// /Content.Shared/EntityEffects/Effects/Damage/HealthChangeEntityEffectSystem.cs
public sealed partial class HealthChange : EntityEffectBase<HealthChange>
{
[DataField(required: true)]
public DamageSpecifier Damage = default!;
[DataField]
public bool IgnoreResistances = true;
}
Как это работает:
HealthChangeYAML прототип:
# /Resources/Prototypes/Reagents/Consumable/Drink/base_drink.yml
- type: reagent
id: Water
name: reagent-name-water
color: "#80a0ff"
metabolisms:
Drink:
effects:
- !type:SatiateThirst
factor: 3.0 # Множитель утоления жажды
C# код эффекта:
// /Content.Shared/EntityEffects/Effects/Body/SatiateEntityEffectSystem.cs
public sealed partial class SatiateThirst : Satiate<ThirstComponent>
{
// factor - множитель для базового значения
}
YAML прототип реакции:
# /Resources/Prototypes/Recipes/Reactions/chemicals.yml
- type: reaction
id: PotassiumExplosion
impact: High
priority: 20
conserveEnergy: false
reactants:
Water:
amount: 1
Potassium:
amount: 1
effects:
- !type:Explosion
explosionType: Default
intensityPerUnit: 0.25 # Интенсивность на единицу реагента
maxTotalIntensity: 100
intensitySlope: 4
maxIntensity: 7
Как это работает:
ExplosionYAML прототип:
# /Resources/Prototypes/Recipes/Reactions/food.yml
- type: reaction
id: Dough
reactants:
Flour:
amount: 5
Water:
amount: 5
effects:
- !type:SpawnEntity
entity: FoodDough # Какую сущность создать
Как это работает:
FoodDough (тесто)YAML прототип:
# /Resources/Prototypes/Reagents/medicine.yml
- type: reagent
id: Cryptobiolin
name: reagent-name-cryptobiolin
group: Medicine
metabolisms:
Medicine:
effects:
- !type:ModifyStatusEffect
effectProto: StatusEffectScrambled # Прототип статус-эффекта
type: Update # Тип действия: Add/Remove/Update
- !type:ModifyStatusEffect
effectProto: StatusEffectWoozy
time: 20.0 # Длительность в секундах
LoadoutEffect используется для ограничения доступности предметов в лодауте (снаряжении персонажа).
YAML прототип:
# /Resources/Prototypes/Loadouts/Miscellaneous/jobtrinkets.yml
- type: loadout
id: LizardPlushieCaptain
effects:
- !type:JobRequirementLoadoutEffect
requirement:
!type:RoleTimeRequirement
role: JobCaptain
time: 3600 # Требуется 1 час (3600 сек) игры за Капитана
storage:
back:
- PlushieLizard
Как это работает:
JobRequirementLoadoutEffectYAML прототип:
# /Resources/Prototypes/Loadouts/Miscellaneous/survival.yml
- type: loadoutEffectGroup
id: OxygenBreather
effects:
- !type:SpeciesLoadoutEffect
species:
- Arachnid
- Diona
- Dwarf
- Human
- Moth
- Reptilian
- Vulpkanin
Как это работает:
YAML прототип:
# /Resources/Prototypes/Loadouts/Miscellaneous/survival.yml
- type: loadout
id: EmergencyOxygen
effects:
- !type:GroupLoadoutEffect
proto: OxygenBreather # Ссылка на группу эффектов
storage:
back:
- BoxSurvival
Если вам нужен новый эффект, создайте C# класс:
Шаг 1: Создайте класс эффекта
// /Content.Shared/EntityEffects/Effects/MyCustomEffect.cs
using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;
namespace Content.Shared.EntityEffects.Effects;
/// <summary>
/// Кастомный эффект для примера - восстанавливает выносливость.
/// </summary>
public sealed partial class RestoreStamina : EntityEffectBase<RestoreStamina>
{
/// <summary>
/// Количество восстанавливаемой выносливости.
/// </summary>
[DataField]
public float Amount { get; private set; } = 50f;
/// <summary>
/// Игнорировать ли ограничения.
/// </summary>
[DataField]
public bool IgnoreLimits { get; private set; } = false;
}
Шаг 2: Создайте систему для эффекта
// /Content.Server/EntityEffects/Effects/RestoreStaminaEntityEffectSystem.cs
using Content.Shared.EntityEffects;
using Content.Shared.Stamina;
namespace Content.Server.EntityEffects.Effects;
public sealed partial class RestoreStaminaEntityEffectSystem : EntityEffectSystem<StaminaComponent, RestoreStamina>
{
protected override void Effect(Entity<StaminaComponent> entity, ref EntityEffectEvent<RestoreStamina> args)
{
var stamina = entity.Comp;
var amount = args.Effect.Amount * args.Scale;
stamina.CurrentStamina = args.Effect.IgnoreLimits
? stamina.CurrentStamina + amount
: Math.Min(stamina.CurrentStamina + amount, stamina.MaxStamina);
}
}
Шаг 3: Используйте в YAML
# /Resources/Prototypes/Reagents/my_reagents.yml
- type: reagent
id: StaminaPotion
name: reagent-name-stamina-potion
color: "#00ff00"
metabolisms:
Medicine:
effects:
- !type:RestoreStamina
amount: 25.0
ignoreLimits: false
Эффекты могут иметь условия, при которых они срабатывают:
Пример с условиями:
# /Resources/Prototypes/Reagents/medicine.yml
- type: reagent
id: Dylovene
metabolisms:
Medicine:
effects:
# Базовый эффект - всегда лечит яд
- !type:HealthChange
damage:
types:
Poison: -1
# Побочный эффект - только при передозировке (20+ единиц)
- !type:HealthChange
conditions:
- !type:ReagentCondition
reagent: Dylovene
min: 20 # Минимум 20 единиц в организме
damage:
groups:
Brute: 2 # Наносит грубый урон
# Рвота при передозировке
- !type:Vomit
conditions:
- !type:ReagentCondition
reagent: Dylovene
min: 20
probability: 0.02 # 2% шанс каждый тик
| Тип эффекта | Описание | Пример использования |
|---|---|---|
HealthChange |
Изменение здоровья | Лекарства, яды |
SatiateThirst |
Утоление жажды | Напитки |
SatiateHunger |
Утоление голода | Еда |
Explosion |
Создание взрыва | Химические реакции |
SpawnEntity |
Спавн сущности | Реакции приготовления |
ModifyStatusEffect |
Статус-эффекты | Наркотики, лекарства |
Electrocute |
Электрошок | Электрические реакции |
Vomit |
Рвота | Пищевое отравление |
Jitter |
Дрожь | Кофеин, передозировка |
Drunk |
Опьянение | Алкоголь |
AdjustReagent |
Изменение реагента | Превращение веществ |
Extinguish |
Тушение огня | Вода на горящем |
Flammable |
Воспламенение | Горючие вещества |
PopupMessage |
Сообщение игроку | Уведомления |
Emote |
Эмоция | Смех, кашель |
Попробуйте создать реагент, который:
Решение:
- type: reagent
id: MyHealingReagent
name: reagent-name-my-healing
color: "#ff8888"
metabolisms:
Medicine:
effects:
# Лечение грубого урона
- !type:HealthChange
damage:
groups:
Brute: -2
# Дрожь при передозировке
- !type:Jitter
conditions:
- !type:ReagentCondition
reagent: MyHealingReagent
min: 15
# Случайное сообщение
- !type:PopupMessage
type: Local
visualType: Small
messages: [ "generic-reagent-effect-tingling" ]
probability: 0.05
Автор: Кейси
Discord: nimf0374