From 68869f77b2546569f4f721ad49693ccfea1573b0 Mon Sep 17 00:00:00 2001 From: Flugente Date: Sat, 7 Mar 2015 15:53:25 +0000 Subject: [PATCH] If ALLOW_BANDAGING_DURING_TRAVEL is set to TRUE, travelling mercs will bandage themselves if they bleed. This behaviour is off by default. git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@7772 3b4a5df2-a311-0410-b5c6-a8a6f20db521 --- GameSettings.cpp | 1 + GameSettings.h | 1 + Strategic/Assignments.cpp | 2 - Strategic/Auto Resolve.cpp | 345 ---------------------------------- Strategic/mapscreen.cpp | 17 +- Strategic/mapscreen.h | 3 + Tactical/Auto Bandage.cpp | 300 ++++++++++++++++++++--------- Tactical/Auto Bandage.h | 9 + Tactical/Soldier Control.cpp | 355 ++++++++++++++++++++++++++++++++++- Tactical/Soldier Control.h | 3 + Utils/Text.h | 2 + Utils/_ChineseText.cpp | 2 + Utils/_DutchText.cpp | 2 + Utils/_EnglishText.cpp | 2 + Utils/_FrenchText.cpp | 2 + Utils/_GermanText.cpp | 2 + Utils/_ItalianText.cpp | 2 + Utils/_PolishText.cpp | 2 + Utils/_RussianText.cpp | 2 + 19 files changed, 610 insertions(+), 444 deletions(-) diff --git a/GameSettings.cpp b/GameSettings.cpp index 90cf9a9b4..3278f7247 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -2160,6 +2160,7 @@ void LoadGameExternalOptions() gGameExternalOptions.ubBaseMedicalSkillToDealWithEmergency = iniReader.ReadInteger("Strategic Assignment Settings","BASE_MEDICAL_SKILL_TO_DEAL_WITH_EMERGENCY",20, 0, 100); gGameExternalOptions.ubMultiplierForDifferenceInLifeValueForEmergency = iniReader.ReadInteger("Strategic Assignment Settings","MULTIPLIER_FOR_DIFFERENCE_IN_LIFE_VALUE_FOR_EMERGENCY",4, 1, 10); gGameExternalOptions.ubPointCostPerHealthBelowOkLife = iniReader.ReadInteger("Strategic Assignment Settings","POINT_COST_PER_HEALTH_BELOW_OKLIFE",2, 1, 5);//OKLIFE = 15 + gGameExternalOptions.fAllowBandagingDuringTravel = iniReader.ReadBoolean("Strategic Assignment Settings","ALLOW_BANDAGING_DURING_TRAVEL", FALSE ); ////////// REPAIR ////////// diff --git a/GameSettings.h b/GameSettings.h index 6ad146e4d..2d3cdfbe5 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -637,6 +637,7 @@ typedef struct INT32 ubBaseMedicalSkillToDealWithEmergency; INT32 ubMultiplierForDifferenceInLifeValueForEmergency; INT32 ubPointCostPerHealthBelowOkLife;//OKLIFE = 15 + BOOLEAN fAllowBandagingDuringTravel; INT32 ubRepairCostPerJam; INT32 ubRepairRateDivisor; diff --git a/Strategic/Assignments.cpp b/Strategic/Assignments.cpp index d4894f7cc..b138d2a54 100644 --- a/Strategic/Assignments.cpp +++ b/Strategic/Assignments.cpp @@ -599,8 +599,6 @@ extern BOOLEAN SectorIsImpassable( INT16 sSector ); extern BOOLEAN CanChangeSleepStatusForCharSlot( INT8 bCharNumber ); -extern UINT32 VirtualSoldierDressWound( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pVictim, OBJECTTYPE *pKit, INT16 sKitPts, INT16 sStatus, BOOLEAN fOnSurgery ); // variable added by SANDRO - // only 2 trainers are allowed per sector, so this function counts the # in a guy's sector // HEADROCK HAM 3.6: Now takes an extra argument for Militia Type INT8 CountMilitiaTrainersInSoldiersSector( SOLDIERTYPE * pSoldier, UINT8 ubMilitiaType ); diff --git a/Strategic/Auto Resolve.cpp b/Strategic/Auto Resolve.cpp index 5eeb23168..d4a8a7b8c 100644 --- a/Strategic/Auto Resolve.cpp +++ b/Strategic/Auto Resolve.cpp @@ -1473,351 +1473,6 @@ void ExpandWindow() } -////////////////////////////////////////////////////////////////////////////////////////////////////// -// SANDRO - This whole procedure was merged with the surgery ability of the doctor trait -UINT32 VirtualSoldierDressWound( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pVictim, OBJECTTYPE *pKit, INT16 sKitPts, INT16 sStatus, BOOLEAN fOnSurgery ) -{ - UINT32 uiDressSkill, uiPossible, uiActual, uiMedcost, uiDeficiency, uiAvailAPs, uiUsedAPs; - UINT8 bBelowOKlife, bPtsLeft; - INT8 bInitialBleeding; - - if( pVictim->bBleeding < 1 && !fOnSurgery ) - return 0; // nothing to do, shouldn't have even been called! - if ( pVictim->stats.bLife == 0 ) - return 0; - if (fOnSurgery && pVictim->ubID == pSoldier->ubID) // cannot make surgery on self - return 0; - - bInitialBleeding = pVictim->bBleeding; - - if ( !gGameOptions.fNewTraitSystem && fOnSurgery) // cannot make surgery if not new traits - fOnSurgery = FALSE; - - // calculate wound-dressing skill (3x medical, 2x equip, 1x level, 1x dex) - if ( gGameOptions.fNewTraitSystem ) - { - uiDressSkill = ( ( 7 * EffectiveMedical( pSoldier ) ) + // medical knowledge - ( sStatus) + // state of medical kit - (10 * EffectiveExpLevel( pSoldier ) ) + // battle injury experience - EffectiveDexterity( pSoldier, FALSE ) ) / 10; // general "handiness" - } - else - { - uiDressSkill = ( ( 3 * EffectiveMedical( pSoldier ) ) + // medical knowledge - ( 2 * sStatus) + // state of medical kit - (10 * EffectiveExpLevel( pSoldier ) ) + // battle injury experience - EffectiveDexterity( pSoldier, FALSE ) ) / 7; // general "handiness" - } - - // try to use every AP that the merc has left - uiAvailAPs = pSoldier->bActionPoints; - - // OK, If we are in real-time, use another value... - if (!(gTacticalStatus.uiFlags & TURNBASED) || !(gTacticalStatus.uiFlags & INCOMBAT ) ) - { // Set to a value which looks good based on out tactical turns duration - uiAvailAPs = RT_FIRST_AID_GAIN_MODIFIER; - } - - // calculate how much bandaging CAN be done this turn - uiPossible = ( uiAvailAPs * uiDressSkill ) / 50; // max rate is 2 * fullAPs - - // if no healing is possible (insufficient APs or insufficient dressSkill) - if (!uiPossible) - return 0; - - if (Item[pSoldier->inv[0].usItem].medicalkit && !(fOnSurgery) ) // using the GOOD medic stuff - uiPossible += ( uiPossible / 2); // add extra 50 % - - // Doctor trait improves basic bandaging ability - if (!(fOnSurgery) && gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( pSoldier, DOCTOR_NT )) - { - uiPossible = uiPossible * (100 - gSkillTraitValues.bSpeedModifierBandaging) / 100; - uiPossible += ( uiPossible * gSkillTraitValues.ubDOBandagingSpeedPercent * NUM_SKILL_TRAITS( pSoldier, DOCTOR_NT ) + pSoldier->GetBackgroundValue(BG_PERC_BANDAGING) ) / 100; - } - - uiActual = uiPossible; // start by assuming maximum possible - - // figure out how far below OKLIFE the victim is - if (pVictim->stats.bLife >= OKLIFE) - bBelowOKlife = 0; - else - bBelowOKlife = OKLIFE - pVictim->stats.bLife; - - // figure out how many healing pts we need to stop dying (2x cost) - uiDeficiency = (2 * bBelowOKlife ); - - // if, after that, the patient will still be bleeding - if ( (pVictim->bBleeding - bBelowOKlife ) > 0) - { // then add how many healing pts we need to stop bleeding (1x cost) - uiDeficiency += ( pVictim->bBleeding - bBelowOKlife ); - } - // On surgery, alter this by amount of life we can heal - if ( fOnSurgery ) - { - uiDeficiency += (pVictim->iHealableInjury / 100); - } - // now, make sure we weren't going to give too much - if ( uiActual > uiDeficiency) // if we were about to apply too much - uiActual = uiDeficiency; // reduce actual not to waste anything - - // now make sure we HAVE that much - if( Item[pKit->usItem].medicalkit ) - { - if (fOnSurgery) - uiMedcost = (uiActual * gSkillTraitValues.usDOSurgeryMedBagConsumption ) /100; // surgery drains the kit a lot - else - uiMedcost = (uiActual + 1) / 2; // cost is only half, rounded up - - if ( uiMedcost == 0 && uiActual > 0) - uiMedcost = 1; - if ( uiMedcost > (UINT32)sKitPts ) // if we can't afford this - { - uiMedcost = sKitPts; // what CAN we afford? - if (fOnSurgery) // surgery check - uiActual = (uiMedcost * 100 )/ gSkillTraitValues.usDOSurgeryMedBagConsumption; - else - uiActual = uiMedcost * 2; // give double this as aid - } - } - else - { - uiMedcost = uiActual; - if ( uiMedcost == 0 && uiActual > 0) - uiMedcost = 1; - if ( uiMedcost > (UINT32)sKitPts) // can't afford it - uiMedcost = uiActual = sKitPts; // recalc cost AND aid - } - - bPtsLeft = (INT8)uiActual; - // heal real life points first (if below OKLIFE) because we don't want the - // patient still DYING if bandages run out, or medic is disabled/distracted! - // NOTE: Dressing wounds for life below OKLIFE now costs 2 pts/life point! - if ( bPtsLeft && pVictim->stats.bLife < OKLIFE) - { - // if we have enough points to bring him all the way to OKLIFE this turn - if ( bPtsLeft >= (2 * bBelowOKlife ) ) - { - // insta-healable injury check - if (pVictim->iHealableInjury > 0) - { - pVictim->iHealableInjury -= ((OKLIFE - pVictim->stats.bLife) * 100); - if (pVictim->iHealableInjury < 0) - pVictim->iHealableInjury = 0; - } - // raise life to OKLIFE - pVictim->stats.bLife = OKLIFE; - // reduce bleeding by the same number of life points healed up - pVictim->bBleeding -= bBelowOKlife; - - // Flugente: increase bPoisonLife, decrease bPoisonBleeding - pVictim->bPoisonLife = min(pVictim->bPoisonSum, pVictim->bPoisonLife + bBelowOKlife); - pVictim->bPoisonBleeding = max(0, pVictim->bPoisonBleeding - bBelowOKlife); - - // use up appropriate # of actual healing points - bPtsLeft -= (2 * bBelowOKlife); - } - else - { - // insta-healable injury check - if (pVictim->iHealableInjury > 0) - { - pVictim->iHealableInjury -= (( bPtsLeft / 2) * 100); - if (pVictim->iHealableInjury < 0) - pVictim->iHealableInjury = 0; - } - pVictim->stats.bLife += ( bPtsLeft / 2); - pVictim->bBleeding -= ( bPtsLeft / 2); - - // Flugente: increase bPoisonLife, decrease bPoisonBleeding - pVictim->bPoisonLife = min(pVictim->bPoisonSum, pVictim->bPoisonLife + ( bPtsLeft / 2)); - pVictim->bPoisonBleeding = max(0, pVictim->bPoisonBleeding - ( bPtsLeft / 2)); - - bPtsLeft = bPtsLeft % 2; // if ptsLeft was odd, ptsLeft = 1 - } - - // this should never happen any more, but make sure bleeding not negative - if (pVictim->bBleeding < 0) - { - pVictim->bBleeding = 0; - pVictim->bPoisonBleeding = 0; - } - - // if this healing brought the patient out of the worst of it, cancel dying - if (pVictim->stats.bLife >= OKLIFE ) - { // turn off merc QUOTE flags - pVictim->flags.fDyingComment = FALSE; - } - - if ( pVictim->bBleeding <= MIN_BLEEDING_THRESHOLD ) - { - pVictim->flags.fWarnedAboutBleeding = FALSE; - } - } - - // SURGERY - // first return the real life back, then bandage the rest if possible - if (fOnSurgery && gGameOptions.fNewTraitSystem) // double check for new traits - { - INT32 iLifeReturned = 0; - UINT16 usReturnDamagedStatRate = 0; - // find out if we will repair any stats... - if ( NumberOfDamagedStats( pVictim ) > 0 ) - { - usReturnDamagedStatRate = ((gSkillTraitValues.usDORepairStatsRateBasic + gSkillTraitValues.usDORepairStatsRateOnTop * NUM_SKILL_TRAITS( pSoldier, DOCTOR_NT ))); - usReturnDamagedStatRate -= max( 0, ((usReturnDamagedStatRate * gSkillTraitValues.ubDORepStPenaltyIfAlsoHealing ) / 100)); - - // ... in which case, reduce the points - bPtsLeft = max(0, ((bPtsLeft * ( 100 - gSkillTraitValues.ubDOHealingPenaltyIfAlsoStatRepair )) / 100)); - } - - // Important note! : HealableInjury is always stores the total HPs the victim is missing, not the amount which we will heal, - // so we always take a portion of patient's damage here, reduce the HealableInjury by this portion, while only healing a portion of this portion in actual HPs; - // this means the rest of HPs will remain as "unhealable", the patient will miss X HPs but has no HealableInjury on self.. - if (bPtsLeft >= (pVictim->iHealableInjury/100)) - { - iLifeReturned = pVictim->iHealableInjury * (gSkillTraitValues.ubDOSurgeryHealPercentBase + gSkillTraitValues.ubDOSurgeryHealPercentOnTop * NUM_SKILL_TRAITS( pSoldier, DOCTOR_NT ))/100; - - pVictim->iHealableInjury = 0; - // keep the rest of the points to bandaging if neccessary - if (pVictim->bBleeding > 0) - { - bPtsLeft = max(0, (bPtsLeft - (iLifeReturned / 100))); - bPtsLeft += (bPtsLeft/2); // we use medical bag so add the bonus for that. - } - else - { - bPtsLeft = 0; - } - - // add to record - another surgery undergoed - if ( pVictim->ubProfile != NO_PROFILE && iLifeReturned >= 100 ) - gMercProfiles[ pVictim->ubProfile ].records.usTimesSurgeryUndergoed++; - - // add to record - another surgery made - if ( pSoldier->ubProfile != NO_PROFILE && iLifeReturned >= 100 ) - gMercProfiles[ pSoldier->ubProfile ].records.usSurgeriesMade++; - } - else - { - iLifeReturned = bPtsLeft * (gSkillTraitValues.ubDOSurgeryHealPercentBase + gSkillTraitValues.ubDOSurgeryHealPercentOnTop * NUM_SKILL_TRAITS( pSoldier, DOCTOR_NT )); - - pVictim->iHealableInjury -= (bPtsLeft * 100); - bPtsLeft = 0; - } - // repair the stats here! - if ( usReturnDamagedStatRate > 0 ) - { - RegainDamagedStats( pVictim, (iLifeReturned * usReturnDamagedStatRate / 100) ); - } - - // some paranoya checks for sure - if ((pVictim->stats.bLife + (iLifeReturned / 100)) <= pVictim->stats.bLifeMax) - { - pVictim->stats.bLife += (iLifeReturned/100); - pVictim->bPoisonLife = min(pVictim->bPoisonSum, pVictim->bPoisonLife + (iLifeReturned/100)); - if (pVictim->bBleeding >= (iLifeReturned/100)) - { - pVictim->bBleeding -= (iLifeReturned/100); - pVictim->bPoisonBleeding = max(0, pVictim->bPoisonBleeding - (iLifeReturned/100)); - uiMedcost += (iLifeReturned / 200); // add medkit points cost for unbandaged part - } - else - { - pVictim->bBleeding = 0; - pVictim->bPoisonBleeding = 0; - uiMedcost += max( 0, (((iLifeReturned/100) - pVictim->bBleeding) / 2)); // add medkit points cost for unbandaged part - } - } - else // this shouldn't even happen, but we still want to have it here for sure - { - pVictim->stats.bLife = pVictim->stats.bLifeMax; - pVictim->bPoisonLife = pVictim->bPoisonSum; - pVictim->iHealableInjury = 0; - pVictim->bBleeding = 0; - pVictim->bPoisonBleeding = 0; - } - // Reduce max breath based on life returned - if ( (pVictim->bBreathMax - (((iLifeReturned/100) * gSkillTraitValues.usDOSurgeryMaxBreathLoss )/ 100)) <= BREATHMAX_ABSOLUTE_MINIMUM ) - { - pVictim->bBreathMax = BREATHMAX_ABSOLUTE_MINIMUM; - } - else - { - pVictim->bBreathMax -= (((iLifeReturned/100) * gSkillTraitValues.usDOSurgeryMaxBreathLoss )/ 100); - } - - if (pVictim->iHealableInjury > ((pVictim->stats.bLifeMax - pVictim->stats.bLife)*100)) - pVictim->iHealableInjury = ((pVictim->stats.bLifeMax - pVictim->stats.bLife)*100); - else if (pVictim->iHealableInjury < 0) - pVictim->iHealableInjury = 0; - - // Flugente: campaign stats - gCurrentIncident.usIncidentFlags |= INCIDENT_SURGERY; - } - - // if any healing points remain, apply that to any remaining bleeding (1/1) - // DON'T spend any APs/kit pts to cure bleeding until merc is no longer dying - //if ( bPtsLeft && pVictim->bBleeding && !pVictim->dying) - if ( bPtsLeft && pVictim->bBleeding ) - { // if we have enough points to bandage all remaining bleeding this turn - if (bPtsLeft >= pVictim->bBleeding ) - { - bPtsLeft -= pVictim->bBleeding; - pVictim->bBleeding = 0; - pVictim->bPoisonBleeding = 0; - } - else // bandage what we can - { - pVictim->bBleeding -= bPtsLeft; - pVictim->bPoisonBleeding = max(0, pVictim->bPoisonBleeding - bPtsLeft); - bPtsLeft = 0; - } - } - - //CHRISL: If by some chance ubPtsLeft ends up being higher then uiActual, we'll end up with a huge value since uiActual is an unsigned variable. - // if there are any ptsLeft now, then we didn't actually get to use them - uiActual = max(0, (INT32)(uiActual - bPtsLeft)); - - // usedAPs equals (actionPts) * (%of possible points actually used) - uiUsedAPs = ( uiActual * uiAvailAPs ) / uiPossible; - - if (Item[pSoldier->inv[0].usItem].medicalkit && !(fOnSurgery)) // using the GOOD medic stuff - uiUsedAPs = ( uiUsedAPs * 2) / 3; // reverse 50% bonus by taking 2/3rds - - // surgery is harder so cost more BPs - if (fOnSurgery) - DeductPoints( pSoldier, (INT16)uiUsedAPs, (INT16)( uiUsedAPs * 15 ) ); - else - DeductPoints( pSoldier, (INT16)uiUsedAPs, (INT16)( ( uiUsedAPs * APBPConstants[BP_PER_AP_LT_EFFORT]) ) ); - - // surgery is harder so gives more exp - if (fOnSurgery) - { - // MEDICAL GAIN (actual / 2): Helped someone by giving first aid - StatChange(pSoldier, MEDICALAMT, (UINT16)(uiActual + 2), FALSE); - - // DEXTERITY GAIN (actual / 6): Helped someone by giving first aid - StatChange(pSoldier, DEXTAMT, (UINT16)((uiActual / 3) + 2), FALSE); - } - else - { - if ( uiActual / 2) - // MEDICAL GAIN (actual / 2): Helped someone by giving first aid - StatChange(pSoldier,MEDICALAMT,( (UINT16)(uiActual / 2) ),FALSE); - - if ( uiActual / 4) - // DEXTERITY GAIN (actual / 4): Helped someone by giving first aid - StatChange(pSoldier,DEXTAMT,(UINT16)( ( uiActual / 4) ),FALSE); - } - - // merc records - bandaging - if( bInitialBleeding > 1 && pVictim->bBleeding == 0 && pSoldier->ubProfile != NO_PROFILE ) - gMercProfiles[ pSoldier->ubProfile ].records.usMercsBandaged++; - - return uiMedcost; -} -////////////////////////////////////////////////////////////////////////////////////////////////////// - OBJECTTYPE* FindMedicalKit() { INT32 i; diff --git a/Strategic/mapscreen.cpp b/Strategic/mapscreen.cpp index ea98d9eb6..8de209775 100644 --- a/Strategic/mapscreen.cpp +++ b/Strategic/mapscreen.cpp @@ -110,6 +110,7 @@ #include "Facilities.h" // HEADROCK HAM 4: Include Militia Squads for Manual Militia Restrictions toggle. #include "MilitiaSquads.h" + #include "Auto Bandage.h" // added by Flugente #endif #include "connect.h" //hayden @@ -5243,8 +5244,7 @@ UINT32 MapScreenHandle(void) // now do the warning box DoMapMessageBox( MSG_BOX_BASIC_STYLE, pMapErrorString[ 4 ], MAP_SCREEN, MSG_BOX_FLAG_OK, MapScreenDefaultOkBoxCallback ); } - - + fFirstTimeInMapScreen = FALSE; if( gpCurrentTalkingFace != NULL ) @@ -5313,6 +5313,13 @@ UINT32 MapScreenHandle(void) HandleStrategicTurn( ); + // Flugente: bandaging during retreat + if ( RetreatBandagingPending() ) + { + // now do the warning box + DoScreenIndependantMessageBox( TacticalStr[ATTEMPT_BANDAGE_DURING_TRAVEL], MSG_BOX_FLAG_OK, RetreatBandageCallback ); + } + /* // update cursor based on state @@ -16774,3 +16781,9 @@ BOOLEAN CanGiveStrategicMilitiaMoveOrder( INT16 sMapX, INT16 sMapY ) return FALSE; } + +// Flugente: bandaging during retreat +void RetreatBandageCallback( UINT8 ubResult ) +{ + HandleRetreatBandaging( ); +} diff --git a/Strategic/mapscreen.h b/Strategic/mapscreen.h index 7e79cf108..b941ef44a 100644 --- a/Strategic/mapscreen.h +++ b/Strategic/mapscreen.h @@ -161,4 +161,7 @@ BOOLEAN CanGiveStrategicMilitiaMoveOrder( INT16 sMapX, INT16 sMapY ); void ConvertMinTimeToDayHourMinString( UINT32 uiTimeInMin, STR16 sString ); void ConvertMinTimeToETADayHourMinString( UINT32 uiTimeInMin, STR16 sString ); +// Flugente: bandaging during retreat +void RetreatBandageCallback( UINT8 ubResult ); + #endif \ No newline at end of file diff --git a/Tactical/Auto Bandage.cpp b/Tactical/Auto Bandage.cpp index b018e7556..28189fef1 100644 --- a/Tactical/Auto Bandage.cpp +++ b/Tactical/Auto Bandage.cpp @@ -27,6 +27,7 @@ #include "WordWrap.h" #include "cursors.h" #include "English.h" + #include "SkillCheck.h" // added by Flugente #endif #include "Music Control.h" @@ -92,12 +93,11 @@ extern UINT8 NumEnemyInSector( ); void BeginAutoBandage( ) { - INT32 cnt; - BOOLEAN fFoundAGuy = FALSE; + INT32 cnt; + BOOLEAN fFoundAGuy = FALSE; SOLDIERTYPE * pSoldier; - BOOLEAN fFoundAMedKit = FALSE; - - + BOOLEAN fFoundAMedKit = FALSE; + // If we are in combat, we con't... if ( (gTacticalStatus.uiFlags & INCOMBAT) || (NumEnemyInSector() != 0) ) { @@ -107,7 +107,7 @@ void BeginAutoBandage( ) cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; // check for anyone needing bandages - for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++,pSoldier++ ) + for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; ++cnt, ++pSoldier ) { // if the soldier isn't active or in sector, we have problems..leave if ( !(pSoldier->bActive) || !(pSoldier->bInSector) || ( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) || (pSoldier->bAssignment == VEHICLE ) ) @@ -116,23 +116,14 @@ void BeginAutoBandage( ) } // can this character be helped out by a teammate? - if ( CanCharacterBeAutoBandagedByTeammate( pSoldier ) == TRUE ) - { + if ( CanCharacterBeAutoBandagedByTeammate( pSoldier ) ) fFoundAGuy = TRUE; - if ( fFoundAGuy && fFoundAMedKit ) - { - break; - } - } - if ( FindObjClass( pSoldier, IC_MEDKIT ) != NO_SLOT ) - { - fFoundAMedKit = TRUE; - if ( fFoundAGuy && fFoundAMedKit ) - { - break; - } - } + if ( FindObjClass( pSoldier, IC_MEDKIT ) != NO_SLOT ) + fFoundAMedKit = TRUE; + + if ( fFoundAGuy && fFoundAMedKit ) + break; } if ( !fFoundAGuy ) @@ -190,7 +181,7 @@ void HandleAutoBandagePending( ) // Do any guys have pending actions...? cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; - for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; cnt++,pSoldier++) + for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; ++cnt, ++pSoldier) { // Are we in sector? if ( pSoldier->bActive ) @@ -211,10 +202,8 @@ void HandleAutoBandagePending( ) return; } - - // If here, all's a go! - gTacticalStatus.fAutoBandagePending = FALSE; + SetAutoBandagePending( FALSE ); BeginAutoBandage( ); } } @@ -238,7 +227,7 @@ void ShouldBeginAutoBandage( ) // ATE: If not in endgame if ( ( gTacticalStatus.uiFlags & IN_DEIDRANNA_ENDGAME ) ) { - return; + return; } if ( CanAutoBandage( FALSE ) ) @@ -308,34 +297,33 @@ BOOLEAN HandleAutoBandage( ) return( TRUE ); } - - - + return( FALSE ); } BOOLEAN CreateAutoBandageString( void ) { - INT32 cnt; + INT32 cnt; // WDS - make number of mercenaries, etc. be configurable - UINT8 ubDoctor[CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS], ubDoctors = 0; - UINT32 uiDoctorNameStringLength = 1; // for end-of-string character - STR16 sTemp; + UINT8 ubDoctor[CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS], ubDoctors = 0; + UINT32 uiDoctorNameStringLength = 1; // for end-of-string character + STR16 sTemp; SOLDIERTYPE * pSoldier; cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; - for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; cnt++,pSoldier++) + for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; ++cnt, ++pSoldier) { if ( pSoldier->bActive && pSoldier->bInSector && pSoldier->stats.bLife >= OKLIFE && !(pSoldier->bCollapsed) && pSoldier->stats.bMedical > 0 && FindObjClass( pSoldier, IC_MEDKIT ) != NO_SLOT) { ubDoctor[ubDoctors] = pSoldier->ubID; - ubDoctors++; + ++ubDoctors; // increase the length of the string by the size of the name // plus 2, one for the comma and one for the space after that uiDoctorNameStringLength += wcslen( pSoldier->name ) + 2; } } + if (ubDoctors == 0) { return( FALSE ); @@ -370,7 +358,7 @@ BOOLEAN CreateAutoBandageString( void ) return( FALSE ); } wcscpy( sTemp, L"" ); - for (cnt = 0; cnt < ubDoctors - 1; cnt++) + for (cnt = 0; cnt < ubDoctors - 1; ++cnt) { wcscat( sTemp, MercPtrs[ubDoctor[cnt]]->name ); if (ubDoctors > 2) @@ -388,6 +376,7 @@ BOOLEAN CreateAutoBandageString( void ) swprintf( sAutoBandageString, Message[STR_ARE_APPLYING_FIRST_AID], sTemp, MercPtrs[ubDoctor[ubDoctors - 1]]->name ); MemFree( sTemp ); } + return( TRUE ); } @@ -395,8 +384,6 @@ void SetAutoBandageComplete( void ) { // this will set the fact autobandage is complete fAutoBandageComplete = TRUE; - - return; } void AutoBandage( BOOLEAN fStart ) @@ -413,7 +400,6 @@ void AutoBandage( BOOLEAN fStart ) gfAutoBandageFailed = FALSE; - // ste up the autobandage panel SetUpAutoBandageUpdatePanel( ); @@ -426,7 +412,7 @@ void AutoBandage( BOOLEAN fStart ) //SetGameTimeCompressionLevel( TIME_COMPRESS_5MINS ); cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; - for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; cnt++,pSoldier++) + for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; ++cnt, ++pSoldier) { if ( pSoldier->bActive ) { @@ -450,8 +436,7 @@ void AutoBandage( BOOLEAN fStart ) // Determine position ( centered in rect ) gsX = (INT16)( ( ( ( aRect.iRight - aRect.iLeft ) - gusTextBoxWidth ) / 2 ) + aRect.iLeft ); gsY = (INT16)( ( ( ( aRect.iBottom - aRect.iTop ) - gusTextBoxHeight ) / 2 ) + aRect.iTop ); - - + // build a mask MSYS_DefineRegion( &gAutoBandageRegion, 0,0, SCREEN_WIDTH, SCREEN_HEIGHT, MSYS_PRIORITY_HIGHEST - 1, CURSOR_NORMAL, MSYS_NO_CALLBACK, MSYS_NO_CALLBACK ); @@ -466,7 +451,7 @@ void AutoBandage( BOOLEAN fStart ) // make sure anyone under AI control has their action cancelled cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; - for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; cnt++,pSoldier++) + for ( pSoldier = MercPtrs[cnt]; cnt <= gTacticalStatus.Team[OUR_TEAM].bLastID; ++cnt, ++pSoldier ) { // 0verhaul: Make sure the merc is also in the sector before making him stand up! if ( pSoldier->bActive && pSoldier->bInSector ) @@ -486,13 +471,11 @@ void AutoBandage( BOOLEAN fStart ) pSoldier->ChangeSoldierStance( ANIM_STAND ); } } - } } - ubLoop = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; - for ( ; ubLoop <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; ubLoop++) + for ( ; ubLoop <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; ++ubLoop) { ActionDone( MercPtrs[ ubLoop ] ); @@ -589,7 +572,6 @@ void SetUpAutoBandageUpdatePanel( void ) // add to list, up the count iDoctorList[ iNumberDoctoring ] = iCounterA; iNumberDoctoring++; - } } @@ -623,8 +605,6 @@ void SetUpAutoBandageUpdatePanel( void ) AddFacesToAutoBandageBox( ); fAutoBandageComplete = FALSE; - - return; } void DisplayAutoBandageUpdatePanel( void ) @@ -669,7 +649,6 @@ void DisplayAutoBandageUpdatePanel( void ) } // build dimensions of box - if( iNumberDoctors < NUMBER_MERC_FACES_AUTOBANDAGE_BOX ) { // nope, get the base amount @@ -680,8 +659,6 @@ void DisplayAutoBandageUpdatePanel( void ) iNumberDoctorsWide = NUMBER_MERC_FACES_AUTOBANDAGE_BOX; } - - // set the min number of mercs if( iNumberDoctorsWide < 3 ) { @@ -738,9 +715,7 @@ void DisplayAutoBandageUpdatePanel( void ) // now the patients iNumberPatientsHigh = ( iNumberPatients / ( NUMBER_MERC_FACES_AUTOBANDAGE_BOX ) ); } - - - + // now the actual pixel dimensions iTotalPixelsHigh = ( iNumberPatientsHigh + iNumberDoctorsHigh ) * TACT_UPDATE_MERC_FACE_X_HEIGHT; @@ -760,12 +735,10 @@ void DisplayAutoBandageUpdatePanel( void ) // now get the x and y position for the box sXPosition = ( SCREEN_WIDTH - iTotalPixelsWide ) / 2; sYPosition = ( INV_INTERFACE_START_Y - iTotalPixelsHigh ) / 2; - - + // now blit down the background GetVideoObject( &hBackGroundHandle, guiUpdatePanelTactical ); - - + // first the doctors on top for( iCounterA = 0; iCounterA < iNumberDoctorsHigh; iCounterA++ ) { @@ -776,13 +749,11 @@ void DisplayAutoBandageUpdatePanel( void ) // slap down background piece BltVideoObject( FRAME_BUFFER , hBackGroundHandle, 15, sCurrentXPosition, sCurrentYPosition, VO_BLT_SRCTRANSPARENCY, NULL ); - - + iIndex = iCounterA * iNumberDoctorsWide + iCounterB; if( iDoctorList[ iIndex ] != -1 ) { - sCurrentXPosition += TACT_UPDATE_MERC_FACE_X_OFFSET; sCurrentYPosition += TACT_UPDATE_MERC_FACE_Y_OFFSET; @@ -809,11 +780,9 @@ void DisplayAutoBandageUpdatePanel( void ) for( iCounterB = 0; iCounterB < iNumberPatientsWide; iCounterB ++ ) { - // slap down background piece - BltVideoObject( FRAME_BUFFER , hBackGroundHandle, 16, sXPosition + ( iCounterB * TACT_UPDATE_MERC_FACE_X_WIDTH ), sCurrentYPosition + ( TACT_UPDATE_MERC_FACE_X_HEIGHT ), VO_BLT_SRCTRANSPARENCY, NULL ); - BltVideoObject( FRAME_BUFFER , hBackGroundHandle, 16, sXPosition + ( iCounterB * TACT_UPDATE_MERC_FACE_X_WIDTH ), sYPosition - 9 , VO_BLT_SRCTRANSPARENCY, NULL ); - - + // slap down background piece + BltVideoObject( FRAME_BUFFER , hBackGroundHandle, 16, sXPosition + ( iCounterB * TACT_UPDATE_MERC_FACE_X_WIDTH ), sCurrentYPosition + ( TACT_UPDATE_MERC_FACE_X_HEIGHT ), VO_BLT_SRCTRANSPARENCY, NULL ); + BltVideoObject( FRAME_BUFFER , hBackGroundHandle, 16, sXPosition + ( iCounterB * TACT_UPDATE_MERC_FACE_X_WIDTH ), sYPosition - 9 , VO_BLT_SRCTRANSPARENCY, NULL ); } // bordering patient title @@ -827,16 +796,14 @@ void DisplayAutoBandageUpdatePanel( void ) // iCurPixelY = sYPosition; iCurPixelY = sYPosition + ( ( iCounterA - 1 ) * TACT_UPDATE_MERC_FACE_X_HEIGHT ); - - + swprintf( sString, L"%s", zMarksMapScreenText[ 13 ] ); FindFontCenterCoordinates( ( INT16 )( sXPosition ), ( INT16 )( sCurrentYPosition ), ( INT16 )( iTotalPixelsWide ), 0, sString, TINYFONT1, &sX, &sY ); // print medic mprintf( sX, sYPosition - 7 , sString ); //DisplayWrappedString( ( INT16 )( sXPosition ), ( INT16 )( sCurrentYPosition - 40 ), ( INT16 )( iTotalPixelsWide ), 0, TINYFONT1, FONT_WHITE, pUpdateMercStrings[ 0 ], FONT_BLACK, 0, 0 ); - - + sYPosition += 9; // now the patients @@ -844,15 +811,12 @@ void DisplayAutoBandageUpdatePanel( void ) { for( iCounterB = 0; iCounterB < iNumberPatientsWide; iCounterB ++ ) { - sCurrentXPosition = sXPosition + ( iCounterB * TACT_UPDATE_MERC_FACE_X_WIDTH ); sCurrentYPosition = sYPosition + ( ( iCounterA + iNumberDoctorsHigh ) * TACT_UPDATE_MERC_FACE_X_HEIGHT ); - - + // slap down background piece BltVideoObject( FRAME_BUFFER , hBackGroundHandle, 15, sCurrentXPosition, sCurrentYPosition , VO_BLT_SRCTRANSPARENCY, NULL ); - - + iIndex = iCounterA * iNumberPatientsWide + iCounterB; if( iPatientList[ iIndex ] != -1 ) @@ -875,11 +839,9 @@ void DisplayAutoBandageUpdatePanel( void ) // print name mprintf( sX, sY , sString ); } - } } - // BORDER PIECES!!!! // bordering patients squares @@ -889,7 +851,6 @@ void DisplayAutoBandageUpdatePanel( void ) BltVideoObject( FRAME_BUFFER , hBackGroundHandle, 5, sXPosition + iTotalPixelsWide , sYPosition + ( ( iCounterA + iNumberDoctorsHigh ) * TACT_UPDATE_MERC_FACE_X_HEIGHT ), VO_BLT_SRCTRANSPARENCY,NULL ); } - // back up 11 pixels sYPosition-=9; @@ -942,13 +903,9 @@ void DisplayAutoBandageUpdatePanel( void ) // print patient mprintf( sX, iCurPixelY + ( TACT_UPDATE_MERC_FACE_X_HEIGHT ) + 2, sString ); - MarkAButtonDirty( iEndAutoBandageButton[ 0 ] ); MarkAButtonDirty( iEndAutoBandageButton[ 1 ] ); - - - DrawButton( iEndAutoBandageButton[ 0 ] ); DrawButton( iEndAutoBandageButton[ 1 ] ); @@ -968,8 +925,6 @@ void DisplayAutoBandageUpdatePanel( void ) // now make sure it goes to the screen InvalidateRegion( sXPosition - 4, sYPosition - 18, ( INT16 )( sXPosition + iTotalPixelsWide + 4), ( INT16 )( sYPosition + iTotalPixelsHigh ) ); - - return; } @@ -1014,8 +969,6 @@ void CreateTerminateAutoBandageButton( INT16 sX, INT16 sY ) SpecifyButtonFont( iEndAutoBandageButton[ 1 ], MAP_SCREEN_FONT ); SpecifyButtonUpTextColors( iEndAutoBandageButton[ 1 ], FONT_MCOLOR_BLACK, FONT_BLACK ); SpecifyButtonDownTextColors( iEndAutoBandageButton[ 1 ], FONT_MCOLOR_BLACK, FONT_BLACK ); - - return; } @@ -1033,15 +986,12 @@ void StopAutoBandageButtonCallback(GUI_BUTTON *btn,INT32 reason) fEndAutoBandage = TRUE; } } - - return; } void DestroyTerminateAutoBandageButton( void ) { - // destroy the kill autobandage button if( fAutoEndBandageButtonCreated == FALSE ) { @@ -1058,8 +1008,6 @@ void DestroyTerminateAutoBandageButton( void ) // unload image UnloadButtonImage( iEndAutoBandageButtonImage[ 0 ] ); UnloadButtonImage( iEndAutoBandageButtonImage[ 1 ] ); - - return; } @@ -1361,3 +1309,171 @@ BOOLEAN RenderSoldierSmallFaceForAutoBandagePanel( INT32 iIndex, INT16 sCurrentX return( TRUE ); } + +// Flugente: bandaging during retreat +BOOLEAN gRetreatBandagingPending = FALSE; + +void SetRetreatBandaging(BOOLEAN aVal) +{ + gRetreatBandagingPending = aVal; +} + +BOOLEAN RetreatBandagingPending() +{ + return gRetreatBandagingPending; +} + +// return the ID of best doctor that has a medkit and is travelling with pPatient +UINT8 GetBestRetreatingMercDoctor( SOLDIERTYPE* pPatient ) +{ + // if this is a travelling, bleeding merc, can somebody who travels with him bandage him/her? + if ( pPatient && pPatient->bActive && pPatient->flags.fBetweenSectors && pPatient->bBleeding ) + { + UINT16 cnt = gTacticalStatus.Team[OUR_TEAM].bFirstID; + SOLDIERTYPE* pSoldier = NULL; + for ( pSoldier = MercPtrs[cnt]; cnt <= gTacticalStatus.Team[OUR_TEAM].bLastID; ++cnt, ++pSoldier ) + { + // this requires mercs to travel and thus NOT be in a sector + // also we need to be in a specific sector + if ( pSoldier->bActive && pSoldier->flags.fBetweenSectors && pSoldier->sSectorX == pPatient->sSectorX && pSoldier->sSectorY == pPatient->sSectorY ) + { + // find the best conscious doctor that has a medkit + if ( pSoldier->stats.bLife >= OKLIFE && pSoldier->stats.bMedical > 0 && FindObjClass( pSoldier, IC_MEDKIT ) != NO_SLOT ) + { + return cnt; + } + } + } + } + + return NOBODY; +} + + +void HandleRetreatBandaging() +{ + // handle bandaging of retreating mercs + // this function will be called when a travelling merc is bleeding + // make sure anyone under AI control has their action cancelled + + // first, have every injured merc bandage himself + // second, repeat tis process: + // if non-bandaged mercs remain, find the best doctor that still has medkits, else break + // if doctor exists, have the doctor treat all injured mercs, else break + // third, set flag that calls this function off + + // for each call, we only do this for one sector, thereby only for one group retreating from a sector + // we can do this because a) it is unlikely that two wounded groups will retreat from combat at the same time, and b) bleeding calls this function again. + // As long as people bleed, this function will be called, thereby we will get to the group in question at some point + + BOOLEAN needhelpinsector = FALSE; + INT16 sX = -1; + INT16 sY = -1; + UINT8 possiblepatient = NOBODY; + + UINT16 cnt = gTacticalStatus.Team[OUR_TEAM].bFirstID; + SOLDIERTYPE* pSoldier = NULL; + for ( pSoldier = MercPtrs[cnt]; cnt <= gTacticalStatus.Team[OUR_TEAM].bLastID; ++cnt, ++pSoldier ) + { + // this requires mercs to travel and thus NOT be in a sector + // are we bleeding? + if ( pSoldier->bActive && pSoldier->flags.fBetweenSectors && pSoldier->bBleeding ) + { + // if we are still conscious, try bandaging ourself + if ( pSoldier->stats.bLife >= OKLIFE ) + { + UINT32 counter = 0; + INT8 bSlot = -1; + UINT32 uiPointsUsed; + UINT16 usKitPts; + OBJECTTYPE *pKit = NULL; + if ( pSoldier->stats.bMedical > 0 && (bSlot = FindObjClass( pSoldier, IC_MEDKIT )) != NO_SLOT ) + { + while ( pSoldier->bBleeding ) + { + pKit = &pSoldier->inv[bSlot]; + usKitPts = TotalPoints( pKit ); + if ( !usKitPts ) + { + //attempt to find another kit before stopping + if ( (bSlot = FindObjClass( pSoldier, IC_MEDKIT )) != NO_SLOT ) + continue; + + break; + } + + uiPointsUsed = VirtualSoldierDressWound( pSoldier, pSoldier, pKit, usKitPts, usKitPts, FALSE ); // changed by SANDRO + UseKitPoints( pKit, (UINT16)uiPointsUsed, pSoldier ); + ++counter; + + if ( counter > 50 ) + break; + } + } + } + + // if we are still bleeding, other mercs have to help us + if ( pSoldier->bBleeding && !needhelpinsector ) + { + needhelpinsector = TRUE; + sX = pSoldier->sSectorX; + sY = pSoldier->sSectorY; + possiblepatient = cnt; + } + } + } + + // someone is bleeding and can't bandage himself, other mercs have to help + if ( needhelpinsector && possiblepatient != NOBODY) + { + // find the best doctor here + UINT16 bestdoctorid = GetBestRetreatingMercDoctor( MercPtrs[possiblepatient] ); + + if ( bestdoctorid != NOBODY ) + { + // have the doctor treat people + SOLDIERTYPE* pDoctor = MercPtrs[bestdoctorid]; + + cnt = gTacticalStatus.Team[OUR_TEAM].bFirstID; + for ( pSoldier = MercPtrs[cnt]; cnt <= gTacticalStatus.Team[OUR_TEAM].bLastID; ++cnt, ++pSoldier ) + { + // this requires mercs to travel and thus NOT be in a sector + // also we need to be in a specific sector + // treat bleeding people only + if ( pSoldier->bActive && pSoldier->flags.fBetweenSectors && sX == pSoldier->sSectorX && sY == pSoldier->sSectorY && pSoldier->bBleeding ) + { + UINT32 counter = 0; + INT8 bSlot = -1; + UINT32 uiPointsUsed; + UINT16 usKitPts; + OBJECTTYPE *pKit = NULL; + if ( pDoctor->stats.bMedical > 0 && (bSlot = FindObjClass( pDoctor, IC_MEDKIT )) != NO_SLOT ) + { + while ( pSoldier->bBleeding ) + { + pKit = &pDoctor->inv[bSlot]; + usKitPts = TotalPoints( pKit ); + if ( !usKitPts ) + { + //attempt to find another kit before stopping + if ( (bSlot = FindObjClass( pDoctor, IC_MEDKIT )) != NO_SLOT ) + continue; + + break; + } + + uiPointsUsed = VirtualSoldierDressWound( pDoctor, pSoldier, pKit, usKitPts, usKitPts, FALSE ); // changed by SANDRO + UseKitPoints( pKit, (UINT16)uiPointsUsed, pDoctor ); + ++counter; + + if ( counter > 50 ) + break; + } + } + } + } + } + } + + SetRetreatBandaging( FALSE ); +} diff --git a/Tactical/Auto Bandage.h b/Tactical/Auto Bandage.h index c270ee342..39b9f8cba 100644 --- a/Tactical/Auto Bandage.h +++ b/Tactical/Auto Bandage.h @@ -16,4 +16,13 @@ void HandleAutoBandagePending( ); // ste the autobandage as complete void SetAutoBandageComplete( void ); +// Flugente: bandaging during retreat +void SetRetreatBandaging( BOOLEAN aVal ); +BOOLEAN RetreatBandagingPending( ); + +// return the ID of best doctor that has a medkit and is travelling with pPatient +UINT8 GetBestRetreatingMercDoctor( SOLDIERTYPE* pPatient ); + +void HandleRetreatBandaging( ); + #endif \ No newline at end of file diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index c7e91b5a2..cf1c6f051 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -95,6 +95,7 @@ #include "Interface Panels.h" #include "Queen Command.h" // added by Flugente #include "Town Militia.h" // added by Flugente +#include "Auto Bandage.h" // added by Flugente #endif #include "ub_config.h" @@ -10092,14 +10093,18 @@ UINT8 SOLDIERTYPE::SoldierTakeDamage( INT8 bHeight, INT16 sLifeDeduct, INT16 sPo HandlePossibleInfection( this, NULL, INFECTION_TYPE_TRAUMATIC ); } + // Flugente: bandaging during retreat + if ( gGameExternalOptions.fAllowBandagingDuringTravel && ubReason == TAKE_DAMAGE_BLOODLOSS && this->flags.fBetweenSectors && GetBestRetreatingMercDoctor( this ) != NOBODY ) + { + SetRetreatBandaging( TRUE ); + } + // Calculate damage to our items if from an explosion! if ( ubReason == TAKE_DAMAGE_EXPLOSION || ubReason == TAKE_DAMAGE_STRUCTURE_EXPLOSION ) { CheckEquipmentForDamage( this, sLifeDeduct ); } - - - + // Calculate bleeding if ( ubReason != TAKE_DAMAGE_GAS && !AM_A_ROBOT( this ) ) { @@ -22449,3 +22454,347 @@ void SetDamageDisplayCounter( SOLDIERTYPE* pSoldier ) } } +////////////////////////////////////////////////////////////////////////////////////////////////////// +// SANDRO - This whole procedure was merged with the surgery ability of the doctor trait +UINT32 VirtualSoldierDressWound( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pVictim, OBJECTTYPE *pKit, INT16 sKitPts, INT16 sStatus, BOOLEAN fOnSurgery ) +{ + UINT32 uiDressSkill, uiPossible, uiActual, uiMedcost, uiDeficiency, uiAvailAPs, uiUsedAPs; + UINT8 bBelowOKlife, bPtsLeft; + INT8 bInitialBleeding; + + if ( pVictim->bBleeding < 1 && !fOnSurgery ) + return 0; // nothing to do, shouldn't have even been called! + if ( pVictim->stats.bLife == 0 ) + return 0; + if ( fOnSurgery && pVictim->ubID == pSoldier->ubID ) // cannot make surgery on self + return 0; + + bInitialBleeding = pVictim->bBleeding; + + if ( !gGameOptions.fNewTraitSystem && fOnSurgery ) // cannot make surgery if not new traits + fOnSurgery = FALSE; + + // calculate wound-dressing skill (3x medical, 2x equip, 1x level, 1x dex) + if ( gGameOptions.fNewTraitSystem ) + { + uiDressSkill = ((7 * EffectiveMedical( pSoldier )) + // medical knowledge + (sStatus)+ // state of medical kit + (10 * EffectiveExpLevel( pSoldier )) + // battle injury experience + EffectiveDexterity( pSoldier, FALSE )) / 10; // general "handiness" + } + else + { + uiDressSkill = ((3 * EffectiveMedical( pSoldier )) + // medical knowledge + (2 * sStatus) + // state of medical kit + (10 * EffectiveExpLevel( pSoldier )) + // battle injury experience + EffectiveDexterity( pSoldier, FALSE )) / 7; // general "handiness" + } + + // try to use every AP that the merc has left + uiAvailAPs = pSoldier->bActionPoints; + + // OK, If we are in real-time, use another value... + if ( !(gTacticalStatus.uiFlags & TURNBASED) || !(gTacticalStatus.uiFlags & INCOMBAT) ) + { // Set to a value which looks good based on out tactical turns duration + uiAvailAPs = RT_FIRST_AID_GAIN_MODIFIER; + } + + // calculate how much bandaging CAN be done this turn + uiPossible = (uiAvailAPs * uiDressSkill) / 50; // max rate is 2 * fullAPs + + // if no healing is possible (insufficient APs or insufficient dressSkill) + if ( !uiPossible ) + return 0; + + if ( Item[pSoldier->inv[0].usItem].medicalkit && !(fOnSurgery) ) // using the GOOD medic stuff + uiPossible += (uiPossible / 2); // add extra 50 % + + // Doctor trait improves basic bandaging ability + if ( !(fOnSurgery) && gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( pSoldier, DOCTOR_NT ) ) + { + uiPossible = uiPossible * (100 - gSkillTraitValues.bSpeedModifierBandaging) / 100; + uiPossible += (uiPossible * gSkillTraitValues.ubDOBandagingSpeedPercent * NUM_SKILL_TRAITS( pSoldier, DOCTOR_NT ) + pSoldier->GetBackgroundValue( BG_PERC_BANDAGING )) / 100; + } + + uiActual = uiPossible; // start by assuming maximum possible + + // figure out how far below OKLIFE the victim is + if ( pVictim->stats.bLife >= OKLIFE ) + bBelowOKlife = 0; + else + bBelowOKlife = OKLIFE - pVictim->stats.bLife; + + // figure out how many healing pts we need to stop dying (2x cost) + uiDeficiency = (2 * bBelowOKlife); + + // if, after that, the patient will still be bleeding + if ( (pVictim->bBleeding - bBelowOKlife) > 0 ) + { // then add how many healing pts we need to stop bleeding (1x cost) + uiDeficiency += (pVictim->bBleeding - bBelowOKlife); + } + // On surgery, alter this by amount of life we can heal + if ( fOnSurgery ) + { + uiDeficiency += (pVictim->iHealableInjury / 100); + } + // now, make sure we weren't going to give too much + if ( uiActual > uiDeficiency ) // if we were about to apply too much + uiActual = uiDeficiency; // reduce actual not to waste anything + + // now make sure we HAVE that much + if ( Item[pKit->usItem].medicalkit ) + { + if ( fOnSurgery ) + uiMedcost = (uiActual * gSkillTraitValues.usDOSurgeryMedBagConsumption) / 100; // surgery drains the kit a lot + else + uiMedcost = (uiActual + 1) / 2; // cost is only half, rounded up + + if ( uiMedcost == 0 && uiActual > 0 ) + uiMedcost = 1; + if ( uiMedcost > (UINT32)sKitPts ) // if we can't afford this + { + uiMedcost = sKitPts; // what CAN we afford? + if ( fOnSurgery ) // surgery check + uiActual = (uiMedcost * 100) / gSkillTraitValues.usDOSurgeryMedBagConsumption; + else + uiActual = uiMedcost * 2; // give double this as aid + } + } + else + { + uiMedcost = uiActual; + if ( uiMedcost == 0 && uiActual > 0 ) + uiMedcost = 1; + if ( uiMedcost > (UINT32)sKitPts ) // can't afford it + uiMedcost = uiActual = sKitPts; // recalc cost AND aid + } + + bPtsLeft = (INT8)uiActual; + // heal real life points first (if below OKLIFE) because we don't want the + // patient still DYING if bandages run out, or medic is disabled/distracted! + // NOTE: Dressing wounds for life below OKLIFE now costs 2 pts/life point! + if ( bPtsLeft && pVictim->stats.bLife < OKLIFE ) + { + // if we have enough points to bring him all the way to OKLIFE this turn + if ( bPtsLeft >= (2 * bBelowOKlife) ) + { + // insta-healable injury check + if ( pVictim->iHealableInjury > 0 ) + { + pVictim->iHealableInjury -= ((OKLIFE - pVictim->stats.bLife) * 100); + if ( pVictim->iHealableInjury < 0 ) + pVictim->iHealableInjury = 0; + } + // raise life to OKLIFE + pVictim->stats.bLife = OKLIFE; + // reduce bleeding by the same number of life points healed up + pVictim->bBleeding -= bBelowOKlife; + + // Flugente: increase bPoisonLife, decrease bPoisonBleeding + pVictim->bPoisonLife = min( pVictim->bPoisonSum, pVictim->bPoisonLife + bBelowOKlife ); + pVictim->bPoisonBleeding = max( 0, pVictim->bPoisonBleeding - bBelowOKlife ); + + // use up appropriate # of actual healing points + bPtsLeft -= (2 * bBelowOKlife); + } + else + { + // insta-healable injury check + if ( pVictim->iHealableInjury > 0 ) + { + pVictim->iHealableInjury -= ((bPtsLeft / 2) * 100); + if ( pVictim->iHealableInjury < 0 ) + pVictim->iHealableInjury = 0; + } + pVictim->stats.bLife += (bPtsLeft / 2); + pVictim->bBleeding -= (bPtsLeft / 2); + + // Flugente: increase bPoisonLife, decrease bPoisonBleeding + pVictim->bPoisonLife = min( pVictim->bPoisonSum, pVictim->bPoisonLife + (bPtsLeft / 2) ); + pVictim->bPoisonBleeding = max( 0, pVictim->bPoisonBleeding - (bPtsLeft / 2) ); + + bPtsLeft = bPtsLeft % 2; // if ptsLeft was odd, ptsLeft = 1 + } + + // this should never happen any more, but make sure bleeding not negative + if ( pVictim->bBleeding < 0 ) + { + pVictim->bBleeding = 0; + pVictim->bPoisonBleeding = 0; + } + + // if this healing brought the patient out of the worst of it, cancel dying + if ( pVictim->stats.bLife >= OKLIFE ) + { // turn off merc QUOTE flags + pVictim->flags.fDyingComment = FALSE; + } + + if ( pVictim->bBleeding <= MIN_BLEEDING_THRESHOLD ) + { + pVictim->flags.fWarnedAboutBleeding = FALSE; + } + } + + // SURGERY + // first return the real life back, then bandage the rest if possible + if ( fOnSurgery && gGameOptions.fNewTraitSystem ) // double check for new traits + { + INT32 iLifeReturned = 0; + UINT16 usReturnDamagedStatRate = 0; + // find out if we will repair any stats... + if ( NumberOfDamagedStats( pVictim ) > 0 ) + { + usReturnDamagedStatRate = ((gSkillTraitValues.usDORepairStatsRateBasic + gSkillTraitValues.usDORepairStatsRateOnTop * NUM_SKILL_TRAITS( pSoldier, DOCTOR_NT ))); + usReturnDamagedStatRate -= max( 0, ((usReturnDamagedStatRate * gSkillTraitValues.ubDORepStPenaltyIfAlsoHealing) / 100) ); + + // ... in which case, reduce the points + bPtsLeft = max( 0, ((bPtsLeft * (100 - gSkillTraitValues.ubDOHealingPenaltyIfAlsoStatRepair)) / 100) ); + } + + // Important note! : HealableInjury is always stores the total HPs the victim is missing, not the amount which we will heal, + // so we always take a portion of patient's damage here, reduce the HealableInjury by this portion, while only healing a portion of this portion in actual HPs; + // this means the rest of HPs will remain as "unhealable", the patient will miss X HPs but has no HealableInjury on self.. + if ( bPtsLeft >= (pVictim->iHealableInjury / 100) ) + { + iLifeReturned = pVictim->iHealableInjury * (gSkillTraitValues.ubDOSurgeryHealPercentBase + gSkillTraitValues.ubDOSurgeryHealPercentOnTop * NUM_SKILL_TRAITS( pSoldier, DOCTOR_NT )) / 100; + + pVictim->iHealableInjury = 0; + // keep the rest of the points to bandaging if neccessary + if ( pVictim->bBleeding > 0 ) + { + bPtsLeft = max( 0, (bPtsLeft - (iLifeReturned / 100)) ); + bPtsLeft += (bPtsLeft / 2); // we use medical bag so add the bonus for that. + } + else + { + bPtsLeft = 0; + } + + // add to record - another surgery undergoed + if ( pVictim->ubProfile != NO_PROFILE && iLifeReturned >= 100 ) + gMercProfiles[pVictim->ubProfile].records.usTimesSurgeryUndergoed++; + + // add to record - another surgery made + if ( pSoldier->ubProfile != NO_PROFILE && iLifeReturned >= 100 ) + gMercProfiles[pSoldier->ubProfile].records.usSurgeriesMade++; + } + else + { + iLifeReturned = bPtsLeft * (gSkillTraitValues.ubDOSurgeryHealPercentBase + gSkillTraitValues.ubDOSurgeryHealPercentOnTop * NUM_SKILL_TRAITS( pSoldier, DOCTOR_NT )); + + pVictim->iHealableInjury -= (bPtsLeft * 100); + bPtsLeft = 0; + } + // repair the stats here! + if ( usReturnDamagedStatRate > 0 ) + { + RegainDamagedStats( pVictim, (iLifeReturned * usReturnDamagedStatRate / 100) ); + } + + // some paranoya checks for sure + if ( (pVictim->stats.bLife + (iLifeReturned / 100)) <= pVictim->stats.bLifeMax ) + { + pVictim->stats.bLife += (iLifeReturned / 100); + pVictim->bPoisonLife = min( pVictim->bPoisonSum, pVictim->bPoisonLife + (iLifeReturned / 100) ); + if ( pVictim->bBleeding >= (iLifeReturned / 100) ) + { + pVictim->bBleeding -= (iLifeReturned / 100); + pVictim->bPoisonBleeding = max( 0, pVictim->bPoisonBleeding - (iLifeReturned / 100) ); + uiMedcost += (iLifeReturned / 200); // add medkit points cost for unbandaged part + } + else + { + pVictim->bBleeding = 0; + pVictim->bPoisonBleeding = 0; + uiMedcost += max( 0, (((iLifeReturned / 100) - pVictim->bBleeding) / 2) ); // add medkit points cost for unbandaged part + } + } + else // this shouldn't even happen, but we still want to have it here for sure + { + pVictim->stats.bLife = pVictim->stats.bLifeMax; + pVictim->bPoisonLife = pVictim->bPoisonSum; + pVictim->iHealableInjury = 0; + pVictim->bBleeding = 0; + pVictim->bPoisonBleeding = 0; + } + // Reduce max breath based on life returned + if ( (pVictim->bBreathMax - (((iLifeReturned / 100) * gSkillTraitValues.usDOSurgeryMaxBreathLoss) / 100)) <= BREATHMAX_ABSOLUTE_MINIMUM ) + { + pVictim->bBreathMax = BREATHMAX_ABSOLUTE_MINIMUM; + } + else + { + pVictim->bBreathMax -= (((iLifeReturned / 100) * gSkillTraitValues.usDOSurgeryMaxBreathLoss) / 100); + } + + if ( pVictim->iHealableInjury > ((pVictim->stats.bLifeMax - pVictim->stats.bLife) * 100) ) + pVictim->iHealableInjury = ((pVictim->stats.bLifeMax - pVictim->stats.bLife) * 100); + else if ( pVictim->iHealableInjury < 0 ) + pVictim->iHealableInjury = 0; + + // Flugente: campaign stats + gCurrentIncident.usIncidentFlags |= INCIDENT_SURGERY; + } + + // if any healing points remain, apply that to any remaining bleeding (1/1) + // DON'T spend any APs/kit pts to cure bleeding until merc is no longer dying + //if ( bPtsLeft && pVictim->bBleeding && !pVictim->dying) + if ( bPtsLeft && pVictim->bBleeding ) + { // if we have enough points to bandage all remaining bleeding this turn + if ( bPtsLeft >= pVictim->bBleeding ) + { + bPtsLeft -= pVictim->bBleeding; + pVictim->bBleeding = 0; + pVictim->bPoisonBleeding = 0; + } + else // bandage what we can + { + pVictim->bBleeding -= bPtsLeft; + pVictim->bPoisonBleeding = max( 0, pVictim->bPoisonBleeding - bPtsLeft ); + bPtsLeft = 0; + } + } + + //CHRISL: If by some chance ubPtsLeft ends up being higher then uiActual, we'll end up with a huge value since uiActual is an unsigned variable. + // if there are any ptsLeft now, then we didn't actually get to use them + uiActual = max( 0, (INT32)(uiActual - bPtsLeft) ); + + // usedAPs equals (actionPts) * (%of possible points actually used) + uiUsedAPs = (uiActual * uiAvailAPs) / uiPossible; + + if ( Item[pSoldier->inv[0].usItem].medicalkit && !(fOnSurgery) ) // using the GOOD medic stuff + uiUsedAPs = (uiUsedAPs * 2) / 3; // reverse 50% bonus by taking 2/3rds + + // surgery is harder so cost more BPs + if ( fOnSurgery ) + DeductPoints( pSoldier, (INT16)uiUsedAPs, (INT16)(uiUsedAPs * 15) ); + else + DeductPoints( pSoldier, (INT16)uiUsedAPs, (INT16)((uiUsedAPs * APBPConstants[BP_PER_AP_LT_EFFORT])) ); + + // surgery is harder so gives more exp + if ( fOnSurgery ) + { + // MEDICAL GAIN (actual / 2): Helped someone by giving first aid + StatChange( pSoldier, MEDICALAMT, (UINT16)(uiActual + 2), FALSE ); + + // DEXTERITY GAIN (actual / 6): Helped someone by giving first aid + StatChange( pSoldier, DEXTAMT, (UINT16)((uiActual / 3) + 2), FALSE ); + } + else + { + if ( uiActual / 2 ) + // MEDICAL GAIN (actual / 2): Helped someone by giving first aid + StatChange( pSoldier, MEDICALAMT, ((UINT16)(uiActual / 2)), FALSE ); + + if ( uiActual / 4 ) + // DEXTERITY GAIN (actual / 4): Helped someone by giving first aid + StatChange( pSoldier, DEXTAMT, (UINT16)((uiActual / 4)), FALSE ); + } + + // merc records - bandaging + if ( bInitialBleeding > 1 && pVictim->bBleeding == 0 && pSoldier->ubProfile != NO_PROFILE ) + gMercProfiles[pSoldier->ubProfile].records.usMercsBandaged++; + + return uiMedcost; +} +////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Tactical/Soldier Control.h b/Tactical/Soldier Control.h index d2edea148..0654bb8ba 100644 --- a/Tactical/Soldier Control.h +++ b/Tactical/Soldier Control.h @@ -2728,6 +2728,9 @@ void HandleTakeDamageDeath( SOLDIERTYPE *pSoldier, UINT8 bOldLife, UINT8 ubReaso void SetDamageDisplayCounter(SOLDIERTYPE* pSoldier); +// SANDRO - This whole procedure was merged with the surgery ability of the doctor trait +UINT32 VirtualSoldierDressWound( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pVictim, OBJECTTYPE *pKit, INT16 sKitPts, INT16 sStatus, BOOLEAN fOnSurgery ); + #endif diff --git a/Utils/Text.h b/Utils/Text.h index b6f61d06c..e71a895d4 100644 --- a/Utils/Text.h +++ b/Utils/Text.h @@ -1116,6 +1116,8 @@ enum QUICK_ITEM_NOT_FOUND, QUICK_ITEM_CANNOT_TAKE, + ATTEMPT_BANDAGE_DURING_TRAVEL, + TEXT_NUM_TACTICAL_STR }; diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index 04ca4be01..6bd59eb7b 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -3389,6 +3389,8 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"没有空手来拿新物品", L"未发现物品", L"无法把物品转移到主手上", + + L"Attempting to bandage travelling mercs...", //TODO.Translate }; //Varying helptext explains (for the "Go to Sector/Map" checkbox) what will happen given different circumstances in the "exiting sector" interface. diff --git a/Utils/_DutchText.cpp b/Utils/_DutchText.cpp index 202407adb..3192d1f26 100644 --- a/Utils/_DutchText.cpp +++ b/Utils/_DutchText.cpp @@ -3387,6 +3387,8 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"No free hand for new item", L"Item not found", L"Cannot take item to main hand", + + L"Attempting to bandage travelling mercs...", //TODO.Translate }; //Varying helptext explains (for the "Go to Sector/Map" checkbox) what will happen given different circumstances in the "exiting sector" interface. diff --git a/Utils/_EnglishText.cpp b/Utils/_EnglishText.cpp index 12c44360c..47b6b5cf4 100644 --- a/Utils/_EnglishText.cpp +++ b/Utils/_EnglishText.cpp @@ -3388,6 +3388,8 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"No free hand for new item", L"Item not found", L"Cannot take item to main hand", + + L"Attempting to bandage travelling mercs...", }; //Varying helptext explains (for the "Go to Sector/Map" checkbox) what will happen given different circumstances in the "exiting sector" interface. diff --git a/Utils/_FrenchText.cpp b/Utils/_FrenchText.cpp index 6ebb6ed9e..3b913fb9b 100644 --- a/Utils/_FrenchText.cpp +++ b/Utils/_FrenchText.cpp @@ -3392,6 +3392,8 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"Aucune main libre pour un nouvel objet", L"Objet non trouvé", L"Vous ne pouvez pas prendre d'objet avec la main principale", + + L"Attempting to bandage travelling mercs...", //TODO.Translate }; //Varying helptext explains (for the "Go to Sector/Map" checkbox) what will happen given different circumstances in the "exiting sector" interface. diff --git a/Utils/_GermanText.cpp b/Utils/_GermanText.cpp index 7453b0beb..43f597e1e 100644 --- a/Utils/_GermanText.cpp +++ b/Utils/_GermanText.cpp @@ -3394,6 +3394,8 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"No free hand for new item", L"Item not found", L"Cannot take item to main hand", + + L"Attempting to bandage travelling mercs...", //TODO.Translate }; //Varying helptext explains (for the "Go to Sector/Map" checkbox) what will happen given different circumstances in the "exiting sector" interface. diff --git a/Utils/_ItalianText.cpp b/Utils/_ItalianText.cpp index 4cfa7e0b2..c8e6414a1 100644 --- a/Utils/_ItalianText.cpp +++ b/Utils/_ItalianText.cpp @@ -3382,6 +3382,8 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"No free hand for new item", L"Item not found", L"Cannot take item to main hand", + + L"Attempting to bandage travelling mercs...", //TODO.Translate }; //Varying helptext explains (for the "Go to Sector/Map" checkbox) what will happen given different circumstances in the "exiting sector" interface. diff --git a/Utils/_PolishText.cpp b/Utils/_PolishText.cpp index f87b99dfd..b52bf59c7 100644 --- a/Utils/_PolishText.cpp +++ b/Utils/_PolishText.cpp @@ -3397,6 +3397,8 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"No free hand for new item", L"Item not found", L"Cannot take item to main hand", + + L"Attempting to bandage travelling mercs...", //TODO.Translate }; //Varying helptext explains (for the "Go to Sector/Map" checkbox) what will happen given different circumstances in the "exiting sector" interface. diff --git a/Utils/_RussianText.cpp b/Utils/_RussianText.cpp index 0fcc64669..63e989ea5 100644 --- a/Utils/_RussianText.cpp +++ b/Utils/_RussianText.cpp @@ -3385,6 +3385,8 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"Нет свободной руки для предмета", L"Предмет не обнаружен", L"Невозможно взять предмет в руку", + + L"Attempting to bandage travelling mercs...", //TODO.Translate }; //Varying helptext explains (for the "Go to Sector/Map" checkbox) what will happen given different circumstances in the "exiting sector" interface.