Skip to content

refactor: class-based units and abilities#269

Merged
olistic merged 39 commits into
masterfrom
refactor/class-based-units-and-abilities
Mar 17, 2026
Merged

refactor: class-based units and abilities#269
olistic merged 39 commits into
masterfrom
refactor/class-based-units-and-abilities

Conversation

@olistic
Copy link
Copy Markdown
Owner

@olistic olistic commented Mar 16, 2026

Summary

  • Replace curried ability factory functions with Action/Sense classes extending an Ability base class, using .with() for deferred config
  • Convert unit definitions (Sludge, Archer, etc.) from plain objects to classes extending MeleeUnit/RangedUnit abstract intermediaries that encapsulate shared playTurn behavior
  • Update engine (loadLevel, getNextTurn, getAbilities) to use instanceof Action instead of ability.action boolean, and handle both class instances and legacy config formats
  • Update both tower definitions and CLI's renderTypes for the new ability/unit patterns

Test plan

  • All 675 tests pass across 92 test files
  • Full build succeeds for all 9 packages
  • Lint passes with no issues
  • Integration tests (runLevel.test.ts) exercise the full pipeline end-to-end

🤖 Generated with Claude Code

olistic and others added 9 commits March 16, 2026 20:41
Introduce abstract base classes for the class-based ability
architecture. Ability provides unit reference, abstract description/meta,
and a static .with() factory for deferred config. Action and Sense
extend it with void and any return types respectively.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Convert walk, attack, shoot, bind, rescue, pivot, rest, and detonate
from curried factory functions to classes extending Action. Abilities
with config params define static .with() methods returning
AbilityBindings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Convert feel, look, listen, health, maxHealth, think, directionOf,
directionOfStairs, and distanceOf from curried factory functions to
classes extending Sense.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove old Ability interface and AbilityCreator type. Fix AbilityBinding
to use a concrete constructor signature compatible with subclass
constructors. Export AbilityMeta instead of removed types.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace ability.action boolean with instanceof Action in getNextTurn
and getAbilities. Add @warriorjs/abilities as core dependency. Update
loadLevel to handle AbilityBinding, bare classes, and legacy factories.
Update tests to use class-based abilities.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Handle AbilityBinding, bare classes, and legacy factories when reading
ability metadata for types.ts generation. Use instanceof Action for
action/sense classification.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace factory function calls with AbilityBinding (.with()) and bare
class references in unit ability declarations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace factory function calls with .with() bindings and bare class
references in both tower definitions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Units are now classes extending Unit from core, with MeleeUnit and
RangedUnit abstract intermediaries providing shared playTurn behavior.
Tower configs use { unit: new Sludge(), position: ... } instead of
spreading plain objects. Engine handles both formats via
loadUnitFromInstance/loadUnitFromConfig. Deep clone preserves class
instances.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@olistic olistic force-pushed the refactor/class-based-units-and-abilities branch from be78d3a to 3a7078f Compare March 16, 2026 23:41
@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 16, 2026

Codecov Report

❌ Patch coverage is 95.50562% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 96.29%. Comparing base (b9d7505) to head (bb50e42).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
libs/core/src/loadLevel.ts 68.96% 8 Missing and 1 partial ⚠️
apps/cli/src/utils/renderTypes.ts 77.77% 2 Missing ⚠️
libs/core/src/Unit.ts 85.71% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #269      +/-   ##
==========================================
- Coverage   96.80%   96.29%   -0.51%     
==========================================
  Files          98      101       +3     
  Lines        1595     1647      +52     
  Branches      368      373       +5     
==========================================
+ Hits         1544     1586      +42     
- Misses         41       50       +9     
- Partials       10       11       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

olistic and others added 10 commits March 17, 2026 09:29
The Powder Keep tips and clues reference method calls players write
(e.g. warrior.feel().isEmpty()), not ability config declarations. The
parentheses were incorrectly removed during the class-based refactor.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rename ability files from camelCase to PascalCase (e.g., attack.ts →
Attack.ts) to match the exported class names. Update all imports across
abilities, units, core, cli, and tower packages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move readonly fields (description, meta) before private fields and
reorder constructor assignments for consistency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move the foundational ability base classes and types (Ability, Action,
Sense, AbilityBinding, AbilityMeta) from @warriorjs/abilities to
@warriorjs/core. Core no longer depends on abilities; abilities
re-exports the base classes from core. Tests for base classes move to
core; concrete ability tests import from @warriorjs/core.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the local Ability interface in Unit.ts with an import of the
Ability class from core, removing the duplicate definition.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add Effect base class to core with abstract passTurn/trigger methods.
Convert ticking from a curried factory to a Ticking class extending
Effect with static .with() for config. Update loadEffects to handle
both class bindings and legacy factories. Remove local Effect interface
from Unit.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add EffectBinding type to core's Effect.ts mirroring AbilityBinding,
and export from both core and effects packages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rename ticking.ts to Ticking.ts to match the class name, consistent
with the abilities package convention.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@olistic olistic force-pushed the refactor/class-based-units-and-abilities branch from 4a7343b to 37b5bbb Compare March 17, 2026 15:50
olistic and others added 8 commits March 17, 2026 12:52
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove re-exports of Ability, Action, Sense, AbilityBinding,
AbilityMeta, Effect, and EffectBinding from @warriorjs/abilities and
@warriorjs/effects. Consumers now import these directly from
@warriorjs/core.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace TowerFloorUnit with TowerUnitEntry and TowerWarriorEntry.
Remove loadUnitFromConfig and the legacy factory paths in loadAbilities
and loadEffects — units are always class instances now.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tower configs now declare unit classes (e.g. unit: Sludge) instead of
instances (unit: new Sludge()). The engine instantiates them at level
load time, consistent with how ability classes are already handled.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Consistent with the Powder Keep style.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove Warrior from @warriorjs/units — each tower defines its own
  sharedWarriorConfig typed as Pick<WarriorConfig, ...>
- Replace UnitConfig and TowerWarriorEntry with WarriorConfig
- Rename TowerUnitEntry to UnitConfig (the natural name now that the
  legacy plain-object format is gone)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add WarriorDefinition, WarriorOverrides, and LevelDefinition types for
the tower definition format. Update getLevelConfig to accept
TowerDefinition and merge tower.warrior with level.warrior at config
resolution time.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
olistic and others added 12 commits March 17, 2026 17:11
Tower constructor now accepts warrior (WarriorDefinition) and uses
LevelDefinition for levels. Update loadTowers to extract warrior from
tower modules.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Define warrior identity (character, color, maxHealth) once at the tower
level instead of spreading sharedWarriorConfig into every level. Each
level now only declares abilities and position.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add optional static declaredAbilities to the Unit base class and a
UnitClass interface for the constructor + static shape. The engine reads
abilities from the class at load time, removing the any cast.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add AbilityClass, EffectClass interfaces alongside AbilityBinding and
EffectBinding. Export AbilityEntry and EffectEntry union types. Move
UnitClass interface to Unit.ts. All Record<string, any> in config types
are now properly typed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…orts

These are implementation details used only within types.ts. External
consumers use AbilityBinding/AbilityEntry and EffectBinding/EffectEntry.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Convert playTurn from a constructor-assigned arrow function to a class
method on Unit, MeleeUnit, and RangedUnit. Add exported Turn type for
the public turn interface and internal TurnState for engine tracking.
Initialize turn as null instead of empty object.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove duplicated playTurn behavior tests from Sludge, ThickSludge,
Archer, and Wizard — now covered by MeleeUnit and RangedUnit tests.
Concrete tests focus on identity and declared abilities only.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@olistic olistic merged commit f774173 into master Mar 17, 2026
4 of 6 checks passed
@olistic olistic deleted the refactor/class-based-units-and-abilities branch March 17, 2026 22:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant