Best Python code snippet using hypothesis
FullScreenMario.js
Source:FullScreenMario.js  
1// @echo '/// <reference path="GameStartr-0.2.0.ts" />'2var __extends = (this && this.__extends) || function (d, b) {3    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];4    function __() { this.constructor = d; }5    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());6};7// @ifdef INCLUDE_DEFINITIONS8/// <reference path="References/GameStartr-0.2.0.ts" />9/// <reference path="FullScreenMario.d.ts" />10// @endif11// @include ../Source/FullScreenMario.d.ts12var FullScreenMario;13(function (FullScreenMario_1) {14    "use strict";15    /**16     * A free HTML5 remake of Nintendo's original Super Mario Bros, expanded for the17     * modern web. It includes the original 32 levels, a random map generator, a18     * level editor, and over a dozen custom mods.19     */20    var FullScreenMario = (function (_super) {21        __extends(FullScreenMario, _super);22        /**23         * Initializes a new instance of the FullScreenMario class using the static24         * settings stored in `FullScreenMario.setting`.25         *26         * @param settings   Extra settings such as screen size.27         */28        function FullScreenMario(settings) {29            this.settings = FullScreenMario.settings;30            this.deviceMotionStatus = {31                motionLeft: false,32                motionRight: false,33                x: undefined,34                y: undefined,35                dy: undefined36            };37            _super.call(this, this.proliferate({38                constantsSource: FullScreenMario,39                constants: ["unitsize", "scale", "gravity", "pointLevels", "customTextMappings"]40            }, settings));41        }42        /* Resets43        */44        /**45         * Sets this.ObjectMaker.46         *47         * Because many Thing functions require access to other FSM modules, each is48         * given a reference to this container FSM via properties.thing.GameStarter.49         *50         * @param FSM51         * @param customs   Any optional custom settings.52         */53        FullScreenMario.prototype.resetObjectMaker = function (FSM, settings) {54            FSM.ObjectMaker = new ObjectMakr.ObjectMakr(FSM.proliferate({55                properties: {56                    Quadrant: {57                        EightBitter: FSM,58                        GameStarter: FSM,59                        FSM: FSM60                    },61                    Thing: {62                        EightBitter: FSM,63                        GameStarter: FSM,64                        FSM: FSM65                    }66                }67            }, FSM.settings.objects));68        };69        /**70         * Sets this.AudioPlayer.71         *72         * @param FSM73         * @param customs   Any optional custom settings.74         */75        FullScreenMario.prototype.resetAudioPlayer = function (FSM, settings) {76            _super.prototype.resetAudioPlayer.call(this, FSM, settings);77            FSM.AudioPlayer.setGetVolumeLocal(FSM.getVolumeLocal.bind(FSM, FSM));78            FSM.AudioPlayer.setGetThemeDefault(FSM.getAudioThemeDefault.bind(FSM, FSM));79        };80        /**81         * Sets this.AreaSpawner.82         *83         * @param FSM84         * @param customs   Any optional custom settings.85         */86        FullScreenMario.prototype.resetAreaSpawner = function (FSM, settings) {87            FSM.AreaSpawner = new AreaSpawnr.AreaSpawnr({88                "MapsCreator": FSM.MapsCreator,89                "MapScreener": FSM.MapScreener,90                "screenAttributes": FSM.settings.maps.screenAttributes,91                "onSpawn": FSM.settings.maps.onSpawn.bind(FSM),92                "stretchAdd": FSM.mapAddStretched.bind(FSM),93                "afterAdd": FSM.mapAddAfter.bind(FSM)94            });95        };96        /**97         * Resets this.ItemsHolder via the parent GameStartr resetItemsHolder.98         *99         * If the screen isn't wide enough to fit the "lives" display, it's hidden.100         *101         * @param FSM102         * @param customs   Any optional custom settings.103         */104        FullScreenMario.prototype.resetItemsHolder = function (FSM, settings) {105            _super.prototype.resetItemsHolder.call(this, FSM, settings);106            if (settings.width < 560) {107                FSM.ItemsHolder.getContainer().children[0].cells[4].style.display = "none";108            }109        };110        /**111         * Sets this.MathDecider, using its existing MapScreenr as its constants.112         *113         * @param FSM114         * @param customs   Any optional custom settings.115         */116        FullScreenMario.prototype.resetMathDecider = function (FSM, customs) {117            FSM.MathDecider = new MathDecidr.MathDecidr(FSM.proliferate({118                "constants": FSM.MapScreener119            }, FSM.settings.math));120        };121        /**122         * Sets this.container via the parent GameStartr resetContaienr.123         *124         * The container is given the "Press Start" font, the PixelRender is told125         * to draw the scenery, solid, character, and text groups, and the container126         * width is set to the custom's width.127         *128         * @param FSM129         * @param customs   Any optional custom settings.130         */131        FullScreenMario.prototype.resetContainer = function (FSM, settings) {132            _super.prototype.resetContainer.call(this, FSM, settings);133            FSM.container.style.fontFamily = "Press Start";134            FSM.container.className += " FullScreenMario";135            FSM.PixelDrawer.setThingArrays([136                FSM.GroupHolder.getGroup("Scenery"),137                FSM.GroupHolder.getGroup("Solid"),138                FSM.GroupHolder.getGroup("Character"),139                FSM.GroupHolder.getGroup("Text")140            ]);141            FSM.ItemsHolder.getContainer().style.width = settings.width + "px";142            FSM.container.appendChild(FSM.ItemsHolder.getContainer());143        };144        /* Global manipulations145        */146        /**147         * Completely restarts the game. Lives are reset to 3, the map goes back148         * to default, and the onGameStart mod trigger is fired.149         */150        FullScreenMario.prototype.gameStart = function () {151            var FSM = FullScreenMario.prototype.ensureCorrectCaller(this);152            FSM.setMap(FSM.settings.maps.mapDefault, FSM.settings.maps.locationDefault);153            FSM.ItemsHolder.setItem("lives", FSM.settings.items.values.lives.valueDefault);154            FSM.ModAttacher.fireEvent("onGameStart");155        };156        /**157         * Completely ends the game. All Thing groups are clared, sounds are158         * stopped, the screen goes to black, "GAME OVER" is displayed. After a159         * while, the game restarts again via gameStart.160         */161        FullScreenMario.prototype.gameOver = function () {162            var FSM = FullScreenMario.prototype.ensureCorrectCaller(this), text = FSM.ObjectMaker.make("CustomText", {163                "texts": [{164                        "text": "GAME OVER"165                    }]166            }), texts, textWidth, i;167            FSM.killNPCs();168            FSM.AudioPlayer.clearAll();169            FSM.AudioPlayer.play("Game Over");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) {4971                    if (solid.killonend.constructor === Function) {4972                        solid.killonend(solid, group, i);4973                    }4974                    else {4975                        solid.FSM.arrayDeleteThing(solid, group, i);4976                    }4977                }4978            }4979        };4980        /**4981         * Kill Function for Bricks. The Brick is killed an an animateBrickShards4982         * animation is timed. If other is provided, it's also marked as the Brick's4983         * up, which will kill colliding characters: this works because4984         * maintainSolids happens before maintainCharacters, so the killNormal won't4985         * come into play until after the next maintainCharacters call.4986         *4987         * @param thing   A Brick to kill.4988         * @param other   An optional Character to mark as the cause of the4989         *                Brick's death (its up attribute).4990         */4991        FullScreenMario.prototype.killBrick = function (thing, other) {4992            thing.FSM.AudioPlayer.play("Break Block");4993            thing.FSM.TimeHandler.addEvent(thing.FSM.animateBrickShards, 1, thing);4994            thing.FSM.killNormal(thing);4995            if (other instanceof thing.FSM.ObjectMaker.getFunction("Thing")) {4996                thing.up = other;4997            }4998            else {4999                thing.up = undefined;5000            }5001        };5002        /**5003         * Kill Function for a Player. It's big and complicated, but in general...5004         * 1. If big === 2, just kill it altogether5005         * 2. If a Player is large and big isn't true, just power down a Player.5006         * 3. A player can't survive this, so animate the "shrug" class and an5007         *    up-then-down movement.5008         * At the end of 1. and 3., decrease the "lives" and "power" statistics and5009         * call the equivalent onPlayerDeath or onGameOver callbacks, depending on5010         * how many lives are left. The mod event is also fired.5011         *5012         * @param thing   A Player to kill.5013         * @param big   The severity of this death: 0 for normal, 1 for not5014         *              survivable, or 2 for immediate death.5015         */5016        FullScreenMario.prototype.killPlayer = function (thing, big) {5017            if (!thing.alive || thing.flickering || thing.dieing) {5018                return;5019            }5020            var FSM = thing.FSM, area = thing.FSM.AreaSpawner.getArea();5021            // Large big: real, no-animation death5022            if (big === 2) {5023                thing.dead = thing.dieing = true;5024                thing.alive = false;5025                FSM.MapScreener.notime = true;5026            }5027            else {5028                // Regular big: regular (enemy, time, etc.) kill5029                // If a Player can survive this, just power down5030                if (!big && thing.power > 1) {5031                    thing.power = 1;5032                    FSM.ItemsHolder.setItem("power", 1);5033                    FSM.AudioPlayer.play("Power Down");5034                    FSM.playerGetsSmall(thing);5035                    return;5036                }5037                else {5038                    // a Player can't survive this: animate a death5039                    thing.dieing = true;5040                    FSM.setSize(thing, 7.5, 7, true);5041                    FSM.updateSize(thing);5042                    FSM.setClass(thing, "character player dead");5043                    FSM.animateCharacterPauseVelocity(thing);5044                    FSM.arrayToEnd(thing, FSM.GroupHolder.getGroup(thing.groupType));5045                    FSM.MapScreener.notime = true;5046                    FSM.MapScreener.nokeys = true;5047                    FSM.TimeHandler.cancelAllCycles(thing);5048                    FSM.TimeHandler.addEvent(function () {5049                        FSM.animateCharacterResumeVelocity(thing, true);5050                        thing.nocollide = true;5051                        thing.movement = thing.resting = undefined;5052                        thing.gravity = FSM.MapScreener.gravity / 2.1;5053                        thing.yvel = FullScreenMario.unitsize * -1.4;5054                    }, 7);5055                }5056            }5057            thing.nocollide = thing.nomove = thing.dead = true;5058            FSM.MapScreener.nokeys = true;5059            FSM.AudioPlayer.clearAll();5060            FSM.AudioPlayer.play("Player Dies");5061            FSM.ItemsHolder.decrease("lives");5062            FSM.ItemsHolder.setItem("power", 1);5063            if (FSM.ItemsHolder.getItem("lives") > 0) {5064                FSM.TimeHandler.addEvent(area.onPlayerDeath.bind(FSM), area.onPlayerDeathTimeout, FSM);5065            }5066            else {5067                FSM.TimeHandler.addEvent(area.onGameOver.bind(FSM), area.onGameOverTimeout, FSM);5068            }5069        };5070        /* Scoring5071        */5072        /**5073         * Determines how many points should be gained from a number of consecutive5074         * enemy deaths (such as via hops or shells).5075         *5076         * @param level   How many deaths have happened.5077         * @returns How many points should be gained (or 0, for having gained a life).5078         */5079        FullScreenMario.prototype.findScore = function (level) {5080            var FSM = FullScreenMario.prototype.ensureCorrectCaller(this);5081            if (level < FSM.pointLevels.length) {5082                return FSM.pointLevels[level];5083            }5084            FSM.gainLife(1);5085            return 0;5086        };5087        /**5088         * Driver function to score some number of points for a Player and show5089         * the gains via an animation.5090         *5091         * @param value   How many points a Player is receiving.5092         * @param continuation   Whether the game shouldn't increase the score5093         *                       amount in the ItemsHoldr (this will only be5094         *                       false on the first score() call).5095         * @remarks For point gains that should not have a visual animation,5096         *          directly call ItemsHolder.increase("score", value).5097         * @remarks The calling chain will be:5098         *              score -> scoreOn -> scoreAnimateOn -> scoreAnimate5099         */5100        FullScreenMario.prototype.score = function (value, continuation) {5101            if (!value) {5102                return;5103            }5104            var FSM = FullScreenMario.prototype.ensureCorrectCaller(this);5105            FSM.scoreOn(value, FSM.player, true);5106            if (!continuation) {5107                this.ItemsHolder.increase("score", value);5108            }5109        };5110        /**5111         * Scores a given number of points for a Player, and shows the gains via5112         * an animation centered at the top of a thing.5113         *5114         * @param value   How many points a Player is receiving.5115         * @param thing   An in-game Thing to place the visual score text5116         *                        on top of and centered.5117         * @param continuation   Whether the game shouldn't increase the score5118         *                       amount in the ItemsHoldr (this will only be5119         *                       false on the first score() call).5120         * @remarks The calling chain will be:5121         *              scoreOn -> scoreAnimateOn -> scoreAnimate5122         */5123        FullScreenMario.prototype.scoreOn = function (value, thing, continuation) {5124            if (!value) {5125                return;5126            }5127            var text = thing.FSM.addThing("Text" + value);5128            thing.FSM.scoreAnimateOn(text, thing);5129            if (!continuation) {5130                this.ItemsHolder.increase("score", value);5131            }5132            thing.FSM.ModAttacher.fireEvent("onScoreOn", value, thing, continuation);5133        };5134        /**5135         * Centers a text associated with some points gain on the top of a Thing,5136         * and animates it updward, setting an event for it to die.5137         *5138         * @param text   The text whose position is being manipulated.5139         * @param thing   An in-game Thing to place the visual score text5140         *                on top of and centered.5141         * @remarks The calling chain will be:5142         *              scoreAnimateOn -> scoreAnimate5143         */5144        FullScreenMario.prototype.scoreAnimateOn = function (text, thing) {5145            thing.FSM.setMidXObj(text, thing);5146            thing.FSM.setBottom(text, thing.top);5147            thing.FSM.scoreAnimate(text);5148        };5149        /**5150         * Animates a score on top of a Thing.5151         *5152         * @param thing   An in-game Thing to place the visual score text5153         *                on top of and centered.5154         * @param timeout   How many game ticks to wait before killing5155         *                  the text (by default, 28).5156         * @remarks This is the last function in the score() calling chain:5157         *              scoreAnimate <- scoreAnimateOn <- scoreOn <- score5158         */5159        FullScreenMario.prototype.scoreAnimate = function (thing, timeout) {5160            if (timeout === void 0) { timeout = 28; }5161            thing.FSM.TimeHandler.addEventInterval(thing.FSM.shiftVert, 1, timeout, thing, -thing.FSM.unitsize / 6);5162            thing.FSM.TimeHandler.addEvent(thing.FSM.killNormal, timeout, thing);5163        };5164        /**5165         * Inelegant catch-all Function for when a Player has hit a shell and5166         * needs points to be scored. This takes into account player star status and5167         * Shell resting and peeking. With none of those modifiers, it defaults to5168         * scoreOn with 400.5169         *5170         * @param thing   A Player hitting other.5171         * @param other   A Shell being hit by thing.5172         * @remarks See http://themushroomkingdom.net/smb_breakdown.shtml5173         */5174        FullScreenMario.prototype.scorePlayerShell = function (thing, other) {5175            // Star player: 200 points5176            if (thing.star) {5177                thing.FSM.scoreOn(200, other);5178                return;5179            }5180            // Shells in the air: 8000 points (see guide)5181            if (!other.resting) {5182                thing.FSM.scoreOn(8000, other);5183                return;5184            }5185            // Peeking shells: 1000 points5186            if (other.peeking) {5187                thing.FSM.scoreOn(1000, other);5188                return;5189            }5190            // Already hopping: 500 points5191            if (thing.jumpcount) {5192                thing.FSM.scoreOn(500, other);5193                return;5194            }5195            // All other cases: the shell's default5196            thing.FSM.scoreOn(400, other);5197        };5198        /**5199         * Determines the amount a Player should score upon hitting a flagpole,5200         * based on the Player's y-position.5201         *5202         * @param thing   A Player hitting a flagpole5203         * @param difference   How far up the pole the collision happened,5204         *                     by absolute amount (not multiplied by5205         *                     unitsize).5206         * @returns How many points to award.5207         * @remarks See http://themushroomkingdom.net/smb_breakdown.shtml5208         */5209        FullScreenMario.prototype.scorePlayerFlag = function (thing, difference) {5210            var amount;5211            if (difference < 28) {5212                amount = difference < 8 ? 100 : 400;5213            }5214            else if (difference < 40) {5215                amount = 800;5216            }5217            else {5218                amount = difference < 62 ? 2000 : 5000;5219            }5220            return amount;5221        };5222        /* Audio5223        */5224        /**5225         * Determines how loud a sound should be at an x-location. This5226         * is louder closer to a Player, and nothing to the right of the5227         * visible screen.5228         *5229         * @param FSM5230         * @param xloc   The x-location of the sound's source.5231         * @returns How loud the sound should be, in [0,1].5232         */5233        FullScreenMario.prototype.getVolumeLocal = function (FSM, xloc) {5234            if (xloc > FSM.MapScreener.right) {5235                return 0;5236            }5237            return Math.max(.14, Math.min(.84, (FSM.MapScreener.width - Math.abs(xloc - FSM.player.left)) / FSM.MapScreener.width));5238        };5239        /**5240         * Determines the name of the default theme for the current area,5241         * which is the first word in the area's setting (split on spaces).5242         *5243         * @param FSM5244         * @returns The default theme for the current area.5245         */5246        FullScreenMario.prototype.getAudioThemeDefault = function (FSM) {5247            return FSM.AreaSpawner.getArea().setting.split(" ")[0];5248        };5249        /* Map sets5250        */5251        /**5252         * Sets the game state to a new map, resetting all Things and inputs in the5253         * process. The mod events are fired.5254         *5255         * @param name   The name of the map (by default, the currently5256         *               played one).5257         * @param location   The name of the location within the map (by5258         *                   default, 0 for the first in Array form).5259         * @remarks Most of the work here is done by setLocation.5260         */5261        FullScreenMario.prototype.setMap = function (name, location) {5262            var FSM = FullScreenMario.prototype.ensureCorrectCaller(this), time, map;5263            if (typeof name === "undefined" || name.constructor === FullScreenMario) {5264                name = FSM.AreaSpawner.getMapName();5265            }5266            map = FSM.AreaSpawner.setMap(name);5267            FSM.ModAttacher.fireEvent("onPreSetMap", map);5268            if (map.seed) {5269                FSM.NumberMaker.resetFromSeed(map.seed);5270            }5271            FSM.ItemsHolder.setItem("world", name);5272            FSM.InputWriter.restartHistory();5273            FSM.ModAttacher.fireEvent("onSetMap", map);5274            FSM.setLocation(location || map.locationDefault || FSM.settings.maps.locationDefault);5275            time = FSM.AreaSpawner.getArea().time || FSM.AreaSpawner.getMap().time;5276            FSM.ItemsHolder.setItem("time", Number(time));5277        };5278        /**5279         * Sets the game state to a location within the current map, resetting all5280         * Things, inputs, the current Area, PixelRender, and MapScreener in the5281         * process. The location's entry Function is called to bring a new Player5282         * into the game. The mod events are fired.5283         *5284         * @param name   The name of the location within the map (by5285         *               default, 0 for the first in Array form).5286         */5287        FullScreenMario.prototype.setLocation = function (name) {5288            if (name === void 0) { name = 0; }5289            var FSM = FullScreenMario.prototype.ensureCorrectCaller(this), location;5290            FSM.MapScreener.nokeys = false;5291            FSM.MapScreener.notime = false;5292            FSM.MapScreener.canscroll = true;5293            FSM.MapScreener.clearScreen();5294            FSM.GroupHolder.clearArrays();5295            FSM.TimeHandler.cancelAllEvents();5296            FSM.AreaSpawner.setLocation((name || 0).toString());5297            FSM.MapScreener.setVariables();5298            location = FSM.AreaSpawner.getLocation((name || 0).toString());5299            FSM.ModAttacher.fireEvent("onPreSetLocation", location);5300            FSM.PixelDrawer.setBackground(FSM.AreaSpawner.getArea().background);5301            FSM.TimeHandler.addEventInterval(FSM.maintainTime, 25, Infinity, FSM);5302            FSM.TimeHandler.addEventInterval(FSM.maintainScenery, 350, Infinity, FSM);5303            FSM.AudioPlayer.clearAll();5304            FSM.AudioPlayer.playTheme();5305            FSM.QuadsKeeper.resetQuadrants();5306            location.entry(FSM, location);5307            FSM.ModAttacher.fireEvent("onSetLocation", location);5308            FSM.GamesRunner.play();5309        };5310        /* Map entrances5311        */5312        /**5313         * Standard map entrance Function for dropping from the ceiling. A new5314         * player is placed 16x16 units away from the top-left corner, with5315         * location.xloc scrolling applied if necessary.5316         *5317         * @param FSM5318         * @param location   The calling Location entering into (by default,5319         *                   not used).5320         */5321        FullScreenMario.prototype.mapEntranceNormal = function (FSM, location) {5322            if (location && location.xloc) {5323                FSM.scrollWindow(location.xloc * FSM.unitsize);5324            }5325            FSM.addPlayer(FSM.unitsize * 16, FSM.unitsize * 16);5326        };5327        /**5328         * Standard map entrance Function for starting on the ground. A new player5329         * is placed 16x16 units away from the top-left corner, with location.xloc5330         * scrolling applied if necessary.5331         *5332         * @param FSM5333         * @param location   The calling Location entering into (by default,5334         *                   not used).5335         */5336        FullScreenMario.prototype.mapEntrancePlain = function (FSM, location) {5337            if (location && location.xloc) {5338                FSM.scrollWindow(location.xloc * FSM.unitsize);5339            }5340            FSM.addPlayer(FSM.unitsize * 16, FSM.MapScreener.floor * FSM.unitsize);5341        };5342        /**5343         * Map entrance Function for starting on the ground and immediately walking5344         * as if in a cutscene. mapEntrancePlain is immediately called, and the5345         * player has movement forced to be walking, with nokeys and notime set to5346         * true.5347         *5348         * @param FSM5349         * @param location   The calling Location entering into (by default,5350         *                   not used).5351         */5352        FullScreenMario.prototype.mapEntranceWalking = function (FSM, location) {5353            FSM.mapEntrancePlain(FSM, location);5354            FSM.player.keys.run = 1;5355            FSM.player.maxspeed = FSM.player.walkspeed;5356            FSM.MapScreener.nokeys = true;5357            FSM.MapScreener.notime = true;5358        };5359        /**5360         * Map entrance Function for entering a castle area. A player is simply5361         * added at 2 x 56.5362         *5363         * @param FSM5364         */5365        FullScreenMario.prototype.mapEntranceCastle = function (FSM) {5366            FSM.addPlayer(FSM.unitsize * 2, FSM.unitsize * 56);5367        };5368        /**5369         * Map entrance Function for entering an area climbing a Vine. The Vine5370         * enters first by growing, then a Player climbs it and hops off. The5371         * player's actions are done via mapEntranceVinePlayer and are triggered5372         * when the Vine's top reaches its threshold.5373         *5374         * @param FSM5375         */5376        FullScreenMario.prototype.mapEntranceVine = function (FSM) {5377            var threshold = FSM.MapScreener.bottom - FSM.unitsize * 40, vine = FSM.addThing("Vine", FSM.unitsize * 32, FSM.MapScreener.bottom + FSM.unitsize * 8);5378            FSM.TimeHandler.addEventInterval(function () {5379                if (vine.top >= threshold) {5380                    return false;5381                }5382                vine.movement = undefined;5383                FSM.mapEntranceVinePlayer(FSM, vine);5384                return true;5385            }, 1, Infinity);5386        };5387        /**5388         * Continuation of mapEntranceVine for a Player's actions. A player5389         * climbs up the Vine; once it reaches the threshold, it hops off using5390         * animatePlayerOffVine.5391         *5392         * @param FSM5393         * @param vine   A Vine bringing a Player up.5394         */5395        FullScreenMario.prototype.mapEntranceVinePlayer = function (FSM, vine) {5396            var threshold = FSM.MapScreener.bottom - FSM.unitsize * 24, speed = FSM.unitsize / -4, player = FSM.addPlayer(FSM.unitsize * 29, FSM.MapScreener.bottom - FSM.unitsize * 4);5397            FSM.shiftVert(player, player.height * FSM.unitsize);5398            FSM.collideVine(player, vine);5399            FSM.TimeHandler.addEventInterval(function () {5400                FSM.shiftVert(player, speed);5401                if (player.top < threshold) {5402                    FSM.TimeHandler.addEvent(FSM.animatePlayerOffVine, 49, player);5403                    return true;5404                }5405                return false;5406            }, 1, Infinity);5407        };5408        /**5409         * Map entrance Function for coming in through a vertical Pipe. A player5410         * is added just below the top of the Pipe, and is animated to rise up5411         * through it like an Italian chestburster.5412         *5413         * @param FSM5414         * @param location   The calling Location entering into (by default,5415         *                   not used).5416         */5417        FullScreenMario.prototype.mapEntrancePipeVertical = function (FSM, location) {5418            if (location && location.xloc) {5419                FSM.scrollWindow(location.xloc * FSM.unitsize);5420            }5421            FSM.addPlayer(location.entrance.left + FSM.player.width * FSM.unitsize / 2, location.entrance.top + FSM.player.height * FSM.unitsize);5422            FSM.animatePlayerPipingStart(FSM.player);5423            FSM.AudioPlayer.play("Pipe");5424            FSM.AudioPlayer.addEventListener("Pipe", "ended", function () {5425                FSM.AudioPlayer.playTheme();5426            });5427            FSM.TimeHandler.addEventInterval(function () {5428                FSM.shiftVert(FSM.player, FSM.unitsize / -4);5429                if (FSM.player.bottom <= location.entrance.top) {5430                    FSM.animatePlayerPipingEnd(FSM.player);5431                    return true;5432                }5433                return false;5434            }, 1, Infinity);5435        };5436        /**5437         * Map entrance Function for coming in through a horizontal Pipe. A player5438         * is added just to the left of the entrance, and is animated to pass5439         * through it like an Italian chestburster.5440         *5441         * @param FSM5442         * @param location   The calling Location entering into (by default,5443         *                   not used).5444         */5445        FullScreenMario.prototype.mapEntrancePipeHorizontal = function (FSM, location) {5446            throw new Error("mapEntrancePipeHorizontal is not yet implemented.");5447        };5448        /**5449         * Map entrance Function for a Player reincarnating into a level,5450         * typically from a random map. A player is placed at 16 x 0 and a5451         * Resting Stone placed some spaces below via playerAddRestingStone.5452         *5453         * @param FSM5454         */5455        FullScreenMario.prototype.mapEntranceRespawn = function (FSM) {5456            FSM.MapScreener.nokeys = false;5457            FSM.MapScreener.notime = false;5458            FSM.MapScreener.canscroll = true;5459            FSM.addPlayer(FSM.unitsize * 16, 0);5460            FSM.animateFlicker(FSM.player);5461            if (!FSM.MapScreener.underwater) {5462                FSM.playerAddRestingStone(FSM.player);5463            }5464            FSM.ModAttacher.fireEvent("onPlayerRespawn");5465        };5466        /* Map exits5467        */5468        /**5469         * Map exit Function for leaving through a vertical Pipe. A player is5470         * animated to pass through it and then transfer locations.5471         *5472         * @param thing   A Player exiting through other.5473         * @param other   A Pipe sucking in thing.5474         */5475        FullScreenMario.prototype.mapExitPipeVertical = function (thing, other) {5476            if (!thing.resting || typeof (other.transport) === "undefined"5477                || thing.right + thing.FSM.unitsize * 2 > other.right5478                || thing.left - thing.FSM.unitsize * 2 < other.left) {5479                return;5480            }5481            thing.FSM.animatePlayerPipingStart(thing);5482            thing.FSM.AudioPlayer.play("Pipe");5483            thing.FSM.TimeHandler.addEventInterval(function () {5484                thing.FSM.shiftVert(thing, thing.FSM.unitsize / 4);5485                if (thing.top <= other.top) {5486                    return false;5487                }5488                thing.FSM.TimeHandler.addEvent(function () {5489                    if (other.transport.constructor === Object) {5490                        thing.FSM.setMap(other.transport.map);5491                    }5492                    else {5493                        thing.FSM.setLocation(other.transport);5494                    }5495                }, 42);5496                return true;5497            }, 1, Infinity);5498        };5499        /**5500         * Map exit Function for leaving through a horiontal Pipe. A player is5501         * animated to pass through it and then transfer locations.5502         *5503         * @param thing   A Player exiting through other.5504         * @param other   A Pipe sucking in thing.5505         * @param shouldTransport   Whether not resting and not paddling does5506         *                          not imply a Player cannot pass through the5507         *                          Pipe (by default, false, as this is normal).5508         * @remarks The shouldTransport argument was added because the "Bouncy5509         *          Bounce!" mod rendered some areas unenterable without it.5510         */5511        FullScreenMario.prototype.mapExitPipeHorizontal = function (thing, other, shouldTransport) {5512            if (!shouldTransport && !thing.resting && !thing.paddling) {5513                return;5514            }5515            if (thing.top < other.top || thing.bottom > other.bottom) {5516                return;5517            }5518            if (!thing.keys.run) {5519                return;5520            }5521            thing.FSM.animatePlayerPipingStart(thing);5522            thing.FSM.AudioPlayer.play("Pipe");5523            thing.FSM.TimeHandler.addEventInterval(function () {5524                thing.FSM.shiftHoriz(thing, thing.FSM.unitsize / 4);5525                if (thing.left <= other.left) {5526                    return false;5527                }5528                thing.FSM.TimeHandler.addEvent(function () {5529                    thing.FSM.setLocation(other.transport);5530                }, 42);5531                return true;5532            }, 1, Infinity);5533        };5534        /* Map creation5535        */5536        /**5537         * The onMake callback for Areas. Attributes are copied as specified in the5538         * prototype, and the background is set based on the setting.5539         *5540         * @remarks The scope for this will be an Area.5541         */5542        FullScreenMario.prototype.initializeArea = function () {5543            var scope = this, i;5544            // Copy all attributes, if they exist5545            if (scope.attributes) {5546                for (i in scope.attributes) {5547                    if (scope.hasOwnProperty(i) && scope[i]) {5548                        FullScreenMario.prototype.proliferate(scope, scope.attributes[i]);5549                    }5550                }5551            }5552            scope.setBackground(scope);5553        };5554        /**5555         * Sets an area's background as a function of its setting.5556         *5557         * @param area   An Area having its background set.5558         * @remarks In the future, it might be more elegant to make Areas inherit5559         * from base Area types (Overworld, etc.) so this inelegant switch5560         * statement doesn't have to be used.5561         */5562        FullScreenMario.prototype.setAreaBackground = function (area) {5563            // Non-underwater Underworld, Castle, and all Nights: black background5564            if (area.setting.indexOf("Underwater") === -15565                && (area.setting.indexOf("Underworld") !== -15566                    || area.setting.indexOf("Castle") !== -15567                    || area.setting.indexOf("Night") !== -1)) {5568                area.background = "#000000";5569            }5570            else {5571                // Default (typically Overworld): sky blue background5572                area.background = "#5c94fc";5573            }5574        };5575        /**5576         * Determines the absolute height of a y-location, which is the distance5577         * from the absolute base (bottom of the user's viewport) to a specific5578         * height above the floor.5579         *5580         * @param yloc   A height to find the distance to the floor from.5581         * @param correctUnitsize   Whether the yloc accounts for unitsize5582         *                          expansion (e.g. 48 rather than 12, for5583         *                          unitsize=4).5584         * @returns The absolute height of the y-location.5585         */5586        FullScreenMario.prototype.getAbsoluteHeight = function (yloc, correctUnitsize) {5587            var FSM = FullScreenMario.prototype.ensureCorrectCaller(this), height = yloc + FSM.MapScreener.height;5588            if (!correctUnitsize) {5589                height *= FSM.unitsize;5590            }5591            return height;5592        };5593        /**5594         * Adds a PreThing to the map and stretches it to fit a width equal to the5595         * current map's outermost boundaries.5596         *5597         * @param prethingRaw   A raw PreThing descriptor.5598         * @returns A strethed Thing, newly added via addThing.5599         */5600        FullScreenMario.prototype.mapAddStretched = function (prethingRaw) {5601            var FSM = FullScreenMario.prototype.ensureCorrectCaller(this), boundaries = FSM.AreaSpawner.getArea().boundaries, prething = prethingRaw instanceof String5602                ? { "thing": prething }5603                : prethingRaw, y = ((FSM.MapScreener.floor - prething.y)5604                * FSM.unitsize), 5605            // It is assumed the PreThing does have a .thing if it's a stretch5606            thing = FSM.ObjectMaker.make(prething.thing, {5607                "width": boundaries.right - boundaries.left,5608                "height": prething.height || FSM.getAbsoluteHeight(prething.y)5609            });5610            return FSM.addThing(thing, boundaries.left, y);5611        };5612        /**5613         * Analyzes a PreThing to be placed to the right of the current map's5614         * boundaries (after everything else).5615         *5616         * @param prethingRaw   A raw PreThing descriptor.5617         */5618        FullScreenMario.prototype.mapAddAfter = function (prethingRaw) {5619            var FSM = FullScreenMario.prototype.ensureCorrectCaller(this), MapsCreator = FSM.MapsCreator, AreaSpawner = FSM.AreaSpawner, prethings = AreaSpawner.getPreThings(), prething = prethingRaw instanceof String5620                ? {5621                    "thing": prething5622                }5623                : prethingRaw, area = AreaSpawner.getArea(), map = AreaSpawner.getMap(), boundaries = FSM.AreaSpawner.getArea().boundaries;5624            prething.x = boundaries.right;5625            MapsCreator.analyzePreSwitch(prething, prethings, area, map);5626        };5627        /* Cutscenes5628        */5629        /**5630         * First cutscene for the Flagpole routine. A player becomes invincible and5631         * starts sliding down the flagpole, while all other Things are killed.5632         * A score calculated by scorePlayerFlag is shown at the base of the pole and5633         * works its way up. The collideFlagBottom callback will be fired when a Player5634         * reaches the bottom.5635         *5636         * @param FSM5637         * @param settings   Storage for the cutscene from ScenePlayr.5638         */5639        FullScreenMario.prototype.cutsceneFlagpoleStartSlidingDown = function (FSM, settings) {5640            var thing = settings.player, other = settings.collider, height = (other.bottom - thing.bottom) | 0, scoreAmount = FSM.scorePlayerFlag(thing, height / FSM.unitsize), scoreThing = FSM.ObjectMaker.make("Text" + scoreAmount);5641            // This is a cutscene. No movement, no deaths, no scrolling.5642            thing.star = 1;5643            thing.nocollidechar = true;5644            FSM.MapScreener.nokeys = true;5645            FSM.MapScreener.notime = true;5646            FSM.MapScreener.canscroll = false;5647            // Kill all other characters and pause a Player next to the pole5648            FSM.killNPCs();5649            FSM.animateCharacterPauseVelocity(thing);5650            FSM.setRight(thing, other.left + FSM.unitsize * 3);5651            FSM.killNormal(other);5652            // a Player is now climbing down the pole5653            FSM.removeClasses(thing, "running jumping skidding");5654            FSM.addClass(thing, "climbing animated");5655            FSM.TimeHandler.addClassCycle(thing, ["one", "two"], "climbing", 0);5656            // Animate the Flag to the base of the pole5657            FSM.TimeHandler.addEventInterval(FSM.shiftVert, 1, 64, other.collection.Flag, FSM.unitsize);5658            // Add a ScoreText element at the bottom of the flag and animate it up5659            FSM.addThing(scoreThing, other.right, other.bottom);5660            FSM.TimeHandler.addEventInterval(FSM.shiftVert, 1, 72, scoreThing, -FSM.unitsize);5661            FSM.TimeHandler.addEvent(FSM.ItemsHolder.increase.bind(FSM.ItemsHolder), 72, "score", scoreAmount);5662            // All audio stops, and the flagpole clip is played5663            FSM.AudioPlayer.clearAll();5664            FSM.AudioPlayer.clearTheme();5665            FSM.AudioPlayer.play("Flagpole");5666            FSM.TimeHandler.addEventInterval(function () {5667                // While a Player hasn't reached the bottom yet, slide down5668                if (thing.bottom < other.bottom) {5669                    FSM.shiftVert(thing, FSM.unitsize);5670                    return false;5671                }5672                // If the flag hasn't reached it but a Player has, don't move yet5673                if ((other.collection.Flag.bottom | 0) < (other.bottom | 0)) {5674                    return false;5675                }5676                // a Player is done climbing: trigger the flag bottom collision5677                thing.movement = undefined;5678                FSM.setBottom(thing, other.bottom);5679                FSM.TimeHandler.cancelClassCycle(thing, "climbing");5680                FSM.TimeHandler.addEvent(FSM.ScenePlayer.bindRoutine("HitBottom"), 21);5681                return true;5682            }, 1, Infinity);5683        };5684        /**5685         * Routine for when a player hits the bottom of a flagpole. It is5686         * flipped horizontally, shifted to the other side of the pole, and the5687         * animatePlayerOffPole callback is quickly timed.5688         *5689         * @param FSM5690         * @param settings   Storage for the cutscene from ScenePlayr.5691         */5692        FullScreenMario.prototype.cutsceneFlagpoleHitBottom = function (FSM, settings) {5693            var thing = settings.player;5694            thing.keys.run = 1;5695            thing.maxspeed = thing.walkspeed;5696            thing.FSM.flipHoriz(thing);5697            thing.FSM.shiftHoriz(thing, (thing.width + 1) * thing.FSM.unitsize);5698            thing.FSM.TimeHandler.addEvent(function () {5699                thing.FSM.AudioPlayer.play("Stage Clear");5700                thing.FSM.animatePlayerOffPole(thing, true);5701            }, 14);5702        };5703        /**5704         * Routine for counting down time and increasing score at the end of5705         * a level. When it's done, it calls the Fireworks routine.5706         *5707         * @param FSM5708         * @param settings   Storage for the cutscene from ScenePlayr.5709         */5710        FullScreenMario.prototype.cutsceneFlagpoleCountdown = function (FSM, settings) {5711            FSM.TimeHandler.addEventInterval(function () {5712                FSM.ItemsHolder.decrease("time");5713                FSM.ItemsHolder.increase("score", 50);5714                FSM.AudioPlayer.play("Coin");5715                if (FSM.ItemsHolder.getItem("time") > 0) {5716                    return false;5717                }5718                FSM.TimeHandler.addEvent(FSM.ScenePlayer.bindRoutine("Fireworks"), 35);5719                return true;5720            }, 1, Infinity);5721        };5722        /**5723         * Animation routine for the fireworks found at the end of EndOutsideCastle.5724         * Fireworks are added on a timer (if there should be any), and the level5725         * transport is called when any fireworks are done.5726         *5727         * @param FSM5728         * @param settings   Storage for the cutscene from ScenePlayr.5729         */5730        FullScreenMario.prototype.cutsceneFlagpoleFireworks = function (FSM, settings) {5731            var numFireworks = FSM.MathDecider.compute("numberOfFireworks", settings.time), player = settings.player, detector = settings.detector, doorRight = detector.left, doorLeft = doorRight - FSM.unitsize * 8, doorBottom = detector.bottom, doorTop = doorBottom - FSM.unitsize * 16, flag = FSM.ObjectMaker.make("CastleFlag", {5732                "position": "beginning"5733            }), flagMovements = 28, fireInterval = 28, fireworkPositions = [5734                [0, -48],5735                [-8, -40],5736                [8, -40],5737                [-8, -32],5738                [0, -48],5739                [-8, -40]5740            ], i = 0, firework, position;5741            // Add a flag to the center of the castle, behind everything else5742            FSM.addThing(flag, doorLeft + FSM.unitsize, doorTop - FSM.unitsize * 24);5743            FSM.arrayToBeginning(flag, FSM.GroupHolder.getGroup(flag.groupType));5744            // Animate the flag raising5745            FSM.TimeHandler.addEventInterval(function () {5746                FSM.shiftVert(flag, FSM.unitsize * -.25);5747            }, 1, flagMovements);5748            // If there should be fireworks, add each of them on an interval5749            if (numFireworks > 0) {5750                FSM.TimeHandler.addEventInterval(function () {5751                    position = fireworkPositions[i];5752                    firework = FSM.addThing("Firework", player.left + position[0] * FSM.unitsize, player.top + position[1] * FSM.unitsize);5753                    firework.animate(firework);5754                    i += 1;5755                }, fireInterval, numFireworks);5756            }5757            // After everything, activate the detector's transport to leave5758            FSM.TimeHandler.addEvent(function () {5759                FSM.AudioPlayer.addEventImmediate("Stage Clear", "ended", function () {5760                    FSM.collideLevelTransport(player, detector);5761                    FSM.ScenePlayer.stopCutscene();5762                });5763            }, i * fireInterval + 420);5764        };5765        /**5766         * Routine for when a player collides with a castle axe. All unimportant NPCs5767         * are killed and a Player running again is scheduled.5768         *5769         * @param FSM5770         * @param settings   Storage for the cutscene from ScenePlayr.5771         */5772        FullScreenMario.prototype.cutsceneBowserVictoryCollideCastleAxe = function (FSM, settings) {5773            var player = settings.player, axe = settings.axe;5774            FSM.animateCharacterPauseVelocity(player);5775            FSM.killNormal(axe);5776            FSM.killNPCs();5777            FSM.AudioPlayer.clearTheme();5778            FSM.MapScreener.nokeys = true;5779            FSM.MapScreener.notime = true;5780            player.FSM.TimeHandler.addEvent(function () {5781                player.keys.run = 1;5782                player.maxspeed = player.walkspeed;5783                FSM.animateCharacterResumeVelocity(player);5784                player.yvel = 0;5785                FSM.MapScreener.canscroll = true;5786                FSM.AudioPlayer.play("World Clear");5787            }, 140);5788        };5789        /**5790         * Routine for a castle bridge opening. Its width is reduced repeatedly on an5791         * interval until it's 0, at which point the BowserFalls routine plays.5792         *5793         * @param FSM5794         * @param settings   Storage for the cutscene from ScenePlayr.5795         * @remarks The castle bridge's animateCastleBridgeOpen (called via killNPCs5796         *          as the bridge's .killonend attribute) is what triggers this.5797         */5798        FullScreenMario.prototype.cutsceneBowserVictoryCastleBridgeOpen = function (FSM, settings) {5799            var bridge = settings.routineArguments[0];5800            FSM.TimeHandler.addEventInterval(function () {5801                bridge.right -= FSM.unitsize * 2;5802                FSM.setWidth(bridge, bridge.width - 2);5803                FSM.AudioPlayer.play("Break Block");5804                if (bridge.width <= 0) {5805                    FSM.ScenePlayer.playRoutine("BowserFalls");5806                    return true;5807                }5808                return false;5809            }, 1, Infinity);5810        };5811        /**5812         * Routine for Bowser falling after his bridge opens.5813         *5814         * @param settings   Storage for the cutscene from ScenePlayr.5815         * @param FSM5816         * @remarks This is called by the CastleBridgeOpen routine, once the bridge5817         *          has been reduced to no width.5818         */5819        FullScreenMario.prototype.cutsceneBowserVictoryBowserFalls = function (FSM, settings) {5820            FSM.AudioPlayer.play("Bowser Falls");5821            // Bowser won't exist if a Player already killed him with a star or fireballs5822            if (settings.bowser) {5823                settings.bowser.nofall = true;5824            }5825        };5826        /**5827         * Routine for displaying text above a castle NPC. Each "layer" of text5828         * is added in order, after which collideLevelTransport is called.5829         *5830         * @param settings   Storage for the cutscene from ScenePlayr.5831         * @param FSM5832         * @remarks This is called by collideCastleNPC.5833         */5834        FullScreenMario.prototype.cutsceneBowserVictoryDialog = function (FSM, settings) {5835            var player = settings.player, detector = settings.detector, keys = settings.keys, interval = 140, i = 0, j, letters;5836            player.keys.run = 0;5837            player.FSM.killNormal(detector);5838            player.FSM.TimeHandler.addEventInterval(function () {5839                letters = detector.collection[keys[i]].children;5840                for (j = 0; j < letters.length; j += 1) {5841                    if (letters[j].title !== "TextSpace") {5842                        letters[j].hidden = false;5843                    }5844                }5845                i += 1;5846            }, interval, keys.length);5847            player.FSM.TimeHandler.addEvent(function () {5848                player.FSM.collideLevelTransport(player, detector);5849            }, 280 + interval * keys.length);5850        };5851        /* Map macros5852        */5853        /**5854         * Macro to place a single type of Thing multiple times, drawing from a5855         * bottom/left corner to a top/right corner.5856         *5857         * @alias Fill5858         * @param reference   Settings for a FillPreThings macro.5859         * @param prethings   The container Area's creation commands.5860         * @param area   The container Area.5861         * @param map   The container Map.5862         * @param FSM   The calling FullScreenMario.5863         * @returns A single type of Thing any number of times.5864         */5865        FullScreenMario.prototype.macroFillPreThings = function (reference, prethings, area, map, FSM) {5866            var defaults = FSM.ObjectMaker.getFullPropertiesOf(reference.thing), xnum = reference.xnum || 1, ynum = reference.ynum || 1, xwidth = reference.xwidth || defaults.width, yheight = reference.yheight || defaults.height, x = reference.x || 0, yref = reference.y || 0, outputs = [], output, o = 0, y, i, j;5867            for (i = 0; i < xnum; ++i) {5868                y = yref;5869                for (j = 0; j < ynum; ++j) {5870                    output = {5871                        "x": x,5872                        "y": y,5873                        "macro": undefined5874                    };5875                    outputs.push(FSM.proliferate(output, reference, true));5876                    o += 1;5877                    y += yheight;5878                }5879                x += xwidth;5880            }5881            return outputs;5882        };5883        /**5884         * Macro to continuously place a listing of Things multiple times, from left5885         * to right. This is commonly used for repeating background scenery.5886         *5887         * @alias Pattern5888         * @param reference   Settings for a FillPrePattern macro.5889         * @param prethings   The container Area's creation commands.5890         * @param area   The container Area.5891         * @param map   The container Map.5892         * @param FSM   The calling FullScreenMario.5893         * @returns Preset Things in a pattern.5894         */5895        FullScreenMario.prototype.macroFillPrePattern = function (reference, prethings, area, map, FSM) {5896            if (!FSM.settings.maps.patterns[reference.pattern]) {5897                throw new Error("An unknown pattern is referenced: " + reference);5898            }5899            var pattern = FSM.settings.maps.patterns[reference.pattern], length = pattern.length, defaults = FSM.ObjectMaker.getFullProperties(), repeats = reference.repeat || 1, xpos = reference.x || 0, ypos = reference.y || 0, outputs = [], o = 0, skips = {}, prething, output, i, j;5900            // If skips are given, record them in an Object for quick access5901            if (typeof reference.skips !== "undefined") {5902                for (i = 0; i < reference.skips.length; i += 1) {5903                    skips[reference.skips[i]] = true;5904                }5905            }5906            // For each time the pattern should be repeated:5907            for (i = 0; i < repeats; i += 1) {5908                // For each Thing listing in the pattern:5909                for (j = 0; j < length; j += 1) {5910                    // Don't place if marked in skips5911                    if (skips[j]) {5912                        continue;5913                    }5914                    prething = pattern[j];5915                    output = {5916                        "thing": prething[0],5917                        "x": xpos + prething[1],5918                        "y": ypos + prething[2]5919                    };5920                    output.y += defaults[prething[0]].height;5921                    if (prething[3]) {5922                        output.width = prething[3];5923                    }5924                    outputs.push(output);5925                    o += 1;5926                }5927                xpos += pattern.width;5928            }5929            return outputs;5930        };5931        /**5932         * Macro to place a Floor Thing with infinite height. All settings are5933         * passed in except "macro", which becomes undefined.5934         *5935         * @alias Floor5936         * @param reference   Settings for a Floor macro.5937         * @param prethings   The container Area's creation commands.5938         * @param area   The container Area.5939         * @param map   The container Map.5940         * @param FSM   The calling FullScreenMario.5941         * @returns A single Floor.5942         */5943        FullScreenMario.prototype.macroFloor = function (reference, prethings, area, map, FSM) {5944            var x = reference.x || 0, y = reference.y || 0, floor = FSM.proliferate({5945                "thing": "Floor",5946                "x": x,5947                "y": y,5948                "width": (reference.width || 8),5949                "height": "Infinity"5950            }, reference, true);5951            floor.macro = undefined;5952            return floor;5953        };5954        /**5955         * Macro to place a Pipe, possibly with a pirahna, location hooks, and/or5956         * infinite height. All settings are copied to Pipe except for "macro",5957         * which becomes undefined.5958         *5959         * @alias Pipe5960         * @param reference   Settings for a Pipe macro.5961         * @param prethings   The container Area's creation commands.5962         * @param area   The container Area.5963         * @param map   The container Map.5964         * @param FSM   The calling FullScreenMario.5965         * @returns A Pipe, and potentially a Piranha.5966         */5967        FullScreenMario.prototype.macroPipe = function (reference, prethings, area, map, scope) {5968            var x = reference.x || 0, y = reference.y || 0, height = reference.height || 16, pipe = FullScreenMario.prototype.proliferate({5969                "thing": "Pipe",5970                "x": x,5971                "y": y,5972                "width": 16,5973                "height": reference.height === Infinity5974                    ? "Infinity"5975                    : reference.height || 85976            }, reference, true), output = [pipe];5977            pipe.macro = undefined;5978            if (height === "Infinity" || height === Infinity) {5979                pipe.height = scope.MapScreener.height;5980            }5981            else {5982                pipe.y += height;5983            }5984            if (reference.piranha) {5985                output.push({5986                    "thing": "Piranha",5987                    "x": x + 4,5988                    "y": pipe.y + 12,5989                    "onPipe": true5990                });5991            }5992            return output;5993        };5994        /**5995         * Macro to place a horizontal Pipe with a vertical one, likely with5996         * location hooks.5997         *5998         * @alias PipeCorner5999         * @param reference   Settings for a PipeCorner macro.6000         * @param prethings   The container Area's creation commands.6001         * @param area   The container Area.6002         * @param map   The container Map.6003         * @param FSM   The calling FullScreenMario.6004         * @returns A horizontal Pipe and a vertical Pipe.6005         */6006        FullScreenMario.prototype.macroPipeCorner = function (reference, prethings, area, map, scope) {6007            var x = reference.x || 0, y = reference.y || 0, height = reference.height || 16, output = [6008                {6009                    "thing": "PipeHorizontal",6010                    "x": x,6011                    "y": y,6012                    "transport": reference.transport || 06013                },6014                {6015                    "thing": "PipeVertical",6016                    "x": x + 16,6017                    "y": y + height - 16,6018                    "height": height6019                }6020            ];6021            if (reference.scrollEnabler) {6022                output.push({6023                    "thing": "ScrollEnabler",6024                    "x": x + 16,6025                    "y": y + height + 48,6026                    "height": 64,6027                    "width": 166028                });6029            }6030            if (reference.scrollBlocker) {6031                output.push({6032                    "thing": "ScrollBlocker",6033                    "x": x + 326034                });6035            }6036            return output;6037        };6038        /**6039         * Macro to place a Tree.6040         *6041         * @alias Tree6042         * @param reference   Settings for a Tree macro.6043         * @param prethings   The container Area's creation commands.6044         * @param area   The container Area.6045         * @param map   The container Map.6046         * @param FSM   The calling FullScreenMario.6047         * @returns A Tree and its trunk.6048         */6049        FullScreenMario.prototype.macroTree = function (reference, prethings, area, map, scope) {6050            var x = reference.x || 0, y = reference.y || 0, width = reference.width || 24, output = [6051                {6052                    "thing": "TreeTop",6053                    "x": x,6054                    "y": y,6055                    "width": width6056                }6057            ];6058            if (width > 16) {6059                output.push({6060                    "thing": "TreeTrunk",6061                    "x": x + 8,6062                    "y": y - 8,6063                    "width": width - 16,6064                    "height": "Infinity",6065                    "groupType": reference.solidTrunk ? "Solid" : "Scenery"6066                });6067            }6068            return output;6069        };6070        /**6071         * Macro to place a large Shroom (a Tree that looks like a large Mushroom).6072         *6073         * @alias Shroom6074         * @param reference   Settings for a Shroom macro.6075         * @param prethings   The container Area's creation commands.6076         * @param area   The container Area.6077         * @param map   The container Map.6078         * @param FSM   The calling FullScreenMario.6079         * @returns A Shroom and its trunk.6080         */6081        FullScreenMario.prototype.macroShroom = function (reference, prethings, area, map, scope) {6082            var x = reference.x || 0, y = reference.y || 0, width = reference.width || 24, output = [6083                {6084                    "thing": "ShroomTop",6085                    "x": x,6086                    "y": y,6087                    "width": width6088                }6089            ];6090            if (width > 16) {6091                output.push({6092                    "thing": "ShroomTrunk",6093                    "x": x + (width - 8) / 2,6094                    "y": y - 8,6095                    "height": Infinity,6096                    "groupType": reference.solidTrunk ? "Solid" : "Scenery"6097                });6098            }6099            return output;6100        };6101        /**6102         * Macro to place Water of infinite height. All settings are copied to the6103         * Water except for "macro", which becomes undefined.6104         *6105         * @alias Water6106         * @param reference   Settings for a Water macro.6107         * @param prethings   The container Area's creation commands.6108         * @param area   The container Area.6109         * @param map   The container Map.6110         * @param FSM   The calling FullScreenMario.6111         * @returns A Water scenery.6112         */6113        FullScreenMario.prototype.macroWater = function (reference, prethings, area, map, FSM) {6114            return FSM.proliferate({6115                "thing": "Water",6116                "x": reference.x || 0,6117                "y": (reference.y || 0) + 2,6118                "height": "Infinity",6119                "macro": undefined6120            }, reference, true);6121        };6122        /**6123         * Macro to place a row of Bricks at y = 88.6124         *6125         * @alias Ceiling6126         * @param reference   Settings for a Ceiling macro.6127         * @returns A Brick ceiling.6128         */6129        FullScreenMario.prototype.macroCeiling = function (reference) {6130            return {6131                "macro": "Fill",6132                "thing": "Brick",6133                "x": reference.x,6134                "y": 88,6135                "xnum": (reference.width / 8) | 0,6136                "xwidth": 86137            };6138        };6139        /**6140         * Macro to place a bridge, possibly with columns at the start and/or end.6141         *6142         * @alias Bridge6143         * @param reference   Settings for a Bridge macro.6144         * @returns A bridge.6145         */6146        FullScreenMario.prototype.macroBridge = function (reference) {6147            var x = reference.x || 0, y = reference.y || 0, width = Math.max(reference.width || 0, 16), output = [];6148            // A beginning column reduces the width and pushes it forward6149            if (reference.begin) {6150                width -= 8;6151                output.push({6152                    "thing": "Stone",6153                    "x": x,6154                    "y": y,6155                    "height": "Infinity"6156                });6157                x += 8;6158            }6159            // An ending column just reduces the width 6160            if (reference.end) {6161                width -= 8;6162                output.push({6163                    "thing": "Stone",6164                    "x": x + width,6165                    "y": y,6166                    "height": "Infinity"6167                });6168            }6169            // Between any columns is a BridgeBase with a Railing on top6170            output.push({ "thing": "BridgeBase", "x": x, "y": y, "width": width });6171            output.push({ "thing": "Railing", "x": x, "y": y + 4, "width": width });6172            return output;6173        };6174        /**6175         * Macro to place a scale, which is two Platforms seemingly suspended6176         * by Strings.6177         *6178         * @alias Scale6179         * @param reference   Settings for a Scale macro.6180         * @param prethings   The container Area's creation commands.6181         * @param area   The container Area.6182         * @param map   The container Map.6183         * @param FSM   The calling FullScreenMario.6184         * @returns A scale group.6185         */6186        FullScreenMario.prototype.macroScale = function (reference, prethings, area, map, FSM) {6187            var x = reference.x || 0, y = reference.y || 0, unitsize = FSM.unitsize, widthLeft = reference.widthLeft || 24, widthRight = reference.widthRight || 24, between = reference.between || 40, dropLeft = reference.dropLeft || 24, dropRight = reference.dropRight || 24, collectionName = "ScaleCollection--" + [6188                x, y, widthLeft, widthRight, dropLeft, dropRight6189            ].join(",");6190            return [6191                {6192                    "thing": "String",6193                    "x": x,6194                    "y": y - 4,6195                    "height": dropLeft - 4,6196                    "collectionName": collectionName,6197                    "collectionKey": "stringLeft"6198                },6199                {6200                    "thing": "String",6201                    "x": x + between,6202                    "y": y - 4,6203                    "height": dropRight - 4,6204                    "collectionName": collectionName,6205                    "collectionKey": "stringRight"6206                }, {6207                    "thing": "String",6208                    "x": x + 4,6209                    "y": y,6210                    "width": between - 7,6211                    "collectionName": collectionName,6212                    "collectionKey": "stringMiddle"6213                }, {6214                    "thing": "StringCornerLeft",6215                    "x": x,6216                    "y": y6217                }, {6218                    "thing": "StringCornerRight",6219                    "x": x + between - 4,6220                    "y": y6221                }, {6222                    "thing": "Platform",6223                    "x": x - (widthLeft / 2),6224                    "y": y - dropLeft,6225                    "width": widthLeft,6226                    "inScale": true,6227                    "tension": (dropLeft - 1.5) * unitsize,6228                    "onThingAdd": FSM.spawnScalePlatform,6229                    "collectionName": collectionName,6230                    "collectionKey": "platformLeft"6231                }, {6232                    "thing": "Platform",6233                    "x": x + between - (widthRight / 2),6234                    "y": y - dropRight,6235                    "width": widthRight,6236                    "inScale": true,6237                    "tension": (dropRight - 1.5) * unitsize,6238                    "onThingAdd": FSM.spawnScalePlatform,6239                    "collectionName": collectionName,6240                    "collectionKey": "platformRight"6241                }];6242        };6243        /**6244         * Macro to place Platforms traveling and spawning vertically.6245         *6246         * @alias PlatformGenerator6247         * @param reference   Settings for a PlatformGenerator macro.6248         * @param prethings   The container Area's creation commands.6249         * @param area   The container Area.6250         * @param map   The container Map.6251         * @param FSM   The calling FullScreenMario.6252         * @returns Multiple Platforms.6253         */6254        FullScreenMario.prototype.macroPlatformGenerator = function (reference, prethings, area, map, FSM) {6255            var output = [], direction = reference.direction || 1, levels = direction > 0 ? [0, 48] : [8, 56], width = reference.width || 16, x = reference.x || 0, yvel = direction * FSM.unitsize * .42, i;6256            for (i = 0; i < levels.length; i += 1) {6257                output.push({6258                    "thing": "Platform",6259                    "x": x,6260                    "y": levels[i],6261                    "width": width,6262                    "yvel": yvel,6263                    "movement": FSM.movePlatformSpawn6264                });6265            }6266            output.push({6267                "thing": "PlatformString",6268                "x": x + (width / 2) - .5,6269                "y": FSM.MapScreener.floor,6270                "width": 1,6271                "height": FSM.MapScreener.height / FSM.unitsize6272            });6273            return output;6274        };6275        /**6276         * Macro to place a Warp World group of Pipes, Texts, Piranhas, and6277         * detectors.6278         *6279         * @alias WarpWorld6280         * @param reference   Settings for a WarpWorld macro.6281         * @param prethings   The container Area's creation commands.6282         * @param area   The container Area.6283         * @param map   The container Map.6284         * @param FSM   The calling FullScreenMario.6285         * @returns A Warp World group.6286         */6287        FullScreenMario.prototype.macroWarpWorld = function (reference, prethings, area, map, FSM) {6288            var output = [], x = reference.x || 0, y = reference.y || 0, textHeight = reference.hasOwnProperty("textHeight") ? reference.textHeight : 8, warps = reference.warps, collectionName = "WarpWorldCollection-" + warps.join("."), keys = [], i;6289            output.push({6290                "thing": "CustomText",6291                "x": x + 8,6292                "y": y + textHeight + 56,6293                "texts": [{6294                        "text": "WELCOME TO WARP WORLD!"6295                    }],6296                "textAttributes": {6297                    "hidden": true6298                },6299                "collectionName": collectionName,6300                "collectionKey": "Welcomer"6301            });6302            output.push({6303                "thing": "DetectCollision",6304                "x": x + 64,6305                "y": y + 174,6306                "width": 40,6307                "height": 102,6308                "activate": FSM.activateWarpWorld,6309                "collectionName": collectionName,6310                "collectionKey": "Detector"6311            });6312            for (i = 0; i < warps.length; i += 1) {6313                keys.push(i);6314                output.push({6315                    "macro": "Pipe",6316                    "x": x + 8 + i * 32,6317                    "height": 24,6318                    "transport": { "map": warps[i] + "-1" },6319                    "collectionName": collectionName,6320                    "collectionKey": i + "-Pipe"6321                });6322                output.push({6323                    "thing": "Piranha",6324                    "x": x + 12 + i * 32,6325                    "y": y + 36,6326                    "collectionName": collectionName,6327                    "collectionKey": i + "-Piranha"6328                });6329                output.push({6330                    "thing": "CustomText",6331                    "x": x + 14 + i * 32,6332                    "y": y + 32 + textHeight,6333                    "texts": [{6334                            "text": String(warps[i])6335                        }],6336                    "textAttributes": {6337                        "hidden": true6338                    },6339                    "collectionName": collectionName,6340                    "collectionKey": i + "-Text"6341                });6342            }6343            if (warps.length === 1) {6344                for (i = 2; i < output.length; i += 1) {6345                    output[i].x += 32;6346                }6347            }6348            return output;6349        };6350        /**6351         * Macro to place a DetectCollision that will start the map spawning random6352         * CheepCheeps intermittently.6353         *6354         * @alias CheepsStart6355         * @param reference   Settings for a CheepsStart macro.6356         * @param prethings   The container Area's creation commands.6357         * @param area   The container Area.6358         * @param map   The container Map.6359         * @param FSM   The calling FullScreenMario.6360         * @returns A detector to start spawning CheepCheeps.6361         */6362        FullScreenMario.prototype.macroCheepsStart = function (reference, prethings, area, map, FSM) {6363            return {6364                "thing": "DetectCollision",6365                "x": reference.x || 0,6366                "y": FSM.MapScreener.floor,6367                "width": reference.width || 8,6368                "height": FSM.MapScreener.height / FSM.unitsize,6369                "activate": FSM.activateCheepsStart6370            };6371        };6372        /**6373         * Macro to place a DetectCollision that will stop the map spawning random6374         * CheepCheeps intermittently.6375         *6376         * @alias CheepsStop6377         * @param reference   Settings for a CheepsStop macro.6378         * @param prethings   The container Area's creation commands.6379         * @param area   The container Area.6380         * @param map   The container Map.6381         * @param FSM   The calling FullScreenMario.6382         * @returns A detector to stop spawning CheepCheeps.6383         */6384        FullScreenMario.prototype.macroCheepsStop = function (reference, prethings, area, map, FSM) {6385            return {6386                "thing": "DetectCollision",6387                "x": reference.x || 0,6388                "y": FSM.MapScreener.floor,6389                "width": reference.width || 8,6390                "height": FSM.MapScreener.height / FSM.unitsize,6391                "activate": FSM.activateCheepsStop6392            };6393        };6394        /**6395         * Macro to place a DetectCollision that will start the map spawning random6396         * BulletBills intermittently.6397         *6398         * @alias BulletBillsStart6399         * @param reference   Settings for a BulletBillsStart macro.6400         * @param prethings   The container Area's creation commands.6401         * @param area   The container Area.6402         * @param map   The container Map.6403         * @param FSM   The calling FullScreenMario.6404         * @returns A detector to start spawning BulletBills.6405         */6406        FullScreenMario.prototype.macroBulletBillsStart = function (reference, prethings, area, map, FSM) {6407            return {6408                "thing": "DetectCollision",6409                "x": reference.x || 0,6410                "y": FSM.MapScreener.floor,6411                "width": reference.width || 8,6412                "height": FSM.MapScreener.height / FSM.unitsize,6413                "activate": FSM.activateBulletBillsStart6414            };6415        };6416        /**6417         * Macro to place a DetectCollision that will stop the map spawning random6418         * BulletBills intermittently.6419         *6420         * @alias BulletBillsStop6421         * @param reference   Settings for a BulletBillsStop macro.6422         * @param prethings   The container Area's creation commands.6423         * @param area   The container Area.6424         * @param map   The container Map.6425         * @param FSM   The calling FullScreenMario.6426         * @returns A detector to stop spawning BulletBills.6427         */6428        FullScreenMario.prototype.macroBulletBillsStop = function (reference, prethings, area, map, FSM) {6429            return {6430                "thing": "DetectCollision",6431                "x": reference.x || 0,6432                "y": FSM.MapScreener.floor,6433                "width": reference.width || 8,6434                "height": FSM.MapScreener.height / FSM.unitsize,6435                "activate": FSM.activateBulletBillsStop6436            };6437        };6438        /**6439         * Macro to place a DetectCollision that will tell any current Lakitu to6440         * flee the scene.6441         *6442         * @alias LakituStop6443         * @param reference   Settings for a LakituStop macro.6444         * @param prethings   The container Area's creation commands.6445         * @param area   The container Area.6446         * @param map   The container Map.6447         * @param FSM   The calling FullScreenMario.6448         * @returns A detector to cause any current Lakitu to flee.6449         */6450        FullScreenMario.prototype.macroLakituStop = function (reference, prethings, area, map, FSM) {6451            return {6452                "thing": "DetectCollision",6453                "x": reference.x || 0,6454                "y": FSM.MapScreener.floor,6455                "width": reference.width || 8,6456                "height": FSM.MapScreener.height / FSM.unitsize,6457                "activate": FSM.activateLakituStop6458            };6459        };6460        /**6461         * Macro to place a small castle, which is really a collection of sceneries.6462         *6463         * @alias CastleSmall6464         * @param reference   Settings for a CastleSmall macro.6465         * @param prethings   The container Area's creation commands.6466         * @param area   The container Area.6467         * @param map   The container Map.6468         * @param FSM   The calling FullScreenMario.6469         * @returns A small castle.6470         */6471        FullScreenMario.prototype.macroCastleSmall = function (reference, prethings, area, map, FSM) {6472            var output = [], x = reference.x || 0, y = reference.y || 0, i, j;6473            // Base filling left6474            for (i = 0; i < 2; i += 1) {6475                output.push({6476                    "thing": "BrickHalf",6477                    "x": x + i * 8,6478                    "y": y + 4,6479                    "position": "end"6480                });6481                for (j = 1; j < 3; j += 1) {6482                    output.push({6483                        "thing": "BrickPlain",6484                        "x": x + i * 8,6485                        "y": y + 4 + j * 8,6486                        "position": "end"6487                    });6488                }6489            }6490            // Base filling right6491            for (i = 0; i < 2; i += 1) {6492                output.push({6493                    "thing": "BrickHalf",6494                    "x": x + 24 + i * 8,6495                    "y": y + 4,6496                    "position": "end"6497                });6498                for (j = 1; j < 3; j += 1) {6499                    output.push({6500                        "thing": "BrickPlain",6501                        "x": x + 24 + i * 8,6502                        "y": y + 4 + j * 8,6503                        "position": "end"6504                    });6505                }6506            }6507            // Medium railing left6508            output.push({6509                "thing": "CastleRailing",6510                "x": x,6511                "y": y + 24,6512                "position": "end"6513            });6514            // Medium railing center6515            for (i = 0; i < 3; i += 1) {6516                output.push({6517                    "thing": "CastleRailingFilled",6518                    "x": x + (i + 1) * 8,6519                    "y": y + 24,6520                    "position": "end"6521                });6522            }6523            // Medium railing right6524            output.push({6525                "thing": "CastleRailing",6526                "x": x + 32,6527                "y": y + 24,6528                "position": "end"6529            });6530            // Top railing6531            for (i = 0; i < 3; i += 1) {6532                output.push({6533                    "thing": "CastleRailing",6534                    "x": x + (i + 1) * 8,6535                    "y": y + 40,6536                    "position": "end"6537                });6538            }6539            // Top bricking6540            for (i = 0; i < 2; i += 1) {6541                output.push({6542                    "thing": "CastleTop",6543                    "x": x + 8 + i * 12,6544                    "y": y + 36,6545                    "position": "end"6546                });6547            }6548            // Door, and detector if required6549            output.push({6550                "thing": "CastleDoor",6551                "x": x + 16,6552                "y": y + 20,6553                "position": "end"6554            });6555            if (reference.transport) {6556                output.push({6557                    "thing": "DetectCollision",6558                    "x": x + 24,6559                    "y": y + 16,6560                    "height": 16,6561                    "activate": FSM.collideCastleDoor,6562                    "transport": reference.transport,6563                    "position": "end"6564                });6565            }6566            return output;6567        };6568        /**6569         * Macro to place a large castle, which is really a collection of sceneries6570         * underneath a small castle.6571         *6572         * @alias CastleLarge6573         * @param reference   Settings for a CastleLarge macro.6574         * @param prethings   The container Area's creation commands.6575         * @param area   The container Area.6576         * @param map   The container Map.6577         * @param FSM   The calling FullScreenMario.6578         * @returns A large castle.6579         */6580        FullScreenMario.prototype.macroCastleLarge = function (reference, prethings, area, map, FSM) {6581            var output = [], x = reference.x || 0, y = reference.y || 0, i, j;6582            output.push({6583                "macro": "CastleSmall",6584                "x": x + 16,6585                "y": y + 486586            });6587            // CastleWalls left6588            for (i = 0; i < 2; i += 1) {6589                output.push({6590                    "thing": "CastleWall",6591                    "x": x + i * 8,6592                    "y": y + 486593                });6594            }6595            // Bottom doors with bricks on top6596            for (i = 0; i < 3; i += 1) {6597                output.push({6598                    "thing": "CastleDoor",6599                    "x": x + 16 + i * 16,6600                    "y": y + 20,6601                    "position": "end"6602                });6603                for (j = 0; j < 2; j += 1) {6604                    output.push({6605                        "thing": "BrickPlain",6606                        "x": x + 16 + i * 16,6607                        "y": y + 28 + j * 86608                    });6609                    output.push({6610                        "thing": "BrickHalf",6611                        "x": x + 16 + i * 16,6612                        "y": y + 40 + j * 46613                    });6614                }6615            }6616            // Bottom bricks with doors on top6617            for (i = 0; i < 2; i += 1) {6618                for (j = 0; j < 3; j += 1) {6619                    output.push({6620                        "thing": "BrickPlain",6621                        "x": x + 24 + i * 16,6622                        "y": y + 8 + j * 86623                    });6624                }6625                output.push({6626                    "thing": "CastleDoor",6627                    "x": x + 24 + i * 16,6628                    "y": y + 446629                });6630            }6631            // Railing (filled)6632            for (i = 0; i < 5; i += 1) {6633                output.push({6634                    "thing": "CastleRailingFilled",6635                    "x": x + 16 + i * 8,6636                    "y": y + 486637                });6638            }6639            // CastleWalls right6640            j = reference.hasOwnProperty("walls") ? reference.walls : 2;6641            for (i = 0; i < j; i += 1) {6642                output.push({6643                    "thing": "CastleWall",6644                    "x": x + 56 + i * 8,6645                    "y": y + 48,6646                    "position": "end"6647                });6648            }6649            if (reference.transport) {6650                output.push({6651                    "thing": "DetectCollision",6652                    "x": x + 24,6653                    "y": y + 16,6654                    "height": 16,6655                    "activate": FSM.collideCastleDoor,6656                    "transport": reference.transport,6657                    "position": "end"6658                });6659            }6660            return output;6661        };6662        /**6663         * Macro to place the typical starting Things for the inside of a castle6664         * area.6665         *6666         * @alias StartInsideCastle6667         * @param reference   Settings for a StartInsideCastle macro.6668         * @returns A starting zone for the inside of a castle.6669         */6670        FullScreenMario.prototype.macroStartInsideCastle = function (reference) {6671            var x = reference.x || 0, y = reference.y || 0, width = (reference.width || 0) - 40, output = [6672                {6673                    "thing": "Stone",6674                    "x": x,6675                    "y": y + 48,6676                    "width": 24,6677                    "height": Infinity6678                },6679                {6680                    "thing": "Stone",6681                    "x": x + 24,6682                    "y": y + 40,6683                    "width": 8,6684                    "height": Infinity6685                },6686                {6687                    "thing": "Stone",6688                    "x": x + 32,6689                    "y": y + 32,6690                    "width": 8,6691                    "height": Infinity6692                }];6693            if (width > 0) {6694                output.push({6695                    "macro": "Floor",6696                    "x": x + 40,6697                    "y": y + 24,6698                    "width": width6699                });6700            }6701            return output;6702        };6703        /**6704         * Macro to place the typical ending Things for the inside of an outdoor6705         * area.6706         *6707         * @alias EndOutsideCastle6708         * @param reference   Settings for an EndOutsideCastle macro.6709         * @param prethings   The container Area's creation commands.6710         * @param area   The container Area.6711         * @param map   The container Map.6712         * @param FSM   The calling FullScreenMario.6713         * @returns An outdoors castle ending.6714         */6715        FullScreenMario.prototype.macroEndOutsideCastle = function (reference, prethings, area, map, FSM) {6716            var x = reference.x || 0, y = reference.y || 0, collectionName = "EndOutsideCastle-" + [6717                reference.x, reference.y, reference.large6718            ].join(","), output;6719            // Output starts off with the general flag & collision detection6720            output = [6721                // Initial collision detector6722                {6723                    "thing": "DetectCollision", x: x, y: y + 108, height: 100,6724                    "activate": FullScreenMario.prototype.collideFlagpole,6725                    "activateFail": FullScreenMario.prototype.killNormal,6726                    "noActivateDeath": true,6727                    "collectionName": collectionName,6728                    "collectionKey": "DetectCollision"6729                },6730                // Flag (scenery)6731                {6732                    "thing": "Flag", "x": x - 4.5, "y": y + 79.5,6733                    "collectionName": collectionName,6734                    "collectionKey": "Flag"6735                },6736                {6737                    "thing": "FlagTop", "x": x + 1.5, "y": y + 84,6738                    "collectionName": collectionName,6739                    "collectionKey": "FlagTop"6740                },6741                {6742                    "thing": "FlagPole", "x": x + 3, "y": y + 80,6743                    "collectionName": collectionName,6744                    "collectionKey": "FlagPole"6745                },6746                // Bottom stone6747                {6748                    "thing": "Stone", "x": x, "y": y + 8,6749                    "collectionName": collectionName,6750                    "collectionKey": "FlagPole"6751                }];6752            if (reference.large) {6753                output.push({6754                    "macro": "CastleLarge",6755                    "x": x + (reference.castleDistance || 24),6756                    "y": y,6757                    "transport": reference.transport,6758                    "walls": reference.walls || 86759                });6760            }6761            else {6762                output.push({6763                    "macro": "CastleSmall",6764                    "x": x + (reference.castleDistance || 32),6765                    "y": y,6766                    "transport": reference.transport6767                });6768            }6769            return output;6770        };6771        /**6772         * Macro to place the typical ending Things for the inside of a castle area.6773         *6774         * @alias EndInsideCastle6775         * @param reference   Settings for an EndInsideCastle macro.6776         * @param prethings   The container Area's creation commands.6777         * @param area   The container Area.6778         * @param map   The container Map.6779         * @param FSM   The calling FullScreenMario.6780         * @returns An ending zone for inside a castle.6781         */6782        FullScreenMario.prototype.macroEndInsideCastle = function (reference, prethings, area, map, FSM) {6783            var x = reference.x || 0, y = reference.y || 0, npc = reference.npc || "Toad", output, texts, keys;6784            if (npc === "Toad") {6785                keys = ["1", "2"];6786                texts = [6787                    {6788                        "thing": "CustomText",6789                        "x": x + 164,6790                        "y": y + 64,6791                        "texts": [{6792                                "text": "THANK YOU MARIO!"6793                            }],6794                        "textAttributes": {6795                            "hidden": true6796                        },6797                        "collectionName": "endInsideCastleText",6798                        "collectionKey": "1"6799                    }, {6800                        "thing": "CustomText",6801                        "x": x + 152,6802                        "y": y + 48,6803                        "texts": [6804                            {6805                                "text": "BUT OUR PRINCESS IS IN"6806                            }, {6807                                "text": "ANOTHER CASTLE!"6808                            }],6809                        "textAttributes": {6810                            "hidden": true6811                        },6812                        "collectionName": "endInsideCastleText",6813                        "collectionKey": "2"6814                    }];6815            }6816            else if (npc === "Peach") {6817                keys = ["1", "2", "3"];6818                texts = [6819                    {6820                        "thing": "CustomText",6821                        "x": x + 164,6822                        "y": y + 64,6823                        "texts": [{6824                                "text": "THANK YOU MARIO!"6825                            }],6826                        "textAttributes": {6827                            "hidden": true6828                        },6829                        "collectionName": "endInsideCastleText",6830                        "collectionKey": "1"6831                    }, {6832                        "thing": "CustomText",6833                        "x": x + 152,6834                        "y": y + 48,6835                        "texts": [6836                            {6837                                "text": "YOUR QUEST IS OVER.",6838                                "offset": 126839                            }, {6840                                "text": "WE PRESENT YOU A NEW QUEST."6841                            }],6842                        "textAttributes": {6843                            "hidden": true6844                        },6845                        "collectionName": "endInsideCastleText",6846                        "collectionKey": "2"6847                    }, {6848                        "thing": "CustomText",6849                        "x": x + 152,6850                        "y": 32,6851                        "texts": [6852                            {6853                                "text": "PRESS BUTTON B",6854                                "offset": 86855                            }, {6856                                "text": "TO SELECT A WORLD"6857                            }],6858                        "textAttributes": {6859                            "hidden": true6860                        },6861                        "collectionName": "endInsideCastleText",6862                        "collectionKey": "3"6863                    }];6864            }6865            output = [6866                { "thing": "Stone", "x": x, "y": y + 88, "width": 256 },6867                { "macro": "Water", "x": x, "y": y, "width": 104 },6868                // Bridge & Bowser area6869                { "thing": "CastleBridge", "x": x, "y": y + 24, "width": 104 },6870                {6871                    "thing": "Bowser", "x": x + 69, "y": y + 42,6872                    "hard": reference.hard,6873                    "spawnType": reference.spawnType || "Goomba",6874                    "throwing": reference.throwing6875                },6876                { "thing": "CastleChain", "x": x + 96, "y": y + 32 },6877                // Axe area6878                { "thing": "CastleAxe", "x": x + 104, "y": y + 40 },6879                { "thing": "ScrollBlocker", "x": x + 112 },6880                { "macro": "Floor", "x": x + 104, "y": y, "width": 152 },6881                {6882                    "thing": "Stone", "x": x + 104, "y": y + 32,6883                    "width": 24, "height": 326884                },6885                {6886                    "thing": "Stone", "x": x + 112, "y": y + 80,6887                    "width": 16, "height": 246888                },6889                // Peach's Magical Happy Chamber of Fantastic Love6890                {6891                    "thing": "DetectCollision", "x": x + 180,6892                    "activate": FSM.collideCastleNPC,6893                    "transport": reference.transport,6894                    "collectionName": "endInsideCastleText",6895                    "collectionKey": "npc",6896                    "collectionKeys": keys6897                },6898                { "thing": npc, "x": x + 200, "y": 13 },6899                { "thing": "ScrollBlocker", "x": x + 256 }6900            ];6901            if (reference.topScrollEnabler) {6902                output.push({6903                    "thing": "ScrollEnabler",6904                    "x": x + 96, "y": y + 140,6905                    "height": 52, "width": 166906                });6907                output.push({6908                    "thing": "ScrollEnabler",6909                    "x": x + 240, "y": y + 140,6910                    "height": 52, "width": 166911                });6912            }6913            output.push.apply(output, texts);6914            return output;6915        };6916        /**6917         * Macro to place a DetectSpawn that will call activateSectionBefore to6918         * start a stretch section.6919         *6920         * @alias Section6921         * @param reference   Settings for a Section macro.6922         * @param prethings   The container Area's creation commands.6923         * @param area   The container Area.6924         * @param map   The container Map.6925         * @param FSM   The calling FullScreenMario.6926         * @returns A section.6927         */6928        FullScreenMario.prototype.macroSection = function (reference, prethings, area, map, FSM) {6929            return {6930                "thing": "DetectSpawn",6931                "x": reference.x || 0,6932                "y": reference.y || 0,6933                "activate": FSM.activateSectionBefore,6934                "section": reference.section || 06935            };6936        };6937        /**6938         * Macro to place a DetectCollision to mark the current section as passed.6939         *6940         * @alias SectionPass6941         * @param reference   Settings for a SectionPass macro.6942         * @returns A section pass detector.6943         */6944        FullScreenMario.prototype.macroSectionPass = function (reference) {6945            return {6946                "thing": "DetectCollision",6947                "x": reference.x || 0,6948                "y": reference.y || 0,6949                "width": reference.width || 8,6950                "height": reference.height || 8,6951                "activate": function (thing) {6952                    thing.FSM.AudioPlayer.play("Coin");6953                    thing.FSM.MapScreener.sectionPassed = true;6954                }6955            };6956        };6957        /**6958         * Macro to place a DetectCollision to mark the current section as failed.6959         *6960         * @alias SectionFail6961         * @param reference   Settings for a SectionFail macro.6962         * @returns A section fail detector.6963         */6964        FullScreenMario.prototype.macroSectionFail = function (reference) {6965            return [6966                {6967                    "thing": "DetectCollision",6968                    "x": reference.x,6969                    "y": reference.y,6970                    "width": reference.width || 8,6971                    "height": reference.height || 8,6972                    "activate": function (thing) {6973                        thing.FSM.AudioPlayer.play("Fail");6974                        thing.FSM.MapScreener.sectionPassed = false;6975                    }6976                }6977            ];6978        };6979        /**6980         * Macro to place a DetectSpawn that will spawn a following section based on6981         * whether the current one was marked as passed or failed.6982         *6983         * @alias SectionDecider6984         * @param reference   Settings for a SectionDecider macro.6985         * @param prethings   The container Area's creation commands.6986         * @param area   The container Area.6987         * @param map   The container Map.6988         * @param FSM   The calling FullScreenMario.6989         * @returns A section decider detector.6990         */6991        FullScreenMario.prototype.macroSectionDecider = function (reference) {6992            return {6993                "thing": "DetectSpawn",6994                "x": reference.x || 0,6995                "y": reference.y || 0,6996                "activate": function (thing) {6997                    if (thing.FSM.MapScreener.sectionPassed) {6998                        thing.section = reference.pass || 0;6999                    }7000                    else {7001                        thing.section = reference.fail || 0;7002                    }7003                    thing.FSM.activateSectionBefore(thing);7004                }7005            };7006        };7007        /* Miscellaneous utilities7008        */7009        /**7010         * Ensures the current object is a GameStartr by throwing an error if it7011         * is not. This should be used for functions in any GameStartr descendants7012         * that have to call 'this' to ensure their caller is what the programmer7013         * expected it to be.7014         *7015         * @param {Mixed} current7016         */7017        FullScreenMario.prototype.ensureCorrectCaller = function (current) {7018            if (!(current instanceof FullScreenMario)) {7019                throw new Error("A function requires the scope ('this') to be the "7020                    + "manipulated FullScreenMario object. Unfortunately, 'this' is a "7021                    + typeof (this) + ".");7022            }7023            return current;7024        };7025        // For the sake of reset functions, constants are stored as members of the 7026        // FullScreenMario Function itself - this allows prototype setters to use 7027        // them regardless of whether the prototype has been instantiated yet.7028        /**7029         * Static settings passed to individual reset Functions. Each of these7030         * should be filled out separately, after the FullScreenMario class7031         * has been declared but before an instance has been instantiated.7032         */7033        FullScreenMario.settings = {7034            "audio": undefined,7035            "collisions": undefined,7036            "devices": undefined,7037            "editor": undefined,7038            "generator": undefined,7039            "groups": undefined,7040            "events": undefined,7041            "help": undefined,7042            "input": undefined,7043            "math": undefined,7044            "maps": undefined,7045            "mods": undefined,7046            "objects": undefined,7047            "quadrants": undefined,7048            "renderer": undefined,7049            "runner": undefined,7050            "scenes": undefined,7051            "sprites": undefined,7052            "items": undefined,7053            "touch": undefined,7054            "ui": undefined7055        };7056        /**7057         * How much to expand each pixel from raw sizing measurements to in-game.7058         */7059        FullScreenMario.unitsize = 4;7060        /**7061         * How much to scale each pixel from PixelDrawr to the real canvas.7062         */7063        FullScreenMario.scale = 2;7064        /**7065         * How much falling Characters accelerate downward by default.7066         */7067        FullScreenMario.gravity = Math.round(12 * FullScreenMario.unitsize) / 100;7068        /**7069         * Levels of points to award for hopping on / shelling enemies.7070         */7071        FullScreenMario.pointLevels = [100, 200, 400, 500, 800, 1000, 2000, 4000, 5000, 8000];7072        /**7073         * Useful for custom text Things, where "text!" cannot be a Function name.7074         */7075        FullScreenMario.customTextMappings = {7076            " ": "Space",7077            ".": "Period",7078            "!": "ExclamationMark",7079            ":": "Colon",7080            "/": "Slash",7081            "©": "Copyright"7082        };7083        return FullScreenMario;7084    })(GameStartr.GameStartr);7085    FullScreenMario_1.FullScreenMario = FullScreenMario;...GameStartr-0.2.0.js
Source:GameStartr-0.2.0.js  
1/// <reference path="AreaSpawnr-0.2.0.ts" />2/// <reference path="AudioPlayr-0.2.1.ts" />3/// <reference path="ChangeLinr-0.2.0.ts" />4/// <reference path="DeviceLayr-0.2.0.ts" />5/// <reference path="EightBittr-0.2.0.ts" />6/// <reference path="FPSAnalyzr-0.2.1.ts" />7/// <reference path="GamesRunnr-0.2.0.ts" />8/// <reference path="GroupHoldr-0.2.1.ts" />9/// <reference path="InputWritr-0.2.0.ts" />10/// <reference path="ItemsHoldr-0.2.1.ts" />11/// <reference path="LevelEditr-0.2.0.ts" />12/// <reference path="MapsCreatr-0.2.1.ts" />13/// <reference path="MapScreenr-0.2.1.ts" />14/// <reference path="MathDecidr-0.2.0.ts" />15/// <reference path="ModAttachr-0.2.2.ts" />16/// <reference path="NumberMakr-0.2.2.ts" />17/// <reference path="ObjectMakr-0.2.2.ts" />18/// <reference path="PixelDrawr-0.2.0.ts" />19/// <reference path="PixelRendr-0.2.0.ts" />20/// <reference path="QuadsKeepr-0.2.1.ts" />21/// <reference path="ScenePlayr-0.2.0.ts" />22/// <reference path="StringFilr-0.2.1.ts" />23/// <reference path="ThingHittr-0.2.0.ts" />24/// <reference path="TimeHandlr-0.2.0.ts" />25/// <reference path="TouchPassr-0.2.0.ts" />26/// <reference path="UsageHelpr-0.2.0.ts" />27/// <reference path="UserWrappr-0.2.0.ts" />28/// <reference path="WorldSeedr-0.2.0.ts" />29/// <reference path="js_beautify.ts" />30var __extends = (this && this.__extends) || function (d, b) {31    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];32    function __() { this.constructor = d; }33    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());34};35var GameStartr;36(function (GameStartr_1) {37    "use strict";38    /**39     * A general-use game engine for 2D 8-bit games.40     */41    var GameStartr = (function (_super) {42        __extends(GameStartr, _super);43        /**44         * Initializes a new instance of the GameStartr class.45         *46         * @param customs   Any optional custom settings.47         */48        function GameStartr(settings) {49            if (settings === void 0) { settings = {}; }50            _super.call(this, {51                "unitsize": settings.unitsize,52                "constantsSource": settings.constantsSource,53                "constants": settings.constants54            });55            /**56             * Default list of reset Functions to call during this.reset or this.resetTimed,57             * in order of when they should be called.58             */59            this.resets = [60                "resetUsageHelper",61                "resetObjectMaker",62                "resetPixelRender",63                "resetTimeHandler",64                "resetItemsHolder",65                "resetAudioPlayer",66                "resetQuadsKeeper",67                "resetGamesRunner",68                "resetGroupHolder",69                "resetThingHitter",70                "resetMapScreener",71                "resetPixelDrawer",72                "resetNumberMaker",73                "resetMapsCreator",74                "resetAreaSpawner",75                "resetInputWriter",76                "resetDeviceLayer",77                "resetTouchPasser",78                "resetLevelEditor",79                "resetWorldSeeder",80                "resetScenePlayer",81                "resetMathDecider",82                "resetModAttacher",83                "startModAttacher",84                "resetContainer"85            ];86            if (settings.extraResets) {87                this.resets.push.apply(this.resets, settings.extraResets);88            }89            if (settings.resetTimed) {90                this.resetTimed(this, settings);91            }92            else {93                this.reset(this, settings);94            }95        }96        /* Resets97        */98        /**99         * Resets the GameStartr by calling the parent EightBittr.prototype.reset.100         *101         * @param GameStarter102         * @param customs   Any optional custom settings.103         */104        GameStartr.prototype.reset = function (GameStarter, settings) {105            _super.prototype.reset.call(this, GameStarter, GameStarter.resets, settings);106        };107        /**108         * Resets the EightBittr and records the time by calling the parent109         * EightBittr.prototype.resetTimed.110         *111         * @param GameStarter112         * @param customs   Any optional custom settings.113         */114        GameStartr.prototype.resetTimed = function (GameStarter, settings) {115            _super.prototype.resetTimed.call(this, GameStarter, GameStarter.resets, settings);116        };117        /**118         * Sets this.UsageHelper.119         *120         * @param GameStarter121         * @param customs   Any optional custom settings.122         */123        GameStartr.prototype.resetUsageHelper = function (GameStarter, settings) {124            GameStarter.UsageHelper = new UsageHelpr.UsageHelpr(GameStarter.settings.help);125        };126        /**127         * Sets this.ObjectMaker.128         *129         * Because many Thing functions require access to other GameStartr modules, each is130         * given a reference to this container GameStartr via properties.thing.GameStarter.131         *132         * @param GameStarter133         * @param customs   Any optional custom settings.134         */135        GameStartr.prototype.resetObjectMaker = function (GameStarter, settings) {136            GameStarter.ObjectMaker = new ObjectMakr.ObjectMakr(GameStarter.proliferate({137                "properties": {138                    "Quadrant": {139                        "EightBitter": GameStarter,140                        "GameStarter": GameStarter141                    },142                    "Thing": {143                        "EightBitter": GameStarter,144                        "GameStarter": GameStarter145                    }146                }147            }, GameStarter.settings.objects));148        };149        /**150         * Sets this.QuadsKeeper.151         *152         * @param GameStarter153         * @param customs   Any optional custom settings.154         */155        GameStartr.prototype.resetQuadsKeeper = function (GameStarter, settings) {156            var quadrantWidth = settings.width / (GameStarter.settings.quadrants.numCols - 3), quadrantHeight = settings.height / (GameStarter.settings.quadrants.numRows - 2);157            GameStarter.QuadsKeeper = new QuadsKeepr.QuadsKeepr(GameStarter.proliferate({158                "ObjectMaker": GameStarter.ObjectMaker,159                "createCanvas": GameStarter.createCanvas,160                "quadrantWidth": quadrantWidth,161                "quadrantHeight": quadrantHeight,162                "startLeft": -quadrantWidth,163                "startHeight": -quadrantHeight,164                "onAdd": GameStarter.onAreaSpawn.bind(GameStarter, GameStarter),165                "onRemove": GameStarter.onAreaUnspawn.bind(GameStarter, GameStarter)166            }, GameStarter.settings.quadrants));167        };168        /**169         * Sets this.PixelRender.170         *171         * @param GameStarter172         * @param customs   Any optional custom settings.173         */174        GameStartr.prototype.resetPixelRender = function (GameStarter, settings) {175            GameStarter.PixelRender = new PixelRendr.PixelRendr(GameStarter.proliferate({176                "scale": GameStarter.scale,177                "QuadsKeeper": GameStarter.QuadsKeeper,178                "unitsize": GameStarter.unitsize179            }, GameStarter.settings.sprites));180        };181        /**182         * Sets this.PixelDrawer.183         *184         * @param GameStarter185         * @param customs   Any optional custom settings.186         */187        GameStartr.prototype.resetPixelDrawer = function (GameStarter, settings) {188            GameStarter.PixelDrawer = new PixelDrawr.PixelDrawr(GameStarter.proliferate({189                "PixelRender": GameStarter.PixelRender,190                "MapScreener": GameStarter.MapScreener,191                "createCanvas": GameStarter.createCanvas,192                "unitsize": GameStarter.unitsize,193                "generateObjectKey": GameStarter.generateThingKey194            }, GameStarter.settings.renderer));195        };196        /**197         * Sets this.TimeHandler.198         *199         * @param GameStarter200         * @param customs   Any optional custom settings.201         */202        GameStartr.prototype.resetTimeHandler = function (GameStarter, settings) {203            GameStarter.TimeHandler = new TimeHandlr.TimeHandlr(GameStarter.proliferate({204                "classAdd": GameStarter.addClass,205                "classRemove": GameStarter.removeClass206            }, GameStarter.settings.events));207        };208        /**209         * Sets this.AudioPlayer.210         *211         * @param GameStarter212         * @param customs   Any optional custom settings.213         */214        GameStartr.prototype.resetAudioPlayer = function (GameStarter, settings) {215            GameStarter.AudioPlayer = new AudioPlayr.AudioPlayr(GameStarter.proliferate({216                "ItemsHolder": GameStarter.ItemsHolder217            }, GameStarter.settings.audio));218        };219        /**220         * Sets this.GamesRunner.221         *222         * @param GameStarter223         * @param customs   Any optional custom settings.224         */225        GameStartr.prototype.resetGamesRunner = function (GameStarter, settings) {226            GameStarter.GamesRunner = new GamesRunnr.GamesRunnr(GameStarter.proliferate({227                "adjustFramerate": true,228                "scope": GameStarter,229                "onPlay": GameStarter.onGamePlay.bind(GameStarter, GameStarter),230                "onPause": GameStarter.onGamePause.bind(GameStarter, GameStarter),231                "FPSAnalyzer": new FPSAnalyzr.FPSAnalyzr()232            }, GameStarter.settings.runner));233            GameStarter.FPSAnalyzer = GameStarter.GamesRunner.getFPSAnalyzer();234        };235        /**236         * Sets this.ItemsHolder.237         *238         * @param GameStarter239         * @param customs   Any optional custom settings.240         */241        GameStartr.prototype.resetItemsHolder = function (GameStarter, settings) {242            GameStarter.ItemsHolder = new ItemsHoldr.ItemsHoldr(GameStarter.proliferate({243                "callbackArgs": [GameStarter]244            }, GameStarter.settings.items));245        };246        /**247         * Sets this.GroupHolder.248         *249         * @param GameStarter250         * @param customs   Any optional custom settings.251         */252        GameStartr.prototype.resetGroupHolder = function (GameStarter, settings) {253            GameStarter.GroupHolder = new GroupHoldr.GroupHoldr(GameStarter.settings.groups);254        };255        /**256         * Sets this.ThingHitter.257         *258         * @param GameStarter259         * @param customs   Any optional custom settings.260         */261        GameStartr.prototype.resetThingHitter = function (GameStarter, settings) {262            GameStarter.ThingHitter = new ThingHittr.ThingHittr(GameStarter.proliferate({263                "scope": GameStarter264            }, GameStarter.settings.collisions));265        };266        /**267         * Sets this.MapScreener.268         *269         * @param GameStarter270         * @param customs   Any optional custom settings.271         */272        GameStartr.prototype.resetMapScreener = function (GameStarter, settings) {273            GameStarter.MapScreener = new MapScreenr.MapScreenr({274                "EightBitter": GameStarter,275                "unitsize": GameStarter.unitsize,276                "width": settings.width,277                "height": settings.height,278                "variableArgs": [GameStarter],279                "variables": GameStarter.settings.maps.screenVariables280            });281        };282        /**283         * Sets this.NumberMaker.284         *285         * @param GameStarter286         * @param customs   Any optional custom settings.287         */288        GameStartr.prototype.resetNumberMaker = function (GameStarter, settings) {289            GameStarter.NumberMaker = new NumberMakr.NumberMakr();290        };291        /**292         * Sets this.MapCreator.293         *294         * @param GameStarter295         * @param customs   Any optional custom settings.296         */297        GameStartr.prototype.resetMapsCreator = function (GameStarter, settings) {298            GameStarter.MapsCreator = new MapsCreatr.MapsCreatr({299                "ObjectMaker": GameStarter.ObjectMaker,300                "groupTypes": GameStarter.settings.maps.groupTypes,301                "macros": GameStarter.settings.maps.macros,302                "entrances": GameStarter.settings.maps.entrances,303                "maps": GameStarter.settings.maps.library,304                "scope": GameStarter305            });306        };307        /**308         * Sets this.AreaSpawner.309         *310         * @param GameStarter311         * @param customs   Any optional custom settings.312         */313        GameStartr.prototype.resetAreaSpawner = function (GameStarter, settings) {314            GameStarter.AreaSpawner = new AreaSpawnr.AreaSpawnr({315                "MapsCreator": GameStarter.MapsCreator,316                "MapScreener": GameStarter.MapScreener,317                "screenAttributes": GameStarter.settings.maps.screenAttributes,318                "onSpawn": GameStarter.settings.maps.onSpawn,319                "onUnspawn": GameStarter.settings.maps.onUnspawn,320                "stretchAdd": GameStarter.settings.maps.stretchAdd,321                "afterAdd": GameStarter.settings.maps.afterAdd,322                "commandScope": GameStarter323            });324        };325        /**326         * Sets this.InputWriter.327         *328         * @param GameStarter329         * @param customs   Any optional custom settings.330         */331        GameStartr.prototype.resetInputWriter = function (GameStarter, settings) {332            GameStarter.InputWriter = new InputWritr.InputWritr(GameStarter.proliferate({333                "canTrigger": GameStarter.canInputsTrigger.bind(GameStarter, GameStarter),334                "eventInformation": GameStarter335            }, GameStarter.settings.input.InputWritrArgs));336        };337        /**338         * Sets this.DeviceLayer.339         *340         * @param GameStarter341         * @param customs   Any optional custom settings.342         */343        GameStartr.prototype.resetDeviceLayer = function (GameStarter, settings) {344            GameStarter.DeviceLayer = new DeviceLayr.DeviceLayr(GameStarter.proliferate({345                "InputWriter": GameStarter.InputWriter346            }, GameStarter.settings.devices));347        };348        /**349         * Sets this.InputWriter.350         *351         * @param GameStarter352         * @param customs   Any optional custom settings.353         */354        GameStartr.prototype.resetTouchPasser = function (GameStarter, settings) {355            GameStarter.TouchPasser = new TouchPassr.TouchPassr(GameStarter.proliferate({356                "InputWriter": GameStarter.InputWriter357            }, GameStarter.settings.touch));358        };359        /**360         * Sets this.LevelEditor.361         *362         * @param GameStarter363         * @param customs   Any optional custom settings.364         */365        GameStartr.prototype.resetLevelEditor = function (GameStarter, settings) {366            GameStarter.LevelEditor = new LevelEditr.LevelEditr(GameStarter.proliferate({367                "GameStarter": GameStarter,368                "beautifier": js_beautify369            }, GameStarter.settings.editor));370        };371        /**372         * Sets this.WorldSeeder.373         *374         * @param GameStarter375         * @param customs   Any optional custom settings.376         */377        GameStartr.prototype.resetWorldSeeder = function (GameStarter, settings) {378            GameStarter.WorldSeeder = new WorldSeedr.WorldSeedr(GameStarter.proliferate({379                "random": GameStarter.NumberMaker.random.bind(GameStarter.NumberMaker),380                "onPlacement": GameStarter.mapPlaceRandomCommands.bind(GameStarter, GameStarter)381            }, GameStarter.settings.generator));382        };383        /**384         * Sets this.ScenePlayer.385         *386         * @param GameStarter387         * @param customs   Any optional custom settings.388         */389        GameStartr.prototype.resetScenePlayer = function (GameStarter, settings) {390            GameStarter.ScenePlayer = new ScenePlayr.ScenePlayr(GameStarter.proliferate({391                "cutsceneArguments": [GameStarter]392            }, GameStarter.settings.scenes));393        };394        /**395         * Sets this.MathDecider.396         *397         * @param GameStarter398         * @param customs   Any optional custom settings.399         */400        GameStartr.prototype.resetMathDecider = function (GameStarter, settings) {401            GameStarter.MathDecider = new MathDecidr.MathDecidr(GameStarter.settings.math);402        };403        /**404         * Sets this.ModAttacher.405         *406         * @param GameStarter407         * @param customs   Any optional custom settings.408         */409        GameStartr.prototype.resetModAttacher = function (GameStarter, settings) {410            GameStarter.ModAttacher = new ModAttachr.ModAttachr(GameStarter.proliferate({411                "scopeDefault": GameStarter,412                "ItemsHoldr": GameStarter.ItemsHolder413            }, GameStarter.settings.mods));414        };415        /**416         * Starts self.ModAttacher. All mods are enabled, and the "onReady" trigger417         * is fired.418         *419         * @param GameStarter420         * @param customs   Any optional custom settings.421         */422        GameStartr.prototype.startModAttacher = function (GameStarter, settings) {423            var mods = settings.mods, i;424            if (mods) {425                for (i in mods) {426                    if (mods.hasOwnProperty(i) && mods[i]) {427                        GameStarter.ModAttacher.enableMod(i);428                    }429                }430            }431            GameStarter.ModAttacher.fireEvent("onReady", GameStarter, GameStarter);432        };433        /**434         * Resets the parent HTML container. Width and height are set by customs,435         * and canvas, ItemsHolder, and TouchPassr container elements are added.436         *437         * @param GameStarter438         * @param customs   Any optional custom settings.439         */440        GameStartr.prototype.resetContainer = function (GameStarter, settings) {441            GameStarter.container = GameStarter.createElement("div", {442                "className": "EightBitter",443                "style": GameStarter.proliferate({444                    "position": "relative",445                    "width": settings.width + "px",446                    "height": settings.height + "px"447                }, settings.style)448            });449            GameStarter.canvas = GameStarter.createCanvas(settings.width, settings.height);450            GameStarter.PixelDrawer.setCanvas(GameStarter.canvas);451            GameStarter.container.appendChild(GameStarter.canvas);452            GameStarter.TouchPasser.setParentContainer(GameStarter.container);453        };454        /* Global manipulations455        */456        /**457         * Scrolls the game window by shifting all Things and checking for quadrant458         * refreshes. Shifts are rounded to the nearest integer, to preserve pixels.459         *460         * @param customs   Any optional custom settings.461         * @param dx   How far to scroll horizontally.462         * @param dy   How far to scroll vertically.463         */464        GameStartr.prototype.scrollWindow = function (dx, dy) {465            var GameStarter = GameStartr.prototype.ensureCorrectCaller(this);466            dx = dx | 0;467            dy = dy | 0;468            if (!dx && !dy) {469                return;470            }471            GameStarter.MapScreener.shift(dx, dy);472            GameStarter.shiftAll(-dx, -dy);473            GameStarter.QuadsKeeper.shiftQuadrants(-dx, -dy);474        };475        /**476         * Scrolls everything but a single Thing.477         *478         * @param thing   The only Thing that shouldn't move on the screen.479         * @param dx   How far to scroll horizontally.480         * @param dy   How far to scroll vertically.481         */482        GameStartr.prototype.scrollThing = function (thing, dx, dy) {483            var saveleft = thing.left, savetop = thing.top;484            thing.GameStarter.scrollWindow(dx, dy);485            thing.GameStarter.setLeft(thing, saveleft);486            thing.GameStarter.setTop(thing, savetop);487        };488        /**489         * Spawns all Things within a given area that should be there.490         *491         * @param GameStarter492         * @param direction   The direction spawning comes from.493         * @param top   A top boundary to spawn within.494         * @param right   A right boundary to spawn within.495         * @param bottom   A bottom boundary to spawn within.496         * @param left   A left boundary to spawn within.497         * @remarks This is generally called by a QuadsKeepr during a screen update.498         */499        GameStartr.prototype.onAreaSpawn = function (GameStarter, direction, top, right, bottom, left) {500            GameStarter.AreaSpawner.spawnArea(direction, (top + GameStarter.MapScreener.top) / GameStarter.unitsize, (right + GameStarter.MapScreener.left) / GameStarter.unitsize, (bottom + GameStarter.MapScreener.top) / GameStarter.unitsize, (left + GameStarter.MapScreener.left) / GameStarter.unitsize);501        };502        /**503         * "Unspawns" all Things within a given area that should be gone by marking504         * their PreThings as not in game.505         *506         * @param GameStarter507         * @param direction   The direction spawning comes from.508         * @param top   A top boundary to spawn within.509         * @param right   A right boundary to spawn within.510         * @param bottom   A bottom boundary to spawn within.511         * @param left   A left boundary to spawn within.512         * @remarks This is generally called by a QuadsKeepr during a screen update.513         */514        GameStartr.prototype.onAreaUnspawn = function (GameStarter, direction, top, right, bottom, left) {515            GameStarter.AreaSpawner.unspawnArea(direction, (top + GameStarter.MapScreener.top) / GameStarter.unitsize, (right + GameStarter.MapScreener.left) / GameStarter.unitsize, (bottom + GameStarter.MapScreener.top) / GameStarter.unitsize, (left + GameStarter.MapScreener.left) / GameStarter.unitsize);516        };517        /**518         * Adds a new Thing to the game at a given position, relative to the top519         * left corner of the screen.520         *521         * @param thingRaw   What type of Thing to add. This may be a String of522         *                   the class title, an Array containing the String523         *                   and an Object of settings, or an actual Thing.524         * @param left   The horizontal point to place the Thing's left at (by default, 0).525         * @param top   The vertical point to place the Thing's top at (by default, 0).526         */527        GameStartr.prototype.addThing = function (thingRaw, left, top) {528            if (left === void 0) { left = 0; }529            if (top === void 0) { top = 0; }530            var thing;531            if (typeof thingRaw === "string" || thingRaw instanceof String) {532                thing = this.ObjectMaker.make(thingRaw);533            }534            else if (thingRaw.constructor === Array) {535                thing = this.ObjectMaker.make.apply(this.ObjectMaker, thingRaw);536            }537            else {538                thing = thingRaw;539            }540            if (arguments.length > 2) {541                thing.GameStarter.setLeft(thing, left);542                thing.GameStarter.setTop(thing, top);543            }544            else if (arguments.length > 1) {545                thing.GameStarter.setLeft(thing, left);546            }547            thing.GameStarter.updateSize(thing);548            thing.GameStarter.GroupHolder.getFunctions().add[thing.groupType](thing);549            thing.placed = true;550            // This will typically be a TimeHandler.cycleClass call551            if (thing.onThingAdd) {552                thing.onThingAdd(thing);553            }554            thing.GameStarter.PixelDrawer.setThingSprite(thing);555            // This will typically be a spawn* call556            if (thing.onThingAdded) {557                thing.onThingAdded(thing);558            }559            thing.GameStarter.ModAttacher.fireEvent("onAddThing", thing, left, top);560            return thing;561        };562        /**563         * Processes a Thing so that it is ready to be placed in gameplay. There are564         * a lot of steps here: width and height must be set with defaults and given565         * to spritewidth and spriteheight, a quadrants Array must be given, the566         * sprite must be set, attributes and onThingMake called upon, and initial567         * class cycles and flipping set.568         *569         * @param thing   The Thing being processed.570         * @param title   What type Thing this is (the name of the class).571         * @param settings   Additional settings to be given to the Thing.572         * @param defaults   The default settings for the Thing's class.573         * @remarks This is generally called as the onMake call in an ObjectMakr.574         */575        GameStartr.prototype.thingProcess = function (thing, title, settings, defaults) {576            var maxQuads = 4, num, cycle;577            // If the Thing doesn't specify its own title, use the type by default578            thing.title = thing.title || title;579            // If a width/height is provided but no spritewidth/height,580            // use the default spritewidth/height581            if (thing.width && !thing.spritewidth) {582                thing.spritewidth = defaults.spritewidth || defaults.width;583            }584            if (thing.height && !thing.spriteheight) {585                thing.spriteheight = defaults.spriteheight || defaults.height;586            }587            // Each thing has at least 4 maximum quadrants for the QuadsKeepr588            num = Math.floor(thing.width * (thing.GameStarter.unitsize / thing.GameStarter.QuadsKeeper.getQuadrantWidth()));589            if (num > 0) {590                maxQuads += ((num + 1) * maxQuads / 2);591            }592            num = Math.floor(thing.height * thing.GameStarter.unitsize / thing.GameStarter.QuadsKeeper.getQuadrantHeight());593            if (num > 0) {594                maxQuads += ((num + 1) * maxQuads / 2);595            }596            thing.maxquads = maxQuads;597            thing.quadrants = new Array(maxQuads);598            // Basic sprite information599            thing.spritewidth = thing.spritewidth || thing.width;600            thing.spriteheight = thing.spriteheight || thing.height;601            // Sprite sizing602            thing.spritewidthpixels = thing.spritewidth * thing.GameStarter.unitsize;603            thing.spriteheightpixels = thing.spriteheight * thing.GameStarter.unitsize;604            // Canvas, context605            thing.canvas = thing.GameStarter.createCanvas(thing.spritewidthpixels, thing.spriteheightpixels);606            thing.context = thing.canvas.getContext("2d");607            if (thing.opacity !== 1) {608                thing.GameStarter.setOpacity(thing, thing.opacity);609            }610            // Attributes, such as Koopa.smart611            if (thing.attributes) {612                thing.GameStarter.thingProcessAttributes(thing, thing.attributes);613            }614            // Important custom functions615            if (thing.onThingMake) {616                thing.onThingMake(thing, settings);617            }618            // Initial class / sprite setting619            thing.GameStarter.setSize(thing, thing.width, thing.height);620            thing.GameStarter.setClassInitial(thing, thing.name || thing.title);621            // Sprite cycles622            if (cycle = thing.spriteCycle) {623                thing.GameStarter.TimeHandler.addClassCycle(thing, cycle[0], cycle[1] || null, cycle[2] || null);624            }625            if (cycle = thing.spriteCycleSynched) {626                thing.GameStarter.TimeHandler.addClassCycleSynched(thing, cycle[0], cycle[1] || null, cycle[2] || null);627            }628            // flipHoriz and flipVert initially 629            if (thing.flipHoriz) {630                thing.GameStarter.flipHoriz(thing);631            }632            if (thing.flipVert) {633                thing.GameStarter.flipVert(thing);634            }635            // Mods!636            thing.GameStarter.ModAttacher.fireEvent("onThingMake", thing.GameStarter, thing, title, settings, defaults);637        };638        /**639         * Processes additional Thing attributes. For each attribute the Thing's640         * class says it may have, if it has it, the attribute's key is appeneded to641         * the Thing's name and the attribute value proliferated onto the Thing.642         *643         * @param thing644         * @param attributes   A lookup of attributes that may be added to the Thing's class.645         */646        GameStartr.prototype.thingProcessAttributes = function (thing, attributes) {647            var attribute;648            // For each listing in the attributes...649            for (attribute in attributes) {650                // If the thing has that attribute as true:651                if (thing[attribute]) {652                    // Add the extra options653                    thing.GameStarter.proliferate(thing, attributes[attribute]);654                    // Also add a marking to the name, which will go into the className655                    if (thing.name) {656                        thing.name += " " + attribute;657                    }658                    else {659                        thing.name = thing.title + " " + attribute;660                    }661                }662            }663        };664        /**665         * Runs through commands generated by a WorldSeedr and evaluates all of666         * to create PreThings via MapsCreator.analyzePreSwitch.667         *668         * @param GameStarter669         * @param generatedCommands   Commands generated by WorldSeedr.generateFull.670         */671        GameStartr.prototype.mapPlaceRandomCommands = function (GameStarter, generatedCommands) {672            var MapsCreator = GameStarter.MapsCreator, AreaSpawner = GameStarter.AreaSpawner, prethings = AreaSpawner.getPreThings(), area = AreaSpawner.getArea(), map = AreaSpawner.getMap(), command, output, i;673            for (i = 0; i < generatedCommands.length; i += 1) {674                command = generatedCommands[i];675                output = {676                    "thing": command.title,677                    "x": command.left,678                    "y": command.top679                };680                if (command.arguments) {681                    GameStarter.proliferateHard(output, command.arguments, true);682                }683                MapsCreator.analyzePreSwitch(output, prethings, area, map);684            }685        };686        /**687         * Triggered Function for when the game is unpaused. Music resumes, and688         * the mod event is fired.689         *690         * @param GameStartr691         */692        GameStartr.prototype.onGamePlay = function (GameStarter) {693            GameStarter.AudioPlayer.resumeAll();694            GameStarter.ModAttacher.fireEvent("onGamePlay");695        };696        /**697         * Triggered Function for when the game is paused. Music stops, and the698         * mod event is fired.699         *700         * @param GameStartr701         */702        GameStartr.prototype.onGamePause = function (GameStarter) {703            GameStarter.AudioPlayer.pauseAll();704            GameStarter.ModAttacher.fireEvent("onGamePause");705        };706        /**707         * Checks whether inputs can be fired, which by default is always true.708         *709         * @param GameStartr710         * @returns Whether inputs can be fired, which is always true.711         */712        GameStartr.prototype.canInputsTrigger = function (GameStarter) {713            return true;714        };715        /**716         * Generic Function to start the game. Nothing actually happens here.717         */718        GameStartr.prototype.gameStart = function () {719            this.ModAttacher.fireEvent("onGameStart");720        };721        /* Physics & similar722        */723        /**724         * Generically kills a Thing by setting its alive to false, hidden to true,725         * and clearing its movement.726         *727         * @param thing728         */729        GameStartr.prototype.killNormal = function (thing) {730            if (!thing) {731                return;732            }733            thing.alive = false;734            thing.hidden = true;735            thing.movement = undefined;736        };737        /**738         * Sets a Thing's "changed" flag to true, which indicates to the PixelDrawr739         * to redraw the Thing and its quadrant.740         *741         * @param thing742         */743        GameStartr.prototype.markChanged = function (thing) {744            thing.changed = true;745        };746        /**747         * Shifts a Thing vertically using the EightBittr utility, and marks the748         * Thing as having a changed appearance.749         *750         * @param thing751         * @param dy   How far to shift the Thing vertically.752         * @param notChanged   Whether to skip marking the Thing as changed (by753         *                     default, false).754         */755        GameStartr.prototype.shiftVert = function (thing, dy, notChanged) {756            EightBittr.EightBittr.prototype.shiftVert(thing, dy);757            if (!notChanged) {758                thing.GameStarter.markChanged(thing);759            }760        };761        /**762         * Shifts a Thing horizontally using the EightBittr utility, and marks the763         * Thing as having a changed appearance.764         *765         * @param thing766         * @param dx   How far to shift the Thing horizontally.767         * @param notChanged   Whether to skip marking the Thing as changed (by768         *                     default, false).769         */770        GameStartr.prototype.shiftHoriz = function (thing, dx, notChanged) {771            EightBittr.EightBittr.prototype.shiftHoriz(thing, dx);772            if (!notChanged) {773                thing.GameStarter.markChanged(thing);774            }775        };776        /**777         * Sets a Thing's top using the EightBittr utility, and marks the Thing as778         * having a changed appearance.779         *780         * @param thing781         * @param top   A new top border for the Thing.782         */783        GameStartr.prototype.setTop = function (thing, top) {784            EightBittr.EightBittr.prototype.setTop(thing, top);785            thing.GameStarter.markChanged(thing);786        };787        /**788         * Sets a Thing's right using the EightBittr utility, and marks the Thing as789         * having a changed appearance.790         *791         * @param thing792         * @param right   A new right border for the Thing.793         */794        GameStartr.prototype.setRight = function (thing, right) {795            EightBittr.EightBittr.prototype.setRight(thing, right);796            thing.GameStarter.markChanged(thing);797        };798        /**799         * Sets a Thing's bottom using the EightBittr utility, and marks the Thing800         * as having a changed appearance.801         *802         * @param thing803         * @param bottom   A new bottom border for the Thing.804         */805        GameStartr.prototype.setBottom = function (thing, bottom) {806            EightBittr.EightBittr.prototype.setBottom(thing, bottom);807            thing.GameStarter.markChanged(thing);808        };809        /**810         * Sets a Thing's left using the EightBittr utility, and marks the Thing811         * as having a changed appearance.812         *813         * @param thing814         * @param left   A new left border for the Thing.815         */816        GameStartr.prototype.setLeft = function (thing, left) {817            EightBittr.EightBittr.prototype.setLeft(thing, left);818            thing.GameStarter.markChanged(thing);819        };820        /**821         * Shifts a thing both horizontally and vertically. If the Thing marks822         * itself as having a parallax effect (parallaxHoriz or parallaxVert), that823         * proportion of movement is respected (.5 = half, etc.).824         *825         * @param thing826         * @param dx   How far to shift the Thing horizontally.827         * @param dy   How far to shift the Thing vertically.828         * @param notChanged   Whether to skip marking the Thing as changed (by829         *                     default, false).830         */831        GameStartr.prototype.shiftBoth = function (thing, dx, dy, notChanged) {832            dx = dx || 0;833            dy = dy || 0;834            if (!thing.noshiftx) {835                if (thing.parallaxHoriz) {836                    thing.GameStarter.shiftHoriz(thing, thing.parallaxHoriz * dx, notChanged);837                }838                else {839                    thing.GameStarter.shiftHoriz(thing, dx, notChanged);840                }841            }842            if (!thing.noshifty) {843                if (thing.parallaxVert) {844                    thing.GameStarter.shiftVert(thing, thing.parallaxVert * dy, notChanged);845                }846                else {847                    thing.GameStarter.shiftVert(thing, dy, notChanged);848                }849            }850        };851        /**852         * Calls shiftBoth on all members of an Array.853         *854         * @param dx   How far to shift the Things horizontally.855         * @param dy   How far to shift the Things vertically.856         * @param notChanged   Whether to skip marking the Things as changed (by857         *                     default, false).858         */859        GameStartr.prototype.shiftThings = function (things, dx, dy, notChanged) {860            for (var i = things.length - 1; i >= 0; i -= 1) {861                things[i].GameStarter.shiftBoth(things[i], dx, dy, notChanged);862            }863        };864        /**865         * Calls shiftBoth on all groups in the calling GameStartr's GroupHoldr.866         *867         * @param dx   How far to shift the Things horizontally.868         * @param dy   How far to shift the Things vertically.869         */870        GameStartr.prototype.shiftAll = function (dx, dy) {871            var GameStarter = GameStartr.prototype.ensureCorrectCaller(this);872            GameStarter.GroupHolder.callAll(GameStarter, GameStarter.shiftThings, dx, dy, true);873        };874        /**875         * Sets the width and unitwidth of a Thing, and optionally updates the876         * Thing's spritewidth and spritewidth pixels, and/or calls updateSize.877         * The thing is marked as having changed appearance.878         *879         * @param thing880         * @param width   A new width for the Thing.881         * @param updateSprite   Whether to update the Thing's spritewidth and882         *                       spritewidthpixels (by default, false).883         * @param updateSize   Whether to call updateSize on the Thing (by884         *                     default, false).885         */886        GameStartr.prototype.setWidth = function (thing, width, updateSprite, updateSize) {887            thing.width = width;888            thing.unitwidth = width * thing.GameStarter.unitsize;889            if (updateSprite) {890                thing.spritewidth = width;891                thing.spritewidthpixels = width * thing.GameStarter.unitsize;892            }893            if (updateSize) {894                thing.GameStarter.updateSize(thing);895            }896            thing.GameStarter.markChanged(thing);897        };898        /**899         * Sets the height and unitheight of a Thing, and optionally updates the900         * Thing's spriteheight and spriteheight pixels, and/or calls updateSize.901         * The thing is marked as having changed appearance.902         *903         * @param thing904         * @param height   A new height for the Thing.905         * @param updateSprite   Whether to update the Thing's spriteheight and906         *                       spriteheightpixels (by default, false).907         * @param updateSize   Whether to call updateSize on the Thing (by908         *                     default, false).909         */910        GameStartr.prototype.setHeight = function (thing, height, updateSprite, updateSize) {911            thing.height = height;912            thing.unitheight = height * thing.GameStarter.unitsize;913            if (updateSprite) {914                thing.spriteheight = height;915                thing.spriteheightpixels = height * thing.GameStarter.unitsize;916            }917            if (updateSize) {918                thing.GameStarter.updateSize(thing);919            }920            thing.GameStarter.markChanged(thing);921        };922        /**923         * Utility to call both setWidth and setHeight on a Thing.924         *925         * @param thing926         * @param width   A new width for the Thing.927         * @param height   A new height for the Thing.928         * @param updateSprite   Whether to update the Thing's spritewidth,929         *                       spriteheight, spritewidthpixels, and930         *                       spritspriteheightpixels (by default, false).931         * @param updateSize   Whether to call updateSize on the Thing (by932         *                     default, false).933         */934        GameStartr.prototype.setSize = function (thing, width, height, updateSprite, updateSize) {935            thing.GameStarter.setWidth(thing, width, updateSprite, updateSize);936            thing.GameStarter.setHeight(thing, height, updateSprite, updateSize);937        };938        /**939         * Shifts a Thing horizontally by its xvel and vertically by its yvel, using940         * shiftHoriz and shiftVert.941         *942         * @param thing943         */944        GameStartr.prototype.updatePosition = function (thing) {945            thing.GameStarter.shiftHoriz(thing, thing.xvel);946            thing.GameStarter.shiftVert(thing, thing.yvel);947        };948        /**949         * Completely updates the size measurements of a Thing. That means the950         * unitwidth, unitheight, spritewidthpixels, spriteheightpixels, and951         * spriteheightpixels attributes. The Thing's sprite is then updated by the952         * PixelDrawer, and its appearance is marked as changed.953         *954         * @param thing955         */956        GameStartr.prototype.updateSize = function (thing) {957            thing.unitwidth = thing.width * thing.GameStarter.unitsize;958            thing.unitheight = thing.height * thing.GameStarter.unitsize;959            thing.spritewidthpixels = thing.spritewidth * thing.GameStarter.unitsize;960            thing.spriteheightpixels = thing.spriteheight * thing.GameStarter.unitsize;961            thing.canvas.width = thing.spritewidthpixels;962            thing.canvas.height = thing.spriteheightpixels;963            thing.GameStarter.PixelDrawer.setThingSprite(thing);964            thing.GameStarter.markChanged(thing);965        };966        /**967         * Reduces a Thing's width by pushing back its right and decreasing its968         * width. It is marked as changed in appearance.969         *970         * @param thing971         * @param dx   How much to reduce the Thing's width.972         * @param updateSize   Whether to also call updateSize on the Thing973         *                     (by default, false).974         */975        GameStartr.prototype.reduceWidth = function (thing, dx, updateSize) {976            thing.right -= dx;977            thing.width -= dx / thing.GameStarter.unitsize;978            if (updateSize) {979                thing.GameStarter.updateSize(thing);980            }981            else {982                thing.GameStarter.markChanged(thing);983            }984        };985        /**986         * Reduces a Thing's height by pushing down its top and decreasing its987         * height. It is marked as changed in appearance.988         *989         * @param thing990         * @param dy   How much to reduce the Thing's height.991         * @param updateSize   Whether to also call updateSize on the Thing992         *                     (by default, false).993         */994        GameStartr.prototype.reduceHeight = function (thing, dy, updateSize) {995            thing.top += dy;996            thing.height -= dy / thing.GameStarter.unitsize;997            if (updateSize) {998                thing.GameStarter.updateSize(thing);999            }1000            else {1001                thing.GameStarter.markChanged(thing);1002            }1003        };1004        /**1005         * Increases a Thing's width by pushing forward its right and decreasing its1006         * width. It is marked as changed in appearance.1007         *1008         * @param thing1009         * @param dx   How much to increase the Thing's width.1010         * @param updateSize   Whether to also call updateSize on the Thing1011         *                     (by default, false).1012         */1013        GameStartr.prototype.increaseWidth = function (thing, dx, updateSize) {1014            thing.right += dx;1015            thing.width += dx / thing.GameStarter.unitsize;1016            thing.unitwidth = thing.width * thing.GameStarter.unitsize;1017            if (updateSize) {1018                thing.GameStarter.updateSize(thing);1019            }1020            else {1021                thing.GameStarter.markChanged(thing);1022            }1023        };1024        /**1025         * Reduces a Thing's height by pushing down its top and decreasing its1026         * height. It is marked as changed in appearance.1027         *1028         * @param thing1029         * @param dy   How much to increase the Thing's height.1030         * @param updateSize   Whether to also call updateSize on the Thing1031         *                     (by default, false).1032         */1033        GameStartr.prototype.increaseHeight = function (thing, dy, updateSize) {1034            thing.top -= dy;1035            thing.height += dy / thing.GameStarter.unitsize;1036            thing.unitheight = thing.height * thing.GameStarter.unitsize;1037            if (updateSize) {1038                thing.GameStarter.updateSize(thing);1039            }1040            else {1041                thing.GameStarter.markChanged(thing);1042            }1043        };1044        /* Appearance utilities1045        */1046        /**1047         * Generates a key for a Thing based off the Thing's basic attributes.1048         * This key should be used for PixelRender.get calls, to cache the Thing's1049         * sprite.1050         *1051         * @param thing1052         * @returns A key that to identify the Thing's sprite.1053         */1054        GameStartr.prototype.generateThingKey = function (thing) {1055            return thing.groupType + " " + thing.title + " " + thing.className;1056        };1057        /**1058         * Sets the class of a Thing, sets the new sprite for it, and marks it as1059         * having changed appearance. The class is stored in the Thing's internal1060         * .className attribute.1061         *1062         * @param thing1063         * @param className   A new .className for the Thing.1064         */1065        GameStartr.prototype.setClass = function (thing, className) {1066            thing.className = className;1067            thing.GameStarter.PixelDrawer.setThingSprite(thing);1068            thing.GameStarter.markChanged(thing);1069        };1070        /**1071         * A version of setClass to be used before the Thing's sprite attributes1072         * have been set. This just sets the internal .className.1073         *1074         * @param thing1075         * @param className   A new .className for the Thing.1076         */1077        GameStartr.prototype.setClassInitial = function (thing, className) {1078            thing.className = className;1079        };1080        /**1081         * Adds a string to a Thing's class after a " ", updates the Thing's1082         * sprite, and marks it as having changed appearance.1083         *1084         * @param thing1085         * @param className   A class to add to the Thing.1086         */1087        GameStartr.prototype.addClass = function (thing, className) {1088            thing.className += " " + className;1089            thing.GameStarter.PixelDrawer.setThingSprite(thing);1090            thing.GameStarter.markChanged(thing);1091        };1092        /**1093         * Adds multiple strings to a Thing's class after a " ", updates the Thing's1094         * sprite, and marks it as having changed appearance. Strings may be given1095         * as Arrays or Strings; Strings will be split on " ". Any number of1096         * additional arguments may be given.1097         *1098         * @param thing1099         * @param classes   Any number of classes to add to the Thing.1100         */1101        GameStartr.prototype.addClasses = function (thing) {1102            var classes = [];1103            for (var _i = 1; _i < arguments.length; _i++) {1104                classes[_i - 1] = arguments[_i];1105            }1106            var adder, i, j;1107            for (i = 0; i < classes.length; i += 1) {1108                adder = classes[i];1109                if (adder.constructor === String || typeof adder === "string") {1110                    adder = adder.split(" ");1111                }1112                for (j = adder.length - 1; j >= 0; j -= 1) {1113                    thing.GameStarter.addClass(thing, adder[j]);1114                }1115            }1116        };1117        /**1118         * Removes a string from a Thing's class, updates the Thing's sprite, and1119         * marks it as having changed appearance.1120         *1121         * @param thing1122         * @param className   A class to remove from the Thing.1123         */1124        GameStartr.prototype.removeClass = function (thing, className) {1125            if (!className) {1126                return;1127            }1128            if (className.indexOf(" ") !== -1) {1129                thing.GameStarter.removeClasses(thing, className);1130            }1131            thing.className = thing.className.replace(new RegExp(" " + className, "gm"), "");1132            thing.GameStarter.PixelDrawer.setThingSprite(thing);1133        };1134        /**1135         * Removes multiple strings from a Thing's class, updates the Thing's1136         * sprite, and marks it as having changed appearance. Strings may be given1137         * as Arrays or Strings; Strings will be split on " ". Any number of1138         * additional arguments may be given.1139         *1140         * @param thing1141         * @param classes   Any number of classes to remove from the Thing.1142         */1143        GameStartr.prototype.removeClasses = function (thing) {1144            var classes = [];1145            for (var _i = 1; _i < arguments.length; _i++) {1146                classes[_i - 1] = arguments[_i];1147            }1148            var adder, i, j;1149            for (i = 0; i < classes.length; i += 1) {1150                adder = classes[i];1151                if (adder.constructor === String || typeof adder === "string") {1152                    adder = adder.split(" ");1153                }1154                for (j = adder.length - 1; j >= 0; --j) {1155                    thing.GameStarter.removeClass(thing, adder[j]);1156                }1157            }1158        };1159        /**1160         * @param thing1161         * @param className   A class to check for in the Thing.1162         * @returns  Whether the Thing's class contains the class.1163         */1164        GameStartr.prototype.hasClass = function (thing, className) {1165            return thing.className.indexOf(className) !== -1;1166        };1167        /**1168         * Removes the first class from a Thing and adds the second. All typical1169         * sprite updates are called.1170         *1171         * @param thing1172         * @param classNameOut   A class to remove from the Thing.1173         * @param classNameIn   A class to add to the thing.1174         */1175        GameStartr.prototype.switchClass = function (thing, classNameOut, classNameIn) {1176            thing.GameStarter.removeClass(thing, classNameOut);1177            thing.GameStarter.addClass(thing, classNameIn);1178        };1179        /**1180         * Marks a Thing as being flipped horizontally by setting its .flipHoriz1181         * attribute to true and giving it a "flipped" class.1182         *1183         * @param thing1184         */1185        GameStartr.prototype.flipHoriz = function (thing) {1186            thing.flipHoriz = true;1187            thing.GameStarter.addClass(thing, "flipped");1188        };1189        /**1190         * Marks a Thing as being flipped vertically by setting its .flipVert1191         * attribute to true and giving it a "flipped" class.1192         *1193         * @param thing1194         */1195        GameStartr.prototype.flipVert = function (thing) {1196            thing.flipVert = true;1197            thing.GameStarter.addClass(thing, "flip-vert");1198        };1199        /**1200         * Marks a Thing as not being flipped horizontally by setting its .flipHoriz1201         * attribute to false and giving it a "flipped" class.1202         *1203         * @param thing1204         */1205        GameStartr.prototype.unflipHoriz = function (thing) {1206            thing.flipHoriz = false;1207            thing.GameStarter.removeClass(thing, "flipped");1208        };1209        /**1210         * Marks a Thing as not being flipped vertically by setting its .flipVert1211         * attribute to true and giving it a "flipped" class.1212         *1213         * @param thing1214         */1215        GameStartr.prototype.unflipVert = function (thing) {1216            thing.flipVert = false;1217            thing.GameStarter.removeClass(thing, "flip-vert");1218        };1219        /**1220         * Sets the opacity of the Thing and marks its appearance as changed.1221         *1222         * @param thing1223         * @param opacity   A number in [0,1].1224         */1225        GameStartr.prototype.setOpacity = function (thing, opacity) {1226            thing.opacity = opacity;1227            thing.GameStarter.markChanged(thing);1228        };1229        /* Miscellaneous utilities1230        */1231        /**1232         * Ensures the current object is a GameStartr by throwing an error if it1233         * is not. This should be used for Functions in any GameStartr descendants1234         * that have to call 'this' to ensure their caller is what the programmer1235         * expected it to be.1236         *1237         * @param current1238         */1239        GameStartr.prototype.ensureCorrectCaller = function (current) {1240            if (!(current instanceof GameStartr)) {1241                throw new Error("A function requires the scope ('this') to be the "1242                    + "manipulated GameStartr object. Unfortunately, 'this' is a "1243                    + typeof (this) + ".");1244            }1245            return current;1246        };1247        /**1248         * Removes a Thing from an Array using Array.splice. If the thing has an1249         * onDelete, that is called.1250         *1251         * @param thing1252         * @param array   The group containing the thing.1253         * @param location   The index of the Thing in the Array, for speed's1254         *                   sake (by default, it is found using Array.indexOf).1255         */1256        GameStartr.prototype.arrayDeleteThing = function (thing, array, location) {1257            if (location === void 0) { location = array.indexOf(thing); }1258            if (location === -1) {1259                return;1260            }1261            array.splice(location, 1);1262            if (typeof thing.onDelete === "function") {1263                thing.onDelete(thing);1264            }1265        };1266        /**1267         * Takes a snapshot of the current screen canvas by simulating a click event1268         * on a dummy link.1269         *1270         * @param name   A name for the image to be saved as.1271         * @param format   A format for the image to be saved as (by default, png).1272         * @remarks For security concerns, browsers won't allow this unless it's1273         *          called within a callback of a genuine user-triggered event.1274         */1275        GameStartr.prototype.takeScreenshot = function (name, format) {1276            if (format === void 0) { format = "image/png"; }1277            var GameStarter = GameStartr.prototype.ensureCorrectCaller(this), link = GameStarter.createElement("a", {1278                "download": name + "." + format.split("/")[1],1279                "href": GameStarter.canvas.toDataURL(format).replace(format, "image/octet-stream")1280            });1281            link.click();1282        };1283        /**1284         * Adds a set of CSS styles to the page.1285         *1286         * @param styles   CSS styles represented as JSON.1287         */1288        GameStartr.prototype.addPageStyles = function (styles) {1289            var GameStarter = GameStartr.prototype.ensureCorrectCaller(this), sheet = GameStarter.createElement("style", {1290                "type": "text/css"1291            }), compiled = "", i, j;1292            for (i in styles) {1293                if (!styles.hasOwnProperty(i)) {1294                    continue;1295                }1296                compiled += i + " { \r\n";1297                for (j in styles[i]) {1298                    if (styles[i].hasOwnProperty(j)) {1299                        compiled += "  " + j + ": " + styles[i][j] + ";\r\n";1300                    }1301                }1302                compiled += "}\r\n";1303            }1304            if (sheet.styleSheet) {1305                sheet.style.cssText = compiled;1306            }1307            else {1308                sheet.appendChild(document.createTextNode(compiled));1309            }1310            document.querySelector("head").appendChild(sheet);1311        };1312        return GameStartr;1313    })(EightBittr.EightBittr);1314    GameStartr_1.GameStartr = GameStartr;...agents.py
Source:agents.py  
...107    >>> list = ['Right', 'Left', 'Suck', 'NoOp']108    >>> program = RandomAgentProgram(list)109    >>> agent = Agent(program)110    >>> environment = TrivialVacuumEnvironment()111    >>> environment.add_thing(agent)112    >>> environment.run()113    >>> environment.status == {(1, 0): 'Clean' , (0, 0): 'Clean'}114    True115    """116    return lambda percept: random.choice(actions)117# ______________________________________________________________________________118def SimpleReflexAgentProgram(rules, interpret_input):119    """120    [Figure 2.10]121    This agent takes action based solely on the percept.122    """123    def program(percept):124        state = interpret_input(percept)125        rule = rule_match(state, rules)126        action = rule.action127        return action128    return program129def ModelBasedReflexAgentProgram(rules, update_state, model):130    """131    [Figure 2.12]132    This agent takes action based on the percept and state.133    """134    def program(percept):135        program.state = update_state(program.state, program.action, percept, model)136        rule = rule_match(program.state, rules)137        action = rule.action138        return action139    program.state = program.action = None140    return program141def rule_match(state, rules):142    """Find the first rule that matches state."""143    for rule in rules:144        if rule.matches(state):145            return rule146# ______________________________________________________________________________147loc_A, loc_B = (0, 0), (1, 0)  # The two locations for the Vacuum world148def RandomVacuumAgent():149    """Randomly choose one of the actions from the vacuum environment.150    >>> agent = RandomVacuumAgent()151    >>> environment = TrivialVacuumEnvironment()152    >>> environment.add_thing(agent)153    >>> environment.run()154    >>> environment.status == {(1,0):'Clean' , (0,0) : 'Clean'}155    True156    """157    return Agent(RandomAgentProgram(['Right', 'Left', 'Suck', 'NoOp']))158def TableDrivenVacuumAgent():159    """Tabular approach towards vacuum world as mentioned in [Figure 2.3]160    >>> agent = TableDrivenVacuumAgent()161    >>> environment = TrivialVacuumEnvironment()162    >>> environment.add_thing(agent)163    >>> environment.run()164    >>> environment.status == {(1,0):'Clean' , (0,0) : 'Clean'}165    True166    """167    table = {((loc_A, 'Clean'),): 'Right',168             ((loc_A, 'Dirty'),): 'Suck',169             ((loc_B, 'Clean'),): 'Left',170             ((loc_B, 'Dirty'),): 'Suck',171             ((loc_A, 'Dirty'), (loc_A, 'Clean')): 'Right',172             ((loc_A, 'Clean'), (loc_B, 'Dirty')): 'Suck',173             ((loc_B, 'Clean'), (loc_A, 'Dirty')): 'Suck',174             ((loc_B, 'Dirty'), (loc_B, 'Clean')): 'Left',175             ((loc_A, 'Dirty'), (loc_A, 'Clean'), (loc_B, 'Dirty')): 'Suck',176             ((loc_B, 'Dirty'), (loc_B, 'Clean'), (loc_A, 'Dirty')): 'Suck'}177    return Agent(TableDrivenAgentProgram(table))178def ReflexVacuumAgent():179    """180    [Figure 2.8]181    A reflex agent for the two-state vacuum environment.182    >>> agent = ReflexVacuumAgent()183    >>> environment = TrivialVacuumEnvironment()184    >>> environment.add_thing(agent)185    >>> environment.run()186    >>> environment.status == {(1,0):'Clean' , (0,0) : 'Clean'}187    True188    """189    def program(percept):190        location, status = percept191        if status == 'Dirty':192            return 'Suck'193        elif location == loc_A:194            return 'Right'195        elif location == loc_B:196            return 'Left'197    return Agent(program)198def ModelBasedVacuumAgent():199    """An agent that keeps track of what locations are clean or dirty.200    >>> agent = ModelBasedVacuumAgent()201    >>> environment = TrivialVacuumEnvironment()202    >>> environment.add_thing(agent)203    >>> environment.run()204    >>> environment.status == {(1,0):'Clean' , (0,0) : 'Clean'}205    True206    """207    model = {loc_A: None, loc_B: None}208    def program(percept):209        """Same as ReflexVacuumAgent, except if everything is clean, do NoOp."""210        location, status = percept211        model[location] = status  # Update the model here212        if model[loc_A] == model[loc_B] == 'Clean':213            return 'NoOp'214        elif status == 'Dirty':215            return 'Suck'216        elif location == loc_A:217            return 'Right'218        elif location == loc_B:219            return 'Left'220    return Agent(program)221# ______________________________________________________________________________222class Environment:223    """Abstract class representing an Environment. 'Real' Environment classes224    inherit from this. Your Environment will typically need to implement:225        percept:           Define the percept that an agent sees.226        execute_action:    Define the effects of executing an action.227                           Also update the agent.performance slot.228    The environment keeps a list of .things and .agents (which is a subset229    of .things). Each agent has a .performance slot, initialized to 0.230    Each thing has a .location slot, even though some environments may not231    need this."""232    def __init__(self):233        self.things = []234        self.agents = []235    def thing_classes(self):236        return []  # List of classes that can go into environment237    def percept(self, agent):238        """Return the percept that the agent sees at this point. (Implement this.)"""239        raise NotImplementedError240    def execute_action(self, agent, action):241        """Change the world to reflect this action. (Implement this.)"""242        raise NotImplementedError243    def default_location(self, thing):244        """Default location to place a new thing with unspecified location."""245        return None246    def exogenous_change(self):247        """If there is spontaneous change in the world, override this."""248        pass249    def is_done(self):250        """By default, we're done when we can't find a live agent."""251        return not any(agent.is_alive() for agent in self.agents)252    def step(self):253        """Run the environment for one time step. If the254        actions and exogenous changes are independent, this method will255        do. If there are interactions between them, you'll need to256        override this method."""257        if not self.is_done():258            actions = []259            for agent in self.agents:260                if agent.alive:261                    actions.append(agent.program(self.percept(agent)))262                else:263                    actions.append("")264            for (agent, action) in zip(self.agents, actions):265                self.execute_action(agent, action)266            self.exogenous_change()267    def run(self, steps=1000):268        """Run the Environment for given number of time steps."""269        for step in range(steps):270            if self.is_done():271                return272            self.step()273    def list_things_at(self, location, tclass=Thing):274        """Return all things exactly at a given location."""275        if isinstance(location, numbers.Number):276            return [thing for thing in self.things277                    if thing.location == location and isinstance(thing, tclass)]278        return [thing for thing in self.things279                if all(x == y for x, y in zip(thing.location, location)) and isinstance(thing, tclass)]280    def some_things_at(self, location, tclass=Thing):281        """Return true if at least one of the things at location282        is an instance of class tclass (or a subclass)."""283        return self.list_things_at(location, tclass) != []284    def add_thing(self, thing, location=None):285        """Add a thing to the environment, setting its location. For286        convenience, if thing is an agent program we make a new agent287        for it. (Shouldn't need to override this.)"""288        if not isinstance(thing, Thing):289            thing = Agent(thing)290        if thing in self.things:291            print("Can't add the same thing twice")292        else:293            thing.location = location if location is not None else self.default_location(thing)294            self.things.append(thing)295            if isinstance(thing, Agent):296                thing.performance = 0297                self.agents.append(thing)298    def delete_thing(self, thing):299        """Remove a thing from the environment."""300        try:301            self.things.remove(thing)302        except ValueError as e:303            print(e)304            print("  in Environment delete_thing")305            print("  Thing to be removed: {} at {}".format(thing, thing.location))306            print("  from list: {}".format([(thing, thing.location) for thing in self.things]))307        if thing in self.agents:308            self.agents.remove(thing)309class Direction:310    """A direction class for agents that want to move in a 2D plane311        Usage:312            d = Direction("down")313            To change directions:314            d = d + "right" or d = d + Direction.R #Both do the same thing315            Note that the argument to __add__ must be a string and not a Direction object.316            Also, it (the argument) can only be right or left."""317    R = "right"318    L = "left"319    U = "up"320    D = "down"321    def __init__(self, direction):322        self.direction = direction323    def __add__(self, heading):324        """325        >>> d = Direction('right')326        >>> l1 = d.__add__(Direction.L)327        >>> l2 = d.__add__(Direction.R)328        >>> l1.direction329        'up'330        >>> l2.direction331        'down'332        >>> d = Direction('down')333        >>> l1 = d.__add__('right')334        >>> l2 = d.__add__('left')335        >>> l1.direction == Direction.L336        True337        >>> l2.direction == Direction.R338        True339        """340        if self.direction == self.R:341            return {342                self.R: Direction(self.D),343                self.L: Direction(self.U),344            }.get(heading, None)345        elif self.direction == self.L:346            return {347                self.R: Direction(self.U),348                self.L: Direction(self.D),349            }.get(heading, None)350        elif self.direction == self.U:351            return {352                self.R: Direction(self.R),353                self.L: Direction(self.L),354            }.get(heading, None)355        elif self.direction == self.D:356            return {357                self.R: Direction(self.L),358                self.L: Direction(self.R),359            }.get(heading, None)360    def move_forward(self, from_location):361        """362        >>> d = Direction('up')363        >>> l1 = d.move_forward((0, 0))364        >>> l1365        (0, -1)366        >>> d = Direction(Direction.R)367        >>> l1 = d.move_forward((0, 0))368        >>> l1369        (1, 0)370        """371        # get the iterable class to return372        iclass = from_location.__class__373        x, y = from_location374        if self.direction == self.R:375            return iclass((x + 1, y))376        elif self.direction == self.L:377            return iclass((x - 1, y))378        elif self.direction == self.U:379            return iclass((x, y - 1))380        elif self.direction == self.D:381            return iclass((x, y + 1))382class XYEnvironment(Environment):383    """This class is for environments on a 2D plane, with locations384    labelled by (x, y) points, either discrete or continuous.385    Agents perceive things within a radius. Each agent in the386    environment has a .location slot which should be a location such387    as (0, 1), and a .holding slot, which should be a list of things388    that are held."""389    def __init__(self, width=10, height=10):390        super().__init__()391        self.width = width392        self.height = height393        self.observers = []394        # Sets iteration start and end (no walls).395        self.x_start, self.y_start = (0, 0)396        self.x_end, self.y_end = (self.width, self.height)397    perceptible_distance = 1398    def things_near(self, location, radius=None):399        """Return all things within radius of location."""400        if radius is None:401            radius = self.perceptible_distance402        radius2 = radius * radius403        return [(thing, radius2 - distance_squared(location, thing.location))404                for thing in self.things if distance_squared(405                location, thing.location) <= radius2]406    def percept(self, agent):407        """By default, agent perceives things within a default radius."""408        return self.things_near(agent.location)409    def execute_action(self, agent, action):410        agent.bump = False411        if action == 'TurnRight':412            agent.direction += Direction.R413        elif action == 'TurnLeft':414            agent.direction += Direction.L415        elif action == 'Forward':416            agent.bump = self.move_to(agent, agent.direction.move_forward(agent.location))417        #         elif action == 'Grab':418        #             things = [thing for thing in self.list_things_at(agent.location)419        #                     if agent.can_grab(thing)]420        #             if things:421        #                 agent.holding.append(things[0])422        elif action == 'Release':423            if agent.holding:424                agent.holding.pop()425    def default_location(self, thing):426        location = self.random_location_inbounds()427        while self.some_things_at(location, Obstacle):428            # we will find a random location with no obstacles429            location = self.random_location_inbounds()430        return location431    def move_to(self, thing, destination):432        """Move a thing to a new location. Returns True on success or False if there is an Obstacle.433        If thing is holding anything, they move with him."""434        thing.bump = self.some_things_at(destination, Obstacle)435        if not thing.bump:436            thing.location = destination437            for o in self.observers:438                o.thing_moved(thing)439            for t in thing.holding:440                self.delete_thing(t)441                self.add_thing(t, destination)442                t.location = destination443        return thing.bump444    def add_thing(self, thing, location=None, exclude_duplicate_class_items=False):445        """Add things to the world. If (exclude_duplicate_class_items) then the item won't be446        added if the location has at least one item of the same class."""447        if location is None:448            super().add_thing(thing)449        elif self.is_inbounds(location):450            if (exclude_duplicate_class_items and451                    any(isinstance(t, thing.__class__) for t in self.list_things_at(location))):452                return453            super().add_thing(thing, location)454    def is_inbounds(self, location):455        """Checks to make sure that the location is inbounds (within walls if we have walls)"""456        x, y = location457        return not (x < self.x_start or x > self.x_end or y < self.y_start or y > self.y_end)458    def random_location_inbounds(self, exclude=None):459        """Returns a random location that is inbounds (within walls if we have walls)"""460        location = (random.randint(self.x_start, self.x_end),461                    random.randint(self.y_start, self.y_end))462        if exclude is not None:463            while location == exclude:464                location = (random.randint(self.x_start, self.x_end),465                            random.randint(self.y_start, self.y_end))466        return location467    def delete_thing(self, thing):468        """Deletes thing, and everything it is holding (if thing is an agent)"""469        if isinstance(thing, Agent):470            for obj in thing.holding:471                super().delete_thing(obj)472                for obs in self.observers:473                    obs.thing_deleted(obj)474        super().delete_thing(thing)475        for obs in self.observers:476            obs.thing_deleted(thing)477    def add_walls(self):478        """Put walls around the entire perimeter of the grid."""479        for x in range(self.width):480            self.add_thing(Wall(), (x, 0))481            self.add_thing(Wall(), (x, self.height - 1))482        for y in range(1, self.height - 1):483            self.add_thing(Wall(), (0, y))484            self.add_thing(Wall(), (self.width - 1, y))485        # Updates iteration start and end (with walls).486        self.x_start, self.y_start = (1, 1)487        self.x_end, self.y_end = (self.width - 1, self.height - 1)488    def add_observer(self, observer):489        """Adds an observer to the list of observers.490        An observer is typically an EnvGUI.491        Each observer is notified of changes in move_to and add_thing,492        by calling the observer's methods thing_moved(thing)493        and thing_added(thing, loc)."""494        self.observers.append(observer)495    def turn_heading(self, heading, inc):496        """Return the heading to the left (inc=+1) or right (inc=-1) of heading."""497        return turn_heading(heading, inc)498class Obstacle(Thing):499    """Something that can cause a bump, preventing an agent from500    moving into the same square it's in."""501    pass502class Wall(Obstacle):503    pass504# ______________________________________________________________________________505class GraphicEnvironment(XYEnvironment):506    def __init__(self, width=10, height=10, boundary=True, color={}, display=False):507        """Define all the usual XYEnvironment characteristics,508        but initialise a BlockGrid for GUI too."""509        super().__init__(width, height)510        self.grid = BlockGrid(width, height, fill=(200, 200, 200))511        if display:512            self.grid.show()513            self.visible = True514        else:515            self.visible = False516        self.bounded = boundary517        self.colors = color518    def get_world(self):519        """Returns all the items in the world in a format520        understandable by the ipythonblocks BlockGrid."""521        result = []522        x_start, y_start = (0, 0)523        x_end, y_end = self.width, self.height524        for x in range(x_start, x_end):525            row = []526            for y in range(y_start, y_end):527                row.append(self.list_things_at((x, y)))528            result.append(row)529        return result530    """531    def run(self, steps=1000, delay=1):532        "" "Run the Environment for given number of time steps,533        but update the GUI too." ""534        for step in range(steps):535            sleep(delay)536            if self.visible:537                self.reveal()538            if self.is_done():539                if self.visible:540                    self.reveal()541                return542            self.step()543        if self.visible:544            self.reveal()545    """546    def run(self, steps=1000, delay=1):547        """Run the Environment for given number of time steps,548        but update the GUI too."""549        for step in range(steps):550            self.update(delay)551            if self.is_done():552                break553            self.step()554        self.update(delay)555    def update(self, delay=1):556        sleep(delay)557        self.reveal()558    def reveal(self):559        """Display the BlockGrid for this world - the last thing to be added560        at a location defines the location color."""561        self.draw_world()562        # wait for the world to update and563        # apply changes to the same grid instead564        # of making a new one.565        clear_output(1)566        self.grid.show()567        self.visible = True568    def draw_world(self):569        self.grid[:] = (200, 200, 200)570        world = self.get_world()571        for x in range(0, len(world)):572            for y in range(0, len(world[x])):573                if len(world[x][y]):574                    self.grid[y, x] = self.colors[world[x][y][-1].__class__.__name__]575    def conceal(self):576        """Hide the BlockGrid for this world"""577        self.visible = False578        display(HTML(''))579# ______________________________________________________________________________580# Continuous environment581class ContinuousWorld(Environment):582    """Model for Continuous World"""583    def __init__(self, width=10, height=10):584        super().__init__()585        self.width = width586        self.height = height587    def add_obstacle(self, coordinates):588        self.things.append(PolygonObstacle(coordinates))589class PolygonObstacle(Obstacle):590    def __init__(self, coordinates):591        """Coordinates is a list of tuples."""592        super().__init__()593        self.coordinates = coordinates594# ______________________________________________________________________________595# Vacuum environment596class Dirt(Thing):597    pass598class VacuumEnvironment(XYEnvironment):599    """The environment of [Ex. 2.12]. Agent perceives dirty or clean,600    and bump (into obstacle) or not; 2D discrete world of unknown size;601    performance measure is 100 for each dirt cleaned, and -1 for602    each turn taken."""603    def __init__(self, width=10, height=10):604        super().__init__(width, height)605        self.add_walls()606    def thing_classes(self):607        return [Wall, Dirt, ReflexVacuumAgent, RandomVacuumAgent,608                TableDrivenVacuumAgent, ModelBasedVacuumAgent]609    def percept(self, agent):610        """The percept is a tuple of ('Dirty' or 'Clean', 'Bump' or 'None').611        Unlike the TrivialVacuumEnvironment, location is NOT perceived."""612        status = ('Dirty' if self.some_things_at(613            agent.location, Dirt) else 'Clean')614        bump = ('Bump' if agent.bump else 'None')615        return status, bump616    def execute_action(self, agent, action):617        agent.bump = False618        if action == 'Suck':619            dirt_list = self.list_things_at(agent.location, Dirt)620            if dirt_list != []:621                dirt = dirt_list[0]622                agent.performance += 100623                self.delete_thing(dirt)624        else:625            super().execute_action(agent, action)626        if action != 'NoOp':627            agent.performance -= 1628class TrivialVacuumEnvironment(Environment):629    """This environment has two locations, A and B. Each can be Dirty630    or Clean. The agent perceives its location and the location's631    status. This serves as an example of how to implement a simple632    Environment."""633    def __init__(self):634        super().__init__()635        self.status = {loc_A: random.choice(['Clean', 'Dirty']),636                       loc_B: random.choice(['Clean', 'Dirty'])}637    def thing_classes(self):638        return [Wall, Dirt, ReflexVacuumAgent, RandomVacuumAgent, TableDrivenVacuumAgent, ModelBasedVacuumAgent]639    def percept(self, agent):640        """Returns the agent's location, and the location status (Dirty/Clean)."""641        return agent.location, self.status[agent.location]642    def execute_action(self, agent, action):643        """Change agent's location and/or location's status; track performance.644        Score 10 for each dirt cleaned; -1 for each move."""645        if action == 'Right':646            agent.location = loc_B647            agent.performance -= 1648        elif action == 'Left':649            agent.location = loc_A650            agent.performance -= 1651        elif action == 'Suck':652            if self.status[agent.location] == 'Dirty':653                agent.performance += 10654            self.status[agent.location] = 'Clean'655    def default_location(self, thing):656        """Agents start in either location at random."""657        return random.choice([loc_A, loc_B])658# ______________________________________________________________________________659# The Wumpus World660class Gold(Thing):661    def __eq__(self, rhs):662        """All Gold are equal"""663        return rhs.__class__ == Gold664    pass665class Bump(Thing):666    pass667class Glitter(Thing):668    pass669class Pit(Thing):670    pass671class Breeze(Thing):672    pass673class Arrow(Thing):674    pass675class Scream(Thing):676    pass677class Wumpus(Agent):678    screamed = False679    pass680class Stench(Thing):681    pass682class Explorer(Agent):683    holding = []684    has_arrow = True685    killed_by = ""686    direction = Direction("right")687    def can_grab(self, thing):688        """Explorer can only grab gold"""689        return thing.__class__ == Gold690class WumpusEnvironment(XYEnvironment):691    pit_probability = 0.2  # Probability to spawn a pit in a location. (From Chapter 7.2)692    # Room should be 4x4 grid of rooms. The extra 2 for walls693    def __init__(self, agent_program, width=6, height=6):694        super().__init__(width, height)695        self.init_world(agent_program)696    def init_world(self, program):697        """Spawn items in the world based on probabilities from the book"""698        "WALLS"699        self.add_walls()700        "PITS"701        for x in range(self.x_start, self.x_end):702            for y in range(self.y_start, self.y_end):703                if random.random() < self.pit_probability:704                    self.add_thing(Pit(), (x, y), True)705                    self.add_thing(Breeze(), (x - 1, y), True)706                    self.add_thing(Breeze(), (x, y - 1), True)707                    self.add_thing(Breeze(), (x + 1, y), True)708                    self.add_thing(Breeze(), (x, y + 1), True)709        "WUMPUS"710        w_x, w_y = self.random_location_inbounds(exclude=(1, 1))711        self.add_thing(Wumpus(lambda x: ""), (w_x, w_y), True)712        self.add_thing(Stench(), (w_x - 1, w_y), True)713        self.add_thing(Stench(), (w_x + 1, w_y), True)714        self.add_thing(Stench(), (w_x, w_y - 1), True)715        self.add_thing(Stench(), (w_x, w_y + 1), True)716        "GOLD"717        self.add_thing(Gold(), self.random_location_inbounds(exclude=(1, 1)), True)718        "AGENT"719        self.add_thing(Explorer(program), (1, 1), True)720    def get_world(self, show_walls=True):721        """Return the items in the world"""722        result = []723        x_start, y_start = (0, 0) if show_walls else (1, 1)724        if show_walls:725            x_end, y_end = self.width, self.height726        else:727            x_end, y_end = self.width - 1, self.height - 1728        for x in range(x_start, x_end):729            row = []730            for y in range(y_start, y_end):731                row.append(self.list_things_at((x, y)))732            result.append(row)733        return result734    def percepts_from(self, agent, location, tclass=Thing):735        """Return percepts from a given location,736        and replaces some items with percepts from chapter 7."""737        thing_percepts = {738            Gold: Glitter(),739            Wall: Bump(),740            Wumpus: Stench(),741            Pit: Breeze()}742        """Agents don't need to get their percepts"""743        thing_percepts[agent.__class__] = None744        """Gold only glitters in its cell"""745        if location != agent.location:746            thing_percepts[Gold] = None747        result = [thing_percepts.get(thing.__class__, thing) for thing in self.things748                  if thing.location == location and isinstance(thing, tclass)]749        return result if len(result) else [None]750    def percept(self, agent):751        """Return things in adjacent (not diagonal) cells of the agent.752        Result format: [Left, Right, Up, Down, Center / Current location]"""753        x, y = agent.location754        result = []755        result.append(self.percepts_from(agent, (x - 1, y)))756        result.append(self.percepts_from(agent, (x + 1, y)))757        result.append(self.percepts_from(agent, (x, y - 1)))758        result.append(self.percepts_from(agent, (x, y + 1)))759        result.append(self.percepts_from(agent, (x, y)))760        """The wumpus gives out a loud scream once it's killed."""761        wumpus = [thing for thing in self.things if isinstance(thing, Wumpus)]762        if len(wumpus) and not wumpus[0].alive and not wumpus[0].screamed:763            result[-1].append(Scream())764            wumpus[0].screamed = True765        return result766    def execute_action(self, agent, action):767        """Modify the state of the environment based on the agent's actions.768        Performance score taken directly out of the book."""769        if isinstance(agent, Explorer) and self.in_danger(agent):770            return771        agent.bump = False772        if action == 'TurnRight':773            agent.direction += Direction.R774            agent.performance -= 1775        elif action == 'TurnLeft':776            agent.direction += Direction.L777            agent.performance -= 1778        elif action == 'Forward':779            agent.bump = self.move_to(agent, agent.direction.move_forward(agent.location))780            agent.performance -= 1781        elif action == 'Grab':782            things = [thing for thing in self.list_things_at(agent.location)783                      if agent.can_grab(thing)]784            if len(things):785                print("Grabbing", things[0].__class__.__name__)786                if len(things):787                    agent.holding.append(things[0])788            agent.performance -= 1789        elif action == 'Climb':790            if agent.location == (1, 1):  # Agent can only climb out of (1,1)791                agent.performance += 1000 if Gold() in agent.holding else 0792                self.delete_thing(agent)793        elif action == 'Shoot':794            """The arrow travels straight down the path the agent is facing"""795            if agent.has_arrow:796                arrow_travel = agent.direction.move_forward(agent.location)797                while self.is_inbounds(arrow_travel):798                    wumpus = [thing for thing in self.list_things_at(arrow_travel)799                              if isinstance(thing, Wumpus)]800                    if len(wumpus):801                        wumpus[0].alive = False802                        break803                    arrow_travel = agent.direction.move_forward(agent.location)804                agent.has_arrow = False805    def in_danger(self, agent):806        """Check if Explorer is in danger (Pit or Wumpus), if he is, kill him"""807        for thing in self.list_things_at(agent.location):808            if isinstance(thing, Pit) or (isinstance(thing, Wumpus) and thing.alive):809                agent.alive = False810                agent.performance -= 1000811                agent.killed_by = thing.__class__.__name__812                return True813        return False814    def is_done(self):815        """The game is over when the Explorer is killed816        or if he climbs out of the cave only at (1,1)."""817        explorer = [agent for agent in self.agents if isinstance(agent, Explorer)]818        if len(explorer):819            if explorer[0].alive:820                return False821            else:822                print("Death by {} [-1000].".format(explorer[0].killed_by))823        else:824            print("Explorer climbed out {}."825                  .format("with Gold [+1000]!" if Gold() not in self.things else "without Gold [+0]"))826        return True827    # TODO: Arrow needs to be implemented828# ______________________________________________________________________________829def compare_agents(EnvFactory, AgentFactories, n=10, steps=1000):830    """See how well each of several agents do in n instances of an environment.831    Pass in a factory (constructor) for environments, and several for agents.832    Create n instances of the environment, and run each agent in copies of833    each one for steps. Return a list of (agent, average-score) tuples.834    >>> environment = TrivialVacuumEnvironment835    >>> agents = [ModelBasedVacuumAgent, ReflexVacuumAgent]836    >>> result = compare_agents(environment, agents)837    >>> performance_ModelBasedVacuumAgent = result[0][1]838    >>> performance_ReflexVacuumAgent = result[1][1]839    >>> performance_ReflexVacuumAgent <= performance_ModelBasedVacuumAgent840    True841    """842    envs = [EnvFactory() for i in range(n)]843    return [(A, test_agent(A, steps, copy.deepcopy(envs)))844            for A in AgentFactories]845def test_agent(AgentFactory, steps, envs):846    """Return the mean score of running an agent in each of the envs, for steps847    >>> def constant_prog(percept):848    ...     return percept849    ...850    >>> agent = Agent(constant_prog)851    >>> result = agent.program(5)852    >>> result == 5853    True854    """855    def score(env):856        agent = AgentFactory()857        env.add_thing(agent)858        env.run(steps)859        return agent.performance860    return mean(map(score, envs))861# _________________________________________________________________________862__doc__ += """863>>> a = ReflexVacuumAgent()864>>> a.program((loc_A, 'Clean'))865'Right'866>>> a.program((loc_B, 'Clean'))867'Left'868>>> a.program((loc_A, 'Dirty'))869'Suck'870>>> a.program((loc_A, 'Dirty'))871'Suck'872>>> e = TrivialVacuumEnvironment()873>>> e.add_thing(ModelBasedVacuumAgent())874>>> e.run(5)...3-1.js
Source:3-1.js  
1FullScreenMario.FullScreenMario.settings.maps.library["3-1"] = {2    "name": "3-1",3    "time": 300,4    "locations": [5        { "entry": "Plain" },6        { "entry": "PipeVertical" },7        { "xloc": 1272 },8        { "area": 1 },9        { "area": 2, "entry": "Vine" }10    ],11    "areas": [12        {13            "setting": "Overworld Night Alt",14            "blockBoundaries": true,15            "creation": [16                { "macro": "Floor", "width": 360 },17                { "macro": "CastleLarge", "x": -16 },18                { "macro": "Pattern", "pattern": "BackFence", "repeat": 5 },19                { "thing": "Block", "x": 128, "y": 32 },20                { "thing": "Block", "x": 152, "y": 40 },21                { "thing": "Block", "x": 176, "y": 40, "contents": "Mushroom" },22                { "thing": "Koopa", "x": 200, "y": 12, "jumping": true },23                { "macro": "Fill", "thing": "Brick", "x": 208, "y": 32, "xnum": 3 },24                { "thing": "Koopa", "x": 224, "y": 20, "jumping": true },25                { "macro": "Pipe", "x": 256, "height": 24, "piranha": true },26                { "thing": "Goomba", "x": 296, "y": 8 },27                { "macro": "Pipe", "x": 304, "height": 32, "piranha": true, "transport": 3 },28                { "macro": "Floor", "x": 384, "width": 232 },29                { "macro": "Fill", "thing": "Goomba", "x": 424, "y": 8, "xnum": 3, "xwidth": 12 },30                { "macro": "Pipe", "x": 456, "height": 24, "piranha": true },31                { "thing": "Brick", "x": 488, "y": 32 },32                { "thing": "Koopa", "x": 520, "y": 12 },33                { "macro": "Pipe", "x": 536, "height": 16, "piranha": true, "entrance": 1 },34                { "thing": "Stone", "x": 584, "y": 8 },35                { "thing": "Stone", "x": 592, "y": 16, "height": 16 },36                { "thing": "Stone", "x": 600, "y": 24, "height": 24 },37                { "thing": "Stone", "x": 608, "y": 32, "height": 32 },38                { "macro": "Water", "x": 616, "y": 10, "width": 64 },39                { "macro": "Bridge", "x": 616, "y": 32, "width": 64 },40                { "macro": "Fill", "thing": "Goomba", "x": 656, "y": 40, "xnum": 3, "xwidth": 12 },41                { "thing": "Block", "x": 656, "y": 64, "contents": "Mushroom1Up", "hidden": true },42                { "macro": "Floor", "x": 680 },43                { "thing": "Stone", "x": 680, "y": 32, "height": 32 },44                { "macro": "Water", "x": 688, "y": 10, "width": 16 },45                { "macro": "Floor", "x": 704, "width": 320 },46                { "thing": "Stone", "x": 704, "y": 32, "height": 32 },47                { "thing": "Stone", "x": 712, "y": 16, "height": 16 },48                { "thing": "Brick", "x": 720, "y": 64, "contents": "Star" },49                { "macro": "Fill", "thing": "Brick", "x": 728, "y": 64, "xnum": 2 },50                { "macro": "Fill", "thing": "Goomba", "x": 752, "y": 8, "xnum": 2, "xwidth": 12 },51                { "thing": "Koopa", "x": 808, "y": 12 },52                { "macro": "Pipe", "x": 824, "height": 32, "piranha": true },53                { "macro": "Fill", "thing": "Brick", "x": 888, "y": 32, "xnum": 11 },54                { "macro": "Fill", "thing": "Brick", "x": 888, "y": 64, "xnum": 2 },55                { "thing": "HammerBro", "x": 904, "y": 44 },56                { "thing": "Block", "x": 904, "y": 64 },57                { "macro": "Fill", "thing": "Brick", "x": 912, "y": 64, "xnum": 3 },58                { "thing": "HammerBro", "x": 936, "y": 12 },59                { "thing": "Block", "x": 936, "y": 64, "contents": "Mushroom" },60                { "macro": "Fill", "thing": "Brick", "x": 944, "y": 64, "xnum": 3 },61                // { "thing": "Springboard", "x": 1008, "y": 14.5 },62                { "macro": "Fill", "thing": "Brick", "x": 1032, "y": 40, "xnum": 3 },63                { "macro": "Fill", "thing": "Brick", "x": 1032, "y": 64, "xnum": 2 },64                { "thing": "Brick", "thing": "Brick", "x": 1048, "y": 64, "contents": ["Vine", { "entrance": 4 }] },65                { "macro": "Floor", "x": 1056, "width": 80 },66                { "thing": "Stone", "x": 1088, "y": 8 },67                { "thing": "Stone", "x": 1096, "y": 16, "height": 16 },68                { "thing": "Stone", "x": 1104, "y": 24, "height": 24 },69                { "thing": "Stone", "x": 1112, "y": 32, "height": 32 },70                { "thing": "Goomba", "x": 1112, "y": 40 },71                { "thing": "Stone", "x": 1120, "y": 40, "height": 40 },72                { "thing": "Goomba", "x": 1120, "y": 48 },73                { "thing": "Stone", "x": 1128, "y": 48, "height": 48 },74                { "macro": "Floor", "x": 1152, "width": 264 },75                { "thing": "Koopa", "x": 1192, "y": 12 },76                { "macro": "Fill", "thing": "Brick", "x": 1200, "y": 32, "xnum": 2, "ynum": 2, "xwidth": 16, "yheight": 32 },77                { "macro": "Fill", "thing": "Block", "x": 1208, "y": 32, "ynum": 2, "yheight": 32 },78                { "thing": "Koopa", "x": 1216, "y": 76 },79                { "macro": "Fill", "thing": "Goomba", "x": 1232, "y": 8, "xnum": 3, "xwidth": 12 },80                { "macro": "Fill", "thing": "Brick", "x": 1240, "y": 32, "xnum": 2, "ynum": 2, "xwidth": 16, "yheight": 32 },81                { "thing": "Block", "x": 1248, "y": 32, "contents": "Mushroom" },82                { "thing": "Block", "x": 1248, "y": 64 },83                { "thing": "Koopa", "x": 1320, "y": 12, "jumping": true },84                { "thing": "Brick", "x": 1328, "y": 32 },85                { "thing": "Brick", "x": 1336, "y": 32, "contents": "Coin" },86                { "thing": "Koopa", "x": 1344, "y": 18, "jumping": true },87                { "macro": "Fill", "thing": "Brick", "x": 1344, "y": 32, "xnum": 3 },88                { "thing": "Koopa", "x": 1360, "y": 44 },89                { "thing": "Koopa", "x": 1368, "y": 12, "jumping": true },90                { "thing": "Stone", "x": 1392, "y": 24, "height": 24 },91                { "thing": "Stone", "x": 1400, "y": 48, "height": 48 },92                { "macro": "Floor", "x": 1440, "width": 320 },93                { "thing": "Stone", "x": 1464, "y": 8 },94                { "thing": "Stone", "x": 1472, "y": 16, "height": 16 },95                { "thing": "Stone", "x": 1480, "y": 24, "height": 24 },96                { "thing": "Stone", "x": 1488, "y": 32, "height": 32 },97                { "thing": "Stone", "x": 1496, "y": 40, "height": 40 },98                { "thing": "Stone", "x": 1504, "y": 48, "height": 48 },99                { "thing": "Koopa", "x": 1504, "y": 60 },100                { "thing": "Stone", "x": 1512, "y": 56, "height": 56 },101                { "thing": "Stone", "x": 1520, "y": 64, "width": 16, "height": 64 },102                { "thing": "Koopa", "x": 1528, "y": 76 },103                { "macro": "EndOutsideCastle", "x": 1600, "transport": { "map": "3-2" } }104            ]105        }, {106            "setting": "Underworld",107            "blockBoundaries": true,108            "creation": [109                { "macro": "Floor", "width": 136 },110                { "macro": "Fill", "thing": "Brick", "y": 8, "ynum": 11 },111                { "macro": "Fill", "thing": "Brick", "x": 24, "y": 40, "xnum": 2, "ynum": 4, "xwidth": 72 },112                { "macro": "Fill", "thing": "Brick", "x": 32, "y": 32, "xnum": 2, "xwidth": 56 },113                { "macro": "Fill", "thing": "Brick", "x": 32, "y": 56, "xnum": 2, "ynum": 2, "xwidth": 56 },114                { "macro": "Fill", "thing": "Coin", "x": 33, "y": 39, "xnum": 2, "xwidth": 56 },115                { "macro": "Fill", "thing": "Brick", "x": 40, "y": 40, "xnum": 2, "xwidth": 40 },116                { "thing": "Brick", "x": 40, "y": 64, "contents": "Mushroom" },117                { "macro": "Fill", "thing": "Coin", "x": 41, "y": 47, "xnum": 2, "xwidth": 40 },118                { "macro": "Fill", "thing": "Brick", "x": 48, "y": 48, "xnum": 2, "xwidth": 24 },119                { "macro": "Fill", "thing": "Coin", "x": 49, "y": 55, "xnum": 2, "ynum": 2, "xwidth": 24, "yheight": 16 },120                { "macro": "Fill", "thing": "Brick", "x": 56, "y": 56, "xnum": 2, "ynum": 2 },121                { "macro": "Fill", "thing": "Coin", "x": 57, "y": 71, "xnum": 2, "ynum": 2, "xwidth": 8, "yheight": 8 },122                { "thing": "Brick", "x": 80, "y": 64 },123                { "thing": "PipeHorizontal", "x": 104, "y": 16, "entrance": 1 },124                { "thing": "PipeVertical", "x": 120, "y": 88, "height": 88 }125            ]126        }, {127            "setting": "Sky Night",128            "blockBoundaries": false,129            "exit": 2,130            "creation": [131                { "thing": "Stone", "width": 32 },132                { "thing": "Stone", "x": 40, "width": 624 },133                { "thing": "Platform", "x": 128, "y": 24, "width": 24, "transport": true },134                { "macro": "Fill", "thing": "Coin", "x": 121, "y": 55, "xnum": 16, "xwidth": 8 },135                { "thing": "Stone", "x": 256, "y": 40 },136                { "macro": "Fill", "thing": "Coin", "x": 273, "y": 55, "xnum": 16, "xwidth": 8 },137                { "thing": "Stone", "x": 408, "y": 48, "height": 16 },138                { "macro": "Fill", "thing": "Coin", "x": 425, "y": 63, "xnum": 7, "xwidth": 8 },139                { "thing": "Stone", "x": 488, "y": 48, "height": 16 },140                { "thing": "Stone", "x": 536, "y": 56, "width": 16 },141                { "macro": "Fill", "thing": "Stone", "x": 568, "y": 56, "xnum": 5, "xwidth": 16 },142                { "macro": "Fill", "thing": "Coin", "x": 569, "y": 63, "xnum": 10, "xwidth": 8 },143                { "macro": "Fill", "thing": "Coin", "x": 681, "y": 15, "xnum": 3, "xwidth": 8 }144            ]145        }146    ]...Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!
