/* ** Command & Conquer Generals Zero Hour(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: AcademyStats.cpp ////////////////////////////////////////////////////// //----------------------------------------------------------------------------- // // Electronic Arts Los Angeles // // Confidential Information // Copyright (C) 2003 - All Rights Reserved // //----------------------------------------------------------------------------- // // Project: RTS3 // // File name: AcademyStats.cpp // // Created: Kris Morness, July 2003 // // Desc: Keeps track of various statistics in order to provide advice to // the player about how to improve playing. // //----------------------------------------------------------------------------- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine #include "Common/AcademyStats.h" #include "Common/Energy.h" #include "Common/Player.h" #include "Common/PlayerList.h" #include "Common/PlayerTemplate.h" #include "Common/SpecialPower.h" #include "Common/ThingTemplate.h" #include "Common/Xfer.h" #include "GameClient/ControlBar.h" #include "GameClient/GameText.h" #include "GameLogic/GameLogic.h" #include "GameLogic/Object.h" #include "GameLogic/Module/ContainModule.h" #ifdef _INTERNAL // for occasional debugging... //#pragma optimize("", off) //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes") #endif const char *TheAcademyClassificationTypeNames[] = { "ACT_NONE", "ACT_UPGRADE_RADAR", "ACT_SUPERPOWER", NULL }; #define FRAMES_BETWEEN_UPDATES 30 //------------------------------------------------------------------------------------------------ AcademyStats::AcademyStats() { } //------------------------------------------------------------------------------------------------ void findDozerCommandSet( Object *object, void *userData ) { const CommandSet *dozerCommandSet = (const CommandSet*)userData; if( dozerCommandSet ) { return; } if( object && object->isKindOf( KINDOF_DOZER ) ) { dozerCommandSet = TheControlBar->findCommandSet( object->getCommandSetString() ); } } //------------------------------------------------------------------------------------------------ void AcademyStats::init( const Player *player ) { if( !TheGameLogic ) { return; // GUIEdit crashes on this, so bail } m_nextUpdateFrame = TheGameLogic->getFrame() + FRAMES_BETWEEN_UPDATES; m_firstUpdate = TRUE; m_unknownSide = FALSE; //Init the player and cache some important information. m_player = player; const PlayerTemplate *plyrTemplate = player->getPlayerTemplate(); if( !plyrTemplate || plyrTemplate->getBaseSide().compareNoCase( "USA" ) && plyrTemplate->getBaseSide().compareNoCase( "China" ) && plyrTemplate->getBaseSide().compareNoCase( "GLA" ) ) { //Admittedly, this is a massive violation of data driven design. Shame on me! //Unknown side... don't provide ANY advice. Simplicity reasons. m_unknownSide = TRUE; } //Find the command set for our dozer... so we can extract information about things //we can build. m_dozerCommandSet = NULL; player->iterateObjects( findDozerCommandSet, (void*)m_dozerCommandSet ); m_commandCenterTemplate = NULL; m_supplyCenterTemplate = NULL; if( m_dozerCommandSet ) { for( int i = 0; i < MAX_COMMANDS_PER_SET; i++ ) { const CommandButton *button = m_dozerCommandSet->getCommandButton( i ); if( button ) { const ThingTemplate *thing = button->getThingTemplate(); if( thing ) { if( thing->isKindOf( KINDOF_FS_SUPPLY_CENTER ) ) { m_supplyCenterTemplate = thing; m_supplyCenterCost = m_supplyCenterTemplate->calcCostToBuild( player ); } else if( thing->isKindOf( KINDOF_COMMANDCENTER ) ) { //Wow, this is bogus... but we need this template pointer in order to fire special powers from the //shortcut... ugh. m_commandCenterTemplate = thing; } } } } } //+-----------------------+ //| Tier 1 (Basic advice) | //+-----------------------+ //1) Did player build at least one of each structure type available? //CUT!!! //2) Did player run out of money before building a supply center? m_spentCashBeforeBuildingSupplyCenter = FALSE; m_supplyCentersBuilt = 0; if( !m_supplyCenterTemplate ) { m_supplyCenterCost = 1000; } //3) Did player build radar (if applicable)? m_researchedRadar = FALSE; //4) Did player build any dozers/workers? m_peonsBuilt = 0; //5) Did player ever capture a structure? m_structuresCaptured = 0; //6) Did player spend any generals points? m_generalsPointsSpent = 0; //7) Did player ever use a generals power or superweapon? m_specialPowersUsed = 0; //8) Did player garrison any structures? m_structuresGarrisoned = 0; //9) How idle was the player in building military units? m_idleBuildingUnitsMaxFrames = 0; m_lastUnitBuiltFrame = 0; //10) Did player drag select units? m_dragSelectUnits = 0; //11) Did player upgrade anything? m_upgradesPurchased = 0; //12) Was player out of power for more than 10 minutes? m_powerOutMaxFrames = 0; m_oldestPowerOutFrame = 0; m_hadPowerLastCheck = 0; //13) Extra gatherers built? m_gatherersBuilt = 0; //14) Heros built? m_heroesBuilt = 0; //+------------------------------+ //| Tier 2 (Intermediate advice) | //+------------------------------+ //15) Selected a strategy center battle plan? m_hadAStrategyCenter = FALSE; m_choseAStrategyForCenter = FALSE; //16) Placed units inside tunnel network? m_hadATunnelNetwork = FALSE; m_unitsEnteredTunnelNetwork = 0; //17) Player used control groups? m_controlGroupsUsed = 0; //18) Built secondary income building? m_secondaryIncomeUnitsBuilt = 0; //19) Cleared out garrisoned buildings? m_clearedGarrisonedBuildings = 0; //20) Did the Player pick up salvage (as GLA)? m_salvageCollected = 0; //21) Did the player ever use the "Guard" ability? m_guardAbilityUsedCount = 0; //22) Did the player build more than one Supply Center (that is, did he expand out)? //Uses m_supplyCentersBuilt! //+--------------------------+ //| Tier 3 (Advanced advice) | //+--------------------------+ //25) Did the player use the new alternate interface in the options? //Uses TheGlobalData->m_useAlternateMouse //26) Player did not use the new "double click location attack move/guard" m_doubleClickAttackMoveOrdersGiven = 0; //27) Built barracks within 5 minutes? m_builtBarracksWithinFiveMinutes = FALSE; //28) Built war factory within 10 minutes? m_builtWarFactoryWithinTenMinutes = FALSE; //29) Built tech structure within 15 minutes? m_builtTechStructureWithinFifteenMinutes = FALSE; //30) No income for 2 minutes? m_lastIncomeFrame = 0; m_maxFramesBetweenIncome = 0; //31) Did the Player ever use Dozers/Workers to clear out traps/mines/booby traps? m_mines = 0; m_minesCleared = 0; //32) Captured any sniped vehicles? m_vehiclesRecovered = 0; m_vehiclesSniped = 0; //Neutral team stat //33) Did the player ever build a "disguisable" unit and never used the disguise ability? m_disguisableVehiclesBuilt = 0; m_vehiclesDisguised = 0; //34) Did the player never build a "stealth" upgrade? //CUT!!! //35) Did the player ever create a "Firestorm" with his MiGs or Inferno Cannons? m_firestormsCreated = 0; } //------------------------------------------------------------------------------------------------ static void updateAcademyStats( Object *obj, void *userData ) { AcademyStats *academy = (AcademyStats*)userData; if( !obj || !academy ) { return; } if( academy->isFirstUpdate() ) { academy->recordProduction( obj, NULL ); } } //------------------------------------------------------------------------------------------------ void AcademyStats::update() { if( m_unknownSide ) { return; } UnsignedInt now = TheGameLogic->getFrame(); if( m_nextUpdateFrame >= now ) { m_nextUpdateFrame = now + FRAMES_BETWEEN_UPDATES; m_player->iterateObjects( updateAcademyStats, (void*)this ); //2) Did player run out of money before building a supply center? if( !m_supplyCentersBuilt && !m_spentCashBeforeBuildingSupplyCenter ) { const Money *money = m_player->getMoney(); if( money ) { UnsignedInt amount = money->countMoney(); if( amount < m_supplyCenterCost ) { m_spentCashBeforeBuildingSupplyCenter = TRUE; } } } //12) Was player out of power for more than 10 minutes? const Energy *power = m_player->getEnergy(); if( power ) { Bool hasPower = power->hasSufficientPower(); if( hasPower != m_hadPowerLastCheck ) { if( !hasPower ) { //We just lost power m_oldestPowerOutFrame = now; } else { //We just gained power... see how long we were without. UnsignedInt frames = now - m_oldestPowerOutFrame; if( frames > m_powerOutMaxFrames ) { m_powerOutMaxFrames = frames; } } m_hadPowerLastCheck = hasPower; } } if( isFirstUpdate() ) { setFirstUpdate( FALSE ); } } } //------------------------------------------------------------------------------------------------ void AcademyStats::recordProduction( const Object *obj, const Object *constructer ) { UnsignedInt now = TheGameLogic->getFrame(); //2) Did player run out of money before building a supply center? //22) Did the player build more than one Supply Center (that is, did he expand out)? if( obj->isKindOf( KINDOF_FS_SUPPLY_CENTER ) ) { m_supplyCentersBuilt++; } //4) Did player build any dozers/workers? if( obj->isKindOf( KINDOF_DOZER ) ) { m_peonsBuilt++; } //9) How idle was the player in building military units? if( obj->isKindOf( KINDOF_INFANTRY ) || obj->isKindOf( KINDOF_VEHICLE ) ) { if( !obj->isKindOf( KINDOF_DOZER ) && !obj->isKindOf( KINDOF_HARVESTER ) ) { //How long has it been since we built our last unit? UnsignedInt idleFrames = now - m_lastUnitBuiltFrame; //If it was longer than our max time... then record it. if( idleFrames > m_idleBuildingUnitsMaxFrames ) { m_idleBuildingUnitsMaxFrames = idleFrames; } //Record the frame we built our unit. m_lastUnitBuiltFrame = now; } } //13) Extra gatherers built? if( obj->isKindOf( KINDOF_HARVESTER ) ) { m_gatherersBuilt++; } //14) Heros built? if( obj->isKindOf( KINDOF_HERO ) ) { m_heroesBuilt++; } //15) Selected a strategy center battle plan? if( obj->isKindOf( KINDOF_FS_STRATEGY_CENTER ) ) { m_hadAStrategyCenter = TRUE; } //16) Placed units inside tunnel network? if( obj->getContain() && obj->getContain()->isTunnelContain() ) { m_hadATunnelNetwork = TRUE; } //18) Built secondary income building? if( obj->isKindOf( KINDOF_MONEY_HACKER ) || obj->isKindOf( KINDOF_FS_BLACK_MARKET ) || obj->isKindOf( KINDOF_FS_SUPPLY_DROPZONE ) ) { m_secondaryIncomeUnitsBuilt++; } //27) Built tech structure within 15 minutes? if( obj->isKindOf( KINDOF_FS_BARRACKS ) ) { if( TheGameLogic->getFrame() <= 300 * LOGICFRAMES_PER_SECOND ) { m_builtBarracksWithinFiveMinutes = TRUE; } } //28) Built tech structure within 15 minutes? if( obj->isKindOf( KINDOF_FS_WARFACTORY ) ) { if( TheGameLogic->getFrame() <= 600 * LOGICFRAMES_PER_SECOND ) { m_builtWarFactoryWithinTenMinutes = TRUE; } } //29) Built tech structure within 15 minutes? if( obj->isKindOf( KINDOF_FS_ADVANCED_TECH ) ) { if( TheGameLogic->getFrame() <= 900 * LOGICFRAMES_PER_SECOND ) { m_builtTechStructureWithinFifteenMinutes = TRUE; } } //33) Did the player ever build a "disguisable" unit and never used the disguise ability? if( obj->isKindOf( KINDOF_DISGUISER ) ) { m_disguisableVehiclesBuilt++; } } //------------------------------------------------------------------------------------------------ void AcademyStats::recordUpgrade( const UpgradeTemplate *upgrade, Bool granted ) { //3) Did player build radar (if applicable)? if( upgrade->getAcademyClassificationType() == ACT_UPGRADE_RADAR ) { m_researchedRadar = TRUE; } //11) Did player upgrade anything? if( !granted ) { m_upgradesPurchased++; } } //------------------------------------------------------------------------------------------------ void AcademyStats::recordSpecialPowerUsed( const SpecialPowerTemplate *spTemplate ) { if( spTemplate->getAcademyClassificationType() == ACT_SUPERPOWER ) { m_specialPowersUsed++; } } //------------------------------------------------------------------------------------------------ void AcademyStats::recordIncome() { UnsignedInt now = TheGameLogic->getFrame(); UnsignedInt delta = m_lastIncomeFrame - now; if( delta > m_maxFramesBetweenIncome ) { m_maxFramesBetweenIncome = delta; } m_lastIncomeFrame = now; } //------------------------------------------------------------------------------------------------ void AcademyStats::evaluateTier1Advice( AcademyAdviceInfo *info, Int numAvailableTips ) { UnsignedInt maxAdviceTips = MAX_ADVICE_TIPS; //-allAdvice feature //if( !TheGlobalData->m_allAdvice ) //{ // maxAdviceTips = 1; //} UnsignedInt now = TheGameLogic->getFrame(); //numAvailableTips is used to determine if we are going to randomly choose a tip //or determine if a tip is available (if -1) Bool choosing = numAvailableTips != -1; Int availableTips = 0; //1) Did player build at least one of each structure type available? //CUT!!! //2) Did player run out of money before building a supply center? if( m_spentCashBeforeBuildingSupplyCenter ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:BuildSupplyCenterEarlier" ) ); info->numTips++; } numAvailableTips--; } //3) Did player build radar (if applicable)? if( !m_researchedRadar ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:TryBuildingRadar" ) ); info->numTips++; } numAvailableTips--; } //4) Did player build any dozers/workers? if( m_peonsBuilt < 2 ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:BuildMorePeons" ) ); info->numTips++; } numAvailableTips--; } //5) Did player ever capture a structure? if( !m_structuresCaptured ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:TryCapturingStructures" ) ); info->numTips++; } numAvailableTips--; } //6) Did player spend any generals points? if( !m_generalsPointsSpent ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:SpendGeneralsPoints" ) ); info->numTips++; } numAvailableTips--; } //7) Did player ever use a generals power or superweapon? if( !m_specialPowersUsed ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:TryUsingSuperweapons" ) ); info->numTips++; } numAvailableTips--; } //8) Did player garrison any structures? if( !m_structuresGarrisoned ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:TryGarrisoningAStructure" ) ); info->numTips++; } numAvailableTips--; } //9) How idle was the player in building military units? UnsignedInt idleFrames = now - m_lastUnitBuiltFrame; if( idleFrames > m_idleBuildingUnitsMaxFrames ) { m_idleBuildingUnitsMaxFrames = idleFrames; } if( m_idleBuildingUnitsMaxFrames > 300 * LOGICFRAMES_PER_SECOND || !m_lastUnitBuiltFrame ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:IdleBuildingUnits" ) ); info->numTips++; } numAvailableTips--; } //10) Did player drag select units? if( !m_dragSelectUnits ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:TryDragSelectingUnits" ) ); info->numTips++; } numAvailableTips--; } //11) Did player upgrade anything? if( !m_upgradesPurchased ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:ResearchUpgrades" ) ); info->numTips++; } numAvailableTips--; } //12) Was player out of power for more than 10 minutes? if( !m_hadPowerLastCheck ) { UnsignedInt frames = now - m_oldestPowerOutFrame; if( frames > m_powerOutMaxFrames ) { m_powerOutMaxFrames = frames; } } if( m_powerOutMaxFrames > 600 * LOGICFRAMES_PER_SECOND ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:RanOutOfPower" ) ); info->numTips++; } numAvailableTips--; } //13) Extra gatherers built? if( !m_gatherersBuilt ) { //Don't count free ones that come with supply centers! availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:BuildMoreGatherers" ) ); info->numTips++; } numAvailableTips--; } //14) Heros built? if( !m_heroesBuilt ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:BuildAHero" ) ); info->numTips++; } numAvailableTips--; } if( !choosing && availableTips > 0 ) { //We're done counting available tips, so now that we know the //number, we can randomly choose. evaluateTier1Advice( info, availableTips ); } } //------------------------------------------------------------------------------------------------ void AcademyStats::evaluateTier2Advice( AcademyAdviceInfo *info, Int numAvailableTips ) { UnsignedInt maxAdviceTips = MAX_ADVICE_TIPS; //-allAdvice feature //if( !TheGlobalData->m_allAdvice ) //{ // maxAdviceTips = 1; //} //numAvailableTips is used to determine if we are going to randomly choose a tip //or determine if a tip is available (if -1) Bool choosing = numAvailableTips != -1; Int availableTips = 0; //15) Selected a strategy center battle plan? if( m_hadAStrategyCenter && !m_choseAStrategyForCenter ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:PickStrategyCenterPlan" ) ); info->numTips++; } numAvailableTips--; } //16) Placed units inside tunnel network? if( m_hadATunnelNetwork && !m_unitsEnteredTunnelNetwork ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:UseTunnelNetwork" ) ); info->numTips++; } numAvailableTips--; } //17) Player used control groups? if( !m_controlGroupsUsed ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:UseControlGroups" ) ); info->numTips++; } numAvailableTips--; } //18) Built secondary income building? if( !m_secondaryIncomeUnitsBuilt ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:UseSecondaryIncomeMethods" ) ); info->numTips++; } numAvailableTips--; } //19) Cleared out garrisoned buildings? if( !m_clearedGarrisonedBuildings ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:ClearBuildings" ) ); info->numTips++; } numAvailableTips--; } //20) Did the Player pick up salvage (as GLA)? if( m_player->getPlayerTemplate() && !m_player->getPlayerTemplate()->getBaseSide().compareNoCase( "GLA" ) ) { if( !m_salvageCollected ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:PickUpSalvage" ) ); info->numTips++; } numAvailableTips--; } } //21) Did the player ever use the "Guard" ability? if( !m_guardAbilityUsedCount ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:UseGuardAbility" ) ); info->numTips++; } numAvailableTips--; } //22) Did the player build more than one Supply Center (that is, did he expand out)? if( m_supplyCentersBuilt < 2 ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:MultipleSupplyCenters" ) ); info->numTips++; } numAvailableTips--; } //23) Did the player ever garrison a vehicle? //CUT!!! //24) Did the player ever use the hotkey to grab all of one unit type (change to use any hotkeys)? //CUT!!! if( !choosing && availableTips > 0 ) { //We're done counting available tips, so now that we know the //number, we can randomly choose. evaluateTier2Advice( info, availableTips ); } } //------------------------------------------------------------------------------------------------ void AcademyStats::evaluateTier3Advice( AcademyAdviceInfo *info, Int numAvailableTips ) { UnsignedInt maxAdviceTips = MAX_ADVICE_TIPS; //-allAdvice feature //if( !TheGlobalData->m_allAdvice ) //{ // maxAdviceTips = 1; //} UnsignedInt now = TheGameLogic->getFrame(); //numAvailableTips is used to determine if we are going to randomly choose a tip //or determine if a tip is available (if -1) Bool choosing = numAvailableTips != -1; Int availableTips = 0; //25) Did the player use the new alternate interface in the options? if( !TheGlobalData->m_useAlternateMouse ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:AlternateMouseInterface" ) ); info->numTips++; } numAvailableTips--; } //26) Player did not use the new "double click location attack move/guard" if( !m_doubleClickAttackMoveOrdersGiven ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:DoubleClickAttackMoveGuard" ) ); info->numTips++; } numAvailableTips--; } //27) Built barracks within 5 minutes? if( !m_builtBarracksWithinFiveMinutes ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:BuildBarracksSooner" ) ); info->numTips++; } numAvailableTips--; } //28) Built warfactory within 10 minutes? if( !m_builtWarFactoryWithinTenMinutes ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:BuildWarFactorySooner" ) ); info->numTips++; } numAvailableTips--; } //29) Built tech structure within 15 minutes? if( !m_builtTechStructureWithinFifteenMinutes ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:BuildTechStructureSooner" ) ); info->numTips++; } numAvailableTips--; } //30) No income for 2 minutes? UnsignedInt delta = m_lastIncomeFrame - now; if( delta > m_maxFramesBetweenIncome ) { m_maxFramesBetweenIncome = delta; } if( m_maxFramesBetweenIncome > LOGICFRAMES_PER_SECOND * 120 || !m_lastIncomeFrame ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:NoIncome" ) ); info->numTips++; } numAvailableTips--; } //31) Did the Player ever use Dozers/Workers to clear out traps/mines/booby traps? if( ThePlayerList->getLocalPlayer()->getAcademyStats()->getMines() > 0 && !m_minesCleared ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:NoIncome" ) ); info->numTips++; } numAvailableTips--; } //32) Captured any sniped vehicles? if( ThePlayerList->getNeutralPlayer()->getAcademyStats()->getVehiclesSniped() > 0 ) { if( !m_vehiclesRecovered ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:UnmannedVehicles" ) ); info->numTips++; } numAvailableTips--; } } //33) Did the player ever build a "disguisable" unit and never used the disguise ability? if( m_disguisableVehiclesBuilt ) { if( !m_vehiclesDisguised ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:DisguisedUnits" ) ); info->numTips++; } numAvailableTips--; } } //35) Did the player ever create a "Firestorm" with his MiGs or Inferno Cannons? if( m_player->getPlayerTemplate() && !m_player->getPlayerTemplate()->getBaseSide().compareNoCase( "China" ) ) { if( !m_firestormsCreated ) { availableTips++; Int rand = GameClientRandomValue( 0, numAvailableTips - 1 ); Int limit = maxAdviceTips - info->numTips; if( choosing && rand < limit ) { info->advice[ info->numTips ].concat( TheGameText->fetch( "ACADEMY:Firestorm" ) ); info->numTips++; } numAvailableTips--; } } if( !choosing && availableTips > 0 ) { //We're done counting available tips, so now that we know the //number, we can randomly choose. evaluateTier3Advice( info, availableTips ); } } //------------------------------------------------------------------------------------------------ Bool AcademyStats::calculateAcademyAdvice( AcademyAdviceInfo *info ) { UnsignedInt maxAdviceTips = MAX_ADVICE_TIPS; //-allAdvice feature //if( !TheGlobalData->m_allAdvice ) //{ // maxAdviceTips = 1; //} if( m_unknownSide ) { return FALSE; } //Sanity if( !info ) { DEBUG_CRASH( ("AcademyStats::calculateAcademyAdvice() was passed in NULL AcademyAdviceInfo.") ); return FALSE; } //It's possible to play a perfect game and not need any advice. info->numTips = 0; //Build the header for each string. for( Int i = 0; i < maxAdviceTips; i++ ) { info->advice[ i ].format( UnicodeString( L"\n\n" ) ); } //First look at tier 1 basic advice and pick any advice we could benefit from. evaluateTier1Advice( info ); //Now look at tier 2 intermediate advice. if( info->numTips < maxAdviceTips ) { evaluateTier2Advice( info ); if( info->numTips < maxAdviceTips ) { //Finally look at tier 3 advanced advice. evaluateTier3Advice( info ); } } //Do we have any advice to give? return info->numTips != 0; } //------------------------------------------------------------------------------------------------ // CRC //------------------------------------------------------------------------------------------------ void AcademyStats::crc( Xfer *xfer ) { } // end crc //------------------------------------------------------------------------------------------------ /** Xfer method * Version Info: * 1: Initial version */ //------------------------------------------------------------------------------------------------ void AcademyStats::xfer( Xfer *xfer ) { // version XferVersion currentVersion = 1; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); xfer->xferUnsignedInt( &m_nextUpdateFrame ); xfer->xferBool( &m_firstUpdate ); xfer->xferBool( &m_unknownSide ); //+-----------------------+ //| Tier 1 (Basic advice) | //+-----------------------+ //1) Did player build at least one of each structure type available? //CUT!!! //2) Did player run out of money before building a supply center? xfer->xferBool( &m_spentCashBeforeBuildingSupplyCenter ); xfer->xferUnsignedInt( &m_supplyCentersBuilt ); xfer->xferUnsignedInt( &m_supplyCenterCost ); //3) Did player build radar (if applicable)? xfer->xferBool( &m_researchedRadar ); //4) Did player build any dozers/workers? xfer->xferUnsignedInt( &m_peonsBuilt ); //5) Did player ever capture a structure? xfer->xferUnsignedInt( &m_structuresCaptured ); //6) Did player spend any generals points? xfer->xferUnsignedInt( &m_generalsPointsSpent ); //7) Did player ever use a generals power or superweapon? xfer->xferUnsignedInt( &m_specialPowersUsed ); //8) Did player garrison any structures? xfer->xferUnsignedInt( &m_structuresGarrisoned ); //9) How idle was the player in building military units? xfer->xferUnsignedInt( &m_idleBuildingUnitsMaxFrames ); xfer->xferUnsignedInt( &m_lastUnitBuiltFrame ); //10) Did player drag select units? xfer->xferUnsignedInt( &m_dragSelectUnits ); //11) Did player upgrade anything? xfer->xferUnsignedInt( &m_upgradesPurchased ); //12) Was player out of power for more than 10 minutes? xfer->xferUnsignedInt( &m_powerOutMaxFrames ); xfer->xferUnsignedInt( &m_oldestPowerOutFrame ); xfer->xferBool ( &m_hadPowerLastCheck ); //13) Extra gathers built? xfer->xferUnsignedInt( &m_gatherersBuilt ); //14) Heros built? xfer->xferUnsignedInt( &m_heroesBuilt ); //+------------------------------+ //| Tier 2 (Intermediate advice) | //+------------------------------+ //15) Selected a strategy center battle plan? xfer->xferBool( &m_hadAStrategyCenter ); xfer->xferBool( &m_choseAStrategyForCenter ); //16) Placed units inside tunnel network? xfer->xferUnsignedInt( &m_unitsEnteredTunnelNetwork ); xfer->xferBool( &m_hadATunnelNetwork ); //17) Player used control groups? xfer->xferUnsignedInt( &m_controlGroupsUsed ); //18) Built secondary income unit (hacker, dropzone, blackmarket)? xfer->xferUnsignedInt( &m_secondaryIncomeUnitsBuilt ); //19) Cleared out garrisoned buildings? xfer->xferUnsignedInt( &m_clearedGarrisonedBuildings ); //20) Did the Player pick up salvage (as GLA)? xfer->xferUnsignedInt( &m_salvageCollected ); //21) Did the player ever use the "Guard" ability? xfer->xferUnsignedInt( &m_guardAbilityUsedCount ); //22) Did the player build more than one Supply Center (that is, did he expand out)? //Uses m_supplyCentersBuilt! //23) Did the player ever garrison a vehicle? //CUT!!! //24) Did the player ever use the hotkey to grab all of one unit type (change to use any hotkeys)? //CUT!!! //+--------------------------+ //| Tier 3 (Advanced advice) | //+--------------------------+ //25) Did the player use the new alternate interface in the options? //Uses TheGlobalData->m_useAlternateMouse //26) Player did not use the new "double click location attack move/guard" xfer->xferUnsignedInt( &m_doubleClickAttackMoveOrdersGiven ); //27) Built barracks within 5 minutes? xfer->xferBool( &m_builtBarracksWithinFiveMinutes ); //28) Built war factory within 10 minutes? xfer->xferBool( &m_builtWarFactoryWithinTenMinutes ); //29) Built tech structure within 15 minutes? xfer->xferBool( &m_builtTechStructureWithinFifteenMinutes ); //30) No income for 2 minutes? xfer->xferUnsignedInt( &m_lastIncomeFrame ); xfer->xferUnsignedInt( &m_maxFramesBetweenIncome ); //31) Did the Player ever use Dozers/Workers to clear out traps/mines/booby traps? xfer->xferUnsignedInt( &m_mines ); //Neutral player stat xfer->xferUnsignedInt( &m_minesCleared ); //32) Captured any sniped vehicles? xfer->xferUnsignedInt( &m_vehiclesRecovered ); xfer->xferUnsignedInt( &m_vehiclesSniped ); //33) Did the player ever build a "disguisable" unit and never used the disguise ability? xfer->xferUnsignedInt( &m_disguisableVehiclesBuilt ); xfer->xferUnsignedInt( &m_vehiclesDisguised ); //34) Did the player never build a "stealth" upgrade? //CUT!!! //35) Did the player ever create a "Firestorm" with his MiGs or Inferno Cannons? xfer->xferUnsignedInt( &m_firestormsCreated ); } // end xfer //------------------------------------------------------------------------------------------------ // Load post process //------------------------------------------------------------------------------------------------ void AcademyStats::loadPostProcess( void ) { } // end loadPostProcess