]> git.r.bdr.sh - rbdr/sumo/blame - lib/systems/control_mapper.js
Use new lib components
[rbdr/sumo] / lib / systems / control_mapper.js
CommitLineData
7ade6f8d
RBR
1import { System } from '@serpentity/serpentity';
2
3import ControllableNode from '../nodes/controllable';
4
7ade6f8d 5const internals = {
43413def
RBR
6 gamepadState: {
7 },
7ade6f8d
RBR
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 */
19export 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();
43413def 35 this._initializeGamepad();
7ade6f8d
RBR
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
43413def
RBR
75 this._updateGamepads();
76
7ade6f8d
RBR
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 }
43413def
RBR
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 }
7ade6f8d
RBR
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
43413def
RBR
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
7ade6f8d
RBR
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 }
7741a3cc 154}
7ade6f8d 155