1 /**********************************************************************************
2 * Copyright (c) 2008, 2009 Derek Yu and Mossmouth, LLC
3 * Copyright (c) 2018, Ketmar Dark
5 * This file is part of Spelunky.
7 * You can redistribute and/or modify Spelunky, including its source code, under
8 * the terms of the Spelunky User License.
10 * Spelunky is distributed in the hope that it will be entertaining and useful,
11 * but WITHOUT WARRANTY. Please see the Spelunky User License for more details.
13 * The Spelunky User License should be available in "Game Information", which
14 * can be found in the Resource Explorer, or as an external file called COPYING.
15 * If not, please obtain a new copy of Spelunky from <http://spelunkyworld.com/>
17 **********************************************************************************/
18 // room generation for Area 2, the Lush Jungle
19 class RoomGenT1 : RoomGen;
22 void generateBlackMarket (int rx, int ry) {
23 GameGlobal global = levgen.global;
25 strTemp = "0000000000"~
34 int roomPath = levgen.getRPath(rx, ry); //[scrGetRoomX(x), scrGetRoomY(y)];
35 int roomPathAbove = -1;
37 if (ry != 0) roomPathAbove = levgen.getRPath(rx, ry-1); //scrGetRoomY(y-128)];
39 if (rx == levgen.startRoomX && ry == levgen.startRoomY) {
41 int n = (roomPath == 2 ? global.randRoom(3, 4) : global.randRoom(1, 2));
43 case 1: { strTemp = "6000060000"~
52 case 2: { strTemp = "1111111111"~
62 case 3: { strTemp = "6000060000"~
71 case 4: { strTemp = "1111111111"~
80 default: FatalError("000");
82 } else if (rx == levgen.endRoomX && ry == levgen.endRoomY) {
84 int n = (roomPathAbove == 2 ? global.randRoom(1, 2) : global.randRoom(3, 4));
86 case 1: { strTemp = "0000000000"~
95 case 2: { strTemp = "0000000000"~
104 case 3: { strTemp = "6000060000"~
113 case 4: { strTemp = "1111111111"~
122 default: FatalError("001");
124 } else if (roomPath == 1) {
125 switch (global.randRoom(1, 8)) {
127 case 1: { strTemp = "6000060000"~
136 case 2: { strTemp = "6000060000"~
145 case 3: { strTemp = "6000060000"~
154 case 4: { strTemp = "6000060000"~
164 case 5: { strTemp = "1111111111"~
174 case 6: { strTemp = "0000000000"~
184 case 7: { strTemp = "0000000000"~
193 case 8: { strTemp = "0060000000"~
202 default: FatalError("002");
204 } else if (roomPath == 3) {
206 switch (global.randRoom(1, 7)) {
208 case 1: { strTemp = "0000000000"~
217 case 2: { strTemp = "0000000000"~
226 case 3: { strTemp = "0000000000"~
237 case 4: { strTemp = "0000000000"~
246 case 5: { strTemp = "0000000000"~
256 case 6: { strTemp = "0000000000"~
265 case 7: { strTemp = "0000000000"~
274 default: FatalError("003");
276 } else if (roomPath == LevelGen::RType.Shop4 && rx == 3/*496*/) {
278 strTemp = "0000000011"~
287 writeln("*** generated Ankh shop");
288 } else if (roomPath == LevelGen::RType.Shop4) {
290 strTemp = ".........."~
301 int n = global.randRoom(1, 5);
305 if (n == 1) { if (!global.genSupplyShop) { shopType = 'General'; global.genSupplyShop = true; } }
306 else if (n == 2) { if (!global.genBombShop) { shopType = 'Bomb'; global.genBombShop = true; } }
307 else if (n == 3) { if (!global.genWeaponShop) { shopType = 'Weapon'; global.genWeaponShop = true; } }
308 else if (n == 4) { if (!global.genRareShop) { shopType = 'Rare'; global.genRareShop = true; } }
309 else if (n == 5) { if (!global.genClothingShop) { shopType = 'Clothing'; global.genClothingShop = true; } }
313 shopType = 'General';
317 } else if (roomPath == LevelGen::RType.Shop5) {
319 strTemp = "1111111111"~
330 int n = (roomPathAbove != 2 ? global.randRoom(1, 6) : global.randRoom(1, 5));
332 case 1: { strTemp = "00G0000000"~
341 case 2: { strTemp = "0000000G00"~
350 case 3: { strTemp = "00000000G0"~
359 case 4: { strTemp = "0000000G00"~
368 case 5: { strTemp = "00G0000000"~
378 case 6: { strTemp = "1111111111"~
387 default: FatalError("007");
391 if (strTemp.length != 80) FatalError("ooops");
394 foreach (int i; 0..80) {
397 string strObs1 = "00000";
398 string strObs2 = "00000";
399 string strObs3 = "00000";
400 string strObs4 = "00000";
401 int tile = strTemp[i];
407 } else if (tile == "5") {
409 int n = (global.randRoom(1, 8) == 1 ? global.randRoom(100, 102) : global.randRoom(1, 2));
411 case 1: { strObs1 = "00000";
415 case 2: { strObs1 = "00000";
419 case 100: { strObs1 = "00000";
423 case 101: { strObs1 = "00000";
427 case 102: { strObs1 = "00000";
431 default: FatalError("009");
433 } else if (tile == "6") {
435 switch (global.randRoom(1, 4)) {
436 case 1: { strObs1 = "11112";
440 case 2: { strObs1 = "21111";
444 case 3: { strObs1 = "22222";
448 case 4: { strObs1 = "11111";
452 default: FatalError("00a");
454 } else if (tile == "V") {
456 switch (global.randRoom(1, 3)) {
457 case 1: { strObs1 = "L0L0L";
462 case 2: { strObs1 = "L0L0L";
467 case 3: { strObs1 = "0L0L0";
472 default: FatalError("00a");
476 if (tile == "5" || tile == "6" || tile == "8" || tile == "V") {
477 //strTemp = string_delete(strTemp, j, 5);
478 //strTemp = string_insert(strObs1, strTemp, j);
479 strTemp[j..j+5] = strObs1;
481 //strTemp = string_delete(strTemp, j, 5);
482 //strTemp = string_insert(strObs2, strTemp, j);
483 strTemp[j..j+5] = strObs2;
485 //strTemp = string_delete(strTemp, j, 5);
486 //strTemp = string_insert(strObs3, strTemp, j);
487 strTemp[j..j+5] = strObs3;
491 //strTemp = string_delete(strTemp, j, 5);
492 //strTemp = string_insert(strObs4, strTemp, j);
493 strTemp[j..j+5] = strObs4;
499 override void generate (int rx, int ry) {
500 GameGlobal global = levgen.global;
502 if (global.blackMarket) {
503 generateBlackMarket(rx, ry);
507 strTemp = "0000000000"~
516 int roomPath = levgen.getRPath(rx, ry); //[scrGetRoomX(x), scrGetRoomY(y)];
517 int roomPathAbove = -1;
518 shopType = 'General';
519 if (ry != 0) roomPathAbove = levgen.getRPath(rx, ry-1); //scrGetRoomY(y-128)];
521 if (rx == levgen.startRoomX && ry == levgen.startRoomY) {
523 int n = (roomPath == 2 ? global.randRoom(3, 4) : global.randRoom(1, 2));
525 case 1: { strTemp = "6000060000"~
534 case 2: { strTemp = "1111111111"~
544 case 3: { strTemp = "6000060000"~
553 case 4: { strTemp = "1111111111"~
562 default: FatalError("000");
564 } else if (rx == levgen.endRoomX && ry == levgen.endRoomY) {
566 int n = (global.lake ? 5 : roomPathAbove == 2 ? global.randRoom(1, 4) : global.randRoom(3, 4));
568 case 1: { strTemp = "0000000000"~
577 case 2: { strTemp = "0000000000"~
586 case 3: { strTemp = "6000060000"~
595 case 4: { strTemp = "1111111111"~
604 case 5: { strTemp = "0000000000"~
613 default: FatalError("001");
615 } else if (roomPath == 0 && global.randRoom(1, 3) <= 2) {
618 if (!global.altar && global.randRoom(1, 12) == 1) {
621 } else if (global.idol) {
622 n = global.randRoom(1, 8);
624 n = global.randRoom(1, 9);
625 if (n == 9) global.idol = true;
630 case 1: { strTemp = "0000000000"~
639 case 2: { strTemp = "1111111111"~
648 case 3: { strTemp = "1111111111"~
657 case 4: { strTemp = "1112002111"~
666 case 5: { strTemp = "1112002111"~
675 case 6: { strTemp = "1112002111"~
685 case 7: { strTemp = "0000000000"~
694 case 8: { strTemp = "0000000000"~
706 if (global.cemetary) strTemp = "tttttttttt"~
714 else strTemp = "0100000010"~
725 case 10: { strTemp = "2200000022"~
734 default: FatalError("002");
736 } else if (roomPath == 0 || roomPath == 1) {
738 switch (global.randRoom(1, 10)) {
740 case 1: { strTemp = "6000060000"~
749 case 2: { strTemp = "6000060000"~
758 case 3: { strTemp = "6000060000"~
767 case 4: { strTemp = "6000060000"~
776 case 5: { strTemp = "2222222222"~
786 if (global.randRoom(1,2) == 1) strTemp = "0L00000000"~
794 else strTemp = "00000000L0"~
805 case 7: { strTemp = "1111111111"~
815 case 8: { strTemp = "0000000000"~
825 case 9: { strTemp = "0000000000"~
834 case 10: { strTemp = "0060000000"~
843 default: FatalError("003");
845 } else if (roomPath == 3) {
847 switch (global.randRoom(1, 7)) {
849 case 1: { strTemp = "0000000000"~
858 case 2: { strTemp = "0000000000"~
867 case 3: { strTemp = "0000000000"~
878 case 4: { strTemp = "0000000000"~
887 case 5: { strTemp = "0000000000"~
897 case 6: { strTemp = "0000000000"~
906 case 7: { strTemp = "0000000000"~
915 default: FatalError("004");
917 } else if (roomPath == LevelGen::RType.Shop4) {
919 strTemp = "1111111111"~
927 int n = global.randRoom(1, 7);
928 if (global.scumGenShopType > 0) n = global.scumGenShopType;
930 case 1: shopType = 'General'; break;
931 case 2: shopType = 'Bomb'; break;
932 case 3: shopType = 'Weapon'; break;
933 case 4: shopType = 'Rare'; break;
934 case 5: shopType = 'Clothing'; break;
935 case 6: shopType = 'Craps';
936 strTemp = "1111111111"~
945 case 7: shopType = 'Kissing';
946 strTemp = "1111111111"~
954 /*oGame*/global.damsel = true;
956 default: FatalError("005");
958 } else if (roomPath == LevelGen::RType.Shop5) {
960 strTemp = "1111111111"~
968 int n = global.randRoom(1, 7);
969 if (global.scumGenShopType > 0) n = global.scumGenShopType;
971 case 1: shopType = 'General'; break;
972 case 2: shopType = 'Bomb'; break;
973 case 3: shopType = 'Weapon'; break;
974 case 4: shopType = 'Rare'; break;
975 case 5: shopType = 'Clothing'; break;
976 case 6: shopType = 'Craps';
977 strTemp = "1111111111"~
986 case 7: shopType = 'Kissing';
987 strTemp = "1111111111"~
995 /*oGame*/global.damsel = true;
997 default: FatalError("006");
999 } else if (roomPath == 7) {
1001 switch (global.randRoom(1, 8)) {
1002 case 1: { strTemp = "wwwwwwwwww"~
1011 case 2: { strTemp = "wwwwwwwwww"~
1018 ",,,,,,,,,,";
1020 case 3: { strTemp = "wwwwwwwwww"~
1027 ",,,,,,,,,,";
1029 case 4: { strTemp = "wwwwwwwwww"~
1038 case 5: { strTemp = "wwwwwwwwww"~
1047 case 6: { strTemp = "wwwwwwwwww"~
1056 case 7: { strTemp = "wwwwwwwwww"~
1063 ",,,,,,,,,,";
1065 case 8: { strTemp = "wwwwwwwwww"~
1072 ",,,,,,,,,,";
1075 } else if (roomPath == 8) {
1077 int n = (roomPathAbove == 2 ? global.randRoom(1, 5) : global.randRoom(1, 8));
1079 case 1: { strTemp = "0000000000"~
1088 case 2: { strTemp = "0000000000"~
1097 case 3: { strTemp = "0000000000"~
1106 case 4: { strTemp = "0000000000"~
1115 case 5: { strTemp = "0000000000"~
1124 case 6: { strTemp = "0000220000"~
1133 case 7: { strTemp = "6000060000"~
1142 case 8: { strTemp = "0000220000"~
1152 } else if (roomPath == 9) {
1154 strTemp = "wwwwwwwwww"~
1161 ",,,,,,,,,,";
1164 int n = (roomPathAbove != 2 ? global.randRoom(1, 6) : global.randRoom(1, 5));
1166 case 1: { strTemp = "0000000000"~
1175 case 2: { strTemp = "0000000000"~
1184 case 3: { strTemp = "0000000000"~
1193 case 4: { strTemp = "0000000000"~
1202 case 5: { strTemp = "0000000000"~
1212 case 6: { strTemp = "1111111111"~
1221 default: FatalError("007");
1225 if (strTemp.length != 80) FatalError("ooops");
1228 foreach (int i; 0..80) {
1231 string strObs1 = "00000";
1232 string strObs2 = "00000";
1233 string strObs3 = "00000";
1234 string strObs4 = "00000";
1235 int tile = strTemp[i];
1241 } else if (tile == "5") {
1243 int n = (global.randRoom(1, 8) == 1 ? global.randRoom(100, 102) : global.randRoom(1, 2));
1245 case 1: { strObs1 = "00000";
1249 case 2: { strObs1 = "00000";
1253 case 100: { strObs1 = "00000";
1257 case 101: { strObs1 = "00000";
1261 case 102: { strObs1 = "00000";
1265 default: FatalError("009");
1267 } else if (tile == "6") {
1269 switch (global.randRoom(1, 4)) {
1270 case 1: { strObs1 = "11112";
1274 case 2: { strObs1 = "21111";
1278 case 3: { strObs1 = "22222";
1282 case 4: { strObs1 = "11111";
1286 default: FatalError("00a");
1288 } else if (tile == "V") {
1290 switch (global.randRoom(1, 3)) {
1291 case 1: { strObs1 = "L0L0L";
1296 case 2: { strObs1 = "L0L0L";
1301 case 3: { strObs1 = "0L0L0";
1306 default: FatalError("00a");
1310 if (tile == "5" || tile == "6" || tile == "8" || tile == "V") {
1311 //strTemp = string_delete(strTemp, j, 5);
1312 //strTemp = string_insert(strObs1, strTemp, j);
1313 strTemp[j..j+5] = strObs1;
1315 //strTemp = string_delete(strTemp, j, 5);
1316 //strTemp = string_insert(strObs2, strTemp, j);
1317 strTemp[j..j+5] = strObs2;
1319 //strTemp = string_delete(strTemp, j, 5);
1320 //strTemp = string_insert(strObs3, strTemp, j);
1321 strTemp[j..j+5] = strObs3;
1325 //strTemp = string_delete(strTemp, j, 5);
1326 //strTemp = string_insert(strObs4, strTemp, j);
1327 strTemp[j..j+5] = strObs4;
1333 override void genTileAt (int tileX, int tileY) {
1334 name shopType = levgen.roomShopType(tileX, tileY);
1335 //if (shopType && shopType != 'General') writeln("::: ", shopType, " :::");
1337 auto global = levgen.global;
1338 auto level = levgen.level;
1340 // border tiles (moved to LevelGen)
1341 if (tileX == 0 || tileY == 0 || tileX == level.tilesWidth-1 || tileY == level.tilesHeight-1) {
1344 // Great Lake is bottomless (nope)
1345 if (false /*global.lake && tileY == level.tilesHeight-1*/) {
1346 tile = level.MakeMapTile(tileX, tileY, 'oWaterSwim');
1348 tile = level.MakeMapTile(tileX, tileY, 'oEdgeBrick');
1349 tile.setSprite('sLush');
1350 tile.invincible = true;
1354 //tile.treasure = '';
1356 level.MakeMapTile(tileX, tileY, 'oLush');
1361 level.tileXY2roomXY(tileX, tileY, roomX, roomY);
1362 level.setTileTypeAt(tileX, tileY, levgen.getRPath(roomX, roomY));
1364 auto tile = levgen.roomGetXYTileType(tileX, tileY);
1366 if (tile == "0") return;
1369 if (level.checkTileAtPoint(tileX*16, tileY*16)) return;
1370 level.MakeMapTile(tileX, tileY, 'oLush');
1375 if (global.randRoom(1, 2) != 1) return;
1376 if (level.checkTileAtPoint(tileX*16, tileY*16)) return;
1377 level.MakeMapTile(tileX, tileY, 'oLush');
1382 if (level.checkTileAtPoint(tileX*16, tileY*16)) return;
1383 level.MakeMapTile(tileX, tileY, 'oTemple');
1388 if (level.checkTileAtPoint(tileX*16, tileY*16)) return;
1389 level.MakeMapTile(tileX, tileY, (global.randRoom(1, 2) == 1 ? 'oTemple' : 'oLush'));
1394 if (level.checkTileAtPoint(tileX*16, tileY*16)) return;
1395 level.MakeMapTile(tileX, tileY, (global.randRoom(1, 2) == 1 ? 'oWaterSwim' : 'oLush'));
1400 level.MakeMapTile(tileX, tileY, 'oVine');
1405 level.MakeMapTile(tileX, tileY, 'oVineTop');
1410 level.MakeMapTile(tileX, tileY, 'oLadderOrange');
1415 level.MakeMapTile(tileX, tileY, 'oLadderTop');
1420 if (global.randRoom(1, trunc(ceil(3.0/global.config.trapMult))) == 1) {
1421 level.MakeMapTile(tileX, tileY, level.scrGenSpikeType());
1427 level.MakeMapTile(tileX, tileY, level.scrGenSpikeType());
1432 level.MakeMapTile(tileX, tileY, 'oPushBlock');
1437 auto block = level.MakeMapTile(tileX, tileY+1, 'oLush');
1438 if (roomX == levgen.startRoomX && roomY == levgen.startRoomY) {
1439 level.MakeMapTile(tileX, tileY, 'oEntrance');
1440 level.spawnPlayerAt(tileX*16+8, tileY*16+8);
1442 level.MakeMapTile(tileX, tileY, 'oExit');
1443 block.invincible = true;
1450 level.scrLevGenCreateChest(tileX*16+8, tileY*16+8);
1455 level.MakeMapTile(tileX, tileY, 'oWaterSwim');
1456 level.scrLevGenCreateChest(tileX*16+8, tileY*16+8);
1461 level.MakeMapTile(tileX, tileY, 'oWaterSwim');
1466 auto obj = level.MakeMapObject(tileX*16+7, tileY*16+8, 'oAnkh');
1467 if (obj) obj.forSale = true;
1472 auto die = level.MakeMapObject(tileX*16+8, tileY*16+8, 'oDice');
1473 if (die) die.forSale = true;
1478 // k8: it looks like a brick in the water; dunno why it is done this way
1479 // it seems that water brick is created to mark it as "water"
1480 //k8:???level.MakeMapTile(tileX, tileY, 'oWaterSwim');
1481 level.MakeMapTile(tileX, tileY, 'oLush');
1482 level.MarkTileAsWet(tileX, tileY);
1487 // k8: it looks like a brick in the water; dunno why it is done this way
1488 // it seems that water brick is created to mark it as "water"
1489 //k8:???level.MakeMapTile(tileX, tileY, 'oWaterSwim');
1490 if (global.randRoom(1, 2) == 1) {
1491 level.MakeMapTile(tileX, tileY, 'oLush');
1492 level.MarkTileAsWet(tileX, tileY);
1498 // k8: it looks like a brick in the water; dunno why it is done this way
1499 // it seems that water brick is created to mark it as "water"
1500 level.MakeMapTile(tileX, tileY, 'oWaterSwim');
1501 level.MakeMapObject(tileX*16, tileY*16, 'oJaws'); // jaws crates are done in jaws object
1506 level.MakeMapObject(tileX*16+16, tileY*16+12, 'oGoldIdol');
1507 // create trap activator
1508 auto obj = level.MakeMapObject(tileX+1, tileY+1, 'oTikiCurseRemoveBricks');
1509 obj.active = false; // force it here
1514 level.MakeMapObject(tileX*16+16, tileY*16+12, 'oCrystalSkull');
1515 // create trap activator
1516 auto obj = level.MakeMapObject(tileX+1, tileY+1, 'oTikiCurseRemoveBricks');
1517 obj.active = false; // force it here
1522 if (level.checkTileAtPoint(tileX*16, tileY*16)) return;
1523 auto obj = level.MakeMapTile(tileX, tileY, 'oLush');
1524 obj.shopWall = true;
1529 if (shopType == 'Craps') level.MakeMapBackTile('bgDiceSign', 0, 0, 48, 32, tileX*16, tileY*16, 9004);
1534 //???: n = rand(1,6);
1535 auto obj = level.scrGenerateItem(tileX*16+8, tileY*16+8, GameLevel::GenItemSet.Craps);
1536 if (obj) obj.inDiceHouse = true;
1541 auto obj = level.MakeMapTile(tileX, tileY, /*'oSolid'*/'oSolidIceBlock');
1542 //obj.sprite_index = sIceBlock;
1543 obj.shopWall = true;
1548 if (global.murderer || global.thiefLevel > 0) {
1549 if (global.isDamsel) level.MakeMapBackTile('bgWanted', 32, 0, 32, 32, tileX*16, tileY*16, 9004);
1550 else if (global.isTunnelMan) level.MakeMapBackTile('bgWanted', 64, 0, 32, 32, tileX*16, tileY*16, 9004);
1551 else level.MakeMapBackTile('bgWanted', 0, 0, 32, 32, tileX*16, tileY*16, 9004);
1557 auto obj = level.MakeMapTile(tileX, tileY, 'oBrickSmooth');
1558 obj.setSprite('sLushSmooth');
1559 obj.shopWall = true;
1564 level.MakeMapTile(tileX, tileY, (shopType == 'Kissing' ? 'oLampRed' : 'oLamp'));
1569 //auto obj = MonsterShopkeeper(level.MakeMapObject(tileX*16, tileY*16, 'oShopkeeper'));
1570 //if (obj) obj.style = shopType;
1571 level.scrLevGenShopkeeper(tileX*16, tileY*16, shopType);
1576 if (shopType == 'General') level.MakeMapTile(tileX, tileY, 'oSignGeneral');
1577 else if (shopType == 'Bomb') level.MakeMapTile(tileX, tileY, 'oSignBomb');
1578 else if (shopType == 'Weapon') level.MakeMapTile(tileX, tileY, 'oSignWeapon');
1579 else if (shopType == 'Clothing') level.MakeMapTile(tileX, tileY, 'oSignClothing');
1580 else if (shopType == 'Rare') level.MakeMapTile(tileX, tileY, 'oSignRare');
1581 else if (shopType == 'Craps') level.MakeMapTile(tileX, tileY, 'oSignCraps');
1582 else if (shopType == 'Kissing') level.MakeMapTile(tileX, tileY, 'oSignKissing');
1583 else level.MakeMapTile(tileX, tileY, 'oSign');
1588 level.scrShopItemsGen(tileX*16, tileY*16, shopType);
1593 auto die = level.MakeMapObject(tileX*16+8, tileY*16+8, 'oDice');
1594 if (die) die.forSale = true;
1599 auto obj = level.MakeMapObject(tileX*16+8, tileY*16+8, 'oDamsel');
1602 obj.status = MonsterDamsel::SLAVE;
1608 auto obj = MapTileLavaLushTrapBlock(level.MakeMapTile(tileX, tileY, 'oTrapBlock'));
1610 auto idol = level.findNearestObject(tileX*16, tileY*16, delegate bool (MapObject o) {
1611 return (o isa ItemGoldIdol);
1613 obj.deathTimer = 40-abs(obj.ix-(idol ? idol.ix-8 : 0));
1614 //writeln("DEATH TIMER: ", obj.deathTimer);
1615 if (obj.deathTimer < 0) obj.deathTimer = 0;
1621 level.MakeMapTile(tileX+0, tileY, 'oSacAltarLeft');
1622 level.MakeMapTile(tileX+1, tileY, 'oSacAltarRight');
1623 level.MakeMapBackTile('bgKaliBody', 0, 0, 64, 64, tileX*16-16, tileY*16-48, 10001);
1624 level.MakeMapTile(tileX+1, tileY-4, 'oKaliHead');
1629 int xpos = tileX*16, ypos = tileY*16;
1630 if (global.randRoom(1, 2) == 1) level.MakeMapObject(xpos, ypos, 'oFakeBones'); //k8: was without `==`
1631 else level.scrLevGenCreateJar(xpos+8, ypos+10);
1637 int xpos = tileX*16, ypos = tileY*16;
1638 level.MakeMapTileAtPix(xpos, ypos, 'oTree');
1644 foreach (int m; 0..5) {
1645 if (global.randRoom(0, m) > 2) break;
1646 if (!level.isSolidAtPoint(tx, ty-16) &&
1647 !level.isSolidAtPoint(tx-16, ty-16) &&
1648 !level.isSolidAtPoint(tx+16, ty-16))
1650 level.MakeMapTileAtPix(tx, ty, 'oTree');
1652 if (!b1 && global.randRoom(1, 5) < 4) {
1653 level.MakeMapTileAtPix(tx+16, ty, 'oTreeBranch');
1658 if (!b2 && global.randRoom(1, 5) < 4) {
1659 level.MakeMapTileAtPix(tx-16, ty, 'oTreeBranch');
1670 level.MakeMapTileAtPix(tx-16, ty+16, 'oLeaves');
1671 level.MakeMapTileAtPix(tx+16, ty+16, 'oLeaves');
1675 FatalError("INVALID JUNGLE BLOCK: '%s'", string.fromChar(tile));
1679 // ////////////////////////////////////////////////////////////////////////// //
1680 final bool cbIsObjectXMarket (MapObject o) { return false/*!(o isa ObjXMarketEntrance)*/; }
1681 final bool cbIsSolidOrWater (MapTile t) { return (t.solid || t.water || t.lava); }
1682 final bool cbIsWater (MapTile t) { return (t.water || t.lava); }
1683 final bool cbIsSolidOrSpikes (MapTile t) { return (t.solid || t.spikes); }
1684 final bool cbIsSpikes (MapTile t) { return (t.spikes); }
1687 override void genEntityAt (int tileX, int tileY) {
1688 auto global = levgen.global;
1689 auto level = levgen.level;
1691 int x = tileX*16, y = tileY*16;
1693 if (global.randRoom(1, 100) == 1 && !level.isSolidAtPoint(x, y-16)) {
1694 level.MakeMapBackTile('bgTrees', 0, 0, 16, 48, x, y-32, 9998);
1697 if (level.isInShop(tileX, tileY)) return;
1699 MapTile t = level.getTileAt(tileX, tileY);
1701 if (y > 32 && level.isSolidAtPoint(x, y-16) && global.genMarketEntrance && !global.madeMarketEntrance) {
1702 auto obj = level.isSolidAtPoint(x, y-16);
1703 if (!obj.altar && !obj.tree && !obj.invincible && global.randRoom(1, global.marketChance) <= 1) {
1704 level.MakeMapTile(tileX, tileY-1, 'oXMarket');
1705 t.invincible = true;
1706 global.madeMarketEntrance = true;
1708 global.marketChance -= 1;
1710 } else if ((!t || !t.altar && !t.tree) && y != 0 &&
1711 !level.checkTilesInRect(x, y-32, 16, 32) &&
1712 !level.isObjectInRect(x, y-16, 16, 16, &level.cbIsObjectEnemy) &&
1713 (!level.isSolidAtPoint(x-16, y) || !level.isSolidAtPoint(x+16, y)) &&
1714 level.isSolidAtPoint(x, y+16) &&
1715 !level.isObjectAtPoint(x, y, &cbIsObjectXMarket) &&
1716 //!isInShop(x, y) && // in the original
1717 level.calcNearestEnterDist(x, y) > 64)
1719 if (global.darkLevel && !level.checkTileAtPoint(x, y-32, &level.cbCollisionWater) && global.randRoom(1, 20) == 1) {
1720 //level.MakeMapObject(x, y-32, 'oTikiTorch');
1721 level.MakeMapTile(tileX, tileY-2, 'oTikiTorch');
1722 } else if (global.randRoom(1, trunc(ceil(12.0/global.config.trapMult))) == 1 && x != 160 && x != 176 && x != 320 && x != 336 && x != 480 && x != 496) {
1723 // don't create tiki traps near the entrance
1724 //writeln("===================");
1725 //!dbg:if (level.calcNearestEnterDist(x, y) < 64) writeln("************** edist: ", level.calcNearestEnterDist(x, y));
1727 writeln("trap at: (", x, ",", y, ")");
1728 foreach (MapTile ttt; level.allEnters) {
1729 int xc = x-ttt.xCenter, c = py-ttt.yCenter;
1730 int distsq = xc*xc+yc*yc;
1731 writeln(" exit at: (", ttt.ix, ",", ttt.iy, "); center at: (", ttt.xCenter, ",", ttt.yCenter, "); dist=", round(sqrt(distsq)));
1734 if (level.calcNearestEnterDist(x, y) >= 32) {
1736 if (level.isSolidAtPoint(x, y-16)) {
1737 auto sol = level.isSolidAtPoint(x, y-16);
1739 wet = sol.water || sol.wet;
1740 sol.cleanDeath = true;
1741 sol.instanceRemove();
1742 //with (sol) { cleanDeath = true; instance_destroy(); }
1745 (level.isLiquidAtPoint(x-2, y) || level.isLiquidAtPoint(x+18, y) ||
1746 level.isLiquidAtPoint(x-2, y-16) || level.isLiquidAtPoint(x+18, y-16)))
1750 auto trap = level.MakeMapTile(tileX, tileY, 'oSpearTrapBottom');
1752 trap = level.MakeMapTile(tileX, tileY-1, (global.darkLevel ? 'oSpearTrapLit' : 'oSpearTrapTop'));
1754 //if (wet) writeln("generated WET spear trap");
1756 t.cleanDeath = true;
1763 if (t && t.isInstanceAlive && !t.altar) {
1764 if (global.cemetary) level.scrTreasureGen(x, y, 10); else level.scrTreasureGen(x, y);
1768 level.tileXY2roomXY(tileX, tileY-1, roomX, roomY); //k8: why -1?
1771 if (roomX == level.startRoomX && roomY == level.startRoomY) return;
1773 if (!level.checkTileAtPoint(x, y+16, &cbIsSolidOrWater) &&
1774 !level.checkTileAtPoint(x, y+32, &cbIsSolidOrWater))
1777 int n = (global.cemetary ? 60 : 80);
1778 if (global.darkLevel && global.randRoom(1, 40) == 1) level.MakeMapObject(x, y+16, 'oScarab');
1779 else if (global.randRoom(1, trunc(ceil(float(n)/global.config.enemyMult))) == 1) level.MakeMapObject(x, y+16, 'oBat');
1780 // else if (randRoom(1, 40) == 1) MakeMapObject(x, y+16, 'oSpiderHang'); // in the original
1783 if (!level.isSolidAtPoint(x, y-16) &&
1784 !level.isObjectAtPoint(x, y, &level.cbIsObjectEnemy) &&
1785 !level.checkTileAtPoint(x, y, &cbIsSpikes))
1787 if (global.cemetary) {
1788 if (global.randRoom(1, trunc(ceil(25.0/global.config.enemyMult))) == 1) level.MakeMapObject(x, y-16, 'oZombie');
1789 else if (global.randRoom(1, trunc(ceil(160.0/global.config.enemyMult))) == 1) level.MakeMapObject(x, y-16, 'oVampire');
1790 } else if (!level.checkTileAtPoint(x, y-16, &cbIsWater)) {
1791 int n = (global.blackMarket && (y%128 == 0) ? 0 : 1); // to prevent mantraps from spawning near shopkeepers in black market
1792 if (global.randRoom(1, trunc(ceil(60.0/global.config.enemyMult))) == n) level.MakeMapObject(x, y-16, 'oManTrap');
1793 else if (global.randRoom(1, trunc(ceil(60.0/global.config.enemyMult))) == 1) level.MakeMapObject(x, y-16, 'oCaveman');
1794 else if (global.randRoom(1, trunc(ceil(120.0/global.config.enemyMult))) == 1) level.MakeMapObject(x, y-16, 'oFireFrog');
1795 else if (global.randRoom(1, trunc(ceil(30.0/global.config.enemyMult))) == 1) level.MakeMapObject(x, y-16, level.scrGenGetFrogType());
1797 else if (global.randRoom(1, trunc(ceil(120.0/global.config.enemyMult))) == 1) level.MakeMapObject(x, y-16, 'oFireFrog');
1798 else if (global.randRoom(1, trunc(ceil(30.0/global.config.enemyMult))) == 1) level.MakeMapObject(x, y-16, level.scrGenGetFrogType());