Implementing a bunch of monads and buttons and stuff and starting to theme it a bit

This commit is contained in:
Tissevert 2018-11-18 23:08:36 +01:00
parent ff0070d90d
commit af342c72d5
8 changed files with 363 additions and 17 deletions

55
src/async.js Normal file
View 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);
};
}
}

View file

@ -1,12 +1,125 @@
function Automaton(messaging, screen, session) {
var startMenu = ['New Game', 'Settings'];
if(session.hasSavedGame()) {
startMenu.unshift('Continue');
function Automaton(async, buttons, dom, messaging, screen, session) {
var menus = {
start: {
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
View 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);
}
}

View file

@ -5,8 +5,10 @@
<title>Pokemon Neige</title>
<script src="main.js"></script>
<script src="session.js"></script>
<script src="async.js"></script>
<script src="dom.js"></script>
<script src="screen.js"></script>
<script src="buttons.js"></script>
<script src="messaging.js"></script>
<script src="automaton.js"></script>
<link rel="stylesheet" href="screen.css" type="text/css"/>

View file

@ -1,9 +1,12 @@
window.addEventListener('load', function() {
messaging = Messaging();
async = Async();
dom = Dom();
screen = Screen(dom);
session = Session();
automaton = Automaton(messaging, screen, session);
buttons = Buttons(session);
automaton = Automaton(async, buttons, dom, messaging, screen, session);
messaging.start();
automaton.run();
});

View file

@ -1,3 +1,9 @@
body {
background: #000;
font-family: mono;
line-height: 1.5em;
}
#screen {
position: absolute;
width: 400px;
@ -6,4 +12,60 @@
top: 50%;
left: 50%;
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;
}

View file

@ -2,18 +2,74 @@ function Screen(dom) {
var root = document.getElementById('screen');
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) {
var domEntries = [];
for(var i = 0; i < entries.length; i++) {
domEntries.push(dom.make('li', {
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);
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();
}
}
}

View file

@ -1,13 +1,15 @@
function Session() {
var game = JSON.parse(localStorage.getItem('game'));
var layout = loadLayout();
return {
hasSavedGame: hasSavedGame,
getLayout: getLayout,
getGame: getGame,
save : save
};
function hasSavedGame() {
return game != undefined;
function getGame() {
return game;
}
function update(state) {
@ -15,6 +17,28 @@ function Session() {
}
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;
}
}