pretty dialogue ui

This commit is contained in:
Nicky Case 2019-02-12 14:59:45 -05:00
parent 8655f71e8f
commit 0f042d89eb
5 changed files with 272 additions and 49 deletions

119
game.css
View File

@ -15,33 +15,130 @@ body{
width: 360px;
height: 600px;
background: #fff;
background: #ccc;
font-size: 20px;
font-family: Helvetica, Arial, sans-serif;
font-weight: 100;
line-height: 1.3em;
}
#game_text{
overflow: hidden;
}
#game_words{
width: auto;
height: 450px;
overflow: scroll;
position: relative;
top: 80px;
}
#game_text > div{
margin: 30px;
.human-bubble {
position: relative;
background: #ffffff;
color: #000000;
border-radius: .4em;
padding: 15px;
margin: 10px 30px;
opacity: 0;
left: -15px;
transition: all 0.3s ease-in-out;
}
.human-bubble:after {
content: '';
position: absolute;
left: 0;
top: 50%;
width: 0;
height: 0;
border: 15px solid transparent;
border-right-color: #ffffff;
border-left: 0;
margin-top: -15px;
margin-left: -15px;
}
.wolf-bubble {
position: relative;
background: #000000;
color: #ffffff;
border-radius: .4em;
padding: 15px;
margin: 10px 30px;
opacity: 0;
left: 15px;
transition: all 0.3s ease-in-out;
}
.wolf-bubble:after {
content: '';
position: absolute;
right: 0;
top: 50%;
width: 0;
height: 0;
border: 15px solid transparent;
border-left-color: #000000;
border-right: 0;
margin-top: -15px;
margin-right: -15px;
}
#game_choices{
width: 100%;
height: 150px;
height: 110px;
padding: 20px 0;
position: absolute;
bottom:0;
background: #666;
text-align: center;
color: #fff;
font-weight: normal;
}
}
#game_choices > div{
margin-bottom:0.5em;
cursor: pointer;
}
/***********************************/
/***********************************/
/***********************************/
#game_hp{
position: absolute;
width: 360px;
height: 80px;
top: 0px;
left: 0px;
background: rgb(204,204,204);
background: linear-gradient(180deg, rgba(204,204,204,1) 80%, rgba(204,204,204,0) 100%);
}
#game_hp > div{
position: absolute;
top:25px;
width: 150px;
height: 20px;
background: #ff4040;
}
#game_hp > #hp_human{
left: 20px;
transform: skew(15deg);
}
#game_hp > #hp_wolf{
right: 20px;
transform: skew(-15deg);
}
/***********************************/
/***********************************/
/***********************************/
#game_canvas{
position: absolute;
top:0; left:0;
border: none;
}

177
game.js
View File

@ -18,9 +18,11 @@ Game.sections = {};
Game.startSectionID = null;
Game.dom = document.querySelector("#game_container");
Game.textDOM = document.querySelector("#game_text");
Game.wordsDOM = document.querySelector("#game_words");
Game.choicesDOM = document.querySelector("#game_choices");
Game.queue = [];
// Parse data!
Game.onload = function(data){
@ -54,6 +56,14 @@ Game.onload = function(data){
});
// Animation!
Game.wordsDOM.style.top = "80px";
var animloop = function(){
Game.update();
requestAnimationFrame(animloop);
};
requestAnimationFrame(animloop);
// Let's go!
Game.start();
@ -64,6 +74,18 @@ Game.start = function(){
Game.goto(Game.startSectionID);
};
Game.update = function(){
var wordsHeight = 80 + Game.wordsDOM.getBoundingClientRect().height;
var currentY = parseFloat(Game.wordsDOM.style.top) || 80;
var gotoY = (wordsHeight<260) ? 0 : wordsHeight-260;
gotoY = 80 - gotoY;
var nextY = currentY*0.9 + gotoY*0.1;
Game.wordsDOM.style.top = nextY+"px";
};
Game.goto = function(sectionID){
// Clear choices
@ -71,47 +93,122 @@ Game.goto = function(sectionID){
// Show each line...
var section = Game.sections[sectionID];
if(!section){
throw "NO SECTION NAMED "+sectionID;
}
var lines = section.lines;
for(var i=0; i<lines.length; i++){
// Parse handlebars
var originalLine = lines[i];
line = Game.parseLine(originalLine);
Game.queue = Game.queue.concat(lines);
Game.executeNextLine();
// Execute line
if(line!=""){ // none, don't execute...
};
Game.executeNextLine = function(){
// Execute based on what type it is!
var lineType = Game.getLineType(line);
switch(lineType){
case "text":
Game.executeText(line);
break;
case "choice":
Game.executeChoice(line);
break;
case "code":
Game.executeCode(line);
break;
}
var doNextLineImmediately = false;
// If it's a goto, end THIS section immediately.
if(lineType=="goto"){
Game.executeGoto(line);
// Parse handlebars
var originalLine = Game.queue.shift();
if(!originalLine) return; // END OF QUEUE.
line = Game.parseLine(originalLine);
// Execute line
var promiseNext;
if(line!=""){ // none, don't execute...
// Execute based on what type it is!
var lineType = Game.getLineType(line);
switch(lineType){
case "text":
promiseNext = Game.executeText(line);
break;
}
case "choice":
promiseNext = Game.executeChoice(line);
doNextLineImmediately = true;
break;
case "code":
promiseNext = Game.executeCode(line);
doNextLineImmediately = true;
break;
}
// If it's a goto, end THIS section immediately.
if(lineType=="goto"){
Game.clearQueue(); // CLEAR ALL ELSE IN QUEUE
Game.executeGoto(line);
return;
}
}
// Do next line?
if(Game.queue.length>0){
promiseNext.then(function(){
Game.executeNextLine();
});
}
};
Game.clearQueue = function(){
Game.queue = [];
};
Game.addToQueue = function(line){
Game.queue.push(line);
}
// Execute text! Just add it to text DOM.
Game.executeText = function(line){
var div = document.createElement("div");
div.innerHTML = line;
Game.textDOM.appendChild(div);
var promiseDone = pinkySwear();
// Is it human or wolf?
var dialogue;
var isWolf = /^\>(.*)/.test(line);
if(isWolf){
div.className = "wolf-bubble";
dialogue = line.match(/^\>(.*)/)[1].trim();
}else{
div.className = "human-bubble";
dialogue = line;
}
// Add the bubble, with animation
Game.wordsDOM.appendChild(div);
requestAnimationFrame(function(){
requestAnimationFrame(function(){
div.style.opacity = 1;
div.style.left = 0;
});
});
// Add the text, letter by letter!
var interval = 0;
var SPEED = 40;
for(var i=0; i<dialogue.length; i++){
var ch = dialogue[i];
// for scopin'
(function(ch, interval){
setTimeout(function(){
div.innerHTML += ch;
}, interval);
})(ch, interval);
// Bigger interval
if(ch=="."){
interval += SPEED*10;
}else{
interval += SPEED;
}
}
// Return promise
setTimeout(function(){
promiseDone(true, []);
}, interval+200);
return promiseDone;
}
// Execute choice! Add it to choice DOM.
@ -123,22 +220,34 @@ Game.executeChoice = function(line){
var div = document.createElement("div");
div.innerHTML = choiceText;
div.onclick = function(){
Game.executeText("> "+choiceText);
Game.addToQueue("> "+choiceText);
Game.goto(choiceID);
};
Game.choicesDOM.appendChild(div);
// Return promise
var promiseImmediate = new pinkySwear();
promiseImmediate(true, []);
return promiseImmediate;
}
// Execute code!
Game.executeCode = function(line){
var code = line.match(/\`+([^\`]*)\`+/)[1].trim();
try{
eval(code);
}catch(e){
console.log(e);
}
// Return promise
var promiseImmediate = new pinkySwear();
promiseImmediate(true, []);
return promiseImmediate;
}
// Execute goto! Just goto.
@ -216,3 +325,15 @@ Game.parseLine = function(line){
};
/*****************************/
/*****************************/
/*****************************/
Game.canvas = document.querySelector("#game_canvas");
Game.canvas.width = 360 * 2;
Game.canvas.height = 450 * 2;
Game.canvas.style.width = Game.canvas.width/2 + "px";
Game.canvas.style.height = Game.canvas.height/2 + "px";

View File

@ -8,7 +8,12 @@
<body>
<div id="game_container">
<div id="game_text">
<canvas id="game_canvas"></canvas>
<div id="game_words">
</div>
<div id="game_hp">
<div id="hp_human"></div>
<div id="hp_wolf"></div>
</div>
<div id="game_choices">
</div>
@ -17,4 +22,5 @@
</body>
</html>
<script src="pinkyswear.min.js"></script>
<script src="game.js"></script>

2
pinkyswear.min.js vendored Normal file
View File

@ -0,0 +1,2 @@
(function(e){function g(h){return"function"==typeof h}function k(h){"undefined"!=typeof setImmediate?setImmediate(h):"undefined"!=typeof process&&process.nextTick?process.nextTick(h):setTimeout(h,0)}e[0][e[1]]=function n(f){function a(a,g){null==b&&null!=a&&(b=a,l=g,c.length&&k(function(){for(var a=0;a<c.length;a++)c[a]()}));return b}var b,l=[],c=[];a.then=function(a,e){function m(){try{var c=b?a:e;if(g(c)){var f=function(a){var c,b=0;try{if(a&&("object"==typeof a||g(a))&&g(c=a.then)){if(a===d)throw new TypeError;
c.call(a,function(){b++||f.apply(void 0,arguments)},function(a){b++||d(!1,[a])})}else d(!0,arguments)}catch(e){b++||d(!1,[e])}};f(c.apply(void 0,l||[]))}else d(b,l)}catch(k){d(!1,[k])}}var d=n(f);null!=b?k(m):c.push(m);return d};f&&(a=f(a));return a}})("undefined"==typeof module?[window,"pinkySwear"]:[module,"exports"]);

View File

@ -1,10 +1,10 @@
# woods
Two woods diverged in the woods and I...
{{if _.eaten}} (this time try not to get eaten by wolves) {{/if}}
`document.body.style.background = "#ddd"`
Two roads diverged in the woods and I...
{{if _.eaten}} (this time try not to get eaten by wolves) {{/if}}
[Took the one less travelled by](#woods-less)
[Took the one more travelled by](#woods-more)
@ -13,18 +13,17 @@ Two woods diverged in the woods and I...
# woods-less
And that's how I got lost in the woods and was eaten by wolves
`document.body.style.background = "#ff4040"`
And that's how I got lost in the woods and was eaten by wolves
{{if _.eaten}} (...again.) {{/if}}
`_.eaten = true`
`_.played_less = true`
[Try again](#woods)
[Goodbye](#end)
# woods-more
And that's how I found my way back to civilization and was not eaten by wolves
@ -35,8 +34,6 @@ And that's how I found my way back to civilization and was not eaten by wolves
[Try again](#woods)
[Goodbye](#end)
# home
Good choice.