1 //----------------------------------------------------------------------------
2 //  EDGE Data Definition File Code (Things - MOBJs)
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 // Moving Object Setup and Parser Code
20 //
21 // -ACB- 1998/08/04 Written.
22 // -ACB- 1998/09/12 Use DDF_MainGetFixed for fixed number references.
23 // -ACB- 1998/09/13 Use DDF_MainGetTime for Time count references.
24 // -KM- 1998/11/25 Translucency is now a fixed_t. Fixed spelling of available.
25 // -KM- 1998/12/16 No limit on number of ammo types.
26 //
27 
28 #include "local.h"
29 
30 #include "thing.h"
31 
32 #include "src/p_action.h"
33 
34 
35 #undef  DF
36 #define DF  DDF_FIELD
37 
38 #define DDF_MobjHashFunc(x)  (((x) + LOOKUP_CACHESIZE) % LOOKUP_CACHESIZE)
39 
40 mobjtype_container_c mobjtypes;
41 
42 static mobjtype_c * default_mobjtype;
43 
44 void DDF_MobjGetSpecial(const char *info);
45 void DDF_MobjGetBenefit(const char *info, void *storage);
46 void DDF_MobjGetPickupEffect(const char *info, void *storage);
47 void DDF_MobjGetDLight(const char *info, void *storage);
48 
49 static void DDF_MobjGetGlowType(const char *info, void *storage);
50 static void DDF_MobjGetYAlign(const char *info, void *storage);
51 static void DDF_MobjGetPercentRange(const char *info, void *storage);
52 static void DDF_MobjGetAngleRange(const char *info, void *storage);
53 
54 static void AddPickupEffect(pickup_effect_c **list, pickup_effect_c *cur);
55 
56 static int dlight_radius_warnings = 0;
57 
58 
59 #undef  DDF_CMD_BASE
60 #define DDF_CMD_BASE  dummy_dlight
61 static dlight_info_c dummy_dlight;
62 
63 const commandlist_t dlight_commands[] =
64 {
65 	DF("TYPE",   type,   DDF_MobjGetDLight),
66 	DF("GRAPHIC",shape,  DDF_MainGetString),
67 	DF("RADIUS", radius, DDF_MainGetFloat),
68 	DF("COLOUR", colour, DDF_MainGetRGB),
69 	DF("HEIGHT", height, DDF_MainGetPercent),
70 	DF("LEAKY",  leaky,  DDF_MainGetBoolean),
71 
72 	// backwards compatibility
73 	DF("INTENSITY", radius, DDF_MainGetFloat),
74 
75 	DDF_CMD_END
76 };
77 
78 
79 #undef  DDF_CMD_BASE
80 #define DDF_CMD_BASE  dummy_weakness
81 static weakness_info_c dummy_weakness;
82 
83 const commandlist_t weakness_commands[] =
84 {
85 	DF("CLASS",   classes, DDF_MainGetBitSet),
86 	DF("HEIGHTS", height,  DDF_MobjGetPercentRange),
87 	DF("ANGLES",  angle,   DDF_MobjGetAngleRange),
88 	DF("MULTIPLY", multiply, DDF_MainGetFloat),
89 	DF("PAINCHANCE", painchance, DDF_MainGetPercent),
90 
91 	DDF_CMD_END
92 };
93 
94 
95 mobjtype_c *dynamic_mobj;
96 
97 #undef  DDF_CMD_BASE
98 #define DDF_CMD_BASE  dummy_mobj
99 static mobjtype_c dummy_mobj;
100 
101 const commandlist_t thing_commands[] =
102 {
103 	// sub-commands
104 	DDF_SUB_LIST("DLIGHT",  dlight[0], dlight_commands),
105 	DDF_SUB_LIST("DLIGHT2", dlight[1], dlight_commands),
106 	DDF_SUB_LIST("WEAKNESS", weak, weakness_commands),
107 	DDF_SUB_LIST("EXPLODE_DAMAGE", explode_damage, damage_commands),
108 	DDF_SUB_LIST("CHOKE_DAMAGE",     choke_damage, damage_commands),
109 
110 	DF("SPAWNHEALTH", spawnhealth, DDF_MainGetFloat),
111 	DF("RADIUS", radius, DDF_MainGetFloat),
112 	DF("HEIGHT", height, DDF_MainGetFloat),
113 	DF("MASS", mass, DDF_MainGetFloat),
114 	DF("SPEED", speed, DDF_MainGetFloat),
115 	DF("FAST", fast, DDF_MainGetFloat),
116 	DF("EXTRA", extendedflags, DDF_MobjGetExtra),
117 	DF("RESPAWN_TIME", respawntime, DDF_MainGetTime),
118 	DF("FUSE", fuse, DDF_MainGetTime),
119 	DF("LIFESPAN", fuse, DDF_MainGetTime),
120 	DF("PALETTE_REMAP", palremap, DDF_MainGetColourmap),
121 	DF("TRANSLUCENCY", translucency, DDF_MainGetPercent),
122 
123 	DF("INITIAL_BENEFIT", initial_benefits, DDF_MobjGetBenefit),
124 	DF("LOSE_BENEFIT", lose_benefits, DDF_MobjGetBenefit),
125 	DF("PICKUP_BENEFIT", pickup_benefits, DDF_MobjGetBenefit),
126 	DF("PICKUP_MESSAGE", pickup_message, DDF_MainGetString),
127 	DF("PICKUP_EFFECT", pickup_effects, DDF_MobjGetPickupEffect),
128 
129 	DF("PAINCHANCE", painchance, DDF_MainGetPercent),
130 	DF("MINATTACK_CHANCE", minatkchance, DDF_MainGetPercent),
131 	DF("REACTION_TIME", reactiontime, DDF_MainGetTime),
132 	DF("JUMP_DELAY", jump_delay, DDF_MainGetTime),
133 	DF("JUMP_HEIGHT", jumpheight, DDF_MainGetFloat),
134 	DF("CROUCH_HEIGHT", crouchheight, DDF_MainGetFloat),
135 	DF("VIEW_HEIGHT", viewheight, DDF_MainGetPercent),
136 	DF("SHOT_HEIGHT", shotheight, DDF_MainGetPercent),
137 	DF("MAX_FALL", maxfall, DDF_MainGetFloat),
138 	DF("CASTORDER", castorder, DDF_MainGetNumeric),
139 	DF("CAST_TITLE", cast_title, DDF_MainGetString),
140 	DF("PLAYER", playernum, DDF_MobjGetPlayer),
141 	DF("SIDE", side, DDF_MainGetBitSet),
142 	DF("CLOSE_ATTACK", closecombat, DDF_MainRefAttack),
143 	DF("RANGE_ATTACK", rangeattack, DDF_MainRefAttack),
144 	DF("SPARE_ATTACK", spareattack, DDF_MainRefAttack),
145 	DF("DROPITEM", dropitem_ref, DDF_MainGetString),
146 	DF("BLOOD", blood_ref, DDF_MainGetString),
147 	DF("RESPAWN_EFFECT", respawneffect_ref, DDF_MainGetString),
148 	DF("SPIT_SPOT", spitspot_ref, DDF_MainGetString),
149 
150 	DF("PICKUP_SOUND", activesound, DDF_MainLookupSound),
151 	DF("ACTIVE_SOUND", activesound, DDF_MainLookupSound),
152 	DF("LAUNCH_SOUND", seesound, DDF_MainLookupSound),
153 	DF("AMBIENT_SOUND", seesound, DDF_MainLookupSound),
154 	DF("SIGHTING_SOUND", seesound, DDF_MainLookupSound),
155 	DF("DEATH_SOUND", deathsound, DDF_MainLookupSound),
156 	DF("OVERKILL_SOUND", overkill_sound, DDF_MainLookupSound),
157 	DF("PAIN_SOUND", painsound, DDF_MainLookupSound),
158 	DF("STARTCOMBAT_SOUND", attacksound, DDF_MainLookupSound),
159 	DF("WALK_SOUND", walksound, DDF_MainLookupSound),
160 	DF("JUMP_SOUND", jump_sound, DDF_MainLookupSound),
161 	DF("NOWAY_SOUND", noway_sound, DDF_MainLookupSound),
162 	DF("OOF_SOUND", oof_sound, DDF_MainLookupSound),
163 	DF("GASP_SOUND", gasp_sound, DDF_MainLookupSound),
164 
165 	DF("FLOAT_SPEED", float_speed, DDF_MainGetFloat),
166 	DF("STEP_SIZE", step_size, DDF_MainGetFloat),
167 	DF("SPRITE_SCALE", scale, DDF_MainGetFloat),
168 	DF("SPRITE_ASPECT", aspect, DDF_MainGetFloat),
169 	DF("SPRITE_YALIGN", yalign, DDF_MobjGetYAlign),   // -AJA- 2007/08/08
170 	DF("MODEL_SKIN", model_skin, DDF_MainGetNumeric), // -AJA- 2007/10/16
171 	DF("MODEL_SCALE", model_scale, DDF_MainGetFloat),
172 	DF("MODEL_ASPECT", model_aspect, DDF_MainGetFloat),
173 	DF("MODEL_BIAS", model_bias, DDF_MainGetFloat),
174 	DF("BOUNCE_SPEED", bounce_speed, DDF_MainGetFloat),
175 	DF("BOUNCE_UP", bounce_up, DDF_MainGetFloat),
176 	DF("SIGHT_SLOPE", sight_slope, DDF_MainGetSlope),
177 	DF("SIGHT_ANGLE", sight_angle, DDF_MainGetAngle),
178 	DF("RIDE_FRICTION", ride_friction, DDF_MainGetFloat),
179 	DF("BOBBING", bobbing, DDF_MainGetPercent),
180 	DF("IMMUNITY_CLASS", immunity, DDF_MainGetBitSet),
181 	DF("RESISTANCE_CLASS", resistance, DDF_MainGetBitSet),
182 	DF("RESISTANCE_MULTIPLY", resist_multiply, DDF_MainGetFloat),
183 	DF("RESISTANCE_PAINCHANCE", resist_painchance, DDF_MainGetPercent),
184 	DF("GHOST_CLASS", ghost, DDF_MainGetBitSet),   // -AJA- 2005/05/15
185 	DF("SHADOW_TRANSLUCENCY", shadow_trans, DDF_MainGetPercent),
186 	DF("LUNG_CAPACITY", lung_capacity, DDF_MainGetTime),
187 	DF("GASP_START", gasp_start, DDF_MainGetTime),
188 	DF("EXPLODE_RADIUS", explode_radius, DDF_MainGetFloat),
189 	DF("RELOAD_SHOTS", reload_shots, DDF_MainGetNumeric),  // -AJA- 2004/11/15
190 	DF("GLOW_TYPE", glow_type, DDF_MobjGetGlowType), // -AJA- 2007/08/19
191 	DF("ARMOUR_PROTECTION", armour_protect, DDF_MainGetPercent),  // -AJA- 2007/08/22
192 	DF("ARMOUR_DEPLETION",  armour_deplete, DDF_MainGetPercentAny),  // -AJA- 2007/08/22
193 	DF("ARMOUR_CLASS",  armour_class, DDF_MainGetBitSet),  // -AJA- 2007/08/22
194 
195 	// -AJA- backwards compatibility cruft...
196 	DF("EXPLOD_DAMAGE", explode_damage.nominal, DDF_MainGetFloat),
197 	DF("EXPLOSION_DAMAGE", explode_damage.nominal, DDF_MainGetFloat),
198 	DF("EXPLOD_DAMAGERANGE", explode_damage.nominal, DDF_MainGetFloat),
199 
200 	DDF_CMD_END
201 };
202 
203 const state_starter_t thing_starters[] =
204 {
205 	DDF_STATE("SPAWN",     "IDLE",    spawn_state),
206 	DDF_STATE("IDLE",      "IDLE",    idle_state),
207 	DDF_STATE("CHASE",     "CHASE",   chase_state),
208 	DDF_STATE("PAIN",      "IDLE",    pain_state),
209 	DDF_STATE("MISSILE",   "IDLE",    missile_state),
210 	DDF_STATE("MELEE",     "IDLE",    melee_state),
211 	DDF_STATE("DEATH",     "REMOVE",  death_state),
212 	DDF_STATE("OVERKILL",  "REMOVE",  overkill_state),
213 	DDF_STATE("RESPAWN",   "IDLE",    raise_state),
214 	DDF_STATE("RESURRECT", "IDLE",    res_state),
215 	DDF_STATE("MEANDER",   "MEANDER", meander_state),
216 	DDF_STATE("BOUNCE",    "IDLE",    bounce_state),
217 	DDF_STATE("TOUCH",     "IDLE",    touch_state),
218 	DDF_STATE("RELOAD",    "IDLE",    reload_state),
219 	DDF_STATE("GIB",       "REMOVE",  gib_state),
220 
221 	DDF_STATE_END
222 };
223 
224 // -KM- 1998/11/25 Added weapon functions.
225 // -AJA- 1999/08/09: Moved this here from p_action.h, and added an extra
226 // field `handle_arg' for things like "WEAPON_SHOOT(FIREBALL)".
227 
228 const actioncode_t thing_actions[] =
229 {
230 	{"NOTHING", NULL, NULL},
231 
232 	{"CLOSEATTEMPTSND",   P_ActMakeCloseAttemptSound, NULL},
233 	{"COMBOATTACK",       P_ActComboAttack, NULL},
234 	{"FACETARGET",        P_ActFaceTarget, NULL},
235 	{"PLAYSOUND",         P_ActPlaySound, DDF_StateGetSound},
236 	{"KILLSOUND",         P_ActKillSound, NULL},
237 	{"MAKESOUND",         P_ActMakeAmbientSound, NULL},
238 	{"MAKEACTIVESOUND",   P_ActMakeActiveSound, NULL},
239 	{"MAKESOUNDRANDOM",   P_ActMakeAmbientSoundRandom, NULL},
240 	{"MAKEDEATHSOUND",    P_ActMakeDyingSound, NULL},
241 	{"MAKEDEAD",          P_ActMakeIntoCorpse, NULL},
242 	{"MAKEOVERKILLSOUND", P_ActMakeOverKillSound, NULL},
243 	{"MAKEPAINSOUND",     P_ActMakePainSound, NULL},
244 	{"PLAYER_SCREAM",     P_ActPlayerScream, NULL},
245 	{"CLOSE_ATTACK",      P_ActMeleeAttack, DDF_StateGetAttack},
246 	{"RANGE_ATTACK",      P_ActRangeAttack, DDF_StateGetAttack},
247 	{"SPARE_ATTACK",      P_ActSpareAttack, DDF_StateGetAttack},
248 
249 	{"RANGEATTEMPTSND",   P_ActMakeRangeAttemptSound, NULL},
250 	{"REFIRE_CHECK",      P_ActRefireCheck, NULL},
251 	{"RELOAD_CHECK",      P_ActReloadCheck, NULL},
252 	{"RELOAD_RESET",      P_ActReloadReset, NULL},
253 	{"LOOKOUT",           P_ActStandardLook, NULL},
254 	{"SUPPORT_LOOKOUT",   P_ActPlayerSupportLook, NULL},
255 	{"CHASE",             P_ActStandardChase, NULL},
256 	{"RESCHASE",          P_ActResurrectChase, NULL},
257 	{"WALKSOUND_CHASE",   P_ActWalkSoundChase, NULL},
258 	{"MEANDER",           P_ActStandardMeander, NULL},
259 	{"SUPPORT_MEANDER",   P_ActPlayerSupportMeander, NULL},
260 	{"EXPLOSIONDAMAGE",   P_ActDamageExplosion, NULL},
261 	{"THRUST",            P_ActThrust, NULL},
262 	{"TRACER",            P_ActHomingProjectile, NULL},
263 	{"RANDOM_TRACER",     P_ActHomingProjectile, NULL},  // same as above
264 	{"RESET_SPREADER",    P_ActResetSpreadCount, NULL},
265 	{"SMOKING",           P_ActCreateSmokeTrail, NULL},
266 	{"TRACKERACTIVE",     P_ActTrackerActive, NULL},
267 	{"TRACKERFOLLOW",     P_ActTrackerFollow, NULL},
268 	{"TRACKERSTART",      P_ActTrackerStart, NULL},
269 	{"EFFECTTRACKER",     P_ActEffectTracker, NULL},
270 	{"CHECKBLOOD",        P_ActCheckBlood, NULL},
271 	{"CHECKMOVING",       P_ActCheckMoving, NULL},
272 	{"CHECK_ACTIVITY",    P_ActCheckActivity, NULL},
273 	{"JUMP",              P_ActJump, DDF_StateGetJump},
274 	{"BECOME",            P_ActBecome, DDF_StateGetBecome},
275 	{"EXPLODE",           P_ActExplode, NULL},
276 	{"ACTIVATE_LINETYPE", P_ActActivateLineType, DDF_StateGetIntPair},
277 	{"RTS_ENABLE_TAGGED", P_ActEnableRadTrig,  DDF_StateGetInteger},
278 	{"RTS_DISABLE_TAGGED",P_ActDisableRadTrig, DDF_StateGetInteger},
279 	{"TOUCHY_REARM",      P_ActTouchyRearm, NULL},
280 	{"TOUCHY_DISARM",     P_ActTouchyDisarm, NULL},
281 	{"BOUNCE_REARM",      P_ActBounceRearm, NULL},
282 	{"BOUNCE_DISARM",     P_ActBounceDisarm, NULL},
283 	{"PATH_CHECK",        P_ActPathCheck, NULL},
284 	{"PATH_FOLLOW",       P_ActPathFollow, NULL},
285 	{"SET_INVULNERABLE",  P_ActSetInvuln,   NULL},
286 	{"CLEAR_INVULNERABLE",P_ActClearInvuln, NULL},
287 
288 	{"DROPITEM",          P_ActDropItem, DDF_StateGetMobj},
289 	{"SPAWN",             P_ActSpawn,    DDF_StateGetMobj},
290 	{"TRANS_SET",         P_ActTransSet, DDF_StateGetPercent},
291 	{"TRANS_FADE",        P_ActTransFade, DDF_StateGetPercent},
292 	{"TRANS_MORE",        P_ActTransMore, DDF_StateGetPercent},
293 	{"TRANS_LESS",        P_ActTransLess, DDF_StateGetPercent},
294 	{"TRANS_ALTERNATE",   P_ActTransAlternate, DDF_StateGetPercent},
295 	{"DLIGHT_SET",        P_ActDLightSet,  DDF_StateGetInteger},
296 	{"DLIGHT_FADE",       P_ActDLightFade, DDF_StateGetInteger},
297 	{"DLIGHT_RANDOM",     P_ActDLightRandom, DDF_StateGetIntPair},
298 	{"DLIGHT_COLOUR",     P_ActDLightColour, DDF_StateGetRGB},
299 	{"SET_SKIN",          P_ActSetSkin,   DDF_StateGetInteger},
300 
301     {"FACE",              P_ActFaceDir, DDF_StateGetAngle},
302     {"TURN",              P_ActTurnDir, DDF_StateGetAngle},
303     {"TURN_RANDOM",       P_ActTurnRandom, DDF_StateGetAngle},
304 	{"MLOOK_FACE",        P_ActMlookFace, DDF_StateGetSlope},
305 	{"MLOOK_TURN",        P_ActMlookTurn, DDF_StateGetSlope},
306 	{"MOVE_FWD",          P_ActMoveFwd, DDF_StateGetFloat},
307 	{"MOVE_RIGHT",        P_ActMoveRight, DDF_StateGetFloat},
308 	{"MOVE_UP",           P_ActMoveUp, DDF_StateGetFloat},
309 	{"STOP",              P_ActStopMoving, NULL},
310 
311 	// Boom/MBF compatibility
312 	{"DIE",               P_ActDie, NULL},
313 	{"KEEN_DIE",          P_ActKeenDie, NULL},
314 
315 	// bossbrain actions
316 	{"BRAINSPIT", P_ActBrainSpit, NULL},
317 	{"CUBESPAWN", P_ActCubeSpawn, NULL},
318 	{"CUBETRACER", P_ActHomeToSpot, NULL},
319 	{"BRAINSCREAM", P_ActBrainScream, NULL},
320 	{"BRAINMISSILEEXPLODE", P_ActBrainMissileExplode, NULL},
321 	{"BRAINDIE", P_ActBrainDie, NULL},
322 
323 	// -AJA- backwards compatibility cruft...
324 	{"VARIEDEXPDAMAGE",   P_ActDamageExplosion, NULL},
325 	{"VARIED_THRUST",     P_ActThrust, NULL},
326 
327 	{NULL, NULL, NULL}
328 };
329 
330 
331 const specflags_t keytype_names[] =
332 {
333 	{"BLUECARD",    KF_BlueCard,    0},
334 	{"YELLOWCARD",  KF_YellowCard,  0},
335 	{"REDCARD",     KF_RedCard,     0},
336 	{"GREENCARD",   KF_GreenCard,   0},
337 
338 	{"BLUESKULL",   KF_BlueSkull,   0},
339 	{"YELLOWSKULL", KF_YellowSkull, 0},
340 	{"REDSKULL",    KF_RedSkull,    0},
341 	{"GREENSKULL",  KF_GreenSkull,  0},
342 
343 	{"GOLD_KEY",    KF_GoldKey,     0},
344 	{"SILVER_KEY",  KF_SilverKey,   0},
345 	{"BRASS_KEY",   KF_BrassKey,    0},
346 	{"COPPER_KEY",  KF_CopperKey,   0},
347 	{"STEEL_KEY",   KF_SteelKey,    0},
348 	{"WOODEN_KEY",  KF_WoodenKey,   0},
349 	{"FIRE_KEY",    KF_FireKey,     0},
350 	{"WATER_KEY",   KF_WaterKey,    0},
351 
352 	// -AJA- compatibility (this way is the easiest)
353 	{"KEY_BLUECARD",    KF_BlueCard,    0},
354 	{"KEY_YELLOWCARD",  KF_YellowCard,  0},
355 	{"KEY_REDCARD",     KF_RedCard,     0},
356 	{"KEY_GREENCARD",   KF_GreenCard,   0},
357 
358 	{"KEY_BLUESKULL",   KF_BlueSkull,   0},
359 	{"KEY_YELLOWSKULL", KF_YellowSkull, 0},
360 	{"KEY_REDSKULL",    KF_RedSkull,    0},
361 	{"KEY_GREENSKULL",  KF_GreenSkull,  0},
362 
363 	{NULL, 0, 0}
364 };
365 
366 
367 const specflags_t armourtype_names[] =
368 {
369 	{"GREEN_ARMOUR",  ARMOUR_Green,  0},
370 	{"BLUE_ARMOUR",   ARMOUR_Blue,   0},
371 	{"PURPLE_ARMOUR", ARMOUR_Purple, 0},
372 	{"YELLOW_ARMOUR", ARMOUR_Yellow, 0},
373 	{"RED_ARMOUR",    ARMOUR_Red,    0},
374 	{NULL, 0, 0}
375 };
376 
377 
378 const specflags_t powertype_names[] =
379 {
380 	{"POWERUP_INVULNERABLE", PW_Invulnerable, 0},
381 	{"POWERUP_BARE_BERSERK", PW_Berserk,      0},
382 	{"POWERUP_BERSERK",      PW_Berserk,      0},
383 	{"POWERUP_PARTINVIS",    PW_PartInvis,    0},
384 	{"POWERUP_ACIDSUIT",     PW_AcidSuit,     0},
385 	{"POWERUP_AUTOMAP",      PW_AllMap,       0},
386 	{"POWERUP_LIGHTGOGGLES", PW_Infrared,     0},
387 	{"POWERUP_JETPACK",      PW_Jetpack,      0},
388 	{"POWERUP_NIGHTVISION",  PW_NightVision,  0},
389 	{"POWERUP_SCUBA",        PW_Scuba,        0},
390 	{NULL, 0, 0}
391 };
392 
393 
394 const specflags_t simplecond_names[] =
395 {
396 	{"JUMPING",     COND_Jumping,    0},
397 	{"CROUCHING",   COND_Crouching,  0},
398 	{"SWIMMING",    COND_Swimming,   0},
399 	{"ATTACKING",   COND_Attacking,  0},
400 	{"RAMPAGING",   COND_Rampaging,  0},
401 	{"USING",       COND_Using,      0},
402 	{"ACTION1",     COND_Action1,    0},
403 	{"ACTION2",     COND_Action2,    0},
404 	{"WALKING",     COND_Walking,    0},
405 	{NULL, 0, 0}
406 };
407 
408 //
409 // DDF_CompareName
410 //
411 // Compare two names. This is like stricmp(), except that spaces
412 // and underscors are ignored for comparison purposes.
413 //
414 // -AJA- 1999/09/11: written.
415 //
DDF_CompareName(const char * A,const char * B)416 int DDF_CompareName(const char *A, const char *B)
417 {
418 	for (;;)
419 	{
420 		// Note: must skip stuff BEFORE checking for NUL
421 		while (*A == ' ' || *A == '_') A++;
422 		while (*B == ' ' || *B == '_') B++;
423 
424 		if (*A == 0 && *B == 0) return 0;
425 
426 		if (*A == 0) return -1;
427 		if (*B == 0) return +1;
428 
429 		if (toupper(*A) == toupper(*B))
430 		{
431 			A++; B++; continue;
432 		}
433 
434 		return toupper(*A) - toupper(*B);
435 	}
436 }
437 
438 
439 //
440 //  DDF PARSE ROUTINES
441 //
442 
ThingStartEntry(const char * buffer,bool extend)443 static void ThingStartEntry(const char *buffer, bool extend)
444 {
445 	if (!buffer || !buffer[0])
446 	{
447 		DDF_WarnError("New thing entry is missing a name!");
448 		buffer = "THING_WITH_NO_NAME";
449 	}
450 
451 	std::string name(buffer);
452 	int number = 0;
453 
454 	const char *pos = strchr(buffer, ':');
455 
456 	if (pos)
457 	{
458 		name = std::string(buffer, pos - buffer);
459 
460 		number = MAX(0, atoi(pos+1));
461 
462 		if (name.empty())
463 		{
464 			DDF_WarnError("New thing entry is missing a name!");
465 			name = "THING_WITH_NO_NAME";
466 		}
467 	}
468 
469 	dynamic_mobj = NULL;
470 
471 	int idx = mobjtypes.FindFirst(name.c_str(), 0);
472 
473 	if (idx >= 0)
474 	{
475 		mobjtypes.MoveToEnd(idx);
476 		dynamic_mobj = mobjtypes[mobjtypes.GetSize()-1];
477 	}
478 
479 	if (extend)
480 	{
481 		if (! dynamic_mobj)
482 			DDF_Error("Unknown thing to extend: %s\n", name.c_str());
483 
484 		if (number > 0)
485 			dynamic_mobj->number = number;
486 
487 		DDF_StateBeginRange(dynamic_mobj->state_grp);
488 		return;
489 	}
490 
491 	// replaces an existing entry?
492 	if (dynamic_mobj)
493 	{
494 		dynamic_mobj->Default();
495 		dynamic_mobj->number = number;
496 	}
497 	else
498 	{
499 		// not found, create a new one
500 		dynamic_mobj = new mobjtype_c;
501 		dynamic_mobj->name = name.c_str();
502 		dynamic_mobj->number = number;
503 
504 		mobjtypes.Insert(dynamic_mobj);
505 	}
506 
507 	DDF_StateBeginRange(dynamic_mobj->state_grp);
508 }
509 
510 
ThingDoTemplate(const char * contents)511 static void ThingDoTemplate(const char *contents)
512 {
513 	int idx = mobjtypes.FindFirst(contents, 0);
514 	if (idx < 0)
515 		DDF_Error("Unknown thing template: '%s'\n", contents);
516 
517 	mobjtype_c *other = mobjtypes[idx];
518 	SYS_ASSERT(other);
519 
520 	if (other == dynamic_mobj)
521 		DDF_Error("Bad thing template: '%s'\n", contents);
522 
523 	dynamic_mobj->CopyDetail(*other);
524 
525 	DDF_StateBeginRange(dynamic_mobj->state_grp);
526 }
527 
528 
ThingParseField(const char * field,const char * contents,int index,bool is_last)529 void ThingParseField(const char *field, const char *contents,
530 					 int index, bool is_last)
531 {
532 #if (DEBUG_DDF)
533 	I_Debugf("THING_PARSE: %s = %s;\n", field, contents);
534 #endif
535 
536 	if (DDF_CompareName(field, "TEMPLATE") == 0)
537 	{
538 		ThingDoTemplate(contents);
539 		return;
540 	}
541 
542 	// -AJA- this needs special handling (it touches several fields)
543 	if (DDF_CompareName(field, "SPECIAL") == 0 ||
544 	    DDF_CompareName(field, "PROJECTILE_SPECIAL") == 0)
545 	{
546 		DDF_MobjGetSpecial(contents);
547 		return;
548 	}
549 
550 	if (DDF_MainParseField(thing_commands, field, contents, (byte *)dynamic_mobj))
551 		return;
552 
553 	if (DDF_MainParseState((byte *)dynamic_mobj, dynamic_mobj->state_grp,
554 	                       field, contents, index, is_last, false /* is_weapon */,
555 						   thing_starters, thing_actions))
556 		return;
557 
558 
559 	DDF_WarnError("Unknown thing/attack command: %s\n", field);
560 }
561 
562 
ThingFinishEntry(void)563 static void ThingFinishEntry(void)
564 {
565 	DDF_StateFinishRange(dynamic_mobj->state_grp);
566 
567 	// count-as-kill things are automatically monsters
568 	if (dynamic_mobj->flags & MF_COUNTKILL)
569 		dynamic_mobj->extendedflags |= EF_MONSTER;
570 
571 	// countable items are always pick-up-able
572 	if (dynamic_mobj->flags & MF_COUNTITEM)
573 		dynamic_mobj->hyperflags |= HF_FORCEPICKUP;
574 
575 	// shootable things are always pushable
576 	if (dynamic_mobj->flags & MF_SHOOTABLE)
577 		dynamic_mobj->hyperflags |= HF_PUSHABLE;
578 
579 	// check stuff...
580 
581 	if (dynamic_mobj->mass < 1)
582 	{
583 		DDF_WarnError("Bad MASS value %f in DDF.\n", dynamic_mobj->mass);
584 		dynamic_mobj->mass = 1;
585 	}
586 
587 	// check CAST stuff
588 	if (dynamic_mobj->castorder > 0)
589 	{
590 		if (! dynamic_mobj->chase_state)
591 			DDF_Error("Cast object must have CHASE states !\n");
592 
593 		if (! dynamic_mobj->death_state)
594 			DDF_Error("Cast object must have DEATH states !\n");
595 	}
596 
597 	// check DAMAGE stuff
598 	if (dynamic_mobj->explode_damage.nominal < 0)
599 	{
600 		DDF_WarnError("Bad EXPLODE_DAMAGE.VAL value %f in DDF.\n",
601 			dynamic_mobj->explode_damage.nominal);
602 	}
603 
604 	if (dynamic_mobj->explode_radius < 0)
605 	{
606 		DDF_Error("Bad EXPLODE_RADIUS value %f in DDF.\n",
607 			dynamic_mobj->explode_radius);
608 	}
609 
610 	if (dynamic_mobj->reload_shots <= 0)
611 	{
612 		DDF_Error("Bad RELOAD_SHOTS value %d in DDF.\n",
613 			dynamic_mobj->reload_shots);
614 	}
615 
616 	if (dynamic_mobj->choke_damage.nominal < 0)
617 	{
618 		DDF_WarnError("Bad CHOKE_DAMAGE.VAL value %f in DDF.\n",
619 			dynamic_mobj->choke_damage.nominal);
620 	}
621 
622 	if (dynamic_mobj->model_skin < 0 || dynamic_mobj->model_skin > 9)
623 		DDF_Error("Bad MODEL_SKIN value %d in DDF (must be 0-9).\n",
624 			dynamic_mobj->model_skin);
625 
626     if (dynamic_mobj->dlight[0].radius > 512)
627 	{
628 		if (dlight_radius_warnings < 3)
629 			DDF_Warning("DLIGHT_RADIUS value %1.1f too large (over 512).\n",
630 				dynamic_mobj->dlight[0].radius);
631 		else if (dlight_radius_warnings == 3)
632 			I_Warning("More too large DLIGHT_RADIUS values found....\n");
633 
634 		dlight_radius_warnings++;
635 	}
636 
637 	// FIXME: check more stuff
638 
639 	// backwards compatibility:
640 	if (!dynamic_mobj->idle_state && dynamic_mobj->spawn_state)
641 		dynamic_mobj->idle_state = dynamic_mobj->spawn_state;
642 
643 	dynamic_mobj->DLightCompatibility();
644 }
645 
646 
ThingClearAll(void)647 static void ThingClearAll(void)
648 {
649 	I_Warning("Ignoring #CLEARALL in things.ddf\n");
650 }
651 
652 
DDF_ReadThings(void * data,int size)653 bool DDF_ReadThings(void *data, int size)
654 {
655 	readinfo_t things;
656 
657 	things.memfile = (char*)data;
658 	things.memsize = size;
659 	things.tag = "THINGS";
660 	things.entries_per_dot = 6;
661 
662 	if (things.memfile)
663 	{
664 		things.message = NULL;
665 		things.filename = NULL;
666 		things.lumpname = "DDFTHING";
667 	}
668 	else
669 	{
670 		things.message = "DDF_InitThings";
671 		things.filename = "things.ddf";
672 		things.lumpname = NULL;
673 	}
674 
675 	things.start_entry  = ThingStartEntry;
676 	things.parse_field  = ThingParseField;
677 	things.finish_entry = ThingFinishEntry;
678 	things.clear_all    = ThingClearAll;
679 
680 	return DDF_MainReadFile(&things);
681 }
682 
DDF_MobjInit(void)683 void DDF_MobjInit(void)
684 {
685 	mobjtypes.Clear();
686 
687 	default_mobjtype = new mobjtype_c();
688 	default_mobjtype->name   = "__DEFAULT_MOBJ";
689 	default_mobjtype->number = 0;
690 }
691 
DDF_MobjCleanUp(void)692 void DDF_MobjCleanUp(void)
693 {
694 	epi::array_iterator_c it;
695 	mobjtype_c *m;
696 
697 	// lookup references
698 	for (it = mobjtypes.GetIterator(0); it.IsValid(); it++)
699 	{
700 		m = ITERATOR_TO_TYPE(it, mobjtype_c*);
701 
702 		cur_ddf_entryname = epi::STR_Format("[%s]  (things.ddf)", m->name.c_str());
703 
704 		m->dropitem = m->dropitem_ref ? mobjtypes.Lookup(m->dropitem_ref) : NULL;
705 		m->blood = m->blood_ref ? mobjtypes.Lookup(m->blood_ref) : mobjtypes.Lookup("BLOOD");
706 
707 		m->respawneffect = m->respawneffect_ref ?
708 			mobjtypes.Lookup(m->respawneffect_ref) :
709 			(m->flags & MF_SPECIAL) ? mobjtypes.Lookup("ITEM_RESPAWN")
710 				                    : mobjtypes.Lookup("RESPAWN_FLASH");
711 
712 		m->spitspot = m->spitspot_ref ? mobjtypes.Lookup(m->spitspot_ref) : NULL;
713 
714 		cur_ddf_entryname.clear();
715 	}
716 
717 	mobjtypes.Trim();
718 }
719 
720 //
721 // ParseBenefitString
722 //
723 // Parses a string like "HEALTH(20:100)".  Returns the number of
724 // number parameters (0, 1 or 2).  If the brackets are missing, an
725 // error occurs.  If the numbers cannot be parsed, then 0 is returned
726 // and the param buffer contains the stuff in brackets (normally the
727 // param string will be empty).   FIXME: this interface is fucked.
728 //
ParseBenefitString(const char * info,char * name,char * param,float * value,float * limit)729 static int ParseBenefitString(const char *info, char *name, char *param,
730 							  float *value, float *limit)
731 {
732 	int len = strlen(info);
733 
734 	const char *pos = strchr(info, '(');
735 
736 	param[0] = 0;
737 
738 	// do we have matched parentheses ?
739 	if (pos && len >= 4 && info[len - 1] == ')')
740 	{
741 		int len2 = (pos - info);
742 
743 		Z_StrNCpy(name, info, len2);
744 
745 		len -= len2 + 2;
746 		Z_StrNCpy(param, pos + 1, len);
747 
748 		switch (sscanf(param, " %f : %f ", value, limit))
749 		{
750 			case 0: return 0;
751 
752 			case 1: param[0] = 0; return 1;
753 			case 2: param[0] = 0; return 2;
754 
755 			default:
756 				DDF_WarnError("Bad value in benefit string: %s\n", info);
757 				return -1;
758 		}
759 	}
760 	else if (pos)
761 	{
762 		DDF_WarnError("Malformed benefit string: %s\n", info);
763 		return -1;
764 	}
765 
766 	strcpy(name, info);
767 	return 0;
768 }
769 
770 //
771 //  BENEFIT TESTERS
772 //
773 //  These return true if the name matches that particular type of
774 //  benefit (e.g. "ROCKET" for ammo), and then adjusts the benefit
775 //  according to how many number values there were.  Otherwise returns
776 //  false.
777 //
778 
BenefitTryAmmo(const char * name,benefit_t * be,int num_vals)779 static bool BenefitTryAmmo(const char *name, benefit_t *be,
780 								int num_vals)
781 {
782 	if (CHKF_Positive != DDF_MainCheckSpecialFlag(name, ammo_types,
783 		&be->sub.type, false, false))
784 	{
785 		return false;
786 	}
787 
788 	be->type = BENEFIT_Ammo;
789 
790 	if ((ammotype_e)be->sub.type == AM_NoAmmo)
791 	{
792 		DDF_WarnError("Illegal ammo benefit: %s\n", name);
793 		return false;
794 	}
795 
796 	if (num_vals < 1)
797 	{
798 		DDF_WarnError("Ammo benefit used, but amount is missing.\n");
799 		return false;
800 	}
801 
802 	if (num_vals < 2)
803 	{
804 		be->limit = be->amount;
805 	}
806 
807 	return true;
808 }
809 
BenefitTryAmmoLimit(const char * name,benefit_t * be,int num_vals)810 static bool BenefitTryAmmoLimit(const char *name, benefit_t *be,
811 									 int num_vals)
812 {
813 	char namebuf[200];
814 	int len = strlen(name);
815 
816 	// check for ".LIMIT" prefix
817 
818 	if (len < 7 || DDF_CompareName(name+len-6, ".LIMIT") != 0)
819 		return false;
820 
821 	len -= 6;
822 	Z_StrNCpy(namebuf, name, len);
823 
824 	if (CHKF_Positive != DDF_MainCheckSpecialFlag(namebuf, ammo_types,
825 		&be->sub.type, false, false))
826 	{
827 		return false;
828 	}
829 
830 	be->type = BENEFIT_AmmoLimit;
831 	be->limit = 0;
832 
833 	if (be->sub.type == AM_NoAmmo)
834 	{
835 		DDF_WarnError("Illegal ammolimit benefit: %s\n", name);
836 		return false;
837 	}
838 
839 	if (num_vals < 1)
840 	{
841 		DDF_WarnError("AmmoLimit benefit used, but amount is missing.\n");
842 		return false;
843 	}
844 
845 	if (num_vals > 1)
846 	{
847 		DDF_WarnError("AmmoLimit benefit cannot have a limit value.\n");
848 		return false;
849 	}
850 
851 	return true;
852 }
853 
BenefitTryWeapon(const char * name,benefit_t * be,int num_vals)854 static bool BenefitTryWeapon(const char *name, benefit_t *be,
855 								  int num_vals)
856 {
857 	int idx = weapondefs.FindFirst(name, 0);
858 
859 	if (idx < 0)
860 		return false;
861 
862 	be->sub.weap = weapondefs[idx];
863 
864 	be->type = BENEFIT_Weapon;
865 	be->limit = 1.0f;
866 
867 	if (num_vals < 1)
868 		be->amount = 1.0f;
869 	else if (be->amount != 0.0f && be->amount != 1.0f)
870 	{
871 		DDF_WarnError("Weapon benefit used, bad amount value: %1.1f\n", be->amount);
872 		return false;
873 	}
874 
875 	if (num_vals > 1)
876 	{
877 		DDF_WarnError("Weapon benefit cannot have a limit value.\n");
878 		return false;
879 	}
880 
881 	return true;
882 }
883 
BenefitTryKey(const char * name,benefit_t * be,int num_vals)884 static bool BenefitTryKey(const char *name, benefit_t *be,
885 							   int num_vals)
886 {
887 	if (CHKF_Positive != DDF_MainCheckSpecialFlag(name, keytype_names,
888 		&be->sub.type, false, false))
889 	{
890 		return false;
891 	}
892 
893 	be->type = BENEFIT_Key;
894 	be->limit = 1.0f;
895 
896 	if (num_vals < 1)
897 		be->amount = 1.0f;
898 	else if (be->amount != 0.0f && be->amount != 1.0f)
899 	{
900 		DDF_WarnError("Key benefit used, bad amount value: %1.1f\n", be->amount);
901 		return false;
902 	}
903 
904 	if (num_vals > 1)
905 	{
906 		DDF_WarnError("Key benefit cannot have a limit value.\n");
907 		return false;
908 	}
909 
910 	return true;
911 }
912 
BenefitTryHealth(const char * name,benefit_t * be,int num_vals)913 static bool BenefitTryHealth(const char *name, benefit_t *be,
914 								  int num_vals)
915 {
916 	if (DDF_CompareName(name, "HEALTH") != 0)
917 		return false;
918 
919 	be->type = BENEFIT_Health;
920 	be->sub.type = 0;
921 
922 	if (num_vals < 1)
923 	{
924 		DDF_WarnError("Health benefit used, but amount is missing.\n");
925 		return false;
926 	}
927 
928 	if (num_vals < 2)
929 		be->limit = 100.0f;
930 
931 	return true;
932 }
933 
BenefitTryArmour(const char * name,benefit_t * be,int num_vals)934 static bool BenefitTryArmour(const char *name, benefit_t *be,
935 								  int num_vals)
936 {
937 	if (CHKF_Positive != DDF_MainCheckSpecialFlag(name, armourtype_names,
938 		&be->sub.type, false, false))
939 	{
940 		return false;
941 	}
942 
943 	be->type = BENEFIT_Armour;
944 
945 	if (num_vals < 1)
946 	{
947 		DDF_WarnError("Armour benefit used, but amount is missing.\n");
948 		return false;
949 	}
950 
951 	if (num_vals < 2)
952 	{
953 		switch (be->sub.type)
954 		{
955 			case ARMOUR_Green:  be->limit = 100; break;
956 			case ARMOUR_Blue:   be->limit = 200; break;
957 			case ARMOUR_Purple: be->limit = 200; break;
958 			case ARMOUR_Yellow: be->limit = 200; break;
959 			case ARMOUR_Red:    be->limit = 200; break;
960 			default: ;
961 		}
962 	}
963 
964 	return true;
965 }
966 
BenefitTryPowerup(const char * name,benefit_t * be,int num_vals)967 static bool BenefitTryPowerup(const char *name, benefit_t *be, int num_vals)
968 {
969 	if (CHKF_Positive != DDF_MainCheckSpecialFlag(name, powertype_names,
970 		&be->sub.type, false, false))
971 	{
972 		return false;
973 	}
974 
975 	be->type = BENEFIT_Powerup;
976 
977 	if (num_vals < 1)
978 		be->amount = 999999.0f;
979 
980 	if (num_vals < 2)
981 		be->limit = 999999.0f;
982 
983 	// -AJA- backwards compatibility (need Fist for Berserk)
984 	if (be->sub.type == PW_Berserk &&
985 		DDF_CompareName(name, "POWERUP_BERSERK") == 0)
986 	{
987 		int idx = weapondefs.FindFirst("FIST", 0);
988 
989 		if (idx >= 0)
990 		{
991 			AddPickupEffect(&dynamic_mobj->pickup_effects,
992 				new pickup_effect_c(PUFX_SwitchWeapon, weapondefs[idx], 0, 0));
993 
994 			AddPickupEffect(&dynamic_mobj->pickup_effects,
995 				new pickup_effect_c(PUFX_KeepPowerup, PW_Berserk, 0, 0));
996 		}
997 	}
998 
999 	return true;
1000 }
1001 
BenefitAdd(benefit_t ** list,benefit_t * source)1002 static void BenefitAdd(benefit_t **list, benefit_t *source)
1003 {
1004 	benefit_t *cur, *tail;
1005 
1006 	// check if this benefit overrides a previous one
1007 	for (cur=(*list); cur; cur=cur->next)
1008 	{
1009 		if (cur->type == BENEFIT_Weapon)
1010 			continue;
1011 
1012 		if (cur->type == source->type && cur->sub.type == source->sub.type)
1013 		{
1014 			cur->amount = source->amount;
1015 			cur->limit  = source->limit;
1016 			return;
1017 		}
1018 	}
1019 
1020 	// nope, create a new one and link it onto the _TAIL_
1021 	cur = new benefit_t;
1022 
1023 	cur[0] = source[0];
1024 	cur->next = NULL;
1025 
1026 	if ((*list) == NULL)
1027 	{
1028 		(*list) = cur;
1029 		return;
1030 	}
1031 
1032 	for (tail = (*list); tail && tail->next; tail=tail->next)
1033 	{ }
1034 
1035 	tail->next = cur;
1036 }
1037 
1038 //
1039 // DDF_MobjGetBenefit
1040 //
1041 // Parse a single benefit and update the benefit list accordingly.  If
1042 // the type/subtype are not in the list, add a new entry, otherwise
1043 // just modify the existing entry.
1044 //
DDF_MobjGetBenefit(const char * info,void * storage)1045 void DDF_MobjGetBenefit(const char *info, void *storage)
1046 {
1047 	char namebuf[200];
1048 	char parambuf[200];
1049 	int num_vals;
1050 
1051 	benefit_t temp;
1052 
1053 	SYS_ASSERT(storage);
1054 
1055 	num_vals = ParseBenefitString(info, namebuf, parambuf, &temp.amount, &temp.limit);
1056 
1057 	// an error occurred ?
1058 	if (num_vals < 0)
1059 		return;
1060 
1061 	if (BenefitTryAmmo(namebuf, &temp, num_vals) ||
1062 		BenefitTryAmmoLimit(namebuf, &temp, num_vals) ||
1063 		BenefitTryWeapon(namebuf, &temp, num_vals) ||
1064 		BenefitTryKey(namebuf, &temp, num_vals) ||
1065 		BenefitTryHealth(namebuf, &temp, num_vals) ||
1066 		BenefitTryArmour(namebuf, &temp, num_vals) ||
1067 		BenefitTryPowerup(namebuf, &temp, num_vals))
1068 	{
1069 		BenefitAdd((benefit_t **) storage, &temp);
1070 		return;
1071 	}
1072 
1073 	DDF_WarnError("Unknown/Malformed benefit type: %s\n", namebuf);
1074 }
1075 
pickup_effect_c(pickup_effect_type_e _type,int _sub,int _slot,float _time)1076 pickup_effect_c::pickup_effect_c(pickup_effect_type_e _type,
1077 	int _sub, int _slot, float _time) :
1078 		next(NULL), type(_type), slot(_slot), time(_time)
1079 {
1080 	sub.type = _sub;
1081 }
1082 
pickup_effect_c(pickup_effect_type_e _type,weapondef_c * _weap,int _slot,float _time)1083 pickup_effect_c::pickup_effect_c(pickup_effect_type_e _type,
1084 	weapondef_c *_weap, int _slot, float _time) :
1085 		next(NULL), type(_type), slot(_slot), time(_time)
1086 {
1087 	sub.weap = _weap;
1088 }
1089 
AddPickupEffect(pickup_effect_c ** list,pickup_effect_c * cur)1090 static void AddPickupEffect(pickup_effect_c **list, pickup_effect_c *cur)
1091 {
1092 	cur->next = NULL;
1093 
1094 	if ((*list) == NULL)
1095 	{
1096 		(*list) = cur;
1097 		return;
1098 	}
1099 
1100 	pickup_effect_c *tail;
1101 
1102 	for (tail = (*list); tail && tail->next; tail=tail->next)
1103 	{ }
1104 
1105 	tail->next = cur;
1106 }
1107 
BA_ParsePowerupEffect(pickup_effect_c ** list,int pnum,float par1,float par2,const char * word_par)1108 void BA_ParsePowerupEffect(pickup_effect_c **list,
1109 	int pnum, float par1, float par2, const char *word_par)
1110 {
1111 	int p_up = (int)par1;
1112 	int slot = (int)par2;
1113 
1114 	SYS_ASSERT(0 <= p_up && p_up < NUMPOWERS);
1115 
1116 	if (slot < 0 || slot >= NUM_FX_SLOT)
1117 		DDF_Error("POWERUP_EFFECT: bad FX slot #%d\n", p_up);
1118 
1119 	AddPickupEffect(list, new pickup_effect_c(PUFX_PowerupEffect, p_up, slot, 0));
1120 }
1121 
BA_ParseScreenEffect(pickup_effect_c ** list,int pnum,float par1,float par2,const char * word_par)1122 void BA_ParseScreenEffect(pickup_effect_c **list,
1123 	int pnum, float par1, float par2, const char *word_par)
1124 {
1125 	int slot = (int)par1;
1126 
1127 	if (slot < 0 || slot >= NUM_FX_SLOT)
1128 		DDF_Error("SCREEN_EFFECT: bad FX slot #%d\n", slot);
1129 
1130 	if (par2 <= 0)
1131 		DDF_Error("SCREEN_EFFECT: bad time value: %1.2f\n", par2);
1132 
1133 	AddPickupEffect(list, new pickup_effect_c(PUFX_ScreenEffect, 0, slot, par2));
1134 }
1135 
BA_ParseSwitchWeapon(pickup_effect_c ** list,int pnum,float par1,float par2,const char * word_par)1136 void BA_ParseSwitchWeapon(pickup_effect_c **list,
1137 	int pnum, float par1, float par2, const char *word_par)
1138 {
1139 	if (pnum != -1)
1140 		DDF_Error("SWITCH_WEAPON: missing weapon name !\n");
1141 
1142 	SYS_ASSERT(word_par && word_par[0]);
1143 
1144 	weapondef_c *weap = weapondefs.Lookup(word_par);
1145 
1146 	AddPickupEffect(list, new pickup_effect_c(PUFX_SwitchWeapon, weap, 0, 0));
1147 }
1148 
BA_ParseKeepPowerup(pickup_effect_c ** list,int pnum,float par1,float par2,const char * word_par)1149 void BA_ParseKeepPowerup(pickup_effect_c **list,
1150 	int pnum, float par1, float par2, const char *word_par)
1151 {
1152 	if (pnum != -1)
1153 		DDF_Error("KEEP_POWERUP: missing powerup name !\n");
1154 
1155 	SYS_ASSERT(word_par && word_par[0]);
1156 
1157 	if (DDF_CompareName(word_par, "BERSERK") != 0)
1158 		DDF_Error("KEEP_POWERUP: %s is not supported\n", word_par);
1159 
1160 	AddPickupEffect(list, new pickup_effect_c(PUFX_KeepPowerup, PW_Berserk, 0, 0));
1161 }
1162 
1163 typedef struct pick_fx_parser_s
1164 {
1165 	const char *name;
1166 	int num_pars;  // -1 means a single word
1167 	void (* parser)(pickup_effect_c **list,
1168 			int pnum, float par1, float par2, const char *word_par);
1169 }
1170 pick_fx_parser_t;
1171 
1172 static const pick_fx_parser_t pick_fx_parsers[] =
1173 {
1174 	{ "SCREEN_EFFECT",  2, BA_ParseScreenEffect },
1175 	{ "SWITCH_WEAPON", -1, BA_ParseSwitchWeapon },
1176 	{ "KEEP_POWERUP",  -1, BA_ParseKeepPowerup },
1177 
1178 	// that's all, folks.
1179 	{ NULL, 0, NULL }
1180 };
1181 
1182 //
1183 // DDF_MobjGetPickupEffect
1184 //
1185 // Parse a single effect and add it to the effect list accordingly.
1186 // No merging is done.
1187 //
DDF_MobjGetPickupEffect(const char * info,void * storage)1188 void DDF_MobjGetPickupEffect(const char *info, void *storage)
1189 {
1190 	char namebuf[200];
1191 	char parambuf[200];
1192 	int num_vals;
1193 
1194 	SYS_ASSERT(storage);
1195 
1196 	pickup_effect_c **fx_list = (pickup_effect_c **) storage;
1197 
1198 	benefit_t temp; // FIXME kludge (write new parser method ?)
1199 
1200 	num_vals = ParseBenefitString(info, namebuf, parambuf, &temp.amount, &temp.limit);
1201 
1202 	// an error occurred ?
1203 	if (num_vals < 0)
1204 		return;
1205 
1206 	if (parambuf[0])
1207 		num_vals = -1;
1208 
1209 	for (int i=0; pick_fx_parsers[i].name; i++)
1210 	{
1211 		if (DDF_CompareName(pick_fx_parsers[i].name, namebuf) != 0)
1212 			continue;
1213 
1214 		(* pick_fx_parsers[i].parser)(fx_list, num_vals,
1215 			temp.amount, temp.limit, parambuf);
1216 
1217 		return;
1218 	}
1219 
1220 	// secondly, try the powerups
1221 	for (int p=0; powertype_names[p].name; p++)
1222 	{
1223 		if (DDF_CompareName(powertype_names[p].name, namebuf) != 0)
1224 			continue;
1225 
1226 		BA_ParsePowerupEffect(fx_list, num_vals, p, temp.amount, parambuf);
1227 
1228 		return;
1229 	}
1230 
1231 	DDF_Error("Unknown/Malformed benefit effect: %s\n", namebuf);
1232 }
1233 
1234 // -KM- 1998/11/25 Translucency to fractional.
1235 // -KM- 1998/12/16 Added individual flags for all.
1236 // -AJA- 2000/02/02: Split into two lists.
1237 
1238 static const specflags_t normal_specials[] =
1239 {
1240 	{"AMBUSH", MF_AMBUSH, 0},
1241 	{"FUZZY", MF_FUZZY, 0},
1242 	{"SOLID", MF_SOLID, 0},
1243 	{"ON_CEILING", MF_SPAWNCEILING + MF_NOGRAVITY, 0},
1244 	{"FLOATER", MF_FLOAT + MF_NOGRAVITY, 0},
1245 	{"INERT", MF_NOBLOCKMAP, 0},
1246 	{"TELEPORT_TYPE", MF_NOGRAVITY, 0},
1247 	{"LINKS", MF_NOBLOCKMAP + MF_NOSECTOR, 1},
1248 	{"DAMAGESMOKE", MF_NOBLOOD, 0},
1249 	{"SHOOTABLE", MF_SHOOTABLE, 0},
1250 	{"COUNT_AS_KILL", MF_COUNTKILL, 0},
1251 	{"COUNT_AS_ITEM", MF_COUNTITEM, 0},
1252 	{"SKULLFLY", MF_SKULLFLY, 0},
1253 	{"SPECIAL", MF_SPECIAL, 0},
1254 	{"SECTOR", MF_NOSECTOR, 1},
1255 	{"BLOCKMAP", MF_NOBLOCKMAP, 1},
1256 	{"SPAWNCEILING", MF_SPAWNCEILING, 0},
1257 	{"GRAVITY", MF_NOGRAVITY, 1},
1258 	{"DROPOFF", MF_DROPOFF, 0},
1259 	{"PICKUP", MF_PICKUP, 0},
1260 	{"CLIP", MF_NOCLIP, 1},
1261 	{"SLIDER", MF_SLIDE, 0},
1262 	{"FLOAT", MF_FLOAT, 0},
1263 	{"TELEPORT", MF_TELEPORT, 0},
1264 	{"MISSILE", MF_MISSILE, 0},   // has a special check
1265 	{"BARE_MISSILE", MF_MISSILE, 0},
1266 	{"DROPPED", MF_DROPPED, 0},
1267 	{"CORPSE", MF_CORPSE, 0},
1268 	{"STEALTH", MF_STEALTH, 0},
1269 	{"DEATHMATCH", MF_NOTDMATCH, 1},
1270 	{"TOUCHY", MF_TOUCHY, 0},
1271 	{NULL, 0, 0}
1272 };
1273 
1274 static specflags_t extended_specials[] =
1275 {
1276 	{"RESPAWN", EF_NORESPAWN, 1},
1277 	{"RESURRECT", EF_NORESURRECT, 1},
1278 	{"DISLOYAL", EF_DISLOYALTYPE, 0},
1279 	{"TRIGGER_HAPPY", EF_TRIGGERHAPPY, 0},
1280 	{"ATTACK_HURTS", EF_OWNATTACKHURTS, 0},
1281 	{"EXPLODE_IMMUNE", EF_EXPLODEIMMUNE, 0},
1282 	{"ALWAYS_LOUD", EF_ALWAYSLOUD, 0},
1283 	{"BOSSMAN", EF_EXPLODEIMMUNE + EF_ALWAYSLOUD, 0},
1284 	{"NEVERTARGETED", EF_NEVERTARGET, 0},
1285 	{"GRAV_KILL", EF_NOGRAVKILL, 1},
1286 	{"GRUDGE", EF_NOGRUDGE, 1},
1287 	{"BOUNCE", EF_BOUNCE, 0},
1288 	{"EDGEWALKER", EF_EDGEWALKER, 0},
1289 	{"GRAVFALL", EF_GRAVFALL, 0},
1290 	{"CLIMBABLE", EF_CLIMBABLE, 0},
1291 	{"WATERWALKER", EF_WATERWALKER, 0},
1292 	{"MONSTER", EF_MONSTER, 0},
1293 	{"CROSSLINES", EF_CROSSLINES, 0},
1294 	{"FRICTION", EF_NOFRICTION, 1},
1295 	{"USABLE", EF_USABLE, 0},
1296 	{"BLOCK_SHOTS", EF_BLOCKSHOTS, 0},
1297 	{"TUNNEL", EF_TUNNEL, 0},
1298 	{"SIMPLE_ARMOUR", EF_SIMPLEARMOUR, 0},
1299 	{NULL, 0, 0}
1300 };
1301 
1302 static specflags_t hyper_specials[] =
1303 {
1304 	{"FORCE_PICKUP", HF_FORCEPICKUP, 0},
1305 	{"SIDE_IMMUNE", HF_SIDEIMMUNE, 0},
1306 	{"SIDE_GHOST", HF_SIDEGHOST, 0},
1307 	{"ULTRA_LOYAL", HF_ULTRALOYAL, 0},
1308 	{"ZBUFFER", HF_NOZBUFFER, 1},
1309 	{"HOVER", HF_HOVER, 0},
1310 	{"PUSHABLE", HF_PUSHABLE, 0},
1311 	{"POINT_FORCE", HF_POINT_FORCE, 0},
1312 	{"PASS_MISSILE", HF_PASSMISSILE, 0},
1313 	{"INVULNERABLE", HF_INVULNERABLE, 0},
1314 	{"VAMPIRE", HF_VAMPIRE, 0},
1315 	{"AUTOAIM", HF_NO_AUTOAIM, 1},
1316 	{"TILT", HF_TILT, 0},
1317 	{NULL, 0, 0}
1318 };
1319 
1320 //
1321 // DDF_MobjGetSpecial
1322 //
1323 // Compares info the the entries in special flag lists.
1324 // If found, apply attributes for it to current mobj.
1325 //
DDF_MobjGetSpecial(const char * info)1326 void DDF_MobjGetSpecial(const char *info)
1327 {
1328 	int flag_value;
1329 
1330 	// handle the "INVISIBLE" tag
1331 	if (DDF_CompareName(info, "INVISIBLE") == 0)
1332 	{
1333 		dynamic_mobj->translucency = PERCENT_MAKE(0);
1334 		return;
1335 	}
1336 
1337 	// handle the "NOSHADOW" tag
1338 	if (DDF_CompareName(info, "NOSHADOW") == 0)
1339 	{
1340 		dynamic_mobj->shadow_trans = PERCENT_MAKE(0);
1341 		return;
1342 	}
1343 
1344 	// the "MISSILE" tag needs special treatment, since it sets both
1345 	// normal flags & extended flags.
1346 	if (DDF_CompareName(info, "MISSILE") == 0)
1347 	{
1348 		dynamic_mobj->flags |= MF_MISSILE;
1349 		dynamic_mobj->extendedflags |= EF_CROSSLINES | EF_NOFRICTION;
1350 		return;
1351 	}
1352 
1353 	int *flag_ptr = &dynamic_mobj->flags;
1354 
1355 	checkflag_result_e res =
1356 		DDF_MainCheckSpecialFlag(info, normal_specials,
1357 			&flag_value, true, false);
1358 
1359 	if (res == CHKF_User || res == CHKF_Unknown)
1360 	{
1361 		// wasn't a normal special.  Try the extended ones...
1362 		flag_ptr = &dynamic_mobj->extendedflags;
1363 
1364 		res = DDF_MainCheckSpecialFlag(info, extended_specials,
1365 				&flag_value, true, false);
1366 	}
1367 
1368 	if (res == CHKF_User || res == CHKF_Unknown)
1369 	{
1370 		// -AJA- 2004/08/25: Try the hyper specials...
1371 		flag_ptr = &dynamic_mobj->hyperflags;
1372 
1373 		res = DDF_MainCheckSpecialFlag(info, hyper_specials,
1374 				&flag_value, true, false);
1375 	}
1376 
1377 	switch (res)
1378 	{
1379 		case CHKF_Positive:
1380 			*flag_ptr |= flag_value;
1381 			break;
1382 
1383 		case CHKF_Negative:
1384 			*flag_ptr &= ~flag_value;
1385 			break;
1386 
1387 		case CHKF_User:
1388 		case CHKF_Unknown:
1389 			DDF_WarnError("DDF_MobjGetSpecial: Unknown special '%s'\n", info);
1390 			break;
1391 	}
1392 }
1393 
1394 static specflags_t dlight_type_names[] =
1395 {
1396 	{"NONE",      DLITE_None, 0},
1397 	{"MODULATE",  DLITE_Modulate, 0},
1398 	{"ADD",       DLITE_Add, 0},
1399 
1400 	// backwards compatibility
1401 	{"LINEAR",    DLITE_Compat_LIN, 0},
1402 	{"QUADRATIC", DLITE_Compat_QUAD, 0},
1403 	{"CONSTANT",  DLITE_Compat_LIN, 0},
1404 
1405 	{NULL, 0, 0}
1406 };
1407 
1408 //
1409 // DDF_MobjGetDLight
1410 //
DDF_MobjGetDLight(const char * info,void * storage)1411 void DDF_MobjGetDLight(const char *info, void *storage)
1412 {
1413 	dlight_type_e *dtype = (dlight_type_e *)storage;
1414 	int flag_value;
1415 
1416 	SYS_ASSERT(dtype);
1417 
1418 	if (CHKF_Positive != DDF_MainCheckSpecialFlag(info,
1419 		dlight_type_names, &flag_value, false, false))
1420 	{
1421 		DDF_WarnError("Unknown dlight type '%s'\n", info);
1422 		return;
1423 	}
1424 
1425 	(*dtype) = (dlight_type_e)flag_value;
1426 }
1427 
1428 //
1429 // DDF_MobjGetExtra
1430 //
DDF_MobjGetExtra(const char * info,void * storage)1431 void DDF_MobjGetExtra(const char *info, void *storage)
1432 {
1433 	int *extendedflags = (int *)storage;
1434 
1435 	// If keyword is "NULL", then the mobj is not marked as extra.
1436 	// Otherwise it is.
1437 
1438 	if (DDF_CompareName(info, "NULL") == 0)
1439 	{
1440 		*extendedflags &= ~EF_EXTRA;
1441 	}
1442 	else
1443 	{
1444 		*extendedflags |= EF_EXTRA;
1445 	}
1446 }
1447 
1448 //
1449 // DDF_MobjGetPlayer
1450 //
1451 // Reads player number and makes sure that maxplayer is large enough.
1452 //
DDF_MobjGetPlayer(const char * info,void * storage)1453 void DDF_MobjGetPlayer(const char *info, void *storage)
1454 {
1455 	int *dest = (int *)storage;
1456 
1457 	DDF_MainGetNumeric(info, storage);
1458 
1459 	if (*dest > 32)
1460 		DDF_Warning("Player number '%d' will not work.", *dest);
1461 }
1462 
1463 
1464 static const specflags_t glow_type_names[] =
1465 {
1466 	{"FLOOR",   GLOW_Floor,   0},
1467 	{"CEILING", GLOW_Ceiling, 0},
1468 	{"WALL",    GLOW_Wall,    0},
1469 
1470 	{NULL, 0, 0}
1471 };
1472 
DDF_MobjGetGlowType(const char * info,void * storage)1473 static void DDF_MobjGetGlowType(const char *info, void *storage)
1474 {
1475 	if (CHKF_Positive != DDF_MainCheckSpecialFlag(info,
1476 		glow_type_names, (int *) storage, false, false))
1477 	{
1478 		DDF_WarnError("DDF_MobjGetGlowType: Unknown glow type: %s\n", info);
1479 	}
1480 }
1481 
1482 
1483 static const specflags_t sprite_yalign_names[] =
1484 {
1485 	{"BOTTOM", SPYA_BottomUp, 0},
1486 	{"MIDDLE", SPYA_Middle,   0},
1487 	{"TOP",    SPYA_TopDown,  0},
1488 
1489 	{NULL, 0, 0}
1490 };
1491 
DDF_MobjGetYAlign(const char * info,void * storage)1492 static void DDF_MobjGetYAlign(const char *info, void *storage)
1493 {
1494 	if (CHKF_Positive != DDF_MainCheckSpecialFlag(info,
1495 		sprite_yalign_names, (int *) storage, false, false))
1496 	{
1497 		DDF_WarnError("DDF_MobjGetYAlign: Unknown alignment: %s\n", info);
1498 	}
1499 }
1500 
DDF_MobjGetPercentRange(const char * info,void * storage)1501 static void DDF_MobjGetPercentRange(const char *info, void *storage)
1502 {
1503 	SYS_ASSERT(info && storage);
1504 
1505 	float *dest = (float *)storage;
1506 
1507 	if (sscanf(info, "%f%%:%f%%", dest+0, dest+1) != 2)
1508 		DDF_Error("Bad percentage range: %s\n", info);
1509 
1510 	dest[0] /= 100.0f;
1511 	dest[1] /= 100.0f;
1512 
1513 	if (dest[0] > dest[1])
1514 		DDF_Error("Bad percent range (low > high) : %s\n", info);
1515 }
1516 
DDF_MobjGetAngleRange(const char * info,void * storage)1517 static void DDF_MobjGetAngleRange(const char *info, void *storage)
1518 {
1519 	SYS_ASSERT(info && storage);
1520 
1521 	angle_t *dest = (angle_t *)storage;
1522 
1523 	float val1, val2;
1524 
1525 	if (sscanf(info, "%f:%f", &val1, &val2) != 2)
1526 		DDF_Error("Bad angle range: %s\n", info);
1527 
1528 	if ((int) val1 == 360)
1529 		val1 = 359.5;
1530 	else if (val1 > 360.0f)
1531 		DDF_Error("Angle '%1.1f' too large (must be less than 360)\n", val1);
1532 
1533 	if ((int) val2 == 360)
1534 		val2 = 359.5;
1535 	else if (val2 > 360.0f)
1536 		DDF_Error("Angle '%1.1f' too large (must be less than 360)\n", val2);
1537 
1538 	dest[0] = FLOAT_2_ANG(val1);
1539 	dest[1] = FLOAT_2_ANG(val2);
1540 }
1541 
1542 
1543 //
1544 //  CONDITION TESTERS
1545 //
1546 //  These return true if the name matches that particular type of
1547 //  condition (e.g. "ROCKET" for ammo), and adjusts the condition
1548 //  accodingly.  Otherwise returns false.
1549 //
1550 
ConditionTryAmmo(const char * name,const char * sub,condition_check_t * cond)1551 static bool ConditionTryAmmo(const char *name, const char *sub,
1552 								  condition_check_t *cond)
1553 {
1554 	if (CHKF_Positive != DDF_MainCheckSpecialFlag(name, ammo_types,
1555 		&cond->sub.type, false, false))
1556 	{
1557 		return false;
1558 	}
1559 
1560 	if ((ammotype_e)cond->sub.type == AM_NoAmmo)
1561 	{
1562 		DDF_WarnError("Illegal ammo in condition: %s\n", name);
1563 		return false;
1564 	}
1565 
1566 	if (sub[0])
1567 		sscanf(sub, " %f ", &cond->amount);
1568 
1569 	cond->cond_type = COND_Ammo;
1570 	return true;
1571 }
1572 
ConditionTryWeapon(const char * name,const char * sub,condition_check_t * cond)1573 static bool ConditionTryWeapon(const char *name, const char *sub,
1574 									condition_check_t *cond)
1575 {
1576 	int idx = weapondefs.FindFirst(name, 0);
1577 
1578 	if (idx < 0)
1579 		return false;
1580 
1581 	cond->sub.weap = weapondefs[idx];
1582 
1583 	cond->cond_type = COND_Weapon;
1584 	return true;
1585 }
1586 
ConditionTryKey(const char * name,const char * sub,condition_check_t * cond)1587 static bool ConditionTryKey(const char *name, const char *sub,
1588 								 condition_check_t *cond)
1589 {
1590 	if (CHKF_Positive != DDF_MainCheckSpecialFlag(name, keytype_names,
1591 		&cond->sub.type, false, false))
1592 	{
1593 		return false;
1594 	}
1595 
1596 	cond->cond_type = COND_Key;
1597 	return true;
1598 }
1599 
ConditionTryHealth(const char * name,const char * sub,condition_check_t * cond)1600 static bool ConditionTryHealth(const char *name, const char *sub,
1601 									condition_check_t *cond)
1602 {
1603 	if (DDF_CompareName(name, "HEALTH") != 0)
1604 		return false;
1605 
1606 	if (sub[0])
1607 		sscanf(sub, " %f ", &cond->amount);
1608 
1609 	cond->cond_type = COND_Health;
1610 	return true;
1611 }
1612 
ConditionTryArmour(const char * name,const char * sub,condition_check_t * cond)1613 static bool ConditionTryArmour(const char *name, const char *sub,
1614 									condition_check_t *cond)
1615 {
1616 	if (DDF_CompareName(name, "ARMOUR") == 0)
1617 	{
1618 		cond->sub.type = ARMOUR_Total;
1619 	}
1620 	else if (CHKF_Positive != DDF_MainCheckSpecialFlag(name, armourtype_names,
1621 		&cond->sub.type, false, false))
1622 	{
1623 		return false;
1624 	}
1625 
1626 	if (sub[0])
1627 		sscanf(sub, " %f ", &cond->amount);
1628 
1629 	cond->cond_type = COND_Armour;
1630 	return true;
1631 }
1632 
ConditionTryPowerup(const char * name,const char * sub,condition_check_t * cond)1633 static bool ConditionTryPowerup(const char *name, const char *sub,
1634 									 condition_check_t *cond)
1635 {
1636 	if (CHKF_Positive != DDF_MainCheckSpecialFlag(name, powertype_names,
1637 		&cond->sub.type, false, false))
1638 	{
1639 		return false;
1640 	}
1641 
1642 	if (sub[0])
1643 	{
1644 		sscanf(sub, " %f ", &cond->amount);
1645 
1646 		cond->amount *= (float)TICRATE;
1647 	}
1648 
1649 	cond->cond_type = COND_Powerup;
1650 	return true;
1651 }
1652 
ConditionTryPlayerState(const char * name,const char * sub,condition_check_t * cond)1653 static bool ConditionTryPlayerState(const char *name, const char *sub,
1654 										 condition_check_t *cond)
1655 {
1656 	return (CHKF_Positive == DDF_MainCheckSpecialFlag(name,
1657 		simplecond_names, (int *)&cond->cond_type, false, false));
1658 }
1659 
1660 //
1661 // DDF_MainParseCondition
1662 //
1663 // Returns `false' if parsing failed.
1664 //
DDF_MainParseCondition(const char * info,condition_check_t * cond)1665 bool DDF_MainParseCondition(const char *info, condition_check_t *cond)
1666 {
1667 	char typebuf[100];
1668 	char sub_buf[100];
1669 
1670 	int len = strlen(info);
1671 	int t_off;
1672 	const char *pos;
1673 
1674 	cond->negate = false;
1675 	cond->cond_type = COND_NONE;
1676 	cond->amount = 1;
1677 
1678 	memset(&cond->sub, 0, sizeof(cond->sub));
1679 
1680 	pos = strchr(info, '(');
1681 
1682 	// do we have matched parentheses ?
1683 	if (pos && pos > info && len >= 4 && info[len-1] == ')')
1684 	{
1685 		int len2 = (pos - info);
1686 
1687 		Z_StrNCpy(typebuf, info, len2);
1688 
1689 		len -= len2 + 2;
1690 		Z_StrNCpy(sub_buf, pos + 1, len);
1691 	}
1692 	else if (pos || strchr(info, ')'))
1693 	{
1694 		DDF_WarnError("Malformed condition string: %s\n", info);
1695 		return false;
1696 	}
1697 	else
1698 	{
1699 		strcpy(typebuf, info);
1700 		sub_buf[0] = 0;
1701 	}
1702 
1703 	// check for negation
1704 	t_off = 0;
1705 	if (strnicmp(typebuf, "NOT_", 4) == 0)
1706 	{
1707 		cond->negate = true;
1708 		t_off = 4;
1709 	}
1710 
1711 	if (ConditionTryAmmo(typebuf + t_off, sub_buf, cond) ||
1712 		ConditionTryWeapon(typebuf + t_off, sub_buf, cond) ||
1713 		ConditionTryKey(typebuf + t_off, sub_buf, cond) ||
1714 		ConditionTryHealth(typebuf + t_off, sub_buf, cond) ||
1715 		ConditionTryArmour(typebuf + t_off, sub_buf, cond) ||
1716 		ConditionTryPowerup(typebuf + t_off, sub_buf, cond) ||
1717 		ConditionTryPlayerState(typebuf + t_off, sub_buf, cond))
1718 	{
1719 		return true;
1720 	}
1721 
1722 	DDF_WarnError("Unknown/Malformed condition type: %s\n", typebuf);
1723 	return false;
1724 }
1725 
1726 
1727 // ---> mobjdef class
1728 
mobjtype_c()1729 mobjtype_c::mobjtype_c() : name(), state_grp()
1730 {
1731 	Default();
1732 }
1733 
~mobjtype_c()1734 mobjtype_c::~mobjtype_c()
1735 {
1736 }
1737 
1738 
CopyDetail(mobjtype_c & src)1739 void mobjtype_c::CopyDetail(mobjtype_c &src)
1740 {
1741 	state_grp.clear();
1742 
1743 	for (unsigned int i = 0; i < src.state_grp.size(); i++)
1744 		state_grp.push_back(src.state_grp[i]);
1745 
1746     spawn_state = src.spawn_state;
1747     idle_state = src.idle_state;
1748     chase_state = src.chase_state;
1749     pain_state = src.pain_state;
1750     missile_state = src.missile_state;
1751     melee_state = src.melee_state;
1752     death_state = src.death_state;
1753     overkill_state = src.overkill_state;
1754     raise_state = src.raise_state;
1755     res_state = src.res_state;
1756     meander_state = src.meander_state;
1757     bounce_state = src.bounce_state;
1758     touch_state = src.touch_state;
1759     reload_state = src.reload_state;
1760     gib_state = src.gib_state;
1761 
1762     reactiontime = src.reactiontime;
1763 	painchance = src.painchance;
1764 	spawnhealth = src.spawnhealth;
1765     speed = src.speed;
1766 	float_speed = src.float_speed;
1767     radius = src.radius;
1768     height = src.height;
1769 	step_size = src.step_size;
1770 	mass = src.mass;
1771 
1772     flags = src.flags;
1773     extendedflags = src.extendedflags;
1774     hyperflags = src.hyperflags;
1775 
1776 	explode_damage = src.explode_damage;
1777 	explode_radius = src.explode_radius;
1778 
1779 	lose_benefits = src.lose_benefits;
1780 	pickup_benefits = src.pickup_benefits;
1781 	pickup_effects = src.pickup_effects;
1782 	pickup_message = src.pickup_message;
1783 	initial_benefits = src.initial_benefits;
1784 
1785     castorder = src.castorder;
1786 	cast_title = src.cast_title;
1787 	respawntime = src.respawntime;
1788 	translucency = src.translucency;
1789 	minatkchance = src.minatkchance;
1790 	palremap = src.palremap;
1791 
1792 	jump_delay = src.jump_delay;
1793     jumpheight = src.jumpheight;
1794     crouchheight = src.crouchheight;
1795 	viewheight = src.viewheight;
1796 	shotheight = src.shotheight;
1797     maxfall = src.maxfall;
1798 	fast = src.fast;
1799 
1800 	scale = src.scale;
1801 	aspect = src.aspect;
1802 	yalign = src.yalign;
1803 
1804 	model_skin = src.model_skin;
1805 	model_scale = src.model_scale;
1806 	model_aspect = src.model_aspect;
1807 	model_bias = src.model_bias;
1808 
1809 	bounce_speed = src.bounce_speed;
1810 	bounce_up = src.bounce_up;
1811 	sight_slope = src.sight_slope;
1812 	sight_angle = src.sight_angle;
1813 	ride_friction = src.ride_friction;
1814 	shadow_trans = src.shadow_trans;
1815 	glow_type = src.glow_type;
1816 
1817 	seesound = src.seesound;
1818 	attacksound = src.attacksound;
1819 	painsound = src.painsound;
1820 	deathsound = src.deathsound;
1821 	overkill_sound = src.overkill_sound;
1822 	activesound = src.activesound;
1823 	walksound = src.walksound;
1824 	jump_sound = src.jump_sound;
1825 	noway_sound = src.noway_sound;
1826 	oof_sound = src.oof_sound;
1827 	gasp_sound = src.gasp_sound;
1828 
1829     fuse = src.fuse;
1830 	reload_shots = src.reload_shots;
1831 	armour_protect = src.armour_protect;
1832 	armour_deplete = src.armour_deplete;
1833 	armour_class = src.armour_class;
1834 
1835 	side = src.side;
1836     playernum = src.playernum;
1837 	lung_capacity = src.lung_capacity;
1838 	gasp_start = src.gasp_start;
1839 
1840 	// choke_damage
1841 	choke_damage = src.choke_damage;
1842 
1843 	bobbing = src.bobbing;
1844 	immunity = src.immunity;
1845 	resistance = src.resistance;
1846 	resist_multiply = src.resist_multiply;
1847 	resist_painchance = src.resist_painchance;
1848 	ghost = src.ghost;
1849 
1850 	closecombat = src.closecombat;
1851 	rangeattack = src.rangeattack;
1852 	spareattack = src.spareattack;
1853 
1854 	// dynamic light info
1855 	dlight[0] = src.dlight[0];
1856 	dlight[1] = src.dlight[1];
1857 
1858 	weak = src.weak;
1859 
1860 	dropitem = src.dropitem;
1861 	dropitem_ref = src.dropitem_ref;
1862 	blood = src.blood;
1863 	blood_ref = src.blood_ref;
1864 	respawneffect = src.respawneffect;
1865 	respawneffect_ref = src.respawneffect_ref;
1866 	spitspot = src.spitspot;
1867 	spitspot_ref = src.spitspot_ref;
1868 }
1869 
1870 
Default()1871 void mobjtype_c::Default()
1872 {
1873 	state_grp.clear();
1874 
1875     spawn_state = 0;
1876     idle_state = 0;
1877     chase_state = 0;
1878     pain_state = 0;
1879     missile_state = 0;
1880     melee_state = 0;
1881     death_state = 0;
1882     overkill_state = 0;
1883     raise_state = 0;
1884     res_state = 0;
1885     meander_state = 0;
1886     bounce_state = 0;
1887     touch_state = 0;
1888     reload_state = 0;
1889     gib_state = 0;
1890 
1891     reactiontime = 0;
1892 	painchance = PERCENT_MAKE(0);
1893 	spawnhealth = 1000.0f;
1894     speed = 0;
1895 	float_speed = 2.0f;
1896     radius = 0;
1897     height = 0;
1898 	step_size = 24.0f;
1899 	mass = 100.0f;
1900 
1901     flags = 0;
1902     extendedflags = 0;
1903 	hyperflags = 0;
1904 
1905 	explode_damage.Default(damage_c::DEFAULT_Mobj);
1906 	explode_radius = 0;
1907 
1908 	lose_benefits = NULL;
1909 	pickup_benefits = NULL;
1910 	pickup_effects = NULL;
1911 	pickup_message = NULL;
1912 	initial_benefits = NULL;
1913 
1914     castorder = 0;
1915 	cast_title.clear();
1916 	respawntime = 30 * TICRATE;
1917 	translucency = PERCENT_MAKE(100);
1918 	minatkchance = PERCENT_MAKE(0);
1919 	palremap = NULL;
1920 
1921 	jump_delay = 1 * TICRATE;
1922     jumpheight = 0;
1923     crouchheight = 0;
1924 	viewheight = PERCENT_MAKE(75);
1925 	shotheight = PERCENT_MAKE(64);
1926     maxfall = 0;
1927 	fast = 1.0f;
1928 	scale = 1.0f;
1929 	aspect = 1.0f;
1930 	yalign = SPYA_BottomUp;
1931 
1932 	model_skin = 1;
1933 	model_scale = 1.0f;
1934 	model_aspect = 1.0f;
1935 	model_bias = 0.0f;
1936 
1937 	bounce_speed = 0.5f;
1938 	bounce_up = 0.5f;
1939 	sight_slope = 16.0f;
1940 	sight_angle = ANG90;
1941 	ride_friction = RIDE_FRICTION;
1942 	shadow_trans = PERCENT_MAKE(50);
1943 	glow_type = GLOW_None;
1944 
1945 	seesound = sfx_None;
1946 	attacksound = sfx_None;
1947 	painsound = sfx_None;
1948 	deathsound = sfx_None;
1949 	overkill_sound = sfx_None;
1950 	activesound = sfx_None;
1951 	walksound = sfx_None;
1952 	jump_sound = sfx_None;
1953 	noway_sound = sfx_None;
1954 	oof_sound = sfx_None;
1955 	gasp_sound = sfx_None;
1956 
1957     fuse = 0;
1958 	reload_shots = 5;
1959 	armour_protect = -1.0;  // disabled!
1960 	armour_deplete = PERCENT_MAKE(100);
1961 	armour_class = BITSET_FULL;
1962 
1963 	side = BITSET_EMPTY;
1964     playernum = 0;
1965 	lung_capacity = 20 * TICRATE;
1966 	gasp_start = 2  * TICRATE;
1967 
1968 	choke_damage.Default(damage_c::DEFAULT_MobjChoke);
1969 
1970 	bobbing = PERCENT_MAKE(100);
1971 	immunity = BITSET_EMPTY;
1972 	resistance = BITSET_EMPTY;
1973 	resist_multiply = 0.4;
1974 	resist_painchance = -1; // disabled
1975 	ghost = BITSET_EMPTY;
1976 
1977 	closecombat = NULL;
1978 	rangeattack = NULL;
1979 	spareattack = NULL;
1980 
1981 	// dynamic light info
1982 	dlight[0].Default();
1983 	dlight[1].Default();
1984 
1985 	weak.Default();
1986 
1987 	dropitem = NULL;
1988 	dropitem_ref.clear();
1989 	blood = NULL;
1990 	blood_ref.clear();
1991 	respawneffect = NULL;
1992 	respawneffect_ref.clear();
1993 	spitspot = NULL;
1994 	spitspot_ref.clear();
1995 }
1996 
DLightCompatibility(void)1997 void mobjtype_c::DLightCompatibility(void)
1998 {
1999 	for (int DL = 0; DL < 2; DL++)
2000 	{
2001 		int r = RGB_RED(dlight[DL].colour);
2002 		int g = RGB_GRN(dlight[DL].colour);
2003 		int b = RGB_BLU(dlight[DL].colour);
2004 
2005 		// dim the colour
2006 		r = int(r * DLIT_COMPAT_ITY);
2007 		g = int(g * DLIT_COMPAT_ITY);
2008 		b = int(b * DLIT_COMPAT_ITY);
2009 
2010 		switch (dlight[DL].type)
2011 		{
2012 			case DLITE_Compat_QUAD:
2013 				dlight[DL].type = DLITE_Modulate;
2014 				dlight[DL].radius = DLIT_COMPAT_RAD(dlight[DL].radius);
2015 				dlight[DL].colour = RGB_MAKE(r, g, b);
2016 
2017 				hyperflags |= HF_QUADRATIC_COMPAT;
2018 				break;
2019 
2020 			case DLITE_Compat_LIN:
2021 				dlight[DL].type = DLITE_Modulate;
2022 				dlight[DL].radius *= 1.3;
2023 				dlight[DL].colour = RGB_MAKE(r, g, b);
2024 				break;
2025 
2026 			default: // nothing to do
2027 				break;
2028 		}
2029 
2030 //??	if (dlight[DL].radius > 500)
2031 //??		dlight[DL].radius = 500;
2032 	}
2033 }
2034 
2035 
2036 // --> mobjtype_container_c class
2037 
mobjtype_container_c()2038 mobjtype_container_c::mobjtype_container_c() : epi::array_c(sizeof(mobjtype_c*))
2039 {
2040 	memset(lookup_cache, 0, sizeof(mobjtype_c*) * LOOKUP_CACHESIZE);
2041 }
2042 
2043 
~mobjtype_container_c()2044 mobjtype_container_c::~mobjtype_container_c()
2045 {
2046 	Clear();					// <-- Destroy self before exiting
2047 }
2048 
2049 
CleanupObject(void * obj)2050 void mobjtype_container_c::CleanupObject(void *obj)
2051 {
2052 	mobjtype_c *m = *(mobjtype_c**)obj;
2053 
2054 	if (m)
2055 		delete m;
2056 
2057 	return;
2058 }
2059 
2060 
FindFirst(const char * name,int startpos)2061 int mobjtype_container_c::FindFirst(const char *name, int startpos)
2062 {
2063 	epi::array_iterator_c it;
2064 	mobjtype_c *m;
2065 
2066 	if (startpos>0)
2067 		it = GetIterator(startpos);
2068 	else
2069 		it = GetBaseIterator();
2070 
2071 	while (it.IsValid())
2072 	{
2073 		m = ITERATOR_TO_TYPE(it, mobjtype_c*);
2074 		if (DDF_CompareName(m->name.c_str(), name) == 0)
2075 		{
2076 			return it.GetPos();
2077 		}
2078 
2079 		it++;
2080 	}
2081 
2082 	return -1;
2083 }
2084 
2085 
FindLast(const char * name,int startpos)2086 int mobjtype_container_c::FindLast(const char *name, int startpos)
2087 {
2088 	epi::array_iterator_c it;
2089 	mobjtype_c *m;
2090 
2091 	if (startpos>=0 && startpos<array_entries)
2092 		it = GetIterator(startpos);
2093 	else
2094 		it = GetTailIterator();
2095 
2096 	while (it.IsValid())
2097 	{
2098 		m = ITERATOR_TO_TYPE(it, mobjtype_c*);
2099 		if (DDF_CompareName(m->name.c_str(), name) == 0)
2100 		{
2101 			return it.GetPos();
2102 		}
2103 
2104 		it--;
2105 	}
2106 
2107 	return -1;
2108 }
2109 
2110 
MoveToEnd(int idx)2111 bool mobjtype_container_c::MoveToEnd(int idx)
2112 {
2113 	// Moves an entry from its current position to end of the list.
2114 
2115 	mobjtype_c* m;
2116 
2117 	if (idx < 0 || idx >= array_entries)
2118 		return false;
2119 
2120 	if (idx == (array_entries - 1))
2121 		return true;					// Already at the end
2122 
2123 	// Get a copy of the pointer
2124 	m = (*this)[idx];
2125 
2126 	memmove(&array[idx*array_block_objsize],
2127 		&array[(idx+1)*array_block_objsize],
2128 		(array_entries-(idx+1))*array_objsize);
2129 
2130 	memcpy(&array[(array_entries-1)*array_block_objsize], (void*)&m, sizeof(mobjtype_c*));
2131 	return true;
2132 }
2133 
2134 
Lookup(const char * refname)2135 const mobjtype_c *mobjtype_container_c::Lookup(const char *refname)
2136 {
2137 	// Looks an mobjdef by name.
2138 	// Fatal error if it does not exist.
2139 
2140 	int idx = FindLast(refname);
2141 
2142 	if (idx >= 0)
2143 		return (*this)[idx];
2144 
2145 	if (lax_errors)
2146 		return default_mobjtype;
2147 
2148 	DDF_Error("Unknown thing type: %s\n", refname);
2149 	return NULL; /* NOT REACHED */
2150 }
2151 
2152 
Lookup(int id)2153 const mobjtype_c *mobjtype_container_c::Lookup(int id)
2154 {
2155 	if (id == 0)
2156 		return default_mobjtype;
2157 
2158 	// Looks an mobjdef by number.
2159 	// Fatal error if it does not exist.
2160 
2161 	int slot = DDF_MobjHashFunc(id);
2162 
2163 	// check the cache
2164 	if (lookup_cache[slot] &&
2165 		lookup_cache[slot]->number == id)
2166 	{
2167 		return lookup_cache[slot];
2168 	}
2169 
2170 	epi::array_iterator_c it;
2171 
2172 	for (it = GetTailIterator(); it.IsValid(); it--)
2173 	{
2174 		mobjtype_c *m = ITERATOR_TO_TYPE(it, mobjtype_c*);
2175 
2176 		if (m->number == id)
2177 		{
2178 			// update the cache
2179 			lookup_cache[slot] = m;
2180 			return m;
2181 		}
2182 	}
2183 
2184 	return NULL;
2185 }
2186 
2187 
LookupCastMember(int castpos)2188 const mobjtype_c *mobjtype_container_c::LookupCastMember(int castpos)
2189 {
2190 	// Lookup the cast member of the one with the nearest match
2191 	// to the position given.
2192 
2193 	epi::array_iterator_c it;
2194 	mobjtype_c* best;
2195 	mobjtype_c* m;
2196 
2197 	best = NULL;
2198 	for (it = GetTailIterator(); it.IsValid(); it--)
2199 	{
2200 		m = ITERATOR_TO_TYPE(it, mobjtype_c*);
2201 		if (m->castorder > 0)
2202 		{
2203 			if (m->castorder == castpos)	// Exact match
2204 				return m;
2205 
2206 			if (best)
2207 			{
2208 				if (m->castorder > castpos)
2209 				{
2210 					if (best->castorder > castpos)
2211 					{
2212 						int of1 = m->castorder - castpos;
2213 						int of2 = best->castorder - castpos;
2214 
2215 						if (of2 > of1)
2216 							best = m;
2217 					}
2218 					else
2219 					{
2220 						// Our previous was before the requested
2221 						// entry in the cast order, this is later and
2222 						// as such always better.
2223 						best = m;
2224 					}
2225 				}
2226 				else
2227 				{
2228 					// We only care about updating this if the
2229 					// best match was also prior to current
2230 					// entry. In this case we are looking for
2231 					// the first entry to wrap around to.
2232 					if (best->castorder < castpos)
2233 					{
2234 						int of1 = castpos - m->castorder;
2235 						int of2 = castpos - best->castorder;
2236 
2237 						if (of1 > of2)
2238 							best = m;
2239 					}
2240 				}
2241 			}
2242 			else
2243 			{
2244 				// We don't have a best item, so this has to be our best current match
2245 				best = m;
2246 			}
2247 		}
2248 	}
2249 
2250 	return best;
2251 }
2252 
2253 
LookupPlayer(int playernum)2254 const mobjtype_c* mobjtype_container_c::LookupPlayer(int playernum)
2255 {
2256 	// Find a player thing (needed by deathmatch code).
2257 
2258 	epi::array_iterator_c it;
2259 
2260 	for (it = GetTailIterator(); it.IsValid(); it--)
2261 	{
2262 		mobjtype_c *m = ITERATOR_TO_TYPE(it, mobjtype_c*);
2263 
2264 		if (m->playernum == playernum)
2265 			return m;
2266 	}
2267 
2268 	I_Error("Missing DDF entry for player number %d\n", playernum);
2269 	return NULL; /* NOT REACHED */
2270 }
2271 
2272 //--- editor settings ---
2273 // vi:ts=4:sw=4:noexpandtab
2274