*create different levels with goals

*display goals at bottom of screen
*implement progress() to earn progress towards goals
*go to next level after meeting all goals
This commit is contained in:
Rob Pearce 2016-08-21 09:32:57 +10:00
parent 5cc296e61f
commit b0f3ec030c
2 changed files with 305 additions and 24 deletions

289
cat.html
View File

@ -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;

40
todo
View File

@ -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