Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
msw-git avatar

Msw Combat System

  • 4.3k installs
  • 33 repo stars
  • Updated July 29, 2026
  • msw-git/msw-ai-coding-plugins-official

msw-combat-system is an agent skill for MSW native combat covering attack resolution, damage hooks, knockback, game feel, AI FSM, and avatar combat motion.

About

msw-combat-system is an agent skill documenting the full MapleStory Worlds combat pipeline using native APIs for two-dimensional multi-genre games. Coverage spans attack resolution with AttackComponent and HitComponent Box Circle Polygon shapes, damage model hooks including CalcDamage CalcCritical GetCriticalDamageRate and HitEvent Extra, hit reaction with per-body knockback and IsHitTarget i-frames, and six native game-feel systems including Hit Stop camera shake zoom flash VFX and SFX. Combat state uses StateComponent with DeadEvent and ReviveEvent plus PlayerComponent HP, while event bus covers HitEvent AttackEvent StateChangeEvent and custom events. AI guidance combines StateComponent FSM with AIComponent behaviour trees and chase or wander components. Additional layers document DamageSkin components, HitEffectSpawnerComponent, and AvatarStateAnimationComponent combat motion mapping. Movement rules require OnUpdate delta-based MovementComponent MoveToDirection for bodied entities and Transform Translate for projectiles, forbidding timer-based teleport movement. References point to monster model assembly, HP gauge, projectile, and AI BT implementation files for full code patter.

  • Coverage matrix for attack, damage, hit reaction, game feel, combat state, events, and AI layers.
  • AttackComponent Box Circle Polygon shapes with Attack AttackFast and attackInfo tagging.
  • Six native game-feel systems: Hit Stop, Shake, Zoom, Flash, VFX, and SFX.
  • OnUpdate delta movement via MoveToDirection or Translate; forbids timer teleport patterns.
  • References for monster models, HP gauge, projectile, and AI behaviour tree implementations.

Msw Combat System by the numbers

  • 4,282 all-time installs (skills.sh)
  • +541 installs in the week ending Aug 2, 2026 (Skillselion tracking)
  • Ranked #8 of 247 Game Development skills by installs in the Skillselion catalog
  • Security screen: LOW risk (skills.sh audit)
  • Data as of Aug 2, 2026 (Skillselion catalog sync)
At a glance

msw-combat-system capabilities & compatibility

Capabilities
attack and hit shape resolution · damage and critical hook overrides · knockback and i frame hit filtering · native game feel effect integration · ai fsm and behaviour tree guidance
Use cases
frontend · api development
npx skills add https://github.com/msw-git/msw-ai-coding-plugins-official --skill msw-combat-system

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs4.3k
repo stars33
Security audit3 / 3 scanners passed
Last updatedJuly 29, 2026
Repositorymsw-git/msw-ai-coding-plugins-official

How do I implement MSW melee, ranged, damage, knockback, and monster AI using native AttackComponent and HitComponent APIs?

Integrate MSW native combat pipeline covering AttackComponent, HitComponent, damage hooks, knockback, game feel, AI FSM, and avatar combat motion.

Who is it for?

MSW developers building combat-capable monsters, projectiles, damage skins, and AI-driven attack behavior with native APIs.

Skip if: Skip when the task is avatar costume editing only or platform movement without combat hit resolution.

When should I use this skill?

User implements MSW attack hit damage combat, monster AI, projectiles, knockback, hit stop, or damage skin systems.

What you get

Correct MSW combat pipeline integration with native hit shapes, damage overrides, game-feel effects, and delta-based movement patterns.

  • combat hit pipeline
  • damage and knockback behaviors

Files

SKILL.mdMarkdownGitHub ↗

msw-combat-system

The full MSW combat pipeline. Covers only items in the common 2D combat layer that have MSW native API support, regardless of genre. Excludes formulas/theory. API signatures are based on Environment/NativeScripts/**/*.d.mlua.

---

0. Coverage matrix

#LayerNativeCustom required
1Attack ResolutionAttackComponent + HitComponent (Box/Circle/Polygon)Capsule/Cone/Ray, pierce count
2Damage ModelCalcDamage/CalcCritical/GetCriticalDamageRate/GetDisplayHitCount hooks + HitEvent.Extra:anyElement affinity, composite formulas
3Hit ReactionPer-Body knockback API, IsHitTarget-based i-frameStagger level, status effects
4Game FeelAll 6 native (Hit Stop, Shake, Zoom, Flash, VFX, SFX)
5Combat StateStateComponent + DeadEvent/ReviveEvent, PlayerComponent HP/reviveMP/Stamina/Rage, aggro
6Event BusHitEvent/AttackEvent/StateChangeEvent/PlayerActionEvent + custom @EventOnKill/OnBlocked
7AIStateComponent (FSM) + AIComponent (BT, 4 Composite types native) + AIChaseComponent/AIWanderComponent, _UserService.UserEntitiesDecorator/Memory(Blackboard), Threat Table
+Damage Skin3 DamageSkin* components + DamageSkinService
+Hit EffectHitEffectSpawnerComponent (auto)
+Avatar MotionAvatarStateAnimationComponent (State→MapleAvatarBodyActionState)

---

0.5 References — where to go

This SKILL.md covers only the system flow and native API surface. Actual model JSON, full script code, and variation patterns are in the references/* files below — Read them directly.

FileScopeWhen to read
`../msw-general/references/monster.md`Monster .model component assembly + ActionSheet + AI choice + canonical Pattern A scripts (Soldier-style) + HP/Respawn + spawn + verificationWhen building a combat-capable monster
`references/hp-gauge.md`Full implementation of an overhead HP bar based on PixelRendererComponentWhen attaching an overhead HP bar
`references/projectile.md`Projectile (Body-less entity + OnUpdate Translate) + homing/pierce/splash variantsWhen building ranged attacks like arrows, bullets, magic bolts
`references/ai-bt.md`BehaviourTree — AIComponent + 4 Composite types + @BTNode + custom Decorator/Memory/ThreatWhen you need BT-based monster/boss AI and multi-layer decision making
Priority: *this SKILL.md (concepts + API tables) → the relevant references/ (full implementation)**.

---

1. Attack Resolution

1-1. Shape & Attack trigger

HitComponent.ColliderType supports only Box / Circle / Polygon. Other shapes must be approximated by composition.

AttackComponent:
  Attack(Vector2 size, Vector2 offset, string attackInfo, CollisionGroup? cg)    → table<Component>
  Attack(Shape shape, string attackInfo, CollisionGroup? cg)                     → table<Component>
  AttackFast(Shape shape, string attackInfo, CollisionGroup? cg)                 → void   (for mass resolution, bullet hell)
  AttackFrom(Vector2 size, Vector2 position, string attackInfo, CollisionGroup? cg) → table<Component>
  emitter EmitAttackEvent(AttackEvent)
  • Shapes: CircleShape(position, radius) / BoxShape(position, size, angle) / PolygonShape(position, points, angle). For an axis-aligned rectangle, use BoxShape(center, size, 0) — there is no RectangleShape type. Pass angle = 0 to BoxShape / PolygonShape when no rotation is needed.
  • Polygon hit surface: HitComponent.PolygonPoints: SyncList<Vector2>
  • AttackFast does not build a hit table → better performance for bullet hell / mass resolution

1-2. Target filter

SideOverridePurpose
AttackerAttackComponent:IsAttackTarget(defender, attackInfo) → booleanFaction / distance / state
DefenderHitComponent:IsHitTarget(attackInfo) → booleanInvincibility / immunity

If either returns false → the hit is excluded. The super call is `__base:IsAttackTarget(...)` (mlua-specific).

Do not add `@ExecSpace` when overriding — both IsAttackTarget and IsHitTarget have an unspecified ExecSpace (=All) on the parent. Adding an annotation like @ExecSpace("ServerOnly") in the child triggers LEA-3014 `SignatureMismatch` at runtime. Even without the annotation, the call path runs through the server-side hit pipeline, so actual execution happens on the server. Details: `msw-scripting/SKILL.md` §9 "Method override".
  • HitComponent.CollisionGroup defaults to CollisionGroups.HitBox. The last argument of Attack(..., cg) specifies the target group.
  • Duplicate-hit prevention / pierce / max hits: not native. Manage in script via the table returned by Attack + a table<Entity, boolean> cache.

1-3. attackInfo tagging

A string extension point that propagates into CalcDamage/IsHitTarget/GetDisplayHitCount. Value conventions are up to the project. A namespace style such as "melee.light", "dot.poison" is recommended.

1-4. ⚠️ IsLegacy

ColliderType/ColliderOffset/PolygonPoints are only valid when HitComponent.IsLegacy = false. BoxOffset/ColliderName are deprecated.

1-5. Shape mapping per attack form

FormShape construction
Frontal melee BoxBoxShape(pos + LookDirectionX*offset, size, 0) — see DefaultPlayer PlayerAttack
Circular AoECircleShape(self.WorldPos, radius)
ProjectileSpawn a Body-less model (Sprite+Transform only) + in OnUpdate(delta) call TransformComponent:Translate(speed*delta, 0) + distance-based hit check + _EntityService:Destroy. Movement rules in §1-6, full implementation → [`references/projectile.md`](references/projectile.md)

There is no MSW-specific projectile system — implemented as an entity + AttackComponent combo.

1-6. Continuous movement — common rules for projectiles, monsters, and AI

Continuous movement (chase, flight, auto-move) is per-frame `OnUpdate(delta)`-based. Moving via a timer (SetTimerRepeat(0.1~0.15s)) produces 6~10Hz teleportation that looks choppy.

Recommended API per target
TargetBody?Movement APIRationale
Monster / NPC / AIYes (map-type Body + MovementComponent)MovementComponent:MoveToDirection(dir, 0) + MovementComponent.InputSpeedMovementComponent.d.mlua:1 — controls all three of Rigid/Kinematic/Sideview. InputSpeed belongs to MovementComponent (.d.mlua:7), so it is not Player-only. The second arg 0 — deltaTime is applied only on ladders (.d.mlua:32). Official BT examples ActionFollow/ActionMoveRandom also use 0.
Projectile / gem / drop item / effectNo (Sprite+Transform+Trigger)self.Entity.TransformComponent:Translate(speed*delta, 0) every frameDirect Transform manipulation is safe without a Body. The pattern from the official "Create a Long-Range Projectile" tutorial.
Direct Rigidbody control (advanced)Yesbody:AddForce(...) — sustained acceleration / impulseRigidbodyComponent.d.mlua:71MoveVelocity is "mainly controlled by MovementComponent", so prefer routing through MovementComponent instead of writing it directly.
For the actual velocity conversion of MovementComponent.InputSpeed per map type, see `msw-general/references/platform.md` §10 (MapleTile=×1, RectTile=÷1.2, SideView=×1.5).
Forbidden patterns
Reason
_TimerService:SetTimerRepeat(move, 0.1~0.15) for movement6~10Hz teleport, no frame interpolation → jerky
body:SetPosition(...) / MovementComponent:SetPosition(...) inside OnUpdateBoth are teleport methods (MovementComponent.d.mlua:37, SetPosition on each Body's .d.mlua). Using them for continuous movement is choppy. Use only for one-shot spawn/respawn/snap.
self.Entity.TransformComponent.Position = newPos (entity with active Body)The physics engine overwrites it next frame and network sync is blocked.
Constant-step move without delta, e.g. Translate(0.009, 0)Frame-rate dependent. Speed differs between 60FPS and 30FPS.
⚠ Be cautious with the official msw-search antipattern

mlua_Document_Retriever returns a high-scoring "Entity Movement Control Using MovementComponent" document, but the body is an antipattern that moves every frame inside OnUpdate with MovementComponent:SetPosition(...)ignore that document and use MoveToDirection / Translate from the table above. FlappyFish Remake, Stopping the Taxi, and Making a Moving Foothold also show missing-delta or direct-Position assignment, so be careful when referring to them.

MovementComponent attachment — monsters / NPCs

Not included in the monster model by default. The .model must contain all of the following components.

ComponentNotes
MOD.Core.TransformComponentDefault
MOD.Core.SpriteRendererComponentRenderer
Body (map type)RigidbodyComponent(MapleTile) / KinematicbodyComponent(RectTile) / SideviewbodyComponent(SideViewRectTile)
MOD.Core.MovementComponentE.g. InputSpeed = 2.0 — required for movement APIs
Cross references
  • Knockback (1-shot impulse) is not continuous movement, so use §3-1 directly.
  • Body selection per map type / InputSpeed conversion: `msw-general/references/platform.md` §4·§10

---

2. Damage Model

AttackComponent:
  method integer CalcDamage(attacker, defender, attackInfo)        -- default 1     (ExecSpace=All)
  method boolean CalcCritical(attacker, defender, attackInfo)      -- default false (ExecSpace=All)
  method float   GetCriticalDamageRate()                           -- default 2.0   (ExecSpace=All)
  method int32   GetDisplayHitCount(attackInfo)                    -- default 1     (ExecSpace=All)
  method void    OnAttack(defender)                                                 -- (ExecSpace=All)

HitComponent:
  method void OnHit(Entity attacker, integer damage, boolean isCritical, string attackInfo, int32 hitCount)
  emitter EmitHitEvent(HitEvent)
⚠ All hooks above have an unspecified ExecSpace (=All) on the parent. Adding @ExecSpace("ServerOnly") etc. when overriding triggers LEA-3014 `SignatureMismatch`. Drop the annotation and declare just method .... Details: `msw-scripting/SKILL.md` §9 "Method override".

2-1. HitEvent payload

AttackCenter:   Vector2
AttackerEntity: Entity (nilable)
Damages:        List<integer>    -- multi-hit split
Extra:          any              -- ★ extension slot (knockback/stun/element/tags)
IsCritical:     boolean
TotalDamage:    integer
FeedbackAction: HitFeedbackAction  -- ⚠ entire enum deprecated

Carry auxiliary info (knockback vector, stagger time, element) on the Extra table.

2-2. AttackEvent payload

A single field, DefenderEntity: Entity. The attacker is the handler's self.

2-3. ⚠️ Antipattern: direct HP subtraction — do not bypass HitEvent

Subtracting the defender's HP directly, such as monster.Hp -= damage / target.MonsterAI.HP -= damage, does not emit `HitEvent` — damage skin, hit effect, IsHitTarget immunity, and OnHit overrides all silently skip.

For player-side custom damage (channel / aura / DoT), the bypass also breaks avatar animation: AvatarStateAnimationComponent only reacts to StateChangeEvent, so the player avatar stays in idle even though the HP bar drops. If you must apply damage without HitEvent, also manually call StateComponent:ChangeState("HIT") (UPPERCASE key — see §10) and, for death/revive, PlayerComponent:ProcessDead() / ProcessRevive(). Otherwise hit/dead motions silently miss with no error.

---

3. Hit Reaction

3-1. Knockback — API per Body

Body (map type)Implementation
Rigidbody (MapleTile)body:AddForce(Vector2(dir*5, 3)) ★recommended · SetForce · JustJump(Vector2(0, 4)) (vertical)
Kinematicbody (RectTile / top-down)body.MoveVelocity = Vector2(dir*5, 0) — no AddForce
Sideviewbody (SideViewRectTile)body.MoveVelocity + body.JumpSpeed
  • Rigidbody is auto-damped by the engine. Kinematic/Sideview must be damped manually inside OnUpdate (MoveVelocity *= 0.9).
  • Wall bounce: subscribe to FootholdCollisionEvent and flip the velocity.
  • Knockback is a 1-shot impulse — do not confuse it with continuous movement (chase, flight). For continuous movement see §1-6.
  • ⚠ Forbidden: assigning TransformComponent.Position directly on an entity with an active Body → network sync is blocked. body:SetPosition(...) is a teleport method, so do not call it inside an OnUpdate loop (§1-6).

3-2. i-frame

Standard pattern: deadline check based on _UtilLogic.ElapsedSeconds + returning false from HitComponent:IsHitTarget. The DefaultPlayer default PlayerHit.mlua provides this pattern as-is (§9-4).

Alternative: while invincible, swap HitComponent.CollisionGroup to a separate group → the resolution itself is excluded. This is better for frame-accurate precision.

3-3. Status effects (Buff/Debuff)

No native support. Implement a @Component BuffComponent directly + tick with _TimerService:SetTimerRepeat + broadcast custom StatusAppliedEvent/StatusExpiredEvent.

For a single simple stun, StateComponent:ChangeState("STUN") + input/AI block flags is enough.

---

4. Game Feel — all native

ElementAPIExecSpace
Hit Stop (global)_UtilLogic:SetClientTimeScale(float) — 0~100ClientOnly
Hit Stop (individual)renderer.PlayRate = 0 (Sprite/Skeleton/Avatar)@Sync
Slow Motion_UtilLogic:SetClientTimeScale(0.3) + timer to restoreClientOnly
Camera ShakecameraComp:ShakeCamera(intensity, duration, targetUserId?)Client
Camera ZoomcameraComp:SetZoomTo(percent, duration, targetUserId?) · requires IsAllowZoomInOut=true firstClient
Hit FlashspriteRenderer.Color = Color(r,g,b,a) → timer to restore@Sync
Color HDR overbrightColor.HSVToRGB(h, s, v, hdr=true) — values > 1.0 allowed
VFX fixed_EffectService:PlayEffect(clipRUID, instigator, pos, zRot, scale, isLoop?, options?) → serial
VFX attached_EffectService:PlayEffectAttached(clipRUID, parent, localPos, localZRot, localScale, isLoop?, options?)
VFX remove_EffectService:RemoveEffect(serial)
SFX 2D_SoundService:PlaySound(id, volume, targetUserId?)Client
SFX 3D_SoundService:PlaySoundAtPos(id, pos, listener, volume)Client
SFX loopPlayLoopSound / PlayLoopSoundAtPosClient
SFX attachedSoundComponent:Play() · pitch randomization via Pitch 0~3Client
BGM_SoundService:PlayBGM(id, volume) / StopBGM(immediately)Client
Preload_SoundService:LoadSound(id)ClientOnly

PlayEffect options keys: FlipX, FlipY, SortingLayer, OrderInLayer, Alpha, StartFrameIndex, EndFrameIndex, PlayRate, SyncFlip, Color, MaterialID, IgnoreMapLayerCheck, LitMode

Get the current camera: _CameraService:GetCurrentCameraComponent().

ParticleService — built-in particles

General-purpose particle effects driven by enum values only, no RUID. 3 categories:

-- BasicParticle: general-purpose presets (no RUID needed)
integer _ParticleService:PlayBasicParticle(BasicParticleType, Entity instigator, Vector3 pos, number zRot, Vector3 scale, boolean isLoop, Dictionary options)
integer _ParticleService:PlayBasicParticleAttached(BasicParticleType, Entity parent, Vector3 localPos, number localZRot, Vector3 localScale, boolean isLoop, Dictionary options)

-- SpriteParticle: custom sprite as a particle (spriteRUID required)
integer _ParticleService:PlaySpriteParticle(SpriteParticleType, string spriteRUID, Entity instigator, Vector3 pos, number zRot, Vector3 scale, boolean isLoop, Dictionary options)
integer _ParticleService:PlaySpriteParticleAttached(SpriteParticleType, string spriteRUID, Entity parent, Vector3 localPos, number localZRot, Vector3 localScale, boolean isLoop, Dictionary options)

-- AreaParticle: environmental particles over a wide area (areaSize added)
integer _ParticleService:PlayAreaParticle(AreaParticleType, Vector2 areaSize, Entity instigator, Vector3 pos, number zRot, Vector3 scale, boolean isLoop, Dictionary options)

void _ParticleService:RemoveParticle(integer serial)

options keys: Color, SortingLayer, OrderInLayer, ParticleSize, ParticleCount

Looping particles (isLoop=true) must be cleaned up via RemoveParticle(serial). Store the serial in self._T so it can be removed later.

Full BasicParticleType list
FamilyNameDescription
Explosion/impactSparkExplosionSparks (one-shot) — general-purpose hit
SparkLoopContinuous sparks
SparkRadialExplosionSparks scattering radially
SmallExplosionSmall explosion + smoke
BigExplosionBig explosion + smoke
TinyExplosionVery small explosion (Color option ignored)
DustExplosionCircular shockwave + smoke (Color option ignored)
EnergyExplosionCircular shockwave then center convergence
CircleBurstCircular light burst
PillarBurstCircular light burst + directional light
Fire/flameFireFieldCartoon flames
FireFieldIntenseIntense cartoon flames
FireBallFlame at a single point
FlameThrowerFlamethrower stream
LargeFlamesLarge flames from the floor
MediumFlamesMedium flames from the floor
TinyFlamesTiny flames from the floor
WildFireGiant pillar of flame (Color option ignored)
Lightning/electricLightningOrbSharpSpherical electric particles
LightningStrikeSharpLightning bolt
LightningStrikeSharpTallTall lightning bolt
LightningOrbSoftElectric wave emission
LightningBlastPeriodic electric waves
LightningStrikePeriodic lightning
LightningStrikeTallPeriodic tall lightning
Buff/magicAuraAurora light from the floor
BuffStrong light rising from the floor
ChargeLarge particles converging on one point
ChargeOrbParticles converging on one point
EnchantLarge light with light/particles around it
SpinFieldParticles around a rotating circle
StarVortexStarlight converging to the center
NovaWide circular wave
UpperCylinderRising pillar from the floor
MiscFireworkFireworks
FireworkClusterMultiple fireworks at once
FireFliesFireflies
GoopSprayLiquid spray to the side
GoopSprayEffectLiquid spray downwards
DustStormWide dust storm
RisingSteamRising white mist from the floor
BigSplashLarge water splash
ShowerWater poured on one spot
Full SpriteParticleType list (8)
NameDescription
BurstBigSprite emerges in a radial pattern
SpawnFieldParticles + sprite emerge in a circular area
BurstNovaParticles + sprite burst in a circular pattern
SimpleSpawnSimple particle + sprite appearance
BurstParticles + sprite scatter
StreamGenerated while moving in a specific direction
StreamSharpThin line moving in a specific direction
AdditiveColorColor effect applied to the sprite
Full AreaParticleType list (12)
NameDescription
RainRain
SnowSnow
FogCalmFog
FogHeavyHeavy descending fog
FogLivelyRising fog
CalmStarFieldRising star cluster
StarFieldSimpleTwinkling star cluster
StarFogStar + nebula particles (stationary)
StarFogFlowStar + nebula particles (rising)
WindlinesThin lines
WindlinesBigThin lines + thick lines
WindlinesSpeedyFast straight lines

Choosing between EffectService and ParticleService

SituationRecommended
MapleStory skill / hit animations (specific imagery)EffectService (specify RUID)
General hit/explosion (fast to implement)ParticleService.BasicParticle
Scatter a custom image as particlesParticleService.SpriteParticle
Environmental ambience like rain/snow/fogParticleService.AreaParticle
Sustained effects like buff aurasEither with isLoop=true
Rich, layered effectsCombine EffectService and ParticleService
Standard pattern for server event → client effect: @Sync property change → detected in OnSyncProperty(ClientOnly) → call EffectService/ParticleService.

---

5. Death / Revive

EventEmission conditionPayload
DeadEventAuto on StateComponent:ChangeState("DEAD")none
ReviveEventAuto on PlayerComponent:Respawn() (players only)none
StateChangeEventAuto on every state transitionCurrentStateName, PrevStateName

Tracking the killer: DeadEvent has no payload → cache self.LastAttacker = event.AttackerEntity in HandleHitEvent and use it in HandleDeadEvent.

For player-specific death/revive, prefer §9-1 PlayerComponent.Respawn/ProcessDead/ProcessRevive.

---

6. Event Bus

Logical eventMSW implementation
OnAttackStartOnAttack hook or custom AttackStartEvent
OnAttackHit / OnDamageTakenNative HitEvent
OnAttackMissCustom — SendEvent when IsAttackTarget returns false
OnCriticalHitCovered by the HitEvent.IsCritical flag
OnDeath / OnReviveNative DeadEvent/ReviveEvent
OnStateChangeNative StateChangeEvent
OnKill / OnBlocked / OnParry / OnStatusAppliedCustom @Event

6-1. Custom event rules

  • Definition: @Event script XxxEvent extends EventType + property declarations
  • Receiving: the handler keyword (not method), @EventSender("Self" | "Service","XxxService" | "Logic","XxxLogic")
  • Connect/disconnect: entity:ConnectEvent(XxxEvent, self.Handler) / call `DisconnectEvent` in `OnEndPlay` (the engine does not auto-disconnect)
  • Global: @Logic CombatEventBusLogic singleton + @EventSender("Logic","CombatEventBusLogic")

---

7. AI — FSM(StateComponent) + BT(AIComponent) + custom-script (Pattern A), all native-compatible

PatternFitReference
FSM (StateComponent + @State)Simple enemies (3~5 states), player IDLE/HIT/DEAD, boss phases, animation sync (AvatarStateAnimationComponent auto mapping §10). Requires StateComponent.IsLegacy=false if you want StateAnimationComponent to auto-swap clips.[`../msw-general/references/animation-state.md`](../msw-general/references/animation-state.md) (state-machine + animation pipeline unified)
BT (AIComponent + 4 Composite types + @BTNode)Patrol + chase + attack combos, varied boss patterns, Composite/Decorator reuse, probability-weighted actions. Requires StateComponent.IsLegacy=false.[`references/ai-bt.md`](references/ai-bt.md)
Custom script with self-state (@Component holding CurrentAIState plus direct SpriteRUID assignment — Soldier-style pattern)Behaviors that don't fit AIChase/AIWander (roam ↔ stand ↔ say ↔ attack, range-gated attacks, talking idle). No `AIChaseComponent`/`AIWanderComponent`, no `IsLegacy=false` needed — the script bypasses the ActionSheet pipeline. Reserve StateComponent for IDLEDEAD only.`../msw-general/references/monster.md` §7 "Canonical Pattern A Scripts (Soldier)"

7-1. FSM — StateComponent (summary)

StateComponent + @State script XxxStateType extends StateType (lifecycle OnEnter/OnUpdate/OnExit/OnConditionCheck). The only auto-registered states are IDLE/DEAD (+ HIT if a HitComponent exists, MOVE if an AIChase/AIWander exists) — ATTACK/PATROL/STUN/PHASE2 etc. must all be pre-registered via AddState("name", XxxStateType) in OnBeginPlay. Auto transitions use AddCondition(from, to) + per-frame OnConditionCheck().

State names must be UPPERCASE; unregistered names immediately produce [LEA-3005] InvalidArgument : 'stateName'. Registering a key in AvatarStateAnimationComponent.StateToAvatarBodyActionSheet does not auto-register it in StateComponent — the two are separate.

Full implementation → [`../msw-general/references/animation-state.md`](../msw-general/references/animation-state.md) (FSM authoring, ChangeState failure matrix, standard PATROL/CHASE/ATTACK/HIT/DEAD monster pattern, and the state→animation pipeline live together — the two are the same underlying system viewed from two angles)

7-2. BT — AIComponent (summary)

AIComponent + SequenceNode/SelectorNode/RandomSelectorNode/ParallelNode + @BTNode Action Nodes + native AIChaseComponent/AIWanderComponent. All 4 Composite types are native; Decorator/Memory(Blackboard)/Threat Table must be implemented by hand.

⚠ When using custom BT, remove AIChaseComponent/AIWanderComponent from the .model.

Full implementation → [`references/ai-bt.md`](references/ai-bt.md)

Full monster entity composition → `../msw-general/references/monster.md`

>

This SKILL.md only covers combat-specific aspects (ATTACK/HIT/DEAD + DeadEvent/ReviveEvent + BT entry point). For general mlua state machine / scripting patterns see `msw-scripting`.

---

8. UI natives

UIAPI
HP bar (screen-fixed)SliderComponent (MinValue/MaxValue/Value/FillRectColor/FillRectImageRUID/Direction/UseHandle) + SliderValueChangedEvent. ⚠ UI entities only
Damage numbers3 DamageSkin* components + DamageSkinService — §11
CrosshairSpriteGUIRendererComponent in .ui
Combo counter / buff iconsTextComponent + SpriteGUIRendererComponent

Worldspace HP bar (overhead): no native support. Two implementation options:

OptionApproachFit
LightweightAdjust LocalScale.x = hp/maxHp on a child entity's SpriteRendererComponent or use TiledSize.x (with SpriteDrawMode.Tiled)Quick prototype, simple gauge
FullBased on PixelRendererComponentfull implementation [`references/hp-gauge.md`](references/hp-gauge.md)Production-grade, many monsters shown at once

---

9. DefaultPlayer combat natives

The player entity has HP, revive, and input natively. Do not create custom `Hp`/`MaxHp` properties — use PlayerComponent.

The full property/method tables for PlayerComponent / PlayerControllerComponent are in `msw-defaultplayer/SKILL.md`. Only combat essentials here.

9-1. Core combat APIs

ItemUsage
HP decrementself.Entity.PlayerComponent.Hp -= event.TotalDamage
Death checkPlayerComponent:IsDead()
RevivePlayerComponent:Respawn()RespawnPosition → SpawnLocation → map entry point. DeadEvent/ReviveEvent auto-emitted
Client-only death processing@ExecSpace("Client") ProcessDead(targetUserId?) / ProcessRevive(targetUserId?)
Direction check ★PlayerControllerComponent.LookDirectionX (+1 right, -1 left). Do not use TransformComponent.Scale.x
Action hook overrideActionAttack / ActionJump / ActionInteraction(key, isKeyDown) etc.
Action event receptionEmitPlayerActionEvent(PlayerActionEvent) → §9-3

9-3. PlayerActionEvent

property string ActionName       -- "Attack" / "Jump" / "Crouch" / ...
property Entity PlayerEntity

The default pattern is PlayerAttack extends AttackComponent that receives @EventSender("Self") handler HandlePlayerActionEvent(...) and branches on event.ActionName == "Attack".

9-4. Default templates (RootDesk/MyDesk/)

Copy-paste without modification. Override as needed:

FileRoleKey points
PlayerAttack.mluaFrontal Box attackLookDirectionX for direction, AttackFast + CollisionGroups.Monster, CalcDamage=50, 30% crit
PlayerHit.mluai-frameImmuneCooldown property, _UtilLogic.ElapsedSeconds deadline, IsHitTarget override
Monster.mluaMonster HPCustom @Sync Hp (no PlayerComponent), HandleHitEventDead/Respawn
MonsterAttack.mluaSprite-size-based meleeisvalid(defender.PlayerComponent) + __base:IsAttackTarget(...) super in IsAttackTarget

9-5. Time reference

`_UtilLogic.ElapsedSeconds` is recommended (world clock, consistent across pause/restore). Do not use os.clock().

9-6. Standard CollisionGroups

ConstantPurpose
CollisionGroups.PlayerMonster → Player attack
CollisionGroups.MonsterPlayer → Monster attack
CollisionGroups.HitBoxDefault for HitComponent.CollisionGroup

---

10. Avatar motion — AvatarStateAnimationComponent

Auto-links StateComponent transitions to avatar animations.

@Sync property SyncDictionary<string, AvatarBodyActionElement> StateToAvatarBodyActionSheet  -- IsLegacy=false
@Sync property SyncDictionary<string, string>                  ActionSheet                    -- IsLegacy=true (deprecated)

method void   SetActionSheet(string key, string animationClipRuid)
method void   RemoveActionSheet(string key)
method string StateStringToAnimationKey(string stateName)
emitter EmitBodyActionStateChangeEvent(BodyActionStateChangeEvent)
  • ChangeState("HIT") → the mapped MapleAvatarBodyActionState.Hit plays automatically
  • Combat-relevant state values: Attack=3, Hit=14, Dead=10, Alert=4, Heal=13
  • IsLegacy=false fixed; use only StateToAvatarBodyActionSheet
The full avatar component coverage (AvatarRendererComponent etc.) is in `msw-defaultplayer`. This section covers only combat motion mapping.

---

11. Damage skin (number display)

Default RUIDs

PurposeRUIDUsed on
Hit3271c3e79bf04ecba9a107d55495970dDefault for attacker's DamageSkinSettingComponent.DamageSkinId
Taken hit02c22d93421b4038b3c413b3e40b57ecDefender-side display — call _DamageSkinService:Play manually
Heald58b67cf0f3a4eaf9fe1ad87c0ffac8aHeal/potion — call _DamageSkinService:Play manually

11-1. Auto mode (component-based)

On Attack/AttackFast, if all 3 components below are present, damage numbers are displayed automatically:

SideComponentRole
AttackerDamageSkinSettingComponentWhich skin/style to display
DefenderDamageSkinSpawnerComponentDisplay position offset
DefenderDamageSkinComponentDamage number body (over the entity)

Include all 3 in the .model and damage numbers appear with zero script code.

DamageSkinSettingComponent (attacker)
PropertyTypeDefaultDescription
DamageSkinIdDataRefhit RUID (table above)Damage number skin RUID
DamageSkinScaleVector2(1, 1)Number size
Alphafloat1Opacity
PlayRatefloat1Playback speed
DelayPerAttackfloat0.05Delay between multi-hits (seconds)
TweenTypeDamageSkinTweenTypeDefaultAnimation style
LitModeLitModeDefaultLighting influence

DamageSkinTweenType: Default (popup) / Volcano (fan) / Blade (overlap) / each *Mini (75% scale)

DamageSkinSpawnerComponent (defender)
PropertyTypeDefault
DamageSkinOffsetVector2(0,0)

11-2. Manual mode — DamageSkinService

Cases not caught by auto mode (heal, Miss/Guard, non-standard damage sources) call _DamageSkinService directly.

_DamageSkinService:Play(targetEntity, skinRuid, delay, damages:List<int>, tweenType, isCritical, offset, scale, playRate, alpha, litMode)
_DamageSkinService:PlayTextDamage(targetEntity, skinRuid, textType, tweenType)
_DamageSkinService:PreloadAsync(skinRuid, callback(success))    -- ClientOnly

DamageSkinTextType: Miss / Guard / Resist / Shot / Counter

_DamageSkinService:Play is in the Client space — to call it from server logic (HP subtraction, etc.) wrap it in an @ExecSpace("Client") method or change a @Sync property and trigger from OnSyncProperty.
`Play()` has 6 required parameters. Passing only some of the 5 optional ones triggers LEA-3005 `InvalidArgument`.

11-3. Recipes

(a) Critical emphasis — auto mode + dynamic scale

Auto mode renders red font automatically when IsCritical=true. To emphasize further, temporarily increase the attacker-side scale:

-- ⚠ AttackComponent hooks (CalcDamage/CalcCritical/GetCriticalDamageRate/GetDisplayHitCount/
--   IsAttackTarget/IsHitTarget/OnAttack) have an unspecified ExecSpace (=All) on the parent.
--   Adding @ExecSpace when overriding triggers LEA-3014 SignatureMismatch.
--   Details: msw-scripting/SKILL.md §9 "Method override → LEA-3014"
method integer CalcDamage(Entity attacker, Entity defender, string attackInfo)
    return 100
end

method boolean CalcCritical(Entity attacker, Entity defender, string attackInfo)
    return math.random() < 0.3
end

method float GetCriticalDamageRate()
    return 2.5     -- 100 → 250
end

Differentiate criticals visually with DamageSkinSettingComponent.TweenType = Volcano (fan scatter) or Blade (overlap).

(b) Heal / recovery — manual call
local HEAL_RUID = "d58b67cf0f3a4eaf9fe1ad87c0ffac8a"

@ExecSpace("Client")
method void ShowHeal(Entity target, integer amount)
    _DamageSkinService:Play(
        target, HEAL_RUID, 0,
        { amount },                            -- damages
        DamageSkinTweenType.Default,
        false,                                 -- isCritical
        Vector2(0, 0.5),                       -- offset (above head)
        Vector2(1, 1), 1.0, 1.0, LitMode.Default
    )
end
(c) Miss / Guard / Resist text
local HIT_RUID = "02c22d93421b4038b3c413b3e40b57ec"

@ExecSpace("Client")
method void ShowMiss(Entity target)
    _DamageSkinService:PlayTextDamage(
        target, HIT_RUID, DamageSkinTextType.Miss, DamageSkinTweenType.Default
    )
end

Call this when AttackComponent:IsAttackTarget returned false → "miss animation + damage 0".

(d) Multi-hit — split into N with a single call

If you pass a List as the damages argument of _DamageSkinService:Play, the numbers are shown sequentially at DelayPerAttack (attacker component value) intervals:

_DamageSkinService:Play(target, ATTACK_RUID, 0, { 12, 8, 14, 11, 9 },
    DamageSkinTweenType.Default, false, Vector2(0,0), Vector2(1,1), 1, 1, LitMode.Default)

Auto mode behaves identically with HitEvent.Damages (List) — override GetDisplayHitCount(attackInfo) to control the split count.

(e) Preload — prevent first-display stutter

The first use of a skin RUID may have texture loading lag. Preload on map entry:

@ExecSpace("ClientOnly")
method void OnBeginPlay()
    _DamageSkinService:PreloadAsync("3271c3e79bf04ecba9a107d55495970d", function(ok) end)
    _DamageSkinService:PreloadAsync("02c22d93421b4038b3c413b3e40b57ec", function(ok) end)
    _DamageSkinService:PreloadAsync("d58b67cf0f3a4eaf9fe1ad87c0ffac8a", function(ok) end)
end
(f) TweenType use cases
TweenTypeRecommended situation
DefaultNormal hits
VolcanoCritical / area hits (upward scatter)
BladeContinuous slashes / combos (overlapping numbers)
*MiniSmall damage like DoT (poison/burn) — less screen clutter
(g) Faction-specific skins

To use different skin RUIDs per side (player vs enemy, PvP factions, etc.), swap DamageSkinSettingComponent.DamageSkinId at runtime:

self.Entity.DamageSkinSettingComponent.DamageSkinId = MY_TEAM_SKIN_RUID

---

12. Hit effect — HitEffectSpawnerComponent

Attach to the defender and a hit effect plays automatically on HitEvent. No properties — just add the component to the .model.

---

13. Full combat checklist

  • [ ] Attacker model: an AttackComponent-derived script (+ optional: DamageSkinSettingComponent)
  • [ ] Defender model: HitComponent + HitEffectSpawnerComponent + (optional: DamageSkinSpawnerComponent + DamageSkinComponent)
  • [ ] HitComponent: IsLegacy=false, set ColliderType/BoxSize/CircleRadius, set CollisionGroup
  • [ ] State motions: register ATTACK/HIT/DEAD in StateComponent + AvatarStateAnimationComponent.StateToAvatarBodyActionSheet
  • [ ] HP handling: player uses PlayerComponent.Hp; monster uses custom @Sync Hp
  • [ ] Direction check: LookDirectionX (no Scale.x)
  • [ ] Time reference: _UtilLogic.ElapsedSeconds (no os.clock)
  • [ ] Event cleanup: explicit DisconnectEvent in OnEndPlay
  • [ ] Body rule: do not assign TransformComponent.Position directly on an entity with an active Body

---

14. Custom implementation is required

Buff/Debuff · BT Decorator/Memory(Blackboard) · Aggro/Threat Table · projectile pooling · pierce/max-hits · stagger-level system · resources (MP/Stamina/Rage) · combo/cancel windows · guard/parry · world→screen coordinate conversion · worldspace HP bar

---

Out of scope

  • General player topics (HP/movement/camera/costume aside): `msw-defaultplayer`
  • General mlua grammar/lifecycle: `msw-scripting`
  • .model authoring rules/templates: `msw-general`

Related skills

How it compares

Pick msw-combat-system for MSW-native combat APIs rather than porting generic game engine combat patterns.

FAQ

Which hit shapes does HitComponent support natively?

Box, Circle, and Polygon only; other shapes must be approximated by composition.

How should projectiles move continuously?

Use TransformComponent Translate with speed times delta each frame in OnUpdate, not timer-based SetPosition jumps.

What game-feel effects are native in MSW combat?

All six: Hit Stop, Camera Shake, Zoom, Sprite Flash, VFX, and SFX.

Is Msw Combat System safe to install?

skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.

Game Developmentbackendintegrations

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.