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