X-Git-Url: https://git.r.bdr.sh/rbdr/sumo/blobdiff_plain/0616b3f00653c66b5e34814653e33413b9ec034e..1676911c8a2621274bf75ff7271faa926cf58d6c:/lib/factories/sumo.js diff --git a/lib/factories/sumo.js b/lib/factories/sumo.js index b0e5416..7b50f8a 100644 --- a/lib/factories/sumo.js +++ b/lib/factories/sumo.js @@ -1,8 +1,29 @@ +import { Bodies, Constraint } from 'matter-js'; import { Entity } from '@serpentity/serpentity'; +// Components + +import AngleComponent from '../components/angle'; +import BodyComponent from '../components/body'; +import ControlMapComponent from '../components/control_map'; +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 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.', + kEntityHasNoBodyError: 'Entity Has No Body: This method requires entities have a BodyComponent.' +}; /** * Factory object that contains many methods to create prefab entities. @@ -28,24 +49,362 @@ export default { const entity = new Entity(); + // POSITION + entity.addComponent(new PositionComponent(config.position)); + const position = entity.getComponent(PositionComponent); + + 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; const container = config.container || { - container: PixiFactory.createSumo() + container: PixiFactory.createSumo({ radius }) }; + container.container.position.x = position.x; + container.container.position.y = position.y; + container.container.rotation = angle.angle; + entity.addComponent(new PixiContainerComponent(container)); + + // PHYSICS + + 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 / Config.meterSize, position.y / Config.meterSize, radius / Config.meterSize, { + label: 'Sumo body', + angle: angle.angle, + density, + frictionAir, + frictionStatic, + 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); + } + + return entity; + }, + + /** + * Creates a rubber band entity and adds it to the engine. + * + * @function createRubberBand + * @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, it + * must include entityA and entityB which will be tied by it. If they + * are not sent or don't have a physics body, this will throw. + * @return {external:Serpentity.Entity} the created entity + */ + createRubberBand(engine, config = {}) { + + const entity = new Entity(); + + if (!config.entityA || !config.entityB) { + throw new Error(internals.kNoEntityError); + } + + if (!config.entityA.hasComponent(BodyComponent) || !config.entityB.hasComponent(BodyComponent)) { + throw new Error(internals.kEntityHasNoBodyError); + } + + // RENDERING + + const container = config.container || { + container: PixiFactory.createEmptyGraphic() + }; + entity.addComponent(new PixiContainerComponent(container)); + + // PHYSICS + + const bodyA = config.entityA.getComponent(BodyComponent).body; + const bodyB = config.entityB.getComponent(BodyComponent).body; + const damping = 0; + const length = 100 / Config.meterSize; + const stiffness = 0.001; + + const body = Constraint.create({ + bodyA, + bodyB, + damping, + length, + stiffness + }); + entity.addComponent(new BodyComponent({ body })); + + entity.addComponent(new CoupledEntitiesComponent({ + 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({ + 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: 90 // Z + }, + target: { + component: DashComponent, + property: 'dashing' + } + }, + { + source: { + type: 'keyboard', + index: 88 // X + }, + target: { + component: GrabComponent, + property: 'grabbing' + } + } + ] + })); + + 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(); - // Match the symbol position with the entity position on init - // Otherwise it's up to systems + // 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); + } + return entity; } };