@aaronjewell/w3-build-order
Version:
Library for configuring a build order in Warcraft 3
1,872 lines (1,659 loc) • 83.3 kB
JavaScript
import clonedeep from 'lodash.clonedeep';
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
if (enumerableOnly) symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
if (i % 2) {
ownKeys(Object(source), true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
}
return target;
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
if (superClass) _setPrototypeOf(subClass, superClass);
}
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
function _isNativeReflectConstruct() {
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
if (Reflect.construct.sham) return false;
if (typeof Proxy === "function") return true;
try {
Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
return true;
} catch (e) {
return false;
}
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}
function _possibleConstructorReturn(self, call) {
if (call && (typeof call === "object" || typeof call === "function")) {
return call;
}
return _assertThisInitialized(self);
}
function _createSuper(Derived) {
var hasNativeReflectConstruct = _isNativeReflectConstruct();
return function _createSuperInternal() {
var Super = _getPrototypeOf(Derived),
result;
if (hasNativeReflectConstruct) {
var NewTarget = _getPrototypeOf(this).constructor;
result = Reflect.construct(Super, arguments, NewTarget);
} else {
result = Super.apply(this, arguments);
}
return _possibleConstructorReturn(this, result);
};
}
function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
}
function _toConsumableArray(arr) {
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
}
function _arrayWithoutHoles(arr) {
if (Array.isArray(arr)) return _arrayLikeToArray(arr);
}
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
function _iterableToArray(iter) {
if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter);
}
function _iterableToArrayLimit(arr, i) {
if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return;
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"] != null) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(o);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
return arr2;
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _createForOfIteratorHelper(o, allowArrayLike) {
var it;
if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) {
if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
if (it) o = it;
var i = 0;
var F = function () {};
return {
s: F,
n: function () {
if (i >= o.length) return {
done: true
};
return {
done: false,
value: o[i++]
};
},
e: function (e) {
throw e;
},
f: F
};
}
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
var normalCompletion = true,
didErr = false,
err;
return {
s: function () {
it = o[Symbol.iterator]();
},
n: function () {
var step = it.next();
normalCompletion = step.done;
return step;
},
e: function (e) {
didErr = true;
err = e;
},
f: function () {
try {
if (!normalCompletion && it.return != null) it.return();
} finally {
if (didErr) throw err;
}
}
};
}
var buildings = [{
id: "altarOfStorms",
name: "Altar of Storms",
type: "building",
gold: 180,
lumber: 50,
buildTime: 60,
supply: 0,
dependsOn: [],
order: 6,
units: ["bladeMaster", "farSeer", "shadowHunter", "taurenChieftain"],
upgrades: [],
items: [],
image: "orc/altarofstorms.png"
}, {
id: "barracks",
name: "Barracks",
type: "building",
gold: 180,
lumber: 50,
buildTime: 60,
supply: 0,
dependsOn: [],
order: 2,
units: ["grunt"],
upgrades: ["trollRegeneration", "berserkerUpgrade", "burningOil"],
items: [],
image: "orc/barracks.png"
}, {
id: "beastiary",
name: "Beastiary",
type: "building",
gold: 145,
lumber: 140,
buildTime: 60,
supply: 0,
dependsOn: ["stronghold"],
order: 8,
units: [],
upgrades: ["ensnare", "envenomedSpears", "liquidFire"],
items: [],
image: "orc/beastiary.png"
}, {
id: "greatHall",
name: "Great Hall",
type: "building",
gold: 385,
lumber: 185,
buildTime: 150,
supply: 11,
tier: 1,
dependsOn: [],
order: 1,
units: ["peon"],
upgrades: ["pillage", "unitInventory", "stronghold", "fortress"],
items: [],
image: "orc/greathall.png"
}, {
id: "orcBurrow",
name: "Orc Burrow",
type: "building",
gold: 160,
lumber: 40,
buildTime: 50,
supply: 10,
dependsOn: [],
order: 5,
units: [],
upgrades: [],
items: [],
image: "orc/orcburrow.png"
}, {
id: "spiritLodge",
name: "Spirit Lodge",
type: "building",
gold: 150,
lumber: 135,
buildTime: 70,
supply: 0,
dependsOn: ["stronghold"],
order: 7,
units: [],
upgrades: ["shamanAdeptTraining", "shamanMasterTraining", "witchDoctorAdeptTraining", "witchDoctorMasterTraining"],
items: [],
image: "orc/spiritlodge.png"
}, {
id: "taurenTotem",
name: "Tauren Totem",
type: "building",
gold: 135,
lumber: 155,
buildTime: 70,
supply: 0,
dependsOn: ["warMill", "fortress"],
order: 9,
units: [],
upgrades: ["spiritWalkeAdeptTraining", "spiritWalkerMasterTraining", "pulverizeDamageIncrease"],
items: [],
image: "orc/taurentotem.png"
}, {
id: "voodooLounge",
name: "Voodoo Lounge",
type: "building",
gold: 130,
lumber: 30,
buildTime: 60,
supply: 0,
dependsOn: [],
order: 10,
units: [],
upgrades: [],
items: ["healingSalve", "lesserClarityPotion", "orbOfLightning", "potionOfHealing", "potionOfMana", "scrollOfSpeed", "scrollOfTownPortal", "tinyGreatHall"],
image: "orc/voodoolounge.png"
}, {
id: "warMill",
name: "War Mill",
type: "building",
gold: 205,
lumber: 0,
buildTime: 70,
supply: 0,
dependsOn: [],
order: 3,
units: [],
upgrades: ["spikedBarricades", "improvedSpikedBarricades", "reinforcedDefenses", "steelMeleeWeapons", "thoriumMeleeWeapons", "arcaniteMeleeWeapons", "steelRangedWeapons", "thoriumRangedWeapons", "arcaniteRangedWeapons"],
items: [],
image: "orc/warmill.png"
}, {
id: "watchTower",
name: "Watch Tower",
type: "building",
gold: 110,
lumber: 80,
buildTime: 60,
supply: 0,
dependsOn: ["warMill"],
order: 4,
units: [],
upgrades: [],
items: [],
image: "orc/watchtower.png"
}];
var units = [{
id: "peon",
name: "Peon",
type: "unit",
gold: 75,
lumber: 0,
supply: 1,
buildTime: 15,
canHarvest: true,
canMine: true,
canBuild: true,
order: 1,
image: "orc/peon.png"
}, {
id: "grunt",
name: "Grunt",
type: "unit",
gold: 200,
lumber: 0,
supply: 3,
buildTime: 30,
order: 1,
image: "orc/grunt.png"
}, {
id: "headhunter",
name: "Headhunter",
type: "unit",
gold: 135,
lumber: 20,
supply: 2,
buildTime: 20,
order: 2,
image: "orc/headhunter.png"
}, {
id: "demolisher",
name: "Demolisher",
type: "unit",
gold: 220,
lumber: 50,
supply: 4,
buildTime: 40,
order: 3,
image: "orc/demolisher.png"
}, {
id: "shaman",
name: "Shaman",
type: "unit",
gold: 130,
lumber: 20,
supply: 2,
buildTime: 30,
order: 1,
image: "orc/shaman.png"
}, {
id: "witchDoctor",
name: "Witch Doctor",
type: "unit",
gold: 145,
lumber: 25,
supply: 2,
buildTime: 30,
order: 2,
image: "orc/witchdoctor.png"
}, {
id: "raider",
name: "Raider",
type: "unit",
gold: 180,
lumber: 40,
supply: 3,
buildTime: 28,
order: 1,
image: "orc/wolfrider.png"
}, {
id: "kodoBeast",
name: "Kodo Beast",
type: "unit",
gold: 255,
lumber: 60,
supply: 4,
buildTime: 30,
order: 3,
image: "orc/kodobeast.png"
}, {
id: "windRider",
name: "Wind Rider",
type: "unit",
gold: 265,
lumber: 40,
supply: 4,
buildTime: 35,
order: 2,
image: "orc/wyvernrider.png"
}, {
id: "trollBatrider",
name: "Troll Batrider",
type: "unit",
gold: 160,
lumber: 40,
supply: 2,
buildTime: 28,
orer: 4,
image: "orc/batrider.png"
}, {
id: "tauren",
name: "Tauren",
type: "unit",
gold: 280,
lumber: 80,
supply: 5,
buildTime: 44,
order: 2,
image: "orc/tauren.png"
}, {
id: "bladeMaster",
name: "Blade Master",
type: "unit",
gold: 425,
lumber: 100,
supply: 5,
buildTime: 55,
limit: 1,
isHero: true,
order: 9,
image: "orc/blademaster.png"
}, {
id: "farSeer",
name: "Far Seer",
type: "unit",
gold: 425,
lumber: 100,
supply: 5,
buildTime: 55,
limit: 1,
isHero: true,
order: 10,
image: "orc/farseer.png"
}, {
id: "taurenChieftain",
name: "Tuaren Chieftain",
type: "unit",
gold: 425,
lumber: 100,
supply: 5,
buildTime: 55,
limit: 1,
isHero: true,
order: 11,
image: "orc/chieftain.png"
}, {
id: "shadowHunter",
name: "Shadow Hunter",
type: "unit",
gold: 425,
lumber: 100,
supply: 5,
buildTime: 55,
limit: 1,
isHero: true,
order: 5,
image: "orc/shadowhunter.png"
}];
var Action = /*#__PURE__*/function () {
function Action(_ref) {
var type = _ref.type,
start = _ref.start,
valid = _ref.valid,
duration = _ref.duration,
meta = _ref.meta;
_classCallCheck(this, Action);
this._id = Action._id++;
this.type = type;
this.start = start;
this.valid = valid;
this.duration = duration;
this.meta = meta;
}
_createClass(Action, null, [{
key: "build",
value: function build(start, buildingData, worker) {
return new Action({
type: "build",
start: start,
duration: buildingData.buildTime,
valid: true,
meta: {
worker: worker,
building: buildingData
}
});
}
}, {
key: "buy",
value: function buy(start, itemData, building) {
return new Action({
type: "buy",
start: start,
duration: 0,
valid: true,
meta: {
building: building,
item: itemData
}
});
}
}, {
key: "train",
value: function train(start, unitData, building) {
return new Action({
type: "train",
start: start,
duration: unitData.buildTime || 0,
valid: true,
meta: {
unit: unitData,
building: building
}
});
}
}, {
key: "upgrade",
value: function upgrade(start, upgradeData, building) {
return new Action({
type: "upgrade",
start: start,
duration: upgradeData.buildTime,
valid: true,
meta: {
building: building,
upgrade: upgradeData
}
});
}
}, {
key: "assignToGold",
value: function assignToGold(start, worker) {
return new Action({
type: "assignToGold",
start: start,
duration: 0,
valid: true,
meta: {
worker: worker
}
});
}
}, {
key: "removeFromGold",
value: function removeFromGold(start, worker) {
return new Action({
type: "removeFromGold",
start: start,
duration: 0,
valid: true,
meta: {
worker: worker
}
});
}
}, {
key: "assignToLumber",
value: function assignToLumber(start, worker) {
return new Action({
type: "assignToLumber",
start: start,
duration: 0,
valid: true,
meta: {
worker: worker
}
});
}
}, {
key: "removeFromLumber",
value: function removeFromLumber(start, worker) {
return new Action({
type: "removeFromLumber",
start: start,
duration: 0,
valid: true,
meta: {
worker: worker
}
});
}
}]);
return Action;
}();
_defineProperty(Action, "_id", 1);
var Building = /*#__PURE__*/function () {
function Building(_ref) {
var id = _ref.id,
name = _ref.name,
gold = _ref.gold,
lumber = _ref.lumber,
buildTime = _ref.buildTime,
dependsOn = _ref.dependsOn,
supply = _ref.supply,
type = _ref.type,
units = _ref.units,
upgrades = _ref.upgrades,
items = _ref.items,
image = _ref.image;
_classCallCheck(this, Building);
this._id = Building._id++;
this.id = id;
this.name = name;
this.gold = gold;
this.lumber = lumber;
this.buildTime = buildTime;
this.dependsOn = dependsOn;
this.units = units;
this.upgrades = upgrades;
this.items = items;
this.supply = supply;
this.image = image;
this.type = type;
}
_createClass(Building, null, [{
key: "resetIds",
value: function resetIds() {
Building._id = 1;
}
}]);
return Building;
}();
_defineProperty(Building, "_id", 1);
var Item = /*#__PURE__*/function () {
function Item(_ref) {
var id = _ref.id,
name = _ref.name,
gold = _ref.gold,
lumber = _ref.lumber,
dependsOn = _ref.dependsOn,
order = _ref.order,
type = _ref.type,
image = _ref.image;
_classCallCheck(this, Item);
this._id = Item._id++;
this.id = id;
this.name = name;
this.gold = gold;
this.order = order, this.lumber = lumber;
this.dependsOn = dependsOn;
this.image = image;
this.type = type;
}
_createClass(Item, null, [{
key: "resetIds",
value: function resetIds() {
Item._id = 1;
}
}]);
return Item;
}();
_defineProperty(Item, "_id", 1);
var ResourceList = /*#__PURE__*/function () {
function ResourceList() {
var value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
_classCallCheck(this, ResourceList);
this.value = value;
}
_createClass(ResourceList, [{
key: "addValues",
value: function addValues(resources) {
var resourceList = resources;
if (!(resourceList instanceof ResourceList)) {
resourceList = new ResourceList(resourceList);
}
for (var _i = 0, _Object$entries = Object.entries(resourceList.value); _i < _Object$entries.length; _i++) {
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
key = _Object$entries$_i[0],
value = _Object$entries$_i[1];
if (key in this.value) {
if (Array.isArray(this.value[key])) {
if (Array.isArray(value)) {
this.value[key] = this.value[key].concat(value);
} else {
this.value[key].push(value);
}
} else {
this.value[key] += value;
}
} else {
this.value[key] = value;
}
}
}
}, {
key: "removeValues",
value: function removeValues(resources) {
var _this = this;
var resourceList = resources;
if (!(resourceList instanceof ResourceList)) {
resourceList = new ResourceList(resourceList);
}
var _loop = function _loop() {
var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i2], 2),
key = _Object$entries2$_i[0],
value = _Object$entries2$_i[1];
if (key in _this.value) {
if (Array.isArray(_this.value[key])) {
if (Array.isArray(value)) {
var _iterator = _createForOfIteratorHelper(value),
_step;
try {
var _loop2 = function _loop2() {
var v = _step.value;
var index = _this.value[key].findIndex(function (i) {
return i._id === v._id;
});
_this.value[key].splice(index, 1);
};
for (_iterator.s(); !(_step = _iterator.n()).done;) {
_loop2();
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
} else {
var index = _this.value[key].findIndex(function (i) {
return i._id === value._id;
});
_this.value[key].splice(index, 1);
}
} else {
_this.value[key] -= value;
}
} else {
throw new Error("Unable to lose resource, none accumulated.");
}
};
for (var _i2 = 0, _Object$entries2 = Object.entries(resourceList.value); _i2 < _Object$entries2.length; _i2++) {
_loop();
}
}
}]);
return ResourceList;
}();
var ResourceEvent = /*#__PURE__*/function () {
function ResourceEvent(tick, actionId) {
_classCallCheck(this, ResourceEvent);
_defineProperty(this, "resourceLoss", new ResourceList());
_defineProperty(this, "resourceGain", new ResourceList());
this.actionId = actionId;
this.tick = tick;
}
_createClass(ResourceEvent, [{
key: "loseResources",
value: function loseResources(resourceList) {
var resources = resourceList;
if (!(resourceList instanceof ResourceList)) {
resources = new ResourceList(resources);
}
this.resourceLoss.addValues(resourceList);
}
}, {
key: "gainResources",
value: function gainResources(resourceList) {
var resources = resourceList;
if (!(resourceList instanceof ResourceList)) {
resources = new ResourceList(resources);
}
this.resourceGain.addValues(resourceList);
}
}]);
return ResourceEvent;
}();
var Unit = /*#__PURE__*/function () {
function Unit(_ref) {
var id = _ref.id,
buildTime = _ref.buildTime,
_ref$canBuild = _ref.canBuild,
canBuild = _ref$canBuild === void 0 ? false : _ref$canBuild,
_ref$canHarvest = _ref.canHarvest,
canHarvest = _ref$canHarvest === void 0 ? false : _ref$canHarvest,
_ref$canMine = _ref.canMine,
canMine = _ref$canMine === void 0 ? false : _ref$canMine,
image = _ref.image,
_ref$isHero = _ref.isHero,
isHero = _ref$isHero === void 0 ? false : _ref$isHero,
order = _ref.order,
gold = _ref.gold,
lumber = _ref.lumber,
name = _ref.name,
supply = _ref.supply,
type = _ref.type;
_classCallCheck(this, Unit);
this._id = Unit._id++;
this.id = id;
this.buildTime = buildTime;
this.canBuild = canBuild;
this.canHarvest = canHarvest;
this.canMine = canMine;
this.gold = gold;
this.isHero = isHero;
this.order = order;
this.image = image;
this.lumber = lumber;
this.name = name;
this.supply = supply;
this.type = type;
}
_createClass(Unit, null, [{
key: "resetIds",
value: function resetIds() {
Unit._id = 1;
}
}]);
return Unit;
}();
_defineProperty(Unit, "_id", 1);
var Upgrade = function Upgrade(_ref) {
var id = _ref.id,
name = _ref.name,
gold = _ref.gold,
lumber = _ref.lumber,
buildTime = _ref.buildTime,
tier = _ref.tier,
replaces = _ref.replaces,
dependsOn = _ref.dependsOn,
image = _ref.image;
_classCallCheck(this, Upgrade);
this.id = id;
this.name = name;
this.gold = gold;
this.lumber = lumber;
this.buildTime = buildTime;
this.tier = tier;
this.replaces = replaces;
this.dependsOn = dependsOn;
this.image = image;
};
var EventsProcessor = /*#__PURE__*/function () {
function EventsProcessor() {
_classCallCheck(this, EventsProcessor);
}
_createClass(EventsProcessor, [{
key: "validateActions",
value: function validateActions(actions, resourceList) {
var accumulatedActions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
var processedActions = [];
var _iterator = _createForOfIteratorHelper(actions),
_step;
try {
var _loop = function _loop() {
var action = _step.value;
action.valid = true;
action.error = null;
switch (action.type) {
case "build":
// Check for sufficient resources
if (resourceList.value.gold < action.meta.building.gold || resourceList.value.lumber < action.meta.building.lumber) {
action.valid = false;
action.error = "not enough resources";
} // Check if builder still exists
if (!resourceList.value.units.find(function (u) {
return u._id === action.meta.worker._id;
})) {
action.valid = false;
action.error = "worker no longer exists";
}
break;
case "buy":
// Check for sufficient resources
if (resourceList.value.gold < action.meta.item.gold || resourceList.value.lumber < action.meta.item.lumber) {
action.valid = false;
action.error = "not enough resources";
} // Check if shop still exists
if (!resourceList.value.buildings.find(function (b) {
return b._id === action.meta.building._id;
})) {
action.valid = false;
action.error = "shop no longer exists";
} // Check that we have the dependencies
if (typeof action.meta.item.dependsOn !== "undefined" && !action.meta.item.dependsOn.every(function (dependency) {
return resourceList.value.upgrades.some(function (completed) {
return completed.id === dependency;
});
})) {
action.valid = false;
action.error = "missing required upgrade";
} // Check that we haven't exceeded replenishment
if ([].concat(_toConsumableArray(accumulatedActions), processedActions).filter(function (a) {
return a.type === "buy" && a.meta.item.id === action.meta.item.id && action.start - a.start < action.meta.item.replenish;
}).length >= action.meta.item.max) {
action.valid = false;
action.error = "exceeded item replenishment";
}
break;
case "train":
if (resourceList.value.gold < action.meta.unit.gold || resourceList.value.lumber < action.meta.unit.lumber || resourceList.value.supply < action.meta.unit.supply) {
action.valid = false;
action.error = "not enough resources";
}
if (action.meta.unit.type !== "neutralUnit" && !resourceList.value.buildings.find(function (b) {
return b._id === action.meta.building._id && b.id === action.meta.building.id;
})) {
action.valid = false;
action.error = "building no longer available for training";
}
if (action.meta.unit.type !== "neutralUnit" && processedActions.filter(function (a) {
return a.type === "train" && a.meta.building._id === action.meta.building._id && a.start + a.duration >= action.start && a.start + a.duration <= action.start + action.duration;
}).length) {
action.valid = false;
action.error = "building is busy training";
}
if (action.meta.unit.limit && action.meta.unit.limit <= processedActions.filter(function (a) {
return a.type === "train" && a.meta.unit.id === action.meta.unit.id;
}).length) {
action.valid = false;
action.error = "too many of this unit produced";
} // Check that we haven't exceeded replenishment
if ([].concat(_toConsumableArray(accumulatedActions), processedActions).filter(function (a) {
return a.type === "train" && a.meta.unit.id === action.meta.unit.id && action.start - a.start < action.meta.unit.replenish;
}).length >= action.meta.unit.max) {
action.valid = false;
action.error = "exceeded unit replenishment";
}
if (action.meta.unit.availability && action.meta.unit.limit < a.start) {
action.valid = false;
action.error = "unit being produced before available";
}
break;
case "assignToGold":
if (!resourceList.value.units.find(function (u) {
return u._id === action.meta.worker._id && u.id === action.meta.worker.id;
})) {
action.valid = false;
action.error = "worker no longer available for mining";
}
break;
case "removeFromGold":
if (!resourceList.value.miningWorkers.find(function (u) {
return u._id === action.meta.worker._id && u.id === action.meta.worker.id;
})) {
action.valid = false;
action.error = "worker no longer mining at this time";
}
break;
case "assignToLumber":
if (!resourceList.value.units.find(function (u) {
return u._id === action.meta.worker._id && u.id === action.meta.worker.id;
})) {
action.valid = false;
action.error = "worker no longer available for harvesting";
}
break;
case "removeFromLumber":
if (!resourceList.value.harvestingWorkers.find(function (u) {
return u._id === action.meta.worker._id && u.id === action.meta.worker.id;
})) {
action.valid = false;
action.error = "worker no longer harvesting at this time";
}
break;
case "upgrade":
if (resourceList.value.gold < action.meta.upgrade.gold || resourceList.value.lumber < action.meta.upgrade.lumber) {
action.valid = false;
action.error = "not enough resources for upgrade";
}
if (!action.meta.upgrade.dependsOn.buildings.every(function (dependency) {
return resourceList.value.buildings.some(function (completed) {
return completed.id === dependency;
});
}) || !action.meta.upgrade.dependsOn.upgrades.every(function (dependency) {
return resourceList.value.upgrades.some(function (completed) {
return completed.id === dependency;
});
})) {
action.valid = false;
action.error = "missing required building or upgrade";
}
if (!!resourceList.value.upgrades.find(function (u) {
return u.id === action.meta.upgrade.id;
})) {
action.valid = false;
action.error = "upgrade already researched";
}
if (action.meta.upgrade.replaces && !resourceList.value.upgrades.find(function (u) {
return u.id === action.meta.upgrade.replaces;
})) {
action.valid = false;
action.error = "missing earlier version of upgrade";
}
}
processedActions.push(action);
};
for (_iterator.s(); !(_step = _iterator.n()).done;) {
_loop();
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
}
}, {
key: "processAction",
value: function processAction(action) {
var _immediateLoss$loseRe;
var resourceEvents = [];
switch (action.type) {
case "build":
var immediateLoss = new ResourceEvent(action.start, action._id);
immediateLoss.loseResources({
gold: action.meta.building.gold,
lumber: action.meta.building.lumber,
units: [action.meta.worker]
});
var futureGain = new ResourceEvent(action.start + action.duration, action._id);
futureGain.gainResources({
supply: action.meta.building.supply,
buildings: [new Building(action.meta.building)],
units: [action.meta.worker]
});
resourceEvents.push(immediateLoss, futureGain);
break;
case "buy":
var immediateLoss = new ResourceEvent(action.start, action._id);
immediateLoss.loseResources({
gold: action.meta.item.gold,
lumber: action.meta.item.lumber || 0 // Neutral items do not cost lumber
});
var immediateGain = new ResourceEvent(action.start + action.duration, action._id);
immediateGain.gainResources({
items: [new Item(action.meta.item)]
});
resourceEvents.push(immediateLoss, immediateGain);
break;
case "train":
var immediateLoss = new ResourceEvent(action.start, action._id);
immediateLoss.loseResources((_immediateLoss$loseRe = {
supply: action.meta.unit.supply,
gold: action.meta.unit.gold,
lumber: action.meta.unit.lumber
}, _defineProperty(_immediateLoss$loseRe, "supply", action.meta.unit.supply), _defineProperty(_immediateLoss$loseRe, "buildings", [action.meta.building]), _immediateLoss$loseRe));
var futureGain = new ResourceEvent(action.start + action.duration, action._id);
futureGain.gainResources({
units: [new Unit(action.meta.unit)],
buildings: [action.meta.building]
});
resourceEvents.push(immediateLoss, futureGain);
break;
case "assignToGold":
var immediateLoss = new ResourceEvent(action.start, action._id);
immediateLoss.loseResources({
units: [action.meta.worker]
});
var immediateGain = new ResourceEvent(action.start, action._id);
immediateGain.gainResources({
miningWorkers: [action.meta.worker]
});
resourceEvents.push(immediateGain, immediateLoss);
break;
case "removeFromGold":
var immediateLoss = new ResourceEvent(action.start, action._id);
immediateLoss.loseResources({
miningWorkers: [action.meta.worker]
});
var immediateGain = new ResourceEvent(action.start, action._id);
immediateGain.gainResources({
units: [action.meta.worker]
});
resourceEvents.push(immediateGain, immediateLoss);
break;
case "assignToLumber":
var immediateLoss = new ResourceEvent(action.start, action._id);
immediateLoss.loseResources({
units: [action.meta.worker]
});
var immediateGain = new ResourceEvent(action.start, action._id);
immediateGain.gainResources({
harvestingWorkers: [action.meta.worker]
});
resourceEvents.push(immediateGain, immediateLoss);
break;
case "removeFromLumber":
var immediateLoss = new ResourceEvent(action.start, action._id);
immediateLoss.loseResources({
harvestingWorkers: [action.meta.worker]
});
var immediateGain = new ResourceEvent(action.start, action._id);
immediateGain.gainResources({
units: [action.meta.worker]
});
resourceEvents.push(immediateGain, immediateLoss);
break;
case "upgrade":
var immediateLoss = new ResourceEvent(action.start, action._id);
immediateLoss.loseResources({
buildings: [action.meta.building],
gold: action.meta.upgrade.gold,
lumber: action.meta.upgrade.lumber
});
var futureGain = new ResourceEvent(action.start + action.duration, action._id);
futureGain.gainResources({
upgrades: [new Upgrade(action.meta.upgrade)],
buildings: [action.meta.building]
});
resourceEvents.push(immediateLoss, futureGain);
break;
}
return resourceEvents;
}
}]);
return EventsProcessor;
}();
var TickEvents = /*#__PURE__*/function () {
function TickEvents(tick) {
_classCallCheck(this, TickEvents);
_defineProperty(this, "actions", []);
this.tick = tick;
}
_createClass(TickEvents, [{
key: "addAction",
value: function addAction(action) {
this.actions.push(action);
}
}, {
key: "removeAction",
value: function removeAction(id) {
var index = this.actions.findIndex(function (a) {
return a._id === id;
});
if (index >= 0) {
this.actions.splice(index, 1);
}
}
}]);
return TickEvents;
}();
var EventRegister = /*#__PURE__*/function () {
function EventRegister(startingResources) {
_classCallCheck(this, EventRegister);
_defineProperty(this, "eventsProcessor", new EventsProcessor());
_defineProperty(this, "eventsIndices", []);
_defineProperty(this, "events", {});
this.startingResources = startingResources;
this.resetEvents();
}
_createClass(EventRegister, [{
key: "eventsAt",
value: function eventsAt(tick) {
var indexLocation = this.eventsIndices.indexOf(tick);
if (indexLocation < 0) {
this.events[tick] = new TickEvents(tick);
this.eventsIndices.push(tick);
this.eventsIndices.sort(function (a, b) {
return a - b;
});
}
return this.events[tick];
}
}, {
key: "resourceEventsAt",
value: function resourceEventsAt(tick) {
var indexLocation = this.resourceEventsIndices.indexOf(tick);
if (indexLocation < 0) {
this.resourceEvents[tick] = [];
this.resourceEventsIndices.push(tick);
this.resourceEventsIndices.sort(function (a, b) {
return a - b;
}); // Make an event so we stop for resource calculation
this.eventsAt(tick);
}
return this.resourceEvents[tick];
}
}, {
key: "resetEvents",
value: function resetEvents() {
this.resourceEventsIndices = [];
this.resourceEvents = {};
var toRemove = [];
var _iterator = _createForOfIteratorHelper(this.eventsIndices),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var index = _step.value;
if (!this.events[index].actions.length) {
toRemove.push(index);
}
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
this.eventsIndices = this.eventsIndices.filter(function (ei) {
return toRemove.indexOf(ei) === -1;
});
for (var _i = 0, _toRemove = toRemove; _i < _toRemove.length; _i++) {
var removeIndex = _toRemove[_i];
delete this.resourceEvents[removeIndex];
}
var initialResourceEvent = new ResourceEvent(0, null);
var startingGain = clonedeep(this.startingResources.value);
Building.resetIds();
Item.resetIds();
Unit.resetIds();
startingGain.units.map(function (u) {
return new Unit(u);
});
startingGain.buildings.map(function (u) {
return new Building(u);
});
initialResourceEvent.gainResources(startingGain);
initialResourceEvent.loseResources({
supply: 5
});
this.addResourceEvent(initialResourceEvent);
}
}, {
key: "forEachEvent",
value: function forEachEvent(cb) {
for (var i = 0; i < this.eventsIndices.length; i++) {
var index = this.eventsIndices[i];
cb(this.events[index], index);
}
}
}, {
key: "addAction",
value: function addAction(action) {
this.eventsAt(action.start).addAction(action);
}
}, {
key: "removeAction",
value: function removeAction(tick, id) {
this.eventsAt(tick).removeAction(id);
}
}, {
key: "addResourceEvent",
value: function addResourceEvent(resourceEvent) {
this.resourceEventsAt(resourceEvent.tick).push(resourceEvent);
}
}]);
return EventRegister;
}();
var ActionManager = /*#__PURE__*/function () {
function ActionManager(race, resourcesManager) {
_classCallCheck(this, ActionManager);
_defineProperty(this, "eventsProcessor", new EventsProcessor());
this.eventsRegister = new EventRegister(race.startingResources);
this.resourcesManager = resourcesManager;
this.processEvents();
}
_createClass(ActionManager, [{
key: "addAction",
value: function addAction(action) {
// Add the action event to the registry
this.eventsRegister.addAction(action); // Reset our accumulated resources
this.resourcesManager.reset(); // Reset previous resource events
this.eventsRegister.resetEvents();
this.processEvents();
}
}, {
key: "processEvents",
value: function processEvents() {
var _this = this;
var accumulatedActions = [];
this.eventsRegister.forEachEvent(function (event, index) {
// Sum up resource events added by previous actions.
var gains = _this.eventsRegister.resourceEventsAt(event.tick).flatMap(function (re) {
return re.resourceGain;
});
var losses = _this.eventsRegister.resourceEventsAt(event.tick).flatMap(function (re) {
return re.resourceLoss;
}); // Get starting resources
var resourcesForTick = new ResourceList(_this.resourcesManager.resourcesAt(event.tick)); // Add in all the new gains
var _iterator = _createForOfIteratorHelper(gains),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var gain = _step.value;
resourcesForTick.addValues(gain);
} // Subtract any losses
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
var _iterator2 = _createForOfIteratorHelper(losses),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var loss = _step2.value;
resourcesForTick.removeValues(loss);
} // Set validity of existing actions
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
_this.eventsProcessor.validateActions(event.actions, resourcesForTick, accumulatedActions); // Process actions to get new resource events.
var _iterator3 = _createForOfIteratorHelper(event.actions),
_step3;
try {
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
var eventAction = _step3.value;
if (eventAction.valid) {
var resourceEvents = _this.eventsProcessor.processAction(eventAction);
var _iterator4 = _createForOfIteratorHelper(resourceEvents),
_step4;
try {
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
var re = _step4.value;
if (re.tick === event.tick) {
resourcesForTick.addValues(re.resourceGain);
resourcesForTick.removeValues(re.resourceLoss);
} else {
_this.eventsRegister.addResourceEvent(re);
}
}
} catch (err) {
_iterator4.e(err);
} finally {
_iterator4.f();
}
}
} // Save the resources value for this tick
} catch (err) {
_iterator3.e(err);
} finally {
_iterator3.f();
}
_this.resourcesManager.setResources(event.tick, resourcesForTick); // Accumulate past actions for next event
accumulatedActions = accumulatedActions.concat(event.actions);
});
}
}, {
key: "removeAction",
value: function removeAction(action) {
// Remove the action from the registery
this.eventsRegister.removeAction(action.start, action._id); // Reset our accumulated resources
this.resourcesManager.reset(); // Reset previous resource events
this.eventsRegister.resetEvents();
this.processEvents();
}
}, {
key: "build",
value: function build(tick, buildingData, worker) {
this.addAction(Action.build(tick, buildingData, worker));
}
}, {
key: "buy",
value: function buy(tick, itemData, building) {
this.addAction(Action.buy(tick, itemData, building));
}
}, {
key: "train",
value: function train(tick, unitData, building) {
this.addAction(Action.train(tick, unitData, building));
}
}, {
key: "upgrade",
value: function upgrade(tick, upgradeData, building) {
this.addAction(Action.upgrade(tick, upgradeData, building));
}
}, {
key: "assignToGold",
value: function assignToGold(tick, worker) {
this.addAction(Action.assignToGold(tick, worker));
}
}, {
key: "removeFromGold",
value: function removeFromGold(tick, miner) {
this.addAction(Action.removeFromGold(tick, miner));
}
}, {
key: "assignToLumber",
value: function assignToLumber(tick, worker) {
this.addAction(Action.assignToLumber(tick, worker));
}
}, {
key: "removeFromLumber",
value: function removeFromLumber(tick, harvester) {
this.addAction(Action.removeFromLumber(tick, harvester));
}
}, {
key: "getActions",
value: function getActions() {
var actions = [];
this.eventsRegister.forEachEvent(function (e) {
return actions.push.apply(actions, _toConsumableArray(e.actions));
});
return actions;
}
}]);
return ActionManager;
}();
var neutralBuildings = [{
id: "goblinMerchant",
name: "Goblin Merchant",
type: "building",
units: [],
items: ["circletOfNobility", "periaptOfVitality", "bootsOfSpeed", "dustOfAppearance", "scrollOfHealing", "scrollOfProtection", "scrollOfTownPortalNeutral", "potionOfInvisibility", "tomeOfRetraining", "staffOfTeleportation", "potionOfLesserInvulnerability"],
image: "neutral/goblinmerchant.png"
}, {
id: "goblinLaboratory",
name: "Goblin Laboratory",
type: "building",
units: ["goblinSapper", "goblinZeppelin", "goblinShredder"],
items: [],
image: "neutral/goblinlaboratory.png"
}, {
id: "tavern",
name: "Tavern",
type: "building",
units: ["nagaSeaWitch", "darkRanger", "pandarenBrewmaster", "firelord", "beastmaster", "pitLord", "goblinTinker", "goblinAlchemist"],
items: [],
image: "neutral/tavern.png"
}, {
id: "mercenaryCamp",
name: "Mercenary Camp",
type: "building",
units: ["forestTrollShadowPriest", "forestTrollBerserker", "mudGolem", "ogreMauler"],
items: [],
image: "neutral/mercenarycamp.png"
}];
var neutralUnits = [{
id: "goblinSapper",
name: "Goblin Sapper",
type: "neutralUnit",
isHero: false,
gold: 215,
lumber: 100,
supply: 2,
order: 1,
max: 3,
replenish: 45,
availability: 440,
image: "neutral/goblinsapper.gif"
}, {
id: "goblinZeppelin",
name: "Goblin Zeppelin",
type: "neutralUnit",
isHero: false,
gold: 240,
lumber: 60,
supply: 0,
order: 2,
max: 1,
replenish: 30,
availability: 440,
image: "neutral/goblinzeppelin.gif"
}, {
id: "goblinShredder",
name: "Goblin Shredder",
type: "neutralUnit",
isHero: false,
gold: 375,
lumber: 100,
supply: 4,
order: 3,
max: 3,
replenish: 80,
availability: 300,
image: "neutral/goblinshredder.gif"
}, {
id: "nagaSeaWitch",
name: "Naga Sea Witch",
type: "neutralUnit",
isHero: true,
gold: 375,
lumber: 135,
supply: 5,
order: 1,
max: 1,
replenish: 0,
availability: 135,
image: "neutral/nagaseawitch.gif"
}, {
id: "darkRanger",
name: "Dark Ranger",
type: "neutralUnit",
isHero: true,
gold: 425,
lumber: 135,
supply: 5,
order: 2,
max: 1,
replenish: 0,
availability: 135,
image: "neutral/darkranger.gif"
}, {
id: "pandarenBrewmaster",
name: "Pandaren Brewmaster",
type: "neutralUnit",
isHero: true,
gold: 425,
lumber: 135,
supply: 5,
order: 3,
max: 1,
replenish: 0,
availability: 135,
image: "neutral/pandarenbrewmaster.gif"
}, {
id: "firelord",
name: "Firelord",
type: "neutralUnit",
isHero: true,
gold: 425,
lumber: 135,
supply: 5,
order: 4,
max: 1,
availability: 135,
image: "neutral/firelord.jpg"
}, {
id: "beastmaster",
name: "Beastmaster",
type: "neutralUnit",
isHero: true,
gold: 425,
lumber: 135,
supply: 5,
order: 6,
max: 1,
availability: 135,
image: "neutral/beastmaster.gif"
}, {
id: "pitLord",
name: "Pit Lord",
type: "neutralUnit",
isHero: true,
gold: 425,
lumber: 135,
supply: 5,
order: 5,
max: 1,
availability: 135,
image: "neutral/pitlord.gif"
}, {
id: "goblinTinker",
name: "Goblin Tinker",
type: "neutralUnit",
isHero: true,
gold: 425,
lumber: 135,
supply: 5,
order: 7,
max: 1,
availability: 135,
image: "neutral/goblintinker.gif"
}, {
id: "goblinAlchemist",
name: "Goblin Alchemist",
type: "neutralUnit",
isHero: true,
gold: 425,
lumber: 135,
supply: 5,
order: 8,
max: 1,
availability: 135,
image: "neutral/goblinalchemist.gif"
}, {
id: "forestTrollShadowPriest",
name: "Forest Troll Shadow Priest",
type: "neutralUnit",
isHero: false,
gold: 195,
lumber: 10,
supply: 2,
order: 1,
replenish: 110,
max: 1,
availability: 120,
image: "neutral/foresttrollshadowpriest.gif"
}, {
id: "forestTrollBerserker",
name: "Forest Troll Berserker",
type: "neutralUnit",
isHero: false,
gold: 245,
lumber: 30,
supply: 3,
order: 2,
replenish: 210,
max: 1,
availability: 220,
image: "neutral/foresttrol