scripts/scrEntityGen.gml
From spelunky.gmk, Spelunky Classic 1.1's source, © 2008, 2009 Derek Yu and Mossmouth, LLC, shown whole under the Spelunky User License v1.1b. Extracted by spelunky.ai's tools, an unofficial fan project, one file per script or event; the // ==== lines are the extractor's, naming each action of the event. Some scripts are others' work that Derek Yu credits in the source's readme.txt: Martin Piecyk's platform engine, ChevyRay's view scaling scripts and tsg1zzn's Super Sound System.
The script scrEntityGen, 547 lines of GML.
//// scrEntityGen()//// Generates enemies, traps, and treasure.///********************************************************************************** Copyright (c) 2008, 2009 Derek Yu and Mossmouth, LLC This file is part of Spelunky. You can redistribute and/or modify Spelunky, including its source code, under the terms of the Spelunky User License. Spelunky is distributed in the hope that it will be entertaining and useful, but WITHOUT WARRANTY. Please see the Spelunky User License for more details. The Spelunky User License should be available in "Game Information", which can be found in the Resource Explorer, or as an external file called COPYING. If not, please obtain a new copy of Spelunky from <http://spelunkyworld.com/> ***********************************************************************************/// Note: depth of trees, statues is 9005global.LockedChest = false;global.Key = false;global.lockedChestChance = 8;if (global.levelType == 0){ global.giantSpider = false; global.genGiantSpider = false; if (rand(1,6) == 1) global.genGiantSpider = true; with oSolid { if (not isInShop(x, y) and y > 16) { if (type != "Altar") { scrTreasureGen(); } // enemies if (scrGetRoomX(x) != global.startRoomX or scrGetRoomY(y-16) != global.startRoomY) { if (y < room_height - 64 and not collision_point(x, y+16, oSolid, 0, 0) and not collision_point(x, y+32, oSolid, 0, 0) and not collision_point(x, y+16, oWater, 0, 0) and not collision_point(x, y+32, oWater, 0, 0) and not collision_point(x, y+16, oEnemy, 0, 0)) { if (global.genGiantSpider and not global.giantSpider and not collision_point(x+16, y+16, oSolid, 0, 0) and not collision_point(x+16, y+32, oSolid, 0, 0) and rand(1,40) == 1) { instance_create(x, y+16, oGiantSpiderHang); global.giantSpider = true; } else if (global.darkLevel and rand(1,60) == 1) instance_create(x, y+16, oLamp); else if (global.darkLevel and rand(1,40) == 1) instance_create(x, y+16, oScarab); else if (rand(1,60) == 1) instance_create(x, y+16, oBat); else if (rand(1,80) == 1) instance_create(x, y+16, oSpiderHang); } if (not collision_point(x, y-16, oSolid, 0, 0)) { if (rand(1,60) == 1) instance_create(x, y-16, oSnake); else if (rand(1,800) == 1) instance_create(x, y-16, oCaveman); } } } } // force generate chest if (global.genUdjatEye and not global.LockedChest) { with oExit { if (not collision_point(x-8, y, oSolid, 0, 0) and not collision_point(x-8, y+15, oTreasure, 0, 0) and not collision_point(x-8, y+8, oChest, 0, 0) and not collision_point(x-8, y+8, oSpikes, 0, 0)) { instance_create(x-8, y+8, oLockedChest); global.LockedChest = true; break; } else if (not collision_point(x+8, y, oSolid, 0, 0) and not collision_point(x+8, y+15, oTreasure, 0, 0) and not collision_point(x+8, y+8, oChest, 0, 0) and not collision_point(x+8, y+8, oSpikes, 0, 0)) { instance_create(x+16+8, y+8, oLockedChest); global.LockedChest = true; break; } else { instance_create(x+8, y+8, oLockedChest); global.LockedChest = true; break; } } } // generate key if locked chest has been generated if (instance_exists(oLockedChest)) { n = 1; while (n < 8 and global.Key == false) { with oTreasure { if (rand(1,8) <= 1 and not collision_point(x, y, oSolid, 0, 0) and global.Key == false) { if (type == "Gold Bars") instance_create(x, y+4, oKey); else instance_create(x, y, oKey); global.Key = true; instance_destroy(); break; } } n += 1; } if (not global.Key) { with oTreasure { if (not collision_point(x, y, oSolid, 0, 0)) { if (type == "Gold Bars") instance_create(x, y+4, oKey); else instance_create(x, y, oKey); global.Key = true; instance_destroy(); break; } } } } if (global.Key) global.madeUdjatEye = true; with oBlock { if (not isInShop(x, y)) { n = point_distance(x, y, oEntrance.x, oEntrance.y); if (not isInShop(x, y) and rand(1,4) == 1 and not (y == oEntrance.y and n < 144) and n > 48) { if (collision_point(x+16, y, oSolid, 0, 0) and not collision_rectangle(x-32, y, x-1, y+15, oSolid, 0, 0)) { if (global.darkLevel) instance_create(x, y, oArrowTrapLeftLit); else instance_create(x, y, oArrowTrapLeft); instance_destroy(); } else if (collision_point(x-16, y, oSolid, 0, 0) and not collision_rectangle(x+16, y, x+48, y+15, oSolid, 0, 0)) { if (global.darkLevel) instance_create(x, y, oArrowTrapRightLit); else instance_create(x, y, oArrowTrapRight); instance_destroy(); } } } }}else if (global.levelType == 1){ global.ashGrave = false; if (global.cemetary) { with oLush { // generate graves if (not collision_point(x, y-16, oSolid, 0, 0) and not collision_point(x, y-16, oEntrance, 0, 0) and not collision_point(x, y-16, oExit, 0, 0) and rand(1,20) == 1 and x != 160 and x != 176 and x != 320 and x != 336 and x != 480 and x != 496) { obj = instance_create(x, y-16, oGrave); if (not global.ashGrave and rand(1,40) == 1) { obj.sprite_index = sGraveAsh; obj = instance_create(x+8, y+8, oShotgun); obj.cost = 0; obj.forSale = false; ashGrave = true; } else if (not collision_point(x+8, y+8, oTreasure, 0, 0)) { if (rand(1,2) == 1) instance_create(x+8, y+8, oGoldNugget); else if (rand(1,4) == 1) instance_create(x+8, y+8, oSapphireBig); else if (rand(1,6) == 1) instance_create(x+8, y+8, oEmeraldBig); else if (rand(1,8) == 1) instance_create(x+8, y+8, oRubyBig); } } } } with oSolid { // bg if (rand(1,100) == 1 and not collision_point(x, y-16, oSolid, 0, 0)) tile_add(bgTrees, 0, 0, 16, 48, x, y-32, 9005); if (not isInShop(x, y)) { if (y > 32 and collision_point(x, y-16, oSolid, 0, 0) and global.genMarketEntrance and not global.madeMarketEntrance) { obj = instance_place(x, y-16, oSolid); if (obj.type != "Tree" and type != "Altar" and not obj.invincible and rand(1,global.marketChance) <= 1) { instance_create(x, y-16, oXMarket); invincible = true; global.madeMarketEntrance = true; } else global.marketChance -= 1; } else if (type != "Tree" and type != "Altar" and y != 0 and not collision_rectangle(x, y-32, x+15, y-1, oSolid, false, true) and not collision_rectangle(x, y-16, x+15, y-1, oEnemy, 0, 0) and (not collision_point(x-16, y, oSolid, 0, 0) or not collision_point(x+16, y, oSolid, 0, 0)) and collision_point(x, y+16, oSolid, 0, 0) and not collision_point(x, y, oXMarket, 0, 0) and not isInShop(x, y) and point_distance(x, y, oEntrance.x, oEntrance.y) > 64) { if (global.darkLevel and not collision_point(x, y-32, oWater, 0, 0) and rand(1,20) == 1) { instance_create(x, y-32, oTikiTorch); } else if (rand(1,12) == 1 and x != 160 and x != 176 and x != 320 and x != 336 and x != 480 and x != 496) { if (collision_point(x, y-16, oSolid, 0, 0)) { sol = instance_nearest(x, y-16, oSolid); with sol { cleanDeath = true; instance_destroy(); } } instance_create(x, y, oSpearTrapBottom); if (global.darkLevel) instance_create(x, y-16, oSpearTrapLit); else instance_create(x, y-16, oSpearTrapTop); cleanDeath = true; instance_destroy(); } } if (type != "Altar") { if (global.cemetary) scrTreasureGen(10); else scrTreasureGen(); } // enemies if (scrGetRoomX(x) != global.startRoomX or scrGetRoomY(y-16) != global.startRoomY) { if (y < room_height - 64 and not collision_point(x, y+16, oSolid, 0, 0) and not collision_point(x, y+32, oSolid, 0, 0) and not collision_point(x, y+16, oWater, 0, 0) and not collision_point(x, y+32, oWater, 0, 0)) { if (global.cemetary) n = 60; else n = 80; if (global.darkLevel and rand(1,40) == 1) instance_create(x, y+16, oScarab); else if (rand(1,n) == 1) instance_create(x, y+16, oBat); // else if (rand(1,40) == 1) instance_create(x, y+16, oSpiderHang); } if (y > 16 and not collision_point(x, y-16, oSolid, 0, 0) and not collision_point(x, y, oEnemy, 0, 0) and not collision_point(x, y, oSpikes, 0, 0)) { if (global.cemetary) { if (rand(1,25) == 1) instance_create(x, y-16, oZombie); else if (rand(1,160) == 1) instance_create(x, y-16, oVampire); } else if (not collision_point(x, y-16, oWater, 0, 0)) { if (global.blackMarket and (y mod 128 == 0)) n = 0; // to prevent mantraps from spawning near shopkeepers in black market else n = 1; if (rand(1,60) == n) instance_create(x, y-16, oManTrap); else if (rand(1,60) == 1) instance_create(x, y-16, oCaveman); else if (rand(1,120) == 1) instance_create(x, y-16, oFireFrog); else if (rand(1,30) == 1) instance_create(x, y-16, oFrog); } else if (rand(1,120) == 1) instance_create(x, y-16, oFireFrog); else if (rand(1,30) == 1) instance_create(x, y-16, oFrog); } } // rock } } // force market entrance if (global.genMarketEntrance and not global.madeMarketEntrance) { with oSolid { if (y > 32 and collision_point(x, y-16, oSolid, 0, 0)) { obj = instance_place(x, y-16, oSolid); if (obj.type != "Tree" and type != "Altar" and not obj.invincible) { instance_create(x, y-16, oXMarket); invincible = true; global.madeMarketEntrance = true; } } } } with oVine { if (rand(1,15) == 1) instance_create(x, y, oMonkey); } with oWater { if (not collision_point(x, y, oSolid, 0, 0)) { if (rand(1,30) == 1) { if (global.cemetary) instance_create(x+4, y+4, oDeadFish); else instance_create(x+4, y+4, oPiranha); } } }}else if (global.levelType == 2){ with oSolid { if (not isInShop(x, y)) { // enemies n = 30; if (global.yetiLair) n = 90; if (scrGetRoomX(x) != global.startRoomX or scrGetRoomY(y-16) != global.startRoomY) { if (y < room_height - 64 and not collision_point(x, y+16, oSolid, 0, 0) and not collision_point(x, y+32, oSolid, 0, 0) and not collision_point(x, y+16, oWater, 0, 0) and not collision_point(x, y+32, oWater, 0, 0)) { if (global.darkLevel and rand(1,40) == 1) instance_create(x, y+16, oScarab); } else if (y > 16 and y < 592 and not collision_point(x, y-16, oSolid, 0, 0) and not isInShop(x, y)) { if (rand(1,n) == 1) instance_create(x, y-16, oUFO); } } if (y > 16 and y < 592 and not collision_point(x, y-16, oSolid, 0, 0) and not collision_point(x+8, y-8, oEnemy, 0, 0) and not collision_point(x+8, y-1, oSpikes, 0, 0) and point_distance(x, y, oEntrance.x, oEntrance.y) > 64 and not isInShop(x, y)) { if (rand(1,10) == 1 and sprite_index = sDark and not collision_rectangle(x, y-64, x+15, y-1, oSolid, 0, 0) and distance_to_object(oExit) > 64) instance_create(x, y-16, oSpringTrap); else if (rand(1,20) == 1 and point_distance(x, y, oEntrance.x, oEntrance.y) > 64) instance_create(x, y-16, oYeti); } if (type != "Altar") { scrTreasureGen(); } } }}else if (global.levelType == 3){ global.TombLord = false; global.genTombLord = false; if (global.currLevel == 13) global.genTombLord = true; else if (rand(1,4) == 1) global.genTombLord = true; global.genGoldEntrance = false; if (global.currLevel == 14) global.genGoldEntrance = true; global.madeGoldEntrance = false; with oSolid { // bg if (rand(1,100) == 1 and not collision_point(x, y-16, oSolid, 0, 0)) tile_add(bgStatues, 0, 0, 16, 48, x, y-32, 9005); if (not isInShop(x, y)) { // traps if (y > 32 and not collision_point(x, y-16, oSolid, 0, 0) and global.genGoldEntrance and not global.madeGoldEntrance) { if (rand(1,global.goldChance) == 1) { instance_create(x, y-16, oGoldDoor); invincible = true; global.madeGoldEntrance = true; } else global.goldChance -= 1; } else if (type != "Tree" and type != "Altar" and y != 0 and not collision_point(x, y-16, oSolid, 0, 0) and not collision_point(x, y-16, oLava, 0, 0) and not collision_rectangle(x, y-16, x+15, y-1, oEnemy, 0, 0) and not collision_point(x, y-32, oSolid, 0, 0) and (not collision_point(x-16, y, oSolid, 0, 0) or not collision_point(x+16, y, oSolid, 0, 0)) and collision_point(x, y+16, oSolid, 0, 0) and not isInShop(x, y) and x != 160 and x != 176 and x != 320 and x != 336 and x != 480 and x != 496) { if (rand(1,12) == 1 and point_distance(x, y, oEntrance.x, oEntrance.y) > 64) { // to keep the spear trap from plugging up lava passage if (collision_point(x-16, y-32, oSolid, 0, 0) and collision_point(x+16, y-32, oSolid, 0, 0) and not collision_point(x, y-32, oSolid, 0, 0)) { // do nothing } else { if (collision_point(x, y-16, oSolid, 0, 0)) { sol = instance_nearest(x, y-16, oSolid); with sol { cleanDeath = true; instance_destroy(); } } instance_create(x, y, oSpearTrapBottom); if (global.darkLevel) instance_create(x, y-16, oSpearTrapLit); else instance_create(x, y-16, oSpearTrapTop); cleanDeath = true; instance_destroy(); } } } // enemies if (y < room_height - 64 and not collision_point(x, y+16, oSolid, 0, 0) and not collision_point(x, y+32, oSolid, 0, 0) and not collision_point(x, y+16, oWater, 0, 0) and not collision_point(x, y+32, oWater, 0, 0)) { if (global.darkLevel and rand(1,40) == 1) instance_create(x, y+16, oScarab); } if (scrGetRoomX(x) != global.startRoomX or scrGetRoomY(y-16) != global.startRoomY and not collision_point(x, y-16, oEnemy, 0, 0)) { if (y > 16 and not collision_point(x, y-16, oSolid, 0, 0)) { if (global.genTombLord and not global.TombLord and not collision_rectangle(x, y-32, x+32, y-1, oSolid, 0, 0) and rand(1,40) == 1) { instance_create(x, y-32, oTombLord); global.TombLord = true; } else if (rand(1,40) == 1) instance_create(x, y-16, oCaveman); else if (rand(1,40) == 1) instance_create(x, y-16, oHawkman); else if (rand(1,60) == 1) { if (global.darkLevel) instance_create(x, y-16, oSmashTrapLit); else instance_create(x, y-16, oSmashTrap); } } } if (type != "Altar") { scrTreasureGen(); } } } // force generate gold door if (global.genGoldEntrance and not global.madeGoldEntrance) { with oSolid { if (y > 32 and not collision_point(x, y-16, oSolid, 0, 0)) { instance_create(x, y-16, oGoldDoor); invincible = true; global.madeGoldEntrance = true; break; } } } with oBlock { cleanDeath = true; if (not isInShop(x, y)) { n = point_distance(x, y, oEntrance.x, oEntrance.y); if (not isInShop(x, y) and rand(1,3) == 1 and not (y == oEntrance.y and n < 144) and n > 48) { if (collision_point(x+16, y, oSolid, 0, 0) and not collision_rectangle(x-32, y, x-1, y+15, oSolid, 0, 0)) { if (global.darkLevel) instance_create(x, y, oArrowTrapLeftLit); else instance_create(x, y, oArrowTrapLeft); instance_destroy(); } else if (collision_point(x-16, y, oSolid, 0, 0) and not collision_rectangle(x+16, y, x+48, y+15, oSolid, 0, 0)) { if (global.darkLevel) instance_create(x, y, oArrowTrapRightLit); else instance_create(x, y, oArrowTrapRight); instance_destroy(); } } } }}// add box of flares to dark levelif (global.darkLevel){ with oEntrance { if (not collision_point(x-16, y, oSolid, 0, 0)) { instance_create(x-16+8, y+8, oFlareCrate); } else if (not collision_point(x+16, y, oSolid, 0, 0)) { instance_create(x+16+8, y+8, oFlareCrate); } else { instance_create(x+8, y+8, oFlareCrate); } }}global.cleanSolids = false;