diff --git a/cat.html b/cat.html index 4418a69..16b455c 100644 --- a/cat.html +++ b/cat.html @@ -36,9 +36,11 @@ canvas { // game vars var things = []; var score = 0; +var multiplier = 1; var overdesc = ""; var curpath = []; var pathdir = -1; +var pathdoor = null; var pathvalid = false; var curlevel = 1; var lastmx = -1, lastmy = -1; @@ -47,11 +49,14 @@ var lastmx = -1, lastmy = -1; // goal types var GOALVERB = { 'food': 'Eat', + 'turns': 'Survive', 'points': 'Earn', 'parades': 'Form', 'llamas': 'Clear', 'cats': 'Clear', 'goats': 'Clear', + 'doors': 'Enter', + 'sun cycles': 'Wait out', }; var FULLSTAR = "\u2605"; @@ -73,6 +78,7 @@ var THINGSIZE = 64; var TEXTSIZE = 16; // in points +var TEXTSIZEMULTIPLIER = 28; // in points var TEXTSPEED = 0.5; var TEXTFADESPEED = 0.05; var TEXTTIME = 35; @@ -112,6 +118,9 @@ var EXPLODETICKS=20; var EXPLODEGAIN= (THINGSIZE*2) / EXPLODETICKS; var EXPLODEFADESPEED = 1.0 / EXPLODETICKS; +var SHRINKTICKS=20; +var SHRINKLOSE= (THINGSIZE / SHRINKTICKS); + var LINEWIDTH=2; var CROSSWIDTH=2; @@ -142,7 +151,7 @@ var FOODPOINTS = 10; var LLAMAPOINTS = 100; var GOATPOINTS = 50; var CATPOINTS = 20; -var SLEEPYCATPOINTS = 30; +var SLEEPYCATPOINTS = 40; var overdesc = ""; @@ -203,7 +212,7 @@ function pathcomplete() { case "chomp": if ((curpath.length >= 2) && (curpath[0].type == "cat") && - (curpath[0].eaten == false)) { + !curpath[0].issleepy()) { var i; var ok = true; // everything else is food? @@ -292,12 +301,26 @@ function pathcontains(what) { return false; } +// return sfirst thing of matching type +function pathcontainstype(type) { + var i; + if (curpath == undefined) return false; + + for (i = 0; i < curpath.length; i += 1) { + if (curpath[i].type == type) { + return curpath[i]; + } + } + return null; +} + function isvalidpath(mypath) { var valid = true; var firstcat = false; var fcount = 0; var lcount = 0; var gcount = 0; + var count = 0; var i; for (i = 0; i < mypath.length - 1; i++) { @@ -321,6 +344,7 @@ function isvalidpath(mypath) { if (thisone.type == "goat") { gcount++; } + count++; if ((thisone.type == "cat") && (nextone.type == "cat")) { // no parades on level 1 @@ -331,6 +355,9 @@ function isvalidpath(mypath) { } else if ((thisone.type == "goat") && (nextone.type == "cat" || nextone.type == "llama" || nextone.type == "goat")) { // goat can go to llama or cat // ok + } else if ((nextone.type == "door") && (count >= PARADELENGTH) && (fcount == 0) && (count >= 3) && + (thisone.type == "goat" || thisone.type == "cat" || thisone.type == "llama")) { + // completed parades can extend into doors } else if ((thisone.type == "cat") && nextone.type == "llama") { // no parades on level 1 if (curlevel == 1) { @@ -522,7 +549,7 @@ function thingsfalling() { function thingsmoving() { var i; for (i = 0; i < things.length; i += 1) { - if (things[i].state == "parade") { + if ((things[i].state == "parade") || (things[i].state == "shrink")) { return "parade"; } } @@ -878,6 +905,16 @@ var game = { return true; }, + turneffects : function() { + var i; + for (i = 0; i < things.length; i += 1) { + // suns can move again + if (things[i].type == "sunlight" && things[i].counter == 1) { + things[i].counter = 0; + } + } + }, + resize : function() { this.curh = window.innerHeight; this.curw = this.curh * this.ratio; @@ -901,6 +938,7 @@ var game = { initgamevars : function() { score = 0; + multiplier = 1; this.frameNo = 0; }, @@ -909,9 +947,7 @@ var game = { clearthings(); overdesc = ""; - curpath = []; - pathdir = -1; - pathvalid = false; + clearpath(); }, addlevel : function (lev, hashelp) { @@ -1028,7 +1064,7 @@ var game = { calcstarpoints : function( lev ) { var i; - var min = 0,gotpointsgoal = false; + var min = 0,pointsgoal = false; // calculate minimum points required to win // first pass for (i = 0; i < this.levels[lev].goals.length; i++) { @@ -1046,31 +1082,31 @@ var game = { case "goats": min += GOATPOINTS * this.levels[lev].goals[i].count; break; + case "turns": + min += FOODPOINTS * this.levels[lev].goals[i].count; + break; + case "doors": + // double the value of a minimum-score parade + min += Math.min(CATPOINTS, SLEEPYCATPOINTS) * 3 * 2 * this.levels[lev].goals[i].count; + break; + case "sun cycles": + min += SLEEPYCATPOINTS*5 * this.levels[lev].goals[i].count; + break; } } // second pass - overrides for (i = 0; i < this.levels[lev].goals.length; i++) { switch (this.levels[lev].goals[i].type) { case "points": - min = this.levels[lev].goals[i].count; - gotpointsgoal = true; + pointsgoal = this.levels[lev].goals[i].count; break; } } this.levels[lev].starpoints = new Array(); - if (gotpointsgoal) { - // if the level ends as soon as you get a certain # of points, - // your only hope of boosting higher is to get LOTS of points - // on your last turn. - this.levels[lev].starpoints[0] = min; - this.levels[lev].starpoints[1] = Math.floor(min * 1.1); - this.levels[lev].starpoints[2] = Math.floor(min * 1.2); - } else { - this.levels[lev].starpoints[0] = Math.floor(min); - this.levels[lev].starpoints[1] = Math.floor(min * 2); - this.levels[lev].starpoints[2] = Math.floor(min * 3); - } + this.levels[lev].starpoints[0] = Math.floor(Math.max(min, pointsgoal)); + this.levels[lev].starpoints[1] = Math.floor(Math.max(min * 2, pointsgoal*1.5)); + this.levels[lev].starpoints[2] = Math.floor(Math.max(min * 3, pointsgoal*2)); }, initlevels : function ( ) { @@ -1081,49 +1117,62 @@ var game = { this.addlevel(1, true); this.addlevelgoals(1, "food", 5); this.setstarpoints(1, 50, 60, 80); - this.addlevelthings(1, "cat", 50, "food", 100); + this.addlevelthings(1, "cat", 50, "food", 50); this.addlevel(2, true); this.addlevelgoals(2, "cats", 10); this.addlevelgoals(2, "parades", 3); - this.addlevelthings(2, "cat", 45, "food", 100); + this.addlevelthings(2, "cat", 45, "food", 55); this.addlevel(3, true); - this.addlevelthings(3, "cat", 45, "food", 90, "llama", 100); + this.addlevelallowedthings(3, "cat", "food", "llama"); this.addlevelgoals(3, "llamas", 3); this.addlevel(4, true); this.addlevelgoals(4, "llamas", 5); this.addlevelgoals(4, "food", 5); - this.addlevelthings(4, "cat", 45, "food", 90, "llama", 100); + this.addlevelallowedthings(4, "cat", "food", "llama"); this.addlevel(5, false); - this.addlevelgoals(5, "points", 1000); - this.addlevelthings(5, "cat", 45, "food", 90, "llama", 100); + this.addlevelgoals(5, "turns", 10); + this.addlevelgoals(5, "points", 600); + this.addlevelallowedthings(5, "cat", "food", "llama"); // introduce goats! this.addlevel(6, true); this.addlevelgoals(6, "goats", 3); - this.addlevelthings(6, "goat", 10, "cat", 60, "food", 90, "llama", 100); // higher than normal goat chance + this.addlevelthings(6, "goat", 10, "cat", 50, "food", 30, "llama", 10); // higher than normal goat chance this.addlevelforcethings(6, "goat", 1); this.addlevel(7, false); this.addlevelallowedthings(7, "goat", "cat", "food", "llama"); - this.addlevelgoals(7, "points", 1000); + this.addlevelgoals(7, "turns", 15); this.addlevelgoals(7, "llamas", 2); this.addlevelgoals(7, "goats", 2); this.addlevel(8, false); this.addlevelallowedthings(8,"goat", "cat", "food", "llama"); - this.addlevelgoals(8, "points", 1100); + this.addlevelgoals(8, "turns", 20); this.addlevelgoals(8, "llamas", 3); // introduce doors -/* - this.addlevel(8, false); - this.addlevelgoals(8, "doors", 1); - this.addlevelforcethings(8, "door", 1); -*/ + this.addlevel(9, true); + this.addlevelallowedthings(9,"goat", "cat", "food", "llama", "door"); + this.addlevelgoals(9, "doors", 1); + this.addlevelforcethings(9, "door", 1); + + this.addlevel(10, false); + this.addlevelallowedthings(10,"goat", "cat", "food", "llama", "door"); + this.addlevelgoals(10, "points", 2000); + this.addlevelgoals(10, "turns", 15); + + // introduce sunlight + this.addlevel(11, true); + this.addlevelallowedthings(11,"goat", "cat", "food", "llama", "door", "sunlight"); + this.addlevelgoals(11, "turns", 10); + this.addlevelgoals(11, "sun cycles", 1); + this.addlevelforcethings(11, "sunlight", 1); + for (i = 1; i < this.levels.length; i++) { var extrastars; @@ -1175,14 +1224,14 @@ var game = { } // force items - if (game.levels[curlevel].forcethings != undefined) { - for (i = 0; i < game.levels[curlevel].forcethings.length; i++) { - var wanttype,thisnum; - wanttype = game.levels[curlevel].forcethings[i].type; - wantnum = game.levels[curlevel].forcethings[i].howmany; + if (game.levels[curlevel].forcelist != undefined) { + for (i = 0; i < game.levels[curlevel].forcelist.length; i++) { + var wanttype,wantnum; + wanttype = game.levels[curlevel].forcelist[i].type; + wantnum = game.levels[curlevel].forcelist[i].howmany; // while we don't have enough... - while (countthingsoftype(wanttype) < num) { + while (countthingsoftype(wanttype) < wantnum) { // add one. var idx; idx = rnd(things.length); @@ -1451,7 +1500,7 @@ var game = { } else { var zoomw,zoomh; // draw winning cat image - console.log("drawing win img. tapx:" + tapx + " tapy:" + tapy + " tapw:" + tapw + " taph:" + taph); + //console.log("drawing win img. tapx:" + tapx + " tapy:" + tapy + " tapw:" + tapw + " taph:" + taph); zoomw = tapw * game.winimgsize; zoomh = taph * game.winimgsize; @@ -2314,6 +2363,246 @@ var game = { y += gridsize; cury = y; + } else if (curlevel == 9) { + cury = this.drawhelpsubtitle(ctx, "Doors", cury); + + ctx.textAlign = "left"; + ctx.textBaseline = "bottom"; + shadowtext(ctx, "Cats love escaping outside through doors.", HELPTEXTSIZE,helpcol, textxspace, cury); + cury += HELPTEXTYSPACE; + shadowtext(ctx, "Ending a parade on a door will score double points!", HELPTEXTSIZE,helpcol, textxspace, cury); + cury += HELPTEXTYSPACE; + + // top line of parade + x = imgsize; + y = cury; + row1y = y; + ctx.drawImage(image['cat'], x, y, imgsize, imgsize); + linex[0] = x + imgsize/2; + liney[0] = y + imgsize/2; + x += gridsize; + + ctx.drawImage(image['catscared'], x, y, imgsize, imgsize); + linex[3] = x + imgsize/2; + liney[3] = y + imgsize/2; + x += gridsize; + + ctx.drawImage(image['llama'], x, y, imgsize, imgsize); + linex[4] = x + imgsize/2; + liney[4] = y + imgsize/2; + x += gridsize; + + // middle line of parade + x = imgsize; + y += gridsize; + row2y = y; + + ctx.drawImage(image['goat'], x, y, imgsize, imgsize); + linex[1] = x + imgsize/2; + liney[1] = y + imgsize/2; + x += gridsize; + + ctx.drawImage(image['catscared'], x, y, imgsize, imgsize); + linex[2] = x + imgsize/2; + liney[2] = y + imgsize/2; + x += gridsize; + + ctx.drawImage(image['door'], x, y, imgsize, imgsize); + linex[5] = x + imgsize/2; + liney[5] = y + imgsize/2; + + + // parade lines + cury = y + HELPTEXTYSPACE; + for (i = 0; i < 5; i++) { + drawline(ctx, linex[i], liney[i], linex[i+1], liney[i+1], "green", LINEWIDTH); + } + drawarrow(ctx, linex[4], liney[4], linex[5], liney[5], "green", LINEWIDTH, PATHARROWSIZE); + + // arrow to middle + x = x + gridsize; + y = row2y; + + x2 = midpoint2 - 10; + y2 = y; + + drawarrow(ctx, x, y, x2, y2, "red", HELPLINEWIDTH, HELPARROWSIZE); + + // explain points for doors + ctx.textAlign = "left"; + ctx.textBaseline = "top"; + x = midpoint2; + + y = row2y - HELPTEXTYSPACE/2; + shadowtext(ctx, "x2 points", HELPTEXTSIZE,pointscol, x, y); + + cury = y; + cury += HELPTEXTYSPACE; + cury += HELPTEXTYSPACE; + cury += HELPTEXTYSPACE; + cury += HELPTEXTYSPACE; + + ctx.textAlign = "left"; + ctx.textBaseline = "bottom"; + shadowtext(ctx, "Doors don't fall down like other objects.", HELPTEXTSIZE,helpcol, textxspace, cury); + cury += HELPTEXTYSPACE; + shadowtext(ctx, "They are only removed when a parade enters them.", HELPTEXTSIZE,helpcol, textxspace, cury); + cury += HELPTEXTYSPACE; + + // top line of door fall example + x = imgsize; + y = cury; + row1y = y; + ctx.drawImage(image['goat'], x, y, imgsize, imgsize); + x += gridsize; + + ctx.drawImage(image['door'], x, y, imgsize, imgsize); + x += gridsize; + + ctx.drawImage(image['cheese'], x, y, imgsize, imgsize); + x += gridsize; + + // middle line of parade + x = imgsize; + y += gridsize; + row2y = y; + + ctx.drawImage(image['cheese'], x, y, imgsize, imgsize); + linex[1] = x + imgsize/2; + liney[1] = y + imgsize/2; + x += gridsize; + + ctx.drawImage(image['cat'], x, y, imgsize, imgsize); + linex[0] = x + imgsize/2; + liney[0] = y + imgsize/2; + x += gridsize; + + ctx.drawImage(image['cheese'], x, y, imgsize, imgsize); + + // cat eating food line + drawarrow(ctx, linex[0], liney[0], linex[1], liney[1], "green", LINEWIDTH, PATHARROWSIZE); + + // arrow to middle + x = x + gridsize; + y = row2y; + + x2 = midpoint2 - 10; + y2 = y; + + drawarrow(ctx, x, y, x2, y2, "red", HELPLINEWIDTH, HELPARROWSIZE); + + // top line of part 2 of door fall example + x = midpoint2; + y = cury; + row1y = y; + ctx.drawImage(image['goat'], x, y, imgsize, imgsize); + x += gridsize; + + ctx.drawImage(image['door'], x, y, imgsize, imgsize); + x += gridsize; + + ctx.drawImage(image['cheese'], x, y, imgsize, imgsize); + x += gridsize; + + // middle line of parade + x = midpoint2; + y += gridsize; + row2y = y; + + ctx.drawImage(image['cat'], x, y, imgsize, imgsize); + linex[0] = x + imgsize/2; + liney[0] = y + imgsize/2; + + x += gridsize*2; + + ctx.drawImage(image['cheese'], x, y, imgsize, imgsize); + } else if (curlevel == 11) { + cury = this.drawhelpsubtitle(ctx, "Sunlight", cury); + + ctx.textAlign = "left"; + ctx.textBaseline = "bottom"; + shadowtext(ctx, "Cats love luxuriating in sunlight.", HELPTEXTSIZE,helpcol, textxspace, cury); + cury += HELPTEXTYSPACE; + shadowtext(ctx, "Any cats next to a sun will fall asleep.", HELPTEXTSIZE,helpcol, textxspace, cury); + cury += HELPTEXTYSPACE; + + x = (SCREENW / 2) - gridsize/2 - gridsize; + y = cury; + ctx.drawImage(image['catfull'], x, y, imgsize, imgsize); + + x += gridsize; + y = cury; + ctx.drawImage(image['sunlight'], x, y, imgsize, imgsize); + + x += gridsize; + y = cury; + ctx.drawImage(image['catfull'], x, y, imgsize, imgsize); + + cury += gridsize; + + x = (SCREENW / 2) - gridsize/2 - gridsize; + y = cury; + ctx.drawImage(image['cheese'], x, y, imgsize, imgsize); + + x += gridsize; + y = cury; + ctx.drawImage(image['goat'], x, y, imgsize, imgsize); + + x += gridsize; + y = cury; + ctx.drawImage(image['cheese'], x, y, imgsize, imgsize); + + cury += HELPTEXTYSPACE; + cury += HELPTEXTYSPACE; + cury += HELPTEXTYSPACE; + cury += HELPTEXTYSPACE; + + shadowtext(ctx, "Each turn, suns swap places with the object below.", HELPTEXTSIZE,helpcol, textxspace, cury); + cury += HELPTEXTYSPACE; + shadowtext(ctx, "Cats will awaken once out of the sunlight.", HELPTEXTSIZE,helpcol, textxspace, cury); + cury += HELPTEXTYSPACE; + + x = (SCREENW / 2) - gridsize/2 - gridsize; + y = cury; + ctx.drawImage(image['cat'], x, y, imgsize, imgsize); + + x += gridsize; + y = cury; + ctx.drawImage(image['goat'], x, y, imgsize, imgsize); + linex[0] = x + imgsize/2; + liney[0] = y + imgsize/2; + + x += gridsize; + y = cury; + ctx.drawImage(image['cat'], x, y, imgsize, imgsize); + + cury += gridsize; + + x = (SCREENW / 2) - gridsize/2 - gridsize; + y = cury; + ctx.drawImage(image['cheese'], x, y, imgsize, imgsize); + + x += gridsize; + y = cury; + ctx.drawImage(image['sunlight'], x, y, imgsize, imgsize); + linex[1] = x + imgsize/2; + liney[1] = y + imgsize/2; + + x += gridsize; + y = cury; + ctx.drawImage(image['cheese'], x, y, imgsize, imgsize); + + drawarrow(ctx, linex[0], liney[0], linex[1], liney[1], "#dd0000", LINEWIDTH, PATHARROWSIZE); + drawarrow(ctx, linex[1], liney[1], linex[0], liney[0], "#dd0000", LINEWIDTH, PATHARROWSIZE); + + cury += HELPTEXTYSPACE; + cury += HELPTEXTYSPACE; + cury += HELPTEXTYSPACE; + cury += HELPTEXTYSPACE; + + + shadowtext(ctx, "After reaching the the bottom of the play area, suns will set.", HELPTEXTSIZE,helpcol, textxspace, cury); + cury += HELPTEXTYSPACE; } @@ -2620,6 +2909,7 @@ var game = { } else if (game.state == "gameover") { if ((realx >= tapx) && (realx <= tapx + tapw) && (realy >= tapy) && (realy <= tapy + taph)) { + game.clearlevelprogress(curlevel); game.setstate("levselect"); } } else if (game.state == "title") { @@ -2838,14 +3128,29 @@ var game = { // first one chomps last one curpath[0].chomp(curpath[curpath.length - 1]); + + game.progress("turns", 1); break; case "parade": + multiplier = 1; if (curpath.length >= 2) { // should always be true var i; // everything in the path exits via a parade + + pathdoor = pathcontainstype("door"); + if (pathdoor) multiplier++; + for (i = 0; i < curpath.length; i++) { - curpath[i].givepoints(); - curpath[i].startparade(); + // ... except doors + if (curpath[i].type == "door") { + game.progress("doors", 1); + console.log("path has doors!"); + // add animation + things.push(new thing(curpath[i].gridx, curpath[i].gridy, "text", "x" + multiplier)); + } else { + curpath[i].givepoints(); + curpath[i].startparade(); + } } clearpath(); } else { @@ -2857,11 +3162,16 @@ var game = { curpath.splice(0, 1); } } + multiplier = 1; game.progress("parades", 1); + game.progress("turns", 1); break; } clearpath(); + + // things that happen once per turn. + game.turneffects(); }, } @@ -2932,6 +3242,7 @@ function shadowtext(ctx, text, size, col, x, y) { function getrandomtype() { var roll,tot,type = null,i; var thinglist,maxroll = 0; + var speciallist = null; if (curlevel >= game.levels.length) { thinglist = null; @@ -2939,20 +3250,39 @@ function getrandomtype() { thinglist = game.levels[curlevel].thinglist; } + // construct a list of permitted special things + var poss = [ "sunlight" ] ; + for (i in poss ) { + if (!game.isbanned(curlevel, poss[i])) { + if (speciallist == undefined) { + speciallist = []; + } + speciallist.push(poss[i]); + } + } + + console.log("speciallist is " + speciallist); + if (thinglist == undefined || thinglist.length == 0) { var f; thinglist = new Array(); // default thinglist // must be sorted from low to high! - if (!game.isbanned(curlevel, 'goat')) thinglist.push({ type: 'goat', pct: 5 } ); - if (!game.isbanned(curlevel, 'llama')) thinglist.push({ type: 'llama', pct: 10 } ); - if (!game.isbanned(curlevel, 'cat')) thinglist.push({ type: 'cat', pct: 40 } ); - if (!game.isbanned(curlevel, 'food')) thinglist.push({ type: 'food', pct: 45 } ); + if (!game.isbanned(curlevel, 'door')) thinglist.push({ type: 'door', pct: 10 } ); + if (speciallist != undefined) { + console.log("specials are possible"); + thinglist.push({ type: 'special', pct: 10 } ); + } + if (!game.isbanned(curlevel, 'goat')) thinglist.push({ type: 'goat', pct: 10 } ); + if (!game.isbanned(curlevel, 'llama')) thinglist.push({ type: 'llama', pct: 20 } ); + if (!game.isbanned(curlevel, 'food')) thinglist.push({ type: 'food', pct: 40 } ); + if (!game.isbanned(curlevel, 'cat')) thinglist.push({ type: 'cat', pct: 45 } ); } for (i = 0; i < thinglist.length; i++) { maxroll += thinglist[i].pct; + //console.log(thinglist[i].type + ": " + thinglist[i].pct + " [" + maxroll + "]"); } roll = rnd(maxroll); @@ -2960,14 +3290,19 @@ function getrandomtype() { for (i = 0; i < thinglist.length; i++) { tot += thinglist[i].pct; if (roll <= tot) { + console.log("rolled " + roll + "/" + maxroll + " --> " + thinglist[i].type + " [<= " + tot + "]"); + type = thinglist[i].type; break; } } - if (type == null) { + if (type == null || type == undefined) { console.log("couldn't find type! roll is " + roll); type = 'cat'; + } else if (type == "special") { + // pick from list of specials + type = speciallist[rnd(speciallist.length)]; } return type; } @@ -2979,9 +3314,28 @@ function coord(x,y) { function thing(gridx, gridy, type, text) { this.opacity = 1.0; + this.isnew = true; if (type == "random") { type = getrandomtype(); + console.log("got random type "+ type); + } + // used for various things + if (type == "sunlight") { + this.counter = 1; + } else { + this.counter = 0; + } + + if (type == undefined) { + console.log("type is now " + type); + debugger; + } + + if (text == undefined) { + this.name = type + "-" + getrandomname(); + } else { + this.name = text; } this.type = type; @@ -2996,7 +3350,11 @@ function thing(gridx, gridy, type, text) { this.color = "#cccccc"; break; case "text": - this.color = "#00cc00"; + if (this.name.indexOf("x") == 0) { + this.color = "#00dddd"; + } else { + this.color = "#00cc00"; + } break; default: // should never happen this.color = getrandomcolour(); @@ -3004,17 +3362,16 @@ function thing(gridx, gridy, type, text) { } if (this.type == "text") { - this.size = TEXTSIZE; + if (this.name.indexOf("x") == 0) { + this.size = TEXTSIZEMULTIPLIER; + } else { + this.size = TEXTSIZE; + } } else { this.size = THINGSIZE; } this.lifetime = 0; - if (text == undefined) { - this.name = type + "-" + getrandomname(); - } else { - this.name = text; - } this.gridx = gridx; if (gridy == "top") { @@ -3040,9 +3397,9 @@ function thing(gridx, gridy, type, text) { this.path = []; this.pathspeed = PARADESPEED; - this.expcount = 0; this.expmax = 0; + this.eaten = false; if (this.type == "text" ) { this.x = gridx * GRIDSIZE + (GRIDSIZE/2); @@ -3052,7 +3409,27 @@ function thing(gridx, gridy, type, text) { this.y = gridy * GRIDSIZE + (GRIDSIZE/2) - (this.size/2); } - this.eaten = false; + + this.issleepy = function() { + if (this.type == "cat") { + if (this.eaten == true) { + return true; + } else if (isadjacenttotype(this, "sunlight")) { + return true; + } + } + return false; + } + + this.canfall = function() { + // doors can't fall after their initial drop + if (this.type == "door") { + if ((this.gridy >= 0) && (this.new == false)) { + return false; + } + } + return true; + } this.pushpath = function(x,y) { this.path.push(new coord(x, y)); @@ -3077,7 +3454,7 @@ function thing(gridx, gridy, type, text) { } // chomp - if (isadjacenttotype(this, "food") && !this.eaten) { + if (isadjacenttotype(this, "food") && !this.issleepy()) { return true; } @@ -3122,7 +3499,7 @@ function thing(gridx, gridy, type, text) { points = LLAMAPOINTS; game.progress("llamas", 1); } else if (this.type == "cat") { - if (this.eaten == true) { + if (this.issleepy()) { points = SLEEPYCATPOINTS; } else { points = CATPOINTS; @@ -3132,11 +3509,16 @@ function thing(gridx, gridy, type, text) { points = GOATPOINTS; game.progress("goats", 1); } - score += points; - game.progress("points", points); - // add animation - things.push(new thing(this.gridx, this.gridy, "text", "+" + points)); + points *= multiplier; + + if (points > 0) { + score += points; + game.progress("points", points); + + // add animation + things.push(new thing(this.gridx, this.gridy, "text", "+" + points)); + } } @@ -3144,12 +3526,12 @@ function thing(gridx, gridy, type, text) { var desc = ""; if (this.type == "cat") { if (isadjacenttotype(this, "llama")) { - if (this.eaten == true) { + if (this.issleepy() == true) { desc = "scared sleepy cat"; } else { desc = "scared cat"; } - } else if (this.eaten == true) { + } else if (this.issleepy() == true) { desc = "sleepy cat"; } else { desc = "cat"; @@ -3187,17 +3569,14 @@ function thing(gridx, gridy, type, text) { if (this.type == "text") { ctx.textAlign = "center"; ctx.textBaseline = "middle"; - shadowtext(ctx, this.name, TEXTSIZE, this.color, BOARDX + this.x,BOARDY + this.y); + shadowtext(ctx, this.name, this.size, this.color, BOARDX + this.x,BOARDY + this.y); } else { if (this.type == "cat") { var myimage; - if (this.state == "exploding") { - } - if (isadjacenttotype(this, "llama")) { myimage = image['catscared']; - } else if (this.eaten == true) { + } else if (this.issleepy() == true) { myimage = image['catfull']; } else { myimage = image['cat']; @@ -3208,6 +3587,13 @@ function thing(gridx, gridy, type, text) { myimage = image['cheese']; } else if (this.type == "goat") { myimage = image['goat']; + } else if (this.type == "door") { + myimage = image['door']; + } else if (this.type == "sunlight") { + myimage = image['sunlight']; + } else { + console.log("ERROR - no image for type " + this.type); + console.log(this); } howbig = this.size; myx = this.x; @@ -3365,6 +3751,7 @@ function thing(gridx, gridy, type, text) { this.isanimating = function() { if (this.type == "text") return true; if (this.state == "explode") return true; + if (this.state == "shrink") return true; if (this.state == "parade") return true; return false; } @@ -3375,6 +3762,12 @@ function thing(gridx, gridy, type, text) { this.state = "explode"; } + this.startshrink = function() { + this.expcount=1; + this.expmax=SHRINKTICKS; + this.state = "shrink"; + } + this.startparade = function() { var i,startidx=-1; // find our position in the current path @@ -3396,14 +3789,19 @@ function thing(gridx, gridy, type, text) { nextidx = i+1; if (nextidx >= curpath.length) { var dir; - // we're at the end. move off the screen in the direction of the path. + if (pathdoor != undefined) { + // we're at a door. vanish. + this.pushpath(-1, -1); + } else { + // we're at the end. move off the screen in the direction of the path. - // find dir from previous to us... - // don't worry about checking for being the first element cause - // parades can't be 1 in length; - dir = getdir(curpath[i-1],curpath[i]); - this.pushpath(curpath[i].x + (DIRXMOD[dir]*GRIDSIZE*GRIDW), - curpath[i].y + (DIRYMOD[dir]*GRIDSIZE*GRIDH)); + // find dir from previous to us... + // don't worry about checking for being the first element cause + // parades can't be 1 in length; + dir = getdir(curpath[i-1],curpath[i]); + this.pushpath(curpath[i].x + (DIRXMOD[dir]*GRIDSIZE*GRIDW), + curpath[i].y + (DIRYMOD[dir]*GRIDSIZE*GRIDH)); + } } else { this.pushpath(curpath[nextidx].x, curpath[nextidx].y); } @@ -3453,42 +3851,67 @@ function thing(gridx, gridy, type, text) { this.y = (this.gridy * GRIDSIZE) + (GRIDSIZE/2) - (this.size/2); } - } else if (this.state == "parade") { + } else if (this.state == "shrink") { + game.dirty = true; // need to redraw + this.expcount++; + if (this.expcount >= this.expmax) { + this.kill(); + } else { + // get smaller + this.size -= SHRINKLOSE; + + if (this.size < 1) this.size == 1; + + // adjust x/y + this.x = (this.gridx * GRIDSIZE) + (GRIDSIZE/2) - (this.size/2); + this.y = (this.gridy * GRIDSIZE) + (GRIDSIZE/2) - (this.size/2); + } + } else if ((this.state == "parade") || (this.state == "swapping")) { // move towards next cell in path var nextx = this.path[0].x; var nexty = this.path[0].y; var xdone = 0, ydone = 0; game.dirty = true; // need to redraw - - if (this.x < nextx) { - this.x += this.pathspeed; - if (this.x > nextx) xdone = true; - } else if (this.x > nextx) { - this.x -= this.pathspeed; - if (this.x < nextx) xdone = true; + + // special case: door. + if ((nextx == -1) && (nexty == -1)) { + this.path = []; + this.startshrink(); } else { - xdone = true; - } + if (this.x < nextx) { + this.x += this.pathspeed; + if (this.x > nextx) xdone = true; + } else if (this.x > nextx) { + this.x -= this.pathspeed; + if (this.x < nextx) xdone = true; + } else { + xdone = true; + } - if (this.y < nexty) { - this.y += this.pathspeed; - if (this.y > nexty) ydone = true; - } else if (this.y > nexty) { - this.y -= this.pathspeed; - if (this.y < nexty) ydone = true; - } else { - ydone = true; - } + if (this.y < nexty) { + this.y += this.pathspeed; + if (this.y > nexty) ydone = true; + } else if (this.y > nexty) { + this.y -= this.pathspeed; + if (this.y < nexty) ydone = true; + } else { + ydone = true; + } - this.calcgridxy(); + this.calcgridxy(); - // at destination? - if (xdone && ydone) { - this.poppath(); - // path finished? - if (this.path == undefined || this.path.length == 0) { - this.kill(); + // at destination? + if (xdone && ydone) { + this.poppath(); + // path finished? + if (this.path == undefined || this.path.length == 0) { + if (this.state == "catparade" ){ + this.kill(); + } else { + this.state = "stop"; + } + } } } } else { @@ -3496,7 +3919,7 @@ function thing(gridx, gridy, type, text) { if ((this.gridy >= GRIDH-1)) { atbottom = true; } - if (!atbottom && !this.getstoppedbelowthing()) { + if (!atbottom && !this.getstoppedbelowthing() && this.canfall()) { game.dirty = true; // need to redraw // accelerate this.yspeed += GRAVITY; @@ -3603,7 +4026,14 @@ function mainloop() { // check for game over and level over if (game.state == "running") { if (!thingsmoving()) { - if (levelfinished()) { + var i; + if (pathdoor != undefined) { + // still a path door needing to be closed/destroyed? + pathdoor.givepoints(); + pathdoor.addabove(); + pathdoor.startshrink(); + pathdoor = null; + } else if (levelfinished()) { // record score if (score > playerdata.levscore[curlevel]) { playerdata.setlevscore(curlevel, score); @@ -3612,7 +4042,37 @@ function mainloop() { game.setstate("levelcomplete"); } else if (!anyvalidmoves()) { game.setstate("gameover"); + } + // mark things as not new + for (i = 0; i < things.length; i += 1) { + if (things[i].gridy >= 0) things[i].isnew = false; } + // suns move down once + for (i = 0; i < things.length; i += 1) { + if (things[i].type == "sunlight" && things[i].gridy >= 0 && !things[i].isnew && + things[i].state != "swapping" && things[i].counter == 0) { + if ((things[i].gridy >= GRIDH-1)) { + // at bottom - disappear + things[i].addabove(); + things[i].startexplode(); + game.progress("sun cycles", 1); + } else { + var thingbelow; + // move down + things[i].pushpath(things[i].x, things[i].y + GRIDSIZE); + things[i].state = "swapping"; + things[i].counter = 1; + + thingbelow = getgridthing(things[i].gridx, things[i].gridy+1); + if (thingbelow != undefined && thingbelow.state != "swapping" && + thingbelow.type != "sunlight") { + thingbelow.pushpath(things[i].x, things[i].y); + thingbelow.state = "swapping"; + } + } + } + } + } } } diff --git a/todo b/todo index ea2b265..3fdbf9c 100644 --- a/todo +++ b/todo @@ -30,31 +30,37 @@ phone fixes as per http://www.html5rocks.com/en/mobile/touch/ https://www.smashingmagazine.com/2012/10/design-your-own-mobile-game/ -*restrict catparades on level 1 -*make help text for llamas come later - *(keep orig help around for reference) - *lv1 help: just how to eat cheese - *lv2 help: just how to make cat parades - *lv3 help: llamas scaring cats, parades can have one llama - *lv4 help: (preview of features) -*Level selection screen +*make forcethings actually work -*Store level progress and hiscores using localstorage +replace points goal with 'survive x turns' + +*doors - +*sunlight +* - makes nearby cats sleepy +* - swaps with thing below each turn. +* - then disappears off the bottom + +parademovement bounces up down! + slow down parades to test. -*goats -* cats and goats can gang up on on llamas. -* parades with a goat can have multiple llamas -* goats can't start parades (only cats) -* 50 points -*goat help text. -*level select screen needs "back to title" -*mechanism to force at least one of a given thing type on a level. -*better level complete animation - zoom in random cat picture. -*only redraw screen if dirty! -*switch to "star" system for unlocking levels -*fix bug when calculating whether there are any valid moves left -*fix bug in random thing selection +still bugs in getarndomtype() ? + +bug with 2 suns above each other + +special things - 5% chance of any +{ + + white cat - + move cat on to it to FIGHT. + destroy 9 in a block around. + pow pow cloud. + + + doona - hide under it in the cold ? + + mouse toy - match 3 to explode everything ? +} more goals: form x parades of length y @@ -68,20 +74,6 @@ sounds: - -doors - - appear on the EDGE of the map ? - PARADE INTO DOOR = triple points (and door vanishes) - - -sunlight - makes nerby cats sleepy - -doona - hide under it in the cold ? - -mouse toy - match 3 to explode everything ? - - - make path part of game object make things part of game object