Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 | 4x 4x 2x 2x 1x 1x 1x 1x 1x 1x 1x 1x 39x 39x 39x 39x 39x 1x 1x 3x 1x 2x 4x 1x 3x 14x 1x 13x 13x 13x 3x 1x 2x 2x 2x 16x 2x 1x 1x 1x 8x 8x 3x 3x 3x 1x 1x | /**
* Body Part Damage Integration for Combat System
*
* **Korean**: 신체부위 피해 통합
*
* Extends the damage calculation system to apply damage to specific body parts
* based on vital point hits and attack locations. Integrates with the existing
* VitalPointSystem and DamageCalculator.
*
* @module systems/bodypart/BodyPartDamageIntegration
* @category Body Part System
* @korean 신체부위피해통합
*/
import { BodyRegion } from "@/types";
import { PlayerState } from "../player";
import { VitalPoint } from "../vitalpoint/types";
import { bodyPartHealthSystem } from "./BodyPartHealthSystem";
import { BodyPart } from "./types";
/**
* Map vital point to its corresponding body region.
*
* **Korean**: 급소에서 신체 영역 매핑
*
* Analyzes a vital point's location and category to determine which
* BodyRegion it belongs to, enabling proper damage distribution.
*
* @param vitalPoint - Vital point that was struck
* @returns Corresponding body region
*
* @public
*/
export function getBodyRegionFromVitalPoint(
vitalPoint: VitalPoint
): BodyRegion {
const pointId = vitalPoint.id.toLowerCase();
// Head region vital points
if (
pointId.includes("head_") ||
pointId.includes("temple") ||
pointId.includes("jaw") ||
pointId.includes("crown") ||
pointId.includes("forehead") ||
pointId.includes("eye") ||
pointId.includes("nose") ||
pointId.includes("ear") ||
pointId.includes("skull")
) {
return BodyRegion.HEAD;
}
// Neck region vital points
if (
pointId.includes("neck") ||
pointId.includes("throat") ||
pointId.includes("carotid") ||
pointId.includes("cervical")
) {
return BodyRegion.NECK;
}
// Torso region vital points
Iif (
pointId.includes("torso_") ||
pointId.includes("chest") ||
pointId.includes("solar_plexus") ||
pointId.includes("ribs") ||
pointId.includes("sternum") ||
pointId.includes("heart") ||
pointId.includes("lung") ||
pointId.includes("abdomen")
) {
return BodyRegion.TORSO;
}
// Core region vital points
Iif (
pointId.includes("core") ||
pointId.includes("dantian") ||
pointId.includes("kidney") ||
pointId.includes("liver") ||
pointId.includes("spleen") ||
pointId.includes("bladder")
) {
return BodyRegion.CORE;
}
// Left arm vital points
Iif (
pointId.includes("arm_left") ||
pointId.includes("left_shoulder") ||
pointId.includes("left_elbow") ||
pointId.includes("left_wrist") ||
pointId.includes("left_hand")
) {
return BodyRegion.LEFT_ARM;
}
// Right arm vital points
Iif (
pointId.includes("arm_right") ||
pointId.includes("right_shoulder") ||
pointId.includes("right_elbow") ||
pointId.includes("right_wrist") ||
pointId.includes("right_hand")
) {
return BodyRegion.RIGHT_ARM;
}
// Left leg vital points
Iif (
pointId.includes("leg_left") ||
pointId.includes("left_knee") ||
pointId.includes("left_ankle") ||
pointId.includes("left_foot") ||
pointId.includes("left_thigh")
) {
return BodyRegion.LEFT_LEG;
}
// Right leg vital points
Iif (
pointId.includes("leg_right") ||
pointId.includes("right_knee") ||
pointId.includes("right_ankle") ||
pointId.includes("right_foot") ||
pointId.includes("right_thigh")
) {
return BodyRegion.RIGHT_LEG;
}
// Default to torso for unmatched points
return BodyRegion.TORSO;
}
/**
* Apply damage to player's body parts based on hit location.
*
* **Korean**: 타격 위치 기반 신체부위 피해 적용
*
* Takes total damage and distributes it across body parts according to
* the hit location. Updates both aggregate health and body part health.
*
* For combat compatibility, the aggregate health is directly reduced by
* the damage amount (traditional behavior), while body part health is
* updated proportionally for visualization purposes.
*
* @param player - Player receiving damage
* @param totalDamage - Total damage amount
* @param bodyRegion - Body region that was hit
* @returns Updated player state with body part damage applied
*
* @public
*/
export function applyDamageToBodyParts(
player: PlayerState,
totalDamage: number,
bodyRegion: BodyRegion
): PlayerState {
// If body part health not initialized, initialize it
const bodyPartHealth =
player.bodyPartHealth ?? bodyPartHealthSystem.createDefaultBodyPartHealth();
const bodyPartMaxHealth =
player.bodyPartMaxHealth ?? bodyPartHealthSystem.createDefaultMaxHealth();
// Apply distributed damage to body parts for visualization
const updatedBodyPartHealth = bodyPartHealthSystem.applyDistributedDamage(
bodyPartHealth,
bodyRegion,
totalDamage,
bodyPartMaxHealth
);
// Calculate new aggregate health directly: traditional damage reduction
// This ensures combat damage calculations remain consistent
const newAggregateHealth = Math.max(0, player.health - totalDamage);
// Update player state with new body part health and aggregate health
return {
...player,
bodyPartHealth: updatedBodyPartHealth,
bodyPartMaxHealth,
health: newAggregateHealth,
};
}
/**
* Apply damage from a vital point hit to appropriate body parts.
*
* **Korean**: 급소 타격 신체부위 피해 적용
*
* Specialized damage application for vital point strikes. Maps the vital
* point to its body region and applies damage with appropriate distribution.
*
* @param player - Player receiving damage
* @param totalDamage - Total damage amount
* @param vitalPoint - Vital point that was struck
* @returns Updated player state with vital point damage applied
*
* @public
*/
export function applyVitalPointDamageToBodyParts(
player: PlayerState,
totalDamage: number,
vitalPoint: VitalPoint
): PlayerState {
const bodyRegion = getBodyRegionFromVitalPoint(vitalPoint);
return applyDamageToBodyParts(player, totalDamage, bodyRegion);
}
/**
* Get combat capability modifiers from body part health.
*
* **Korean**: 신체부위 전투 능력 수정치 조회
*
* Calculates how body part damage affects combat capabilities.
* Returns modifiers that should be applied to:
* - Consciousness (awareness, reaction time)
* - Stamina regeneration
* - Attack damage output
* - Movement speed
* - Balance and stability
* - Technique accuracy
*
* @param player - Player state to analyze
* @returns Combat capability effect multipliers (0.0-1.0)
*
* @example
* ```typescript
* const effects = getBodyPartCombatEffects(player);
* const actualDamage = baseDamage * effects.attackDamageModifier;
* const actualSpeed = baseSpeed * effects.movementSpeedModifier;
* ```
*
* @public
*/
export function getBodyPartCombatEffects(player: PlayerState) {
// If no body part health, return no effects
if (!player.bodyPartHealth) {
return {
consciousnessModifier: 1.0,
staminaRegenModifier: 1.0,
attackDamageModifier: 1.0,
movementSpeedModifier: 1.0,
balanceModifier: 1.0,
techniqueAccuracyModifier: 1.0,
};
}
return bodyPartHealthSystem.calculateBodyPartEffects(
player.bodyPartHealth,
player.bodyPartMaxHealth
);
}
/**
* Check if player is incapacitated by body part damage.
*
* **Korean**: 신체부위 무력화 확인
*
* Determines if the player can continue fighting based on body part health.
* Player is incapacitated if:
* - Head is at 0 HP (unconscious)
* - Both legs are at 0 HP (cannot stand)
* - Aggregate health is below 10%
*
* @param player - Player state to check
* @returns Whether player is incapacitated
*
* @public
*/
export function isPlayerIncapacitatedByBodyDamage(
player: PlayerState
): boolean {
if (!player.bodyPartHealth) {
return false; // No body part system = use aggregate health only
}
return bodyPartHealthSystem.isIncapacitated(player.bodyPartHealth);
}
/**
* Initialize body part health for a player.
*
* **Korean**: 플레이어 신체부위 체력 초기화
*
* Sets up body part health and max health for a player if not already present.
* Uses the provided max health or defaults to 100 per part.
*
* @param player - Player state to initialize
* @param maxHealthPerPart - Optional custom max health per part
* @returns Player state with body part health initialized
*
* @public
*/
export function initializeBodyPartHealthForPlayer(
player: PlayerState,
maxHealthPerPart: number = 100
): PlayerState {
// Skip if already initialized
if (player.bodyPartHealth && player.bodyPartMaxHealth) {
return player;
}
const bodyPartHealth =
bodyPartHealthSystem.createDefaultBodyPartHealth(maxHealthPerPart);
const bodyPartMaxHealth =
bodyPartHealthSystem.createDefaultMaxHealth(maxHealthPerPart);
return {
...player,
bodyPartHealth,
bodyPartMaxHealth,
health: bodyPartHealthSystem.calculateAggregateHealth(bodyPartHealth),
};
}
/**
* Heal body parts proportionally based on total healing amount.
*
* **Korean**: 신체부위 비례 치유
*
* Distributes healing across all damaged body parts proportionally to
* their damage level. Most damaged parts receive more healing.
*
* @param player - Player state to heal
* @param totalHealAmount - Total healing to distribute
* @returns Updated player state with healing applied
*
* @public
*/
export function healBodyPartsProportionally(
player: PlayerState,
totalHealAmount: number
): PlayerState {
if (!player.bodyPartHealth || !player.bodyPartMaxHealth) {
return player; // No body part system active
}
const bodyPartHealth = player.bodyPartHealth;
const bodyPartMaxHealth = player.bodyPartMaxHealth;
// Calculate total missing health across all parts
const missingHealth = (Object.keys(bodyPartHealth) as BodyPart[]).reduce(
(sum, part) => {
return sum + (bodyPartMaxHealth[part] - bodyPartHealth[part]);
},
0
);
// If no damage, return unchanged
if (missingHealth <= 0) {
return player;
}
// Heal each part proportionally to its damage
let updatedHealth = { ...bodyPartHealth };
(Object.keys(bodyPartHealth) as BodyPart[]).forEach((part) => {
const partMissingHealth = bodyPartMaxHealth[part] - bodyPartHealth[part];
if (partMissingHealth > 0) {
const proportion = partMissingHealth / missingHealth;
const healForThisPart = totalHealAmount * proportion;
updatedHealth = bodyPartHealthSystem.healBodyPart(
updatedHealth,
part,
healForThisPart,
bodyPartMaxHealth
);
}
});
// Calculate new aggregate health
const newAggregateHealth =
bodyPartHealthSystem.calculateAggregateHealth(updatedHealth);
return {
...player,
bodyPartHealth: updatedHealth,
health: newAggregateHealth,
};
}
|