1 //----------------------------------------------------------------------------
2 //  EDGE Data Definition File Code (Sectors)
3 //----------------------------------------------------------------------------
4 //
5 //  Copyright (c) 1999-2008  The EDGE Team.
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License
9 //  as published by the Free Software Foundation; either version 2
10 //  of the License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 //
17 //----------------------------------------------------------------------------
18 //
19 // Sector Setup and Parser Code
20 //
21 // -KM- 1998/09/27 Written.
22 
23 #include "local.h"
24 
25 #include "line.h"
26 
27 #undef  DF
28 #define DF  DDF_FIELD
29 
30 #define DDF_SectHashFunc(x)  (((x) + LOOKUP_CACHESIZE) % LOOKUP_CACHESIZE)
31 
32 static sectortype_c *dynamic_sector;
33 
34 sectortype_container_c sectortypes; 	// <-- User-defined
35 
36 static sectortype_c *default_sector;
37 
38 void DDF_SectGetSpecialFlags(const char *info, void *storage);
39 static void DDF_SectMakeCrush(const char *info);
40 
41 #undef  DDF_CMD_BASE
42 #define DDF_CMD_BASE  dummy_sector
43 static sectortype_c dummy_sector;
44 
45 static const commandlist_t sect_commands[] =
46 {
47 	// sub-commands
48 	DDF_SUB_LIST("FLOOR",    f,  floor_commands),
49 	DDF_SUB_LIST("CEILING",  c,  floor_commands),
50 	DDF_SUB_LIST("DAMAGE",   damage, damage_commands),
51 
52 	DF("SECRET", secret, DDF_MainGetBoolean),
53 	DF("HUB", hub, DDF_MainGetBoolean),
54 	DF("SPECIAL", special_flags, DDF_SectGetSpecialFlags),
55 
56 	DF("LIGHT_TYPE", l.type, DDF_SectGetLighttype),
57 	DF("LIGHT_LEVEL", l.level, DDF_MainGetNumeric),
58 	DF("LIGHT_DARKTIME", l.darktime, DDF_MainGetTime),
59 	DF("LIGHT_BRIGHTTIME", l.brighttime, DDF_MainGetTime),
60 	DF("LIGHT_CHANCE", l.chance, DDF_MainGetPercent),
61 	DF("LIGHT_SYNC", l.sync, DDF_MainGetTime),
62 	DF("LIGHT_STEP", l.step, DDF_MainGetNumeric),
63 	DF("EXIT", e_exit, DDF_SectGetExit),
64 	DF("USE_COLOURMAP", use_colourmap, DDF_MainGetColourmap),
65 	DF("GRAVITY", gravity, DDF_MainGetFloat),
66 	DF("FRICTION", friction, DDF_MainGetFloat),
67 	DF("VISCOSITY", viscosity, DDF_MainGetFloat),
68 	DF("DRAG", drag, DDF_MainGetFloat),
69 	DF("AMBIENT_SOUND", ambient_sfx, DDF_MainLookupSound),
70 	DF("SPLASH_SOUND", splash_sfx, DDF_MainLookupSound),
71 	DF("WHEN_APPEAR", appear, DDF_MainGetWhenAppear),
72 	DF("PUSH_ANGLE", push_angle, DDF_MainGetAngle),
73 	DF("PUSH_SPEED", push_speed, DDF_MainGetFloat),
74 	DF("PUSH_ZSPEED", push_zspeed, DDF_MainGetFloat),
75 
76 	// -AJA- backwards compatibility cruft...
77 	DF("DAMAGE",     damage.nominal, DDF_MainGetFloat),
78 	DF("DAMAGETIME", damage.delay,   DDF_MainGetTime),
79 
80 	DDF_CMD_END
81 };
82 
83 //
84 //  DDF PARSE ROUTINES
85 //
86 
87 //
88 // SectorStartEntry
89 //
SectorStartEntry(const char * name,bool extend)90 static void SectorStartEntry(const char *name, bool extend)
91 {
92 	int number = MAX(0, atoi(name));
93 
94 	if (number == 0)
95 		DDF_Error("Bad sectortype number in sectors.ddf: %s\n", name);
96 
97 	dynamic_sector = sectortypes.Lookup(number);
98 
99 	if (extend)
100 	{
101 		if (! dynamic_sector)
102 			DDF_Error("Unknown sectortype to extend: %s\n", name);
103 		return;
104 	}
105 
106 	// replaces an existing entry?
107 	if (dynamic_sector)
108 	{
109 		dynamic_sector->Default();
110 		return;
111 	}
112 
113 	// not found, create a new one
114 	dynamic_sector = new sectortype_c;
115 	dynamic_sector->number = number;
116 
117 	sectortypes.Insert(dynamic_sector);
118 }
119 
120 
SectorDoTemplate(const char * contents)121 static void SectorDoTemplate(const char *contents)
122 {
123 	int number = MAX(0, atoi(contents));
124 	if (number == 0)
125 		DDF_Error("Bad sectortype number for template: %s\n", contents);
126 
127 	sectortype_c *other = sectortypes.Lookup(number);
128 
129 	if (!other || other == dynamic_sector)
130 		DDF_Error("Unknown sector template: '%s'\n", contents);
131 
132 	dynamic_sector->CopyDetail(*other);
133 }
134 
135 
136 //
137 // SectorParseField
138 //
SectorParseField(const char * field,const char * contents,int index,bool is_last)139 static void SectorParseField(const char *field, const char *contents,
140 							 int index, bool is_last)
141 {
142 #if (DEBUG_DDF)
143 	I_Debugf("SECTOR_PARSE: %s = %s;\n", field, contents);
144 #endif
145 
146 	if (DDF_CompareName(field, "TEMPLATE") == 0)
147 	{
148 		SectorDoTemplate(contents);
149 		return;
150 	}
151 
152 	// backwards compatibility...
153 	if (DDF_CompareName(field, "CRUSH") == 0 ||
154 	    DDF_CompareName(field, "CRUSH_DAMAGE") == 0)
155 	{
156 		DDF_SectMakeCrush(contents);
157 		return;
158 	}
159 
160 	if (DDF_MainParseField(sect_commands, field, contents, (byte *)dynamic_sector))
161 		return;  // OK
162 
163 	DDF_WarnError("Unknown sectors.ddf command: %s\n", field);
164 }
165 
166 //
167 // SectorFinishEntry
168 //
SectorFinishEntry(void)169 static void SectorFinishEntry(void)
170 {
171 	// TODO: check stuff
172 }
173 
174 //
175 // SectorClearAll
176 //
SectorClearAll(void)177 static void SectorClearAll(void)
178 {
179 	// 100% safe to delete all sector types
180 	sectortypes.Reset();
181 }
182 
183 //
184 // DDF_ReadSectors
185 //
DDF_ReadSectors(void * data,int size)186 bool DDF_ReadSectors(void *data, int size)
187 {
188 	readinfo_t sects;
189 
190 	sects.memfile = (char*)data;
191 	sects.memsize = size;
192 	sects.tag = "SECTORS";
193 	sects.entries_per_dot = 1;
194 
195 	if (sects.memfile)
196 	{
197 		sects.message = NULL;
198 		sects.filename = NULL;
199 		sects.lumpname = "DDFSECT";
200 	}
201 	else
202 	{
203 		sects.message = "DDF_InitSectors";
204 		sects.filename = "sectors.ddf";
205 		sects.lumpname = NULL;
206 	}
207 
208 	sects.start_entry  = SectorStartEntry;
209 	sects.parse_field  = SectorParseField;
210 	sects.finish_entry = SectorFinishEntry;
211 	sects.clear_all    = SectorClearAll;
212 
213 	return DDF_MainReadFile(&sects);
214 }
215 
216 //
217 // DDF_SectorInit
218 //
DDF_SectorInit(void)219 void DDF_SectorInit(void)
220 {
221 	sectortypes.Reset();
222 
223 	default_sector = new sectortype_c;
224 	default_sector->number = 0;
225 }
226 
227 //
228 // DDF_SectorCleanUp
229 //
DDF_SectorCleanUp(void)230 void DDF_SectorCleanUp(void)
231 {
232 	sectortypes.Trim();
233 }
234 
235 //----------------------------------------------------------------------------
236 
237 static specflags_t sector_specials[] =
238 {
239 	{"WHOLE_REGION", SECSP_WholeRegion, 0},
240 	{"PROPORTIONAL", SECSP_Proportional, 0},
241 	{"PUSH_ALL", SECSP_PushAll, 0},
242 	{"PUSH_CONSTANT", SECSP_PushConstant, 0},
243 	{"AIRLESS", SECSP_AirLess, 0},
244 	{"SWIM", SECSP_Swimming, 0},
245 	{NULL, 0, 0}
246 };
247 
248 //
249 // DDF_SectGetSpecialFlags
250 //
251 // Gets the sector specials.
252 //
DDF_SectGetSpecialFlags(const char * info,void * storage)253 void DDF_SectGetSpecialFlags(const char *info, void *storage)
254 {
255 	sector_flag_e *special = (sector_flag_e *)storage;
256 
257 	int flag_value;
258 
259 	switch (DDF_MainCheckSpecialFlag(info, sector_specials, &flag_value, true, false))
260 	{
261 		case CHKF_Positive:
262 			*special =
263 				(sector_flag_e)(*special | flag_value);
264 
265 			break;
266 
267 		case CHKF_Negative:
268 			*special =
269 				(sector_flag_e)(*special & ~flag_value);
270 
271 			break;
272 
273 		case CHKF_User:
274 		case CHKF_Unknown:
275 			DDF_WarnError("Unknown sector special: %s", info);
276 			break;
277 	}
278 }
279 
280 static specflags_t exit_types[] =
281 {
282 	{"NONE", EXIT_None, 0},
283 	{"NORMAL", EXIT_Normal, 0},
284 	{"SECRET", EXIT_Secret, 0},
285 
286 	// -AJA- backwards compatibility cruft...
287 	{"!EXIT", EXIT_Normal, 0},
288 	{NULL, 0, 0}
289 };
290 
291 //
292 // DDF_SectGetExit
293 //
294 // Get the exit type
295 //
DDF_SectGetExit(const char * info,void * storage)296 void DDF_SectGetExit(const char *info, void *storage)
297 {
298 	int *dest = (int *)storage;
299 	int flag_value;
300 
301 	switch (DDF_MainCheckSpecialFlag(info, exit_types, &flag_value,
302 		false, false))
303 	{
304 		case CHKF_Positive:
305 		case CHKF_Negative:
306 			(*dest) = flag_value;
307 			break;
308 
309 		case CHKF_User:
310 		case CHKF_Unknown:
311 			DDF_WarnError("Unknown Exit type: %s\n", info);
312 			break;
313 	}
314 }
315 
316 static specflags_t light_types[] =
317 {
318 	{"NONE", LITE_None, 0},
319 	{"SET",  LITE_Set,  0},
320 	{"FADE", LITE_Fade, 0},
321 	{"STROBE", LITE_Strobe, 0},
322 	{"FLASH",  LITE_Flash,  0},
323 	{"GLOW",   LITE_Glow,   0},
324 	{"FLICKER", LITE_FireFlicker, 0},
325 	{NULL, 0, 0}
326 };
327 
328 //
329 // DDF_SectGetLighttype
330 //
331 // Get the light type
332 //
DDF_SectGetLighttype(const char * info,void * storage)333 void DDF_SectGetLighttype(const char *info, void *storage)
334 {
335 	int *dest = (int *)storage;
336 	int flag_value;
337 
338 	switch (DDF_MainCheckSpecialFlag(info, light_types, &flag_value,
339 		false, false))
340 	{
341 		case CHKF_Positive:
342 		case CHKF_Negative:
343 			(*dest) = flag_value;
344 			break;
345 
346 		case CHKF_User:
347 		case CHKF_Unknown:
348 			DDF_WarnError("Unknown light type: %s\n", info);
349 			break;
350 	}
351 }
352 
353 static specflags_t movement_types[] =
354 {
355 	{"MOVE", mov_Once, 0},
356 	{"MOVEWAITRETURN", mov_MoveWaitReturn, 0},
357 	{"CONTINUOUS", mov_Continuous, 0},
358 	{"PLAT", mov_Plat, 0},
359 	{"BUILDSTAIRS", mov_Stairs, 0},
360 	{"STOP", mov_Stop, 0},
361 	{"TOGGLE", mov_Toggle, 0},
362 	{"ELEVATOR", mov_Elevator, 0},
363 	{NULL, 0, 0}
364 };
365 
366 //
367 // DDF_SectGetMType
368 //
369 // Get movement types: MoveWaitReturn etc
370 //
DDF_SectGetMType(const char * info,void * storage)371 void DDF_SectGetMType(const char *info, void *storage)
372 {
373 	int *dest = (int *)storage;
374 	int flag_value;
375 
376 	switch (DDF_MainCheckSpecialFlag(info, movement_types, &flag_value,
377 		false, false))
378 	{
379 		case CHKF_Positive:
380 		case CHKF_Negative:
381 			(*dest) = flag_value;
382 			break;
383 
384 		case CHKF_User:
385 		case CHKF_Unknown:
386 			DDF_WarnError("Unknown Movement type: %s\n", info);
387 			break;
388 	}
389 }
390 
391 static specflags_t reference_types[] =
392 {
393 	{"ABSOLUTE", REF_Absolute, false},
394 
395 	{"FLOOR", REF_Current, false},
396 	{"CEILING", REF_Current + REF_CEILING, false},
397 
398 	{"TRIGGERFLOOR", REF_Trigger, false},
399 	{"TRIGGERCEILING", REF_Trigger + REF_CEILING, false},
400 
401 	// Note that LOSURROUNDINGFLOOR has the REF_INCLUDE flag, but the
402 	// others do not.  It's there to maintain backwards compatibility.
403 	//
404 	{"LOSURROUNDINGCEILING", REF_Surrounding + REF_CEILING, false},
405 	{"HISURROUNDINGCEILING", REF_Surrounding + REF_CEILING + REF_HIGHEST, false},
406 	{"LOSURROUNDINGFLOOR", REF_Surrounding + REF_INCLUDE, false},
407 	{"HISURROUNDINGFLOOR", REF_Surrounding + REF_HIGHEST, false},
408 
409 	// Note that REF_HIGHEST is used for the NextLowest types, and
410 	// vice versa, which may seem strange.  It's because the next
411 	// lowest sector is actually the highest of all adjacent sectors
412 	// that are lower than the current sector.
413 	//
414 	{"NEXTLOWESTFLOOR", REF_Surrounding + REF_NEXT + REF_HIGHEST, false},
415 	{"NEXTHIGHESTFLOOR", REF_Surrounding + REF_NEXT, false},
416 	{"NEXTLOWESTCEILING", REF_Surrounding + REF_NEXT + REF_CEILING + REF_HIGHEST, false},
417 	{"NEXTHIGHESTCEILING", REF_Surrounding + REF_NEXT + REF_CEILING, false},
418 
419 	{"LOWESTBOTTOMTEXTURE", REF_LowestLoTexture, false}
420 };
421 
422 //
423 // DDF_SectGetDestRef
424 //
425 // Get surroundingsectorceiling/floorheight etc
426 //
DDF_SectGetDestRef(const char * info,void * storage)427 void DDF_SectGetDestRef(const char *info, void *storage)
428 {
429 	int *dest = (int *)storage;
430 	int flag_value;
431 
432 	// check for modifier flags
433 	if (DDF_CompareName(info, "INCLUDE") == 0)
434 	{
435 		*dest |= REF_INCLUDE;
436 		return;
437 	}
438 	else if (DDF_CompareName(info, "EXCLUDE") == 0)
439 	{
440 		*dest &= ~REF_INCLUDE;
441 		return;
442 	}
443 
444 	switch (DDF_MainCheckSpecialFlag(info, reference_types, &flag_value,
445 		false, false))
446 	{
447 		case CHKF_Positive:
448 		case CHKF_Negative:
449 			(*dest) = flag_value;
450 			break;
451 
452 		case CHKF_User:
453 		case CHKF_Unknown:
454 			DDF_WarnError("Unknown Reference Point: %s\n", info);
455 			break;
456 	}
457 }
458 
DDF_SectMakeCrush(const char * info)459 static void DDF_SectMakeCrush(const char *info)
460 {
461 	dynamic_sector->f.crush_damage = 10;
462 	dynamic_sector->c.crush_damage = 10;
463 }
464 
465 
466 //----------------------------------------------------------------------------
467 
468 
469 // --> Sector type definition class
470 
471 //
472 // sectortype_c Constructor
473 //
sectortype_c()474 sectortype_c::sectortype_c() : number(0)
475 {
476 	Default();
477 }
478 
479 //
480 // sectortype_c Destructor
481 //
~sectortype_c()482 sectortype_c::~sectortype_c()
483 {
484 }
485 
486 
487 //
488 // sectortype_c::CopyDetail()
489 //
CopyDetail(sectortype_c & src)490 void sectortype_c::CopyDetail(sectortype_c &src)
491 {
492 	secret = src.secret;
493 	hub = src.hub;
494 
495 	gravity = src.gravity;
496 	friction = src.friction;
497 	viscosity = src.viscosity;
498 	drag = src.drag;
499 
500 	f = src.f;
501 	c = src.c;
502 	l = src.l;
503 
504 	damage = src.damage;
505 
506 	special_flags = src.special_flags;
507 	e_exit = src.e_exit;
508 
509 	use_colourmap = src.use_colourmap;
510 
511 	ambient_sfx = src.ambient_sfx;
512 	splash_sfx = src.splash_sfx;
513 
514 	appear = src.appear;
515 
516 	push_speed = src.push_speed;
517 	push_zspeed = src.push_zspeed;
518 	push_angle = src.push_angle;
519 }
520 
521 //
522 // sectortype_c::Default()
523 //
Default()524 void sectortype_c::Default()
525 {
526 	secret = false;
527 	hub = false;
528 
529 	gravity = GRAVITY;
530 	friction = FRICTION;
531 	viscosity = VISCOSITY;
532 	drag = DRAG;
533 
534 	f.Default(movplanedef_c::DEFAULT_FloorSect);
535 	c.Default(movplanedef_c::DEFAULT_CeilingSect);
536 
537 	l.Default();
538 
539 	damage.Default(damage_c::DEFAULT_Sector);
540 
541 	special_flags = SECSP_None;
542 	e_exit = EXIT_None;
543 	use_colourmap = NULL;
544 	ambient_sfx = NULL;
545 	splash_sfx = NULL;
546 
547 	appear = DEFAULT_APPEAR;
548 
549 	push_speed = 0.0f;
550 	push_zspeed = 0.0f;
551 
552 	push_angle = 0;
553 }
554 
555 
556 // --> Sector definition type container class
557 
558 //
559 // sectortype_container_c Constructor
560 //
sectortype_container_c()561 sectortype_container_c::sectortype_container_c() :
562 	epi::array_c(sizeof(sectortype_c*))
563 {
564 	Reset();
565 }
566 
567 //
568 // sectortype_container_c Destructor
569 //
~sectortype_container_c()570 sectortype_container_c::~sectortype_container_c()
571 {
572 	Clear();
573 }
574 
575 //
576 // sectortype_container_c::CleanupObject
577 //
CleanupObject(void * obj)578 void sectortype_container_c::CleanupObject(void *obj)
579 {
580 	sectortype_c *s = *(sectortype_c**)obj;
581 
582 	if (s)
583 		delete s;
584 
585 	return;
586 }
587 
588 //
589 // Looks an linetype by id, returns NULL if line can't be found.
590 //
Lookup(const int id)591 sectortype_c *sectortype_container_c::Lookup(const int id)
592 {
593 	if (id == 0)
594 		return default_sector;
595 
596 	int slot = DDF_SectHashFunc(id);
597 
598 	// check the cache
599 	if (lookup_cache[slot] &&
600 		lookup_cache[slot]->number == id)
601 	{
602 		return lookup_cache[slot];
603 	}
604 
605 	epi::array_iterator_c it;
606 
607 	for (it = GetTailIterator(); it.IsValid(); it--)
608 	{
609 		sectortype_c *s = ITERATOR_TO_TYPE(it, sectortype_c*);
610 
611 		if (s->number == id)
612 		{
613 			// update the cache
614 			lookup_cache[slot] = s;
615 			return s;
616 		}
617 	}
618 
619 	return NULL;
620 }
621 
622 //
623 // sectortype_container_c::Reset()
624 //
625 // Clears down both the data and the cache
626 //
Reset()627 void sectortype_container_c::Reset()
628 {
629 	Clear();
630 	memset(lookup_cache, 0, sizeof(sectortype_c*) * LOOKUP_CACHESIZE);
631 }
632 
633 //--- editor settings ---
634 // vi:ts=4:sw=4:noexpandtab
635