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(§s);
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