aboutsummaryrefslogtreecommitdiff
path: root/lib/systems
diff options
context:
space:
mode:
authorRubén Beltrán del Río <ben@nsovocal.com>2018-05-29 02:34:38 -0500
committerGitHub <noreply@github.com>2018-05-29 02:34:38 -0500
commit3100e0533cb89a185ea021dfb83c4f364750180f (patch)
treea957c7efc0b1d1e5f1ddafd69e2bfd39c70b50df /lib/systems
parent43413defcb854c8706a84a6a54a61c7977b52614 (diff)
Add Naive Game Rules (#11)
* Add components for tracking points * Add methods to create new entities * Adds nodes for points * Add system to detect points * Adds system to detect a winner * Add system to render points and winner * Add points required to win to config * Add it all to the engine * Add mention of points to changelog
Diffstat (limited to 'lib/systems')
-rw-r--r--lib/systems/detect_points_collision.js100
-rw-r--r--lib/systems/detect_winner.js104
-rw-r--r--lib/systems/render_points.js114
-rw-r--r--lib/systems/render_winner.js114
4 files changed, 432 insertions, 0 deletions
diff --git a/lib/systems/detect_points_collision.js b/lib/systems/detect_points_collision.js
new file mode 100644
index 0000000..c84f3fc
--- /dev/null
+++ b/lib/systems/detect_points_collision.js
@@ -0,0 +1,100 @@
+import { System } from '@serpentity/serpentity';
+import { SAT } from 'matter-js';
+
+import BodyComponent from '../components/body';
+import PointsColliderNode from '../nodes/points_collider';
+import PointsNode from '../nodes/points';
+
+/**
+ * Handles grabbing between entities
+ *
+ * @extends {external:Serpentity.System}
+ * @class DetectPointsCollisionSystem
+ * @param {object} config a configuration object to extend.
+ */
+export default class DetectPointsCollisionSystem extends System {
+
+ constructor(config = {}) {
+
+ super();
+
+ /**
+ * The collection of points colliders
+ *
+ * @property {external:Serpentity.NodeCollection} pointsColliders
+ * @instance
+ * @memberof DetectPointsCollisionSystem
+ */
+ this.pointsColliders = null;
+
+ /**
+ * The collection of points keepers
+ *
+ * @property {external:Serpentity.NodeCollection} pointsKeepers
+ * @instance
+ * @memberof DetectPointsCollisionSystem
+ */
+ this.pointsKeepers = null;
+ }
+
+ /**
+ * Initializes system when added. Requests points colliders and points
+ * accumulators
+ *
+ * @function added
+ * @memberof DetectPointsCollisionSystem
+ * @instance
+ * @param {external:Serpentity.Engine} engine the serpentity engine to
+ * which we are getting added
+ */
+ added(engine) {
+
+ this.pointsCollider = engine.getNodes(PointsColliderNode);
+ this.pointsKeepers = engine.getNodes(PointsNode);
+ }
+
+ /**
+ * Clears system resources when removed.
+ *
+ * @function removed
+ * @instance
+ * @memberof DetectPointsCollisionSystem
+ */
+ removed() {
+
+ this.pointsCollider = null;
+ this.pointsKeepers = null;
+ }
+
+ /**
+ * Runs on every update of the loop. Triggers grab and manages cooldown
+ *
+ * @function update
+ * @instance
+ * @param {Number} currentFrameDuration the duration of the current
+ * frame
+ * @memberof DetectPointsCollisionSystem
+ */
+ update(currentFrameDuration) {
+
+ const points = {};
+
+ for (const collider of this.pointsCollider) {
+ const collisionTargetBody = collider.pointsCollider.collisionTarget.getComponent(BodyComponent);
+ const pointsTarget = collider.pointsCollider.pointsTarget;
+
+ if (collisionTargetBody) {
+ const collision = SAT.collides(collider.body.body, collisionTargetBody.body);
+ if (collision.collided) {
+ points[pointsTarget] = (points[pointsTarget] || 0) + 1;
+ }
+ }
+ }
+
+ for (const pointsKeeper of this.pointsKeepers) {
+ for (const pointsLabel of Object.keys(points)) {
+ pointsKeeper.points[pointsLabel] = (pointsKeeper.points[pointsLabel] || 0) + points[pointsLabel];
+ }
+ }
+ }
+};
diff --git a/lib/systems/detect_winner.js b/lib/systems/detect_winner.js
new file mode 100644
index 0000000..e7fbd76
--- /dev/null
+++ b/lib/systems/detect_winner.js
@@ -0,0 +1,104 @@
+import { System } from '@serpentity/serpentity';
+
+import WinnerNode from '../nodes/winner';
+import PointsNode from '../nodes/points';
+
+import Config from '../config';
+
+/**
+ * Handles finding the winner
+ *
+ * @extends {external:Serpentity.System}
+ * @class DetectWinnerSystem
+ * @param {object} config a configuration object to extend.
+ */
+export default class DetectWinnerSystem extends System {
+
+ constructor(config = {}) {
+
+ super();
+
+ /**
+ * The collection of points keepers
+ *
+ * @property {external:Serpentity.NodeCollection} pointsKeepers
+ * @instance
+ * @memberof DetectWinnerSystem
+ */
+ this.pointsKeepers = null;
+
+ /**
+ * The collection of winner keepers
+ *
+ * @property {external:Serpentity.NodeCollection} winnerKeepers
+ * @instance
+ * @memberof DetectWinnerSystem
+ */
+ this.winnerKeepers = null;
+ }
+
+ /**
+ * Initializes system when added. Requests points and winner keepers
+ *
+ * @function added
+ * @memberof DetectWinnerSystem
+ * @instance
+ * @param {external:Serpentity.Engine} engine the serpentity engine to
+ * which we are getting added
+ */
+ added(engine) {
+
+ this.pointsKeepers = engine.getNodes(PointsNode);
+ this.winnerKeepers = engine.getNodes(WinnerNode);
+ }
+
+ /**
+ * Clears system resources when removed.
+ *
+ * @function removed
+ * @instance
+ * @memberof DetectWinnerSystem
+ */
+ removed() {
+
+ this.pointsKeepers = null;
+ this.winnerKeepers = null;
+ }
+
+ /**
+ * Runs on every update of the loop. Checks if someone won
+ *
+ * @function update
+ * @instance
+ * @param {Number} currentFrameDuration the duration of the current
+ * frame
+ * @memberof DetectWinnerSystem
+ */
+ update(currentFrameDuration) {
+
+ let winner = null;
+
+ for (const pointsKeeper of this.pointsKeepers) {
+ for (const pointsCandidate of Object.keys(pointsKeeper.points)) {
+ if (pointsKeeper.points[pointsCandidate] >= Config.maxPoints) {
+ winner = pointsCandidate;
+ break;
+ }
+ }
+ }
+
+ if (!winner) {
+ return;
+ }
+
+ for (const winnerKeeper of this.winnerKeepers) {
+
+ // Only one winner, another system should reset
+ if (!winnerKeeper.winner.winner) {
+ winnerKeeper.winner.winner = winner;
+ }
+
+ }
+ }
+};
+
diff --git a/lib/systems/render_points.js b/lib/systems/render_points.js
new file mode 100644
index 0000000..f7fd410
--- /dev/null
+++ b/lib/systems/render_points.js
@@ -0,0 +1,114 @@
+import { System } from '@serpentity/serpentity';
+import { Graphics } from 'pixi.js';
+
+import Config from '../config';
+import PointsNode from '../nodes/points';
+
+const internals = {
+ kNoPixiError: 'No pixi application passed to render system. Make sure you set the `application` key in the config object when initializing.',
+ redBar: new Graphics(),
+ blueBar: new Graphics()
+};
+
+/**
+ * Renders points assuming there exist a "red" and a "blue" score
+ *
+ * @extends {external:Serpentity.System}
+ * @class RenderPointsSystem
+ * @param {object} config a configuration object to extend.
+ */
+export default class RenderPointsSystem extends System {
+
+ constructor(config = {}) {
+
+ super();
+
+ /**
+ * The node collection of points keepers
+ *
+ * @property {external:Serpentity.NodeCollection} pointsKeepers
+ * @instance
+ * @memberof RenderPointsSystem
+ */
+ this.pointsKeepers = null;
+
+ /**
+ * The pixi engine we will use to render
+ *
+ * @property {external:PixiJs.Application} application
+ * @instance
+ * @memberof RenderPointsSystem
+ */
+ this.application = config.application;
+
+ if (!this.application) {
+ throw new Error(internals.kNoPixiError);
+ }
+ }
+
+ /**
+ * Initializes system when added. Requests renderable nodes and
+ * attaches to event listeners to add / remove them to pixi stage
+ *
+ * @function added
+ * @memberof RenderPointsSystem
+ * @instance
+ * @param {external:Serpentity.Engine} engine the serpentity engine to
+ * which we are getting added
+ */
+ added(engine) {
+
+ this.pointsKeepers = engine.getNodes(PointsNode);
+ this.application.stage.addChild(internals.blueBar);
+ this.application.stage.addChild(internals.redBar);
+ }
+
+ /**
+ * Clears system resources when removed.
+ *
+ * @function removed
+ * @instance
+ * @memberof RenderPointsSystem
+ */
+ removed() {
+
+ this.pointsKeepers = null;
+ this.application.stage.removeChild(internals.blueBar);
+ this.application.stage.removeChild(internals.redBar);
+ }
+
+ /**
+ * Runs on every update of the loop. Updates the bars
+ *
+ * @function update
+ * @instance
+ * @param {Number} currentFrameDuration the duration of the current
+ * frame
+ * @memberof RenderPointsSystem
+ */
+ update(currentFrameDuration) {
+
+ for (const pointsKeeper of this.pointsKeepers) {
+
+ const redPoints = pointsKeeper.points.red;
+ const bluePoints = pointsKeeper.points.blue;
+
+ const redBarHeight = redPoints * Config.verticalResolution / Config.maxPoints;
+ const blueBarHeight = bluePoints * Config.verticalResolution / Config.maxPoints;
+
+ internals.redBar.removeChildren();
+ const redBar = new Graphics();
+ redBar.lineStyle(20, 0xeaacac, 0.75)
+ .moveTo(Config.horizontalResolution, 0)
+ .lineTo(Config.horizontalResolution, redBarHeight);
+ internals.redBar.addChild(redBar);
+
+ internals.blueBar.removeChildren();
+ const blueBar = new Graphics();
+ blueBar.lineStyle(20, 0x87c5ea, 0.75)
+ .moveTo(0, 0)
+ .lineTo(0, blueBarHeight);
+ internals.blueBar.addChild(blueBar);
+ }
+ }
+};
diff --git a/lib/systems/render_winner.js b/lib/systems/render_winner.js
new file mode 100644
index 0000000..53f3602
--- /dev/null
+++ b/lib/systems/render_winner.js
@@ -0,0 +1,114 @@
+import { System } from '@serpentity/serpentity';
+import { Text } from 'pixi.js';
+
+import Config from '../config';
+import WinnerNode from '../nodes/winner';
+
+const internals = {
+ kNoPixiError: 'No pixi application passed to render system. Make sure you set the `application` key in the config object when initializing.'
+};
+
+/**
+ * Renders a winner if there is one
+ *
+ * @extends {external:Serpentity.System}
+ * @class RenderWinnerSystem
+ * @param {object} config a configuration object to extend.
+ */
+export default class RenderWinnerSystem extends System {
+
+ constructor(config = {}) {
+
+ super();
+
+ /**
+ * The keepers of the winner
+ *
+ * @property {external:Serpentity.NodeCollection} pointsKeepers
+ * @instance
+ * @memberof RenderWinnerSystem
+ */
+ this.winnerKeepers = null;
+
+ /**
+ * The pixi engine we will use to render
+ *
+ * @property {external:PixiJs.Application} application
+ * @instance
+ * @memberof RenderWinnerSystem
+ */
+ this.application = config.application;
+
+ if (!this.application) {
+ throw new Error(internals.kNoPixiError);
+ }
+ }
+
+ /**
+ * Initializes system when added. Requests winner keepers
+ *
+ * @function added
+ * @memberof RenderWinnerSystem
+ * @instance
+ * @param {external:Serpentity.Engine} engine the serpentity engine to
+ * which we are getting added
+ */
+ added(engine) {
+
+ this.winnerKeepers = engine.getNodes(WinnerNode);
+ }
+
+ /**
+ * Clears system resources when removed.
+ *
+ * @function removed
+ * @instance
+ * @memberof RenderWinnerSystem
+ */
+ removed() {
+
+ this.pointsKeepers = null;
+ if (internals.winnerText) {
+ this.application.stage.removeChild(internals.winnerText);
+ internals.winnerText = null;
+ }
+ }
+
+ /**
+ * Runs on every update of the loop. Updates the bars
+ *
+ * @function update
+ * @instance
+ * @param {Number} currentFrameDuration the duration of the current
+ * frame
+ * @memberof RenderWinnerSystem
+ */
+ update(currentFrameDuration) {
+
+ // Right now this is final, once a winner is rendered you would need
+ // to restart the whole system.
+ if (internals.winnerText) {
+ return;
+ }
+
+ for (const winnerKeeper of this.winnerKeepers) {
+ const winner = winnerKeeper.winner.winner;
+ if (winner) {
+ const message = `${winner} has won`;
+ internals.winnerText = new Text(message, {
+ fontFamily: 'Arial',
+ fontSize: 96,
+ fontWeight: 'bold',
+ fill: 0xffffff,
+ align: 'center'
+ });
+ internals.winnerText.position.x = Config.horizontalResolution / 2;
+ internals.winnerText.position.y = Config.verticalResolution / 2;
+ internals.winnerText.anchor.set(0.5);
+ this.application.stage.addChild(internals.winnerText);
+ return;
+ }
+ }
+ }
+};
+