]> git.r.bdr.sh - rbdr/sumo/blobdiff - lib/factories/sumo.js
Add gamepad support (#10)
[rbdr/sumo] / lib / factories / sumo.js
index d1f31f0628bf04ddcbbbb1b3ad9322e74737a982..18e9d72dfafacfd37455658b5cddc40f0db2bf14 100644 (file)
@@ -1,12 +1,24 @@
 import { Bodies, Constraint } from 'matter-js';
 import { Entity } from '@serpentity/serpentity';
 
 import { Bodies, Constraint } from 'matter-js';
 import { Entity } from '@serpentity/serpentity';
 
+// Components
+
 import AngleComponent from '../components/angle';
 import BodyComponent from '../components/body';
 import AngleComponent from '../components/angle';
 import BodyComponent from '../components/body';
+import ControlMapComponent from '../components/control_map';
 import CoupledEntitiesComponent from '../components/coupled_entities';
 import CoupledEntitiesComponent from '../components/coupled_entities';
+import DashComponent from '../components/dash';
+import ElasticComponent from '../components/elastic';
+import ForceComponent from '../components/force';
+import GrabAreaComponent from '../components/grab_area';
+import GrabbableComponent from '../components/grabbable';
+import GrabComponent from '../components/grab';
+import MaxVelocityComponent from '../components/max_velocity';
 import PositionComponent from '@serpentity/components.position';
 import PixiContainerComponent from '../components/pixi_container';
 import PositionComponent from '@serpentity/components.position';
 import PixiContainerComponent from '../components/pixi_container';
+
 import PixiFactory from '../factories/pixi';
 import PixiFactory from '../factories/pixi';
+import Config from '../config';
 
 const internals = {
   kNoEntityError: 'Entity Not Found: This method requires entityA and entityB to be set in the config.',
 
 const internals = {
   kNoEntityError: 'Entity Not Found: This method requires entityA and entityB to be set in the config.',
@@ -45,12 +57,26 @@ export default {
     entity.addComponent(new AngleComponent(config.angle));
     const angle = entity.getComponent(AngleComponent);
 
     entity.addComponent(new AngleComponent(config.angle));
     const angle = entity.getComponent(AngleComponent);
 
+    entity.addComponent(new ForceComponent(config.force));
+
+    config.maxVelocity = {
+      maxVelocity: 12
+    };
+    entity.addComponent(new MaxVelocityComponent(config.maxVelocity));
+
+    // CONTROLS & ABILITIES
+
+    entity.addComponent(new DashComponent(config.dash));
+
     // RENDERING
 
     const radius = 25;
     // RENDERING
 
     const radius = 25;
+    const pixiConfig = Object.assign({
+      radius
+    }, config.pixi);
 
     const container = config.container || {
 
     const container = config.container || {
-      container: PixiFactory.createSumo({ radius })
+      container: PixiFactory.createSumo(pixiConfig)
     };
     container.container.position.x = position.x;
     container.container.position.y = position.y;
     };
     container.container.position.x = position.x;
     container.container.position.y = position.y;
@@ -59,20 +85,35 @@ export default {
 
     // PHYSICS
 
 
     // PHYSICS
 
-    const frictionAir = 0.00001;
-    const friction = 0.00001;
-    const restitution = 0.9;
-    const density = 0.5;
+    const frictionAir = 0.02;
+    const friction = 0.01;
+    const frictionStatic = 0.01;
+    const restitution = 1;
+    const density = 1.5;
 
 
-    const body = Bodies.circle(position.x, position.y, radius, {
+    const body = Bodies.circle(position.x / Config.meterSize, position.y / Config.meterSize, radius / Config.meterSize, {
+      label: 'Sumo body',
       angle: angle.angle,
       density,
       frictionAir,
       angle: angle.angle,
       density,
       frictionAir,
+      frictionStatic,
       friction,
       restitution
     });
     entity.addComponent(new BodyComponent({ body }));
 
       friction,
       restitution
     });
     entity.addComponent(new BodyComponent({ body }));
 
+    // GRAB
+
+    const areaSizeFactor = 2; // Multiplier vs the radius
+    const area = Bodies.circle(position.x / Config.meterSize, position.y / Config.meterSize, (radius * areaSizeFactor) / Config.meterSize, {
+      label: 'Sumo Grab Area',
+      isSensor: true
+    });
+
+    entity.addComponent(new GrabAreaComponent({ area }));
+    entity.addComponent(new GrabComponent({ body }));
+    entity.addComponent(new GrabbableComponent({ body }));
+
     if (engine) {
       engine.addEntity(entity);
     }
     if (engine) {
       engine.addEntity(entity);
     }
@@ -115,18 +156,16 @@ export default {
 
     const bodyA = config.entityA.getComponent(BodyComponent).body;
     const bodyB = config.entityB.getComponent(BodyComponent).body;
 
     const bodyA = config.entityA.getComponent(BodyComponent).body;
     const bodyB = config.entityB.getComponent(BodyComponent).body;
-    const damping = 0.01;
-    const length = 100;
-    const stiffness = 0.0001;
-    const angularStiffness = 0.0001;
+    const damping = 0;
+    const length = 100 / Config.meterSize;
+    const stiffness = 0.001;
 
     const body = Constraint.create({
       bodyA,
       bodyB,
       damping,
       length,
 
     const body = Constraint.create({
       bodyA,
       bodyB,
       damping,
       length,
-      stiffness,
-      angularStiffness
+      stiffness
     });
     entity.addComponent(new BodyComponent({ body }));
 
     });
     entity.addComponent(new BodyComponent({ body }));
 
@@ -134,6 +173,467 @@ export default {
       coupledEntities: [config.entityA, config.entityB]
     }));
 
       coupledEntities: [config.entityA, config.entityB]
     }));
 
+    entity.addComponent(new ElasticComponent());
+
+    if (engine) {
+      engine.addEntity(entity);
+    }
+
+    return entity;
+  },
+
+  /**
+   * Creates a controllable sumo entity and adds it to the engine. Can override
+   * position in the config object
+   *
+   * @function createControllableSumo
+   * @memberof SumoFactory
+   * @param {external:Serpentity} [engine] the serpentity engine to attach
+   * to. If not sent, it will not be attached.
+   * @param {object} [config] the config to override the entity, accepts
+   * the key `position` as an object with an x and y property.
+   * @return {external:Serpentity.Entity} the created entity
+   */
+  createControllableSumo(engine, config = {}) {
+
+    const entity = this.createSumo(null, config);
+
+    entity.addComponent(new ControlMapComponent(config.controlMap));
+
+    if (engine) {
+      engine.addEntity(entity);
+    }
+
+    return entity;
+  },
+
+  /**
+   * Creates a controllable sumo entity and adds it to the engine. Can override
+   * position in the config object. Has contrrol scheme defaults for
+   * player 1
+   *
+   * @function createPlayer1Sumo
+   * @memberof SumoFactory
+   * @param {external:Serpentity} [engine] the serpentity engine to attach
+   * to. If not sent, it will not be attached.
+   * @param {object} [config] the config to override the entity, accepts
+   * the key `position` as an object with an x and y property.
+   * @return {external:Serpentity.Entity} the created entity
+   */
+  createPlayer1Sumo(engine, config = {}) {
+
+    const playerConfig = Object.assign({
+      controlMap: {
+        map: [
+          {
+            source: {
+              type: 'keyboard',
+              index: 65 // a
+            },
+            target: {
+              component: ForceComponent,
+              property: 'x',
+              value: (value) => -Number(value)
+            }
+          },
+          {
+            source: {
+              type: 'keyboard',
+              index: 68 // d
+            },
+            target: {
+              component: ForceComponent,
+              property: 'x',
+              value: (value) => Number(value)
+            }
+          },
+          {
+            source: {
+              type: 'keyboard',
+              index: 87 // w
+            },
+            target: {
+              component: ForceComponent,
+              property: 'y',
+              value: (value) => -Number(value)
+            }
+          },
+          {
+            source: {
+              type: 'keyboard',
+              index: 83 // s
+            },
+            target: {
+              component: ForceComponent,
+              property: 'y',
+              value: (value) => Number(value)
+            }
+          },
+          {
+            source: {
+              type: 'keyboard',
+              index: 90 // Z
+            },
+            target: {
+              component: DashComponent,
+              property: 'dashing'
+            }
+          },
+          {
+            source: {
+              type: 'keyboard',
+              index: 88 // X
+            },
+            target: {
+              component: GrabComponent,
+              property: 'grabbing'
+            }
+          },
+          {
+            source: {
+              type: 'gamepad',
+              gamepadNumber: 0,
+              gamepadInputSource: 'axes',
+              index: 0 // left stick horizontal
+            },
+            target: {
+              component: ForceComponent,
+              property: 'x',
+              value: (value) => Number(value)
+            }
+          },
+          {
+            source: {
+              type: 'gamepad',
+              gamepadNumber: 0,
+              gamepadInputSource: 'axes',
+              index: 1 // left stick vertical
+            },
+            target: {
+              component: ForceComponent,
+              property: 'y',
+              value: (value) => Number(value)
+            }
+          },
+          {
+            source: {
+              type: 'gamepad',
+              gamepadNumber: 0,
+              gamepadInputSource: 'buttons',
+              index: 2 // left face button
+            },
+            target: {
+              component: DashComponent,
+              property: 'dashing',
+              value: (value) => value.value
+            }
+          },
+          {
+            source: {
+              type: 'gamepad',
+              gamepadNumber: 0,
+              gamepadInputSource: 'buttons',
+              index: 0 // bottom face button
+            },
+            target: {
+              component: GrabComponent,
+              property: 'grabbing',
+              value: (value) => value.value
+            }
+          }
+        ]
+      }
+    }, config);
+
+    const entity = this.createControllableSumo(null, playerConfig);
+
+    if (engine) {
+      engine.addEntity(entity);
+    }
+
+    return entity;
+  },
+
+  /**
+   * Creates a controllable sumo entity and adds it to the engine. Can override
+   * position in the config object. Has contrrol scheme defaults for
+   * player 2
+   *
+   * @function createPlayer2Sumo
+   * @memberof SumoFactory
+   * @param {external:Serpentity} [engine] the serpentity engine to attach
+   * to. If not sent, it will not be attached.
+   * @param {object} [config] the config to override the entity, accepts
+   * the key `position` as an object with an x and y property.
+   * @return {external:Serpentity.Entity} the created entity
+   */
+  createPlayer2Sumo(engine, config = {}) {
+
+    const playerConfig = Object.assign({
+      pixi: {
+        color: 0xeaacac
+      },
+      controlMap: {
+        map: [
+          {
+            source: {
+              type: 'keyboard',
+              index: 37 // left arrow
+            },
+            target: {
+              component: ForceComponent,
+              property: 'x',
+              value: (value) => -Number(value)
+            }
+          },
+          {
+            source: {
+              type: 'keyboard',
+              index: 39 // right arrow
+            },
+            target: {
+              component: ForceComponent,
+              property: 'x',
+              value: (value) => Number(value)
+            }
+          },
+          {
+            source: {
+              type: 'keyboard',
+              index: 38 // up arrow
+            },
+            target: {
+              component: ForceComponent,
+              property: 'y',
+              value: (value) => -Number(value)
+            }
+          },
+          {
+            source: {
+              type: 'keyboard',
+              index: 40 // down arrow
+            },
+            target: {
+              component: ForceComponent,
+              property: 'y',
+              value: (value) => Number(value)
+            }
+          },
+          {
+            source: {
+              type: 'keyboard',
+              index: 188 // ,
+            },
+            target: {
+              component: DashComponent,
+              property: 'dashing'
+            }
+          },
+          {
+            source: {
+              type: 'keyboard',
+              index: 190 // .
+            },
+            target: {
+              component: GrabComponent,
+              property: 'grabbing'
+            }
+          },
+          {
+            source: {
+              type: 'gamepad',
+              gamepadNumber: 1,
+              gamepadInputSource: 'axes',
+              index: 0 // left stick horizontal
+            },
+            target: {
+              component: ForceComponent,
+              property: 'x',
+              value: (value) => Number(value)
+            }
+          },
+          {
+            source: {
+              type: 'gamepad',
+              gamepadNumber: 1,
+              gamepadInputSource: 'axes',
+              index: 1 // left stick vertical
+            },
+            target: {
+              component: ForceComponent,
+              property: 'y',
+              value: (value) => Number(value)
+            }
+          },
+          {
+            source: {
+              type: 'gamepad',
+              gamepadNumber: 1,
+              gamepadInputSource: 'buttons',
+              index: 2 // left face button
+            },
+            target: {
+              component: DashComponent,
+              property: 'dashing',
+              value: (value) => value.value
+            }
+          },
+          {
+            source: {
+              type: 'gamepad',
+              gamepadNumber: 1,
+              gamepadInputSource: 'buttons',
+              index: 0 // bottom face button
+            },
+            target: {
+              component: GrabComponent,
+              property: 'grabbing',
+              value: (value) => value.value
+            }
+          }
+        ]
+      }
+    }, config);
+
+    const entity = this.createControllableSumo(null, playerConfig);
+
+    if (engine) {
+      engine.addEntity(entity);
+    }
+
+    return entity;
+  },
+
+  /**
+   * Creates a static harness entity
+   *
+   * @function createHarness
+   * @memberof SumoFactory
+   * @param {external:Serpentity} [engine] the serpentity engine to attach
+   * to. If not sent, it will not be attached.
+   * @param {object} [config] the config to override the entity, accepts
+   * the key `position` as an object with an x and y property.
+   * @return {external:Serpentity.Entity} the created entity
+   */
+  createHarness(engine, config = {}) {
+
+    const entity = new Entity();
+
+    // POSITION
+
+    entity.addComponent(new PositionComponent(config.position));
+    const position = entity.getComponent(PositionComponent);
+
+    // RENDERING
+
+    const radius = 15;
+
+    const container = config.container || {
+      container: PixiFactory.createHarness({ radius })
+    };
+    container.container.position.x = position.x;
+    container.container.position.y = position.y;
+    entity.addComponent(new PixiContainerComponent(container));
+
+    // PHYSICS
+
+    const friction = 0;
+    const frictionStatic = 0;
+    const restitution = 1;
+
+    const body = Bodies.circle(position.x / Config.meterSize, position.y / Config.meterSize, radius / Config.meterSize, {
+      isStatic: true,
+      label: 'Harness body',
+      friction,
+      restitution,
+      frictionStatic
+    });
+    entity.addComponent(new BodyComponent({ body }));
+
+    if (engine) {
+      engine.addEntity(entity);
+    }
+
+    return entity;
+  },
+
+  /**
+   * Creates a static arena entity
+   *
+   * @function createArena
+   * @memberof SumoFactory
+   * @param {external:Serpentity} [engine] the serpentity engine to attach
+   * to. If not sent, it will not be attached.
+   * @param {object} [config] the config to override the entity, accepts
+   * the key `position` as an object with an x and y property.
+   * @return {external:Serpentity.Entity} the created entity
+   */
+  createArena(engine, config = {}) {
+
+    const entity = new Entity();
+
+    // POSITION
+
+    entity.addComponent(new PositionComponent(config.position));
+    const position = entity.getComponent(PositionComponent);
+
+    // RENDERING
+
+    const radius = 300;
+
+    const container = config.container || {
+      container: PixiFactory.createArena({ radius })
+    };
+    container.container.position.x = position.x;
+    container.container.position.y = position.y;
+    entity.addComponent(new PixiContainerComponent(container));
+
+    if (engine) {
+      engine.addEntity(entity);
+    }
+
+    return entity;
+  },
+
+  /**
+   * Creates an invisible block
+   *
+   * @function createInvisibleBlock
+   * @memberof SumoFactory
+   * @param {external:Serpentity} [engine] the serpentity engine to attach
+   * to. If not sent, it will not be attached.
+   * @param {object} [config] the config to override the entity, accepts
+   * the key `position` as an object with an x and y property.
+   * @return {external:Serpentity.Entity} the created entity
+   */
+  createInvisibleBlock(engine, config = {}) {
+
+    const entity = new Entity();
+
+    // POSITION
+
+    entity.addComponent(new PositionComponent(config.position));
+    const position = entity.getComponent(PositionComponent);
+
+    // PHYSICS
+
+    const friction = 0;
+    const frictionStatic = 0;
+    const restitution = 1;
+
+    const body = Bodies.rectangle(position.x / Config.meterSize,
+      position.y / Config.meterSize,
+      config.width / Config.meterSize,
+      config.height / Config.meterSize,
+      {
+        isStatic: true,
+        label: 'Invisible Block',
+        friction,
+        restitution,
+        frictionStatic
+      });
+    entity.addComponent(new BodyComponent({ body }));
+
     if (engine) {
       engine.addEntity(entity);
     }
     if (engine) {
       engine.addEntity(entity);
     }