mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-17 13:53:57 -07:00
Up to now `eslint-plugin-brace-rules` was used to enforce a common brace style for JavaScript code. This plugin was however updated the last time over 9 years ago and will be incompatible with ESLint v10, as that [removes `context.getSourceCode()`][1], the plugin relies on. To keep the eslint config working with ESLint v10, this replaces `eslint-plugin-brace-rules` with the [`@stylistic/brace-style`][2] rule from `@stylistic/eslint-plugin`, a package we already use. While `@stylistic/brace-style` doesn't offer an option to format braces in exactly the same way as before, the "allman" style seems to be the one closest to the existing code. [1]: https://eslint.org/blog/2025/11/eslint-v10.0.0-alpha.0-released/#removed-deprecated-rule-context-members [2]: https://eslint.style/rules/brace-style
174 lines
6.6 KiB
JavaScript
174 lines
6.6 KiB
JavaScript
Engine.LoadHelperScript("Player.js");
|
|
Engine.LoadHelperScript("Requirements.js");
|
|
Engine.LoadHelperScript("ValueModification.js");
|
|
Resources = {
|
|
"BuildSchema": type =>
|
|
{
|
|
let schema = "";
|
|
for (const res of ["food", "metal", "stone", "wood"])
|
|
schema +=
|
|
"<optional>" +
|
|
"<element name='" + res + "'>" +
|
|
"<ref name='" + type + "'/>" +
|
|
"</element>" +
|
|
"</optional>";
|
|
return "<interleave>" + schema + "</interleave>";
|
|
}
|
|
};
|
|
Engine.LoadComponentScript("interfaces/ProductionQueue.js");
|
|
Engine.LoadComponentScript("interfaces/ModifiersManager.js"); // Provides `IID_ModifiersManager`, used below.
|
|
Engine.LoadComponentScript("interfaces/Timer.js"); // Provides `IID_Timer`, used below.
|
|
|
|
// What we're testing:
|
|
Engine.LoadComponentScript("interfaces/Upgrade.js");
|
|
Engine.LoadComponentScript("Upgrade.js");
|
|
|
|
// Input (bare minimum needed for tests):
|
|
const techs = {
|
|
"alter_tower_upgrade_cost": {
|
|
"modifications": [
|
|
{ "value": "Upgrade/Cost/stone", "add": 60.0 },
|
|
{ "value": "Upgrade/Cost/wood", "multiply": 0.5 },
|
|
{ "value": "Upgrade/Time", "replace": 90 }
|
|
],
|
|
"affects": ["Tower"]
|
|
}
|
|
};
|
|
const template = {
|
|
"Identity": {
|
|
"Classes": { '@datatype': "tokens", "_string": "Tower" },
|
|
"VisibleClasses": { '@datatype': "tokens", "_string": "" }
|
|
},
|
|
"Upgrade": {
|
|
"Tower": {
|
|
"Cost": { "stone": "100", "wood": "50" },
|
|
"Entity": "structures/{civ}/defense_tower",
|
|
"Time": "100"
|
|
}
|
|
}
|
|
};
|
|
const civCode = "pony";
|
|
const playerID = 1;
|
|
|
|
// Usually, the tech modifications would be worked out by the TechnologyManager
|
|
// with assistance from globalscripts. This test is not about testing the
|
|
// TechnologyManager, so the modifications (both with and without the technology
|
|
// researched) are worked out before hand and placed here.
|
|
let isResearched = false;
|
|
const templateTechModifications = {
|
|
"without": {},
|
|
"with": {
|
|
"Upgrade/Cost/stone": [{ "affects": [["Tower"]], "add": 60 }],
|
|
"Upgrade/Cost/wood": [{ "affects": [["Tower"]], "multiply": 0.5 }],
|
|
"Upgrade/Time": [{ "affects": [["Tower"]], "replace": 90 }]
|
|
}
|
|
};
|
|
const entityTechModifications = {
|
|
"without": {
|
|
'Upgrade/Cost/stone': { "20": { "origValue": 100, "newValue": 100 } },
|
|
'Upgrade/Cost/wood': { "20": { "origValue": 50, "newValue": 50 } },
|
|
'Upgrade/Time': { "20": { "origValue": 100, "newValue": 100 } }
|
|
},
|
|
"with": {
|
|
'Upgrade/Cost/stone': { "20": { "origValue": 100, "newValue": 160 } },
|
|
'Upgrade/Cost/wood': { "20": { "origValue": 50, "newValue": 25 } },
|
|
'Upgrade/Time': { "20": { "origValue": 100, "newValue": 90 } }
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Initialise various bits.
|
|
*/
|
|
// System Entities:
|
|
AddMock(SYSTEM_ENTITY, IID_PlayerManager, {
|
|
"GetPlayerByID": pID => 10 // Called in helpers/player.js::QueryPlayerIDInterface(), as part of Tests T2 and T5.
|
|
});
|
|
AddMock(SYSTEM_ENTITY, IID_TemplateManager, {
|
|
"GetTemplate": () => template, // Called in components/Upgrade.js::ChangeUpgradedEntityCount().
|
|
"TemplateExists": (templ) => true
|
|
});
|
|
AddMock(SYSTEM_ENTITY, IID_Timer, {
|
|
"SetInterval": () => 1, // Called in components/Upgrade.js::Upgrade().
|
|
"CancelTimer": () => {} // Called in components/Upgrade.js::CancelUpgrade().
|
|
});
|
|
|
|
AddMock(SYSTEM_ENTITY, IID_ModifiersManager, {
|
|
"ApplyTemplateModifiers": (valueName, curValue, templ, player) =>
|
|
{
|
|
// Called in helpers/ValueModification.js::ApplyValueModificationsToTemplate()
|
|
// as part of Tests T2 and T5 below.
|
|
const mods = isResearched ? templateTechModifications.with : templateTechModifications.without;
|
|
|
|
if (mods[valueName])
|
|
return GetTechModifiedProperty(mods[valueName], GetIdentityClasses(template.Identity), curValue);
|
|
return curValue;
|
|
},
|
|
"ApplyModifiers": (valueName, curValue, ent) =>
|
|
{
|
|
// Called in helpers/ValueModification.js::ApplyValueModificationsToEntity()
|
|
// as part of Tests T3, T6 and T7 below.
|
|
const mods = isResearched ? entityTechModifications.with : entityTechModifications.without;
|
|
return mods[valueName][ent].newValue;
|
|
}
|
|
});
|
|
|
|
// Init Player:
|
|
AddMock(10, IID_Player, {
|
|
"AddResources": () => {}, // Called in components/Upgrade.js::CancelUpgrade().
|
|
"GetPlayerID": () => playerID, // Called in helpers/Player.js::QueryOwnerInterface() (and several times below).
|
|
"TrySubtractResources": () => true // Called in components/Upgrade.js::Upgrade().
|
|
});
|
|
AddMock(10, IID_Identity, {
|
|
"GetCiv": () => civCode
|
|
});
|
|
|
|
// Create an entity with an Upgrade component:
|
|
AddMock(20, IID_Ownership, {
|
|
"GetOwner": () => playerID // Called in helpers/Player.js::QueryOwnerInterface().
|
|
});
|
|
AddMock(20, IID_Identity, {
|
|
"GetCiv": () => civCode // Called in components/Upgrade.js::init().
|
|
});
|
|
AddMock(20, IID_ProductionQueue, {
|
|
"HasQueuedProduction": () => false
|
|
});
|
|
const cmpUpgrade = ConstructComponent(20, "Upgrade", template.Upgrade);
|
|
cmpUpgrade.owner = playerID;
|
|
cmpUpgrade.OnOwnershipChanged({ "to": playerID });
|
|
|
|
/**
|
|
* Now to start the test proper
|
|
* To start with, no techs are researched...
|
|
*/
|
|
// T1: Check the cost of the upgrade without a player value being passed (as it would be in the structree).
|
|
let parsed_template = GetTemplateDataHelper(template, null, {}, Resources);
|
|
TS_ASSERT_UNEVAL_EQUALS(parsed_template.upgrades[0].cost, { "stone": 100, "wood": 50, "time": 100 });
|
|
|
|
// T2: Check the value, with a player ID (as it would be in-session).
|
|
parsed_template = GetTemplateDataHelper(template, playerID, {}, Resources);
|
|
TS_ASSERT_UNEVAL_EQUALS(parsed_template.upgrades[0].cost, { "stone": 100, "wood": 50, "time": 100 });
|
|
|
|
// T3: Check that the value is correct within the Update Component.
|
|
TS_ASSERT_UNEVAL_EQUALS(cmpUpgrade.GetUpgrades()[0].cost, { "stone": 100, "wood": 50, "time": 100 });
|
|
|
|
/**
|
|
* Tell the Upgrade component to start the Upgrade,
|
|
* then mark the technology that alters the upgrade cost as researched.
|
|
*/
|
|
cmpUpgrade.Upgrade("structures/" + civCode + "/defense_tower");
|
|
isResearched = true;
|
|
|
|
// T4: Check that the player-less value hasn't increased...
|
|
parsed_template = GetTemplateDataHelper(template, null, {}, Resources);
|
|
TS_ASSERT_UNEVAL_EQUALS(parsed_template.upgrades[0].cost, { "stone": 100, "wood": 50, "time": 100 });
|
|
|
|
// T5: ...but the player-backed value has.
|
|
parsed_template = GetTemplateDataHelper(template, playerID, {}, Resources);
|
|
TS_ASSERT_UNEVAL_EQUALS(parsed_template.upgrades[0].cost, { "stone": 160, "wood": 25, "time": 90 });
|
|
|
|
// T6: The upgrade component should still be using the old resource cost (but new time cost) for the upgrade in progress...
|
|
TS_ASSERT_UNEVAL_EQUALS(cmpUpgrade.GetUpgrades()[0].cost, { "stone": 100, "wood": 50, "time": 90 });
|
|
|
|
// T7: ...but with the upgrade cancelled, it now uses the modified value.
|
|
cmpUpgrade.CancelUpgrade(playerID);
|
|
TS_ASSERT_UNEVAL_EQUALS(cmpUpgrade.GetUpgrades()[0].cost, { "stone": 160, "wood": 25, "time": 90 });
|