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