health bar and refactor

This commit is contained in:
Nicky Case 2019-02-17 16:54:29 -05:00
parent 0f042d89eb
commit 3ad8f55829
15 changed files with 455 additions and 75 deletions

View File

@ -2,25 +2,29 @@
<html>
<head>
<title>Adventures with Anxiety!</title>
<link rel="stylesheet" type="text/css" href="game.css">
<link rel="stylesheet" type="text/css" href="styles/game.css">
<meta name="viewport" content="width=device-width">
</head>
<body>
<div id="game_container">
<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>
<div id="game_words" class="no_select"></div>
<div id="game_hp"></div>
<div id="game_choices" class="no_select"></div>
</div>
</body>
</html>
<script src="pinkyswear.min.js"></script>
<script src="game.js"></script>
<!-- SCRIPTS -->
<script src="scripts/lib/pinkyswear.min.js"></script>
<script src="scripts/lib/minpubsub.min.js"></script>
<script src="scripts/game/Game.js"></script>
<script src="scripts/game/Sprite.js"></script>
<script src="scripts/game/Beebee.js"></script>
<script src="scripts/game/HP.js"></script>
<script src="scripts/game/SceneSetup.js"></script>
<script src="scripts/main.js"></script>

47
scenes/demo.md Normal file
View File

@ -0,0 +1,47 @@
# demo
`SceneSetup.demo()`
> Prepare for trouble!
Oh no!
(#demo-attacks)
# demo-attacks
[Attack 10 points](#demo-attack-low)
[Attack 20 points](#demo-attack-med)
[Attack 50%](#demo-attack-hi)
# demo-attack-low
`HP.attackHong("10p")`
{{if HP.hong==0}} (#dead) {{/if}}
(#demo-attacks)
# demo-attack-med
`HP.attackHong("20p")`
{{if HP.hong==0}} (#dead) {{/if}}
(#demo-attacks)
# demo-attack-hi
`HP.attackHong("50%")`
{{if HP.hong==0}} (#dead) {{/if}}
(#demo-attacks)
# dead
i am ded
> u r ded

View File

@ -1,6 +1,9 @@
# woods
`document.body.style.background = "#ddd"`
```
document.body.style.background = "#ddd"
publish("beebee", ["normal"])
```
Two roads diverged in the woods and I...
{{if _.eaten}} (this time try not to get eaten by wolves) {{/if}}
@ -13,11 +16,14 @@ Two roads diverged in the woods and I...
# woods-less
`document.body.style.background = "#ff4040"`
And that's how I got lost in the woods and was eaten by wolves
{{if _.eaten}} (...again.) {{/if}}
```
document.body.style.background = "#ff4040"
publish("beebee", ["panic"])
```
`_.eaten = true`
`_.played_less = true`
@ -38,6 +44,8 @@ And that's how I found my way back to civilization and was not eaten by wolves
Good choice.
`publish("beebee", ["yay"])`
(#END)
do NOT show this line

View File

54
scripts/game/Beebee.js Normal file
View File

@ -0,0 +1,54 @@
function Beebee(){
var self = this;
// Sprite!
var beebeeImage = new Image();
beebeeImage.src = "sprites/beebee.png";
self.sprite = new Sprite({
image: beebeeImage,
grid:{
width: 1,
height: 2
},
frame:{
width: 720,
height: 500
},
anchor:{
x: 300,
y: 230
},
frameNames:[
"normal",
"panic",
"yay"
],
x: 300,
y: 430,
rotation: 0,
scale: 1,
squash: 1
});
// Draw
var ticker = 1;
self.draw = function(ctx){
// Bouncing based on frame!
ticker += 1/20;
if(self.sprite.currentFrameName == "panic"){
ticker += 1;
}
self.sprite.squash = 1 + Math.sin(ticker)*0.05;
// Draw me!
self.sprite.draw(ctx);
};
subscribe("beebee", function(frameName){
self.sprite.gotoFrameByName(frameName);
});
}

View File

@ -1,16 +1,3 @@
var xhr = new XMLHttpRequest();
xhr.open('GET', 'woods.md?v='+Math.random());
xhr.onload = function() {
if(xhr.status===200){
Game.onload(xhr.responseText);
}
};
xhr.send();
/*****************************/
/*****************************/
/*****************************/
window._ = {};
window.Game = {};
@ -69,21 +56,19 @@ Game.onload = function(data){
}
////////////////////////////////////////////////////////////////////////////////////////////////
// SCENE MANAGEMENT ////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
Game.start = function(){
window._ = {}; // global var, reset
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.updateText();
Game.updateCanvas();
publish("update");
};
Game.goto = function(sectionID){
@ -101,9 +86,8 @@ Game.goto = function(sectionID){
Game.executeNextLine();
};
Game.executeNextLine = function(){
var doNextLineImmediately = false;
Game.executeNextLine = function(){
// Parse handlebars
var originalLine = Game.queue.shift();
@ -112,7 +96,11 @@ Game.executeNextLine = function(){
// Execute line
var promiseNext;
if(line!=""){ // none, don't execute...
if(line==""){
// If no line, get immediate promise...
promiseNext = new pinkySwear();
promiseNext(true, []);
}else{
// Execute based on what type it is!
var lineType = Game.getLineType(line);
@ -122,11 +110,9 @@ Game.executeNextLine = function(){
break;
case "choice":
promiseNext = Game.executeChoice(line);
doNextLineImmediately = true;
break;
case "code":
promiseNext = Game.executeCode(line);
doNextLineImmediately = true;
break;
}
@ -154,6 +140,21 @@ Game.addToQueue = function(line){
Game.queue.push(line);
}
////////////////////////////////////////////////////////////////////////////////////////////////
// TEXT AND STUFF //////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
// Move the text DOM to latest
Game.updateText = 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";
};
// Execute text! Just add it to text DOM.
Game.executeText = function(line){
@ -325,15 +326,45 @@ Game.parseLine = function(line){
};
/*****************************/
/*****************************/
/*****************************/
////////////////////////////////////////////////////////////////////////////////////////////////
// WHERE STUFF WILL BE DRAWN ///////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
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";
Game.context = Game.canvas.getContext("2d");
// A blank scene
Game.resetScene = function(){
Game.scene = {};
Game.scene.children = [];
};
Game.resetScene();
// Update & draw all the kids!
Game.updateCanvas = function(){
// For retina
var ctx = Game.context;
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
ctx.save();
ctx.scale(2,2);
// Update/Draw all kids
Game.scene.children.forEach(function(child){
child.draw(ctx);
});
// Restore
ctx.restore();
// Draw HP
HP.draw();
};

124
scripts/game/HP.js Normal file
View File

@ -0,0 +1,124 @@
// Singleton - it's always there!
window.HP = new HitPoints();
// The Class!
function HitPoints(){
var self = this;
// My DOM & canvas
self.dom = document.querySelector("#game_hp");
self.canvas = document.createElement("canvas");
self.canvas.width = 360 * 2;
self.canvas.height = 70 * 2;
self.canvas.style.width = self.canvas.width/2 + "px";
self.canvas.style.height = self.canvas.height/2 + "px";
self.context = self.canvas.getContext("2d");
self.dom.appendChild(self.canvas);
// My sprite
self.image = new Image();
self.image.src = "sprites/hp.png";
// My stats
self.reset = function(){
self.hong = 100;
self.beebee = 100;
};
self.reset();
// Attack!
self.doDamage = function(str, target){
// Absolute or Relative Damage?
var num = parseFloat(str);
var isAbsolute = (str.slice(-1)=="p"); // p = absolute, % = relative
if(isAbsolute){
self[target] -= num;
}else{
var relativeDamage = Math.floor( self[target] * (num/100) );
self[target] -= relativeDamage;
}
// Floor bound
if(self[target]<0){
self[target] = 0;
}
};
// TODO: SHAKING BASED ON AMOUNT OF ABSOLUTE DAMAGE.
self.attackHong = function(str){
self.doDamage(str, "hong");
self.leftShake = 30;
};
self.attackBeebee = function(str){
self.doDamage(str, "beebee");
self.rightShake = 30;
};
// Draw
self.leftShake = 0;
self.leftWidth = 360;
self.rightShake = 0;
self.rightWidth = 360;
self.drawHalf = function(ctx, isRight){
ctx.save();
// Which side?
var side = isRight ? "right" : "left";
var hp = isRight ? self.beebee : self.hong;
// Shaking
if(self[side+"Shake"]>0){
var amp = self[side+"Shake"]/7;
var shakeY = Math.sin(self[side+"Shake"]*1.3)*amp;
ctx.translate(0,shakeY);
self[side+"Shake"]--;
}else{
self[side+"Shake"]=0;
}
// BLACK
var sx=isRight ? 360 : 0, sy=0, sw=360, sh=150;
ctx.drawImage(self.image, sx,sy,sw,sh, sx/2,sy/2,sw/2,sh/2); // black
// RED
var hpRatio = (hp+32)/(100+32); // 100,0 => 1,0.3
sw = 360 * hpRatio;
sy = 150;
self[side+"Width"] = self[side+"Width"]*0.8 + sw*0.2;
sw = self[side+"Width"];
if(sw>88){
if(isRight){
ctx.drawImage(self.image, sx+(360-sw),sy,sw,sh, 360/2,0,sw/2,sh/2);
}else{
ctx.drawImage(self.image, sx,sy,sw,sh, (360-sw)/2,0,sw/2,sh/2);
}
}
if(self[side+"WhiteWidth"]>sw && self[side+"Shake"]<=0){
self[side+"WhiteWidth"] -= 0.6;
}
ctx.restore();
};
self.draw = function(){
var ctx = self.context;
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
ctx.save();
ctx.scale(2,2);
// Draw Left & Right Sides
self.drawHalf(ctx, false);
self.drawHalf(ctx, true);
// Draw "Timer"
var sx=0, sy=450, sw=720, sh=150;
ctx.drawImage(self.image, sx,sy,sw,sh, 0,0,sw/2,sh/2);
ctx.restore();
};
}

View File

@ -0,0 +1,16 @@
/**********************************
A big ol' singleton class that just makes it easy to create scenes.
**********************************/
window.SceneSetup = {};
SceneSetup.demo = function(){
Game.resetScene();
var beebee = new Beebee();
Game.scene.children.push(beebee);
};

100
scripts/game/Sprite.js Normal file
View File

@ -0,0 +1,100 @@
/***************************************************
A sprite built off this config:
{
image: image,
grid:{ // in frames
width: 2,
height: 2
},
frame:{ // in pixels
width: 100,
height: 100
},
anchor:{ // in pixels. optional.
x: 10,
y: 30
},
frameNames:[ // optional
"one",
"two",
"three"
],
x: 0, // optional
y: 0, // optional
rotation: 0, // optional
scale: 1, // optinal
squash: 1, // optional
}
***************************************************/
function Sprite(config){
var self = this;
// Sprite image & dimensions
self.image = config.image;
self.grid = config.grid;
self.frame = config.frame;
// Sprite anchor
self.anchor = {};
self.anchor.x = config.anchor.x || 0;
self.anchor.y = config.anchor.y || 0;
// Current frame
self.currentFrame = 0;
self.currentFrameName = "";
self.frameNames = config.frameNames || [];
self.gotoFrame = function(index){
self.currentFrame = index;
self.currentFrameName = self.frameNames[self.currentFrame] || "";
};
self.gotoFrameByName = function(name){
var index = self.frameNames.indexOf(name);
self.gotoFrame(index);
};
self.gotoFrame(0);
// Other transformations
self.x = config.x || 0;
self.y = config.y || 0;
self.rotation = config.rotation || 0;
self.scale = config.scale || 1;
self.squash = config.squash || 1;
// Draw frame!
self.draw = function(ctx){
ctx.save();
// Which part of image to draw?
var sx = self.currentFrame % self.grid.width;
var sy = Math.floor((self.currentFrame - sx)/self.grid.width);
var fw = self.frame.width;
var fh = self.frame.height;
// Translate...
var dx = self.x;
var dy = self.y;
ctx.translate(dx, dy);
// Scale
var scaleX = self.scale * self.squash;
var scaleY = self.scale / self.squash;
ctx.scale(scaleX, scaleY);
// Draw it!
ctx.drawImage(
self.image,
sx*fw, sy*fh, fw, fh,
-self.anchor.x, -self.anchor.y, fw/2, fh/2
);
ctx.restore();
};
}

2
scripts/lib/minpubsub.min.js vendored Normal file
View File

@ -0,0 +1,2 @@
(function(b){var a={},e=b.c_||{};a.publish=function(f,c){for(var a=e[f],d=a?a.length:0;d--;)a[d].apply(b,c||[])};a.subscribe=function(a,c){e[a]||(e[a]=[]);e[a].push(c);return[a,c]};a.unsubscribe=function(a,c){var b=e[c?a:a[0]];c=c||a[1];for(var d=b?b.length:0;d--;)b[d]===c&&b.splice(d,1)};"object"===typeof module&&module.exports?module.exports=exports=a:"function"===typeof define&&define.amd?define(function(){return a}):"object"===typeof b&&(b.publish=a.publish,b.subscribe=a.subscribe,b.unsubscribe=
a.unsubscribe)})(this.window);

8
scripts/main.js Normal file
View File

@ -0,0 +1,8 @@
var xhr = new XMLHttpRequest();
xhr.open('GET', 'scenes/demo.md?v='+Math.random());
xhr.onload = function() {
if(xhr.status===200){
Game.onload(xhr.responseText);
}
};
xhr.send();

BIN
sprites/beebee.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
sprites/hp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@ -7,6 +7,13 @@ body{
background: #ddd;
}
.no_select{
-webkit-user-select: none; /* Safari 3.1+ */
-moz-user-select: none; /* Firefox 2+ */
-ms-user-select: none; /* IE 10+ */
user-select: none; /* Standard syntax */
}
#game_container{
position: absolute;
@ -107,38 +114,17 @@ body{
/***********************************/
/***********************************/
#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%);
canvas{
border: none;
}
#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;
}
#game_hp{
position: absolute;
width: 360px;
height: 100px;
top:0; left:0;
}