1 /*-------------------------------------------------------------------------------
2
3 BARONY
4 File: maps.cpp
5 Desc: level generator code
6
7 Copyright 2013-2016 (c) Turning Wheel LLC, all rights reserved.
8 See LICENSE for details.
9
10 -------------------------------------------------------------------------------*/
11
12 #include "main.hpp"
13 #include "game.hpp"
14 #include "stat.hpp"
15 #include "entity.hpp"
16 #include "files.hpp"
17 #include "items.hpp"
18 #include "prng.hpp"
19 #include "monster.hpp"
20 #include "magic/magic.hpp"
21 #include "interface/interface.hpp"
22 #include "book.hpp"
23 #include "net.hpp"
24 #include "paths.hpp"
25 #include "collision.hpp"
26 #include "player.hpp"
27 #include "scores.hpp"
28 #include "mod_tools.hpp"
29 #include "menu.hpp"
30
31 int startfloor = 0;
32
33 /*-------------------------------------------------------------------------------
34
35 monsterCurve
36
37 selects a monster randomly, taking into account the region the monster
38 is being spawned in
39
40 -------------------------------------------------------------------------------*/
41
monsterCurve(int level)42 int monsterCurve(int level)
43 {
44 if ( !strncmp(map.name, "The Mines", 9) ) // the mines
45 {
46 switch ( rand() % 10 )
47 {
48 case 0:
49 case 1:
50 case 2:
51 case 3:
52 return RAT;
53 case 4:
54 case 5:
55 case 6:
56 case 7:
57 return SKELETON;
58 case 8:
59 if ( level >= 2 )
60 {
61 return SPIDER;
62 }
63 else
64 {
65 return SKELETON;
66 }
67 case 9:
68 if ( level >= 2 )
69 {
70 return TROLL;
71 }
72 else
73 {
74 return SKELETON;
75 }
76 break;
77 }
78 }
79 else if ( !strncmp(map.name, "The Swamp", 9) ) // the swamp
80 {
81 switch ( rand() % 10 )
82 {
83 case 0:
84 case 1:
85 return SPIDER;
86 case 2:
87 case 3:
88 case 4:
89 return GOBLIN;
90 case 5:
91 case 6:
92 case 7:
93 return SLIME;
94 case 8:
95 case 9:
96 return GHOUL;
97 }
98 }
99 else if ( !strncmp(map.name, "The Labyrinth", 13) ) // sand labyrinth
100 {
101 switch ( rand() % 20 )
102 {
103 case 0:
104 case 1:
105 case 2:
106 case 3:
107 return GOBLIN;
108 case 4:
109 case 5:
110 case 6:
111 case 7:
112 case 8:
113 return SCORPION;
114 case 9:
115 case 10:
116 case 11:
117 case 12:
118 return TROLL;
119 case 13:
120 case 14:
121 case 15:
122 case 16:
123 return SCARAB;
124 case 17:
125 case 18:
126 case 19:
127 return INSECTOID;
128 }
129 }
130 else if ( !strncmp(map.name, "The Ruins", 9) ) // blue ruins
131 {
132 switch ( rand() % 10 )
133 {
134 case 0:
135 return GOBLIN;
136 case 1:
137 case 2:
138 case 3:
139 return GNOME;
140 case 4:
141 case 5:
142 case 6:
143 case 7:
144 return TROLL;
145 case 8:
146 if ( rand() % 10 > 0 )
147 {
148 return TROLL;
149 }
150 else
151 {
152 return VAMPIRE;
153 }
154 case 9:
155 return DEMON;
156 }
157 }
158 else if ( !strncmp(map.name, "Underworld", 10) ) // underworld
159 {
160 switch ( rand() % 10 )
161 {
162 case 0:
163 return SLIME;
164 case 1:
165 return SHADOW;
166 case 2:
167 case 3:
168 case 4:
169 return CREATURE_IMP;
170 case 5:
171 case 6:
172 case 7:
173 return GHOUL;
174 case 8:
175 case 9:
176 return SKELETON;
177 }
178 }
179 else if ( !strncmp(map.name, "Hell", 4) ) // hell
180 {
181 switch ( rand() % 20 )
182 {
183 case 0:
184 case 1:
185 return SUCCUBUS;
186 case 2:
187 case 3:
188 return INCUBUS;
189 case 4:
190 case 5:
191 case 6:
192 case 7:
193 case 8:
194 if ( strstr(map.name, "Boss") )
195 {
196 return DEMON; // we would otherwise lag bomb on the boss level
197 }
198 else
199 {
200 return CREATURE_IMP;
201 }
202 case 9:
203 case 10:
204 case 11:
205 case 12:
206 case 13:
207 case 14:
208 return DEMON;
209 case 15:
210 case 16:
211 case 17:
212 case 18:
213 return GOATMAN;
214 case 19:
215 return SHADOW;
216 }
217 }
218 else if ( !strncmp(map.name, "Caves", 5) )
219 {
220 if ( currentlevel <= 26 )
221 {
222 switch ( rand() % 15 )
223 {
224 case 0:
225 case 1:
226 case 2:
227 case 3:
228 case 4:
229 return KOBOLD;
230 case 5:
231 case 6:
232 return SCARAB;
233 case 7:
234 return AUTOMATON;
235 case 8:
236 case 9:
237 case 10:
238 case 11:
239 return INSECTOID;
240 case 12:
241 case 13:
242 if ( rand() % 2 == 0 )
243 {
244 return INCUBUS;
245 }
246 else
247 {
248 return INSECTOID;
249 }
250 case 14:
251 if ( rand() % 2 == 0 )
252 {
253 return CRYSTALGOLEM;
254 }
255 else
256 {
257 return COCKATRICE;
258 }
259 }
260 }
261 else
262 {
263 switch ( rand() % 15 )
264 {
265 case 0:
266 case 1:
267 case 2:
268 case 3:
269 return KOBOLD;
270 case 4:
271 return SCARAB;
272 case 5:
273 return AUTOMATON;
274 case 6:
275 case 7:
276 case 8:
277 case 9:
278 return INSECTOID;
279 case 10:
280 case 11:
281 case 12:
282 return CRYSTALGOLEM;
283 case 13:
284 return INCUBUS;
285 case 14:
286 return COCKATRICE;
287 }
288 }
289 }
290 else if ( !strncmp(map.name, "Citadel", 7) )
291 {
292 switch ( rand() % 15 )
293 {
294 case 0:
295 return KOBOLD;
296 case 1:
297 return SCARAB;
298 case 2:
299 case 3:
300 case 4:
301 case 5:
302 return GOATMAN;
303 case 6:
304 case 7:
305 return CRYSTALGOLEM;
306 case 8:
307 case 9:
308 return VAMPIRE;
309 case 10:
310 return SHADOW;
311 case 11:
312 return INCUBUS;
313 case 12:
314 return AUTOMATON;
315 case 13:
316 case 14:
317 return COCKATRICE;
318 }
319 }
320 return SKELETON; // basic monster
321 }
322
323 /*-------------------------------------------------------------------------------
324
325 generateDungeon
326
327 generates a level by drawing data from numerous files and connecting
328 their rooms together with tunnels.
329
330 -------------------------------------------------------------------------------*/
331
generateDungeon(char * levelset,Uint32 seed,std::tuple<int,int,int,int> mapParameters)332 int generateDungeon(char* levelset, Uint32 seed, std::tuple<int, int, int, int> mapParameters)
333 {
334 char* sublevelname, *subRoomName;
335 char sublevelnum[3];
336 map_t* tempMap, *subRoomMap;
337 list_t mapList, *newList, *subRoomList, subRoomMapList;
338 node_t* node, *node2, *node3, *nextnode, *subRoomNode;
339 Sint32 c, i, j;
340 Sint32 numlevels, levelnum, levelnum2, subRoomNumLevels;
341 Sint32 x, y, z;
342 Sint32 x0, y0, x1, y1;
343 door_t* door, *newDoor;
344 bool* possiblelocations, *possiblelocations2, *possiblerooms;
345 bool* firstroomtile;
346 Sint32 numpossiblelocations, pickedlocation, subroomPickRoom;
347 Entity* entity, *entity2, *childEntity;
348 Uint32 levellimit;
349 list_t doorList;
350 node_t* doorNode, *subRoomDoorNode;
351 bool shoplevel = false;
352 map_t shopmap;
353 map_t secretlevelmap;
354 int secretlevelexit = 0;
355 bool *trapexcludelocations;
356 bool *monsterexcludelocations;
357 bool *lootexcludelocations;
358
359 if ( std::get<LEVELPARAM_CHANCE_SECRET>(mapParameters) == -1
360 && std::get<LEVELPARAM_CHANCE_DARKNESS>(mapParameters) == -1
361 && std::get<LEVELPARAM_CHANCE_MINOTAUR>(mapParameters) == -1
362 && std::get<LEVELPARAM_DISABLE_NORMAL_EXIT>(mapParameters) == 0 )
363 {
364 printlog("generating a dungeon from level set '%s' (seed %d)...\n", levelset, seed);
365 }
366 else
367 {
368 char generationLog[256] = "generating a dungeon from level set '%s'";
369 char tmpBuffer[32];
370 if ( std::get<LEVELPARAM_CHANCE_SECRET>(mapParameters) != -1 )
371 {
372 snprintf(tmpBuffer, 31, ", secret chance %d%%%%", std::get<LEVELPARAM_CHANCE_SECRET>(mapParameters));
373 strcat(generationLog, tmpBuffer);
374 }
375 if ( std::get<LEVELPARAM_CHANCE_DARKNESS>(mapParameters) != -1 )
376 {
377 snprintf(tmpBuffer, 31, ", darkmap chance %d%%%%", std::get<LEVELPARAM_CHANCE_DARKNESS>(mapParameters));
378 strcat(generationLog, tmpBuffer);
379 }
380 if ( std::get<LEVELPARAM_CHANCE_MINOTAUR>(mapParameters) != -1 )
381 {
382 snprintf(tmpBuffer, 31, ", minotaur chance %d%%%%", std::get<LEVELPARAM_CHANCE_MINOTAUR>(mapParameters));
383 strcat(generationLog, tmpBuffer);
384 }
385 if ( std::get<LEVELPARAM_DISABLE_NORMAL_EXIT>(mapParameters) != 0 )
386 {
387 snprintf(tmpBuffer, 31, ", disabled normal exit", std::get<LEVELPARAM_DISABLE_NORMAL_EXIT>(mapParameters));
388 strcat(generationLog, tmpBuffer);
389 }
390 strcat(generationLog, ", (seed %d)...\n");
391 printlog(generationLog, levelset, seed);
392
393 conductGameChallenges[CONDUCT_MODDED] = 1;
394 }
395
396 std::string fullMapPath;
397 fullMapPath = physfsFormatMapName(levelset);
398
399 int checkMapHash = -1;
400 if ( fullMapPath.empty() || loadMap(fullMapPath.c_str(), &map, map.entities, map.creatures, &checkMapHash) == -1 )
401 {
402 printlog("error: no level of set '%s' could be found.\n", levelset);
403 return -1;
404 }
405 if ( checkMapHash == 0 )
406 {
407 conductGameChallenges[CONDUCT_MODDED] = 1;
408 }
409
410 // store this map's seed
411 mapseed = seed;
412 prng_seed_bytes(&mapseed, sizeof(mapseed));
413
414 // generate a custom monster curve if file exists
415 monsterCurveCustomManager.readFromFile();
416
417 // determine whether shop level or not
418 if ( gameplayCustomManager.processedShopFloor(currentlevel, secretlevel, map.name, shoplevel) )
419 {
420 // function sets shop level for us.
421 }
422 else if ( prng_get_uint() % 2 && currentlevel > 1 && strncmp(map.name, "Underworld", 10) && strncmp(map.name, "Hell", 4) )
423 {
424 shoplevel = true;
425 }
426
427 // determine whether minotaur level or not
428 if ( (svFlags & SV_FLAG_MINOTAURS) && gameplayCustomManager.processedMinotaurSpawn(currentlevel, secretlevel, map.name) )
429 {
430 // function sets mino level for us.
431 }
432 else if ( std::get<LEVELPARAM_CHANCE_MINOTAUR>(mapParameters) != -1 )
433 {
434 if ( prng_get_uint() % 100 < std::get<LEVELPARAM_CHANCE_MINOTAUR>(mapParameters) && (svFlags & SV_FLAG_MINOTAURS) )
435 {
436 minotaurlevel = 1;
437 }
438 }
439 else if ( (currentlevel < 25 && (currentlevel % LENGTH_OF_LEVEL_REGION == 2 || currentlevel % LENGTH_OF_LEVEL_REGION == 3))
440 || (currentlevel > 25 && (currentlevel % LENGTH_OF_LEVEL_REGION == 2 || currentlevel % LENGTH_OF_LEVEL_REGION == 4)) )
441 {
442 if ( prng_get_uint() % 2 && (svFlags & SV_FLAG_MINOTAURS) )
443 {
444 minotaurlevel = 1;
445 }
446 }
447
448 // dark level
449 if ( gameplayCustomManager.processedDarkFloor(currentlevel, secretlevel, map.name) )
450 {
451 // function sets dark level for us.
452 if ( darkmap )
453 {
454 messagePlayer(clientnum, language[1108]);
455 }
456 }
457 else if ( !secretlevel )
458 {
459 if ( std::get<LEVELPARAM_CHANCE_DARKNESS>(mapParameters) != -1 )
460 {
461 if ( prng_get_uint() % 100 < std::get<LEVELPARAM_CHANCE_DARKNESS>(mapParameters) )
462 {
463 darkmap = true;
464 messagePlayer(clientnum, language[1108]);
465 }
466 else
467 {
468 darkmap = false;
469 }
470 }
471 else if ( currentlevel % LENGTH_OF_LEVEL_REGION >= 2 )
472 {
473 if ( prng_get_uint() % 4 == 0 )
474 {
475 darkmap = true;
476 messagePlayer(clientnum, language[1108]);
477 }
478 }
479 }
480
481 // secret stuff
482 if ( !secretlevel )
483 {
484 if ( std::get<LEVELPARAM_CHANCE_SECRET>(mapParameters) != -1 )
485 {
486 if ( prng_get_uint() % 100 < std::get<LEVELPARAM_CHANCE_SECRET>(mapParameters) )
487 {
488 secretlevelexit = 7;
489 }
490 else
491 {
492 secretlevelexit = 0;
493 }
494 }
495 else if ( (currentlevel == 3 && prng_get_uint() % 2) || currentlevel == 2 )
496 {
497 secretlevelexit = 1;
498 }
499 else if ( currentlevel == 7 || currentlevel == 8 )
500 {
501 secretlevelexit = 2;
502 }
503 else if ( currentlevel == 11 || currentlevel == 13 )
504 {
505 secretlevelexit = 3;
506 }
507 else if ( currentlevel == 16 || currentlevel == 18 )
508 {
509 secretlevelexit = 4;
510 }
511 else if ( currentlevel == 28 )
512 {
513 secretlevelexit = 5;
514 }
515 else if ( currentlevel == 33 )
516 {
517 secretlevelexit = 6;
518 }
519 }
520
521 mapList.first = nullptr;
522 mapList.last = nullptr;
523 doorList.first = nullptr;
524 doorList.last = nullptr;
525
526 // load shop room
527 if ( shoplevel )
528 {
529 sublevelname = (char*) malloc(sizeof(char) * 128);
530 for ( numlevels = 0; numlevels < 100; numlevels++ )
531 {
532 strcpy(sublevelname, "shop");
533 snprintf(sublevelnum, 3, "%02d", numlevels);
534 strcat(sublevelname, sublevelnum);
535
536 fullMapPath = physfsFormatMapName(sublevelname);
537
538 if ( fullMapPath.empty() )
539 {
540 break; // no more levels to load
541 }
542 }
543 if ( numlevels )
544 {
545 int shopleveltouse = prng_get_uint() % numlevels;
546 if ( !strncmp(map.name, "Citadel", 7) )
547 {
548 strcpy(sublevelname, "shopcitadel");
549 }
550 else
551 {
552 strcpy(sublevelname, "shop");
553 snprintf(sublevelnum, 3, "%02d", shopleveltouse);
554 strcat(sublevelname, sublevelnum);
555 }
556
557 fullMapPath = physfsFormatMapName(sublevelname);
558
559 shopmap.tiles = nullptr;
560 shopmap.entities = (list_t*) malloc(sizeof(list_t));
561 shopmap.entities->first = nullptr;
562 shopmap.entities->last = nullptr;
563 shopmap.creatures = new list_t;
564 shopmap.creatures->first = nullptr;
565 shopmap.creatures->last = nullptr;
566 if ( fullMapPath.empty() || loadMap(fullMapPath.c_str(), &shopmap, shopmap.entities, shopmap.creatures, &checkMapHash) == -1 )
567 {
568 list_FreeAll(shopmap.entities);
569 free(shopmap.entities);
570 list_FreeAll(shopmap.creatures);
571 delete shopmap.creatures;
572 if ( shopmap.tiles )
573 {
574 free(shopmap.tiles);
575 }
576 }
577 if ( checkMapHash == 0 )
578 {
579 conductGameChallenges[CONDUCT_MODDED] = 1;
580 }
581 }
582 else
583 {
584 shoplevel = false;
585 }
586 free( sublevelname );
587 }
588
589 sublevelname = (char*)malloc(sizeof(char) * 128);
590
591 // a maximum of 100 (0-99 inclusive) sublevels can be added to the pool
592 for ( numlevels = 0; numlevels < 100; ++numlevels )
593 {
594 strcpy(sublevelname, levelset);
595 snprintf(sublevelnum, 3, "%02d", numlevels);
596 strcat(sublevelname, sublevelnum);
597
598 fullMapPath = physfsFormatMapName(sublevelname);
599 if ( fullMapPath.empty() )
600 {
601 break; // no more levels to load
602 }
603
604 // allocate memory for the next sublevel and attempt to load it
605 tempMap = (map_t*) malloc(sizeof(map_t));
606 tempMap->tiles = nullptr;
607 tempMap->entities = (list_t*) malloc(sizeof(list_t));
608 tempMap->entities->first = nullptr;
609 tempMap->entities->last = nullptr;
610 tempMap->creatures = new list_t;
611 tempMap->creatures->first = nullptr;
612 tempMap->creatures->last = nullptr;
613 if ( fullMapPath.empty() || loadMap(fullMapPath.c_str(), tempMap, tempMap->entities, tempMap->creatures, &checkMapHash) == -1 )
614 {
615 mapDeconstructor((void*)tempMap);
616 continue; // failed to load level
617 }
618 if ( checkMapHash == 0 )
619 {
620 conductGameChallenges[CONDUCT_MODDED] = 1;
621 }
622
623 // level is successfully loaded, add it to the pool
624 newList = (list_t*) malloc(sizeof(list_t));
625 newList->first = nullptr;
626 newList->last = nullptr;
627 node = list_AddNodeLast(&mapList);
628 node->element = newList;
629 node->deconstructor = &listDeconstructor;
630
631 node = list_AddNodeLast(newList);
632 node->element = tempMap;
633 node->deconstructor = &mapDeconstructor;
634
635 // more nodes are created to record the exit points on the sublevel
636 for ( y = 0; y < tempMap->height; y++ )
637 {
638 for ( x = 0; x < tempMap->width; x++ )
639 {
640 if ( x == 0 || y == 0 || x == tempMap->width - 1 || y == tempMap->height - 1 )
641 {
642 if ( !tempMap->tiles[OBSTACLELAYER + y * MAPLAYERS + x * MAPLAYERS * tempMap->height] )
643 {
644 door = (door_t*) malloc(sizeof(door_t));
645 door->x = x;
646 door->y = y;
647 if ( x == tempMap->width - 1 )
648 {
649 door->dir = 0;
650 }
651 else if ( y == tempMap->height - 1 )
652 {
653 door->dir = 1;
654 }
655 else if ( x == 0 )
656 {
657 door->dir = 2;
658 }
659 else if ( y == 0 )
660 {
661 door->dir = 3;
662 }
663 node2 = list_AddNodeLast(newList);
664 node2->element = door;
665 node2->deconstructor = &defaultDeconstructor;
666 }
667 }
668 }
669 }
670 }
671
672 subRoomName = (char*)malloc(sizeof(char) * 128);
673 subRoomMapList.first = nullptr;
674 subRoomMapList.last = nullptr;
675 char letterString[2];
676 letterString[1] = '\0';
677 int subroomCount[100] = {0};
678
679 // a maximum of 100 (0-99 inclusive) sublevels can be added to the pool
680 for ( subRoomNumLevels = 0; subRoomNumLevels <= numlevels; subRoomNumLevels++ )
681 {
682 for ( char letter = 'a'; letter <= 'z'; letter++ )
683 {
684 // look for mapnames ending in a letter a to z
685 strcpy(subRoomName, levelset);
686 snprintf(sublevelnum, 3, "%02d", subRoomNumLevels);
687 letterString[0] = letter;
688 strcat(subRoomName, sublevelnum);
689 strcat(subRoomName, letterString);
690
691 fullMapPath = physfsFormatMapName(subRoomName);
692
693 if ( fullMapPath.empty() )
694 {
695 break; // no more levels to load
696 }
697
698 // check if there is another subroom to load
699 //if ( !dataPathExists(fullMapPath.c_str()) )
700 //{
701 // break; // no more levels to load
702 //}
703
704 printlog("[SUBMAP GENERATOR] Found map lv %s, count: %d", subRoomName, subroomCount[subRoomNumLevels]);
705 ++subroomCount[subRoomNumLevels];
706
707 // allocate memory for the next subroom and attempt to load it
708 subRoomMap = (map_t*)malloc(sizeof(map_t));
709 subRoomMap->tiles = nullptr;
710 subRoomMap->entities = (list_t*)malloc(sizeof(list_t));
711 subRoomMap->entities->first = nullptr;
712 subRoomMap->entities->last = nullptr;
713 subRoomMap->creatures = new list_t;
714 subRoomMap->creatures->first = nullptr;
715 subRoomMap->creatures->last = nullptr;
716 if ( fullMapPath.empty() || loadMap(fullMapPath.c_str(), subRoomMap, subRoomMap->entities, subRoomMap->creatures, &checkMapHash) == -1 )
717 {
718 mapDeconstructor((void*)subRoomMap);
719 continue; // failed to load level
720 }
721 if ( checkMapHash == 0 )
722 {
723 conductGameChallenges[CONDUCT_MODDED] = 1;
724 }
725
726 // level is successfully loaded, add it to the pool
727 subRoomList = (list_t*)malloc(sizeof(list_t));
728 subRoomList->first = nullptr;
729 subRoomList->last = nullptr;
730 node = list_AddNodeLast(&subRoomMapList);
731 node->element = subRoomList;
732 node->deconstructor = &listDeconstructor;
733
734 node = list_AddNodeLast(subRoomList);
735 node->element = subRoomMap;
736 node->deconstructor = &mapDeconstructor;
737
738 // more nodes are created to record the exit points on the sublevel
739 /*for ( y = 0; y < subRoomMap->height; y++ )
740 {
741 for ( x = 0; x < subRoomMap->width; x++ )
742 {
743 if ( x == 0 || y == 0 || x == subRoomMap->width - 1 || y == subRoomMap->height - 1 )
744 {
745 if ( !subRoomMap->tiles[OBSTACLELAYER + y * MAPLAYERS + x * MAPLAYERS * subRoomMap->height] )
746 {
747 door = (door_t*)malloc(sizeof(door_t));
748 door->x = x;
749 door->y = y;
750 if ( x == subRoomMap->width - 1 )
751 {
752 door->dir = 0;
753 }
754 else if ( y == subRoomMap->height - 1 )
755 {
756 door->dir = 1;
757 }
758 else if ( x == 0 )
759 {
760 door->dir = 2;
761 }
762 else if ( y == 0 )
763 {
764 door->dir = 3;
765 }
766 node2 = list_AddNodeLast(subRoomList);
767 node2->element = door;
768 node2->deconstructor = &defaultDeconstructor;
769 }
770 }
771 }
772 }*/
773 }
774 }
775
776 // generate dungeon level...
777 int roomcount = 0;
778 if ( numlevels > 1 )
779 {
780 possiblelocations = (bool*) malloc(sizeof(bool) * map.width * map.height);
781 trapexcludelocations = (bool*)malloc(sizeof(bool) * map.width * map.height);
782 monsterexcludelocations = (bool*)malloc(sizeof(bool) * map.width * map.height);
783 lootexcludelocations = (bool*)malloc(sizeof(bool) * map.width * map.height);
784 for ( y = 0; y < map.height; y++ )
785 {
786 for ( x = 0; x < map.width; x++ )
787 {
788 if ( x < 2 || y < 2 || x > map.width - 3 || y > map.height - 3 )
789 {
790 possiblelocations[x + y * map.width] = false;
791 }
792 else
793 {
794 possiblelocations[x + y * map.width] = true;
795 }
796 trapexcludelocations[x + y * map.width] = false;
797 if ( map.flags[MAP_FLAG_DISABLEMONSTERS] == true )
798 {
799 // the base map excludes all monsters
800 monsterexcludelocations[x + y * map.width] = true;
801 }
802 else
803 {
804 monsterexcludelocations[x + y * map.width] = false;
805 }
806 if ( map.flags[MAP_FLAG_DISABLELOOT] == true )
807 {
808 // the base map excludes all monsters
809 lootexcludelocations[x + y * map.width] = true;
810 }
811 else
812 {
813 lootexcludelocations[x + y * map.width] = false;
814 }
815 }
816 }
817 possiblelocations2 = (bool*) malloc(sizeof(bool) * map.width * map.height);
818 firstroomtile = (bool*) malloc(sizeof(bool) * map.width * map.height);
819 possiblerooms = (bool*) malloc(sizeof(bool) * numlevels);
820 for ( c = 0; c < numlevels; c++ )
821 {
822 possiblerooms[c] = true;
823 }
824 levellimit = (map.width * map.height);
825 for ( c = 0; c < levellimit; c++ )
826 {
827 // reset array of possible locations for the current room
828 for ( y = 0; y < map.height; y++ )
829 for ( x = 0; x < map.width; x++ )
830 {
831 possiblelocations2[x + y * map.width] = true;
832 }
833
834 // pick the room to be used
835 if ( c == 0 )
836 {
837 levelnum = 0; // the first room *must* be an entrance hall
838 levelnum2 = 0;
839 numlevels--;
840 possiblerooms[0] = false;
841 node = mapList.first;
842 node = ((list_t*)node->element)->first;
843 doorNode = node->next;
844 tempMap = (map_t*)node->element;
845 }
846 else if ( c == 1 && secretlevelexit )
847 {
848 secretlevelmap.tiles = nullptr;
849 secretlevelmap.entities = (list_t*) malloc(sizeof(list_t));
850 secretlevelmap.entities->first = nullptr;
851 secretlevelmap.entities->last = nullptr;
852 secretlevelmap.creatures = new list_t;
853 secretlevelmap.creatures->first = nullptr;
854 secretlevelmap.creatures->last = nullptr;
855 char secretmapname[128];
856 switch ( secretlevelexit )
857 {
858 case 1:
859 strcpy(secretmapname, "minesecret");
860 break;
861 case 2:
862 strcpy(secretmapname, "swampsecret");
863 break;
864 case 3:
865 strcpy(secretmapname, "labyrinthsecret");
866 break;
867 case 4:
868 strcpy(secretmapname, "ruinssecret");
869 break;
870 case 5:
871 strcpy(secretmapname, "cavessecret");
872 break;
873 case 6:
874 strcpy(secretmapname, "citadelsecret");
875 break;
876 case 7:
877 strcpy(secretmapname, levelset);
878 strcat(secretmapname, "secret");
879 break;
880 default:
881 break;
882 }
883 fullMapPath = physfsFormatMapName(secretmapname);
884 if ( fullMapPath.empty() || loadMap(fullMapPath.c_str(), &secretlevelmap, secretlevelmap.entities, secretlevelmap.creatures, &checkMapHash) == -1 )
885 {
886 list_FreeAll(secretlevelmap.entities);
887 free(secretlevelmap.entities);
888 list_FreeAll(secretlevelmap.creatures);
889 delete secretlevelmap.creatures;
890 if ( secretlevelmap.tiles )
891 {
892 free(secretlevelmap.tiles);
893 }
894 }
895 if ( checkMapHash == 0 )
896 {
897 conductGameChallenges[CONDUCT_MODDED] = 1;
898 }
899
900 levelnum = 0;
901 levelnum2 = -1;
902 tempMap = &secretlevelmap;
903 }
904 else if ( c == 2 && shoplevel )
905 {
906 // generate a shop
907 levelnum = 0;
908 levelnum2 = -1;
909 tempMap = &shopmap;
910 }
911 else
912 {
913 if ( !numlevels )
914 {
915 break;
916 }
917 levelnum = prng_get_uint() % (numlevels); // draw randomly from the pool
918
919 // traverse the map list to the picked level
920 node = mapList.first;
921 i = 0;
922 j = -1;
923 while (1)
924 {
925 if (possiblerooms[i])
926 {
927 ++j;
928 if (j == levelnum)
929 {
930 break;
931 }
932 }
933 node = node->next;
934 ++i;
935 }
936 levelnum2 = i;
937 node = ((list_t*)node->element)->first;
938 doorNode = node->next;
939 tempMap = (map_t*)node->element;
940 }
941
942 // find locations where the selected room can be added to the level
943 numpossiblelocations = map.width * map.height;
944
945 bool hellGenerationFix = !strncmp(map.name, "Hell", 4);
946
947 for ( y0 = 0; y0 < map.height; y0++ )
948 {
949 for ( x0 = 0; x0 < map.width; x0++ )
950 {
951 for ( y1 = y0; y1 < std::min(y0 + tempMap->height, map.height); y1++ )
952 {
953 // don't generate start room in hell along the rightmost wall, causes pathing to fail. Check 2 tiles to the right extra
954 // to try fit start room.
955 for ( x1 = x0; x1 < std::min(x0 + tempMap->width + ((hellGenerationFix && c == 0) ? 2 : 0), map.width); x1++ )
956 {
957 if ( possiblelocations[x1 + y1 * map.width] == false && possiblelocations2[x0 + y0 * map.width] == true )
958 {
959 possiblelocations2[x0 + y0 * map.width] = false;
960 numpossiblelocations--;
961 }
962 }
963 }
964 }
965 }
966
967 // in case no locations are available, remove this room from the selection
968 if ( numpossiblelocations <= 0 )
969 {
970 if ( levelnum2 >= 0 && levelnum2 < numlevels )
971 {
972 possiblerooms[levelnum2] = false;
973 }
974 --numlevels;
975 if ( levelnum2 == 0 )
976 {
977 // if we couldn't even fit the entrance hall into the dungeon, we have a problem
978 free(possiblerooms);
979 free(possiblelocations);
980 free(possiblelocations2);
981 free(trapexcludelocations);
982 free(monsterexcludelocations);
983 free(lootexcludelocations);
984 free(firstroomtile);
985 free(sublevelname);
986 free(subRoomName);
987 list_FreeAll(&subRoomMapList);
988 list_FreeAll(&mapList);
989 if ( shoplevel && c == 2 )
990 {
991 list_FreeAll(shopmap.entities);
992 free(shopmap.entities);
993 list_FreeAll(shopmap.creatures);
994 delete shopmap.creatures;
995 if ( shopmap.tiles )
996 {
997 free(shopmap.tiles);
998 }
999 }
1000 if ( secretlevelexit && c == 1 )
1001 {
1002 list_FreeAll(secretlevelmap.entities);
1003 free(secretlevelmap.entities);
1004 list_FreeAll(secretlevelmap.creatures);
1005 delete secretlevelmap.creatures;
1006 if ( secretlevelmap.tiles )
1007 {
1008 free(secretlevelmap.tiles);
1009 }
1010 }
1011 printlog("error: entrance room must fit into dungeon!\n");
1012 return -1;
1013 }
1014 else if ( numlevels < 1 )
1015 {
1016 // if we've run out of rooms to build the dungeon with, skip to the next step of generation
1017 break;
1018 }
1019 continue;
1020 }
1021
1022 // otherwise, choose a location from those available (to be stored in x/y)
1023 if ( MFLAG_GENADJACENTROOMS )
1024 {
1025 pickedlocation = 0;
1026 i = -1;
1027 x = 0;
1028 y = 0;
1029
1030 if ( !strncmp(map.name, "Citadel", 7) )
1031 {
1032 if ( c == 0 )
1033 {
1034 // 7x7, pick random location across all map.
1035 x = 2 + (prng_get_uint() % 7) * 7;
1036 y = 2 + (prng_get_uint() % 7) * 7;
1037 }
1038 else if ( secretlevelexit && c == 1 )
1039 {
1040 // 14x14, pick random location minus 1 from both edges.
1041 x = 2 + (prng_get_uint() % 6) * 7;
1042 y = 2 + (prng_get_uint() % 6) * 7;
1043 }
1044 else if ( c == 2 && shoplevel )
1045 {
1046 // 7x7, pick random location across all map.
1047 x = 2 + (prng_get_uint() % 7) * 7;
1048 y = 2 + (prng_get_uint() % 7) * 7;
1049 }
1050 }
1051 else
1052 {
1053 if ( c == 0 )
1054 {
1055 // pick random location across all map.
1056 x = 2 + (prng_get_uint() % tempMap->width) * tempMap->width;
1057 y = 2 + (prng_get_uint() % tempMap->height) * tempMap->height;
1058 }
1059 else if ( secretlevelexit && c == 1 )
1060 {
1061 x = 2 + (prng_get_uint() % tempMap->width) * tempMap->width;
1062 y = 2 + (prng_get_uint() % tempMap->height) * tempMap->height;
1063 while ( x + tempMap->width >= map.width )
1064 {
1065 x = 2 + (prng_get_uint() % tempMap->width) * tempMap->width;
1066 }
1067 while ( y + tempMap->height >= map.height )
1068 {
1069 y = 2 + (prng_get_uint() % tempMap->height) * tempMap->height;
1070 }
1071 }
1072 else if ( c == 2 && shoplevel )
1073 {
1074 // pick random location across all map.
1075 x = 2 + (prng_get_uint() % tempMap->width) * tempMap->width;
1076 y = 2 + (prng_get_uint() % tempMap->height) * tempMap->height;
1077 }
1078 }
1079
1080 while ( 1 )
1081 {
1082 if ( possiblelocations2[x + y * map.width] == true )
1083 {
1084 ++i;
1085 if ( i == pickedlocation )
1086 {
1087 break;
1088 }
1089 }
1090 ++x;
1091 if ( x >= map.width )
1092 {
1093 x = 0;
1094 ++y;
1095 if ( y >= map.height )
1096 {
1097 y = 0;
1098 ++pickedlocation;
1099 }
1100 }
1101 }
1102 }
1103 else
1104 {
1105 pickedlocation = prng_get_uint() % numpossiblelocations;
1106 i = -1;
1107 x = 0;
1108 y = 0;
1109 while ( 1 )
1110 {
1111 if ( possiblelocations2[x + y * map.width] == true )
1112 {
1113 ++i;
1114 if ( i == pickedlocation )
1115 {
1116 break;
1117 }
1118 }
1119 ++x;
1120 if ( x >= map.width )
1121 {
1122 x = 0;
1123 ++y;
1124 if ( y >= map.height )
1125 {
1126 y = 0;
1127 }
1128 }
1129 }
1130 }
1131
1132 // now copy all the geometry from the sublevel to the chosen location
1133 if ( c == 0 )
1134 {
1135 for ( z = 0; z < map.width * map.height; ++z )
1136 {
1137 firstroomtile[z] = false;
1138 }
1139 }
1140 x1 = x + tempMap->width;
1141 y1 = y + tempMap->height;
1142
1143
1144 //**********pick subroom if available
1145 int pickSubRoom = 0;
1146 int k = 0;
1147 int subRoom_tilex = 0;
1148 int subRoom_tiley = 0;
1149 int subRoom_tileStartx = -1;
1150 int subRoom_tileStarty = -1;
1151 int foundSubRoom = 0;
1152 if ( ((levelnum2 - levelnum) > 1) && (c > 0) && (subroomCount[levelnum2] > 0) )
1153 {
1154 // levelnum is the start of map search, levelnum2 is jumps required to get to a suitable map.
1155 // normal operation is levelnum2 - levelnum == 1. if a levelnum map is unavailable,
1156 // then levelnum2 will advance search by 1 (higher than normal).
1157 // levelnum2 will keep incrementing until a suitable map is found.
1158 printlog("[SUBMAP GENERATOR] Skipped map when searching for levelnum %d, setting to %d", levelnum, levelnum2 - 1);
1159 levelnum = levelnum2 - 1;
1160 }
1161 //printlog("(%d | %d), possible: (%d, %d) x: %d y: %d", levelnum, levelnum2, possiblerooms[1], possiblerooms[2], x, y);
1162 if ( subroomCount[levelnum + 1] > 0 )
1163 {
1164 int jumps = 0;
1165 pickSubRoom = prng_get_uint() % subroomCount[levelnum + 1];
1166 // traverse the map list to the picked level
1167 subRoomNode = subRoomMapList.first;
1168 for ( int cycleRooms = 0; (cycleRooms < levelnum + 1) && (subRoomNode != nullptr); ++cycleRooms )
1169 {
1170 for ( int cycleRoomSubMaps = subroomCount[cycleRooms]; cycleRoomSubMaps > 0; --cycleRoomSubMaps )
1171 {
1172 // advance the subroom map list by the previous entries.
1173 // e.g 2 subrooms, 3 maps each should advance pointer 3 maps when loading second room.
1174 subRoomNode = subRoomNode->next;
1175 jumps++; // just to keep track of how many jumps we made.
1176 }
1177 }
1178 k = 0;
1179
1180 while ( 1 )
1181 {
1182 if ( k == pickSubRoom )
1183 {
1184 break;
1185 }
1186 subRoomNode = subRoomNode->next;
1187 k++;
1188 }
1189 //messagePlayer(0, "%d + %d jumps!", jumps, k + 1);
1190 subRoomNode = ((list_t*)subRoomNode->element)->first;
1191 subRoomMap = (map_t*)subRoomNode->element;
1192 subRoomDoorNode = subRoomNode->next;
1193 }
1194
1195 for ( z = 0; z < MAPLAYERS; z++ )
1196 {
1197 for ( y0 = y; y0 < y1; y0++ )
1198 {
1199 for ( x0 = x; x0 < x1; x0++ )
1200 {
1201 if ( subroomCount[levelnum + 1] > 0 && tempMap->tiles[z + (y0 - y) * MAPLAYERS + (x0 - x) * MAPLAYERS * tempMap->height] == 201 )
1202 {
1203 if ( !foundSubRoom )
1204 {
1205 subRoom_tileStartx = x0;
1206 subRoom_tileStarty = y0;
1207 foundSubRoom = 1;
1208 printlog("Picked level: %d from %d possible rooms in submap %d at x:%d y:%d", pickSubRoom + 1, subroomCount[levelnum + 1], levelnum + 1, x, y);
1209 }
1210
1211 map.tiles[z + y0 * MAPLAYERS + x0 * MAPLAYERS * map.height] = subRoomMap->tiles[z + (subRoom_tiley)* MAPLAYERS + (subRoom_tilex)* MAPLAYERS * subRoomMap->height];
1212
1213 ++subRoom_tilex;
1214 if ( subRoom_tilex >= subRoomMap->width )
1215 {
1216 subRoom_tilex = 0;
1217 ++subRoom_tiley;
1218 if ( subRoom_tiley >= subRoomMap->height )
1219 {
1220 subRoom_tiley = 0;
1221 }
1222 }
1223 }
1224 else
1225 {
1226 map.tiles[z + y0 * MAPLAYERS + x0 * MAPLAYERS * map.height] = tempMap->tiles[z + (y0 - y) * MAPLAYERS + (x0 - x) * MAPLAYERS * tempMap->height];
1227 }
1228
1229 if ( z == 0 )
1230 {
1231 possiblelocations[x0 + y0 * map.width] = false;
1232 if ( tempMap->flags[MAP_FLAG_DISABLETRAPS] == 1 )
1233 {
1234 trapexcludelocations[x0 + y0 * map.width] = true;
1235 //map.tiles[z + y0 * MAPLAYERS + x0 * MAPLAYERS * map.height] = 83;
1236 }
1237 if ( tempMap->flags[MAP_FLAG_DISABLEMONSTERS] == 1 )
1238 {
1239 monsterexcludelocations[x0 + y0 * map.width] = true;
1240 }
1241 if ( tempMap->flags[MAP_FLAG_DISABLELOOT] == 1 )
1242 {
1243 lootexcludelocations[x0 + y0 * map.width] = true;
1244 }
1245 if ( c == 0 )
1246 {
1247 firstroomtile[y0 + x0 * map.height] = true;
1248 }
1249 else if ( c == 2 && shoplevel )
1250 {
1251 firstroomtile[y0 + x0 * map.height] = true;
1252 if ( x0 - x > 0 && y0 - y > 0 && x0 - x < tempMap->width - 1 && y0 - y < tempMap->height - 1 )
1253 {
1254 shoparea[y0 + x0 * map.height] = true;
1255 }
1256 }
1257 }
1258
1259 // remove any existing entities in this region too
1260 for ( node = map.entities->first; node != nullptr; node = nextnode )
1261 {
1262 nextnode = node->next;
1263 Entity* entity = (Entity*)node->element;
1264 if ( (int)entity->x == x0 << 4 && (int)entity->y == y0 << 4 )
1265 {
1266 list_RemoveNode(entity->mynode);
1267 }
1268 }
1269 }
1270 }
1271 }
1272
1273 // copy the entities as well from the tempMap.
1274 for ( node = tempMap->entities->first; node != nullptr; node = node->next )
1275 {
1276 entity = (Entity*)node->element;
1277 childEntity = newEntity(entity->sprite, 1, map.entities, nullptr);
1278
1279 // entity will return nullptr on getStats called in setSpriteAttributes as behaviour &actmonster is not set.
1280 // check if the monster sprite is correct and set the behaviour manually for getStats.
1281 if ( checkSpriteType(entity->sprite) == 1 && multiplayer != CLIENT )
1282 {
1283 entity->behavior = &actMonster;
1284 }
1285
1286 setSpriteAttributes(childEntity, entity, entity);
1287 childEntity->x = entity->x + x * 16;
1288 childEntity->y = entity->y + y * 16;
1289 //printlog("1 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
1290
1291 if ( entity->behavior == actMonster || entity->behavior == actPlayer )
1292 {
1293 entity->addToCreatureList(map.creatures);
1294 }
1295 }
1296
1297 if ( foundSubRoom )
1298 {
1299 // copy the entities from subroom
1300 for ( subRoomNode = subRoomMap->entities->first; subRoomNode != nullptr; subRoomNode = subRoomNode->next )
1301 {
1302 entity = (Entity*)subRoomNode->element;
1303 childEntity = newEntity(entity->sprite, 1, map.entities, nullptr);
1304
1305 // entity will return nullptr on getStats called in setSpriteAttributes as behaviour &actmonster is not set.
1306 // check if the monster sprite is correct and set the behaviour manually for getStats.
1307 if ( checkSpriteType(entity->sprite) == 1 && multiplayer != CLIENT )
1308 {
1309 entity->behavior = &actMonster;
1310 }
1311
1312 setSpriteAttributes(childEntity, entity, entity);
1313 childEntity->x = entity->x + subRoom_tileStartx * 16;
1314 childEntity->y = entity->y + subRoom_tileStarty * 16;
1315
1316 if ( entity->behavior == actMonster || entity->behavior == actPlayer )
1317 {
1318 entity->addToCreatureList(map.creatures);
1319 }
1320
1321 //messagePlayer(0, "1 Generated entity. Sprite: %d X: %.2f Y: %.2f", childEntity->sprite, childEntity->x / 16, childEntity->y / 16);
1322 }
1323 }
1324
1325 // finally, copy the doors into a single doors list
1326 while ( doorNode != nullptr )
1327 {
1328 door = (door_t*)doorNode->element;
1329 newDoor = (door_t*) malloc(sizeof(door_t));
1330 newDoor->x = door->x + x;
1331 newDoor->y = door->y + y;
1332 newDoor->dir = door->dir;
1333 node = list_AddNodeLast(&doorList);
1334 node->element = newDoor;
1335 node->deconstructor = &defaultDeconstructor;
1336 doorNode = doorNode->next;
1337 }
1338
1339 if ( foundSubRoom )
1340 {
1341 // copy subroom doors
1342 while ( subRoomDoorNode != nullptr )
1343 {
1344 door = (door_t*)subRoomDoorNode->element;
1345 newDoor = (door_t*)malloc(sizeof(door_t));
1346 newDoor->x = door->x + subRoom_tileStartx;
1347 newDoor->y = door->y + subRoom_tileStarty;
1348 newDoor->dir = door->dir;
1349 node = list_AddNodeLast(&doorList);
1350 node->element = newDoor;
1351 node->deconstructor = &defaultDeconstructor;
1352 subRoomDoorNode = subRoomDoorNode->next;
1353 }
1354 }
1355
1356 // free shop map if used
1357 if ( shoplevel && c == 2 )
1358 {
1359 list_FreeAll(shopmap.entities);
1360 free(shopmap.entities);
1361 list_FreeAll(shopmap.creatures);
1362 delete shopmap.creatures;
1363 if ( shopmap.tiles )
1364 {
1365 free(shopmap.tiles);
1366 }
1367 }
1368 if ( secretlevelexit && c == 1 )
1369 {
1370 list_FreeAll(secretlevelmap.entities);
1371 free(secretlevelmap.entities);
1372 list_FreeAll(secretlevelmap.creatures);
1373 delete secretlevelmap.creatures;
1374 if ( secretlevelmap.tiles )
1375 {
1376 free(secretlevelmap.tiles);
1377 }
1378 }
1379 ++roomcount;
1380 }
1381 free(possiblerooms);
1382 free(possiblelocations2);
1383 }
1384 else
1385 {
1386 free(subRoomName);
1387 free(sublevelname);
1388 list_FreeAll(&subRoomMapList);
1389 list_FreeAll(&mapList);
1390 list_FreeAll(&doorList);
1391 printlog("error: not enough levels to begin generating dungeon.\n");
1392 return -1;
1393 }
1394
1395 // post-processing:
1396
1397 // doors
1398 for ( node = doorList.first; node != nullptr; node = node->next )
1399 {
1400 door = (door_t*)node->element;
1401 for (node2 = map.entities->first; node2 != nullptr; node2 = node2->next)
1402 {
1403 entity = (Entity*)node2->element;
1404 if ( entity->x / 16 == door->x && entity->y / 16 == door->y && (entity->sprite == 2 || entity->sprite == 3) )
1405 {
1406 switch ( door->dir )
1407 {
1408 case 0: // east
1409 map.tiles[OBSTACLELAYER + door->y * MAPLAYERS + (door->x + 1)*MAPLAYERS * map.height] = 0;
1410 for ( node3 = map.entities->first; node3 != nullptr; node3 = nextnode )
1411 {
1412 entity = (Entity*)node3->element;
1413 nextnode = node3->next;
1414 if ( entity->sprite == 2 || entity->sprite == 3 )
1415 {
1416 if ( (int)(entity->x / 16) == door->x + 2 && (int)(entity->y / 16) == door->y )
1417 {
1418 list_RemoveNode(entity->mynode);
1419 break;
1420 }
1421 else if ( (int)(entity->x / 16) == door->x + 1 && (int)(entity->y / 16) == door->y )
1422 {
1423 list_RemoveNode(entity->mynode);
1424 break;
1425 }
1426 else if ( (int)(entity->x / 16) == door->x + 1 && (int)(entity->y / 16) == door->y + 1 )
1427 {
1428 list_RemoveNode(entity->mynode);
1429 break;
1430 }
1431 else if ( (int)(entity->x / 16) == door->x + 1 && (int)(entity->y / 16) == door->y - 1 )
1432 {
1433 list_RemoveNode(entity->mynode);
1434 break;
1435 }
1436 }
1437 }
1438 break;
1439 case 1: // south
1440 map.tiles[OBSTACLELAYER + (door->y + 1)*MAPLAYERS + door->x * MAPLAYERS * map.height] = 0;
1441 for ( node3 = map.entities->first; node3 != nullptr; node3 = nextnode )
1442 {
1443 entity = (Entity*)node3->element;
1444 nextnode = node3->next;
1445 if ( entity->sprite == 2 || entity->sprite == 3 )
1446 {
1447 if ( (int)(entity->x / 16) == door->x && (int)(entity->y / 16) == door->y + 2 )
1448 {
1449 list_RemoveNode(entity->mynode);
1450 break;
1451 }
1452 else if ( (int)(entity->x / 16) == door->x && (int)(entity->y / 16) == door->y + 1 )
1453 {
1454 list_RemoveNode(entity->mynode);
1455 break;
1456 }
1457 else if ( (int)(entity->x / 16) == door->x + 1 && (int)(entity->y / 16) == door->y + 1 )
1458 {
1459 list_RemoveNode(entity->mynode);
1460 break;
1461 }
1462 else if ( (int)(entity->x / 16) == door->x - 1 && (int)(entity->y / 16) == door->y + 1 )
1463 {
1464 list_RemoveNode(entity->mynode);
1465 break;
1466 }
1467 }
1468 }
1469 break;
1470 case 2: // west
1471 map.tiles[OBSTACLELAYER + door->y * MAPLAYERS + (door->x - 1)*MAPLAYERS * map.height] = 0;
1472 for ( node3 = map.entities->first; node3 != nullptr; node3 = nextnode )
1473 {
1474 entity = (Entity*)node3->element;
1475 nextnode = node3->next;
1476 if ( entity->sprite == 2 || entity->sprite == 3 )
1477 {
1478 if ( (int)(entity->x / 16) == door->x - 2 && (int)(entity->y / 16) == door->y )
1479 {
1480 list_RemoveNode(entity->mynode);
1481 break;
1482 }
1483 else if ( (int)(entity->x / 16) == door->x - 1 && (int)(entity->y / 16) == door->y )
1484 {
1485 list_RemoveNode(entity->mynode);
1486 break;
1487 }
1488 else if ( (int)(entity->x / 16) == door->x - 1 && (int)(entity->y / 16) == door->y + 1 )
1489 {
1490 list_RemoveNode(entity->mynode);
1491 break;
1492 }
1493 else if ( (int)(entity->x / 16) == door->x - 1 && (int)(entity->y / 16) == door->y - 1 )
1494 {
1495 list_RemoveNode(entity->mynode);
1496 break;
1497 }
1498 }
1499 }
1500 break;
1501 case 3: // north
1502 map.tiles[OBSTACLELAYER + (door->y - 1)*MAPLAYERS + door->x * MAPLAYERS * map.height] = 0;
1503 for ( node3 = map.entities->first; node3 != nullptr; node3 = nextnode )
1504 {
1505 entity = (Entity*)node3->element;
1506 nextnode = node3->next;
1507 if ( entity->sprite == 2 || entity->sprite == 3 )
1508 {
1509 if ( (int)(entity->x / 16) == door->x && (int)(entity->y / 16) == door->y - 2 )
1510 {
1511 list_RemoveNode(entity->mynode);
1512 break;
1513 }
1514 else if ( (int)(entity->x / 16) == door->x && (int)(entity->y / 16) == door->y - 1 )
1515 {
1516 list_RemoveNode(entity->mynode);
1517 break;
1518 }
1519 else if ( (int)(entity->x / 16) == door->x + 1 && (int)(entity->y / 16) == door->y - 1 )
1520 {
1521 list_RemoveNode(entity->mynode);
1522 break;
1523 }
1524 else if ( (int)(entity->x / 16) == door->x - 1 && (int)(entity->y / 16) == door->y - 1 )
1525 {
1526 list_RemoveNode(entity->mynode);
1527 break;
1528 }
1529 }
1530 }
1531 break;
1532 }
1533 }
1534 }
1535 }
1536 bool foundsubmaptile = false;
1537 // if for whatever reason some submap 201 tiles didn't get filled in, let's get rid of those.
1538 for ( z = 0; z < MAPLAYERS; ++z )
1539 {
1540 for ( y = 1; y < map.height; ++y )
1541 {
1542 for ( x = 1; x < map.height; ++x )
1543 {
1544 if ( map.tiles[z + y * MAPLAYERS + x * MAPLAYERS * map.height] == 201 )
1545 {
1546 map.tiles[z + y * MAPLAYERS + x * MAPLAYERS * map.height] = 0;
1547 foundsubmaptile = true;
1548 }
1549 }
1550 }
1551 }
1552 if ( foundsubmaptile )
1553 {
1554 printlog("[SUBMAP GENERATOR] Found some junk tiles!");
1555 }
1556
1557 for ( node = map.entities->first; node != nullptr; node = node->next )
1558 {
1559 // fix gate air-gap borders on citadel map next to perimeter gates.
1560 if ( !strncmp(map.name, "Citadel", 7) )
1561 {
1562 Entity* gateEntity = (Entity*)node->element;
1563 if ( gateEntity->sprite == 19 || gateEntity->sprite == 20 ) // N/S E/W gates take these sprite numbers in the editor.
1564 {
1565 int gatex = static_cast<int>(gateEntity->x) / 16;
1566 int gatey = static_cast<int>(gateEntity->y) / 16;
1567 for ( z = OBSTACLELAYER; z < MAPLAYERS; ++z )
1568 {
1569 if ( gateEntity->x / 16 == 1 ) // along leftmost edge
1570 {
1571 if ( !map.tiles[z + gatey * MAPLAYERS + (gatex + 1) * MAPLAYERS * map.height] )
1572 {
1573 map.tiles[z + gatey * MAPLAYERS + (gatex + 1) * MAPLAYERS * map.height] = 230;
1574 //messagePlayer(0, "replaced at: %d, %d", gatex, gatey);
1575 }
1576 }
1577 else if ( gateEntity->x / 16 == 51 ) // along rightmost edge
1578 {
1579 if ( !map.tiles[z + gatey * MAPLAYERS + (gatex - 1) * MAPLAYERS * map.height] )
1580 {
1581 map.tiles[z + gatey * MAPLAYERS + (gatex - 1) * MAPLAYERS * map.height] = 230;
1582 //messagePlayer(0, "replaced at: %d, %d", gatex, gatey);
1583 }
1584 }
1585 else if ( gateEntity->y / 16 == 1 ) // along top edge
1586 {
1587 if ( !map.tiles[z + (gatey + 1) * MAPLAYERS + gatex * MAPLAYERS * map.height] )
1588 {
1589 map.tiles[z + (gatey + 1) * MAPLAYERS + gatex * MAPLAYERS * map.height] = 230;
1590 //messagePlayer(0, "replaced at: %d, %d", gatex, gatey);
1591 }
1592 }
1593 else if ( gateEntity->y / 16 == 51 ) // along bottom edge
1594 {
1595 if ( !map.tiles[z + (gatey - 1) * MAPLAYERS + gatex * MAPLAYERS * map.height] )
1596 {
1597 map.tiles[z + (gatey - 1) * MAPLAYERS + gatex * MAPLAYERS * map.height] = 230;
1598 //messagePlayer(0, "replaced at: %d, %d", gatex, gatey);
1599 }
1600 }
1601 }
1602 }
1603 }
1604 }
1605
1606 bool customTrapsForMapInUse = false;
1607 struct CustomTraps
1608 {
1609 bool boulders = false;
1610 bool arrows = false;
1611 bool spikes = false;
1612 bool verticalSpelltraps = false;
1613 } customTraps;
1614
1615 if ( gameplayCustomManager.inUse() && gameplayCustomManager.mapGenerationExistsForMapName(map.name) )
1616 {
1617 auto m = gameplayCustomManager.getMapGenerationForMapName(map.name);
1618 if ( m && m->usingTrapTypes )
1619 {
1620 customTrapsForMapInUse = true;
1621 for ( auto& traps : m->trapTypes )
1622 {
1623 if ( traps.compare("boulders") == 0 )
1624 {
1625 customTraps.boulders = true;
1626 }
1627 else if ( traps.compare("arrows") == 0 )
1628 {
1629 customTraps.arrows = true;
1630 }
1631 else if ( traps.compare("spikes") == 0 )
1632 {
1633 customTraps.spikes = true;
1634 }
1635 else if ( traps.compare("spelltrap_vertical") == 0 )
1636 {
1637 customTraps.verticalSpelltraps = true;
1638 }
1639 }
1640 }
1641 }
1642
1643 // boulder and arrow traps
1644 if ( (svFlags & SV_FLAG_TRAPS) && map.flags[MAP_FLAG_DISABLETRAPS] == 0
1645 && (!customTrapsForMapInUse || (customTrapsForMapInUse && (customTraps.boulders || customTraps.arrows)) )
1646 )
1647 {
1648 numpossiblelocations = 0;
1649 for ( c = 0; c < map.width * map.height; ++c )
1650 {
1651 possiblelocations[c] = false;
1652 }
1653 for ( y = 1; y < map.height - 1; ++y )
1654 {
1655 for ( x = 1; x < map.width - 1; ++x )
1656 {
1657 int sides = 0;
1658 if ( firstroomtile[y + x * map.height] )
1659 {
1660 continue;
1661 }
1662 if ( !map.tiles[OBSTACLELAYER + y * MAPLAYERS + (x + 1)*MAPLAYERS * map.height] )
1663 {
1664 sides++;
1665 }
1666 if ( !map.tiles[OBSTACLELAYER + (y + 1)*MAPLAYERS + x * MAPLAYERS * map.height] )
1667 {
1668 sides++;
1669 }
1670 if ( !map.tiles[OBSTACLELAYER + y * MAPLAYERS + (x - 1)*MAPLAYERS * map.height] )
1671 {
1672 sides++;
1673 }
1674 if ( !map.tiles[OBSTACLELAYER + (y - 1)*MAPLAYERS + x * MAPLAYERS * map.height] )
1675 {
1676 sides++;
1677 }
1678 if ( sides == 1 && (trapexcludelocations[x + y * map.width] == false) )
1679 {
1680 possiblelocations[y + x * map.height] = true;
1681 numpossiblelocations++;
1682 }
1683 }
1684 }
1685
1686 // don't spawn traps in doors
1687 node_t* doorNode;
1688 for ( doorNode = doorList.first; doorNode != nullptr; doorNode = doorNode->next )
1689 {
1690 door_t* door = (door_t*)doorNode->element;
1691 int x = std::min<unsigned int>(std::max(0, door->x), map.width); //TODO: Why are const int and unsigned int being compared?
1692 int y = std::min<unsigned int>(std::max(0, door->y), map.height); //TODO: Why are const int and unsigned int being compared?
1693 if ( possiblelocations[y + x * map.height] == true )
1694 {
1695 possiblelocations[y + x * map.height] = false;
1696 --numpossiblelocations;
1697 }
1698 }
1699
1700 // do a second pass to look for internal doorways
1701 for ( node = map.entities->first; node != nullptr; node = node->next )
1702 {
1703 entity = (Entity*)node->element;
1704 int x = entity->x / 16;
1705 int y = entity->y / 16;
1706 if ( (entity->sprite == 2 || entity->sprite == 3)
1707 && (x >= 0 && x < map.width)
1708 && (y >= 0 && y < map.height) )
1709 {
1710 if ( possiblelocations[y + x * map.height] )
1711 {
1712 possiblelocations[y + x * map.height] = false;
1713 --numpossiblelocations;
1714 }
1715 }
1716 }
1717
1718 int whatever = prng_get_uint() % 5;
1719 if ( strncmp(map.name, "Hell", 4) )
1720 j = std::min(
1721 std::min(
1722 std::max(1, currentlevel),
1723 5
1724 )
1725 + whatever, numpossiblelocations
1726 )
1727 / ((strcmp(map.name, "The Mines") == 0) + 1);
1728 else
1729 {
1730 j = std::min(15, numpossiblelocations);
1731 }
1732 //printlog("j: %d\n",j);
1733 //printlog("numpossiblelocations: %d\n",numpossiblelocations);
1734 for ( c = 0; c < j; ++c )
1735 {
1736 // choose a random location from those available
1737 pickedlocation = prng_get_uint() % numpossiblelocations;
1738 i = -1;
1739 //printlog("pickedlocation: %d\n",pickedlocation);
1740 //printlog("numpossiblelocations: %d\n",numpossiblelocations);
1741 x = 0;
1742 y = 0;
1743 while ( 1 )
1744 {
1745 if ( possiblelocations[y + x * map.height] == true )
1746 {
1747 i++;
1748 if ( i == pickedlocation )
1749 {
1750 break;
1751 }
1752 }
1753 x++;
1754 if ( x >= map.width )
1755 {
1756 x = 0;
1757 y++;
1758 if ( y >= map.height )
1759 {
1760 y = 0;
1761 }
1762 }
1763 }
1764 int side = 0;
1765 if ( !map.tiles[OBSTACLELAYER + y * MAPLAYERS + (x + 1)*MAPLAYERS * map.height] )
1766 {
1767 side = 0;
1768 }
1769 else if ( !map.tiles[OBSTACLELAYER + (y + 1)*MAPLAYERS + x * MAPLAYERS * map.height] )
1770 {
1771 side = 1;
1772 }
1773 else if ( !map.tiles[OBSTACLELAYER + y * MAPLAYERS + (x - 1)*MAPLAYERS * map.height] )
1774 {
1775 side = 2;
1776 }
1777 else if ( !map.tiles[OBSTACLELAYER + (y - 1)*MAPLAYERS + x * MAPLAYERS * map.height] )
1778 {
1779 side = 3;
1780 }
1781 bool arrowtrap = false;
1782 bool noceiling = false;
1783 bool arrowtrapspawn = false;
1784 if ( !strncmp(map.name, "Hell", 4) )
1785 {
1786 if ( side == 0 && !map.tiles[(MAPLAYERS - 1) + y * MAPLAYERS + (x + 1)*MAPLAYERS * map.height] )
1787 {
1788 noceiling = true;
1789 }
1790 if ( side == 1 && !map.tiles[(MAPLAYERS - 1) + (y + 1)*MAPLAYERS + x * MAPLAYERS * map.height] )
1791 {
1792 noceiling = true;
1793 }
1794 if ( side == 2 && !map.tiles[(MAPLAYERS - 1) + y * MAPLAYERS + (x - 1)*MAPLAYERS * map.height] )
1795 {
1796 noceiling = true;
1797 }
1798 if ( side == 3 && !map.tiles[(MAPLAYERS - 1) + (y - 1)*MAPLAYERS + x * MAPLAYERS * map.height] )
1799 {
1800 noceiling = true;
1801 }
1802 if ( noceiling )
1803 {
1804 arrowtrapspawn = true;
1805 }
1806 }
1807 else
1808 {
1809 if ( prng_get_uint() % 2 && (currentlevel > 5 && currentlevel <= 25) )
1810 {
1811 arrowtrapspawn = true;
1812 }
1813 }
1814
1815 if ( customTrapsForMapInUse )
1816 {
1817 arrowtrapspawn = customTraps.arrows;
1818 if ( customTraps.boulders && prng_get_uint() % 2 )
1819 {
1820 arrowtrapspawn = false;
1821 }
1822 }
1823
1824 if ( arrowtrapspawn || noceiling )
1825 {
1826 arrowtrap = true;
1827 entity = newEntity(32, 1, map.entities, nullptr); // arrow trap
1828 entity->behavior = &actArrowTrap;
1829 map.tiles[OBSTACLELAYER + y * MAPLAYERS + x * MAPLAYERS * map.height] = 53; // trap wall
1830 }
1831 else
1832 {
1833 //messagePlayer(0, "Included at x: %d, y: %d", x, y);
1834 entity = newEntity(38, 1, map.entities, nullptr); // boulder trap
1835 entity->behavior = &actBoulderTrap;
1836 }
1837 entity->x = x * 16;
1838 entity->y = y * 16;
1839 //printlog("2 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",entity->sprite,entity->getUID(),entity->x,entity->y);
1840 entity = newEntity(18, 1, map.entities, nullptr); // electricity node
1841 entity->x = x * 16 - (side == 3) * 16 + (side == 1) * 16;
1842 entity->y = y * 16 - (side == 0) * 16 + (side == 2) * 16;
1843 //printlog("4 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",entity->sprite,entity->getUID(),entity->x,entity->y);
1844 // make torches
1845 if ( arrowtrap )
1846 {
1847 entity = newEntity(4 + side, 1, map.entities, nullptr);
1848 Entity* entity2 = newEntity(4 + side, 1, map.entities, nullptr);
1849 switch ( side )
1850 {
1851 case 0:
1852 entity->x = x * 16 + 16;
1853 entity->y = y * 16 + 4;
1854 entity2->x = x * 16 + 16;
1855 entity2->y = y * 16 - 4;
1856 break;
1857 case 1:
1858 entity->x = x * 16 + 4;
1859 entity->y = y * 16 + 16;
1860 entity2->x = x * 16 - 4;
1861 entity2->y = y * 16 + 16;
1862 break;
1863 case 2:
1864 entity->x = x * 16 - 16;
1865 entity->y = y * 16 + 4;
1866 entity2->x = x * 16 - 16;
1867 entity2->y = y * 16 - 4;
1868 break;
1869 case 3:
1870 entity->x = x * 16 + 4;
1871 entity->y = y * 16 - 16;
1872 entity2->x = x * 16 - 4;
1873 entity2->y = y * 16 - 16;
1874 break;
1875 }
1876 //printlog("5 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",entity->sprite,entity->getUID(),entity->x,entity->y);
1877 //printlog("6 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",entity2->sprite,entity2->getUID(),entity2->x,entity2->y);
1878 }
1879 i = 0;
1880 int testx = 0, testy = 0;
1881 do
1882 {
1883 if ( i == 0 )
1884 {
1885 // get rid of extraneous torch
1886 node_t* tempNode;
1887 node_t* nextTempNode;
1888 for ( tempNode = map.entities->first; tempNode != nullptr; tempNode = nextTempNode )
1889 {
1890 nextTempNode = tempNode->next;
1891 Entity* tempEntity = (Entity*)tempNode->element;
1892 if ( tempEntity->sprite >= 4 && tempEntity->sprite <= 7 )
1893 {
1894 if ( ((int)floor(tempEntity->x + 8)) / 16 == x && ((int)floor(tempEntity->y + 8)) / 16 == y )
1895 {
1896 list_RemoveNode(tempNode);
1897 }
1898 }
1899 }
1900 }
1901 if ( arrowtrap )
1902 {
1903 entity = newEntity(33, 1, map.entities, nullptr); // pressure plate
1904 }
1905 else
1906 {
1907 entity = newEntity(34, 1, map.entities, nullptr); // pressure plate
1908 }
1909 entity->x = x * 16;
1910 entity->y = y * 16;
1911 //printlog("7 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",entity->sprite,entity->getUID(),entity->x,entity->y);
1912 entity = newEntity(18, 1, map.entities, nullptr); // electricity node
1913 entity->x = x * 16 - (side == 3) * 16 + (side == 1) * 16;
1914 entity->y = y * 16 - (side == 0) * 16 + (side == 2) * 16;
1915 //printlog("8 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",entity->sprite,entity->getUID(),entity->x,entity->y);
1916 switch ( side )
1917 {
1918 case 0:
1919 x++;
1920 break;
1921 case 1:
1922 y++;
1923 break;
1924 case 2:
1925 x--;
1926 break;
1927 case 3:
1928 y--;
1929 break;
1930 }
1931 i++;
1932 testx = std::min(std::max<unsigned int>(0, x), map.width - 1); //TODO: Why are const int and unsigned int being compared?
1933 testy = std::min(std::max<unsigned int>(0, y), map.height - 1); //TODO: Why are const int and unsigned int being compared?
1934 }
1935 while ( !map.tiles[OBSTACLELAYER + testy * MAPLAYERS + testx * MAPLAYERS * map.height] && i <= 10 );
1936 }
1937 }
1938
1939 // monsters, decorations, and items
1940 numpossiblelocations = map.width * map.height;
1941 for ( y = 0; y < map.height; y++ )
1942 {
1943 for ( x = 0; x < map.width; x++ )
1944 {
1945 if ( checkObstacle( x * 16 + 8, y * 16 + 8, NULL, NULL ) || firstroomtile[y + x * map.height] )
1946 {
1947 possiblelocations[y + x * map.height] = false;
1948 numpossiblelocations--;
1949 }
1950 else if ( lavatiles[map.tiles[y * MAPLAYERS + x * MAPLAYERS * map.height]] )
1951 {
1952 possiblelocations[y + x * map.height] = false;
1953 numpossiblelocations--;
1954 }
1955 else if ( swimmingtiles[map.tiles[y * MAPLAYERS + x * MAPLAYERS * map.height]] )
1956 {
1957 possiblelocations[y + x * map.height] = false;
1958 numpossiblelocations--;
1959 }
1960 else
1961 {
1962 possiblelocations[y + x * map.height] = true;
1963 }
1964 }
1965 }
1966 for ( node = map.entities->first; node != nullptr; node = node->next )
1967 {
1968 entity = (Entity*)node->element;
1969 x = entity->x / 16;
1970 y = entity->y / 16;
1971 if ( x >= 0 && x < map.width && y >= 0 && y < map.height )
1972 {
1973 if ( possiblelocations[y + x * map.height] )
1974 {
1975 possiblelocations[y + x * map.height] = false;
1976 --numpossiblelocations;
1977 }
1978 }
1979 }
1980
1981 // read some editor map data if available:
1982 int genEntityMin = 0;
1983 int genEntityMax = 0;
1984 int genMonsterMin = 0;
1985 int genMonsterMax = 0;
1986 int genLootMin = 0;
1987 int genLootMax = 0;
1988 int genDecorationMin = 0;
1989 int genDecorationMax = 0;
1990
1991 if ( map.flags[MAP_FLAG_GENBYTES1] != 0 || map.flags[MAP_FLAG_GENBYTES2] != 0 )
1992 {
1993 genEntityMin = (map.flags[MAP_FLAG_GENBYTES1] >> 24) & 0xFF; // first leftmost byte
1994 genEntityMax = (map.flags[MAP_FLAG_GENBYTES1] >> 16) & 0xFF; // second leftmost byte
1995
1996 genMonsterMin = (map.flags[MAP_FLAG_GENBYTES1] >> 8) & 0xFF; // third leftmost byte
1997 genMonsterMax = (map.flags[MAP_FLAG_GENBYTES1] >> 0) & 0xFF; // fourth leftmost byte
1998
1999 genLootMin = (map.flags[MAP_FLAG_GENBYTES2] >> 24) & 0xFF; // first leftmost byte
2000 genLootMax = (map.flags[MAP_FLAG_GENBYTES2] >> 16) & 0xFF; // second leftmost byte
2001
2002 genDecorationMin = (map.flags[MAP_FLAG_GENBYTES2] >> 8) & 0xFF; // third leftmost byte
2003 genDecorationMax = (map.flags[MAP_FLAG_GENBYTES2] >> 0) & 0xFF; // fourth leftmost byte
2004 }
2005
2006 int entitiesToGenerate = 30;
2007 int randomEntities = 10;
2008
2009 if ( genEntityMin > 0 || genEntityMax > 0 )
2010 {
2011 genEntityMin = std::max(genEntityMin, 2); // make sure there's room for a ladder.
2012 entitiesToGenerate = genEntityMin;
2013 randomEntities = std::max(genEntityMax - genEntityMin, 1); // difference between min and max is the extra chances.
2014 //Needs to be 1 for prng_get_uint() % to not divide by 0.
2015 j = std::min<Uint32>(entitiesToGenerate + prng_get_uint() % randomEntities, numpossiblelocations); //TODO: Why are Uint32 and Sin32 being compared?
2016 }
2017 else
2018 {
2019 // revert to old mechanics.
2020 j = std::min<Uint32>(30 + prng_get_uint() % 10, numpossiblelocations); //TODO: Why are Uint32 and Sin32 being compared?
2021 }
2022 int forcedMonsterSpawns = 0;
2023 int forcedLootSpawns = 0;
2024 int forcedDecorationSpawns = 0;
2025
2026 if ( genMonsterMin > 0 || genMonsterMax > 0 )
2027 {
2028 forcedMonsterSpawns = genMonsterMin + prng_get_uint() % std::max(genMonsterMax - genMonsterMin, 1);
2029 }
2030 if ( genLootMin > 0 || genLootMax > 0 )
2031 {
2032 forcedLootSpawns = genLootMin + prng_get_uint() % std::max(genLootMax - genLootMin, 1);
2033 }
2034 if ( genDecorationMin > 0 || genDecorationMax > 0 )
2035 {
2036 forcedDecorationSpawns = genDecorationMin + prng_get_uint() % std::max(genDecorationMax - genDecorationMin, 1);
2037 }
2038
2039 //messagePlayer(0, "Num locations: %d of %d possible, force monsters: %d, force loot: %d, force decorations: %d", j, numpossiblelocations, forcedMonsterSpawns, forcedLootSpawns, forcedDecorationSpawns);
2040 printlog("Num locations: %d of %d possible, force monsters: %d, force loot: %d, force decorations: %d", j, numpossiblelocations, forcedMonsterSpawns, forcedLootSpawns, forcedDecorationSpawns);
2041 int numGenItems = 0;
2042 int numGenGold = 0;
2043 int numGenDecorations = 0;
2044
2045 //printlog("j: %d\n",j);
2046 //printlog("numpossiblelocations: %d\n",numpossiblelocations);
2047 for ( c = 0; c < std::min(j, numpossiblelocations); ++c )
2048 {
2049 // choose a random location from those available
2050 pickedlocation = prng_get_uint() % numpossiblelocations;
2051 i = -1;
2052 //printlog("pickedlocation: %d\n",pickedlocation);
2053 //printlog("numpossiblelocations: %d\n",numpossiblelocations);
2054 x = 0;
2055 y = 0;
2056 while ( 1 )
2057 {
2058 if ( possiblelocations[y + x * map.height] == true )
2059 {
2060 ++i;
2061 if ( i == pickedlocation )
2062 {
2063 break;
2064 }
2065 }
2066 ++x;
2067 if ( x >= map.width )
2068 {
2069 x = 0;
2070 ++y;
2071 if ( y >= map.height )
2072 {
2073 y = 0;
2074 }
2075 }
2076 }
2077
2078 // create entity
2079 entity = nullptr;
2080 if ( (c == 0 || (minotaurlevel && c < 2)) && (!secretlevel || currentlevel != 7) && (!secretlevel || currentlevel != 20)
2081 && std::get<LEVELPARAM_DISABLE_NORMAL_EXIT>(mapParameters) == 0 )
2082 {
2083 if ( strcmp(map.name, "Hell") )
2084 {
2085 entity = newEntity(11, 1, map.entities, nullptr); // ladder
2086 entity->behavior = &actLadder;
2087 }
2088 else
2089 {
2090 entity = newEntity(45, 1, map.entities, nullptr); // hell uses portals instead
2091 entity->behavior = &actPortal;
2092 entity->skill[3] = 1; // not secret portals though
2093 }
2094
2095 // determine if the ladder generated in a viable location
2096 if ( strncmp(map.name, "Underworld", 10) )
2097 {
2098 bool nopath = false;
2099 bool hellLadderFix = !strncmp(map.name, "Hell", 4);
2100 /*if ( !hellLadderFix )
2101 {
2102 hellLadderFix = !strncmp(map.name, "Caves", 4);
2103 }*/
2104 for ( node = map.entities->first; node != NULL; node = node->next )
2105 {
2106 entity2 = (Entity*)node->element;
2107 if ( entity2->sprite == 1 )
2108 {
2109 list_t* path = generatePath(x, y, entity2->x / 16, entity2->y / 16, entity, entity2, hellLadderFix);
2110 if ( path == NULL )
2111 {
2112 nopath = true;
2113 }
2114 else
2115 {
2116 list_FreeAll(path);
2117 free(path);
2118 }
2119 break;
2120 }
2121 }
2122 if ( nopath )
2123 {
2124 // try again
2125 c--;
2126 list_RemoveNode(entity->mynode);
2127 entity = NULL;
2128 }
2129 }
2130 }
2131 else if ( c == 1 && secretlevel && currentlevel == 7 && !strncmp(map.name, "Underworld", 10) )
2132 {
2133 entity = newEntity(89, 1, map.entities, nullptr);
2134 entity->monsterStoreType = 1;
2135 entity->skill[5] = nummonsters;
2136 ++nummonsters;
2137 //entity = newEntity(68, 1, map.entities, nullptr); // magic (artifact) bow
2138 }
2139 else
2140 {
2141 int x2, y2;
2142 bool nodecoration = false;
2143 int obstacles = 0;
2144 for ( x2 = -1; x2 <= 1; x2++ )
2145 {
2146 for ( y2 = -1; y2 <= 1; y2++ )
2147 {
2148 if ( checkObstacle((x + x2) * 16, (y + y2) * 16, NULL, NULL) )
2149 {
2150 obstacles++;
2151 if ( obstacles > 1 )
2152 {
2153 break;
2154 }
2155 }
2156 }
2157 if ( obstacles > 1 )
2158 {
2159 break;
2160 }
2161 }
2162 if ( obstacles > 1 )
2163 {
2164 nodecoration = true;
2165 }
2166 if ( forcedMonsterSpawns > 0 || forcedLootSpawns > 0 || (forcedDecorationSpawns > 0 && !nodecoration) )
2167 {
2168 // force monsters, then loot, then decorations.
2169 if ( forcedMonsterSpawns > 0 )
2170 {
2171 --forcedMonsterSpawns;
2172 if ( monsterexcludelocations[x + y * map.width] == false )
2173 {
2174 bool doNPC = false;
2175 if ( gameplayCustomManager.processedPropertyForFloor(currentlevel, secretlevel, map.name, GameplayCustomManager::PROPERTY_NPC, doNPC) )
2176 {
2177 // doNPC processed by function
2178 }
2179 else if ( prng_get_uint() % 10 == 0 && currentlevel > 1 )
2180 {
2181 doNPC = true;
2182 }
2183
2184 if ( doNPC )
2185 {
2186 if ( currentlevel > 15 && prng_get_uint() % 4 > 0 )
2187 {
2188 entity = newEntity(93, 1, map.entities, map.creatures); // automaton
2189 if ( currentlevel < 25 )
2190 {
2191 entity->monsterStoreType = 1; // weaker version
2192 }
2193 }
2194 else
2195 {
2196 entity = newEntity(27, 1, map.entities, map.creatures); // human
2197 if ( multiplayer != CLIENT && currentlevel > 5 )
2198 {
2199 entity->monsterStoreType = (currentlevel / 5) * 3 + (rand() % 4); // scale humans with depth. 3 LVL each 5 floors, + 0-3.
2200 }
2201 }
2202 }
2203 else
2204 {
2205 entity = newEntity(10, 1, map.entities, map.creatures); // monster
2206 }
2207 entity->skill[5] = nummonsters;
2208 ++nummonsters;
2209 }
2210 }
2211 else if ( forcedLootSpawns > 0 )
2212 {
2213 --forcedLootSpawns;
2214 if ( lootexcludelocations[x + y * map.width] == false )
2215 {
2216 if ( prng_get_uint() % 10 == 0 ) // 10% chance
2217 {
2218 entity = newEntity(9, 1, map.entities, nullptr); // gold
2219 numGenGold++;
2220 }
2221 else
2222 {
2223 entity = newEntity(8, 1, map.entities, nullptr); // item
2224 setSpriteAttributes(entity, nullptr, nullptr);
2225 numGenItems++;
2226 }
2227 }
2228 }
2229 else if ( forcedDecorationSpawns > 0 && !nodecoration )
2230 {
2231 --forcedDecorationSpawns;
2232 // decorations
2233 if ( (prng_get_uint() % 4 == 0 || currentlevel <= 10 && !customTrapsForMapInUse) && strcmp(map.name, "Hell") )
2234 {
2235 switch ( prng_get_uint() % 7 )
2236 {
2237 case 0:
2238 entity = newEntity(12, 1, map.entities, nullptr); //Firecamp.
2239 break; //Firecamp
2240 case 1:
2241 entity = newEntity(14, 1, map.entities, nullptr); //Fountain.
2242 break; //Fountain
2243 case 2:
2244 entity = newEntity(15, 1, map.entities, nullptr); //Sink.
2245 break; //Sink
2246 case 3:
2247 entity = newEntity(21, 1, map.entities, nullptr); //Chest.
2248 setSpriteAttributes(entity, nullptr, nullptr);
2249 entity->chestLocked = -1;
2250 break; //Chest
2251 case 4:
2252 entity = newEntity(39, 1, map.entities, nullptr); //Tomb.
2253 break; //Tomb
2254 case 5:
2255 entity = newEntity(59, 1, map.entities, nullptr); //Table.
2256 setSpriteAttributes(entity, nullptr, nullptr);
2257 break; //Table
2258 case 6:
2259 entity = newEntity(60, 1, map.entities, nullptr); //Chair.
2260 setSpriteAttributes(entity, nullptr, nullptr);
2261 break; //Chair
2262 }
2263 }
2264 else
2265 {
2266 if ( customTrapsForMapInUse )
2267 {
2268 if ( !customTraps.spikes && !customTraps.verticalSpelltraps )
2269 {
2270 continue;
2271 }
2272 else if ( customTraps.verticalSpelltraps && prng_get_uint() % 2 == 0 )
2273 {
2274 entity = newEntity(120, 1, map.entities, nullptr); // vertical spell trap.
2275 setSpriteAttributes(entity, nullptr, nullptr);
2276 }
2277 else if ( customTraps.spikes )
2278 {
2279 entity = newEntity(64, 1, map.entities, nullptr); // spear trap
2280 }
2281 }
2282 else
2283 {
2284 if ( currentlevel <= 25 )
2285 {
2286 entity = newEntity(64, 1, map.entities, nullptr); // spear trap
2287 }
2288 else
2289 {
2290 if ( prng_get_uint() % 2 == 0 )
2291 {
2292 entity = newEntity(120, 1, map.entities, nullptr); // vertical spell trap.
2293 setSpriteAttributes(entity, nullptr, nullptr);
2294 }
2295 else
2296 {
2297 entity = newEntity(64, 1, map.entities, nullptr); // spear trap
2298 }
2299 }
2300 }
2301 Entity* also = newEntity(33, 1, map.entities, nullptr);
2302 also->x = x * 16;
2303 also->y = y * 16;
2304 //printlog("15 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",also->sprite,also->getUID(),also->x,also->y);
2305 }
2306 numGenDecorations++;
2307 }
2308 }
2309 else
2310 {
2311 // return to normal generation
2312 if ( prng_get_uint() % 2 || nodecoration )
2313 {
2314 // balance for total number of players
2315 int balance = 0;
2316 for ( i = 0; i < MAXPLAYERS; i++ )
2317 {
2318 if ( !client_disconnected[i] )
2319 {
2320 balance++;
2321 }
2322 }
2323 switch ( balance )
2324 {
2325 case 1:
2326 balance = 4;
2327 break;
2328 case 2:
2329 balance = 3;
2330 break;
2331 case 3:
2332 balance = 2;
2333 break;
2334 case 4:
2335 balance = 2;
2336 break;
2337 default:
2338 balance = 2;
2339 break;
2340 }
2341
2342 // monsters/items
2343 if ( balance )
2344 {
2345 if ( prng_get_uint() % balance )
2346 {
2347 if ( lootexcludelocations[x + y * map.width] == false )
2348 {
2349 if ( prng_get_uint() % 10 == 0 ) // 10% chance
2350 {
2351 entity = newEntity(9, 1, map.entities, nullptr); // gold
2352 numGenGold++;
2353 }
2354 else
2355 {
2356 entity = newEntity(8, 1, map.entities, nullptr); // item
2357 setSpriteAttributes(entity, nullptr, nullptr);
2358 numGenItems++;
2359 }
2360 }
2361 }
2362 else
2363 {
2364 if ( monsterexcludelocations[x + y * map.width] == false )
2365 {
2366 bool doNPC = false;
2367 if ( gameplayCustomManager.processedPropertyForFloor(currentlevel, secretlevel, map.name, GameplayCustomManager::PROPERTY_NPC, doNPC) )
2368 {
2369 // doNPC processed by function
2370 }
2371 else if ( prng_get_uint() % 10 == 0 && currentlevel > 1 )
2372 {
2373 doNPC = true;
2374 }
2375
2376 if ( doNPC )
2377 {
2378 if ( currentlevel > 15 && prng_get_uint() % 4 > 0 )
2379 {
2380 entity = newEntity(93, 1, map.entities, map.creatures); // automaton
2381 if ( currentlevel < 25 )
2382 {
2383 entity->monsterStoreType = 1; // weaker version
2384 }
2385 }
2386 else
2387 {
2388 entity = newEntity(27, 1, map.entities, map.creatures); // human
2389 if ( multiplayer != CLIENT && currentlevel > 5 )
2390 {
2391 entity->monsterStoreType = (currentlevel / 5) * 3 + (rand() % 4); // scale humans with depth. 3 LVL each 5 floors, + 0-3.
2392 }
2393 }
2394 }
2395 else
2396 {
2397 entity = newEntity(10, 1, map.entities, map.creatures); // monster
2398 }
2399 entity->skill[5] = nummonsters;
2400 nummonsters++;
2401 }
2402 }
2403 }
2404 }
2405 else
2406 {
2407 // decorations
2408 if ( (prng_get_uint() % 4 == 0 || (currentlevel <= 10 && !customTrapsForMapInUse)) && strcmp(map.name, "Hell") )
2409 {
2410 switch ( prng_get_uint() % 7 )
2411 {
2412 case 0:
2413 entity = newEntity(12, 1, map.entities, nullptr); //Firecamp entity.
2414 break; //Firecamp
2415 case 1:
2416 entity = newEntity(14, 1, map.entities, nullptr); //Fountain entity.
2417 break; //Fountain
2418 case 2:
2419 entity = newEntity(15, 1, map.entities, nullptr); //Sink entity.
2420 break; //Sink
2421 case 3:
2422 entity = newEntity(21, 1, map.entities, nullptr); //Chest entity.
2423 setSpriteAttributes(entity, nullptr, nullptr);
2424 entity->chestLocked = -1;
2425 break; //Chest
2426 case 4:
2427 entity = newEntity(39, 1, map.entities, nullptr); //Tomb entity.
2428 break; //Tomb
2429 case 5:
2430 entity = newEntity(59, 1, map.entities, nullptr); //Table entity.
2431 setSpriteAttributes(entity, nullptr, nullptr);
2432 break; //Table
2433 case 6:
2434 entity = newEntity(60, 1, map.entities, nullptr); //Chair entity.
2435 setSpriteAttributes(entity, nullptr, nullptr);
2436 break; //Chair
2437 }
2438 }
2439 else
2440 {
2441 if ( customTrapsForMapInUse )
2442 {
2443 if ( !customTraps.spikes && !customTraps.verticalSpelltraps )
2444 {
2445 continue;
2446 }
2447 else if ( customTraps.verticalSpelltraps && prng_get_uint() % 2 == 0 )
2448 {
2449 entity = newEntity(120, 1, map.entities, nullptr); // vertical spell trap.
2450 setSpriteAttributes(entity, nullptr, nullptr);
2451 }
2452 else if ( customTraps.spikes )
2453 {
2454 entity = newEntity(64, 1, map.entities, nullptr); // spear trap
2455 }
2456 }
2457 else
2458 {
2459 if ( currentlevel <= 25 )
2460 {
2461 entity = newEntity(64, 1, map.entities, nullptr); // spear trap
2462 }
2463 else
2464 {
2465 if ( prng_get_uint() % 2 == 0 )
2466 {
2467 entity = newEntity(120, 1, map.entities, nullptr); // vertical spell trap.
2468 setSpriteAttributes(entity, nullptr, nullptr);
2469 }
2470 else
2471 {
2472 entity = newEntity(64, 1, map.entities, nullptr); // spear trap
2473 }
2474 }
2475 }
2476 Entity* also = newEntity(33, 1, map.entities, nullptr);
2477 also->x = x * 16;
2478 also->y = y * 16;
2479 //printlog("15 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",also->sprite,also->getUID(),also->x,also->y);
2480 }
2481 numGenDecorations++;
2482 }
2483 }
2484 }
2485 if ( entity != nullptr )
2486 {
2487 entity->x = x * 16;
2488 entity->y = y * 16;
2489 //printlog("9 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",entity->sprite,entity->getUID(),entity->x,entity->y);
2490 }
2491 // mark this location as inelligible for reselection
2492 possiblelocations[y + x * map.height] = false;
2493 numpossiblelocations--;
2494 }
2495
2496 // on hell levels, lava doesn't bubble. helps performance
2497 /*if( !strcmp(map.name,"Hell") ) {
2498 for( node=map.entities->first; node!=NULL; node=node->next ) {
2499 Entity *entity = (Entity *)node->element;
2500 if( entity->sprite == 41 ) { // lava.png
2501 entity->skill[4] = 1; // LIQUID_LAVANOBUBBLE =
2502 }
2503 }
2504 }*/
2505
2506 free(possiblelocations);
2507 free(trapexcludelocations);
2508 free(monsterexcludelocations);
2509 free(lootexcludelocations);
2510 free(firstroomtile);
2511 free(subRoomName);
2512 free(sublevelname);
2513 list_FreeAll(&subRoomMapList);
2514 list_FreeAll(&mapList);
2515 list_FreeAll(&doorList);
2516 printlog("successfully generated a dungeon with %d rooms, %d monsters, %d gold, %d items, %d decorations.\n", roomcount, nummonsters, numGenGold, numGenItems, numGenDecorations);
2517 //messagePlayer(0, "successfully generated a dungeon with %d rooms, %d monsters, %d gold, %d items, %d decorations.", roomcount, nummonsters, numGenGold, numGenItems, numGenDecorations);
2518 return secretlevelexit;
2519 }
2520
2521 /*-------------------------------------------------------------------------------
2522
2523 assignActions
2524
2525 configures a map to be playable from a default state
2526
2527 -------------------------------------------------------------------------------*/
2528
assignActions(map_t * map)2529 void assignActions(map_t* map)
2530 {
2531 Sint32 x, y, c;
2532 //Sint32 z;
2533 node_t* node, *nextnode;
2534 Entity* entity, *childEntity;
2535 Item* item;
2536 bool itemsdonebefore = false;
2537 Entity* vampireQuestChest = nullptr;
2538
2539 if ( map == nullptr )
2540 {
2541 return;
2542 }
2543
2544 // add lava lights
2545 for ( y = 0; y < map->height; ++y )
2546 {
2547 for ( x = 0; x < map->width; ++x )
2548 {
2549 if ( lavatiles[map->tiles[y * MAPLAYERS + x * MAPLAYERS * map->height]] )
2550 {
2551 lightSphereShadow(x, y, 2, 128);
2552 }
2553 }
2554 }
2555
2556 // seed the random generator
2557
2558 prng_seed_bytes(&mapseed, sizeof(mapseed));
2559
2560 int balance = 0;
2561 int i;
2562 for ( i = 0; i < MAXPLAYERS; i++ )
2563 {
2564 if ( !client_disconnected[i] )
2565 {
2566 balance++;
2567 }
2568 }
2569
2570 bool customMonsterCurveExists = false;
2571 if ( !monsterCurveCustomManager.inUse() )
2572 {
2573 monsterCurveCustomManager.readFromFile();
2574 }
2575 if ( monsterCurveCustomManager.curveExistsForCurrentMapName(map->name) )
2576 {
2577 customMonsterCurveExists = true;
2578 conductGameChallenges[CONDUCT_MODDED] = 1;
2579 gamemods_disableSteamAchievements = true;
2580 }
2581 if ( gameplayCustomManager.inUse() )
2582 {
2583 conductGameChallenges[CONDUCT_MODDED] = 1;
2584 gamemods_disableSteamAchievements = true;
2585 }
2586
2587 // assign entity behaviors
2588 for ( node = map->entities->first; node != nullptr; node = nextnode )
2589 {
2590 entity = (Entity*)node->element;
2591 nextnode = node->next;
2592 switch ( entity->sprite )
2593 {
2594 // null:
2595 case 0:
2596 {
2597 list_RemoveNode(entity->mynode);
2598 entity = nullptr;
2599 break;
2600 // player:
2601 }
2602 case 1:
2603 {
2604 if ( numplayers >= 0 && numplayers < MAXPLAYERS )
2605 {
2606 if ( client_disconnected[numplayers] )
2607 {
2608 // don't spawn missing players
2609 ++numplayers;
2610 list_RemoveNode(entity->mynode);
2611 entity = nullptr;
2612 break;
2613 }
2614 if ( multiplayer != CLIENT )
2615 {
2616 if ( stats[numplayers]->HP <= 0 )
2617 {
2618 messagePlayer(numplayers, language[1109]);
2619 stats[numplayers]->HP = stats[numplayers]->MAXHP / 2;
2620 stats[numplayers]->MP = stats[numplayers]->MAXMP / 2;
2621 stats[numplayers]->HUNGER = 500;
2622 for ( c = 0; c < NUMEFFECTS; ++c )
2623 {
2624 if ( !(c == EFF_VAMPIRICAURA && stats[numplayers]->EFFECTS_TIMERS[c] == -2)
2625 && c != EFF_WITHDRAWAL && c != EFF_SHAPESHIFT )
2626 {
2627 stats[numplayers]->EFFECTS[c] = false;
2628 stats[numplayers]->EFFECTS_TIMERS[c] = 0;
2629 }
2630 }
2631 }
2632 }
2633 entity->behavior = &actPlayer;
2634 entity->addToCreatureList(map->creatures);
2635 entity->x += 8;
2636 entity->y += 8;
2637 entity->z = -1;
2638 entity->focalx = limbs[HUMAN][0][0]; // 0
2639 entity->focaly = limbs[HUMAN][0][1]; // 0
2640 entity->focalz = limbs[HUMAN][0][2]; // -1.5
2641 entity->sprite = 113; // head model
2642 entity->sizex = 4;
2643 entity->sizey = 4;
2644 entity->flags[GENIUS] = true;
2645 if ( numplayers == clientnum && multiplayer == CLIENT )
2646 {
2647 entity->flags[UPDATENEEDED] = false;
2648 }
2649 else
2650 {
2651 entity->flags[UPDATENEEDED] = true;
2652 }
2653 entity->flags[BLOCKSIGHT] = true;
2654 entity->skill[2] = numplayers; // skill[2] == PLAYER_NUM
2655 players[numplayers]->entity = entity;
2656 if ( entity->playerStartDir == -1 )
2657 {
2658 entity->yaw = (prng_get_uint() % 8) * 45 * (PI / 180.f);
2659 }
2660 else
2661 {
2662 entity->yaw = entity->playerStartDir * 45 * (PI / 180.f);
2663 }
2664 entity->playerStartDir = 0;
2665 if ( multiplayer != CLIENT )
2666 {
2667 if ( numplayers == 0 && minotaurlevel )
2668 {
2669 createMinotaurTimer(entity, map);
2670 }
2671 }
2672 ++numplayers;
2673 }
2674 if ( balance > 4 )
2675 {
2676 // if MAXPLAYERS > 4, then add some new player markers
2677 --balance;
2678 Entity* extraPlayer = newEntity(1, 1, map->entities, nullptr);
2679 extraPlayer->x = entity->x - 8;
2680 extraPlayer->y = entity->y - 8;
2681 }
2682 if ( numplayers > MAXPLAYERS )
2683 {
2684 printlog("warning: too many player objects in level!\n");
2685 }
2686 break;
2687 }
2688 // east/west door:
2689 case 2:
2690 {
2691 entity->x += 8;
2692 entity->y += 8;
2693 entity->sprite = 1;
2694 entity->flags[PASSABLE] = true;
2695 entity->behavior = &actDoorFrame;
2696 childEntity = newEntity(2, 0, map->entities, nullptr); //Door frame entity.
2697 childEntity->x = entity->x;
2698 childEntity->y = entity->y;
2699 TileEntityList.addEntity(*childEntity);
2700 //printlog("16 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
2701 childEntity->sizex = 1;
2702 childEntity->sizey = 8;
2703 childEntity->behavior = &actDoor;
2704 childEntity->flags[BLOCKSIGHT] = true;
2705 childEntity->skill[0] = 0; // signify behavior code of DOOR_DIR
2706
2707 // copy editor options from frame to door itself.
2708 childEntity->doorDisableLockpicks = entity->doorDisableLockpicks;
2709 childEntity->doorForceLockedUnlocked = entity->doorForceLockedUnlocked;
2710 childEntity->doorDisableOpening = entity->doorDisableOpening;
2711
2712 childEntity = newEntity(1, 0, map->entities, nullptr); //Door entity.
2713 childEntity->flags[INVISIBLE] = true;
2714 childEntity->flags[BLOCKSIGHT] = true;
2715 childEntity->x = entity->x;
2716 childEntity->y = entity->y - 7;
2717 TileEntityList.addEntity(*childEntity);
2718
2719 //printlog("17 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
2720 childEntity->sizex = 2;
2721 childEntity->sizey = 2;
2722 childEntity->behavior = &actDoorFrame;
2723 childEntity = newEntity(1, 0, map->entities, nullptr); //Door frame entity.
2724 childEntity->flags[INVISIBLE] = true;
2725 childEntity->flags[BLOCKSIGHT] = true;
2726 childEntity->x = entity->x;
2727 childEntity->y = entity->y + 7;
2728 TileEntityList.addEntity(*childEntity);
2729 //printlog("18 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
2730 childEntity->sizex = 2;
2731 childEntity->sizey = 2;
2732 childEntity->behavior = &actDoorFrame;
2733 break;
2734 }
2735 // north/south door:
2736 case 3:
2737 {
2738 entity->x += 8;
2739 entity->y += 8;
2740 entity->yaw -= PI / 2.0;
2741 entity->sprite = 1;
2742 entity->flags[PASSABLE] = true;
2743 entity->behavior = &actDoorFrame;
2744 childEntity = newEntity(2, 0, map->entities, nullptr); //Door frame entity.
2745 childEntity->x = entity->x;
2746 childEntity->y = entity->y;
2747 TileEntityList.addEntity(*childEntity);
2748 //printlog("19 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
2749 childEntity->sizex = 8;
2750 childEntity->sizey = 1;
2751 childEntity->yaw -= PI / 2.0;
2752 childEntity->behavior = &actDoor;
2753 childEntity->flags[BLOCKSIGHT] = true;
2754 childEntity->skill[0] = 1; // signify behavior code of DOOR_DIR
2755
2756 // copy editor options from frame to door itself.
2757 childEntity->doorDisableLockpicks = entity->doorDisableLockpicks;
2758 childEntity->doorForceLockedUnlocked = entity->doorForceLockedUnlocked;
2759 childEntity->doorDisableOpening = entity->doorDisableOpening;
2760
2761 childEntity = newEntity(1, 0, map->entities, nullptr); //Door entity.
2762 childEntity->flags[INVISIBLE] = true;
2763 childEntity->flags[BLOCKSIGHT] = true;
2764 childEntity->x = entity->x - 7;
2765 childEntity->y = entity->y;
2766
2767 TileEntityList.addEntity(*childEntity);
2768 //printlog("20 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
2769 childEntity->sizex = 2;
2770 childEntity->sizey = 2;
2771 childEntity->behavior = &actDoorFrame;
2772
2773 childEntity = newEntity(1, 0, map->entities, nullptr); //Door frame entity.
2774 childEntity->flags[INVISIBLE] = true;
2775 childEntity->flags[BLOCKSIGHT] = true;
2776 childEntity->x = entity->x + 7;
2777 childEntity->y = entity->y;
2778 TileEntityList.addEntity(*childEntity);
2779 //printlog("21 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
2780 childEntity->sizex = 2;
2781 childEntity->sizey = 2;
2782 childEntity->behavior = &actDoorFrame;
2783 break;
2784 }
2785 // east torch:
2786 case 4:
2787 {
2788 if ( darkmap )
2789 {
2790 list_RemoveNode(entity->mynode);
2791 entity = NULL;
2792 break;
2793 }
2794 entity->behavior = &actTorch;
2795 entity->x += 1;
2796 entity->y += 8;
2797 entity->z -= 1;
2798 entity->sprite = 3;
2799 entity->flags[PASSABLE] = true;
2800 entity->flags[BRIGHT] = true;
2801 break;
2802 // south torch:
2803 }
2804 case 5:
2805 {
2806 if ( darkmap )
2807 {
2808 list_RemoveNode(entity->mynode);
2809 entity = NULL;
2810 break;
2811 }
2812 entity->behavior = &actTorch;
2813 entity->x += 8;
2814 entity->y += 1;
2815 entity->z -= 1;
2816 entity->yaw += PI / 2.0;
2817 entity->sprite = 3;
2818 entity->flags[PASSABLE] = true;
2819 entity->flags[BRIGHT] = true;
2820 break;
2821 }
2822 // west torch:
2823 case 6:
2824 {
2825 if ( darkmap )
2826 {
2827 list_RemoveNode(entity->mynode);
2828 entity = NULL;
2829 break;
2830 }
2831 entity->behavior = &actTorch;
2832 entity->x += 15;
2833 entity->y += 8;
2834 entity->z -= 1;
2835 entity->yaw += PI;
2836 entity->sprite = 3;
2837 entity->flags[PASSABLE] = true;
2838 entity->flags[BRIGHT] = true;
2839 break;
2840 }
2841 // north torch:
2842 case 7:
2843 {
2844 if ( darkmap )
2845 {
2846 list_RemoveNode(entity->mynode);
2847 entity = NULL;
2848 break;
2849 }
2850 entity->behavior = &actTorch;
2851 entity->x += 8;
2852 entity->y += 15;
2853 entity->z -= 1;
2854 entity->yaw += 3 * PI / 2.0;
2855 entity->sprite = 3;
2856 entity->flags[PASSABLE] = true;
2857 entity->flags[BRIGHT] = true;
2858 break;
2859 }
2860 // item:
2861 case 68:
2862 case 69:
2863 case 8:
2864 {
2865 entity->sizex = 4;
2866 entity->sizey = 4;
2867 entity->x += 8;
2868 entity->y += 8;
2869 entity->roll = PI / 2.0;
2870 entity->yaw = (prng_get_uint() % 360) * PI / 180.0;
2871 entity->flags[PASSABLE] = true;
2872 entity->behavior = &actItem;
2873 if ( entity->sprite == 68 ) // magic_bow.png
2874 {
2875 entity->skill[10] = ARTIFACT_BOW;
2876 }
2877 else if ( entity->sprite == 69 ) // magic_spear.png
2878 {
2879 entity->skill[10] = ARTIFACT_SPEAR;
2880 entity->x += 8;
2881 entity->y += 8;
2882 }
2883 else if ( entity->skill[10] == 0 || entity->skill[10] == 1 )
2884 {
2885 if ( entity->skill[16] == 0 )
2886 {
2887 // random category default, either set by editor or spawn naturally.
2888 if ( !itemsdonebefore && !strcmp(map->name, "Start Map") )
2889 {
2890 entity->skill[10] = READABLE_BOOK;
2891 }
2892 else
2893 {
2894 bool extrafood = false;
2895 switch ( balance )
2896 {
2897 case 2:
2898 if ( prng_get_uint() % 8 == 0 )
2899 {
2900 extrafood = true;
2901 }
2902 break;
2903 case 3:
2904 if ( prng_get_uint() % 6 == 0 )
2905 {
2906 extrafood = true;
2907 }
2908 break;
2909 case 4:
2910 if ( prng_get_uint() % 5 == 0 )
2911 {
2912 extrafood = true;
2913 }
2914 break;
2915 default:
2916 extrafood = false;
2917 break;
2918 }
2919 if ( !extrafood )
2920 {
2921 if ( prng_get_uint() % 2 == 0 )
2922 {
2923 // possible magicstaff
2924 int randType = prng_get_uint() % (NUMCATEGORIES - 1);
2925 if ( randType == THROWN && prng_get_uint() % 3 ) // THROWN items 66% to be re-roll.
2926 {
2927 randType = prng_get_uint() % (NUMCATEGORIES - 1);
2928 }
2929 entity->skill[10] = itemLevelCurve(static_cast<Category>(randType), 0, currentlevel);
2930 }
2931 else
2932 {
2933 // impossible magicstaff
2934 int randType = prng_get_uint() % (NUMCATEGORIES - 2);
2935 if ( randType >= MAGICSTAFF )
2936 {
2937 randType++;
2938 }
2939 if ( randType == THROWN && prng_get_uint() % 3 ) // THROWN items 66% to be re-roll.
2940 {
2941 randType = prng_get_uint() % (NUMCATEGORIES - 2);
2942 if ( randType >= MAGICSTAFF )
2943 {
2944 randType++;
2945 }
2946 }
2947 entity->skill[10] = itemLevelCurve(static_cast<Category>(randType), 0, currentlevel);
2948 }
2949 }
2950 else
2951 {
2952 entity->skill[10] = itemLevelCurve(FOOD, 0, currentlevel);
2953 }
2954 }
2955 }
2956 else
2957 {
2958 // editor set the random category of the item to be spawned.
2959 if ( entity->skill[16] > 0 && entity->skill[16] <= 13 )
2960 {
2961 entity->skill[10] = itemLevelCurve(static_cast<Category>(entity->skill[16] - 1), 0, currentlevel);
2962 }
2963 else
2964 {
2965 int randType = 0;
2966 if ( entity->skill[16] == 14 )
2967 {
2968 // equipment
2969 randType = prng_get_uint() % 2;
2970 if ( randType == 0 )
2971 {
2972 entity->skill[10] = itemLevelCurve(static_cast<Category>(WEAPON), 0, currentlevel);
2973 }
2974 else if ( randType == 1 )
2975 {
2976 entity->skill[10] = itemLevelCurve(static_cast<Category>(ARMOR), 0, currentlevel);
2977 }
2978 }
2979 else if ( entity->skill[16] == 15 )
2980 {
2981 // jewelry
2982 randType = prng_get_uint() % 2;
2983 if ( randType == 0 )
2984 {
2985 entity->skill[10] = itemLevelCurve(static_cast<Category>(AMULET), 0, currentlevel);
2986 }
2987 else
2988 {
2989 entity->skill[10] = itemLevelCurve(static_cast<Category>(RING), 0, currentlevel);
2990 }
2991 }
2992 else if ( entity->skill[16] == 16 )
2993 {
2994 // magical
2995 randType = prng_get_uint() % 3;
2996 if ( randType == 0 )
2997 {
2998 entity->skill[10] = itemLevelCurve(static_cast<Category>(SCROLL), 0, currentlevel);
2999 }
3000 else if ( randType == 1 )
3001 {
3002 entity->skill[10] = itemLevelCurve(static_cast<Category>(MAGICSTAFF), 0, currentlevel);
3003 }
3004 else
3005 {
3006 entity->skill[10] = itemLevelCurve(static_cast<Category>(SPELLBOOK), 0, currentlevel);
3007 }
3008 }
3009 }
3010 }
3011 }
3012 else if ( entity->skill[10] != 0 && entity->skill[10] != 1 ) //editor set the item type
3013 {
3014 entity->skill[10] = entity->skill[10] - 2; //reduce by 2 as the editor treats 1 as random, 0 is NULL
3015 }
3016
3017 if ( entity->sprite == 8 )
3018 {
3019 if ( entity->skill[11] == 0 ) //random
3020 {
3021 entity->skill[11] = 1 + prng_get_uint() % 4; // status
3022 }
3023 else
3024 {
3025 entity->skill[11]--; //editor set number, sets this value to 0-5, with 1 being BROKEN, 5 being EXCELLENT
3026 }
3027 }
3028 else
3029 {
3030 entity->skill[11] = DECREPIT + (currentlevel > 5) + (currentlevel > 15) + (currentlevel > 20);
3031 }
3032 if ( entity->sprite == 8 )
3033 {
3034 if ( entity->skill[12] == 10 ) //random, else the value of this variable is the curse/bless
3035 {
3036 if ( prng_get_uint() % 2 == 0 ) // 50% chance of curse/bless
3037 {
3038 entity->skill[12] = -2 + prng_get_uint() % 5;
3039 }
3040 else
3041 {
3042 entity->skill[12] = 0;
3043 }
3044 }
3045 }
3046 else
3047 {
3048 entity->skill[12] = 1;
3049 }
3050 if ( entity->sprite == 8 )
3051 {
3052 if ( entity->skill[13] == 0 )
3053 {
3054 entity->skill[13] = 1; // count set by maps.cpp, otherwise set by editor
3055 }
3056 else if ( entity->skill[13] == 1 )
3057 {
3058 if ( items[entity->skill[10]].category == FOOD )
3059 {
3060 switch ( balance )
3061 {
3062 case 2:
3063 if ( prng_get_uint() % 3 == 0 )
3064 {
3065 entity->skill[13] += prng_get_uint() % 2;
3066 }
3067 break;
3068 case 3:
3069 if ( prng_get_uint() % 3 == 0 )
3070 {
3071 entity->skill[13] += prng_get_uint() % 3;
3072 }
3073 break;
3074 case 4:
3075 if ( prng_get_uint() % 2 == 0 )
3076 {
3077 entity->skill[13] += prng_get_uint() % 3;
3078 }
3079 break;
3080 default:
3081 break;
3082 }
3083 }
3084 }
3085 }
3086
3087 if ( !itemsdonebefore && !strcmp(map->name, "Start Map") )
3088 {
3089 entity->skill[14] = getBook("My Journal");
3090 }
3091 else
3092 {
3093 if ( items[entity->skill[10]].category == SCROLL || items[entity->skill[10]].variations > 1 )
3094 {
3095 entity->skill[14] = prng_get_uint(); // appearance
3096 }
3097 else
3098 {
3099 entity->skill[14] = 0; // appearance
3100 }
3101 }
3102 if ( entity->skill[15] == 1 ) // editor set as identified
3103 {
3104 entity->skill[15] = 1;
3105 }
3106 else if ( entity->skill[15] == 0 ) // unidentified (default)
3107 {
3108 entity->skill[15] = 0;
3109 }
3110 else if ( entity->skill[15] == 2 ) // editor set as random
3111 {
3112 entity->skill[15] = prng_get_uint() % 2;
3113 }
3114 else
3115 {
3116 entity->skill[15] = 0; // unidentified.
3117 }
3118
3119 if ( entity->skill[10] == ENCHANTED_FEATHER )
3120 {
3121 entity->skill[14] = 75 + 25 * (prng_get_uint() % 2); // appearance
3122 }
3123 else if ( entity->skill[10] >= BRONZE_TOMAHAWK && entity->skill[10] <= CRYSTAL_SHURIKEN )
3124 {
3125 // thrown weapons always fixed status. (tomahawk = decrepit, shuriken = excellent)
3126 entity->skill[11] = std::min(DECREPIT + (entity->skill[10] - BRONZE_TOMAHAWK), static_cast<int>(EXCELLENT));
3127 }
3128
3129 item = newItemFromEntity(entity);
3130 entity->sprite = itemModel(item);
3131 if ( !entity->itemNotMoving )
3132 {
3133 // shurikens and chakrams need to lie flat on floor as their models are rotated.
3134 if ( item->type == CRYSTAL_SHURIKEN || item->type == STEEL_CHAKRAM || item->type == BOOMERANG )
3135 {
3136 entity->roll = PI;
3137 if ( item->type == CRYSTAL_SHURIKEN )
3138 {
3139 entity->z = 8.5 - models[entity->sprite]->sizey * .25;
3140 }
3141 else if ( item->type == BOOMERANG )
3142 {
3143 entity->z = 9.0 - models[entity->sprite]->sizey * .25;
3144 }
3145 else
3146 {
3147 entity->z = 8.75 - models[entity->sprite]->sizey * .25;
3148 }
3149 }
3150 else if ( item->type == TOOL_BOMB || item->type == TOOL_FREEZE_BOMB
3151 || item->type == TOOL_SLEEP_BOMB || item->type == TOOL_TELEPORT_BOMB )
3152 {
3153 entity->roll = PI;
3154 entity->z = 7 - models[entity->sprite]->sizey * .25;
3155 }
3156 else
3157 {
3158 entity->z = 7.5 - models[entity->sprite]->sizey * .25;
3159 }
3160 }
3161 entity->itemNotMoving = 1; // so the item retains its position
3162 entity->itemNotMovingClient = 1; // so the item retains its position for clients
3163 itemsdonebefore = true;
3164 if ( !strcmp(map->name, "Sokoban") && item->type == ARTIFACT_GLOVES ) // artifact gloves.
3165 {
3166 entity->flags[INVISIBLE] = true;
3167 entity->itemSokobanReward = 1;
3168 }
3169 free(item);
3170 break;
3171 }
3172 // gold:
3173 case 9:
3174 entity->sizex = 4;
3175 entity->sizey = 4;
3176 entity->x += 8;
3177 entity->y += 8;
3178 entity->z = 6.5;
3179 entity->yaw = (prng_get_uint() % 360) * PI / 180.0;
3180 entity->flags[PASSABLE] = true;
3181 entity->behavior = &actGoldBag;
3182 entity->skill[0] = 10 + rand() % 100 + (currentlevel); // amount
3183 entity->sprite = 130; // gold bag model
3184 if ( !strcmp(map->name, "Sokoban") )
3185 {
3186 entity->flags[INVISIBLE] = true;
3187 entity->goldSokoban = 1;
3188 }
3189 break;
3190 // monster:
3191 case 71:
3192 case 70:
3193 case 62:
3194 case 48:
3195 case 36:
3196 case 35:
3197 case 30:
3198 case 27:
3199 case 10:
3200 case 75:
3201 case 76:
3202 case 77:
3203 case 78:
3204 case 79:
3205 case 80:
3206 case 81:
3207 case 82:
3208 case 83:
3209 case 84:
3210 case 85:
3211 case 86:
3212 case 87:
3213 case 88:
3214 case 89:
3215 case 90:
3216 case 91:
3217 case 92:
3218 case 93:
3219 case 94:
3220 case 95:
3221 case 163:
3222 case 164:
3223 case 165:
3224 case 166:
3225 {
3226 entity->sizex = 4;
3227 entity->sizey = 4;
3228 entity->x += 8;
3229 entity->y += 8;
3230 entity->z = 6;
3231 entity->yaw = (rand() % 360) * PI / 180.0;
3232 entity->behavior = &actMonster;
3233 entity->flags[UPDATENEEDED] = true;
3234 entity->skill[5] = -1;
3235 Stat* myStats = NULL;
3236 if ( multiplayer != CLIENT )
3237 {
3238 myStats = entity->getStats();
3239 }
3240 //Assign entity creature list pointer.
3241 entity->addToCreatureList(map->creatures);
3242
3243 Monster monsterType = SKELETON;
3244 bool monsterIsFixedSprite = true;
3245
3246 if ( entity->sprite == 27 ) // human.png
3247 {
3248 monsterType = HUMAN;
3249 }
3250 else if ( entity->sprite == 30 ) // troll.png
3251 {
3252 monsterType = TROLL;
3253 }
3254 else if ( entity->sprite == 35 ) // shop.png
3255 {
3256 monsterType = SHOPKEEPER;
3257 }
3258 else if ( entity->sprite == 36 ) // goblin.png
3259 {
3260 monsterType = GOBLIN;
3261 }
3262 else if ( entity->sprite == 48 ) // spider.png
3263 {
3264 monsterType = SPIDER;
3265 }
3266 else if ( entity->sprite == 62 ) // herx.png
3267 {
3268 monsterType = LICH;
3269 }
3270 else if ( entity->sprite == 70 ) // gnome.png
3271 {
3272 monsterType = GNOME;
3273 }
3274 else if ( entity->sprite == 71 ) // devil.png
3275 {
3276 monsterType = DEVIL;
3277 }
3278 else if ( entity->sprite == 83 ) // devil.png
3279 {
3280 monsterType = SKELETON;
3281 }
3282 else if ( entity->sprite == 84 ) // devil.png
3283 {
3284 monsterType = KOBOLD;
3285 }
3286 else if ( entity->sprite == 85 ) // devil.png
3287 {
3288 monsterType = SCARAB;
3289 }
3290 else if ( entity->sprite == 86 ) // devil.png
3291 {
3292 monsterType = CRYSTALGOLEM;
3293 }
3294 else if ( entity->sprite == 87 ) // devil.png
3295 {
3296 monsterType = INCUBUS;
3297 }
3298 else if ( entity->sprite == 88 ) // devil.png
3299 {
3300 monsterType = VAMPIRE;
3301 }
3302 else if ( entity->sprite == 89 ) // devil.png
3303 {
3304 monsterType = SHADOW;
3305 }
3306 else if ( entity->sprite == 90 ) // devil.png
3307 {
3308 monsterType = COCKATRICE;
3309 }
3310 else if ( entity->sprite == 91 ) // devil.png
3311 {
3312 monsterType = INSECTOID;
3313 }
3314 else if ( entity->sprite == 92 ) // devil.png
3315 {
3316 monsterType = GOATMAN;
3317 }
3318 else if ( entity->sprite == 93 ) // devil.png
3319 {
3320 monsterType = AUTOMATON;
3321 }
3322 else if ( entity->sprite == 94 ) // devil.png
3323 {
3324 monsterType = LICH_ICE;
3325 }
3326 else if ( entity->sprite == 95 ) // devil.png
3327 {
3328 monsterType = LICH_FIRE;
3329 }
3330 else if ( entity->sprite == 81 ) // devil.png
3331 {
3332 monsterType = RAT;
3333 }
3334 else if ( entity->sprite == 75 ) // devil.png
3335 {
3336 monsterType = DEMON;
3337 }
3338 else if ( entity->sprite == 76 ) // devil.png
3339 {
3340 monsterType = CREATURE_IMP;
3341 }
3342 else if ( entity->sprite == 77 ) // devil.png
3343 {
3344 monsterType = MINOTAUR;
3345 }
3346 else if ( entity->sprite == 78 ) // devil.png
3347 {
3348 monsterType = SCORPION;
3349 }
3350 else if ( entity->sprite == 79 ) // devil.png
3351 {
3352 monsterType = SLIME;
3353 }
3354 else if ( entity->sprite == 80 ) // devil.png
3355 {
3356 monsterType = SUCCUBUS;
3357 }
3358 else if ( entity->sprite == 82 ) // devil.png
3359 {
3360 monsterType = GHOUL;
3361 }
3362 else if ( entity->sprite == 163 )
3363 {
3364 monsterType = SENTRYBOT;
3365 }
3366 else if ( entity->sprite == 164 )
3367 {
3368 monsterType = SPELLBOT;
3369 }
3370 else if ( entity->sprite == 165 )
3371 {
3372 monsterType = DUMMYBOT;
3373 }
3374 else if ( entity->sprite == 166 )
3375 {
3376 monsterType = GYROBOT;
3377 }
3378 else
3379 {
3380 monsterIsFixedSprite = false;
3381 monsterType = static_cast<Monster>(monsterCurve(currentlevel));
3382 if ( customMonsterCurveExists )
3383 {
3384 Monster customMonsterType = static_cast<Monster>(monsterCurveCustomManager.rollMonsterFromCurve(map->name));
3385 if ( customMonsterType != NOTHING )
3386 {
3387 monsterType = customMonsterType;
3388 }
3389 else
3390 {
3391 customMonsterCurveExists = false;
3392 }
3393 }
3394 }
3395
3396 if ( multiplayer != CLIENT )
3397 {
3398 if ( myStats == nullptr )
3399 {
3400 // need to give the entity its list stuff.
3401 // create an empty first node for traversal purposes
3402 node_t* node2 = list_AddNodeFirst(&entity->children);
3403 node2->element = nullptr;
3404 node2->deconstructor = &emptyDeconstructor;
3405
3406 if ( entity->sprite == 10 )
3407 {
3408 // if the sprite is 10, then choose from monsterCurve.
3409 // Create the stat struct again for the new monster
3410 myStats = new Stat(monsterType + 1000);
3411 }
3412 else
3413 {
3414 // if monster not random, then create the stat struct here
3415 // should not occur (unless we hack it)
3416 myStats = new Stat(entity->sprite);
3417 }
3418 node2 = list_AddNodeLast(&entity->children);
3419 node2->element = myStats;
3420 // node2->deconstructor = &myStats->~Stat;
3421 node2->size = sizeof(myStats);
3422 }
3423 else if ( entity->sprite == 10 )
3424 {
3425 // monster is random, but generated from editor
3426 // stat struct is already created, need to set stats
3427 setDefaultMonsterStats(myStats, monsterType + 1000);
3428 setRandomMonsterStats(myStats);
3429 }
3430
3431 std::string checkName = myStats->name;
3432 if ( checkName.find(".json") != std::string::npos )
3433 {
3434 monsterCurveCustomManager.createMonsterFromFile(entity, myStats, checkName, monsterType);
3435 }
3436 else if ( customMonsterCurveExists )
3437 {
3438 std::string variantName = "default";
3439 if ( monsterIsFixedSprite )
3440 {
3441 if ( isMonsterStatsDefault(*myStats) )
3442 {
3443 variantName = monsterCurveCustomManager.rollFixedMonsterVariant(map->name, monsterType);
3444 }
3445 }
3446 else
3447 {
3448 variantName = monsterCurveCustomManager.rollMonsterVariant(map->name, monsterType);
3449 }
3450
3451 if ( variantName.compare("default") != 0 )
3452 {
3453 // find a custom file name.
3454 monsterCurveCustomManager.createMonsterFromFile(entity, myStats, variantName, monsterType);
3455 }
3456 }
3457 }
3458
3459 switch ( monsterType )
3460 {
3461 case RAT:
3462 entity->focalx = limbs[RAT][0][0]; // 0
3463 entity->focaly = limbs[RAT][0][1]; // 0
3464 entity->focalz = limbs[RAT][0][2]; // 0
3465 break;
3466 case SCORPION:
3467 entity->focalx = limbs[SCORPION][0][0]; // 0
3468 entity->focaly = limbs[SCORPION][0][1]; // 0
3469 entity->focalz = limbs[SCORPION][0][2]; // 0
3470 break;
3471 case HUMAN:
3472 entity->z = -1;
3473 entity->focalx = limbs[HUMAN][0][0]; // 0
3474 entity->focaly = limbs[HUMAN][0][1]; // 0
3475 entity->focalz = limbs[HUMAN][0][2]; // -1.5
3476 break;
3477 case GOBLIN:
3478 entity->z = 0;
3479 entity->focalx = limbs[GOBLIN][0][0]; // 0
3480 entity->focaly = limbs[GOBLIN][0][1]; // 0
3481 entity->focalz = limbs[GOBLIN][0][2]; // -1.75
3482 break;
3483 case SLIME:
3484 if ( multiplayer != CLIENT )
3485 {
3486 myStats->LVL = 7;
3487 }
3488 break;
3489 case SUCCUBUS:
3490 entity->z = -1;
3491 entity->focalx = limbs[SUCCUBUS][0][0]; // 0
3492 entity->focaly = limbs[SUCCUBUS][0][1]; // 0
3493 entity->focalz = limbs[SUCCUBUS][0][2]; // -1.5
3494 break;
3495 case TROLL:
3496 entity->z = -1.5;
3497 entity->focalx = limbs[TROLL][0][0]; // 1
3498 entity->focaly = limbs[TROLL][0][1]; // 0
3499 entity->focalz = limbs[TROLL][0][2]; // -2
3500 break;
3501 case SHOPKEEPER:
3502 entity->z = -1;
3503 entity->focalx = limbs[SHOPKEEPER][0][0]; // 0
3504 entity->focaly = limbs[SHOPKEEPER][0][1]; // 0
3505 entity->focalz = limbs[SHOPKEEPER][0][2]; // -1.5
3506 break;
3507 case SKELETON:
3508 entity->z = -.5;
3509 entity->focalx = limbs[SKELETON][0][0]; // 0
3510 entity->focaly = limbs[SKELETON][0][1]; // 0
3511 entity->focalz = limbs[SKELETON][0][2]; // -1.5
3512 break;
3513 case MINOTAUR:
3514 entity->z = -6;
3515 entity->focalx = limbs[MINOTAUR][0][0]; // 0
3516 entity->focaly = limbs[MINOTAUR][0][1]; // 0
3517 entity->focalz = limbs[MINOTAUR][0][2]; // 0
3518 break;
3519 case GHOUL:
3520 entity->z = -.25;
3521 entity->focalx = limbs[GHOUL][0][0]; // 0
3522 entity->focaly = limbs[GHOUL][0][1]; // 0
3523 entity->focalz = limbs[GHOUL][0][2]; // -1.5
3524 break;
3525 case DEMON:
3526 entity->z = -8.5;
3527 entity->focalx = limbs[DEMON][0][0]; // -1
3528 entity->focaly = limbs[DEMON][0][1]; // 0
3529 entity->focalz = limbs[DEMON][0][2]; // -1.25
3530 break;
3531 case SPIDER:
3532 entity->z = 4.5;
3533 entity->focalx = limbs[SPIDER][0][0]; // -3
3534 entity->focaly = limbs[SPIDER][0][1]; // 0
3535 entity->focalz = limbs[SPIDER][0][2]; // -1
3536 break;
3537 case LICH:
3538 entity->focalx = limbs[LICH][0][0]; // -0.75
3539 entity->focaly = limbs[LICH][0][1]; // 0
3540 entity->focalz = limbs[LICH][0][2]; // 0
3541 entity->z = -2;
3542 entity->yaw = PI;
3543 entity->sprite = 274;
3544 entity->skill[29] = 120;
3545 break;
3546 case CREATURE_IMP:
3547 entity->z = -4.5;
3548 entity->focalx = limbs[CREATURE_IMP][0][0]; // 0
3549 entity->focaly = limbs[CREATURE_IMP][0][1]; // 0
3550 entity->focalz = limbs[CREATURE_IMP][0][2]; // -1.75
3551 break;
3552 case GNOME:
3553 entity->z = 2.25;
3554 entity->focalx = limbs[GNOME][0][0]; // 0
3555 entity->focaly = limbs[GNOME][0][1]; // 0
3556 entity->focalz = limbs[GNOME][0][2]; // -2
3557 break;
3558 case DEVIL:
3559 entity->focalx = limbs[DEVIL][0][0]; // 0
3560 entity->focaly = limbs[DEVIL][0][1]; // 0
3561 entity->focalz = limbs[DEVIL][0][2]; // 0
3562 entity->z = -4;
3563 entity->sizex = 20;
3564 entity->sizey = 20;
3565 entity->yaw = PI;
3566 break;
3567 case KOBOLD:
3568 entity->z = 2.25;
3569 entity->focalx = limbs[KOBOLD][0][0]; // 0
3570 entity->focaly = limbs[KOBOLD][0][1]; // 0
3571 entity->focalz = limbs[KOBOLD][0][2]; // -2
3572 break;
3573 case SCARAB:
3574 entity->focalx = limbs[SCARAB][0][0]; // 0
3575 entity->focaly = limbs[SCARAB][0][1]; // 0
3576 entity->focalz = limbs[SCARAB][0][2]; // 0
3577 if ( !strncmp(map->name, "The Labyrinth", 13) )
3578 {
3579 if ( myStats )
3580 {
3581 myStats->DEX -= 4;
3582 myStats->LVL = 10;
3583 }
3584 }
3585 break;
3586 case CRYSTALGOLEM:
3587 entity->z = -1.5;
3588 entity->focalx = limbs[CRYSTALGOLEM][0][0]; // 1
3589 entity->focaly = limbs[CRYSTALGOLEM][0][1]; // 0
3590 entity->focalz = limbs[CRYSTALGOLEM][0][2]; // -2
3591 break;
3592 case INCUBUS:
3593 entity->z = -1;
3594 entity->focalx = limbs[INCUBUS][0][0]; // 0
3595 entity->focaly = limbs[INCUBUS][0][1]; // 0
3596 entity->focalz = limbs[INCUBUS][0][2]; // -1.5
3597 break;
3598 case VAMPIRE:
3599 entity->z = -1;
3600 entity->focalx = limbs[VAMPIRE][0][0]; // 0
3601 entity->focaly = limbs[VAMPIRE][0][1]; // 0
3602 entity->focalz = limbs[VAMPIRE][0][2]; // -1.5
3603 if ( !strncmp(map->name, "The Ruins", 9) )
3604 {
3605 if ( myStats )
3606 {
3607 strcpy(myStats->name, "young vampire");
3608 }
3609 }
3610 break;
3611 case SHADOW:
3612 entity->z = -1;
3613 entity->focalx = limbs[SHADOW][0][0]; // 0
3614 entity->focaly = limbs[SHADOW][0][1]; // 0
3615 entity->focalz = limbs[SHADOW][0][2]; // -1.75
3616 if ( !strncmp(map->name, "Underworld", 10) && currentlevel <= 7 && entity->monsterStoreType == 0 )
3617 {
3618 entity->monsterStoreType = 2;
3619 }
3620 break;
3621 case COCKATRICE:
3622 entity->z = -4.5;
3623 entity->focalx = limbs[COCKATRICE][0][0]; // 0
3624 entity->focaly = limbs[COCKATRICE][0][1]; // 0
3625 entity->focalz = limbs[COCKATRICE][0][2]; // -1.75
3626 break;
3627 case INSECTOID:
3628 entity->z = 0;
3629 entity->focalx = limbs[INSECTOID][0][0]; // 0
3630 entity->focaly = limbs[INSECTOID][0][1]; // 0
3631 entity->focalz = limbs[INSECTOID][0][2]; // -1.75
3632 if ( !strncmp(map->name, "The Labyrinth", 13) )
3633 {
3634 if ( myStats )
3635 {
3636 strcpy(myStats->name, "lesser insectoid");
3637 }
3638 }
3639 break;
3640 case GOATMAN:
3641 entity->z = 0;
3642 entity->focalx = limbs[GOATMAN][0][0]; // 0
3643 entity->focaly = limbs[GOATMAN][0][1]; // 0
3644 entity->focalz = limbs[GOATMAN][0][2]; // -1.75
3645 if ( strstr(map->name, "Hell") )
3646 {
3647 if ( myStats )
3648 {
3649 strcpy(myStats->name, "lesser goatman");
3650 }
3651 }
3652 break;
3653 case AUTOMATON:
3654 entity->z = -.5;
3655 entity->focalx = limbs[AUTOMATON][0][0]; // 0
3656 entity->focaly = limbs[AUTOMATON][0][1]; // 0
3657 entity->focalz = limbs[AUTOMATON][0][2]; // -1.5
3658 if ( entity->monsterStoreType == 1 )
3659 {
3660 if ( myStats )
3661 {
3662 strcpy(myStats->name, "damaged automaton");
3663 }
3664 }
3665 break;
3666 case LICH_ICE:
3667 entity->focalx = limbs[LICH_ICE][0][0]; // -0.75
3668 entity->focaly = limbs[LICH_ICE][0][1]; // 0
3669 entity->focalz = limbs[LICH_ICE][0][2]; // 0
3670 entity->z = -2;
3671 entity->yaw = PI;
3672 entity->sprite = 650;
3673 entity->skill[29] = 120;
3674 break;
3675 case LICH_FIRE:
3676 entity->focalx = limbs[LICH_FIRE][0][0]; // -0.75
3677 entity->focaly = limbs[LICH_FIRE][0][1]; // 0
3678 entity->focalz = limbs[LICH_FIRE][0][2]; // 0
3679 entity->z = -1.2;
3680 entity->yaw = PI;
3681 entity->sprite = 646;
3682 entity->skill[29] = 120;
3683 break;
3684 case SENTRYBOT:
3685 entity->z = 0;
3686 entity->focalx = limbs[SENTRYBOT][0][0]; // 0
3687 entity->focaly = limbs[SENTRYBOT][0][1]; // 0
3688 entity->focalz = limbs[SENTRYBOT][0][2]; // -1.75
3689 break;
3690 case SPELLBOT:
3691 entity->z = 0;
3692 entity->focalx = limbs[SENTRYBOT][0][0];
3693 entity->focaly = limbs[SENTRYBOT][0][1];
3694 entity->focalz = limbs[SENTRYBOT][0][2];
3695 break;
3696 case GYROBOT:
3697 entity->z = 5;
3698 entity->focalx = limbs[GYROBOT][0][0];
3699 entity->focaly = limbs[GYROBOT][0][1];
3700 entity->focalz = limbs[GYROBOT][0][2];
3701 break;
3702 case DUMMYBOT:
3703 entity->z = 0;
3704 entity->focalx = limbs[DUMMYBOT][0][0];
3705 entity->focaly = limbs[DUMMYBOT][0][1];
3706 entity->focalz = limbs[DUMMYBOT][0][2];
3707 break;
3708 default:
3709 break;
3710 }
3711 if ( multiplayer != CLIENT )
3712 {
3713 myStats->type = monsterType;
3714 if ( myStats->type == DEVIL )
3715 {
3716 childEntity = newEntity(72, 1, map->entities, nullptr);
3717 //printlog("Generated devil spawner. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
3718 childEntity->x = entity->x - 8;
3719 childEntity->y = entity->y - 8;
3720 childEntity->setUID(-3);
3721 entity_uids--;
3722 }
3723 }
3724 break;
3725 }
3726 // ladder:
3727 case 11:
3728 entity->sizex = 4;
3729 entity->sizey = 4;
3730 entity->x += 8;
3731 entity->y += 8;
3732 entity->z = 5.45;
3733 entity->flags[PASSABLE] = true;
3734 entity->behavior = &actLadder;
3735 entity->sprite = 161; // ladder
3736 break;
3737 // campfire:
3738 case 12:
3739 if ( darkmap )
3740 {
3741 list_RemoveNode(entity->mynode);
3742 entity = NULL;
3743 break;
3744 }
3745 entity->sizex = 3;
3746 entity->sizey = 3;
3747 entity->x += 8;
3748 entity->y += 8;
3749 entity->z = 6;
3750 entity->flags[BRIGHT] = true;
3751 entity->flags[PASSABLE] = true;
3752 entity->behavior = &actCampfire;
3753 entity->sprite = 162; // firepit
3754 break;
3755 //The Mystical Fountain of TODO:
3756 case 14:
3757 {
3758 entity->sizex = 4;
3759 entity->sizey = 4;
3760 entity->x += 8;
3761 entity->y += 8;
3762 entity->z = 6.5;
3763 entity->behavior = &actFountain;
3764 entity->sprite = 163; //Fountain
3765 entity->skill[0] = 1; //Fountain is full.
3766 //Randomly determine effect.
3767 int effect = rand() % 10; //3 possible effects.
3768 entity->skill[28] = 1; //TODO: This is just for testing purposes.
3769 switch (effect)
3770 {
3771 case 0:
3772 //10% chance
3773 entity->skill[1] = 3; //Will bless all equipment.
3774 if ( (rand() % 4) != 0 )
3775 {
3776 entity->skill[1] = 4; //Will bless only one piece of equipment.
3777 }
3778 break;
3779 case 1:
3780 case 2:
3781 //20% chance.
3782 entity->skill[1] = 0; //Will spawn a succubus.
3783 break;
3784 case 3:
3785 case 4:
3786 case 5:
3787 //30% chance.
3788 entity->skill[1] = 1; //Will raise nutrients.
3789 break;
3790 case 6:
3791 case 7:
3792 case 8:
3793 case 9:
3794 //40% chance.
3795 //Random potion effect.
3796 entity->skill[1] = 2;
3797 entity->skill[3] = rand() % 15; //Randomly choose from the number of potion effects there are.
3798 break;
3799 default:
3800 break; //Should never happen.
3801 }
3802 break;
3803 }
3804 //Sink.
3805 case 15:
3806 entity->sizex = 4;
3807 entity->sizey = 4;
3808 entity->x += 8;
3809 entity->y += 8;
3810 entity->z = 5;
3811 entity->behavior = &actSink;
3812 entity->sprite = 164;
3813 entity->skill[0] = 1 + rand() % 4; // number of uses
3814 switch ( rand() % 10 )
3815 {
3816 case 0:
3817 //10% chance.
3818 entity->skill[3] = 0; //Player will find a ring.
3819 break;
3820 case 1:
3821 //10% chance.
3822 entity->skill[3] = 1; //Will spawn a slime.
3823 break;
3824 case 2:
3825 case 3:
3826 case 4:
3827 case 5:
3828 case 6:
3829 case 7:
3830 //60% chance.
3831 entity->skill[3] = 2; //Will raise nutrition.
3832 break;
3833 case 8:
3834 case 9:
3835 //20% chance.
3836 entity->skill[3] = 3; //Player will lose 1 HP.
3837 break;
3838 default:
3839 break;
3840 }
3841 break;
3842 //Switch.
3843 case 17:
3844 entity->sizex = 1;
3845 entity->sizey = 1;
3846 entity->x += 8;
3847 entity->y += 8;
3848 entity->z = 7;
3849 entity->sprite = 184; // this is the switch base.
3850 entity->flags[PASSABLE] = true;
3851 childEntity = newEntity(186, 0, map->entities, nullptr); //Switch entity.
3852 childEntity->x = entity->x;
3853 childEntity->y = entity->y;
3854 TileEntityList.addEntity(*childEntity);
3855 //printlog("22 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
3856 childEntity->z = 8;
3857 childEntity->focalz = -4.5;
3858 childEntity->sizex = 1;
3859 childEntity->sizey = 1;
3860 childEntity->sprite = 185; // this is the switch handle.
3861 childEntity->roll = PI / 4; // "off" position
3862 childEntity->flags[PASSABLE] = true;
3863 childEntity->behavior = &actSwitch;
3864 entity->parent = childEntity->getUID();
3865 break;
3866 //Circuit.
3867 case 18:
3868 entity->sizex = 3;
3869 entity->sizey = 3;
3870 entity->x += 8;
3871 entity->y += 8;
3872 entity->z = 5;
3873 entity->behavior = &actCircuit;
3874 entity->flags[PASSABLE] = true;
3875 entity->flags[INVISIBLE] = true;
3876 entity->flags[NOUPDATE] = true;
3877 //entity->sprite = 164; //No sprite.
3878 entity->skill[28] = 1; //It's a depowered powerable.
3879 break;
3880 //North/South gate: //TODO: Adjust this. It's a copypaste of door.
3881 case 19:
3882 entity->x += 8;
3883 entity->y += 8;
3884 entity->yaw -= PI / 2.0;
3885 entity->sprite = 1;
3886 entity->flags[PASSABLE] = true;
3887 entity->behavior = &actDoorFrame;
3888
3889 //entity->skill[28] = 1; //It's a mechanism.
3890 childEntity = newEntity(186, 0, map->entities, nullptr); //Gate entity.
3891 childEntity->x = entity->x;
3892 childEntity->y = entity->y;
3893 TileEntityList.addEntity(*childEntity);
3894 //printlog("23 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
3895 childEntity->sizex = 8;
3896 childEntity->sizey = 1;
3897 childEntity->yaw -= PI / 2.0;
3898 childEntity->gateInverted = 0; // non-inverted
3899 childEntity->gateStatus = 0; // closed.
3900 childEntity->skill[28] = 1; //It's a mechanism.
3901 childEntity->behavior = &actGate;
3902 childEntity->skill[0] = 1; // signify behavior code of DOOR_DIR
3903
3904 // copy editor options from frame to gate itself.
3905 childEntity->gateDisableOpening = entity->gateDisableOpening;
3906
3907 childEntity = newEntity(1, 0, map->entities, nullptr); //Door frame entity.
3908 childEntity->flags[INVISIBLE] = true;
3909 childEntity->flags[BLOCKSIGHT] = true;
3910 childEntity->x = entity->x - 7;
3911 childEntity->y = entity->y;
3912 TileEntityList.addEntity(*childEntity);
3913 //printlog("24 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
3914 childEntity->sizex = 2;
3915 childEntity->sizey = 2;
3916 childEntity->behavior = &actDoorFrame;
3917
3918 childEntity = newEntity(1, 0, map->entities, nullptr); //Door frame entity.
3919 childEntity->flags[INVISIBLE] = true;
3920 childEntity->flags[BLOCKSIGHT] = true;
3921 childEntity->x = entity->x + 7;
3922 childEntity->y = entity->y;
3923 TileEntityList.addEntity(*childEntity);
3924 //printlog("25 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
3925 childEntity->sizex = 2;
3926 childEntity->sizey = 2;
3927 childEntity->behavior = &actDoorFrame;
3928 break;
3929 //East/west gate: //TODO: Adjust this. It's a copypaste of door.
3930 case 20:
3931 entity->x += 8;
3932 entity->y += 8;
3933 entity->sprite = 1;
3934 entity->flags[PASSABLE] = true;
3935 entity->behavior = &actDoorFrame;
3936
3937 childEntity = newEntity(186, 0, map->entities, nullptr); //Gate entity.
3938 childEntity->x = entity->x;
3939 childEntity->y = entity->y;
3940 TileEntityList.addEntity(*childEntity);
3941 //printlog("26 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
3942 childEntity->sizex = 1;
3943 childEntity->sizey = 8;
3944 childEntity->gateInverted = 0; // non-inverted
3945 childEntity->gateStatus = 0; // closed.
3946 childEntity->skill[28] = 1; //It's a mechanism.
3947 childEntity->behavior = &actGate;
3948 childEntity->skill[0] = 0; // signify behavior code of DOOR_DIR
3949
3950 // copy editor options from frame to gate itself.
3951 childEntity->gateDisableOpening = entity->gateDisableOpening;
3952
3953 childEntity = newEntity(1, 0, map->entities, nullptr); //Door frame entity.
3954 childEntity->flags[INVISIBLE] = true;
3955 childEntity->flags[BLOCKSIGHT] = true;
3956 childEntity->x = entity->x;
3957 childEntity->y = entity->y - 7;
3958 TileEntityList.addEntity(*childEntity);
3959 //printlog("27 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
3960 childEntity->sizex = 2;
3961 childEntity->sizey = 2;
3962 childEntity->behavior = &actDoorFrame;
3963
3964 childEntity = newEntity(1, 0, map->entities, nullptr); //Door frame entity.
3965 childEntity->flags[INVISIBLE] = true;
3966 childEntity->flags[BLOCKSIGHT] = true;
3967 childEntity->x = entity->x;
3968 childEntity->y = entity->y + 7;
3969 TileEntityList.addEntity(*childEntity);
3970 //printlog("28 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
3971 childEntity->sizex = 2;
3972 childEntity->sizey = 2;
3973 childEntity->behavior = &actDoorFrame;
3974 break;
3975 //Chest.
3976 case 21:
3977 {
3978 entity->sizex = 3;
3979 entity->sizey = 2;
3980 entity->x += 8;
3981 entity->y += 8;
3982 entity->z = 5.5;
3983 entity->yaw = entity->yaw * (PI / 2); //set to 0 by default in editor, can be set 0-3
3984 entity->behavior = &actChest;
3985 entity->sprite = 188;
3986 //entity->skill[9] = -1; //Set default chest as random category < 0
3987
3988 childEntity = newEntity(216, 0, map->entities, nullptr); //Chest lid entity.
3989 childEntity->parent = entity->getUID();
3990 entity->parent = childEntity->getUID();
3991 if ( entity->yaw == 0 ) //EAST FACING
3992 {
3993 childEntity->x = entity->x - 3;
3994 childEntity->y = entity->y;
3995 }
3996 else if ( entity->yaw == PI / 2 ) //SOUTH FACING
3997 {
3998 childEntity->x = entity->x;
3999 childEntity->y = entity->y - 3;
4000 }
4001 else if ( entity->yaw == PI ) //WEST FACING
4002 {
4003 childEntity->x = entity->x + 3;
4004 childEntity->y = entity->y;
4005 }
4006 else if (entity->yaw == 3 * PI/2 ) //NORTH FACING
4007 {
4008 childEntity->x = entity->x;
4009 childEntity->y = entity->y + 3;
4010 }
4011 else
4012 {
4013 childEntity->x = entity->x;
4014 childEntity->y = entity->y - 3;
4015 }
4016 TileEntityList.addEntity(*childEntity);
4017 //printlog("29 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
4018 childEntity->z = entity->z - 2.75;
4019 childEntity->focalx = 3;
4020 childEntity->focalz = -.75;
4021 childEntity->yaw = entity->yaw;
4022 childEntity->sizex = 2;
4023 childEntity->sizey = 2;
4024 childEntity->behavior = &actChestLid;
4025 childEntity->flags[PASSABLE] = true;
4026
4027 //Chest inventory.
4028 node_t* tempNode = list_AddNodeFirst(&entity->children);
4029 tempNode->element = NULL;
4030 tempNode->deconstructor = &emptyDeconstructor;
4031
4032 if ( !strcmp(map->name, "The Mystic Library") && !vampireQuestChest )
4033 {
4034 vampireQuestChest = entity;
4035 }
4036 break;
4037 }
4038 // liquid marker
4039 case 28:
4040 entity->sizex = 8;
4041 entity->sizey = 8;
4042 entity->x += 8;
4043 entity->y += 8;
4044 entity->behavior = &actLiquid;
4045 entity->flags[SPRITE] = true;
4046 entity->flags[INVISIBLE] = true;
4047 entity->skill[2] = -9; // so clients know it's a liquid
4048 break;
4049 // arrow trap
4050 case 32:
4051 entity->sizex = 2;
4052 entity->sizey = 2;
4053 entity->x += 8;
4054 entity->y += 8;
4055 entity->behavior = &actArrowTrap;
4056 entity->flags[SPRITE] = true;
4057 entity->flags[INVISIBLE] = true;
4058 entity->flags[PASSABLE] = true;
4059 entity->flags[NOUPDATE] = true;
4060 entity->skill[28] = 1; // is a mechanism
4061 entity->skill[1] = QUIVER_SILVER + rand() % 7; // random arrow type.
4062 if ( currentlevel <= 15 )
4063 {
4064 while ( entity->skill[1] == QUIVER_CRYSTAL || entity->skill[1] == QUIVER_PIERCE )
4065 {
4066 entity->skill[1] = QUIVER_SILVER + rand() % 7; // random arrow type.
4067 }
4068 }
4069 entity->skill[3] = 0; // refire type.
4070 break;
4071 // trap (pressure plate thingy)
4072 case 33:
4073 entity->sizex = 2;
4074 entity->sizey = 2;
4075 entity->x += 8;
4076 entity->y += 8;
4077 entity->behavior = &actTrap;
4078 entity->flags[SPRITE] = true;
4079 entity->flags[INVISIBLE] = true;
4080 entity->flags[PASSABLE] = true;
4081 entity->flags[NOUPDATE] = true;
4082 break;
4083 // trap permanent (pressure plate thingy) (remains on)
4084 case 34:
4085 entity->sizex = 2;
4086 entity->sizey = 2;
4087 entity->x += 8;
4088 entity->y += 8;
4089 entity->behavior = &actTrapPermanent;
4090 entity->flags[SPRITE] = true;
4091 entity->flags[INVISIBLE] = true;
4092 entity->flags[PASSABLE] = true;
4093 entity->flags[NOUPDATE] = true;
4094 break;
4095 // minotaur spawn trap
4096 case 37:
4097 entity->skill[28] = 1; // is a mechanism
4098 entity->sizex = 2;
4099 entity->sizey = 2;
4100 entity->x += 8;
4101 entity->y += 8;
4102 entity->behavior = &actMinotaurTrap;
4103 entity->flags[SPRITE] = true;
4104 entity->flags[INVISIBLE] = true;
4105 entity->flags[PASSABLE] = true;
4106 entity->flags[NOUPDATE] = true;
4107 break;
4108 // summon monster trap
4109 case 97:
4110 entity->skill[28] = 1; // is a mechanism
4111 if ( entity->skill[1] == 0 )
4112 {
4113 // not generated by editor, set monster qty to 1.
4114 entity->skill[1] = 1;
4115 }
4116 if ( entity->skill[2] == 0 )
4117 {
4118 // not generated by editor, set time between spawns to 1.
4119 entity->skill[2] = 1;
4120 }
4121 if ( entity->skill[3] == 0 )
4122 {
4123 // not generated by editor, amount spawns to 1.
4124 entity->skill[3] = 1;
4125 }
4126 if ( entity->skill[4] > 1 )
4127 {
4128 // catch invalid input by editor, require power flag
4129 entity->skill[4] = 0;
4130 }
4131 if ( entity->skill[5] > 100 )
4132 {
4133 // catch invalid input by editor, chance to fail set to 0
4134 entity->skill[5] = 0;
4135 }
4136 entity->sizex = 2;
4137 entity->sizey = 2;
4138 entity->x += 8;
4139 entity->y += 8;
4140 entity->behavior = &actSummonTrap;
4141 entity->flags[SPRITE] = true;
4142 entity->flags[INVISIBLE] = true;
4143 entity->flags[PASSABLE] = true;
4144 entity->flags[NOUPDATE] = true;
4145 break;
4146 // boulder trap
4147 case 38:
4148 {
4149 entity->sizex = 2;
4150 entity->sizey = 2;
4151 entity->x += 8;
4152 entity->y += 8;
4153 entity->behavior = &actBoulderTrap;
4154 entity->flags[SPRITE] = true;
4155 entity->flags[INVISIBLE] = true;
4156 entity->flags[PASSABLE] = true;
4157 entity->flags[NOUPDATE] = true;
4158 entity->skill[28] = 1; // is a mechanism
4159 for ( c = 0; c < 4; c++ )
4160 {
4161 switch ( c )
4162 {
4163 case 0:
4164 x = 16;
4165 y = 0;
4166 break;
4167 case 1:
4168 x = 0;
4169 y = 16;
4170 break;
4171 case 2:
4172 x = -16;
4173 y = 0;
4174 break;
4175 case 3:
4176 x = 0;
4177 y = -16;
4178 break;
4179 }
4180 x = ((int)(x + entity->x)) >> 4;
4181 y = ((int)(y + entity->y)) >> 4;
4182 if ( x >= 0 && y >= 0 && x < map->width && y < map->height )
4183 {
4184 if ( !map->tiles[OBSTACLELAYER + y * MAPLAYERS + x * MAPLAYERS * map->height] )
4185 {
4186 Entity* childEntity = newEntity(252, 1, map->entities, nullptr);
4187 childEntity->x = (x << 4) + 8;
4188 childEntity->y = (y << 4) + 8;
4189 //printlog("30 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
4190 childEntity->flags[PASSABLE] = true;
4191 if ( !map->tiles[(MAPLAYERS - 1) + y * MAPLAYERS + x * MAPLAYERS * map->height] )
4192 {
4193 childEntity->z = -26.99;
4194 }
4195 else
4196 {
4197 childEntity->z = -10.99;
4198 }
4199 TileEntityList.addEntity(*childEntity);
4200 entity->boulderTrapRocksToSpawn |= (1 << c); // add this location to spawn a boulder below the trapdoor model.
4201 }
4202 }
4203 }
4204 break;
4205 }
4206 // headstone
4207 case 39:
4208 entity->sizex = 4;
4209 entity->sizey = 4;
4210 entity->x += 8;
4211 entity->y += 8;
4212 entity->sprite = 224; // gravestone_faded.vox
4213 entity->z = 8 - models[entity->sprite]->sizez / 4;
4214 entity->behavior = &actHeadstone;
4215 entity->skill[28] = 1; // is a mechanism
4216 if ( !strcmp(map->name, "The Haunted Castle") )
4217 {
4218 entity->flags[INVISIBLE] = true;
4219 entity->flags[PASSABLE] = true;
4220 }
4221 break;
4222 // model tester
4223 case 40:
4224 entity->behavior = &actRotate;
4225 entity->sprite = 0;
4226 break;
4227 // lava
4228 case 41:
4229 entity->sizex = 8;
4230 entity->sizey = 8;
4231 entity->x += 8;
4232 entity->y += 8;
4233 entity->behavior = &actLiquid;
4234 entity->flags[USERFLAG1] = true; // this is lava
4235 entity->flags[SPRITE] = true;
4236 entity->flags[INVISIBLE] = true;
4237 entity->skill[2] = -9; // so clients know it's a liquid
4238 break;
4239 // ladder hole
4240 case 43:
4241 entity->x += 8;
4242 entity->y += 8;
4243 entity->sprite = 253;
4244 entity->flags[PASSABLE] = true;
4245 entity->behavior = &actLadderUp;
4246 x = entity->x / 16;
4247 y = entity->y / 16;
4248 if ( x >= 0 && y >= 0 && x < map->width && y < map->height )
4249 {
4250 if ( !map->tiles[(MAPLAYERS - 1) + y * MAPLAYERS + x * MAPLAYERS * map->height] )
4251 {
4252 entity->z = -21.49;
4253 }
4254 else
4255 {
4256 entity->z = -5.49;
4257 }
4258 }
4259 break;
4260 // boulder
4261 case 44:
4262 entity->x += 8;
4263 entity->y += 8;
4264 entity->sprite = 245;
4265 entity->sizex = 7;
4266 entity->sizey = 7;
4267 entity->behavior = &actBoulder;
4268 entity->skill[0] = 1; // BOULDER_STOPPED
4269 break;
4270 // portal
4271 case 45:
4272 entity->x += 8;
4273 entity->y += 8;
4274 entity->sprite = 254;
4275 entity->sizex = 4;
4276 entity->sizey = 4;
4277 entity->yaw = PI / 2;
4278 entity->behavior = &actPortal;
4279 if ( !strcmp(map->name, "Mages Guild") )
4280 {
4281 entity->skill[3] = 1; // not secret portal, just aesthetic.
4282 }
4283 entity->flags[PASSABLE] = true;
4284 entity->flags[BRIGHT] = true;
4285 break;
4286 // secret ladder:
4287 case 46:
4288 entity->sizex = 4;
4289 entity->sizey = 4;
4290 entity->x += 8;
4291 entity->y += 8;
4292 entity->z = 5.45;
4293 entity->flags[PASSABLE] = true;
4294 entity->behavior = &actLadder;
4295 entity->sprite = 161; // ladder
4296 entity->skill[3] = 1; // LADDER_SECRET = 1;
4297 break;
4298 // table
4299 case 59:
4300 {
4301 entity->sizex = 5;
4302 entity->sizey = 4;
4303 entity->x += 8;
4304 entity->y += 8;
4305 entity->z = 8;
4306 entity->focalz = -3;
4307 entity->sprite = 271;
4308 entity->behavior = &actFurniture;
4309 entity->flags[BURNABLE] = true;
4310 entity->furnitureType = FURNITURE_TABLE;
4311 if ( entity->furnitureDir != -1 )
4312 {
4313 entity->yaw = entity->furnitureDir * 45 * (PI / 180.f);
4314 if ( entity->furnitureDir == 0 || entity->furnitureDir == 4 )
4315 {
4316 entity->sizex = 5;
4317 entity->sizey = 4;
4318 }
4319 else if ( entity->furnitureDir == 2 || entity->furnitureDir == 6 )
4320 {
4321 entity->sizex = 4;
4322 entity->sizey = 5;
4323 }
4324 else
4325 {
4326 entity->sizex = 6;
4327 entity->sizey = 6;
4328 }
4329 }
4330 bool doItem = false;
4331 if ( entity->furnitureTableRandomItemChance == -1 )
4332 {
4333 if ( prng_get_uint() % 4 == 0 || !strcmp(map->name, "Start Map") )
4334 {
4335 doItem = true;
4336 }
4337 }
4338 else if ( entity->furnitureTableRandomItemChance > 1 )
4339 {
4340 if ( prng_get_uint() % 100 < entity->furnitureTableRandomItemChance )
4341 {
4342 doItem = true;
4343 }
4344 }
4345 if ( doItem )
4346 {
4347 // put an item on the table
4348 childEntity = newEntity(8, 1, map->entities, nullptr);
4349 setSpriteAttributes(childEntity, nullptr, nullptr);
4350 childEntity->x = entity->x - 8;
4351 childEntity->y = entity->y - 8;
4352 TileEntityList.addEntity(*childEntity);
4353 //printlog("31 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
4354 childEntity->z = 0;
4355 childEntity->itemNotMoving = 1; // so the item retains its position
4356 childEntity->itemNotMovingClient = 1; // so the item retains its position for clients
4357 entity->parent = childEntity->getUID();
4358 }
4359
4360 bool doChairs = false;
4361 int numChairs = 0;
4362 if ( entity->furnitureTableSpawnChairs == -1 )
4363 {
4364 if ( prng_get_uint() % 2 == 0 )
4365 {
4366 doChairs = true;
4367 }
4368 }
4369 else if ( entity->furnitureTableSpawnChairs > 0 )
4370 {
4371 doChairs = true;
4372 }
4373 if ( doChairs )
4374 {
4375 // surround the table with chairs
4376 int c;
4377 if ( entity->furnitureTableSpawnChairs == -1 )
4378 {
4379 numChairs = prng_get_uint() % 4 + 1;
4380 }
4381 else
4382 {
4383 numChairs = entity->furnitureTableSpawnChairs;
4384 }
4385 for ( c = 0; c < numChairs; c++ )
4386 {
4387 childEntity = newEntity(60, 1, map->entities, nullptr);
4388 setSpriteAttributes(childEntity, nullptr, nullptr);
4389 childEntity->x = entity->x - 8;
4390 childEntity->y = entity->y - 8;
4391 //printlog("32 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
4392 childEntity->yaw = ((int)(prng_get_uint() % 80) - 40 + c * 90) * (PI / 180.f);
4393 if ( childEntity->yaw >= PI * 2 )
4394 {
4395 childEntity->yaw -= PI * 2;
4396 }
4397 else if ( childEntity->yaw < 0 )
4398 {
4399 childEntity->yaw += PI * 2;
4400 }
4401 childEntity->x -= cos(childEntity->yaw) * 7;
4402 childEntity->y -= sin(childEntity->yaw) * 7;
4403 TileEntityList.addEntity(*childEntity);
4404 }
4405 }
4406 break;
4407 }
4408 // chair
4409 case 60:
4410 entity->furnitureType = FURNITURE_CHAIR; // so everything knows I'm a chair
4411 entity->sizex = 2;
4412 entity->sizey = 2;
4413 entity->x += 8;
4414 entity->y += 8;
4415 entity->z = 8;
4416 entity->focalz = -5;
4417 entity->sprite = 272;
4418 entity->behavior = &actFurniture;
4419 entity->flags[BURNABLE] = true;
4420 if ( entity->furnitureDir == -1 )
4421 {
4422 if ( !entity->yaw )
4423 {
4424 entity->yaw = (prng_get_uint() % 360) * (PI / 180.f);
4425 }
4426 }
4427 else
4428 {
4429 entity->yaw = entity->furnitureDir * 45 * (PI / 180.f);
4430 }
4431 break;
4432 // MC easter egg:
4433 case 61:
4434 entity->sizex = 2;
4435 entity->sizey = 2;
4436 entity->x += 8;
4437 entity->y += 8;
4438 entity->z = 4;
4439 entity->sprite = 273;
4440 entity->behavior = &actMCaxe;
4441 entity->flags[PASSABLE] = true;
4442 break;
4443 // end game portal:
4444 case 63:
4445 entity->x += 8;
4446 entity->y += 8;
4447 entity->sprite = 278;
4448 entity->sizex = 4;
4449 entity->sizey = 4;
4450 entity->yaw = PI / 2;
4451 entity->behavior = &actWinningPortal;
4452 entity->flags[PASSABLE] = true;
4453 entity->flags[BRIGHT] = true;
4454 if ( strstr(map->name, "Boss") )
4455 {
4456 entity->flags[INVISIBLE] = true;
4457 entity->skill[28] = 1; // is a mechanism
4458 }
4459 if ( strstr(map->name, "Hell") )
4460 {
4461 entity->skill[4] = 2;
4462 entity->flags[INVISIBLE] = true;
4463 entity->skill[28] = 1; // is a mechanism
4464 }
4465 else
4466 {
4467 entity->skill[4] = 1;
4468 }
4469 break;
4470 // speartrap:
4471 case 64:
4472 entity->sizex = 6;
4473 entity->sizey = 6;
4474 entity->x += 8;
4475 entity->y += 8;
4476 entity->z = 16;
4477 entity->focalz = 7;
4478 entity->sprite = 282;
4479 entity->behavior = &actSpearTrap;
4480 entity->skill[28] = 1; // is a mechanism
4481 entity->flags[PASSABLE] = true;
4482 childEntity = newEntity(283, 0, map->entities, nullptr);
4483 childEntity->x = entity->x;
4484 childEntity->y = entity->y;
4485 TileEntityList.addEntity(*childEntity);
4486 //printlog("33 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
4487 childEntity->z = entity->z - 7.75 - 0.01;
4488 childEntity->flags[PASSABLE] = true;
4489 break;
4490 // magic trap:
4491 case 65:
4492 entity->sizex = 2;
4493 entity->sizey = 2;
4494 entity->x += 8;
4495 entity->y += 8;
4496 entity->behavior = &actMagicTrap;
4497 entity->flags[SPRITE] = true;
4498 entity->flags[INVISIBLE] = true;
4499 entity->flags[PASSABLE] = true;
4500 entity->skill[28] = 1; // is a mechanism
4501 break;
4502 // wall buster:
4503 case 66:
4504 entity->sizex = 2;
4505 entity->sizey = 2;
4506 entity->x += 8;
4507 entity->y += 8;
4508 entity->behavior = &actWallBuster;
4509 entity->flags[SPRITE] = true;
4510 entity->flags[INVISIBLE] = true;
4511 entity->flags[PASSABLE] = true;
4512 entity->flags[NOUPDATE] = true;
4513 entity->skill[28] = 1; // is a mechanism
4514 break;
4515 // wall builder:
4516 case 67:
4517 entity->sizex = 2;
4518 entity->sizey = 2;
4519 entity->x += 8;
4520 entity->y += 8;
4521 entity->behavior = &actWallBuilder;
4522 entity->flags[SPRITE] = true;
4523 entity->flags[INVISIBLE] = true;
4524 entity->flags[PASSABLE] = true;
4525 entity->flags[NOUPDATE] = true;
4526 entity->skill[28] = 1; // is a mechanism
4527 break;
4528 // devil/other teleport location:
4529 case 72:
4530 case 73:
4531 case 74:
4532 case 128:
4533 entity->sizex = 2;
4534 entity->sizey = 2;
4535 entity->x += 8;
4536 entity->y += 8;
4537 entity->behavior = &actDevilTeleport;
4538 entity->flags[SPRITE] = true;
4539 entity->flags[INVISIBLE] = true;
4540 entity->flags[PASSABLE] = true;
4541 entity->flags[NOUPDATE] = true;
4542 break;
4543
4544 // east crystal shard:
4545 case 98:
4546 {
4547 if ( darkmap )
4548 {
4549 list_RemoveNode(entity->mynode);
4550 entity = NULL;
4551 break;
4552 }
4553 entity->behavior = &actCrystalShard;
4554 entity->x += 1;
4555 entity->y += 8;
4556 entity->z -= 1;
4557 entity->sprite = 587;
4558 entity->flags[PASSABLE] = true;
4559 entity->flags[BRIGHT] = true;
4560 break;
4561 }
4562 // south crystal shard:
4563 case 99:
4564 {
4565 if ( darkmap )
4566 {
4567 list_RemoveNode(entity->mynode);
4568 entity = NULL;
4569 break;
4570 }
4571 entity->behavior = &actCrystalShard;
4572 entity->x += 8;
4573 entity->y += 1;
4574 entity->z -= 1;
4575 entity->yaw += PI / 2.0;
4576 entity->sprite = 587;
4577 entity->flags[PASSABLE] = true;
4578 entity->flags[BRIGHT] = true;
4579 break;
4580 }
4581 // west crystal shard:
4582 case 100:
4583 {
4584 if ( darkmap )
4585 {
4586 list_RemoveNode(entity->mynode);
4587 entity = NULL;
4588 break;
4589 }
4590 entity->behavior = &actCrystalShard;
4591 entity->x += 15;
4592 entity->y += 8;
4593 entity->z -= 1;
4594 entity->yaw += PI;
4595 entity->sprite = 587;
4596 entity->flags[PASSABLE] = true;
4597 entity->flags[BRIGHT] = true;
4598 break;
4599 }
4600 // north crystal shard:
4601 case 101:
4602 {
4603 if ( darkmap )
4604 {
4605 list_RemoveNode(entity->mynode);
4606 entity = NULL;
4607 break;
4608 }
4609 entity->behavior = &actCrystalShard;
4610 entity->x += 8;
4611 entity->y += 15;
4612 entity->z -= 1;
4613 entity->yaw += 3 * PI / 2.0;
4614 entity->sprite = 587;
4615 entity->flags[PASSABLE] = true;
4616 entity->flags[BRIGHT] = true;
4617 break;
4618 }
4619
4620 // boulder trap east
4621 case 102:
4622 {
4623 entity->sizex = 2;
4624 entity->sizey = 2;
4625 entity->x += 8;
4626 entity->y += 8;
4627 entity->behavior = &actBoulderTrapEast;
4628 entity->flags[SPRITE] = true;
4629 entity->flags[INVISIBLE] = true;
4630 entity->flags[PASSABLE] = true;
4631 entity->flags[NOUPDATE] = true;
4632 entity->skill[28] = 1; // is a mechanism
4633 entity->boulderTrapPreDelay = entity->boulderTrapPreDelay * TICKS_PER_SECOND; // convert seconds to ticks from editor
4634
4635 x = ((int)(entity->x)) >> 4;
4636 y = ((int)(entity->y)) >> 4;
4637 if ( x >= 0 && y >= 0 && x < map->width && y < map->height )
4638 {
4639 if ( !map->tiles[OBSTACLELAYER + y * MAPLAYERS + x * MAPLAYERS * map->height] )
4640 {
4641 Entity* childEntity = newEntity(252, 1, map->entities, nullptr);
4642 childEntity->x = (x << 4) + 8;
4643 childEntity->y = (y << 4) + 8;
4644 //printlog("30 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
4645 childEntity->flags[PASSABLE] = true;
4646 if ( !map->tiles[(MAPLAYERS - 1) + y * MAPLAYERS + x * MAPLAYERS * map->height] )
4647 {
4648 childEntity->z = -26.99;
4649 }
4650 else
4651 {
4652 childEntity->z = -10.99;
4653 }
4654 TileEntityList.addEntity(*childEntity);
4655 }
4656 }
4657 break;
4658 }
4659
4660 // boulder trap south
4661 case 103:
4662 {
4663 entity->sizex = 2;
4664 entity->sizey = 2;
4665 entity->x += 8;
4666 entity->y += 8;
4667 entity->behavior = &actBoulderTrapSouth;
4668 entity->flags[SPRITE] = true;
4669 entity->flags[INVISIBLE] = true;
4670 entity->flags[PASSABLE] = true;
4671 entity->flags[NOUPDATE] = true;
4672 entity->skill[28] = 1; // is a mechanism
4673 entity->boulderTrapPreDelay = entity->boulderTrapPreDelay * TICKS_PER_SECOND; // convert seconds to ticks from editor
4674
4675 x = ((int)(entity->x)) >> 4;
4676 y = ((int)(entity->y)) >> 4;
4677 if ( x >= 0 && y >= 0 && x < map->width && y < map->height )
4678 {
4679 if ( !map->tiles[OBSTACLELAYER + y * MAPLAYERS + x * MAPLAYERS * map->height] )
4680 {
4681 Entity* childEntity = newEntity(252, 1, map->entities, nullptr);
4682 childEntity->x = (x << 4) + 8;
4683 childEntity->y = (y << 4) + 8;
4684 //printlog("30 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
4685 childEntity->flags[PASSABLE] = true;
4686 if ( !map->tiles[(MAPLAYERS - 1) + y * MAPLAYERS + x * MAPLAYERS * map->height] )
4687 {
4688 childEntity->z = -26.99;
4689 }
4690 else
4691 {
4692 childEntity->z = -10.99;
4693 }
4694 TileEntityList.addEntity(*childEntity);
4695 }
4696 }
4697 break;
4698 }
4699
4700 // boulder trap west
4701 case 104:
4702 {
4703 entity->sizex = 2;
4704 entity->sizey = 2;
4705 entity->x += 8;
4706 entity->y += 8;
4707 entity->behavior = &actBoulderTrapWest;
4708 entity->flags[SPRITE] = true;
4709 entity->flags[INVISIBLE] = true;
4710 entity->flags[PASSABLE] = true;
4711 entity->flags[NOUPDATE] = true;
4712 entity->skill[28] = 1; // is a mechanism
4713 entity->boulderTrapPreDelay = entity->boulderTrapPreDelay * TICKS_PER_SECOND; // convert seconds to ticks from editor
4714
4715 x = ((int)(entity->x)) >> 4;
4716 y = ((int)(entity->y)) >> 4;
4717 if ( x >= 0 && y >= 0 && x < map->width && y < map->height )
4718 {
4719 if ( !map->tiles[OBSTACLELAYER + y * MAPLAYERS + x * MAPLAYERS * map->height] )
4720 {
4721 Entity* childEntity = newEntity(252, 1, map->entities, nullptr);
4722 childEntity->x = (x << 4) + 8;
4723 childEntity->y = (y << 4) + 8;
4724 //printlog("30 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
4725 childEntity->flags[PASSABLE] = true;
4726 if ( !map->tiles[(MAPLAYERS - 1) + y * MAPLAYERS + x * MAPLAYERS * map->height] )
4727 {
4728 childEntity->z = -26.99;
4729 }
4730 else
4731 {
4732 childEntity->z = -10.99;
4733 }
4734 TileEntityList.addEntity(*childEntity);
4735 }
4736 }
4737 break;
4738 }
4739
4740 // boulder trap north
4741 case 105:
4742 {
4743 entity->sizex = 2;
4744 entity->sizey = 2;
4745 entity->x += 8;
4746 entity->y += 8;
4747 entity->behavior = &actBoulderTrapNorth;
4748 entity->flags[SPRITE] = true;
4749 entity->flags[INVISIBLE] = true;
4750 entity->flags[PASSABLE] = true;
4751 entity->flags[NOUPDATE] = true;
4752 entity->skill[28] = 1; // is a mechanism
4753 entity->boulderTrapPreDelay = entity->boulderTrapPreDelay * TICKS_PER_SECOND; // convert seconds to ticks from editor
4754
4755 x = ((int)(entity->x)) >> 4;
4756 y = ((int)(entity->y)) >> 4;
4757 if ( x >= 0 && y >= 0 && x < map->width && y < map->height )
4758 {
4759 if ( !map->tiles[OBSTACLELAYER + y * MAPLAYERS + x * MAPLAYERS * map->height] )
4760 {
4761 Entity* childEntity = newEntity(252, 1, map->entities, nullptr);
4762 childEntity->x = (x << 4) + 8;
4763 childEntity->y = (y << 4) + 8;
4764 //printlog("30 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
4765 childEntity->flags[PASSABLE] = true;
4766 if ( !map->tiles[(MAPLAYERS - 1) + y * MAPLAYERS + x * MAPLAYERS * map->height] )
4767 {
4768 childEntity->z = -26.99;
4769 }
4770 else
4771 {
4772 childEntity->z = -10.99;
4773 }
4774 TileEntityList.addEntity(*childEntity);
4775 }
4776 }
4777 break;
4778 }
4779
4780 // power crystal
4781 case 106:
4782 {
4783 entity->sizex = 4;
4784 entity->sizey = 4;
4785 entity->x += 8;
4786 entity->y += 8;
4787 entity->z = 6.5;
4788 entity->behavior = &actPowerCrystalBase;
4789 entity->sprite = 577; //crystal base
4790 entity->yaw = entity->yaw * (PI / 2); // rotate as set in editor
4791 entity->flags[PASSABLE] = false;
4792
4793 childEntity = newEntity(578, 0, map->entities, nullptr); //floating crystal
4794 childEntity->parent = entity->getUID();
4795
4796 childEntity->x = entity->x;
4797 childEntity->y = entity->y;
4798 childEntity->sizex = 4;
4799 childEntity->sizey = 4;
4800 childEntity->crystalStartZ = entity->z - 10; //start position
4801 childEntity->z = childEntity->crystalStartZ - 0.4 + ((prng_get_uint() % 8) * 0.1); // start the height randomly
4802 childEntity->crystalMaxZVelocity = 0.02; //max velocity
4803 childEntity->crystalMinZVelocity = 0.001; //min velocity
4804 childEntity->crystalTurnVelocity = 0.2; //yaw turning velocity
4805 childEntity->vel_z = childEntity->crystalMaxZVelocity * ((prng_get_uint() % 99) * 0.01 + 0.01); // start the velocity randomly
4806
4807 childEntity->crystalNumElectricityNodes = entity->crystalNumElectricityNodes; //number of electricity nodes to generate in facing direction.
4808 childEntity->crystalTurnReverse = entity->crystalTurnReverse;
4809 childEntity->crystalSpellToActivate = entity->crystalSpellToActivate;
4810 if ( childEntity->crystalSpellToActivate )
4811 {
4812 childEntity->z = childEntity->crystalStartZ + 5;
4813 childEntity->vel_z = childEntity->crystalMaxZVelocity * 2;
4814 }
4815 childEntity->yaw = entity->yaw;
4816 childEntity->sizex = 4;
4817 childEntity->sizey = 4;
4818 childEntity->behavior = &actPowerCrystal;
4819 childEntity->flags[PASSABLE] = true;
4820 TileEntityList.addEntity(*childEntity);
4821
4822 node_t* tempNode = list_AddNodeLast(&entity->children);
4823 tempNode->element = childEntity; // add the node to the children list.
4824 tempNode->deconstructor = &emptyDeconstructor;
4825 tempNode->size = sizeof(Entity*);
4826
4827 break;
4828 }
4829 // set beartrap
4830 case 107:
4831 {
4832 entity->skill[0] = 0;
4833 entity->sizex = 4;
4834 entity->sizey = 4;
4835 entity->x += 8;
4836 entity->y += 8;
4837 entity->z = 6.75;
4838
4839 //entity->focalz = -5;
4840 entity->sprite = 668;
4841
4842 entity->behavior = &actBeartrap;
4843 entity->flags[PASSABLE] = true;
4844 entity->flags[UPDATENEEDED] = true;
4845 if ( !entity->yaw )
4846 {
4847 entity->yaw = (prng_get_uint() % 360) * (PI / 180.f);
4848 }
4849 entity->roll = -PI / 2; // flip the model
4850
4851 entity->skill[11] = DECREPIT; //status
4852 entity->skill[12] = 0; //beatitude
4853 entity->skill[13] = 1; //qty
4854 entity->skill[14] = 0; //appearance
4855 entity->skill[15] = 0; //identified
4856 break;
4857 }
4858 case 108: //stalag column
4859 entity->x += 8;
4860 entity->y += 8;
4861 entity->sprite = 580;
4862 entity->sizex = 8;
4863 entity->sizey = 8;
4864 entity->z = -7.75;
4865 entity->flags[BLOCKSIGHT] = false;
4866 entity->behavior = &actStalagColumn;
4867 break;
4868 case 109: //stalagmite single
4869 entity->x += 8;
4870 entity->y += 8;
4871 entity->sprite = 581;
4872 entity->sizex = 4;
4873 entity->sizey = 4;
4874 entity->z = 1.75;
4875 entity->flags[BLOCKSIGHT] = false;
4876 entity->behavior = &actStalagFloor;
4877 break;
4878 case 110: //stalagmite multiple
4879 entity->x += 8;
4880 entity->y += 8;
4881 entity->sprite = 582;
4882 entity->sizex = 7;
4883 entity->sizey = 7;
4884 entity->z = -1;
4885 entity->flags[BLOCKSIGHT] = false;
4886 entity->behavior = &actStalagFloor;
4887 break;
4888 case 111: //stalagtite single
4889 entity->x += 8;
4890 entity->y += 8;
4891 entity->sprite = 583;
4892 entity->sizex = 4;
4893 entity->sizey = 4;
4894 entity->z = -1.75;
4895 x = entity->x / 16;
4896 y = entity->y / 16;
4897 entity->flags[BLOCKSIGHT] = false;
4898 entity->behavior = &actStalagCeiling;
4899 if ( x >= 0 && y >= 0 && x < map->width && y < map->height )
4900 {
4901 if ( !map->tiles[(MAPLAYERS - 1) + y * MAPLAYERS + x * MAPLAYERS * map->height] )
4902 {
4903 entity->flags[PASSABLE] = true;
4904 entity->z -= 16;
4905 }
4906 }
4907 break;
4908 case 112: //stalagtite multiple
4909 entity->x += 8;
4910 entity->y += 8;
4911 entity->sprite = 584;
4912 entity->sizex = 7;
4913 entity->sizey = 7;
4914 entity->z = 1;
4915 x = entity->x / 16;
4916 y = entity->y / 16;
4917 entity->flags[BLOCKSIGHT] = false;
4918 entity->behavior = &actStalagCeiling;
4919 if ( x >= 0 && y >= 0 && x < map->width && y < map->height )
4920 {
4921 if ( !map->tiles[(MAPLAYERS - 1) + y * MAPLAYERS + x * MAPLAYERS * map->height] )
4922 {
4923 entity->flags[PASSABLE] = true;
4924 entity->z -= 16;
4925 }
4926 }
4927 break;
4928 //North/South gate inverted: //TODO: Adjust this. It's a copypaste of door.
4929 case 113:
4930 entity->x += 8;
4931 entity->y += 8;
4932 entity->yaw -= PI / 2.0;
4933 entity->sprite = 1;
4934 entity->flags[PASSABLE] = true;
4935 entity->behavior = &actDoorFrame;
4936
4937 //entity->skill[28] = 1; //It's a mechanism.
4938 childEntity = newEntity(186, 0, map->entities, nullptr);
4939 childEntity->x = entity->x;
4940 childEntity->y = entity->y;
4941 TileEntityList.addEntity(*childEntity);
4942 //printlog("23 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
4943 childEntity->sizex = 8;
4944 childEntity->sizey = 1;
4945 childEntity->yaw -= PI / 2.0;
4946 childEntity->gateInverted = 1; // inverted.
4947 childEntity->gateStatus = 1; // open.
4948 childEntity->skill[28] = 1; //It's a mechanism.
4949 childEntity->behavior = &actGate;
4950 childEntity->skill[0] = 1; // signify behavior code of DOOR_DIR
4951
4952 // copy editor options from frame to gate itself.
4953 childEntity->gateDisableOpening = entity->gateDisableOpening;
4954
4955 childEntity = newEntity(1, 0, map->entities, nullptr);
4956 childEntity->flags[INVISIBLE] = true;
4957 childEntity->flags[BLOCKSIGHT] = true;
4958 childEntity->x = entity->x - 7;
4959 childEntity->y = entity->y;
4960 TileEntityList.addEntity(*childEntity);
4961 //printlog("24 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
4962 childEntity->sizex = 2;
4963 childEntity->sizey = 2;
4964 childEntity->behavior = &actDoorFrame;
4965
4966 childEntity = newEntity(1, 0, map->entities, nullptr);
4967 childEntity->flags[INVISIBLE] = true;
4968 childEntity->flags[BLOCKSIGHT] = true;
4969 childEntity->x = entity->x + 7;
4970 childEntity->y = entity->y;
4971 TileEntityList.addEntity(*childEntity);
4972 //printlog("25 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
4973 childEntity->sizex = 2;
4974 childEntity->sizey = 2;
4975 childEntity->behavior = &actDoorFrame;
4976 break;
4977 //East/west gate inverted: //TODO: Adjust this. It's a copypaste of door.
4978 case 114:
4979 entity->x += 8;
4980 entity->y += 8;
4981 entity->sprite = 1;
4982 entity->flags[PASSABLE] = true;
4983 entity->behavior = &actDoorFrame;
4984
4985 childEntity = newEntity(186, 0, map->entities, nullptr);
4986 childEntity->x = entity->x;
4987 childEntity->y = entity->y;
4988 TileEntityList.addEntity(*childEntity);
4989 //printlog("26 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
4990 childEntity->sizex = 1;
4991 childEntity->gateInverted = 1; // inverted.
4992 childEntity->gateStatus = 1; // open.
4993 childEntity->sizey = 8;
4994 childEntity->skill[28] = 1; //It's a mechanism.
4995 childEntity->behavior = &actGate;
4996 childEntity->skill[0] = 0; // signify behavior code of DOOR_DIR
4997
4998 // copy editor options from frame to gate itself.
4999 childEntity->gateDisableOpening = entity->gateDisableOpening;
5000
5001 childEntity = newEntity(1, 0, map->entities, nullptr);
5002 childEntity->flags[INVISIBLE] = true;
5003 childEntity->flags[BLOCKSIGHT] = true;
5004 childEntity->x = entity->x;
5005 childEntity->y = entity->y - 7;
5006 TileEntityList.addEntity(*childEntity);
5007 //printlog("27 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
5008 childEntity->sizex = 2;
5009 childEntity->sizey = 2;
5010 childEntity->behavior = &actDoorFrame;
5011
5012 childEntity = newEntity(1, 0, map->entities, nullptr);
5013 childEntity->flags[INVISIBLE] = true;
5014 childEntity->flags[BLOCKSIGHT] = true;
5015 childEntity->x = entity->x;
5016 childEntity->y = entity->y + 7;
5017 TileEntityList.addEntity(*childEntity);
5018 //printlog("28 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
5019 childEntity->sizex = 2;
5020 childEntity->sizey = 2;
5021 childEntity->behavior = &actDoorFrame;
5022 break;
5023 //Switch with timer.
5024 case 115:
5025 entity->sizex = 1;
5026 entity->sizey = 1;
5027 entity->x += 8;
5028 entity->y += 8;
5029 entity->z = 7;
5030 entity->sprite = 585; // this is the switch base.
5031 entity->flags[PASSABLE] = true;
5032 childEntity = newEntity(586, 0, map->entities, nullptr);
5033 childEntity->x = entity->x;
5034 childEntity->y = entity->y;
5035 TileEntityList.addEntity(*childEntity);
5036 //printlog("22 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
5037 childEntity->z = 8;
5038 childEntity->leverTimerTicks = std::max(entity->leverTimerTicks, 1) * TICKS_PER_SECOND; // convert seconds to ticks from editor, make sure not less than 1
5039 childEntity->leverStatus = 0; // set default to off.
5040 childEntity->focalz = -4.5;
5041 childEntity->sizex = 1;
5042 childEntity->sizey = 1;
5043 childEntity->sprite = 586; // this is the switch handle.
5044 childEntity->roll = -PI / 4; // "off" position
5045 childEntity->flags[PASSABLE] = true;
5046 childEntity->behavior = &actSwitchWithTimer;
5047 break;
5048 // pedestal
5049 case 116:
5050 {
5051 entity->sizex = 4;
5052 entity->sizey = 4;
5053 entity->x += 8;
5054 entity->y += 8;
5055 entity->z = 4.5;
5056 entity->behavior = &actPedestalBase;
5057 entity->sprite = 601; //pedestal base
5058 entity->flags[PASSABLE] = false;
5059 entity->pedestalOrbType = entity->pedestalOrbType + 1;// set in editor as 0-3, need 1-4.
5060 if ( entity->pedestalHasOrb == 1 ) // set in editor
5061 {
5062 entity->pedestalHasOrb = entity->pedestalOrbType;
5063 }
5064 //entity->pedestalInvertedPower // set in editor
5065 entity->pedestalInit = 0;
5066 //entity->pedestalInGround = 0; // set in editor
5067 //entity->pedestalLockOrb // set in editor
5068 if ( entity->pedestalInGround )
5069 {
5070 entity->z += 11;
5071 entity->flags[PASSABLE] = true;
5072 }
5073
5074 childEntity = newEntity(602 + entity->pedestalOrbType - 1, 0, map->entities, nullptr); //floating orb
5075 childEntity->parent = entity->getUID();
5076 childEntity->behavior = &actPedestalOrb;
5077 childEntity->x = entity->x;
5078 childEntity->y = entity->y;
5079 TileEntityList.addEntity(*childEntity);
5080 childEntity->z = -2;
5081 childEntity->sizex = 2;
5082 childEntity->sizey = 2;
5083 childEntity->flags[UNCLICKABLE] = true;
5084 childEntity->flags[PASSABLE] = true;
5085 childEntity->flags[INVISIBLE] = false;
5086 if ( entity->pedestalInGround )
5087 {
5088 childEntity->z += 11;
5089 childEntity->orbStartZ = -2;
5090 }
5091 childEntity->pedestalOrbInit();
5092
5093 node_t* tempNode = list_AddNodeLast(&entity->children);
5094 tempNode->element = childEntity; // add the node to the children list.
5095 tempNode->deconstructor = &emptyDeconstructor;
5096 tempNode->size = sizeof(Entity*);
5097 break;
5098 }
5099 // mid game portal:
5100 case 117:
5101 entity->x += 8;
5102 entity->y += 8;
5103 entity->sprite = 614;
5104 entity->sizex = 4;
5105 entity->sizey = 4;
5106 entity->yaw = PI / 2;
5107 entity->behavior = &actMidGamePortal;
5108 entity->flags[PASSABLE] = true;
5109 entity->flags[BRIGHT] = true;
5110 if ( strstr(map->name, "Boss") )
5111 {
5112 entity->flags[INVISIBLE] = true;
5113 entity->skill[28] = 1; // is a mechanism
5114 }
5115 /*if ( strstr(map->name, "Hell") )
5116 {
5117 entity->skill[4] = 2;
5118 }
5119 else
5120 {
5121 entity->skill[4] = 1;
5122 }*/
5123 break;
5124 // teleporter.
5125 case 118:
5126 entity->x += 8;
5127 entity->y += 8;
5128 entity->flags[PASSABLE] = true;
5129 if ( entity->teleporterType == 0 )
5130 {
5131 entity->sprite = 618; // ladder hole
5132 entity->behavior = &actTeleporter;
5133 x = entity->x / 16;
5134 y = entity->y / 16;
5135 if ( x >= 0 && y >= 0 && x < map->width && y < map->height )
5136 {
5137 if ( !map->tiles[(MAPLAYERS - 1) + y * MAPLAYERS + x * MAPLAYERS * map->height] )
5138 {
5139 entity->z = -21.49;
5140 }
5141 else
5142 {
5143 entity->z = -5.49;
5144 }
5145 }
5146 }
5147 else if ( entity->teleporterType == 1 )
5148 {
5149 entity->sizex = 4;
5150 entity->sizey = 4;
5151 entity->z = 5.45;
5152 entity->flags[PASSABLE] = true;
5153 entity->behavior = &actTeleporter;
5154 entity->sprite = 619; // ladder
5155 }
5156 else
5157 {
5158 entity->sprite = 254;
5159 entity->sizex = 4;
5160 entity->sizey = 4;
5161 entity->yaw = PI / 2;
5162 entity->behavior = &actTeleporter;
5163 entity->flags[PASSABLE] = true;
5164 entity->flags[BRIGHT] = true;
5165 }
5166 break;
5167 // ceiling tile:
5168 case 119:
5169 entity->x += 8;
5170 entity->y += 8;
5171 entity->z = -24;
5172 if ( entity->ceilingTileModel != 0 )
5173 {
5174 entity->sprite = entity->ceilingTileModel;
5175 }
5176 else
5177 {
5178 entity->sprite = 621;
5179 }
5180 entity->sizex = 8;
5181 entity->sizey = 8;
5182 //entity->yaw = PI / 2;
5183 entity->behavior = &actCeilingTile;
5184 entity->flags[PASSABLE] = true;
5185 entity->flags[BLOCKSIGHT] = false;
5186 //entity->flags[BRIGHT] = true;
5187 break;
5188 // spell trap ceiling
5189 case 120:
5190 {
5191 entity->sizex = 2;
5192 entity->sizey = 2;
5193 entity->x += 8;
5194 entity->y += 8;
5195 entity->behavior = &actMagicTrapCeiling;
5196 entity->flags[SPRITE] = true;
5197 entity->flags[INVISIBLE] = true;
5198 entity->flags[PASSABLE] = true;
5199 entity->flags[NOUPDATE] = true;
5200 entity->skill[28] = 1; // is a mechanism
5201 entity->spellTrapRefireRate = entity->spellTrapRefireRate * TICKS_PER_SECOND; // convert seconds to ticks from editor
5202
5203 x = ((int)(entity->x)) >> 4;
5204 y = ((int)(entity->y)) >> 4;
5205 //map->tiles[y * MAPLAYERS + x * MAPLAYERS * map->height] = 208; //entity->spellTrapCeilingModel
5206 Entity* childEntity = nullptr;
5207 if ( x >= 0 && y >= 0 && x < map->width && y < map->height )
5208 {
5209 if ( !map->tiles[OBSTACLELAYER + y * MAPLAYERS + x * MAPLAYERS * map->height] )
5210 {
5211 childEntity = newEntity(644, 1, map->entities, nullptr);
5212 childEntity->parent = entity->getUID();
5213 childEntity->x = entity->x;
5214 childEntity->y = entity->y;
5215 //printlog("30 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
5216 childEntity->flags[PASSABLE] = true;
5217 if ( !map->tiles[(MAPLAYERS - 1) + y * MAPLAYERS + x * MAPLAYERS * map->height] )
5218 {
5219 childEntity->z = -22.99;
5220 }
5221 else
5222 {
5223 childEntity->z = -6.99;
5224 }
5225 TileEntityList.addEntity(*childEntity);
5226 node_t* tempNode = list_AddNodeLast(&entity->children);
5227 tempNode->element = childEntity; // add the node to the children list.
5228 tempNode->deconstructor = &emptyDeconstructor;
5229 tempNode->size = sizeof(Entity*);
5230
5231 childEntity = newEntity(645, 1, map->entities, nullptr);
5232 childEntity->parent = entity->getUID();
5233 childEntity->x = entity->x;
5234 childEntity->y = entity->y;
5235 childEntity->z = 8.24;
5236 TileEntityList.addEntity(*childEntity);
5237 //printlog("30 Generated entity. Sprite: %d Uid: %d X: %.2f Y: %.2f\n",childEntity->sprite,childEntity->getUID(),childEntity->x,childEntity->y);
5238 childEntity->flags[PASSABLE] = true;
5239 tempNode = list_AddNodeLast(&entity->children);
5240 tempNode->element = childEntity; // add the node to the children list.
5241 tempNode->deconstructor = &emptyDeconstructor;
5242 tempNode->size = sizeof(Entity*);
5243 }
5244 }
5245 break;
5246 }
5247 // arcane chair
5248 case 121:
5249 entity->furnitureType = FURNITURE_CHAIR; // so everything knows I'm a chair
5250 entity->sizex = 2;
5251 entity->sizey = 2;
5252 entity->x += 8;
5253 entity->y += 8;
5254 entity->z = 8;
5255 entity->focalz = -5;
5256 entity->sprite = 626;
5257 entity->behavior = &actFurniture;
5258 entity->flags[BURNABLE] = true;
5259 if ( entity->furnitureDir == -1 && !entity->yaw )
5260 {
5261 entity->yaw = (prng_get_uint() % 360) * (PI / 180.f);
5262 }
5263 else
5264 {
5265 entity->yaw = entity->furnitureDir * 45 * (PI / 180.f);
5266 }
5267 break;
5268 // arcane bed
5269 case 122:
5270 entity->furnitureType = FURNITURE_BED; // so everything knows I'm a bed
5271 entity->x += 8;
5272 entity->y += 8;
5273 entity->z = 4;
5274 entity->sprite = 627;
5275 entity->behavior = &actFurniture;
5276 entity->flags[BURNABLE] = true;
5277 if ( entity->furnitureDir == -1 && !entity->yaw )
5278 {
5279 entity->furnitureDir = (prng_get_uint() % 4);
5280 entity->furnitureDir *= 2; // create an even number
5281 entity->yaw = entity->furnitureDir * 45 * (PI / 180.f);
5282 }
5283 else
5284 {
5285 entity->yaw = entity->furnitureDir * 45 * (PI / 180.f);
5286 }
5287 if ( entity->furnitureDir == 0 || entity->furnitureDir == 4 )
5288 {
5289 entity->sizex = 8;
5290 entity->sizey = 4;
5291 }
5292 else if ( entity->furnitureDir == 2 || entity->furnitureDir == 6 )
5293 {
5294 entity->sizex = 4;
5295 entity->sizey = 8;
5296 }
5297 else
5298 {
5299 entity->sizex = 8;
5300 entity->sizey = 8;
5301 }
5302 break;
5303 // bunk bed
5304 case 123:
5305 entity->furnitureType = FURNITURE_BUNKBED; // so everything knows I'm a bunkbed
5306 entity->x += 8;
5307 entity->y += 8;
5308 entity->z = 1.75;
5309 entity->sprite = 628;
5310 entity->behavior = &actFurniture;
5311 entity->flags[BURNABLE] = true;
5312 if ( entity->furnitureDir == -1 && !entity->yaw )
5313 {
5314 entity->furnitureDir = (prng_get_uint() % 4);
5315 entity->furnitureDir *= 2; // create an even number
5316 entity->yaw = entity->furnitureDir * 45 * (PI / 180.f);
5317 }
5318 else
5319 {
5320 entity->yaw = entity->furnitureDir * 45 * (PI / 180.f);
5321 }
5322 if ( entity->furnitureDir == 0 || entity->furnitureDir == 4 )
5323 {
5324 entity->sizex = 8;
5325 entity->sizey = 4;
5326 }
5327 else if ( entity->furnitureDir == 2 || entity->furnitureDir == 6 )
5328 {
5329 entity->sizex = 4;
5330 entity->sizey = 8;
5331 }
5332 else
5333 {
5334 entity->sizex = 8;
5335 entity->sizey = 8;
5336 }
5337 break;
5338 // column.
5339 case 124:
5340 {
5341 entity->x += 8;
5342 entity->y += 8;
5343 entity->sprite = 629;
5344 entity->sizex = 6;
5345 entity->sizey = 6;
5346 entity->z = -7.75;
5347 entity->flags[BLOCKSIGHT] = false;
5348 entity->behavior = &actColumn;
5349 break;
5350 }
5351 // podium
5352 case 125:
5353 {
5354 entity->sizex = 3;
5355 entity->sizey = 3;
5356 entity->x += 8;
5357 entity->y += 8;
5358 entity->z = 7.75;
5359 entity->focalz = -3;
5360 entity->sprite = 630;
5361 entity->behavior = &actFurniture;
5362 entity->furnitureType = FURNITURE_PODIUM;
5363 entity->flags[BURNABLE] = true;
5364 if ( entity->furnitureDir == -1 && !entity->yaw )
5365 {
5366 entity->furnitureDir = (prng_get_uint() % 4);
5367 entity->furnitureDir *= 2; // create an even number
5368 entity->yaw = entity->furnitureDir * 45 * (PI / 180.f);
5369 }
5370 else
5371 {
5372 entity->yaw = entity->furnitureDir * 45 * (PI / 180.f);
5373 }
5374 break;
5375 }
5376 // piston.
5377 case 126:
5378 {
5379 entity->x += 8;
5380 entity->y += 8;
5381 entity->sprite = 631;
5382 entity->sizex = 8;
5383 entity->sizey = 8;
5384 entity->z = 0;
5385 entity->flags[BLOCKSIGHT] = false;
5386 entity->behavior = &actPistonBase;
5387
5388 childEntity = newEntity(632, 1, map->entities, nullptr); //cam1
5389 childEntity->parent = entity->getUID();
5390 childEntity->x = entity->x + 2.25;
5391 childEntity->y = entity->y + 2.25;
5392 childEntity->behavior = &actPistonCam;
5393 childEntity->pistonCamRotateSpeed = 0.2;
5394 childEntity->flags[UNCLICKABLE] = true;
5395 TileEntityList.addEntity(*childEntity);
5396 /*if ( multiplayer != CLIENT )
5397 {
5398 entity_uids--;
5399 }
5400 childEntity->setUID(-3);*/
5401 childEntity = newEntity(633, 1, map->entities, nullptr); //cam2
5402 childEntity->parent = entity->getUID();
5403 childEntity->x = entity->x - 2.25;
5404 childEntity->y = entity->y - 2.25;
5405 childEntity->behavior = &actPistonCam;
5406 childEntity->pistonCamRotateSpeed = -0.2;
5407 childEntity->flags[UNCLICKABLE] = true;
5408 TileEntityList.addEntity(*childEntity);
5409 /*if ( multiplayer != CLIENT )
5410 {
5411 entity_uids--;
5412 }
5413 childEntity->setUID(-3);*/
5414 break;
5415 }
5416 // grass texture
5417 case 127:
5418 {
5419 entity->x += 8;
5420 entity->y += 8;
5421 entity->sprite = entity->floorDecorationModel;
5422 entity->sizex = 0.01;
5423 entity->sizey = 0.01;
5424 entity->z = 7.5 - entity->floorDecorationHeightOffset * 0.25;
5425 entity->x += entity->floorDecorationXOffset * 0.25;
5426 entity->y += entity->floorDecorationYOffset * 0.25;
5427 if ( entity->floorDecorationRotation == -1 )
5428 {
5429 entity->yaw = (prng_get_uint() % 8) * (PI / 4);
5430 }
5431 else
5432 {
5433 entity->yaw = entity->floorDecorationRotation * (PI / 4);
5434 }
5435 entity->flags[BLOCKSIGHT] = false;
5436 entity->flags[PASSABLE] = true;
5437 if ( entity->floorDecorationInteractText1 == 0 )
5438 {
5439 entity->flags[UNCLICKABLE] = true;
5440 }
5441 entity->behavior = &actFloorDecoration;
5442 /*if ( multiplayer != CLIENT )
5443 {
5444 entity_uids--;
5445 }
5446 entity->setUID(-3);*/
5447 break;
5448 }
5449 // expansion end game portal:
5450 case 129:
5451 entity->x += 8;
5452 entity->y += 8;
5453 entity->sprite = 614;
5454 entity->sizex = 4;
5455 entity->sizey = 4;
5456 entity->yaw = PI / 2;
5457 entity->behavior = &actExpansionEndGamePortal;
5458 entity->flags[PASSABLE] = true;
5459 entity->flags[BRIGHT] = true;
5460 //entity->flags[INVISIBLE] = true;
5461 entity->portalVictoryType = 3;
5462 entity->skill[28] = 1; // is a mechanism
5463 break;
5464 //sound source
5465 case 130:
5466 entity->sizex = 2;
5467 entity->sizey = 2;
5468 entity->x += 8;
5469 entity->y += 8;
5470 entity->behavior = &actSoundSource;
5471 entity->flags[SPRITE] = true;
5472 entity->flags[INVISIBLE] = true;
5473 entity->flags[PASSABLE] = true;
5474 entity->flags[NOUPDATE] = true;
5475 entity->skill[28] = 1; // is a mechanism
5476 break;
5477 //light source
5478 case 131:
5479 entity->sizex = 2;
5480 entity->sizey = 2;
5481 entity->x += 8;
5482 entity->y += 8;
5483 entity->behavior = &actLightSource;
5484 entity->flags[SPRITE] = true;
5485 entity->flags[INVISIBLE] = true;
5486 entity->flags[PASSABLE] = true;
5487 //entity->flags[NOUPDATE] = true;
5488 entity->skill[28] = 1; // is a mechanism
5489 break;
5490 //text source
5491 case 132:
5492 entity->sizex = 2;
5493 entity->sizey = 2;
5494 entity->x += 8;
5495 entity->y += 8;
5496 entity->behavior = &actTextSource;
5497 entity->flags[SPRITE] = true;
5498 entity->flags[INVISIBLE] = true;
5499 entity->flags[PASSABLE] = true;
5500 entity->flags[NOUPDATE] = true;
5501 entity->skill[28] = 1; // is a mechanism
5502 break;
5503 //signal timer
5504 case 133:
5505 entity->sizex = 2;
5506 entity->sizey = 2;
5507 entity->x += 8;
5508 entity->y += 8;
5509 entity->behavior = &actSignalTimer;
5510 entity->flags[SPRITE] = true;
5511 entity->flags[INVISIBLE] = true;
5512 entity->flags[PASSABLE] = true;
5513 entity->flags[NOUPDATE] = true;
5514 entity->skill[28] = 1; // is a mechanism
5515 break;
5516 //custom teleporter
5517 case 161:
5518 entity->x += 8;
5519 entity->y += 8;
5520 entity->sprite = entity->portalCustomSprite;
5521 entity->sizex = 4;
5522 entity->sizey = 4;
5523 entity->yaw = PI / 2;
5524 entity->behavior = &actCustomPortal;
5525 entity->flags[PASSABLE] = true;
5526 if ( entity->portalCustomSpriteAnimationFrames > 0 )
5527 {
5528 entity->flags[BRIGHT] = true;
5529 }
5530 if ( entity->portalCustomRequiresPower )
5531 {
5532 entity->flags[INVISIBLE] = true;
5533 }
5534 entity->z = 7.5 - entity->portalCustomZOffset * 0.25;
5535 if ( entity->portalCustomRequiresPower == 1 )
5536 {
5537 entity->skill[28] = 1; // is a mechanism
5538 }
5539 break;
5540 case 162:
5541 {
5542 // readable book
5543 entity->sizex = 4;
5544 entity->sizey = 4;
5545 entity->x += 8;
5546 entity->y += 8;
5547 entity->roll = PI / 2.0;
5548 entity->yaw = (prng_get_uint() % 360) * PI / 180.0;
5549 entity->flags[PASSABLE] = true;
5550 entity->behavior = &actItem;
5551 entity->skill[10] = READABLE_BOOK;
5552 if ( entity->skill[11] == 0 ) //random
5553 {
5554 entity->skill[11] = 1 + prng_get_uint() % 4; // status
5555 }
5556 else
5557 {
5558 entity->skill[11]--; //editor set number, sets this value to 0-5, with 1 being BROKEN, 5 being EXCELLENT
5559 }
5560 if ( entity->skill[12] == 10 ) //random, else the value of this variable is the curse/bless
5561 {
5562 if ( prng_get_uint() % 2 == 0 ) // 50% chance of curse/bless
5563 {
5564 entity->skill[12] = -2 + prng_get_uint() % 5;
5565 }
5566 else
5567 {
5568 entity->skill[12] = 0;
5569 }
5570 }
5571 entity->skill[13] = 1; // qty
5572
5573 // assemble the book string.
5574 char buf[256] = "";
5575 int totalChars = 0;
5576 for ( int i = 40; i <= 52; ++i )
5577 {
5578 if ( i == 28 ) // circuit_status
5579 {
5580 continue;
5581 }
5582 if ( entity->skill[i] != 0 )
5583 {
5584 for ( int c = 0; c < 4; ++c )
5585 {
5586 buf[totalChars] = static_cast<char>((entity->skill[i] >> (c * 8)) & 0xFF);
5587 ++totalChars;
5588 }
5589 }
5590 }
5591 if ( buf[totalChars] != '\0' )
5592 {
5593 buf[totalChars] = '\0';
5594 }
5595 std::string output = buf;
5596 size_t found = output.find("\\n");
5597 while ( found != std::string::npos )
5598 {
5599 output.erase(found, 2);
5600 output.insert(found, 1, '\n');
5601 found = output.find("\\n");
5602 }
5603 strcpy(buf, output.c_str());
5604
5605 entity->skill[14] = getBook(buf);
5606
5607 if ( entity->skill[15] == 1 ) // editor set as identified
5608 {
5609 entity->skill[15] = 1;
5610 }
5611 else if ( entity->skill[15] == 0 ) // unidentified (default)
5612 {
5613 entity->skill[15] = 0;
5614 }
5615 else if ( entity->skill[15] == 2 ) // editor set as random
5616 {
5617 entity->skill[15] = prng_get_uint() % 2;
5618 }
5619 else
5620 {
5621 entity->skill[15] = 0; // unidentified.
5622 }
5623
5624 item = newItemFromEntity(entity);
5625 entity->sprite = itemModel(item);
5626 if ( !entity->itemNotMoving )
5627 {
5628 entity->z = 7.5 - models[entity->sprite]->sizey * .25;
5629 }
5630 entity->itemNotMoving = 1; // so the item retains its position
5631 entity->itemNotMovingClient = 1; // so the item retains its position for clients
5632 free(item);
5633 item = nullptr;
5634 }
5635 break;
5636 default:
5637 break;
5638 }
5639 if ( entity )
5640 {
5641 nextnode = node->next;
5642 TileEntityList.addEntity(*entity);
5643 }
5644 }
5645
5646 for ( node = map->entities->first; node != nullptr; )
5647 {
5648 Entity* postProcessEntity = (Entity*)node->element;
5649 node = node->next;
5650 if ( postProcessEntity )
5651 {
5652 if ( postProcessEntity->behavior == &actItem )
5653 {
5654 // see if there's any platforms to set items upon.
5655 for ( node_t* tmpnode = map->entities->first; tmpnode != nullptr; tmpnode = tmpnode->next )
5656 {
5657 Entity* tmpentity = (Entity*)tmpnode->element;
5658 if ( (tmpentity->behavior == &actFurniture
5659 && (tmpentity->x == postProcessEntity->x) && (tmpentity->y == postProcessEntity->y)
5660 ) )
5661 {
5662 if ( postProcessEntity->z > 4 )
5663 {
5664 if ( tmpentity->sprite == 271 )
5665 {
5666 // is table
5667 postProcessEntity->z -= 6;
5668 tmpentity->parent = postProcessEntity->getUID();
5669 }
5670 else if ( tmpentity->sprite == 630 )
5671 {
5672 // is podium
5673 postProcessEntity->z -= 6;
5674 tmpentity->parent = postProcessEntity->getUID();
5675 }
5676 }
5677 break;
5678 }
5679 }
5680 }
5681 else if ( postProcessEntity->sprite == 252 && postProcessEntity->z <= -10 )
5682 {
5683 // trapdoor for boulder traps.
5684 int findx = static_cast<int>(postProcessEntity->x) >> 4;
5685 int findy = static_cast<int>(postProcessEntity->y) >> 4;
5686 list_t* entitiesOnTile = TileEntityList.getTileList(findx, findy);
5687 for ( node_t* tmpnode = entitiesOnTile->first; tmpnode != nullptr; tmpnode = tmpnode->next )
5688 {
5689 Entity* tmpentity = (Entity*)tmpnode->element;
5690 if ( tmpentity && tmpentity != postProcessEntity )
5691 {
5692 if ( tmpentity->behavior != &actMonster
5693 && !tmpentity->flags[PASSABLE]
5694 && tmpentity->behavior != &actFurniture )
5695 {
5696 // remove the trapdoor since we've spawned over a gate, chest, door etc.
5697 list_RemoveNode(postProcessEntity->mynode);
5698 break;
5699 }
5700 }
5701 }
5702 }
5703 else if ( postProcessEntity->sprite == 586 || postProcessEntity->sprite == 585
5704 || postProcessEntity->sprite == 184 || postProcessEntity->sprite == 185 )
5705 {
5706 int findx = static_cast<int>(postProcessEntity->x) >> 4;
5707 int findy = static_cast<int>(postProcessEntity->y) >> 4;
5708 if ( !map->tiles[findy * MAPLAYERS + findx * MAPLAYERS * map->height] )
5709 {
5710 // remove the lever as it is over a pit.
5711 printlog("[MAP GENERATOR] Removed switch over a pit at x:%d y:%d.", findx, findy);
5712 list_RemoveNode(postProcessEntity->mynode);
5713 }
5714 }
5715 }
5716 }
5717 if ( vampireQuestChest )
5718 {
5719 for ( c = 0; c < MAXPLAYERS; ++c )
5720 {
5721 if ( client_classes[c] == CLASS_ACCURSED )
5722 {
5723 vampireQuestChest->chestHasVampireBook = 1;
5724 break;
5725 }
5726 }
5727 }
5728
5729 for ( node = map->entities->first; node != nullptr; )
5730 {
5731 Entity* postProcessEntity = (Entity*)node->element;
5732 node = node->next;
5733 if ( postProcessEntity )
5734 {
5735 if ( postProcessEntity->behavior == &actTextSource )
5736 {
5737 textSourceScript.parseScriptInMapGeneration(*postProcessEntity);
5738 }
5739 }
5740 }
5741 }
5742
mapLevel(int player)5743 void mapLevel(int player)
5744 {
5745 int x, y;
5746 for ( y = 0; y < map.height; ++y )
5747 {
5748 for ( x = 0; x < map.width; ++x )
5749 {
5750 if ( map.tiles[OBSTACLELAYER + y * MAPLAYERS + x * MAPLAYERS * map.height] )
5751 {
5752 if ( !minimap[y][x] )
5753 {
5754 minimap[y][x] = 4;
5755 }
5756 }
5757 else if ( map.tiles[y * MAPLAYERS + x * MAPLAYERS * map.height] )
5758 {
5759 if ( !minimap[y][x] )
5760 {
5761 minimap[y][x] = 3;
5762 }
5763 }
5764 else
5765 {
5766 minimap[y][x] = 0;
5767 }
5768 }
5769 }
5770 }
5771
mapFoodOnLevel(int player)5772 void mapFoodOnLevel(int player)
5773 {
5774 int numFood = 0;
5775 bool previouslyIdentifiedFood = false;
5776 for ( node_t* node = map.entities->first; node != nullptr; node = node->next )
5777 {
5778 Entity* entity = (Entity*)node->element;
5779 if ( entity && entity->behavior == &actItem )
5780 {
5781 Item* item = newItemFromEntity(entity);
5782 if ( item )
5783 {
5784 if ( itemCategory(item) == FOOD )
5785 {
5786 if ( entity->itemShowOnMap != 0 )
5787 {
5788 previouslyIdentifiedFood = true;
5789 }
5790 else
5791 {
5792 ++numFood;
5793 }
5794 entity->itemShowOnMap = 1;
5795 }
5796 free(item);
5797 }
5798 }
5799 }
5800 if ( numFood == 0 && previouslyIdentifiedFood )
5801 {
5802 messagePlayer(player, language[3425]);
5803 }
5804 else if ( numFood == 0 )
5805 {
5806 messagePlayer(player, language[3423]);
5807 }
5808 else
5809 {
5810 messagePlayerColor(player, SDL_MapRGB(mainsurface->format, 0, 255, 0),language[3424]);
5811 }
5812 }
5813
loadMainMenuMap(bool blessedAdditionMaps,bool forceVictoryMap)5814 int loadMainMenuMap(bool blessedAdditionMaps, bool forceVictoryMap)
5815 {
5816 bool foundVictory = false;
5817 for ( node_t* node = topscores.first; node != nullptr && !foundVictory; node = node->next )
5818 {
5819 score_t* score = (score_t*)node->element;
5820 if ( score && score->victory == 3 )
5821 {
5822 foundVictory = true;
5823 }
5824 }
5825 for ( node_t* node = topscoresMultiplayer.first; node != nullptr && !foundVictory; node = node->next )
5826 {
5827 score_t* score = (score_t*)node->element;
5828 if ( score && score->victory == 3 )
5829 {
5830 foundVictory = true;
5831 }
5832 }
5833
5834 std::string fullMapName;
5835
5836 if ( forceVictoryMap || (foundVictory && rand() % 5 == 0) )
5837 {
5838 fullMapName = physfsFormatMapName("mainmenu9");
5839 loadMap(fullMapName.c_str(), &map, map.entities, map.creatures);
5840 menucam.x = 34.3;
5841 menucam.y = 15;
5842 menucam.z = -20;
5843 menucam.ang = 5.84;
5844 return 1;
5845 }
5846 else if ( blessedAdditionMaps )
5847 {
5848 switch ( rand() % 4 )
5849 {
5850 case 0:
5851 fullMapName = physfsFormatMapName("mainmenu5");
5852 loadMap(fullMapName.c_str(), &map, map.entities, map.creatures);
5853 menucam.x = 30.8;
5854 menucam.y = 24.3;
5855 menucam.z = 0;
5856 menucam.ang = 2.76;
5857 break;
5858 case 1:
5859 fullMapName = physfsFormatMapName("mainmenu6");
5860 loadMap(fullMapName.c_str(), &map, map.entities, map.creatures);
5861 menucam.x = 11;
5862 menucam.y = 4;
5863 menucam.z = 0;
5864 menucam.ang = 2.4;
5865 break;
5866 case 2:
5867 fullMapName = physfsFormatMapName("mainmenu7");
5868 loadMap(fullMapName.c_str(), &map, map.entities, map.creatures);
5869 menucam.x = 8.7;
5870 menucam.y = 9.3;
5871 menucam.z = 0;
5872 menucam.ang = 5.8;
5873 break;
5874 case 3:
5875 fullMapName = physfsFormatMapName("mainmenu8");
5876 loadMap(fullMapName.c_str(), &map, map.entities, map.creatures);
5877 menucam.x = 3.31;
5878 menucam.y = 5.34;
5879 menucam.z = 0;
5880 menucam.ang = 0.96;
5881 break;
5882 default:
5883 break;
5884 }
5885 }
5886 else
5887 {
5888 switch ( rand() % 4 )
5889 {
5890 case 0:
5891 fullMapName = physfsFormatMapName("mainmenu1");
5892 loadMap(fullMapName.c_str(), &map, map.entities, map.creatures);
5893 menucam.x = 8;
5894 menucam.y = 4.5;
5895 menucam.z = 0;
5896 menucam.ang = 0.6;
5897 break;
5898 case 1:
5899 fullMapName = physfsFormatMapName("mainmenu2");
5900 loadMap(fullMapName.c_str(), &map, map.entities, map.creatures);
5901 menucam.x = 7;
5902 menucam.y = 4;
5903 menucam.z = -4;
5904 menucam.ang = 1.0;
5905 break;
5906 case 2:
5907 fullMapName = physfsFormatMapName("mainmenu3");
5908 loadMap(fullMapName.c_str(), &map, map.entities, map.creatures);
5909 menucam.x = 5;
5910 menucam.y = 3;
5911 menucam.z = 0;
5912 menucam.ang = 1.0;
5913 break;
5914 case 3:
5915 fullMapName = physfsFormatMapName("mainmenu4");
5916 loadMap(fullMapName.c_str(), &map, map.entities, map.creatures);
5917 menucam.x = 6;
5918 menucam.y = 14.5;
5919 menucam.z = -24;
5920 menucam.ang = 5.0;
5921 break;
5922 }
5923 }
5924
5925 return 0;
5926 }
5927
5928