Best JavaScript code snippet using chai
FullScreenMario.js
Source:FullScreenMario.js
...170 FSM.GroupHolder.clearArrays();171 FSM.ItemsHolder.hideContainer();172 FSM.TimeHandler.cancelAllEvents();173 FSM.PixelDrawer.setBackground("black");174 FSM.addThing(text, FSM.MapScreener.width / 2, FSM.MapScreener.height / 2);175 texts = text.children;176 textWidth = -(texts[texts.length - 1].right - texts[0].left) / 2;177 for (i = 0; i < texts.length; i += 1) {178 FSM.shiftHoriz(texts[i], textWidth);179 }180 FSM.TimeHandler.addEvent(function () {181 FSM.gameStart();182 FSM.ItemsHolder.displayContainer();183 }, 420);184 FSM.ModAttacher.fireEvent("onGameOver");185 };186 /**187 * Slight addition to the GameStartr thingProcess Function. The Thing's hit188 * check type is cached immediately.189 *190 * @param thing The Thing being processed.191 * @param title What type Thing this is (the name of the class).192 * @param settings Additional settings to be given to the Thing.193 * @param defaults The default settings for the Thing's class.194 * @remarks This is generally called as the onMake call in an ObjectMakr.195 */196 FullScreenMario.prototype.thingProcess = function (thing, title, settings, defaults) {197 // Infinite height refers to objects that reach exactly to the bottom198 if (thing.height === "Infinity" || thing.height === Infinity) {199 thing.height = thing.FSM.getAbsoluteHeight(thing.y) / thing.FSM.unitsize;200 }201 _super.prototype.thingProcess.call(this, thing, title, settings, defaults);202 thing.FSM.ThingHitter.cacheChecksForType(thing.title, thing.groupType);203 };204 /**205 * Generates a key for a Thing based off the current area and the Thing's206 * basic attributes. This should be used for PixelRender.get calls, to207 * cache the Thing's sprite.208 *209 * @param thing210 * @returns A key that to identify the Thing's sprite.211 */212 FullScreenMario.prototype.generateThingKey = function (thing) {213 return thing.GameStarter.AreaSpawner.getArea().setting214 + " " + thing.groupType + " "215 + thing.title + " " + thing.className;216 };217 /**218 * Adds a Thing via addPreThing based on the specifications in a PreThing.219 * This is done relative to MapScreener.left and MapScreener.floor.220 *221 * @param prething A PreThing whose Thing is to be added to the game.222 */223 FullScreenMario.prototype.addPreThing = function (prething) {224 var thing = prething.thing, position = prething.position || thing.position;225 thing.FSM.addThing(thing, prething.left * thing.FSM.unitsize - thing.FSM.MapScreener.left, (thing.FSM.MapScreener.floor - prething.top) * thing.FSM.unitsize);226 // Either the prething or thing, in that order, may request to be in the227 // front or back of its container using the "position" attribute228 if (position) {229 thing.FSM.TimeHandler.addEvent(function () {230 switch (position) {231 case "beginning":232 thing.FSM.arrayToBeginning(thing, thing.FSM.GroupHolder.getGroup(thing.groupType));233 break;234 case "end":235 thing.FSM.arrayToEnd(thing, thing.FSM.GroupHolder.getGroup(thing.groupType));236 break;237 default:238 break;239 }240 });241 }242 thing.FSM.ModAttacher.fireEvent("onAddPreThing", prething);243 };244 /**245 * Adds a new Player Thing to the game and sets it as EightBitter.play. Any246 * required additional settings (namely keys, power/size, and swimming) are247 * applied here.248 *249 * @param left A left edge to place the Thing at (by default, unitsize * 16).250 * @param bottom A bottom to place the Thing upon (by default, unitsize * 16).251 * @returns A newly created Player in the game.252 */253 FullScreenMario.prototype.addPlayer = function (left, bottom) {254 if (left === void 0) { left = this.unitsize * 16; }255 if (bottom === void 0) { bottom = this.unitsize * 16; }256 var FSM = FullScreenMario.prototype.ensureCorrectCaller(this), player;257 player = FSM.player = FSM.ObjectMaker.make("Player", {258 "power": FSM.ItemsHolder.getItem("power")259 });260 player.keys = player.getKeys();261 if (FSM.MapScreener.underwater) {262 player.swimming = true;263 FSM.TimeHandler.addClassCycle(player, [264 "swim1", "swim2"265 ], "swimming", 5);266 FSM.TimeHandler.addEventInterval(player.FSM.animatePlayerBubbling, 96, Infinity, player);267 }268 FSM.setPlayerSizeSmall(player);269 if (player.power >= 2) {270 FSM.playerGetsBig(player, true);271 if (player.power === 3) {272 FSM.playerGetsFire(player);273 }274 }275 FSM.addThing(player, left, bottom - player.height * FSM.unitsize);276 FSM.ModAttacher.fireEvent("onAddPlayer", player);277 return player;278 };279 /**280 * Shortcut to call scrollThing on a Player.281 *282 * @param dx How far to scroll horizontally.283 * @param dy How far to scroll vertically.284 */285 FullScreenMario.prototype.scrollPlayer = function (dx, dy) {286 var FSM = FullScreenMario.prototype.ensureCorrectCaller(this);287 FSM.scrollThing(FSM.player, dx, dy);288 FSM.ModAttacher.fireEvent("onScrollPlayer", dx, dy);289 };290 /**291 * Triggered Function for when the game is paused. Music stops, the pause292 * bleep is played, and the mod event is fired.293 *294 * @param FSM295 */296 FullScreenMario.prototype.onGamePause = function (FSM) {297 FSM.AudioPlayer.pauseAll();298 FSM.AudioPlayer.play("Pause");299 FSM.ModAttacher.fireEvent("onGamePause");300 };301 /**302 * Triggered Function for when the game is played or unpause. Music resumes303 * and the mod event is fired.304 *305 * @param FSM306 */307 FullScreenMario.prototype.onGamePlay = function (FSM) {308 FSM.AudioPlayer.resumeAll();309 FSM.ModAttacher.fireEvent("onGamePlay");310 };311 /* Input312 */313 /**314 * Reacts to the left key being pressed. keys.run and leftDown are marked315 * and the mod event is fired.316 *317 * @param FSM318 * @param event The original user-caused Event.319 */320 FullScreenMario.prototype.keyDownLeft = function (FSM, event) {321 if (FSM.GamesRunner.getPaused()) {322 return;323 }324 var player = FSM.player;325 player.keys.run = -1;326 player.keys.leftDown = true; // independent of changes to keys.run327 player.FSM.ModAttacher.fireEvent("onKeyDownLeft");328 };329 /**330 * Reacts to the right key being pressed. keys.run and keys.rightDown are331 * marked and the mod event is fired.332 *333 * @param FSM334 * @param event The original user-caused Event.335 */336 FullScreenMario.prototype.keyDownRight = function (FSM, event) {337 if (FSM.GamesRunner.getPaused()) {338 return;339 }340 var player = FSM.player;341 player.keys.run = 1;342 player.keys.rightDown = true; // independent of changes to keys.run343 player.FSM.ModAttacher.fireEvent("onKeyDownRight");344 if (event && event.preventDefault !== undefined) {345 event.preventDefault();346 }347 };348 /**349 * Reacts to the up key being pressed. If a Player can jump, it does, and350 * underwater paddling is checked. The mod event is fired.351 *352 * @param FSM353 * @param event The original user-caused Event.354 */355 FullScreenMario.prototype.keyDownUp = function (FSM, event) {356 if (FSM.GamesRunner.getPaused()) {357 return;358 }359 var player = FSM.player;360 player.keys.up = true;361 if (player.canjump && (player.resting || FSM.MapScreener.underwater)) {362 player.keys.jump = true;363 player.canjump = false;364 player.keys.jumplev = 0;365 if (player.power > 1) {366 FSM.AudioPlayer.play("Jump Super");367 }368 else {369 FSM.AudioPlayer.play("Jump Small");370 }371 if (FSM.MapScreener.underwater) {372 FSM.TimeHandler.addEvent(function () {373 player.jumping = player.keys.jump = false;374 }, 14);375 }376 }377 FSM.ModAttacher.fireEvent("onKeyDownUp");378 if (event && event.preventDefault !== undefined) {379 event.preventDefault();380 }381 };382 /**383 * Reacts to the down key being pressed. A player's keys.crouch is marked384 * and the mod event is fired.385 *386 * @param FSM387 * @param event The original user-caused Event.388 */389 FullScreenMario.prototype.keyDownDown = function (FSM, event) {390 if (FSM.GamesRunner.getPaused()) {391 return;392 }393 var player = FSM.player;394 player.keys.crouch = true;395 FSM.ModAttacher.fireEvent("onKeyDownDown");396 if (event && event.preventDefault !== undefined) {397 event.preventDefault();398 }399 };400 /**401 * Reacts to the sprint key being pressed. Firing happens if a Player is402 * able, keys.spring is marked, and the mod event is fired.403 *404 * @param FSM405 * @param event The original user-caused Event.406 */407 FullScreenMario.prototype.keyDownSprint = function (FSM, event) {408 if (FSM.GamesRunner.getPaused()) {409 return;410 }411 var player = FSM.player;412 if (player.power === 3 && player.keys.sprint === false && !player.crouching) {413 player.fire(player);414 }415 player.keys.sprint = true;416 player.FSM.ModAttacher.fireEvent("onKeyDownSprint");417 if (event && event.preventDefault !== undefined) {418 event.preventDefault();419 }420 };421 /**422 * Reacts to the pause key being pressed. The game is either paused or unpaused,423 * and the mod event is fired.424 *425 * @param FSM426 * @param event The original user-caused Event.427 */428 FullScreenMario.prototype.keyDownPause = function (FSM, event) {429 if (FSM.GamesRunner.getPaused()) {430 FSM.GamesRunner.play();431 }432 else {433 FSM.GamesRunner.pause();434 }435 FSM.ModAttacher.fireEvent("onKeyDownPause");436 if (event && event.preventDefault !== undefined) {437 event.preventDefault();438 }439 };440 /**441 * Reacts to the mute key being lifted. Muting is toggled and the mod event442 * is fired.443 *444 * @param FSM445 * @param event The original user-caused Event.446 */447 FullScreenMario.prototype.keyDownMute = function (FSM, event) {448 if (FSM.GamesRunner.getPaused()) {449 return;450 }451 FSM.AudioPlayer.toggleMuted();452 FSM.ModAttacher.fireEvent("onKeyDownMute");453 if (event && event.preventDefault !== undefined) {454 event.preventDefault();455 }456 };457 /**458 * Reacts to the left key being lifted. keys.run and keys.leftDown are459 * marked and the mod event is fired.460 *461 * @param FSM462 * @param event The original user-caused Event.463 */464 FullScreenMario.prototype.keyUpLeft = function (FSM, event) {465 var player = FSM.player;466 player.keys.run = 0;467 player.keys.leftDown = false;468 FSM.ModAttacher.fireEvent("onKeyUpLeft");469 if (event && event.preventDefault !== undefined) {470 event.preventDefault();471 }472 };473 /**474 * Reacts to the right key being lifted. keys.run and keys.rightDown are475 * marked and the mod event is fired.476 *477 * @param FSM478 * @param event The original user-caused Event.479 */480 FullScreenMario.prototype.keyUpRight = function (FSM, event) {481 var player = FSM.player;482 player.keys.run = 0;483 player.keys.rightDown = false;484 FSM.ModAttacher.fireEvent("onKeyUpRight");485 if (event && event.preventDefault !== undefined) {486 event.preventDefault();487 }488 };489 /**490 * Reacts to the up key being lifted. Jumping stops and the mod event is491 * fired.492 *493 * @param FSM494 * @param event The original user-caused Event.495 */496 FullScreenMario.prototype.keyUpUp = function (FSM, event) {497 var player = FSM.player;498 if (!FSM.MapScreener.underwater) {499 player.keys.jump = player.keys.up = false;500 }501 player.canjump = true;502 FSM.ModAttacher.fireEvent("onKeyUpUp");503 if (event && event.preventDefault !== undefined) {504 event.preventDefault();505 }506 };507 /**508 * Reacts to the down key being lifted. keys.crouch is marked, crouch509 * removal happens if necessary, and the mod event is fired.510 *511 * @param FSM512 * @param event The original user-caused Event.513 */514 FullScreenMario.prototype.keyUpDown = function (FSM, event) {515 var player = FSM.player;516 player.keys.crouch = false;517 if (!player.piping) {518 FSM.animatePlayerRemoveCrouch(player);519 }520 FSM.ModAttacher.fireEvent("onKeyUpDown");521 if (event && event.preventDefault !== undefined) {522 event.preventDefault();523 }524 };525 /**526 * Reacts to the spring key being lifted. keys.sprint is marked and the mod527 * event is fired.528 *529 * @param FSM530 * @param event The original user-caused Event.531 */532 FullScreenMario.prototype.keyUpSprint = function (FSM, event) {533 var player = FSM.player;534 player.keys.sprint = false;535 FSM.ModAttacher.fireEvent("onKeyUpSprint");536 if (event && event.preventDefault !== undefined) {537 event.preventDefault();538 }539 };540 /**541 * Reacts to the pause key being lifted. The mod event is fired.542 *543 * @param FSM544 * @param event The original user-caused Event.545 */546 FullScreenMario.prototype.keyUpPause = function (FSM, event) {547 FSM.ModAttacher.fireEvent("onKeyUpPause");548 if (event && event.preventDefault !== undefined) {549 event.preventDefault();550 }551 };552 /**553 * Reacts to a right click being pressed. Pausing is toggled and the mod554 * event is fired.555 *556 * @param FSM557 * @param event The original user-caused Event.558 */559 FullScreenMario.prototype.mouseDownRight = function (FSM, event) {560 FSM.GamesRunner.togglePause();561 FSM.ModAttacher.fireEvent("onMouseDownRight");562 if (event && event.preventDefault !== undefined) {563 event.preventDefault();564 }565 };566 /**567 * Reacts to a regularly caused device motion event. Acceleration is checked568 * for changed tilt horizontally (to trigger left or right key statuses) or569 * changed tilt vertically (jumping). The mod event is also fired.570 *571 * @param FSM572 * @param event The original user-caused Event.573 */574 FullScreenMario.prototype.deviceMotion = function (FSM, event) {575 var player = FSM.player, deviceMotionStatus = FSM.deviceMotionStatus, acceleration = event.accelerationIncludingGravity;576 FSM.ModAttacher.fireEvent("onDeviceMotion", event);577 if (deviceMotionStatus.y !== undefined) {578 deviceMotionStatus.dy = acceleration.y - deviceMotionStatus.y;579 if (deviceMotionStatus.dy > 0.21) {580 FSM.keyDownUp(FSM);581 }582 else if (deviceMotionStatus.dy < -0.14) {583 FSM.keyUpUp(FSM);584 }585 }586 deviceMotionStatus.x = acceleration.x;587 deviceMotionStatus.y = acceleration.y;588 if (deviceMotionStatus.x > 2.1) {589 if (!deviceMotionStatus.motionLeft) {590 player.FSM.keyDownLeft(FSM);591 deviceMotionStatus.motionLeft = true;592 }593 }594 else if (deviceMotionStatus.x < -2.1) {595 if (!deviceMotionStatus.motionRight) {596 player.FSM.keyDownRight(FSM);597 deviceMotionStatus.motionRight = true;598 }599 }600 else {601 if (deviceMotionStatus.motionLeft) {602 player.FSM.keyUpLeft(FSM);603 deviceMotionStatus.motionLeft = false;604 }605 if (deviceMotionStatus.motionRight) {606 player.FSM.keyUpRight(FSM);607 deviceMotionStatus.motionRight = false;608 }609 }610 };611 /**612 * Checks whether inputs can be fired, which is equivalent to the status of613 * the MapScreener's nokeys variable (an inverse value).614 *615 * @param FSM616 * @returns Whether inputs are allowed to trigger.617 */618 FullScreenMario.prototype.canInputsTrigger = function (FSM) {619 return !FSM.MapScreener.nokeys;620 };621 /* Upkeep maintenence622 */623 /**624 * Regular maintenance Function called to decrease time every 25 game ticks.625 *626 * @param FSM627 * @returns Whether time should stop counting, which is whether it's <= 0.628 */629 FullScreenMario.prototype.maintainTime = function (FSM) {630 if (!FSM.MapScreener.notime) {631 FSM.ItemsHolder.decrease("time", 1);632 return false;633 }634 if (!FSM.ItemsHolder.getItem("time")) {635 return true;636 }637 return false;638 };639 /**640 * Regular maintenance Function called on the Scenery group every 350641 * upkeeps (slightly over 5 seconds). Things are checked for being alive642 * and to the left of QuadsKeeper.left; if they aren't, they are removed.643 *644 * @param FSM645 */646 FullScreenMario.prototype.maintainScenery = function (FSM) {647 var things = FSM.GroupHolder.getGroup("Scenery"), delx = FSM.QuadsKeeper.left, thing, i;648 for (i = 0; i < things.length; i += 1) {649 thing = things[i];650 if (thing.right < delx && thing.outerok !== 2) {651 FSM.arrayDeleteThing(thing, things, i);652 i -= 1;653 }654 }655 };656 /**657 * Regular maintenance Function called on the Solids group every upkeep.658 * Things are checked for being alive and to the right of QuadsKeeper.left;659 * if they aren't, they are removed. Each Thing is also allowed a movement660 * Function.661 *662 * @param FSM663 * @param solids FSM's GroupHolder's Solid group.664 */665 FullScreenMario.prototype.maintainSolids = function (FSM, solids) {666 var delx = FSM.QuadsKeeper.left, solid, i;667 FSM.QuadsKeeper.determineAllQuadrants("Solid", solids);668 for (i = 0; i < solids.length; i += 1) {669 solid = solids[i];670 if (solid.alive && solid.right > delx) {671 if (solid.movement) {672 solid.movement(solid);673 }674 }675 else if (!solid.alive || solid.outerok !== 2) {676 FSM.arrayDeleteThing(solid, solids, i);677 i -= 1;678 }679 }680 };681 /**682 * Regular maintenance Function called on the Characters group every upkeep.683 * Things have gravity and y-velocities, collision detection, and resting684 * checks applied before they're checked for being alive. If they are, they685 * are allowed a movement Function; if not, they are removed.686 *687 * @param FSM688 * @param characters FSM's GroupHolder's Characters group.689 */690 FullScreenMario.prototype.maintainCharacters = function (FSM, characters) {691 var delx = FSM.QuadsKeeper.right, character, i;692 for (i = 0; i < characters.length; i += 1) {693 character = characters[i];694 // Gravity695 if (character.resting) {696 character.yvel = 0;697 }698 else {699 if (!character.nofall) {700 character.yvel += character.gravity || FSM.MapScreener.gravity;701 }702 character.yvel = Math.min(character.yvel, FSM.MapScreener.maxyvel);703 }704 // Position updating and collision detection705 character.under = character.undermid = undefined;706 FSM.updatePosition(character);707 FSM.QuadsKeeper.determineThingQuadrants(character);708 FSM.ThingHitter.checkHitsForThing(character);709 // Overlaps710 if (character.overlaps && character.overlaps.length) {711 FSM.maintainOverlaps(character);712 }713 // Resting tests714 if (character.resting) {715 if (!FSM.isCharacterOnResting(character, character.resting)) {716 if (character.onRestingOff) {717 character.onRestingOff(character, character.resting);718 }719 else {720 // Necessary for moving platforms721 character.resting = undefined;722 }723 }724 else {725 character.yvel = 0;726 FSM.setBottom(character, character.resting.top);727 }728 }729 // Movement or deletion730 // To do: rethink this...731 if (character.alive) {732 if (!character.player &&733 (character.numquads === 0 || character.left > delx) &&734 (!character.outerok || (character.outerok !== 2735 && character.right < FSM.MapScreener.width - delx))) {736 FSM.arrayDeleteThing(character, characters, i);737 i -= 1;738 }739 else {740 if (!character.nomove && character.movement) {741 character.movement(character);742 }743 }744 }745 else {746 FSM.arrayDeleteThing(character, characters, i);747 i -= 1;748 }749 }750 };751 /**752 * Maintenance Function only triggered for Things that are known to have753 * overlapping Solids stored in their overlaps attribute. This will slide754 * the offending Thing away from the midpoint of those overlaps once a call755 * until it's past the boundary (and check for those boundaries if not756 * already set).757 *758 * @param character A Character that is known to be overlapping Solid(s).759 */760 FullScreenMario.prototype.maintainOverlaps = function (character) {761 // If checkOverlaps is still true, this is the first maintain call762 if (character.checkOverlaps) {763 if (!character.FSM.setOverlapBoundaries(character)) {764 return;765 }766 }767 character.FSM.slideToX(character, character.overlapGoal, character.FSM.unitsize);768 // Goal to the right: has the thing gone far enough to the right?769 if (character.overlapGoRight) {770 if (character.left >= character.overlapCheck) {771 character.FSM.setLeft(character, character.overlapCheck);772 }773 else {774 return;775 }776 }777 else {778 // Goal to the left: has the thing gone far enough to the left?779 if (character.right <= character.overlapCheck) {780 character.FSM.setRight(character, character.overlapCheck);781 }782 else {783 return;784 }785 }786 // A check above didn't fail into a return, so overlapping is solved787 character.overlaps.length = 0;788 character.checkOverlaps = true;789 };790 /**791 * Sets the overlapping properties of a Thing when it is first detected as792 * overlapping in maintainOverlaps. All Solids in its overlaps Array are793 * checked to find the leftmost and rightmost extremes and midpoint.794 * Then, the Thing is checked for being to the left or right of the795 * midpoint, and the goal set to move it away from the midpoint.796 *797 * @param thing798 * @returns Whether the Thing's overlaps were successfully recorded.799 */800 FullScreenMario.prototype.setOverlapBoundaries = function (thing) {801 // Only having one overlap means nothing should be done802 if (thing.overlaps.length === 1) {803 thing.overlaps.length = 0;804 return false;805 }806 var rightX = -Infinity, leftX = Infinity, overlaps = thing.overlaps, other, leftThing, rightThing, midpoint, i;807 for (i = 0; i < overlaps.length; i += 1) {808 other = overlaps[i];809 if (other.right > rightX) {810 rightThing = other;811 }812 if (other.left < leftX) {813 leftThing = other;814 }815 }816 midpoint = (leftX + rightX) / 2;817 if (thing.FSM.getMidX(thing) >= midpoint) {818 thing.overlapGoal = Infinity;819 thing.overlapGoRight = true;820 thing.overlapCheck = rightThing.right;821 }822 else {823 thing.overlapGoal = -Infinity;824 thing.overlapGoRight = false;825 thing.overlapCheck = leftThing.left;826 }827 thing.checkOverlaps = false;828 return true;829 };830 /**831 * Regular maintenance Function called on a Player every upkeep. A barrage832 * of tests are applied, namely falling/jumping, dieing, x- and y-velocities,833 * running, and scrolling. This is separate from the movePlayer movement834 * Function that will be called in maintainCharacters.835 *836 * @param FSM837 */838 FullScreenMario.prototype.maintainPlayer = function (FSM) {839 var player = FSM.player;840 if (!FSM.isThingAlive(player)) {841 return;842 }843 // Player is falling844 if (player.yvel > 0) {845 if (!FSM.MapScreener.underwater) {846 player.keys.jump = false;847 }848 // Jumping?849 if (!player.jumping && !player.crouching) {850 // Paddling? (from falling off a solid)851 if (FSM.MapScreener.underwater) {852 if (!player.paddling) {853 FSM.switchClass(player, "paddling", "paddling");854 player.paddling = true;855 }856 }857 else {858 FSM.addClass(player, "jumping");859 player.jumping = true;860 }861 }862 // Player has fallen too far863 if (!player.dieing && player.top > FSM.MapScreener.bottom) {864 // If the map has an exit (e.g. cloud world), transport there865 if (FSM.AreaSpawner.getArea().exit) {866 FSM.setLocation(FSM.AreaSpawner.getArea().exit);867 }868 else {869 // Otherwise, since Player is below the screen, kill him dead870 FSM.killPlayer(player, 2);871 }872 return;873 }874 }875 // Player is moving to the right876 if (player.xvel > 0) {877 if (player.right > FSM.MapScreener.middleX) {878 // If Player is to the right of the screen's middle, move the screen879 if (player.right > FSM.MapScreener.right - FSM.MapScreener.left) {880 player.xvel = Math.min(0, player.xvel);881 }882 }883 }884 else if (player.left < 0) {885 // Player is moving to the left886 // Stop Player from going to the left.887 player.xvel = Math.max(0, player.xvel);888 }889 // Player is hitting something (stop jumping)890 if (player.under) {891 player.jumpcount = 0;892 }893 // Scrolloffset is how far over the middle player's right is894 if (FSM.MapScreener.canscroll) {895 var scrolloffset = player.right - FSM.MapScreener.middleX;896 if (scrolloffset > 0) {897 FSM.scrollWindow(Math.min(player.scrollspeed, scrolloffset));898 }899 }900 };901 /* Collision detectors902 */903 /**904 * Function generator for the generic canThingCollide checker. This is used905 * repeatedly by ThingHittr to generate separately optimized Functions for906 * different Thing types.907 *908 * @returns A Function that generates a canThingCollide checker.909 */910 FullScreenMario.prototype.generateCanThingCollide = function () {911 /**912 * Generic checker for canCollide, used for both Solids and Characters.913 * This just returns if the Thing is alive and doesn't have the914 * nocollide flag.915 *916 * @param thing917 * @returns Whether the thing can collide.918 */919 return function canThingCollide(thing) {920 return thing.alive && !thing.nocollide;921 };922 };923 /**924 * @param thing925 * @returns Whether the Thing is alive, meaning it has a true alive flag926 * and a false dead flag.927 */928 FullScreenMario.prototype.isThingAlive = function (thing) {929 return thing && thing.alive && !thing.dead;930 };931 /**932 * Generic base function to check if one Thing is touching another. This933 * will be called by the more specific Thing touching functions.934 *935 * @param thing936 * @param other937 * @returns Whether the two Things are touching.938 * @remarks The horizontal checks use allow a unitsize of flexibility.939 */940 FullScreenMario.prototype.isThingTouchingThing = function (thing, other) {941 return (!thing.nocollide && !other.nocollide942 && thing.right - thing.FSM.unitsize > other.left943 && thing.left + thing.FSM.unitsize < other.right944 && thing.bottom >= other.top945 && thing.top <= other.bottom);946 };947 /**948 * General top collision detection Function for two Things to determine if949 * one Thing is on top of another. This takes into consideration factors950 * such as which are solid or an enemy, and y-velocity.951 *952 * @param thing953 * @param other954 * @returns Whether thing is on top of other.955 * @remarks This is a more specific form of isThingTouchingThing.956 */957 FullScreenMario.prototype.isThingOnThing = function (thing, other) {958 // If thing is a solid and other is falling, thing can't be above other959 if (thing.groupType === "Solid" && other.yvel > 0) {960 return false;961 }962 // If other is falling faster than thing, and isn't a solid,963 // thing can't be on top (if anything, the opposite is true)964 if (thing.yvel < other.yvel && other.groupType !== "Solid") {965 return false;966 }967 // If thing is a Player, and it's on top of an enemy, that's true968 if (thing.player && thing.bottom < other.bottom && other.enemy) {969 return true;970 }971 // If thing is too far to the right, it can't be touching other972 if (thing.left + thing.FSM.unitsize >= other.right) {973 return false;974 }975 // If thing is too far to the left, it can't be touching other976 if (thing.right - thing.FSM.unitsize <= other.left) {977 return false;978 }979 // If thing's bottom is below other's top, factoring tolerance and980 // other's vertical velocity, they're touching981 if (thing.bottom <= other.top + other.toly + other.yvel) {982 return true;983 }984 // Same as before, but with velocity as the absolute difference985 // between their two velocities986 if (thing.bottom <= other.top + other.toly + Math.abs(thing.yvel - other.yvel)) {987 return true;988 }989 // None of the above checks passed for true, so this is false (thing's990 // bottom is above other's top)991 return false;992 };993 /**994 * Top collision Function to determine if a Thing is on top of a Solid.995 *996 * @param thing997 * @param other998 * @returns Whether thing is on top of other.999 * @remarks Similar to isThingOnThing, but more specifically used for1000 * isCharacterOnSolid and isCharacterOnResting1001 */1002 FullScreenMario.prototype.isThingOnSolid = function (thing, other) {1003 // If thing is too far to the right, they're not touching1004 if (thing.left + thing.FSM.unitsize >= other.right) {1005 return false;1006 }1007 // If thing is too far to the left, they're not touching1008 if (thing.right - thing.FSM.unitsize <= other.left) {1009 return false;1010 }1011 // If thing's bottom is below other's top, factoring thing's velocity1012 // and other's tolerance, they're touching1013 if (thing.bottom - thing.yvel <= other.top + other.toly + thing.yvel) {1014 return true;1015 }1016 // Same as before, but with velocity as the absolute difference between1017 // their two velocities1018 if (thing.bottom <= other.top + other.toly + Math.abs(thing.yvel - other.yvel)) {1019 return true;1020 }1021 // None of the above checks passed for true, so this is false (thing's1022 // bottom is above other's top1023 return false;1024 };1025 /**1026 * Top collision Function to determine if a character is on top of a solid.1027 * This is always true for resting (since resting checks happen before when1028 * this should be called).1029 *1030 * @param thing1031 * @param other1032 * @returns Whether thing is on top of other.1033 */1034 FullScreenMario.prototype.isCharacterOnSolid = function (thing, other) {1035 // If character is resting on solid, this is automatically true1036 if (thing.resting === other) {1037 return true;1038 }1039 // If the character is jumping upwards, it's not on a solid1040 // (removing this check would cause Mario to have "sticky" behavior when1041 // jumping at the corners of solids)1042 if (thing.yvel < 0) {1043 return false;1044 }1045 // The character and solid must be touching appropriately1046 if (!thing.FSM.isThingOnSolid(thing, other)) {1047 return false;1048 }1049 // Corner case: when character is exactly falling off the right (false)1050 if (thing.left + thing.xvel + thing.FSM.unitsize === other.right) {1051 return false;1052 }1053 // Corner case: when character is exactly falling off the left (false)1054 if (thing.right - thing.xvel - thing.FSM.unitsize === other.left) {1055 return false;1056 }1057 // None of the above checks caught a falsity, so this must be true1058 return true;1059 };1060 /**1061 * Top collision Function to determine if a character should be considered1062 * resting on a solid. This mostly uses isThingOnSolid, but also checks for1063 * the corner cases of the character being exactly at the edge of the solid1064 * (such as when jumping while next to it).1065 *1066 * @param thing1067 * @param other1068 * @returns Whether thing is on top of other.1069 */1070 FullScreenMario.prototype.isCharacterOnResting = function (thing, other) {1071 if (!thing.FSM.isThingOnSolid(thing, other)) {1072 return false;1073 }1074 // Corner case: when character is exactly falling off the right (false)1075 if (thing.left + thing.xvel + thing.FSM.unitsize === other.right) {1076 return false;1077 }1078 // Corner case: when character is exactly falling off the left (false)1079 if (thing.right - thing.xvel - thing.FSM.unitsize === other.left) {1080 return false;1081 }1082 // None of the above checks caught a falsity, so this must be true1083 return true;1084 };1085 /**1086 * Function generator for the generic isCharacterTouchingCharacter checker.1087 * This is used repeatedly by ThingHittr to generate separately optimized1088 * Functions for different Thing types.1089 *1090 * @returns A Function that generates isCharacterTouchingCharacter.1091 */1092 FullScreenMario.prototype.generateIsCharacterTouchingCharacter = function () {1093 /**1094 * Generic checker for whether two characters are touching each other.1095 * This mostly checks to see if either has the nocollidechar flag, and1096 * if the other is a player. isThingTouchingThing is used after.1097 *1098 * @param thing1099 * @param other1100 * @returns Whether thing is touching other.1101 */1102 return function isCharacterTouchingCharacter(thing, other) {1103 if (thing.nocollidechar && (!other.player || thing.nocollideplayer)) {1104 return false;1105 }1106 if (other.nocollidechar && (!thing.player || other.nocollideplayer)) {1107 return false;1108 }1109 return thing.FSM.isThingTouchingThing(thing, other);1110 };1111 };1112 /**1113 * Function generator for the generic isCharacterTouchingSolid checker. This1114 * is used repeatedly by ThingHittr to generate separately optimized1115 * Functions for different Thing types.1116 *1117 * @returns A Function that generates isCharacterTouchingSolid.1118 */1119 FullScreenMario.prototype.generateIsCharacterTouchingSolid = function () {1120 /**1121 * Generic checker for whether a character is touching a solid. The1122 * hidden, collideHidden, and nocollidesolid flags are most relevant.1123 *1124 * @param thing1125 * @param other1126 * @returns Whether thing is touching other.1127 */1128 return function isCharacterTouchingSolid(thing, other) {1129 // Hidden solids can only be touched by a Player bottom-bumping1130 // them, or by specifying collideHidden1131 if (other.hidden && !other.collideHidden) {1132 if (!thing.player || !thing.FSM.isSolidOnCharacter(other, thing)) {1133 return false;1134 }1135 }1136 if (thing.nocollidesolid && !(thing.allowUpSolids && other.up)) {1137 return false;1138 }1139 return thing.FSM.isThingTouchingThing(thing, other);1140 };1141 };1142 /**1143 * @param thing1144 * @param other1145 * @returns Whether thing's bottom is above other's top, allowing for1146 * other's toly.1147 */1148 FullScreenMario.prototype.isCharacterAboveEnemy = function (thing, other) {1149 return thing.bottom < other.top + other.toly;1150 };1151 /**1152 * @param thing1153 * @param other1154 * @returns Whether thing's top is above other's bottom, allowing for1155 * the Thing's toly and yvel.1156 */1157 FullScreenMario.prototype.isCharacterBumpingSolid = function (thing, other) {1158 return thing.top + thing.toly + Math.abs(thing.yvel) > other.bottom;1159 };1160 /**1161 * @param thing1162 * @param other1163 * @returns Whether thing is "overlapping" other.1164 */1165 FullScreenMario.prototype.isCharacterOverlappingSolid = function (thing, other) {1166 return thing.top <= other.top && thing.bottom > other.bottom;1167 };1168 /**1169 * @param thing1170 * @param other1171 * @returns Whether thing, typically a solid, is on top of other.1172 * @remarks This is similar to isThingOnThing, but more specifically1173 * used for characterTouchedSolid.1174 */1175 FullScreenMario.prototype.isSolidOnCharacter = function (thing, other) {1176 // This can never be true if other is falling1177 if (other.yvel >= 0) {1178 return false;1179 }1180 // Horizontally, all that's required is for the other's midpoint to1181 // be within the thing's left and right1182 var midx = thing.FSM.getMidX(other);1183 if (midx <= thing.left || midx >= thing.right) {1184 return false;1185 }1186 // If the thing's bottom is below the other's top, factoring1187 // tolerance and velocity, that's false (this function assumes they're1188 // already touching)1189 if (thing.bottom - thing.yvel > other.top + other.toly - other.yvel) {1190 return false;1191 }1192 // The above checks never caught falsities, so this must be true1193 return true;1194 };1195 /* Collision reactions1196 */1197 /**1198 * Externally facing Function to gain some number of lives. ItemsHolder1199 * increases the "score" statistic, an audio is played, and the mod event is1200 * fired.1201 *1202 * @param amount How many lives to gain (by default, 1).1203 * @param nosound Whether the sound should be skipped (by default,1204 * false).1205 */1206 FullScreenMario.prototype.gainLife = function (amount, nosound) {1207 var FSM = FullScreenMario.prototype.ensureCorrectCaller(this);1208 amount = Number(amount) || 1;1209 FSM.ItemsHolder.increase("lives", amount);1210 if (!nosound) {1211 this.AudioPlayer.play("Gain Life");1212 }1213 FSM.ModAttacher.fireEvent("onGainLife", amount);1214 };1215 /**1216 * Basic Function for an item to jump slightly into the air, such as from1217 * a Player hitting a solid below it.1218 *1219 * @param thing An item.1220 * @remarks This simply moves the thing up slightly and decreases its1221 * y-velocity, without considering x-direction.1222 */1223 FullScreenMario.prototype.itemJump = function (thing) {1224 thing.yvel -= FullScreenMario.unitsize * 1.4;1225 this.shiftVert(thing, -FullScreenMario.unitsize);1226 };1227 /**1228 * Generic Function for when a Player jumps on top of an enemy. The enemy1229 * is killed, a Player's velocity points upward, and score is gained.1230 *1231 * @param thing A Player jumping on other.1232 * @param other An Enemy being jumped upon.1233 */1234 FullScreenMario.prototype.jumpEnemy = function (thing, other) {1235 if (thing.keys.up) {1236 thing.yvel = thing.FSM.unitsize * -1.4;1237 }1238 else {1239 thing.yvel = thing.FSM.unitsize * -0.7;1240 }1241 thing.xvel *= 0.91;1242 thing.FSM.AudioPlayer.play("Kick");1243 if (!thing.item || other.shell) {1244 thing.jumpcount += 1;1245 thing.FSM.scoreOn(thing.FSM.findScore(thing.jumpcount + thing.jumpers), other);1246 }1247 thing.jumpers += 1;1248 thing.FSM.TimeHandler.addEvent(function (thing) {1249 thing.jumpers -= 1;1250 }, 1, thing);1251 };1252 /**1253 * Callback for a Player hitting a Mushroom or FireFlower. A player's1254 * power and the ItemsHolder's "power" statistic both go up, and the1255 * corresponding animations and mod event are triggered.1256 *1257 * @param thing A Player powering up.1258 * @param other A Mushroom powering up hte Player.1259 */1260 FullScreenMario.prototype.playerShroom = function (thing, other) {1261 if (thing.shrooming || !thing.player) {1262 return;1263 }1264 thing.FSM.AudioPlayer.play("Powerup");1265 thing.FSM.scoreOn(1000, thing.FSM.player);1266 if (thing.power < 3) {1267 thing.FSM.ItemsHolder.increase("power");1268 if (thing.power < 3) {1269 thing.shrooming = true;1270 thing.power += 1;1271 if (thing.power === 3) {1272 thing.FSM.playerGetsFire(thing.FSM.player);1273 }1274 else {1275 thing.FSM.playerGetsBig(thing.FSM.player);1276 }1277 }1278 }1279 thing.FSM.ModAttacher.fireEvent("onPlayerShroom", thing, other);1280 };1281 /**1282 * Callback for a Player hitting a Mushroom1Up. The game simply calls1283 * gainLife and triggers the mod event.1284 *1285 * @param thing A Player gaining a life.1286 * @param other The Mushroom1Up giving the life.1287 */1288 FullScreenMario.prototype.playerShroom1Up = function (thing, other) {1289 if (!thing.player) {1290 return;1291 }1292 thing.FSM.gainLife(1);1293 thing.FSM.ModAttacher.fireEvent("onPlayerShroom1Up", thing, other);1294 };1295 /**1296 * Callback for a Player hitting a Star. A set of animation loops and1297 * sounds play, and the mod event is triggered. After some long period time,1298 * playerStarDown is called to start the process of removing star power.1299 *1300 * @param thing A Player gaining star powers.1301 * @param timeout How long to wait before calling playerStarDown1302 * (by default, 560).1303 */1304 FullScreenMario.prototype.playerStarUp = function (thing, timeout) {1305 if (timeout === void 0) { timeout = 560; }1306 thing.star += 1;1307 thing.FSM.switchClass(thing, "normal fiery", "star");1308 thing.FSM.AudioPlayer.play("Powerup");1309 thing.FSM.AudioPlayer.addEventListener("Powerup", "ended", thing.FSM.AudioPlayer.playTheme.bind(thing.FSM.AudioPlayer, "Star", true));1310 thing.FSM.TimeHandler.addClassCycle(thing, ["star1", "star2", "star3", "star4"], "star", 2);1311 thing.FSM.TimeHandler.addEvent(thing.FSM.playerStarDown, timeout || 560, thing);1312 thing.FSM.ModAttacher.fireEvent("onPlayerStarUp", thing);1313 };1314 /**1315 * Trigger to commence reducing a Player's star power. This slows the1316 * class cycle, times a playerStarOffCycle trigger, and fires the mod event.1317 *1318 * @param thing A Player losing star powers.1319 */1320 FullScreenMario.prototype.playerStarDown = function (thing) {1321 if (!thing.player) {1322 return;1323 }1324 thing.FSM.TimeHandler.cancelClassCycle(thing, "star");1325 thing.FSM.TimeHandler.addClassCycle(thing, [1326 "star1", "star2", "star3", "star4"1327 ], "star", 5);1328 thing.FSM.TimeHandler.addEvent(thing.FSM.playerStarOffCycle, 140, thing);1329 thing.FSM.AudioPlayer.removeEventListeners("Powerup", "ended");1330 thing.FSM.ModAttacher.fireEvent("onPlayerStarDown", thing);1331 };1332 /**1333 * Trigger to continue reducing a Player's star power. This resumes1334 * playing the regular theme, times a playerStarOffFinal trigger, and fires1335 * the mod event.1336 *1337 * @param thing A Player losing star powers.1338 */1339 FullScreenMario.prototype.playerStarOffCycle = function (thing) {1340 if (!thing.player) {1341 return;1342 }1343 if (thing.star > 1) {1344 thing.star -= 1;1345 return;1346 }1347 if (!thing.FSM.AudioPlayer.getTheme().paused) {1348 thing.FSM.AudioPlayer.playTheme();1349 }1350 thing.FSM.TimeHandler.addEvent(thing.FSM.playerStarOffFinal, 70, thing);1351 thing.FSM.ModAttacher.fireEvent("onPlayerStarOffCycle", thing);1352 };1353 /**1354 * Trigger to finish reducing a Player's star power. This actually reduces1355 * a Player's star attribute, cancels the sprite cycle, adds the previous1356 * classes back, and fires the mod event.1357 *1358 * @param thing A Player losing star powers.1359 */1360 FullScreenMario.prototype.playerStarOffFinal = function (thing) {1361 if (!thing.player) {1362 return;1363 }1364 thing.star -= 1;1365 thing.FSM.TimeHandler.cancelClassCycle(thing, "star");1366 thing.FSM.removeClasses(thing, "star star1 star2 star3 star4");1367 thing.FSM.addClass(thing, "normal");1368 if (thing.power === 3) {1369 thing.FSM.addClass(thing, "fiery");1370 }1371 thing.FSM.ModAttacher.fireEvent("onPlayerStarOffFinal", thing);1372 };1373 /**1374 * Sizing modifier for a Player, typically called when entering a location1375 * or colliding with a Mushroom. This sets a Player's size to the large1376 * mode and optionally plays the animation. The mod event is then fired.1377 *1378 * @param thing A Player increasing in size.1379 * @param noAnimation Whether to skip the animation (by default,1380 * false).1381 */1382 FullScreenMario.prototype.playerGetsBig = function (thing, noAnimation) {1383 thing.FSM.setPlayerSizeLarge(thing);1384 thing.FSM.removeClasses(thing, "crouching small");1385 thing.FSM.updateBottom(thing, 0);1386 thing.FSM.updateSize(thing);1387 if (noAnimation) {1388 thing.FSM.addClass(thing, "large");1389 }1390 else {1391 thing.FSM.playerGetsBigAnimation(thing);1392 }1393 thing.FSM.ModAttacher.fireEvent("onPlayerGetsBig", thing);1394 };1395 /**1396 * Animation scheduler for a Player getting big. The shrooming classes are1397 * cycled through rapidly while a Player's velocity is paused.1398 *1399 * @param thing A Player increasing in size.1400 */1401 FullScreenMario.prototype.playerGetsBigAnimation = function (thing) {1402 var stages = [1403 "shrooming1", "shrooming2",1404 "shrooming1", "shrooming2",1405 "shrooming3", "shrooming2", "shrooming3"1406 ];1407 thing.FSM.addClass(thing, "shrooming");1408 thing.FSM.animateCharacterPauseVelocity(thing);1409 // The last stage in the events clears it, resets movement, and stops1410 stages.push(function (thing) {1411 thing.shrooming = false;1412 stages.length = 0;1413 thing.FSM.addClass(thing, "large");1414 thing.FSM.removeClasses(thing, "shrooming shrooming3");1415 thing.FSM.animateCharacterResumeVelocity(thing);1416 return true;1417 });1418 thing.FSM.TimeHandler.addClassCycle(thing, stages, "shrooming", 6);1419 };1420 /**1421 * Sizing modifier for a Player, typically called when going down to1422 * normal size after being large. This containst eha nimation scheduling1423 * to cycle through paddling classes, then flickers a Player. The mod1424 * event is fired.1425 *1426 * @param thing A Player decreasing in size.1427 */1428 FullScreenMario.prototype.playerGetsSmall = function (thing) {1429 var bottom = thing.bottom;1430 thing.FSM.animateCharacterPauseVelocity(thing);1431 // Step one1432 thing.nocollidechar = true;1433 thing.FSM.animateFlicker(thing);1434 thing.FSM.removeClasses(thing, "running skidding jumping fiery");1435 thing.FSM.addClasses(thing, "paddling small");1436 // Step two (t+21)1437 thing.FSM.TimeHandler.addEvent(function (thing) {1438 thing.FSM.removeClass(thing, "large");1439 thing.FSM.setPlayerSizeSmall(thing);1440 thing.FSM.setBottom(thing, bottom - FullScreenMario.unitsize);1441 }, 21, thing);1442 // Step three (t+42)1443 thing.FSM.TimeHandler.addEvent(function (thing) {1444 thing.FSM.animateCharacterResumeVelocity(thing, false);1445 thing.FSM.removeClass(thing, "paddling");1446 if (thing.running || thing.xvel) {1447 thing.FSM.addClass(thing, "running");1448 }1449 thing.FSM.PixelDrawer.setThingSprite(thing);1450 }, 42, thing);1451 // Step four (t+70)1452 thing.FSM.TimeHandler.addEvent(function (thing) {1453 thing.nocollidechar = false;1454 }, 70, thing);1455 thing.FSM.ModAttacher.fireEvent("onPlayerGetsSmall");1456 };1457 /**1458 * Visual changer for when a Player collides with a FireFlower. The1459 * "fiery" class is added, and the mod event is fired.1460 *1461 * @param thing A Player gaining fire powers.1462 */1463 FullScreenMario.prototype.playerGetsFire = function (thing) {1464 thing.shrooming = false;1465 if (!thing.star) {1466 thing.FSM.addClass(thing, "fiery");1467 }1468 thing.FSM.ModAttacher.fireEvent("onPlayerGetsFire");1469 };1470 /**1471 * Actually sets the size for a player to small (8x8) via setSize and1472 * updateSize.1473 *1474 * @param thing A Player decreasing in size.1475 */1476 FullScreenMario.prototype.setPlayerSizeSmall = function (thing) {1477 thing.FSM.setSize(thing, 8, 8, true);1478 thing.FSM.updateSize(thing);1479 };1480 /**1481 * Actually sets the size for a player to large (8x16) via setSize and1482 * updateSize.1483 *1484 * @param thing A Player increasing in size.1485 */1486 FullScreenMario.prototype.setPlayerSizeLarge = function (thing) {1487 thing.FSM.setSize(thing, 8, 16, true);1488 thing.FSM.updateSize(thing);1489 };1490 /**1491 * Removes the crouching flag from a Player and re-adds the running cycle.1492 * If a Player is large (has power > 1), size and classes must be set.1493 *1494 * @param thing A Player that is no longer crouching.1495 */1496 FullScreenMario.prototype.animatePlayerRemoveCrouch = function (thing) {1497 thing.crouching = false;1498 thing.toly = thing.tolyOld || 0;1499 if (thing.power !== 1) {1500 thing.FSM.setHeight(thing, 16, true, true);1501 thing.FSM.removeClasses(thing, "crouching");1502 thing.FSM.updateBottom(thing, 0);1503 thing.FSM.updateSize(thing);1504 }1505 thing.FSM.animatePlayerRunningCycle(thing);1506 };1507 /**1508 * Officially unattaches a player from a solid. The thing's physics flags1509 * are reset to normal, the two have their attachment flags set, and the1510 * thing is set to be jumping off.1511 *1512 * @param thing A Player attached to other.1513 * @param other A Solid thing is attached to.1514 */1515 FullScreenMario.prototype.unattachPlayer = function (thing, other) {1516 thing.nofall = false;1517 thing.nocollide = false;1518 thing.checkOverlaps = true;1519 thing.attachedSolid = undefined;1520 thing.xvel = thing.keys ? thing.keys.run : 0;1521 thing.movement = thing.FSM.movePlayer;1522 thing.FSM.addClass(thing, "jumping");1523 thing.FSM.removeClasses(thing, "climbing", "animated");1524 other.attachedCharacter = undefined;1525 };1526 /**1527 * Adds an invisible RestingStone underneath a Player. It is hidden and1528 * unable to collide until a Player falls to its level, at which point the1529 * stone is set underneath a Player to be rested upon.1530 *1531 * @param thing A Player respawning into the game.1532 */1533 FullScreenMario.prototype.playerAddRestingStone = function (thing) {1534 var stone = thing.FSM.addThing("RestingStone", thing.left, thing.top + thing.FSM.unitsize * 48);1535 thing.nocollide = true;1536 thing.FSM.TimeHandler.addEventInterval(function () {1537 if (thing.bottom < stone.top) {1538 return false;1539 }1540 thing.nocollide = false;1541 thing.FSM.setMidXObj(stone, thing);1542 thing.FSM.setBottom(thing, stone.top);1543 return true;1544 }, 1, Infinity);1545 };1546 /**1547 * Marks a new overlapping Thing in the first Thing's overlaps Array,1548 * creating the Array if needed.1549 *1550 * @param thing The Thing that is overlapping another Thing.1551 * @param other The Thing being added to the overlaps Array.1552 */1553 FullScreenMario.prototype.markOverlap = function (thing, other) {1554 if (!thing.overlaps) {1555 thing.overlaps = [other];1556 }1557 else {1558 thing.overlaps.push(other);1559 }1560 };1561 /* Spawn / activate functions1562 */1563 /**1564 * Spawn callback for DeadGoombas. They simply disappear after 21 steps.1565 *1566 * @param thing A DeadGoomba being spawned.1567 */1568 FullScreenMario.prototype.spawnDeadGoomba = function (thing) {1569 thing.FSM.TimeHandler.addEvent(FullScreenMario.prototype.killNormal, 21, thing);1570 };1571 /**1572 * Spawn callback for HammerBros. Gravity is reduced, and the hammer and1573 * jump event intervals are started. The cyclical movement counter is set to1574 * 0.1575 *1576 * @param thing A HammerBro being spawned.1577 */1578 FullScreenMario.prototype.spawnHammerBro = function (thing) {1579 thing.counter = 0;1580 thing.gravity = thing.FSM.MapScreener.gravity / 2.1;1581 thing.FSM.TimeHandler.addEvent(thing.FSM.animateThrowingHammer, 35, thing, 7);1582 thing.FSM.TimeHandler.addEventInterval(thing.FSM.animateJump, 140, Infinity, thing);1583 };1584 /**1585 * Spawn callback for Bowsers. The cyclical movement counter is set to 0 and1586 * the firing and jumping event intervals are started. If it also specifies1587 * a throwing interval, that's started too.1588 *1589 * @param thing A Bowser being spawned.1590 */1591 FullScreenMario.prototype.spawnBowser = function (thing) {1592 var i;1593 thing.counter = 0;1594 thing.deathcount = 0;1595 for (i = 0; i < thing.fireTimes.length; i += 1) {1596 thing.FSM.TimeHandler.addEventInterval(thing.FSM.animateBowserFire, thing.fireTimes[i], Infinity, thing);1597 }1598 for (i = 0; i < thing.jumpTimes.length; i += 1) {1599 thing.FSM.TimeHandler.addEventInterval(thing.FSM.animateBowserJump, thing.jumpTimes[i], Infinity, thing);1600 }1601 if (thing.throwing) {1602 for (i = 0; i < thing.throwAmount; i += 1) {1603 thing.FSM.TimeHandler.addEvent(function () {1604 thing.FSM.TimeHandler.addEventInterval(thing.FSM.animateBowserThrow, thing.throwPeriod, Infinity, thing);1605 }, thing.throwDelay + i * thing.throwBetween);1606 }1607 }1608 };1609 /**1610 * Spawn callback for Piranhas. The movement counter and direction are1611 * reset, and if the Piranha is on a pipe, it has a reduced height (6).1612 *1613 * @param thing A Piranha being spawned.1614 */1615 FullScreenMario.prototype.spawnPiranha = function (thing) {1616 var bottom;1617 thing.counter = 0;1618 thing.direction = thing.FSM.unitsize / -40;1619 if (thing.onPipe) {1620 bottom = thing.bottom;1621 thing.FSM.setHeight(thing, 6);1622 thing.FSM.setBottom(thing, bottom);1623 }1624 };1625 /**1626 * Spawn callback for Bloopers. Its squeeze and movement counters are1627 * set to 0.1628 *1629 * @param thing A Blooper being spawned.1630 */1631 FullScreenMario.prototype.spawnBlooper = function (thing) {1632 thing.squeeze = 0;1633 thing.counter = 0;1634 };1635 /**1636 * Spawn callback for Podoboos. The jumping interval is set to the Thing's1637 * frequency.1638 *1639 * @param thing A Podoboo being spawned.1640 */1641 FullScreenMario.prototype.spawnPodoboo = function (thing) {1642 thing.FSM.TimeHandler.addEventInterval(thing.FSM.animatePodobooJumpUp, thing.frequency, Infinity, thing);1643 };1644 /**1645 * Spawn callback for Lakitus. MapScreenr registers the most recently1646 * added Lakitu as some areas spawn them every once in a while.1647 *1648 * @param thing A Lakitu being spawned.1649 */1650 FullScreenMario.prototype.spawnLakitu = function (thing) {1651 thing.FSM.MapScreener.lakitu = thing;1652 thing.FSM.TimeHandler.addEventInterval(thing.FSM.animateLakituThrowingSpiny, 140, Infinity, thing);1653 };1654 /**1655 * Spawning callback for Cannons. Unless specified by the noBullets flag,1656 * the firing interval is set to the Thing's frequency.1657 *1658 * @param thing A Cannon being spawned.1659 */1660 FullScreenMario.prototype.spawnCannon = function (thing) {1661 if (thing.noBullets) {1662 return;1663 }1664 thing.FSM.TimeHandler.addEventInterval(thing.FSM.animateCannonFiring, thing.frequency, thing.frequency, thing);1665 };1666 /**1667 * Spawning callback for CastleBlocks. If the Thing has fireballs, an Array1668 * of them are made and animated to tick around the block like a clock, set1669 * by the thing's speed and direction.1670 *1671 * @param thing A CastleBlock being spawned.1672 */1673 FullScreenMario.prototype.spawnCastleBlock = function (thing) {1674 if (!thing.fireballs) {1675 return;1676 }1677 var balls = [], i;1678 for (i = 0; i < thing.fireballs; i += 1) {1679 balls.push(thing.FSM.addThing("CastleFireball"));1680 thing.FSM.setMidObj(balls[i], thing);1681 }1682 if (thing.speed >= 0) {1683 thing.dt = 0.07;1684 thing.angle = 0.25;1685 }1686 else {1687 thing.dt = -0.07;1688 thing.angle = -0.25;1689 }1690 if (!thing.direction) {1691 thing.direction = -1;1692 }1693 thing.FSM.TimeHandler.addEventInterval(thing.FSM.animateCastleBlock, Math.round(7 / Math.abs(thing.speed)), Infinity, thing, balls);1694 };1695 /**1696 * Spawning callback for floating Things, such as Koopas and Platforms. The1697 * Thing's begin and end attributes are set relative to the MapScreener's1698 * floor, so its movement can handle cycling between the two.1699 *1700 * @param thing A Thing being spawned to float around.1701 */1702 FullScreenMario.prototype.spawnMoveFloating = function (thing) {1703 // Make sure thing.begin <= thing.end1704 thing.FSM.setMovementEndpoints(thing);1705 // Make thing.begin and thing.end relative to the area's floor1706 thing.begin = thing.FSM.MapScreener.floor * thing.FSM.unitsize - thing.begin;1707 thing.end = thing.FSM.MapScreener.floor * thing.FSM.unitsize - thing.end;1708 };1709 /**1710 * Spawning callback for sliding Things, such as Platforms. The Thing's1711 * begin and end attributes do not need to be relative to anything.1712 *1713 * @param thing A Thing being spawned to slide back and forth.1714 */1715 FullScreenMario.prototype.spawnMoveSliding = function (thing) {1716 // Make sure thing.begin <= thing.end1717 thing.FSM.setMovementEndpoints(thing);1718 };1719 /**1720 * Spawning callback for a Platform that's a part of a Scale.1721 *1722 * @param thing A Platform being spawned within a Scale group.1723 */1724 FullScreenMario.prototype.spawnScalePlatform = function (thing) {1725 var collection = thing.collection || {}, ownKey = thing.collectionKey === "platformLeft" ? "Left" : "Right", partnerKey = ownKey === "Left" ? "Right" : "Left";1726 thing.partners = {1727 "ownString": collection["string" + ownKey],1728 "partnerString": collection["string" + partnerKey],1729 "partnerPlatform": collection["platform" + partnerKey]1730 };1731 };1732 /**1733 * Generator callback to create a random CheepCheep. The spawn is given a1734 * random x-velocity, is placed at a random point just below the screen, and1735 * is oriented towards a Player.1736 *1737 * @param FSM1738 * @returns Whether CheepCheep spawning has been cancelled.1739 */1740 FullScreenMario.prototype.spawnRandomCheep = function (FSM) {1741 if (!FSM.MapScreener.spawningCheeps) {1742 return true;1743 }1744 var spawn = FSM.ObjectMaker.make("CheepCheep", {1745 "flying": true,1746 "xvel": FSM.NumberMaker.random() * FSM.unitsize * 1.4,1747 "yvel": FSM.unitsize * -1.41748 });1749 FSM.addThing(spawn, FSM.NumberMaker.random() * FSM.MapScreener.width, FSM.MapScreener.height);1750 if (spawn.left < FSM.MapScreener.width / 2) {1751 FSM.flipHoriz(spawn);1752 }1753 else {1754 spawn.xvel *= -1;1755 }1756 return false;1757 };1758 /**1759 * Generator callback to create a BulleBill. The spawn moves horizontally1760 * at a constant rate towards the left side of the bill, and is placed at a1761 * random point to the right side of the screen.1762 *1763 * @param FSM1764 * @returns Whether BulletBill spawning has been cancelled.1765 */1766 FullScreenMario.prototype.spawnRandomBulletBill = function (FSM) {1767 if (!FSM.MapScreener.spawningBulletBills) {1768 return true;1769 }1770 var spawn = FSM.ObjectMaker.make("BulletBill");1771 spawn.direction = 1;1772 spawn.moveleft = true;1773 spawn.xvel *= -1;1774 FSM.flipHoriz(spawn);1775 FSM.addThing(spawn, FSM.MapScreener.width, Math.floor(FSM.NumberMaker.randomIntWithin(0, FSM.MapScreener.floor) / 8) * 8 * FSM.unitsize);1776 return false;1777 };1778 /**1779 * Spawns a CustomText by killing it and placing the contents of its texts1780 * member variable. These are written with a determined amount of spacing1781 * between them, as if by a typewriter.1782 *1783 * @param thing A CustomText being spawned.1784 */1785 FullScreenMario.prototype.spawnCustomText = function (thing) {1786 var top = thing.top, texts = thing.texts, attributes = thing.textAttributes, spacingHorizontal = thing.spacingHorizontal * thing.FSM.unitsize, spacingVertical = thing.spacingVertical * thing.FSM.unitsize, spacingVerticalBlank = thing.spacingVerticalBlank * thing.FSM.unitsize, children = [], textChild, left, text, letter, i, j;1787 thing.children = children;1788 for (i = 0; i < texts.length; i += 1) {1789 if (!texts[i]) {1790 top += spacingVerticalBlank;1791 continue;1792 }1793 text = texts[i].text;1794 if (texts[i].offset) {1795 left = thing.left + texts[i].offset * thing.FSM.unitsize;1796 }1797 else {1798 left = thing.left;1799 }1800 for (j = 0; j < text.length; j += 1) {1801 letter = text[j];1802 if (thing.FSM.customTextMappings.hasOwnProperty(letter)) {1803 letter = thing.FSM.customTextMappings[letter];1804 }1805 letter = "Text" + thing.size + letter;1806 textChild = thing.FSM.ObjectMaker.make(letter, attributes);1807 textChild.FSM.addThing(textChild, left, top);1808 children.push(textChild);1809 left += textChild.width * thing.FSM.unitsize;1810 left += spacingHorizontal;1811 }1812 top += spacingVertical;1813 }1814 thing.FSM.killNormal(thing);1815 };1816 /**1817 * Spawning callback for generic detectors, activated as soon as they are1818 * placed. The Thing's activate trigger is called, then it is killed.1819 *1820 * @param thing A Detector being spawned.1821 */1822 FullScreenMario.prototype.spawnDetector = function (thing) {1823 thing.activate(thing);1824 thing.FSM.killNormal(thing);1825 };1826 /**1827 * Spawning callback for ScrollBlockers. If the Thing is to the right of1828 * the visible viewframe, it should limit scrolling when triggered.1829 *1830 * @param thing A ScrollBlocker being spawned.1831 */1832 FullScreenMario.prototype.spawnScrollBlocker = function (thing) {1833 if (thing.FSM.MapScreener.width < thing.right) {1834 thing.setEdge = true;1835 }1836 };1837 /**1838 * Used by Things in a collection to register themselves as a part of their1839 * container collection Object. This is called by onThingMake, so they're1840 * immediately put in the collection and have it as a member variable.1841 *1842 * @param collection The collection Object shared by all members.1843 * @param thing A member of the collection being spawned.1844 */1845 FullScreenMario.prototype.spawnCollectionComponent = function (collection, thing) {1846 thing.collection = collection;1847 collection[thing.collectionName] = thing;1848 };1849 /**1850 * Spawning callback for RandomSpawner Things, which generate a set of1851 * commands using the WorldSeeder to be piped into the AreaSpawnr, then1852 * spawn the immediate area.1853 *1854 * @param thing A RandomSpawner being spawned.1855 */1856 FullScreenMario.prototype.spawnRandomSpawner = function (thing) {1857 var FSM = thing.FSM, left = (thing.left + FSM.MapScreener.left) / FSM.unitsize;1858 FSM.WorldSeeder.clearGeneratedCommands();1859 FSM.WorldSeeder.generateFull({1860 "title": thing.randomization,1861 "top": thing.randomTop,1862 "right": left + thing.randomWidth,1863 "bottom": thing.randomBottom,1864 "left": left,1865 "width": thing.randomWidth,1866 "height": thing.randomTop - thing.randomBottom1867 });1868 FSM.WorldSeeder.runGeneratedCommands();1869 FSM.AreaSpawner.spawnArea("xInc", FSM.QuadsKeeper.top / FSM.unitsize, FSM.QuadsKeeper.right / FSM.unitsize, FSM.QuadsKeeper.bottom / FSM.unitsize, FSM.QuadsKeeper.left / FSM.unitsize);1870 };1871 /**1872 * Activation callback for starting spawnRandomCheep on an interval.1873 * MapScreener is notified that spawningCheeps is true.1874 *1875 * @param thing A Detector activated to start spawning CheepCheeps.1876 */1877 FullScreenMario.prototype.activateCheepsStart = function (thing) {1878 thing.FSM.MapScreener.spawningCheeps = true;1879 thing.FSM.TimeHandler.addEventInterval(thing.FSM.spawnRandomCheep, 21, Infinity, thing.FSM);1880 };1881 /**1882 * Activation callback to stop spawning CheepCheeps. MapScreener is notified1883 * that spawningCheeps is false.1884 *1885 * @param thing A Detector activated to stop spawning CheepCheeps.1886 */1887 FullScreenMario.prototype.activateCheepsStop = function (thing) {1888 thing.FSM.MapScreener.spawningCheeps = false;1889 };1890 /**1891 * Activation callback for starting spawnRandomBulletBill on an interval.1892 * MapScreener is notified that spawningBulletBills is true.1893 *1894 * @param thing A Detector activated to start spawning BulletBills.1895 */1896 FullScreenMario.prototype.activateBulletBillsStart = function (thing) {1897 thing.FSM.MapScreener.spawningBulletBills = true;1898 thing.FSM.TimeHandler.addEventInterval(thing.FSM.spawnRandomBulletBill, 210, Infinity, thing.FSM);1899 };1900 /**1901 * Activation callback to stop spawning BulletBills. MapScreener is notified1902 * that spawningBulletBills is false.1903 *1904 * @param thing A Detector activated to stop spawning BulletBills.1905 */1906 FullScreenMario.prototype.activateBulletBillsStop = function (thing) {1907 thing.FSM.MapScreener.spawningBulletBills = false;1908 };1909 /**1910 * Activation callback to tell the area's Lakitu, if it exists, to start1911 * fleeing the scene.1912 *1913 * @param thing A Detector activated to make the Lakitu flee.1914 */1915 FullScreenMario.prototype.activateLakituStop = function (thing) {1916 var lakitu = thing.FSM.MapScreener.lakitu;1917 if (!lakitu) {1918 return;1919 }1920 lakitu.fleeing = true;1921 lakitu.movement = thing.FSM.moveLakituFleeing;1922 };1923 /**1924 * Activation callback for a warp world area, triggered by a Player1925 * touching a collider on top of it. Piranhas disappear and texts are1926 * revealed.1927 *1928 * @param thing A Player activating the warp world.1929 * @param other A Detector triggered by thing to activate a warp world.1930 */1931 FullScreenMario.prototype.activateWarpWorld = function (thing, other) {1932 var collection = other.collection, key = 0, keyString, texts, j;1933 if (!thing.player) {1934 return;1935 }1936 texts = collection.Welcomer.children;1937 for (j = 0; j < texts.length; j += 1) {1938 if (texts[j].title !== "TextSpace") {1939 texts[j].hidden = false;1940 }1941 }1942 while (true) {1943 keyString = key + "-Text";1944 if (!collection.hasOwnProperty(keyString)) {1945 break;1946 }1947 texts = collection[keyString].children;1948 for (j = 0; j < texts.length; j += 1) {1949 if (texts[j].title !== "TextSpace") {1950 texts[j].hidden = false;1951 }1952 }1953 thing.FSM.killNormal(collection[key + "-Piranha"]);1954 key += 1;1955 }1956 };1957 /**1958 * Activation callback for when a Player lands on a RestingStone. The1959 * stone "appears" (via opacity), the regular theme plays if it wasn't1960 * already, and the RestingStone waits to kill itself when a Player isn't1961 * touching it.1962 *1963 * @param thing A RestingStone being landed on.1964 * @param other A Player landing on thing.1965 */1966 FullScreenMario.prototype.activateRestingStone = function (thing, other) {1967 if (thing.activated) {1968 return;1969 }1970 thing.activated = true;1971 thing.opacity = 1;1972 thing.FSM.AudioPlayer.playTheme();1973 thing.FSM.TimeHandler.addEventInterval(function () {1974 if (other.resting === thing) {1975 return false;1976 }1977 thing.FSM.killNormal(thing);1978 return true;1979 }, 1, Infinity);1980 };1981 /**1982 * Generic activation callback for DetectWindow Things. This is typically1983 * set as a .movement Function, so it waits until the calling Thing is1984 * within the MapScreener's area to call the activate Function and kill1985 * itself.1986 *1987 * @param thing A DetectWindow that might be activated.1988 */1989 FullScreenMario.prototype.activateWindowDetector = function (thing) {1990 if (thing.FSM.MapScreener.right - thing.FSM.MapScreener.left < thing.left) {1991 return;1992 }1993 thing.activate(thing);1994 thing.FSM.killNormal(thing);1995 };1996 /**1997 * Activation callback for ScrollBlocker Things. These are WindowDetectors1998 * that set MapScreener.canscroll to false when they're triggered. If the1999 * latest scrollWindow call pushed it too far to the left, it scrolls back2000 * the other way.2001 *2002 * @param thing A ScrollBlocker that might be activated.2003 */2004 FullScreenMario.prototype.activateScrollBlocker = function (thing) {2005 var dx = thing.FSM.MapScreener.width - thing.left;2006 thing.FSM.MapScreener.canscroll = false;2007 if (thing.setEdge && dx > 0) {2008 thing.FSM.scrollWindow(-dx);2009 }2010 };2011 /**2012 * Activation callback for ScrollBlocker Things. These are DetectCollision2013 * that set MapScreener.canscroll to true when they're triggered.2014 *2015 * @param thing An activated ScrollEnabler.2016 */2017 FullScreenMario.prototype.activateScrollEnabler = function (thing) {2018 thing.FSM.MapScreener.canscroll = true;2019 };2020 /**2021 * Activates the "before" component of a stretchable section. The creation2022 * commands of the section are loaded onto the screen as is and a2023 * DetectWindow is added to their immediate right that will trigger the2024 * equivalent activateSectionStretch.2025 *2026 * @param thing An activated SectionDecider.2027 */2028 FullScreenMario.prototype.activateSectionBefore = function (thing) {2029 var FSM = thing.FSM, MapsCreator = FSM.MapsCreator, MapScreener = FSM.MapScreener, AreaSpawner = FSM.AreaSpawner, area = AreaSpawner.getArea(), map = AreaSpawner.getMap(), prethings = AreaSpawner.getPreThings(), section = area.sections[thing.section || 0], left = (thing.left + MapScreener.left) / FSM.unitsize, before = section.before ? section.before.creation : undefined, command, i;2030 // If there is a before, parse each command into the prethings array2031 if (before) {2032 for (i = 0; i < before.length; i += 1) {2033 // A copy of the command must be used to not modify the original 2034 command = FSM.proliferate({}, before[i]);2035 // The command's x must be shifted by the thing's placement2036 if (!command.x) {2037 command.x = left;2038 }2039 else {2040 command.x += left;2041 }2042 // For Platforms that slide around, start and end are dynamic2043 if (command.sliding) {2044 command.begin += left;2045 command.end += left;2046 }2047 MapsCreator.analyzePreSwitch(command, prethings, area, map);2048 }2049 }2050 // Add a prething at the end of all this to trigger the stretch part2051 command = {2052 "thing": "DetectWindow",2053 "x": left + (before ? section.before.width : 0), "y": 0,2054 "activate": FSM.activateSectionStretch,2055 "section": thing.section || 02056 };2057 MapsCreator.analyzePreSwitch(command, prethings, area, map);2058 // Spawn new Things that should be placed for being nearby2059 AreaSpawner.spawnArea("xInc", MapScreener.top / FSM.unitsize, (MapScreener.left + FSM.QuadsKeeper.right) / FSM.unitsize, MapScreener.bottom / FSM.unitsize, left);2060 };2061 /**2062 * Activates the "stretch" component of a stretchable section. The creation2063 * commands of the section are loaded onto the screen and have their widths2064 * set to take up the entire width of the screen. A DetectWindow is added2065 * to their immediate right that will trigger the equivalent2066 * activateSectionAfter.2067 *2068 * @param thing An activated SectionDetector.2069 */2070 FullScreenMario.prototype.activateSectionStretch = function (thing) {2071 var FSM = thing.FSM, MapsCreator = FSM.MapsCreator, MapScreener = FSM.MapScreener, AreaSpawner = FSM.AreaSpawner, area = AreaSpawner.getArea(), map = AreaSpawner.getMap(), prethings = AreaSpawner.getPreThings(), section = area.sections[thing.section || 0], stretch = section.stretch ? section.stretch.creation : undefined, left = (thing.left + MapScreener.left) / FSM.unitsize, width = MapScreener.width / FSM.unitsize, command, i;2072 // If there is a stretch, parse each command into the current prethings array2073 if (stretch) {2074 for (i = 0; i < stretch.length; i += 1) {2075 // A copy of the command must be used, so the original isn't modified2076 command = FSM.proliferate({}, stretch[i]);2077 command.x = left;2078 // "stretch" the command by making its width equal to the screen2079 command.width = width;2080 MapsCreator.analyzePreSwitch(command, prethings, area, map);2081 }2082 // Add a prething at the end of all this to trigger the after part2083 command = {2084 "thing": "DetectWindow",2085 "x": left + width,2086 "y": 0,2087 "activate": FSM.activateSectionAfter,2088 "section": thing.section || 02089 };2090 MapsCreator.analyzePreSwitch(command, prethings, area, map);2091 }2092 // Spawn the map, so new Things that should be placed will be spawned if nearby2093 AreaSpawner.spawnArea("xInc", MapScreener.top / FSM.unitsize, left + (MapScreener.width / FSM.unitsize), MapScreener.bottom / FSM.unitsize, left);2094 };2095 /**2096 * Activates the "after" component of a stretchable sectin. The creation2097 * commands of the stretch are loaded onto the screen as is.2098 *2099 * @param thing An activated SectioNDetector.2100 */2101 FullScreenMario.prototype.activateSectionAfter = function (thing) {2102 // Since the section was passed, do the rest of things normally2103 var FSM = thing.FSM, MapsCreator = FSM.MapsCreator, MapScreener = FSM.MapScreener, AreaSpawner = FSM.AreaSpawner, area = AreaSpawner.getArea(), map = AreaSpawner.getMap(), prethings = AreaSpawner.getPreThings(), section = area.sections[thing.section || 0], left = (thing.left + MapScreener.left) / FSM.unitsize, after = section.after ? section.after.creation : undefined, command, i;2104 // If there is an after, parse each command into the current prethings array2105 if (after) {2106 for (i = 0; i < after.length; i += 1) {2107 // A copy of the command must be used, so the original isn't modified2108 command = FSM.proliferate({}, after[i]);2109 // The command's x-location must be shifted by the thing's placement2110 if (!command.x) {2111 command.x = left;2112 }2113 else {2114 command.x += left;2115 }2116 // For Platforms that slide around, start and end are dynamic2117 if (command.sliding) {2118 command.begin += left;2119 command.end += left;2120 }2121 MapsCreator.analyzePreSwitch(command, prethings, area, map);2122 }2123 }2124 // Spawn the map, so new Things that should be placed will be spawned if nearby2125 AreaSpawner.spawnArea("xInc", MapScreener.top / FSM.unitsize, left + (MapScreener.right / FSM.unitsize), MapScreener.bottom / FSM.unitsize, left);2126 };2127 /* Collision functions2128 */2129 /**2130 * Function generator for the generic hitCharacterSolid callback. This is2131 * used repeatedly by ThingHittr to generate separately optimized Functions2132 * for different Thing types.2133 *2134 * @returns A Function that generates hitCharacterSolid.2135 */2136 FullScreenMario.prototype.generateHitCharacterSolid = function () {2137 /**2138 * Generic callback for when a character touches a solid. Solids that2139 * "up" kill anything that didn't cause the up, but otherwise this will2140 * normally involve the solid's collide callback being called and2141 * under/undermid checks activating.2142 *2143 * @param thing2144 * @param other2145 * @returns Whether thing is hitting other.2146 */2147 return function hitCharacterSolid(thing, other) {2148 // "Up" solids are special (they kill things that aren't their .up)2149 if (other.up && thing !== other.up) {2150 return thing.FSM.collideCharacterSolidUp(thing, other);2151 }2152 other.collide(thing, other);2153 // If a character is bumping into the bottom, call that2154 if (thing.undermid) {2155 if (thing.undermid.bottomBump) {2156 thing.undermid.bottomBump(thing.undermid, thing);2157 }2158 }2159 else if (thing.under && thing.under && thing.under.bottomBump) {2160 thing.under.bottomBump(thing.under[0], thing);2161 }2162 // If the character is overlapping the solid, call that too2163 if (thing.checkOverlaps2164 && thing.FSM.isCharacterOverlappingSolid(thing, other)) {2165 thing.FSM.markOverlap(thing, other);2166 }2167 };2168 };2169 /**2170 * Function generator for the generic hitCharacterCharacter callback. This2171 * is used repeatedly by ThingHittr to generate separately optimized2172 * Functions for different Thing types.2173 *2174 * @returns A Function that generates hitCharacterCharacter.2175 */2176 FullScreenMario.prototype.generateHitCharacterCharacter = function () {2177 /**2178 * Generic callback for when a character touches another character. The2179 * first Thing's collide callback is called unless it's a player, in2180 * which the other Thing's is.2181 *2182 * @param thing2183 * @param other2184 * @returns Whether thing is hitting other.2185 */2186 return function hitCharacterCharacter(thing, other) {2187 // a Player calls the other's collide function, such as playerStar2188 if (thing.player) {2189 if (other.collide) {2190 return other.collide(thing, other);2191 }2192 }2193 else if (thing.collide) {2194 // Otherwise just use thing's collide function2195 thing.collide(other, thing);2196 }2197 };2198 };2199 /**2200 * Collision callback used by most Items. The item's action callback will2201 * be called only if the first Thing is a player.2202 *2203 * @param thing A Character touching other.2204 * @param other An Item being touched by thing.2205 */2206 FullScreenMario.prototype.collideFriendly = function (thing, other) {2207 if (!thing.player || !thing.FSM.isThingAlive(other)) {2208 return;2209 }2210 if (other.action) {2211 other.action(thing, other);2212 }2213 other.death(other);2214 };2215 /**2216 * General callback for when a character touches a solid. This mostly2217 * determines if the character is on top (it should rest on the solid), to2218 * the side (it should shouldn't overlap), or undernearth (it also shouldn't2219 * overlap).2220 *2221 * @param thing A Character touching other.2222 * @param other A Solid being touched by thing.2223 */2224 FullScreenMario.prototype.collideCharacterSolid = function (thing, other) {2225 if (other.up === thing) {2226 return;2227 }2228 // Character on top of solid2229 if (thing.FSM.isCharacterOnSolid(thing, other)) {2230 if (other.hidden && !other.collideHidden) {2231 return;2232 }2233 if (thing.resting !== other) {2234 thing.resting = other;2235 if (thing.onResting) {2236 thing.onResting(thing, other);2237 }2238 if (other.onRestedUpon) {2239 other.onRestedUpon(other, thing);2240 }2241 }2242 }2243 else if (thing.FSM.isSolidOnCharacter(other, thing)) {2244 // Solid on top of character2245 var midx = thing.FSM.getMidX(thing);2246 if (midx > other.left && midx < other.right) {2247 thing.undermid = other;2248 }2249 else if (other.hidden && !other.collideHidden) {2250 return;2251 }2252 if (!thing.under) {2253 thing.under = [other];2254 }2255 else {2256 thing.under.push(other);2257 }2258 if (thing.player) {2259 thing.keys.jump = false;2260 thing.FSM.setTop(thing, other.bottom - thing.toly + other.yvel);2261 }2262 thing.yvel = other.yvel;2263 }2264 if (other.hidden && !other.collideHidden) {2265 return;2266 }2267 // Character bumping into the side of the solid2268 if (thing.resting !== other2269 && !thing.FSM.isCharacterBumpingSolid(thing, other)2270 && !thing.FSM.isThingOnThing(thing, other)2271 && !thing.FSM.isThingOnThing(other, thing)2272 && !thing.under) {2273 // Character to the left of the solid2274 if (thing.right <= other.right) {2275 thing.xvel = Math.min(thing.xvel, 0);2276 thing.FSM.shiftHoriz(thing, Math.max(other.left + thing.FSM.unitsize - thing.right, thing.FSM.unitsize / -2));2277 }2278 else {2279 // Character to the right of the solid2280 thing.xvel = Math.max(thing.xvel, 0);2281 thing.FSM.shiftHoriz(thing, Math.min(other.right - thing.FSM.unitsize - thing.left, thing.FSM.unitsize / 2));2282 }2283 // Non-players flip horizontally2284 if (!thing.player) {2285 if (!thing.noflip) {2286 thing.moveleft = !thing.moveleft;2287 }2288 // Some items require fancy versions (e.g. Shell)2289 if (thing.item) {2290 thing.collide(other, thing);2291 }2292 }2293 else if (other.actionLeft) {2294 // Players trigger other actions (e.g. Pipe's mapExitPipeHorizontal)2295 thing.FSM.ModAttacher.fireEvent("onPlayerActionLeft", thing, other);2296 other.actionLeft(thing, other, other.transport);2297 }2298 }2299 };2300 /**2301 * Collision callback for a character hitting an "up" solid. If it has an2302 * onCollideUp callback, that is called; otherwise, it is killed.2303 *2304 * @param thing A Character touching other.2305 * @param other A Solid being touched by thing.2306 */2307 FullScreenMario.prototype.collideCharacterSolidUp = function (thing, other) {2308 if (thing.onCollideUp) {2309 thing.onCollideUp(thing, other);2310 }2311 else {2312 thing.FSM.scoreOn(thing.scoreBelow, thing);2313 thing.death(thing, 2);2314 }2315 };2316 /**2317 * Collision callback for an item hitting an "up" solid. Items just hop2318 * and switch direction.2319 *2320 * @param thing An Item touching other.2321 * @param other A Solid being touched by thing.2322 */2323 FullScreenMario.prototype.collideUpItem = function (thing, other) {2324 thing.FSM.animateCharacterHop(thing);2325 thing.moveleft = thing.FSM.objectToLeft(thing, other);2326 };2327 /**2328 * Collision callback for a floating coin being hit by an "up" solid. It is2329 * animated, as if it were hit as the contents of a solid.2330 *2331 * @param thing A Coin being touched by other.2332 * @param other A Solid touching thing.2333 */2334 FullScreenMario.prototype.collideUpCoin = function (thing, other) {2335 thing.blockparent = other;2336 thing.animate(thing, other);2337 };2338 /**2339 * Collision callback for a player hitting a regular Coin. The Coin2340 * disappears but points and Coin totals are both increased, along with2341 * the "Coin" sound being played.2342 *2343 * @param thing A Player touching other.2344 * @param other A Coin being touched by thing.2345 */2346 FullScreenMario.prototype.collideCoin = function (thing, other) {2347 if (!thing.player) {2348 return;2349 }2350 thing.FSM.AudioPlayer.play("Coin");2351 thing.FSM.ItemsHolder.increase("score", 200);2352 thing.FSM.ItemsHolder.increase("coins", 1);2353 thing.FSM.killNormal(other);2354 };2355 /**2356 * Collision callback for a player hitting a Star. The Star is killed, and2357 * a PlayerStarUp trigger is called on the Thing.2358 *2359 * @param thing A Player touching other.2360 * @param other A Star being touched by thing.2361 */2362 FullScreenMario.prototype.collideStar = function (thing, other) {2363 if (!thing.player || thing.star) {2364 return;2365 }2366 thing.FSM.playerStarUp(thing);2367 thing.FSM.ModAttacher.fireEvent("onCollideStar", thing, other);2368 };2369 /**2370 * Collision callback for a character being hit by a fireball. It will2371 * most likely be killed with an explosion unless it has the nofiredeath2372 * flag, in which case only the fireball dies.2373 *2374 * @param thing A Character being touched by other.2375 * @param other A Fireball touching thing.2376 */2377 FullScreenMario.prototype.collideFireball = function (thing, other) {2378 if (!thing.FSM.isThingAlive(thing) || thing.height < thing.FSM.unitsize) {2379 return;2380 }2381 if (thing.nofire) {2382 if (thing.nofire > 1) {2383 other.death(other);2384 }2385 return;2386 }2387 if (thing.nofiredeath) {2388 thing.FSM.AudioPlayer.playLocal("Bump", thing.FSM.getMidX(other));2389 thing.death(thing);2390 }2391 else {2392 thing.FSM.AudioPlayer.playLocal("Kick", thing.FSM.getMidX(other));2393 thing.death(thing, 2);2394 thing.FSM.scoreOn(thing.scoreFire, thing);2395 }2396 other.death(other);2397 };2398 /**2399 * Collision callback for hitting a CastleFireball. The character is killed2400 * unless it has the star flag, in which case the CastleFireball is.2401 *2402 * @param thing A Character being touched by other.2403 * @param other A CastleFireball touching thing.2404 */2405 FullScreenMario.prototype.collideCastleFireball = function (thing, other) {2406 if (thing.star) {2407 other.death(other);2408 }2409 else {2410 thing.death(thing);2411 }2412 };2413 /**2414 * Collision callback for when a character hits a Shell. This covers various2415 * cases, such as deaths, side-to-side Shell collisions, player stomps, and2416 * so on.2417 *2418 * @param thing A Character touching other.2419 * @param other A Shell being touched by thing.2420 */2421 FullScreenMario.prototype.collideShell = function (thing, other) {2422 // If only one is a shell, it should be other, not thing2423 if (thing.shell) {2424 if (other.shell) {2425 return thing.FSM.collideShellShell(thing, other);2426 }2427 return thing.FSM.collideShell(thing, other);2428 }2429 // Hitting a solid (e.g. wall) 2430 if (thing.groupType === "Solid") {2431 return thing.FSM.collideShellSolid(thing, other);2432 }2433 // Hitting a Player2434 if (thing.player) {2435 return thing.FSM.collideShellPlayer(thing, other);2436 }2437 // Assume anything else to be an enemy, which only moving shells kill2438 if (other.xvel) {2439 thing.FSM.killFlip(thing);2440 if (thing.shellspawn) {2441 thing = thing.FSM.killSpawn(thing);2442 }2443 thing.FSM.AudioPlayer.play("Kick");2444 thing.FSM.scoreOn(thing.FSM.findScore(other.enemyhitcount), thing);2445 other.enemyhitcount += 1;2446 }2447 else {2448 thing.moveleft = thing.FSM.objectToLeft(thing, other);2449 }2450 };2451 /**2452 * Collision callback for a solid being hit by a Shell. The Shell will2453 * bounce the opposition direction.2454 *2455 * @param thing A Solid being touched by other.2456 * @param other A Shell touching thing.2457 */2458 FullScreenMario.prototype.collideShellSolid = function (thing, other) {2459 if (other.right < thing.right) {2460 thing.FSM.AudioPlayer.playLocal("Bump", thing.left);2461 thing.FSM.setRight(other, thing.left);2462 other.xvel = -other.speed;2463 other.moveleft = true;2464 }2465 else {2466 thing.FSM.AudioPlayer.playLocal("Bump", thing.right);2467 thing.FSM.setLeft(other, thing.right);2468 other.xvel = other.speed;2469 other.moveleft = false;2470 }2471 };2472 /**2473 * Collision callback for when a Player hits a Shell. This covers all the2474 * possible scenarios, and is much larger than common sense dictates.2475 *2476 * @param thing A Player touching other.2477 * @param other A Shell being touched by thing.2478 */2479 FullScreenMario.prototype.collideShellPlayer = function (thing, other) {2480 var shelltoleft = thing.FSM.objectToLeft(other, thing), playerjump = thing.yvel > 0 && (thing.bottom <= other.top + thing.FSM.unitsize * 2);2481 // Star players kill the shell no matter what2482 if (thing.star) {2483 thing.FSM.scorePlayerShell(thing, other);2484 other.death(other, 2);2485 return;2486 }2487 // If the shell is already being landed on by a Player, see if it's2488 // still being pushed to the side, or has reversed direction (is deadly)2489 if (other.landing) {2490 // Equal shelltoleft measurements: it's still being pushed2491 if (other.shelltoleft === shelltoleft) {2492 // Tepmorarily increase the landing count of the shell; if it is 2493 // just being started, that counts as the score hit2494 other.landing += 1;2495 if (other.landing === 1) {2496 thing.FSM.scorePlayerShell(thing, other);2497 }2498 thing.FSM.TimeHandler.addEvent(function (other) {2499 other.landing -= 1;2500 }, 2, other);2501 }2502 else {2503 // Different shelltoleft measurements: it's deadly2504 thing.death(thing);2505 }2506 return;2507 }2508 // If the shell is being kicked by a Player, either by hitting a still2509 // shell or jumping onto an already moving one2510 if (other.xvel === 0 || playerjump) {2511 // Reset any signs of peeking from the shell2512 other.counting = 0;2513 // If the shell is standing still, make it move2514 if (other.xvel === 0) {2515 thing.FSM.AudioPlayer.play("Kick");2516 thing.FSM.scorePlayerShell(thing, other);2517 if (shelltoleft) {2518 other.moveleft = true;2519 other.xvel = -other.speed;2520 }2521 else {2522 other.moveleft = false;2523 other.xvel = other.speed;2524 }2525 other.hitcount += 1;2526 thing.FSM.TimeHandler.addEvent(function (other) {2527 other.hitcount -= 1;2528 }, 2, other);2529 }2530 else {2531 // Otherwise it was moving, but should now be still2532 other.xvel = 0;2533 }2534 if (other.peeking) {2535 other.peeking = 0;2536 thing.FSM.removeClass(other, "peeking");2537 other.height -= thing.FSM.unitsize / 8;2538 thing.FSM.updateSize(other);2539 }2540 // If a Player is landing on the shell (with movements and xvels2541 // already set), a Player should then jump up a bit2542 if (playerjump) {2543 thing.FSM.AudioPlayer.play("Kick");2544 if (!other.xvel) {2545 thing.FSM.jumpEnemy(thing, other);2546 thing.yvel *= 2;2547 // thing.FSM.scorePlayerShell(thing, other);2548 thing.FSM.setBottom(thing, other.top - thing.FSM.unitsize);2549 }2550 else {2551 }2552 other.landing += 1;2553 other.shelltoleft = shelltoleft;2554 thing.FSM.TimeHandler.addEvent(function (other) {2555 other.landing -= 1;2556 }, 2, other);2557 }2558 }2559 else {2560 // Since a Player is touching the shell normally, that's a death if2561 // the shell isn't moving away2562 if (!other.hitcount && ((shelltoleft && other.xvel > 0)2563 || (!shelltoleft && other.xvel < 0))) {2564 thing.death(thing);2565 }2566 }2567 };2568 /**2569 * Collision callback for two Shells. If one is moving, it kills the other;2570 * otherwise, they bounce off.2571 *2572 * @param thing A Shell touching other.2573 * @param other A Shell being touched by thing.2574 */2575 FullScreenMario.prototype.collideShellShell = function (thing, other) {2576 if (thing.xvel !== 0) {2577 if (other.xvel !== 0) {2578 var temp = thing.xvel;2579 thing.xvel = other.xvel;2580 other.xvel = temp;2581 thing.FSM.shiftHoriz(thing, thing.xvel);2582 thing.FSM.shiftHoriz(other, other.xvel);2583 }2584 else {2585 thing.FSM.ItemsHolder.increase("score", 500);2586 other.death(other);2587 }2588 }2589 else {2590 thing.FSM.ItemsHolder.increase("score", 500);2591 thing.death(thing);2592 }2593 };2594 /**2595 * Collision callback for a general character hitting an enemy. This covers2596 * many general cases, most of which involve a player and an enemy.2597 *2598 * @param thing A Character touching other.2599 * @param other An Enemy being touched by thing.2600 */2601 FullScreenMario.prototype.collideEnemy = function (thing, other) {2602 // If either is a player, make it thing (not other)2603 if (!thing.player && other.player) {2604 return thing.FSM.collideEnemy(thing, other);2605 }2606 // Death: nothing happens2607 if (!thing.FSM.isThingAlive(thing) || !thing.FSM.isThingAlive(other)) {2608 return;2609 }2610 // Items2611 if (thing.item) {2612 if (thing.collidePrimary) {2613 return thing.collide(other, thing);2614 }2615 return;2616 }2617 // For non-players, it's just to characters colliding: they bounce2618 if (!thing.player) {2619 thing.moveleft = thing.FSM.objectToLeft(thing, other);2620 other.moveleft = !thing.moveleft;2621 return;2622 }2623 // Player landing on top of an enemy2624 if ((thing.star && !other.nostar)2625 || (!thing.FSM.MapScreener.underwater2626 && (!other.deadly && thing.FSM.isThingOnThing(thing, other)))) {2627 // For the sake of typing. Should be optimized during runtime.2628 var player = thing;2629 // Enforces toly (not touching means stop)2630 if (player.FSM.isCharacterAboveEnemy(player, other)) {2631 return;2632 }2633 // A star player just kills the enemy, no matter what2634 if (player.star) {2635 other.nocollide = true;2636 other.death(other, 2);2637 player.FSM.scoreOn(other.scoreStar, other);2638 player.FSM.AudioPlayer.play("Kick");2639 }2640 else {2641 // A non-star player kills the enemy with spawn, and hops2642 player.FSM.setBottom(player, Math.min(player.bottom, other.top + player.FSM.unitsize));2643 player.FSM.TimeHandler.addEvent(player.FSM.jumpEnemy, 0, player, other);2644 other.death(other, player.star ? 2 : 0);2645 player.FSM.addClass(player, "hopping");2646 player.FSM.removeClasses(player, "running skidding jumping one two three");2647 player.hopping = true;2648 if (player.power === 1) {2649 player.FSM.setPlayerSizeSmall(player);2650 }2651 }2652 }2653 else if (!thing.FSM.isCharacterAboveEnemy(thing, other)) {2654 // Player being landed on by an enemy2655 thing.death(thing);2656 }2657 };2658 /**2659 * Collision callback for a character bumping into the bottom of a solid.2660 * Only players cause the solid to jump and be considered "up", though large2661 * players will kill solids that have the breakable flag on. If the solid2662 * does jump and has contents, they emerge.2663 *2664 * @param thing A Brick being touched by other.2665 * @param other A Character touching thing.2666 */2667 FullScreenMario.prototype.collideBottomBrick = function (thing, other) {2668 if (other.solid && !thing.solid) {2669 return thing.FSM.collideBottomBrick(other, thing);2670 }2671 if (thing.up || !other.player) {2672 return;2673 }2674 thing.FSM.AudioPlayer.play("Bump");2675 if (thing.used) {2676 return;2677 }2678 thing.up = other;2679 if (other.power > 1 && thing.breakable && !thing.contents) {2680 thing.FSM.TimeHandler.addEvent(thing.FSM.killBrick, 2, thing, other);2681 return;2682 }2683 thing.FSM.animateSolidBump(thing);2684 if (thing.contents) {2685 thing.FSM.TimeHandler.addEvent(function () {2686 thing.FSM.animateSolidContents(thing, other);2687 if (thing.contents !== "Coin") {2688 thing.FSM.animateBlockBecomesUsed(thing);2689 }2690 else {2691 if (thing.lastcoin) {2692 thing.FSM.animateBlockBecomesUsed(thing);2693 }2694 else {2695 thing.FSM.TimeHandler.addEvent(function () {2696 thing.lastcoin = true;2697 }, 245);2698 }2699 }2700 }, 7);2701 }2702 };2703 /**2704 * Collision callback for a Player hitting the bottom of a Block. Unused2705 * Blocks have their contents emerge (by default a Coin), while used Blocks2706 * just have a small bump noise played.2707 *2708 * @param thing A Block being touched by other.2709 * @param other A Player touching thing.2710 */2711 FullScreenMario.prototype.collideBottomBlock = function (thing, other) {2712 if (other.solid && !thing.solid) {2713 return thing.FSM.collideBottomBlock(other, thing);2714 }2715 if (thing.up || !other.player) {2716 return;2717 }2718 if (thing.used) {2719 thing.FSM.AudioPlayer.play("Bump");2720 return;2721 }2722 thing.used = true;2723 thing.hidden = false;2724 thing.up = other;2725 thing.FSM.animateSolidBump(thing);2726 thing.FSM.removeClass(thing, "hidden");2727 thing.FSM.switchClass(thing, "unused", "used");2728 thing.FSM.TimeHandler.addEvent(thing.FSM.animateSolidContents, 7, thing, other);2729 };2730 /**2731 * Collision callback for Vines. A player becomes "attached" to the Vine2732 * and starts climbing it, with movement set to movePlayerVine.2733 *2734 * @param thing A Player touching other.2735 * @param other A Solid being touched by thing.2736 */2737 FullScreenMario.prototype.collideVine = function (thing, other) {2738 if (!thing.player || thing.attachedSolid || thing.climbing) {2739 return;2740 }2741 if (thing.bottom > other.bottom + thing.FSM.unitsize * 2) {2742 return;2743 }2744 other.attachedCharacter = thing;2745 thing.attachedSolid = other;2746 thing.nofall = true;2747 thing.checkOverlaps = false;2748 thing.resting = undefined;2749 // To the left of the vine2750 if (thing.right < other.right) {2751 thing.lookleft = false;2752 thing.moveleft = false;2753 thing.attachedDirection = -1;2754 thing.FSM.unflipHoriz(thing);2755 }2756 else {2757 // To the right of the vine2758 thing.lookleft = true;2759 thing.moveleft = true;2760 thing.attachedDirection = 1;2761 thing.FSM.flipHoriz(thing);2762 }2763 thing.FSM.animateCharacterPauseVelocity(thing);2764 thing.FSM.addClass(thing, "climbing");2765 thing.FSM.removeClasses(thing, "running", "jumping", "skidding");2766 thing.FSM.TimeHandler.cancelClassCycle(thing, "running");2767 thing.FSM.TimeHandler.addClassCycle(thing, ["one", "two"], "climbing", 0);2768 thing.attachedLeft = !thing.FSM.objectToLeft(thing, other);2769 thing.attachedOff = thing.attachedLeft ? 1 : -1;2770 thing.movement = thing.FSM.movePlayerVine;2771 };2772 /**2773 * Collision callback for a character hitting a Springboard. This acts as a2774 * normal solid to non-players, and only acts as a spring if a Player is2775 * above it and moving down.2776 *2777 * @param thing A Character touching other.2778 * @param other A Springboard being touched by thing.2779 */2780 FullScreenMario.prototype.collideSpringboard = function (thing, other) {2781 if (thing.player && thing.yvel >= 0 && !other.tension2782 && thing.FSM.isCharacterOnSolid(thing, other)) {2783 other.tension = other.tensionSave = Math.max(thing.yvel * 0.77, thing.FSM.unitsize);2784 thing.movement = thing.FSM.movePlayerSpringboardDown;2785 thing.spring = other;2786 thing.xvel /= 2.8;2787 }2788 else {2789 thing.FSM.collideCharacterSolid(thing, other);2790 }2791 };2792 /**2793 * Collision callback for a character hitting a WaterBlocker on the top of2794 * an underwater area. It simply stops them from moving up.2795 *2796 * @param thing A Character touching other.2797 * @param other A WaterBlocker being touched by thing.2798 */2799 FullScreenMario.prototype.collideWaterBlocker = function (thing, other) {2800 thing.FSM.collideCharacterSolid(thing, other);2801 };2802 /**2803 * Collision callback for the DetectCollision on a flagpole at the end of an2804 * EndOutsideCastle. The Flagpole cutscene is started.2805 *2806 * @param thing A Player touching other.2807 * @param other A DetectCollision being touched by thing.2808 */2809 FullScreenMario.prototype.collideFlagpole = function (thing, other) {2810 if (thing.bottom > other.bottom) {2811 return;2812 }2813 thing.FSM.ScenePlayer.startCutscene("Flagpole", {2814 "player": thing,2815 "collider": other2816 });2817 };2818 /**2819 * Collision callback for a Player hitting a CastleAxe. A player and2820 * screen are paused for 140 steps (other callbacks should be animating2821 * the custcene).2822 *2823 * @param thing A Player touching other.2824 * @param other A CastleAxe being touched by thing.2825 */2826 FullScreenMario.prototype.collideCastleAxe = function (thing, other) {2827 if (!thing.FSM.MathDecider.compute("canPlayerTouchCastleAxe", thing, other)) {2828 return;2829 }2830 thing.FSM.ScenePlayer.startCutscene("BowserVictory", {2831 "player": thing,2832 "axe": other2833 });2834 };2835 /**2836 * Collision callback for a player hitting the DetectCollision placed next2837 * a CastleDoor in EndOutsideCastle. Things and the current time are added2838 * to cutscene settings. Infinite time goes directly to the Fireworks2839 * routine, while having non-infinite time goes to the Countdown routine.2840 *2841 * @param thing A Player touching other.2842 * @param other A DetectCollision being touched by thing.2843 */2844 FullScreenMario.prototype.collideCastleDoor = function (thing, other) {2845 thing.FSM.killNormal(thing);2846 if (!thing.player) {2847 return;2848 }2849 var time = thing.FSM.ItemsHolder.getItem("time");2850 thing.FSM.ScenePlayer.addCutsceneSetting("player", thing);2851 thing.FSM.ScenePlayer.addCutsceneSetting("detector", other);2852 thing.FSM.ScenePlayer.addCutsceneSetting("time", time);2853 if (time === Infinity) {2854 thing.FSM.ScenePlayer.playRoutine("Fireworks");2855 }2856 else {2857 thing.FSM.ScenePlayer.playRoutine("Countdown");2858 }2859 };2860 /**2861 * Collision callback for a player reaching a castle NPC. Things and2862 * the NPC's keys are added to cutscene settings, and the Dialog routine2863 * is played.2864 *2865 * @param thing A Player touching other.2866 * @param other A DetectCollision being touched by thing.2867 */2868 FullScreenMario.prototype.collideCastleNPC = function (thing, other) {2869 var keys = other.collection.npc.collectionKeys;2870 thing.FSM.ScenePlayer.addCutsceneSetting("keys", keys);2871 thing.FSM.ScenePlayer.addCutsceneSetting("player", thing);2872 thing.FSM.ScenePlayer.addCutsceneSetting("detector", other);2873 thing.FSM.ScenePlayer.playRoutine("Dialog");2874 };2875 /**2876 * Collision callback for a player hitting the transportation Platform in2877 * cloud worlds. A player collides with it as normal for solids, but if2878 * a Player is then resting on it, it becomes a normal moving platform2879 * with only horizontal momentum.2880 *2881 * @param thing A Player touching other.2882 * @param other A Solid being touched by thing.2883 */2884 FullScreenMario.prototype.collideTransport = function (thing, other) {2885 if (!thing.player) {2886 return;2887 }2888 thing.FSM.collideCharacterSolid(thing, other);2889 if (thing.resting !== other) {2890 return;2891 }2892 other.xvel = thing.FSM.unitsize / 2;2893 other.movement = thing.FSM.movePlatform;2894 other.collide = thing.FSM.collideCharacterSolid;2895 };2896 /**2897 * General collision callback for DetectCollision Things. The real activate2898 * callback is only hit if the Thing is a player; otherwise, an optional2899 * activateFail may be activated. The DetectCollision is then killed if it2900 * doesn't have the noActivateDeath flag.2901 *2902 * @param thing A Character touching other.2903 * @param other A DetectCollision being touched by thing.2904 */2905 FullScreenMario.prototype.collideDetector = function (thing, other) {2906 if (!thing.player) {2907 if (other.activateFail) {2908 other.activateFail(thing);2909 }2910 return;2911 }2912 other.activate(thing, other);2913 if (!other.noActivateDeath) {2914 thing.FSM.killNormal(other);2915 }2916 };2917 /**2918 * Collision callback for level transports (any Thing with a .transport2919 * attribute). Depending on the transport, either the map or location are2920 * shifted to it.2921 *2922 * @param thing A Player touching other.2923 * @param other A Solid being touched by thing.2924 */2925 FullScreenMario.prototype.collideLevelTransport = function (thing, other) {2926 if (!thing.player) {2927 return;2928 }2929 var transport = other.transport;2930 if (typeof transport === "undefined") {2931 throw new Error("No transport given to collideLevelTransport");2932 }2933 if (transport.constructor === String) {2934 thing.FSM.setLocation(transport);2935 }2936 else if (typeof transport.map !== "undefined") {2937 if (typeof transport.location !== "undefined") {2938 thing.FSM.setMap(transport.map, transport.location);2939 }2940 else {2941 thing.FSM.setMap(transport.map);2942 }2943 }2944 else if (typeof transport.location !== "undefined") {2945 thing.FSM.setLocation(transport.location);2946 }2947 else {2948 throw new Error("Unknown transport type:" + transport);2949 }2950 };2951 /* Movement functions2952 */2953 /**2954 * Base, generic movement Function for simple characters. The Thing moves2955 * at a constant rate in either the x or y direction, and switches direction2956 * only if directed by the engine (e.g. when it hits a Solid)2957 *2958 * @param thing A Character that should move.2959 * @remarks thing.speed is the only required member attribute; .direction2960 * and .moveleft should be set by the game engine.2961 */2962 FullScreenMario.prototype.moveSimple = function (thing) {2963 // If the thing is looking away from the intended direction, flip it2964 if (thing.direction !== (thing.moveleft ? 1 : 0)) {2965 // thing.moveleft is truthy: it should now be looking to the right2966 if (thing.moveleft) {2967 thing.xvel = -thing.speed;2968 if (!thing.noflip) {2969 thing.FSM.unflipHoriz(thing);2970 }2971 }2972 else {2973 // thing.moveleft is falsy: it should now be looking to the left2974 thing.xvel = thing.speed;2975 if (!thing.noflip) {2976 thing.FSM.flipHoriz(thing);2977 }2978 }2979 thing.direction = thing.moveleft ? 1 : 0;2980 }2981 };2982 /**2983 * Extension of the moveSimple movement Function for Things that shouldn't2984 * fall off the edge of their resting blocks2985 *2986 * @param thing A Character that should move.2987 */2988 FullScreenMario.prototype.moveSmart = function (thing) {2989 // Start off by calling moveSimple for normal movement2990 thing.FSM.moveSimple(thing);2991 // If this isn't resting, it's the same as moveSimple2992 if (thing.yvel !== 0) {2993 return;2994 }2995 if (!thing.resting || !thing.FSM.isCharacterOnResting(thing, thing.resting)) {2996 if (thing.moveleft) {2997 thing.FSM.shiftHoriz(thing, thing.FSM.unitsize, true);2998 }2999 else {3000 thing.FSM.shiftHoriz(thing, -thing.FSM.unitsize, true);3001 }3002 thing.moveleft = !thing.moveleft;3003 }3004 };3005 /**3006 * Extension of the moveSimple movement Function for Things that should3007 * jump whenever they start resting.3008 *3009 * @param thing A Character that should move.3010 * @remarks thing.jumpheight is required to know how high to jump.3011 */3012 FullScreenMario.prototype.moveJumping = function (thing) {3013 // Start off by calling moveSimple for normal movement3014 thing.FSM.moveSimple(thing);3015 // If .resting, jump!3016 if (thing.resting) {3017 thing.yvel = -Math.abs(thing.jumpheight);3018 thing.resting = undefined;3019 }3020 };3021 /**3022 * Movement Function for Characters that slide back and forth, such as3023 * HammerBros and Lakitus.3024 *3025 * @param thing A HammerBro or Lakitu that should move.3026 * @remarks thing.counter must be set elsewhere, such as during spawning.3027 */3028 FullScreenMario.prototype.movePacing = function (thing) {3029 thing.counter += .007;3030 thing.xvel = Math.sin(Math.PI * thing.counter) / 2.1;3031 };3032 /**3033 * Movement Function for HammerBros. They movePacing, look towards the3034 * player, and have the nocollidesolid flag if they're jumping up or3035 * intentionally falling through a solid.3036 *3037 * @param thing A HammerBro that should move.3038 */3039 FullScreenMario.prototype.moveHammerBro = function (thing) {3040 thing.FSM.movePacing(thing);3041 thing.FSM.lookTowardsPlayer(thing);3042 thing.nocollidesolid = thing.yvel < 0 || thing.falling;3043 };3044 /**3045 * Movement Function for Bowser. Bowser always faces a Player and3046 * movePaces if he's to the right of a Player, or moves to the right if3047 * he's to the left.3048 *3049 * @param thing A Bowser that should move.3050 */3051 FullScreenMario.prototype.moveBowser = function (thing) {3052 // Facing to the right3053 if (thing.flipHoriz) {3054 // To the left of player: walk to the right3055 if (thing.FSM.objectToLeft(thing, thing.FSM.player)) {3056 thing.FSM.moveSimple(thing);3057 }3058 else {3059 // To the right of player: look to the left and movePacing as normal3060 thing.moveleft = thing.lookleft = true;3061 thing.FSM.unflipHoriz(thing);3062 thing.FSM.movePacing(thing);3063 }3064 }3065 else {3066 // Facing to the left3067 // To the left of player: look and walk to the right3068 if (thing.FSM.objectToLeft(thing, thing.FSM.player)) {3069 thing.moveleft = thing.lookleft = false;3070 thing.FSM.flipHoriz(thing);3071 thing.FSM.moveSimple(thing);3072 }3073 else {3074 // To the right of a Player: movePacing as normal3075 thing.FSM.movePacing(thing);3076 }3077 }3078 };3079 /**3080 * Movement Function for Bowser's spewed fire. It has a ylev stored from3081 * creation that will tell it when to stop changing its vertical3082 * velocity from this Function; otherwise, it shifts its vertical3083 * position to move to the ylev.3084 *3085 * @param thing A BowserFire that should move.3086 */3087 FullScreenMario.prototype.moveBowserFire = function (thing) {3088 if (Math.round(thing.bottom) === Math.round(thing.ylev)) {3089 thing.movement = undefined;3090 return;3091 }3092 thing.FSM.shiftVert(thing, Math.min(Math.max(0, thing.ylev - thing.bottom), thing.FSM.unitsize));3093 };3094 /**3095 * Movement function for Things that float up and down (vertically).3096 * If the Thing has reached thing.begin or thing.end, it gradually switches3097 * thing.yvel3098 *3099 * @param thing A Thing that should move.3100 * @remarks thing.maxvel is used as the maximum absolute speed vertically3101 * @remarks thing.begin and thing.end are used as the vertical endpoints;3102 * .begin is the bottom and .end is the top (since begin <= end)3103 */3104 FullScreenMario.prototype.moveFloating = function (thing) {3105 // If above the endpoint:3106 if (thing.top <= thing.end) {3107 thing.yvel = Math.min(thing.yvel + thing.FSM.unitsize / 64, thing.maxvel);3108 }3109 else if (thing.bottom >= thing.begin) {3110 // If below the endpoint:3111 thing.yvel = Math.max(thing.yvel - thing.FSM.unitsize / 64, -thing.maxvel);3112 }3113 // Deal with velocities and whether a Player is resting on this3114 thing.FSM.movePlatform(thing);3115 };3116 /**3117 * Actual movement Function for Things that float sideways (horizontally).3118 * If the Thing has reached thing.begin or thing.end, it gradually switches3119 * thing.xvel.3120 *3121 * @param thing A Thing that should move.3122 * @remarks thing.maxvel is used as the maximum absolute speed horizontally3123 * @remarks thing.begin and thing.end are used as the horizontal endpoints;3124 * .begin is the left and .end is the right (since begin <= end)3125 */3126 FullScreenMario.prototype.moveSliding = function (thing) {3127 // If to the left of the endpoint:3128 if (thing.FSM.MapScreener.left + thing.left <= thing.begin) {3129 thing.xvel = Math.min(thing.xvel + thing.FSM.unitsize / 64, thing.maxvel);3130 }3131 else if (thing.FSM.MapScreener.left + thing.right > thing.end) {3132 // If to the right of the endpoint:3133 thing.xvel = Math.max(thing.xvel - thing.FSM.unitsize / 64, -thing.maxvel);3134 }3135 // Deal with velocities and whether a Player is resting on this3136 thing.FSM.movePlatform(thing);3137 };3138 /**3139 * Ensures thing.begin <= thing.end (so there won't be glitches pertaining3140 * to them in functions like moveFloating and moveSliding3141 *3142 * @param thing A spawning Thing that needs its movement endpoings set.3143 */3144 FullScreenMario.prototype.setMovementEndpoints = function (thing) {3145 if (thing.begin > thing.end) {3146 var temp = thing.begin;3147 thing.begin = thing.end;3148 thing.end = temp;3149 }3150 thing.begin *= thing.FSM.unitsize;3151 thing.end *= thing.FSM.unitsize;3152 };3153 /**3154 * General movement Function for Platforms. Moves a Platform by its3155 * velocities, and checks for whether a Thing is resting on it (if so,3156 * the Thing is accordingly).3157 *3158 * @param thing A Platform that should move.3159 */3160 FullScreenMario.prototype.movePlatform = function (thing) {3161 thing.FSM.shiftHoriz(thing, thing.xvel);3162 thing.FSM.shiftVert(thing, thing.yvel);3163 // If a Player is resting on this and this is alive, move a Player3164 if (thing === thing.FSM.player.resting && thing.FSM.player.alive) {3165 thing.FSM.setBottom(thing.FSM.player, thing.top);3166 thing.FSM.shiftHoriz(thing.FSM.player, thing.xvel);3167 // If a Player is too far to the right or left, stop that overlap3168 if (thing.FSM.player.right > thing.FSM.MapScreener.width) {3169 thing.FSM.setRight(thing.FSM.player, thing.FSM.MapScreener.width);3170 }3171 else if (thing.FSM.player.left < 0) {3172 thing.FSM.setLeft(thing.FSM.player, 0);3173 }3174 }3175 };3176 /**3177 * Movement Function for platforms that are in a PlatformGenerator. They3178 * have the typical movePlatform applied to them, but if they reach the3179 * bottom or top of the screen, they are shifted to the opposite side.3180 *3181 * @param thing A Platform that should move.3182 */3183 FullScreenMario.prototype.movePlatformSpawn = function (thing) {3184 if (thing.bottom < 0) {3185 thing.FSM.setTop(thing, thing.FSM.MapScreener.bottomPlatformMax);3186 }3187 else if (thing.top > thing.FSM.MapScreener.bottomPlatformMax) {3188 thing.FSM.setBottom(thing, 0);3189 }3190 else {3191 thing.FSM.movePlatform(thing);3192 return;3193 }3194 if (thing.FSM.player3195 && thing.FSM.player.resting === thing) {3196 thing.FSM.player.resting = undefined;3197 }3198 };3199 /**3200 * Movement Function for Platforms that fall whenever rested upon by a3201 * player. Being rested upon means the Platform falls; when it reaches a3202 * terminal velocity, it switches to moveFreeFalling forever.3203 *3204 * @param thing A Platform that should move.3205 */3206 FullScreenMario.prototype.moveFalling = function (thing) {3207 // If a Player isn't resting on this thing (any more?), ignore it3208 if (thing.FSM.player.resting !== thing) {3209 // Since a Player might have been on this thing but isn't anymore, 3210 // set the yvel to 0 just in case3211 thing.yvel = 0;3212 return;3213 }3214 // Since a Player is on this thing, start falling more3215 thing.FSM.shiftVert(thing, thing.yvel += thing.FSM.unitsize / 8);3216 thing.FSM.setBottom(thing.FSM.player, thing.top);3217 // After a velocity threshold, start always falling3218 if (thing.yvel >= (thing.fallThresholdStart || thing.FSM.unitsize * 2.8)) {3219 thing.freefall = true;3220 thing.movement = thing.FSM.moveFreeFalling;3221 }3222 };3223 /**3224 * Movement Function for Platforms that have reached terminal velocity in3225 * moveFalling and are now destined to die. The Platform will continue to3226 * accelerate towards certain death until another velocity threshold,3227 * and then switches to movePlatform to remain at that rate.3228 *3229 * @param thing A Platform that should move.3230 */3231 FullScreenMario.prototype.moveFreeFalling = function (thing) {3232 // Accelerate downwards, increasing the thing's y-velocity3233 thing.yvel += thing.acceleration || thing.FSM.unitsize / 16;3234 thing.FSM.shiftVert(thing, thing.yvel);3235 // After a velocity threshold, stop accelerating3236 if (thing.yvel >= (thing.fallThresholdEnd || thing.FSM.unitsize * 2.1)) {3237 thing.movement = thing.FSM.movePlatform;3238 }3239 };3240 /**3241 * Movement Function for Platforms that are a part of a scale. Nothing3242 * happens if a Platform isn't being rested and doesn't have a y-velocity.3243 * Being rested upon means the y-velocity increases, and not being rested3244 * means the y-velocity decreases: either moves the corresponding Platform3245 * "partner" in the other vertical direction. When the Platform is too far3246 * down (visually has no string left), they both fall.3247 *3248 * @param thing A Platform that should move.3249 */3250 FullScreenMario.prototype.movePlatformScale = function (thing) {3251 // If a Player is resting on this, fall hard3252 if (thing.FSM.player.resting === thing) {3253 thing.yvel += thing.FSM.unitsize / 16;3254 }3255 else if (thing.yvel > 0) {3256 // If this still has velocity from a Player, stop or fall less3257 if (!thing.partners) {3258 thing.yvel = 0;3259 }3260 else {3261 thing.yvel = Math.max(thing.yvel - thing.FSM.unitsize / 16, 0);3262 }3263 }3264 else {3265 // Not being rested upon or having a yvel means nothing happens3266 return;3267 }3268 thing.tension += thing.yvel;3269 thing.FSM.shiftVert(thing, thing.yvel);3270 // The rest of the logic is for the platform's partner(s)3271 if (!thing.partners) {3272 return;3273 }3274 thing.partners.partnerPlatform.tension -= thing.yvel;3275 // If the partner has fallen off, everybody falls!3276 if (thing.partners.partnerPlatform.tension <= 0) {3277 thing.FSM.scoreOn(1000, thing);3278 thing.partners.partnerPlatform.yvel = thing.FSM.unitsize / 2;3279 thing.collide = thing.partners.partnerPlatform.collide = (thing.FSM.collideCharacterSolid);3280 thing.movement = thing.partners.partnerPlatform.movement = (thing.FSM.moveFreeFalling);3281 }3282 // The partner has yvel equal and opposite to this platform's3283 thing.FSM.shiftVert(thing.partners.partnerPlatform, -thing.yvel);3284 // This platform's string grows with its yvel3285 thing.FSM.setHeight(thing.partners.ownString, thing.partners.ownString.height + thing.yvel / thing.FSM.unitsize);3286 // The partner's string shrinks while this platform's string grows3287 thing.FSM.setHeight(thing.partners.partnerString, Math.max(thing.partners.partnerString.height - (thing.yvel / thing.FSM.unitsize), 0));3288 };3289 /**3290 * Movement Function for Vines. They are constantly growing upward, until3291 * some trigger (generally from animateEmergeVine) sets movement to3292 * undefined. If there is an attached Thing, it is moved up at the same rate3293 * as the Vine.3294 *3295 * @param thing A Vine that should move.3296 */3297 FullScreenMario.prototype.moveVine = function (thing) {3298 thing.FSM.increaseHeight(thing, thing.speed);3299 thing.FSM.updateSize(thing);3300 if (thing.attachedSolid) {3301 thing.FSM.setBottom(thing, thing.attachedSolid.top);3302 }3303 if (thing.attachedCharacter) {3304 thing.FSM.shiftVert(thing.attachedCharacter, -thing.speed);3305 }3306 };3307 /**3308 * Movement Function for Springboards that are "pushing up" during or after3309 * being hit by a player. The Springboard changes its height based on its3310 * tension. If a Player is still on it, then a Player is given extra3311 * vertical velocity and taken off.3312 *3313 * @param thing A Springboard that should move.3314 */3315 FullScreenMario.prototype.moveSpringboardUp = function (thing) {3316 var player = thing.FSM.player;3317 thing.FSM.reduceHeight(thing, -thing.tension, true);3318 thing.tension *= 2;3319 // If the spring height is past the normal, it's done moving3320 if (thing.height > thing.heightNormal) {3321 thing.FSM.reduceHeight(thing, (thing.height - thing.heightNormal) * thing.FSM.unitsize);3322 if (thing === player.spring) {3323 player.yvel = thing.FSM.MathDecider.compute("springboardYvelUp", thing);3324 player.resting = player.spring = undefined;3325 player.movement = thing.FSM.movePlayer;3326 }3327 thing.tension = 0;3328 thing.movement = undefined;3329 }3330 else {3331 thing.FSM.setBottom(player, thing.top);3332 }3333 if (thing === player.spring) {3334 if (!thing.FSM.isThingTouchingThing(player, thing)) {3335 player.spring = undefined;3336 player.movement = FullScreenMario.prototype.movePlayer;3337 }3338 }3339 };3340 /**3341 * Movement Function for Shells. This actually does nothing for moving3342 * Shells (since they only interact unusually on collision). For Shells with3343 * no x-velocity, a counting variable is increased. Once it reaches 350, the3344 * shell is "peeking" visually; when it reaches 490, the Shell spawns back3345 * into its original spawner (typically Koopa or Beetle).3346 *3347 * @param thing A Shell that should move.3348 */3349 FullScreenMario.prototype.moveShell = function (thing) {3350 if (thing.xvel !== 0) {3351 return;3352 }3353 thing.counting += 1;3354 if (thing.counting === 350) {3355 thing.peeking = 1;3356 thing.height += thing.FSM.unitsize / 8;3357 thing.FSM.addClass(thing, "peeking");3358 thing.FSM.updateSize(thing);3359 }3360 else if (thing.counting === 455) {3361 thing.peeking = 2;3362 }3363 else if (thing.counting === 490) {3364 thing.spawnSettings = {3365 "smart": thing.smart3366 };3367 thing.FSM.killSpawn(thing);3368 }3369 };3370 /**3371 * Movement Function for Piranhas. These constantly change their height3372 * except when they reach 0 or full height (alternating direction), at which3373 * point they switch to movePiranhaLatent to wait to move in the opposite3374 * direction.3375 *3376 * @param thing A Piranha that should move.3377 */3378 FullScreenMario.prototype.movePiranha = function (thing) {3379 var bottom = thing.bottom, height = thing.height + thing.direction, atEnd = false;3380 if (thing.resting && !thing.FSM.isThingAlive(thing.resting)) {3381 bottom = thing.constructor.prototype.height * thing.FSM.unitsize + thing.top;3382 height = Infinity;3383 thing.resting = undefined;3384 }3385 if (height <= 0) {3386 height = thing.height = 0;3387 atEnd = true;3388 }3389 else if (height >= thing.constructor.prototype.height) {3390 height = thing.height = thing.constructor.prototype.height;3391 atEnd = true;3392 }3393 thing.FSM.setHeight(thing, height);3394 thing.FSM.setBottom(thing, bottom);3395 // Canvas height should be manually reset, as PixelRendr will otherwise3396 // store the height as the initial small height from spawnPiranha...3397 thing.canvas.height = height * thing.FSM.unitsize;3398 thing.FSM.PixelDrawer.setThingSprite(thing);3399 if (atEnd) {3400 thing.counter = 0;3401 thing.movement = thing.FSM.movePiranhaLatent;3402 }3403 };3404 /**3405 * Movement Function for Piranhas that are not changing size. They wait3406 * until a counter reaches a point (and then, if their height is 0, for the3407 * player to go away) to switch back to movePiranha.3408 *3409 * @param thing A Piranha that should move.3410 */3411 FullScreenMario.prototype.movePiranhaLatent = function (thing) {3412 var playerX = thing.FSM.getMidX(thing.FSM.player);3413 if (thing.counter >= thing.countermax3414 && (thing.height > 03415 || playerX < thing.left - thing.FSM.unitsize * 83416 || playerX > thing.right + thing.FSM.unitsize * 8)) {3417 thing.movement = undefined;3418 thing.direction *= -1;3419 thing.FSM.TimeHandler.addEvent(function () {3420 thing.movement = thing.FSM.movePiranha;3421 }, 7);3422 }3423 else {3424 thing.counter += 1;3425 }3426 };3427 /**3428 * Movement Function for the Bubbles that come out of a player's mouth3429 * underwater. They die when they reach a top threshold of unitsize * 16.3430 *3431 * @param thing A Thing that should move.3432 */3433 FullScreenMario.prototype.moveBubble = function (thing) {3434 if (thing.top < (thing.FSM.MapScreener.top + thing.FSM.unitsize * 16)) {3435 thing.FSM.killNormal(thing);3436 }3437 };3438 /**3439 * Movement Function for typical CheepCheeps, which are underwater. They3440 * move according to their native velocities except that they cannot travel3441 * above the unitsize * 16 top threshold.3442 *3443 * @param thing A CheepCheep that should move.3444 */3445 FullScreenMario.prototype.moveCheepCheep = function (thing) {3446 if (thing.top < thing.FSM.unitsize * 16) {3447 thing.FSM.setTop(thing, thing.FSM.unitsize * 16);3448 thing.yvel *= -1;3449 }3450 };3451 /**3452 * Movement Function for flying CheepCheeps, like in bridge areas. They3453 * lose a movement Function (and therefore just fall) at a unitsize * 28 top3454 * threshold.3455 *3456 * @param thing A CheepCheep that should move.3457 */3458 FullScreenMario.prototype.moveCheepCheepFlying = function (thing) {3459 if (thing.top < thing.FSM.unitsize * 28) {3460 thing.movement = undefined;3461 thing.nofall = false;3462 }3463 };3464 /**3465 * Movement Function for Bloopers. These switch between "squeezing" (moving3466 * down) and moving up ("unsqueezing"). They always try to unsqueeze if the3467 * player is above them.3468 *3469 * @param thing A Blooper that should move.3470 */3471 FullScreenMario.prototype.moveBlooper = function (thing) {3472 // If a Player is dead, just drift aimlessly3473 if (!thing.FSM.isThingAlive(thing.FSM.player)) {3474 thing.xvel = thing.FSM.unitsize / -4;3475 thing.yvel = 0;3476 thing.movement = undefined;3477 return;3478 }3479 switch (thing.counter) {3480 case 56:3481 thing.squeeze = 1;3482 thing.counter += 1;3483 break;3484 case 63:3485 thing.FSM.moveBlooperSqueezing(thing);3486 break;3487 default:3488 thing.counter += 1;3489 if (thing.top < thing.FSM.unitsize * 18) {3490 thing.FSM.moveBlooperSqueezing(thing);3491 }3492 break;3493 }3494 if (thing.squeeze) {3495 thing.yvel = Math.max(thing.yvel + .021, .7); // going down3496 }3497 else {3498 thing.yvel = Math.min(thing.yvel - .035, -.7); // going up3499 }3500 if (thing.top > thing.FSM.unitsize * 16) {3501 thing.FSM.shiftVert(thing, thing.yvel, true);3502 }3503 if (!thing.squeeze) {3504 if (thing.FSM.player.left > thing.right + thing.FSM.unitsize * 8) {3505 // Go to the right3506 thing.xvel = Math.min(thing.speed, thing.xvel + thing.FSM.unitsize / 32);3507 }3508 else if (thing.FSM.player.right < thing.left - thing.FSM.unitsize * 8) {3509 // Go to the left3510 thing.xvel = Math.max(-thing.speed, thing.xvel - thing.FSM.unitsize / 32);3511 }3512 }3513 };3514 /**3515 * Additional movement Function for Bloopers that are "squeezing". Squeezing3516 * Bloopers travel downard at a gradual pace until they reach either the3517 * player's bottom or a threshold of unitsize * 90.3518 *3519 * @param thing A Blooper that should move.3520 */3521 FullScreenMario.prototype.moveBlooperSqueezing = function (thing) {3522 if (thing.squeeze !== 2) {3523 thing.squeeze = 2;3524 thing.FSM.addClass(thing, "squeeze");3525 thing.FSM.setHeight(thing, 10, true, true);3526 }3527 if (thing.squeeze < 7) {3528 thing.xvel /= 1.4;3529 }3530 else if (thing.squeeze === 7) {3531 thing.xvel = 0;3532 }3533 thing.squeeze += 1;3534 if (thing.top > thing.FSM.player.bottom || thing.bottom > thing.FSM.unitsize * 91) {3535 thing.FSM.animateBlooperUnsqueezing(thing);3536 }3537 };3538 /**3539 * Movement Function for Podoboos that is only used when they are falling.3540 * Podoboo animations trigger this when they reach a certain height, and3541 * use this to determine when they should stop accelerating downward, which3542 * is their starting location.3543 *3544 * @param thing A Podoboo that should move.3545 */3546 FullScreenMario.prototype.movePodobooFalling = function (thing) {3547 if (thing.top >= thing.starty) {3548 thing.yvel = 0;3549 thing.movement = undefined;3550 thing.FSM.unflipVert(thing);3551 thing.FSM.setTop(thing, thing.starty);3552 return;3553 }3554 if (thing.yvel >= thing.speed) {3555 thing.yvel = thing.speed;3556 return;3557 }3558 if (!thing.flipVert && thing.yvel > 0) {3559 thing.FSM.flipVert(thing);3560 }3561 thing.yvel += thing.gravity;3562 };3563 /**3564 * Movement Function for Lakitus that have finished their moveLakituInitial3565 * run. This is similar to movePacing in that it makes the Lakitu pace to3566 * left and right of a Player, and moves with a Player rather than the3567 * scrolling window.3568 *3569 * @param thing A Lakitu that should move.3570 */3571 FullScreenMario.prototype.moveLakitu = function (thing) {3572 var player = thing.FSM.player;3573 // If a Player is moving quickly to the right, move in front and stay there3574 if (player.xvel > thing.FSM.unitsize / 83575 && player.left > thing.FSM.MapScreener.width / 2) {3576 if (thing.left < player.right + thing.FSM.unitsize * 16) {3577 // slide to xloc3578 thing.FSM.slideToX(thing, player.right + player.xvel + thing.FSM.unitsize * 32, player.maxspeed * 1.4);3579 thing.counter = 0;3580 }3581 }3582 else {3583 thing.counter += .007;3584 thing.FSM.slideToX(thing, player.left + player.xvel + Math.sin(Math.PI * thing.counter) * 117, player.maxspeed * .7);3585 }3586 };3587 /**3588 * Initial entry movement Function for Lakitus. They enter by sliding across3589 * the top of the screen until they reach a Player, and then switch to3590 * their standard moveLakitu movement.3591 *3592 * @param thing A Lakitu that should move.3593 */3594 FullScreenMario.prototype.moveLakituInitial = function (thing) {3595 if (thing.right < thing.FSM.player.left) {3596 thing.counter = 0;3597 thing.movement = thing.FSM.moveLakitu;3598 thing.movement(thing);3599 return;3600 }3601 thing.FSM.shiftHoriz(thing, -thing.FSM.unitsize);3602 };3603 /**3604 * Alternate movement Function for Lakitus. This is used when a Player3605 * reaches the ending flagpole in a level and the Lakitu just flies to the3606 * left.3607 *3608 * @param thing A Lakitu that should move.3609 */3610 FullScreenMario.prototype.moveLakituFleeing = function (thing) {3611 thing.FSM.shiftHoriz(thing, -thing.FSM.unitsize);3612 };3613 /**3614 * Movement Function for Coins that have been animated. They move based on3615 * their yvel, and if they have a parent, die when they go below the parent.3616 *3617 * @param thing A Coin that should move up.3618 * @param parent A parent Solid spawning thing.3619 */3620 FullScreenMario.prototype.moveCoinEmerge = function (thing, parent) {3621 thing.FSM.shiftVert(thing, thing.yvel);3622 if (parent && thing.bottom >= thing.blockparent.bottom) {3623 thing.FSM.killNormal(thing);3624 }3625 };3626 /**3627 * Movement Function for a Player. It reacts to almost all actions that3628 * to be done, but is horribly written so that is all the documentation you3629 * get here. Sorry! Sections are labeled on the inside.3630 *3631 * @param thing A player that should move.3632 */3633 FullScreenMario.prototype.movePlayer = function (thing) {3634 // Not jumping3635 if (!thing.keys.up) {3636 thing.keys.jump = false;3637 }3638 else if (3639 // Jumping3640 thing.keys.jump3641 && (thing.yvel <= 0 || thing.FSM.MapScreener.underwater)) {3642 if (thing.FSM.MapScreener.underwater) {3643 thing.FSM.animatePlayerPaddling(thing);3644 thing.FSM.removeClass(thing, "running");3645 }3646 if (thing.resting) {3647 if (thing.resting.xvel) {3648 thing.xvel += thing.resting.xvel;3649 }3650 thing.resting = undefined;3651 }3652 else {3653 // Jumping, not resting3654 if (!thing.jumping && !thing.FSM.MapScreener.underwater) {3655 thing.FSM.switchClass(thing, "running skidding", "jumping");3656 }3657 thing.jumping = true;3658 if (thing.power > 1 && thing.crouching) {3659 thing.FSM.removeClass(thing, "jumping");3660 }3661 }3662 if (!thing.FSM.MapScreener.underwater) {3663 thing.keys.jumplev += 1;3664 thing.FSM.MathDecider.compute("decreasePlayerJumpingYvel", thing);3665 }3666 }3667 // Crouching3668 if (thing.keys.crouch && !thing.crouching && thing.resting) {3669 if (thing.power > 1) {3670 thing.crouching = true;3671 thing.FSM.removeClass(thing, "running");3672 thing.FSM.addClass(thing, "crouching");3673 thing.FSM.setHeight(thing, 11, true, true);3674 thing.height = 11;3675 thing.tolyOld = thing.toly;3676 thing.toly = thing.FSM.unitsize * 4;3677 thing.FSM.updateBottom(thing, 0);3678 thing.FSM.updateSize(thing);3679 }3680 // Pipe movement3681 if (thing.resting.actionTop) {3682 thing.FSM.ModAttacher.fireEvent("onPlayerActionTop", thing, thing.resting);3683 thing.resting.actionTop(thing, thing.resting);3684 }3685 }3686 // Running3687 if (thing.FSM.MathDecider.compute("decreasePlayerRunningXvel", thing)) {3688 if (thing.skidding) {3689 thing.FSM.addClass(thing, "skidding");3690 }3691 else {3692 thing.FSM.removeClass(thing, "skidding");3693 }3694 }3695 // Movement mods3696 // Slowing down3697 if (Math.abs(thing.xvel) < .14) {3698 if (thing.running) {3699 thing.running = false;3700 if (thing.power === 1) {3701 thing.FSM.setPlayerSizeSmall(thing);3702 }3703 thing.FSM.removeClasses(thing, "running skidding one two three");3704 thing.FSM.addClass(thing, "still");3705 thing.FSM.TimeHandler.cancelClassCycle(thing, "running");3706 }3707 }3708 else if (!thing.running) {3709 // Not moving slowly3710 thing.running = true;3711 thing.FSM.animatePlayerRunningCycle(thing);3712 if (thing.power === 1) {3713 thing.FSM.setPlayerSizeSmall(thing);3714 }3715 }3716 if (thing.xvel > 0) {3717 thing.xvel = Math.min(thing.xvel, thing.maxspeed);3718 if (thing.moveleft && (thing.resting || thing.FSM.MapScreener.underwater)) {3719 thing.FSM.unflipHoriz(thing);3720 thing.moveleft = false;3721 }3722 }3723 else if (thing.xvel < 0) {3724 thing.xvel = Math.max(thing.xvel, thing.maxspeed * -1);3725 if (!thing.moveleft && (thing.resting || thing.FSM.MapScreener.underwater)) {3726 thing.moveleft = true;3727 thing.FSM.flipHoriz(thing);3728 }3729 }3730 // Resting stops a bunch of other stuff3731 if (thing.resting) {3732 // Hopping3733 if (thing.hopping) {3734 thing.hopping = false;3735 thing.FSM.removeClass(thing, "hopping");3736 if (thing.xvel) {3737 thing.FSM.addClass(thing, "running");3738 }3739 }3740 // Jumping3741 thing.keys.jumplev = thing.yvel = thing.jumpcount = 0;3742 if (thing.jumping) {3743 thing.jumping = false;3744 thing.FSM.removeClass(thing, "jumping");3745 if (thing.power === 1) {3746 thing.FSM.setPlayerSizeSmall(thing);3747 }3748 thing.FSM.addClass(thing, Math.abs(thing.xvel) < .14 ? "still" : "running");3749 }3750 // Paddling3751 if (thing.paddling) {3752 thing.paddling = thing.swimming = false;3753 thing.FSM.TimeHandler.cancelClassCycle(thing, "paddling");3754 thing.FSM.removeClasses(thing, "paddling swim1 swim2");3755 thing.FSM.addClass(thing, "running");3756 }3757 }3758 };3759 /**3760 * Alternate movement Function for players attached to a Vine. They may3761 * climb up or down the Vine, or jump off.3762 *3763 * @param thing A Player that should move.3764 */3765 FullScreenMario.prototype.movePlayerVine = function (thing) {3766 var attachedSolid = thing.attachedSolid, animatedClimbing;3767 if (!attachedSolid) {3768 thing.movement = thing.FSM.movePlayer;3769 return;3770 }3771 if (thing.bottom < thing.attachedSolid.top) {3772 thing.FSM.unattachPlayer(thing, thing.attachedSolid);3773 return;3774 }3775 // Running away from the vine means dropping off3776 if (thing.keys.run !== 0 && thing.keys.run === thing.attachedDirection) {3777 // Leaving to the left3778 if (thing.attachedDirection === -1) {3779 thing.FSM.setRight(thing, attachedSolid.left - thing.FSM.unitsize);3780 }3781 else if (thing.attachedDirection === 1) {3782 // Leaving to the right3783 thing.FSM.setLeft(thing, attachedSolid.right + thing.FSM.unitsize);3784 }3785 thing.FSM.unattachPlayer(thing, attachedSolid);3786 return;3787 }3788 // If a Player is moving up, simply move up3789 if (thing.keys.up) {3790 animatedClimbing = true;3791 thing.FSM.shiftVert(thing, thing.FSM.unitsize / -4);3792 }3793 else if (thing.keys.crouch) {3794 // If the thing is moving down, move down and check for unattachment3795 animatedClimbing = true;3796 thing.FSM.shiftVert(thing, thing.FSM.unitsize / 2);3797 if (thing.top > attachedSolid.bottom) {3798 thing.FSM.unattachPlayer(thing, thing.attachedSolid);3799 }3800 return;3801 }3802 else {3803 animatedClimbing = false;3804 }3805 if (animatedClimbing && !thing.animatedClimbing) {3806 thing.FSM.addClass(thing, "animated");3807 }3808 else if (!animatedClimbing && thing.animatedClimbing) {3809 thing.FSM.removeClass(thing, "animated");3810 }3811 thing.animatedClimbing = animatedClimbing;3812 if (thing.bottom < thing.FSM.MapScreener.top - thing.FSM.unitsize * 4) {3813 thing.FSM.setLocation(thing.attachedSolid.transport);3814 }3815 };3816 /**3817 * Movement Function for players pressing down onto a Springboard. This does3818 * basically nothing except check for when a Player is off the spring or3819 * the spring is fully contracted. The former restores a Player's movement3820 * and the latter clears it (to be restored in moveSpringboardUp).3821 *3822 * @param thing A Player that should move.3823 */3824 FullScreenMario.prototype.movePlayerSpringboardDown = function (thing) {3825 var other = thing.spring;3826 // If a Player has moved off the spring, get outta here3827 if (!thing.FSM.isThingTouchingThing(thing, other)) {3828 thing.movement = thing.FSM.movePlayer;3829 other.movement = thing.FSM.moveSpringboardUp;3830 thing.spring = undefined;3831 return;3832 }3833 // If the spring is fully contracted, go back up3834 if (other.height < thing.FSM.unitsize * 2.53835 || other.tension < thing.FSM.unitsize / 32) {3836 thing.movement = undefined;3837 other.movement = thing.FSM.moveSpringboardUp;3838 return;3839 }3840 // Make sure it's hard to slide off3841 if (thing.left < other.left + thing.FSM.unitsize * 23842 || thing.right > other.right - thing.FSM.unitsize * 2) {3843 thing.xvel /= 1.4;3844 }3845 thing.FSM.reduceHeight(other, other.tension, true);3846 other.tension /= 2;3847 thing.FSM.setBottom(thing, other.top);3848 thing.FSM.updateSize(other);3849 };3850 /* Animations3851 */3852 /**3853 * Animates a solid that has just had its bottom "bumped" by a player. It3854 * moves with a dx that is initially negative (up) and increases (to down).3855 *3856 * @param thing A Solid being bumped.3857 */3858 FullScreenMario.prototype.animateSolidBump = function (thing) {3859 var dx = -3;3860 thing.FSM.TimeHandler.addEventInterval(function (thing) {3861 thing.FSM.shiftVert(thing, dx);3862 dx += .5;3863 if (dx === 3.5) {3864 thing.up = undefined;3865 return true;3866 }3867 return false;3868 }, 1, Infinity, thing);3869 };3870 /**3871 * Animates a Block to switch from unused to used.3872 *3873 * @param thing A Block that is now marked as used.3874 */3875 FullScreenMario.prototype.animateBlockBecomesUsed = function (thing) {3876 thing.used = true;3877 thing.FSM.switchClass(thing, "unused", "used");3878 };3879 /**3880 * Animates a solid to have its contents emerge. A new Thing based on the3881 * contents is spawned directly on top of (visually behind) the solid, and3882 * has its animate callback triggered.3883 *3884 * @param thing A Solid whose contents are coming out.3885 * @param other A Playe triggering the Solid contents.3886 * @remarks If the contents are "Mushroom" and a large player hits the3887 * solid, they turn into "FireFlower".3888 * @remarks For level editors, if thing.contents is falsy, the prototype3889 * is tried (so nothing becomes Coin in Blocks).3890 */3891 FullScreenMario.prototype.animateSolidContents = function (thing, other) {3892 var output;3893 if (other && other.player && other.power > 1 && thing.contents === "Mushroom") {3894 thing.contents = "FireFlower";3895 }3896 output = thing.FSM.addThing(thing.contents || thing.constructor.prototype.contents);3897 thing.FSM.setMidXObj(output, thing);3898 thing.FSM.setTop(output, thing.top);3899 output.blockparent = thing;3900 output.animate(output, thing);3901 return output;3902 };3903 /**3904 * Animates a Brick turning into four rotating shards flying out of it. The3905 * shards have an initial x- and y-velocities, and die after 70 steps.3906 *3907 * @param thing A destroyed Brick to be animated.3908 */3909 FullScreenMario.prototype.animateBrickShards = function (thing) {3910 var unitsize = thing.FSM.unitsize, shard, left, top, i;3911 for (i = 0; i < 4; i += 1) {3912 left = thing.left + Number(i < 2) * thing.width * unitsize - unitsize * 2;3913 top = thing.top + (i % 2) * thing.height * unitsize - unitsize * 2;3914 shard = thing.FSM.addThing("BrickShard", left, top);3915 shard.xvel = shard.speed = unitsize / 2 - unitsize * Number(i > 1);3916 shard.yvel = unitsize * -1.4 + i % 2;3917 thing.FSM.TimeHandler.addEvent(thing.FSM.killNormal, 70, shard);3918 }3919 };3920 /**3921 * Standard animation Function for Things emerging from a solid as contents.3922 * They start at inside the solid, slowly move up, then moveSimple until3923 * they're off the solid, at which point they revert to their normal3924 * movement.3925 *3926 * @param thing A Character emerging from other.3927 * @param other A Solid that thing is emerging from.3928 */3929 FullScreenMario.prototype.animateEmerge = function (thing, other) {3930 thing.nomove = thing.nocollide = thing.nofall = thing.alive = true;3931 thing.FSM.flipHoriz(thing);3932 thing.FSM.AudioPlayer.play("Powerup Appears");3933 thing.FSM.arraySwitch(thing, thing.FSM.GroupHolder.getGroup("Character"), thing.FSM.GroupHolder.getGroup("Scenery"));3934 thing.FSM.TimeHandler.addEventInterval(function () {3935 thing.FSM.shiftVert(thing, thing.FSM.unitsize / -8);3936 // Only stop once the bottom has reached the solid's top3937 if (thing.bottom > other.top) {3938 return false;3939 }3940 thing.FSM.setBottom(thing, other.top);3941 thing.FSM.GroupHolder.switchMemberGroup(thing, "Scenery", "Character");3942 thing.nomove = thing.nocollide = thing.nofall = thing.moveleft = false;3943 if (thing.emergeOut) {3944 thing.emergeOut(thing, other);3945 }3946 // Wait for movement until moveSimple moves this off the solid3947 if (thing.movement) {3948 thing.movementOld = thing.movement;3949 thing.movement = thing.FSM.moveSimple;3950 thing.FSM.TimeHandler.addEventInterval(function () {3951 if (thing.resting === other) {3952 return false;3953 }3954 thing.FSM.TimeHandler.addEvent(function () {3955 thing.movement = thing.movementOld;3956 }, 1);3957 return true;3958 }, 1, Infinity);3959 }3960 return true;3961 }, 1, Infinity);3962 };3963 /**3964 * Animation Function for Coins emerging from (or being hit by) a solid. The3965 * Coin switches to the Scenery group, rotates between animation classes,3966 * moves up then down then dies, plays the "Coin" sound, and increaes the3967 * "coins" and "score" statistics.3968 *3969 * @param thing A Coin emerging from other.3970 * @param other A Solid thing is emerging from.3971 */3972 FullScreenMario.prototype.animateEmergeCoin = function (thing, other) {3973 thing.nocollide = thing.alive = thing.nofall = true;3974 thing.yvel -= thing.FSM.unitsize;3975 thing.FSM.switchClass(thing, "still", "anim");3976 thing.FSM.GroupHolder.switchMemberGroup(thing, "Character", "Scenery");3977 thing.FSM.AudioPlayer.play("Coin");3978 thing.FSM.ItemsHolder.increase("coins", 1);3979 thing.FSM.ItemsHolder.increase("score", 200);3980 thing.FSM.TimeHandler.cancelClassCycle(thing, "0");3981 thing.FSM.TimeHandler.addClassCycle(thing, [3982 "anim1", "anim2", "anim3", "anim4", "anim3", "anim2"3983 ], "0", 5);3984 thing.FSM.TimeHandler.addEventInterval(function () {3985 thing.FSM.moveCoinEmerge(thing, other);3986 return !thing.FSM.isThingAlive(thing);3987 }, 1, Infinity);3988 thing.FSM.TimeHandler.addEvent(function () {3989 thing.FSM.killNormal(thing);3990 }, 49);3991 thing.FSM.TimeHandler.addEvent(function () {3992 thing.yvel *= -1;3993 }, 25);3994 };3995 /**3996 * Animation Function for a Vine emerging from a solid. It continues to grow3997 * as normal via moveVine for 700 steps, then has its movement erased to3998 * stop.3999 *4000 * @param thing A Vine emerging from other.4001 * @param other A Solid thing is emerging from.4002 */4003 FullScreenMario.prototype.animateEmergeVine = function (thing, solid) {4004 // This allows the thing's movement to keep it on the solid4005 thing.attachedSolid = solid;4006 thing.FSM.setHeight(thing, 0);4007 thing.FSM.AudioPlayer.play("Vine Emerging");4008 thing.FSM.TimeHandler.addEvent(function () {4009 thing.nocollide = false;4010 }, 14);4011 thing.FSM.TimeHandler.addEvent(function () {4012 thing.movement = undefined;4013 }, 700);4014 };4015 /**4016 * Animates a "flicker" effect on a Thing by repeatedly toggling its hidden4017 * flag for a little while.4018 *4019 * @param thing A Thing switching between hidden and visible.4020 * @param cleartime How long to wait to stop the effect (by default, 49).4021 * @param interval How many steps between hidden toggles (by default, 2).4022 */4023 FullScreenMario.prototype.animateFlicker = function (thing, cleartime, interval) {4024 cleartime = Math.round(cleartime) || 49;4025 interval = Math.round(interval) || 2;4026 thing.flickering = true;4027 thing.FSM.TimeHandler.addEventInterval(function () {4028 thing.hidden = !thing.hidden;4029 thing.FSM.PixelDrawer.setThingSprite(thing);4030 }, interval, cleartime);4031 thing.FSM.TimeHandler.addEvent(function () {4032 thing.flickering = thing.hidden = false;4033 thing.FSM.PixelDrawer.setThingSprite(thing);4034 }, cleartime * interval + 1);4035 };4036 /**4037 * Animate Function for a HammerBro to throw a hammer. The HammerBro4038 * switches to the "throwing" class, waits and throws a few repeats, then4039 * goes back to normal.4040 *4041 * @param thing A HammerBro throwing hammers.4042 * @param count How many times left there are to throw a hammer. If equal4043 * to 3, a hammer will not be thrown (to mimic the pause in4044 * the original game).4045 */4046 FullScreenMario.prototype.animateThrowingHammer = function (thing, count) {4047 if (!thing.FSM.isThingAlive(thing)) {4048 return true;4049 }4050 if (thing.FSM.isThingAlive(thing.FSM.player)4051 && thing.right >= thing.FSM.unitsize * -324052 && count !== 3) {4053 thing.FSM.switchClass(thing, "thrown", "throwing");4054 }4055 thing.FSM.TimeHandler.addEvent(function () {4056 if (!thing.FSM.isThingAlive(thing)) {4057 return;4058 }4059 // Schedule the next animateThrowingHammer call4060 if (count > 0) {4061 thing.FSM.TimeHandler.addEvent(thing.FSM.animateThrowingHammer, 7, thing, count - 1);4062 }4063 else {4064 thing.FSM.TimeHandler.addEvent(thing.FSM.animateThrowingHammer, 70, thing, 7);4065 thing.FSM.removeClass(thing, "thrown");4066 }4067 // Don't throw if a Player is dead, or this is too far to the left4068 if (!thing.FSM.isThingAlive(thing.FSM.player) || thing.right < thing.FSM.unitsize * -32) {4069 thing.FSM.switchClass(thing, "throwing", "thrown");4070 return;4071 }4072 // Don't throw in the third iteration (makes a gap in the hammers)4073 if (count === 3) {4074 return;4075 }4076 // Throw by creating a hammer and visually updating4077 thing.FSM.switchClass(thing, "throwing", "thrown");4078 thing.FSM.addThing(["Hammer", {4079 "xvel": thing.lookleft4080 ? thing.FSM.unitsize / -1.44081 : thing.FSM.unitsize / 1.4,4082 "yvel": thing.FSM.unitsize * -1.4,4083 "gravity": thing.FSM.MapScreener.gravity / 2.14084 }], thing.left - thing.FSM.unitsize * 2, thing.top - thing.FSM.unitsize * 2);4085 }, 14);4086 return false;4087 };4088 /**4089 * Animation Function for when Bowser jumps. This will only trigger if he is4090 * facing left and a player exists. If either Bowser or a Player die, it4091 * is cancelled. He is given a negative yvel to jump, and the nocollidesolid4092 * flag is enabled as long as he is rising.4093 *4094 * @param thing A Bowser about to jump.4095 * @returns Whether to stop the event interval occasionally triggering this.4096 */4097 FullScreenMario.prototype.animateBowserJump = function (thing) {4098 if (!thing.lookleft || !thing.FSM.isThingAlive(thing.FSM.player)) {4099 return false;4100 }4101 if (!thing.FSM.isThingAlive(thing)) {4102 return true;4103 }4104 thing.resting = undefined;4105 thing.yvel = thing.FSM.unitsize * -1.4;4106 // If there is a platform, don't bump into it4107 thing.nocollidesolid = true;4108 thing.FSM.TimeHandler.addEventInterval(function () {4109 if (thing.dead || thing.yvel > thing.FSM.unitsize) {4110 thing.nocollidesolid = false;4111 return true;4112 }4113 return false;4114 }, 3, Infinity);4115 return false;4116 };4117 /**4118 * Animation Function for when Bowser fires. This will only trigger if he is4119 * facing left and a player exists. If either Bowser or a Player die, it4120 * is cancelled. His mouth is closed and an animateBowserFireOpen call is4121 * scheduled to complete the animation.4122 *4123 * @param thing A Bowser about to fire.4124 * @returns Whether to stop the event interval occasionally triggering this.4125 */4126 FullScreenMario.prototype.animateBowserFire = function (thing) {4127 if (!thing.lookleft || !thing.FSM.isThingAlive(thing.FSM.player)) {4128 return false;4129 }4130 if (!thing.FSM.isThingAlive(thing)) {4131 return true;4132 }4133 // Close the mouth4134 thing.FSM.addClass(thing, "firing");4135 thing.FSM.AudioPlayer.playLocal("Bowser Fires", thing.left);4136 // After a bit, re-open and fire4137 thing.FSM.TimeHandler.addEvent(thing.FSM.animateBowserFireOpen, 14, thing);4138 return false;4139 };4140 /**4141 * Animation Function for when Bowser actually fires. A BowserFire Thing is4142 * placed at his mouth, given a (rounded to unitsize * 8) destination y, and4143 * sent firing to a Player.4144 *4145 * @param thing A Bowser opening its mouth.4146 * @returns Whether to stop the event interval occasionally triggering this.4147 */4148 FullScreenMario.prototype.animateBowserFireOpen = function (thing) {4149 var unitsize = thing.FSM.unitsize, ylev = Math.max(-thing.height * unitsize, Math.round(thing.FSM.player.bottom / (unitsize * 8))4150 * unitsize * 8);4151 if (!thing.FSM.isThingAlive(thing)) {4152 return true;4153 }4154 thing.FSM.removeClass(thing, "firing");4155 thing.FSM.addThing(["BowserFire", {4156 "ylev": ylev4157 }], thing.left - thing.FSM.unitsize * 8, thing.top + thing.FSM.unitsize * 4);4158 return false;4159 };4160 /**4161 * Animation Function for when Bowser throws a Hammer. It's similar to a4162 * HammerBro, but the hammer appears on top of Bowser for a few steps4163 * before being thrown in the direction Bowser is facing (though it will4164 * only be added if facing left).4165 *4166 * @param thing A Bowser about to throw a hammer.4167 * @returns Whether to stop the event interval occasionally triggering this.4168 */4169 FullScreenMario.prototype.animateBowserThrow = function (thing) {4170 if (!thing.lookleft || !thing.FSM.player || !thing.FSM.isThingAlive(thing.FSM.player)) {4171 return false;4172 }4173 if (!thing.FSM.isThingAlive(thing)) {4174 return true;4175 }4176 var hammer = thing.FSM.addThing("Hammer", thing.left + thing.FSM.unitsize * 2, thing.top - thing.FSM.unitsize * 2);4177 thing.FSM.TimeHandler.addEventInterval(function () {4178 if (!thing.FSM.isThingAlive(thing)) {4179 thing.FSM.killNormal(hammer);4180 return true;4181 }4182 thing.FSM.setTop(hammer, thing.top - thing.FSM.unitsize * 2);4183 if (thing.lookleft) {4184 thing.FSM.setLeft(hammer, thing.left + thing.FSM.unitsize * 2);4185 }4186 else {4187 thing.FSM.setLeft(hammer, thing.right - thing.FSM.unitsize * 2);4188 }4189 return true;4190 }, 1, 14);4191 thing.FSM.TimeHandler.addEvent(function () {4192 hammer.xvel = thing.FSM.unitsize * 1.17;4193 hammer.yvel = thing.FSM.unitsize * -2.1;4194 // hammer.gravity = thing.FSM.MapScreener.gravity / 1.4;4195 if (thing.lookleft) {4196 hammer.xvel *= -1;4197 }4198 }, 14);4199 return false;4200 };4201 /**4202 * Animation Function for when Bowser freezes upon a Player hitting a4203 * CastleAxe. Velocity and movement are paused, and the Bowser is added to4204 * the current cutscene's settings.4205 *4206 * @param thing A Bowser that has just been killed.4207 * @remarks This is triggered as Bowser's killonend property.4208 */4209 FullScreenMario.prototype.animateBowserFreeze = function (thing) {4210 thing.nofall = true;4211 thing.nothrow = true;4212 thing.movement = undefined;4213 thing.dead = true;4214 thing.FSM.animateCharacterPauseVelocity(thing);4215 thing.FSM.ScenePlayer.addCutsceneSetting("bowser", thing);4216 thing.FSM.TimeHandler.addEvent(function () {4217 thing.nofall = false;4218 }, 70);4219 };4220 /**4221 * Animation Function for a standard jump, such as what HammerBros do. The4222 * jump may be in either up or down, chosen at random by the NumberMaker.4223 * Steps are taken to ensure the Thing does not collide at improper points4224 * during the jump.4225 *4226 * @param thing A HammerBro about to jump.4227 * @returns Whether to stop the event interval occasionally triggering this.4228 */4229 FullScreenMario.prototype.animateJump = function (thing) {4230 // Finish4231 if (!thing.FSM.isThingAlive(thing) || !thing.FSM.isThingAlive(thing.FSM.player)) {4232 return true;4233 }4234 // Skip4235 if (!thing.resting) {4236 return false;4237 }4238 // Jump up?4239 if (thing.FSM.MapScreener.floor - (thing.bottom / thing.FSM.unitsize) >= 304240 && thing.resting.title !== "Floor"4241 && thing.FSM.NumberMaker.randomBoolean()) {4242 thing.falling = true;4243 thing.yvel = thing.FSM.unitsize * -.7;4244 thing.FSM.TimeHandler.addEvent(function () {4245 thing.falling = false;4246 }, 42);4247 }4248 else {4249 // Jump down4250 thing.nocollidesolid = true;4251 thing.yvel = thing.FSM.unitsize * -2.1;4252 thing.FSM.TimeHandler.addEvent(function () {4253 thing.nocollidesolid = false;4254 }, 42);4255 }4256 thing.resting = undefined;4257 return false;4258 };4259 /**4260 * Animation Function for Bloopers starting to "unsqueeze". The "squeeze"4261 * class is removed, their height is reset to 12, and their counter reset.4262 *4263 * @param thing An unsqueezing Blooper.4264 */4265 FullScreenMario.prototype.animateBlooperUnsqueezing = function (thing) {4266 thing.counter = 0;4267 thing.squeeze = 0;4268 thing.FSM.removeClass(thing, "squeeze");4269 thing.FSM.setHeight(thing, 12, true, true);4270 };4271 /**4272 * Animation Function for Podoboos jumping up. Their top is recorded and a4273 * large negative yvel is given; after the jumpheight number of steps, they4274 * fall back down.4275 *4276 * @param thing A Podoboo jumping up.4277 */4278 FullScreenMario.prototype.animatePodobooJumpUp = function (thing) {4279 thing.starty = thing.top;4280 thing.yvel = thing.speed * -1;4281 thing.FSM.TimeHandler.addEvent(thing.FSM.animatePodobooJumpDown, thing.jumpHeight, thing);4282 };4283 /**4284 * Animation Function for when a Podoboo needs to stop jumping. It obtains4285 * the movePodobooFalling movement to track its descent.4286 *4287 * @param thing A Podoboo jumping down.4288 */4289 FullScreenMario.prototype.animatePodobooJumpDown = function (thing) {4290 thing.movement = thing.FSM.movePodobooFalling;4291 };4292 /**4293 * Animation Function for a Lakitu throwing a SpinyEgg. The Lakitu hides4294 * behind its cloud ("hiding" class), waits 21 steps, then throws an egg up4295 * and comes out of "hiding".4296 *4297 * @param thing A Lakitu throwing a Spiny.4298 * @returns Whether to stop the event interval occasionally triggering this.4299 */4300 FullScreenMario.prototype.animateLakituThrowingSpiny = function (thing) {4301 if (thing.fleeing || !thing.FSM.isThingAlive(thing)) {4302 return true;4303 }4304 thing.FSM.switchClass(thing, "out", "hiding");4305 thing.FSM.TimeHandler.addEvent(function () {4306 if (thing.dead) {4307 return;4308 }4309 var spawn = thing.FSM.addThing("SpinyEgg", thing.left, thing.top);4310 spawn.yvel = thing.FSM.unitsize * -2.1;4311 thing.FSM.switchClass(thing, "hiding", "out");4312 }, 21);4313 };4314 /**4315 * Animation Function for when a SpinyEgg hits the ground. The SpinyEgg is4316 * killed and a Spiny is put in its place, moving towards a Player.4317 *4318 * @param thing A SpinyEgg hatching into a Spiny.4319 */4320 FullScreenMario.prototype.animateSpinyEggHatching = function (thing) {4321 if (!thing.FSM.isThingAlive(thing)) {4322 return;4323 }4324 var spawn = thing.FSM.addThing("Spiny", thing.left, thing.top - thing.yvel);4325 spawn.moveleft = thing.FSM.objectToLeft(thing.FSM.player, spawn);4326 thing.FSM.killNormal(thing);4327 };4328 /**4329 * Animation Function for when a Fireball emerges from a Player. All that4330 * happens is the "Fireball" sound plays.4331 *4332 * @param thing A Fireball emerging from a Player.4333 */4334 FullScreenMario.prototype.animateFireballEmerge = function (thing) {4335 thing.FSM.AudioPlayer.play("Fireball");4336 };4337 /**4338 * Animation Function for when a Fireball explodes. It is deleted and,4339 * unless big is === 2 (as this is used as a kill Function), a Firework is4340 * put in its place.4341 *4342 * @param thing An exploding Fireball.4343 * @param big The "level" of death this is (a 2 implies this is a sudden4344 * death, without animations).4345 */4346 FullScreenMario.prototype.animateFireballExplode = function (thing, big) {4347 thing.nocollide = true;4348 thing.FSM.killNormal(thing);4349 if (big === 2) {4350 return;4351 }4352 var output = thing.FSM.addThing("Firework");4353 thing.FSM.setMidXObj(output, thing);4354 thing.FSM.setMidYObj(output, thing);4355 output.animate(output);4356 };4357 /**4358 * Animation Function for a Firework, triggered immediately upon spawning.4359 * The Firework cycles between "n1" through "n3", then dies.4360 *4361 * @param thing An exploding Firework.4362 */4363 FullScreenMario.prototype.animateFirework = function (thing) {4364 var name = thing.className + " n", i;4365 for (i = 0; i < 3; i += 1) {4366 thing.FSM.TimeHandler.addEvent(function (i) {4367 thing.FSM.setClass(thing, name + (i + 1).toString());4368 }, i * 7, i);4369 }4370 thing.FSM.AudioPlayer.play("Firework");4371 thing.FSM.TimeHandler.addEvent(function () {4372 thing.FSM.killNormal(thing);4373 }, i * 7);4374 };4375 /**4376 * Animation Function for a Cannon outputting a BulletBill. This will only4377 * happen if the Cannon isn't within 8 units of a Player. The spawn flies4378 * at a constant rate towards a Player.4379 *4380 * @param thing A firing Cannon.4381 */4382 FullScreenMario.prototype.animateCannonFiring = function (thing) {4383 if (!thing.FSM.isThingAlive(thing)) {4384 return;4385 }4386 // Don't fire if Player is too close4387 if (thing.FSM.player.right > (thing.left - thing.FSM.unitsize * 8)4388 && thing.FSM.player.left < (thing.right + thing.FSM.unitsize * 8)) {4389 return;4390 }4391 var spawn = thing.FSM.ObjectMaker.make("BulletBill");4392 if (thing.FSM.objectToLeft(thing.FSM.player, thing)) {4393 spawn.direction = 1;4394 spawn.moveleft = true;4395 spawn.xvel *= -1;4396 thing.FSM.flipHoriz(spawn);4397 thing.FSM.addThing(spawn, thing.left, thing.top);4398 }4399 else {4400 thing.FSM.addThing(spawn, thing.left + thing.width, thing.top);4401 }4402 thing.FSM.AudioPlayer.playLocal("Bump", thing.right);4403 };4404 /**4405 * Animation Function for a fiery player throwing a Fireball. A player may4406 * only do so if fewer than 2 other thrown Fireballs exist. A new Fireball4407 * is created in front of where a Player is facing and are sent bouncing4408 * away.4409 *4410 * @param thing A Player throwing a fireball.4411 */4412 FullScreenMario.prototype.animatePlayerFire = function (thing) {4413 if (thing.numballs >= 2) {4414 return;4415 }4416 var xloc = thing.moveleft4417 ? (thing.left - thing.FSM.unitsize / 4)4418 : (thing.right + thing.FSM.unitsize / 4), ball = thing.FSM.ObjectMaker.make("Fireball", {4419 "moveleft": thing.moveleft,4420 "speed": thing.FSM.unitsize * 1.75,4421 "jumpheight": thing.FSM.unitsize * 1.56,4422 "gravity": thing.FSM.MapScreener.gravity * 1.56,4423 "yvel": thing.FSM.unitsize,4424 "movement": thing.FSM.moveJumping4425 });4426 thing.FSM.addThing(ball, xloc, thing.top + thing.FSM.unitsize * 8);4427 ball.animate(ball);4428 ball.onDelete = function () {4429 thing.numballs -= 1;4430 };4431 thing.numballs += 1;4432 thing.FSM.addClass(thing, "firing");4433 thing.FSM.TimeHandler.addEvent(function () {4434 thing.FSM.removeClass(thing, "firing");4435 }, 7);4436 };4437 /**4438 * Animation Function that regularly spings CastleFireballs around their4439 * parent CastleBlock. The CastleBlock's location and angle determine the4440 * location of each CastleFireball, and its dt and direction determine how4441 * the angle is changed for the next call.4442 *4443 * @param thing A CastleBlock with CastleFireballs around it.4444 * @param balls CastleFireballs rotating from thing's center.4445 */4446 FullScreenMario.prototype.animateCastleBlock = function (thing, balls) {4447 var midx = thing.EightBitter.getMidX(thing), midy = thing.EightBitter.getMidY(thing), ax = Math.cos(thing.angle * Math.PI) * thing.FSM.unitsize * 4, ay = Math.sin(thing.angle * Math.PI) * thing.FSM.unitsize * 4, i;4448 for (i = 0; i < balls.length; i += 1) {4449 thing.FSM.setMidX(balls[i], midx + ax * i);4450 thing.FSM.setMidY(balls[i], midy + ay * i);4451 }4452 thing.angle += thing.dt * thing.direction;4453 };4454 /**4455 * Animation Function to close a CastleBridge when a Player triggers its4456 * killonend after hitting the CastleAxe in EndInsideCastle. Its width is4457 * reduced repeatedly on an interval until it's 0.4458 *4459 * @param thing A CastleBridge opening from a CastleAxe's trigger.4460 * @remarks This is triggered as the killonend property of the bridge.4461 */4462 FullScreenMario.prototype.animateCastleBridgeOpen = function (thing) {4463 thing.FSM.ScenePlayer.playRoutine("CastleBridgeOpen", thing);4464 };4465 /**4466 * Animation Function for when a CastleChain opens, which just delays a4467 * killNormal call for 7 steps.4468 *4469 * @param thing A CastleChain opening from a CastleAxe's trigger.4470 * @remarks This is triggered as the killonend property of the chain.4471 */4472 FullScreenMario.prototype.animateCastleChainOpen = function (thing) {4473 thing.FSM.TimeHandler.addEvent(thing.FSM.killNormal, 3, thing);4474 };4475 /**4476 * Animation Function for when a Player paddles underwater. Any previous4477 * Any previous paddling classes and cycle are removed, and a new one is4478 * added that, when it finishes, remnoves a Player's paddlingCycle as4479 * well.4480 *4481 * @param thing A Player paddling in water.4482 */4483 FullScreenMario.prototype.animatePlayerPaddling = function (thing) {4484 if (!thing.paddlingCycle) {4485 thing.FSM.removeClasses(thing, "skidding paddle1 paddle2 paddle3 paddle4 paddle5");4486 thing.FSM.addClass(thing, "paddling");4487 thing.FSM.TimeHandler.cancelClassCycle(thing, "paddlingCycle");4488 thing.FSM.TimeHandler.addClassCycle(thing, [4489 "paddle1", "paddle2", "paddle3", "paddle2", "paddle1",4490 function () { return thing.paddlingCycle = false; }4491 ], "paddlingCycle", 7);4492 }4493 thing.paddling = thing.paddlingCycle = thing.swimming = true;4494 thing.yvel = thing.FSM.unitsize * -.84;4495 };4496 /**4497 * Animation Function for when a player lands to reset size and remove4498 * hopping (and if underwater, paddling) classes. The mod event is fired.4499 *4500 * @param thing A Player landing on a Solid.4501 */4502 FullScreenMario.prototype.animatePlayerLanding = function (thing) {4503 if (thing.crouching && thing.power > 1) {4504 thing.FSM.setHeight(thing, 11, true, true);4505 }4506 if (thing.FSM.hasClass(thing, "hopping")) {4507 thing.FSM.switchClass(thing, "hopping", "jumping");4508 }4509 if (thing.FSM.MapScreener.underwater) {4510 thing.FSM.removeClass(thing, "paddling");4511 }4512 thing.FSM.ModAttacher.fireEvent("onPlayerLanding", thing, thing.resting);4513 };4514 /**4515 * Animation Function for when a Player moves off a resting solid. It4516 * sets resting to undefined, and if underwater, switches the "running" and4517 * "paddling" classes.4518 *4519 * @param thing A Player moving off a resting Solid.4520 */4521 FullScreenMario.prototype.animatePlayerRestingOff = function (thing) {4522 thing.resting = undefined;4523 if (thing.FSM.MapScreener.underwater) {4524 thing.FSM.switchClass(thing, "running", "paddling");4525 }4526 };4527 /**4528 * Animation Function for when a player breathes a underwater. This creates4529 * a Bubble, which slowly rises to the top of the screen.4530 *4531 * @param thing An underwater Player.4532 */4533 FullScreenMario.prototype.animatePlayerBubbling = function (thing) {4534 thing.FSM.addThing("Bubble", thing.right, thing.top);4535 };4536 /**4537 * Animation Function to give a Player a cycle of running classes. The4538 * cycle auto-updates its time as a function of how fast a Player is4539 * moving relative to its maximum speed.4540 *4541 * @param thing A running player.4542 */4543 FullScreenMario.prototype.animatePlayerRunningCycle = function (thing) {4544 thing.FSM.switchClass(thing, "still", "running");4545 thing.running = thing.FSM.TimeHandler.addClassCycle(thing, [4546 "one", "two", "three", "two"4547 ], "running", function () {4548 return 5 + Math.ceil(thing.maxspeedsave - Math.abs(thing.xvel));4549 });4550 };4551 /**4552 * Completely pauses a Thing by setting its velocities to zero and disabling4553 * it from falling, colliding, or moving. Its old attributes for those are4554 * saved so thingResumeVelocity may restore them.4555 *4556 * @param thing A Character being forzen in place.4557 * @param keepMovement Whether to keep movement instead of wiping it4558 * (by default, false).4559 */4560 FullScreenMario.prototype.animateCharacterPauseVelocity = function (thing, keepMovement) {4561 thing.xvelOld = thing.xvel || 0;4562 thing.yvelOld = thing.yvel || 0;4563 thing.nofallOld = thing.nofall || false;4564 thing.nocollideOld = thing.nocollide || false;4565 thing.movementOld = thing.movement || thing.movementOld;4566 thing.nofall = thing.nocollide = true;4567 thing.xvel = thing.yvel = 0;4568 if (!keepMovement) {4569 thing.movement = undefined;4570 }4571 };4572 /**4573 * Resumes a Thing's velocity and movements after they were paused by4574 * thingPauseVelocity.4575 *4576 * @param thing A Character being unfrozen.4577 * @param noVelocity Whether to skip restoring the Thing's velocity4578 * (by default, false).4579 */4580 FullScreenMario.prototype.animateCharacterResumeVelocity = function (thing, noVelocity) {4581 if (!noVelocity) {4582 thing.xvel = thing.xvelOld || 0;4583 thing.yvel = thing.yvelOld || 0;4584 }4585 thing.movement = thing.movementOld || thing.movement;4586 thing.nofall = thing.nofallOld || false;4587 thing.nocollide = thing.nocollideOld || false;4588 };4589 /**4590 * Animation Function for when a player hops on an enemy. Resting is set to4591 * undefined, and a small vertical yvel is given.4592 *4593 * @param thing A Character hopping up.4594 */4595 FullScreenMario.prototype.animateCharacterHop = function (thing) {4596 thing.resting = undefined;4597 thing.yvel = thing.FSM.unitsize * -1.4;4598 };4599 /**4600 * Animation Function to start a player transferring through a Pipe. This is4601 * generic for entrances and exists horizontally and vertically: movement4602 * and velocities are frozen, size is reset, and the piping flag enabled.4603 * a Player is also moved into the Scenery group to be behind the Pipe.4604 *4605 * @param thing A Player entering a Pipe.4606 */4607 FullScreenMario.prototype.animatePlayerPipingStart = function (thing) {4608 thing.nocollide = thing.nofall = thing.piping = true;4609 thing.xvel = thing.yvel = 0;4610 thing.movementOld = thing.movement;4611 thing.movement = undefined;4612 if (thing.power > 1) {4613 thing.FSM.animatePlayerRemoveCrouch(thing);4614 thing.FSM.setPlayerSizeLarge(thing);4615 }4616 else {4617 thing.FSM.setPlayerSizeSmall(thing);4618 }4619 thing.FSM.removeClasses(thing, "jumping running crouching");4620 thing.FSM.AudioPlayer.clearTheme();4621 thing.FSM.TimeHandler.cancelAllCycles(thing);4622 thing.FSM.GroupHolder.switchMemberGroup(thing, "Character", "Scenery");4623 };4624 /**4625 * Animation Function for when a player is done passing through a Pipe. This4626 * is abstracted for exits both horizontally and vertically, typically after4627 * an area has just been entered.4628 *4629 * @param thing A Player completing a pass through a Pipe.4630 */4631 FullScreenMario.prototype.animatePlayerPipingEnd = function (thing) {4632 thing.movement = thing.movementOld;4633 thing.nocollide = thing.nofall = thing.piping = false;4634 thing.FSM.AudioPlayer.resumeTheme();4635 thing.FSM.GroupHolder.switchMemberGroup(thing, "Scenery", "Character");4636 };4637 /**4638 * Animation Function for when a player is hopping off a pole. It hops off4639 * and faces the opposite direction.4640 *4641 * @param thing A Player moving a way from a pole.4642 * @param doRun Whether a Player should have a running cycle added4643 * added immediately, such as during cutscenes (by4644 * default, false).4645 */4646 FullScreenMario.prototype.animatePlayerOffPole = function (thing, doRun) {4647 thing.FSM.removeClasses(thing, "climbing running");4648 thing.FSM.addClass(thing, "jumping");4649 thing.xvel = 1.4;4650 thing.yvel = -.7;4651 thing.nocollide = thing.nofall = false;4652 thing.gravity = thing.FSM.MapScreener.gravity / 14;4653 thing.FSM.TimeHandler.addEvent(function () {4654 thing.movement = thing.FSM.movePlayer;4655 thing.gravity = thing.FSM.MapScreener.gravity;4656 thing.FSM.unflipHoriz(thing);4657 if (doRun) {4658 thing.FSM.animatePlayerRunningCycle(thing);4659 }4660 }, 21);4661 };4662 /**4663 * Animation Function for when a player must hop off a Vine during an area's4664 * opening cutscene. A player switches sides, waits 14 steps, then calls4665 * animatePlayerOffPole.4666 *4667 * @param thing A Player moving away from a Vine.4668 */4669 FullScreenMario.prototype.animatePlayerOffVine = function (thing) {4670 thing.FSM.flipHoriz(thing);4671 thing.FSM.shiftHoriz(thing, (thing.width - 1) * thing.FSM.unitsize);4672 thing.FSM.TimeHandler.addEvent(thing.FSM.animatePlayerOffPole, 14, thing);4673 };4674 /* Appearance utilities4675 */4676 /**4677 * Makes one Thing look towards another, chainging lookleft and moveleft in4678 * the process.4679 *4680 * @param thing A Character looking towards other.4681 * @param other A Thing being looked at by thing.4682 */4683 FullScreenMario.prototype.lookTowardsThing = function (thing, other) {4684 // Case: other is to the left4685 if (other.right <= thing.left) {4686 thing.lookleft = true;4687 thing.moveleft = true;4688 thing.FSM.unflipHoriz(thing);4689 }4690 else if (other.left >= thing.right) {4691 // Case: other is to the right4692 thing.lookleft = false;4693 thing.moveleft = false;4694 thing.FSM.flipHoriz(thing);4695 }4696 };4697 /**4698 * Makes one Thing look towards a Player, chainging lookleft and moveleft4699 * in the process.4700 *4701 * @param thing A Character looking towards the Player.4702 * @param big Whether to always change lookleft and moveleft,4703 * even if lookleft is already accurate (by4704 * default, false).4705 */4706 FullScreenMario.prototype.lookTowardsPlayer = function (thing, big) {4707 // Case: Player is to the left4708 if (thing.FSM.player.right <= thing.left) {4709 if (!thing.lookleft || big) {4710 thing.lookleft = true;4711 thing.moveleft = false;4712 thing.FSM.unflipHoriz(thing);4713 }4714 }4715 else if (thing.FSM.player.left >= thing.right) {4716 // Case: Player is to the right4717 if (thing.lookleft || big) {4718 thing.lookleft = false;4719 thing.moveleft = true;4720 thing.FSM.flipHoriz(thing);4721 }4722 }4723 };4724 /* Death functions4725 */4726 /**4727 * Standard Function to kill a Thing, which means marking it as dead and4728 * clearing its numquads, resting, movement, and cycles. It will later be4729 * marked as gone by its maintain* Function (Solids or Characters).4730 *4731 * @param thing A Thing to kill.4732 */4733 FullScreenMario.prototype.killNormal = function (thing) {4734 if (!thing) {4735 return;4736 }4737 thing.hidden = thing.dead = true;4738 thing.alive = false;4739 thing.numquads = 0;4740 thing.movement = undefined;4741 if (this.hasOwnProperty("resting")) {4742 thing.resting = undefined;4743 }4744 if (thing.FSM) {4745 thing.FSM.TimeHandler.cancelAllCycles(thing);4746 }4747 thing.FSM.ModAttacher.fireEvent("onKillNormal", thing);4748 };4749 /**4750 * Death Function commonly called on characters to animate a small flip4751 * before killNormal is called.4752 *4753 * @param thing A Thing to kill.4754 * @param extra How much time to wait beyond the standard 70 steps4755 * before calling killNormal (by default, 0).4756 */4757 FullScreenMario.prototype.killFlip = function (thing, extra) {4758 if (extra === void 0) { extra = 0; }4759 thing.FSM.flipVert(thing);4760 if (thing.bottomBump) {4761 thing.bottomBump = undefined;4762 }4763 thing.nocollide = thing.dead = true;4764 thing.speed = thing.xvel = 0;4765 thing.nofall = false;4766 thing.resting = thing.movement = undefined;4767 thing.yvel = -thing.FSM.unitsize;4768 thing.FSM.TimeHandler.addEvent(thing.FSM.killNormal, 70 + extra, thing);4769 };4770 /**4771 * Kill Function to replace a Thing with a spawned Thing, determined by the4772 * thing's spawnType, in the same location.4773 *4774 * @param thing A Thing to kill.4775 * @param big Whether this should skip creating the spawn (by default,4776 * false).4777 */4778 FullScreenMario.prototype.killSpawn = function (thing, big) {4779 if (big) {4780 thing.FSM.killNormal(thing);4781 return;4782 }4783 if (!thing.spawnType) {4784 throw new Error("Thing " + thing.title + " has no .spawnType.");4785 }4786 var spawn = thing.FSM.ObjectMaker.make(thing.spawnType, thing.spawnSettings || {});4787 thing.FSM.addThing(spawn);4788 thing.FSM.setBottom(spawn, thing.bottom);4789 thing.FSM.setMidXObj(spawn, thing);4790 thing.FSM.killNormal(thing);4791 return spawn;4792 };4793 /**4794 * A kill Function similar to killSpawn but more configurable. A spawned4795 * Thing is created with the given attributes and copies over any specified4796 * attributes from the original Thing.4797 *4798 * @param thing A Thing to kill.4799 * @param title The type of new Thing to create, such as "Goomba".4800 * @param attributes An optional object to pass in to the ObjectMaker.make4801 * call (by default, {}).4802 * @param attributesCopied An optional listing of attributes to copy from4803 * the original Thing (by default, none).4804 */4805 FullScreenMario.prototype.killReplace = function (thing, title, attributes, attributesCopied) {4806 if (attributes === void 0) { attributes = {}; }4807 var spawn, i;4808 if (typeof attributesCopied !== "undefined") {4809 for (i = 0; i < attributesCopied.length; i += 1) {4810 attributes[attributesCopied[i]] = thing[attributesCopied[i]];4811 }4812 }4813 spawn = thing.FSM.ObjectMaker.make(title, attributes);4814 if (thing.flipHoriz) {4815 thing.FSM.flipHoriz(spawn);4816 }4817 if (thing.flipVert) {4818 thing.FSM.flipVert(spawn);4819 }4820 thing.FSM.addThing(spawn, thing.left, thing.top);4821 thing.FSM.killNormal(thing);4822 return spawn;4823 };4824 /**4825 * Kill Function for Goombas. If big isn't specified, it replaces the4826 * killed Goomba with a DeadGoomba via killSpawn.4827 *4828 * @param thing A Goomba to kill.4829 * @param big Whether to call killFlip on the Thing instead of4830 * killSpawn, such as when a Shell hits it.4831 */4832 FullScreenMario.prototype.killGoomba = function (thing, big) {4833 if (big) {4834 thing.FSM.killFlip(thing);4835 return;4836 }4837 thing.FSM.killSpawn(thing);4838 };4839 /**4840 * Kill Function for Koopas. Jumping and floating Koopas are replacing with4841 * an equivalent Koopa that's just walking, while walking Koopas become4842 * Shells.4843 *4844 * @param thing A Koopa to kill.4845 * @param big Whether shells should be immediately killed.4846 * @remarks This isn't called when a Shell hits a Koopa.4847 */4848 FullScreenMario.prototype.killKoopa = function (thing, big) {4849 var spawn;4850 if (thing.jumping || thing.floating) {4851 spawn = thing.FSM.killReplace(thing, "Koopa", undefined, ["smart", "direction", "moveleft"]);4852 spawn.xvel = spawn.moveleft ? -spawn.speed : spawn.speed;4853 }4854 else {4855 spawn = thing.FSM.killToShell(thing, Number(big));4856 }4857 return spawn;4858 };4859 /**4860 * Kill Function for Lakitus. If this is the last Lakitu in Characters,4861 * a new one is scheduled to be spawned at the same y-position.4862 *4863 * @param thing A Lakitu to kill.4864 */4865 FullScreenMario.prototype.killLakitu = function (thing) {4866 var characters = thing.FSM.GroupHolder.getGroup("Character"), i;4867 thing.FSM.killFlip(thing);4868 thing.FSM.MapScreener.lakitu = undefined;4869 // If any other Lakitu exists after killNormal, killLakitu is done4870 for (i = 0; i < characters.length; i += 1) {4871 if (characters[i].title === "Lakitu") {4872 thing.FSM.MapScreener.lakitu = characters[i];4873 return;4874 }4875 }4876 // The next Lakitu is spawned ~5 seconds later, give or take4877 thing.FSM.TimeHandler.addEvent(thing.FSM.addThing.bind(thing.FSM), 350, "Lakitu", thing.FSM.MapScreener.right, thing.top);4878 };4879 /**4880 * Kill Function for Bowsers. In reality this is only called when a Player4881 * Fireballs him or all NPCs are to be killed. It takes five Fireballs to4882 * killFlip a Bowser, which scores 5000 points.4883 *4884 * @param thing A Bowser to kill.4885 * @param big Whether this should default to killFlip, as in an4886 * EndInsideCastle cutscene.4887 */4888 FullScreenMario.prototype.killBowser = function (thing, big) {4889 if (big) {4890 thing.nofall = false;4891 thing.movement = undefined;4892 thing.FSM.killFlip(thing.FSM.killSpawn(thing));4893 return;4894 }4895 thing.deathcount += 1;4896 if (thing.deathcount === 5) {4897 thing.yvel = thing.speed = 0;4898 thing.movement = undefined;4899 thing.FSM.killFlip(thing.FSM.killSpawn(thing), 350);4900 thing.FSM.scoreOn(5000, thing);4901 }4902 };4903 /**4904 * Kills a Thing by replacing it with another Thing, typically a Shell or4905 * BeetleShell (determined by thing.shelltype). The spawn inherits smartness4906 * and location from its parent, and is temporarily given nocollidechar to4907 * stop double collision detections.4908 *4909 * @param thing A Character to kill.4910 * @param big Whether the spawned Shell should be killed4911 * immediately (by default, false).4912 */4913 FullScreenMario.prototype.killToShell = function (thing, big) {4914 var spawn, nocollidecharold, nocollideplayerold;4915 thing.spawnSettings = {4916 "smart": thing.smart4917 };4918 if (big && big !== 2) {4919 thing.spawnType = thing.title;4920 }4921 else {4922 thing.spawnType = thing.shelltype || "Shell";4923 }4924 thing.spawnSettings = {4925 "smart": thing.smart4926 };4927 spawn = thing.FSM.killSpawn(thing);4928 nocollidecharold = spawn.nocollidechar;4929 nocollideplayerold = spawn.nocollideplayer;4930 spawn.nocollidechar = true;4931 spawn.nocollideplayer = true;4932 thing.FSM.TimeHandler.addEvent(function () {4933 spawn.nocollidechar = nocollidecharold;4934 spawn.nocollideplayer = nocollideplayerold;4935 }, 7);4936 thing.FSM.killNormal(thing);4937 if (big === 2) {4938 thing.FSM.killFlip(spawn);4939 }4940 return spawn;4941 };4942 /**4943 * Wipes the screen of any characters or solids that should be gone during4944 * an important cutscene, such as hitting an end-of-level flag.4945 * For characters, they're deleted if .nokillonend isn't truthy. If they4946 * have a .killonend function, that's called on them.4947 * Solids are only deleted if their .killonend is true.4948 *4949 * @remarks If thing.killonend is a Function, it is called on the Thing.4950 */4951 FullScreenMario.prototype.killNPCs = function () {4952 var FSM = FullScreenMario.prototype.ensureCorrectCaller(this), group, character, solid, i;4953 // Characters: they must opt out of being killed with .nokillonend, and4954 // may opt into having a function called instead (such as Lakitus).4955 group = FSM.GroupHolder.getGroup("Character");4956 for (i = group.length - 1; i >= 0; --i) {4957 character = group[i];4958 if (!character.nokillend) {4959 character.FSM.killNormal(character);4960 character.FSM.arrayDeleteThing(character, group, i);4961 }4962 else if (character.killonend) {4963 character.killonend(character);4964 }4965 }4966 // Solids: they may opt into being deleted4967 group = FSM.GroupHolder.getGroup("Solid");4968 for (i = group.length - 1; i >= 0; --i) {4969 solid = group[i];4970 if (solid.killonend) {4