UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

212 lines (156 loc) • 6.25 kB
import { assert } from "../../core/assert.js"; import { ArrayIteratorRandom } from "../../core/collection/array/iterator/ArrayIteratorRandom.js"; import { ArrayIteratorSequential } from "../../core/collection/array/iterator/ArrayIteratorSequential.js"; import { PI_HALF } from "../../core/math/PI_HALF.js"; import { seededRandom } from "../../core/math/random/seededRandom.js"; import TaskGroup from "../../core/process/task/TaskGroup.js"; import { countTask } from "../../core/process/task/util/countTask.js"; import { randomCountTask } from "../../core/process/task/util/randomCountTask.js"; import { RuleSelectionPolicyType } from "./RuleSelectionPolicyType.js"; /** * * @enum {Class<AbstractArrayIterator>} */ const POLICY_ITERATORS = { [RuleSelectionPolicyType.Sequential]: ArrayIteratorSequential, [RuleSelectionPolicyType.Random]: ArrayIteratorRandom }; export class GridActionRuleSet { /** * * @type {GridCellPlacementRule[]} */ elements = []; /** * * @type {RuleSelectionPolicyType|number} */ policy = RuleSelectionPolicyType.Sequential; /** * Rules will be evaluated for each cell, this pattern will be applied within each cell * @type {number[]} */ pattern = [0, 0]; /** * * @type {boolean} */ matchOrigin = true; /** * * @param {GridCellPlacementRule[]} rules * @param {RuleSelectionPolicyType} [policy] * @param {number[]} [pattern] * @param {boolean} [allowEveryRotation] * @returns {GridActionRuleSet} */ static from({ rules, policy = RuleSelectionPolicyType.Sequential, pattern = [0, 0], allowEveryRotation }) { assert.enum(policy, RuleSelectionPolicyType, 'policy'); assert.isArray(rules, 'rules'); assert.isArray(pattern, 'pattern'); const r = new GridActionRuleSet(); rules.forEach(r.add, r); r.policy = policy; r.pattern = pattern; if (allowEveryRotation !== undefined) { console.warn('.allowEveryRotation is deprecated'); } return r; } /** * * @param {GridCellPlacementRule} rule */ add(rule) { this.elements.push(rule); } /** * * @param {GridData} grid * @param {number} seed * @param {number} [resolution=1] Number of sub-samples per single grid cell in each dimension * @returns {Task} */ process(grid, seed, resolution = 1) { assert.isNumber(seed, 'seed'); const width = grid.width; const height = grid.height; const random = seededRandom(seed); /** * @type {Class<AbstractArrayIterator>} */ const RuleIteratorClass = POLICY_ITERATORS[this.policy]; /** * * @type {AbstractArrayIterator<GridCellPlacementRule>} */ const ruleIterator = new RuleIteratorClass(); //initialize rules const tInitializeRules = countTask(0, this.elements.length, i => { const rule = this.elements[i]; rule.initialize(grid, seed); }); /** * * @type {number[]} */ const pattern = this.pattern; const patternSize = pattern.length; const matchOrigin = this.matchOrigin; const sampleCountX = width * resolution; const sampleCountY = height * resolution; const sampleCount = sampleCountX * sampleCountY; const iteratorValue = { done: false, value: undefined }; const tMain = randomCountTask(seed, 0, sampleCount, index => { const sample_y = (index / sampleCountX) | 0; const sample_x = index % sampleCountX; const x = sample_x / resolution; const y = sample_y / resolution; ruleIterator.initialize(this.elements); ruleIterator.next(iteratorValue); while (!iteratorValue.done) { /** * * @type {GridCellPlacementRule} */ const element = iteratorValue.value; const matcher = element.pattern; sampling_loop: for (let sampleIndex = 0; sampleIndex < patternSize;) { const sampleOffsetX = pattern[sampleIndex++]; const sampleOffsetY = pattern[sampleIndex++]; const ROTATION_COUNT = element.allowRotation ? 4 : 1; for (let j = 0; j < ROTATION_COUNT; j++) { const rotation = j * PI_HALF; const sin = Math.sin(rotation); const cos = Math.cos(rotation); const final_sample_offset_x = sampleOffsetX * cos - sampleOffsetY * sin; const final_sample_offset_y = sampleOffsetX * sin + sampleOffsetY * cos; const p_x = final_sample_offset_x + x; const p_y = final_sample_offset_y + y; let match; if (matchOrigin) { match = matcher.match(grid, x, y, rotation); } else { match = matcher.match(grid, p_x, p_y, rotation); } if (!match) { continue; } const roll = random(); const probabilityValue = element.probability.execute(grid, p_x, p_y, rotation); if (probabilityValue > roll) { element.execute(grid, p_x, p_y, rotation); break sampling_loop; } } } ruleIterator.next(iteratorValue); } }); tMain.addDependency(tInitializeRules); return new TaskGroup([tInitializeRules, tMain]); } }