parent
							
								
									8b0f6f34a9
								
							
						
					
					
						commit
						a0bace91a0
					
				
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| 
						 | 
					@ -10,6 +10,9 @@ dist/*
 | 
				
			||||||
# Include the built-in asset folder
 | 
					# Include the built-in asset folder
 | 
				
			||||||
!dist/builtin/
 | 
					!dist/builtin/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Include the hw1 assets
 | 
				
			||||||
 | 
					!dist/hw3_assets/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Include the hw1 assets
 | 
					# Include the hw1 assets
 | 
				
			||||||
!dist/hw4_assets/
 | 
					!dist/hw4_assets/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
import Actor from "../DataTypes/Interfaces/Actor";
 | 
					import Actor from "../DataTypes/Interfaces/Actor";
 | 
				
			||||||
import Updateable from "../DataTypes/Interfaces/Updateable";
 | 
					import Updateable from "../DataTypes/Interfaces/Updateable";
 | 
				
			||||||
import AI from "../DataTypes/Interfaces/AI";
 | 
					import AI from "../DataTypes/Interfaces/AI";
 | 
				
			||||||
 | 
					import GoapAI from "../DataTypes/Interfaces/GoapAI"
 | 
				
			||||||
import Map from "../DataTypes/Map";
 | 
					import Map from "../DataTypes/Map";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					@ -39,7 +40,7 @@ export default class AIManager implements Updateable {
 | 
				
			||||||
	 * @param name The name of the AI to register
 | 
						 * @param name The name of the AI to register
 | 
				
			||||||
	 * @param constr The constructor for the AI
 | 
						 * @param constr The constructor for the AI
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	registerAI(name: string, constr: new <T extends AI>() => T ): void {
 | 
						registerAI(name: string, constr: new <T extends AI | GoapAI>() => T ): void {
 | 
				
			||||||
		this.registeredAI.add(name, constr);
 | 
							this.registeredAI.add(name, constr);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -48,7 +49,7 @@ export default class AIManager implements Updateable {
 | 
				
			||||||
	 * @param name The name of the AI to add
 | 
						 * @param name The name of the AI to add
 | 
				
			||||||
	 * @returns A new AI instance
 | 
						 * @returns A new AI instance
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	generateAI(name: string): AI {
 | 
						generateAI(name: string): AI | GoapAI {
 | 
				
			||||||
		if(this.registeredAI.has(name)){
 | 
							if(this.registeredAI.has(name)){
 | 
				
			||||||
			return new (this.registeredAI.get(name))();
 | 
								return new (this.registeredAI.get(name))();
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										82
									
								
								src/Wolfie2D/AI/GoapActionPlanner.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/Wolfie2D/AI/GoapActionPlanner.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,82 @@
 | 
				
			||||||
 | 
					import Graph from "../DataTypes/Graphs/Graph";
 | 
				
			||||||
 | 
					import GoapAction from "../DataTypes/Interfaces/GoapAction";
 | 
				
			||||||
 | 
					import GoapAI from "../DataTypes/Interfaces/GoapAI";
 | 
				
			||||||
 | 
					import Queue from "../DataTypes/Queue";
 | 
				
			||||||
 | 
					import Stack from "../DataTypes/Stack";
 | 
				
			||||||
 | 
					import GraphUtils from "../Utils/GraphUtils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class GoapActionPlanner {
 | 
				
			||||||
 | 
					    mapping: Map<number,GoapAction | string>;
 | 
				
			||||||
 | 
					    graph: Graph;
 | 
				
			||||||
 | 
					    path: Array<number>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    plan(goal: string, possibleActions: Array<GoapAction>, currentStatus: Array<string>, actor: GoapAI): Stack<GoapAction> {
 | 
				
			||||||
 | 
					        this.graph = new Graph(true);
 | 
				
			||||||
 | 
					        this.mapping = new Map();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //0 is our root
 | 
				
			||||||
 | 
					        this.graph.addNode();
 | 
				
			||||||
 | 
					        this.mapping.set(0,"Start");
 | 
				
			||||||
 | 
					        //1 is the goal
 | 
				
			||||||
 | 
					        this.graph.addNode();
 | 
				
			||||||
 | 
					        this.mapping.set(1,"Goal");
 | 
				
			||||||
 | 
					        this.graph.addEdge(1,1,Number.POSITIVE_INFINITY);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //Build tree from 0 to 1
 | 
				
			||||||
 | 
					        this.buildTree(0, goal, possibleActions, currentStatus);
 | 
				
			||||||
 | 
					        console.log(this.graph.toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //Run djikstra to find shortest path
 | 
				
			||||||
 | 
					        this.path = GraphUtils.djikstra(this.graph, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //Push all elements of the plan
 | 
				
			||||||
 | 
					        let plan = new Stack<GoapAction>();
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							let i = 1;
 | 
				
			||||||
 | 
							while(this.path[i] !== -1){
 | 
				
			||||||
 | 
					            console.log(this.path[i]);
 | 
				
			||||||
 | 
					            if (this.path[i] !== 0){
 | 
				
			||||||
 | 
								    plan.push(<GoapAction>this.mapping.get(this.path[i]));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
								i = this.path[i];
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return plan;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    buildTree(root: number, goal:string, possibleActions: Array<GoapAction>, currentStatus: Array<string>): void {
 | 
				
			||||||
 | 
					        //For each possible action 
 | 
				
			||||||
 | 
					        possibleActions.forEach(action => {
 | 
				
			||||||
 | 
					            console.log("root:" + root + ",action precons:" + action.preconditions.toString() 
 | 
				
			||||||
 | 
					                + ", action effects:" + action.effects.toString() + ", current Status:" + currentStatus.toString())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //Can it be performed?
 | 
				
			||||||
 | 
					            if (action.checkPreconditions(currentStatus)){
 | 
				
			||||||
 | 
					                //This action can be performed
 | 
				
			||||||
 | 
					                //Add effects to currentStatus
 | 
				
			||||||
 | 
					                let newStatus = [...currentStatus];
 | 
				
			||||||
 | 
					                newStatus.push(...action.effects);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                //Check if the new node is the goal
 | 
				
			||||||
 | 
					                if (newStatus.includes(goal)){
 | 
				
			||||||
 | 
					                    console.log("AT GOAL");
 | 
				
			||||||
 | 
					                    let newNode = this.graph.addNode() - 1;
 | 
				
			||||||
 | 
					                    this.mapping.set(newNode, action);
 | 
				
			||||||
 | 
					                    this.graph.addEdge(root, newNode, action.cost);
 | 
				
			||||||
 | 
					                    this.graph.addEdge(newNode, 1, 0);
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                //Add node and edge from root
 | 
				
			||||||
 | 
					                let newNode = this.graph.addNode() - 1;
 | 
				
			||||||
 | 
					                this.mapping.set(newNode, action);
 | 
				
			||||||
 | 
					                this.graph.addEdge(root, newNode, action.cost);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                //Recursive call
 | 
				
			||||||
 | 
					                console.log(possibleActions.indexOf(action))
 | 
				
			||||||
 | 
					                let newActions = possibleActions.filter(act => act !== action)
 | 
				
			||||||
 | 
					                this.buildTree(newNode, goal, newActions, action.effects);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										40
									
								
								src/Wolfie2D/AI/StateMachineGoapAI.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/Wolfie2D/AI/StateMachineGoapAI.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,40 @@
 | 
				
			||||||
 | 
					import GoapAction from "../DataTypes/Interfaces/GoapAction";
 | 
				
			||||||
 | 
					import GoapAI from "../DataTypes/Interfaces/GoapAI";
 | 
				
			||||||
 | 
					import Queue from "../DataTypes/Queue";
 | 
				
			||||||
 | 
					import Stack from "../DataTypes/Stack";
 | 
				
			||||||
 | 
					import StateMachine from "../DataTypes/State/StateMachine";
 | 
				
			||||||
 | 
					import GameNode from "../Nodes/GameNode";
 | 
				
			||||||
 | 
					import GoapActionPlanner from "./GoapActionPlanner";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * A version of a @reference[StateMachine] that is configured to work as an AI controller for a @reference[GameNode]
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					 export default class StateMachineGoapAI extends StateMachine implements GoapAI {
 | 
				
			||||||
 | 
						/**	The GameNode that uses this StateMachine for its AI */
 | 
				
			||||||
 | 
						protected owner: GameNode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						goal: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    currentStatus: Array<string>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    possibleActions: Array<GoapAction>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    plan: Stack<GoapAction>;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    planner: GoapActionPlanner;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// @implemented
 | 
				
			||||||
 | 
						initializeAI(owner: GameNode, config: Record<string, any>): void {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// @implemented
 | 
				
			||||||
 | 
						destroy(){
 | 
				
			||||||
 | 
							// Get rid of our reference to the owner
 | 
				
			||||||
 | 
							delete this.owner;
 | 
				
			||||||
 | 
							this.receiver.destroy();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// @implemented
 | 
				
			||||||
 | 
						activate(options: Record<string, any>): void {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						changeGoal(goal: string): void {}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -125,7 +125,7 @@ export default class Graph {
 | 
				
			||||||
		for(let i = 0; i < this.numVertices; i++){
 | 
							for(let i = 0; i < this.numVertices; i++){
 | 
				
			||||||
			let edge = this.edges[i];
 | 
								let edge = this.edges[i];
 | 
				
			||||||
			let edgeStr = "";
 | 
								let edgeStr = "";
 | 
				
			||||||
			while(edge !== null){
 | 
								while(edge !== undefined && edge !== null){
 | 
				
			||||||
				edgeStr += edge.y.toString();
 | 
									edgeStr += edge.y.toString();
 | 
				
			||||||
				if(this.weighted){
 | 
									if(this.weighted){
 | 
				
			||||||
					edgeStr += " (" + edge.weight + ")";
 | 
										edgeStr += " (" + edge.weight + ")";
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,12 +1,13 @@
 | 
				
			||||||
import NavigationPath from "../../Pathfinding/NavigationPath";
 | 
					import NavigationPath from "../../Pathfinding/NavigationPath";
 | 
				
			||||||
import AI from "./AI";
 | 
					import AI from "./AI";
 | 
				
			||||||
 | 
					import GoapAI from "./GoapAI";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * A game object that has an AI and can perform its own actions every update cycle
 | 
					 * A game object that has an AI and can perform its own actions every update cycle
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export default interface Actor {
 | 
					export default interface Actor {
 | 
				
			||||||
    /** The AI of the actor */
 | 
					    /** The AI of the actor */
 | 
				
			||||||
    ai: AI;
 | 
					    ai: AI | GoapAI;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /** The activity status of the actor */
 | 
					    /** The activity status of the actor */
 | 
				
			||||||
    aiActive: boolean;
 | 
					    aiActive: boolean;
 | 
				
			||||||
| 
						 | 
					@ -21,8 +22,9 @@ export default interface Actor {
 | 
				
			||||||
     * Adds an AI to this Actor.
 | 
					     * Adds an AI to this Actor.
 | 
				
			||||||
     * @param ai The name of the AI, or the actual AI, to add to the Actor.
 | 
					     * @param ai The name of the AI, or the actual AI, to add to the Actor.
 | 
				
			||||||
     * @param options The options to give to the AI for initialization.
 | 
					     * @param options The options to give to the AI for initialization.
 | 
				
			||||||
 | 
					     * @param type The type of the AI, 0 for AI, 1 for GoapAI, defaults to assume AI
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    addAI<T extends AI>(ai: string | (new () => T), options: Record<string, any>): void;
 | 
					    addAI<T extends AI>(ai: string | (new () => T), options: Record<string, any>, type?: number): void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Sets the AI to start/stop for this Actor.
 | 
					     * Sets the AI to start/stop for this Actor.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										42
									
								
								src/Wolfie2D/DataTypes/Interfaces/GoapAI.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/Wolfie2D/DataTypes/Interfaces/GoapAI.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,42 @@
 | 
				
			||||||
 | 
					import GoapActionPlanner from "../../AI/GoapActionPlanner";
 | 
				
			||||||
 | 
					import GameEvent from "../../Events/GameEvent";
 | 
				
			||||||
 | 
					import GameNode from "../../Nodes/GameNode";
 | 
				
			||||||
 | 
					import Queue from "../Queue";
 | 
				
			||||||
 | 
					import Stack from "../Stack";
 | 
				
			||||||
 | 
					import GoapAction from "./GoapAction";
 | 
				
			||||||
 | 
					import Updateable from "./Updateable";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Defines a controller for a bot or a human. Must be able to update
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export default interface GoapAI extends Updateable {
 | 
				
			||||||
 | 
					    /** Current goal of the AI */
 | 
				
			||||||
 | 
					    goal: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** All current statuses this AI has */
 | 
				
			||||||
 | 
					    currentStatus: Array<string>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** All possible actions that can be carried out */
 | 
				
			||||||
 | 
					    possibleActions: Array<GoapAction>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Current actions to be carried out */
 | 
				
			||||||
 | 
					    plan: Stack<GoapAction>;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /** Once we have no actions, the planner can be called to find a new sequence of actions */
 | 
				
			||||||
 | 
					    planner: GoapActionPlanner;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Clears references from to the owner */
 | 
				
			||||||
 | 
					    destroy(): void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Activates this AI from a stopped state and allows variables to be passed in */
 | 
				
			||||||
 | 
					    activate(options: Record<string, any>): void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Handles events from the Actor */
 | 
				
			||||||
 | 
					    handleEvent(event: GameEvent): void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Initializes the AI with the actor and any additional config */
 | 
				
			||||||
 | 
					    initializeAI(owner:GameNode, options: Record<string, any>): void
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Change the goal to a new goal */
 | 
				
			||||||
 | 
					    changeGoal(goal: string): void
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										61
									
								
								src/Wolfie2D/DataTypes/Interfaces/GoapAction.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/Wolfie2D/DataTypes/Interfaces/GoapAction.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,61 @@
 | 
				
			||||||
 | 
					import StateMachineGoapAI from "../../AI/StateMachineGoapAI";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default abstract class GoapAction {
 | 
				
			||||||
 | 
					    /** Cost it takes to complete this action */
 | 
				
			||||||
 | 
					    cost: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Preconditions that have to be satisfied for an action to happen */
 | 
				
			||||||
 | 
					    preconditions: Array<string>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Resulting statuses after this action completes */
 | 
				
			||||||
 | 
					    effects: Array<string>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** If the action fails, do we keep trying until we succeed */
 | 
				
			||||||
 | 
					    loopAction: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Attempt to perform an action, if successful, it will return an array of the expected effects, otherwise it will return null
 | 
				
			||||||
 | 
					     * @param statuses Current statuses of the actor
 | 
				
			||||||
 | 
					     * @param actor GameNode for the actor
 | 
				
			||||||
 | 
					     * @param deltaT The time sine the last update
 | 
				
			||||||
 | 
					     * @param target GameNode for a optional target
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    abstract performAction(statuses: Array<string>, actor: StateMachineGoapAI, deltaT: number, target?: StateMachineGoapAI): Array<string>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Check preconditions with current statuses to see if action can be performed */
 | 
				
			||||||
 | 
					    checkPreconditions(statuses: Array<string>): boolean {
 | 
				
			||||||
 | 
					        // Check that every element in the preconditions array is found in the statuses array
 | 
				
			||||||
 | 
					        return (this.preconditions.every((status) => {
 | 
				
			||||||
 | 
					            if (!statuses.includes(status)){
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Add one or more preconditions to this action */
 | 
				
			||||||
 | 
					    addPrecondition(preconditions: string | string[]): void {
 | 
				
			||||||
 | 
					        this.preconditions.push(...preconditions);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Add one or more effects to this action */
 | 
				
			||||||
 | 
					    addEffect(effects: string | string[]): void {
 | 
				
			||||||
 | 
					        this.effects.push(...effects);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Removes an precondition, returns true if successful */
 | 
				
			||||||
 | 
					    removePrecondition(precondition: string): boolean {
 | 
				
			||||||
 | 
					        throw new Error("Method not implemented.");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /** Removes an precondition, returns true if successful */
 | 
				
			||||||
 | 
					    removeEffect(effect: string): boolean {
 | 
				
			||||||
 | 
					        throw new Error("Method not implemented.");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Update the cost of this action based on options */
 | 
				
			||||||
 | 
					    abstract updateCost(options: Record<string,number>): void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    abstract toString(): string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@ export default interface Navigable {
 | 
				
			||||||
     * Gets a new navigation path based on this Navigable object.
 | 
					     * Gets a new navigation path based on this Navigable object.
 | 
				
			||||||
     * @param fromPosition The position to start navigation from.
 | 
					     * @param fromPosition The position to start navigation from.
 | 
				
			||||||
     * @param toPosition The position to navigate to.
 | 
					     * @param toPosition The position to navigate to.
 | 
				
			||||||
 | 
					     * @param direct If true, move directly from fromPosition to toPosition
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    getNavigationPath(fromPosition: Vec2, toPosition: Vec2): NavigationPath;
 | 
					    getNavigationPath(fromPosition: Vec2, toPosition: Vec2, direct?: boolean): NavigationPath;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										38
									
								
								src/Wolfie2D/Events/BattleSystem.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/Wolfie2D/Events/BattleSystem.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,38 @@
 | 
				
			||||||
 | 
					import Updateable from "../DataTypes/Interfaces/Updateable";
 | 
				
			||||||
 | 
					import EventQueue from "./EventQueue";
 | 
				
			||||||
 | 
					import Receiver from "./Receiver";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default abstract class BattleSystem implements Updateable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    units: Map<number,Record<string, number>>;
 | 
				
			||||||
 | 
					    receiver: Receiver;
 | 
				
			||||||
 | 
					    eventQueue: EventQueue;
 | 
				
			||||||
 | 
					    statSystem: Array<string>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(battleEvents: Array<string>, statSystem: Array<string>){
 | 
				
			||||||
 | 
					        this.eventQueue = EventQueue.getInstance();
 | 
				
			||||||
 | 
					        this.eventQueue.subscribe(this.receiver, battleEvents);
 | 
				
			||||||
 | 
					        this.statSystem = statSystem;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getUnitStats(id: number): Record<string,number> {
 | 
				
			||||||
 | 
					        return this.units.get(id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    validateStats(stats: Record<string,number>): Record<string,number> {
 | 
				
			||||||
 | 
					        this.statSystem.forEach(e => {
 | 
				
			||||||
 | 
					            if (stats[e] === undefined){
 | 
				
			||||||
 | 
					                stats[e] = 0;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        return stats;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    initalizeUnit(id: number, stats: Record<string, number>): void{
 | 
				
			||||||
 | 
					        this.units.set(id, this.validateStats(stats));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    abstract update(deltaT: number): void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -12,6 +12,7 @@ import { GameEventType } from "../Events/GameEventType";
 | 
				
			||||||
export default class Input {
 | 
					export default class Input {
 | 
				
			||||||
	private static mousePressed: boolean;
 | 
						private static mousePressed: boolean;
 | 
				
			||||||
	private static mouseJustPressed: boolean;
 | 
						private static mouseJustPressed: boolean;
 | 
				
			||||||
 | 
						private static mouseButtonPressed: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private static keyJustPressed: Map<boolean>;
 | 
						private static keyJustPressed: Map<boolean>;
 | 
				
			||||||
	private static keyPressed: Map<boolean>;
 | 
						private static keyPressed: Map<boolean>;
 | 
				
			||||||
| 
						 | 
					@ -35,7 +36,7 @@ export default class Input {
 | 
				
			||||||
	 * Initializes the Input object
 | 
						 * Initializes the Input object
 | 
				
			||||||
	 * @param viewport A reference to the viewport of the game
 | 
						 * @param viewport A reference to the viewport of the game
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	static initialize(viewport: Viewport, keyMap: Array<Record<string, any>>){
 | 
						static initialize(viewport: Viewport, keyMap: Array<Record<string, any>>) {
 | 
				
			||||||
		Input.viewport = viewport;
 | 
							Input.viewport = viewport;
 | 
				
			||||||
		Input.mousePressed = false;
 | 
							Input.mousePressed = false;
 | 
				
			||||||
		Input.mouseJustPressed = false;
 | 
							Input.mouseJustPressed = false;
 | 
				
			||||||
| 
						 | 
					@ -53,7 +54,7 @@ export default class Input {
 | 
				
			||||||
		Input.keyMap = new Map();
 | 
							Input.keyMap = new Map();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Add all keys to the keymap
 | 
							// Add all keys to the keymap
 | 
				
			||||||
		for(let entry in keyMap){
 | 
							for (let entry in keyMap) {
 | 
				
			||||||
			let name = keyMap[entry].name;
 | 
								let name = keyMap[entry].name;
 | 
				
			||||||
			let keys = keyMap[entry].keys;
 | 
								let keys = keyMap[entry].keys;
 | 
				
			||||||
			Input.keyMap.add(name, keys);
 | 
								Input.keyMap.add(name, keys);
 | 
				
			||||||
| 
						 | 
					@ -62,7 +63,7 @@ export default class Input {
 | 
				
			||||||
		Input.eventQueue = EventQueue.getInstance();
 | 
							Input.eventQueue = EventQueue.getInstance();
 | 
				
			||||||
		// Subscribe to all input events
 | 
							// Subscribe to all input events
 | 
				
			||||||
		Input.eventQueue.subscribe(Input.receiver, [GameEventType.MOUSE_DOWN, GameEventType.MOUSE_UP, GameEventType.MOUSE_MOVE,
 | 
							Input.eventQueue.subscribe(Input.receiver, [GameEventType.MOUSE_DOWN, GameEventType.MOUSE_UP, GameEventType.MOUSE_MOVE,
 | 
				
			||||||
			 GameEventType.KEY_DOWN, GameEventType.KEY_UP, GameEventType.CANVAS_BLUR, GameEventType.WHEEL_UP, GameEventType.WHEEL_DOWN]);
 | 
							GameEventType.KEY_DOWN, GameEventType.KEY_UP, GameEventType.CANVAS_BLUR, GameEventType.WHEEL_UP, GameEventType.WHEEL_DOWN]);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	static update(deltaT: number): void {
 | 
						static update(deltaT: number): void {
 | 
				
			||||||
| 
						 | 
					@ -72,53 +73,54 @@ export default class Input {
 | 
				
			||||||
		Input.justScrolled = false;
 | 
							Input.justScrolled = false;
 | 
				
			||||||
		Input.scrollDirection = 0;
 | 
							Input.scrollDirection = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		while(Input.receiver.hasNextEvent()){			
 | 
							while (Input.receiver.hasNextEvent()) {
 | 
				
			||||||
			let event = Input.receiver.getNextEvent();
 | 
								let event = Input.receiver.getNextEvent();
 | 
				
			||||||
			
 | 
					
 | 
				
			||||||
			// Handle each event type
 | 
								// Handle each event type
 | 
				
			||||||
			if(event.type === GameEventType.MOUSE_DOWN){
 | 
								if (event.type === GameEventType.MOUSE_DOWN) {
 | 
				
			||||||
				Input.mouseJustPressed = true;
 | 
									Input.mouseJustPressed = true;
 | 
				
			||||||
				Input.mousePressed = true;
 | 
									Input.mousePressed = true;
 | 
				
			||||||
				Input.mousePressPosition = event.data.get("position");	
 | 
									Input.mousePressPosition = event.data.get("position");
 | 
				
			||||||
 | 
									Input.mouseButtonPressed = event.data.get("button");
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if(event.type === GameEventType.MOUSE_UP){
 | 
								if (event.type === GameEventType.MOUSE_UP) {
 | 
				
			||||||
				Input.mousePressed = false;
 | 
									Input.mousePressed = false;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if(event.type === GameEventType.MOUSE_MOVE){
 | 
								if (event.type === GameEventType.MOUSE_MOVE) {
 | 
				
			||||||
				Input.mousePosition = event.data.get("position");
 | 
									Input.mousePosition = event.data.get("position");
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if(event.type === GameEventType.KEY_DOWN){
 | 
								if (event.type === GameEventType.KEY_DOWN) {
 | 
				
			||||||
				let key = event.data.get("key");
 | 
									let key = event.data.get("key");
 | 
				
			||||||
				// Handle space bar
 | 
									// Handle space bar
 | 
				
			||||||
				if(key === " "){
 | 
									if (key === " ") {
 | 
				
			||||||
					key = "space";
 | 
										key = "space";
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				if(!Input.keyPressed.get(key)){
 | 
									if (!Input.keyPressed.get(key)) {
 | 
				
			||||||
					Input.keyJustPressed.set(key, true);
 | 
										Input.keyJustPressed.set(key, true);
 | 
				
			||||||
					Input.keyPressed.set(key, true);
 | 
										Input.keyPressed.set(key, true);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if(event.type === GameEventType.KEY_UP){
 | 
								if (event.type === GameEventType.KEY_UP) {
 | 
				
			||||||
				let key = event.data.get("key");
 | 
									let key = event.data.get("key");
 | 
				
			||||||
				// Handle space bar
 | 
									// Handle space bar
 | 
				
			||||||
				if(key === " "){
 | 
									if (key === " ") {
 | 
				
			||||||
					key = "space";
 | 
										key = "space";
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				Input.keyPressed.set(key, false);
 | 
									Input.keyPressed.set(key, false);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if(event.type === GameEventType.CANVAS_BLUR){
 | 
								if (event.type === GameEventType.CANVAS_BLUR) {
 | 
				
			||||||
				Input.clearKeyPresses()
 | 
									Input.clearKeyPresses()
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if(event.type === GameEventType.WHEEL_UP){
 | 
								if (event.type === GameEventType.WHEEL_UP) {
 | 
				
			||||||
				Input.scrollDirection = -1;
 | 
									Input.scrollDirection = -1;
 | 
				
			||||||
				Input.justScrolled = true;
 | 
									Input.justScrolled = true;
 | 
				
			||||||
			} else if(event.type === GameEventType.WHEEL_DOWN){
 | 
								} else if (event.type === GameEventType.WHEEL_DOWN) {
 | 
				
			||||||
				Input.scrollDirection = 1;
 | 
									Input.scrollDirection = 1;
 | 
				
			||||||
				Input.justScrolled = true;
 | 
									Input.justScrolled = true;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					@ -137,9 +139,9 @@ export default class Input {
 | 
				
			||||||
	 * @returns True if the key was just pressed, false otherwise
 | 
						 * @returns True if the key was just pressed, false otherwise
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	static isKeyJustPressed(key: string): boolean {
 | 
						static isKeyJustPressed(key: string): boolean {
 | 
				
			||||||
		if(Input.keysDisabled) return false;
 | 
							if (Input.keysDisabled) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if(Input.keyJustPressed.has(key)){
 | 
							if (Input.keyJustPressed.has(key)) {
 | 
				
			||||||
			return Input.keyJustPressed.get(key)
 | 
								return Input.keyJustPressed.get(key)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
| 
						 | 
					@ -152,11 +154,11 @@ export default class Input {
 | 
				
			||||||
	 * @returns An array of all of the newly pressed keys.
 | 
						 * @returns An array of all of the newly pressed keys.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	static getKeysJustPressed(): Array<string> {
 | 
						static getKeysJustPressed(): Array<string> {
 | 
				
			||||||
		if(Input.keysDisabled) return [];
 | 
							if (Input.keysDisabled) return [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let keys = Array<string>();
 | 
							let keys = Array<string>();
 | 
				
			||||||
		Input.keyJustPressed.forEach(key => {
 | 
							Input.keyJustPressed.forEach(key => {
 | 
				
			||||||
			if(Input.keyJustPressed.get(key)){
 | 
								if (Input.keyJustPressed.get(key)) {
 | 
				
			||||||
				keys.push(key);
 | 
									keys.push(key);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
| 
						 | 
					@ -169,9 +171,9 @@ export default class Input {
 | 
				
			||||||
	 * @returns True if the key is currently pressed, false otherwise
 | 
						 * @returns True if the key is currently pressed, false otherwise
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	static isKeyPressed(key: string): boolean {
 | 
						static isKeyPressed(key: string): boolean {
 | 
				
			||||||
		if(Input.keysDisabled) return false;
 | 
							if (Input.keysDisabled) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if(Input.keyPressed.has(key)){
 | 
							if (Input.keyPressed.has(key)) {
 | 
				
			||||||
			return Input.keyPressed.get(key)
 | 
								return Input.keyPressed.get(key)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
| 
						 | 
					@ -200,20 +202,20 @@ export default class Input {
 | 
				
			||||||
	 * @returns True if the input was just pressed, false otherwise
 | 
						 * @returns True if the input was just pressed, false otherwise
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	static isJustPressed(inputName: string): boolean {
 | 
						static isJustPressed(inputName: string): boolean {
 | 
				
			||||||
		if(Input.keysDisabled) return false;
 | 
							if (Input.keysDisabled) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if(Input.keyMap.has(inputName)){
 | 
							if (Input.keyMap.has(inputName)) {
 | 
				
			||||||
			const keys = Input.keyMap.get(inputName);
 | 
								const keys = Input.keyMap.get(inputName);
 | 
				
			||||||
			let justPressed = false;
 | 
								let justPressed = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			for(let key of keys){
 | 
								for (let key of keys) {
 | 
				
			||||||
				justPressed = justPressed || Input.isKeyJustPressed(key);
 | 
									justPressed = justPressed || Input.isKeyJustPressed(key);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			return justPressed;
 | 
								return justPressed;
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
		}	
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
| 
						 | 
					@ -222,13 +224,13 @@ export default class Input {
 | 
				
			||||||
	 * @returns True if the input is pressed, false otherwise
 | 
						 * @returns True if the input is pressed, false otherwise
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	static isPressed(inputName: string): boolean {
 | 
						static isPressed(inputName: string): boolean {
 | 
				
			||||||
		if(Input.keysDisabled) return false;
 | 
							if (Input.keysDisabled) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if(Input.keyMap.has(inputName)){
 | 
							if (Input.keyMap.has(inputName)) {
 | 
				
			||||||
			const keys = Input.keyMap.get(inputName);
 | 
								const keys = Input.keyMap.get(inputName);
 | 
				
			||||||
			let pressed = false;
 | 
								let pressed = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			for(let key of keys){
 | 
								for (let key of keys) {
 | 
				
			||||||
				pressed = pressed || Input.isKeyPressed(key);
 | 
									pressed = pressed || Input.isKeyPressed(key);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -237,20 +239,30 @@ export default class Input {
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Returns whether or not the mouse was newly pressed Input frame
 | 
						 * 
 | 
				
			||||||
 | 
						 * Returns whether or not the mouse was newly pressed Input frame.
 | 
				
			||||||
 | 
						 * @param mouseButton Optionally specify which mouse click you want to know was pressed. 
 | 
				
			||||||
 | 
						 * 0 for left click, 1 for middle click, 2 for right click.
 | 
				
			||||||
	 * @returns True if the mouse was just pressed, false otherwise
 | 
						 * @returns True if the mouse was just pressed, false otherwise
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	static isMouseJustPressed(): boolean {
 | 
						static isMouseJustPressed(mouseButton?: number): boolean {
 | 
				
			||||||
 | 
							if (mouseButton) {
 | 
				
			||||||
 | 
								return Input.mouseJustPressed && !Input.mouseDisabled && mouseButton == this.mouseButtonPressed;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		return Input.mouseJustPressed && !Input.mouseDisabled;
 | 
							return Input.mouseJustPressed && !Input.mouseDisabled;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Returns whether or not the mouse is currently pressed
 | 
						 * Returns whether or not the mouse is currently pressed
 | 
				
			||||||
 | 
						 * @param mouseButton Optionally specify which mouse click you want to know was pressed. 
 | 
				
			||||||
 | 
						 * 0 for left click, 1 for middle click, 2 for right click.
 | 
				
			||||||
	 * @returns True if the mouse is currently pressed, false otherwise
 | 
						 * @returns True if the mouse is currently pressed, false otherwise
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	static isMousePressed(): boolean {
 | 
						static isMousePressed(mouseButton?: number): boolean {
 | 
				
			||||||
 | 
							if (mouseButton) {
 | 
				
			||||||
 | 
								return Input.mousePressed && !Input.mouseDisabled && mouseButton == this.mouseButtonPressed;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		return Input.mousePressed && !Input.mouseDisabled;
 | 
							return Input.mousePressed && !Input.mouseDisabled;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -275,7 +287,7 @@ export default class Input {
 | 
				
			||||||
	 * @returns The mouse position stored as a Vec2
 | 
						 * @returns The mouse position stored as a Vec2
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	static getMousePosition(): Vec2 {
 | 
						static getMousePosition(): Vec2 {
 | 
				
			||||||
		return Input.mousePosition.scaled(1/this.viewport.getZoomLevel());
 | 
							return Input.mousePosition.scaled(1 / this.viewport.getZoomLevel());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
| 
						 | 
					@ -284,7 +296,7 @@ export default class Input {
 | 
				
			||||||
	 * @returns The mouse position stored as a Vec2
 | 
						 * @returns The mouse position stored as a Vec2
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	static getGlobalMousePosition(): Vec2 {
 | 
						static getGlobalMousePosition(): Vec2 {
 | 
				
			||||||
		return Input.mousePosition.clone().scale(1/this.viewport.getZoomLevel()).add(Input.viewport.getOrigin());
 | 
							return Input.mousePosition.clone().scale(1 / this.viewport.getZoomLevel()).add(Input.viewport.getOrigin());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,7 +29,8 @@ export default class InputHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private handleMouseDown = (event: MouseEvent, canvas: HTMLCanvasElement): void => {
 | 
					    private handleMouseDown = (event: MouseEvent, canvas: HTMLCanvasElement): void => {
 | 
				
			||||||
		let pos = this.getMousePosition(event, canvas);
 | 
							let pos = this.getMousePosition(event, canvas);
 | 
				
			||||||
        let gameEvent = new GameEvent(GameEventType.MOUSE_DOWN, {position: pos});
 | 
					        let button = event.button;
 | 
				
			||||||
 | 
					        let gameEvent = new GameEvent(GameEventType.MOUSE_DOWN, {position: pos, button: button});
 | 
				
			||||||
        this.eventQueue.addEvent(gameEvent);
 | 
					        this.eventQueue.addEvent(gameEvent);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,6 +18,7 @@ import TweenController from "../Rendering/Animations/TweenController";
 | 
				
			||||||
import Debug from "../Debug/Debug";
 | 
					import Debug from "../Debug/Debug";
 | 
				
			||||||
import Color from "../Utils/Color";
 | 
					import Color from "../Utils/Color";
 | 
				
			||||||
import Circle from "../DataTypes/Shapes/Circle";
 | 
					import Circle from "../DataTypes/Shapes/Circle";
 | 
				
			||||||
 | 
					import GoapAI from "../DataTypes/Interfaces/GoapAI";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * The representation of an object in the game world.
 | 
					 * The representation of an object in the game world.
 | 
				
			||||||
| 
						 | 
					@ -54,7 +55,7 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
 | 
				
			||||||
	isColliding: boolean = false;
 | 
						isColliding: boolean = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*---------- ACTOR ----------*/
 | 
						/*---------- ACTOR ----------*/
 | 
				
			||||||
	_ai: AI;
 | 
						_ai: AI | GoapAI;
 | 
				
			||||||
	aiActive: boolean;
 | 
						aiActive: boolean;
 | 
				
			||||||
	path: NavigationPath;
 | 
						path: NavigationPath;
 | 
				
			||||||
	pathfinding: boolean = false;
 | 
						pathfinding: boolean = false;
 | 
				
			||||||
| 
						 | 
					@ -331,11 +332,11 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*---------- ACTOR ----------*/
 | 
						/*---------- ACTOR ----------*/
 | 
				
			||||||
	get ai(): AI {
 | 
						get ai(): AI | GoapAI {
 | 
				
			||||||
		return this._ai;
 | 
							return this._ai;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	set ai(ai: AI) {
 | 
						set ai(ai: AI | GoapAI) {
 | 
				
			||||||
		if(!this._ai){
 | 
							if(!this._ai){
 | 
				
			||||||
			// If we haven't been previously had an ai, register us with the ai manager
 | 
								// If we haven't been previously had an ai, register us with the ai manager
 | 
				
			||||||
			this.scene.getAIManager().registerActor(this);
 | 
								this.scene.getAIManager().registerActor(this);
 | 
				
			||||||
| 
						 | 
					@ -346,7 +347,7 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// @implemented
 | 
						// @implemented
 | 
				
			||||||
	addAI<T extends AI>(ai: string | (new () => T), options?: Record<string, any>): void {
 | 
						addAI<T extends AI | GoapAI>(ai: string | (new () => T), options?: Record<string, any>, type?: number): void {
 | 
				
			||||||
		if(!this._ai){
 | 
							if(!this._ai){
 | 
				
			||||||
			this.scene.getAIManager().registerActor(this);
 | 
								this.scene.getAIManager().registerActor(this);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -357,6 +358,7 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
 | 
				
			||||||
			this._ai = new ai();
 | 
								this._ai = new ai();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Question, how much do we want different type of AI to be handled the same, i.e. should GoapAI and AI similar methods and signatures for the sake of unity
 | 
				
			||||||
		this._ai.initializeAI(this, options);
 | 
							this._ai.initializeAI(this, options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.aiActive = true;
 | 
							this.aiActive = true;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,4 +29,28 @@ export default abstract class Graphic extends CanvasNode {
 | 
				
			||||||
    setColor(color: Color){
 | 
					    setColor(color: Color){
 | 
				
			||||||
        this.color = color;
 | 
					        this.color = color;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    set colorR(r: number){
 | 
				
			||||||
 | 
					        this.color.r = r;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    get colorR(): number {
 | 
				
			||||||
 | 
					        return this.color.r;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    set colorG(g: number){
 | 
				
			||||||
 | 
					        this.color.g = g;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    get colorG(): number {
 | 
				
			||||||
 | 
					        return this.color.g;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    set colorB(b: number){
 | 
				
			||||||
 | 
					        this.color.b = b;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    get colorB(): number {
 | 
				
			||||||
 | 
					        return this.color.b;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -2,4 +2,5 @@ export enum GraphicType {
 | 
				
			||||||
	POINT = "POINT",
 | 
						POINT = "POINT",
 | 
				
			||||||
	RECT = "RECT",
 | 
						RECT = "RECT",
 | 
				
			||||||
	LINE = "LINE",
 | 
						LINE = "LINE",
 | 
				
			||||||
 | 
						PARTICLE = "PARTICLE"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										57
									
								
								src/Wolfie2D/Nodes/Graphics/Particle.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/Wolfie2D/Nodes/Graphics/Particle.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,57 @@
 | 
				
			||||||
 | 
					import Vec2 from "../../DataTypes/Vec2";
 | 
				
			||||||
 | 
					import Point from "./Point";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * - Position X
 | 
				
			||||||
 | 
					- Velocity (speed and direction) X
 | 
				
			||||||
 | 
					- Color X
 | 
				
			||||||
 | 
					- Lifetime 
 | 
				
			||||||
 | 
					- Age can be handled as lifetime
 | 
				
			||||||
 | 
					- Shape X
 | 
				
			||||||
 | 
					- Size X
 | 
				
			||||||
 | 
					- Transparency X
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class Particle extends Point {
 | 
				
			||||||
 | 
					    age: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    inUse: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vel: Vec2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mass: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(position: Vec2, size: Vec2, mass: number) {
 | 
				
			||||||
 | 
					        // Are we making this a circle?
 | 
				
			||||||
 | 
					        super(position, size);
 | 
				
			||||||
 | 
					        this.inUse = false;
 | 
				
			||||||
 | 
					        this.mass = mass;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setParticleActive(lifetime: number, position: Vec2) {
 | 
				
			||||||
 | 
					        this.age = lifetime;
 | 
				
			||||||
 | 
					        this.inUse = true;
 | 
				
			||||||
 | 
					        this.visible = true;
 | 
				
			||||||
 | 
					        this.position = position;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    decrementAge(decay: number) {
 | 
				
			||||||
 | 
					        this.age -= decay;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setParticleInactive(){
 | 
				
			||||||
 | 
					        this.inUse = false;
 | 
				
			||||||
 | 
					        this.visible = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    set velY(y: number){
 | 
				
			||||||
 | 
					        this.vel.y = y;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    get velY(): number {
 | 
				
			||||||
 | 
					        return this.vel.y;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -4,9 +4,10 @@ import Vec2 from "../../DataTypes/Vec2";
 | 
				
			||||||
/** A basic point to be drawn on the screen. */
 | 
					/** A basic point to be drawn on the screen. */
 | 
				
			||||||
export default class Point extends Graphic {
 | 
					export default class Point extends Graphic {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(position: Vec2){
 | 
					    constructor(position: Vec2, size: Vec2) {
 | 
				
			||||||
 | 
					        // Are we making this a circle?
 | 
				
			||||||
        super();
 | 
					        super();
 | 
				
			||||||
        this.position = position;
 | 
					        this.position = position;
 | 
				
			||||||
        this.size.set(5, 5);
 | 
					        this.size.set(size.x, size.y);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -30,10 +30,11 @@ export default class NavigationManager {
 | 
				
			||||||
	 * @param navName The name of the registered Navigable object
 | 
						 * @param navName The name of the registered Navigable object
 | 
				
			||||||
	 * @param fromPosition The starting position of navigation
 | 
						 * @param fromPosition The starting position of navigation
 | 
				
			||||||
	 * @param toPosition The ending position of Navigation
 | 
						 * @param toPosition The ending position of Navigation
 | 
				
			||||||
 | 
						 * @param direct If true, go direct from fromPosition to toPosition, don't use NavMesh
 | 
				
			||||||
	 * @returns A NavigationPath containing the route to take over the Navigable entity to get between the provided positions.
 | 
						 * @returns A NavigationPath containing the route to take over the Navigable entity to get between the provided positions.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	getPath(navName: string, fromPosition: Vec2, toPosition: Vec2): NavigationPath {
 | 
						getPath(navName: string, fromPosition: Vec2, toPosition: Vec2, direct?: boolean): NavigationPath {
 | 
				
			||||||
		let nav = this.navigableEntities.get(navName);
 | 
							let nav = this.navigableEntities.get(navName);
 | 
				
			||||||
		return nav.getNavigationPath(fromPosition.clone(), toPosition.clone());
 | 
							return nav.getNavigationPath(fromPosition.clone(), toPosition.clone(), direct);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -21,18 +21,23 @@ export default class Navmesh implements Navigable {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// @implemented
 | 
						// @implemented
 | 
				
			||||||
	getNavigationPath(fromPosition: Vec2, toPosition: Vec2): NavigationPath {
 | 
						getNavigationPath(fromPosition: Vec2, toPosition: Vec2, direct: boolean): NavigationPath {
 | 
				
			||||||
		let start = this.getClosestNode(fromPosition);
 | 
							let start = this.getClosestNode(fromPosition);
 | 
				
			||||||
		let end = this.getClosestNode(toPosition);
 | 
							let end = this.getClosestNode(toPosition);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let parent = GraphUtils.djikstra(this.graph, start);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		let pathStack = new Stack<Vec2>(this.graph.numVertices);
 | 
							let pathStack = new Stack<Vec2>(this.graph.numVertices);
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		// Push the final position and the final position in the graph
 | 
							// Push the final position and the final position in the graph
 | 
				
			||||||
		pathStack.push(toPosition.clone());
 | 
							pathStack.push(toPosition.clone());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (direct) {
 | 
				
			||||||
 | 
								return new NavigationPath(pathStack);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		pathStack.push(this.graph.positions[end]);
 | 
							pathStack.push(this.graph.positions[end]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let parent = GraphUtils.djikstra(this.graph, start);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Add all parents along the path
 | 
							// Add all parents along the path
 | 
				
			||||||
		let i = end;
 | 
							let i = end;
 | 
				
			||||||
		while(parent[i] !== -1){
 | 
							while(parent[i] !== -1){
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										163
									
								
								src/Wolfie2D/Rendering/Animations/ParticleSystem.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								src/Wolfie2D/Rendering/Animations/ParticleSystem.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,163 @@
 | 
				
			||||||
 | 
					import Updateable from "../../DataTypes/Interfaces/Updateable";
 | 
				
			||||||
 | 
					import Vec2 from "../../DataTypes/Vec2";
 | 
				
			||||||
 | 
					import { GraphicType } from "../../Nodes/Graphics/GraphicTypes";
 | 
				
			||||||
 | 
					import Particle from "../../Nodes/Graphics/Particle";
 | 
				
			||||||
 | 
					import Scene from "../../Scene/Scene";
 | 
				
			||||||
 | 
					import Timer from "../../Timing/Timer";
 | 
				
			||||||
 | 
					import Color from "../../Utils/Color";
 | 
				
			||||||
 | 
					import { EaseFunctionType } from "../../Utils/EaseFunctions";
 | 
				
			||||||
 | 
					import RandUtils from "../../Utils/RandUtils";
 | 
				
			||||||
 | 
					import ParticleSystemManager from "./ParticleSystemManager";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					-Move particle system to HW#4, particle class and particle manager(object pool), source, randomized period of decay,
 | 
				
			||||||
 | 
					 semi-randomized approach for spawning, should be general purpose 
 | 
				
			||||||
 | 
					 and load some settings from a json (location, states, colors, randomization). 
 | 
				
			||||||
 | 
					 Should be effect when balloon is popped 
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class ParticleSystem implements Updateable {
 | 
				
			||||||
 | 
					    protected particlePool: Array<Particle>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected lifetime: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected liveParticles: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected maxLiveParticles: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected sourcePoint: Vec2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected particleSize: Vec2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected systemLifetime: Timer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected systemRunning: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected color: Color = new Color(255, 0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(poolSize: number, sourcePoint: Vec2, lifetime: number, size: number, maxParticles: number) {
 | 
				
			||||||
 | 
					        this.particlePool = new Array(poolSize);
 | 
				
			||||||
 | 
					        this.sourcePoint = sourcePoint;
 | 
				
			||||||
 | 
					        this.lifetime = lifetime;
 | 
				
			||||||
 | 
					        this.particleSize = new Vec2(size, size);
 | 
				
			||||||
 | 
					        this.maxLiveParticles = maxParticles;
 | 
				
			||||||
 | 
					        this.systemRunning = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ParticleSystemManager.getInstance().registerParticleSystem(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    initalizePool(scene: Scene, layer: string, type: ParticleSystemType, mass: number) {
 | 
				
			||||||
 | 
					        for (let i = 0; i < this.particlePool.length; i++) {
 | 
				
			||||||
 | 
					            this.particlePool[i] = <Particle>scene.add.graphic(GraphicType.PARTICLE, layer,
 | 
				
			||||||
 | 
					                { position: this.sourcePoint.clone(), size: this.particleSize.clone(), mass: mass });
 | 
				
			||||||
 | 
					            this.particlePool[i].addPhysics();
 | 
				
			||||||
 | 
					            this.particlePool[i].isCollidable = false;
 | 
				
			||||||
 | 
					            this.particlePool[i].visible = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    startSystem(time: number, startPoint?: Vec2) {
 | 
				
			||||||
 | 
					        this.systemLifetime = new Timer(time);
 | 
				
			||||||
 | 
					        this.systemLifetime.start();
 | 
				
			||||||
 | 
					        this.systemRunning = true;
 | 
				
			||||||
 | 
					        this.sourcePoint = startPoint;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    stopSystem() {
 | 
				
			||||||
 | 
					        console.log(this);
 | 
				
			||||||
 | 
					        this.systemRunning = false;
 | 
				
			||||||
 | 
					        for (let particle of this.particlePool) {
 | 
				
			||||||
 | 
					            if (particle.inUse) {
 | 
				
			||||||
 | 
					                particle.setParticleInactive();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    changeColor(color: Color) {
 | 
				
			||||||
 | 
					        this.color = color;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    update(deltaT: number) {
 | 
				
			||||||
 | 
					        if (!this.systemRunning) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (this.systemLifetime.isStopped()) {
 | 
				
			||||||
 | 
					            this.stopSystem();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            for (let particle of this.particlePool) {
 | 
				
			||||||
 | 
					                if (particle.inUse) {
 | 
				
			||||||
 | 
					                    particle.decrementAge(deltaT * 1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (particle.age <= 0) {
 | 
				
			||||||
 | 
					                        particle.setParticleInactive();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    //particle.vel.y += 200*deltaT;
 | 
				
			||||||
 | 
					                    particle.move(particle.vel.scaled(deltaT));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else {
 | 
				
			||||||
 | 
					                    particle.setParticleActive(this.lifetime, this.sourcePoint.clone());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    particle.color = this.color;
 | 
				
			||||||
 | 
					                    particle.alpha = 1;
 | 
				
			||||||
 | 
					                    //particle.size.set(1)
 | 
				
			||||||
 | 
					                    particle.vel = RandUtils.randVec(-50, 50, -100, 100);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    particle.tweens.add("active", {
 | 
				
			||||||
 | 
					                        startDelay: 0,
 | 
				
			||||||
 | 
					                        duration: 2000,
 | 
				
			||||||
 | 
					                        effects: [
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                property: "alpha",
 | 
				
			||||||
 | 
					                                resetOnComplete: true,
 | 
				
			||||||
 | 
					                                start: 1,
 | 
				
			||||||
 | 
					                                end: 0,
 | 
				
			||||||
 | 
					                                ease: EaseFunctionType.IN_OUT_SINE
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
 | 
					                            /*{
 | 
				
			||||||
 | 
					                                property: "colorR",
 | 
				
			||||||
 | 
					                                resetOnComplete: true,
 | 
				
			||||||
 | 
					                                start: particle.color.r,
 | 
				
			||||||
 | 
					                                end: 255,
 | 
				
			||||||
 | 
					                                ease: EaseFunctionType.IN_OUT_SINE
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                property: "colorG",
 | 
				
			||||||
 | 
					                                resetOnComplete: true,
 | 
				
			||||||
 | 
					                                start: particle.color.g,
 | 
				
			||||||
 | 
					                                end: 255,
 | 
				
			||||||
 | 
					                                ease: EaseFunctionType.IN_OUT_SINE
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                property: "colorB",
 | 
				
			||||||
 | 
					                                resetOnComplete: true,
 | 
				
			||||||
 | 
					                                start: particle.color.b,
 | 
				
			||||||
 | 
					                                end: 255,
 | 
				
			||||||
 | 
					                                ease: EaseFunctionType.IN_OUT_SINE
 | 
				
			||||||
 | 
					                            },*/
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                property: "velY",
 | 
				
			||||||
 | 
					                                resetOnComplete: true,
 | 
				
			||||||
 | 
					                                start: particle.vel.y,
 | 
				
			||||||
 | 
					                                end: particle.vel.y + ((this.lifetime * particle.mass)/2),
 | 
				
			||||||
 | 
					                                ease: EaseFunctionType.IN_OUT_SINE
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        ]
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    particle.tweens.play("active");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    //particle.vel = RandUtils.randVec(-150, 150, -100, 100);
 | 
				
			||||||
 | 
					                    //console.log(particle.vel.toString());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export enum ParticleSystemType {
 | 
				
			||||||
 | 
					    emitter = "emitter",
 | 
				
			||||||
 | 
					    burst = "burst"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										40
									
								
								src/Wolfie2D/Rendering/Animations/ParticleSystemManager.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/Wolfie2D/Rendering/Animations/ParticleSystemManager.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,40 @@
 | 
				
			||||||
 | 
					import Updateable from "../../DataTypes/Interfaces/Updateable";
 | 
				
			||||||
 | 
					import ParticleSystem from "./ParticleSystem";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class ParticleSystemManager implements Updateable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static instance: ParticleSystemManager = null;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    protected particleSystems: Array<ParticleSystem>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private constructor(){
 | 
				
			||||||
 | 
					        this.particleSystems = new Array();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static getInstance(): ParticleSystemManager {
 | 
				
			||||||
 | 
					        if(ParticleSystemManager.instance === null){
 | 
				
			||||||
 | 
					            ParticleSystemManager.instance = new ParticleSystemManager();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return ParticleSystemManager.instance;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    registerParticleSystem(system: ParticleSystem){
 | 
				
			||||||
 | 
					        this.particleSystems.push(system);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    deregisterParticleSystem(system: ParticleSystem){
 | 
				
			||||||
 | 
					        let index = this.particleSystems.indexOf(system);
 | 
				
			||||||
 | 
					        this.particleSystems.splice(index, 1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    clearParticleSystems(){
 | 
				
			||||||
 | 
					        this.particleSystems = new Array();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    update(deltaT: number): void {
 | 
				
			||||||
 | 
					        for(let particleSystem of this.particleSystems){
 | 
				
			||||||
 | 
					            particleSystem.update(deltaT);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,7 @@ import TextInput from "../../Nodes/UIElements/TextInput";
 | 
				
			||||||
import Rect from "../../Nodes/Graphics/Rect";
 | 
					import Rect from "../../Nodes/Graphics/Rect";
 | 
				
			||||||
import ResourceManager from "../../ResourceManager/ResourceManager";
 | 
					import ResourceManager from "../../ResourceManager/ResourceManager";
 | 
				
			||||||
import Line from "../../Nodes/Graphics/Line";
 | 
					import Line from "../../Nodes/Graphics/Line";
 | 
				
			||||||
 | 
					import Particle from "../../Nodes/Graphics/Particle";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// @ignorePage
 | 
					// @ignorePage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -143,6 +144,9 @@ export default class CanvasNodeFactory {
 | 
				
			||||||
			case GraphicType.RECT:
 | 
								case GraphicType.RECT:
 | 
				
			||||||
				instance = this.buildRect(options);
 | 
									instance = this.buildRect(options);
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
 | 
								case GraphicType.PARTICLE:
 | 
				
			||||||
 | 
									instance = this.buildParticle(options);
 | 
				
			||||||
 | 
									break;				
 | 
				
			||||||
			default:
 | 
								default:
 | 
				
			||||||
				throw `GraphicType '${type}' does not exist, or is registered incorrectly.`
 | 
									throw `GraphicType '${type}' does not exist, or is registered incorrectly.`
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -196,8 +200,18 @@ export default class CanvasNodeFactory {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	buildPoint(options?: Record<string, any>): Point {
 | 
						buildPoint(options?: Record<string, any>): Point {
 | 
				
			||||||
		this.checkIfPropExists("Point", options, "position", Vec2, "Vec2");
 | 
							this.checkIfPropExists("Point", options, "position", Vec2, "Vec2");
 | 
				
			||||||
 | 
							this.checkIfPropExists("Point", options, "size", Vec2, "Vec2");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return new Point(options.position);
 | 
							return new Point(options.position, options.size);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						buildParticle(options?: Record<string, any>): Point {
 | 
				
			||||||
 | 
							this.checkIfPropExists("Particle", options, "position", Vec2, "Vec2");
 | 
				
			||||||
 | 
							this.checkIfPropExists("Particle", options, "size", Vec2, "Vec2");
 | 
				
			||||||
 | 
							this.checkIfPropExists("Particle", options, "mass", "number", "number");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							//Changed for testing
 | 
				
			||||||
 | 
							return new Particle(options.position, options.size, options.mass);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	buildLine(options?: Record<string, any>): Point {
 | 
						buildLine(options?: Record<string, any>): Point {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,6 +25,7 @@ import RenderingManager from "../Rendering/RenderingManager";
 | 
				
			||||||
import Debug from "../Debug/Debug";
 | 
					import Debug from "../Debug/Debug";
 | 
				
			||||||
import TimerManager from "../Timing/TimerManager";
 | 
					import TimerManager from "../Timing/TimerManager";
 | 
				
			||||||
import TweenManager from "../Rendering/Animations/TweenManager";
 | 
					import TweenManager from "../Rendering/Animations/TweenManager";
 | 
				
			||||||
 | 
					import ParticleSystemManager from "../Rendering/Animations/ParticleSystemManager";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Scenes are the main container in the game engine.
 | 
					 * Scenes are the main container in the game engine.
 | 
				
			||||||
| 
						 | 
					@ -173,6 +174,9 @@ export default class Scene implements Updateable {
 | 
				
			||||||
        // Update all tweens
 | 
					        // Update all tweens
 | 
				
			||||||
        TweenManager.getInstance().update(deltaT);
 | 
					        TweenManager.getInstance().update(deltaT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Update all particle systems
 | 
				
			||||||
 | 
					        ParticleSystemManager.getInstance().update(deltaT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Update viewport
 | 
					        // Update viewport
 | 
				
			||||||
        this.viewport.update(deltaT);
 | 
					        this.viewport.update(deltaT);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,4 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Game from "./Wolfie2D/Loop/Game";
 | 
					import Game from "./Wolfie2D/Loop/Game";
 | 
				
			||||||
import default_scene from "./default_scene";
 | 
					import default_scene from "./default_scene";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user