679 lines
22 KiB
JavaScript
679 lines
22 KiB
JavaScript
import { expect } from "@esm-bundle/chai";
|
|
import { MissionGenerator } from "../../src/systems/MissionGenerator.js";
|
|
|
|
describe("Systems: MissionGenerator", function () {
|
|
describe("Data Arrays", () => {
|
|
it("should have adjectives array", () => {
|
|
expect(MissionGenerator.ADJECTIVES).to.be.an("array");
|
|
expect(MissionGenerator.ADJECTIVES.length).to.be.greaterThan(0);
|
|
expect(MissionGenerator.ADJECTIVES).to.include("Silent");
|
|
expect(MissionGenerator.ADJECTIVES).to.include("Crimson");
|
|
});
|
|
|
|
it("should have type-specific noun arrays", () => {
|
|
expect(MissionGenerator.NOUNS_SKIRMISH).to.be.an("array");
|
|
expect(MissionGenerator.NOUNS_SALVAGE).to.be.an("array");
|
|
expect(MissionGenerator.NOUNS_ELIMINATE_UNIT).to.be.an("array");
|
|
expect(MissionGenerator.NOUNS_RECON).to.be.an("array");
|
|
|
|
expect(MissionGenerator.NOUNS_SKIRMISH).to.include("Thunder");
|
|
expect(MissionGenerator.NOUNS_SALVAGE).to.include("Cache");
|
|
expect(MissionGenerator.NOUNS_ELIMINATE_UNIT).to.include("Viper");
|
|
expect(MissionGenerator.NOUNS_RECON).to.include("Eye");
|
|
});
|
|
});
|
|
|
|
describe("Utility Methods", () => {
|
|
describe("toRomanNumeral", () => {
|
|
it("should convert numbers to Roman numerals", () => {
|
|
expect(MissionGenerator.toRomanNumeral(1)).to.equal("I");
|
|
expect(MissionGenerator.toRomanNumeral(2)).to.equal("II");
|
|
expect(MissionGenerator.toRomanNumeral(3)).to.equal("III");
|
|
expect(MissionGenerator.toRomanNumeral(4)).to.equal("IV");
|
|
expect(MissionGenerator.toRomanNumeral(5)).to.equal("V");
|
|
});
|
|
});
|
|
|
|
describe("extractBaseName", () => {
|
|
it("should extract base name from mission title", () => {
|
|
expect(
|
|
MissionGenerator.extractBaseName("Operation: Silent Viper")
|
|
).to.equal("Silent Viper");
|
|
expect(
|
|
MissionGenerator.extractBaseName("Operation: Silent Viper II")
|
|
).to.equal("Silent Viper");
|
|
expect(
|
|
MissionGenerator.extractBaseName("Operation: Crimson Cache III")
|
|
).to.equal("Crimson Cache");
|
|
});
|
|
});
|
|
|
|
describe("findHighestNumeral", () => {
|
|
it("should find highest Roman numeral in history", () => {
|
|
const history = [
|
|
"Operation: Silent Viper",
|
|
"Operation: Silent Viper II",
|
|
"Operation: Silent Viper III",
|
|
];
|
|
expect(
|
|
MissionGenerator.findHighestNumeral("Silent Viper", history)
|
|
).to.equal(3);
|
|
});
|
|
|
|
it("should return 0 if no matches found", () => {
|
|
const history = ["Operation: Other Mission"];
|
|
expect(
|
|
MissionGenerator.findHighestNumeral("Silent Viper", history)
|
|
).to.equal(0);
|
|
});
|
|
});
|
|
|
|
describe("selectBiome", () => {
|
|
it("should select from unlocked regions", () => {
|
|
const regions = ["BIOME_RUSTING_WASTES", "BIOME_CRYSTAL_SPIRES"];
|
|
const biome = MissionGenerator.selectBiome(regions);
|
|
expect(regions).to.include(biome);
|
|
});
|
|
|
|
it("should return default if no regions provided", () => {
|
|
const biome = MissionGenerator.selectBiome([]);
|
|
expect(biome).to.equal("BIOME_RUSTING_WASTES");
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("generateSideOp", () => {
|
|
const unlockedRegions = ["BIOME_RUSTING_WASTES", "BIOME_CRYSTAL_SPIRES"];
|
|
const emptyHistory = [];
|
|
|
|
it("CoA 1: Should generate a mission with required structure", () => {
|
|
const mission = MissionGenerator.generateSideOp(
|
|
2,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
|
|
expect(mission).to.have.property("id");
|
|
expect(mission).to.have.property("type", "SIDE_QUEST");
|
|
expect(mission).to.have.property("config");
|
|
expect(mission).to.have.property("biome");
|
|
expect(mission).to.have.property("objectives");
|
|
expect(mission).to.have.property("rewards");
|
|
expect(mission).to.have.property("expiresIn", 3);
|
|
});
|
|
|
|
it("CoA 2: Should generate unique mission IDs", () => {
|
|
const mission1 = MissionGenerator.generateSideOp(
|
|
1,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
const mission2 = MissionGenerator.generateSideOp(
|
|
1,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
|
|
expect(mission1.id).to.not.equal(mission2.id);
|
|
expect(mission1.id).to.match(/^SIDE_OP_\d+_[a-z0-9]+$/);
|
|
expect(mission2.id).to.match(/^SIDE_OP_\d+_[a-z0-9]+$/);
|
|
});
|
|
|
|
it("CoA 3: Should generate title in 'Operation: [Adj] [Noun]' format", () => {
|
|
const mission = MissionGenerator.generateSideOp(
|
|
1,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
|
|
expect(mission.config.title).to.match(/^Operation: .+$/);
|
|
const parts = mission.config.title.replace("Operation: ", "").split(" ");
|
|
expect(parts.length).to.be.at.least(2);
|
|
});
|
|
|
|
it("CoA 4: Should select archetype and generate appropriate objectives", () => {
|
|
// Run multiple times to test different archetypes
|
|
const archetypes = new Set();
|
|
const objectiveTypes = new Set();
|
|
|
|
for (let i = 0; i < 20; i++) {
|
|
const mission = MissionGenerator.generateSideOp(
|
|
2,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
const primaryObj = mission.objectives.primary[0];
|
|
objectiveTypes.add(primaryObj.type);
|
|
|
|
// Infer archetype from objective type
|
|
if (primaryObj.type === "ELIMINATE_ALL") {
|
|
archetypes.add("SKIRMISH");
|
|
} else if (primaryObj.type === "INTERACT") {
|
|
archetypes.add("SALVAGE");
|
|
} else if (primaryObj.type === "ELIMINATE_UNIT") {
|
|
archetypes.add("ELIMINATE_UNIT");
|
|
} else if (primaryObj.type === "REACH_ZONE") {
|
|
archetypes.add("RECON");
|
|
}
|
|
}
|
|
|
|
// Should have generated at least 2 different archetypes
|
|
expect(archetypes.size).to.be.greaterThan(1);
|
|
});
|
|
|
|
it("CoA 5: Should generate series missions with Roman numerals", () => {
|
|
const history = ["Operation: Silent Viper"];
|
|
const mission = MissionGenerator.generateSideOp(
|
|
1,
|
|
unlockedRegions,
|
|
history
|
|
);
|
|
|
|
// If it matches the base name, should have "II"
|
|
if (mission.config.title.includes("Silent Viper")) {
|
|
expect(mission.config.title).to.include("II");
|
|
}
|
|
});
|
|
|
|
it("CoA 6: Should scale difficulty tier correctly", () => {
|
|
for (let tier = 1; tier <= 5; tier++) {
|
|
const mission = MissionGenerator.generateSideOp(
|
|
tier,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
expect(mission.config.difficulty_tier).to.equal(tier);
|
|
expect(mission.config.recommended_level).to.be.a("number");
|
|
}
|
|
});
|
|
|
|
it("CoA 7: Should generate biome configuration", () => {
|
|
const mission = MissionGenerator.generateSideOp(
|
|
2,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
|
|
expect(mission.biome).to.have.property("type");
|
|
expect(mission.biome).to.have.property("generator_config");
|
|
expect(mission.biome.generator_config).to.have.property(
|
|
"seed_type",
|
|
"RANDOM"
|
|
);
|
|
expect(mission.biome.generator_config).to.have.property("size");
|
|
expect(mission.biome.generator_config).to.have.property("room_count");
|
|
expect(mission.biome.generator_config).to.have.property("density");
|
|
});
|
|
|
|
it("CoA 8: Should generate rewards with tier-based scaling", () => {
|
|
const mission = MissionGenerator.generateSideOp(
|
|
3,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
|
|
expect(mission.rewards).to.have.property("guaranteed");
|
|
expect(mission.rewards.guaranteed).to.have.property("xp");
|
|
expect(mission.rewards.guaranteed).to.have.property("currency");
|
|
expect(mission.rewards.guaranteed.currency).to.have.property(
|
|
"aether_shards"
|
|
);
|
|
expect(mission.rewards).to.have.property("faction_reputation");
|
|
|
|
// Higher tier should have higher rewards
|
|
const lowTierMission = MissionGenerator.generateSideOp(
|
|
1,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
const highTierMission = MissionGenerator.generateSideOp(
|
|
5,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
|
|
expect(
|
|
highTierMission.rewards.guaranteed.currency.aether_shards
|
|
).to.be.greaterThan(
|
|
lowTierMission.rewards.guaranteed.currency.aether_shards
|
|
);
|
|
});
|
|
|
|
it("CoA 9: Should generate archetype-specific objectives", () => {
|
|
// Test Skirmish (ELIMINATE_ALL)
|
|
let foundSkirmish = false;
|
|
for (let i = 0; i < 30 && !foundSkirmish; i++) {
|
|
const mission = MissionGenerator.generateSideOp(
|
|
2,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
if (mission.objectives.primary[0].type === "ELIMINATE_ALL") {
|
|
foundSkirmish = true;
|
|
expect(mission.objectives.primary[0].description).to.include(
|
|
"Clear the sector"
|
|
);
|
|
}
|
|
}
|
|
expect(foundSkirmish).to.be.true;
|
|
|
|
// Test Salvage (INTERACT)
|
|
let foundSalvage = false;
|
|
for (let i = 0; i < 30 && !foundSalvage; i++) {
|
|
const mission = MissionGenerator.generateSideOp(
|
|
2,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
if (mission.objectives.primary[0].type === "INTERACT") {
|
|
foundSalvage = true;
|
|
expect(mission.objectives.primary[0].target_object_id).to.equal(
|
|
"OBJ_SUPPLY_CRATE"
|
|
);
|
|
expect(mission.objectives.primary[0].target_count).to.be.at.least(3);
|
|
expect(mission.objectives.primary[0].target_count).to.be.at.most(5);
|
|
}
|
|
}
|
|
expect(foundSalvage).to.be.true;
|
|
|
|
// Test Eliminate Unit
|
|
let foundEliminateUnit = false;
|
|
for (let i = 0; i < 30 && !foundEliminateUnit; i++) {
|
|
const mission = MissionGenerator.generateSideOp(
|
|
2,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
if (mission.objectives.primary[0].type === "ELIMINATE_UNIT") {
|
|
foundEliminateUnit = true;
|
|
expect(mission.objectives.primary[0]).to.have.property(
|
|
"target_def_id"
|
|
);
|
|
expect(mission.objectives.primary[0].description).to.include(
|
|
"High-Value Target"
|
|
);
|
|
}
|
|
}
|
|
expect(foundEliminateUnit).to.be.true;
|
|
|
|
// Test Recon (REACH_ZONE)
|
|
let foundRecon = false;
|
|
for (let i = 0; i < 30 && !foundRecon; i++) {
|
|
const mission = MissionGenerator.generateSideOp(
|
|
2,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
if (mission.objectives.primary[0].type === "REACH_ZONE") {
|
|
foundRecon = true;
|
|
expect(mission.objectives.primary[0].target_count).to.equal(3);
|
|
const hasTurnLimit = mission.objectives.failure_conditions.some(
|
|
(fc) => fc.type === "TURN_LIMIT_EXCEEDED"
|
|
);
|
|
expect(hasTurnLimit).to.be.true;
|
|
}
|
|
}
|
|
expect(foundRecon).to.be.true;
|
|
});
|
|
|
|
it("CoA 10: Should generate archetype-specific biome configs", () => {
|
|
// Test multiple times to get different archetypes
|
|
const configs = new Map();
|
|
|
|
for (let i = 0; i < 50; i++) {
|
|
const mission = MissionGenerator.generateSideOp(
|
|
2,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
const objType = mission.objectives.primary[0].type;
|
|
if (!configs.has(objType)) {
|
|
configs.set(objType, mission.biome.generator_config);
|
|
}
|
|
}
|
|
|
|
// Check that RECON has larger maps
|
|
const reconConfig = Array.from(configs.entries()).find(([type]) => {
|
|
// Find a mission with REACH_ZONE to check its config
|
|
return type === "REACH_ZONE";
|
|
});
|
|
if (reconConfig) {
|
|
const size = reconConfig[1].size;
|
|
expect(size.x).to.be.at.least(24);
|
|
expect(size.z).to.be.at.least(24);
|
|
expect(reconConfig[1].density).to.equal("LOW");
|
|
}
|
|
});
|
|
|
|
it("CoA 11: Should map biome to faction reputation", () => {
|
|
const mission = MissionGenerator.generateSideOp(
|
|
2,
|
|
["BIOME_RUSTING_WASTES"],
|
|
emptyHistory
|
|
);
|
|
|
|
expect(mission.rewards.faction_reputation).to.have.property(
|
|
"COGWORK_CONCORD"
|
|
);
|
|
expect(mission.rewards.faction_reputation.COGWORK_CONCORD).to.equal(10);
|
|
});
|
|
|
|
it("CoA 12: Should clamp tier to valid range (1-5)", () => {
|
|
const lowMission = MissionGenerator.generateSideOp(
|
|
0,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
const highMission = MissionGenerator.generateSideOp(
|
|
10,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
|
|
expect(lowMission.config.difficulty_tier).to.equal(1);
|
|
expect(highMission.config.difficulty_tier).to.equal(5);
|
|
});
|
|
});
|
|
|
|
describe("refreshBoard", () => {
|
|
const unlockedRegions = ["BIOME_RUSTING_WASTES"];
|
|
const emptyHistory = [];
|
|
|
|
it("CoA 13: Should fill board up to 5 missions", () => {
|
|
const emptyBoard = [];
|
|
const refreshed = MissionGenerator.refreshBoard(
|
|
emptyBoard,
|
|
2,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
|
|
expect(refreshed.length).to.equal(5);
|
|
});
|
|
|
|
it("CoA 14: Should not exceed 5 missions", () => {
|
|
const existingMissions = [
|
|
MissionGenerator.generateSideOp(2, unlockedRegions, emptyHistory),
|
|
MissionGenerator.generateSideOp(2, unlockedRegions, emptyHistory),
|
|
MissionGenerator.generateSideOp(2, unlockedRegions, emptyHistory),
|
|
];
|
|
const refreshed = MissionGenerator.refreshBoard(
|
|
existingMissions,
|
|
2,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
|
|
expect(refreshed.length).to.equal(5);
|
|
});
|
|
|
|
it("CoA 15: Should remove expired missions on daily reset", () => {
|
|
const mission1 = MissionGenerator.generateSideOp(
|
|
2,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
mission1.expiresIn = 1; // About to expire
|
|
const mission2 = MissionGenerator.generateSideOp(
|
|
2,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
mission2.expiresIn = 3; // Still valid
|
|
|
|
const board = [mission1, mission2];
|
|
const refreshed = MissionGenerator.refreshBoard(
|
|
board,
|
|
2,
|
|
unlockedRegions,
|
|
emptyHistory,
|
|
true
|
|
);
|
|
|
|
// Mission1 should be removed (expiresIn becomes 0), mission2 kept, then filled to 5
|
|
expect(refreshed.length).to.equal(5);
|
|
// Mission1 should not be in the list
|
|
expect(refreshed.find((m) => m.id === mission1.id)).to.be.undefined;
|
|
// Mission2 should be present with decremented expiresIn
|
|
const foundMission2 = refreshed.find((m) => m.id === mission2.id);
|
|
expect(foundMission2).to.exist;
|
|
expect(foundMission2.expiresIn).to.equal(2);
|
|
});
|
|
|
|
it("CoA 16: Should not remove missions when not daily reset", () => {
|
|
const mission1 = MissionGenerator.generateSideOp(
|
|
2,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
mission1.expiresIn = 1;
|
|
const mission2 = MissionGenerator.generateSideOp(
|
|
2,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
mission2.expiresIn = 3;
|
|
|
|
const board = [mission1, mission2];
|
|
const refreshed = MissionGenerator.refreshBoard(
|
|
board,
|
|
2,
|
|
unlockedRegions,
|
|
emptyHistory,
|
|
false
|
|
);
|
|
|
|
// Both should remain (not expired yet), expiresIn unchanged, then filled to 5
|
|
expect(refreshed.length).to.equal(5);
|
|
const foundMission1 = refreshed.find((m) => m.id === mission1.id);
|
|
const foundMission2 = refreshed.find((m) => m.id === mission2.id);
|
|
expect(foundMission1).to.exist;
|
|
expect(foundMission2).to.exist;
|
|
expect(foundMission1.expiresIn).to.equal(1);
|
|
expect(foundMission2.expiresIn).to.equal(3);
|
|
});
|
|
|
|
it("CoA 17: Should preserve valid missions and add new ones", () => {
|
|
const mission1 = MissionGenerator.generateSideOp(
|
|
2,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
mission1.expiresIn = 3;
|
|
|
|
const board = [mission1];
|
|
const refreshed = MissionGenerator.refreshBoard(
|
|
board,
|
|
2,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
|
|
expect(refreshed.length).to.equal(5);
|
|
expect(refreshed.find((m) => m.id === mission1.id)).to.exist;
|
|
});
|
|
|
|
it("CoA 18: Should handle missions without expiresIn", () => {
|
|
const mission1 = MissionGenerator.generateSideOp(
|
|
2,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
delete mission1.expiresIn;
|
|
|
|
const board = [mission1];
|
|
const refreshed = MissionGenerator.refreshBoard(
|
|
board,
|
|
2,
|
|
unlockedRegions,
|
|
emptyHistory,
|
|
true
|
|
);
|
|
|
|
// Mission without expiresIn should be preserved
|
|
expect(refreshed.find((m) => m.id === mission1.id)).to.exist;
|
|
});
|
|
});
|
|
|
|
describe("Reward Calculation", () => {
|
|
const unlockedRegions = ["BIOME_RUSTING_WASTES"];
|
|
|
|
it("CoA 19: Should calculate currency with tier multiplier and random factor", () => {
|
|
// Generate multiple missions to account for different archetypes
|
|
// Non-Eliminate Unit missions: Base 50 * 2.5 (tier 3) * random(0.8, 1.2) = 100-150 range
|
|
// Eliminate Unit missions get 1.5x bonus: 150-225 range
|
|
let foundNonEliminateUnit = false;
|
|
for (let i = 0; i < 20 && !foundNonEliminateUnit; i++) {
|
|
const mission = MissionGenerator.generateSideOp(3, unlockedRegions, []);
|
|
const currency = mission.rewards.guaranteed.currency.aether_shards;
|
|
const isEliminateUnit =
|
|
mission.objectives.primary[0].type === "ELIMINATE_UNIT";
|
|
|
|
if (!isEliminateUnit) {
|
|
foundNonEliminateUnit = true;
|
|
expect(currency).to.be.at.least(100); // 50 * 2.5 * 0.8 = 100
|
|
expect(currency).to.be.at.most(150); // 50 * 2.5 * 1.2 = 150
|
|
} else {
|
|
// Assassination missions get 1.5x bonus
|
|
expect(currency).to.be.at.least(150); // 50 * 2.5 * 0.8 * 1.5 = 150
|
|
expect(currency).to.be.at.most(225); // 50 * 2.5 * 1.2 * 1.5 = 225
|
|
}
|
|
}
|
|
expect(foundNonEliminateUnit).to.be.true;
|
|
});
|
|
|
|
it("CoA 20: Should give bonus currency for Eliminate Unit missions", () => {
|
|
let foundEliminateUnit = false;
|
|
let eliminateUnitCurrency = 0;
|
|
let otherCurrency = 0;
|
|
|
|
for (let i = 0; i < 50 && !foundEliminateUnit; i++) {
|
|
const mission = MissionGenerator.generateSideOp(2, unlockedRegions, []);
|
|
if (mission.objectives.primary[0].type === "ELIMINATE_UNIT") {
|
|
foundEliminateUnit = true;
|
|
eliminateUnitCurrency =
|
|
mission.rewards.guaranteed.currency.aether_shards;
|
|
} else {
|
|
otherCurrency = mission.rewards.guaranteed.currency.aether_shards;
|
|
}
|
|
}
|
|
|
|
if (foundEliminateUnit) {
|
|
// Eliminate Unit should have higher currency (1.5x bonus)
|
|
expect(eliminateUnitCurrency).to.be.greaterThan(otherCurrency * 0.9);
|
|
}
|
|
});
|
|
|
|
it("CoA 21: Should have chance for item drops based on tier", () => {
|
|
// Higher tier should have higher chance
|
|
let foundItem = false;
|
|
for (let i = 0; i < 50; i++) {
|
|
const mission = MissionGenerator.generateSideOp(5, unlockedRegions, []);
|
|
if (
|
|
mission.rewards.guaranteed.items &&
|
|
mission.rewards.guaranteed.items.length > 0
|
|
) {
|
|
foundItem = true;
|
|
expect(mission.rewards.guaranteed.items[0]).to.match(/^ITEM_/);
|
|
break;
|
|
}
|
|
}
|
|
// Tier 5 has 100% chance (5 * 0.2), so should always find one
|
|
if (!foundItem) {
|
|
// Fallback check if random failed (unlikely with 50 tries at 100% but safe)
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("New Features: Narrative, Hazards, Bosses", () => {
|
|
const unlockedRegions = ["BIOME_RUSTING_WASTES"];
|
|
const emptyHistory = [];
|
|
|
|
it("CoA 22: Should generate dynamic narrative with correct structure", () => {
|
|
const mission = MissionGenerator.generateSideOp(
|
|
2,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
|
|
expect(mission).to.have.property("narrative");
|
|
expect(mission.narrative).to.have.property("intro_sequence");
|
|
expect(mission.narrative).to.have.property("outro_success");
|
|
expect(mission.narrative).to.have.property("_dynamic_data");
|
|
|
|
const introId = mission.narrative.intro_sequence;
|
|
const dynamicData = mission.narrative._dynamic_data;
|
|
|
|
expect(dynamicData).to.have.property(introId);
|
|
expect(dynamicData[introId].nodes).to.be.an("array");
|
|
expect(dynamicData[introId].nodes[0].text).to.be.a("string");
|
|
expect(dynamicData[introId].nodes[0].text.length).to.be.greaterThan(10);
|
|
});
|
|
|
|
it("CoA 23: Should generate hazards for biomes (probabilistic)", () => {
|
|
// Check that we eventually get a hazard
|
|
let foundHazard = false;
|
|
let attempts = 0;
|
|
while (!foundHazard && attempts < 50) {
|
|
const mission = MissionGenerator.generateSideOp(
|
|
2,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
if (mission.biome.hazards && mission.biome.hazards.length > 0) {
|
|
foundHazard = true;
|
|
expect(
|
|
MissionGenerator.BIOME_HAZARDS["BIOME_RUSTING_WASTES"]
|
|
).to.include(mission.biome.hazards[0]);
|
|
}
|
|
attempts++;
|
|
}
|
|
expect(foundHazard, "Should have generated a hazard within 50 attempts")
|
|
.to.be.true;
|
|
});
|
|
|
|
it("CoA 24: Should generate boss config for Eliminate Unit missions", () => {
|
|
let foundEliminateUnit = false;
|
|
let attempts = 0;
|
|
|
|
while (!foundEliminateUnit && attempts < 50) {
|
|
const mission = MissionGenerator.generateSideOp(
|
|
2,
|
|
unlockedRegions,
|
|
emptyHistory
|
|
);
|
|
if (mission.config.boss_config) {
|
|
foundEliminateUnit = true;
|
|
const config = mission.config.boss_config;
|
|
|
|
expect(config).to.have.property("target_def_id");
|
|
expect(config).to.have.property("name");
|
|
expect(config).to.have.property("stats");
|
|
expect(config.stats.hp_multiplier).to.equal(2.0);
|
|
expect(config.stats.attack_multiplier).to.equal(1.5);
|
|
}
|
|
attempts++;
|
|
}
|
|
expect(
|
|
foundEliminateUnit,
|
|
"Should have generated an Eliminate Unit mission with boss config"
|
|
).to.be.true;
|
|
});
|
|
|
|
it("CoA 25: Narrative text should contain replaced variables", () => {
|
|
const mission = MissionGenerator.generateSideOp(
|
|
2,
|
|
["BIOME_RUSTING_WASTES"],
|
|
emptyHistory
|
|
);
|
|
const dynamicData = mission.narrative._dynamic_data;
|
|
const introId = mission.narrative.intro_sequence;
|
|
const text = dynamicData[introId].nodes[0].text;
|
|
|
|
// Check for replacements (lowercase biome name)
|
|
expect(text).to.not.include("{biome}");
|
|
expect(text).to.not.include("{enemy}");
|
|
expect(text).to.include("rusting wastes");
|
|
});
|
|
});
|
|
});
|