void SetFinalTile( SOLDIERTYPE *pSoldier, INT16 sGridNo, BOOLEAN fGivenUp ) { // OK, If we were waiting for stuff, do it here... // ATE: Disabled stuff below, made obsolete by timeout... //if ( pSoldier->ubWaitActionToDo ) //{ // pSoldier->ubWaitActionToDo = 0; // gbNumMercsUntilWaitingOver--; //} pSoldier->sFinalDestination = pSoldier->sGridNo; #ifdef JA2BETAVERSION if ( gTacticalStatus.uiFlags & INCOMBAT ) { OutputDebugInfoForTurnBasedNextTileWaiting( pSoldier ); } #endif if ( pSoldier->bTeam == gbPlayerNum && fGivenUp ) { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, TacticalStr[ NO_PATH_FOR_MERC ], pSoldier->name ); } EVENT_StopMerc( pSoldier, pSoldier->sGridNo, pSoldier->bDirection ); }
UINT32 AddHistoryToPlayersLog(UINT8 ubCode, UINT8 ubSecondCode, UINT32 uiDate, INT16 sSectorX, INT16 sSectorY) { // adds History item to player's log(History List), returns unique id number of it // outside of the History system(the code in this .c file), this is the only function you'll ever need UINT32 uiId=0; HistoryUnitPtr pHistory = pHistoryListHead; // clear the list ClearHistoryList( ); // process the actual data uiId = ProcessAndEnterAHistoryRecord(ubCode, uiDate, ubSecondCode, sSectorX, sSectorY, 0, 0); ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, pMessageStrings[ MSG_HISTORY_UPDATED ] ); // history list head pHistory = pHistoryListHead; // append to end of file AppendHistoryToEndOfFile( pHistory ); // if in history mode, reload current page if( fInHistoryMode ) { iCurrentHistoryPage--; // load in first page LoadNextHistoryPage( ); } // return unique id of this transaction return uiId; }
void EndUIPlan( ) { int cnt; SOLDIERTYPE *pSoldier; // Zero out any planned soldiers for( cnt = MAX_NUM_SOLDIERS; cnt < TOTAL_SOLDIERS; cnt++ ) { pSoldier = MercPtrs[ cnt ]; if ( pSoldier->bActive ) { if ( pSoldier->sPlannedTargetX != -1 ) { SetRenderFlags(RENDER_FLAG_FULL ); } TacticalRemoveSoldier( pSoldier->ubID ); } } gfInUIPlanMode = FALSE; gusSelectedSoldier = gpUIStartPlannedSoldier->ubID; gfPlotNewMovement = TRUE; ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Leaving Planning Mode" ); }
void SetBoxingState( INT8 bNewState ) { if ( gTacticalStatus.bBoxingState == NOT_BOXING ) { if ( bNewState != NOT_BOXING ) { // pause time PauseGame(); } } else { if ( bNewState == NOT_BOXING ) { // unpause time UnPauseGame(); if ( BoxersAvailable() == NUM_BOXERS ) { // set one boxer to be set as boxed so that the game will allow another // fight to occur gfBoxerFought[ 0 ] = TRUE; } } } gTacticalStatus.bBoxingState = bNewState; #ifdef JA2TESTVERSION ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, L"Boxing state now %d", bNewState ); #endif }
void DisplayGameSettings( ) { //Display the version number ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s: %s (%S)", pMessageStrings[ MSG_VERSION ], zVersionLabel, czVersionNumber ); //Display the difficulty level ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s: %s", gzGIOScreenText[ GIO_DIF_LEVEL_TEXT ], gzGIOScreenText[ gGameOptions.ubDifficultyLevel + GIO_EASY_TEXT - 1 ] ); //Iron man option ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s: %s", gzGIOScreenText[ GIO_GAME_SAVE_STYLE_TEXT ], gzGIOScreenText[ GIO_SAVE_ANYWHERE_TEXT + gGameOptions.fIronManMode ] ); // Gun option if( gGameOptions.fGunNut ) ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s: %s", gzGIOScreenText[ GIO_GUN_OPTIONS_TEXT ], gzGIOScreenText[ GIO_GUN_NUT_TEXT ] ); else ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s: %s", gzGIOScreenText[ GIO_GUN_OPTIONS_TEXT ], gzGIOScreenText[ GIO_REDUCED_GUNS_TEXT ] ); //Sci fi option ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s: %s", gzGIOScreenText[ GIO_GAME_STYLE_TEXT ], gzGIOScreenText[ GIO_REALISTIC_TEXT + gGameOptions.fSciFi ] ); //Timed Turns option // JA2Gold: no timed turns //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s: %s", gzGIOScreenText[ GIO_TIMED_TURN_TITLE_TEXT ], gzGIOScreenText[ GIO_NO_TIMED_TURNS_TEXT + gGameOptions.fTurnTimeLimit ] ); if( CHEATER_CHEAT_LEVEL() ) { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, gzLateLocalizedString[58], CurrentPlayerProgressPercentage(), HighestPlayerProgressPercentage() ); } }
void SwitchMineViewOff() { if (gubDrawModeMine == MINES_DRAW_OFF) return; gubDrawModeMine = MINES_DRAW_OFF; ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Turning off trap display"); DisplayMines(TRUE); }
void SwitchToHostileTrapsView() { if (gubDrawModeMine == MINES_DRAW_DETECT_ENEMY) return; gubDrawModeMine = MINES_DRAW_DETECT_ENEMY; ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Display nearby traps"); DisplayMines(TRUE); }
///BEGIN key binding functions void SwitchToTrapNetworkView() { if (gubDrawModeMine == MINES_DRAW_PLAYERTEAM_NETWORKS) return; gubDrawModeMine = MINES_DRAW_PLAYERTEAM_NETWORKS; ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Display trap network"); DisplayMines(TRUE); }
void SwitchViewOff() { if (gubDrawMode == COVER_DRAW_OFF) return; gubDrawMode = COVER_DRAW_OFF; ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, gzDisplayCoverText[DC_MSG__COVER_DRAW_OFF]); DisplayCover(TRUE); }
void SwitchToEnemyView() { if (gubDrawMode == COVER_DRAW_ENEMY_VIEW) return; gubDrawMode = COVER_DRAW_ENEMY_VIEW; ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, gzDisplayCoverText[DC_MSG__COVER_DRAW_ENEMY_VIEW]); DisplayCover(TRUE); }
void Campaign_Stats::AddMoneyEarned(UINT8 aType, INT32 aVal) { if ( aType >= CAMPAIGN_MONEY_MAX ) { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Error: Unknown money type!" ); return; } sMoneyEarned[aType] += aVal; }
void Campaign_Stats::AddConsumption(UINT8 aType, FLOAT aVal) { if ( aType >= CAMPAIGN_CONSUMED_MAX ) { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Error: Unknown consumption type!" ); return; } usConsumed[aType] += aVal; }
void ChatLogMessage( UINT16 usColor, UINT8 ubPriority, STR16 pStringA, ... ) { // this function sets up the string into several single line structures //ScrollStringStPtr pStringSt; UINT32 uiFont = CHAT_MESSAGE_FONT; //STR16pString; va_list argptr; CHAR16 DestString[512]; WRAPPED_STRING *pStringWrapper=NULL; WRAPPED_STRING *pStringWrapperHead=NULL; BOOLEAN fNewString = FALSE; UINT16 usLineWidthIfWordIsWiderThenWidth; /*pStringSt=pStringS; while(GetNextString(pStringSt)) pStringSt=GetNextString(pStringSt);*/ va_start(argptr, pStringA); // Set up variable argument pointer vswprintf(DestString, pStringA, argptr); // process gprintf string (get output str) va_end(argptr); // send message to tactical screen and map screen ScreenMsg( usColor, ubPriority, DestString ); pStringWrapperHead=LineWrap(uiFont, CHAT_LINE_WIDTH, &usLineWidthIfWordIsWiderThenWidth, DestString); pStringWrapper=pStringWrapperHead; if(!pStringWrapper) return; fNewString = TRUE; while(pStringWrapper->pNextWrappedString!=NULL) { AddStringToChatLogMessageList(pStringWrapper->sString, usColor, uiFont, fNewString, ubPriority ); fNewString = FALSE; pStringWrapper=pStringWrapper->pNextWrappedString; } AddStringToChatLogMessageList(pStringWrapper->sString, usColor, uiFont, fNewString, ubPriority ); // clear up list of wrapped strings ClearWrappedStringsCHAT( pStringWrapperHead ); // play new message beep //PlayNewMessageSound( ); MoveToEndOfChatScreenMessageList( ); //LeaveMutex(SCROLL_MESSAGE_MUTEX, __LINE__, __FILE__); }
void HourlyCamouflageUpdate( void ) { INT8 bMercID, bLastTeamID; SOLDIERTYPE * pSoldier; bMercID = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; bLastTeamID = gTacticalStatus.Team[ gbPlayerNum ].bLastID; // loop through all mercs for ( pSoldier = MercPtrs[ bMercID ]; bMercID <= bLastTeamID; bMercID++,pSoldier++) { if ( pSoldier->bActive ) { // if the merc has non-zero camo, degrade it by 1% if( ( pSoldier->bCamo > 0) && ( !( HAS_SKILL_TRAIT( pSoldier, CAMOUFLAGED) ) ) ) { pSoldier->bCamo -= 2; if (pSoldier->bCamo <= 0) { pSoldier->bCamo = 0; // Reload palettes.... if ( pSoldier->bInSector ) { CreateSoldierPalettes( pSoldier ); } ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, Message[STR_CAMMO_WORN_OFF], pSoldier->name ); DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 ); } } // if the merc has non-zero monster smell, degrade it by 1 if ( pSoldier->bMonsterSmell > 0 ) { pSoldier->bMonsterSmell--; /* if (pSoldier->bMonsterSmell == 0) { // Reload palettes.... if ( pSoldier->bInSector ) { CreateSoldierPalettes( pSoldier ); } ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, Message[STR_CAMMO_WORN_OFF], pSoldier->name ); DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 ); } */ } } } }
BOOLEAN BeginUIPlan( SOLDIERTYPE *pSoldier ) { gubNumUIPlannedMoves = 0; gpUIPlannedSoldier = pSoldier; gpUIStartPlannedSoldier = pSoldier; gfInUIPlanMode = TRUE; gfPlotNewMovement = TRUE; ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Entering Planning Mode" ); return( TRUE ); }
void SwitchMinesDrawModeForNetworks() { switch ( gubDrawModeMine ) { case MINES_DRAW_OFF: case MINES_DRAW_DETECT_ENEMY: gubDrawModeMine = MINES_DRAW_PLAYERTEAM_NETWORKS; ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Display trap network"); DisplayMines(TRUE); break; case MINES_DRAW_PLAYERTEAM_NETWORKS: gubDrawModeMine = MINES_DRAW_NETWORKCOLOURING; ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Display trap network colouring"); DisplayMines(TRUE); break; case MINES_DRAW_NETWORKCOLOURING: gubDrawModeMine = MINES_DRAW_NET_A; ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Display trap network A"); DisplayMines(TRUE); break; case MINES_DRAW_NET_A: gubDrawModeMine = MINES_DRAW_NET_B; ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Display trap network B"); DisplayMines(TRUE); break; case MINES_DRAW_NET_B: gubDrawModeMine = MINES_DRAW_NET_C; ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Display trap network C"); DisplayMines(TRUE); break; case MINES_DRAW_NET_C: gubDrawModeMine = MINES_DRAW_NET_D; ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Display trap network D"); DisplayMines(TRUE); break; case MINES_DRAW_NET_D: case MINES_DRAW_MAX: default: gubDrawModeMine = MINES_DRAW_OFF; ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Turning off trap display"); DisplayMines(TRUE); break; } }
// MercDailyUpdate() gets called every day at midnight. If something is to happen to a merc that day, add an event for it. void MercDailyUpdate() { INT32 cnt; INT8 bLastTeamID; SOLDIERTYPE *pSoldier; //SOLDIERTYPE *pQuitList[ 21 ]; MERCPROFILESTRUCT *pProfile; UINT32 uiChance; INT32 iOffset = 0; BOOLEAN fFoundSomeOneForMenuShowing = FALSE; //if its the first day, leave if( GetWorldDay() == 1 ) return; // debug message ScreenMsg( MSG_FONT_RED, MSG_DEBUG, L"%s - Doing MercDailyUpdate", WORLDTIMESTR ); // if the death rate is very low (this is independent of mercs' personal deathrate tolerances) if (CalcDeathRate() < 5) { // everyone gets a morale bonus, which also gets player a reputation bonus. HandleMoraleEvent( NULL, MORALE_LOW_DEATHRATE, -1, -1, -1 ); } //add an event so the merc will say the departing warning ( 2 hours prior to leaving // Do so for all time slots they will depart from AddSameDayStrategicEvent( EVENT_MERC_ABOUT_TO_LEAVE, MERC_ARRIVE_TIME_SLOT_1 - ( 2 * 60 ), 0 ); AddSameDayStrategicEvent( EVENT_MERC_ABOUT_TO_LEAVE, MERC_ARRIVE_TIME_SLOT_2 - ( 2 * 60 ), 0 ); AddSameDayStrategicEvent( EVENT_MERC_ABOUT_TO_LEAVE, MERC_ARRIVE_TIME_SLOT_3 - ( 2 * 60 ), 0 ); AddSameDayStrategicEvent( EVENT_BEGIN_CONTRACT_RENEWAL_SEQUENCE, MERC_ARRIVE_TIME_SLOT_1, 0 ); AddSameDayStrategicEvent( EVENT_BEGIN_CONTRACT_RENEWAL_SEQUENCE, MERC_ARRIVE_TIME_SLOT_2, 0 ); AddSameDayStrategicEvent( EVENT_BEGIN_CONTRACT_RENEWAL_SEQUENCE, MERC_ARRIVE_TIME_SLOT_3, 0 ); cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; bLastTeamID = gTacticalStatus.Team[ gbPlayerNum ].bLastID; //loop though all the mercs for ( pSoldier = MercPtrs[ cnt ]; cnt <= bLastTeamID; cnt++,pSoldier++) { //if the merc is active if ( ( pSoldier->bActive )&&( pSoldier->bAssignment != ASSIGNMENT_POW ) && ( pSoldier->bAssignment != IN_TRANSIT ) ) { //CJC: Reset dialogue flags for quotes that can be said once/day pSoldier->usQuoteSaidFlags &= ( ~SOLDIER_QUOTE_SAID_ANNOYING_MERC ); // ATE: Reset likes gun flag pSoldier->usQuoteSaidFlags &= ( ~SOLDIER_QUOTE_SAID_LIKESGUN ); // ATE: Reset seen corpse flag pSoldier->usQuoteSaidFlags &= ( ~SOLDIER_QUOTE_SAID_ROTTINGCORPSE ); // ATE; Reset found something nice flag... pSoldier->usQuoteSaidFlags &= ( ~SOLDIER_QUOTE_SAID_FOUND_SOMETHING_NICE ); // ATE: Decrement tolerance value... pSoldier->bCorpseQuoteTolerance--; if ( pSoldier->bCorpseQuoteTolerance < 0 ) { pSoldier->bCorpseQuoteTolerance = 0; } // CJC: For some personalities, reset personality quote said flag if ( pSoldier->ubProfile != NO_PROFILE ) { switch( gMercProfiles[ pSoldier->ubProfile ].bPersonalityTrait ) { case HEAT_INTOLERANT: case CLAUSTROPHOBIC: case NONSWIMMER: case FEAR_OF_INSECTS: // repeatable once per day pSoldier->usQuoteSaidFlags &= ( ~SOLDIER_QUOTE_SAID_PERSONALITY ); break; default: break; } } //ATE: Try to see if our equipment sucks! if ( SoldierHasWorseEquipmentThanUsedTo( pSoldier ) ) { // Randomly anytime between 6:00, and 10:00 AddSameDayStrategicEvent( EVENT_MERC_COMPLAIN_EQUIPMENT, 360 + Random( 1080 ) , pSoldier->ubProfile ); } // increment days served by this grunt gMercProfiles[pSoldier->ubProfile].usTotalDaysServed++; // player has hired him, so he'll eligible to get killed off on another job gMercProfiles[pSoldier->ubProfile].ubMiscFlags3 |= PROFILE_MISC_FLAG3_PLAYER_HAD_CHANCE_TO_HIRE; //if the character is an RPC if( pSoldier->ubProfile >= FIRST_RPC && pSoldier->ubProfile < FIRST_NPC ) { INT16 sSalary = gMercProfiles[ pSoldier->ubProfile ].sSalary; INT32 iMoneyOwedToMerc = 0; //increment the number of days the mercs has been on the team pSoldier->iTotalContractLength++; //if the player owes the npc money, the balance field will be negative if( gMercProfiles[ pSoldier->ubProfile ].iBalance < 0 ) { //the player owes the npc the salary and whatever money the player owes the npc iMoneyOwedToMerc = sSalary + ( - gMercProfiles[ pSoldier->ubProfile ].iBalance ); } else { //else the player only owes the salary iMoneyOwedToMerc = sSalary; } //if the player owes money if( iMoneyOwedToMerc != 0 ) { //if the player can afford to pay them if( LaptopSaveInfo.iCurrentBalance >= iMoneyOwedToMerc ) { //add the transaction to the player AddTransactionToPlayersBook( PAYMENT_TO_NPC, pSoldier->ubProfile, GetWorldTotalMin(), -iMoneyOwedToMerc); //if the player owed money to the npc if( gMercProfiles[ pSoldier->ubProfile ].iBalance < 0 ) { // reset the amount gMercProfiles[ pSoldier->ubProfile ].iBalance = 0; } } else { CHAR16 zMoney[128]; //create a string for the salary owed to the npc swprintf( zMoney, L"%d", sSalary ); InsertCommasForDollarFigure( zMoney ); InsertDollarSignInToString( zMoney ); //Display a screen msg indicating that the npc was NOT paid ScreenMsg( FONT_MCOLOR_WHITE, MSG_INTERFACE, pMessageStrings[ MSG_CANT_AFFORD_TO_PAY_NPC_DAILY_SALARY_MSG ], gMercProfiles[ pSoldier->ubProfile ].zNickname, zMoney ); //if the merc hasnt been paid for NUM_DAYS_TILL_UNPAID_RPC_QUITS days, the merc will quit if( ( gMercProfiles[ pSoldier->ubProfile ].iBalance - sSalary ) <= -( sSalary * NUM_DAYS_TILL_UNPAID_RPC_QUITS ) ) { // //Set it up so the merc quits // MercsContractIsFinished( pSoldier->ubID ); } else { //set how much money the player owes the merc gMercProfiles[ pSoldier->ubProfile ].iBalance -= sSalary; // Add even for displaying a dialogue telling the player this.... AddSameDayStrategicEvent( EVENT_RPC_WHINE_ABOUT_PAY, MERC_ARRIVE_TIME_SLOT_1, pSoldier->ubID ); } } } } DailyMoraleUpdate( pSoldier ); CheckIfMercGetsAnotherContract( pSoldier ); } else { if( ( pSoldier->bActive ) && ( pSoldier->bAssignment == ASSIGNMENT_POW ) ) { pSoldier->iEndofContractTime += 1440; } } // if active, here, & alive (POW is ok, don't care) if( ( pSoldier->bActive ) && ( pSoldier->bAssignment != ASSIGNMENT_DEAD ) && ( pSoldier->bAssignment != IN_TRANSIT ) ) { // increment the "man days" played counter for each such merc in the player's employment gStrategicStatus.uiManDaysPlayed++; } } //r eset the counter cnt = 0; for ( pSoldier = MercPtrs[ cnt ]; cnt <= bLastTeamID; cnt++,pSoldier++) { //if the merc is active if ( ( pSoldier->bActive )&&( pSoldier->bAssignment != ASSIGNMENT_POW ) && ( pSoldier->bAssignment != IN_TRANSIT ) ) { //if its a MERC merc, determine if the merc should leave ( because player refused to pay for merc ) if( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__MERC ) { //if the players account status is invalid if( LaptopSaveInfo.gubPlayersMercAccountStatus == MERC_ACCOUNT_INVALID ) { //if the soldier is alive anc concious if( IsTheSoldierAliveAndConcious( pSoldier ) ) { //if the merc should leave today MercsContractIsFinished( pSoldier->ubID ); } } } } } //Loop through all the profiles for( cnt = 0; cnt < NUM_PROFILES; cnt++) { pProfile = &(gMercProfiles[ cnt ]); // dead guys don't do nuthin' ! if ( pProfile->bMercStatus == MERC_IS_DEAD ) { continue; } //Every day reset this variable pProfile->uiPrecedentQuoteSaid = 0; // skip anyone currently on the player's team if ( IsMercOnTeam( (UINT8) cnt )) { continue; } // if he's an AIM/M.E.R.C. merc if ( cnt < AIM_AND_MERC_MERCS ) { // if he's not just on his way home if ( pProfile->bMercStatus != MERC_RETURNING_HOME ) { // check if any of his stats improve through working or training HandleUnhiredMercImprovement(pProfile); // if he's working on another job if (pProfile->bMercStatus == MERC_WORKING_ELSEWHERE) { // check if he's killed HandleUnhiredMercDeaths( cnt ); } } } // if merc is currently unavailable if( pProfile->uiDayBecomesAvailable > 0 ) { // reduce time til available by one day pProfile->uiDayBecomesAvailable--; // Check to see if the merc has become available if (pProfile->uiDayBecomesAvailable == 0) { //if the merc CAN become ready if( pProfile->bMercStatus != MERC_FIRED_AS_A_POW ) { pProfile->bMercStatus = MERC_OK; // if the player has left a message for this merc if ( pProfile->ubMiscFlags3 & PROFILE_MISC_FLAG3_PLAYER_LEFT_MSG_FOR_MERC_AT_AIM ) { iOffset = AIM_REPLY_BARRY; //remove the Flag, so if the merc goes on another assignment, the player can leave an email. pProfile->ubMiscFlags3 &= ~PROFILE_MISC_FLAG3_PLAYER_LEFT_MSG_FOR_MERC_AT_AIM; // TO DO: send E-mail to player telling him the merc has returned from an assignment AddEmail( ( UINT8 )( iOffset + ( cnt * AIM_REPLY_LENGTH_BARRY ) ), AIM_REPLY_LENGTH_BARRY, ( UINT8 )( 6 + cnt ), GetWorldTotalMin() ); } } } } else // was already available today { // if it's an AIM or M.E.R.C. merc if (cnt < AIM_AND_MERC_MERCS) { // check to see if he goes on another assignment if (cnt < MAX_NUMBER_MERCS) { // A.I.M. merc uiChance = 2 * pProfile->bExpLevel; // player has now had a chance to hire him, so he'll eligible to get killed off on another job pProfile->ubMiscFlags3 |= PROFILE_MISC_FLAG3_PLAYER_HAD_CHANCE_TO_HIRE; } else { // M.E.R.C. merc - very rarely get other work uiChance = 1 * pProfile->bExpLevel; // player doesn't have a chance to hire any M.E.R.C's until after Speck's E-mail is sent if (GetWorldDay() > DAYS_TIL_M_E_R_C_AVAIL) { // player has now had a chance to hire him, so he'll eligible to get killed off on another job pProfile->ubMiscFlags3 |= PROFILE_MISC_FLAG3_PLAYER_HAD_CHANCE_TO_HIRE; } } if (Random(100) < uiChance) { pProfile->bMercStatus = MERC_WORKING_ELSEWHERE; pProfile->uiDayBecomesAvailable = 1 + Random(6 + (pProfile->bExpLevel / 2) ); // 1-(6 to 11) days } } } // Decrement morale hangover (merc appears hirable, he just gives lame refusals during this time, though) if( pProfile->ubDaysOfMoraleHangover > 0 ) { pProfile->ubDaysOfMoraleHangover--; } } // build quit list //BuildMercQuitList( pQuitList ); HandleSlayDailyEvent( ); // rebuild list for mapscreen ReBuildCharactersList( ); }
void CountPeopleInBoxingRingAndDoActions( void ) { UINT32 uiLoop; UINT8 ubTotalInRing = 0; //DBrot: More Rooms UINT16 usRoom; UINT16 ubPlayersInRing = 0; SOLDIERTYPE * pSoldier; SOLDIERTYPE * pInRing[2] = { NULL, NULL }; SOLDIERTYPE * pNonBoxingPlayer = NULL; for ( uiLoop = 0; uiLoop < guiNumMercSlots; ++uiLoop ) { pSoldier = MercSlots[ uiLoop ]; if ( pSoldier != NULL ) { if ( InARoom( pSoldier->sGridNo, &usRoom ) && usRoom == BOXING_RING) { if ( ubTotalInRing < 2 ) { pInRing[ ubTotalInRing ] = pSoldier; } ++ubTotalInRing; if ( pSoldier->flags.uiStatusFlags & SOLDIER_PC ) { ++ubPlayersInRing; if ( !pNonBoxingPlayer && !(pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) ) { pNonBoxingPlayer = pSoldier; } } } } } if ( ubPlayersInRing > 1 ) { // boxing match just became invalid! if ( gTacticalStatus.bBoxingState <= PRE_BOXING ) { BoxingPlayerDisqualified( pNonBoxingPlayer, NON_BOXER_IN_RING ); // set to not in boxing or it won't be handled otherwise SetBoxingState( NOT_BOXING ); } else { BoxingPlayerDisqualified( pNonBoxingPlayer, NON_BOXER_IN_RING ); } return; } if ( gTacticalStatus.bBoxingState == BOXING_WAITING_FOR_PLAYER ) { if ( ubTotalInRing == 1 && ubPlayersInRing == 1 ) { // time to go to pre-boxing SetBoxingState( PRE_BOXING ); PickABoxer(); } } else { // if pre-boxing, check for two people (from different teams!) in the ring if (gTacticalStatus.bBoxingState == PRE_BOXING) { if (ubTotalInRing == 2 && ubPlayersInRing == 1) { // ladieees and gennleman, we have a fight! for (uiLoop = 0; uiLoop < 2; ++uiLoop) { if (!(pInRing[uiLoop]->flags.uiStatusFlags & SOLDIER_BOXER)) { // set as boxer! pInRing[uiLoop]->flags.uiStatusFlags |= SOLDIER_BOXER; } } // start match! SetBoxingState(BOXING); gfLastBoxingMatchWonByPlayer = FALSE; #ifdef JA2TESTVERSION ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Boxer APs %d %d", pInRing[0]->bActionPoints, pInRing[1]->bActionPoints ); #endif // give the first turn to a randomly chosen boxer EnterCombatMode(pInRing[Random(2)]->bTeam); } } /* else { // check to see if the player has more than one person in the ring if ( ubPlayersInRing > 1 ) { // boxing match just became invalid! BoxingPlayerDisqualified( pNonBoxingPlayer, NON_BOXER_IN_RING ); return; } } */ } }
BOOLEAN CanExchangePlaces( SOLDIERTYPE *pSoldier1, SOLDIERTYPE *pSoldier2, BOOLEAN fShow ) { // NB checks outside of this function if ( EnoughPoints( pSoldier1, AP_EXCHANGE_PLACES, 0, fShow ) ) { if ( EnoughPoints( pSoldier2, AP_EXCHANGE_PLACES, 0, fShow ) ) { if ( ( gAnimControl[ pSoldier2->usAnimState ].uiFlags & ANIM_MOVING ) ) { return( FALSE ); } if ( ( gAnimControl[ pSoldier1->usAnimState ].uiFlags & ANIM_MOVING ) && !(gTacticalStatus.uiFlags & INCOMBAT) ) { return( FALSE ); } if ( pSoldier2->bSide == 0 ) { return( TRUE ); } // hehe - don't allow animals to exchange places if ( pSoldier2->uiStatusFlags & ( SOLDIER_ANIMAL ) ) { return( FALSE ); } // must NOT be hostile, must NOT have stationary orders OR militia team, must be >= OKLIFE if ( pSoldier2->bNeutral && pSoldier2->bLife >= OKLIFE && pSoldier2->ubCivilianGroup != HICKS_CIV_GROUP && ( ( pSoldier2->bOrders != STATIONARY || pSoldier2->bTeam == MILITIA_TEAM ) || ( pSoldier2->sAbsoluteFinalDestination != NOWHERE && pSoldier2->sAbsoluteFinalDestination != pSoldier2->sGridNo ) ) ) { return( TRUE ); } if ( fShow ) { if ( pSoldier2->ubProfile == NO_PROFILE ) { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, TacticalStr[ REFUSE_EXCHANGE_PLACES ] ); } else { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, gzLateLocalizedString[3], pSoldier2->name ); } } // ATE: OK, reduce this guy's next ai counter.... pSoldier2->uiAIDelay = 100; return( FALSE ); } else { return( FALSE ); } } else { return( FALSE ); } return( TRUE ); }
void DisplayRangeToTarget( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo ) { UINT16 usRange=0; CHAR16 zOutputString[512]; UINT8 title = (UsingNewCTHSystem() == true ? DC_MSG__NCTH_GUN_RANGE_INFORMATION : DC_MSG__GUN_RANGE_INFORMATION); if( sTargetGridNo == NOWHERE || sTargetGridNo == 0 ) { return; } { UINT8 ubLightLevel = LightTrueLevel(sTargetGridNo, gsInterfaceLevel); UINT8 ubBrightness = 100 - 100 * (ubLightLevel-SHADE_MAX)/(SHADE_MIN-SHADE_MAX); // percentage UINT8 ubTerrainType = NO_TERRAIN; // anv: additional tile properties ADDITIONAL_TILE_PROPERTIES_VALUES zGivenTileProperties; memset(&zGivenTileProperties,0,sizeof(zGivenTileProperties)); if(gGameExternalOptions.fAdditionalTileProperties) { zGivenTileProperties = GetAllAdditonalTilePropertiesForGrid(sTargetGridNo, gsInterfaceLevel); } else { ubTerrainType = GetTerrainTypeForGrid(sTargetGridNo, gsInterfaceLevel); } INT8 ubCover = - GetSightAdjustment(pSoldier, sTargetGridNo, gsInterfaceLevel); //display a string with cover value of current selected merc and brightness //swprintf( zOutputString, gzDisplayCoverText[DC_MSG__COVER_INFORMATION], ubCover, GetTerrainName(ubTerrainType), ubBrightness ); //Display the msg //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, zOutputString ); if(gGameExternalOptions.fAdditionalTileProperties) { if(!gGameExternalOptions.fCoverTooltipDetailedTileProperties) { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, gzDisplayCoverText[DC_MSG__COVER_INFORMATION], ubCover, GetDetailedTerrainName(zGivenTileProperties), ubBrightness ); } else { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, gzDisplayCoverText[DC_MSG__COVER_INFORMATION_WITH_DETAILED_CAMO], ubCover, ubBrightness ); UINT8 ubApplicableProperties = 0; swprintf( zOutputString, L"" ); if(zGivenTileProperties.bWoodCamoAffinity > 0) { swprintf( zOutputString + wcslen(zOutputString), gzDisplayCoverText[DC_TTI__WOOD]); swprintf( zOutputString + wcslen(zOutputString), L": %d/100", zGivenTileProperties.bWoodCamoAffinity); ubApplicableProperties++; } if(zGivenTileProperties.bDesertCamoAffinity > 0) { if(ubApplicableProperties) swprintf( zOutputString + wcslen(zOutputString), L", "); swprintf( zOutputString + wcslen(zOutputString), gzDisplayCoverText[DC_TTI__DESERT]); swprintf( zOutputString + wcslen(zOutputString), L": %d/100", zGivenTileProperties.bDesertCamoAffinity); ubApplicableProperties++; } if(zGivenTileProperties.bUrbanCamoAffinity > 0) { if(ubApplicableProperties) swprintf( zOutputString + wcslen(zOutputString), L", "); swprintf( zOutputString + wcslen(zOutputString), gzDisplayCoverText[DC_TTI__URBAN]); swprintf( zOutputString + wcslen(zOutputString), L": %d/100", zGivenTileProperties.bUrbanCamoAffinity); ubApplicableProperties++; } if(zGivenTileProperties.bSnowCamoAffinity > 0) { if(ubApplicableProperties) swprintf( zOutputString + wcslen(zOutputString), L", "); swprintf( zOutputString + wcslen(zOutputString), gzDisplayCoverText[DC_TTI__SNOW]); swprintf( zOutputString + wcslen(zOutputString), L": %d/100", zGivenTileProperties.bSnowCamoAffinity); ubApplicableProperties++; } if(zGivenTileProperties.bSoundModifier != 0) { if(ubApplicableProperties) swprintf( zOutputString + wcslen(zOutputString), L", "); swprintf( zOutputString + wcslen(zOutputString), gzDisplayCoverText[DC_TTI__DETAILED_SOUND]); if(zGivenTileProperties.bSoundModifier > 0) swprintf( zOutputString + wcslen(zOutputString), L": +%d", zGivenTileProperties.bSoundModifier); else swprintf( zOutputString + wcslen(zOutputString), L": %d", zGivenTileProperties.bSoundModifier); ubApplicableProperties++; } if(zGivenTileProperties.bStealthDifficultyModifer != 0) { if(ubApplicableProperties) swprintf( zOutputString + wcslen(zOutputString), L", "); swprintf( zOutputString + wcslen(zOutputString), gzDisplayCoverText[DC_TTI__DETAILED_STEALTH]); if(zGivenTileProperties.bStealthDifficultyModifer > 0) swprintf( zOutputString + wcslen(zOutputString), L": +%d/100", zGivenTileProperties.bStealthDifficultyModifer); else swprintf( zOutputString + wcslen(zOutputString), L": %d/100", zGivenTileProperties.bStealthDifficultyModifer); ubApplicableProperties++; } if(zGivenTileProperties.bTrapBonus != 0) { if(ubApplicableProperties) swprintf( zOutputString + wcslen(zOutputString), L", "); swprintf( zOutputString + wcslen(zOutputString), gzDisplayCoverText[DC_TTI__DETAILED_TRAP_LEVEL]); if(zGivenTileProperties.bTrapBonus > 0) swprintf( zOutputString + wcslen(zOutputString), L": +%d", zGivenTileProperties.bTrapBonus); else swprintf( zOutputString + wcslen(zOutputString), L": %d", zGivenTileProperties.bTrapBonus); ubApplicableProperties++; } if( wcslen(zOutputString) > 0 ) ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, zOutputString ); } } else { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, gzDisplayCoverText[DC_MSG__COVER_INFORMATION], ubCover, GetTerrainName(ubTerrainType), ubBrightness ); } } //Get the range to the target location usRange = GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo, sTargetGridNo ); //if the soldier has a weapon in hand, display gun range and chance to hit if( WeaponInHand( pSoldier ) ) { UINT32 uiHitChance; //AXP 30.03.2007: Fix CtH calculation for first shot after changing aim level (roof/ground) INT8 bTempTargetLevel = pSoldier->bTargetLevel; pSoldier->bTargetLevel = (INT8)gsInterfaceLevel; uiHitChance = CalcChanceToHitGun( pSoldier, sTargetGridNo, (INT8)(pSoldier->aiData.bShownAimTime ), pSoldier->bAimShotLocation ); // HEADROCK HAM B2.7: CTH approximation? if (gGameExternalOptions.fApproximateCTH) { uiHitChance = ChanceToHitApproximation( pSoldier, uiHitChance ); } pSoldier->bTargetLevel = bTempTargetLevel; // HEADROCK HAM 3.6: Calculate Gun Range using formula. // Flugente: we might be equipped with an underbarrel gun.... OBJECTTYPE* pObjhand = pSoldier->GetUsedWeapon(&pSoldier->inv[HANDPOS]); UINT16 usGunRange = GunRange(pObjhand, pSoldier ); // SANDRO - added argument swprintf( zOutputString, gzDisplayCoverText[title], usRange / 10, usGunRange / 10, uiHitChance ); //Display the msg ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, zOutputString ); } else { swprintf( zOutputString, gzDisplayCoverText[title], usRange / 10, 0, 0 ); //Display the msg ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, zOutputString ); } //increment the display gun range counter ( just seeing how many times people use it ) //gJa25SaveStruct.uiDisplayGunRangeCounter++; }
void IssueHeadMinerQuote( INT8 bMineIndex, UINT8 ubQuoteType ) { UINT8 ubHeadMinerIndex = 0; UINT16 usHeadMinerProfileId = 0; INT8 bQuoteNum = 0; UINT8 ubFaceIndex = 0; BOOLEAN fForceMapscreen = FALSE; INT16 sXPos, sYPos; Assert( ( bMineIndex >= 0 ) && ( bMineIndex < MAX_NUMBER_OF_MINES ) ); Assert( ubQuoteType < NUM_HEAD_MINER_STRATEGIC_QUOTES ); Assert( CheckFact( FACT_MINERS_PLACED, 0 ) ); ubHeadMinerIndex = GetHeadMinerIndexForMine( bMineIndex ); usHeadMinerProfileId = gHeadMinerData[ ubHeadMinerIndex ].usProfileId; // make sure the miner ain't dead if (gMercProfiles[ usHeadMinerProfileId ].bLife < OKLIFE) { // debug message ScreenMsg( MSG_FONT_RED, MSG_DEBUG, L"Head Miner #%s can't talk (quote #%d)", gMercProfiles[ usHeadMinerProfileId ].zNickname, ubQuoteType); return; } bQuoteNum = gHeadMinerData[ ubHeadMinerIndex ].bQuoteNum[ ubQuoteType ]; Assert( bQuoteNum != -1 ); ubFaceIndex = ( UINT8 ) uiExternalStaticNPCFaces[ gHeadMinerData[ ubHeadMinerIndex ].ubExternalFace ]; // transition to mapscreen is not necessary for "creatures gone" quote - player is IN that mine, so he'll know if ( ubQuoteType != HEAD_MINER_STRATEGIC_QUOTE_CREATURES_GONE ) { fForceMapscreen = TRUE; } // decide where the miner's face and text box should be positioned in order to not obscure the mine he's in as it flashes switch ( bMineIndex ) { case MINE_GRUMM: sXPos = DEFAULT_EXTERN_PANEL_X_POS, sYPos = DEFAULT_EXTERN_PANEL_Y_POS; break; case MINE_CAMBRIA: sXPos = DEFAULT_EXTERN_PANEL_X_POS, sYPos = DEFAULT_EXTERN_PANEL_Y_POS; break; case MINE_ALMA: sXPos = DEFAULT_EXTERN_PANEL_X_POS, sYPos = DEFAULT_EXTERN_PANEL_Y_POS; break; case MINE_DRASSEN: sXPos = DEFAULT_EXTERN_PANEL_X_POS, sYPos = 135; break; case MINE_CHITZENA: sXPos = DEFAULT_EXTERN_PANEL_X_POS, sYPos = 117; break; // there's no head miner in San Mona, this is an error! case MINE_SAN_MONA: default: Assert( FALSE ); sXPos = DEFAULT_EXTERN_PANEL_X_POS, sYPos = DEFAULT_EXTERN_PANEL_Y_POS; break; } SetExternMapscreenSpeechPanelXY( sXPos, sYPos ); // cause this quote to come up for this profile id and an indicator to flash over the mine sector HandleMinerEvent( gHeadMinerData[ ubHeadMinerIndex ].ubExternalFace, gMineLocation[ bMineIndex ].sSectorX, gMineLocation[ bMineIndex ].sSectorY, (INT16) bQuoteNum, fForceMapscreen ); // stop time compression with any miner quote - these are important events. StopTimeCompression(); }
//Because loading and saving the map takes a few seconds, we want to post a message //on the screen and then update it which requires passing the screen back to the main loop. //When we come back for the next frame, we then actually save or load the map. So this //process takes two full screen cycles. UINT32 ProcessFileIO() { INT16 usStartX, usStartY; UINT8 ubNewFilename[50]; switch( gbCurrentFileIOStatus ) { case INITIATE_MAP_SAVE: //draw save message StartFrameBufferRender( ); SaveFontSettings(); SetFont( HUGEFONT ); SetFontForeground( FONT_LTKHAKI ); SetFontShadow( FONT_DKKHAKI ); SetFontBackground( 0 ); swprintf( zOrigName, L"Saving map: %s", gzFilename ); usStartX = 320 - StringPixLength( zOrigName, LARGEFONT1 ) / 2; usStartY = 180 - GetFontHeight( LARGEFONT1 ) / 2; mprintf( usStartX, usStartY, zOrigName ); InvalidateScreen( ); EndFrameBufferRender( ); gbCurrentFileIOStatus = SAVING_MAP; return LOADSAVE_SCREEN; case SAVING_MAP: //save map sprintf( ubNewFilename, "%S", gzFilename ); RaiseWorldLand(); if( gfShowPits ) RemoveAllPits(); OptimizeSchedules(); if ( !SaveWorld( ubNewFilename ) ) { if( gfErrorCatch ) { InitErrorCatchDialog(); return EDIT_SCREEN; } return ERROR_SCREEN; } if( gfShowPits ) AddAllPits(); SetGlobalSectorValues( gzFilename ); if( gfGlobalSummaryExists ) UpdateSectorSummary( gzFilename, gfUpdateSummaryInfo ); iCurrentAction = ACTION_NULL; gbCurrentFileIOStatus = IOSTATUS_NONE; gfRenderWorld = TRUE; gfRenderTaskbar = TRUE; fEnteringLoadSaveScreen = TRUE; RestoreFontSettings(); if( gfErrorCatch ) { InitErrorCatchDialog(); return EDIT_SCREEN; } if( gMapInformation.ubMapVersion != gubMinorMapVersion ) ScreenMsg( FONT_MCOLOR_RED, MSG_ERROR, L"Map data has just been corrupted!!! What did you just do? KM : 0" ); return EDIT_SCREEN; case INITIATE_MAP_LOAD: //draw load message SaveFontSettings(); gbCurrentFileIOStatus = LOADING_MAP; if( gfEditMode && iCurrentTaskbar == TASK_MERCS ) IndicateSelectedMerc( SELECT_NO_MERC ); SpecifyItemToEdit( NULL, -1 ); return LOADSAVE_SCREEN; case LOADING_MAP: //load map DisableUndo(); sprintf( ubNewFilename, "%S", gzFilename ); RemoveMercsInSector( ); if( !LoadWorld( ubNewFilename ) ) { //Want to override crash, so user can do something else. EnableUndo(); SetPendingNewScreen( LOADSAVE_SCREEN ); gbCurrentFileIOStatus = IOSTATUS_NONE; gfGlobalError = FALSE; gfLoadError = TRUE; //RemoveButton( iTempButton ); CreateMessageBox( L" Error loading file. Try another filename?" ); return LOADSAVE_SCREEN; } SetGlobalSectorValues( gzFilename ); RestoreFontSettings(); //Load successful, update necessary information. //ATE: Any current mercs are transfered here... //UpdateMercsInSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ); AddSoldierInitListTeamToWorld( ENEMY_TEAM, 255 ); AddSoldierInitListTeamToWorld( CREATURE_TEAM, 255 ); AddSoldierInitListTeamToWorld( MILITIA_TEAM, 255 ); AddSoldierInitListTeamToWorld( CIV_TEAM, 255 ); iCurrentAction = ACTION_NULL; gbCurrentFileIOStatus = IOSTATUS_NONE; if( !gfCaves && !gfBasement ) { gusLightLevel = 12; if( ubAmbientLightLevel != 4 ) { ubAmbientLightLevel = 4; LightSetBaseLevel( ubAmbientLightLevel ); } } else gusLightLevel = (UINT16)(EDITOR_LIGHT_MAX - ubAmbientLightLevel ); gEditorLightColor = gpLightColors[ 0 ]; gfRenderWorld = TRUE; gfRenderTaskbar = TRUE; fEnteringLoadSaveScreen = TRUE; InitJA2SelectionWindow(); ShowEntryPoints(); EnableUndo(); RemoveAllFromUndoList(); SetEditorSmoothingMode( gMapInformation.ubEditorSmoothingType ); if( gMapInformation.ubEditorSmoothingType == SMOOTHING_CAVES ) AnalyseCaveMapForStructureInfo(); AddLockedDoorCursors(); gubCurrRoomNumber = gubMaxRoomNumber; UpdateRoofsView(); UpdateWallsView(); ShowLightPositionHandles(); SetMercTeamVisibility( ENEMY_TEAM, gfShowEnemies ); SetMercTeamVisibility( CREATURE_TEAM, gfShowCreatures ); SetMercTeamVisibility( MILITIA_TEAM, gfShowRebels ); SetMercTeamVisibility( CIV_TEAM, gfShowCivilians ); BuildItemPoolList(); if( gfShowPits ) AddAllPits(); if( iCurrentTaskbar == TASK_MAPINFO ) { //We have to temporarily remove the current textinput mode, //update the disabled text field values, then restore the current //text input fields. SaveAndRemoveCurrentTextInputMode(); UpdateMapInfoFields(); RestoreSavedTextInputMode(); } return EDIT_SCREEN; } gbCurrentFileIOStatus = IOSTATUS_NONE; return LOADSAVE_SCREEN; }
void LegacyAIPlan::execute(PlanInputData& environment) { if(!environment.turn_based()) { if ( (get_npc()->ubProfile != NO_PROFILE) && (gMercProfiles[ get_npc()->ubProfile ].ubMiscFlags3 & PROFILE_MISC_FLAG3_HANDLE_DONE_TRAVERSAL ) ) { TriggerNPCWithGivenApproach( get_npc()->ubProfile, APPROACH_DONE_TRAVERSAL, FALSE ); gMercProfiles[ get_npc()->ubProfile ].ubMiscFlags3 &= (~PROFILE_MISC_FLAG3_HANDLE_DONE_TRAVERSAL); get_npc()->ubQuoteActionID = 0; // wait a tiny bit get_npc()->aiData.usActionData = 100; get_npc()->aiData.bAction = AI_ACTION_WAIT; return; } if (get_npc()->bTeam == gbPlayerNum) { if (environment.get_tactical_status().fAutoBandageMode) { get_npc()->aiData.bAction = DecideAutoBandage( get_npc() ); return; } } } if ( get_npc()->bTeam != MILITIA_TEAM ) { if ( !sniperwarning && get_npc()->aiData.bOrders == SNIPER ) { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_WATHCHOUTFORSNIPERS] ); sniperwarning = TRUE; } if (!biggunwarning && FindRocketLauncherOrCannon(get_npc()) != NO_SLOT ) { biggunwarning = TRUE; //TODO: don't say this again after reloading a savegame SayQuoteFromAnyBodyInSector( QUOTE_WEARY_SLASH_SUSPUCIOUS ); } } get_npc()->aiData.fAIFlags &= (~AI_CAUTIOUS); // turn off cautious flag // if status override is set, bypass RED/YELLOW and go directly to GREEN! if ((get_npc()->aiData.bBypassToGreen) && (get_npc()->aiData.bAlertStatus < STATUS_BLACK)) { get_npc()->aiData.bAction = DecideActionGreen(get_npc()); if ( !gfTurnBasedAI ) { // reset bypass now get_npc()->aiData.bBypassToGreen = 0; } } else { switch (get_npc()->aiData.bAlertStatus) { case STATUS_GREEN: get_npc()->aiData.bAction = DecideActionGreen(get_npc()); break; case STATUS_YELLOW: get_npc()->aiData.bAction = DecideActionYellow(get_npc()); break; case STATUS_RED: get_npc()->aiData.bAction = DecideActionRed(get_npc(),TRUE); break; case STATUS_BLACK: get_npc()->aiData.bAction = DecideActionBlack(get_npc()); break; } } DEBUGAIMSG("Deciding for guynum "<<(int)get_npc()->ubID<<" at gridno "<<get_npc()->sGridNo<<", APs "<<get_npc()->bActionPoints<< ", decided action: "<<(int)get_npc()->aiData.bAction<<", data "<<(int)get_npc()->aiData.usActionData); }
BOOLEAN AddUIPlan( UINT16 sGridNo, UINT8 ubPlanID ) { SOLDIERTYPE *pPlanSoldier; INT16 sXPos, sYPos; INT16 sAPCost = 0; INT8 bDirection; INT32 iLoop; SOLDIERCREATE_STRUCT MercCreateStruct; UINT8 ubNewIndex; // Depeding on stance and direction facing, add guy! // If we have a planned action here, ignore! // If not OK Dest, ignore! if ( !NewOKDestination( gpUIPlannedSoldier, sGridNo, FALSE, (INT8)gsInterfaceLevel ) ) { return( FALSE ); } if ( ubPlanID == UIPLAN_ACTION_MOVETO ) { // Calculate cost to move here sAPCost = PlotPath( gpUIPlannedSoldier, sGridNo, COPYROUTE, NO_PLOT, TEMPORARY, (UINT16) gpUIPlannedSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, gpUIPlannedSoldier->bActionPoints ); // Adjust for running if we are not already running if ( gpUIPlannedSoldier->usUIMovementMode == RUNNING ) { sAPCost += AP_START_RUN_COST; } if ( EnoughPoints( gpUIPlannedSoldier, sAPCost, 0, FALSE ) ) { memset( &MercCreateStruct, 0, sizeof( MercCreateStruct ) ); MercCreateStruct.bTeam = SOLDIER_CREATE_AUTO_TEAM; MercCreateStruct.ubProfile = NO_PROFILE; MercCreateStruct.fPlayerPlan = TRUE; MercCreateStruct.bBodyType = gpUIPlannedSoldier->ubBodyType; MercCreateStruct.sInsertionGridNo = sGridNo; // Get Grid Corrdinates of mouse if ( TacticalCreateSoldier( &MercCreateStruct, &ubNewIndex ) ) { // Get pointer to soldier GetSoldier( &pPlanSoldier, (UINT16)ubNewIndex ); pPlanSoldier->sPlannedTargetX = -1; pPlanSoldier->sPlannedTargetY = -1; // Compare OPPLISTS! // Set ones we don't know about but do now back to old ( ie no new guys ) for (iLoop = 0; iLoop < MAX_NUM_SOLDIERS; iLoop++ ) { if ( gpUIPlannedSoldier->bOppList[ iLoop ] < 0 ) { pPlanSoldier->bOppList[ iLoop ] = gpUIPlannedSoldier->bOppList[ iLoop ]; } } // Get XY from Gridno ConvertGridNoToCenterCellXY( sGridNo, &sXPos, &sYPos ); EVENT_SetSoldierPosition( pPlanSoldier, sXPos, sYPos ); EVENT_SetSoldierDestination( pPlanSoldier, sGridNo ); pPlanSoldier->bVisible = 1; pPlanSoldier->usUIMovementMode = gpUIPlannedSoldier->usUIMovementMode; pPlanSoldier->bActionPoints = gpUIPlannedSoldier->bActionPoints - sAPCost; pPlanSoldier->ubPlannedUIAPCost = (UINT8)pPlanSoldier->bActionPoints; // Get direction bDirection = (INT8)gpUIPlannedSoldier->usPathingData[ gpUIPlannedSoldier->usPathDataSize - 1 ]; // Set direction pPlanSoldier->bDirection = bDirection; pPlanSoldier->bDesiredDirection = bDirection; // Set walking animation ChangeSoldierState( pPlanSoldier, pPlanSoldier->usUIMovementMode, 0, FALSE ); // Change selected soldier gusSelectedSoldier = (UINT16)pPlanSoldier->ubID; // Change global planned mode to this guy! gpUIPlannedSoldier = pPlanSoldier; gubNumUIPlannedMoves++; gfPlotNewMovement = TRUE; ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Adding Merc Move to Plan" ); } } else { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Merc will not have enough action points" ); } } else if ( ubPlanID == UIPLAN_ACTION_FIRE ) { sAPCost = CalcTotalAPsToAttack( gpUIPlannedSoldier, sGridNo, TRUE, (INT8)(gpUIPlannedSoldier->bShownAimTime /2) ); // Get XY from Gridno ConvertGridNoToCenterCellXY( sGridNo, &sXPos, &sYPos ); // If this is a player guy, show message about no APS if ( EnoughPoints( gpUIPlannedSoldier, sAPCost, 0, FALSE ) ) { // CHECK IF WE ARE A PLANNED SOLDIER OR NOT< IF SO< CREATE! if ( gpUIPlannedSoldier->ubID < MAX_NUM_SOLDIERS ) { memset( &MercCreateStruct, 0, sizeof( MercCreateStruct ) ); MercCreateStruct.bTeam = SOLDIER_CREATE_AUTO_TEAM; MercCreateStruct.ubProfile = NO_PROFILE; MercCreateStruct.fPlayerPlan = TRUE; MercCreateStruct.bBodyType = gpUIPlannedSoldier->ubBodyType; MercCreateStruct.sInsertionGridNo = sGridNo; // Get Grid Corrdinates of mouse if ( TacticalCreateSoldier( &MercCreateStruct, &ubNewIndex ) ) { // Get pointer to soldier GetSoldier( &pPlanSoldier, (UINT16)ubNewIndex ); pPlanSoldier->sPlannedTargetX = -1; pPlanSoldier->sPlannedTargetY = -1; // Compare OPPLISTS! // Set ones we don't know about but do now back to old ( ie no new guys ) for (iLoop = 0; iLoop < MAX_NUM_SOLDIERS; iLoop++ ) { if ( gpUIPlannedSoldier->bOppList[ iLoop ] < 0 ) { pPlanSoldier->bOppList[ iLoop ] = gpUIPlannedSoldier->bOppList[ iLoop ]; } } EVENT_SetSoldierPosition( pPlanSoldier, gpUIPlannedSoldier->dXPos, gpUIPlannedSoldier->dYPos ); EVENT_SetSoldierDestination( pPlanSoldier, gpUIPlannedSoldier->sGridNo ); pPlanSoldier->bVisible = 1; pPlanSoldier->usUIMovementMode = gpUIPlannedSoldier->usUIMovementMode; pPlanSoldier->bActionPoints = gpUIPlannedSoldier->bActionPoints - sAPCost; pPlanSoldier->ubPlannedUIAPCost = (UINT8)pPlanSoldier->bActionPoints; // Get direction bDirection = (INT8)gpUIPlannedSoldier->usPathingData[ gpUIPlannedSoldier->usPathDataSize - 1 ]; // Set direction pPlanSoldier->bDirection = bDirection; pPlanSoldier->bDesiredDirection = bDirection; // Set walking animation ChangeSoldierState( pPlanSoldier, pPlanSoldier->usUIMovementMode, 0, FALSE ); // Change selected soldier gusSelectedSoldier = (UINT16)pPlanSoldier->ubID; // Change global planned mode to this guy! gpUIPlannedSoldier = pPlanSoldier; gubNumUIPlannedMoves++; } } gpUIPlannedSoldier->bActionPoints = gpUIPlannedSoldier->bActionPoints - sAPCost; gpUIPlannedSoldier->ubPlannedUIAPCost = (UINT8)gpUIPlannedSoldier->bActionPoints; // Get direction from gridno bDirection = (INT8)GetDirectionFromGridNo( sGridNo, gpUIPlannedSoldier ); // Set direction gpUIPlannedSoldier->bDirection = bDirection; gpUIPlannedSoldier->bDesiredDirection = bDirection; // Set to shooting animation SelectPausedFireAnimation( gpUIPlannedSoldier ); gpUIPlannedSoldier->sPlannedTargetX = sXPos; gpUIPlannedSoldier->sPlannedTargetY = sYPos; ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Adding Merc Shoot to Plan" ); } else { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Merc will not have enough action points" ); } } return( TRUE ); }
INT16 InternalGoAsFarAsPossibleTowards(SOLDIERTYPE *pSoldier, INT16 sDesGrid, INT8 bReserveAPs, INT8 bAction, INT8 fFlags ) { INT16 sLoop,sAPCost; INT16 sTempDest,sGoToGrid; INT16 sOrigin; UINT16 usMaxDist; UINT8 ubDirection,ubDirsLeft,ubDirChecked[8],fFound = FALSE; INT8 bAPsLeft, fPathFlags; UINT8 ubRoomRequired = 0, ubTempRoom; if ( bReserveAPs == -1 ) { // default reserve points if ( CREATURE_OR_BLOODCAT( pSoldier ) ) { bReserveAPs = 0; } else { bReserveAPs = MAX_AP_CARRIED; } } sTempDest = -1; // obtain maximum roaming distance from soldier's sOrigin usMaxDist = RoamingRange(pSoldier,&sOrigin); if ( pSoldier->bOrders <= CLOSEPATROL && (pSoldier->bTeam == CIV_TEAM || pSoldier->ubProfile != NO_PROFILE ) ) { if ( InARoom( pSoldier->usPatrolGrid[0], &ubRoomRequired ) ) { // make sure this doesn't interfere with pathing for scripts if ( pSoldier->sAbsoluteFinalDestination != NOWHERE ) { ubRoomRequired = 0; } } } pSoldier->usUIMovementMode = DetermineMovementMode(pSoldier, bAction ); if ( pSoldier->usUIMovementMode == RUNNING && fFlags & FLAG_CAUTIOUS ) { pSoldier->usUIMovementMode = WALKING; } #ifdef DEBUGDECISIONS sprintf(tempstr,"%s wants to go towards %d (has range %d)",pSoldier->name,sDesGrid,usMaxDist); AIPopMessage(tempstr); #endif // if soldier is ALREADY at the desired destination, quit right away if (sDesGrid == pSoldier->sGridNo) { return(NOWHERE); } // don't try to approach go after noises or enemies actually in water // would be too easy to throw rocks in water, etc. & distract the AI if (Water(sDesGrid)) { return(NOWHERE); } fPathFlags = 0; if ( CREATURE_OR_BLOODCAT( pSoldier ) ) { /* if ( PythSpacesAway( pSoldier->sGridNo, sDesGrid ) <= PATH_CLOSE_RADIUS ) { // then do a limited range path search and see if we can get there gubNPCDistLimit = 10; if ( !LegalNPCDestination( pSoldier, sDesGrid, ENSURE_PATH, NOWATER, fPathFlags) ) { gubNPCDistLimit = 0; return( NOWHERE ); } else { // allow attempt to path without 'good enough' flag on gubNPCDistLimit = 0; } } else { */ fPathFlags = PATH_CLOSE_GOOD_ENOUGH; //} } // first step: try to find an OK destination at or near the desired gridno if (!LegalNPCDestination(pSoldier,sDesGrid,ENSURE_PATH,NOWATER,fPathFlags)) { #ifdef DEBUGDECISIONS AIPopMessage("destination Grid # itself not valid, looking around it"); #endif if ( CREATURE_OR_BLOODCAT( pSoldier ) ) { // we tried to get close, failed; abort! return( NOWHERE ); } else { // else look at the 8 nearest gridnos to sDesGrid for a valid destination // clear ubDirChecked flag for all 8 directions for (ubDirection = 0; ubDirection < 8; ubDirection++) ubDirChecked[ubDirection] = FALSE; ubDirsLeft = 8; // examine all 8 spots around 'sDesGrid' // keep looking while directions remain and a satisfactory one not found for (ubDirsLeft = 8; ubDirsLeft != 0; ubDirsLeft--) { if (fFound) { break; } // randomly select a direction which hasn't been 'checked' yet do { ubDirection = (UINT8) Random(8); } while (ubDirChecked[ubDirection]); ubDirChecked[ubDirection] = TRUE; // determine the gridno 1 tile away from current friend in this direction sTempDest = NewGridNo(sDesGrid,DirectionInc( (INT16)(ubDirection + 1) )); // if that's out of bounds, ignore it & check next direction if (sTempDest == sDesGrid) continue; if (LegalNPCDestination(pSoldier,sTempDest,ENSURE_PATH,NOWATER,0)) { fFound = TRUE; // found a spot #ifdef DEBUGDECISIONS AINumMessage("Found a spot! ubDirection = ",ubDirection + 1); #endif break; // stop checking in other directions } } if (!fFound) { #ifdef DEBUGDECISIONS AINumMessage("Couldn't find OK destination around grid #",sDesGrid); #endif return(NOWHERE); } // found a good grid #, this becomes our actual desired grid # sDesGrid = sTempDest; } } // HAVE FOUND AN OK destination AND PLOTTED A VALID BEST PATH TO IT #ifdef DEBUGDECISIONS AINumMessage("Chosen legal destination is gridno ",sDesGrid); AINumMessage("Tracing along path, pathRouteToGo = ",pSoldier->pathRouteToGo); #endif sGoToGrid = pSoldier->sGridNo; // start back where soldier is standing now sAPCost = 0; // initialize path cost counter // we'll only go as far along the plotted route as is within our // permitted roaming range, and we'll stop as soon as we're down to <= 5 APs for (sLoop = 0; sLoop < (pSoldier->usPathDataSize - pSoldier->usPathIndex); sLoop++) { // what is the next gridno in the path? //sTempDest = NewGridNo( sGoToGrid,DirectionInc( (INT16) (pSoldier->usPathingData[sLoop] + 1) ) ); sTempDest = NewGridNo( sGoToGrid,DirectionInc( (INT16) (pSoldier->usPathingData[sLoop]) ) ); //NumMessage("sTempDest = ",sTempDest); // this should NEVER be out of bounds if (sTempDest == sGoToGrid) { #ifdef BETAVERSION sprintf(tempstr,"GoAsFarAsPossibleTowards: ERROR - gridno along valid route is invalid! guynum %d, sTempDest = %d",pSoldier->ubID,sTempDest); #ifdef RECORDNET fprintf(NetDebugFile,"\n\t%s\n",tempstr); #endif PopMessage(tempstr); SaveGame(ERROR_SAVE); #endif break; // quit here, sGoToGrid is where we are going } // if this takes us beyond our permitted "roaming range" if (SpacesAway(sOrigin,sTempDest) > usMaxDist) break; // quit here, sGoToGrid is where we are going if ( ubRoomRequired ) { if ( !( InARoom( sTempDest, &ubTempRoom ) && ubTempRoom == ubRoomRequired ) ) { // quit here, limited by room! break; } } if ( (fFlags & FLAG_STOPSHORT) && SpacesAway( sDesGrid, sTempDest ) <= STOPSHORTDIST ) { break; // quit here, sGoToGrid is where we are going } // if this gridno is NOT a legal NPC destination // DONT'T test path again - that would replace the traced path! - Ian // NOTE: It's OK to go *THROUGH* water to try and get to the destination! if (!LegalNPCDestination(pSoldier,sTempDest,IGNORE_PATH,WATEROK,0)) break; // quit here, sGoToGrid is where we are going // CAN'T CALL PathCost() HERE! IT CALLS findBestPath() and overwrites // pathRouteToGo !!! Gotta calculate the cost ourselves - Ian // //ubAPsLeft = pSoldier->bActionPoints - PathCost(pSoldier,sTempDest,FALSE,FALSE,FALSE,FALSE,FALSE); if (gfTurnBasedAI) { // if we're just starting the "costing" process (first gridno) if (sLoop == 0) { /* // first, add any additional costs - such as intermediate animations, etc. switch(pSoldier->anitype[pSoldier->anim]) { // in theory, no NPC should ever be in one of these animations as // things stand (they don't medic anyone), but leave it for robustness case START_AID : case GIVING_AID : sAnimCost = AP_STOP_FIRST_AID; break; case TWISTOMACH : case COLLAPSED : sAnimCost = AP_GET_UP; break; case TWISTBACK : case UNCONSCIOUS : sAnimCost = (AP_ROLL_OVER + AP_GET_UP); break; default : sAnimCost = 0; } // this is our first cost sAPCost += sAnimCost; */ if (pSoldier->usUIMovementMode == RUNNING) { sAPCost += AP_START_RUN_COST; } } // ATE: Direction here? sAPCost += EstimateActionPointCost( pSoldier, sTempDest, (INT8) pSoldier->usPathingData[sLoop], pSoldier->usUIMovementMode, (INT8) sLoop, (INT8) pSoldier->usPathDataSize ); bAPsLeft = pSoldier->bActionPoints - sAPCost; } // if after this, we have <= 5 APs remaining, that's far enough, break out // (the idea is to preserve APs so we can crouch or react if // necessary, and benefit from the carry-over next turn if not needed) // This routine is NOT used by any GREEN AI, so such caution is warranted! if ( gfTurnBasedAI && (bAPsLeft < bReserveAPs) ) break; else { sGoToGrid = sTempDest; // we're OK up to here // if exactly 5 APs left, don't bother checking any further if ( gfTurnBasedAI && (bAPsLeft == bReserveAPs) ) break; } } // if it turned out we couldn't go even 1 tile towards the desired gridno if (sGoToGrid == pSoldier->sGridNo) { #ifdef DEBUGDECISIONS sprintf(tempstr,"%s will go NOWHERE, path doesn't meet criteria",pSoldier->name); AIPopMessage(tempstr); #endif return(NOWHERE); // then go nowhere } else { // possible optimization - stored path IS good if we're going all the way if (sGoToGrid == sDesGrid) { pSoldier->bPathStored = TRUE; pSoldier->sFinalDestination = sGoToGrid; } else if ( pSoldier->usPathIndex == 0 ) { // we can hack this surely! -- CJC pSoldier->bPathStored = TRUE; pSoldier->sFinalDestination = sGoToGrid; pSoldier->usPathDataSize = sLoop + 1; } #ifdef DEBUGDECISIONS ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"%d to %d with %d APs left", pSoldier->ubID, sGoToGrid, pSoldier->bActionPoints ); #endif return( sGoToGrid ); } }
void StrategicHandlePlayerTeamMercDeath( SOLDIERTYPE *pSoldier ) { SOLDIERTYPE *pKiller = NULL; INT16 sSectorX, sSectorY; //if the soldier HAS a profile if( pSoldier->ubProfile != NO_PROFILE ) { //add to the history log the fact that the merc died and the circumstances if( pSoldier->ubAttackerID != NOBODY ) { pKiller = MercPtrs[ pSoldier->ubAttackerID ]; } // CJC Nov 11, 2002 // Use the soldier's sector location unless impossible if (pSoldier->sSectorX != 0 && pSoldier->sSectorY != 0) { sSectorX = pSoldier->sSectorX; sSectorY = pSoldier->sSectorY; } else { sSectorX = gWorldSectorX; sSectorY = gWorldSectorY; } if( pKiller && pKiller->bTeam == OUR_TEAM ) { AddHistoryToPlayersLog( HISTORY_MERC_KILLED_CHARACTER, pSoldier->ubProfile, GetWorldTotalMin(), sSectorX, sSectorY ); } else { AddHistoryToPlayersLog( HISTORY_MERC_KILLED, pSoldier->ubProfile, GetWorldTotalMin(), sSectorX, sSectorY ); } } if ( guiCurrentScreen != GAME_SCREEN ) { ScreenMsg( FONT_RED, MSG_INTERFACE, pMercDeadString[ 0 ], pSoldier->name ); } // robot and EPCs don't count against death rate - the mercs back home don't particularly give a damn about locals & machines! if ( !AM_AN_EPC( pSoldier ) && !AM_A_ROBOT( pSoldier ) ) { // keep track of how many mercs have died under player's command (for death rate, can't wait until removed from team) gStrategicStatus.ubMercDeaths++; } pSoldier->uiStatusFlags |= SOLDIER_DEAD; // Set breath to 0! pSoldier->bBreathMax = pSoldier->bBreath = 0; // not asleep, DEAD! pSoldier->fMercAsleep = FALSE; //if the merc had life insurance if( pSoldier->usLifeInsurance ) { // if he didn't die during auto-resolve if( guiCurrentScreen != AUTORESOLVE_SCREEN ) { // check whether this was obviously a suspicious death // if killed within an hour of being insured if ( pSoldier->uiStartTimeOfInsuranceContract <= GetWorldTotalMin() && GetWorldTotalMin() - pSoldier->uiStartTimeOfInsuranceContract < 60 ) { gMercProfiles[ pSoldier->ubProfile ].ubSuspiciousDeath = VERY_SUSPICIOUS_DEATH; } // if killed by someone on our team, or while there weren't any opponents around else if (Menptr[ pSoldier->ubAttackerID ].bTeam == OUR_TEAM || !gTacticalStatus.fEnemyInSector ) { // cause insurance company to suspect fraud and investigate this claim gMercProfiles[ pSoldier->ubProfile ].ubSuspiciousDeath = SUSPICIOUS_DEATH; } } AddLifeInsurancePayout( pSoldier ); } // robot and EPCs don't penalize morale - merc don't care about fighting machines and the lives of locals much if ( !AM_AN_EPC( pSoldier ) && !AM_A_ROBOT( pSoldier ) ) { // Change morale of others based on this HandleMoraleEvent( pSoldier, MORALE_TEAMMATE_DIED, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ); } //if its a MERC merc, record the time of his death if( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__MERC ) { pSoldier->iEndofContractTime = GetWorldTotalMin(); //set is so Speck can say that a merc is dead LaptopSaveInfo.ubSpeckCanSayPlayersLostQuote = 1; } //Set the fact that the merc is DEAD!! gMercProfiles[ pSoldier->ubProfile ].bMercStatus = MERC_IS_DEAD; if( pSoldier->bAssignment != ASSIGNMENT_DEAD ) { SetTimeOfAssignmentChangeForMerc( pSoldier ); } // handle strategic level death HandleStrategicDeath( pSoldier ); }
void InitNPCs( void ) { MERCPROFILESTRUCT * pProfile; // add the pilot at a random location! pProfile = &(gMercProfiles[ SKYRIDER ]); switch( Random( 4 ) ) { case 0: pProfile->sSectorX = 15; pProfile->sSectorY = MAP_ROW_B; pProfile->bSectorZ = 0; break; case 1: pProfile->sSectorX = 14; pProfile->sSectorY = MAP_ROW_E; pProfile->bSectorZ = 0; break; case 2: pProfile->sSectorX = 12; pProfile->sSectorY = MAP_ROW_D; pProfile->bSectorZ = 0; break; case 3: pProfile->sSectorX = 16; pProfile->sSectorY = MAP_ROW_C; pProfile->bSectorZ = 0; break; } #ifdef JA2TESTVERSION ScreenMsg( MSG_FONT_RED, MSG_DEBUG, L"Skyrider in %c %d", 'A' + pProfile->sSectorY - 1, pProfile->sSectorX ); #endif // use alternate map, with Skyrider's shack, in this sector SectorInfo[ SECTOR( pProfile->sSectorX, pProfile->sSectorY ) ].uiFlags |= SF_USE_ALTERNATE_MAP; // set up Madlab's secret lab (he'll be added when the meanwhile scene occurs) switch( Random( 4 ) ) { case 0: // use alternate map in this sector SectorInfo[ SECTOR( 7, MAP_ROW_H ) ].uiFlags |= SF_USE_ALTERNATE_MAP; break; case 1: SectorInfo[ SECTOR( 16, MAP_ROW_H ) ].uiFlags |= SF_USE_ALTERNATE_MAP; break; case 2: SectorInfo[ SECTOR( 11, MAP_ROW_I ) ].uiFlags |= SF_USE_ALTERNATE_MAP; break; case 3: SectorInfo[ SECTOR( 4, MAP_ROW_E ) ].uiFlags |= SF_USE_ALTERNATE_MAP; break; } // add Micky in random location pProfile = &(gMercProfiles[MICKY]); switch( Random( 5 ) ) { case 0: pProfile->sSectorX = 9; pProfile->sSectorY = MAP_ROW_G; pProfile->bSectorZ = 0; break; case 1: pProfile->sSectorX = 13; pProfile->sSectorY = MAP_ROW_D; pProfile->bSectorZ = 0; break; case 2: pProfile->sSectorX = 5; pProfile->sSectorY = MAP_ROW_C; pProfile->bSectorZ = 0; break; case 3: pProfile->sSectorX = 2; pProfile->sSectorY = MAP_ROW_H; pProfile->bSectorZ = 0; break; case 4: pProfile->sSectorX = 6; pProfile->sSectorY = MAP_ROW_C; pProfile->bSectorZ = 0; break; } #ifdef JA2TESTVERSION ScreenMsg( MSG_FONT_RED, MSG_DEBUG, L"%s in %c %d", pProfile->zNickname, 'A' + pProfile->sSectorY - 1, pProfile->sSectorX ); #endif // use alternate map in this sector //SectorInfo[ SECTOR( pProfile->sSectorX, pProfile->sSectorY ) ].uiFlags |= SF_USE_ALTERNATE_MAP; gfPlayerTeamSawJoey = FALSE; if ( gGameOptions.fSciFi ) { // add Bob pProfile = &(gMercProfiles[BOB]); pProfile->sSectorX = 8; pProfile->sSectorY = MAP_ROW_F; pProfile->bSectorZ = 0; // add Gabby in random location pProfile = &(gMercProfiles[GABBY]); switch( Random( 2 ) ) { case 0: pProfile->sSectorX = 11; pProfile->sSectorY = MAP_ROW_H; pProfile->bSectorZ = 0; break; case 1: pProfile->sSectorX = 4; pProfile->sSectorY = MAP_ROW_I; pProfile->bSectorZ = 0; break; } #ifdef JA2TESTVERSION ScreenMsg( MSG_FONT_RED, MSG_DEBUG, L"%s in %c %d", pProfile->zNickname, 'A' + pProfile->sSectorY - 1, pProfile->sSectorX ); #endif // use alternate map in this sector SectorInfo[ SECTOR( pProfile->sSectorX, pProfile->sSectorY ) ].uiFlags |= SF_USE_ALTERNATE_MAP; } else { //not scifi, so use alternate map in Tixa's b1 level that doesn't have the stairs going down to the caves. UNDERGROUND_SECTORINFO *pSector; pSector = FindUnderGroundSector( 9, 10, 1 ); //j9_b1 if( pSector ) { pSector->uiFlags |= SF_USE_ALTERNATE_MAP; } } // init hospital variables giHospitalTempBalance = 0; giHospitalRefund = 0; gbHospitalPriceModifier = 0; // set up Devin so he will be placed ASAP gMercProfiles[ DEVIN ].bNPCData = 3; }
//This will automatically update obsolete map versions to the new ones. This will even //work in the game itself, but would require conversion to happen every time. This is completely //transparent to the rest of the game, but in the editor, obsolete versions will be updated upon //loading and won't be permanently updated until the map is saved, regardless of changes. void UpdateOldVersionMap() { #if 0 //This code is no longer needed since the major version update from 1.0 to 4.0 //However, I am keeping it in for reference. SOLDIERINITNODE *curr; INT32 i; LEVELNODE *pStruct; //VERSION 0 -- obsolete November 14, 1997 if( gMapInformation.ubMapVersion == 0 ) { //Soldier information contained two fixable bugs. gMapInformation.ubMapVersion++; curr = gSoldierInitHead; while( curr ) { //Bug #01) Nodes without detailed slots weren't initialized. if( !curr->pBasicPlacement->fDetailedPlacement ) curr->pDetailedPlacement = NULL; //Bug #02) The attitude variable was accidentally being generated like attributes // which put it completely out of range. if( curr->pBasicPlacement->bAttitude > 7 ) curr->pBasicPlacement->bAttitude = (INT8)Random(8); //go to next node curr = curr->next; } } //VERSION 1 -- obsolete January 7, 1998 if( gMapInformation.ubMapVersion == 1 ) { gMapInformation.ubMapVersion++; //Bug #03) Removing all wall decals from map, because of new changes to the slots // as well as certain decals found commonly in illegal places. for( i = 0; i < WORLD_MAX; i++ ) { RemoveAllStructsOfTypeRange( i, FIRSTWALLDECAL, LASTWALLDECAL ); RemoveAllStructsOfTypeRange( i, FIFTHWALLDECAL, SIXTHWALLDECAL ); } } //VERSION 2 -- obsolete February 3, 1998 if( gMapInformation.ubMapVersion == 2 ) { gMapInformation.ubMapVersion++; curr = gSoldierInitHead; while( curr ) { //Bug #04) Assign enemy mercs default army color code if applicable if( curr->pBasicPlacement->bTeam == ENEMY_TEAM && !curr->pBasicPlacement->ubSoldierClass ) { if( !curr->pDetailedPlacement ) { curr->pBasicPlacement->ubSoldierClass = SOLDIER_CLASS_ARMY; } else if( curr->pDetailedPlacement && curr->pDetailedPlacement->ubProfile == NO_PROFILE ) { curr->pBasicPlacement->ubSoldierClass = SOLDIER_CLASS_ARMY; curr->pDetailedPlacement->ubSoldierClass = SOLDIER_CLASS_ARMY; } } curr = curr->next; } } //VERSION 3 -- obsolete February 9, 1998 if( gMapInformation.ubMapVersion == 3 ) { gMapInformation.ubMapVersion++; //Bug #05) Move entry points down if necessary. ValidateEntryPointGridNo( &gMapInformation.sNorthGridNo ); ValidateEntryPointGridNo( &gMapInformation.sEastGridNo ); ValidateEntryPointGridNo( &gMapInformation.sSouthGridNo ); ValidateEntryPointGridNo( &gMapInformation.sWestGridNo ); } //VERSION 4 -- obsolete February 25, 1998 if( gMapInformation.ubMapVersion == 4 ) { gMapInformation.ubMapVersion++; //6) Change all doors to FIRSTDOOR for( i = 0; i < WORLD_MAX; i++ ) { //NOTE: Here are the index values for the various doors //DOOR OPEN CLOSED //FIRST 916 912 //SECOND 936 932 //THIRD 956 952 //FOURTH 976 972 pStruct = gpWorldLevelData[ i ].pStructHead; while( pStruct ) { //outside topleft if( pStruct->usIndex == 932 || pStruct->usIndex == 952 || pStruct->usIndex == 972 ) { ReplaceStructIndex( i, pStruct->usIndex, 912 ); break; } else if( pStruct->usIndex == 936 || pStruct->usIndex == 956 || pStruct->usIndex == 976 ) { ReplaceStructIndex( i, pStruct->usIndex, 916 ); break; } //outside topright else if( pStruct->usIndex == 927 || pStruct->usIndex == 947 || pStruct->usIndex == 967 ) { ReplaceStructIndex( i, pStruct->usIndex, 907 ); break; } else if( pStruct->usIndex == 931 || pStruct->usIndex == 951 || pStruct->usIndex == 971 ) { ReplaceStructIndex( i, pStruct->usIndex, 911 ); break; } //inside topleft else if( pStruct->usIndex == 942 || pStruct->usIndex == 962 || pStruct->usIndex == 982 ) { ReplaceStructIndex( i, pStruct->usIndex, 922 ); break; } else if( pStruct->usIndex == 946 || pStruct->usIndex == 966 || pStruct->usIndex == 986 ) { ReplaceStructIndex( i, pStruct->usIndex, 926 ); break; } //inside topright else if( pStruct->usIndex == 937 || pStruct->usIndex == 957 || pStruct->usIndex == 977 ) { ReplaceStructIndex( i, pStruct->usIndex, 917 ); break; } else if( pStruct->usIndex == 941 || pStruct->usIndex == 961 || pStruct->usIndex == 981 ) { ReplaceStructIndex( i, pStruct->usIndex, 921 ); break; } pStruct = pStruct->pNext; } } } //VERSION 5 -- obsolete March 4, 1998 if( gMapInformation.ubMapVersion == 5 ) { gMapInformation.ubMapVersion++; //Bug 7) Remove all exit grids (the format has changed) for( i = 0; i < WORLD_MAX; i++ ) RemoveExitGridFromWorld( i ); } //VERSION 6 -- obsolete March 9, 1998 if( gMapInformation.ubMapVersion == 6 ) { //Bug 8) Change droppable status of merc items so that they are all undroppable. gMapInformation.ubMapVersion++; curr = gSoldierInitHead; while( curr ) { //Bug #04) Assign enemy mercs default army color code if applicable if( curr->pDetailedPlacement ) { for( i = 0; i < curr->pDetailedPlacement->Inv.size(); i++ ) { //make all items undroppable, even if it is empty. This will allow for //random item generation, while empty, droppable slots are locked as empty //during random item generation. curr->pDetailedPlacement->Inv[ i ].fFlags |= OBJECT_UNDROPPABLE; } } curr = curr->next; } } //VERSION 7 -- obsolete April 14, 1998 if( gMapInformation.ubMapVersion == 7 ) { gMapInformation.ubMapVersion++; //Bug 9) Priority placements have been dropped in favor of splitting it into two categories. // The first is Detailed placements, and the second is priority existance. So, all // current detailed placements will also have priority existance. curr = gSoldierInitHead; while( curr ) { if( curr->pDetailedPlacement ) { curr->pBasicPlacement->fPriorityExistance = TRUE; } curr = curr->next; } } if( gMapInformation.ubMapVersion == 14 ) { //Toast all of the ambiguous road pieces that ended up wrapping the byte. LEVELNODE *pStruct, *pStruct2; INT32 i; for( i = 0; i < WORLD_MAX; i++ ) { pStruct = gpWorldLevelData[ i ].pObjectHead; if( pStruct && pStruct->usIndex == 1078 && i < WORLD_MAX-2 && i >= 320 ) { //This is the only detectable road piece that we can repair. pStruct2 = gpWorldLevelData[ i+1 ].pObjectHead; if( pStruct2 && pStruct2->usIndex == 1081 ) { RemoveObject( i, pStruct->usIndex ); RemoveObject( i+1, pStruct->usIndex+1 ); RemoveObject( i+2, pStruct->usIndex+2 ); RemoveObject( i-160, pStruct->usIndex-160 ); RemoveObject( i-159, pStruct->usIndex-159 ); RemoveObject( i-158, pStruct->usIndex-158 ); RemoveObject( i-320, pStruct->usIndex-320 ); RemoveObject( i-319, pStruct->usIndex-319 ); RemoveObject( i-318, pStruct->usIndex-318 ); AddObjectToTail( i, 1334 ); AddObjectToTail( i-160, 1335 ); AddObjectToTail( i-320, 1336 ); AddObjectToTail( i+1, 1337 ); AddObjectToTail( i-159, 1338 ); AddObjectToTail( i-319, 1339 ); AddObjectToTail( i+2, 1340 ); AddObjectToTail( i-158, 1341 ); AddObjectToTail( i-318, 1342 ); } } else if( pStruct && pStruct->usIndex >= 1079 && pStruct->usIndex < 1115 ) { RemoveObject( i, pStruct->usIndex ); } } } if( gMapInformation.ubMapVersion <= 7 ) { if( gfEditMode ) { #ifdef JA2TESTVERSION ScreenMsg( FONT_MCOLOR_RED, MSG_ERROR, L"Currently loaded map is corrupt! Allowing the map to load anyway!" ); #endif } else { if( gbWorldSectorZ ) { AssertMsg( 0, String( "Currently loaded map (%c%d_b%d.dat) is invalid -- less than the minimum supported version.", gWorldSectorY + 'A' - 1, gWorldSectorX, gbWorldSectorZ ) ); } else if( !gbWorldSectorZ ) { AssertMsg( 0, String( "Currently loaded map (%c%d.dat) is invalid -- less than the minimum supported version.", gWorldSectorY + 'A' - 1, gWorldSectorX ) ); } } } //VERSION 8 -- obsolete April 18, 1998 if( gMapInformation.ubMapVersion == 8 ) { gMapInformation.ubMapVersion++; //Bug 10) Padding on detailed placements is uninitialized. Clear all data starting at // fKillSlotIfOwnerDies. curr = gSoldierInitHead; while( curr ) { if( curr->pDetailedPlacement ) { //The size 120 was hand calculated. The remaining padding was 118 bytes //and there were two one byte fields cleared, fKillSlotIfOwnerDies and ubScheduleID memset( &curr->pDetailedPlacement->fKillSlotIfOwnerDies, 0, 120 ); } curr = curr->next; } } //Version 9 -- Kris -- obsolete April 27, 1998 if( gMapInformation.ubMapVersion == 9 ) { gMapInformation.ubMapVersion++; curr = gSoldierInitHead; while( curr ) { //Bug 11) Convert all wheelchaired placement bodytypes to cows. Result of change in the animation database. if( curr->pDetailedPlacement && curr->pDetailedPlacement->bBodyType == CRIPPLECIV ) { curr->pDetailedPlacement->bBodyType = COW; curr->pBasicPlacement->bBodyType = COW; } curr = curr->next; } } if( gMapInformation.ubMapVersion < 12 ) { gMapInformation.ubMapVersion = 12; gMapInformation.sCenterGridNo = -1; } if( gMapInformation.ubMapVersion < 13 ) { //replace all merc ammo inventory slots status value with the max ammo that the clip can hold. INT32 i, cnt; OBJECTTYPE *pItem; gMapInformation.ubMapVersion++; //Bug 10) Padding on detailed placements is uninitialized. Clear all data starting at // fKillSlotIfOwnerDies. curr = gSoldierInitHead; while( curr ) { if( curr->pDetailedPlacement ) { for ( i = 0; i < curr->pDetailedPlacement->Inv.size(); i++ ) { pItem = &curr->pDetailedPlacement->Inv[ i ]; if( Item[ pItem->usItem ].usItemClass & IC_AMMO ) { for( cnt = 0; cnt < pItem->ubNumberOfObjects; cnt++ ) { pItem->shots.ubShotsLeft[ cnt ] = Magazine[ Item[ pItem->usItem ].ubClassIndex ].ubMagSize; } } } } curr = curr->next; } } if( gMapInformation.ubMapVersion < 14 ) { gMapInformation.ubMapVersion++; if( !gfCaves && !gfBasement ) { ReplaceObsoleteRoads(); } } if( gMapInformation.ubMapVersion < 15 ) { //Do nothing. The object layer was expanded from 1 byte to 2 bytes, effecting the //size of the maps. This was due to the fact that the ROADPIECES tileset contains //over 300 pieces, hence requiring a size increase of the tileset subindex for this //layer only. } #endif //end of MAJOR VERSION 3.0 obsolete code if( gMapInformation.ubMapVersion < 15 ) { AssertMsg( 0, "Map is less than minimum supported version." ); } if( gMapInformation.ubMapVersion < 16 ) { gMapInformation.ubMapVersion = 16; gMapInformation.sIsolatedGridNo = -1; } if( gMapInformation.ubMapVersion < 17 ) { gMapInformation.ubMapVersion = 17; //EliminateObjectLayerRedundancy(); } if( gMapInformation.ubMapVersion < 18 ) { // replace useless crowbars with proper ones UINT32 i; gMapInformation.ubMapVersion = 18; for ( i = 0; i < guiNumWorldItems; i++ ) { if ( gWorldItems[ i ].object.usItem == JAR_ELIXIR ) { gWorldItems[ i ].object.usItem = CROWBAR; } } } if( gMapInformation.ubMapVersion < 19 ) { //Do nothing, this is used to force regenerate the map edgepoints in map edgepoints.c gMapInformation.ubMapVersion = 19; } if( gMapInformation.ubMapVersion < 20 ) { //validate the map entry points as the world boundaries have changed. gMapInformation.ubMapVersion = 20; ValidateEntryPointGridNo( &gMapInformation.sNorthGridNo ); ValidateEntryPointGridNo( &gMapInformation.sEastGridNo ); ValidateEntryPointGridNo( &gMapInformation.sSouthGridNo ); ValidateEntryPointGridNo( &gMapInformation.sWestGridNo ); ValidateEntryPointGridNo( &gMapInformation.sCenterGridNo ); ValidateEntryPointGridNo( &gMapInformation.sIsolatedGridNo ); } if( gMapInformation.ubMapVersion < 21 ) { SOLDIERINITNODE *curr; //override any item slots being locked if there is no item in that slot. //Laymen terms: If any items slots are locked to be empty, make them empty but available //for random item generation. gMapInformation.ubMapVersion = 21; curr = gSoldierInitHead; while( curr ) { if( curr->pDetailedPlacement ) { for( UINT32 i = 0; i < curr->pDetailedPlacement->Inv.size(); i++ ) { if( !curr->pDetailedPlacement->Inv[ i ].usItem ) { if( curr->pDetailedPlacement->Inv[ i ].fFlags & OBJECT_UNDROPPABLE ) { if( curr->pDetailedPlacement->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE ) { curr->pDetailedPlacement->Inv[ i ].fFlags &= ~OBJECT_NO_OVERWRITE; } } } } } curr = curr->next; } } if( gMapInformation.ubMapVersion < 22 ) { //Allow map edgepoints to be regenerated as new system has been reenabled. gMapInformation.ubMapVersion = 22; } if( gMapInformation.ubMapVersion < 23 ) { //Allow map edgepoints to be regenerated as new system has been reenabled. SOLDIERINITNODE *curr; gMapInformation.ubMapVersion = 23; if( giCurrentTilesetID == 1 ) //cave/mine tileset only { //convert all civilians to miners which use uniforms and more masculine body types. curr = gSoldierInitHead; while( curr ) { if( curr->pBasicPlacement->bTeam == CIV_TEAM && !curr->pDetailedPlacement ) { curr->pBasicPlacement->ubSoldierClass = SOLDIER_CLASS_MINER; curr->pBasicPlacement->bBodyType = -1; } curr = curr->next; } } } if( gMapInformation.ubMapVersion < 25 ) { gMapInformation.ubMapVersion = 25; if( gfCaves ) { LightSetBaseLevel( 13 ); } } if( gMapInformation.ubMapVersion < 26 ) { //Allow map edgepoints to be regenerated as new system has been reenabled. gMapInformation.ubMapVersion = 26; } if( gMapInformation.ubMapVersion < 27 ) { //Allow map edgepoints to be regenerated as new system has been reenabled. gMapInformation.ubMapVersion = 27; } if( gMapInformation.ubMapVersion < 29 ) { gMapInformation.ubMapVersion = 29; } }
INT32 AddItemToWorld( INT32 sGridNo, OBJECTTYPE *pObject, UINT8 ubLevel, UINT16 usFlags, INT8 bRenderZHeightAboveLevel, INT8 bVisible, INT8 soldierID ) { UINT32 iItemIndex; INT32 iReturn; // ATE: Check if the gridno is OK if ( TileIsOutOfBounds(sGridNo)) { // Display warning..... #ifdef JA2BETAVERSION ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"Error: Item %d was given invalid grid location %d. Please report", pObject->usItem, sGridNo); #endif return( -1 ); } iItemIndex = GetFreeWorldItemIndex( ); //Add the new world item to the table. gWorldItems[ iItemIndex ].fExists = TRUE; gWorldItems[ iItemIndex ].sGridNo = sGridNo; gWorldItems[ iItemIndex ].ubLevel = ubLevel; gWorldItems[ iItemIndex ].usFlags = usFlags; gWorldItems[ iItemIndex ].bVisible = bVisible; gWorldItems[ iItemIndex ].bRenderZHeightAboveLevel = bRenderZHeightAboveLevel; gWorldItems[ iItemIndex ].soldierID = soldierID; gWorldItems[ iItemIndex ].object = *pObject; // Add a bomb reference if needed // Flugente: we can arm bombs in our inventory and then throw them out, which will cause them to be added to the world. Only way to identify those items is via a check for their bDetonatorType if (usFlags & WORLD_ITEM_ARMED_BOMB || ( (Item[pObject->usItem].usItemClass & (IC_BOMB)) && ( ( (*pObject)[0]->data.misc.bDetonatorType == BOMB_TIMED ) || ( (*pObject)[0]->data.misc.bDetonatorType == BOMB_REMOTE ) ) ) ) { // sevenfm: added flag WORLD_ITEM_ARMED_BOMB // this fixes bug with remote explosives not being removed after activation, if they were armed in inventory and thrown afterwards gWorldItems[ iItemIndex ].usFlags |= WORLD_ITEM_ARMED_BOMB; iReturn = AddBombToWorld( iItemIndex ); if (iReturn == -1) { return( -1 ); } else { // OJW - 20091002 - Explosives if (is_networked && is_client) { SOLDIERTYPE* pSoldier = NULL; if (soldierID == -1) { if (gWorldItems[ iItemIndex ].object[0]->data.misc.ubBombOwner > 1) { soldierID = gWorldItems[ iItemIndex ].object[0]->data.misc.ubBombOwner - 2; // undo the hack pSoldier = MercPtrs[ soldierID ]; } } if (pSoldier != NULL) { // if soldier is on our team, or is AI and we are the server if (pSoldier->bTeam == 0 || (pSoldier->bTeam == 1 && is_server)) { // this is a local bomb, so init it that way gWorldBombs[iReturn].iMPWorldItemIndex = 0; gWorldBombs[iReturn].ubMPTeamIndex = pSoldier->bTeam; gWorldBombs[iReturn].bIsFromRemotePlayer = false; // <TODO> the 99 is a hack..though probably doesnt matter send_plant_explosive(soldierID , pObject->usItem , 99 , usFlags , sGridNo , ubLevel , iItemIndex ); } } } } } return ( iItemIndex ); }