feat: restructure guardian progression system with dynamic element support
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m20s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m20s
This commit is contained in:
@@ -5,7 +5,7 @@ import type { DisciplineBonuses } from './mana-utils';
|
||||
import { SPELLS_DEF, ELEMENT_OPPOSITES, INCURSION_START_DAY, MAX_DAY } from '../constants';
|
||||
import { ENCHANTMENT_EFFECTS } from '../data/enchantment-effects';
|
||||
import { getGuardianForFloor } from '../data/guardian-encounters';
|
||||
import { BASE_GUARDIANS } from '../data/guardian-data';
|
||||
import { STATIC_GUARDIANS as BASE_GUARDIANS } from '../data/guardian-data';
|
||||
|
||||
// ─── Damage Calculation Params ──────────────────────────────────────────────
|
||||
|
||||
@@ -160,7 +160,7 @@ export function calcDamage(
|
||||
const elemMasteryBonus = 1;
|
||||
|
||||
// Guardian bane bonus
|
||||
const isGuardianFloor = floorElem && Object.values(BASE_GUARDIANS).some(g => g.element === floorElem);
|
||||
const isGuardianFloor = floorElem && Object.values(BASE_GUARDIANS).some(g => g.element.includes(floorElem));
|
||||
const guardianBonus = 1;
|
||||
|
||||
// Get boon bonuses from pacts
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
// ─── Guardian Utilities ─────────────────────────────────────────────────────────
|
||||
// Helper functions for guardian element resolution and name generation.
|
||||
|
||||
import { ELEMENTS } from '../constants/elements';
|
||||
|
||||
// ─── Element Chain Resolution ──────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Resolve the full component chain for an element.
|
||||
* Walks the recipe tree from ELEMENTS to collect all base elements + the element itself.
|
||||
*
|
||||
* Examples:
|
||||
* resolveUnlockChain('fire') → ['fire']
|
||||
* resolveUnlockChain('metal') → ['fire', 'earth', 'metal']
|
||||
* resolveUnlockChain('crystal') → ['earth', 'water', 'light', 'sand', 'crystal']
|
||||
* resolveUnlockChain('stellar') → ['fire', 'light', 'stellar']
|
||||
* resolveUnlockChain('void') → ['dark', 'death', 'void']
|
||||
*/
|
||||
export function resolveUnlockChain(element: string): string[] {
|
||||
const result: string[] = [];
|
||||
const visited = new Set<string>();
|
||||
const queue: string[] = [element];
|
||||
|
||||
while (queue.length > 0) {
|
||||
const current = queue.shift()!;
|
||||
if (visited.has(current)) continue;
|
||||
visited.add(current);
|
||||
|
||||
const def = ELEMENTS[current];
|
||||
if (!def) continue;
|
||||
|
||||
if (def.recipe) {
|
||||
for (const component of def.recipe) {
|
||||
if (!visited.has(component)) {
|
||||
queue.push(component);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Base, utility, or raw element — no recipe, it's a leaf
|
||||
result.push(current);
|
||||
}
|
||||
}
|
||||
|
||||
// Always include the element itself
|
||||
if (!result.includes(element)) {
|
||||
result.push(element);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve unlock chains for multiple elements (union, deduped).
|
||||
* Used when a guardian has multiple elements.
|
||||
*
|
||||
* Example:
|
||||
* resolveMultiUnlockChain(['fire', 'earth']) → ['fire', 'earth']
|
||||
* resolveMultiUnlockChain(['metal', 'sand']) → ['fire', 'earth', 'water', 'metal', 'sand']
|
||||
*/
|
||||
export function resolveMultiUnlockChain(elements: string[]): string[] {
|
||||
const all = new Set<string>();
|
||||
for (const el of elements) {
|
||||
for (const resolved of resolveUnlockChain(el)) {
|
||||
all.add(resolved);
|
||||
}
|
||||
}
|
||||
return Array.from(all);
|
||||
}
|
||||
|
||||
// ─── Dynamic Guardian Floor Computation ────────────────────────────────────────
|
||||
|
||||
const TIER_CONFIG = [
|
||||
// [startFloor, endFloor, tiersPerFloor, description]
|
||||
{ start: 10, end: 80, spacing: 10 }, // Base + utility: floors 10-80
|
||||
{ start: 90, end: 120, spacing: 10 }, // Composite: floors 90-120
|
||||
{ start: 130, end: 160, spacing: 10 }, // Composite + Components
|
||||
{ start: 170, end: 200, spacing: 10 }, // Exotic
|
||||
{ start: 210, end: 240, spacing: 10 }, // Dual Element
|
||||
{ start: 250, end: 290, spacing: 10 }, // Dual Composite + Components
|
||||
{ start: 300, end: 340, spacing: 10 }, // Exotic + Components
|
||||
{ start: 350, end: 390, spacing: 10 }, // Exotic + Composite + Components
|
||||
{ start: 400, end: 450, spacing: 10 }, // 1 Exotic + 2 Composite + All Components
|
||||
] as const;
|
||||
|
||||
/** Get all guardian floors from a given start, dynamically computed. */
|
||||
export function computeGuardianFloors(maxFloor: number = 450): number[] {
|
||||
const floors: number[] = [];
|
||||
for (const tier of TIER_CONFIG) {
|
||||
for (let f = tier.start; f <= Math.min(tier.end, maxFloor); f += tier.spacing) {
|
||||
floors.push(f);
|
||||
}
|
||||
}
|
||||
return floors.sort((a, b) => a - b);
|
||||
}
|
||||
@@ -109,7 +109,7 @@ export function generateFloorState(floor: number): FloorState {
|
||||
armor: guardian.armor || 0,
|
||||
dodgeChance: 0,
|
||||
barrier: 0,
|
||||
element: guardian.element,
|
||||
element: guardian.element.join('+'),
|
||||
}],
|
||||
};
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ export function generateSpireFloorState(floor: number, roomIndex: number, totalR
|
||||
armor: guardian.armor || 0,
|
||||
dodgeChance: 0,
|
||||
barrier: 0,
|
||||
element: guardian.element,
|
||||
element: guardian.element.join('+'),
|
||||
}],
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user