]> git.r.bdr.sh - rbdr/sumo/blob - lib/systems/control_mapper.js
c0ab05ec1de4362260bb18201199a911b1facfce
[rbdr/sumo] / lib / systems / control_mapper.js
1 import { System } from '@serpentity/serpentity';
2
3 import ControllableNode from '../nodes/controllable';
4
5 const internals = {
6 gamepadState: {
7 },
8 keyboardState: {
9 }
10 };
11
12 /**
13 * Updates control status based on the controller map
14 *
15 * @extends {external:Serpentity.System}
16 * @class ControlMapperSystem
17 * @param {object} config a configuration object to extend.
18 */
19 export default class ControlMapperSystem extends System {
20
21 constructor(config = {}) {
22
23 super();
24
25 /**
26 * The node collection of controllable entities
27 *
28 * @property {external:Serpentity.NodeCollection} controllables
29 * @instance
30 * @memberof RenderSystem
31 */
32 this.controllables = null;
33
34 this._initializeKeyboard();
35 this._initializeGamepad();
36 }
37
38 /**
39 * Initializes system when added. Requests controllable nodes.
40 *
41 * @function added
42 * @memberof RenderSystem
43 * @instance
44 * @param {external:Serpentity.Engine} engine the serpentity engine to
45 * which we are getting added
46 */
47 added(engine) {
48
49 this.controllables = engine.getNodes(ControllableNode);
50 }
51
52 /**
53 * Clears system resources when removed.
54 *
55 * @function removed
56 * @instance
57 * @memberof RenderSystem
58 */
59 removed() {
60
61 this.controllables = null;
62 }
63
64 /**
65 * Runs on every update of the loop. Maps the actions given the current state of the inputs
66 *
67 * @function update
68 * @instance
69 * @param {Number} currentFrameDuration the duration of the current
70 * frame
71 * @memberof RenderSystem
72 */
73 update(currentFrameDuration) {
74
75 this._updateGamepads();
76
77 for (const controllable of this.controllables) {
78 for (const map of controllable.controlMap.map) {
79 if (map.source.type === 'keyboard') {
80 this._setValue(controllable.entity, map.target, !!internals.keyboardState[map.source.index]);
81 }
82
83 if (map.source.type === 'gamepad') {
84 const gamepad = internals.gamepadState[map.source.gamepadNumber];
85 if (gamepad) {
86 const source = gamepad[map.source.gamepadInputSource];
87 source && this._setValue(controllable.entity, map.target, source[map.source.index]);
88 }
89 }
90 }
91 }
92 }
93
94 // Listens to keyboard to update internal map
95
96 _initializeKeyboard() {
97
98 window.addEventListener('keydown', (event) => {
99
100 internals.keyboardState[event.keyCode] = true;
101 });
102
103 window.addEventListener('keyup', (event) => {
104
105 internals.keyboardState[event.keyCode] = false;
106 });
107 }
108
109 // Requests gamepad access and binds to the events
110
111 _initializeGamepad() {
112
113 window.addEventListener('gamepadconnected', (event) => {
114
115 internals.gamepadState[event.gamepad.index] = event.gamepad;
116 window.gamepad = event.gamepad;
117 });
118
119 window.addEventListener('gamepaddisconnected', (event) => {
120
121 delete internals.gamepadState[event.gamepad.index];
122 });
123 }
124
125 // Update Gamepad
126
127 _updateGamepads() {
128
129 const gamepads = navigator.getGamepads();
130 for (const index of Object.keys(internals.gamepadState)) {
131 internals.gamepadState[index] = gamepads[index];
132 }
133 }
134
135 // Sets the value to a target
136
137 _setValue(entity, target, value) {
138
139 const component = entity.getComponent(target.component);
140
141 if (component) {
142 const keyFragments = target.property.split('.');
143 let currentObject = component;
144 for (const keyFragment of keyFragments.slice(0, keyFragments.length - 1)) {
145 currentObject = currentObject[keyFragment] = currentObject[keyFragment] || {};
146 }
147
148
149 const finalValue = !!target.value ? target.value(value) : value;
150 const finalProperty = keyFragments.pop();
151 currentObject[finalProperty] += finalValue;
152 }
153 }
154 }
155