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