1 /* Copyright 2000 David Chess; Copyright 2005-2006 Sam Trenholme
2 *
3 * Slump is free software; you can redistribute it and/or modify it under
4 * the terms of the GNU General Public License as published by the Free
5 * Software Foundation; either version 2, or (at your option) any later
6 * version.
7 *
8 * Slump is distributed in the hope that it will be useful, but WITHOUT ANY
9 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with Slump; see the file GPL. If not, write to the Free
15 * Software Foundation, 59 Temple Place - Suite 330, Boston, MA
16 * 02111-1307, USA.
17 *
18 * Additionally, while not required for redistribution of this program,
19 * the following requests are made when making a derived version of
20 * this program:
21 *
22 * - Slump's code is partly derived from the Doom map generator
23 * called SLIGE, by David Chess. Please inform David Chess of
24 * any derived version that you make. His email address is at
25 * the domain "theogeny.com" with the name "chess" placed before
26 * the at symbol.
27 *
28 * - Please do not call any derivative of this program SLIGE.
29
30 SLUMP - a modern port of SLIGE: May 2005
31
32 Original notes:
33
34 SLIGE - a random-level generator for DOOM
35 by David M. Chess
36
37 http://www.davidchess.com/
38 http://users.aol.com/dmchess/
39 http://www.doomworld.com/slige/
40
41 January, 2000
42
43 This source code is made available to the world in general,
44 with the following requests:
45
46 - If you're doing a port and you find that something or other needs
47 twiddling to get it to compile, let me know and I'll stick in some
48 ifdefs. The current code assumes that Microsoft C has a horrible
49 rand() function and uses str(n)icmp() instead of str(n)casecmp(),
50 whereas everyone else has an ok rand() and str(n)casecmp(). And
51 make sure that you compile with tight structure-packing (/Zp1 or
52 -fpack_struct, I think) or DOOM won't understand the files. Also,
53 if you want to port to a non-Intel-byte-order platform, you've got
54 a bit of work, but I think it's mostly isolated to DumpLevel,
55 CloseDump, dump_texture_lmp, and a few others. Good luck! *8)
56
57 - The current version should compile without any special switches
58 except structure-packing, under both MSVC++ and DJGPP, without
59 any warnings (unless you use -O3 in DJGPP, in which case you'll
60 get some warnings that don't matter). It should also compile
61 and work under Linux gcc, but I've never tried it myself; if you
62 do, write and tell me about it! Anyone wanna do a Mac port?
63 I'd be glad to give advice...
64
65 - If you make various changes and improvements to it and release
66 a modified version yourself, see the above GPL license with two
67 optional (but strongly desired by the development team) requests.
68
69 - If you want to do lots of wonderful things to the program to
70 make it better, and then send me the result to incorporate into
71 my next version, I strongly suggest telling me your plans first.
72 Otherwise you may hear nothing from me for weeks after sending me
73 your source mods (I don't check my mail that often sometimes), and
74 then get a disappointing "thanks, but I don't want to take the
75 program that direction, I have other plans" sort of reply. Small
76 bug fixes, of course, are always welcome! All source mods sent
77 to me become the property of the world at large.
78
79 Otherwise, enjoy the program, and let me know what you think of the code!
80
81 DC
82
83 */
84 /*
85
86 New stuff (since 474): first try at random DM starts/weapons, fix rare
87 trap in falling core generation, add -nosemo switch, fix(?) bug from
88 rellwood that could get you trapped on a step-dias, -bimo/!/we switches,
89 no jambs without doors, remove locked gates in DM (per Jim Kneuper),
90 make extra-huge more likely in DM.
91
92 Stuff to do:
93 BUG: if there's a gate leading to the room containing the gate leading
94 to the arena, that first-mentioned gate ?sometimes doesn't work;
95 see slige (475) -e2m1 -config blue.cfg -arena -seed 1098402211.
96 (Looks like the in-between gate has no sector tag and no TP exit in it.)
97 BUG: very rarely, a slige -dm level will still not have four DM starts.
98 Try more rooms than just first and last, and/or use a smaller "width"
99 to allow putting a DM start on a pickable object?
100 fold in the RISC OS mods from Justin Fletcher,
101 SPARC mods from Oliver Kraus
102 fold in Rob Ellwood's ceiling-effect and split_linedef mods,
103 when he has a version he likes enough
104 armor-damage model is wrong: green armor only absorbs 1/3 of
105 damage taken; only blue armor absorbs 1/2. Fix!
106 BUG: when trigger_box() calls point_sector(), it should check
107 the "danger" return (rather than passing in NULL)
108 more lamps/lights when night and/or dim?
109 very-dark secret-level flavor?
110 level flavor like "always big floordeltas"
111 implement the "favorite monster" style of secret level
112 simple multi-level "ramps" in patios, for variety?
113 sometimes monsters can be so tightly packed into a small room
114 that none can move until you kill one. Detect/avoid somehow.
115 eliminate "ladders", by having random_basic_link() just make sure that
116 if there are stairs abs(floordelta)<=linkdepth?
117 switches mounted on columns/placards, not just in walls
118 nukage in downsecs of falling-core traps, sometimes. Barrels, too!
119 regularize lighting (less ad-hoc, more constants), have dark
120 secret levels, perhaps darker style, perhaps dark rooms with
121 bright otherstuff, etc.
122 secret doors/closets at the ends of ambush closets
123 configify lamps and barrels
124 configify other objects and monsters and anything else that's left
125 unhardcode health's amounts, weapons' damage, etc
126 shouldn't "error" and "gateexitsign" just be property bits?
127 fixed the bug that sometimes accidentally made the monster hiding
128 on top of a pillar be down in the room; but it'd be cool to
129 sometimes do this on purpose!
130 trees can block patio doors; fix
131 BUG: A raised teleporter right next to a step-up doorway can be
132 inaccessible. Fix!
133 once in awhile a barrel in a secret, to discourage the "open and
134 shoot without looking" trick. heh heh.
135 have use of lights (gridlights, lightstrips, kickplate lights, etc)
136 be correlated per level or config or something, not random
137 use CEILING+LIGHT in even more lighting effects (link to
138 light_recesses, for instance, and maybe use in windows)
139 do mancubus-special in MAP07 even if not last_mission
140 do arach-special in MAP07 also (somehow)
141 place_timely_something() and friends need an "is it a secret?"
142 switch, then could be used in various secret places.
143 Secret grid-pillars need something more significant on them!
144 Biggest-monster mode in DOOM 1 gives lots and lots and lots and
145 lots of spectres... (if BIG is off, I suspect) Problem?
146 Still the occasional level with many too many rooms; heh!
147 BUG: the embellishment of the outersec of a rising room
148 can decide to triggerbox the key, even though the rising room
149 has already boxed it. Fix. (Actually both boxes seem to work,
150 even though they're exactly coextensive, but still...)
151 BUG: have seen at least one extwindow that extended orthogonal to
152 the direction it should have extended. Very odd!
153 BUG: still getting a monster stuck in a floor-lamp now and then? how?
154 also perhaps a monster stuck in a grid-room pillar (or was that two
155 grid-room monsters stuck to each other?) (Those may be fixed now.)
156 Also once saw a Hell Knight stuck between a new-style "pillar" and
157 a wall. Looked like.
158 BUG: Sometimes a room has two different gates, both in the midtile.
159 Well, you know what I mean... This causes trouble. Fixed?
160 Nope; still can happen with a gate to an arena. Is that OK?
161 More exit-to-secret-level secret kinds.
162 More special things about secret levels. A black-and-starry
163 theme using custom patches?
164 For co-op, put all the weapons that we assume the player has
165 into the start room (with multiplayer-only bit set).
166 For DM, put a DM start (with a multiplayer-only weapon nearly)
167 in every Nth room? This conflicts with the above; maybe -dms?
168 It would be nice to not give up on a bath whenever a monster might
169 get stuck in the edge. Try *moving* the monsters, or the edges
170 of the bath, instead?
171 Make arenas even more interesting. powerups/armor? More scenarii!
172 dead-gardens flavor
173 More interesting sorts of GATE_GOAL-locked links (like just
174 a grated door). Also have the gate be one-way if the locked
175 link is passible in the far-to-near direction.
176 More Instaforks! (Really just pushes, not forks at all)
177 twinned links need to have their extra-painted-door bits and
178 nukage-link status and stuff correlated, eh?
179 read switches from config file
180 constructs (computers, at least) hanging from the ceiling?
181 more rational facings in grid-rooms (sort of toward the door)
182 have the new-pillar probability in the style, and have it sometimes
183 be very very high?
184 more open-link stuff: gratings, locks, secrets, monsters, etc, etc
185 sometimes have open(ish) links block sound, to avoid excess monster
186 accumulation? (Doesn't seem to be a problem?)
187 open lifts sometimes activate at the top also
188 sometimes blaze autodoors / lifts
189 monsters/pickables in sidesectors of an open link
190 maybe have a minimum-link-width thing (used only on non-door links,
191 maybe?), usually zero, and sometimes (often when bigification is
192 high) higher (like 128). For a non-narrow level, but not hugeness
193 put nukage-edge textures (bottom-aligned) around nukage cores (and
194 normal nukage pools, eh?) Need new attribute bit/thing
195 put pillars with nukage-pourers (green gunk vents, red demon
196 faces, various leaking pipes) in nukage pools
197 use step-textures and/or wall textures for/instead of support
198 textures in various other kickplate places (below doors?)
199 put decorations up on the centers of some pillars (/constructs)
200 I suspect it's possible for a sequence of new-style pillars to
201 block off the center of the room (thus perhaps making it impossible
202 to get a key). Implement fix if so.
203 probably don't want to put triggerboxes around candles, eh? They
204 aren't *really* pickable, and the player is unlikely to go around
205 stepping on all of them for any reason. Not a big deal.
206 make the sunrooms and nukage-city effects less correlated (if nukage
207 was forced, have sky lower-prob, and vice-versa)
208 Other patio things:
209 There are often no plants. That "128" is possibly too big?
210 Store floor and wall textures in style? or even level?
211 Patios should count toward the level's (the quest's?) room count, eh?
212 Patios would really make more sense as a kind of microquest, rather
213 than a mere embellishment
214 Make sure "roof" height is also big enough to accomodate the
215 link->height1, and for that matter the door's height (twice),
216 if there's a door
217 in homogenize mode, that hack that will always add one more of the
218 room's monsters if there's room for *any* monster can produce
219 very painful results (the Revenant Brothers, in particular).
220 Fix it? Just separate out the "is there room for monster M in
221 the model" check into a routine to call in homgen mode.
222 homogenize_monsters, should be per-level, not per-WAD, eh? or should it?
223 once had a really really steep stairway with a monster on it. The
224 stairway was so narrow I couldn't get around the monster, but so
225 steep that I couldn't see it to shoot it! avoid? (I actually
226 managed to sneak around it eventually, but it got in lots of bites.)
227 probably just put no link-guards on steep stairs, eh?
228 y-offsets pretty good now; put some support texture in when two
229 coalignable textures come together at a base-ceiling boundary?
230 that's about the only place that y-offset problems remain
231 some cool light-effect around pillars (new and old styles)
232 find all monster-width assumptions (tough!), generalize or whtever
233 merge secret-closet and plaque-closet code sections (diffs: plaques
234 are recessed, and are sometimes not openable at all)
235 way-cool recessed windows style
236 traditional locked nop-DOOR3 sometimes in entry room?
237 sometimes put a rising room at the end of tag-switch quests, too?
238 allow embellishing far sides of links (w/lightboxes etc?)
239 sometimes vertical lightstrips on walls / in corners (won't happen
240 in Wod theme, because no "LIGHT" walls anymore; OK?)
241 more extensive outdoor areas; need a theory
242 non-rectangular rooms (lots of work there!)
243 same texture on both sides of a door?
244 before giving up finding a linedef to put a link on, split
245 all too-big linedefs around the current room
246 if a lift can be walked off of, leave out the WR-lower linedef
247 at the top end. sometimes
248 raised sniper-closets
249 make level-exits look even more obvious in DOOM I somehow?
250 sometimes put keys in ambush closets, other special places
251 sometimes have the back wall of a closet be a secret door (hint?)
252 have big monsters / weapons (only) in later episodes / maps?
253 use of has_chainsaw/berserk on !(FLIES|SHOOTS) working OK?
254 use has_chaingun information for something. remove wimp-bonus?
255 chainguns tend to be ammo-inefficient; recognize?
256 silly to put in the linedef box around an object before checking if
257 there's enough room for the secret closet, eh? fixed?
258 have plaque-doors sometimes be triggered also
259 more sophisticated backpack counting (depending on the weapon(s)
260 the player has)
261 model the invis and radsuit better?
262 a big boss monster in a big secret room that lowers down when you
263 take pickable. also in the boss' room is the key/switch you need.
264 have to make sure player has enough ammo/health first. details?
265 put a single window-grating in the center, not two half-invisible
266 ones on the sides
267 don't watermark the first room of non-first levels in a PWAD?
268 keep them TLITE ceilings on door-recess sectors (and stuff);
269 looks way cool! (CEILING+LIGHT, now)
270 autodoors and lifts are sort of boring currently (only 24-deep rec's).
271 make them more interesting?
272 plain-sky ceilings don't work, because if a nearby room has a
273 slightly higher non-sky ceiling, or worse is a steep stairway
274 goes up out of the room, it looks really dumb. think about fixes.
275 perhaps always raise the ceiling to be at least as high as all
276 ceilings of attached rooms? that simple?
277 the support_misaligns stuff looks awful. think about this some more.
278 skyclosets are still silly-looking if you can stand in the end and
279 look back at the "building". Either fix somehow (how?), or use
280 point_froms to put the skylight only at the far end of the closet,
281 even in deep closets, so you can't see out from the end. Also,
282 the lip of the outer sides of the skycloset walls isn't necessary;
283 would look best, I think, with a lip on the near side, and the
284 other sides just ending blap like they used to.
285 deathmatch starts, somehow? Maybe deathmatches
286 in each goal-end room, or something? As well as the start.
287 Other DM-friendly stuff, -deathmatch switch.
288 sometimes use a light-wall texture on walls (especially flanking
289 doors/links?) But there aren't that many of them.
290 have a "minwall" in config, to usually forbid those skinny little
291 4-pel-wide walls and like that. (done a different way, but we
292 might want to think about something >8 as a config option)
293 multiple snipers/ambushers in a single (wider) closet?
294 OK to have population before embellishment? Should prevent
295 monsters getting stuck in the wall because of swelling, yes?
296 also makes haa-model accesses during embellishment more
297 logical (since probably one cleans out the wandering monsters
298 first, then the embellished?) probably need both pre-pop and
299 post-pop embellishments eventually for something
300 random-link still returns too wide sometimes; that whole routine
301 needs to be re-thought and rationalized (fixed?)
302 use real door-texture widths, not the current hack, to center faces
303 niches: often symettrically around doors, with lights/monsters/items
304 sometimes with (secret) doors, sometimes non-walkable (lights,
305 airholes to the outside, portraits, etc)
306 generalize lightstrips to include counters, plaques, computer
307 displays, even those little closets with monsters and stuff.
308 do basic stairs by first making the core and then splitting it up; this
309 will help auto-alignment, and sort of rationalize things also
310 (can use stairify(), with a little work).
311 swelling a room moves out the apparent corners, and that interferes
312 with later find_rec() calls used during population. Uh-oh!!
313 need true distance-from-point-to-line code. Now that we're
314 embellishing after populating, this isn't as bad.
315 figure out how alignment works on left sidedefs, for split_linedef()
316 (current code OK?)
317 style setting for closet height? variation here is nice, though
318 more interesting window-borders (not just random) and widths
319 very narrow "windows" can of course have lower floors and
320 higher heights, since you can't walk through 'em anyway
321 (just like gratings). Think about how to do in style / link.
322 (Slits sort of provide that now.)
323 window stuff in Link, not in Style, eh?
324 have generate_room_outline sometimes extend the starting
325 linedef with another colinear linedef at one or both
326 ends, so you can come into a room on a *long* side
327 in intersect(), properly check for overlap of colinear segments.
328 use texture-size data more in alignment, door-choice, etc, etc
329 need a copy_link() that does small perturbations to an
330 existing link (like copy_style())
331 restore the ability for twinned links to have different
332 door types? Say! How about allowing the two links
333 of a twinned link to be *completely different? Well,
334 they have to have the same total depth and floordelta,
335 anyway. Hmm...
336 on the other hand, it'd be logical if the door-types at
337 either end of a link were the same. That means having
338 it in the link, not the room's style. Sensible? On
339 the third hand, they needn't always be the same, and
340 for that matter couldn't you have an alcove with a door
341 at one end, and none at the other?
342 allow different-size openings at either end of a link?
343 (tapered stair-walls, e.g.)
344 have special deliverers for weapons if c->weapons_are_special
345 (and then sometimes set that TRUE)
346 more global (per-level or per-config) restrictions on style;
347 never moving jambs, always soundproof doors, doorceilings
348 always/never copy room ceiling, etc, etc? Also pillars
349 and windows and gunkage. (Extend "Nukage City" concept)
350 if link.height1==0, use ceiling height of main room, eh?
351 pick one (at least) 64-aligned 64x64 square, make it a sector,
352 change the ceiling texture to some light, maybe raise or
353 lower the ceiling a bit, and change the light level (up,
354 usually), and perhaps make it blink.
355 need more general object-copiers (for split_linedef etc)
356 generalize the idea that a monster can provide a weapon? (eventually,
357 including in config-file etc)
358 in basic philosophy, we should think more about making the individual
359 room fancy. as it is, the overall flavor is likely to always
360 be a chain of simple rooms. Which is OK, but limited. Put
361 another way, the monsters standing around in a room are dull;
362 the interesting stuff are the monsters in traps, on ledges,
363 behind gratings, etc. Do lots of monster-placement during
364 embellishment?
365 or another way, it's too easy now, just running through rooms
366 killing things. make it harder!
367
368 */
369
370 #include <stdio.h>
371 #include <stdlib.h>
372 #include <string.h>
373 #include <time.h>
374 #include <assert.h>
375 #include "slump.h"
376
377 /* Global variables */
378
379 extern int current_level_number;
380 extern int global_verbosity; /* Oooh, a global variable! */
381 extern boolean ok_to_roll; /* Stop breaking -seed... */
382
383 /* Free up all the allocated structures associated with the */
384 /* level, so we can start on a new one without burning too */
385 /* much memory. */
FreeLevel(level * l)386 void FreeLevel(level *l)
387 {
388 linedef *ld, *ldn;
389 sidedef *sd, *sdn;
390 vertex *v, *vn;
391 thing *t, *tn;
392 sector *s, *sn;
393 link *link, *linkn;
394 style *style, *stylen;
395 arena *arena, *arenan;
396 gate *gate, *gaten;
397
398 for (ld=l->linedef_anchor;ld;ld=ldn) {
399 ldn = ld->next;
400 free(ld);
401 }
402 l->linedef_anchor = NULL;
403 for (sd=l->sidedef_anchor;sd;sd=sdn) {
404 sdn = sd->next;
405 free(sd);
406 }
407 l->sidedef_anchor = NULL;
408 for (v=l->vertex_anchor;v;v=vn) {
409 vn = v->next;
410 free(v);
411 }
412 l->vertex_anchor = NULL;
413 for (t=l->thing_anchor;t;t=tn) {
414 tn = t->next;
415 free(t);
416 }
417 l->thing_anchor = NULL;
418 for (s=l->sector_anchor;s;s=sn) {
419 sn = s->next;
420 free(s);
421 }
422 l->sector_anchor = NULL;
423 for (link=l->link_anchor;link;link=linkn) {
424 linkn = link->next;
425 free(link);
426 }
427 l->link_anchor = NULL;
428 for (style=l->style_anchor;style;style=stylen) {
429 stylen = style->next;
430 free(style);
431 }
432 l->style_anchor = NULL;
433 for (arena=l->arena_anchor;arena;arena=arenan) {
434 arenan = arena->next;
435 free(arena);
436 }
437 l->arena_anchor = NULL;
438 for (gate=l->gate_anchor;gate;gate=gaten) {
439 gaten = gate->next;
440 free(gate);
441 }
442 l->gate_anchor = NULL;
443 }
444
445 /* Get the next unused tag for the level */
new_tag(level * l)446 short new_tag(level *l)
447 {
448 return (short)++(l->last_tag_used);
449 }
450
451 /* Get an unused-color key for the level (if any), and use it. */
452 /* Zero if all are used. */
new_key(level * l)453 short new_key(level *l)
454 {
455 if ((!l->used_red)&&rollpercent(2,33)) {
456 l->used_red = TRUE;
457 return (l->skullkeys) ? ID_REDKEY : ID_REDCARD;
458 } else if ((!l->used_blue)&&rollpercent(3,50)) {
459 l->used_blue = TRUE;
460 return (l->skullkeys) ? ID_BLUEKEY : ID_BLUECARD;
461 } else if ((!l->used_yellow)) {
462 l->used_yellow = TRUE;
463 return (l->skullkeys) ? ID_YELLOWKEY : ID_YELLOWCARD;
464 } else return 0;
465 }
466
467 /* Remove a vertex from the level. Frees the memory, but */
468 /* doesn't do anything about linedefs nor nothin'. */
delete_vertex(level * l,vertex * v)469 void delete_vertex(level *l, vertex *v)
470 {
471 vertex *v1;
472
473 if (v==l->vertex_anchor) {
474 l->vertex_anchor = v->next;
475 } else {
476 for (v1=l->vertex_anchor;v1;v1=v1->next) {
477 if (v1->next==v) {
478 v1->next=v->next;
479 break;
480 }
481 }
482 }
483 free(v); /* oh, that'll help a lot, eh? */
484 }
485
486 /* Add a vertex to the given level at the given place. Return it. */
new_vertex(level * l,int x,int y)487 vertex *new_vertex(level *l,int x,int y)
488 {
489 vertex *answer;
490
491 answer = (vertex *)malloc(sizeof(*answer));
492 answer->x = x;
493 answer->y = y;
494 answer->marked = 0;
495 answer->next = l->vertex_anchor;
496 l->vertex_anchor = answer;
497 return answer;
498 }
499
500 /* Remove a linedef from the level. Frees the memory, but */
501 /* doesn't do anything about sidedefs nor nothin'. */
delete_linedef(level * l,linedef * ld)502 void delete_linedef(level *l, linedef *ld)
503 {
504 linedef *ld1;
505
506 if (ld==l->linedef_anchor) {
507 l->linedef_anchor = ld->next;
508 } else {
509 for (ld1=l->linedef_anchor;ld1;ld1=ld1->next) {
510 if (ld1->next==ld) {
511 ld1->next=ld->next;
512 break;
513 }
514 }
515 }
516 free(ld); /* ooohhh, look, he freed something! */
517 }
518
519 /* Add a linedef to the given level between the given vertexes. No */
520 /* sidedefs or anything are filled in. */
new_linedef(level * l,vertex * from,vertex * to)521 linedef *new_linedef(level *l,vertex *from,vertex *to)
522 {
523 linedef *answer;
524
525 answer = (linedef *)malloc(sizeof(*answer));
526 answer->from = from;
527 answer->to = to;
528 answer->flags = 0;
529 answer->type = LINEDEF_NORMAL;
530 answer->tag = 0;
531 answer->left = NULL;
532 answer->right = NULL;
533 answer->group_next = NULL;
534 answer->group_previous = NULL;
535 answer->next = l->linedef_anchor;
536 answer->marked = 0;
537 l->linedef_anchor = answer;
538 return answer;
539 }
540
541 /* Return a new sector for the given level */
new_sector(level * l,short fh,short ch,flat * ft,flat * ct)542 sector *new_sector(level *l,short fh, short ch, flat *ft, flat *ct)
543 {
544 sector *answer;
545
546 if ((ft==NULL) || (ct==NULL))
547 announce(WARNING,"Null flat in new_sector.");
548 answer = (sector *)malloc(sizeof(*answer));
549 answer->floor_height = fh;
550 answer->ceiling_height = ch;
551 answer->floor_flat = ft;
552 answer->ceiling_flat = ct;
553 answer->light_level = 0;
554 answer->special = 0;
555 answer->tag = 0;
556 answer->marked = 0;
557 answer->style = NULL;
558 answer->entry_x = answer->entry_y = 0;
559 answer->findrec_data_valid = FALSE;
560 answer->has_key = FALSE;
561 answer->has_dm = FALSE;
562 answer->has_dm_weapon = FALSE;
563 answer->middle_enhanced = FALSE;
564 answer->gate = NULL;
565 answer->next = l->sector_anchor;
566 l->sector_anchor = answer;
567 return answer;
568 }
569
570 /* Return a new sector just like the old sector (mostly) */
clone_sector(level * l,sector * s)571 sector *clone_sector(level *l,sector *s)
572 {
573 sector *answer;
574 answer = new_sector(l,s->floor_height,s->ceiling_height,
575 s->floor_flat,s->ceiling_flat);
576 answer->style = s->style;
577 answer->light_level = s->light_level;
578 return answer;
579 }
580
581 /* A new sidedef, similarly, with sensible defaults */
new_sidedef(level * l,sector * s,config * c)582 sidedef *new_sidedef(level *l, sector *s, config *c)
583 {
584 sidedef *answer;
585
586 if (s==NULL) announce(ERROR,"Null sector passed to new_sidedef!");
587 answer = (sidedef *)malloc(sizeof(*answer));
588 answer->x_offset = 0;
589 answer->x_misalign = 0;
590 answer->y_offset = 0;
591 answer->y_misalign = 0;
592 answer->upper_texture = c->null_texture;
593 answer->lower_texture = c->null_texture;
594 answer->middle_texture = c->error_texture;
595 answer->sector = s;
596 answer->isBoundary = 1; /* Do we use this sensibly? */
597 answer->next = l->sidedef_anchor;
598 l->sidedef_anchor = answer;
599 return answer;
600 }
601
602 /* Put down a new thing as given */
new_thing(level * l,int x,int y,short angle,short type,short options,config * c)603 thing *new_thing(level *l, int x, int y, short angle, short type,
604 short options, config *c)
605 {
606 thing *answer;
607
608 if (type==ID_ELEC) announce(VERBOSE,"Tech column");
609 if (type==ID_CBRA) announce(VERBOSE,"Candelabra");
610 if (type==ID_LAMP2) announce(VERBOSE,"Lamp2");
611 if (type==ID_TLAMP2) announce(VERBOSE,"Tlamp2");
612 if (type==ID_LAMP) announce(VERBOSE,"Lamp");
613 answer = (thing *)malloc(sizeof(*answer));
614 answer->x = (short)x;
615 answer->y = (short)y;
616 answer->angle = angle;
617 answer->genus = find_genus(c,type);
618 answer->options = options;
619 answer->next = l->thing_anchor;
620 l->thing_anchor = answer;
621 return answer;
622 }
623
624 /* Return a new arena approprite for the level. */
new_arena(level * l,config * c)625 arena *new_arena(level *l, config *c)
626 {
627 arena *answer = (arena *)malloc(sizeof(*answer));
628 int bossno;
629
630 answer->boss_count = 1; /* Default */
631
632 if (c->mission==8) { /* Do episode-ends canonically */
633 if (c->episode==1) {
634 bossno = 0;
635 } else if (c->episode==2) {
636 bossno = 1;
637 } else {
638 bossno = 2;
639 }
640 } else if (c->map==7) {
641 bossno = 3;
642 #ifdef USING_SPAWNER
643 } else if (c->map==30) { /* Including the end of DooM II, eventually */
644 /* bossno=666; */
645 bossno = 1;
646 #endif
647 } else if (c->map) { /* Otherwise a random DooM II boss, */
648 bossno = roll(2,7);
649 } else { /* Or a random DooM I boss. */
650 bossno = roll(3,3);
651 }
652
653 /* How can we configify all the monsters and weapons in here?? */
654
655 /* Only allow bosses that are in FreeDoom; others are commented out */
656 switch (bossno) {
657 case 0: /* Baron Brothers */
658 /* answer->boss = find_genus(c,ID_BARON);
659 answer->boss_count = 2;
660 if (rollpercent(4,75)) {
661 answer->weapon = find_genus(c,ID_LAUNCHER);
662 answer->ammo = find_genus(c,ID_ROCKBOX);
663 } else {
664 answer->weapon = find_genus(c,ID_CHAINGUN);
665 answer->ammo = find_genus(c,ID_BULBOX);
666 }
667 break; */
668 case 1: /* Cybie */
669 /* answer->boss = find_genus(c,ID_CYBER);
670 if (rollpercent(5,75)) {
671 answer->weapon = find_genus(c,ID_LAUNCHER);
672 answer->ammo = find_genus(c,ID_ROCKBOX);
673 } else {
674 answer->weapon = find_genus(c,ID_BFG);
675 answer->ammo = find_genus(c,ID_CELLPACK);
676 }
677 break; */
678 case 2: /* Spiderboss */
679 /* answer->boss = find_genus(c,ID_SPIDERBOSS);
680 if (rollpercent(6,75)) {
681 answer->weapon = find_genus(c,ID_BFG);
682 answer->ammo = find_genus(c,ID_CELLPACK);
683 } else {
684 answer->weapon = find_genus(c,ID_LAUNCHER);
685 answer->ammo = find_genus(c,ID_ROCKBOX);
686 }
687 break; */
688 case 3: /* Two mancubi (for MAP07, random) */
689 answer->boss = find_genus(c,ID_MANCUB);
690 answer->boss_count = 2;
691 if (rollpercent(7,75)) {
692 answer->weapon = find_genus(c,ID_LAUNCHER);
693 answer->ammo = find_genus(c,ID_ROCKBOX);
694 } else {
695 answer->weapon = find_genus(c,ID_PLASMA);
696 answer->ammo = find_genus(c,ID_CELLPACK);
697 }
698 break;
699 case 4: /* Two pains */
700 /* answer->boss = find_genus(c,ID_PAIN);
701 answer->boss_count = 2;
702 if (rollpercent(8,50)) {
703 answer->weapon = find_genus(c,ID_CHAINGUN);
704 answer->ammo = find_genus(c,ID_BULBOX);
705 } else {
706 answer->weapon = find_genus(c,ID_PLASMA);
707 answer->ammo = find_genus(c,ID_CELLPACK);
708 }
709 break; */
710 case 5:
711 case 6:
712 switch (roll(4,2)) {
713 case 0: /* answer->boss = find_genus(c,ID_ARCHIE); break; */
714 default: answer->boss = find_genus(c,ID_ARACH); break;
715 }
716 answer->boss_count = 2;
717 if (rollpercent(9,75)) {
718 answer->weapon = find_genus(c,ID_LAUNCHER);
719 answer->ammo = find_genus(c,ID_ROCKBOX);
720 } else {
721 answer->weapon = find_genus(c,ID_PLASMA);
722 answer->ammo = find_genus(c,ID_CELLPACK);
723 }
724 break;
725 case 666:
726 /* Just what are we going to do here? */
727 /* answer->boss = find_genus(c,ID_BRAIN); */
728 answer->weapon = find_genus(c,ID_LAUNCHER);
729 answer->ammo = find_genus(c,ID_ROCKBOX);
730 break;
731 default:
732 announce(ERROR,"Arena missing a boss?");
733 }
734
735 answer->props = 0;
736 if (rollpercent(10,20)) answer->props |= ARENA_ROOF;
737 if (rollpercent(11,20)) answer->props |= ARENA_PORCH;
738 if (rollpercent(12,20)) answer->props |= ARENA_LAMPS;
739 if (rollpercent(13,20)) answer->props |= ARENA_ARRIVAL_HOLE;
740 if (rollpercent(14,10+l->p_force_nukage)) answer->props |= ARENA_NUKAGE;
741
742 if (answer->props & ARENA_ROOF) {
743 answer->floor = random_flat0(FLOOR,c,NULL); /* These NULLs OK? */
744 answer->walls = random_texture0(WALL,c,NULL);
745 } else {
746 answer->floor = random_flat0(OUTDOOR,c,NULL);
747 answer->walls = random_texture0(OUTDOOR,c,NULL);
748 }
749 answer->placed_health = FALSE;
750 answer->placed_armor = FALSE;
751 answer->placed_ammo = FALSE;
752 answer->placed_weapon = FALSE;
753 answer->fromtag = 0;
754 answer->next = l->arena_anchor;
755 l->arena_anchor = answer;
756 return answer;
757 }
758
759 /* Between two points */
distancesquared(int x1,int y1,int x2,int y2)760 int distancesquared(int x1, int y1, int x2, int y2)
761 {
762 int xd,yd;
763
764 xd = (x2-x1);
765 yd = (y2-y1);
766
767 return xd*xd + yd*yd;
768 }
769
770 /* Between two points, simple DOOM algorithm */
infinity_norm(int x1,int y1,int x2,int y2)771 int infinity_norm(int x1, int y1, int x2, int y2)
772 {
773 int xd,yd;
774
775 xd = abs(x2-x1);
776 yd = abs(y2-y1);
777
778 if (xd>yd) return xd;
779 else return yd;
780 }
781
782 /* length-sqaured of a linedef */
lengthsquared(linedef * ld)783 int lengthsquared(linedef *ld)
784 {
785 int xd,yd;
786
787 xd = (ld->to->x) - (ld->from->x);
788 yd = (ld->to->y) - (ld->from->y);
789 return xd*xd + yd*yd;
790 }
791
792 /* Return a quest for the very start of the game; always level-end, */
793 /* consult the config for length and stuff. */
starting_quest(level * l,config * c)794 quest *starting_quest(level *l,config *c)
795 {
796 quest *answer;
797
798 answer = (quest *)malloc(sizeof(*answer));
799 answer->goal = LEVEL_END_GOAL;
800 answer->room = NULL; /* won't be used, because this is always stack bottom */
801 answer->tag = 0; /* not a linedef goal */
802 answer->type = LINEDEF_S1_END_LEVEL;
803 answer->count = 0; /* no rooms yet! */
804 answer->minrooms = c->minrooms;
805 answer->auxtag = 0;
806 answer->thing = NULL;
807 answer->surprise = NULL;
808 answer->next = NULL; /* only one so far */
809
810 return answer;
811 }
812
813 /* Return a health/armor/ammo estimate for the game start */
starting_haa(void)814 haa *starting_haa(void)
815 {
816 haa *answer;
817 int i;
818
819 answer = (haa *)malloc(sizeof(* answer));
820
821 for (i=ITYTD;i<=UV;i++) {
822 answer->haas[i].health = (float)100;
823 answer->haas[i].ammo = (float)500;
824 answer->haas[i].armor = (float)0;
825 answer->haas[i].can_use_shells = 0;
826 answer->haas[i].shells_pending = 0;
827 answer->haas[i].has_chaingun = 0;
828 answer->haas[i].chaingun_pending = 0;
829 answer->haas[i].has_chainsaw = 0;
830 answer->haas[i].has_backpack = 0;
831 answer->haas[i].has_berserk = 0;
832 answer->haas[i].has_ssgun = 0;
833 answer->haas[i].can_use_rockets = 0;
834 answer->haas[i].can_use_cells = 0;
835 }
836
837 return answer;
838 }
839
840 /* Mark each boundary linedef from the given sector which isn't */
841 /* already in obvious use, and which is at least minlen long. */
mark_decent_boundary_linedefs(level * l,sector * s,int minlen)842 int mark_decent_boundary_linedefs(level *l,sector *s,int minlen)
843 {
844 linedef *ld;
845 sidedef *sd;
846 int answer = 0;
847
848 minlen = minlen * minlen;
849 for (ld = l->linedef_anchor;ld;ld=ld->next) {
850 if (ld->left) continue;
851 if (ld->type) continue;
852 sd = ld->right;
853 if (sd)
854 if (sd->sector == s)
855 if (sd->isBoundary)
856 if (ld->type==0)
857 if (lengthsquared(ld)>=minlen) {
858 ld->marked = 1;
859 answer++;
860 }
861 }
862 return answer;
863 }
864
865 /* Look at each linedef out of the given sector. Mark it if it's */
866 /* reasonable to consider putting a room on the other side of it. */
867 /* Return the number of linedefs marked. */
mark_adequate_linedefs(level * l,sector * s,style * ThisStyle,config * c)868 int mark_adequate_linedefs(level *l,sector *s,style *ThisStyle,config *c)
869 {
870 linedef *ld;
871 sidedef *sd;
872 int answer = 0;
873
874 for (ld = l->linedef_anchor;ld;ld=ld->next) {
875 sd = ld->right;
876 if (sd)
877 if (sd->sector == s)
878 if (sd->isBoundary)
879 if (isAdequate(l,ld,ThisStyle,c)) {
880 ld->marked = 1;
881 answer++;
882 }
883 }
884 return answer;
885 }
886
887 /* Given that there are i marked linedefs, return a */
888 /* random one of them. NULL if zero. */
random_marked_linedef(level * l,int i)889 linedef *random_marked_linedef(level *l,int i)
890 {
891 linedef *ld;
892
893 if (i==0) return NULL;
894 i = roll(5,i);
895 for (ld=l->linedef_anchor;ld;ld=ld->next) {
896 if (ld->marked) {
897 if (i==0) return ld;
898 i--;
899 }
900 }
901
902 /* Gosh, I *hope* we never get here... */
903 announce(ERROR,"Not enough marked linedefs!");
904 return NULL;
905
906 }
907
908 /* Reset all the linedef marks */
unmark_linedefs(level * l)909 void unmark_linedefs(level *l)
910 {
911 linedef *ld;
912 for (ld=l->linedef_anchor;ld;ld=ld->next) ld->marked = 0;
913 }
914
915 /*
916 Calculates the square root of a 32-bit number.
917 Found somewhere on the net.
918 */
psi_sqrt(int v)919 unsigned short psi_sqrt(int v)
920 {
921 register int t = 1L << 30, r = 0, s;
922
923 #define PSISTEP(k) s = t + r; r >>= 1; if (s <= v) { v -= s; r |= t; }
924
925 PSISTEP(15); t >>= 2;
926 PSISTEP(14); t >>= 2;
927 PSISTEP(13); t >>= 2;
928 PSISTEP(12); t >>= 2;
929 PSISTEP(11); t >>= 2;
930 PSISTEP(10); t >>= 2;
931 PSISTEP(9); t >>= 2;
932 PSISTEP(8); t >>= 2;
933 PSISTEP(7); t >>= 2;
934 PSISTEP(6); t >>= 2;
935 PSISTEP(5); t >>= 2;
936 PSISTEP(4); t >>= 2;
937 PSISTEP(3); t >>= 2;
938 PSISTEP(2); t >>= 2;
939 PSISTEP(1); t >>= 2;
940 PSISTEP(0);
941
942 return (unsigned short) r;
943 }
944
945 /* Find a flat with the given name, creating one if */
946 /* it doesn't already exist. */
find_flat(config * c,char * name)947 flat *find_flat(config *c, char *name)
948 {
949 flat *t = NULL;
950
951 for (t=c->flat_anchor;t;t=t->next)
952 if (!strcmp(name,t->name)) return t;
953 return new_flat(c,name);
954 }
955
956 /* Return a new flat with the given name */
new_flat(config * c,char * name)957 flat *new_flat(config *c,char *name)
958 {
959 flat *answer;
960
961 answer = (flat *)malloc(sizeof(*answer));
962 memset(answer->name,0,9);
963 memcpy(answer->name,name,strlen(name));
964 answer->gamemask = DOOM0_BIT | DOOM1_BIT | DOOM2_BIT | DOOMC_BIT | DOOMI_BIT;
965 answer->compatible = 0;
966 answer->props = 0;
967 answer->used = FALSE;
968 answer->next = c->flat_anchor;
969 c->flat_anchor = answer;
970 return answer;
971 }
972
973 /* Return a new gate with the given attributes and stuff */
new_gate(level * l,short intag,short outtag,short lock,boolean entry,config * c)974 gate *new_gate(level *l, short intag, short outtag, short lock,
975 boolean entry, config *c)
976 {
977 gate *answer = (gate *)malloc(sizeof(*answer));
978 answer->in_tag = intag;
979 answer->out_tag = outtag;
980 answer->gate_lock = lock;
981 answer->is_entry = entry;
982 answer->next = l->gate_anchor;
983 l->gate_anchor = answer;
984 return answer;
985 }
986
987 /* Return a new theme with the given name and secretness. Non-secret */
988 /* themes go at the start of the list, secret ones at the end */
new_theme(config * c,char * name,boolean secret)989 theme *new_theme(config *c,char *name,boolean secret)
990 {
991 theme *answer = (theme *)malloc(sizeof(*answer));
992 theme *t;
993
994 answer->name = strdup(name);
995 answer->secret = secret;
996 if (!secret) { /* Stick it at the end of the non-secrets */
997 c->themecount++;
998 if ((!c->theme_anchor)||c->theme_anchor->secret) {
999 answer->next = c->theme_anchor;
1000 c->theme_anchor = answer;
1001 } else {
1002 for (t=c->theme_anchor;(t->next)&&!t->next->secret;t=t->next) {};
1003 answer->next = t->next;
1004 t->next = answer;
1005 }
1006 } else { /* Stick it at the very end */
1007 c->sthemecount++;
1008 answer->next = NULL;
1009 if (c->theme_anchor) {
1010 for (t=c->theme_anchor;t->next;t=t->next) {};
1011 t->next = answer;
1012 } else {
1013 c->theme_anchor = answer;
1014 }
1015 }
1016 return answer;
1017 }
1018
1019 /* Return a new monster-class with the given thingid */
new_monster(config * c,int thingid)1020 genus *new_monster(config *c, int thingid)
1021 {
1022 genus *answer;
1023 int i;
1024
1025 answer = new_genus(c,thingid);
1026 answer->bits |= MONSTER;
1027 answer->bits &= ~PICKABLE; /* Can't pick up a monster! */
1028 for (i=0;i<3;i++) {
1029 answer->ammo_to_kill[i] = (float)1000; /* Any reason to have defaults? */
1030 answer->damage[i] = (float)1000;
1031 answer->altdamage[i] = (float)1000;
1032 }
1033 answer->ammo_provides = (float)0;
1034 return answer;
1035 }
1036
1037 /* Return a new genus with the given thingid */
new_genus(config * c,int thingid)1038 genus *new_genus(config *c,int thingid)
1039 {
1040 genus *answer;
1041
1042 answer = (genus *)malloc(sizeof(*answer));
1043 /* Default mask */
1044 answer->gamemask = DOOM0_BIT|DOOM1_BIT|DOOM2_BIT|DOOMC_BIT|DOOMI_BIT;
1045 answer->compatible = ~(unsigned long)0; /* Assume all themes OK */
1046 answer->thingid = thingid;
1047 answer->width = 65; /* Sort of sensible default */
1048 answer->height = 56; /* Just "not tall" */
1049 answer->marked = 0;
1050 answer->next = c->genus_anchor;
1051 answer->bits = PICKABLE; /* A plausible default? */
1052 c->genus_anchor = answer;
1053 return answer;
1054 }
1055
1056 /* Return a monster-class with the given thingid, */
1057 /* creating it first if needed. */
find_monster(config * c,int thingid)1058 genus *find_monster(config *c, int thingid)
1059 {
1060 genus *g;
1061
1062 for (g=c->genus_anchor;g;g=g->next) {
1063 if (g->thingid==thingid) return g;
1064 }
1065 return new_monster(c,thingid);
1066 }
1067
1068 /* Return a thing-class with the given thingid, */
1069 /* creating it first if needed. */
find_genus(config * c,int thingid)1070 genus *find_genus(config *c, int thingid)
1071 {
1072 genus *g;
1073
1074 for (g=c->genus_anchor;g;g=g->next) {
1075 if (g->thingid==thingid) return g;
1076 }
1077 return new_genus(c,thingid);
1078 }
1079
1080 /* Find a texture with the given name, creating one if */
1081 /* it doesn't already exist. */
find_texture(config * c,char * name)1082 texture *find_texture(config *c, char *name)
1083 {
1084 texture *t = NULL;
1085
1086 for (t=c->texture_anchor;t;t=t->next)
1087 if (!strcmp(name,t->name)) return t;
1088 return new_texture(c,name);
1089 }
1090
1091 /* Return a new texture with the given name */
new_texture(config * c,char * name)1092 texture *new_texture(config *c,char *name)
1093 {
1094 texture *answer;
1095
1096 answer = (texture *)malloc(sizeof(*answer));
1097 memset(answer->name,0,9);
1098 memcpy(answer->name,name,strlen(name));
1099 answer->realname = answer->name;
1100 answer->gamemask = DOOM0_BIT | DOOM1_BIT | DOOM2_BIT | DOOMC_BIT | DOOMI_BIT;
1101 answer->compatible = 0;
1102 answer->core = 0;
1103 answer->props = 0; /* Filled in later */
1104 answer->width = 256; /* Or some fraction thereof! */
1105 answer->height = 128; /* Except sometimes */
1106 answer->y_hint = 5;
1107 answer->y_bias = 0;
1108 answer->subtle = NULL;
1109 answer->switch_texture = NULL;
1110 answer->used = FALSE;
1111 answer->next = c->texture_anchor;
1112 c->texture_anchor = answer;
1113 return answer;
1114 }
1115
1116 /* Split the given linedef at the given distance along it. */
1117 /* Return the new (after-splitpoint) linedef. */
split_linedef(level * l,linedef * ld,int len,config * c)1118 linedef *split_linedef(level *l, linedef *ld, int len, config *c)
1119 {
1120 double ratio,q1,q2,q3,q4;
1121 int dx,dy;
1122 vertex *v;
1123 linedef *answer;
1124 char repr[128];
1125
1126 assert(len>0);
1127
1128 /* This code is in this unusual form because GCC optimizations
1129 affect the output of these functions otherwise. The original
1130 code looked like this:
1131
1132 ratio = (double)len / (double)linelen(ld);
1133 dx = (int)(ratio * (double)(ld->to->x - ld->from->x));
1134 dy = (int)(ratio * (double)(ld->to->y - ld->from->y));
1135
1136 But this code generated different numbers at vaious levels of
1137 GCC optimization. Since one goal of slump is to always have
1138 the same seed and flags generate the same levels, I had to change
1139 this code to not be affected by optimization. - Sam */
1140 q1 = (double)len;
1141 q2 = (double)linelen(ld);
1142 if(q2 > 0) {
1143 ratio = q1 / q2;
1144 } else {
1145 ratio = 1000;
1146 }
1147 q3 = (double)(ld->to->x - ld->from->x);
1148 q4 = (double)(ld->to->y - ld->from->y);
1149 q1 = ratio * q3;
1150 q2 = ratio * q4;
1151 sprintf(repr,"%9.0f",q1);
1152 dx = atoi(repr);
1153 sprintf(repr,"%9.0f",q2);
1154 dy = atoi(repr);
1155
1156
1157 v = new_vertex(l,ld->from->x+dx,ld->from->y+dy);
1158 answer = new_linedef(l,v,ld->to);
1159 ld->to = v;
1160 answer->flags = ld->flags;
1161 answer->type = ld->type;
1162 answer->tag = ld->tag;
1163 answer->group_previous = ld;
1164 answer->group_next = ld->group_next;
1165 if (answer->group_next) answer->group_next->group_previous = answer;
1166 ld->group_next = answer;
1167 if (ld->right) {
1168 answer->right = new_sidedef(l,ld->right->sector,c);
1169 answer->right->x_offset = ld->right->x_offset + len;
1170 answer->right->y_offset = ld->right->y_offset;
1171 answer->right->upper_texture = ld->right->upper_texture;
1172 answer->right->lower_texture = ld->right->lower_texture;
1173 answer->right->middle_texture = ld->right->middle_texture;
1174 answer->right->isBoundary = ld->right->isBoundary;
1175 } else {
1176 answer->right = NULL;
1177 }
1178 if (ld->left) {
1179 answer->left = new_sidedef(l,ld->left->sector,c);
1180 answer->left->x_offset = ld->left->x_offset + len; /* is that right? */
1181 answer->left->y_offset = ld->left->y_offset;
1182 answer->left->upper_texture = ld->left->upper_texture;
1183 answer->left->lower_texture = ld->left->lower_texture;
1184 answer->left->middle_texture = ld->left->middle_texture;
1185 answer->left->isBoundary = ld->left->isBoundary;
1186 } else {
1187 answer->left = NULL;
1188 }
1189 answer->marked = ld->marked; /* I suppose */
1190 return answer;
1191 }
1192
1193 /* Put in any upper textures required */
patch_upper(linedef * ld,texture * t,config * c)1194 void patch_upper(linedef *ld,texture *t,config *c)
1195 {
1196 #ifdef TOLERATE_ERRORS
1197 if (ld->left==NULL) return;
1198 #endif
1199 if (ld->right->sector->ceiling_height >
1200 ld->left->sector->ceiling_height) {
1201 if ((ld->right->upper_texture==NULL) ||
1202 (ld->right->upper_texture->name[0]=='-')) {
1203 ld->right->upper_texture = t;
1204 ld->flags |= UPPER_UNPEGGED; /* Seems a good default */
1205 }
1206 }
1207 if (ld->left->sector->ceiling_height >
1208 ld->right->sector->ceiling_height) {
1209 if ((ld->left->upper_texture==NULL) ||
1210 (ld->left->upper_texture->name[0]=='-')) {
1211 ld->left->upper_texture = t;
1212 ld->flags |= UPPER_UNPEGGED; /* Seems a good default */
1213 }
1214 }
1215 }
1216
1217 /* Put in any lower textures required */
patch_lower(linedef * ld,texture * t,config * c)1218 void patch_lower(linedef *ld,texture *t,config *c)
1219 {
1220 #ifdef TOLERATE_ERRORS
1221 if (ld->left==NULL) return;
1222 #endif
1223 if (ld->right->sector->floor_height <
1224 ld->left->sector->floor_height) {
1225 if ((ld->right->lower_texture==NULL) ||
1226 (ld->right->lower_texture->name[0]=='-')) {
1227 ld->right->lower_texture = t;
1228 ld->flags |= LOWER_UNPEGGED; /* Seems a good default */
1229 }
1230 }
1231 if (ld->left->sector->floor_height <
1232 ld->right->sector->floor_height) {
1233 if ((ld->left->lower_texture==NULL) ||
1234 (ld->left->lower_texture->name[0]=='-')) {
1235 ld->left->lower_texture = t;
1236 ld->flags |= LOWER_UNPEGGED; /* Seems a good default */
1237 }
1238 }
1239 }
1240
1241 /* Flip the linedef end-for-end */
flip_linedef(linedef * ld)1242 linedef *flip_linedef(linedef *ld)
1243 {
1244 sidedef *sd;
1245 vertex *v;
1246
1247 v = ld->from;
1248 sd = ld->left;
1249
1250 ld->from = ld->to;
1251 ld->left = ld->right;
1252
1253 ld->to = v;
1254 ld->right = sd;
1255
1256 return ld;
1257 }
1258
Usage0(void)1259 void Usage0(void)
1260 {
1261 printf("Usage: slige [switches] [filename.ext] [switches]\n\n");
1262 printf("Produces a (nodeless) PWAD file that can be completed by a\n");
1263 printf("nodebuilder such as BSP, and then played using the -file\n");
1264 printf("function of DOOM (or DOOM2). The default output file is\n");
1265 printf("SLUMP.OUT. Gets all sorts of data and options and stuff from\n");
1266 printf("SLUMP.CFG (or other file given with the -config switch.)\n\n");
1267 }
1268
Usage(void)1269 void Usage(void)
1270 {
1271 Usage0();
1272 printf("For details, say \"slige -?\".\n\n");
1273 return;
1274 }
1275
1276 /* Remove anything from the config that would be dangerous if left */
1277 /* in, and optionally remove anything that just benignly won't ever */
1278 /* be used and might take up valuable time and space. */
compact_config(config * c)1279 void compact_config(config *c)
1280 {
1281 texture *t;
1282
1283 /* NULLify any texture subtles that aren't in this DOOM version */
1284 for (t=c->texture_anchor;t;t=t->next)
1285 if (t->subtle)
1286 if ((t->subtle->gamemask&c->gamemask)!=c->gamemask) t->subtle = NULL;
1287
1288 /* Now we *could* also remove from the config all textures and flats */
1289 /* and monsters and stuff that aren't in this DOOM, and then skip the */
1290 /* checks later on. Someday... */
1291
1292 }
1293
1294 /* Alter this config to be good and strange for a secret */
1295 /* level. Add stuff to this over time! */
secretize_config(config * c)1296 void secretize_config(config *c)
1297 {
1298 boolean something_special = FALSE;
1299
1300 c->minrooms = c->minrooms * 2 / 3;
1301 if (c->minrooms<4) c->minrooms = 4;
1302 if (c->minrooms>20) c->minrooms = 20;
1303 c->allow_boring_rooms = FALSE;
1304 c->lock_themes = TRUE;
1305 if (rollpercent(16,25)) c->force_biggest = TRUE; /* stub */
1306 c->big_monsters = TRUE;
1307
1308 for (;!something_special;) {
1309
1310 /* Sometimes bizarre theme */
1311 if (rollpercent(17,30)) {
1312 c->secret_themes = TRUE;
1313 something_special = TRUE;
1314 announce(VERBOSE,"Bizarre theme");
1315 }
1316
1317 /* Sometimes lots and lots of nukage */
1318 if (rollpercent(18,30)) {
1319 c->major_nukage = TRUE;
1320 something_special = TRUE;
1321 announce(VERBOSE,"Nukage everywhere");
1322 }
1323
1324 /* Sometimes some DooM II nazis */
1325 if (rollpercent(19,80)&&!(c->gamemask&(DOOM0_BIT|DOOM1_BIT))) {
1326 c->forbidden_monster_bits &= ~SPECIAL;
1327 something_special = TRUE;
1328 if (rollpercent(20,50)) {
1329 c->required_monster_bits |= SPECIAL;
1330 c->required_monster_bits &= ~BIG;
1331 announce(VERBOSE,"All nazis");
1332 } else {
1333 announce(VERBOSE,"Some nazis");
1334 }
1335 }
1336
1337 /* Sometimes some monster thing */
1338 if (rollpercent(21,30) && !something_special) {
1339 if (rollpercent(22,50)) {
1340 c->required_monster_bits |= BIG;
1341 c->required_monster_bits &= ~SPECIAL;
1342 c->big_monsters = TRUE;
1343 announce(VERBOSE,"All big monsters");
1344 something_special = TRUE;
1345 } else {
1346 /* Put in a favorite monster here! */
1347 /* and set something_special */
1348 announce(VERBOSE,"Someday a favorite monster");
1349 }
1350 }
1351
1352 } /* end for */
1353
1354 }
1355
1356 /* Get the configuration data, switches, etc. Five steps: */
1357 /* 1. Parse the arglist to find out where the config file is, */
1358 /* 2. Read through the config file to get other switches, */
1359 /* 3. Parse the arglist to get overrides to switches, */
1360 /* 4. Read the config for non-switches (flats, themes, etc). */
1361 /* 5. Do postproduction defaults and calculations and such. */
get_config(int argc,char * argv[])1362 config *get_config(int argc, char *argv[])
1363 {
1364 config *answer;
1365 int i;
1366 genus *m;
1367
1368 answer = (config *)malloc(sizeof(*answer));
1369
1370 /* Set various defaults and stuff */
1371 answer->configfile = strdup("SLUMP.CFG"); /* So's we kin free() it */
1372 answer->outfile = NULL;
1373 answer->cwadonly = FALSE;
1374 answer->ranseed = (time(0) % 20000) + 1; /* Default seed */
1375 if(answer->ranseed < 2 || answer->ranseed > 20000)
1376 answer->ranseed = 2;
1377
1378 /* Do initial parsing for possible other config file and ranseed */
1379 if (!do_switches(argc,argv,answer,"Command line",1)) return NULL;
1380
1381 init_rng();
1382 rng_set_seed(answer->ranseed);
1383 ok_to_roll = TRUE;
1384
1385 /* Set other defaults, possibly involving random numbers */
1386 answer->theme_anchor = NULL;
1387 answer->flat_anchor = NULL;
1388 answer->texture_anchor = NULL;
1389 answer->construct_anchor = NULL;
1390 answer->genus_anchor = NULL;
1391 answer->null_texture = NULL;
1392 answer->error_texture = NULL;
1393 answer->gate_exitsign_texture = NULL;
1394 answer->sky_flat = NULL;
1395 answer->themecount = 0;
1396 answer->sthemecount = 0;
1397 answer->secret_themes = FALSE;
1398 answer->lock_themes = FALSE;
1399 answer->major_nukage = FALSE;
1400 answer->required_monster_bits = 0;
1401 answer->forbidden_monster_bits = SPECIAL;
1402 /* answer->minrooms = 18; */
1403 answer->minrooms = 17; /* Medium size */
1404 /* answer->gamemask = DOOM1_BIT; */ /* All/Only things supported by DOOM 1.9 */
1405 answer->gamemask = DOOM2_BIT; /* FreeDoom is doom2 not doom1 */
1406 answer->episode = 0;
1407 answer->mission = 0;
1408 answer->last_mission = FALSE;
1409 answer->levelcount = 30; /* Default: Do a megawad */
1410 answer->force_arena = TRUE;
1411 answer->force_biggest = FALSE;
1412 answer->do_music = 0;
1413 answer->secret_monsters = FALSE;
1414 answer->do_dm = 1;
1415 answer->do_slinfo = TRUE;
1416 answer->produce_null_lmps = FALSE;
1417 answer->do_seclevels = TRUE;
1418 answer->force_secret = FALSE;
1419 answer->map = 1;
1420 answer->minlight = 115;
1421 /* Is this the right place for all these? */
1422 answer->immediate_monsters = rollpercent(23,10);
1423 answer->p_hole_ends_level = 0;
1424 if (rollpercent(24,8)) answer->p_hole_ends_level = 100;
1425 if (rollpercent(25,3)) answer->p_hole_ends_level = roll(8,100);
1426 /* These should depend on lastness of the level in the PWAD? */
1427 answer->p_gate_ends_level = 0;
1428 if (rollpercent(26,8)) answer->p_gate_ends_level = 100;
1429 if (rollpercent(27,3)) answer->p_gate_ends_level = roll(9,100);
1430 answer->p_use_steps = 100;
1431 if (rollpercent(28,5)) answer->p_use_steps = roll(10,100);
1432 answer->p_sync_doors = 10;
1433 if (rollpercent(29,50)) answer->p_sync_doors = 100;
1434 if (rollpercent(30,5)) answer->p_sync_doors = roll(11,100);
1435 answer->p_grid_gaps = 0;
1436 if (rollpercent(31,40)) answer->p_grid_gaps = 1;
1437 if (rollpercent(32,10)) answer->p_grid_gaps = roll(12,20);
1438 answer->p_pushquest = 10;
1439 if (rollpercent(33,40)) answer->p_pushquest = 50;
1440 if (rollpercent(34,10)) answer->p_pushquest = roll(13,90);
1441 answer->rad_newtheme = 100;
1442 answer->norm_newtheme = 0;
1443 answer->rad_vary = 100;
1444 answer->norm_vary = 25;
1445 if (rollpercent(35,15)) { /* Some older settings */
1446 announce(VERBOSE,"Old themeing");
1447 answer->rad_newtheme = 12;
1448 answer->norm_newtheme = 4;
1449 answer->rad_vary = 60;
1450 answer->norm_vary = 20;
1451 }
1452 if (rollpercent(36,15)) { /* Sometimes never change themes! */
1453 announce(VERBOSE,"One theme");
1454 answer->rad_newtheme = 0;
1455 answer->norm_newtheme = 0;
1456 answer->rad_vary = 100; /* But change other stuff more */
1457 answer->norm_vary = 60;
1458 }
1459 answer->monsters_can_teleport = TRUE;
1460 if (rollpercent(37,25)) answer->monsters_can_teleport = FALSE;
1461 answer->window_airshafts = rollpercent(38,50);
1462 answer->homogenize_monsters = 0;
1463 if (rollpercent(39,8)) answer->homogenize_monsters = 90;
1464 if (rollpercent(40,15)) answer->homogenize_monsters = roll(14,100);
1465 {
1466 char s[80];
1467 sprintf(s,"Homogenization %d.",answer->homogenize_monsters);
1468 announce(VERBOSE,s);
1469 }
1470 answer->weapons_are_special = FALSE; /* Just mix weapons in with ammo */
1471 answer->recess_switches = rollpercent(41,95);
1472 answer->allow_boring_rooms = rollpercent(42,20);
1473 answer->both_doors = rollpercent(43,50);
1474 answer->doorless_jambs = rollpercent(44,10);
1475 answer->gunk_channels = rollpercent(45,70);
1476 answer->clights = rollpercent(46,50);
1477 answer->machoh = (float)1;
1478 answer->machou = (float)1;
1479 answer->p_bigify = roll(15,100); /* Less uniform? */
1480
1481 /* Initial defaults; at each level, some chance of turning on */
1482 answer->big_weapons = rollpercent(47,50);
1483 if (answer->big_weapons) answer->big_monsters = rollpercent(48,80);
1484 else answer->big_monsters = rollpercent(49,35);
1485
1486 /* Open or whatever the config file */
1487 load_config(answer);
1488
1489 /* Read switch-defaults from the config file */
1490 if (!read_switches(answer)) return NULL;
1491
1492 /* Now scan the command line again */
1493 if (!do_switches(argc,argv,answer,"Command line",0)) return NULL;
1494
1495 /* And finally read in all the hard stuff from the file */
1496 if (!nonswitch_config(answer)) return NULL;
1497
1498 /* Close/free the config stuff */
1499 unload_config(answer);
1500
1501 /* Then we set some final defaulty stuff */
1502 if (answer->outfile==NULL)
1503 answer->outfile = strdup("SLUMP.OUT"); /* So we can free it */
1504 if (answer->error_texture==NULL) /* Use REDWALL if none specified */
1505 answer->error_texture = find_texture(answer,"REDWALL"); /* OK default? */
1506 if (answer->sky_flat==NULL)
1507 answer->sky_flat = find_flat(answer,"F_SKY1"); /* Default */
1508 if (answer->water_flat==NULL)
1509 answer->water_flat = find_flat(answer,"FWATER1"); /* Default */
1510 if (answer->null_texture==NULL)
1511 answer->null_texture = find_texture(answer,"-"); /* Always, really... */
1512
1513 /* And figure some resultants */
1514 for (m=answer->genus_anchor;m;m=m->next) { /* Apply macho factors */
1515 if (!(m->bits&MONSTER)) continue;
1516 m->ammo_to_kill[HMP] *= answer->machoh;
1517 m->damage[HMP] *= answer->machoh;
1518 m->altdamage[HMP] *= answer->machoh;
1519 m->ammo_to_kill[UV] *= answer->machou;
1520 m->damage[UV] *= answer->machou;
1521 m->altdamage[UV] *= answer->machou;
1522 }
1523
1524 if (answer->force_secret) secretize_config(answer);
1525
1526 /* And finally compact out any unneeded/dangerous stuff */
1527 compact_config(answer);
1528
1529 return answer;
1530
1531 }
1532
make_watermark_path(level * l,vertex * v1,vertex * v2,sidedef * rsd,sidedef * lsd)1533 vertex *make_watermark_path(level *l,vertex *v1,vertex *v2,
1534 sidedef *rsd, sidedef *lsd)
1535 {
1536 linedef *ld = new_linedef(l,v1,v2);
1537 ld->flags = TWO_SIDED;
1538 ld->left = lsd;
1539 ld->right = rsd;
1540 return v2;
1541 }
1542
watermark_sector(level * l,sector * s,style * ThisStyle,config * c)1543 void watermark_sector(level *l,sector *s, style *ThisStyle, config *c)
1544 {
1545 /* This isn't a stub except that find_rec is */
1546 /* Well, and it's not clear what you'd do with a */
1547 /* non-rectangular sector */
1548 int minx, miny, maxx, maxy;
1549 int x1, x2, x3, x4;
1550 int y1, y2, y3, y4, y5, y6;
1551 sector *newsector;
1552 sidedef *lsd, *rsd;
1553 vertex *v0, *v1;
1554
1555 /* Make a new sector for the S-shape */
1556 newsector = new_sector(l,s->floor_height,(short)(s->ceiling_height+16),
1557 s->floor_flat,c->sky_flat);
1558 newsector->light_level = l->outside_light_level;
1559 newsector->style = ThisStyle;
1560
1561 /* Figure out the relevant grid-lines */
1562 find_rec(l,s,&minx,&miny,&maxx,&maxy);
1563 x1 = minx + 4;
1564 x4 = maxx - 4;
1565 y6 = miny + 4;
1566 y1 = maxy - 4;
1567 x2 = x1 + (x4-x1)/3;
1568 x3 = x2 + (x4-x1)/3;
1569 y5 = y6 + (y1-y6)/5;
1570 y4 = y5 + (y1-y6)/5;
1571 y3 = y4 + (y1-y6)/5;
1572 y2 = y3 + (y1-y6)/5;
1573
1574 /* Make the two sidedefs that the linedefs will share */
1575 rsd = new_sidedef(l,newsector,c);
1576 rsd->middle_texture = c->null_texture;
1577 rsd->upper_texture = ThisStyle->wall0;
1578 rsd->isBoundary = FALSE;
1579 lsd = new_sidedef(l,s,c);
1580 lsd->middle_texture = c->null_texture;
1581 lsd->isBoundary = FALSE;
1582
1583 /* Now make a whole buncha linedefs */
1584
1585 v0 = new_vertex(l,x1,y1);
1586 v1 = make_watermark_path(l,v0,new_vertex(l,x4,y1),rsd,lsd);
1587 v1 = make_watermark_path(l,v1,new_vertex(l,x4,y2),rsd,lsd);
1588 v1 = make_watermark_path(l,v1,new_vertex(l,x2,y2),rsd,lsd);
1589 v1 = make_watermark_path(l,v1,new_vertex(l,x2,y3),rsd,lsd);
1590 v1 = make_watermark_path(l,v1,new_vertex(l,x4,y3),rsd,lsd);
1591 v1 = make_watermark_path(l,v1,new_vertex(l,x4,y6),rsd,lsd);
1592 v1 = make_watermark_path(l,v1,new_vertex(l,x1,y6),rsd,lsd);
1593 v1 = make_watermark_path(l,v1,new_vertex(l,x1,y5),rsd,lsd);
1594 v1 = make_watermark_path(l,v1,new_vertex(l,x3,y5),rsd,lsd);
1595 v1 = make_watermark_path(l,v1,new_vertex(l,x3,y4),rsd,lsd);
1596 v1 = make_watermark_path(l,v1,new_vertex(l,x1,y4),rsd,lsd);
1597 v1 = make_watermark_path(l,v1,v0,rsd,lsd);
1598
1599 } /* end watermark_sector() */
1600
1601 /* Do segments AB and CD intersect? Algorithm from the Net */
intersects(int XA,int YA,int XB,int YB,int XC,int YC,int XD,int YD)1602 boolean intersects(int XA, int YA, int XB, int YB,
1603 int XC, int YC, int XD, int YD)
1604 {
1605 double r,s;
1606 int r_top, s_top, bottom;
1607
1608 bottom = (XB-XA)*(YD-YC)-(YB-YA)*(XD-XC);
1609 r_top = (YA-YC)*(XD-XC)-(XA-XC)*(YD-YC);
1610 if (bottom==0) /* parallel */
1611 if (r_top!=0) {
1612 return 0; /* proper parallel */
1613 } else { /* colinear; hard case */
1614 return 0; /* This is wrong, of course! But rarely... */
1615 }
1616 s_top = (YA-YC)*(XB-XA)-(XA-XC)*(YB-YA);
1617 r = (double)r_top / (double)bottom;
1618 s = (double)s_top / (double)bottom;
1619 if (r<0) return 0;
1620 if (r>1) return 0;
1621 if (s<0) return 0;
1622 if (s>1) return 0;
1623 return 1;
1624 }
1625
1626 /* Fix up any obvious HOMs with an obvious error texture. */
1627 /* Note that you can still get HOMs with lifts and other */
1628 /* floors and ceilings that move during play. */
global_paint_HOMs(level * l,config * c)1629 void global_paint_HOMs(level *l, config *c)
1630 {
1631 linedef *ld;
1632 for (ld=l->linedef_anchor;ld;ld=ld->next) {
1633 if (ld->right)
1634 if (ld->left) {
1635 patch_upper(ld,c->error_texture,c);
1636 patch_lower(ld,c->error_texture,c);
1637 }
1638 }
1639 }
1640
1641 /* Return the (int number of) a random theme that exists in */
1642 /* the given configuration. Need anything fancier here? */
random_theme(config * c)1643 int random_theme(config *c)
1644 {
1645 int answer;
1646
1647 if (c->secret_themes) {
1648 answer = c->themecount + roll(16,c->sthemecount);
1649 } else {
1650 answer = roll(17,c->themecount);
1651 #ifdef THEME_INVERTER_HACK
1652 answer = (answer==2) ? answer : 1-answer; /* Just an experiment */
1653 #endif
1654 }
1655 {
1656 char s[80];
1657 sprintf(s,"Theme %d.",answer);
1658 announce(VERBOSE,s);
1659 }
1660 return answer;
1661 }
1662
1663 /* Need anything fancier in these next few things? */
1664
1665 /* Linedef type for an ordinary inter-room non-secret door */
random_doortype(level * l,config * c,style * s)1666 short random_doortype(level *l, config *c, style *s)
1667 {
1668 short answer;
1669
1670 answer = LINEDEF_NORMAL_DOOR;
1671 if (rollpercent(50,l->p_s1_door)) answer = LINEDEF_NORMAL_S1_DOOR;
1672 if (!(DOOM0_BIT&c->gamemask) && rollpercent(51,20)) {
1673 if (answer==LINEDEF_NORMAL_DOOR) answer=LINEDEF_BLAZE_DOOR;
1674 if (answer==LINEDEF_NORMAL_S1_DOOR) answer=LINEDEF_BLAZE_S1_DOOR;
1675 announce(VERBOSE,"Blaze door type");
1676 }
1677 return answer;
1678 }
1679
random_slifttype(config * c,style * s)1680 short random_slifttype(config *c, style *s)
1681 {
1682 short answer;
1683
1684 answer = LINEDEF_SR_LOWER_LIFT;
1685 if (!(DOOM0_BIT&c->gamemask) && rollpercent(52,20))
1686 answer = LINEDEF_SR_TURBO_LIFT;
1687 return answer;
1688 }
1689
random_sillheight(config * c,style * s)1690 int random_sillheight(config *c, style *s)
1691 {
1692 if (s->window_grate) {
1693 if (rollpercent(53,50)) return 0;
1694 else return 4 * roll(18,13);
1695 } else return 28 + 4 * roll(19,6);
1696 }
1697
random_windowheight(config * c,style * s)1698 int random_windowheight(config *c, style *s)
1699 {
1700 if (s->window_grate) return 64 + roll(20,16); /* Does this make any sense? */
1701 else return 16 + 4 * roll(21,9);
1702 }
1703
random_windowborder(config * c,style * s)1704 int random_windowborder(config *c, style *s)
1705 {
1706 return 4 + 4 * roll(22,6);
1707 }
1708
random_windowdecor(config * c,style * s)1709 int random_windowdecor(config*c, style *s)
1710 {
1711 switch (roll(23,5)) {
1712 case 0:
1713 case 1: return WINDOW_NORMAL;
1714 case 2: return WINDOW_JAMBS;
1715 case 3: return WINDOW_SUPPORT;
1716 default: return WINDOW_LIGHT;
1717 }
1718 }
1719
random_lightboxlighting(config * c,style * s)1720 int random_lightboxlighting(config*c, style *s)
1721 {
1722 switch (roll(24,4)) {
1723 case 0: return LIGHTBOX_NORMAL;
1724 case 1:
1725 case 2: return LIGHTBOX_LIGHTED;
1726 default: return LIGHTBOX_DARK;
1727 }
1728 }
1729
1730 /* Various plants etc; should be from the config also of course. */
random_plant(config * c,style * s)1731 genus *random_plant(config *c, style *s)
1732 {
1733 genus *answer;
1734 int tcount;
1735
1736 tcount = (c->gamemask & DOOM1_BIT) ? 3 : 4;
1737
1738 switch (roll(25,tcount)) {
1739 case 0: answer = find_genus(c,ID_SMIT);
1740 answer->bits &= ~PICKABLE;
1741 answer->width = 33; break;
1742 case 1: answer = find_genus(c,ID_TREE1);
1743 answer->bits &= ~PICKABLE;
1744 answer->width = 33; break;
1745 case 2: answer = find_genus(c,ID_TREE2);
1746 answer->bits &= ~PICKABLE;
1747 answer->width = 65; break;
1748 case 3:
1749 default: answer = find_genus(c,ID_FBARREL);
1750 answer->bits &= ~PICKABLE;
1751 answer->width = 33; break;
1752 }
1753 return answer;
1754 }
1755
1756 /* Can return NULL if there are no explodables that are */
1757 /* compatible with the theme. */
random_barrel(config * c,style * s)1758 genus *random_barrel(config *c, style *s)
1759 {
1760 return random_thing0(EXPLODES,c,s,0,10000);
1761 }
1762
1763 /* A lamp or similar decoration, tall or short */
random_lamp0(config * c,style * s)1764 genus *random_lamp0(config *c, style *s)
1765 {
1766 genus *answer;
1767
1768 answer = random_thing0(LIGHT,c,s,70,10000);
1769 if (answer==NULL) answer = random_thing0(LIGHT,c,s,0,10000);
1770 return answer;
1771 }
1772
1773 /* A lamp or similar decoration, no taller than a player */
random_shortlamp0(config * c,style * s)1774 genus *random_shortlamp0(config *c, style *s)
1775 {
1776 return random_thing0(LIGHT,c,s,0,56);
1777 }
1778
1779
1780 /* Return the number of a random construct family that contains at */
1781 /* least one construct compatible with this style's theme. */
construct_family_for(config * c,style * s)1782 int construct_family_for(config *c, style *s)
1783 {
1784 construct *cs;
1785 int tmask = 0x01 << s->theme_number;
1786 #define MAX_COMPATIBLE_FAMILIES (5)
1787 int compats[MAX_COMPATIBLE_FAMILIES];
1788 int compat_count = 0;
1789 boolean already;
1790 int i;
1791
1792 for (cs=c->construct_anchor;cs;cs=cs->next) {
1793 if (!(cs->compatible&tmask)) continue;
1794 if ( (cs->gamemask & c->gamemask) != c->gamemask ) continue;
1795 for (already=FALSE,i=0;i<compat_count;i++)
1796 if (compats[i]==cs->family) already = TRUE;
1797 if (already) continue;
1798 compats[compat_count++] = cs->family;
1799 }
1800
1801 if (compat_count==0) {
1802 #ifdef REQUIRE_CONSTRUCTS_IN_ALL_THEMES
1803 announce(WARNING,"No compatible constructs for theme.");
1804 #endif
1805 return -1; /* Whatever */
1806 }
1807
1808 return(compats[roll(26,compat_count)]);
1809 }
1810
1811 /* Make a new style that is in the given theme, and copies the */
1812 /* given style. Vary is a number from 0 to 100 saying how */
1813 /* noisy the copy should be; not linear! If vary is 100, old */
1814 /* can be NULL or whatever. */
copy_style(level * l,style * old,int themenumber,int vary,config * c)1815 style *copy_style(level *l,style *old,int themenumber,int vary,config *c)
1816 {
1817 style *answer;
1818
1819 /* Is this the correct sort of nonlinearity? */
1820
1821 answer = (style *)malloc(sizeof(*answer));
1822 answer->next = l->style_anchor;
1823 l->style_anchor = answer;
1824 answer->theme_number = themenumber;
1825 if (!rollpercent(54,vary)) answer->floor0 = old->floor0;
1826 else answer->floor0 = random_floor0(c,answer);
1827 if (!rollpercent(55,vary)) answer->ceiling0 = old->ceiling0;
1828 else answer->ceiling0 = random_ceiling0(c,answer);
1829 if (!rollpercent(56,vary)) answer->ceilinglight = old->ceilinglight;
1830 else answer->ceilinglight = random_ceilinglight(c,answer);
1831 if (!rollpercent(57,vary)) answer->doorfloor = old->doorfloor;
1832 else answer->doorfloor = random_doorfloor(c,answer);
1833 if (!rollpercent(58,vary)) answer->stepfloor = old->stepfloor;
1834 else answer->stepfloor = random_stepfloor(c,answer);
1835 if (!rollpercent(59,vary)) answer->nukage1 = old->nukage1;
1836 else answer->nukage1 = random_nukage1(c,answer);
1837 if (!rollpercent(60,vary)) answer->doorceiling = old->doorceiling;
1838 else answer->doorceiling = random_doorceiling(c,answer);
1839 if (!rollpercent(61,vary)) {
1840 answer->wall0 = old->wall0;
1841 answer->switch0 = old->switch0;
1842 } else {
1843 answer->wall0 = random_wall0(c,answer);
1844 answer->switch0 = switch0_for(c,answer);
1845 }
1846 if (!rollpercent(62,vary)) answer->kickplate = old->kickplate;
1847 else answer->kickplate = random_kickplate(c,answer);
1848 if (!rollpercent(63,vary)) answer->stepfront = old->stepfront;
1849 else answer->stepfront = random_stepfront(c,answer);
1850 if (!rollpercent(64,vary)) answer->support0 = old->support0;
1851 else answer->support0 = random_support0(c,answer);
1852 if (!rollpercent(65,vary)) answer->doorjamb = old->doorjamb;
1853 else answer->doorjamb = random_doorjamb(c,answer);
1854 if (!rollpercent(66,vary)) answer->widedoorface = old->widedoorface;
1855 else answer->widedoorface = random_widedoorface(c,answer);
1856 if (!rollpercent(67,vary)) answer->narrowdoorface = old->narrowdoorface;
1857 else answer->narrowdoorface = random_narrowdoorface(c,answer);
1858 if (!rollpercent(68,vary)) answer->twdoorface = old->twdoorface;
1859 else answer->twdoorface = random_twdoorface(c,answer);
1860 if (!rollpercent(69,vary)) answer->tndoorface = old->tndoorface;
1861 else answer->tndoorface = random_tndoorface(c,answer);
1862 if (!rollpercent(70,vary)) answer->lockdoorface = old->lockdoorface;
1863 else answer->lockdoorface = random_lockdoorface(c,answer);
1864 if (!rollpercent(71,vary)) answer->walllight = old->walllight;
1865 else answer->walllight = random_walllight(c,answer);
1866 if (!rollpercent(72,vary)) answer->liftface = old->liftface;
1867 else answer->liftface = random_liftface(c,answer);
1868 if (!rollpercent(73,vary)) answer->plaque = old->plaque;
1869 else answer->plaque = random_plaque(c,answer);
1870 if (!rollpercent(74,vary)) answer->redface = old->redface;
1871 else answer->redface = random_redface(c,answer);
1872 if (!rollpercent(75,vary)) answer->blueface = old->blueface;
1873 else answer->blueface = random_blueface(c,answer);
1874 if (!rollpercent(76,vary)) answer->yellowface = old->yellowface;
1875 else answer->yellowface = random_yellowface(c,answer);
1876 if (!rollpercent(77,vary)) answer->lamp0 = old->lamp0;
1877 else answer->lamp0 = random_lamp0(c,answer);
1878 if (!rollpercent(78,vary)) answer->shortlamp0 = old->shortlamp0;
1879 else answer->shortlamp0 = random_shortlamp0(c,answer);
1880 if (!rollpercent(79,vary)) answer->grating = old->grating;
1881 else answer->grating = random_grating(c,answer);
1882 if (!rollpercent(80,vary)) answer->roomlight0 = old->roomlight0;
1883 else answer->roomlight0 = c->minlight +
1884 roll(27,(l->bright_light_level-c->minlight)/2) +
1885 roll(28,(l->bright_light_level-c->minlight)/2);
1886 answer->doorlight0 = answer->roomlight0 + 20 - roll(29,41);
1887 if (!rollpercent(81,vary)) {
1888 answer->wallheight0 = old->wallheight0;
1889 } else {
1890 if (rollpercent(82,20)) { /* More variety in here */
1891 answer->wallheight0 = 256;
1892 } else if (rollpercent(83,50)) {
1893 answer->wallheight0 = 128;
1894 } else {
1895 answer->wallheight0 = 96;
1896 }
1897 }
1898 if (!rollpercent(84,vary)) {
1899 answer->linkheight0 = old->linkheight0;
1900 } else {
1901 if (rollpercent(85,20)) { /* More variety in here */
1902 answer->linkheight0 = 128;
1903 } else if (rollpercent(86,50)) {
1904 answer->linkheight0 = 64;
1905 } else {
1906 answer->linkheight0 = 72;
1907 }
1908 answer->linkheight0 *= l->hugeness;
1909 }
1910 if (!rollpercent(87,vary)) answer->closet_width = old->closet_width;
1911 else {
1912 /* Old narrow method */
1913 answer->closet_width = 64 + roll(30,4) +
1914 roll(250,4) +
1915 roll(251,4) +
1916 roll(252,4);
1917 if (rollpercent(88,50))
1918 /* Something wider? */
1919 answer->closet_width = 64 + 16 * roll(31,5);
1920 }
1921 if (!rollpercent(89,vary)) answer->closet_depth = old->closet_depth;
1922 else {
1923 answer->closet_depth = 64 + roll(32,4) +
1924 roll(253,4) +
1925 roll(254,4) +
1926 roll(255,4);
1927 if (rollpercent(90,40)) answer->closet_depth *= 2;
1928 }
1929 if (!rollpercent(91,vary)) answer->closet_light_delta = old->closet_light_delta;
1930 else answer->closet_light_delta = roll(33,55) - 35;
1931 if (!rollpercent(92,vary)) answer->moving_jambs = old->moving_jambs;
1932 else answer->moving_jambs = rollpercent(93,10);
1933 if (!rollpercent(94,vary)) answer->secret_doors = old->secret_doors;
1934 else answer->secret_doors = rollpercent(95,5);
1935 /* These have to co-vary, because grating determines heights */
1936 if (!rollpercent(96,vary)) {
1937 answer->window_grate = old->window_grate;
1938 answer->sillheight = old->sillheight;
1939 answer->windowheight = old->windowheight;
1940 } else {
1941 answer->window_grate = rollpercent(97,30);
1942 answer->sillheight = random_sillheight(c,answer);
1943 answer->windowheight = random_windowheight(c,answer);
1944 }
1945 if (!rollpercent(98,vary)) answer->light_recesses = old->light_recesses;
1946 else answer->light_recesses = rollpercent(99,30);
1947 if (!rollpercent(100,vary)) answer->do_constructs = old->do_constructs;
1948 else answer->do_constructs = rollpercent(101,80);
1949 if (!rollpercent(102,vary)) answer->light_steps = old->light_steps;
1950 else answer->light_steps = rollpercent(103,10); /* Too low? */
1951 if (!rollpercent(104,vary)) answer->light_edges = old->light_edges;
1952 else answer->light_edges = rollpercent(105,20);
1953 if (!rollpercent(106,vary)) answer->peg_lightstrips = old->peg_lightstrips;
1954 else answer->peg_lightstrips = rollpercent(107,50);
1955 if (!rollpercent(108,vary)) answer->construct_family = old->construct_family;
1956 else answer->construct_family = construct_family_for(c,answer);
1957 if (!rollpercent(109,vary)) answer->window_decor = old->window_decor;
1958 else answer->window_decor = random_windowdecor(c,answer);
1959 if (!rollpercent(110,vary)) answer->lightbox_lighting = old->lightbox_lighting;
1960 else answer->lightbox_lighting = random_lightboxlighting(c,answer);
1961 if (!rollpercent(111,vary)) answer->slitwindows = old->slitwindows;
1962 else answer->slitwindows = rollpercent(112,20);
1963 if (!rollpercent(113,vary)) answer->windowborder = old->windowborder;
1964 else answer->windowborder = random_windowborder(c,answer);
1965 if (!rollpercent(114,vary)) answer->soundproof_doors = old->soundproof_doors;
1966 else answer->soundproof_doors = rollpercent(115,30);
1967 if (!rollpercent(116,vary)) answer->center_pillars = old->center_pillars;
1968 else answer->center_pillars = rollpercent(117,70);
1969 if (!rollpercent(118,vary)) answer->paint_recesses = old->paint_recesses;
1970 else answer->paint_recesses = rollpercent(119,60);
1971 if (!rollpercent(120,vary)) answer->gaudy_locks = old->gaudy_locks;
1972 else answer->gaudy_locks = rollpercent(121,10);
1973 answer->lightboxes = FALSE; /* Ephemeral; usually FALSE */
1974 if (!rollpercent(122,vary)) answer->auxheight = old->auxheight;
1975 else answer->auxheight = roll(34,2) * (8 + 8 * roll(256,8));
1976 if (!rollpercent(123,vary)) answer->auxspecial = old->auxspecial;
1977 else answer->auxspecial = rollpercent(124,80) ? 0 : RANDOM_BLINK;
1978 if (!rollpercent(125,vary)) answer->doortype = old->doortype;
1979 else answer->doortype = random_doortype(l,c,answer);
1980 if (!rollpercent(126,vary)) answer->slifttype = old->slifttype;
1981 else answer->slifttype = random_slifttype(c,answer);
1982 if (!rollpercent(127,vary)) answer->link0 = old->link0;
1983 else answer->link0 = random_link(l,NULL,answer,NULL,c);
1984
1985 return answer;
1986 }
1987
1988 /* Return a new style derived from the given one, based on the config */
1989 /* If "radical", choose a whole new theme. Else don't. */
1990 /* should be. It's not linear! */
new_style(level * l,style * old,boolean radical,config * c)1991 style *new_style(level *l,style *old, boolean radical, config *c)
1992 {
1993 int newtheme = radical ? c->rad_newtheme : c->norm_newtheme;
1994 int vary = radical ? c->rad_vary : c->norm_vary;
1995
1996 if ((!c->lock_themes)&&rollpercent(128,newtheme)) { /* Sometimes entirely new */
1997 return copy_style(l,old,random_theme(c),100,c);
1998 } else if (rollpercent(129,vary)) { /* Sometimes new, same theme */
1999 return copy_style(l,old,old->theme_number,100,c);
2000 } else { /* else partly new, same theme */
2001 return copy_style(l,old,old->theme_number,vary,c);
2002 }
2003 }
2004
2005 /* Return a random style structure according to the configuration */
random_style(level * l,config * c)2006 style *random_style(level *l,config *c)
2007 {
2008 return copy_style(l,NULL,random_theme(c),100,c);
2009 }
2010
2011 /* Shockingly special-purpose routine that puts some stuff into */
2012 /* a room that contains a gate in the midtile. */
gate_populate(level * l,sector * s,haa * haa,boolean first,config * c)2013 void gate_populate(level *l,sector *s,haa *haa, boolean first, config *c)
2014 {
2015 int minx,miny,maxx,maxy;
2016 short tlx, tly, thx, thy;
2017 genus *m;
2018 int levels;
2019
2020 if (first) return; /* punt! */
2021
2022 find_rec(l,s,&minx,&miny,&maxx,&maxy);
2023 mid_tile(l,s,&tlx,&tly,&thx,&thy);
2024
2025 if (tlx-minx>63) { /* "63"s are all wrong */
2026 if (rollpercent(130,50)) { /* A monster */
2027 m = timely_monster(haa,c,&levels,rollpercent(131,l->p_biggest_monsters),1);
2028 if (levels)
2029 if (NULL!=place_object_in_region(l,minx,miny,tlx,maxy,
2030 c,m->thingid,MONSTER_WIDTH(m),-1,s->entry_x,s->entry_y,
2031 levels)) update_haa_for_monster(haa,m,levels,1,c);
2032 } else {
2033 place_timely_something(l,haa,c,(minx+tlx)/2,(miny+maxy)/2);
2034 }
2035 }
2036 if (maxx-thx>63) { /* "63"s are all wrong */
2037 if (rollpercent(132,50)) { /* A monster */
2038 m = timely_monster(haa,c,&levels,rollpercent(133,l->p_biggest_monsters),1);
2039 if (levels)
2040 if (NULL!=place_object_in_region(l,thx,miny,maxx,maxy,
2041 c,m->thingid,MONSTER_WIDTH(m),-1,s->entry_x,s->entry_y,
2042 levels)) update_haa_for_monster(haa,m,levels,1,c);
2043 } else {
2044 place_timely_something(l,haa,c,(thx+maxx)/2,(miny+maxy)/2);
2045 }
2046 }
2047
2048 /* Absurd duplication! */
2049 if (tly-miny>63) { /* "63"s are all wrong */
2050 if (rollpercent(134,50)) { /* A monster */
2051 m = timely_monster(haa,c,&levels,rollpercent(135,l->p_biggest_monsters),1);
2052 if (levels)
2053 if (NULL!=place_object_in_region(l,minx,miny,maxx,tly,
2054 c,m->thingid,MONSTER_WIDTH(m),-1,s->entry_x,s->entry_y,
2055 levels)) update_haa_for_monster(haa,m,levels,1,c);
2056 } else {
2057 place_timely_something(l,haa,c,(minx+maxx)/2,(miny+tly)/2);
2058 }
2059 }
2060 if (maxy-thy>63) { /* "63"s are all wrong */
2061 if (rollpercent(136,50)) { /* A monster */
2062 m = timely_monster(haa,c,&levels,rollpercent(137,l->p_biggest_monsters),1);
2063 if (levels)
2064 if (NULL!=place_object_in_region(l,minx,thy,maxx,maxy,
2065 c,m->thingid,MONSTER_WIDTH(m),-1,s->entry_x,s->entry_y,
2066 levels)) update_haa_for_monster(haa,m,levels,1,c);
2067 } else {
2068 place_timely_something(l,haa,c,(minx+maxx)/2,(thy+maxy)/2);
2069 }
2070 }
2071
2072 /* And finally do weapon pickups */
2073 haa_unpend(haa);
2074 }
2075
2076 /* Put monsters and health and armor and stuff in the room */
2077 /* Update the health/ammo/armor estimate structure also */
populate(level * l,sector * s,config * c,haa * haa,boolean first_room)2078 void populate(level *l,sector *s,config *c,haa *haa,boolean first_room)
2079 {
2080 if ((!first_room)||(c->immediate_monsters))
2081 place_monsters(l,s,c,haa);
2082 place_health(l,s,c,haa);
2083 place_ammo(l,s,c,haa);
2084 place_armor(l,s,c,haa);
2085 place_barrels(l,s,c,haa);
2086 return;
2087 }
2088
2089 /* Taking all the given stuff into account, have we put */
2090 /* enough rooms into the current quest yet? */
2091 /* This routine can also mess with the current quest, */
2092 /* to do special end-stuff, like arenas. */
enough_quest(level * l,sector * s,quest * ThisQuest,config * c)2093 boolean enough_quest(level *l,sector *s,quest *ThisQuest,config *c)
2094 {
2095 #ifndef NOT_DOING_ARENAS
2096 /* Perhaps an arena? */
2097 if ( (ThisQuest->goal==LEVEL_END_GOAL) &&
2098 (s!=l->first_room) &&
2099 (!c->do_dm) &&
2100 ( (l->sl_tag!=0) || !need_secret_level(c) ) &&
2101 ( (l->sl_tag==0) || l->sl_done ) &&
2102 (ThisQuest->count >= (ThisQuest->minrooms - 5)) ) {
2103 if ( (c->mission==8) ||
2104 (c->map==30) ||
2105 ((c->map==7)&&(c->last_mission)) ||
2106 (c->last_mission&&(c->force_arena||rollpercent(138,3*c->levelcount))) ) {
2107 ThisQuest->goal = ARENA_GOAL;
2108 return 1;
2109 }
2110 }
2111 #endif
2112 /* Don't stop a GATE_QUEST at an already-gate room */
2113 if (ThisQuest->goal==GATE_GOAL)
2114 if (s->gate) return 0;
2115 /* Otherwise the ordinary check. */
2116 if (ThisQuest->count >= ThisQuest->minrooms) return 1;
2117 return 0;
2118 }
2119
2120 /* Process the switches in the given arg array, filling in the */
2121 /* given config structure. Use s in error messages. If conly, */
2122 /* all we're parsing for here are -config and -seed. */
2123 /* Print msg and return FALSE if error, else return TRUE. */
do_switches(int argc,char * argv[],config * c,char * s,int conly)2124 boolean do_switches(int argc,char *argv[],config *c,char *s,int conly)
2125 {
2126 int i;
2127
2128 if (conly) { /* config, seed, -v only; imperfect algorithm! Can be fooled. */
2129 for (i=1;i<argc;i++) {
2130 if (!stricmp(argv[i],"-config")) {
2131 if (i<(argc-1)) { /* If -config is the last arg, just ignore it */
2132 c->configfile = strdup(argv[++i]);
2133 } /* end if enough args */
2134 } else if (!stricmp(argv[i],"-seed")) {
2135 c->ranseed = atoi(argv[++i]) % 20020;
2136 if(c->ranseed < 1 || c->ranseed > 20020)
2137 c->ranseed = 1;
2138 } else if (!stricmp(argv[i],"-v")) {
2139 global_verbosity = 1;
2140 } /* end if -config arg */
2141 } /* end for args */
2142 /* Now we have the seed, from timer or cmdline, so use it */
2143 srand(c->ranseed);
2144 {
2145 char logstring[60];
2146 sprintf(logstring,"Seed: %d",c->ranseed);
2147 announce(LOG,logstring);
2148 }
2149 } else { /* not conly */
2150 for (i=1;i<argc;i++) {
2151 if (argv[i][0]!='-') {
2152 c->outfile = strdup(argv[i]); /* Just take last if multiple */
2153 } else if (!stricmp(argv[i],"-?")) {
2154 Usage2();
2155 exit(100);
2156 } else if (!stricmp(argv[i],"-outfile")) {
2157 if (i<(argc-1)) { /* If this is the last arg, just ignore it */
2158 c->outfile = strdup(argv[++i]);
2159 } /* end not last arg */
2160 } else if (!stricmp(argv[i],"-doom")) {
2161 c->gamemask = DOOM1_BIT;
2162 if (c->episode==0) c->episode = c->mission = 1;
2163 c->map = 0;
2164 } else if (!stricmp(argv[i],"-doom2")) {
2165 c->gamemask = DOOM2_BIT;
2166 c->episode = c->mission = 0;
2167 if (c->map==0) c->map = 1;
2168 } else if (!stricmp(argv[i],"-nogross")) {
2169 c->gamemask |= DOOMC_BIT;
2170 } else if (!stricmp(argv[i],"-v")) {
2171 global_verbosity = 1;
2172 } else if (!stricmp(argv[i],"-cwad")) {
2173 c->cwadonly = TRUE;
2174 } else if (!stricmp(argv[i],"-nocustom")) {
2175 c->gamemask |= DOOMI_BIT;
2176 } else if (!stricmp(argv[i],"-nulls")) {
2177 c->produce_null_lmps = TRUE;
2178 } else if (!stricmp(argv[i],"-noslinfo")) {
2179 c->do_slinfo = FALSE;
2180 } else if (!stricmp(argv[i],"-noseclevels")) {
2181 c->do_seclevels = FALSE;
2182 } else if (!stricmp(argv[i],"-bimo!")) {
2183 c->force_biggest = TRUE;
2184 } else if (!stricmp(argv[i],"-bimo")) {
2185 c->big_monsters = TRUE;
2186 } else if (!stricmp(argv[i],"-biwe")) {
2187 c->big_weapons = TRUE;
2188 } else if (!stricmp(argv[i],"-xsecret")) {
2189 c->force_secret = TRUE;
2190 } else if (!stricmp(argv[i],"-gross")) {
2191 c->gamemask &= ~DOOMC_BIT;
2192 } else if (!stricmp(argv[i],"-music")) {
2193 c->do_music = TRUE;
2194 } else if (!stricmp(argv[i],"-nosemo")) {
2195 c->secret_monsters = FALSE;
2196 } else if (!stricmp(argv[i],"-dm")) {
2197 c->do_dm = TRUE;
2198 } else if (!stricmp(argv[i],"-arena")) {
2199 c->force_arena = TRUE;
2200 } else if (!stricmp(argv[i],"-levels")) {
2201 if (i<(argc-1)) { /* If this is the last arg, just ignore it */
2202 c->levelcount = atoi(argv[++i]);
2203 if (c->levelcount==0) {
2204 fprintf(stderr,"%s error: invalid -levels arg <%s>.\n",
2205 s,argv[i]);
2206 return FALSE;
2207 }
2208 } /* end if enough args */
2209 } else if (!stricmp(argv[i],"-minlight")) {
2210 if (i<(argc-1)) { /* If this is the last arg, just ignore it */
2211 c->minlight = atoi(argv[++i]);
2212 if (c->minlight==0) {
2213 fprintf(stderr,"%s error: invalid -minlight arg <%s>.\n",
2214 s,argv[i]);
2215 return FALSE;
2216 }
2217 } /* end if enough args */
2218 } else if (!stricmp(argv[i],"-macho")) {
2219 int mfac;
2220 if (i<(argc-1)) { /* If this is the last arg, just ignore it */
2221 mfac = atoi(argv[++i]);
2222 if ((mfac<1) || (mfac>100)) {
2223 fprintf(stderr,"%s error: -macho must be in [1,100], not <%s>.\n",
2224 s,argv[i]);
2225 return FALSE;
2226 }
2227 c->machoh = ((float)100 - (float)mfac/(float)4) / (float)100;
2228 c->machou = ((float)100 - (float)mfac/(float)2) / (float)100;
2229 } /* end if enough args */
2230 } else if (!stricmp(argv[i],"-restrict")) {
2231 if (i<(argc-1)) { /* If this is the last arg, just ignore it */
2232 int j;
2233 i++;
2234 c->gamemask = 0;
2235 for (j=0;j<(int)strlen(argv[i]);j++) {
2236 if (argv[i][j]=='C') c->gamemask |= DOOMC_BIT;
2237 else if (argv[i][j]=='I') c->gamemask |= DOOMI_BIT;
2238 else if (argv[i][j]=='0') c->gamemask |= DOOM0_BIT | DOOM1_BIT;
2239 else if (argv[i][j]=='1') c->gamemask |= DOOM1_BIT;
2240 else if (argv[i][j]=='2') c->gamemask |= DOOM2_BIT;
2241 else {
2242 fprintf(stderr,"%s error: invalid -restrict arg <%s>.\n",
2243 s,argv[i]);
2244 return FALSE;
2245 }
2246 } /* end for bits of next arg */
2247 } /* end if enough args */
2248 } else if ((strlen(argv[i])==5) &&
2249 ( (argv[i][1]=='E') || (argv[i][1]=='e') ) &&
2250 ( (argv[i][3]=='M') || (argv[i][3]=='m') ) ) {
2251 if (2!=sscanf(argv[i],"-%*c%d%*c%d",&(c->episode),&(c->mission))) {
2252 fprintf(stderr,"%s error: Invalid -ExMx switch <%s>.\n",s,argv[i]);
2253 return FALSE;
2254 }
2255 c->map = 0;
2256 } else if (!strnicmp(argv[i],"-map",4)) {
2257 c->map = atoi(argv[i]+4);
2258 if (c->map==0) {
2259 fprintf(stderr,"%s error: Invalid -MAPxx switch <%s>.\n",s,argv[i]);
2260 return FALSE;
2261 }
2262 c->episode = c->mission = 0;
2263 } else if (!stricmp(argv[i],"-rooms")) {
2264 if (i<(argc-1)) { /* If this is the last arg, just ignore it */
2265 c->minrooms = atoi(argv[++i]);
2266 if(c->minrooms < 2)
2267 c->minrooms = 2;
2268 if(c->minrooms > 37)
2269 c->minrooms = 37;
2270 } /* end if enough args */
2271 } else if (!stricmp(argv[i],"-config")) {
2272 if (i<(argc-1)) i++; /* Ignored in this stage */
2273 } else if (!stricmp(argv[i],"-seed")) {
2274 if (i<(argc-1)) i++; /* Ignored in this stage */
2275 } else { /* Unknown switch */
2276 fprintf(stderr,"%s error: unknown switch <%s>.\n\n",s,argv[i]);
2277 return FALSE;
2278 }
2279 } /* end for args */
2280 } /* end not conly */
2281
2282 return TRUE;
2283 }
2284
2285 /* Put this object in this sector. It's a non-obstable object */
place_required_pickable(level * l,sector * s,config * c,short id)2286 thing *place_required_pickable(level *l,sector *s,config *c,short id)
2287 {
2288 thing *answer;
2289
2290 answer = place_object(l,s,c,id,48,0,0,0,7); /* 48 for nice-looking-ness */
2291 if (answer==NULL)
2292 answer = place_object(l,s,c,id,1,0,0,0,7); /* This had better work! */
2293 if (answer==NULL) announce(ERROR,"Important object could not be placed.");
2294 return answer;
2295 }
2296
place_required_small_pickable(level * l,sector * s,config * c)2297 thing *place_required_small_pickable(level *l,sector *s,config *c)
2298 {
2299 short tid;
2300
2301 if (rollpercent(139,50)) tid = ID_POTION;
2302 else tid = ID_HELMET; /* More choices? */
2303
2304 return place_required_pickable(l,s,c,tid);
2305 }
2306
2307 /* This is for sector-specific texture alignment. Is there */
2308 /* some reason to want to do that? */
align_textures(level * l,sector * oldsector,config * c)2309 void align_textures(level *l,sector *oldsector,config *c)
2310 {
2311 return;
2312 }
2313
2314 /* Do these two sidedefs share any texture(s) that should */
2315 /* be aligned together? */
common_texture(sidedef * sd1,sidedef * sd2)2316 boolean common_texture(sidedef *sd1, sidedef *sd2)
2317 {
2318 /* Pass along most of the job to coalignable(). */
2319 if (sd1->middle_texture->name[0]!='-') {
2320 if (coalignable(sd1->middle_texture,sd2->middle_texture)) return 1;
2321 if (coalignable(sd1->middle_texture,sd2->upper_texture)) return 1;
2322 if (coalignable(sd1->middle_texture,sd2->lower_texture)) return 1;
2323 }
2324 if (sd1->upper_texture->name[0]!='-') {
2325 if (coalignable(sd1->upper_texture,sd2->middle_texture)) return 1;
2326 if (coalignable(sd1->upper_texture,sd2->upper_texture)) return 1;
2327 if (coalignable(sd1->upper_texture,sd2->lower_texture)) return 1;
2328 }
2329 if (sd1->lower_texture->name[0]!='-') {
2330 if (coalignable(sd1->lower_texture,sd2->middle_texture)) return 1;
2331 if (coalignable(sd1->lower_texture,sd2->upper_texture)) return 1;
2332 if (coalignable(sd1->lower_texture,sd2->lower_texture)) return 1;
2333 }
2334 return 0;
2335 }
2336
global_align_forward(level * l,linedef * ld)2337 void global_align_forward(level *l, linedef *ld)
2338 {
2339 vertex *v;
2340 linedef *ld2;
2341 int newoff;
2342
2343 v = ld->to;
2344 for (ld2=l->linedef_anchor;ld2;ld2=ld2->next) {
2345 if (ld2->from==v)
2346 if (common_texture(ld->right,ld2->right)) {
2347 newoff = ld->right->x_offset + linelen(ld);
2348 newoff = newoff % 256;
2349 if (newoff<0) newoff += 256;
2350 if (ld2->marked==0) {
2351 ld2->right->x_offset = newoff;
2352 ld2->marked = 1;
2353 global_align_linedef(l,ld2);
2354 } else {
2355 if (ld2->right->x_offset!=newoff)
2356 ld->f_misaligned = 1;
2357 }
2358 } /* end if common texture */
2359 } /* end for ld2 */
2360 }
2361
global_align_backward(level * l,linedef * ld)2362 void global_align_backward(level *l, linedef *ld)
2363 {
2364 vertex *v;
2365 linedef *ld2;
2366 int newoff;
2367
2368 v = ld->from;
2369 for (ld2=l->linedef_anchor;ld2;ld2=ld2->next) {
2370 if (ld2->to==v)
2371 if (common_texture(ld->right,ld2->right)) {
2372 newoff = ld->right->x_offset - linelen(ld2);
2373 newoff = newoff % 256;
2374 if (newoff<0) newoff += 256;
2375 if (ld2->marked==0) {
2376 ld2->right->x_offset = newoff;
2377 ld2->marked = 1;
2378 global_align_linedef(l,ld2);
2379 } else {
2380 if (ld2->right->x_offset!=newoff)
2381 ld->b_misaligned = 1;
2382 }
2383 } /* end if common texture */
2384 } /* end for ld2 */
2385 }
2386
global_align_linedef(level * l,linedef * ld)2387 void global_align_linedef(level *l, linedef *ld)
2388 {
2389 ld->marked = 1;
2390 global_align_group_backbone_forward(l,ld);
2391 global_align_group_backbone_backward(l,ld);
2392 global_align_group_etc_forward(l,ld);
2393 global_align_group_etc_backward(l,ld);
2394 }
2395
global_align_group_backbone_forward(level * l,linedef * ld)2396 void global_align_group_backbone_forward(level *l,linedef *ld)
2397 {
2398 linedef *ldnext;
2399 int newoff;
2400
2401 if (NULL != (ldnext = ld->group_next)) {
2402 if (ld->to != ldnext->from) announce(LOG,"Yow forward!");
2403 if (common_texture(ld->right,ldnext->right)) {
2404 newoff = ld->right->x_offset + linelen(ld);
2405 newoff = newoff % 256;
2406 if (newoff<0) newoff += 256;
2407 if (ldnext->marked==0) {
2408 ldnext->right->x_offset = newoff;
2409 ldnext->marked = 1;
2410 global_align_group_backbone_forward(l,ldnext);
2411 } else {
2412 announce(LOG,"Found a locked linedef in g_a_g_b_f?");
2413 if (ldnext->right->x_offset!=newoff)
2414 ldnext->f_misaligned = 1;
2415 }
2416 } /* end if common texture */
2417 }
2418 }
2419
global_align_group_etc_forward(level * l,linedef * ld)2420 void global_align_group_etc_forward(level *l,linedef *ld)
2421 {
2422 linedef *ldnext;
2423
2424 if (NULL != (ldnext = ld->group_next))
2425 global_align_group_etc_forward(l,ldnext);
2426 global_align_forward(l,ld);
2427 }
2428
global_align_group_etc_backward(level * l,linedef * ld)2429 void global_align_group_etc_backward(level *l,linedef *ld)
2430 {
2431 linedef *ldnext;
2432
2433 if (NULL != (ldnext = ld->group_previous))
2434 global_align_group_etc_backward(l,ldnext);
2435 global_align_backward(l,ld);
2436 }
2437
global_align_group_backbone_backward(level * l,linedef * ld)2438 void global_align_group_backbone_backward(level *l,linedef *ld)
2439 {
2440 linedef *ldprev;
2441 int newoff;
2442
2443 if (NULL != (ldprev = ld->group_previous)) {
2444 if (ld->from != ldprev->to) announce(LOG,"Yow backward!");
2445 if (common_texture(ld->right,ldprev->right)) {
2446 newoff = ld->right->x_offset - linelen(ldprev);
2447 newoff = newoff % 256;
2448 if (newoff<0) newoff += 256;
2449 if (ldprev->marked==0) {
2450 ldprev->right->x_offset = newoff;
2451 ldprev->marked = 1;
2452 global_align_group_backbone_backward(l,ldprev);
2453 } else {
2454 announce(LOG,"Found a locked linedef in g_a_g_b_b?");
2455 if (ldprev->right->x_offset!=newoff)
2456 ldprev->b_misaligned = 1;
2457 }
2458 } /* end if common texture */
2459 }
2460 }
2461
2462 /* Align textures all around the level */
global_align_textures(level * l,config * c)2463 void global_align_textures(level *l,config *c)
2464 {
2465 /* This complicated knot of recursives seem pretty good. */
2466 /* It doesn't know about NOSPLITs and stuff yet, and it */
2467 /* only does horizontal alignment. Should we do Y here also? */
2468 linedef *ld1, *ld2;
2469 int newoff;
2470
2471 announce(LOG,"Globally aligning...");
2472
2473 #define DONT_MARK_MISALIGNS
2474
2475 for (ld1=l->linedef_anchor;ld1;ld1=ld1->next) {
2476 ld1->f_misaligned = 0;
2477 ld1->b_misaligned = 0;
2478 }
2479
2480 unmark_linedefs(l); /* just in case */
2481 for (ld1=l->linedef_anchor;ld1;ld1=ld1->next)
2482 if (ld1->marked==0) global_align_linedef(l,ld1);
2483
2484 /* Now put in any intentional misalignments, for hints etc */
2485 for (ld1=l->linedef_anchor;ld1;ld1=ld1->next) {
2486 if (ld1->right) {
2487 ld1->right->x_offset += ld1->right->x_misalign;
2488 ld1->right->y_offset += ld1->right->y_misalign;
2489 }
2490 } /* end for ld1 */
2491
2492 /* Sometimes put in supports in the places we know didn't align */
2493 /* This actually looks pretty terrible! Think about it more */
2494 if (l->support_misaligns) {
2495 announce(LOG,"Prettying up misalignments...");
2496 for (ld1=l->linedef_anchor;ld1;ld1=ld1->next) {
2497 if (ld1->right)
2498 if (ld1->right->sector->style)
2499 if (ld1->b_misaligned) {
2500 newoff = linelen(ld1);
2501 if (newoff>8) split_linedef(l,ld1,8,c);
2502 if (ld1->right->upper_texture->name[0]!='-')
2503 ld1->right->upper_texture = ld1->right->sector->style->support0;
2504 if (ld1->right->lower_texture->name[0]!='-')
2505 ld1->right->lower_texture = ld1->right->sector->style->support0;
2506 if (ld1->right->middle_texture->name[0]!='-')
2507 ld1->right->middle_texture = ld1->right->sector->style->support0;
2508 }
2509 if (ld1->f_misaligned) {
2510 newoff = linelen(ld1);
2511 if (newoff>8) ld2 = split_linedef(l,ld1,newoff-8,c);
2512 else ld2 = ld1;
2513 if (ld2->right->upper_texture->name[0]!='-')
2514 ld2->right->upper_texture = ld1->right->sector->style->support0;
2515 if (ld2->right->lower_texture->name[0]!='-')
2516 ld2->right->lower_texture = ld1->right->sector->style->support0;
2517 if (ld2->right->middle_texture->name[0]!='-')
2518 ld2->right->middle_texture = ld1->right->sector->style->support0;
2519 }
2520 } /* end for ld1 */
2521 } /* end if supporting */
2522
2523 #ifdef MARK_MISALIGNS
2524 /* And finally for debugging mark the places we know still didn't align */
2525 for (ld1=l->linedef_anchor;ld1;ld1=ld1->next) {
2526 if (ld1->right)
2527 if (ld1->b_misaligned) {
2528 newoff = linelen(ld1);
2529 if (newoff>8) split_linedef(l,ld1,8,c);
2530 if (ld1->right->upper_texture->name[0]!='-')
2531 ld1->right->upper_texture = c->error_texture;
2532 if (ld1->right->lower_texture->name[0]!='-')
2533 ld1->right->lower_texture = c->error_texture;
2534 if (ld1->right->middle_texture->name[0]!='-')
2535 ld1->right->middle_texture = c->error_texture;
2536 }
2537 if (ld1->f_misaligned) {
2538 newoff = linelen(ld1);
2539 if (newoff>8) ld2 = split_linedef(l,ld1,newoff-8,c);
2540 else ld2 = ld1;
2541 if (ld2->right->upper_texture->name[0]!='-')
2542 ld2->right->upper_texture = c->error_texture; /* or blue! */
2543 if (ld2->right->lower_texture->name[0]!='-')
2544 ld2->right->lower_texture = c->error_texture;
2545 if (ld2->right->middle_texture->name[0]!='-')
2546 ld2->right->middle_texture = c->error_texture;
2547 }
2548 } /* end for ld1 */
2549 #endif
2550
2551 return;
2552 }
2553
2554 /* Random other last-minute fixups to a level */
global_fixups(level * l)2555 void global_fixups(level *l)
2556 {
2557 linedef *ld;
2558
2559 for (ld=l->linedef_anchor;ld;ld=ld->next)
2560 if (ld->left==NULL) ld->flags |= IMPASSIBLE;
2561 }
2562
2563 /* This just paints all one-sided boundary sidddefs of the sector */
paint_room(level * l,sector * s,style * ThisStyle,config * c)2564 void paint_room(level *l,sector *s,style *ThisStyle,config *c)
2565 {
2566 linedef *ld;
2567
2568 for (ld=l->linedef_anchor;ld;ld=ld->next) {
2569 if (ld->right)
2570 if (ld->right->sector==s)
2571 if (ld->right->isBoundary)
2572 if (ld->left==NULL) {
2573 ld->right->middle_texture = ThisStyle->wall0;
2574 } else {
2575 patch_upper(ld,ThisStyle->wall0,c);
2576 patch_lower(ld,ThisStyle->kickplate,c); /* Or stepfront? */
2577 }
2578 }
2579 s->light_level = ThisStyle->roomlight0;
2580
2581 return;
2582 }
2583
2584 /* Construct a linedef on the left side of this linedef, */
2585 /* <depth> away from it and pro-parallel to it. */
2586 /* If old is not NULL, re-use it, just changing its to and from. */
make_parallel(level * l,linedef * ld,int depth,linedef * old)2587 linedef *make_parallel(level *l,linedef *ld,int depth,linedef *old)
2588 {
2589 vertex *v1, *v2;
2590 int x,y;
2591
2592 point_from(ld->from->x, ld->from->y, ld->to->x, ld->to->y,
2593 LEFT_TURN,depth,&x,&y);
2594 if (old) {
2595 old->to->x = x; /* Assumes no one else is using */
2596 old->to->y = y; /* these vertexes. OK? */
2597 x += ld->from->x - ld->to->x;
2598 y += ld->from->y - ld->to->y;
2599 old->from->x = x;
2600 old->from->y = y;
2601 return old;
2602 } else {
2603 v1 = new_vertex(l,x,y);
2604 x += ld->from->x - ld->to->x;
2605 y += ld->from->y - ld->to->y;
2606 v2 = new_vertex(l,x,y);
2607 return new_linedef(l,v2,v1);
2608 }
2609 }
2610
2611 /* Given two linedefs, construct a new rhomboid between them, on */
2612 /* the left side of the first and the right side of the second. */
2613 /* Return the sector. If the edge1 and edge2 args are not null, */
2614 /* they get the from-joining new linedef, and the to-joining one */
2615 /* respectively. */
make_box_ext(level * l,linedef * ldf1,linedef * ldf2,style * ThisStyle,config * c,linedef ** edge1,linedef ** edge2)2616 sector *make_box_ext(level *l,linedef *ldf1, linedef *ldf2,
2617 style *ThisStyle, config *c,
2618 linedef **edge1, linedef **edge2)
2619 {
2620 sector *answer;
2621 linedef *ldnew1, *ldnew2;
2622 sector *oldsec;
2623
2624 /* Make the orthogonal sides */
2625 ldnew1 = new_linedef(l,ldf1->from,ldf2->from);
2626 ldnew2 = new_linedef(l,ldf2->to,ldf1->to);
2627 if (edge1) *edge1 = ldnew1;
2628 if (edge2) *edge2 = ldnew2;
2629 /* Now tie them all to a sector */
2630 answer = new_sector(l,0,0,c->sky_flat,c->sky_flat);
2631 answer->style = ThisStyle;
2632 if (ldf1->right) {
2633 oldsec = ldf1->right->sector;
2634 answer->floor_height = oldsec->floor_height;
2635 answer->ceiling_height = oldsec->ceiling_height;
2636 answer->floor_flat = oldsec->floor_flat;
2637 answer->ceiling_flat = oldsec->ceiling_flat;
2638 answer->light_level = oldsec->light_level; /* default */
2639 answer->special = oldsec->special; /* or zero? */
2640 ldf1->right->middle_texture = c->null_texture;
2641 ldf1->flags |= TWO_SIDED;
2642 }
2643 ldf1->left = new_sidedef(l,answer,c);
2644 ldf2->right = new_sidedef(l,answer,c);
2645 ldnew1->right = new_sidedef(l,answer,c);
2646 ldnew2->right = new_sidedef(l,answer,c);
2647 ldf2->right->middle_texture = c->null_texture; /* Useful? */
2648 if (ldf2->left)
2649 ldf2->left->middle_texture = c->null_texture; /* HOM until filled */
2650 ldf1->left->middle_texture = c->null_texture;
2651 ldnew1->right->middle_texture = ThisStyle->wall0; /* guess */
2652 ldnew2->right->middle_texture = ThisStyle->wall0; /* also */
2653 return answer;
2654 }
2655
2656 /* Given a one-sided linedef, construct a rectangular sector on */
2657 /* the left side of it, of the given depth. Returns the other */
2658 /* (parallel) long side. */
lefthand_box_ext(level * l,linedef * ldf1,int depth,style * ThisStyle,config * c,linedef ** nld1,linedef ** nld2)2659 linedef *lefthand_box_ext(level *l,linedef *ldf1,int depth,
2660 style *ThisStyle,config *c,
2661 linedef **nld1, linedef **nld2)
2662 {
2663 linedef *answer;
2664 sector *s;
2665
2666 /* Get the other side of the box */
2667 answer = make_parallel(l,ldf1,depth,NULL);
2668 s = make_box_ext(l,ldf1,answer,ThisStyle,c,nld1,nld2);
2669 return answer;
2670 }
2671
2672 /* Find the corners of the minimal enclosing rectangle around the */
2673 /* given sector. Or something like that. Uses a cache in the */
2674 /* sector record for speed. How dangerous is that? */
find_rec(level * l,sector * s,int * minx,int * miny,int * maxx,int * maxy)2675 void find_rec(level *l, sector *s, int *minx, int *miny, int *maxx, int *maxy)
2676 {
2677 /* Not a stub, but many of its callers are */
2678 linedef *ld;
2679
2680 if (!s->findrec_data_valid) {
2681 int lx,ly,hx,hy;
2682 lx = ly = HUGE_NUMBER;
2683 hx = hy = 0-HUGE_NUMBER;
2684 for (ld=l->linedef_anchor;ld;ld=ld->next) {
2685 if (ld->right)
2686 if (ld->right->sector==s) {
2687 // if (ld->right->isBoundary) { /* Need to check this? */
2688 if (ld->to->x>hx) hx = ld->to->x;
2689 if (ld->to->y>hy) hy = ld->to->y;
2690 if (ld->to->x<lx) lx = ld->to->x;
2691 if (ld->to->y<ly) ly = ld->to->y;
2692 // }
2693 }
2694 }
2695 s->minx = lx; s->miny = ly; s->maxx = hx; s->maxy = hy;
2696 s->findrec_data_valid = TRUE;
2697 }
2698
2699 *minx = s->minx; *miny = s->miny; *maxx = s->maxx; *maxy = s->maxy;
2700 }
2701
dump_link(linedef * ldf1,linedef * ldf2,link * ThisLink,char * s1)2702 void dump_link(linedef *ldf1,linedef *ldf2,link *ThisLink,char *s1)
2703 {
2704 char s[200];
2705
2706 sprintf(s,"%s Link",s1);
2707 if (ldf1) {
2708 sprintf(s,"%s between (%d,%d)-(%d,%d) and (%d,%d)-(%d,%d).",s,
2709 ldf1->from->x,ldf1->from->y,ldf1->to->x,ldf1->to->y,
2710 ldf2->from->x,ldf2->from->y,ldf2->to->x,ldf2->to->y);
2711 }
2712 announce(VERBOSE,s);
2713 announce(VERBOSE,"T W R ND FD C A S L M h1 w1 w2 d1 d2 d3 fd sc ");
2714 sprintf(s,"%1d %1d %1d %1d %1d %1d %1d %1d %1d %03d %03d %03d %03d %03d %03d %04d %03d %03d",
2715 (ThisLink->bits&LINK_TWIN)!=0,
2716 (ThisLink->bits&LINK_WINDOW)!=0,
2717 (ThisLink->bits&LINK_RECESS)!=0,
2718 (ThisLink->bits&LINK_NEAR_DOOR)!=0,
2719 (ThisLink->bits&LINK_FAR_DOOR)!=0,
2720 (ThisLink->bits&LINK_CORE)!=0,
2721 (ThisLink->bits&LINK_ALCOVE)!=0,
2722 (ThisLink->bits&LINK_STEPS)!=0,
2723 (ThisLink->bits&LINK_LIFT)!=0,
2724 (ThisLink->bits&LINK_MAX_CEILING)!=0,
2725 ThisLink->height1, ThisLink->width1, ThisLink->width2,
2726 ThisLink->depth1, ThisLink->depth2, ThisLink->depth3,
2727 ThisLink->floordelta, ThisLink->stepcount);
2728 announce(VERBOSE,s);
2729 }
2730
2731 /* Push a new (defaulty) quest onto the given stack */
push_quest(quest * old)2732 quest *push_quest(quest *old)
2733 {
2734 quest *answer;
2735
2736 answer = (quest *)malloc(sizeof(*answer));
2737
2738 answer->goal = NULL_GOAL;
2739 answer->tag = 0;
2740 answer->type = 0;
2741 answer->count = 0;
2742 answer->room = NULL;
2743 answer->minrooms = 0;
2744 answer->auxtag = 0;
2745 answer->surprise = NULL;
2746 answer->next = old;
2747 return answer;
2748 }
2749
2750 /* Pop the top off the stack, free it, return new top */
pop_quest(quest * current)2751 quest *pop_quest(quest *current)
2752 {
2753 quest *answer;
2754
2755 answer = current->next;
2756 free(current);
2757 return answer;
2758 }
2759
2760 /******* Many routines from here on need more work ****************/
2761
Usage2(void)2762 void Usage2(void)
2763 {
2764 Usage0();
2765 printf("Switches that do something at the moment:\n");
2766 printf(" -rooms [n] -seed [nnnnnn] -outfile [filename.ext]\n");
2767 printf(" -restrict [012C] -ExMx -MAPxx -doom1 -doom2 -levels <x> \n");
2768 printf(" -minlight <x> -music -macho <nn> -noslinfo -nocustom -cwad \n");
2769 printf(" -arena -nulls -nosemo -biwe -bimo -bimo! \n");
2770 #ifdef SWITCHES_IN_CONFIG_FILES
2771 printf("(-outfile being most useful in the config file; on the\n");
2772 printf("command line you don't need the switch.)\n");
2773 #endif
2774 printf("\nM O R E D E T A I L S T O F O L L O W\n\n");
2775 return;
2776 }
2777
2778 /*
2779 Anything that calls this routine is probably too simple to
2780 handle non-rectangular rooms!
2781
2782 Is there any vertex that's
2783 not marked that occurs in the given rectangle, or do any
2784 of these four points lie in any unmarked sector? Or,
2785 assuming that these points are in clockwise or countercwise
2786 order, do any existing linedefs between unmarked vertexes
2787 intersect any of these four lines? Probably pretty suboptimal!
2788 */
empty_rectangle(level * l,int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4)2789 boolean empty_rectangle(level *l, int x1, int y1, int x2, int y2,
2790 int x3, int y3, int x4, int y4)
2791 {
2792 int minx, maxx, miny, maxy;
2793 vertex *v;
2794 sector *s;
2795 linedef *ld;
2796
2797 /* Find the enclosing rectangle of these points */
2798 if (x1>x2) {
2799 maxx = x1;
2800 } else {
2801 maxx = x2;
2802 }
2803 if (x3>maxx) maxx = x3;
2804 if (x4>maxx) maxx = x4;
2805
2806 if (y1>y2) {
2807 maxy = y1;
2808 } else {
2809 maxy = y2;
2810 }
2811 if (y3>maxy) maxy = y3;
2812 if (y4>maxy) maxy = y4;
2813
2814 if (x1<x2) {
2815 minx = x1;
2816 } else {
2817 minx = x2;
2818 }
2819 if (x3<minx) minx = x3;
2820 if (x4<minx) minx = x4;
2821
2822 if (y1<y2) {
2823 miny = y1;
2824 } else {
2825 miny = y2;
2826 }
2827 if (y3<miny) miny = y3;
2828 if (y4<miny) miny = y4;
2829
2830 /* Look at all unmarked vertexes, see if any */
2831 /* are within the enclosing rectangle. */
2832 for (v=l->vertex_anchor;v;v=v->next) {
2833 if (v->marked==0)
2834 if ( ( (v->x <= maxx) && (v->x >= minx) ) &&
2835 ( (v->y <= maxy) && (v->y >= miny) ) ) return 0;
2836 }
2837
2838 /* Now look at all sectors, see if any of these four */
2839 /* proposed vertexes is inside the rectangular envelope */
2840 for (s=l->sector_anchor;s;s=s->next) {
2841 if (s->marked==0) {
2842 find_rec(l,s,&minx,&miny,&maxx,&maxy);
2843 if ( (x1 <= maxx) && (x1 >= minx) &&
2844 (y1 <= maxy) && (y1 >= miny) ) return 0;
2845 if ( (x2 <= maxx) && (x2 >= minx) &&
2846 (y2 <= maxy) && (y2 >= miny) ) return 0;
2847 if ( (x3 <= maxx) && (x3 >= minx) &&
2848 (y3 <= maxy) && (y3 >= miny) ) return 0;
2849 if ( (x4 <= maxx) && (x4 >= minx) &&
2850 (y4 <= maxy) && (y4 >= miny) ) return 0;
2851 }
2852 }
2853
2854 /* Now look at all linedefs, see if any intersects with */
2855 /* any of the four implied boundary lines. Doesn't assume */
2856 /* axis-parallel lines, for a change! Does assume there are */
2857 /* only four sides, though. Need true polygons. */
2858 for (ld=l->linedef_anchor;ld;ld=ld->next) {
2859 if (ld->to->marked==0)
2860 if (ld->from->marked==0) {
2861 if (intersects(x1,y1,x2,y2,ld->from->x,ld->from->y,ld->to->x,ld->to->y))
2862 return 0;
2863 if (intersects(x2,y2,x3,y3,ld->from->x,ld->from->y,ld->to->x,ld->to->y))
2864 return 0;
2865 if (intersects(x3,y3,x4,y4,ld->from->x,ld->from->y,ld->to->x,ld->to->y))
2866 return 0;
2867 if (intersects(x4,y4,x1,y1,ld->from->x,ld->from->y,ld->to->x,ld->to->y))
2868 return 0;
2869 }
2870 }
2871
2872 return 1;
2873 }
2874
2875 /* Given a linedef and a point, return the distance from the */
2876 /* linedef to the point, positive if the point is on the right */
2877 /* side, negative if to the left. May return HUGE_NUMBER if */
2878 /* the point is not in either axis-shadow of the linedef, and */
2879 /* may assume that the linedef is basically axis-parallel. */
point_from_linedef(level * l,int x,int y,linedef * ld)2880 int point_from_linedef(level *l, int x, int y, linedef *ld)
2881 {
2882 int answer = HUGE_NUMBER;
2883 int candidate;
2884 int parity = 1;
2885
2886 /* An utter kludge; why not just do it right? */
2887
2888 if ((ld->from->x!=ld->to->x)&&(ld->from->x<=x)&&(ld->to->x>=x)) {
2889 candidate = abs(ld->to->y-y);
2890 if (candidate<answer) {
2891 answer = candidate;
2892 if (ld->to->y<y) parity = -1;
2893 else parity = 1;
2894 }
2895 candidate = abs(ld->from->y-y);
2896 if (candidate<answer) {
2897 answer = candidate;
2898 if (ld->from->y<y) parity = -1;
2899 else parity = 1;
2900 }
2901 }
2902
2903 if ((ld->to->x!=ld->from->x)&&(ld->to->x<=x)&&(ld->from->x>=x)) {
2904 candidate = abs(ld->to->y-y);
2905 if (candidate<answer) {
2906 answer = candidate;
2907 if (ld->to->y<y) parity = 1;
2908 else parity = -1;
2909 }
2910 candidate = abs(ld->from->y-y);
2911 if (candidate<answer) {
2912 answer = candidate;
2913 if (ld->from->y<y) parity = 1;
2914 else parity = -1;
2915 }
2916 }
2917
2918 if ((ld->from->y!=ld->to->y)&&(ld->from->y<=y)&&(ld->to->y>=y)) {
2919 candidate = abs(ld->to->x-x);
2920 if (candidate<answer) {
2921 answer = candidate;
2922 if (ld->to->x<x) parity = 1;
2923 else parity = -1;
2924 }
2925 candidate = abs(ld->from->x-x);
2926 if (candidate<answer) {
2927 answer = candidate;
2928 if (ld->from->x<x) parity = 1;
2929 else parity = -1;
2930 }
2931 }
2932
2933 if ((ld->to->y!=ld->from->y)&&(ld->to->y<=y)&&(ld->from->y>=y)) {
2934 candidate = abs(ld->to->x-x);
2935 if (candidate<answer) {
2936 answer = candidate;
2937 if (ld->to->x<x) parity = -1;
2938 else parity = 1;
2939 }
2940 candidate = abs(ld->from->x-x);
2941 if (candidate<answer) {
2942 answer = candidate;
2943 if (ld->from->x<x) parity = -1;
2944 else parity = 1;
2945 }
2946 }
2947
2948 return answer * parity;
2949
2950 }
2951
2952 /* Are any non-flying monsters so close to this linedef */
2953 /* that if the sides of the linedef were unwalkable, the */
2954 /* monster would be stuck? */
no_monsters_stuck_on(level * l,linedef * ld)2955 boolean no_monsters_stuck_on(level *l, linedef *ld)
2956 {
2957 thing *m;
2958 int dist;
2959
2960 for (m=l->thing_anchor;m;m=m->next) {
2961 if (!(m->genus->bits&MONSTER)) continue; /* Only monsters */
2962 if (m->genus->bits&FLIES) continue; /* Fliers can escape */
2963 dist = abs(point_from_linedef(l,m->x,m->y,ld));
2964 if (dist<=(MONSTER_WIDTH(m)/2)) {
2965 #ifdef ALLOW_STUCK_MONSTERS_IN_BATHS
2966 announce(LOG,"Bath Bug!");
2967 return TRUE;
2968 #endif
2969 return FALSE;
2970 }
2971 }
2972
2973 return TRUE;
2974
2975 }
2976
2977 /* Return sector that the given x,y is in, and if dist is */
2978 /* not null return the distance from the nearest wall that */
2979 /* it is. Or something like that. NULL if in no sector. */
2980 /* Set <danger>, if non-NULL, if any non-normal linedef is */
2981 /* within 48 of the point. */
point_sector(level * l,int x,int y,int * dist,boolean * danger)2982 sector *point_sector(level *l,int x, int y, int *dist, boolean *danger)
2983 {
2984 int thisdist, closest;
2985 linedef *ld, *ldbest;
2986 sector *answer = NULL;
2987
2988 if (danger!=NULL) *danger = FALSE;
2989 closest = HUGE_NUMBER;
2990 for (ld=l->linedef_anchor;ld;ld=ld->next) {
2991 thisdist = point_from_linedef(l,x,y,ld);
2992 if (abs(thisdist)<49)
2993 if (ld->type!=LINEDEF_NORMAL)
2994 if (danger!=NULL)
2995 *danger = TRUE;
2996 if (abs(thisdist)<closest) {
2997 if (thisdist>0) {
2998 answer = ld->right->sector;
2999 closest = abs(thisdist);
3000 ldbest = ld;
3001 } else if (ld->left) {
3002 /* Actually, if we find that the closest thing is the left side */
3003 /* of a one-sided linedef, we should set answer to NULL, and */
3004 /* update closest and ldbest. But, because sometimes the crude */
3005 /* point_from_linedef() seriously underestimates distances, we'll */
3006 /* actually do nothing in that case, on the theory that some */
3007 /* linedef that gives us a non-NULL answer is *really* the */
3008 /* closest one. This is a hack; we should fix point_from_linedef */
3009 /* instead. */
3010 answer = ld->left->sector;
3011 closest = abs(thisdist);
3012 ldbest = ld;
3013 }
3014 }
3015 }
3016
3017 if (dist!=NULL) *dist = closest;
3018 return answer;
3019
3020 }
3021
3022
3023 /* Return a patch array followed by MUS-format pseudo-MIDI */
3024 /* for one piece of music, and fill in the given header */
3025 /* with the data it needs. Return value is free()able. */
one_piece(musheader * pmh)3026 byte *one_piece(musheader *pmh)
3027 {
3028 byte *answer;
3029 byte patch = roll(35,128);
3030
3031 /* Now this is the very definition of "stub" */
3032 pmh->tag[0] = 'M';
3033 pmh->tag[1] = 'U';
3034 pmh->tag[2] = 'S';
3035 pmh->tag[3] = 0x1a;
3036 pmh->primchannels = 1;
3037 pmh->secchannels = 0;
3038 pmh->dummy = 0;
3039 pmh->patches = 1;
3040 pmh->headerlength = sizeof(musheader)+pmh->patches*sizeof(short);
3041 pmh->muslength = 17;
3042 answer = (byte *)malloc( pmh->patches*sizeof(short)+pmh->muslength );
3043 answer[0] = patch; /* instrument */
3044 answer[1] = 0;
3045 answer[2] = 0x40; /* Control change, channel zero */
3046 answer[3] = 0x00; /* Select patch */
3047 answer[4] = patch; /* that again */
3048 answer[5] = 0x40; /* Control change, channel zero */
3049 answer[6] = 0x07; /* volume */
3050 answer[7] = 0x7f; /* loud! */
3051
3052 answer[8] = 0x80 | 0x10; /* Play note, channel zero, last event */
3053 answer[9] = 0x80 | 0x50; /* Note 80, with volume */
3054 answer[10] = 0x7f; /* volume */
3055 answer[11] = 70; /* half-second delay */
3056 answer[12] = 0x00; /* Note off, channel zero */
3057 answer[13] = 0x50; /* Note 80 off */
3058 answer[14] = 0x80 | 0x10; /* Play note, channel zero, last event */
3059 answer[15] = 0x4b; /* Note 75, no volume */
3060 answer[16] = 70; /* delay */
3061
3062 answer[17] = 0x60; /* end score */
3063 answer[18] = 0x4d; /* maybe? */
3064 return answer;
3065 }
3066
3067 /* Allocate, initialize, and return a new lmp for custom textures */
new_texture_lmp(char * name)3068 texture_lmp *new_texture_lmp(char *name)
3069 {
3070 texture_lmp *answer = (texture_lmp *)malloc(sizeof(*answer));
3071
3072 answer->name = strdup(name);
3073 answer->custom_texture_anchor = NULL;
3074 return answer;
3075 }
3076
3077 /* Allocate, initialize, register with the given lmp, and return */
3078 /* a new custom texture record structure thing */
new_custom_texture(texture_lmp * tl,char * name,short xsize,short ysize)3079 custom_texture *new_custom_texture(texture_lmp *tl,char *name,
3080 short xsize, short ysize)
3081 {
3082 custom_texture *answer = (custom_texture *)malloc(sizeof(*answer));
3083
3084 answer->name = strdup(name);
3085 answer->xsize = xsize;
3086 answer->ysize = ysize;
3087 answer->patch_anchor = NULL;
3088 answer->next = tl->custom_texture_anchor;
3089 tl->custom_texture_anchor = answer;
3090 return answer;
3091 }
3092
3093 /* Free up all resources associated with a texture lump */
free_texture_lmp(texture_lmp * tl)3094 void free_texture_lmp(texture_lmp *tl)
3095 {
3096 custom_texture* ctp;
3097 patch *p;
3098
3099 /* Free each texture */
3100 for (;;) {
3101 ctp = tl->custom_texture_anchor;
3102 if (ctp==NULL) break;
3103 tl->custom_texture_anchor = tl->custom_texture_anchor->next;
3104 /* Free each patch */
3105 for (;;) {
3106 p = ctp->patch_anchor;
3107 if (p==NULL) break;
3108 ctp->patch_anchor = ctp->patch_anchor->next;
3109 free(p);
3110 }
3111 free(ctp->name);
3112 free(ctp);
3113 }
3114 free(tl->name);
3115 free(tl);
3116 }
3117
3118 /* A primitive not-quite-random-field image-writing thing */
basic_background(byte * fbuf,byte bottom,int range)3119 void basic_background(byte *fbuf, byte bottom, int range)
3120 {
3121 int i,j;
3122 int above, below, left, right, total;
3123
3124 for (i=0;i<64;i++) {
3125 for (j=(i&1);j<64;j+=2) {
3126 fbuf[64*i+j] = bottom + roll(36,range);
3127 }
3128 }
3129
3130 for (i=0;i<64;i++) {
3131 for (j=1-(i&1);j<64;j+=2) {
3132 above = (i==0) ? 63 : i-1;
3133 below = (i==63) ? 0 : i+1;
3134 left = (j==0) ? 63 : j-1;
3135 right = (j==63) ? 0 : j+1;
3136 total = fbuf[64*above+j] +
3137 fbuf[64*below+j] +
3138 fbuf[64*i+left] +
3139 fbuf[64*i+right];
3140 total >>= 2;
3141 fbuf[64*i+j] = total;
3142 }
3143 }
3144 }
3145
3146 /* A primitive not-quite-random-field image-writing thing */
basic_background2(byte * fbuf,byte bottom,int range)3147 void basic_background2(byte *fbuf, byte bottom, int range)
3148 {
3149 int i,j;
3150 int above, below, left, right, total;
3151
3152 /* The randomly-set large grid */
3153 for (i=0;i<64;i+=2) {
3154 for (j=0;j<64;j+=2) {
3155 fbuf[64*i+j] = bottom + roll(37,range);
3156 }
3157 }
3158
3159 /* The quinicunx points of the large grid */
3160 for (i=1;i<64;i+=2) {
3161 for (j=1;j<64;j+=2) {
3162 above = (i==0) ? 63 : i-1;
3163 below = (i==63) ? 0 : i+1;
3164 left = (j==0) ? 63 : j-1;
3165 right = (j==63) ? 0 : j+1;
3166 total = fbuf[64*above+left] +
3167 fbuf[64*below+left] +
3168 fbuf[64*above+right] +
3169 fbuf[64*below+right] + 2;
3170 total >>= 2;
3171 fbuf[64*i+j] = total;
3172 }
3173 }
3174
3175 /* The remaining grid */
3176 for (i=0;i<64;i++) {
3177 for (j=1-(i&1);j<64;j+=2) {
3178 above = (i==0) ? 63 : i-1;
3179 below = (i==63) ? 0 : i+1;
3180 left = (j==0) ? 63 : j-1;
3181 right = (j==63) ? 0 : j+1;
3182 total = fbuf[64*above+j] +
3183 fbuf[64*below+j] +
3184 fbuf[64*i+left] +
3185 fbuf[64*i+right] + 2;
3186 total >>= 2;
3187 fbuf[64*i+j] = total;
3188 }
3189 }
3190 }
3191
3192 /* A primitive not-quite-random-field image-writing thing */
basic_background3(byte * fbuf,byte bottom,int range)3193 void basic_background3(byte *fbuf, byte bottom, int range)
3194 {
3195 int i,j;
3196 int above, below, left, right, total;
3197
3198 for (i=0;i<64;i+=2) {
3199 for (j=(i&2);j<64;j+=4) {
3200 fbuf[64*i+j] = bottom + roll(38,range);
3201 }
3202 }
3203
3204 for (i=0;i<64;i+=2) {
3205 for (j=2-(i&2);j<64;j+=4) {
3206 above = (i<2) ? i + 62 : i-2;
3207 below = (i>61) ? i - 62 : i+2;
3208 left = (j<2) ? j + 62 : j-2;
3209 right = (j>61) ? j - 62 : j+2;
3210 total = fbuf[64*above+j] +
3211 fbuf[64*below+j] +
3212 fbuf[64*i+left] +
3213 fbuf[64*i+right];
3214 total >>= 2;
3215 total += roll(39,4) - roll(257,4);
3216 if (total<bottom) total = bottom;
3217 if (total>=bottom+range) total = bottom + range - 1;
3218 fbuf[64*i+j] = total;
3219 }
3220 }
3221
3222 for (i=1;i<64;i+=2) {
3223 for (j=1-(i&1);j<64;j+=2) {
3224 above = (i==0) ? 63 : i-1;
3225 below = (i==63) ? 0 : i+1;
3226 left = (j==0) ? 63 : j-1;
3227 right = (j==63) ? 0 : j+1;
3228 total = fbuf[64*above+j] +
3229 fbuf[64*below+j] +
3230 fbuf[64*i+left] +
3231 fbuf[64*i+right];
3232 total += roll(40,2) - roll(258,2);
3233 if (total<bottom) total = bottom;
3234 if (total>=bottom+range) total = bottom + range - 1;
3235 total >>= 2;
3236 fbuf[64*i+j] = total;
3237 }
3238 }
3239 }
3240
3241 /* Should there be a secret level after the current level? */
need_secret_level(config * c)3242 boolean need_secret_level(config *c)
3243 {
3244 if (c->do_seclevels==FALSE) return FALSE;
3245 if (c->map==15) return TRUE;
3246 if (c->map==31) return TRUE;
3247 switch (c->episode) {
3248 case 1: return (c->mission == 3);
3249 case 2: return (c->mission == 5);
3250 case 3: return (c->mission == 6);
3251 case 4: return (c->mission == 2);
3252 default: return FALSE;
3253 }
3254 }
3255
3256 /* Can this link be locked to the given quest? Note that this is */
3257 /* only called to check if an existing link made up at random can */
3258 /* be locked, so it can false-negative sometimes safely. */
link_fitsq(link * ThisLink,quest * ThisQuest)3259 boolean link_fitsq(link *ThisLink,quest *ThisQuest)
3260 {
3261 if (ThisQuest==NULL) return TRUE; /* Nothing to fit */
3262
3263 if (ThisQuest->goal==GATE_GOAL) {
3264 if (ThisLink->type==OPEN_LINK) return TRUE; /* Easy */
3265 return FALSE; /* else punt */
3266 }
3267
3268 /* Keys and switches require doors */
3269 if ( (ThisQuest->goal==KEY_GOAL) || (ThisQuest->goal==SWITCH_GOAL) ) {
3270 if (!(ThisLink->bits&LINK_NEAR_DOOR)) return FALSE;
3271 if (!(ThisLink->type==BASIC_LINK)) return FALSE;
3272 }
3273 /* Actually because of nukage locks, SWITCH_GOALs don't require */
3274 /* doors. Do something about that here. */
3275 /* For that matter, there are kinds of OPEN_LINK and GATE_LINK */
3276 /* that can fit a SWITCH_GOAL, also. So fix that, too. */
3277 return TRUE;
3278 }
3279
3280 /* Will this link fit along this linedef? */
link_fitsh(linedef * ldf,link * ThisLink,config * c)3281 boolean link_fitsh(linedef *ldf,link *ThisLink,config *c)
3282 {
3283 int available, required;
3284
3285 available = linelen(ldf);
3286 required = ThisLink->width1;
3287
3288 switch (ThisLink->type) {
3289 case BASIC_LINK:
3290 if (required==0) required = 64; /* Minimum to pass, eh? */
3291 if ((ThisLink->bits)&LINK_TWIN) available = (available/2)-16;
3292 if ((ThisLink->bits)&LINK_ALCOVE)
3293 required = required * 2 + ThisLink->depth3;
3294 break;
3295 case OPEN_LINK:
3296 if (required==0) required = 33;
3297 required += 66;
3298 break;
3299 case GATE_LINK:
3300 /* No gate-links outgoing from a gate room, eh? */
3301 if (ldf->right->sector->gate) {
3302 return FALSE;
3303 }
3304 return TRUE;
3305 default:
3306 announce(WARNING,"Funny type in link_fitsh");
3307 return FALSE;
3308 }
3309
3310 return (available>=required);
3311 }
3312
3313 /* Make the given linedefs (which currently form an open archway) */
3314 /* into a cool set-of-bars door (appropriate to the quest). For */
3315 /* the door/bar sectors, use newsector, or a new one if NULL. */
3316 /* Note that barwidth must be 32 or less, because the algorithm */
3317 /* has some quirks that I really ought to fix; if barwidth is too */
3318 /* big, it can recurse forever or something like that. */
barify(level * l,linedef * ldf1,linedef * ldf2,quest * ThisQuest,int barwidth,sector * newsector,style * ThisStyle,config * c)3319 void barify(level *l,linedef *ldf1,linedef *ldf2,quest *ThisQuest,
3320 int barwidth,sector *newsector,style *ThisStyle,config *c)
3321 {
3322 linedef *ld1a, *ld1b, *ld2a, *ld2b, *ldedge1, *ldedge2;
3323 sector *oldsector;
3324 texture *t1;
3325 short type1;
3326
3327 if (linelen(ldf1)<=32) return; /* Already impassable */
3328 /* Get a handle on the sectors involved */
3329 oldsector = ldf1->left->sector;
3330 if (newsector==NULL) {
3331 newsector = clone_sector(l,oldsector);
3332 newsector->ceiling_height = newsector->floor_height; /* close it! */
3333 if (ThisQuest)
3334 if (ThisQuest->goal==SWITCH_GOAL) newsector->tag = ThisQuest->tag;
3335 announce(VERBOSE,"Multiple");
3336 }
3337 /* Then recurse to get the side bars, if needed */
3338 ld1a = centerpart(l,ldf1,&ld1b,barwidth,ThisStyle,c);
3339 ld2a = centerpart(l,ldf2,&ld2b,barwidth,ThisStyle,c);
3340 barify(l,ldf1,ld2b,ThisQuest,barwidth,newsector,ThisStyle,c);
3341 barify(l,ld1b,ldf2,ThisQuest,barwidth,newsector,ThisStyle,c);
3342 /* Now frame the center section, the a's, with linedefs */
3343 ldedge1 = new_linedef(l,ld2a->to,ld1a->from);
3344 ldedge2 = new_linedef(l,ld1a->to,ld2a->from);
3345 /* Fix up existing sidedefs */
3346 ld1a->left->sector = newsector;
3347 ld1a->flags &= ~UPPER_UNPEGGED;
3348 ld1a->right->x_offset = 0;
3349 ld2a->left->sector = newsector;
3350 ld2a->flags &= ~UPPER_UNPEGGED;
3351 ld2a->right->x_offset = 0;
3352 /* And make some new ones */
3353 ldedge1->left = new_sidedef(l,newsector,c);
3354 ldedge1->right = new_sidedef(l,oldsector,c);
3355 ldedge1->flags |= TWO_SIDED;
3356 ldedge2->left = new_sidedef(l,newsector,c);
3357 ldedge2->right = new_sidedef(l,oldsector,c);
3358 ldedge2->flags |= TWO_SIDED;
3359 /* Decide on a texture for the bar faces */
3360 t1 = ThisStyle->support0; /* Or wall0? */
3361 if (ThisQuest)
3362 if (ThisQuest->goal==KEY_GOAL)
3363 t1 = texture_for_key(ThisQuest->type,ThisStyle,c);
3364 /* and the opening linedef type */
3365 type1 = ThisStyle->doortype;
3366 if (ThisQuest)
3367 if (ThisQuest->goal==KEY_GOAL) type1 = type_for_key(ThisQuest->type);
3368 else if (ThisQuest->goal==SWITCH_GOAL)
3369 type1 = (c->do_dm) ? LINEDEF_NORMAL_S1_DOOR : LINEDEF_NORMAL;
3370 /* Now fill in all the textures and stuff */
3371 ld1a->type = type1;
3372 ld2a->type = type1;
3373 ld1a->right->upper_texture = t1;
3374 ld2a->right->upper_texture = t1;
3375 ldedge1->left->middle_texture = c->null_texture;
3376 ldedge1->right->middle_texture = c->null_texture;
3377 ldedge1->right->upper_texture = t1;
3378 ldedge2->left->middle_texture = c->null_texture;
3379 ldedge2->right->middle_texture = c->null_texture;
3380 ldedge2->right->upper_texture = t1;
3381 /* Record that we did that */
3382 l->barcount++;
3383
3384 } /* end barify */
3385
3386 /* Take the given linedefs (which are currently antiparallel with */
3387 /* just the void between them), and put in a nice too-narrow-to- */
3388 /* pass slit. Or, sometimes, split the current linedefs in half */
3389 /* and recurse on the halves, for a set of slits. Use the given */
3390 /* sector for the sector in the slits, or make one if NULL. */
3391 /* Sort of like both barify() and make_window(). */
slitify(level * l,linedef * ldf1,linedef * ldf2,int slitwidth,sector * newsector,style * ThisStyle,config * c)3392 boolean slitify(level *l,linedef *ldf1,linedef *ldf2, int slitwidth,
3393 sector *newsector,style *ThisStyle,config *c)
3394 {
3395 linedef *ld1a, *ld2a, *ldedge1, *ldedge2;
3396 sector *nearsector, *farsector;
3397 short newch, newch2, newfh, newfh2;
3398 int len = linelen(ldf1);
3399
3400 /* Get a handle on the sectors involved */
3401 nearsector = ldf1->right->sector;
3402 farsector = ldf2->right->sector;
3403 /* Invent the new one, if needed */
3404 if (newsector==NULL) {
3405 newfh = nearsector->floor_height;
3406 if (farsector->floor_height<newfh) newfh = farsector->floor_height;
3407 if (rollpercent(142,30)) {
3408 newfh2 = newfh + 4 * roll(56,9);
3409 if (newfh2 > (nearsector->ceiling_height - 32)) newfh2 = newfh;
3410 if (newfh2 > (farsector->ceiling_height - 32)) newfh2 = newfh;
3411 newfh = newfh2;
3412 }
3413 newch = nearsector->ceiling_height;
3414 if (farsector->ceiling_height>newch) newch = farsector->ceiling_height;
3415 if (rollpercent(143,30)) {
3416 newch2 = newfh + 32 + 8 * roll(57,9);
3417 if (newch2>newch) newch2 = newch;
3418 if (newch < (nearsector->floor_height + 32)) newch2 = newch;
3419 if (newch < (farsector->floor_height + 32)) newch2 = newch;
3420 newch = newch2;
3421 }
3422 newsector = clone_sector(l,nearsector);
3423 newsector->floor_height = newfh;
3424 newsector->ceiling_height = newch;
3425 }
3426 /* Sometimes just recurse */
3427 if ((len>(16+slitwidth+slitwidth)) &&
3428 (rollpercent(144,60))) {
3429 ld1a = split_linedef(l,ldf1,len/2,c);
3430 ld2a = split_linedef(l,ldf2,len/2,c);
3431 slitify(l,ldf1,ld2a,slitwidth,newsector,ThisStyle,c);
3432 slitify(l,ld1a,ldf2,slitwidth,newsector,ThisStyle,c);
3433 } else {
3434 ld1a = centerpart(l,ldf1,NULL,slitwidth,ThisStyle,c);
3435 ld2a = centerpart(l,ldf2,NULL,slitwidth,ThisStyle,c);
3436 ldedge1 = new_linedef(l,ld2a->from,ld1a->to);
3437 ldedge2 = new_linedef(l,ld1a->from,ld2a->to);
3438 /* Fix up existing sidedefs */
3439 ld1a->right->middle_texture = c->null_texture; /* Or grating? */
3440 ld1a->flags |= TWO_SIDED;
3441 ld2a->right->middle_texture = c->null_texture; /* Or grating? */
3442 ld2a->flags |= TWO_SIDED;
3443 /* and make new ones */
3444 ldedge1->right = new_sidedef(l,newsector,c);
3445 ldedge1->right->middle_texture = ldf1->right->middle_texture;
3446 ldedge2->right = new_sidedef(l,newsector,c);
3447 ldedge2->right->middle_texture = ldf1->right->middle_texture;
3448 ldedge1->right->y_offset = ldedge2->right->y_offset =
3449 nearsector->ceiling_height - newsector->ceiling_height;
3450 ld1a->left = new_sidedef(l,newsector,c);
3451 ld1a->left->middle_texture = c->null_texture;
3452 patch_upper(ld1a,ldf1->right->middle_texture,c);
3453 patch_lower(ld1a,ThisStyle->support0,c);
3454 ld2a->left = new_sidedef(l,newsector,c);
3455 ld2a->left->middle_texture = c->null_texture;
3456 patch_upper(ld2a,ldf2->right->sector->style->wall0,c);
3457 patch_lower(ld2a,ldf2->right->sector->style->support0,c);
3458 announce(VERBOSE,"Slit");
3459 }
3460
3461 return TRUE;
3462
3463 } /* end slitify */
3464
3465 /* OK, so you have a single square sector bounded by ldf1 and ldf2 */
3466 /* at the ends, and lde1 and lde2 at the sides. They point */
3467 /* *counterclockwise* around the outside of the sector. This */
3468 /* will make it into a flight of climbable stairs from nearheight */
3469 /* at the ldf1 end to farheight at the ldf2 end. */
3470 /* Should level-hugeness do anything in here? */
stairify(level * l,linedef * ldf1,linedef * ldf2,linedef * lde1,linedef * lde2,short nearheight,short farheight,quest * ThisQuest,style * ThisStyle,config * c)3471 void stairify(level *l,linedef *ldf1,linedef *ldf2,linedef *lde1,
3472 linedef *lde2,short nearheight,short farheight,
3473 quest *ThisQuest, style *ThisStyle, config *c)
3474 {
3475 linedef *ldn1, *ldn2, *ldl;
3476 sector *s, *nearsec;
3477 int len, stepdepth, i;
3478 int minstepcount, maxstepcount, stepcount, stepheight;
3479 boolean need_lock = (ThisQuest!=NULL) && (ThisQuest->goal==SWITCH_GOAL);
3480 texture *front = ThisStyle->kickplate;
3481 boolean do_edges = FALSE;
3482
3483 nearsec = ldf1->right->sector;
3484 len = linelen(lde1);
3485
3486 /* Need at least enough steps to get up, 24 at a time */
3487 minstepcount = (farheight-nearheight)/24;
3488 /* Want steps at least 24 deep to fit in the space */
3489 maxstepcount = len/24;
3490
3491 stepcount = minstepcount + roll(58,1+(maxstepcount-minstepcount));
3492 stepdepth = len/stepcount;
3493 stepheight = (farheight-nearheight)/stepcount;
3494
3495 /* Hack, to avoid having to actually understand the above */
3496 if (stepheight>24) {
3497 stepcount = stepcount + 1;
3498 stepdepth = len/stepcount;
3499 stepheight = (farheight-nearheight)/stepcount;
3500 }
3501 if (stepheight>24)
3502 announce(ERROR,"Step too high to climb!");
3503 if (need_lock) {
3504 announce(LOG,"Locked stairs");
3505 stepheight = 8;
3506 stepcount = (farheight-nearheight)/stepheight;
3507 stepdepth = len/stepcount;
3508 }
3509
3510 {
3511 char s[100];
3512 sprintf(s,"%d steps from [%d-%d], each %d deep and %d high.\n",
3513 stepcount,minstepcount,maxstepcount,stepdepth,stepheight);
3514 announce(VERBOSE,s);
3515 sprintf(s,"Total: %d deep, %d high.\n",len,farheight-nearheight);
3516 announce(VERBOSE,s);
3517 }
3518
3519 if (ThisStyle->stepfront)
3520 if (abs(stepheight)<=ThisStyle->stepfront->height)
3521 front = ThisStyle->stepfront;
3522
3523 if (ThisStyle->walllight) {
3524 if (ThisStyle->light_steps) {
3525 front = ThisStyle->walllight;
3526 } else if (ThisStyle->light_edges &&
3527 (linelen(ldf1)>=(64*l->hugeness)) && (stepheight>7) ) {
3528 do_edges = TRUE;
3529 announce(VERBOSE,"Step-edge lights");
3530 }
3531 }
3532
3533 if (need_lock) {
3534 ThisQuest->type = LINEDEF_S1_RAISE_STAIRS;
3535 }
3536
3537 ldf1->right->lower_texture = front;
3538 ldf1->flags &= ~LOWER_UNPEGGED;
3539
3540 for (i=0;i<(stepcount-1);i++) { /* Minus one, since one's already made */
3541 s = clone_sector(l,nearsec);
3542 s->ceiling_height = ldf2->right->sector->ceiling_height;
3543 if (need_lock) if (i==0) s->tag = ThisQuest->tag;
3544 len -= stepdepth;
3545 ldn1 = split_linedef(l,lde1,len,c);
3546 ldn2 = lde2;
3547 lde2 = split_linedef(l,ldn2,stepdepth,c);
3548 ldn1->left->sector = s;
3549 ldn2->left->sector = s;
3550 ldf1->left->sector = s;
3551 if (do_edges) {
3552 ldl = split_linedef(l,ldf1,linelen(ldf1)-16*l->hugeness,c);
3553 ldl->right->lower_texture = ThisStyle->walllight;
3554 split_linedef(l,ldf1,16*l->hugeness,c);
3555 ldf1->right->lower_texture = ThisStyle->walllight;
3556 }
3557 nearheight += stepheight;
3558 s->floor_height = nearheight;
3559 ldf1 = new_linedef(l,ldn1->from,ldn2->to);
3560 ldf1->right = new_sidedef(l,s,c);
3561 ldf1->right->lower_texture = front;
3562 ldf1->flags &= ~LOWER_UNPEGGED;
3563 ldf1->right->middle_texture = c->null_texture;
3564 ldf1->left = new_sidedef(l,s,c); /* s is wrong; fixed above/below */
3565 ldf1->left->middle_texture = c->null_texture;
3566 ldf1->flags |= TWO_SIDED;
3567 if (need_lock) s->floor_height = nearsec->floor_height;
3568 if (!need_lock) { /* recalc to avoid top-step-looks-silly bug */
3569 stepheight = (farheight-nearheight)/(stepcount-(i+1));
3570 if (abs(stepheight)>front->height) front = ThisStyle->kickplate;
3571 }
3572 }
3573 ldf1->left->sector = ldf2->left->sector;
3574 patch_lower(ldf1,front,c);
3575 ldf1->flags &= ~LOWER_UNPEGGED;
3576 if (do_edges) {
3577 ldl = split_linedef(l,ldf1,linelen(ldf1)-16*l->hugeness,c);
3578 ldl->right->lower_texture = ThisStyle->walllight;
3579 split_linedef(l,ldf1,16*l->hugeness,c);
3580 ldf1->right->lower_texture = ThisStyle->walllight;
3581 }
3582 if (need_lock) {
3583 ldf2->left->sector->floor_height = nearsec->floor_height;
3584 ldf2->left->sector->floor_flat = nearsec->floor_flat;
3585 }
3586 }
3587
3588 /* Make the given sector into a standard door, opened by the */
3589 /* given linedefs. Doesn't do any flipping, or alter jambs. */
doorify(sector * s,linedef * ldf1,linedef * ldf2,style * ThisStyle,style * NewStyle,config * c)3590 void doorify(sector *s,linedef *ldf1,linedef *ldf2,
3591 style *ThisStyle,style *NewStyle, config *c)
3592 {
3593 /* Needs to use style more, but almost done */
3594 int lensq;
3595
3596 s->ceiling_height = s->floor_height;
3597 s->floor_flat = ThisStyle->doorfloor;
3598 s->ceiling_flat = ThisStyle->doorceiling;
3599 /* This should be from the style or link or role or something */
3600 ldf1->type = ThisStyle->doortype;
3601 lensq = lengthsquared(ldf1);
3602 /* This "100" should be determined from the width of the style textures */
3603 if (lensq>(100*100)) { /* One of the wide textures */
3604 if (ldf1->right->sector->ceiling_height-s->floor_height>
3605 ThisStyle->widedoorface->height)
3606 ldf1->right->upper_texture = ThisStyle->twdoorface;
3607 else ldf1->right->upper_texture = ThisStyle->widedoorface;
3608 if (lensq<(128*128)) /* "128" is wrong */
3609 ldf1->right->x_offset = (128 - linelen(ldf1))/2; /* All of these */
3610 else ldf1->right->x_offset = 128 - (linelen(ldf1)%128)/2;
3611 /* Avoid TFE! */
3612 if (ldf1->right->upper_texture->height<128)
3613 if (ldf1->right->sector->ceiling_height-ldf1->right->sector->floor_height>
3614 ldf1->right->upper_texture->height)
3615 ldf1->right->upper_texture = ThisStyle->twdoorface;
3616 } else {
3617 if (ldf1->right->sector->ceiling_height-s->floor_height>
3618 ThisStyle->narrowdoorface->height)
3619 ldf1->right->upper_texture = ThisStyle->tndoorface;
3620 else ldf1->right->upper_texture = ThisStyle->narrowdoorface;
3621 if (lensq<(64*64)) /* Also "64" */
3622 ldf1->right->x_offset = (64-linelen(ldf1))/2; /* All of these */
3623 else ldf1->right->x_offset = 64 - (linelen(ldf1)%64)/2;
3624 /* Avoid TFE! */
3625 if (ldf1->right->upper_texture->height<128)
3626 if (ldf1->right->sector->ceiling_height-ldf1->right->sector->floor_height>
3627 ldf1->right->upper_texture->height)
3628 ldf1->right->upper_texture = ThisStyle->tndoorface;
3629 }
3630 ldf2->type = ldf1->type;
3631 lensq = lengthsquared(ldf2);
3632 /* This "100" should be determined from the width of the style textures? */
3633 if (lensq>(100*100)) { /* One of the wide textures */
3634 if (ldf2->right->sector->ceiling_height-s->floor_height>
3635 NewStyle->widedoorface->height)
3636 ldf2->right->upper_texture = NewStyle->twdoorface;
3637 else ldf2->right->upper_texture = NewStyle->widedoorface;
3638 if (lensq<(128*128))
3639 ldf2->right->x_offset = (128 - linelen(ldf2))/2;
3640 else ldf2->right->x_offset = 128 - (linelen(ldf2)%128)/2;
3641 /* Avoid TFE! */
3642 if (ldf2->right->upper_texture->height<128)
3643 if (ldf2->right->sector->ceiling_height-ldf2->right->sector->floor_height>
3644 ldf2->right->upper_texture->height)
3645 ldf2->right->upper_texture = NewStyle->twdoorface;
3646 } else {
3647 if (ldf2->right->sector->ceiling_height-s->floor_height>
3648 NewStyle->narrowdoorface->height)
3649 ldf2->right->upper_texture = NewStyle->tndoorface;
3650 else ldf2->right->upper_texture = NewStyle->narrowdoorface;
3651 if (lensq<(64*64))
3652 ldf2->right->x_offset = (64 - linelen(ldf2))/2;
3653 else ldf2->right->x_offset = 64 - (linelen(ldf2)%64)/2;
3654 /* Avoid TFE! */
3655 if (ldf2->right->upper_texture->height<128)
3656 if (ldf2->right->sector->ceiling_height-ldf2->right->sector->floor_height>
3657 ldf2->right->upper_texture->height)
3658 ldf2->right->upper_texture = NewStyle->tndoorface;
3659 }
3660 #ifdef SOMETIMES_UNPEG_DOORFACES
3661 if (!ThisStyle->secret_doors) /* Doors secret-flavor? */
3662 #endif
3663 {
3664 ldf1->flags &= ~UPPER_UNPEGGED;
3665 ldf2->flags &= ~UPPER_UNPEGGED;
3666 }
3667 if (ThisStyle->soundproof_doors) {
3668 ldf1->flags |= BLOCK_SOUND;
3669 ldf2->flags |= BLOCK_SOUND;
3670 }
3671 /* And in any case avoid stoop silliness */
3672 ldf1->flags &= ~LOWER_UNPEGGED;
3673 ldf2->flags &= ~LOWER_UNPEGGED;
3674
3675 } /* end doorify */
3676
3677 /* Make a window between the given antiparallel linedefs */
make_window_inner(level * l,linedef * ldf1,linedef * ldf2,link * ThisLink,style * ThisStyle,style * NewStyle,config * c)3678 boolean make_window_inner(level *l,linedef *ldf1,linedef *ldf2,link *ThisLink,
3679 style *ThisStyle,style *NewStyle,config *c)
3680 {
3681 linedef *ldnew1, *ldnew2;
3682 sector *nearsec, *farsec, *newsec;
3683 texture *t1, *t2;
3684 int len;
3685
3686 announce(VERBOSE,"Making a window");
3687
3688 nearsec = ldf1->right->sector;
3689 farsec = ldf2->right->sector;
3690 t1 = ldf1->right->middle_texture;
3691 t2 = NewStyle->wall0;
3692
3693 /* Make sure a window is possible */
3694 if (nearsec->floor_height+ThisStyle->sillheight>farsec->ceiling_height-16)
3695 return FALSE;
3696 if (nearsec->floor_height+ThisStyle->sillheight+ThisStyle->windowheight<
3697 farsec->floor_height+16)
3698 return FALSE;
3699
3700 if (ThisStyle->slitwindows) {
3701 return slitify(l,ldf1,ldf2,16+roll(59,17),NULL,ThisStyle,c);
3702 }
3703
3704 /* Put a little border on it. Very simple version for now. */
3705 ldf1 = split_linedef(l,ldf1,ThisStyle->windowborder,c);
3706 len = linelen(ldf1);
3707 split_linedef(l,ldf1,len-ThisStyle->windowborder,c);
3708 ldf2 = split_linedef(l,ldf2,ThisStyle->windowborder,c);
3709 len = linelen(ldf2);
3710 split_linedef(l,ldf2,len-ThisStyle->windowborder,c);
3711
3712 flip_linedef(ldf2); /* parallelize, for make_box */
3713 newsec = make_box_ext(l,ldf1,ldf2,ThisStyle,c,&ldnew1,&ldnew2);
3714 flip_linedef(ldf2);
3715
3716 newsec->floor_height = nearsec->floor_height + ThisStyle->sillheight;
3717 newsec->ceiling_height = newsec->floor_height + ThisStyle->windowheight;
3718 newsec->light_level = ThisStyle->doorlight0; /* Wrongish */
3719 newsec->style = ThisStyle; /* Is this right? */
3720
3721 /* Various possibilities for window decor */
3722 switch (ThisStyle->window_decor) {
3723 case WINDOW_JAMBS:
3724 ldnew1->right->middle_texture = ThisStyle->doorjamb;
3725 ldnew2->right->middle_texture = ThisStyle->doorjamb;
3726 break;
3727 case WINDOW_SUPPORT:
3728 ldnew1->right->middle_texture = ThisStyle->support0;
3729 ldnew2->right->middle_texture = ThisStyle->support0;
3730 break;
3731 case WINDOW_LIGHT:
3732 make_lighted(l,newsec,c);
3733 if (ThisStyle->walllight) {
3734 ldnew1->right->middle_texture = ThisStyle->walllight;
3735 ldnew2->right->middle_texture = ThisStyle->walllight;
3736 announce(VERBOSE,"Lit window");
3737 } else {
3738 ldnew1->right->middle_texture = ThisStyle->support0;
3739 ldnew2->right->middle_texture = ThisStyle->support0;
3740 }
3741 break;
3742 default: /* WINDOW_NORMAL */
3743 ldnew1->right->y_offset =
3744 ldnew2->right->y_offset =
3745 nearsec->ceiling_height - newsec->ceiling_height;
3746 break;
3747 }
3748 if (ThisStyle->window_grate) {
3749 ldf1->right->middle_texture = ThisStyle->grating;
3750 /* Unpeg, to keep the texture from floating away, eh? */
3751 ldf1->flags |= LOWER_UNPEGGED;
3752 ldf1->flags |= TWO_SIDED | IMPASSIBLE;
3753 #ifdef OLD_FUNNY_WINDOWGRATES
3754 ldf2->right->middle_texture = NewStyle->grating;
3755 ldf2->flags |= LOWER_UNPEGGED;
3756 ldf2->flags |= TWO_SIDED | IMPASSIBLE;
3757 #else
3758 ldf1->left->middle_texture = ThisStyle->grating;
3759 ldf2->flags |= TWO_SIDED;
3760 #endif
3761 announce(VERBOSE,"Window grate");
3762 } else {
3763 ldf1->flags |= TWO_SIDED | IMPASSIBLE;
3764 ldf2->flags |= TWO_SIDED | IMPASSIBLE;
3765 }
3766
3767 ldf1->left->y_offset = ldf1->right->y_offset =
3768 ldf2->left->y_offset = ldf2->right->y_offset = 0;
3769
3770 /* Should windows ever block sound? Prolly not! */
3771
3772 /* Prevent texture-bleeding bug. Will this do it? */
3773 /* Possibly the LOWER_UNPEGging above did it alread. */
3774 if (ThisStyle->window_grate) {
3775 if (newsec->floor_height==nearsec->floor_height)
3776 newsec->floor_height++;
3777 if (newsec->floor_height==farsec->floor_height)
3778 newsec->floor_height++;
3779 }
3780
3781 patch_upper(ldf1,t1,c);
3782 patch_upper(ldf2,t2,c);
3783 patch_lower(ldf1,t1,c);
3784 patch_lower(ldf2,t2,c);
3785
3786 return TRUE;
3787
3788 } /* end make_window_inner */
3789
3790 /* Make a window between the given antiparallel linedefs, */
3791 /* possibly elaborately. */
make_window(level * l,linedef * ldf1,linedef * ldf2,link * ThisLink,style * ThisStyle,style * NewStyle,config * c)3792 boolean make_window(level *l,linedef *ldf1,linedef *ldf2,link *ThisLink,
3793 style *ThisStyle,style *NewStyle,config *c)
3794 {
3795 if ((distancesquared(ldf1->to->x,ldf1->to->y,ldf2->from->x,ldf2->from->y)
3796 >=(l->hugeness*l->hugeness*96*96)) && c->window_airshafts) {
3797 linedef *ld1n, *ld2n, *lde1, *lde2;
3798 sector *newsec;
3799 short newfh, newch;
3800 sector *nearsec = ldf1->right->sector;
3801 sector *farsec = ldf2->right->sector;
3802 boolean rc1, rc2;
3803 ld1n = make_parallel(l,ldf1,16*l->hugeness,NULL);
3804 flip_linedef(ld1n);
3805 ld2n = make_parallel(l,ldf2,16*l->hugeness,NULL);
3806 flip_linedef(ld2n);
3807 lde1 = new_linedef(l,ld1n->to,ld2n->from);
3808 lde2 = new_linedef(l,ld2n->to,ld1n->from);
3809 newfh = nearsec->floor_height;
3810 if (farsec->floor_height<newfh) newfh = farsec->floor_height;
3811 if (rollpercent(145,50)) newfh -= 8 * roll(60,10);
3812 newch = nearsec->ceiling_height;
3813 if (farsec->ceiling_height>newch) newch = farsec->ceiling_height;
3814 newch += 16 + 8 * roll(61,10);
3815 newsec = new_sector(l,newfh,newch,random_flat0(OUTDOOR,c,NULL),
3816 nearsec->ceiling_flat);
3817 newsec->style =
3818 copy_style(l,nearsec->style,nearsec->style->theme_number,0,c);
3819 newsec->style->roomlight0 = l->outside_light_level;
3820 /* Do we want to make the walls OUTDOOR here? */
3821 ld1n->right = new_sidedef(l,newsec,c);
3822 ld2n->right = new_sidedef(l,newsec,c);
3823 lde1->right = new_sidedef(l,newsec,c);
3824 lde2->right = new_sidedef(l,newsec,c);
3825 paint_room(l,newsec,newsec->style,c);
3826 place_plants(l,48,newsec,c); /* Put in some plants for decor */
3827 rc1 = make_window_inner(l,ldf1,ld1n,ThisLink,ThisStyle,newsec->style,c);
3828 newsec->style->sillheight += farsec->floor_height - newsec->floor_height;
3829 rc2 = make_window_inner(l,ld2n,ldf2,ThisLink,newsec->style,NewStyle,c);
3830 newsec->ceiling_flat = c->sky_flat;
3831 if (rollpercent(146,l->p_force_nukage)) {
3832 newsec->floor_flat = newsec->style->nukage1;
3833 newsec->special = NUKAGE1_SPECIAL;
3834 }
3835 if (rc1||rc2) announce(LOG,"Window airshaft");
3836 return rc1 || rc2;
3837 } else {
3838 return make_window_inner(l,ldf1,ldf2,ThisLink,ThisStyle,NewStyle,c);
3839 }
3840 }
3841
3842 /* Make a decorative room between the given antiparallel linedefs */
make_decroom(level * l,linedef * ldf1,linedef * ldf2,config * c)3843 boolean make_decroom(level *l,linedef *ldf1,linedef *ldf2,config *c)
3844 {
3845 linedef *ldnew1, *ldnew2;
3846 sector *nearsec, *farsec, *newsec;
3847 texture *t1;
3848 int len;
3849 style *ThisStyle = ldf1->right->sector->style;
3850
3851 nearsec = ldf1->right->sector;
3852 farsec = ldf2->right->sector;
3853 t1 = ldf1->right->middle_texture;
3854
3855 /* Put a little border on it. Very simple version for now. */
3856 /* Don't really need at all if recesses etc, eh? */
3857 ldf1 = split_linedef(l,ldf1,ThisStyle->windowborder,c);
3858 len = linelen(ldf1);
3859 split_linedef(l,ldf1,len-ThisStyle->windowborder,c);
3860 ldf2 = split_linedef(l,ldf2,ThisStyle->windowborder,c);
3861 len = linelen(ldf2);
3862 split_linedef(l,ldf2,len-ThisStyle->windowborder,c);
3863
3864 flip_linedef(ldf2); /* parallelize, for make_box */
3865 newsec = make_box_ext(l,ldf1,ldf2,ThisStyle,c,&ldnew1,&ldnew2);
3866 flip_linedef(ldf2);
3867
3868 newsec->floor_height = nearsec->floor_height - 8 * (roll(62,4));
3869 newsec->ceiling_height = nearsec->ceiling_height + 32 + 8 * (roll(63,6));
3870 newsec->light_level = l->outside_light_level;
3871 newsec->ceiling_flat = c->sky_flat;
3872 if (rollpercent(147,10)||rollpercent(438,l->p_force_nukage)) {
3873 newsec->floor_flat = ThisStyle->nukage1;
3874 newsec->special = NUKAGE1_SPECIAL; /* Not that you can get in there! */
3875 announce(LOG,"Intertwin nukage");
3876 } else {
3877 newsec->floor_flat = random_flat0(OUTDOOR,c,NULL);
3878 }
3879 newsec->style = ThisStyle;
3880
3881 ldnew1->right->y_offset =
3882 ldnew2->right->y_offset =
3883 nearsec->ceiling_height - newsec->ceiling_height;
3884 ldf1->right->middle_texture = ThisStyle->grating;
3885 ldf1->left->middle_texture = ThisStyle->grating;
3886 ldf2->right->middle_texture = ThisStyle->grating;
3887 ldf2->left->middle_texture = ThisStyle->grating;
3888 /* Unpeg, to keep the texture from floating away, eh? */
3889 ldf1->flags |= LOWER_UNPEGGED;
3890 ldf2->flags |= LOWER_UNPEGGED;
3891
3892 ldf1->flags |= TWO_SIDED | IMPASSIBLE;
3893 ldf2->flags |= TWO_SIDED | IMPASSIBLE;
3894 ldf1->left->y_offset = ldf1->right->y_offset =
3895 ldf2->left->y_offset = ldf2->right->y_offset = 0;
3896
3897 patch_upper(ldf1,t1,c);
3898 patch_upper(ldf2,t1,c);
3899 patch_lower(ldf1,t1,c);
3900 patch_lower(ldf2,t1,c);
3901
3902 len = linelen(ldnew1);
3903 if (len>31) { /* Inset a bit */
3904 linedef *lt1, *lt2;
3905 /* Unhook from alignment groups, for simplicity */
3906 if (ldf1->group_previous) {
3907 ldf1->group_previous->group_next = NULL;
3908 ldf1->group_previous = NULL;
3909 }
3910 if (ldf1->group_next) {
3911 ldf1->group_next->group_previous = NULL;
3912 ldf1->group_next = NULL;
3913 }
3914 if (ldf2->group_previous) {
3915 ldf2->group_previous->group_next = NULL;
3916 ldf2->group_previous = NULL;
3917 }
3918 if (ldf2->group_next) {
3919 ldf2->group_next->group_previous = NULL;
3920 ldf2->group_next = NULL;
3921 }
3922 /* Do it */
3923 lt1 = split_linedef(l,ldnew1,8,c); /* 8's should vary */
3924 ldnew1->right->sector = ldf1->right->sector;
3925 ldnew1->right->y_offset = ldf1->right->y_offset;
3926 ldf1->from = ldnew1->to;
3927 lt2 = split_linedef(l,ldnew2,8,c);
3928 ldnew2->right->sector = ldf2->right->sector;
3929 ldnew2->right->y_offset = ldf2->right->y_offset;
3930 ldf2->from = ldnew2->to;
3931 lt2 = split_linedef(l,lt2,len-16,c);
3932 lt2->right->sector = ldf1->right->sector;
3933 lt2->right->y_offset = ldf1->right->y_offset;
3934 ldf1->to = lt2->from;
3935 lt1 = split_linedef(l,lt1,len-16,c);
3936 lt1->right->sector = ldf2->right->sector;
3937 lt1->right->y_offset = ldf2->right->y_offset;
3938 ldf2->to = lt1->from;
3939 }
3940
3941 place_plants(l,48,newsec,c); /* Put in some plants for decor */
3942
3943 return TRUE;
3944
3945 } /* end make_decroom */
3946
texture_for_key(short key,style * s,config * c)3947 texture *texture_for_key(short key, style *s, config *c)
3948 {
3949 switch (key) {
3950 case ID_BLUEKEY:
3951 case ID_BLUECARD: return s->blueface;
3952 case ID_REDKEY:
3953 case ID_REDCARD: return s->redface;
3954 case ID_YELLOWKEY:
3955 case ID_YELLOWCARD: return s->yellowface;
3956 }
3957 announce(WARNING,"Unknown key in texture_for_key()");
3958 return c->error_texture;
3959 }
3960
texture_for_bits(propertybits pb,style * s,config * c)3961 texture *texture_for_bits(propertybits pb, style *s, config *c)
3962 {
3963 texture *answer = NULL;
3964
3965 switch (pb) {
3966 case BLUE: answer = s->blueface; break;
3967 case RED: answer = s->redface; break;
3968 case YELLOW: answer = s->yellowface; break;
3969 case LIGHT: answer = s->walllight; break;
3970 }
3971 if (answer==NULL) answer = s->wall0;
3972 return answer;
3973 }
3974
type_for_key(short key)3975 short type_for_key(short key)
3976 {
3977 switch (key) {
3978 case ID_BLUEKEY:
3979 case ID_BLUECARD: return LINEDEF_BLUE_S1_DOOR;
3980 case ID_REDKEY:
3981 case ID_REDCARD: return LINEDEF_RED_S1_DOOR;
3982 case ID_YELLOWKEY:
3983 case ID_YELLOWCARD: return LINEDEF_YELLOW_S1_DOOR;
3984 }
3985 announce(WARNING,"Unknown key in type_for_key()");
3986 return LINEDEF_NORMAL_S1_DOOR;
3987 }
3988
3989 /* Mark the given door of the given level to look like it's locked */
3990 /* with the given key (thingid). */
mark_door_for_key(level * l,linedef * ldf1,short key,style * ThisStyle,config * c)3991 void mark_door_for_key(level *l,linedef *ldf1,short key,
3992 style *ThisStyle,config *c)
3993 {
3994 linedef *ldf2;
3995 texture *t1;
3996
3997 if (ThisStyle->gaudy_locks) {
3998 announce(VERBOSE,"Gaudy door");
3999 ldf1->right->upper_texture =
4000 texture_for_key(key,ThisStyle,c);
4001 } else {
4002 t1 = texture_for_key(key,ThisStyle,c);
4003 ldf2 = split_linedef(l,ldf1,16,c); /* '16' is wrong, but not bad */
4004 ldf1->right->upper_texture = t1;
4005 ldf2 = split_linedef(l,ldf2,linelen(ldf2)-16,c);
4006 ldf2->right->upper_texture = t1;
4007 }
4008 }
4009
mark_door_for_lock(level * l,linedef * ldf1,style * ThisStyle,config * c)4010 void mark_door_for_lock(level *l,linedef *ldf1,style *ThisStyle,config *c)
4011 {
4012 if (ThisStyle->lockdoorface==NULL) return;
4013 if (ThisStyle->lockdoorface->height!=128)
4014 if (ThisStyle->lockdoorface->height<
4015 (ldf1->right->sector->ceiling_height-ldf1->right->sector->floor_height))
4016 return;
4017 ldf1->right->upper_texture = ThisStyle->lockdoorface;
4018 announce(VERBOSE,"Specially marked door");
4019 }
4020
4021 /* Given a linedef type, return the equivalent linedef type, */
4022 /* only locked with the given key. If there isn't one, return 0 */
locked_linedef_for(short type,short key,config * c)4023 short locked_linedef_for(short type,short key,config *c)
4024 {
4025 switch (type) {
4026 case LINEDEF_S1_OPEN_DOOR:
4027 if (DOOM0_BIT & c->gamemask) return 0; /* Not in ancient DooMs */
4028 switch (key) {
4029 case ID_BLUEKEY:
4030 case ID_BLUECARD:
4031 return LINEDEF_S1_OPEN_DOOR_BLUE;
4032 case ID_REDKEY:
4033 case ID_REDCARD:
4034 return LINEDEF_S1_OPEN_DOOR_RED;
4035 case ID_YELLOWKEY:
4036 case ID_YELLOWCARD:
4037 return LINEDEF_S1_OPEN_DOOR_YELLOW;
4038 default:
4039 announce(ERROR,"Unknown key in l_l_f");
4040 return 0;
4041 }
4042 default: return 0;
4043 }
4044 }
4045
4046 /* Make the sector look likeit's in the range of a light */
make_lighted(level * l,sector * s,config * c)4047 void make_lighted(level *l, sector *s, config *c)
4048 {
4049 /* Too many hardcoded constants! */
4050 if (rollpercent(148,60))
4051 if (s->light_level<l->lit_light_level)
4052 s->light_level = l->lit_light_level;
4053 if (rollpercent(149,10))
4054 s->special = RANDOM_BLINK;
4055 }
4056
4057 /* Make a nice box with a thing to the left of the linedef */
lightbox(level * l,linedef * ld,genus * g,style * ThisStyle,config * c)4058 linedef *lightbox(level *l,linedef *ld,genus *g,style *ThisStyle,config *c)
4059 {
4060 int len;
4061 linedef *ldb;
4062 int x,y;
4063 sector *oldsec, *newsec;
4064
4065 len = linelen(ld);
4066 if (len<48) return NULL; /* All these "48"s should vary, eh? */ /* Hugeness? */
4067 if (!empty_left_side(l,ld,48)) return NULL;
4068 announce(VERBOSE,"lightbox");
4069 if (len>48)
4070 ld = centerpart(l,ld,NULL,48,ThisStyle,c);
4071 ldb = lefthand_box(l,ld,48,ThisStyle,c); /* This one too */
4072 ldb->right->middle_texture = ThisStyle->wall0;
4073 oldsec = ld->right->sector;
4074 newsec = ldb->right->sector;
4075 newsec->special = ThisStyle->auxspecial;
4076 newsec->floor_height += ThisStyle->auxheight;
4077 if (newsec->ceiling_height-newsec->floor_height<64)
4078 newsec->floor_height = newsec->ceiling_height-64;
4079 patch_lower(ld,ThisStyle->wall0,c);
4080 point_from(ld->from->x,ld->from->y,
4081 ld->to->x,ld->to->y,
4082 LEFT_TURN,24,&x,&y);
4083 point_from(ld->to->x,ld->to->y,x,y,LEFT_TURN,24,&x,&y);
4084 if (g->height>(newsec->ceiling_height-newsec->floor_height))
4085 g = ThisStyle->shortlamp0;
4086 new_thing(l,x,y,0,g->thingid,7,c);
4087 if (g->bits&EXPLODES) announce(VERBOSE,"Barrelbox");
4088 return ld;
4089 }
4090
4091 /* Make a nice bar with lights to the left of the linedef */
4092 /* Actually looks pretty terrible! Fix before using */
lightbar(level * l,linedef * ld,propertybits pb,style * ThisStyle,config * c)4093 void lightbar(level *l,linedef *ld,propertybits pb,style *ThisStyle,config *c)
4094 {
4095 int len, wid, dep;
4096 linedef *ldb,*lde1,*lde2;
4097 sector *oldsec, *newsec;
4098
4099 len = linelen(ld);
4100 if (len<16) return;
4101 wid = 12 + roll(64,len-17);
4102 dep = 8 + 4 * roll(65,5);
4103 if (!empty_left_side(l,ld,dep)) return;
4104 announce(VERBOSE,"lightbar");
4105 if (len>wid)
4106 ld = centerpart(l,ld,NULL,wid,ThisStyle,c);
4107 ldb = lefthand_box_ext(l,ld,dep,ThisStyle,c,&lde1,&lde2);
4108 ldb->right->middle_texture =
4109 lde1->right->middle_texture =
4110 lde2->right->middle_texture = texture_for_bits(pb,ThisStyle,c);
4111 {
4112 texture *t = lde1->right->middle_texture;
4113 if (t!=ThisStyle->wall0)
4114 if (!(t->props & LIGHT))
4115 announce(LOG,"Colorbar");
4116 }
4117 oldsec = ld->right->sector;
4118 newsec = ldb->right->sector;
4119 newsec->special = ThisStyle->auxspecial;
4120 if (oldsec->light_level<=l->lit_light_level)
4121 if (rollpercent(150,60))
4122 newsec->light_level = oldsec->light_level + 20;
4123 }
4124
4125 /* Return a <width>-long linedef which is the center of */
4126 /* the given linedef. In ld2, return the linedef that */
4127 /* is the far part of the triply-split line. */
centerpart(level * l,linedef * ld,linedef ** ld2,int width,style * ThisStyle,config * c)4128 linedef *centerpart(level *l,linedef *ld,linedef **ld2,int width,
4129 style *ThisStyle,config *c)
4130 {
4131 int len, border;
4132 linedef *answer, *answer2;
4133
4134 len = linelen(ld);
4135 border = (len - width) / 2;
4136 border += (len - (width + 2*border)); /* Fix roundoff errors */
4137 if (border<=0) {
4138 answer = ld;
4139 answer2 = ld;
4140 } else {
4141 answer = split_linedef(l,ld,border,c);
4142 answer2 = split_linedef(l,answer,width,c);
4143 }
4144 if (ld2) *ld2 = answer2;
4145
4146 return answer;
4147 }
4148
4149 /* Return a <width>-long linedef which is the center of */
4150 /* the given linedef. Optionally embellish the borders, */
4151 /* if called for in the style. */
borderize(level * l,linedef * ld,int width,boolean fancy,style * ThisStyle,propertybits pb,genus * keyg,boolean * painted_door,config * c)4152 linedef *borderize(level *l,linedef *ld,int width,boolean fancy,
4153 style *ThisStyle,propertybits pb,genus *keyg,
4154 boolean *painted_door,config *c)
4155 {
4156 linedef *answer, *ld2;
4157 sector *nearsec = ld->right->sector;
4158 sector *lsec;
4159 linedef *ldt;
4160 boolean try_keybox = TRUE;
4161
4162 answer = centerpart(l,ld,&ld2,width,ThisStyle,c);
4163
4164 /* Now optionally do fancy things to ld and ld2 */
4165 /* Gotta think of some other fancy things! */
4166 if (nearsec->ceiling_height - nearsec->floor_height < 88) try_keybox = FALSE;
4167 if (painted_door) *painted_door = FALSE;
4168 if (ld!=ld2)
4169 if (fancy)
4170 if (ThisStyle->lightboxes)
4171 if ((linelen(ld)>=64)) {
4172 short box_light_level = nearsec->light_level;
4173 short box_special = 0;
4174 genus *g = keyg;
4175 if (g==NULL) {
4176 if (rollpercent(151,l->p_barrels*2))
4177 g = random_barrel(c,ThisStyle);
4178 if (g==NULL) g = ThisStyle->lamp0;
4179 }
4180 if (g->bits&LIGHT) {
4181 if (ThisStyle->lightbox_lighting==LIGHTBOX_LIGHTED)
4182 if (box_light_level<l->lit_light_level)
4183 box_light_level = l->lit_light_level;
4184 if (ThisStyle->lightbox_lighting==LIGHTBOX_DARK)
4185 box_light_level = c->minlight;
4186 if (rollpercent(152,20)) box_special = RANDOM_BLINK; /* 20? */
4187 }
4188 ldt = lightbox(l,ld,g,ThisStyle,c);
4189 /* Maybe do the cool keybox thing! */
4190 if (ldt&&keyg&&try_keybox) {
4191 lsec = ldt->left->sector;
4192 lsec->floor_height = nearsec->floor_height + 72;
4193 lsec->ceiling_height = lsec->floor_height + 32;
4194 patch_upper(ldt,nearsec->style->wall0,c);
4195 patch_lower(ldt,nearsec->style->wall0,c);
4196 ldt->flags |= IMPASSIBLE;
4197 lsec->special = GLOW_BLINK;
4198 if (lsec->light_level<l->lit_light_level)
4199 lsec->light_level = l->lit_light_level;
4200 if (painted_door) *painted_door = TRUE;
4201 announce(LOG,"Keybox");
4202 } else if (ldt) {
4203 ldt->left->sector->light_level = box_light_level;
4204 ldt->left->sector->special = box_special;
4205 }
4206 ldt = lightbox(l,ld2,g,ThisStyle,c);
4207 if (ldt&&keyg&&try_keybox) {
4208 lsec = ldt->left->sector;
4209 lsec->floor_height = nearsec->floor_height + 72;
4210 lsec->ceiling_height = lsec->floor_height + 32;
4211 patch_upper(ldt,nearsec->style->wall0,c);
4212 patch_lower(ldt,nearsec->style->wall0,c);
4213 ldt->flags |= IMPASSIBLE;
4214 lsec->special = GLOW_BLINK;
4215 if (lsec->light_level<l->lit_light_level)
4216 lsec->light_level = l->lit_light_level;
4217 if (painted_door) *painted_door = TRUE;
4218 announce(LOG,"Keybox");
4219 } else if (ldt) {
4220 ldt->left->sector->light_level = box_light_level;
4221 ldt->left->sector->special = box_special;
4222 }
4223
4224 #ifdef LIGHTBAR_STUFF
4225 } else if (linelen(ld)>16) {
4226 /* These actually look very silly; don't use! */
4227 lightbar(l,ld,pb,ThisStyle,c);
4228 lightbar(l,ld2,pb,ThisStyle,c);
4229 #endif
4230 }
4231
4232 return answer;
4233 }
4234
4235 /* Try sticking a falling-core trap into the core bounded by the */
4236 /* two given linedefs. */
try_falling_core(level * l,linedef * ld1,linedef * ld2,haa * haa,config * c)4237 void try_falling_core(level *l,linedef *ld1,linedef *ld2,haa *haa,config *c)
4238 {
4239 int len, depth;
4240 boolean room1, room2;
4241 linedef *ldn1, *ldn2, *ldfar;
4242 sector *oldsec, *coresec;
4243 sector *downsec1 = NULL;
4244 sector *downsec2 = NULL;
4245 short downspec;
4246
4247 oldsec = ld1->right->sector;
4248 depth = l->hugeness * (1 + 16 * ( 4 + roll(66,6) ));
4249 len = linelen(ld1)-(16*l->hugeness);
4250 ld1 = split_linedef(l,ld1,8*l->hugeness,c);
4251 split_linedef(l,ld1,len,c);
4252 room1 = empty_left_side(l,ld1,depth);
4253 ld2 = split_linedef(l,ld2,8*l->hugeness,c);
4254 split_linedef(l,ld2,len,c);
4255 room2 = empty_left_side(l,ld2,depth);
4256 if (!(room1||room2)) return; /* No room! */
4257 switch (roll(67,6)) {
4258 case 0:
4259 case 1:
4260 case 2: downspec = 0; break;
4261 case 3: downspec = RANDOM_BLINK; break;
4262 case 4: downspec = SYNC_FAST_BLINK; break;
4263 case 5: downspec = SYNC_SLOW_BLINK; break;
4264 default: downspec = 0;
4265 }
4266 coresec = clone_sector(l,ld1->right->sector);
4267 coresec->tag = new_tag(l);
4268 ldn1 = new_linedef(l,ld2->to,ld1->from);
4269 ldn2 = new_linedef(l,ld1->to,ld2->from);
4270 ldn1->right = ldn2->right = new_sidedef(l,coresec,c);
4271 ldn1->left = ldn2->left = new_sidedef(l,oldsec,c);
4272 ldn1->left->middle_texture = c->null_texture;
4273 ldn1->right->middle_texture = c->null_texture;
4274 ldn1->right->lower_texture =
4275 ldn2->right->lower_texture = coresec->style->support0;
4276 ldn1->flags |= TWO_SIDED;
4277 ldn2->flags |= TWO_SIDED;
4278 ld1->right->sector = coresec;
4279 ld2->right->sector = coresec;
4280 if (room1) {
4281 ld1->right->upper_texture = ld1->right->middle_texture;
4282 ld1->right->y_offset = coresec->floor_height - coresec->ceiling_height;
4283 ldfar = lefthand_box_ext(l,ld1,depth,coresec->style,c,&ldn1,&ldn2);
4284 downsec1 = ld1->left->sector;
4285 ld1->right->middle_texture = c->null_texture;
4286 ld1->left->middle_texture = c->null_texture;
4287 ld1->flags |= TWO_SIDED | SECRET_LINEDEF;
4288 ldn1->tag = coresec->tag;
4289 ldn1->type = LINEDEF_SR_LOWER_LIFT;
4290 ldn2->tag = coresec->tag;
4291 ldn2->type = LINEDEF_SR_LOWER_LIFT;
4292 ldfar->right->middle_texture =
4293 ldn1->right->middle_texture =
4294 ldn2->right->middle_texture =
4295 ld1->left->lower_texture = coresec->style->support0;
4296 downsec1->floor_height = coresec->floor_height - 128;
4297 downsec1->ceiling_height = coresec->floor_height;
4298 downsec1->light_level = c->minlight + roll(68,40);
4299 downsec1->special = downspec;
4300 }
4301 if (room2) {
4302 ld2->right->upper_texture = ld2->right->middle_texture;
4303 ld2->right->y_offset = coresec->floor_height - coresec->ceiling_height;
4304 ldfar = lefthand_box_ext(l,ld2,depth,coresec->style,c,&ldn1,&ldn2);
4305 downsec2 = ld2->left->sector;
4306 ld2->right->middle_texture = c->null_texture;
4307 ld2->left->middle_texture = c->null_texture;
4308 ld2->flags |= TWO_SIDED | SECRET_LINEDEF;
4309 ldn1->tag = coresec->tag;
4310 ldn1->type = LINEDEF_SR_LOWER_LIFT;
4311 ldn2->tag = coresec->tag;
4312 ldn2->type = LINEDEF_SR_LOWER_LIFT;
4313 ldfar->right->middle_texture =
4314 ldn1->right->middle_texture =
4315 ldn2->right->middle_texture =
4316 ld2->left->lower_texture = coresec->style->support0;
4317 downsec2->floor_height = coresec->floor_height - 128;
4318 downsec2->ceiling_height = coresec->floor_height;
4319 downsec2->light_level = c->minlight + roll(69,40);
4320 downsec2->special = downspec;
4321 }
4322 /* Make the tripwire */
4323 split_linedef(l,ld1,len/2,c);
4324 if (room1) {
4325 downsec1->entry_x = ld1->to->x;
4326 downsec1->entry_y = ld1->to->y;
4327 }
4328 split_linedef(l,ld2,len/2,c);
4329 if (room2) {
4330 downsec2->entry_x = ld2->to->x;
4331 downsec2->entry_y = ld2->to->y;
4332 }
4333 ldn1 = new_linedef(l,ld1->to,ld2->to);
4334 ldn1->left = ldn1->right = new_sidedef(l,coresec,c);
4335 ldn1->left->middle_texture = c->null_texture;
4336 ldn1->flags |= TWO_SIDED;
4337 if (!(c->gamemask&DOOM0_BIT)) {
4338 ldn1->type = LINEDEF_WR_TURBO_LIFT;
4339 } else {
4340 ldn1->type = LINEDEF_WR_LOWER_LIFT;
4341 }
4342 ldn1->tag = coresec->tag;
4343
4344 /* Monsters and stuff (works?) */
4345 if (room1) {
4346 place_monsters(l,downsec1,c,haa);
4347 place_health(l,downsec1,c,haa);
4348 place_ammo(l,downsec1,c,haa);
4349 }
4350 if (room2) {
4351 place_monsters(l,downsec2,c,haa);
4352 place_health(l,downsec2,c,haa);
4353 place_ammo(l,downsec2,c,haa);
4354 }
4355
4356 /* and that's all */
4357 announce(VERBOSE,"Falling core");
4358
4359 }
4360
4361 /* Implement the given link between the given linedefs. */
4362 /* For OPEN and BASIC links, these are antiparallel. */
establish_link(level * l,linedef * ldf1,linedef * ldf2,link * ThisLink,quest * ThisQuest,style * ThisStyle,style * NewStyle,haa * haa,config * c)4363 void establish_link(level *l,linedef *ldf1,linedef *ldf2,link *ThisLink,
4364 quest *ThisQuest,style *ThisStyle,style *NewStyle,
4365 haa *haa,config *c)
4366 {
4367 switch (ThisLink->type) {
4368 case BASIC_LINK:
4369 establish_basic_link(l,ldf1,ldf2,ThisLink,ThisQuest,
4370 ThisStyle,NewStyle,haa,c);
4371 break;
4372 case OPEN_LINK:
4373 establish_open_link(l,ldf1,ldf2,ThisLink,ThisQuest,
4374 ThisStyle,NewStyle,haa,c);
4375 break;
4376 case GATE_LINK: {
4377 short tag1, tag2;
4378 tag1 = new_tag(l);
4379 tag2 = new_tag(l);
4380 ldf1->right->sector->gate = new_gate(l,tag1,tag2,0,FALSE,c);
4381 ldf2->right->sector->gate = new_gate(l,tag2,tag1,0,TRUE,c);
4382 if (ThisQuest) {
4383 if (rollpercent(153,50)) {
4384 ThisQuest->type = LINEDEF_S1_OPEN_DOOR;
4385 } else {
4386 ThisQuest->type = LINEDEF_S1_LOWER_FLOOR;
4387 }
4388 ThisQuest->tag = tag1;
4389 ldf1->right->sector->gate->gate_lock = ThisQuest->type;
4390 }
4391 break;
4392 }
4393 default:
4394 announce(ERROR,"Unknown linktype, sectors not linked.");
4395 return;
4396 }
4397 }
4398
4399 /* Implement the given link between the given (antiparallel) linedefs. */
4400 /* Decide which way is up-going, call the inner routine. */
establish_open_link(level * l,linedef * ldf1,linedef * ldf2,link * ThisLink,quest * ThisQuest,style * ThisStyle,style * NewStyle,haa * haa,config * c)4401 void establish_open_link(level *l,linedef *ldf1,linedef *ldf2,link *ThisLink,
4402 quest *ThisQuest,style *ThisStyle,style *NewStyle,
4403 haa *haa,config *c)
4404 {
4405 int newfloor;
4406 sector *nearsec, *farsec;
4407 boolean need_lock;
4408
4409 need_lock = (ThisQuest) && ( (ThisQuest->goal==SWITCH_GOAL) ||
4410 (ThisQuest->goal==GATE_GOAL) );
4411
4412 nearsec = ldf1->right->sector;
4413 farsec = ldf2->right->sector;
4414 /* Are these right? */
4415 farsec->entry_x = (ldf2->from->x + ldf2->to->x) / 2;
4416 farsec->entry_y = (ldf2->from->y + ldf2->to->y) / 2;
4417
4418 if (need_lock||rollpercent(154,65)) { /* Upward-going */
4419 /* Get recommendation */
4420 newfloor = nearsec->ceiling_height - ThisLink->height1;
4421 /* Rationalize it */
4422 if (newfloor-nearsec->floor_height<25)
4423 newfloor = nearsec->floor_height+25;
4424 if (newfloor-nearsec->floor_height>128)
4425 newfloor = nearsec->floor_height+128;
4426 /* Limit step steepness */
4427 if (ThisLink->bits&LINK_STEPS)
4428 if (newfloor-nearsec->floor_height>ThisLink->depth1)
4429 newfloor = nearsec->floor_height+ThisLink->depth1;
4430 /* OK, now set far sector, and do it */
4431 farsec->floor_height = newfloor;
4432 farsec->ceiling_height = farsec->floor_height + NewStyle->wallheight0;
4433 e_ol_inner(l,ldf1,ldf2,ThisLink,ThisQuest,ThisStyle,NewStyle,haa,c);
4434 } else {
4435 newfloor = nearsec->floor_height + ThisLink->height1
4436 - NewStyle->wallheight0;
4437 if (nearsec->floor_height-newfloor<25)
4438 newfloor = nearsec->floor_height-25;
4439 if (nearsec->floor_height-newfloor>128)
4440 newfloor = nearsec->floor_height-128;
4441 /* Limit step steepness */
4442 if (ThisLink->bits&LINK_STEPS)
4443 if (nearsec->floor_height-newfloor>ThisLink->depth1)
4444 newfloor = nearsec->floor_height-ThisLink->depth1;
4445 farsec->floor_height = newfloor;
4446 farsec->ceiling_height = farsec->floor_height + NewStyle->wallheight0;
4447 e_ol_inner(l,ldf2,ldf1,ThisLink,ThisQuest,NewStyle,ThisStyle,haa,c);
4448 }
4449
4450 }
4451
4452 /* Implement the given link between the given (antiparallel) linedefs, */
4453 /* always upward-going. */
e_ol_inner(level * l,linedef * ldf1,linedef * ldf2,link * ThisLink,quest * ThisQuest,style * ThisStyle,style * NewStyle,haa * haa,config * c)4454 void e_ol_inner(level *l,linedef *ldf1,linedef *ldf2,link *ThisLink,
4455 quest *ThisQuest,style *ThisStyle,style *NewStyle,
4456 haa *haa,config *c)
4457 {
4458 linedef *ldf1a, *ldf1b, *ldf2a, *ldf2b;
4459 linedef *lde1, *lde2, *ldes;
4460 sector *sideseca, *sidesecb, *midsec;
4461 sector *nearsec, *farsec;
4462 int midwidth, len, sidefloor, dieroll;
4463 boolean nukage = FALSE;
4464 boolean high_sides = FALSE;
4465 boolean sidesteps = FALSE;
4466
4467 if (ThisLink->bits&LINK_LIFT)
4468 announce(VERBOSE,"Open lift");
4469
4470 if ((!(ThisLink->bits&LINK_LIFT))&&
4471 (!(ThisLink->bits&LINK_STEPS)))
4472 announce(WARNING,"Non-lift non-stair open link; oops!");
4473
4474 if (rollpercent(155,l->p_force_nukage) || rollpercent(439,10)) nukage = TRUE;
4475 if ((ThisLink->bits&LINK_STEPS) && (ThisLink->bits&LINK_ALCOVE)) nukage=FALSE;
4476
4477 nearsec = ldf1->right->sector;
4478 farsec = ldf2->right->sector;
4479
4480 /* If a teleporter-goal, just make a simple air-connection. */
4481 /* Would(n't) this be more simply expressed as a BASIC link? */
4482 if (ThisQuest && (ThisQuest->goal==GATE_GOAL) && l->use_gates) {
4483 midsec = clone_sector(l,farsec);
4484 midsec->floor_height = nearsec->floor_height;
4485 midsec->floor_flat = nearsec->floor_flat;
4486 ldf1->right->middle_texture = c->null_texture;
4487 ldf1->left = new_sidedef(l,midsec,c);
4488 ldf1->left->middle_texture = c->null_texture;
4489 ldf1->left->upper_texture = farsec->style->wall0;
4490 ldf1->flags |= TWO_SIDED | UPPER_UNPEGGED;
4491 patch_upper(ldf1,ThisStyle->wall0,c);
4492 patch_lower(ldf1,ThisStyle->support0,c);
4493 ldf2->right->middle_texture = c->null_texture;
4494 ldf2->left = new_sidedef(l,midsec,c);
4495 ldf2->left->middle_texture = c->null_texture;
4496 ldf2->left->lower_texture = farsec->style->wall0;
4497 ldf2->flags |= TWO_SIDED;
4498 patch_upper(ldf2,NewStyle->wall0,c);
4499 patch_lower(ldf2,NewStyle->support0,c);
4500 lde1 = new_linedef(l,ldf1->from,ldf2->to);
4501 lde1->right = new_sidedef(l,midsec,c);
4502 lde1->right->middle_texture = farsec->style->wall0;
4503 lde2 = new_linedef(l,ldf2->from,ldf1->to);
4504 lde2->right = new_sidedef(l,midsec,c);
4505 lde2->right->middle_texture = farsec->style->wall0;
4506 if (nukage) {
4507 ldf1->left->lower_texture = ThisStyle->support0;
4508 midsec->floor_flat = ThisStyle->nukage1;
4509 midsec->special = NUKAGE1_SPECIAL;
4510 if (midsec->light_level<160) midsec->light_level = 160; /* visible */
4511 midsec->floor_height -= 8;
4512 }
4513 /* Now arrange for the gate and stuff */
4514 ThisQuest->tag = new_tag(l);
4515 if (rollpercent(156,50)) {
4516 ThisQuest->tag2 = 0; /* Can be one-way */
4517 } else {
4518 ThisQuest->tag2 = new_tag(l);
4519 ldf2->right->middle_texture = ThisStyle->grating;
4520 ldf2->left->middle_texture = ThisStyle->grating;
4521 ldf2->flags |= IMPASSIBLE | LOWER_UNPEGGED; /* Lower the grating, eh? */
4522 }
4523 farsec->gate = new_gate(l,ThisQuest->tag,ThisQuest->tag2,0,TRUE,c);
4524 announce(LOG,"OL Gate quest");
4525 return; /* and that's it */
4526 }
4527
4528 /* Otherwise it's (even) more complicated */
4529
4530 /* Figure how wide */
4531 len = linelen(ldf1);
4532 if (len<100) announce(WARNING,"Open link on a too-narrow linedef!");
4533 midwidth = ThisLink->width1;
4534 if (midwidth==0) midwidth = linelen(ldf1)/3;
4535 if (midwidth<64) midwidth = 64;
4536 if (((len-midwidth)/2)<33) midwidth = len - 66;
4537 if (midwidth<33) midwidth = 33;
4538
4539 /* Decide if doing the sideways-step thing */
4540 if ( (ThisLink->bits&LINK_STEPS) && (ThisLink->bits&LINK_ALCOVE) &&
4541 (midwidth>=(farsec->floor_height - nearsec->floor_height)) ) {
4542 sidesteps = TRUE;
4543 }
4544
4545 /* Decide about nukage and side heights and stuff */
4546 dieroll = roll(70,100);
4547 if (sidesteps) {
4548 sidefloor=nearsec->floor_height;
4549 } else if ((dieroll<50)||nukage) {
4550 sidefloor = nearsec->floor_height;
4551 } else if (dieroll<75) {
4552 nukage = FALSE;
4553 high_sides = TRUE;
4554 sidefloor = farsec->floor_height;
4555 if (sidefloor>nearsec->ceiling_height-57)
4556 sidefloor=nearsec->ceiling_height-57;
4557 } else {
4558 nukage = FALSE;
4559 sidefloor = farsec->floor_height;
4560 if (sidefloor>nearsec->ceiling_height-57)
4561 sidefloor=nearsec->ceiling_height-57;
4562 if (farsec->floor_height-nearsec->floor_height>48) {
4563 sidefloor = nearsec->floor_height +
4564 roll(71,1+sidefloor-nearsec->floor_height);
4565 }
4566 }
4567
4568 ldf1->flags |= TWO_SIDED;
4569 ldf1->right->middle_texture = c->null_texture;
4570 ldf2->flags |= TWO_SIDED;
4571 ldf2->right->middle_texture = c->null_texture;
4572
4573 ldf1a = ldf1;
4574 ldf1 = split_linedef(l,ldf1,(len-midwidth)/2,c);
4575 ldf1b = split_linedef(l,ldf1,midwidth,c);
4576 ldf2b = ldf2;
4577 ldf2 = split_linedef(l,ldf2,(len-midwidth)/2,c);
4578 ldf2a = split_linedef(l,ldf2,midwidth,c);
4579
4580 midsec = clone_sector(l,farsec);
4581 if (ThisLink->bits&LINK_LIFT) {
4582 midsec->tag = new_tag(l);
4583 ldf1->type = NewStyle->slifttype;
4584 ldf1->tag = midsec->tag;
4585 }
4586
4587 ldf1->left = new_sidedef(l,midsec,c);
4588 ldf1->left->middle_texture = c->null_texture;
4589 if (nukage&&(ThisLink->bits&LINK_LIFT))
4590 ldf1->left->lower_texture = ThisStyle->support0;
4591 ldf2->left = new_sidedef(l,midsec,c);
4592 ldf2->left->middle_texture = c->null_texture;
4593 if (ThisLink->bits&LINK_LIFT) {
4594 ldf2->left->lower_texture = NewStyle->support0;
4595 ldf2->flags |= LOWER_UNPEGGED;
4596 }
4597 patch_upper(ldf1,ThisStyle->wall0,c);
4598 patch_lower(ldf1,ThisStyle->support0,c);
4599 if ((ThisLink->bits&LINK_LIFT) &&
4600 (ThisStyle->liftface) &&
4601 (farsec->floor_height - nearsec->floor_height <=
4602 ThisStyle->liftface->height) &&
4603 (midwidth==ThisStyle->liftface->width) ) {
4604 ldf1->right->lower_texture = ThisStyle->liftface;
4605 ldf1->right->x_offset = 0;
4606 announce(VERBOSE,"Lift texture");
4607 }
4608 ldf1->flags &= ~LOWER_UNPEGGED;
4609 patch_upper(ldf2,NewStyle->wall0,c);
4610 patch_lower(ldf2,NewStyle->wall0,c);
4611
4612 flip_linedef(ldf2a);
4613 sideseca = make_box_ext(l,ldf1a,ldf2a,ThisStyle,c,&lde1,&lde2);
4614 flip_linedef(ldf2a);
4615 sideseca->floor_height = sidefloor;
4616 sideseca->ceiling_height = midsec->ceiling_height;
4617 sideseca->ceiling_flat = midsec->ceiling_flat;
4618 lde1->right->middle_texture = NewStyle->wall0;
4619 lde2->left = new_sidedef(l,midsec,c);
4620 lde2->flags |= TWO_SIDED;
4621 lde2->left->middle_texture = c->null_texture;
4622 lde2->left->lower_texture = NewStyle->support0;
4623 lde2->right->lower_texture = NewStyle->wall0;
4624 lde2->right->middle_texture = c->null_texture;
4625 lde2->right->y_offset = farsec->ceiling_height - farsec->floor_height;
4626 ldes = lde2; /* Save for stairification */
4627 patch_upper(ldf1a,ThisStyle->wall0,c);
4628 patch_lower(ldf1a,ThisStyle->wall0,c);
4629 patch_upper(ldf2a,NewStyle->wall0,c);
4630 patch_lower(ldf2a,NewStyle->wall0,c);
4631 if (nukage) {
4632 announce(VERBOSE,"Open nukage");
4633 sideseca->floor_height -= 8;
4634 sideseca->floor_flat = ThisStyle->nukage1;
4635 sideseca->special = NUKAGE1_SPECIAL;
4636 patch_lower(ldf1a,ThisStyle->support0,c);
4637 patch_lower(ldf2a,ThisStyle->support0,c);
4638 nearsec->marked = farsec->marked = TRUE;
4639 if (c->gunk_channels && empty_left_side(l,lde1,32)) {
4640 lefthand_box(l,lde1,32,ThisStyle,c)->right->middle_texture =
4641 ThisStyle->support0;
4642 lde1->left->sector->ceiling_height =
4643 lde1->left->sector->floor_height + 8;
4644 lde1->left->sector->light_level =
4645 lde1->right->sector->light_level - 20;
4646 lde1->left->sector->floor_flat = ThisStyle->nukage1;
4647 patch_upper(lde1,NewStyle->wall0,c);
4648 announce(VERBOSE,"Channel");
4649 }
4650 nearsec->marked = farsec->marked = FALSE;
4651 }
4652
4653 flip_linedef(ldf2b);
4654 sidesecb = make_box_ext(l,ldf1b,ldf2b,ThisStyle,c,&lde1,&lde2);
4655 flip_linedef(ldf2b);
4656 sidesecb->floor_height = sidefloor;
4657 sidesecb->ceiling_height = midsec->ceiling_height;
4658 sidesecb->ceiling_flat = midsec->ceiling_flat;
4659 lde2->right->middle_texture = NewStyle->wall0;
4660 lde1->left = new_sidedef(l,midsec,c);
4661 lde1->flags |= TWO_SIDED;
4662 lde1->left->middle_texture = c->null_texture;
4663 lde1->left->lower_texture = NewStyle->support0;
4664 lde1->right->lower_texture = NewStyle->wall0;
4665 lde1->right->middle_texture = c->null_texture;
4666 lde1->right->y_offset = farsec->ceiling_height - farsec->floor_height;
4667 patch_upper(ldf1b,ThisStyle->wall0,c);
4668 patch_lower(ldf1b,ThisStyle->wall0,c);
4669 patch_upper(ldf2b,NewStyle->wall0,c);
4670 patch_lower(ldf2b,NewStyle->wall0,c);
4671 if (nukage) {
4672 sidesecb->floor_height -= 8;
4673 sidesecb->floor_flat = ThisStyle->nukage1;
4674 sidesecb->special = NUKAGE1_SPECIAL;
4675 patch_lower(ldf1b,ThisStyle->support0,c);
4676 patch_lower(ldf2b,ThisStyle->support0,c);
4677 nearsec->marked = farsec->marked = TRUE;
4678 if (c->gunk_channels && empty_left_side(l,lde2,32)) {
4679 lefthand_box(l,lde2,32,ThisStyle,c)->right->middle_texture =
4680 ThisStyle->support0;
4681 lde2->left->sector->ceiling_height =
4682 lde2->left->sector->floor_height + 8;
4683 lde2->left->sector->light_level =
4684 lde2->right->sector->light_level - 20;
4685 lde2->left->sector->floor_flat = ThisStyle->nukage1;
4686 patch_upper(lde2,NewStyle->wall0,c);
4687 announce(VERBOSE,"Channel");
4688 }
4689 nearsec->marked = farsec->marked = FALSE;
4690 }
4691
4692 /* Could be more interesting... */
4693 midsec->light_level = sideseca->light_level = sidesecb->light_level =
4694 ThisStyle->roomlight0;
4695
4696 /* Make center into stairs if we need them */
4697 if ((ThisLink->bits&LINK_STEPS)&&!sidesteps) {
4698 announce(VERBOSE,"Open stairs");
4699 /* Maybe stick on some lights */
4700 if (rollpercent(157,50)) { /* 50 should vary? */
4701 genus *g = ThisStyle->lamp0;
4702 if (g->height>(sideseca->ceiling_height-sideseca->floor_height))
4703 g = ThisStyle->shortlamp0;
4704 if ( (high_sides&&((len-midwidth)/2 >= 2 * g->width)) ||
4705 ((len-midwidth)/2 >= g->width + 69) ) {
4706 announce(VERBOSE,"and lights");
4707 new_thing(l,
4708 (ldf1a->to->x + ldf1a->from->x + ldf2a->to->x + ldf2a->from->x)/4,
4709 (ldf1a->to->y + ldf1a->from->y + ldf2a->to->y + ldf2a->from->y)/4,
4710 0,g->thingid,7,c);
4711 new_thing(l,
4712 (ldf1b->to->x + ldf1b->from->x + ldf2b->to->x + ldf2b->from->x)/4,
4713 (ldf1b->to->y + ldf1b->from->y + ldf2b->to->y + ldf2b->from->y)/4,
4714 0,g->thingid,7,c);
4715 if (rollpercent(158,70)) { /* Should be in link/style? */
4716 if (sideseca->light_level<=l->bright_light_level)
4717 sideseca->light_level += 20; /* Lamps light things up */
4718 if (sidesecb->light_level<=l->bright_light_level)
4719 sidesecb->light_level += 20;
4720 if (midsec->light_level>c->minlight)
4721 midsec->light_level -= 20; /* and throw shadows! */
4722 }
4723 }
4724 }
4725 /* Change a few details */
4726 lde1->right->y_offset = 0;
4727 lde1->left->lower_texture = NewStyle->wall0;
4728 lde1->flags |= LOWER_UNPEGGED;
4729 ldes->right->y_offset = 0;
4730 ldes->left->lower_texture = NewStyle->wall0;
4731 ldes->flags |= LOWER_UNPEGGED;
4732 if (ThisStyle->light_steps && ThisStyle->walllight) {
4733 ldf1->right->lower_texture = ThisStyle->walllight;
4734 } else {
4735 ldf1->right->lower_texture = ThisStyle->kickplate;
4736 }
4737 ldf2->left->lower_texture = NewStyle->wall0; /* In case of lock */
4738 /* Make the center into stairs */
4739 stairify(l,ldf1,ldf2,ldes,lde1,nearsec->floor_height,farsec->floor_height,
4740 ThisQuest,ThisStyle,c);
4741 }
4742
4743 /* Or make the center into *sideways* stairs */
4744 if ((ThisLink->bits&LINK_STEPS)&&sidesteps) {
4745 announce(NONE,"Open side-stairs");
4746 ldf1->right->lower_texture = ThisStyle->wall0;
4747 ldf2->left->lower_texture = NewStyle->wall0;
4748 ldf1->right->y_offset = 0;
4749 ldf1->left->lower_texture = NewStyle->wall0;
4750 ldf1->flags |= LOWER_UNPEGGED;
4751 if (ThisLink->bits&LINK_LEFT) {
4752 if (ThisStyle->light_steps && ThisStyle->walllight) {
4753 ldes->right->lower_texture = ThisStyle->walllight;
4754 } else {
4755 ldes->right->lower_texture = ThisStyle->kickplate;
4756 }
4757 ldes->right->y_offset = 0;
4758 lde1->left->lower_texture = NewStyle->wall0; /* In case of lock? */
4759 sidesecb->floor_height = farsec->floor_height;
4760 sidesecb->floor_flat = farsec->floor_flat;
4761 } else {
4762 if (ThisStyle->light_steps && ThisStyle->walllight) {
4763 lde1->right->lower_texture = ThisStyle->walllight;
4764 } else {
4765 lde1->right->lower_texture = ThisStyle->kickplate;
4766 }
4767 lde1->right->y_offset = 0;
4768 ldes->left->lower_texture = NewStyle->wall0; /* In case of lock? */
4769 sideseca->floor_height = farsec->floor_height;
4770 sideseca->floor_flat = farsec->floor_flat;
4771 }
4772 ldf2->right->y_offset = 0;
4773 ldf2->left->lower_texture = NewStyle->wall0;
4774 ldf2->flags |= LOWER_UNPEGGED;
4775 patch_lower(ldf1a,ThisStyle->wall0,c);
4776 patch_lower(ldf1b,ThisStyle->wall0,c);
4777 if (ThisLink->bits&LINK_LEFT) {
4778 stairify(l,ldes,lde1,ldf2,ldf1,nearsec->floor_height,farsec->floor_height,
4779 ThisQuest,ThisStyle,c);
4780 } else {
4781 stairify(l,lde1,ldes,ldf1,ldf2,nearsec->floor_height,farsec->floor_height,
4782 ThisQuest,ThisStyle,c);
4783 }
4784 }
4785
4786 /* Bells and whistles! */
4787 if ((farsec->floor_height-sideseca->floor_height==128) &&
4788 (linelen(ldf2a)>=128)) {
4789 if (linelen(ldf2a)>128) {
4790 ldf2a = centerpart(l,ldf2a,NULL,128,ThisStyle,c);
4791 ldf2b = centerpart(l,ldf2b,NULL,128,ThisStyle,c);
4792 }
4793 ldf2a->left->lower_texture = ThisStyle->plaque;
4794 ldf2a->left->x_offset = 0;
4795 ldf2a->left->y_offset = 0;
4796 ldf2a->flags &= ~LOWER_UNPEGGED;
4797 ldf2b->left->lower_texture = ThisStyle->plaque;
4798 ldf2b->left->x_offset = 0;
4799 ldf2b->left->y_offset = 0;
4800 ldf2b->flags &= ~LOWER_UNPEGGED;
4801 announce(VERBOSE,"Open-link plaques");
4802 }
4803
4804 /* and so on */
4805
4806 }
4807
4808 /* Implement the given link between the given (antiparallel) linedefs. */
4809 /* Set bits for any ephemera, then call inner recursive routine. */
establish_basic_link(level * l,linedef * ldf1,linedef * ldf2,link * ThisLink,quest * ThisQuest,style * ThisStyle,style * NewStyle,haa * haa,config * c)4810 void establish_basic_link(level *l,linedef *ldf1,linedef *ldf2,link *ThisLink,
4811 quest *ThisQuest,style *ThisStyle,style *NewStyle,
4812 haa *haa,config *c)
4813 {
4814 ThisStyle->lightboxes = rollpercent(159,5); /* Should be from style, or? */
4815 e_bl_inner(l,ldf1,ldf2,ThisLink,ThisQuest,ThisStyle,NewStyle,0,haa,c);
4816 ThisStyle->lightboxes = FALSE; /* Just to be neat */
4817 }
4818
4819 /* Implement the given link between the given (antiparallel) linedefs */
4820 /* Potentially recursive, for windows and twinnings. */
4821 /* Needs lots of cleaning up and organizing and splitting into */
4822 /* smaller functions! */
e_bl_inner(level * l,linedef * ldf1,linedef * ldf2,link * ThisLink,quest * ThisQuest,style * ThisStyle,style * NewStyle,short flipstate,haa * haa,config * c)4823 void e_bl_inner(level *l,linedef *ldf1,linedef *ldf2,link *ThisLink,
4824 quest *ThisQuest,style *ThisStyle,style *NewStyle,
4825 short flipstate, haa *haa,config *c)
4826 {
4827 linedef *ldnew1, *ldnew2;
4828 int len, border, maxtop;
4829 texture *t1, *t2;
4830 sector *nearsec, *farsec, *newsec = NULL;
4831 boolean need_to_doorify = FALSE;
4832 linedef *ldflip1a = NULL;
4833 linedef *ldflip1b = NULL;
4834 linedef *ldflip2a = NULL;
4835 linedef *ldflip2b = NULL;
4836 sector *sflip1 = NULL;
4837 sector *sflip2 = NULL;
4838 linedef *ldedge1, *ldedge2;
4839 int tag1 = 0;
4840 boolean trigger_lift = FALSE;
4841 boolean trigger_door = FALSE;
4842 boolean painted_door = FALSE;
4843 int mminx, mminy, mmaxx, mmaxy, mangle;
4844 propertybits effective_left = ThisLink->bits&LINK_LEFT;
4845 propertybits litecol = LIGHT;
4846
4847 if ((ThisLink->bits&LINK_CORE)&&(ThisLink->bits&LINK_ANY_DOOR))
4848 announce(VERBOSE,"Core and door(s)");
4849
4850 if ((ThisQuest) && (ThisQuest->goal==KEY_GOAL)) {
4851 switch (ThisQuest->type) {
4852 case ID_BLUEKEY:
4853 case ID_BLUECARD: litecol = BLUE; break;
4854 case ID_REDKEY:
4855 case ID_REDCARD: litecol = RED; break;
4856 case ID_YELLOWKEY:
4857 case ID_YELLOWCARD: litecol = YELLOW; break;
4858 }
4859 } else {
4860 litecol = LIGHT;
4861 }
4862
4863 /* The type of a SWITCH_GOAL isn't set until the link's established */
4864 if (ThisQuest)
4865 if (ThisQuest->goal==SWITCH_GOAL)
4866 if (ThisLink->bits&LINK_LOCK_CORE) {
4867 ThisQuest->type = LINEDEF_S1_RAISE_AND_CLEAN_FLOOR;
4868 } else {
4869 ThisQuest->type = LINEDEF_S1_OPEN_DOOR;
4870 }
4871
4872 dump_link(ldf1,ldf2,ThisLink,"Establishing");
4873
4874 if ((ThisLink->bits&LINK_ALCOVE) &&
4875 (ThisLink->bits&LINK_TWIN) &&
4876 (ThisLink->bits&LINK_ANY_DOOR) ) announce(VERBOSE,"Twin door alcoves!");
4877
4878 nearsec = ldf1->right->sector;
4879 farsec = ldf2->right->sector;
4880
4881 /* Figure floor and ceiling heights for new sector */
4882 farsec->floor_height = nearsec->floor_height + ThisLink->floordelta;
4883 farsec->ceiling_height = farsec->floor_height + NewStyle->wallheight0;
4884
4885 /* Make sure we don't overdo the bar thing and crash the engine... */
4886 if (l->barcount>LEVEL_MAX_BARS) ThisLink->bits &= ~LINK_BARS;
4887
4888 /* See if we need to force the floordelta toward zero to avoid */
4889 /* impassable doorways. */
4890 if (ThisLink->bits&LINK_STEPS) {
4891 int need;
4892 /* The clearance we need is 56 plus the step height times */
4893 /* the number of steps our 64ish-wide shadow is on at once */
4894 need = 64 + (1+(64/(ThisLink->depth3/(ThisLink->stepcount))))
4895 * abs(ThisLink->floordelta / (ThisLink->stepcount-1));
4896 if (ThisLink->bits&LINK_ANY_DOOR) need += 8; /* Doors don't open all the way */
4897 if ( (farsec->ceiling_height-farsec->floor_height<need) ||
4898 (nearsec->ceiling_height-nearsec->floor_height<need) ) {
4899 /* There's probably something less drastic we can do here... */
4900 ThisLink->floordelta = 0;
4901 ThisLink->bits &= ~LINK_STEPS;
4902 farsec->floor_height = nearsec->floor_height + ThisLink->floordelta;
4903 farsec->ceiling_height = farsec->floor_height + NewStyle->wallheight0;
4904 }
4905 }
4906
4907 /* If we need to twin, split and recurse, or do the window thing */
4908 if ((flipstate==0)&&(ThisLink->bits&LINK_TWIN)) {
4909 len = linelen(ldf1)/2;
4910 ldnew1 = split_linedef(l,ldf1,len,c);
4911 ldnew2 = split_linedef(l,ldf2,len,c);
4912 if (!(ThisLink->bits&LINK_WINDOW)) { /* make twin links */
4913 e_bl_inner(l,ldf1,ldnew2,ThisLink,ThisQuest,ThisStyle,NewStyle,
4914 1,haa,c); /* Lefthand one */
4915 e_bl_inner(l,ldnew1,ldf2,ThisLink,ThisQuest,ThisStyle,NewStyle,
4916 2,haa,c); /* Righthand one */
4917 } else { /* Make a window */
4918 if (rollpercent(160,50)) { /* left or right */
4919 e_bl_inner(l,ldf1,ldnew2,ThisLink,ThisQuest,ThisStyle,NewStyle,3,haa,c);
4920 make_window(l,ldnew1,ldf2,ThisLink,ThisStyle,NewStyle,c);
4921 } else {
4922 /* Note: always establish the link before making the window! */
4923 e_bl_inner(l,ldnew1,ldf2,ThisLink,ThisQuest,ThisStyle,NewStyle,3,haa,c);
4924 make_window(l,ldf1,ldnew2,ThisLink,ThisStyle,NewStyle,c);
4925 }
4926 } /* end else a window */
4927 return;
4928 }
4929
4930 /* If this isn't supposed to be passable at all, just winowify. */
4931 /* Another fun thing would be to make the door, but bar it. */
4932 if (ThisQuest && (ThisQuest->goal == GATE_GOAL) && l->use_gates) {
4933 make_window(l,ldf1,ldf2,ThisLink,ThisStyle,NewStyle,c);
4934 /* Now arrange for the gate and stuff */
4935 ThisQuest->tag = new_tag(l);
4936 ThisQuest->tag2 = new_tag(l);
4937 farsec->gate = new_gate(l,ThisQuest->tag,ThisQuest->tag2,0,TRUE,c);
4938 announce(LOG,"BL Gate quest");
4939 return; /* and that's it */
4940 }
4941
4942 /* Figure out maxtop, for MAX_CEILING */
4943 maxtop = nearsec->floor_height + ThisLink->height1;
4944 if (ThisLink->floordelta>0) maxtop += ThisLink->floordelta;
4945
4946 /* If not the whole wall, center it, or do alcove things, or etc */
4947 len = linelen(ldf1); /* Really assumes lens are == */
4948 if (ThisLink->width1>len) {
4949 announce(WARNING,"Link-width > linedef size! Reducing...");
4950 ThisLink->width1 = len;
4951 }
4952 if ((ThisLink->width1!=0)&&(ThisLink->width1<len)) {
4953 if (ThisLink->bits&LINK_ALCOVE) {
4954 border = (len - (ThisLink->width1*2 + ThisLink->depth3))/2;
4955 if (border<0) { /* This should never happen */
4956 announce(WARNING,"A-link width too big! Reducing...");
4957 ThisLink->width1 = (len-ThisLink->depth3)/2;
4958 border = 0;
4959 /* May not actually help enough... */
4960 /* But can't dealcove; too complicated! */
4961 }
4962 if ((border!=0)&&(flipstate==2)&&rollpercent(161,50)) {
4963 effective_left ^= LINK_LEFT;
4964 announce(VERBOSE,"Flipping twinned alcove");
4965 }
4966 if (effective_left) {
4967 if (border!=0) ldf1 = split_linedef(l,ldf1,border,c);
4968 split_linedef(l,ldf1,ThisLink->width1,c);
4969 if (border!=0) ldf2 = split_linedef(l,ldf2,border,c);
4970 split_linedef(l,ldf2,ThisLink->width1,c);
4971 } else {
4972 ldf1 = split_linedef(l,ldf1,(len-(border+ThisLink->width1)),c);
4973 if (border!=0) split_linedef(l,ldf1,ThisLink->width1,c);
4974 ldf2 = split_linedef(l,ldf2,(len-(border+ThisLink->width1)),c);
4975 if (border!=0) split_linedef(l,ldf2,ThisLink->width1,c);
4976 }
4977 } else if ( (flipstate==1) && (ThisLink->bits&LINK_FAR_TWINS) ) {
4978 split_linedef(l,ldf1,ThisLink->width1,c);
4979 ldf2 = split_linedef(l,ldf2,len-ThisLink->width1,c);
4980 announce(NONE,"Far twins");
4981 } else if ( (flipstate==2) && (ThisLink->bits&LINK_FAR_TWINS) ) {
4982 split_linedef(l,ldf2,ThisLink->width1,c);
4983 ldf1 = split_linedef(l,ldf1,len-ThisLink->width1,c);
4984 } else { /* No alcove or farness; simple centered borders */
4985 if (ThisQuest && ThisQuest->goal==KEY_GOAL && l->skullkeys) {
4986 ldf1 = borderize(l,ldf1,ThisLink->width1,TRUE,ThisStyle,
4987 litecol,find_genus(c,ThisQuest->type),&painted_door,c);
4988 } else {
4989 ldf1 = borderize(l,ldf1,ThisLink->width1,TRUE,ThisStyle,
4990 litecol,NULL,NULL,c);
4991 }
4992 /* Embellish only near side of the link linedef? */
4993 ldf2 = borderize(l,ldf2,ThisLink->width1,FALSE,NewStyle,
4994 litecol,NULL,NULL,c);
4995 } /* end else no-alcove case */
4996 } else {
4997 if (ThisLink->bits&LINK_ALCOVE)
4998 announce(WARNING,"ALCOVE with width zero, or width == linelen");
4999 /* yikes! */
5000 }
5001
5002 /* Now we know where the player will be entering the far room, */
5003 /* more or less. Record that for the far sector. */
5004 farsec->entry_x = (ldf2->from->x + ldf2->to->x) / 2;
5005 farsec->entry_y = (ldf2->from->y + ldf2->to->y) / 2;
5006
5007 /* Get any tags we need for triggered links, and put them on */
5008 /* the linedefs we're currently working on. */
5009 /* Trigger alcoved lifts. */
5010 if ((ThisLink->bits&LINK_LIFT)&&
5011 (ThisLink->bits&LINK_ALCOVE)&&
5012 (!(ThisLink->bits&LINK_ANY_DOOR))&&
5013 (ThisLink->bits&LINK_TRIGGERED)) {
5014 trigger_lift = TRUE;
5015 announce(VERBOSE,"Walking lift");
5016 tag1 = new_tag(l);
5017 if (ThisLink->floordelta>0) {
5018 ldf1->tag = tag1;
5019 ldf1->type = LINEDEF_WR_LOWER_LIFT;
5020 } else {
5021 ldf2->tag = tag1;
5022 ldf2->type = LINEDEF_WR_LOWER_LIFT;
5023 }
5024 }
5025 /* Trigger deeply-recessed liftless doors also */
5026 if (((ThisQuest==NULL)||
5027 ((ThisQuest->goal!=SWITCH_GOAL)&&(ThisQuest->goal!=KEY_GOAL)))&&
5028 (ThisLink->bits&LINK_ANY_DOOR)&&
5029 (!(ThisLink->bits&LINK_BARS))&&
5030 (ThisLink->bits&LINK_RECESS)&&
5031 (ThisLink->depth2>16)&&
5032 (ThisLink->bits&LINK_TRIGGERED)) {
5033 trigger_door = TRUE;
5034 tag1 = new_tag(l);
5035 /* Don't always need both of these, but couldn't hurt... */
5036 ldf1->tag = tag1;
5037 ldf1->type = LINEDEF_WR_OC_DOOR; /* Or WR_OPEN_DOOR? */
5038 ldf2->tag = tag1;
5039 ldf2->type = LINEDEF_WR_OC_DOOR; /* Or WR_OPEN_DOOR? */
5040 }
5041 /* and deeply-recessed lifts */
5042 if (((ThisQuest==NULL)||
5043 ((ThisQuest->goal!=SWITCH_GOAL)&&(ThisQuest->goal!=KEY_GOAL)))&&
5044 (ThisLink->bits&LINK_LIFT)&&
5045 (ThisLink->bits&LINK_RECESS)&&
5046 (!(ThisLink->bits&LINK_ALCOVE))&&
5047 (!(ThisLink->bits&LINK_ANY_DOOR))&&
5048 (ThisLink->depth2>16)&&
5049 (ThisLink->bits&LINK_TRIGGERED)) {
5050 trigger_lift = TRUE;
5051 tag1 = new_tag(l);
5052 if (ThisLink->floordelta>0) {
5053 ldf1->tag = tag1;
5054 ldf1->type = LINEDEF_WR_LOWER_LIFT;
5055 } else {
5056 ldf2->tag = tag1;
5057 ldf2->type = LINEDEF_WR_LOWER_LIFT;
5058 }
5059 }
5060
5061 /* Remember these for later */
5062 t1 = ldf1->right->middle_texture;
5063 t2 = NewStyle->wall0;
5064 len = linelen(ldf1); /* Really assumes lens are == */
5065
5066 /* If recessed, make recess sectors and stuff */
5067 if (ThisLink->bits&LINK_RECESS) {
5068 ldnew1 = lefthand_box_ext(l,ldf1,ThisLink->depth2,ThisStyle,c,&ldedge1,&ldedge2);
5069 /* The near recess copies the near room */
5070 ldnew1->right->sector->floor_height =
5071 ldf1->right->sector->floor_height;
5072 if (ThisLink->bits&LINK_MAX_CEILING) {
5073 ldnew1->right->sector->ceiling_height = maxtop;
5074 } else {
5075 ldnew1->right->sector->ceiling_height =
5076 ldf1->right->sector->floor_height + ThisLink->height1;
5077 }
5078 if (nearsec->style->ceilinglight) if (c->clights) { /* Too often? */
5079 ldnew1->right->sector->ceiling_flat = nearsec->style->ceilinglight;
5080 announce(VERBOSE,"rcl");
5081 }
5082 /* Paint key-color, or adjust y-offsets, for recess edges */
5083 if ((ThisQuest) && (ThisStyle->paint_recesses) && (!painted_door) &&
5084 (ThisQuest->goal==KEY_GOAL) &&
5085 (ThisLink->depth2>=
5086 (texture_for_key(ThisQuest->type,ThisStyle,c)->width)) ) {
5087 ldedge1->right->middle_texture =
5088 ldedge2->right->middle_texture =
5089 texture_for_key(ThisQuest->type,ThisStyle,c);
5090 if (l->scrolling_keylights) {
5091 ldedge1->type = LINEDEF_SCROLL;
5092 ldedge2->type = LINEDEF_SCROLL;
5093 }
5094 /* Make sure the paint is visible! */
5095 if (ldedge1->right->sector->light_level<l->lit_light_level)
5096 ldedge1->right->sector->light_level=l->lit_light_level;
5097 announce(VERBOSE,"painted recess");
5098 if (rollpercent(162,75)) {
5099 painted_door = TRUE;
5100 } else {
5101 announce(VERBOSE,"Extra-painted recess"); /* Paint the door, too */
5102 ldedge1->flags |= LOWER_UNPEGGED; /* and make it all line up */
5103 ldedge2->flags |= LOWER_UNPEGGED;
5104 }
5105 } else if ((ThisLink->bits&LINK_NEAR_DOOR)
5106 && (ThisStyle->light_recesses)
5107 && (ThisStyle->walllight!=NULL)) {
5108 announce(VERBOSE,"Lit recess");
5109 ldedge1->right->middle_texture =
5110 ldedge2->right->middle_texture =
5111 ThisStyle->walllight;
5112 make_lighted(l,ldedge1->right->sector,c);
5113 } else {
5114 ldedge2->right->y_offset = ldedge1->right->y_offset =
5115 nearsec->ceiling_height - ldnew1->right->sector->ceiling_height;
5116 }
5117 patch_upper(ldf1,t1,c);
5118 /* and the far the far */
5119 ldnew2 = lefthand_box_ext(l,ldf2,ThisLink->depth2,NewStyle,c,&ldedge1,&ldedge2);
5120 ldnew2->right->sector->floor_height = farsec->floor_height;
5121 if (ThisLink->bits&LINK_MAX_CEILING) {
5122 ldnew2->right->sector->ceiling_height = maxtop;
5123 } else {
5124 ldnew2->right->sector->ceiling_height = farsec->floor_height +
5125 ThisLink->height1;
5126 }
5127 ldnew2->right->sector->light_level = NewStyle->doorlight0;
5128 if (farsec->style->ceilinglight) if (c->clights) { /* Too often? */
5129 ldnew2->right->sector->ceiling_flat = farsec->style->ceilinglight;
5130 announce(VERBOSE,"rcl");
5131 }
5132 if ((ThisLink->bits&LINK_FAR_DOOR)
5133 && (NewStyle->light_recesses)
5134 && (NewStyle->walllight!=NULL)) {
5135 announce(VERBOSE,"Lit recess");
5136 ldedge1->right->middle_texture =
5137 ldedge2->right->middle_texture =
5138 NewStyle->walllight;
5139 make_lighted(l,ldedge1->right->sector,c);
5140 } else {
5141 ldedge2->right->y_offset = ldedge1->right->y_offset =
5142 farsec->ceiling_height - ldnew2->right->sector->ceiling_height;
5143 }
5144 patch_upper(ldf2,t2,c);
5145 /* Now we're working inside the recesses */
5146 ldf1 = ldnew1;
5147 ldf2 = ldnew2;
5148 }
5149
5150 /* If no core or alcoves, make the one arch/door sector */
5151 if (!(ThisLink->bits&(LINK_CORE|LINK_ALCOVE))) {
5152 flip_linedef(ldf2);
5153 newsec = make_box_ext(l,ldf1,ldf2,ThisStyle,c,&ldnew1,&ldnew2);
5154 flip_linedef(ldf2);
5155 ldnew2->right->y_offset = ldnew1->right->y_offset =
5156 (nearsec->ceiling_height - nearsec->floor_height) - ThisLink->height1;
5157 if ((ThisLink->bits&(LINK_ANY_DOOR)) || c->doorless_jambs) {
5158 ldnew1->right->middle_texture = ThisStyle->doorjamb;
5159 ldnew2->right->middle_texture = ThisStyle->doorjamb;
5160 }
5161
5162 newsec->floor_height = nearsec->floor_height;
5163 if (ThisLink->bits&LINK_MAX_CEILING) {
5164 newsec->ceiling_height = maxtop;
5165 } else {
5166 #ifdef STRANGE_THINGS
5167 newsec->ceiling_height = nearsec->ceiling_height;
5168 #else
5169 newsec->ceiling_height = newsec->floor_height + ThisLink->height1;
5170 #endif
5171 }
5172 newsec->light_level = ThisStyle->doorlight0;
5173 newsec->style = ThisStyle; /* Is this right? */
5174
5175 /* This is where the level-change actually happens */
5176 patch_upper(ldf1,t1,c);
5177 patch_upper(ldf2,t2,c);
5178 patch_lower(ldf1,ThisStyle->kickplate,c); /* or stepfronts? */
5179 patch_lower(ldf2,NewStyle->kickplate,c);
5180
5181 ldf1->flags |= TWO_SIDED;
5182 ldf2->flags |= TWO_SIDED;
5183
5184 }
5185
5186 /* If no core, and a door, doorify the middle sector (s) */
5187 if ( (!(ThisLink->bits&(LINK_CORE|LINK_ALCOVE))) && (ThisLink->bits&LINK_ANY_DOOR) ) {
5188 if (!(ThisLink->bits&LINK_BARS)) {
5189 doorify(newsec,ldf1,ldf2,ThisStyle,NewStyle,c);
5190 if (trigger_door) {
5191 ldf1->type = LINEDEF_NORMAL;
5192 ldf2->type = (c->do_dm) ? LINEDEF_NORMAL_S1_DOOR : LINEDEF_NORMAL;
5193 newsec->tag = tag1;
5194 }
5195 if (!ThisStyle->moving_jambs) {
5196 ldnew1->flags |= LOWER_UNPEGGED;
5197 ldnew2->flags |= LOWER_UNPEGGED;
5198 }
5199 if (ThisQuest) {
5200 if (ThisQuest->goal==KEY_GOAL) {
5201 ldf1->type = type_for_key(ThisQuest->type);
5202 if (!painted_door)
5203 mark_door_for_key(l,ldf1,ThisQuest->type,ThisStyle,c);
5204 ldf2->type = type_for_key(ThisQuest->type); /* Prevent monsters! */
5205 } else if (ThisQuest->goal==SWITCH_GOAL && !(ThisLink->bits&LINK_LOCK_CORE)) {
5206 ldf1->type = LINEDEF_NORMAL;
5207 ldf2->type = (c->do_dm) ? LINEDEF_NORMAL_S1_DOOR : LINEDEF_NORMAL;
5208 newsec->tag = ThisQuest->tag;
5209 mark_door_for_lock(l,ldf1,ThisStyle,c);
5210 } /* end else if tag goal */
5211 } /* end if ThisQuest */
5212 } else {
5213 announce(VERBOSE,"Barred door");
5214 if (ThisLink->bits&LINK_LOCK_CORE)
5215 barify(l,ldf1,ldf2,NULL,16*l->hugeness,NULL,ThisStyle,c);
5216 else barify(l,ldf1,ldf2,ThisQuest,16*l->hugeness,NULL,ThisStyle,c);
5217 } /* end else barred door */
5218 }
5219
5220 /* If a core, and door(s), need to make the door sector(s) */
5221 if ( ((ThisLink->bits&LINK_CORE)) && (ThisLink->bits&LINK_ANY_DOOR) ) {
5222 if (ThisLink->bits&LINK_NEAR_DOOR) {
5223 ldnew1 = lefthand_box_ext(l,ldf1,ThisLink->depth1,ThisStyle,c,&ldedge1,&ldedge2);
5224 ldedge1->right->middle_texture = ThisStyle->doorjamb;
5225 ldedge2->right->middle_texture = ThisStyle->doorjamb;
5226 if (!ThisStyle->moving_jambs) {
5227 ldedge1->flags |= LOWER_UNPEGGED;
5228 ldedge2->flags |= LOWER_UNPEGGED;
5229 }
5230 /* Does the y offset of the doorjambs actually matter? */
5231 ldedge2->right->y_offset = ldedge1->right->y_offset =
5232 (nearsec->ceiling_height - nearsec->floor_height) - ThisLink->height1;
5233 }
5234 if (ThisLink->bits&LINK_FAR_DOOR) {
5235 ldnew2 = lefthand_box_ext(l,ldf2,ThisLink->depth1,NewStyle,c,&ldedge1,&ldedge2);
5236 ldedge1->right->middle_texture = NewStyle->doorjamb;
5237 ldedge2->right->middle_texture = NewStyle->doorjamb;
5238 if (!NewStyle->moving_jambs) {
5239 ldedge1->flags |= LOWER_UNPEGGED;
5240 ldedge2->flags |= LOWER_UNPEGGED;
5241 }
5242 ldedge2->right->y_offset = ldedge1->right->y_offset =
5243 (farsec->ceiling_height - farsec->floor_height) - ThisLink->height1;
5244 }
5245 /* Now we're working on the other sides of the doors. */
5246 /* But we can't really doorify them yet, since they have */
5247 /* no far sectors, and their linedefs need to be flipped */
5248 /* for the rest of the alg to work. So record fixup info */
5249 /* for later. */
5250 need_to_doorify = TRUE; /* Need to flip when done */
5251 if (ThisLink->bits&LINK_NEAR_DOOR) {
5252 ldflip1a = ldf1;
5253 ldflip1b = ldnew1;
5254 sflip1 = ldf1->left->sector;
5255 }
5256 if (ThisLink->bits&LINK_FAR_DOOR) {
5257 ldflip2a = ldf2;
5258 ldflip2b = ldnew2;
5259 sflip2 = ldf2->left->sector;
5260 /* Might as well fix the light-level on the far door while we're here */
5261 sflip2->light_level = NewStyle->doorlight0;
5262 }
5263 /* Now we're working on the far sides of the (future) door(s) */
5264 if (ThisLink->bits&LINK_NEAR_DOOR) ldf1 = ldnew1;
5265 if (ThisLink->bits&LINK_FAR_DOOR) ldf2 = ldnew2;
5266 }
5267
5268 /* If alcoves, make them now, and take new linedefs */
5269 if (ThisLink->bits&LINK_ALCOVE) {
5270 linedef *ldedgeopen, *ldedgeclosed;
5271 announce(VERBOSE,"Making alcoves");
5272 ldnew1 = lefthand_box_ext(l,ldf1,ThisLink->width2,ThisStyle,c,&ldedge1,&ldedge2);
5273 if (effective_left) {
5274 ldedgeopen = ldedge2;
5275 ldedgeclosed = ldedge1;
5276 } else {
5277 ldedgeopen = ldedge1;
5278 ldedgeclosed = ldedge2;
5279 }
5280 /* The near alcove copies the near room */
5281 ldnew1->right->middle_texture = ldedgeopen->right->middle_texture;
5282 ldedgeopen->right->middle_texture = c->null_texture;
5283 ldedgeopen->flags |= TWO_SIDED;
5284 ldnew1->right->sector->floor_height =
5285 ldf1->right->sector->floor_height;
5286 if (ThisLink->bits&LINK_MAX_CEILING) {
5287 ldnew1->right->sector->ceiling_height = maxtop;
5288 } else {
5289 ldnew1->right->sector->ceiling_height =
5290 ldf1->right->sector->floor_height + ThisLink->height1;
5291 }
5292 ldnew1->right->y_offset = ldedgeclosed->right->y_offset =
5293 nearsec->ceiling_height - ldnew1->right->sector->ceiling_height;
5294 patch_upper(ldf1,t1,c);
5295 ldf1 = ldedgeopen;
5296 ldnew2 = lefthand_box_ext(l,ldf2,ThisLink->width2,NewStyle,c,&ldedge1,&ldedge2);
5297 if (effective_left) {
5298 ldedgeopen = ldedge2;
5299 ldedgeclosed = ldedge1;
5300 } else {
5301 ldedgeopen = ldedge1;
5302 ldedgeclosed = ldedge2;
5303 }
5304 /* and the far the far */
5305 ldnew2->right->middle_texture = ldedgeopen->right->middle_texture;
5306 ldedgeopen->right->middle_texture = c->null_texture;
5307 ldedgeopen->flags |= TWO_SIDED;
5308 ldnew2->right->sector->floor_height = farsec->floor_height;
5309 if (ThisLink->bits&LINK_MAX_CEILING) {
5310 ldnew2->right->sector->ceiling_height = maxtop;
5311 } else {
5312 ldnew2->right->sector->ceiling_height = farsec->floor_height +
5313 ThisLink->height1;
5314 }
5315 ldnew2->right->sector->light_level = NewStyle->roomlight0;
5316 ldedgeclosed->right->y_offset = ldnew2->right->y_offset =
5317 farsec->ceiling_height - ldnew2->right->sector->ceiling_height;
5318 patch_upper(ldf2,t2,c);
5319 ldf2 = ldedgeopen;
5320 /* Now we're working out from the alcoves */
5321 len = ThisLink->width2;
5322 }
5323
5324 /* Record the area to maybe put a monster in */
5325 mminx = ldf1->from->x;
5326 if (ldf1->to->x<mminx) mminx = ldf1->to->x;
5327 if (ldf2->from->x<mminx) mminx = ldf2->from->x;
5328 if (ldf2->to->x<mminx) mminx = ldf2->to->x;
5329 mminy = ldf1->from->y;
5330 if (ldf1->to->y<mminy) mminy = ldf1->to->y;
5331 if (ldf2->from->y<mminy) mminy = ldf2->from->y;
5332 if (ldf2->to->y<mminy) mminy = ldf2->to->y;
5333 mmaxx = ldf1->from->x;
5334 if (ldf1->to->x>mmaxx) mmaxx = ldf1->to->x;
5335 if (ldf2->from->x>mmaxx) mmaxx = ldf2->from->x;
5336 if (ldf2->to->x>mmaxx) mmaxx = ldf2->to->x;
5337 mmaxy = ldf1->from->y;
5338 if (ldf1->to->y>mmaxy) mmaxy = ldf1->to->y;
5339 if (ldf2->from->y>mmaxy) mmaxy = ldf2->from->y;
5340 if (ldf2->to->y>mmaxy) mmaxy = ldf2->to->y;
5341 /* and the facing */
5342 mangle = facing_right_from_ld(ldf1);
5343
5344 /* If the core is stairs, put in all but the last */
5345 if (ThisLink->bits&LINK_STEPS) {
5346 int i, depth, stepdelta, x, y;
5347 texture *front = ThisStyle->kickplate;
5348 genus *g = ThisStyle->lamp0;
5349 boolean add_lamps = FALSE;
5350 if (g->height>ThisLink->height1) g = ThisStyle->shortlamp0;
5351 depth = ThisLink->depth3 / (ThisLink->stepcount+1);
5352 if ((ThisLink->bits&LINK_LAMPS) &&
5353 (g->width<=depth) && (g->width*2+64<=len)) {
5354 add_lamps = TRUE;
5355 announce(VERBOSE,"stair lamps");
5356 }
5357 stepdelta = ThisLink->floordelta / ThisLink->stepcount;
5358 if (ThisStyle->light_steps && ThisStyle->walllight) {
5359 front = ThisStyle->walllight;
5360 } else if (ThisStyle->stepfront &&
5361 (ThisStyle->stepfront->height>=abs(stepdelta))) {
5362 front = ThisStyle->stepfront;
5363 }
5364 /* Looks just like the recess stuff, mostly */
5365 for (i=0;i<ThisLink->stepcount;i++) {
5366 ldnew1 = lefthand_box_ext(l,ldf1,depth,ThisStyle,c,&ldedge1,&ldedge2);
5367 if ((add_lamps)&&(i&0x01)) {
5368 point_from(ldedge1->from->x,ldedge1->from->y,ldedge1->to->x,
5369 ldedge1->to->y,RIGHT_TURN,g->width/2,&x,&y);
5370 point_from(ldedge1->to->x,ldedge1->to->y,x,y,
5371 RIGHT_TURN,depth/2,&x,&y);
5372 new_thing(l,x,y,0,g->thingid,7,c);
5373 point_from(ldedge2->from->x,ldedge2->from->y,ldedge2->to->x,
5374 ldedge2->to->y,RIGHT_TURN,g->width/2,&x,&y);
5375 point_from(ldedge2->to->x,ldedge2->to->y,x,y,
5376 RIGHT_TURN,depth/2,&x,&y);
5377 new_thing(l,x,y,0,g->thingid,7,c);
5378 }
5379 ldnew1->right->sector->floor_height =
5380 ldf1->right->sector->floor_height + stepdelta;
5381 if (ThisLink->bits&LINK_MAX_CEILING)
5382 ldnew1->right->sector->ceiling_height = maxtop;
5383 else ldnew1->right->sector->ceiling_height =
5384 ldnew1->right->sector->floor_height + ThisLink->height1;
5385 ldnew1->right->sector->floor_flat = ThisStyle->stepfloor;
5386 ldedge1->right->y_offset =
5387 ldedge2->right->y_offset =
5388 nearsec->ceiling_height - ldedge1->right->sector->ceiling_height;
5389 patch_upper(ldf1,t1,c);
5390 patch_lower(ldf1,front,c);
5391 ldf1->flags &= ~LOWER_UNPEGGED;
5392 ldf1 = ldnew1;
5393 } /* end for each step */
5394 /* The core-making step will do the top (bottom) (far) step */
5395 } /* end if each steps */
5396
5397 /* If a core, need to make it */
5398 if (ThisLink->bits&LINK_CORE) {
5399 flip_linedef(ldf2);
5400 newsec = make_box_ext(l,ldf1,ldf2,ThisStyle,c,&ldedge1,&ldedge2);
5401 flip_linedef(ldf2);
5402 if (ThisLink->bits&LINK_MAX_CEILING) newsec->ceiling_height = maxtop;
5403 if (newsec->ceiling_height - ldf1->right->sector->floor_height < 64)
5404 newsec->ceiling_height = ldf1->right->sector->floor_height + 64;
5405 if (newsec->ceiling_height - ldf2->right->sector->floor_height < 64)
5406 newsec->ceiling_height = ldf2->right->sector->floor_height + 64;
5407 ldedge2->right->y_offset = ldedge1->right->y_offset =
5408 nearsec->ceiling_height - newsec->ceiling_height;
5409 if ((ThisQuest!=NULL)&&(ThisLink->bits&LINK_LOCK_CORE)) {
5410 newsec->floor_flat = ThisStyle->nukage1;
5411 newsec->special = NUKAGE1_SPECIAL;
5412 newsec->tag = ThisQuest->tag;
5413 newsec->floor_height -= 24 - roll(72,ThisLink->floordelta);
5414 if (newsec->light_level<160) newsec->light_level = 160; /* Visible */
5415 patch_lower(ldf1,ThisStyle->kickplate,c); /* Or stepfront? */
5416 if (rollpercent(163,50)) ldf2->flags |= BLOCK_MONSTERS; /* Can't decide! */
5417 haa->haas[ITYTD].health -= 10;
5418 haa->haas[HMP].health -= 5;
5419 announce(VERBOSE,"Nukage lock");
5420 } else if (rollpercent(164,l->p_force_nukage)&&!(ThisLink->bits&LINK_LIFT)
5421 &&!(ThisLink->bits&LINK_STEPS)&&(ThisLink->depth3>=64)
5422 &&(ThisLink->depth3<=196)) {
5423 newsec->floor_flat = ThisStyle->nukage1;
5424 newsec->special = NUKAGE1_SPECIAL;
5425 if (ThisLink->floordelta<0) {
5426 newsec->floor_height += ThisLink->floordelta; /* Fixed +- bug here */
5427 newsec->floor_height -= roll(73,25+ThisLink->floordelta);
5428 } else {
5429 newsec->floor_height -= roll(74,25-ThisLink->floordelta);
5430 }
5431 if (newsec->light_level<160) newsec->light_level = 160; /* Visible */
5432 patch_lower(ldf1,ThisStyle->kickplate,c);
5433 haa->haas[ITYTD].health -= 10;
5434 haa->haas[HMP].health -= 5;
5435 announce(VERBOSE,"Nukage link");
5436 } else if ((rollpercent(165,l->p_falling_core))&&
5437 (linelen(ldedge1)>=(120*l->hugeness))&&
5438 (!(ThisLink->bits&LINK_LIFT))&&
5439 (flipstate==0) ) { /* Maybe a fun trap */
5440 try_falling_core(l,ldedge1,ldedge2,haa,c);
5441 }
5442 ldf2->flags |= TWO_SIDED;
5443 patch_upper(ldf1,t1,c);
5444
5445 /* This is where the level-change actually happens, eh? */
5446 patch_upper(ldf2,NewStyle->wall0,c);
5447 patch_lower(ldf2,NewStyle->kickplate,c); /* or stepfront? */
5448
5449 /* On the first pass, sometimes save the righthand wall of the */
5450 /* core, so that the second pass can make a decorative room. */
5451 if (flipstate==1) {
5452 if ( (!(ThisLink->bits&(LINK_LIFT|LINK_ALCOVE)))
5453 && (ThisLink->bits&LINK_DECROOM)
5454 && (linelen(ldedge2)>63) ) {
5455 ThisLink->cld = ldedge2;
5456 } else {
5457 ThisLink->cld = NULL;
5458 }
5459 }
5460 /* On the second pass, if we did that, make the room */
5461 if ((flipstate==2)&&ThisLink->cld) {
5462 if (rollpercent(166,10)) {
5463 if (make_window(l,ldedge1,ThisLink->cld,ThisLink,ThisStyle,NewStyle,c))
5464 announce(LOG,"Intertwin window");
5465 } else {
5466 if (make_decroom(l,ldedge1,ThisLink->cld,c))
5467 announce(LOG,"Intertwin decroom");
5468 }
5469 }
5470
5471 } /* end if a core */
5472
5473 /* If the core is a lift, make that happen */
5474 if (ThisLink->bits&LINK_LIFT) {
5475 if (trigger_lift) newsec->tag = tag1;
5476 else newsec->tag = new_tag(l);
5477 newsec->ceiling_flat = ThisStyle->doorceiling; /* really? */
5478 newsec->floor_flat = ThisStyle->doorfloor; /* really? */
5479 ldf1->tag = newsec->tag;
5480 ldf2->tag = newsec->tag;
5481 if (nearsec->floor_height>farsec->floor_height) {
5482 newsec->floor_height = nearsec->floor_height;
5483 ldf1->type = LINEDEF_WR_LOWER_LIFT;
5484 ldf1->left->lower_texture = ThisStyle->support0; /* yes? */
5485 if (!trigger_lift) ldf2->type = NewStyle->slifttype;
5486 patch_lower(ldf2,NewStyle->support0,c);
5487 if ( (NewStyle->liftface) &&
5488 (nearsec->floor_height - farsec->floor_height <=
5489 NewStyle->liftface->height) &&
5490 (linelen(ldf2)==NewStyle->liftface->width) ) {
5491 ldf2->right->lower_texture = NewStyle->liftface;
5492 ldf2->right->x_offset = 0;
5493 announce(VERBOSE,"Lift texture");
5494 }
5495 ldf2->flags &= ~LOWER_UNPEGGED; /* Lift-falling must be visible! */
5496 } else {
5497 newsec->floor_height = farsec->floor_height;
5498 if (!trigger_lift) ldf1->type = ThisStyle->slifttype;
5499 patch_lower(ldf1,ThisStyle->support0,c);
5500 if ( (ThisStyle->liftface) &&
5501 (farsec->floor_height - nearsec->floor_height <=
5502 ThisStyle->liftface->height) &&
5503 (linelen(ldf1)==ThisStyle->liftface->width) ) {
5504 ldf1->right->lower_texture = ThisStyle->liftface;
5505 ldf1->right->x_offset = 0;
5506 announce(VERBOSE,"Lift texture");
5507 }
5508 ldf1->flags &= ~LOWER_UNPEGGED; /* Lift-falling must be visible! */
5509 ldf2->type = LINEDEF_WR_LOWER_LIFT;
5510 ldf2->left->lower_texture = NewStyle->support0; /* right? */
5511 } /* end else lift that way */
5512 newsec->ceiling_height = newsec->floor_height + ThisLink->height1;
5513 /* and re-figure the y offsets in the core */
5514 ldedge2->right->y_offset = ldedge1->right->y_offset =
5515 nearsec->ceiling_height - newsec->ceiling_height;
5516 patch_upper(ldf1,ThisStyle->wall0,c);
5517 patch_upper(ldf2,NewStyle->wall0,c);
5518 } /* end if doing a lift */
5519
5520 /* Maybe put a monster in the link */
5521 if ((haa!=NULL)&&(ThisLink->bits&LINK_CORE)&&(rollpercent(167,40))) {
5522 int levels;
5523 genus *m = timely_monster(haa,c,&levels,rollpercent(168,l->p_biggest_monsters),0);
5524 if (m) {
5525 if (rollpercent(169,15)) levels |= 0x08; /* deaf */
5526 /* Try to place it */
5527 if (!rollpercent(170,l->p_rational_facing)) mangle = 90 * roll(75,4);
5528 if (NULL!=place_object_in_region(l,mminx,mminy,mmaxx,mmaxy,
5529 c,m->thingid,MONSTER_WIDTH(m),mangle,0,0,levels)) {
5530
5531 if (m->thingid == ID_SKULL) announce(NONE,"Skull");
5532 if (m->thingid == ID_HEAD) announce(VERBOSE,"HEAD");
5533 if (m->thingid == ID_SKEL) announce(VERBOSE,"SKEL");
5534 if (m->thingid == ID_HELL) announce(VERBOSE,"KNIGHT");
5535 if (m->thingid == ID_ARCHIE) announce(VERBOSE,"VILE");
5536
5537 update_haa_for_monster(haa,m,levels,0,c);
5538 haa_unpend(haa);
5539
5540 announce(VERBOSE,"Link guard");
5541 } /* end if placed it */
5542 } /* end if timely one */
5543 } /* end if a monster */
5544
5545 /* Finally, if we have unmade doors, make 'em */
5546 if (need_to_doorify) {
5547 if (ThisLink->bits&LINK_NEAR_DOOR) {
5548 flip_linedef(ldflip1b);
5549 doorify(sflip1,ldflip1a,ldflip1b,ThisStyle,ThisStyle,c);
5550 if (trigger_door) {
5551 ldflip1a->type = LINEDEF_NORMAL;
5552 ldflip1b->type = LINEDEF_NORMAL_DOOR; /* S1 door would break! */
5553 sflip1->tag = tag1;
5554 }
5555 if (ThisQuest) {
5556 if (ThisQuest->goal==KEY_GOAL) {
5557 ldflip1a->type = type_for_key(ThisQuest->type);
5558 if (!painted_door)
5559 mark_door_for_key(l,ldflip1a,ThisQuest->type,ThisStyle,c);
5560 ldflip1b->type = type_for_key(ThisQuest->type); /* Prevent monsters! */
5561 } else if ((ThisQuest->goal==SWITCH_GOAL) && !(ThisLink->bits&LINK_LOCK_CORE)) {
5562 ldflip1a->type = LINEDEF_NORMAL;
5563 ldflip1b->type = (c->do_dm) ? LINEDEF_NORMAL_S1_DOOR : LINEDEF_NORMAL;
5564 sflip1->tag = ThisQuest->tag;
5565 mark_door_for_lock(l,ldflip1a,ThisStyle,c);
5566 } /* end else if tag goal */
5567 } /* end else if ThisQuest */
5568 }
5569 if (ThisLink->bits&LINK_FAR_DOOR) {
5570 flip_linedef(ldflip2b);
5571 doorify(sflip2,ldflip2a,ldflip2b,NewStyle,NewStyle,c);
5572 if (trigger_door) {
5573 ldflip2a->type = LINEDEF_NORMAL;
5574 ldflip2b->type = LINEDEF_NORMAL_DOOR; /* S1 door would break! */
5575 sflip2->tag = tag1;
5576 }
5577 }
5578 } /* end if need to doorify */
5579
5580 /* Whew! */
5581
5582 } /* end e_bl_inner() */
5583
5584 /* Return a random linedef in the plain; no sidedefs or anything. */
starting_linedef(level * l,style * ThisStyle,config * c)5585 linedef *starting_linedef(level *l,style *ThisStyle,config *c)
5586 {
5587 vertex *v1,*v2;
5588
5589 int first_room_size;
5590
5591 first_room_size = l->hugeness*64*(2+roll(76,9));
5592 /* We don't want rooms to be too small if we can have teleports on the
5593 level - SET */
5594 if(l->use_gates && first_room_size < TELEPORT_MINROOMSIZE) {
5595 first_room_size = TELEPORT_MINROOMSIZE;
5596 }
5597 v1 = new_vertex(l,0,0); /* Might as well */
5598 /* Should consult the style/config? */
5599 v2 = new_vertex(l,0,first_room_size);
5600 return new_linedef(l,v1,v2);
5601 }
5602
5603 /* Given a ray from the given point to the given point, turn in */
5604 /* the given direction and go the given distance. Return the */
5605 /* point that we end up at. */
point_from(int x1,int y1,int x2,int y2,int angle,int len,int * x3,int * y3)5606 void point_from(int x1, int y1, int x2, int y2, int angle, int len,
5607 int *x3, int *y3)
5608 {
5609 /* Stub; right angles and axis-parallel lines only */
5610 int newdx, newdy;
5611
5612 if (x1==x2) { /* Parallel to the Y-axis */
5613 newdy = 0;
5614 if (y2>y1) { /* Up */
5615 newdx = 1;
5616 } else { /* Down */
5617 newdx = -1;
5618 }
5619 } else { /* to the X-axis */
5620 newdx = 0;
5621 if (x2>x1) { /* rightward */
5622 newdy = -1;
5623 } else {
5624 newdy = 1;
5625 }
5626 }
5627 if (angle==LEFT_TURN) {
5628 newdx = 0 - newdx;
5629 newdy = 0 - newdy;
5630 }
5631 *x3 = x2 + len * newdx;
5632 *y3 = y2 + len * newdy;
5633 return;
5634 }
5635
5636 /* Print, log, whatever. Really ought to support real log files. */
announce(int announcelevel,char * s)5637 void announce(int announcelevel, char *s)
5638 {
5639 switch (announcelevel) {
5640 case NONE: return;
5641 case VERBOSE: if (global_verbosity==0) return; /** fallthrough **/
5642 case LOG: break;
5643 case NOTE: printf("NOTE: "); break;
5644 case WARNING: printf("WARNING: "); break;
5645 case ERROR: printf("ERROR: "); break;
5646 default: printf("HEY: "); break;
5647 }
5648 printf("%s\n",s);
5649 }
5650
5651 /* Install a switch on the given linedef. If the actual */
5652 /* switch linedef changes (due to recessing, say), return */
5653 /* that new linedef, else the old one. Doesn't set the */
5654 /* type or tag, just the texture and stuff. If xld is */
5655 /* given, it gets the two-sided center part of ld. */
install_switch(level * l,linedef * ld,boolean recess,boolean fancy,short key,style * ThisStyle,config * c,linedef ** xld)5656 linedef *install_switch(level *l,linedef *ld,boolean recess,boolean fancy,
5657 short key, style *ThisStyle,config *c,linedef **xld)
5658 {
5659 linedef *ld2, *ldedge1, *ldedge2;
5660 int rdepth = 8;
5661 texture *tx = NULL;
5662
5663 if (fancy) {
5664 ThisStyle->lightboxes = TRUE;
5665 announce(VERBOSE,"fancy switch");
5666 }
5667 ld = borderize(l,ld,64,TRUE,ThisStyle,LIGHT,NULL,NULL,c);
5668 if (xld) *xld = ld;
5669 ThisStyle->lightboxes = FALSE;
5670 if (recess)
5671 if (key) {
5672 tx = texture_for_key(key,ThisStyle,c);
5673 rdepth = tx->width;
5674 if (rdepth>8)
5675 if (!empty_left_side(l,ld,rdepth)) rdepth=8;
5676 }
5677 if (empty_left_side(l,ld,rdepth)) {
5678 texture *t1 = ld->right->middle_texture;
5679 ld2 = lefthand_box_ext(l,ld,rdepth,ThisStyle,c,&ldedge1,&ldedge2);
5680 ld2->right->sector->ceiling_height =
5681 ld2->right->sector->floor_height + 72; /* Should vary? */
5682 /* Maybe paint/light the recesses */
5683 if (key) {
5684 ldedge2->right->middle_texture = ldedge1->right->middle_texture = tx;
5685 } else if ((ThisStyle->light_recesses)&&(ThisStyle->walllight!=NULL)) {
5686 announce(VERBOSE,"Lit switch");
5687 ldedge2->right->middle_texture = ldedge1->right->middle_texture =
5688 ThisStyle->walllight;
5689 make_lighted(l,ld2->right->sector,c);
5690 } else {
5691 ldedge2->right->y_offset = ldedge1->right->y_offset =
5692 (ld->right->sector->ceiling_height -
5693 ld->right->sector->floor_height) - 72;
5694 }
5695 patch_upper(ld,t1,c);
5696 ld = ld2;
5697 }
5698 ld->right->middle_texture = ThisStyle->switch0;
5699 ld->right->x_offset = 0;
5700 ld->right->y_offset = ThisStyle->switch0->y_bias;
5701 ld->flags |= LOWER_UNPEGGED;
5702 return ld;
5703 }
5704
5705 /* Perhaps add a deathmatch start to this sector, if it doesn't */
5706 /* already have one, and we're doing deathmatch stuff. */
maybe_add_dm_start(level * l,sector * s,config * c,boolean force)5707 boolean maybe_add_dm_start(level *l, sector *s, config *c, boolean force) {
5708
5709 if (!c->do_dm) return FALSE;
5710 if (s->has_dm && !force) return FALSE;
5711 if (place_object(l,s,c,ID_DM,34,-1,s->entry_x,s->entry_y,7)) {
5712 s->has_dm = TRUE;
5713 l->dm_count++;
5714 if (!s->has_dm_weapon)
5715 if (place_object(l,s,c,ID_SHOTGUN,24,0,0,0,0x17))
5716 s->has_dm_weapon = TRUE;
5717 return TRUE;
5718 }
5719 return FALSE;
5720 }
5721
5722 /* Does anything involved with closing a quest that has to happen */
5723 /* -after- the room is populated and embellished and stuff. */
close_quest_final(level * l,sector * s,quest * q,haa * haa,config * c)5724 void close_quest_final(level *l, sector *s, quest *q, haa *haa, config *c)
5725 {
5726 thing *t = q->thing;
5727
5728 l->goal_room = s;
5729
5730 maybe_add_dm_start(l,s,c,FALSE);
5731
5732 if ( t && (q->auxtag) && (q->surprise)) {
5733 trigger_box(l,t,s,q->auxtag,LINEDEF_WR_OPEN_DOOR,c);
5734 populate_linedef(l,q->surprise,haa,c,FALSE);
5735 }
5736
5737 /* If we've put in the SL exit, but not yet the thing that */
5738 /* opens it, do one last try at that. */
5739 if ((q->goal==LEVEL_END_GOAL) && (l->sl_open_ok)) {
5740 t = place_required_small_pickable(l,s,c);
5741 if (t) {
5742 trigger_box(l,t,s,l->sl_tag,l->sl_type,c);
5743 l->sl_done = TRUE;
5744 l->sl_open_ok = FALSE;
5745 announce(VERBOSE,"Did sl triggerbox");
5746 }
5747 }
5748
5749 /* On the other hand, if we haven't even put in the SL exit yet, */
5750 /* we're really desparate! */
5751 if (need_secret_level(c) && !l->sl_done &&!l->sl_tag &&
5752 q->goal==LEVEL_END_GOAL) {
5753 int i;
5754 linedef *ldf;
5755 i = mark_decent_boundary_linedefs(l,s,32);
5756 ldf = random_marked_linedef(l,i);
5757 unmark_linedefs(l);
5758 if (i) {
5759 if (ldf->right->middle_texture->subtle)
5760 ldf->right->middle_texture = ldf->right->middle_texture->subtle;
5761 else ldf->right->middle_texture = s->style->support0;
5762 ldf->type = LINEDEF_S1_SEC_LEVEL;
5763 announce(LOG,"Last-ditch SL exit!");
5764 l->sl_done = TRUE;
5765 }
5766 }
5767 }
5768
5769 /* Make one of them instant-death rooms, like at the end */
5770 /* of E1M8, using the linedef in the linkto(), and the */
5771 /* style for the walls 'n' stuff. */
death_room(level * l,linedef * ld,style * ThisStyle,config * c)5772 short death_room(level *l,linedef *ld,style *ThisStyle,config *c)
5773 {
5774 linedef *ldnew;
5775 link *gatelink = gate_link(l,c);
5776 sector *newsector;
5777 int minx,miny,maxx,maxy;
5778 int x;
5779
5780 ldnew = make_linkto(l,ld,gatelink,ThisStyle,c,NULL);
5781 if (ldnew==NULL) return 0;
5782 for (;linelen(ldnew)<320;) {
5783 ldnew->to->x = ldnew->from->x + 2 *
5784 (ldnew->to->x - ldnew->from->x);
5785 ldnew->to->y = ldnew->from->y + 2 *
5786 (ldnew->to->y - ldnew->from->y);
5787 }
5788 newsector = generate_room_outline(l,ldnew,ThisStyle,FALSE,c);
5789 newsector->style = ThisStyle;
5790 paint_room(l,newsector,ThisStyle,c);
5791 newsector->tag = new_tag(l);
5792 newsector->special = DEATH_SECTOR;
5793 newsector->light_level = 80;
5794
5795 find_rec(l,newsector,&minx,&miny,&maxx,&maxy);
5796 new_thing(l,(minx+maxx)/2,(miny+maxy)/2,(short)(90*roll(77,4)),ID_GATEOUT,7,c);
5797
5798 /* Worry about having *too many* sergeants? */
5799 for (x=minx+22; x<=maxx-22; x+=44) {
5800 new_thing(l,x,miny+22,90,ID_SERGEANT,7,c);
5801 new_thing(l,x,maxy-22,270,ID_SERGEANT,7,c);
5802 }
5803
5804 return newsector->tag;
5805
5806 }
5807
5808 /* Simple trial implementation: just an "EXIT" gate to */
5809 /* an e1m8 instant-death room. */
e1m8_gate(level * l,linedef * ld,sector * s,haa * haa,config * c)5810 boolean e1m8_gate(level *l,linedef *ld,sector *s,haa *haa,config *c)
5811 {
5812 short tag = death_room(l,ld,s->style,c);
5813
5814 if (tag==0) return FALSE;
5815 s->gate = new_gate(l,0,tag,0,FALSE,c);
5816 install_gate(l,s,s->style,haa,TRUE,c);
5817 s->middle_enhanced = TRUE;
5818 gate_populate(l,s,haa,FALSE,c); /* safe/correct? */
5819 return TRUE;
5820
5821 }
5822
5823 /* Put down zero or more of the required powerups in the */
5824 /* arena's prep-room. */
prepare_arena_gate(level * l,sector * s,arena * a,haa * haa,config * c)5825 void prepare_arena_gate(level *l,sector *s,arena *a, haa *haa,config *c)
5826 {
5827 /*STUB!*/
5828 }
5829
5830 /* Actually put down the main linedefs and sectors */
5831 /* and stuff for the arena. */
install_arena(level * l,arena * a,sector * s,haa * haa,config * c)5832 void install_arena(level *l,arena *a,sector *s,haa *haa,config *c)
5833 {
5834 int maxx = 0-HUGE_NUMBER;
5835 vertex *v, *v1, *v2, *v3, *v4;
5836 vertex *vt1, *vt2;
5837 int upness, acrossness, border, i, n;
5838 sector *newsec;
5839 linedef *ld;
5840 short ch;
5841 genus *lamp;
5842
5843 ch = 128; /* Too simple */
5844 newsec = new_sector(l,0,ch,a->floor,c->sky_flat);
5845 newsec->light_level = c->minlight+roll(78,100);
5846 newsec->style = s->style;
5847 a->outersec = newsec;
5848
5849 for (v=l->vertex_anchor;v;v=v->next) if (v->x > maxx) maxx = v->x;
5850 maxx += 256;
5851 upness = 750 + roll(79,501);
5852 acrossness = 3*upness;
5853 if (a->props&ARENA_PORCH) {
5854 border = 72 + 32 * roll(80,11);
5855 } else {
5856 border = 50 + roll(81,200);
5857 }
5858 maxx += border+16;
5859 a->minx = maxx;
5860 a->maxx = maxx + acrossness;
5861 a->miny = 0-upness/2;
5862 a->maxy = upness/2;
5863
5864 /* Make the outer walls */
5865 if (a->props&ARENA_PORCH) {
5866 /* Flat with promenade */
5867 newsec->ceiling_flat = newsec->style->ceiling0;
5868 v1 = new_vertex(l,a->minx-(16+border/2),a->miny-(16+border/2));
5869 v2 = new_vertex(l,a->minx-(16+border/2),a->maxy+(16+border/2));
5870 v3 = new_vertex(l,a->maxx+(16+border/2),a->maxy+(16+border/2));
5871 v4 = new_vertex(l,a->maxx+(16+border/2),a->miny-(16+border/2));
5872
5873 ld = new_linedef(l,v1,v2);
5874 ld->right = new_sidedef(l,newsec,c);
5875 ld->right->middle_texture = a->walls;
5876
5877 ld = new_linedef(l,v2,v3);
5878 ld->right = new_sidedef(l,newsec,c);
5879 ld->right->middle_texture = a->walls;
5880
5881 ld = new_linedef(l,v3,v4);
5882 ld->right = new_sidedef(l,newsec,c);
5883 ld->right->middle_texture = a->walls;
5884
5885 ld = new_linedef(l,v4,v1);
5886 ld->right = new_sidedef(l,newsec,c);
5887 ld->right->middle_texture = a->walls;
5888
5889 if (a->props&ARENA_LAMPS) {
5890 lamp = newsec->style->lamp0;
5891 if (lamp->height >= ch) lamp = newsec->style->shortlamp0;
5892 new_thing(l,v1->x+64,v1->y+64,0,lamp->thingid,7,c);
5893 new_thing(l,v2->x+64,v2->y-64,0,lamp->thingid,7,c);
5894 new_thing(l,v3->x-64,v3->y-64,0,lamp->thingid,7,c);
5895 new_thing(l,v4->x-64,v4->y+64,0,lamp->thingid,7,c);
5896 }
5897
5898 } else {
5899 /* or irregular */
5900 v1 = new_vertex(l,a->minx-(16+roll(82,border/2)),
5901 a->miny-(16+roll(264,border/2)));
5902 v2 = new_vertex(l,a->minx-(16+roll(83,border/2)),
5903 a->maxy+(16+roll(265,border/2)));
5904 v3 = new_vertex(l,a->maxx+(16+roll(84,border/2)),
5905 a->maxy+(16+roll(266,border/2)));
5906 v4 = new_vertex(l,a->maxx+(16+roll(85,border/2)),
5907 a->miny-(16+roll(267,border/2)));
5908 /* left north-south wallset */
5909 n = 1 + roll(86,10); vt1 = v1;
5910 for (i=1;i<n;i++) {
5911 vt2 = new_vertex(l,a->minx-(16+roll(87,border)),a->miny+i*(upness/(n+1)));
5912 ld = new_linedef(l,vt1,vt2);
5913 ld->right = new_sidedef(l,newsec,c);
5914 ld->right->middle_texture = a->walls;
5915 vt1 = vt2;
5916 }
5917 ld = new_linedef(l,vt1,v2);
5918 ld->right = new_sidedef(l,newsec,c);
5919 ld->right->middle_texture = a->walls;
5920 /* top left-right wallset */
5921 n = 1 + roll(88,10); vt1 = v2;
5922 for (i=1;i<n;i++) {
5923 vt2 = new_vertex(l,a->minx+i*(acrossness/(n+1)),a->maxy+(16+roll(89,border)));
5924 ld = new_linedef(l,vt1,vt2);
5925 ld->right = new_sidedef(l,newsec,c);
5926 ld->right->middle_texture = a->walls;
5927 vt1 = vt2;
5928 }
5929 ld = new_linedef(l,vt1,v3);
5930 ld->right = new_sidedef(l,newsec,c);
5931 ld->right->middle_texture = a->walls;
5932 /* right south-north wallset */
5933 n = 1 + roll(90,10); vt1 = v3;
5934 for (i=1;i<n;i++) {
5935 vt2 = new_vertex(l,a->maxx+(16+roll(91,border)),a->maxy-i*(upness/(n+1)));
5936 ld = new_linedef(l,vt1,vt2);
5937 ld->right = new_sidedef(l,newsec,c);
5938 ld->right->middle_texture = a->walls;
5939 vt1 = vt2;
5940 }
5941 ld = new_linedef(l,vt1,v4);
5942 ld->right = new_sidedef(l,newsec,c);
5943 ld->right->middle_texture = a->walls;
5944 /* bottom right-left wallset */
5945 n = 1 + roll(92,10); vt1 = v4;
5946 for (i=1;i<n;i++) {
5947 vt2 = new_vertex(l,a->maxx-i*(acrossness/(n+1)),a->miny-(16+roll(93,border)));
5948 ld = new_linedef(l,vt1,vt2);
5949 ld->right = new_sidedef(l,newsec,c);
5950 ld->right->middle_texture = a->walls;
5951 vt1 = vt2;
5952 }
5953 ld = new_linedef(l,vt1,v1);
5954 ld->right = new_sidedef(l,newsec,c);
5955 ld->right->middle_texture = a->walls;
5956 }
5957
5958 /* Now the inner sector */
5959
5960 ch = 256 + 64 * roll(94,3); /* Too simple? */
5961 newsec = new_sector(l,0,ch,a->floor,c->sky_flat);
5962 newsec->light_level = a->outersec->light_level;
5963 newsec->style = s->style;
5964 if (a->props&ARENA_ROOF) {
5965 newsec->ceiling_flat = newsec->style->ceiling0;
5966 a->outersec->ceiling_flat = newsec->style->ceiling0;
5967 newsec->light_level -= 16;
5968 if (newsec->light_level < c->minlight)
5969 newsec->light_level = c->minlight;
5970 if (newsec->light_level>l->bright_light_level)
5971 newsec->light_level = l->bright_light_level;
5972 }
5973
5974 /* Some sector adjustments... */
5975 if (rollpercent(171,30) && (a->props&ARENA_PORCH)) {
5976 flat *light_flat;
5977 a->outersec->special = RANDOM_BLINK;
5978 a->outersec->light_level += 20;
5979 if (a->outersec->light_level > l->bright_light_level)
5980 a->outersec->light_level = l->bright_light_level;
5981 light_flat = random_flat0(CEILING|LIGHT,c,NULL);
5982 if (light_flat) a->outersec->ceiling_flat = light_flat;
5983 }
5984
5985 if (a->props&ARENA_NUKAGE) {
5986 a->outersec->floor_height -= 8;
5987 if (a->props&ARENA_PORCH) a->outersec->ceiling_height -= 8;
5988 a->outersec->floor_flat = a->outersec->style->nukage1;
5989 a->outersec->special = NUKAGE1_SPECIAL;
5990 } else if ((a->props&ARENA_PORCH) && rollpercent(172,50)) {
5991 short d = 8 + 8 * roll(95,3);
5992 a->outersec->floor_height += d;
5993 a->outersec->ceiling_height += d;
5994 }
5995
5996 /* Now the inner sector's boundaries */
5997
5998 v1 = new_vertex(l,a->minx,a->miny);
5999 v2 = new_vertex(l,a->minx,a->maxy);
6000 v3 = new_vertex(l,a->maxx,a->maxy);
6001 v4 = new_vertex(l,a->maxx,a->miny);
6002
6003 ld = new_linedef(l,v1,v2);
6004 ld->flags |= TWO_SIDED;
6005 ld->right = new_sidedef(l,newsec,c);
6006 ld->right->middle_texture = c->null_texture;
6007 ld->left = new_sidedef(l,a->outersec,c);
6008 ld->left->middle_texture = c->null_texture;
6009 patch_upper(ld,a->walls,c);
6010 patch_lower(ld,a->walls,c);
6011
6012 ld = new_linedef(l,v2,v3);
6013 ld->flags |= TWO_SIDED;
6014 ld->right = new_sidedef(l,newsec,c);
6015 ld->right->middle_texture = c->null_texture;
6016 ld->left = new_sidedef(l,a->outersec,c);
6017 ld->left->middle_texture = c->null_texture;
6018 patch_upper(ld,a->walls,c);
6019 patch_lower(ld,a->walls,c);
6020
6021 ld = new_linedef(l,v3,v4);
6022 ld->flags |= TWO_SIDED;
6023 ld->right = new_sidedef(l,newsec,c);
6024 ld->right->middle_texture = c->null_texture;
6025 ld->left = new_sidedef(l,a->outersec,c);
6026 ld->left->middle_texture = c->null_texture;
6027 patch_upper(ld,a->walls,c);
6028 patch_lower(ld,a->walls,c);
6029
6030 ld = new_linedef(l,v4,v1);
6031 ld->flags |= TWO_SIDED;
6032 ld->right = new_sidedef(l,newsec,c);
6033 ld->right->middle_texture = c->null_texture;
6034 ld->left = new_sidedef(l,a->outersec,c);
6035 ld->left->middle_texture = c->null_texture;
6036 patch_upper(ld,a->walls,c);
6037 patch_lower(ld,a->walls,c);
6038
6039 if ((a->props&ARENA_LAMPS)&&(!(a->props&ARENA_PORCH))) {
6040 lamp = newsec->style->lamp0;
6041 if (lamp->height >= ch) lamp = newsec->style->shortlamp0;
6042 new_thing(l,v1->x+2,v1->y+2,0,lamp->thingid,7,c);
6043 new_thing(l,v2->x+2,v2->y-2,0,lamp->thingid,7,c);
6044 new_thing(l,v3->x-2,v3->y-2,0,lamp->thingid,7,c);
6045 new_thing(l,v4->x-2,v4->y+2,0,lamp->thingid,7,c);
6046 }
6047
6048 a->innersec = newsec;
6049 }
6050
6051 /* Make the arrival area, where the player first enters */
6052 /* the arena, as well as any powerups he needs. */
arena_arrival(level * l,arena * a,haa * haa,config * c)6053 void arena_arrival(level *l,arena *a,haa *haa,config *c)
6054 {
6055 int minx,maxx;
6056 int cx,cy;
6057 float na0, na1, na2; /* Needed ammos */
6058 int f0,f1,f2;
6059 int mask = 7;
6060 sector *newsec;
6061
6062 minx = a->minx;
6063 maxx = a->minx + ( a->maxx - a->minx ) / 3;
6064 cx = ( minx + maxx ) / 2;
6065 cy = ( a->miny + a->maxy ) / 2;
6066
6067 /* a simple version */
6068 new_thing(l,cx,cy,(short)(90*roll(96,4)),ID_GATEOUT,7,c);
6069 a->innersec->tag = a->fromtag;
6070 a->innersec->entry_x = cx;
6071 a->innersec->entry_y = cy;
6072
6073 /* except for this */
6074 if (a->props&ARENA_ARRIVAL_HOLE) {
6075 linedef *ld1,*ld2,*ld3,*ld4;
6076 newsec = clone_sector(l,a->innersec);
6077 newsec->floor_height -= 384; /* Down in a hole! */
6078 parallel_innersec_ex(l,a->innersec,newsec,
6079 NULL,a->walls,a->walls,
6080 cx-31,cy-31,cx+31,cy+31,
6081 c,&ld1,&ld2,&ld3,&ld4);
6082 /* Fix teleport-tag */
6083 a->innersec->tag = 0;
6084 newsec->tag = a->fromtag;
6085 /* Make walls touchable */
6086 flip_linedef(ld1);
6087 flip_linedef(ld2);
6088 flip_linedef(ld3);
6089 flip_linedef(ld4);
6090 ld1->type = LINEDEF_S1_RAISE_FLOOR;
6091 ld1->tag = newsec->tag;
6092 ld2->type = LINEDEF_S1_RAISE_FLOOR;
6093 ld2->tag = newsec->tag;
6094 ld3->type = LINEDEF_S1_RAISE_FLOOR;
6095 ld3->tag = newsec->tag;
6096 ld4->type = LINEDEF_S1_RAISE_FLOOR;
6097 ld4->tag = newsec->tag;
6098 }
6099
6100 /* Now weapons and ammo and stuff */
6101 if (!place_object_in_region(l,minx,a->miny,maxx,a->maxy,
6102 c,a->weapon->thingid,24,0,0,0,7))
6103 if (!place_object_in_region(l,minx,a->miny,maxx,a->maxy,
6104 c,a->weapon->thingid,1,0,0,0,7))
6105 announce(ERROR,"No room for important weapon!");
6106 place_object_in_region(l,minx,a->miny,maxx,a->maxy,
6107 c,ID_SOUL,24,0,0,0,1);
6108 ammo_value(a->weapon->thingid,haa,&f0,&f1,&f2);
6109 na0 = (a->boss_count * a->boss->ammo_to_kill[0]) - (float)f0;
6110 na1 = (a->boss_count * a->boss->ammo_to_kill[1]) - (float)f1;
6111 na2 = (a->boss_count * a->boss->ammo_to_kill[2]) - (float)f2;
6112 ammo_value(a->ammo->thingid,haa,&f0,&f1,&f2);
6113 for (;mask;) {
6114 if (!place_object_in_region(l,minx,a->miny,maxx,a->maxy,
6115 c,a->ammo->thingid,24,0,0,0,mask))
6116 if (!place_object_in_region(l,minx,a->miny,maxx,a->maxy,
6117 c,a->ammo->thingid,1,0,0,0,mask))
6118 announce(ERROR,"No room for important ammo!");
6119 na0 -= (float)f0;
6120 na1 -= (float)f1;
6121 na2 -= (float)f2;
6122 if (na0<=0) mask &= ~0x01;
6123 if (na1<=0) mask &= ~0x02;
6124 if (na2<=0) mask &= ~0x04;
6125 }
6126 if (a->props&ARENA_NUKAGE) { /* Little stub health */
6127 place_object_in_region(l,minx,a->miny,maxx,a->maxy,
6128 c,ID_MEDIKIT,16,0,0,0,7);
6129 }
6130
6131 }
6132
6133 /* Make some decorations, ponds, cover, and so on in */
6134 /* the arena. */
arena_decor(level * l,arena * a,haa * haa,config * c)6135 void arena_decor(level *l,arena *a,haa *haa,config *c)
6136 {
6137 /* STUB; just a pillar in the center */
6138 int cx,cy,xmult,ymult,zmult;
6139 sector *newsec;
6140 texture *tm;
6141 linedef *ld1, *ld2, *ld3, *ld4;
6142
6143 if (rollpercent(173,25)) {
6144 xmult=1;
6145 ymult=1;
6146 zmult=1;
6147 } else {
6148 xmult = ( (a->maxx - a->minx) / 3 - 128 ) / 128;
6149 xmult = 1 + roll(97,xmult);
6150 ymult = ( (a->maxy - a->miny) - 128 ) / 128;
6151 ymult = 1 + roll(98,ymult);
6152 zmult = 1 + roll(99,3);
6153 }
6154
6155 if ( (128*zmult) > (a->innersec->ceiling_height - a->innersec->floor_height) )
6156 zmult = 1;
6157
6158 newsec = clone_sector(l,a->innersec);
6159 newsec->floor_height = (short)(a->innersec->floor_height+zmult*128);
6160 cx = (a->minx + a->maxx ) / 2 - 64 * xmult;
6161 cy = (a->miny + a->maxy ) / 2 - 64 * ymult;
6162 if ((a->innersec->style->plaque->props & VTILES)||(zmult==1)) {
6163 tm = a->innersec->style->plaque;
6164 } else {
6165 tm = a->innersec->style->support0;
6166 }
6167 parallel_innersec_ex(l,a->innersec,newsec,
6168 c->null_texture,a->innersec->style->wall0,tm,
6169 cx,cy,cx+128*xmult,cy+128*ymult,c,
6170 &ld1,&ld2,&ld3,&ld4);
6171 ld1->flags &= ~LOWER_UNPEGGED;
6172 ld2->flags &= ~LOWER_UNPEGGED;
6173 ld3->flags &= ~LOWER_UNPEGGED;
6174 ld4->flags &= ~LOWER_UNPEGGED;
6175 if ((a->props&ARENA_LAMPS)&&rollpercent(174,50)) {
6176 genus *lamp;
6177 lamp = a->innersec->style->lamp0;
6178 if ( (a->innersec->ceiling_flat != c->sky_flat) &
6179 (lamp->height < (a->innersec->ceiling_height - newsec->floor_height)))
6180 lamp = a->innersec->style->shortlamp0;
6181 if ( (a->innersec->ceiling_flat != c->sky_flat) &
6182 (lamp->height < (a->innersec->ceiling_height - newsec->floor_height)))
6183 lamp = NULL;
6184 if (lamp) {
6185 new_thing(l,cx+16,cy+16,0,lamp->thingid,7,c);
6186 new_thing(l,cx+16,cy+128*ymult-16,0,lamp->thingid,7,c);
6187 new_thing(l,cx+128*xmult-16,cy+128*ymult-16,0,lamp->thingid,7,c);
6188 new_thing(l,cx+128*xmult-16,cy+16,0,lamp->thingid,7,c);
6189 if (newsec->light_level <= l->lit_light_level)
6190 newsec->light_level += 20;
6191 }
6192 }
6193 }
6194
6195 /* Put down the main enemy for the arena, his structures, */
6196 /* and whatever ending gates and/or switches are needed. */
arena_boss(level * l,arena * a,haa * haa,config * c)6197 void arena_boss(level *l,arena *a,haa *haa,config *c)
6198 {
6199 /* STUB */
6200 int cx,cy;
6201 short facing;
6202 boolean need_switch = TRUE;
6203
6204 cx = a->minx + 5 * ( a->maxx - a->minx ) / 6;
6205 cy = (a->miny + a->maxy ) / 2;
6206 facing = facing_along(cx,cy,a->innersec->entry_x,a->innersec->entry_y);
6207 new_thing(l,cx,cy,facing,a->boss->thingid,7,c);
6208 if (a->boss_count>1) /* Only 1 and 2 supported! */
6209 new_thing(l,cx,cy-(a->boss->width+8),facing,a->boss->thingid,7,c);
6210
6211 if ((c->episode==2)&&(c->mission==8)) need_switch = FALSE;
6212 if ((c->episode==3)&&(c->mission==8)) need_switch = FALSE;
6213 if (((c->episode==4)&&(c->mission==8))||(c->map==7)) {
6214 linedef *ld1, *ld2, *ld3, *ld4;
6215 sector *newsec;
6216 need_switch = FALSE;
6217 cx -= 32;
6218 cx &= ~(63);
6219 cy += a->boss->width+72;
6220 cy &= ~(63);
6221 newsec = new_sector(l,(short)(a->innersec->floor_height+64),
6222 a->innersec->ceiling_height,
6223 random_gate(c,a->innersec->style),
6224 a->innersec->ceiling_flat);
6225 newsec->style = a->innersec->style;
6226 newsec->light_level = 250;
6227 newsec->special = GLOW_BLINK;
6228 newsec->tag = 666;
6229 parallel_innersec_ex(l,a->innersec,newsec,
6230 NULL,NULL,a->innersec->style->wall0,
6231 cx,cy,cx+64,cy+64,c,
6232 &ld1,&ld2,&ld3,&ld4);
6233 ld1->type = LINEDEF_W1_END_LEVEL;
6234 ld1->flags &= ~LOWER_UNPEGGED;
6235 ld2->type = LINEDEF_W1_END_LEVEL;
6236 ld2->flags &= ~LOWER_UNPEGGED;
6237 ld3->type = LINEDEF_W1_END_LEVEL;
6238 ld3->flags &= ~LOWER_UNPEGGED;
6239 ld4->type = LINEDEF_W1_END_LEVEL;
6240 ld4->flags &= ~LOWER_UNPEGGED;
6241 }
6242 if ((c->episode==1)&&(c->mission==8)) {
6243 linedef *ld1, *ld2, *ld3, *ld4;
6244 sector *newsec;
6245 short tag = death_room(l,NULL,a->innersec->style,c);
6246 if (tag) {
6247 need_switch = FALSE;
6248 cx -= 32;
6249 cx &= ~(63);
6250 cy += a->boss->width+72;
6251 cy &= ~(63);
6252 newsec = new_sector(l,(short)(a->innersec->floor_height+64),
6253 a->innersec->ceiling_height,
6254 random_gate(c,a->innersec->style),
6255 a->innersec->ceiling_flat);
6256 newsec->style = a->innersec->style;
6257 newsec->light_level = 250;
6258 newsec->special = GLOW_BLINK;
6259 newsec->tag = 666;
6260 parallel_innersec_ex(l,a->innersec,newsec,
6261 NULL,NULL,a->innersec->style->wall0,
6262 cx,cy,cx+64,cy+64,c,
6263 &ld1,&ld2,&ld3,&ld4);
6264 ld1->type = LINEDEF_TELEPORT;
6265 ld1->tag = tag;
6266 ld1->flags &= ~LOWER_UNPEGGED;
6267 ld2->type = LINEDEF_TELEPORT;
6268 ld2->tag = tag;
6269 ld2->flags &= ~LOWER_UNPEGGED;
6270 ld3->type = LINEDEF_TELEPORT;
6271 ld3->tag = tag;
6272 ld3->flags &= ~LOWER_UNPEGGED;
6273 ld4->type = LINEDEF_TELEPORT;
6274 ld4->tag = tag;
6275 ld4->flags &= ~LOWER_UNPEGGED;
6276 }
6277 }
6278
6279 if (need_switch) {
6280 linedef *ld;
6281 texture *tm;
6282 cx -= 64;
6283 cy += a->boss->width+8;
6284 parallel_innersec_ex(l,a->innersec,NULL,
6285 a->innersec->style->wall0,NULL,NULL,
6286 cx,cy,cx+128,cy+128,c,
6287 NULL,NULL,&ld,NULL);
6288 /* This next line is a painful hack to get the switch recessed; */
6289 /* install_switch uses empty_left_side(), which doesn't grok */
6290 /* nested enclosing sectors, sigh! */
6291 a->outersec->marked = 1;
6292 ld = install_switch(l,ld,TRUE,FALSE,0,a->innersec->style,c,NULL);
6293 a->outersec->marked = 0;
6294 ld->type = LINEDEF_S1_END_LEVEL;
6295 tm = random_texture0(EXITSWITCH,c,a->innersec->style);
6296 if (tm) {
6297 ld->right->middle_texture = tm;
6298 ld->right->y_offset = tm->y_bias;
6299 }
6300 if (ld->right->sector != a->innersec)
6301 ld->right->sector->ceiling_flat = a->innersec->style->ceiling0;
6302 }
6303 }
6304
6305 /* Gate out to a big place to fight bosses. */
6306 /* NOTE: this renders the haa invalid at the moment, and so can only */
6307 /* be used in an Episode 8, or the end of a PWAD. */
arena_gate(level * l,sector * s,haa * haa,config * c)6308 void arena_gate(level *l,sector *s,haa *haa,config *c)
6309 {
6310 arena *ThisArena = new_arena(l,c);
6311
6312 /* Put in an exit-style outgoing gate */
6313 s->gate = new_gate(l,0,new_tag(l),0,FALSE,c);
6314 ThisArena->fromtag = s->gate->out_tag;
6315 install_gate(l,s,s->style,haa,FALSE,c); /* Don't want EXIT style, eh? */
6316 s->middle_enhanced = TRUE;
6317
6318 /* Now put down some powerups and stuff in s... */
6319 prepare_arena_gate(l,s,ThisArena,haa,c);
6320
6321 /* Make the arena... */
6322 rng_set_level(99,99,99,c->ranseed,c->minrooms);
6323
6324 install_arena(l,ThisArena,s,haa,c);
6325
6326 arena_decor(l,ThisArena,haa,c);
6327
6328 arena_arrival(l,ThisArena,haa,c);
6329
6330 arena_boss(l,ThisArena,haa,c);
6331
6332 /* and we're done */
6333 announce(VERBOSE,"Arena");
6334 return;
6335
6336 }
6337
6338 /* A room that has a big well in the center that eventually rises */
6339 /* while you fight the awful monsters with the big teeth. */
rising_room(level * l,sector * s,config * c,haa * haa,quest * ThisQuest)6340 boolean rising_room(level *l,sector *s,config *c,haa *haa,quest *ThisQuest)
6341 {
6342 int minx, miny, maxx, maxy;
6343 int xborder, yborder, depth;
6344 sector *newsec;
6345 boolean did_trigger = FALSE;
6346 linedef *ld1,*ld2,*ld3,*ld4;
6347 thing *t;
6348 short tid = rollpercent(175,50) ? ID_POTION : ID_HELMET;
6349
6350 if (s->gate) return FALSE;
6351
6352 /* Make sure nice and huge */
6353 find_rec(l,s,&minx,&miny,&maxx,&maxy);
6354 if (maxx-minx<320) return FALSE;
6355 if (maxy-miny<320) return FALSE;
6356
6357 xborder = (64 + roll(100,(maxx-minx)-320)) / 2;
6358 yborder = (64 + roll(101,(maxy-miny)-320)) / 2;
6359 switch(roll(102,3)) {
6360 case 0: depth = 256; break;
6361 case 1: depth = 256 + 32 * roll(103,33); break;
6362 default: depth = 256 + 32 * roll(104,13); break;
6363 }
6364 newsec = clone_sector(l,s);
6365 newsec->floor_height -= depth;
6366 if (newsec->light_level>160) newsec->light_level = 160;
6367 newsec->tag = new_tag(l);
6368 parallel_innersec_ex(l,s,newsec,NULL,s->style->wall0,s->style->support0,
6369 minx+xborder,miny+yborder,maxx-xborder,maxy-yborder,
6370 c,&ld1,&ld2,&ld3,&ld4);
6371 s->middle_enhanced = TRUE;
6372
6373 /* Point the right sides into the well, for pushing and find_rec */
6374 flip_linedef(ld1);
6375 flip_linedef(ld2);
6376 flip_linedef(ld3);
6377 flip_linedef(ld4);
6378
6379 if (ThisQuest->goal == KEY_GOAL) {
6380 t = new_thing(l,(minx+maxx)/2,(miny+maxy)/2,0,ThisQuest->type,7,c);
6381 ThisQuest->thing = t; /* For later */
6382 if (ThisQuest->auxtag==0)
6383 if (!(c->gamemask&DOOM0_BIT))
6384 if (rollpercent(176,80)) {
6385 did_trigger = TRUE;
6386 trigger_box(l,t,newsec,newsec->tag,LINEDEF_W1_RAISE_FLOOR,c);
6387 announce(VERBOSE,"Zlooty");
6388 }
6389 }
6390
6391 if (ThisQuest->goal == NULL_GOAL) {
6392 ThisQuest->thing = place_required_small_pickable(l,newsec,c);
6393 if (ThisQuest->auxtag==0)
6394 if (!(c->gamemask&DOOM0_BIT))
6395 if (rollpercent(177,50)) {
6396 t = new_thing(l,(minx+maxx)/2,(miny+maxy)/2,0,tid,7,c);
6397 did_trigger = TRUE;
6398 trigger_box(l,t,newsec,newsec->tag,LINEDEF_W1_RAISE_FLOOR,c);
6399 }
6400 }
6401
6402 if (!did_trigger) {
6403 ld1->type = LINEDEF_S1_RAISE_FLOOR;
6404 ld1->tag = newsec->tag;
6405 ld2->type = LINEDEF_S1_RAISE_FLOOR;
6406 ld2->tag = newsec->tag;
6407 ld3->type = LINEDEF_S1_RAISE_FLOOR;
6408 ld3->tag = newsec->tag;
6409 ld4->type = LINEDEF_S1_RAISE_FLOOR;
6410 ld4->tag = newsec->tag;
6411 }
6412
6413 /* Put a couple of things on the ledge */
6414 if (rollpercent(178,30)) place_timely_something(l,haa,c,minx+16,miny+16+roll(105,(maxy-miny)-31));
6415 if (rollpercent(179,30)) place_timely_something(l,haa,c,maxx-16,miny+16+roll(106,(maxy-miny)-31));
6416 if (rollpercent(180,30)) place_timely_something(l,haa,c,minx+16+roll(107,(maxx-minx)-31),miny+16);
6417 if (rollpercent(181,30)) place_timely_something(l,haa,c,minx+16+roll(108,(maxx-minx)-31),maxy-16);
6418
6419 /* Now populate the well */
6420 populate(l,newsec,c,haa,FALSE);
6421
6422 if (rollpercent(182,20)) {
6423 newsec->floor_flat = c->water_flat; /* Eli's idea */
6424 announce(VERBOSE,"Water");
6425 }
6426
6427 announce(VERBOSE,"Rising room");
6428
6429 return TRUE;
6430
6431 }
6432
6433
6434 /* Put whatever's required by this quest into this sector */
close_quest(level * l,sector * s,quest * q,haa * haa,config * c)6435 void close_quest(level *l,sector *s,quest *q,haa *haa,config *c)
6436 {
6437 linedef *ld;
6438 int i, j;
6439 thing *t;
6440 texture *tm;
6441 boolean done = FALSE;
6442
6443 s->has_key = TRUE;
6444
6445 switch (q->goal) {
6446 case SWITCH_GOAL:
6447 /* Could decide to use a walkthrough linedef or whatever */
6448 /* instead of a switch for a tag goal. */
6449 i = mark_decent_boundary_linedefs(l,s,64);
6450 ld = random_marked_linedef(l,i);
6451 unmark_linedefs(l);
6452 if (ld==NULL) {
6453 announce(ERROR,"No applicable linedef to put switch on!");
6454 } else {
6455 ld = install_switch(l,ld,c->recess_switches,FALSE,0,s->style,c,NULL);
6456 ld->type = q->type;
6457 ld->tag = q->tag;
6458 }
6459 /* Maybe a potion or whatever for surprises. Always?? */
6460 t = place_required_small_pickable(l,s,c);
6461 q->thing = t; /* For later */
6462 break;
6463 case LEVEL_END_GOAL:
6464 /* Just alter some non-tiny boundary linedef to be a switch, */
6465 /* and try to make it obvious via some light stuff. Or, */
6466 /* sometimes, do the floor-hole thing, or a gate, or... */
6467 i = mark_decent_boundary_linedefs(l,s,64);
6468 for (j=0;j<5;j++) { /* Try a little harder to get recessible! */
6469 ld = random_marked_linedef(l,i);
6470 if (empty_left_side(l,ld,16)) break;
6471 }
6472 unmark_linedefs(l);
6473 if (ld==NULL) {
6474 announce(ERROR,"No applicable linedef to end level on!");
6475 } else {
6476 if ((c->episode==1)&&(c->mission==8)) { /* Try a fun thing! */
6477 if (e1m8_gate(l,ld,s,haa,c)) {
6478 announce(VERBOSE,"e1m8 finale");
6479 break;
6480 }
6481 }
6482 if (rollpercent(183,c->p_hole_ends_level)) { /* Try a floor-hole */
6483 if (empty_left_side(l,ld,128)) { /* 128? Hugeness? */
6484 if (linelen(ld)>192) split_linedef(l,ld,128,c);
6485 lefthand_box(l,ld,128,s->style,c)->right->middle_texture =
6486 s->style->wall0;
6487 ld->type = LINEDEF_W1_END_LEVEL;
6488 if (ld->left->sector->light_level<160)
6489 ld->left->sector->light_level=160;
6490 ld->left->sector->floor_flat = c->sky_flat;
6491 ld->left->sector->floor_height -= 16;
6492 announce(VERBOSE,"Hole ends level");
6493 done = TRUE;
6494 }
6495 }
6496 if ((!done)&&(s->gate==NULL)&&rollpercent(184,c->p_gate_ends_level)
6497 &&l->use_gates) {
6498 /* Do an exit gate */
6499 s->gate = new_gate(l,0,0,0,TRUE,c);
6500 install_gate(l,s,s->style,haa,FALSE,c);
6501 gate_populate(l,s,haa,FALSE,c); /* Some stuff */
6502 s->middle_enhanced = TRUE;
6503 if (s->light_level>130) s->light_level=130; /* To see "EXIT" */
6504 announce(VERBOSE,"Gate ends level");
6505 done = TRUE;
6506 }
6507 /* Switch with recess, sometimes fancied-up */
6508 if (!done) {
6509 ld = install_switch(l,ld,TRUE,rollpercent(185,10),0,s->style,c,NULL);
6510 ld->type = q->type;
6511 ld->tag = q->tag; /* Will be zero, actually */
6512 ld->right->sector->special = GLOW_BLINK;
6513 if (s->light_level>190) s->light_level = 190; /* So the glow shows */
6514 ld->right->sector->light_level = 255;
6515 done = TRUE;
6516 tm = random_texture0(EXITSWITCH,c,s->style);
6517 if (tm) {
6518 ld->right->middle_texture = tm;
6519 ld->right->y_offset = tm->y_bias;
6520 announce(VERBOSE,"Custom exit switch");
6521 }
6522 }
6523 if (need_secret_level(c) && !l->sl_done &&!l->sl_tag) {
6524 /* This sets sl_done if it works */
6525 install_sl_exit(l,s,haa,s->style,q,TRUE,c);
6526 }
6527 }
6528 break;
6529 case ARENA_GOAL:
6530 /* A teleporter to a big arena in which to fight bosses */
6531 arena_gate(l,s,haa,c);
6532 break;
6533 case GATE_GOAL:
6534 /* A teleporter to, and perhaps from, the goal room. */
6535 s->gate = new_gate(l,q->tag2,q->tag,0,FALSE,c);
6536 break;
6537 case KEY_GOAL:
6538 if (rollpercent(186,l->p_rising_room)) {
6539 done = rising_room(l,s,c,haa,q);
6540 }
6541 if (!done) { /* Simple case */
6542 t = place_required_pickable(l,s,c,q->type);
6543 q->thing = t; /* For later */
6544 }
6545 break;
6546 case NULL_GOAL:
6547 if (rollpercent(187,2*l->p_rising_room)) {
6548 done = rising_room(l,s,c,haa,q);
6549 }
6550 if (!done) { /* Simple case; potion or whatever for surprises */
6551 t = place_required_small_pickable(l,s,c);
6552 q->thing = t; /* For later */
6553 }
6554 break;
6555 default:
6556 announce(ERROR,"Unfamiliar goal type; quest not ended.");
6557 }
6558 return;
6559 }
6560
6561 /* Consider "pushing" the current quest, which really means */
6562 /* putting its goal right here, but guarding it with something */
6563 /* that still has to be quested for. */
maybe_push_quest(level * l,sector * s,quest * q,config * c)6564 void maybe_push_quest(level *l,sector *s,quest *q,config *c)
6565 {
6566 short newkey;
6567 linedef *ld;
6568 short locked_linedef_type;
6569 int i;
6570
6571 if (!rollpercent(188,c->p_pushquest)) return; /* Do we want to? */
6572 if (q->goal!=SWITCH_GOAL) return; /* Do we know how? */
6573
6574 newkey = new_key(l); /* Get an unused key */
6575 if (!newkey) return;
6576
6577 /* Figure out how to lock it */
6578 locked_linedef_type = locked_linedef_for(q->type,newkey,c);
6579 if (!locked_linedef_type) return;
6580
6581 /* Find a linedef to install the switch on */
6582 i = mark_decent_boundary_linedefs(l,s,64);
6583 ld = random_marked_linedef(l,i);
6584 unmark_linedefs(l);
6585 if (ld==NULL) return;
6586
6587 /* Install the switch and hook it up */
6588 ld = install_switch(l,ld,TRUE,rollpercent(189,50),newkey,s->style,c,NULL);
6589 ld->type = locked_linedef_type;
6590 ld->tag = q->tag;
6591
6592 /* Now modify the quest */
6593 q->goal = KEY_GOAL;
6594 q->type = newkey;
6595 q->tag = 0;
6596
6597 /* and we're done... */
6598 announce(LOG,"Quest push");
6599 return;
6600 }
6601
6602 /* Construct a linedef suitable for a generate_room_outline for */
6603 /* the next room. If old is not NULL, re-use it, just changing */
6604 /* its to and from. */
6605 /* For most links this will be an antiparallel linedef on the left */
6606 /* side of this one. */
make_linkto(level * l,linedef * ld,link * ThisLink,style * ThisStyle,config * c,linedef * old)6607 linedef *make_linkto(level *l,linedef *ld,link *ThisLink, style *ThisStyle,
6608 config *c, linedef *old)
6609 {
6610 int depth;
6611
6612 switch(ThisLink->type) {
6613 case BASIC_LINK:
6614 depth = 0;
6615 /* Account for any recesses */
6616 if (ThisLink->bits&LINK_RECESS) depth += 2*ThisLink->depth2;
6617 /* Account for single door/arch, if any */
6618 if (!(ThisLink->bits&(LINK_CORE|LINK_ALCOVE))) depth += ThisLink->depth1;
6619 /* Account for double doors around the core, if any */
6620 if ( ((ThisLink->bits&LINK_CORE)) && (ThisLink->bits&LINK_NEAR_DOOR) )
6621 depth += ThisLink->depth1;
6622 if ( ((ThisLink->bits&LINK_CORE)) && (ThisLink->bits&LINK_FAR_DOOR) )
6623 depth += ThisLink->depth1;
6624 /* Alcove width */
6625 if (ThisLink->bits&LINK_ALCOVE) {
6626 depth += ThisLink->width2;
6627 } else { /* Straight-through core */
6628 if (ThisLink->bits&LINK_CORE) depth += ThisLink->depth3;
6629 }
6630 break;
6631 case OPEN_LINK:
6632 depth = ThisLink->depth1;
6633 break;
6634 case GATE_LINK: {
6635 linedef *ldnew;
6636 vertex *v, *v1;
6637 int newsize;
6638 int minx = HUGE_NUMBER;
6639 for (v=l->vertex_anchor;v;v=v->next) if (v->x < minx) minx = v->x;
6640 minx -= 64;
6641 if (ld) {
6642 newsize = linelen(ld);
6643 } else {
6644 newsize = 512;
6645 }
6646 if (newsize< 256 * l->hugeness) newsize = 256 * l->hugeness;
6647 if (old) {
6648 old->from->x = minx;
6649 old->to->x = minx;
6650 old->from->y = newsize/2;
6651 old->to->y = 0-newsize/2;
6652 ldnew = old;
6653 } else {
6654 v = new_vertex(l,minx,newsize/2);
6655 v1 = new_vertex(l,minx,0-newsize/2);
6656 ldnew = new_linedef(l,v,v1);
6657 }
6658 return ldnew;
6659 }
6660 default:
6661 announce(ERROR,"Funny linktype in make_linkto.");
6662 depth = ThisLink->depth1;
6663 }
6664 return flip_linedef(make_parallel(l,ld,depth,old));
6665 }
6666
6667 /* Given two antiparallel linedefs, does there seem to be an empty */
6668 /* rectangle between their left sides, or whatever else this link */
6669 /* needs? */
link_fitsv(level * l,linedef * ldf1,linedef * ldf2,link * ThisLink)6670 boolean link_fitsv(level *l,linedef *ldf1, linedef *ldf2, link *ThisLink)
6671 {
6672 boolean answer;
6673
6674 if (ThisLink->type==GATE_LINK) return TRUE; /* These don't care */
6675
6676 ldf1->from->marked = TRUE;
6677 ldf1->to->marked = TRUE;
6678 ldf2->from->marked = TRUE;
6679 ldf2->to->marked = TRUE;
6680 if (ldf1->right) ldf1->right->sector->marked = 1;
6681 if (ldf2->right) ldf2->right->sector->marked = 1;
6682 answer = empty_rectangle(l,ldf1->from->x,ldf1->from->y,
6683 ldf1->to->x,ldf1->to->y,
6684 ldf2->from->x,ldf2->from->y,
6685 ldf2->to->x,ldf2->to->y);
6686 if (ldf1->right) ldf1->right->sector->marked = 0;
6687 if (ldf2->right) ldf2->right->sector->marked = 0;
6688 ldf1->from->marked = FALSE;
6689 ldf1->to->marked = FALSE;
6690 ldf2->from->marked = FALSE;
6691 ldf2->to->marked = FALSE;
6692 return answer;
6693 }
6694
mid_tile(level * l,sector * s,short * tlx,short * tly,short * thx,short * thy)6695 void mid_tile(level *l, sector *s,
6696 short *tlx, short *tly, short *thx, short *thy)
6697 {
6698 int minx,miny,maxx,maxy;
6699
6700 find_rec(l,s,&minx,&miny,&maxx,&maxy);
6701 *tlx = (minx + maxx) / 2;
6702 *tlx = *tlx & 0xFFC0; /* Round down to 64; down? */
6703 if (*tlx<=minx) *tlx = minx + 1;
6704 *tly = (miny + maxy) / 2;
6705 *tly = *tly & 0xFFC0; /* Round down to 64; down? */
6706 if (*tly<=miny) *tly = miny + 1;
6707 *thx = *tlx + 64;
6708 if (*thx>=maxx) *thx = maxx - 1;
6709 *thy = *tly + 64;
6710 if (*thy>=maxy) *thy = maxy - 1;
6711 }
6712
6713 /* Is it OK to obstruct the middle (as defined by mid_tile()) tile */
6714 /* of this sector? i.e. might it block a way or a door? */
ok_to_block_mid_tile(level * l,sector * s)6715 boolean ok_to_block_mid_tile(level *l, sector *s)
6716 {
6717 short tlx, tly, thx, thy;
6718 int minx, miny, maxx, maxy;
6719
6720 find_rec(l,s,&minx,&miny,&maxx,&maxy);
6721 mid_tile(l,s,&tlx,&tly,&thx,&thy);
6722 /* Very strong rectangle assumptions here! */
6723 if (tlx-minx<33) return FALSE;
6724 if (tly-miny<33) return FALSE;
6725 if (maxx-thx<33) return FALSE;
6726 if (maxy-thy<33) return FALSE;
6727 return TRUE;
6728 }
6729
6730 /* Given a bare linedef, make a room extending from its right side. */
generate_room_outline(level * l,linedef * ld,style * ThisStyle,boolean try_reduction,config * c)6731 sector *generate_room_outline(level *l,linedef *ld,style *ThisStyle,
6732 boolean try_reduction,config *c)
6733 {
6734 sector *answer;
6735 linedef *newld;
6736 vertex *v1,*v2;
6737 int x1,y1,x2,y2,len1,len2;
6738
6739 /* Very simple squarish rooms */
6740 len1 = linelen(ld);
6741 if (roll(109,2)) {
6742 len2 = len1;
6743 } else {
6744 len2 = len1 + l->hugeness * 64 * (4-roll(110,9));
6745 }
6746 if (len2<128) {
6747 len2 = 128;
6748 } else if (len2>1600) {
6749 len2 = 1600;
6750 }
6751
6752 /* We need to make rooms bigger on levels with teleports - SET */
6753 if(l->use_gates && len2<TELEPORT_MINROOMSIZE) {
6754 len2 = TELEPORT_MINROOMSIZE;
6755 }
6756
6757 /* Bigify. */
6758 if (!try_reduction) /* Not if we're constrained */
6759 if (rollpercent(190,c->p_bigify))
6760 if (len2<512) len2 *= 2; /* Keep 'em big! */
6761
6762 for (;;) { /* Until we find one that fits */
6763 point_from(ld->from->x,ld->from->y,ld->to->x,ld->to->y,
6764 RIGHT_TURN,len2,&x1,&y1);
6765 point_from(ld->to->x,ld->to->y,x1,y1,RIGHT_TURN,len1,&x2,&y2);
6766 ld->from->marked = 1;
6767 ld->to->marked = 1;
6768 if (empty_rectangle(l,ld->from->x,ld->from->y,ld->to->x,ld->to->y,
6769 x1,y1,x2,y2)) break;
6770 if (!try_reduction) return NULL;
6771 len2 -= 32; /* If failed, try again with smaller room */
6772 if (len2<(l->hugeness*64)) {
6773 announce(VERBOSE,"No possible rectangle fits in the space.");
6774 ld->to->marked=0;
6775 ld->from->marked=0;
6776 return NULL;
6777 }
6778 }
6779 ld->to->marked = 0;
6780 ld->from->marked = 0;
6781
6782 printf(".");
6783 fflush(stdout);
6784 {
6785 char s[200];
6786 sprintf(s,"New room, corners (%d %d) (%d %d) (%d %d) (%d %d).",
6787 ld->from->x,ld->from->y,ld->to->x,ld->to->y,x1,y1,x2,y2);
6788 announce(VERBOSE,s);
6789 }
6790
6791 answer = new_sector(l,0,ThisStyle->wallheight0,
6792 ThisStyle->floor0, ThisStyle->ceiling0);
6793 answer->style = ThisStyle;
6794 answer->light_level = ThisStyle->roomlight0; /* paint_room can override? */
6795 ld->right = new_sidedef(l,answer,c);
6796
6797 v1 = ld->to;
6798 v2 = new_vertex(l,x1,y1);
6799 newld = new_linedef(l,v1,v2);
6800 newld->right = new_sidedef(l,answer,c);
6801 /* If the wall is long, sometimes split it, for more outlinks */
6802 /* Should use styles here and stuff too (and config) */
6803 if (linelen(newld)>(l->hugeness*256))
6804 if (rollpercent(191,25))
6805 split_linedef(l,newld,linelen(newld)/2,c);
6806
6807 v1 = v2;
6808 v2 = new_vertex(l,x2,y2);
6809 newld = new_linedef(l,v1,v2);
6810 newld->right = new_sidedef(l,answer,c);
6811 if (linelen(newld)>(l->hugeness*256))
6812 if (rollpercent(192,25))
6813 split_linedef(l,newld,linelen(newld)/2,c);
6814
6815 v1 = v2;
6816 v2 = ld->from;
6817 newld = new_linedef(l,v1,v2);
6818 newld->right = new_sidedef(l,answer,c);
6819 if (linelen(newld)>(l->hugeness*256))
6820 if (rollpercent(193,25))
6821 split_linedef(l,newld,linelen(newld)/2,c);
6822
6823 return answer;
6824 }
6825
6826 /* Return a random link that will fit on this linedef, */
6827 /* and that can be locked for this quest (if any). */
6828 /* Note that ld can be NULL, meaning "don't worry about it" */
random_link(level * l,linedef * ld,style * ThisStyle,quest * ThisQuest,config * c)6829 link *random_link(level *l,linedef *ld,style *ThisStyle,quest *ThisQuest,
6830 config *c)
6831 {
6832 link *answer = NULL;
6833 boolean open_ok = TRUE;
6834
6835 if (ld) if (linelen(ld)<100) open_ok = FALSE;
6836 if (ThisQuest)
6837 if (ThisQuest->goal==KEY_GOAL)
6838 open_ok = FALSE;
6839
6840 if (l->use_gates)
6841 if (ThisQuest)
6842 if (rollpercent(194,20)) /* Should vary */
6843 if (ThisQuest->goal==SWITCH_GOAL)
6844 if (!(ld->right->sector->gate))
6845 if (ld->right->sector != l->first_room)
6846 if(linelen(ld) > 1000)
6847 if (ok_to_block_mid_tile(l,ld->right->sector))
6848 if (!(c->do_dm))
6849 return gate_link(l,c); /* Already in link_anchor */
6850
6851 if (answer==NULL) {
6852 if (rollpercent(195,l->p_open_link)&&open_ok) {
6853 answer = random_open_link(l,ld,ThisStyle,ThisQuest,c);
6854 } else {
6855 answer = random_basic_link(l,ld,ThisStyle,ThisQuest,c);
6856 }
6857 }
6858
6859 answer->next = l->link_anchor;
6860 l->link_anchor = answer;
6861 return answer;
6862 }
6863
6864 /* Return a random open link that will fit on this linedef */
6865 /* Note that ld can be NULL, meaning "don't worry about it" */
random_open_link(level * l,linedef * ld,style * ThisStyle,quest * ThisQuest,config * c)6866 link *random_open_link(level *l,linedef *ld,style *ThisStyle,quest *ThisQuest,
6867 config *c)
6868 {
6869 int dieroll, len = 0;
6870 link *answer = (link *)malloc(sizeof (*answer));
6871
6872 answer->bits = 0;
6873 answer->type = OPEN_LINK;
6874
6875 if ((ThisQuest==NULL)&&rollpercent(196,40)) { /* 40 should vary */
6876 answer->bits |= LINK_LIFT;
6877 } else {
6878 answer->bits |= LINK_STEPS;
6879 if (rollpercent(197,30)) answer->bits |= LINK_ALCOVE; /* sidesteps */
6880 if (rollpercent(198,50)) answer->bits |= LINK_LEFT;
6881 }
6882
6883 if (ld) len = linelen(ld);
6884
6885 /* Primary width; need more variety! */
6886 dieroll = roll(111,100);
6887 if (dieroll<35) {
6888 answer->width1 = 64 * l->hugeness;
6889 } else if (dieroll<70) {
6890 answer->width1 = 128 * l->hugeness;
6891 } else {
6892 answer->width1 = 0; /* Means "about a third of the wall" */
6893 }
6894
6895 if (ld) if (answer->width1+66>len) answer->width1 = 0;
6896
6897 /* We must have a minimum room size for levels which can have teleports
6898 - SET */
6899
6900 if(l->use_gates && answer->width1 < TELEPORT_MINROOMSIZE) {
6901 if(len > TELEPORT_MINROOMSIZE) { answer->width1 = TELEPORT_MINROOMSIZE; }
6902 else { answer->width1 = len; }
6903 }
6904
6905 /* Primary depth */
6906 if (answer->bits&LINK_LIFT) {
6907 answer->depth1 = l->hugeness * 32 * (1+roll(112,5));
6908 } else {
6909 if ( (answer->bits&LINK_ALCOVE) && rollpercent(199,50)) {
6910 answer->depth1 = l->hugeness * 32 * (1+roll(113,4));
6911 announce(VERBOSE,"Narrow side-steps?");
6912 } else {
6913 answer->depth1 = l->hugeness * 64 * (2+roll(114,5)); /* Or something */
6914 }
6915 }
6916 if (answer->depth1<33) answer->depth1 = 33;
6917
6918 /* Suggested height from new floor to existing ceiling */
6919 answer->height1 = l->hugeness * 16 * (2+roll(115,7));
6920
6921 return answer;
6922 }
6923
6924 /* Return a random basic link that will fit on this linedef */
6925 /* Note that ld can be NULL, meaning "don't worry about it" */
6926 /* This routine has grown like kudzu, and needs to be */
6927 /* heavily pruned and organized and fixed. */
random_basic_link(level * l,linedef * ld,style * ThisStyle,quest * ThisQuest,config * c)6928 link *random_basic_link(level *l,linedef *ld,style *ThisStyle,quest *ThisQuest,
6929 config *c)
6930 {
6931 link *answer;
6932 int dieroll;
6933 int len = 0;
6934 boolean need_door = FALSE;
6935 boolean nukage_core_trap = FALSE;
6936
6937 if (ld) len = linelen(ld);
6938
6939 answer = (link *)malloc(sizeof (*answer));
6940
6941 /* Should use style and config more here and there */
6942
6943 answer->type = BASIC_LINK;
6944 answer->bits = 0;
6945
6946 if (ThisQuest) {
6947 if (ThisQuest->goal==KEY_GOAL) need_door = TRUE;
6948 /* So far the only tags we know of are door-opens and nukage traps */
6949 if (ThisQuest->goal==SWITCH_GOAL)
6950 if (rollpercent(200,30)||rollpercent(440,l->p_force_nukage)) /* Huh? */
6951 need_door = TRUE;
6952 else nukage_core_trap = TRUE;
6953 }
6954
6955 /* Depth of the door sector, if any */
6956 if (rollpercent(201,50)) {
6957 answer->depth1 = 16;
6958 } else if (rollpercent(202,50)) {
6959 answer->depth1 = 8;
6960 } else if (rollpercent(203,50)) {
6961 answer->depth1 = 32;
6962 } else {
6963 answer->depth1 = 64; /* tunneldoor... */
6964 }
6965 answer->depth1 *= l->hugeness;
6966
6967 /* Stairs and lifts will change this walkable default */
6968 if (rollpercent(204,50)) {
6969 answer->floordelta = 0;
6970 } else {
6971 answer->floordelta = 24 - 8 * (roll(116,7));
6972 }
6973
6974 /* Primary width default */
6975 dieroll = roll(117,100);
6976 if (dieroll<50) {
6977 answer->width1 = 64;
6978 } else if (dieroll<60) {
6979 answer->width1 = 128;
6980 } else if (dieroll<80) {
6981 answer->width1 = 96;
6982 } else {
6983 answer->width1 = 0;
6984 }
6985 answer->width1 *= l->hugeness;
6986 if (ld)
6987 if (len<answer->width1)
6988 answer->width1 = 0;
6989 if (l->all_wide_links) answer->width1 = 0;
6990
6991 /* We must have a minimum room size for levels which can have teleports
6992 - SET */
6993
6994 if(l->use_gates && answer->width1 < TELEPORT_MINROOMSIZE) {
6995 if(len > TELEPORT_MINROOMSIZE) { answer->width1 = TELEPORT_MINROOMSIZE; }
6996 else { answer->width1 = len; }
6997 }
6998
6999 answer->height1 = ThisStyle->linkheight0; /* should vary some? */
7000 switch(roll(118,3)) { /* alcove depth -- needs more variety */
7001 case 0: answer->width2 = 64; break;
7002 case 1: answer->width2 = answer->width1; break;
7003 case 2: answer->width2 = 64 + 8 * (roll(119,17)); break;
7004 }
7005 answer->width2 *= l->hugeness;
7006 if (answer->width2==0) answer->width2 = 64 * l->hugeness;
7007 switch (roll(120,4)) { /* recess depth -- also */
7008 case 0: answer->depth2 = 8; break;
7009 case 1: answer->depth2 = 4; break;
7010 case 2: answer->depth2 = 16; break;
7011 case 3: answer->depth2 = 20; break;
7012 }
7013 if (rollpercent(205,10)) answer->depth2 *= 2; /* Nice and deep! */
7014 answer->depth2 *= l->hugeness;
7015
7016 /* In case they're needed for cores and stairs and stuff */
7017 answer->depth3 = 32 * (1+roll(121,5)) * l->hugeness;
7018 answer->stepcount = 2+roll(122,9);
7019
7020 dieroll = roll(123,100);
7021 /* <Half standard, >half whimsical, for now, for fun */
7022 /* Other numbers are also sorta high, but that gives variety */
7023 if (dieroll<30) { /* A standard thing of some kind */
7024 dieroll = roll(124,100);
7025 if (dieroll<20) { /* Nice recessed door */
7026 answer->bits = LINK_ANY_DOOR | LINK_RECESS;
7027 } else if (dieroll<65) { /* Nice arch */
7028 answer->bits = 0;
7029 } else { /* Simple stairs */
7030 answer->bits = LINK_CORE | LINK_STEPS;
7031 answer->depth3 *= 3;
7032 answer->floordelta = answer->stepcount * (2+roll(125,20));
7033 }
7034 } else { /* Make something up */
7035 answer->bits = 0;
7036 if (roll(126,2)) answer->bits |= LINK_RECESS;
7037 if (rollpercent(206,40)) {
7038 if (rollpercent(207,40) || c->both_doors) {
7039 answer->bits |= LINK_ANY_DOOR;
7040 } else if (rollpercent(208,30)) {
7041 answer->bits |= LINK_NEAR_DOOR;
7042 } else {
7043 answer->bits |= LINK_FAR_DOOR;
7044 }
7045 }
7046 if (rollpercent(209,10)) answer->bits |= LINK_BARS;
7047 if (answer->width1!=0) /* Twinning a full-wall link is ugly */
7048 if ( (!ld) || ( (len/2 - 16) > answer->width1) )
7049 if (rollpercent(210,30)) {
7050 answer->bits |= LINK_TWIN;
7051 if (rollpercent(211,60)) answer->bits |= LINK_WINDOW;
7052 }
7053 if (rollpercent(212,30)) answer->bits |= LINK_ALCOVE;
7054 if ( (ld) && ( (len/2 - 16) < answer->width1) )
7055 answer->bits &= ~LINK_ALCOVE;
7056 if ( (ld) && ( (len/4 - 32) < answer->width1) && (answer->bits&LINK_TWIN))
7057 answer->bits &= ~LINK_ALCOVE;
7058 if (rollpercent(213,40)) {
7059 answer->bits |= LINK_CORE;
7060 if (rollpercent(214,40)) {
7061 answer->bits |= LINK_STEPS;
7062 answer->depth3 *= 3;
7063 answer->floordelta = answer->stepcount * (2+roll(127,20));
7064 } else if (l->lift_rho&&!need_door) {
7065 answer->bits |= LINK_LIFT;
7066 if (!(answer->bits&LINK_ALCOVE))
7067 answer->bits &= ~LINK_ANY_DOOR; /* not currently compatible */
7068 if (rollpercent(215,50)) {
7069 answer->floordelta = 32 + 8 * roll(128,51); /* Potentially big */
7070 } else {
7071 answer->floordelta = 25 + 4 * roll(129,26); /* smaller */
7072 }
7073 if (answer->depth3<64) answer->depth3 = 64;
7074 }
7075 }
7076 }
7077 if (l->no_doors) answer->bits &= ~LINK_ANY_DOOR;
7078
7079 /* Make sure we have a door if we need one (to lock, etc) */
7080 if (need_door) answer->bits |= LINK_NEAR_DOOR;
7081
7082 /* Fewer unrecessed and/or really high doors */
7083 if (answer->bits|LINK_ANY_DOOR) {
7084 if (rollpercent(216,75)) answer->bits |= LINK_RECESS;
7085 if (rollpercent(217,75))
7086 if (answer->height1>72) answer->height1 = 72; /* Hugeness? */
7087 }
7088
7089 /* Sometimes up, sometimes down */
7090 if (roll(130,2)) answer->floordelta = 0 - answer->floordelta;
7091
7092 /* More random fun stuff */
7093 if (rollpercent(218,l->p_stair_lamps)) answer->bits |= LINK_LAMPS;
7094 if (rollpercent(219,50)) answer->bits |= LINK_MAX_CEILING;
7095 if (rollpercent(220,50)) answer->bits |= LINK_LEFT;
7096 if (rollpercent(221,75)) answer->bits |= LINK_FAR_TWINS;
7097 if (rollpercent(222,75)) answer->bits |= LINK_TRIGGERED;
7098 if (rollpercent(223,l->p_force_sky)||rollpercent(441,l->p_force_sky)||
7099 rollpercent(224,50)) answer->bits |= LINK_DECROOM; /* 50? */
7100
7101 /* If nukage_core_trap, override much of the above! */
7102 if (nukage_core_trap) {
7103 /* A relatively simple core */
7104 answer->bits &= ~(LINK_STEPS | LINK_ALCOVE | LINK_TWIN | LINK_LIFT);
7105 answer->bits |= LINK_CORE;
7106 /* At least 128 long */
7107 if (answer->depth3<128) answer->depth3 = 128;
7108 /* And going up a bit */
7109 answer->floordelta = 4 + roll(131,18);
7110 answer->bits |= LINK_LOCK_CORE;
7111 }
7112
7113 /* If a gate quest, override much of the above also. All we want */
7114 /* is a recessed archway-thing, walkable, etc, to make_window() on */
7115 if ( (ThisQuest) && (ThisQuest->goal == GATE_GOAL) ) {
7116 answer->bits &= ~(LINK_STEPS | LINK_ALCOVE | LINK_LIFT | LINK_CORE);
7117 answer->bits &= ~(LINK_ANY_DOOR | LINK_TRIGGERED);
7118 answer->bits |= LINK_RECESS;
7119 if (rollpercent(225,50)) {
7120 answer->floordelta = 0;
7121 } else {
7122 answer->floordelta = 24 - 8 * (roll(132,7));
7123 }
7124 }
7125
7126 /* Alcoves require either a door or a recess, and a non-whole width, */
7127 /* and for now at least a tiny core. */
7128 if (answer->bits&LINK_ALCOVE) {
7129 if (LINK_ANY_DOOR!=(answer->bits&LINK_ANY_DOOR)) {
7130 answer->bits |= LINK_RECESS;
7131 if (answer->depth2<(8*l->hugeness)) answer->depth2 = 8 * l->hugeness;
7132 }
7133 if (answer->width1==0) answer->width1 = 64 * l->hugeness;
7134 if (!(answer->bits&LINK_CORE)) {
7135 answer->bits |= LINK_CORE;
7136 answer->depth3 = 4 * l->hugeness;
7137 }
7138 }
7139
7140 /* Some final sanity checks on stair-sector heights and stuff */
7141 if (answer->bits&LINK_STEPS) {
7142 int need;
7143 /* The clearance we need is 56 plus the step height times */
7144 /* the number of steps our 64ish-wide shadow is on at once */
7145 /* (plus eight more in case of doors). Roughly! */
7146 need = 64 + (1+(64/(answer->depth3/(answer->stepcount)))) * abs(answer->floordelta / (answer->stepcount-1));
7147 if (answer->bits&LINK_ANY_DOOR) need += 8; /* Doors don't open all the way */
7148 if (answer->height1 < need) answer->height1 = need;
7149 } else if (!(answer->bits&LINK_LIFT)) {
7150 if (answer->height1+answer->floordelta<64) answer->height1 = 64 - answer->floordelta;
7151 if (answer->height1-answer->floordelta<64) answer->height1 = answer->floordelta + 64;
7152 }
7153
7154 /* From here on down all we do is turn off fancy bits that worry us, */
7155 /* or make sure core-depth isn't too small. */
7156
7157 /* Make sure we're not twinning/alcoving on a too-narrow linedef */
7158 /* Although this should all be covered above already */
7159 if (ld) {
7160 if (len<144) answer->bits &= ~(LINK_TWIN|LINK_ALCOVE);
7161 if (len<(2*answer->width1))
7162 answer->bits &= ~(LINK_TWIN|LINK_ALCOVE);
7163 if (answer->bits&LINK_ALCOVE)
7164 if (!link_fitsh(ld,answer,c)) /* Try the Official Checker! */
7165 answer->bits &= ~LINK_ALCOVE;
7166 }
7167
7168 if (answer->width1==0) answer->bits &= ~LINK_ALCOVE;
7169
7170 if ( (answer->bits&LINK_LIFT) && (!(answer->bits&LINK_ALCOVE)))
7171 answer->bits &= ~LINK_ANY_DOOR; /* not currently compatible */
7172
7173 /* Only make a window if not too much floordelta */
7174 if (answer->floordelta+16>ThisStyle->sillheight+ThisStyle->windowheight)
7175 answer->bits &= ~LINK_WINDOW;
7176 /* We don't know a ceiling-delta, so guess here */
7177 if ((56+answer->floordelta)<(ThisStyle->sillheight))
7178 answer->bits &= ~LINK_WINDOW;
7179
7180 /* If two doors are too close together, they won't work. */
7181 /* Could just turn off NEAR or FAR, eh? */
7182 if ( (answer->bits&LINK_ANY_DOOR)&&
7183 (answer->bits&LINK_CORE)&&
7184 !(answer->bits&LINK_ALCOVE)&&
7185 (answer->depth3<24) ) answer->depth3 = 24;
7186
7187 return answer;
7188
7189 } /* end random_link() */
7190
7191 /* Make a cool recessed lightstrip in the given linedef */
make_lightstrip(level * l,linedef * ld,style * ThisStyle,int ll,int depth,int spec,int fh,int ch,config * c)7192 void make_lightstrip(level *l, linedef *ld,style *ThisStyle,int ll,int depth,
7193 int spec, int fh, int ch, config *c)
7194 {
7195 linedef *ldnew;
7196 sector *s;
7197 texture *t;
7198
7199 /* should do an empty_area check here */
7200 t = ld->right->middle_texture;
7201 ldnew = lefthand_box(l,ld,4,ThisStyle,c);
7202 /* Have to shorten ldnew a bit here, for tapered edges, to */
7203 /* avoid colliding with orthogonal doors and stuff, if we're */
7204 /* not gonna do a full area check. Use rather silly shortening */
7205 if (ldnew->to->x>ldnew->from->x) {
7206 ldnew->to->x-=2;
7207 ldnew->from->x+=2;
7208 }
7209 if (ldnew->to->x<ldnew->from->x) {
7210 ldnew->to->x+=2;
7211 ldnew->from->x-=2;
7212 }
7213 if (ldnew->to->y>ldnew->from->y) {
7214 ldnew->to->y-=2;
7215 ldnew->from->y+=2;
7216 }
7217 if (ldnew->to->y<ldnew->from->y) {
7218 ldnew->to->y+=2;
7219 ldnew->from->y-=2;
7220 }
7221 ldnew->right->middle_texture = ThisStyle->walllight;
7222 /* Sometimes use bottom of lights. */
7223 if (!ThisStyle->peg_lightstrips) ldnew->flags |= LOWER_UNPEGGED;
7224 s = ldnew->right->sector;
7225 s->light_level = ll;
7226 s->special = spec;
7227 s->floor_height = fh;
7228 s->ceiling_height = ch;
7229 patch_upper(ld,t,c);
7230 patch_lower(ld,t,c);
7231 /* That wasn't so hard! */
7232 }
7233
7234 /* Is there an <sdepth> empty area on the lefthand side */
7235 /* of the linedef? */
empty_left_side(level * l,linedef * ld,int sdepth)7236 boolean empty_left_side(level *l, linedef *ld, int sdepth)
7237 {
7238 int newx1, newy1, newx2, newy2;
7239 boolean rc;
7240
7241 point_from(ld->from->x,ld->from->y,ld->to->x,ld->to->y,
7242 LEFT_TURN,sdepth,&newx1,&newy1);
7243 newx2 = newx1 - ld->to->x + ld->from->x;
7244 newy2 = newy1 - ld->to->y + ld->from->y;
7245 ld->from->marked = 1;
7246 ld->to->marked = 1;
7247 if (ld->right) ld->right->sector->marked = 1;
7248 rc = empty_rectangle(l,ld->from->x,ld->from->y,ld->to->x,ld->to->y,
7249 newx1,newy1,newx2,newy2);
7250 if (ld->right) ld->right->sector->marked = 0;
7251 ld->from->marked = 0;
7252 ld->to->marked = 0;
7253 return rc;
7254 }
7255
7256 /* Swell the linedef outward a bit; sdepth pels to the left, */
7257 /* in sno places. sno must be 2 or 3. */
7258 /* Makes boring rectangular rooms a little more interesting */
7259 /* Seems to have some strange bugs */
swell_linedef(level * l,linedef * ld,style * ThisStyle,config * c,int sno,int sdepth)7260 void swell_linedef(level *l,linedef *ld,style *ThisStyle,config *c,
7261 int sno,int sdepth)
7262 {
7263 int len,newx1,newy1,newx2,newy2;
7264 linedef *ldnew1, *ldnew2;
7265 boolean rc;
7266 char logstring[200];
7267
7268 rc = empty_left_side(l,ld,sdepth);
7269 if (!rc) return; /* oh, well! */
7270
7271 sprintf(logstring,"Swelling (%d,%d)-(%d,%d)...\n",ld->from->x,ld->from->y,
7272 ld->to->x,ld->to->y);
7273 announce(VERBOSE,logstring);
7274
7275 /* Now split the linedef, and jiggle the result(s) */
7276 len = linelen(ld)/sno;
7277 ldnew1 = split_linedef(l,ld,len,c);
7278 if (sno==3) ldnew2 = split_linedef(l,ldnew1,len,c);
7279 point_from(ld->from->x,ld->from->y,ld->to->x,ld->to->y,
7280 LEFT_TURN,sdepth,&newx1,&newy1);
7281 if (sno==3) point_from(ldnew1->from->x,ldnew1->from->y,
7282 ldnew1->to->x,ldnew1->to->y,
7283 LEFT_TURN,sdepth,&newx2,&newy2);
7284 ld->to->x = newx1;
7285 ld->to->y = newy1;
7286 sprintf(logstring,"Swol to (%d,%d)-(%d,%d)...\n",ld->from->x,ld->from->y,
7287 ld->to->x,ld->to->y);
7288 announce(VERBOSE,logstring);
7289 if (sno==3) {
7290 ldnew1->to->x = newx2;
7291 ldnew1->to->y = newy2;
7292 sprintf(logstring," and (%d,%d)-(%d,%d)...\n",ldnew1->from->x,ldnew1->from->y,
7293 ldnew1->to->x,ldnew1->to->y);
7294 announce(VERBOSE,logstring);
7295 }
7296
7297 } /* end swell_linedef */
7298
7299 /* Should these textures be aligned as if they were the same? */
coalignable(texture * t1,texture * t2)7300 boolean coalignable(texture *t1, texture *t2)
7301 {
7302 if (t1->subtle==t2) return TRUE;
7303 if (t2->subtle==t1) return TRUE;
7304 return (t1==t2);
7305 }
7306
7307 /* Is there room on the given level for the given type of object */
7308 /* at the given point, allowing for at the very least the given */
7309 /* width? */
room_at(level * l,genus * g,int x,int y,int width,config * c)7310 boolean room_at(level *l,genus *g,int x,int y,int width,config *c)
7311 {
7312 thing *t;
7313
7314 /* Check for requested length */
7315 for (t=l->thing_anchor;t;t=t->next)
7316 if (infinity_norm(t->x,t->y,x,y)<width) return FALSE;
7317 /* If it's not pickable, make sure not stuck-together */
7318 if (!(g->bits&PICKABLE))
7319 for (t=l->thing_anchor;t;t=t->next) {
7320 if (t->genus->bits&PICKABLE) continue;
7321 /* This is overly conservative; the real check should */
7322 /* be against g->width/2 + t->genus->width/2, eh? */
7323 if (infinity_norm(t->x,t->y,x,y)<g->width) return FALSE;
7324 if (infinity_norm(t->x,t->y,x,y)<t->genus->width) return FALSE;
7325 }
7326 return TRUE;
7327 }
7328
7329 /* Try to put an object with the given thingid and width into the */
7330 /* given sector. Use the given appearance bits, and heading */
7331 /* deafness. Return the new thing, or NULL if no room to be found */
7332 /* If angle is -1, point it toward ax/ay. */
place_object(level * l,sector * s,config * c,short thingid,int width,int angle,int ax,int ay,int bits)7333 thing *place_object(level *l,sector *s,config *c,short thingid,int width,
7334 int angle,int ax, int ay,int bits)
7335 {
7336 int minx,miny,maxx,maxy;
7337
7338 find_rec(l,s,&minx,&miny,&maxx,&maxy);
7339
7340 if ((maxx-minx)<width) return NULL;
7341 if ((maxy-miny)<width) return NULL;
7342
7343 return place_object_in_region(l,minx,miny,maxx,maxy,
7344 c,thingid,width,angle,ax,ay,bits);
7345 }
7346
7347 /* Try to put an object with the given thingid and width into the */
7348 /* given box. Use the given appearance bits, and heading */
7349 /* deafness. Return the new thing, or NULL if no room to be found */
7350 /* If angle is -1, point it toward ax/ay. */
place_object_in_region(level * l,int minx,int miny,int maxx,int maxy,config * c,short thingid,int width,int angle,int ax,int ay,int bits)7351 thing *place_object_in_region(level *l,int minx, int miny, int maxx, int maxy,
7352 config *c,short thingid,int width,
7353 int angle,int ax,int ay,int bits)
7354 {
7355 /* Stub assumes rectangles and stuff */
7356 int x,y,i,n,decksize,tangle;
7357 genus *g;
7358 thing *answer;
7359 struct s_deck {
7360 int x;
7361 int y;
7362 int tried;
7363 } deck[16];
7364
7365 {
7366 char s[200];
7367 sprintf(s,"place_object trying to place a %04x.",thingid);
7368 announce(NONE,s);
7369 }
7370
7371 g = find_genus(c,thingid);
7372
7373 if (!(g->bits&PICKABLE)) {
7374 if (maxx-minx<g->width) return NULL;
7375 if (maxy-miny<g->width) return NULL;
7376 }
7377
7378 /* Try the corners */
7379 deck[0].x = minx + width/2;
7380 deck[0].y = miny + width/2;
7381 deck[1].x = maxx - width/2;
7382 deck[1].y = maxy - width/2;
7383 deck[2].x = deck[0].x;
7384 deck[2].y = deck[1].y;
7385 deck[3].x = deck[1].x;
7386 deck[3].y = deck[0].y;
7387 /* And eight random spots */
7388 for (i=4;i<12;i++) {
7389 deck[i].x = minx + width/2 + roll(133,(maxx-minx)-width);
7390 deck[i].y = miny + width/2 + roll(134,(maxy-miny)-width);
7391 }
7392 /* And the center area, if there's room */
7393 if (((maxx-minx)>(width*2)) && ((maxy-miny)>(width*2))) {
7394 x = minx + (maxx-minx)/2;
7395 y = miny + (maxy-miny)/2;
7396 deck[12].x = x - width/2;
7397 deck[12].y = y - width/2;
7398 deck[13].x = x + width/2;
7399 deck[13].y = y + width/2;
7400 deck[14].x = deck[12].x;
7401 deck[14].y = deck[13].y;
7402 deck[15].x = deck[13].x;
7403 deck[15].y = deck[12].y;
7404 decksize = 16;
7405 } else {
7406 decksize = 12;
7407 }
7408
7409 /* Now we *should* shuffle the deck and go through it in order */
7410 /* until one is OK, but shuffling is expensive, so for now */
7411 /* we'll just use probes. */
7412
7413 for (i=0;i<decksize;i++) deck[i].tried = FALSE;
7414 for (i=0;i<10;i++) {
7415 n = roll(135,decksize);
7416 if (deck[n].tried) continue;
7417 x = deck[n].x;
7418 y = deck[n].y;
7419 if (room_at(l,g,x,y,width,c)) { /* Use first point with room we find */
7420 tangle = (angle==-1) ? facing_along(x,y,ax,ay) : angle;
7421 if (!rollpercent(226,l->p_rational_facing)) tangle = 90 * roll(136,4);
7422 answer = new_thing(l,x,y,(short)tangle,thingid,(short)bits,c);
7423 {
7424 char s[200];
7425 sprintf(s,"place_object placed it at (%d,%d).",x,y);
7426 announce(NONE,s);
7427 }
7428 return answer;
7429 }
7430 deck[n].tried = TRUE;
7431 } /* end for ten probes */
7432
7433 announce(NONE,"place_object failed");
7434 return NULL;
7435 }
7436
7437 /* Maybe place some explodables. Should this effect the haa? Well, */
7438 /* you can get hurt by an exploding one; on the other hand, you can use */
7439 /* one to kill a monster and thus avoid getting hurt. So punt. */
place_barrels(level * l,sector * s,config * c,haa * haa)7440 void place_barrels(level *l,sector *s,config *c,haa *haa)
7441 {
7442 int i = 0;
7443 genus *g;
7444
7445 if (!rollpercent(227,l->p_barrels)) return;
7446
7447 g = random_barrel(c,s->style);
7448 if (g==NULL) return; /* No barrels in this style! */
7449
7450 i = 1 + roll(137,5); /* Sort of boring, eh? */
7451
7452 for (;i;i--) {
7453
7454 if (NULL==place_object(l,s,c,g->thingid,g->width,0,0,0,(short)7)) return;
7455
7456 announce(VERBOSE,"Barrel");
7457
7458 } /* end forever */
7459
7460 } /* end place_barrels */
7461
7462 /* Maybe place some plants and other lawn decorations. */
place_plants(level * l,int allow,sector * s,config * c)7463 void place_plants(level *l,int allow,sector *s,config *c)
7464 {
7465 genus *g;
7466
7467 for (;;) {
7468 g = random_plant(c,s->style);
7469 if (g==NULL) return; /* No plants available! */
7470
7471 if (rollpercent(228,10)) return; /* hmmm... */
7472
7473 if (g->width<=allow)
7474 /* Next line used to have "allow", not "g->wdith". Why? */
7475 if (NULL==place_object(l,s,c,g->thingid,g->width,0,0,0,(short)7))
7476 return;
7477
7478 announce(VERBOSE,"Plant");
7479
7480 } /* end forever */
7481
7482 } /* end place_plants */
7483
7484 /* Return some random piece of armor, and a note as to which */
7485 /* levels need some. */
timely_armor(haa * haa,int * rlevels,config * c)7486 int timely_armor(haa *haa, int *rlevels, config *c)
7487 {
7488 int i, levels, armortype;
7489
7490 /* See which levels need more */
7491 levels = 0;
7492 for (i=0;i<3;i++) { /* for each hardness level */
7493 levels >>= 1;
7494 if (haa->haas[i].armor < c->usualarmor[i]) levels |= 0x04;
7495 }
7496
7497 *rlevels = levels;
7498
7499 if (levels==0) return 0;
7500
7501 /* Should be less primitive? */
7502 if (rollpercent(229,50)) {
7503 armortype = ID_HELMET;
7504 } else if (rollpercent(230,70)) {
7505 armortype = ID_GREENSUIT;
7506 } else {
7507 armortype = ID_BLUESUIT;
7508 }
7509
7510 return armortype;
7511
7512 }
7513
7514 /* Update the haa in the obvious way. Well, almost the obvious */
7515 /* way. It has to make some assumptions about how optimally */
7516 /* the player will utilize a suit. Some random parameters in here! */
update_haa_for_armor(haa * haa,int levels,short armortype)7517 void update_haa_for_armor(haa *haa,int levels,short armortype)
7518 {
7519
7520 switch (armortype) {
7521 case ID_HELMET:
7522 if (levels&0x01) haa->haas[ITYTD].armor++;
7523 if (levels&0x02) haa->haas[HMP].armor++;
7524 if (levels&0x04) haa->haas[UV].armor++;
7525 break;
7526 case ID_GREENSUIT:
7527 if (levels&0x01) {
7528 haa->haas[ITYTD].armor += 20;
7529 if (haa->haas[ITYTD].armor<100) haa->haas[ITYTD].armor = (float)100;
7530 }
7531 if (levels&0x02) {
7532 haa->haas[HMP].armor += 30;
7533 if (haa->haas[HMP].armor<100) haa->haas[HMP].armor = (float)100;
7534 }
7535 if (levels&0x04) {
7536 haa->haas[UV].armor += 50;
7537 if (haa->haas[UV].armor<100) haa->haas[UV].armor = (float)100;
7538 }
7539 break;
7540 case ID_BLUESUIT:
7541 if (levels&0x01) {
7542 haa->haas[ITYTD].armor += 40;
7543 if (haa->haas[ITYTD].armor<200) haa->haas[ITYTD].armor = (float)200;
7544 }
7545 if (levels&0x02) {
7546 haa->haas[HMP].armor += 60;
7547 if (haa->haas[HMP].armor<200) haa->haas[HMP].armor = (float)200;
7548 }
7549 if (levels&0x04) {
7550 haa->haas[UV].armor += 100;
7551 if (haa->haas[UV].armor<200) haa->haas[UV].armor = (float)200;
7552 }
7553 break;
7554 default:
7555 announce(ERROR,"Odd armortype in u_h_f_armor");
7556 }
7557
7558 }
7559
7560 /* Maybe place some armor, update the haa */
place_armor(level * l,sector * s,config * c,haa * haa)7561 void place_armor(level *l,sector *s,config *c,haa *haa)
7562 {
7563 int levels = 0;
7564 int armortype;
7565
7566 if (rollpercent(231,10)) return; /* Correct? */
7567
7568 for (;;) {
7569 announce(NONE,"place_armor looking for needy levels");
7570 armortype = timely_armor(haa,&levels,c);
7571 if (levels==0) return; /* Done if none */
7572 announce(NONE,"place_armor found some needy levels");
7573 if (NULL==place_object(l,s,c,(short)armortype,48,0,0,0,(short)levels)) return;
7574 announce(NONE,"place_armor placed some armor");
7575 update_haa_for_armor(haa,levels,(short)armortype);
7576 if (rollpercent(232,25)) return; /* Reasonable? */
7577 } /* end forever */
7578
7579 }
7580
7581 /* Return some useful kind of ammo or weapon, and what levels */
7582 /* it ought to be given to. */
timely_ammo(haa * haa,int * rlevels,config * c)7583 int timely_ammo(haa *haa, int *rlevels, config *c)
7584 {
7585 int levels = 0;
7586 int i, ammotype = 0;
7587 boolean need_shotgun, need_plasgun, need_launcher;
7588
7589 need_shotgun = FALSE;
7590 need_plasgun = FALSE;
7591 need_launcher = FALSE;
7592
7593 /* See which levels need more */
7594 for (i=0;i<3;i++) { /* for each hardness level */
7595 levels >>= 1;
7596 if (haa->haas[i].ammo < c->usualammo[i]) levels |= 0x04;
7597 if (haa->haas[i].can_use_shells == FALSE) need_shotgun = TRUE;
7598 if (haa->haas[i].can_use_cells == FALSE) need_plasgun = TRUE;
7599 if (haa->haas[i].can_use_rockets == FALSE) need_launcher = TRUE;
7600 }
7601
7602 *rlevels = levels;
7603
7604 if (levels==0) return 0;
7605
7606 /* it would be logical to only put down shells if */
7607 /* the player-model can use them, only put down */
7608 /* cells if... etc. But that's a little complex, */
7609 /* since we want to put down a single ammo that will */
7610 /* be useful for all the hardness levels. We cheat on */
7611 /* the shotgun, by always putting down one of those if */
7612 /* any level doesn't have one. And for the plasma gun */
7613 /* and the rocket launcher, we always give to all levels, */
7614 /* if to any, if any at all didn't have one yet. */
7615
7616 if ( (!c->weapons_are_special) && (need_shotgun) ) {
7617 if ((!(c->gamemask&(DOOM0_BIT|DOOM1_BIT))) && rollpercent(233,30)) {
7618 ammotype = ID_SSGUN;
7619 } else {
7620 ammotype = ID_SHOTGUN;
7621 }
7622 } else if ( (!c->weapons_are_special) && rollpercent(234,15) ) {
7623 int weapcount;
7624 if (c->gamemask&(DOOM0_BIT|DOOM1_BIT)) weapcount = 4;
7625 else weapcount = 5;
7626 switch (roll(138,weapcount)) {
7627 case 0: if (c->big_weapons) {
7628 ammotype = ID_PLASMA;
7629 } else {
7630 ammotype = ID_SHOTGUN;
7631 }
7632 break;
7633 case 1: ammotype = ID_SHOTGUN; break;
7634 case 2: ammotype = ID_CHAINGUN; break;
7635 case 3: if (c->big_weapons) {
7636 ammotype = ID_LAUNCHER;
7637 } else {
7638 ammotype = ID_SHOTGUN;
7639 }
7640 break;
7641 case 4: ammotype = ID_SSGUN; break;
7642 }
7643 } else if (rollpercent(235,10)) {
7644 ammotype = ID_CLIP;
7645 } else if (haa->haas[0].can_use_cells && rollpercent(236,10)) {
7646 ammotype = ID_CELL;
7647 } else if (haa->haas[0].can_use_cells && rollpercent(237,15)) {
7648 ammotype = ID_CELLPACK;
7649 } else if (haa->haas[0].can_use_rockets && rollpercent(238,12)) {
7650 ammotype = ID_ROCKET;
7651 } else if (haa->haas[0].can_use_rockets && rollpercent(239,15)) {
7652 ammotype = ID_ROCKBOX;
7653 } else if (rollpercent(240,10)) {
7654 ammotype = ID_BULBOX;
7655 } else if (rollpercent(241,60)) {
7656 ammotype = ID_SHELLS;
7657 } else {
7658 ammotype = ID_SHELLBOX;
7659 }
7660
7661 if ((ammotype==ID_PLASMA) && (need_plasgun)) {
7662 levels |= 0x07; /* All, if any */
7663 }
7664
7665 if ((ammotype==ID_LAUNCHER) && (need_launcher)) {
7666 levels |= 0x07; /* All, if any */
7667 }
7668
7669 *rlevels = levels; /* In case we just changed them */
7670 return ammotype;
7671 }
7672
7673 /* How much is that ammo in the window? Three numbers, one for each */
7674 /* skill level (since value can vary with what weapons you have!) */
ammo_value(short ammotype,haa * haa,int * f0,int * f1,int * f2)7675 void ammo_value(short ammotype,haa *haa,int *f0,int *f1,int *f2)
7676 {
7677 int answer;
7678 boolean special_case = FALSE;
7679
7680 /* These numbers should just be stored in the config, in the genus */
7681 switch (ammotype) {
7682 case ID_SSGUN:
7683 case ID_SHOTGUN: answer = 560;
7684 special_case = TRUE; break;
7685 case ID_SHELLS: answer = 280;
7686 special_case = TRUE; break;
7687 case ID_SHELLBOX: answer = 1400;
7688 special_case = TRUE; break;
7689 case ID_PLASMA: answer = 880; break;
7690 case ID_BFG: answer = 880; break; /* but a BFG is better, eh? */
7691 case ID_CHAINGUN: answer = 200; break;
7692 case ID_LAUNCHER: answer = 200; break;
7693 case ID_CLIP: answer = 100; break;
7694 case ID_BULBOX: answer = 500; break;
7695 case ID_CELL: answer = 440; break;
7696 case ID_CELLPACK: answer = 2200; break;
7697 case ID_ROCKET: answer = 100; break;
7698 case ID_ROCKBOX: answer = 500; break;
7699 default:
7700 announce(ERROR,"Funny ammo type in a_v");
7701 answer = 0;
7702 }
7703 *f0 = *f1 = *f2 = answer;
7704 if (special_case) { /* Sort of a hack! Make more general? */
7705 if ( (ammotype==ID_SSGUN) || (haa->haas[0].has_ssgun) )
7706 *f0 = (int) ( (double)answer * 10.0 / 7.0 );
7707 if ( (ammotype==ID_SSGUN) || (haa->haas[1].has_ssgun) )
7708 *f1 = (int) ( (double)answer * 10.0 / 7.0 );
7709 if ( (ammotype==ID_SSGUN) || (haa->haas[2].has_ssgun) )
7710 *f2 = (int) ( (double)answer * 10.0 / 7.0 );
7711 }
7712 return;
7713 }
7714
7715 /* The obvious thing */
update_haa_for_ammo(haa * haa,int levels,short ammotype)7716 void update_haa_for_ammo(haa *haa,int levels,short ammotype)
7717 {
7718 int a0,a1,a2;
7719
7720 ammo_value(ammotype,haa,&a0,&a1,&a2);
7721
7722 if (levels&0x01) haa->haas[ITYTD].ammo += a0;
7723 if (levels&0x02) haa->haas[HMP].ammo += a1;
7724 if (levels&0x04) haa->haas[UV].ammo += a2;
7725 if ((ammotype==ID_SHOTGUN)||(ammotype==ID_SSGUN)) {
7726 if (levels&0x01) haa->haas[ITYTD].can_use_shells = 1;
7727 if (levels&0x02) haa->haas[HMP].can_use_shells = 1;
7728 if (levels&0x04) haa->haas[UV].can_use_shells = 1;
7729 }
7730 if (ammotype==ID_CHAINGUN) {
7731 if (levels&0x01) haa->haas[ITYTD].has_chaingun = 1;
7732 if (levels&0x02) haa->haas[HMP].has_chaingun = 1;
7733 if (levels&0x04) haa->haas[UV].has_chaingun = 1;
7734 }
7735 if (ammotype==ID_PLASMA) {
7736 if (levels&0x01) haa->haas[ITYTD].can_use_cells = 1;
7737 if (levels&0x02) haa->haas[HMP].can_use_cells = 1;
7738 if (levels&0x04) haa->haas[UV].can_use_cells = 1;
7739 }
7740 if (ammotype==ID_LAUNCHER) {
7741 if (levels&0x01) haa->haas[ITYTD].can_use_rockets = 1;
7742 if (levels&0x02) haa->haas[HMP].can_use_rockets = 1;
7743 if (levels&0x04) haa->haas[UV].can_use_rockets = 1;
7744 }
7745 if (ammotype==ID_SSGUN) {
7746 if (levels&0x01) haa->haas[ITYTD].has_ssgun = 1;
7747 if (levels&0x02) haa->haas[HMP].has_ssgun = 1;
7748 if (levels&0x04) haa->haas[UV].has_ssgun = 1;
7749 }
7750 }
7751
7752 /* Is this thingid a weapon? Should use config! */
is_weapon(short thingid)7753 boolean is_weapon(short thingid)
7754 {
7755 switch (thingid) {
7756 case ID_SHOTGUN:
7757 case ID_SSGUN:
7758 case ID_CHAINGUN:
7759 case ID_CHAINSAW:
7760 case ID_PLASMA:
7761 case ID_BFG:
7762 case ID_LAUNCHER:
7763 return TRUE;
7764 default: return FALSE;
7765 }
7766 }
7767
7768 /* Maybe place some ammo, update the haa */
place_ammo(level * l,sector * s,config * c,haa * haa)7769 void place_ammo(level *l,sector *s,config *c,haa *haa)
7770 {
7771 int levels = 0;
7772 short ammotype;
7773
7774 if (c->allow_boring_rooms&&rollpercent(242,10)) return;
7775
7776 for (;;) {
7777 announce(NONE,"place_ammo looking for needy levels");
7778 ammotype = timely_ammo(haa,&levels,c);
7779 if (levels==0) return; /* Done if none */
7780 announce(NONE,"place_ammo found some needy levels");
7781 /* The 48 is just to avoid bunching-up and wall-illusions, */
7782 /* as well as the grab-through-wall effect. */
7783 if (NULL==place_object(l,s,c,ammotype,48,0,0,0,levels)) return;
7784 announce(NONE,"place_ammo placed some ammo");
7785 if (levels==7) if (is_weapon(ammotype)) s->has_dm_weapon = TRUE;
7786 update_haa_for_ammo(haa,levels,ammotype);
7787 if (rollpercent(243,20)) return; /* Reasonable? */
7788 } /* end forever */
7789
7790 }
7791
7792 /* Update the haa in the obvious way */
update_haa_for_health(haa * haa,int levels,short healthtype)7793 void update_haa_for_health(haa *haa,int levels,short healthtype)
7794 {
7795 int amount;
7796
7797 if (healthtype==ID_BERSERK) {
7798 announce(VERBOSE,"Put in a berserk pack!");
7799 if (levels&0x01) {
7800 if (haa->haas[ITYTD].health<100)
7801 haa->haas[ITYTD].health=(float)100;
7802 haa->haas[ITYTD].has_berserk = TRUE;
7803 }
7804 if (levels&0x02) {
7805 if (haa->haas[HMP].health<100)
7806 haa->haas[HMP].health=(float)100;
7807 haa->haas[HMP].has_berserk = TRUE;
7808 }
7809 if (levels&0x04) {
7810 if (haa->haas[UV].health<100)
7811 haa->haas[UV].health=(float)100;
7812 haa->haas[UV].has_berserk = TRUE;
7813 }
7814 } else {
7815 switch (healthtype) {
7816 case ID_STIMPACK: amount= 10; break;
7817 case ID_MEDIKIT: amount= 25; break;
7818 case ID_POTION: amount= 1; break;
7819 case ID_SOUL: amount=100; break;
7820 default: announce(WARNING,"Odd healthtype in u_h_f_h");
7821 amount=0;
7822 }
7823 if (levels&0x01) haa->haas[ITYTD].health += amount;
7824 if (levels&0x02) haa->haas[HMP].health += amount;
7825 if (levels&0x04) haa->haas[UV].health += amount;
7826 }
7827 }
7828
7829 /* Return a random kind of ordinary health-bonus for those levels */
7830 /* that need some health. If *levels comes back as zero, return */
7831 /* value is undefined. */
timely_health(haa * haa,int * levels,config * c)7832 short timely_health(haa *haa,int *levels,config *c)
7833 {
7834 int i;
7835 boolean berserk_ok = FALSE;
7836 short healthtype;
7837
7838 /* See which levels need more */
7839 for ((*levels)=0,i=0;i<3;i++) { /* for each hardness level */
7840 (*levels) >>= 1;
7841 if (haa->haas[i].health < c->usualhealth[i]) (*levels) |= 0x04;
7842 if (haa->haas[i].has_berserk==FALSE) berserk_ok = TRUE;
7843 }
7844
7845 if ((*levels)==0) return 0;
7846
7847 if (rollpercent(244,50)) {
7848 healthtype = ID_STIMPACK;
7849 } else if (rollpercent(245,50)) {
7850 healthtype = ID_MEDIKIT;
7851 } else if (rollpercent(246,90)) {
7852 healthtype = ID_POTION;
7853 } else if (berserk_ok&&rollpercent(247,50)) {
7854 healthtype = ID_BERSERK;
7855 } else {
7856 healthtype = ID_SOUL;
7857 }
7858 return healthtype;
7859 }
7860
7861 /* Maybe place some health boni, update the haa */
place_health(level * l,sector * s,config * c,haa * haa)7862 void place_health(level *l,sector *s,config *c,haa *haa)
7863 {
7864 int levels = 0;
7865 short healthtype;
7866
7867 /* Coming along. Might want to create effects around SOULs etc, eh? */
7868
7869 if (c->allow_boring_rooms&&rollpercent(248,10)) return;
7870
7871 for (;;) {
7872
7873 healthtype = timely_health(haa,&levels,c);
7874
7875 if (levels==0) return; /* Done if none */
7876
7877 /* The 48 is just to avoid bunching-up and wall-illusions */
7878 if (NULL==place_object(l,s,c,healthtype,48,0,0,0,levels)) return;
7879
7880 update_haa_for_health(haa,levels,healthtype);
7881
7882 if (rollpercent(249,20)) return; /* Reasonable? */
7883
7884 } /* end forever */
7885
7886 } /* end place_health */
7887
7888 /* Probably put some random bonus that the player needs at */
7889 /* the given location, and update the haa accordingly. */
place_timely_something(level * l,haa * haa,config * c,int x,int y)7890 void place_timely_something(level *l,haa *haa, config *c,int x, int y)
7891 {
7892 int thingtype, levels;
7893
7894 switch (roll(139,5)) {
7895 case 0: /* Armor */
7896 thingtype = timely_armor(haa,&levels,c);
7897 if (levels==0) return; /* Done if none */
7898 new_thing(l,x,y,0,(short)thingtype,(short)levels,c);
7899 update_haa_for_armor(haa,levels,(short)thingtype);
7900 return;
7901 case 1: /* Ammo/weapons */
7902 case 2:
7903 thingtype = timely_ammo(haa,&levels,c);
7904 if (levels==0) return; /* Done if none */
7905 new_thing(l,x,y,0,(short)thingtype,(short)levels,c);
7906 update_haa_for_ammo(haa,levels,(short)thingtype);
7907 return;
7908 case 3: /* Health */
7909 case 4:
7910 thingtype = timely_health(haa,&levels,c);
7911 if (levels==0) return; /* Done if none */
7912 new_thing(l,x,y,0,(short)thingtype,(short)levels,c);
7913 update_haa_for_health(haa,levels,(short)thingtype);
7914 return;
7915 }
7916 return; /* Unreachable */
7917 }
7918
7919 /* Return the size of monster, and the difficulty levels, that's due */
7920 /* in the current user-model (the haa). */
haa_monster_data(haa * haa,config * c,float * monster_size_health,float * monster_size_ammo,int * levels)7921 boolean haa_monster_data(haa *haa,config *c, float *monster_size_health,
7922 float *monster_size_ammo,int *levels)
7923 {
7924 float excess_health;
7925 float available_ammo;
7926 int i;
7927
7928 /* Determine what size monster we want */
7929 *levels = 0;
7930 *monster_size_health = (float)10000;
7931 *monster_size_ammo = (float)10000;
7932 for (i=0;i<3;i++) {
7933 *levels >>= 1; /* Shift the bits over */
7934 excess_health = haa->haas[i].health - c->minhealth[i];
7935 if (excess_health>0) {
7936 *levels |= 0x04; /* Set the bit */
7937 /* Can take more damage if armored */
7938 if (excess_health<haa->haas[i].armor) {
7939 excess_health += excess_health;
7940 } else {
7941 excess_health += haa->haas[i].armor;
7942 }
7943 /* -Will- take more if no good weapons */
7944 if (!(haa->haas[i].can_use_shells||haa->haas[i].can_use_cells))
7945 excess_health /= 2;
7946 if (excess_health<*monster_size_health)
7947 *monster_size_health = excess_health;
7948 available_ammo = haa->haas[i].ammo;
7949 /* If wimpy weapons, will use more ammo */
7950 if (!(haa->haas[i].can_use_shells||haa->haas[i].can_use_cells))
7951 available_ammo /= 2;
7952 if (haa->haas[i].ammo<*monster_size_ammo)
7953 *monster_size_ammo = haa->haas[i].ammo;
7954 } /* end this level has excess health */
7955 } /* end for difficulty levels determining limits */
7956 *monster_size_health += (float)5; /* A little leeway */
7957 if (*levels==0) return FALSE; /* No excess health anywhere */
7958 return TRUE;
7959 }
7960
7961 /* Find a monster that fits the given health and ammo allowance, */
7962 /* for the given apearence bits. If none, return the monster */
7963 /* that's the easiest to kill. Never return null! */
proper_monster(float health,float ammo,int bits,haa * haa,int mno,propertybits require,propertybits forbid,boolean biggest,config * c)7964 genus *proper_monster(float health,float ammo,int bits,haa *haa,
7965 int mno,propertybits require,propertybits forbid,
7966 boolean biggest, config *c)
7967 {
7968 genus *m, *m1, *m0, *mx, *my;
7969 float damage,ammo0,bx;
7970 int i,count;
7971 float hl, am;
7972 int thisbit;
7973
7974 {
7975 char s[200];
7976 sprintf(s,"proper_monster looking for %f health, %f ammo, levels %d",
7977 health, ammo, bits);
7978 announce(NONE,s);
7979 }
7980
7981 require |= MONSTER; /* Duh! */
7982 /* This is disabled; with min_level, we just start having boss creatures
7983 * show up in later levels */
7984 forbid |= BOSS; /* No wandering bosses */
7985 /* (Wandering bosses would be nice for the later levels. Alas, the
7986 * bosses are too big to really fit where SLUMP wants to put them,
7987 * so we have to do without them */
7988 /* With min_level, we just have big monsters show up in later levels */
7989 /* if (!c->big_monsters) forbid |= BIG; */
7990
7991 /* Mark eligible monsters, and find wimpiest and biggest just in case */
7992 count = 0;
7993 ammo0 = (float)10000;
7994 m0 = NULL;
7995 mx = NULL;
7996 my = NULL;
7997 bx = (float)0;
7998 for (m=c->genus_anchor;m;m=m->next) {
7999 m->marked = 0;
8000 if ((m->bits & require) != require) continue;
8001 if ((m->bits & forbid) != 0) continue;
8002 /* Levels above 15 are more likely to have big meanies */
8003 if (current_level_number > 15 && current_level_number <= 30) {
8004 if(rollpercent(444,(6 * (current_level_number - 15))) &&
8005 (m->bits & BOSS) == 0 && (m->bits & BIG) == 0) {
8006 continue;
8007 }
8008 /* Levels 26 and above are more likely to have ARACHes */
8009 /* Disabled: Too buggy */
8010 /* if(current_level_number > 25 &&
8011 rollpercent(445,(13 * (current_level_number - 25))) &&
8012 m->thingid != ID_ARACH) {
8013 continue;
8014 } */
8015 }
8016 if (m->in_freedoom == 0) continue; /* Only FreeDoom monsters */
8017 if (m->min_level > current_level_number) continue; /* Progression */
8018 #ifdef IMPOSSIBLE_MONSTERS_IN_CONFIG
8019 if ((m->gamemask&c->gamemask)!=c->gamemask) continue;
8020 #endif
8021 if (m0==NULL) m0 = m;
8022 m->marked = 1;
8023 for (i=0,thisbit=1;(i<3)&&(m->marked);i++,thisbit<<=1) {
8024 if (!(thisbit&bits)) continue;
8025 /* If we don't have any good weapons, we'll take more */
8026 /* damage and use more ammo, so halve the limits. */
8027 /* Include chaingun here, too? */
8028 if (!(haa->haas[i].can_use_shells||haa->haas[i].can_use_cells)) {
8029 hl = health / 2;
8030 am = ammo / 2; /* This may not be reasonable / necessary */
8031 } else {
8032 hl = health;
8033 am = ammo;
8034 }
8035 if (mno) {
8036 damage = m->damage[i];
8037 } else {
8038 damage = m->altdamage[i];
8039 }
8040 if (damage>hl) m->marked=0;
8041 if (m->ammo_to_kill[i]>am) m->marked=0;
8042 if (m->ammo_to_kill[i]<ammo0) {
8043 m0 = m;
8044 ammo0 = m->ammo_to_kill[i];
8045 }
8046 } /* end for levels */
8047 if (m->marked) {
8048 count++;
8049 if (m->ammo_to_kill[0]+m->damage[0] > bx) {
8050 my = mx;
8051 mx = m;
8052 bx = m->ammo_to_kill[0]+m->damage[0];
8053 }
8054 }
8055 } /* end for m over monsters */
8056 if (count==0) { /* Put down the wimpiest monster */
8057 m = m0;
8058 } else if (biggest) { /* Put down the biggest monster */
8059 m = mx;
8060 if (my) if (rollpercent(250,40)) m = my; /* Or the second-biggest */
8061 } else { /* Choose a random one */
8062 count = 1 + roll(140,count);
8063 for (m=c->genus_anchor;m;m=m->next) {
8064 if (m->marked) count--;
8065 if (count==0) break;
8066 }
8067 }
8068
8069 /* Unmark monsters */
8070 for (m1=c->genus_anchor;m1;m1=m1->next) m1->marked = 0;
8071
8072 return m;
8073 }
8074
8075 /* This says that the current battle is over, so any pending */
8076 /* weapon-pickups can occur. If we ever want to be kind, we */
8077 /* can defer the ammo to this point as well. */
haa_unpend(haa * haa)8078 void haa_unpend(haa *haa)
8079 {
8080 int i;
8081
8082 for (i=ITYTD;i<=UV;i++) {
8083 if (haa->haas[i].shells_pending) {
8084 haa->haas[i].can_use_shells = TRUE;
8085 haa->haas[i].shells_pending = FALSE;
8086 }
8087 if (haa->haas[i].chaingun_pending) {
8088 haa->haas[i].has_chaingun = TRUE;
8089 haa->haas[i].chaingun_pending = FALSE;
8090 }
8091 }
8092 }
8093
8094 /* This makes the model assume that, unlike in-room goodies, ammo */
8095 /* from monsters is taken at once. challenging! Weapons, on the */
8096 /* other hand, just go into the _pending bits, for haa_unpend(). */
update_haa_for_monster(haa * haa,genus * m,int levels,int mno,config * c)8097 void update_haa_for_monster(haa *haa,genus *m,int levels,int mno,config *c)
8098 {
8099 int i, thisbit;
8100 float damage;
8101
8102 for (i=0,thisbit=1;i<3;i++,thisbit<<=1) {
8103 if (!(thisbit&levels)) continue;
8104 if (mno) {
8105 damage = m->damage[i];
8106 } else {
8107 damage = m->altdamage[i];
8108 }
8109 if (!(haa->haas[i].can_use_shells||haa->haas[i].can_use_cells))
8110 damage *= 2;
8111 if (damage > 2*haa->haas[i].armor) {
8112 haa->haas[i].health += haa->haas[i].armor;
8113 haa->haas[i].armor = (float)0;
8114 haa->haas[i].health -= damage;
8115 } else {
8116 haa->haas[i].health -= damage/2;
8117 haa->haas[i].armor -= damage/2;
8118 }
8119 if (haa->haas[i].health<0) announce(VERBOSE,"Health estimate negative?");
8120 damage = m->ammo_to_kill[i];
8121 /* Takes more ammo, if no good weapons */
8122 if (!(haa->haas[i].can_use_shells||haa->haas[i].can_use_cells))
8123 damage *= 2;
8124 /* But less ammo if we can saw it or punch it! */
8125 if (haa->haas[i].has_chainsaw&&!(m->bits&(FLIES|SHOOTS))) {
8126 damage /= 2;
8127 } else if (haa->haas[i].has_berserk&&!(m->bits&(FLIES|SHOOTS))) {
8128 damage *= (float)0.80;
8129 }
8130 haa->haas[i].ammo -= damage;
8131 haa->haas[i].ammo += m->ammo_provides; /* Should be in stage two? */
8132 if (haa->haas[i].ammo<0) announce(VERBOSE,"Ammo estimate negative?");
8133 if (m->thingid == ID_SERGEANT) haa->haas[i].shells_pending = TRUE;
8134 if (m->thingid == ID_COMMANDO) haa->haas[i].chaingun_pending = TRUE;
8135
8136 } /* end for levels adjusting haa */
8137
8138 } /* end update_haa_for_monster */
8139
8140 /* Return a monster that there's room for in the model now. */
timely_monster(haa * haa,config * c,int * levels,boolean biggest,int mno)8141 genus *timely_monster(haa *haa,config *c,int *levels,boolean biggest,
8142 int mno)
8143 {
8144 /* Should just be a macro, eh? */
8145 return timely_monster_ex(haa,c,levels,biggest,mno,0); /* no extra reqs */
8146 }
8147
8148 /* Return a monster that there's room for in the model now, with */
8149 /* some required bits set. */
8150 /* Should really take into account the _size_ of the place you're */
8151 /* planning to put the monster, eh? */
timely_monster_ex(haa * haa,config * c,int * levels,boolean biggest,int mno,propertybits req)8152 genus *timely_monster_ex(haa *haa,config *c,int *levels,boolean biggest,
8153 int mno,propertybits req)
8154 {
8155 float monster_size_health;
8156 float monster_size_ammo;
8157
8158 /* Find how big a monster we can tolerate */
8159 if (!haa_monster_data(haa,c,
8160 &monster_size_health,&monster_size_ammo,levels))
8161 return NULL; /* Not enough excess health in any level */
8162
8163 /* Find a monster of that size */
8164 return proper_monster(monster_size_health,monster_size_ammo,*levels,haa,mno,
8165 c->required_monster_bits+req,
8166 c->forbidden_monster_bits,
8167 biggest,c);
8168 }
8169
8170 /* Maybe add some monsters, update the haa */
place_monsters(level * l,sector * s,config * c,haa * haa)8171 void place_monsters(level *l,sector *s,config *c,haa *haa)
8172 {
8173 int mno,n;
8174 int levels;
8175 genus *m, *lastm;
8176 boolean rc;
8177
8178 /* Decide on a limit, if any, for the monster loop; should be config/style? */
8179 if (c->allow_boring_rooms&&rollpercent(251,20)) return; /* No monsters at all */
8180 if (rollpercent(252,80)) {
8181 n = 2 + roll(141,8); /* N to M monsters */
8182 } else {
8183 n = 1000; /* As many monsters as will fit! */
8184 }
8185 n *= l->hugeness; /* Reasonable? */
8186
8187 /* The loop itself */
8188 for (lastm=NULL,mno=0;mno<n;mno++) {
8189
8190 m = timely_monster(haa,c,&levels,rollpercent(253,l->p_biggest_monsters),mno);
8191 if (!m) return;
8192 if (lastm)
8193 if (rollpercent(254,c->homogenize_monsters))
8194 m = lastm; /* Yoiks, dangerous! */
8195 lastm = m;
8196
8197 if (rollpercent(255,15)) levels |= 0x08; /* deaf */
8198
8199 announce(NONE,"Trying to place a monster");
8200
8201 /* Try to place it */
8202 rc = (NULL!=place_object(l,s,c,m->thingid,MONSTER_WIDTH(m),-1,
8203 s->entry_x,s->entry_y,levels));
8204 if (!rc) {
8205 announce(NONE,"Placement failed");
8206 goto done_monsters; /* Might as well give up entirely */
8207 }
8208
8209 if (m->thingid == ID_SKULL) announce(NONE,"Skull");
8210 if (m->thingid == ID_HEAD) announce(VERBOSE,"HEAD");
8211 if (m->thingid == ID_SKEL) announce(VERBOSE,"SKEL");
8212 if (m->thingid == ID_HELL) announce(VERBOSE,"KNIGHT");
8213 if (m->thingid == ID_ARCHIE) announce(VERBOSE,"VILE");
8214
8215 update_haa_for_monster(haa,m,levels,mno,c);
8216
8217 } /* end for a long time */
8218
8219 done_monsters:
8220
8221 /* NOTE: tempting as it is, we don't do this *within* the */
8222 /* loop, because the model is that the player doesn't pick */
8223 /* up the objects in the room (including dropped weapons) */
8224 /* until after everything is dead. So we don't use the */
8225 /* new values of these data until we've placed all the */
8226 /* monsters for a room. On the other hand, the effects */
8227 /* of the can_use_x variables are sort of down in the */
8228 /* noise, so it may not be worth *too* much effort to */
8229 /* get this exactly right... */
8230 haa_unpend(haa);
8231
8232 return; /* all done */
8233
8234 } /* end place_monsters */
8235
8236
8237 /* Boy is this primitive! On the other hand, other checks later */
8238 /* mean that this can probably be somewhat optimistic. */
isAdequate(level * l,linedef * ld,style * ThisStyle,config * c)8239 boolean isAdequate(level *l,linedef *ld,style *ThisStyle,config *c)
8240 {
8241 /* Assume all 1S longish linedefs are OK; very dangerous! */
8242 if (ld->left) return 0;
8243 if (ld->flags & TWO_SIDED) return 0;
8244 if (lengthsquared(ld)>=(128*128)) { /* Why 128? */
8245 return 1;
8246 } else {
8247 return 0;
8248 }
8249 }
8250
8251 /* Fill in the default config-file data contents stuff */
load_default_config(config * c)8252 void load_default_config(config *c)
8253 {
8254 char *p;
8255 c->configdata = strdup( /* So we can free() it */
8256 "[THEMES] T M T B T W T R ? "
8257 "t PANEL5 0 1 "
8258 "t PANCASE2 0 1 "
8259 "t PANCASE1 0 1 "
8260 "t PANBORD2 0 1 "
8261 "t PANBORD1 0 1 "
8262 "t METAL7 0 1 "
8263 "t METAL6 0 1 "
8264 "t METAL2 0 1 "
8265 "t COMP2 2 "
8266 "t SILVER2 0 1 "
8267 "t SILVER1 0 1 "
8268 "t EXITSIGN X "
8269 "t ZZWOLF1 o 0 1 "
8270 "t ZIMMER3 o 0 1 "
8271 "t ZIMMER5 o 0 1 "
8272 "t TANROCK5 o 0 1 "
8273 "t TANROCK4 o 0 1 "
8274 "t TANROCK2 o 0 1 "
8275 "t STUCCO o 0 1 "
8276 "t STONE6 o 0 1 "
8277 "t ROCK1 o 0 1 "
8278 "t MODWALL1 o 0 1 "
8279 "t BSTONE1 o 0 1 "
8280 "t BRICK5 o 0 1 "
8281 "t BRICK4 o 0 1 "
8282 "t ASHWALL7 o 0 1 "
8283 "t ASHWALL6 o 0 1 "
8284 "t ASHWALL4 o 0 1 "
8285 "t ASHWALL2 o 0 1 "
8286 "t STONE3 o "
8287 "t SP_ROCK1 o "
8288 "t GRAYVINE o "
8289 "t GRAYBIG o "
8290 "t SLDOOR1 z 64 128 d L c M c B = SP_DUDE5 u "
8291 "t DOORSKUL z 64 72 d L c M c B 0 1 u "
8292 "t TEKBRON2 z 64 128 d c M c B 0 1 "
8293 "t SPCDOOR4 z 64 128 d c M 0 1 "
8294 "t SPCDOOR3 z 64 128 d c M 0 1 "
8295 "t SPCDOOR2 z 64 128 d c M 0 1 "
8296 "t SPCDOOR1 z 64 128 d c M 0 1 "
8297 "t DOORHI z 64 128 d c M 2 "
8298 "t DOOR3 z 64 72 d c M "
8299 "t DOOR1 z 64 72 d c M "
8300 "t WOODSKUL z 64 128 d c W c B c R 2 "
8301 "t WOODMET2 z 64 128 d c W c B c R 0 1 "
8302 "t WOODGARG z 64 128 d c W c B c R "
8303 "t BIGDOOR4 z 128 128 d c M "
8304 "t BIGDOOR3 z 128 128 d c M "
8305 "t BIGDOOR2 z 128 128 d c M "
8306 "t BIGDOOR1 z 128 96 d c M "
8307 "t BIGDOOR7 z 128 128 d c W c B c R "
8308 "t BIGDOOR6 z 128 112 d c W c B c R "
8309 "t BIGDOOR5 z 128 128 d c W c B c R "
8310 "t EXITSWIR E c R 0 1 u "
8311 "t EXITSWIW E c W c B 0 1 u "
8312 "t EXITSWIT E c M 0 1 u "
8313 "t BFALL1 z 8 128 l c R 0 1 "
8314 "t LITEREDL z 8 128 l c R = LITERED 2 "
8315 "t TEKLITE l c M 0 1 "
8316 "t LITE4 l c M c B 2 "
8317 "t LITE5 l c M c B "
8318 "t LITE3 l c M c B "
8319 "t SILVER3 p v c M 0 1 "
8320 "t SPACEW3 p v c M 0 1 "
8321 "t COMPSTA2 p v H c M "
8322 "t COMPSTA1 p v H c M "
8323 "t COMPTALL p v c M "
8324 "t COMPUTE1 p v H c M 2 "
8325 "t PLANET1 p v H c M 2 "
8326 "t PANBOOK p c W 0 1 "
8327 "t SKIN2 p v c R "
8328 "t GSTFONT1 p c R "
8329 "t SKY1 p c W c B "
8330 "t SKY3 p c B c R "
8331 "t MARBFAC3 p v c W c B "
8332 "t MARBFAC2 p v c W c B "
8333 "t MARBFACE p v c W c B "
8334 "t BRNBIGC g c M 2 "
8335 "t MIDSPACE g c M 0 1 "
8336 "t MIDVINE1 g c W c M c B c R 2 "
8337 "t MIDBARS1 g c W c M c B c R 0 1 "
8338 "t MIDGRATE g c W c M c B c R "
8339 "t LITERED z 8 128 r c M c B 2 "
8340 "t DOORYEL z 8 128 y c M c B "
8341 "t DOORRED z 8 128 r c M c B "
8342 "t LITEBLU4 z 16 128 b c M c B "
8343 "t LITEBLU1 z 8 128 b c M c B "
8344 "t DOORBLU z 8 128 b c M c B "
8345 "t DOORYEL2 z 16 128 y c W c R "
8346 "t DOORBLU2 z 16 128 b c W c R "
8347 "t DOORRED2 z 16 128 r c W c R "
8348 "t STEP6 z 256 16 e c W c M c B "
8349 "t STEP5 z 256 16 e c W c M c B "
8350 "t STEP4 z 256 16 e c W c M c B "
8351 "t STEP3 z 256 8 e c W c M c B "
8352 "t STEP2 z 256 8 e c W c M c B "
8353 "t STEP1 z 256 8 e c W c M c B "
8354 "t FIRELAVA j c R "
8355 "t DOORTRAK j c W c M c B "
8356 "t DOORSTOP j c W c M c B "
8357 "t SKSNAKE2 I c R "
8358 "t ROCKRED1 I c R "
8359 "t ZIMMER7 I c B 0 1 "
8360 "t BRICK10 I o c B 0 1 "
8361 "t COMPSPAN I c M "
8362 "t SUPPORT2 I c M c B "
8363 "t SHAWN2 I c M c B "
8364 "t ASHWALL3 I o c W 0 1 "
8365 "t ASHWALL I o c W 2 "
8366 "t BROWNHUG I o c W c M c B "
8367 "t SUPPORT3 I c W c B "
8368 "t METAL z 64 128 I d c W c B c R "
8369 "t SW1HOT i c R "
8370 "t SKY3_W w c R = SKY3 "
8371 "t SP_HOT1 w C R "
8372 "t REDWALL w ! C R "
8373 "t FIREMAG1 p c B "
8374 "t FIREBLU1 w C R S FIREMAG1 @ 0 "
8375 "t SW1MARB i c B 0 1 "
8376 "t SW1GSTON i c B "
8377 "t GSTVINE1 w o c B S GSTVINE2 "
8378 "t MARBGRAY w c B S GRAY5 0 1 "
8379 "t GSTONE1 w o c B S GSTGARG "
8380 "t MARBGARG w c B S MARBLE1 0 1 u "
8381 "t MARBLE2 w C B S MARBLE3 "
8382 "t MARBLE3 w C B S MARBLE1 "
8383 "t MARBLE1 w C B S MARBLE3 @ 0 "
8384 "t PLAT1 z 128 128 F c M "
8385 "t GRAYALT w C M s SW1GRAY 0 1 u "
8386 "t TEKVINE w c M S TEKGREN3 s SW1TEK @ 0 0 1 u "
8387 "t SPACEW4 w c M s SW1TEK 0 1 "
8388 "t SW1MET2 Y 64 "
8389 "t METAL3 0 1 "
8390 "t METAL5 w c M S METAL3 s SW1MET2 0 1 "
8391 "t COMPUTE3 w c M s SW1STRTN 2 "
8392 "t TEKWALL4 w c M S COMPWERD s SW1COMP @ 0 "
8393 "t TEKWALL1 w c M S COMPWERD s SW1COMP @ 0 "
8394 "t ICKWALL3 o "
8395 "t GRAY1 w c M S ICKWALL3 s SW1GRAY "
8396 "t BROVINE2 w c M s SW1SLAD @ 24 "
8397 "t BRONZE4 w C M S BRONZE3 s SW1TEK 0 1 "
8398 "t STARTAN1 w C M S STARTAN2 s SW1STRTN 2 "
8399 "t STARTAN3 w C M S STARG3 s SW1STRTN "
8400 "t LITEMET 2 "
8401 "t METAL1 w c M S LITEMET s SW1METAL "
8402 "t STARBR2 w c M S STARTAN2 s SW1STRTN "
8403 "t STARTAN2 w C M S STARBR2 s SW1STRTN "
8404 "t STARG3 w C M S STARGR1 s SW1STRTN "
8405 "t STARG2 w C M S STARG1 s SW1STRTN "
8406 "t STARG1 w C M S STARG2 s SW1STRTN "
8407 "t SW1DIRT Y 72 "
8408 "t BROWN144 o "
8409 "t BROWN96 w C M S BROWN144 s SW1DIRT "
8410 "t BROWN1 w C M s SW1BRN2 "
8411 "t BROWNGRN w C M S SLADWALL s SW1BRNGN "
8412 "t SLADWALL w C M S BROWNGRN s SW1SLAD "
8413 "t SW1WOOD i c W c B "
8414 "t SW1SATYR i c W "
8415 "t SW1LION i c W "
8416 "t SW1GARG i c W "
8417 "t WOOD12 w c W @ 3 0 1 "
8418 "t SLOPPY2 w c W S SLOPPY1 @ 0 0 1 Q "
8419 "t SKULWALL w c W S SKULWAL3 @ 0 2 Q "
8420 "t SKINSYMB w c W S SKINMET1 "
8421 "t SKINMET2 w c W S SKINMET1 "
8422 "t SKINMET1 w c W S SKINMET2 "
8423 "t PIPE2 w j c W c M S PIPE4 s SW1WOOD @ 0 "
8424 "t WOODVINE w c W S WOOD9 @ 0 0 1 u "
8425 "t WOOD4 w c W @ 64 "
8426 "t WOOD9 w C W S WOOD7 @ 0 0 1 "
8427 "t WOOD5 w C W S WOOD1 "
8428 "t WOOD3 w C W S WOOD1 @ 3 "
8429 "t WOOD1 w C W S WOOD3 "
8430 "f FWATER1 W "
8431 "f F_SKY1 K "
8432 "f SLGATE1 G c W c M c B c R u "
8433 "f GATE4 G c W c M c B c R "
8434 "f GATE3 G c W c M c B c R "
8435 "f GATE2 G c W c M c B c R "
8436 "f GATE1 G c W c M c B c R "
8437 "f SLGRASS1 o u "
8438 "f SLIME13 o 0 1 "
8439 "f RROCK19 o 0 1 "
8440 "f RROCK16 o 0 1 "
8441 "f RROCK11 o 0 1 "
8442 "f GRNROCK o 0 1 "
8443 "f GRASS2 o 0 1 "
8444 "f GRASS1 o 0 1 "
8445 "f MFLR8_4 o "
8446 "f MFLR8_3 o "
8447 "f MFLR8_2 o "
8448 "f FLAT5_7 o "
8449 "f FLAT10 o "
8450 "f GRNLITE1 U l c B 0 1 "
8451 "f FLOOR7_2 U c B "
8452 "f SLLITE1 U l c M u "
8453 "f TLITE6_6 U l c M "
8454 "f FLOOR7_1 U o c M "
8455 "f FLOOR5_2 U c M "
8456 "f FLOOR5_1 U c M "
8457 "f CEIL3_1 U c M "
8458 "f FLOOR5_4 U o c W "
8459 "f FLOOR4_6 U c W "
8460 "f CEIL3_3 U c W "
8461 "f CEIL1_1 U c W "
8462 "f LAVA1 n c R "
8463 "f SLSPARKS D c R u "
8464 "f SFLR6_4 D U c R "
8465 "f TLITE6_5 U l c R "
8466 "f FLOOR6_1 D U r c R "
8467 "f FLOOR1_7 U l c R "
8468 "f FLOOR1_6 D U r c R "
8469 "f FLAT5_3 D U r c R "
8470 "f SLFLAT01 D c B u "
8471 "f FLAT1 D c B "
8472 "f DEM1_6 D c B "
8473 "f DEM1_5 D U c B "
8474 "f NUKAGE1 n c M c B "
8475 "f FLOOR4_1 D c M "
8476 "f FLOOR3_3 D U c M c B "
8477 "f FLOOR0_2 D c M "
8478 "f FLOOR0_1 D o c M "
8479 "f FLAT1_2 D o c M "
8480 "f FLAT5 D c M "
8481 "f SLIME09 n c W 0 1 "
8482 "f BLOOD1 n r c W c B c R "
8483 "f FLAT8 D c W "
8484 "f FLAT5_2 D c W "
8485 "f FLAT5_1 D U c W c B "
8486 "f FLAT1_1 D o c W "
8487 "f CEIL5_2 D o c W "
8488 "f MFLR8_1 D c W "
8489 "x m 4 h 128 c W c B 0 1 "
8490 "O FLAT5_1 O CRATOP2 O CEIL5_2 O CEIL3_3 O CEIL1_1 "
8491 "B PANEL5 ~ 64 "
8492 "B PANCASE2 ~ 64 "
8493 "B PANCASE1 ~ 64 "
8494 "B PANBORD2 ~ 16 "
8495 "B PANBORD1 ~ 32 "
8496 "A PANBOOK ~ 64 "
8497 "x m 3 h 64 c W c M "
8498 "O CRATOP2 "
8499 "A CRATWIDE ] 64 64 "
8500 "A CRATE1 ~ 64 "
8501 "x m 3 h 64 c W c M "
8502 "O CRATOP1 "
8503 "A CRATWIDE "
8504 "A CRATE2 ~ 64 "
8505 "x m 3 h 64 c W c M "
8506 "O CRATOP1 "
8507 "A CRATELIT ~ 32 "
8508 "x m 3 h 32 c W c M "
8509 "O CRATOP1 "
8510 "A CRATELIT ~ 32 "
8511 "x m 3 h 16 c W c M "
8512 "O CRATOP1 "
8513 "A CRATINY ~ 16 "
8514 "x m 2 h 128 c M "
8515 "O CEIL5_1 O FLAT4 O TLITE6_1 "
8516 "B METAL7 ] 0 64 ~ 64 "
8517 "B METAL6 ] 0 64 ~ 64 "
8518 "B METAL5 ] 0 64 ~ 64 "
8519 "B METAL3 ] 0 64 ~ 64 "
8520 "B METAL2 ] 0 64 ~ 64 "
8521 "B COMPWERD ~ 64 "
8522 "B COMPSPAN ~ 16 "
8523 "A SPACEW3 ~ 64 "
8524 "A COMPTALL ~ 256 "
8525 "A COMP2 ~ 256 "
8526 "x m 2 h 64 c M "
8527 "O CEIL5_1 O FLAT4 O TLITE6_1 "
8528 "B METAL7 ] 0 64 ~ 64 "
8529 "B METAL6 ] 0 64 ~ 64 "
8530 "B METAL5 ] 0 64 ~ 64 "
8531 "B METAL3 ] 0 64 ~ 64 "
8532 "B METAL2 ] 0 64 ~ 64 "
8533 "B COMPWERD ~ 64 "
8534 "B COMPSPAN ~ 16 "
8535 "A SPACEW3 ] 0 64 ~ 64 "
8536 "A COMPTALL ] 0 64 ~ 256 "
8537 "A COMP2 ] 0 64 ~ 256 "
8538 "x m 1 h 128 c M c B "
8539 "O FLAT9 O FLAT4 O FLAT23 O FLAT19 O FLAT18 O CRATOP1 O COMP01 "
8540 "B SILVER2 ~ 64 "
8541 "B SILVER1 ~ 64 "
8542 "B SUPPORT2 ~ 16 "
8543 "B SHAWN2 ~ 16 "
8544 "A SILVER3 "
8545 "A COMPUTE1 ] 0 64 "
8546 "x m 1 h 64 c M c B "
8547 "O FLAT9 O FLAT4 O FLAT23 O FLAT19 O FLAT18 O CRATOP1 O COMP01 "
8548 "B SUPPORT2 ~ 16 "
8549 "B SHAWN2 ~ 16 "
8550 "A COMPUTE1 ] 0 64 "
8551 "A COMPSTA2 "
8552 "A COMPSTA1 "
8553 ". 2035 c M "
8554 ". 34 c W c M c B "
8555 ". 44 c W c B "
8556 ". 45 c W c B "
8557 ". 46 c W c B c R "
8558 ". 55 c W c B "
8559 ". 56 c W c B "
8560 ". 57 c W c B c R "
8561 ". 48 c M "
8562 ". 2028 c M "
8563 ". 85 c M "
8564 ". 86 c M "
8565 ". 70 c M c W "
8566 ". 35 c W "
8567 "# ");
8568 for (p=c->configdata;*p;p++)
8569 if (*p==' ') *p = '\0';
8570 return;
8571 }
8572
8573 /* Make the config-file data accessible */
load_config(config * c)8574 void load_config(config *c)
8575 {
8576 FILE *f;
8577 char thisline[200];
8578 char *inc, *outc;
8579 long flen;
8580 boolean blankmode = TRUE; /* Strip leading blanks */
8581
8582 f = fopen(c->configfile,"rb");
8583 if (f==NULL) {
8584 fprintf(stderr,"Could not open %s; using default configuration.\n",
8585 c->configfile);
8586 load_default_config(c);
8587 } else {
8588 fseek(f,0,SEEK_END);
8589 flen = ftell(f);
8590 fseek(f,0,SEEK_SET);
8591 c->configdata = (char *)malloc(5+flen);
8592 outc = c->configdata;
8593 printf("Loading %s...\n",c->configfile);
8594 for (;;) {
8595 fgets(thisline,190,f);
8596 if (feof(f)) break;
8597 if (strlen(thisline)>180) {
8598 fprintf(stderr,"Line too long in %s: %s\n",c->configfile,thisline);
8599 exit(110);
8600 }
8601 for (inc=thisline;*inc;inc++) {
8602 if (*inc==';') break;
8603 if (strchr(" \t\n\r",*inc)) {
8604 if (!blankmode) *(outc++) = '\0';
8605 blankmode = TRUE;
8606 } else {
8607 *(outc++) = *inc;
8608 blankmode = FALSE;
8609 }
8610 } /* Done with line */
8611 } /* Done reading file */
8612 if (!blankmode) *(outc++) = '\0';
8613 *(outc++) = '\0';
8614 printf("Loaded.\n");
8615 c->configdata = (char *)realloc(c->configdata,1+(outc-c->configdata));
8616 fclose(f);
8617 }
8618 #if 0
8619 for (inc=c->configdata;*inc;inc+=1+strlen(inc))
8620 printf("%s ",inc);
8621 #endif
8622 return;
8623 }
8624
8625 /* Free up config-file resources */
unload_config(config * c)8626 void unload_config(config *c)
8627 {
8628 if (c->configdata) free(c->configdata);
8629 c->configdata = NULL;
8630 return;
8631 }
8632
8633 /* Look through the config's config file, and fill in values for */
8634 /* the switch lines therein. Return FALSE if error. These are */
8635 /* of course overridable by command-line switches. */
read_switches(config * c)8636 boolean read_switches(config *c)
8637 {
8638 /* Dis here is a STUB */
8639 return TRUE;
8640 }
8641
8642 /* Allocate and return a new, empty construct */
new_construct(config * c)8643 construct *new_construct(config *c)
8644 {
8645 construct *answer = (construct *)malloc(sizeof(*answer));
8646
8647 answer->height = 64;
8648 answer->gamemask = DOOM1_BIT|DOOM0_BIT|DOOM2_BIT|DOOMI_BIT|DOOMC_BIT;
8649 answer->compatible = 0;
8650 answer->texture_cell_anchor = NULL;
8651 answer->flat_cell_anchor = NULL;
8652 answer->family = 0;
8653 answer->marked = FALSE;
8654 answer->next = c->construct_anchor;
8655 c->construct_anchor = answer;
8656 return answer;
8657 }
8658
add_flat_cell(construct * cn,char * name,config * c)8659 flat_cell *add_flat_cell(construct *cn,char *name,config *c)
8660 {
8661 flat_cell *answer = (flat_cell *)malloc(sizeof(*answer));
8662
8663 answer->flat = find_flat(c,name);
8664 answer->next = cn->flat_cell_anchor;
8665 cn->flat_cell_anchor = answer;
8666 return answer;
8667 }
8668
add_texture_cell(construct * cn,char * name,boolean primary,short y1,short y2,config * c)8669 texture_cell *add_texture_cell(construct *cn,char *name,boolean primary,
8670 short y1, short y2,config *c)
8671 {
8672 texture_cell *answer = (texture_cell *)malloc(sizeof(*answer));
8673
8674 answer->texture = find_texture(c,name);
8675 answer->width = 128; /* A nicer default, as it happens */
8676 answer->y_offset1 = y1;
8677 answer->y_offset2 = y2;
8678 answer->primary = primary;
8679 answer->marked = FALSE;
8680 answer->next = cn->texture_cell_anchor;
8681 cn->texture_cell_anchor = answer;
8682 return answer;
8683 }
8684
8685 #ifdef CONFIG_DUMP
8686
8687 #ifndef CONFIG_DUMP_VERBOSE_NOT
8688
dump_foo_themebits(themebits yes,themebits no,char * tag,config * c)8689 void dump_foo_themebits(themebits yes, themebits no, char *tag, config *c)
8690 {
8691 themebits mask = 1;
8692 theme *t = c->theme_anchor;
8693
8694 for (t=c->theme_anchor;t;t=t->next) {
8695 if ((yes&mask) && !(no&&mask)) printf("%s %s ",tag,t->name);
8696 mask <<=1;
8697 }
8698 }
8699
dump_foo_texture(texture * t,config * c)8700 void dump_foo_texture(texture *t,config *c)
8701 {
8702 printf("Texture %s ",t->name);
8703 if ((t->width!=256)||(t->height!=128))
8704 printf("size %d %d ",t->width,t->height);
8705 if (t->props&WALL) printf("wall ");
8706 if (t->props&SWITCH) printf("isswitch ");
8707 if (t->props&LIFT_TEXTURE) printf("lift ");
8708 if (t->props&SUPPORT) printf("support ");
8709 if (t->props&JAMB) printf("jamb ");
8710 if (t->props&STEP) printf("step ");
8711 if (t->props&GRATING) printf("grating ");
8712 if (t->props&PLAQUE) printf("plaque ");
8713 if (t->props&VTILES) printf("vtiles ");
8714 if (t->props&HALF_PLAQUE) printf("half_plaque ");
8715 if (t->props&LIGHT) printf("light ");
8716 if (t->props&EXITSWITCH) printf("exitswitch ");
8717 if (t->props&DOOR) printf("door ");
8718 if (t->props&GATE) printf("locked ");
8719 if (t->props&OUTDOOR) printf("outside ");
8720 if (t->props&RED) printf("red ");
8721 if (t->props&BLUE) printf("blue ");
8722 if (t->props&YELLOW) printf("yellow ");
8723 if (t==c->error_texture) printf("error ");
8724 if (t==c->gate_exitsign_texture) printf("gateexitsign ");
8725 dump_foo_themebits(t->core,0,"core",c);
8726 dump_foo_themebits(t->compatible,t->core,"comp",c);
8727 if (t->subtle)
8728 printf("subtle %s ",t->subtle->name);
8729 if (t->switch_texture)
8730 printf("switch %s ",t->switch_texture->name);
8731 if (t->realname != t->name)
8732 printf("realname %s ",t->realname);
8733 if (t->y_bias) printf("ybias %d ",t->y_bias);
8734 if (5!=t->y_hint) printf("yhint %d ",t->y_hint);
8735 if (!(t->gamemask&DOOM0_BIT)) printf("noDoom0 ");
8736 if (!(t->gamemask&DOOM1_BIT)) printf("noDoom1 ");
8737 if (!(t->gamemask&DOOM2_BIT)) printf("noDoom2 ");
8738 if (!(t->gamemask&DOOMI_BIT)) printf("custom " );
8739 if (!(t->gamemask&DOOMC_BIT)) printf("gross ");
8740 printf("\n");
8741
8742 }
8743
dump_foo_flat(flat * f,config * c)8744 void dump_foo_flat(flat *f,config *c)
8745 {
8746 printf("Flat %s ",f->name);
8747
8748 if (f->props&FLOOR) printf("floor ");
8749 if (f->props&CEILING) printf("ceiling ");
8750 if (f->props&LIGHT) printf("light ");
8751 if (f->props&NUKAGE) printf("nukage ");
8752 if (f->props&OUTDOOR) printf("outside ");
8753 if (f->props&GATE) printf("gate ");
8754 if (f->props&RED) printf("red ");
8755 if (f==c->sky_flat) printf("sky ");
8756 if (f==c->water_flat) printf("water ");
8757 dump_foo_themebits(f->compatible,0,"comp",c);
8758 if (!(f->gamemask&DOOM0_BIT)) printf("noDoom0 ");
8759 if (!(f->gamemask&DOOM1_BIT)) printf("noDoom1 ");
8760 if (!(f->gamemask&DOOM2_BIT)) printf("noDoom2 ");
8761 if (!(f->gamemask&DOOMI_BIT)) printf("custom ");
8762 if (!(f->gamemask&DOOMC_BIT)) printf("gross ");
8763 printf("\n");
8764 }
8765
dump_foo_genus(genus * g,config * c)8766 void dump_foo_genus(genus *g,config *c)
8767 {
8768 if (g->compatible==0) return;
8769 if (g->compatible==~(unsigned long)0) return;
8770 printf("Thing %d ",g->thingid);
8771 dump_foo_themebits(g->compatible,0,"comp",c);
8772 printf("\n");
8773 }
8774
dump_foo_construct(construct * x,config * c)8775 void dump_foo_construct(construct *x,config *c)
8776 {
8777 texture_cell *tc;
8778 flat_cell *fc;
8779 printf("Construct family %d height %d ",x->family,x->height);
8780
8781 dump_foo_themebits(x->compatible,0,"comp",c);
8782 if (!(x->gamemask&DOOM0_BIT)) printf("noDoom0 ");
8783 if (!(x->gamemask&DOOM1_BIT)) printf("noDoom1 ");
8784 if (!(x->gamemask&DOOM2_BIT)) printf("noDoom2 ");
8785 if (!(x->gamemask&DOOMI_BIT)) printf("custom ");
8786 if (!(x->gamemask&DOOMC_BIT)) printf("gross ");
8787 printf("\n");
8788 printf(" ");
8789 for (fc=x->flat_cell_anchor;fc;fc=fc->next) {
8790 printf("top %s ",fc->flat->name);
8791 }
8792 printf("\n");
8793 for (tc=x->texture_cell_anchor;tc;tc=tc->next) {
8794 printf(" %sary %s ",tc->primary?"Prim":"Second",tc->texture->name);
8795 if (tc->y_offset1 || tc->y_offset2)
8796 printf("yoffsets %d %d ",tc->y_offset1,tc->y_offset2);
8797 if (tc->width!=128) printf("width %d ",tc->width);
8798 printf("\n");
8799 }
8800 }
8801
8802 #else
8803
dump_foo_texture(texture * t,config * c)8804 void dump_foo_texture(texture *t,config *c)
8805 {
8806 printf("t %s ",t->name);
8807 if ((t->width!=256)||(t->height!=128))
8808 printf("z %d %d ",t->width,t->height);
8809 if (t->props&WALL) printf("w ");
8810 if (t->props&SWITCH) printf("i ");
8811 if (t->props&LIFT_TEXTURE) printf("F ");
8812 if (t->props&SUPPORT) printf("I ");
8813 if (t->props&JAMB) printf("j ");
8814 if (t->props&STEP) printf("e ");
8815 if (t->props&GRATING) printf("g ");
8816 if (t->props&PLAQUE) printf("p ");
8817 if (t->props&VTILES) printf("v ");
8818 if (t->props&HALF_PLAQUE) printf("H ");
8819 if (t->props&LIGHT) printf("l ");
8820 if (t->props&EXITSWITCH) printf("E ");
8821 if (t->props&DOOR) printf("d ");
8822 if (t->props&GATE) printf("L ");
8823 if (t->props&OUTDOOR) printf("o ");
8824 if (t->props&RED) printf("r ");
8825 if (t->props&BLUE) printf("b ");
8826 if (t->props&YELLOW) printf("y ");
8827 if (t==c->error_texture) printf("! ");
8828 if (t==c->gate_exitsign_texture) printf("X ");
8829 dump_foo_themebits(t->core,0,"C",c);
8830 dump_foo_themebits(t->compatible,t->core,"c",c);
8831 if (t->subtle)
8832 printf("S %s ",t->subtle->name);
8833 if (t->switch_texture)
8834 printf("s %s ",t->switch_texture->name);
8835 if (t->realname != t->name)
8836 printf("= %s ",t->realname);
8837 if (t->y_bias) printf("Y %d ",t->y_bias);
8838 if (5!=t->y_hint) printf("@ %d ",t->y_hint);
8839 if (!(t->gamemask&DOOM0_BIT)) printf("0 ");
8840 if (!(t->gamemask&DOOM1_BIT)) printf("1 ");
8841 if (!(t->gamemask&DOOM2_BIT)) printf("2 ");
8842 if (!(t->gamemask&DOOMI_BIT)) printf("u " );
8843 if (!(t->gamemask&DOOMC_BIT)) printf("Q ");
8844 printf("\n");
8845
8846 }
8847
dump_foo_flat(flat * f,config * c)8848 void dump_foo_flat(flat *f,config *c)
8849 {
8850 printf("f %s ",f->name);
8851
8852 if (f->props&FLOOR) printf("D ");
8853 if (f->props&CEILING) printf("U ");
8854 if (f->props&LIGHT) printf("l ");
8855 if (f->props&NUKAGE) printf("n ");
8856 if (f->props&OUTDOOR) printf("o ");
8857 if (f->props&GATE) printf("G ");
8858 if (f->props&RED) printf("r ");
8859 if (f==c->sky_flat) printf("K ");
8860 if (f==c->water_flat) printf("W ");
8861 dump_foo_themebits(f->compatible,0,"c",c);
8862 if (!(f->gamemask&DOOM0_BIT)) printf("0 ");
8863 if (!(f->gamemask&DOOM1_BIT)) printf("1 ");
8864 if (!(f->gamemask&DOOM2_BIT)) printf("2 ");
8865 if (!(f->gamemask&DOOMI_BIT)) printf("u " );
8866 if (!(f->gamemask&DOOMC_BIT)) printf("G ");
8867 printf("\n");
8868 }
8869
dump_foo_genus(genus * g,config * c)8870 void dump_foo_genus(genus *g,config *c)
8871 {
8872 if (g->compatible==0) return;
8873 printf(". %d ",g->thingid);
8874 dump_foo_themebits(g->compatible,0,"c",c);
8875 printf("\n");
8876 }
8877
dump_foo_construct(construct * x,config * c)8878 void dump_foo_construct(construct *x,config *c)
8879 {
8880 texture_cell *tc;
8881 flat_cell *fc;
8882 printf("x m %d h %d ",x->family,x->height);
8883
8884 dump_foo_themebits(x->compatible,0,"c",c);
8885 if (!(x->gamemask&DOOM0_BIT)) printf("0 ");
8886 if (!(x->gamemask&DOOM1_BIT)) printf("1 ");
8887 if (!(x->gamemask&DOOM2_BIT)) printf("2 ");
8888 if (!(x->gamemask&DOOMI_BIT)) printf("u ");
8889 if (!(x->gamemask&DOOMC_BIT)) printf("G ");
8890 printf("\n");
8891 for (fc=x->flat_cell_anchor;fc;fc=fc->next) {
8892 printf("O %s ",fc->flat->name);
8893 }
8894 printf("\n");
8895 for (tc=x->texture_cell_anchor;tc;tc=tc->next) {
8896 printf("%s %s ",tc->primary?"A":"B",tc->texture->name);
8897 if (tc->y_offset1 || tc->y_offset2)
8898 printf("] %d %d ",tc->y_offset1,tc->y_offset2);
8899 if (tc->width!=128) printf("~ %d ",tc->width);
8900 printf("\n");
8901 }
8902 }
8903
8904 #endif
8905
8906
dump_foo(config * c)8907 void dump_foo(config *c)
8908 {
8909 texture *t;
8910 flat *f;
8911 construct *x;
8912 genus *g;
8913 for (t=c->texture_anchor;t;t=t->next) dump_foo_texture(t,c);
8914 for (f=c->flat_anchor;f;f=f->next) dump_foo_flat(f,c);
8915 for (x=c->construct_anchor;x;x=x->next) dump_foo_construct(x,c);
8916 for (g=c->genus_anchor;g;g=g->next) dump_foo_genus(g,c);
8917 }
8918
8919 #endif
8920
8921 /* Get the hardwired nonswitch-nontheme config stuff (like */
8922 /* monsters and obstables and such. */
hardwired_nonswitch_nontheme_config(config * c)8923 boolean hardwired_nonswitch_nontheme_config(config *c)
8924 {
8925 genus *m;
8926
8927 /* get these obstacles registered as non-pickables */
8928 m = find_genus(c,ID_LAMP);
8929 m->bits &= ~PICKABLE;
8930 m->bits |= LIGHT;
8931 m->width = 33;
8932 m = find_genus(c,ID_ELEC);
8933 m->bits &= ~PICKABLE;
8934 m->bits |= LIGHT;
8935 m->width = 33;
8936 m->height = 127; /* About */
8937 m = find_genus(c,ID_LAMP2);
8938 m->bits &= ~PICKABLE;
8939 m->bits |= LIGHT;
8940 m->width = 33;
8941 m->gamemask = DOOM2_BIT | DOOMC_BIT | DOOMI_BIT;
8942 m = find_genus(c,ID_TLAMP2);
8943 m->bits &= ~PICKABLE;
8944 m->bits |= LIGHT;
8945 m->width = 33;
8946 m->height = 72; /* Very roughly */
8947 m->gamemask = DOOM2_BIT | DOOMC_BIT | DOOMI_BIT;
8948 m = find_genus(c,ID_SHORTRED);
8949 m->bits &= ~PICKABLE;
8950 m->bits |= LIGHT;
8951 m->width = 33;
8952 m = find_genus(c,ID_SHORTBLUE);
8953 m->bits &= ~PICKABLE;
8954 m->bits |= LIGHT;
8955 m->width = 33;
8956 m = find_genus(c,ID_SHORTGREEN);
8957 m->bits &= ~PICKABLE;
8958 m->bits |= LIGHT;
8959 m->width = 33;
8960 m = find_genus(c,ID_TALLRED);
8961 m->bits &= ~PICKABLE;
8962 m->bits |= LIGHT;
8963 m->width = 33;
8964 m->height = 127; /* sure */
8965 m = find_genus(c,ID_TALLBLUE);
8966 m->bits &= ~PICKABLE;
8967 m->bits |= LIGHT;
8968 m->width = 33;
8969 m->height = 127; /* sure */
8970 m = find_genus(c,ID_TALLGREEN);
8971 m->bits &= ~PICKABLE;
8972 m->bits |= LIGHT;
8973 m->width = 33;
8974 m->height = 127; /* sure */
8975 m = find_genus(c,ID_CBRA);
8976 m->bits &= ~PICKABLE;
8977 m->bits |= LIGHT;
8978 m->width = 33;
8979 m->height = 72; /* about */
8980 m = find_genus(c,ID_FBARREL);
8981 m->gamemask = DOOM2_BIT | DOOMC_BIT | DOOMI_BIT;
8982 m->bits &= ~PICKABLE;
8983 m->bits |= LIGHT;
8984 m->width = 33;
8985 m = find_genus(c,ID_BARREL);
8986 m->bits &= ~PICKABLE;
8987 m->bits |= EXPLODES;
8988 m->width = 33;
8989 /* pretend the candle is pickable; really just "not blocking" */
8990 m = find_genus(c,ID_CANDLE);
8991 m->bits |= PICKABLE;
8992 m->bits |= LIGHT;
8993 m->width = 16;
8994 /* and register the weapons and ammos and healths */
8995 /* at least the ones that we need for arenas! */
8996 m = find_genus(c,ID_ROCKBOX);
8997 m->bits |= AMMO;
8998 m->ammo_provides = (float)500;
8999 m = find_genus(c,ID_BULBOX);
9000 m->bits |= AMMO;
9001 m->ammo_provides = (float)500;
9002 m = find_genus(c,ID_CELLPACK);
9003 m->bits |= AMMO;
9004 m->ammo_provides = (float)2000; /* Hoo-hoo! Same for BFG as plasgun? */
9005 /* violence and mayhem */
9006 c->usualammo[ITYTD] = 5000;
9007 c->usualammo[HMP] = 3500; /* or 3k? */
9008 c->usualammo[UV] = 3500; /* or 2k? or 3k? */
9009 c->usualarmor[ITYTD] = 100;
9010 c->usualarmor[HMP] = 50;
9011 c->usualarmor[UV] = 30; /* or 20 */
9012 c->usualhealth[ITYTD] = 80;
9013 c->usualhealth[HMP] = 65;
9014 c->usualhealth[UV] = 55;
9015 c->minhealth[ITYTD] = 50;
9016 c->minhealth[HMP] = 35;
9017 c->minhealth[UV] = 20;
9018 /* We have a level progression:
9019 * 1) Trooper (non-shooting zombie)
9020 * 1) Imp (cobra-like think in FreeDoom)
9021 * 2) Sargent (shooting zombie)
9022 * 3) Pink (Dog/Demon thing)
9023 * 5) Commando (thing with chaingun)
9024 * 6) Skull (floating head)
9025 * 7) Spectre (Invisible dog/demon thing)
9026 * 11) Head
9027 */
9028 /* Description of monsters */
9029 m = find_monster(c,ID_TROOPER);
9030 m->width = 42;
9031 m->ammo_provides = (float)100;
9032 m->ammo_to_kill[ITYTD] = (float)55;
9033 m->ammo_to_kill[HMP] = (float)35;
9034 m->ammo_to_kill[UV] = (float)30;
9035 m->damage[ITYTD] = (float)15;
9036 m->damage[HMP] = (float)3;
9037 m->damage[UV] = (float)1;
9038 m->altdamage[ITYTD] = (float)10;
9039 m->altdamage[HMP] = (float)1;
9040 m->altdamage[UV] = (float)1;
9041 m->bits |= SHOOTS;
9042 m->min_level = 1;
9043 m->in_freedoom = TRUE;
9044 /* Tropper with gun */
9045 m = find_monster(c,ID_SERGEANT);
9046 m->width = 42;
9047 m->ammo_provides = (float)280;
9048 m->ammo_to_kill[ITYTD] = (float)80;
9049 m->ammo_to_kill[HMP] = (float)50;
9050 m->ammo_to_kill[UV] = (float)40;
9051 m->damage[ITYTD] = (float)25;
9052 m->damage[HMP] = (float)6;
9053 m->damage[UV] = (float)2;
9054 m->altdamage[ITYTD] = (float)20;
9055 m->altdamage[HMP] = (float)2;
9056 m->altdamage[UV] = (float)1;
9057 m->bits |= SHOOTS;
9058 m->min_level = 2;
9059 m->in_freedoom = TRUE;
9060 /* Cobra-like thing in FreeDoom */
9061 m = find_monster(c,ID_IMP);
9062 m->width = 42;
9063 m->ammo_provides = (float)0;
9064 m->ammo_to_kill[ITYTD] = (float)160;
9065 m->ammo_to_kill[HMP] = (float)95;
9066 m->ammo_to_kill[UV] = (float)80;
9067 m->damage[ITYTD] = (float)20;
9068 m->damage[HMP] = (float)6;
9069 m->damage[UV] = (float)3;
9070 m->altdamage[ITYTD] = (float)20;
9071 m->altdamage[HMP] = (float)5;
9072 m->altdamage[UV] = (float)2;
9073 m->bits |= SHOOTS;
9074 m->min_level = 1;
9075 m->in_freedoom = TRUE;
9076 /* Pink dog/demon */
9077 m = find_monster(c,ID_PINK);
9078 m->width = 62;
9079 m->ammo_provides = (float)0;
9080 m->ammo_to_kill[ITYTD] = (float)385;
9081 m->ammo_to_kill[HMP] = (float)236;
9082 m->ammo_to_kill[UV] = (float)195;
9083 m->damage[ITYTD] = (float)25;
9084 m->damage[HMP] = (float)10;
9085 m->damage[UV] = (float)8;
9086 m->altdamage[ITYTD] = (float)20;
9087 m->altdamage[HMP] = (float)8;
9088 m->altdamage[UV] = (float)4;
9089 m->min_level = 3;
9090 m->in_freedoom = TRUE;
9091 /* Invisible dog/demon */
9092 m = find_monster(c,ID_SPECTRE);
9093 m->width = 62;
9094 m->ammo_provides = (float)0;
9095 m->ammo_to_kill[ITYTD] = (float)410;
9096 m->ammo_to_kill[HMP] = (float)260;
9097 m->ammo_to_kill[UV] = (float)220;
9098 m->damage[ITYTD] = (float)25;
9099 m->damage[HMP] = (float)10;
9100 m->damage[UV] = (float)8;
9101 m->altdamage[ITYTD] = (float)25;
9102 m->altdamage[HMP] = (float)8;
9103 m->altdamage[UV] = (float) 6;
9104 m->min_level = 7;
9105 m->in_freedoom = TRUE;
9106 /* Floating head */
9107 m = find_monster(c,ID_SKULL);
9108 m->width = 34;
9109 m->bits |= BIG; /* Well, sort of! */
9110 m->ammo_provides = (float)0;
9111 m->ammo_to_kill[ITYTD] = (float)260;
9112 m->ammo_to_kill[HMP] = (float)165;
9113 m->ammo_to_kill[UV] = (float)130;
9114 m->damage[ITYTD] = (float)22;
9115 m->damage[HMP] = (float)8;
9116 m->damage[UV] = (float)5;
9117 m->altdamage[ITYTD] = (float)18;
9118 m->altdamage[HMP] = (float)5;
9119 m->altdamage[UV] = (float)2;
9120 m->bits |= FLIES;
9121 m->min_level = 6;
9122 m->in_freedoom = TRUE;
9123 /* Spider thing (I think) */
9124 m = find_monster(c,ID_HEAD);
9125 m->width = 63; /* Or 62 or maybe 64 */
9126 m->bits |= BIG;
9127 m->ammo_provides = (float)0;
9128 m->ammo_to_kill[ITYTD] = (float)1050;
9129 m->ammo_to_kill[HMP] = (float)630;
9130 m->ammo_to_kill[UV] = (float)590;
9131 m->damage[ITYTD] = (float)60;
9132 m->damage[HMP] = (float)35;
9133 m->damage[UV] = (float)18;
9134 m->altdamage[ITYTD] = (float)50;
9135 m->altdamage[HMP] = (float)20;
9136 m->altdamage[UV] = (float)10;
9137 m->bits |= SHOOTS;
9138 m->bits |= FLIES;
9139 m->min_level = 11;
9140 m->in_freedoom = TRUE;
9141
9142 m = find_monster(c,ID_BARON);
9143 m->width = 50; /* Roughly */
9144 m->height = 64;
9145 m->bits |= BIG | BOSS; /* Not placed randomly */
9146 m->ammo_provides = (float)0;
9147 m->ammo_to_kill[ITYTD] = (float)1900; /* Numbers are all guesses; fix */
9148 m->ammo_to_kill[HMP] = (float)1600;
9149 m->ammo_to_kill[UV] = (float)1500;
9150 m->damage[ITYTD] = (float)80;
9151 m->damage[HMP] = (float)40;
9152 m->damage[UV] = (float)25;
9153 m->altdamage[ITYTD] = (float)70;
9154 m->altdamage[HMP] = (float)25;
9155 m->altdamage[UV] = (float)18;
9156 m->bits |= SHOOTS;
9157 m->min_level = 12;
9158 m->in_freedoom = FALSE;
9159
9160 /* Other bosses; need to fill in data! */
9161 m = find_monster(c,ID_CYBER);
9162 m->width = 84;
9163 m->height = 110;
9164 m->bits |= BIG | BOSS;
9165 m->ammo_provides = (float)0;
9166 m->ammo_to_kill[ITYTD] = (float)8000; /* Numbers are all guesses; fix */
9167 m->ammo_to_kill[HMP] = (float)6500;
9168 m->ammo_to_kill[UV] = (float)6200;
9169 m = find_monster(c,ID_SPIDERBOSS);
9170 m->width = 260;
9171 m->height = 100;
9172 m->bits |= BIG | BOSS;
9173 m->ammo_provides = (float)0;
9174 m->ammo_to_kill[ITYTD] = (float)6000; /* Numbers are all guesses; fix */
9175 m->ammo_to_kill[HMP] = (float)5000;
9176 m->ammo_to_kill[UV] = (float)4500;
9177 m->min_level=17;
9178 m->in_freedoom = FALSE;
9179
9180 /* DOOM2 monsters */
9181 if (!(c->gamemask&(DOOM0_BIT|DOOM1_BIT))) {
9182 m = find_monster(c,ID_NAZI);
9183 m->gamemask = DOOM2_BIT;
9184 m->width = 42;
9185 m->ammo_to_kill[ITYTD] = (float)117;
9186 m->ammo_to_kill[HMP] = (float)78;
9187 m->ammo_to_kill[UV] = (float)65;
9188 m->damage[ITYTD] = (float)40;
9189 m->damage[HMP] = (float)14;
9190 m->damage[UV] = (float)7;
9191 m->altdamage[ITYTD] = (float)27;
9192 m->altdamage[HMP] = (float)10;
9193 m->altdamage[UV] = (float)4;
9194 m->bits |= SHOOTS | SPECIAL;
9195 m->min_level = 1;
9196 m->in_freedoom = TRUE;
9197 m = find_monster(c,ID_COMMANDO);
9198 m->gamemask = DOOM2_BIT;
9199 m->width = 42;
9200 m->ammo_provides = (float)100;
9201 m->ammo_to_kill[ITYTD] = (float)155;
9202 m->ammo_to_kill[HMP] = (float)106;
9203 m->ammo_to_kill[UV] = (float)90;
9204 m->damage[ITYTD] = (float)60;
9205 m->damage[HMP] = (float)25;
9206 m->damage[UV] = (float)15;
9207 m->altdamage[ITYTD] = (float)40;
9208 m->altdamage[HMP] = (float)20;
9209 m->altdamage[UV] = (float)10;
9210 m->bits |= SHOOTS;
9211 m->min_level = 5;
9212 m->in_freedoom = TRUE;
9213 m = find_monster(c,ID_SKEL);
9214 m->gamemask = DOOM2_BIT;
9215 m->width = 42;
9216 m->bits |= BIG;
9217 m->ammo_provides = (float)0;
9218 m->ammo_to_kill[ITYTD] = (float)800;
9219 m->ammo_to_kill[HMP] = (float)500;
9220 m->ammo_to_kill[UV] = (float)400;
9221 m->damage[ITYTD] = (float)125;
9222 m->damage[HMP] = (float)70;
9223 m->damage[UV] = (float)40;
9224 m->altdamage[ITYTD] = (float)100;
9225 m->altdamage[HMP] = (float)40;
9226 m->altdamage[UV] = (float)25;
9227 m->bits |= SHOOTS;
9228 m->min_level = 7;
9229 m->in_freedoom = FALSE;
9230 m = find_monster(c,ID_HELL);
9231 m->gamemask = DOOM2_BIT;
9232 m->width = 50;
9233 m->bits |= BIG;
9234 m->ammo_provides = (float)0;
9235 m->ammo_to_kill[ITYTD] = (float)1400;
9236 m->ammo_to_kill[HMP] = (float)850;
9237 m->ammo_to_kill[UV] = (float)666;
9238 m->damage[ITYTD] = (float)140;
9239 m->damage[HMP] = (float)80;
9240 m->damage[UV] = (float)50;
9241 m->altdamage[ITYTD] = (float)120;
9242 m->altdamage[HMP] = (float)50;
9243 m->altdamage[UV] = (float)35;
9244 m->bits |= SHOOTS;
9245 m->min_level = 11;
9246 m->in_freedoom = FALSE;
9247
9248 /* DOOM2 bosses and underbosses; need to fill in data! */
9249 m = find_monster(c,ID_MANCUB);
9250 m->gamemask = DOOM2_BIT;
9251 m->width = 100;
9252 m->height = 64;
9253 m->bits |= BIG | BOSS;
9254 m->ammo_provides = (float)0;
9255 m->ammo_to_kill[ITYTD] = (float)100; /* Numbers are all guesses; fix */
9256 m->ammo_to_kill[HMP] = (float)50;
9257 m->ammo_to_kill[UV] = (float)40;
9258 m->min_level = 19;
9259 m->in_freedoom = TRUE;
9260 m = find_monster(c,ID_ARCHIE);
9261 m->gamemask = DOOM2_BIT;
9262 m->width = 42;
9263 m->height = 56;
9264 m->bits |= BIG | BOSS;
9265 m->ammo_provides = (float)0;
9266 m->ammo_to_kill[ITYTD] = (float)1300; /* Numbers are all guesses; fix */
9267 m->ammo_to_kill[HMP] = (float)1100;
9268 m->ammo_to_kill[UV] = (float)1000;
9269 m->min_level = 17;
9270 m->in_freedoom = TRUE;
9271 m = find_monster(c,ID_PAIN);
9272 m->gamemask = DOOM2_BIT;
9273 m->width = 63;
9274 m->bits |= BIG | BOSS;
9275 m->ammo_provides = (float)0;
9276 m->ammo_to_kill[ITYTD] = (float)1900; /* Numbers are all guesses; fix */
9277 m->ammo_to_kill[HMP] = (float)1600;
9278 m->ammo_to_kill[UV] = (float)1500;
9279 m = find_monster(c,ID_ARACH);
9280 m->gamemask = DOOM2_BIT;
9281 m->width = 130;
9282 m->bits |= BIG | BOSS;
9283 m->ammo_provides = (float)0;
9284 m->ammo_to_kill[ITYTD] = (float)100; /* Numbers are all guesses; fix */
9285 m->ammo_to_kill[HMP] = (float)50;
9286 m->ammo_to_kill[UV] = (float)30;
9287 m->min_level = 23;
9288 m->in_freedoom = TRUE;
9289 }
9290
9291 return TRUE;
9292 }
9293
9294
9295 /* Absorb a property-word, return the corresponding bit, and */
9296 /* update *r to point to the last string we used (but since all */
9297 /* the properties we handle are one token long, we never actually */
9298 /* change *r). */
absorb_propertybit(char ** r)9299 propertybits absorb_propertybit(char **r)
9300 {
9301 char *p;
9302
9303 p = *r;
9304
9305 if ((!stricmp(p,"wall")) || (!strcmp(p,"w"))) return WALL;
9306 if ((!stricmp(p,"isswitch")) || (!strcmp(p,"i"))) return SWITCH;
9307 if ((!stricmp(p,"lift")) || (!strcmp(p,"F"))) return LIFT_TEXTURE;
9308 if ((!stricmp(p,"support")) || (!strcmp(p,"I"))) return SUPPORT;
9309 if ((!stricmp(p,"jamb")) || (!strcmp(p,"j"))) return JAMB;
9310 if ((!stricmp(p,"step")) || (!strcmp(p,"e"))) return STEP;
9311 if ((!stricmp(p,"grating")) || (!strcmp(p,"g"))) return GRATING;
9312 if ((!stricmp(p,"plaque")) || (!strcmp(p,"p"))) return PLAQUE;
9313 if ((!stricmp(p,"vtiles")) || (!strcmp(p,"v"))) return VTILES;
9314 if ((!stricmp(p,"half_plaque")) || (!strcmp(p,"H"))) return HALF_PLAQUE;
9315 if ((!stricmp(p,"light")) || (!strcmp(p,"l"))) return LIGHT;
9316 if ((!stricmp(p,"exitswitch")) || (!strcmp(p,"E"))) return EXITSWITCH;
9317 if ((!stricmp(p,"door")) || (!strcmp(p,"d"))) return DOOR;
9318 if ((!stricmp(p,"locked")) || (!strcmp(p,"L"))) return GATE;
9319 if ((!stricmp(p,"outside")) || (!strcmp(p,"o"))) return OUTDOOR;
9320 if ((!stricmp(p,"red")) || (!strcmp(p,"r"))) return RED;
9321 if ((!stricmp(p,"blue")) || (!strcmp(p,"b"))) return BLUE;
9322 if ((!stricmp(p,"yellow")) || (!strcmp(p,"y"))) return YELLOW;
9323 if ((!stricmp(p,"floor")) || (!strcmp(p,"D"))) return FLOOR;
9324 if ((!stricmp(p,"ceiling")) || (!strcmp(p,"U"))) return CEILING;
9325 if ((!stricmp(p,"nukage")) || (!strcmp(p,"n"))) return NUKAGE;
9326 if ((!stricmp(p,"gate")) || (!strcmp(p,"G"))) return GATE;
9327
9328 return 0;
9329 }
9330
9331 /* Absorb a restriction-word, return the corresponding bit, and */
9332 /* update *r to point to the last string we used (but since all */
9333 /* the restrictions we handle are one token long, we never actually */
9334 /* change *r). */
absorb_gamebit(char ** r)9335 gamebits absorb_gamebit(char **r)
9336 {
9337 char *p;
9338
9339 p = *r;
9340
9341 if ((!stricmp(p,"nodoom0")) || (!strcmp(p,"0"))) return DOOM0_BIT;
9342 if ((!stricmp(p,"nodoom1")) || (!strcmp(p,"1"))) return DOOM1_BIT;
9343 if ((!stricmp(p,"nodoom2")) || (!strcmp(p,"2"))) return DOOM2_BIT;
9344 if ((!stricmp(p,"gross")) || (!strcmp(p,"Q"))) return DOOMC_BIT;
9345 if ((!stricmp(p,"custom")) || (!strcmp(p,"u"))) return DOOMI_BIT;
9346
9347 return 0;
9348 }
9349
9350 /* Absorb a Theme record from the config data, returning the last */
9351 /* string that we actually used. */
absorb_theme(char * p,config * c)9352 char *absorb_theme(char *p, config *c)
9353 {
9354 char *q, *name;
9355 theme *t;
9356 boolean b = FALSE;
9357
9358 p += 1+strlen(p); /* That's the name */
9359 name = p;
9360
9361 q = p + 1 + strlen(p);
9362 if ((!stricmp(q,"secret")) || (!strcmp(q,"?"))) {
9363 p = q;
9364 b = TRUE;
9365 }
9366
9367 t = new_theme(c,name,b);
9368
9369 return p;
9370 }
9371
9372 /* Return a themebit for the given name, or zero if none */
themebit_for_name(char * name,config * c)9373 themebits themebit_for_name(char *name, config *c)
9374 {
9375 theme *t;
9376 themebits answer = 1;
9377
9378 for (t=c->theme_anchor;t;t=t->next) {
9379 if (!stricmp(t->name,name)) return answer;
9380 answer <<= 1;
9381 }
9382 return 0;
9383 }
9384
9385 /* Absorb a parameter like "thing stringval", returning stringval. If */
9386 /* neither the short nor long "thing"s given match, return NULL. Update */
9387 /* *r to point to the last string we used. */
absorb_string(char ** r,char * ln,char * sn)9388 char *absorb_string(char **r,char *ln, char *sn)
9389 {
9390 /* Needs more error-checking. Input Is Evil. */
9391 if (stricmp(*r,ln) && strcmp(*r,sn)) return NULL;
9392 (*r) += 1+strlen(*r); /* That's the name */
9393 return *r;
9394
9395 }
9396
9397 /* Absorb a parameter like "yhint 5", etc etc etc, see above. */
absorb_short(char ** r,char * ln,char * sn,short * s)9398 boolean absorb_short(char **r,char *ln,char *sn,short *s)
9399 {
9400 char *v = absorb_string(r,ln,sn);
9401 if (v==NULL) return FALSE;
9402 *s = (short)atoi(v);
9403 return TRUE;
9404 }
9405
9406 /* Absorb a parameter like "size 5 6", etc etc etc, see above. */
absorb_two_shorts(char ** r,char * ln,char * sn,short * s,short * t)9407 boolean absorb_two_shorts(char **r,char *ln,char *sn,short *s,short *t)
9408 {
9409 char *v = absorb_string(r,ln,sn);
9410 if (v==NULL) return FALSE;
9411 *s = (short)atoi(v);
9412 *r += 1 + strlen(*r);
9413 *t = (short)atoi(*r);
9414 return TRUE;
9415 }
9416
9417 /* Absorb a Texture record from the config data, returning the last */
9418 /* string that we actually used. */
absorb_texture(char * p,config * c)9419 char *absorb_texture(char *p, config *c)
9420 {
9421 char *q, *name;
9422 short n,m;
9423 texture *t;
9424 propertybits pb;
9425 gamebits gb;
9426 themebits tb;
9427
9428 p += 1+strlen(p); /* That's the name */
9429 t = find_texture(c,p);
9430
9431 q = p;
9432 for (;;) {
9433 p=q;
9434 q = p + 1 + strlen(p);
9435 if (0 != (pb = absorb_propertybit(&q))) { t->props |= pb; continue; }
9436 if (0 != (gb = absorb_gamebit(&q))) { t->gamemask &= ~gb; continue; }
9437 if (NULL != (name = absorb_string(&q,"core","C"))) {
9438 tb = themebit_for_name(name,c);
9439 if (tb==0) {
9440 fprintf(stderr,"Unknown theme <%s> in core.\n",name);
9441 exit(129);
9442 }
9443 t->core |= tb;
9444 t->compatible |= tb;
9445 continue;
9446 }
9447 if (NULL != (name = absorb_string(&q,"comp","c"))) {
9448 tb = themebit_for_name(name,c);
9449 if (tb==0) {
9450 fprintf(stderr,"Unknown theme <%s> in comp.\n",name);
9451 exit(129);
9452 }
9453 t->compatible |= tb;
9454 continue;
9455 }
9456 if (NULL != (name = absorb_string(&q,"switch","s")))
9457 { t->switch_texture = find_texture(c,name); continue; }
9458 if (NULL != (name = absorb_string(&q,"subtle","S")))
9459 { t->subtle = find_texture(c,name); continue; }
9460 if (NULL != (name = absorb_string(&q,"realname","=")))
9461 { t->realname = strdup(name); continue; }
9462 if (absorb_short(&q,"yhint","@",&m)) { t->y_hint = m; continue; }
9463 if (absorb_short(&q,"ybias","Y",&m)) { t->y_bias = m; continue; }
9464 if (absorb_two_shorts(&q,"size","z",&m,&n))
9465 { t->width = m; t->height = n; continue; }
9466 if ((!stricmp(q,"error")) || (!stricmp(q,"!"))) {
9467 c->error_texture = t;
9468 continue;
9469 }
9470 if ((!stricmp(q,"gateexitsign")) || (!stricmp(q,"X"))) {
9471 c->gate_exitsign_texture = t;
9472 continue;
9473 }
9474 break;
9475 }
9476 return p;
9477 }
9478
9479 /* Absorb a Flat record from the config data, returning the last */
9480 /* string that we actually used. */
absorb_flat(char * p,config * c)9481 char *absorb_flat(char *p, config *c)
9482 {
9483 char *q, *name;
9484 flat *f;
9485 propertybits pb;
9486 gamebits gb;
9487 themebits tb;
9488
9489 p += 1+strlen(p); /* That's the name */
9490 f = find_flat(c,p);
9491
9492 q = p;
9493 for (;;) {
9494 p=q;
9495 q = p + 1 + strlen(p);
9496 if (0 != (pb = absorb_propertybit(&q))) { f->props |= pb; continue; }
9497 if (0 != (gb = absorb_gamebit(&q))) { f->gamemask &= ~gb; continue; }
9498 if (NULL != (name = absorb_string(&q,"comp","c"))) {
9499 tb = themebit_for_name(name,c);
9500 if (tb==0) {
9501 fprintf(stderr,"Unknown theme <%s> in comp.\n",name);
9502 exit(129);
9503 }
9504 f->compatible |= tb;
9505 continue;
9506 }
9507 if ((!stricmp(q,"sky")) || (!stricmp(q,"K"))) {
9508 c->sky_flat = f;
9509 continue;
9510 }
9511 if ((!stricmp(q,"water")) || (!stricmp(q,"W"))) {
9512 c->water_flat = f;
9513 continue;
9514 }
9515 break;
9516 }
9517 return p;
9518 }
9519
9520 /* Absorb a Thing record from the config data, returning the last */
9521 /* string that we actually used. */
absorb_thing(char * p,config * c)9522 char *absorb_thing(char *p, config *c)
9523 {
9524 char *q, *name;
9525 genus *g;
9526 themebits tb;
9527
9528 p += 1+strlen(p); /* That's the number */
9529 g = find_genus(c,atoi(p));
9530 if (g->compatible==~(unsigned long)0) g->compatible = 0;
9531
9532 q = p;
9533 for (;;) {
9534 p=q;
9535 q = p + 1 + strlen(p);
9536 if (NULL != (name = absorb_string(&q,"comp","c"))) {
9537 tb = themebit_for_name(name,c);
9538 if (tb==0) {
9539 fprintf(stderr,"Unknown theme <%s> in comp.\n",name);
9540 exit(129);
9541 }
9542 g->compatible |= tb;
9543 continue;
9544 }
9545 break;
9546 }
9547 return p;
9548 }
9549
9550 /* Absorb a cell subrecord of a construct record, returning TRUE if */
9551 /* there is one there, or FALSE if not. Update r to point to the */
9552 /* last string we actually used. */
absorb_cell(construct * x,char ** r,char * ln,char * sn,boolean b,config * c)9553 boolean absorb_cell(construct *x,char **r,char *ln,char *sn,boolean b,config *c)
9554 {
9555 char *p, *q, *name;
9556 texture_cell *tc;
9557 short o1 = 0;
9558 short o2 = 0;
9559 short width = 128;
9560 short s,t;
9561
9562 p=*r;
9563 if (stricmp(p,ln) && strcmp(p,sn)) return FALSE;
9564 p += 1+strlen(p); /* That's the name */
9565 name = p;
9566
9567 q = p;
9568 for (;;) {
9569 p=q;
9570 q = p + 1 + strlen(p);
9571 if (absorb_short(&q,"width","~",&s)) { width = s; continue; }
9572 if (absorb_two_shorts(&q,"yoffsets","]",&s,&t))
9573 { o1 = s; o2 = t; continue; }
9574 break;
9575 }
9576 *r = p;
9577
9578 tc = add_texture_cell(x,name,b,o1,o2,c);
9579 tc->width = width;
9580
9581 return TRUE;
9582 }
9583
9584 /* Absorb a Construct record from the config data, returning the last */
9585 /* string that we actually used. */
absorb_construct(char * p,config * c)9586 char *absorb_construct(char *p, config *c)
9587 {
9588 char *q, *name;
9589 short s;
9590 construct *x;
9591 gamebits gb;
9592 themebits tb;
9593
9594 x = new_construct(c);
9595
9596 q = p;
9597 for (;;) {
9598 p=q;
9599 q = p + 1 + strlen(p);
9600 if (absorb_short(&q,"family","m",&s)) { x->family = s; continue; }
9601 if (absorb_short(&q,"height","h",&s)) { x->height = s; continue; }
9602 if (0 != (gb = absorb_gamebit(&q))) { x->gamemask &= ~gb; continue; }
9603 if (NULL != (name = absorb_string(&q,"comp","c"))) {
9604 tb = themebit_for_name(name,c);
9605 if (tb==0) {
9606 fprintf(stderr,"Unknown theme <%s> in comp.\n",name);
9607 exit(129);
9608 }
9609 x->compatible |= tb;
9610 continue;
9611 }
9612 if (NULL != (name = absorb_string(&q,"top","O")))
9613 { add_flat_cell(x,name,c); continue; }
9614 if (absorb_cell(x,&q,"primary","A",TRUE,c)) continue;
9615 if (absorb_cell(x,&q,"secondary","B",FALSE,c)) continue;
9616 break;
9617 }
9618 return p;
9619 }
9620
9621
9622
9623 /* Look through the config's config file, and fill in values for */
9624 /* all the non-switch lines therein. Return FALSE if error. */
nonswitch_config(config * c)9625 boolean nonswitch_config(config *c)
9626 {
9627
9628 #ifndef USE_OLD_HARDWIRED_STUFF
9629
9630 char *p;
9631
9632 /* Skip to the "[THEMES]" section */
9633 for (p=c->configdata;*p;p+=1+strlen(p))
9634 if (!stricmp("[themes]",p)) break;
9635 if (!*p) {
9636 fprintf(stderr,"No [THEMES] section in config file.\n");
9637 exit(143);
9638 }
9639 p+=1+strlen(p);
9640 for (;*p;p+=1+strlen(p)) {
9641 if (p[0]=='[') break; /* End of section */
9642 if ((!strcmp(p,"T")) || (!stricmp(p,"theme")))
9643 p = absorb_theme(p,c);
9644 else if ((!strcmp(p,"t")) || (!stricmp(p,"texture")))
9645 p = absorb_texture(p,c);
9646 else if ((!strcmp(p,"f")) || (!stricmp(p,"flat")))
9647 p = absorb_flat(p,c);
9648 else if ((!strcmp(p,"x")) || (!stricmp(p,"construct")))
9649 p = absorb_construct(p,c);
9650 else if ((!strcmp(p,".")) || (!stricmp(p,"thing")))
9651 p = absorb_thing(p,c);
9652 else if ((!strcmp(p,"#")) || (!stricmp(p,"hardwired1")))
9653 hardwired_nonswitch_nontheme_config(c);
9654 else {
9655 fprintf(stderr,"Nonsensical token <%s> in config file.\n",p);
9656 exit(174);
9657 }
9658 }
9659
9660 #else
9661
9662 /* Read from the config file here! */
9663 hardwired_nonswitch_config(c);
9664
9665 #endif
9666
9667 #ifdef CONFIG_DUMP
9668 dump_foo(c);
9669 #endif
9670
9671 return TRUE;
9672 }
9673
9674 /* Random parts for the style, based on the config, and other */
9675 /* parts of the style. */
9676
9677 /* Return a random thing in the given style and config, */
9678 /* satisfying the given pmask, in the given height-range. */
9679 /* Ignore s if NULL. */
random_thing0(propertybits pmask,config * c,style * s,int minh,int maxh)9680 genus *random_thing0(propertybits pmask,config *c,style *s,int minh,int maxh)
9681 {
9682 int tmask = 0;
9683 int tcount;
9684 genus *answer;
9685
9686 if (s)
9687 tmask = 0x01 << s->theme_number;
9688
9689 tcount = 0;
9690 for (answer=c->genus_anchor;answer;answer=answer->next) {
9691 if ((answer->bits&pmask)!=pmask) continue;
9692 if (s) if (!(answer->compatible&tmask)) continue;
9693 if ( (answer->gamemask & c->gamemask) != c->gamemask ) continue;
9694 if (answer->height>maxh) continue;
9695 if (answer->height<minh) continue;
9696 tcount++;
9697 }
9698 if (tcount==0) {
9699 announce(NONE,"No compatible things for theme"); /* This is OK */
9700 return NULL;
9701 }
9702 tcount = 1 + roll(142,tcount);
9703 for (answer=c->genus_anchor;answer;answer=answer->next) {
9704 if ( ((answer->bits&pmask)==pmask) &&
9705 ((s==NULL)||(answer->compatible&tmask)) &&
9706 (answer->height <= maxh) &&
9707 (answer->height >= minh) &&
9708 ( (answer->gamemask & c->gamemask) == c->gamemask ) ) {
9709 tcount--;
9710 if (tcount==0) return answer;
9711 }
9712 }
9713 return NULL;
9714 }
9715
9716 /* Return a random flat in the given style and config, */
9717 /* satisfying the given pmask. If s is NULL, ignore it. */
random_flat0(propertybits pmask,config * c,style * s)9718 flat *random_flat0(propertybits pmask, config *c, style *s)
9719 {
9720 int fmask = 0;
9721 int fcount;
9722 flat *answer;
9723
9724 if (s)
9725 fmask = 0x01 << s->theme_number;
9726
9727 fcount = 0;
9728 for (answer=c->flat_anchor;answer;answer=answer->next) {
9729 if ((answer->props&pmask)!=pmask) continue;
9730 if (s) if (!(answer->compatible&fmask)) continue;
9731 if ( (answer->gamemask & c->gamemask) != c->gamemask ) continue;
9732 fcount++;
9733 }
9734 if (fcount==0) {
9735 announce(NONE,"No compatible flats for theme"); /* This is OK */
9736 return NULL;
9737 }
9738 fcount = 1 + roll(143,fcount);
9739 for (answer=c->flat_anchor;answer;answer=answer->next) {
9740 if ( ((answer->props&pmask)==pmask) &&
9741 ((s==NULL)||(answer->compatible&fmask)) &&
9742 ( (answer->gamemask & c->gamemask) == c->gamemask ) ) {
9743 fcount--;
9744 if (fcount==0) return answer;
9745 }
9746 }
9747 return NULL;
9748 }
9749
random_floor0(config * c,style * s)9750 flat *random_floor0(config *c, style *s)
9751 {
9752 return random_flat0(FLOOR,c,s);
9753 }
9754
random_gate(config * c,style * s)9755 flat *random_gate(config *c, style *s)
9756 {
9757 return random_flat0(GATE,c,s);
9758 }
9759
random_ceiling0(config * c,style * s)9760 flat *random_ceiling0(config *c, style *s)
9761 {
9762 return random_flat0(CEILING,c,s);
9763 }
9764
random_ceilinglight(config * c,style * s)9765 flat *random_ceilinglight(config *c, style *s)
9766 {
9767 return random_flat0(CEILING+LIGHT,c,s);
9768 }
9769
random_nukage1(config * c,style * s)9770 flat *random_nukage1(config *c,style *s)
9771 {
9772 return random_flat0(NUKAGE,c,s);
9773 }
9774
random_doorceiling(config * c,style * s)9775 flat *random_doorceiling(config *c,style *s)
9776 {
9777 if (rollpercent(256,50)) return s->ceiling0;
9778 else return random_ceiling0(c,s);
9779 }
9780
random_doorfloor(config * c,style * s)9781 flat *random_doorfloor(config *c,style *s)
9782 {
9783 if (rollpercent(257,50)) return s->floor0;
9784 else return random_floor0(c,s); /* stub */
9785 }
9786
random_stepfloor(config * c,style * s)9787 flat *random_stepfloor(config *c,style *s)
9788 {
9789 return random_doorfloor(c,s); /* stub stub */
9790 }
9791
9792 /* Return a random texture in the given style and config, */
9793 /* satisfying the given pmask. If s is NULL, ignore it. */
random_texture0(propertybits pmask,config * c,style * s)9794 texture *random_texture0(propertybits pmask, config *c, style *s)
9795 {
9796 int tmask = 0;
9797 int tcount;
9798 texture *answer;
9799
9800 if (s) {
9801 tmask = 0x01 << s->theme_number;
9802 }
9803
9804 tcount = 0;
9805 for (answer=c->texture_anchor;answer;answer=answer->next) {
9806 if ((answer->props&pmask)!=pmask) continue;
9807 if (s) if (!(answer->compatible&tmask)) continue;
9808 if ( (answer->gamemask & c->gamemask) != c->gamemask ) continue;
9809 tcount++;
9810 }
9811 if (tcount==0) {
9812 announce(NONE,"No compatible textures for theme"); /* It's OK! */
9813 return NULL;
9814 }
9815 tcount = 1 + roll(144,tcount);
9816 for (answer=c->texture_anchor;answer;answer=answer->next) {
9817 if ( ((answer->props&pmask)==pmask) &&
9818 ((s==NULL)||(answer->compatible&tmask)) &&
9819 ( (answer->gamemask & c->gamemask) == c->gamemask ) ) {
9820 tcount--;
9821 if (tcount==0) return answer;
9822 }
9823 }
9824 return NULL;
9825 }
9826
random_support0(config * c,style * s)9827 texture *random_support0(config *c, style *s)
9828 {
9829 return random_texture0(SUPPORT,c,s);
9830 }
9831
random_wall0(config * c,style * s)9832 texture *random_wall0(config *c, style *s)
9833 {
9834 int tmask, tcount;
9835 texture *answer;
9836
9837 tmask = 0x01 << s->theme_number;
9838
9839 if (rollpercent(258,80)) { /* Use a core wall texture */
9840 tcount = 0;
9841 for (answer=c->texture_anchor;answer;answer=answer->next) {
9842 if (!(answer->props&WALL)) continue;
9843 if (!(answer->core&tmask)) continue;
9844 if ( (answer->gamemask & c->gamemask) != c->gamemask ) continue;
9845 tcount++;
9846 }
9847 if (tcount==0) {
9848 announce(WARNING,"No core wall textures for theme");
9849 return c->error_texture;
9850 }
9851 tcount = 1 + roll(145,tcount);
9852 for (answer=c->texture_anchor;answer;answer=answer->next) {
9853 if ( (answer->props&WALL) && (answer->core&tmask) &&
9854 ( (answer->gamemask & c->gamemask) == c->gamemask ) ) {
9855 tcount--;
9856 if (tcount==0) return answer;
9857 }
9858 }
9859 } else { /* Use any compatible wall texture */
9860 return random_texture0(WALL,c,s);
9861 }
9862
9863 return NULL;
9864 }
9865
random_kickplate(config * c,style * s)9866 texture *random_kickplate(config *c, style *s)
9867 {
9868 return random_support0(c,s);
9869 #if 0
9870 return random_wall0(c,s); /* stub */
9871 #endif
9872 }
9873
random_stepfront(config * c,style * s)9874 texture *random_stepfront(config *c, style *s)
9875 {
9876 texture *answer;
9877 if (!rollpercent(259,c->p_use_steps))
9878 answer = random_kickplate(c,s);
9879 else answer = random_texture0(STEP,c,s);
9880 if (answer==NULL) answer = random_kickplate(c,s);
9881 return answer;
9882 }
9883
switch0_for(config * c,style * s)9884 texture *switch0_for(config *c, style *s)
9885 {
9886 if (s->wall0->switch_texture) {
9887 return s->wall0->switch_texture;
9888 } else {
9889 return random_texture0(SWITCH,c,s);
9890 }
9891 }
9892
random_doorjamb(config * c,style * s)9893 texture *random_doorjamb(config *c, style *s)
9894 {
9895 return random_texture0(JAMB,c,s);
9896 }
9897
random_redface(config * c,style * s)9898 texture *random_redface(config*c, style *s)
9899 {
9900 return random_texture0(RED,c,s);
9901 }
9902
random_blueface(config * c,style * s)9903 texture *random_blueface(config*c, style *s)
9904 {
9905 return random_texture0(BLUE,c,s);
9906 }
9907
random_yellowface(config * c,style * s)9908 texture *random_yellowface(config*c, style *s)
9909 {
9910 return random_texture0(YELLOW,c,s);
9911 }
9912
random_walllight(config * c,style * s)9913 texture *random_walllight(config*c, style *s)
9914 {
9915 return random_texture0(LIGHT,c,s);
9916 }
9917
random_liftface(config * c,style * s)9918 texture *random_liftface(config*c, style *s)
9919 {
9920 return random_texture0(LIFT_TEXTURE,c,s);
9921 }
9922
9923 /* should consult the lists in the config like the others do */
9924
9925
9926 /* Return a door-face that looks good on a wide door. */
random_widedoorface(config * c,style * s)9927 texture *random_widedoorface(config *c, style *s)
9928 {
9929 return random_widedoorface_ex(c,s,FALSE);
9930 }
9931
9932 /* Return a door-face that looks good on a wide door. If needhigh, */
9933 /* also require 128 high. */
random_widedoorface_ex(config * c,style * s,boolean needhigh)9934 texture *random_widedoorface_ex(config *c, style *s, boolean needhigh)
9935 {
9936 int tmask = 0;
9937 int tcount;
9938 texture *answer;
9939
9940 tmask = 0x01 << s->theme_number;
9941
9942 tcount = 0;
9943 for (answer=c->texture_anchor;answer;answer=answer->next) {
9944 if (!(answer->props&DOOR)) continue;
9945 if (answer->props&GATE) continue;
9946 if ((answer->width)<128) continue;
9947 if (needhigh && (answer->height<128)) continue;
9948 if (!(answer->compatible&tmask)) continue;
9949 if ( (answer->gamemask & c->gamemask) != c->gamemask ) continue;
9950 tcount++;
9951 }
9952 if (tcount==0) {
9953 announce(ERROR,"No wide doorfaces for theme"); /* Bad! */
9954 return c->error_texture;
9955 }
9956 tcount = 1 + roll(146,tcount);
9957 for (answer=c->texture_anchor;answer;answer=answer->next) {
9958 if ( (answer->props&DOOR) &&
9959 (!(answer->props&GATE)) &&
9960 (answer->width>=128) &&
9961 (!(needhigh && (answer->height<128))) &&
9962 (answer->compatible&tmask) &&
9963 ( (answer->gamemask & c->gamemask) == c->gamemask ) ) {
9964 tcount--;
9965 if (tcount==0) return answer;
9966 }
9967 }
9968 return c->error_texture;
9969 }
9970
9971 /* Return a door-face that looks good on a narrow door. */
random_narrowdoorface(config * c,style * s)9972 texture *random_narrowdoorface(config *c, style *s)
9973 {
9974 texture *answer = random_narrowdoorface_ex(c,s,FALSE);
9975 return answer;
9976 }
9977
9978 /* Return a door-face that looks good on a wide door. If needhigh, */
9979 /* also require 128 high. */
random_narrowdoorface_ex(config * c,style * s,boolean needhigh)9980 texture *random_narrowdoorface_ex(config *c, style *s, boolean needhigh)
9981 {
9982 int tmask = 0;
9983 int tcount;
9984 texture *answer;
9985
9986 tmask = 0x01 << s->theme_number;
9987
9988 tcount = 0;
9989 for (answer=c->texture_anchor;answer;answer=answer->next) {
9990 if (!(answer->props&DOOR)) continue;
9991 if (answer->props&GATE) continue;
9992 if ((answer->width)>=128) continue;
9993 if (needhigh && (answer->height<128)) continue;
9994 if (!(answer->compatible&tmask)) continue;
9995 if ( (answer->gamemask & c->gamemask) != c->gamemask ) continue;
9996 tcount++;
9997 }
9998 if (tcount==0) {
9999 announce(ERROR,"No narrow doorfaces for theme"); /* Bad! */
10000 return c->error_texture;
10001 }
10002 tcount = 1 + roll(147,tcount);
10003 for (answer=c->texture_anchor;answer;answer=answer->next) {
10004 if ( (answer->props&DOOR) &&
10005 (!(answer->props&GATE)) &&
10006 (answer->width<128) &&
10007 (!(needhigh && (answer->height<128))) &&
10008 (answer->compatible&tmask) &&
10009 ( (answer->gamemask & c->gamemask) == c->gamemask ) ) {
10010 tcount--;
10011 if (tcount==0) return answer;
10012 }
10013 }
10014 return c->error_texture;
10015 }
10016
10017
10018 /* Looks good wide, and is 128 high. Note this should only be called */
10019 /* after the style's "widedoorface" has been set. */
random_twdoorface(config * c,style * s)10020 texture *random_twdoorface(config *c, style *s)
10021 {
10022 if (s->widedoorface->height>=128) return s->widedoorface;
10023 return random_widedoorface_ex(c,s,TRUE);
10024
10025 }
10026
10027 /* Looks good narrow, and is 128 high. Note this should only be called */
10028 /* after the style's "narrowdoorface" has been set. */
random_tndoorface(config * c,style * s)10029 texture *random_tndoorface(config *c, style *s)
10030 {
10031 texture *answer;
10032 if (s->narrowdoorface->height>=128) answer = s->narrowdoorface;
10033 else answer = random_narrowdoorface_ex(c,s,TRUE);
10034 return answer;
10035 }
10036
10037 /* Special texture to use (if it fits) on locked doors */
10038 /* May return NULL for a given style */
random_lockdoorface(config * c,style * s)10039 texture *random_lockdoorface(config *c, style *s)
10040 {
10041 int tmask = 0;
10042 int tcount;
10043 texture *answer;
10044
10045 tmask = 0x01 << s->theme_number;
10046
10047 tcount = 0;
10048 for (answer=c->texture_anchor;answer;answer=answer->next) {
10049 if (!(answer->props&DOOR)) continue;
10050 if (!(answer->props&GATE)) continue;
10051 if (!(answer->compatible&tmask)) continue;
10052 if ( (answer->gamemask & c->gamemask) != c->gamemask ) continue;
10053 tcount++;
10054 }
10055 if (tcount==0) {
10056 announce(NONE,"No locked doorfaces for theme"); /* That's OK */
10057 return NULL;
10058 }
10059 tcount = 1 + roll(148,tcount);
10060 for (answer=c->texture_anchor;answer;answer=answer->next) {
10061 if ( (answer->props&DOOR) &&
10062 (answer->props&GATE) &&
10063 (answer->compatible&tmask) &&
10064 ( (answer->gamemask & c->gamemask) == c->gamemask ) ) {
10065 tcount--;
10066 if (tcount==0) return answer;
10067 }
10068 }
10069 return NULL;
10070 }
10071
random_grating(config * c,style * s)10072 texture *random_grating(config *c, style *s)
10073 {
10074 return random_texture0(GRATING,c,s);
10075 }
10076
random_plaque(config * c,style * s)10077 texture *random_plaque(config *c, style *s)
10078 {
10079 return random_texture0(PLAQUE,c,s);
10080 }
10081
10082 /* Return the angle (east is zero, north is ninety) of a thing */
10083 /* that's standing in the linedef, facing right from it. */
facing_along(int x1,int y1,int x2,int y2)10084 int facing_along(int x1, int y1, int x2, int y2)
10085 {
10086 int answer = facing_right_from(x1,y1,x2,y2);
10087 return (answer==270) ? 0 : (answer + 90);
10088 }
10089
10090 /* Return the angle (east is zero, north is ninety) of a thing */
10091 /* that's standing in the linedef, facing right from it. */
facing_right_from(int x1,int y1,int x2,int y2)10092 int facing_right_from(int x1, int y1, int x2, int y2)
10093 {
10094 /* Best at right angles and axis-parallel lines */
10095
10096 if (abs(x1-x2)<abs(y1-y2)) { /* More parallel to the Y-axis */
10097 if (y2>y1) { /* Up */
10098 return 0;
10099 } else { /* Down */
10100 return 180;
10101 }
10102 } else { /* to the X-axis */
10103 if (x2>x1) { /* rightward */
10104 return 270;
10105 } else {
10106 return 90;
10107 }
10108 }
10109 }
10110
10111 /* Return the angle (east is zero, north is ninety) of a thing */
10112 /* that's standing in the linedef, facing right from it. */
facing_right_from_ld(linedef * ld)10113 int facing_right_from_ld(linedef *ld)
10114 {
10115 /* could be a macro */
10116 return facing_right_from(ld->from->x,ld->from->y,ld->to->x,ld->to->y);
10117 }
10118
10119 /* Wall up the given clear-walled sector inside the given outer sector. */
10120 /* Or if innersec is NULL, put a void sector in there, and use the given */
10121 /* texture on the walls. If the four last pointers are given, returns */
10122 /* the four linedefs it makes. */
frame_innersec_ex(level * l,sector * oldsector,sector * innersec,texture * tm,texture * tu,texture * tl,int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4,config * c,linedef ** ld1,linedef ** ld2,linedef ** ld3,linedef ** ld4)10123 void frame_innersec_ex(level *l,sector *oldsector,sector *innersec,
10124 texture *tm, texture *tu, texture *tl,
10125 int x1,int y1,int x2,int y2,
10126 int x3,int y3,int x4,int y4,
10127 config *c,
10128 linedef **ld1, linedef **ld2, linedef **ld3, linedef **ld4)
10129 {
10130 linedef *ld;
10131 vertex *v0, *v1, *v2;
10132 short newflags;
10133
10134 if (innersec) newflags = TWO_SIDED;
10135 else newflags = 0;
10136 if (!tm) tm = c->null_texture;
10137
10138 v1 = new_vertex(l,x1,y1);
10139 v0 = v1;
10140 v2 = new_vertex(l,x2,y2);
10141 ld = new_linedef(l,v2,v1);
10142 ld->flags |= newflags;
10143 ld->right = new_sidedef(l,oldsector,c);
10144 ld->right->isBoundary = 0;
10145 ld->right->middle_texture = tm;
10146 if (innersec) {
10147 ld->left = new_sidedef(l,innersec,c);
10148 ld->left->middle_texture = tm;
10149 patch_upper(ld,tu,c);
10150 patch_lower(ld,tl,c);
10151 }
10152 if (ld1) *ld1 = ld;
10153
10154 v1 = v2;
10155 v2 = new_vertex(l,x3,y3);
10156 ld = new_linedef(l,v2,v1);
10157 ld->flags |= newflags;
10158 ld->right = new_sidedef(l,oldsector,c);
10159 ld->right->isBoundary = 0;
10160 ld->right->middle_texture = tm;
10161 if (innersec) {
10162 ld->left = new_sidedef(l,innersec,c);
10163 ld->left->middle_texture = tm;
10164 patch_upper(ld,tu,c);
10165 patch_lower(ld,tl,c);
10166 }
10167 if (ld2) *ld2 = ld;
10168
10169 v1 = v2;
10170 v2 = new_vertex(l,x4,y4);
10171 ld = new_linedef(l,v2,v1);
10172 ld->flags |= newflags;
10173 ld->right = new_sidedef(l,oldsector,c);
10174 ld->right->isBoundary = 0;
10175 ld->right->middle_texture = tm;
10176 if (innersec) {
10177 ld->left = new_sidedef(l,innersec,c);
10178 ld->left->middle_texture = tm;
10179 patch_upper(ld,tu,c);
10180 patch_lower(ld,tl,c);
10181 }
10182 if (ld3) *ld3 = ld;
10183
10184 v1 = v2;
10185 v2 = v0;
10186 ld = new_linedef(l,v2,v1);
10187 ld->flags |= newflags;
10188 ld->right = new_sidedef(l,oldsector,c);
10189 ld->right->isBoundary = 0;
10190 ld->right->middle_texture = tm;
10191 if (innersec) {
10192 ld->left = new_sidedef(l,innersec,c);
10193 ld->left->middle_texture = tm;
10194 patch_upper(ld,tu,c);
10195 patch_lower(ld,tl,c);
10196 }
10197 if (ld4) *ld4 = ld;
10198
10199 } /* frame_innersec_ex() */
10200
10201 /* The common axis-parallel case of frame_innersec. If the four pointers */
10202 /* are given, the first and third will be y-parallel, the second and */
10203 /* fourth x-parallel. */
parallel_innersec_ex(level * l,sector * oldsector,sector * innersec,texture * tm,texture * tu,texture * tl,int minx,int miny,int maxx,int maxy,config * c,linedef ** ld1,linedef ** ld2,linedef ** ld3,linedef ** ld4)10204 void parallel_innersec_ex(level *l,sector *oldsector,sector *innersec,
10205 texture *tm, texture *tu, texture *tl,
10206 int minx,int miny,int maxx,int maxy,config *c,
10207 linedef **ld1, linedef **ld2,
10208 linedef **ld3, linedef **ld4)
10209 {
10210 frame_innersec_ex(l,oldsector,innersec,tm,tu,tl,
10211 minx,miny,minx,maxy,maxx,maxy,maxx,miny,c,
10212 ld1,ld2,ld3,ld4);
10213 }
10214
10215 /* Your basic visual room-center embellishments */
10216 /* Square in the middle of the room with higher/lower/no ceiling, */
10217 /* possibly different floor, light level, nukage, etc. */
ceiling_effect(level * l,sector * oldsector,style * ThisStyle,haa * haa,config * c)10218 boolean ceiling_effect(level *l, sector *oldsector,
10219 style *ThisStyle, haa *haa, config *c)
10220 {
10221 int minx, miny, maxx, maxy, offset, deltah;
10222 genus *g = ThisStyle->lamp0;
10223 short thing_id;
10224 int beamsize, maxbeam;
10225 sector *innersec;
10226 boolean force_nukage = rollpercent(260,l->p_force_nukage);
10227 boolean force_sky = rollpercent(261,l->p_force_sky);
10228 boolean force_quad = rollpercent(262,15);
10229 boolean edge_lights = FALSE;
10230 boolean center_light = FALSE;
10231 texture *upt = oldsector->style->wall0;
10232
10233 if (g->height>(oldsector->ceiling_height-oldsector->floor_height))
10234 g = ThisStyle->shortlamp0;
10235 thing_id = g->thingid;
10236
10237 /* Only do this sometimes! */
10238 if (!(rollpercent(263,5)||force_nukage||force_sky)) return FALSE;
10239
10240 /* Find the inner sector corners */
10241 find_rec(l,oldsector,&minx,&miny,&maxx,&maxy);
10242 offset = maxx - minx;
10243 if (maxy - miny < offset) offset = maxy - miny;
10244 if (offset<96) return FALSE; /* No sense making a really teeny one */
10245 offset = 16 + roll(149,(offset>>1)-48);
10246 minx = minx + offset;
10247 miny = miny + offset;
10248 maxx = maxx - offset;
10249 maxy = maxy - offset;
10250
10251 /* Sometimes do four little effects */
10252 if ((maxx-minx)<144) force_quad = FALSE;
10253 if ((maxy-miny)<144) force_quad = FALSE;
10254 maxbeam = (maxx-minx)-128;
10255 if (((maxy-miny)-128)<maxbeam) maxbeam = (maxy-miny)-128;
10256 beamsize = 16+roll(150,maxbeam-15);
10257 if (beamsize>64) beamsize = 64;
10258 /* In that case, almost always force sky or nukage (why?) */
10259 if (force_quad && !force_nukage && !force_sky) {
10260 if (rollpercent(264,45)) force_nukage = TRUE;
10261 else if (rollpercent(265,82)) force_sky = TRUE;
10262 }
10263
10264 /* With the box effect, sometimes put a Thing or Things, */
10265 /* if there's room (don't want stuck monsters!) */
10266 if (rollpercent(266,50))
10267 if (maxx-minx>170)
10268 if (maxy-miny>170) {
10269 if (rollpercent(267,80)) {
10270 edge_lights = TRUE;
10271 } else {
10272 center_light = TRUE;
10273 }
10274 if (force_quad && rollpercent(268,50))
10275 edge_lights = center_light = TRUE;
10276 if (offset<(38+8+g->width))
10277 edge_lights = FALSE;
10278 if (force_quad && (beamsize<g->width))
10279 center_light = FALSE;
10280 if (edge_lights &&
10281 room_at(l,g,minx-8,miny-8,g->width,c) &&
10282 room_at(l,g,minx-8,maxy+8,g->width,c) &&
10283 room_at(l,g,maxx+8,miny-8,g->width,c) &&
10284 room_at(l,g,maxx+8,maxy+8,g->width,c) ) {
10285 new_thing(l,minx-8,miny-8,0,thing_id,7,c);
10286 new_thing(l,minx-8,maxy+8,0,thing_id,7,c);
10287 new_thing(l,maxx+8,miny-8,0,thing_id,7,c);
10288 new_thing(l,maxx+8,maxy+8,0,thing_id,7,c);
10289 announce(VERBOSE,"edgelights");
10290 }
10291 if (center_light &&
10292 room_at(l,g,minx+(maxx-minx)/2,miny+(maxy-miny)/2,g->width,c)) {
10293 new_thing(l,minx+(maxx-minx)/2,miny+(maxy-miny)/2,0,thing_id,7,c);
10294 announce(VERBOSE,"centerlight");
10295 }
10296 } /* end if big enough square for lights */
10297
10298 {
10299 char s[200];
10300 sprintf(s,"Ceiling effect between (%d,%d) and (%d,%d).",minx,miny,
10301 maxx,maxy);
10302 announce(VERBOSE,s);
10303 }
10304
10305 /* Make the sector itself */
10306 innersec = clone_sector(l,oldsector);
10307
10308 if (rollpercent(269,50)||force_sky) { /* Ceiling hole */
10309 innersec->ceiling_height += 16 * (1 + roll(151,3));
10310 innersec->light_level = l->outside_light_level - 20; /* Minus 20? */
10311 innersec->ceiling_flat = c->sky_flat;
10312 } else { /* Just a difference */
10313 innersec->ceiling_flat = random_ceiling0(c,ThisStyle);
10314 deltah = 32 - (roll(152,65));
10315 /* Don't lower the ceiling too near a wall (door)! */
10316 if ((offset<64)&&(deltah<0)) deltah = 0 - deltah;
10317 innersec->ceiling_height += deltah;
10318 if ((innersec->ceiling_height-innersec->floor_height)<64)
10319 innersec->ceiling_height = innersec->floor_height + 64;
10320 } /* end not a hole */
10321
10322 /* Fun recessed ceiling lights? */
10323 if (rollpercent(270,20))
10324 if (innersec->ceiling_height > oldsector->ceiling_height)
10325 if (oldsector->style->walllight!=NULL) {
10326 if (innersec->ceiling_height < oldsector->ceiling_height+16)
10327 innersec->ceiling_height = oldsector->ceiling_height+16;
10328 upt = oldsector->style->walllight;
10329 if (innersec->ceiling_flat!=c->sky_flat) {
10330 innersec->light_level = oldsector->light_level + 20;
10331 if (rollpercent(271,90))
10332 innersec->ceiling_flat = oldsector->ceiling_flat;
10333 }
10334 announce(VERBOSE,"Indirect lighting");
10335 }
10336
10337 /* If no light-level decision made yet, make one */
10338 if ((innersec->ceiling_flat != c->sky_flat) &&
10339 (upt == oldsector->style->wall0)) { /* Poor test! */
10340 innersec->light_level = oldsector->light_level + roll(153,41) - 20;
10341 if (innersec->light_level<100) innersec->light_level = 100; /* Minlight? */
10342 }
10343
10344 /* If not open-air, maybe make the lights blink/flash */
10345 if (innersec->ceiling_flat != c->sky_flat) {
10346 if (rollpercent(272,20)) switch (roll(154,4)) {
10347 case 0: innersec->special = RANDOM_BLINK; break;
10348 case 1: innersec->special = SYNC_FAST_BLINK; break;
10349 case 2: innersec->special = SYNC_SLOW_BLINK; break;
10350 case 3: innersec->special = GLOW_BLINK; break;
10351 }
10352 } /* end if light effects */
10353
10354 if (force_nukage||rollpercent(273,30)) { /* Floor thing also */
10355 innersec->floor_flat = random_floor0(c,ThisStyle);
10356 deltah = 24 - (roll(155,49));
10357 /* Don't raise the floor too near a wall (door)! */
10358 if ((offset<64)&&(deltah>0)) deltah = 0 - deltah;
10359 /* and if forced nukage, always lower the floor */
10360 if (force_nukage&&(deltah>0)) deltah = 0 - deltah;
10361 innersec->floor_height += deltah;
10362 if ((innersec->ceiling_height-innersec->floor_height)<64)
10363 innersec->floor_height = innersec->ceiling_height - 64;
10364 if ((oldsector->ceiling_height-innersec->floor_height)<64)
10365 innersec->floor_height = oldsector->ceiling_height - 64;
10366 if ((innersec->ceiling_height-oldsector->floor_height)<64)
10367 innersec->ceiling_height = oldsector->ceiling_height + 64;
10368
10369 /* And maybe some nukage! */
10370 if (deltah<0)
10371 if (force_nukage||rollpercent(274,30)) {
10372 announce(VERBOSE,"Nukage");
10373 innersec->floor_flat = ThisStyle->nukage1;
10374 innersec->special = NUKAGE1_SPECIAL;
10375 haa->haas[ITYTD].health -= 10;
10376 haa->haas[HMP].health -= 5;
10377 }
10378 } else {
10379 deltah = 0; /* Note we didn't do it */
10380 }
10381
10382 /* Make the necessary linedefs and sidedefs */
10383 if (force_quad) {
10384 int xsize = ((maxx - minx) - beamsize) / 2;
10385 int ysize = ((maxy - miny) - beamsize) / 2;
10386 parallel_innersec(l,oldsector,innersec,
10387 NULL,upt,oldsector->style->wall0,
10388 minx,miny,minx+xsize,miny+ysize,c);
10389 parallel_innersec(l,oldsector,innersec,
10390 NULL,upt,oldsector->style->wall0,
10391 minx,maxy-ysize,minx+xsize,maxy,c);
10392 parallel_innersec(l,oldsector,innersec,
10393 NULL,upt,oldsector->style->wall0,
10394 maxx-xsize,miny,maxx,miny+ysize,c);
10395 parallel_innersec(l,oldsector,innersec,
10396 NULL,upt,oldsector->style->wall0,
10397 maxx-xsize,maxy-ysize,maxx,maxy,c);
10398 } else if ( (maxx-minx>128) && (maxy-miny>128) &&
10399 ((maxx-minx)<=(2*(maxy-miny))) &&
10400 ((maxy-miny)<=(2*(maxx-minx))) &&
10401 rollpercent(275,10) ) {
10402 /* A diamond! Is this safe? Not axis-parallel! */
10403 announce(LOG,"Diamond");
10404 frame_innersec(l,oldsector,innersec,
10405 NULL,upt,oldsector->style->wall0,
10406 (minx+maxx)/2,miny,
10407 minx,(miny+maxy)/2,
10408 (minx+maxx)/2,maxy,
10409 maxx,(miny+maxy)/2,
10410 c);
10411 } else {
10412 /* Just an old fashioned square */
10413 linedef *ld1, *ld2, *ld3, *ld4;
10414 boolean fancied = FALSE;
10415 parallel_innersec_ex(l,oldsector,innersec,
10416 NULL,upt,oldsector->style->wall0,
10417 minx,miny,maxx,maxy,c,
10418 &ld1,&ld2,&ld3,&ld4);
10419 /* but maybe with fancy stuff! */
10420 /* Stairs to get out? */
10421 if ( (deltah<0) && (!fancied) &&
10422 no_monsters_stuck_on(l,ld1) &&
10423 no_monsters_stuck_on(l,ld2) &&
10424 no_monsters_stuck_on(l,ld4) &&
10425 rollpercent(276,l->p_deep_baths) ) {
10426 int xsize = maxx-minx;
10427 linedef *ld2new, *ld4new, *ldnew;
10428 sector *newsec;
10429 deltah = -24; /* 24? */
10430 innersec->floor_height = oldsector->floor_height + deltah;
10431 for (;xsize>=(128*l->hugeness);) {
10432 xsize -= 48 * l->hugeness;
10433 ld2new = ld2;
10434 ld2 = split_linedef(l,ld2,48*l->hugeness,c); /* 48? */
10435 ld4new = split_linedef(l,ld4,xsize,c);
10436 newsec = clone_sector(l,innersec);
10437 newsec->floor_flat = oldsector->floor_flat;
10438 innersec->floor_height += deltah;
10439 ld3->left->sector = newsec;
10440 ld2new->left->sector = newsec;
10441 ld4new->left->sector = newsec;
10442 ldnew = new_linedef(l,ld4new->from,ld2new->to);
10443 ldnew->left = new_sidedef(l,innersec,c);
10444 ldnew->right = new_sidedef(l,newsec,c);
10445 ldnew->flags |= TWO_SIDED;
10446 ldnew->right->middle_texture = c->null_texture;
10447 ldnew->left->middle_texture = c->null_texture;
10448 patch_lower(ldnew,newsec->style->wall0,c);
10449 ld3 = ldnew;
10450 fancied = TRUE;
10451 if (rollpercent(277,30)) break;
10452 } /* end loop-thing */
10453 if (innersec->floor_flat != ThisStyle->nukage1)
10454 if (rollpercent(278,75)) {
10455 innersec->floor_flat = c->water_flat;
10456 announce(LOG,"Water pool");
10457 }
10458 if (fancied) announce(LOG,"Bath");
10459 }
10460 /* Sometimes more layers! How dangerous is this? */
10461 if (rollpercent(279,20) && (!fancied) && /* Generalize "20" */
10462 (maxx-minx>128) && (maxy-miny>128) &&
10463 (innersec->floor_flat!=ThisStyle->nukage1)) {
10464 sector *inner2;
10465 int deltah;
10466 deltah = 12 + roll(156,13); /* Generalize this? */
10467 if (rollpercent(280,50)) deltah = 0 - deltah;
10468 if ((offset<64)&&(deltah>0)) deltah = 0 - deltah;
10469 for (;(maxx-minx>128)&&(maxy-miny>128);) {
10470 if ((innersec->ceiling_height-oldsector->floor_height)-deltah < 64) break;
10471 if ((innersec->ceiling_height-innersec->floor_height)-deltah < 64) break;
10472 if ((oldsector->ceiling_height-innersec->floor_height)-deltah < 64) break;
10473 inner2 = clone_sector(l,innersec);
10474 inner2->special = innersec->special;
10475 inner2->floor_height += deltah;
10476 announce(VERBOSE,"Sunk");
10477 minx += 32; /* Generalize "32"s */
10478 maxx -= 32;
10479 miny += 32;
10480 maxy -= 32;
10481 parallel_innersec(l,innersec,inner2,
10482 NULL,upt,oldsector->style->wall0,
10483 minx,miny,maxx,maxy,c);
10484 innersec = inner2;
10485 }
10486 }
10487 }
10488
10489 return TRUE;
10490
10491 } /* end ceiling_effect() */
10492
10493 /* Perhaps place a timely monster just to the right of the center */
10494 /* of the givenly-ended line. Update the haa if. */
righthand_monster(level * l,int xa,int ya,int xb,int yb,haa * haa,config * c)10495 void righthand_monster(level *l,int xa,int ya,int xb,int yb,haa *haa,config *c)
10496 {
10497 genus *m;
10498 int x1, y1, x, y, flags;
10499 short angle;
10500
10501 /* See if the model wants a monster */
10502 m = timely_monster(haa,c,&flags,rollpercent(281,l->p_biggest_monsters),1); /* 1 correct? */
10503 if (!m) return;
10504 /* Figure out where we want it */
10505 x1 = (xa+xb)/2;
10506 y1 = (ya+yb)/2;
10507 point_from(xa,ya,x1,y1,RIGHT_TURN,1+MONSTER_WIDTH(m)/2,&x,&y);
10508 if (!room_at(l,m,x,y,MONSTER_WIDTH(m),c)) return;
10509 /* Fill in other details */
10510 angle = facing_right_from(xa,ya,xb,yb); /* Correct? */
10511 if (rollpercent(282,50)) flags |= 0x08; /* deaf; how often? */
10512 /* And finally create it and update the haa */
10513 new_thing(l,x,y,angle,m->thingid,(short)flags,c);
10514 update_haa_for_monster(haa,m,flags,1,c); /* 1 correct? */
10515
10516 } /* end righthand_monster */
10517
10518 /* Stick in a pillar (or post). Assumes rectangles lots. */
10519 /* Now with sometimes monsters! */
10520 /* Should shrink the candidate pillar, not just give up, if */
10521 /* it finds an existing thing in the way. */
do_pillar(level * l,sector * oldsector,style * ThisStyle,haa * haa,config * c)10522 void do_pillar(level *l,sector *oldsector,style *ThisStyle,haa *haa,config *c)
10523 {
10524 int minx, miny, maxx, maxy;
10525 int xsize, ysize, xoff, yoff;
10526 thing *t;
10527 texture *t1;
10528
10529 /* Figure out where we might want to put it */
10530 find_rec(l,oldsector,&minx,&miny,&maxx,&maxy);
10531 /* The room has to be >192 in each direction, for now */
10532 /* 64 for the pillar, and 64 on every side for monster checks */
10533 if (maxx-minx<=192) return;
10534 if (maxy-miny<=192) return;
10535 /* random sizes within allowable range */
10536 xsize = 64 + roll(157, (maxx-minx)-192 );
10537 ysize = 64 + roll(158, (maxy-miny)-192 );
10538 if (rollpercent(283,50))
10539 if (xsize>127)
10540 if (ysize>127) {
10541 xsize = 128;
10542 ysize = 128;
10543 }
10544 /* Now the offsets from min. Hmm... */
10545 if (ThisStyle->center_pillars) {
10546 xoff = 64 + ( ((maxx-minx)-128) - xsize ) / 2;
10547 yoff = 64 + ( ((maxy-miny)-128) - ysize ) / 2;
10548 } else {
10549 xoff = 64 + roll (269, ((maxx-minx)-128) - xsize );
10550 yoff = 64 + roll (270, ((maxy-miny)-128) - ysize );
10551 }
10552 /* Now we have the corners of the candidate pillar */
10553 /* but go out an extra 64 for the check */
10554 minx = minx + xoff - 64;
10555 miny = miny + yoff - 64;
10556 maxx = minx + xsize + 128; /* Should be 64? */
10557 maxy = miny + ysize + 128; /* Should be 64? */
10558 /* Now we need to see if any Thing is in the area */
10559 for (t=l->thing_anchor;t;t=t->next)
10560 if ( (t->x>=minx) && (t->x<=maxx) &&
10561 (t->y>=miny) && (t->y<=maxy) ) {
10562 announce(VERBOSE,"Too many things for a pillar");
10563 return;
10564 }
10565 /* None! A miracle. Define the space. */
10566 t1 = ThisStyle->wall0;
10567 if (rollpercent(284,80)) t1 = random_wall0(c,ThisStyle);
10568 if (xsize==128)
10569 if (ysize==128)
10570 if (128 == oldsector->ceiling_height - oldsector->floor_height)
10571 t1 = ThisStyle->plaque;
10572 minx += 64;
10573 miny += 64;
10574 maxx -= 64;
10575 maxy -= 64;
10576 if (ThisStyle->do_constructs) {
10577 install_construct(l,oldsector,minx,miny,maxx,maxy,ThisStyle,c);
10578 } else {
10579 parallel_innersec(l,oldsector,NULL,t1,NULL,NULL,minx,miny,maxx,maxy,c);
10580 }
10581 announce(VERBOSE,"Made a pillar");
10582
10583 /* Consider putting some monsters around it */
10584 righthand_monster(l,minx,maxy,minx,miny,haa,c);
10585 righthand_monster(l,minx,miny,maxx,miny,haa,c);
10586 righthand_monster(l,maxx,miny,maxx,maxy,haa,c);
10587 righthand_monster(l,maxx,maxy,minx,maxy,haa,c);
10588 haa_unpend(haa);
10589
10590 /* Whew, I guess that's all! */
10591
10592 } /* end do_pillar */
10593
10594 /* Does this construct fit into this sector, on sides of these */
10595 /* sizes, in this style? */
construct_fits(construct * cs,int xsize,int ysize,sector * s,style * ThisStyle,config * c)10596 boolean construct_fits(construct *cs,int xsize, int ysize,sector *s,
10597 style *ThisStyle, config *c)
10598 {
10599 boolean good_primary = FALSE;
10600 boolean x_fit = FALSE;
10601 boolean y_fit = FALSE;
10602 texture_cell *tc;
10603
10604 /* Needs to be room between the floor and ceiling */
10605 if ( cs->height > (s->ceiling_height - s->floor_height) ) return FALSE;
10606
10607 /* Needs to be in the right family */
10608 if ( cs->family != ThisStyle->construct_family ) return FALSE;
10609
10610 /* Need to have at least one primary texture that can fit on */
10611 /* one side, and at least one texture of any kind that can */
10612 /* fit on each side. */
10613 for (tc=cs->texture_cell_anchor;tc;tc=tc->next) {
10614 if ( (tc->texture->gamemask&c->gamemask)==c->gamemask) {
10615 if (tc->width<=xsize) {
10616 x_fit = TRUE;
10617 if (tc->primary) good_primary = TRUE;
10618 }
10619 if (tc->width<=ysize) {
10620 y_fit = TRUE;
10621 if (tc->primary) good_primary = TRUE;
10622 }
10623 }
10624 }
10625 return (x_fit && y_fit && good_primary);
10626 }
10627
10628 /* Return a texture-cell from the given construct that */
10629 /* fits the given size. If accept_secondaries, then do */
fitting_tc(construct * cs,int size,boolean accept_secondaries,config * c)10630 texture_cell *fitting_tc(construct *cs, int size, boolean accept_secondaries,
10631 config *c)
10632 {
10633 texture_cell *tc1;
10634 texture_cell *answer = NULL;
10635 int ccount;
10636
10637 for (tc1=cs->texture_cell_anchor;tc1;tc1=tc1->next) tc1->marked=FALSE;
10638 for (ccount=0,tc1=cs->texture_cell_anchor;tc1;tc1=tc1->next) {
10639 if ( (accept_secondaries||tc1->primary)&&(tc1->width<=size)&&
10640 ( (tc1->texture->gamemask&c->gamemask) == c->gamemask) ) {
10641 tc1->marked=TRUE;
10642 ccount++;
10643 }
10644 }
10645 if (ccount) {
10646 ccount = roll(159,ccount);
10647 for (tc1=cs->texture_cell_anchor;tc1;tc1=tc1->next)
10648 if (tc1->marked) if (0==ccount--) break;
10649 answer = tc1;
10650 }
10651 for (tc1=cs->texture_cell_anchor;tc1;tc1=tc1->next) tc1->marked=FALSE;
10652 return answer;
10653 }
10654
10655 /* Install, if possible, some construct that fits the style, */
10656 /* in the given place. */
install_construct(level * l,sector * oldsector,int minx,int miny,int maxx,int maxy,style * ThisStyle,config * c)10657 boolean install_construct(level *l,sector *oldsector,
10658 int minx,int miny,int maxx,int maxy,
10659 style *ThisStyle,config *c)
10660 {
10661 construct *cs, *cs2;
10662 sector *innersec;
10663 int ccount = 0;
10664 boolean floor_to_ceiling, primary_on_x = FALSE;
10665 linedef *ld1, *ld2, *ld3, *ld4;
10666 texture_cell *tc1, *tc2, *tc3, *tc4, *tcp;
10667 flat_cell *fc;
10668 int xsize, ysize, mult;
10669
10670 /* Mark just those constructs that fit */
10671 for (cs=c->construct_anchor;cs;cs=cs->next) cs->marked = FALSE;
10672 for (cs=c->construct_anchor;cs;cs=cs->next) {
10673 if (construct_fits(cs,maxx-minx,maxy-miny,oldsector,ThisStyle,c)) {
10674 cs->marked = TRUE;
10675 ccount++;
10676 }
10677 }
10678 if (ccount==0) return FALSE; /* Give up if none */
10679 /* Otherwise pick a random marked one */
10680 ccount = roll(160,ccount);
10681 for (cs=c->construct_anchor;cs;cs=cs->next) {
10682 if (cs->marked) {
10683 if (ccount==0) break;
10684 ccount--;
10685 }
10686 }
10687 /* Clean up */
10688 for (cs2=c->construct_anchor;cs2;cs2=cs2->next) cs2->marked = FALSE;
10689
10690 floor_to_ceiling =
10691 (oldsector->ceiling_height - oldsector->floor_height == cs->height);
10692
10693 /* Iff we need an innersec, make sure we have one */
10694 if (!floor_to_ceiling) {
10695 innersec = clone_sector(l,oldsector);
10696 /* Set the top of the object */
10697 for (ccount=0,fc=cs->flat_cell_anchor;fc;fc=fc->next) ccount++;
10698 ccount = roll(161,ccount);
10699 for (fc=cs->flat_cell_anchor;fc;fc=fc->next) if (0==ccount--) break;
10700 innersec->floor_flat = fc->flat;
10701 innersec->light_level = oldsector->light_level;
10702 innersec->ceiling_height = oldsector->ceiling_height;
10703 innersec->floor_height = oldsector->floor_height + cs->height;
10704 } else {
10705 innersec=NULL;
10706 }
10707
10708 /* Pick a primary texture (cell) */
10709 tcp = NULL;
10710 if (rollpercent(285,50)) { /* Try X first */
10711 tcp = fitting_tc(cs,maxy-miny,FALSE,c);
10712 if (tcp!=NULL) primary_on_x = TRUE;
10713 }
10714 if (tcp==NULL) { /* Nothing yet, try Y */
10715 tcp = fitting_tc(cs,maxx-minx,FALSE,c);
10716 if (tcp!=NULL) primary_on_x = FALSE;
10717 }
10718 if (tcp==NULL) { /* Nothing yet, try X (again) */
10719 tcp = fitting_tc(cs,maxy-miny,FALSE,c);
10720 if (tcp!=NULL) primary_on_x = TRUE;
10721 }
10722 if (tcp==NULL) { /* Impossible! */
10723 announce(WARNING,"Some impossible error in construct-construction.");
10724 return FALSE;
10725 }
10726
10727 /* Set all four cells */
10728 if (primary_on_x) {
10729 if (rollpercent(286,50)) {
10730 tc2 = tcp;
10731 tc4 = fitting_tc(cs,maxy-miny,TRUE,c);
10732 } else {
10733 tc4 = tcp;
10734 tc2 = fitting_tc(cs,maxy-miny,TRUE,c);
10735 }
10736 tc1 = fitting_tc(cs,maxx-minx,TRUE,c);
10737 tc3 = fitting_tc(cs,maxx-minx,TRUE,c);
10738 } else {
10739 if (rollpercent(287,50)) {
10740 tc1 = tcp;
10741 tc3 = fitting_tc(cs,maxx-minx,TRUE,c);
10742 } else {
10743 tc3 = tcp;
10744 tc1 = fitting_tc(cs,maxx-minx,TRUE,c);
10745 }
10746 tc2 = fitting_tc(cs,maxy-miny,TRUE,c);
10747 tc4 = fitting_tc(cs,maxy-miny,TRUE,c);
10748 }
10749
10750 /* Now decide how large the X and Y dimensions should actually be */
10751 xsize = tc1->width;
10752 if (tc3->width>xsize) xsize = tc3->width;
10753 mult = (maxx-minx)/xsize;
10754 if (mult>4) mult=4; /* Not too huge! */
10755 if (rollpercent(288,50)) mult = 1 + roll(162,mult);
10756 xsize *= mult;
10757 ysize = tc2->width;
10758 if (tc4->width>ysize) ysize = tc4->width;
10759 mult = (maxy-miny)/ysize;
10760 if (mult>4) mult=4; /* Not too huge! */
10761 if (rollpercent(289,50)) mult = 1 + roll(163,mult);
10762 ysize *= mult;
10763
10764 /* Finally! Make the sector */
10765 parallel_innersec_ex(l,oldsector,innersec,NULL,NULL,NULL,
10766 minx,miny,minx+xsize,miny+ysize,c,
10767 &ld2,&ld1,&ld4,&ld3);
10768
10769 /* And fix up the linedefs */
10770 if (floor_to_ceiling) {
10771 ld1->right->middle_texture = tc1->texture;
10772 ld2->right->middle_texture = tc2->texture;
10773 ld3->right->middle_texture = tc3->texture;
10774 ld4->right->middle_texture = tc4->texture;
10775 } else {
10776 ld1->right->middle_texture = c->null_texture;
10777 ld2->right->middle_texture = c->null_texture;
10778 ld3->right->middle_texture = c->null_texture;
10779 ld4->right->middle_texture = c->null_texture;
10780 ld1->right->lower_texture = tc1->texture;
10781 ld2->right->lower_texture = tc2->texture;
10782 ld3->right->lower_texture = tc3->texture;
10783 ld4->right->lower_texture = tc4->texture;
10784 ld1->flags &= ~LOWER_UNPEGGED;
10785 ld2->flags &= ~LOWER_UNPEGGED;
10786 ld3->flags &= ~LOWER_UNPEGGED;
10787 ld4->flags &= ~LOWER_UNPEGGED;
10788 }
10789 ld1->right->y_offset = rollpercent(290,50) ? tc1->y_offset1 : tc1->y_offset2;
10790 ld2->right->y_offset = rollpercent(291,50) ? tc2->y_offset1 : tc2->y_offset2;
10791 ld3->right->y_offset = rollpercent(292,50) ? tc3->y_offset1 : tc3->y_offset2;
10792 ld4->right->y_offset = rollpercent(293,50) ? tc4->y_offset1 : tc4->y_offset2;
10793
10794 announce(VERBOSE,"Construct");
10795 return TRUE;
10796
10797 } /* end install_construct */
10798
10799 /* Put in a single pillarish thing, with a much cleverer */
10800 /* algorithm than do_pillar. Use texture t if given, */
10801 /* else if NULL use a random one, or sometimes use a */
10802 /* plaque texture. If innersec is not null, use that */
10803 /* inside the pillar (else void). */
do_new_pillar(level * l,sector * oldsector,sector * innersec,texture * t1,style * ThisStyle,haa * haa,config * c)10804 boolean do_new_pillar(level *l,sector *oldsector,sector *innersec,texture *t1,
10805 style *ThisStyle, haa *haa,config *c)
10806 {
10807 int minx, miny, maxx, maxy, tx, ty;
10808 thing *t;
10809 vertex *v;
10810 linedef *ld;
10811 texture *tm;
10812
10813 /* Initialize the 64-enclosing range */
10814 find_rec(l,oldsector,&minx,&miny,&maxx,&maxy);
10815 /* The room has to be >192 in each direction, for now */
10816 /* 64 for the pillar, and 64 on every side for monster checks */
10817 if (maxx-minx<=192) return FALSE;
10818 if (maxy-miny<=192) return FALSE;
10819 /* Pick a point we'd like the pillar to contain, */
10820 /* to guide the following algorithm */
10821 tx = minx + 1 + roll(164,maxx-(minx+1));
10822 ty = miny + 1 + roll(165,maxy-(miny+1));
10823 /* If that point is inside some existing pillar, fail */
10824 if (oldsector!=point_sector(l,tx,ty,NULL,NULL)) return FALSE;
10825 /* For each vertex, if the vertex is in the current range, */
10826 /* shrink the range so it misses the vertex, but still */
10827 /* contains the t point. */
10828 for (v=l->vertex_anchor;v;v=v->next) {
10829 if (infinity_norm(tx,ty,v->x,v->y)<64) return FALSE; /* Failure! */
10830 if ( (v->x<minx) || (v->x>maxx) || (v->y<miny) || (v->y>maxy) ) continue;
10831 if (v->x>tx) maxx = v->x - 1;
10832 else minx = v->x + 1;
10833 if (v->y>ty) maxy = v->y - 1;
10834 else miny = v->y + 1;
10835 }
10836 /* and the same for each thing, although in fact the requirement */
10837 /* to be 64 away from even pickables is overconservative */
10838 for (t=l->thing_anchor;t;t=t->next) {
10839 if (infinity_norm(tx,ty,t->x,t->y)<64) return FALSE; /* Failure! */
10840 if ( (t->x<minx) || (t->x>maxx) || (t->y<miny) || (t->y>maxy) ) continue;
10841 if (t->x>tx) maxx = t->x - 1;
10842 else minx = t->x + 1;
10843 if (t->y>ty) maxy = t->y - 1;
10844 else miny = t->y + 1;
10845 }
10846 /* Now reduce the enclosing range */
10847 minx = minx + 64;
10848 maxx = maxx - 64;
10849 miny = miny + 64;
10850 maxy = maxy - 64;
10851 if (minx>=maxx-15) return FALSE;
10852 if (miny>=maxy-15) return FALSE;
10853 /* See if the result has any nasty intersections */
10854 for (ld=l->linedef_anchor;ld;ld=ld->next) {
10855 if (intersects(minx,miny,minx,maxy,ld->from->x,ld->from->y,ld->to->x,ld->to->y))
10856 return FALSE;
10857 if (intersects(minx,maxy,maxx,maxy,ld->from->x,ld->from->y,ld->to->x,ld->to->y))
10858 return FALSE;
10859 if (intersects(maxx,maxy,maxx,miny,ld->from->x,ld->from->y,ld->to->x,ld->to->y))
10860 return FALSE;
10861 if (intersects(maxx,miny,minx,miny,ld->from->x,ld->from->y,ld->to->x,ld->to->y))
10862 return FALSE;
10863 }
10864 /* If we made it this far, we found room! */
10865 /* Now decide how much to use (i.e. should sometimes shrink/narrow here) */
10866 /* and finally make the pillar (or whatever!) */
10867 /* Perhaps a construct */
10868 if (ThisStyle->do_constructs) {
10869 install_construct(l,oldsector,minx,miny,maxx,maxy,ThisStyle,c);
10870 } else {
10871 if (t1==NULL) t1 = random_wall0(c,ThisStyle);
10872 /* Sometimes do a special plaque thing */
10873 if ((innersec==NULL) && ((maxx-minx)>=128) && ((maxy-miny)>=128) &&
10874 ( oldsector->ceiling_height - oldsector->floor_height == 128) ) {
10875 minx = minx + ((maxx-minx)-128)/2;
10876 maxx = minx + 128;
10877 miny = miny + ((maxy-miny)-128)/2;
10878 maxy = miny + 128;
10879 t1 = ThisStyle->plaque;
10880 announce(VERBOSE,"Plaque-pillar");
10881 }
10882 if (innersec) {
10883 announce(VERBOSE,"Inner pillar");
10884 tm = NULL;
10885 } else {
10886 tm = t1;
10887 }
10888 parallel_innersec(l,oldsector,innersec,tm,t1,t1,minx,miny,maxx,maxy,c);
10889 announce(VERBOSE,"New pillar");
10890 }
10891 /* Consider putting some monsters around it */
10892 if (rollpercent(294,50)) righthand_monster(l,minx,maxy,minx,miny,haa,c);
10893 if (rollpercent(295,50)) righthand_monster(l,minx,miny,maxx,miny,haa,c);
10894 if (rollpercent(296,50)) righthand_monster(l,maxx,miny,maxx,maxy,haa,c);
10895 if (rollpercent(297,50)) righthand_monster(l,maxx,maxy,minx,maxy,haa,c);
10896 haa_unpend(haa);
10897
10898 return TRUE;
10899
10900 } /* end do_new_pillar */
10901
10902 /* Put in a bunch of pillarish things, with a much cleverer */
10903 /* algorithm than do_pillar. */
10904 /* No monsters at present */
do_new_pillars(level * l,sector * oldsector,style * ThisStyle,haa * haa,config * c)10905 void do_new_pillars(level *l,sector *oldsector,style *ThisStyle,
10906 haa *haa,config *c)
10907 {
10908 int want, tried, delta;
10909 texture *t = NULL;
10910 sector *s = NULL;
10911
10912 switch(roll(166,4)) {
10913 case 0:
10914 case 1: t = ThisStyle->wall0; break;
10915 case 2: t = random_wall0(c,ThisStyle); break;
10916 case 3: t = NULL;
10917 }
10918
10919 /* Number of tries should really be sensitive to */
10920 /* room-size or something, eh? */
10921 switch(roll(167,3)) {
10922 case 0: want = 1; break;
10923 case 1: want = 20; break;
10924 case 2: want = 1 + roll(168,6); break;
10925 default: want = 40; /* Impossible! */
10926 }
10927
10928 /* Decide if the pillars should have a non-void inside sector */
10929 if ((ThisStyle->do_constructs==FALSE)&&(rollpercent(298,100))) { /* 100? */
10930 s = clone_sector(l,oldsector);
10931 if (rollpercent(299,30)) {
10932 s->light_level += 30 - roll(169,61);
10933 if (s->light_level>240) s->light_level = 240;
10934 if (s->light_level<80) s->light_level = 80;
10935 }
10936 if (rollpercent(300,80)) {
10937 delta = roll(170,(s->ceiling_height-32)-s->floor_height);
10938 if (delta>128) delta = 128;
10939 s->floor_height += delta;
10940 } else if (rollpercent(301,50)) {
10941 s->ceiling_height -= roll(171,(s->ceiling_height-32)-s->floor_height);
10942 } else {
10943 delta = roll(172,(s->ceiling_height-32)-s->floor_height);
10944 if (delta>128) delta = 128;
10945 s->floor_height += delta;
10946 s->ceiling_height -= roll(173,(s->ceiling_height-32)-s->floor_height);
10947 }
10948 }
10949
10950 for (tried=0;(tried<100)&&want;tried++)
10951 if (do_new_pillar(l,oldsector,s,t,ThisStyle,haa,c)) want--;
10952 }
10953
10954 /* Put some appropriate monster(s) and bonus(es) along the right */
10955 /* side of the given linedef. Adjust the haa (haa adjustment */
10956 /* assumes that ITYTD doesn't find the bonuses, and HMP only */
10957 /* finds them half the time, if <secret> is true). */
populate_linedef(level * l,linedef * ldnew2,haa * haa,config * c,boolean secret)10958 void populate_linedef(level *l,linedef *ldnew2,haa *haa,config *c,
10959 boolean secret)
10960 {
10961 int x,y,x1,y1;
10962 short bonustype;
10963 int bonusamount;
10964 genus *m;
10965 int levels, farness, plen;
10966 short angle;
10967
10968 point_from(ldnew2->from->x,ldnew2->from->y,ldnew2->to->x,ldnew2->to->y,
10969 RIGHT_TURN,32,&x1,&y1); /* "32" should be improved */
10970 plen = linelen(ldnew2);
10971 switch (roll(174,4)) {
10972 case 1: farness = plen-32; break;
10973 case 2: farness = plen/2; break;
10974 case 3: farness = 32+roll(175,plen-63); break;
10975 default: farness = 32; break;
10976 }
10977 point_from(ldnew2->to->x,ldnew2->to->y,x1,y1,RIGHT_TURN,farness,&x,&y);
10978 /* pick a prize; stubby */
10979 bonustype = ID_POTION; /* Just in case! */
10980 if (rollpercent(302,50)) { /* Health or whatever */
10981 switch (roll(176,4)) {
10982 case 0: bonustype = ID_MEDIKIT; bonusamount = 25; break;
10983 case 1: bonustype = ID_MEDIKIT; bonusamount = 25; break;
10984 case 2: bonustype = ID_STIMPACK; bonusamount = 10; break;
10985 case 3: if ((FALSE==l->seen_suit)&&(rollpercent(303,l->p_force_nukage))) {
10986 bonustype = ID_SUIT; bonusamount = 10; /* Guess */
10987 l->seen_suit = TRUE;
10988 } else if ((FALSE==l->seen_map)&&rollpercent(304,30)) {
10989 bonustype = ID_MAP; bonusamount = 0;
10990 l->seen_map = TRUE;
10991 announce(VERBOSE,"Area map");
10992 } else {
10993 bonustype = ID_INVIS; bonusamount = 10; /* Also guess */
10994 }
10995 break;
10996 default: bonustype = ID_POTION; bonusamount = 1; /* Impossible */
10997 }
10998 /* We assume ITYTD didn't find the closet! */
10999 haa->haas[1].health += bonusamount/2; /* and HMP might not have */
11000 haa->haas[2].health += bonusamount;
11001 if (!secret) { /* Unless it's not a secret */
11002 haa->haas[0].health += bonusamount;
11003 haa->haas[1].health += bonusamount/2;
11004 }
11005 } else { /* Some ammo or whatever */
11006 if ((haa->haas[0].can_use_cells)&&(rollpercent(305,20))) {
11007 bonustype = ID_CELLPACK;
11008 bonusamount = 2000; /* yow! */
11009 } else if ((haa->haas[0].can_use_rockets)&&(rollpercent(306,20))) {
11010 bonustype = ID_ROCKBOX;
11011 bonusamount = 500;
11012 } else if ((!(haa->haas[2].has_chainsaw))&&(rollpercent(307,20))) {
11013 bonustype = ID_CHAINSAW;
11014 bonusamount = 0;
11015 haa->haas[2].has_chainsaw = TRUE;
11016 } else if (rollpercent(308,2)) {
11017 bonustype = ID_CHAINSAW;
11018 bonusamount = 0;
11019 haa->haas[2].has_chainsaw = TRUE;
11020 } else switch (roll(177,3)) {
11021 case 1: bonustype = ID_SHELLBOX; bonusamount = 1400; break;
11022 case 2: bonustype = ID_BACKPACK;
11023 bonusamount = 380;
11024 if (haa->haas[1].can_use_rockets) bonusamount += 100;
11025 if (haa->haas[1].can_use_cells) bonusamount += 400;
11026 haa->haas[1].has_backpack = TRUE;
11027 haa->haas[2].has_backpack = TRUE;
11028 break;
11029 default: bonustype = ID_BULBOX; bonusamount = 500; break;
11030 } /* end switch */
11031 /* We assume ITYTD didn't find the closet! */
11032 haa->haas[1].ammo += bonusamount/2; /* And HMP only prolly did */
11033 haa->haas[2].ammo += bonusamount;
11034 if (!secret) { /* Unless it's not a secret */
11035 haa->haas[0].ammo += bonusamount;
11036 haa->haas[1].ammo += bonusamount/2;
11037 }
11038 /* Account for chainsaws; primitive */
11039 if (bonustype==ID_CHAINSAW) {
11040 haa->haas[1].has_chainsaw = TRUE; /* OK? */
11041 haa->haas[2].has_chainsaw = TRUE;
11042 }
11043 } /* end ammo bonuses */
11044 new_thing(l,x,y,0,bonustype,7,c); /* Place the bonus */
11045 /* Now monsters! */
11046 if ( ((!secret) || c->secret_monsters) && (rollpercent(309,90))) {
11047 farness = 32; /* mwidth here */
11048 point_from(ldnew2->to->x,ldnew2->to->y,x1,y1,RIGHT_TURN,farness,&x,&y);
11049 for (;;) {
11050 m = timely_monster(haa,c,&levels,rollpercent(310,l->p_biggest_monsters),0);
11051 if (m) {
11052 angle = facing_right_from_ld(ldnew2);
11053 new_thing(l,x,y,angle,m->thingid,(short)levels,c); /* not deaf */
11054 update_haa_for_monster(haa,m,levels,0,c); /* zero? one? */
11055 /* Note that for monster purposes, ALL levels find the closet! */
11056 } else {
11057 break;
11058 }
11059 farness += 64; /* Should be mwidths stuff here */
11060 if (farness+32>plen) break;
11061 point_from(ldnew2->to->x,ldnew2->to->y,x1,y1,RIGHT_TURN,farness,&x,&y);
11062 } /* end forever while monsters and space */
11063 haa_unpend(haa);
11064 } /* end roll for having a monster */
11065 }
11066
11067
11068 /* Put a secret closet behind the given linedef, with something */
11069 /* or other of interest in it. Doesn't do anything about hints. */
11070 /* If tag is -1, makes the door faces normal doors. Otherwise, */
11071 /* makes the inner doorface a door, but leaves the outer one */
11072 /* functionless, and tags the door sector with the tag. */
11073 /* h is the height of the closet, unless it's zero in which case */
11074 /* the existing height is used. Returns NULL for failure, or */
11075 /* the linedef of the far wall of the closet. If a haa is */
11076 /* given, will also populate the closet. Use the given */
11077 /* ceiling_height (ch) for y-alignment. */
secret_closet(level * l,linedef * ld,style * ThisStyle,int h,haa * haa,config * c,boolean inside_sr,int tag,short ch,boolean secret)11078 linedef *secret_closet(level *l,linedef *ld,style *ThisStyle,int h,
11079 haa *haa, config *c, boolean inside_sr, int tag, short ch,
11080 boolean secret)
11081 {
11082 linedef *ldnew, *ldnew2, *ldedge1, *ldedge2;
11083 sector *s;
11084 short doortype;
11085
11086 if (!empty_left_side(l,ld,72)) return NULL; /* Room? */
11087
11088 doortype = LINEDEF_NORMAL_DOOR;
11089 if (!(DOOM0_BIT&c->gamemask) && rollpercent(311,80))
11090 doortype = LINEDEF_BLAZE_DOOR;
11091
11092 /* Modify the outermost linedef to be doory */
11093 ld->right->upper_texture = ld->right->middle_texture; /* Door face */
11094 ld->flags |= SECRET_LINEDEF;
11095 if (tag==-1)
11096 ld->type = doortype;
11097 /* Correct? */
11098 ld->right->y_offset = 128 -
11099 (ld->right->sector->ceiling_height - ld->right->sector->floor_height);
11100 /* Make the door sector itself -- "8" should be variable */
11101 ldnew = lefthand_box_ext(l,ld,8,ThisStyle,c,&ldedge1,&ldedge2);
11102 if (tag!=-1) ldnew->right->sector->tag = tag;
11103 ldedge1->flags |= LOWER_UNPEGGED;
11104 ldedge2->flags |= LOWER_UNPEGGED;
11105 ldedge1->right->y_offset =
11106 ldedge2->right->y_offset =
11107 ch - ldedge1->right->sector->floor_height;
11108 /* Make the closet sector -- "64" should be variable */
11109 ldnew2 = lefthand_box_ext(l,ldnew,64,ThisStyle,c,&ldedge1,&ldedge2);
11110 if (h!=0) ldnew2->right->sector->ceiling_height =
11111 ldnew2->right->sector->floor_height + h;
11112 ldedge1->right->y_offset =
11113 ldedge2->right->y_offset =
11114 ldnew2->right->y_offset =
11115 ch - ldnew2->right->sector->ceiling_height;
11116 /* Finish making the door doory */
11117 s = ldnew->right->sector;
11118 flip_linedef(ldnew);
11119 if (secret) s->special = SECRET_SECTOR;
11120 if (inside_sr) {
11121 ldnew->type = doortype; /* reopenable */
11122 } else {
11123 ldnew->type = LINEDEF_NORMAL_S1_DOOR; /* Triggered doors never close */
11124 }
11125 s->ceiling_height = s->floor_height;
11126 s->light_level = ThisStyle->doorlight0;
11127 ldnew->right->upper_texture = ThisStyle->support0;
11128 ld->flags |= BLOCK_SOUND; /* Always? */
11129 ldnew->flags |= BLOCK_SOUND;
11130 /* and polish up the closet */
11131 ldnew2->right->middle_texture = ThisStyle->wall0;
11132 s = ldnew2->right->sector;
11133 if (s->light_level>160) s->light_level = 160;
11134 if (s->style->ceilinglight) if (c->clights) {
11135 s->ceiling_flat = s->style->ceilinglight;
11136 announce(VERBOSE,"ccl");
11137 make_lighted(l,s,c);
11138 }
11139
11140 /* Sometimes a nukage floor on triggered ones, just */
11141 /* for fun. Note that these aren't SECRET anymore. */
11142 if ((tag!=-1) && (rollpercent(312,10) ||
11143 rollpercent(442,l->p_force_nukage))) {
11144 s->floor_height -= 8;
11145 patch_lower(ldnew,ldnew->right->upper_texture,c);
11146 s->floor_flat = ThisStyle->nukage1;
11147 s->special = NUKAGE1_SPECIAL;
11148 }
11149
11150 if (s->special == SECRET_SECTOR) l->secret_count++;
11151
11152 if (haa) populate_linedef(l,ldnew2,haa,c,secret);
11153
11154 return ldnew2;
11155
11156 } /* end secret_closet() */
11157
11158 /* Put a box around the given thing, with the given tag and */
11159 /* type on each of the linedefs. */
trigger_box(level * l,thing * t,sector * oldsector,short tag,short type,config * c)11160 void trigger_box(level *l,thing *t,sector* oldsector,short tag,short type,
11161 config *c)
11162 {
11163 vertex *v1, *v2, *v3, *v4;
11164 linedef *ldnew;
11165 int dist;
11166 sector *ns;
11167
11168 /* Incoming sector is just a guess; confirm it */
11169 ns = point_sector(l,t->x,t->y,&dist,NULL); /* Should check "danger"! */
11170 if (ns) {
11171 oldsector = ns;
11172 } else {
11173 /* This shouldn't ever happen anymore, but just in case... */
11174 announce(WARNING,"point_sector returned NULL in trigger_box");
11175 }
11176 if (dist>24) dist = 24;
11177 if (dist<4) {
11178 announce(LOG,"Tiny triggerbox");
11179 dist = 4;
11180 } else if (dist<24) {
11181 announce(LOG,"Small triggerbox");
11182 }
11183
11184 v1 = new_vertex(l,t->x-dist,t->y-dist);
11185 v2 = new_vertex(l,t->x+dist,t->y-dist);
11186 v3 = new_vertex(l,t->x+dist,t->y+dist);
11187 v4 = new_vertex(l,t->x-dist,t->y+dist);
11188 ldnew = new_linedef(l,v1,v2);
11189 ldnew->right = new_sidedef(l,oldsector,c);
11190 ldnew->left = new_sidedef(l,oldsector,c);
11191 ldnew->tag = tag;
11192 ldnew->type = type;
11193 ldnew->flags |= TWO_SIDED;
11194 ldnew->right->middle_texture = c->null_texture;
11195 ldnew->left->middle_texture = c->null_texture;
11196 ldnew = new_linedef(l,v2,v3);
11197 ldnew->right = new_sidedef(l,oldsector,c);
11198 ldnew->left = new_sidedef(l,oldsector,c);
11199 ldnew->tag = tag;
11200 ldnew->type = type;
11201 ldnew->flags |= TWO_SIDED;
11202 ldnew->right->middle_texture = c->null_texture;
11203 ldnew->left->middle_texture = c->null_texture;
11204 ldnew = new_linedef(l,v3,v4);
11205 ldnew->right = new_sidedef(l,oldsector,c);
11206 ldnew->left = new_sidedef(l,oldsector,c);
11207 ldnew->tag = tag;
11208 ldnew->type = type;
11209 ldnew->flags |= TWO_SIDED;
11210 ldnew->right->middle_texture = c->null_texture;
11211 ldnew->left->middle_texture = c->null_texture;
11212 ldnew = new_linedef(l,v4,v1);
11213 ldnew->right = new_sidedef(l,oldsector,c);
11214 ldnew->left = new_sidedef(l,oldsector,c);
11215 ldnew->tag = tag;
11216 ldnew->type = type;
11217 ldnew->flags |= TWO_SIDED;
11218 ldnew->right->middle_texture = c->null_texture;
11219 ldnew->left->middle_texture = c->null_texture;
11220 }
11221
11222 /* Make a small floor-preserving link that fits on the given */
11223 /* linedef. Suitable for walking out onto the patio. */
random_patio_link(level * l,linedef * ld,style * ThisStyle,config * c)11224 link *random_patio_link(level *l,linedef *ld,style *ThisStyle,config *c)
11225 {
11226 link *answer = (link *)malloc(sizeof (*answer));
11227
11228 answer->type = BASIC_LINK;
11229 answer->bits = 0;
11230 answer->floordelta = 0;
11231 if (rollpercent(313,50)) {
11232 answer->height1 = 72;
11233 } else {
11234 answer->height1 = 64 + 8 * roll(178,9);
11235 }
11236 if (rollpercent(314,50)) {
11237 answer->width1 = 64 * l->hugeness;
11238 } else {
11239 answer->width1 = 64 + roll(179,linelen(ld)-79);
11240 }
11241 if (rollpercent(315,50)) {
11242 answer->depth1 = 16 * l->hugeness; /* Door/arch depth */
11243 } else {
11244 answer->depth1 = (8 + 4 * roll(180,15)) * l->hugeness;
11245 }
11246 if (rollpercent(316,50)) {
11247 answer->depth2 = 8 * l->hugeness; /* Recess depth */
11248 } else {
11249 answer->depth2 = 20 * l->hugeness;
11250 }
11251 if (rollpercent(317,50)) {
11252 answer->depth3 = 16 * l->hugeness; /* Core depth */
11253 } else {
11254 answer->depth3 = (8 + 4 * roll(181,15)) * l->hugeness;
11255 }
11256 if (rollpercent(318,50)) answer->bits |= LINK_RECESS;
11257 if (rollpercent(319,20)) answer->bits |= LINK_CORE;
11258 if (rollpercent(320,5)) answer->bits |= LINK_BARS;
11259 if (rollpercent(321,20)) { /* Single door */
11260 answer->bits |= LINK_RECESS | LINK_ANY_DOOR;
11261 answer->bits &= ~LINK_CORE;
11262 }
11263 return answer;
11264 }
11265
11266 /* Try to make a little patio out of the given room */
make_extroom(level * l,sector * oldsector,haa * haa,style * ThisStyle,config * c)11267 void make_extroom(level *l, sector *oldsector, haa *haa,
11268 style *ThisStyle, config *c)
11269 {
11270 int i, depth, x, y, fenceh, saveh;
11271 short cthick;
11272 linedef *ld, *newldf, *ldfar, *lde1, *lde2, *ldt;
11273 texture *t1;
11274 link *ThisLink;
11275 sector *hisec, *losec;
11276 vertex *v;
11277 boolean outtex = rollpercent(322,70);
11278
11279 fenceh = 96; /* Should vary */
11280 i = mark_decent_boundary_linedefs(l,oldsector,256);
11281 ld = random_marked_linedef(l,i);
11282 unmark_linedefs(l);
11283 if (ld!=NULL) {
11284 if (!empty_left_side(l,ld,256)) return;
11285 t1 = ld->right->middle_texture;
11286 ThisLink = random_patio_link(l,ld,ThisStyle,c);
11287 newldf = make_linkto(l,ld,ThisLink,ThisStyle,c,NULL);
11288 if (newldf==NULL) return; /* Shouldn't happen */
11289 depth = linelen(ld);
11290 if ((depth<=512)&&rollpercent(323,25)) depth *= 2;
11291 flip_linedef(newldf); /* Just so we can use the lefthand functions */
11292 for (;;) {
11293 if (empty_left_side(l,newldf,depth)) break;
11294 depth -= 64;
11295 if (depth<128) {
11296 delete_vertex(l,newldf->from);
11297 delete_vertex(l,newldf->to);
11298 delete_linedef(l,newldf);
11299 return; /* How'd that happen? */
11300 }
11301 }
11302 ldfar = lefthand_box_ext(l,newldf,depth,ThisStyle,c,&lde1,&lde2);
11303 flip_linedef(newldf); /* Fix it */
11304 ldfar->right->middle_texture = newldf->right->middle_texture = t1;
11305 if (outtex) ldfar->right->middle_texture = lde1->right->middle_texture =
11306 lde2->right->middle_texture = random_texture0(OUTDOOR,c,NULL);
11307 losec = newldf->right->sector;
11308 losec->floor_height = oldsector->floor_height;
11309 losec->floor_flat = oldsector->floor_flat;
11310 losec->light_level = l->outside_light_level; /* Minus twenty? */
11311 cthick = 32;
11312 if (rollpercent(324,30)) cthick += 8 * roll(182,10);
11313 hisec = clone_sector(l,losec);
11314 losec->ceiling_height = losec->floor_height + fenceh;
11315 hisec->ceiling_flat = oldsector->ceiling_flat; /* For e-l; fixed later */
11316 newldf->right->sector = hisec;
11317 /* And now the little triangle thing to look good */
11318 x = (newldf->to->x + newldf->from->x) / 2;
11319 y = (newldf->to->y + newldf->from->y) / 2;
11320 point_from(newldf->from->x,newldf->from->y,x,y,
11321 RIGHT_TURN,32,&x,&y);
11322 v = new_vertex(l,x,y);
11323 ldt = new_linedef(l,newldf->to,v);
11324 ldt->right = new_sidedef(l,hisec,c);
11325 ldt->right->middle_texture = c->null_texture;
11326 ldt->left = new_sidedef(l,losec,c);
11327 ldt->left->middle_texture = c->null_texture;
11328 ldt->flags |= TWO_SIDED | NOT_ON_MAP;
11329 ldt = new_linedef(l,v,newldf->from);
11330 ldt->right = new_sidedef(l,hisec,c);
11331 ldt->right->middle_texture = c->null_texture;
11332 ldt->left = new_sidedef(l,losec,c);
11333 ldt->left->middle_texture = c->null_texture;
11334 ldt->flags |= TWO_SIDED | NOT_ON_MAP;
11335 /* Adjust stuff */
11336 hisec->ceiling_height = oldsector->ceiling_height + cthick;
11337 if (hisec->ceiling_height<losec->ceiling_height)
11338 hisec->ceiling_height = losec->ceiling_height + cthick;
11339 ldfar->right->y_offset = lde1->right->y_offset = lde2->right->y_offset =
11340 oldsector->ceiling_height - losec->ceiling_height;
11341 newldf->right->y_offset = oldsector->ceiling_height - hisec->ceiling_height;
11342 /* Actually make the link */
11343 saveh = hisec->ceiling_height;
11344 establish_link(l,ld,newldf,ThisLink,NULL,ThisStyle,ThisStyle,haa,c);
11345 hisec->ceiling_flat = c->sky_flat;
11346 hisec->ceiling_height = saveh;
11347 /* A hack to fix some quasi-HOMs */
11348 for (ldt=l->linedef_anchor;ldt;ldt=ldt->next)
11349 if (ldt->left)
11350 if (ldt->right)
11351 if (ldt->right->sector==hisec)
11352 patch_upper(ldt,t1,c);
11353 if (outtex) hisec->floor_flat = losec->floor_flat =
11354 random_flat0(OUTDOOR,c,NULL);
11355 /* Now populate it and stuff */
11356 populate(l,losec,c,haa,FALSE);
11357 place_plants(l,128,losec,c); /* 128? */
11358 announce(VERBOSE,"Patio");
11359 }
11360
11361 } /* end make_extroom */
11362
11363
11364 /* Try to make an external window out of the given room */
make_extwindow(level * l,sector * oldsector,style * ThisStyle,config * c)11365 void make_extwindow(level *l, sector *oldsector, style *ThisStyle, config *c)
11366 {
11367 int wlen, wheight, i, depth, ldlen, border;
11368 linedef *ld, *ldnew;
11369 texture *t1;
11370 linedef *e1, *e2;
11371 short yoff;
11372
11373 i = mark_decent_boundary_linedefs(l,oldsector,64);
11374 ld = random_marked_linedef(l,i);
11375 unmark_linedefs(l);
11376 if (ld!=NULL) {
11377 t1 = ld->right->middle_texture;
11378 ldlen = linelen(ld);
11379 wlen = 32 + roll(183,ldlen-31);
11380 if (wlen>ldlen) wlen = ldlen;
11381 border = ( ldlen - wlen ) / 2;
11382 if (border!=0) {
11383 ld = split_linedef(l,ld,border,c);
11384 split_linedef(l,ld,wlen,c);
11385 }
11386 depth = 40;
11387 /* The "48" in the next line is the two eight-deep little sectors, */
11388 /* plus another 32 just so windows won't be placed *too* absurdly. */
11389 if (empty_left_side(l,ld,depth+48)) {
11390 wheight = oldsector->ceiling_height - oldsector->floor_height;
11391 if (wheight>128) wheight = 128;
11392 wheight = oldsector->floor_height + 48 + roll(184,wheight-47);
11393 if (wheight>oldsector->ceiling_height-32)
11394 wheight=oldsector->ceiling_height-32;
11395 ldnew = lefthand_box_ext(l,ld,depth,ThisStyle,c,&e1,&e2);
11396 ldnew->right->sector->light_level = l->outside_light_level;
11397 ldnew->right->sector->special = 0;
11398 ldnew->right->sector->floor_height = wheight;
11399 ldnew->right->sector->ceiling_height = wheight + 32 + roll(185,97);
11400 if ((ldnew->right->sector->ceiling_height>oldsector->ceiling_height) ||
11401 rollpercent(325,20))
11402 ldnew->right->sector->ceiling_height = oldsector->ceiling_height;
11403 if ((ThisStyle->window_grate)&&rollpercent(326,50)) {
11404 announce(VERBOSE,"Grated extwindow");
11405 ld->right->middle_texture = ThisStyle->grating;
11406 if (ldnew->right->sector->ceiling_height
11407 -ldnew->right->sector->floor_height<128) {
11408 /* Do something about ld->right->y_offset? */
11409 }
11410 }
11411 patch_upper(ld,t1,c);
11412 patch_lower(ld,t1,c);
11413 yoff = oldsector->ceiling_height - ldnew->right->sector->ceiling_height;
11414 e1->right->y_offset = e2->right->y_offset = yoff;
11415 ldnew = lefthand_box_ext(l,ldnew,8,ThisStyle,c,&e1,&e2);
11416 e1->right->y_offset = e2->right->y_offset = yoff;
11417 ldnew->right->sector->floor_height = wheight - 4;
11418 ldnew->right->sector->ceiling_flat = c->sky_flat;
11419 ldnew = lefthand_box(l,ldnew,8,ThisStyle,c);
11420 ldnew->right->sector->floor_height = wheight - 16;
11421 ldnew->right->sector->ceiling_height = wheight - 8;
11422 ldnew->right->sector->ceiling_flat = c->sky_flat;
11423 announce(VERBOSE,"Outside Window");
11424 } /* end if enough room */
11425 } /* end if found a linedef */
11426 }
11427
11428 /* Special room all full of pillars and stuff. TRUE if works. */
grid_room(level * l,sector * oldsector,haa * haa,style * ThisStyle,quest * ThisQuest,boolean first,config * c)11429 boolean grid_room(level *l,sector *oldsector,haa *haa,style *ThisStyle,
11430 quest *ThisQuest,boolean first, config *c)
11431 {
11432 int minx, miny, maxx, maxy;
11433 int x1,y1,xi,yi;
11434 int xcount, ycount;
11435 int xwidth, ywidth;
11436 int xspace, yspace;
11437 int secretx = -1;
11438 int secrety = -1;
11439 int sx = 0;
11440 int sy = 0;
11441 genus *m;
11442 int levels;
11443 short facing;
11444 texture *t;
11445 boolean trying_constructs;
11446
11447 find_rec(l,oldsector,&minx,&miny,&maxx,&maxy);
11448 if (maxx-minx<192) return FALSE;
11449 if (maxy-miny<192) return FALSE;
11450 xcount = (maxx-minx) / 96;
11451 xcount = 2 + roll(186,xcount-1);
11452 ycount = (maxy-miny) / 96;
11453 ycount = 2 + roll(187,ycount-1);
11454 if (xcount*ycount>100) {
11455 xcount /= 2;
11456 ycount /= 2;
11457 }
11458 xspace = (maxx-minx)/xcount;
11459 xwidth = 30 + roll(188,xspace-95);
11460 yspace = (maxy-miny)/ycount;
11461 ywidth = 30 + roll(189,yspace-95);
11462 if (rollpercent(327,40)) { /* Square pillars */
11463 if (xwidth<ywidth) ywidth = xwidth;
11464 if (ywidth<xwidth) xwidth = ywidth;
11465 }
11466
11467 switch (roll(190,6)) {
11468 case 0:
11469 case 1:
11470 case 2: t = ThisStyle->wall0; break;
11471 case 3:
11472 case 4: t = random_wall0(c,ThisStyle); break;
11473 default: t = ThisStyle->support0; break;
11474 }
11475 if ((ThisStyle->walllight!=NULL)&&rollpercent(328,3)) {
11476 announce(LOG,"Gridlight");
11477 t = ThisStyle->walllight;
11478 oldsector->light_level = 240; /* Or so */
11479 switch (roll(191,3)) {
11480 case 0: oldsector->special = RANDOM_BLINK; break;
11481 case 1: oldsector->special = SYNC_FAST_BLINK; break;
11482 default: oldsector->special = SYNC_SLOW_BLINK; break;
11483 }
11484 }
11485
11486 trying_constructs = ThisStyle->do_constructs && rollpercent(329,25);
11487 if (c->secret_monsters&&rollpercent(330,75)&&
11488 (xwidth>63)&&(ywidth>63)&&!trying_constructs) {
11489 secretx = roll(192,xcount);
11490 secrety = roll(193,ycount);
11491 }
11492 for (xi=0,x1=minx+(xspace-xwidth)/2;xi<xcount;x1+=xspace,xi++)
11493 for (yi=0,y1=miny+(yspace-ywidth)/2;yi<ycount;y1+=yspace,yi++) {
11494 if ( (xi==secretx) && (yi==secrety) ) {
11495 linedef *ld1, *ld2, *ld3, *ld4;
11496 texture *tx = ThisStyle->support0;
11497 sector *newsec = clone_sector(l,oldsector);
11498 newsec->floor_height = newsec->ceiling_height;
11499 newsec->ceiling_height += 96; /* Fixed? */
11500 if (tx==t) tx = ThisStyle->wall0;
11501 newsec->tag = new_tag(l);
11502 newsec->special = SECRET_SECTOR;
11503 parallel_innersec_ex(l,oldsector,newsec,NULL,ThisStyle->wall0,t,
11504 x1,y1,x1+xwidth,y1+ywidth,c,&ld1,&ld2,&ld3,&ld4);
11505 ld2->flags |= SECRET_LINEDEF;
11506 ld3->flags |= SECRET_LINEDEF;
11507 ld4->flags |= SECRET_LINEDEF;
11508 ld1->flags &= ~(LOWER_UNPEGGED|UPPER_UNPEGGED); /* Ought to */
11509 ld2->flags &= ~(LOWER_UNPEGGED|UPPER_UNPEGGED); /* re-y-align, */
11510 ld3->flags &= ~(LOWER_UNPEGGED|UPPER_UNPEGGED); /* also, */
11511 ld4->flags &= ~(LOWER_UNPEGGED|UPPER_UNPEGGED); /* eh? */
11512 if (rollpercent(331,50)) {
11513 ld1->right->lower_texture = tx;
11514 ld1->flags |= SECRET_LINEDEF;
11515 }
11516 ld1->tag = newsec->tag;
11517 ld1->type = ThisStyle->slifttype;
11518 flip_linedef(ld3);
11519 ld3->tag = newsec->tag;
11520 ld3->type = ThisStyle->slifttype;
11521 ld3->flags &= ~UPPER_UNPEGGED;
11522 if (linelen(ld3)>64) split_linedef(l,ld3,64,c);
11523 ld3->right->upper_texture = ThisStyle->switch0;
11524 ld3->right->x_offset = 0;
11525 ld3->right->y_offset += ThisStyle->switch0->y_bias;
11526 sx = x1+xwidth/2;
11527 sy = y1+ywidth/2;
11528 announce(VERBOSE,"Secret grid-pillar");
11529 continue;
11530 }
11531 if (rollpercent(332,c->p_grid_gaps)) continue;
11532 if (trying_constructs) {
11533 trying_constructs =
11534 install_construct(l,oldsector,x1,y1,x1+xwidth,y1+ywidth,ThisStyle,c);
11535 if (trying_constructs) announce(VERBOSE,"Grid construct");
11536 }
11537 if (!trying_constructs)
11538 parallel_innersec(l,oldsector,NULL,t,NULL,NULL,
11539 x1,y1,x1+xwidth,y1+ywidth,c);
11540 /* Pretty primitive; monster-width assumptions etc. */
11541 if (xi&&rollpercent(333,50)) {
11542 m = timely_monster(haa,c,&levels,rollpercent(334,l->p_biggest_monsters),1);
11543 if (m) {
11544 if (yi<(ycount/2)) facing = 90; else facing = 270;
11545 levels |= 0x08; /* deaf */
11546 new_thing(l,x1-32,y1+(ywidth/2),facing,m->thingid,(short)levels,c);
11547 update_haa_for_monster(haa,m,levels,1,c);
11548 }
11549 }
11550 if (yi&&rollpercent(335,50)) {
11551 m = timely_monster(haa,c,&levels,rollpercent(336,l->p_biggest_monsters),1);
11552 if (m) {
11553 if (xi<(xcount/2)) facing = 0; else facing = 180;
11554 levels |= 0x08; /* deaf */
11555 new_thing(l,x1+(xwidth/2),y1-32,facing,m->thingid,(short)levels,c);
11556 update_haa_for_monster(haa,m,levels,1,c);
11557 }
11558 }
11559 } /* end for */
11560 haa_unpend(haa);
11561
11562 /* Rather primitive bonus-depositing */
11563 for (xi=0,x1=minx+(xspace-xwidth)/2;xi<xcount;x1+=xspace,xi++)
11564 for (yi=0,y1=miny+(yspace-ywidth)/2;yi<ycount;y1+=yspace,yi++)
11565 if (yi&&rollpercent(337,30))
11566 place_timely_something(l,haa,c,x1+(xwidth/2),y1-23);
11567
11568 if (secretx!=-1) {
11569 m = timely_monster(haa,c,&levels,rollpercent(338,l->p_biggest_monsters),0);
11570 if (m) {
11571 facing = 180;
11572 levels |= 0x08; /* deaf */
11573 new_thing(l,sx,sy,facing,m->thingid,(short)levels,c);
11574 update_haa_for_monster(haa,m,levels,1,c);
11575 }
11576 /* This doesn't account for the secretness!! */
11577 /* It'll also sometimes place nothing at all! */
11578 place_timely_something(l,haa,c,sx,sy);
11579 haa_unpend(haa);
11580 }
11581
11582 announce(VERBOSE,"Grid room");
11583 return TRUE;
11584 }
11585
11586 /* Install a teleport gate in the room, and any attendant monsters */
11587 /* and stuff. Even nukage! */
install_gate(level * l,sector * s,style * ThisStyle,haa * ThisHaa,boolean force_exit_style,config * c)11588 void install_gate(level *l,sector *s,style *ThisStyle,haa *ThisHaa,
11589 boolean force_exit_style, config *c)
11590 {
11591 short lowx,lowy,hix,hiy;
11592 sector *innersec, *outersec;
11593 linedef *ld1, *ld2, *ld3, *ld4;
11594 flat *gateflat = random_gate(c,s->style);
11595 boolean exit_style = force_exit_style;
11596 boolean exit_gate = (s->gate->in_tag==0) && (s->gate->out_tag==0);
11597 texture *front;
11598 int rise = 0;
11599 short tag_mask;
11600
11601 mid_tile(l,s,&lowx,&lowy,&hix,&hiy);
11602
11603 outersec = s;
11604
11605 if (rollpercent(339,l->p_force_nukage)||rollpercent(443,10)) {
11606 int minx,miny,maxx,maxy,leeway;
11607 find_rec(l,s,&minx,&miny,&maxx,&maxy);
11608 leeway = lowx - minx;
11609 if (lowy-miny<leeway) leeway = lowy-miny;
11610 if (maxx-hix<leeway) leeway = maxx-hix;
11611 if (maxy-hiy<leeway) leeway = maxy-hiy;
11612 if (leeway>48) {
11613 leeway = 16 + roll(194,leeway-48);
11614 outersec = clone_sector(l,s);
11615 outersec->floor_height -= 8;
11616 outersec->floor_flat = s->style->nukage1;
11617 if (outersec->light_level<160) outersec->light_level = 160;
11618 outersec->special = NUKAGE1_SPECIAL;
11619 rise += 8;
11620 parallel_innersec(l,s,outersec,NULL,s->style->wall0,s->style->support0,
11621 minx+leeway,miny+leeway,maxx-leeway,maxy-leeway,c);
11622 if (s->gate->is_entry) {
11623 announce(VERBOSE,"Nukage arrival");
11624 } else {
11625 announce(VERBOSE,"Nukage gate");
11626 }
11627 ThisHaa->haas[ITYTD].health -= 10;
11628 ThisHaa->haas[HMP].health -= 5;
11629 if (s->gate->is_entry) {
11630 ThisHaa->haas[ITYTD].health -= 10;
11631 ThisHaa->haas[HMP].health -= 5;
11632 ThisHaa->haas[UV].health -= 5;
11633 }
11634 }
11635 }
11636
11637 innersec = clone_sector(l,s);
11638 if (s->gate->out_tag || exit_gate) {
11639 innersec->floor_flat = gateflat;
11640 if ((innersec->ceiling_flat!=c->sky_flat)&&ThisStyle->ceilinglight) {
11641 innersec->ceiling_flat = ThisStyle->ceilinglight;
11642 announce(VERBOSE,"gcl");
11643 }
11644 innersec->light_level = 240; /* Should vary by style or level */
11645 innersec->special = GLOW_BLINK; /* Also */
11646 }
11647 if (s->gate->in_tag) {
11648 innersec->tag = s->gate->in_tag;
11649 new_thing(l,(lowx+hix)/2,(lowy+hiy)/2,(short)(90*roll(195,4)),ID_GATEOUT,7,c);
11650 if (s->gate->is_entry) {
11651 s->entry_x = lowx+32;
11652 s->entry_y = lowy+32;
11653 }
11654 }
11655 parallel_innersec_ex(l,outersec,innersec,NULL,NULL,NULL,lowx,lowy,hix,hiy,c,
11656 &ld1,&ld2,&ld3,&ld4);
11657 if (s->gate->gate_lock) {
11658 switch (s->gate->gate_lock) {
11659 case LINEDEF_S1_OPEN_DOOR:
11660 innersec->ceiling_height = innersec->floor_height + 32;
11661 announce(LOG,"Uplocked gate");
11662 break;
11663 case LINEDEF_S1_LOWER_FLOOR:
11664 innersec->floor_height += 32;
11665 rise += 32;
11666 announce(LOG,"Downlocked gate");
11667 break;
11668 default:
11669 announce(ERROR,"Odd lock-type in install_gate");
11670 }
11671 patch_upper(ld1,s->style->wall0,c);
11672 patch_upper(ld2,s->style->wall0,c);
11673 patch_upper(ld3,s->style->wall0,c);
11674 patch_upper(ld4,s->style->wall0,c);
11675 }
11676 if (l->raise_gates) { /* Don't really want to do this for downlocked? */
11677 rise += 8;
11678 innersec->floor_height += 8;
11679 }
11680 if (c->p_use_steps) front = s->style->stepfront;
11681 else front = s->style->support0;
11682 if (rise>front->height) front = s->style->support0;
11683 patch_lower(ld1,front,c);
11684 patch_lower(ld2,front,c);
11685 patch_lower(ld3,front,c);
11686 patch_lower(ld4,front,c);
11687 if (c->monsters_can_teleport) tag_mask = 0;
11688 else tag_mask = BLOCK_MONSTERS;
11689 if (c->monsters_can_teleport)
11690 announce(VERBOSE,"Possible teleporting monsters");
11691 if (s->gate->out_tag) {
11692 ld1->type = LINEDEF_TELEPORT;
11693 ld1->flags |= tag_mask; /* Always? */
11694 ld1->tag = s->gate->out_tag;
11695 ld2->type = LINEDEF_TELEPORT;
11696 ld2->flags |= tag_mask; /* Always? */
11697 ld2->tag = s->gate->out_tag;
11698 ld3->type = LINEDEF_TELEPORT;
11699 ld3->flags |= tag_mask; /* Always? */
11700 ld3->tag = s->gate->out_tag;
11701 ld4->type = LINEDEF_TELEPORT;
11702 ld4->flags |= tag_mask; /* Always? */
11703 ld4->tag = s->gate->out_tag;
11704 } else if (0==s->gate->in_tag) { /* Must be a level-end gate */
11705 exit_style = TRUE;
11706 ld1->type = LINEDEF_W1_END_LEVEL;
11707 ld1->flags |= tag_mask;
11708 ld2->type = LINEDEF_W1_END_LEVEL;
11709 ld2->flags |= tag_mask;
11710 ld3->type = LINEDEF_W1_END_LEVEL;
11711 ld3->flags |= tag_mask;
11712 ld4->type = LINEDEF_W1_END_LEVEL;
11713 ld4->flags |= tag_mask;
11714 } /* Otherwise an in-only gate */
11715 if (exit_style) {
11716 innersec->floor_height = outersec->floor_height + 16;
11717 if (c->gate_exitsign_texture) {
11718 ld1->right->lower_texture =
11719 ld2->right->lower_texture =
11720 ld3->right->lower_texture =
11721 ld4->right->lower_texture = c->gate_exitsign_texture;
11722 } else {
11723 ld1->right->lower_texture =
11724 ld2->right->lower_texture =
11725 ld3->right->lower_texture =
11726 ld4->right->lower_texture = ThisStyle->support0;
11727 }
11728 s->middle_enhanced = TRUE;
11729 innersec->ceiling_flat = gateflat;
11730 }
11731 if (s->gate->out_tag || exit_gate)
11732 if (innersec->ceiling_flat->props&LIGHT)
11733 if (innersec->ceiling_height-innersec->floor_height>=96)
11734 if (!s->gate->gate_lock) {
11735 innersec->ceiling_height -= 16;
11736 ld1->right->upper_texture =
11737 ld2->right->upper_texture =
11738 ld3->right->upper_texture =
11739 ld4->right->upper_texture = ThisStyle->support0;
11740 }
11741 ld1->flags &= ~LOWER_UNPEGGED;
11742 ld2->flags &= ~LOWER_UNPEGGED;
11743 ld3->flags &= ~LOWER_UNPEGGED;
11744 ld4->flags &= ~LOWER_UNPEGGED;
11745
11746 } /* end install_gate */
11747
11748 /* Install the locked/hidden thing that contains the exit that */
11749 /* leads to a Secret Level. If it works, set the sl_tag and */
11750 /* sl_type things in the level. If <opens>, make it openable, */
11751 /* and immediately set sl_done. */
install_sl_exit(level * l,sector * oldsector,haa * ThisHaa,style * ThisStyle,quest * ThisQuest,boolean opens,config * c)11752 boolean install_sl_exit(level *l,sector *oldsector,haa *ThisHaa,
11753 style *ThisStyle, quest *ThisQuest,
11754 boolean opens, config *c)
11755 {
11756 int i, tries;
11757 linedef *ld, *ld2, *ld3;
11758 short tag;
11759 sector *newsec;
11760 boolean found;
11761
11762 for (found=FALSE,tries=0,ld=NULL;(!found)&&(tries<5);tries++) {
11763 i = mark_decent_boundary_linedefs(l,oldsector,72);
11764 ld = random_marked_linedef(l,i);
11765 unmark_linedefs(l);
11766 if (ld==NULL) return FALSE;
11767 if (empty_left_side(l,ld,8)) found = TRUE;
11768 }
11769 ld2 = install_switch(l,ld,TRUE,FALSE,0,ThisStyle,c,&ld3);
11770 if (ld==ld2) { /* Didn't recess after all?? */
11771 announce(WARNING,"Silly switch left sitting around?");
11772 /* Try to fix it */
11773 ld->right->middle_texture = ThisStyle->wall0;
11774 ld->type = LINEDEF_NORMAL;
11775 return FALSE;
11776 }
11777 tag = new_tag(l);
11778 ld2->type = LINEDEF_S1_SEC_LEVEL;
11779 newsec = ld2->right->sector;
11780 newsec->special = GLOW_BLINK;
11781 newsec->light_level = 255;
11782 newsec->ceiling_height = newsec->floor_height;
11783 l->sl_tag = tag;
11784 if (opens) {
11785 ld3->type = LINEDEF_NORMAL_S1_DOOR;
11786 announce(VERBOSE,"Openable sl exit");
11787 l->sl_done = TRUE;
11788 } else {
11789 newsec->tag = tag;
11790 l->sl_type = LINEDEF_W1_OPEN_DOOR; /* Or S1, eh? So... */
11791 ld->flags |= SECRET_LINEDEF;
11792 if (ThisQuest->goal==LEVEL_END_GOAL) {
11793 l->sl_open_ok = TRUE;
11794 } else {
11795 l->sl_open_ok = FALSE;
11796 l->sl_open_start = ThisQuest->room;
11797 }
11798 l->sl_exit_sector = oldsector;
11799 announce(VERBOSE,"Installed sl exit");
11800 }
11801 return TRUE;
11802 }
11803
11804 /* Try to put a triggerbox around something in this sector, */
11805 /* to open the sl exit thing. */
try_sl_triggerbox(level * l,sector * oldsector,config * c)11806 void try_sl_triggerbox(level *l, sector *oldsector, config *c)
11807 {
11808 boolean danger;
11809 thing *t;
11810 int border;
11811
11812 for (t=l->thing_anchor;t;t=t->next) {
11813 if (!(t->genus->bits&PICKABLE)) continue;
11814 if (oldsector!=point_sector(l,t->x,t->y,&border,&danger)) continue;
11815 if (border<32) continue;
11816 if (danger) continue;
11817 if ((t->options&0x07)!=0x07) continue;
11818 break;
11819 } /* end for things */
11820 if (t) {
11821 trigger_box(l,t,oldsector,l->sl_tag,l->sl_type,c);
11822 l->sl_done = TRUE;
11823 l->sl_open_ok = FALSE;
11824 announce(VERBOSE,"Did sl triggerbox");
11825 } /* if found a good thing */
11826 }
11827
11828 /* Fancy up the room, put stuff in it, install gates, etc. */
enhance_room(level * l,sector * oldsector,haa * ThisHaa,style * ThisStyle,quest * ThisQuest,boolean first,config * c)11829 void enhance_room(level *l,sector *oldsector,haa *ThisHaa,style *ThisStyle,
11830 quest *ThisQuest,boolean first, config *c)
11831 {
11832 boolean done_room = FALSE;
11833 boolean did_dm = FALSE;
11834
11835 if ((ThisQuest) && (ThisQuest->goal != NULL_GOAL) &&
11836 (need_secret_level(c)) && (l->sl_tag == 0) &&
11837 (rollpercent(340,20)||(ThisQuest->count+4>ThisQuest->minrooms)) ) {
11838 install_sl_exit(l,oldsector,ThisHaa,ThisStyle,ThisQuest,FALSE,c);
11839 }
11840
11841 if ((first)&&(oldsector->gate)) {
11842 announce(WARNING,"Gate and watermark do not mix!");
11843 }
11844
11845 if ((!done_room)&&oldsector->middle_enhanced) {
11846 /* Someone else did everything else */
11847 embellish_room(l,oldsector,ThisHaa,ThisStyle,ThisQuest,first,TRUE,c);
11848 done_room = TRUE;
11849 }
11850
11851 if ((!done_room)&&oldsector->gate) {
11852 install_gate(l,oldsector,ThisStyle,ThisHaa,FALSE,c);
11853 gate_populate(l,oldsector,ThisHaa,first,c); /* Very special-purpose! */
11854 embellish_room(l,oldsector,ThisHaa,ThisStyle,ThisQuest,first,TRUE,c);
11855 done_room = TRUE;
11856 }
11857
11858 if ((!done_room)&&(!first)&&(!oldsector->has_key)
11859 &&rollpercent(341,l->p_special_room)) {
11860 if (grid_room(l,oldsector,ThisHaa,ThisStyle,ThisQuest,first,c)) {
11861 embellish_room(l,oldsector,ThisHaa,ThisStyle,ThisQuest,first,TRUE,c);
11862 done_room = TRUE;
11863 }
11864 }
11865
11866 if (!done_room) { /* Simple randomly-enhanced room */
11867 populate(l,oldsector,c,ThisHaa,first); /* or after embellish? */
11868 embellish_room(l,oldsector,ThisHaa,ThisStyle,ThisQuest,first,FALSE,c);
11869 }
11870
11871 if (first || rollpercent(342,l->dm_rho)) {
11872 did_dm = maybe_add_dm_start(l,oldsector,c,FALSE);
11873 }
11874
11875 if (did_dm) {
11876 l->dm_rho = 10;
11877 } else {
11878 if (l->dm_rho < 80) l->dm_rho += (400 / c->minrooms);
11879 }
11880
11881 align_textures(l,oldsector,c);
11882
11883 } /* end enhance_room() */
11884
11885 /* Fancy-up the room, after all links are established, and after */
11886 /* populating with Things. If <first>, do the obvious */
11887 /* SLUMP-mark to the room, and prolly no monsters. */
embellish_room(level * l,sector * oldsector,haa * haa,style * ThisStyle,quest * ThisQuest,boolean first,boolean edges_only,config * c)11888 void embellish_room(level *l,sector *oldsector,haa *haa,style *ThisStyle,
11889 quest *ThisQuest,boolean first, boolean edges_only,
11890 config *c)
11891 {
11892 /* Just some random fun things; assumes rectangles etc */
11893 int i, border, ldlen, depth;
11894 int switch_tag = 0;
11895 linedef *switch_ld = NULL;
11896 linedef *ld = 0;
11897 boolean did_ceiling = FALSE;
11898 boolean install_closet, switch_closet;
11899
11900 if (rollpercent(343,10))
11901 if (oldsector->special==0)
11902 oldsector->special = RANDOM_BLINK;
11903
11904 if (first) { /* Watermark */
11905
11906 /* Mark the first room as SLUMP generated */
11907 watermark_sector(l,oldsector,ThisStyle,c);
11908
11909 l->first_room = oldsector; /* for later */
11910
11911 } else if (!edges_only) {
11912
11913 did_ceiling = ceiling_effect(l,oldsector,ThisStyle,haa,c);
11914
11915 if (!did_ceiling)
11916 if (rollpercent(344,0)) /* Looks silly often! */
11917 oldsector->ceiling_flat = c->sky_flat; /* Just a plain open top */
11918
11919 if (!did_ceiling)
11920 if (rollpercent(345,80)) /* high, because often fails */
11921 do_pillar(l,oldsector,ThisStyle,haa,c);
11922
11923 if (!did_ceiling)
11924 if (rollpercent(346,l->p_new_pillars))
11925 do_new_pillars(l,oldsector,ThisStyle,haa,c);
11926
11927 } /* end things we can't do if watermarking or edges_only */
11928
11929 /* Edgy-things we can do even if watermarking follow */
11930
11931 /* Perhaps triggerbox a thing in here, to open the */
11932 /* secret-level-exit thing, if any */
11933 if (l->sl_open_ok && rollpercent(347,30) && (oldsector!=l->sl_exit_sector))
11934 try_sl_triggerbox(l,oldsector,c);
11935
11936 /* One or more ambush closets */
11937 /* This should really be a separate routine! */
11938 if ((!first)||(c->immediate_monsters)) /* Not right off! */
11939 if (rollpercent(348,l->amcl_rho)) {
11940 int n, k, clen, depth, yoff, x1, y1;
11941 linedef *ldnew, *ldedge1, *ldedge2;
11942 texture *t1;
11943 boolean sky_thing = rollpercent(349,l->skyclosets);
11944 boolean crushing = FALSE;
11945 n = 1+roll(196,3);
11946 for (k=0;k<n;k++) {
11947 i = mark_decent_boundary_linedefs(l,oldsector,64);
11948 ld = random_marked_linedef(l,i);
11949 unmark_linedefs(l);
11950 if (ld!=NULL) {
11951 t1 = ld->right->middle_texture;
11952 clen = ThisStyle->closet_width;
11953 #ifndef WIDE_SKIES_OK
11954 if (sky_thing) if (clen>72) clen = 72; /* Wide skyc's look lame */
11955 #endif
11956 ldlen = linelen(ld);
11957 if (clen>ldlen) clen = ldlen;
11958 border = ( ldlen - clen ) / 2;
11959 if (border!=0) {
11960 ld = split_linedef(l,ld,border,c);
11961 split_linedef(l,ld,clen,c);
11962 }
11963 depth = ThisStyle->closet_depth;
11964 if (empty_left_side(l,ld,depth)) {
11965 genus *m;
11966 int levels;
11967 short angle, bonustype;
11968 sector *innersec, *outersec;
11969 ldnew = lefthand_box_ext(l,ld,depth,ThisStyle,c,&ldedge1,&ldedge2);
11970 outersec = ldnew->right->sector;
11971 ldnew->right->middle_texture = ldedge1->right->middle_texture;
11972 if (rollpercent(350,50))
11973 if (oldsector->ceiling_height-oldsector->floor_height>72) {
11974 outersec->ceiling_height = outersec->floor_height + 72;
11975 yoff = (oldsector->ceiling_height - oldsector->floor_height) - 72;
11976 ldnew->right->y_offset = yoff;
11977 ldedge1->right->y_offset = yoff;
11978 ldedge2->right->y_offset = yoff;
11979 patch_upper(ld,t1,c);
11980 } /* end if bigger'n 72 */
11981 if (sky_thing) {
11982 int minx,miny,maxx,maxy;
11983 announce(VERBOSE,"Sky closet");
11984 innersec = clone_sector(l,outersec);
11985 innersec->ceiling_height += 16; /* 8? 16? */
11986 find_rec(l,outersec,&minx,&miny,&maxx,&maxy);
11987 minx += 8;
11988 miny += 8;
11989 maxx -= 8;
11990 maxy -= 8;
11991 parallel_innersec(l,outersec,innersec,
11992 NULL,outersec->style->wall0,outersec->style->wall0,
11993 minx,miny,maxx,maxy,c);
11994 innersec->ceiling_flat = c->sky_flat;
11995 innersec->light_level = l->outside_light_level;
11996 if (outersec->light_level<l->bright_light_level)
11997 outersec->light_level = oldsector->light_level +
11998 roll(197,l->bright_light_level-oldsector->light_level);
11999 } else { /* Not sky closet; maybe nukage and/or crushing */
12000 outersec->light_level = oldsector->light_level +
12001 ThisStyle->closet_light_delta;
12002 if (outersec->light_level<80) outersec->light_level = 80;
12003 if (outersec->light_level>240) outersec->light_level = 240;
12004 if (rollpercent(351,2+l->p_force_nukage/2)) {
12005 outersec->floor_height -= 8;
12006 outersec->floor_flat = ThisStyle->nukage1;
12007 outersec->special = NUKAGE1_SPECIAL;
12008 if (outersec->light_level<120) outersec->light_level = 120;
12009 patch_lower(ld,ThisStyle->support0,c);
12010 announce(VERBOSE,"Nukage ambush");
12011 } /* end nukage */
12012 if (rollpercent(352,2) /* "2"? Crush! */
12013 &&(outersec->ceiling_height-outersec->floor_height<=72)
12014 &&(l->crushercount<LEVEL_MAX_CRUSHERS)) {
12015 l->crushercount++;
12016 ld->type = LINEDEF_WR_FAST_CRUSH;
12017 ld->tag = new_tag(l);
12018 ld->right->upper_texture = ThisStyle->wall0;
12019 ld->flags &= ~UPPER_UNPEGGED;
12020 ld->right->y_offset -=
12021 outersec->ceiling_height - oldsector->ceiling_height;
12022 ldnew->flags |= LOWER_UNPEGGED;
12023 ldedge1->flags |= LOWER_UNPEGGED;
12024 ldedge2->flags |= LOWER_UNPEGGED;
12025 ldnew->right->y_offset +=
12026 outersec->ceiling_height - outersec->floor_height;
12027 ldedge1->right->y_offset +=
12028 outersec->ceiling_height - outersec->floor_height;
12029 ldedge2->right->y_offset +=
12030 outersec->ceiling_height - outersec->floor_height;
12031 outersec->tag = ld->tag;
12032 outersec->ceiling_flat = random_flat0(RED,c,NULL);
12033 if (outersec->light_level>120) outersec->light_level = 120;
12034 crushing = TRUE;
12035 announce(VERBOSE,"Crush ambush");
12036 } /* end if crushing */
12037 if (oldsector->light_level - outersec->light_level >= 16) {
12038 linedef *ldnew2;
12039 announce(VERBOSE,"shadow");
12040 innersec = clone_sector(l,outersec);
12041 innersec->tag = outersec->tag;
12042 innersec->style = oldsector->style; /* Why? */
12043 innersec->light_level = oldsector->light_level;
12044 if (rollpercent(353,50)) {
12045 split_linedef(l,ldedge1,16+roll(198,20),c); /* OK depth? */
12046 ldedge1->right->sector = innersec;
12047 ldnew2 = new_linedef(l,ldedge1->to,ld->to);
12048 } else {
12049 ldedge2 =
12050 split_linedef(l,ldedge2,linelen(ldedge2)-(16+roll(199,20)),c);
12051 ldedge2->right->sector = innersec;
12052 ldnew2 = new_linedef(l,ld->from,ldedge2->from);
12053 }
12054 ldnew2->flags |= TWO_SIDED | NOT_ON_MAP;
12055 ldnew2->right = new_sidedef(l,innersec,c);
12056 ldnew2->right->middle_texture = c->null_texture;
12057 ldnew2->left = new_sidedef(l,outersec,c);
12058 ldnew2->left->middle_texture = c->null_texture;
12059 ld->left->sector = innersec;
12060 } else if ((outersec->style->ceilinglight) && (c->clights)) {
12061 outersec->ceiling_flat = outersec->style->ceilinglight;
12062 announce(VERBOSE,"acl");
12063 }
12064 } /* end general lighting effects */
12065 /* Put something in the closet */
12066 point_from(ldnew->from->x,ldnew->from->y,ldnew->to->x,ldnew->to->y,
12067 RIGHT_TURN,32,&x1,&y1);
12068 point_from(ldnew->to->x,ldnew->to->y,x1,y1,RIGHT_TURN,32,&x1,&y1);
12069 /* Eek, a monster! */
12070 m = timely_monster(haa,c,&levels,rollpercent(354,l->p_biggest_monsters),1);
12071 /* Should check for monster width here!! */
12072 if (!m) {
12073 new_thing(l,x1,y1,0,ID_POTION,7,c); /* Punt. Stub. */
12074 } else {
12075 angle = facing_right_from_ld(ldnew);
12076 new_thing(l,x1,y1,angle,m->thingid,
12077 (short)(levels|0x08),c); /* Deaf */
12078 if (rollpercent(355,50))
12079 if (m->bits&SHOOTS) ld->flags |= BLOCK_MONSTERS;
12080 update_haa_for_monster(haa,m,levels,0,c); /* zero? one? */
12081 } /* end there was a monster */
12082 /* Maybe some small bonus also */
12083 if (rollpercent(356,15) && ld->type==0) { /* Not if ld is a trap? */
12084 if (rollpercent(357,50)) { /* Health or whatever */
12085 switch (roll(200,3)) {
12086 case 0: bonustype = ID_MEDIKIT; break;
12087 case 1: bonustype = ID_STIMPACK; break;
12088 default: bonustype = ID_POTION; break;
12089 }
12090 update_haa_for_health(haa,7,bonustype);
12091 } else { /* Some ammo or whatever */
12092 if ((!(haa->haas[2].has_chainsaw))&&(rollpercent(358,5))) {
12093 bonustype = ID_CHAINSAW;
12094 haa->haas[0].has_chainsaw = TRUE;
12095 haa->haas[1].has_chainsaw = TRUE;
12096 haa->haas[2].has_chainsaw = TRUE;
12097 } else {
12098 switch (roll(201,2)) { /* What about a cell? / a rocket */
12099 case 0: bonustype = ID_CLIP; break;
12100 default: bonustype = ID_SHELLS; break;
12101 } /* end switch */
12102 update_haa_for_ammo(haa,7,bonustype);
12103 }
12104 } /* end ammo bonuses */
12105 new_thing(l,x1+1,y1+1,0,bonustype,7,c); /* Place the bonus */
12106 } /* end some small bonus */
12107 } /* end if empty space */
12108 } /* end if found a linedef */
12109 } /* end for k */
12110 } /* end if 1/n */
12111
12112 if (rollpercent(359,15)) {
12113 /* One or more wall-plaques, */
12114 /* with occasional secrets and monsters */
12115 boolean sync_doors = rollpercent(360,c->p_sync_doors);
12116 int sync_tag = -1;
12117 int sync_count = 0; /* Just for announcing */
12118 int yoff, pheight, j, pup;
12119 linedef *ldnew, *ldedge1, *ldedge2;
12120 texture *t1, *tplaque;
12121 pheight = ThisStyle->plaque->height;
12122 if (ThisStyle->plaque->props&HALF_PLAQUE)
12123 if (rollpercent(361,80)) pheight = pheight / 2;
12124 pup = ((oldsector->ceiling_height-oldsector->floor_height)-pheight)/2;
12125 pup = roll(202,pup);
12126 tplaque = ThisStyle->plaque;
12127 if (oldsector->ceiling_height-oldsector->floor_height>pheight) {
12128 for (j=1;;j++) {
12129 i = mark_decent_boundary_linedefs(l,oldsector,128); /* 128 is wrong */
12130 ld = random_marked_linedef(l,i);
12131 unmark_linedefs(l);
12132 if (ld!=NULL) {
12133 t1 = ld->right->middle_texture;
12134 ldlen = linelen(ld);
12135 /* Use borderize(TRUE), to get possible lightboxes etc */
12136 if (rollpercent(362,5)) {
12137 ThisStyle->lightboxes = TRUE;
12138 ThisStyle->auxheight = pup; /* "pheight" here is a nice bug! */
12139 announce(VERBOSE,"fancy plaque");
12140 }
12141 ld = borderize(l,ld,128,TRUE,ThisStyle,LIGHT,NULL,NULL,c);
12142 ThisStyle->lightboxes = FALSE; /* Neaten up */
12143 depth = 4 + roll(203,5) + roll(271,5);
12144 if (empty_left_side(l,ld,depth)) {
12145 announce(VERBOSE,"Putting in a plaque");
12146 ldnew = lefthand_box_ext(l,ld,depth,ThisStyle,c,&ldedge1,&ldedge2);
12147 ldnew->right->middle_texture = tplaque;
12148 if (tplaque!=ThisStyle->plaque) announce(VERBOSE,"Multiplaque");
12149 ldnew->right->sector->floor_height = oldsector->floor_height+pup;
12150 ldnew->right->sector->ceiling_height =
12151 ldnew->right->sector->floor_height + pheight;
12152 /* Maybe light the recesses */
12153 if ((ThisStyle->light_recesses)&&(ThisStyle->walllight!=NULL)) {
12154 announce(VERBOSE,"Lit plaque");
12155 ldedge2->right->middle_texture = ldedge1->right->middle_texture =
12156 ThisStyle->walllight;
12157 } else {
12158 yoff = oldsector->ceiling_height - ldnew->right->sector->ceiling_height;
12159 ldedge1->right->y_offset = yoff;
12160 ldedge2->right->y_offset = yoff;
12161 }
12162 patch_upper(ld,t1,c);
12163 patch_lower(ld,t1,c);
12164 if (rollpercent(363,60)) {
12165 ldnew->right->sector->light_level = oldsector->light_level + roll(204,21);
12166 if (ldnew->right->sector->light_level>l->bright_light_level)
12167 ldnew->right->sector->light_level = oldsector->light_level;
12168 if (rollpercent(364,40)) switch (roll(205,4)) {
12169 case 0: ldnew->right->sector->special = RANDOM_BLINK; break;
12170 case 1: ldnew->right->sector->special = SYNC_FAST_BLINK; break;
12171 case 2: ldnew->right->sector->special = SYNC_SLOW_BLINK; break;
12172 case 3: ldnew->right->sector->special = GLOW_BLINK; break;
12173 } /* end switch */
12174 } /* end if doing lights */
12175 if (pup<25)
12176 if (rollpercent(365,80)) { /* Put a secret thingie behind it! */
12177 if (sync_doors) if (sync_tag==-1) sync_tag = new_tag(l);
12178 if (NULL!=secret_closet(l,ldnew,ThisStyle,0,haa,c,TRUE,
12179 sync_tag,oldsector->ceiling_height,TRUE)) {
12180 announce(VERBOSE,"Plaque closet");
12181 if (sync_doors) {
12182 ldnew->tag = sync_tag;
12183 ldnew->type = LINEDEF_SR_OC_DOOR;
12184 if (!(c->gamemask&DOOM0_BIT))
12185 ldnew->type = LINEDEF_SR_BLAZE_OC_DOOR;
12186 if (sync_count++) announce(VERBOSE,"Synced doors");
12187 }
12188 }
12189 } /* end if secret closet */
12190 } /* end if empty space */
12191 } /* end if found a linedef */
12192 if (rollpercent(366,50)) break;
12193 if (j>4) break;
12194 tplaque = random_plaque(c,ThisStyle);
12195 if ((tplaque->height != pheight) &&
12196 ((!(tplaque->props&HALF_PLAQUE)) || (tplaque->height != 2*pheight)) ) {
12197 tplaque = ThisStyle->plaque;
12198 }
12199 } /* end for j */
12200 } /* end if tall enough room */
12201 } /* end if 1/10 */
12202
12203 /* The other kind(s) of secret closet */
12204 install_closet = FALSE;
12205 switch_closet = FALSE;
12206 if (rollpercent(367,l->p_surprise)) {
12207 install_closet = TRUE;
12208 } else if (rollpercent(368,l->p_swcloset)) {
12209 int i = mark_decent_boundary_linedefs(l,oldsector,72);
12210 switch_ld = random_marked_linedef(l,i);
12211 unmark_linedefs(l);
12212 if (switch_ld && empty_left_side(l,switch_ld,8)) {
12213 install_closet = TRUE;
12214 switch_closet = TRUE;
12215 }
12216 }
12217 if (install_closet) {
12218 thing *t = NULL;
12219 short tag = -1;
12220 int plen, pheight;
12221 boolean goal_trigger, had_map;
12222 linedef *ldc;
12223
12224 i = mark_decent_boundary_linedefs(l,oldsector,128);
12225 ld = random_marked_linedef(l,i);
12226 unmark_linedefs(l);
12227 if (switch_closet && (ld==switch_ld)) ld = NULL; /* Give up */
12228 if (ld!=NULL) {
12229 ldlen = linelen(ld);
12230 plen = ldlen - 64;
12231 if (rollpercent(369,50))
12232 if (plen>64)
12233 plen = plen - roll(206, 1 + plen-64 );
12234 if (plen>256) plen=256;
12235 border = ( ldlen - plen ) / 2;
12236 if (border!=0) {
12237 ld = split_linedef(l,ld,border,c);
12238 split_linedef(l,ld,plen,c);
12239 }
12240 goal_trigger = FALSE;
12241 if (empty_left_side(l,ld,72)) { /* "72" is from secret_closet() */
12242 if (((ThisQuest->goal==KEY_GOAL)||(ThisQuest->goal==NULL_GOAL)) &&
12243 (!switch_closet) &&
12244 (ThisQuest->auxtag==0)&&(ThisQuest->surprise==NULL)) {
12245 /* Goal-triggered, if we can */
12246 goal_trigger=TRUE;
12247 tag = new_tag(l);
12248 ThisQuest->auxtag = tag;
12249 } else if (switch_closet) { /* Switch-triggered */
12250 switch_tag = new_tag(l);
12251 tag = switch_tag;
12252 } else if (rollpercent(370,60)) { /* Immediate-triggered */
12253 boolean danger;
12254 for (t=l->thing_anchor;t;t=t->next) {
12255 if (!(t->genus->bits&PICKABLE)) continue;
12256 /* Old bug: &border in next line was NULL */
12257 if (oldsector!=point_sector(l,t->x,t->y,&border,&danger)) continue;
12258 if (border<32) continue;
12259 if (danger) continue;
12260 if ((t->options&0x07)!=0x07) continue;
12261 break;
12262 } /* end for things */
12263 if (t) {
12264 tag = new_tag(l);
12265 trigger_box(l,t,oldsector,tag,LINEDEF_WR_OPEN_DOOR,c);
12266 } /* if found a good thing */
12267 } /* if triggered closet */
12268 pheight = 72 + roll(207, 1 +
12269 (oldsector->ceiling_height - oldsector->floor_height) - 72);
12270 had_map = l->seen_map;
12271 ldc=secret_closet(l,ld,ThisStyle,pheight,(goal_trigger)?NULL:haa,
12272 c,(boolean)(tag==-1),tag,oldsector->ceiling_height,
12273 (boolean)(tag==-1));
12274 if (NULL!=ldc) {
12275 if (switch_closet) {
12276 switch_ld = install_switch(l,switch_ld,TRUE,FALSE,0,ThisStyle,c,NULL);
12277 switch_ld->tag = switch_tag;
12278 if (DOOM0_BIT&c->gamemask) switch_ld->type = LINEDEF_S1_OPEN_DOOR;
12279 else switch_ld->type = LINEDEF_S1_BLAZE_O_DOOR;
12280 announce(VERBOSE,"Switch closet");
12281 }
12282 ld->right->y_offset = (oldsector->ceiling_height -
12283 oldsector->floor_height) - 128; /* 128 should be tex-height */
12284 ld->flags |= SECRET_LINEDEF;
12285 if (tag==-1) { /* Need a subtle hint here */
12286 boolean hinted=FALSE;
12287 if (rollpercent(371,5)) {
12288 /* Use a barrel or candle */
12289 genus *g = random_barrel(c,ThisStyle);
12290 int x = (ld->from->x+ld->to->x)/2;
12291 int y = (ld->from->y+ld->to->y)/2;
12292 if ((g==NULL)||rollpercent(372,50))
12293 g = find_genus(c,ID_CANDLE);
12294 point_from(ld->from->x,ld->from->y,x,y,
12295 RIGHT_TURN,g->width/2,&x,&y);
12296 if (room_at(l,g,x,y,g->width/2,c)) {
12297 hinted=TRUE;
12298 new_thing(l,x,y,0,g->thingid,7,c);
12299 }
12300 }
12301 if ((!hinted) && had_map && rollpercent(373,15)) {
12302 /* Make 'em use the map! */
12303 hinted = TRUE;
12304 }
12305 if ((!hinted)&&rollpercent(374,40)&&
12306 (ld->right->upper_texture->y_hint!=0)) {
12307 /* Typical misalign-hint */
12308 ld->right->y_misalign = ld->right->upper_texture->y_hint;
12309 hinted = TRUE;
12310 }
12311 if ((!hinted)&&rollpercent(375,90) &&
12312 (ld->right->upper_texture->subtle!=NULL)) {
12313 /* Subtly different texture */
12314 ld->right->upper_texture = ld->right->upper_texture->subtle;
12315 announce(VERBOSE,"subtle");
12316 hinted = TRUE;
12317 }
12318 if (!hinted) {
12319 /* Make it just show on the automap (always possible) */
12320 ld->flags &= ~SECRET_LINEDEF;
12321 announce(VERBOSE,"Map hint");
12322 hinted = TRUE;
12323 }
12324 }
12325 if (goal_trigger) {
12326 ThisQuest->surprise = ldc;
12327 announce(VERBOSE,"Goal-trigger");
12328 } else if (tag!=-1) announce(VERBOSE,"Trigger");
12329 } else if (goal_trigger) {
12330 ThisQuest->auxtag = 0;
12331 } /* end else if secret closet failed and we were goal triggered */
12332 } /* end if enough room */
12333 } /* end if found a linedef */
12334 } /* end if try a closet */
12335
12336 if (rollpercent(376,l->p_extroom))
12337 make_extroom(l,oldsector,haa,ThisStyle,c);
12338
12339 if (rollpercent(377,l->p_extwindow)) { /* Trivial windows to the outside */
12340 make_extwindow(l,oldsector,ThisStyle,c);
12341 } /* end if try a window */
12342
12343 if (rollpercent(378,3) && (ThisStyle->walllight!=NULL)) {
12344 /* Lightstrips on the walls */
12345 int ll,sdepth,spec,fh,ch;
12346 announce(VERBOSE,"Doing the lightstrip thing.");
12347 sdepth = 4 + 4 * roll(208,2);
12348 ll = oldsector->light_level;
12349 if (ll<l->lit_light_level) ll+= 20;
12350 /* Mess with the light-motion sometimes */
12351 if (rollpercent(379,15)) switch (roll(209,4)) {
12352 case 0: spec = RANDOM_BLINK; break;
12353 case 1: spec = SYNC_FAST_BLINK; break;
12354 case 2: spec = SYNC_SLOW_BLINK; break;
12355 default: spec = GLOW_BLINK; break;
12356 } else spec = 0;
12357 fh = oldsector->floor_height + 8 * roll(210,8); /* Boring? */
12358 ch = fh + roll(211,24) + 24 * roll(272,2);
12359 if (ch>oldsector->ceiling_height) ch = oldsector->ceiling_height;
12360 for (ld=l->linedef_anchor;ld;ld=ld->next)
12361 if (ld->type==0)
12362 if (ld->right)
12363 if (ld->left==NULL)
12364 if (ld->right->sector==oldsector)
12365 if (lengthsquared(ld)>(8*8))
12366 if (ld->right->isBoundary)
12367 make_lightstrip(l,ld,ThisStyle,ll,sdepth,spec,fh,ch,c);
12368 } else if (rollpercent(380,20)) {
12369 /* swell some boundaries; don't do if lightstripped! */
12370 /* NOTE: produces some non-square lines! Terrible bugs! */
12371 /* But not as bad if you populate() first! */
12372 int sno, sdepth;
12373 announce(VERBOSE,"Swelling the room boundaries");
12374 sno = 1 + roll(212,2);
12375 sdepth = 4 + 4 * roll(213,4);
12376 for (ld=l->linedef_anchor;ld;ld=ld->next)
12377 if (ld->type==0)
12378 if (ld->left==NULL)
12379 if (ld->right)
12380 if (ld->right->sector==oldsector)
12381 if (lengthsquared(ld)>(16*16))
12382 if (lengthsquared(ld)>(sdepth*sdepth))
12383 if (ld->right->isBoundary)
12384 swell_linedef(l,ld,ThisStyle,c,sno,sdepth);
12385 } /* end if swell-embellish */
12386
12387 return;
12388 }
12389
12390 /* Return a gate-type link. Not many properties! */
gate_link(level * l,config * c)12391 link *gate_link(level *l,config *c)
12392 {
12393 link *answer = (link *)malloc(sizeof (*answer));
12394
12395 answer->bits = 0;
12396 answer->type = GATE_LINK;
12397 answer->next = l->link_anchor;
12398 l->link_anchor = answer;
12399 return answer;
12400 }
12401
12402
12403 /* From the given sector of the given level, make a next room nearby, */
12404 /* and return the linedefs by which they should be joined. NULL if */
12405 /* no room could be placed. The returned linedef is the one in the */
12406 /* new room; the **ldf one is the one in oldsector. */
make_next_room(level * l,sector * oldsector,boolean radical,config * c,linedef ** ldf,link ** ThisLink,quest * ThisQuest)12407 linedef *make_next_room(level *l,sector *oldsector,boolean radical,config *c,
12408 linedef **ldf, link **ThisLink, quest *ThisQuest)
12409 {
12410 linedef *newldf;
12411 int i, tries;
12412 boolean try_reduction;
12413 sector *newsector;
12414 style *ThisStyle,*NewStyle;
12415
12416 #ifdef DEBUG_QUEST_STACK
12417 {
12418 quest *q = ThisQuest;
12419 for (;q;q=q->next) printf("%d of %d; ",q->count,q->minrooms);
12420 printf("\n");
12421 }
12422 #endif
12423
12424 ThisStyle = oldsector->style;
12425 newldf = NULL;
12426 newsector = NULL;
12427 NewStyle = new_style(l,ThisStyle,radical,c);
12428 for (try_reduction=FALSE;;) {
12429 /* this loop is a hack, eh? */
12430 for (tries=0;tries<20;tries++) {
12431 i = mark_adequate_linedefs(l,oldsector,ThisStyle,c);
12432 *ldf = random_marked_linedef(l,i);
12433 unmark_linedefs(l);
12434 if (i==0) return NULL;
12435 /* Decide on a link-style for this link */
12436 if (roll(214,3)&&(link_fitsq(ThisStyle->link0,ThisQuest))) {
12437 *ThisLink = ThisStyle->link0; /* Often use style default */
12438 } else {
12439 /* Sometimes not */
12440 *ThisLink = random_link(l,*ldf,ThisStyle,ThisQuest,c);
12441 }
12442 /* If we're getting really desparate, maybe use a gate */
12443 if (l->use_gates&&try_reduction&&(tries>15)&&(ThisQuest==NULL)) {
12444 i = mark_adequate_linedefs(l,oldsector,ThisStyle,c);
12445 *ldf = random_marked_linedef(l,i);
12446 unmark_linedefs(l);
12447 *ThisLink = gate_link(l,c);
12448 announce(VERBOSE,"Gate link");
12449 }
12450 if (!link_fitsh(*ldf,*ThisLink,c)) /* Fix if doesn't fit */
12451 *ThisLink = random_link(l,*ldf,ThisStyle,ThisQuest,c);
12452 if (!link_fitsh(*ldf,*ThisLink,c))
12453 announce(WARNING,"random_link() returned too wide!!");
12454 newldf = make_linkto(l,*ldf,*ThisLink,NewStyle,c,newldf);
12455 if (!link_fitsv(l,*ldf,newldf,*ThisLink)) {
12456 announce(VERBOSE,"Retrying because link didn't fit...");
12457 continue;
12458 }
12459 newsector = generate_room_outline(l,newldf,NewStyle,try_reduction,c);
12460 if (newsector) break;
12461 announce(VERBOSE,"Retrying because new room didn't fit...");
12462 } /* end until one works */
12463 if (newsector) break;
12464 if (try_reduction) break;
12465 try_reduction = TRUE;
12466 } /* end with and without reduction */
12467 if (newsector==NULL) {
12468 if (newldf) { /* Avoid engine crashes! */
12469 delete_vertex(l,newldf->from);
12470 delete_vertex(l,newldf->to);
12471 delete_linedef(l,newldf);
12472 }
12473 newldf = NULL;
12474 }
12475
12476 return newldf;
12477
12478 } /* end make_next_room() */
12479
12480 /* Place the start positions for Players 1-4 in the given sector */
place_start_things(level * l,sector * s,config * c)12481 void place_start_things(level *l,sector *s,config *c)
12482 {
12483 int minx, miny, maxx, maxy;
12484 short angle;
12485 boolean rational_angles = rollpercent(381,90);
12486
12487 find_rec(l,s,&minx,&miny,&maxx,&maxy);
12488
12489 /* Let's make sure they always have a single-barrel shotgun */
12490 new_thing(l,(minx+maxx)/2,(miny+maxy)/2,90,ID_SHOTGUN,7,c);
12491
12492 /* Now the start positions */
12493 if (rational_angles) angle = 0; else angle = 90*roll(215,4);
12494 new_thing(l,minx+32,miny+32,angle,ID_PLAYER1,7,c); /* 1-player start */
12495 /* In the first room, *lie* about where the player comes in */
12496 s->entry_x = maxx-32;
12497 s->entry_y = maxy-32;
12498 if ((maxx-minx<128)||(maxy-miny<128)) {
12499 announce(WARNING,"Not enough room for co-op start positions");
12500 return;
12501 }
12502 if (rational_angles) angle = 0; else angle = 90*roll(216,4);
12503 new_thing(l,minx+32,maxy-32,angle,ID_PLAYER2,7,c); /* 2-player start */
12504 if (rational_angles) angle = 180; else angle = 90*roll(217,4);
12505 new_thing(l,maxx-32,miny+32,angle,ID_PLAYER3,7,c); /* 3-player start */
12506 if (rational_angles) angle = 180; else angle = 90*roll(218,4);
12507 new_thing(l,maxx-32,maxy-32,angle,ID_PLAYER4,7,c); /* 4-player start */
12508
12509 }
12510
12511 /* Set all the fields of the given level to empty things */
empty_level(level * l,config * c)12512 void empty_level(level *l, config *c)
12513 {
12514 int dieroll;
12515
12516 l->thing_anchor = NULL;
12517 l->vertex_anchor = NULL;
12518 l->sector_anchor = NULL;
12519 l->linedef_anchor = NULL;
12520 l->sidedef_anchor = NULL;
12521 l->style_anchor = NULL;
12522 l->link_anchor = NULL;
12523 l->arena_anchor = NULL;
12524 l->gate_anchor = NULL;
12525 l->used_red = FALSE;
12526 l->used_blue = FALSE;
12527 l->used_yellow = FALSE;
12528 l->last_tag_used = 0;
12529 l->sl_tag = 0;
12530 l->sl_type = 0;
12531 l->sl_done = FALSE;
12532 l->sl_open_ok = FALSE;
12533 l->sl_open_start = NULL;
12534 l->sl_exit_sector = NULL;
12535 l->first_room = NULL;
12536 l->goal_room = NULL;
12537 l->seen_suit = FALSE;
12538 l->seen_map = FALSE;
12539 l->scrolling_keylights = rollpercent(382,5);
12540 l->support_misaligns = rollpercent(383,2); /* Looks crummy! */
12541 l->skyclosets = 2;
12542 if (rollpercent(384,10)) l->skyclosets = roll(219,100);
12543 l->lift_rho = 10;
12544 if (rollpercent(385,25)) l->lift_rho = 0;
12545 if (rollpercent(386,15)) l->lift_rho = roll(220,100);
12546 l->amcl_rho = 30;
12547 if (rollpercent(387,25)) l->amcl_rho = 0;
12548 if (rollpercent(388,15)) l->amcl_rho = roll(221,100);
12549 l->p_new_pillars = 30;
12550 if (rollpercent(389,10)) l->p_new_pillars = 0;
12551 if (rollpercent(390,8)) l->p_new_pillars = 80 + roll(222,40);
12552 l->p_stair_lamps = 20;
12553 if (rollpercent(391,5)) l->p_stair_lamps = 50 + roll(223,60);
12554 l->p_force_sky = roll(224,60);
12555 if (rollpercent(392,5)) l->p_force_sky = 20 + roll(225,60);
12556 if (l->p_force_sky>30) announce(LOG,"Sunrooms");
12557 l->p_force_nukage = 0;
12558 if (rollpercent(393,8)) l->p_force_nukage = 20 + roll(226,60);
12559 if (c->major_nukage) l->p_force_nukage = 85;
12560 if (l->p_force_nukage>30) announce(LOG,"Nukage city!!");
12561 l->p_deep_baths = 20;
12562 if (rollpercent(394,50)) l->p_deep_baths += l->p_force_nukage;
12563 if (rollpercent(395,8)) l->p_deep_baths = 75 + roll(227,30);
12564 if (rollpercent(396,8)) l->p_deep_baths = 0;
12565 l->p_falling_core = 0;
12566 if (rollpercent(397,25)) l->p_falling_core = 5;
12567 if (rollpercent(398,5)) l->p_falling_core = 5 + roll(228,30);
12568 l->p_barrels = 10;
12569 if (rollpercent(399,8)) l->p_barrels = 20 + roll(229,30);
12570 if (l->p_force_nukage>30)
12571 if (rollpercent(400,50))
12572 l->p_barrels = l->p_force_nukage;
12573 l->p_extwindow = 8;
12574 if (rollpercent(401,5)) l->p_extwindow = 15 + roll(230,75);
12575 if (rollpercent(402,5)) l->p_extwindow = 0;
12576 l->p_extroom = 2;
12577 if (rollpercent(403,5)) l->p_extroom = 15 + roll(231,75);
12578 if (rollpercent(404,10)) l->p_extroom = 0;
12579 l->p_rising_room = 0;
12580 if (rollpercent(405,50)) l->p_rising_room = 6;
12581 if (rollpercent(406,5)) l->p_rising_room = 25 + roll(232,75);
12582 if (l->p_force_sky>30) {
12583 if (rollpercent(407,60))
12584 l->p_extwindow = l->p_force_sky;
12585 if (rollpercent(408,60))
12586 l->skyclosets = l->p_force_sky;
12587 if (rollpercent(409,60))
12588 l->p_extroom = l->p_force_sky;
12589 }
12590 l->p_surprise = 30;
12591 if (rollpercent(410,10)) l->p_surprise = 30 + roll(233,60);
12592 l->p_swcloset = 0;
12593 if (rollpercent(411,20)) l->p_swcloset = 5;
12594 if (rollpercent(412,10)) l->p_swcloset = 5 + roll(234,20);
12595 l->p_rational_facing = 90;
12596 if (rollpercent(413,2)) l->p_rational_facing = roll(235,100);
12597 if (rollpercent(414,10)) l->p_rational_facing = 100;
12598 {
12599 char s[80];
12600 sprintf(s,"p_rational_facing %d.",l->p_rational_facing);
12601 announce(VERBOSE,s);
12602 }
12603 l->p_biggest_monsters = 0;
12604 if (rollpercent(415,5)&&(c->big_monsters)) l->p_biggest_monsters = 100;
12605 if (c->force_biggest) l->p_biggest_monsters = 100;
12606 if (l->p_biggest_monsters==100) announce(LOG,"Biggest monsters");
12607 l->p_open_link = 15;
12608 if (rollpercent(416,15)) l->p_open_link = 0;
12609 if (rollpercent(417,20)) l->p_open_link = roll(236,100);
12610 l->p_s1_door = 20;
12611 if (rollpercent(418,10)) l->p_s1_door = roll(237,100);
12612 if (rollpercent(419,5)) l->p_s1_door = 100;
12613 if (l->p_s1_door>95)
12614 announce(VERBOSE,"Doors stick");
12615 l->p_special_room = 2 + roll(238,5);
12616 if (rollpercent(420,5)) l->p_special_room = 0;
12617 if (rollpercent(421,5)) l->p_special_room = 20 + roll(239,20);
12618 l->secret_count = 0;
12619 l->dm_count = 0;
12620 l->dm_rho = 10;
12621 l->first_room = NULL;
12622 l->skullkeys = rollpercent(422,50);
12623 l->use_gates = rollpercent(423,TELEPORTS_PERCENT);
12624 l->raise_gates = rollpercent(424,60);
12625 l->no_doors =
12626 l->all_wide_links = FALSE;
12627 if (rollpercent(425,15)) switch(roll(240,6)) {
12628 case 0:
12629 case 1:
12630 case 2: l->all_wide_links =
12631 l->no_doors = TRUE; break;
12632 case 3: l->all_wide_links = TRUE; break;
12633 case 4: l->no_doors = TRUE; break;
12634 default: break;
12635 }
12636 if (l->all_wide_links)
12637 announce(VERBOSE,"All wide links");
12638 if (l->no_doors)
12639 announce(VERBOSE,"No doors");
12640 l->hugeness = 1;
12641 if (rollpercent(426,c->do_dm ? 30 : 8)) {
12642 l->hugeness = 2;
12643 announce(LOG,"Extra hugeness");
12644 }
12645 l->outside_light_level = 240;
12646 if (rollpercent(427,20)) {
12647 l->outside_light_level = c->minlight + 5;
12648 announce(VERBOSE,"Night");
12649 }
12650 l->bright_light_level = 220;
12651 if (rollpercent(428,20)) {
12652 l->bright_light_level = c->minlight +
12653 roll(241,(221-c->minlight)/2);
12654 announce(VERBOSE,"Dim");
12655 }
12656 l->lit_light_level = 220; /* Always? */
12657 dieroll = roll(242,100);
12658 if (dieroll<10) l->maxkeys = 0;
12659 else if (dieroll<30) l->maxkeys = 1;
12660 else if (dieroll<50) l->maxkeys = 2;
12661 else l->maxkeys = 3;
12662 l->barcount = 0;
12663 l->crushercount = 0;
12664 }
12665
12666 /* Make a whole new level, assuming the player starts with the given */
12667 /* amount of health, ammo, and armor, using the given config. */
NewLevel(level * l,haa * ThisHaa,config * c)12668 void NewLevel(level *l, haa *ThisHaa, config *c)
12669 {
12670 style *ThisStyle, *NewStyle; /* basic decors */
12671 quest *ThisQuest; /* stack of pending goals */
12672 link *ThisLink, *ForkLink; /* Particular instances */
12673 linedef *ldf, *newldf = NULL;
12674 sector *oldsector, *newsector = NULL;
12675 int i, forks, nullforks;
12676 boolean done_quest = FALSE;
12677 boolean first_room = TRUE;
12678 int keys_used = 0;
12679
12680 current_level_number = c->map + (9 * c->episode) + c->mission;
12681 rng_set_level(c->episode,c->map,c->mission,c->ranseed,c->minrooms);
12682
12683 empty_level(l,c);
12684
12685 ThisStyle = random_style(l,c);
12686 ThisQuest = starting_quest(l,c);
12687
12688 ldf = starting_linedef(l,ThisStyle,c);
12689 oldsector = generate_room_outline(l,ldf,ThisStyle,TRUE,c);
12690 l->first_room = oldsector;
12691
12692 /* Make starting position(s) in the first sector */
12693 place_start_things(l,oldsector,c);
12694
12695 /* first call to embellish() will add the SLUMP watermark */
12696
12697 NewStyle = NULL;
12698
12699 for (;;) {
12700
12701 /* Are we done by virtue of room-count or similar? */
12702 ThisQuest->count++;
12703 if (enough_quest(l,oldsector,ThisQuest,c)) done_quest = 1;
12704 else done_quest = 0;
12705
12706 if (!done_quest) {
12707 newldf = make_next_room(l,oldsector,FALSE,c,&ldf,&ThisLink,NULL);
12708 if (newldf==NULL) {
12709 done_quest = 1;
12710 if (ThisQuest->next==NULL) {
12711 announce(LOG,"Self-collision; may be fewer rooms than expected.");
12712 } else {
12713 ThisQuest->next->minrooms +=
12714 ThisQuest->minrooms - ThisQuest->count;
12715 }
12716 } else {
12717 newsector = newldf->right->sector;
12718 NewStyle = newsector->style;
12719 }
12720 }
12721
12722 paint_room(l,oldsector,ThisStyle,c);
12723
12724 if (!done_quest) {
12725 establish_link(l,ldf,newldf,ThisLink,NULL,ThisStyle,NewStyle,ThisHaa,c);
12726 maybe_push_quest(l,oldsector,ThisQuest,c);
12727 } else {
12728 close_quest(l,oldsector,ThisQuest,ThisHaa,c);
12729 }
12730
12731 /*
12732 forking in here. design: if we want to fork, do
12733 another make_next_room. give up if fails. if it
12734 works, do an establish_locked_link, and push the
12735 new quest onto the stack. if the fork works, the
12736 establish_link above will have established a link
12737 to newsector in the new quest.
12738
12739 pull out all the meat into a separate function sometime.
12740 */
12741 /* Fork some number of times */
12742 for (nullforks=0,forks=0;forks<14;forks++) {
12743 linedef *lld1, *lld2;
12744 boolean radical;
12745 short newkey;
12746 if (done_quest) break;
12747 if (nullforks) break; /* Only one of these at a time */
12748 if ((forks==0)&!rollpercent(429,45)) break; /* 55% of rooms don't fork */
12749 if ((forks!=0)&!rollpercent(430,60)) break; /* 40% don't fork any more */
12750 /* This next bit should be in a routine */
12751 ThisQuest = push_quest(ThisQuest);
12752 if ((keys_used>=l->maxkeys)||
12753 (rollpercent(431,15))/*||
12754 (ThisQuest->next->goal==NULL_GOAL)*/) {
12755 if(rollpercent(433,50) && l->use_gates) {
12756 ThisQuest->goal = GATE_GOAL;
12757 }
12758 else { ThisQuest->goal = NULL_GOAL; }
12759 } else if (rollpercent(433,50) && l->use_gates) {
12760 ThisQuest->goal = GATE_GOAL;
12761 /* Everything else decided later */
12762 } else if (rollpercent(432,60) && (0!=(newkey=new_key(l)))) {
12763 ThisQuest->goal = KEY_GOAL;
12764 ThisQuest->type = newkey;
12765 } else {
12766 ThisQuest->goal = SWITCH_GOAL;
12767 ThisQuest->tag = new_tag(l);
12768 announce(LOG,"switch quest");
12769 }
12770 radical = (ThisQuest->goal != NULL_GOAL);
12771 lld1 = make_next_room(l,oldsector,radical,c,&lld2,&ForkLink,ThisQuest);
12772 if (lld1) {
12773 announce(VERBOSE,"Fork");
12774 if (forks) announce(LOG,"Multifork");
12775 establish_link(l,lld2,lld1,ForkLink,ThisQuest,ThisStyle,
12776 lld1->right->sector->style,ThisHaa,c);
12777 ThisQuest->room = lld1->right->sector;
12778 if (ThisQuest->goal==NULL_GOAL) {
12779 ThisQuest->minrooms = 1 + roll(243,4);
12780 nullforks++;
12781 } else {
12782 ThisQuest->minrooms =
12783 1 + roll(244,ThisQuest->next->minrooms-ThisQuest->next->count);
12784 }
12785 ThisQuest->next->minrooms -= ThisQuest->minrooms;
12786 if (ThisQuest->next->minrooms<1)
12787 ThisQuest->next->minrooms = 1;
12788 if (ThisQuest->goal==KEY_GOAL) {
12789 announce(LOG,"Key thing");
12790 keys_used++;
12791 }
12792 } else {
12793 ThisQuest = pop_quest(ThisQuest); /* Oh, well! */
12794 break; /* No sense in trying any more, eh? */
12795 }
12796 } /* end for */
12797
12798 /* See if it's OK to put in a secret-level exit-opener yet */
12799 if (oldsector==l->sl_open_start) l->sl_open_ok = TRUE;
12800
12801 /* Fancy up and fill in the room itself */
12802 enhance_room(l,oldsector,ThisHaa,ThisStyle,ThisQuest,first_room,c);
12803 first_room = FALSE;
12804
12805 /* Now get ready for the next pass */
12806 if (!done_quest) {
12807 oldsector = newsector;
12808 ThisStyle = NewStyle;
12809 } else {
12810 close_quest_final(l,oldsector,ThisQuest,ThisHaa,c);
12811 if (ThisQuest->next==NULL) break; /* We're done! */
12812 oldsector = ThisQuest->room;
12813 ThisStyle = oldsector->style;
12814 ThisQuest = pop_quest(ThisQuest);
12815 }
12816
12817 } /* end forever */
12818
12819 /* Hack to avoid ammo-starvation in megawads due to leftbehinds */
12820 /* Also turn off berserk effect */
12821 for (i=ITYTD;i<=UV;i++) {
12822 ThisHaa->haas[i].ammo *= (float)0.75; /* awful! */
12823 ThisHaa->haas[i].has_berserk = FALSE;
12824 }
12825
12826 /* Sometimes turn on big stuff; probably too simple */
12827 if (c->big_weapons) c->big_monsters |= rollpercent(434,50);
12828 else c->big_monsters |= rollpercent(435,15);
12829 if (c->big_monsters) c->big_weapons |= rollpercent(436,50);
12830 else c->big_weapons |= rollpercent(437,15);
12831
12832 /* Do some final global twiddles */
12833 global_paint_HOMs(l,c);
12834 global_align_textures(l,c);
12835 global_fixups(l);
12836
12837 /* Warn if we failed to do a secret level */
12838 if (need_secret_level(c) && !l->sl_done)
12839 announce(WARNING,"Secret level(s) may be unreachable; durn!");
12840
12841 /* Add emergency deathmatch starts, if -dm and needed, announce count */
12842 if (c->do_dm) {
12843 char s[80];
12844 for (;l->dm_count<4;) {
12845 if (maybe_add_dm_start(l,l->first_room,c,TRUE)) continue;
12846 if (maybe_add_dm_start(l,l->goal_room,c,TRUE)) continue;
12847 announce(ERROR,"Not enough deathmatch starts!");
12848 break;
12849 }
12850 sprintf(s,"%d deathmatch starts.",l->dm_count);
12851 announce(LOG,s);
12852 }
12853
12854 /* and finally, always have at least one "secret", for the 100% */
12855 if (l->secret_count==0)
12856 if (l->first_room)
12857 l->first_room->special = SECRET_SECTOR;
12858
12859 }
12860
12861 /****** the end of SLUMP.C ********* please come again *********/
12862
12863 /*
12864 Now admit it; what was more fun than farting around with
12865 level-editors, wasn't it?
12866 */
12867