241 lines
6.1 KiB
JavaScript
241 lines
6.1 KiB
JavaScript
// 1. ARENA 도메인
|
|
const GRID_SIZE = 50;
|
|
const TILE_SIZE = 64;
|
|
const ARENA_SIZE = GRID_SIZE * TILE_SIZE;
|
|
|
|
export const ARENA = {
|
|
GRID_SIZE,
|
|
TILE_SIZE,
|
|
SIZE: ARENA_SIZE,
|
|
};
|
|
|
|
// 2. FIGHTER 도메인
|
|
export const FIGHTER = {
|
|
SCALE: 3,
|
|
DEPTH: 2,
|
|
DEAD_DEPTH: 1,
|
|
DEAD_ALPHA: 0.42,
|
|
FRAME_WIDTH: 100,
|
|
FRAME_HEIGHT: 100,
|
|
HITBOX_WIDTH: 22,
|
|
HITBOX_HEIGHT: 20,
|
|
HITBOX_OFFSET_X: 39,
|
|
HITBOX_OFFSET_Y: 40,
|
|
NICKNAME_LENGTH: 18,
|
|
// 캐릭터 액션별 애니메이션 프레임 속도와 반복 횟수
|
|
ANIMATION_OPTIONS: {
|
|
attack: { frameRate: 15, repeat: 0 },
|
|
attack02: { frameRate: 15, repeat: 0 },
|
|
attack03: { frameRate: 15, repeat: 0 },
|
|
block: { frameRate: 13, repeat: 0 },
|
|
death: { frameRate: 11, repeat: 0 },
|
|
heal: { frameRate: 13, repeat: 0 },
|
|
hurt: { frameRate: 13, repeat: 0 },
|
|
idle: { frameRate: 7, repeat: -1 },
|
|
walk: { frameRate: 10, repeat: -1 },
|
|
walk02: { frameRate: 10, repeat: -1 },
|
|
},
|
|
// 역할별 기본 스탯
|
|
TYPE_STATS: {
|
|
melee: {
|
|
maxHp: 100,
|
|
moveSpeed: 148 * 1.1,
|
|
attackRange: 84,
|
|
attackCooldown: 840,
|
|
damageMin: 14,
|
|
damageMax: 24,
|
|
criticalChance: 0.2,
|
|
windupDelay: 260,
|
|
},
|
|
ranged: {
|
|
maxHp: 80,
|
|
moveSpeed: 148,
|
|
attackRange: TILE_SIZE * 5,
|
|
attackCooldown: 840 * 1.1,
|
|
damageMin: 14 * 1.2,
|
|
damageMax: 24 * 1.2,
|
|
criticalChance: 0,
|
|
windupDelay: 360,
|
|
projectileSpeed: 420,
|
|
},
|
|
magic: {
|
|
maxHp: 80,
|
|
moveSpeed: 148,
|
|
attackRange: TILE_SIZE * 5,
|
|
attackCooldown: 840 * 1.1,
|
|
damageMin: 14 * 1.5,
|
|
damageMax: 24 * 1.5,
|
|
criticalChance: 0,
|
|
windupDelay: 340,
|
|
effectHitDelay: 160,
|
|
},
|
|
},
|
|
};
|
|
|
|
// 3. SPAWN 도메인
|
|
export const SPAWN = {
|
|
DEFAULT_TEAM_SIZE: 5,
|
|
DEFAULT_PLACEMENT: "random",
|
|
PLACEMENTS: {
|
|
RANDOM: "random",
|
|
STARTING_ZONES: "starting-zones",
|
|
},
|
|
STARTING_ZONE_RADIUS: 2,
|
|
STARTING_ZONE_FILL_ALPHA: 0.07,
|
|
STARTING_ZONE_BORDER_ALPHA: 0.14,
|
|
STARTING_ZONE_VISIBLE_DURATION_MS: 5000,
|
|
PRESENTATION_TEAM_COUNT: 10,
|
|
PRESENTATION_TEAM_SIZE: 5,
|
|
MAX_TEAM_SIZE: 100,
|
|
};
|
|
|
|
// 4. COMBAT 도메인
|
|
export const COMBAT = {
|
|
KILL_HEALTH_RECOVERY_RATIO: 0.3,
|
|
KILL_HEAL_EFFECT_FRAMES: 4,
|
|
KILL_HEAL_EFFECT_FRAME_RATE: 12,
|
|
KILL_GROWTH_MULTIPLIER: 1.25,
|
|
KILL_GROWTH_MAX_MULTIPLIER: 5,
|
|
KILL_GROWTH_TWEEN_DURATION: 180,
|
|
// 최종교전 슬로우모션 설정
|
|
FINAL_SLOW_MOTION_ENABLED: false,
|
|
FINAL_SLOW_MOTION_ENTER_DURATION: 14000,
|
|
FINAL_SLOW_MOTION_HOLD_DURATION: 14000,
|
|
FINAL_SLOW_MOTION_EXIT_DURATION: 14000,
|
|
FINAL_SLOW_MOTION_SCALE: 0.28,
|
|
};
|
|
|
|
// 5. PROJECTILE 도메인
|
|
export const PROJECTILE = {
|
|
LIFETIME: 1800,
|
|
BODY_OFFSET: 4,
|
|
HIT_PADDING: 20,
|
|
HIT_RADIUS: 12,
|
|
SPAWN_DISTANCE: 1,
|
|
};
|
|
|
|
// 6. WORLD_EFFECT 도메인
|
|
export const WORLD_EFFECT = {
|
|
INTERVAL: 4000,
|
|
AREA_TILES: 5,
|
|
FRAMES: 7,
|
|
FRAME_RATE: 14,
|
|
FALL_DURATION: 920,
|
|
FALL_TRAVEL_TILES: 8,
|
|
VISUAL_SCALE: 12,
|
|
METEOR_DAMAGE: 80,
|
|
FROST_DAMAGE: 40,
|
|
FROST_STUN_DURATION: 2000,
|
|
FROST_STUN_TINT: 0x82e9ff,
|
|
FROST_DURATION: 20000,
|
|
FROST_SPEED_MULTIPLIER: 0.55,
|
|
};
|
|
|
|
// 7. CAMERA 도메인
|
|
export const CAMERA = {
|
|
MIN_ZOOM: 1,
|
|
MAX_ZOOM: 3,
|
|
ZOOM_STEP: 0.1,
|
|
// 자동 관전 진입 전 화염/냉기 메테오 낙하 위치를 임시로 확대 추적합니다.
|
|
METEOR_FOCUS_ENABLED: true,
|
|
METEOR_FOCUS_ZOOM: 2,
|
|
SPECTATOR_LERP: 0.1,
|
|
// 메테오 착탄 후 카메라를 해당 위치에 유지하는 시간(ms)입니다.
|
|
METEOR_FOCUS_HOLD_DURATION: 1200,
|
|
SPECTATOR_FINAL_FIGHTER_THRESHOLD: 5,
|
|
SPECTATOR_FINAL_FIGHT_ZOOM: 3,
|
|
SPECTATOR_FINAL_TEAM_COUNT: 2,
|
|
SPECTATOR_FINAL_TEAM_TOTAL_THRESHOLD: 8,
|
|
SPECTATOR_RANDOM_FOCUS_INTERVAL: 10000,
|
|
SPECTATOR_LATE_FIGHTER_THRESHOLD: 30,
|
|
SPECTATOR_LATE_FIGHT_ZOOM: 2,
|
|
SELECTED_FIGHTER_ZOOM: 2,
|
|
};
|
|
|
|
// 8. UI 도메인
|
|
export const UI = {
|
|
MINIMAP_ALPHA: 0.8,
|
|
MINIMAP_MARGIN: Math.round(ARENA_SIZE * 0.016),
|
|
MINIMAP_VIEWPORT_SIZE: Math.round(ARENA_SIZE * 0.22),
|
|
MINIMAP_VIEW_FRAME_STROKE: 10,
|
|
SELECTED_FIGHTER_OUTLINE_GAP: 1,
|
|
SELECTED_FIGHTER_OUTLINE_WIDTH: 1,
|
|
SELECTED_FIGHTER_OUTLINE_RED: 255,
|
|
SELECTED_FIGHTER_OUTLINE_GREEN: 228,
|
|
SELECTED_FIGHTER_OUTLINE_BLUE: 64,
|
|
SELECTED_FIGHTER_OUTLINE_ALPHA: 0.65,
|
|
};
|
|
|
|
// 9. TEAM 도메인
|
|
const TEAM_COLORS = [
|
|
"#da6a48",
|
|
"#5fb4d9",
|
|
"#9bd15a",
|
|
"#d6a94a",
|
|
"#d477b8",
|
|
"#7f90e8",
|
|
"#63c5a6",
|
|
"#d98755",
|
|
];
|
|
|
|
const TEAM_COLOR_GOLDEN_ANGLE = 137.508;
|
|
const TEAM_COLOR_HUE_OFFSET = 12;
|
|
const TEAM_COLOR_SATURATIONS = [72, 62, 78, 68];
|
|
const TEAM_COLOR_LIGHTNESSES = [57, 63, 51, 69];
|
|
|
|
function getTeamColor(index, totalTeams = TEAM_COLORS.length) {
|
|
const safeIndex = Math.max(0, Math.floor(Number(index) || 0));
|
|
const safeTeamCount = Math.max(1, Math.floor(Number(totalTeams) || 1));
|
|
|
|
if (safeTeamCount <= TEAM_COLORS.length) {
|
|
return TEAM_COLORS[safeIndex % TEAM_COLORS.length];
|
|
}
|
|
|
|
const hue =
|
|
(TEAM_COLOR_HUE_OFFSET + safeIndex * TEAM_COLOR_GOLDEN_ANGLE) % 360;
|
|
const saturation =
|
|
TEAM_COLOR_SATURATIONS[safeIndex % TEAM_COLOR_SATURATIONS.length];
|
|
const lightness =
|
|
TEAM_COLOR_LIGHTNESSES[
|
|
Math.floor(safeIndex / TEAM_COLOR_SATURATIONS.length) %
|
|
TEAM_COLOR_LIGHTNESSES.length
|
|
];
|
|
|
|
return hslToHex(hue, saturation, lightness);
|
|
}
|
|
|
|
function hslToHex(hue, saturation, lightness) {
|
|
const normalizedSaturation = saturation / 100;
|
|
const normalizedLightness = lightness / 100;
|
|
const chroma =
|
|
(1 - Math.abs(2 * normalizedLightness - 1)) * normalizedSaturation;
|
|
const huePrime = hue / 60;
|
|
const x = chroma * (1 - Math.abs((huePrime % 2) - 1));
|
|
const match = normalizedLightness - chroma / 2;
|
|
const [red, green, blue] =
|
|
huePrime < 1
|
|
? [chroma, x, 0]
|
|
: huePrime < 2
|
|
? [x, chroma, 0]
|
|
: huePrime < 3
|
|
? [0, chroma, x]
|
|
: huePrime < 4
|
|
? [0, x, chroma]
|
|
: huePrime < 5
|
|
? [x, 0, chroma]
|
|
: [chroma, 0, x];
|
|
|
|
return `#${[red, green, blue]
|
|
.map((channel) =>
|
|
Math.round((channel + match) * 255)
|
|
.toString(16)
|
|
.padStart(2, "0"),
|
|
)
|
|
.join("")}`;
|
|
}
|
|
|
|
export const TEAM = {
|
|
COLORS: TEAM_COLORS,
|
|
getColor: getTeamColor,
|
|
};
|