1 /* omega copyright (c) 1987,1988,1989 by Laurence Raphael Brothers */
2 /* lev.c */
3 
4 #include "glob.h"
5 
6 /* Functions dealing with dungeon and country levels aside from actual
7 level structure generation */
8 
9 
10 /* monsters for tactical encounters */
make_country_monsters(terrain)11 void make_country_monsters(terrain)
12 Symbol terrain;
13 {
14   pml tml,ml=NULL;
15   static int plains[10] =
16     {BUNNY,BUNNY,HORNET,QUAIL,HAWK,DEER,WOLF,LION,BRIGAND,RANDOM};
17 /*    {BUNNY,BUNNY,BLACKSNAKE,HAWK,IMPALA,WOLF,LION,BRIGAND,RANDOM};*/
18   /* DG changed (WDT: I'd like to see a blacksnake). */
19   static int forest[10] =
20    {BUNNY,QUAIL,HAWK,BADGER,DEER,DEER,WOLF,BEAR,BRIGAND,RANDOM};
21   static int jungle[10] =
22     {ANTEATER,PARROT,MAMBA,ANT,ANT,HYENA,HYENA,ELEPHANT,LION,RANDOM};
23   static int river[10] =
24     {QUAIL,TROUT,TROUT,MANOWAR,BASS,BASS,CROC,CROC,BRIGAND,RANDOM};
25   static int swamp[10] =
26     {BASS,BASS,CROC,CROC,BOGTHING,ANT,ANT,RANDOM,RANDOM,RANDOM};
27   static int desert[10] =
28     {HAWK,HAWK,CAMEL,CAMEL,HYENA,HYENA,LION,LION,RANDOM,RANDOM};
29   static int tundra[10] =
30     {WOLF,WOLF,BEAR,BEAR,DEER,DEER,RANDOM,RANDOM,RANDOM,RANDOM};
31   static int mountain[10] =
32     {BUNNY,SHEEP,WOLF,WOLF,HAWK,HAWK,HAWK,RANDOM,RANDOM,RANDOM};
33   int *monsters,i,nummonsters;
34 
35   nummonsters = (random_range(5)+1) * (random_range(3)+1);
36 
37   switch(terrain) {
38   case PLAINS: monsters = plains; break;
39   case FOREST: monsters = forest; break;
40   case JUNGLE: monsters = jungle; break;
41   case RIVER: monsters = river; break;
42   case SWAMP: monsters = swamp; break;
43   case MOUNTAINS: case PASS: case VOLCANO: monsters = mountain; break;
44   case DESERT: monsters = desert; break;
45   case TUNDRA: monsters = tundra; break;
46   default: monsters = NULL;
47   }
48   for(i=0;i<nummonsters;i++) {
49     tml = ((pml) checkmalloc(sizeof(mltype)));
50     tml->m = ((pmt) checkmalloc(sizeof(montype)));
51     if (monsters == NULL) tml->m =
52       m_create(random_range(WIDTH),random_range(LENGTH),TRUE,difficulty());
53     else {
54       tml->m = make_creature(*(monsters+random_range(10)));
55       tml->m->x = random_range(WIDTH);
56       tml->m->y = random_range(LENGTH);
57     }
58     Level->site[tml->m->x][tml->m->y].creature = tml->m;
59     tml->m->sense = WIDTH;
60     if (m_statusp(tml->m,ONLYSWIM)) {
61       Level->site[tml->m->x][tml->m->y].locchar = WATER;
62       Level->site[tml->m->x][tml->m->y].p_locf = L_WATER;
63       lset(tml->m->x, tml->m->y, CHANGED);
64     }
65 
66     tml->next = ml;
67     ml = tml;
68   }
69   Level->mlist = ml;
70 }
71 
72 
73 
74 /* monstertype is more or less Current_Dungeon */
75 /* The caves and sewers get harder as you penetrate them; the castle
76 is completely random, but also gets harder as it is explored;
77 the astral and the volcano just stay hard... */
populate_level(monstertype)78 void populate_level(monstertype)
79 int monstertype;
80 {
81   pml head,tml;
82   int i,j,k,monsterid,nummonsters=(random_range(difficulty()/3)+1)*3+8;
83 
84   if (monstertype == E_CASTLE) nummonsters += 10;
85   else if (monstertype == E_ASTRAL) nummonsters += 10;
86   else if (monstertype == E_VOLCANO) nummonsters += 20;
87 
88   head = tml = ((pml) checkmalloc(sizeof(mltype)));
89 
90   for(k=0;k<nummonsters;k++) {
91 
92     findspace(&i,&j,-1);
93 
94     switch(monstertype) {
95     case E_CAVES:
96       if (Level->depth*10+random_range(100) > 150)
97 	monsterid = GOBLIN_SHAMAN;
98       else if (Level->depth*10+random_range(100) > 100)
99 	monsterid = GOBLIN_CHIEF; /* Goblin Chieftain */
100       else if (random_range(100) > 50) monsterid = GOBLIN;
101       else monsterid = RANDOM; /* IE, random monster */
102       break;
103     case E_SEWERS:
104       if (! random_range(3)) monsterid = -1;
105       else switch(random_range(Level->depth+3)) {
106       case 0: monsterid = SEWER_RAT; break;
107       case 1: monsterid = AGGRAVATOR; break; /* aggravator fungus */
108       case 2: monsterid = BLIPPER; break; /* blipper rat */
109       case 3: monsterid = NIGHT_GAUNT; break;
110       case 4: monsterid = NASTY; break; /* transparent nasty */
111       case 5: monsterid = MURK; break; /* murk fungus */
112       case 6: monsterid = CATOBLEPAS; break;
113       case 7: monsterid = ACID_CLOUD; break;
114       case 8: monsterid = DENEBIAN; break; /* denebian slime devil */
115       case 9: monsterid = CROC; break; /* giant crocodile */
116       case 10: monsterid = TESLA; break; /* tesla monster */
117       case 11: monsterid = SHADOW; break; /* shadow spirit */
118       case 12: monsterid = BOGTHING; break; /* bogthing */
119       case 13: monsterid = WATER_ELEM; break; /* water elemental */
120       case 14: monsterid = TRITON; break;
121       case 15: monsterid = ROUS; break;
122       default: monsterid = RANDOM; break; /* whatever seems good */
123       }
124       break;
125     case E_ASTRAL:
126       if (random_range(2)) /* random astral creatures */
127 	switch(random_range(12)) {
128 	case 0: monsterid = THOUGHTFORM; break;
129 	case 1: monsterid = FUZZY; break; /* astral fuzzy */
130 	case 2: monsterid = BAN_SIDHE; break;
131 	case 3: monsterid = GRUE; break; /* astral grue */
132 	case 4: monsterid = SHADOW; break; /* shadow spirit */
133 	case 5: monsterid = ASTRAL_VAMP; break; /* astral vampire */
134 	case 6: monsterid = MANABURST; break;
135 	case 7: monsterid = RAKSHASA; break;
136 	case 8: monsterid = ILL_FIEND; break; /* illusory fiend */
137 	case 9: monsterid = MIRRORMAST; break; /* mirror master */
138 	case 10: monsterid = ELDER_GRUE; break; /* elder etheric grue */
139 	case 11: monsterid = SHADOW_SLAY; break; /* shadow slayer */
140 	}
141       else if (random_range(2) && (Level->depth == 1)) /* plane of earth */
142 	monsterid = EARTH_ELEM; /* earth elemental */
143       else if (random_range(2) && (Level->depth == 2)) /* plane of air */
144 	monsterid = AIR_ELEM;  /* air elemental */
145       else if (random_range(2) && (Level->depth == 3)) /* plane of water */
146 	monsterid = WATER_ELEM;  /* water elemental */
147       else if (random_range(2) && (Level->depth == 4)) /* plane of fire */
148 	monsterid = FIRE_ELEM;  /* fire elemental */
149       else if (random_range(2) && (Level->depth == 5)) /* deep astral */
150 	switch (random_range(12)) {
151 	case 0:monsterid = NIGHT_GAUNT; break;
152 	case 1:monsterid = SERV_LAW; break; /* servant of law */
153 	case 2:monsterid = SERV_CHAOS; break; /* servant of chaos */
154 	case 3:monsterid = FROST_DEMON; break; /* lesser frost demon */
155 	case 4:monsterid = OUTER_DEMON; break; /* outer circle demon */
156 	case 5:monsterid = DEMON_SERP; break; /* demon serpent */
157 	case 6:monsterid = ANGEL; break;
158 	case 7:monsterid = INNER_DEMON; break; /* inner circle demon */
159 	case 8:monsterid = FDEMON_L; break; /* frost demon lord */
160 	case 9:monsterid = HIGH_ANGEL; break;
161 	case 10:monsterid = DEMON_PRINCE; break; /* prime circle demon */
162 	case 11:monsterid = ARCHANGEL; break;
163 	}
164       else monsterid = RANDOM;
165       break;
166     case E_VOLCANO:
167       if (random_range(2)) {
168 	do monsterid = random_range(ML10-ML4)+ML4;
169 	while (Monsters[monsterid].uniqueness != COMMON);
170       }
171       else switch(random_range(Level->depth/2+2)) { /* evil & fire creatures */
172       case 0: monsterid = HAUNT; break;
173       case 1: monsterid = INCUBUS; break;
174       case 2: monsterid = DRAGONETTE; break;
175       case 3: monsterid = FROST_DEMON; break;
176       case 4: monsterid = SPECTRE; break;
177       case 5: monsterid = LAVA_WORM; break;
178       case 6: monsterid = FIRE_ELEM; break;
179       case 7: monsterid = LICHE; break;
180       case 8: monsterid = RAKSHASA; break;
181       case 9: monsterid = DEMON_SERP; break;
182       case 10: monsterid = NAZGUL; break;
183       case 11: monsterid = FLAME_DEV; break;
184       case 12: monsterid = LOATHLY; break;
185       case 13: monsterid = ZOMBIE; break;
186       case 14: monsterid = INNER_DEMON; break;
187       case 15: monsterid = BAD_FAIRY; break;
188       case 16: monsterid = DRAGON; break;
189       case 17: monsterid = FDEMON_L; break;
190       case 18: monsterid = SHADOW_SLAY; break;
191       case 19: monsterid = DEATHSTAR; break;
192       case 20: monsterid = VAMP_LORD; break;
193       case 21: monsterid = DEMON_PRINCE; break;
194       default: monsterid = RANDOM; break;
195       }
196       break;
197     case E_CASTLE:
198       if (random_range(4)==1) {
199 	if (difficulty() < 5)
200 	  monsterid = ENCHANTOR;
201 	else if (difficulty() < 6)
202 	  monsterid = NECROMANCER;
203 	else if (difficulty() < 8)
204 	  monsterid = FIRE_ELEM;
205 	else monsterid = THAUMATURGIST;
206       }
207       else monsterid = RANDOM;
208     break;
209 
210     default: monsterid = RANDOM; break;
211     }
212 
213     assert( RANDOM == -1 ); /* WDT: the following test slightly assumes
214                              * this. */
215     if (monsterid > RANDOM)
216       Level->site[i][j].creature = make_creature(monsterid);
217     else Level->site[i][j].creature = m_create(i,j,TRUE,difficulty());
218 
219     Level->site[i][j].creature->x = i;
220     Level->site[i][j].creature->y = j;
221 
222     if (m_statusp(Level->site[i][j].creature,ONLYSWIM)) {
223       Level->site[i][j].locchar = WATER;
224       Level->site[i][j].p_locf = L_WATER;
225       lset(i, j, CHANGED);
226     }
227 
228     tml->next = ((pml) checkmalloc(sizeof(mltype)));
229     tml->next->m = Level->site[i][j].creature;
230     tml = tml->next;
231   }
232 
233   if (Level->mlist==NULL) {
234     tml->next = NULL;
235     Level->mlist = head->next;
236   }
237   else {
238     tml->next = Level->mlist;
239     Level->mlist = head->next;
240   }
241 }
242 
243 
244 
245 /* Add a wandering monster possibly */
wandercheck()246 void wandercheck()
247 {
248   int x,y;
249   pml tml;
250   if (random_range(MaxDungeonLevels) < difficulty()) {
251     findspace(&x,&y,-1);
252     tml = ((pml) checkmalloc(sizeof(mltype)));
253     tml->next = Level->mlist;
254     tml->m = Level->site[x][y].creature = m_create(x,y,WANDERING,difficulty());
255     Level->mlist = tml;
256   }
257 }
258 
259 
260 
261 /* call make_creature and place created monster on Level->mlist and Level */
make_site_monster(i,j,mid)262 void make_site_monster(i,j,mid)
263 int i,j,mid;
264 {
265   pml ml = ((pml) checkmalloc(sizeof(mltype)));
266   pmt m;
267   if (mid > -1)  Level->site[i][j].creature = (m = make_creature(mid));
268   else Level->site[i][j].creature = (m = m_create(i,j,WANDERING,difficulty()));
269   m->x = i;
270   m->y = j;
271   ml->m = m;
272   ml->next = Level->mlist;
273   Level->mlist = ml;
274 }
275 
276 
277 /* make and return an appropriate monster for the level and depth*/
278 /* called by populate_level, doesn't actually add to mlist for some reason*/
279 /* eventually to be more intelligent */
m_create(x,y,kind,level)280 pmt m_create(x,y,kind,level)
281 int x,y,kind,level;
282 {
283   pmt newmonster;
284   int monster_range;
285   int mid;
286 
287   switch(level) {
288     case 0:monster_range = ML1; break;
289     case 1:monster_range = ML2; break;
290     case 2:monster_range = ML3; break;
291     case 3:monster_range = ML4; break;
292     case 4:monster_range = ML5; break;
293     case 5:monster_range = ML6; break;
294     case 6:monster_range = ML7; break;
295     case 7:monster_range = ML8; break;
296     case 8:monster_range = ML9; break;
297     case 9:monster_range = ML10; break;
298     default:monster_range = NUMMONSTERS; break;
299   }
300 
301   do
302     mid = random_range(monster_range);
303   while (Monsters[mid].uniqueness != COMMON);
304   newmonster = make_creature(mid);
305 
306   /* no duplicates of unique monsters */
307   if (kind == WANDERING) m_status_set(newmonster,WANDERING);
308   newmonster->x = x;
309   newmonster->y = y;
310   return(newmonster);
311 }
312 
313 
314 
315 /* make creature # mid, totally random if mid == -1 */
316 /* make creature allocates space for the creature */
make_creature(mid)317 pmt make_creature(mid)
318 int mid;
319 {
320   pmt newmonster = ((pmt) checkmalloc(sizeof(montype)));
321   pob ob;
322   int i,treasures;
323 
324   if (mid == -1) mid = random_range(ML9);
325   *newmonster = Monsters[mid];
326   if ((mid == ANGEL) || (mid == HIGH_ANGEL) || (mid == ARCHANGEL)) {
327     /* aux1 field of an angel is its deity */
328     if (Current_Environment == E_TEMPLE)
329       newmonster->aux1 = Country[LastCountryLocX][LastCountryLocY].aux;
330     else
331       newmonster->aux1 = random_range(6)+1;
332     strcpy(Str3,Monsters[mid].monstring);
333     switch(newmonster->aux1) {
334     case ODIN: strcat(Str3," of Odin"); break;
335     case SET: strcat(Str3," of Set"); break;
336     case HECATE: strcat(Str3," of Hecate"); break;
337     case ATHENA: strcat(Str3," of Athena"); break;
338     case DESTINY: strcat(Str3," of Destiny"); break;
339     case DRUID: strcat(Str3," of the Balance"); break;
340     }
341     newmonster->monstring = salloc(Str3);
342   }
343   else if (mid == ZERO_NPC || mid == WEREHUMAN) {
344     /* generic 0th level human, or a were-human */
345     newmonster->monstring = mantype();
346     strcpy(Str1,"dead ");
347     strcat(Str1,newmonster->monstring);
348     newmonster->corpsestr = salloc(Str1);
349   }
350   else if ((newmonster->monchar&0xff) == '!') {
351     /* the nymph/satyr and incubus/succubus */
352     if (Player.preference == 'f' ||
353 	(Player.preference != 'm' && random_range(2))) {
354       newmonster->monchar = 'n'|CLR(RED);
355       newmonster->monstring = "nymph";
356       newmonster->corpsestr = "dead nymph";
357     }
358     else {
359       newmonster->monchar = 's'|CLR(RED);
360       newmonster->monstring = "satyr";
361       newmonster->corpsestr = "dead satyr";
362     }
363     if (newmonster->id == INCUBUS) {
364       if ((newmonster->monchar&0xff) == 'n')
365 	newmonster->corpsestr = "dead succubus";
366       else newmonster->corpsestr = "dead incubus";
367     }
368   }
369   if (mid == NPC)
370     make_log_npc(newmonster);
371   else if (mid == HISCORE_NPC)
372     make_hiscore_npc(newmonster, random_range(15));
373   else {
374     if (newmonster->sleep < random_range(100))
375       m_status_set(newmonster,AWAKE);
376     if (newmonster->startthing > -1 &&
377       Objects[newmonster->startthing].uniqueness <= UNIQUE_MADE) {
378       ob = ((pob) checkmalloc(sizeof(objtype)));
379       *ob = Objects[newmonster->startthing];
380       m_pickup(newmonster,ob);
381     }
382     treasures = random_range(newmonster->treasure);
383     for(i=0;i<treasures;i++) {
384       do {
385         ob = (pob) (create_object(newmonster->level));
386         if (ob->uniqueness != COMMON) {
387           Objects[ob->id].uniqueness = UNIQUE_UNMADE;
388           free(ob);
389           ob = NULL;
390         }
391       } while (!ob);
392       m_pickup(newmonster,ob);
393     }
394   }
395   newmonster->click = (Tick + 1) % 50;
396   return(newmonster);
397 }
398 
399 
400 
401 
402 /* drop treasures randomly onto level */
stock_level()403 void stock_level()
404 {
405   int i,j,k,numtreasures=2*random_range(difficulty()/4)+4;
406 
407   /* put cash anywhere, including walls, put other treasures only on floor */
408   for (k=0;k<numtreasures+10;k++) {
409     do {
410       i = random_range(WIDTH);
411       j = random_range(LENGTH);
412     } while (Level->site[i][j].locchar != FLOOR);
413     make_site_treasure(i,j,difficulty());
414     i = random_range(WIDTH);
415     j = random_range(LENGTH);
416     Level->site[i][j].things = ((pol) checkmalloc(sizeof(oltype)));
417     Level->site[i][j].things->thing = ((pob) checkmalloc(sizeof(objtype)));
418     make_cash(Level->site[i][j].things->thing,difficulty());
419     Level->site[i][j].things->next = NULL;
420     /* caves have more random cash strewn around */
421     if (Current_Dungeon == E_CAVES) {
422       i = random_range(WIDTH);
423       j = random_range(LENGTH);
424       Level->site[i][j].things = ((pol) checkmalloc(sizeof(oltype)));
425       Level->site[i][j].things->thing = ((pob) checkmalloc(sizeof(objtype)));
426       make_cash(Level->site[i][j].things->thing,difficulty());
427       Level->site[i][j].things->next = NULL;
428       i = random_range(WIDTH);
429       j = random_range(LENGTH);
430       Level->site[i][j].things = ((pol) checkmalloc(sizeof(oltype)));
431       Level->site[i][j].things->thing = ((pob) checkmalloc(sizeof(objtype)));
432       make_cash(Level->site[i][j].things->thing,difficulty());
433       Level->site[i][j].things->next = NULL;
434     }
435   }
436 }
437 
438 
439 /* make a new object (of at most level itemlevel) at site i,j on level*/
make_site_treasure(i,j,itemlevel)440 void make_site_treasure(i,j,itemlevel)
441 int i,j,itemlevel;
442 {
443   pol tmp = ((pol) checkmalloc(sizeof(oltype)));
444   tmp->thing = ((pob) create_object(itemlevel));
445   tmp->next = Level->site[i][j].things;
446   Level->site[i][j].things = tmp;
447 }
448 
449 /* make a specific new object at site i,j on level*/
make_specific_treasure(i,j,itemid)450 void make_specific_treasure(i,j,itemid)
451 int i,j,itemid;
452 {
453   pol tmp;
454   if (Objects[itemid].uniqueness == UNIQUE_TAKEN)
455     return;
456   tmp = ((pol) checkmalloc(sizeof(oltype)));
457   tmp->thing = ((pob) checkmalloc(sizeof(objtype)));
458   *(tmp->thing) = Objects[itemid];
459   tmp->next = Level->site[i][j].things;
460   Level->site[i][j].things = tmp;
461 }
462 
463 
464 
465 #ifndef MSDOS_SUPPORTED_ANTIQUE
466 /* returns a "level of difficulty" based on current environment
467    and depth in dungeon. Is somewhat arbitrary. value between 1 and 10.
468    May not actually represent real difficulty, but instead level
469    of items, monsters encountered.    */
difficulty()470 int difficulty()
471 {
472   int depth = 1;
473   if (Level != NULL) depth = Level->depth;
474   switch(Current_Environment) {
475   case E_COUNTRYSIDE: return(7);
476   case E_CITY: return(3);
477   case E_VILLAGE: return(1);
478   case E_TACTICAL_MAP: return(7);
479   case E_SEWERS: return(depth/6)+3;
480   case E_CASTLE: return(depth/4)+4;
481   case E_CAVES: return(depth/3)+1;
482   case E_VOLCANO: return(depth/4)+5;
483   case E_ASTRAL: return(8);
484   case E_ARENA: return(5);
485   case E_HOVEL: return(3);
486   case E_MANSION: return(7);
487   case E_HOUSE: return(5);
488   case E_DLAIR: return(9);
489   case E_ABYSS: return(10);
490   case E_STARPEAK: return(9);
491   case E_CIRCLE: return(8);
492   case E_MAGIC_ISLE: return(8);
493   case E_TEMPLE: return(8);
494   default: return(3);
495   }
496 }
497 #endif
498