feat: add shop engine with 3-tab support and artifact upgrades
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"id": "taoist_license",
|
||||
"name": "道碟",
|
||||
"tab": "item",
|
||||
"cost": 500,
|
||||
"unlockConditions": [
|
||||
{ "type": "age", "op": ">=", "value": 10 },
|
||||
{ "type": "or", "conditions": [
|
||||
{ "type": "identity", "value": "taoist_orphan" },
|
||||
{ "type": "worldFlag", "flag": "met_taoist_priest", "value": true }
|
||||
]}
|
||||
],
|
||||
"effects": [
|
||||
{ "type": "unlockCareer", "careerId": "taoist_priest" },
|
||||
{ "type": "setFlag", "flag": "has_taoist_license", "value": true }
|
||||
],
|
||||
"resetOnRebirth": true
|
||||
},
|
||||
{
|
||||
"id": "power_pill",
|
||||
"name": "大力丸",
|
||||
"tab": "item",
|
||||
"cost": 50,
|
||||
"unlockConditions": [],
|
||||
"effects": [
|
||||
{ "type": "addStat", "stat": "body", "value": 5 }
|
||||
],
|
||||
"resetOnRebirth": true
|
||||
}
|
||||
],
|
||||
"buffs": [
|
||||
{
|
||||
"id": "clever_brush",
|
||||
"name": "妙笔生花",
|
||||
"tab": "buff",
|
||||
"cost": 200,
|
||||
"unlockConditions": [],
|
||||
"effects": [
|
||||
{ "type": "addStat", "stat": "wisdom", "value": 10 }
|
||||
],
|
||||
"resetOnRebirth": true
|
||||
}
|
||||
],
|
||||
"artifacts": [
|
||||
{
|
||||
"id": "immortal_sword",
|
||||
"name": "仙剑",
|
||||
"tab": "artifact",
|
||||
"baseCost": 1000,
|
||||
"costGrowth": 1.5,
|
||||
"unlockConditions": [
|
||||
{ "type": "careerLevel", "careerId": "taoist_priest", "op": ">=", "value": 50 }
|
||||
],
|
||||
"effects": [
|
||||
{ "type": "addStat", "stat": "body", "value": 10 }
|
||||
],
|
||||
"resetOnRebirth": false,
|
||||
"maxLevel": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
import { evaluateCondition } from './conditionEvaluator.js';
|
||||
import { applyEffect } from './effectApplier.js';
|
||||
import { canAfford, spendMoney } from './moneySystem.js';
|
||||
|
||||
let _shopConfig = null;
|
||||
|
||||
export function setShopConfig(config) {
|
||||
_shopConfig = config;
|
||||
}
|
||||
|
||||
export function getShopConfig() {
|
||||
return _shopConfig;
|
||||
}
|
||||
|
||||
export function findEntry(config, id) {
|
||||
if (!config || !id) return null;
|
||||
|
||||
const sections = ['items', 'buffs', 'artifacts'];
|
||||
for (const section of sections) {
|
||||
const list = config[section];
|
||||
if (!Array.isArray(list)) continue;
|
||||
const entry = list.find(e => e.id === id);
|
||||
if (entry) {
|
||||
return { ...entry, section };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getArtifactUpgradeCost(config, artifactId, currentLevel) {
|
||||
if (!config || !config.artifacts || !artifactId) return null;
|
||||
const entry = config.artifacts.find(a => a.id === artifactId);
|
||||
if (!entry) return null;
|
||||
return Math.floor(entry.baseCost * Math.pow(entry.costGrowth, currentLevel));
|
||||
}
|
||||
|
||||
export function canBuyItem(config, state, itemId) {
|
||||
const entry = findEntry(config, itemId);
|
||||
if (!entry) return false;
|
||||
|
||||
// Check unlock conditions
|
||||
if (Array.isArray(entry.unlockConditions) && entry.unlockConditions.length > 0) {
|
||||
const allMet = entry.unlockConditions.every(cond => evaluateCondition(cond, state));
|
||||
if (!allMet) return false;
|
||||
}
|
||||
|
||||
// Check max level for artifacts
|
||||
if (entry.section === 'artifacts') {
|
||||
const currentLevel = state.artifacts?.[itemId] || 0;
|
||||
if (entry.maxLevel !== undefined && currentLevel >= entry.maxLevel) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check cost
|
||||
let cost;
|
||||
if (entry.section === 'artifacts') {
|
||||
const currentLevel = state.artifacts?.[itemId] || 0;
|
||||
cost = getArtifactUpgradeCost(config, itemId, currentLevel);
|
||||
} else {
|
||||
cost = entry.cost;
|
||||
}
|
||||
|
||||
if (cost === null || cost === undefined) return false;
|
||||
return canAfford(state, cost);
|
||||
}
|
||||
|
||||
export function buyItem(config, state, itemId) {
|
||||
if (!canBuyItem(config, state, itemId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const entry = findEntry(config, itemId);
|
||||
if (!entry) return false;
|
||||
|
||||
// Calculate cost
|
||||
let cost;
|
||||
if (entry.section === 'artifacts') {
|
||||
const currentLevel = state.artifacts?.[itemId] || 0;
|
||||
cost = getArtifactUpgradeCost(config, itemId, currentLevel);
|
||||
} else {
|
||||
cost = entry.cost;
|
||||
}
|
||||
|
||||
// Spend money
|
||||
if (!spendMoney(state, cost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Apply effects
|
||||
if (Array.isArray(entry.effects)) {
|
||||
for (const effect of entry.effects) {
|
||||
applyEffect(effect, state);
|
||||
}
|
||||
}
|
||||
|
||||
// Record purchase
|
||||
if (entry.section === 'items') {
|
||||
if (!state.shopItems) state.shopItems = {};
|
||||
state.shopItems[itemId] = (state.shopItems[itemId] || 0) + 1;
|
||||
} else if (entry.section === 'buffs') {
|
||||
if (!state.activeBuffs) state.activeBuffs = {};
|
||||
state.activeBuffs[itemId] = { expiresDay: -1 };
|
||||
} else if (entry.section === 'artifacts') {
|
||||
if (!state.artifacts) state.artifacts = {};
|
||||
state.artifacts[itemId] = (state.artifacts[itemId] || 0) + 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function resetShopItems(state) {
|
||||
state.shopItems = {};
|
||||
state.activeBuffs = {};
|
||||
// Artifacts are preserved
|
||||
}
|
||||
|
||||
export function applyArtifactEffects(config, state) {
|
||||
if (!config || !state.artifacts) return;
|
||||
|
||||
for (const [artifactId, level] of Object.entries(state.artifacts)) {
|
||||
if (level <= 0) continue;
|
||||
|
||||
const entry = config.artifacts?.find(a => a.id === artifactId);
|
||||
if (!entry || !Array.isArray(entry.effects)) continue;
|
||||
|
||||
for (const effect of entry.effects) {
|
||||
const scaledEffect = {
|
||||
...effect,
|
||||
value: effect.value * level
|
||||
};
|
||||
applyEffect(scaledEffect, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user