Refactored redux code and snake game
This commit is contained in:
152
src/redux/game.js
Normal file
152
src/redux/game.js
Normal file
@@ -0,0 +1,152 @@
|
||||
import Module from "./module.js";
|
||||
|
||||
export const MODULE_NAME = "GAME";
|
||||
|
||||
export const module = new Module(MODULE_NAME, {
|
||||
loopId: null,
|
||||
callers: [],
|
||||
keyPressSubscribers: [],
|
||||
fps: 5
|
||||
});
|
||||
|
||||
export const keys = {
|
||||
UP: "ArrowUp",
|
||||
RIGHT: "ArrowRight",
|
||||
DOWN: "ArrowDown",
|
||||
LEFT: "ArrowLeft",
|
||||
PLUS: "+",
|
||||
MINUS: "-"
|
||||
};
|
||||
|
||||
export const [START_LOOP, startLoop] = module.action("START_LOOP");
|
||||
export const [STOP_LOOP, stopLoop] = module.action("STOP_LOOP");
|
||||
export const [LOOP, loop] = module.action("LOOP", ({ recur, skip } = {}) => ({ recur, skip }));
|
||||
export const [UPDATE_LOOP_ID, updateLoopId] = module.action("UPDATE_LOOP_ID", loopId => ({ loopId }));
|
||||
export const [SET_FPS, setFps] = module.action("SET_FPS", fps => ({ fps }));
|
||||
export const [INVOKE_CALLERS, invokeCallers] = module.action("INVOKE_CALLERS");
|
||||
export const [REGISTER_CALLER, registerCaller] = module.action("REGISTER_CALLER", name => ({ name }));
|
||||
export const [UNREGISTER_CALLER, unregisterCaller] = module.action("UNREGISTER_CALLER", name => ({ name }));
|
||||
export const [KEY_PRESS, keyPress] = module.action("KEY_PRESS", key => ({ key }));
|
||||
export const [SUBSCRIBE_KEY_PRESSED, subscribeKeyPressed] = module.action("SUBSCRIBE_KEY_PRESSED", name => ({ name }));
|
||||
export const [UNSUBSCRIBE_KEY_PRESSED, unsubscribeKeyPressed] = module.action("UNSUBSCRIBE_KEY_PRESSED", name => ({
|
||||
name
|
||||
}));
|
||||
|
||||
module.reducer(UPDATE_LOOP_ID, (state, { loopId }) => {
|
||||
return { ...state, loopId };
|
||||
});
|
||||
|
||||
module.reducer(SET_FPS, (state, { fps }) => {
|
||||
return { ...state, fps };
|
||||
});
|
||||
|
||||
module.reducer(REGISTER_CALLER, (state, { name }) => {
|
||||
if (!name) {
|
||||
throw new Error("Argument 'name' is required.");
|
||||
}
|
||||
if (typeof name !== "string") {
|
||||
throw new Error("Argument 'name' is must be a function.");
|
||||
}
|
||||
if (state.callers.includes(name)) {
|
||||
return state;
|
||||
}
|
||||
return { ...state, callers: [...state.callers, name] };
|
||||
});
|
||||
|
||||
module.reducer(UNREGISTER_CALLER, (state, { name }) => {
|
||||
if (!name) {
|
||||
throw new Error("Argument 'name' is required.");
|
||||
}
|
||||
if (typeof name !== "string") {
|
||||
throw new Error("Argument 'name' is must be a function.");
|
||||
}
|
||||
if (!state.callers.includes(name)) {
|
||||
return state;
|
||||
}
|
||||
return { ...state, callers: state.callers.filter(caller => caller !== name) };
|
||||
});
|
||||
|
||||
module.reducer(SUBSCRIBE_KEY_PRESSED, (state, { name }) => {
|
||||
if (!name) {
|
||||
throw new Error("Argument 'name' is required.");
|
||||
}
|
||||
if (typeof name !== "string") {
|
||||
throw new Error("Argument 'name' is must be a function.");
|
||||
}
|
||||
if (state.keyPressSubscribers.includes(name)) {
|
||||
return state;
|
||||
}
|
||||
return { ...state, keyPressSubscribers: [...state.keyPressSubscribers, name] };
|
||||
});
|
||||
|
||||
module.reducer(UNSUBSCRIBE_KEY_PRESSED, (state, { name }) => {
|
||||
if (!name) {
|
||||
throw new Error("Argument 'name' is required.");
|
||||
}
|
||||
if (typeof name !== "string") {
|
||||
throw new Error("Argument 'name' is must be a function.");
|
||||
}
|
||||
if (!state.keyPressSubscribers.includes(name)) {
|
||||
return state;
|
||||
}
|
||||
return { ...state, keyPressSubscribers: state.keyPressSubscribers.filter(subscriber => subscriber !== name) };
|
||||
});
|
||||
|
||||
module.middleware(KEY_PRESS, (dispatch, { keyPressSubscribers = [], fps }, { key }) => {
|
||||
keyPressSubscribers.forEach(subscriber => dispatch({ type: subscriber, key }));
|
||||
if (key === keys.PLUS) {
|
||||
dispatch(setFps(fps + 1));
|
||||
}
|
||||
if (key === keys.MINUS) {
|
||||
dispatch(setFps(fps - 1));
|
||||
}
|
||||
});
|
||||
|
||||
module.middleware(INVOKE_CALLERS, (dispatch, { callers = [] }) => {
|
||||
callers.forEach(caller => dispatch({ type: caller }));
|
||||
});
|
||||
|
||||
module.middleware(START_LOOP, (dispatch, { loopId, callers, fps }) => {
|
||||
if (loopId) {
|
||||
return;
|
||||
}
|
||||
dispatch(loop());
|
||||
});
|
||||
|
||||
module.middleware(LOOP, (dispatch, { loopId, fps }, { skip = 0, recur = false }, getState) => {
|
||||
if (loopId) {
|
||||
return;
|
||||
}
|
||||
|
||||
let start = null;
|
||||
const loop = ({ recur = false, skip = 0 } = {}) => {
|
||||
const { loopId, fps } = getState()[MODULE_NAME];
|
||||
|
||||
if (recur && !loopId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return requestAnimationFrame(timestamp => {
|
||||
const minDuration = 1000 / fps;
|
||||
if (!start) {
|
||||
start = timestamp;
|
||||
}
|
||||
const progress = timestamp - start;
|
||||
if (progress === 0 || progress > minDuration) {
|
||||
start = timestamp;
|
||||
dispatch(invokeCallers());
|
||||
}
|
||||
return loop({ recur: true, skip: skip > 10 ? 0 : skip + 1 });
|
||||
});
|
||||
};
|
||||
dispatch(updateLoopId(loop()));
|
||||
});
|
||||
|
||||
module.middleware(STOP_LOOP, (dispatch, { loopId }) => {
|
||||
if (!loopId) {
|
||||
return;
|
||||
}
|
||||
dispatch(updateLoopId(null));
|
||||
});
|
||||
|
||||
export default module;
|
||||
Reference in New Issue
Block a user