Implementing a bunch of monads and buttons and stuff and starting to theme it a bit
This commit is contained in:
parent
ff0070d90d
commit
af342c72d5
8 changed files with 363 additions and 17 deletions
55
src/async.js
Normal file
55
src/async.js
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
function Async() {
|
||||||
|
return {
|
||||||
|
bind: bind,
|
||||||
|
run: run,
|
||||||
|
sequence: sequence,
|
||||||
|
wait: wait,
|
||||||
|
wrap: wrap
|
||||||
|
};
|
||||||
|
|
||||||
|
function bind(m, f) {
|
||||||
|
return function(g) {
|
||||||
|
m(function(x) {
|
||||||
|
f(x)(g);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function run() {
|
||||||
|
var m;
|
||||||
|
if(arguments.length == 1) {
|
||||||
|
m = arguments[0];
|
||||||
|
} else {
|
||||||
|
m = sequence.apply(null, arguments);
|
||||||
|
}
|
||||||
|
m(function() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
function sequence() {
|
||||||
|
var steps = arguments;
|
||||||
|
var i = 0;
|
||||||
|
return function(f) {
|
||||||
|
var step = function() {
|
||||||
|
if(i < steps.length) {
|
||||||
|
steps[i++](step);
|
||||||
|
} else {
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
step();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function wait(delay) {
|
||||||
|
return function(f) {
|
||||||
|
setTimeout(f, delay);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function wrap(x) {
|
||||||
|
return function(f) {
|
||||||
|
f(x);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
131
src/automaton.js
131
src/automaton.js
|
@ -1,12 +1,125 @@
|
||||||
function Automaton(messaging, screen, session) {
|
function Automaton(async, buttons, dom, messaging, screen, session) {
|
||||||
var startMenu = ['New Game', 'Settings'];
|
var menus = {
|
||||||
if(session.hasSavedGame()) {
|
start: {
|
||||||
startMenu.unshift('Continue');
|
entries: [
|
||||||
|
{label: 'New Game', action: newGame},
|
||||||
|
{label: 'Settings', action: function() { console.log("Not implemented yet"); }}
|
||||||
|
],
|
||||||
|
cancel: intro
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
},
|
||||||
|
skin: {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var game = session.getGame();
|
||||||
|
if(game != undefined) {
|
||||||
|
menus.start.entries.unshift(
|
||||||
|
{label: 'Continue', action: function() { messaging.send({tag: 'Resume', game: game}); }}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
screen.menu(startMenu);
|
|
||||||
messaging.addEventListener(['Init'], function(game) {
|
|
||||||
console.log(game);
|
|
||||||
});
|
|
||||||
//messaging.send({tag: 'NewGame'});
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
run: run
|
||||||
|
}
|
||||||
|
|
||||||
|
//messaging.addEventListener(['Init'], function(game) {
|
||||||
|
// console.log(game);
|
||||||
|
//});
|
||||||
|
|
||||||
|
function animation(frames) {
|
||||||
|
var frameCounter = 0;
|
||||||
|
var scheduled;
|
||||||
|
var step = function() {
|
||||||
|
if(frameCounter < frames.length) {
|
||||||
|
scheduled = null;
|
||||||
|
frames[frameCounter].action();
|
||||||
|
if(frames[frameCounter].delay != undefined) {
|
||||||
|
scheduled = setTimeout(step, frames[frameCounter].delay);
|
||||||
|
}
|
||||||
|
frameCounter++
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
run: step,
|
||||||
|
skip: function() { if(scheduled != undefined) { clearTimeout(scheduled); } }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function text(message) {
|
||||||
|
var characters = message.split('');
|
||||||
|
var frames = [];
|
||||||
|
var ready = false;
|
||||||
|
var append = function(c) { return function() { screen.appendText(c); }; };
|
||||||
|
for(var i = 0; i < characters.length; i++) {
|
||||||
|
frames.push({action: append(characters[i]), delay: 50});
|
||||||
|
}
|
||||||
|
frames.push({action: function() { ready = true; screen.markAsRead(); }, delay: null});
|
||||||
|
var remote = animation(frames);
|
||||||
|
return function(f) {
|
||||||
|
buttons.push({
|
||||||
|
'A': function() {
|
||||||
|
if(ready) {
|
||||||
|
f();
|
||||||
|
} else {
|
||||||
|
remote.skip();
|
||||||
|
screen.text(message);
|
||||||
|
ready = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
screen.text('');
|
||||||
|
remote.run();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function menu(config) {
|
||||||
|
var cursor = 0;
|
||||||
|
var m = screen.menu(config.entries);
|
||||||
|
var sync = function() { screen.select(cursor); };
|
||||||
|
var mapping = {
|
||||||
|
'Up': function() { cursor = Math.max(0, cursor - 1); sync(); },
|
||||||
|
'Down': function() { cursor = Math.min(config.entries.length - 1, cursor + 1); sync(); },
|
||||||
|
'A': function() { config.entries[cursor].action(); }
|
||||||
|
};
|
||||||
|
if(config.cancel != undefined) {
|
||||||
|
mapping['B'] = function() { buttons.pop(); screen.clear(m); config.cancel(); }
|
||||||
|
}
|
||||||
|
buttons.push(mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
function run() {
|
||||||
|
intro();
|
||||||
|
}
|
||||||
|
|
||||||
|
function intro() {
|
||||||
|
var title = dom.make('p', {textContent: "P O K E M O N", class: 'title'});
|
||||||
|
var subtitle = dom.make('p', {textContent: "< press start >", class: 'subtitle'});
|
||||||
|
var remote = animation([
|
||||||
|
{action: function() {}, delay: 500},
|
||||||
|
{action: function() { screen.show(title); }, delay: 1000},
|
||||||
|
{action: function() { screen.show(subtitle); }, delay: null}
|
||||||
|
]);
|
||||||
|
buttons.map({
|
||||||
|
'B': function() { remote.skip(); intro() },
|
||||||
|
'Start': function() {
|
||||||
|
remote.skip();
|
||||||
|
screen.frame(2);
|
||||||
|
screen.clear();
|
||||||
|
menu(menus.start);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
screen.frame(1);
|
||||||
|
screen.clear();
|
||||||
|
remote.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
function newGame() {
|
||||||
|
screen.frame(3);
|
||||||
|
screen.clear();
|
||||||
|
async.run(
|
||||||
|
text("Bonjour ! Bienvenue dans le monde merveilleux des pokémons !"),
|
||||||
|
text("Pour certains, les pokemons sont des amis. Pour d'autres, ils sont une ressource. "),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
31
src/buttons.js
Normal file
31
src/buttons.js
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
function Buttons(session) {
|
||||||
|
var mapping;
|
||||||
|
var mappingsStack = [];
|
||||||
|
var layout = session.getLayout();
|
||||||
|
|
||||||
|
document.addEventListener('keydown', function(event) {
|
||||||
|
var button = layout[event.key];
|
||||||
|
if(button != undefined && mapping[button] != undefined) {
|
||||||
|
mapping[button]();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
map: map,
|
||||||
|
pop: pop,
|
||||||
|
push: push
|
||||||
|
};
|
||||||
|
|
||||||
|
function map(newMapping) {
|
||||||
|
mapping = newMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pop() {
|
||||||
|
mappingsStack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
function push(newMapping) {
|
||||||
|
mappingsStack.push(newMapping);
|
||||||
|
map(newMapping);
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,8 +5,10 @@
|
||||||
<title>Pokemon Neige</title>
|
<title>Pokemon Neige</title>
|
||||||
<script src="main.js"></script>
|
<script src="main.js"></script>
|
||||||
<script src="session.js"></script>
|
<script src="session.js"></script>
|
||||||
|
<script src="async.js"></script>
|
||||||
<script src="dom.js"></script>
|
<script src="dom.js"></script>
|
||||||
<script src="screen.js"></script>
|
<script src="screen.js"></script>
|
||||||
|
<script src="buttons.js"></script>
|
||||||
<script src="messaging.js"></script>
|
<script src="messaging.js"></script>
|
||||||
<script src="automaton.js"></script>
|
<script src="automaton.js"></script>
|
||||||
<link rel="stylesheet" href="screen.css" type="text/css"/>
|
<link rel="stylesheet" href="screen.css" type="text/css"/>
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
window.addEventListener('load', function() {
|
window.addEventListener('load', function() {
|
||||||
messaging = Messaging();
|
messaging = Messaging();
|
||||||
|
async = Async();
|
||||||
dom = Dom();
|
dom = Dom();
|
||||||
screen = Screen(dom);
|
screen = Screen(dom);
|
||||||
session = Session();
|
session = Session();
|
||||||
automaton = Automaton(messaging, screen, session);
|
buttons = Buttons(session);
|
||||||
|
automaton = Automaton(async, buttons, dom, messaging, screen, session);
|
||||||
|
|
||||||
messaging.start();
|
messaging.start();
|
||||||
|
automaton.run();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
body {
|
||||||
|
background: #000;
|
||||||
|
font-family: mono;
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
#screen {
|
#screen {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 400px;
|
width: 400px;
|
||||||
|
@ -6,4 +12,60 @@
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
margin: -150px 0 0 -200px;
|
margin: -150px 0 0 -200px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#screen .framed {
|
||||||
|
border: 3px double #000;
|
||||||
|
border-radius: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#screen .text {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 4.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#screen .text p {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.menu {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0 0 0 1em;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.menu .selected {
|
||||||
|
background: #000;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1st frame */
|
||||||
|
#screen.frame1 * {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
#screen.frame1 p {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
margin: 0;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
#screen.frame1 .title {
|
||||||
|
font-size: 2em;
|
||||||
|
top: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#screen.frame1 .subtitle {
|
||||||
|
top: 75%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2nd frame */
|
||||||
|
#screen.frame2 ul {
|
||||||
|
width: 7em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,18 +2,74 @@ function Screen(dom) {
|
||||||
var root = document.getElementById('screen');
|
var root = document.getElementById('screen');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
menu: menu
|
appendText: appendText,
|
||||||
|
clear: clear,
|
||||||
|
frame: frame,
|
||||||
|
markAsRead: markAsRead,
|
||||||
|
menu: menu,
|
||||||
|
select: select,
|
||||||
|
show: show,
|
||||||
|
text: text
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function appendText(message) {
|
||||||
|
getTextZone().children[0].textContent += message;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear(elem) {
|
||||||
|
if(elem != undefined) {
|
||||||
|
root.removeChild(elem);
|
||||||
|
} else {
|
||||||
|
dom.clear(root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function frame(n) {
|
||||||
|
root.className = "frame" + n;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTextZone() {
|
||||||
|
var textZone = root.getElementsByClassName('text')[0];
|
||||||
|
if(textZone == undefined) {
|
||||||
|
textZone = dom.make('div', {class: ['text', 'framed']}, [
|
||||||
|
dom.make('p')
|
||||||
|
]);
|
||||||
|
root.appendChild(textZone);
|
||||||
|
}
|
||||||
|
return textZone
|
||||||
|
}
|
||||||
|
|
||||||
|
function markAsRead() {
|
||||||
|
getTextZone().classList.add('read');
|
||||||
|
}
|
||||||
|
|
||||||
function menu(entries) {
|
function menu(entries) {
|
||||||
var domEntries = [];
|
var domEntries = [];
|
||||||
for(var i = 0; i < entries.length; i++) {
|
for(var i = 0; i < entries.length; i++) {
|
||||||
domEntries.push(dom.make('li', {
|
domEntries.push(dom.make('li', {
|
||||||
class: i == 0 ? 'selected' : [],
|
class: i == 0 ? 'selected' : [],
|
||||||
textContent: entries[i]
|
textContent: entries[i].label
|
||||||
}, []));
|
}, []));
|
||||||
}
|
}
|
||||||
var ul = dom.make('ul', {}, domEntries);
|
var ul = dom.make('ul', {class: ['menu', 'framed']}, domEntries);
|
||||||
root.appendChild(ul);
|
root.appendChild(ul);
|
||||||
|
return ul;
|
||||||
|
}
|
||||||
|
|
||||||
|
function select(cursor) {
|
||||||
|
var ul = root.getElementsByClassName('menu')[0];
|
||||||
|
ul.getElementsByClassName('selected')[0].className = '';
|
||||||
|
ul.children[cursor].className = 'selected';
|
||||||
|
}
|
||||||
|
|
||||||
|
function show(elem) {
|
||||||
|
root.appendChild(elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
function text(message) {
|
||||||
|
getTextZone().children[0].textContent = message;
|
||||||
|
if(message.length > 0) {
|
||||||
|
markAsRead();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
function Session() {
|
function Session() {
|
||||||
var game = JSON.parse(localStorage.getItem('game'));
|
var game = JSON.parse(localStorage.getItem('game'));
|
||||||
|
var layout = loadLayout();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hasSavedGame: hasSavedGame,
|
getLayout: getLayout,
|
||||||
|
getGame: getGame,
|
||||||
save : save
|
save : save
|
||||||
};
|
};
|
||||||
|
|
||||||
function hasSavedGame() {
|
function getGame() {
|
||||||
return game != undefined;
|
return game;
|
||||||
}
|
}
|
||||||
|
|
||||||
function update(state) {
|
function update(state) {
|
||||||
|
@ -15,6 +17,28 @@ function Session() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
localStorage.setItem('game', game);
|
localStorage.setItem('game', JSON.stringify(game));
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadLayout() {
|
||||||
|
var saved = JSON.parse(localStorage.getItem('layout'));
|
||||||
|
if(saved == undefined) {
|
||||||
|
saved = {
|
||||||
|
'a': 'A',
|
||||||
|
'b': 'B',
|
||||||
|
'ArrowLeft': 'Left',
|
||||||
|
'ArrowRight': 'Right',
|
||||||
|
'ArrowUp': 'Up',
|
||||||
|
'ArrowDown': 'Down',
|
||||||
|
'Enter': 'Start',
|
||||||
|
' ': 'Select'
|
||||||
|
};
|
||||||
|
localStorage.setItem('layout', JSON.stringify(saved));
|
||||||
|
}
|
||||||
|
return saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLayout() {
|
||||||
|
return layout;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue