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!!