UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

269 lines (201 loc) • 6.53 kB
import { assert } from "../../../../core/assert.js"; import { BitSet } from "../../../../core/binary/BitSet.js"; import { BehaviorStatus } from "../BehaviorStatus.js"; import { CompositeBehavior } from "./CompositeBehavior.js"; /** * * @enum {number} */ export const ParallelBehaviorPolicy = { RequireOne: 0, RequireAll: 1 }; /** * Executes all contained behaviors in parallel. * * @example * ParallelBehavior.from([ * ActionBehavior.from(()=> console.log("A")), * ActionBehavior.from(()=> console.log("B")), * ActionBehavior.from(()=> console.log("C")), * ]); // will print A, B and C in console in a single tick * * @author Alex Goldring * @copyright Company Named Limited (c) 2025 */ export class ParallelBehavior extends CompositeBehavior { /** * * @param {ParallelBehaviorPolicy} successPolicy * @param {ParallelBehaviorPolicy} failurePolicy */ constructor(successPolicy, failurePolicy) { super(); assert.enum(successPolicy, ParallelBehaviorPolicy, 'successPolicy'); assert.enum(failurePolicy, ParallelBehaviorPolicy, 'failurePolicy'); /** * @private * @type {ParallelBehaviorPolicy} */ this.successPolicy = successPolicy; /** * @private * @type {ParallelBehaviorPolicy} */ this.failurePolicy = failurePolicy; /** * Which child behaviors are currently running? * @readonly * @private * @type {BitSet} */ this.activeSet = new BitSet(); /** * @private * @type {number} */ this.successCount = 0; /** * @private * @type {number} */ this.failureCount = 0; } /** * * @return {ParallelBehaviorPolicy} */ getSuccessPolicy() { return this.successPolicy; } /** * * @param {ParallelBehaviorPolicy|number} policy */ setSuccessPolicy(policy) { assert.enum(policy, ParallelBehaviorPolicy, 'policy'); this.successPolicy = policy; } /** * * @return {ParallelBehaviorPolicy} */ getFailurePolicy() { return this.failurePolicy; } /** * * @param {ParallelBehaviorPolicy|number} policy */ setFailurePolicy(policy) { assert.enum(policy, ParallelBehaviorPolicy, 'policy'); this.failurePolicy = policy; } /** * * @param {number} timeDelta * @returns {BehaviorStatus|number} */ tick(timeDelta) { const activeSet = this.activeSet; /** * * @type {Behavior[]} */ const children = this.__children; const numChildren = children.length; let i; for (i = 0; i < numChildren; i++) { if (!activeSet.get(i)) { continue; } const child = children[i]; const status = child.tick(timeDelta); if (status === BehaviorStatus.Succeeded) { activeSet.set(i, false); this.successCount++; child.finalize(); if (this.successPolicy === ParallelBehaviorPolicy.RequireOne) { this.__finalizeActiveChildren(); return BehaviorStatus.Succeeded; } } else if (status === BehaviorStatus.Failed) { activeSet.set(i, false); this.failureCount++; child.finalize(); if (this.failurePolicy === ParallelBehaviorPolicy.RequireOne) { this.__finalizeActiveChildren(); return BehaviorStatus.Failed; } else if (this.successPolicy === ParallelBehaviorPolicy.RequireAll) { this.__finalizeActiveChildren(); return BehaviorStatus.Failed; } } } if (this.successCount === numChildren && this.successPolicy === ParallelBehaviorPolicy.RequireAll) { return BehaviorStatus.Succeeded; } else if (this.failureCount === numChildren && this.failurePolicy === ParallelBehaviorPolicy.RequireAll) { return BehaviorStatus.Failed; } else if ((this.failureCount + this.successCount) === numChildren) { // this should never happen, we should fail before this return BehaviorStatus.Failed; } else { return BehaviorStatus.Running; } } initialize(context) { this.successCount = 0; this.failureCount = 0; const children = this.__children; const numChildren = children.length; for (let i = 0; i < numChildren; i++) { const behavior = children[i]; behavior.initialize(context); this.activeSet.set(i, true); } super.initialize(context); } /** * * @private */ __finalizeActiveChildren() { const children = this.__children; const activeSet = this.activeSet; for (let i = activeSet.nextSetBit(0); i !== -1; i = activeSet.nextSetBit(i + 1)) { const behavior = children[i]; behavior.finalize(); } } finalize() { //finalize remaining active behaviours this.__finalizeActiveChildren(); super.finalize(); } /** * Default policies are fine for most use cases. * @param {Behavior[]} children * @param {ParallelBehaviorPolicy} [success] how should successful completion be determined? * @param {ParallelBehaviorPolicy} [failure] how should failing completion be determined * @returns {ParallelBehavior} */ static from( children, success = ParallelBehaviorPolicy.RequireAll, failure = ParallelBehaviorPolicy.RequireOne ) { const r = new ParallelBehavior(success, failure); r.addChildren(children); return r; } } /** * @readonly * @type {boolean} */ ParallelBehavior.prototype.isParallelBehavior = true; /** * @readonly * @type {string} */ ParallelBehavior.typeName = "ParallelBehavior";