1 /*
2 ** p_terrain.cpp
3 ** Terrain maintenance
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 1998-2006 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 ** notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 ** notice, this list of conditions and the following disclaimer in the
17 ** documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 ** derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34
35 // HEADER FILES ------------------------------------------------------------
36
37 #include <string.h>
38
39 #include "doomtype.h"
40 #include "cmdlib.h"
41 #include "p_terrain.h"
42 #include "gi.h"
43 #include "r_state.h"
44 #include "w_wad.h"
45 #include "sc_man.h"
46 #include "s_sound.h"
47 #include "p_local.h"
48 #include "templates.h"
49 #include "farchive.h"
50
51 // MACROS ------------------------------------------------------------------
52
53 #define SET_FIELD(type,val) *((type*)((BYTE *)fields + \
54 parser[keyword].u.Offset)) = val;
55
56 // TYPES -------------------------------------------------------------------
57
58 enum EOuterKeywords
59 {
60 OUT_SPLASH,
61 OUT_TERRAIN,
62 OUT_FLOOR,
63 OUT_IFDOOM,
64 OUT_IFHERETIC,
65 OUT_IFHEXEN,
66 OUT_IFSTRIFE,
67 OUT_ENDIF,
68 OUT_DEFAULTTERRAIN
69 };
70
71 enum ETerrainKeywords
72 {
73 TR_CLOSE,
74 TR_SPLASH,
75 TR_DAMAGEAMOUNT,
76 TR_DAMAGETYPE,
77 TR_DAMAGETIMEMASK,
78 TR_FOOTCLIP,
79 TR_STEPVOLUME,
80 TR_WALKINGSTEPTIME,
81 TR_RUNNINGSTEPTIME,
82 TR_LEFTSTEPSOUNDS,
83 TR_RIGHTSTEPSOUNDS,
84 TR_LIQUID,
85 TR_FRICTION,
86 TR_ALLOWPROTECTION
87 };
88
89 enum EGenericType
90 {
91 GEN_End,
92 GEN_Fixed,
93 GEN_Sound,
94 GEN_Byte,
95 GEN_Class,
96 GEN_Splash,
97 GEN_Float,
98 GEN_Time,
99 GEN_Bool,
100 GEN_Int,
101 GEN_Custom,
102 };
103
104 struct FGenericParse
105 {
106 EGenericType Type;
107 union {
108 size_t Offset;
109 void (*Handler) (FScanner &sc, int type, void *fields);
110 } u;
111 };
112
113 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
114
115 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
116
117 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
118
119 static void MakeDefaultTerrain ();
120 static void ParseOuter (FScanner &sc);
121 static void ParseSplash (FScanner &sc);
122 static void ParseTerrain (FScanner &sc);
123 static void ParseFloor (FScanner &sc);
124 static int FindSplash (FName name);
125 static void GenericParse (FScanner &sc, FGenericParse *parser, const char **keywords,
126 void *fields, const char *type, FName name);
127 static void ParseDamage (FScanner &sc, int keyword, void *fields);
128 static void ParseFriction (FScanner &sc, int keyword, void *fields);
129 static void ParseDefault (FScanner &sc);
130
131 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
132
133 // PUBLIC DATA DEFINITIONS -------------------------------------------------
134
135 FTerrainTypeArray TerrainTypes;
136 TArray<FSplashDef> Splashes;
137 TArray<FTerrainDef> Terrains;
138 WORD DefaultTerrainType;
139
140 // PRIVATE DATA DEFINITIONS ------------------------------------------------
141
142 static const char *OuterKeywords[] =
143 {
144 "splash",
145 "terrain",
146 "floor",
147 "ifdoom",
148 "ifheretic",
149 "ifhexen",
150 "ifstrife",
151 "endif",
152 "defaultterrain",
153 NULL
154 };
155
156 static const char *SplashKeywords[] =
157 {
158 "}",
159 "smallsound",
160 "smallclip",
161 "sound",
162 "smallclass",
163 "baseclass",
164 "chunkclass",
165 "chunkxvelshift",
166 "chunkyvelshift",
167 "chunkzvelshift",
168 "chunkbasezvel",
169 "noalert",
170 NULL
171 };
172
173 static const char *TerrainKeywords[] =
174 {
175 "}",
176 "splash",
177 "damageamount",
178 "damagetype",
179 "damagetimemask",
180 "footclip",
181 "stepvolume",
182 "walkingsteptime",
183 "runningsteptime",
184 "leftstepsounds",
185 "rightstepsounds",
186 "liquid",
187 "friction",
188 "allowprotection",
189 NULL
190 };
191
192 static FGenericParse SplashParser[] =
193 {
194 { GEN_End, {0} },
195 { GEN_Sound, {myoffsetof(FSplashDef, SmallSplashSound)} },
196 { GEN_Fixed, {myoffsetof(FSplashDef, SmallSplashClip)} },
197 { GEN_Sound, {myoffsetof(FSplashDef, NormalSplashSound)} },
198 { GEN_Class, {myoffsetof(FSplashDef, SmallSplash)} },
199 { GEN_Class, {myoffsetof(FSplashDef, SplashBase)} },
200 { GEN_Class, {myoffsetof(FSplashDef, SplashChunk)} },
201 { GEN_Byte, {myoffsetof(FSplashDef, ChunkXVelShift)} },
202 { GEN_Byte, {myoffsetof(FSplashDef, ChunkYVelShift)} },
203 { GEN_Byte, {myoffsetof(FSplashDef, ChunkZVelShift)} },
204 { GEN_Fixed, {myoffsetof(FSplashDef, ChunkBaseZVel)} },
205 { GEN_Bool, {myoffsetof(FSplashDef, NoAlert)} }
206 };
207
208 static FGenericParse TerrainParser[] =
209 {
210 { GEN_End, {0} },
211 { GEN_Splash, {myoffsetof(FTerrainDef, Splash)} },
212 { GEN_Int, {myoffsetof(FTerrainDef, DamageAmount)} },
213 { GEN_Custom, {(size_t)ParseDamage} },
214 { GEN_Int, {myoffsetof(FTerrainDef, DamageTimeMask)} },
215 { GEN_Fixed, {myoffsetof(FTerrainDef, FootClip)} },
216 { GEN_Float, {myoffsetof(FTerrainDef, StepVolume)} },
217 { GEN_Time, {myoffsetof(FTerrainDef, WalkStepTics)} },
218 { GEN_Time, {myoffsetof(FTerrainDef, RunStepTics)} },
219 { GEN_Sound, {myoffsetof(FTerrainDef, LeftStepSound)} },
220 { GEN_Sound, {myoffsetof(FTerrainDef, RightStepSound)} },
221 { GEN_Bool, {myoffsetof(FTerrainDef, IsLiquid)} },
222 { GEN_Custom, {(size_t)ParseFriction} },
223 { GEN_Bool, {myoffsetof(FTerrainDef, AllowProtection)} },
224 };
225
226
227
228 // CODE --------------------------------------------------------------------
229
230 //==========================================================================
231 //
232 // P_InitTerrainTypes
233 //
234 //==========================================================================
235
P_InitTerrainTypes()236 void P_InitTerrainTypes ()
237 {
238 int lastlump;
239 int lump;
240 int size;
241
242 Splashes.Clear();
243 Terrains.Clear();
244 size = (TexMan.NumTextures()+1);
245 TerrainTypes.Resize(size);
246 TerrainTypes.Clear();
247
248 MakeDefaultTerrain ();
249
250 lastlump = 0;
251 while (-1 != (lump = Wads.FindLump ("TERRAIN", &lastlump)) )
252 {
253 FScanner sc(lump);
254 ParseOuter (sc);
255 }
256 Splashes.ShrinkToFit ();
257 Terrains.ShrinkToFit ();
258 }
259
260 //==========================================================================
261 //
262 // MakeDefaultTerrain
263 //
264 //==========================================================================
265
MakeDefaultTerrain()266 static void MakeDefaultTerrain ()
267 {
268 FTerrainDef def;
269
270 memset (&def, 0, sizeof(def));
271 def.Name = "Solid";
272 def.Splash = -1;
273 Terrains.Push (def);
274 }
275
276 //==========================================================================
277 //
278 // ParseOuter
279 //
280 //==========================================================================
281
ParseOuter(FScanner & sc)282 static void ParseOuter (FScanner &sc)
283 {
284 int bracedepth = 0;
285 bool ifskip = false;
286
287 while (sc.GetString ())
288 {
289 if (ifskip)
290 {
291 if (bracedepth > 0)
292 {
293 if (sc.Compare ("}"))
294 {
295 bracedepth--;
296 continue;
297 }
298 }
299 else if (sc.Compare ("endif"))
300 {
301 ifskip = false;
302 continue;
303 }
304 if (sc.Compare ("{"))
305 {
306 bracedepth++;
307 }
308 else if (sc.Compare ("}"))
309 {
310 sc.ScriptError ("Too many left braces ('}')");
311 }
312 }
313 else
314 {
315 switch (sc.MustMatchString (OuterKeywords))
316 {
317 case OUT_SPLASH:
318 ParseSplash (sc);
319 break;
320
321 case OUT_TERRAIN:
322 ParseTerrain (sc);
323 break;
324
325 case OUT_FLOOR:
326 ParseFloor (sc);
327 break;
328
329 case OUT_DEFAULTTERRAIN:
330 ParseDefault (sc);
331 break;
332
333 case OUT_IFDOOM:
334 case OUT_IFHERETIC:
335 case OUT_IFHEXEN:
336 case OUT_IFSTRIFE:
337 ifskip = !CheckGame(sc.String+2, true);
338 break;
339
340 case OUT_ENDIF:
341 break;
342 }
343 }
344 }
345 }
346
347 //==========================================================================
348 //
349 // SetSplashDefaults
350 //
351 //==========================================================================
352
SetSplashDefaults(FSplashDef * splashdef)353 static void SetSplashDefaults (FSplashDef *splashdef)
354 {
355 splashdef->SmallSplashSound =
356 splashdef->NormalSplashSound = 0;
357 splashdef->SmallSplash =
358 splashdef->SplashBase =
359 splashdef->SplashChunk = NULL;
360 splashdef->ChunkXVelShift =
361 splashdef->ChunkYVelShift =
362 splashdef->ChunkZVelShift = 8;
363 splashdef->ChunkBaseZVel = FRACUNIT;
364 splashdef->SmallSplashClip = 12*FRACUNIT;
365 splashdef->NoAlert = false;
366 }
367
368 //==========================================================================
369 //
370 // ParseSplash
371 //
372 //==========================================================================
373
ParseSplash(FScanner & sc)374 void ParseSplash (FScanner &sc)
375 {
376 int splashnum;
377 FSplashDef *splashdef;
378 bool isnew = false;
379 FName name;
380
381 sc.MustGetString ();
382 name = sc.String;
383 splashnum = (int)FindSplash (name);
384 if (splashnum < 0)
385 {
386 FSplashDef def;
387 SetSplashDefaults (&def);
388 def.Name = name;
389 splashnum = (int)Splashes.Push (def);
390 isnew = true;
391 }
392 splashdef = &Splashes[splashnum];
393
394 sc.MustGetString ();
395 if (!sc.Compare ("modify"))
396 { // Set defaults
397 if (!isnew)
398 { // New ones already have their defaults set before they're pushed.
399 SetSplashDefaults (splashdef);
400 }
401 }
402 else
403 {
404 sc.MustGetString();
405 }
406 if (!sc.Compare ("{"))
407 {
408 sc.ScriptError ("Expected {");
409 }
410 else
411 {
412 GenericParse (sc, SplashParser, SplashKeywords, splashdef, "splash",
413 splashdef->Name);
414 }
415 }
416
417 //==========================================================================
418 //
419 // ParseTerrain
420 //
421 //==========================================================================
422
ParseTerrain(FScanner & sc)423 void ParseTerrain (FScanner &sc)
424 {
425 int terrainnum;
426 FName name;
427
428 sc.MustGetString ();
429 name = sc.String;
430 terrainnum = (int)P_FindTerrain (name);
431 if (terrainnum < 0)
432 {
433 FTerrainDef def;
434 memset (&def, 0, sizeof(def));
435 def.Splash = -1;
436 def.Name = name;
437 terrainnum = (int)Terrains.Push (def);
438 }
439
440 // Set defaults
441 sc.MustGetString ();
442 if (!sc.Compare ("modify"))
443 {
444 name = Terrains[terrainnum].Name;
445 memset (&Terrains[terrainnum], 0, sizeof(FTerrainDef));
446 Terrains[terrainnum].Splash = -1;
447 Terrains[terrainnum].Name = name;
448 }
449 else
450 {
451 sc.MustGetString ();
452 }
453
454 if (sc.Compare ("{"))
455 {
456 GenericParse (sc, TerrainParser, TerrainKeywords, &Terrains[terrainnum],
457 "terrain", Terrains[terrainnum].Name);
458 }
459 else
460 {
461 sc.ScriptError ("Expected {");
462 }
463 }
464
465 //==========================================================================
466 //
467 // ParseDamage
468 //
469 //==========================================================================
470
ParseDamage(FScanner & sc,int keyword,void * fields)471 static void ParseDamage (FScanner &sc, int keyword, void *fields)
472 {
473 FTerrainDef *def = (FTerrainDef *)fields;
474
475 sc.MustGetString ();
476 // Lava is synonymous with Fire here!
477 if (sc.Compare("Lava")) def->DamageMOD=NAME_Fire;
478 else def->DamageMOD=sc.String;
479 }
480
481 //==========================================================================
482 //
483 // ParseFriction
484 //
485 //==========================================================================
486
ParseFriction(FScanner & sc,int keyword,void * fields)487 static void ParseFriction (FScanner &sc, int keyword, void *fields)
488 {
489 FTerrainDef *def = (FTerrainDef *)fields;
490 fixed_t friction, movefactor;
491
492 sc.MustGetFloat ();
493
494 // These calculations should match those in P_SetSectorFriction().
495 // A friction of 1.0 is equivalent to ORIG_FRICTION.
496
497 friction = (fixed_t)(0x1EB8*(sc.Float*100))/0x80 + 0xD001;
498 friction = clamp<fixed_t> (friction, 0, FRACUNIT);
499
500 if (friction > ORIG_FRICTION) // ice
501 movefactor = ((0x10092 - friction) * 1024) / 4352 + 568;
502 else
503 movefactor = ((friction - 0xDB34)*(0xA))/0x80;
504
505 if (movefactor < 32)
506 movefactor = 32;
507
508 def->Friction = friction;
509 def->MoveFactor = movefactor;
510 }
511
512 //==========================================================================
513 //
514 // GenericParse
515 //
516 //==========================================================================
517
GenericParse(FScanner & sc,FGenericParse * parser,const char ** keywords,void * fields,const char * type,FName name)518 static void GenericParse (FScanner &sc, FGenericParse *parser, const char **keywords,
519 void *fields, const char *type, FName name)
520 {
521 bool notdone = true;
522 int keyword;
523 int val = 0;
524 const PClass *info;
525
526 do
527 {
528 sc.MustGetString ();
529 keyword = sc.MustMatchString (keywords);
530 switch (parser[keyword].Type)
531 {
532 case GEN_End:
533 notdone = false;
534 break;
535
536 case GEN_Fixed:
537 sc.MustGetFloat ();
538 SET_FIELD (fixed_t, (fixed_t)(FRACUNIT * sc.Float));
539 break;
540
541 case GEN_Sound:
542 sc.MustGetString ();
543 SET_FIELD (FSoundID, FSoundID(sc.String));
544 /* unknown sounds never produce errors anywhere else so they shouldn't here either.
545 if (val == 0)
546 {
547 Printf ("Unknown sound %s in %s %s\n",
548 sc.String, type, name.GetChars());
549 }
550 */
551 break;
552
553 case GEN_Byte:
554 sc.MustGetNumber ();
555 SET_FIELD (BYTE, sc.Number);
556 break;
557
558 case GEN_Class:
559 sc.MustGetString ();
560 if (sc.Compare ("None"))
561 {
562 info = NULL;
563 }
564 else
565 {
566 info = PClass::FindClass (sc.String);
567 if (!info->IsDescendantOf (RUNTIME_CLASS(AActor)))
568 {
569 Printf ("%s is not an Actor (in %s %s)\n",
570 sc.String, type, name.GetChars());
571 info = NULL;
572 }
573 else if (info == NULL)
574 {
575 Printf ("Unknown actor %s in %s %s\n",
576 sc.String, type, name.GetChars());
577 }
578 }
579 SET_FIELD (const PClass *, info);
580 break;
581
582 case GEN_Splash:
583 sc.MustGetString ();
584 val = FindSplash (sc.String);
585 SET_FIELD (int, val);
586 if (val == -1)
587 {
588 Printf ("Splash %s is not defined yet (in %s %s)\n",
589 sc.String, type, name.GetChars());
590 }
591 break;
592
593 case GEN_Float:
594 sc.MustGetFloat ();
595 SET_FIELD (float, float(sc.Float));
596 break;
597
598 case GEN_Time:
599 sc.MustGetFloat ();
600 SET_FIELD (int, (int)(sc.Float * TICRATE));
601 break;
602
603 case GEN_Bool:
604 SET_FIELD (bool, true);
605 break;
606
607 case GEN_Int:
608 sc.MustGetNumber ();
609 SET_FIELD (int, sc.Number);
610 break;
611
612 case GEN_Custom:
613 parser[keyword].u.Handler (sc, keyword, fields);
614 break;
615 }
616 } while (notdone);
617 }
618
619 //==========================================================================
620 //
621 // ParseFloor
622 //
623 //==========================================================================
624
ParseFloor(FScanner & sc)625 static void ParseFloor (FScanner &sc)
626 {
627 FTextureID picnum;
628 int terrain;
629
630 bool opt = sc.CheckString("optional");
631 sc.MustGetString ();
632
633 picnum = TexMan.CheckForTexture (sc.String, FTexture::TEX_Flat,
634 FTextureManager::TEXMAN_Overridable|FTextureManager::TEXMAN_TryAny);
635
636 if (!picnum.Exists())
637 {
638 if (!opt)
639 {
640 Printf("Unknown flat %s\n", sc.String);
641 }
642 sc.MustGetString();
643 return;
644 }
645 sc.MustGetString ();
646 terrain = P_FindTerrain (sc.String);
647 if (terrain == -1)
648 {
649 Printf ("Unknown terrain %s\n", sc.String);
650 terrain = 0;
651 }
652 TerrainTypes.Set(picnum.GetIndex(), terrain);
653 }
654
655 //==========================================================================
656 //
657 // ParseDefault
658 //
659 //==========================================================================
660
ParseDefault(FScanner & sc)661 static void ParseDefault (FScanner &sc)
662 {
663 int terrain;
664
665 sc.MustGetString ();
666 terrain = P_FindTerrain (sc.String);
667 if (terrain == -1)
668 {
669 Printf ("Unknown terrain %s\n", sc.String);
670 terrain = 0;
671 }
672 DefaultTerrainType = terrain;
673 }
674
675 //==========================================================================
676 //
677 // FindSplash
678 //
679 //==========================================================================
680
FindSplash(FName name)681 int FindSplash (FName name)
682 {
683 unsigned int i;
684
685 for (i = 0; i < Splashes.Size (); i++)
686 {
687 if (Splashes[i].Name == name)
688 {
689 return (int)i;
690 }
691 }
692 return -1;
693 }
694
695 //==========================================================================
696 //
697 // FindTerrain
698 //
699 //==========================================================================
700
P_FindTerrain(FName name)701 int P_FindTerrain (FName name)
702 {
703 unsigned int i;
704
705 for (i = 0; i < Terrains.Size (); i++)
706 {
707 if (Terrains[i].Name == name)
708 {
709 return (int)i;
710 }
711 }
712 return -1;
713 }
714
P_SerializeTerrain(FArchive & arc,int & terrainnum)715 void P_SerializeTerrain(FArchive &arc, int &terrainnum)
716 {
717 FName val;
718 if (arc.IsStoring())
719 {
720 if (terrainnum < 0 || terrainnum >= (int)Terrains.Size())
721 {
722 val = NAME_Null;
723 }
724 else
725 {
726 val = Terrains[terrainnum].Name;
727 }
728 arc << val;
729 }
730 else
731 {
732 arc << val;
733 terrainnum = P_FindTerrain(val);
734
735 }
736 }
737