This commit is contained in:
430
scripts/smoke-content.ts
Normal file
430
scripts/smoke-content.ts
Normal file
@@ -0,0 +1,430 @@
|
||||
import { buildCompanionState, ROLE_TEMPLATE_CHARACTERS, resolveEncounterRecruitCharacter } from '../src/data/characterPresets.ts';
|
||||
import { activateRosterCompanion, benchActiveCompanion, recruitCompanionToParty } from '../src/data/companionRoster.ts';
|
||||
import { getInventoryItemValue, getNpcPurchasePrice } from '../src/data/economy.ts';
|
||||
import {
|
||||
buildEncounterEntryState,
|
||||
buildEncounterTransitionState,
|
||||
interpolateEncounterTransitionState,
|
||||
} from '../src/data/encounterTransition.ts';
|
||||
import {
|
||||
applyEquipmentLoadoutToState,
|
||||
buildInitialEquipmentLoadout,
|
||||
createEmptyEquipmentLoadout,
|
||||
getEquipmentBonuses,
|
||||
} from '../src/data/equipmentEffects.ts';
|
||||
import { createSceneHostileNpcsFromIds } from '../src/data/hostileNpcs.ts';
|
||||
import { isInventoryItemUsable, resolveInventoryItemUseEffect } from '../src/data/inventoryEffects.ts';
|
||||
import { buildInitialNpcState, buildInitialPlayerInventory, buildNpcEncounterStoryMoment, checkTradeItem, createNpcBattleMonster } from '../src/data/npcInteractions.ts';
|
||||
import {
|
||||
acceptQuest,
|
||||
applyQuestProgressFromHostileNpcDefeat,
|
||||
applyQuestProgressFromNpcTalk,
|
||||
buildQuestForEncounter,
|
||||
findQuestById,
|
||||
markQuestTurnedIn,
|
||||
} from '../src/data/questFlow.ts';
|
||||
import { createInitialGameRuntimeStats } from '../src/data/runtimeStats.ts';
|
||||
import { createSceneCallOutEncounter, createSceneEncounterPreview, ensureSceneEncounterPreview } from '../src/data/sceneEncounterPreviews.ts';
|
||||
import { getSceneHostileNpcPresetIds, getScenePresetsByWorld } from '../src/data/scenePresets.ts';
|
||||
import { resolveFunctionOption } from '../src/data/stateFunctions.ts';
|
||||
import { buildTreasureEncounterStoryMoment, buildTreasureResultText, resolveTreasureReward } from '../src/data/treasureInteractions.ts';
|
||||
import { AnimationState, GameState, WorldType } from '../src/types.ts';
|
||||
|
||||
function assert(condition: unknown, message: string): asserts condition {
|
||||
if (!condition) {
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
|
||||
function createBaseState(worldType: WorldType, sceneId?: string): GameState {
|
||||
const playerCharacter = ROLE_TEMPLATE_CHARACTERS[0];
|
||||
const currentScenePreset = sceneId
|
||||
? getScenePresetsByWorld(worldType).find(scene => scene.id === sceneId) ?? null
|
||||
: getScenePresetsByWorld(worldType)[0] ?? null;
|
||||
|
||||
return {
|
||||
worldType,
|
||||
customWorldProfile: null,
|
||||
playerCharacter,
|
||||
runtimeStats: createInitialGameRuntimeStats({ isActiveRun: true }),
|
||||
currentScene: 'Story',
|
||||
storyHistory: [],
|
||||
characterChats: {},
|
||||
ambientIdleMode: undefined,
|
||||
animationState: AnimationState.IDLE,
|
||||
currentEncounter: null,
|
||||
npcInteractionActive: false,
|
||||
currentScenePreset,
|
||||
sceneHostileNpcs: [],
|
||||
playerX: 0,
|
||||
playerOffsetY: 0,
|
||||
playerFacing: 'right',
|
||||
playerActionMode: 'idle',
|
||||
scrollWorld: false,
|
||||
inBattle: false,
|
||||
playerHp: 180,
|
||||
playerMaxHp: 180,
|
||||
playerMana: 100,
|
||||
playerMaxMana: 100,
|
||||
playerSkillCooldowns: {},
|
||||
activeCombatEffects: [],
|
||||
playerCurrency: 180,
|
||||
playerInventory: [],
|
||||
playerEquipment: createEmptyEquipmentLoadout(),
|
||||
npcStates: {},
|
||||
quests: [],
|
||||
roster: [],
|
||||
companions: [],
|
||||
currentBattleNpcId: null,
|
||||
currentNpcBattleMode: null,
|
||||
currentNpcBattleOutcome: null,
|
||||
sparReturnEncounter: null,
|
||||
sparPlayerHpBefore: null,
|
||||
sparPlayerMaxHpBefore: null,
|
||||
sparStoryHistoryBefore: null,
|
||||
};
|
||||
}
|
||||
|
||||
function smokeScenePreviews() {
|
||||
for (const worldType of [WorldType.WUXIA, WorldType.XIANXIA]) {
|
||||
const scene = getScenePresetsByWorld(worldType)[0];
|
||||
assert(scene, `[preview] missing first scene for ${worldType}`);
|
||||
|
||||
const preview = createSceneEncounterPreview(createBaseState(worldType, scene.id));
|
||||
assert(preview.currentEncounter?.kind !== 'treasure', `[preview] treasure encounter should be disabled for ${worldType}`);
|
||||
assert(preview.currentEncounter || preview.sceneHostileNpcs.length > 0 || scene.treasureHints.length === 0, `[preview] ${scene.id} produced no preview entity`);
|
||||
|
||||
const ensured = ensureSceneEncounterPreview(createBaseState(worldType, scene.id));
|
||||
assert(ensured.currentEncounter || ensured.sceneHostileNpcs.length > 0 || scene.treasureHints.length === 0, `[preview] ${scene.id} failed ensureSceneEncounterPreview`);
|
||||
}
|
||||
}
|
||||
|
||||
function smokeNpcStories() {
|
||||
for (const worldType of [WorldType.WUXIA, WorldType.XIANXIA]) {
|
||||
const sceneWithNpc = getScenePresetsByWorld(worldType).find(scene => scene.npcs.length > 0);
|
||||
assert(sceneWithNpc, `[npc] missing npc scene for ${worldType}`);
|
||||
|
||||
const encounter = {
|
||||
id: sceneWithNpc.npcs[0].id,
|
||||
kind: 'npc' as const,
|
||||
characterId: sceneWithNpc.npcs[0].characterId,
|
||||
npcName: sceneWithNpc.npcs[0].name,
|
||||
npcDescription: sceneWithNpc.npcs[0].description,
|
||||
npcAvatar: sceneWithNpc.npcs[0].avatar,
|
||||
context: sceneWithNpc.npcs[0].role,
|
||||
xMeters: 3.2,
|
||||
};
|
||||
const playerCharacter = ROLE_TEMPLATE_CHARACTERS[0];
|
||||
const npcState = buildInitialNpcState(encounter, worldType);
|
||||
const story = buildNpcEncounterStoryMoment({
|
||||
encounter,
|
||||
npcState,
|
||||
playerCharacter,
|
||||
playerInventory: [],
|
||||
activeQuests: [],
|
||||
scene: sceneWithNpc,
|
||||
worldType,
|
||||
partySize: 0,
|
||||
});
|
||||
|
||||
assert(story.options.length >= 3, `[npc] ${sceneWithNpc.id} npc story returned too few options`);
|
||||
const battleMonster = createNpcBattleMonster(encounter, npcState, 'spar');
|
||||
assert(battleMonster.hp >= 7 && battleMonster.hp <= 12, `[npc] spar hp for ${encounter.npcName} out of expected range`);
|
||||
}
|
||||
}
|
||||
|
||||
function smokeTreasureStories() {
|
||||
for (const worldType of [WorldType.WUXIA, WorldType.XIANXIA]) {
|
||||
const sceneWithTreasure = getScenePresetsByWorld(worldType).find(scene => scene.treasureHints.length > 0);
|
||||
assert(sceneWithTreasure, `[treasure] missing treasure scene for ${worldType}`);
|
||||
const state = createBaseState(worldType, sceneWithTreasure.id);
|
||||
|
||||
const encounter = {
|
||||
id: `treasure-${sceneWithTreasure.id}`,
|
||||
kind: 'treasure' as const,
|
||||
npcName: '前方宝藏',
|
||||
npcDescription: `你在前方发现了${sceneWithTreasure.treasureHints[0]}的痕迹。`,
|
||||
npcAvatar: '/Icons/47_treasure.png',
|
||||
context: '宝藏',
|
||||
xMeters: 3.2,
|
||||
};
|
||||
|
||||
const story = buildTreasureEncounterStoryMoment({
|
||||
state,
|
||||
encounter,
|
||||
});
|
||||
|
||||
assert(story.options.length === 3, `[treasure] ${sceneWithTreasure.id} treasure story should provide exactly 3 options`);
|
||||
const inspectReward = resolveTreasureReward(state, encounter, 'inspect');
|
||||
assert(inspectReward.items.length >= 2, `[treasure] ${sceneWithTreasure.id} inspect reward should contain at least 2 items`);
|
||||
assert(buildTreasureResultText(encounter, 'inspect', inspectReward).includes('收'), `[treasure] ${sceneWithTreasure.id} inspect result text should describe loot`);
|
||||
}
|
||||
}
|
||||
|
||||
function smokeMonsterCreation() {
|
||||
for (const worldType of [WorldType.WUXIA, WorldType.XIANXIA]) {
|
||||
const sceneWithMonster = getScenePresetsByWorld(worldType).find(scene => getSceneHostileNpcPresetIds(scene).length > 0);
|
||||
assert(sceneWithMonster, `[monster] missing monster scene for ${worldType}`);
|
||||
const hostileNpcPresetIds = getSceneHostileNpcPresetIds(sceneWithMonster);
|
||||
const monsters = createSceneHostileNpcsFromIds(worldType, hostileNpcPresetIds, 0);
|
||||
assert(monsters.length > 0, `[monster] ${sceneWithMonster.id} failed to create scene monsters`);
|
||||
assert(
|
||||
monsters.length === Math.min(hostileNpcPresetIds.length, 3),
|
||||
`[monster] ${sceneWithMonster.id} should keep the full configured encounter group`,
|
||||
);
|
||||
|
||||
const resolvedState = createBaseState(worldType, sceneWithMonster.id);
|
||||
resolvedState.sceneHostileNpcs = monsters;
|
||||
resolvedState.inBattle = true;
|
||||
assert(
|
||||
resolvedState.sceneHostileNpcs.length === monsters.length,
|
||||
`[monster] ${sceneWithMonster.id} multi-enemy battle state lost monsters`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function smokeRecruitmentData() {
|
||||
for (const worldType of [WorldType.WUXIA, WorldType.XIANXIA]) {
|
||||
const sceneWithCharacterNpc = getScenePresetsByWorld(worldType).find(scene => scene.npcs.some(npc => npc.characterId));
|
||||
assert(sceneWithCharacterNpc, `[recruit] missing recruitable character npc scene for ${worldType}`);
|
||||
const recruitableNpc = sceneWithCharacterNpc.npcs.find(npc => npc.characterId)!;
|
||||
const recruitCharacter = resolveEncounterRecruitCharacter({
|
||||
characterId: recruitableNpc.characterId,
|
||||
context: recruitableNpc.role,
|
||||
npcName: recruitableNpc.name,
|
||||
});
|
||||
assert(recruitCharacter, `[recruit] failed to resolve recruit character for ${recruitableNpc.id}`);
|
||||
const companionState = buildCompanionState(recruitableNpc.id, recruitCharacter, 60);
|
||||
assert(companionState.hp > 0 && companionState.maxHp >= companionState.hp, `[recruit] invalid hp for ${recruitableNpc.id}`);
|
||||
assert(Object.keys(companionState.skillCooldowns).length === recruitCharacter.skills.length, `[recruit] cooldown map mismatch for ${recruitableNpc.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
function smokeObserveAndCallOut() {
|
||||
for (const worldType of [WorldType.WUXIA, WorldType.XIANXIA]) {
|
||||
const scene = getScenePresetsByWorld(worldType)[0];
|
||||
assert(scene, `[idle] missing first scene for ${worldType}`);
|
||||
const baseState = createBaseState(worldType, scene.id);
|
||||
const callOutResult = createSceneCallOutEncounter(baseState);
|
||||
assert(callOutResult.currentEncounter?.kind !== 'treasure', `[idle] treasure call_out should be disabled for ${worldType}`);
|
||||
assert(callOutResult.currentEncounter || callOutResult.sceneHostileNpcs.length > 0 || getSceneHostileNpcPresetIds(scene).length === 0, `[idle] call_out failed for ${scene.id}`);
|
||||
|
||||
const observeOption = resolveFunctionOption(
|
||||
'idle_observe_signs',
|
||||
{
|
||||
worldType,
|
||||
playerCharacter: baseState.playerCharacter,
|
||||
inBattle: false,
|
||||
currentSceneId: scene.id,
|
||||
currentSceneName: scene.name,
|
||||
monsters: [],
|
||||
playerHp: baseState.playerHp,
|
||||
playerMaxHp: baseState.playerMaxHp,
|
||||
playerMana: baseState.playerMana,
|
||||
playerMaxMana: baseState.playerMaxMana,
|
||||
},
|
||||
'观察周围动静',
|
||||
);
|
||||
assert(observeOption?.functionId === 'idle_observe_signs', `[idle] observe_signs option missing for ${scene.id}`);
|
||||
assert(Boolean(observeOption?.detailText?.trim()), `[idle] observe_signs detail missing for ${scene.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
function smokeInventoryUseLoop() {
|
||||
for (const worldType of [WorldType.WUXIA, WorldType.XIANXIA]) {
|
||||
const playerCharacter = ROLE_TEMPLATE_CHARACTERS[0];
|
||||
const inventory = buildInitialPlayerInventory(playerCharacter, worldType);
|
||||
const usableItem = inventory.find(item => isInventoryItemUsable(item));
|
||||
assert(usableItem, `[inventory] missing usable starter item for ${worldType}`);
|
||||
|
||||
const effect = resolveInventoryItemUseEffect(usableItem, playerCharacter);
|
||||
assert(effect, `[inventory] failed to resolve use effect for ${usableItem.name}`);
|
||||
assert(
|
||||
effect.hpRestore > 0 || effect.manaRestore > 0 || effect.cooldownReduction > 0,
|
||||
`[inventory] ${usableItem.name} should provide at least one useful effect`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function smokeEquipmentLoop() {
|
||||
const playerCharacter = ROLE_TEMPLATE_CHARACTERS[0];
|
||||
const starterLoadout = buildInitialEquipmentLoadout(playerCharacter);
|
||||
const starterBonuses = getEquipmentBonuses(starterLoadout);
|
||||
|
||||
assert(starterBonuses.maxHpBonus > 0, '[equipment] starter loadout should provide HP bonus');
|
||||
assert(starterBonuses.outgoingDamageMultiplier > 1, '[equipment] starter loadout should provide damage bonus');
|
||||
|
||||
const baseState = createBaseState(WorldType.WUXIA);
|
||||
const equippedState = applyEquipmentLoadoutToState(baseState, starterLoadout);
|
||||
assert(equippedState.playerMaxHp > baseState.playerMaxHp, '[equipment] applying loadout should increase max HP');
|
||||
assert(equippedState.playerMaxMana > baseState.playerMaxMana, '[equipment] applying loadout should increase max mana');
|
||||
}
|
||||
|
||||
function smokeTradeEconomyLoop() {
|
||||
for (const worldType of [WorldType.WUXIA, WorldType.XIANXIA]) {
|
||||
const sceneWithNpc = getScenePresetsByWorld(worldType).find(scene => scene.npcs.length > 0);
|
||||
assert(sceneWithNpc, `[trade] missing npc scene for ${worldType}`);
|
||||
|
||||
const encounter = {
|
||||
id: sceneWithNpc.npcs[0].id,
|
||||
kind: 'npc' as const,
|
||||
characterId: sceneWithNpc.npcs[0].characterId,
|
||||
npcName: sceneWithNpc.npcs[0].name,
|
||||
npcDescription: sceneWithNpc.npcs[0].description,
|
||||
npcAvatar: sceneWithNpc.npcs[0].avatar,
|
||||
context: sceneWithNpc.npcs[0].role,
|
||||
xMeters: 3.2,
|
||||
};
|
||||
const npcState = buildInitialNpcState(encounter, worldType);
|
||||
const npcItem = npcState.inventory[0];
|
||||
const playerItem = buildInitialPlayerInventory(ROLE_TEMPLATE_CHARACTERS[0], worldType)[0];
|
||||
assert(npcItem, `[trade] missing npc item for ${worldType}`);
|
||||
assert(playerItem, `[trade] missing player item for ${worldType}`);
|
||||
|
||||
const npcItemValue = getInventoryItemValue(npcItem);
|
||||
const playerItemValue = getInventoryItemValue(playerItem);
|
||||
assert(npcItemValue > 0 && playerItemValue > 0, `[trade] item values should be positive for ${worldType}`);
|
||||
|
||||
const purchasePrice = getNpcPurchasePrice(npcItem, npcState.affinity);
|
||||
assert(purchasePrice > 0, `[trade] purchase price should be positive for ${worldType}`);
|
||||
|
||||
const purchaseCheck = checkTradeItem(null, npcItem, npcState.affinity, purchasePrice);
|
||||
assert(purchaseCheck.canPurchase, `[trade] direct purchase should succeed when currency matches price for ${worldType}`);
|
||||
|
||||
const barterCheck = checkTradeItem(playerItem, npcItem, npcState.affinity, 0);
|
||||
assert(typeof barterCheck.canBarter === 'boolean', `[trade] barter check should return a boolean for ${worldType}`);
|
||||
}
|
||||
}
|
||||
|
||||
function smokeEncounterTransitionLoop() {
|
||||
for (const worldType of [WorldType.WUXIA, WorldType.XIANXIA]) {
|
||||
const sceneWithMonster = getScenePresetsByWorld(worldType).find(scene => getSceneHostileNpcPresetIds(scene).length >= 2);
|
||||
assert(sceneWithMonster, `[transition] missing multi-monster scene for ${worldType}`);
|
||||
|
||||
const hostileNpcPresetIds = getSceneHostileNpcPresetIds(sceneWithMonster);
|
||||
const finalMonsters = createSceneHostileNpcsFromIds(worldType, hostileNpcPresetIds, 0);
|
||||
const finalState = {
|
||||
...createBaseState(worldType, sceneWithMonster.id),
|
||||
inBattle: true,
|
||||
sceneHostileNpcs: finalMonsters,
|
||||
};
|
||||
const previewState = {
|
||||
...finalState,
|
||||
inBattle: false,
|
||||
sceneHostileNpcs: finalMonsters.map((monster, index) => ({
|
||||
...monster,
|
||||
xMeters: 12 + (index * 1.8),
|
||||
})),
|
||||
};
|
||||
|
||||
const transitionState = buildEncounterTransitionState(finalState, previewState);
|
||||
assert(
|
||||
transitionState.sceneHostileNpcs[1]?.xMeters === previewState.sceneHostileNpcs[1]?.xMeters,
|
||||
`[transition] second monster should keep its preview x during transition for ${worldType}`,
|
||||
);
|
||||
|
||||
const halfwayState = interpolateEncounterTransitionState(transitionState, finalState, 0.5);
|
||||
assert(
|
||||
halfwayState.sceneHostileNpcs.every((monster, index) => {
|
||||
const startX = transitionState.sceneHostileNpcs[index]?.xMeters ?? monster.xMeters;
|
||||
const endX = finalState.sceneHostileNpcs[index]?.xMeters ?? monster.xMeters;
|
||||
return monster.xMeters !== startX && monster.xMeters !== endX;
|
||||
}),
|
||||
`[transition] all monsters should interpolate instead of only the first one for ${worldType}`,
|
||||
);
|
||||
|
||||
const offscreenState = buildEncounterEntryState(finalState, 18);
|
||||
assert(
|
||||
offscreenState.sceneHostileNpcs.every(monster => monster.xMeters >= 18),
|
||||
`[transition] offscreen entry should place the entire encounter group offscreen for ${worldType}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function smokeRosterLoop() {
|
||||
const playerCharacter = ROLE_TEMPLATE_CHARACTERS[0];
|
||||
const reserveCharacter = ROLE_TEMPLATE_CHARACTERS[1];
|
||||
const recruitCharacter = ROLE_TEMPLATE_CHARACTERS[2];
|
||||
const activeCompanion = buildCompanionState('active-npc', playerCharacter, 68);
|
||||
const reserveCompanion = buildCompanionState('reserve-npc', reserveCharacter, 62);
|
||||
const recruitedCompanion = buildCompanionState('new-npc', recruitCharacter, 72);
|
||||
|
||||
const baseState = {
|
||||
...createBaseState(WorldType.WUXIA),
|
||||
companions: [activeCompanion],
|
||||
roster: [reserveCompanion],
|
||||
};
|
||||
|
||||
const benchedState = benchActiveCompanion(baseState, activeCompanion.npcId);
|
||||
assert(benchedState.companions.length === 0, '[roster] active companion should move off active team');
|
||||
assert(benchedState.roster.some(companion => companion.npcId === activeCompanion.npcId), '[roster] benched companion should enter reserve roster');
|
||||
|
||||
const activatedState = activateRosterCompanion(baseState, reserveCompanion.npcId);
|
||||
assert(activatedState.companions.some(companion => companion.npcId === reserveCompanion.npcId), '[roster] reserve companion should be activatable');
|
||||
assert(!activatedState.roster.some(companion => companion.npcId === reserveCompanion.npcId), '[roster] activated companion should leave reserve roster');
|
||||
|
||||
const swappedState = recruitCompanionToParty(
|
||||
{
|
||||
...baseState,
|
||||
companions: [activeCompanion, reserveCompanion],
|
||||
roster: [],
|
||||
},
|
||||
recruitedCompanion,
|
||||
reserveCompanion.npcId,
|
||||
);
|
||||
assert(swappedState.companions.some(companion => companion.npcId === recruitedCompanion.npcId), '[roster] recruited companion should join active party');
|
||||
assert(swappedState.roster.some(companion => companion.npcId === reserveCompanion.npcId), '[roster] replaced companion should move to reserve roster');
|
||||
}
|
||||
|
||||
function smokeQuestLoop() {
|
||||
for (const worldType of [WorldType.WUXIA, WorldType.XIANXIA]) {
|
||||
const sceneWithNpcAndMonster = getScenePresetsByWorld(worldType).find(
|
||||
scene => scene.npcs.length > 0 && getSceneHostileNpcPresetIds(scene).length > 0,
|
||||
);
|
||||
assert(sceneWithNpcAndMonster, `[quest] missing npc+monster scene for ${worldType}`);
|
||||
|
||||
const issuer = sceneWithNpcAndMonster.npcs[0];
|
||||
const quest = buildQuestForEncounter({
|
||||
issuerNpcId: issuer.id,
|
||||
issuerNpcName: issuer.name,
|
||||
roleText: issuer.role,
|
||||
scene: sceneWithNpcAndMonster,
|
||||
worldType,
|
||||
});
|
||||
|
||||
assert(quest, `[quest] failed to build quest for ${sceneWithNpcAndMonster.id}`);
|
||||
const accepted = acceptQuest([], quest);
|
||||
assert(findQuestById(accepted, quest.id)?.status === 'active', `[quest] ${quest.id} should be active after accept`);
|
||||
|
||||
const afterBattle = applyQuestProgressFromHostileNpcDefeat(
|
||||
accepted,
|
||||
sceneWithNpcAndMonster.id,
|
||||
quest.objective.targetHostileNpcId ? [quest.objective.targetHostileNpcId] : [],
|
||||
);
|
||||
assert(findQuestById(afterBattle, quest.id)?.status === 'active', `[quest] ${quest.id} should stay active until report back`);
|
||||
|
||||
const afterReport = applyQuestProgressFromNpcTalk(afterBattle, issuer.id);
|
||||
assert(findQuestById(afterReport, quest.id)?.status === 'ready_to_turn_in', `[quest] ${quest.id} should become reward-ready after reporting back`);
|
||||
|
||||
const turnedIn = markQuestTurnedIn(afterReport, quest.id);
|
||||
assert(findQuestById(turnedIn, quest.id)?.status === 'turned_in', `[quest] ${quest.id} should turn in successfully`);
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
smokeScenePreviews();
|
||||
smokeNpcStories();
|
||||
smokeTreasureStories();
|
||||
smokeMonsterCreation();
|
||||
smokeRecruitmentData();
|
||||
smokeObserveAndCallOut();
|
||||
smokeInventoryUseLoop();
|
||||
smokeEquipmentLoop();
|
||||
smokeTradeEconomyLoop();
|
||||
smokeEncounterTransitionLoop();
|
||||
smokeRosterLoop();
|
||||
smokeQuestLoop();
|
||||
console.log('Content smoke checks passed.');
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user