#ifdef PRECOMPILEDHEADERS #include "Strategic All.h" #include "GameSettings.h" #else #include "Assignments.h" #include "Strategic.h" #include "Items.h" #include "Overhead.h" #include "Game Clock.h" #include "stdlib.h" #include "Message.h" #include "Font Control.h" #include "Map Screen Interface.h" #include "Soldier Profile Type.h" #include "Soldier Profile.h" #include "Campaign.h" #include "Queen Command.h" #include "strategicmap.h" #include "Text.h" #include "dialogue control.h" #include "NPC.h" #include "Strategic Town Loyalty.h" #include "animation control.h" #include "mapscreen.h" #include "Squads.h" #include "Map Screen Helicopter.h" #include "PopUpBox.h" #include "Vehicles.h" #include "Strategic Merc Handler.h" #include "Merc Contract.h" #include "Map Screen Interface Map.h" #include "Strategic Movement.h" #include "laptop.h" #include "Finances.h" #include "LaptopSave.h" #include "renderworld.h" #include "Interface Control.h" #include "Interface.h" #include "Soldier Find.h" #include "ai.h" #include "utilities.h" #include "random.h" #include "line.h" #include "Soldier Add.h" #include "GameSettings.h" #include "Isometric Utils.h" #include "Soldier Macros.h" #include "Explosion Control.h" #include "SkillCheck.h" #include "Quests.h" #include "Town Militia.h" #include "Map Screen Interface Border.h" #include "math.h" #include "SkillCheck.h" #include "Strategic Pathing.h" #include "Game Event Hook.h" #include "Strategic Event Handler.h" #include "Map Information.h" #include "Strategic Status.h" #include "history.h" #include "Map Screen Interface Map Inventory.h" #include "interface dialogue.h" #endif #include //forward declarations of common classes to eliminate includes class OBJECTTYPE; class SOLDIERTYPE; #include "MilitiaSquads.h" // various reason an assignment can be aborted before completion enum{ NO_MORE_MED_KITS = 40, INSUF_DOCTOR_SKILL, NO_MORE_TOOL_KITS, INSUF_REPAIR_SKILL, NUM_ASSIGN_ABORT_REASONS }; enum{ REPAIR_MENU_VEHICLE1 = 0, REPAIR_MENU_VEHICLE2, REPAIR_MENU_VEHICLE3, // REPAIR_MENU_SAM_SITE, REPAIR_MENU_ROBOT, REPAIR_MENU_ITEMS, REPAIR_MENU_CANCEL, }; enum{ VEHICLE_MENU_VEHICLE1 = 0, VEHICLE_MENU_VEHICLE2, VEHICLE_MENU_VEHICLE3, VEHICLE_MENU_CANCEL, }; /* CHRISL: Adjusted enumerations to allow for seperation of the three different pocket types in the new inventory system. */ enum { REPAIR_HANDS_AND_ARMOR = 0, REPAIR_HEADGEAR, REPAIR_BIG_POCKETS, REPAIR_MED_POCKETS, REPAIR_SML_POCKETS, NUM_REPAIR_PASS_TYPES, }; #define FINAL_REPAIR_PASS REPAIR_SML_POCKETS /* CHRISL: bSlot[xx] array declaration needs to reflect largest number of inventory locations. New inventory system increses possible locations from 12 to 30. Also added a new field so we can set number of choices based on game options. */ typedef struct REPAIR_PASS_SLOTS_TYPE { UINT8 ubChoices[2]; // how many valid choices there are in this pass INT8 bSlot[ 30 ]; // list of slots to be repaired in this pass } REPAIR_PASS_SLOTS_TYPE; /* CHRISL: Added new definitions introduced by the new inventory system.*/ REPAIR_PASS_SLOTS_TYPE gRepairPassSlotList[ NUM_REPAIR_PASS_TYPES ] = { // pass # choices # new choices slots repaired in this pass { /* hands and armor */ 5, 12, HANDPOS, SECONDHANDPOS, VESTPOS, HELMETPOS, LEGPOS, VESTPOCKPOS, LTHIGHPOCKPOS, RTHIGHPOCKPOS, CPACKPOCKPOS, BPACKPOCKPOS, GUNSLINGPOCKPOS, KNIFEPOCKPOS, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { /* headgear */ 2, 2, HEAD1POS, HEAD2POS, -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 }, { /* big pockets */ 4, 7, BIGPOCK1POS, BIGPOCK2POS, BIGPOCK3POS, BIGPOCK4POS, BIGPOCK5POS, BIGPOCK6POS, BIGPOCK7POS, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { /* med pockets */ 0, 4, MEDPOCK1POS, MEDPOCK2POS, MEDPOCK3POS, MEDPOCK4POS, -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 }, { /* sml pockets */ 8, 30, SMALLPOCK1POS, SMALLPOCK2POS, SMALLPOCK3POS, SMALLPOCK4POS, SMALLPOCK5POS, SMALLPOCK6POS, SMALLPOCK7POS, SMALLPOCK8POS, SMALLPOCK9POS, SMALLPOCK10POS, SMALLPOCK11POS, SMALLPOCK12POS, SMALLPOCK13POS, SMALLPOCK14POS, SMALLPOCK15POS, SMALLPOCK16POS, SMALLPOCK17POS, SMALLPOCK18POS, SMALLPOCK19POS, SMALLPOCK20POS, SMALLPOCK21POS, SMALLPOCK22POS, SMALLPOCK23POS, SMALLPOCK24POS, SMALLPOCK25POS, SMALLPOCK26POS, SMALLPOCK27POS, SMALLPOCK28POS, SMALLPOCK29POS, SMALLPOCK30POS }, }; extern STR16 sRepairsDoneString[]; // PopUp Box Handles INT32 ghAssignmentBox = -1; INT32 ghEpcBox = -1; INT32 ghSquadBox = -1; INT32 ghVehicleBox = -1; INT32 ghRepairBox = -1; INT32 ghTrainingBox = -1; INT32 ghAttributeBox = -1; INT32 ghRemoveMercAssignBox = -1; INT32 ghContractBox = -1; INT32 ghMoveBox = -1; //INT32 ghUpdateBox = -1; // the x,y position of assignment pop up in tactical INT16 gsAssignmentBoxesX, gsAssignmentBoxesY; // assignment menu mouse regions MOUSE_REGION gAssignmentMenuRegion[ MAX_ASSIGN_STRING_COUNT ]; MOUSE_REGION gTrainingMenuRegion[ MAX_TRAIN_STRING_COUNT ]; MOUSE_REGION gAttributeMenuRegion[ MAX_ATTRIBUTE_STRING_COUNT ]; MOUSE_REGION gSquadMenuRegion[ MAX_SQUAD_MENU_STRING_COUNT ]; MOUSE_REGION gContractMenuRegion[ MAX_CONTRACT_MENU_STRING_COUNT ]; MOUSE_REGION gRemoveMercAssignRegion[ MAX_REMOVE_MERC_COUNT ]; MOUSE_REGION gEpcMenuRegion[ MAX_EPC_MENU_STRING_COUNT ]; MOUSE_REGION gRepairMenuRegion[ 20 ]; // mouse region for vehicle menu MOUSE_REGION gVehicleMenuRegion[ 20 ]; MOUSE_REGION gAssignmentScreenMaskRegion; BOOLEAN fShownAssignmentMenu = FALSE; BOOLEAN fShowVehicleMenu = FALSE; BOOLEAN fShowRepairMenu = FALSE; BOOLEAN fShownContractMenu = FALSE; BOOLEAN fFirstClickInAssignmentScreenMask = FALSE; // render pre battle interface? extern BOOLEAN gfRenderPBInterface; extern BOOLEAN fMapScreenBottomDirty; extern SOLDIERTYPE *pMilitiaTrainerSoldier; // in the mapscreen? extern BOOLEAN fInMapMode; // we are in fact training?..then who temmates, or self? INT8 gbTrainingMode = -1; // who is the highlighted guy extern UINT16 gusUIFullTargetID; // showing town info? extern BOOLEAN fShowTownInfo; extern INT32 giMapBorderButtons[]; extern BOOLEAN fProcessingAMerc; extern SOLDIERTYPE *pProcessingSoldier ; BOOLEAN gfAddDisplayBoxToWaitingQueue = FALSE; // redraw character list extern BOOLEAN fDrawCharacterList; extern BOOLEAN fSelectedListOfMercsForMapScreen[ CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ]; SOLDIERTYPE *gpDismissSoldier = NULL; BOOLEAN gfReEvaluateEveryonesNothingToDo = FALSE; /* // the amount time must be on assignment before it can have any effect #define MINUTES_FOR_ASSIGNMENT_TO_COUNT 45 // number we divide the total pts accumlated per day by for each assignment period #define ASSIGNMENT_UNITS_PER_DAY 24 // base skill to deal with an emergency #define BASE_MEDICAL_SKILL_TO_DEAL_WITH_EMERGENCY 20 // multiplier for skill needed for each point below OKLIFE #define MULTIPLIER_FOR_DIFFERENCE_IN_LIFE_VALUE_FOR_EMERGENCY 4 // number of pts needed for each point below OKLIFE #define POINT_COST_PER_HEALTH_BELOW_OKLIFE 2 // how many points of healing each hospital patients gains per hour in the hospital #define HOSPITAL_HEALING_RATE 5 // a top merc doctor can heal about 4 pts/hour maximum, but that's spread among patients! // increase to reduce repair pts, or vice versa #define REPAIR_RATE_DIVISOR 2500 // increase to reduce doctoring pts, or vice versa #define DOCTORING_RATE_DIVISOR 2400 // at 2400, the theoretical maximum is 150 full healing pts/day // cost to unjam a weapon in repair pts #define REPAIR_COST_PER_JAM 2 // divisor for rate of self-training #define SELF_TRAINING_DIVISOR 1000 // the divisor for rate of training bonus due to instructors influence #define INSTRUCTED_TRAINING_DIVISOR 3000 // this controls how fast town militia gets trained #define TOWN_TRAINING_RATE 4 #define MAX_MILITIA_TRAINERS_PER_SECTOR 2 // militia training bonus for EACH level of teaching skill (percentage points) #define TEACH_BONUS_TO_TRAIN 30 // militia training bonus for RPC (percentage points) #define RPC_BONUS_TO_TRAIN 10 // the bonus to training in marksmanship in the Alma gun range sector #define GUN_RANGE_TRAINING_BONUS 25 // breath bonus divider #define BREATH_BONUS_DIVIDER 10 // the min rating that is need to teach a fellow teammate #define MIN_RATING_TO_TEACH 25 // activity levels for natural healing ( the higher the number, the slower the natural recover rate #define LOW_ACTIVITY_LEVEL 1 #define MEDIUM_ACTIVITY_LEVEL 4 #define HIGH_ACTIVITY_LEVEL 12 */ /* // the min breath to stay awake #define MIN_BREATH_TO_STAY_AWAKE 15 // average number of hours a merc needs to sleep per day #define AVG_NUMBER_OF_HOURS_OF_SLEEP_NEEDED 7 */ /* Assignment distance limits removed. Sep/11/98. ARM #define MAX_DISTANCE_FOR_DOCTORING 5 #define MAX_DISTANCE_FOR_REPAIR 5 #define MAX_DISTANCE_FOR_TRAINING 5 */ /* // controls how easily SAM sites are repaired // NOTE: A repairman must generate a least this many points / hour to be ABLE to repair a SAM site at all! #define SAM_SITE_REPAIR_DIVISOR 10 // minimum condition a SAM site must be in to be fixable #define MIN_CONDITION_TO_FIX_SAM 20 */ // how many points worth of tool kits does the character have? UINT16 ToolKitPoints(SOLDIERTYPE *pSoldier); // how many points worth of doctoring does the character have in his medical kits ? UINT16 TotalMedicalKitPoints(SOLDIERTYPE *pSoldier); // handle doctor in this sector void HandleDoctorsInSector( INT16 sX, INT16 sY, INT8 bZ ); // handle any repair man in sector void HandleRepairmenInSector( INT16 sX, INT16 sY, INT8 bZ ); // heal characters in this sector with this doctor void HealCharacters( SOLDIERTYPE *pDoctor, INT16 sX, INT16 sY, INT8 bZ ); // update characters who might done healing but are still patients void UpdatePatientsWhoAreDoneHealing( void ); // returns minimum medical skill necessary to treat this patient UINT8 GetMinHealingSkillNeeded( SOLDIERTYPE *pPatient ); // heal patient, given doctor and total healing pts available to doctor at this time UINT16 HealPatient( SOLDIERTYPE *pPatient, SOLDIERTYPE * pDoctor, UINT16 usHundredthsHealed ); // can item be repaired? BOOLEAN IsItemRepairable( UINT16 usItem, INT16 bStatus ); // does another merc have a repairable item on them? OBJECTTYPE* FindRepairableItemOnOtherSoldier( SOLDIERTYPE * pSoldier, UINT8 ubPassType ); //CHRISL: This function will handle the actual searching for repairable items OBJECTTYPE* FindRepairableItemInSpecificPocket( OBJECTTYPE * pObj, UINT8 subObject); //CHRISL: This function will search through LBENODE items for repairable items OBJECTTYPE* FindRepairableItemInLBENODE( OBJECTTYPE * pObj, UINT8 subObject); // repair stuff void HandleRepairBySoldier( SOLDIERTYPE *pSoldier ); // rest the character void RestCharacter( SOLDIERTYPE *pSoldier ); // fatigue the character void FatigueCharacter( SOLDIERTYPE *pSoldier ); // a list of which sectors have characters BOOLEAN fSectorsWithSoldiers[ MAP_WORLD_X * MAP_WORLD_Y ][ 4 ]; // can soldier repair robot BOOLEAN CanCharacterRepairRobot( SOLDIERTYPE *pSoldier ); // can the character repair this vehicle? BOOLEAN CanCharacterRepairVehicle( SOLDIERTYPE *pSoldier, INT32 iVehicleId ); // handle training of character in sector void HandleTrainingInSector( INT16 sMapX, INT16 sMapY, INT8 bZ ); // QSort compare function for town training int TownTrainerQsortCompare(const void *pArg1, const void *pArg2); // this function will actually pass on the pts to the mercs stat void TrainSoldierWithPts( SOLDIERTYPE *pSoldier, INT16 sTrainPts ); // train militia in this sector with this soldier BOOLEAN TrainTownInSector( SOLDIERTYPE *pTrainer, INT16 sMapX, INT16 sMapY, INT16 sTrainingPts ); // is the character between secotrs in mvt BOOLEAN CharacterIsBetweenSectors( SOLDIERTYPE *pSoldier ); // update soldier life void UpDateSoldierLife( SOLDIERTYPE *pSoldier ); // handle natural healing for all mercs on players team void HandleNaturalHealing( void ); // handle natural healing for any individual grunt void HandleHealingByNaturalCauses( SOLDIERTYPE *pSoldier ); /* // auto sleep mercs BOOLEAN AutoSleepMerc( SOLDIERTYPE *pSoldier ); */ // assignment screen mask void AssignmentScreenMaskBtnCallback(MOUSE_REGION * pRegion, INT32 iReason ); // glow area for contract region? BOOLEAN fGlowContractRegion = FALSE; void HandleShadingOfLinesForSquadMenu( void ); void HandleShadingOfLinesForVehicleMenu( void ); void HandleShadingOfLinesForRepairMenu( void ); void HandleShadingOfLinesForTrainingMenu( void ); void HandleShadingOfLinesForAttributeMenus( void ); // post message about contract void PostContractMessage( SOLDIERTYPE *pCharacter, INT32 iContract ); // post a terminate message void PostTerminateMessage( SOLDIERTYPE *pCharacter ); BOOLEAN DisplayVehicleMenu( SOLDIERTYPE *pSoldier ); BOOLEAN DisplayRepairMenu( SOLDIERTYPE *pSoldier ); // create menus void CreateEPCBox( void ); void CreateSquadBox( void ); void CreateVehicleBox(); void CreateRepairBox( void ); /* // get how fast the person regains sleep INT8 GetRegainDueToSleepNeeded( SOLDIERTYPE *pSoldier, INT32 iRateOfReGain ); */ void PositionCursorForTacticalAssignmentBox( void ); // can this soldier be healed by this doctor? BOOLEAN CanSoldierBeHealedByDoctor( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pDoctor, BOOLEAN fIgnoreAssignment, BOOLEAN fThisHour, BOOLEAN fSkipKitCheck, BOOLEAN fSkipSkillCheck ); UINT8 GetNumberThatCanBeDoctored( SOLDIERTYPE *pDoctor, BOOLEAN fThisHour, BOOLEAN fSkipKitCheck, BOOLEAN fSkipSkillCheck ); void CheckForAndHandleHospitalPatients( void ); void HealHospitalPatient( SOLDIERTYPE *pPatient, UINT16 usHealingPtsLeft ); void MakeSureToolKitIsInHand( SOLDIERTYPE *pSoldier ); BOOLEAN MakeSureMedKitIsInHand( SOLDIERTYPE *pSoldier ); void RepositionMouseRegions( void ); void CheckAndUpdateTacticalAssignmentPopUpPositions( void ); void HandleRestFatigueAndSleepStatus( void ); BOOLEAN CharacterIsTakingItEasy( SOLDIERTYPE *pSoldier ); void RepairMenuBtnCallback( MOUSE_REGION * pRegion, INT32 iReason ); BOOLEAN CanCharacterDoctorButDoesntHaveMedKit( SOLDIERTYPE *pSoldier ); BOOLEAN CanCharacterRepairButDoesntHaveARepairkit( SOLDIERTYPE *pSoldier ); // robot replated stuff BOOLEAN IsRobotInThisSector( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ ); SOLDIERTYPE * GetRobotSoldier( void ); UINT8 RepairRobot( SOLDIERTYPE *pRobot, UINT8 ubRepairPts, BOOLEAN *pfNothingLeftToRepair ); UINT8 HandleRepairOfRobotBySoldier( SOLDIERTYPE *pSoldier, UINT8 ubRepairPts, BOOLEAN *pfNothingLeftToRepair ); BOOLEAN HandleAssignmentExpansionAndHighLightForAssignMenu( SOLDIERTYPE *pSoldier ); BOOLEAN HandleAssignmentExpansionAndHighLightForTrainingMenu( void ); BOOLEAN HandleShowingOfMovementBox( void ); //BOOLEAN HandleShowingOfUpBox( void ); void ReportTrainersTraineesWithoutPartners( void ); BOOLEAN ValidTrainingPartnerInSameSectorOnAssignmentFound( SOLDIERTYPE *pSoldier, INT8 bTargetAssignment, INT8 bTargetStat ); extern void AddSectorForSoldierToListOfSectorsThatCompletedMilitiaTraining( SOLDIERTYPE *pSoldier ); extern BOOLEAN SectorIsImpassable( INT16 sSector ); extern BOOLEAN CanChangeSleepStatusForCharSlot( INT8 bCharNumber ); extern UINT32 VirtualSoldierDressWound( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pVictim, OBJECTTYPE *pKit, INT16 sKitPts, INT16 sStatus ); // only 2 trainers are allowed per sector, so this function counts the # in a guy's sector INT8 CountMilitiaTrainersInSoldiersSector( SOLDIERTYPE * pSoldier ); // notify player of assignment attempt failure void NotifyPlayerOfAssignmentAttemptFailure( INT8 bAssignment ); BOOLEAN PlayerSoldierTooTiredToTravel( SOLDIERTYPE *pSoldier ); void AssignmentAborted( SOLDIERTYPE *pSoldier, UINT8 ubReason ); UINT8 CalcSoldierNeedForSleep( SOLDIERTYPE *pSoldier ); UINT32 GetLastSquadListedInSquadMenu( void ); BOOLEAN IsAnythingAroundForSoldierToRepair( SOLDIERTYPE * pSoldier ); BOOLEAN HasCharacterFinishedRepairing( SOLDIERTYPE * pSoldier ); BOOLEAN DoesCharacterHaveAnyItemsToRepair( SOLDIERTYPE * pSoldier, INT8 bHighestPass ); BOOLEAN CanCharacterRepairAnotherSoldiersStuff( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pOtherSoldier ); // can this character EVER train militia? BOOLEAN BasicCanCharacterTrainMilitia( SOLDIERTYPE *pCharacter ); SOLDIERTYPE *GetSelectedAssignSoldier( BOOLEAN fNullOK ); BOOLEAN RepairObject( SOLDIERTYPE * pSoldier, SOLDIERTYPE * pOwner, OBJECTTYPE * pObj, UINT8 * pubRepairPtsLeft ); void RepairItemsOnOthers( SOLDIERTYPE *pSoldier, UINT8 *pubRepairPtsLeft ); BOOLEAN UnjamGunsOnSoldier( SOLDIERTYPE *pOwnerSoldier, SOLDIERTYPE *pRepairSoldier, UINT8 *pubRepairPtsLeft ); /* No point in allowing SAM site repair any more. Jan/13/99. ARM BOOLEAN IsTheSAMSiteInSectorRepairable( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ ); BOOLEAN SoldierInSameSectorAsSAM( SOLDIERTYPE *pSoldier ); BOOLEAN CanSoldierRepairSAM( SOLDIERTYPE *pSoldier, INT8 bRepairPoints ); BOOLEAN IsSoldierCloseEnoughToSAMControlPanel( SOLDIERTYPE *pSoldier ); */ /* Assignment distance limits removed. Sep/11/98. ARM BOOLEAN IsSoldierCloseEnoughToADoctor( SOLDIERTYPE *pPatient ); */ #ifdef JA2BETAVERSION void VerifyTownTrainingIsPaidFor( void ); #endif void InitSectorsWithSoldiersList( void ) { // init list of sectors memset( &fSectorsWithSoldiers, 0, sizeof( fSectorsWithSoldiers ) ); return; } void BuildSectorsWithSoldiersList( void ) { SOLDIERTYPE *pSoldier, *pTeamSoldier; INT32 cnt=0; pSoldier = MercPtrs[ 0 ]; // fills array with pressence of player controlled characters for ( pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ pSoldier->bTeam ].bLastID; cnt++,pTeamSoldier++) { if(pTeamSoldier->bActive) { fSectorsWithSoldiers[ pTeamSoldier->sSectorX + pTeamSoldier->sSectorY * MAP_WORLD_X ][ pTeamSoldier->bSectorZ ] = TRUE; } } } void ChangeSoldiersAssignment( SOLDIERTYPE *pSoldier, INT8 bAssignment ) { // This is the most basic assignment-setting function. It must be called before setting any subsidiary // values like fFixingRobot. It will clear all subsidiary values so we don't leave the merc in a messed // up state! AssertNotNIL(pSoldier); pSoldier->bAssignment = bAssignment; /// don't kill iVehicleId, though, 'cause militia training tries to put guys back in their vehicles when it's done(!) pSoldier->flags.fFixingSAMSite = FALSE; pSoldier->flags.fFixingRobot = FALSE; pSoldier->bVehicleUnderRepairID = -1; if ( ( bAssignment == DOCTOR ) || ( bAssignment == PATIENT ) || ( bAssignment == ASSIGNMENT_HOSPITAL ) ) { AddStrategicEvent( EVENT_BANDAGE_BLEEDING_MERCS, GetWorldTotalMin() + 1, 0 ); } // update character info, and the team panel fCharacterInfoPanelDirty = TRUE; fTeamPanelDirty = TRUE; // merc may have come on/off duty, make sure map icons are updated fMapPanelDirty = TRUE; } BOOLEAN BasicCanCharacterAssignment( SOLDIERTYPE * pSoldier, BOOLEAN fNotInCombat ) { AssertNotNIL(pSoldier); // global conditions restricting all assignment changes if ( SectorIsImpassable( (INT16) SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ) ) ) { return( FALSE ); } if ( fNotInCombat && pSoldier->bActive && pSoldier->bInSector && gTacticalStatus.fEnemyInSector ) { return( FALSE ); } return( TRUE ); } /* BOOLEAN CanSoldierAssignment( SOLDIERTYPE *pSoldier, INT8 bAssignment ) { AssertNotNIL(pSoldier); switch( bAssignment ) { case( DOCTOR ): return( CanCharacterDoctor( pSoldier ) ); break; case( PATIENT ): return( CanCharacterPatient( pSoldier ) ); break; case( REPAIR ): return( CanCharacterRepair( pSoldier ) ); break; case( TRAIN_TOWN ): return( CanCharacterTrainMilitia( pSoldier ) ); break; case( TRAIN_SELF ): return( CanCharacterTrainStat( pSoldier, pSoldier->bTrainStat, TRUE, FALSE ) ); break; case( TRAIN_TEAMMATE ): return( CanCharacterTrainStat( pSoldier, pSoldier->bTrainStat, FALSE, TRUE ) ); break; case TRAIN_BY_OTHER: return( CanCharacterTrainStat( pSoldier, pSoldier->bTrainStat, TRUE, FALSE ) ); break; case( VEHICLE ): return( CanCharacterVehicle( pSoldier ) ); break; default: return( (CanCharacterSquad( pSoldier, bAssignment ) == CHARACTER_CAN_JOIN_SQUAD ) ); break; } } */ BOOLEAN CanCharacterDoctorButDoesntHaveMedKit( SOLDIERTYPE *pSoldier ) { AssertNotNIL(pSoldier); if ( !BasicCanCharacterAssignment( pSoldier, TRUE ) ) { return( FALSE ); } // make sure character is alive and conscious if( pSoldier->stats.bLife < OKLIFE ) { // dead or unconscious... return ( FALSE ); } // has medical skill? if( pSoldier->stats.bMedical <= 0 ) { // no skill whatsoever return ( FALSE ); } // in transit? if( IsCharacterInTransit( pSoldier ) == TRUE ) { return ( FALSE ); } // character on the move? if( CharacterIsBetweenSectors( pSoldier ) ) { return( FALSE ); } if( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__EPC ) { // epcs can't do this return( FALSE ); } // check in helicopter in hostile sector if( pSoldier->bAssignment == VEHICLE ) { if( ( iHelicopterVehicleId != -1 ) && ( pSoldier->iVehicleId == iHelicopterVehicleId ) ) { // enemies in sector if( NumEnemiesInSector( pSoldier->sSectorX, pSoldier->sSectorY ) > 0 ) { return( FALSE ); } } } return( TRUE ); } // is character capable of 'playing' doctor? // check that character is alive, conscious, has medical skill and equipment BOOLEAN CanCharacterDoctor( SOLDIERTYPE *pSoldier ) { BOOLEAN fFoundMedKit = FALSE; INT8 bPocket = 0; AssertNotNIL(pSoldier); if ( !BasicCanCharacterAssignment( pSoldier, TRUE ) ) { return( FALSE ); } if( CanCharacterDoctorButDoesntHaveMedKit( pSoldier ) == FALSE ) { return( FALSE ); } // find med kit // CHRISL: Changed to dynamically determine max inventory locations. for (bPocket = HANDPOS; bPocket < NUM_INV_SLOTS; bPocket++) { // doctoring is allowed using either type of med kit (but first aid kit halves doctoring effectiveness) if( IsMedicalKitItem( &( pSoldier -> inv[ bPocket ] ) ) ) { fFoundMedKit = TRUE; break; } } if( fFoundMedKit == FALSE ) { return( FALSE ); } // all criteria fit, can doctor return ( TRUE ); } BOOLEAN IsAnythingAroundForSoldierToRepair( SOLDIERTYPE * pSoldier ) { INT32 iCounter; AssertNotNIL(pSoldier); // items? if ( DoesCharacterHaveAnyItemsToRepair( pSoldier, FINAL_REPAIR_PASS ) ) { return( TRUE ); } // robot? if ( CanCharacterRepairRobot( pSoldier ) ) { return( TRUE ); } // vehicles? if ( pSoldier->bSectorZ == 0 ) { for ( iCounter = 0; iCounter < ubNumberOfVehicles; iCounter++ ) { if ( pVehicleList[ iCounter ].fValid == TRUE ) { // the helicopter, is NEVER repairable... if ( iCounter != iHelicopterVehicleId ) { if ( IsThisVehicleAccessibleToSoldier( pSoldier, iCounter ) ) { if( CanCharacterRepairVehicle( pSoldier, iCounter ) == TRUE ) { // there is a repairable vehicle here return( TRUE ); } } } } } } return( FALSE ); } BOOLEAN HasCharacterFinishedRepairing( SOLDIERTYPE * pSoldier ) { BOOLEAN fCanStillRepair; AssertNotNIL(pSoldier); // NOTE: This must detect situations where the vehicle/robot has left the sector, in which case we want the // guy to say "assignment done", so we return that he can no longer repair // check if we are repairing a vehicle if ( pSoldier->bVehicleUnderRepairID != -1 ) { fCanStillRepair = CanCharacterRepairVehicle( pSoldier, pSoldier->bVehicleUnderRepairID ); } // check if we are repairing a robot else if( pSoldier->flags.fFixingRobot ) { fCanStillRepair = CanCharacterRepairRobot( pSoldier ); } else // repairing items { fCanStillRepair = DoesCharacterHaveAnyItemsToRepair( pSoldier, FINAL_REPAIR_PASS ); } // if it's no longer damaged, we're finished! return( !fCanStillRepair ); } BOOLEAN DoesCharacterHaveAnyItemsToRepair( SOLDIERTYPE *pSoldier, INT8 bHighestPass ) { INT8 bPocket; UINT8 ubItemsInPocket, ubObjectInPocketCounter; INT8 bLoop; SOLDIERTYPE * pOtherSoldier; OBJECTTYPE * pObj; UINT8 ubPassType; AssertNotNIL(pSoldier); // check for jams // CHRISL: Changed to dynamically determine max inventory locations. for (bPocket = HELMETPOS; bPocket < NUM_INV_SLOTS; bPocket++) { ubItemsInPocket = pSoldier->inv[ bPocket ].ubNumberOfObjects; // unjam any jammed weapons // run through pocket and repair for( ubObjectInPocketCounter = 0; ubObjectInPocketCounter < ubItemsInPocket; ubObjectInPocketCounter++ ) { // jammed gun? if ( ( Item[ pSoldier->inv[ bPocket ].usItem ].usItemClass == IC_GUN ) && ( pSoldier->inv[ bPocket ][0]->data.gun.bGunAmmoStatus < 0 ) ) { return( TRUE ); } } } // now check for items to repair // CHRISL: Changed to dynamically determine max inventory locations. for( bPocket = HELMETPOS; bPocket < NUM_INV_SLOTS; bPocket++ ) { // run through pocket for( ubObjectInPocketCounter = 0; ubObjectInPocketCounter < pSoldier->inv[ bPocket ].ubNumberOfObjects; ubObjectInPocketCounter++ ) { pObj = FindRepairableItemInSpecificPocket(&(pSoldier->inv[ bPocket ]), ubObjectInPocketCounter); // if it's repairable and NEEDS repairing if(pObj != 0) { return( TRUE ); } if(UsingNewInventorySystem() == true && Item[pSoldier->inv[ bPocket ].usItem].usItemClass == IC_LBEGEAR) { pObj = FindRepairableItemInLBENODE( &pSoldier->inv[ bPocket ], ubObjectInPocketCounter); if(pObj != 0) { return( TRUE ); } } } } // if we wanna check for the items belonging to others in the sector if ( bHighestPass != - 1 ) { // now look for items to repair on other mercs for( bLoop = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; bLoop < gTacticalStatus.Team[ gbPlayerNum ].bLastID; bLoop++ ) { pOtherSoldier = MercPtrs[ bLoop ]; if ( CanCharacterRepairAnotherSoldiersStuff( pSoldier, pOtherSoldier ) ) { // okay, seems like a candidate! Check if he has anything that needs unjamming or repairs // CHRISL: Changed to dynamically determine max inventory locations. for ( bPocket = HANDPOS; bPocket < NUM_INV_SLOTS; bPocket++ ) { // the object a weapon? and jammed? if ( ( Item[ pOtherSoldier->inv[ bPocket ].usItem ].usItemClass == IC_GUN ) && ( pOtherSoldier->inv[ bPocket ][0]->data.gun.bGunAmmoStatus < 0 ) ) { return( TRUE ); } } // repair everyone's hands and armor slots first, then headgear, and pockets last for ( ubPassType = REPAIR_HANDS_AND_ARMOR; ubPassType <= ( UINT8 ) bHighestPass; ubPassType++ ) { if (FindRepairableItemOnOtherSoldier( pOtherSoldier, ubPassType )) { return( TRUE ); } } } } } return( FALSE ); } BOOLEAN BasicCanCharacterRepair( SOLDIERTYPE * pSoldier ) { AssertNotNIL(pSoldier); if ( !BasicCanCharacterAssignment( pSoldier, TRUE ) ) { return( FALSE ); } // make sure character is alive and oklife if( pSoldier->stats.bLife < OKLIFE ) { // dead or unconscious... return ( FALSE ); } // has repair skill? if( pSoldier->stats.bMechanical <= 0 ) { // no skill whatsoever return ( FALSE ); } // in transit? if( IsCharacterInTransit( pSoldier ) == TRUE ) { return ( FALSE ); } // character on the move? if( CharacterIsBetweenSectors( pSoldier ) ) { return( FALSE ); } if( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__EPC ) { // epcs can't do this return( FALSE ); } // check in helicopter in hostile sector if( pSoldier->bAssignment == VEHICLE ) { if( ( iHelicopterVehicleId != -1 ) && ( pSoldier->iVehicleId == iHelicopterVehicleId ) ) { // enemies in sector if( NumEnemiesInSector( pSoldier->sSectorX, pSoldier->sSectorY ) > 0 ) { return( FALSE ); } } } return( TRUE ); } BOOLEAN CanCharacterRepairButDoesntHaveARepairkit( SOLDIERTYPE *pSoldier ) { AssertNotNIL(pSoldier); if ( BasicCanCharacterRepair( pSoldier ) == FALSE ) { return( FALSE ); } // make sure he actually doesn't have a toolkit if ( FindToolkit( pSoldier ) != NO_SLOT ) { return( FALSE ); } return( TRUE ); } // can character be assigned as repairman? // check that character is alive, oklife, has repair skill, and equipment, etc. BOOLEAN CanCharacterRepair( SOLDIERTYPE *pSoldier ) { AssertNotNIL(pSoldier); if ( !BasicCanCharacterAssignment( pSoldier, TRUE ) ) { return( FALSE ); } if ( BasicCanCharacterRepair( pSoldier ) == FALSE ) { return( FALSE ); } // make sure he has a toolkit if ( FindToolkit( pSoldier ) == NO_SLOT ) { return( FALSE ); } // anything around to fix? if ( !IsAnythingAroundForSoldierToRepair( pSoldier ) ) { return( FALSE ); } // NOTE: This will not detect situations where character lacks the SKILL to repair the stuff that needs repairing... // So, in that situation, his assignment will NOT flash, but a message to that effect will be reported every hour. // all criteria fit, can repair return ( TRUE ); } // can character be set to patient? BOOLEAN CanCharacterPatient( SOLDIERTYPE *pSoldier ) { AssertNotNIL(pSoldier); if ( !BasicCanCharacterAssignment( pSoldier, TRUE ) ) { return( FALSE ); } // Robot must be REPAIRED to be "healed", not doctored if( ( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) || AM_A_ROBOT( pSoldier ) ) { return ( FALSE ); } if( pSoldier->bAssignment == ASSIGNMENT_POW ) { return ( FALSE ); } // is character alive and not in perfect health? if( ( pSoldier->stats.bLife <= 0 ) || ( pSoldier->stats.bLife == pSoldier->stats.bLifeMax ) ) { // dead or in perfect health return ( FALSE ); } // in transit? if( IsCharacterInTransit( pSoldier ) == TRUE ) { return ( FALSE ); } // character on the move? if( CharacterIsBetweenSectors( pSoldier ) ) { return( FALSE ); } // check in helicopter in hostile sector if( pSoldier->bAssignment == VEHICLE ) { if( ( iHelicopterVehicleId != -1 ) && ( pSoldier->iVehicleId == iHelicopterVehicleId ) ) { // enemies in sector if( NumEnemiesInSector( pSoldier->sSectorX, pSoldier->sSectorY ) > 0 ) { return( FALSE ); } } } // alive and can be healed return ( TRUE ); } BOOLEAN BasicCanCharacterTrainMilitia( SOLDIERTYPE *pSoldier ) { // is the character capable of training a town? // they must be alive/conscious and in the sector with the town BOOLEAN fSamSitePresent = FALSE; AssertNotNIL(pSoldier); if ( !BasicCanCharacterAssignment( pSoldier, TRUE ) ) { return( FALSE ); } // make sure character is alive and conscious if( pSoldier->stats.bLife < OKLIFE ) { // dead or unconscious... return ( FALSE ); } // underground training is not allowed (code doesn't support and it's a reasonable enough limitation) if( pSoldier->bSectorZ != 0 ) { return( FALSE ); } // is there a town in the character's current sector? if( StrategicMap[ CALCULATE_STRATEGIC_INDEX( pSoldier->sSectorX, pSoldier->sSectorY ) ].bNameId == BLANK_SECTOR ) { fSamSitePresent = IsThisSectorASAMSector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ); // check if sam site if( fSamSitePresent == FALSE ) { // nope return ( FALSE ); } } if( NumEnemiesInAnySector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ) ) { return( FALSE ); } // check in helicopter in hostile sector if( pSoldier->bAssignment == VEHICLE ) { if( ( iHelicopterVehicleId != -1 ) && ( pSoldier->iVehicleId == iHelicopterVehicleId ) ) { // enemies in sector if( NumEnemiesInSector( pSoldier->sSectorX, pSoldier->sSectorY ) > 0 ) { return( FALSE ); } } } // in transit? if( IsCharacterInTransit( pSoldier ) == TRUE ) { return ( FALSE ); } // character on the move? if( CharacterIsBetweenSectors( pSoldier ) ) { return( FALSE ); } if( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__EPC ) { // epcs can't do this return( FALSE ); } if ( ( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) || AM_A_ROBOT( pSoldier ) ) { return( FALSE ); } // has leadership skill? if( pSoldier->stats.bLeadership <= 0 ) { // no skill whatsoever return ( FALSE ); } // can train militia return( TRUE ); } // Kaiden: Roaming Militia Training: // Commenting out the check for: // IsMilitiaTrainableFromSoldiersSectorMaxed( // At this point, we don't care if it's full yet BOOLEAN CanCharacterTrainMilitia( SOLDIERTYPE *pSoldier ) { AssertNotNIL(pSoldier); if (gGameExternalOptions.gfmusttrainroaming && (GetWorldDay( ) >= gGameExternalOptions.guiAllowMilitiaGroupsDelay) && (!IsThisSectorASAMSector(pSoldier->sSectorX,pSoldier->sSectorY,0 ))) { if( BasicCanCharacterTrainMilitia( pSoldier ) && MilitiaTrainingAllowedInSector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ) && DoesSectorMercIsInHaveSufficientLoyaltyToTrainMilitia( pSoldier ) && /*( IsMilitiaTrainableFromSoldiersSectorMaxed( pSoldier ) == FALSE ) && */ ( CountMilitiaTrainersInSoldiersSector( pSoldier ) < gGameExternalOptions.ubMaxMilitiaTrainersPerSector ) ) { return( TRUE ); } else { return( FALSE ); } } else { if( BasicCanCharacterTrainMilitia( pSoldier ) && MilitiaTrainingAllowedInSector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ) && DoesSectorMercIsInHaveSufficientLoyaltyToTrainMilitia( pSoldier ) && ( IsMilitiaTrainableFromSoldiersSectorMaxed( pSoldier,ELITE_MILITIA ) == FALSE ) && ( CountMilitiaTrainersInSoldiersSector( pSoldier ) < gGameExternalOptions.ubMaxMilitiaTrainersPerSector ) ) { return( TRUE ); } else { return( FALSE ); } } } BOOLEAN DoesTownHaveRatingToTrainMilitia( INT8 bTownId ) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Assignments1"); // min loyalty rating? if( ( gTownLoyalty[ bTownId ].ubRating < gGameExternalOptions.iMinLoyaltyToTrain ) ) { // nope return( FALSE ); } return( TRUE ); } BOOLEAN DoesSectorMercIsInHaveSufficientLoyaltyToTrainMilitia( SOLDIERTYPE *pSoldier ) { INT8 bTownId = 0; BOOLEAN fSamSitePresent = FALSE; AssertNotNIL(pSoldier); // underground training is not allowed (code doesn't support and it's a reasonable enough limitation) if( pSoldier->bSectorZ != 0 ) { return( FALSE ); } bTownId = GetTownIdForSector( pSoldier->sSectorX, pSoldier->sSectorY ); // is there a town really here if( bTownId == BLANK_SECTOR ) { fSamSitePresent = IsThisSectorASAMSector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ); // if there is a sam site here if( fSamSitePresent ) { return( TRUE ); } return( FALSE ); } // does this town have sufficient loyalty to train militia if( DoesTownHaveRatingToTrainMilitia( bTownId ) == FALSE ) { return( FALSE ); } return( TRUE ); } INT8 CountMilitiaTrainersInSoldiersSector( SOLDIERTYPE * pSoldier ) { INT8 bLoop; SOLDIERTYPE * pOtherSoldier; INT8 bCount = 0; AssertNotNIL(pSoldier); for ( bLoop = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; bLoop <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; bLoop++ ) { pOtherSoldier = MercPtrs[ bLoop ]; if ( pSoldier != pOtherSoldier && pOtherSoldier->bActive && pOtherSoldier->stats.bLife >= OKLIFE && pOtherSoldier->sSectorX == pSoldier->sSectorX && pOtherSoldier->sSectorY == pSoldier->sSectorY && pSoldier->bSectorZ == pOtherSoldier->bSectorZ ) { if (pOtherSoldier->bAssignment == TRAIN_TOWN ) { bCount++; } } } return( bCount ); } BOOLEAN IsMilitiaTrainableFromSoldiersSectorMaxed( SOLDIERTYPE *pSoldier, INT8 iMilitiaType ) { INT8 bTownId = 0; BOOLEAN fSamSitePresent = FALSE; AssertNotNIL(pSoldier); if( pSoldier->bSectorZ != 0 ) { return( TRUE ); } bTownId = GetTownIdForSector( pSoldier->sSectorX, pSoldier->sSectorY ); // is there a town really here if( bTownId == BLANK_SECTOR ) { fSamSitePresent = IsThisSectorASAMSector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ); // if there is a sam site here if( fSamSitePresent ) { if( IsSAMSiteFullOfMilitia( pSoldier->sSectorX, pSoldier->sSectorY,iMilitiaType ) ) { return( TRUE ); } return( FALSE ); } return( FALSE ); } // this considers *ALL* safe sectors of the town, not just the one soldier is in if( IsTownFullMilitia( bTownId, iMilitiaType ) ) { // town is full of militia return( TRUE ); } return( FALSE ); } BOOLEAN CanCharacterTrainStat( SOLDIERTYPE *pSoldier, INT8 bStat, BOOLEAN fTrainSelf, BOOLEAN fTrainTeammate ) { // is the character capable of training this stat? either self or as trainer AssertNotNIL(pSoldier); if ( !BasicCanCharacterAssignment( pSoldier, TRUE ) ) { return( FALSE ); } // alive and conscious if( pSoldier->stats.bLife < OKLIFE ) { // dead or unconscious... return ( FALSE ); } // underground training is not allowed (code doesn't support and it's a reasonable enough limitation) if( pSoldier->bSectorZ != 0 ) { return( FALSE ); } // check in helicopter in hostile sector if( pSoldier->bAssignment == VEHICLE ) { if( ( iHelicopterVehicleId != -1 ) && ( pSoldier->iVehicleId == iHelicopterVehicleId ) ) { // enemies in sector if( NumEnemiesInSector( pSoldier->sSectorX, pSoldier->sSectorY ) > 0 ) { return( FALSE ); } } } if( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__EPC ) { // epcs can't do this return( FALSE ); } // check stat values, 0 means no chance in hell switch( bStat ) { case ( STRENGTH ): // strength if ( pSoldier->stats.bStrength < gGameExternalOptions.ubTrainingSkillMin ) return FALSE; else if( ( ( pSoldier->stats.bStrength < gGameExternalOptions.ubMinSkillToTeach ) && ( fTrainTeammate ) ) ) { return ( FALSE ); } else if( ( pSoldier->stats.bStrength >= gGameExternalOptions.ubTrainingSkillMax ) && ( fTrainSelf ) ) { return ( FALSE ); } break; case( DEXTERITY ): // dexterity if ( pSoldier->stats.bDexterity < gGameExternalOptions.ubTrainingSkillMin ) return FALSE; else if( ( ( pSoldier->stats.bDexterity < gGameExternalOptions.ubMinSkillToTeach ) && ( fTrainTeammate ) ) ) { return ( FALSE ); } else if( ( pSoldier->stats.bDexterity >= gGameExternalOptions.ubTrainingSkillMax )&&( fTrainSelf ) ) { return ( FALSE ); } break; case( AGILITY ): // agility if ( pSoldier->stats.bAgility < gGameExternalOptions.ubTrainingSkillMin ) return FALSE; else if( ( ( pSoldier->stats.bAgility < gGameExternalOptions.ubMinSkillToTeach ) && ( fTrainTeammate ) ) ) { return ( FALSE ); } else if( ( pSoldier->stats.bAgility >= gGameExternalOptions.ubTrainingSkillMax )&&( fTrainSelf ) ) { return ( FALSE ); } break; case( HEALTH ): // health if ( pSoldier->stats.bLifeMax < gGameExternalOptions.ubTrainingSkillMin ) return FALSE; else if( ( ( pSoldier->stats.bLifeMax < gGameExternalOptions.ubMinSkillToTeach ) && ( fTrainTeammate ) ) ) { return ( FALSE ); } else if( ( pSoldier->stats.bLifeMax >= gGameExternalOptions.ubTrainingSkillMax )&&( fTrainSelf ) ) { return ( FALSE ); } break; case( MARKSMANSHIP ): // marksmanship if ( pSoldier->stats.bMarksmanship < gGameExternalOptions.ubTrainingSkillMin ) return FALSE; else if( ( ( pSoldier->stats.bMarksmanship < gGameExternalOptions.ubMinSkillToTeach ) && ( fTrainTeammate ) ) ) { return ( FALSE ); } else if( ( pSoldier->stats.bMarksmanship >= gGameExternalOptions.ubTrainingSkillMax )&&( fTrainSelf ) ) { return ( FALSE ); } break; case( MEDICAL ): // medical if ( pSoldier->stats.bMedical < gGameExternalOptions.ubTrainingSkillMin ) return FALSE; else if( ( ( pSoldier->stats.bMedical < gGameExternalOptions.ubMinSkillToTeach ) && ( fTrainTeammate ) ) ) { return ( FALSE ); } else if( ( pSoldier->stats.bMedical >= gGameExternalOptions.ubTrainingSkillMax )&&( fTrainSelf ) ) { return ( FALSE ); } break; case( MECHANICAL ): // mechanical if ( pSoldier->stats.bMechanical < gGameExternalOptions.ubTrainingSkillMin ) return FALSE; else if( ( ( pSoldier->stats.bMechanical < gGameExternalOptions.ubMinSkillToTeach ) && ( fTrainTeammate ) ) ) { return ( FALSE ); } else if( ( pSoldier->stats.bMechanical >= gGameExternalOptions.ubTrainingSkillMax )&&( fTrainSelf ) ) { return ( FALSE ); } break; case( LEADERSHIP ): // leadership if ( pSoldier->stats.bLeadership < gGameExternalOptions.ubTrainingSkillMin ) return FALSE; else if( ( ( pSoldier->stats.bLeadership < gGameExternalOptions.ubMinSkillToTeach ) && ( fTrainTeammate ) ) ) { return ( FALSE ); } else if( ( pSoldier->stats.bLeadership >= gGameExternalOptions.ubTrainingSkillMax )&&( fTrainSelf ) ) { return ( FALSE ); } break; case( EXPLOSIVE_ASSIGN ): // explosives if ( pSoldier->stats.bExplosive < gGameExternalOptions.ubTrainingSkillMin ) return FALSE; else if( ( ( pSoldier->stats.bExplosive < gGameExternalOptions.ubMinSkillToTeach ) && ( fTrainTeammate ) ) ) { return ( FALSE ); } else if( ( pSoldier->stats.bExplosive >= gGameExternalOptions.ubTrainingSkillMax )&&( fTrainSelf ) ) { return ( FALSE ); } break; } // in transit? if( IsCharacterInTransit( pSoldier ) == TRUE ) { return ( FALSE ); } // character on the move? if( CharacterIsBetweenSectors( pSoldier ) ) { return( FALSE ); } // stat is ok and character alive and conscious return( TRUE ); } BOOLEAN CanCharacterOnDuty( SOLDIERTYPE *pSoldier ) { // can character commit themselves to on duty? AssertNotNIL(pSoldier); // only need to be alive and well to do so right now // alive and conscious if( pSoldier->stats.bLife < OKLIFE ) { // dead or unconscious... return ( FALSE ); } if ( !BasicCanCharacterAssignment( pSoldier, FALSE ) ) { return( FALSE ); } // check in helicopter in hostile sector if( pSoldier->bAssignment == VEHICLE ) { if( ( iHelicopterVehicleId != -1 ) && ( pSoldier->iVehicleId == iHelicopterVehicleId ) ) { // enemies in sector if( NumEnemiesInSector( pSoldier->sSectorX, pSoldier->sSectorY ) > 0 ) { return( TRUE ); } } } // in transit? if( IsCharacterInTransit( pSoldier ) == TRUE ) { return ( FALSE ); } // ARM: New rule: can't change squads or exit vehicles between sectors! if( pSoldier->flags.fBetweenSectors ) { return ( FALSE ); } /* if( pSoldier->flags.fBetweenSectors ) { if( pSoldier->bAssignment == VEHICLE ) { if( GetNumberInVehicle( pSoldier->iVehicleId ) == 1 ) { // can't change, go away return( FALSE ); } } } */ return( TRUE ); } BOOLEAN CanCharacterPractise( SOLDIERTYPE *pSoldier ) { // can character practise right now? AssertNotNIL(pSoldier); if ( !BasicCanCharacterAssignment( pSoldier, TRUE ) ) { return( FALSE ); } // only need to be alive and well to do so right now // alive and conscious if( pSoldier->stats.bLife < OKLIFE ) { // dead or unconscious... return ( FALSE ); } if( pSoldier->bSectorZ != 0 ) { return( FALSE ); } // in transit? if( IsCharacterInTransit( pSoldier ) == TRUE ) { return ( FALSE ); } // character on the move? if( CharacterIsBetweenSectors( pSoldier ) ) { return( FALSE ); } // check in helicopter in hostile sector if( pSoldier->bAssignment == VEHICLE ) { if( ( iHelicopterVehicleId != -1 ) && ( pSoldier->iVehicleId == iHelicopterVehicleId ) ) { // enemies in sector if( NumEnemiesInSector( pSoldier->sSectorX, pSoldier->sSectorY ) > 0 ) { return( FALSE ); } } } if( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__EPC ) { // epcs can't do this return( FALSE ); } // can practise return( TRUE ); } BOOLEAN CanCharacterTrainTeammates( SOLDIERTYPE *pSoldier ) { AssertNotNIL(pSoldier); // can character train at all if( CanCharacterPractise( pSoldier ) == FALSE ) { // nope return( FALSE ); } // if alone in sector, can't enter the attributes submenu at all if ( PlayerMercsInSector( ( UINT8 ) pSoldier->sSectorX, ( UINT8 ) pSoldier->sSectorY, pSoldier->bSectorZ ) == 0 ) { return( FALSE ); } // ARM: we allow this even if there are no students assigned yet. Flashing is warning enough. return( TRUE ); } BOOLEAN CanCharacterBeTrainedByOther( SOLDIERTYPE *pSoldier ) { AssertNotNIL(pSoldier); // can character train at all if( CanCharacterPractise( pSoldier ) == FALSE ) { return( FALSE ); } // if alone in sector, can't enter the attributes submenu at all if ( PlayerMercsInSector( ( UINT8 ) pSoldier->sSectorX, ( UINT8 ) pSoldier->sSectorY, pSoldier->bSectorZ ) == 0 ) { return( FALSE ); } // ARM: we now allow this even if there are no trainers assigned yet. Flashing is warning enough. return( TRUE ); } // can character sleep right now? BOOLEAN CanCharacterSleep( SOLDIERTYPE *pSoldier, BOOLEAN fExplainWhyNot ) { CHAR16 sString[ 128 ]; AssertNotNIL(pSoldier); // dead or dying? if( pSoldier->stats.bLife < OKLIFE ) { return( FALSE ); } // vehicle or robot? if( ( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) || AM_A_ROBOT( pSoldier ) ) { return( FALSE ); } // in transit? if( IsCharacterInTransit( pSoldier ) == TRUE ) { return( FALSE ); } // POW? if( pSoldier->bAssignment == ASSIGNMENT_POW ) { return( FALSE ); } // traveling? if ( pSoldier->flags.fBetweenSectors ) { // if walking if ( pSoldier->bAssignment != VEHICLE ) { // can't sleep while walking or driving a vehicle if( fExplainWhyNot ) { // on the move, can't sleep swprintf( sString, zMarksMapScreenText[ 5 ], pSoldier->name ); DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL ); } return( FALSE ); } else // in a vehicle { // if this guy has to drive ('cause nobody else can) if ( SoldierMustDriveVehicle( pSoldier, pSoldier->iVehicleId, FALSE ) ) { // can't sleep while walking or driving a vehicle if( fExplainWhyNot ) { // is driving, can't sleep swprintf( sString, zMarksMapScreenText[ 7 ], pSoldier->name ); DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL ); } return( FALSE ); } } } else // in a sector { // if not above it all... if ( !SoldierAboardAirborneHeli( pSoldier ) ) { // if he's in the loaded sector, and it's hostile or in combat if( pSoldier->bInSector && ( ( gTacticalStatus.uiFlags & INCOMBAT ) || gTacticalStatus.fEnemyInSector ) ) { if( fExplainWhyNot ) { DoScreenIndependantMessageBox( Message[ STR_SECTOR_NOT_CLEARED ], MSG_BOX_FLAG_OK, NULL ); } return( FALSE ); } // on surface, and enemies are in the sector if( ( pSoldier->bSectorZ == 0 ) && ( NumEnemiesInAnySector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ) > 0 ) ) { if( fExplainWhyNot ) { DoScreenIndependantMessageBox( Message[ STR_SECTOR_NOT_CLEARED ], MSG_BOX_FLAG_OK, NULL ); } return( FALSE ); } } } // not tired? if( pSoldier->bBreathMax >= BREATHMAX_FULLY_RESTED ) { if( fExplainWhyNot ) { swprintf( sString, zMarksMapScreenText[ 4 ], pSoldier->name ); DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL ); } return( FALSE ); } // can sleep return( TRUE ); } BOOLEAN CanCharacterBeAwakened( SOLDIERTYPE *pSoldier, BOOLEAN fExplainWhyNot ) { CHAR16 sString[ 128 ]; AssertNotNIL(pSoldier); // if dead tired if( ( pSoldier->bBreathMax <= BREATHMAX_ABSOLUTE_MINIMUM ) && !pSoldier->flags.fMercCollapsedFlag ) { // should be collapsed, then! pSoldier->flags.fMercCollapsedFlag = TRUE; } // merc collapsed due to being dead tired, you can't wake him up until he recovers substantially if ( pSoldier->flags.fMercCollapsedFlag == TRUE ) { if ( fExplainWhyNot ) { swprintf( sString, zMarksMapScreenText[ 6 ], pSoldier->name ); DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL ); } return( FALSE ); } // can be awakened return( TRUE ); } BOOLEAN CanCharacterVehicle( SOLDIERTYPE *pSoldier ) { // can character enter/leave vehicle? AssertNotNIL(pSoldier); if ( !BasicCanCharacterAssignment( pSoldier, TRUE ) ) { return( FALSE ); } // only need to be alive and well to do so right now // alive and conscious if( pSoldier->stats.bLife < OKLIFE ) { // dead or unconscious... return ( FALSE ); } // in transit? if( IsCharacterInTransit( pSoldier ) == TRUE ) { return ( FALSE ); } // character on the move? if( CharacterIsBetweenSectors( pSoldier ) ) { return( FALSE ); } // underground? if( pSoldier->bSectorZ != 0 ) { return( FALSE ); } // check in helicopter in hostile sector if( pSoldier->bAssignment == VEHICLE ) { if( ( iHelicopterVehicleId != -1 ) && ( pSoldier->iVehicleId == iHelicopterVehicleId ) ) { // enemies in sector if( NumEnemiesInSector( pSoldier->sSectorX, pSoldier->sSectorY ) > 0 ) { return( FALSE ); } } } // any accessible vehicles in soldier's sector (excludes those between sectors, etc.) if( AnyAccessibleVehiclesInSoldiersSector( pSoldier ) == FALSE ) { return( FALSE ); } // have to be in mapscreen (strictly for visual reasons - we don't want them just vanishing if in tactical) if( fInMapMode == FALSE ) { return( FALSE ); } // if we're in BATTLE in the current sector, disallow if ( gTacticalStatus.fEnemyInSector ) { if ( ( pSoldier->sSectorX == gWorldSectorX ) && ( pSoldier->sSectorY == gWorldSectorY ) && ( pSoldier->bSectorZ == gbWorldSectorZ) ) { return( FALSE ); } } return( TRUE ); } INT8 CanCharacterSquad( SOLDIERTYPE *pSoldier, INT8 bSquadValue ) { // can character join this squad? INT16 sX, sY, sZ; AssertLT( bSquadValue, ON_DUTY ); AssertNotNIL(pSoldier); if ( pSoldier->bAssignment == bSquadValue ) { return( CHARACTER_CANT_JOIN_SQUAD_ALREADY_IN_IT ); } // is the character alive and well? if( pSoldier->stats.bLife < OKLIFE ) { // dead or unconscious... return ( CHARACTER_CANT_JOIN_SQUAD ); } // in transit? if( IsCharacterInTransit( pSoldier ) == TRUE ) { return ( CHARACTER_CANT_JOIN_SQUAD ); } if ( pSoldier->bAssignment == ASSIGNMENT_POW ) { // not allowed to be put on a squad return( CHARACTER_CANT_JOIN_SQUAD ); } /* Driver can't abandon vehicle between sectors - OBSOLETE - nobody is allowed to change squads between sectors now! if( pSoldier->flags.fBetweenSectors ) { if( pSoldier->bAssignment == VEHICLE ) { if( GetNumberInVehicle( pSoldier->iVehicleId ) == 1 ) { // can't change, go away return( CHARACTER_CANT_JOIN_SQUAD ); } } } */ // see if the squad us at the same x,y,z SectorSquadIsIn( bSquadValue, &sX, &sY, &sZ ); // check sector x y and z, if not same, cannot join squad if( ( sX != pSoldier->sSectorX ) || ( sY != pSoldier->sSectorY ) || ( sZ != pSoldier->bSectorZ ) ) { // is there anyone on this squad? if( NumberOfPeopleInSquad( bSquadValue ) > 0 ) { return ( CHARACTER_CANT_JOIN_SQUAD_TOO_FAR ); } } if( IsThisSquadOnTheMove( bSquadValue ) == TRUE ) { // can't join while squad is moving return( CHARACTER_CANT_JOIN_SQUAD_SQUAD_MOVING ); } if ( DoesVehicleExistInSquad( bSquadValue ) ) { // sorry can't change to this squad that way! return( CHARACTER_CANT_JOIN_SQUAD_VEHICLE ); } if ( NumberOfPeopleInSquad( bSquadValue ) >= NUMBER_OF_SOLDIERS_PER_SQUAD ) { return( CHARACTER_CANT_JOIN_SQUAD_FULL ); } return ( CHARACTER_CAN_JOIN_SQUAD ); } BOOLEAN IsCharacterInTransit( SOLDIERTYPE *pSoldier ) { AssertNotNIL(pSoldier); // valid character? if( pSoldier == NULL ) { return ( FALSE ); } // check if character is currently in transit if( pSoldier->bAssignment == IN_TRANSIT ) { // yep return ( TRUE ); } // no return ( FALSE ); } void UpdateAssignments() { INT8 sX,sY, bZ; // init sectors with soldiers list InitSectorsWithSoldiersList( ); // build list BuildSectorsWithSoldiersList( ); // handle natural healing HandleNaturalHealing( ); // handle any patients in the hospital CheckForAndHandleHospitalPatients( ); // see if any grunt or trainer just lost student/teacher ReportTrainersTraineesWithoutPartners( ); // clear out the update list ClearSectorListForCompletedTrainingOfMilitia( ); // rest resting mercs, fatigue active mercs, // check for mercs tired enough go to sleep, and wake up well-rested mercs HandleRestFatigueAndSleepStatus( ); #ifdef JA2BETAVERSION // put this BEFORE training gets handled to avoid detecting an error everytime a sector completes training VerifyTownTrainingIsPaidFor(); #endif // run through sectors and handle each type in sector for(sX = 0 ; sX < MAP_WORLD_X; sX++ ) { for( sY =0; sY < MAP_WORLD_X; sY++ ) { for( bZ = 0; bZ < 4; bZ++) { // handle militia squads movings and creating (not an assignment) if(!bZ && sX < 17 && sY < 17 && sX > 0 && sY > 0)UpdateMilitiaSquads( sX, sY ); // is there anyone in this sector? if( fSectorsWithSoldiers[ sX + sY * MAP_WORLD_X ][ bZ ] == TRUE ) { // handle any doctors HandleDoctorsInSector( sX, sY, bZ ); // handle any repairmen HandleRepairmenInSector( sX, sY, bZ ); // handle any training HandleTrainingInSector( sX, sY, bZ ); } } } } // check to see if anyone is done healing? UpdatePatientsWhoAreDoneHealing( ); // check if we have anyone who just finished their assignment if( gfAddDisplayBoxToWaitingQueue ) { AddDisplayBoxToWaitingQueue( ); gfAddDisplayBoxToWaitingQueue = FALSE; } HandleContinueOfTownTraining( ); // check if anyone is on an assignment where they have nothing to do ReEvaluateEveryonesNothingToDo(); // update mapscreen fCharacterInfoPanelDirty = TRUE; fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; } #ifdef JA2BETAVERSION void VerifyTownTrainingIsPaidFor( void ) { INT32 iCounter = 0; SOLDIERTYPE *pSoldier = NULL; for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { // valid character? if( gCharactersList[ iCounter ].fValid == FALSE ) { // nope continue; } pSoldier = &Menptr[ gCharactersList[ iCounter ].usSolID ]; if( pSoldier->bActive && ( pSoldier->bAssignment == TRAIN_TOWN ) ) { // make sure that sector is paid up! if( SectorInfo[ SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ) ].fMilitiaTrainingPaid == FALSE ) { // NOPE! We've got a bug somewhere StopTimeCompression(); // report the error DoScreenIndependantMessageBox( L"ERROR: Unpaid militia training. Describe *how* you're re-assigning mercs, how many/where/when! Send *prior* save!", MSG_BOX_FLAG_OK, NULL ); // avoid repeating this break; } } } } #endif UINT8 FindNumberInSectorWithAssignment( INT16 sX, INT16 sY, INT8 bAssignment ) { // run thought list of characters find number with this assignment SOLDIERTYPE *pSoldier, *pTeamSoldier; INT32 cnt=0; INT8 bNumberOfPeople = 0; // set psoldier as first in merc ptrs pSoldier = MercPtrs[0]; // go through list of characters, find all who are on this assignment for ( pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ pSoldier->bTeam ].bLastID; cnt++,pTeamSoldier++) { if( pTeamSoldier->bActive ) { if( ( pTeamSoldier->sSectorX == sX ) && ( pTeamSoldier->sSectorY == sY ) &&( pTeamSoldier->bAssignment == bAssignment ) ) { // increment number of people who are on this assignment if(pTeamSoldier->bActive) bNumberOfPeople++; } } } return( bNumberOfPeople ); } UINT8 GetNumberThatCanBeDoctored( SOLDIERTYPE *pDoctor, BOOLEAN fThisHour, BOOLEAN fSkipKitCheck, BOOLEAN fSkipSkillCheck ) { int cnt; SOLDIERTYPE *pSoldier = MercPtrs[0], *pTeamSoldier = NULL; UINT8 ubNumberOfPeople = 0; AssertNotNIL(pDoctor); // go through list of characters, find all who are patients/doctors healable by this doctor for ( cnt = 0, pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ pSoldier->bTeam ].bLastID; cnt++,pTeamSoldier++) { if( pTeamSoldier->bActive ) { if( CanSoldierBeHealedByDoctor( pTeamSoldier, pDoctor, FALSE, fThisHour, fSkipKitCheck, fSkipSkillCheck ) == TRUE ) { // increment number of doctorable patients/doctors ubNumberOfPeople++; } } } return( ubNumberOfPeople ); } SOLDIERTYPE *AnyDoctorWhoCanHealThisPatient( SOLDIERTYPE *pPatient, BOOLEAN fThisHour ) { int cnt; SOLDIERTYPE *pSoldier = MercPtrs[0], *pTeamSoldier = NULL; AssertNotNIL(pPatient); AssertNotNIL(pSoldier); // go through list of characters, find all who are patients/doctors healable by this doctor for ( cnt = 0, pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ pSoldier->bTeam ].bLastID; cnt++,pTeamSoldier++) { // doctor? if( ( pTeamSoldier->bActive ) && ( pTeamSoldier->bAssignment == DOCTOR ) ) { if( CanSoldierBeHealedByDoctor( pPatient, pTeamSoldier, FALSE, fThisHour, FALSE, FALSE ) == TRUE ) { // found one return( pTeamSoldier ); } } } // there aren't any doctors, or the ones there can't do the job return( NULL ); } UINT16 CalculateHealingPointsForDoctor(SOLDIERTYPE *pDoctor, UINT16 *pusMaxPts, BOOLEAN fMakeSureKitIsInHand ) { UINT16 usHealPts = 0; UINT16 usKitPts = 0; INT8 bMedFactor; AssertNotNIL(pDoctor); AssertNotNIL(pusMaxPts); // make sure he has a medkit in his hand, and preferably make it a medical bag, not a first aid kit if( fMakeSureKitIsInHand ) { if ( !MakeSureMedKitIsInHand( pDoctor ) ) { return(0); } } // calculate effective doctoring rate (adjusted for drugs, alcohol, etc.) usHealPts = (UINT16) (( EffectiveMedical( pDoctor ) * (( EffectiveDexterity( pDoctor ) + EffectiveWisdom( pDoctor ) ) / 2) * (100 + ( 5 * EffectiveExpLevel( pDoctor) ) ) ) / gGameExternalOptions.ubDoctoringRateDivisor); // calculate normal doctoring rate - what it would be if his stats were "normal" (ignoring drugs, fatigue, equipment condition) // and equipment was not a hindrance *pusMaxPts = ( pDoctor->stats.bMedical * (( pDoctor->stats.bDexterity + pDoctor->stats.bWisdom ) / 2 ) * (100 + ( 5 * pDoctor->stats.bExpLevel) ) ) / gGameExternalOptions.ubDoctoringRateDivisor; // adjust for fatigue ReducePointsForFatigue( pDoctor, &usHealPts ); // count how much medical supplies we have usKitPts = 100 * TotalMedicalKitPoints( pDoctor ); // if we don't have enough medical KIT points, reduce what we can heal if (usKitPts < usHealPts) { usHealPts = usKitPts; } // get the type of medkit being used bMedFactor = IsMedicalKitItem( &( pDoctor->inv[ HANDPOS ] ) ); if( bMedFactor != 0 ) { // no med kit left? // if he's working with only a first aid kit, the doctoring rate is halved! // for simplicity, we're ignoring the situation where a nearly empty medical bag in is hand and the rest are just first aid kits usHealPts /= bMedFactor; } else { usHealPts = 0; } // return healing pts value return( usHealPts ); } UINT8 CalculateRepairPointsForRepairman(SOLDIERTYPE *pSoldier, UINT16 *pusMaxPts, BOOLEAN fMakeSureKitIsInHand ) { UINT16 usRepairPts; UINT16 usKitPts; UINT8 ubKitEffectiveness; // make sure toolkit in hand? if( fMakeSureKitIsInHand ) { MakeSureToolKitIsInHand( pSoldier ); } // can't repair at all without a toolkit if (!Item[pSoldier->inv[HANDPOS].usItem].toolkit ) { *pusMaxPts = 0; return(0); } // calculate effective repair rate (adjusted for drugs, alcohol, etc.) usRepairPts = (UINT16) ((EffectiveMechanical( pSoldier ) * EffectiveDexterity( pSoldier ) * (100 + ( 5 * EffectiveExpLevel( pSoldier) ) ) ) / ( gGameExternalOptions.ubRepairRateDivisor * gGameExternalOptions.ubAssignmentUnitsPerDay )); // calculate normal repair rate - what it would be if his stats were "normal" (ignoring drugs, fatigue, equipment condition) // and equipment was not a hindrance *pusMaxPts = ( pSoldier->stats.bMechanical * pSoldier->stats.bDexterity * (100 + ( 5 * pSoldier->stats.bExpLevel) ) ) / ( gGameExternalOptions.ubRepairRateDivisor * gGameExternalOptions.ubAssignmentUnitsPerDay ); // adjust for fatigue ReducePointsForFatigue( pSoldier, &usRepairPts ); // figure out what shape his "equipment" is in ("coming" in JA3: Viagra - improves the "shape" your "equipment" is in) usKitPts = ToolKitPoints( pSoldier ); // if kit(s) in extremely bad shape if ( usKitPts < 25 ) { ubKitEffectiveness = 50; } // if kit(s) in pretty bad shape else if ( usKitPts < 50 ) { ubKitEffectiveness = 75; } else { ubKitEffectiveness = 100; } // adjust for equipment usRepairPts = (usRepairPts * ubKitEffectiveness) / 100; // return current repair pts return(( UINT8 )usRepairPts); } UINT16 ToolKitPoints(SOLDIERTYPE *pSoldier) { UINT16 usKitpts=0; UINT8 ubPocket; // add up kit points // CHRISL: Changed to dynamically determine max inventory locations. for (ubPocket=HANDPOS; ubPocket < NUM_INV_SLOTS; ubPocket++) { if( Item[pSoldier->inv[ ubPocket ].usItem].toolkit ) { usKitpts += TotalPoints( &( pSoldier->inv[ ubPocket ] ) ); } } return( usKitpts ); } UINT16 TotalMedicalKitPoints(SOLDIERTYPE *pSoldier) { UINT8 ubPocket; UINT16 usKitpts=0; // add up kit points of all medkits // CHRISL: Changed to dynamically determine max inventory locations. for (ubPocket = HANDPOS; ubPocket < NUM_INV_SLOTS; ubPocket++) { // NOTE: Here, we don't care whether these are MEDICAL BAGS or FIRST AID KITS! if ( IsMedicalKitItem( &( pSoldier->inv[ ubPocket ] ) ) ) { usKitpts += TotalPoints( &( pSoldier->inv[ ubPocket ] ) ); } } return( usKitpts ); } void HandleDoctorsInSector( INT16 sX, INT16 sY, INT8 bZ ) { SOLDIERTYPE *pSoldier, *pTeamSoldier; INT32 cnt=0; // set psoldier as first in merc ptrs pSoldier = MercPtrs[0]; // will handle doctor/patient relationship in sector // go through list of characters, find all doctors in sector for ( pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ pSoldier->bTeam ].bLastID; cnt++,pTeamSoldier++) { if(pTeamSoldier->bActive) { if( ( pTeamSoldier->sSectorX == sX ) && ( pTeamSoldier->sSectorY == sY ) && ( pTeamSoldier->bSectorZ == bZ ) ) { if ( ( pTeamSoldier->bAssignment == DOCTOR ) && ( pTeamSoldier->flags.fMercAsleep == FALSE ) ) { MakeSureMedKitIsInHand( pTeamSoldier ); // character is in sector, check if can doctor, if so...heal people if ( CanCharacterDoctor( pTeamSoldier ) && EnoughTimeOnAssignment( pTeamSoldier ) ) { HealCharacters( pTeamSoldier, sX, sY, bZ ); } } /* if( ( pTeamSoldier->bAssignment == DOCTOR ) && ( pTeamSoldier->flags.fMercAsleep == FALSE ) ) { MakeSureMedKitIsInHand( pTeamSoldier ); } // character is in sector, check if can doctor, if so...heal people if( CanCharacterDoctor( pTeamSoldier ) && ( pTeamSoldier->bAssignment == DOCTOR ) && ( pTeamSoldier->flags.fMercAsleep == FALSE ) && EnoughTimeOnAssignment( pTeamSoldier ) ) { HealCharacters( pTeamSoldier, sX, sY, bZ ); } */ } } } // total healing pts for this sector, now heal people return; } void UpdatePatientsWhoAreDoneHealing( void ) { INT32 cnt = 0; SOLDIERTYPE *pSoldier = NULL, *pTeamSoldier = NULL; // set as first in list pSoldier = MercPtrs[0]; for ( pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ pSoldier->bTeam ].bLastID; cnt++,pTeamSoldier++) { // active soldier? if( pTeamSoldier->bActive ) { // patient who doesn't need healing if( ( pTeamSoldier->bAssignment == PATIENT ) &&( pTeamSoldier->stats.bLife == pTeamSoldier->stats.bLifeMax ) ) { AssignmentDone( pTeamSoldier, TRUE, TRUE ); } } } return; } void HealCharacters( SOLDIERTYPE *pDoctor, INT16 sX, INT16 sY, INT8 bZ ) { // heal all patients in this sector UINT16 usAvailableHealingPts = 0; UINT16 usRemainingHealingPts = 0; UINT16 usUsedHealingPts = 0; UINT16 usEvenHealingAmount = 0; UINT16 usMax =0; UINT8 ubTotalNumberOfPatients = 0; SOLDIERTYPE *pSoldier = MercPtrs[0], *pTeamSoldier = NULL, *pWorstHurtSoldier = NULL; INT32 cnt = 0; UINT16 usOldLeftOvers = 0; // now find number of healable mercs in sector that are wounded ubTotalNumberOfPatients = GetNumberThatCanBeDoctored( pDoctor, HEALABLE_THIS_HOUR, FALSE, FALSE ); // if there is anybody who can be healed right now if( ubTotalNumberOfPatients > 0 ) { // get available healing pts usAvailableHealingPts = CalculateHealingPointsForDoctor( pDoctor, &usMax, TRUE ); usRemainingHealingPts = usAvailableHealingPts; // find how many healing points can be evenly distributed to each wounded, healable merc usEvenHealingAmount = usRemainingHealingPts / ubTotalNumberOfPatients; // heal each of the healable mercs by this equal amount for ( cnt = 0, pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ pSoldier->bTeam ].bLastID; cnt++,pTeamSoldier++) { if( pTeamSoldier->bActive ) { if( CanSoldierBeHealedByDoctor( pTeamSoldier, pDoctor, FALSE, HEALABLE_THIS_HOUR, FALSE, FALSE ) == TRUE ) { // can heal and is patient, heal them usRemainingHealingPts -= HealPatient( pTeamSoldier, pDoctor, usEvenHealingAmount ); } } } // if we have any remaining pts if ( usRemainingHealingPts > 0) { // split those up based on need - lowest life patients get them first do { // find the worst hurt patient pWorstHurtSoldier = NULL; for ( cnt = 0, pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ pSoldier->bTeam ].bLastID; cnt++,pTeamSoldier++) { if( pTeamSoldier->bActive ) { if( CanSoldierBeHealedByDoctor( pTeamSoldier, pDoctor, FALSE, HEALABLE_THIS_HOUR, FALSE, FALSE ) == TRUE ) { if( pWorstHurtSoldier == NULL ) { pWorstHurtSoldier = pTeamSoldier; } else { // check to see if this guy is hurt worse than anyone previous? if( pTeamSoldier->stats.bLife < pWorstHurtSoldier->stats.bLife ) { // he is now the worse hurt guy pWorstHurtSoldier = pTeamSoldier; } } } } } if( pWorstHurtSoldier != NULL ) { // heal the worst hurt guy usOldLeftOvers = usRemainingHealingPts; usRemainingHealingPts -= HealPatient( pWorstHurtSoldier, pDoctor, usRemainingHealingPts ); // couldn't expend any pts, leave if( usRemainingHealingPts == usOldLeftOvers ) { usRemainingHealingPts = 0; } } } while( ( usRemainingHealingPts > 0 ) && ( pWorstHurtSoldier != NULL ) ); } usUsedHealingPts = usAvailableHealingPts - usRemainingHealingPts; // increment skills based on healing pts used StatChange(pDoctor, MEDICALAMT, (UINT16) (usUsedHealingPts / 100), FALSE); StatChange(pDoctor, DEXTAMT, (UINT16) (usUsedHealingPts / 100), FALSE); StatChange(pDoctor, WISDOMAMT, (UINT16) (usUsedHealingPts / 100), FALSE); } // if there's nobody else here who can EVER be helped by this doctor (regardless of whether they got healing this hour) if( GetNumberThatCanBeDoctored( pDoctor, HEALABLE_EVER, FALSE, FALSE ) == 0 ) { // then this doctor has done all that he can do, but let's find out why and tell player the reason // try again, but skip the med kit check! if( GetNumberThatCanBeDoctored( pDoctor, HEALABLE_EVER, TRUE, FALSE ) > 0 ) { // he could doctor somebody, but can't because he doesn't have a med kit! AssignmentAborted( pDoctor, NO_MORE_MED_KITS ); } // try again, but skip the skill check! else if( GetNumberThatCanBeDoctored( pDoctor, HEALABLE_EVER, FALSE, TRUE ) > 0 ) { // he could doctor somebody, but can't because he doesn't have enough skill! AssignmentAborted( pDoctor, INSUF_DOCTOR_SKILL ); } else { // all patients should now be healed - truly DONE! AssignmentDone( pDoctor, TRUE, TRUE ); } } } /* Assignment distance limits removed. Sep/11/98. ARM BOOLEAN IsSoldierCloseEnoughToADoctor( SOLDIERTYPE *pPatient ) { // run through all doctors in sector, if it is loaded // if no - one is close enough and there is a doctor assigned in sector, inform player BOOLEAN fDoctorInSector = FALSE; BOOLEAN fDoctorCloseEnough = FALSE; SOLDIERTYPE *pSoldier = NULL; INT32 iCounter = 0; CHAR16 sString[ 128 ]; if( ( pPatient->sSectorX != gWorldSectorX ) || ( pPatient->sSectorY != gWorldSectorY ) || ( pPatient->bSectorZ != gbWorldSectorZ ) ) { // not currently loaded return( TRUE ); } for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { pSoldier = &Menptr[ iCounter ]; if( pSoldier->bActive ) { // are they two of these guys in the same sector? if( ( pSoldier->sSectorX == pPatient->sSectorX ) && ( pSoldier->sSectorY == pPatient->sSectorY ) && ( pSoldier->bSectorZ == pPatient->bSectorZ ) ) { // is a doctor if( pSoldier->bAssignment == DOCTOR ) { // the doctor is in the house fDoctorInSector = TRUE; // can this patient be healed by the doctor? if( CanSoldierBeHealedByDoctor( pPatient, pSoldier, TRUE, HEALABLE_EVER, FALSE, FALSE ) == TRUE ) { // yep fDoctorCloseEnough = TRUE; } } } } } // there are doctors here but noone can heal this guy if( ( fDoctorInSector ) && ( fDoctorCloseEnough == FALSE ) ) { swprintf( sString, pDoctorWarningString[ 0 ] , pPatient->name ); DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL); return( FALSE ); } return( TRUE ); } */ BOOLEAN CanSoldierBeHealedByDoctor( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pDoctor, BOOLEAN fIgnoreAssignment, BOOLEAN fThisHour, BOOLEAN fSkipKitCheck, BOOLEAN fSkipSkillCheck ) { // must be an active guy if (pSoldier->bActive == FALSE) { return(FALSE); } // must be a patient or a doctor if( ( pSoldier->bAssignment != PATIENT ) && ( pSoldier->bAssignment != DOCTOR ) && ( fIgnoreAssignment == FALSE ) ) { return(FALSE); } // if dead or unhurt if ( (pSoldier->stats.bLife == 0) || (pSoldier->stats.bLife == pSoldier->stats.bLifeMax ) ) { return(FALSE); } // if we care about how long it's been, and he hasn't been on a healable assignment long enough if( fThisHour && ( EnoughTimeOnAssignment( pSoldier ) == FALSE ) && ( fIgnoreAssignment == FALSE ) ) { return( FALSE ); } // must be in the same sector if( ( pSoldier->sSectorX != pDoctor->sSectorX ) || ( pSoldier->sSectorY != pDoctor->sSectorY ) || ( pSoldier->bSectorZ != pDoctor->bSectorZ ) ) { return(FALSE); } // can't be between sectors (possible to get here if ignoring assignment) if ( pSoldier->flags.fBetweenSectors ) { return(FALSE); } // if doctor's skill is unsufficient to save this guy if ( !fSkipSkillCheck && ( pDoctor->stats.bMedical < GetMinHealingSkillNeeded( pSoldier ) ) ) { return(FALSE); } if ( !fSkipKitCheck && ( FindMedKit( pDoctor ) == NO_SLOT ) ) { // no medical kit to use! return( FALSE ); } return( TRUE ); } UINT8 GetMinHealingSkillNeeded( SOLDIERTYPE *pPatient ) { // get the minimum skill to handle a character under OKLIFE if( pPatient->stats.bLife < OKLIFE ) { // less than ok life, return skill needed return( gGameExternalOptions.ubBaseMedicalSkillToDealWithEmergency + ( gGameExternalOptions.ubMultiplierForDifferenceInLifeValueForEmergency * ( OKLIFE - pPatient->stats.bLife ) ) ); } else { // only need some skill return ( 1 ); } } UINT16 HealPatient( SOLDIERTYPE *pPatient, SOLDIERTYPE * pDoctor, UINT16 usHundredthsHealed ) { // heal patient and return the number of healing pts used UINT16 usHealingPtsLeft; UINT16 usTotalFullPtsUsed = 0; UINT16 usTotalHundredthsUsed = 0; INT8 bPointsToUse = 0; INT8 bPointsUsed = 0; INT8 bPointsHealed = 0; INT8 bPocket = 0; INT8 bMedFactor; // INT8 bOldPatientLife = pPatient->stats.bLife; pPatient->sFractLife += usHundredthsHealed; usTotalHundredthsUsed = usHundredthsHealed; // we'll subtract any unused amount later if we become fully healed... // convert fractions into full points usHealingPtsLeft = pPatient->sFractLife / 100; pPatient->sFractLife %= 100; // if we haven't accumulated any full points yet if (usHealingPtsLeft == 0) { return( usTotalHundredthsUsed ); } /* ARM - Eliminated. We now have other methods of properly using doctors to auto-bandage bleeding, // using the correct kits points instead of this 1 pt. "special" // stop all bleeding of patient..for 1 pt (it's fast). But still use up normal kit pts to do it if (pPatient->bBleeding > 0) { usHealingPtsLeft--; usTotalFullPtsUsed++; // get points needed to heal him to dress bleeding wounds bPointsToUse = pPatient->bBleeding; // go through doctor's pockets and heal, starting at with his in-hand item // the healing pts are based on what type of medkit is in his hand, so we HAVE to start there first! for (bPocket = HANDPOS; bPocket <= SMALLPOCK8POS; bPocket++) { bMedFactor = IsMedicalKitItem( &( pDoctor->inv[ bPocket ] ) ); if ( bMedFactor > 0 ) { // ok, we have med kit in this pocket, use it // The medFactor here doesn't affect how much the doctor can heal (that's already factored into lower healing pts) // but it does effect how fast the medkit is used up! First aid kits disappear at double their doctoring rate! bPointsUsed = (INT8) UseKitPoints( &( pDoctor->inv[ bPocket ] ), (UINT16) (bPointsToUse * bMedFactor), pDoctor ); bPointsHealed = bPointsUsed / bMedFactor; bPointsToUse -= bPointsHealed; pPatient->bBleeding -= bPointsHealed; // if we're done all we're supposed to, or the guy's no longer bleeding, bail if ( ( bPointsToUse <= 0 ) || ( pPatient->bBleeding == 0 ) ) { break; } } } } */ // if below ok life, heal these first at double point cost if( pPatient->stats.bLife < OKLIFE ) { // get points needed to heal him to OKLIFE bPointsToUse = gGameExternalOptions.ubPointCostPerHealthBelowOkLife * ( OKLIFE - pPatient->stats.bLife ); // if he needs more than we have, reduce to that if( bPointsToUse > usHealingPtsLeft ) { bPointsToUse = ( INT8 )usHealingPtsLeft; } // go through doctor's pockets and heal, starting at with his in-hand item // the healing pts are based on what type of medkit is in his hand, so we HAVE to start there first! // CHRISL: Changed to dynamically determine max inventory locations. for (bPocket = HANDPOS; bPocket < NUM_INV_SLOTS; bPocket++) { bMedFactor = IsMedicalKitItem( &( pDoctor->inv[ bPocket ] ) ); if ( bMedFactor > 0 ) { // ok, we have med kit in this pocket, use it // The medFactor here doesn't affect how much the doctor can heal (that's already factored into lower healing pts) // but it does effect how fast the medkit is used up! First aid kits disappear at double their doctoring rate! bPointsUsed = (INT8) UseKitPoints( &( pDoctor->inv[ bPocket ] ), (UINT16) (bPointsToUse * bMedFactor), pDoctor ); bPointsHealed = bPointsUsed / bMedFactor; bPointsToUse -= bPointsHealed; usHealingPtsLeft -= bPointsHealed; usTotalFullPtsUsed += bPointsHealed; // heal person the amount / POINT_COST_PER_HEALTH_BELOW_OKLIFE pPatient->stats.bLife += (bPointsHealed / gGameExternalOptions.ubPointCostPerHealthBelowOkLife); // if we're done all we're supposed to, or the guy's at OKLIFE, bail if ( ( bPointsToUse <= 0 ) || ( pPatient->stats.bLife >= OKLIFE ) ) { break; } } } } // critical conditions handled, now apply normal healing if (pPatient->stats.bLife < pPatient->stats.bLifeMax) { bPointsToUse = ( pPatient->stats.bLifeMax - pPatient->stats.bLife ); // if guy is hurt more than points we have...heal only what we have if( bPointsToUse > usHealingPtsLeft ) { bPointsToUse = ( INT8 )usHealingPtsLeft; } // go through doctor's pockets and heal, starting at with his in-hand item // the healing pts are based on what type of medkit is in his hand, so we HAVE to start there first! // CHRISL: Changed to dynamically determine max inventory locations. for (bPocket = HANDPOS; bPocket < NUM_INV_SLOTS; bPocket++) { bMedFactor = IsMedicalKitItem( &( pDoctor->inv[ bPocket ] ) ); if ( bMedFactor > 0 ) { // ok, we have med kit in this pocket, use it (use only half if it's worth double) // The medFactor here doesn't affect how much the doctor can heal (that's already factored into lower healing pts) // but it does effect how fast the medkit is used up! First aid kits disappear at double their doctoring rate! bPointsUsed = (INT8) UseKitPoints( &( pDoctor->inv[ bPocket ] ), (UINT16) (bPointsToUse * bMedFactor), pDoctor ); bPointsHealed = bPointsUsed / bMedFactor; bPointsToUse -= bPointsHealed; usHealingPtsLeft -= bPointsHealed; usTotalFullPtsUsed += bPointsHealed; pPatient->stats.bLife += bPointsHealed; // if we're done all we're supposed to, or the guy's fully healed, bail if ( ( bPointsToUse <= 0 ) || ( pPatient->stats.bLife == pPatient->stats.bLifeMax ) ) { break; } } } } // if this patient is fully healed if( pPatient->stats.bLife == pPatient->stats.bLifeMax ) { // don't count unused full healing points as being used usTotalHundredthsUsed -= (100 * usHealingPtsLeft); // wipe out fractions of extra life, and DON'T count them as used usTotalHundredthsUsed -= pPatient->sFractLife; pPatient->sFractLife = 0; /* ARM Removed. This is duplicating the check in UpdatePatientsWhoAreDoneHealing(), guy would show up twice! // if it isn't the doctor himself) if( ( pPatient != pDoctor ) { AssignmentDone( pPatient, TRUE, TRUE ); } */ } return ( usTotalHundredthsUsed ); } void CheckForAndHandleHospitalPatients( void ) { SOLDIERTYPE *pSoldier, *pTeamSoldier; INT32 cnt=0; if( fSectorsWithSoldiers[ HOSPITAL_SECTOR_X + HOSPITAL_SECTOR_Y * MAP_WORLD_X ][ 0 ] == FALSE ) { // nobody in the hospital sector... leave return; } // set pSoldier as first in merc ptrs pSoldier = MercPtrs[0]; // go through list of characters, find all who are on this assignment for ( pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ pSoldier->bTeam ].bLastID; cnt++,pTeamSoldier++) { if( pTeamSoldier->bActive ) { if ( pTeamSoldier->bAssignment == ASSIGNMENT_HOSPITAL ) { if ( ( pTeamSoldier->sSectorX == HOSPITAL_SECTOR_X ) && ( pTeamSoldier->sSectorY == HOSPITAL_SECTOR_Y ) && ( pTeamSoldier->bSectorZ == 0 ) ) { // heal this character HealHospitalPatient( pTeamSoldier, gGameExternalOptions.ubHospitalHealingRate ); } } } } } void HealHospitalPatient( SOLDIERTYPE *pPatient, UINT16 usHealingPtsLeft ) { INT8 bPointsToUse; if (usHealingPtsLeft <= 0) { return; } /* Stopping hospital patients' bleeding must be handled immediately, not during a regular hourly check // stop all bleeding of patient..for 1 pt. if (pPatient->bBleeding > 0) { usHealingPtsLeft--; pPatient->bBleeding = 0; } */ // if below ok life, heal these first at double cost if( pPatient->stats.bLife < OKLIFE ) { // get points needed to heal him to OKLIFE bPointsToUse = gGameExternalOptions.ubPointCostPerHealthBelowOkLife * ( OKLIFE - pPatient->stats.bLife ); // if he needs more than we have, reduce to that if( bPointsToUse > usHealingPtsLeft ) { bPointsToUse = ( INT8 )usHealingPtsLeft; } usHealingPtsLeft -= bPointsToUse; // heal person the amount / POINT_COST_PER_HEALTH_BELOW_OKLIFE pPatient->stats.bLife += ( bPointsToUse / gGameExternalOptions.ubPointCostPerHealthBelowOkLife ); } // critical condition handled, now solve normal healing if ( pPatient->stats.bLife < pPatient->stats.bLifeMax ) { bPointsToUse = ( pPatient->stats.bLifeMax - pPatient->stats.bLife ); // if guy is hurt more than points we have...heal only what we have if( bPointsToUse > usHealingPtsLeft ) { bPointsToUse = ( INT8 )usHealingPtsLeft; } usHealingPtsLeft -= bPointsToUse; // heal person the amount pPatient->stats.bLife += bPointsToUse; } // if this patient is fully healed if ( pPatient->stats.bLife == pPatient->stats.bLifeMax ) { AssignmentDone( pPatient, TRUE, TRUE ); } } void HandleRepairmenInSector( INT16 sX, INT16 sY, INT8 bZ ) { SOLDIERTYPE *pSoldier, *pTeamSoldier; INT32 cnt=0; // set psoldier as first in merc ptrs pSoldier = MercPtrs[0]; // will handle doctor/patient relationship in sector // go through list of characters, find all doctors in sector for ( pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ pSoldier->bTeam ].bLastID; cnt++,pTeamSoldier++) { if( pTeamSoldier->bActive ) { if( ( pTeamSoldier->sSectorX == sX ) && ( pTeamSoldier->sSectorY == sY ) && ( pTeamSoldier->bSectorZ == bZ) ) { if ( ( pTeamSoldier->bAssignment == REPAIR ) && ( pTeamSoldier->flags.fMercAsleep == FALSE ) ) { MakeSureToolKitIsInHand( pTeamSoldier ); // character is in sector, check if can repair if ( CanCharacterRepair( pTeamSoldier ) && ( EnoughTimeOnAssignment( pTeamSoldier ) ) ) { HandleRepairBySoldier( pTeamSoldier ); } } } } } return; } /* No point in allowing SAM site repair any more. Jan/13/99. ARM INT8 HandleRepairOfSAMSite( SOLDIERTYPE *pSoldier, INT8 bPointsAvailable, BOOLEAN * pfNothingLeftToRepair ) { INT8 bPtsUsed = 0; INT16 sStrategicSector = 0; if( IsThisSectorASAMSector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ) == FALSE ) { return( bPtsUsed ); } else if( ( pSoldier->sSectorX == gWorldSectorX ) && ( pSoldier->bSectorZ == gbWorldSectorZ )&&( pSoldier->sSectorY == gWorldSectorY ) ) { if( CanSoldierRepairSAM( pSoldier, bPointsAvailable ) == FALSE ) { return( bPtsUsed ); } } // repair the SAM sStrategicSector = CALCULATE_STRATEGIC_INDEX( pSoldier->sSectorX, pSoldier->sSectorY ); // do we have more than enough? if( 100 - StrategicMap[ sStrategicSector ].bSAMCondition >= bPointsAvailable / SAM_SITE_REPAIR_DIVISOR ) { // no, use up all we have StrategicMap[ sStrategicSector ].bSAMCondition += bPointsAvailable / SAM_SITE_REPAIR_DIVISOR; bPtsUsed = bPointsAvailable - ( bPointsAvailable % SAM_SITE_REPAIR_DIVISOR ); // SAM site may have been put back into working order... UpdateAirspaceControl( ); } else { // yep bPtsUsed = SAM_SITE_REPAIR_DIVISOR * ( 100 - StrategicMap[ sStrategicSector ].bSAMCondition ); StrategicMap[ sStrategicSector ].bSAMCondition = 100; //ARM: NOTE THAT IF THIS CODE IS EVER RE-ACTIVATED, THE SAM GRAPHICS SHOULD CHANGE NOT WHEN THE SAM SITE RETURNS TO // FULL STRENGTH (condition 100), but as soon as it reaches MIN_CONDITION_TO_FIX_SAM!!! // Bring Hit points back up to full, adjust graphic to full graphic..... UpdateSAMDoneRepair( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ); } if ( StrategicMap[ sStrategicSector ].bSAMCondition == 100 ) { *pfNothingLeftToRepair = TRUE; } else { *pfNothingLeftToRepair = FALSE; } return( bPtsUsed ); } */ OBJECTTYPE* FindRepairableItemOnOtherSoldier( SOLDIERTYPE * pSoldier, UINT8 ubPassType ) { INT8 bLoop, bLoop2; REPAIR_PASS_SLOTS_TYPE *pPassList; INT8 bSlotToCheck; OBJECTTYPE * pObj; AssertLT( ubPassType, NUM_REPAIR_PASS_TYPES ); AssertNotNIL(pSoldier); pPassList = &( gRepairPassSlotList[ ubPassType ] ); // CHRISL: for ( bLoop = 0; bLoop < pPassList->ubChoices[UsingNewInventorySystem()]; bLoop++ ) { bSlotToCheck = pPassList->bSlot[ bLoop ]; AssertNE( bSlotToCheck, -1 ); for ( bLoop2 = 0; bLoop2 < pSoldier->inv[ bSlotToCheck ].ubNumberOfObjects; bLoop2++ ) { pObj = FindRepairableItemInSpecificPocket(&( pSoldier->inv[ bSlotToCheck ] ), bLoop2); if(pObj != 0) { return( pObj ); } //CHRISL: In NewInv, we should also repair items stored in LBENODE items if(UsingNewInventorySystem() == true && Item[pSoldier->inv[ bSlotToCheck ].usItem].usItemClass == IC_LBEGEAR) { pObj = FindRepairableItemInLBENODE( &pSoldier->inv[ bSlotToCheck ], bLoop2); if(pObj != 0) { return( pObj ); } } } } return( 0 ); } OBJECTTYPE* FindRepairableItemInLBENODE( OBJECTTYPE * pObj, UINT8 subObject) { OBJECTTYPE * pObject; AssertNotNIL(pObj); if(UsingNewInventorySystem() == false) return( 0 ); if(pObj->IsActiveLBE(subObject) == true) { LBENODE* pLBE = pObj->GetLBEPointer(subObject); for(UINT8 lbePocket = 0; lbePocket < pLBE->inv.size(); lbePocket++) { for(UINT8 ubItemsInPocket = 0; ubItemsInPocket < pLBE->inv[lbePocket].ubNumberOfObjects; ubItemsInPocket++) { pObject = FindRepairableItemInSpecificPocket(&pLBE->inv[lbePocket], ubItemsInPocket); if(pObject != 0) { return( pObject ); } if(Item[pLBE->inv[lbePocket].usItem].usItemClass == IC_LBEGEAR) { pObject = FindRepairableItemInLBENODE(&pLBE->inv[lbePocket], ubItemsInPocket); if(pObject != 0) { return( pObject ); } } } } } return( 0 ); } OBJECTTYPE* FindRepairableItemInSpecificPocket( OBJECTTYPE * pObj, UINT8 subObject) { AssertNotNIL(pObj); if ( IsItemRepairable( pObj->usItem, (*pObj)[subObject]->data.objectStatus ) ) { return( pObj ); } // have to check for attachments after... for (attachmentList::iterator iter = (*pObj)[subObject]->attachments.begin(); iter != (*pObj)[subObject]->attachments.end(); ++iter) { // if it's repairable and NEEDS repairing if ( IsItemRepairable( iter->usItem, (*iter)[subObject]->data.objectStatus ) ) { return( &(*iter) ); } } return( 0 ); } void DoActualRepair( SOLDIERTYPE * pSoldier, UINT16 usItem, INT16 * pbStatus, UINT8 * pubRepairPtsLeft ) { INT16 sRepairCostAdj; UINT16 usDamagePts, usPtsFixed; AssertNotNIL (pSoldier); AssertNotNIL (pbStatus); AssertNotNIL (pubRepairPtsLeft); // get item's repair ease, for each + point is 10% easier, each - point is 10% harder to repair sRepairCostAdj = 100 - ( 10 * Item[ usItem ].bRepairEase ); // make sure it ain't somehow gone too low! if (sRepairCostAdj < 10) { sRepairCostAdj = 10; } // repairs on electronic items take twice as long if the guy doesn't have the skill // if ( ( Item[ usItem ].fFlags & ITEM_ELECTRONIC ) && ( !( HAS_SKILL_TRAIT( pSoldier, ELECTRONICS ) ) ) ) if ( ( Item[ usItem ].electronic ) && ( !( HAS_SKILL_TRAIT( pSoldier, ELECTRONICS ) ) ) ) { sRepairCostAdj *= 2; } // how many points of damage is the item down by? usDamagePts = 100 - *pbStatus; // adjust that by the repair cost adjustment percentage usDamagePts = (usDamagePts * sRepairCostAdj) / 100; // do we have enough pts to fully repair the item? if ( *pubRepairPtsLeft >= usDamagePts ) { // fix it to 100% *pbStatus = 100; *pubRepairPtsLeft -= usDamagePts; } else // not enough, partial fix only, if any at all { // fix what we can - add pts left adjusted by the repair cost usPtsFixed = ( *pubRepairPtsLeft * 100 ) / sRepairCostAdj; // if we have enough to actually fix anything // NOTE: a really crappy repairman with only 1 pt/hr CAN'T repair electronics or difficult items! if (usPtsFixed > 0) { *pbStatus += usPtsFixed; // make sure we don't somehow end up over 100 if ( *pbStatus > 100 ) { *pbStatus = 100; } } *pubRepairPtsLeft = 0; } } BOOLEAN RepairObject( SOLDIERTYPE * pSoldier, SOLDIERTYPE * pOwner, OBJECTTYPE * pObj, UINT8 * pubRepairPtsLeft ) { UINT8 ubLoop, ubItemsInPocket, lbeLoop; BOOLEAN fSomethingWasRepaired = FALSE; ubItemsInPocket = pObj->ubNumberOfObjects; for ( ubLoop = 0; ubLoop < ubItemsInPocket; ubLoop++ ) { // if it's repairable and NEEDS repairing if ( IsItemRepairable( pObj->usItem, (*pObj)[ubLoop]->data.objectStatus ) ) { // repairable, try to repair it DoActualRepair( pSoldier, pObj->usItem, &((*pObj)[ubLoop]->data.objectStatus), pubRepairPtsLeft ); fSomethingWasRepaired = true; if ( (*pObj)[ubLoop]->data.objectStatus == 100 ) { // report it as fixed if ( pSoldier == pOwner ) { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, Message[ STR_REPAIRED ], pSoldier->name, ItemNames[ pObj->usItem ] ); } else { // NOTE: may need to be changed for localized versions ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, gzLateLocalizedString[ 35 ], pSoldier->name, pOwner->name, ItemNames[ pObj->usItem ] ); } } if ( *pubRepairPtsLeft == 0 ) { // we're out of points! return true; } } // now check for attachments after for (attachmentList::iterator iter = (*pObj)[ubLoop]->attachments.begin(); iter != (*pObj)[ubLoop]->attachments.end(); ++iter) { if (RepairObject(pSoldier, pOwner, &(*iter), pubRepairPtsLeft)) { fSomethingWasRepaired = true; if ( *pubRepairPtsLeft == 0 ) { // we're out of points! return true; } } } //CHRISL: Now check and see if this is an LBENODE with items that need repairing if(UsingNewInventorySystem() == true && Item[pObj->usItem].usItemClass == IC_LBEGEAR && pObj->IsActiveLBE(ubLoop) == true) { LBENODE* pLBE = pObj->GetLBEPointer(ubLoop); for(lbeLoop = 0; lbeLoop < pLBE->inv.size(); lbeLoop++) { if(RepairObject(pSoldier, pOwner, &pLBE->inv[lbeLoop], pubRepairPtsLeft)) { fSomethingWasRepaired = true; if ( *pubRepairPtsLeft == 0 ) { // we're out of points! return true; } } } } } return( fSomethingWasRepaired ); } void HandleRepairBySoldier( SOLDIERTYPE *pSoldier ) { UINT16 usMax=0; UINT8 ubRepairPtsLeft =0; UINT8 ubInitialRepairPts = 0; UINT8 ubRepairPtsUsed = 0; INT8 bPocket =0; BOOLEAN fNothingLeftToRepair = FALSE; INT8 bLoop, bLoopStart, bLoopEnd; BOOLEAN fAnyOfSoldiersOwnItemsWereFixed = FALSE; OBJECTTYPE * pObj; // grab max number of repair pts open to this soldier ubRepairPtsLeft = CalculateRepairPointsForRepairman( pSoldier, &usMax, TRUE ); // no points if ( ubRepairPtsLeft == 0 ) { AssignmentDone( pSoldier, TRUE, TRUE ); return; } // remember what we've started off with ubInitialRepairPts = ubRepairPtsLeft; // check if we are repairing a vehicle if ( pSoldier->bVehicleUnderRepairID != -1 ) { if ( CanCharacterRepairVehicle( pSoldier, pSoldier->bVehicleUnderRepairID ) ) { // attempt to fix vehicle ubRepairPtsLeft -= RepairVehicle( pSoldier->bVehicleUnderRepairID, ubRepairPtsLeft, &fNothingLeftToRepair ); } } // check if we are repairing a robot else if( pSoldier->flags.fFixingRobot ) { if ( CanCharacterRepairRobot( pSoldier ) ) { // repairing the robot is very slow & difficult ubRepairPtsLeft /= 2; ubInitialRepairPts /= 2; if( !( HAS_SKILL_TRAIT( pSoldier, ELECTRONICS ) ) ) { ubRepairPtsLeft /= 2; ubInitialRepairPts /= 2; } // robot ubRepairPtsLeft -= HandleRepairOfRobotBySoldier( pSoldier, ubRepairPtsLeft, &fNothingLeftToRepair ); } } else { fAnyOfSoldiersOwnItemsWereFixed = UnjamGunsOnSoldier( pSoldier, pSoldier, &ubRepairPtsLeft ); // repair items on self for( bLoop = 0; bLoop < 2; bLoop++ ) { if ( bLoop == 0 ) { bLoopStart = SECONDHANDPOS; // CHRISL: Changed to dynamically determine max inventory locations. bLoopEnd = (NUM_INV_SLOTS - 1); } else { bLoopStart = HELMETPOS; bLoopEnd = UsingNewInventorySystem() == false ? HEAD2POS : BPACKPOCKPOS; } // now repair objects running from left hand to small pocket for( bPocket = bLoopStart; bPocket <= bLoopEnd; bPocket++ ) { //CHRISL: These two conditions allow us to repair LBE pocket items at the same time as worn armor, while // still letting us repair the item in our offhand first. if(UsingNewInventorySystem() == true && bLoop == 0 && bPocket>SECONDHANDPOS && bPocketinv[ bPocket ]); if ( RepairObject( pSoldier, pSoldier, pObj, &ubRepairPtsLeft ) ) { fAnyOfSoldiersOwnItemsWereFixed = TRUE; // quit looking if we're already out if ( ubRepairPtsLeft == 0 ) break; } } } // if he fixed something of his, and now has no more of his own items to fix if ( fAnyOfSoldiersOwnItemsWereFixed && !DoesCharacterHaveAnyItemsToRepair( pSoldier, -1 ) ) { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, sRepairsDoneString[ 0 ], pSoldier->name ); // let player react StopTimeCompression(); } // repair items on others RepairItemsOnOthers( pSoldier, &ubRepairPtsLeft ); } // what are the total amount of pts used by character? ubRepairPtsUsed = ubInitialRepairPts - ubRepairPtsLeft; if( ubRepairPtsUsed > 0 ) { // improve stats StatChange( pSoldier, MECHANAMT, ( UINT16 ) (ubRepairPtsUsed / 2), FALSE ); StatChange( pSoldier, DEXTAMT, ( UINT16 ) (ubRepairPtsUsed / 2), FALSE ); // check if kit damaged/depleted if( ( Random( 100 ) ) < (UINT32) (ubRepairPtsUsed * 5) ) // CJC: added a x5 as this wasn't going down anywhere fast enough { // kit item damaged/depleted, burn up points of toolkit..which is in right hand UseKitPoints( &( pSoldier->inv[ HANDPOS ] ), 1, pSoldier ); } } // if he really done if ( HasCharacterFinishedRepairing( pSoldier ) ) { // yup, that's all folks AssignmentDone( pSoldier, TRUE, TRUE ); } else // still has stuff to repair { // if nothing got repaired, there's a problem if ( ubRepairPtsUsed == 0 ) { // see if not having a toolkit is the problem if ( FindToolkit( pSoldier ) == NO_SLOT ) { // he could (maybe) repair something, but can't because he doesn't have a tool kit! AssignmentAborted( pSoldier, NO_MORE_TOOL_KITS ); } else { // he can't repair anything because he doesn't have enough skill! AssignmentAborted( pSoldier, INSUF_REPAIR_SKILL ); } } } return; } BOOLEAN IsItemRepairable( UINT16 usItem, INT16 bStatus ) { // check to see if item can/needs to be repaired // if ( ( bStatus < 100) && ( Item[ usItem ].fFlags & ITEM_REPAIRABLE ) ) if ( ( bStatus < 100) && ( Item[ usItem ].repairable ) ) { // yep return ( TRUE ); } // nope return ( FALSE ); } void HandleRestAndFatigueInSector( INT16 sMapX, INT16 sMapY, INT8 bMapZ ) { // this will handle all sleeping characters in this sector SOLDIERTYPE *pSoldier, *pTeamSoldier; INT32 cnt=0; pSoldier = MercPtrs[0]; // go through list of characters, find all sleepers in sector for ( pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ pSoldier->bTeam ].bLastID; cnt++,pTeamSoldier++) { if( ( pTeamSoldier->bActive ) && ( pSoldier->bAssignment != ASSIGNMENT_POW ) ) { if( ( pTeamSoldier->sSectorX == sMapX ) && ( pTeamSoldier->sSectorY == sMapY ) && ( pTeamSoldier->bSectorZ == bMapZ ) ) { } } } } /* INT8 GetRegainDueToSleepNeeded( SOLDIERTYPE *pSoldier, INT32 iRateOfReGain ) { // look at persons regain rate, // if they infact loses sleep, make sure it doesn't go below the current rate INT8 bRate = 0; UINT8 ubNeedForSleep = 0; // get profile id and then grab sleep need value ubNeedForSleep = gMercProfiles[ pSoldier->ubProfile ].ubNeedForSleep; bRate = ( AVG_NUMBER_OF_HOURS_OF_SLEEP_NEEDED - ( INT8 )ubNeedForSleep ); if( bRate >= iRateOfReGain ) { bRate = ( - iRateOfReGain ) + 1; } return( bRate ); } */ void RestCharacter( SOLDIERTYPE *pSoldier ) { // handle the sleep of this character, update bBreathMax based on sleep they have INT8 bMaxBreathRegain = 0; bMaxBreathRegain = 50 / CalcSoldierNeedForSleep( pSoldier ); // if breath max is below the "really tired" threshold if( pSoldier->bBreathMax < BREATHMAX_PRETTY_TIRED ) { // real tired, rest rate is 50% higher (this is to prevent absurdly long sleep times for totally exhausted mercs) bMaxBreathRegain = ( bMaxBreathRegain * 3 / 2 ); } pSoldier->bBreathMax += bMaxBreathRegain; if( pSoldier->bBreathMax > 100 ) { pSoldier->bBreathMax = 100; } else if( pSoldier->bBreathMax < BREATHMAX_ABSOLUTE_MINIMUM ) { pSoldier->bBreathMax = BREATHMAX_ABSOLUTE_MINIMUM; } pSoldier->bBreath = pSoldier->bBreathMax; if ( pSoldier->bBreathMax >= BREATHMAX_CANCEL_TIRED ) { pSoldier->flags.fComplainedThatTired = FALSE; } return; } void FatigueCharacter( SOLDIERTYPE *pSoldier ) { // fatigue character INT32 iPercentEncumbrance; INT32 iBreathLoss; INT8 bMaxBreathLoss = 0, bDivisor = 1; // vehicle or robot? if( ( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) || AM_A_ROBOT( pSoldier ) ) { return; } // check if in transit, do not wear out if( IsCharacterInTransit( pSoldier ) == TRUE ) { return; } // POW? if( pSoldier->bAssignment == ASSIGNMENT_POW ) { return; } bDivisor = 24 - CalcSoldierNeedForSleep( pSoldier ); bMaxBreathLoss = 50 / bDivisor; if( bMaxBreathLoss < 2 ) { bMaxBreathLoss = 2; } //KM: Added encumbrance calculation to soldiers moving on foot. Anything above 100% will increase // rate of fatigue. 200% encumbrance will cause soldiers to tire twice as quickly. if( pSoldier->flags.fBetweenSectors && pSoldier->bAssignment != VEHICLE ) { //Soldier is on foot and travelling. Factor encumbrance into fatigue rate. iPercentEncumbrance = CalculateCarriedWeight( pSoldier ); if( iPercentEncumbrance > 100 ) { iBreathLoss = (bMaxBreathLoss * iPercentEncumbrance / 100); bMaxBreathLoss = (INT8)min( 127, iBreathLoss ); } } // if breath max is below the "really tired" threshold if( pSoldier->bBreathMax < BREATHMAX_PRETTY_TIRED ) { // real tired, fatigue rate is 50% higher bMaxBreathLoss = ( bMaxBreathLoss * 3 / 2 ); } pSoldier->bBreathMax -= bMaxBreathLoss; if( pSoldier->bBreathMax > 100 ) { pSoldier->bBreathMax = 100; } else if( pSoldier->bBreathMax < BREATHMAX_ABSOLUTE_MINIMUM ) { pSoldier->bBreathMax = BREATHMAX_ABSOLUTE_MINIMUM; } // current breath can't exceed maximum if( pSoldier->bBreath > pSoldier->bBreathMax ) { pSoldier->bBreath = pSoldier->bBreathMax; } return; } // ONCE PER HOUR, will handle ALL kinds of training (self, teaching, and town) in this sector void HandleTrainingInSector( INT16 sMapX, INT16 sMapY, INT8 bZ ) { SOLDIERTYPE *pTrainer; SOLDIERTYPE *pStudent; UINT8 ubStat; BOOLEAN fAtGunRange = FALSE; UINT32 uiCnt=0; INT16 sTotalTrainingPts = 0; INT16 sTrainingPtsDueToInstructor = 0; SOLDIERTYPE *pStatTrainerList[ NUM_TRAINABLE_STATS ]; // can't have more "best" trainers than trainable stats INT16 sBestTrainingPts; INT16 sTownTrainingPts; // WDS - make number of mercenaries, etc. be configurable TOWN_TRAINER_TYPE TownTrainer[ CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ]; // std::vector TownTrainer (CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS); UINT8 ubTownTrainers; UINT16 usMaxPts; BOOLEAN fSamSiteInSector = FALSE; BOOLEAN fTrainingCompleted = FALSE; // find out if a sam site here fSamSiteInSector = IsThisSectorASAMSector( sMapX, sMapY, 0 ); // Training in underground sectors is disallowed by the interface code, so there should never be any if (bZ != 0) { return; } // if sector not under our control, has enemies in it, or is currently in combat mode if (!SectorOursAndPeaceful( sMapX, sMapY, bZ )) { // then training is canceled for this hour. // This is partly logical, but largely to prevent newly trained militia from appearing in mid-battle return; } // are we training in the sector with gun range in Alma? if ( (sMapX == GUN_RANGE_X) && (sMapY == GUN_RANGE_Y) && (bZ == GUN_RANGE_Z) ) { fAtGunRange = TRUE; } // init trainer list memset( pStatTrainerList, 0, sizeof( pStatTrainerList ) ); // build list of teammate trainers in this sector. // Only the trainer with the HIGHEST training ability in each stat is effective. This is mainly to avoid having to // sort them from highest to lowest if some form of trainer degradation formula was to be used for multiple trainers. // for each trainable stat for (ubStat = 0; ubStat < NUM_TRAINABLE_STATS; ubStat++) { sBestTrainingPts = -1; // search team for active instructors in this sector for ( uiCnt = 0, pTrainer = MercPtrs[ uiCnt ]; uiCnt <= gTacticalStatus.Team[ MercPtrs[0]->bTeam ].bLastID; uiCnt++, pTrainer++) { if( pTrainer->bActive && ( pTrainer->sSectorX == sMapX ) && ( pTrainer->sSectorY == sMapY ) && ( pTrainer->bSectorZ == bZ) ) { // if he's training teammates in this stat if( ( pTrainer->bAssignment == TRAIN_TEAMMATE ) && ( pTrainer->bTrainStat == ubStat) && ( EnoughTimeOnAssignment( pTrainer ) ) && ( pTrainer->flags.fMercAsleep == FALSE ) ) { sTrainingPtsDueToInstructor = GetBonusTrainingPtsDueToInstructor( pTrainer, NULL, ubStat, fAtGunRange, &usMaxPts ); // if he's the best trainer so far for this stat if (sTrainingPtsDueToInstructor > sBestTrainingPts) { // then remember him as that, and the points he scored pStatTrainerList[ ubStat ] = pTrainer; sBestTrainingPts = sTrainingPtsDueToInstructor; } } } } } // now search team for active self-trainers in this sector for ( uiCnt = 0, pStudent = MercPtrs[ uiCnt ]; uiCnt <= gTacticalStatus.Team[ MercPtrs[0]->bTeam ].bLastID; uiCnt++, pStudent++) { // see if this merc is active and in the same sector if( ( pStudent->bActive) && ( pStudent->sSectorX == sMapX ) && ( pStudent->sSectorY == sMapY ) && ( pStudent->bSectorZ == bZ ) ) { // if he's training himself (alone, or by others), then he's a student if ( ( pStudent->bAssignment == TRAIN_SELF ) || ( pStudent->bAssignment == TRAIN_BY_OTHER ) ) { if ( EnoughTimeOnAssignment( pStudent ) && ( pStudent->flags.fMercAsleep == FALSE ) ) { // figure out how much the grunt can learn in one training period sTotalTrainingPts = GetSoldierTrainingPts( pStudent, pStudent->bTrainStat, fAtGunRange, &usMaxPts ); // if he's getting help if ( pStudent->bAssignment == TRAIN_BY_OTHER ) { // grab the pointer to the (potential) trainer for this stat pTrainer = pStatTrainerList[ pStudent->bTrainStat ]; // if this stat HAS a trainer in sector at all if (pTrainer != NULL) { /* Assignment distance limits removed. Sep/11/98. ARM // if this sector either ISN'T currently loaded, or it is but the trainer is close enough to the student if ( ( sMapX != gWorldSectorX ) || ( sMapY != gWorldSectorY ) || ( pStudent->bSectorZ != gbWorldSectorZ ) || ( PythSpacesAway( pStudent->sGridNo, pTrainer->sGridNo ) < MAX_DISTANCE_FOR_TRAINING ) && ( EnoughTimeOnAssignment( pTrainer ) ) ) */ // NB this EnoughTimeOnAssignment() call is redundent since it is called up above //if ( EnoughTimeOnAssignment( pTrainer ) ) { // valid trainer is available, this gives the student a large training bonus! sTrainingPtsDueToInstructor = GetBonusTrainingPtsDueToInstructor( pTrainer, pStudent, pStudent->bTrainStat, fAtGunRange, &usMaxPts ); StatChange(pTrainer,LDRAMT,sTrainingPtsDueToInstructor,FALSE); StatChange(pTrainer,WISDOMAMT,sTrainingPtsDueToInstructor,FALSE); // add the bonus to what merc can learn on his own sTotalTrainingPts += sTrainingPtsDueToInstructor; } } } // now finally train the grunt TrainSoldierWithPts( pStudent, sTotalTrainingPts ); } } } } // check if we're doing a sector where militia can be trained if( ( (StrategicMap[ sMapX + ( sMapY * MAP_WORLD_X ) ].bNameId != BLANK_SECTOR ) || ( fSamSiteInSector == TRUE ) ) && (bZ == 0) ) { // init town trainer list memset( TownTrainer, 0, sizeof( TownTrainer ) ); ubTownTrainers = 0; // build list of all the town trainers in this sector and their training pts for ( uiCnt = 0, pTrainer = MercPtrs[ uiCnt ]; uiCnt <= gTacticalStatus.Team[ MercPtrs[0]->bTeam ].bLastID; uiCnt++,pTrainer++) { if( pTrainer->bActive && ( pTrainer->sSectorX == sMapX ) && ( pTrainer->sSectorY == sMapY ) && ( pTrainer->bSectorZ == bZ ) ) { if( ( pTrainer->bAssignment == TRAIN_TOWN ) && ( EnoughTimeOnAssignment( pTrainer ) ) && ( pTrainer->flags.fMercAsleep == FALSE ) ) { sTownTrainingPts = GetTownTrainPtsForCharacter( pTrainer, &usMaxPts ); // if he's actually worth anything if( sTownTrainingPts > 0 ) { // remember this guy as a town trainer TownTrainer[ubTownTrainers].sTrainingPts = sTownTrainingPts; TownTrainer[ubTownTrainers].pSoldier = pTrainer; ubTownTrainers++; } } } } // if we have more than one if (ubTownTrainers > 1) { // sort the town trainer list from best trainer to worst qsort( TownTrainer, ubTownTrainers, sizeof(TOWN_TRAINER_TYPE), TownTrainerQsortCompare); } // for each trainer, in sorted order from the best to the worst for (uiCnt = 0; uiCnt < ubTownTrainers; uiCnt++) { // top trainer has full effect (divide by 1), then divide by 2, 4, 8, etc. //sTownTrainingPts = TownTrainer[ uiCnt ].sTrainingPts / (UINT16) pow(2, uiCnt); // CJC: took this out and replaced with limit of 2 guys per sector sTownTrainingPts = TownTrainer[ uiCnt ].sTrainingPts; if (sTownTrainingPts > 0) { fTrainingCompleted = TrainTownInSector( TownTrainer[ uiCnt ].pSoldier, sMapX, sMapY, sTownTrainingPts ); if ( fTrainingCompleted ) { // there's no carryover into next session for extra training (cause player might cancel), so break out of loop break; } } } } } int TownTrainerQsortCompare(const void *pArg1, const void *pArg2) { if (((TOWN_TRAINER_TYPE *)pArg1)->sTrainingPts > ((TOWN_TRAINER_TYPE *)pArg2)->sTrainingPts) { return(-1); } else if (((TOWN_TRAINER_TYPE *)pArg1)->sTrainingPts < ((TOWN_TRAINER_TYPE *)pArg2)->sTrainingPts) { return(1); } else { return(0); } } INT16 GetBonusTrainingPtsDueToInstructor( SOLDIERTYPE *pInstructor, SOLDIERTYPE *pStudent, INT8 bTrainStat, BOOLEAN fAtGunRange, UINT16 *pusMaxPts ) { // return the bonus training pts of this instructor with this student,...if student null, simply assignment student skill of 0 and student wisdom of 100 INT16 sTrainingPts = 0; INT8 bTraineeEffWisdom = 0; INT8 bTraineeNatWisdom = 0; INT8 bTraineeSkill = 0; INT8 bTrainerEffSkill = 0; INT8 bTrainerNatSkill = 0; INT16 bTrainingBonus = 0; INT8 bOpinionFactor; // assume training impossible for max pts *pusMaxPts = 0; if( pInstructor == NULL ) { // no instructor, leave return ( 0 ); } switch( bTrainStat ) { case( STRENGTH ): bTrainerEffSkill = EffectiveStrength ( pInstructor ); bTrainerNatSkill = pInstructor->stats.bStrength; break; case( DEXTERITY ): bTrainerEffSkill = EffectiveDexterity ( pInstructor ); bTrainerNatSkill = pInstructor->stats.bDexterity; break; case( AGILITY ): bTrainerEffSkill = EffectiveAgility( pInstructor ); bTrainerNatSkill = pInstructor->stats.bAgility; break; case( HEALTH ): bTrainerEffSkill = pInstructor->stats.bLifeMax; bTrainerNatSkill = pInstructor->stats.bLifeMax; break; case( LEADERSHIP ): bTrainerEffSkill = EffectiveLeadership( pInstructor ); bTrainerNatSkill = pInstructor->stats.bLeadership; break; case( MARKSMANSHIP ): bTrainerEffSkill = EffectiveMarksmanship( pInstructor ); bTrainerNatSkill = pInstructor->stats.bMarksmanship; break; case( EXPLOSIVE_ASSIGN ): bTrainerEffSkill = EffectiveExplosive( pInstructor ); bTrainerNatSkill = pInstructor->stats.bExplosive; break; case( MEDICAL ): bTrainerEffSkill = EffectiveMedical( pInstructor ); bTrainerNatSkill = pInstructor->stats.bMedical; break; case( MECHANICAL ): bTrainerEffSkill = EffectiveMechanical( pInstructor ); bTrainerNatSkill = pInstructor->stats.bMechanical; break; // NOTE: Wisdom can't be trained! default: // BETA message #ifdef JA2BETAVERSION ScreenMsg( FONT_ORANGE, MSG_BETAVERSION, L"GetBonusTrainingPtsDueToInstructor: ERROR - Unknown bTrainStat %d", bTrainStat); #endif return(0); } // if there's no student if( pStudent == NULL ) { // assume these default values bTraineeEffWisdom = 100; bTraineeNatWisdom = 100; bTraineeSkill = 0; bOpinionFactor = 0; } else { // set student's variables bTraineeEffWisdom = EffectiveWisdom ( pStudent ); bTraineeNatWisdom = pStudent->stats.bWisdom; // for trainee's stat skill, must use the natural value, not the effective one, to avoid drunks training beyond cap switch( bTrainStat ) { case( STRENGTH ): bTraineeSkill = pStudent->stats.bStrength; break; case( DEXTERITY ): bTraineeSkill = pStudent->stats.bDexterity; break; case( AGILITY ): bTraineeSkill = pStudent->stats.bAgility; break; case( HEALTH ): bTraineeSkill = pStudent->stats.bLifeMax; break; case( LEADERSHIP ): bTraineeSkill = pStudent->stats.bLeadership; break; case( MARKSMANSHIP ): bTraineeSkill = pStudent->stats.bMarksmanship; break; case( EXPLOSIVE_ASSIGN ): bTraineeSkill = pStudent->stats.bExplosive; break; case( MEDICAL ): bTraineeSkill = pStudent->stats.bMedical; break; case( MECHANICAL ): bTraineeSkill = pStudent->stats.bMechanical; break; // NOTE: Wisdom can't be trained! default: // BETA message #ifdef JA2BETAVERSION ScreenMsg( FONT_ORANGE, MSG_BETAVERSION, L"GetBonusTrainingPtsDueToInstructor: ERROR - Unknown bTrainStat %d", bTrainStat); #endif return(0); } // if trainee skill 0 or at/beyond the training cap, can't train //Orig: if ( ( bTraineeSkill == 0 ) || ( bTraineeSkill >= TRAINING_RATING_CAP ) ) // Madd if ( bTraineeSkill < gGameExternalOptions.ubTrainingSkillMin || bTraineeSkill >= gGameExternalOptions.ubTrainingSkillMax ) { return 0; } // factor in their mutual relationship bOpinionFactor = gMercProfiles[ pStudent->ubProfile ].bMercOpinion[ pInstructor->ubProfile ]; bOpinionFactor += gMercProfiles[ pInstructor->ubProfile ].bMercOpinion[ pStudent->ubProfile ] / 2; } // check to see if student better than/equal to instructor's effective skill, if so, return 0 // don't use natural skill - if the guy's too doped up to tell what he know, student learns nothing until sobriety returns! if( bTraineeSkill >= bTrainerEffSkill ) { return ( 0 ); } // calculate effective training pts sTrainingPts = ( bTrainerEffSkill - bTraineeSkill ) * ( bTraineeEffWisdom + ( EffectiveWisdom( pInstructor ) + EffectiveLeadership( pInstructor ) ) / 2 ) / gGameExternalOptions.ubInstructedTrainingDivisor; // calculate normal training pts - what it would be if his stats were "normal" (ignoring drugs, fatigue) *pusMaxPts = ( bTrainerNatSkill - bTraineeSkill ) * ( bTraineeNatWisdom + ( pInstructor->stats.bWisdom + pInstructor->stats.bLeadership ) / 2 ) / gGameExternalOptions.ubInstructedTrainingDivisor; // put in a minimum (that can be reduced due to instructor being tired?) if (*pusMaxPts == 0) { // we know trainer is better than trainee, make sure they are at least 10 pts better if ( bTrainerEffSkill > bTraineeSkill + 10 ) { sTrainingPts = 1; *pusMaxPts = 1; } } // check for teaching skill bonuses if( gMercProfiles[ pInstructor->ubProfile ].bSkillTrait == TEACHING ) { bTrainingBonus += gGameExternalOptions.ubTeachBonusToTrain; } if( gMercProfiles[ pInstructor->ubProfile ].bSkillTrait2 == TEACHING ) { bTrainingBonus += gGameExternalOptions.ubTeachBonusToTrain; } // teaching bonus is counted as normal, but gun range bonus is not *pusMaxPts += ( ( ( bTrainingBonus + bOpinionFactor ) * *pusMaxPts ) / 100 ); // get special bonus if we're training marksmanship and we're in the gun range sector in Alma if ( ( bTrainStat == MARKSMANSHIP ) && fAtGunRange ) { bTrainingBonus += gGameExternalOptions.ubGunRangeTrainingBonus; } // adjust for any training bonuses and for the relationship sTrainingPts += ( ( ( bTrainingBonus + bOpinionFactor ) * sTrainingPts ) / 100 ); // adjust for instructor fatigue ReducePointsForFatigue( pInstructor, (UINT16 *)&sTrainingPts ); return( sTrainingPts ); } INT16 GetSoldierTrainingPts( SOLDIERTYPE *pSoldier, INT8 bTrainStat, BOOLEAN fAtGunRange, UINT16 *pusMaxPts ) { INT16 sTrainingPts = 0; INT16 bTrainingBonus = 0; INT8 bSkill = 0; // assume training impossible for max pts *pusMaxPts = 0; // use NATURAL not EFFECTIVE values here switch( bTrainStat ) { case( STRENGTH ): bSkill = pSoldier->stats.bStrength; break; case( DEXTERITY ): bSkill = pSoldier->stats.bDexterity; break; case( AGILITY ): bSkill = pSoldier->stats.bAgility; break; case( HEALTH ): bSkill = pSoldier->stats.bLifeMax; break; case( LEADERSHIP ): bSkill = pSoldier->stats.bLeadership; break; case( MARKSMANSHIP ): bSkill = pSoldier->stats.bMarksmanship; break; case( EXPLOSIVE_ASSIGN ): bSkill = pSoldier->stats.bExplosive; break; case( MEDICAL ): bSkill = pSoldier->stats.bMedical; break; case( MECHANICAL ): bSkill = pSoldier->stats.bMechanical; break; // NOTE: Wisdom can't be trained! default: // BETA message #ifdef JA2BETAVERSION ScreenMsg( FONT_ORANGE, MSG_BETAVERSION, L"GetSoldierTrainingPts: ERROR - Unknown bTrainStat %d", bTrainStat); #endif return(0); } // if skill 0 or at/beyond the training cap, can't train if ( ( bSkill < gGameExternalOptions.ubTrainingSkillMin) || ( bSkill >= gGameExternalOptions.ubTrainingSkillMax ) ) { return 0; } // calculate normal training pts - what it would be if his stats were "normal" (ignoring drugs, fatigue) *pusMaxPts = __max( ( ( pSoldier->stats.bWisdom * ( gGameExternalOptions.ubTrainingSkillMax - bSkill ) ) / gGameExternalOptions.ubSelfTrainingDivisor ), 1 ); // calculate effective training pts sTrainingPts = __max( ( ( EffectiveWisdom( pSoldier ) * ( gGameExternalOptions.ubTrainingSkillMax - bSkill ) ) / gGameExternalOptions.ubSelfTrainingDivisor ), 1 ); // get special bonus if we're training marksmanship and we're in the gun range sector in Alma if ( ( bTrainStat == MARKSMANSHIP ) && fAtGunRange ) { bTrainingBonus += gGameExternalOptions.ubGunRangeTrainingBonus; } // adjust for any training bonuses sTrainingPts += ( ( bTrainingBonus * sTrainingPts ) / 100 ); // adjust for fatigue ReducePointsForFatigue( pSoldier, (UINT16 *)&sTrainingPts ); return( sTrainingPts ); } INT16 GetSoldierStudentPts( SOLDIERTYPE *pSoldier, INT8 bTrainStat, BOOLEAN fAtGunRange, UINT16 *pusMaxPts ) { INT16 sTrainingPts = 0; INT16 bTrainingBonus = 0; INT8 bSkill = 0; INT16 sBestTrainingPts, sTrainingPtsDueToInstructor; UINT16 usMaxTrainerPts, usBestMaxTrainerPts = 0; UINT32 uiCnt; SOLDIERTYPE * pTrainer; // assume training impossible for max pts *pusMaxPts = 0; // use NATURAL not EFFECTIVE values here switch( bTrainStat ) { case( STRENGTH ): bSkill = pSoldier->stats.bStrength; break; case( DEXTERITY ): bSkill = pSoldier->stats.bDexterity; break; case( AGILITY ): bSkill = pSoldier->stats.bAgility; break; case( HEALTH ): bSkill = pSoldier->stats.bLifeMax; break; case( LEADERSHIP ): bSkill = pSoldier->stats.bLeadership; break; case( MARKSMANSHIP ): bSkill = pSoldier->stats.bMarksmanship; break; case( EXPLOSIVE_ASSIGN ): bSkill = pSoldier->stats.bExplosive; break; case( MEDICAL ): bSkill = pSoldier->stats.bMedical; break; case( MECHANICAL ): bSkill = pSoldier->stats.bMechanical; break; // NOTE: Wisdom can't be trained! default: // BETA message #ifdef JA2BETAVERSION ScreenMsg( FONT_ORANGE, MSG_BETAVERSION, L"GetSoldierTrainingPts: ERROR - Unknown bTrainStat %d", bTrainStat); #endif return(0); } // if skill 0 or at/beyond the training cap, can't train if ( bSkill < gGameExternalOptions.ubTrainingSkillMin || bSkill >= gGameExternalOptions.ubTrainingSkillMax ) { return 0; } // calculate normal training pts - what it would be if his stats were "normal" (ignoring drugs, fatigue) *pusMaxPts = __max( ( ( pSoldier->stats.bWisdom * ( gGameExternalOptions.ubTrainingSkillMax - bSkill ) ) / gGameExternalOptions.ubSelfTrainingDivisor ), 1 ); // calculate effective training pts sTrainingPts = __max( ( ( EffectiveWisdom( pSoldier ) * ( gGameExternalOptions.ubTrainingSkillMax - bSkill ) ) / gGameExternalOptions.ubSelfTrainingDivisor ), 1 ); // get special bonus if we're training marksmanship and we're in the gun range sector in Alma if ( ( bTrainStat == MARKSMANSHIP ) && fAtGunRange ) { bTrainingBonus += gGameExternalOptions.ubGunRangeTrainingBonus; } // adjust for any training bonuses sTrainingPts += ( ( bTrainingBonus * sTrainingPts ) / 100 ); // adjust for fatigue ReducePointsForFatigue( pSoldier, (UINT16 *)&sTrainingPts ); // now add in stuff for trainer // for each trainable stat sBestTrainingPts = -1; // search team for active instructors in this sector for ( uiCnt = 0, pTrainer = MercPtrs[ uiCnt ]; uiCnt <= gTacticalStatus.Team[ MercPtrs[0]->bTeam ].bLastID; uiCnt++, pTrainer++) { if( pTrainer->bActive && ( pTrainer->sSectorX == pSoldier->sSectorX ) && ( pTrainer->sSectorY == pSoldier->sSectorY ) && ( pTrainer->bSectorZ == pSoldier->bSectorZ) ) { // if he's training teammates in this stat // NB skip the EnoughTime requirement to display what the value should be even if haven't been training long yet... if ( ( pTrainer->bAssignment == TRAIN_TEAMMATE ) && ( pTrainer->bTrainStat == bTrainStat) && ( pTrainer->flags.fMercAsleep == FALSE ) ) { sTrainingPtsDueToInstructor = GetBonusTrainingPtsDueToInstructor( pTrainer, pSoldier, bTrainStat, fAtGunRange, &usMaxTrainerPts ); // if he's the best trainer so far for this stat if (sTrainingPtsDueToInstructor > sBestTrainingPts) { // then remember him as that, and the points he scored sBestTrainingPts = sTrainingPtsDueToInstructor; usBestMaxTrainerPts = usMaxTrainerPts; } } } } if ( sBestTrainingPts != -1 ) { // add the bonus to what merc can learn on his own sTrainingPts += sBestTrainingPts; *pusMaxPts += usBestMaxTrainerPts; } return( sTrainingPts ); } void TrainSoldierWithPts( SOLDIERTYPE *pSoldier, INT16 sTrainPts ) { UINT8 ubChangeStat = 0; if( sTrainPts <= 0 ) { return; } BOOLEAN addWis = FALSE; // which stat to modify? switch( pSoldier->bTrainStat ) { case( STRENGTH ): ubChangeStat = STRAMT; break; case( DEXTERITY ): ubChangeStat = DEXTAMT; break; case( AGILITY ): ubChangeStat = AGILAMT; break; case( HEALTH ): ubChangeStat = HEALTHAMT; break; case( LEADERSHIP ): ubChangeStat = LDRAMT; addWis = TRUE; break; case( MARKSMANSHIP ): ubChangeStat = MARKAMT; addWis = TRUE; break; case( EXPLOSIVE_ASSIGN ): ubChangeStat = EXPLODEAMT; addWis = TRUE; break; case( MEDICAL ): ubChangeStat = MEDICALAMT; addWis = TRUE; break; case( MECHANICAL ): ubChangeStat = MECHANAMT; addWis = TRUE; break; // NOTE: Wisdom can't be trained! default: // BETA message #ifdef JA2BETAVERSION ScreenMsg( FONT_ORANGE, MSG_BETAVERSION, L"TrainSoldierWithPts: ERROR - Unknown bTrainStat %d", pSoldier->bTrainStat); #endif return; } // give this merc a few chances to increase a stat (TRUE means it's training, reverse evolution doesn't apply) StatChange( pSoldier, ubChangeStat, sTrainPts, FROM_TRAINING ); if (addWis) StatChange( pSoldier, WISDOMAMT, sTrainPts/2, FROM_TRAINING ); } // will train a town in sector by character BOOLEAN TrainTownInSector( SOLDIERTYPE *pTrainer, INT16 sMapX, INT16 sMapY, INT16 sTrainingPts ) { SECTORINFO *pSectorInfo = &( SectorInfo[ SECTOR( sMapX, sMapY ) ] ); UINT8 ubTownId = 0; BOOLEAN fSamSiteInSector = FALSE; // find out if a sam site here fSamSiteInSector = IsThisSectorASAMSector( sMapX, sMapY, 0 ); // get town index ubTownId = StrategicMap[ pTrainer->sSectorX + pTrainer->sSectorY * MAP_WORLD_X ].bNameId; if( fSamSiteInSector == FALSE ) { AssertNE(ubTownId, BLANK_SECTOR); } // trainer gains leadership - training argument is FALSE, because the trainer is not the one training! StatChange( pTrainer, LDRAMT, (UINT16) ( 1 + ( sTrainingPts / 200 ) ), FALSE ); StatChange( pTrainer, WISDOMAMT, (UINT16) ( 1 + ( sTrainingPts / 400 ) ), FALSE ); // increase town's training completed percentage pSectorInfo->ubMilitiaTrainingPercentDone += (sTrainingPts / 100); pSectorInfo->ubMilitiaTrainingHundredths += (sTrainingPts % 100); if (pSectorInfo->ubMilitiaTrainingHundredths >= 100) { pSectorInfo->ubMilitiaTrainingPercentDone++; pSectorInfo->ubMilitiaTrainingHundredths -= 100; } // NOTE: Leave this at 100, change TOWN_TRAINING_RATE if necessary. This value gets reported to player as a %age! if( pSectorInfo->ubMilitiaTrainingPercentDone >= 100 ) { // zero out training completion - there's no carryover to the next training session pSectorInfo->ubMilitiaTrainingPercentDone = 0; pSectorInfo->ubMilitiaTrainingHundredths = 0; // make the player pay again next time he wants to train here pSectorInfo->fMilitiaTrainingPaid = FALSE; TownMilitiaTrainingCompleted( pTrainer, sMapX, sMapY ); // training done return( TRUE ); } else { // not done return ( FALSE ); } } INT16 GetTownTrainPtsForCharacter( SOLDIERTYPE *pTrainer, UINT16 *pusMaxPts ) { INT16 sTotalTrainingPts = 0; INT16 bTrainingBonus = 0; // UINT8 ubTownId = 0; // calculate normal training pts - what it would be if his stats were "normal" (ignoring drugs, fatigue) *pusMaxPts = ( pTrainer->stats.bWisdom + pTrainer->stats.bLeadership + ( 10 * pTrainer->stats.bExpLevel ) ) * gGameExternalOptions.ubTownMilitiaTrainingRate; // calculate effective training points (this is hundredths of pts / hour) // typical: 300/hr, maximum: 600/hr sTotalTrainingPts = ( EffectiveWisdom( pTrainer ) + EffectiveLeadership ( pTrainer ) + ( 10 * EffectiveExpLevel ( pTrainer ) ) ) * gGameExternalOptions.ubTownMilitiaTrainingRate; // check for teaching bonuses if( gMercProfiles[ pTrainer->ubProfile ].bSkillTrait == TEACHING ) { bTrainingBonus += gGameExternalOptions.ubTeachBonusToTrain; } if( gMercProfiles[ pTrainer->ubProfile ].bSkillTrait2 == TEACHING ) { bTrainingBonus += gGameExternalOptions.ubTeachBonusToTrain; } // RPCs get a small training bonus for being more familiar with the locals and their customs/needs than outsiders if( pTrainer->ubProfile >= FIRST_RPC ) { bTrainingBonus += gGameExternalOptions.ubRpcBonusToTrainMilitia; } // adjust for teaching bonus (a percentage) sTotalTrainingPts += ( ( bTrainingBonus * sTotalTrainingPts ) / 100 ); // teach bonus is considered "normal" - it's always there *pusMaxPts += ( ( bTrainingBonus * *pusMaxPts ) / 100 ); // adjust for fatigue of trainer ReducePointsForFatigue( pTrainer, (UINT16 *)&sTotalTrainingPts ); /* ARM: Decided this didn't make much sense - the guys I'm training damn well BETTER be loyal - and screw the rest! // get town index ubTownId = StrategicMap[ pTrainer->sSectorX + pTrainer->sSectorY * MAP_WORLD_X ].bNameId; AssertNE(ubTownId, BLANK_SECTOR); // adjust for town loyalty sTotalTrainingPts = (sTotalTrainingPts * gTownLoyalty[ ubTownId ].ubRating) / 100; */ return( sTotalTrainingPts ); } void MakeSoldiersTacticalAnimationReflectAssignment( SOLDIERTYPE *pSoldier ) { // soldier is in tactical, world loaded, he's OKLIFE if( ( pSoldier->bInSector ) && gfWorldLoaded && ( pSoldier->stats.bLife >= OKLIFE ) ) { // Set animation based on his assignment if ( pSoldier->bAssignment == DOCTOR ) { SoldierInSectorDoctor( pSoldier, pSoldier->usStrategicInsertionData ); } else if ( pSoldier->bAssignment == PATIENT ) { SoldierInSectorPatient( pSoldier, pSoldier->usStrategicInsertionData ); } else if ( pSoldier->bAssignment == REPAIR ) { SoldierInSectorRepair( pSoldier, pSoldier->usStrategicInsertionData ); } else { if ( pSoldier->usAnimState != WKAEUP_FROM_SLEEP && !(pSoldier->bOldAssignment < ON_DUTY ) ) { // default: standing pSoldier->ChangeSoldierState( STANDING, 1, TRUE ); } } } } void AssignmentAborted( SOLDIERTYPE *pSoldier, UINT8 ubReason ) { AssertLT( ubReason, NUM_ASSIGN_ABORT_REASONS ); ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, gzLateLocalizedString[ ubReason ], pSoldier->name ); StopTimeCompression(); // update mapscreen fCharacterInfoPanelDirty = TRUE; fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; } void AssignmentDone( SOLDIERTYPE *pSoldier, BOOLEAN fSayQuote, BOOLEAN fMeToo ) { if ( ( pSoldier->bInSector ) && ( gfWorldLoaded ) ) { if ( pSoldier->bAssignment == DOCTOR ) { if ( guiCurrentScreen == GAME_SCREEN ) { pSoldier->ChangeSoldierState( END_DOCTOR, 1, TRUE ); } else { pSoldier->ChangeSoldierState( STANDING, 1, TRUE ); } } else if ( pSoldier->bAssignment == REPAIR ) { if ( guiCurrentScreen == GAME_SCREEN ) { pSoldier->ChangeSoldierState( END_REPAIRMAN, 1, TRUE ); } else { pSoldier->ChangeSoldierState( STANDING, 1, TRUE ); } } else if ( pSoldier->bAssignment == PATIENT ) { if ( guiCurrentScreen == GAME_SCREEN ) { pSoldier->ChangeSoldierStance( ANIM_CROUCH ); } else { pSoldier->ChangeSoldierState( STANDING, 1, TRUE ); } } } if ( pSoldier->bAssignment == ASSIGNMENT_HOSPITAL ) { // hack - reset AbsoluteFinalDestination in case it was left non-nowhere pSoldier->sAbsoluteFinalDestination = NOWHERE; } if ( fSayQuote ) { if ( ( fMeToo == FALSE ) && (pSoldier->bAssignment == TRAIN_TOWN ) ) { TacticalCharacterDialogue( pSoldier, QUOTE_ASSIGNMENT_COMPLETE ); if( pSoldier->bAssignment == TRAIN_TOWN ) { AddSectorForSoldierToListOfSectorsThatCompletedMilitiaTraining( pSoldier ); } } } // don't bother telling us again about guys we already know about if ( !( pSoldier->usQuoteSaidExtFlags & SOLDIER_QUOTE_SAID_DONE_ASSIGNMENT ) ) { pSoldier->usQuoteSaidExtFlags |= SOLDIER_QUOTE_SAID_DONE_ASSIGNMENT; if ( fSayQuote ) { if ( pSoldier->bAssignment == DOCTOR || pSoldier->bAssignment == REPAIR || pSoldier->bAssignment == PATIENT || pSoldier->bAssignment == ASSIGNMENT_HOSPITAL ) { TacticalCharacterDialogue( pSoldier, QUOTE_ASSIGNMENT_COMPLETE ); } } AddReasonToWaitingListQueue( ASSIGNMENT_FINISHED_FOR_UPDATE ); AddSoldierToWaitingListQueue( pSoldier ); // trigger a single call AddDisplayBoxToWaitingQueue for assignments done gfAddDisplayBoxToWaitingQueue = TRUE; } // update mapscreen fCharacterInfoPanelDirty = TRUE; fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; } BOOLEAN CharacterIsBetweenSectors( SOLDIERTYPE *pSoldier ) { // is the character on the move if( pSoldier == NULL ) { return ( FALSE ); } else { return( pSoldier->flags.fBetweenSectors ); } } void HandleNaturalHealing( void ) { SOLDIERTYPE *pSoldier, *pTeamSoldier; INT32 cnt=0; // set psoldier as first in merc ptrs pSoldier = MercPtrs[0]; // go through list of characters, find all who are on this assignment for ( pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ pSoldier->bTeam ].bLastID; cnt++,pTeamSoldier++) { if( pTeamSoldier->bActive ) { // mechanical members don't regenerate! if( !( pTeamSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) && !( AM_A_ROBOT( pTeamSoldier ) ) ) { HandleHealingByNaturalCauses( pTeamSoldier ); } } } return; } // handle healing of this soldier by natural causes. void HandleHealingByNaturalCauses( SOLDIERTYPE *pSoldier ) { UINT32 uiPercentHealth = 0; INT8 bActivityLevelDivisor = 0; // check if soldier valid if( pSoldier == NULL ) { return; } // dead if( pSoldier->stats.bLife == 0 ) { return; } // lost any pts? if( pSoldier->stats.bLife == pSoldier->stats.bLifeMax ) { return; } // any bleeding pts - can' recover if still bleeding! if( pSoldier->bBleeding != 0 ) { return; } // not bleeding and injured... if( pSoldier->bAssignment == ASSIGNMENT_POW ) { // use high activity level to simulate stress, torture, poor conditions for healing bActivityLevelDivisor = gGameExternalOptions.ubHighActivityLevel; } if( ( pSoldier->flags.fMercAsleep == TRUE ) || ( pSoldier->bAssignment == PATIENT ) || ( pSoldier->bAssignment == ASSIGNMENT_HOSPITAL ) ) { bActivityLevelDivisor = gGameExternalOptions.ubLowActivityLevel; } else if ( pSoldier->bAssignment < ON_DUTY ) { // if time is being compressed, and the soldier is not moving strategically if ( IsTimeBeingCompressed() && !PlayerIDGroupInMotion( pSoldier->ubGroupID ) ) { // basically resting bActivityLevelDivisor = gGameExternalOptions.ubLowActivityLevel; } else { // either they're on the move, or they're being tactically active bActivityLevelDivisor = gGameExternalOptions.ubHighActivityLevel; } } else // this includes being in a vehicle - that's neither very strenous, nor very restful { bActivityLevelDivisor = gGameExternalOptions.ubMediumActivityLevel; } // what percentage of health is he down to uiPercentHealth = ( pSoldier->stats.bLife * 100 ) / pSoldier->stats.bLifeMax; // gain that many hundredths of life points back, divided by the activity level modifier pSoldier->sFractLife += ( INT16 ) ( uiPercentHealth / bActivityLevelDivisor ); // now update the real life values UpDateSoldierLife( pSoldier ); return; } void UpDateSoldierLife( SOLDIERTYPE *pSoldier ) { // update soldier life, make sure we don't go out of bounds pSoldier->stats.bLife += pSoldier->sFractLife / 100; // keep remaining fract of life pSoldier->sFractLife %= 100; // check if we have gone too far if( pSoldier->stats.bLife >= pSoldier->stats.bLifeMax ) { // reduce pSoldier->stats.bLife = pSoldier->stats.bLifeMax; pSoldier->sFractLife = 0; } return; } /* // merc is tired, put to sleep BOOLEAN AutoSleepMerc( SOLDIERTYPE *pSoldier ) { if( pSoldier == NULL ) { return ( FALSE ); } // already asleep if( pSoldier->flags.fMercAsleep == TRUE ) { return ( FALSE ); } if( pSoldier->bBreathMax > MIN_BREATH_TO_STAY_AWAKE ) { if( ( pSoldier->bAssignment < ON_DUTY ) ) { return ( FALSE ); } if( pSoldier->stats.bLife < OKLIFE ) { // can't sleep return ( FALSE ); } // if was forced to stay awake, leave if( pSoldier->fForcedToStayAwake == TRUE ) { return( FALSE ); } } else { // ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s", pMercFellAsleepString[ 0 ], pSoldier->name ); } // store old assignment pSoldier->bOldAssignment = pSoldier->bAssignment; if( pSoldier->bAssignment < ON_DUTY ) { RemoveCharacterFromASquad( pSoldier, pSoldier->bAssignment ); } if( SetMercAsleep( pSoldier, FALSE ) ) { // change soldier state SoldierInSectorSleep( pSoldier, pSoldier->usStrategicInsertionData ); // update mapscreen fCharacterInfoPanelDirty = TRUE; fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; return( TRUE ); } else { return( FALSE ); } } */ void CheckIfSoldierUnassigned( SOLDIERTYPE *pSoldier ) { if( pSoldier->bAssignment == NO_ASSIGNMENT ) { // unassigned AddCharacterToAnySquad( pSoldier ); if( ( gfWorldLoaded ) && ( pSoldier->bInSector ) ) { pSoldier->ChangeSoldierState( STANDING, 1, TRUE ); } } return; } void CreateDestroyMouseRegionsForAssignmentMenu( void ) { static BOOLEAN fCreated = FALSE; UINT32 iCounter = 0; INT32 iFontHeight = 0; INT32 iBoxXPosition = 0; INT32 iBoxYPosition = 0; SOLDIERTYPE *pSoldier = NULL; SGPPoint pPosition; INT32 iBoxWidth = 0; SGPRect pDimensions; static BOOLEAN fShowRemoveMenu = FALSE; // will create/destroy mouse regions for the map screen assignment main menu // check if we can only remove character from team..not assign if( ( bSelectedAssignChar != -1 )|| ( fShowRemoveMenu == TRUE ) ) { if( fShowRemoveMenu == TRUE ) { // dead guy handle menu stuff fShowRemoveMenu = fShowAssignmentMenu | fShowContractMenu; CreateDestroyMouseRegionsForRemoveMenu( ); return; } if( ( Menptr[gCharactersList[bSelectedAssignChar].usSolID].stats.bLife == 0 ) || ( Menptr[gCharactersList[bSelectedAssignChar].usSolID].bAssignment == ASSIGNMENT_POW ) ) { // dead guy handle menu stuff fShowRemoveMenu = fShowAssignmentMenu | fShowContractMenu; CreateDestroyMouseRegionsForRemoveMenu( ); return; } } if( ( fShowAssignmentMenu == TRUE ) && ( fCreated == FALSE ) ) { gfIgnoreScrolling = FALSE; if( ( fShowAssignmentMenu ) && ( guiCurrentScreen == MAP_SCREEN ) ) { SetBoxPosition( ghAssignmentBox, AssignmentPosition ); } pSoldier = GetSelectedAssignSoldier( FALSE ); if( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__EPC ) { // grab height of font iFontHeight = GetLineSpace( ghEpcBox ) + GetFontHeight( GetBoxFont( ghEpcBox ) ); // get x.y position of box GetBoxPosition( ghEpcBox, &pPosition); // grab box x and y position iBoxXPosition = pPosition.iX; iBoxYPosition = pPosition.iY; // get dimensions..mostly for width GetBoxSize( ghEpcBox, &pDimensions ); // get width iBoxWidth = pDimensions.iRight; SetCurrentBox( ghEpcBox ); } else { // grab height of font iFontHeight = GetLineSpace( ghAssignmentBox ) + GetFontHeight( GetBoxFont( ghAssignmentBox ) ); // get x.y position of box GetBoxPosition( ghAssignmentBox, &pPosition); // grab box x and y position iBoxXPosition = pPosition.iX; iBoxYPosition = pPosition.iY; // get dimensions..mostly for width GetBoxSize( ghAssignmentBox, &pDimensions ); // get width iBoxWidth = pDimensions.iRight; SetCurrentBox( ghAssignmentBox ); } // define regions for( iCounter = 0; iCounter < GetNumberOfLinesOfTextInBox( ghAssignmentBox ); iCounter++ ) { // add mouse region for each line of text..and set user data MSYS_DefineRegion( &gAssignmentMenuRegion[ iCounter ], ( INT16 )( iBoxXPosition ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghAssignmentBox ) + ( iFontHeight ) * iCounter ), ( INT16 )( iBoxXPosition + iBoxWidth ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghAssignmentBox ) + ( iFontHeight ) * ( iCounter + 1 ) ), MSYS_PRIORITY_HIGHEST - 4 , MSYS_NO_CURSOR, AssignmentMenuMvtCallBack, AssignmentMenuBtnCallback ); MSYS_SetRegionUserData( &gAssignmentMenuRegion[ iCounter ], 0, iCounter ); } // created fCreated = TRUE; // unhighlight all strings in box UnHighLightBox( ghAssignmentBox ); CheckAndUpdateTacticalAssignmentPopUpPositions( ); PositionCursorForTacticalAssignmentBox( ); } else if( ( fShowAssignmentMenu == FALSE ) && ( fCreated == TRUE ) ) { // destroy for( iCounter = 0; iCounter < GetNumberOfLinesOfTextInBox( ghAssignmentBox ); iCounter++ ) { MSYS_RemoveRegion( &gAssignmentMenuRegion[ iCounter ] ); } fShownAssignmentMenu = FALSE; // not created fCreated = FALSE; SetRenderFlags( RENDER_FLAG_FULL ); } } void CreateDestroyMouseRegionForVehicleMenu( void ) { static BOOLEAN fCreated = FALSE; UINT32 uiMenuLine = 0; INT32 iVehicleId = 0; INT32 iFontHeight = 0; INT32 iBoxXPosition = 0; INT32 iBoxYPosition = 0; SGPPoint pPosition, pPoint; INT32 iBoxWidth = 0; SGPRect pDimensions; SOLDIERTYPE *pSoldier = NULL; if( fShowVehicleMenu ) { GetBoxPosition( ghAssignmentBox, &pPoint); // get dimensions..mostly for width GetBoxSize( ghAssignmentBox, &pDimensions ); // vehicle position VehiclePosition.iX = pPoint.iX + pDimensions.iRight; SetBoxPosition( ghVehicleBox , VehiclePosition ); } if( ( fShowVehicleMenu == TRUE ) && ( fCreated == FALSE ) ) { // grab height of font iFontHeight = GetLineSpace( ghVehicleBox ) + GetFontHeight( GetBoxFont( ghVehicleBox ) ); // get x.y position of box GetBoxPosition( ghVehicleBox, &pPosition); // grab box x and y position iBoxXPosition = pPosition.iX; iBoxYPosition = pPosition.iY; // get dimensions..mostly for width GetBoxSize( ghVehicleBox, &pDimensions ); SetBoxSecondaryShade( ghVehicleBox, FONT_YELLOW ); // get width iBoxWidth = pDimensions.iRight; SetCurrentBox( ghVehicleBox ); pSoldier = GetSelectedAssignSoldier( FALSE ); // run through list of vehicles in sector for ( iVehicleId = 0; iVehicleId < ubNumberOfVehicles; iVehicleId++ ) { if ( pVehicleList[ iVehicleId ].fValid == TRUE ) { if ( IsThisVehicleAccessibleToSoldier( pSoldier, iVehicleId ) ) { // add mouse region for each accessible vehicle MSYS_DefineRegion( &gVehicleMenuRegion[ uiMenuLine ], ( INT16 )( iBoxXPosition ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghAssignmentBox ) + ( iFontHeight ) * uiMenuLine ), ( INT16 )( iBoxXPosition + iBoxWidth ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghAssignmentBox ) + ( iFontHeight ) * ( uiMenuLine + 1 ) ), MSYS_PRIORITY_HIGHEST - 4 , MSYS_NO_CURSOR, VehicleMenuMvtCallback, VehicleMenuBtnCallback ); MSYS_SetRegionUserData( &gVehicleMenuRegion[ uiMenuLine ], 0, uiMenuLine ); // store vehicle ID in the SECOND user data MSYS_SetRegionUserData( &gVehicleMenuRegion[ uiMenuLine ], 1, iVehicleId ); uiMenuLine++; } } } // cancel line MSYS_DefineRegion( &gVehicleMenuRegion[ uiMenuLine ], ( INT16 )( iBoxXPosition ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghAssignmentBox ) + ( iFontHeight ) * uiMenuLine ), ( INT16 )( iBoxXPosition + iBoxWidth ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghAssignmentBox ) + ( iFontHeight ) * ( uiMenuLine + 1 ) ), MSYS_PRIORITY_HIGHEST - 4 , MSYS_NO_CURSOR, VehicleMenuMvtCallback, VehicleMenuBtnCallback ); MSYS_SetRegionUserData( &gVehicleMenuRegion[ uiMenuLine ], 0, VEHICLE_MENU_CANCEL ); // created fCreated = TRUE; // pause game PauseGame( ); // unhighlight all strings in box UnHighLightBox( ghVehicleBox ); fCreated = TRUE; HandleShadingOfLinesForVehicleMenu( ); } else if( ( ( fShowVehicleMenu == FALSE ) || ( fShowAssignmentMenu == FALSE ) ) && ( fCreated == TRUE ) ) { fCreated = FALSE; // remove these regions for( uiMenuLine = 0; uiMenuLine < GetNumberOfLinesOfTextInBox( ghVehicleBox ); uiMenuLine++ ) { MSYS_RemoveRegion( &gVehicleMenuRegion[ uiMenuLine ] ); } fShowVehicleMenu = FALSE; SetRenderFlags( RENDER_FLAG_FULL ); HideBox( ghVehicleBox ); if ( fShowAssignmentMenu ) { // remove highlight on the parent menu UnHighLightBox( ghAssignmentBox ); } } return; } void HandleShadingOfLinesForVehicleMenu( void ) { SOLDIERTYPE *pSoldier = NULL; INT32 iVehicleId; UINT32 uiMenuLine = 0; if ( ( fShowVehicleMenu == FALSE ) || ( ghVehicleBox == - 1 ) ) { return; } pSoldier = GetSelectedAssignSoldier( FALSE ); // run through list of vehicles for ( iVehicleId = 0; iVehicleId < ubNumberOfVehicles; iVehicleId++ ) { if ( pVehicleList[ iVehicleId ].fValid == TRUE ) { // inaccessible vehicles aren't listed at all! if ( IsThisVehicleAccessibleToSoldier( pSoldier, iVehicleId ) ) { if ( IsEnoughSpaceInVehicle( iVehicleId ) ) { // legal vehicle, leave it green UnShadeStringInBox( ghVehicleBox, uiMenuLine ); UnSecondaryShadeStringInBox( ghVehicleBox, uiMenuLine ); } else { // unjoinable vehicle - yellow SecondaryShadeStringInBox( ghVehicleBox, uiMenuLine ); } uiMenuLine++; } } } } void VehicleMenuBtnCallback( MOUSE_REGION * pRegion, INT32 iReason ) { // btn callback handler for assignment region INT32 iValue = -1, iVehicleID; SOLDIERTYPE * pSoldier; iValue = MSYS_GetRegionUserData( pRegion, 0 ); if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP) { if( iValue == VEHICLE_MENU_CANCEL ) { fShowVehicleMenu = FALSE; UnHighLightBox( ghAssignmentBox ); fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; fCharacterInfoPanelDirty = TRUE; return; } pSoldier = GetSelectedAssignSoldier( FALSE ); iVehicleID = MSYS_GetRegionUserData( pRegion, 1 ); // inaccessible vehicles shouldn't be listed in the menu! // AssertT( IsThisVehicleAccessibleToSoldier( pSoldier, iVehicleID ) ); if ( IsEnoughSpaceInVehicle( iVehicleID ) ) { PutSoldierInVehicle( pSoldier, ( INT8 ) iVehicleID ); } else { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, gzLateLocalizedString[ 18 ], zVehicleName[ pVehicleList[ iVehicleID ].ubVehicleType ] ); } fShowAssignmentMenu = FALSE; // update mapscreen fTeamPanelDirty = TRUE; fCharacterInfoPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; giAssignHighLine = -1; SetAssignmentForList( VEHICLE, ( INT8 ) iVehicleID ); } } void VehicleMenuMvtCallback(MOUSE_REGION * pRegion, INT32 iReason ) { // mvt callback handler for assignment region INT32 iValue = -1; iValue = MSYS_GetRegionUserData( pRegion, 0 ); if (iReason & MSYS_CALLBACK_REASON_GAIN_MOUSE ) { if( iValue != VEHICLE_MENU_CANCEL ) { // no shaded(disabled) lines actually appear in vehicle menus if( GetBoxShadeFlag( ghVehicleBox, iValue ) == FALSE ) { // highlight vehicle line HighLightBoxLine( ghVehicleBox, iValue ); } } else { // highlight cancel line HighLightBoxLine( ghVehicleBox, GetNumberOfLinesOfTextInBox( ghVehicleBox ) - 1 ); } } else if (iReason & MSYS_CALLBACK_REASON_LOST_MOUSE ) { // unhighlight all strings in box UnHighLightBox( ghVehicleBox ); HandleShadingOfLinesForVehicleMenu( ); } } BOOLEAN DisplayRepairMenu( SOLDIERTYPE *pSoldier ) { INT32 iVehicleIndex=0; INT32 hStringHandle=0; // run through list of vehicles in sector and add them to pop up box // first, clear pop up box RemoveBox(ghRepairBox); ghRepairBox = -1; CreateRepairBox(); SetCurrentBox(ghRepairBox); // PLEASE NOTE: make sure any changes you do here are reflected in all 3 routines which must remain in synch: // CreateDestroyMouseRegionForRepairMenu(), DisplayRepairMenu(), and HandleShadingOfLinesForRepairMenu(). if( pSoldier->bSectorZ == 0 ) { // run through list of vehicles and see if any in sector for ( iVehicleIndex = 0; iVehicleIndex < ubNumberOfVehicles; iVehicleIndex++ ) { if ( pVehicleList[ iVehicleIndex ].fValid == TRUE ) { // don't even list the helicopter, because it's NEVER repairable... if ( iVehicleIndex != iHelicopterVehicleId ) { if ( IsThisVehicleAccessibleToSoldier( pSoldier, iVehicleIndex ) ) { AddMonoString( (UINT32 *)&hStringHandle,pVehicleStrings[ pVehicleList[ iVehicleIndex ].ubVehicleType ] ); } } } } } /* No point in allowing SAM site repair any more. Jan/13/99. ARM // is there a SAM SITE Here? if( ( IsThisSectorASAMSector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ) == TRUE ) && ( IsTheSAMSiteInSectorRepairable( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ) ) ) { // SAM site AddMonoString(&hStringHandle, pRepairStrings[ 1 ] ); } */ // is the ROBOT here? if( IsRobotInThisSector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ) ) { // robot AddMonoString((UINT32 *)&hStringHandle, pRepairStrings[ 3 ] ); } // items AddMonoString((UINT32 *)&hStringHandle, pRepairStrings[ 0 ] ); // cancel AddMonoString((UINT32 *)&hStringHandle, pRepairStrings[ 2 ] ); SetBoxFont(ghRepairBox, MAP_SCREEN_FONT); SetBoxHighLight(ghRepairBox, FONT_WHITE); SetBoxShade(ghRepairBox, FONT_GRAY7); SetBoxForeground(ghRepairBox, FONT_LTGREEN); SetBoxBackground(ghRepairBox, FONT_BLACK); // resize box to text ResizeBoxToText( ghRepairBox ); CheckAndUpdateTacticalAssignmentPopUpPositions( ); return TRUE; } void HandleShadingOfLinesForRepairMenu( void ) { SOLDIERTYPE *pSoldier = NULL; INT32 iVehicleIndex = 0; INT32 iCount = 0; if( ( fShowRepairMenu == FALSE ) || ( ghRepairBox == -1 ) ) { return; } pSoldier = GetSelectedAssignSoldier( FALSE ); // PLEASE NOTE: make sure any changes you do here are reflected in all 3 routines which must remain in synch: // CreateDestroyMouseRegionForRepairMenu(), DisplayRepairMenu(), and HandleShadingOfLinesForRepairMenu(). if ( pSoldier->bSectorZ == 0 ) { for ( iVehicleIndex = 0; iVehicleIndex < ubNumberOfVehicles; iVehicleIndex++ ) { if ( pVehicleList[ iVehicleIndex ].fValid == TRUE ) { // don't even list the helicopter, because it's NEVER repairable... if ( iVehicleIndex != iHelicopterVehicleId ) { if ( IsThisVehicleAccessibleToSoldier( pSoldier, iVehicleIndex ) ) { if( CanCharacterRepairVehicle( pSoldier, iVehicleIndex ) == TRUE ) { // unshade vehicle line UnShadeStringInBox( ghRepairBox, iCount ); } else { // shade vehicle line ShadeStringInBox( ghRepairBox, iCount ); } iCount++; } } } } } /* No point in allowing SAM site repair any more. Jan/13/99. ARM if( ( IsThisSectorASAMSector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ) == TRUE ) && ( IsTheSAMSiteInSectorRepairable( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ) ) ) { // handle enable disable of repair sam option if( CanSoldierRepairSAM( pSoldier, SAM_SITE_REPAIR_DIVISOR ) ) { // unshade SAM line UnShadeStringInBox( ghRepairBox, iCount ); } else { // shade SAM line ShadeStringInBox( ghRepairBox, iCount ); } iCount++; } */ if( IsRobotInThisSector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ) ) { // handle shading of repair robot option if( CanCharacterRepairRobot( pSoldier ) ) { // unshade robot line UnShadeStringInBox( ghRepairBox, iCount ); } else { // shade robot line ShadeStringInBox( ghRepairBox, iCount ); } iCount++; } if ( DoesCharacterHaveAnyItemsToRepair( pSoldier, FINAL_REPAIR_PASS ) ) { // unshade items line UnShadeStringInBox( ghRepairBox, iCount ); } else { // shade items line ShadeStringInBox( ghRepairBox, iCount ); } iCount++; return; } void CreateDestroyMouseRegionForRepairMenu( void ) { static BOOLEAN fCreated = FALSE; UINT32 uiCounter = 0; INT32 iCount = 0; INT32 iFontHeight = 0; INT32 iBoxXPosition = 0; INT32 iBoxYPosition = 0; SGPPoint pPosition; INT32 iBoxWidth = 0; SGPRect pDimensions; SOLDIERTYPE *pSoldier = NULL; INT32 iVehicleIndex = 0; if( ( fShowRepairMenu == TRUE ) && ( fCreated == FALSE ) ) { CheckAndUpdateTacticalAssignmentPopUpPositions( ); if( ( fShowRepairMenu ) && ( guiCurrentScreen == MAP_SCREEN ) ) { //SetBoxPosition( ghRepairBox ,RepairPosition); } // grab height of font iFontHeight = GetLineSpace( ghRepairBox ) + GetFontHeight( GetBoxFont( ghRepairBox ) ); // get x.y position of box GetBoxPosition( ghRepairBox, &pPosition); // grab box x and y position iBoxXPosition = pPosition.iX; iBoxYPosition = pPosition.iY; // get dimensions..mostly for width GetBoxSize( ghRepairBox, &pDimensions ); // get width iBoxWidth = pDimensions.iRight; SetCurrentBox( ghRepairBox ); pSoldier = GetSelectedAssignSoldier( FALSE ); // PLEASE NOTE: make sure any changes you do here are reflected in all 3 routines which must remain in synch: // CreateDestroyMouseRegionForRepairMenu(), DisplayRepairMenu(), and HandleShadingOfLinesForRepairMenu(). if ( pSoldier->bSectorZ == 0 ) { // vehicles for ( iVehicleIndex = 0; iVehicleIndex < ubNumberOfVehicles; iVehicleIndex++ ) { if ( pVehicleList[ iVehicleIndex ].fValid == TRUE ) { // don't even list the helicopter, because it's NEVER repairable... if ( iVehicleIndex != iHelicopterVehicleId ) { // other vehicles *in the sector* are listed, but later shaded dark if they're not repairable if ( IsThisVehicleAccessibleToSoldier( pSoldier, iVehicleIndex ) ) { // add mouse region for each line of text..and set user data MSYS_DefineRegion( &gRepairMenuRegion[ iCount ], ( INT16 )( iBoxXPosition ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghAssignmentBox ) + ( iFontHeight ) * iCount ), ( INT16 )( iBoxXPosition + iBoxWidth ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghAssignmentBox ) + ( iFontHeight ) * ( iCount + 1 ) ), MSYS_PRIORITY_HIGHEST - 4 , MSYS_NO_CURSOR, RepairMenuMvtCallback, RepairMenuBtnCallback ); MSYS_SetRegionUserData( &gRepairMenuRegion[ iCount ], 0, iCount ); // 2nd user data is the vehicle index, which can easily be different from the region index! MSYS_SetRegionUserData( &gRepairMenuRegion[ iCount ], 1, iVehicleIndex ); iCount++; } } } } } /* No point in allowing SAM site repair any more. Jan/13/99. ARM // SAM site if( ( IsThisSectorASAMSector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ) == TRUE ) && ( IsTheSAMSiteInSectorRepairable( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ) ) ) { MSYS_DefineRegion( &gRepairMenuRegion[ iCount ], ( INT16 )( iBoxXPosition ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghAssignmentBox ) + ( iFontHeight ) * iCount ), ( INT16 )( iBoxXPosition + iBoxWidth ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghAssignmentBox ) + ( iFontHeight ) * ( iCount + 1 ) ), MSYS_PRIORITY_HIGHEST - 4 , MSYS_NO_CURSOR, RepairMenuMvtCallback, RepairMenuBtnCallback ); MSYS_SetRegionUserData( &gRepairMenuRegion[ iCount ], 0, REPAIR_MENU_SAM_SITE ); iCount++; } */ // robot if( IsRobotInThisSector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ) ) { MSYS_DefineRegion( &gRepairMenuRegion[ iCount ], ( INT16 )( iBoxXPosition ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghAssignmentBox ) + ( iFontHeight ) * iCount ), ( INT16 )( iBoxXPosition + iBoxWidth ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghAssignmentBox ) + ( iFontHeight ) * ( iCount + 1 ) ), MSYS_PRIORITY_HIGHEST - 4 , MSYS_NO_CURSOR, RepairMenuMvtCallback, RepairMenuBtnCallback ); MSYS_SetRegionUserData( &gRepairMenuRegion[ iCount ], 0, iCount ); MSYS_SetRegionUserData( &gRepairMenuRegion[ iCount ], 1, REPAIR_MENU_ROBOT ); iCount++; } // items MSYS_DefineRegion( &gRepairMenuRegion[ iCount ], ( INT16 )( iBoxXPosition ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghAssignmentBox ) + ( iFontHeight ) * iCount ), ( INT16 )( iBoxXPosition + iBoxWidth ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghAssignmentBox ) + ( iFontHeight ) * ( iCount + 1 ) ), MSYS_PRIORITY_HIGHEST - 4 , MSYS_NO_CURSOR, RepairMenuMvtCallback, RepairMenuBtnCallback ); MSYS_SetRegionUserData( &gRepairMenuRegion[ iCount ], 0, iCount ); MSYS_SetRegionUserData( &gRepairMenuRegion[ iCount ], 1, REPAIR_MENU_ITEMS ); iCount++; // cancel MSYS_DefineRegion( &gRepairMenuRegion[ iCount ], ( INT16 )( iBoxXPosition ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghAssignmentBox ) + ( iFontHeight ) * iCount ), ( INT16 )( iBoxXPosition + iBoxWidth ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghAssignmentBox ) + ( iFontHeight ) * ( iCount + 1 ) ), MSYS_PRIORITY_HIGHEST - 4 , MSYS_NO_CURSOR, RepairMenuMvtCallback, RepairMenuBtnCallback ); MSYS_SetRegionUserData( &gRepairMenuRegion[ iCount ], 0, iCount ); MSYS_SetRegionUserData( &gRepairMenuRegion[ iCount ], 1, REPAIR_MENU_CANCEL ); PauseGame( ); // unhighlight all strings in box UnHighLightBox( ghRepairBox ); fCreated = TRUE; } else if( ( ( fShowRepairMenu == FALSE ) || ( fShowAssignmentMenu == FALSE ) ) && ( fCreated == TRUE ) ) { fCreated = FALSE; // remove these regions for( uiCounter = 0; uiCounter < GetNumberOfLinesOfTextInBox( ghRepairBox ); uiCounter++ ) { MSYS_RemoveRegion( &gRepairMenuRegion[ uiCounter ] ); } fShowRepairMenu = FALSE; SetRenderFlags( RENDER_FLAG_FULL ); HideBox( ghRepairBox ); if ( fShowAssignmentMenu ) { // remove highlight on the parent menu UnHighLightBox( ghAssignmentBox ); } } return; } void RepairMenuBtnCallback( MOUSE_REGION * pRegion, INT32 iReason ) { // btn callback handler for assignment region INT32 iValue = -1; SOLDIERTYPE *pSoldier = NULL; INT32 iRepairWhat; iValue = MSYS_GetRegionUserData( pRegion, 0 ); // ignore clicks on disabled lines if( GetBoxShadeFlag( ghRepairBox, iValue ) == TRUE ) { return; } // WHAT is being repaired is stored in the second user data argument iRepairWhat = MSYS_GetRegionUserData( pRegion, 1 ); pSoldier = GetSelectedAssignSoldier( FALSE ); if ( pSoldier && pSoldier->bActive && ( iReason & MSYS_CALLBACK_REASON_LBUTTON_UP ) ) { if( ( iRepairWhat >= REPAIR_MENU_VEHICLE1 ) && ( iRepairWhat <= REPAIR_MENU_VEHICLE3 ) ) { // repair VEHICLE pSoldier->bOldAssignment = pSoldier->bAssignment; if( ( pSoldier->bAssignment != REPAIR )|| ( pSoldier->flags.fFixingRobot ) || ( pSoldier->flags.fFixingSAMSite ) ) { SetTimeOfAssignmentChangeForMerc( pSoldier ); } if( pSoldier->bOldAssignment == VEHICLE ) { TakeSoldierOutOfVehicle( pSoldier ); } // remove from squad RemoveCharacterFromSquads( pSoldier ); MakeSureToolKitIsInHand( pSoldier ); ChangeSoldiersAssignment( pSoldier, REPAIR ); pSoldier->bVehicleUnderRepairID = ( INT8 ) iRepairWhat; MakeSureToolKitIsInHand( pSoldier ); // assign to a movement group AssignMercToAMovementGroup( pSoldier ); // set assignment for group SetAssignmentForList( ( INT8 ) REPAIR, 0 ); fShowAssignmentMenu = FALSE; } /* No point in allowing SAM site repair any more. Jan/13/99. ARM else if( iRepairWhat == REPAIR_MENU_SAM_SITE ) { // repair SAM site // remove from squad RemoveCharacterFromSquads( pSoldier ); MakeSureToolKitIsInHand( pSoldier ); if( ( pSoldier->bAssignment != REPAIR )|| ( pSoldier->flags.fFixingSAMSite == FALSE ) ) { SetTimeOfAssignmentChangeForMerc( pSoldier ); } ChangeSoldiersAssignment( pSoldier, REPAIR ); pSoldier->flags.fFixingSAMSite = TRUE; // the second argument is irrelevant here, function looks at pSoldier itself to know what's being repaired SetAssignmentForList( ( INT8 ) REPAIR, 0 ); fShowAssignmentMenu = FALSE; MakeSureToolKitIsInHand( pSoldier ); // assign to a movement group AssignMercToAMovementGroup( pSoldier ); } */ else if( iRepairWhat == REPAIR_MENU_ROBOT ) { // repair ROBOT pSoldier->bOldAssignment = pSoldier->bAssignment; // remove from squad if( pSoldier->bOldAssignment == VEHICLE ) { TakeSoldierOutOfVehicle( pSoldier ); } RemoveCharacterFromSquads( pSoldier ); MakeSureToolKitIsInHand( pSoldier ); if( ( pSoldier->bAssignment != REPAIR )|| ( pSoldier->flags.fFixingRobot == FALSE ) ) { SetTimeOfAssignmentChangeForMerc( pSoldier ); } ChangeSoldiersAssignment( pSoldier, REPAIR ); pSoldier->flags.fFixingRobot = TRUE; // the second argument is irrelevant here, function looks at pSoldier itself to know what's being repaired SetAssignmentForList( ( INT8 ) REPAIR, 0 ); fShowAssignmentMenu = FALSE; MakeSureToolKitIsInHand( pSoldier ); // assign to a movement group AssignMercToAMovementGroup( pSoldier ); } else if( iRepairWhat == REPAIR_MENU_ITEMS ) { // items SetSoldierAssignment( pSoldier, REPAIR, FALSE, FALSE, -1 ); // the second argument is irrelevant here, function looks at pSoldier itself to know what's being repaired SetAssignmentForList( ( INT8 ) REPAIR, 0 ); fShowAssignmentMenu = FALSE; } else { // CANCEL fShowRepairMenu = FALSE; } // update mapscreen fCharacterInfoPanelDirty = TRUE; fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; giAssignHighLine = -1; } } void RepairMenuMvtCallback(MOUSE_REGION * pRegion, INT32 iReason ) { // mvt callback handler for assignment region INT32 iValue = -1; iValue = MSYS_GetRegionUserData( pRegion, 0 ); if (iReason & MSYS_CALLBACK_REASON_GAIN_MOUSE ) { if( iValue < REPAIR_MENU_CANCEL ) { if( GetBoxShadeFlag( ghRepairBox, iValue ) == FALSE ) { // highlight choice HighLightBoxLine( ghRepairBox, iValue ); } } else { // highlight cancel line HighLightBoxLine( ghRepairBox, GetNumberOfLinesOfTextInBox( ghRepairBox ) - 1 ); } } else if (iReason & MSYS_CALLBACK_REASON_LOST_MOUSE ) { // unhighlight all strings in box UnHighLightBox( ghRepairBox ); } } void MakeSureToolKitIsInHand( SOLDIERTYPE *pSoldier ) { INT8 bPocket = 0; // if there isn't a toolkit in his hand if( !Item[pSoldier->inv[ HANDPOS].usItem].toolkit ) { // run through rest of inventory looking for toolkits, swap the first one into hand if found // CHRISL: Changed to dynamically determine max inventory locations. for (bPocket = SECONDHANDPOS; bPocket < NUM_INV_SLOTS; bPocket++) { if( Item[pSoldier->inv[ bPocket ].usItem].toolkit ) { // If the second hand is empty, swap item in first hand there if (pSoldier->inv[HANDPOS].exists() && !pSoldier->inv[SECONDHANDPOS].exists() && CanItemFitInPosition(pSoldier, &pSoldier->inv[SECONDHANDPOS], HANDPOS, FALSE)) { SwapObjs( pSoldier, HANDPOS, SECONDHANDPOS, TRUE ); } if(!CanItemFitInPosition(pSoldier, &pSoldier->inv[HANDPOS], bPocket, FALSE)) SwapObjs(pSoldier, HANDPOS, SECONDHANDPOS, TRUE); SwapObjs( pSoldier, HANDPOS, bPocket, TRUE ); break; } } } } BOOLEAN MakeSureMedKitIsInHand( SOLDIERTYPE *pSoldier ) { INT8 bPocket = 0; fTeamPanelDirty = TRUE; // if there is a MEDICAL BAG in his hand, we're set if ( Item[pSoldier->inv[ HANDPOS ].usItem].medicalkit ) { return(TRUE); } // run through rest of inventory looking 1st for MEDICAL BAGS, swap the first one into hand if found // CHRISL: Changed to dynamically determine max inventory locations. for (bPocket = SECONDHANDPOS; bPocket < NUM_INV_SLOTS; bPocket++) { if ( Item[pSoldier->inv[ bPocket ].usItem].medicalkit ) { fCharacterInfoPanelDirty = TRUE; // If the second hand is empty, swap item in first hand there if (pSoldier->inv[HANDPOS].exists() && !pSoldier->inv[SECONDHANDPOS].exists() && CanItemFitInPosition(pSoldier, &pSoldier->inv[SECONDHANDPOS], HANDPOS, FALSE)) { SwapObjs( pSoldier, HANDPOS, SECONDHANDPOS, TRUE ); } if(!CanItemFitInPosition(pSoldier, &pSoldier->inv[HANDPOS], bPocket, FALSE)) SwapObjs(pSoldier, HANDPOS, SECONDHANDPOS, TRUE); SwapObjs( pSoldier, HANDPOS, bPocket, TRUE ); return(TRUE); } } // we didn't find a medical bag, so settle for a FIRST AID KIT if ( Item[pSoldier->inv[ HANDPOS ].usItem].firstaidkit ) { return(TRUE); } // run through rest of inventory looking 1st for MEDICAL BAGS, swap the first one into hand if found // CHRISL: Changed to dynamically determine max inventory locations. for (bPocket = SECONDHANDPOS; bPocket < NUM_INV_SLOTS; bPocket++) { if ( Item[pSoldier->inv[ bPocket ].usItem].firstaidkit ) { // CHRISL: This needs to start with the first "non-big" pocket. if( ( Item[ pSoldier -> inv[ HANDPOS ].usItem ].twohanded ) && ( bPocket >= SMALLPOCKSTART ) ) { // first move from hand to second hand SwapObjs( pSoldier, HANDPOS, SECONDHANDPOS, TRUE ); // dirty mapscreen and squad panels fCharacterInfoPanelDirty = TRUE; fInterfacePanelDirty = DIRTYLEVEL2; } SwapObjs( pSoldier, HANDPOS, bPocket, TRUE ); return(TRUE); } } // no medkit items in possession! return(FALSE); } void HandleShadingOfLinesForAssignmentMenus( void ) { SOLDIERTYPE *pSoldier = NULL; // updates which menus are selectable based on character status if( ( fShowAssignmentMenu == FALSE ) || ( ghAssignmentBox == - 1 ) ) { return; } pSoldier = GetSelectedAssignSoldier( FALSE ); if ( pSoldier && pSoldier->bActive ) { if( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__EPC ) { // patient if( CanCharacterPatient( pSoldier ) ) { // unshade patient line UnShadeStringInBox( ghEpcBox, EPC_MENU_PATIENT ); } else { // shade patient line ShadeStringInBox( ghEpcBox, EPC_MENU_PATIENT ); } if( CanCharacterOnDuty( pSoldier ) ) { // unshade on duty line UnShadeStringInBox( ghEpcBox, EPC_MENU_ON_DUTY ); } else { // shade on duty line ShadeStringInBox( ghEpcBox, EPC_MENU_ON_DUTY ); } if( CanCharacterVehicle( pSoldier ) ) { // unshade vehicle line UnShadeStringInBox( ghEpcBox, EPC_MENU_VEHICLE ); } else { // shade vehicle line ShadeStringInBox( ghEpcBox, EPC_MENU_VEHICLE ); } } else { // doctor if( CanCharacterDoctor( pSoldier ) ) { // unshade doctor line UnShadeStringInBox( ghAssignmentBox, ASSIGN_MENU_DOCTOR ); UnSecondaryShadeStringInBox( ghAssignmentBox, ASSIGN_MENU_DOCTOR ); } else { // only missing a med kit if( CanCharacterDoctorButDoesntHaveMedKit( pSoldier ) ) { SecondaryShadeStringInBox( ghAssignmentBox, ASSIGN_MENU_DOCTOR ); } else { // shade doctor line ShadeStringInBox( ghAssignmentBox, ASSIGN_MENU_DOCTOR ); } } // repair if( CanCharacterRepair( pSoldier ) ) { // unshade repair line UnShadeStringInBox( ghAssignmentBox, ASSIGN_MENU_REPAIR ); UnSecondaryShadeStringInBox( ghAssignmentBox, ASSIGN_MENU_REPAIR ); } else { // only missing a tool kit if( CanCharacterRepairButDoesntHaveARepairkit( pSoldier ) ) { SecondaryShadeStringInBox( ghAssignmentBox, ASSIGN_MENU_REPAIR ); } else { // shade repair line ShadeStringInBox( ghAssignmentBox, ASSIGN_MENU_REPAIR ); } } // patient if( CanCharacterPatient( pSoldier ) ) { // unshade patient line UnShadeStringInBox( ghAssignmentBox, ASSIGN_MENU_PATIENT ); } else { // shade patient line ShadeStringInBox( ghAssignmentBox, ASSIGN_MENU_PATIENT ); } if( CanCharacterOnDuty( pSoldier ) ) { // unshade on duty line UnShadeStringInBox( ghAssignmentBox, ASSIGN_MENU_ON_DUTY ); } else { // shade on duty line ShadeStringInBox( ghAssignmentBox, ASSIGN_MENU_ON_DUTY ); } if( CanCharacterPractise( pSoldier ) ) { // unshade train line UnShadeStringInBox( ghAssignmentBox, ASSIGN_MENU_TRAIN ); } else { // shade train line ShadeStringInBox( ghAssignmentBox, ASSIGN_MENU_TRAIN ); } if( CanCharacterVehicle( pSoldier ) ) { // unshade vehicle line UnShadeStringInBox( ghAssignmentBox, ASSIGN_MENU_VEHICLE ); } else { // shade vehicle line ShadeStringInBox( ghAssignmentBox, ASSIGN_MENU_VEHICLE ); } } } // squad submenu HandleShadingOfLinesForSquadMenu( ); // vehicle submenu HandleShadingOfLinesForVehicleMenu( ); // repair submenu HandleShadingOfLinesForRepairMenu( ); // training submenu HandleShadingOfLinesForTrainingMenu( ); // training attributes submenu HandleShadingOfLinesForAttributeMenus( ); return; } void DetermineWhichAssignmentMenusCanBeShown( void ) { BOOLEAN fCharacterNoLongerValid = FALSE; SOLDIERTYPE *pSoldier = NULL; if ( (guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) ) { if( fShowMapScreenMovementList == TRUE ) { if( bSelectedDestChar == -1 ) { fCharacterNoLongerValid = TRUE; HandleShowingOfMovementBox( ); } else { fShowMapScreenMovementList = FALSE; fCharacterNoLongerValid = TRUE; } } /* else if( fShowUpdateBox ) { //handle showing of the merc update box HandleShowingOfUpBox( ); } */ else if( bSelectedAssignChar == -1 ) { fCharacterNoLongerValid = TRUE; } // update the assignment positions UpdateMapScreenAssignmentPositions( ); } // determine which assign menu needs to be shown if( ( ( fShowAssignmentMenu == FALSE ) )||( fCharacterNoLongerValid == TRUE ) ) { // reset show assignment menus fShowAssignmentMenu = FALSE; fShowVehicleMenu = FALSE; fShowRepairMenu = FALSE; // destroy mask, if needed CreateDestroyScreenMaskForAssignmentAndContractMenus( ); // destroy menu if needed CreateDestroyMouseRegionForVehicleMenu( ); CreateDestroyMouseRegionsForAssignmentMenu( ); CreateDestroyMouseRegionsForTrainingMenu( ); CreateDestroyMouseRegionsForAttributeMenu( ); CreateDestroyMouseRegionsForSquadMenu( TRUE ); CreateDestroyMouseRegionForRepairMenu( ); // hide all boxes being shown if ( IsBoxShown( ghEpcBox ) ) { HideBox( ghEpcBox ); fTeamPanelDirty = TRUE; gfRenderPBInterface = TRUE; } if ( IsBoxShown( ghAssignmentBox ) ) { HideBox( ghAssignmentBox ); fTeamPanelDirty = TRUE; gfRenderPBInterface = TRUE; } if ( IsBoxShown( ghTrainingBox ) ) { HideBox( ghTrainingBox ); fTeamPanelDirty = TRUE; gfRenderPBInterface = TRUE; } if ( IsBoxShown( ghRepairBox ) ) { HideBox( ghRepairBox ); fTeamPanelDirty = TRUE; gfRenderPBInterface = TRUE; } if ( IsBoxShown( ghAttributeBox ) ) { HideBox( ghAttributeBox ); fTeamPanelDirty = TRUE; gfRenderPBInterface = TRUE; } if ( IsBoxShown( ghVehicleBox ) ) { HideBox( ghVehicleBox ); fTeamPanelDirty = TRUE; gfRenderPBInterface = TRUE; } // do we really want ot hide this box? if( fShowContractMenu == FALSE ) { if ( IsBoxShown( ghRemoveMercAssignBox ) ) { HideBox( ghRemoveMercAssignBox ); fTeamPanelDirty = TRUE; gfRenderPBInterface = TRUE; } } //HideBox( ghSquadBox ); //SetRenderFlags(RENDER_FLAG_FULL); // no menus, leave return; } // update the assignment positions UpdateMapScreenAssignmentPositions( ); // create mask, if needed CreateDestroyScreenMaskForAssignmentAndContractMenus( ); // created assignment menu if needed CreateDestroyMouseRegionsForAssignmentMenu( ); CreateDestroyMouseRegionsForTrainingMenu( ); CreateDestroyMouseRegionsForAttributeMenu( ); CreateDestroyMouseRegionsForSquadMenu( TRUE ); CreateDestroyMouseRegionForRepairMenu( ); if( ( ( Menptr[gCharactersList[ bSelectedInfoChar ].usSolID].stats.bLife == 0 )||( Menptr[gCharactersList[bSelectedInfoChar].usSolID].bAssignment == ASSIGNMENT_POW ) ) && ( (guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) ) ) { // show basic assignment menu ShowBox( ghRemoveMercAssignBox ); } else { pSoldier = GetSelectedAssignSoldier( FALSE ); if( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__EPC ) { ShowBox( ghEpcBox ); } else { // show basic assignment menu ShowBox( ghAssignmentBox ); } } // TRAINING menu if( fShowTrainingMenu == TRUE ) { HandleShadingOfLinesForTrainingMenu( ); ShowBox( ghTrainingBox ); } else { if( IsBoxShown( ghTrainingBox ) ) { HideBox( ghTrainingBox ); fTeamPanelDirty = TRUE; fMapPanelDirty = TRUE; gfRenderPBInterface = TRUE; // SetRenderFlags(RENDER_FLAG_FULL); } } // REPAIR menu if( fShowRepairMenu == TRUE ) { HandleShadingOfLinesForRepairMenu( ); ShowBox( ghRepairBox ); } else { // hide box if( IsBoxShown( ghRepairBox ) ) { HideBox( ghRepairBox ); fTeamPanelDirty = TRUE; fMapPanelDirty = TRUE; gfRenderPBInterface = TRUE; // SetRenderFlags(RENDER_FLAG_FULL); } } // ATTRIBUTE menu if( fShowAttributeMenu == TRUE ) { HandleShadingOfLinesForAttributeMenus( ); ShowBox( ghAttributeBox ); } else { if( IsBoxShown( ghAttributeBox ) ) { HideBox( ghAttributeBox ); fTeamPanelDirty = TRUE; fMapPanelDirty = TRUE; gfRenderPBInterface = TRUE; // SetRenderFlags(RENDER_FLAG_FULL); } } // VEHICLE menu if( fShowVehicleMenu == TRUE ) { ShowBox( ghVehicleBox ); } else { if( IsBoxShown( ghVehicleBox ) ) { HideBox( ghVehicleBox ); fTeamPanelDirty = TRUE; fMapPanelDirty = TRUE; gfRenderPBInterface = TRUE; // SetRenderFlags(RENDER_FLAG_FULL); } } CreateDestroyMouseRegionForVehicleMenu( ); return; } void CreateDestroyScreenMaskForAssignmentAndContractMenus( void ) { static BOOLEAN fCreated = FALSE; // will create a screen mask to catch mouse input to disable assignment menus // not created, create if( ( fCreated == FALSE ) && ( ( fShowAssignmentMenu == TRUE )||( fShowContractMenu == TRUE ) || ( fShowTownInfo == TRUE) ) ) { MSYS_DefineRegion( &gAssignmentScreenMaskRegion, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, MSYS_PRIORITY_HIGHEST - 4 , MSYS_NO_CURSOR, MSYS_NO_CALLBACK, AssignmentScreenMaskBtnCallback ); // created fCreated = TRUE; if ( !(guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) ) { MSYS_ChangeRegionCursor( &gAssignmentScreenMaskRegion, 0 ); } } else if( ( fCreated == TRUE ) && ( fShowAssignmentMenu == FALSE )&&( fShowContractMenu == FALSE ) && ( fShowTownInfo == FALSE ) ) { // created, get rid of it MSYS_RemoveRegion( &gAssignmentScreenMaskRegion ); // not created fCreated = FALSE; } return; } void AssignmentScreenMaskBtnCallback(MOUSE_REGION * pRegion, INT32 iReason ) { // btn callback handler for assignment screen mask region if( ( iReason & MSYS_CALLBACK_REASON_LBUTTON_UP ) || ( iReason & MSYS_CALLBACK_REASON_RBUTTON_UP ) ) { if( fFirstClickInAssignmentScreenMask == TRUE ) { fFirstClickInAssignmentScreenMask = FALSE; return; } // button event, stop showing menus fShowAssignmentMenu = FALSE; fShowVehicleMenu = FALSE; fShowContractMenu = FALSE; // stop showing town mine info fShowTownInfo = FALSE; // reset contract character and contract highlight line giContractHighLine =-1; bSelectedContractChar = -1; fGlowContractRegion = FALSE; // update mapscreen fTeamPanelDirty = TRUE; fCharacterInfoPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; gfRenderPBInterface = TRUE; SetRenderFlags( RENDER_FLAG_FULL ); } return; } void ClearScreenMaskForMapScreenExit( void ) { // reset show assignment menu fShowAssignmentMenu = FALSE; // update the assignment positions UpdateMapScreenAssignmentPositions( ); // stop showing town mine info too fShowTownInfo = FALSE; // destroy mask, if needed CreateDestroyScreenMaskForAssignmentAndContractMenus( ); // destroy assignment menu if needed CreateDestroyMouseRegionsForAssignmentMenu( ); CreateDestroyMouseRegionsForTrainingMenu( ); CreateDestroyMouseRegionsForAttributeMenu( ); CreateDestroyMouseRegionsForSquadMenu( TRUE ); CreateDestroyMouseRegionForRepairMenu( ); return; } void CreateDestroyMouseRegions( void ) { static BOOLEAN fCreated = FALSE; UINT32 iCounter = 0; INT32 iFontHeight = 0; INT32 iBoxXPosition = 0; INT32 iBoxYPosition = 0; SGPPoint pPosition; INT32 iBoxWidth = 0; SGPRect pDimensions; // will create/destroy mouse regions for the map screen assignment main menu // do we show the remove menu if( fShowRemoveMenu ) { CreateDestroyMouseRegionsForRemoveMenu( ); return; } if( ( fShowAssignmentMenu == TRUE ) && ( fCreated == FALSE ) ) { // grab height of font iFontHeight = GetLineSpace( ghAssignmentBox ) + GetFontHeight( GetBoxFont( ghAssignmentBox ) ); // get x.y position of box GetBoxPosition( ghAssignmentBox, &pPosition); // grab box x and y position iBoxXPosition = pPosition.iX; iBoxYPosition = pPosition.iY; // get dimensions..mostly for width GetBoxSize( ghAssignmentBox, &pDimensions ); // get width iBoxWidth = pDimensions.iRight; SetCurrentBox( ghAssignmentBox ); // define regions for( iCounter = 0; iCounter < GetNumberOfLinesOfTextInBox( ghAssignmentBox ); iCounter++ ) { // add mouse region for each line of text..and set user data MSYS_DefineRegion( &gAssignmentMenuRegion[ iCounter ], ( INT16 )( iBoxXPosition ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghAssignmentBox ) + ( iFontHeight ) * iCounter ), ( INT16 )( iBoxXPosition + iBoxWidth ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghAssignmentBox ) + ( iFontHeight ) * ( iCounter + 1 ) ), MSYS_PRIORITY_HIGHEST - 4 , MSYS_NO_CURSOR, AssignmentMenuMvtCallBack, AssignmentMenuBtnCallback ); // set user defines MSYS_SetRegionUserData( &gAssignmentMenuRegion[ iCounter ], 0, iCounter ); } // created fCreated = TRUE; // pause game PauseGame( ); fMapPanelDirty = TRUE; fCharacterInfoPanelDirty= TRUE; fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; // unhighlight all strings in box UnHighLightBox( ghAssignmentBox ); } else if( ( fShowAssignmentMenu == FALSE ) && ( fCreated == TRUE ) ) { // destroy for( iCounter = 0; iCounter < GetNumberOfLinesOfTextInBox( ghAssignmentBox ); iCounter++ ) { MSYS_RemoveRegion( &gAssignmentMenuRegion[ iCounter ] ); } RestorePopUpBoxes(); // not created fCreated = FALSE; } } void CreateDestroyMouseRegionsForContractMenu( void ) { static BOOLEAN fCreated = FALSE; UINT32 iCounter = 0; INT32 iFontHeight = 0; INT32 iBoxXPosition = 0; INT32 iBoxYPosition = 0; SGPPoint pPosition; INT32 iBoxWidth = 0; SGPRect pDimensions; static BOOLEAN fShowRemoveMenu = FALSE; // will create/destroy mouse regions for the map screen Contract main menu // will create/destroy mouse regions for the map screen assignment main menu // check if we can only remove character from team..not assign if( ( bSelectedContractChar != -1 )|| ( fShowRemoveMenu == TRUE ) ) { if( fShowRemoveMenu == TRUE ) { // dead guy handle menu stuff fShowRemoveMenu = fShowContractMenu; // ATE: Added this setting of global variable 'cause // it will cause an assert failure in GetSelectedAssignSoldier() bSelectedAssignChar = bSelectedContractChar; CreateDestroyMouseRegionsForRemoveMenu( ); return; } if( Menptr[gCharactersList[bSelectedContractChar].usSolID].stats.bLife == 0 ) { // dead guy handle menu stuff fShowRemoveMenu = fShowContractMenu; // ATE: Added this setting of global variable 'cause // it will cause an assert failure in GetSelectedAssignSoldier() bSelectedAssignChar = bSelectedContractChar; CreateDestroyMouseRegionsForRemoveMenu( ); return; } } if( ( fShowContractMenu == TRUE ) && ( fCreated == FALSE ) ) { if( bSelectedContractChar == -1 ) { return; } if( fShowContractMenu ) { SetBoxPosition( ghContractBox , ContractPosition ); } // grab height of font iFontHeight = GetLineSpace( ghContractBox ) + GetFontHeight( GetBoxFont( ghContractBox ) ); // get x.y position of box GetBoxPosition( ghContractBox, &pPosition); // grab box x and y position iBoxXPosition = pPosition.iX; iBoxYPosition = pPosition.iY; // get dimensions..mostly for width GetBoxSize( ghContractBox, &pDimensions ); // get width iBoxWidth = pDimensions.iRight; SetCurrentBox( ghContractBox ); // define regions for( iCounter = 0; iCounter < GetNumberOfLinesOfTextInBox( ghContractBox ); iCounter++ ) { // add mouse region for each line of text..and set user data MSYS_DefineRegion( &gContractMenuRegion[ iCounter ], ( INT16 )( iBoxXPosition ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghContractBox ) + ( iFontHeight ) * iCounter ), ( INT16 )( iBoxXPosition + iBoxWidth ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghContractBox ) + ( iFontHeight ) * ( iCounter + 1 ) ), MSYS_PRIORITY_HIGHEST - 4 , MSYS_NO_CURSOR, ContractMenuMvtCallback, ContractMenuBtnCallback ); // set user defines MSYS_SetRegionUserData( &gContractMenuRegion[ iCounter ], 0, iCounter ); } // created fCreated = TRUE; // pause game PauseGame( ); // unhighlight all strings in box UnHighLightBox( ghContractBox ); } else if( ( fShowContractMenu == FALSE ) && ( fCreated == TRUE ) ) { // destroy for( iCounter = 0; iCounter < GetNumberOfLinesOfTextInBox( ghContractBox ); iCounter++ ) { MSYS_RemoveRegion( &gContractMenuRegion[ iCounter ] ); } fShownContractMenu = FALSE; //if( ( fProcessingAMerc ) && ( pProcessingSoldier ) ) //{ // if( (UINT32)(pProcessingSoldier->iEndofContractTime) == GetWorldTotalMin() ) // { // StrategicRemoveMerc( pProcessingSoldier, MERC_FIRED ); // pProcessingSoldier = NULL; // fProcessingAMerc = FALSE; // } //} fMapPanelDirty = TRUE; fCharacterInfoPanelDirty= TRUE; fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; RestorePopUpBoxes( ); // not created fCreated = FALSE; } } void CreateDestroyMouseRegionsForTrainingMenu( void ) { static BOOLEAN fCreated = FALSE; UINT32 iCounter = 0; INT32 iFontHeight = 0; INT32 iBoxXPosition = 0; INT32 iBoxYPosition = 0; SGPPoint pPosition; INT32 iBoxWidth = 0; SGPRect pDimensions; // will create/destroy mouse regions for the map screen assignment main menu if( ( fShowTrainingMenu == TRUE ) && ( fCreated == FALSE ) ) { if( ( fShowTrainingMenu ) && ( guiCurrentScreen == MAP_SCREEN ) ) { SetBoxPosition( ghTrainingBox, TrainPosition ); } HandleShadingOfLinesForTrainingMenu( ); CheckAndUpdateTacticalAssignmentPopUpPositions( ); // grab height of font iFontHeight = GetLineSpace( ghTrainingBox ) + GetFontHeight( GetBoxFont( ghTrainingBox ) ); // get x.y position of box GetBoxPosition( ghTrainingBox, &pPosition); // grab box x and y position iBoxXPosition = pPosition.iX; iBoxYPosition = pPosition.iY; // get dimensions..mostly for width GetBoxSize( ghTrainingBox, &pDimensions ); SetBoxSecondaryShade( ghTrainingBox, FONT_YELLOW ); // get width iBoxWidth = pDimensions.iRight; SetCurrentBox( ghTrainingBox ); // define regions for( iCounter = 0; iCounter < GetNumberOfLinesOfTextInBox( ghTrainingBox ); iCounter++ ) { // add mouse region for each line of text..and set user data MSYS_DefineRegion( &gTrainingMenuRegion[ iCounter ], ( INT16 )( iBoxXPosition ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghTrainingBox ) + ( iFontHeight ) * iCounter ), ( INT16 )( iBoxXPosition + iBoxWidth ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghTrainingBox ) + ( iFontHeight ) * ( iCounter + 1 ) ), MSYS_PRIORITY_HIGHEST - 3 , MSYS_NO_CURSOR, TrainingMenuMvtCallBack, TrainingMenuBtnCallback ); // set user defines MSYS_SetRegionUserData( &gTrainingMenuRegion[ iCounter ], 0, iCounter ); } // created fCreated = TRUE; // unhighlight all strings in box UnHighLightBox( ghTrainingBox ); } else if( ( ( fShowAssignmentMenu == FALSE ) || ( fShowTrainingMenu == FALSE ) ) && ( fCreated == TRUE ) ) { // destroy for( iCounter = 0; iCounter < GetNumberOfLinesOfTextInBox( ghTrainingBox ); iCounter++ ) { MSYS_RemoveRegion( &gTrainingMenuRegion[ iCounter ] ); } // stop showing training menu if( fShowAssignmentMenu == FALSE ) { fShowTrainingMenu = FALSE; } RestorePopUpBoxes( ); fMapPanelDirty = TRUE; fCharacterInfoPanelDirty= TRUE; fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; HideBox( ghTrainingBox ); SetRenderFlags( RENDER_FLAG_FULL ); // not created fCreated = FALSE; if ( fShowAssignmentMenu ) { // remove highlight on the parent menu UnHighLightBox( ghAssignmentBox ); } } } void CreateDestroyMouseRegionsForAttributeMenu( void ) { static BOOLEAN fCreated = FALSE; UINT32 iCounter = 0; INT32 iFontHeight = 0; INT32 iBoxXPosition = 0; INT32 iBoxYPosition = 0; SGPPoint pPosition; INT32 iBoxWidth = 0; SGPRect pDimensions; // will create/destroy mouse regions for the map screen attribute menu if( ( fShowAttributeMenu == TRUE ) && ( fCreated == FALSE ) ) { if( ( fShowAssignmentMenu ) && ( guiCurrentScreen == MAP_SCREEN ) ) { SetBoxPosition( ghAssignmentBox, AssignmentPosition ); } HandleShadingOfLinesForAttributeMenus( ); CheckAndUpdateTacticalAssignmentPopUpPositions( ); // grab height of font iFontHeight = GetLineSpace( ghAttributeBox ) + GetFontHeight( GetBoxFont( ghAttributeBox ) ); // get x.y position of box GetBoxPosition( ghAttributeBox, &pPosition); // grab box x and y position iBoxXPosition = pPosition.iX; iBoxYPosition = pPosition.iY; // get dimensions..mostly for width GetBoxSize( ghAttributeBox, &pDimensions ); // get width iBoxWidth = pDimensions.iRight; SetCurrentBox( ghAttributeBox ); // define regions for( iCounter = 0; iCounter < GetNumberOfLinesOfTextInBox( ghAttributeBox ); iCounter++ ) { // add mouse region for each line of text..and set user data MSYS_DefineRegion( &gAttributeMenuRegion[ iCounter ], ( INT16 )( iBoxXPosition ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghAttributeBox ) + ( iFontHeight ) * iCounter ), ( INT16 )( iBoxXPosition + iBoxWidth ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghAttributeBox ) + ( iFontHeight ) * ( iCounter + 1 ) ), MSYS_PRIORITY_HIGHEST - 2 , MSYS_NO_CURSOR, AttributeMenuMvtCallBack, AttributesMenuBtnCallback ); // set user defines MSYS_SetRegionUserData( &gAttributeMenuRegion[ iCounter ], 0, iCounter ); } // created fCreated = TRUE; // unhighlight all strings in box UnHighLightBox( ghAttributeBox ); } else if( ( ( fShowAssignmentMenu == FALSE ) || ( fShowTrainingMenu == FALSE ) ||( fShowAttributeMenu == FALSE) ) && ( fCreated == TRUE ) ) { // destroy for( iCounter = 0; iCounter < GetNumberOfLinesOfTextInBox( ghAttributeBox ); iCounter++ ) { MSYS_RemoveRegion( &gAttributeMenuRegion[ iCounter ] ); } // stop showing training menu if( ( fShowAssignmentMenu == FALSE )||( fShowTrainingMenu == FALSE ) || ( fShowAttributeMenu == FALSE)) { fShowAttributeMenu = FALSE; gfRenderPBInterface = TRUE; } RestorePopUpBoxes( ); fMapPanelDirty = TRUE; fCharacterInfoPanelDirty= TRUE; fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; HideBox( ghAttributeBox ); SetRenderFlags( RENDER_FLAG_FULL ); // not created fCreated = FALSE; if ( fShowTrainingMenu ) { // remove highlight on the parent menu UnHighLightBox( ghTrainingBox ); } } } void CreateDestroyMouseRegionsForRemoveMenu( void ) { static BOOLEAN fCreated = FALSE; UINT32 iCounter = 0; INT32 iFontHeight = 0; INT32 iBoxXPosition = 0; INT32 iBoxYPosition = 0; SGPPoint pPosition; INT32 iBoxWidth = 0; SGPRect pDimensions; // will create/destroy mouse regions for the map screen attribute menu if( ( ( fShowAssignmentMenu == TRUE ) || ( fShowContractMenu == TRUE ) ) && ( fCreated == FALSE ) ) { if( fShowContractMenu ) { SetBoxPosition( ghContractBox , ContractPosition ); } else { SetBoxPosition( ghAssignmentBox , AssignmentPosition ); } if( fShowContractMenu ) { // set box position to contract box position SetBoxPosition( ghRemoveMercAssignBox , ContractPosition ); } else { // set box position to contract box position SetBoxPosition( ghRemoveMercAssignBox , AssignmentPosition ); } CheckAndUpdateTacticalAssignmentPopUpPositions( ); // grab height of font iFontHeight = GetLineSpace( ghRemoveMercAssignBox ) + GetFontHeight( GetBoxFont( ghRemoveMercAssignBox ) ); // get x.y position of box GetBoxPosition( ghRemoveMercAssignBox, &pPosition); // grab box x and y position iBoxXPosition = pPosition.iX; iBoxYPosition = pPosition.iY; // get dimensions..mostly for width GetBoxSize( ghRemoveMercAssignBox, &pDimensions ); // get width iBoxWidth = pDimensions.iRight; SetCurrentBox( ghRemoveMercAssignBox ); // define regions for( iCounter = 0; iCounter < GetNumberOfLinesOfTextInBox( ghRemoveMercAssignBox ); iCounter++ ) { // add mouse region for each line of text..and set user data MSYS_DefineRegion( &gRemoveMercAssignRegion[ iCounter ], ( INT16 )( iBoxXPosition ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghAttributeBox ) + ( iFontHeight ) * iCounter ), ( INT16 )( iBoxXPosition + iBoxWidth ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghAttributeBox ) + ( iFontHeight ) * ( iCounter + 1 ) ), MSYS_PRIORITY_HIGHEST - 2 , MSYS_NO_CURSOR, RemoveMercMenuMvtCallBack, RemoveMercMenuBtnCallback ); // set user defines MSYS_SetRegionUserData( &gRemoveMercAssignRegion[ iCounter ], 0, iCounter ); } // created fCreated = TRUE; // unhighlight all strings in box UnHighLightBox( ghRemoveMercAssignBox ); } else if( ( fShowAssignmentMenu == FALSE ) && ( fCreated == TRUE ) && ( fShowContractMenu == FALSE ) ) { // destroy for( iCounter = 0; iCounter < GetNumberOfLinesOfTextInBox( ghRemoveMercAssignBox ); iCounter++ ) { MSYS_RemoveRegion( &gRemoveMercAssignRegion[ iCounter ] ); } fShownContractMenu = FALSE; // stop showing menu if( fShowRemoveMenu == FALSE ) { fShowAttributeMenu = FALSE; fMapPanelDirty = TRUE; gfRenderPBInterface = TRUE; } RestorePopUpBoxes( ); fMapPanelDirty = TRUE; fCharacterInfoPanelDirty= TRUE; fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; // turn off the GLOBAL fShowRemoveMenu flag!!! fShowRemoveMenu = FALSE; // and the assignment menu itself!!! fShowAssignmentMenu = FALSE; // not created fCreated = FALSE; } } void CreateDestroyMouseRegionsForSquadMenu( BOOLEAN fPositionBox ) { static BOOLEAN fCreated = FALSE; UINT32 iCounter = 0; INT32 iFontHeight = 0; INT32 iBoxXPosition = 0; INT32 iBoxYPosition = 0; SGPPoint pPosition; INT32 iBoxWidth = 0; SGPRect pDimensions; // will create/destroy mouse regions for the map screen attribute menu if( ( fShowSquadMenu == TRUE ) && ( fCreated == FALSE ) ) { // create squad box CreateSquadBox( ); GetBoxSize( ghAssignmentBox, &pDimensions ); CheckAndUpdateTacticalAssignmentPopUpPositions( ); // grab height of font iFontHeight = GetLineSpace( ghSquadBox ) + GetFontHeight( GetBoxFont( ghSquadBox ) ); // get x.y position of box GetBoxPosition( ghSquadBox, &pPosition); // grab box x and y position iBoxXPosition = pPosition.iX; iBoxYPosition = pPosition.iY; // get dimensions..mostly for width GetBoxSize( ghSquadBox, &pDimensions ); // get width iBoxWidth = pDimensions.iRight; SetCurrentBox( ghSquadBox ); // define regions for( iCounter = 0; iCounter < GetNumberOfLinesOfTextInBox( ghSquadBox ) - 1; iCounter++ ) { // add mouse region for each line of text..and set user data MSYS_DefineRegion( &gSquadMenuRegion[ iCounter ], ( INT16 )( iBoxXPosition ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghSquadBox ) + ( iFontHeight ) * iCounter ), ( INT16 )( iBoxXPosition + iBoxWidth ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghSquadBox ) + ( iFontHeight ) * ( iCounter + 1 ) ), MSYS_PRIORITY_HIGHEST - 2 , MSYS_NO_CURSOR, SquadMenuMvtCallBack, SquadMenuBtnCallback ); MSYS_SetRegionUserData( &gSquadMenuRegion[ iCounter ], 0, iCounter ); } // now create cancel region MSYS_DefineRegion( &gSquadMenuRegion[ iCounter ], ( INT16 )( iBoxXPosition ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghSquadBox ) + ( iFontHeight ) * iCounter ), ( INT16 )( iBoxXPosition + iBoxWidth ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghSquadBox ) + ( iFontHeight ) * ( iCounter + 1 ) ), MSYS_PRIORITY_HIGHEST - 2 , MSYS_NO_CURSOR, SquadMenuMvtCallBack, SquadMenuBtnCallback ); MSYS_SetRegionUserData( &gSquadMenuRegion[ iCounter ], 0, SQUAD_MENU_CANCEL ); // created fCreated = TRUE; // show the box ShowBox( ghSquadBox ); // unhighlight all strings in box UnHighLightBox( ghSquadBox ); // update based on current squad HandleShadingOfLinesForSquadMenu( ); } else if( ( ( fShowAssignmentMenu == FALSE ) || ( fShowSquadMenu == FALSE ) ) && ( fCreated == TRUE ) ) { // destroy for( iCounter = 0; iCounter < GetNumberOfLinesOfTextInBox( ghSquadBox ); iCounter++ ) { MSYS_RemoveRegion( &gSquadMenuRegion[ iCounter ] ); } fShowSquadMenu = FALSE; // remove squad box RemoveBox(ghSquadBox); ghSquadBox = -1; RestorePopUpBoxes( ); fMapPanelDirty = TRUE; fCharacterInfoPanelDirty= TRUE; fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; SetRenderFlags( RENDER_FLAG_FULL ); // not created fCreated = FALSE; fMapPanelDirty = TRUE; if ( fShowAssignmentMenu ) { // remove highlight on the parent menu UnHighLightBox( ghAssignmentBox ); } } } void AssignmentMenuMvtCallBack(MOUSE_REGION * pRegion, INT32 iReason ) { // mvt callback handler for assignment region INT32 iValue = -1; SOLDIERTYPE *pSoldier; iValue = MSYS_GetRegionUserData( pRegion, 0 ); pSoldier = GetSelectedAssignSoldier( FALSE ); if( HandleAssignmentExpansionAndHighLightForAssignMenu( pSoldier ) == TRUE ) { return; } if (iReason & MSYS_CALLBACK_REASON_GAIN_MOUSE ) { // is the line shaded?..if so, don't highlight if( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__EPC ) { if( GetBoxShadeFlag( ghEpcBox, iValue ) == FALSE ) { HighLightBoxLine( ghEpcBox, iValue ); } } else { if( GetBoxShadeFlag( ghAssignmentBox, iValue ) == FALSE ) { HighLightBoxLine( ghAssignmentBox, iValue ); } } } else if (iReason & MSYS_CALLBACK_REASON_LOST_MOUSE ) { if( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__EPC ) { // unhighlight all strings in box UnHighLightBox( ghEpcBox ); } else { // unhighlight all strings in box UnHighLightBox( ghAssignmentBox ); } } } void RemoveMercMenuMvtCallBack(MOUSE_REGION * pRegion, INT32 iReason ) { // mvt callback handler for assignment region INT32 iValue = -1; iValue = MSYS_GetRegionUserData( pRegion, 0 ); if (iReason & MSYS_CALLBACK_REASON_GAIN_MOUSE ) { // highlight string // get the string line handle // is the line shaded?..if so, don't highlight if( GetBoxShadeFlag( ghRemoveMercAssignBox, iValue ) == FALSE ) { HighLightBoxLine( ghRemoveMercAssignBox, iValue ); } } else if (iReason & MSYS_CALLBACK_REASON_LOST_MOUSE ) { // unhighlight all strings in box UnHighLightBox( ghRemoveMercAssignBox ); } } void ContractMenuMvtCallback(MOUSE_REGION * pRegion, INT32 iReason ) { // mvt callback handler for Contract region INT32 iValue = -1; iValue = MSYS_GetRegionUserData( pRegion, 0 ); if (iReason & MSYS_CALLBACK_REASON_GAIN_MOUSE ) { // highlight string if( iValue != CONTRACT_MENU_CURRENT_FUNDS ) { if( GetBoxShadeFlag( ghContractBox, iValue ) == FALSE ) { // get the string line handle HighLightBoxLine( ghContractBox, iValue ); } } } else if (iReason & MSYS_CALLBACK_REASON_LOST_MOUSE ) { // unhighlight all strings in box UnHighLightBox( ghContractBox ); } } void SquadMenuMvtCallBack(MOUSE_REGION * pRegion, INT32 iReason ) { // mvt callback handler for assignment region INT32 iValue = -1; iValue = MSYS_GetRegionUserData( pRegion, 0 ); if (iReason & MSYS_CALLBACK_REASON_GAIN_MOUSE ) { // highlight string if( iValue != SQUAD_MENU_CANCEL ) { if( GetBoxShadeFlag( ghSquadBox, iValue ) == FALSE ) { // get the string line handle HighLightBoxLine( ghSquadBox, iValue ); } } else { // highlight cancel line HighLightBoxLine( ghSquadBox, GetNumberOfLinesOfTextInBox( ghSquadBox ) - 1 ); } } else if (iReason & MSYS_CALLBACK_REASON_LOST_MOUSE ) { // unhighlight all strings in box UnHighLightBox( ghSquadBox ); // update based on current squad HandleShadingOfLinesForSquadMenu( ); } } void RemoveMercMenuBtnCallback( MOUSE_REGION * pRegion, INT32 iReason ) { // btn callback handler for contract region INT32 iValue = -1; SOLDIERTYPE * pSoldier = NULL; pSoldier = GetSelectedAssignSoldier( FALSE ); iValue = MSYS_GetRegionUserData( pRegion, 0 ); if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP) { switch( iValue ) { case( REMOVE_MERC_CANCEL ): // stop showing menus fShowAssignmentMenu = FALSE; fShowContractMenu = FALSE; // reset characters bSelectedAssignChar = -1; bSelectedContractChar = -1; giAssignHighLine = -1; // dirty regions fCharacterInfoPanelDirty = TRUE; fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; gfRenderPBInterface = TRUE; // stop contratc glow if we are fGlowContractRegion = FALSE; giContractHighLine = -1; break; case( REMOVE_MERC ): StrategicRemoveMerc( pSoldier ); // dirty region fCharacterInfoPanelDirty = TRUE; fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; gfRenderPBInterface = TRUE; // stop contratc glow if we are fGlowContractRegion = FALSE; giContractHighLine = -1; // reset selected characters bSelectedAssignChar = -1; bSelectedContractChar = -1; giAssignHighLine = -1; // stop showing menus fShowAssignmentMenu = FALSE; fShowContractMenu = FALSE; //Def: 10/13/99: When a merc is either dead or a POW, the Remove Merc popup comes up instead of the // Assign menu popup. When the the user removes the merc, we need to make sure the assignment menu //doesnt come up ( because the both assign menu and remove menu flags are needed for the remove pop up to appear //dont ask why?!! ) fShownContractMenu = FALSE; fShownAssignmentMenu = FALSE; fShowRemoveMenu = FALSE; break; } } } void BeginRemoveMercFromContract( SOLDIERTYPE *pSoldier ) { // This function will setup the quote, then start dialogue beginning the actual leave sequence if( ( pSoldier->stats.bLife > 0 ) && ( pSoldier->bAssignment != ASSIGNMENT_POW ) ) { if( ( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__MERC ) || ( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__NPC ) ) { HandleImportantMercQuote( pSoldier, QUOTE_RESPONSE_TO_MIGUEL_SLASH_QUOTE_MERC_OR_RPC_LETGO ); SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,0 ,MAP_SCREEN ,0 ,0 ,0 ); TacticalCharacterDialogueWithSpecialEvent( pSoldier, 0, DIALOGUE_SPECIAL_EVENT_CONTRACT_ENDING, 1,0 ); } else // quote is different if he's fired in less than 48 hours if( ( GetWorldTotalMin() - pSoldier->uiTimeOfLastContractUpdate ) < 60 * 48 ) { SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,1 ,MAP_SCREEN ,0 ,0 ,0 ); if( ( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__AIM_MERC ) ) { // Only do this if they want to renew..... if ( WillMercRenew( pSoldier, FALSE ) ) { HandleImportantMercQuote( pSoldier, QUOTE_DEPART_COMMET_CONTRACT_NOT_RENEWED_OR_TERMINATED_UNDER_48 ); } } SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,0 ,MAP_SCREEN ,0 ,0 ,0 ); TacticalCharacterDialogueWithSpecialEvent( pSoldier, 0, DIALOGUE_SPECIAL_EVENT_CONTRACT_ENDING, 1,0 ); } else { SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,1 ,MAP_SCREEN ,0 ,0 ,0 ); if( ( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__AIM_MERC ) ) { // Only do this if they want to renew..... if ( WillMercRenew( pSoldier, FALSE ) ) { HandleImportantMercQuote( pSoldier, QUOTE_DEPARTING_COMMENT_CONTRACT_NOT_RENEWED_OR_48_OR_MORE ); } } else if( ( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__MERC ) || ( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__NPC ) ) { HandleImportantMercQuote( pSoldier, QUOTE_RESPONSE_TO_MIGUEL_SLASH_QUOTE_MERC_OR_RPC_LETGO ); } SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_LOCK_INTERFACE,0 ,MAP_SCREEN ,0 ,0 ,0 ); TacticalCharacterDialogueWithSpecialEvent( pSoldier, 0, DIALOGUE_SPECIAL_EVENT_CONTRACT_ENDING, 1,0 ); } if( ( GetWorldTotalMin() - pSoldier->uiTimeOfLastContractUpdate ) < 60 * 3 ) { // this will cause him give us lame excuses for a while until he gets over it // 3-6 days (but the first 1-2 days of that are spent "returning" home) gMercProfiles[ pSoldier->ubProfile ].ubDaysOfMoraleHangover = (UINT8) (3 + Random(4)); // if it's an AIM merc, word of this gets back to AIM... Bad rep. if( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__AIM_MERC ) { ModifyPlayerReputation(REPUTATION_EARLY_FIRING); } } } } void MercDismissConfirmCallBack( UINT8 bExitValue ) { if ( bExitValue == MSG_BOX_RETURN_YES ) { // Setup history code gpDismissSoldier->ubLeaveHistoryCode = HISTORY_MERC_FIRED; BeginRemoveMercFromContract( gpDismissSoldier ); } } void ContractMenuBtnCallback( MOUSE_REGION * pRegion, INT32 iReason ) { // btn callback handler for contract region INT32 iValue = -1; BOOLEAN fOkToClose = FALSE; SOLDIERTYPE * pSoldier = NULL; if ( (guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) ) { pSoldier = &Menptr[ gCharactersList[ bSelectedInfoChar ].usSolID ]; } else { // can't renew contracts from tactical! } AssertNotNIL( pSoldier ); AssertT( pSoldier->bActive ); iValue = MSYS_GetRegionUserData( pRegion, 0 ); if (iReason & MSYS_CALLBACK_REASON_RBUTTON_UP) { fOkToClose = TRUE; } if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP) { if( GetBoxShadeFlag( ghContractBox, iValue ) == TRUE ) { // not valid return; } if( iValue == CONTRACT_MENU_CANCEL ) { // reset contract character and contract highlight line giContractHighLine =-1; bSelectedContractChar = -1; fGlowContractRegion = FALSE; fShowContractMenu = FALSE; // dirty region fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; fCharacterInfoPanelDirty = TRUE; gfRenderPBInterface = TRUE; if ( gfInContractMenuFromRenewSequence ) { BeginRemoveMercFromContract( pSoldier ); } return; } // else handle based on contract switch( iValue ) { case( CONTRACT_MENU_DAY ): MercContractHandling( pSoldier, CONTRACT_EXTEND_1_DAY ); PostContractMessage( pSoldier, CONTRACT_EXTEND_1_DAY ); fOkToClose = TRUE; break; case( CONTRACT_MENU_WEEK ): MercContractHandling( pSoldier, CONTRACT_EXTEND_1_WEEK ); PostContractMessage( pSoldier, CONTRACT_EXTEND_1_WEEK ); fOkToClose = TRUE; break; case( CONTRACT_MENU_TWO_WEEKS ): MercContractHandling( pSoldier, CONTRACT_EXTEND_2_WEEK ); PostContractMessage( pSoldier, CONTRACT_EXTEND_2_WEEK ); fOkToClose = TRUE; break; case( CONTRACT_MENU_TERMINATE ): gpDismissSoldier = pSoldier; // If in the renewal sequence.. do right away... // else put up requester. if ( gfInContractMenuFromRenewSequence ) { MercDismissConfirmCallBack( MSG_BOX_RETURN_YES ); } else { // The game should be unpaused when this message box disappears UnPauseGame(); DoMapMessageBox( MSG_BOX_BASIC_STYLE, gzLateLocalizedString[ 48 ], MAP_SCREEN, MSG_BOX_FLAG_YESNO, MercDismissConfirmCallBack ); } fOkToClose = TRUE; break; } pProcessingSoldier = NULL; fProcessingAMerc = FALSE; } if( fOkToClose == TRUE ) { // reset contract character and contract highlight line giContractHighLine =-1; bSelectedContractChar = -1; fGlowContractRegion = FALSE; fShowContractMenu = FALSE; // dirty region fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; fCharacterInfoPanelDirty = TRUE; gfRenderPBInterface = TRUE; } return; } void TrainingMenuMvtCallBack(MOUSE_REGION * pRegion, INT32 iReason ) { // mvt callback handler for assignment region INT32 iValue = -1; iValue = MSYS_GetRegionUserData( pRegion, 0 ); if( HandleAssignmentExpansionAndHighLightForTrainingMenu( ) == TRUE ) { return; } if (iReason & MSYS_CALLBACK_REASON_GAIN_MOUSE ) { // highlight string // do not highlight current balance if( GetBoxShadeFlag( ghTrainingBox, iValue ) == FALSE ) { // get the string line handle HighLightBoxLine( ghTrainingBox, iValue ); } } else if (iReason & MSYS_CALLBACK_REASON_LOST_MOUSE ) { // unhighlight all strings in box UnHighLightBox( ghTrainingBox ); } } void AttributeMenuMvtCallBack(MOUSE_REGION * pRegion, INT32 iReason ) { // mvt callback handler for assignment region INT32 iValue = -1; iValue = MSYS_GetRegionUserData( pRegion, 0 ); if (iReason & MSYS_CALLBACK_REASON_GAIN_MOUSE ) { // highlight string if( GetBoxShadeFlag( ghAttributeBox, iValue ) == FALSE ) { // get the string line handle HighLightBoxLine( ghAttributeBox, iValue ); } } else if (iReason & MSYS_CALLBACK_REASON_LOST_MOUSE ) { // unhighlight all strings in box UnHighLightBox( ghAttributeBox ); } } void SquadMenuBtnCallback( MOUSE_REGION * pRegion, INT32 iReason ) { // btn callback handler for assignment region INT32 iValue = -1; SOLDIERTYPE * pSoldier = NULL; CHAR16 sString[ 128 ]; INT8 bCanJoinSquad; /* ARM: Squad menu is now disabled for anyone between sectors UINT8 ubNextX, ubNextY, ubPrevX, ubPrevY; UINT32 uiTraverseTime, uiArriveTime; INT32 iOldSquadValue = -1; BOOLEAN fCharacterWasBetweenSectors = FALSE; */ pSoldier = GetSelectedAssignSoldier( FALSE ); iValue = MSYS_GetRegionUserData( pRegion, 0 ); if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP) { if( iValue == SQUAD_MENU_CANCEL ) { // stop displaying, leave fShowSquadMenu = FALSE; // unhighlight the assignment box UnHighLightBox( ghAssignmentBox ); // dirty region fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; fCharacterInfoPanelDirty = TRUE; gfRenderPBInterface = TRUE; return; } bCanJoinSquad = CanCharacterSquad( pSoldier, ( INT8 )iValue ); // can the character join this squad? (If already in it, accept that as a legal choice and exit menu) if ( ( bCanJoinSquad == CHARACTER_CAN_JOIN_SQUAD ) || ( bCanJoinSquad == CHARACTER_CANT_JOIN_SQUAD_ALREADY_IN_IT ) ) { if ( bCanJoinSquad == CHARACTER_CAN_JOIN_SQUAD ) { // able to add, do it /* ARM: Squad menu is now disabled for anyone between sectors // old squad character was in iOldSquadValue = SquadCharacterIsIn( pSoldier ); // grab if char was between sectors fCharacterWasBetweenSectors = pSoldier->flags.fBetweenSectors; if( fCharacterWasBetweenSectors ) { if( pSoldier->bAssignment == VEHICLE ) { if( GetNumberInVehicle( pSoldier->iVehicleId ) == 1 ) { // can't change, go away return; } } } if( pSoldier->ubGroupID ) { GetGroupPosition(&ubNextX, &ubNextY, &ubPrevX, &ubPrevY, &uiTraverseTime, &uiArriveTime, pSoldier->ubGroupID ); } */ pSoldier->bOldAssignment = pSoldier->bAssignment; if( pSoldier->bOldAssignment == VEHICLE ) { TakeSoldierOutOfVehicle( pSoldier ); } AddCharacterToSquad( pSoldier, ( INT8 )iValue ); if( pSoldier->bOldAssignment == VEHICLE ) { SetSoldierExitVehicleInsertionData( pSoldier, pSoldier->iVehicleId ); } //Clear any desired squad assignments -- seeing the player has physically changed it! pSoldier->ubNumTraversalsAllowedToMerge = 0; pSoldier->ubDesiredSquadAssignment = NO_ASSIGNMENT; /* ARM: Squad menu is now disabled for anyone between sectors if( fCharacterWasBetweenSectors ) { // grab location of old squad and set this value for new squad if( iOldSquadValue != -1 ) { GetSquadPosition( &ubNextX, &ubNextY, &ubPrevX, &ubPrevY, &uiTraverseTime, &uiArriveTime, ( UINT8 )iOldSquadValue ); } SetGroupPosition( ubNextX, ubNextY, ubPrevX, ubPrevY, uiTraverseTime, uiArriveTime, pSoldier->ubGroupID ); } */ MakeSoldiersTacticalAnimationReflectAssignment( pSoldier ); } // stop displaying, leave fShowAssignmentMenu = FALSE; giAssignHighLine = -1; // dirty region fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; fCharacterInfoPanelDirty = TRUE; gfRenderPBInterface = TRUE; } else { BOOLEAN fDisplayError = TRUE; switch( bCanJoinSquad ) { case CHARACTER_CANT_JOIN_SQUAD_SQUAD_MOVING: swprintf( sString, pMapErrorString[ 36 ], pSoldier->name, pLongAssignmentStrings[ iValue ] ); break; case CHARACTER_CANT_JOIN_SQUAD_VEHICLE: swprintf( sString, pMapErrorString[ 37 ], pSoldier->name ); break; case CHARACTER_CANT_JOIN_SQUAD_TOO_FAR: swprintf( sString, pMapErrorString[ 20 ], pSoldier->name, pLongAssignmentStrings[ iValue ] ); break; case CHARACTER_CANT_JOIN_SQUAD_FULL: swprintf( sString, pMapErrorString[ 19 ], pSoldier->name, pLongAssignmentStrings[ iValue ] ); break; default: // generic "you can't join this squad" msg swprintf( sString, pMapErrorString[ 38 ], pSoldier->name, pLongAssignmentStrings[ iValue ] ); break; } if ( fDisplayError ) { DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL); } } // set this assignment for the list too SetAssignmentForList( ( INT8 ) iValue, 0 ); } return; } void TrainingMenuBtnCallback( MOUSE_REGION * pRegion, INT32 iReason ) { // btn callback handler for assignment region INT32 iValue = -1; SOLDIERTYPE * pSoldier = NULL; INT8 bTownId; CHAR16 sString[ 128 ]; CHAR16 sStringA[ 128 ]; BOOLEAN fCanTrainMilitia = TRUE; pSoldier = GetSelectedAssignSoldier( FALSE ); iValue = MSYS_GetRegionUserData( pRegion, 0 ); if( ( iReason & MSYS_CALLBACK_REASON_LBUTTON_DWN ) || ( iReason & MSYS_CALLBACK_REASON_RBUTTON_DWN ) ) { if ( (guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) && !fShowMapInventoryPool ) { UnMarkButtonDirty( giMapBorderButtons[ MAP_BORDER_TOWN_BTN ] ); } } if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP) { if( fShowAttributeMenu ) { // cancel attribute submenu fShowAttributeMenu = FALSE; // rerender tactical stuff gfRenderPBInterface = TRUE; return; } switch( iValue ) { case( TRAIN_MENU_SELF): // practise in stat gbTrainingMode = TRAIN_SELF; // show menu fShowAttributeMenu = TRUE; DetermineBoxPositions( ); break; case( TRAIN_MENU_TOWN): if( BasicCanCharacterTrainMilitia(pSoldier) ) { bTownId = GetTownIdForSector( pSoldier->sSectorX, pSoldier->sSectorY ); // if it's a town sector (the following 2 errors can't happen at non-town SAM sites) if( bTownId != BLANK_SECTOR ) { // can we keep militia in this town? if( MilitiaTrainingAllowedInSector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ) == FALSE ) { swprintf( sString, pMapErrorString[ 31 ], pTownNames[ bTownId ] ); DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL ); break; } // is the current loyalty high enough to train some? if( DoesSectorMercIsInHaveSufficientLoyaltyToTrainMilitia( pSoldier ) == FALSE ) { DoScreenIndependantMessageBox( zMarksMapScreenText[ 20 ], MSG_BOX_FLAG_OK, NULL ); break; } } // Kaiden: Roaming Militia Training: if(IsMilitiaTrainableFromSoldiersSectorMaxed( pSoldier, ELITE_MILITIA )) if (!gGameExternalOptions.gfmusttrainroaming) fCanTrainMilitia = FALSE; else if (GetWorldDay( ) < gGameExternalOptions.guiAllowMilitiaGroupsDelay) fCanTrainMilitia = FALSE; else if (IsThisSectorASAMSector(pSoldier->sSectorX,pSoldier->sSectorY,0 )) fCanTrainMilitia = FALSE; else fCanTrainMilitia = TRUE; if(!fCanTrainMilitia) { if( bTownId == BLANK_SECTOR ) { // SAM site GetShortSectorString( pSoldier->sSectorX, pSoldier->sSectorY, sStringA ); swprintf( sString, zMarksMapScreenText[ 21 ], sStringA ); } else { // town swprintf( sString, zMarksMapScreenText[ 21 ], pTownNames[ bTownId ] ); } DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL ); break; } if ( CountMilitiaTrainersInSoldiersSector( pSoldier ) >= gGameExternalOptions.ubMaxMilitiaTrainersPerSector ) { swprintf( sString, gzLateLocalizedString[ 47 ], gGameExternalOptions.ubMaxMilitiaTrainersPerSector ); DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL ); break; } // PASSED ALL THE TESTS - ALLOW SOLDIER TO TRAIN MILITIA HERE pSoldier->bOldAssignment = pSoldier->bAssignment; if( ( pSoldier->bAssignment != TRAIN_TOWN ) ) { SetTimeOfAssignmentChangeForMerc( pSoldier ); } MakeSoldiersTacticalAnimationReflectAssignment( pSoldier ); // stop showing menu fShowAssignmentMenu = FALSE; giAssignHighLine = -1; // remove from squad if( pSoldier->bOldAssignment == VEHICLE ) { TakeSoldierOutOfVehicle( pSoldier ); } RemoveCharacterFromSquads( pSoldier ); ChangeSoldiersAssignment( pSoldier, TRAIN_TOWN ); // assign to a movement group AssignMercToAMovementGroup( pSoldier ); if( SectorInfo[ SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ) ].fMilitiaTrainingPaid == FALSE ) { // show a message to confirm player wants to charge cost HandleInterfaceMessageForCostOfTrainingMilitia( pSoldier ); } else { SetAssignmentForList( TRAIN_TOWN, 0 ); } gfRenderPBInterface = TRUE; } break; case( TRAIN_MENU_TEAMMATES): if( CanCharacterTrainTeammates( pSoldier ) == TRUE ) { // train teammates gbTrainingMode = TRAIN_TEAMMATE; // show menu fShowAttributeMenu = TRUE; DetermineBoxPositions( ); } break; case( TRAIN_MENU_TRAIN_BY_OTHER ): if( CanCharacterBeTrainedByOther( pSoldier ) == TRUE ) { // train teammates gbTrainingMode = TRAIN_BY_OTHER; // show menu fShowAttributeMenu = TRUE; DetermineBoxPositions( ); } break; case( TRAIN_MENU_CANCEL ): // stop showing menu fShowTrainingMenu = FALSE; // unhighlight the assignment box UnHighLightBox( ghAssignmentBox ); // reset list ResetSelectedListForMapScreen( ); gfRenderPBInterface = TRUE; break; } fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; } else if( iReason & MSYS_CALLBACK_REASON_RBUTTON_UP ) { if( fShowAttributeMenu ) { // cancel attribute submenu fShowAttributeMenu = FALSE; // rerender tactical stuff gfRenderPBInterface = TRUE; return; } } } void AttributesMenuBtnCallback( MOUSE_REGION * pRegion, INT32 iReason ) { // btn callback handler for assignment region INT32 iValue = -1; SOLDIERTYPE * pSoldier = NULL; pSoldier = GetSelectedAssignSoldier( FALSE ); iValue = MSYS_GetRegionUserData( pRegion, 0 ); if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP) { if( iValue == ATTRIB_MENU_CANCEL ) { // cancel, leave // stop showing menu fShowAttributeMenu = FALSE; // unhighlight the training box UnHighLightBox( ghTrainingBox ); } else if( CanCharacterTrainStat( pSoldier, ( INT8 )( iValue ), ( BOOLEAN )( ( gbTrainingMode == TRAIN_SELF ) || ( gbTrainingMode == TRAIN_BY_OTHER ) ), ( BOOLEAN )( gbTrainingMode == TRAIN_TEAMMATE ) ) ) { pSoldier->bOldAssignment = pSoldier->bAssignment; if( ( pSoldier->bAssignment != gbTrainingMode ) ) { SetTimeOfAssignmentChangeForMerc( pSoldier ); } // set stat to train pSoldier->bTrainStat = ( INT8 )iValue; MakeSoldiersTacticalAnimationReflectAssignment( pSoldier ); // stop showing ALL menus fShowAssignmentMenu = FALSE; giAssignHighLine = -1; // remove from squad/vehicle if( pSoldier->bOldAssignment == VEHICLE ) { TakeSoldierOutOfVehicle( pSoldier ); } RemoveCharacterFromSquads( pSoldier ); // train stat ChangeSoldiersAssignment( pSoldier, gbTrainingMode ); // assign to a movement group AssignMercToAMovementGroup( pSoldier ); // set assignment for group SetAssignmentForList( gbTrainingMode, ( INT8 )iValue ); } // rerender tactical stuff gfRenderPBInterface = TRUE; fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; } }; void AssignmentMenuBtnCallback( MOUSE_REGION * pRegion, INT32 iReason ) { // btn callback handler for assignment region INT32 iValue = -1; CHAR16 sString[ 128 ]; SOLDIERTYPE * pSoldier = NULL; pSoldier = GetSelectedAssignSoldier( FALSE ); iValue = MSYS_GetRegionUserData( pRegion, 0 ); if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP) { if( ( fShowAttributeMenu )||( fShowTrainingMenu ) || ( fShowRepairMenu ) || ( fShowVehicleMenu ) ||( fShowSquadMenu ) ) { return; } UnHighLightBox( ghAssignmentBox ); if( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__EPC ) { switch( iValue ) { case( EPC_MENU_ON_DUTY ): if( CanCharacterOnDuty( pSoldier ) ) { // put character on a team fShowSquadMenu = TRUE; fShowTrainingMenu = FALSE; fShowVehicleMenu = FALSE; fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; } break; case( EPC_MENU_PATIENT ): // can character doctor? if( CanCharacterPatient( pSoldier ) ) { /* Assignment distance limits removed. Sep/11/98. ARM if( IsSoldierCloseEnoughToADoctor( pSoldier ) == FALSE ) { return; } */ pSoldier->bOldAssignment = pSoldier->bAssignment; if( ( pSoldier->bAssignment != PATIENT ) ) { SetTimeOfAssignmentChangeForMerc( pSoldier ); } // stop showing menu fShowAssignmentMenu = FALSE; giAssignHighLine = -1; MakeSoldiersTacticalAnimationReflectAssignment( pSoldier ); // set dirty flag fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; // remove from squad if ( pSoldier->bOldAssignment == VEHICLE ) { TakeSoldierOutOfVehicle( pSoldier ); } RemoveCharacterFromSquads( pSoldier ); ChangeSoldiersAssignment( pSoldier, PATIENT ); AssignMercToAMovementGroup( pSoldier ); // set assignment for group SetAssignmentForList( ( INT8 ) PATIENT, 0 ); } break; case( EPC_MENU_VEHICLE ): if ( CanCharacterVehicle( pSoldier ) ) { if( DisplayVehicleMenu( pSoldier ) ) { fShowVehicleMenu = TRUE; ShowBox( ghVehicleBox ); } else { fShowVehicleMenu = FALSE; } } break; case( EPC_MENU_REMOVE ): fShowAssignmentMenu = FALSE; UnEscortEPC( pSoldier ); break; case( EPC_MENU_CANCEL ): fShowAssignmentMenu = FALSE; giAssignHighLine = -1; // set dirty flag fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; // reset list of characters ResetSelectedListForMapScreen( ); break; } } else { switch( iValue ) { case( ASSIGN_MENU_ON_DUTY ): if( CanCharacterOnDuty( pSoldier ) ) { // put character on a team fShowSquadMenu = TRUE; fShowTrainingMenu = FALSE; fShowVehicleMenu = FALSE; fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; fShowRepairMenu = FALSE; } break; case( ASSIGN_MENU_DOCTOR ): // can character doctor? if( CanCharacterDoctor( pSoldier ) ) { // stop showing menu fShowAssignmentMenu = FALSE; giAssignHighLine = -1; pSoldier->bOldAssignment = pSoldier->bAssignment; if( ( pSoldier->bAssignment != DOCTOR ) ) { SetTimeOfAssignmentChangeForMerc( pSoldier ); } // remove from squad if( pSoldier->bOldAssignment == VEHICLE ) { TakeSoldierOutOfVehicle( pSoldier ); } RemoveCharacterFromSquads( pSoldier ); ChangeSoldiersAssignment( pSoldier, DOCTOR ); MakeSureMedKitIsInHand( pSoldier ); AssignMercToAMovementGroup( pSoldier ); MakeSoldiersTacticalAnimationReflectAssignment( pSoldier ); // set dirty flag fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; // set assignment for group SetAssignmentForList( ( INT8 ) DOCTOR, 0 ); } else if( CanCharacterDoctorButDoesntHaveMedKit( pSoldier ) ) { fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; swprintf( sString, zMarksMapScreenText[ 19 ], pSoldier->name ); DoScreenIndependantMessageBox( sString , MSG_BOX_FLAG_OK, NULL ); } break; case( ASSIGN_MENU_PATIENT ): // can character patient? if( CanCharacterPatient( pSoldier ) ) { /* Assignment distance limits removed. Sep/11/98. ARM if( IsSoldierCloseEnoughToADoctor( pSoldier ) == FALSE ) { return; } */ pSoldier->bOldAssignment = pSoldier->bAssignment; if( ( pSoldier->bAssignment != PATIENT ) ) { SetTimeOfAssignmentChangeForMerc( pSoldier ); } MakeSoldiersTacticalAnimationReflectAssignment( pSoldier ); // stop showing menu fShowAssignmentMenu = FALSE; giAssignHighLine = -1; // set dirty flag fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; // remove from squad if( pSoldier->bOldAssignment == VEHICLE ) { TakeSoldierOutOfVehicle( pSoldier ); } RemoveCharacterFromSquads( pSoldier ); ChangeSoldiersAssignment( pSoldier, PATIENT ); AssignMercToAMovementGroup( pSoldier ); // set assignment for group SetAssignmentForList( ( INT8 ) PATIENT, 0 ); } break; case( ASSIGN_MENU_VEHICLE ): if ( CanCharacterVehicle( pSoldier ) ) { if( DisplayVehicleMenu( pSoldier ) ) { fShowVehicleMenu = TRUE; ShowBox( ghVehicleBox ); } else { fShowVehicleMenu = FALSE; } } break; case( ASSIGN_MENU_REPAIR ): if( CanCharacterRepair( pSoldier ) ) { fShowSquadMenu = FALSE; fShowTrainingMenu = FALSE; fShowVehicleMenu = FALSE; fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; pSoldier->bOldAssignment = pSoldier->bAssignment; if( pSoldier->bSectorZ ==0 ) { fShowRepairMenu = FALSE; if( DisplayRepairMenu( pSoldier ) ) { fShowRepairMenu = TRUE; } } } else if( CanCharacterRepairButDoesntHaveARepairkit( pSoldier ) ) { fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; swprintf( sString, zMarksMapScreenText[ 18 ], pSoldier->name ); DoScreenIndependantMessageBox( sString , MSG_BOX_FLAG_OK, NULL ); } break; case( ASSIGN_MENU_TRAIN ): if( CanCharacterPractise( pSoldier ) ) { fShowTrainingMenu = TRUE; DetermineBoxPositions( ); fShowSquadMenu = FALSE; fShowVehicleMenu = FALSE; fShowRepairMenu = FALSE; fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; } break; case( ASSIGN_MENU_CANCEL ): fShowAssignmentMenu = FALSE; giAssignHighLine = -1; // set dirty flag fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; // reset list of characters ResetSelectedListForMapScreen( ); break; } } gfRenderPBInterface = TRUE; } else if( iReason & MSYS_CALLBACK_REASON_RBUTTON_UP ) { if( ( fShowAttributeMenu )||( fShowTrainingMenu ) || ( fShowRepairMenu ) || ( fShowVehicleMenu ) ||( fShowSquadMenu ) ) { fShowAttributeMenu = FALSE; fShowTrainingMenu = FALSE; fShowRepairMenu = FALSE; fShowVehicleMenu = FALSE; fShowSquadMenu = FALSE; // rerender tactical stuff gfRenderPBInterface = TRUE; // set dirty flag fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; return; } } } void RestorePopUpBoxes( void ) { ContractPosition.iX = OrigContractPosition.iX; AttributePosition.iX = OrigAttributePosition.iX; SquadPosition.iX = OrigSquadPosition.iX ; AssignmentPosition.iX = OrigAssignmentPosition.iX ; TrainPosition.iX = OrigTrainPosition.iX; VehiclePosition.iX = OrigVehiclePosition.iX; return; } void CreateSquadBox( void ) { // will create a pop up box for squad selection SGPPoint pPoint; SGPRect pDimensions; UINT32 hStringHandle; UINT32 uiCounter; CHAR16 sString[ 64 ]; UINT32 uiMaxSquad; // create basic box CreatePopUpBox(&ghSquadBox, SquadDimensions, SquadPosition, (POPUP_BOX_FLAG_CLIP_TEXT|POPUP_BOX_FLAG_RESIZE )); // which buffer will box render to SetBoxBuffer(ghSquadBox, FRAME_BUFFER); // border type? SetBorderType(ghSquadBox,guiPOPUPBORDERS); // background texture SetBackGroundSurface(ghSquadBox, guiPOPUPTEX); // margin sizes SetMargins( ghSquadBox, 6, 6, 4, 4 ); // space between lines SetLineSpace(ghSquadBox, 2); // set current box to this one SetCurrentBox( ghSquadBox ); uiMaxSquad = GetLastSquadListedInSquadMenu(); // add strings for box for(uiCounter=0; uiCounter <= uiMaxSquad; uiCounter++) { // get info about current squad and put in string swprintf( sString, L"%s ( %d/%d )", pSquadMenuStrings[uiCounter], NumberOfPeopleInSquad( ( INT8 )uiCounter ), NUMBER_OF_SOLDIERS_PER_SQUAD ); AddMonoString(&hStringHandle, sString ); // make sure it is unhighlighted UnHighLightLine(hStringHandle); } // add cancel line AddMonoString(&hStringHandle, pSquadMenuStrings[ NUMBER_OF_SQUADS ]); // set font type SetBoxFont(ghSquadBox, MAP_SCREEN_FONT); // set highlight color SetBoxHighLight(ghSquadBox, FONT_WHITE); // unhighlighted color SetBoxForeground(ghSquadBox, FONT_LTGREEN); // the secondary shade color SetBoxSecondaryShade( ghSquadBox, FONT_YELLOW ); // background color SetBoxBackground(ghSquadBox, FONT_BLACK); // shaded color..for darkened text SetBoxShade( ghSquadBox, FONT_GRAY7 ); // resize box to text ResizeBoxToText( ghSquadBox ); DetermineBoxPositions( ); GetBoxPosition( ghSquadBox, &pPoint); GetBoxSize( ghSquadBox, &pDimensions ); if( giBoxY + pDimensions.iBottom > 479 ) { pPoint.iY = SquadPosition.iY = 479 - pDimensions.iBottom; } SetBoxPosition( ghSquadBox, pPoint ); } void CreateEPCBox( void ) { // will create a pop up box for squad selection SGPPoint pPoint; SGPRect pDimensions; UINT32 hStringHandle; INT32 iCount; // create basic box CreatePopUpBox(&ghEpcBox, SquadDimensions, AssignmentPosition, (POPUP_BOX_FLAG_CLIP_TEXT|POPUP_BOX_FLAG_RESIZE|POPUP_BOX_FLAG_CENTER_TEXT )); // which buffer will box render to SetBoxBuffer(ghEpcBox, FRAME_BUFFER); // border type? SetBorderType(ghEpcBox,guiPOPUPBORDERS); // background texture SetBackGroundSurface(ghEpcBox, guiPOPUPTEX); // margin sizes SetMargins( ghEpcBox, 6, 6, 4, 4 ); // space between lines SetLineSpace(ghEpcBox, 2); // set current box to this one SetCurrentBox( ghEpcBox ); for(iCount=0; iCount < MAX_EPC_MENU_STRING_COUNT; iCount++) { AddMonoString(&hStringHandle, pEpcMenuStrings[ iCount]); } // set font type SetBoxFont(ghEpcBox, MAP_SCREEN_FONT); // set highlight color SetBoxHighLight(ghEpcBox, FONT_WHITE); // unhighlighted color SetBoxForeground(ghEpcBox, FONT_LTGREEN); // background color SetBoxBackground(ghEpcBox, FONT_BLACK); // shaded color..for darkened text SetBoxShade( ghEpcBox, FONT_GRAY7 ); // resize box to text ResizeBoxToText( ghEpcBox ); GetBoxPosition( ghEpcBox, &pPoint); GetBoxSize( ghEpcBox, &pDimensions ); if( giBoxY + pDimensions.iBottom > 479 ) { pPoint.iY = AssignmentPosition.iY = 479 - pDimensions.iBottom; } SetBoxPosition( ghEpcBox, pPoint ); } void HandleShadingOfLinesForSquadMenu( void ) { // find current squad and set that line the squad box a lighter green UINT32 uiCounter; SOLDIERTYPE *pSoldier = NULL; UINT32 uiMaxSquad; INT8 bResult = 0; if ( ( fShowSquadMenu == FALSE ) || ( ghSquadBox == -1 ) ) { return; } pSoldier = GetSelectedAssignSoldier( FALSE ); uiMaxSquad = GetLastSquadListedInSquadMenu(); for( uiCounter = 0; uiCounter <= uiMaxSquad; uiCounter++ ) { if ( pSoldier != NULL ) { bResult = CanCharacterSquad( pSoldier, (INT8) uiCounter ); } // if no soldier, or a reason which doesn't have a good explanatory message if ( ( pSoldier == NULL ) || ( bResult == CHARACTER_CANT_JOIN_SQUAD ) ) { // darken /disable line ShadeStringInBox( ghSquadBox, uiCounter ); } else { if ( bResult == CHARACTER_CAN_JOIN_SQUAD ) { // legal squad, leave it green UnShadeStringInBox( ghSquadBox, uiCounter ); UnSecondaryShadeStringInBox( ghSquadBox, uiCounter ); } else { // unjoinable squad - yellow SecondaryShadeStringInBox( ghSquadBox, uiCounter ); } } } } void PostContractMessage( SOLDIERTYPE *pCharacter, INT32 iContract ) { // do nothing return; // send a message stating that offer of contract extension made //MapScreenMessage(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Offered to extend %s's contract by another %s.", pCharacter->name, pContractExtendStrings[ iContract ] ); return; } void PostTerminateMessage( SOLDIERTYPE *pCharacter ) { // do nothing return; // send a message stating that termination of contract done //MapScreenMessage(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s's contract has been terminated.", pCharacter->name ); } BOOLEAN DisplayVehicleMenu( SOLDIERTYPE *pSoldier ) { BOOLEAN fVehiclePresent=FALSE; INT32 iCounter=0; INT32 hStringHandle=0; // first, clear pop up box RemoveBox(ghVehicleBox); ghVehicleBox = -1; CreateVehicleBox(); SetCurrentBox(ghVehicleBox); // run through list of vehicles in sector and add them to pop up box for ( iCounter = 0; iCounter < ubNumberOfVehicles; iCounter++ ) { if ( pVehicleList[iCounter].fValid == TRUE ) { if ( IsThisVehicleAccessibleToSoldier( pSoldier, iCounter ) ) { AddMonoString((UINT32 *)&hStringHandle, pVehicleStrings[ pVehicleList[ iCounter ].ubVehicleType ]); fVehiclePresent = TRUE; } } } // cancel string (borrow the one in the squad menu) AddMonoString((UINT32 *)&hStringHandle, pSquadMenuStrings[ SQUAD_MENU_CANCEL ]); SetBoxFont(ghVehicleBox, MAP_SCREEN_FONT); SetBoxHighLight(ghVehicleBox, FONT_WHITE); SetBoxForeground(ghVehicleBox, FONT_LTGREEN); SetBoxBackground(ghVehicleBox, FONT_BLACK); return fVehiclePresent; } void CreateVehicleBox() { CreatePopUpBox(&ghVehicleBox, VehicleDimensions, VehiclePosition, (POPUP_BOX_FLAG_CLIP_TEXT|POPUP_BOX_FLAG_CENTER_TEXT|POPUP_BOX_FLAG_RESIZE )); SetBoxBuffer(ghVehicleBox, FRAME_BUFFER); SetBorderType(ghVehicleBox,guiPOPUPBORDERS); SetBackGroundSurface(ghVehicleBox, guiPOPUPTEX); SetMargins( ghVehicleBox, 6, 6, 4, 4 ); SetLineSpace(ghVehicleBox, 2); } void CreateRepairBox( void ) { CreatePopUpBox(&ghRepairBox, RepairDimensions, RepairPosition, (POPUP_BOX_FLAG_CLIP_TEXT|POPUP_BOX_FLAG_CENTER_TEXT|POPUP_BOX_FLAG_RESIZE )); SetBoxBuffer(ghRepairBox, FRAME_BUFFER); SetBorderType(ghRepairBox,guiPOPUPBORDERS); SetBackGroundSurface(ghRepairBox, guiPOPUPTEX); SetMargins( ghRepairBox, 6, 6, 4, 4 ); SetLineSpace(ghRepairBox, 2); } void CreateContractBox( SOLDIERTYPE *pCharacter ) { UINT32 hStringHandle; UINT32 uiCounter; CHAR16 sString[ 50 ]; CHAR16 sDollarString[ 50 ]; ContractPosition.iX = OrigContractPosition.iX; if( giBoxY != 0 ) { ContractPosition.iX = giBoxY; } CreatePopUpBox(&ghContractBox, ContractDimensions, ContractPosition, (POPUP_BOX_FLAG_CLIP_TEXT|POPUP_BOX_FLAG_RESIZE )); SetBoxBuffer(ghContractBox, FRAME_BUFFER); SetBorderType(ghContractBox,guiPOPUPBORDERS); SetBackGroundSurface(ghContractBox, guiPOPUPTEX); SetMargins( ghContractBox, 6, 6, 4, 4 ); SetLineSpace(ghContractBox, 2); // set current box to this one SetCurrentBox( ghContractBox ); // not null character? if( pCharacter != NULL ) { for(uiCounter=0; uiCounter < MAX_CONTRACT_MENU_STRING_COUNT; uiCounter++) { switch( uiCounter ) { case( CONTRACT_MENU_CURRENT_FUNDS ): /* // add current balance after title string swprintf( sDollarString, L"%d", LaptopSaveInfo.iCurrentBalance); InsertCommasForDollarFigure( sDollarString ); InsertDollarSignInToString( sDollarString ); swprintf( sString, L"%s %s", pContractStrings[uiCounter], sDollarString ); AddMonoString(&hStringHandle, sString); */ AddMonoString(&hStringHandle, pContractStrings[uiCounter]); break; case( CONTRACT_MENU_DAY ): if( pCharacter->ubWhatKindOfMercAmI != MERC_TYPE__AIM_MERC ) { swprintf( sDollarString, L"%d", 0 ); } else { swprintf( sDollarString, L"%d", gMercProfiles[ pCharacter->ubProfile ].sSalary ); } InsertCommasForDollarFigure( sDollarString ); InsertDollarSignInToString( sDollarString ); swprintf( sString, L"%s ( %s )", pContractStrings[uiCounter], sDollarString); AddMonoString(&hStringHandle, sString); break; case( CONTRACT_MENU_WEEK ): if( pCharacter->ubWhatKindOfMercAmI != MERC_TYPE__AIM_MERC ) { swprintf( sDollarString, L"%d", 0 ); } else { swprintf( sDollarString, L"%d", gMercProfiles[ pCharacter->ubProfile ].uiWeeklySalary ); } InsertCommasForDollarFigure( sDollarString ); InsertDollarSignInToString( sDollarString ); swprintf( sString, L"%s ( %s )", pContractStrings[uiCounter], sDollarString ); AddMonoString(&hStringHandle, sString); break; case( CONTRACT_MENU_TWO_WEEKS ): if( pCharacter->ubWhatKindOfMercAmI != MERC_TYPE__AIM_MERC ) { swprintf( sDollarString, L"%d", 0 ); } else { swprintf( sDollarString, L"%d", gMercProfiles[ pCharacter->ubProfile ].uiBiWeeklySalary ); } InsertCommasForDollarFigure( sDollarString ); InsertDollarSignInToString( sDollarString ); swprintf( sString, L"%s ( %s )", pContractStrings[uiCounter], sDollarString); AddMonoString(&hStringHandle, sString); break; default: AddMonoString(&hStringHandle, pContractStrings[uiCounter] ); break; } UnHighLightLine(hStringHandle); } } SetBoxFont(ghContractBox, MAP_SCREEN_FONT); SetBoxHighLight(ghContractBox, FONT_WHITE); SetBoxForeground(ghContractBox, FONT_LTGREEN); SetBoxBackground(ghContractBox, FONT_BLACK); // shaded color..for darkened text SetBoxShade( ghContractBox, FONT_GRAY7 ); if( pCharacter != NULL ) { // now set the color for the current balance value SetBoxLineForeground( ghContractBox, 0, FONT_YELLOW ); } // resize box to text ResizeBoxToText( ghContractBox ); } void CreateAttributeBox( void ) { UINT32 hStringHandle; UINT32 uiCounter; // will create attribute pop up menu for mapscreen assignments AttributePosition.iX = OrigAttributePosition.iX; if( giBoxY != 0 ) { AttributePosition.iY = giBoxY; } // update screen assignment positions UpdateMapScreenAssignmentPositions( ); // create basic box CreatePopUpBox(&ghAttributeBox, AttributeDimensions, AttributePosition, (POPUP_BOX_FLAG_CLIP_TEXT|POPUP_BOX_FLAG_CENTER_TEXT|POPUP_BOX_FLAG_RESIZE )); // which buffer will box render to SetBoxBuffer(ghAttributeBox, FRAME_BUFFER); // border type? SetBorderType(ghAttributeBox,guiPOPUPBORDERS); // background texture SetBackGroundSurface(ghAttributeBox, guiPOPUPTEX); // margin sizes SetMargins( ghAttributeBox, 6, 6, 4, 4 ); // space between lines SetLineSpace(ghAttributeBox, 2); // set current box to this one SetCurrentBox( ghAttributeBox ); // add strings for box for(uiCounter=0; uiCounter < MAX_ATTRIBUTE_STRING_COUNT; uiCounter++) { AddMonoString(&hStringHandle, pAttributeMenuStrings[uiCounter]); // make sure it is unhighlighted UnHighLightLine(hStringHandle); } // set font type SetBoxFont(ghAttributeBox, MAP_SCREEN_FONT); // set highlight color SetBoxHighLight(ghAttributeBox, FONT_WHITE); // unhighlighted color SetBoxForeground(ghAttributeBox, FONT_LTGREEN); // background color SetBoxBackground(ghAttributeBox, FONT_BLACK); // shaded color..for darkened text SetBoxShade( ghAttributeBox, FONT_GRAY7 ); // resize box to text ResizeBoxToText( ghAttributeBox ); } void CreateTrainingBox( void ) { UINT32 hStringHandle; UINT32 uiCounter; // will create attribute pop up menu for mapscreen assignments TrainPosition.iX = OrigTrainPosition.iX; if( giBoxY != 0 ) { TrainPosition.iY = giBoxY + ( ASSIGN_MENU_TRAIN * GetFontHeight( MAP_SCREEN_FONT ) ); } // create basic box CreatePopUpBox(&ghTrainingBox, TrainDimensions, TrainPosition, (POPUP_BOX_FLAG_CLIP_TEXT|POPUP_BOX_FLAG_CENTER_TEXT|POPUP_BOX_FLAG_RESIZE )); // which buffer will box render to SetBoxBuffer(ghTrainingBox, FRAME_BUFFER); // border type? SetBorderType(ghTrainingBox,guiPOPUPBORDERS); // background texture SetBackGroundSurface(ghTrainingBox, guiPOPUPTEX); // margin sizes SetMargins(ghTrainingBox, 6, 6, 4, 4 ); // space between lines SetLineSpace(ghTrainingBox, 2); // set current box to this one SetCurrentBox( ghTrainingBox ); // add strings for box for(uiCounter=0; uiCounter < MAX_TRAIN_STRING_COUNT; uiCounter++) { AddMonoString(&hStringHandle, pTrainingMenuStrings[uiCounter]); // make sure it is unhighlighted UnHighLightLine(hStringHandle); } // set font type SetBoxFont(ghTrainingBox, MAP_SCREEN_FONT); // set highlight color SetBoxHighLight(ghTrainingBox, FONT_WHITE); // unhighlighted color SetBoxForeground(ghTrainingBox, FONT_LTGREEN); // background color SetBoxBackground(ghTrainingBox, FONT_BLACK); // shaded color..for darkened text SetBoxShade( ghTrainingBox, FONT_GRAY7 ); // resize box to text ResizeBoxToText( ghTrainingBox ); DetermineBoxPositions( ); } void CreateAssignmentsBox( void ) { UINT32 hStringHandle; UINT32 uiCounter; CHAR16 sString[ 128 ]; SOLDIERTYPE *pSoldier = NULL; // will create attribute pop up menu for mapscreen assignments AssignmentPosition.iX = OrigAssignmentPosition.iX; if( giBoxY != 0 ) { AssignmentPosition.iY = giBoxY; } pSoldier = GetSelectedAssignSoldier( TRUE ); // pSoldier NULL is legal here! Gets called during every mapscreen initialization even when nobody is assign char // create basic box CreatePopUpBox(&ghAssignmentBox, AssignmentDimensions, AssignmentPosition, (POPUP_BOX_FLAG_CLIP_TEXT|POPUP_BOX_FLAG_CENTER_TEXT|POPUP_BOX_FLAG_RESIZE )); // which buffer will box render to SetBoxBuffer(ghAssignmentBox, FRAME_BUFFER); // border type? SetBorderType(ghAssignmentBox,guiPOPUPBORDERS); // background texture SetBackGroundSurface(ghAssignmentBox, guiPOPUPTEX); // margin sizes SetMargins(ghAssignmentBox, 6, 6, 4, 4 ); // space between lines SetLineSpace(ghAssignmentBox, 2); // set current box to this one SetCurrentBox( ghAssignmentBox ); // add strings for box for(uiCounter=0; uiCounter < MAX_ASSIGN_STRING_COUNT; uiCounter++) { // if we have a soldier, and this is the squad line if( ( uiCounter == ASSIGN_MENU_ON_DUTY ) && ( pSoldier != NULL ) && ( pSoldier->bAssignment < ON_DUTY ) ) { // show his squad # in brackets swprintf( sString, L"%s(%d)", pAssignMenuStrings[uiCounter], pSoldier->bAssignment + 1 ); } else { swprintf( sString, pAssignMenuStrings[uiCounter] ); } AddMonoString(&hStringHandle, sString ); // make sure it is unhighlighted UnHighLightLine(hStringHandle); } // set font type SetBoxFont(ghAssignmentBox, MAP_SCREEN_FONT); // set highlight color SetBoxHighLight(ghAssignmentBox, FONT_WHITE); // unhighlighted color SetBoxForeground(ghAssignmentBox, FONT_LTGREEN); // background color SetBoxBackground(ghAssignmentBox, FONT_BLACK); // shaded color..for darkened text SetBoxShade( ghAssignmentBox, FONT_GRAY7 ); SetBoxSecondaryShade( ghAssignmentBox, FONT_YELLOW ); // resize box to text ResizeBoxToText( ghAssignmentBox ); DetermineBoxPositions( ); } void CreateMercRemoveAssignBox( void ) { // will create remove mercbox to be placed in assignment area UINT32 hStringHandle; UINT32 uiCounter; // create basic box CreatePopUpBox(&ghRemoveMercAssignBox, AssignmentDimensions, AssignmentPosition, (POPUP_BOX_FLAG_CLIP_TEXT|POPUP_BOX_FLAG_CENTER_TEXT|POPUP_BOX_FLAG_RESIZE )); // which buffer will box render to SetBoxBuffer(ghRemoveMercAssignBox, FRAME_BUFFER); // border type? SetBorderType(ghRemoveMercAssignBox,guiPOPUPBORDERS); // background texture SetBackGroundSurface(ghRemoveMercAssignBox, guiPOPUPTEX); // margin sizes SetMargins( ghRemoveMercAssignBox, 6, 6, 4, 4 ); // space between lines SetLineSpace(ghRemoveMercAssignBox, 2); // set current box to this one SetCurrentBox( ghRemoveMercAssignBox ); // add strings for box for(uiCounter=0; uiCounter < MAX_REMOVE_MERC_COUNT; uiCounter++) { AddMonoString(&hStringHandle, pRemoveMercStrings[uiCounter]); // make sure it is unhighlighted UnHighLightLine(hStringHandle); } // set font type SetBoxFont(ghRemoveMercAssignBox, MAP_SCREEN_FONT); // set highlight color SetBoxHighLight(ghRemoveMercAssignBox, FONT_WHITE); // unhighlighted color SetBoxForeground(ghRemoveMercAssignBox, FONT_LTGREEN); // background color SetBoxBackground(ghRemoveMercAssignBox, FONT_BLACK); // shaded color..for darkened text SetBoxShade( ghRemoveMercAssignBox, FONT_GRAY7 ); // resize box to text ResizeBoxToText( ghRemoveMercAssignBox ); } BOOLEAN CreateDestroyAssignmentPopUpBoxes( void ) { static BOOLEAN fCreated= FALSE; VSURFACE_DESC vs_desc; VOBJECT_DESC VObjectDesc; if( ( fShowAssignmentMenu == TRUE ) && ( fCreated == FALSE ) ) { VObjectDesc.fCreateFlags=VOBJECT_CREATE_FROMFILE; FilenameForBPP("INTERFACE\\popup.sti", VObjectDesc.ImageFile); CHECKF(AddVideoObject(&VObjectDesc, &guiPOPUPBORDERS)); vs_desc.fCreateFlags = VSURFACE_CREATE_FROMFILE | VSURFACE_SYSTEM_MEM_USAGE; strcpy(vs_desc.ImageFile, "INTERFACE\\popupbackground.pcx"); CHECKF(AddVideoSurface(&vs_desc, &guiPOPUPTEX)); // these boxes are always created while in mapscreen... CreateEPCBox( ); CreateAssignmentsBox( ); CreateTrainingBox( ); CreateAttributeBox(); CreateVehicleBox(); CreateRepairBox( ); UpdateMapScreenAssignmentPositions( ); fCreated = TRUE; } else if( ( fShowAssignmentMenu == FALSE ) && ( fCreated == TRUE ) ) { DeleteVideoObjectFromIndex(guiPOPUPBORDERS); DeleteVideoSurfaceFromIndex(guiPOPUPTEX); RemoveBox(ghAttributeBox); ghAttributeBox = -1; RemoveBox(ghVehicleBox); ghVehicleBox = -1; RemoveBox(ghAssignmentBox); ghAssignmentBox = -1; RemoveBox(ghEpcBox); ghEpcBox = -1; RemoveBox(ghRepairBox); ghRepairBox = -1; RemoveBox(ghTrainingBox); ghTrainingBox = -1; fCreated = FALSE; gfIgnoreScrolling = FALSE; RebuildCurrentSquad( ); } return( TRUE ); } void DetermineBoxPositions( void ) { // depending on how many boxes there are, reposition as needed SGPPoint pPoint; SGPPoint pNewPoint; SGPRect pDimensions; SOLDIERTYPE *pSoldier = NULL; if( ( fShowAssignmentMenu == FALSE ) || ( ghAssignmentBox == -1 ) ) { return; } pSoldier = GetSelectedAssignSoldier( TRUE ); // pSoldier NULL is legal here! Gets called during every mapscreen initialization even when nobody is assign char if ( pSoldier == NULL ) { return; } if ( (guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) ) { GetBoxPosition( ghAssignmentBox, &pPoint ); gsAssignmentBoxesX = ( INT16 )pPoint.iX; gsAssignmentBoxesY = ( INT16 )pPoint.iY; } pPoint.iX = gsAssignmentBoxesX; pPoint.iY = gsAssignmentBoxesY; if( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__EPC ) { SetBoxPosition( ghEpcBox, pPoint ); GetBoxSize( ghEpcBox, &pDimensions ); } else { SetBoxPosition( ghAssignmentBox, pPoint ); GetBoxSize( ghAssignmentBox, &pDimensions ); } // hang it right beside the assignment/EPC box menu pNewPoint.iX = pPoint.iX + pDimensions.iRight; pNewPoint.iY = pPoint.iY; if( ( fShowSquadMenu == TRUE ) && ( ghSquadBox != -1 ) ) { SetBoxPosition( ghSquadBox, pNewPoint ); } if( ( fShowRepairMenu == TRUE ) && ( ghRepairBox != -1 ) ) { CreateDestroyMouseRegionForRepairMenu( ); pNewPoint.iY += ( ( GetFontHeight( MAP_SCREEN_FONT ) + 2 ) * ASSIGN_MENU_REPAIR ); SetBoxPosition( ghRepairBox, pNewPoint ); } if( ( fShowTrainingMenu == TRUE ) && ( ghTrainingBox != -1 ) ) { pNewPoint.iY += ( ( GetFontHeight( MAP_SCREEN_FONT ) + 2 ) * ASSIGN_MENU_TRAIN ); SetBoxPosition( ghTrainingBox, pNewPoint ); TrainPosition.iX = pNewPoint.iX; TrainPosition.iY = pNewPoint.iY; OrigTrainPosition.iY = pNewPoint.iY; OrigTrainPosition.iX = pNewPoint.iX; GetBoxSize( ghTrainingBox, &pDimensions ); GetBoxPosition( ghTrainingBox, &pPoint ); if( ( fShowAttributeMenu == TRUE ) && ( ghAttributeBox != -1 ) ) { // hang it right beside the training box menu pNewPoint.iX = pPoint.iX + pDimensions.iRight; pNewPoint.iY = pPoint.iY; SetBoxPosition( ghAttributeBox, pNewPoint ); } } return; } void SetTacticalPopUpAssignmentBoxXY( void ) { INT16 sX, sY; SOLDIERTYPE *pSoldier; //get the soldier pSoldier = GetSelectedAssignSoldier( FALSE ); // grab soldier's x,y screen position GetSoldierScreenPos( pSoldier, &sX, &sY ); if( sX < 0 ) { sX = 0; } gsAssignmentBoxesX = sX + 30; if( sY < 0 ) { sY = 0; } gsAssignmentBoxesY = sY; // ATE: Check if we are past tactical viewport.... // Use estimate width's/heights if ( ( gsAssignmentBoxesX + 100 ) > SCREEN_WIDTH ) { //gsAssignmentBoxesX = 540; gsAssignmentBoxesX = SCREEN_WIDTH - 100; } if ( ( gsAssignmentBoxesY + 130 ) > (SCREEN_HEIGHT - 160) ) { gsAssignmentBoxesY = SCREEN_HEIGHT - 290; } return; } void RepositionMouseRegions( void ) { INT16 sDeltaX, sDeltaY; INT32 iCounter = 0; if( fShowAssignmentMenu == TRUE ) { sDeltaX = gsAssignmentBoxesX - gAssignmentMenuRegion[ 0 ].RegionTopLeftX; sDeltaY = ( INT16 ) ( gsAssignmentBoxesY - gAssignmentMenuRegion[ 0 ].RegionTopLeftY + GetTopMarginSize( ghAssignmentBox ) ); // find the delta from the old to the new, and alter values accordingly for( iCounter = 0; iCounter < ( INT32 )GetNumberOfLinesOfTextInBox( ghAssignmentBox ); iCounter++ ) { gAssignmentMenuRegion[ iCounter ].RegionTopLeftX += sDeltaX; gAssignmentMenuRegion[ iCounter ].RegionTopLeftY += sDeltaY; gAssignmentMenuRegion[ iCounter ].RegionBottomRightX += sDeltaX; gAssignmentMenuRegion[ iCounter ].RegionBottomRightY += sDeltaY; } gfPausedTacticalRenderFlags = RENDER_FLAG_FULL; } } void CheckAndUpdateTacticalAssignmentPopUpPositions( void ) { SGPRect pDimensions, pDimensions2, pDimensions3; SGPPoint pPoint; INT16 sLongest; SOLDIERTYPE *pSoldier = NULL; if( fShowAssignmentMenu == FALSE ) { return; } if ( (guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) ) { return; } //get the soldier pSoldier = GetSelectedAssignSoldier( FALSE ); if( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__EPC ) { GetBoxSize( ghEpcBox, &pDimensions2 ); } else { GetBoxSize( ghAssignmentBox, &pDimensions2 ); } if( fShowRepairMenu == TRUE ) { GetBoxSize( ghRepairBox, &pDimensions ); if( gsAssignmentBoxesX + pDimensions2.iRight + pDimensions.iRight >= SCREEN_WIDTH ) { gsAssignmentBoxesX = ( INT16 ) ( (SCREEN_WIDTH - 1) - ( pDimensions2.iRight + pDimensions.iRight ) ); SetRenderFlags( RENDER_FLAG_FULL ); } if( pDimensions2.iBottom > pDimensions.iBottom ) { sLongest = ( INT16 )pDimensions2.iBottom + ( ( GetFontHeight( MAP_SCREEN_FONT ) + 2 ) * ASSIGN_MENU_REPAIR ); } else { sLongest = ( INT16 )pDimensions.iBottom + ( ( GetFontHeight( MAP_SCREEN_FONT ) + 2 ) * ASSIGN_MENU_REPAIR ); } if( gsAssignmentBoxesY + sLongest >= (SCREEN_HEIGHT - 120) ) { gsAssignmentBoxesY = ( INT16 )( (SCREEN_HEIGHT - 121) - ( sLongest ) ); SetRenderFlags( RENDER_FLAG_FULL ); } pPoint.iX = gsAssignmentBoxesX + pDimensions2.iRight; pPoint.iY = gsAssignmentBoxesY + ( ( GetFontHeight( MAP_SCREEN_FONT ) + 2 ) * ASSIGN_MENU_REPAIR ); SetBoxPosition( ghRepairBox, pPoint ); } else if( fShowSquadMenu == TRUE ) { GetBoxSize( ghSquadBox, &pDimensions ); if( gsAssignmentBoxesX + pDimensions2.iRight + pDimensions.iRight >= SCREEN_WIDTH ) { gsAssignmentBoxesX = ( INT16 ) ( (SCREEN_WIDTH - 1) - ( pDimensions2.iRight + pDimensions.iRight ) ); SetRenderFlags( RENDER_FLAG_FULL ); } if( pDimensions2.iBottom > pDimensions.iBottom ) { sLongest = ( INT16 )pDimensions2.iBottom; } else { sLongest = ( INT16 )pDimensions.iBottom; } if( gsAssignmentBoxesY + sLongest >= (SCREEN_HEIGHT - 120) ) { gsAssignmentBoxesY = ( INT16 )( (SCREEN_HEIGHT - 121) - ( sLongest ) ); SetRenderFlags( RENDER_FLAG_FULL ); } pPoint.iX = gsAssignmentBoxesX + pDimensions2.iRight; pPoint.iY = gsAssignmentBoxesY; SetBoxPosition( ghSquadBox, pPoint ); } else if( fShowAttributeMenu == TRUE ) { GetBoxSize( ghTrainingBox, &pDimensions ); GetBoxSize( ghAttributeBox, &pDimensions3 ); if( gsAssignmentBoxesX + pDimensions2.iRight + pDimensions.iRight + pDimensions3.iRight >= SCREEN_WIDTH ) { gsAssignmentBoxesX = ( INT16 ) ( (SCREEN_WIDTH - 1) - ( pDimensions2.iRight + pDimensions.iRight + pDimensions3.iRight ) ); SetRenderFlags( RENDER_FLAG_FULL ); } if( gsAssignmentBoxesY + pDimensions3.iBottom + ( GetFontHeight( MAP_SCREEN_FONT ) * ASSIGN_MENU_TRAIN ) >= (SCREEN_HEIGHT - 120) ) { gsAssignmentBoxesY = ( INT16 )( (SCREEN_HEIGHT - 121) - ( pDimensions3.iBottom ) ); SetRenderFlags( RENDER_FLAG_FULL ); } pPoint.iX = gsAssignmentBoxesX + pDimensions2.iRight + pDimensions.iRight; pPoint.iY = gsAssignmentBoxesY; pPoint.iY += ( ( GetFontHeight( MAP_SCREEN_FONT ) + 2 ) * ASSIGN_MENU_TRAIN ); SetBoxPosition( ghAttributeBox, pPoint ); pPoint.iX = gsAssignmentBoxesX + pDimensions2.iRight; pPoint.iY = gsAssignmentBoxesY; pPoint.iY += ( ( GetFontHeight( MAP_SCREEN_FONT ) + 2 ) * ASSIGN_MENU_TRAIN ); SetBoxPosition( ghTrainingBox, pPoint ); } else if( fShowTrainingMenu == TRUE ) { GetBoxSize( ghTrainingBox, &pDimensions ); if( gsAssignmentBoxesX + pDimensions2.iRight + pDimensions.iRight >= SCREEN_WIDTH ) { gsAssignmentBoxesX = ( INT16 ) ( (SCREEN_WIDTH - 1) - ( pDimensions2.iRight + pDimensions.iRight ) ); SetRenderFlags( RENDER_FLAG_FULL ); } if( gsAssignmentBoxesY + pDimensions2.iBottom + ( ( GetFontHeight( MAP_SCREEN_FONT ) + 2 ) * ASSIGN_MENU_TRAIN ) >= (SCREEN_HEIGHT - 120) ) { gsAssignmentBoxesY = ( INT16 )( (SCREEN_HEIGHT - 121) - ( pDimensions2.iBottom ) - ( ( GetFontHeight( MAP_SCREEN_FONT ) + 2 ) * ASSIGN_MENU_TRAIN ) ); SetRenderFlags( RENDER_FLAG_FULL ); } pPoint.iX = gsAssignmentBoxesX + pDimensions2.iRight; pPoint.iY = gsAssignmentBoxesY; pPoint.iY += ( ( GetFontHeight( MAP_SCREEN_FONT ) + 2 ) * ASSIGN_MENU_TRAIN ); SetBoxPosition( ghTrainingBox, pPoint ); } else { // just the assignment box if( gsAssignmentBoxesX + pDimensions2.iRight >= SCREEN_WIDTH ) { gsAssignmentBoxesX = ( INT16 ) ( (SCREEN_WIDTH - 1) - ( pDimensions2.iRight ) ); SetRenderFlags( RENDER_FLAG_FULL ); } if( gsAssignmentBoxesY + pDimensions2.iBottom >= (SCREEN_HEIGHT - 120) ) { gsAssignmentBoxesY = ( INT16 )( (SCREEN_HEIGHT - 121) - ( pDimensions2.iBottom ) ); SetRenderFlags( RENDER_FLAG_FULL ); } pPoint.iX = gsAssignmentBoxesX; pPoint.iY = gsAssignmentBoxesY; if( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__EPC) { SetBoxPosition( ghEpcBox, pPoint ); } else { SetBoxPosition( ghAssignmentBox, pPoint ); } } RepositionMouseRegions( ); } void PositionCursorForTacticalAssignmentBox( void ) { // position cursor over y of on duty in tactical assignments SGPPoint pPosition; SGPRect pDimensions; INT32 iFontHeight; // get x.y position of box GetBoxPosition( ghAssignmentBox, &pPosition); // get dimensions..mostly for width GetBoxSize( ghAssignmentBox, &pDimensions ); iFontHeight = GetLineSpace( ghAssignmentBox ) + GetFontHeight( GetBoxFont( ghAssignmentBox ) ); if( gGameSettings.fOptions[ TOPTION_DONT_MOVE_MOUSE ] == FALSE ) { //SimulateMouseMovement( pPosition.iX + pDimensions.iRight - 6, pPosition.iY + ( iFontHeight / 2 ) + 2 ); } } void HandleRestFatigueAndSleepStatus( void ) { INT32 iCounter = 0, iNumberOnTeam = 0; SOLDIERTYPE * pSoldier; BOOLEAN fReasonAdded = FALSE; BOOLEAN fBoxSetUp = FALSE; BOOLEAN fMeToo = FALSE; iNumberOnTeam =gTacticalStatus.Team[ OUR_TEAM ].bLastID; // run through all player characters and handle their rest, fatigue, and going to sleep for( iCounter = 0; iCounter < iNumberOnTeam; iCounter++ ) { pSoldier = &Menptr[ iCounter ]; if( pSoldier->bActive ) { if( ( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) || AM_A_ROBOT( pSoldier ) ) { continue; } if( ( pSoldier->bAssignment == ASSIGNMENT_POW ) || ( pSoldier->bAssignment == IN_TRANSIT ) ) { continue; } // if character CAN sleep, he doesn't actually have to be put asleep to get some rest, // many other assignments and conditions allow for automatic recovering from fatigue. if( CharacterIsTakingItEasy( pSoldier ) ) { // let them rest some RestCharacter( pSoldier ); } else { // wear them down FatigueCharacter( pSoldier ); } // CHECK FOR MERCS GOING TO SLEEP // if awake if ( !pSoldier->flags.fMercAsleep ) { // if dead tired if( pSoldier->bBreathMax <= BREATHMAX_ABSOLUTE_MINIMUM ) { // if between sectors, don't put tired mercs to sleep... will be handled when they arrive at the next sector if ( pSoldier->flags.fBetweenSectors ) { continue; } // he goes to sleep, provided it's at all possible (it still won't happen in a hostile sector, etc.) if( SetMercAsleep( pSoldier, FALSE ) ) { if( ( pSoldier->bAssignment < ON_DUTY ) || ( pSoldier->bAssignment == VEHICLE ) ) { // on a squad/vehicle, complain, then drop TacticalCharacterDialogue( pSoldier, QUOTE_NEED_SLEEP ); TacticalCharacterDialogueWithSpecialEvent( pSoldier, QUOTE_NEED_SLEEP, DIALOGUE_SPECIAL_EVENT_SLEEP, 1,0 ); fMeToo = TRUE; } // guy collapses pSoldier->flags.fMercCollapsedFlag = TRUE; } } // if pretty tired, and not forced to stay awake else if( ( pSoldier->bBreathMax < BREATHMAX_PRETTY_TIRED ) && ( pSoldier->flags.fForcedToStayAwake == FALSE ) ) { // if not on squad/ in vehicle if( ( pSoldier->bAssignment >= ON_DUTY ) && ( pSoldier->bAssignment != VEHICLE ) ) { // try to go to sleep on your own if( SetMercAsleep( pSoldier, FALSE ) ) { if( gGameSettings.fOptions[ TOPTION_SLEEPWAKE_NOTIFICATION ] ) { // if the first one if( fReasonAdded == FALSE ) { // tell player about it AddReasonToWaitingListQueue( ASLEEP_GOING_AUTO_FOR_UPDATE ); TacticalCharacterDialogueWithSpecialEvent( pSoldier, 0, DIALOGUE_SPECIAL_EVENT_SHOW_UPDATE_MENU, 0,0 ); fReasonAdded = TRUE; } AddSoldierToWaitingListQueue( pSoldier ); fBoxSetUp = TRUE; } // seems unnecessary now? ARM pSoldier->bOldAssignment = pSoldier->bAssignment; } } else // tired, in a squad / vehicle { // if he hasn't complained yet if ( !pSoldier->flags.fComplainedThatTired ) { // say quote if( fMeToo == FALSE ) { TacticalCharacterDialogue( pSoldier, QUOTE_NEED_SLEEP ); fMeToo = TRUE; } else { TacticalCharacterDialogue( pSoldier, QUOTE_ME_TOO ); } pSoldier->flags.fComplainedThatTired = TRUE; } } } } } } if( fBoxSetUp ) { UnPauseGameDuringNextQuote( ); AddDisplayBoxToWaitingQueue( ); fBoxSetUp = FALSE; } fReasonAdded = FALSE; // now handle waking (needs seperate list queue, that's why it has its own loop) for( iCounter = 0; iCounter < iNumberOnTeam; iCounter++ ) { pSoldier = &Menptr[ iCounter ]; if( pSoldier->bActive ) { if( ( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) || AM_A_ROBOT( pSoldier ) ) { continue; } if( ( pSoldier->bAssignment == ASSIGNMENT_POW ) || ( pSoldier->bAssignment == IN_TRANSIT ) ) { continue; } // guys between sectors CAN wake up while between sectors (sleeping in vehicle)... // CHECK FOR MERCS WAKING UP if ( pSoldier->bBreathMax >= BREATHMAX_CANCEL_COLLAPSE ) { // reset the collapsed flag well before reaching the wakeup state pSoldier->flags.fMercCollapsedFlag = FALSE; } // if asleep if ( pSoldier->flags.fMercAsleep ) { // but has had enough rest? if( pSoldier->bBreathMax >= BREATHMAX_FULLY_RESTED ) { // try to wake merc up if( SetMercAwake( pSoldier, FALSE, FALSE ) ) { // if not on squad/ in vehicle, tell player about it if( ( pSoldier->bAssignment >= ON_DUTY ) && ( pSoldier->bAssignment != VEHICLE ) ) { if( gGameSettings.fOptions[ TOPTION_SLEEPWAKE_NOTIFICATION ] ) { if( fReasonAdded == FALSE ) { AddReasonToWaitingListQueue( ASSIGNMENT_RETURNING_FOR_UPDATE ); fReasonAdded = TRUE; } AddSoldierToWaitingListQueue( pSoldier ); fBoxSetUp = TRUE; } } } } } } } if( fBoxSetUp ) { UnPauseGameDuringNextQuote( ); AddDisplayBoxToWaitingQueue( ); fBoxSetUp = FALSE; } return; } BOOLEAN CanCharacterRepairVehicle( SOLDIERTYPE *pSoldier, INT32 iVehicleId ) { // is the vehicle valid? if( VehicleIdIsValid( iVehicleId ) == FALSE ) { return( FALSE ); } // is vehicle destroyed? if( pVehicleList[ iVehicleId ].fDestroyed ) { return(FALSE); } // is it damaged at all? if ( !DoesVehicleNeedAnyRepairs( iVehicleId ) ) { return(FALSE); } // it's not Skyrider's helicopter (which isn't damagable/repairable) if ( iVehicleId == iHelicopterVehicleId ) { return( FALSE ); } // same sector, neither is between sectors, and OK To Use (player owns it) ? if ( !IsThisVehicleAccessibleToSoldier( pSoldier, iVehicleId ) ) { return( FALSE ); } /* Assignment distance limits removed. Sep/11/98. ARM // if currently loaded sector, are we close enough? if( ( pSoldier->sSectorX == gWorldSectorX ) && ( pSoldier->sSectorY == gWorldSectorY ) && ( pSoldier->bSectorZ == gbWorldSectorZ ) ) { if( PythSpacesAway( pSoldier->sGridNo, pVehicleList[ iVehicleId ].sGridNo ) > MAX_DISTANCE_FOR_REPAIR ) { return( FALSE ); } } */ return( TRUE ); } BOOLEAN IsRobotInThisSector( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ ) { SOLDIERTYPE *pSoldier; pSoldier = GetRobotSoldier( ); if ( pSoldier != NULL ) { if( ( pSoldier->sSectorX == sSectorX ) && ( pSoldier->sSectorY == sSectorY ) && ( pSoldier->bSectorZ == bSectorZ ) && ( pSoldier->flags.fBetweenSectors == FALSE ) ) { return( TRUE ); } } return( FALSE ); } SOLDIERTYPE * GetRobotSoldier( void ) { SOLDIERTYPE *pSoldier = NULL, *pTeamSoldier = NULL; INT32 cnt=0; // set pSoldier as first in merc ptrs pSoldier = MercPtrs[0]; // go through list of characters, find all who are on this assignment for ( pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ pSoldier->bTeam ].bLastID; cnt++,pTeamSoldier++) { if( pTeamSoldier->bActive ) { if( AM_A_ROBOT( pTeamSoldier ) ) { return (pTeamSoldier); } } } return( NULL ); } BOOLEAN CanCharacterRepairRobot( SOLDIERTYPE *pSoldier ) { SOLDIERTYPE *pRobot = NULL; // do we in fact have the robot on the team? pRobot = GetRobotSoldier( ); if( pRobot == NULL ) { return( FALSE ); } // if robot isn't damaged at all if( pRobot->stats.bLife == pRobot->stats.bLifeMax ) { return( FALSE ); } // is the robot in the same sector if( IsRobotInThisSector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ) == FALSE ) { return( FALSE ); } /* Assignment distance limits removed. Sep/11/98. ARM // if that sector is currently loaded, check distance to robot if( ( pSoldier->sSectorX == gWorldSectorX ) && ( pSoldier->sSectorY == gWorldSectorY ) && ( pSoldier->bSectorZ == gbWorldSectorZ ) ) { if( PythSpacesAway( pSoldier->sGridNo, pRobot->sGridNo ) > MAX_DISTANCE_FOR_REPAIR ) { return( FALSE ); } } */ return( TRUE ); } UINT8 HandleRepairOfRobotBySoldier( SOLDIERTYPE *pSoldier, UINT8 ubRepairPts, BOOLEAN *pfNothingLeftToRepair ) { SOLDIERTYPE *pRobot = NULL; pRobot = GetRobotSoldier( ); // do the actual repairs return( RepairRobot( pRobot, ubRepairPts, pfNothingLeftToRepair ) ); } UINT8 RepairRobot( SOLDIERTYPE *pRobot, UINT8 ubRepairPts, BOOLEAN *pfNothingLeftToRepair ) { UINT8 ubPointsUsed = 0; // is it "dead" ? if( pRobot->stats.bLife == 0 ) { *pfNothingLeftToRepair = TRUE; return( ubPointsUsed ); } // is it "unhurt" ? if( pRobot->stats.bLife == pRobot->stats.bLifeMax ) { *pfNothingLeftToRepair = TRUE; return( ubPointsUsed ); } // if we have enough or more than we need if( pRobot->stats.bLife + ubRepairPts >= pRobot->stats.bLifeMax ) { ubPointsUsed = ( pRobot->stats.bLifeMax - pRobot->stats.bLife ); pRobot->stats.bLife = pRobot->stats.bLifeMax; } else { // not enough, do what we can ubPointsUsed = ubRepairPts; pRobot->stats.bLife += ubRepairPts; } if ( pRobot->stats.bLife == pRobot->stats.bLifeMax ) { *pfNothingLeftToRepair = TRUE; } else { *pfNothingLeftToRepair = FALSE; } return( ubPointsUsed ); } void SetSoldierAssignment( SOLDIERTYPE *pSoldier, INT8 bAssignment, INT32 iParam1, INT32 iParam2, INT32 iParam3 ) { switch( bAssignment ) { case( ASSIGNMENT_HOSPITAL ): if( CanCharacterPatient( pSoldier ) ) { pSoldier->bOldAssignment = pSoldier->bAssignment; pSoldier->bBleeding = 0; // set dirty flag fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; // remove from squad RemoveCharacterFromSquads( pSoldier ); // remove from any vehicle if( pSoldier->bOldAssignment == VEHICLE ) { TakeSoldierOutOfVehicle( pSoldier ); } if( ( pSoldier->bAssignment != ASSIGNMENT_HOSPITAL ) ) { SetTimeOfAssignmentChangeForMerc( pSoldier ); } RebuildCurrentSquad( ); ChangeSoldiersAssignment( pSoldier, ASSIGNMENT_HOSPITAL ); AssignMercToAMovementGroup( pSoldier ); } break; case( PATIENT ): if( CanCharacterPatient( pSoldier ) ) { // set as doctor /* Assignment distance limits removed. Sep/11/98. ARM if( IsSoldierCloseEnoughToADoctor( pSoldier ) == FALSE ) { return; } */ pSoldier->bOldAssignment = pSoldier->bAssignment; // set dirty flag fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; // remove from squad RemoveCharacterFromSquads( pSoldier ); // remove from any vehicle if( pSoldier->bOldAssignment == VEHICLE ) { TakeSoldierOutOfVehicle( pSoldier ); } if( ( pSoldier->bAssignment != PATIENT ) ) { SetTimeOfAssignmentChangeForMerc( pSoldier ); } ChangeSoldiersAssignment( pSoldier, PATIENT ); AssignMercToAMovementGroup( pSoldier ); } break; case( DOCTOR ): if( CanCharacterDoctor( pSoldier ) ) { pSoldier->bOldAssignment = pSoldier->bAssignment; // set dirty flag fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; gfRenderPBInterface = TRUE; // remove from squad RemoveCharacterFromSquads( pSoldier ); // remove from any vehicle if( pSoldier->bOldAssignment == VEHICLE ) { TakeSoldierOutOfVehicle( pSoldier ); } if( ( pSoldier->bAssignment != DOCTOR ) ) { SetTimeOfAssignmentChangeForMerc( pSoldier ); } ChangeSoldiersAssignment( pSoldier, DOCTOR ); MakeSureMedKitIsInHand( pSoldier ); AssignMercToAMovementGroup( pSoldier ); } break; case( TRAIN_TOWN ): if( CanCharacterTrainMilitia( pSoldier ) ) { // train militia pSoldier->bOldAssignment = pSoldier->bAssignment; // set dirty flag fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; // remove from squad RemoveCharacterFromSquads( pSoldier ); // remove from any vehicle if( pSoldier->bOldAssignment == VEHICLE ) { TakeSoldierOutOfVehicle( pSoldier ); } if( ( pSoldier->bAssignment != TRAIN_TOWN ) ) { SetTimeOfAssignmentChangeForMerc( pSoldier ); } ChangeSoldiersAssignment( pSoldier, TRAIN_TOWN ); if( pMilitiaTrainerSoldier == NULL ) { if( SectorInfo[ SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ) ].fMilitiaTrainingPaid == FALSE ) { // show a message to confirm player wants to charge cost HandleInterfaceMessageForCostOfTrainingMilitia( pSoldier ); } } AssignMercToAMovementGroup( pSoldier ); // set dirty flag fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; gfRenderPBInterface = TRUE; } break; case( TRAIN_SELF ): if( CanCharacterTrainStat( pSoldier, ( INT8 )iParam1, TRUE, FALSE ) ) { // train stat pSoldier->bOldAssignment = pSoldier->bAssignment; // remove from squad RemoveCharacterFromSquads( pSoldier ); // remove from any vehicle if( pSoldier->bOldAssignment == VEHICLE ) { TakeSoldierOutOfVehicle( pSoldier ); } if( ( pSoldier->bAssignment != TRAIN_SELF ) ) { SetTimeOfAssignmentChangeForMerc( pSoldier ); } ChangeSoldiersAssignment( pSoldier, TRAIN_SELF ); AssignMercToAMovementGroup( pSoldier ); // set stat to train pSoldier->bTrainStat = ( INT8 )iParam1; // set dirty flag fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; gfRenderPBInterface = TRUE; } break; case( TRAIN_TEAMMATE ): if( CanCharacterTrainStat( pSoldier, ( INT8 )iParam1, FALSE, TRUE ) ) { pSoldier->bOldAssignment = pSoldier->bAssignment; // remove from squad RemoveCharacterFromSquads( pSoldier ); // remove from any vehicle if( pSoldier->bOldAssignment == VEHICLE ) { TakeSoldierOutOfVehicle( pSoldier ); } if( ( pSoldier->bAssignment != TRAIN_TEAMMATE ) ) { SetTimeOfAssignmentChangeForMerc( pSoldier ); } ChangeSoldiersAssignment( pSoldier, TRAIN_TEAMMATE ); AssignMercToAMovementGroup( pSoldier ); // set stat to train pSoldier->bTrainStat = ( INT8 )iParam1; // set dirty flag fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; gfRenderPBInterface = TRUE; } break; case( TRAIN_BY_OTHER ): if( CanCharacterTrainStat( pSoldier, ( INT8 )iParam1, TRUE, FALSE ) ) { // train stat pSoldier->bOldAssignment = pSoldier->bAssignment; // remove from squad RemoveCharacterFromSquads( pSoldier ); // remove from any vehicle if( pSoldier->bOldAssignment == VEHICLE ) { TakeSoldierOutOfVehicle( pSoldier ); } if( ( pSoldier->bAssignment != TRAIN_BY_OTHER ) ) { SetTimeOfAssignmentChangeForMerc( pSoldier ); } ChangeSoldiersAssignment( pSoldier, TRAIN_BY_OTHER ); AssignMercToAMovementGroup( pSoldier ); // set stat to train pSoldier->bTrainStat = ( INT8 )iParam1; // set dirty flag fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; gfRenderPBInterface = TRUE; } break; case( REPAIR ): if( CanCharacterRepair( pSoldier ) ) { pSoldier->bOldAssignment = pSoldier->bAssignment; // remove from squad RemoveCharacterFromSquads( pSoldier ); // remove from any vehicle if( pSoldier->bOldAssignment == VEHICLE ) { TakeSoldierOutOfVehicle( pSoldier ); } if( ( pSoldier->bAssignment != REPAIR ) ||( pSoldier->flags.fFixingSAMSite != ( UINT8 )iParam1 ) || ( pSoldier->flags.fFixingRobot != ( UINT8 )iParam2 ) || ( pSoldier->bVehicleUnderRepairID != ( UINT8 )iParam3 ) ) { SetTimeOfAssignmentChangeForMerc( pSoldier ); } ChangeSoldiersAssignment( pSoldier, REPAIR ); MakeSureToolKitIsInHand( pSoldier ); AssignMercToAMovementGroup( pSoldier ); pSoldier->flags.fFixingSAMSite = ( UINT8 )iParam1; pSoldier->flags.fFixingRobot = ( UINT8 )iParam2; pSoldier->bVehicleUnderRepairID = ( INT8 )iParam3; } break; case( VEHICLE ): if( CanCharacterVehicle( pSoldier ) && IsThisVehicleAccessibleToSoldier( pSoldier, iParam1 ) ) { if ( IsEnoughSpaceInVehicle( (INT8) iParam1 ) ) { pSoldier->bOldAssignment = pSoldier->bAssignment; // set dirty flag fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; gfRenderPBInterface = TRUE; if( pSoldier->bOldAssignment == VEHICLE ) { TakeSoldierOutOfVehicle( pSoldier ); } // remove from squad RemoveCharacterFromSquads( pSoldier ); if( PutSoldierInVehicle( pSoldier, ( INT8 )( iParam1 ) ) == FALSE ) { AddCharacterToAnySquad( pSoldier ); } else { if( ( pSoldier->bAssignment != VEHICLE ) || ( pSoldier->iVehicleId != ( UINT8 )iParam1 ) ) { SetTimeOfAssignmentChangeForMerc( pSoldier ); } pSoldier->iVehicleId = iParam1; ChangeSoldiersAssignment( pSoldier, VEHICLE ); AssignMercToAMovementGroup( pSoldier ); } } else { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, gzLateLocalizedString[ 18 ], zVehicleName[ pVehicleList[ iParam1 ].ubVehicleType ] ); } } break; } return; } /* No point in allowing SAM site repair any more. Jan/13/99. ARM BOOLEAN CanSoldierRepairSAM( SOLDIERTYPE *pSoldier, INT8 bRepairPoints) { INT16 sGridNoA = 0, sGridNoB = 0; // is the soldier in the sector as the SAM if( SoldierInSameSectorAsSAM( pSoldier ) == FALSE ) { return( FALSE ); } // is the soldier close enough to the control panel? if( IsSoldierCloseEnoughToSAMControlPanel( pSoldier ) == FALSE ) { return( FALSE ); } //can it be fixed? if( IsTheSAMSiteInSectorRepairable( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ) == FALSE ) { return( FALSE ); } // is he good enough? (Because of the division of repair pts in SAM repair formula, a guy with any less that this // can't make any headway if (bRepairPoints < SAM_SITE_REPAIR_DIVISOR ) { return( FALSE ); } return( TRUE ); } BOOLEAN IsTheSAMSiteInSectorRepairable( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ ) { INT32 iCounter = 0; INT8 bSAMCondition; // is the guy above ground, if not, it can't be fixed, now can it? if( sSectorZ != 0 ) { return( FALSE ); } for( iCounter = 0; iCounter < NUMBER_OF_SAMS; iCounter++ ) { if( pSamList[ iCounter ] == SECTOR( sSectorX, sSectorY ) ) { bSAMCondition = StrategicMap[ CALCULATE_STRATEGIC_INDEX( sSectorX, sSectorY ) ].bSAMCondition; if( ( bSAMCondition < 100 ) && ( bSAMCondition >= MIN_CONDITION_TO_FIX_SAM ) ) { return( TRUE ); } else { // it's not broken at all, or it's beyond repair return( FALSE ); } } } // none found return( FALSE ); } BOOLEAN SoldierInSameSectorAsSAM( SOLDIERTYPE *pSoldier ) { INT32 iCounter = 0; // is the soldier on the surface? if( pSoldier->bSectorZ != 0 ) { return( FALSE ); } // now check each sam site in the list for( iCounter = 0; iCounter < NUMBER_OF_SAMS; iCounter++ ) { if( pSamList[ iCounter] == SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ) ) { return( TRUE ); } } return( FALSE ); } BOOLEAN IsSoldierCloseEnoughToSAMControlPanel( SOLDIERTYPE *pSoldier ) { INT32 iCounter = 0; // now check each sam site in the list for( iCounter = 0; iCounter < NUMBER_OF_SAMS; iCounter++ ) { if( pSamList[ iCounter ] == SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ) ) { // Assignment distance limits removed. Sep/11/98. ARM // if( ( PythSpacesAway( pSamGridNoAList[ iCounter ], pSoldier->sGridNo ) < MAX_DISTANCE_FOR_REPAIR )||( PythSpacesAway( pSamGridNoBList[ iCounter ], pSoldier->sGridNo ) < MAX_DISTANCE_FOR_REPAIR ) ) { return( TRUE ); } } } return( FALSE ); } */ BOOLEAN HandleAssignmentExpansionAndHighLightForAssignMenu( SOLDIERTYPE *pSoldier ) { if( fShowSquadMenu ) { // squad menu up?..if so, highlight squad line the previous menu if( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__EPC ) { HighLightBoxLine( ghEpcBox, EPC_MENU_ON_DUTY ); } else { HighLightBoxLine( ghAssignmentBox, ASSIGN_MENU_ON_DUTY ); } return( TRUE ); } else if( fShowTrainingMenu ) { // highlight train line the previous menu HighLightBoxLine( ghAssignmentBox, ASSIGN_MENU_TRAIN ); return( TRUE ); } else if( fShowRepairMenu ) { // highlight repair line the previous menu HighLightBoxLine( ghAssignmentBox, ASSIGN_MENU_REPAIR ); return( TRUE ); } else if( fShowVehicleMenu ) { // highlight vehicle line the previous menu HighLightBoxLine( ghAssignmentBox, ASSIGN_MENU_VEHICLE ); return( TRUE ); } return( FALSE ); } BOOLEAN HandleAssignmentExpansionAndHighLightForTrainingMenu( void ) { if( fShowAttributeMenu ) { switch ( gbTrainingMode ) { case TRAIN_SELF: HighLightBoxLine( ghTrainingBox, TRAIN_MENU_SELF ); return( TRUE ); case TRAIN_TEAMMATE: HighLightBoxLine( ghTrainingBox, TRAIN_MENU_TEAMMATES ); return( TRUE ); case TRAIN_BY_OTHER: HighLightBoxLine( ghTrainingBox, TRAIN_MENU_TRAIN_BY_OTHER ); return( TRUE ); default: Assert( FALSE ); break; } } return( FALSE ); } /* BOOLEAN HandleShowingOfUpBox( void ) { // if the list is being shown, then show it if( fShowUpdateBox == TRUE ) { MarkAllBoxesAsAltered( ); ShowBox( ghUpdateBox ); return( TRUE ); } else { if( IsBoxShown( ghUpdateBox ) ) { HideBox( ghUpdateBox ); fMapPanelDirty = TRUE; gfRenderPBInterface = TRUE; fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; fCharacterInfoPanelDirty = TRUE; } } return( FALSE ); } */ BOOLEAN HandleShowingOfMovementBox( void ) { // if the list is being shown, then show it if( fShowMapScreenMovementList == TRUE ) { MarkAllBoxesAsAltered( ); ShowBox( ghMoveBox ); return( TRUE ); } else { if( IsBoxShown( ghMoveBox ) ) { HideBox( ghMoveBox ); fMapPanelDirty = TRUE; gfRenderPBInterface = TRUE; fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; fCharacterInfoPanelDirty = TRUE; } } return( FALSE ); } void HandleShadingOfLinesForTrainingMenu( void ) { SOLDIERTYPE *pSoldier = NULL; // check if valid if( ( fShowTrainingMenu == FALSE ) || ( ghTrainingBox == - 1 ) ) { return; } pSoldier = GetSelectedAssignSoldier( FALSE ); // can character practise? if( CanCharacterPractise( pSoldier ) == FALSE ) { ShadeStringInBox( ghTrainingBox, TRAIN_MENU_SELF ); } else { UnShadeStringInBox( ghTrainingBox, TRAIN_MENU_SELF ); } // can character EVER train militia? if( BasicCanCharacterTrainMilitia( pSoldier ) ) { // can he train here, now? if( CanCharacterTrainMilitia( pSoldier ) ) { // unshade train militia line UnShadeStringInBox( ghTrainingBox, TRAIN_MENU_TOWN ); UnSecondaryShadeStringInBox( ghTrainingBox, TRAIN_MENU_TOWN ); } else { SecondaryShadeStringInBox( ghTrainingBox, TRAIN_MENU_TOWN ); } } else { // shade train militia line ShadeStringInBox( ghTrainingBox, TRAIN_MENU_TOWN ); } // can character train teammates? if( CanCharacterTrainTeammates( pSoldier ) == FALSE ) { ShadeStringInBox( ghTrainingBox, TRAIN_MENU_TEAMMATES ); } else { UnShadeStringInBox( ghTrainingBox, TRAIN_MENU_TEAMMATES ); } // can character be trained by others? if( CanCharacterBeTrainedByOther( pSoldier ) == FALSE ) { ShadeStringInBox( ghTrainingBox, TRAIN_MENU_TRAIN_BY_OTHER ); } else { UnShadeStringInBox( ghTrainingBox, TRAIN_MENU_TRAIN_BY_OTHER ); } return; } void HandleShadingOfLinesForAttributeMenus( void ) { // will do the same as updateassignments...but with training pop up box strings SOLDIERTYPE *pSoldier; INT8 bAttrib =0; BOOLEAN fStatTrainable; if( ( fShowTrainingMenu == FALSE ) || ( ghTrainingBox == - 1 ) ) { return; } if( ( fShowAttributeMenu == FALSE ) || ( ghAttributeBox == - 1 ) ) { return; } pSoldier = GetSelectedAssignSoldier( FALSE ); for( bAttrib = 0; bAttrib < ATTRIB_MENU_CANCEL; bAttrib++ ) { switch ( gbTrainingMode ) { case TRAIN_SELF: fStatTrainable = CanCharacterTrainStat( pSoldier, bAttrib, TRUE, FALSE ); break; case TRAIN_TEAMMATE: // DO allow trainers to be assigned without any partners (students) fStatTrainable = CanCharacterTrainStat( pSoldier, bAttrib, FALSE, TRUE ); break; case TRAIN_BY_OTHER: // DO allow students to be assigned without any partners (trainers) fStatTrainable = CanCharacterTrainStat( pSoldier, bAttrib, TRUE, FALSE ); break; default: Assert( FALSE ); fStatTrainable = FALSE; break; } if ( fStatTrainable ) { // also unshade stat UnShadeStringInBox( ghAttributeBox, bAttrib ); } else { // shade stat ShadeStringInBox( ghAttributeBox, bAttrib ); } } return; } void ResetAssignmentsForAllSoldiersInSectorWhoAreTrainingTown( SOLDIERTYPE *pSoldier ) { INT32 iNumberOnTeam = 0, iCounter = 0; SOLDIERTYPE *pCurSoldier = NULL; iNumberOnTeam = gTacticalStatus.Team[ OUR_TEAM ].bLastID; for( iCounter = 0; iCounter < iNumberOnTeam; iCounter++ ) { pCurSoldier = &Menptr[ iCounter ]; if( ( pCurSoldier->bActive ) && ( pCurSoldier->stats.bLife >= OKLIFE ) ) { if( pCurSoldier->bAssignment == TRAIN_TOWN ) { if( ( pCurSoldier->sSectorX == pSoldier->sSectorX ) && ( pCurSoldier->sSectorY == pSoldier->sSectorY ) && ( pSoldier->bSectorZ == 0 ) ) { AddCharacterToAnySquad( pCurSoldier ); } } } } return; } void ReportTrainersTraineesWithoutPartners( void ) { SOLDIERTYPE *pTeamSoldier = NULL; INT32 iCounter = 0, iNumberOnTeam = 0; iNumberOnTeam = gTacticalStatus.Team[ OUR_TEAM ].bLastID; // check for each instructor for( iCounter = 0; iCounter < iNumberOnTeam; iCounter++ ) { pTeamSoldier = &Menptr[ iCounter ]; if( ( pTeamSoldier->bAssignment == TRAIN_TEAMMATE ) && ( pTeamSoldier->stats.bLife > 0 ) ) { if ( !ValidTrainingPartnerInSameSectorOnAssignmentFound( pTeamSoldier, TRAIN_BY_OTHER, pTeamSoldier->bTrainStat ) ) { AssignmentDone( pTeamSoldier, TRUE, TRUE ); } } } // check each trainee for( iCounter = 0; iCounter < iNumberOnTeam; iCounter++ ) { pTeamSoldier = &Menptr[ iCounter ]; if( ( pTeamSoldier->bAssignment == TRAIN_BY_OTHER ) && ( pTeamSoldier->stats.bLife > 0 ) ) { if ( !ValidTrainingPartnerInSameSectorOnAssignmentFound( pTeamSoldier, TRAIN_TEAMMATE, pTeamSoldier->bTrainStat ) ) { AssignmentDone( pTeamSoldier, TRUE, TRUE ); } } } return; } BOOLEAN SetMercAsleep( SOLDIERTYPE *pSoldier, BOOLEAN fGiveWarning ) { if( CanCharacterSleep( pSoldier, fGiveWarning ) ) { // put him to sleep PutMercInAsleepState( pSoldier ); // successful return( TRUE ); } else { // can't sleep for some other reason return( FALSE ); } } BOOLEAN PutMercInAsleepState( SOLDIERTYPE *pSoldier ) { if( pSoldier->flags.fMercAsleep == FALSE ) { if( ( gfWorldLoaded ) && ( pSoldier->bInSector ) ) { if( guiCurrentScreen == GAME_SCREEN ) { pSoldier->ChangeSoldierState( GOTO_SLEEP, 1, TRUE ); } else { pSoldier->ChangeSoldierState( SLEEPING, 1, TRUE ); } } // set merc asleep pSoldier->flags.fMercAsleep = TRUE; // refresh panels fCharacterInfoPanelDirty = TRUE; fTeamPanelDirty = TRUE; } return( TRUE ); } BOOLEAN SetMercAwake( SOLDIERTYPE *pSoldier, BOOLEAN fGiveWarning, BOOLEAN fForceHim ) { // forcing him skips all normal checks! if ( !fForceHim ) { if ( !CanCharacterBeAwakened( pSoldier, fGiveWarning ) ) { return( FALSE ); } } PutMercInAwakeState( pSoldier ); return( TRUE ); } BOOLEAN PutMercInAwakeState( SOLDIERTYPE *pSoldier ) { if ( pSoldier->flags.fMercAsleep ) { if ( ( gfWorldLoaded ) && ( pSoldier->bInSector ) ) { if ( guiCurrentScreen == GAME_SCREEN ) { pSoldier->ChangeSoldierState( WKAEUP_FROM_SLEEP, 1, TRUE ); } else { pSoldier->ChangeSoldierState( STANDING, 1, TRUE ); } } // set merc awake pSoldier->flags.fMercAsleep = FALSE; // refresh panels fCharacterInfoPanelDirty = TRUE; fTeamPanelDirty = TRUE; // determine if merc is being forced to stay awake if ( pSoldier->bBreathMax < BREATHMAX_PRETTY_TIRED ) { pSoldier->flags.fForcedToStayAwake = TRUE; } else { pSoldier->flags.fForcedToStayAwake = FALSE; } } return( TRUE ); } BOOLEAN IsThereASoldierInThisSector( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ ) { if( fSectorsWithSoldiers[ sSectorX + sSectorY * MAP_WORLD_X ][ bSectorZ ] == TRUE ) { return( TRUE ); } return( FALSE ); } // set the time this soldier's assignment changed void SetTimeOfAssignmentChangeForMerc( SOLDIERTYPE *pSoldier ) { // if someone is being taken off of HOSPITAL then track how much // of payment wasn't used up if ( pSoldier->bAssignment == ASSIGNMENT_HOSPITAL ) { giHospitalRefund += CalcPatientMedicalCost( pSoldier ); pSoldier->bHospitalPriceModifier = 0; } // set time of last assignment change pSoldier->uiLastAssignmentChangeMin = GetWorldTotalMin( ); // assigning new PATIENTs gives a DOCTOR something to do, etc., so set flag to recheck them all. // CAN'T DO IT RIGHT AWAY IN HERE 'CAUSE WE TYPICALLY GET CALLED *BEFORE* bAssignment GETS SET TO NEW VALUE!! gfReEvaluateEveryonesNothingToDo = TRUE; return; } // have we spent enough time on assignment for it to count? BOOLEAN EnoughTimeOnAssignment( SOLDIERTYPE *pSoldier ) { if( GetWorldTotalMin() - pSoldier->uiLastAssignmentChangeMin >= (UINT32)gGameExternalOptions.ubMinutesForAssignmentToCount ) { return( TRUE ); } return( FALSE ); } BOOLEAN AnyMercInGroupCantContinueMoving( GROUP *pGroup ) { PLAYERGROUP *pPlayer; SOLDIERTYPE *pSoldier; BOOLEAN fMeToo = FALSE; BOOLEAN fGroupMustStop = FALSE; AssertNotNIL( pGroup ); AssertT( pGroup->fPlayer ); pPlayer = pGroup->pPlayerList; while( pPlayer ) { // if group has player list... and a valid first soldier if( pPlayer && pPlayer->pSoldier ) { pSoldier = pPlayer->pSoldier; if ( PlayerSoldierTooTiredToTravel( pSoldier ) ) { // NOTE: we only complain about it if it's gonna force the group to stop moving! fGroupMustStop = TRUE; // say quote if( fMeToo == FALSE ) { HandleImportantMercQuote( pSoldier, QUOTE_NEED_SLEEP ); fMeToo = TRUE; } else { HandleImportantMercQuote( pSoldier, QUOTE_ME_TOO ); } // put him to bed PutMercInAsleepState( pSoldier ); // player can't wake him up right away pSoldier->flags.fMercCollapsedFlag = TRUE; } } pPlayer = pPlayer->next; } return( fGroupMustStop ); } BOOLEAN PlayerSoldierTooTiredToTravel( SOLDIERTYPE *pSoldier ) { Assert( pSoldier ); // if this guy ever needs sleep at all if ( CanChangeSleepStatusForSoldier( pSoldier ) ) { // if walking, or only remaining possible driver for a vehicle group if ( ( pSoldier->bAssignment != VEHICLE ) || SoldierMustDriveVehicle( pSoldier, pSoldier->iVehicleId, TRUE ) ) { // if awake, but so tired they can't move/drive anymore if ( ( !pSoldier->flags.fMercAsleep ) && ( pSoldier->bBreathMax < BREATHMAX_GOTTA_STOP_MOVING ) ) { return( TRUE ); } // asleep, and can't be awakened? if ( ( pSoldier->flags.fMercAsleep ) && !CanCharacterBeAwakened( pSoldier, FALSE ) ) { return( TRUE ); } } } return( FALSE ); } BOOLEAN AssignMercToAMovementGroup( SOLDIERTYPE *pSoldier ) { // if merc doesn't have a group or is in a vehicle or on a squad assign to group INT8 bGroupId = 0; // on a squad? if( pSoldier->bAssignment < ON_DUTY ) { return( FALSE ); } // in a vehicle? if( pSoldier->bAssignment == VEHICLE ) { return( FALSE ); } // in transit if( pSoldier->bAssignment == IN_TRANSIT ) { return( FALSE ); } // in a movement group? if( pSoldier->ubGroupID != 0 ) { return( FALSE ); } // create group bGroupId = CreateNewPlayerGroupDepartingFromSector( ( UINT8 )( pSoldier->sSectorX ), ( UINT8 )( pSoldier->sSectorY ) ); if( bGroupId ) { // add merc AddPlayerToGroup( bGroupId, pSoldier ); // success return( TRUE ); } return( TRUE ); } void NotifyPlayerOfAssignmentAttemptFailure( INT8 bAssignment ) { // notify player if ( guiCurrentScreen != MSG_BOX_SCREEN ) { DoScreenIndependantMessageBox( pMapErrorString[ 18 ], MSG_BOX_FLAG_OK, NULL); } else { // use screen msg instead! ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, pMapErrorString[ 18 ] ); } if ( bAssignment == TRAIN_TOWN ) { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, pMapErrorString[ 48 ] ); } } BOOLEAN HandleSelectedMercsBeingPutAsleep( BOOLEAN fWakeUp, BOOLEAN fDisplayWarning ) { BOOLEAN fSuccess = TRUE; INT32 iCounter = 0; SOLDIERTYPE *pSoldier = NULL; UINT8 ubNumberOfSelectedSoldiers = 0; CHAR16 sString[ 128 ]; for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { pSoldier = NULL; // if the current character in the list is valid...then grab soldier pointer for the character if( gCharactersList[ iCounter ].fValid ) { // get the soldier pointer pSoldier = &Menptr[ gCharactersList[ iCounter ].usSolID ]; if( pSoldier->bActive == FALSE ) { continue; } if( iCounter == bSelectedInfoChar ) { continue; } if( IsEntryInSelectedListSet( ( INT8 )iCounter ) == FALSE ) { continue; } // don't try to put vehicles, robots, to sleep if they're also selected if ( CanChangeSleepStatusForCharSlot( (INT8) iCounter ) == FALSE ) { continue; } // up the total number of soldiers ubNumberOfSelectedSoldiers++; if( fWakeUp ) { // try to wake merc up if( SetMercAwake( pSoldier, FALSE, FALSE ) == FALSE ) { fSuccess = FALSE; } } else { // set this soldier asleep if( SetMercAsleep( pSoldier, FALSE ) == FALSE ) { fSuccess = FALSE; } } } } // if there was anyone processed, check for success and inform player of failure if( ubNumberOfSelectedSoldiers ) { if( fSuccess == FALSE ) { if( fWakeUp ) { // inform player not everyone could be woke up swprintf( sString, pMapErrorString[ 27 ] ); } else { // inform player not everyone could be put to sleep swprintf( sString, pMapErrorString[ 26 ]); } if( fDisplayWarning ) { DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL); } } } return( fSuccess ); } BOOLEAN IsAnyOneOnPlayersTeamOnThisAssignment( INT8 bAssignment ) { INT32 iCounter = 0; SOLDIERTYPE *pSoldier = NULL; for( iCounter = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; iCounter <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; iCounter++ ) { // get the current soldier pSoldier = &Menptr[ iCounter ]; // active? if( pSoldier->bActive == FALSE ) { continue; } if( pSoldier->bAssignment == bAssignment ) { return( TRUE ); } } return( FALSE ); } void RebuildAssignmentsBox( void ) { // destroy and recreate assignments box if ( ghAssignmentBox != -1 ) { RemoveBox( ghAssignmentBox ); ghAssignmentBox = -1; } CreateAssignmentsBox( ); } void BandageBleedingDyingPatientsBeingTreated( ) { INT32 iCounter = 0; SOLDIERTYPE *pSoldier = NULL; SOLDIERTYPE *pDoctor = NULL; INT32 iKitSlot; OBJECTTYPE *pKit = NULL; UINT16 usKitPts; UINT32 uiKitPtsUsed; BOOLEAN fSomeoneStillBleedingDying = FALSE; for( iCounter = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; iCounter <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; iCounter++ ) { // get the soldier pSoldier = &Menptr[ iCounter ]; // check if the soldier is currently active? if( pSoldier->bActive == FALSE ) { continue; } // and he is bleeding or dying if( ( pSoldier->bBleeding ) || ( pSoldier->stats.bLife < OKLIFE ) ) { // if soldier is receiving care if( ( pSoldier->bAssignment == PATIENT ) || ( pSoldier->bAssignment == ASSIGNMENT_HOSPITAL ) || ( pSoldier->bAssignment == DOCTOR ) ) { // if in the hospital if ( pSoldier->bAssignment == ASSIGNMENT_HOSPITAL ) { // this is instantaneous, and doesn't use up any bandages! // stop bleeding automatically pSoldier->bBleeding = 0; if ( pSoldier->stats.bLife < OKLIFE ) { pSoldier->stats.bLife = OKLIFE; } } else // assigned to DOCTOR/PATIENT { // see if there's a doctor around who can help him pDoctor = AnyDoctorWhoCanHealThisPatient( pSoldier, HEALABLE_EVER ); if ( pDoctor != NULL ) { iKitSlot = FindObjClass( pDoctor, IC_MEDKIT ); if( iKitSlot != NO_SLOT ) { pKit = &( pDoctor->inv[ iKitSlot ] ); usKitPts = TotalPoints( pKit ); if( usKitPts ) { uiKitPtsUsed = VirtualSoldierDressWound( pDoctor, pSoldier, pKit, usKitPts, usKitPts ); UseKitPoints( pKit, (UINT16)uiKitPtsUsed, pDoctor ); // if he is STILL bleeding or dying if( ( pSoldier->bBleeding ) || ( pSoldier->stats.bLife < OKLIFE ) ) { fSomeoneStillBleedingDying = TRUE; } } } } } } } } // this event may be posted many times because of multiple assignment changes. Handle it only once per minute! DeleteAllStrategicEventsOfType( EVENT_BANDAGE_BLEEDING_MERCS ); if ( fSomeoneStillBleedingDying ) { AddStrategicEvent( EVENT_BANDAGE_BLEEDING_MERCS, GetWorldTotalMin() + 1, 0 ); } } void ReEvaluateEveryonesNothingToDo() { INT32 iCounter = 0; SOLDIERTYPE *pSoldier = NULL; BOOLEAN fNothingToDo; for( iCounter = 0; iCounter <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; iCounter++ ) { pSoldier = &Menptr[ iCounter ]; if( pSoldier->bActive ) { switch( pSoldier->bAssignment ) { case DOCTOR: fNothingToDo = !CanCharacterDoctor( pSoldier ) || ( GetNumberThatCanBeDoctored( pSoldier, HEALABLE_EVER, FALSE, FALSE ) == 0 ); break; case PATIENT: fNothingToDo = !CanCharacterPatient( pSoldier ) || ( AnyDoctorWhoCanHealThisPatient( pSoldier, HEALABLE_EVER ) == NULL ); break; case ASSIGNMENT_HOSPITAL: fNothingToDo = !CanCharacterPatient( pSoldier ); break; case REPAIR: fNothingToDo = !CanCharacterRepair( pSoldier ) || HasCharacterFinishedRepairing( pSoldier ); break; case TRAIN_TOWN: fNothingToDo = !CanCharacterTrainMilitia( pSoldier ); break; case TRAIN_SELF: fNothingToDo = !CanCharacterTrainStat( pSoldier, pSoldier->bTrainStat, TRUE, FALSE ); break; case TRAIN_TEAMMATE: fNothingToDo = !CanCharacterTrainStat( pSoldier, pSoldier->bTrainStat, FALSE, TRUE ) || !ValidTrainingPartnerInSameSectorOnAssignmentFound( pSoldier, TRAIN_BY_OTHER, pSoldier->bTrainStat ); break; case TRAIN_BY_OTHER: fNothingToDo = !CanCharacterTrainStat( pSoldier, pSoldier->bTrainStat, TRUE, FALSE ) || !ValidTrainingPartnerInSameSectorOnAssignmentFound( pSoldier, TRAIN_TEAMMATE, pSoldier->bTrainStat ); break; case VEHICLE: default: // squads fNothingToDo = FALSE; break; } // if his flag is wrong if ( fNothingToDo != pSoldier->flags.fDoneAssignmentAndNothingToDoFlag ) { // update it! pSoldier->flags.fDoneAssignmentAndNothingToDoFlag = fNothingToDo; // update mapscreen's character list display fDrawCharacterList = TRUE; } // if he now has something to do, reset the quote flag if ( !fNothingToDo ) { pSoldier->usQuoteSaidExtFlags &= ~SOLDIER_QUOTE_SAID_DONE_ASSIGNMENT; } } } // re-evaluation completed gfReEvaluateEveryonesNothingToDo = FALSE; // redraw the map, in case we're showing teams, and someone just came on duty or off duty, their icon needs updating fMapPanelDirty = TRUE; } void SetAssignmentForList( INT8 bAssignment, INT8 bParam ) { INT32 iCounter = 0; SOLDIERTYPE *pSelectedSoldier = NULL; SOLDIERTYPE *pSoldier = NULL; BOOLEAN fItWorked; BOOLEAN fRemoveFromSquad = TRUE; BOOLEAN fNotifiedOfFailure = FALSE; INT8 bCanJoinSquad; // if not in mapscreen, there is no functionality available to change multiple assignments simultaneously! if ( !( guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) ) { return; } // pSelectedSoldier is currently used only for REPAIR, and this block of code is copied from RepairMenuBtnCallback() if( bSelectedAssignChar != -1 ) { if( gCharactersList[ bSelectedAssignChar ].fValid == TRUE ) { pSelectedSoldier = &Menptr[ gCharactersList[ bSelectedAssignChar ].usSolID ]; } } Assert( pSelectedSoldier && pSelectedSoldier->bActive ); // sets assignment for the list for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( ( gCharactersList[ iCounter ].fValid ) && ( fSelectedListOfMercsForMapScreen[ iCounter ] == TRUE ) && ( iCounter != bSelectedAssignChar ) && !(Menptr[ gCharactersList[ iCounter ].usSolID].flags.uiStatusFlags & SOLDIER_VEHICLE ) ) { pSoldier = MercPtrs[ gCharactersList[ iCounter ].usSolID ]; // assume it's NOT gonna work fItWorked = FALSE; switch( bAssignment ) { case( DOCTOR ): // can character doctor? if( CanCharacterDoctor( pSoldier ) ) { // set as doctor pSoldier->bOldAssignment = pSoldier->bAssignment; SetSoldierAssignment( pSoldier, DOCTOR, 0,0,0 ); fItWorked = TRUE; } break; case( PATIENT ): // can character patient? if( CanCharacterPatient( pSoldier ) ) { // set as patient pSoldier->bOldAssignment = pSoldier->bAssignment; SetSoldierAssignment( pSoldier, PATIENT, 0,0,0 ); fItWorked = TRUE; } break; case( VEHICLE ): if( CanCharacterVehicle( pSoldier ) && IsThisVehicleAccessibleToSoldier( pSoldier, bParam ) ) { // if ( IsEnoughSpaceInVehicle( bParam ) ) { // if the vehicle is FULL, then this will return FALSE! fItWorked = PutSoldierInVehicle( pSoldier, bParam ); // failure produces its own error popup fNotifiedOfFailure = TRUE; } } break; case( REPAIR ): if( CanCharacterRepair( pSoldier ) ) { BOOLEAN fCanFixSpecificTarget = TRUE; // make sure he can repair the SPECIFIC thing being repaired too (must be in its sector, for example) /* if ( pSelectedSoldier->flags.fFixingSAMSite ) { fCanFixSpecificTarget = CanSoldierRepairSAM( pSoldier, SAM_SITE_REPAIR_DIVISOR ); } else */ if ( pSelectedSoldier->bVehicleUnderRepairID != -1 ) { fCanFixSpecificTarget = CanCharacterRepairVehicle( pSoldier, pSelectedSoldier->bVehicleUnderRepairID ); } else if( pSoldier->flags.fFixingRobot ) { fCanFixSpecificTarget = CanCharacterRepairRobot( pSoldier ); } if ( fCanFixSpecificTarget ) { // set as repair pSoldier->bOldAssignment = pSoldier->bAssignment; SetSoldierAssignment( pSoldier, REPAIR, pSelectedSoldier->flags.fFixingSAMSite, pSelectedSoldier->flags.fFixingRobot, pSelectedSoldier->bVehicleUnderRepairID ); fItWorked = TRUE; } } break; case( TRAIN_SELF ): if( CanCharacterTrainStat( pSoldier, bParam , TRUE, FALSE ) ) { pSoldier->bOldAssignment = pSoldier->bAssignment; SetSoldierAssignment( pSoldier, TRAIN_SELF, bParam, 0,0 ); fItWorked = TRUE; } break; case( TRAIN_TOWN ): if( CanCharacterTrainMilitia( pSoldier ) ) { pSoldier->bOldAssignment = pSoldier->bAssignment; SetSoldierAssignment( pSoldier, TRAIN_TOWN, 0, 0, 0 ); fItWorked = TRUE; } break; case( TRAIN_TEAMMATE ): if( CanCharacterTrainStat( pSoldier, bParam, FALSE, TRUE ) ) { pSoldier->bOldAssignment = pSoldier->bAssignment; SetSoldierAssignment( pSoldier, TRAIN_TEAMMATE, bParam, 0,0 ); fItWorked = TRUE; } break; case TRAIN_BY_OTHER: if( CanCharacterTrainStat( pSoldier, bParam, TRUE, FALSE ) ) { pSoldier->bOldAssignment = pSoldier->bAssignment; SetSoldierAssignment( pSoldier, TRAIN_BY_OTHER, bParam, 0,0 ); fItWorked = TRUE; } break; case( SQUAD_1 ): case( SQUAD_2 ): case( SQUAD_3 ): case( SQUAD_4 ): case( SQUAD_5 ): case( SQUAD_6 ): case( SQUAD_7 ): case( SQUAD_8 ): case( SQUAD_9 ): case( SQUAD_10 ): case( SQUAD_11 ): case( SQUAD_12 ): case( SQUAD_13 ): case( SQUAD_14 ): case( SQUAD_15 ): case( SQUAD_16 ): case( SQUAD_17 ): case( SQUAD_18 ): case( SQUAD_19 ): case( SQUAD_20 ): bCanJoinSquad = CanCharacterSquad( pSoldier, ( INT8 )bAssignment ); // if already in it, don't repor that as an error... if ( ( bCanJoinSquad == CHARACTER_CAN_JOIN_SQUAD ) || ( bCanJoinSquad == CHARACTER_CANT_JOIN_SQUAD_ALREADY_IN_IT ) ) { if ( bCanJoinSquad == CHARACTER_CAN_JOIN_SQUAD ) { pSoldier->bOldAssignment = pSoldier->bAssignment; // is the squad between sectors if( Squad[ bAssignment ][ 0 ] ) { if( Squad[ bAssignment ][ 0 ]->flags.fBetweenSectors ) { // between sectors, remove from old mvt group if ( pSoldier->bOldAssignment >= ON_DUTY ) { // remove from group // the guy wasn't in a sqaud, but moving through a sector? if ( pSoldier->ubGroupID != 0 ) { // now remove from mvt group RemovePlayerFromGroup( pSoldier->ubGroupID, pSoldier ); } } } } if( pSoldier->bOldAssignment == VEHICLE ) { TakeSoldierOutOfVehicle( pSoldier ); } // remove from current squad, if any RemoveCharacterFromSquads( pSoldier ); // able to add, do it AddCharacterToSquad( pSoldier, bAssignment ); } fItWorked = TRUE; fRemoveFromSquad = FALSE; // already done, would screw it up! } break; default: // remove from current vehicle/squad, if any if( pSoldier->bAssignment == VEHICLE ) { TakeSoldierOutOfVehicle( pSoldier ); } RemoveCharacterFromSquads( pSoldier ); AddCharacterToAnySquad( pSoldier ); fItWorked = TRUE; fRemoveFromSquad = FALSE; // already done, would screw it up! break; } if ( fItWorked ) { if ( fRemoveFromSquad ) { // remove him from his old squad if he was on one RemoveCharacterFromSquads( pSoldier ); } MakeSoldiersTacticalAnimationReflectAssignment( pSoldier ); } else { // didn't work - report it once if( !fNotifiedOfFailure ) { fNotifiedOfFailure = TRUE; NotifyPlayerOfAssignmentAttemptFailure( bAssignment ); } } } } // reset list // ResetSelectedListForMapScreen( ); //CHRISL: When setting mercs to a squad, resort the Squad list so we're in ubID order so team panel appears the same // when switching between squads switch( bAssignment ) { case( SQUAD_1 ): case( SQUAD_2 ): case( SQUAD_3 ): case( SQUAD_4 ): case( SQUAD_5 ): case( SQUAD_6 ): case( SQUAD_7 ): case( SQUAD_8 ): case( SQUAD_9 ): case( SQUAD_10 ): case( SQUAD_11 ): case( SQUAD_12 ): case( SQUAD_13 ): case( SQUAD_14 ): case( SQUAD_15 ): case( SQUAD_16 ): case( SQUAD_17 ): case( SQUAD_18 ): case( SQUAD_19 ): case( SQUAD_20 ): SortSquadByID(bAssignment); break; } // check if we should start/stop flashing any mercs' assignment strings after these changes gfReEvaluateEveryonesNothingToDo = TRUE; return; } BOOLEAN IsCharacterAliveAndConscious( SOLDIERTYPE *pCharacter ) { // is the character alive and conscious? if( pCharacter->stats.bLife < CONSCIOUSNESS ) { return( FALSE ); } return ( TRUE ); } BOOLEAN ValidTrainingPartnerInSameSectorOnAssignmentFound( SOLDIERTYPE *pTargetSoldier, INT8 bTargetAssignment, INT8 bTargetStat ) { INT32 iCounter = 0; SOLDIERTYPE *pSoldier = NULL; INT16 sTrainingPts = 0; BOOLEAN fAtGunRange = FALSE; UINT16 usMaxPts; // this function only makes sense for training teammates or by others, not for self training which doesn't require partners Assert( ( bTargetAssignment == TRAIN_TEAMMATE ) || ( bTargetAssignment == TRAIN_BY_OTHER ) ); for( iCounter = 0; iCounter <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; iCounter++ ) { pSoldier = &Menptr[ iCounter ]; if ( pSoldier->bActive ) { // if the guy is not the target, has the assignment we want, is training the same stat, and is in our sector, alive // and is training the stat we want if( ( pSoldier != pTargetSoldier ) && ( pSoldier->bAssignment == bTargetAssignment ) && // CJC: this seems incorrect in light of the check for bTargetStat and in any case would // cause a problem if the trainer was assigned and we weren't! //( pSoldier->bTrainStat == pTargetSoldier->bTrainStat ) && ( pSoldier->sSectorX == pTargetSoldier->sSectorX ) && ( pSoldier->sSectorY == pTargetSoldier->sSectorY ) && ( pSoldier->bSectorZ == pTargetSoldier->bSectorZ ) && ( pSoldier->bTrainStat == bTargetStat ) && ( pSoldier->stats.bLife > 0 ) ) { // so far so good, now let's see if the trainer can really teach the student anything new // are we training in the sector with gun range in Alma? if ( ( pSoldier->sSectorX == GUN_RANGE_X ) && (pSoldier->sSectorY == GUN_RANGE_Y ) && ( pSoldier->bSectorZ == GUN_RANGE_Z ) ) { fAtGunRange = TRUE; } if ( pSoldier->bAssignment == TRAIN_TEAMMATE ) { // pSoldier is the instructor, target is the student sTrainingPts = GetBonusTrainingPtsDueToInstructor( pSoldier, pTargetSoldier, bTargetStat, fAtGunRange, &usMaxPts ); } else { // target is the instructor, pSoldier is the student sTrainingPts = GetBonusTrainingPtsDueToInstructor( pTargetSoldier, pSoldier, bTargetStat, fAtGunRange, &usMaxPts ); } if ( sTrainingPts > 0 ) { // yes, then he makes a valid training partner for us! return( TRUE ); } } } } // no one found return( FALSE ); } void UnEscortEPC( SOLDIERTYPE *pSoldier ) { if ( guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) { BOOLEAN fGotInfo; UINT16 usQuoteNum; UINT16 usFactToSetToTrue; SetupProfileInsertionDataForSoldier( pSoldier ); fGotInfo = GetInfoForAbandoningEPC( pSoldier->ubProfile, &usQuoteNum, &usFactToSetToTrue ); if ( fGotInfo ) { // say quote usQuoteNum gMercProfiles[ pSoldier->ubProfile ].ubMiscFlags |= PROFILE_MISC_FLAG_FORCENPCQUOTE; TacticalCharacterDialogue( pSoldier, usQuoteNum ); // the flag will be turned off in the remove-epc event //gMercProfiles[ pSoldier->ubProfile ].ubMiscFlags &= ~PROFILE_MISC_FLAG_FORCENPCQUOTE; SetFactTrue( usFactToSetToTrue ); } SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_REMOVE_EPC, pSoldier->ubProfile, 0, 0, 0, 0 ); HandleFactForNPCUnescorted( pSoldier->ubProfile ); if ( pSoldier->ubProfile == JOHN ) { SOLDIERTYPE * pSoldier2; // unrecruit Mary as well pSoldier2 = FindSoldierByProfileID( MARY, TRUE ); if ( pSoldier2 ) { SetupProfileInsertionDataForSoldier( pSoldier2 ); fGotInfo = GetInfoForAbandoningEPC( MARY, &usQuoteNum, &usFactToSetToTrue ); if ( fGotInfo ) { // say quote usQuoteNum gMercProfiles[ MARY ].ubMiscFlags |= PROFILE_MISC_FLAG_FORCENPCQUOTE; TacticalCharacterDialogue( pSoldier2, usQuoteNum ); // the flag will be turned off in the remove-epc event //gMercProfiles[ MARY ].ubMiscFlags &= ~PROFILE_MISC_FLAG_FORCENPCQUOTE; SetFactTrue( usFactToSetToTrue ); } SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_REMOVE_EPC, MARY, 0, 0, 0, 0 ); } } else if ( pSoldier->ubProfile == MARY ) { SOLDIERTYPE * pSoldier2; // unrecruit John as well pSoldier2 = FindSoldierByProfileID( JOHN, TRUE ); if ( pSoldier2 ) { SetupProfileInsertionDataForSoldier( pSoldier2 ); fGotInfo = GetInfoForAbandoningEPC( JOHN, &usQuoteNum, &usFactToSetToTrue ); if ( fGotInfo ) { // say quote usQuoteNum gMercProfiles[ JOHN ].ubMiscFlags |= PROFILE_MISC_FLAG_FORCENPCQUOTE; TacticalCharacterDialogue( pSoldier2, usQuoteNum ); // the flag will be turned off in the remove-epc event //gMercProfiles[ JOHN ].ubMiscFlags &= ~PROFILE_MISC_FLAG_FORCENPCQUOTE; SetFactTrue( usFactToSetToTrue ); } SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_REMOVE_EPC, JOHN, 0, 0, 0, 0 ); } } // stop showing menu giAssignHighLine = -1; // set dirty flag fTeamPanelDirty = TRUE; fMapScreenBottomDirty = TRUE; fCharacterInfoPanelDirty = TRUE; } else { // how do we handle this if it's the right sector? TriggerNPCWithGivenApproach( pSoldier->ubProfile, APPROACH_EPC_IN_WRONG_SECTOR, TRUE ); } } BOOLEAN CharacterIsTakingItEasy( SOLDIERTYPE *pSoldier ) { // actually asleep? if ( pSoldier->flags.fMercAsleep == TRUE ) { return( TRUE ); } // if able to sleep if ( CanCharacterSleep( pSoldier, FALSE ) ) { // on duty, but able to catch naps (either not traveling, or not the driver of the vehicle) // The actual checks for this are in the "can he sleep" check above if ( ( pSoldier->bAssignment < ON_DUTY ) || ( pSoldier->bAssignment == VEHICLE ) ) { return( TRUE ); } // and healing up? if ( ( pSoldier->bAssignment == PATIENT ) || ( pSoldier->bAssignment == ASSIGNMENT_HOSPITAL ) ) { return( TRUE ); } // on a real assignment, but done with it? if ( pSoldier->flags.fDoneAssignmentAndNothingToDoFlag ) { return( TRUE ); } } // on assignment, or walking/driving & unable to sleep return( FALSE ); } UINT8 CalcSoldierNeedForSleep( SOLDIERTYPE *pSoldier ) { UINT8 ubNeedForSleep; UINT8 ubPercentHealth; // base comes from profile ubNeedForSleep = gMercProfiles[ pSoldier->ubProfile ].ubNeedForSleep; ubPercentHealth = pSoldier->stats.bLife / pSoldier->stats.bLifeMax; if ( ubPercentHealth < 75 ) { ubNeedForSleep++; if ( ubPercentHealth < 50 ) { ubNeedForSleep++; if ( ubPercentHealth < 25 ) { ubNeedForSleep += 2; } } } // reduce for each Night Ops or Martial Arts trait ubNeedForSleep -= NUM_SKILL_TRAITS( pSoldier, NIGHTOPS ); ubNeedForSleep -= NUM_SKILL_TRAITS( pSoldier, MARTIALARTS ); if ( ubNeedForSleep < 4 ) { ubNeedForSleep = 4; } if ( ubNeedForSleep > 12 ) { ubNeedForSleep = 12; } return( ubNeedForSleep ); } UINT32 GetLastSquadListedInSquadMenu( void ) { UINT32 uiMaxSquad; uiMaxSquad = GetLastSquadActive( ) + 1; if ( uiMaxSquad >= NUMBER_OF_SQUADS ) { uiMaxSquad = NUMBER_OF_SQUADS - 1; } return( uiMaxSquad ); } BOOLEAN CanCharacterRepairAnotherSoldiersStuff( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pOtherSoldier ) { if ( pOtherSoldier == pSoldier ) { return( FALSE ); } if ( !pOtherSoldier->bActive ) { return( FALSE ); } if ( pOtherSoldier->stats.bLife == 0 ) { return( FALSE ); } if ( pOtherSoldier->sSectorX != pSoldier->sSectorX || pOtherSoldier->sSectorY != pSoldier->sSectorY || pOtherSoldier->bSectorZ != pSoldier->bSectorZ ) { return( FALSE ); } if ( pOtherSoldier->flags.fBetweenSectors ) { return( FALSE ); } if ( ( pOtherSoldier->bAssignment == IN_TRANSIT ) || ( pOtherSoldier->bAssignment == ASSIGNMENT_POW ) || ( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) || ( AM_A_ROBOT( pSoldier ) ) || ( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__EPC ) || ( pOtherSoldier->bAssignment == ASSIGNMENT_DEAD ) ) { return( FALSE ); } return( TRUE ); } SOLDIERTYPE *GetSelectedAssignSoldier( BOOLEAN fNullOK ) { SOLDIERTYPE *pSoldier = NULL; if ( (guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) ) { // mapscreen version if( ( bSelectedAssignChar >= 0 ) && ( bSelectedAssignChar < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ) && ( gCharactersList[ bSelectedAssignChar ].fValid ) ) { pSoldier = &Menptr[ gCharactersList[ bSelectedAssignChar ].usSolID ]; } } else { // tactical version pSoldier = &Menptr[ gusUIFullTargetID ]; } if ( !fNullOK ) { Assert( pSoldier ); } if ( pSoldier != NULL ) { // better be an active person, not a vehicle Assert( pSoldier->bActive ); Assert( !( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) ); } return( pSoldier ); } void ResumeOldAssignment( SOLDIERTYPE *pSoldier ) { BOOLEAN fOldAssignmentInvalid = FALSE; // ARM: I don't think the whole "old assignment" idea is a very good one, and I doubt the code that maintains that // variable is very foolproof, plus what meaning does the old assignemnt have later, anyway? // so I'd rather just settle for putting him into any squad: fOldAssignmentInvalid = TRUE; /* if ( pSoldier->bOldAssignment == pSoldier->bAssigment ) { // no good: we rely on this to make sure guys training militia STOP training militia! fOldAssignmentInvalid = TRUE; } else if( pSoldier->bOldAssignment == VEHICLE ) { SetSoldierAssignment( pSoldier, ( INT8 )( pSoldier->bOldAssignment ), ( pSoldier->iVehicleId ), 0, 0 ); // it might not work - check if ( pSoldier->bAssignment != VEHICLE ) { fOldAssignmentInvalid = TRUE; } } else if( pSoldier->bOldAssignment < ON_DUTY ) { if( AddCharacterToSquad( pSoldier, pSoldier->bOldAssignment ) == FALSE ) { fOldAssignmentInvalid = TRUE; } } else { fOldAssignmentInvalid = TRUE; } */ if ( fOldAssignmentInvalid ) { AddCharacterToAnySquad( pSoldier ); } // make sure the player has time to OK this before proceeding StopTimeCompression(); // assignment has changed, redraw left side as well as the map (to update on/off duty icons) fTeamPanelDirty = TRUE; fCharacterInfoPanelDirty = TRUE; fMapPanelDirty = TRUE; } void RepairItemsOnOthers( SOLDIERTYPE *pSoldier, UINT8 *pubRepairPtsLeft ) { UINT8 ubPassType; INT8 bLoop; SOLDIERTYPE * pOtherSoldier; SOLDIERTYPE * pBestOtherSoldier; INT8 bPriority, bBestPriority = -1; BOOLEAN fSomethingWasRepairedThisPass; // repair everyone's hands and armor slots first, then headgear, and pockets last for ( ubPassType = REPAIR_HANDS_AND_ARMOR; ubPassType <= FINAL_REPAIR_PASS; ubPassType++ ) { fSomethingWasRepairedThisPass = FALSE; // look for jammed guns on other soldiers in sector and unjam them for( bLoop = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; bLoop < gTacticalStatus.Team[ gbPlayerNum ].bLastID; bLoop++ ) { pOtherSoldier = MercPtrs[ bLoop ]; // check character is valid, alive, same sector, not between, has inventory, etc. if ( CanCharacterRepairAnotherSoldiersStuff( pSoldier, pOtherSoldier ) ) { if ( UnjamGunsOnSoldier( pOtherSoldier, pSoldier, pubRepairPtsLeft ) ) { fSomethingWasRepairedThisPass = TRUE; } } } while ( *pubRepairPtsLeft > 0 ) { bBestPriority = -1; pBestOtherSoldier = NULL; // now look for items to repair on other mercs for( bLoop = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; bLoop < gTacticalStatus.Team[ gbPlayerNum ].bLastID; bLoop++ ) { pOtherSoldier = MercPtrs[ bLoop ]; // check character is valid, alive, same sector, not between, has inventory, etc. if ( CanCharacterRepairAnotherSoldiersStuff( pSoldier, pOtherSoldier ) ) { // okay, seems like a candidate! if ( FindRepairableItemOnOtherSoldier( pOtherSoldier, ubPassType ) != 0 ) { bPriority = pOtherSoldier->stats.bExpLevel; if ( bPriority > bBestPriority ) { bBestPriority = bPriority; pBestOtherSoldier = pOtherSoldier; } } } } // did we find anyone to repair on this pass? if ( pBestOtherSoldier != NULL ) { // yes, repair all items (for this pass type!) on this soldier that need repair OBJECTTYPE * pObjectToRepair; do { pObjectToRepair = FindRepairableItemOnOtherSoldier( pBestOtherSoldier, ubPassType ); if ( pObjectToRepair ) { if ( RepairObject( pSoldier, pBestOtherSoldier, pObjectToRepair, pubRepairPtsLeft ) ) { fSomethingWasRepairedThisPass = TRUE; } } } while ( pObjectToRepair && *pubRepairPtsLeft > 0 ); } else { break; } } if ( fSomethingWasRepairedThisPass && !DoesCharacterHaveAnyItemsToRepair( pSoldier, ubPassType ) ) { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, sRepairsDoneString[ 1 + ubPassType ], pSoldier->name ); // let player react StopTimeCompression(); } } } BOOLEAN UnjamGunsOnSoldier( SOLDIERTYPE *pOwnerSoldier, SOLDIERTYPE *pRepairSoldier, UINT8 *pubRepairPtsLeft ) { BOOLEAN fAnyGunsWereUnjammed = FALSE; INT8 bPocket; // try to unjam everything before beginning any actual repairs.. successful unjamming costs 2 points per weapon // CHRISL: Changed to dynamically determine max inventory locations. for (bPocket = HANDPOS; bPocket < NUM_INV_SLOTS; bPocket++) { // the object a weapon? and jammed? if ( ( Item[ pOwnerSoldier->inv[ bPocket ].usItem ].usItemClass == IC_GUN ) && ( pOwnerSoldier->inv[ bPocket ][0]->data.gun.bGunAmmoStatus < 0 ) ) { if ( *pubRepairPtsLeft >= gGameExternalOptions.ubRepairCostPerJam ) { *pubRepairPtsLeft -= gGameExternalOptions.ubRepairCostPerJam; pOwnerSoldier->inv [ bPocket ][0]->data.gun.bGunAmmoStatus *= -1; // MECHANICAL/DEXTERITY GAIN: Unjammed a gun StatChange( pRepairSoldier, MECHANAMT, 5, FALSE ); StatChange( pRepairSoldier, DEXTAMT, 5, FALSE ); // report it as unjammed if ( pRepairSoldier == pOwnerSoldier ) { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, gzLateLocalizedString[ 53 ], pRepairSoldier->name, ItemNames[ pOwnerSoldier->inv[ bPocket ].usItem ] ); } else { // NOTE: may need to be changed for localized versions ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, gzLateLocalizedString[ 54 ], pRepairSoldier->name, pOwnerSoldier->name, ItemNames[ pOwnerSoldier->inv[ bPocket ].usItem ] ); } fAnyGunsWereUnjammed = TRUE; } else { // out of points, we're done for now break; } } } return ( fAnyGunsWereUnjammed ); }