diff --git a/cat.html b/cat.html
index c098692..14033d5 100644
--- a/cat.html
+++ b/cat.html
@@ -36,9 +36,22 @@ var overdesc = "";
var curpath = [];
var pathdir = -1;
var pathvalid = false;
+var curlevel = 1;
var lastmx = -1, lastmy = -1;
+
+// goal types
+
+
+var GOALVERB = {
+ 'food': 'Eat',
+ 'points': 'Earn',
+ 'parades': 'Form',
+ 'llamas': 'Clear',
+ 'cats': 'Clear',
+};
+
var MAXDIRS = 4;
var DIRXMOD = [ 0, 1, 0, -1 ];
var DIRYMOD = [ -1, 0, 1, 0 ];
@@ -60,6 +73,8 @@ var HELPARROWSIZE=15;
var HELPTITLESIZE = 18;
var HELPTEXTSIZE = 12;
+var GOALTEXTSIZE = 16;
+
var TITLETEXTSIZE = 36;
var TITLECREDITTEXTSIZE = 16;
var TITLESTARTTEXTSIZE = 26;
@@ -357,6 +372,7 @@ function startGame() {
loadimage('title', 'images/title.png');
game.init();
+ game.initlevels();
window.addEventListener('load', game.init, false);
window.addEventListener('resize', game.resize, false);
@@ -507,6 +523,24 @@ function addcommas(num) {
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
+function drawcross(x, y, x2, y2, col, width) {
+ // cross out
+ ctx.fillStyle = col
+ ctx.lineWidth = width;
+ ctx.strokeStyle = col;
+ // NW -> SE
+ ctx.beginPath();
+ ctx.moveTo(x, y);
+ ctx.lineTo(x2, y2);
+ ctx.stroke();
+
+ // SW -> NE
+ ctx.beginPath();
+ ctx.moveTo(x, y2);
+ ctx.lineTo(x2, y);
+ ctx.stroke();
+}
+
function drawarrow(ctx,x1,y1,x2,y2,col,width, arrowsize) {
drawline(ctx, x1, y1, x2, y2, col, width);
drawarrowhead(ctx, x1, y1, x2, y2, col, arrowsize);
@@ -547,11 +581,10 @@ function drawarrowhead(ctx, x1, y1, x2, y2, col, size) {
}
var game = {
-
-
ratio: null,
curw: null,
curh: null,
+ levels: null,
canvas : document.createElement("canvas"),
@@ -638,22 +671,84 @@ var game = {
initgamevars : function() {
+ score = 0;
+ },
+
+ initlevelvars : function() {
// kill any existing objects
clearthings();
- score = 0;
overdesc = "";
curpath = [];
pathdir = -1;
pathvalid = false;
},
- start : function() {
- var x,y;
- this.frameNo = 0;
+ // goal1type goal1count goal2type goal2count etc...
+ addlevel : function () {
+ var i,mylevel,idx;
+ mylevel = new Object();
+ mylevel.goals = new Array();
+ for (i = 0 ; i < arguments.length; i += 2) {
+ idx = mylevel.goals.push(new Object()) - 1;
+ mylevel.goals[idx].type = arguments[i];
+ mylevel.goals[idx].count = arguments[i+1];
+ mylevel.goals[idx].progress = 0;
+ }
+ this.levels.push(mylevel);
+ },
- this.initgamevars();
+ // earn progress towards goals
+ progress : function(type, amt) {
+ var i;
+ // past last level!
+ if (curlevel >= game.levels.length) return false;
+
+ console.log("progress()");
+ for (i = 0 ; i < this.levels[curlevel].goals.length; i++ ) {
+ console.log("this goal type is " + this.levels[curlevel].goals[i].type);
+ if (this.levels[curlevel].goals[i].type == type) {
+ this.levels[curlevel].goals[i].progress += amt;
+ if (this.levels[curlevel].goals[i].progress >= this.levels[curlevel].goals[i].count) {
+ this.levels[curlevel].goals[i].progress = this.levels[curlevel].goals[i].count;
+ }
+ }
+ }
+ },
+
+ initlevels : function ( ) {
+ var mylevel,i,n;
+
+ console.log("doing level init");
+ this.levels = [];
+ this.addlevel(); // dummy level with 0 goals
+ this.addlevel("food", 10);
+ this.addlevel("points", 200);
+
+ /*
+ for (i = 0; i < this.levels.length; i++) {
+ console.log("Level " + (i+1) + " goals:");
+ for (n = 0; n < this.levels[i].goals.length; n++) {
+ console.log(GOALVERB[this.levels[i].goals[n].type] + " " +
+ this.levels[i].goals[n].count + " " +
+ this.levels[i].goals[n].type);
+ }
+ }
+ */
+ },
+
+ nextlevel : function() {
+ curlevel++;
+
+ this.initlevelvars();
+ this.populategrid();
+
+ this.state = "running";
+ },
+
+ populategrid : function() {
+ var i;
while (!anyvalidmoves()) {
var m;
clearthings();
@@ -673,6 +768,17 @@ var game = {
things[i].gridy -= GRIDH;
things[i].updatexy();
}
+ },
+
+ startgame : function() {
+ var x,y;
+ this.frameNo = 0;
+
+ this.initgamevars();
+ this.initlevelvars();
+ this.populategrid();
+
+ curlevel = 1;
this.state = "running";
},
@@ -689,12 +795,22 @@ var game = {
this.context.clearRect(0, 0, this.canvas.width-1, BOARDY-1);
if (game.state == "running") {
+ var y1 = 6;
+ var y2 = 32;
+
+ // show level
+ this.context.font = "16pt Futura";
+ this.context.textAlign = "center";
+ this.context.textBaseline = "top";
+ this.context.fillStyle = "#00aaee";
+ this.context.fillText("Level " + curlevel, SCREENW/2, y1);
+
// show score
this.context.font = "16pt Futura";
this.context.textAlign = "left";
this.context.textBaseline = "top";
this.context.fillStyle = "white";
- this.context.fillText("Score: " + addcommas(score), 16, 16);
+ this.context.fillText("Score: " + addcommas(score), 16, y2);
switch (thingsmoving()) {
case "parade":
@@ -720,17 +836,135 @@ var game = {
this.context.textAlign = "right";
this.context.textBaseline = "top";
this.context.fillStyle = col;
- this.context.fillText(texttodraw, SCREENW-16, 16);
+ this.context.fillText(texttodraw, SCREENW-16, y2);
}
} else if (game.state == "gameover") {
this.context.textAlign = "center";
this.context.textBaseline = "top";
shadowtext(this.context, "GAME OVER", 20, "red", SCREENW / 2, 5);
shadowtext(this.context, "Final Score: " + addcommas(score), 16, "white", SCREENW / 2, 35);
+ } else if (game.state == "levelcomplete") {
+ this.context.textAlign = "center";
+ this.context.textBaseline = "top";
+ shadowtext(this.context, "LEVEL COMPLETE!", 20, "#00ee00", SCREENW / 2, 5);
+ shadowtext(this.context, "Score: " + addcommas(score), 16, "white", SCREENW / 2, 35);
}
},
+ drawbottom : function() {
+ var bx = 0,by = BOARDY + GRIDH*GRIDSIZE + 15;
+ var bw = SCREENW, bh = SCREENH - by;
+ var x,y,ctx;
+ var margin = 10;
+ var indent = 50;
+ var boxindent = 20;
+ ctx = this.context;
+
+ if (game.state == "running") {
+ var texth = GOALTEXTSIZE+10;
+ var gradient;
+ var borderwidth = 5;
+ var goaltitlecol = "#00aaee";
+
+ // background
+ gradient = this.context.createLinearGradient(0, 0, bw, bh);
+ gradient.addColorStop(0, "#0000ff");
+ gradient.addColorStop(1, "#000055");
+
+ // clear
+ ctx.fillStyle = gradient;
+ ctx.fillRect(bx, by, bw, bh);
+
+ //outline
+ ctx.strokeStyle = "cyan";
+ ctx.lineWidth = 5;
+ ctx.beginPath();
+ ctx.rect(bx+(borderwidth/2), by+(borderwidth/2), bw-(borderwidth/2), bh-(borderwidth/2));
+ ctx.stroke();
+
+ // draw goals
+ x = bx + margin;
+ y = by + margin;
+ ctx.textAlign = "left";
+ ctx.textBaseline = "top";
+ shadowtext(ctx, "Goals:", GOALTEXTSIZE,goaltitlecol, x , y); y += texth;
+
+ if (curlevel < this.levels.length) {
+ for (n = 0; n < this.levels[curlevel].goals.length; n++) {
+ var goalcol = "#aaee00";
+ var goalmet = false;
+
+ if (this.levels[curlevel].goals[n].progress >= this.levels[curlevel].goals[n].count) {
+ goalmet = true;
+ }
+
+ if (goalmet) {
+ goalcol = "#00ddff";
+ } else {
+ goalcol = "#aaee00";
+ }
+
+ // goal name
+ var goaltext = GOALVERB[this.levels[curlevel].goals[n].type] + " " +
+ this.levels[curlevel].goals[n].count + " " +
+ this.levels[curlevel].goals[n].type;
+
+ var progtext = "[" + this.levels[curlevel].goals[n].progress + " / " +
+ this.levels[curlevel].goals[n].count + "]";
+
+ ctx.textAlign = "left";
+ ctx.textBaseline = "top";
+ shadowtext(ctx, goaltext, GOALTEXTSIZE,goalcol, x+indent, y);
+
+ ctx.textAlign = "right";
+ ctx.textBaseline = "top";
+ shadowtext(ctx, progtext, GOALTEXTSIZE,goalcol, SCREENW-indent, y);
+
+ // checkbox
+ var boxsize = texth-8;
+ var boxx = x+boxindent;
+ var boxy = y + texth/2 - boxsize/2;
+ ctx.beginPath();
+ ctx.lineWidth = 3;
+ ctx.strokeStyle = "black";
+ ctx.rect(boxx,boxy,boxsize,boxsize); // shadow1
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.lineWidth = 2;
+ ctx.strokeStyle = goalcol;
+ ctx.rect(boxx,boxy,boxsize,boxsize); // shadow1
+ ctx.stroke();
+
+ // ticked ?
+ if (goalmet) {
+ //drawcross(boxx,boxy, boxx+boxsize-1, boxy+boxsize-1, goalcol, 2);
+ var tickleftx = boxx + boxsize/4;
+ var ticklefty = boxy + boxsize/2;
+ var tickbasex = boxx + boxsize/2;
+ var tickbasey = boxy+boxsize-boxsize/8;
+ var tickrightx = boxx + boxsize - boxsize/5;
+ var tickrighty = boxy + boxsize/8;
+ drawline(ctx, tickleftx,ticklefty, tickbasex, tickbasey, goalcol, 2);
+ drawline(ctx, tickbasex, tickbasey, tickrightx, tickrighty, goalcol, 2);
+ }
+
+ y += texth;
+ }
+ } else {
+ // past last level
+ ctx.textAlign = "left";
+ ctx.textBaseline = "top";
+ shadowtext(ctx, "(none)", GOALTEXTSIZE,goaltitlecol, x+indent, y);
+ }
+ } else if (game.state == "levelcomplete") {
+ this.context.textAlign = "center";
+ this.context.textBaseline = "bottom";
+ shadowtext(this.context, "Tap for next level", TITLESTARTTEXTSIZE, "#00dd00", SCREENW / 2, by + bh/2);
+ }
+ },
+
drawtitle : function() {
var ratio,w,h;
var img = image['title'];
@@ -1079,6 +1313,8 @@ var game = {
if (ch == 'a') {
console.log("ending game");
game.state = "gameover";
+ } else if (ch == 'n') {
+ game.state = "levelcomplete";
}
},
@@ -1123,7 +1359,11 @@ var game = {
} else if (game.state == "title") {
game.setstate("help");
} else if (game.state == "help") {
- game.start();
+ game.startgame();
+ } else if (game.state == "levelcomplete") {
+ // TODO : show help for this level
+ // TODO: start next levle
+ game.nextlevel();
}
},
@@ -1275,6 +1515,7 @@ var game = {
curpath.splice(0, 1);
}
}
+ game.progress("parades", 1);
break;
}
@@ -1483,16 +1724,20 @@ function thing(gridx, gridy, type, text) {
var points = 0;
if (this.type == "food") {
points = FOODPOINTS;
+ game.progress("food", 1);
} else if (this.type == "llama") {
points = LLAMAPOINTS;
+ game.progress("llamas", 1);
} else if (this.type == "cat") {
if (this.eaten == true) {
points = SLEEPYCATPOINTS;
} else {
points = CATPOINTS;
}
+ game.progress("cats", 1);
}
score += points;
+ game.progress("points", points);
// add animation
things.push(new thing(this.gridx, this.gridy, "text", "+" + points));
@@ -1915,21 +2160,39 @@ function mainloop() {
things[i].draw();
}
}
- // hide top of canvads
+ // draw top of canvas (score etc)
game.drawtop();
+ // draw bottom of canvas (goals)
+ game.drawbottom();
+
// draw dragged arrow
game.drawpath();
- // check for valid moves
+ // check for game over and level over
if (!thingsmoving()) {
- if (!anyvalidmoves()) {
+ if (levelfinished()) {
+ game.state = "levelcomplete";
+ } else if (!anyvalidmoves()) {
game.state = "gameover";
}
}
}
}
+function levelfinished() {
+ var i;
+ // past last level!
+ if (curlevel >= game.levels.length) return false;
+
+ for (i = 0 ; i < game.levels[curlevel].goals.length; i++ ) {
+ if (game.levels[curlevel].goals[i].progress < game.levels[curlevel].goals[i].count) {
+ return false;
+ }
+ }
+ return true;
+}
+
function anyvalidmoves() {
var gotmoves = false;
var i;
diff --git a/todo b/todo
index 56ea4e3..42c5bc2 100644
--- a/todo
+++ b/todo
@@ -29,20 +29,36 @@ phone fixes as per http://www.html5rocks.com/en/mobile/touch/
https://www.smashingmagazine.com/2012/10/design-your-own-mobile-game/
-*put commas in score
+*create diff levels with goals
+*display goals at bottom of screen
+*implement progress() to earn progress towards goals
+*go to next level after meeting all goals
-different levels with goals
- eat x cheese
- get x parades
- get x llamas
- break x obstacles
- get x goats
- etc.
- later levels have multiple goals
+implement level-specific help text
+ when starting a levle, go to 'help text' state if it has one.
+ if not, just start the level
-use the bottom of the screen to show level goals
+ (make current 'help text' the text for level 1)
-title screen -> level select
+Level selection screen
+ grid of numbers for each level
+ 1 2 3 4 5
+ 6 7 8 9 10
+ ...etc
+
+ scrolling background of cat paws ?
+
+
+custom thing chance ratios for each level
+ 1: no llamas
+ 2: no llamas
+ 3: llamas
+
+better level complete animation
+ zoom in random cat picture?
+
+
+title screen -> level select -> help -> startgame
certain levels have help screens (ie. lev 1 is initial help)
@@ -50,6 +66,8 @@ remember which level you're up to
after each level, show cat picture.
+complex goal: form x parades of length y
+
sounds:
chomp