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