*make forcethings actually work

*survive x turns goal
*doors
*sunlight
This commit is contained in:
Rob Pearce 2016-08-23 01:13:47 +10:00
parent 34ee0d34f3
commit 0d41c312ac
2 changed files with 594 additions and 142 deletions

592
cat.html
View File

@ -36,9 +36,11 @@ canvas {
// game vars // game vars
var things = []; var things = [];
var score = 0; var score = 0;
var multiplier = 1;
var overdesc = ""; var overdesc = "";
var curpath = []; var curpath = [];
var pathdir = -1; var pathdir = -1;
var pathdoor = null;
var pathvalid = false; var pathvalid = false;
var curlevel = 1; var curlevel = 1;
var lastmx = -1, lastmy = -1; var lastmx = -1, lastmy = -1;
@ -47,11 +49,14 @@ var lastmx = -1, lastmy = -1;
// goal types // goal types
var GOALVERB = { var GOALVERB = {
'food': 'Eat', 'food': 'Eat',
'turns': 'Survive',
'points': 'Earn', 'points': 'Earn',
'parades': 'Form', 'parades': 'Form',
'llamas': 'Clear', 'llamas': 'Clear',
'cats': 'Clear', 'cats': 'Clear',
'goats': 'Clear', 'goats': 'Clear',
'doors': 'Enter',
'sun cycles': 'Wait out',
}; };
var FULLSTAR = "\u2605"; var FULLSTAR = "\u2605";
@ -73,6 +78,7 @@ var THINGSIZE = 64;
var TEXTSIZE = 16; // in points var TEXTSIZE = 16; // in points
var TEXTSIZEMULTIPLIER = 28; // in points
var TEXTSPEED = 0.5; var TEXTSPEED = 0.5;
var TEXTFADESPEED = 0.05; var TEXTFADESPEED = 0.05;
var TEXTTIME = 35; var TEXTTIME = 35;
@ -112,6 +118,9 @@ var EXPLODETICKS=20;
var EXPLODEGAIN= (THINGSIZE*2) / EXPLODETICKS; var EXPLODEGAIN= (THINGSIZE*2) / EXPLODETICKS;
var EXPLODEFADESPEED = 1.0 / EXPLODETICKS; var EXPLODEFADESPEED = 1.0 / EXPLODETICKS;
var SHRINKTICKS=20;
var SHRINKLOSE= (THINGSIZE / SHRINKTICKS);
var LINEWIDTH=2; var LINEWIDTH=2;
var CROSSWIDTH=2; var CROSSWIDTH=2;
@ -142,7 +151,7 @@ var FOODPOINTS = 10;
var LLAMAPOINTS = 100; var LLAMAPOINTS = 100;
var GOATPOINTS = 50; var GOATPOINTS = 50;
var CATPOINTS = 20; var CATPOINTS = 20;
var SLEEPYCATPOINTS = 30; var SLEEPYCATPOINTS = 40;
var overdesc = ""; var overdesc = "";
@ -203,7 +212,7 @@ function pathcomplete() {
case "chomp": case "chomp":
if ((curpath.length >= 2) && if ((curpath.length >= 2) &&
(curpath[0].type == "cat") && (curpath[0].type == "cat") &&
(curpath[0].eaten == false)) { !curpath[0].issleepy()) {
var i; var i;
var ok = true; var ok = true;
// everything else is food? // everything else is food?
@ -292,12 +301,26 @@ function pathcontains(what) {
return false; 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) { function isvalidpath(mypath) {
var valid = true; var valid = true;
var firstcat = false; var firstcat = false;
var fcount = 0; var fcount = 0;
var lcount = 0; var lcount = 0;
var gcount = 0; var gcount = 0;
var count = 0;
var i; var i;
for (i = 0; i < mypath.length - 1; i++) { for (i = 0; i < mypath.length - 1; i++) {
@ -321,6 +344,7 @@ function isvalidpath(mypath) {
if (thisone.type == "goat") { if (thisone.type == "goat") {
gcount++; gcount++;
} }
count++;
if ((thisone.type == "cat") && (nextone.type == "cat")) { if ((thisone.type == "cat") && (nextone.type == "cat")) {
// no parades on level 1 // 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")) { } else if ((thisone.type == "goat") && (nextone.type == "cat" || nextone.type == "llama" || nextone.type == "goat")) {
// goat can go to llama or cat // goat can go to llama or cat
// ok // 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") { } else if ((thisone.type == "cat") && nextone.type == "llama") {
// no parades on level 1 // no parades on level 1
if (curlevel == 1) { if (curlevel == 1) {
@ -522,7 +549,7 @@ function thingsfalling() {
function thingsmoving() { function thingsmoving() {
var i; var i;
for (i = 0; i < things.length; i += 1) { for (i = 0; i < things.length; i += 1) {
if (things[i].state == "parade") { if ((things[i].state == "parade") || (things[i].state == "shrink")) {
return "parade"; return "parade";
} }
} }
@ -878,6 +905,16 @@ var game = {
return true; 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() { resize : function() {
this.curh = window.innerHeight; this.curh = window.innerHeight;
this.curw = this.curh * this.ratio; this.curw = this.curh * this.ratio;
@ -901,6 +938,7 @@ var game = {
initgamevars : function() { initgamevars : function() {
score = 0; score = 0;
multiplier = 1;
this.frameNo = 0; this.frameNo = 0;
}, },
@ -909,9 +947,7 @@ var game = {
clearthings(); clearthings();
overdesc = ""; overdesc = "";
curpath = []; clearpath();
pathdir = -1;
pathvalid = false;
}, },
addlevel : function (lev, hashelp) { addlevel : function (lev, hashelp) {
@ -1028,7 +1064,7 @@ var game = {
calcstarpoints : function( lev ) { calcstarpoints : function( lev ) {
var i; var i;
var min = 0,gotpointsgoal = false; var min = 0,pointsgoal = false;
// calculate minimum points required to win // calculate minimum points required to win
// first pass // first pass
for (i = 0; i < this.levels[lev].goals.length; i++) { for (i = 0; i < this.levels[lev].goals.length; i++) {
@ -1046,31 +1082,31 @@ var game = {
case "goats": case "goats":
min += GOATPOINTS * this.levels[lev].goals[i].count; min += GOATPOINTS * this.levels[lev].goals[i].count;
break; 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 // second pass - overrides
for (i = 0; i < this.levels[lev].goals.length; i++) { for (i = 0; i < this.levels[lev].goals.length; i++) {
switch (this.levels[lev].goals[i].type) { switch (this.levels[lev].goals[i].type) {
case "points": case "points":
min = this.levels[lev].goals[i].count; pointsgoal = this.levels[lev].goals[i].count;
gotpointsgoal = true;
break; break;
} }
} }
this.levels[lev].starpoints = new Array(); this.levels[lev].starpoints = new Array();
if (gotpointsgoal) { this.levels[lev].starpoints[0] = Math.floor(Math.max(min, pointsgoal));
// if the level ends as soon as you get a certain # of points, this.levels[lev].starpoints[1] = Math.floor(Math.max(min * 2, pointsgoal*1.5));
// your only hope of boosting higher is to get LOTS of points this.levels[lev].starpoints[2] = Math.floor(Math.max(min * 3, pointsgoal*2));
// 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);
}
}, },
initlevels : function ( ) { initlevels : function ( ) {
@ -1081,49 +1117,62 @@ var game = {
this.addlevel(1, true); this.addlevel(1, true);
this.addlevelgoals(1, "food", 5); this.addlevelgoals(1, "food", 5);
this.setstarpoints(1, 50, 60, 80); 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.addlevel(2, true);
this.addlevelgoals(2, "cats", 10); this.addlevelgoals(2, "cats", 10);
this.addlevelgoals(2, "parades", 3); this.addlevelgoals(2, "parades", 3);
this.addlevelthings(2, "cat", 45, "food", 100); this.addlevelthings(2, "cat", 45, "food", 55);
this.addlevel(3, true); 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.addlevelgoals(3, "llamas", 3);
this.addlevel(4, true); this.addlevel(4, true);
this.addlevelgoals(4, "llamas", 5); this.addlevelgoals(4, "llamas", 5);
this.addlevelgoals(4, "food", 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.addlevel(5, false);
this.addlevelgoals(5, "points", 1000); this.addlevelgoals(5, "turns", 10);
this.addlevelthings(5, "cat", 45, "food", 90, "llama", 100); this.addlevelgoals(5, "points", 600);
this.addlevelallowedthings(5, "cat", "food", "llama");
// introduce goats! // introduce goats!
this.addlevel(6, true); this.addlevel(6, true);
this.addlevelgoals(6, "goats", 3); 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.addlevelforcethings(6, "goat", 1);
this.addlevel(7, false); this.addlevel(7, false);
this.addlevelallowedthings(7, "goat", "cat", "food", "llama"); 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, "llamas", 2);
this.addlevelgoals(7, "goats", 2); this.addlevelgoals(7, "goats", 2);
this.addlevel(8, false); this.addlevel(8, false);
this.addlevelallowedthings(8,"goat", "cat", "food", "llama"); this.addlevelallowedthings(8,"goat", "cat", "food", "llama");
this.addlevelgoals(8, "points", 1100); this.addlevelgoals(8, "turns", 20);
this.addlevelgoals(8, "llamas", 3); this.addlevelgoals(8, "llamas", 3);
// introduce doors // introduce doors
/* this.addlevel(9, true);
this.addlevel(8, false); this.addlevelallowedthings(9,"goat", "cat", "food", "llama", "door");
this.addlevelgoals(8, "doors", 1); this.addlevelgoals(9, "doors", 1);
this.addlevelforcethings(8, "door", 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++) { for (i = 1; i < this.levels.length; i++) {
var extrastars; var extrastars;
@ -1175,14 +1224,14 @@ var game = {
} }
// force items // force items
if (game.levels[curlevel].forcethings != undefined) { if (game.levels[curlevel].forcelist != undefined) {
for (i = 0; i < game.levels[curlevel].forcethings.length; i++) { for (i = 0; i < game.levels[curlevel].forcelist.length; i++) {
var wanttype,thisnum; var wanttype,wantnum;
wanttype = game.levels[curlevel].forcethings[i].type; wanttype = game.levels[curlevel].forcelist[i].type;
wantnum = game.levels[curlevel].forcethings[i].howmany; wantnum = game.levels[curlevel].forcelist[i].howmany;
// while we don't have enough... // while we don't have enough...
while (countthingsoftype(wanttype) < num) { while (countthingsoftype(wanttype) < wantnum) {
// add one. // add one.
var idx; var idx;
idx = rnd(things.length); idx = rnd(things.length);
@ -1451,7 +1500,7 @@ var game = {
} else { } else {
var zoomw,zoomh; var zoomw,zoomh;
// draw winning cat image // 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; zoomw = tapw * game.winimgsize;
zoomh = taph * game.winimgsize; zoomh = taph * game.winimgsize;
@ -2314,6 +2363,246 @@ var game = {
y += gridsize; y += gridsize;
cury = y; 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") { } else if (game.state == "gameover") {
if ((realx >= tapx) && (realx <= tapx + tapw) && if ((realx >= tapx) && (realx <= tapx + tapw) &&
(realy >= tapy) && (realy <= tapy + taph)) { (realy >= tapy) && (realy <= tapy + taph)) {
game.clearlevelprogress(curlevel);
game.setstate("levselect"); game.setstate("levselect");
} }
} else if (game.state == "title") { } else if (game.state == "title") {
@ -2838,15 +3128,30 @@ var game = {
// first one chomps last one // first one chomps last one
curpath[0].chomp(curpath[curpath.length - 1]); curpath[0].chomp(curpath[curpath.length - 1]);
game.progress("turns", 1);
break; break;
case "parade": case "parade":
multiplier = 1;
if (curpath.length >= 2) { // should always be true if (curpath.length >= 2) { // should always be true
var i; var i;
// everything in the path exits via a parade // everything in the path exits via a parade
pathdoor = pathcontainstype("door");
if (pathdoor) multiplier++;
for (i = 0; i < curpath.length; i++) { for (i = 0; i < curpath.length; i++) {
// ... 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].givepoints();
curpath[i].startparade(); curpath[i].startparade();
} }
}
clearpath(); clearpath();
} else { } else {
// just kill everything in path // just kill everything in path
@ -2857,11 +3162,16 @@ var game = {
curpath.splice(0, 1); curpath.splice(0, 1);
} }
} }
multiplier = 1;
game.progress("parades", 1); game.progress("parades", 1);
game.progress("turns", 1);
break; break;
} }
clearpath(); clearpath();
// things that happen once per turn.
game.turneffects();
}, },
} }
@ -2932,6 +3242,7 @@ function shadowtext(ctx, text, size, col, x, y) {
function getrandomtype() { function getrandomtype() {
var roll,tot,type = null,i; var roll,tot,type = null,i;
var thinglist,maxroll = 0; var thinglist,maxroll = 0;
var speciallist = null;
if (curlevel >= game.levels.length) { if (curlevel >= game.levels.length) {
thinglist = null; thinglist = null;
@ -2939,20 +3250,39 @@ function getrandomtype() {
thinglist = game.levels[curlevel].thinglist; 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) { if (thinglist == undefined || thinglist.length == 0) {
var f; var f;
thinglist = new Array(); thinglist = new Array();
// default thinglist // default thinglist
// must be sorted from low to high! // must be sorted from low to high!
if (!game.isbanned(curlevel, 'goat')) thinglist.push({ type: 'goat', pct: 5 } ); if (!game.isbanned(curlevel, 'door')) thinglist.push({ type: 'door', pct: 10 } );
if (!game.isbanned(curlevel, 'llama')) thinglist.push({ type: 'llama', pct: 10 } ); if (speciallist != undefined) {
if (!game.isbanned(curlevel, 'cat')) thinglist.push({ type: 'cat', pct: 40 } ); console.log("specials are possible");
if (!game.isbanned(curlevel, 'food')) thinglist.push({ type: 'food', pct: 45 } ); 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++) { for (i = 0; i < thinglist.length; i++) {
maxroll += thinglist[i].pct; maxroll += thinglist[i].pct;
//console.log(thinglist[i].type + ": " + thinglist[i].pct + " [" + maxroll + "]");
} }
roll = rnd(maxroll); roll = rnd(maxroll);
@ -2960,14 +3290,19 @@ function getrandomtype() {
for (i = 0; i < thinglist.length; i++) { for (i = 0; i < thinglist.length; i++) {
tot += thinglist[i].pct; tot += thinglist[i].pct;
if (roll <= tot) { if (roll <= tot) {
console.log("rolled " + roll + "/" + maxroll + " --> " + thinglist[i].type + " [<= " + tot + "]");
type = thinglist[i].type; type = thinglist[i].type;
break; break;
} }
} }
if (type == null) { if (type == null || type == undefined) {
console.log("couldn't find type! roll is " + roll); console.log("couldn't find type! roll is " + roll);
type = 'cat'; type = 'cat';
} else if (type == "special") {
// pick from list of specials
type = speciallist[rnd(speciallist.length)];
} }
return type; return type;
} }
@ -2979,9 +3314,28 @@ function coord(x,y) {
function thing(gridx, gridy, type, text) { function thing(gridx, gridy, type, text) {
this.opacity = 1.0; this.opacity = 1.0;
this.isnew = true;
if (type == "random") { if (type == "random") {
type = getrandomtype(); 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; this.type = type;
@ -2996,7 +3350,11 @@ function thing(gridx, gridy, type, text) {
this.color = "#cccccc"; this.color = "#cccccc";
break; break;
case "text": case "text":
if (this.name.indexOf("x") == 0) {
this.color = "#00dddd";
} else {
this.color = "#00cc00"; this.color = "#00cc00";
}
break; break;
default: // should never happen default: // should never happen
this.color = getrandomcolour(); this.color = getrandomcolour();
@ -3004,17 +3362,16 @@ function thing(gridx, gridy, type, text) {
} }
if (this.type == "text") { if (this.type == "text") {
if (this.name.indexOf("x") == 0) {
this.size = TEXTSIZEMULTIPLIER;
} else {
this.size = TEXTSIZE; this.size = TEXTSIZE;
}
} else { } else {
this.size = THINGSIZE; this.size = THINGSIZE;
} }
this.lifetime = 0; this.lifetime = 0;
if (text == undefined) {
this.name = type + "-" + getrandomname();
} else {
this.name = text;
}
this.gridx = gridx; this.gridx = gridx;
if (gridy == "top") { if (gridy == "top") {
@ -3040,9 +3397,9 @@ function thing(gridx, gridy, type, text) {
this.path = []; this.path = [];
this.pathspeed = PARADESPEED; this.pathspeed = PARADESPEED;
this.expcount = 0; this.expcount = 0;
this.expmax = 0; this.expmax = 0;
this.eaten = false;
if (this.type == "text" ) { if (this.type == "text" ) {
this.x = gridx * GRIDSIZE + (GRIDSIZE/2); 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.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.pushpath = function(x,y) {
this.path.push(new coord(x, y)); this.path.push(new coord(x, y));
@ -3077,7 +3454,7 @@ function thing(gridx, gridy, type, text) {
} }
// chomp // chomp
if (isadjacenttotype(this, "food") && !this.eaten) { if (isadjacenttotype(this, "food") && !this.issleepy()) {
return true; return true;
} }
@ -3122,7 +3499,7 @@ function thing(gridx, gridy, type, text) {
points = LLAMAPOINTS; points = LLAMAPOINTS;
game.progress("llamas", 1); game.progress("llamas", 1);
} else if (this.type == "cat") { } else if (this.type == "cat") {
if (this.eaten == true) { if (this.issleepy()) {
points = SLEEPYCATPOINTS; points = SLEEPYCATPOINTS;
} else { } else {
points = CATPOINTS; points = CATPOINTS;
@ -3132,24 +3509,29 @@ function thing(gridx, gridy, type, text) {
points = GOATPOINTS; points = GOATPOINTS;
game.progress("goats", 1); game.progress("goats", 1);
} }
points *= multiplier;
if (points > 0) {
score += points; score += points;
game.progress("points", points); game.progress("points", points);
// add animation // add animation
things.push(new thing(this.gridx, this.gridy, "text", "+" + points)); things.push(new thing(this.gridx, this.gridy, "text", "+" + points));
} }
}
this.getdesc = function() { this.getdesc = function() {
var desc = ""; var desc = "";
if (this.type == "cat") { if (this.type == "cat") {
if (isadjacenttotype(this, "llama")) { if (isadjacenttotype(this, "llama")) {
if (this.eaten == true) { if (this.issleepy() == true) {
desc = "scared sleepy cat"; desc = "scared sleepy cat";
} else { } else {
desc = "scared cat"; desc = "scared cat";
} }
} else if (this.eaten == true) { } else if (this.issleepy() == true) {
desc = "sleepy cat"; desc = "sleepy cat";
} else { } else {
desc = "cat"; desc = "cat";
@ -3187,17 +3569,14 @@ function thing(gridx, gridy, type, text) {
if (this.type == "text") { if (this.type == "text") {
ctx.textAlign = "center"; ctx.textAlign = "center";
ctx.textBaseline = "middle"; 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 { } else {
if (this.type == "cat") { if (this.type == "cat") {
var myimage; var myimage;
if (this.state == "exploding") {
}
if (isadjacenttotype(this, "llama")) { if (isadjacenttotype(this, "llama")) {
myimage = image['catscared']; myimage = image['catscared'];
} else if (this.eaten == true) { } else if (this.issleepy() == true) {
myimage = image['catfull']; myimage = image['catfull'];
} else { } else {
myimage = image['cat']; myimage = image['cat'];
@ -3208,6 +3587,13 @@ function thing(gridx, gridy, type, text) {
myimage = image['cheese']; myimage = image['cheese'];
} else if (this.type == "goat") { } else if (this.type == "goat") {
myimage = image['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; howbig = this.size;
myx = this.x; myx = this.x;
@ -3365,6 +3751,7 @@ function thing(gridx, gridy, type, text) {
this.isanimating = function() { this.isanimating = function() {
if (this.type == "text") return true; if (this.type == "text") return true;
if (this.state == "explode") return true; if (this.state == "explode") return true;
if (this.state == "shrink") return true;
if (this.state == "parade") return true; if (this.state == "parade") return true;
return false; return false;
} }
@ -3375,6 +3762,12 @@ function thing(gridx, gridy, type, text) {
this.state = "explode"; this.state = "explode";
} }
this.startshrink = function() {
this.expcount=1;
this.expmax=SHRINKTICKS;
this.state = "shrink";
}
this.startparade = function() { this.startparade = function() {
var i,startidx=-1; var i,startidx=-1;
// find our position in the current path // find our position in the current path
@ -3396,6 +3789,10 @@ function thing(gridx, gridy, type, text) {
nextidx = i+1; nextidx = i+1;
if (nextidx >= curpath.length) { if (nextidx >= curpath.length) {
var dir; var dir;
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. // we're at the end. move off the screen in the direction of the path.
// find dir from previous to us... // find dir from previous to us...
@ -3404,6 +3801,7 @@ function thing(gridx, gridy, type, text) {
dir = getdir(curpath[i-1],curpath[i]); dir = getdir(curpath[i-1],curpath[i]);
this.pushpath(curpath[i].x + (DIRXMOD[dir]*GRIDSIZE*GRIDW), this.pushpath(curpath[i].x + (DIRXMOD[dir]*GRIDSIZE*GRIDW),
curpath[i].y + (DIRYMOD[dir]*GRIDSIZE*GRIDH)); curpath[i].y + (DIRYMOD[dir]*GRIDSIZE*GRIDH));
}
} else { } else {
this.pushpath(curpath[nextidx].x, curpath[nextidx].y); this.pushpath(curpath[nextidx].x, curpath[nextidx].y);
} }
@ -3453,7 +3851,22 @@ function thing(gridx, gridy, type, text) {
this.y = (this.gridy * GRIDSIZE) + (GRIDSIZE/2) - (this.size/2); 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 // move towards next cell in path
var nextx = this.path[0].x; var nextx = this.path[0].x;
var nexty = this.path[0].y; var nexty = this.path[0].y;
@ -3461,6 +3874,11 @@ function thing(gridx, gridy, type, text) {
game.dirty = true; // need to redraw game.dirty = true; // need to redraw
// special case: door.
if ((nextx == -1) && (nexty == -1)) {
this.path = [];
this.startshrink();
} else {
if (this.x < nextx) { if (this.x < nextx) {
this.x += this.pathspeed; this.x += this.pathspeed;
if (this.x > nextx) xdone = true; if (this.x > nextx) xdone = true;
@ -3488,7 +3906,12 @@ function thing(gridx, gridy, type, text) {
this.poppath(); this.poppath();
// path finished? // path finished?
if (this.path == undefined || this.path.length == 0) { if (this.path == undefined || this.path.length == 0) {
if (this.state == "catparade" ){
this.kill(); this.kill();
} else {
this.state = "stop";
}
}
} }
} }
} else { } else {
@ -3496,7 +3919,7 @@ function thing(gridx, gridy, type, text) {
if ((this.gridy >= GRIDH-1)) { if ((this.gridy >= GRIDH-1)) {
atbottom = true; atbottom = true;
} }
if (!atbottom && !this.getstoppedbelowthing()) { if (!atbottom && !this.getstoppedbelowthing() && this.canfall()) {
game.dirty = true; // need to redraw game.dirty = true; // need to redraw
// accelerate // accelerate
this.yspeed += GRAVITY; this.yspeed += GRAVITY;
@ -3603,7 +4026,14 @@ function mainloop() {
// check for game over and level over // check for game over and level over
if (game.state == "running") { if (game.state == "running") {
if (!thingsmoving()) { 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 // record score
if (score > playerdata.levscore[curlevel]) { if (score > playerdata.levscore[curlevel]) {
playerdata.setlevscore(curlevel, score); playerdata.setlevscore(curlevel, score);
@ -3613,6 +4043,36 @@ function mainloop() {
} else if (!anyvalidmoves()) { } else if (!anyvalidmoves()) {
game.setstate("gameover"); 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";
}
}
}
}
} }
} }
} }

64
todo
View File

@ -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/ https://www.smashingmagazine.com/2012/10/design-your-own-mobile-game/
*restrict catparades on level 1 *make forcethings actually work
*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
*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 still bugs in getarndomtype() ?
* cats and goats can gang up on on llamas.
* parades with a goat can have multiple llamas bug with 2 suns above each other
* goats can't start parades (only cats)
* 50 points special things - 5% chance of any
*goat help text. {
*level select screen needs "back to title"
*mechanism to force at least one of a given thing type on a level. white cat -
*better level complete animation - zoom in random cat picture. move cat on to it to FIGHT.
*only redraw screen if dirty! destroy 9 in a block around.
*switch to "star" system for unlocking levels pow pow cloud.
*fix bug when calculating whether there are any valid moves left
*fix bug in random thing selection
doona - hide under it in the cold ?
mouse toy - match 3 to explode everything ?
}
more goals: more goals:
form x parades of length y 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 path part of game object
make things part of game object make things part of game object