]>
Commit | Line | Data |
---|---|---|
493ec31c | 1 | import { Bodies, Constraint } from 'matter-js'; |
0616b3f0 RBR |
2 | import { Entity } from '@serpentity/serpentity'; |
3 | ||
7ade6f8d RBR |
4 | // Components |
5 | ||
493ec31c RBR |
6 | import AngleComponent from '../components/angle'; |
7 | import BodyComponent from '../components/body'; | |
7ade6f8d | 8 | import ControlMapComponent from '../components/control_map'; |
493ec31c | 9 | import CoupledEntitiesComponent from '../components/coupled_entities'; |
7ade6f8d RBR |
10 | import DashComponent from '../components/dash'; |
11 | import ElasticComponent from '../components/elastic'; | |
12 | import ForceComponent from '../components/force'; | |
1676911c RBR |
13 | import GrabAreaComponent from '../components/grab_area'; |
14 | import GrabbableComponent from '../components/grabbable'; | |
15 | import GrabComponent from '../components/grab'; | |
7ade6f8d | 16 | import MaxVelocityComponent from '../components/max_velocity'; |
0616b3f0 RBR |
17 | import PositionComponent from '@serpentity/components.position'; |
18 | import PixiContainerComponent from '../components/pixi_container'; | |
7ade6f8d | 19 | |
0616b3f0 | 20 | import PixiFactory from '../factories/pixi'; |
7ade6f8d | 21 | import Config from '../config'; |
0616b3f0 | 22 | |
493ec31c RBR |
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 | ||
0616b3f0 RBR |
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 | ||
493ec31c RBR |
52 | // POSITION |
53 | ||
0616b3f0 | 54 | entity.addComponent(new PositionComponent(config.position)); |
493ec31c RBR |
55 | const position = entity.getComponent(PositionComponent); |
56 | ||
57 | entity.addComponent(new AngleComponent(config.angle)); | |
58 | const angle = entity.getComponent(AngleComponent); | |
59 | ||
7ade6f8d RBR |
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 | ||
493ec31c RBR |
71 | // RENDERING |
72 | ||
73 | const radius = 25; | |
0616b3f0 RBR |
74 | |
75 | const container = config.container || { | |
493ec31c | 76 | container: PixiFactory.createSumo({ radius }) |
0616b3f0 | 77 | }; |
0616b3f0 RBR |
78 | container.container.position.x = position.x; |
79 | container.container.position.y = position.y; | |
493ec31c RBR |
80 | container.container.rotation = angle.angle; |
81 | entity.addComponent(new PixiContainerComponent(container)); | |
82 | ||
83 | // PHYSICS | |
84 | ||
7ade6f8d | 85 | const frictionAir = 0.02; |
764ac76a RBR |
86 | const friction = 0.01; |
87 | const frictionStatic = 0.01; | |
6768cd46 RBR |
88 | const restitution = 1; |
89 | const density = 1.5; | |
493ec31c | 90 | |
7ade6f8d RBR |
91 | const body = Bodies.circle(position.x / Config.meterSize, position.y / Config.meterSize, radius / Config.meterSize, { |
92 | label: 'Sumo body', | |
493ec31c RBR |
93 | angle: angle.angle, |
94 | density, | |
95 | frictionAir, | |
7ade6f8d | 96 | frictionStatic, |
493ec31c RBR |
97 | friction, |
98 | restitution | |
99 | }); | |
100 | entity.addComponent(new BodyComponent({ body })); | |
101 | ||
1676911c RBR |
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 | ||
493ec31c RBR |
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 | } | |
0616b3f0 | 144 | |
493ec31c RBR |
145 | // RENDERING |
146 | ||
147 | const container = config.container || { | |
148 | container: PixiFactory.createEmptyGraphic() | |
149 | }; | |
0616b3f0 RBR |
150 | entity.addComponent(new PixiContainerComponent(container)); |
151 | ||
493ec31c RBR |
152 | // PHYSICS |
153 | ||
154 | const bodyA = config.entityA.getComponent(BodyComponent).body; | |
155 | const bodyB = config.entityB.getComponent(BodyComponent).body; | |
7ade6f8d RBR |
156 | const damping = 0; |
157 | const length = 100 / Config.meterSize; | |
158 | const stiffness = 0.001; | |
493ec31c RBR |
159 | |
160 | const body = Constraint.create({ | |
161 | bodyA, | |
162 | bodyB, | |
163 | damping, | |
164 | length, | |
7ade6f8d | 165 | stiffness |
493ec31c RBR |
166 | }); |
167 | entity.addComponent(new BodyComponent({ body })); | |
168 | ||
169 | entity.addComponent(new CoupledEntitiesComponent({ | |
170 | coupledEntities: [config.entityA, config.entityB] | |
171 | })); | |
172 | ||
7ade6f8d RBR |
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 | } | |
1676911c RBR |
253 | }, |
254 | { | |
255 | source: { | |
256 | type: 'keyboard', | |
257 | index: 88 // X | |
258 | }, | |
259 | target: { | |
260 | component: GrabComponent, | |
261 | property: 'grabbing' | |
262 | } | |
7ade6f8d RBR |
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 | ||
0616b3f0 RBR |
320 | if (engine) { |
321 | engine.addEntity(entity); | |
322 | } | |
323 | ||
6768cd46 RBR |
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 | ||
764ac76a RBR |
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 | ||
0616b3f0 RBR |
408 | return entity; |
409 | } | |
410 | }; |