refactor(slay-the-spire-like): rename depletion to consumedUses
Rename `depletion` to `consumedUses` in `GameItemMeta` to better reflect its purpose. Update combat effect logic and tests to use the new field name. Also apply consistent formatting and import organization to combat effect modules.
This commit is contained in:
parent
08c6a67d16
commit
9bed2ca13e
|
|
@ -1,78 +1,96 @@
|
||||||
import {CombatEntity, CombatGameContext, CombatState, EffectTable, PlayerEntity} from "./types";
|
import {
|
||||||
|
CombatEntity,
|
||||||
|
CombatGameContext,
|
||||||
|
CombatState,
|
||||||
|
EffectTable,
|
||||||
|
PlayerEntity,
|
||||||
|
} from "./types";
|
||||||
import {
|
import {
|
||||||
CardData,
|
CardData,
|
||||||
CardEffectTarget,
|
CardEffectTarget,
|
||||||
CardTargetType,
|
CardTargetType,
|
||||||
EffectData,
|
EffectData,
|
||||||
EffectTarget
|
EffectTarget,
|
||||||
} from "@/samples/slay-the-spire-like/system/types";
|
} from "@/samples/slay-the-spire-like/system/types";
|
||||||
import {GameItemMeta} from "@/samples/slay-the-spire-like/system/progress/types";
|
import { GameItemMeta } from "@/samples/slay-the-spire-like/system/progress/types";
|
||||||
import {GridInventory} from "@/samples/slay-the-spire-like/system/grid-inventory/types";
|
import { GridInventory } from "@/samples/slay-the-spire-like/system/grid-inventory/types";
|
||||||
|
|
||||||
export function addEffect(effects: EffectTable, effect: EffectData, stacks: number){
|
export function addEffect(
|
||||||
|
effects: EffectTable,
|
||||||
|
effect: EffectData,
|
||||||
|
stacks: number,
|
||||||
|
) {
|
||||||
let current = effects[effect.id];
|
let current = effects[effect.id];
|
||||||
|
|
||||||
if(!current) current = {data: effect, stacks};
|
if (!current) current = { data: effect, stacks };
|
||||||
else current.stacks += stacks;
|
else current.stacks += stacks;
|
||||||
|
|
||||||
if(current.stacks === 0 && effects[effect.id])
|
if (current.stacks === 0 && effects[effect.id]) delete effects[effect.id];
|
||||||
delete effects[effect.id];
|
else if (current.stacks !== 0 && !effects[effect.id])
|
||||||
else if(current.stacks !== 0 && !effects[effect.id])
|
|
||||||
effects[effect.id] = current;
|
effects[effect.id] = current;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addEntityEffect(entity: CombatEntity, effect: EffectData, stacks: number){
|
export function addEntityEffect(
|
||||||
|
entity: CombatEntity,
|
||||||
|
effect: EffectData,
|
||||||
|
stacks: number,
|
||||||
|
) {
|
||||||
addEffect(entity.effects, effect, stacks);
|
addEffect(entity.effects, effect, stacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addItemEffect(entity: PlayerEntity, itemKey: string, effect: EffectData, stacks: number){
|
export function addItemEffect(
|
||||||
|
entity: PlayerEntity,
|
||||||
|
itemKey: string,
|
||||||
|
effect: EffectData,
|
||||||
|
stacks: number,
|
||||||
|
) {
|
||||||
entity.itemEffects[itemKey] = entity.itemEffects[itemKey] || {};
|
entity.itemEffects[itemKey] = entity.itemEffects[itemKey] || {};
|
||||||
addEffect(entity.itemEffects[itemKey], effect, stacks);
|
addEffect(entity.itemEffects[itemKey], effect, stacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onEntityEffectUpkeep(entity: CombatEntity){
|
export function onEntityEffectUpkeep(entity: CombatEntity) {
|
||||||
for(const effect of Object.values(entity.effects)){
|
for (const effect of Object.values(entity.effects)) {
|
||||||
const lifecycle = effect.data.lifecycle;
|
const lifecycle = effect.data.lifecycle;
|
||||||
if(lifecycle === 'temporary')
|
if (lifecycle === "temporary")
|
||||||
addEntityEffect(entity, effect.data, -effect.stacks);
|
addEntityEffect(entity, effect.data, -effect.stacks);
|
||||||
else if(lifecycle === 'lingering')
|
else if (lifecycle === "lingering")
|
||||||
addEntityEffect(entity, effect.data, effect.stacks >= 0 ? -1 : 1);
|
addEntityEffect(entity, effect.data, effect.stacks >= 0 ? -1 : 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onEntityPostureDamage(entity: CombatEntity, damage: number){
|
export function onEntityPostureDamage(entity: CombatEntity, damage: number) {
|
||||||
for(const effect of Object.values(entity.effects)){
|
for (const effect of Object.values(entity.effects)) {
|
||||||
const lifecycle = effect.data.lifecycle;
|
const lifecycle = effect.data.lifecycle;
|
||||||
if(lifecycle === 'posture')
|
if (lifecycle === "posture")
|
||||||
addEntityEffect(entity, effect.data, -Math.min(damage, effect.stacks));
|
addEntityEffect(entity, effect.data, -Math.min(damage, effect.stacks));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onPlayerItemEffectUpkeep(entity: PlayerEntity){
|
export function onPlayerItemEffectUpkeep(entity: PlayerEntity) {
|
||||||
for(const [itemKey, itemEffects] of Object.entries(entity.itemEffects)){
|
for (const [itemKey, itemEffects] of Object.entries(entity.itemEffects)) {
|
||||||
for(const effect of Object.values(itemEffects)){
|
for (const effect of Object.values(itemEffects)) {
|
||||||
const lifecycle = effect.data.lifecycle;
|
const lifecycle = effect.data.lifecycle;
|
||||||
if(lifecycle === 'itemTemporary')
|
if (lifecycle === "itemTemporary")
|
||||||
addItemEffect(entity, itemKey, effect.data, -effect.stacks);
|
addItemEffect(entity, itemKey, effect.data, -effect.stacks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onItemPlay(entity: PlayerEntity, itemKey: string){
|
export function onItemPlay(entity: PlayerEntity, itemKey: string) {
|
||||||
const effects = entity.itemEffects[itemKey];
|
const effects = entity.itemEffects[itemKey];
|
||||||
if(!effects)return;
|
if (!effects) return;
|
||||||
for(const effect of Object.values(effects)){
|
for (const effect of Object.values(effects)) {
|
||||||
if(effect.data.lifecycle === 'itemUntilPlay'){
|
if (effect.data.lifecycle === "itemUntilPlay") {
|
||||||
addItemEffect(entity, itemKey, effect.data, -effect.stacks);
|
addItemEffect(entity, itemKey, effect.data, -effect.stacks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onItemDiscard(entity: PlayerEntity, itemKey: string){
|
export function onItemDiscard(entity: PlayerEntity, itemKey: string) {
|
||||||
const effects = entity.itemEffects[itemKey];
|
const effects = entity.itemEffects[itemKey];
|
||||||
if(!effects)return;
|
if (!effects) return;
|
||||||
for(const effect of Object.values(effects)){
|
for (const effect of Object.values(effects)) {
|
||||||
if(effect.data.lifecycle === 'itemUntilDiscard'){
|
if (effect.data.lifecycle === "itemUntilDiscard") {
|
||||||
addItemEffect(entity, itemKey, effect.data, -effect.stacks);
|
addItemEffect(entity, itemKey, effect.data, -effect.stacks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -86,49 +104,67 @@ export function* getAliveEnemies(state: CombatState) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* getEffectTargets(target: CardEffectTarget | EffectTarget, game: CombatGameContext, targetId?: string){
|
export function* getEffectTargets(
|
||||||
if(target === 'all' || target === 'team'){
|
target: CardEffectTarget | EffectTarget,
|
||||||
for(const enemy of getAliveEnemies(game.value)){
|
game: CombatGameContext,
|
||||||
|
targetId?: string,
|
||||||
|
) {
|
||||||
|
if (target === "all" || target === "team") {
|
||||||
|
for (const enemy of getAliveEnemies(game.value)) {
|
||||||
yield enemy;
|
yield enemy;
|
||||||
}
|
}
|
||||||
} else if(target === 'self') {
|
} else if (target === "self") {
|
||||||
yield game.value.player;
|
yield game.value.player;
|
||||||
} else if(target === 'target'){
|
} else if (target === "target") {
|
||||||
if(!targetId) return;
|
if (!targetId) return;
|
||||||
const entity = getCombatEntity(game.value, targetId);
|
const entity = getCombatEntity(game.value, targetId);
|
||||||
if(entity) yield entity;
|
if (entity) yield entity;
|
||||||
} else if(target === 'random'){
|
} else if (target === "random") {
|
||||||
const aliveEnemies = [...getAliveEnemies(game.value)];
|
const aliveEnemies = [...getAliveEnemies(game.value)];
|
||||||
if(aliveEnemies.length === 0) return;
|
if (aliveEnemies.length === 0) return;
|
||||||
const index = game.rng.nextInt(aliveEnemies.length);
|
const index = game.rng.nextInt(aliveEnemies.length);
|
||||||
yield aliveEnemies[index];
|
yield aliveEnemies[index];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCombatEntity(state: CombatState, entityKey: string){
|
export function getCombatEntity(state: CombatState, entityKey: string) {
|
||||||
return entityKey === 'player' ? state.player : state.enemies.find(e => e.id === entityKey);
|
return entityKey === "player"
|
||||||
|
? state.player
|
||||||
|
: state.enemies.find((e) => e.id === entityKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function canPlayCard(player: PlayerEntity, costType: CardData['costType'], costCount: number, itemId: string, inventory: GridInventory<GameItemMeta>): boolean {
|
export function canPlayCard(
|
||||||
if (costType === 'energy') {
|
player: PlayerEntity,
|
||||||
|
costType: CardData["costType"],
|
||||||
|
costCount: number,
|
||||||
|
itemId: string,
|
||||||
|
inventory: GridInventory<GameItemMeta>,
|
||||||
|
): boolean {
|
||||||
|
if (costType === "energy") {
|
||||||
return player.energy >= costCount;
|
return player.energy >= costCount;
|
||||||
}
|
}
|
||||||
if (costType === 'uses') {
|
if (costType === "uses") {
|
||||||
const item = inventory.items.get(itemId);
|
const item = inventory.items.get(itemId);
|
||||||
if (!item || !item.meta) return false;
|
if (!item || !item.meta) return false;
|
||||||
const depletion = item.meta.depletion ?? 0;
|
const depletion = item.meta.consumedUses ?? 0;
|
||||||
return depletion < costCount;
|
return depletion < costCount;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function payCardCost(player: PlayerEntity, costType: CardData['costType'], costCount: number, itemId: string, inventory: GridInventory<GameItemMeta>): void {
|
export function payCardCost(
|
||||||
if (costType === 'energy') {
|
player: PlayerEntity,
|
||||||
|
costType: CardData["costType"],
|
||||||
|
costCount: number,
|
||||||
|
itemId: string,
|
||||||
|
inventory: GridInventory<GameItemMeta>,
|
||||||
|
): void {
|
||||||
|
if (costType === "energy") {
|
||||||
player.energy -= costCount;
|
player.energy -= costCount;
|
||||||
} else if (costType === 'uses') {
|
} else if (costType === "uses") {
|
||||||
const item = inventory.items.get(itemId);
|
const item = inventory.items.get(itemId);
|
||||||
if (item && item.meta) {
|
if (item && item.meta) {
|
||||||
item.meta.depletion = (item.meta.depletion ?? 0) + costCount;
|
item.meta.consumedUses = (item.meta.consumedUses ?? 0) + costCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import type { PointCrawlMap } from '../map/types';
|
import type { PointCrawlMap } from "../map/types";
|
||||||
import type { GridInventory, InventoryItem } from '../grid-inventory/types';
|
import type { GridInventory, InventoryItem } from "../grid-inventory/types";
|
||||||
import type { ParsedShape } from '../utils/parse-shape';
|
import type { ParsedShape } from "../utils/parse-shape";
|
||||||
import {ItemData} from "@/samples/slay-the-spire-like/system/types";
|
import { ItemData } from "@/samples/slay-the-spire-like/system/types";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Result of an encounter (combat, event, etc.).
|
* Result of an encounter (combat, event, etc.).
|
||||||
|
|
@ -39,7 +39,9 @@ export interface GameItemMeta {
|
||||||
/** Parsed shape for grid placement */
|
/** Parsed shape for grid placement */
|
||||||
shape: ParsedShape;
|
shape: ParsedShape;
|
||||||
/** Consumed uses, if card cost type is uses**/
|
/** Consumed uses, if card cost type is uses**/
|
||||||
depletion?: number;
|
consumedUses?: number;
|
||||||
|
/** Effects applied to the item */
|
||||||
|
effects?: Record<string, number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -84,4 +86,6 @@ export interface RunState {
|
||||||
/**
|
/**
|
||||||
* Result of a mutation operation on the run state.
|
* Result of a mutation operation on the run state.
|
||||||
*/
|
*/
|
||||||
export type RunMutationResult = { success: true } | { success: false; reason: string };
|
export type RunMutationResult =
|
||||||
|
| { success: true }
|
||||||
|
| { success: false; reason: string };
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ function createItem(
|
||||||
description: "",
|
description: "",
|
||||||
},
|
},
|
||||||
shape: { id: "1x1", cells: [{ x: 0, y: 0 }] } as unknown as ParsedShape,
|
shape: { id: "1x1", cells: [{ x: 0, y: 0 }] } as unknown as ParsedShape,
|
||||||
depletion: costType === "uses" ? depletion : undefined,
|
consumedUses: costType === "uses" ? depletion : undefined,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -591,7 +591,7 @@ describe("combat/effects", () => {
|
||||||
|
|
||||||
payCardCost(player, "uses", 3, "potion-1", inventory);
|
payCardCost(player, "uses", 3, "potion-1", inventory);
|
||||||
|
|
||||||
expect(item.meta?.depletion).toBe(4);
|
expect(item.meta?.consumedUses).toBe(4);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should do nothing for none cost card", () => {
|
it("should do nothing for none cost card", () => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue