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