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