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