t1k:unity:dots-combat:rpg
| Field | Value |
|---|---|
| Module | dots-combat |
| Version | 2.3.8 |
| Effort | medium |
| Tools | — |
Keywords: auto battler, auto-battler, battle strip, BDP, behavior tree, board placement, boss, bullet heaven, combat, combat strip, cross-demo reuse, damage numbers, damage popup, DOTS, draft shop, role-playing, round phase, RPG, spawn troops, survivor, thin demo wrapper, troop spawn, wave
How to invoke
Section titled “How to invoke”/t1k:unity:dots-combat:rpgDOTS Multi-Package Library Reference
Section titled “DOTS Multi-Package Library Reference”com.the1studio.dots-rpg is DELETED. Replaced by 7 focused packages:
| Package | Namespace | Modules |
|---|---|---|
| com.the1studio.dots-core | DOTSCore.* | Core, Stats, Navigation, Spawning, Structures, Camera, Save/Load |
| com.the1studio.dots-combat | DOTSCombat.* | Combat, Skills, Boss, Synergy, Talent, Summon, Placement |
| com.the1studio.dots-ai | DOTSAI.* | AI perception, aggro, patrol, party, leash |
| com.the1studio.dots-bdp | DOTSBDP.* | Behavior Designer Pro tasks + throttle system |
| com.the1studio.dots-inventory | DOTSInventory.* | Inventory, equipment, loot, crafting, currency, set bonuses |
| com.the1studio.dots-progression | DOTSProgression.* | Upgrade, Achievement, Faction, Quest, Dialogue, World |
| com.the1studio.dots-puzzle | DOTSPuzzle.* | Match-3/puzzle board, cascade, scoring |
| com.the1studio.dots-battlefield | DOTSBattlefield.* | Arena geometry, terrain, NavMesh (existing) |
Related skills: dots-ecs-core, dots-jobs-burst, dots-physics, dots-graphics, agents-navigation, dots-architecture, dots-inventory-grid, behavior-designer-pro
Cross-Demo Reuse — READ FIRST
Section titled “Cross-Demo Reuse — READ FIRST”Before writing ANY combat code in a demo, audit references/cross-demo-reuse-patterns.md. The library already covers ~80% of typical combat-demo needs (damage numbers, auto-battler round/phase flow, troop spawn, waves, boss encounters, behavior trees). The remaining ~20% is authoring + genre-specific glue.
Common library entry points (full table + decision flow in the reference doc):
- Floating damage numbers →
DOTSCombat.Survivor.DamageNumberEvent+DamageNumberSystem - XP gem drops →
DOTSCombat.Survivor.DropsXPOnDeath+XPDropSystem - Auto-fire weapons (bullet heaven) →
DOTSCombat.Survivor.AutoWeapon+AutoWeaponSystem - Round/phase state machine (auto-battler) →
DOTSCombat.AutoBattler.RoundPhase+RoundManagementSystem - Draft shop / board placement →
DOTSCombat.AutoBattler.DraftShop+BoardPlacementSystem - Continuous troop spawn →
DOTSCore.Spawning.SpawnerConfig+SpawnState+SpawnSystem - Ring / edge / formation spawn →
RingSpawnRequest/EdgeSpawnPattern/FormationSpawnerConfig - Wave schedules →
DOTSCore.Spawning.WaveDefinition+WaveManagerSystem - Boss phase + ability + combo →
DOTSCombat.Boss.BossPhase+BossAbilitySelectionSystem+BossComboSystem - Pathfinding / avoidance → set
DOTSCore.Navigation.NavigationTarget→AgentNavigationBridgeSystembridges to ProjectDawn Agents Nav - Canonical melee/ranger/boss BDP trees →
DOTSBDP.Editor.BDPStandardTrees+BDPTreeBuilder.BuildAll(...)
Gold-standard “thin demo wrapper” template: Assets/Demos/RPG/BattleDemoSideView/ — 1 Runtime script (camera) + 4 Editor scripts; ALL gameplay is library composition.
Per rules/library-feature-discovery-protocol.md, if you need a feature this doc doesn’t mention, run the 3 grep research passes against Packages/unity-dots-library/ BEFORE implementing new code. Update this doc with whatever you find.
→ Full reference: references/cross-demo-reuse-patterns.md
When This Skill Triggers
Section titled “When This Skill Triggers”- Using DOTSCore., DOTSCombat., DOTSAI., DOTSBDP., DOTSInventory., DOTSProgression., DOTSPuzzle.*
- Adding Health, Mana, DamageEvent, StatusEffect, StatModifier, WaveState, SummonConfig, TalentNode components
- Implementing AI behaviors (aggro, patrol, flee, detection, party formation)
- Setting up skill casting, projectiles, AoE, summon, talent tree effects
- Working with inventory slots, equipment, loot drops, crafting, currency
- Configuring spawners, respawn mechanics, wave rounds
- Creating towers, barracks, walls, structure placement grid (Structures module)
- Implementing boss phases, abilities, combos (Boss module)
- Using CameraTarget, CameraTrauma, HitStopEvent (Camera module in DOTSCore)
- Triggering save/load via SaveStateRequest, LoadStateRequest (DOTSCore)
Package to Module Map
Section titled “Package to Module Map”| Module | Package | Key Types |
|---|---|---|
| Core (24c/9s) | dots-core | GameEntityTag, TeamId, MoveSpeed, NavigationTarget, Lifetime, RespawnReadyTag, PooledTag, EntityEvent, AudioEvent, CameraTarget, CameraShakeEvent, SaveableTag |
| Stats (9c/3s) | dots-core | BaseStats, DerivedCombatStats, DerivedResourceStats, DerivedLocomotion, Level, Experience, StatModifier, StatFormulaConfig |
| Navigation (6c/4s) | dots-core | AgentNavigationBridgeSystem, DOTSGroundingSystem, AgentCCOverrideSystem, NavigationAuthoring |
| Spawning (9c/5s) | dots-core | SpawnerConfig, SpawnState, HasSpawnedTag, SpawnTimer, SpawnedBy, RespawnConfig, RespawnTimer |
| Wave Manager (6c/4s) | dots-core | WaveDefinition, WaveState, WavePhase, WaveEvent, WaveConfig, WaveSpawnedTag |
| Camera (6c/5s) | dots-core | CameraTarget, CameraShakeEvent, CameraAutoZoomTarget, CameraTrauma, HitStopEvent, CameraAccessibility, CameraFocusRequest |
| Save/Load (4c/3s) | dots-core | SaveSlot (managed!), SaveComplete, LoadComplete, SavedComponentFlags, SaveConstants |
| Combat (28c/19s) | dots-combat | Health, Mana, Shield, DamageEvent, HealEvent, StatusEffect (MaxStacks, StackBehavior), DeadTag, InvulnerableTag, KnockbackEvent, BattleState, AttackConfig, OnHitEffect, OnDeathEffect, AuraEffect |
| Skills (11c/8s) | dots-combat | SkillSlot, ActiveCastState, ProjectileData, ParabolicArc, AreaEffect, HomingTarget, SkillBehaviorType, RangedAttackConfig |
| Boss (7c/4s) | dots-combat | BossTag, BossPhase, BossPhaseThreshold, BossAbility, ComboSequence, ComboState, PhaseTransitionEvent |
| Synergy/Trait (5c/3s) | dots-combat | TraitEntry, SynergyDefinition, ActiveSynergy, TeamSynergyState, SynergyModifierSource |
| Talent Tree (5c/2s) | dots-combat | TalentNode, TalentProgress, TalentPoints, TalentUnlockRequest, TalentEvent |
| Summon (5c/4s) | dots-combat | SummonConfig, SummonState, SummonRequest, SummonEvent, SummonedByTag |
| Structure Grid (6c/4s) | dots-combat | StructureGrid, StructureGridCell, PlaceRequest, PlaceEvent, GridPosition, DemolishRequest |
| AI (14c/8s) | dots-ai | PerceivedEntity, DetectionRange, AggroEntry, AILeash, AIConfig, TauntData, StealthState, AlertLevel, AlertTier |
| Party (6c/2s) | dots-ai | PartyMember, PartyRole, PartyLeaderTag, PartyFormation, FormationType, PartyBuff |
| BDP Tasks (17 tasks) | dots-bdp | EvaluateFlag throttle, BDPEvaluateFlagThrottleSystem + 17 ECS task implementations |
| Inventory (18c/8s) | dots-inventory | InventorySlot, EquippedItem, Item, LootTableEntry, EquipmentStatBonus, CraftingRecipe (currency moved → DOTSEconomy.WalletEntry; CurrencyWallet deprecated, see Gotchas) |
| Upgrade (4c/2s) | dots-progression | UpgradeLevel, UpgradeDefinition, UpgradeRequest, UpgradeEvent |
| Progression (6c/2s) | dots-progression | AchievementDefinition, AchievementProgress, FactionMembership, ReputationEntry |
| Quest/Dialogue (8c/3s) | dots-progression | QuestDefinition, QuestProgress, DialogueNode, DialogueState |
| World (7c/2s) | dots-progression | WorldTime, WorldTimeConfig, DayPhase, WeatherState, WeatherConfig |
| Puzzle (Board/Match) | dots-puzzle | BoardConfig, BoardCell, PieceData, MatchDetectionSystem, CascadeControlSystem |
Detail guides: references/core-guide.md, references/combat-guide.md, references/ai-guide.md, references/inventory-guide.md, references/new-modules-guide.md
Key Conventions
Section titled “Key Conventions”- Namespaces: DOTSCore, DOTSCombat, DOTSAI, DOTSBDP, DOTSInventory, DOTSProgression, DOTSPuzzle
- Assembly refs: Add separate asmdef reference per package — no single monolithic ref
- Constants: GameplayConstants (DOTSCore), CombatConstants, AIConstants, CameraConstants, SaveConstants — never hardcode. See references/constants-guide.md
- Teams: TeamId.Value (byte); 255 = no team
- Events (IBufferElementData): DamageEvent, HealEvent, KnockbackEvent, StatusEffect, AudioEvent, ScoreEvent are per-entity payloaded buffers; EntityEvent is singleton global queue
- Events (IEnableableComponent): LeveledUpEvent, WaveEvent, PhaseTransitionEvent, BuildEvent, DemolishEvent, SummonEvent, TalentEvent, UpgradeEvent, SaveComplete, LoadComplete are stateless one-frame signals
- Event cleanup: each system group has a
*EventCleanupSystem(OrderFirst) clearing its events via IJobEntity — seedots-rpg/references/event-conventions.md - Enableable tags: DeadTag, InvulnerableTag, CCState, KnockbackState — toggle via SetComponentEnabled
- CC masks: CCMasks.AttackBlock, .CastBlock, .MoveBlock — never inline bitmask expressions
- Combat formulas: CombatFormulas.ComputeAutoAttackDamage(), .EffectiveCooldown(), .FrameRngSeed() — never inline
- Stat pipeline: BaseStats -> StatModifier buffer -> DerivedStats -> StatSyncSystem syncs Health.Max/Mana.Max
- Burst rule: All systems Burst-compiled EXCEPT SaveSystem/LoadSystem (file I/O)
Submodule Sync — Always Pull Before Diagnosing API Drift
Section titled “Submodule Sync — Always Pull Before Diagnosing API Drift”If Packages/unity-dots-library/ (or any DOTS library) is consumed as a git submodule, and a consumer (Assets/Demos/** or another package) reports CS0103 'Foo' does not exist, CS0246 type/namespace 'Foo' could not be found, or CS0117 'Bar' does not contain a definition for 'Baz', the first hypothesis is submodule staleness, not library deletion.
Run THIS in the submodule before doing anything else:
cd Packages/unity-dots-librarygit fetch origin && git status -sbgit log --oneline HEAD..origin/main # what local is missingIf behind, the symbols probably exist on origin/main but were never pulled locally. git pull resolves it. Only AFTER confirming local is even with origin should you treat the missing API as a real deletion / migration target.
Why this matters: refactor commits frequently land utility helpers and consumer updates in the same commit. Pulling brings in both halves; staying stale shows only the consumer half — the helpers look “missing” when they exist remotely. Misdiagnosing this as a library API change wastes hours rewriting demos that just need a git pull.
After confirming the symbol really IS gone from origin/main, see the refactor/rename checklist in dots-architecture skill (references/refactor-rename-checklist.md).
RPG-Specific Anti-Patterns
Section titled “RPG-Specific Anti-Patterns”| Anti-Pattern | Fix |
|---|---|
| Importing old com.the1studio.dots-rpg | Add specific package refs: dots-core, dots-combat, etc. |
| Using old DOTSRPG.* namespace | Map: Core->DOTSCore, Combat->DOTSCombat, AI->DOTSAI, BDP->DOTSBDP |
| Inline magic numbers | Use domain constants class |
| Duplicate DamageEvent construction | Use DamageEvent.Create() factory |
| ISystem for TalentUnlockSystem | Use SystemBase — Entities 1.4 BufferTypeHandle two-pass invalidation |
| SetComponentEnabled before buffer reads | All buffer reads MUST complete before any SetComponentEnabled |
| SaveSystem/LoadSystem with [BurstCompile] | File I/O — Burst intentionally absent |
| WaypointSpeedZone mutating MoveSpeed.Value | Use WaypointFollower.SpeedMultiplier local field |
| Missing asmdef ref for cross-package type | Each package is separate — add explicit asmdef reference |
| Over-specific instance name for a library type | Name the generic ROLE, not one concrete instance. ArrowRegistryTag/TeamArrowPrefab → Projectile*; MageAttack* → ProjectileAttack*/CasterAttack*. See Gotcha “Generic component naming” + library-quality-mandate-unity.md § “Naming charter” (SSOT) |
General ECS anti-patterns: dots-ecs-core. Dimension-agnostic design: dots-architecture.
System Update Order: See references/system-ordering-guide.md for full ASCII diagram.
Gotchas
Section titled “Gotchas”-
ECS damage calculation in Burst-compiled jobs cannot allocate managed memory — pre-allocate buffers or use blob assets.
-
Stat modifications in IComponentData copy on write — entity stat changes need EntityManager.SetComponentData, not field mutation.
-
Save/load of ECS entities requires SubScene serialization — runtime-spawned entities are NOT saved by default.
-
GoldInterestSystem and PlayerHPSystem belong in the library, not demos — these are game-agnostic idle/RPG mechanics (gold compounding, HP regen) that multiple demos re-implement. Extract to
DOTSProgression.Idle(GoldInterestSystem) andDOTSCombat.Structures(PlayerHPSystem) when a second demo needs the same logic. Source: review-260520-round5-extraction-skills.md §B P0 -
dots-combatreward + structures systems consume the v2DOTSEconomywallet — NOT the deprecatedDOTSInventory.CurrencyWallet/CurrencyTransaction/CurrencyWalletUtility(W7 wallet v1→v2 cutover, 2026-05-28). One coherent contract across two subsystems:- Reward systems (
KillRewardCurrencySystem,Boss.BossRewardSystem) enqueueDOTSEconomy.WalletTransaction(wasCurrencyTransaction) onto the killer’s wallet buffer:{ CurrencyId, long Delta, WalletTransactionSource, FixedString64Bytes Reference }. Kill/boss loot usesWalletTransactionSource.Loot(ordinal 0, value-safe vs v1). SetReference = defaultwhen there is no audit tag. - Structures systems (
BuildValidationSystem,BuildExecutionSystem,DemolishSystem) query.WithAll<Wallet, WalletEntry>(), read viaWalletUtility.FindEntryIndex(buffer, CurrencyId)/WalletUtility.GetBalance(...)(sentinelWalletUtility.InvalidEntryIndex == -1), and mutateWalletEntry.Amountdirectly (parity-first; bypasses the ledger/event audit — intentional deferred follow-up, noted inBuildExecutionSystemremarks). - Component currency keys are now
DOTSEconomy.CurrencyIdenums, notint—KillRewardCurrency.CurrencyType,BossRewardCurrency.CurrencyType,BuildRequest.CurrencyType,DemolishRequest.RefundCurrencyType.CurrencyIdhas named tiersSoft=0/Hard=1/Premium=2/Energy=3/EventToken=4plusCustom0..Custom7(100-107) for game-specific currencies. BREAKING authoring change — buffers must be authored with enum values. - The
Wallettag is now REQUIRED on any reward/cost target (v1 had no tag). Any entity receiving rewards or paying build costs needs the full v2 buffer set:Wallettag +WalletEntry+WalletTransaction+WalletLedger+CurrencyEarnedEvent+CurrencySpentEvent+CurrencyInsufficientEvent(easiest: route throughDOTSEconomy.WalletAuthoring). Miss the tag/buffers →HasBuffer<WalletTransaction>is false,WalletSystemskips the entity, rewards/costs silently no-op.WalletEntrycap is 8 (was 4);longamounts (wasint).
- Reward systems (
-
HealthBarRenderer.targetLayerMUST be set for sub-camera demos. The library renderer (DOTSCore.UI.HealthBarRenderer) callsGraphics.DrawMeshInstancedProceduralwhich has alayerparameter that filters cameras — cameras whosecullingMaskdoes NOT include the layer silently SKIP the draw. Default is0(Default layer, rendered by Main Camera). If your demo uses a sub-camera RT (combat strip, mini-map, overlay) with a restricted cullingMask, you MUST setrenderer.targetLayer = YourSubCameraLayerIndexat scene-setup time, otherwise HP bars render fine on Main Camera but never appear in the sub-camera RT — even though entities haveHealth+HealthBarOffset+TeamId+LocalToWorld. TheHealthBarOffsetcomponent being present is necessary but not sufficient. Real incident 2026-05-26 in ChaosForge: enemies clearly taking damage (HP=40/60) but bars invisible in the combat strip RT. Fix:renderer.targetLayer = ChaosForgeCombatConstants.CombatStripWorldLayerinChaosForgeSceneSetup.cs:869. Library API was extended in commitbc91093(unity-dots-library) to exposetargetLayer(was hardcoded 0 prior); consumer wire-up landed in DOTS-AI commit2124a9af. -
Generic component naming — name the ROLE, not the instance. When creating any combat component/system/tag, name the generic role (
Projectile,Caster,Agent,Tile), never one concrete instance (Arrow,Mage,Sword,Knight). TheArrow*family (ArrowRegistryTag,TeamArrowPrefab,ArrowPrefabAuthoring) is over-specific: it really means “the projectile a ranged attacker fires”, and the library ALREADY has a genericProjectileData/ProjectileCollisionSystem— soArrowis an inconsistent island. Same smell:Mage*attack types (generic caster/projectile). A concrete instance belongs in an enum VALUE or demo content, not a type name. SSOT:rules/library-quality-mandate-unity.md§ “Naming charter”. -
Every
IUIModalHandlerneeds a system that EMITS itsEventType— modals with no producer are silent bugs. TheDOTSCore.UI.Bridge.UIEventBus+UIModalDirectorpattern decouples modals from triggers: a modal declarespublic int EventType => UIEventTypeRegistry.XYZ, registers viaIUIModalHandler, and waits for the bus to dispatch. The flaw: if NO production system ever writes aUIEventBus { EventType = XYZ }entry, the modal exists, compiles, has correct internal logic, but never opens. Tests can still pass (a test fixture writes the event directly viaEntityManager→ modal works in isolation) while the live demo silently has a dead modal. The compiler cannot catch “modal without emitter” because the contract is data-shaped (an integer event id), not type-shaped.Audit pattern (run on every kit/demo before milestone gates):
grep -rn 'public int EventType =>' Runtime/UI/ Runtime/**/UI/— list every IUIModalHandler.- For each, extract the event id constant name (e.g.
UIEventTypeRegistry.ItemCompare). grep -rn 'EventType.*=.*<name>' Runtime/excluding tests — find the producers.- If exactly 0 producers in production code → BUG: the modal has no trigger.
- The fix usually lives in whatever system OWNS the state-transition the modal exists to surface (e.g. the system that owns a Pending→Hold transition is the one that should emit the corresponding compare/decision modal).
Why this belongs in milestone gates: the bus pattern is what makes Mono-UI ↔ ECS decoupled, but the decoupling cuts both ways — the type system cannot detect the missing producer. Add this audit to every game-producer milestone gate or game-designer wiki-pass.
Related rules
Section titled “Related rules”rules/library-feature-discovery-protocol.md— research the library 3× before implementing new combat functionality; always update this skill regardless of outcomerules/manual-correction-implies-skill-gap-unity.md— if you find yourself injecting “use library X for Y” into a teammate brief, that knowledge belongs in this skillreferences/cross-demo-reuse-patterns.md— canonical reuse-first reference (this skill’s combat-side companion to the discovery protocol)