arena/context/combat.md

18 KiB

Update: Special Projectile Trail

  • SPECIAL_EFFECT.PROJECTILE.TRAIL controls optional afterimages for the moving special projectile. Each trail copy uses the projectile's current texture frame, scale, rotation, and flip state.
  • Trail objects are visual-only combat objects: they fade out and self-dispose, but they do not participate in hit detection.
  • TRAIL.INTERVAL_MS and TRAIL.LIFETIME_MS bound how many afterimages can exist at once.

Update: Split Special Projectile Visual Configs

  • Special projectile visual asset settings are separated by caster type. Melee visual sheets are configured under SPECIAL_EFFECT.MELEE; ranged visual sheets are configured under SPECIAL_EFFECT.RANGE.
  • SPECIAL_EFFECT.PROJECTILE now carries shared projectile behavior only: hold time, acceleration/ease, fallback speed, target density area, travel clamp, hit radius, max lifetime, and optional trail visuals.

Update: One-Shot Accelerating Special Projectile

  • SPECIAL_EFFECT.MELEE.REPEAT = 0 makes melee special sprites play once. special-melee-effect-1 currently has frameSequence commented out, so it uses the sprite sheet's natural frame order.
  • Special projectile movement now uses a tween instead of constant physics.moveTo. SPECIAL_EFFECT.PROJECTILE.startHoldMs keeps the effect stationary for the pre-launch tell, travelDurationMs controls the launch duration when set, and movementEase controls the acceleration curve. If travelDurationMs is unset, movement falls back to speed.
  • Projectile hit checks still run from the scene UPDATE event while the tween moves the sprite, so instant-kill path detection remains active during acceleration.

Update: Special Effect Frame Sequence Refresh

  • createSpecialAnimation() now rebuilds a special animation when the existing Phaser global animation no longer matches the configured frames, repeat count, or frame rate. This prevents stale animation keys from hiding frameSequence edits.
  • frameSequence values stay 1-based in config and are converted with generateFrameNumbers(..., { frames }), so repeated frames are preserved when a special asset enables a custom sequence.

Update: Special Effect Frame Rate And Render Budget

  • SPECIAL_EFFECT.FRAME_RATE_MULTIPLIER multiplies only special-effect animation frame rates. Caster sparkle, melee special sprites, and ranged special projectile frames can play faster or slower without changing caster hold time, launch delay, projectile movement speed, travel distance, or cleanup timers.
  • SPECIAL_EFFECT.FOCUS_LAYER.BLUR_MAX_FIGHTERS caps when specialEffects.js creates the full-arena blurred render-texture snapshot. Above that living-fighter count, the special focus keeps the dim layer and raised caster but skips the expensive blur pass.
  • Special projectile hit checks prefer scene.combatTargetIndex and scan only spatial cells around the projectile segment, falling back to scene.fighters when no index exists.

Update: Special Effect Projectile

  • specialEffects.js owns the one-shot special effect flow: asset preload/animation creation, random live-match scheduling, underdog caster selection, caster pose lock, launch visuals, projectile path checks, and cleanup.
  • SPECIAL_EFFECT in src/constants.js tunes trigger timing, caster Hurt-frame hold, camera zoom, melee projectile assets, ranged projectile asset scale/speed/travel distance/hit radius, target density area, arena edge padding, and lifetime. WORLD_EFFECT.SPECIAL points to the same config object.
  • Casters must be living, non-elite, non-magic fighters from teams that are not currently tied for first by represented living count. When SPECIAL_EFFECT.CASTER.BALANCE_NON_MAGIC_TYPES is enabled, caster selection first picks among available non-magic types and then picks a fighter from that type, preventing the larger melee roster from overwhelming ranged special casts. If no caster exists at the chosen time, the timer retries within the configured window instead of firing multiple times.
  • Special casters receive realtime invulnerability for SPECIAL_EFFECT.CASTER.INVULNERABLE_MS. combat.js checks that window in normal attacks, world-effect damage, and special instant kills, while worldEffects.js also skips frost survivor effects for invulnerable fighters.
  • While the caster holds the Hurt frame, specialEffects.js pauses fighter AI, Arcade Physics, the scene clock, existing combat-object physics velocity, combat-object animations, and combat-object tweens. Existing combat/world delayed calls and already-falling meteor/frost tweens stop advancing until the realtime preparation hold releases into the attack animation.
  • The Hurt-frame preparation also spawns a caster sparkle overlay from public/assets/effects/special/effect.png. Its animation uses the 1-based frame sequence [2, 3, 4] only, positions near the caster's eyes through SPECIAL_EFFECT.CASTER_SPARKLE, and is disposed before the attack animation starts.
  • Caster emphasis uses SPECIAL_EFFECT.FOCUS_LAYER: specialEffects.js snapshots the current battlefield into a render texture, applies Phaser Blur FX when available, adds a dim layer, and raises the caster above both layers until cleanup.
  • The special camera does not follow the projectile. When projectile movement begins, zoomOutSpecialEffectCameraFocus() zooms out in place using SPECIAL_EFFECT.CAMERA.PROJECTILE_VIEW_ZOOM and PROJECTILE_ZOOM_OUT_MS so the projectile remains readable without dragging the camera off the arena.
  • Melee special projectile effects can define 1-based frameSequence arrays. specialEffects.js converts them to Phaser frames so specific frames can be repeated for readability. The projectile's startHoldMs keeps it visible at the caster before travel begins.
  • At target-selection time, the special projectile scans living enemies with a summed-area table and locks onto the SPECIAL_EFFECT.PROJECTILE.targetAreaTiles square containing the highest represented stackCount population. The moving projectile visual is type-based: melee casters fire one random SPECIAL_EFFECT.MELEE.ASSETS sprite, while ranged casters use SPECIAL_EFFECT.RANGE. Movement uses a tweened Arcade Physics sprite, matching normal ranged projectile path-update checks while allowing acceleration, and projectile travel is cut to the arena bounds using arenaEdgePadding.
  • The special projectile calls applySpecialEffectInstantKill() from combat.js, so instant kills still use the normal death animation, death-stat recording, kill log attribution when there is a surviving caster, split-on-death behavior, scoreboard refresh, and match-finish checks.

Update: Elite Magic Attack Effect Scale

  • combat.js resolves instant-spell attack effect scale through constants instead of hard-coding FIGHTER.SCALE.
  • Normal spell effects use FIGHTER.SCALE * FIGHTER.ATTACK_EFFECT_SCALE_MULTIPLIER.
  • Elite magic spell effects additionally multiply by FIGHTER.ELITE.ATTACK_EFFECT_SCALE_MULTIPLIER, keeping caster body scale and effect scale separately tunable.

Update: Elite Target Damage And Density

  • combat.js uses fighter.isElite to split damage rules. Elite critical hits deal the greater of the ordinary hit or COMBAT.CRITICAL_DAMAGE_PERCENT of max HP; normal critical hits deal NORMAL_CRITICAL_DAMAGE_MULTIPLIER times the ordinary hit.
  • Elite attack and movement speed are calculated through FIGHTER.ELITE.ATTACK_SPEED_* and MOVE_SPEED_* constants. Each multiplier is additive: 0 removes its added stack bonus, while 1 applies its configured exponent.
  • Elite direct kills trigger a kill splash at the killed fighter's body position. The splash deals COMBAT.ELITE_KILL_SPLASH_DAMAGE_PERCENT of that killed fighter's max HP to living enemies inside COMBAT.ELITE_KILL_SPLASH_RADIUS; splash kills are recorded normally, recursive splash chaining is controlled by ELITE_KILL_SPLASH_CHAIN_ENABLED, and the optional visual uses square pixel dots rather than smooth circles.
  • Kills still record the attacker/defender and drive match resolution, but COMBAT.KILL_REWARD_ENABLED = false prevents heal effects, scale growth, and kill-derived speed multipliers in compressed elite battles.
  • worldEffects.js passes an effect type into applyWorldEffectDamage(): normal targets retain fixed fire/frost damage, while elite targets take WORLD_EFFECT.METEOR_DAMAGE_PERCENT or FROST_DAMAGE_PERCENT of max HP.
  • Dense-area target scanning adds each fighter's represented stackCount into its tile, preventing compressed armies from disappearing from meteor/frost targeting pressure.

Update: Dense-Area Meteor Barrage

  • worldEffects.js aggregates living fighters on the arena tile grid and uses a summed-area scan to select the WORLD_EFFECT.AREA_TILES square with the highest population.
  • That selected square is a warning/focus area. Each fire or frost event schedules WORLD_EFFECT.IMPACT_COUNT_MIN to IMPACT_COUNT_MAX smaller strikes inside it.
  • Tune the large warning visibility with WORLD_EFFECT.WARNING_DURATION_MS, actual damage/frost footprints with WORLD_EFFECT.IMPACT_AREA_TILES, sprite size with WORLD_EFFECT.IMPACT_VISUAL_SCALE, strike spacing with WORLD_EFFECT.IMPACT_STAGGER_MS, and per-strike variation with WORLD_EFFECT.SIZE_SCALE_VARIANCE.
  • WORLD_EFFECT.INTERVAL sets the delay before the first barrage; subsequent normal barrages use WORLD_EFFECT.REPEAT_INTERVAL, with SUDDEN_DEATH.INTERVAL_MS taking over once sudden death is active.
  • Meteor impact shake uses the same size multiplier, scaling from WORLD_EFFECT.METEOR_SHAKE_DURATION_MS and WORLD_EFFECT.METEOR_SHAKE_INTENSITY.

Update: Large Battle Targeting

  • combat.js now prepares a per-frame target spatial index through prepareCombatFrame(scene).
  • resolveTargetEnemy() keeps valid cached targets until their scan interval expires, then immediately looks up a fresh nearest enemy through the spatial grid.
  • Nearest enemy lookup searches grid cells outward from the fighter's current cell, with full-array scanning kept only as a fallback when no frame index exists.
  • Large-battle corpse cleanup uses PERFORMANCE.LARGE_BATTLE_FIGHTER_THRESHOLD and PERFORMANCE.LARGE_BATTLE_DEAD_DESPAWN_DELAY_MS from src/constants.js.

Update: Team Shadow Animations And Frost Tint

  • Dead fighters keep their death animation/corpse state at initial opacity, then fade out until combat.js removes them from scene.fighters and destroys the sprite.
  • Tune that fade/despawn lifetime with FIGHTER.DEAD_DESPAWN_DELAY_MS and the final alpha with FIGHTER.DEAD_DESPAWN_ALPHA in src/constants.js.
  • combat.js resolves fighter animation keys through ensureFighterTeamAnimation() so every action can use the team-shadow baked texture generated from the original spritesheet.
  • playIfNeeded() compares against the team-shadow animation key. This avoids switching back to the original non-team-colored spritesheet when fighters move, attack, take damage, or die.
  • Frost stun remains a body tint effect in worldEffects.js. Since team identity is baked into the floor shadow pixels, there is no teamMarker tint state to update or restore.
  • The removed teamMarker display object means death handling no longer needs to hide or destroy a separate marker. HUD cleanup only owns health-bar objects because battlefield name labels are no longer created.

Update: Focused Combat Effects In Large Battles

  • combat.js exposes supplemental combat visuals only while a large battle is inside the temporary meteor camera-focus window.
  • Outside that window, large battles skip critical labels, instant-spell sprites, kill-heal sprites, and kill-growth tweens while retaining underlying damage calculations. Kill rewards are globally disabled by the elite policy.
  • World-effect meteor/frost visuals remain visible, and projectile objects remain enabled because projectiles currently participate in hit detection.

Context: Combat System

1. 모듈별 상세 역할 (src/game/combat/)

  • combat.js: 전투 AI, 피해 계산, 처치 기록 및 비활성화된 보너스 경로를 담당합니다. fighterStats.js에서 해석한 역할별 수치로 이동, 공격, 투사체 발사 등을 처리합니다.
  • combatSettings.js: 전투 속도 배율 등 런타임 전투 설정을 관리합니다.
  • arenaFinalCombatEffects.js: 최종 교전 시 슬로우 모션 등 연출 효과를 담당합니다. 수학적인 이징(easing) 함수와 물리 시간 배율 계산을 포함합니다.
  • worldEffects.js: 실제 전투에서 설정 주기마다 생존자 밀집 구역을 탐색하고 화염/냉기 소형 메테오 포격을 실행하며, 대각선 낙하 연출, 개별 탄착 판정, 냉기 동결과 감속 구역 수명주기를 처리합니다.

2. 주요 로직 구현 세부 사항

전투 AI 및 유닛 동작

  • updateFighter(): 가장 가까운 적을 찾아 이동하거나 공격하는 유닛 AI의 핵심입니다.
  • applyHit(): 일반 공격 피해량은 공격자의 melee/ranged/magic 프로필 피해량 범위에서 계산합니다. 치명타 적중은 Critical!을 표시하고, 일반 대상에는 일반 피해의 2배, elite 대상에는 최대 체력 비례 피해를 적용합니다.
  • 역할별 기본값: src/constants.jsFIGHTER_TYPE_STATS에서 체력, 이동속도, 사거리, 공격 쿨다운, 피해량, 치명타 확률, 발동 지연을 독립적으로 조절합니다. 투사체 속도는 ranged, 효과 적중 지연은 magic 프로필에 포함됩니다.
  • projectilePathHitsDefender(): 투사체가 대상을 스쳐 지나가지 않도록 궤적(Line)과 히트박스(Rectangle) 겹침 검사를 수행합니다.

처치 보너스 정책

  • elite 압축 전투에서는 COMBAT.KILL_REWARD_ENABLEDfalse이므로 처치자 체력 회복, 크기 성장, 공격속도/이동속도 보너스와 회복 이펙트가 적용되지 않습니다.
  • 킬로그, 사망 통계, 분열 판정, 승패 판정은 처치 보너스와 별개로 계속 처리됩니다.
  • applyKillReward()와 관련 상수는 향후 별도의 비압축 모드에서 명시적으로 활성화할 수 있는 경로로만 보존합니다.

월드 이펙트

  • 발동 규칙: 프리뷰가 아닌 실제 전투에서 시작 후 첫 포격은 WORLD_EFFECT.INTERVAL이 지난 뒤 발생하고, 이후 일반 포격은 WORLD_EFFECT.REPEAT_INTERVAL 간격으로 발생합니다. 각 포격은 AREA_TILES 크기의 모든 후보 구역을 타일 누적합으로 평가해, 생존 캐릭터가 가장 많이 모인 범위를 선택합니다. 같은 밀도의 후보가 여러 개일 때만 그 후보 사이에서 무작위로 고릅니다.
  • 포격 판정: 선택된 큰 범위는 경고 표시와 카메라 포커스 대상으로 사용되고, 내부에 투하되는 작은 탄착 영역만 피해, 기절, 냉기 감속을 처리합니다. 이 때문에 넓은 밀집지대를 위협하면서도 영역 전체를 즉시 동일 피해로 덮지 않습니다.
  • 서든 데스 (Sudden Death):
    • 조건: 매치 시작 후 WORLD_EFFECT.SUDDEN_DEATH.TRIGGER_MS 시간이 경과하면 서든 데스 상태에 진입합니다 (활성화 시).
    • 효과: 메테오 투하 주기가 SUDDEN_DEATH.INTERVAL_MS로 단축되며, FORCE_FROST 설정 시 빙결 효과를 가진 냉기 메테오가 집중적으로 생성됩니다.
    • 목적: 장기전을 방지하고 전장에 무작위 변수를 극대화하여 물량 중심 팀에게 리스크를 부여합니다.
  • 낙하 방향과 크기: 대상이 전장 좌측 반면(2, 3사분면)이면 화살표가 좌상단에서 우하단으로, 우측 반면(1, 4사분면)이면 좌우 반전되어 우상단에서 좌하단으로 이동합니다. 스프라이트를 45도로 기울이고 전용 시각 배율을 사용해 전역 마법 규모로 표현합니다.
  • 화염 메테오: world_Effect.png의 소형 탄착 애니메이션 3~4개가 밀집 경고 구역 내부로 순차 낙하합니다. 각 탄착은 크기에 따른 화면 흔들림과 개별 영역 고정 피해를 적용합니다. 환경 피해로 인한 사망은 킬 보상을 지급하지 않지만 사망 통계와 승패 판정에는 반영됩니다.
  • 냉기 메테오: world_Effect_2.png의 소형 탄착 애니메이션들이 같은 방식으로 낙하합니다. 개별 탄착 피해를 입고 생존한 대상은 얼음색으로 바뀐 채 설정 시간 동안 기절하며, 각 탄착점에 남는 냉각지대 안에서는 공격속도와 이동속도 감속 배율을 적용합니다.

최종교전 슬로우모션

COMBAT.FINAL_SLOW_MOTION_ENABLED가 활성화된 경우:

  • 최종교전 상태에서 공격 모션이 시작될 때 전역 time scale을 낮춥니다.
  • 진입/유지/복귀 속도 램프(Ease)를 적용합니다.
  • Arcade Physics는 timeScale 방향이 반대라 물리 이동에는 역수 배율을 적용합니다.

3. 유지보수 규칙

  • 처치 보너스: elite 압축 규칙을 유지하는 동안 src/constants.jsCOMBAT.KILL_REWARD_ENABLEDfalse로 유지합니다.
  • 공격력 조정: 일반 역할 피해량은 src/constants.jsFIGHTER_TYPE_STATS.<type>.damageMin/damageMax를 수정하고, elite 추가 공격력은 FIGHTER.ELITE.ATTACK_DAMAGE_BONUS_MULTIPLIERATTACK_DAMAGE_STACK_EXPONENT를 수정합니다.
  • 월드 이펙트 및 서든 데스 조정:
    • src/constants.jsWORLD_EFFECT.METEOR_DAMAGEWORLD_EFFECT.FROST_DAMAGE는 normal 고정 피해를, METEOR_DAMAGE_PERCENTFROST_DAMAGE_PERCENT는 elite 최대 체력 비례 피해를 조정합니다.
    • SUDDEN_DEATH.ENABLED로 서든 데스 활성화 여부를 결정하며, TRIGGER_MS(시작 시간), INTERVAL_MS(주기), FORCE_FROST(냉기 고정) 설정을 변경할 수 있습니다.
    • INTERVAL은 첫 포격까지의 대기 시간, REPEAT_INTERVAL은 이후 일반 포격 주기입니다. AREA_TILES는 밀집도를 검색하고 경고로 표시할 큰 구역이며, WARNING_DURATION_MS는 그 경고 표시 시간입니다. IMPACT_AREA_TILES, IMPACT_COUNT_MIN/IMPACT_COUNT_MAX, IMPACT_STAGGER_MS, IMPACT_VISUAL_SCALE는 내부 소형 포격의 판정 범위, 발수, 간격, 시각 크기를 조정합니다.
    • WORLD_EFFECT.FROST_STUN_DURATION/FROST_STUN_TINT로 동결 시간과 표시 색상을 조정합니다.
    • 나머지 WORLD_EFFECT.* 값으로 발동 주기, 범위, 냉각 지속시간과 감속 정도를 수정하며, 메테오 착탄 위치 포커싱은 CAMERA.METEOR_FOCUS_ENABLED에서 켜고 끕니다.
  • 특수 규칙: 캐릭터별 특수 공격 방식은 fighterManifest.jscombat 설정을 확인합니다.