msw-combat-system
msw-combat-system
MSW 전투 파이프라인 전체. 2D 장르 불문 공통 전투 레이어 중 MSW 네이티브 API 지원 항목만 정리. 수식·이론 제외. 전부 default-local-workspace/Environment/NativeScripts/**/*.d.mlua 원본 재검증 완료 (2026-04-21).
의심/예외는 docs/engine-source-index.md 캐시 먼저 확인.
0. 커버리지 매트릭스
| # | 레이어 | 네이티브 | 커스텀 필요 |
|---|---|---|---|
| 1 | Attack Resolution | AttackComponent + HitComponent (Box/Circle/Polygon) |
Capsule/Cone/Ray, 관통 카운트 |
| 2 | Damage Model | CalcDamage/CalcCritical/GetCriticalDamageRate/GetDisplayHitCount 훅 + HitEvent.Extra:any |
속성 상성, 복합 공식 |
| 3 | Hit Reaction | Body별 넉백 API, IsHitTarget 기반 i-frame |
경직 레벨, 상태이상 |
| 4 | Game Feel | 6개 전부 네이티브 (Hit Stop, Shake, Zoom, Flash, VFX, SFX) | — |
| 5 | Combat State | StateComponent + DeadEvent/ReviveEvent, PlayerComponent HP/부활 |
MP/Stamina/Rage, 어그로 |
| 6 | Event Bus | HitEvent/AttackEvent/StateChangeEvent/PlayerActionEvent + 커스텀 @Event |
OnKill/OnBlocked |
| 7 | AI FSM | StateComponent, _UserService.UserEntities |
BehaviourTree, Threat Table |
| + | Damage Skin | DamageSkin* 3종 + DamageSkinService |
— |
| + | Hit Effect | HitEffectSpawnerComponent (auto) |
— |
| + | Avatar Motion | AvatarStateAnimationComponent (State→MapleAvatarBodyActionState) |
— |
1. Attack Resolution
1-1. Shape & Attack 발동
HitComponent.ColliderType = Box / Circle / Polygon 만 지원. 기타 형태는 조합으로 근사.
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 (대량 판정용, 탄막)
AttackFrom(Vector2 size, Vector2 position, string attackInfo, CollisionGroup? cg) → table<Component>
emitter EmitAttackEvent(AttackEvent)
- Shape:
RectangleShape(center, size)/CircleShape(center, radius)/BoxShape(pos, size, angle) - Polygon 피격면:
HitComponent.PolygonPoints: SyncList<Vector2> AttackFast는 히트 테이블 미생성 → 탄막·대량 판정 성능 유리
1-2. 타겟 필터
| 지점 | 오버라이드 | 용도 |
|---|---|---|
| 공격자 | AttackComponent:IsAttackTarget(defender, attackInfo) → boolean |
진영·거리·상태 |
| 피격자 | HitComponent:IsHitTarget(attackInfo) → boolean |
무적·면역 |
한쪽이라도 false → 히트 제외. super 호출은 __base:IsAttackTarget(...) (mlua 고유).
HitComponent.CollisionGroup기본CollisionGroups.HitBox.Attack(..., cg)마지막 인자로 타겟 그룹 지정- 중복 히트 방지 / 관통 / 최대 히트 수: 네이티브 없음.
Attack반환 테이블 +table<Entity, boolean>캐시로 스크립트 관리
1-3. attackInfo 태깅
CalcDamage/IsHitTarget/GetDisplayHitCount 로 전파되는 string 확장 지점. 값 규약은 프로젝트 자율. "melee.light", "dot.poison" 식 네임스페이스 권장.
1-4. ⚠️ IsLegacy
HitComponent.IsLegacy = false 에서만 ColliderType/ColliderOffset/PolygonPoints 유효. BoxOffset/ColliderName deprecated.
1-5. 공격 형태별 Shape 매핑
| 형태 | Shape 구성 |
|---|---|
| 근접 전방 Box | BoxShape(pos + LookDirectionX*offset, size, 0) — DefaultPlayer PlayerAttack 참조 |
| 원형 AoE | CircleShape(self.WorldPos, radius) |
| 투사체 | Body 없는 모델(Sprite+Transform만) 스폰 + OnUpdate(delta)에서 TransformComponent:Translate(speed*delta, 0) + 거리 기반 적중 판정 + _EntityService:Destroy. 이동 규칙 §1-6, 전체 구현(모델·스크립트·U도·변형 패턴) → references/projectile.md |
MSW 전용 투사체 시스템 없음 — 엔티티 + AttackComponent 조합으로 구현.
1-6. 연속 이동 — 투사체·몬스터·AI 공통 규칙
연속 이동(추적·비행·자동 이동)은 매 프레임 OnUpdate(delta) 기반. 타이머(SetTimerRepeat(0.1~0.15s))로 이동시키면 6~10Hz 순간이동으로 보여 끊긴다.
대상별 권장 API
| 대상 | Body 유무 | 이동 API | 근거 |
|---|---|---|---|
| 몬스터 / NPC / AI | 있음 (맵 타입 Body + MovementComponent) |
MovementComponent:MoveToDirection(dir, 0) + MovementComponent.InputSpeed |
MovementComponent.d.mlua:1 — Rigid/Kinematic/Sideview 셋 다 제어. InputSpeed는 MovementComponent 소속(.d.mlua:7)으로 Player 전용 아님. 두 번째 인자 0 — deltaTime은 사다리에서만 적용(.d.mlua:32). BT 공식 예제 ActionFollow/ActionMoveRandom도 0 사용 |
| 투사체 / 젬 / 드롭 아이템 / 이펙트 | 없음 (Sprite+Transform+Trigger) | self.Entity.TransformComponent:Translate(speed*delta, 0) 매 프레임 |
Body 없으면 Transform 직접 조작 안전. 공식 "Create a Long-Range Projectile" 튜토리얼 패턴 |
| Rigidbody 엔티티 직접 제어 (고급) | 있음 | body:AddForce(...) — 지속 가속/임펄스 |
RigidbodyComponent.d.mlua:71 — MoveVelocity는 "mainly controlled by MovementComponent" 이므로 직접 쓰기보다 MovementComponent 경유 권장 |
MovementComponent.InputSpeed맵 타입별 실제 velocity 환산은msw-general/references/platform.md§10 참조 (MapleTile=×1, RectTile=÷1.2, SideView=×1.5).
금지 패턴
| ❌ | 사유 |
|---|---|
_TimerService:SetTimerRepeat(move, 0.1~0.15) 이동 |
6~10Hz teleport, 프레임 보간 없음 → 뚝뚝 끊김 |
OnUpdate 내 body:SetPosition(...) / MovementComponent:SetPosition(...) |
둘 다 텔레포트 메서드 (MovementComponent.d.mlua:37, 각 Body .d.mlua의 SetPosition). 연속 이동에 쓰면 끊김. 1회성 스폰·리스폰·스냅에만 |
self.Entity.TransformComponent.Position = newPos (Body 활성 엔티티) |
다음 프레임 물리엔진이 덮어쓰고 네트워크 동기화 차단 |
Translate(0.009, 0) 같이 delta 없이 상수 이동 |
프레임률 의존. 60FPS·30FPS에서 속도 다름 |
⚠ 공식 msw-search 안티패턴 주의
mlua_Document_Retriever에 "Entity Movement Control Using MovementComponent" 문서가 높은 점수로 잡히지만, 본문은 OnUpdate 안에서 MovementComponent:SetPosition(...)으로 매 프레임 이동하는 안티패턴이다 — 이 문서는 무시하고 위 표의 MoveToDirection / Translate로. FlappyFish Remake·Stopping the Taxi·Making a Moving Foothold도 delta 누락 또는 Position 직접 대입을 보여주므로 참조 시 주의.
MovementComponent 부착 — 몬스터/NPC
몬스터 모델에 기본 포함 안 됨. .model에 다음 컴포넌트가 모두 있어야 한다.
| 컴포넌트 | 비고 |
|---|---|
MOD.Core.TransformComponent |
기본 |
MOD.Core.SpriteRendererComponent |
렌더 |
| Body (맵 타입) | RigidbodyComponent(MapleTile) / KinematicbodyComponent(RectTile) / SideviewbodyComponent(SideViewRectTile) |
MOD.Core.MovementComponent |
InputSpeed = 2.0 등 — 이동 API용 |
교차 참조
- **넉백(1-shot 임펄스)**은 연속 이동이 아니므로 §3-1 그대로 사용
- 맵 타입별 Body 선택 / InputSpeed 환산식:
msw-general/references/platform.md§4·§10
2. Damage Model
AttackComponent:
method integer CalcDamage(attacker, defender, attackInfo) -- default 1
method boolean CalcCritical(attacker, defender, attackInfo) -- default false
method float GetCriticalDamageRate() -- default 2.0
method int32 GetDisplayHitCount(attackInfo) -- default 1
method void OnAttack(defender)
HitComponent:
method void OnHit(Entity attacker, integer damage, boolean isCritical, string attackInfo, int32 hitCount)
emitter EmitHitEvent(HitEvent)
2-1. HitEvent 페이로드
AttackCenter: Vector2
AttackerEntity: Entity (nilable)
Damages: List<integer> -- 멀티히트 분할
Extra: any -- ★ 확장 슬롯 (knockback/stun/element/tags)
IsCritical: boolean
TotalDamage: integer
FeedbackAction: HitFeedbackAction -- ⚠ 전체 enum deprecated
부가 정보(넉백 벡터·경직 시간·속성)는 Extra 테이블로 실어 전달.
2-2. AttackEvent 페이로드
DefenderEntity: Entity 하나. 공격자는 핸들러 self.
3. Hit Reaction
3-1. 넉백 — Body별 API
| Body (맵 타입) | 구현 |
|---|---|
| Rigidbody (MapleTile) | body:AddForce(Vector2(dir*5, 3)) ★권장 · SetForce · JustJump(Vector2(0, 4)) (수직) |
| Kinematicbody (RectTile/탑다운) | body.MoveVelocity = Vector2(dir*5, 0) — AddForce 없음 |
| Sideviewbody (SideViewRectTile) | body.MoveVelocity + body.JumpSpeed |
- Rigidbody 는 엔진이 자동 감쇠. Kinematic/Sideview 는
OnUpdate에서 수동 감쇠 (MoveVelocity *= 0.9) - 벽 튕김:
FootholdCollisionEvent구독 후 velocity 반전 - 넉백은 1-shot 임펄스 — 연속 이동(추적·비행)과 혼동 금지. 연속 이동은 §1-6
- ⚠ 금지: Body 활성 엔티티에
TransformComponent.Position직접 대입 → 네트워크 동기화 차단.body:SetPosition(...)은 텔레포트 메서드이므로OnUpdate루프에서 호출 금지 (§1-6)
3-2. i-frame
표준 패턴: _UtilLogic.ElapsedSeconds 기반 deadline 체크 + HitComponent:IsHitTarget 에서 false. DefaultPlayer 기본 PlayerHit.mlua 가 이 패턴을 그대로 제공 (§9-4).
대안: 무적 중 HitComponent.CollisionGroup 을 별도 그룹으로 교체 → 판정 자체 제외. 프레임 단위 정확도에 유리.
3-3. 상태이상 (Buff/Debuff)
네이티브 부재. @Component BuffComponent 직접 구현 + _TimerService:SetTimerRepeat 틱 + 커스텀 StatusAppliedEvent/StatusExpiredEvent 브로드캐스트.
단순 스턴 1종이면 StateComponent:ChangeState("STUN") + 입력/AI 차단 플래그로 충분.
4. Game Feel — 전부 네이티브
| 요소 | API | ExecSpace |
|---|---|---|
| Hit Stop (전역) | _UtilLogic:SetClientTimeScale(float) — 0~100 |
ClientOnly |
| Hit Stop (개별) | renderer.PlayRate = 0 (Sprite/Skeleton/Avatar) |
@Sync |
| Slow Motion | _UtilLogic:SetClientTimeScale(0.3) + 타이머 복귀 |
ClientOnly |
| Camera Shake | cameraComp:ShakeCamera(intensity, duration, targetUserId?) |
Client |
| Camera Zoom | cameraComp:SetZoomTo(percent, duration, targetUserId?) · 단 IsAllowZoomInOut=true 선행 |
Client |
| Hit Flash | spriteRenderer.Color = Color(r,g,b,a) → 타이머 복귀 |
@Sync |
| Color HDR 오버브라이트 | Color.HSVToRGB(h, s, v, hdr=true) — 1.0 초과 허용 |
— |
| VFX 고정 | _EffectService:PlayEffect(clipRUID, instigator, pos, zRot, scale, isLoop?, options?) → serial |
— |
| VFX 부착 | _EffectService:PlayEffectAttached(clipRUID, parent, localPos, localZRot, localScale, isLoop?, options?) |
— |
| VFX 제거 | _EffectService:RemoveEffect(serial) |
— |
| SFX 2D | _SoundService:PlaySound(id, volume, targetUserId?) |
Client |
| SFX 3D | _SoundService:PlaySoundAtPos(id, pos, listener, volume) |
Client |
| SFX 루프 | PlayLoopSound / PlayLoopSoundAtPos |
Client |
| SFX 부착 | SoundComponent:Play() · 피치 랜덤은 Pitch 0~3 |
Client |
| BGM | _SoundService:PlayBGM(id, volume) / StopBGM(immediately) |
Client |
| 프리로드 | _SoundService:LoadSound(id) |
ClientOnly |
PlayEffect options 키: FlipX, FlipY, SortingLayer, OrderInLayer, Alpha, StartFrameIndex, EndFrameIndex, PlayRate, SyncFlip, Color, MaterialID, IgnoreMapLayerCheck, LitMode
현재 카메라 획득: _CameraService:GetCurrentCameraComponent().
ParticleService — 내장 파티클
RUID 없이 enum 값만으로 범용 파티클 연출. 3종류:
-- BasicParticle: 범용 프리셋 (RUID 불필요)
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: 커스텀 스프라이트를 파티클로 (spriteRUID 필요)
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: 넓은 영역 환경 파티클 (areaSize 추가)
integer _ParticleService:PlayAreaParticle(AreaParticleType, Vector2 areaSize, Entity instigator, Vector3 pos, number zRot, Vector3 scale, boolean isLoop, Dictionary options)
void _ParticleService:RemoveParticle(integer serial)
options 키: Color, SortingLayer, OrderInLayer, ParticleSize, ParticleCount
루프 파티클(isLoop=true)은 반드시 RemoveParticle(serial)로 정리. serial을 self._T에 저장해두어야 나중에 제거 가능.
BasicParticleType 전체 목록
| 계열 | 이름 | 설명 |
|---|---|---|
| 폭발/충격 | SparkExplosion |
스파크 (1회) — 범용 피격 |
SparkLoop |
연속 스파크 | |
SparkRadialExplosion |
원형으로 튀는 스파크 | |
SmallExplosion |
작은 폭발 + 연기 | |
BigExplosion |
큰 폭발 + 연기 | |
TinyExplosion |
아주 작은 폭발 (Color 옵션 무효) | |
DustExplosion |
원형 파동 + 연기 (Color 옵션 무효) | |
EnergyExplosion |
원형 파동 후 중심 수렴 | |
CircleBurst |
원형 빛 폭발 | |
PillarBurst |
원형 빛 폭발 + 방향성 빛 | |
| 불/화염 | FireField |
만화풍 불꽃 |
FireFieldIntense |
강한 만화풍 불꽃 | |
FireBall |
한 곳에 화염 생성 | |
FlameThrower |
화염 방출 | |
LargeFlames |
바닥에서 큰 화염 | |
MediumFlames |
바닥에서 중간 화염 | |
TinyFlames |
바닥에서 작은 화염 | |
WildFire |
거대한 화염 기둥 (Color 옵션 무효) | |
| 번개/전기 | LightningOrbSharp |
구형 전기 파티클 |
LightningStrikeSharp |
번개 | |
LightningStrikeSharpTall |
긴 번개 | |
LightningOrbSoft |
전기파 방출 | |
LightningBlast |
주기적 전기파 | |
LightningStrike |
주기적 번개 | |
LightningStrikeTall |
주기적 긴 번개 | |
| 버프/마법 | Aura |
바닥에서 오로라 빛 |
Buff |
바닥에서 강한 빛 상승 | |
Charge |
큰 파티클이 한 점으로 수렴 | |
ChargeOrb |
파티클이 한 점으로 수렴 | |
Enchant |
큰 빛 주변에 빛과 파티클 | |
SpinField |
회전하는 원 주변에 파티클 | |
StarVortex |
별빛이 중심으로 수렴 | |
Nova |
넓은 원형 파동 | |
UpperCylinder |
바닥에서 기둥 형태 상승 | |
| 기타 | Firework |
불꽃놀이 |
FireworkCluster |
여러 폭죽 동시 | |
FireFlies |
반딧불이 | |
GoopSpray |
옆으로 액체 분출 | |
GoopSprayEffect |
바닥으로 액체 분출 | |
DustStorm |
넓은 모래 폭풍 | |
RisingSteam |
바닥에서 흰 안개 상승 | |
BigSplash |
큰 물보라 | |
Shower |
한 곳에 물 뿌림 |
SpriteParticleType 전체 목록 (8종)
| 이름 | 설명 |
|---|---|
BurstBig |
스프라이트가 원형으로 퍼지며 등장 |
SpawnField |
파티클+스프라이트가 원형 영역에 등장 |
BurstNova |
파티클+스프라이트가 원형으로 폭발 |
SimpleSpawn |
파티클+스프라이트 단순 등장 |
Burst |
파티클+스프라이트가 퍼짐 |
Stream |
특정 방향으로 이동하며 생성 |
StreamSharp |
가느다란 선으로 특정 방향 이동 |
AdditiveColor |
스프라이트에 색상 효과 적용 |
AreaParticleType 전체 목록 (12종)
| 이름 | 설명 |
|---|---|
Rain |
비 |
Snow |
눈 |
FogCalm |
안개 |
FogHeavy |
짙은 내려오는 안개 |
FogLively |
올라오는 안개 |
CalmStarField |
올라오는 별 무리 |
StarFieldSimple |
반짝이는 별 무리 |
StarFog |
별+성운 파티클 (제자리) |
StarFogFlow |
별+성운 파티클 (상승) |
Windlines |
가는 선 |
WindlinesBig |
가는 선 + 굵은 선 |
WindlinesSpeedy |
빠른 직선 |
EffectService vs ParticleService 선택
| 상황 | 권장 |
|---|---|
| 메이플스토리 스킬/피격 애니메이션 (구체적 이미지) | EffectService (RUID 지정) |
| 범용 피격·폭발 (빠른 구현) | ParticleService.BasicParticle |
| 커스텀 이미지를 파티클로 흩뿌리기 | ParticleService.SpriteParticle |
| 비·눈·안개 등 환경 연출 | ParticleService.AreaParticle |
| 버프 오라 등 지속 효과 | 둘 다 isLoop=true 가능 |
| 풍성한 연출 | EffectService + ParticleService 동시 조합 |
서버 이벤트 → 클라이언트 이펙트 표준 패턴:
@Sync프로퍼티 변경 →OnSyncProperty(ClientOnly)감지 → EffectService/ParticleService 호출.
5. 사망 / 부활
| 이벤트 | 발행 조건 | 페이로드 |
|---|---|---|
DeadEvent |
StateComponent:ChangeState("DEAD") 시 자동 |
없음 |
ReviveEvent |
부활 시 자동 | 없음 |
StateChangeEvent |
모든 상태 전환 | CurrentStateName, PrevStateName |
킬러 추적: DeadEvent 페이로드 없음 → HandleHitEvent 에서 self.LastAttacker = event.AttackerEntity 캐시 후 HandleDeadEvent 에서 사용.
수동 발행: StateComponent:EmitDeadEvent(DeadEvent) / EmitReviveEvent(ReviveEvent) / EmitStateChangeEvent(StateChangeEvent) 가능.
플레이어 전용 사망·부활은 §9-1 PlayerComponent.Respawn/ProcessDead/ProcessRevive 우선 사용.
6. Event Bus
| 논리 이벤트 | MSW 구현 |
|---|---|
| OnAttackStart | OnAttack 훅 또는 커스텀 AttackStartEvent |
| OnAttackHit / OnDamageTaken | 네이티브 HitEvent |
| OnAttackMiss | 커스텀 — IsAttackTarget false 시 SendEvent |
| OnCriticalHit | HitEvent.IsCritical 플래그로 커버 |
| OnDeath / OnRevive | 네이티브 DeadEvent/ReviveEvent |
| OnStateChange | 네이티브 StateChangeEvent |
| OnKill / OnBlocked / OnParry / OnStatusApplied | 커스텀 @Event |
6-1. 커스텀 이벤트 규칙
- 정의:
@Event script XxxEvent extends EventType+property들 - 수신:
handler키워드 (method 아님),@EventSender("Self" | "Service","XxxService" | "Logic","XxxLogic") - 연결/해제:
entity:ConnectEvent(XxxEvent, self.Handler)/OnEndPlay에서 반드시DisconnectEvent(엔진 자동 해제 없음) - 전역:
@Logic CombatEventBusLogic싱글턴 +@EventSender("Logic","CombatEventBusLogic")
7. AI FSM — StateComponent
readonly @Sync property string CurrentStateName = "IDLE"
method boolean AddState(string stateName, Type stateType)
method boolean AddCondition(string stateName, string nextStateName, boolean reverseResult = false)
method boolean ChangeState(string stateName)
method void RemoveState(string name)
method void RemoveCondition(string stateName, string nextStateName)
emitter EmitDeadEvent / EmitReviveEvent / EmitStateChangeEvent
- BehaviourTree 네이티브 부재 (
BehaviourTreeStatusenum 만 존재) - 플레이어 조회:
_UserService.UserEntities: ReadOnlyDictionary<string, Entity> - 거리·시야:
(target.WorldPosition - self.WorldPosition):Length()+LookDirectionX부호 - 쿨다운:
_TimerService:SetTimerOnce또는_UtilLogic.ElapsedSecondsdeadline - 보스 페이즈:
AddCondition+ HP% 체크 자동 전이, 또는 스크립트ChangeState("PHASE2")강제 - Threat/Aggro Table 은 커스텀
table<Entity, float>필요
몬스터 엔티티 전체 구성 (9컴포넌트 조립 + ActionSheet 5상태 + 히트박스 + AI 선택 + MonsterAI 스크립트 부착) →
references/monster-setup.md본 SKILL.md는 전투 특화(ATTACK/HIT/DEAD + DeadEvent/ReviveEvent)만 다룬다. 일반 mlua 상태 머신/스크립팅 패턴은
msw-scripting참조.
8. UI 네이티브
| UI | API |
|---|---|
| HP 바 (화면 고정) | SliderComponent (MinValue/MaxValue/Value/FillRectColor/FillRectImageRUID/Direction/UseHandle) + SliderValueChangedEvent. ⚠ UI 엔티티 전용 |
| 데미지 숫자 | DamageSkin* 3종 + DamageSkinService — §11 |
| 크로스헤어 | .ui 에 SpriteGUIRendererComponent |
| 콤보 카운터 / 버프 아이콘 | TextComponent + SpriteGUIRendererComponent |
월드스페이스 HP바 (머리 위): 네이티브 부재. 자식 엔티티 SpriteRendererComponent 에 LocalScale.x = hp/maxHp 또는 TiledSize.x 조정 (SpriteDrawMode.Tiled 전제).
PixelRendererComponent기반 체력바 (@Sync HP + Lazy Init + 색상 변화) 전체 구현 →references/hp-gauge.md
9. DefaultPlayer 전투 네이티브
플레이어 엔티티는 HP·부활·입력을 네이티브로 갖는다. 커스텀 Hp/MaxHp 프로퍼티 만들지 말 것 — PlayerComponent 사용.
PlayerComponent/PlayerControllerComponent전체 프로퍼티·메서드 표는msw-defaultplayer/SKILL.md참조. 여기서는 전투 핵심만.
9-1. 전투 핵심 API
| 항목 | 사용법 |
|---|---|
| HP 감소 | self.Entity.PlayerComponent.Hp -= event.TotalDamage |
| 사망 판정 | PlayerComponent:IsDead() |
| 부활 | PlayerComponent:Respawn() — RespawnPosition → SpawnLocation → 맵 진입점. DeadEvent/ReviveEvent 자동 발행 |
| 클라 전용 사망 처리 | @ExecSpace("Client") ProcessDead(targetUserId?) / ProcessRevive(targetUserId?) |
| 방향 판정 ★ | PlayerControllerComponent.LookDirectionX (+1 우, -1 좌). TransformComponent.Scale.x 사용 금지 |
| 액션 훅 오버라이드 | ActionAttack / ActionJump / ActionInteraction(key, isKeyDown) 등 |
| 액션 이벤트 수신 | EmitPlayerActionEvent(PlayerActionEvent) → §9-3 |
9-3. PlayerActionEvent
property string ActionName -- "Attack" / "Jump" / "Crouch" / ...
property Entity PlayerEntity
PlayerAttack extends AttackComponent 에서 @EventSender("Self") handler HandlePlayerActionEvent(...) 로 수신 후 event.ActionName == "Attack" 분기가 기본 패턴.
9-4. 기본 제공 템플릿 (RootDesk/MyDesk/)
수정 없이 복붙 가능. 필요 시 오버라이드:
| 파일 | 역할 | 핵심 포인트 |
|---|---|---|
PlayerAttack.mlua |
전방 Box 공격 | LookDirectionX 로 방향, AttackFast + CollisionGroups.Monster, CalcDamage=50, 30% 크리 |
PlayerHit.mlua |
i-frame | ImmuneCooldown 프로퍼티, _UtilLogic.ElapsedSeconds deadline, IsHitTarget 오버라이드 |
Monster.mlua |
몬스터 HP | 커스텀 @Sync Hp (PlayerComponent 없음), HandleHitEvent → Dead/Respawn |
MonsterAttack.mlua |
sprite 크기 기반 근접 | IsAttackTarget 에서 isvalid(defender.PlayerComponent) + __base:IsAttackTarget(...) super |
9-5. 시간 기준
_UtilLogic.ElapsedSeconds 권장 (월드 기준, 일시정지/리스토어 일관). os.clock() 금지.
9-6. 표준 CollisionGroup
| 상수 | 용도 |
|---|---|
CollisionGroups.Player |
몬스터 → 플레이어 공격 |
CollisionGroups.Monster |
플레이어 → 몬스터 공격 |
CollisionGroups.HitBox |
HitComponent.CollisionGroup 기본 |
10. 아바타 모션 — AvatarStateAnimationComponent
StateComponent 전환을 아바타 애니메이션에 자동 연결.
@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")→ 매핑된MapleAvatarBodyActionState.Hit자동 재생- 전투 관련 상태값:
Attack=3,Hit=14,Dead=10,Alert=4,Heal=13 IsLegacy=false고정,StateToAvatarBodyActionSheet만 사용
Avatar 컴포넌트(
AvatarRendererComponent등) 전반은msw-defaultplayer참조. 본 섹션은 전투 모션 매핑만 다룬다.
11. 데미지 스킨 (숫자 표시)
기본 RUID
| 용도 | RUID | 사용처 |
|---|---|---|
| 타격 | 3271c3e79bf04ecba9a107d55495970d |
공격자 DamageSkinSettingComponent.DamageSkinId 디폴트 |
| 피격 | 02c22d93421b4038b3c413b3e40b57ec |
피격자 측 표시 — _DamageSkinService:Play 수동 호출 |
| 회복 | d58b67cf0f3a4eaf9fe1ad87c0ffac8a |
힐/포션 — _DamageSkinService:Play 수동 호출 |
11-1. 자동 모드 (컴포넌트 기반)
Attack/AttackFast 호출 시, 아래 3개가 모두 있으면 자동으로 데미지 숫자 표시:
| 위치 | 컴포넌트 | 역할 |
|---|---|---|
| 공격자 | DamageSkinSettingComponent |
어떤 스킨/스타일로 표시할지 |
| 피격자 | DamageSkinSpawnerComponent |
표시 위치 오프셋 |
| 피격자 | DamageSkinComponent |
데미지 숫자 본체(엔티티 위) |
3개 모두 .model에 포함시켜두면 스크립트 코드 0줄로 데미지 숫자가 뜬다.
DamageSkinSettingComponent (공격자)
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
DamageSkinId |
DataRef | 타격 RUID (위 표) | 데미지 숫자 스킨 RUID |
DamageSkinScale |
Vector2 | (1, 1) | 숫자 크기 |
Alpha |
float | 1 | 투명도 |
PlayRate |
float | 1 | 재생 속도 |
DelayPerAttack |
float | 0.05 | 멀티히트 간 딜레이(초) |
TweenType |
DamageSkinTweenType | Default | 연출 타입 |
LitMode |
LitMode | Default | 조명 영향 |
DamageSkinTweenType: Default(팝업) / Volcano(부채꼴) / Blade(겹침) / 각 *Mini(75% 축소)
DamageSkinSpawnerComponent (피격자)
| 속성 | 타입 | 기본값 |
|---|---|---|
DamageSkinOffset |
Vector2 | (0,0) |
11-2. 수동 모드 — DamageSkinService
자동 모드로 잡히지 않는 케이스(힐, Miss/Guard, 비표준 데미지원)는 _DamageSkinService 직접 호출.
_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는Client영역 — 서버 로직(HP 차감 등)에서 호출하려면@ExecSpace("Client")메서드로 래핑하거나@Sync프로퍼티 변경 후OnSyncProperty에서 트리거.
11-3. 활용 레시피
(a) 크리티컬 강조 — 자동 모드 + 동적 스케일
자동 모드는 IsCritical=true면 빨강 폰트로 자동 처리. 추가 강조하려면 공격자 측 스케일을 일시 증대:
@ExecSpace("ServerOnly")
method int32 CalcDamage(Entity attacker, Entity defender, string attackInfo)
return 100
end
@ExecSpace("ServerOnly")
method boolean CalcCritical(Entity attacker, Entity defender, string attackInfo)
return math.random() < 0.3
end
method float GetCriticalDamageRate()
return 2.5 -- 100 → 250
end
DamageSkinSettingComponent.TweenType = Volcano (부채꼴 흩뿌림) 또는 Blade (겹침)로 크리티컬 시각 차별화.
(b) 힐 / 회복 — 수동 호출
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 (머리 위)
Vector2(1, 1), 1.0, 1.0, LitMode.Default
)
end
(c) Miss / Guard / Resist 텍스트
local HIT_RUID = "02c22d93421b4038b3c413b3e40b57ec"
@ExecSpace("Client")
method void ShowMiss(Entity target)
_DamageSkinService:PlayTextDamage(
target, HIT_RUID, DamageSkinTextType.Miss, DamageSkinTweenType.Default
)
end
AttackComponent:IsAttackTarget이 false를 반환했을 때 호출하면 "miss 연출 + 데미지 0".
(d) 멀티히트 — 한 번 호출로 N개 분할
_DamageSkinService:Play의 damages 인자에 List를 통째로 넘기면 DelayPerAttack(공격자 컴포넌트의 값) 간격으로 순차 표시:
_DamageSkinService:Play(target, ATTACK_RUID, 0, { 12, 8, 14, 11, 9 },
DamageSkinTweenType.Default, false, Vector2(0,0), Vector2(1,1), 1, 1, LitMode.Default)
자동 모드도 HitEvent.Damages(List)로 동일 동작 — GetDisplayHitCount(attackInfo)를 오버라이드해 분할 수를 제어한다.
(e) Preload — 첫 표시 끊김 방지
스킨 RUID 첫 사용 시 텍스처 로드 지연이 있을 수 있다. 맵 진입 시 미리 로드:
@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 사용 케이스
| TweenType | 추천 상황 |
|---|---|
Default |
일반 타격 |
Volcano |
크리티컬 / 광역 타격 (위로 흩뿌림) |
Blade |
연속 베기 / 콤보 (숫자 겹침) |
*Mini |
DoT(독/화상) 같은 작은 데미지 — 화면 점유 줄임 |
(g) 진영별 스킨 차별화
플레이어 vs 적, PvP 진영 등에서 다른 스킨 RUID를 쓰려면 DamageSkinSettingComponent.DamageSkinId를 런타임에 교체:
self.Entity.DamageSkinSettingComponent.DamageSkinId = MY_TEAM_SKIN_RUID
12. 히트 이펙트 — HitEffectSpawnerComponent
피격자에 부착만 하면 HitEvent 발생 시 자동으로 히트 이펙트 재생. property 없음, .model 에 컴포넌트 추가만.
13. 완성형 전투 체크리스트
- 공격자 모델:
AttackComponent상속 스크립트 (+ 선택:DamageSkinSettingComponent) - 피격자 모델:
HitComponent+HitEffectSpawnerComponent+ (선택:DamageSkinSpawnerComponent+DamageSkinComponent) - HitComponent:
IsLegacy=false,ColliderType/BoxSize/CircleRadius설정,CollisionGroup설정 - 상태 모션:
StateComponent+AvatarStateAnimationComponent.StateToAvatarBodyActionSheet에ATTACK/HIT/DEAD등록 - HP 처리: 플레이어는
PlayerComponent.Hp, 몬스터는 커스텀@Sync Hp - 방향 판정:
LookDirectionX(Scale.x 금지) - 시간 기준:
_UtilLogic.ElapsedSeconds(os.clock 금지) - 이벤트 정리:
OnEndPlay에서DisconnectEvent명시 해제 - Body 규칙: Body 활성 엔티티에
TransformComponent.Position직접 대입 금지
14. 커스텀 구현이 필수
Buff/Debuff · BehaviourTree · Aggro Table · 투사체 풀링 · 관통/최대 히트 수 · 경직 레벨 체계 · 자원(MP/Stamina/Rage) · 세계→스크린 좌표 변환 · 월드스페이스 HP바
비범위
- 플레이어 전반(HP/이동/카메라/코스튬 외):
msw-defaultplayer - mlua 일반 문법·라이프사이클:
msw-scripting .model저작 규칙·템플릿:msw-general
More from msw-git/msw-ai-coding-plugins-official
msw-general
Foundation skill for MSW (MapleStory Worlds). Read this FIRST before anything else in MSW.
27msw-search
MSW search integration — (1) vector search for API docs and implementation guides (msw-guide-mcp or curl against mlua_Document_Retriever / mlua_API_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 rendering', etc. Keywords: document search, API details, examples, guide, retriever, resource, sprite, animation, sound, RUID, resource pack, avatar.
27msw-scripting
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.
27msw-defaultplayer
MSW DefaultPlayer(캐릭터) 관리. DefaultPlayer.model 파일 직접 조회/수정, 컴포넌트 추가/제거, 이동 속도/점프력/HP/카메라 설정, 맵 모드별 이동 컴포넌트. Use for DefaultPlayer model, player components, movement speed, jump force, HP, camera, physics. Keywords: player, DefaultPlayer, speed, jump, HP, camera, gravity, 플레이어, 캐릭터, 이동속도, 점프력, 중력, 카메라, 체력, 부활.
27msw-costume
MSW 아바타 코스튬 관리. 코스튬 조회/적용, CostumeManagerComponent 17슬롯, 아바타 유틸리티. DefaultPlayer뿐 아니라 모든 엔티티(NPC, 몬스터 등)에 적용 가능. Use for avatar costume get/set, CostumeManagerComponent 17 equip slots, avatar utilities. Keywords: costume, avatar, equip, slot, 코스튬, 아바타, 장비, 꾸미기, 모자, 상의, 하의, 무기.
23msw-avatar
MSW 아바타 관리 — 코스튬(CostumeManagerComponent 17슬롯) + 애니메이션 3계층 파이프라인(StateComponent → AvatarStateAnimationComponent → AvatarRendererComponent). State 키(대문자)/AvatarBodyActionStateName(소문자)/MapleAvatarBodyActionState enum/스프라이트 액션 ID(swingO1, shoot1) 4단계 구분, IsLegacy/ActionSheet/StateToAvatarBodyActionSheet 두 시스템, PlayerControllerComponent 자동 상태 전이와 ActionStateChangedEvent 충돌 시 RemoveActionSheet/SetActionSheet/BodyActionStateChangeEvent로 해결. DefaultPlayer뿐 아니라 모든 엔티티(NPC, 몬스터 등)에 적용 가능. Use for avatar 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, ActionStateChangedEvent, BodyActionStateChangeEvent, RemoveActionSheet, SetActionSheet, StateToAvatarBodyActionSheet, AvatarStateAnimationComponent, AvatarRendererComponent, MapleAvatarBodyActionState, PlayerControllerComponent, 아바타, 코스튬, 애니메이션, 상태, 동작, 장비, 무기, 공격, 활, 칼, 사격, 휘두르기, 커스텀 액션, 자동 재생 차단, 매핑 변경.
5