frontend/main/mario/utility.js
2023-05-21 21:16:27 -04:00

852 lines
25 KiB
JavaScript

/* Utility.js */
// Contains all needed helper functions not in toned.js
/* General stuff */
// Recursively goes down sub-objects of obj
// Path is ["path", "to", "target"], where num is how far along the path it is
function followPath(obj, path, num) {
if(path[num] != null && obj[path[num]] != null)
return followPath(obj[path[num]], path, ++num);
return obj;
}
// Expensive - use only on clearing
function clearAllTimeouts() {
var id = setTimeout(function() {});
while(id--) clearTimeout(id);
}
// Width and height are given as number of pixels (to scale; unitsize)
function getCanvas(width, height, stylemult) {
var canv = createElement("canvas", {
width: width,
height: height
});
// If necessary, make this thing's visual style
if(stylemult) {
// stylemult is 1 by default, but may be something else (typically unitsize)
stylemult = stylemult || unitsize;
proliferate(canv.style, {
width: (width * stylemult) + "px",
height: (height * stylemult) + "px"
});
}
// For speed
canv.getContext("2d").webkitImageSmoothingEnabled = false
return canv;
}
function step(num) {
unpause();
upkeep();
pause();
if(num > 0) step(num - 1);
}
function fastforward(num) {
pause();
function resume() {
for(i = speed = (num || 1) - 1; i > 0; --i)
step();
unpause();
}
requestAnimationFrame(resume);
}
function specifyTimer(timerin) {
// Only use if you're not worried about losing the benefits of requestAnimationFrame
// Also, this kills performance. Works best with smaller windows!
timer = timerin;
requestAnimationFrame = function(func) {
window.setTimeout(func, timer);
};
}
function changeUnitsize(num) {
if(!num) return;
resetUnitsize(num);
function setter(arr) {
for(i in arr) {
updateSize(arr[i]);
updatePosition(arr[i]);
}
}
setter(solids);
setter(characters);
}
// num = 1 by default
// 1 = floor(0->2) = 50% chance
// 2 = floor(0->3) = 67% chance
// 3 = floor(0->4) = 75% chance
function randTrue(num) {
return floor(getSeed() * ((num || 1) + 1));
// return floor(random() * ((num || 1) + 1));
}
function randSign(num) {
return randTrue(num) * 2 - 1;
}
function randBoolJS(num) {
return floor(random() * 2);
}
/*
* Basic object positioning helper functions
*/
function updatePosition(me) {
// if(!me.nomove) shiftHoriz(me, me.xvel * realtime);
// if(!me.nofall) shiftVert(me, me.yvel * realtime);
if(!me.nomove) shiftHoriz(me, me.xvel);
if(!me.nofall) shiftVert(me, me.yvel);
}
function updateSize(me) {
me.unitwidth = me.width * unitsize;
me.unitheight = me.height * unitsize;
me.spritewidthpixels = me.spritewidth * unitsize;
me.spriteheightpixels = me.spriteheight * unitsize;
var canvas;
if(canvas = me.canvas) {
canvas.width = me.spritewidthpixels;
canvas.height = me.spriteheightpixels;
// me.context = canvas.getContext("2d");
refillThingCanvas(me);
}
}
function reduceHeight(me, dy, see) {
me.top += dy;
me.height -= dy / unitsize;
if(see) {
updateSize(me);
}
}
function shiftBoth(me, dx, dy) {
if(!me.noshiftx) shiftHoriz(me, dx);
if(!me.noshifty) shiftVert(me, dy);
}
function shiftHoriz(me, dx) {
me.left += dx;
me.right += dx;
}
function shiftVert(me, dy) {
me.top += dy;
me.bottom += dy;
}
function setLeft(me, left) {
me.left = left;
me.right = me.left + me.width * unitsize;
}
function setRight(me, right) {
me.right = right;
me.left = me.right - me.width * unitsize;
}
function setTop(me, top) {
me.top = top;
me.bottom = me.top + me.height * unitsize;
}
function setBottom(me, bottom) {
me.bottom = bottom;
me.top = me.bottom - me.height * unitsize;
}
function setWidth(me, width, spriter, updater) {
me.width = width;
me.unitwidth = width * unitsize;
if(spriter) {
me.spritewidth = width;
me.spritewidthpixels = width * unitsize;
}
if(updater) {
updateSize(me);
setThingSprite(me);
}
}
function setHeight(me, height, spriter, updater) {
me.height = height;
me.unitheight = height * unitsize;
if(spriter) {
me.spriteheight = height;
me.spriteheightpixels = height * unitsize;
}
if(updater) {
updateSize(me);
setThingSprite(me);
}
}
function setSize(me, width, height, spriter, updater) {
if(width) setWidth(me, width, spriter);
if(height) setHeight(me, height, spriter);
if(updater) {
updateSize(me);
setThingSprite(me);
}
}
function setMidX(me, left, see) {
setLeft(me, left + me.width * unitsized2, see);
}
function getMidX(me) {
return me.left + me.width * unitsized2;
}
function setMidY(me, top, see) {
setTop(me, top + me.height * unitsized2, see);
}
function setMidXObj(me, object, see) {
setLeft(me, (object.left + object.width * unitsized2) - (me.width * unitsized2), see);
}
function slideToXLoc(me, xloc, maxspeed, see) {
maxspeed = maxspeed || Infinity;
var midx = getMidX(me);
if(midx < xloc) {
// Me is the left
shiftHoriz(me, min(maxspeed, (xloc - midx)), see);
} else {
// Me is the right
shiftHoriz(me, max(-maxspeed, (xloc - midx)), see);
}
}
function updateLeft(me, dx) {
me.left += dx;
me.right = me.left + me.width * unitsize;
}
function updateRight(me, dx) {
me.right += dx;
me.left = me.right - me.width * unitsize;
}
function updateTop(me, dy) {
me.top += dy;
me.bottom = me.top + me.height * unitsize;
}
function updateBottom(me, dy) {
me.bottom += dy;
me.top = me.bottom - me.height * unitsize;
}
// Increases the height, keeping the bottom the same
// dy comes in as factored for unitsize... e.g. increaseHeightTop(me, unitsized4)
function increaseHeightTop(me, dy, spriter) {
me.top -= dy;
me.height += dy / unitsize;
me.unitheight = me.height * unitsize;
}
/*
* Collisions
*/
function determineThingCollisions(me) {
if(me.nocollide) return;
else if(!me.resting || me.resting.yvel == 0) me.resting = false;
// Cur is each quadrant this object is in, and other is each other object in them.
var cur, others, other, contents,
i, j, leni, lenj;
// Unless directed not to, make sure this doesn't overlap anything
// Overlaps are actually added a few lines down, under collisions for solids
if(!me.skipoverlaps) checkOverlap(me);
// For each quadrant the thing is in:
for(i = 0, leni = me.numquads; i < leni; ++i) {
cur = me.quads[i];
others = cur.things;
// For each other thing in that quadrant:
for(j = 0, lenj = cur.numthings; j < lenj; ++j) {
other = others[j];
if(me == other) break; // breaking prevents double collisions
if(!other.alive || other.scenery || other.nocollide) continue; // not removed in upkeep
// The .hidden check is required. Try the beginning of 2-1 without it.
// visual_scenery is also necessary because of Pirhanas (nothing else uses that)
if(objectsTouch(me, other) && (me.mario || (!other.hidden || !(other.visual_scenery && other.visual_scenery.hidden)) || solidOnCharacter(other, me))) {
// Collisions for characters are simple
if(other.character)
// if(charactersTouch(me, other))
objectsCollided(me, other);
// Collisions for solids, slightly less so (overlaps)
else if(!me.nocollidesolid) {
objectsCollided(me, other);
if(!me.skipoverlaps && !other.skipoverlaps && characterOverlapsSolid(me, other))
me.overlaps.push(other);
}
}
}
}
if(me.undermid) me.undermid.bottomBump(me.undermid, me);
else if(me.under instanceof Thing) me.under.bottomBump(me.under, me);
}
// give solid a tag for overlap
// remove tag when overlaps = []
function checkOverlap(me) {
if(me.overlapdir) {
if((me.overlapdir < 0 && me.right <= me.ocheck.left + unitsizet2)
|| me.left >= me.ocheck.right - unitsizet2) {
me.overlapdir = 0;
me.overlaps = [];
}
else shiftHoriz(me, me.overlapdir, true);
}
else if(me.overlaps.length > 0) {
// mid = me.omid is the midpoint of what is being overlapped
var overlaps = me.overlaps,
right = {right: -Infinity},
left = {left: Infinity},
mid = 0, over,
i;
me.overlapfix = true;
for(i in overlaps) {
over = overlaps[i];
mid += getMidX(over);
if(over.right > right.right) right = over;
if(over.left < left.left) left = over;
}
mid /= overlaps.length;
if(getMidX(me) >= mid - unitsized16) {
// To the right of the middle: travel until past right
me.overlapdir = unitsize;
me.ocheck = right;
} else {
// To the left of the middle: travel until past left
me.overlapdir = -unitsize;
me.ocheck = left;
}
}
}
function characterOverlapsSolid(me, solid) {
return me.top <= solid.top && me.bottom > solid.bottom;
}
// Purposefully only looks at toly; horizontal uses 1 unitsize
function objectsTouch(one, two) {
if(one.right - unitsize > two.left && one.left + unitsize < two.right)
if(one.bottom >= two.top && one.top <= two.bottom)
return true;
return false;
}
// Used to double-check objectsTouch
function charactersTouch(one, two) {
if(one.bottom <= two.top + unitsizet2 || one.top + unitsizet2 >= two.bottom) return false;
return true;
}
// No tolerance! Just unitsize.
function objectInQuadrant(one, quad) {
if(one.right + unitsize >= quad.left && one.left - unitsize <= quad.right)
if(one.bottom + unitsize >= quad.top && one.top - unitsize <= quad.bottom)
return true;
return false;
}
function objectsCollided(one, two) {
// Assume that if there's a solid, it's two. (solids don't collide with each other)
if(one.solid) {
if(!two.solid) return objectsCollided(two, one);
}
// Up solids are special
if(two.up && one != two.up) return characterTouchesUp(one, two);
// Otherwise, regular collisions
if(two.solid || one.mario)
two.collide(one, two);
else one.collide(two, one);
}
// Sees whether one's midpoint is to the left of two's
function objectToLeft(one, two) {
return (one.left + one.right) / 2 < (two.left + two.right) / 2;
}
/*
TO DO: Revamp these
*/
function objectOnTop(one, two) {
if(one.type == "solid" && two.yvel > 0) return false;
if(one.yvel < two.yvel && two.type != "solid") return false;
if(one.mario && one.bottom < two.bottom && two.group == "enemy") return true;
return( (one.left + unitsize < two.right && one.right - unitsize > two.left) &&
(one.bottom - two.yvel <= two.top + two.toly || one.bottom <= two.top + two.toly + abs(one.yvel - two.yvel)));
}
// Like objectOnTop, but more specifically used for characterOnSolid and characterOnResting
function objectOnSolid(one, two) {
return(
( one.left + unitsize < two.right &&
one.right - unitsize > two.left )
&&
( one.bottom - one.yvel <= two.top + two.toly ||
one.bottom <= two.top + two.toly + abs(one.yvel - two.yvel) )
);
}
function solidOnCharacter(solid, me) {
if(me.yvel >= 0) return false;
me.midx = getMidX(me);
return me.midx > solid.left && me.midx < solid.right &&
(solid.bottom - solid.yvel <= me.top + me.toly - me.yvel);
}
// This would make the smart koopas stay on the edges more intelligently
// Can't use objectOnTop for this, else Mario will walk on walls.
function characterOnSolid(me, solid) {
return (me.resting == solid || (objectOnSolid(me, solid) && me.yvel >= 0 &&
me.left + me.xvel + unitsize != solid.right && me.right - me.xvel - unitsize != solid.left));
// me.left - me.xvel + unitsize != solid.right && me.right + me.xvel - unitsize != solid.left));
// me.left - me.xvel + unitsize != solid.right && me.right - me.xvel - unitsize != solid.left));
}
function characterOnResting(me, solid) {
return objectOnSolid(me, solid) &&
// me.left - me.xvel + unitsize != solid.right && me.right - me.xvel - unitsize != solid.left;
me.left + me.xvel + unitsize != solid.right && me.right - me.xvel - unitsize != solid.left;
}
function characterTouchedSolid(me, solid) {
if(solid.up == me) return;
// Me on top of the solid
if(characterOnSolid(me, solid)) {
if(solid.hidden) return;
me.resting = solid;
// Meh.
if(me.mario && map.underwater) removeClass(me, "paddling");
}
// Solid on top of me
else if(solidOnCharacter(solid, me)) {
var mid = me.left + me.width * unitsize / 2;
if(mid > solid.left && mid < solid.right) me.undermid = solid;
else if(solid.hidden) return;
if(!me.under) me.under = [solid];
else me.under.push(solid);
// To do: make this not so obviously hardcoded
if(me.mario) {
setTop(me, solid.bottom - me.toly + solid.yvel, true);
}
me.yvel = solid.yvel;
if(me.mario) me.keys.jump = 0;
}
if(solid.hidden) return;
// Character bumping into the side
//// .midx is given by solidOnCharacter
if(!characterNotBumping(me, solid) && !objectOnTop(me, solid) && !objectOnTop(solid, me) && !me.under && me != solid.up) {
if(me.right <= solid.right) { // To left of solid
me.xvel = min(me.xvel, 0);
shiftHoriz(me, max(solid.left + unitsize - me.right, -unitsized2), true);
} else if(me.left >= solid.left) { // To right of solid
me.xvel = max(me.xvel, 0);
shiftHoriz(me, min(solid.right - unitsize - me.left, unitsized2), true);
}
// Non-Marios are instructed to flip
if(!me.mario) {
me.moveleft = !me.moveleft;
if(me.group == "item") me.collide(solid, me);
}
// Mario uses solid.actionLeft (e.g. Pipe -> intoPipeHoriz)
else if(solid.actionLeft)
solid.actionLeft(me, solid, solid.transport);
}
}
// Really just for koopas
function characterNotBumping(me, solid) {
if(me.top + me.toly + abs(me.yvel) > solid.bottom) return true;
return false;
}
function characterTouchesUp(me, solid) {
switch(me.group) {
case "item":
me.moveleft = getMidX(me) <= getMidX(solid) + unitsized2;
characterHops(me);
break;
case "coin":
me.animate(me);
break;
default:
me.death(me, 2);
scoreEnemyBelow(me);
break;
}
}
function characterHops(me) {
me.yvel = -1.4 * unitsize;
me.resting = false;
}
function characterIsAlive(me) {
return !(!me || me.dead || !me.alive);
}
/*
* Scoring on enemies
*/
function scoreMarioShell(mario, shell) {
// Star Mario gets 200
if(mario.star) return score(shell, 200, true);
// Shells in the air cause 8000 points, oh lawdy
if(!shell.resting) return score(shell, 8000, true);
// Peeking shells are also more
if(shell.peeking) return score(shell, 1000, true);
// Regular points are just 100
return score(shell, 100, true);
}
function scoreEnemyStomp(enemy) {
var amount = 100;
switch(enemy.type.split(" ")[0]) {
case "koopa": amount = enemy.fly ? 400 : 100; break;
case "bulletbill": amount = 200; break;
case "cheepcheep": amount = 200; break;
case "hammerbro": amount = 1000; break;
case "lakitu": amount = 800; break;
default: amount = 100; break;
}
// scoreEnemyFin(enemy, amount);
}
function scoreEnemyFire(enemy) {
var amount = 200;
switch(enemy.type.split(" ")[0]) {
case "goomba": amount = 100; break;
case "hammerbro": amount = 1000; break;
case "bowser": amount = 5000; break;
default: amount = 200; break;
}
scoreEnemyFin(enemy, amount);
}
function scoreEnemyStar(enemy) {
var amount = 200;
switch(enemy.type.split(" ")[0]) {
case "goomba": amount = 100; break;
case "hammerbro": amount = 1000; break;
default: amount = 200; break;
}
scoreEnemyFin(enemy, amount);
play("Kick");
}
function scoreEnemyBelow(enemy) {
var amount = 100;
switch(enemy.type.split(" ")[0]) {
case "hammerbro": amount = 1000; break;
default: amount = 100; break;
}
scoreEnemyFin(enemy, amount);
}
function scoreEnemyFin(enemy, amount) {
score(enemy, amount, true);
}
/*
* General actions
*/
function moveSimple(me) {
if(me.direction != me.moveleft) {
if(me.moveleft) {
me.xvel = -me.speed;
if(!me.noflip) unflipHoriz(me);
} else {
if(!me.noflip) flipHoriz(me);
me.xvel = me.speed;
}
me.direction = me.moveleft;
}
}
function moveSmart(me) {
moveSimple(me);
if(me.yvel == 0 && (!me.resting || (offResting(me)))) {
if(me.moveleft) shiftHoriz(me, unitsize, true);
else shiftHoriz(me, -unitsize, true);
me.moveleft = !me.moveleft;
}
}
function offResting(me) {
if(me.moveleft) return me.right - unitsize < me.resting.left;
else return me.left + unitsize > me.resting.right;
}
function moveJumping(me) {
moveSimple(me);
if(me.resting) {
me.yvel = -abs(me.jumpheight);
me.resting = false;
}
}
// Floating: the vertical version
// Example usage on World 1-3
// [moveFloating, 30, 72] slides up and down between 30 and 72
function moveFloating(me) {
setPlatformEndpoints(me);
me.begin = map.floor * unitsize - me.begin;
me.end = map.floor * unitsize - me.end;
(me.movement = moveFloatingReal)(me);
}
function moveFloatingReal(me) {
if(me.top < me.end)
me.yvel = min(me.yvel + unitsized32, me.maxvel);
else if(me.bottom > me.begin)
me.yvel = max(me.yvel - unitsized32, -me.maxvel);
movePlatformNorm(me);
}
// Sliding: the horizontal version
// Example usage on World 3-3
// [moveSliding, 228, 260] slides back and forth between 228 and 260
function moveSliding(me) {
setPlatformEndpoints(me);
(me.movement = moveSlidingReal)(me);
}
function moveSlidingReal(me) {
if(gamescreen.left + me.left < me.begin)
me.xvel = min(me.xvel + unitsized32, me.maxvel);
else if(gamescreen.left + me.right > me.end)
me.xvel = max(me.xvel - unitsized32, -me.maxvel);
movePlatformNorm(me);
}
// Makes sure begin < end by swapping if not so
function setPlatformEndpoints(me) {
if(me.begin > me.end) {
var temp = me.begin;
me.begin = me.end;
me.end = temp;
}
}
function collideTransport(me, solid) {
characterTouchedSolid(me, solid);
if(solid != me.resting) return;
solid.movement = movePlatformNorm;
solid.collide = characterTouchedSolid;
solid.xvel = unitsized2;
}
// To do: make me.collide and stages w/functions
// To do: split this into .partner and whatnot
function moveFalling(me) {
if(me != mario.resting) return me.yvel = 0;
// Since Mario is on me, fall
shiftVert(me, me.yvel += unitsized8);
setBottom(mario, me.top);
// After a velocity threshold, always fall
if(me.yvel >= unitsize * 2.8) {
me.freefall = true;
me.movement = moveFreeFalling;
}
}
function moveFallingScale(me) {
// If Mario is resting on me, fall
if(mario.resting == me) {
shiftScaleStringVert(me, me.string, me.yvel += unitsized16);
shiftScaleStringVert(me.partner, me.partner.string, -me.yvel);
me.tension += me.yvel;
me.partner.tension -= me.yvel;
}
// Otherwise, if me or partner has a positive yvel, slow it down
else if(me.yvel > 0) {
shiftScaleStringVert(me, me.string, me.yvel -= unitsized32);
shiftScaleStringVert(me.partner, me.partner.string, -me.yvel);
me.tension -= me.yvel;
me.partner.tension += me.yvel;
}
// If the platform falls off
if(me.partner.tension <= 0) {
me.collide = me.partner.collide = characterTouchedSolid;
// Keep falling at an increasing pace
me.movement = me.partner.movement = moveFreeFalling;
}
}
function moveFreeFalling(me) {
shiftVert(me, me.yvel += unitsized16);
if(me.yvel > unitsizet2)
me.movement = function(me) { shiftVert(me, me.yvel); }
}
function shiftScaleStringVert(me, string, yvel) {
shiftVert(me, yvel);
string.bottom = me.top;
string.height = (string.bottom - string.top) / unitsize;
updateSize(string);
}
function setClass(me, strin) { me.className = strin; setThingSprite(me); }
function setClassInitial(me, strin) { me.className = strin; }
function addClass(me, strin) { me.className += " " + strin; setThingSprite(me); }
function removeClass(me, strout) { me.className = me.className.replace(new RegExp(" " + strout,"gm"),''); setThingSprite(me); }
function switchClass(me, strout, strin) { removeClass(me, strout); addClass(me, strin); }
function removeClasses(me) {
var strings, arr, i, j;
for(i = 1; i < arguments.length; ++i) {
arr = arguments[i];
if(!(arr instanceof Array)) arr = arr.split(" ");
for(j = arr.length - 1; j >= 0; --j)
removeClass(me, arr[j]);
}
}
function addClasses(me, strings) {
var arr = strings instanceof Array ? strings : strings.split(" ");
for(var i = arr.length - 1; i >= 0; --i)
addClass(me, arr[i]);
}
// Used in Editor
function addElementClass(element, strin) { element.className += " " + strin; }
function removeElementClass(element, strin) { element.className = element.className.replace(new RegExp(" " + strin,"gm"),''); }
function flipHoriz(me) { addClass(me, "flipped"); }
function flipVert(me) { addClass(me, "flip-vert"); }
function unflipHoriz(me) { removeClass(me, "flipped"); }
function unflipVert(me) { removeClass(me, "flip-vert"); }
/*
* Deaths & removing
*/
// Javascript memory management, you are bad and should feel bad.
function deleteThing(me, array, arrayloc) {
array.splice(arrayloc, 1);
if(me.ondelete) me.ondelete();
}
function switchContainers(me, outer, inner) {
outer.splice(outer.indexOf(me), 1);
inner.push(me);
}
function containerForefront(me, container) {
container.splice(container.indexOf(me), 1);
container.unshift(me);
}
function killNormal(me) {
if(!me) return;
me.hidden = me.dead = true;
me.alive = me.resting = me.movement = false;
TimeHandler.clearAllCycles(me);
}
function killFlip(me, extra) {
flipVert(me);
me.bottomBump = function() {};
me.nocollide = me.dead = true;
me.resting = me.movement = me.speed = me.xvel = me.nofall = false;
me.yvel = -unitsize;
TimeHandler.addEvent(function(me) { killNormal(me); }, 70 + (extra || 0));
}
// To do: phase this out in favor of an addEvent-based one
function generalMovement(me, dx, dy, cleartime) {
var move = setInterval(function() {
shiftVert(me, dy);
shiftHoriz(me, dx);
}, timer);
setTimeout(function() { clearInterval(move); }, cleartime);
}
function blockBumpMovement(me) {
var dir = -3, dd = .5;
// To do: addEventInterval?
var move = setInterval(function() {
shiftVert(me, dir);
dir += dd;
if(dir == 3.5) {
clearInterval(move);
me.up = false;
}
determineThingCollisions(me); // for coins
}, timer);
}
function emergeUp(me, solid) {
play("Powerup Appears");
flipHoriz(me);
me.nomove = me.nocollide = me.alive = me.nofall = me.emerging = true;
determineThingQuadrants(me);
switchContainers(me, characters, scenery);
// Start moving up
var move = setInterval(function() {
shiftVert(me, -unitsized8);
// Stop once the bottom is high enough
if(me.bottom <= solid.top) {
clearInterval(move);
switchContainers(me, scenery, characters);
me.nocollide = me.nomove = me.moveleft = me.nofall = me.emerging = false;
// If it has a function to call after being completely out (vines), do it
if(me.emergeOut) me.emergeOut(me, solid);
// If there's movement, don't do it at first
if(me.movement) {
me.movementsave = me.movement;
me.movement = moveSimple;
// Wait until it's off the solid
me.moving = TimeHandler.addEventInterval(function(me, solid) {
if(me.resting != solid) {
TimeHandler.addEvent(function(me) { me.movement = me.movementsave; }, 1, me);
return true;
}
}, 1, Infinity, me, solid);
}
}
}, timer);
}
function flicker(me, cleartime, interval) {
var cleartime = round(cleartime) || 49,
interval = round(interval) || 3;
me.flickering = true;
TimeHandler.addEventInterval(function(me) { me.hidden = !me.hidden; }, interval, cleartime, me);
TimeHandler.addEvent(function(me) { me.flickering = me.hidden = false; }, cleartime * interval + 1, me);
}
// Kills all characters other than mario
// Used in endCastleOutside/Inside
// Also kills all moving solids
function killOtherCharacters() {
var thing, i;
if(window.characters) {
for(i = characters.length - 1; i >= 0; --i) {
thing = characters[i];
if(!thing.nokillend) deleteThing(thing, characters, i);
else if(thing.killonend) thing.killonend(thing);
}
}
if(window.solids) {
for(i = solids.length - 1; i >= 0; --i)
if(solids[i].killonend)
deleteThing(solids[i], solids, i);
}
}
function lookTowardMario(me, big) {
// Mario is to the left
if(mario.right <= me.left) {
if(!me.lookleft || big) {
me.lookleft = true;
me.moveleft = false;
unflipHoriz(me);
}
}
// Mario is to the right
else if(mario.left >= me.right) {
if(me.lookleft || big) {
me.lookleft = false;
me.moveleft = true;
flipHoriz(me);
}
}
}
function lookTowardThing(me, thing) {
// It's to the left
if(thing.right <= me.left) {
me.lookleft = true;
me.moveleft = false;
unflipHoriz(me);
}
// It's to the right
else if(thing.left >= me.right) {
me.lookleft = false;
me.moveleft = true;
flipHoriz(me);
}
}