1 /**********************************************************************************
2 * Copyright (c) 2008, 2009 Derek Yu and Mossmouth, LLC
3 * Copyright (c) 2010, Moloch
4 * Copyright (c) 2018, Ketmar Dark
6 * This file is part of Spelunky.
8 * You can redistribute and/or modify Spelunky, including its source code, under
9 * the terms of the Spelunky User License.
11 * Spelunky is distributed in the hope that it will be entertaining and useful,
12 * but WITHOUT WARRANTY. Please see the Spelunky User License for more details.
14 * The Spelunky User License should be available in "Game Information", which
15 * can be found in the Resource Explorer, or as an external file called COPYING.
16 * If not, please obtain a new copy of Spelunky from <http://spelunkyworld.com/>
18 **********************************************************************************/
19 // room generation for Area 2, the Lush Jungle
20 class RoomGenT1 : RoomGen;
23 void generateBlackMarket (int rx, int ry) {
24 GameGlobal global = levgen.global;
26 strTemp = "0000000000"~
35 int roomPath = levgen.getRPath(rx, ry); //[scrGetRoomX(x), scrGetRoomY(y)];
36 int roomPathAbove = -1;
38 if (ry != 0) roomPathAbove = levgen.getRPath(rx, ry-1); //scrGetRoomY(y-128)];
40 if (rx == levgen.startRoomX && ry == levgen.startRoomY) {
42 int n = (roomPath == 2 ? global.randRoom(3, 4) : global.randRoom(1, 2));
44 case 1: { strTemp = "6000060000"~
53 case 2: { strTemp = "1111111111"~
63 case 3: { strTemp = "6000060000"~
72 case 4: { strTemp = "1111111111"~
81 default: FatalError("000");
83 } else if (rx == levgen.endRoomX && ry == levgen.endRoomY) {
85 int n = (roomPathAbove == 2 ? global.randRoom(1, 2) : global.randRoom(3, 4));
87 case 1: { strTemp = "0000000000"~
96 case 2: { strTemp = "0000000000"~
105 case 3: { strTemp = "6000060000"~
114 case 4: { strTemp = "1111111111"~
123 default: FatalError("001");
125 } else if (roomPath == 1) {
126 switch (global.randRoom(1, 8)) {
128 case 1: { strTemp = "6000060000"~
137 case 2: { strTemp = "6000060000"~
146 case 3: { strTemp = "6000060000"~
155 case 4: { strTemp = "6000060000"~
165 case 5: { strTemp = "1111111111"~
175 case 6: { strTemp = "0000000000"~
185 case 7: { strTemp = "0000000000"~
194 case 8: { strTemp = "0060000000"~
203 default: FatalError("002");
205 } else if (roomPath == 3) {
207 switch (global.randRoom(1, 7)) {
209 case 1: { strTemp = "0000000000"~
218 case 2: { strTemp = "0000000000"~
227 case 3: { strTemp = "0000000000"~
238 case 4: { strTemp = "0000000000"~
247 case 5: { strTemp = "0000000000"~
257 case 6: { strTemp = "0000000000"~
266 case 7: { strTemp = "0000000000"~
275 default: FatalError("003");
277 } else if (roomPath == LevelGen::RType.Shop4 && rx == 3/*496*/) {
279 strTemp = "0000000011"~
288 writeln("*** generated Ankh shop");
289 } else if (roomPath == LevelGen::RType.Shop4) {
291 strTemp = ".........."~
302 int n = global.randRoom(1, 5);
306 if (n == 1) { if (!global.genSupplyShop) { shopType = 'General'; global.genSupplyShop = true; } }
307 else if (n == 2) { if (!global.genBombShop) { shopType = 'Bomb'; global.genBombShop = true; } }
308 else if (n == 3) { if (!global.genWeaponShop) { shopType = 'Weapon'; global.genWeaponShop = true; } }
309 else if (n == 4) { if (!global.genRareShop) { shopType = 'Rare'; global.genRareShop = true; } }
310 else if (n == 5) { if (!global.genClothingShop) { shopType = 'Clothing'; global.genClothingShop = true; } }
314 shopType = 'General';
318 } else if (roomPath == LevelGen::RType.Shop5) {
320 strTemp = "1111111111"~
331 int n = (roomPathAbove != 2 ? global.randRoom(1, 6) : global.randRoom(1, 5));
333 case 1: { strTemp = "00G0000000"~
342 case 2: { strTemp = "0000000G00"~
351 case 3: { strTemp = "00000000G0"~
360 case 4: { strTemp = "0000000G00"~
369 case 5: { strTemp = "00G0000000"~
379 case 6: { strTemp = "1111111111"~
388 default: FatalError("007");
392 if (strTemp.length != 80) FatalError("ooops");
395 foreach (int i; 0..80) {
398 string strObs1 = "00000";
399 string strObs2 = "00000";
400 string strObs3 = "00000";
401 string strObs4 = "00000";
402 int tile = strTemp[i];
408 } else if (tile == "5") {
410 int n = (global.randRoom(1, 8) == 1 ? global.randRoom(100, 102) : global.randRoom(1, 2));
412 case 1: { strObs1 = "00000";
416 case 2: { strObs1 = "00000";
420 case 100: { strObs1 = "00000";
424 case 101: { strObs1 = "00000";
428 case 102: { strObs1 = "00000";
432 default: FatalError("009");
434 } else if (tile == "6") {
436 switch (global.randRoom(1, 4)) {
437 case 1: { strObs1 = "11112";
441 case 2: { strObs1 = "21111";
445 case 3: { strObs1 = "22222";
449 case 4: { strObs1 = "11111";
453 default: FatalError("00a");
455 } else if (tile == "V") {
457 switch (global.randRoom(1, 3)) {
458 case 1: { strObs1 = "L0L0L";
463 case 2: { strObs1 = "L0L0L";
468 case 3: { strObs1 = "0L0L0";
473 default: FatalError("00a");
477 if (tile == "5" || tile == "6" || tile == "8" || tile == "V") {
478 //strTemp = string_delete(strTemp, j, 5);
479 //strTemp = string_insert(strObs1, strTemp, j);
480 strTemp[j..j+5] = strObs1;
482 //strTemp = string_delete(strTemp, j, 5);
483 //strTemp = string_insert(strObs2, strTemp, j);
484 strTemp[j..j+5] = strObs2;
486 //strTemp = string_delete(strTemp, j, 5);
487 //strTemp = string_insert(strObs3, strTemp, j);
488 strTemp[j..j+5] = strObs3;
492 //strTemp = string_delete(strTemp, j, 5);
493 //strTemp = string_insert(strObs4, strTemp, j);
494 strTemp[j..j+5] = strObs4;
500 override void generate (int rx, int ry) {
501 GameGlobal global = levgen.global;
503 if (global.blackMarket) {
504 generateBlackMarket(rx, ry);
508 strTemp = "0000000000"~
517 int roomPath = levgen.getRPath(rx, ry); //[scrGetRoomX(x), scrGetRoomY(y)];
518 int roomPathAbove = -1;
519 shopType = 'General';
520 if (ry != 0) roomPathAbove = levgen.getRPath(rx, ry-1); //scrGetRoomY(y-128)];
522 if (rx == levgen.startRoomX && ry == levgen.startRoomY) {
524 int n = (roomPath == 2 ? global.randRoom(3, 4) : global.randRoom(1, 2));
526 case 1: { strTemp = "6000060000"~
535 case 2: { strTemp = "1111111111"~
545 case 3: { strTemp = "6000060000"~
554 case 4: { strTemp = "1111111111"~
563 default: FatalError("000");
565 } else if (rx == levgen.endRoomX && ry == levgen.endRoomY) {
567 int n = (global.lake == 1 ? 5 : roomPathAbove == 2 ? global.randRoom(1, 4) : global.randRoom(3, 4));
569 case 1: { strTemp = "0000000000"~
578 case 2: { strTemp = "0000000000"~
587 case 3: { strTemp = "6000060000"~
596 case 4: { strTemp = "1111111111"~
605 case 5: { strTemp = "0000000000"~
614 default: FatalError("001");
616 } else if (roomPath == 0 && global.randRoom(1, 3) <= 2) {
619 if (!global.altar && global.randRoom(1, 12) == 1) {
622 } else if (global.idol) {
623 n = global.randRoom(1, 8);
625 n = global.randRoom(1, 9);
626 if (n == 9) global.idol = true;
631 case 1: { strTemp = "0000000000"~
640 case 2: { strTemp = "1111111111"~
649 case 3: { strTemp = "1111111111"~
658 case 4: { strTemp = "1112002111"~
667 case 5: { strTemp = "1112002111"~
676 case 6: { strTemp = "1112002111"~
686 case 7: { strTemp = "0000000000"~
695 case 8: { strTemp = "0000000000"~
707 if (global.cemetary) strTemp = "tttttttttt"~
715 else strTemp = "0100000010"~
726 case 10: { strTemp = "2200000022"~
735 default: FatalError("002");
737 } else if (roomPath == 0 || roomPath == 1) {
739 switch (global.randRoom(1, 10)) {
741 case 1: { strTemp = "6000060000"~
750 case 2: { strTemp = "6000060000"~
759 case 3: { strTemp = "6000060000"~
768 case 4: { strTemp = "6000060000"~
777 case 5: { strTemp = "2222222222"~
787 if (global.randRoom(1,2) == 1) strTemp = "0L00000000"~
795 else strTemp = "00000000L0"~
806 case 7: { strTemp = "1111111111"~
816 case 8: { strTemp = "0000000000"~
826 case 9: { strTemp = "0000000000"~
835 case 10: { strTemp = "0060000000"~
844 default: FatalError("003");
846 } else if (roomPath == 3) {
848 switch (global.randRoom(1, 7)) {
850 case 1: { strTemp = "0000000000"~
859 case 2: { strTemp = "0000000000"~
868 case 3: { strTemp = "0000000000"~
879 case 4: { strTemp = "0000000000"~
888 case 5: { strTemp = "0000000000"~
898 case 6: { strTemp = "0000000000"~
907 case 7: { strTemp = "0000000000"~
916 default: FatalError("004");
918 } else if (roomPath == LevelGen::RType.Shop4) {
920 strTemp = "1111111111"~
928 int n = global.randRoom(1, 7);
929 if (global.scumGenShopType > 0) n = global.scumGenShopType;
931 case 1: shopType = 'General'; break;
932 case 2: shopType = 'Bomb'; break;
933 case 3: shopType = 'Weapon'; break;
934 case 4: shopType = 'Rare'; break;
935 case 5: shopType = 'Clothing'; break;
936 case 6: shopType = 'Craps';
937 strTemp = "1111111111"~
946 case 7: shopType = 'Kissing';
947 strTemp = "1111111111"~
955 /*oGame*/global.damsel = true;
957 default: FatalError("005");
959 } else if (roomPath == LevelGen::RType.Shop5) {
961 strTemp = "1111111111"~
969 int n = global.randRoom(1, 7);
970 if (global.scumGenShopType > 0) n = global.scumGenShopType;
972 case 1: shopType = 'General'; break;
973 case 2: shopType = 'Bomb'; break;
974 case 3: shopType = 'Weapon'; break;
975 case 4: shopType = 'Rare'; break;
976 case 5: shopType = 'Clothing'; break;
977 case 6: shopType = 'Craps';
978 strTemp = "1111111111"~
987 case 7: shopType = 'Kissing';
988 strTemp = "1111111111"~
996 /*oGame*/global.damsel = true;
998 default: FatalError("006");
1000 } else if (roomPath == 7) {
1002 switch (global.randRoom(1, 8)) {
1003 case 1: { strTemp = "wwwwwwwwww"~
1012 case 2: { strTemp = "wwwwwwwwww"~
1019 ",,,,,,,,,,";
1021 case 3: { strTemp = "wwwwwwwwww"~
1028 ",,,,,,,,,,";
1030 case 4: { strTemp = "wwwwwwwwww"~
1039 case 5: { strTemp = "wwwwwwwwww"~
1048 case 6: { strTemp = "wwwwwwwwww"~
1057 case 7: { strTemp = "wwwwwwwwww"~
1064 ",,,,,,,,,,";
1066 case 8: { strTemp = "wwwwwwwwww"~
1073 ",,,,,,,,,,";
1076 } else if (roomPath == 8) {
1078 int n = (roomPathAbove == 2 ? global.randRoom(1, 5) : global.randRoom(1, 8));
1080 case 1: { strTemp = "0000000000"~
1089 case 2: { strTemp = "0000000000"~
1098 case 3: { strTemp = "0000000000"~
1107 case 4: { strTemp = "0000000000"~
1116 case 5: { strTemp = "0000000000"~
1125 case 6: { strTemp = "0000220000"~
1134 case 7: { strTemp = "6000060000"~
1143 case 8: { strTemp = "0000220000"~
1153 } else if (roomPath == 9) {
1155 strTemp = "wwwwwwwwww"~
1162 ",,,,,,,,,,";
1165 int n = (roomPathAbove != 2 ? global.randRoom(1, 6) : global.randRoom(1, 5));
1167 case 1: { strTemp = "0000000000"~
1176 case 2: { strTemp = "0000000000"~
1185 case 3: { strTemp = "0000000000"~
1194 case 4: { strTemp = "0000000000"~
1203 case 5: { strTemp = "0000000000"~
1213 case 6: { strTemp = "1111111111"~
1222 default: FatalError("007");
1226 if (strTemp.length != 80) FatalError("ooops");
1229 foreach (int i; 0..80) {
1232 string strObs1 = "00000";
1233 string strObs2 = "00000";
1234 string strObs3 = "00000";
1235 string strObs4 = "00000";
1236 int tile = strTemp[i];
1242 } else if (tile == "5") {
1244 int n = (global.randRoom(1, 8) == 1 ? global.randRoom(100, 102) : global.randRoom(1, 2));
1246 case 1: { strObs1 = "00000";
1250 case 2: { strObs1 = "00000";
1254 case 100: { strObs1 = "00000";
1258 case 101: { strObs1 = "00000";
1262 case 102: { strObs1 = "00000";
1266 default: FatalError("009");
1268 } else if (tile == "6") {
1270 switch (global.randRoom(1, 4)) {
1271 case 1: { strObs1 = "11112";
1275 case 2: { strObs1 = "21111";
1279 case 3: { strObs1 = "22222";
1283 case 4: { strObs1 = "11111";
1287 default: FatalError("00a");
1289 } else if (tile == "V") {
1291 switch (global.randRoom(1, 3)) {
1292 case 1: { strObs1 = "L0L0L";
1297 case 2: { strObs1 = "L0L0L";
1302 case 3: { strObs1 = "0L0L0";
1307 default: FatalError("00a");
1311 if (tile == "5" || tile == "6" || tile == "8" || tile == "V") {
1312 //strTemp = string_delete(strTemp, j, 5);
1313 //strTemp = string_insert(strObs1, strTemp, j);
1314 strTemp[j..j+5] = strObs1;
1316 //strTemp = string_delete(strTemp, j, 5);
1317 //strTemp = string_insert(strObs2, strTemp, j);
1318 strTemp[j..j+5] = strObs2;
1320 //strTemp = string_delete(strTemp, j, 5);
1321 //strTemp = string_insert(strObs3, strTemp, j);
1322 strTemp[j..j+5] = strObs3;
1326 //strTemp = string_delete(strTemp, j, 5);
1327 //strTemp = string_insert(strObs4, strTemp, j);
1328 strTemp[j..j+5] = strObs4;
1334 override void genTileAt (int tileX, int tileY) {
1335 name shopType = levgen.roomShopType(tileX, tileY);
1336 //if (shopType && shopType != 'General') writeln("::: ", shopType, " :::");
1338 auto global = levgen.global;
1339 auto level = levgen.level;
1341 // border tiles (moved to LevelGen)
1342 if (tileX == 0 || tileY == 0 || tileX == level.tilesWidth-1 || tileY == level.tilesHeight-1) {
1345 // Great Lake is bottomless (nope)
1346 if (false /*global.lake && tileY == level.tilesHeight-1*/) {
1347 tile = level.MakeMapTile(tileX, tileY, 'oWaterSwim');
1349 tile = level.MakeMapTile(tileX, tileY, 'oEdgeBrick');
1350 tile.setSprite('sLush');
1351 tile.invincible = true;
1355 //tile.treasure = '';
1357 level.MakeMapTile(tileX, tileY, 'oLush');
1362 level.tileXY2roomXY(tileX, tileY, roomX, roomY);
1363 level.setTileTypeAt(tileX, tileY, levgen.getRPath(roomX, roomY));
1365 auto tile = levgen.roomGetXYTileType(tileX, tileY);
1367 if (tile == "0") return;
1370 if (level.checkTileAtPoint(tileX*16, tileY*16)) return;
1371 level.MakeMapTile(tileX, tileY, 'oLush');
1376 if (global.randRoom(1, 2) != 1) return;
1377 if (level.checkTileAtPoint(tileX*16, tileY*16)) return;
1378 level.MakeMapTile(tileX, tileY, 'oLush');
1383 if (level.checkTileAtPoint(tileX*16, tileY*16)) return;
1384 level.MakeMapTile(tileX, tileY, 'oTemple');
1389 if (level.checkTileAtPoint(tileX*16, tileY*16)) return;
1390 level.MakeMapTile(tileX, tileY, (global.randRoom(1, 2) == 1 ? 'oTemple' : 'oLush'));
1395 if (level.checkTileAtPoint(tileX*16, tileY*16)) return;
1396 level.MakeMapTile(tileX, tileY, (global.randRoom(1, 2) == 1 ? 'oWaterSwim' : 'oLush'));
1401 level.MakeMapTile(tileX, tileY, 'oVine');
1406 level.MakeMapTile(tileX, tileY, 'oVineTop');
1411 level.MakeMapTile(tileX, tileY, 'oLadderOrange');
1416 level.MakeMapTile(tileX, tileY, 'oLadderTop');
1421 if (global.randRoom(1, trunc(ceil(3.0/global.config.trapMult))) == 1) {
1422 level.MakeMapTile(tileX, tileY, level.scrGenSpikeType());
1428 level.MakeMapTile(tileX, tileY, level.scrGenSpikeType());
1433 level.MakeMapTile(tileX, tileY, 'oPushBlock');
1438 auto block = level.MakeMapTile(tileX, tileY+1, 'oLush');
1439 if (roomX == levgen.startRoomX && roomY == levgen.startRoomY) {
1440 level.MakeMapTile(tileX, tileY, 'oEntrance');
1441 level.spawnPlayerAt(tileX*16+8, tileY*16+8);
1443 level.MakeMapTile(tileX, tileY, 'oExit');
1444 block.invincible = true;
1451 level.scrLevGenCreateChest(tileX*16+8, tileY*16+8);
1456 level.MakeMapTile(tileX, tileY, 'oWaterSwim');
1457 level.scrLevGenCreateChest(tileX*16+8, tileY*16+8);
1462 level.MakeMapTile(tileX, tileY, 'oWaterSwim');
1467 auto obj = level.MakeMapObject(tileX*16+7, tileY*16+8, 'oAnkh');
1470 obj.shopType = 'Ankh';
1476 auto die = level.MakeMapObject(tileX*16+8, tileY*16+8, 'oDice');
1479 die.shopType = 'Craps';
1485 // k8: it looks like a brick in the water; dunno why it is done this way
1486 // it seems that water brick is created to mark it as "water"
1487 //k8:???level.MakeMapTile(tileX, tileY, 'oWaterSwim');
1488 level.MakeMapTile(tileX, tileY, 'oLush');
1489 //level.MarkTileAsWet(tileX, tileY);
1494 // k8: it looks like a brick in the water; dunno why it is done this way
1495 // it seems that water brick is created to mark it as "water"
1496 //k8:???level.MakeMapTile(tileX, tileY, 'oWaterSwim');
1497 if (global.randRoom(1, 2) == 1) {
1498 level.MakeMapTile(tileX, tileY, 'oLush');
1499 //level.MarkTileAsWet(tileX, tileY);
1505 // k8: it looks like a brick in the water; dunno why it is done this way
1506 // it seems that water brick is created to mark it as "water"
1507 level.MakeMapTile(tileX, tileY, 'oWaterSwim');
1508 level.MakeMapObject(tileX*16, tileY*16, 'oJaws'); // jaws crates are done in jaws object
1513 level.MakeMapObject(tileX*16+16, tileY*16+12, 'oGoldIdol');
1514 // create trap activator
1515 auto obj = level.MakeMapObject(tileX+1, tileY+1, 'oTikiCurseRemoveBricks');
1516 obj.active = false; // force it here
1521 level.MakeMapObject(tileX*16+16, tileY*16+12, 'oCrystalSkull');
1522 // create trap activator
1523 auto obj = level.MakeMapObject(tileX+1, tileY+1, 'oTikiCurseRemoveBricks');
1524 obj.active = false; // force it here
1529 if (level.checkTileAtPoint(tileX*16, tileY*16)) return;
1530 auto obj = level.MakeMapTile(tileX, tileY, 'oLush');
1531 obj.shopWall = true;
1536 if (shopType == 'Craps') level.MakeMapBackTile('bgDiceSign', 0, 0, 48, 32, tileX*16, tileY*16, 9004);
1541 //???: n = rand(1,6);
1542 auto obj = level.scrGenerateItem(tileX*16+8, tileY*16+8, GameLevel::GenItemSet.Craps);
1543 if (obj) obj.inDiceHouse = true;
1548 auto obj = level.MakeMapTile(tileX, tileY, /*'oSolid'*/'oSolidIceBlock');
1549 //obj.sprite_index = sIceBlock;
1550 obj.shopWall = true;
1555 if (global.murderer || global.thiefLevel > 0) {
1556 if (global.isDamsel) level.MakeMapBackTile('bgWanted', 32, 0, 32, 32, tileX*16, tileY*16, 9004);
1557 else if (global.isTunnelMan) level.MakeMapBackTile('bgWanted', 64, 0, 32, 32, tileX*16, tileY*16, 9004);
1558 else level.MakeMapBackTile('bgWanted', 0, 0, 32, 32, tileX*16, tileY*16, 9004);
1564 auto obj = level.MakeMapTile(tileX, tileY, 'oBrickSmooth');
1565 obj.setSprite('sLushSmooth');
1566 obj.shopWall = true;
1571 level.MakeMapTile(tileX, tileY, (shopType == 'Kissing' ? 'oLampRed' : 'oLamp'));
1576 //auto obj = MonsterShopkeeper(level.MakeMapObject(tileX*16, tileY*16, 'oShopkeeper'));
1577 //if (obj) obj.style = shopType;
1578 level.scrLevGenShopkeeper(tileX*16, tileY*16, shopType);
1583 if (shopType == 'General') level.MakeMapTile(tileX, tileY, 'oSignGeneral');
1584 else if (shopType == 'Bomb') level.MakeMapTile(tileX, tileY, 'oSignBomb');
1585 else if (shopType == 'Weapon') level.MakeMapTile(tileX, tileY, 'oSignWeapon');
1586 else if (shopType == 'Clothing') level.MakeMapTile(tileX, tileY, 'oSignClothing');
1587 else if (shopType == 'Rare') level.MakeMapTile(tileX, tileY, 'oSignRare');
1588 else if (shopType == 'Craps') level.MakeMapTile(tileX, tileY, 'oSignCraps');
1589 else if (shopType == 'Kissing') level.MakeMapTile(tileX, tileY, 'oSignKissing');
1590 else level.MakeMapTile(tileX, tileY, 'oSign');
1595 level.scrShopItemsGen(tileX*16, tileY*16, shopType);
1600 auto die = level.MakeMapObject(tileX*16+8, tileY*16+8, 'oDice');
1603 die.shopType = 'Craps';
1609 auto obj = level.MakeMapObject(tileX*16+8, tileY*16+8, 'oDamsel');
1612 obj.status = MonsterDamsel::SLAVE;
1613 obj.shopType = 'Kissing';
1619 auto obj = MapTileLushTrapBlock(level.MakeMapTile(tileX, tileY, 'oTrapBlock'));
1621 auto idol = level.findNearestObject(tileX*16, tileY*16, delegate bool (MapObject o) {
1622 return (o isa ItemGoldIdol);
1624 obj.deathTimer = 40-abs(obj.ix-(idol ? idol.ix-8 : 0));
1625 //writeln("DEATH TIMER: ", obj.deathTimer);
1626 if (obj.deathTimer < 0) obj.deathTimer = 0;
1632 level.MakeMapTile(tileX+0, tileY, 'oSacAltarLeft');
1633 level.MakeMapTile(tileX+1, tileY, 'oSacAltarRight');
1634 level.MakeMapBackTile('bgKaliBody', 0, 0, 64, 64, tileX*16-16, tileY*16-48, 10001);
1635 level.MakeMapTile(tileX+1, tileY-4, 'oKaliHead');
1640 int xpos = tileX*16, ypos = tileY*16;
1641 if (global.randRoom(1, 2) == 1) level.MakeMapObject(xpos, ypos, 'oFakeBones'); //k8: was without `==`
1642 else level.scrLevGenCreateJar(xpos+8, ypos+10);
1648 int xpos = tileX*16, ypos = tileY*16;
1649 level.MakeMapTileAtPix(xpos, ypos, 'oTree');
1655 foreach (int m; 0..5) {
1656 if (global.randRoom(0, m) > 2) break;
1657 if (!level.isSolidAtPoint(tx, ty-16) &&
1658 !level.isSolidAtPoint(tx-16, ty-16) &&
1659 !level.isSolidAtPoint(tx+16, ty-16))
1661 level.MakeMapTileAtPix(tx, ty, 'oTree');
1663 if (!b1 && global.randRoom(1, 5) < 4) {
1664 level.MakeMapTileAtPix(tx+16, ty, 'oTreeBranch');
1669 if (!b2 && global.randRoom(1, 5) < 4) {
1670 level.MakeMapTileAtPix(tx-16, ty, 'oTreeBranch');
1681 level.MakeMapTileAtPix(tx-16, ty+16, 'oLeaves');
1682 level.MakeMapTileAtPix(tx+16, ty+16, 'oLeaves');
1686 FatalError("INVALID JUNGLE BLOCK: '%s'", string.fromChar(tile));
1690 // ////////////////////////////////////////////////////////////////////////// //
1691 final bool cbIsObjectXMarket (MapObject o) { return false/*!(o isa ObjXMarketEntrance)*/; }
1692 final bool cbIsSolidOrWater (MapTile t) { return (t.solid || t.water || t.lava); }
1693 final bool cbIsWater (MapTile t) { return (t.water || t.lava); }
1694 final bool cbIsSolidOrSpikes (MapTile t) { return (t.solid || t.spikes); }
1695 final bool cbIsSpikes (MapTile t) { return (t.spikes); }
1698 override void genEntityAt (int tileX, int tileY) {
1699 auto global = levgen.global;
1700 auto level = levgen.level;
1702 int x = tileX*16, y = tileY*16;
1704 if (global.randRoom(1, 100) == 1 && !level.isSolidAtPoint(x, y-16)) {
1705 level.MakeMapBackTile('bgTrees', 0, 0, 16, 48, x, y-32, 9998);
1708 if (level.isInShop(tileX, tileY)) return;
1710 MapTile t = level.getTileAtGrid(tileX, tileY);
1712 if (tileY > 2 && level.isSolidAtPoint(x, y-16) && global.genMarketEntrance && !global.madeMarketEntrance) {
1713 auto obj = level.isSolidAtPoint(x, y-16);
1714 if (!obj.altar && !obj.tree && !obj.invincible && global.randRoom(1, global.marketChance) <= 1) {
1715 level.MakeMapTile(tileX, tileY-1, 'oXMarket');
1716 t.invincible = true;
1717 global.madeMarketEntrance = true;
1719 global.marketChance -= 1;
1721 } else if ((t && t.isInstanceAlive && !t.altar && !t.tree) && y != 0 &&
1722 !level.checkTilesInRect(x, y-32, 16, 32) &&
1723 !level.isObjectInRect(x, y-16, 16, 16, &level.cbIsObjectEnemy) &&
1724 (!level.isSolidAtPoint(x-16, y) || !level.isSolidAtPoint(x+16, y)) &&
1725 level.isSolidAtPoint(x, y+16) &&
1726 !level.isObjectAtPoint(x, y, &cbIsObjectXMarket) &&
1727 //!isInShop(x, y) && // in the original
1728 level.calcNearestEnterDist(x, y) > 64)
1730 if (global.darkLevel && !level.checkTileAtPoint(x, y-32, &level.cbCollisionWater) && global.randRoom(1, 20) == 1) {
1731 //level.MakeMapObject(x, y-32, 'oTikiTorch');
1732 level.MakeMapTile(tileX, tileY-2, 'oTikiTorch');
1733 } else if (!level.checkTilesInRect(tileX*16, (tileY-1)*16, 16, 16*2, castClass:MapTileSpearTrapBase) &&
1734 x != 160 && x != 176 && x != 320 && x != 336 && x != 480 && x != 496 &&
1735 global.randRoom(1, trunc(ceil(12.0/global.config.trapMult))) == 1) {
1736 // don't create tiki traps near the entrance
1737 if (level.calcNearestEnterDist(x, y) >= 32) {
1738 level.RemoveMapTileFromGrid(tileX, tileY-1, "spear trap top");
1739 level.MakeMapTile(tileX, tileY-1, (global.darkLevel ? 'oSpearTrapTopLit' : 'oSpearTrapTop'));
1740 level.RemoveMapTileFromGrid(tileX, tileY, "spear trap bottom");
1741 level.MakeMapTile(tileX, tileY, 'oSpearTrapBottom');
1742 if (t && t.isInstanceAlive) {
1743 t.cleanDeath = true;
1751 if (t && t.isInstanceAlive && !t.altar) {
1752 if (global.cemetary) level.scrTreasureGen(x, y, 10); else level.scrTreasureGen(x, y);
1756 level.tileXY2roomXY(tileX, tileY-1, roomX, roomY); //k8: why -1?
1759 if (roomX == level.startRoomX && roomY == level.startRoomY) return;
1761 if (!level.checkTileAtPoint(x, y+16, &cbIsSolidOrWater) &&
1762 !level.checkTileAtPoint(x, y+32, &cbIsSolidOrWater))
1765 int n = (global.cemetary ? 60 : 80);
1766 if (global.darkLevel && global.randRoom(1, 40) == 1) level.MakeMapObject(x, y+16, 'oScarab');
1767 else if (global.randRoom(1, trunc(ceil(float(n)/global.config.enemyMult))) == 1) level.MakeMapObject(x, y+16, 'oBat');
1768 // else if (randRoom(1, 40) == 1) MakeMapObject(x, y+16, 'oSpiderHang'); // in the original
1771 if (!level.isSolidAtPoint(x, y-16) &&
1772 !level.isObjectAtPoint(x, y, &level.cbIsObjectEnemy) &&
1773 !level.checkTileAtPoint(x, y, &cbIsSpikes))
1775 if (global.cemetary) {
1776 if (global.randRoom(1, trunc(ceil(25.0/global.config.enemyMult))) == 1) level.MakeMapObject(x, y-16, 'oZombie');
1777 else if (global.randRoom(1, trunc(ceil(160.0/global.config.enemyMult))) == 1) level.MakeMapObject(x, y-16, 'oVampire');
1778 } else if (!level.checkTileAtPoint(x, y-16, &cbIsWater)) {
1779 int n = (global.blackMarket && (y%128 == 0) ? 0 : 1); // to prevent mantraps from spawning near shopkeepers in black market
1780 if (global.randRoom(1, trunc(ceil(60.0/global.config.enemyMult))) == n) level.MakeMapObject(x, y-16, 'oManTrap');
1781 else if (global.randRoom(1, trunc(ceil(60.0/global.config.enemyMult))) == 1) level.MakeMapObject(x, y-16, 'oCaveman');
1782 else if (global.randRoom(1, trunc(ceil(120.0/global.config.enemyMult))) == 1) level.MakeMapObject(x, y-16, 'oFireFrog');
1783 else if (global.randRoom(1, trunc(ceil(30.0/global.config.enemyMult))) == 1) level.MakeMapObject(x, y-16, level.scrGenGetFrogType());
1785 else if (global.randRoom(1, trunc(ceil(120.0/global.config.enemyMult))) == 1) level.MakeMapObject(x, y-16, 'oFireFrog');
1786 else if (global.randRoom(1, trunc(ceil(30.0/global.config.enemyMult))) == 1) level.MakeMapObject(x, y-16, level.scrGenGetFrogType());