UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

255 lines (202 loc) • 6.05 kB
import { assert } from "../../assert.js"; import LineBuilder from "../../codegen/LineBuilder.js"; import { array_push_if_unique } from "../../collection/array/array_push_if_unique.js"; import Signal from "../../events/signal/Signal.js"; import { objectKeyByValue } from "../../model/object/objectKeyByValue.js"; import ObservedInteger from "../../model/ObservedInteger.js"; import Task from "./Task.js"; import TaskState from "./TaskState.js"; class TaskGroup { /** * * @param {(Task|TaskGroup)[]} subtasks * @param {string} [name] * @constructor */ constructor(subtasks, name = 'Unnamed') { assert.isArray(subtasks, 'subtasks'); assert.isString(name, 'name'); /** * * @type {string} */ this.name = name; /** * * @type {(Task|TaskGroup)[]} */ this.children = subtasks; this.on = { started: new Signal(), completed: new Signal(), failed: new Signal() }; /** * * @type {ObservedInteger} */ this.state = new ObservedInteger(TaskState.INITIAL); } /** * Time in milliseconds that the task has been executing for, suspended time does not count * @returns {number} */ getExecutedCpuTime() { let result = 0; const children = this.children; const n = children.length; for (let i = 0; i < n; i++) { const child = children[i]; const time = child.getExecutedCpuTime(); result += time; } return result; } /** * * @param {Task|TaskGroup} child * @returns {boolean} */ addChild(child) { return array_push_if_unique(this.children, child); } /** * * @param {(Task|TaskGroup)[]} children */ addChildren(children) { const n = children.length; for (let i = 0; i < n; i++) { const child = children[i]; this.addChild(child); } } /** * * @param {Task|TaskGroup} dependency */ addDependency(dependency) { if (dependency.isTaskGroup) { this.addDependencies(dependency.children); } else { const children = this.children; const n = children.length; for (let i = 0; i < n; i++) { const child = children[i]; child.addDependency(dependency); } } } addDependencies(dependencies) { const n = dependencies.length; for (let i = 0; i < n; i++) { const dependency = dependencies[i]; this.addDependency(dependency); } } /** * * @returns {number} */ getEstimatedDuration() { let result = 0; const children = this.children; const n = children.length; for (let i = 0; i < n; i++) { const child = children[i]; const childDuration = child.getEstimatedDuration(); if (!(isNaN(childDuration) || childDuration < 0)) { result += childDuration; } } return result; } /** * Dumps task group tree along with state of each task * @returns {string} */ getVerboseStatusMessage() { const b = new LineBuilder(); /** * * @param {Task|TaskGroup} t */ function addTask(t) { if (t.isTaskGroup) { b.add(`group ['${t.name}']`) b.indent(); for (let i = 0; i < t.children.length; i++) { addTask(t.children[i]); } b.dedent(); } else { b.add(`task ['${t.name}'] ${objectKeyByValue(TaskState, t.state.getValue())}`) } } addTask(this); return b.build(); } computeProgress() { const children = this.children; const numChildren = children.length; let progressSum = 0; let progressTotal = 0; for (let i = 0; i < numChildren; i++) { /** * * @type {Task|TaskGroup} */ const child = children[i]; const estimatedDuration = child.getEstimatedDuration(); if (isNaN(estimatedDuration)) { //Duration is not a number, ignore this child continue; } const childProgress = computeTaskProgress(child); assert.ok(childProgress >= 0 && childProgress <= 1, `Expected progress to be between 0 and 1, instead was '${childProgress}' for child '${child.name}'`); progressSum += childProgress * estimatedDuration; progressTotal += estimatedDuration; } if (progressTotal === 0) { return 0; } else { return progressSum / progressTotal; } } /** * * @param resolve * @param reject */ join(resolve, reject) { Task.join(this, resolve, reject); } /** * * @returns {Promise<unknown>} */ promise() { return new Promise((resolve, reject) => this.join(resolve, reject)); } } /** * @readonly * @type {boolean} */ TaskGroup.prototype.isTaskGroup = true; /** * * @param {Task|TaskGroup} child * @returns {number} */ function computeTaskProgress(child) { if (child.isTask) { const state = child.state.getValue(); if (state === TaskState.INITIAL || state === TaskState.READY) { // not started yet return 0; } } return child.computeProgress(); } export default TaskGroup;