@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
212 lines (156 loc) • 6.25 kB
JavaScript
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]);
}
}