1 #include "Inventory_Choosing.h"
2 #include "Animation_Data.h"
3 #include "Items.h"
4 #include "Random.h"
5 #include "Weapons.h"
6 #include "Strategic_Status.h"
7 #include "Campaign.h"
8 #include "GameSettings.h"
9 #include "StrategicMap.h"
10 #include "Auto_Resolve.h"
11 #include "Map_Screen_Interface_Map.h"
12 #include "Debug.h"
13
14 #include "AmmoTypeModel.h"
15 #include "ContentManager.h"
16 #include "GameInstance.h"
17 #include "MagazineModel.h"
18 #include "WeaponModels.h"
19 #include "policy/GamePolicy.h"
20
21 #define ENEMYAMMODROPRATE 50 // % of time enemies drop ammunition
22 #define ENEMYGRENADEDROPRATE 25 // % of time enemies drop grenades
23 #define ENEMYEQUIPDROPRATE 15 // % of stuff enemies drop equipment
24
25 // only 1/10th of what enemies drop...
26 #define MILITIAAMMODROPRATE 5 // % of time enemies drop ammunition
27 #define MILITIAGRENADEDROPRATE 3 // % of time enemies drop grenades
28 #define MILITIAEQUIPDROPRATE 2 // % of stuff enemies drop equipment
29
30 #define MAX_MORTARS_PER_TEAM 1 // one team can't randomly roll more than this many mortars per sector
31
32
33 UINT32 guiMortarsRolledByTeam = 0;
34
35
36 static void MarkAllWeaponsOfSameGunClassAsDropped(UINT16 usWeapon);
37
38
InitArmyGunTypes(void)39 void InitArmyGunTypes(void)
40 {
41 UINT8 ubWeapon;
42
43 // set all flags that track whether this weapon type has been dropped before to FALSE
44 for (ubWeapon = 0; ubWeapon < MAX_WEAPONS; ubWeapon++)
45 {
46 gStrategicStatus.fWeaponDroppedAlready[ubWeapon] = FALSE;
47 }
48
49 // avoid auto-drops for the gun class with the crappiest guns in it
50 MarkAllWeaponsOfSameGunClassAsDropped( SW38 );
51 }
52
53
GetWeaponClass(UINT16 usGun)54 static INT8 GetWeaponClass(UINT16 usGun)
55 {
56 const std::vector<std::vector<const WeaponModel*> > & gunChoice = GCM->getExtendedGunChoice();
57 UINT32 uiGunLevel, uiLoop;
58
59 // always use the extended list since it contains all guns...
60 for (uiGunLevel = 0; uiGunLevel < gunChoice.size(); uiGunLevel++)
61 {
62 for ( uiLoop = 0; uiLoop < gunChoice[uiGunLevel].size(); uiLoop++ )
63 {
64 if ( gunChoice[uiGunLevel][uiLoop]->getItemIndex() == usGun )
65 {
66 return( (INT8) uiGunLevel );
67 }
68 }
69 }
70 return( -1 );
71 }
72
73
MarkAllWeaponsOfSameGunClassAsDropped(UINT16 usWeapon)74 static void MarkAllWeaponsOfSameGunClassAsDropped(UINT16 usWeapon)
75 {
76 INT8 bGunClass;
77 UINT32 uiLoop;
78
79
80 // mark that item itself as dropped, whether or not it's part of a gun class
81 gStrategicStatus.fWeaponDroppedAlready[ usWeapon ] = TRUE;
82
83 bGunClass = GetWeaponClass( usWeapon );
84
85 // if the gun belongs to a gun class (mortars, GLs, LAWs, etc. do not and are handled independently)
86 const std::vector<std::vector<const WeaponModel*> > & gunChoice = GCM->getExtendedGunChoice();
87
88 if ( bGunClass != -1 )
89 {
90 // then mark EVERY gun in that class as dropped
91 for ( uiLoop = 0; uiLoop < gunChoice[bGunClass].size(); uiLoop++ )
92 {
93 gStrategicStatus.fWeaponDroppedAlready[gunChoice[bGunClass][uiLoop]->getItemIndex()] = TRUE;
94 }
95 }
96 }
97
98
99 static void ChooseArmourForSoldierCreateStruct(SOLDIERCREATE_STRUCT* pp, INT8 bHelmetClass, INT8 bVestClass, INT8 bLeggingsClass);
100 static void ChooseBombsForSoldierCreateStruct(SOLDIERCREATE_STRUCT* pp, INT8 bBombClass);
101 static void ChooseFaceGearForSoldierCreateStruct(SOLDIERCREATE_STRUCT* pp);
102 static void ChooseGrenadesForSoldierCreateStruct(SOLDIERCREATE_STRUCT* pp, INT8 bGrenades, INT8 bGrenadeClass, BOOLEAN fGrenadeLauncher);
103 static void ChooseKitsForSoldierCreateStruct(SOLDIERCREATE_STRUCT* pp, INT8 bKitClass);
104 static void ChooseLocationSpecificGearForSoldierCreateStruct(SOLDIERCREATE_STRUCT* pp);
105 static void ChooseMiscGearForSoldierCreateStruct(SOLDIERCREATE_STRUCT* pp, INT8 bMiscClass);
106 static void ChooseSpecialWeaponsForSoldierCreateStruct(SOLDIERCREATE_STRUCT* pp, INT8 bKnifeClass, BOOLEAN fGrenadeLauncher, BOOLEAN fLAW, BOOLEAN fMortar);
107 static void ChooseWeaponForSoldierCreateStruct(SOLDIERCREATE_STRUCT* pp, INT8 bWeaponClass, INT8 bAmmoClips, INT8 bAttachClass, BOOLEAN fAttachment);
108 static void EquipTank(SOLDIERCREATE_STRUCT* pp);
109 static void RandomlyChooseWhichItemsAreDroppable(SOLDIERCREATE_STRUCT* pp, INT8 bSoldierClass);
110
111
112 //Chooses equipment based on the relative equipment level (0-4) with best being 4. It allocates a range
113 //of equipment to choose from.
114 //NOTE: I'm just winging it for the decisions on which items that different groups can have. Basically,
115 // there are variations, so a guy at a certain level may get a better gun and worse armour or vice versa.
GenerateRandomEquipment(SOLDIERCREATE_STRUCT * pp,INT8 bSoldierClass,INT8 bEquipmentRating)116 void GenerateRandomEquipment( SOLDIERCREATE_STRUCT *pp, INT8 bSoldierClass, INT8 bEquipmentRating )
117 {
118 OBJECTTYPE *pItem;
119 //general rating information
120 INT8 bRating = 0;
121 //numbers of items
122 INT8 bAmmoClips = 0;
123 INT8 bGrenades = 0;
124 BOOLEAN fAttachment = FALSE;
125 //item levels
126 INT8 bWeaponClass = 0;
127 INT8 bHelmetClass = 0;
128 INT8 bVestClass = 0;
129 INT8 bLeggingClass = 0;
130 INT8 bAttachClass = 0;
131 INT8 bGrenadeClass = 0;
132 INT8 bKnifeClass = 0;
133 INT8 bKitClass = 0;
134 INT8 bMiscClass = 0;
135 INT8 bBombClass = 0;
136 //special weapons
137 BOOLEAN fMortar = FALSE;
138 BOOLEAN fGrenadeLauncher = FALSE;
139 BOOLEAN fLAW = FALSE;
140 INT32 i;
141 INT8 bEquipmentModifier;
142 UINT8 ubMaxSpecialWeaponRoll;
143
144
145 Assert( pp );
146
147 // kids don't get anything 'cause they don't have any weapon animations and the rest is inappropriate
148 if ( ( pp->bBodyType == HATKIDCIV ) || ( pp->bBodyType == KIDCIV ) )
149 {
150 return;
151 }
152
153
154 if ( ( pp->bBodyType == TANK_NE ) || ( pp->bBodyType == TANK_NW ) )
155 {
156 EquipTank( pp );
157 return;
158 }
159
160
161 Assert( ( bSoldierClass >= SOLDIER_CLASS_NONE ) && ( bSoldierClass <= SOLDIER_CLASS_ELITE_MILITIA ) );
162 Assert( ( bEquipmentRating >= 0 ) && ( bEquipmentRating <= 4 ) );
163
164
165 // equipment level is modified by 1/10 of the difficulty percentage, -5, so it's between -5 to +5
166 // (on normal, this is actually -4 to +4, easy is -5 to +3, and hard is -3 to +5)
167 bEquipmentModifier = bEquipmentRating + ( ( CalcDifficultyModifier( bSoldierClass ) / 10 ) - 5 );
168
169 switch( bSoldierClass )
170 {
171 case SOLDIER_CLASS_NONE:
172 // ammo is here only so that civilians with pre-assigned ammo will get some clips for it!
173 bAmmoClips = (INT8)(1 + Random( 2 ));
174
175 // civilians get nothing, anyone who should get something should be preassigned by Linda
176 break;
177
178 case SOLDIER_CLASS_ADMINISTRATOR:
179 case SOLDIER_CLASS_GREEN_MILITIA:
180 bRating = BAD_ADMINISTRATOR_EQUIPMENT_RATING + bEquipmentModifier;
181 bRating = (INT8)MAX( MIN_EQUIPMENT_CLASS, MIN( MAX_EQUIPMENT_CLASS, bRating ) );
182
183 bWeaponClass = bRating;
184
185 //Note: in some cases the class of armour and/or helmet won't be high enough to make
186 // the lowest level.
187 bVestClass = bRating;
188 bHelmetClass = bRating;
189 // no leggings
190
191 if( Random( 2 ) )
192 bKnifeClass = bRating;
193
194 bAmmoClips = (INT8)(1 + Random( 2 ));
195
196 if( bRating >= GOOD_ADMINISTRATOR_EQUIPMENT_RATING )
197 {
198 bAmmoClips++;
199
200 bKitClass = bRating;
201 bMiscClass = bRating;
202 }
203
204 if( bRating >= GREAT_ADMINISTRATOR_EQUIPMENT_RATING )
205 {
206 bGrenades = 1, bGrenadeClass = bRating;
207 }
208
209 if( ( bRating > MIN_EQUIPMENT_CLASS ) && bRating < MAX_EQUIPMENT_CLASS )
210 {
211 //Randomly decide if there is to be an upgrade of guns vs armour (one goes up, the other down)
212 switch( Random( 5 ) )
213 {
214 case 0:
215 bWeaponClass++, bVestClass--;
216 break; //better gun, worse armour
217 case 1:
218 bWeaponClass--, bVestClass++;
219 break; //worse gun, better armour
220 }
221 }
222 break;
223
224 case SOLDIER_CLASS_ARMY:
225 case SOLDIER_CLASS_REG_MILITIA:
226 //army guys tend to have a broad range of equipment
227 bRating = BAD_ARMY_EQUIPMENT_RATING + bEquipmentModifier;
228 bRating = (INT8)MAX( MIN_EQUIPMENT_CLASS, MIN( MAX_EQUIPMENT_CLASS, bRating ) );
229
230 bWeaponClass = bRating;
231 bVestClass = bRating;
232 bHelmetClass = bRating;
233 bGrenadeClass = bRating;
234
235 if( ( bRating >= GOOD_ARMY_EQUIPMENT_RATING ) && ( Random( 100 ) < 33 ) )
236 {
237 fAttachment = TRUE;
238 bAttachClass = bRating;
239 }
240
241 bAmmoClips = (INT8)(2 + Random( 2 ));
242
243 if( bRating >= AVERAGE_ARMY_EQUIPMENT_RATING )
244 {
245 bGrenades = (INT8)Random( 2 );
246 bKitClass = bRating;
247 bMiscClass = bRating;
248 }
249
250 if( bRating >= GOOD_ARMY_EQUIPMENT_RATING )
251 {
252 bGrenades++;
253 }
254
255 if( bRating >= GREAT_ARMY_EQUIPMENT_RATING )
256 {
257 bGrenades++;
258
259 bLeggingClass = bRating;
260
261 if ( Chance( 25 ) )
262 {
263 bBombClass = bRating;
264 }
265 }
266
267 if( Random( 2 ) )
268 bKnifeClass = bRating;
269
270 if( ( bRating > MIN_EQUIPMENT_CLASS ) && bRating < MAX_EQUIPMENT_CLASS )
271 {
272 switch( Random( 7 ) )
273 {
274 case 3:
275 bWeaponClass++, bVestClass--;
276 break; //better gun, worse armour
277 case 4:
278 bWeaponClass--, bVestClass++;
279 break; //worse gun, better armour
280 case 5:
281 bVestClass++, bHelmetClass--;
282 break; //better armour, worse helmet
283 case 6:
284 bVestClass--, bHelmetClass++;
285 break; //worse armour, better helmet
286 }
287 }
288
289 // if well-enough equipped, 1/5 chance of something really special
290 if( ( bRating >= GREAT_ARMY_EQUIPMENT_RATING ) && ( Random( 100 ) < 20 ) )
291 {
292 //give this man a special weapon! No mortars if underground, however
293 ubMaxSpecialWeaponRoll = ( !IsAutoResolveActive() && ( gbWorldSectorZ != 0 ) ) ? 6 : 7;
294 switch ( Random ( ubMaxSpecialWeaponRoll ) )
295 {
296 case 0:
297 case 1:
298 case 2:
299 if ( pp->bExpLevel >= 3 )
300 {
301 //grenade launcher
302 fGrenadeLauncher = TRUE;
303 bGrenades = 3 + (INT8)(Random( 3 )); //3-5
304 }
305 break;
306
307 case 3:
308 case 4:
309 case 5:
310 if ( pp->bExpLevel >= 4 )
311 {
312 // LAW rocket launcher
313 fLAW = TRUE;
314 }
315 break;
316
317 case 6:
318 // one per team maximum!
319 if ( ( pp->bExpLevel >= 5 ) && ( guiMortarsRolledByTeam < MAX_MORTARS_PER_TEAM ) )
320 {
321 //mortar
322 fMortar = TRUE;
323 guiMortarsRolledByTeam++;
324
325 // the grenades will actually represent mortar shells in this case
326 bGrenades = 2 + (INT8)(Random( 3 )); //2-4
327 bGrenadeClass = MORTAR_GRENADE_CLASS;
328 }
329 break;
330 }
331 }
332 break;
333
334 case SOLDIER_CLASS_ELITE:
335 case SOLDIER_CLASS_ELITE_MILITIA:
336 bRating = BAD_ELITE_EQUIPMENT_RATING + bEquipmentModifier;
337 bRating = (INT8)MAX( MIN_EQUIPMENT_CLASS, MIN( MAX_EQUIPMENT_CLASS, bRating ) );
338
339 bWeaponClass = bRating;
340 bHelmetClass = bRating;
341 bVestClass = bRating;
342 bLeggingClass = bRating;
343 bAttachClass = bRating;
344 bGrenadeClass = bRating;
345 bKitClass = bRating;
346 bMiscClass = bRating;
347
348 if ( Chance( 25 ) )
349 {
350 bBombClass = bRating;
351 }
352
353 bAmmoClips = (INT8)(3 + Random( 2 ));
354 bGrenades = (INT8)(1 + Random( 3 ));
355
356 if( ( bRating >= AVERAGE_ELITE_EQUIPMENT_RATING ) && ( Random( 100 ) < 75 ) )
357 {
358 fAttachment = TRUE;
359 bAttachClass = bRating;
360 }
361
362 if( Random( 2 ) )
363 bKnifeClass = bRating;
364
365 if( ( bRating > MIN_EQUIPMENT_CLASS ) && bRating < MAX_EQUIPMENT_CLASS )
366 {
367 switch( Random( 11 ) )
368 {
369 case 4:
370 bWeaponClass++, bVestClass--;
371 break;
372 case 5:
373 bWeaponClass--, bVestClass--;
374 break;
375 case 6:
376 bVestClass++, bHelmetClass--;
377 break;
378 case 7:
379 bGrenades += 2;
380 break;
381 case 8:
382 bHelmetClass++;
383 break;
384 case 9:
385 bVestClass++;
386 break;
387 case 10:
388 bWeaponClass++;
389 break;
390 }
391 }
392
393 // if well-enough equipped, 1/3 chance of something really special
394 if( ( bRating >= GOOD_ELITE_EQUIPMENT_RATING ) && ( Random( 100 ) < 33 ) )
395 {
396 //give this man a special weapon! No mortars if underground, however
397 ubMaxSpecialWeaponRoll = ( !IsAutoResolveActive() && ( gbWorldSectorZ != 0 ) ) ? 6 : 7;
398 switch ( Random ( ubMaxSpecialWeaponRoll ) )
399 {
400 case 0:
401 case 1:
402 case 2:
403 //grenade launcher
404 fGrenadeLauncher = TRUE;
405 bGrenades = 4 + (INT8)(Random( 4 )); //4-7
406 break;
407 case 3:
408 case 4:
409 case 5:
410 // LAW rocket launcher
411 fLAW = TRUE;
412 break;
413 case 6:
414 // one per team maximum!
415 if ( guiMortarsRolledByTeam < MAX_MORTARS_PER_TEAM )
416 {
417 //mortar
418 fMortar = TRUE;
419 guiMortarsRolledByTeam++;
420
421 // the grenades will actually represent mortar shells in this case
422 bGrenades = 3 + (INT8)(Random( 5 )); //3-7
423 bGrenadeClass = MORTAR_GRENADE_CLASS;
424 }
425 break;
426 }
427 }
428 break;
429 }
430
431 for ( i = 0; i < NUM_INV_SLOTS; i++ )
432 {
433 //clear items, but only if they have write status.
434 if( !(pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE) )
435 {
436 pp->Inv[ i ] = OBJECTTYPE{};
437 pp->Inv[ i ].fFlags |= OBJECT_UNDROPPABLE;
438 }
439 else
440 {
441 //check to see what kind of item is here. If we find a gun, for example, it'll make the
442 //bWeaponClass negative to signify that a gun has already been specified, and later
443 //code will use that to determine that and to pick ammo for it.
444 pItem = &pp->Inv[ i ];
445 if( !pItem )
446 continue;
447 switch( GCM->getItem(pItem->usItem)->getItemClass() )
448 {
449 case IC_GUN:
450 if ( pItem->usItem != ROCKET_LAUNCHER )
451 {
452 bWeaponClass *= -1;
453 }
454 else // rocket launcher!
455 {
456 fLAW = FALSE;
457 }
458 break;
459 case IC_AMMO:
460 bAmmoClips = 0;
461 break;
462 case IC_BLADE:
463 case IC_THROWING_KNIFE:
464 bKnifeClass = 0;
465 break;
466 case IC_LAUNCHER:
467 fGrenadeLauncher = FALSE;
468 fMortar = FALSE;
469 break;
470 case IC_ARMOUR:
471 if( i == HELMETPOS )
472 bHelmetClass = 0;
473 else if( i == VESTPOS )
474 bVestClass = 0;
475 else if( i == LEGPOS )
476 bLeggingClass = 0;
477 break;
478 case IC_GRENADE:
479 bGrenades = 0;
480 bGrenadeClass = 0;
481 break;
482 case IC_MEDKIT:
483 case IC_KIT:
484 bKitClass = 0;
485 break;
486 case IC_MISC:
487 bMiscClass = 0;
488 break;
489 case IC_BOMB:
490 bBombClass = 0;
491 break;
492 }
493 }
494 }
495
496
497 // special: militia shouldn't drop bombs
498 if ( !( SOLDIER_CLASS_ENEMY( bSoldierClass ) ) )
499 {
500 bBombClass = 0;
501 }
502
503
504 //Now actually choose the equipment!
505 ChooseWeaponForSoldierCreateStruct( pp, bWeaponClass, bAmmoClips, bAttachClass, fAttachment );
506 ChooseGrenadesForSoldierCreateStruct( pp, bGrenades, bGrenadeClass, fGrenadeLauncher );
507 ChooseArmourForSoldierCreateStruct( pp, bHelmetClass, bVestClass, bLeggingClass );
508 ChooseSpecialWeaponsForSoldierCreateStruct( pp, bKnifeClass, fGrenadeLauncher, fLAW, fMortar );
509 ChooseFaceGearForSoldierCreateStruct( pp );
510 ChooseKitsForSoldierCreateStruct( pp, bKitClass );
511 ChooseMiscGearForSoldierCreateStruct( pp, bMiscClass );
512 ChooseBombsForSoldierCreateStruct( pp, bBombClass );
513 ChooseLocationSpecificGearForSoldierCreateStruct( pp );
514 RandomlyChooseWhichItemsAreDroppable( pp, bSoldierClass );
515 }
516
517
518 static BOOLEAN PlaceObjectInSoldierCreateStruct(SOLDIERCREATE_STRUCT* pp, OBJECTTYPE* pObject);
519 static UINT16 SelectStandardArmyGun(UINT8 uiGunLevel);
520
521
522 //When using the class values, they should all range from 0-11, 0 meaning that there will be no
523 //selection for that particular type of item, and 1-11 means to choose an item if possible. 1 is
524 //the worst class of item, while 11 is the best.
ChooseWeaponForSoldierCreateStruct(SOLDIERCREATE_STRUCT * pp,INT8 bWeaponClass,INT8 bAmmoClips,INT8 bAttachClass,BOOLEAN fAttachment)525 static void ChooseWeaponForSoldierCreateStruct(SOLDIERCREATE_STRUCT* pp, INT8 bWeaponClass, INT8 bAmmoClips, INT8 bAttachClass, BOOLEAN fAttachment)
526 {
527 OBJECTTYPE Object;
528 UINT16 i;
529 UINT16 usRandom;
530 UINT16 usNumMatches = 0;
531 UINT16 usGunIndex = 0;
532 UINT16 usAmmoIndex = 0;
533 UINT16 usAttachIndex = 0;
534 UINT8 ubChanceStandardAmmo;
535 INT8 bStatus;
536
537 // Choose weapon:
538 // WEAPONS are very important, and are therefore handled differently using special pre-generated tables.
539 // It was requested that enemies use only a small subset of guns with a lot duplication of the same gun type.
540
541 // if gun was pre-selected (rcvd negative weapon class) and needs ammo
542 if( bWeaponClass < 0 && bAmmoClips )
543 {
544 //Linda has added a specific gun to the merc's inventory, but no ammo. So, we
545 //will choose ammunition that works with the gun.
546 for( i = 0; i < NUM_INV_SLOTS; i++ )
547 {
548 if( GCM->getItem(pp->Inv[ i ].usItem)->getItemClass() == IC_GUN )
549 {
550 usGunIndex = pp->Inv[ i ].usItem;
551 ubChanceStandardAmmo = 100 - (bWeaponClass * -9); // weapon class is negative!
552 usAmmoIndex = RandomMagazine( usGunIndex, ubChanceStandardAmmo );
553
554 if ( usGunIndex == ROCKET_RIFLE )
555 {
556 pp->Inv[ i ].ubImprintID = (NO_PROFILE + 1);
557 }
558
559 break;
560 }
561 }
562 if( bAmmoClips && usAmmoIndex )
563 {
564 CreateItems( usAmmoIndex, 100, bAmmoClips, &Object );
565 Object.fFlags |= OBJECT_UNDROPPABLE;
566 PlaceObjectInSoldierCreateStruct( pp, &Object );
567 }
568
569 return;
570 }
571
572 if (bWeaponClass < 1)
573 return; //empty handed / pre-selected
574
575 // reduce anything over 11 to 11
576 if (bWeaponClass > ARMY_GUN_LEVELS)
577 {
578 bWeaponClass = ARMY_GUN_LEVELS;
579 }
580
581
582 // the weapon class here ranges from 1 to 11, so subtract 1 to get a gun level
583 usGunIndex = SelectStandardArmyGun( (UINT8) (bWeaponClass - 1));
584
585
586 if( bAmmoClips )
587 {
588 //We have a gun, so choose ammo clips
589
590 // check default ammo first...
591 usAmmoIndex = DefaultMagazine( usGunIndex );
592 switch( GCM->getItem(usAmmoIndex)->asAmmo()->ammoType->index )
593 {
594 case AMMO_AP:
595 case AMMO_SUPER_AP:
596 // assault rifle, rocket rifle (etc) - high chance of having AP ammo
597 ubChanceStandardAmmo = 80;
598 break;
599 default:
600 ubChanceStandardAmmo = 100 - (bWeaponClass * 9);
601 break;
602 }
603 usAmmoIndex = RandomMagazine( usGunIndex, ubChanceStandardAmmo );
604 }
605
606 //Choose attachment
607 if( bAttachClass && fAttachment )
608 {
609 usNumMatches = 0;
610 while( bAttachClass && !usNumMatches )
611 {
612 //Count the number of valid attachments.
613 for( i = 0; i < MAXITEMS; i++ )
614 {
615 const ItemModel * pItem = GCM->getItem(i);
616 if( pItem->getItemClass() == IC_MISC && pItem->getCoolness() == bAttachClass && ValidAttachment( i, usGunIndex ) )
617 usNumMatches++;
618 }
619 if( !usNumMatches )
620 {
621 bAttachClass--;
622 }
623 }
624 if( usNumMatches )
625 {
626 usRandom = (UINT16)Random( usNumMatches );
627 for( i = 0; i < MAXITEMS; i++ )
628 {
629 const ItemModel * pItem = GCM->getItem(i);
630 if( pItem->getItemClass() == IC_MISC && pItem->getCoolness() == bAttachClass && ValidAttachment( i, usGunIndex ) )
631 {
632 if( usRandom )
633 usRandom--;
634 else
635 {
636 usAttachIndex = i;
637 break;
638 }
639 }
640 }
641 }
642 }
643 //Now, we have chosen all of the correct items. Now, we will assign them into the slots.
644 //Because we are dealing with enemies, automatically give them full ammo in their weapon.
645 if( !(pp->Inv[ HANDPOS ].fFlags & OBJECT_NO_OVERWRITE) )
646 {
647 switch( pp->ubSoldierClass )
648 {
649 case SOLDIER_CLASS_ADMINISTRATOR:
650 case SOLDIER_CLASS_ARMY:
651 case SOLDIER_CLASS_GREEN_MILITIA:
652 case SOLDIER_CLASS_REG_MILITIA:
653 //Admins/Troops: 60-75% + 1% every 4% progress
654 bStatus = (INT8)(60 + Random( 16 ));
655 bStatus += (INT8)(HighestPlayerProgressPercentage() / 4);
656 bStatus = (INT8)MIN( 100, bStatus );
657 break;
658 case SOLDIER_CLASS_ELITE:
659 case SOLDIER_CLASS_ELITE_MILITIA:
660 //85-90% + 1% every 10% progress
661 bStatus = (INT8)(85 + Random( 6 ));
662 bStatus += (INT8)(HighestPlayerProgressPercentage() / 10);
663 bStatus = (INT8)MIN( 100, bStatus );
664 break;
665 default:
666 bStatus = (INT8)(50 + Random( 51 ) );
667 break;
668 }
669 // don't allow it to be lower than marksmanship, we don't want it to affect their chances of hitting
670 bStatus = (INT8)MAX( pp->bMarksmanship, bStatus );
671
672 bStatus = (INT8)MAX( bStatus, gamepolicy(enemy_weapon_minimal_status));
673
674 CreateItem( usGunIndex, bStatus, &(pp->Inv[ HANDPOS ]) );
675 pp->Inv[ HANDPOS ].fFlags |= OBJECT_UNDROPPABLE;
676
677 // Rocket Rifles must come pre-imprinted, in case carrier gets killed without getting a shot off
678 if ( usGunIndex == ROCKET_RIFLE )
679 {
680 pp->Inv[ HANDPOS ].ubImprintID = (NO_PROFILE + 1);
681 }
682 }
683 else
684 {
685 //slot locked, so don't add any attachments to it!
686 usAttachIndex = 0;
687 }
688 //Ammo
689 if( bAmmoClips && usAmmoIndex )
690 {
691 CreateItems( usAmmoIndex, 100, bAmmoClips, &Object );
692 Object.fFlags |= OBJECT_UNDROPPABLE;
693 PlaceObjectInSoldierCreateStruct( pp, &Object );
694 }
695 if( usAttachIndex )
696 {
697 CreateItem( usAttachIndex, 100, &Object );
698 Object.fFlags |= OBJECT_UNDROPPABLE;
699 AttachObject( NULL, &(pp->Inv[ HANDPOS ]), &Object );
700 }
701 }
702
703
ChooseGrenadesForSoldierCreateStruct(SOLDIERCREATE_STRUCT * pp,INT8 bGrenades,INT8 bGrenadeClass,BOOLEAN fGrenadeLauncher)704 static void ChooseGrenadesForSoldierCreateStruct(SOLDIERCREATE_STRUCT* pp, INT8 bGrenades, INT8 bGrenadeClass, BOOLEAN fGrenadeLauncher)
705 {
706 OBJECTTYPE Object;
707 INT16 sNumPoints;
708 UINT16 usItem;
709 UINT8 ubBaseQuality;
710 UINT8 ubQualityVariation;
711 //numbers of each type the player will get!
712 UINT8 ubNumStun = 0;
713 UINT8 ubNumTear = 0;
714 UINT8 ubNumMustard = 0;
715 UINT8 ubNumMini = 0;
716 UINT8 ubNumReg = 0;
717 UINT8 ubNumSmoke = 0;
718 UINT8 ubNumFlare = 0;
719
720 //determine how many *points* the enemy will get to spend on grenades...
721 sNumPoints = bGrenades * bGrenadeClass;
722
723 //no points, no grenades!
724 if( !sNumPoints )
725 return;
726
727 // special mortar shell handling
728 if (bGrenadeClass == MORTAR_GRENADE_CLASS)
729 {
730 CreateItems( MORTAR_SHELL, (INT8) (80 + Random(21)), bGrenades, &Object );
731 Object.fFlags |= OBJECT_UNDROPPABLE;
732 PlaceObjectInSoldierCreateStruct( pp, &Object );
733 return;
734 }
735
736 Assert( bGrenadeClass <= 11 );
737
738 //determine the quality of grenades. The elite guys get the best quality, while the others
739 //get progressively worse.
740 ubBaseQuality = (UINT8)MIN( 45 + bGrenadeClass * 5, 90 );
741 ubQualityVariation = 101 - ubBaseQuality;
742
743
744 //now, purchase the grenades.
745 while( sNumPoints > 0 )
746 {
747 if( sNumPoints >= 20 )
748 { //Choose randomly between mustard and regular
749 if( Random( 2 ) && !fGrenadeLauncher )
750 ubNumMustard++, sNumPoints -= 10;
751 else
752 ubNumReg++, sNumPoints -= 9;
753 }
754
755 if( sNumPoints >= 10 )
756 { //Choose randomly between any
757 switch( Random( 7 ) )
758 {
759 case 0:
760 if ( !fGrenadeLauncher )
761 {
762 ubNumMustard++;
763 sNumPoints -= 10;
764 break;
765 }
766 // if grenade launcher, pick regular instead
767 // fallthrough
768 case 1:
769 ubNumReg++;
770 sNumPoints -= 9;
771 break;
772 case 2:
773 if ( !fGrenadeLauncher )
774 {
775 ubNumMini++;
776 sNumPoints -= 7;
777 break;
778 }
779 // if grenade launcher, pick tear instead
780 // fallthrough
781 case 3:
782 ubNumTear++;
783 sNumPoints -= 6;
784 break;
785 case 4:
786 ubNumStun++;
787 sNumPoints -= 5;
788 break;
789 case 5:
790 ubNumSmoke++;
791 sNumPoints -= 4;
792 break;
793 case 6:
794 if (!fGrenadeLauncher )
795 {
796 ubNumFlare++;
797 sNumPoints -= 3;
798 }
799 break;
800 }
801 }
802
803 // JA2 Gold: don't make mini-grenades take all points available, and add chance of break lights
804 if( sNumPoints >= 1 && sNumPoints < 10 )
805 {
806 switch( Random( 10 ) )
807 {
808 case 0:
809 case 1:
810 case 2:
811 ubNumSmoke++;
812 sNumPoints -= 4;
813 break;
814 case 3:
815 case 4:
816 ubNumTear++;
817 sNumPoints -= 6;
818 break;
819 case 5:
820 case 6:
821 if (!fGrenadeLauncher)
822 {
823 ubNumFlare++;
824 sNumPoints -= 3;
825 }
826 break;
827 case 7:
828 case 8:
829 ubNumStun++;
830 sNumPoints -= 5;
831 break;
832 case 9:
833 if (!fGrenadeLauncher)
834 {
835 ubNumMini++;
836 sNumPoints -= 7;
837 }
838 break;
839 }
840 }
841 /*
842 if( usNumPoints >= 1 && usNumPoints < 10 )
843 { //choose randomly between either stun or tear, (normal with rare chance)
844 switch( Random( 10 ) )
845 {
846 case 0:
847 case 1:
848 case 2:
849 case 3:
850 ubNumSmoke++;
851 if( usNumPoints > 4 )
852 usNumPoints -= 4;
853 else
854 usNumPoints = 0;
855 break;
856 case 4:
857 case 5:
858 case 6:
859 ubNumTear++;
860 if( usNumPoints > 6 )
861 usNumPoints -= 6;
862 else
863 usNumPoints = 0;
864 break;
865 case 7:
866 case 8:
867 ubNumStun++;
868 if( usNumPoints > 5 )
869 usNumPoints -= 5;
870 else
871 usNumPoints = 0;
872 break;
873 case 9:
874 ubNumMini++;
875 usNumPoints = 0;
876 break;
877 }
878 }*/
879 }
880
881
882 //Create the grenades and add them to the soldier
883
884 if( ubNumSmoke )
885 {
886 if ( fGrenadeLauncher )
887 {
888 usItem = GL_SMOKE_GRENADE;
889 }
890 else
891 {
892 usItem = SMOKE_GRENADE;
893 }
894 CreateItems( usItem, (INT8)(ubBaseQuality + Random( ubQualityVariation )), ubNumSmoke, &Object );
895 Object.fFlags |= OBJECT_UNDROPPABLE;
896 PlaceObjectInSoldierCreateStruct( pp, &Object );
897 }
898 if( ubNumTear )
899 {
900 if ( fGrenadeLauncher )
901 {
902 usItem = GL_TEARGAS_GRENADE;
903 }
904 else
905 {
906 usItem = TEARGAS_GRENADE;
907 }
908 CreateItems( usItem, (INT8)(ubBaseQuality + Random( ubQualityVariation )), ubNumTear, &Object );
909 Object.fFlags |= OBJECT_UNDROPPABLE;
910 PlaceObjectInSoldierCreateStruct( pp, &Object );
911 }
912 if( ubNumStun )
913 {
914 if ( fGrenadeLauncher )
915 {
916 usItem = GL_STUN_GRENADE;
917 }
918 else
919 {
920 usItem = STUN_GRENADE;
921 }
922 CreateItems( usItem, (INT8)(ubBaseQuality + Random( ubQualityVariation )), ubNumStun, &Object );
923 Object.fFlags |= OBJECT_UNDROPPABLE;
924 PlaceObjectInSoldierCreateStruct( pp, &Object );
925 }
926 if( ubNumReg )
927 {
928 if ( fGrenadeLauncher )
929 {
930 usItem = GL_HE_GRENADE;
931 }
932 else
933 {
934 usItem = HAND_GRENADE;
935 }
936 CreateItems( usItem, (INT8)(ubBaseQuality + Random( ubQualityVariation )), ubNumReg, &Object );
937 Object.fFlags |= OBJECT_UNDROPPABLE;
938 PlaceObjectInSoldierCreateStruct( pp, &Object );
939 }
940
941 if( ubNumMini )
942 {
943 CreateItems( MINI_GRENADE, (INT8)(ubBaseQuality + Random( ubQualityVariation )), ubNumMini, &Object );
944 Object.fFlags |= OBJECT_UNDROPPABLE;
945 PlaceObjectInSoldierCreateStruct( pp, &Object );
946 }
947 if( ubNumMustard )
948 {
949 CreateItems( MUSTARD_GRENADE, (INT8)(ubBaseQuality + Random( ubQualityVariation )), ubNumMustard, &Object );
950 Object.fFlags |= OBJECT_UNDROPPABLE;
951 PlaceObjectInSoldierCreateStruct( pp, &Object );
952 }
953 if( ubNumFlare )
954 {
955 CreateItems( BREAK_LIGHT, (INT8)(ubBaseQuality + Random( ubQualityVariation )), ubNumFlare, &Object );
956 Object.fFlags |= OBJECT_UNDROPPABLE;
957 PlaceObjectInSoldierCreateStruct( pp, &Object );
958 }
959
960 }
961
962
ChooseArmourForSoldierCreateStruct(SOLDIERCREATE_STRUCT * pp,INT8 bHelmetClass,INT8 bVestClass,INT8 bLeggingsClass)963 static void ChooseArmourForSoldierCreateStruct(SOLDIERCREATE_STRUCT* pp, INT8 bHelmetClass, INT8 bVestClass, INT8 bLeggingsClass)
964 {
965 UINT16 i;
966 UINT16 usRandom;
967 UINT16 usNumMatches;
968 INT8 bOrigVestClass = bVestClass;
969 OBJECTTYPE Object;
970
971 //Choose helmet
972 if( bHelmetClass )
973 {
974 usNumMatches = 0;
975 while( bHelmetClass && !usNumMatches )
976 { //First step is to count the number of helmets in the helmet class range. If we
977 //don't find one, we keep lowering the class until we do.
978 for( i = 0; i < MAXITEMS; i++ )
979 {
980 const ItemModel * pItem = GCM->getItem(i);
981 // NOTE: This relies on treated armor to have a coolness of 0 in order for enemies not to be equipped with it
982 if( pItem->getItemClass() == IC_ARMOUR && pItem->getCoolness() == bHelmetClass )
983 {
984 if( Armour[ pItem->getClassIndex() ].ubArmourClass == ARMOURCLASS_HELMET )
985 usNumMatches++;
986 }
987 }
988 if( !usNumMatches )
989 bHelmetClass--;
990 }
991 if( usNumMatches )
992 { //There is a helmet that we can choose.
993 usRandom = (UINT16)Random( usNumMatches );
994 for( i = 0; i < MAXITEMS; i++ )
995 {
996 const ItemModel * pItem = GCM->getItem(i);
997 if( pItem->getItemClass() == IC_ARMOUR && pItem->getCoolness() == bHelmetClass )
998 {
999 if( Armour[ pItem->getClassIndex() ].ubArmourClass == ARMOURCLASS_HELMET )
1000 {
1001 if( usRandom )
1002 usRandom--;
1003 else
1004 {
1005 if( !(pp->Inv[ HELMETPOS ].fFlags & OBJECT_NO_OVERWRITE) )
1006 {
1007 CreateItem( i, (INT8)(70+Random(31)), &(pp->Inv[ HELMETPOS ]) );
1008 pp->Inv[ HELMETPOS ].fFlags |= OBJECT_UNDROPPABLE;
1009 }
1010 break;
1011 }
1012 }
1013 }
1014 }
1015 }
1016 }
1017 //Choose vest
1018 if( bVestClass )
1019 {
1020 usNumMatches = 0;
1021 while( bVestClass && !usNumMatches )
1022 {
1023 //First step is to count the number of armours in the armour class range. If we
1024 //don't find one, we keep lowering the class until we do.
1025 for( i = 0; i < MAXITEMS; i++ )
1026 {
1027 // these 3 have a non-zero coolness, and would otherwise be selected, so skip them
1028 if ( ( i == TSHIRT ) || ( i == LEATHER_JACKET ) || ( i == TSHIRT_DEIDRANNA ) )
1029 continue;
1030
1031 const ItemModel * pItem = GCM->getItem(i);
1032 // NOTE: This relies on treated armor to have a coolness of 0 in order for enemies not to be equipped with it
1033 if( pItem->getItemClass() == IC_ARMOUR && pItem->getCoolness() == bVestClass )
1034 {
1035 if( Armour[ pItem->getClassIndex() ].ubArmourClass == ARMOURCLASS_VEST )
1036 usNumMatches++;
1037 }
1038 }
1039 if( !usNumMatches )
1040 bVestClass--;
1041 }
1042 if( usNumMatches )
1043 {
1044 //There is an armour that we can choose.
1045 usRandom = (UINT16)Random( usNumMatches );
1046 for( i = 0; i < MAXITEMS; i++ )
1047 {
1048 // these 3 have a non-zero coolness, and would otherwise be selected, so skip them
1049 if ( ( i == TSHIRT ) || ( i == LEATHER_JACKET ) || ( i == TSHIRT_DEIDRANNA ) )
1050 continue;
1051
1052 const ItemModel * pItem = GCM->getItem(i);
1053 if( pItem->getItemClass() == IC_ARMOUR && pItem->getCoolness() == bVestClass )
1054 {
1055 if( Armour[ pItem->getClassIndex() ].ubArmourClass == ARMOURCLASS_VEST )
1056 {
1057 if( usRandom )
1058 usRandom--;
1059 else
1060 {
1061 if( !(pp->Inv[ VESTPOS ].fFlags & OBJECT_NO_OVERWRITE) )
1062 {
1063 CreateItem( i, (INT8)(70+Random(31)), &(pp->Inv[ VESTPOS ]) );
1064 pp->Inv[ VESTPOS ].fFlags |= OBJECT_UNDROPPABLE;
1065
1066 if ( ( i == KEVLAR_VEST ) || ( i == SPECTRA_VEST ) )
1067 {
1068 // roll to see if he gets a CERAMIC PLATES, too.
1069 // Higher chance the higher his entitled vest class is
1070 if ((INT8) Random(100) < (15 * (bOrigVestClass - pItem->getCoolness())))
1071 {
1072 CreateItem( CERAMIC_PLATES, (INT8)(70+Random(31)), &Object );
1073 Object.fFlags |= OBJECT_UNDROPPABLE;
1074 AttachObject( NULL, &(pp->Inv[ VESTPOS ]), &Object );
1075 }
1076 }
1077 }
1078 break;
1079 }
1080 }
1081 }
1082 }
1083 }
1084 }
1085 //Choose Leggings
1086 if( bLeggingsClass )
1087 {
1088 usNumMatches = 0;
1089 while( bLeggingsClass && !usNumMatches )
1090 { //First step is to count the number of Armours in the Armour class range. If we
1091 //don't find one, we keep lowering the class until we do.
1092 for( i = 0; i < MAXITEMS; i++ )
1093 {
1094 const ItemModel * pItem = GCM->getItem(i);
1095 // NOTE: This relies on treated armor to have a coolness of 0 in order for enemies not to be equipped with it
1096 if( pItem->getItemClass() == IC_ARMOUR && pItem->getCoolness() == bLeggingsClass )
1097 {
1098 if( Armour[ pItem->getClassIndex() ].ubArmourClass == ARMOURCLASS_LEGGINGS )
1099 usNumMatches++;
1100 }
1101 }
1102 if( !usNumMatches )
1103 bLeggingsClass--;
1104 }
1105 if( usNumMatches )
1106 {
1107 //There is a legging that we can choose.
1108 usRandom = (UINT16)Random( usNumMatches );
1109 for( i = 0; i < MAXITEMS; i++ )
1110 {
1111 const ItemModel * pItem = GCM->getItem(i);
1112 if( pItem->getItemClass() == IC_ARMOUR && pItem->getCoolness() == bLeggingsClass )
1113 {
1114 if( Armour[ pItem->getClassIndex() ].ubArmourClass == ARMOURCLASS_LEGGINGS )
1115 {
1116 if( usRandom )
1117 usRandom--;
1118 else
1119 {
1120 if( !(pp->Inv[ LEGPOS ].fFlags & OBJECT_NO_OVERWRITE) )
1121 {
1122 CreateItem( i, (INT8)(70+Random(31)), &(pp->Inv[ LEGPOS ]) );
1123 pp->Inv[ LEGPOS ].fFlags |= OBJECT_UNDROPPABLE;
1124 break;
1125 }
1126 }
1127 }
1128 }
1129 }
1130 }
1131 }
1132 }
1133
1134
ChooseSpecialWeaponsForSoldierCreateStruct(SOLDIERCREATE_STRUCT * pp,INT8 bKnifeClass,BOOLEAN fGrenadeLauncher,BOOLEAN fLAW,BOOLEAN fMortar)1135 static void ChooseSpecialWeaponsForSoldierCreateStruct(SOLDIERCREATE_STRUCT* pp, INT8 bKnifeClass, BOOLEAN fGrenadeLauncher, BOOLEAN fLAW, BOOLEAN fMortar)
1136 {
1137 UINT16 i;
1138 UINT16 usRandom;
1139 UINT16 usNumMatches = 0;
1140 UINT16 usKnifeIndex = 0;
1141 OBJECTTYPE Object;
1142
1143 //Choose knife
1144 while( bKnifeClass && !usNumMatches )
1145 {
1146 //First step is to count the number of weapons in the weapon class range. If we
1147 //don't find one, we keep lowering the class until we do.
1148 for( i = 0; i < MAXITEMS; i++ )
1149 {
1150 const ItemModel * pItem = GCM->getItem(i);
1151 if( ( pItem->getItemClass() == IC_BLADE || pItem->getItemClass() == IC_THROWING_KNIFE ) && pItem->getCoolness() == bKnifeClass )
1152 {
1153 usNumMatches++;
1154 }
1155 }
1156 if( !usNumMatches )
1157 bKnifeClass--;
1158 }
1159 if( usNumMatches )
1160 {
1161 //There is a knife that we can choose.
1162 usRandom = (UINT16)Random( usNumMatches );
1163 for( i = 0; i < MAXITEMS; i++ )
1164 {
1165 const ItemModel * pItem = GCM->getItem(i);
1166 if( ( pItem->getItemClass() == IC_BLADE || pItem->getItemClass() == IC_THROWING_KNIFE ) && pItem->getCoolness() == bKnifeClass )
1167 {
1168 if( usRandom )
1169 usRandom--;
1170 else
1171 {
1172 usKnifeIndex = i;
1173 break;
1174 }
1175 }
1176 }
1177 }
1178
1179 if( usKnifeIndex )
1180 {
1181 CreateItem( usKnifeIndex, (INT8)(70 + Random( 31 )), &Object );
1182 Object.fFlags |= OBJECT_UNDROPPABLE;
1183 PlaceObjectInSoldierCreateStruct( pp, &Object );
1184 }
1185
1186 if (fGrenadeLauncher)
1187 {
1188 // give grenade launcher
1189 CreateItem( GLAUNCHER, (INT8)(50 + Random( 51 )), &Object );
1190 Object.fFlags |= OBJECT_UNDROPPABLE;
1191 PlaceObjectInSoldierCreateStruct( pp, &Object );
1192 }
1193
1194 if (fLAW)
1195 {
1196 // give rocket launcher
1197 CreateItem( ROCKET_LAUNCHER, (INT8)(50 + Random( 51 )), &Object );
1198 Object.fFlags |= OBJECT_UNDROPPABLE;
1199 PlaceObjectInSoldierCreateStruct( pp, &Object );
1200 }
1201
1202 if (fMortar)
1203 {
1204 // make sure we're not distributing them underground!
1205 Assert( IsAutoResolveActive() || ( gbWorldSectorZ == 0 ) );
1206
1207 // give mortar
1208 CreateItem( MORTAR, (INT8)(50 + Random( 51 )), &Object );
1209 Object.fFlags |= OBJECT_UNDROPPABLE;
1210 PlaceObjectInSoldierCreateStruct( pp, &Object );
1211 }
1212 }
1213
1214
ChooseFaceGearForSoldierCreateStruct(SOLDIERCREATE_STRUCT * pp)1215 static void ChooseFaceGearForSoldierCreateStruct(SOLDIERCREATE_STRUCT* pp)
1216 {
1217 INT32 i;
1218 INT8 bDifficultyRating = CalcDifficultyModifier( pp->ubSoldierClass );
1219
1220
1221 if (gWorldSectorX == TIXA_SECTOR_X && gWorldSectorY == TIXA_SECTOR_Y &&
1222 StrategicMap[TIXA_SECTOR_X + TIXA_SECTOR_Y * MAP_WORLD_X].fEnemyControlled)
1223 {
1224 //Tixa is a special case that is handled differently.
1225 return;
1226 }
1227
1228 //Look for any face item in the big pocket positions (the only place they can be added in the editor)
1229 //If any are found, then don't assign any
1230 for( i = BIGPOCK1POS; i < BIGPOCK4POS; i++ )
1231 {
1232 if( GCM->getItem(pp->Inv[ i ].usItem)->getItemClass() == IC_FACE )
1233 {
1234 return;
1235 }
1236 }
1237
1238 //KM: (NEW)
1239 //Note the lack of overwritable item checking here. This is because faceitems are not
1240 //supported in the editor, hence they can't have this status.
1241 switch( pp->ubSoldierClass )
1242 {
1243 case SOLDIER_CLASS_ELITE:
1244 case SOLDIER_CLASS_ELITE_MILITIA:
1245 //All elites get a gasmask and either night goggles or uv goggles.
1246 if( Chance( 75 ) )
1247 {
1248 CreateItem( GASMASK, (INT8)(70+Random(31)), &(pp->Inv[ HEAD1POS ]) );
1249 pp->Inv[ HEAD1POS ].fFlags |= OBJECT_UNDROPPABLE;
1250 }
1251 else
1252 {
1253 CreateItem( EXTENDEDEAR, (INT8)(70+Random(31)), &(pp->Inv[ HEAD1POS ]) );
1254 pp->Inv[ HEAD1POS ].fFlags |= OBJECT_UNDROPPABLE;
1255 }
1256 if( Chance( 75 ) )
1257 {
1258 CreateItem( NIGHTGOGGLES, (INT8)(70+Random(31)), &(pp->Inv[ HEAD2POS ]) );
1259 pp->Inv[ HEAD2POS ].fFlags |= OBJECT_UNDROPPABLE;
1260 }
1261 else
1262 {
1263 CreateItem( UVGOGGLES, (INT8)(70+Random(31)), &(pp->Inv[ HEAD2POS ]) );
1264 pp->Inv[ HEAD2POS ].fFlags |= OBJECT_UNDROPPABLE;
1265 }
1266 break;
1267 case SOLDIER_CLASS_ARMY:
1268 case SOLDIER_CLASS_REG_MILITIA:
1269 if( Chance( bDifficultyRating / 2 ) )
1270 {
1271 //chance of getting a face item
1272 if( Chance( 50 ) )
1273 {
1274 CreateItem( GASMASK, (INT8)(70+Random(31)), &(pp->Inv[ HEAD1POS ]) );
1275 pp->Inv[ HEAD1POS ].fFlags |= OBJECT_UNDROPPABLE;
1276 }
1277 else
1278 {
1279 CreateItem( NIGHTGOGGLES, (INT8)(70+Random(31)), &(pp->Inv[ HEAD1POS ]) );
1280 pp->Inv[ HEAD1POS ].fFlags |= OBJECT_UNDROPPABLE;
1281 }
1282 }
1283 if( Chance( bDifficultyRating / 3 ) )
1284 {
1285 //chance of getting a extended ear
1286 CreateItem( EXTENDEDEAR, (INT8)(70+Random(31)), &(pp->Inv[ HEAD2POS ]) );
1287 pp->Inv[ HEAD2POS ].fFlags |= OBJECT_UNDROPPABLE;
1288 }
1289 break;
1290 case SOLDIER_CLASS_ADMINISTRATOR:
1291 case SOLDIER_CLASS_GREEN_MILITIA:
1292 break;
1293 }
1294 }
1295
1296
ChooseKitsForSoldierCreateStruct(SOLDIERCREATE_STRUCT * pp,INT8 bKitClass)1297 static void ChooseKitsForSoldierCreateStruct(SOLDIERCREATE_STRUCT* pp, INT8 bKitClass)
1298 {
1299 UINT16 i;
1300 UINT16 usRandom;
1301 UINT16 usNumMatches = 0;
1302 OBJECTTYPE Object;
1303 UINT16 usKitItem = 0;
1304
1305
1306 // we want these mostly to be first aid and medical kits, and for those kit class doesn't matter, they're always useful
1307 if ( Chance( 50 ) )
1308 {
1309 usKitItem = FIRSTAIDKIT;
1310 }
1311 else
1312 if ( Chance( 25 ) )
1313 {
1314 usKitItem = MEDICKIT;
1315 }
1316 else
1317 {
1318 // count how many non-medical KITS are eligible ( of sufficient or lower coolness)
1319 for( i = 0; i < MAXITEMS; i++ )
1320 {
1321 const ItemModel * pItem = GCM->getItem(i);
1322 // skip toolkits
1323 if( ( pItem->getItemClass() == IC_KIT ) && ( pItem->getCoolness() > 0 ) && pItem->getCoolness() <= bKitClass && ( i != TOOLKIT ) )
1324 {
1325 usNumMatches++;
1326 }
1327 }
1328
1329 // if any are eligible, pick one of them at random
1330 if( usNumMatches )
1331 {
1332 usRandom = (UINT16)Random( usNumMatches );
1333 for( i = 0; i < MAXITEMS; i++ )
1334 {
1335 const ItemModel * pItem = GCM->getItem(i);
1336 // skip toolkits
1337 if((pItem->getItemClass() == IC_KIT) && (pItem->getCoolness() > 0) &&
1338 pItem->getCoolness() <= bKitClass && (i != TOOLKIT))
1339 {
1340 if( usRandom )
1341 usRandom--;
1342 else
1343 {
1344 usKitItem = i;
1345 break;
1346 }
1347 }
1348 }
1349 }
1350 }
1351
1352
1353 if ( usKitItem != 0 )
1354 {
1355 CreateItem( usKitItem, (INT8)(80 + Random( 21 )), &Object );
1356 Object.fFlags |= OBJECT_UNDROPPABLE;
1357 PlaceObjectInSoldierCreateStruct( pp, &Object );
1358 }
1359 }
1360
1361
ChooseMiscGearForSoldierCreateStruct(SOLDIERCREATE_STRUCT * pp,INT8 bMiscClass)1362 static void ChooseMiscGearForSoldierCreateStruct(SOLDIERCREATE_STRUCT* pp, INT8 bMiscClass)
1363 {
1364 // not all of these are IC_MISC, some are IC_PUNCH (not covered anywhere else)
1365 static const INT32 iMiscItemsList[] =
1366 {
1367 CANTEEN,
1368 CANTEEN,
1369 CANTEEN,
1370 CANTEEN,
1371 ALCOHOL,
1372 ALCOHOL,
1373 ADRENALINE_BOOSTER,
1374 ADRENALINE_BOOSTER,
1375 REGEN_BOOSTER,
1376 BRASS_KNUCKLES,
1377 CHEWING_GUM,
1378 CIGARS,
1379 GOLDWATCH,
1380 NOTHING
1381 };
1382
1383 // count how many are eligible
1384 UINT16 usNumMatches = 0;
1385 for (const INT32* i = iMiscItemsList; *i != NOTHING; ++i)
1386 {
1387 const ItemModel * pItem = GCM->getItem(*i);
1388 if (pItem->getCoolness() > 0 &&
1389 pItem->getCoolness() <= bMiscClass &&
1390 (*i != REGEN_BOOSTER || gGameOptions.fSciFi)) // exclude REGEN_BOOSTERs unless Sci-Fi flag is on
1391 {
1392 ++usNumMatches;
1393 }
1394 }
1395
1396 // if any are eligible, pick one of them at random
1397 if (usNumMatches == 0) return;
1398
1399 UINT16 usRandom = Random(usNumMatches);
1400 for (const INT32* i = iMiscItemsList; *i != NOTHING; ++i)
1401 {
1402 const ItemModel * pItem = GCM->getItem(*i);
1403 if (pItem->getCoolness() > 0 &&
1404 pItem->getCoolness() <= bMiscClass &&
1405 (*i != REGEN_BOOSTER || gGameOptions.fSciFi)) // exclude REGEN_BOOSTERs unless Sci-Fi flag is on
1406 {
1407 if (usRandom)
1408 {
1409 --usRandom;
1410 }
1411 else
1412 {
1413 OBJECTTYPE Object;
1414 CreateItem(*i, 80 + Random(21), &Object);
1415 Object.fFlags |= OBJECT_UNDROPPABLE;
1416 PlaceObjectInSoldierCreateStruct(pp, &Object);
1417 break;
1418 }
1419 }
1420 }
1421 }
1422
1423
ChooseBombsForSoldierCreateStruct(SOLDIERCREATE_STRUCT * pp,INT8 bBombClass)1424 static void ChooseBombsForSoldierCreateStruct(SOLDIERCREATE_STRUCT* pp, INT8 bBombClass)
1425 {
1426 UINT16 i;
1427 UINT16 usRandom;
1428 UINT16 usNumMatches = 0;
1429 OBJECTTYPE Object;
1430
1431 // count how many are eligible
1432 for( i = 0; i < MAXITEMS; i++ )
1433 {
1434 const ItemModel * pItem = GCM->getItem(i);
1435 if( ( pItem->getItemClass() == IC_BOMB ) && ( pItem->getCoolness() > 0 ) && ( pItem->getCoolness() <= bBombClass ) )
1436 {
1437 usNumMatches++;
1438 }
1439 }
1440
1441
1442 // if any are eligible, pick one of them at random
1443 if( usNumMatches )
1444 {
1445 usRandom = (UINT16)Random( usNumMatches );
1446 for( i = 0; i < MAXITEMS; i++ )
1447 {
1448 const ItemModel * pItem = GCM->getItem(i);
1449 if( ( pItem->getItemClass() == IC_BOMB ) && ( pItem->getCoolness() > 0 ) && ( pItem->getCoolness() <= bBombClass ) )
1450 {
1451 if( usRandom )
1452 usRandom--;
1453 else
1454 {
1455 CreateItem( i, (INT8)(80 + Random( 21 )), &Object );
1456 Object.fFlags |= OBJECT_UNDROPPABLE;
1457 PlaceObjectInSoldierCreateStruct( pp, &Object );
1458 break;
1459 }
1460 }
1461 }
1462 }
1463 }
1464
1465
ChooseLocationSpecificGearForSoldierCreateStruct(SOLDIERCREATE_STRUCT * pp)1466 static void ChooseLocationSpecificGearForSoldierCreateStruct(SOLDIERCREATE_STRUCT* pp)
1467 {
1468 OBJECTTYPE Object;
1469
1470 // If this is Tixa and the player doesn't control Tixa then give all enemies gas masks,
1471 // but somewhere on their person, not in their face positions
1472 if ( gWorldSectorX == TIXA_SECTOR_X && gWorldSectorY == TIXA_SECTOR_Y && StrategicMap[ TIXA_SECTOR_X + TIXA_SECTOR_Y * MAP_WORLD_X ].fEnemyControlled )
1473 {
1474 CreateItem( GASMASK, (INT8) (95+Random(6)), &Object );
1475 PlaceObjectInSoldierCreateStruct( pp, &Object );
1476 }
1477 }
1478
1479
PlaceObjectInSoldierCreateStruct(SOLDIERCREATE_STRUCT * pp,OBJECTTYPE * pObject)1480 static BOOLEAN PlaceObjectInSoldierCreateStruct(SOLDIERCREATE_STRUCT* pp, OBJECTTYPE* pObject)
1481 {
1482 INT8 i;
1483 if( !GCM->getItem(pObject->usItem )->getPerPocket() )
1484 {
1485 //ubPerPocket == 0 will only fit in large pockets.
1486 pObject->ubNumberOfObjects = 1;
1487 for( i = BIGPOCK1POS; i <= BIGPOCK4POS; i++ )
1488 {
1489 if( !(pp->Inv[ i ].usItem) && !(pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE) )
1490 {
1491 pp->Inv[i] = *pObject;
1492 return TRUE;
1493 }
1494 }
1495 return FALSE;
1496 }
1497 else
1498 {
1499 pObject->ubNumberOfObjects = (UINT8)MIN( GCM->getItem(pObject->usItem )->getPerPocket(), pObject->ubNumberOfObjects );
1500 //try to get it into a small pocket first
1501 for( i = SMALLPOCK1POS; i <= SMALLPOCK8POS; i++ )
1502 {
1503 if( !(pp->Inv[ i ].usItem) && !(pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE) )
1504 {
1505 pp->Inv[i] = *pObject;
1506 return TRUE;
1507 }
1508 }
1509 for( i = BIGPOCK1POS; i <= BIGPOCK4POS; i++ )
1510 {
1511 //no space free in small pockets, so put it into a large pocket.
1512 if( !(pp->Inv[ i ].usItem) && !(pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE) )
1513 {
1514 pp->Inv[i] = *pObject;
1515 return TRUE;
1516 }
1517 }
1518 }
1519 return FALSE;
1520 }
1521
1522
MakeOneItemOfClassDroppable(SOLDIERCREATE_STRUCT * const sc,UINT32 const item_class)1523 static void MakeOneItemOfClassDroppable(SOLDIERCREATE_STRUCT* const sc, UINT32 const item_class)
1524 {
1525 // XXX TODO001B: OBJECT_NO_OVERWRITE test should probably continue instead of
1526 // break, but it was this way in the original code. This is even more
1527 // plausible, because the OBJECT_NO_OVERWRITE condition in the second loop
1528 // never can be true in the current configuration. A comment below says that
1529 // no object of that class should be dropped if any has this flag set, but the
1530 // code did not do this.
1531 UINT8 n_matches = 0;
1532 for (INT32 i = 0; i != NUM_INV_SLOTS; ++i)
1533 {
1534 OBJECTTYPE const& o = sc->Inv[i];
1535 if (!(GCM->getItem(o.usItem)->getItemClass() & item_class))
1536 continue;
1537 if (o.fFlags & OBJECT_NO_OVERWRITE)
1538 break;
1539 ++n_matches;
1540 }
1541 if (n_matches == 0) return;
1542
1543 for (INT32 i = 0; i != NUM_INV_SLOTS; ++i)
1544 {
1545 OBJECTTYPE& o = sc->Inv[i];
1546 if (!(GCM->getItem(o.usItem)->getItemClass() & item_class))
1547 continue;
1548 if (o.fFlags & OBJECT_NO_OVERWRITE)
1549 break;
1550 if (Random(n_matches--) != 0)
1551 continue;
1552 o.fFlags &= ~OBJECT_UNDROPPABLE;
1553 break;
1554 }
1555 }
1556
1557
RandomlyChooseWhichItemsAreDroppable(SOLDIERCREATE_STRUCT * pp,INT8 bSoldierClass)1558 static void RandomlyChooseWhichItemsAreDroppable(SOLDIERCREATE_STRUCT* pp, INT8 bSoldierClass)
1559 {
1560 INT32 i;
1561 //UINT16 usRandomNum;
1562 UINT32 uiItemClass;
1563 UINT16 usItem;
1564 UINT8 ubAmmoDropRate;
1565 UINT8 ubGrenadeDropRate;
1566 UINT8 ubOtherDropRate;
1567 BOOLEAN fWeapon = FALSE;
1568 BOOLEAN fGrenades = FALSE; // this includes all grenades!
1569 BOOLEAN fAmmo = FALSE;
1570 BOOLEAN fArmour = FALSE;
1571 BOOLEAN fKnife = FALSE;
1572 BOOLEAN fKit = FALSE;
1573 BOOLEAN fFace = FALSE;
1574 BOOLEAN fMisc = FALSE;
1575
1576
1577 /*
1578 //40% of soldiers will have droppable items.
1579 usRandomNum = (UINT16)Random( 1000 );
1580 if( usRandomNum >= 400 )
1581 return;
1582 //so now the number is 0-399. This is kind of like a D&D die roll where
1583 //various numbers drop different items, or even more than one item. At this
1584 //point, we don't care if the enemy has anything in the slot that is made droppable.
1585 //Any items containing the OBJECT_NO_OVERWRITE slot is rejected for droppable
1586 //consideration.
1587
1588 if( usRandomNum < 32 ) //3.2% of dead bodies present the possibility of several items (0-5 items : avg 3).
1589 {
1590 //31 is the magic number that allows all 5 item classes to be dropped!
1591 if( usRandomNum & 16 )
1592 fWeapon = TRUE;
1593 if( usRandomNum & 8 )
1594 fAmmo = TRUE;
1595 if( usRandomNum & 4 )
1596 fGrenades = TRUE;
1597 if( usRandomNum & 2 )
1598 fArmour = TRUE;
1599 if( usRandomNum & 1 )
1600 fMisc = TRUE;
1601 }
1602 else if( usRandomNum < 100 ) //6.8% chance of getting 2-3 different items.
1603 {
1604 //do a more generalized approach to dropping items.
1605 switch( usRandomNum / 10 )
1606 {
1607 case 3:
1608 fWeapon = TRUE;
1609 fAmmo = TRUE;
1610 break;
1611 case 4:
1612 fWeapon = TRUE;
1613 fGrenades = TRUE;
1614 break;
1615 case 5:
1616 fGrenades = TRUE;
1617 fMisc = TRUE;
1618 break;
1619 case 6:
1620 fGrenades = TRUE;
1621 fArmour = TRUE;
1622 break;
1623 case 7:
1624 fAmmo = TRUE;
1625 fArmour = TRUE;
1626 break;
1627 case 8:
1628 fAmmo = TRUE;
1629 fArmour = TRUE;
1630 fMisc = TRUE;
1631 break;
1632 case 9:
1633 fGrenades = TRUE;
1634 fAmmo = TRUE;
1635 fMisc = TRUE;
1636 break;
1637 }
1638 }
1639 else
1640 {
1641 switch( usRandomNum / 50 ) //30% chance of getting 1-2 items (no weapons)
1642 {
1643 case 2:
1644 fGrenades = TRUE;
1645 break;
1646 case 3:
1647 fAmmo = TRUE;
1648 break;
1649 case 4:
1650 fArmour = TRUE;
1651 break;
1652 case 5:
1653 fMisc = TRUE;
1654 break;
1655 case 6:
1656 fAmmo = TRUE;
1657 fMisc = TRUE;
1658 break;
1659 case 7:
1660 fGrenades = TRUE;
1661 fAmmo = TRUE;
1662 break;
1663 }
1664 }
1665
1666 fKnife = (Random(3)) ? FALSE : TRUE;*/
1667
1668
1669 // only enemy soldiers use auto-drop system!
1670 // don't use the auto-drop system in auto-resolve: player won't see what's being used & enemies will often win & keep'em
1671 if ( SOLDIER_CLASS_ENEMY( bSoldierClass ) && !IsAutoResolveActive() )
1672 {
1673 // SPECIAL handling for weapons: we'll always drop a weapon type that has never been dropped before
1674 for( i = 0; i < NUM_INV_SLOTS; i++ )
1675 {
1676 usItem = pp->Inv[ i ].usItem;
1677 // if it's a weapon (monster parts included - they won't drop due to checks elsewhere!)
1678 if ((usItem > NONE) && (usItem < MAX_WEAPONS))
1679 {
1680 // and we're allowed to change its flags
1681 if(! (pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE ))
1682 {
1683 // and it's never been dropped before in this game
1684 if (!gStrategicStatus.fWeaponDroppedAlready[usItem])
1685 {
1686 // mark it as droppable, and remember we did so. If the player never kills this particular dude,
1687 // oh well, tough luck, he missed his chance for an easy reward, he'll have to wait til next
1688 // time and need some luck...
1689 pp->Inv[ i ].fFlags &= ~OBJECT_UNDROPPABLE;
1690
1691 MarkAllWeaponsOfSameGunClassAsDropped( usItem );
1692 }
1693 }
1694 }
1695 }
1696 }
1697
1698
1699 if ( SOLDIER_CLASS_MILITIA( bSoldierClass ) )
1700 {
1701 // militia (they drop much less stuff)
1702 ubAmmoDropRate = MILITIAAMMODROPRATE;
1703 ubGrenadeDropRate = MILITIAGRENADEDROPRATE;
1704 ubOtherDropRate = MILITIAEQUIPDROPRATE;
1705 }
1706 else
1707 {
1708 // enemy army
1709 ubAmmoDropRate = ENEMYAMMODROPRATE;
1710 ubGrenadeDropRate = ENEMYGRENADEDROPRATE;
1711 ubOtherDropRate = ENEMYEQUIPDROPRATE;
1712 }
1713
1714
1715
1716 if( Random(100) < ubAmmoDropRate )
1717 fAmmo = TRUE;
1718
1719 if( Random(100) < ubOtherDropRate )
1720 fWeapon = TRUE;
1721
1722 if( Random(100) < ubOtherDropRate )
1723 fArmour = TRUE;
1724
1725 if( Random(100) < ubOtherDropRate )
1726 fKnife = TRUE;
1727
1728 if( Random(100) < ubGrenadeDropRate )
1729 fGrenades = TRUE;
1730
1731 if( Random(100) < ubOtherDropRate )
1732 fKit = TRUE;
1733
1734 if( Random(100) < (UINT32)(ubOtherDropRate / 3) )
1735 fFace = TRUE;
1736
1737 if( Random(100) < ubOtherDropRate )
1738 fMisc = TRUE;
1739
1740
1741 //Now, that the flags are set for each item, we now have to search through the item slots to
1742 //see if we can find a matching item, however, if we find any items in a particular class that
1743 //have the OBJECT_NO_OVERWRITE flag set, we will not make any items droppable for that class
1744 //because the editor would have specified it already.
1745 if( fAmmo )
1746 {
1747 // now drops ALL ammo found, not just the first slot
1748 for( i = 0; i < NUM_INV_SLOTS; i++ )
1749 {
1750 uiItemClass = GCM->getItem(pp->Inv[ i ].usItem)->getItemClass();
1751 if( uiItemClass == IC_AMMO )
1752 {
1753 if( pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE )
1754 continue;
1755 else
1756 {
1757 pp->Inv[ i ].fFlags &= ~OBJECT_UNDROPPABLE;
1758 }
1759 }
1760 }
1761 }
1762
1763 if (fWeapon) MakeOneItemOfClassDroppable(pp, IC_LAUNCHER | IC_GUN);
1764 if (fArmour) MakeOneItemOfClassDroppable(pp, IC_ARMOUR);
1765
1766 if( fKnife)
1767 {
1768 for( i = 0; i < NUM_INV_SLOTS; i++ )
1769 {
1770 // drops FIRST knife found
1771 uiItemClass = GCM->getItem(pp->Inv[ i ].usItem)->getItemClass();
1772 if( uiItemClass == IC_BLADE || uiItemClass == IC_THROWING_KNIFE )
1773 {
1774 if( pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE )
1775 break;
1776 else
1777 {
1778 pp->Inv[ i ].fFlags &= ~OBJECT_UNDROPPABLE;
1779 break;
1780 }
1781 }
1782 }
1783 }
1784
1785 // note that they'll only drop ONE TYPE of grenade if they have multiple types (very common)
1786 if (fGrenades) MakeOneItemOfClassDroppable(pp, IC_GRENADE);
1787 if (fKit) MakeOneItemOfClassDroppable(pp, IC_KIT | IC_MEDKIT);
1788 if (fFace) MakeOneItemOfClassDroppable(pp, IC_FACE);
1789 if (fMisc) MakeOneItemOfClassDroppable(pp, IC_MISC);
1790 }
1791
1792
AssignCreatureInventory(SOLDIERTYPE * pSoldier)1793 void AssignCreatureInventory( SOLDIERTYPE *pSoldier )
1794 {
1795 UINT32 uiChanceToDrop = 0;
1796 BOOLEAN fMaleCreature = FALSE;
1797 BOOLEAN fBloodcat = FALSE;
1798
1799 // all creature items in this first section are only offensive/defensive placeholders, and
1800 // never get dropped, because they're not real items!
1801 switch(pSoldier->ubBodyType)
1802 {
1803 case ADULTFEMALEMONSTER:
1804 CreateItem(CREATURE_OLD_FEMALE_CLAWS, 100, &(pSoldier->inv[HANDPOS]));
1805 CreateItem(CREATURE_OLD_FEMALE_HIDE, 100, &(pSoldier->inv[HELMETPOS]));
1806 CreateItem(CREATURE_OLD_FEMALE_HIDE, 100, &(pSoldier->inv[VESTPOS]));
1807 CreateItem(CREATURE_OLD_FEMALE_HIDE, 100, &(pSoldier->inv[LEGPOS]));
1808 uiChanceToDrop = 30;
1809 break;
1810 case AM_MONSTER:
1811 CreateItem(CREATURE_OLD_MALE_CLAWS, 100, &(pSoldier->inv[HANDPOS]));
1812 CreateItem(CREATURE_OLD_MALE_SPIT, 100, &(pSoldier->inv[SECONDHANDPOS]));
1813 CreateItem(CREATURE_OLD_MALE_HIDE, 100, &(pSoldier->inv[HELMETPOS]));
1814 CreateItem(CREATURE_OLD_MALE_HIDE, 100, &(pSoldier->inv[VESTPOS]));
1815 CreateItem(CREATURE_OLD_MALE_HIDE, 100, &(pSoldier->inv[LEGPOS]));
1816 uiChanceToDrop = 30;
1817 fMaleCreature = TRUE;
1818 break;
1819 case YAF_MONSTER:
1820 CreateItem(CREATURE_YOUNG_FEMALE_CLAWS, 100, &(pSoldier->inv[HANDPOS]));
1821 CreateItem(CREATURE_YOUNG_FEMALE_HIDE, 100, &(pSoldier->inv[HELMETPOS]));
1822 CreateItem(CREATURE_YOUNG_FEMALE_HIDE, 100, &(pSoldier->inv[VESTPOS]));
1823 CreateItem(CREATURE_YOUNG_FEMALE_HIDE, 100, &(pSoldier->inv[LEGPOS]));
1824 uiChanceToDrop = 15;
1825 break;
1826 case YAM_MONSTER:
1827 CreateItem(CREATURE_YOUNG_MALE_CLAWS, 100, &(pSoldier->inv[HANDPOS]));
1828 CreateItem(CREATURE_YOUNG_MALE_SPIT, 100, &(pSoldier->inv[SECONDHANDPOS]));
1829 CreateItem(CREATURE_YOUNG_MALE_HIDE, 100, &(pSoldier->inv[HELMETPOS]));
1830 CreateItem(CREATURE_YOUNG_MALE_HIDE, 100, &(pSoldier->inv[VESTPOS]));
1831 CreateItem(CREATURE_YOUNG_MALE_HIDE, 100, &(pSoldier->inv[LEGPOS]));
1832 uiChanceToDrop = 15;
1833 fMaleCreature = TRUE;
1834 break;
1835 case INFANT_MONSTER:
1836 CreateItem(CREATURE_INFANT_SPIT, 100, &(pSoldier->inv[HANDPOS]));
1837 CreateItem(CREATURE_INFANT_HIDE, 100, &(pSoldier->inv[HELMETPOS]));
1838 CreateItem(CREATURE_INFANT_HIDE, 100, &(pSoldier->inv[VESTPOS]));
1839 CreateItem(CREATURE_INFANT_HIDE, 100, &(pSoldier->inv[LEGPOS]));
1840 uiChanceToDrop = 5;
1841 break;
1842 case LARVAE_MONSTER:
1843 uiChanceToDrop = 0;
1844 break;
1845 case QUEENMONSTER:
1846 CreateItem(CREATURE_QUEEN_SPIT, 100, &(pSoldier->inv[HANDPOS]));
1847 CreateItem(CREATURE_QUEEN_TENTACLES, 100, &(pSoldier->inv[SECONDHANDPOS]));
1848 CreateItem(CREATURE_QUEEN_HIDE, 100, &(pSoldier->inv[HELMETPOS]));
1849 CreateItem(CREATURE_QUEEN_HIDE, 100, &(pSoldier->inv[VESTPOS]));
1850 CreateItem(CREATURE_QUEEN_HIDE, 100, &(pSoldier->inv[LEGPOS]));
1851 // she can't drop anything, because the items are unreachable anyways (she's too big!)
1852 uiChanceToDrop = 0;
1853 break;
1854 case BLOODCAT:
1855 CreateItem(BLOODCAT_CLAW_ATTACK, 100, &(pSoldier->inv[HANDPOS]));
1856 CreateItem(BLOODCAT_BITE, 100, &(pSoldier->inv[SECONDHANDPOS]));
1857 fBloodcat = TRUE;
1858 uiChanceToDrop = 30;
1859 break;
1860
1861 default:
1862 AssertMsg(FALSE, String("Invalid creature bodytype %d", pSoldier->ubBodyType));
1863 return;
1864 }
1865
1866 // decide if the creature will drop any REAL bodyparts
1867 if (Random(100) < uiChanceToDrop)
1868 {
1869 CreateItem( (UINT16)(fBloodcat ? BLOODCAT_CLAWS : CREATURE_PART_CLAWS), (INT8) (80 + Random(21)), &(pSoldier->inv[BIGPOCK1POS]) );
1870 }
1871
1872 if (Random(100) < uiChanceToDrop)
1873 {
1874 CreateItem( (UINT16)(fBloodcat ? BLOODCAT_TEETH : CREATURE_PART_FLESH), (INT8) (80 + Random(21)), &(pSoldier->inv[BIGPOCK2POS]) );
1875 }
1876
1877 // as requested by ATE, males are more likely to drop their "organs" (he actually suggested this, I'm serious!)
1878 if (fMaleCreature)
1879 {
1880 // increase chance by 50%
1881 uiChanceToDrop += (uiChanceToDrop / 2);
1882 }
1883
1884 if (Random(100) < uiChanceToDrop)
1885 {
1886 CreateItem( (UINT16)(fBloodcat ? BLOODCAT_PELT : CREATURE_PART_ORGAN), (INT8) (80 + Random(21)), &(pSoldier->inv[BIGPOCK3POS]) );
1887 }
1888 }
1889
ReplaceExtendedGuns(SOLDIERCREATE_STRUCT * pp,INT8 bSoldierClass)1890 void ReplaceExtendedGuns( SOLDIERCREATE_STRUCT *pp, INT8 bSoldierClass )
1891 {
1892 UINT32 uiLoop, uiLoop2, uiAttachDestIndex;
1893 INT8 bWeaponClass;
1894 OBJECTTYPE OldObj;
1895 UINT16 usItem, usNewGun, usAmmo, usNewAmmo;
1896
1897 for ( uiLoop = 0; uiLoop < NUM_INV_SLOTS; uiLoop++ )
1898 {
1899 usItem = pp->Inv[ uiLoop ].usItem;
1900 const ItemModel *item = GCM->getItem(usItem);
1901 const WeaponModel *weapon = item->asWeapon();
1902
1903 if (weapon && weapon->isInBigGunList())
1904 {
1905 if ( bSoldierClass == SOLDIER_CLASS_NONE )
1906 {
1907 usNewGun = GCM->getWeaponByName(weapon->getStandardReplacement())->getItemIndex();
1908 }
1909 else
1910 {
1911 bWeaponClass = GetWeaponClass( usItem );
1912 AssertMsg( bWeaponClass != -1, String( "Gun %d does not have a match in the extended gun array", usItem ) );
1913 usNewGun = SelectStandardArmyGun( bWeaponClass );
1914 }
1915
1916 if ( usNewGun != NOTHING )
1917 {
1918 // have to replace! but first (ugh) must store backup (b/c of attachments)
1919 OldObj = pp->Inv[uiLoop];
1920 CreateItem( usNewGun, OldObj.bGunStatus, &(pp->Inv[ uiLoop ]) );
1921 pp->Inv[ uiLoop ].fFlags = OldObj.fFlags;
1922
1923 // copy any valid attachments; for others, just drop them...
1924 if (ItemHasAttachments(OldObj))
1925 {
1926 // we're going to copy into the first attachment position first :-)
1927 uiAttachDestIndex = 0;
1928 // loop!
1929 for ( uiLoop2 = 0; uiLoop2 < MAX_ATTACHMENTS; uiLoop2++ )
1930 {
1931 if ( ( OldObj.usAttachItem[ uiLoop2 ] != NOTHING ) && ValidAttachment( OldObj.usAttachItem[ uiLoop2 ], usNewGun ) )
1932 {
1933 pp->Inv[ uiLoop ].usAttachItem[ uiAttachDestIndex ] = OldObj.usAttachItem[ uiLoop2 ];
1934 pp->Inv[ uiLoop ].bAttachStatus[ uiAttachDestIndex ] = OldObj.bAttachStatus[ uiLoop2 ];
1935 uiAttachDestIndex++;
1936 }
1937 }
1938 }
1939
1940 // must search through inventory and replace ammo accordingly
1941 for ( uiLoop2 = 0; uiLoop2 < NUM_INV_SLOTS; uiLoop2++ )
1942 {
1943 usAmmo = pp->Inv[ uiLoop2 ].usItem;
1944 if ( (GCM->getItem(usAmmo)->isAmmo()) )
1945 {
1946 usNewAmmo = FindReplacementMagazineIfNecessary(item->asWeapon(), usAmmo, GCM->getWeapon(usNewGun) );
1947 if (usNewAmmo != NOTHING )
1948 {
1949 // found a new magazine, replace...
1950 CreateItems( usNewAmmo, 100, pp->Inv[ uiLoop2 ].ubNumberOfObjects, &( pp->Inv[ uiLoop2 ] ) );
1951 }
1952 }
1953 }
1954 }
1955 }
1956 }
1957 }
1958
1959
SelectStandardArmyGun(UINT8 uiGunLevel)1960 static UINT16 SelectStandardArmyGun(UINT8 uiGunLevel)
1961 {
1962 UINT32 uiChoice;
1963 UINT16 usGunIndex;
1964
1965 const std::vector<std::vector<const WeaponModel*> > * gunChoice = NULL;
1966
1967 if (gGameOptions.fGunNut)
1968 {
1969 gunChoice = &GCM->getExtendedGunChoice();
1970 }
1971 else
1972 {
1973 gunChoice = &GCM->getNormalGunChoice();
1974 }
1975
1976 // choose one the of the possible gun choices
1977 uiChoice = Random(static_cast<UINT32>(gunChoice->at(uiGunLevel).size()));
1978 usGunIndex = (gunChoice->at(uiGunLevel)[uiChoice])->getItemIndex();
1979
1980 Assert(usGunIndex);
1981
1982 return(usGunIndex);
1983 }
1984
1985
EquipTank(SOLDIERCREATE_STRUCT * pp)1986 static void EquipTank(SOLDIERCREATE_STRUCT* pp)
1987 {
1988 OBJECTTYPE Object;
1989
1990 // tanks get special equipment, and they drop nothing (MGs are hard-mounted & non-removable)
1991
1992 // main cannon
1993 CreateItem( TANK_CANNON, ( INT8 )( 80 + Random( 21 ) ), &( pp->Inv[ HANDPOS ]) );
1994 pp->Inv[ HANDPOS ].fFlags |= OBJECT_UNDROPPABLE;
1995
1996 // machine gun
1997 CreateItems( MINIMI, ( INT8 )( 80 + Random( 21 ) ), 1, &Object );
1998 Object.fFlags |= OBJECT_UNDROPPABLE;
1999 PlaceObjectInSoldierCreateStruct( pp, &Object );
2000
2001 // tanks don't deplete shells or ammo...
2002 CreateItems( TANK_SHELL, 100, 1, &Object );
2003 Object.fFlags |= OBJECT_UNDROPPABLE;
2004 PlaceObjectInSoldierCreateStruct( pp, &Object );
2005
2006 // armour equal to spectra all over (for vs explosives)
2007 CreateItem( SPECTRA_VEST, 100, &(pp->Inv[ VESTPOS ]) );
2008 pp->Inv[ VESTPOS ].fFlags |= OBJECT_UNDROPPABLE;
2009 CreateItem( SPECTRA_HELMET, 100, &(pp->Inv[ HELMETPOS ]) );
2010 pp->Inv[ HELMETPOS ].fFlags |= OBJECT_UNDROPPABLE;
2011 CreateItem( SPECTRA_LEGGINGS, 100, &(pp->Inv[ LEGPOS ]) );
2012 pp->Inv[ LEGPOS ].fFlags |= OBJECT_UNDROPPABLE;
2013
2014 }
2015
2016
2017
ResetMortarsOnTeamCount(void)2018 void ResetMortarsOnTeamCount( void )
2019 {
2020 guiMortarsRolledByTeam = 0;
2021 }
2022