PluginBench
Skill
Pass
Audit score 90

msw-combat-system

msw-git/msw-ai-coding-plugins-official

How to install msw-combat-system

npx skills add https://github.com/msw-git/msw-ai-coding-plugins-official --skill msw-combat-system
Claude Code
Cursor
Windsurf
Cline
Full instructions (SKILL.md)

Source of truth, from msw-git/msw-ai-coding-plugins-official.


name: msw-combat-system description: "MSW combat system integration guide. Covers the Attack→Hit pipeline, damage model, i-frame, knockback, Hit Stop, Camera Shake, Sprite Flash, SFX, death/revive, damage skin, hit effect, avatar combat motion, custom events, and AI FSM — all based on MSW native APIs for 2D multi-genre coverage. Keywords: attack, hit, damage, combat, monster, hit effect, critical, projectile, damage skin, knockback, hit stop, combo, HP bar."

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.mdMonster .model component assembly + ActionSheet + AI choice + canonical Pattern A scripts (Soldier-style) + HP/Respawn + spawn + verificationWhen building a combat-capable monster
references/hp-gauge.mdFull implementation of an overhead HP bar based on PixelRendererComponentWhen attaching an overhead HP bar
references/projectile.mdProjectile (Body-less entity + OnUpdate Translate) + homing/pierce/splash variantsWhen building ranged attacks like arrows, bullets, magic bolts
references/ai-bt.mdBehaviourTree — 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

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


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 (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
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 (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

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.mdProduction-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

Related skills

More from msw-git/msw-ai-coding-plugins-official and the wider catalog.

MS

msw-search

msw-git/msw-ai-coding-plugins-official

MSW search integration — (1) vector search for API docs and implementation guides via the msw-mcp MCP server (mlua_api_retriever / mlua_document_retriever), (2) REST API search for resources (sprite / animation / sound / resource pack / avatar). Use for 'find details, examples, or related APIs not in .d.mlua', 'need a SpriteRUID', 'monster sprite', 'background image', 'find a sound', 'avatar item lookup', etc. Keywords: document search, API details, examples, guide, retriever, resource, sprite, animation, sound, RUID, resource pack, avatar.

2.1k installsAudited
MS

msw-defaultplayer

msw-git/msw-ai-coding-plugins-official

MSW DefaultPlayer (character) management. Use the msw-general ModelBuilder to inspect/patch DefaultPlayer.model and Player.model, add/remove components, configure movement speed / jump force / HP / camera, and per-map-mode movement components. Use for DefaultPlayer model, player components, movement speed, jump force, HP, camera, physics. Keywords: player, DefaultPlayer, speed, jump, HP, camera, gravity, revive, respawn, character.

2.1k installsAudited
MS

msw-scripting

msw-git/msw-ai-coding-plugins-official

Authoring MSW scripts (.mlua) plus integrated playtest and debugging. Covers mlua syntax, annotations (@Component/@Logic/@ExecSpace/@Sync), lifecycle, exec spaces, property sync, event system, file workflow, build-log inspection, error classification, and the test/debug loop. Keywords: script, mlua, lua, Component, Logic, annotation, ExecSpace, Sync, event, play, test, debug, lifecycle.

2.1k installs
MS

msw-general

msw-git/msw-ai-coding-plugins-official

Foundation skill for MSW (MapleStory Worlds). Read this FIRST before anything else in MSW.

2.1k installsAudited
MS

msw-avatar

msw-git/msw-ai-coding-plugins-official

MSW avatar management — costume (CostumeManagerComponent, 17 slots) + animation 3-layer pipeline (StateComponent → AvatarStateAnimationComponent → AvatarRendererComponent). Four-level distinction: State key (uppercase) / AvatarBodyActionStateName (lowercase) / MapleAvatarBodyActionState enum / sprite action ID (swingO1, shoot1). Two mapping systems via IsLegacy/ActionSheet/StateToAvatarBodyActionSheet. PlayerControllerComponent auto-transition conflicts with ActionStateChangedEvent resolved via RemoveActionSheet/SetActionSheet/BodyActionStateChangeEvent. Applies to any avatar-bearing entity (NPC, monster, etc.), not only DefaultPlayer. Use for costume get/set, 17 equip slots, animation state mapping, action override, weapon-specific attack motion, custom shoot/cast/dance action. Keywords: avatar, costume, animation, state, action, shoot, swing, weapon, equipment, custom action, block auto playback, remap.

2.1k installsAudited
MS

msw-packages

msw-git/msw-ai-coding-plugins-official

MSWPackages catalog — official 1st-party prebuilt packages for common MSW features. Use this skill BEFORE writing a feature from scratch when the user asks for any standard game system (toast/notification, ranking/leaderboard, inventory/equipment, shop/store, world shop, mail, quest/achievement, dialog/NPC conversation, key binding, game event broadcast, player data/save, collection/dex, slash command, scrollview/virtualized list, drop table, global config, GM message, resource/currency, UI components). Always check the catalog first; if a package matches the requested feature, fetch its README and propose integrating instead of building from zero. Keywords: MSWPackages, package, prebuilt, integration.

2.0k installs