00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00022 #include "BattleEntry.h"
00023 #include "Globals.h"
00024 #include "HUD.h"
00025 #include "GameState.h"
00026 #include "Sound.h"
00027 #include "Icon.h"
00028
00029 using namespace Globals;
00030 using namespace NetworkMessages;
00031
00032 const int SELECT_JETS = 100;
00033
00034
00035
00036
00037 BattleEntry::BattleEntry( const Vec3D teamAColor, const Vec3D teamBColor, const Vec2D location )
00038 {
00039 m_teamAColor = teamAColor;
00040 m_teamBColor = teamBColor;
00041 m_location = location;
00042
00043 m_camTarget = Vec3D(0.0f,0.0f,0.0f);
00044 m_camHeight = 100.0f;
00045
00046 m_firstTime = true;
00047 m_selected = -1;
00048 m_winner = '?';
00049
00050 BS = new BattleState();
00051
00052 for( int i=0; i<9; i++ ) m_ctrlGroup[i] = -1;
00053
00054 BS->m_cvLastContact = 0;
00055 BS->m_cvLastStress = 0;
00056 BS->m_cvLastPanick = 0;
00057
00058 m_rfmCalledA = m_rfmCalledB = 0;
00059 m_showBigMiniMap = true;
00060 }
00061
00062 void BattleEntry::inputSelectGroups()
00063 {
00064 Vec2D mousePos( input_getMouseX(), input_getMouseY() );
00065 Vec3D playerColor( GameState::consolePlayer()->color() );
00066 ControlMode mode = GameState::controlMode();
00067
00068 if( input_isMouseLBClicked() && mousePos.y < 0.8f )
00069 {
00070 int groupAtCursor = getGroupAtPos2D(mousePos);
00071 if( groupAtCursor!=-1 )
00072 {
00073 if( BS->groups[groupAtCursor].m_color==playerColor || mode==GOD )
00074 m_selected = groupAtCursor;
00075 }
00076 else m_selected = -1;
00077
00078 if( m_selected==-1 )
00079 HUD::clearSelections();
00080 else
00081 {
00082 if(m_selected < SELECT_JETS ) HUD::setSelectedGroupType( BS->groups[m_selected].m_unitType );
00083 else HUD::setSelectedGroupType(UnitTypes::JET);
00084 Sound::playRandomSound( Sound::CLASS_SELECT );
00085 }
00086
00087 }
00088 }
00089
00090 void BattleEntry::inputSelectJets()
00091 {
00092 Vec3D playerColor( GameState::consolePlayer()->color() );
00093 Vec2D mousePos( input_getMouseX(), input_getMouseY() );
00094
00095 if( input_isMouseLBClicked() && mousePos.x > 0.940f )
00096 {
00097 FLOAT yOffset = 0.2f;
00098 for( UINT i=0; i<BS->jets.size(); i++ )
00099 {
00100 if( BS->jets[i].m_color!=playerColor ) continue;
00101
00102 if( mousePos.y >= yOffset && mousePos.y <= yOffset+0.07f )
00103 {
00104 m_selected = i + SELECT_JETS;
00105 break;
00106 }
00107 yOffset += 0.08f;
00108 }
00109
00110 if(m_selected>=SELECT_JETS) Sound::playRandomSound( Sound::CLASS_SELECT );
00111 }
00112
00113 if(m_selected==-1 ) HUD::setSelectedGroupType(-1);
00114 else if(m_selected < SELECT_JETS ) HUD::setSelectedGroupType( BS->groups[m_selected].m_unitType );
00115 else HUD::setSelectedGroupType(UnitTypes::JET);
00116 }
00117
00118 void BattleEntry::inputIssueGroupCommand()
00119 {
00120 Vec2D mousePos( input_getMouseX(),input_getMouseY() );
00121
00122 if( m_selected!=-1 && m_selected < SELECT_JETS)
00123 if( input_isKeyPressed(KeyCodes::key_S) || HUD::isButtonStopClicked() )
00124 {
00125 ostringstream cmd;
00126 cmd << COMMAND_BATTLEMODE << " " << COMMIT_TIME << " " << (currGameTick+NETWORK_GAME_TICK_LAG)
00127 << " " << COMMAND_BATTLE_ID << " " << this->uniqueID();
00128 if( net_amIServer() ) cmd << " A "; else cmd << " B ";
00129 cmd << "Stop " << m_selected;
00130 net_send(cmd.str());
00131 return;
00132 }
00133
00134
00135 if( input_isMouseRBClicked() && m_selected!=-1 && m_selected < SELECT_JETS)
00136 {
00137 BattleGroup &G = BS->groups[m_selected];
00138 int targetGrp = getGroupAtPos2D(mousePos);
00139 Sound::playRandomSound( Sound::CLASS_ACK );
00140
00141 ostringstream cmd;
00142 cmd << COMMAND_BATTLEMODE << " " << COMMIT_TIME << " " << (currGameTick+NETWORK_GAME_TICK_LAG)
00143 << " " << COMMAND_BATTLE_ID << " " << this->uniqueID();
00144 if( net_amIServer() ) cmd << " A "; else cmd << " B ";
00145
00146 if( targetGrp!=-1 && getTeamCode(targetGrp)!=getTeamCode(m_selected) )
00147 {
00148 if( input_isKeyDown(KeyCodes::key_LCONTROL) || input_isKeyDown(KeyCodes::key_RCONTROL) )
00149 {
00150 cmd << "AssignTarget " << m_selected << ' ' << targetGrp;
00151 }
00152 else
00153 {
00154 cmd << "Intercept " << m_selected << ' ' << targetGrp;
00155 }
00156 }
00157 else
00158 {
00159 Vec3D pt = math_translateScreenToWorldPlane( mousePos, 0.0f );
00160
00161 if( !input_isKeyDown(KeyCodes::key_LSHIFT) && !input_isKeyDown(KeyCodes::key_RSHIFT) )
00162 cmd << "SetWaypoint "; else cmd << "AddWaypoint ";
00163 cmd << m_selected << " ";
00164 cmd << pt.x << " " << pt.y << " " << pt.z;
00165 }
00166
00167 net_send(cmd.str());
00168 }
00169 }
00170
00171 void BattleEntry::inputIssueJetsCommand()
00172 {
00173 Vec2D mousePos( input_getMouseX(),input_getMouseY() );
00174 int jSelected = m_selected - SELECT_JETS;
00175 if( jSelected<0 ) return;
00176
00177 ostringstream cmd;
00178 cmd << COMMAND_BATTLEMODE << " " << COMMIT_TIME << " " << (currGameTick+NETWORK_GAME_TICK_LAG)
00179 << " " << COMMAND_BATTLE_ID << " " << this->uniqueID();
00180 if( net_amIServer() ) cmd << " A "; else cmd << " B ";
00181
00182
00183 if( input_isKeyPressed(KeyCodes::key_S) || HUD::isButtonStopClicked() )
00184 {
00185 BattleJets &J = BS->jets[ jSelected ];
00186 if( J.getCurrentCommand()!=JetCommands::IDLE )
00187 {
00188 Sound::playRandomSound( Sound::CLASS_ACK );
00189 cmd << "JReturn " << jSelected;
00190 net_send(cmd.str());
00191 return;
00192 }
00193 }
00194
00195 if( input_isMouseRBClicked() )
00196 {
00197 BattleJets &J = BS->jets[ jSelected ];
00198 int targetGrp = getGroupAtPos2D(mousePos);
00199 if( targetGrp!=-1 && getTeamCode(targetGrp)!=getTeamCode(m_selected) )
00200 {
00201 if( J.isReadyForOrder() )
00202 {
00203 Sound::playRandomSound( Sound::CLASS_ACK );
00204 cmd << "JBomb " << jSelected << ' ' << targetGrp;
00205 net_send(cmd.str());
00206 }
00207 else if( J.getCurrentCommand()==JetCommands::BOMB )
00208 {
00209 Sound::playRandomSound( Sound::CLASS_ACK );
00210 cmd << "JAssignNewBomb " << jSelected << ' ' << targetGrp;
00211 net_send(cmd.str());
00212 }
00213 }
00214 else
00215 {
00216 Vec3D pt = math_translateScreenToWorldPlane( mousePos, 0.0f );
00217 pt.z += terrain_getHeight( BS->terrain, pt.x, pt.y ) + 5.0f;
00218
00219 if( J.isReadyForOrder() )
00220 {
00221 Sound::playRandomSound( Sound::CLASS_ACK );
00222 cmd << "JGuard " << jSelected << ' ' << pt.x << ' ' << pt.y << ' ' << pt.z;
00223 net_send(cmd.str());
00224 }
00225 else if( J.getCurrentCommand()==JetCommands::GUARD )
00226 {
00227 Sound::playRandomSound( Sound::CLASS_ACK );
00228 cmd << "JAssignNewGuard " << jSelected << ' ' << pt.x << ' ' << pt.y << ' ' << pt.z;
00229 net_send(cmd.str());
00230 }
00231 }
00232 }
00233 }
00234
00235 void BattleEntry::inputCtrlGroup()
00236 {
00237 if( input_isKeyDown(KeyCodes::key_LCONTROL) || input_isKeyDown(KeyCodes::key_RCONTROL) )
00238 {
00239 if( input_isKeyPressed(KeyCodes::key_0) ) m_ctrlGroup[0] = m_selected;
00240 if( input_isKeyPressed(KeyCodes::key_1) ) m_ctrlGroup[1] = m_selected;
00241 if( input_isKeyPressed(KeyCodes::key_2) ) m_ctrlGroup[2] = m_selected;
00242 if( input_isKeyPressed(KeyCodes::key_3) ) m_ctrlGroup[3] = m_selected;
00243 if( input_isKeyPressed(KeyCodes::key_4) ) m_ctrlGroup[4] = m_selected;
00244 if( input_isKeyPressed(KeyCodes::key_5) ) m_ctrlGroup[5] = m_selected;
00245 if( input_isKeyPressed(KeyCodes::key_6) ) m_ctrlGroup[6] = m_selected;
00246 if( input_isKeyPressed(KeyCodes::key_7) ) m_ctrlGroup[7] = m_selected;
00247 if( input_isKeyPressed(KeyCodes::key_8) ) m_ctrlGroup[8] = m_selected;
00248 if( input_isKeyPressed(KeyCodes::key_9) ) m_ctrlGroup[9] = m_selected;
00249 }
00250 else
00251 {
00252 if( input_isKeyPressed(KeyCodes::key_0) ) m_selected = m_ctrlGroup[0];
00253 if( input_isKeyPressed(KeyCodes::key_1) ) m_selected = m_ctrlGroup[1];
00254 if( input_isKeyPressed(KeyCodes::key_2) ) m_selected = m_ctrlGroup[2];
00255 if( input_isKeyPressed(KeyCodes::key_3) ) m_selected = m_ctrlGroup[3];
00256 if( input_isKeyPressed(KeyCodes::key_4) ) m_selected = m_ctrlGroup[4];
00257 if( input_isKeyPressed(KeyCodes::key_5) ) m_selected = m_ctrlGroup[5];
00258 if( input_isKeyPressed(KeyCodes::key_6) ) m_selected = m_ctrlGroup[6];
00259 if( input_isKeyPressed(KeyCodes::key_7) ) m_selected = m_ctrlGroup[7];
00260 if( input_isKeyPressed(KeyCodes::key_8) ) m_selected = m_ctrlGroup[8];
00261 if( input_isKeyPressed(KeyCodes::key_9) ) m_selected = m_ctrlGroup[9];
00262 }
00263 }
00264
00265 void BattleEntry::inputSelectByHotKey()
00266 {
00267 if( input_isKeyPressed(KeyCodes::key_N) )
00268 {
00269 int N = (int) BS->groups.size();
00270 m_selected = (m_selected+1) % N;
00271 Vec3D playerCol( GameState::consolePlayer()->color() );
00272 while( BS->groups[m_selected].m_color!=playerCol || BS->groups[m_selected].m_numUnits<=0 )
00273 m_selected = (m_selected+1) % N;
00274 }
00275
00276 if( input_isKeyPressed(KeyCodes::key_J) )
00277 {
00278 m_selected = -1;
00279 Vec3D playerCol( GameState::consolePlayer()->color() );
00280 for( int i=0; i<(int)BS->jets.size(); i++ )
00281 {
00282 BattleJets &J = BS->jets[i];
00283 if( J.m_color==playerCol && J.getNumJets()>0 && J.isReadyForOrder() )
00284 {
00285 m_selected = SELECT_JETS + i;
00286 return;
00287 }
00288 }
00289 }
00290 }
00291
00292 void BattleEntry::input()
00293 {
00294 if(HUD::processOverheadInput()) return;
00295
00296 Vec2D mousePos( input_getMouseX(), input_getMouseY() );
00297
00298
00299 m_camHeight = 10.0f + 130.0f * (1 - input_getMouseWheel());
00300 if( input_getMouseX()<0.02f ) m_camTarget.y += 2.5f;
00301 if( input_getMouseX()>0.98f ) m_camTarget.y -= 2.5f;
00302 if( input_getMouseY()<0.02f ) m_camTarget.x -= 2.5f;
00303 if( input_getMouseY()>0.98f ) m_camTarget.x += 2.5f;
00304 if( input_isKeyDown(KeyCodes::key_LEFT ) ) m_camTarget.y += 5.5f;
00305 if( input_isKeyDown(KeyCodes::key_RIGHT ) ) m_camTarget.y -= 5.5f;
00306 if( input_isKeyDown(KeyCodes::key_UP ) ) m_camTarget.x -= 5.5f;
00307 if( input_isKeyDown(KeyCodes::key_DOWN ) ) m_camTarget.x += 5.5f;
00308
00309
00310 while( math_distPlanar3D(m_camTarget,Vec3D(0,45,0)) > 290.0f )
00311 m_camTarget = m_camTarget * 0.99f;
00312 Vec3D camPos( m_camTarget+Vec3D(60.0f+m_camHeight*0.4f,0.0f,30.0f+m_camHeight) );
00313 while( math_dist3D(camPos,Vec3D(0,45,0)) > 380.0f )
00314 {
00315 m_camHeight *= 0.99f;
00316 m_camTarget = m_camTarget * 0.991f;
00317 camPos = m_camTarget+Vec3D(60.0f+m_camHeight*0.4f,0.0f,30.0f+m_camHeight);
00318 }
00319
00320 camera_set( camPos, m_camTarget, 0 );
00321
00322 if( input_isKeyPressed(KeyCodes::key_TAB) ) m_showBigMiniMap = !m_showBigMiniMap;
00323 Box toggleButton;
00324 if( m_showBigMiniMap ) toggleButton = Box( 0.835f, 0.05f, 0.85f, 0.065f );
00325 else toggleButton = Box( 0.135f, 0.65f, 0.15f, 0.665f );
00326 if( input_isMouseLBClicked() && toggleButton.isContain(mousePos) ) m_showBigMiniMap = !m_showBigMiniMap;
00327
00328 inputSelectGroups();
00329 inputSelectJets();
00330 inputCtrlGroup();
00331 inputSelectByHotKey();
00332
00333 if( m_selected >= 0 && m_selected<SELECT_JETS )
00334 if( BS->groups[m_selected].m_numUnits<=0 ) m_selected = -1;
00335 if( m_selected >= SELECT_JETS )
00336 if( BS->jets[m_selected-SELECT_JETS].getNumJets() <= 0 ) m_selected = -1;
00337
00338 inputIssueGroupCommand();
00339 inputIssueJetsCommand();
00340 }
00341
00342 void BattleEntry::enQueueInput( string cmd )
00343 {
00344 m_cmdQueue.push_back(cmd);
00345 }
00346
00347 void BattleEntry::processQueuedInput()
00348 {
00349 list<string>::iterator iter;
00350 for( iter=m_cmdQueue.begin(); iter!=m_cmdQueue.end(); )
00351 {
00352 string cmd = *iter;
00353 sys_console() << "BattleEntry::processQueuedInput:" << cmd << endl;
00354 istringstream strm(cmd.c_str());
00355 string msgType;
00356 string cmdType;
00357 char player;
00358 int tick;
00359 int groupNum, targetNum, jetNum;
00360 FLOAT x, y, z;
00361
00362 strm >> msgType;
00363 assert(msgType==COMMAND_BATTLEMODE);
00364 string strBattleID;
00365 string commitTime;
00366 int battleID;
00367 strm >> commitTime >> tick >> strBattleID >> battleID >> player;
00368
00369 if( tick > currGameTick ) { iter++; continue; }
00370
00371 assert(battleID == this->uniqueID() && "Invalid message");
00372 strm >> cmdType;
00373
00374 if(cmdType=="SetWaypoint")
00375 {
00376 strm >> groupNum >> x >> y >> z;
00377 BS->groups[groupNum].m_waypoints.resize(0);
00378 BS->groups[groupNum].addWaypoint(Vec3D(x,y,z));
00379 BS->groups[groupNum].m_primaryTarget = -1;
00380 }
00381 else if( cmdType=="AddWaypoint")
00382 {
00383 strm >> groupNum >> x >> y >> z;
00384 BS->groups[groupNum].addWaypoint(Vec3D(x,y,z));
00385 BS->groups[groupNum].m_primaryTarget = -1;
00386 }
00387 else if( cmdType=="AssignTarget" )
00388 {
00389 strm >> groupNum >> targetNum;
00390 BS->groups[groupNum].m_primaryTarget = targetNum;
00391 BS->groups[groupNum].m_isChasePrimaryTarget = false;
00392 BS->groups[groupNum].m_isStrictPrimaryTarget = true;
00393 }
00394 else if( cmdType=="Intercept" )
00395 {
00396 strm >> groupNum >> targetNum;
00397 BS->groups[groupNum].m_primaryTarget = targetNum;
00398 BS->groups[groupNum].m_isChasePrimaryTarget = true;
00399 BS->groups[groupNum].m_isStrictPrimaryTarget = true;
00400 }
00401 else if( cmdType=="JReturn" )
00402 {
00403 strm >> jetNum;
00404 BattleJets &J = BS->jets[jetNum];
00405 if( J.getCurrentCommand()!=JetCommands::IDLE )
00406 J.orderToReturn();
00407 }
00408 else if( cmdType=="JBomb" )
00409 {
00410 strm >> jetNum >> targetNum;
00411 BattleJets &J = BS->jets[jetNum];
00412 if( J.isReadyForOrder() )
00413 J.orderToBomb(targetNum);
00414 }
00415 else if( cmdType=="JAssignNewBomb" )
00416 {
00417 strm >> jetNum >> targetNum;
00418 BattleJets &J = BS->jets[jetNum];
00419 if( J.getCurrentCommand()==JetCommands::BOMB )
00420 J.assignNewBombingTarget(targetNum);
00421 }
00422 else if( cmdType=="JGuard" )
00423 {
00424 strm >> jetNum >> x >> y >> z;
00425 BattleJets &J = BS->jets[jetNum];
00426 if( J.isReadyForOrder() )
00427 J.orderToGuard(Vec3D(x,y,z));
00428 }
00429 else if( cmdType=="JAssignNewGuard" )
00430 {
00431 strm >> jetNum >> x >> y >> z;
00432 BattleJets &J = BS->jets[jetNum];
00433 if( J.getCurrentCommand()==JetCommands::GUARD )
00434 J.assignNewGuardTarget(Vec3D(x,y,z));
00435 }
00436 else if( cmdType=="Stop" )
00437 {
00438 strm >> groupNum;
00439 BattleGroup &G = BS->groups[groupNum];
00440 G.m_waypoints.resize(0);
00441 G.m_primaryTarget = -1;
00442 G.m_isChasePrimaryTarget = false;
00443 G.m_isStrictPrimaryTarget = false;
00444 }
00445 else
00446 {
00447 sys_console() << WARNING << "Invalid command" << endl;
00448 sys_flushConsole();
00449 }
00450
00451 iter++;
00452 m_cmdQueue.pop_front();
00453 }
00454 }
00455
00456 void BattleEntry::initBattle()
00457 {
00458 int offsetA = (int)teamAGroups.size()/2;
00459 int offsetB = (int)teamBGroups.size()/2;
00460 Vec3D offsetVecA = Vec3D( 100.0f, 100.0f, 0.0f );
00461 Vec3D offsetVecB = Vec3D(-100.0f,-100.0f, 0.0f );
00462 Vec3D scaleVecA = Vec3D( -35.0f, 35.0f, 0.0f );
00463 Vec3D scaleVecB = Vec3D( 35.0f, -35.0f, 0.0f );
00464 Vec3D dirA = Vec3D( 225.0f, 90.0f, 0.0f );
00465 Vec3D dirB = Vec3D( 45.0f, 90.0f, 0.0f );
00466
00467 for( int i=0; i<(int)teamAGroups.size(); i++ )
00468 {
00469 Vec3D pos = offsetVecA + (FLOAT)(i-offsetA)*scaleVecA;
00470 Int3Tuple &t = teamAGroups[i];
00471 if( t.a==UnitTypes::JET )
00472 BS->jets.push_back( BattleJets( t.b, m_teamAColor, 'A', t.c) );
00473 else
00474 BS->groups.push_back( BattleGroup( pos, dirA, 'A', m_teamAColor, t.a, t.b, t.c ) );
00475 }
00476
00477 for( int i=0; i<(int)teamBGroups.size(); i++ )
00478 {
00479 Vec3D pos = offsetVecB + (FLOAT)(i-offsetB)*scaleVecB;
00480 Int3Tuple &t = teamBGroups[i];
00481 if( t.a==UnitTypes::JET )
00482 BS->jets.push_back( BattleJets( t.b, m_teamBColor, 'B', t.c) );
00483 else
00484 BS->groups.push_back( BattleGroup( pos, dirB, 'B', m_teamBColor, t.a, t.b, t.c ) );
00485 }
00486
00487 BS->teamAColor = m_teamAColor;
00488 BS->teamBColor = m_teamBColor;
00489 BS->terrain = artWork->battleTerrains.at(0);
00490 sys_console() << "Battle Starts at tick = " << Globals::currGameTick << endl;
00491
00492
00493 for( UINT i=0; i<BS->groups.size(); i++ ) BS->groups[i].BS = BS;
00494 for( UINT i=0; i<BS->jets.size(); i++ ) BS->jets[i].BS = BS;
00495
00496 BS->synch_firstShotTick = -1;
00497 BS->synch_firstKillTick = -1;
00498
00499
00500 if( GameState::consolePlayer()->color()==m_teamAColor ) m_camTarget = offsetVecA;
00501 else m_camTarget = offsetVecB;
00502 }
00503
00504 void BattleEntry::updateGroups()
00505 {
00506
00507 for( UINT i=0; i<BS->groups.size(); i++ )
00508 {
00509 BS->groups[i].moveBattleGroup();
00510 BS->groups[i].moveUnits();
00511 BS->groups[i].acquireGroupTargets();
00512 BS->groups[i].fire();
00513 }
00514 }
00515
00516 void BattleEntry::updateJets()
00517 {
00518 for( UINT i=0; i<BS->jets.size(); i++ )
00519 BS->jets[i].update();
00520 }
00521
00522 void BattleEntry::updateBullets()
00523 {
00524
00525 FLOAT bulletSpeed[4] = { 1.2f, 2.1f, 1.6f, 2.9f };
00526 FLOAT bulletHeight[4] = { 22.0f, 10.0f, 30.0f, 8.0f };
00527 for( int i=0; i<BS->bulletCount; i++ )
00528 {
00529 BattleBullet &b = BS->bullets[i];
00530 b.m_time++;
00531 b.m_pos.x += bulletSpeed[b.m_type] * cosf( D3DXToRadian( b.m_dir[0] ));
00532 b.m_pos.y += bulletSpeed[b.m_type] * sinf( D3DXToRadian( b.m_dir[0] ));
00533
00534 Vec3D delta = b.m_target - b.m_pos;
00535 FLOAT dist = sqrtf( delta.x*delta.x + delta.y*delta.y);
00536 FLOAT zFactor = 0.5f-(dist/b.m_totalDist);
00537 if(zFactor<0) zFactor = -zFactor;
00538 b.m_pos.z = b.m_firingZ + sqrtf(0.5f-zFactor) * bulletHeight[b.m_type];
00539
00540 FLOAT deltaZ = b.m_target.z - b.m_firingZ;
00541 b.m_pos.z += deltaZ * (1.0f - dist/b.m_totalDist);
00542
00543 if( b.m_type==BulletTypes::ROCKET )
00544 BS->sprites.push_back( BattleSprite( b.m_pos, 15, BattleSpriteTypes::ROCKET_TRAIL ));
00545
00546 if( b.m_type==BulletTypes::ANTIAIR)
00547 BS->sprites.push_back( BattleSprite( b.m_pos, 15, BattleSpriteTypes::ROCKET_TRAIL ));
00548
00549
00550 if( dist < 3.0f )
00551 {
00552 if( b.m_targetGroup==-1 )
00553 {
00554 if( b.m_type==BulletTypes::ANTIAIR )
00555 {
00556 for( UINT i=0; i<BS->jets.size(); i++ )
00557 if( BS->jets[i].m_team!=b.m_team )
00558 BS->jets[i].takeHit(b);
00559 }
00560 else
00561 {
00562 for( UINT i=0; i<BS->groups.size(); i++ ) BS->groups[i].takeHit(b);
00563 }
00564 }
00565 else if( b.m_type!=BulletTypes::ANTIAIR )
00566 BS->groups[ b.m_targetGroup ].takeHit(b);
00567
00568 BS->bulletCount--;
00569 BS->bullets[i] = BS->bullets[BS->bulletCount];
00570 i--;
00571 }
00572 }
00573 }
00574
00575 void BattleEntry::updateParticles()
00576 {
00577 updateParticleList(BS->sprites);
00578 }
00579
00580 void BattleEntry::updateDebris()
00581 {
00582 for( list<BattleDebris>::iterator iter = BS->debris.begin(); iter!=BS->debris.end(); )
00583 {
00584 BattleDebris &d = (*iter);
00585 d.pos += d.speed;
00586 d.speed.z -= 0.003f;
00587 d.dir += d.dirSpeed;
00588
00589 if( sys_randInt()%5==0 ) BS->sprites.push_back( BattleSprite( d.pos, 20, BattleSpriteTypes::EXPLO_SMALL ));
00590 if( sys_randInt()%12==0 ) BS->sprites.push_back( BattleSprite( d.pos, 60, BattleSpriteTypes::SMOKE_SMALL ));
00591
00592 if( d.pos.z < terrain_getHeight(BS->terrain, d.pos.x, d.pos.y) )
00593 {
00594 list<BattleDebris>::iterator toDel(iter);
00595 iter++;
00596 BS->debris.erase(toDel);
00597 }
00598 else
00599 iter++;
00600 }
00601 }
00602
00603 void BattleEntry::update()
00604 {
00605 if(m_firstTime)
00606 {
00607 m_firstTime = false;
00608 initBattle();
00609 }
00610
00611 processQueuedInput();
00612
00613 updateGroups();
00614 updateJets();
00615 updateBullets();
00616 updateParticles();
00617 updateDebris();
00618
00619 BS->m_cvLastContact++;
00620 BS->m_cvLastStress++;
00621 BS->m_cvLastPanick++;
00622 }
00623
00624
00625 char BattleEntry::getTeamCode(int selected)
00626 {
00627 if( selected==-1 ) return '-';
00628 if( selected < SELECT_JETS ) return BS->groups[selected].m_team;
00629 else return BS->jets[selected-SELECT_JETS].m_team;
00630 }
00631
00632 int BattleEntry::getGroupAtPos2D( Vec2D pos )
00633 {
00634 int s = -1;
00635 for( UINT i=0; i<BS->groups.size(); i++ )
00636 if( BS->groups[i].m_numUnits > 0 )
00637 {
00638 if( BS->groups[i].isContain2DPoint(pos) )
00639 {
00640 s = i;
00641 break;
00642 }
00643 }
00644 return s;
00645 }
00646
00647 void BattleEntry::renderEnvironment()
00648 {
00649
00650 terrain_render( BS->terrain, artWork->terrainTexLow, artWork->terrainTexHigh,
00651 artWork->terrainTexNormal );
00652
00653
00654 mesh_render( artWork->skyMesh, Vec3D(0.0f,0.0f,0.0f), Vec3D(750.0f,750.0f,650.0f),
00655 Vec3D(0.0f,0.0f,0.0f), artWork->skyTexture );
00656
00657
00658 const Vec3D dir(0,0,0);
00659 const Vec3D treeSize(1.9f,1.9f,5.0f);
00660 const Vec3D rockSize(2.4f,2.4f,1.1f);
00661 const Vec4D shadowMult(1,1,1,0.25f);
00662 Vec3D shadowVec(0.0f,0.0f,1.1f);
00663
00664 for( int i=0; i<MAX_TREE; i++ )
00665 {
00666 mesh_render( artWork->envTreeMesh, artWork->trees[i], treeSize, dir, artWork->envTreeTex );
00667 sprite_add(artWork->shadow, BlendModes::MIX, artWork->trees[i]+shadowVec, shadowMult, 4.5f );
00668 }
00669 for( int i=0; i<MAX_ROCK; i++ )
00670 mesh_render( artWork->envRockMesh, artWork->rocks[i], rockSize, dir, artWork->envRockTex );
00671 }
00672
00673 void BattleEntry::renderGroups()
00674 {
00675 for( UINT i=0; i<BS->groups.size(); i++ ) BS->groups[i].render();
00676
00677 if( m_selected >= 0 && m_selected < SELECT_JETS )
00678 {
00679 BattleGroup &G = BS->groups[m_selected];
00680 Vec3D pt3D( G.m_pos );
00681 pt3D.z += terrain_getHeight(BS->terrain, pt3D.x, pt3D.y );
00682 Vec2D pt2D = math_translateWorldToScreen( pt3D );
00683 G.drawOutlineBox( G.m_color );
00684 }
00685 }
00686
00687 void BattleEntry::renderJets()
00688 {
00689 Vec3D playerColor( GameState::consolePlayer()->color() );
00690 FLOAT yOffset = 0.2f;
00691 for( UINT i=0; i<BS->jets.size(); i++ )
00692 {
00693 BS->jets[i].render();
00694
00695 if( BS->jets[i].m_color!=playerColor ) continue;
00696
00697 BS->jets[i].renderIcon(yOffset);
00698 if( i == m_selected - SELECT_JETS )
00699 {
00700 Vec2D pt1( 0.940f, yOffset );
00701 Vec2D pt2( 0.999f, yOffset+0.07f );
00702 overlay_rect( pt1, pt2, false, Colors::yellow );
00703 }
00704 yOffset += 0.08f;
00705 }
00706 }
00707
00708 void BattleEntry::renderBullets()
00709 {
00710 AMesh bulletMesh[4] = { artWork->battleShellMesh, artWork->battleRocketMesh,
00711 artWork->battleICBMMesh, artWork->battleRocketMesh };
00712 Vec3D bulletSize[4] = { Vec3D(2.0f,1.0f,1.0f), Vec3D(3.5f,1.5f,1.5f),
00713 Vec3D(6.0f,2.0f,2.0f), Vec3D(3.0f,1.2f,1.2f) };
00714 for( int i=0; i<BS->bulletCount; i++ )
00715 {
00716 BattleBullet &b = BS->bullets[i];
00717 mesh_render( bulletMesh[b.m_type], b.m_pos, bulletSize[b.m_type], b.m_dir, artWork->battleBulletTexture );
00718 }
00719 }
00720
00721 void BattleEntry::renderParticles()
00722 {
00723 renderParticleList(BS->sprites);
00724 }
00725
00726 void BattleEntry::renderDebris()
00727 {
00728 AMesh debrisMesh[2] = { artWork->debris1, artWork->debris2 };
00729 Vec3D debrisSize[2] = { Vec3D(2.5f,2.5f,2.5f), Vec3D(0.7f,1.9f,1.9f) };
00730 for( list<BattleDebris>::iterator iter = BS->debris.begin(); iter!=BS->debris.end(); iter++ )
00731 {
00732 mesh_render( debrisMesh[iter->type], iter->pos, debrisSize[iter->type],
00733 Vec3D(iter->dir,0,0), artWork->debrisTex );
00734 }
00735 }
00736
00737 void BattleEntry::renderInterface()
00738 {
00739 FLOAT mx = input_getMouseX(), my = input_getMouseY();
00740 Vec2D mousePos(mx,my);
00741 overlay_image( Globals::artWork->mouse, Vec2D(mx+0.025f,my+0.025f), Vec2D(0.05f,0.05f), BlendModes::ADD, 0.5f );
00742 overlay_image( Globals::artWork->mouse, Vec2D(mx+0.025f,my+0.025f), Vec2D(0.05f,0.05f), BlendModes::KEY, 1.0f, MOUSE_RENDER_PRIORITY );
00743
00744 if( m_selected != -1 )
00745 {
00746 int target = getGroupAtPos2D(mousePos);
00747 if( target!=-1 && getTeamCode(m_selected) != getTeamCode(target) )
00748 {
00749 BattleGroup &G = BS->groups[target];
00750 G.drawOutlineBox( G.m_color );
00751 }
00752 }
00753
00754
00755 if( m_selected != -1 && m_selected < SELECT_JETS )
00756 {
00757 Vec3D grpPos3D( BS->groups[m_selected].m_pos );
00758 Vec2D grpPos2D( math_translateWorldToScreen( grpPos3D ) );
00759 Vec3D targetPos3D( math_translateScreenToWorldPlane( mousePos, 0.0f ) );
00760 overlay_line( mousePos, grpPos2D, Colors::white, BlendModes::ADD, 0.25f );
00761 FLOAT dist = math_distPlanar3D( grpPos3D, targetPos3D ) * 0.04f;
00762 dist = ((int)(dist*100.0f)) / 100.0f;
00763 overlay_text() << dist << " miles";
00764 overlay_textOut( mousePos+Vec2D(0,0.05f) );
00765 }
00766 }
00767
00768 void BattleEntry::renderMiniMap()
00769 {
00770 Vec2D mousePos( input_getMouseX(), input_getMouseY() );
00771 FLOAT ptSize, lineLen;
00772 Vec2D UL, LR;
00773 if( m_showBigMiniMap )
00774 {
00775 UL = Vec2D( 0.15f, 0.05f );
00776 LR = Vec2D( 0.85f, 0.75f );
00777 ptSize = 4.0f;
00778 lineLen = 0.060f;
00779 }
00780 else
00781 {
00782 UL = Vec2D( 0.0f, 0.65f );
00783 LR = Vec2D( 0.15f, 0.8f );
00784 ptSize = 1.0f;
00785 lineLen = 0.015f;
00786 }
00787 Vec2D UR( LR.x, UL.y );
00788
00789 overlay_rect( UL, LR, true, Colors::gray, BlendModes::MIX, 0.3f, 2 );
00790 overlay_rect( UR, UR+Vec2D(-0.015f,0.015f), true, Colors::gray, BlendModes::MIX, 0.4f, 3 );
00791 overlay_rect( UR, UR+Vec2D(-0.015f,0.015f), false, Colors::gray, BlendModes::MIX, 0.6f, 3 );
00792
00793
00794 Icon toggleIcon;
00795 if( m_showBigMiniMap ) toggleIcon = Icon( Vec2D(0.835f,0.05f), Vec2D(0.85f,0.065f), NULL );
00796 else toggleIcon = Icon( Vec2D(0.135f,0.65f), Vec2D(0.15f,0.665f), NULL );
00797 toggleIcon.setHelpString("Toggle Battle MiniMap\nHotKey[Tab]");
00798 if( toggleIcon.contains(mousePos) ) toggleIcon.drawOutline(Colors::yellow);
00799 toggleIcon.drawHelpString();
00800
00801 for( int i=0; i<(int)BS->groups.size(); i++ )
00802 {
00803 BattleGroup &G = BS->groups[i];
00804 if( G.m_numUnits<=0 ) continue;
00805 Vec2D pos2D( G.m_pos.x, G.m_pos.y );
00806 Vec2D pt( pos2D*(1.0f/800.0f) + Vec2D(0.5f,0.5f) );
00807 swap( pt.x, pt.y );
00808 pt.x = 1.0f - pt.x;
00809 pt.x = pt.x * ( LR.x - UL.x );
00810 pt.y = pt.y * ( LR.y - UL.y );
00811 pt += UL;
00812 overlay_point( pt, ptSize, G.m_color, BlendModes::MIX, 0.7f, 4 );
00813
00814 FLOAT theta = D3DXToRadian( G.m_dir.x );
00815 Vec2D dirVec( lineLen*(-sinf(theta)), lineLen*cosf(theta) );
00816 overlay_line( pt, pt+dirVec, G.m_color, BlendModes::MIX, 0.7f, 5 );
00817
00818 if( m_showBigMiniMap )
00819 {
00820 char letters[4] = { 'L','H','R','A' };
00821 overlay_text() << G.m_numUnits << letters[G.m_unitType];
00822 overlay_textOut( pt+Vec2D(-0.001f,-0.001f), 0.6f, Colors::white, NULL, 5 );
00823 }
00824 }
00825
00826 for( int i=0; i<(int)BS->jets.size(); i++ )
00827 {
00828 BattleJets &G = BS->jets[i];
00829 if( G.getNumJets()<=0 ) continue;
00830 Vec2D pos2D( G.m_pos.x, G.m_pos.y );
00831 if( !Box(-400.0f,-400.0f,400.0f,400.0f).isContain(pos2D) ) continue;
00832 Vec2D pt( pos2D*(1.0f/800.0f) + Vec2D(0.5f,0.5f) );
00833 swap( pt.x, pt.y );
00834 pt.x = 1.0f - pt.x;
00835 pt.x = pt.x * ( LR.x - UL.x );
00836 pt.y = pt.y * ( LR.y - UL.y );
00837 pt += UL;
00838 overlay_point( pt, ptSize, G.m_color, BlendModes::MIX, 0.7f, 4 );
00839
00840 FLOAT theta = D3DXToRadian( G.m_dir.x );
00841 Vec2D dirVec( lineLen*(-sinf(theta)), lineLen*cosf(theta) );
00842 overlay_line( pt, pt+dirVec, G.m_color, BlendModes::MIX, 0.7f, 5 );
00843
00844 if( m_showBigMiniMap )
00845 {
00846 overlay_text() << G.getNumJets() << 'J';
00847 overlay_textOut( pt+Vec2D(-0.001f,-0.001f), 0.6f, Colors::white, NULL, 5 );
00848 }
00849 }
00850 }
00851
00852 void BattleEntry::render()
00853 {
00854 if(m_firstTime) return;
00855
00856 flare_begin( artWork->flareTexture, 512 );
00857
00858 renderEnvironment();
00859 renderGroups();
00860 renderJets();
00861 renderBullets();
00862 renderParticles();
00863 renderDebris();
00864 renderInterface();
00865 renderMiniMap();
00866
00867 overlay_text() << "Battle Mode";
00868 overlay_textOut( Vec2D(0.3f,0.01f), 3.0f, Colors::red );
00869
00870 flare_end();
00871 }
00872
00873 bool BattleEntry::isBattleFinished()
00874 {
00875 if(m_winner!='?') return true;
00876
00877
00878 int countA, countB;
00879 countA = countB = 0;
00880 for( UINT i=0; i<BS->groups.size(); i++ )
00881 {
00882 BattleGroup &g = BS->groups[i];
00883 if( g.m_team=='A' ) countA += g.m_numUnits;
00884 else countB += g.m_numUnits;
00885 }
00886
00887 if( countA>0 && countB>0 ) return false;
00888
00889 HUD::clearSelections();
00890 if( countA==0 ) m_winner = 'B';
00891 else m_winner = 'A';
00892
00893 teamAGroups.resize(0);
00894 teamBGroups.resize(0);
00895 for( UINT i=0; i<BS->groups.size(); i++ )
00896 {
00897 BattleGroup &g = BS->groups[i];
00898 if( g.m_numUnits > 0 )
00899 if( g.m_team=='A' ) teamAGroups.push_back( Int3Tuple( g.m_unitType, g.m_numUnits, g.m_overheadForceNumber ) );
00900 else teamBGroups.push_back( Int3Tuple( g.m_unitType, g.m_numUnits, g.m_overheadForceNumber ) );
00901 }
00902
00903
00904 for( UINT i=0; i<BS->jets.size(); i++ )
00905 {
00906 BattleJets &J = BS->jets[i];
00907 if( J.getNumJets()>0 )
00908 if( J.m_team=='A' ) teamAGroups.push_back( Int3Tuple( UnitTypes::JET, J.getNumJets(), J.m_cityID ) );
00909 else teamBGroups.push_back( Int3Tuple( UnitTypes::JET, J.getNumJets(), J.m_cityID ) );
00910 }
00911
00912 sys_console() << "Battle Finishes at tick = " << Globals::currGameTick << endl;
00913
00914 return true;
00915 }
00916
00917 vector<Int3Tuple> BattleEntry::getSurvivors()
00918 {
00919 if( teamAGroups.size()==0 ) return teamBGroups;
00920 else return teamAGroups;
00921 }
00922
00923 Vec3D BattleEntry::getWinningTeamColor()
00924 {
00925 if(m_winner=='B')
00926 return m_teamBColor;
00927 else
00928 return m_teamAColor;
00929 }
00930
00931 Vec2D BattleEntry::getBattleLocation()
00932 {
00933 return m_location;
00934 }
00935
00936 bool BattleEntry::isFoughtBy(const Vec3D& teamColor) const
00937 {
00938 return teamColor == m_teamAColor || teamColor == m_teamBColor;
00939 }
00940
00941 int BattleEntry::countGroupsBelongingToPlayer( Vec3D playerColor )
00942 {
00943 int count = 0;
00944 for( UINT i=0; i<BS->groups.size(); i++ )
00945 {
00946 BattleGroup &G = BS->groups[i];
00947 if( G.m_color == playerColor && G.m_numUnits > 0 )
00948 count++;
00949 }
00950 for( UINT i=0; i<BS->jets.size(); i++ )
00951 {
00952 BattleJets &G = BS->jets[i];
00953 if( G.m_color == playerColor && G.getNumJets() > 0 )
00954 count++;
00955 }
00956 return count;
00957 }
00958
00959 bool BattleEntry::maySendGroundReinforcement( Vec3D playerColor )
00960 {
00961
00962
00963
00964 int count = 0;
00965 for( UINT i=0; i<BS->groups.size(); i++ )
00966 {
00967 BattleGroup &G = BS->groups[i];
00968 if( G.m_color == playerColor && G.m_numUnits > 0 )
00969 count++;
00970 }
00971
00972 if( count >= 8 ) return false;
00973 return true;
00974 }
00975
00976 bool BattleEntry::maySendAirReinforcement( Vec3D playerColor )
00977 {
00978
00979
00980
00981 int count = 0;
00982 for( UINT i=0; i<BS->jets.size(); i++ )
00983 {
00984 BattleJets &G = BS->jets[i];
00985 if( G.m_color == playerColor && G.getNumJets() > 0 )
00986 count++;
00987 }
00988
00989 if( count >= 4 ) return false;
00990 return true;
00991 }
00992
00993 void BattleEntry::sendGroundReinforcement( Vec3D playerColor, int unitType, int numUnits, int forceNum )
00994 {
00995 char team;
00996 Vec3D pos, dir;
00997
00998 int offsetA = 4;
00999 int offsetB = 4;
01000 Vec3D offsetVecA = Vec3D( 130.0f, 130.0f, 0.0f );
01001 Vec3D offsetVecB = Vec3D(-130.0f,-130.0f, 0.0f );
01002 Vec3D scaleVecA = Vec3D( -32.0f, 32.0f, 0.0f );
01003 Vec3D scaleVecB = Vec3D( 32.0f, -32.0f, 0.0f );
01004 Vec3D dirA = Vec3D( 225.0f, 90.0f, 0.0f );
01005 Vec3D dirB = Vec3D( 45.0f, 90.0f, 0.0f );
01006
01007 if( playerColor==m_teamAColor )
01008 {
01009 team = 'A';
01010 pos = offsetVecA + (FLOAT)(m_rfmCalledA-offsetA)*scaleVecA;
01011 dir = dirA;
01012 m_rfmCalledA = (m_rfmCalledA + 1) % 8;
01013 }
01014 else
01015 {
01016 team = 'B';
01017 pos = offsetVecB + (FLOAT)(m_rfmCalledB-offsetB)*scaleVecB;
01018 dir = dirB;
01019 m_rfmCalledB = (m_rfmCalledB + 1) % 8;
01020 }
01021
01022 BS->groups.push_back( BattleGroup( pos, dir, team, playerColor, unitType, numUnits, forceNum ) );
01023 BS->groups[ BS->groups.size()-1 ].BS = BS;
01024 }
01025
01026 void BattleEntry::sendAirReinforcement( Vec3D playerColor, int numJets, int homeTown )
01027 {
01028 char team;
01029 if( playerColor==m_teamAColor )
01030 {
01031 m_rfmCalledA++;
01032 team = 'A';
01033 }
01034 else
01035 {
01036 m_rfmCalledB++;
01037 team = 'B';
01038 }
01039 BS->jets.push_back( BattleJets( numJets, playerColor, team, homeTown) );
01040 BS->jets[ BS->jets.size()-1 ].BS = BS;
01041 }
01042
01043 bool BattleEntry::contains(Vec3D pt)
01044 {
01045 Vec3D myLocation(this->getBattleLocation().x, this->getBattleLocation().y, BATTLE_CIRCLE_HEIGHT);
01046 if (math_dist3D(pt, myLocation) < BATTLE_RADIUS / 2.0f) return true;
01047 else return false;
01048 }
01049
01050 void BattleEntry::clearUnitSelection()
01051 {
01052 m_selected = -1;
01053 }
01054
01055