]> git.r.bdr.sh - rbdr/sumo/blob - lib/factories/sumo.js
7b50f8ae50493ce087d8d8f570e23a12c08f7a9a
[rbdr/sumo] / lib / factories / sumo.js
1 import { Bodies, Constraint } from 'matter-js';
2 import { Entity } from '@serpentity/serpentity';
3
4 // Components
5
6 import AngleComponent from '../components/angle';
7 import BodyComponent from '../components/body';
8 import ControlMapComponent from '../components/control_map';
9 import CoupledEntitiesComponent from '../components/coupled_entities';
10 import DashComponent from '../components/dash';
11 import ElasticComponent from '../components/elastic';
12 import ForceComponent from '../components/force';
13 import GrabAreaComponent from '../components/grab_area';
14 import GrabbableComponent from '../components/grabbable';
15 import GrabComponent from '../components/grab';
16 import MaxVelocityComponent from '../components/max_velocity';
17 import PositionComponent from '@serpentity/components.position';
18 import PixiContainerComponent from '../components/pixi_container';
19
20 import PixiFactory from '../factories/pixi';
21 import Config from '../config';
22
23 const internals = {
24 kNoEntityError: 'Entity Not Found: This method requires entityA and entityB to be set in the config.',
25 kEntityHasNoBodyError: 'Entity Has No Body: This method requires entities have a BodyComponent.'
26 };
27
28 /**
29 * Factory object that contains many methods to create prefab entities.
30 *
31 * @type object
32 * @name SumoFactory
33 */
34 export default {
35
36 /**
37 * Creates a sumo entity and adds it to the engine. Can override
38 * position in the config object
39 *
40 * @function createSumo
41 * @memberof SumoFactory
42 * @param {external:Serpentity} [engine] the serpentity engine to attach
43 * to. If not sent, it will not be attached.
44 * @param {object} [config] the config to override the entity, accepts
45 * the key `position` as an object with an x and y property.
46 * @return {external:Serpentity.Entity} the created entity
47 */
48 createSumo(engine, config = {}) {
49
50 const entity = new Entity();
51
52 // POSITION
53
54 entity.addComponent(new PositionComponent(config.position));
55 const position = entity.getComponent(PositionComponent);
56
57 entity.addComponent(new AngleComponent(config.angle));
58 const angle = entity.getComponent(AngleComponent);
59
60 entity.addComponent(new ForceComponent(config.force));
61
62 config.maxVelocity = {
63 maxVelocity: 12
64 };
65 entity.addComponent(new MaxVelocityComponent(config.maxVelocity));
66
67 // CONTROLS & ABILITIES
68
69 entity.addComponent(new DashComponent(config.dash));
70
71 // RENDERING
72
73 const radius = 25;
74
75 const container = config.container || {
76 container: PixiFactory.createSumo({ radius })
77 };
78 container.container.position.x = position.x;
79 container.container.position.y = position.y;
80 container.container.rotation = angle.angle;
81 entity.addComponent(new PixiContainerComponent(container));
82
83 // PHYSICS
84
85 const frictionAir = 0.02;
86 const friction = 0.01;
87 const frictionStatic = 0.01;
88 const restitution = 1;
89 const density = 1.5;
90
91 const body = Bodies.circle(position.x / Config.meterSize, position.y / Config.meterSize, radius / Config.meterSize, {
92 label: 'Sumo body',
93 angle: angle.angle,
94 density,
95 frictionAir,
96 frictionStatic,
97 friction,
98 restitution
99 });
100 entity.addComponent(new BodyComponent({ body }));
101
102 // GRAB
103
104 const areaSizeFactor = 2; // Multiplier vs the radius
105 const area = Bodies.circle(position.x / Config.meterSize, position.y / Config.meterSize, (radius * areaSizeFactor) / Config.meterSize, {
106 label: 'Sumo Grab Area',
107 isSensor: true
108 });
109
110 entity.addComponent(new GrabAreaComponent({ area }));
111 entity.addComponent(new GrabComponent({ body }));
112 entity.addComponent(new GrabbableComponent({ body }));
113
114 if (engine) {
115 engine.addEntity(entity);
116 }
117
118 return entity;
119 },
120
121 /**
122 * Creates a rubber band entity and adds it to the engine.
123 *
124 * @function createRubberBand
125 * @memberof SumoFactory
126 * @param {external:Serpentity} [engine] the serpentity engine to attach
127 * to. If not sent, it will not be attached.
128 * @param {object} [config] the config to override the entity, it
129 * must include entityA and entityB which will be tied by it. If they
130 * are not sent or don't have a physics body, this will throw.
131 * @return {external:Serpentity.Entity} the created entity
132 */
133 createRubberBand(engine, config = {}) {
134
135 const entity = new Entity();
136
137 if (!config.entityA || !config.entityB) {
138 throw new Error(internals.kNoEntityError);
139 }
140
141 if (!config.entityA.hasComponent(BodyComponent) || !config.entityB.hasComponent(BodyComponent)) {
142 throw new Error(internals.kEntityHasNoBodyError);
143 }
144
145 // RENDERING
146
147 const container = config.container || {
148 container: PixiFactory.createEmptyGraphic()
149 };
150 entity.addComponent(new PixiContainerComponent(container));
151
152 // PHYSICS
153
154 const bodyA = config.entityA.getComponent(BodyComponent).body;
155 const bodyB = config.entityB.getComponent(BodyComponent).body;
156 const damping = 0;
157 const length = 100 / Config.meterSize;
158 const stiffness = 0.001;
159
160 const body = Constraint.create({
161 bodyA,
162 bodyB,
163 damping,
164 length,
165 stiffness
166 });
167 entity.addComponent(new BodyComponent({ body }));
168
169 entity.addComponent(new CoupledEntitiesComponent({
170 coupledEntities: [config.entityA, config.entityB]
171 }));
172
173 entity.addComponent(new ElasticComponent());
174
175 if (engine) {
176 engine.addEntity(entity);
177 }
178
179 return entity;
180 },
181
182 /**
183 * Creates a controllable sumo entity and adds it to the engine. Can override
184 * position in the config object
185 *
186 * @function createControllableSumo
187 * @memberof SumoFactory
188 * @param {external:Serpentity} [engine] the serpentity engine to attach
189 * to. If not sent, it will not be attached.
190 * @param {object} [config] the config to override the entity, accepts
191 * the key `position` as an object with an x and y property.
192 * @return {external:Serpentity.Entity} the created entity
193 */
194 createControllableSumo(engine, config = {}) {
195
196 const entity = this.createSumo(null, config);
197
198 entity.addComponent(new ControlMapComponent({
199 map: [
200 {
201 source: {
202 type: 'keyboard',
203 index: 37 // left arrow
204 },
205 target: {
206 component: ForceComponent,
207 property: 'x',
208 value: (value) => -Number(value)
209 }
210 },
211 {
212 source: {
213 type: 'keyboard',
214 index: 39 // right arrow
215 },
216 target: {
217 component: ForceComponent,
218 property: 'x',
219 value: (value) => Number(value)
220 }
221 },
222 {
223 source: {
224 type: 'keyboard',
225 index: 38 // up arrow
226 },
227 target: {
228 component: ForceComponent,
229 property: 'y',
230 value: (value) => -Number(value)
231 }
232 },
233 {
234 source: {
235 type: 'keyboard',
236 index: 40 // down arrow
237 },
238 target: {
239 component: ForceComponent,
240 property: 'y',
241 value: (value) => Number(value)
242 }
243 },
244 {
245 source: {
246 type: 'keyboard',
247 index: 90 // Z
248 },
249 target: {
250 component: DashComponent,
251 property: 'dashing'
252 }
253 },
254 {
255 source: {
256 type: 'keyboard',
257 index: 88 // X
258 },
259 target: {
260 component: GrabComponent,
261 property: 'grabbing'
262 }
263 }
264 ]
265 }));
266
267 if (engine) {
268 engine.addEntity(entity);
269 }
270
271 return entity;
272 },
273
274 /**
275 * Creates a static harness entity
276 *
277 * @function createHarness
278 * @memberof SumoFactory
279 * @param {external:Serpentity} [engine] the serpentity engine to attach
280 * to. If not sent, it will not be attached.
281 * @param {object} [config] the config to override the entity, accepts
282 * the key `position` as an object with an x and y property.
283 * @return {external:Serpentity.Entity} the created entity
284 */
285 createHarness(engine, config = {}) {
286
287 const entity = new Entity();
288
289 // POSITION
290
291 entity.addComponent(new PositionComponent(config.position));
292 const position = entity.getComponent(PositionComponent);
293
294 // RENDERING
295
296 const radius = 15;
297
298 const container = config.container || {
299 container: PixiFactory.createHarness({ radius })
300 };
301 container.container.position.x = position.x;
302 container.container.position.y = position.y;
303 entity.addComponent(new PixiContainerComponent(container));
304
305 // PHYSICS
306
307 const friction = 0;
308 const frictionStatic = 0;
309 const restitution = 1;
310
311 const body = Bodies.circle(position.x / Config.meterSize, position.y / Config.meterSize, radius / Config.meterSize, {
312 isStatic: true,
313 label: 'Harness body',
314 friction,
315 restitution,
316 frictionStatic
317 });
318 entity.addComponent(new BodyComponent({ body }));
319
320 if (engine) {
321 engine.addEntity(entity);
322 }
323
324 return entity;
325 },
326
327 /**
328 * Creates a static arena entity
329 *
330 * @function createArena
331 * @memberof SumoFactory
332 * @param {external:Serpentity} [engine] the serpentity engine to attach
333 * to. If not sent, it will not be attached.
334 * @param {object} [config] the config to override the entity, accepts
335 * the key `position` as an object with an x and y property.
336 * @return {external:Serpentity.Entity} the created entity
337 */
338 createArena(engine, config = {}) {
339
340 const entity = new Entity();
341
342 // POSITION
343
344 entity.addComponent(new PositionComponent(config.position));
345 const position = entity.getComponent(PositionComponent);
346
347 // RENDERING
348
349 const radius = 300;
350
351 const container = config.container || {
352 container: PixiFactory.createArena({ radius })
353 };
354 container.container.position.x = position.x;
355 container.container.position.y = position.y;
356 entity.addComponent(new PixiContainerComponent(container));
357
358 if (engine) {
359 engine.addEntity(entity);
360 }
361
362 return entity;
363 },
364
365 /**
366 * Creates an invisible block
367 *
368 * @function createInvisibleBlock
369 * @memberof SumoFactory
370 * @param {external:Serpentity} [engine] the serpentity engine to attach
371 * to. If not sent, it will not be attached.
372 * @param {object} [config] the config to override the entity, accepts
373 * the key `position` as an object with an x and y property.
374 * @return {external:Serpentity.Entity} the created entity
375 */
376 createInvisibleBlock(engine, config = {}) {
377
378 const entity = new Entity();
379
380 // POSITION
381
382 entity.addComponent(new PositionComponent(config.position));
383 const position = entity.getComponent(PositionComponent);
384
385 // PHYSICS
386
387 const friction = 0;
388 const frictionStatic = 0;
389 const restitution = 1;
390
391 const body = Bodies.rectangle(position.x / Config.meterSize,
392 position.y / Config.meterSize,
393 config.width / Config.meterSize,
394 config.height / Config.meterSize,
395 {
396 isStatic: true,
397 label: 'Invisible Block',
398 friction,
399 restitution,
400 frictionStatic
401 });
402 entity.addComponent(new BodyComponent({ body }));
403
404 if (engine) {
405 engine.addEntity(entity);
406 }
407
408 return entity;
409 }
410 };