/**
* Враги демо-боя (перенос данных v1): «пепельные сгустки» — оживший пепел,
* просыпается от гулкого звука. Чистые данные — боевой системой и тестами.
* Вьюхи (воксельные модели) — на игре, поэтому `frames` v1 сюда не входит.
*/
export type EnemyKindId = 'crawler' | 'spitter' | 'heavy';
export interface EnemyKindDef {
id: EnemyKindId;
/** Здоровье. */
hp: number;
/** Скорость, юнитов/сек. */
speed: number;
/** Радиус тела для попаданий, юнитов. */
radius: number;
/** Дистанция начала атаки, юнитов. */
attackRange: number;
/** Урон атаки. */
attackDamage: number;
/** Пауза между атаками, сек. */
attackCooldown: number;
/** Замах до удара, сек (у ползуна 0 — контактный урон). */
windup: number;
/** Восстановление после удара, сек. */
recover: number;
/** Плевун держит дистанцию (юнитов) — отходит при сближении. */
keepDistance?: number;
/** Дроп «пепельной пыли». */
motes: number;
// --- ИИ (необязательные: withDefaults подставляет разумные значения) ---
/** Дистанция обнаружения героя (при прямой видимости), юнитов. */
aggroRange?: number;
/** Секунд без прямой видимости, после которых враг отступает к дому. */
deaggroSec?: number;
/** Ниже этой доли hp — отступает (0 — не отступает никогда). */
fleeBelowHpFraction?: number;
/** При отступлении убегает, пока герой ближе этого радиуса, юнитов. */
fleeRange?: number;
/** Радиус слуха шума (уровень >= 0.35), юнитов. */
hearRadius?: number;
/** Секунд настороженности без новой информации, после которых враг возвращается. */
warySec?: number;
}
/** Патруль отдельного спавна: точки маршрута (проходится туда-обратно) и пауза. */
export interface EnemyPatrolDef {
points: { x: number; z: number }[];
pauseSec: number;
}
/** Дефолты полей ИИ (контент задаёт только отличия). */
export function withDefaults(kind: EnemyKindDef): EnemyKindDef {
return {
...kind,
aggroRange: kind.aggroRange ?? 4,
deaggroSec: kind.deaggroSec ?? 2.5,
fleeBelowHpFraction: kind.fleeBelowHpFraction ?? 0,
fleeRange: kind.fleeRange ?? 4,
hearRadius: kind.hearRadius ?? 5,
warySec: kind.warySec ?? 2.5,
};
}
export const ENEMY_KINDS: Record<EnemyKindId, EnemyKindDef> = {
crawler: {
id: 'crawler',
hp: 3,
speed: 0.7,
radius: 0.2,
attackRange: 0.4,
attackDamage: 1,
attackCooldown: 0.8,
windup: 0,
recover: 0.3,
motes: 1,
// Сторожит пепел, шатаясь между кустов; слышит шаги вблизи.
hearRadius: 3.5,
},
spitter: {
id: 'spitter',
hp: 2,
speed: 0.55,
radius: 0.2,
attackRange: 3.0,
attackDamage: 1,
attackCooldown: 2.2,
windup: 0.35,
recover: 0.4,
keepDistance: 2.25,
motes: 1,
// Обстреливает издали и не отступает — стоит насмерть.
aggroRange: 5,
},
heavy: {
id: 'heavy',
hp: 8,
speed: 0.45,
radius: 0.3,
attackRange: 0.8,
attackDamage: 2,
attackCooldown: 2,
windup: 0.6,
recover: 0.5,
motes: 3,
// Крупный сгусток: глуховат, но с трудом убитый — отступает при малом hp.
hearRadius: 4,
fleeBelowHpFraction: 0.35,
fleeRange: 4.5,
warySec: 3,
},
};