msw-combat-system

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

By msw-git · 6,812 installs

npx skills add msw-git/msw-ai-coding-plugins-official --skill msw-combat-system

Source repository · Upstream listing

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 Layer Native Custom required 1 Attack Resolution AttackComponent + HitComponent (Box/Circle/Polygon) Capsule/Cone/Ray, pierce count 2 Damage Model CalcDamage / CalcCritical / GetCriticalDamageRate / GetDisplayHitCount hooks + HitEvent.Extra:any Element affinity, composite formulas 3 Hit Reaction Per Body knockback API, IsHitTarget based i frame Stagger level, status effects 4 Game Feel All 6 native (Hit Stop, Shake, Zoom, Flash, VFX, SFX) — 5 Combat State StateComponent + DeadEvent / ReviveEvent , PlayerComponent HP/revive MP/Stamina/Rage, aggro 6 Event Bus HitEvent / AttackEvent / StateChangeEvent / PlayerActionEvent + custom @Event OnKill/OnBlocked 7 AI StateComponent (FSM) + AIComponent (BT, 4 native Composites + custom extends CompositeNode ) + AIChaseComponent / AIWanderComponent , UserService.UserEntities Decorator/Memory(Blackboard), Threat Table + Damage Skin 3 DamageSkin components + DamageSkinService — + Hit Effect HitEffectSpawnerComponent (auto) — + Avatar Motion AvatarStateAnimationComponent (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. File Scope When to read [ ../msw general/references/monster.md ](../msw general/references/monster.md) Monster .model component assembly + ActionSheet + AI choice + canonical Pattern A scripts (Soldier style) + HP/Respawn + spawn + verification When building a combat capable monster [ references/hp gauge.md ](references/hp gauge.md) Full implementation of an overhead HP bar based on PixelRendererComponent When attaching an overhead HP bar [ references/projectile.md ](references/projectile.md) Projectile (Body less entity + OnUpdate Translate ) + homing/pierce/splash variants When building ranged attacks like arrows, bullets, magic bolts [ references/ai bt.md ](references/ai bt.md) BehaviourTree — AIComponent + Composites (4 native + custom) + @BTNode + custom Decorator/Memory/Threat When 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. 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 Side Override Purpose Attacker AttackComponent:IsAttackTarget(defender, attackInfo) → boolean Faction / distance / state Defender HitComponent:IsHitTarget(attackInfo) → boolean Invincibility / 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"](../msw scripting/SKILL.md). The cg argument of Attack (..., cg) is an exact match filter on the defender's HitComponent.CollisionGroup (defaults to CollisionGroups.HitBox ). It does not consult the collision matrix, and the attacker's own group is irrelevant. nil (omitted) = every HitComponent is a candidate. A valid group that differs from the defenders' actual group hits 0 targets with no error (silent); a group reference that does not exist in this world's collision group set behaves like nil (all groups). When passing a group (e.g. CollisionGroups.Monster ), first set the target monsters' HitComponent.CollisionGroup to that same group — the sample workspace monsters override it to Monster , which is why the sample attack scripts work. A model imported from another world whose group id is missing from this world's set silently registers under the Default group — reach it with nil or CollisionGroups.Default . 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 Form Shape construction Frontal melee Box BoxShape(pos + LookDirectionX offset, size, 0) — see DefaultPlayer PlayerAttack Circular AoE CircleShape(self.WorldPos, radius) Projectile Spawn 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 Target Body? Movement API Rationale : : Monster / NPC / AI Yes (map type Body + MovementComponent ) MovementComponent:MoveToDirection(dir, 0) + MovementComponent.InputSpeed MovementComponent.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 / effect No (Sprite+Transform+Trigger) self.Entity.TransformComponent:Translate(speed delta, 0) every frame Direct Transform manipulation is safe without a Body. The pattern from the official "Create a Long Range Projectile" tutorial. Direct Rigidbody control (advanced) Yes body:AddForce(...) — sustained acceleration / impulse RigidbodyComponent.d.mlua:71 — MoveVelocity 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](../msw general/references/platform.md) (MapleTile=×1, RectTile=÷1.2, SideView=×1.5). Forbidden patterns ❌ Reason TimerService:SetTimerRepeat(move, 0.1~0.15) for movement 6~10Hz teleport, no frame interpolation → jerky body:SetPosition(...) / MovementComponent:SetPosition(...) inside OnUpdate Both 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. Component Notes MOD.Core.TransformComponent Default MOD.Core.SpriteRendererComponent Renderer Body (map type) RigidbodyComponent (MapleTile) / KinematicbodyComponent (RectTile) / SideviewbodyComponent (SideViewRectTile) MOD.Core.MovementComponent E.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](../msw general/references/platform.md) 1 7. Collision based hit — pick the detection method (do not hand roll distance) When the request is "collision / contact based hit" (player↔monster touch damage, hazard zone, trap, overlap), pick by intent. Per frame distance math is not the default — reaching for it when the design asked for collider based contact is the most common requester↔implementer mismatch. Intent Detection Why Active attack swing / hitbox / projectile burst AttackComponent:Attack / AttackFast / AttackFrom → resolves against defender HitComponent , emits HitEvent Full pipeline: damage skin, hit effect, IsHitTarget i frame, OnHit Body to body contact / zone / trap overlap, targets arbitrary or many TriggerComponent + OnEnterTriggerBody / OnStayTriggerBody / OnLeaveTriggerBody (or TriggerEnter/Stay/LeaveEvent ) + CollisionGroup filter, then route damage through the HitEvent pipeline Engine side overlap: honors real collider shape (Box/Circle/Polygon), ColliderOffset , and group filtering Homing / single already known target Per frame OnUpdate distance check ([ references/projectile.md ](references/projectile.md)) Justified only when exactly one target entity is known up front ❌ Per frame math.sqrt(dx dx+dy dy) < r for player↔monster or multi target collision: it silently approximates one circle centered on the transform (ignores each collider's shape / size / ColliderOffset ), skips CollisionGroup filtering, and costs O(attackers × targets) every frame. "Collision based hit" means native colliders, not distance polling. CollisionGroup defaults to CollisionGroups.TriggerBox . OnStayTriggerBody fires every frame while overlapping — gate damage with a cooldown, don't apply per tick. Detection ≠ damage application: after a native overlap fires, still deal damage through HitEvent / AttackComponent — never subtract HP directly. 2. Damage Model ⚠ All hooks above have an unspecified ExecSpace (=All) on the parent. Adding @ExecSpace("ServerOnly") etc. when overriding triggers LEA 3014 SignatureMis