g_misc_model.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793
  1. // leave this line at the top for all g_xxxx.cpp files...
  2. #include "g_headers.h"
  3. #include "g_local.h"
  4. #include "g_functions.h"
  5. #include "bg_public.h"
  6. extern cvar_t *g_spskill;
  7. //
  8. // Helper functions
  9. //
  10. //------------------------------------------------------------
  11. void SetMiscModelModels( char *modelNameString, gentity_t *ent, qboolean damage_model )
  12. {
  13. char damageModel[MAX_QPATH];
  14. char chunkModel[MAX_QPATH];
  15. int len;
  16. //Main model
  17. ent->s.modelindex = G_ModelIndex( modelNameString );
  18. if ( damage_model )
  19. {
  20. len = strlen( modelNameString ) - 4; // extract the extension
  21. //Dead/damaged model
  22. strncpy( damageModel, modelNameString, len );
  23. damageModel[len] = 0;
  24. strncpy( chunkModel, damageModel, sizeof(chunkModel));
  25. strcat( damageModel, "_d1.md3" );
  26. ent->s.modelindex2 = G_ModelIndex( damageModel );
  27. ent->spawnflags |= 4; // deadsolid
  28. //Chunk model
  29. strcat( chunkModel, "_c1.md3" );
  30. ent->s.modelindex3 = G_ModelIndex( chunkModel );
  31. }
  32. }
  33. //------------------------------------------------------------
  34. void SetMiscModelDefaults( gentity_t *ent, useFunc_t use_func, char *material, int solid_mask,int animFlag,
  35. qboolean take_damage, qboolean damage_model = qfalse )
  36. {
  37. // Apply damage and chunk models if they exist
  38. SetMiscModelModels( ent->model, ent, damage_model );
  39. ent->s.eFlags = animFlag;
  40. ent->svFlags |= SVF_PLAYER_USABLE;
  41. ent->contents = solid_mask;
  42. G_SetOrigin( ent, ent->s.origin );
  43. VectorCopy( ent->s.angles, ent->s.apos.trBase );
  44. gi.linkentity (ent);
  45. // Set a generic use function
  46. ent->e_UseFunc = use_func;
  47. /* if (use_func == useF_health_use)
  48. {
  49. G_SoundIndex("sound/player/suithealth.wav");
  50. }
  51. else if (use_func == useF_ammo_use )
  52. {
  53. G_SoundIndex("sound/player/suitenergy.wav");
  54. }
  55. */
  56. G_SpawnInt( "material", material, (int *)&ent->material );
  57. if (ent->health)
  58. {
  59. ent->max_health = ent->health;
  60. ent->takedamage = take_damage;
  61. ent->e_PainFunc = painF_misc_model_breakable_pain;
  62. ent->e_DieFunc = dieF_misc_model_breakable_die;
  63. }
  64. }
  65. void HealthStationSettings(gentity_t *ent)
  66. {
  67. G_SpawnInt( "count", "0", &ent->count );
  68. if (!ent->count)
  69. {
  70. switch (g_spskill->integer)
  71. {
  72. case 0: // EASY
  73. ent->count = 100;
  74. break;
  75. case 1: // MEDIUM
  76. ent->count = 75;
  77. break;
  78. default :
  79. case 2: // HARD
  80. ent->count = 50;
  81. break;
  82. }
  83. }
  84. }
  85. void CrystalAmmoSettings(gentity_t *ent)
  86. {
  87. G_SpawnInt( "count", "0", &ent->count );
  88. if (!ent->count)
  89. {
  90. switch (g_spskill->integer)
  91. {
  92. case 0: // EASY
  93. ent->count = 75;
  94. break;
  95. case 1: // MEDIUM
  96. ent->count = 75;
  97. break;
  98. default :
  99. case 2: // HARD
  100. ent->count = 75;
  101. break;
  102. }
  103. }
  104. }
  105. //------------------------------------------------------------
  106. //------------------------------------------------------------
  107. /*QUAKED misc_model_ghoul (1 0 0) (-16 -16 -37) (16 16 32)
  108. "model" arbitrary .glm file to display
  109. "health" - how much health the model has - default 60 (zero makes non-breakable)
  110. */
  111. //------------------------------------------------------------
  112. #include "anims.h"
  113. extern int G_ParseAnimFileSet( const char *skeletonName, const char *modelName=0);
  114. int temp_animFileIndex;
  115. void set_MiscAnim( gentity_t *ent)
  116. {
  117. animation_t *animations = level.knownAnimFileSets[temp_animFileIndex].animations;
  118. if (ent->playerModel & 1)
  119. {
  120. int anim = BOTH_STAND3;
  121. float animSpeed = 50.0f / animations[anim].frameLerp;
  122. // yes, its the same animation, so work out where we are in the leg anim, and blend us
  123. gi.G2API_SetBoneAnim(&ent->ghoul2[0], "model_root", animations[anim].firstFrame,
  124. (animations[anim].numFrames -1 )+ animations[anim].firstFrame,
  125. BONE_ANIM_OVERRIDE_FREEZE | BONE_ANIM_BLEND , animSpeed, (cg.time?cg.time:level.time), -1, 350);
  126. }
  127. else
  128. {
  129. int anim = BOTH_PAIN3;
  130. float animSpeed = 50.0f / animations[anim].frameLerp;
  131. gi.G2API_SetBoneAnim(&ent->ghoul2[0], "model_root", animations[anim].firstFrame,
  132. (animations[anim].numFrames -1 )+ animations[anim].firstFrame,
  133. BONE_ANIM_OVERRIDE_FREEZE | BONE_ANIM_BLEND, animSpeed, (cg.time?cg.time:level.time), -1, 350);
  134. }
  135. ent->nextthink = level.time + 900;
  136. ent->playerModel++;
  137. }
  138. void SP_misc_model_ghoul( gentity_t *ent )
  139. {
  140. #if 1
  141. ent->s.modelindex = G_ModelIndex( ent->model );
  142. gi.G2API_InitGhoul2Model(ent->ghoul2, ent->model, ent->s.modelindex);
  143. ent->s.radius = 50;
  144. #else
  145. char name1[200] = "models/players/kyle/model.glm";
  146. ent->s.modelindex = G_ModelIndex( name1 );
  147. gi.G2API_InitGhoul2Model(ent->ghoul2, name1, ent->s.modelindex);
  148. ent->s.radius = 150;
  149. // we found the model ok - load it's animation config
  150. temp_animFileIndex = G_ParseAnimFileSet("_humanoid", "kyle");
  151. if ( temp_animFileIndex<0 )
  152. {
  153. Com_Printf( S_COLOR_RED"Failed to load animation file set models/players/jedi/animation.cfg\n");
  154. }
  155. ent->s.angles[0] = 0;
  156. ent->s.angles[1] = 90;
  157. ent->s.angles[2] = 0;
  158. ent->s.origin[2] = 20;
  159. ent->s.origin[1] = 80;
  160. // ent->s.modelScale[0] = ent->s.modelScale[1] = ent->s.modelScale[2] = 0.8f;
  161. VectorSet (ent->mins, -16, -16, -37);
  162. VectorSet (ent->maxs, 16, 16, 32);
  163. //#if _DEBUG
  164. //loadsavecrash
  165. // VectorCopy(ent->mins, ent->s.mins);
  166. // VectorCopy(ent->maxs, ent->s.maxs);
  167. //#endif
  168. ent->contents = CONTENTS_BODY;
  169. ent->clipmask = MASK_NPCSOLID;
  170. G_SetOrigin( ent, ent->s.origin );
  171. VectorCopy( ent->s.angles, ent->s.apos.trBase );
  172. ent->health = 1000;
  173. // ent->s.modelindex = G_ModelIndex( "models/weapons2/blaster_r/g2blaster_w.glm" );
  174. // gi.G2API_InitGhoul2Model(ent->ghoul2, "models/weapons2/blaster_r/g2blaster_w.glm", ent->s.modelindex);
  175. // gi.G2API_AddBolt(&ent->ghoul2[0], "*weapon");
  176. // gi.G2API_AttachG2Model(&ent->ghoul2[1],&ent->ghoul2[0], 0, 0);
  177. gi.linkentity (ent);
  178. animation_t *animations = level.knownAnimFileSets[temp_animFileIndex].animations;
  179. int anim = BOTH_STAND3;
  180. float animSpeed = 50.0f / animations[anim].frameLerp;
  181. gi.G2API_SetBoneAnim(&ent->ghoul2[0], "model_root", animations[anim].firstFrame,
  182. (animations[anim].numFrames -1 )+ animations[anim].firstFrame,
  183. BONE_ANIM_OVERRIDE_FREEZE , animSpeed, cg.time);
  184. // int test = gi.G2API_GetSurfaceRenderStatus(&ent->ghoul2[0], "l_hand");
  185. // gi.G2API_SetSurfaceOnOff(&ent->ghoul2[0], "l_arm",0x00000100);
  186. // test = gi.G2API_GetSurfaceRenderStatus(&ent->ghoul2[0], "l_hand");
  187. // gi.G2API_SetNewOrigin(&ent->ghoul2[0], gi.G2API_AddBolt(&ent->ghoul2[0], "rhang_tag_bone"));
  188. // ent->s.apos.trDelta[1] = 10;
  189. // ent->s.apos.trType = TR_LINEAR;
  190. ent->nextthink = level.time + 1000;
  191. ent->e_ThinkFunc = thinkF_set_MiscAnim;
  192. #endif
  193. }
  194. #define RACK_BLASTER 1
  195. #define RACK_REPEATER 2
  196. #define RACK_ROCKET 4
  197. /*QUAKED misc_model_gun_rack (1 0 0.25) (-14 -14 -4) (14 14 30) BLASTER REPEATER ROCKET
  198. model="models/map_objects/kejim/weaponsrack.md3"
  199. NOTE: can mix and match these spawnflags to get multi-weapon racks. If only one type is checked the rack will be full of those weapons
  200. BLASTER - Puts one or more blaster guns on the rack.
  201. REPEATER - Puts one or more repeater guns on the rack.
  202. ROCKET - Puts one or more rocket launchers on the rack.
  203. */
  204. void GunRackAddItem( gitem_t *gun, vec3_t org, vec3_t angs, float ffwd, float fright, float fup )
  205. {
  206. vec3_t fwd, right;
  207. gentity_t *it_ent = G_Spawn();
  208. qboolean rotate = qtrue;
  209. AngleVectors( angs, fwd, right, NULL );
  210. if ( it_ent && gun )
  211. {
  212. // FIXME: scaling the ammo will probably need to be tweaked to a reasonable amount...adjust as needed
  213. // Set base ammo per type
  214. if ( gun->giType == IT_WEAPON )
  215. {
  216. it_ent->spawnflags |= 16;// VERTICAL
  217. switch( gun->giTag )
  218. {
  219. case WP_BLASTER:
  220. it_ent->count = 15;
  221. break;
  222. case WP_REPEATER:
  223. it_ent->count = 100;
  224. break;
  225. case WP_ROCKET_LAUNCHER:
  226. it_ent->count = 4;
  227. break;
  228. }
  229. }
  230. else
  231. {
  232. rotate = qfalse;
  233. // must deliberately make it small, or else the objects will spawn inside of each other.
  234. VectorSet( it_ent->maxs, 6.75f, 6.75f, 6.75f );
  235. VectorScale( it_ent->maxs, -1, it_ent->mins );
  236. }
  237. it_ent->spawnflags |= 1;// ITMSF_SUSPEND
  238. it_ent->classname = G_NewString(gun->classname); //copy it so it can be freed safely
  239. G_SpawnItem( it_ent, gun );
  240. // FinishSpawningItem handles everything, so clear the thinkFunc that was set in G_SpawnItem
  241. FinishSpawningItem( it_ent );
  242. if ( gun->giType == IT_AMMO )
  243. {
  244. if ( gun->giTag == AMMO_BLASTER ) // I guess this just has to use different logic??
  245. {
  246. if ( g_spskill->integer >= 2 )
  247. {
  248. it_ent->count += 10; // give more on higher difficulty because there will be more/harder enemies?
  249. }
  250. }
  251. else
  252. {
  253. // scale ammo based on skill
  254. switch ( g_spskill->integer )
  255. {
  256. case 0: // do default
  257. break;
  258. case 1:
  259. it_ent->count *= 0.75f;
  260. break;
  261. case 2:
  262. it_ent->count *= 0.5f;
  263. break;
  264. }
  265. }
  266. }
  267. it_ent->nextthink = 0;
  268. VectorCopy( org, it_ent->s.origin );
  269. VectorMA( it_ent->s.origin, fright, right, it_ent->s.origin );
  270. VectorMA( it_ent->s.origin, ffwd, fwd, it_ent->s.origin );
  271. it_ent->s.origin[2] += fup;
  272. VectorCopy( angs, it_ent->s.angles );
  273. // by doing this, we can force the amount of ammo we desire onto the weapon for when it gets picked-up
  274. it_ent->flags |= ( FL_DROPPED_ITEM | FL_FORCE_PULLABLE_ONLY );
  275. it_ent->physicsBounce = 0.1f;
  276. for ( int t = 0; t < 3; t++ )
  277. {
  278. if ( rotate )
  279. {
  280. if ( t == YAW )
  281. {
  282. it_ent->s.angles[t] = AngleNormalize180( it_ent->s.angles[t] + 180 + crandom() * 14 );
  283. }
  284. else
  285. {
  286. it_ent->s.angles[t] = AngleNormalize180( it_ent->s.angles[t] + crandom() * 4 );
  287. }
  288. }
  289. else
  290. {
  291. if ( t == YAW )
  292. {
  293. it_ent->s.angles[t] = AngleNormalize180( it_ent->s.angles[t] + 90 + crandom() * 4 );
  294. }
  295. }
  296. }
  297. G_SetAngles( it_ent, it_ent->s.angles );
  298. G_SetOrigin( it_ent, it_ent->s.origin );
  299. gi.linkentity( it_ent );
  300. }
  301. }
  302. //---------------------------------------------
  303. void SP_misc_model_gun_rack( gentity_t *ent )
  304. {
  305. gitem_t *blaster = NULL, *repeater = NULL, *rocket = NULL;
  306. int ct = 0;
  307. float ofz[3];
  308. gitem_t *itemList[3];
  309. // If BLASTER is checked...or nothing is checked then we'll do blasters
  310. if (( ent->spawnflags & RACK_BLASTER ) || !(ent->spawnflags & ( RACK_BLASTER | RACK_REPEATER | RACK_ROCKET )))
  311. {
  312. blaster = FindItemForWeapon( WP_BLASTER );
  313. }
  314. if (( ent->spawnflags & RACK_REPEATER ))
  315. {
  316. repeater = FindItemForWeapon( WP_REPEATER );
  317. }
  318. if (( ent->spawnflags & RACK_ROCKET ))
  319. {
  320. rocket = FindItemForWeapon( WP_ROCKET_LAUNCHER );
  321. }
  322. //---------weapon types
  323. if ( blaster )
  324. {
  325. ofz[ct] = 23.0f;
  326. itemList[ct++] = blaster;
  327. }
  328. if ( repeater )
  329. {
  330. ofz[ct] = 24.5f;
  331. itemList[ct++] = repeater;
  332. }
  333. if ( rocket )
  334. {
  335. ofz[ct] = 25.5f;
  336. itemList[ct++] = rocket;
  337. }
  338. if ( ct ) //..should always have at least one item on their, but just being safe
  339. {
  340. for ( ; ct < 3 ; ct++ )
  341. {
  342. ofz[ct] = ofz[0];
  343. itemList[ct] = itemList[0]; // first weapon ALWAYS propagates to fill up the shelf
  344. }
  345. }
  346. // now actually add the items to the shelf...validate that we have a list to add
  347. if ( ct )
  348. {
  349. for ( int i = 0; i < ct; i++ )
  350. {
  351. GunRackAddItem( itemList[i], ent->s.origin, ent->s.angles, crandom() * 2, ( i - 1 ) * 9 + crandom() * 2, ofz[i] );
  352. }
  353. }
  354. ent->s.modelindex = G_ModelIndex( "models/map_objects/kejim/weaponsrack.md3" );
  355. G_SetOrigin( ent, ent->s.origin );
  356. G_SetAngles( ent, ent->s.angles );
  357. ent->contents = CONTENTS_SOLID;
  358. gi.linkentity( ent );
  359. }
  360. #define RACK_METAL_BOLTS 2
  361. #define RACK_ROCKETS 4
  362. #define RACK_WEAPONS 8
  363. #define RACK_HEALTH 16
  364. #define RACK_PWR_CELL 32
  365. #define RACK_NO_FILL 64
  366. /*QUAKED misc_model_ammo_rack (1 0 0.25) (-14 -14 -4) (14 14 30) BLASTER METAL_BOLTS ROCKETS WEAPON HEALTH PWR_CELL NO_FILL
  367. model="models/map_objects/kejim/weaponsrung.md3"
  368. NOTE: can mix and match these spawnflags to get multi-ammo racks. If only one type is checked the rack will be full of that ammo. Only three ammo packs max can be displayed.
  369. BLASTER - Adds one or more ammo packs that are compatible with Blasters and the Bryar pistol.
  370. METAL_BOLTS - Adds one or more metal bolt ammo packs that are compatible with the heavy repeater and the flechette gun
  371. ROCKETS - Puts one or more rocket packs on a rack.
  372. WEAPON - adds a weapon matching a selected ammo type to the rack.
  373. HEALTH - adds a health pack to the top shelf of the ammo rack
  374. PWR_CELL - Adds one or more power cell packs that are compatible with the Disuptor, bowcaster, and demp2
  375. NO_FILL - Only puts selected ammo on the rack, it never fills up all three slots if only one or two items were checked
  376. */
  377. extern gitem_t *FindItemForAmmo( ammo_t ammo );
  378. //---------------------------------------------
  379. void SP_misc_model_ammo_rack( gentity_t *ent )
  380. {
  381. // If BLASTER is checked...or nothing is checked then we'll do blasters
  382. if (( ent->spawnflags & RACK_BLASTER ) || !(ent->spawnflags & ( RACK_BLASTER | RACK_METAL_BOLTS | RACK_ROCKETS | RACK_PWR_CELL )))
  383. {
  384. if ( ent->spawnflags & RACK_WEAPONS )
  385. {
  386. RegisterItem( FindItemForWeapon( WP_BLASTER ));
  387. }
  388. RegisterItem( FindItemForAmmo( AMMO_BLASTER ));
  389. }
  390. if (( ent->spawnflags & RACK_METAL_BOLTS ))
  391. {
  392. if ( ent->spawnflags & RACK_WEAPONS )
  393. {
  394. RegisterItem( FindItemForWeapon( WP_REPEATER ));
  395. }
  396. RegisterItem( FindItemForAmmo( AMMO_METAL_BOLTS ));
  397. }
  398. if (( ent->spawnflags & RACK_ROCKETS ))
  399. {
  400. if ( ent->spawnflags & RACK_WEAPONS )
  401. {
  402. RegisterItem( FindItemForWeapon( WP_ROCKET_LAUNCHER ));
  403. }
  404. RegisterItem( FindItemForAmmo( AMMO_ROCKETS ));
  405. }
  406. if (( ent->spawnflags & RACK_PWR_CELL ))
  407. {
  408. RegisterItem( FindItemForAmmo( AMMO_POWERCELL ));
  409. }
  410. if (( ent->spawnflags & RACK_HEALTH ))
  411. {
  412. RegisterItem( FindItem( "item_medpak_instant" ));
  413. }
  414. ent->e_ThinkFunc = thinkF_spawn_rack_goods;
  415. ent->nextthink = level.time + 100;
  416. G_SetOrigin( ent, ent->s.origin );
  417. G_SetAngles( ent, ent->s.angles );
  418. ent->contents = CONTENTS_SHOTCLIP|CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP;//CONTENTS_SOLID;//so use traces can go through them
  419. gi.linkentity( ent );
  420. }
  421. // AMMO RACK!!
  422. void spawn_rack_goods( gentity_t *ent )
  423. {
  424. float v_off = 0;
  425. gitem_t *blaster = NULL, *metal_bolts = NULL, *rockets = NULL, *it = NULL;
  426. gitem_t *am_blaster = NULL, *am_metal_bolts = NULL, *am_rockets = NULL, *am_pwr_cell = NULL;
  427. gitem_t *health = NULL;
  428. int pos = 0, ct = 0;
  429. gitem_t *itemList[4]; // allocating 4, but we only use 3. done so I don't have to validate that the array isn't full before I add another
  430. gi.unlinkentity( ent );
  431. // If BLASTER is checked...or nothing is checked then we'll do blasters
  432. if (( ent->spawnflags & RACK_BLASTER ) || !(ent->spawnflags & ( RACK_BLASTER | RACK_METAL_BOLTS | RACK_ROCKETS | RACK_PWR_CELL )))
  433. {
  434. if ( ent->spawnflags & RACK_WEAPONS )
  435. {
  436. blaster = FindItemForWeapon( WP_BLASTER );
  437. }
  438. am_blaster = FindItemForAmmo( AMMO_BLASTER );
  439. }
  440. if (( ent->spawnflags & RACK_METAL_BOLTS ))
  441. {
  442. if ( ent->spawnflags & RACK_WEAPONS )
  443. {
  444. metal_bolts = FindItemForWeapon( WP_REPEATER );
  445. }
  446. am_metal_bolts = FindItemForAmmo( AMMO_METAL_BOLTS );
  447. }
  448. if (( ent->spawnflags & RACK_ROCKETS ))
  449. {
  450. if ( ent->spawnflags & RACK_WEAPONS )
  451. {
  452. rockets = FindItemForWeapon( WP_ROCKET_LAUNCHER );
  453. }
  454. am_rockets = FindItemForAmmo( AMMO_ROCKETS );
  455. }
  456. if (( ent->spawnflags & RACK_PWR_CELL ))
  457. {
  458. am_pwr_cell = FindItemForAmmo( AMMO_POWERCELL );
  459. }
  460. if (( ent->spawnflags & RACK_HEALTH ))
  461. {
  462. health = FindItem( "item_medpak_instant" );
  463. RegisterItem( health );
  464. }
  465. //---------Ammo types
  466. if ( am_blaster )
  467. {
  468. itemList[ct++] = am_blaster;
  469. }
  470. if ( am_metal_bolts )
  471. {
  472. itemList[ct++] = am_metal_bolts;
  473. }
  474. if ( am_pwr_cell )
  475. {
  476. itemList[ct++] = am_pwr_cell;
  477. }
  478. if ( am_rockets )
  479. {
  480. itemList[ct++] = am_rockets;
  481. }
  482. if ( !(ent->spawnflags & RACK_NO_FILL) && ct ) //double negative..should always have at least one item on there, but just being safe
  483. {
  484. for ( ; ct < 3 ; ct++ )
  485. {
  486. itemList[ct] = itemList[0]; // first item ALWAYS propagates to fill up the shelf
  487. }
  488. }
  489. // now actually add the items to the shelf...validate that we have a list to add
  490. if ( ct )
  491. {
  492. for ( int i = 0; i < ct; i++ )
  493. {
  494. GunRackAddItem( itemList[i], ent->s.origin, ent->s.angles, crandom() * 0.5f, (i-1)* 8, 7.0f );
  495. }
  496. }
  497. // -----Weapon option
  498. if ( ent->spawnflags & RACK_WEAPONS )
  499. {
  500. if ( !(ent->spawnflags & ( RACK_BLASTER | RACK_METAL_BOLTS | RACK_ROCKETS | RACK_PWR_CELL )))
  501. {
  502. // nothing was selected, so we assume blaster pack
  503. it = blaster;
  504. }
  505. else
  506. {
  507. // if weapon is checked...and so are one or more ammo types, then pick a random weapon to display..always give weaker weapons first
  508. if ( blaster )
  509. {
  510. it = blaster;
  511. v_off = 25.5f;
  512. }
  513. else if ( metal_bolts )
  514. {
  515. it = metal_bolts;
  516. v_off = 27.0f;
  517. }
  518. else if ( rockets )
  519. {
  520. it = rockets;
  521. v_off = 28.0f;
  522. }
  523. }
  524. if ( it )
  525. {
  526. // since we may have to put up a health pack on the shelf, we should know where we randomly put
  527. // the gun so we don't put the pack on the same spot..so pick either the left or right side
  528. pos = ( random() > .5 ) ? -1 : 1;
  529. GunRackAddItem( it, ent->s.origin, ent->s.angles, crandom() * 2, ( random() * 6 + 4 ) * pos, v_off );
  530. }
  531. }
  532. // ------Medpack
  533. if (( ent->spawnflags & RACK_HEALTH ) && health )
  534. {
  535. if ( !pos )
  536. {
  537. // we haven't picked a side already...
  538. pos = ( random() > .5 ) ? -1 : 1;
  539. }
  540. else
  541. {
  542. // switch to the opposite side
  543. pos *= -1;
  544. }
  545. GunRackAddItem( health, ent->s.origin, ent->s.angles, crandom() * 0.5f, ( random() * 4 + 4 ) * pos, 24 );
  546. }
  547. ent->s.modelindex = G_ModelIndex( "models/map_objects/kejim/weaponsrung.md3" );
  548. G_SetOrigin( ent, ent->s.origin );
  549. G_SetAngles( ent, ent->s.angles );
  550. gi.linkentity( ent );
  551. }
  552. #define DROP_MEDPACK 1
  553. #define DROP_SHIELDS 2
  554. #define DROP_BACTA 4
  555. #define DROP_BATTERIES 8
  556. /*QUAKED misc_model_cargo_small (1 0 0.25) (-14 -14 -4) (14 14 30) MEDPACK SHIELDS BACTA BATTERIES
  557. model="models/map_objects/kejim/cargo_small.md3"
  558. Cargo crate that can only be destroyed by heavy class weapons ( turrets, emplaced guns, at-st ) Can spawn useful things when it breaks
  559. MEDPACK - instant use medpacks
  560. SHIELDS - instant shields
  561. BACTA - bacta tanks
  562. BATTERIES -
  563. "health" - how much damage to take before blowing up ( default 25 )
  564. "splashRadius" - damage range when it explodes ( default 96 )
  565. "splashDamage" - damage to do within explode range ( default 1 )
  566. */
  567. extern gentity_t *LaunchItem( gitem_t *item, const vec3_t origin, const vec3_t velocity, char *target );
  568. void misc_model_cargo_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod, int dFlags, int hitLoc )
  569. {
  570. int flags;
  571. vec3_t org, temp;
  572. gitem_t *health = NULL, *shields = NULL, *bacta = NULL, *batteries = NULL;
  573. // copy these for later
  574. flags = self->spawnflags;
  575. VectorCopy( self->currentOrigin, org );
  576. // we already had spawn flags, but we don't care what they were...we just need to set up the flags we want for misc_model_breakable_die
  577. self->spawnflags = 8; // NO_DMODEL
  578. // pass through to get the effects and such
  579. misc_model_breakable_die( self, inflictor, attacker, damage, mod );
  580. // now that the model is broken, we can safely spawn these in it's place without them being in solid
  581. temp[2] = org[2] + 16;
  582. // annoying, but spawn each thing in its own little quadrant so that they don't end up on top of each other
  583. if (( flags & DROP_MEDPACK ))
  584. {
  585. health = FindItem( "item_medpak_instant" );
  586. if ( health )
  587. {
  588. temp[0] = org[0] + crandom() * 8 + 16;
  589. temp[1] = org[1] + crandom() * 8 + 16;
  590. LaunchItem( health, temp, vec3_origin, NULL );
  591. }
  592. }
  593. if (( flags & DROP_SHIELDS ))
  594. {
  595. shields = FindItem( "item_shield_sm_instant" );
  596. if ( shields )
  597. {
  598. temp[0] = org[0] + crandom() * 8 - 16;
  599. temp[1] = org[1] + crandom() * 8 + 16;
  600. LaunchItem( shields, temp, vec3_origin, NULL );
  601. }
  602. }
  603. if (( flags & DROP_BACTA ))
  604. {
  605. bacta = FindItem( "item_bacta" );
  606. if ( bacta )
  607. {
  608. temp[0] = org[0] + crandom() * 8 - 16;
  609. temp[1] = org[1] + crandom() * 8 - 16;
  610. LaunchItem( bacta, temp, vec3_origin, NULL );
  611. }
  612. }
  613. if (( flags & DROP_BATTERIES ))
  614. {
  615. batteries = FindItem( "item_battery" );
  616. if ( batteries )
  617. {
  618. temp[0] = org[0] + crandom() * 8 + 16;
  619. temp[1] = org[1] + crandom() * 8 - 16;
  620. LaunchItem( batteries, temp, vec3_origin, NULL );
  621. }
  622. }
  623. }
  624. //---------------------------------------------
  625. void SP_misc_model_cargo_small( gentity_t *ent )
  626. {
  627. G_SpawnInt( "splashRadius", "96", &ent->splashRadius );
  628. G_SpawnInt( "splashDamage", "1", &ent->splashDamage );
  629. if (( ent->spawnflags & DROP_MEDPACK ))
  630. {
  631. RegisterItem( FindItem( "item_medpak_instant" ));
  632. }
  633. if (( ent->spawnflags & DROP_SHIELDS ))
  634. {
  635. RegisterItem( FindItem( "item_shield_sm_instant" ));
  636. }
  637. if (( ent->spawnflags & DROP_BACTA ))
  638. {
  639. // RegisterItem( FindItem( "item_bacta" ));
  640. }
  641. if (( ent->spawnflags & DROP_BATTERIES ))
  642. {
  643. RegisterItem( FindItem( "item_battery" ));
  644. }
  645. G_SpawnInt( "health", "25", &ent->health );
  646. SetMiscModelDefaults( ent, useF_NULL, "11", CONTENTS_SOLID|CONTENTS_OPAQUE|CONTENTS_BODY|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP, NULL, qtrue, NULL );
  647. ent->s.modelindex2 = G_ModelIndex("/models/map_objects/kejim/cargo_small.md3"); // Precache model
  648. // we only take damage from a heavy weapon class missile
  649. ent->flags |= FL_DMG_BY_HEAVY_WEAP_ONLY;
  650. ent->e_DieFunc = dieF_misc_model_cargo_die;
  651. ent->radius = 1.5f; // scale number of chunks spawned
  652. }