#include #include "Inventory Choosing.h" #include "animation data.h" #include "Items.h" #include "Random.h" #include "Weapons.h" #include "Strategic Status.h" #include "Campaign.h" #include "GameSettings.h" #include "AIInternals.h" #include "strategicmap.h" #include "Auto Resolve.h" #include "Map Screen Interface Map.h" #include "EnemyItemDrops.h" #include "strategic.h" #include "Game Clock.h" #include "message.h" #include "Tactical Save.h" // added by Flugente #include "Soldier macros.h" // added by Flugente #include "Rebel Command.h" extern WorldItems gAllWorldItems; /* #define ENEMYAMMODROPRATE 100 //Madd 50 // % of time enemies drop ammunition #define ENEMYGRENADEDROPRATE 100 //Madd 25 // % of time enemies drop grenades #define ENEMYEQUIPDROPRATE 50 //Madd 15 // % of stuff enemies drop equipment // only 1/10th of what enemies drop... #define MILITIAAMMODROPRATE 100 //Madd 5 // % of time enemies drop ammunition #define MILITIAGRENADEDROPRATE 100 //Madd 3 // % of time enemies drop grenades #define MILITIAEQUIPDROPRATE 50 //Madd 2 // % of stuff enemies drop equipment */ int ENEMYAMMODROPRATE; //100 //Madd 50 // % of time enemies drop ammunition int ENEMYGRENADEDROPRATE; //100 //Madd 25 // % of time enemies drop grenades int ENEMYEQUIPDROPRATE; //50 //Madd 15 // % of stuff enemies drop equipment // only 1/10th of what enemies drop... int MILITIAAMMODROPRATE; //100 //Madd 5 // % of time enemies drop ammunition int MILITIAGRENADEDROPRATE; //100 //Madd 3 // % of time enemies drop grenades int MILITIAEQUIPDROPRATE; //50 //Madd 2 // % of stuff enemies drop equipment UINT16 itemMortar; UINT16 itemGrenadeLauncher; UINT16 itemLAW; UINT16 itemRPG; UINT32 guiMortarsRolledByTeam = 0; // // WDS note 08/06/2008: This is no longer used // ARMY_GUN_CHOICE_TYPE gRegularArmyGunChoices[ARMY_GUN_LEVELS] = // { // INDEX CLASS #CHOICES // { /* 0 - lo pistols */ 2, SW38, DESERTEAGLE, -1, -1, -1 }, // { /* 1 - hi pistols */ 2, GLOCK_17, BERETTA_93R, -1, -1, -1 }, // { /* 2 - lo SMG/shotgun */ 2, M870, MP5K, -1, -1, -1 }, // { /* 3 - lo rifles */ 1, MINI14, -1, -1, -1, -1 }, // { /* 4 - hi SMGs */ 2, MAC10, COMMANDO, -1, -1, -1 }, // { /* 5 - med rifles */ 1, G41, -1, -1, -1, -1 }, // { /* 6 - sniper rifles */ 1, M24, -1, -1, -1, -1 }, // { /* 7 - hi rifles */ 2, M14, C7, -1, -1, -1 }, // { /* 8 - best rifle */ 1, FNFAL, -1, -1, -1, -1 }, // { /* 9 - machine guns */ 1, MINIMI, -1, -1, -1, -1 }, // { /* 10- rocket rifle */ 2, ROCKET_RIFLE, MINIMI, -1, -1, -1 }, // }; // Flugente: created separate gun choices for different soldier classes ARMY_GUN_CHOICE_TYPE gExtendedArmyGunChoices[SOLDIER_GUN_CHOICE_SELECTIONS][ARMY_GUN_LEVELS];// = //{ // INDEX CLASS #CHOICES // { /* 0 - lo pistols */ 6, SW38, BARRACUDA, DESERTEAGLE, GLOCK_17, M1911, BERETTA_92F,-1,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 }, // { /* 1 - hi pist/shtgn */ 6, GLOCK_18, BERETTA_93R, P7M8, M870, M950 ,FIVE7,-1,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 }, // { /* 2 - lo SMGs/shtgn */ 6, TYPE85, THOMPSON, MP5, MP53, MP5K, MP5SD,-1,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, // { /* 3 - lo rifles */ 4, MINI14, SKS, BENNELLI, SPAS15, -1 ,-1,-1,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 }, // { /* 4 - hi SMGs */ 7, MAC10, AKSU74, MICRO_UZI, COMMANDO, M960, UMP45 ,MP5N,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 }, // { /* 5 - med rifles */ 6, AKM, G3A3, G41, AK74, M900 ,AK103,-1,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 }, // { /* 6 - sniper rifles */ 6, DRAGUNOV, M24, PSG, VAL_SILENT, BARRETT ,MSG90A1,-1,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 }, // { /* 7 - hi rifles */ 7, FAMAS, M14, AUG, C7, ENFIELD, G36K,G36C,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 }, // { /* 8 - best rifle */ 4, FNFAL, P90, G11, TAR21, -1, -1,-1,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 }, // { /* 9 - machine guns */ 3, MINIMI, RPK74, HK21E, -1, -1,-1,-1,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 }, // { /* 10- rocket rifle */ 5, ROCKET_RIFLE, AUTO_ROCKET_RIFLE , RPK74, HK21E, MINIMI , -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, //}; // Flugente: created separate gun choices for different soldier classes ARMY_GUN_CHOICE_TYPE gArmyItemChoices[SOLDIER_GUN_CHOICE_SELECTIONS][MAX_ITEM_TYPES]; void RandomlyChooseWhichItemsAreDroppable( SOLDIERCREATE_STRUCT *pp, INT8 bSoldierClass ); void EquipArmouredVehicle( SOLDIERCREATE_STRUCT *pp ); void ChooseKitsForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bKitClass ); void ChooseMiscGearForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bMiscClass ); void ChooseBombsForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bBombClass ); // Headrock: Added function definition for LBE chooser void ChooseLBEsForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bLBEClass ); UINT16 PickARandomItem(UINT8 typeIndex, INT8 bSoldierClass); UINT16 PickARandomItem(UINT8 typeIndex, INT8 bSoldierClass, UINT8 wantedCoolness); UINT16 PickARandomItem(UINT8 typeIndex, INT8 bSoldierClass, UINT8 wantedCoolness, BOOLEAN getMatchingCoolness); UINT16 PickARandomAttachment(UINT8 typeIndex, INT8 bSoldierClass, UINT16 usBaseItem, UINT8 maxCoolness, BOOLEAN getMatchingCoolness); void InitArmyGunTypes(void) { ARMY_GUN_CHOICE_TYPE *pGunChoiceTable; UINT32 uiGunLevel; UINT32 uiChoice; UINT16 bItemNo; UINT16 ubWeapon; DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"InitArmyGunTypes "); DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"InitArmyGunTypes set gun choice table"); // depending on selection of the gun nut option //if (gGameOptions.fGunNut) //{ // use table of extended gun choices pGunChoiceTable = &(gExtendedArmyGunChoices[SOLDIER_CLASS_NONE][0]); //} //else //{ // // use table of regular gun choices // pGunChoiceTable = &(gRegularArmyGunChoices[0]); //} DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"InitArmyGunTypes pick a gun type"); // for each gun category for (uiGunLevel = 0; uiGunLevel < ARMY_GUN_LEVELS; uiGunLevel++) { // choose one the of the possible gun choices to be used by the army for this game & store it uiChoice = Random(pGunChoiceTable[ uiGunLevel ].ubChoices); bItemNo = pGunChoiceTable[ uiGunLevel ].bItemNo[ uiChoice ]; AssertMsg(bItemNo != -1, "Invalid army gun choice in table"); gStrategicStatus.ubStandardArmyGunIndex[uiGunLevel] = (UINT8) bItemNo; } DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"InitArmyGunTypes mark weapons as not dropped"); // set all flags that track whether this weapon type has been dropped before to FALSE for (ubWeapon = 0; ubWeapon < MAXITEMS; ++ubWeapon) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("InitArmyGunTypes marking weapons %d", ubWeapon)); gStrategicStatus.fWeaponDroppedAlready[ubWeapon] = FALSE; } DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"InitArmyGunTypes marking 38s as dropped"); // avoid auto-drops for the gun class with the crappiest guns in it MarkAllWeaponsOfSameGunClassAsDropped( SW38 ); DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"InitArmyGunTypes done"); } INT8 GetWeaponClass( UINT16 usGun ) { UINT32 uiGunLevel, uiLoop; // always use the extended list since it contains all guns... if ( IsWeapon (usGun) ) { for (uiGunLevel = 0; uiGunLevel < ARMY_GUN_LEVELS; uiGunLevel++) { for ( uiLoop = 0; uiLoop < gExtendedArmyGunChoices[SOLDIER_CLASS_NONE][ uiGunLevel ].ubChoices; uiLoop++ ) { if ( gExtendedArmyGunChoices[SOLDIER_CLASS_NONE][ uiGunLevel ].bItemNo[ uiLoop ] == usGun ) { return( (INT8) uiGunLevel ); } } } } return( -1 ); } void MarkAllWeaponsOfSameGunClassAsDropped( UINT16 usWeapon ) { INT8 bGunClass; UINT32 uiLoop; DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"MarkAllWeaponsOfSameGunClassAsDropped"); // mark that item itself as dropped, whether or not it's part of a gun class gStrategicStatus.fWeaponDroppedAlready[ usWeapon ] = TRUE; bGunClass = GetWeaponClass( usWeapon ); // if the gun belongs to a gun class (mortars, GLs, LAWs, etc. do not and are handled independently) if ( bGunClass != -1 ) { // then mark EVERY gun in that class as dropped for ( uiLoop = 0; uiLoop < gExtendedArmyGunChoices[SOLDIER_CLASS_NONE][ bGunClass ].ubChoices; uiLoop++ ) { gStrategicStatus.fWeaponDroppedAlready[ gExtendedArmyGunChoices[SOLDIER_CLASS_NONE][ bGunClass ].bItemNo[ uiLoop ] ] = TRUE; } } DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"MarkAllWeaponsOfSameGunClassAsDropped done"); } //Chooses equipment based on the relative equipment level (0-4) with best being 4. It allocates a range //of equipment to choose from. //NOTE: I'm just winging it for the decisions on which items that different groups can have. Basically, // there are variations, so a guy at a certain level may get a better gun and worse armour or vice versa. void GenerateRandomEquipment( SOLDIERCREATE_STRUCT *pp, INT8 bSoldierClass, INT8 bEquipmentRating ) { DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("GenerateRandomEquipment")); OBJECTTYPE *pItem; //general rating information INT8 bRating = 0; //numbers of items INT8 bAmmoClips = 0; INT8 bGrenades = 0; BOOLEAN fAttachment = FALSE; //item levels INT8 bWeaponClass = 0; INT8 bHelmetClass = 0; INT8 bVestClass = 0; INT8 bLeggingClass = 0; INT8 bAttachClass = 0; INT8 bGrenadeClass = 0; INT8 bKnifeClass = 0; INT8 bKitClass = 0; INT8 bMiscClass = 0; INT8 bBombClass = 0; // Headrock: Added Zeroed LBE integer INT8 bLBEClass = 0; //special weapons BOOLEAN fMortar = FALSE; BOOLEAN fGrenadeLauncher = FALSE; BOOLEAN fLAW = FALSE; BOOLEAN fRPG = FALSE; UINT32 i; INT8 bEquipmentModifier; UINT8 ubMaxSpecialWeaponRoll; Assert( pp ); // kids don't get anything 'cause they don't have any weapon animations and the rest is inappropriate if ( ( pp->ubBodyType == HATKIDCIV ) || ( pp->ubBodyType == KIDCIV ) ) { return; } if ( ARMED_VEHICLE( pp ) || ENEMYROBOT( pp ) ) { EquipArmouredVehicle( pp ); return; } // rftr: enemy tanks, jeeps, and robots would have early exited above Assert( (bSoldierClass >= SOLDIER_CLASS_NONE) && (bSoldierClass <= SOLDIER_CLASS_ELITE_MILITIA) ); Assert( ( bEquipmentRating >= 0 ) && ( bEquipmentRating <= 4 ) ); // equipment level is modified by 1/10 of the difficulty percentage, -5, so it's between -5 to +5 // (on normal, this is actually -4 to +4, easy is -5 to +3, and hard is -3 to +5) //bEquipmentModifier = bEquipmentRating + ( ( CalcDifficultyModifier( bSoldierClass ) / 10 ) - 5 ); // Lesh 1.13 mod notes // My idea of enemy choice balancing is based on bEquipmentModifier, which is influenced by // game progress and difficulty. You can see that I throw out bEquipmentRating from the formulae. // This variable has a very big influence to result. Also as you can see below I commented // out such constants as BAD_ADMINISTRATOR_EQUIPMENT_RATING. They have big influence too, // maybe because of enemy choices table was incorrectly assembled. For example: // BAD_ADMINISTRATOR_EQUIPMENT_RATING is equal 1 and BAD_ELITE_EQUIPMENT_RATING is equal 5. // So in the same time when admins hold crappy weapons, elites get stuff up to nearly 6 level // in Drassen counter-attack. In this situation it is very hard to make balanced xml-file(IMHO). // Made in optional form // SANDRO - new behaviour of progress setting bEquipmentModifier = bEquipmentRating + ( ( CalcDifficultyModifier( bSoldierClass ) / 10 ) - 5 ); if (bSoldierClass >= SOLDIER_CLASS_ADMINISTRATOR && bSoldierClass <= SOLDIER_CLASS_ARMY) bEquipmentModifier += RebelCommand::GetEnemyEquipmentCoolnessModifier(); switch( gGameOptions.ubProgressSpeedOfItemsChoices ) { case ITEM_PROGRESS_VERY_SLOW: // On "Very Slow", at the beginning of the game, the modifier is -5 and goes up to -1 at +-100% game progress bEquipmentModifier -= (1 + (4 - (HighestPlayerProgressPercentage() / 25) )); break; case ITEM_PROGRESS_SLOW: // On "Slow", it's -3 until hit at least 10% game progress, then -2 till 40%, then -1 till 80% if (HighestPlayerProgressPercentage() < 10 ) bEquipmentModifier -= 3; else if (HighestPlayerProgressPercentage() < 40 ) bEquipmentModifier -= 2; else if (HighestPlayerProgressPercentage() < 80 ) bEquipmentModifier -= 1; break; case ITEM_PROGRESS_NORMAL: // Nothing needed here break; case ITEM_PROGRESS_FAST: // On "Fast", it's just +1 at the beginning and rise up through out the game if (HighestPlayerProgressPercentage() < 25 ) bEquipmentModifier += 1; else if (HighestPlayerProgressPercentage() < 60 ) bEquipmentModifier += 2; else if (HighestPlayerProgressPercentage() < 90 ) bEquipmentModifier += 3; else bEquipmentModifier += 4; break; case ITEM_PROGRESS_VERY_FAST: // "Very Fast" just make everyone loaded with top gear quickly if (HighestPlayerProgressPercentage() < 10 ) bEquipmentModifier += 2; else if (HighestPlayerProgressPercentage() < 25 ) bEquipmentModifier += 3; else if (HighestPlayerProgressPercentage() < 50 ) bEquipmentModifier += 4; else if (HighestPlayerProgressPercentage() < 80 ) bEquipmentModifier += 5; else bEquipmentModifier += 6; break; default: break; } switch( bSoldierClass ) { case SOLDIER_CLASS_NONE: // ammo is here only so that civilians with pre-assigned ammo will get some clips for it! bAmmoClips = (INT8)(2 + Random( 2 )); // civilians get nothing, anyone who should get something should be preassigned by Linda break; // SANDRO - I've split the item choosing below, so the item choices for militia can be // handled differently than for enemies. case SOLDIER_CLASS_ADMINISTRATOR: case SOLDIER_CLASS_GREEN_MILITIA: case SOLDIER_CLASS_BANDIT: bRating = BAD_ADMINISTRATOR_EQUIPMENT_RATING + bEquipmentModifier; // Equipment quality modifier if ( bSoldierClass == SOLDIER_CLASS_ADMINISTRATOR || bSoldierClass == SOLDIER_CLASS_BANDIT ) { if ( gGameExternalOptions.sEnemyAdminEquipmentQualityModifier != 0 ) bRating += gGameExternalOptions.sEnemyAdminEquipmentQualityModifier; } else if ( bSoldierClass == SOLDIER_CLASS_GREEN_MILITIA ) { // SANDRO - Militia equipment quality bonus if ( gGameExternalOptions.bGreenMilitiaEquipmentQualityModifier != 0 ) bRating += gGameExternalOptions.bGreenMilitiaEquipmentQualityModifier; } bRating = (INT8)max( MIN_EQUIPMENT_CLASS, min( MAX_EQUIPMENT_CLASS, bRating ) ); bWeaponClass = bRating; //Headrocktest, remove for release bLBEClass = bRating; //Note: in some cases the class of armour and/or helmet won't be high enough to make // the lowest level. bVestClass = bRating; bHelmetClass = bRating; // no leggings if ( bSoldierClass == SOLDIER_CLASS_ADMINISTRATOR || bSoldierClass == SOLDIER_CLASS_BANDIT ) { if ( Chance( 30 ) ) bKnifeClass = bRating; } else if ( bSoldierClass == SOLDIER_CLASS_GREEN_MILITIA ) { if ( Chance( 15 ) ) bKnifeClass = bRating; } bAmmoClips = (INT8)(3 + Random( 2 )); if( bRating >= GOOD_ADMINISTRATOR_EQUIPMENT_RATING ) { bAmmoClips++; bKitClass = bRating; bMiscClass = bRating; // Headrock: Low Level LBEs possible on Administrators: bLBEClass = bRating; } if( bRating >= GREAT_ADMINISTRATOR_EQUIPMENT_RATING ) { bGrenades = 1, bGrenadeClass = bRating; } if( ( bRating > MIN_EQUIPMENT_CLASS ) && bRating < MAX_EQUIPMENT_CLASS ) { //Randomly decide if there is to be an upgrade of guns vs armour (one goes up, the other down) switch( Random( 5 ) ) { case 0: bWeaponClass++, bVestClass--; break; //better gun, worse armour case 1: bWeaponClass--, bVestClass++; break; //worse gun, better armour } } break; case SOLDIER_CLASS_ARMY: case SOLDIER_CLASS_REG_MILITIA: //army guys tend to have a broad range of equipment //if ( gGameOptions.fSlowProgressForEnemyItemsChoice ) // bRating = bEquipmentModifier - ( bSoldierClass == SOLDIER_CLASS_ARMY ); //else bRating = BAD_ARMY_EQUIPMENT_RATING + bEquipmentModifier; // Equipment quality modifier if ( bSoldierClass == SOLDIER_CLASS_ARMY ) { if ( gGameExternalOptions.sEnemyRegularEquipmentQualityModifier != 0 ) bRating += gGameExternalOptions.sEnemyRegularEquipmentQualityModifier; } else if ( bSoldierClass == SOLDIER_CLASS_REG_MILITIA ) { // SANDRO - Militia equipment quality bonus if ( gGameExternalOptions.bRegularMilitiaEquipmentQualityModifier != 0 ) bRating += gGameExternalOptions.bRegularMilitiaEquipmentQualityModifier; } bRating = (INT8)max( MIN_EQUIPMENT_CLASS, min( MAX_EQUIPMENT_CLASS, bRating ) ); bWeaponClass = bRating; bVestClass = bRating; bHelmetClass = bRating; bGrenadeClass = bRating; // Headrock: Added LBE set to Coolness Rating bLBEClass = bRating; //WarmSteel - attachments don't need to be as high a class, controversional and might be better to externalize? bAttachClass = bRating*3/8; if( ( bRating >= GOOD_ARMY_EQUIPMENT_RATING ) && Chance(33) ) { fAttachment = TRUE; bAttachClass = bRating*5/8; } bAmmoClips = (INT8)(3 + Random( 2 )); if( bRating >= AVERAGE_ARMY_EQUIPMENT_RATING ) { bGrenades = (INT8)Random( 2 ); bKitClass = bRating; bMiscClass = bRating; } if( bRating >= GOOD_ARMY_EQUIPMENT_RATING ) { bGrenades++; } if( bRating >= GREAT_ARMY_EQUIPMENT_RATING ) { bGrenades++; bLeggingClass = bRating; if ( Chance( 25 ) ) { bBombClass = bRating; } } if ( bSoldierClass == SOLDIER_CLASS_ARMY ) { if ( Chance( 35 ) ) bKnifeClass = bRating; } else if ( bSoldierClass == SOLDIER_CLASS_REG_MILITIA ) { if ( Chance( 20 ) ) bKnifeClass = bRating; } // Headrock: Chance for soldier to carry better LBE //if( Chance( 50 ) ) // bLBEClass++; if( ( bRating > MIN_EQUIPMENT_CLASS ) && bRating < MAX_EQUIPMENT_CLASS ) { switch( Random( 7 ) ) { case 3: bWeaponClass++, bVestClass--; break; //better gun, worse armour case 4: bWeaponClass--, bVestClass++; break; //worse gun, better armour case 5: bVestClass++, bHelmetClass--; break; //better armour, worse helmet case 6: bVestClass--, bHelmetClass++; break; //worse armour, better helmet } } // if well-enough equipped, 1/5 chance of something really special if( ( bRating >= GREAT_ARMY_EQUIPMENT_RATING ) && ( Random( 100 ) < 20 ) ) { //give this man a special weapon! No mortars if underground, however ubMaxSpecialWeaponRoll = ( !IsAutoResolveActive() && ( gbWorldSectorZ != 0 ) ) ? 6 : 7; switch ( Random ( ubMaxSpecialWeaponRoll ) ) { case 0: case 1: case 2: if ( pp->bExpLevel >= 3 ) { //grenade launcher fGrenadeLauncher = TRUE; bGrenades = 3 + (INT8)(Random( 3 )); //3-5 } break; case 3: case 4: if ( pp->bExpLevel >= 4 ) { // LAW rocket launcher fLAW = TRUE; } break; case 5: if ( pp->bExpLevel >= 5 ) { // RPG rocket launcher fRPG = TRUE; bGrenades = 2 + (INT8)(Random( 3 )); //2-4 bGrenadeClass = RPG_GRENADE_CLASS; } break; case 6: // one per team maximum! if ( bSoldierClass != SOLDIER_CLASS_REG_MILITIA && ( pp->bExpLevel >= 5 ) && (guiMortarsRolledByTeam < zDiffSetting[gGameOptions.ubDifficultyLevel].usMaxMortarsPerTeam) ) { //mortar fMortar = TRUE; guiMortarsRolledByTeam++; // the grenades will actually represent mortar shells in this case bGrenades = 2 + (INT8)(Random( 3 )); //2-4 bGrenadeClass = MORTAR_GRENADE_CLASS; } break; } } break; case SOLDIER_CLASS_ELITE: case SOLDIER_CLASS_ELITE_MILITIA: //if ( gGameOptions.fSlowProgressForEnemyItemsChoice ) // bRating = bEquipmentModifier - ( bSoldierClass == SOLDIER_CLASS_ELITE ); //else bRating = BAD_ELITE_EQUIPMENT_RATING + bEquipmentModifier; // Equipment quality modifier if ( bSoldierClass == SOLDIER_CLASS_ELITE ) { if ( gGameExternalOptions.sEnemyEliteEquipmentQualityModifier != 0 ) bRating += gGameExternalOptions.sEnemyEliteEquipmentQualityModifier; } else if ( bSoldierClass == SOLDIER_CLASS_ELITE_MILITIA ) { // SANDRO - Militia equipment quality bonus if ( gGameExternalOptions.bVeteranMilitiaEquipmentQualityModifier != 0 ) bRating += gGameExternalOptions.bVeteranMilitiaEquipmentQualityModifier; } bRating = (INT8)max( MIN_EQUIPMENT_CLASS, min( MAX_EQUIPMENT_CLASS, bRating ) ); bWeaponClass = bRating; bHelmetClass = bRating; bVestClass = bRating; bLeggingClass = bRating; bAttachClass = bRating*7/8; bGrenadeClass = bRating; bKitClass = bRating; bMiscClass = bRating; // Headrock: Elite LBEs bLBEClass = bRating; if ( Chance( 25 ) ) { bBombClass = bRating; } bAmmoClips = (INT8)(3 + Random( 2 )); bGrenades = (INT8)(2 + Random( 3 )); if( ( bRating >= AVERAGE_ELITE_EQUIPMENT_RATING ) && Chance(75) ) { fAttachment = TRUE; bAttachClass = bRating; } if ( bSoldierClass == SOLDIER_CLASS_ELITE ) { if ( Chance( 35 ) ) bKnifeClass = bRating; } else if ( bSoldierClass == SOLDIER_CLASS_ELITE_MILITIA ) { if ( Chance( 25 ) ) bKnifeClass = bRating; } if( ( bRating > MIN_EQUIPMENT_CLASS ) && bRating < MAX_EQUIPMENT_CLASS ) { UINT32 uiRange = ((UsingNewInventorySystem() == false)) ? Random(11) : Random(12); switch( uiRange ) { case 4: bWeaponClass++, bVestClass--; break; case 5: bWeaponClass--, bVestClass--; break; case 6: bVestClass++, bHelmetClass--; break; case 7: bGrenades += 2; break; case 8: bHelmetClass++; break; case 9: bVestClass++; break; case 10: bWeaponClass++; break; case 11: bLBEClass++; break; } } if ( bSoldierClass == SOLDIER_CLASS_ELITE ) { // if well-enough equipped, 1/3 chance of something really special if ( (bRating >= GOOD_ELITE_EQUIPMENT_RATING) && (Random( 100 ) < 33) ) { //give this man a special weapon! No mortars if underground, however ubMaxSpecialWeaponRoll = (!IsAutoResolveActive( ) && (gbWorldSectorZ != 0)) ? 6 : 7; switch ( Random( ubMaxSpecialWeaponRoll ) ) { case 0: case 1: case 2: //grenade launcher fGrenadeLauncher = TRUE; bGrenades = 4 + (INT8)(Random( 4 )); //4-7 break; case 3: case 4: // LAW rocket launcher fLAW = TRUE; break; case 5: // RPG rocket launcher fRPG = TRUE; bGrenades = 3 + (INT8)(Random( 5 )); //3-7 bGrenadeClass = RPG_GRENADE_CLASS; break; case 6: // one per team maximum! if ( guiMortarsRolledByTeam < zDiffSetting[gGameOptions.ubDifficultyLevel].usMaxMortarsPerTeam ) { //mortar fMortar = TRUE; ++guiMortarsRolledByTeam; // the grenades will actually represent mortar shells in this case bGrenades = 3 + (INT8)(Random( 5 )); //3-7 bGrenadeClass = MORTAR_GRENADE_CLASS; } break; } } } else if ( bSoldierClass == SOLDIER_CLASS_ELITE_MILITIA ) { // if well-enough equipped, 1/3 chance of something really special if ( (bRating >= GOOD_ELITE_EQUIPMENT_RATING) && (Random( 100 ) < 60) ) { //give this man a special weapon! No mortars if underground, however ubMaxSpecialWeaponRoll = (!IsAutoResolveActive( ) && (gbWorldSectorZ != 0)) ? 4 : 5; switch ( Random( ubMaxSpecialWeaponRoll ) ) { case 0: //grenade launcher fGrenadeLauncher = TRUE; bGrenades = 4 + (INT8)(Random( 4 )); //4-7 break; case 1: case 2: // LAW rocket launcher fLAW = TRUE; break; case 3: // RPG rocket launcher fRPG = TRUE; bGrenades = 3 + (INT8)(Random( 5 )); //3-7 bGrenadeClass = RPG_GRENADE_CLASS; break; case 4: // one per team maximum! if ( guiMortarsRolledByTeam < zDiffSetting[gGameOptions.ubDifficultyLevel].usMaxMortarsPerTeam ) { //mortar fMortar = TRUE; ++guiMortarsRolledByTeam; // the grenades will actually represent mortar shells in this case bGrenades = 3 + (INT8)(Random( 5 )); //3-7 bGrenadeClass = MORTAR_GRENADE_CLASS; } else { // RPG rocket launcher fRPG = TRUE; bGrenades = 3 + (INT8)(Random( 5 )); //3-7 bGrenadeClass = RPG_GRENADE_CLASS; } break; } } } case SOLDIER_CLASS_ZOMBIE: // zombies get no items at all... default: break; } UINT32 invsize = pp->Inv.size(); for( i = 0; i < invsize; ++i ) { //clear items, but only if they have write status. if( !(pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE) ) { DeleteObj(&pp->Inv[ i ]); //ADB this is ok, delete only inits it pp->Inv[ i ].fFlags |= OBJECT_UNDROPPABLE; } else { //check to see what kind of item is here. If we find a gun, for example, it'll make the //bWeaponClass negative to signify that a gun has already been specified, and later //code will use that to determine that and to pick ammo for it. pItem = &pp->Inv[ i ]; if( !pItem ) continue; switch( Item[ pItem->usItem ].usItemClass ) { case IC_GUN: if ( !ItemIsRocketLauncher(pItem->usItem) ) { bWeaponClass *= -1; } else // rocket launcher! { fLAW = FALSE; } (*pItem)[0]->data.gun.ubGunState |= GS_CARTRIDGE_IN_CHAMBER; break; case IC_AMMO: bAmmoClips = 0; break; case IC_BLADE: case IC_THROWING_KNIFE: bKnifeClass = 0; break; case IC_LAUNCHER: (*pItem)[0]->data.gun.ubGunState |= GS_CARTRIDGE_IN_CHAMBER; fGrenadeLauncher = FALSE; if (fMortar || fRPG) { bGrenadeClass = 0; bGrenades = 0; } fMortar = FALSE; fRPG = FALSE; break; case IC_ARMOUR: if( i == HELMETPOS ) bHelmetClass = 0; else if( i == VESTPOS ) bVestClass = 0; else if( i == LEGPOS ) bLeggingClass = 0; break; case IC_GRENADE: bGrenades = 0; bGrenadeClass = 0; break; case IC_MEDKIT: case IC_KIT: bKitClass = 0; break; case IC_MISC: bMiscClass = 0; case IC_BOMB: bBombClass = 0; break; // Headrock: Added failsafe for LBEs case IC_LBEGEAR: bLBEClass = 0; break; } } } // special: militia shouldn't drop bombs if ( !( SOLDIER_CLASS_ENEMY( bSoldierClass ) ) ) { bBombClass = 0; } //Now actually choose the equipment! ChooseWeaponForSoldierCreateStruct( pp, bWeaponClass, bAmmoClips, bAttachClass, fAttachment ); // Headrock: This is where the program calls LBE choosing if ( SOLDIER_CLASS_ENEMY( bSoldierClass )) // BUT ONLY FOR ENEMIES, NOT MILITIA - SANDRO { if (Chance(50)) // Make EDB gear rare ChooseLBEsForSoldierCreateStruct( pp, bLBEClass ); } ChooseSpecialWeaponsForSoldierCreateStruct( pp, bKnifeClass, fGrenadeLauncher, fLAW, fMortar, fRPG ); ChooseGrenadesForSoldierCreateStruct( pp, bGrenades, bGrenadeClass, fGrenadeLauncher ); ChooseArmourForSoldierCreateStruct( pp, bHelmetClass, bVestClass, bLeggingClass ); ChooseFaceGearForSoldierCreateStruct( pp ); if (Chance(65)) { ChooseKitsForSoldierCreateStruct( pp, bKitClass ); } // Kits not so often - SANDRO ChooseMiscGearForSoldierCreateStruct( pp, bMiscClass ); ChooseBombsForSoldierCreateStruct( pp, bBombClass ); //ADB why is this here twice? // Headrock: This is where the program calls LBE choosing //ChooseLBEsForSoldierCreateStruct( pp, bLBEClass ); // SANDRO - removed duplicated ChooseLocationSpecificGearForSoldierCreateStruct( pp ); RandomlyChooseWhichItemsAreDroppable( pp, bSoldierClass ); DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("GenerateRandomEquipment done")); } //When using the class values, they should all range from 0-11, 0 meaning that there will be no //selection for that particular type of item, and 1-11 means to choose an item if possible. 1 is //the worst class of item, while 11 is the best. void ChooseWeaponForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bWeaponClass, INT8 bAmmoClips, INT8 bAttachClass, BOOLEAN fAttachment ) { //INVTYPE *pItem; UINT16 i; //UINT16 usRandom; UINT16 usGunIndex = 0; UINT16 usAmmoIndex = 0; UINT16 usAttachIndex = 0; UINT16 usAttachIndex2 = 0; UINT16 usScopeIndex = 0; UINT8 ubChanceStandardAmmo; INT8 bStatus; DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"ChooseWeaponForSoldierCreateStruct"); // Choose weapon: // WEAPONS are very important, and are therefore handled differently using special pre-generated tables. // It was requested that enemies use only a small subset of guns with a lot duplication of the same gun type. // if gun was pre-selected (rcvd negative weapon class) and needs ammo if( bWeaponClass < 0 && bAmmoClips ) { //Linda has added a specific gun to the merc's inventory, but no ammo. So, we //will choose ammunition that works with the gun. UINT16 invsize = pp->Inv.size(); for( i = 0; i < invsize; ++i ) { if( Item[ pp->Inv[ i ].usItem ].usItemClass == IC_GUN && pp->Inv[ i ].exists() == true) { usGunIndex = pp->Inv[ i ].usItem; ubChanceStandardAmmo = 100 - (bWeaponClass * -9); // weapon class is negative! usAmmoIndex = RandomMagazine( usGunIndex, ubChanceStandardAmmo, max(Item[usGunIndex].ubCoolness, HighestPlayerProgressPercentage() / 10 + 3), pp->ubSoldierClass); if ( usAmmoIndex <= 0 ) usAmmoIndex = DefaultMagazine(usGunIndex); pp->Inv[ i ][0]->data.gun.ubGunAmmoType = Magazine[Item[usAmmoIndex].ubClassIndex].ubAmmoType; pp->Inv[ i ][0]->data.gun.usGunAmmoItem = usAmmoIndex; if (ItemHasFingerPrintID(usGunIndex)) { pp->Inv[ i ][0]->data.ubImprintID = (NO_PROFILE + 1); } break; } } if( bAmmoClips && usAmmoIndex ) { CreateItems( usAmmoIndex, 100, bAmmoClips, &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); } return; } if (bWeaponClass < 1) return; //empty handed / pre-selected // reduce anything over 11 to 11 if (bWeaponClass > ARMY_GUN_LEVELS) { bWeaponClass = ARMY_GUN_LEVELS; } // the weapon class here ranges from 1 to 11, so subtract 1 to get a gun level usGunIndex = SelectStandardArmyGun( (UINT8) (bWeaponClass - 1), pp->ubSoldierClass ); // Flugente: random items UINT16 newitemfromrandom = 0; if ( GetItemFromRandomItem(usGunIndex, &newitemfromrandom) ) usGunIndex = newitemfromrandom; // if item is still random, use default gun -> GLOCK 17 if ( Item[usGunIndex].randomitem > 0 ) usGunIndex = GLOCK_17; //WarmSteel - Removed the attachment check here. why check twice anyway? //Now, we have chosen all of the correct items. Now, we will assign them into the slots. //Because we are dealing with enemies, automatically give them full ammo in their weapon. switch( pp->ubSoldierClass ) { case SOLDIER_CLASS_ADMINISTRATOR: case SOLDIER_CLASS_ARMY: case SOLDIER_CLASS_GREEN_MILITIA: case SOLDIER_CLASS_REG_MILITIA: case SOLDIER_CLASS_BANDIT: //Admins/Troops: 60-75% + 1% every 4% progress bStatus = (INT8)(60 + Random( 16 )); bStatus += (INT8)(HighestPlayerProgressPercentage() / 4); bStatus = (INT8)min( 100, bStatus ); break; case SOLDIER_CLASS_ELITE: case SOLDIER_CLASS_ELITE_MILITIA: //85-90% + 1% every 10% progress bStatus = (INT8)(85 + Random( 6 )); bStatus += (INT8)(HighestPlayerProgressPercentage() / 10); bStatus = (INT8)min( 100, bStatus ); break; case SOLDIER_CLASS_ZOMBIE: bStatus = 1; // if a zombie gets anything, it'll be broken default: bStatus = (INT8)(50 + Random( 51 ) ); break; } // don't allow it to be lower than marksmanship, we don't want it to affect their chances of hitting bStatus = (INT8)max( pp->bMarksmanship, bStatus ); // ... unless we've done something to ruin their gear bStatus = RebelCommand::GetEnemyEquipmentStatusModifier(bStatus); CreateItem( usGunIndex, bStatus, &(pp->Inv[ HANDPOS ]) ); pp->Inv[ HANDPOS ].fFlags |= OBJECT_UNDROPPABLE; // Rocket Rifles must come pre-imprinted, in case carrier gets killed without getting a shot off if (ItemHasFingerPrintID(usGunIndex)) { pp->Inv[ HANDPOS ][0]->data.ubImprintID = (NO_PROFILE + 1); } DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"ChooseWeaponForSoldierCreateStruct: Gun Created"); if( !(pp->Inv[ HANDPOS ].fFlags & OBJECT_NO_OVERWRITE) ) { //slot not locked, so add attachments to it if(UsingNewAttachmentSystem()==false){ if ( Weapon[usGunIndex].ubWeaponType == GUN_SN_RIFLE ) { usScopeIndex = PickARandomAttachment(SCOPE, pp->ubSoldierClass, usGunIndex,bAttachClass,TRUE); if ( usScopeIndex == 0 ) { // find any one that works usScopeIndex = PickARandomAttachment(SCOPE, pp->ubSoldierClass, usGunIndex,bAttachClass,FALSE); } } if( ValidItemAttachment(&(pp->Inv[ HANDPOS ]),usScopeIndex,TRUE,FALSE) ) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("ChooseWeaponForSoldierCreateStruct: Create Scope %d",usScopeIndex)); CreateItem( usScopeIndex, 100, &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; pp->Inv[ HANDPOS ].AttachObject( NULL, &gTempObject, FALSE ); } else { usScopeIndex = 0; } //Choose attachment if( bAttachClass && ( fAttachment )) usAttachIndex = PickARandomAttachment(ATTACHMENTS, pp->ubSoldierClass, usGunIndex,bAttachClass,FALSE); if( ValidItemAttachment(&(pp->Inv[ HANDPOS ]),usAttachIndex,TRUE,FALSE)) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("ChooseWeaponForSoldierCreateStruct: Create Attachment %d",usAttachIndex)); CreateItem( usAttachIndex, 100, &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; pp->Inv[ HANDPOS ].AttachObject( NULL, &gTempObject, FALSE ); } else { usAttachIndex = 0; } //check for chance of second attachment if ( ( bAttachClass - Item[usAttachIndex].ubCoolness ) > 0 && Random(2) ) usAttachIndex2 = PickARandomAttachment(ATTACHMENTS, pp->ubSoldierClass, usGunIndex,bAttachClass,FALSE); if( ValidItemAttachment(&(pp->Inv[ HANDPOS ]),usAttachIndex2,TRUE,FALSE)) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("ChooseWeaponForSoldierCreateStruct: Create 2nd Attachment %d",usAttachIndex2)); CreateItem( usAttachIndex2, 100, &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; pp->Inv[ HANDPOS ].AttachObject( NULL, &gTempObject, FALSE ); } else { usAttachIndex2 = 0; } } else { //The total pool of coolness we have for all the attachments. If fAttachment is true, the chance of getting an attachment is boosted. INT8 iMiscAttachmentChance = 40 + (fAttachment * 20); //Add a value equal to the avarage amount of attachments that will be on this gun. //Because the guns scope does not subtract from this value, -1. INT16 iAttachmentCoolnessPool = (bAttachClass * (gGameExternalOptions.iMaxEnemyAttachments-1) * iMiscAttachmentChance) / 100; BOOLEAN fFoundValidAttachment = FALSE; BOOLEAN fRandomPassed = FALSE; BOOLEAN fGetMatchingCoolness = FALSE; UINT16 usTryCount = 0; //When using NAS, we want more attachments. if ( Weapon[usGunIndex].ubWeaponType == GUN_SN_RIFLE ) { usScopeIndex = PickARandomAttachment(SCOPE, pp->ubSoldierClass, usGunIndex,bAttachClass,TRUE); if ( usScopeIndex == 0 ) { // find any one that works usScopeIndex = PickARandomAttachment(SCOPE, pp->ubSoldierClass, usGunIndex,bAttachClass,FALSE); } //Guns should have a fairly good chance of having a scope. Even when they're not sniper rifles. //They're likely to be crappier, though. } else if (Chance(75) && fAttachment){ usScopeIndex = PickARandomAttachment(SCOPE, pp->ubSoldierClass, usGunIndex,bAttachClass-1,FALSE); } if( ValidItemAttachmentSlot(&(pp->Inv[ HANDPOS ]),usScopeIndex, TRUE,FALSE) ) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("ChooseWeaponForSoldierCreateStruct: Create Scope %d",usScopeIndex)); CreateItem( usScopeIndex, 100, &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; pp->Inv[ HANDPOS ].AttachObject( NULL, &gTempObject, FALSE ); } //Loop for the maximum amount of attachments (-1 because we can already have a scope by here) //cnt = -1 because we start with ++ing it for(INT16 cnt = -1; cnt < (gGameExternalOptions.iMaxEnemyAttachments-1); ){ usTryCount++; usAttachIndex = 0; //Our coolness pool was depleted, we won't find any more attachments anyway. if(iAttachmentCoolnessPool <= 0) break; //Only move on to the next attachment if 1: we found a good one. 2: randomness decided this attachment won't be. 3: We've exceeded the maximum number of tries. if(fFoundValidAttachment || !fRandomPassed || usTryCount > 20){ cnt++; usTryCount = 0; fFoundValidAttachment = FALSE; //Determine if this attachments deserves to be. fRandomPassed = Chance(iMiscAttachmentChance); //Sometimes allow lower grade attachments. fGetMatchingCoolness = Chance(70); } if (fRandomPassed) usAttachIndex = PickARandomAttachment(ATTACHMENTS, pp->ubSoldierClass, usGunIndex,bAttachClass,fGetMatchingCoolness); if(!usAttachIndex){ continue; } if( ValidItemAttachmentSlot(&(pp->Inv[ HANDPOS ]),usAttachIndex, TRUE,FALSE)) { fFoundValidAttachment = TRUE; DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("ChooseWeaponForSoldierCreateStruct: Created Attachment %d, num: %d",usAttachIndex, cnt)); CreateItem( usAttachIndex, 100, &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; pp->Inv[ HANDPOS ].AttachObject( NULL, &gTempObject, FALSE ); iAttachmentCoolnessPool -= Item[usAttachIndex].ubCoolness; } } } } DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("ChooseWeaponForSoldierCreateStruct: Set bullets")); //set bullets = to magsize including any attachments (c-mag adapters, etc) pp->Inv[ HANDPOS ][0]->data.gun.ubGunShotsLeft = GetMagSize(&pp->Inv[ HANDPOS ]); DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("ChooseWeaponForSoldierCreateStruct: choose ammo")); if( bAmmoClips ) { //We have a gun, so choose ammo clips // check default ammo first... usAmmoIndex = DefaultMagazine( usGunIndex ); if ( AmmoTypes[Magazine[ Item[ usAmmoIndex ].ubClassIndex ].ubAmmoType].standardIssue ) ubChanceStandardAmmo = 80; else ubChanceStandardAmmo = 100 - (bWeaponClass * 9); //switch( Magazine[ Item[ usAmmoIndex ].ubClassIndex ].ubAmmoType ) //{ // case AMMO_AP: // case AMMO_SUPER_AP: // // assault rifle, rocket rifle (etc) - high chance of having AP ammo // ubChanceStandardAmmo = 80; // break; // default: // ubChanceStandardAmmo = 100 - (bWeaponClass * 9); // break; //} usAmmoIndex = RandomMagazine( &pp->Inv[HANDPOS], ubChanceStandardAmmo, max(Item[usGunIndex].ubCoolness, HighestPlayerProgressPercentage() / 10 + 3 ), pp->ubSoldierClass); if ( usAmmoIndex <= 0 ) usAmmoIndex = DefaultMagazine(usGunIndex); pp->Inv[ HANDPOS ][0]->data.gun.ubGunAmmoType = Magazine[Item[usAmmoIndex].ubClassIndex].ubAmmoType; pp->Inv[ HANDPOS ][0]->data.gun.usGunAmmoItem = usAmmoIndex; } //Ammo DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("ChooseWeaponForSoldierCreateStruct: create ammo")); if( bAmmoClips && usAmmoIndex ) { //Madd: ensure a minimum # of bullets to make sure enemies don't run out and run away while ( ( (bAmmoClips - 1) * Weapon[usGunIndex].ubMagSize ) < 20 ) // each soldier should have at least 20 bullets, ie: 2 9mm 15rd clips, 3 7rd shotgun shells, 4 6rd speedloaders, etc. { bAmmoClips++; } CreateItems( usAmmoIndex, 100, bAmmoClips, &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); } DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("ChooseWeaponForSoldierCreateStruct: done")); } void ChooseGrenadesForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bGrenades, INT8 bGrenadeClass, BOOLEAN fGrenadeLauncher ) { INT16 sNumPoints; UINT16 usItem; UINT8 ubBaseQuality; UINT8 ubQualityVariation; //numbers of each type the player will get! UINT8 count = 0; DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"ChooseGrenadesForSoldierCreateStruct"); //determine how many *points* the enemy will get to spend on grenades... sNumPoints = bGrenades * bGrenadeClass; //no points, no grenades! if( !sNumPoints ) return; // special mortar shell handling if (bGrenadeClass == MORTAR_GRENADE_CLASS) { // 0verhaul: itemMortar can be 0 if the previous function // 1) failed to find a Mortar that the soldier would want to use (due to XML prefs) // 2) randomly chose not to supply a Mortar (always possible, even if choices exist). // Since we should not go beyond this point in the case where Mortar Rounds are desired, // return here in any case if (itemMortar > 0 ) { // sevenfm: more variety for mortar shells for (int i = 0; i < bGrenades; i++) { usItem = PickARandomLaunchable(itemMortar); if (usItem > 0) { CreateItems(usItem, (INT8)(80 + Random(21)), 1, &gTempObject); gTempObject.fFlags |= OBJECT_UNDROPPABLE; PlaceObjectInSoldierCreateStruct(pp, &gTempObject); } } } return; } // special rpg rocket handling if (bGrenadeClass == RPG_GRENADE_CLASS) { // 0verhaul: itemRPG can be 0 if the previous function // 1) failed to find an RPG that the soldier would want to use (due to XML prefs) // 2) randomly chose not to supply an RPG (always possible, even if choices exist). // Since we should not go beyond this point in the case where RPG grenades are desired, // return here in any case if (itemRPG > 0 ) { usItem = PickARandomLaunchable ( itemRPG ); if ( usItem > 0 ) { for ( int i = 0; i < bGrenades; i++ ) { CreateItem( usItem, (INT8) (70 + Random(31)), &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); } } } return; } Assert( bGrenadeClass <= 11 ); //determine the quality of grenades. The elite guys get the best quality, while the others //get progressively worse. ubBaseQuality = (UINT8)min( 45 + bGrenadeClass * 5, 90 ); ubQualityVariation = 101 - ubBaseQuality; // Madd: GL guys don't get hand grenades anymore if ( itemGrenadeLauncher > 0 ) { //do this for every 1-2 grenades so that we can get more variety while ( bGrenades > 0 ) { count = min(1 + Random(2), bGrenades); usItem = PickARandomLaunchable ( itemGrenadeLauncher ); if ( usItem > 0 && count > 0 ) { CreateItems( usItem, (INT8)(ubBaseQuality + Random( ubQualityVariation )), count, &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); } bGrenades -= count; } return; } //Madd: screw the original code; it's impossible to externalize and too complicated anyway //do this for every 1-2 grenades so that we can get more variety while ( bGrenades > 0 ) { count = min(1 + Random(2), bGrenades); usItem = PickARandomItem ( GRENADE, pp->ubSoldierClass, bGrenadeClass, FALSE ); if ( usItem > 0 && count > 0 ) { CreateItems( usItem, (INT8)(ubBaseQuality + Random( ubQualityVariation )), count, &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); } bGrenades -= count; } return; #ifdef obsoleteCode ////now, purchase the grenades. //while( sNumPoints > 0 ) //{ // if( sNumPoints >= 20 ) // { //Choose randomly between mustard and regular // if( Random( 2 ) && !fGrenadeLauncher ) // ubNumMustard++, sNumPoints -= 10; // else // ubNumReg++, sNumPoints -= 9; // } // if( sNumPoints >= 10 ) // { //Choose randomly between any // switch( Random( 7 ) ) // { // case 0: if ( !fGrenadeLauncher ) // { // ubNumMustard++; sNumPoints -= 10; break; // } // // if grenade launcher, pick regular instead // case 1: ubNumReg++; sNumPoints -= 9; break; // case 2: if ( !fGrenadeLauncher ) // { // ubNumMini++; sNumPoints -= 7; break; // } // // if grenade launcher, pick tear instead // case 3: ubNumTear++; sNumPoints -= 6; break; // case 4: ubNumStun++; sNumPoints -= 5; break; // case 5: ubNumSmoke++; sNumPoints -= 4; break; // case 6: if (!fGrenadeLauncher ) // { // ubNumFlare++; sNumPoints -= 3; // } // break; // } // } // // JA2 Gold: don't make mini-grenades take all points available, and add chance of break lights // if( sNumPoints >= 1 && sNumPoints < 10 ) // { // switch( Random( 10 ) ) // { // case 0: // case 1: // case 2: // ubNumSmoke++; // sNumPoints -= 4; // break; // case 3: // case 4: // ubNumTear++; // sNumPoints -= 6; // break; // case 5: // case 6: // if (!fGrenadeLauncher) // { // ubNumFlare++; // sNumPoints -= 3; // } // break; // case 7: // case 8: // ubNumStun++; // sNumPoints -= 5; // break; // case 9: // if (!fGrenadeLauncher) // { // ubNumMini++; // sNumPoints -= 7; // } // break; // } // } // /* // if( usNumPoints >= 1 && usNumPoints < 10 ) // { //choose randomly between either stun or tear, (normal with rare chance) // switch( Random( 10 ) ) // { // case 0: // case 1: // case 2: // case 3: // ubNumSmoke++; // if( usNumPoints > 4 ) // usNumPoints -= 4; // else // usNumPoints = 0; // break; // case 4: // case 5: // case 6: // ubNumTear++; // if( usNumPoints > 6 ) // usNumPoints -= 6; // else // usNumPoints = 0; // break; // case 7: // case 8: // ubNumStun++; // if( usNumPoints > 5 ) // usNumPoints -= 5; // else // usNumPoints = 0; // break; // case 9: // ubNumMini++; // usNumPoints = 0; // break; // } // } // */ //} ////Create the grenades and add them to the soldier //if( ubNumSmoke ) //{ // if ( fGrenadeLauncher ) // { // usItem = GL_SMOKE_GRENADE; // } // else // { // usItem = SMOKE_GRENADE; // } // CreateItems( usItem, (INT8)(ubBaseQuality + Random( ubQualityVariation )), ubNumSmoke, &gTempObject ); // gTempObject.fFlags |= OBJECT_UNDROPPABLE; // PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); //} //if( ubNumTear ) //{ // if ( fGrenadeLauncher ) // { // usItem = GL_TEARGAS_GRENADE; // } // else // { // usItem = TEARGAS_GRENADE; // } // CreateItems( usItem, (INT8)(ubBaseQuality + Random( ubQualityVariation )), ubNumTear, &gTempObject ); // gTempObject.fFlags |= OBJECT_UNDROPPABLE; // PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); //} //if( ubNumStun ) //{ // if ( fGrenadeLauncher ) // { // usItem = GL_STUN_GRENADE; // } // else // { // usItem = STUN_GRENADE; // } // CreateItems( usItem, (INT8)(ubBaseQuality + Random( ubQualityVariation )), ubNumStun, &gTempObject ); // gTempObject.fFlags |= OBJECT_UNDROPPABLE; // PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); //} //if( ubNumReg ) //{ // if ( fGrenadeLauncher ) // { // usItem = GL_HE_GRENADE; // } // else // { // usItem = HAND_GRENADE; // } // CreateItems( usItem, (INT8)(ubBaseQuality + Random( ubQualityVariation )), ubNumReg, &gTempObject ); // gTempObject.fFlags |= OBJECT_UNDROPPABLE; // PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); //} //if( ubNumMini ) //{ // CreateItems( MINI_GRENADE, (INT8)(ubBaseQuality + Random( ubQualityVariation )), ubNumMini, &gTempObject ); // gTempObject.fFlags |= OBJECT_UNDROPPABLE; // PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); //} //if( ubNumMustard ) //{ // CreateItems( MUSTARD_GRENADE, (INT8)(ubBaseQuality + Random( ubQualityVariation )), ubNumMustard, &gTempObject ); // gTempObject.fFlags |= OBJECT_UNDROPPABLE; // PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); //} //if( ubNumFlare ) //{ // CreateItems( BREAK_LIGHT, (INT8)(ubBaseQuality + Random( ubQualityVariation )), ubNumFlare, &gTempObject ); // gTempObject.fFlags |= OBJECT_UNDROPPABLE; // PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); //} #endif //obsoleteCode } void ChooseArmourForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bHelmetClass, INT8 bVestClass, INT8 bLeggingsClass ) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"ChooseArmourForSoldierCreateStruct"); //UINT16 i; //INVTYPE *pItem; //UINT16 usRandom; UINT16 usItem = 0, usHelmetItem = 0, usVestItem = 0, usLeggingsItem = 0; INT8 bStatus = 0; //UINT16 usNumMatches; //INT8 bOrigVestClass = bVestClass; // This option wants us to dress soldier up surely. But unfortunately it cannot be guaranteed due to: // * item choises XML can be empty // * a picking is random after all // So we are going to try to pick something other than nothing but not guarantee it. if (gGameExternalOptions.fSoldiersWearAnyArmour) { INT8 i = 0; if (bHelmetClass < 1) bHelmetClass = 1; //search for a non-empty class with items we need for (i = bHelmetClass;i <= 10;i++) { usHelmetItem = PickARandomItem(HELMET, pp->ubSoldierClass, i); //if we find a non-empty class change to that and break if (usHelmetItem > 0) { bHelmetClass = i; break; } } if (bVestClass < 1) bVestClass = 1; //search for a non-empty class with items we need for (i = bVestClass;i <= 10;i++) { usVestItem = PickARandomItem(VEST, pp->ubSoldierClass, i); //if we find a non-empty class change to that and break if (usVestItem > 0) { bVestClass = i; break; } } if (bLeggingsClass < 1) bLeggingsClass = 1; //search for a non-empty class with items we need for (i = bLeggingsClass;i <= 10;i++) { usLeggingsItem = PickARandomItem(LEGS, pp->ubSoldierClass, i); //if we find a non-empty class change to that and break if (usLeggingsItem > 0) { bLeggingsClass = i; break; } } } //Madd: added minimum protection of 10 for armours to be used by enemies //Choose helmet if( bHelmetClass ) { if(!gGameExternalOptions.fSoldiersWearAnyArmour) usHelmetItem = PickARandomItem(HELMET, pp->ubSoldierClass, bHelmetClass ); if ( usHelmetItem > 0 && Item[usHelmetItem].usItemClass == IC_ARMOUR && !(pp->Inv[ HELMETPOS ].fFlags & OBJECT_NO_OVERWRITE) && Armour[ Item[usHelmetItem].ubClassIndex ].ubArmourClass == ARMOURCLASS_HELMET ) { bStatus = RebelCommand::GetEnemyEquipmentStatusModifier(70 + Random(31)); CreateItem( usHelmetItem, bStatus, &(pp->Inv[ HELMETPOS ]) ); pp->Inv[ HELMETPOS ].fFlags |= OBJECT_UNDROPPABLE; // roll to see if he gets an attachment, too. Higher chance the higher his entitled helmet class is if (( INT8 ) Random( 100 ) < ( 15 * ( bHelmetClass - Item[usHelmetItem].ubCoolness ) ) ) { UINT16 usAttachment = PickARandomAttachment(ARMOURATTACHMENT, pp->ubSoldierClass, usHelmetItem, bHelmetClass, FALSE); if ( usAttachment > 0 ) { bStatus = RebelCommand::GetEnemyEquipmentStatusModifier(70 + Random(31)); CreateItem( usAttachment, bStatus, &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; pp->Inv[ HELMETPOS ].AttachObject( NULL, &gTempObject, FALSE ); } } } #ifdef obsoleteCode //usNumMatches = 0; //while( bHelmetClass && !usNumMatches ) //{ //First step is to count the number of helmets in the helmet class range. If we // //don't find one, we keep lowering the class until we do. // for( i = 0; i < MAXITEMS; i++ ) // { // if ( Item[i].usItemClass == 0 ) // break; // pItem = &Item[ i ]; // // NOTE: This relies on treated armor to have a coolness of 0 in order for enemies not to be equipped with it // if( pItem->usItemClass == IC_ARMOUR && pItem->ubCoolness == bHelmetClass ) // { // if( Armour[ pItem->ubClassIndex ].ubArmourClass == ARMOURCLASS_HELMET && Armour[ pItem->ubClassIndex ].ubProtection >= 10 ) // usNumMatches++; // } // } // if( !usNumMatches ) // bHelmetClass--; //} //if( usNumMatches ) //{ //There is a helmet that we can choose. // usRandom = (UINT16)Random( usNumMatches ); // for( i = 0; i < MAXITEMS; i++ ) // { // if ( Item[i].usItemClass == 0 ) // break; // pItem = &Item[ i ]; // if( pItem->usItemClass == IC_ARMOUR && pItem->ubCoolness == bHelmetClass ) // { // if( Armour[ pItem->ubClassIndex ].ubArmourClass == ARMOURCLASS_HELMET && Armour[ pItem->ubClassIndex ].ubProtection >= 10 ) // { // if( usRandom ) // usRandom--; // else // { // if( !(pp->Inv[ HELMETPOS ].fFlags & OBJECT_NO_OVERWRITE) ) // { // CreateItem( i, (INT8)(70+Random(31)), &(pp->Inv[ HELMETPOS ]) ); // pp->Inv[ HELMETPOS ].fFlags |= OBJECT_UNDROPPABLE; // } // break; // } // } // } // } //} #endif //obsoleteCode } //Choose vest if( bVestClass ) { if(!gGameExternalOptions.fSoldiersWearAnyArmour) usVestItem = PickARandomItem(VEST, pp->ubSoldierClass, bVestClass ); if ( usVestItem > 0 && Item[usVestItem].usItemClass == IC_ARMOUR && !(pp->Inv[ VESTPOS ].fFlags & OBJECT_NO_OVERWRITE) && Armour[ Item[usVestItem].ubClassIndex ].ubArmourClass == ARMOURCLASS_VEST ) { bStatus = RebelCommand::GetEnemyEquipmentStatusModifier(70 + Random(31)); CreateItem( usVestItem, bStatus, &(pp->Inv[ VESTPOS ]) ); pp->Inv[ VESTPOS ].fFlags |= OBJECT_UNDROPPABLE; // roll to see if he gets a CERAMIC PLATES, too. Higher chance the higher his entitled vest class is if (( INT8 ) Random( 100 ) < ( 15 * ( bVestClass - Item[usVestItem].ubCoolness ) ) ) { UINT16 usAttachment = PickARandomAttachment(ARMOURATTACHMENT, pp->ubSoldierClass, usVestItem, bVestClass, FALSE); if ( usAttachment > 0 ) { bStatus = RebelCommand::GetEnemyEquipmentStatusModifier(70 + Random(31)); CreateItem( usAttachment, bStatus, &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; pp->Inv[ VESTPOS ].AttachObject( NULL, &gTempObject, FALSE ); } } } #ifdef obsoleteCode //usNumMatches = 0; //while( bVestClass && !usNumMatches ) //{ //First step is to count the number of armours in the armour class range. If we // //don't find one, we keep lowering the class until we do. // for( i = 0; i < MAXITEMS; i++ ) // { // if ( Item[i].usItemClass == 0 ) // break; // // these 3 have a non-zero coolness, and would otherwise be selected, so skip them // if ( Item[i].leatherjacket) // continue; // pItem = &Item[ i ]; // // Madd: don't pick crappy armour like t-shirts, must be a minimum protection of ten // // NOTE: This relies on treated armor to have a coolness of 0 in order for enemies not to be equipped with it // if( pItem->usItemClass == IC_ARMOUR && pItem->ubCoolness == bVestClass ) // { // if( Armour[ pItem->ubClassIndex ].ubArmourClass == ARMOURCLASS_VEST && Armour[pItem->ubClassIndex].ubProtection >= 10 ) // usNumMatches++; // } // } // if( !usNumMatches ) // bVestClass--; //} //if( usNumMatches ) //{ //There is an armour that we can choose. // usRandom = (UINT16)Random( usNumMatches ); // for( i = 0; i < MAXITEMS; i++ ) // { // if ( Item[i].usItemClass == 0 ) // break; // pItem = &Item[ i ]; // if( pItem->usItemClass == IC_ARMOUR && pItem->ubCoolness == bVestClass ) // { // if ( Item[i].leatherjacket ) // continue; // // Madd: don't pick crappy armour like t-shirts, must be a minimum protection of ten // if( Armour[ pItem->ubClassIndex ].ubArmourClass == ARMOURCLASS_VEST && Armour[ pItem->ubClassIndex ].ubProtection >= 10 ) // { // if( usRandom ) // usRandom--; // else // { // if( !(pp->Inv[ VESTPOS ].fFlags & OBJECT_NO_OVERWRITE) ) // { // CreateItem( i, (INT8)(70+Random(31)), &(pp->Inv[ VESTPOS ]) ); // pp->Inv[ VESTPOS ].fFlags |= OBJECT_UNDROPPABLE; // // roll to see if he gets a CERAMIC PLATES, too. Higher chance the higher his entitled vest class is // if (( INT8 ) Random( 100 ) < ( 15 * ( bOrigVestClass - pItem->ubCoolness ) ) ) // { // UINT16 usAttachment = PickARandomItem(ARMOURATTACHMENT, bOrigVestClass); // if ( usAttachment > 0 && ValidAttachment(usAttachment, i ) ) // { // CreateItem( usAttachment, (INT8)(70+Random(31)), &gTempObject ); // gTempObject.fFlags |= OBJECT_UNDROPPABLE; // AttachObject( NULL, &(pp->Inv[ VESTPOS ]), &gTempObject ); // } // } // } // break; // } // } // } // } //} #endif //obsoleteCode } //Choose Leggings if( bLeggingsClass ) { if(!gGameExternalOptions.fSoldiersWearAnyArmour) usLeggingsItem = PickARandomItem(LEGS, pp->ubSoldierClass, bLeggingsClass ); if ( usLeggingsItem > 0 && Item[usLeggingsItem].usItemClass == IC_ARMOUR && !(pp->Inv[ LEGPOS ].fFlags & OBJECT_NO_OVERWRITE) && Armour[ Item[usLeggingsItem].ubClassIndex ].ubArmourClass == ARMOURCLASS_LEGGINGS ) { bStatus = RebelCommand::GetEnemyEquipmentStatusModifier(70 + Random(31)); CreateItem( usLeggingsItem, bStatus, &(pp->Inv[ LEGPOS ]) ); pp->Inv[ LEGPOS ].fFlags |= OBJECT_UNDROPPABLE; // roll to see if he gets an attachment, too. Higher chance the higher his entitled Leggings class is if (( INT8 ) Random( 100 ) < ( 15 * ( bLeggingsClass - Item[usLeggingsItem].ubCoolness ) ) ) { UINT16 usAttachment = PickARandomAttachment(ARMOURATTACHMENT, pp->ubSoldierClass, usLeggingsItem, bLeggingsClass, FALSE); if ( usAttachment > 0 ) { bStatus = RebelCommand::GetEnemyEquipmentStatusModifier(70 + Random(31)); CreateItem( usAttachment, bStatus, &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; pp->Inv[ LEGPOS ].AttachObject( NULL, &gTempObject, FALSE); } } } #ifdef obsoleteCode //usNumMatches = 0; //while( bLeggingsClass && !usNumMatches ) //{ //First step is to count the number of Armours in the Armour class range. If we // //don't find one, we keep lowering the class until we do. // for( i = 0; i < MAXITEMS; i++ ) // { // if ( Item[i].usItemClass == 0 ) // break; // pItem = &Item[ i ]; // // NOTE: This relies on treated armor to have a coolness of 0 in order for enemies not to be equipped with it // if( pItem->usItemClass == IC_ARMOUR && pItem->ubCoolness == bLeggingsClass ) // { // if( Armour[ pItem->ubClassIndex ].ubArmourClass == ARMOURCLASS_LEGGINGS && Armour[ pItem->ubClassIndex ].ubProtection >= 10 ) // usNumMatches++; // } // } // if( !usNumMatches ) // bLeggingsClass--; //} //if( usNumMatches ) //{ //There is a legging that we can choose. // usRandom = (UINT16)Random( usNumMatches ); // for( i = 0; i < MAXITEMS; i++ ) // { // if ( Item[i].usItemClass == 0 ) // break; // pItem = &Item[ i ]; // if( pItem->usItemClass == IC_ARMOUR && pItem->ubCoolness == bLeggingsClass ) // { // if( Armour[ pItem->ubClassIndex ].ubArmourClass == ARMOURCLASS_LEGGINGS && Armour[ pItem->ubClassIndex ].ubProtection >= 10 ) // { // if( usRandom ) // usRandom--; // else // { // if( !(pp->Inv[ LEGPOS ].fFlags & OBJECT_NO_OVERWRITE) ) // { // CreateItem( i, (INT8)(70+Random(31)), &(pp->Inv[ LEGPOS ]) ); // pp->Inv[ LEGPOS ].fFlags |= OBJECT_UNDROPPABLE; // break; // } // } // } // } // } //} #endif //obsoleteCode } } void ChooseSpecialWeaponsForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bKnifeClass, BOOLEAN fGrenadeLauncher, BOOLEAN fLAW, BOOLEAN fMortar, BOOLEAN fRPG ) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"ChooseSpecialWeaponsForSoldierCreateStruct"); //UINT16 i; //INVTYPE *pItem; //UINT16 usRandom; UINT16 usKnifeIndex = 0; itemGrenadeLauncher = 0; itemLAW = 0; itemRPG = 0; itemMortar=0; #ifdef obsoleteCode //Choose knife //while( bKnifeClass && !usNumMatches ) //{ //First step is to count the number of weapons in the weapon class range. If we // //don't find one, we keep lowering the class until we do. // for( i = 0; i < MAXITEMS; i++ ) // { // if ( Item[i].usItemClass == 0 ) // break; // pItem = &Item[ i ]; // if( ( pItem->usItemClass == IC_BLADE || pItem->usItemClass == IC_THROWING_KNIFE ) && pItem->ubCoolness == bKnifeClass ) // { // usNumMatches++; // } // } // if( !usNumMatches ) // bKnifeClass--; //} //if( usNumMatches ) //{ //There is a knife that we can choose. // usRandom = (UINT16)Random( usNumMatches ); // for( i = 0; i < MAXITEMS; i++ ) // { // if ( Item[i].usItemClass == 0 ) // break; // pItem = &Item[ i ]; // if( ( pItem->usItemClass == IC_BLADE || pItem->usItemClass == IC_THROWING_KNIFE ) && pItem->ubCoolness == bKnifeClass ) // { // if( usRandom ) // usRandom--; // else // { // usKnifeIndex = i; // break; // } // } // } //} #endif //obsoleteCode if ( bKnifeClass ) { usKnifeIndex = PickARandomItem(KNIVES, pp->ubSoldierClass, bKnifeClass); if( usKnifeIndex > 0 ) { CreateItem( usKnifeIndex, (INT8)(70 + Random( 31 )), &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); } } ////test code // CreateItem( 347, (INT8)(50 + Random( 51 )), &gTempObject ); // PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); // // CreateItem( 298, (INT8)(50 + Random( 51 )), &gTempObject ); // PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); // // CreateItem( 298, (INT8)(50 + Random( 51 )), &gTempObject ); // PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); if ( IsGrenadeLauncherAttached(&pp->Inv[HANDPOS]) ) //Madd: if there's a grenade launcher attached to the gun, then use that instead of giving him another one { itemGrenadeLauncher = GetAttachedGrenadeLauncher(&pp->Inv[HANDPOS]); } else if (fGrenadeLauncher) { itemGrenadeLauncher = PickARandomItem ( GRENADELAUNCHER, pp->ubSoldierClass ); // give grenade launcher if ( itemGrenadeLauncher > 0 ) { CreateItem( itemGrenadeLauncher, (INT8)(50 + Random( 51 )), &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); } } if (fLAW) { // give rocket launcher itemLAW = PickARandomItem (SINGLESHOTROCKETLAUNCHER, pp->ubSoldierClass ); if ( itemLAW > 0 ) { CreateItem( itemLAW, (INT8)(50 + Random( 51 )), &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); } } if (fRPG) { // give rpg itemRPG = PickARandomItem (ROCKETLAUNCHER, pp->ubSoldierClass); if ( itemRPG > 0 ) { CreateItem( itemRPG, (INT8)(50 + Random( 51 )), &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); } } if (fMortar) { // make sure we're not distributing them underground! Assert( IsAutoResolveActive() || ( gbWorldSectorZ == 0 ) ); // give mortar itemMortar = PickARandomItem (MORTARLAUNCHER, pp->ubSoldierClass); if ( itemMortar > 0 ) { CreateItem( itemMortar, (INT8)(50 + Random( 51 )), &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); } } } void ChooseFaceGearForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp ) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"ChooseFaceGearForSoldierCreateStruct"); INT32 i; INT8 bDifficultyRating = CalcDifficultyModifier( pp->ubSoldierClass ); UINT16 usItem=0; if ( gWorldSectorX == TIXA_SECTOR_X && gWorldSectorY == TIXA_SECTOR_Y && StrategicMap[ TIXA_SECTOR_X + TIXA_SECTOR_Y * MAP_WORLD_X ].fEnemyControlled ) { //Tixa is a special case that is handled differently. return; } //Look for any face item in the big pocket positions (the only place they can be added in the editor) //If any are found, then don't assign any // CHRISL: Change static inventory pocket definition to dynamic for( i = BIGPOCKSTART; i < BIGPOCKFINAL; i++ ) { if( Item[ pp->Inv[ i ].usItem ].usItemClass == IC_FACE ) { return; } } //KM: (NEW) //Note the lack of overwritable item checking here. This is because faceitems are not //supported in the editor, hence they can't have this status. switch( pp->ubSoldierClass ) { case SOLDIER_CLASS_ELITE: case SOLDIER_CLASS_ELITE_MILITIA: if ( Chance( bDifficultyRating ) ) { //All elites get a gasmask and either night goggles or uv goggles. if( Chance( 75 ) ) { usItem = PickARandomItem(GASMASKS, pp->ubSoldierClass); if ( usItem > 0 ) { CreateItem( usItem, (INT8)(70+Random(31)), &(pp->Inv[ HEAD1POS ]) ); pp->Inv[ HEAD1POS ].fFlags |= OBJECT_UNDROPPABLE; } } else { usItem = PickARandomItem(HEARINGAIDS, pp->ubSoldierClass); if ( usItem > 0 ) { CreateItem( usItem, (INT8)(70+Random(31)), &(pp->Inv[ HEAD1POS ]) ); pp->Inv[ HEAD1POS ].fFlags |= OBJECT_UNDROPPABLE; } } } if ( Chance( bDifficultyRating / 3 ) ) { if( Chance( 75 ) ) { usItem = PickARandomItem(NVGLOW, pp->ubSoldierClass); if ( usItem > 0 ) { CreateItem( usItem, (INT8)(70+Random(31)), &(pp->Inv[ HEAD2POS ]) ); pp->Inv[ HEAD2POS ].fFlags |= OBJECT_UNDROPPABLE; } } else { usItem = PickARandomItem(NVGHIGH, pp->ubSoldierClass); if ( usItem > 0 ) { CreateItem( usItem, (INT8)(70+Random(31)), &(pp->Inv[ HEAD2POS ]) ); pp->Inv[ HEAD2POS ].fFlags |= OBJECT_UNDROPPABLE; } } } break; case SOLDIER_CLASS_ARMY: case SOLDIER_CLASS_REG_MILITIA: if( Chance( bDifficultyRating / 2 ) ) { //chance of getting a face item if( Chance( 50 ) ) { usItem = PickARandomItem(GASMASKS, pp->ubSoldierClass); if ( usItem > 0 ) { CreateItem( usItem, (INT8)(70+Random(31)), &(pp->Inv[ HEAD1POS ]) ); pp->Inv[ HEAD1POS ].fFlags |= OBJECT_UNDROPPABLE; } } else { usItem = PickARandomItem(NVGLOW, pp->ubSoldierClass); if ( usItem > 0 ) { CreateItem( usItem, (INT8)(70+Random(31)), &(pp->Inv[ HEAD1POS ]) ); pp->Inv[ HEAD1POS ].fFlags |= OBJECT_UNDROPPABLE; } } } if( Chance( bDifficultyRating / 3 ) ) { //chance of getting a extended ear usItem = PickARandomItem(HEARINGAIDS, pp->ubSoldierClass); if ( usItem > 0 ) { CreateItem( usItem, (INT8)(70+Random(31)), &(pp->Inv[ HEAD2POS ]) ); pp->Inv[ HEAD2POS ].fFlags |= OBJECT_UNDROPPABLE; } } break; case SOLDIER_CLASS_ADMINISTRATOR: case SOLDIER_CLASS_GREEN_MILITIA: case SOLDIER_CLASS_ZOMBIE: case SOLDIER_CLASS_BANDIT: break; } } void ChooseKitsForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bKitClass ) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"ChooseKitsForSoldierCreateStruct"); //UINT16 i; //INVTYPE *pItem; //UINT16 usRandom; //UINT16 usNumMatches = 0; UINT16 usKitItem = 0; //Madd: specify kit items in xml instead #ifdef obsoleteCode // we want these mostly to be first aid and medical kits, and for those kit class doesn't matter, they're always useful //if ( Chance( 50 ) ) //{ // usKitItem = FIRSTAIDKIT; //} //else //if ( Chance( 25 ) ) //{ // usKitItem = MEDICKIT; //} //else //{ // // count how many non-medical KITS are eligible ( of sufficient or lower coolness) // for( i = 0; i < MAXITEMS; i++ ) // { // if ( Item[i].usItemClass == 0 ) // break; // pItem = &Item[ i ]; // // skip toolkits // if( ( pItem->usItemClass == IC_KIT ) && ( pItem->ubCoolness > 0 ) && pItem->ubCoolness <= bKitClass && ( !Item[i].toolkit ) ) // { // usNumMatches++; // } // } // // if any are eligible, pick one of them at random // if( usNumMatches ) // { // usRandom = (UINT16)Random( usNumMatches ); // for( i = 0; i < MAXITEMS; i++ ) // { // if ( Item[i].usItemClass == 0 ) // break; // pItem = &Item[ i ]; // // skip toolkits // if( ( pItem->usItemClass == IC_KIT ) && ( pItem->ubCoolness > 0 ) && pItem->ubCoolness <= bKitClass && ( !Item[i].toolkit ) ) // { // if( usRandom ) // usRandom--; // else // { // usKitItem = i; // break; // } // } // } // } //} #endif //obsoleteCode // if we still want more medical and first aid kits, we can just add extras of those item #s to the array usKitItem = PickARandomItem( KIT, pp->ubSoldierClass, bKitClass, FALSE ); if ( usKitItem > 0 ) { CreateItem( usKitItem, (INT8)(80 + Random( 21 )), &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); } } void ChooseMiscGearForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bMiscClass ) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"ChooseMiscGearForSoldierCreateStruct"); //UINT16 i; //INVTYPE *pItem; UINT16 usRandom; //Madd: let's do this a couple times, so we can have > 1 misc item per soldier usRandom = Random(3); if ( usRandom ) { UINT16 usItem = 0; for ( UINT16 i=0;iubSoldierClass, bMiscClass, FALSE); if ( usItem > 0 ) { int bStatus = 80 + Random( 21 ); if ( Item[usItem].usItemClass == IC_MONEY ) bStatus = 1 + Random( 20 ); CreateItem( usItem, (INT8)(bStatus), &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); } } } // Madd: code above is externalized #ifdef obsoleteCode //// not all of these are IC_MISC, some are IC_PUNCH (not covered anywhere else) //INT32 iMiscItemsList[] = //{ // CANTEEN, // CANTEEN, // CANTEEN, // CANTEEN, // ALCOHOL, // ALCOHOL, // ADRENALINE_BOOSTER, // ADRENALINE_BOOSTER, // REGEN_BOOSTER, // BRASS_KNUCKLES, // CHEWING_GUM, // CIGARS, // GOLDWATCH, // -1 //}; //// count how many are eligible //i = 0; //while ( iMiscItemsList[ i ] != -1 ) //{ // pItem = &Item[ iMiscItemsList [ i ] ]; // if( ( pItem->ubCoolness > 0 ) && ( pItem->ubCoolness <= bMiscClass ) ) // { // // exclude REGEN_BOOSTERs unless Sci-Fi flag is on // if ( ( iMiscItemsList [ i ] != REGEN_BOOSTER ) || ( gGameOptions.ubGameStyle == STYLE_SCIFI ) ) // { // usNumMatches++; // } // } // i++; //} //// if any are eligible, pick one of them at random //if( usNumMatches ) //{ // usRandom = (UINT16)Random( usNumMatches ); // i = 0; // while ( iMiscItemsList[ i ] != -1 ) // { // pItem = &Item[ iMiscItemsList [ i ] ]; // if( ( pItem->ubCoolness > 0 ) && ( pItem->ubCoolness <= bMiscClass ) ) // { // // exclude REGEN_BOOSTERs unless Sci-Fi flag is on // if ( ( iMiscItemsList [ i ] != REGEN_BOOSTER ) || ( gGameOptions.ubGameStyle == STYLE_SCIFI ) ) // { // if( usRandom ) // usRandom--; // else // { // CreateItem( ( UINT16 ) iMiscItemsList[ i ], (INT8)(80 + Random( 21 )), &gTempObject ); // gTempObject.fFlags |= OBJECT_UNDROPPABLE; // PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); // break; // } // } // } // i++; // } //} #endif //obsoleteCode } void ChooseBombsForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bBombClass ) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"ChooseBombsForSoldierCreateStruct"); //UINT16 i; //INVTYPE *pItem; //UINT16 usRandom; UINT16 usItem = 0; usItem = PickARandomItem( BOMB, pp->ubSoldierClass, bBombClass, FALSE ); if ( usItem > 0 ) { CreateItem( usItem, (INT8)(80 + Random( 21 )), &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); } // Madd: externalized again #ifdef obsoleteCode //// count how many are eligible //for( i = 0; i < MAXITEMS; i++ ) //{ // if ( Item[i].usItemClass == 0 ) // break; // pItem = &Item[ i ]; // if( ( pItem->usItemClass == IC_BOMB ) && ( pItem->ubCoolness > 0 ) && ( pItem->ubCoolness <= bBombClass ) ) // { // usNumMatches++; // } //} //// if any are eligible, pick one of them at random //if( usNumMatches ) //{ // usRandom = (UINT16)Random( usNumMatches ); // for( i = 0; i < MAXITEMS; i++ ) // { // if ( Item[i].usItemClass == 0 ) // break; // pItem = &Item[ i ]; // if( ( pItem->usItemClass == IC_BOMB ) && ( pItem->ubCoolness > 0 ) && ( pItem->ubCoolness <= bBombClass ) ) // { // if( usRandom ) // usRandom--; // else // { // CreateItem( i, (INT8)(80 + Random( 21 )), &gTempObject ); // gTempObject.fFlags |= OBJECT_UNDROPPABLE; // PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); // break; // } // } // } //} #endif //obsoleteCode } // Headrock: Added a function to randomly create LBEs void ChooseLBEsForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bLBEClass ) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"ChooseLBEsForSoldierCreateStruct"); // CHRISL: If we're using the old inventory system, just return if((UsingNewInventorySystem() == false)) return; UINT16 usItem = PickARandomItem( LBE, pp->ubSoldierClass, bLBEClass, FALSE ); if ( usItem > 0 ) { CreateItem( usItem, (INT8)(80 + Random( 21 )), &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; auto isLBEgear = Item[usItem].usItemClass & IC_LBEGEAR; if (isLBEgear) { // put backpacks into the backpack slot for LOBOT auto lbeClass = LoadBearingEquipment[Item[usItem].ubClassIndex].lbeClass; if (lbeClass == BACKPACK && !pp->Inv[BPACKPOCKPOS].exists()) { pp->Inv[BPACKPOCKPOS] = gTempObject; return; } // same for pistol holster if (lbeClass == THIGH_PACK) { if (!pp->Inv[LTHIGHPOCKPOS].exists()) { pp->Inv[LTHIGHPOCKPOS] = gTempObject; return; } if (!pp->Inv[RTHIGHPOCKPOS].exists()) { pp->Inv[RTHIGHPOCKPOS] = gTempObject; return; } } } // Couldn't find a specific pocket, just stuff it somewhere PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); } } void ChooseLocationSpecificGearForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp ) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"ChooseLocationSpecificGearForSoldierCreateStruct"); UINT16 usItem = 0; // If this is Tixa and the player doesn't control Tixa then give all enemies gas masks, // but somewhere on their person, not in their face positions if ( gWorldSectorX == TIXA_SECTOR_X && gWorldSectorY == TIXA_SECTOR_Y && StrategicMap[ TIXA_SECTOR_X + TIXA_SECTOR_Y * MAP_WORLD_X ].fEnemyControlled ) { //dnl ch40 041009 usItem = PickARandomItem(GASMASKS, pp->ubSoldierClass); if(Random(100) > 80) usItem = NOTHING; if(usItem > 0) { CreateItem(usItem, (INT8)(90+Random(10)), &gTempObject); PlaceObjectInSoldierCreateStruct(pp, &gTempObject); } } } BOOLEAN PlaceObjectInSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, OBJECTTYPE *pObject ) { INT8 i; if( FitsInSmallPocket(pObject) == true ) { // CHRISL: Change static inventory pocket definition to dynamic //try to get it into a small pocket first for( i = SMALLPOCKSTART; i < SMALLPOCKFINAL; i++ ) { if( pp->Inv[ i ].exists() == false && !(pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE) ) { pp->Inv[ i ] = *pObject; return TRUE; } } } //NPCs skip medium pockets // CHRISL: Change static inventory pocket definition to dynamic for( i = BIGPOCKSTART; i < BIGPOCKFINAL; i++ ) { //no space free in small pockets, so put it into a large pocket. if( pp->Inv[ i ].exists() == false && !(pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE) ) { pp->Inv[ i ] = *pObject; return TRUE; } } return FALSE; } void RandomlyChooseWhichItemsAreDroppable( SOLDIERCREATE_STRUCT *pp, INT8 bSoldierClass ) { UINT32 i; INT32 j; // UINT16 usRandomNum; UINT32 uiItemClass; UINT8 ubNumMatches = 0; UINT16 usItem; UINT8 ubAmmoDropRate; UINT8 ubGrenadeDropRate; UINT8 ubOtherDropRate; BOOLEAN fWeapon = FALSE; BOOLEAN fGrenades = FALSE; // this includes all grenades! BOOLEAN fAmmo = FALSE; BOOLEAN fArmour = FALSE; BOOLEAN fKnife = FALSE; BOOLEAN fKit = FALSE; BOOLEAN fFace = FALSE; BOOLEAN fMisc = FALSE; UINT32 uiRandomValue = 0; //Madd //if ( gGameSettings.fOptions[TOPTION_DROP_ALL] ) // EXTERNILIZED TO THE INI INSTEAD - SANDRO if ( gGameExternalOptions.fEnemiesDropAllItems == 1 ) { ENEMYAMMODROPRATE = 100; ENEMYGRENADEDROPRATE = 100; ENEMYEQUIPDROPRATE = 100; // WANNE: Changed from 50 to 100, because DROP ALL should mean DROP ALL! MILITIAAMMODROPRATE = 100; MILITIAGRENADEDROPRATE = 100; MILITIAEQUIPDROPRATE = 100; // WANNE: Changed from 50 to 100, because DROP ALL should mean DROP ALL! } else { // Default random drop if (gGameExternalOptions.ubEnemiesItemDrop == 0) { ENEMYAMMODROPRATE = 50; ENEMYGRENADEDROPRATE = 25; ENEMYEQUIPDROPRATE = 15; MILITIAAMMODROPRATE = 5; MILITIAGRENADEDROPRATE = 3; MILITIAEQUIPDROPRATE = 2; } // Get drop rate from XML-Files else { // Reset, because it is not used anymore ENEMYAMMODROPRATE = -1; ENEMYGRENADEDROPRATE = -1; ENEMYEQUIPDROPRATE = -1; MILITIAAMMODROPRATE = -1; MILITIAGRENADEDROPRATE = -1; MILITIAEQUIPDROPRATE = -1; } } #ifdef obsoleteCode /* //40% of soldiers will have droppable items. usRandomNum = (UINT16)Random( 1000 ); if( usRandomNum >= 400 ) return; //so now the number is 0-399. This is kind of like a D&D die roll where //various numbers drop different items, or even more than one item. At this //point, we don't care if the enemy has anything in the slot that is made droppable. //Any items containing the OBJECT_NO_OVERWRITE slot is rejected for droppable //consideration. if( usRandomNum < 32 ) //3.2% of dead bodies present the possibility of several items (0-5 items : avg 3). { //31 is the magic number that allows all 5 item classes to be dropped! if( usRandomNum & 16 ) fWeapon = TRUE; if( usRandomNum & 8 ) fAmmo = TRUE; if( usRandomNum & 4 ) fGrenades = TRUE; if( usRandomNum & 2 ) fArmour = TRUE; if( usRandomNum & 1 ) fMisc = TRUE; } else if( usRandomNum < 100 ) //6.8% chance of getting 2-3 different items. { //do a more generalized approach to dropping items. switch( usRandomNum / 10 ) { case 3: fWeapon = TRUE; fAmmo = TRUE; break; case 4: fWeapon = TRUE; fGrenades = TRUE; break; case 5: fGrenades = TRUE; fMisc = TRUE; break; case 6: fGrenades = TRUE; fArmour = TRUE; break; case 7: fAmmo = TRUE; fArmour = TRUE; break; case 8: fAmmo = TRUE; fArmour = TRUE; fMisc = TRUE; break; case 9: fGrenades = TRUE; fAmmo = TRUE; fMisc = TRUE; break; } } else { switch( usRandomNum / 50 ) //30% chance of getting 1-2 items (no weapons) { case 2: fGrenades = TRUE; break; case 3: fAmmo = TRUE; break; case 4: fArmour = TRUE; break; case 5: fMisc = TRUE; break; case 6: fAmmo = TRUE; fMisc = TRUE; break; case 7: fGrenades = TRUE; fAmmo = TRUE; break; } } fKnife = (Random(3)) ? FALSE : TRUE; */ #endif //obsoleteCode if ( SOLDIER_CLASS_MILITIA( bSoldierClass ) ) { // militia (they drop much less stuff) ubAmmoDropRate = MILITIAAMMODROPRATE; ubGrenadeDropRate = MILITIAGRENADEDROPRATE; ubOtherDropRate = MILITIAEQUIPDROPRATE; } else { // enemy army ubAmmoDropRate = ENEMYAMMODROPRATE; ubGrenadeDropRate = ENEMYGRENADEDROPRATE; ubOtherDropRate = ENEMYEQUIPDROPRATE; } // only enemy soldiers use auto-drop system! // don't use the auto-drop system in auto-resolve: player won't see what's being used & enemies will often win & keep'em // Flugente: nah. We can decide to not drop later on if we want anyway. // HEADROCK HAM B2.8: Militia now drop all their equipment... IF not killed by player (see TurnSoldierIntoCorpse() ) if ( SOLDIER_CLASS_ENEMY( bSoldierClass ) || ( gGameExternalOptions.ubMilitiaDropEquipment > 0 && SOLDIER_CLASS_MILITIA( bSoldierClass ) ) ) { // SPECIAL handling for weapons: we'll always drop a weapon type that has never been dropped before UINT32 invsize = pp->Inv.size(); for( i = 0; i < invsize; ++i ) { usItem = pp->Inv[ i ].usItem; //TODO: someday maybe pp->Inv[i].fFlags &= ~ITEM_REPAIRABLE; //if ( gGameSettings.fOptions[TOPTION_DROP_ALL] ) // EXTERNILIZED TO THE INI INSTEAD - SANDRO if ( gGameExternalOptions.fEnemiesDropAllItems == 1 ) { // WANNE: "Drop all" should mean "Drop all" /* if ( Item[ pp->Inv[ i ].usItem ].usItemClass == IC_FACE ) { if( Random(100) < ubOtherDropRate ) // way too many gas masks, nvg and extended ears getting dropped! pp->Inv[ i ].fFlags &= ~OBJECT_UNDROPPABLE; } else */ pp->Inv[ i ].fFlags &= ~OBJECT_UNDROPPABLE; } else { // Default random drop if (gGameExternalOptions.ubEnemiesItemDrop == 0) { // if it's a weapon (monster parts included - they won't drop due to checks elsewhere!) if ( (usItem > NONE) && (usItem < gMAXITEMS_READ) ) // Madd -- this should be ok set to maxitems instead of max_Weapons { // and we're allowed to change its flags if(! (pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE )) { // and it's never been dropped before in this game if (!gStrategicStatus.fWeaponDroppedAlready[usItem]) { // mark it as droppable, and remember we did so. If the player never kills this particular dude, oh well, // tough luck, he missed his chance for an easy reward, he'll have to wait til next time and need some luck... pp->Inv[ i ].fFlags &= ~OBJECT_UNDROPPABLE; MarkAllWeaponsOfSameGunClassAsDropped( usItem ); } } } } } } } //if ( gGameSettings.fOptions[TOPTION_DROP_ALL] ) // EXTERNILIZED TO THE INI INSTEAD - SANDRO if ( gGameExternalOptions.fEnemiesDropAllItems == 1 ) return; // WANNE: Randomly choose which type of items should be dropped if (gGameExternalOptions.ubEnemiesItemDrop == 0) { if( Random(100) < ubAmmoDropRate ) fAmmo = TRUE; if( Random(100) < ubOtherDropRate ) fWeapon = TRUE; if( Random(100) < ubOtherDropRate ) fArmour = TRUE; if( Random(100) < ubOtherDropRate ) fKnife = TRUE; if( Random(100) < ubGrenadeDropRate ) fGrenades = TRUE; if( Random(100) < ubOtherDropRate ) fKit = TRUE; if( Random(100) < (UINT32)(ubOtherDropRate / 3) ) fFace = TRUE; if( Random(100) < ubOtherDropRate ) fMisc = TRUE; //Now, that the flags are set for each item, we now have to search through the item slots to //see if we can find a matching item, however, if we find any items in a particular class that //have the OBJECT_NO_OVERWRITE flag set, we will not make any items droppable for that class //because the editor would have specified it already. if( fAmmo ) { // now drops ALL ammo found, not just the first slot UINT32 invsize = pp->Inv.size(); for( i = 0; i < invsize; ++i ) { uiItemClass = Item[ pp->Inv[ i ].usItem ].usItemClass; if( uiItemClass == IC_AMMO ) { if( pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE ) continue; else { pp->Inv[ i ].fFlags &= ~OBJECT_UNDROPPABLE; } } } } if( fWeapon ) { ubNumMatches = 0; UINT32 invsize = pp->Inv.size(); for( i = 0; i < invsize; ++i ) { uiItemClass = Item[ pp->Inv[ i ].usItem ].usItemClass; if( uiItemClass == IC_GUN || uiItemClass == IC_LAUNCHER ) { if( pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE ) break; else ++ubNumMatches; } } if ( ubNumMatches > 0 ) { for( i = 0; i < invsize; ++i ) { uiItemClass = Item[ pp->Inv[ i ].usItem ].usItemClass; if( uiItemClass == IC_GUN || uiItemClass == IC_LAUNCHER ) { if( pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE ) break; else if( !Random( ubNumMatches-- ) ) { pp->Inv[ i ].fFlags &= ~OBJECT_UNDROPPABLE; break; } } } } } if( fArmour ) { ubNumMatches = 0; UINT32 invsize = pp->Inv.size(); for( i = 0; i < invsize; ++i ) { uiItemClass = Item[ pp->Inv[ i ].usItem ].usItemClass; if( uiItemClass == IC_ARMOUR ) { if( pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE ) break; else ++ubNumMatches; } } if ( ubNumMatches > 0 ) { for( i = 0; i < invsize; ++i ) { uiItemClass = Item[ pp->Inv[ i ].usItem ].usItemClass; if( uiItemClass == IC_ARMOUR ) { if( pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE ) break; else if( !Random( ubNumMatches-- ) ) { pp->Inv[ i ].fFlags &= ~OBJECT_UNDROPPABLE; break; } } } } } if( fKnife) { UINT32 invsize = pp->Inv.size(); for( i = 0; i < invsize; ++i ) { // drops FIRST knife found uiItemClass = Item[ pp->Inv[ i ].usItem ].usItemClass; if( uiItemClass == IC_BLADE || uiItemClass == IC_THROWING_KNIFE ) { if( pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE ) break; else { pp->Inv[ i ].fFlags &= ~OBJECT_UNDROPPABLE; break; } } } } // note that they'll only drop ONE TYPE of grenade if they have multiple types (very common) if( fGrenades ) { ubNumMatches = 0; UINT32 invsize = pp->Inv.size(); for( i = 0; i < invsize; ++i ) { uiItemClass = Item[ pp->Inv[ i ].usItem ].usItemClass; if( uiItemClass == IC_GRENADE ) { if( pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE ) break; else ++ubNumMatches; } } if ( ubNumMatches > 0 ) { for( i = 0; i < invsize; ++i ) { uiItemClass = Item[ pp->Inv[ i ].usItem ].usItemClass; if( uiItemClass == IC_GRENADE ) { if( pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE ) break; else if( !Random( ubNumMatches-- ) ) { pp->Inv[ i ].fFlags &= ~OBJECT_UNDROPPABLE; break; } } } } } if( fKit ) { ubNumMatches = 0; UINT32 invsize = pp->Inv.size(); for( i = 0; i < invsize; ++i ) { uiItemClass = Item[ pp->Inv[ i ].usItem ].usItemClass; if( uiItemClass == IC_MEDKIT || uiItemClass == IC_KIT ) { if( pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE ) break; else ubNumMatches++; } } if ( ubNumMatches > 0 ) { for( i = 0; i < invsize; ++i ) { uiItemClass = Item[ pp->Inv[ i ].usItem ].usItemClass; if( uiItemClass == IC_MEDKIT || uiItemClass == IC_KIT ) { if( pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE ) break; else if( !Random( ubNumMatches-- ) ) { pp->Inv[ i ].fFlags &= ~OBJECT_UNDROPPABLE; break; } } } } } if( fFace ) { ubNumMatches = 0; UINT32 invsize = pp->Inv.size(); for( i = 0; i < invsize; ++i ) { uiItemClass = Item[ pp->Inv[ i ].usItem ].usItemClass; if( uiItemClass == IC_FACE ) { if( pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE ) break; else ++ubNumMatches; } } if ( ubNumMatches > 0 ) { for( i = 0; i < invsize; ++i ) { uiItemClass = Item[ pp->Inv[ i ].usItem ].usItemClass; if( uiItemClass == IC_FACE ) { if( pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE ) break; else if( !Random( ubNumMatches-- ) ) { pp->Inv[ i ].fFlags &= ~OBJECT_UNDROPPABLE; break; } } } } } if( fMisc ) { ubNumMatches = 0; UINT32 invsize = pp->Inv.size(); for( i = 0; i < invsize; ++i ) { uiItemClass = Item[ pp->Inv[ i ].usItem ].usItemClass; if( uiItemClass == IC_MISC ) { if( pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE ) break; else ++ubNumMatches; } } if ( ubNumMatches > 0 ) { for( i = 0; i < invsize; ++i ) { uiItemClass = Item[ pp->Inv[ i ].usItem ].usItemClass; if( uiItemClass == IC_MISC ) { if( pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE ) break; else if( !Random( ubNumMatches-- ) ) { pp->Inv[ i ].fFlags &= ~OBJECT_UNDROPPABLE; break; } } } } } } // WANNE: Drop-rate from different XML-Files else if (gGameExternalOptions.ubEnemiesItemDrop == 1) { // Loop through the enemy inter UINT32 invsize = pp->Inv.size(); for( i = 0; i < invsize; ++i ) { uiItemClass = Item[ pp->Inv[ i ].usItem ].usItemClass; // We are allowed to change the object and it is not the first (nothing) object //if(! (pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE ) && pp->Inv[ i ].usItem != 0) if( pp->Inv[ i ].exists() == true) { // Weapon if( uiItemClass == IC_GUN) { // Find matching weaponType in the XML for (j = 0; j < MAX_DROP_ITEMS; ++j) { // We have no more weapon items->exit from loop if (j > 0 && gEnemyWeaponDrops[j].uiIndex == 0) break; // We found the matching weapon type if (Weapon[ Item[ pp->Inv[ i ].usItem ].ubClassIndex ].ubWeaponType == gEnemyWeaponDrops[j].ubWeaponType) { uiRandomValue = Random(100); if (uiRandomValue == 0) uiRandomValue++; // militia if ( SOLDIER_CLASS_MILITIA( bSoldierClass ) ) { // Drop the item! if (uiRandomValue <= gEnemyWeaponDrops[j].ubMilitiaDropRate) { pp->Inv[ i ].fFlags &= ~OBJECT_UNDROPPABLE; } } // enemy else { // Drop the item! if (uiRandomValue <= gEnemyWeaponDrops[j].ubEnemyDropRate) { pp->Inv[ i ].fFlags &= ~OBJECT_UNDROPPABLE; } } break; } } } // Ammo else if (uiItemClass == IC_AMMO) { // Find matching ammo in the XML for (j = 0; j < MAX_DROP_ITEMS; ++j) { // We have no more ammo items->exit from loop if (j > 0 && gEnemyAmmoDrops[j].uiIndex == 0) break; // We found the matching ammo type if (Magazine[ Item[ pp->Inv[ i ].usItem ].ubClassIndex ].ubAmmoType == gEnemyAmmoDrops[j].uiType) { uiRandomValue = Random(100); if (uiRandomValue == 0) ++uiRandomValue; // militia if ( SOLDIER_CLASS_MILITIA( bSoldierClass ) ) { // Drop the item! if (uiRandomValue <= gEnemyAmmoDrops[j].ubMilitiaDropRate) { pp->Inv[ i ].fFlags &= ~OBJECT_UNDROPPABLE; } } else { // Drop the item! if (uiRandomValue <= gEnemyAmmoDrops[j].ubEnemyDropRate) { pp->Inv[ i ].fFlags &= ~OBJECT_UNDROPPABLE; } } break; } } } // Explosive else if (uiItemClass == IC_GRENADE || uiItemClass == IC_BOMB) { // Find matching explosive in the XML for (j = 0; j < MAX_DROP_ITEMS; j++) { // We have no more explosive items->exit from loop if (j > 0 && gEnemyExplosiveDrops[j].uiIndex == 0) break; // We found the matching explosive type if (Explosive[Item[ pp->Inv[ i ].usItem ].ubClassIndex].ubType == gEnemyExplosiveDrops[j].ubType) { uiRandomValue = Random(100); if (uiRandomValue == 0) uiRandomValue++; // militia if ( SOLDIER_CLASS_MILITIA( bSoldierClass ) ) { // Drop the item! if (uiRandomValue <= gEnemyExplosiveDrops[j].ubMilitiaDropRate) { pp->Inv[ i ].fFlags &= ~OBJECT_UNDROPPABLE; } } // enemy else { // Drop the item! if (uiRandomValue <= gEnemyExplosiveDrops[j].ubEnemyDropRate) { pp->Inv[ i ].fFlags &= ~OBJECT_UNDROPPABLE; } } break; } } } // Armour else if (uiItemClass == IC_ARMOUR) { // Find matching armour in the XML for (j = 0; j < MAX_DROP_ITEMS; j++) { // We have no more armour items->exit from loop if (j > 0 && gEnemyArmourDrops[j].uiIndex == 0) break; // We found the matching armour type if (Armour[ Item[ pp->Inv[ i ].usItem ].ubClassIndex ].ubArmourClass == gEnemyArmourDrops[j].ubArmourClass) { uiRandomValue = Random(100); if (uiRandomValue == 0) ++uiRandomValue; // militia if ( SOLDIER_CLASS_MILITIA( bSoldierClass ) ) { // Drop the item! if (uiRandomValue <= gEnemyArmourDrops[j].ubMilitiaDropRate) { pp->Inv[ i ].fFlags &= ~OBJECT_UNDROPPABLE; } } // enemy else { // Drop the item! if (uiRandomValue <= gEnemyArmourDrops[j].ubEnemyDropRate) { pp->Inv[ i ].fFlags &= ~OBJECT_UNDROPPABLE; } } break; } } } // Misc else if (uiItemClass == IC_BLADE || uiItemClass == IC_MEDKIT || uiItemClass == IC_KIT || uiItemClass == IC_THROWING_KNIFE || uiItemClass == IC_LAUNCHER || uiItemClass == IC_APPLIABLE || uiItemClass == IC_FACE || uiItemClass == IC_TENTACLES || uiItemClass == IC_THROWN || uiItemClass == IC_PUNCH || uiItemClass == IC_KEY || uiItemClass == IC_MISC || uiItemClass == IC_MONEY // WANNE: Fix by Roadkill: Also drop LBE Gear || uiItemClass == IC_LBEGEAR || uiItemClass == IC_BELTCLIP ) { // Find matching armour in the XML for (j = 0; j < MAX_DROP_ITEMS; j++) { // We have no more armour items->exit from loop if (j > 0 && gEnemyMiscDrops[j].uiIndex == 0) break; // We found the matching armour type if (Item[ pp->Inv[ i ].usItem ].usItemClass == gEnemyMiscDrops[j].usItemClass) { uiRandomValue = Random(100); if (uiRandomValue == 0) uiRandomValue++; // militia if ( SOLDIER_CLASS_MILITIA( bSoldierClass ) ) { // Drop the item! if (uiRandomValue <= gEnemyMiscDrops[j].ubMilitiaDropRate) { pp->Inv[ i ].fFlags &= ~OBJECT_UNDROPPABLE; } } // enemy else { // Drop the item! if (uiRandomValue <= gEnemyMiscDrops[j].ubEnemyDropRate) { pp->Inv[ i ].fFlags &= ~OBJECT_UNDROPPABLE; } } break; } } } } } } } void AssignCreatureInventory( SOLDIERTYPE *pSoldier ) { UINT32 uiChanceToDrop = 0; BOOLEAN fMaleCreature = FALSE; BOOLEAN fBloodcat = FALSE; // all creature items in this first section are only offensive/defensive placeholders, and // never get dropped, because they're not real items! switch( pSoldier->ubBodyType) { case ADULTFEMALEMONSTER: CreateItem( CREATURE_OLD_FEMALE_CLAWS, 100, &(pSoldier->inv[HANDPOS]) ); CreateItem( CREATURE_OLD_FEMALE_HIDE, 100, &(pSoldier->inv[HELMETPOS]) ); CreateItem( CREATURE_OLD_FEMALE_HIDE, 100, &(pSoldier->inv[VESTPOS]) ); CreateItem( CREATURE_OLD_FEMALE_HIDE, 100, &(pSoldier->inv[LEGPOS]) ); uiChanceToDrop = 30; break; case AM_MONSTER: CreateItem( CREATURE_OLD_MALE_CLAWS, 100, &(pSoldier->inv[HANDPOS]) ); CreateItem( CREATURE_OLD_MALE_SPIT, 100, &(pSoldier->inv[SECONDHANDPOS]) ); CreateItem( CREATURE_OLD_MALE_HIDE, 100, &(pSoldier->inv[HELMETPOS]) ); CreateItem( CREATURE_OLD_MALE_HIDE, 100, &(pSoldier->inv[VESTPOS]) ); CreateItem( CREATURE_OLD_MALE_HIDE, 100, &(pSoldier->inv[LEGPOS]) ); uiChanceToDrop = 30; fMaleCreature = TRUE; break; case YAF_MONSTER: CreateItem( CREATURE_YOUNG_FEMALE_CLAWS, 100, &(pSoldier->inv[HANDPOS]) ); CreateItem( CREATURE_YOUNG_FEMALE_HIDE, 100, &(pSoldier->inv[HELMETPOS]) ); CreateItem( CREATURE_YOUNG_FEMALE_HIDE, 100, &(pSoldier->inv[VESTPOS]) ); CreateItem( CREATURE_YOUNG_FEMALE_HIDE, 100, &(pSoldier->inv[LEGPOS]) ); uiChanceToDrop = 15; break; case YAM_MONSTER: CreateItem( CREATURE_YOUNG_MALE_CLAWS, 100, &(pSoldier->inv[HANDPOS]) ); CreateItem( CREATURE_YOUNG_MALE_SPIT, 100, &(pSoldier->inv[SECONDHANDPOS]) ); CreateItem( CREATURE_YOUNG_MALE_HIDE, 100, &(pSoldier->inv[HELMETPOS]) ); CreateItem( CREATURE_YOUNG_MALE_HIDE, 100, &(pSoldier->inv[VESTPOS]) ); CreateItem( CREATURE_YOUNG_MALE_HIDE, 100, &(pSoldier->inv[LEGPOS]) ); uiChanceToDrop = 15; fMaleCreature = TRUE; break; case INFANT_MONSTER: CreateItem( CREATURE_INFANT_SPIT, 100, &(pSoldier->inv[HANDPOS]) ); CreateItem( CREATURE_INFANT_HIDE, 100, &(pSoldier->inv[HELMETPOS]) ); CreateItem( CREATURE_INFANT_HIDE, 100, &(pSoldier->inv[VESTPOS]) ); CreateItem( CREATURE_INFANT_HIDE, 100, &(pSoldier->inv[LEGPOS]) ); uiChanceToDrop = 5; break; case LARVAE_MONSTER: uiChanceToDrop = 0; break; case QUEENMONSTER: CreateItem( CREATURE_QUEEN_SPIT, 100, &(pSoldier->inv[HANDPOS]) ); CreateItem( CREATURE_QUEEN_TENTACLES, 100, &(pSoldier->inv[SECONDHANDPOS]) ); CreateItem( CREATURE_QUEEN_HIDE, 100, &(pSoldier->inv[HELMETPOS]) ); CreateItem( CREATURE_QUEEN_HIDE, 100, &(pSoldier->inv[VESTPOS]) ); CreateItem( CREATURE_QUEEN_HIDE, 100, &(pSoldier->inv[LEGPOS]) ); // she can't drop anything, because the items are unreachable anyways (she's too big!) uiChanceToDrop = 0; break; case BLOODCAT: CreateItem( BLOODCAT_CLAW_ATTACK, 100, &(pSoldier->inv[HANDPOS]) ); CreateItem( BLOODCAT_BITE, 100, &(pSoldier->inv[SECONDHANDPOS]) ); fBloodcat = TRUE; uiChanceToDrop = 30; break; default: AssertMsg( FALSE, String( "Invalid creature bodytype %d", pSoldier->ubBodyType ) ); return; } // decide if the creature will drop any REAL bodyparts if (Random(100) < uiChanceToDrop) { CreateItem( (UINT16)(fBloodcat ? BLOODCAT_CLAWS : CREATURE_PART_CLAWS), (INT8) (80 + Random(21)), &(pSoldier->inv[BIGPOCK1POS]) ); } if (Random(100) < uiChanceToDrop) { CreateItem( (UINT16)(fBloodcat ? BLOODCAT_TEETH : CREATURE_PART_FLESH), (INT8) (80 + Random(21)), &(pSoldier->inv[BIGPOCK2POS]) ); } // as requested by ATE, males are more likely to drop their "organs" (he actually suggested this, I'm serious!) if (fMaleCreature) { // increase chance by 50% uiChanceToDrop += (uiChanceToDrop / 2); } if (Random(100) < uiChanceToDrop) { CreateItem( (UINT16)(fBloodcat ? BLOODCAT_PELT : CREATURE_PART_ORGAN), (INT8) (80 + Random(21)), &(pSoldier->inv[BIGPOCK3POS]) ); } } void ReplaceExtendedGuns( SOLDIERCREATE_STRUCT *pp, INT8 bSoldierClass ) { UINT32 uiLoop, uiLoop2; INT8 bWeaponClass; UINT16 usItem, usNewGun, usAmmo, usNewAmmo; UINT32 invsize = pp->Inv.size(); for ( uiLoop = 0; uiLoop < invsize; ++uiLoop ) { usItem = pp->Inv[ uiLoop ].usItem; if ( ( Item[ usItem ].usItemClass & IC_GUN ) && ItemIsOnlyInTonsOfGuns( usItem ) ) { if ( bSoldierClass == SOLDIER_CLASS_NONE ) { usNewGun = StandardGunListReplacement( usItem ); } else { bWeaponClass = GetWeaponClass( usItem ); AssertMsg( bWeaponClass != -1, String( "Gun %d does not have a match in the extended gun array", usItem ) ); usNewGun = SelectStandardArmyGun( bWeaponClass, bSoldierClass ); } if ( usNewGun != NOTHING ) { //We are creating a new gun, but the new gun needs the old gun's attachments OBJECTTYPE* pObj = &(pp->Inv[ uiLoop ]); CreateItem( usNewGun, (*pObj)[0]->data.gun.bGunStatus, &gTempObject ); gTempObject.fFlags = (*pObj).fFlags; //init attachments InitItemAttachments(pObj); for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) { if(iter->exists()){ gTempObject.AttachObject(0, &(*iter), FALSE); } } //copy it over *pObj = gTempObject; // must search through inventory and replace ammo accordingly UINT32 invsize = pp->Inv.size(); for ( uiLoop2 = 0; uiLoop2 < invsize; ++uiLoop2 ) { usAmmo = pp->Inv[ uiLoop2 ].usItem; if ( (Item[ usAmmo ].usItemClass & IC_AMMO) ) { usNewAmmo = FindReplacementMagazineIfNecessary( usItem, usAmmo, usNewGun ); if (usNewAmmo != NOTHING ) { // found a new magazine, replace... CreateItems( usNewAmmo, 100, pp->Inv[ uiLoop2 ].ubNumberOfObjects, &( pp->Inv[ uiLoop2 ] ) ); } } } } } } } UINT16 SelectStandardArmyGun( UINT8 uiGunLevel, INT8 bSoldierClass ) { ARMY_GUN_CHOICE_TYPE *pGunChoiceTable; int uiChoice; int usGunIndex; // pick the standard army gun for this weapon class from table // usGunIndex = gStrategicStatus.ubStandardArmyGunIndex[uiGunLevel]; // decided to randomize it afterall instead of repeating the same weapon over and over // depending on selection of the gun nut option //if (gGameOptions.fGunNut) //{ // use table of extended gun choices // Flugente: if accessing with wrong soldier class, or not using different selection choices, take default one if ( bSoldierClass >= SOLDIER_GUN_CHOICE_SELECTIONS || bSoldierClass < SOLDIER_CLASS_NONE || !gGameExternalOptions.fSoldierClassSpecificItemTables ) bSoldierClass = SOLDIER_CLASS_NONE; pGunChoiceTable = &(gExtendedArmyGunChoices[bSoldierClass][0]); //} //else //{ // // use table of regular gun choices // pGunChoiceTable = &(gRegularArmyGunChoices[0]); //} // choose one the of the possible gun choices usGunIndex = -1; BOOLEAN isnight = NightTime(); while (usGunIndex == -1) { uiChoice = Random(pGunChoiceTable[ uiGunLevel ].ubChoices); usGunIndex = pGunChoiceTable[ uiGunLevel ].bItemNo[ uiChoice ]; if (!ItemIsLegal(usGunIndex)) //Madd: check for tons of guns usGunIndex = -1; //Check to avoid an endless loop looking for "normal" guns if (usGunIndex == -1) { //Madd: there better be something from the original JA2 guns here somewhere (biggunlist=0)! int numTries = 0; //Try 5 more times... while (numTries < 5 && usGunIndex == -1) { uiChoice = Random(pGunChoiceTable[ uiGunLevel ].ubChoices); usGunIndex = pGunChoiceTable[ uiGunLevel ].bItemNo[ uiChoice ]; if (!ItemIsLegal(usGunIndex)) //Madd: check for tons of guns usGunIndex = -1; numTries++; } if (usGunIndex == -1) //We still haven't found one! Start just looping through the guns then { for (int i=0;iInv[ HANDPOS ]) ); pp->Inv[ HANDPOS ].fFlags |= OBJECT_UNDROPPABLE; // tanks don't deplete shells or ammo... CreateItems( TANK_SHELL, 100, 1, &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); // machine gun CreateItems( MINIMI, (INT8)(80 + Random( 21 )), 1, &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); // smoke grenade CreateItems( SMOKE_GRENADE, (INT8)(80 + Random( 21 )), 1, &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); } else if ( COMBAT_JEEP(pp) ) { // machine gun CreateItems( MINIMI, (INT8)(80 + Random( 21 )), 1, &(pp->Inv[HANDPOS]) ); pp->Inv[HANDPOS].fFlags |= OBJECT_UNDROPPABLE; } else if ( ENEMYROBOT(pp) ) { // machine gun CreateItems( MINIMI, (INT8)(80 + Random( 21 )), 1, &(pp->Inv[HANDPOS]) ); pp->Inv[HANDPOS].fFlags |= OBJECT_UNDROPPABLE; } // armour equal to spectra all over (for vs explosives) CreateItem( SPECTRA_VEST, 100, &(pp->Inv[ VESTPOS ]) ); pp->Inv[ VESTPOS ].fFlags |= OBJECT_UNDROPPABLE; CreateItem( SPECTRA_HELMET, 100, &(pp->Inv[ HELMETPOS ]) ); pp->Inv[ HELMETPOS ].fFlags |= OBJECT_UNDROPPABLE; CreateItem( SPECTRA_LEGGINGS, 100, &(pp->Inv[ LEGPOS ]) ); pp->Inv[ LEGPOS ].fFlags |= OBJECT_UNDROPPABLE; } void ResetMortarsOnTeamCount( void ) { guiMortarsRolledByTeam = 0; } UINT16 PickARandomItem(UINT8 typeIndex, INT8 bSoldierClass) { return PickARandomItem(typeIndex, bSoldierClass, 100,FALSE); } UINT16 PickARandomItem(UINT8 typeIndex, INT8 bSoldierClass, UINT8 wantedCoolness) { return PickARandomItem(typeIndex, bSoldierClass, wantedCoolness,TRUE); } UINT16 PickARandomItem(UINT8 typeIndex, INT8 bSoldierClass, UINT8 wantedCoolness, BOOLEAN getMatchingCoolness) { //DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("PickARandomItem: typeIndex = %d, maxCoolness = %d, getMatchingCoolness = %d",typeIndex,maxCoolness,getMatchingCoolness)); UINT16 usItem = 0; UINT32 uiChoice; UINT16 defaultItem = 0; BOOLEAN pickItem = FALSE; // Flugente: if accessing with wrong soldier class, or not using different selection choices, take default one if ( bSoldierClass >= SOLDIER_GUN_CHOICE_SELECTIONS || bSoldierClass < SOLDIER_CLASS_NONE || !gGameExternalOptions.fSoldierClassSpecificItemTables ) bSoldierClass = SOLDIER_CLASS_NONE; if ( gArmyItemChoices[bSoldierClass][ typeIndex ].ubChoices <= 0 ) return 0; BOOLEAN isnight = NightTime(); // check up to 10 times for an item with a matching coolness for (int i=0; i < 10; ++i) { //if we've already tried more times then there are items + 1, limit the looping to speed up the game, and just plain give up if ( i > gArmyItemChoices[bSoldierClass][ typeIndex ].ubChoices ) break; uiChoice = Random(gArmyItemChoices[bSoldierClass][typeIndex].ubChoices + (int)(gArmyItemChoices[bSoldierClass][typeIndex].ubChoices / 3)); if ( uiChoice >= gArmyItemChoices[bSoldierClass][ typeIndex ].ubChoices ) { if ( !getMatchingCoolness ) return 0; else uiChoice = Random(gArmyItemChoices[bSoldierClass][ typeIndex ].ubChoices); } usItem = gArmyItemChoices[bSoldierClass][ typeIndex ].bItemNo[ uiChoice ]; // Flugente: ignore this item if we aren't allowed to pick it at this time of day if ( ( isnight && Item[usItem].usItemChoiceTimeSetting == 1 ) || ( !isnight && Item[usItem].usItemChoiceTimeSetting == 2 ) ) continue; // Flugente: random items UINT16 newitemfromrandom = 0; if ( GetItemFromRandomItem(usItem, &newitemfromrandom) ) usItem = newitemfromrandom; pickItem = FALSE; if (usItem >= 0 && Item[usItem].ubCoolness <= wantedCoolness && ItemIsLegal(usItem)) { // On day if (DayTime() == TRUE) { // Only pick items, that have not a negative day vision bonus range // So we only pick normal items and day items (sun googles, ...) if (Item[usItem].dayvisionrangebonus >= 0 ) { pickItem = TRUE; } } // At night else { // Only pick items, that have not a negative night vision bonus range // So we only pick normal items and night items (NVG, ...) if (Item[usItem].nightvisionrangebonus >= 0 ) { pickItem = TRUE; } } if (Item[usItem].randomitem > 0) pickItem = FALSE; } //Madd: quickfix: don't use NVGs during the day, and no sungoggles at night either //if ( usItem >= 0 && Item[usItem].ubCoolness <= maxCoolness && ItemIsLegal(usItem) && (( DayTime() && Item[usItem].nightvisionrangebonus == 0 ) || ( NightTime() && Item[usItem].dayvisionrangebonus == 0 ))) if (pickItem == TRUE) { // pick a default item in case we don't find anything with a matching coolness, but pick the most matching (by coolness) item if (defaultItem == 0 || Item[usItem].ubCoolness > Item[defaultItem].ubCoolness) { defaultItem = usItem; } // found something with the right coolness if ( Item[usItem].ubCoolness == wantedCoolness || !getMatchingCoolness ) return usItem; } } // couldn't find anything with the exact matching coolness, so return the best item we did find if (ItemIsLegal(defaultItem)) return defaultItem; else return 0; } UINT16 PickARandomAttachment(UINT8 typeIndex, INT8 bSoldierClass, UINT16 usBaseItem, UINT8 maxCoolness, BOOLEAN getMatchingCoolness) { //DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("PickARandomAttachment: baseItem = %d, typeIndex = %d, maxCoolness = %d, getMatchingCoolness = %d",usBaseItem, typeIndex,maxCoolness,getMatchingCoolness)); UINT16 usItem = 0; UINT32 uiChoice; UINT16 defaultItem = 0; // Flugente: if accessing with wrong soldier class, or not using different selection choices, take default one if ( bSoldierClass >= SOLDIER_GUN_CHOICE_SELECTIONS || bSoldierClass < SOLDIER_CLASS_NONE || !gGameExternalOptions.fSoldierClassSpecificItemTables ) bSoldierClass = SOLDIER_CLASS_NONE; // DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("PickARandomAttachment: # choices = %d", gArmyItemChoices[ typeIndex ].ubChoices )); if ( gArmyItemChoices[bSoldierClass][ typeIndex ].ubChoices <= 0 ) return 0; BOOLEAN isnight = NightTime(); // check up to 10 times for an item with a matching coolness for (int i=0; i < 50; i++) { //if we've already tried more times then there are items + 1, limit the looping to speed up the game, and just plain give up if ( i > gArmyItemChoices[bSoldierClass][ typeIndex ].ubChoices ) break; uiChoice = Random(gArmyItemChoices[bSoldierClass][ typeIndex ].ubChoices); usItem = gArmyItemChoices[bSoldierClass][ typeIndex ].bItemNo[ uiChoice ]; // Flugente: ignore this item if we aren't allowed to pick it at this time of day if ( ( isnight && Item[usItem].usItemChoiceTimeSetting == 1 ) || ( !isnight && Item[usItem].usItemChoiceTimeSetting == 2 ) ) continue; BOOLEAN fDefaultAttachment = FALSE; for(UINT8 cnt = 0; cnt < MAX_DEFAULT_ATTACHMENTS; cnt++){ if(Item[usBaseItem].defaultattachments[cnt] == 0) break; if(Item[usBaseItem].defaultattachments[cnt] == usItem){ fDefaultAttachment = TRUE; } } if ( usItem >= 0 && Item[usItem].ubCoolness <= maxCoolness && ValidAttachment(usItem,usBaseItem) && !fDefaultAttachment) { // pick a default item in case we don't find anything with a matching coolness, but pick the coolest item we can find if ( defaultItem == 0 || Item[usItem].ubCoolness > Item[defaultItem].ubCoolness ) defaultItem = usItem; // found something with the right coolness if ( (Item[usItem].ubCoolness >= maxCoolness-2) || !getMatchingCoolness ) { // DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("PickARandomAttachment: baseItem = %d, usItem = %d",usBaseItem, usItem)); if (ItemIsLegal(usItem)) return usItem; } } } // couldn't find anything with the exact matching coolness, so return the best item we did find // DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("PickARandomAttachment: baseItem = %d, defaultItem = %d",usBaseItem, defaultItem)); if (ItemIsLegal(defaultItem)) return defaultItem; else return 0; } ////////////////// Flugente: militia equipment feature /////////////////////////////////// // determines how good an item is UINT32 ItemFitness( OBJECTTYPE* pObj, UINT8 idx ) { UINT32 value = 0; if ( Item[ pObj->usItem ].usItemClass & IC_ARMOUR ) { value = Armour[ Item[ pObj->usItem ].ubClassIndex ].ubProtection * Armour[ Item[ pObj->usItem ].ubClassIndex ].ubCoverage * (*pObj)[idx]->data.objectStatus; } else if ( Item[ pObj->usItem ].usItemClass & IC_FACE ) { if (ItemIsGasmask(pObj->usItem)) value = (*pObj)[idx]->data.objectStatus; else if ( Item[ pObj->usItem ].hearingrangebonus ) { value = Item[ pObj->usItem ].hearingrangebonus; } else { value = (*pObj)[idx]->data.objectStatus * max(0, max( Item[ pObj->usItem ].nightvisionrangebonus, Item[ pObj->usItem ].dayvisionrangebonus ) ); } } else if ( Item[ pObj->usItem ].usItemClass & (IC_BLADE|IC_PUNCH) ) { value = GetDamage(pObj); value *= (*pObj)[idx]->data.objectStatus; } else if ( Item[ pObj->usItem ].usItemClass & (IC_GUN|IC_LAUNCHER) ) { // value is damage * range * firing rate, modified by number of attachments and accuracy value = GetDamage(pObj) * GunRange(pObj, NULL) * max(1, GetAutofireShotsPerFiveAPs(pObj)); value = value * (10 + (UINT32)GetGunAccuracy(pObj)) / 10; value = value * (10 + (*pObj)[0]->AttachmentListSize()) / 10; value *= (*pObj)[idx]->data.objectStatus; } // default else { value = (*pObj)[idx]->data.objectStatus * Item[pObj->usItem].ubCoolness; } return value; } // how much ammo does this item need? UINT32 GetNeededTotalAmmo( UINT16 usItem ) { // determine what amount of ammo is deemed 'enough' for this gun return max(gGameExternalOptions.usMilitiaAmmo_Min, min(gGameExternalOptions.usMilitiaAmmo_Max, gGameExternalOptions.usMilitiaAmmo_OptimalMagCount * Weapon[ usItem ].ubMagSize)); } // special version of ItemFitness() that takes ammo count into consideration UINT32 ItemFitness_WithAmmo( OBJECTTYPE* pObj, UINT8 idx, UINT32 uiBullets ) { UINT32 optimalammo = GetNeededTotalAmmo(pObj->usItem); return (ItemFitness(pObj, idx) * min(uiBullets, optimalammo) ) / max(1, optimalammo); } // retreives the next free slot in pWorldItem (we want to keep it short and not add too many items) UINT32 GetNextFreeIndex( std::vector& pWorldItem, UINT32 uiMaxCount, UINT32 uiCount )//dnl ch75 271013 { for( ; uiCount < uiMaxCount; ++uiCount ) { if( !pWorldItem[ uiCount ].fExists ) return uiCount; } return -1; } // These are the things we are searching for enum { SI_HELMET = 0, SI_VEST, SI_LEGS, SI_SIGHT, // for sunglasses and NVGs SI_FACE2, SI_FACE_SPARESIGHT, // for sunglasses at night an NVGs during the day - militia take those as well, otherwise mobiles are ill equipped when the night begins/ends SI_GASMASK, SI_MELEE, SI_GRENADE, SI_GUN, SI_LAUNCHER, SI_MAX, } searchItem; // this struct is used to store the location of the best item of a category, we use it to later easily retrieve it from the sector inventory struct ItemSearchStruct { ItemSearchStruct::ItemSearchStruct() : found(FALSE), done(FALSE), pos(0), slot(0), soldierslot(0), val(0) {} BOOLEAN found; // has any item been found for this? BOOLEAN done; // are we done evaluating this item? UINT32 pos; // position in pWorldItem of this object UINT8 slot; // slot of the specific object on the position in pWorldItem UINT16 soldierslot; // slot on which this item should be equipped UINT32 val; // the fitness value of this object }; void addAttachementsToMilitiaWeapon(std::vector& pWorldItem, UINT16 usTabooFlag, SOLDIERCREATE_STRUCT *pp); // evaluate an object an remember it if it is the best so far void EvaluateObjForItem( std::vector& pWorldItem, OBJECTTYPE* pObj, UINT32 uiCount, ItemSearchStruct* pSi )//dnl ch75 271013 { // safety first if ( pWorldItem.empty() || !pObj || !pSi ) // THIS SHOULDN'T HAPPEN! return; for(UINT8 i = 0; i < pObj->ubNumberOfObjects; ++i) { UINT32 fitness = ItemFitness(pObj, i); if ( !pSi->found || fitness > pSi->val ) { pSi->found = TRUE; pSi->pos = uiCount; pSi->slot = i; pSi->val = fitness; } } } // special version of EvaluateObjForItem(): we take the existing ammo count into consideration void EvaluateObjForItem_WithAmmo( std::vector& pWorldItem, OBJECTTYPE* pObj, UINT32 uiCount, ItemSearchStruct* pSi, UINT32 uiBullets )//dnl ch75 271013 { // safety first if ( pWorldItem.empty() || !pObj || !pSi ) // THIS SHOULDN'T HAPPEN! return; for(UINT8 i = 0; i < pObj->ubNumberOfObjects; ++i) { UINT32 fitness = ItemFitness_WithAmmo(pObj, i, uiBullets); if ( !pSi->found || fitness > pSi->val ) { pSi->found = TRUE; pSi->pos = uiCount; pSi->slot = i; pSi->val = fitness; } } } /** Makes sure that stack of items on stacksIndexInWorldItem is removed, if it does not contain any items now. Use after removing stuff from pWorldItem like by attaching it to something or by putting it into inventory. */ void RemoveStackIfEmpty(std::vector& pWorldItem, UINT32 stacksIndexInWorldItem) { if (pWorldItem[stacksIndexInWorldItem].object.ubNumberOfObjects < 1) { // account for items with invalid gridnos... if (pWorldItem[stacksIndexInWorldItem].sGridNo != NOWHERE) { RemoveItemFromPool(pWorldItem[stacksIndexInWorldItem].sGridNo, stacksIndexInWorldItem, pWorldItem[stacksIndexInWorldItem].ubLevel); } // setting this to false can lead to cases where we 'forget' items without a valid gridno - though I am unsure why. //pWorldItem[ pSi->pos ].fExists = FALSE; } } // forward declaration for default parameter // if pSi has an entry, move gun from pWorldItem into pp void SearchItemRetrieval( std::vector& pWorldItem, ItemSearchStruct* pSi, SOLDIERCREATE_STRUCT *pp, UINT8 usTake = 1 );//dnl ch75 271013 /** Used after taking item from world and putting it into militias inventory. Makes sure TAKEN_BY_MILITIA flags are set in manner reflecting the flags on items stack of origin. */ void SetTakenByMilitiaFlagsWhenForRetrievedItem(std::vector& pWorldItem, UINT32 itemsPositionInWorld, SOLDIERCREATE_STRUCT *pp, UINT32 slotInInventory, UINT8 numberOfItemsToMark) { for (UINT8 i = 0; i < numberOfItemsToMark; ++i) { (pp->Inv[slotInInventory])[i]->data.sObjectFlag |= TAKEN_BY_MILITIA; } if (pWorldItem[itemsPositionInWorld].usFlags & WORLD_ITEM_TABOO_FOR_MILITIA_EQ_GREEN) (pp->Inv[slotInInventory])[0]->data.sObjectFlag |= TAKEN_BY_MILITIA_TABOO_GREEN; //remember the taboo we set on item while it was on ground if (pWorldItem[itemsPositionInWorld].usFlags & WORLD_ITEM_TABOO_FOR_MILITIA_EQ_BLUE) (pp->Inv[slotInInventory])[0]->data.sObjectFlag |= TAKEN_BY_MILITIA_TABOO_BLUE; } // if pSi has an entry, move gun from pWorldItem into pp void SearchItemRetrieval( std::vector& pWorldItem, ItemSearchStruct* pSi, SOLDIERCREATE_STRUCT *pp, UINT8 usTake )//dnl ch75 271013 { if ( pSi->found && !pSi->done ) { //never take more than there actually is on the given stack (atm. we try to take 2 grenades, but there might be only 1) UINT8 usRealTake = min(usTake, pWorldItem[ pSi->pos ].object.ubNumberOfObjects); //move objects (like weapon) to soldiers slot, position of object in the world and target slot are determined by values in pSi pWorldItem[ pSi->pos ].object.MoveThisObjectTo(pp->Inv[ pSi->soldierslot ], usRealTake ); SetTakenByMilitiaFlagsWhenForRetrievedItem(pWorldItem, pSi->pos, pp, pSi->soldierslot, usRealTake); // account for items with invalid gridnos - when we take last item from the stack, the whole stack object should be gone RemoveStackIfEmpty(pWorldItem, pSi->pos); } pSi->done = TRUE; } typedef std::map AmmoType_BulletCountMap; // used to count how many bullets of an ammotype we have typedef std::map Calibre_BulletCountMap; // this map stores a map containing all the different ammotypes and how many bulltes we have for them struct LauncherHelpStruct { LauncherHelpStruct::LauncherHelpStruct() : fNeedsAmmo(TRUE), ammocount(0) {} BOOLEAN fNeedsAmmo; // the launcher isn't singleshot (LAW), it needs ammo UINT16 ammocount; // so much ammo found for this }; typedef std::map LauncherHelpMap; // how many bullets does this map have? (used for one caliber each) UINT32 GetTotalCalibreAmmo( AmmoType_BulletCountMap* pAmmoBulletMap ) { UINT32 bullets = 0; AmmoType_BulletCountMap::iterator ammotype_itend = pAmmoBulletMap->end(); for( AmmoType_BulletCountMap::iterator ammotype_it = pAmmoBulletMap->begin(); ammotype_it != ammotype_itend; ++ammotype_it ) { bullets += (*ammotype_it).second; } return bullets; } // are there enough bullets for the gun? BOOLEAN EnoughBulletsForGun( UINT16 usItem, AmmoType_BulletCountMap* pAmmoBulletMap ) { if ( !pAmmoBulletMap || !(Item[usItem].usItemClass & IC_GUN) ) return FALSE; // for now, we are satisfied if we have enough bullets for at least 2 magazines/at least 10 bullets/max 200 bullets. This could be externalised... if ( GetTotalCalibreAmmo(pAmmoBulletMap) >= GetNeededTotalAmmo(usItem) ) return TRUE; return FALSE; } // spawn fitting ammo for pObj into pp void SpawnFittingAmmo(SOLDIERCREATE_STRUCT *pp, OBJECTTYPE* pObj, UINT8 ammotype, UINT32 numberofbullets ) { if ( !pObj || numberofbullets == 0 || !(Item[pObj->usItem].usItemClass & IC_GUN) ) return; UINT16 magsize = GetMagSize(pObj); UINT16 magitem = FindReplacementMagazine( Weapon[ pObj->usItem ].ubCalibre, magsize, ammotype ); if ( !magsize || !magitem ) return; UINT16 deductedbullets = 0; if ( (*pObj)[0]->data.gun.ubGunShotsLeft == 0 ) { (*pObj)[0]->data.gun.ubGunAmmoType = ammotype; (*pObj)[0]->data.gun.usGunAmmoItem = magitem; deductedbullets = min(magsize, numberofbullets); numberofbullets -= deductedbullets; (*pObj)[0]->data.gun.ubGunShotsLeft = deductedbullets; } while ( numberofbullets > 0 ) { deductedbullets = min(magsize, numberofbullets); numberofbullets -= deductedbullets; // create ammo in inventory CreateAmmo(magitem, &gTempObject, deductedbullets); gTempObject[0]->data.sObjectFlag |= TAKEN_BY_MILITIA; PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); // if we have as many bullets as we wanted, get out of here if ( !numberofbullets ) return; } } void MoveOneMilitiaEquipmentSet(INT16 sSourceX, INT16 sSourceY, INT16 sTargetX, INT16 sTargetY, INT8 bSoldierClass) { UINT32 uiTotalNumberOfRealItems = 0; std::vector pWorldItem;//dnl ch75 271013 SOLDIERCREATE_STRUCT tmp; UINT32 uiCount = 0; INT32 dummygridno = NOWHERE; if ( !gGameExternalOptions.fMilitiaUseSectorInventory ) return; TakeMilitiaEquipmentfromSector(sSourceX, sSourceY, 0, &tmp, bSoldierClass); // open target sector inv if( ( gWorldSectorX == sTargetX )&&( gWorldSectorY == sTargetY ) && (gbWorldSectorZ == 0 ) ) { uiTotalNumberOfRealItems = guiNumWorldItems; pWorldItem = gWorldItems; } else { // not loaded, load const auto ii = FindWorldItemSector(sTargetX, sTargetY, 0); if (ii != -1) { uiTotalNumberOfRealItems = gAllWorldItems.NumItems[ii]; pWorldItem = gAllWorldItems.Items[ii]; } else { uiTotalNumberOfRealItems = 10; pWorldItem.resize(uiTotalNumberOfRealItems); } } // we note the last item existing in the inventory (but not ammo, as we delete those). We use this to assess how much we really need to increase the inventory UINT32 existingitemsfound = 0; for( uiCount = 0; uiCount < uiTotalNumberOfRealItems; ++uiCount ) // ... for all items in the world ... { if( pWorldItem[ uiCount ].fExists ) // ... if item exists ... { ++existingitemsfound; dummygridno = pWorldItem[ uiCount ].sGridNo; } } if ( dummygridno == NOWHERE ) { dummygridno = RandomGridNo(); if ( dummygridno == NOWHERE ) return; } /////////////////////////////// ADD ITEMS FROM STRUCT TO SECTOR ///////////////////////////////////////////////////// // We will now add the items from tmp to the target sector // first, determine how many items we added, and add an increased copy of pWorldItem UINT16 numnewitems = 0; UINT8 invsize = tmp.Inv.size(); for (UINT8 i = 0; i < invsize; ++i) { if ( tmp.Inv[ i ].exists() ) ++numnewitems; } // new total size needed is number of existing items found + number of needed ammo crate objects, but at least uiTotalNumberOfRealItems: UINT32 uiNewInvSize = max(uiTotalNumberOfRealItems, existingitemsfound + numnewitems); // create a bigger inventory wit big enough size std::vector pWorldItem_tmp(uiNewInvSize);//dnl ch75 271013 // copy over old inventory for( uiCount = 0; uiCount < uiTotalNumberOfRealItems; ++uiCount ) { pWorldItem_tmp[uiCount] = pWorldItem[uiCount]; } uiCount = 0; // now add the items from the struct for (UINT8 i = 0; i < invsize; ++i) { // only add existing items that were taken by militia (it can happen that we generate fitting ammo, do not take that) if ( tmp.Inv[ i ].exists() && tmp.Inv[ i ][0]->data.sObjectFlag & TAKEN_BY_MILITIA ) { uiCount = GetNextFreeIndex( pWorldItem_tmp, uiNewInvSize, uiCount ); if ( uiCount < 0 ) { // Error! this shouldn't happen! return; } pWorldItem_tmp[uiCount].fExists = TRUE; pWorldItem_tmp[uiCount].sGridNo = dummygridno; pWorldItem_tmp[uiCount].ubLevel = 0; pWorldItem_tmp[uiCount].usFlags = WORLD_ITEM_REACHABLE; pWorldItem_tmp[uiCount].bVisible = 1; pWorldItem_tmp[uiCount].bRenderZHeightAboveLevel = 0; pWorldItem_tmp[uiCount].soldierID = -1; pWorldItem_tmp[uiCount].object = tmp.Inv[ i ]; } } // use the new map uiTotalNumberOfRealItems = uiNewInvSize; pWorldItem = pWorldItem_tmp; /////////////////////////////// ADD ITEMS FROM STRUCT TO SECTOR ///////////////////////////////////////////////////// // save the changed intentory // open sector inv if( ( gWorldSectorX == sTargetX )&&( gWorldSectorY == sTargetY ) && (gbWorldSectorZ == 0 ) ) { guiNumWorldItems = uiTotalNumberOfRealItems; gWorldItems = pWorldItem; } else { UpdateWorldItems(sTargetX, sTargetY, 0, uiTotalNumberOfRealItems, pWorldItem); } } void MoveMilitiaEquipment(INT16 sSourceX, INT16 sSourceY, INT16 sTargetX, INT16 sTargetY, UINT8 usElites, UINT8 usRegulars, UINT8 usGreens) { if ( !gGameExternalOptions.fMilitiaUseSectorInventory ) return; // atm there is no class-specific selection, but that might change in the future for (UINT8 i = 0; i < usElites; ++i) { MoveOneMilitiaEquipmentSet( sSourceX, sSourceY, sTargetX, sTargetY, SOLDIER_CLASS_ELITE_MILITIA); } for (UINT8 i = 0; i < usRegulars; ++i) { MoveOneMilitiaEquipmentSet( sSourceX, sSourceY, sTargetX, sTargetY, SOLDIER_CLASS_REG_MILITIA); } for (UINT8 i = 0; i < usGreens; ++i) { MoveOneMilitiaEquipmentSet( sSourceX, sSourceY, sTargetX, sTargetY, SOLDIER_CLASS_GREEN_MILITIA); } } // This function loads the sector inventory of a given sector and fills the inventory of a SOLDIERCREATE_STRUCT according to the game settings. The soldier class is a parameter, but is currently not used // This function is rather lengthy, this is due to the fact that we have to loop over the entire sector inventory mulitple times. Below is a small documentation. If you change anything, please update it - Flugente // We loop over the inventory 3 times. I'll briefly explain why. After each loop we do a few other things - like taking items or doing further evaluation // Note that any items we checked have to be reachable AND not be 'taboo' to the militia. // Preparation: load correct sector inventory // init selection structures // decide which item categories to look for // delete existing soldier inventory, but account for gear already selected by this function (failsafe) // early exit if no items are found in sector at all // 1st loop: Count all ammo in ammo and guns, evaluate all armour/face items/hand grenades/melee items/guns/launchers // We do not yet evaluate guns - we do that later, when we know what ammo exists // We store ammo in a map that also stores caliber and ammotype // We store launchers in a structure that stores their item, wether they need ammo, and how much ammo we found // We evaluate objects via EvaluateObjForItem(...). If you are unsatisfied with militia selections, try to improve this funtion first // 1st loop afterwork: If we are unable to determine a valid GridNo, something is wrong here, abort // Definite decision on armour/face items/melee items/hand grenades // 2nd loop: Evaluate all guns, taking ammo into account (we know how much ammo there is, as we wrote that into our map) // We know know what launchers that need ammo we have here, thus we can test bombs and grenades on wether they are such ammo. Note this in the launcher map // 2nd loop afterwork: We now know all launchers and their ammo, we decide which launcher to take // Decide on a gun, we can now do that as we know all ammo // If we have selected a gun, but do not take ammo from the inventory, spawn ammo in the traditional way // 3rd loop: If we ever decide to pick up a gun, we will have done so by now. We will look for fitting ammo in all ammo/guns, spawn correct magazines in our inventory, and remove ammo from found objects // As we have decided on which launcher we want, we evaluate all launchers of this type and take if afterwards. We also move enough ammo into our inventory. // If we created a new mag from the gun we took, try to add this to an empty inventory slot, if none is found, enlarge the inventory afterwards // 3rd loop afterwork: Take launcher // Add mag if created and it couldn't be added to empty slot earlier by enlarging the inventory // Exit: Save changed inventory void TakeMilitiaEquipmentfromSector( INT16 sMapX, INT16 sMapY, INT8 sMapZ, SOLDIERCREATE_STRUCT *pp, INT8 bSoldierClass ) { BOOLEAN fReturn = FALSE; UINT32 uiTotalNumberOfRealItems = 0; std::vector pWorldItem;//dnl ch75 271013 UINT32 uiCount = 0; INT32 dummygridno = NOWHERE; // this gridno will be the new position of items we create (ammo crates) BOOLEAN fNightTime = NightTime(); ItemSearchStruct si[SI_MAX]; // this struct stores the necessary data on the items we find during our search Calibre_BulletCountMap calibrebulletountmap; // a struct that saves all ammo we will find UINT16 numcrates = 0; BOOLEAN fNewMagCreated = FALSE; OBJECTTYPE newAmmoObj; BOOLEAN fSearchForAmmo = TRUE; UINT16 usLauncherItem = 0; UINT8 usLauncherAmmoLeftToTake = 0; UINT16 usSelectedGunBulletsNeeded = 0; // how many bullets do we want for our gun UINT16 usSelectedGunBulletCount = 0; // how many bullets have we already taken for our selected gun UINT16 usTabooFlag = 0; if ( !gGameExternalOptions.fMilitiaUseSectorInventory ) return; // depending on our bSoldierClass, we may be forbidden from taking items the player has flagged // note that in the current implmentation, items with the WORLD_ITEM_TABOO_FOR_MILITIA_EQ_GREEN-flag will have the higher flags as well, but that might change in the future if ( bSoldierClass == SOLDIER_CLASS_ELITE_MILITIA ) usTabooFlag = WORLD_ITEM_TABOO_FOR_MILITIA_EQ_ELITE; else if ( bSoldierClass == SOLDIER_CLASS_REG_MILITIA ) usTabooFlag = WORLD_ITEM_TABOO_FOR_MILITIA_EQ_BLUE; else usTabooFlag = WORLD_ITEM_TABOO_FOR_MILITIA_EQ_GREEN; ///////////////////////////////// Preparation ///////////////////////////////////////////////////////// // Preparation: load correct sector inventory // init selection structures // decide which item categories to look for // delete existing soldier inventory, but account for gear already selected by this function (failsafe) // early exit if no items are found in sector at all // open sector inv if( ( gWorldSectorX == sMapX )&&( gWorldSectorY == sMapY ) && (gbWorldSectorZ == sMapZ ) ) { uiTotalNumberOfRealItems = guiNumWorldItems; pWorldItem = gWorldItems; } else { const auto ii = FindWorldItemSector(sMapX, sMapY, (INT8)(sMapZ)); if (ii != -1) { uiTotalNumberOfRealItems = gAllWorldItems.NumItems[ii]; pWorldItem = gAllWorldItems.Items[ii]; } else { uiTotalNumberOfRealItems = 0; } } // this vector isn't used anywhere - it simply allows us to easily view the contents of the inventory in debug mode #ifdef JA2TESTVERSION std::vector > overviewvector; for( uiCount = 0; uiCount < uiTotalNumberOfRealItems; ++uiCount ) { overviewvector.push_back( std::pair(pWorldItem[ uiCount ].fExists, pWorldItem[ uiCount ].object.usItem) ); } #endif // initialize soldier slots si[SI_HELMET].soldierslot = HELMETPOS; si[SI_VEST].soldierslot = VESTPOS; si[SI_LEGS].soldierslot = LEGPOS; si[SI_SIGHT].soldierslot = HEAD1POS; si[SI_FACE2].soldierslot = HEAD2POS; si[SI_FACE_SPARESIGHT].soldierslot = MEDPOCK1POS; si[SI_GASMASK].soldierslot = MEDPOCK2POS; si[SI_MELEE].soldierslot = BIGPOCK1POS; si[SI_GRENADE].soldierslot = BIGPOCK2POS; si[SI_GUN].soldierslot = HANDPOS; si[SI_LAUNCHER].soldierslot = BIGPOCK3POS; // depending on gamesettings, we only have to search for some items si[SI_HELMET].done = !gGameExternalOptions.fMilitiaUseSectorInventory_Armour; si[SI_VEST].done = !gGameExternalOptions.fMilitiaUseSectorInventory_Armour; si[SI_LEGS].done = !gGameExternalOptions.fMilitiaUseSectorInventory_Armour; si[SI_SIGHT].done = !gGameExternalOptions.fMilitiaUseSectorInventory_Face; si[SI_FACE2].done = !gGameExternalOptions.fMilitiaUseSectorInventory_Face; si[SI_FACE_SPARESIGHT].done = !gGameExternalOptions.fMilitiaUseSectorInventory_Face; si[SI_GASMASK].done = !gGameExternalOptions.fMilitiaUseSectorInventory_Face; si[SI_MELEE].done = !gGameExternalOptions.fMilitiaUseSectorInventory_Melee; si[SI_GRENADE].done = !gGameExternalOptions.fMilitiaUseSectorInventory_Grenade; si[SI_GUN].done = !gGameExternalOptions.fMilitiaUseSectorInventory_Gun; si[SI_LAUNCHER].done = !gGameExternalOptions.fMilitiaUseSectorInventory_Launcher; fSearchForAmmo = (gGameExternalOptions.fMilitiaUseSectorInventory_Gun && gGameExternalOptions.fMilitiaUseSectorInventory_Ammo); // we only search for ammo if we also search for guns LauncherHelpMap launcherhelpmap; // erase existing soldier inventory, if any UINT8 invsize = pp->Inv.size(); for ( UINT8 i = 0; i < invsize; ++i ) { //clear items, but only if they have write status. if( !(pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE) ) { if ( Item[pp->Inv[ i ].usItem].usItemClass & IC_ARMOUR && gGameExternalOptions.fMilitiaUseSectorInventory_Armour ) { if ( pp->Inv[ i ][0]->data.sObjectFlag & TAKEN_BY_MILITIA ) si[SI_HELMET].done = si[SI_VEST].done = si[SI_LEGS].done = TRUE; else DeleteObj(&pp->Inv[ i ]); } else if ( Item[pp->Inv[ i ].usItem].usItemClass & IC_FACE && gGameExternalOptions.fMilitiaUseSectorInventory_Face ) { if ( pp->Inv[ i ][0]->data.sObjectFlag & TAKEN_BY_MILITIA ) si[SI_SIGHT].done = si[SI_FACE2].done = si[SI_GASMASK].done = si[SI_FACE_SPARESIGHT].done = TRUE; else DeleteObj(&pp->Inv[ i ]); } else if ( Item[pp->Inv[ i ].usItem].usItemClass & (IC_BLADE|IC_PUNCH) && gGameExternalOptions.fMilitiaUseSectorInventory_Melee ) { if ( pp->Inv[ i ][0]->data.sObjectFlag & TAKEN_BY_MILITIA ) si[SI_MELEE].done = TRUE; else DeleteObj(&pp->Inv[ i ]); } else if ( Item[pp->Inv[ i ].usItem].usItemClass & IC_GRENADE && gGameExternalOptions.fMilitiaUseSectorInventory_Grenade ) { if ( pp->Inv[ i ][0]->data.sObjectFlag & TAKEN_BY_MILITIA ) si[SI_GRENADE].done = TRUE; else DeleteObj(&pp->Inv[ i ]); } else if ( Item[pp->Inv[ i ].usItem].usItemClass & (IC_GUN|IC_AMMO) && gGameExternalOptions.fMilitiaUseSectorInventory_Gun ) { if ( pp->Inv[ i ][0]->data.sObjectFlag & TAKEN_BY_MILITIA ) { si[SI_GUN].done = TRUE; fSearchForAmmo = FALSE; } else DeleteObj(&pp->Inv[ i ]); } else if ( Item[pp->Inv[ i ].usItem].usItemClass & IC_LAUNCHER && gGameExternalOptions.fMilitiaUseSectorInventory_Launcher ) { if ( pp->Inv[ i ][0]->data.sObjectFlag & TAKEN_BY_MILITIA ) si[SI_LAUNCHER].done = TRUE; else DeleteObj(&pp->Inv[ i ]); } } } if ( uiTotalNumberOfRealItems == 0 ) { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Militia found no items to equip, uses harsh language instead!" ); return; } ///////////////////////////////// Preparation ///////////////////////////////////////////////////////// ///////////////////////////////// 1st loop ///////////////////////////////////////////////////////// // 1st loop: Count all ammo in ammo and guns, evaluate all armour/face items/hand grenades/melee items/guns/launchers // We do not yet evaluate guns - we do that later, when we know what ammo exists // We store ammo in a map that also stores caliber and ammotype // We store launchers in a structure that stores their item, wether they need ammo, and how much ammo we found // We evaluate objects via EvaluateObjForItem(...). If you are unsatisgied with militia selections, try to improve this funtion first for( uiCount = 0; uiCount < uiTotalNumberOfRealItems; ++uiCount ) // ... for all items in the world ... { if( pWorldItem[ uiCount ].fExists ) // ... if item exists ... { dummygridno = pWorldItem[ uiCount ].sGridNo; OBJECTTYPE* pObj = &(pWorldItem[ uiCount ].object); // ... get pointer for this item ... if ( pObj != NULL && pObj->exists() ) // ... if pointer is not obviously useless ... { // this would be the place where we check wether the militia is allowed to pick up an item depending on its soldierclass // test wether item is reachable and useable by militia if ( (pWorldItem[ uiCount ].usFlags & WORLD_ITEM_REACHABLE) && !(pWorldItem[ uiCount ].usFlags & usTabooFlag) ) { // armour if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & IC_ARMOUR && (!si[SI_HELMET].done || !si[SI_VEST].done || !si[SI_LEGS].done ) ) { switch( Armour[ Item[ pWorldItem[ uiCount ].object.usItem ].ubClassIndex ].ubArmourClass ) { case( ARMOURCLASS_HELMET ): EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[SI_HELMET] ); break; case( ARMOURCLASS_VEST ): EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[SI_VEST] ); break; case( ARMOURCLASS_LEGGINGS ): EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[SI_LEGS] ); break; } } // face gear else if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & IC_FACE && (!si[SI_SIGHT].done || !si[SI_FACE2].done || !si[SI_FACE_SPARESIGHT].done || !si[SI_GASMASK].done) ) { // gasmasks are reserved for a special slot and will only be worn if we do not have 2 face items. items that increase our vision (NVGs adn sungooggles) get to slot 1, everything else in 2 if (ItemIsGasmask(pWorldItem[ uiCount ].object.usItem)) EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[SI_GASMASK] ); else if ( Item[ pWorldItem[ uiCount ].object.usItem ].nightvisionrangebonus > 0 ) { if ( fNightTime ) EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[SI_SIGHT] ); else EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[SI_FACE_SPARESIGHT] ); } else if ( Item[ pWorldItem[ uiCount ].object.usItem ].dayvisionrangebonus > 0 ) { if ( !fNightTime ) EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[SI_SIGHT] ); else EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[SI_FACE_SPARESIGHT] ); } else EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[SI_FACE2] ); } else if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & (IC_BLADE|IC_PUNCH) && !si[SI_MELEE].done ) { EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[SI_MELEE] ); } else if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & IC_GRENADE && !si[SI_GRENADE].done ) { // if this item can be thrown, its a hand grenade, thus we will evaluate it if ( Item[ pWorldItem[ uiCount ].object.usItem ].ubCursor == TOSSCURS ) EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[SI_GRENADE] ); } // ammo else if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & IC_AMMO && fSearchForAmmo ) { UINT16 usItem = pWorldItem[ uiCount ].object.usItem; UINT16 usMagIndex = Item[usItem].ubClassIndex; // we count ammo and then remove it. We will later add it again in the form of ammo crates UINT8 calibre = Magazine[usMagIndex].ubCalibre; UINT8 ammotype = Magazine[usMagIndex].ubAmmoType; UINT32 newammo = 0; for(INT16 i = 0; i < pObj->ubNumberOfObjects; ++i) { newammo += (*pObj)[i]->data.ubShotsLeft; } // add ammo to our map if ( calibrebulletountmap[calibre][ammotype] ) calibrebulletountmap[calibre][ammotype] += newammo; else calibrebulletountmap[calibre][ammotype] = newammo; } // gun else if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & IC_GUN && !si[SI_GUN].done ) { // we count ammo and then remove it. We will later add it again in the form of ammo crates UINT8 calibre = Weapon[ pWorldItem[ uiCount ].object.usItem ].ubCalibre; // guns in a stack can have different ammo, thus he need to add it individually for(INT16 i = 0; i < pObj->ubNumberOfObjects; ++i) { UINT8 ammotype = (*pObj)[i]->data.gun.ubGunAmmoType; if ( (*pObj)[i]->data.gun.ubGunShotsLeft ) { // add ammo to our map if ( calibrebulletountmap[calibre][ammotype] ) calibrebulletountmap[calibre][ammotype] += (*pObj)[i]->data.gun.ubGunShotsLeft; else calibrebulletountmap[calibre][ammotype] = (*pObj)[i]->data.gun.ubGunShotsLeft; } } } // launcher else if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & IC_LAUNCHER && !si[SI_LAUNCHER].done ) { // add launcher type to our launcher map LauncherHelpMap::iterator fnd = launcherhelpmap.find( pWorldItem[ uiCount ].object.usItem ); if ( fnd == launcherhelpmap.end() ) { LauncherHelpStruct tmp; tmp.fNeedsAmmo = !ItemIsSingleShotRocketLauncher(pWorldItem[ uiCount ].object.usItem); launcherhelpmap[ pWorldItem[ uiCount ].object.usItem ] = tmp; } } } } } } ///////////////////////////////// 1st loop ///////////////////////////////////////////////////////// ///////////////////////////////// 1st loop afterwork ///////////////////////////////////////////////////////// // 1st loop afterwork: If we are unable to determine a valid GridNo, soemthing is wrong here, abort // Definite decision on armour/face items/melee items/hand grenades // if the dummy GridNo is still NOWHERE, we did not find a single reachable item. If getting a random gridno also fails, better get out of here if ( dummygridno == NOWHERE ) { dummygridno = RandomGridNo(); if ( dummygridno == NOWHERE ) { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Militia found no items to equip, uses harsh langugage instead!" ); return; } } SearchItemRetrieval( pWorldItem, &si[SI_HELMET], pp ); SearchItemRetrieval( pWorldItem, &si[SI_VEST], pp ); SearchItemRetrieval( pWorldItem, &si[SI_LEGS], pp ); SearchItemRetrieval( pWorldItem, &si[SI_SIGHT], pp ); SearchItemRetrieval( pWorldItem, &si[SI_FACE2], pp ); SearchItemRetrieval( pWorldItem, &si[SI_FACE_SPARESIGHT], pp ); // special: of there is a free face slot and we have a gasmask, equip it there if ( si[SI_GASMASK].found ) { if ( !si[SI_SIGHT].found ) si[SI_GASMASK].soldierslot = si[SI_SIGHT].soldierslot; else if ( !si[SI_FACE2].found ) si[SI_GASMASK].soldierslot = si[SI_FACE2].soldierslot; } SearchItemRetrieval( pWorldItem, &si[SI_GASMASK], pp ); SearchItemRetrieval( pWorldItem, &si[SI_MELEE], pp ); SearchItemRetrieval( pWorldItem, &si[SI_GRENADE], pp, 2 ); // if on a stack, we are willing to take 2 grenades at once ///////////////////////////////// 1st loop afterwork ///////////////////////////////////////////////////////// ///////////////////////////////// 2nd loop ///////////////////////////////////////////////////////// // Second loop: Evaluate all guns, taking ammo into account (we know how much ammo there is, as we wrote that into our map) // We know know what launchers that need ammo we have here, thus we can test bombs and grenades on wether they are such ammo. Note this in the launcher map for( uiCount = 0; uiCount < uiTotalNumberOfRealItems; ++uiCount ) // ... for all items in the world ... { if( pWorldItem[ uiCount ].fExists ) // ... if item exists ... { OBJECTTYPE* pObj = &(pWorldItem[ uiCount ].object); // ... get pointer for this item ... if ( pObj != NULL && pObj->exists() ) // ... if pointer is not obviously useless ... { // this would be the place where we check wether the militia is allowed to pick up an item depending on its soldierclass // test wether item is reachable if ( (pWorldItem[ uiCount ].usFlags & WORLD_ITEM_REACHABLE) && !(pWorldItem[ uiCount ].usFlags & usTabooFlag) ) { // only if we are still looking for a gun if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & IC_GUN && !si[SI_GUN].done ) { UINT8 calibre = Weapon[ pWorldItem[ uiCount ].object.usItem ].ubCalibre; Calibre_BulletCountMap::iterator calibremap_it = calibrebulletountmap.find( calibre ); if ( fSearchForAmmo ) { // only evaluate of ammo is found! if ( calibremap_it != calibrebulletountmap.end() ) { UINT32 bullets = GetTotalCalibreAmmo( &((*calibremap_it).second) ); EvaluateObjForItem_WithAmmo( pWorldItem, pObj, uiCount, &si[SI_GUN], bullets ); } } else { EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[SI_GUN] ); } } else if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & (IC_GRENADE|IC_BOMB) && !si[SI_LAUNCHER].done ) { // if this is a valid launchable for any launcher we found, update their potential ammocount LauncherHelpMap::iterator itend = launcherhelpmap.end(); for (LauncherHelpMap::iterator it = launcherhelpmap.begin(); it != itend; ++it) { if ( (*it).second.fNeedsAmmo && ValidLaunchable( pWorldItem[ uiCount ].object.usItem, (*it).first ) ) (*it).second.ammocount += pWorldItem[ uiCount ].object.ubNumberOfObjects; } } } } } } ///////////////////////////////// 2nd loop ///////////////////////////////////////////////////////// ///////////////////////////////// 2nd loop afterwork ///////////////////////////////////////////////////////// // 2nd loop afterwork: We now know all launchers and their ammo, we decide which launcher to take // Decide on a gun, we can now do that as we know all ammo // If we have selected a gun, but do not take ammo from the inventory, spawn ammo in the traditional way // we investigate the launchers we found, and their ammo. if ( gGameExternalOptions.fMilitiaUseSectorInventory_Launcher && !si[SI_LAUNCHER].done ) { // remember the best ammo count we found UINT16 bestammocount = 0; LauncherHelpMap::iterator itend = launcherhelpmap.end(); for (LauncherHelpMap::iterator it = launcherhelpmap.begin(); it != itend; ++it) { // if a launcher does not need ammo, take that one if ( !(*it).second.fNeedsAmmo ) { usLauncherItem = (*it).first; usLauncherAmmoLeftToTake = 0; break; } // otherwise pick the one with the highest ammo count (but not higher than 3, we don't want to hoard all the ammo) else if ( (*it).second.ammocount > bestammocount ) { usLauncherItem = (*it).first; usLauncherAmmoLeftToTake = (*it).second.ammocount ? 3 : 0; bestammocount = usLauncherAmmoLeftToTake; } } } // now that we know which gun to take, do so // if found a gun, check wether we have enough ammo for it if ( !si[SI_GUN].done ) { if ( si[SI_GUN].found ) { UINT8 calibre = Weapon[ pWorldItem[ si[SI_GUN].pos ].object.usItem ].ubCalibre; Calibre_BulletCountMap::iterator calibremap_it = calibrebulletountmap.find( calibre ); if ( !fSearchForAmmo || calibremap_it != calibrebulletountmap.end() ) { // do we have enough ammo form that calibre? if ( !fSearchForAmmo || EnoughBulletsForGun( pWorldItem[ si[SI_GUN].pos ].object.usItem, &((*calibremap_it).second) ) ) { usSelectedGunBulletsNeeded = fSearchForAmmo ? GetNeededTotalAmmo( pWorldItem[ si[SI_GUN].pos ].object.usItem ) : 0; usSelectedGunBulletCount = fSearchForAmmo ? pWorldItem[ si[SI_GUN].pos ].object[0]->data.gun.ubGunShotsLeft : 0; // take this gun SearchItemRetrieval( pWorldItem, &si[SI_GUN], pp ); //soldier has his gun now, look for attachments for it addAttachementsToMilitiaWeapon(pWorldItem, usTabooFlag, pp); // empty the gun, create a mag if ( !fSearchForAmmo ) { UINT16 bulletstomove = (pp->Inv[ HANDPOS ])[0]->data.gun.ubGunShotsLeft; if ( bulletstomove ) { (pp->Inv[ HANDPOS ])[0]->data.gun.ubGunShotsLeft = 0; UINT8 ammotype = (pp->Inv[ HANDPOS ])[0]->data.gun.ubGunAmmoType; UINT16 magsize = GetMagSize( &(pp->Inv[ HANDPOS ] ) ); UINT16 magitem = FindReplacementMagazine( Weapon[ pp->Inv[ HANDPOS ].usItem ].ubCalibre, magsize, ammotype ); // create ammo in inventory CreateAmmo(magitem, &newAmmoObj, bulletstomove); fNewMagCreated = TRUE; } } } else { usSelectedGunBulletsNeeded = fSearchForAmmo ? GetNeededTotalAmmo( pWorldItem[ si[SI_GUN].pos ].object.usItem ) : 0; usSelectedGunBulletCount = fSearchForAmmo ? pWorldItem[ si[SI_GUN].pos ].object[0]->data.gun.ubGunShotsLeft : 0; // take this gun SearchItemRetrieval( pWorldItem, &si[SI_GUN], pp ); //soldier has his gun now, look for attachments for it addAttachementsToMilitiaWeapon(pWorldItem, usTabooFlag, pp); // empty the gun, create a mag if ( !fSearchForAmmo ) { UINT16 bulletstomove = (pp->Inv[ HANDPOS ])[0]->data.gun.ubGunShotsLeft; if ( bulletstomove ) { (pp->Inv[ HANDPOS ])[0]->data.gun.ubGunShotsLeft = 0; UINT8 ammotype = (pp->Inv[ HANDPOS ])[0]->data.gun.ubGunAmmoType; UINT16 magsize = GetMagSize( &(pp->Inv[ HANDPOS ] ) ); UINT16 magitem = FindReplacementMagazine( Weapon[ pp->Inv[ HANDPOS ].usItem ].ubCalibre, magsize, ammotype ); // create ammo in inventory CreateAmmo(magitem, &newAmmoObj, bulletstomove); fNewMagCreated = TRUE; } } } } // no ammo at all... this is bad else { si[SI_GUN].done = TRUE; ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Militia found no gun to equip, uses harsh langugage instead!" ); } } // we didn't find any gun at all. Now what? else { si[SI_GUN].done = TRUE; ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Militia found no gun to equip, uses harsh langugage instead!" ); } } // if we selected a gun but do not select ammo, we have to spawn fitting ammo the traditional way if ( gGameExternalOptions.fMilitiaUseSectorInventory_Gun && !gGameExternalOptions.fMilitiaUseSectorInventory_Ammo) { UINT16 usGunIndex = pp->Inv[ HANDPOS ].usItem; if ( usGunIndex ) { // check default ammo first... UINT16 usAmmoIndex = DefaultMagazine( usGunIndex ); UINT8 ubChanceStandardAmmo = 100; if ( AmmoTypes[Magazine[ Item[ usAmmoIndex ].ubClassIndex ].ubAmmoType].standardIssue ) ubChanceStandardAmmo = 80; else ubChanceStandardAmmo = 100 - (Random(9) * 9); usAmmoIndex = RandomMagazine( &pp->Inv[HANDPOS], ubChanceStandardAmmo, max(Item[usGunIndex].ubCoolness, HighestPlayerProgressPercentage() / 10 + 3 ), pp->ubSoldierClass); if ( usAmmoIndex <= 0 ) usAmmoIndex = DefaultMagazine(usGunIndex); //Madd: ensure a minimum # of bullets to make sure enemies don't run out and run away UINT8 numberofmags = 3 + Random(2); while ( ( (numberofmags - 1) * Weapon[usGunIndex].ubMagSize ) < 20 ) // each soldier should have at least 20 bullets, ie: 2 9mm 15rd clips, 3 7rd shotgun shells, 4 6rd speedloaders, etc ++numberofmags; CreateItems( usAmmoIndex, 100, numberofmags, &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); } } ///////////////////////////////// 2nd loop afterwork ///////////////////////////////////////////////////////// ///////////////////////////////// 3rd loop ///////////////////////////////////////////////////////// // Third loop: If we ever decide to pick up a gun, we will have done so now. We will look for fitting ammo in all ammo/guns, spawn correct magazines in our inventory, and remove ammo from found objects // As we have decided on which launcher we want, we evaluate all launchers of this type and take if afterwards. We also move enough ammo into our inventory. // If we created a new mag from the gun we took, try to add this to an empty invetory slot, if none is found, enlarge the inventory afterwards // 3rd loop: take ammo and grenade launcher grenades for( uiCount = 0; uiCount < uiTotalNumberOfRealItems; ++uiCount ) // ... for all items in the world ... { if( pWorldItem[ uiCount ].fExists ) // ... if item exists ... { OBJECTTYPE* pObj = &(pWorldItem[ uiCount ].object); // ... get pointer for this item ... if ( pObj != NULL && pObj->exists() ) // ... if pointer is not obviously useless ... { // test wether item is reachable if ( (pWorldItem[ uiCount ].usFlags & WORLD_ITEM_REACHABLE) && !(pWorldItem[ uiCount ].usFlags & usTabooFlag) ) { // look for fitting ammo if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & IC_GUN && fSearchForAmmo && usSelectedGunBulletCount < usSelectedGunBulletsNeeded ) { if ( Weapon[ pWorldItem[ uiCount ].object.usItem ].ubCalibre == Weapon[ pp->Inv[ HANDPOS ].usItem ].ubCalibre ) { // guns in a stack can have different ammo, thus we need to add it individually for(INT16 i = 0; i < pObj->ubNumberOfObjects; ++i) { UINT8 ammotype = (*pObj)[i]->data.gun.ubGunAmmoType; UINT32 addammo = min(usSelectedGunBulletsNeeded - usSelectedGunBulletCount, (*pObj)[i]->data.gun.ubGunShotsLeft); SpawnFittingAmmo( pp, &(pp->Inv[ HANDPOS ]), ammotype, addammo ); (*pObj)[i]->data.gun.ubGunShotsLeft -= addammo; usSelectedGunBulletCount += addammo; } } } else if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & IC_AMMO && fSearchForAmmo && usSelectedGunBulletCount < usSelectedGunBulletsNeeded ) { UINT16 usItem = pWorldItem[ uiCount ].object.usItem; UINT16 usMagIndex = Item[usItem].ubClassIndex; UINT8 calibre = Magazine[usMagIndex].ubCalibre; // if caliber fits, add ammo to inventory, remove bullets and update bullet counter if ( calibre == Weapon[ pp->Inv[ HANDPOS ].usItem ].ubCalibre ) { UINT8 ammotype = Magazine[usMagIndex].ubAmmoType; UINT32 newammo = 0; for(INT16 i = 0; i < pObj->ubNumberOfObjects; ++i) newammo += (*pObj)[i]->data.ubShotsLeft; UINT32 addammo = min((UINT32)(usSelectedGunBulletsNeeded - usSelectedGunBulletCount), newammo);//dnl ch75 271013 SpawnFittingAmmo( pp, &(pp->Inv[ HANDPOS ]), ammotype, addammo ); usSelectedGunBulletCount += addammo; // remove ammo from stack for(INT16 i = 0; i < pObj->ubNumberOfObjects; ++i) { if ( !addammo ) break; UINT32 takeammo = min(addammo, (*pObj)[i]->data.ubShotsLeft); addammo -= takeammo; (*pObj)[i]->data.ubShotsLeft -= takeammo; if ( !(*pObj)[i]->data.ubShotsLeft ) { pWorldItem[uiCount].object.RemoveObjectAtIndex( i ); // we removed an item from the stack - set back the loop variable so we don't miss an object --i; } } if ( pWorldItem[ uiCount ].object.ubNumberOfObjects < 1 ) { if ( pWorldItem[ uiCount ].sGridNo != NOWHERE ) RemoveItemFromPool(pWorldItem[ uiCount ].sGridNo, uiCount, pWorldItem[ uiCount ].ubLevel); pWorldItem[uiCount].fExists = FALSE; } } } else if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & IC_LAUNCHER && !si[SI_LAUNCHER].done ) { // if launcher is of the type we want, evaluate it if ( pWorldItem[ uiCount ].object.usItem == usLauncherItem ) { EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[SI_LAUNCHER] ); } } else if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & (IC_GRENADE|IC_BOMB) && !si[SI_LAUNCHER].done && usLauncherAmmoLeftToTake ) { // if launcher is of the type we want, evaluate it if ( ValidLaunchable(pWorldItem[ uiCount ].object.usItem, usLauncherItem) ) { // take item and reduce amount left to take UINT8 totake = min(usLauncherAmmoLeftToTake, pWorldItem[ uiCount ].object.ubNumberOfObjects); usLauncherAmmoLeftToTake = max(0, usLauncherAmmoLeftToTake - totake); pWorldItem[ uiCount ].object.MoveThisObjectTo(gTempObject, totake); gTempObject[0]->data.sObjectFlag |= TAKEN_BY_MILITIA; PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); if ( pWorldItem[ uiCount ].object.ubNumberOfObjects < 1 ) { if ( pWorldItem[ uiCount ].sGridNo != NOWHERE ) RemoveItemFromPool(pWorldItem[ uiCount ].sGridNo, uiCount, pWorldItem[ uiCount ].ubLevel); pWorldItem[uiCount].fExists = FALSE; } } } } } // found an empty slot - we can put the magazine we created here else if ( fNewMagCreated ) { pWorldItem[uiCount].fExists = TRUE; pWorldItem[uiCount].sGridNo = dummygridno; pWorldItem[uiCount].ubLevel = 0; pWorldItem[uiCount].usFlags = WORLD_ITEM_REACHABLE; pWorldItem[uiCount].bVisible = 1; pWorldItem[uiCount].bRenderZHeightAboveLevel = 0; pWorldItem[uiCount].soldierID = -1; pWorldItem[uiCount].object = newAmmoObj; fNewMagCreated = FALSE; } } } ///////////////////////////////// 3rd loop ///////////////////////////////////////////////////////// ///////////////////////////////// 3rd loop afterwork ///////////////////////////////////////////////////////// // 3rd loop afterwork: Take launcher // Add mag if created adn it couldn't be added to empty slot earlier by enlarging the inventory SearchItemRetrieval( pWorldItem, &si[SI_LAUNCHER], pp ); // it can happen that we create a new mag item (that happens if we search for guns but not ammo, and the gun we selected has ammo) // if we didn't find an empty slot earlier, we still have to add the item, so we increase the sector inventory size by 1 to make room if ( fNewMagCreated ) { // new total size needed is number of existing items found + number of needed ammo crate objects, but at least uiTotalNumberOfRealItems: UINT32 uiNewInvSize = uiTotalNumberOfRealItems + 1; // create a bigger inventory wit big enough size std::vector pWorldItem_tmp(uiNewInvSize);//dnl ch75 271013 // copy over old inventory for( uiCount = 0; uiCount < uiTotalNumberOfRealItems; ++uiCount ) { pWorldItem_tmp[uiCount] = pWorldItem[uiCount]; } pWorldItem_tmp[uiCount].fExists = TRUE; pWorldItem_tmp[uiCount].sGridNo = dummygridno; pWorldItem_tmp[uiCount].ubLevel = 0; pWorldItem_tmp[uiCount].usFlags = WORLD_ITEM_REACHABLE; pWorldItem_tmp[uiCount].bVisible = 1; pWorldItem_tmp[uiCount].bRenderZHeightAboveLevel = 0; pWorldItem_tmp[uiCount].soldierID = -1; pWorldItem_tmp[uiCount].object = newAmmoObj; fNewMagCreated = FALSE; // use the new map uiTotalNumberOfRealItems = uiNewInvSize; pWorldItem = pWorldItem_tmp; } ///////////////////////////////// 3rd loop afterwork ///////////////////////////////////////////////////////// ///////////////////////////////// Exit ///////////////////////////////////////////////////////// // Exit: Save changed inventory #ifdef JA2TESTVERSION std::vector > overviewvector2; for( uiCount = 0; uiCount < uiTotalNumberOfRealItems; ++uiCount ) { overviewvector2.push_back( std::pair(pWorldItem[ uiCount ].fExists, pWorldItem[ uiCount ].object.usItem) ); } #endif // save the changed inventory // open sector inv if( ( sMapX == gWorldSectorX )&&( gWorldSectorY == sMapY ) && (gbWorldSectorZ == sMapZ ) ) { guiNumWorldItems = uiTotalNumberOfRealItems; gWorldItems = pWorldItem; } else { //Save the Items to the the file UpdateWorldItems(sMapX, sMapY, (INT8)sMapZ, uiTotalNumberOfRealItems, pWorldItem); } ///////////////////////////////// Exit ///////////////////////////////////////////////////////// } /** Goes through items in pWorldItem and looks for scopes, foregrips, lasers, etc. and tries to attach them to thing in soldiers (pp) hand. Hopefuly, it is a weapon. If thing is successfuly attached, it is removed from world. Attachement has to be reachable and not a taboo for militia. Does not attempt to improve current attachments, first item to fit in a position will remain there. */ void addAttachementsToMilitiaWeapon(std::vector& pWorldItem, UINT16 usTabooFlag, SOLDIERCREATE_STRUCT *pp) { if ( !gGameExternalOptions.fMilitiaUseSectorInventory_Gun || !gGameExternalOptions.fMilitiaUseSectorInventory_GunAttachments ) return; OBJECTTYPE* pGun = &pp->Inv[HANDPOS]; //we are looking to attach to thing in hand, should be gun if ( !pGun || !pGun->exists( ) ) return; // search for the following types of attachments: (AC_BIPOD | AC_MUZZLE | AC_SCOPE | AC_FOREGRIP | AC_STOCK | AC_SIGHT | AC_LASER | AC_SLING) // we loop over all items, evaluate what attachments we find, and later on stick them on the gun ItemSearchStruct si[8]; // each attachment type we search for gets a separate entry for (UINT32 uiCount = 0; uiCount < pWorldItem.size(); ++uiCount) // ... for all items in the world ... { if ((pWorldItem)[uiCount].fExists) // ... if item exists ... { OBJECTTYPE* pObj = &((pWorldItem)[uiCount].object); // ... get pointer for this item ... if (pObj != NULL && pObj->exists()) // ... if pointer is not obviously useless ... { // this would be the place where we check wether the militia is allowed to pick up an item depending on its soldierclass // test wether item is reachable and its not taboo if (((pWorldItem)[uiCount].usFlags & WORLD_ITEM_REACHABLE) && !((pWorldItem)[uiCount].usFlags & usTabooFlag)) { UINT32 usItemClass = Item[(pWorldItem)[uiCount].object.usItem].usItemClass; UINT32 attachmentClass = Item[(pWorldItem)[uiCount].object.usItem].attachmentclass; // check if this item is something we might want to attach if ( usItemClass & IC_MISC && attachmentClass & (AC_BIPOD | AC_MUZZLE | AC_SCOPE | AC_FOREGRIP | AC_STOCK | AC_SIGHT | AC_LASER | AC_SLING) ) { if ( ValidItemAttachmentSlot( pGun, pObj->usItem, TRUE, FALSE ) ) { if ( attachmentClass & AC_SCOPE ) EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[0] ); else if ( attachmentClass & AC_FOREGRIP ) EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[1] ); else if ( attachmentClass & AC_BIPOD ) EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[2] ); else if ( attachmentClass & AC_LASER ) EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[3] ); else if ( attachmentClass & AC_SIGHT ) EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[4] ); else if ( attachmentClass & AC_MUZZLE ) EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[5] ); else if ( attachmentClass & AC_STOCK ) EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[6] ); else if ( attachmentClass & AC_SLING ) EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[7] ); } } } } } } // we've evaluated all possible attachments - now we add what we've found for ( int i = 0; i < 8; ++i ) { if ( si[i].found && !si[i].done ) { OBJECTTYPE* pObj = &((pWorldItem)[si[i].pos].object); // ... get pointer for this item ... if ( pObj != NULL && pObj->exists() ) { BOOLEAN isAttachedNow = pGun->AttachObject( NULL, pObj, FALSE ); //do the actual attaching if ( isAttachedNow ) { //might be false, if attachement was not valid for the gun (like small scope to M-960A) RemoveStackIfEmpty( pWorldItem, si[i].pos ); //chceck uiCount stack, might be empty now and should be removed //SetTakenByMilitiaFlagsWhenForRetrievedItem(pWorldItem,... //Nav:I realized, this is not required for attachement, hopefuly correctly (because weapon got proper flags already) } } } } } ////////////////// Flugente: militia equipment feature ///////////////////////////////////