]> git.r.bdr.sh - rbdr/sumo/blame_incremental - lib/systems/grab.js
Deploy to Pages
[rbdr/sumo] / lib / systems / grab.js
... / ...
CommitLineData
1import { System } from '@serpentity/serpentity';
2import { Body, Constraint, SAT, World } from 'matter-js';
3
4import GrabberNode from '../nodes/grabber';
5import GrabbableNode from '../nodes/grabbable';
6
7import Config from '../config';
8
9const internals = {
10 kGrabRadius: 50,
11 kNoEngine: 'No matter js physics engine found. Make sure you set the `engine` key in the config object when initializing.'
12};
13
14/**
15 * Handles grabbing between entities
16 *
17 * @extends {external:Serpentity.System}
18 * @class GrabSystem
19 * @param {object} config a configuration object to extend.
20 */
21export default class GrabSystem extends System {
22
23 constructor(config = {}) {
24
25 super();
26
27 /**
28 * The node collection of grabbers
29 *
30 * @property {external:Serpentity.NodeCollection} grabbers
31 * @instance
32 * @memberof GrabSystem
33 */
34 this.grabbers = null;
35
36 /**
37 * The node collection of grabbables
38 *
39 * @property {external:Serpentity.NodeCollection} grabbables
40 * @instance
41 * @memberof GrabSystem
42 */
43 this.grabbables = null;
44
45 /**
46 * The matter-js engine we will use to add and remove constraints
47 *
48 * @property {external:MatterJs.Engine} engine
49 * @instance
50 * @memberof GrabSystem
51 */
52 this.engine = config.engine;
53
54 if (!this.engine) {
55 throw new Error(internals.kNoEngine);
56 }
57 }
58
59 /**
60 * Initializes system when added. Requests grabber and grabbable nodes
61 *
62 * @function added
63 * @memberof GrabSystem
64 * @instance
65 * @param {external:Serpentity.Engine} engine the serpentity engine to
66 * which we are getting added
67 */
68 added(engine) {
69
70 this.grabbers = engine.getNodes(GrabberNode);
71 this.grabbables = engine.getNodes(GrabbableNode);
72 }
73
74 /**
75 * Clears system resources when removed.
76 *
77 * @function removed
78 * @instance
79 * @memberof GrabSystem
80 */
81 removed() {
82
83 this.grabbers = null;
84 this.grabbables = null;
85 }
86
87 /**
88 * Runs on every update of the loop. Triggers grab and manages cooldown
89 *
90 * @function update
91 * @instance
92 * @param {Number} currentFrameDuration the duration of the current
93 * frame
94 * @memberof GrabSystem
95 */
96 update(currentFrameDuration) {
97
98 for (const grabber of this.grabbers) {
99
100 const grab = grabber.grab;
101
102 if (grab.grabbing && !grab.locked) {
103 this._grab(grabber);
104 }
105
106 const isGrabReleased = !grab.grabbing || grab.currentCooldown >= grab.cooldown;
107 if (grab.constraint && isGrabReleased) {
108 this._release(grabber);
109 }
110
111 if (!grab.grabbing && grab.locked && grab.currentCooldown >= grab.cooldown) {
112 this._unlock(grabber);
113 }
114
115 if (grab.locked) {
116 grab.currentCooldown += currentFrameDuration;
117 }
118
119 grab.grabbing = 0;
120 }
121 }
122
123 // Executes the dash action
124
125 _grab(grabber) {
126
127 const grab = grabber.grab;
128
129 grab.locked = true;
130 grab.currentCooldown = 0;
131
132 Body.setPosition(grabber.grabArea.area, grabber.body.body.position);
133
134 for (const grabbable of this.grabbables) {
135
136 if (grabbable.entity === grabber.entity) {
137 continue;
138 }
139
140 const collision = SAT.collides(grabber.grabArea.area, grabbable.body.body);
141 if (collision.collided) {
142 grab.constraint = this._createConstraint(grabber.body.body, grabbable.body.body);
143 }
144 }
145 }
146
147 // Executes the unlock action
148
149 _unlock(grabber) {
150
151 grabber.grab.locked = false;
152 }
153
154 // Releases a constraint
155
156 _release(grabber) {
157
158 World.remove(this.engine.world, grabber.grab.constraint);
159 grabber.grab.currentCooldown = 0;
160 grabber.grab.constraint = null;
161 }
162
163 // Performs a grab between two entities
164
165 _createConstraint(grabber, grabbable) {
166
167 const constraint = Constraint.create({ // Attach the sensor to the body
168 bodyA: grabber,
169 bodyB: grabbable,
170 damping: 0,
171 length: internals.kGrabRadius / Config.meterSize,
172 stiffness: 1
173 });
174
175 World.add(this.engine.world, [constraint]);
176
177 return constraint;
178 }
179};