UNPKG

@gamepark/rules-api

Version:

API to implement the rules of a board game

311 lines 13.9 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MaterialMutator = void 0; var isEqual_1 = __importDefault(require("lodash/isEqual")); var merge_1 = __importDefault(require("lodash/merge")); var location_1 = require("../location"); var moves_1 = require("../moves"); var index_1 = require("./index"); /** * Helper class to change the state of any {@link MaterialItem} in a game implemented with {@link MaterialRules}. * * @typeparam P - identifier of a player. Either a number or a numeric enum (eg: PlayerColor) * @typeparam M - Numeric enum of the types of material manipulated in the game * @typeparam L - Numeric enum of the types of location in the game where the material can be located */ var MaterialMutator = /** @class */ (function () { /** * @param type Type of items this mutator will work on * @param items Items to work with * @param locationsStrategies The strategies that these items must follow * @param canMerge Whether to items at the exact same location can merge into one item with a quantity * @param rulesClassName Constructor name of the main rules class for logging */ function MaterialMutator(type, items, locationsStrategies, canMerge, rulesClassName) { if (locationsStrategies === void 0) { locationsStrategies = {}; } if (canMerge === void 0) { canMerge = true; } if (rulesClassName === void 0) { rulesClassName = ''; } this.type = type; this.items = items; this.locationsStrategies = locationsStrategies; this.canMerge = canMerge; this.rulesClassName = rulesClassName; } /** * Executes a move on the game items * @param move */ MaterialMutator.prototype.applyMove = function (move) { switch (move.type) { case moves_1.ItemMoveType.Create: this.create(move); break; case moves_1.ItemMoveType.CreateAtOnce: for (var _i = 0, _a = move.items; _i < _a.length; _i++) { var item = _a[_i]; this.create(__assign(__assign({}, move), { type: moves_1.ItemMoveType.Create, item: item })); } break; case moves_1.ItemMoveType.Move: this.move(move); break; case moves_1.ItemMoveType.MoveAtOnce: this.moveItemsAtOnce(move); break; case moves_1.ItemMoveType.Roll: this.roll(move); break; case moves_1.ItemMoveType.Delete: this.delete(move); break; case moves_1.ItemMoveType.DeleteAtOnce: for (var _b = 0, _c = move.indexes; _b < _c.length; _b++) { var index = _c[_b]; this.removeItem(this.items[index], Infinity); } break; case moves_1.ItemMoveType.Shuffle: this.shuffle(move); break; case moves_1.ItemMoveType.Select: this.select(move); break; } }; /** * Find the index of an existing item we could merge a new item with (create a single item with a quantity) * * @param newItem An item to compare with existing items * @returns {number} Index of the existing item we can merge with, or -1 if there is no possible merge */ MaterialMutator.prototype.findMergeIndex = function (newItem) { if (!this.canMerge) return -1; var q1 = newItem.quantity, data1 = __rest(newItem, ["quantity"]); return this.items.findIndex(function (_a) { var q2 = _a.quantity, data2 = __rest(_a, ["quantity"]); return q1 !== 0 && q2 !== 0 && (0, isEqual_1.default)(data1, data2); }); }; MaterialMutator.prototype.addItem = function (item) { this.applyAddItemStrategy(item); var availableIndex = this.items.findIndex(function (item) { return item.quantity === 0; }); if (availableIndex !== -1) { this.items[availableIndex] = item; } else { this.items.push(item); } }; /** * Provides the index that the new item will have * @param newItem An item that is going to be created * @returns {number} the future index of that item */ MaterialMutator.prototype.getItemCreationIndex = function (newItem) { var mergeIndex = this.findMergeIndex(newItem); if (mergeIndex !== -1) return mergeIndex; var availableIndex = this.items.findIndex(function (item) { return item.quantity === 0; }); if (availableIndex !== -1) return availableIndex; return this.items.length; }; MaterialMutator.prototype.applyAddItemStrategy = function (item) { if (item.location.type in this.locationsStrategies) { var strategy = this.locationsStrategies[item.location.type]; if (strategy.addItem) { var material = new index_1.Material(this.type, this.items) .location(item.location.type).player(item.location.player).locationId(item.location.id).parent(item.location.parent); strategy.addItem(material, item); } } }; MaterialMutator.prototype.applyMoveItemStrategy = function (item, index) { if (item.location.type in this.locationsStrategies) { var strategy = this.locationsStrategies[item.location.type]; if (strategy.moveItem) { var material = new index_1.Material(this.type, this.items) .location(item.location.type).player(item.location.player).locationId(item.location.id).parent(item.location.parent); strategy.moveItem(material, item, index); } } }; MaterialMutator.prototype.removeItem = function (item, quantity) { var _a; if (quantity === void 0) { quantity = 1; } item.quantity = Math.max(0, ((_a = item.quantity) !== null && _a !== void 0 ? _a : 1) - quantity); if (item.quantity === 0) { this.applyRemoveItemStrategy(item); } }; MaterialMutator.prototype.applyRemoveItemStrategy = function (item) { if (item.location.type in this.locationsStrategies) { var strategy = this.locationsStrategies[item.location.type]; if (strategy.removeItem) { var material = new index_1.Material(this.type, this.items) .location(item.location.type).player(item.location.player).locationId(item.location.id).parent(item.location.parent); strategy.removeItem(material, item); } } }; MaterialMutator.prototype.create = function (move) { var _a, _b; var mergeIndex = this.findMergeIndex(move.item); if (mergeIndex !== -1) { var mergeItem = this.items[mergeIndex]; mergeItem.quantity = ((_a = mergeItem.quantity) !== null && _a !== void 0 ? _a : 1) + ((_b = move.item.quantity) !== null && _b !== void 0 ? _b : 1); } else { if (move.item.quantity && !this.canMerge) { console.error("".concat(this.rulesClassName, ": do not use quantity on items that cannot merge. Items that can be hidden cannot merge.")); } this.addItem(JSON.parse(JSON.stringify(move.item))); } }; MaterialMutator.prototype.move = function (move) { var _a, _b; var quantity = (_a = move.quantity) !== null && _a !== void 0 ? _a : 1; var sourceItem = this.items[move.itemIndex]; var itemAfterMove = this.getItemAfterMove(move); var mergeIndex = this.findMergeIndex(itemAfterMove); if (mergeIndex !== -1) { if (mergeIndex === move.itemIndex) { console.warn("".concat(this.rulesClassName, ": item is moved to the location he already has - ").concat(JSON.stringify(move))); } else { var mergeItem = this.items[mergeIndex]; mergeItem.quantity = ((_b = mergeItem.quantity) !== null && _b !== void 0 ? _b : 1) + quantity; this.removeItem(sourceItem, quantity); } } else if (sourceItem.quantity && sourceItem.quantity > quantity) { sourceItem.quantity -= quantity; this.addItem(itemAfterMove); } else { this.moveItem(sourceItem, itemAfterMove, move.itemIndex); } }; MaterialMutator.prototype.roll = function (move) { var sourceItem = this.items[move.itemIndex]; var itemAfterMove = __assign(__assign({}, sourceItem), { location: JSON.parse(JSON.stringify(move.location)) }); this.moveItem(sourceItem, itemAfterMove, move.itemIndex); }; MaterialMutator.prototype.moveItem = function (item, newItem, index) { if (!(0, location_1.isSameLocationArea)(newItem.location, item.location)) { this.applyAddItemStrategy(newItem); } else { this.applyMoveItemStrategy(newItem, index); } this.items[index] = newItem; if (!(0, location_1.isSameLocationArea)(newItem.location, item.location)) { this.applyRemoveItemStrategy(item); } }; MaterialMutator.prototype.moveItemsAtOnce = function (move) { for (var _i = 0, _a = move.indexes; _i < _a.length; _i++) { var index = _a[_i]; var sourceItem = this.items[index]; var itemAfterMove = this.getItemAfterMoveAtOnce(move, index); if (!(0, location_1.isSameLocationArea)(itemAfterMove.location, sourceItem.location)) { this.applyAddItemStrategy(itemAfterMove); } else { this.applyMoveItemStrategy(itemAfterMove, index); } this.items[index] = itemAfterMove; if (!(0, location_1.isSameLocationArea)(itemAfterMove.location, sourceItem.location)) { this.applyRemoveItemStrategy(sourceItem); } } }; /** * Provides the state of an item after it is moved * @param move The move that is going to happen * @return {MaterialItem} state of the item after the move is executed */ MaterialMutator.prototype.getItemAfterMove = function (move) { var item = this.getItemWithLocation(move.location, move.itemIndex); if (move.reveal) { (0, merge_1.default)(item, move.reveal); } if (move.quantity) { item.quantity = move.quantity; } else { delete item.quantity; } return item; }; /** * Provides the state of an item after it is moved * @param move The move that is going to happen * @param index Index of the item to consider * @return {MaterialItem} state of the item after the move is executed */ MaterialMutator.prototype.getItemAfterMoveAtOnce = function (move, index) { var item = this.getItemWithLocation(move.location, index); if (move.reveal && move.reveal[index]) { (0, merge_1.default)(item, move.reveal[index]); } return item; }; MaterialMutator.prototype.getItemWithLocation = function (location, index) { var moveLocation = JSON.parse(JSON.stringify(location)); var actualItem = this.items[index]; var newLocation = location.type === undefined ? __assign(__assign({}, actualItem.location), moveLocation) : moveLocation; return __assign(__assign({}, actualItem), { location: newLocation }); }; MaterialMutator.prototype.delete = function (move) { return this.removeItem(this.items[move.itemIndex], move.quantity); }; MaterialMutator.prototype.shuffle = function (move) { var _this = this; if (!(0, moves_1.isShuffleRandomized)(move)) return; // Nothing to do on front-end side for a shuffle. The index swap is only required on the backend. var shuffledItems = move.indexes.map(function (index) { return _this.items[index]; }); move.newIndexes.forEach(function (newIndex, i) { _this.items[newIndex] = __assign(__assign({}, shuffledItems[i]), { location: _this.items[newIndex].location }); }); }; MaterialMutator.prototype.select = function (move) { var _a; var item = this.items[move.itemIndex]; if (move.selected === false) { delete item.selected; } else { item.selected = (_a = move.quantity) !== null && _a !== void 0 ? _a : true; } }; return MaterialMutator; }()); exports.MaterialMutator = MaterialMutator; //# sourceMappingURL=MaterialMutator.js.map