fsmission.cpp 163 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022
  1. /*-------------------------------------------------------------------------
  2. * FSMission.cpp
  3. *
  4. * FedSrv mission handling stuff
  5. *
  6. * Owner:
  7. *
  8. * Copyright 1986-1998 Microsoft Corporation, All Rights Reserved
  9. *-----------------------------------------------------------------------*/
  10. #include "pch.h"
  11. //CFSMission statics
  12. ListFSMission CFSMission::s_list;
  13. int CFSMission::s_iMissionID = 1; // just for igc--this too wil go away when we have single exe
  14. const DWORD CFSMission::c_sbtPlayer = 0x00000001;
  15. const DWORD CFSMission::c_sbtLeader = 0x00000002;
  16. const float c_flUpdateTimeInterval = 10.0f;
  17. static const char* sideNames[c_cSidesMax] =
  18. {
  19. "Team 1",
  20. "Team 2",
  21. "Team 3",
  22. "Team 4",
  23. "Team 5",
  24. "Team 6"
  25. };
  26. void CFSMission::InitSide(SideID sideID)
  27. {
  28. /* { "Crusaders",
  29. "Unity",
  30. "Survivors",
  31. "Protectorate",
  32. "Colossal Mining Corp",
  33. "Midnight Runners" };
  34. */
  35. assert(sideID >= 0 && sideID < c_cSidesMax);
  36. strcpy(m_misdef.rgszName[sideID], sideNames[sideID]);
  37. /*
  38. if (m_misdef.misparms.rgCivID[sideID] == NA)
  39. m_misdef.misparms.rgCivID[sideID] = PickNewCiv(sideID, m_misdef.rgCivID);
  40. assert (m_misdef.misparms.rgCivID[sideID] != NA);
  41. */
  42. assert (m_misdef.misparms.rgCivID[sideID] != NA);
  43. //m_misdef.rgCivID [sideID] = m_misdef.misparms.rgCivID[sideID];
  44. m_misdef.rgShipIDLeaders[sideID] = NA;
  45. m_misdef.rgcPlayers [sideID] = 0;
  46. m_misdef.rgfAutoAccept [sideID] = true;
  47. m_misdef.rgfReady [sideID] = false;
  48. m_misdef.rgfForceReady [sideID] = false;
  49. m_misdef.rgfActive [sideID] = true;
  50. m_rgMoney[sideID] = 0;
  51. }
  52. static int InitializeCivList(const CivilizationListIGC* pcivs,
  53. CivID civIDs[])
  54. {
  55. int n = 0;
  56. for (CivilizationLinkIGC* pcl = pcivs->first(); (pcl != NULL); pcl = pcl->next())
  57. {
  58. CivID civID = pcl->data()->GetObjectID();
  59. //Hack to prevent bonus civs from showing up
  60. //if (civID < 300)
  61. civIDs[n++] = civID;
  62. }
  63. return n;
  64. }
  65. CFSMission::CFSMission(
  66. const MissionParams& misparms,
  67. char * szDesc,
  68. IMissionSite * psiteMission,
  69. IIgcSite* psiteIGC,
  70. CAdditionalAGCParamData * paagcParamData,
  71. const char* pszStoryText
  72. ) :
  73. m_pMission(::CreateMission()), // IGC CreateMission
  74. m_psiteMission(psiteMission),
  75. m_psiteIGC(psiteIGC),
  76. m_fLobbyDirty(true),
  77. m_timeLastLobbyMissionInfo(Time::Now() - c_flUpdateTimeInterval), // fudge factor--want to
  78. m_nFrame(0),
  79. m_bShouldDelete(false),
  80. m_pszReason(NULL),
  81. m_flGameDuration(0.0f),
  82. m_pttbmNewSetting(NULL),
  83. m_pttbmAltered(NULL),
  84. m_strStoryText(pszStoryText)
  85. {
  86. // Init mission def
  87. ZeroMemory(&m_misdef, sizeof(m_misdef));
  88. BEGIN_PFM_CREATE_PREALLOC(g.fm, &m_misdef, S, MISSIONDEF)
  89. END_PFM_CREATE
  90. m_misdef.misparms = misparms;
  91. //m_misdef.misparms.fStartingMoney = 0.0f;
  92. //m_misdef.misparms.iGoalTerritoryPercentage = 75; //NYI
  93. //m_misdef.misparms.iRandomEncounters = 2; //NYI
  94. psiteMission->Create(this);
  95. assert(!g.strLobbyServer.IsEmpty() == !!g.fmLobby.IsConnected());
  96. // keep track of whether this is a lobbied and/or club game
  97. m_misdef.misparms.bLobbiedGame = g.fmLobby.IsConnected();
  98. #if !defined(ALLSRV_STANDALONE)
  99. m_misdef.misparms.bClubGame = true;
  100. strcpy(m_misdef.misparms.szIGCStaticFile, IGC_ENCRYPT_CORE_FILENAME);
  101. #else // !defined(ALLSRV_STANDALONE)
  102. m_misdef.misparms.bClubGame = false;
  103. strcpy(m_misdef.misparms.szIGCStaticFile, IGC_STATIC_CORE_FILENAME);
  104. // hardcode this cap in one more place to make it harder to work around.
  105. m_misdef.misparms.nTotalMaxPlayersPerGame = min(c_cMaxPlayersPerGame, misparms.nTotalMaxPlayersPerGame);
  106. #endif // !defined(ALLSRV_STANDALONE)
  107. // if this game is an auto-start game, set the start time appropriately
  108. if (m_misdef.misparms.bAutoRestart)
  109. m_misdef.misparms.timeStart = Time::Now() + m_misdef.misparms.fStartCountdown;
  110. // set Mission Name here too so that AGC Admin tool reports the params while mission stage is STAGE_NOTSTARTED
  111. m_pMission->SetMissionParams(&m_misdef.misparms);
  112. m_pMission->Initialize(g.timeNow, psiteIGC);
  113. m_pMission->SetPrivateData((DWORD) this); // so we can get back to a CFSMission from an ImissionIGC
  114. m_pgrpMission = g.fm.CreateGroup(m_misdef.misparms.strGameName);
  115. if (szDesc)
  116. {
  117. assert(lstrlen(szDesc) < sizeof(m_misdef.szDescription));
  118. lstrcpy(m_misdef.szDescription, szDesc);
  119. }
  120. else
  121. {
  122. // use the description in the mission params
  123. strncpy(m_misdef.szDescription, m_misdef.misparms.strGameName, sizeof(m_misdef.szDescription));
  124. m_misdef.szDescription[sizeof(m_misdef.szDescription)-1] = '\0';
  125. }
  126. m_misdef.iSideMissionOwner = NA; // will turn into the person who created this when they do a SideChange
  127. m_misdef.fAutoAcceptLeaders = true;
  128. m_misdef.fInProgress = false;
  129. m_misdef.stage = STAGE_NOTSTARTED;
  130. #if !defined(ALLSRV_STANDALONE)
  131. extern void DoDecrypt(int size, char* pdata);
  132. m_misdef.misparms.verIGCcore = LoadIGCStaticCore(m_misdef.misparms.szIGCStaticFile, m_pMission, false, DoDecrypt);
  133. #else // !defined(ALLSRV_STANDALONE)
  134. m_misdef.misparms.verIGCcore = LoadIGCStaticCore(m_misdef.misparms.szIGCStaticFile, m_pMission, false, NULL);
  135. #endif // !defined(ALLSRV_STANDALONE)
  136. m_misdef.dwCookie = NULL;
  137. const CivilizationListIGC* pcivs = m_pMission->GetCivilizations();
  138. const int cCivsMax = 20;
  139. assert (pcivs->n() < cCivsMax);
  140. //Get random initial civs for each side, minimizing duplication vs. pre-existing sides
  141. CivID civIDs[cCivsMax];
  142. int n = InitializeCivList(pcivs, civIDs);
  143. int iSide = 0;
  144. for (iSide = 0; iSide < c_cSidesMax; iSide++)
  145. {
  146. if (NA == m_misdef.misparms.rgCivID[iSide])
  147. {
  148. if (n == 1)
  149. {
  150. m_misdef.misparms.rgCivID[iSide] = civIDs[0];
  151. n = InitializeCivList(pcivs, civIDs);
  152. }
  153. else
  154. {
  155. int index = randomInt(0, --n);
  156. m_misdef.misparms.rgCivID[iSide] = civIDs[index];
  157. if (index != n)
  158. {
  159. civIDs[index] = civIDs[n];
  160. }
  161. }
  162. }
  163. InitSide(iSide);
  164. }
  165. // Build the mission
  166. m_pMission->SetMissionID(s_iMissionID++);
  167. char szRealTeams[] = ": All real teams";
  168. char szBuff[sizeof(m_misdef.misparms.strGameName) + sizeof(szRealTeams)];
  169. wsprintf(szBuff, "%s%s", m_misdef.misparms.strGameName, szRealTeams);
  170. m_pgrpSidesReal = g.fm.CreateGroup(szBuff);
  171. GetSystemTime(&m_stStartTime);
  172. m_pMission->UpdateSides(Time::Now(), &m_misdef.misparms, m_misdef.rgszName);
  173. s_list.last(this); // add us to the list of missions
  174. // handle initation list, if any
  175. m_nInvitationListID = m_misdef.misparms.nInvitationListID;
  176. if (RequiresInvitation())
  177. {
  178. // Invitations will NOT be added to the right right now, but will be added later, asynchronously after they come back from sql.
  179. GetInvitations(m_nInvitationListID, GetMissionID());
  180. }
  181. if (paagcParamData)
  182. {
  183. TechTreeBitMask ttbmBlank;
  184. ttbmBlank.ClearAll();
  185. for (iSide = 0; iSide < c_cSidesMax; iSide++)
  186. {
  187. // detect admin made changes
  188. if (ttbmBlank != paagcParamData->m_ttbmAltered[iSide])
  189. {
  190. MakeOverrideTechBits();
  191. m_pttbmAltered[iSide] = paagcParamData->m_ttbmAltered[iSide];
  192. m_pttbmNewSetting[iSide] = paagcParamData->m_ttbmNewSetting[iSide];
  193. }
  194. // detect admin made changes
  195. if(paagcParamData->m_szTeamName[iSide][0] != '\0')
  196. SetSideName(iSide, paagcParamData->m_szTeamName[iSide]);
  197. }
  198. }
  199. //
  200. // Fire off an AGC event that a game was created
  201. //
  202. LPCSTR pszGame = GetMissionDef()->misparms.strGameName;
  203. long idGame = GetIGCMission() ? GetIGCMission()->GetMissionID() : -1;
  204. LPCSTR pszContext = GetIGCMission() ? GetIGCMission()->GetContextName() : NULL;
  205. _AGCModule.TriggerContextEvent(NULL, EventID_GameCreated, pszContext,
  206. pszGame, idGame, -1, -1, 0);
  207. }
  208. CFSMission::~CFSMission()
  209. {
  210. //
  211. // Fire off an AGC event that a game was destroyed
  212. //
  213. LPCSTR pszGame = GetMissionDef()->misparms.strGameName;
  214. long idGame = GetIGCMission() ? GetIGCMission()->GetMissionID() : -1;
  215. LPCSTR pszContext = GetIGCMission() ? GetIGCMission()->GetContextName() : NULL;
  216. _AGCModule.TriggerContextEvent(NULL, EventID_GameDestroyed, pszContext,
  217. pszGame, idGame, -1, -1, 0);
  218. // Destroy any existing AGC game associated with the IGC mission
  219. GetAGCGlobal()->RemoveAGCObject(m_pMission, true);
  220. Vacate();
  221. // We don't want anymore mission related messages from any players still here
  222. ShipLinkIGC * pShiplink = m_pMission->GetShips()->first();
  223. while (pShiplink)
  224. {
  225. CFSShip * pfsShip = (CFSShip *) pShiplink->data()->GetPrivateData();
  226. pShiplink = pShiplink->next();
  227. assert (pfsShip->IsPlayer());
  228. RemovePlayerFromMission(pfsShip->GetPlayer(), QSR_ServerShutdown);
  229. }
  230. //
  231. // free all container elements
  232. //
  233. for (std::vector<CFSCluster*>::iterator i(m_pFSClusters.begin()); i != m_pFSClusters.end(); ++i)
  234. delete static_cast<CFSCluster*>(*i);
  235. for (std::vector<CFSSide*>::iterator _i(m_pFSSides.begin()); _i != m_pFSSides.end(); ++_i)
  236. delete static_cast<CFSSide*>(*_i);
  237. if(m_pttbmNewSetting)
  238. delete[] m_pttbmNewSetting;
  239. if(m_pttbmAltered)
  240. delete[] m_pttbmAltered;
  241. // And the mission is definitely over at this point, so the lobby and all clients should forget about it
  242. SetStage(STAGE_TERMINATE);
  243. g.fm.DeleteGroup(m_pgrpMission);
  244. if (g.fmLobby.IsConnected())
  245. {
  246. BEGIN_PFM_CREATE(g.fmLobby, pfmMissionGone, LS, MISSION_GONE)
  247. END_PFM_CREATE
  248. pfmMissionGone->dwCookie = GetCookie();
  249. g.fmLobby.SendMessages(g.fmLobby.GetServerConnection(), FM_GUARANTEED, FM_FLUSH);
  250. }
  251. // remove us from our list of missions
  252. for (LinkFSMission * plinkFSMis = s_list.first(); plinkFSMis; plinkFSMis = plinkFSMis->next())
  253. {
  254. CFSMission * pfsMission = plinkFSMis->data();
  255. if (this == pfsMission)
  256. {
  257. delete plinkFSMis;
  258. break;
  259. }
  260. }
  261. assert (plinkFSMis); // better have found it
  262. m_pMission->Terminate();
  263. delete m_pMission;
  264. m_psiteMission->Destroy(this);
  265. g.fm.DeleteGroup(m_pgrpSidesReal);
  266. #if defined(ALLSRV_STANDALONE)
  267. // Possibly shutdown the standalone server if no more games
  268. if (0 == s_list.n())
  269. {
  270. // Disconnect from the lobby server
  271. DisconnectFromLobby();
  272. g.strLobbyServer.SetEmpty();
  273. // Shutdown the server if no sessions exist
  274. if (0 == CAdminSession::GetSessionCount())
  275. PostThreadMessage(g.idReceiveThread, WM_QUIT, 0, 0);
  276. }
  277. #endif // defined(ALLSRV_STANDALONE)
  278. // kill any pending ballots
  279. while (!m_ballots.IsEmpty())
  280. delete m_ballots.PopFront();
  281. }
  282. /*-------------------------------------------------------------------------
  283. * AddPlayerToMission
  284. *-------------------------------------------------------------------------
  285. * Purpose:
  286. * Set a player on a mission in the team lobby.
  287. * This must be called INSTEAD OF IGC's SetSide() for the player's new side.
  288. */
  289. void CFSMission::AddPlayerToMission(CFSPlayer * pfsPlayer)
  290. {
  291. debugf("Player %s, ship=%d joined mission=%x\n",
  292. pfsPlayer->GetName(), pfsPlayer->GetShipID(),
  293. GetCookie());
  294. assert (pfsPlayer->GetPlayerScoreObject());
  295. OldPlayerLink* popl = GetOldPlayerLink(pfsPlayer->GetName());
  296. if (popl)
  297. {
  298. pfsPlayer->SetBannedSideMask(popl->data().bannedSideMask);
  299. PlayerScoreObject * ppso = &(popl->data().pso);
  300. pfsPlayer->GetIGCShip()->SetExperience(0.0f);
  301. pfsPlayer->GetIGCShip()->SetKills(ppso->GetKills());
  302. pfsPlayer->GetIGCShip()->SetDeaths(ppso->GetDeaths());
  303. pfsPlayer->GetIGCShip()->SetEjections(ppso->GetEjections());
  304. pfsPlayer->SetPlayerScoreObject(ppso);
  305. if (popl->data().pclusterLifepod)
  306. pfsPlayer->SetLifepod(popl->data().pclusterLifepod,
  307. popl->data().positionLifepod);
  308. //delete popl;
  309. }
  310. else
  311. {
  312. pfsPlayer->GetIGCShip()->SetKills(0);
  313. pfsPlayer->GetIGCShip()->SetDeaths(0);
  314. pfsPlayer->GetIGCShip()->SetEjections(0);
  315. }
  316. g.fm.AddConnectionToGroup(GetGroupMission(), pfsPlayer->GetConnection());
  317. SendLobbyMissionInfo(pfsPlayer);
  318. pfsPlayer->SetSide(this, GetIGCMission()->GetSide(SIDE_TEAMLOBBY));
  319. pfsPlayer->GetIGCShip()->CreateDamageTrack();
  320. SendPlayerInfo(NULL, pfsPlayer, this);
  321. assert(0 == g.fm.CbUsedSpaceInOutbox()); // everything should've been sent
  322. // tell the lobby that this character is now in this mission
  323. if (g.fmLobby.IsConnected())
  324. {
  325. assert(GetCookie()); // we can't be sending messages w/ cookies unless we have a real cookie
  326. BEGIN_PFM_CREATE(g.fmLobby, pfmPlayerJoined, S, PLAYER_JOINED)
  327. FM_VAR_PARM(pfsPlayer->GetName(), CB_ZTS)
  328. FM_VAR_PARM(pfsPlayer->GetCDKey(), CB_ZTS)
  329. END_PFM_CREATE
  330. pfmPlayerJoined->dwMissionCookie = GetCookie();
  331. g.fmLobby.SendMessages(g.fmLobby.GetServerConnection(), FM_GUARANTEED, FM_FLUSH);
  332. }
  333. m_bShouldDelete = false;
  334. SetLobbyIsDirty();
  335. }
  336. /*-------------------------------------------------------------------------
  337. * RemovePlayerFromMission
  338. *-------------------------------------------------------------------------
  339. * Purpose:
  340. * Removes a player in the team lobby from the mission.
  341. * This must be called INSTEAD OF IGC's SetSide() for the player's new side.
  342. */
  343. void CFSMission::RemovePlayerFromMission(CFSPlayer * pfsPlayer, QuitSideReason reason, const char* szMessageParam)
  344. {
  345. debugf("Player %s, ship=%d quiting mission=%x, reason=%d\n",
  346. pfsPlayer->GetName(), pfsPlayer->GetShipID(),
  347. GetCookie(), reason);
  348. // move them to the lobby first
  349. PlayerScoreObject* ppso = pfsPlayer->GetPlayerScoreObject();
  350. if (ppso->Connected())
  351. {
  352. pfsPlayer->GetPlayerScoreObject()->Disconnect(g.timeNow);
  353. }
  354. //Cache the player's old score object
  355. SaveAsOldPlayer(pfsPlayer, QSRIsBoot(reason));
  356. if (pfsPlayer->GetSide()->GetObjectID() != SIDE_TEAMLOBBY)
  357. RemovePlayerFromSide(pfsPlayer, reason, szMessageParam);
  358. // Since they're quiting, then they're not requesting a position anywhere anymore
  359. RemoveJoinRequest(pfsPlayer, NULL);
  360. BEGIN_PFM_CREATE(g.fm, pfmQuitMission, CS, QUIT_MISSION)
  361. FM_VAR_PARM(szMessageParam, CB_ZTS)
  362. END_PFM_CREATE
  363. pfmQuitMission->shipID = pfsPlayer->GetShipID();
  364. pfmQuitMission->reason = reason;
  365. g.fm.SendMessages(GetGroupMission(), FM_GUARANTEED, FM_FLUSH);
  366. // if they are quitting the mission, wait to remove them until we have sent
  367. // the side change.
  368. pfsPlayer->GetIGCShip()->DeleteDamageTrack();
  369. pfsPlayer->SetSide(NULL, NULL);
  370. // Let's just be super paranoid and make sure they're not in any of the mission's groups
  371. // TODO: Verify that they're already gone in debug builds
  372. g.fm.DeleteConnectionFromGroup(GetGroupMission(), pfsPlayer->GetConnection());
  373. g.fm.DeleteConnectionFromGroup(GetGroupLobbySide(), pfsPlayer->GetConnection());
  374. g.fm.DeleteConnectionFromGroup(GetGroupRealSides(), pfsPlayer->GetConnection());
  375. // tell the lobby that this character is no longer in this mission
  376. if (g.fmLobby.IsConnected() && GetCookie())
  377. {
  378. BEGIN_PFM_CREATE(g.fmLobby, pfmPlayerQuit, S, PLAYER_QUIT)
  379. FM_VAR_PARM(pfsPlayer->GetName(), CB_ZTS)
  380. END_PFM_CREATE
  381. pfmPlayerQuit->dwMissionCookie = GetCookie();
  382. g.fmLobby.SendMessages(g.fmLobby.GetServerConnection(), FM_GUARANTEED, FM_FLUSH);
  383. }
  384. if (!HasPlayers(NULL, true) && // no one left in the mission
  385. !m_misdef.misparms.bAllowEmptyTeams && // game doesn't allow empty sides
  386. m_misdef.misparms.bClubGame && // not a standalone server
  387. (IMPLIES( GetMissionDef()->misparms.bObjectModelCreated, GetStage() != STAGE_NOTSTARTED )) // not an admin created game that has started
  388. )
  389. {
  390. m_bShouldDelete = true;
  391. }
  392. else
  393. {
  394. SetLobbyIsDirty();
  395. }
  396. }
  397. /*-------------------------------------------------------------------------
  398. * AddPlayerToSide
  399. *-------------------------------------------------------------------------
  400. * Purpose:
  401. * Set a player from the team lobby to a side, to maintain team/mission leader status
  402. * This must be called INSTEAD OF IGC's SetSide() for the player's new side.
  403. *
  404. * Side Effects:
  405. * Maintain team/mission leader status
  406. */
  407. void CFSMission::AddPlayerToSide(CFSPlayer * pfsPlayer, IsideIGC * pside)
  408. {
  409. assert (pside);
  410. ShipID shipid = pfsPlayer->GetShipID();
  411. SideID sideid = pside->GetObjectID();
  412. bool fTeamLeader = false;
  413. bool fMissionOwner = false;
  414. ImissionIGC * pMission = GetIGCMission();
  415. assert (sideid != SIDE_TEAMLOBBY);
  416. CFSSide* pfsSide = CFSSide::FromIGC(pside);
  417. if (pfsPlayer->GetSide() == NULL || pfsPlayer->GetMission() != this)
  418. {
  419. assert(false);
  420. if (pfsPlayer->GetMission())
  421. RemovePlayerFromMission(pfsPlayer, QSR_Quit);
  422. pfsPlayer->GetMission()->AddPlayerToMission(pfsPlayer);
  423. }
  424. else if (pfsPlayer->GetSide()->GetObjectID() != SIDE_TEAMLOBBY)
  425. RemovePlayerFromSide(pfsPlayer, QSR_SwitchingSides);
  426. debugf("Player %s, ship=%d joined side %d, mission=%x\n",
  427. pfsPlayer->GetName(), pfsPlayer->GetShipID(),
  428. sideid, GetCookie());
  429. {
  430. OldPlayerLink* popl = GetOldPlayerLink(pfsPlayer->GetName());
  431. if (popl)
  432. {
  433. delete popl;
  434. }
  435. }
  436. // Since they're changing sides, then they're not requesting a position anywhere anymore
  437. RemoveJoinRequest(pfsPlayer, pside);
  438. // check to see if we need a mission leader...
  439. if (m_misdef.iSideMissionOwner < 0)
  440. fMissionOwner = true;
  441. else
  442. {
  443. IsideIGC * pside = pMission->GetSide(m_misdef.iSideMissionOwner);
  444. if (!HasPlayers(pside, true)) // no one on the mission leader's side
  445. fMissionOwner = true;
  446. }
  447. assert(pside->GetActiveF()); // shouldn't be joining an inactive team
  448. m_misdef.rgcPlayers[sideid]++;
  449. // Set their stuff appropriate for this side
  450. assert(pfsPlayer->GetMoney() == 0);
  451. pfsPlayer->GetIGCShip()->SetWingID(1);
  452. if (!HasPlayers(pside, true)) // we have a new team leader
  453. {
  454. fTeamLeader = true;
  455. assert (m_misdef.rgShipIDLeaders[sideid] == NA);
  456. SetLeaderID(sideid, shipid);
  457. if (fMissionOwner) // we have a new mission leader too
  458. {
  459. fMissionOwner = true;
  460. m_misdef.iSideMissionOwner = sideid;
  461. }
  462. }
  463. // announce the guy that we were called for. Make sure nothing got wacky
  464. assert(IMPLIES(fMissionOwner, fTeamLeader));
  465. assert(IFF(pside->GetActiveF(),
  466. (STAGE_NOTSTARTED == GetStage()) || (HasPlayers(pside, true) || m_misdef.misparms.bAllowEmptyTeams)));
  467. BEGIN_PFM_CREATE(g.fm, pfmJoinSide, S, JOIN_SIDE)
  468. END_PFM_CREATE
  469. pfmJoinSide->shipID = shipid;
  470. pfmJoinSide->sideID = sideid;
  471. if (fTeamLeader)
  472. {
  473. BEGIN_PFM_CREATE(g.fm, pfmSetTeamLeader, CS, SET_TEAM_LEADER)
  474. END_PFM_CREATE
  475. pfmSetTeamLeader->shipID = shipid;
  476. pfmSetTeamLeader->sideID = sideid;
  477. // put them on the command wing
  478. pfsPlayer->GetIGCShip()->SetWingID(0);
  479. }
  480. if (fMissionOwner)
  481. {
  482. BEGIN_PFM_CREATE(g.fm, pfmSetMissionOwner, CS, SET_MISSION_OWNER)
  483. END_PFM_CREATE
  484. pfmSetMissionOwner->shipID = shipid;
  485. pfmSetMissionOwner->sideID = sideid;
  486. }
  487. g.fm.SendMessages(GetGroupMission(), FM_GUARANTEED, FM_FLUSH);
  488. pfsPlayer->SetTreasureObjectID(NA); //NYI shouldn't we also set money here?
  489. pfsPlayer->SetSide(this, pside);
  490. pfsPlayer->SetReady(true);
  491. // set them on their starting wing
  492. BEGIN_PFM_CREATE(g.fm, pfmSetWingID, CS, SET_WINGID)
  493. END_PFM_CREATE
  494. pfmSetWingID->wingID = pfsPlayer->GetIGCShip()->GetWingID();
  495. pfmSetWingID->shipID = shipid;
  496. pfmSetWingID->bCommanded = true;
  497. g.fm.SendMessages(pfsSide->GetGroup(), FM_GUARANTEED, FM_FLUSH);
  498. if (fTeamLeader)
  499. {
  500. if (m_misdef.misparms.bLockTeamSettings)
  501. {
  502. // if this is a squad game, set the squad to the invited squad.
  503. if (m_misdef.misparms.bSquadGame)
  504. {
  505. const char* pzSquadName = CFSSide::FromIGC(pside)->GetInvitedSquadName();
  506. if(pzSquadName)
  507. {
  508. SquadMembershipLink* pSquadLink = pfsPlayer->GetSquadMembershipList()->first();
  509. for (; pSquadLink; pSquadLink = pSquadLink->next())
  510. {
  511. if (_stricmp(pSquadLink->data()->GetName(), pzSquadName) == 0)
  512. {
  513. SetSideSquad(sideid, pSquadLink->data()->GetID());
  514. break;
  515. }
  516. }
  517. }
  518. }
  519. }
  520. // set the squad if this is a squad game
  521. else if (m_misdef.misparms.bSquadGame)
  522. SetSideSquad(sideid, pfsPlayer->GetPreferredSquadToLead());
  523. // otherwise reset the team name
  524. else
  525. SetSideName(sideid, sideNames[sideid]);
  526. }
  527. if (STAGE_STARTED != GetStage() && STAGE_STARTING != GetStage())
  528. {
  529. g.fm.SetDefaultRecipient((CFMRecipient*) pfsPlayer->GetConnection(),
  530. FM_GUARANTEED);
  531. //Tell the player wing ID for every other person on the side
  532. for (ShipLinkIGC* psl = pside->GetShips()->first(); (psl != NULL); psl = psl->next())
  533. {
  534. IshipIGC* ps = psl->data();
  535. if (ps != pfsPlayer->GetIGCShip())
  536. {
  537. ShipID shipID = ps->GetObjectID();
  538. BEGIN_PFM_CREATE(g.fm, pfmSetWingID, CS, SET_WINGID)
  539. END_PFM_CREATE
  540. pfmSetWingID->wingID = ps->GetWingID();
  541. pfmSetWingID->shipID = shipID;
  542. pfmSetWingID->bCommanded = true;
  543. }
  544. }
  545. g.fm.SendMessages(NULL, FM_GUARANTEED, FM_FLUSH); // default recipient
  546. if (STAGE_NOTSTARTED == GetStage())
  547. CheckForSideAllReady(pside);
  548. }
  549. else
  550. {
  551. // set them to auto-donate to the team leader
  552. CFSPlayer* pfsLeader = GetLeader(pside->GetObjectID());
  553. pfsPlayer->SetAutoDonate((pfsPlayer != pfsLeader) ? pfsLeader : NULL, 0, true);
  554. // tell them their starting sector so they can display the mission briefing
  555. bool bGenerateCivBriefing = m_strStoryText.IsEmpty();
  556. const char* szBriefingText = bGenerateCivBriefing
  557. ? GetBase(pside)->GetCluster()->GetName() : m_strStoryText;
  558. BEGIN_PFM_CREATE(g.fm, pfmSetBriefingText, S, SET_BRIEFING_TEXT)
  559. FM_VAR_PARM(szBriefingText, CB_ZTS)
  560. END_PFM_CREATE
  561. pfmSetBriefingText->fGenerateCivBriefing = bGenerateCivBriefing;
  562. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  563. // tell them about the world
  564. SendMissionInfo(pfsPlayer, pside);
  565. // tell them about the players who have flags
  566. {
  567. const ShipListIGC* pships = m_pMission->GetShips();
  568. for (ShipLinkIGC* psl = pships->first(); (psl != NULL); psl = psl->next())
  569. {
  570. IshipIGC* pship = psl->data();
  571. SideID sidFlag = pship->GetFlag();
  572. if (sidFlag != NA)
  573. {
  574. BEGIN_PFM_CREATE(g.fm, pfmGain, S, GAIN_FLAG)
  575. END_PFM_CREATE
  576. pfmGain->sideidFlag = sidFlag;
  577. pfmGain->shipidRecipient = pship->GetObjectID();
  578. }
  579. }
  580. }
  581. assert (pfsPlayer->GetIGCShip()->GetDeaths() == pfsPlayer->GetPlayerScoreObject()->GetDeaths());
  582. assert (pfsPlayer->GetIGCShip()->GetEjections() == pfsPlayer->GetPlayerScoreObject()->GetEjections());
  583. assert (pfsPlayer->GetIGCShip()->GetChildShips()->n() == 0);
  584. if (pfsPlayer->GetLifepodCluster() && (STAGE_STARTED == GetStage()))
  585. {
  586. //Flush the send buffer to clear out the flag information.
  587. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  588. //Oopsie ... joined in their underwear ... make em' walk home
  589. IhullTypeIGC* pht = pfsPlayer->GetSide()->GetCivilization()->GetLifepod();
  590. pfsPlayer->GetIGCShip()->SetBaseHullType(pht);
  591. pfsPlayer->ShipStatusHullChange(pht);
  592. pfsPlayer->GetIGCShip()->SetPosition(pfsPlayer->GetLifepodPosition());
  593. pfsPlayer->GetIGCShip()->SetVelocity(Vector::GetZero());
  594. Orientation o;
  595. o.Reset();
  596. pfsPlayer->GetIGCShip()->SetOrientation(o);
  597. pfsPlayer->GetIGCShip()->SetCluster(pfsPlayer->GetLifepodCluster());
  598. pfsPlayer->SetLifepod(NULL, Vector::GetZero());
  599. }
  600. else
  601. {
  602. IstationIGC * pstation = GetBase(pside);
  603. pfsPlayer->GetIGCShip()->SetStation(pstation);
  604. pfsPlayer->ShipStatusStart(pstation);
  605. }
  606. if (STAGE_STARTED == GetStage())
  607. {
  608. pfsPlayer->GetPlayerScoreObject()->Connect(g.timeNow);
  609. // let them know that the game has started
  610. BEGIN_PFM_CREATE(g.fm, pfmEnterGame, S, ENTER_GAME)
  611. END_PFM_CREATE
  612. if (m_misdef.misparms.IsProsperityGame())
  613. {
  614. //Ship down the win the game bucket for all sides
  615. for (SideLinkIGC* psl = pMission->GetSides()->first(); (psl != NULL); psl = psl->next())
  616. {
  617. BucketLinkIGC* pbl = psl->data()->GetBuckets()->last();
  618. {
  619. assert (pbl != NULL);
  620. IbucketIGC* pbucket = pbl->data();
  621. assert ((pbucket->GetBucketType() == OT_development) &&
  622. (pbucket->GetBuyable()->GetObjectID() == c_didTeamMoney));
  623. BEGIN_PFM_CREATE(g.fm, pfmBucketStatus, S, BUCKET_STATUS)
  624. END_PFM_CREATE
  625. pfmBucketStatus->timeTotal = pbucket->GetTime();
  626. pfmBucketStatus->moneyTotal = pbucket->GetMoney();
  627. pfmBucketStatus->iBucket = pbucket->GetObjectID();
  628. pfmBucketStatus->sideID = psl->data()->GetObjectID();
  629. }
  630. }
  631. }
  632. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  633. }
  634. }
  635. }
  636. /*-------------------------------------------------------------------------
  637. * RemovePlayerFromSide
  638. *-------------------------------------------------------------------------
  639. * Purpose:
  640. * Removes a player from a side to the team lobby, to maintain team/mission leader status
  641. * This must be called INSTEAD OF IGC's SetSide() for the player's new side.
  642. *
  643. * Side Effects:
  644. * Maintain team/mission leader status
  645. */
  646. void CFSMission::RemovePlayerFromSide(CFSPlayer * pfsPlayer, QuitSideReason reason, const char* szMessageParam)
  647. {
  648. IsideIGC * psideOld = pfsPlayer->GetSide();
  649. SideID sideidOld = psideOld->GetObjectID();
  650. pfsPlayer->SetDPGroup(NULL, false);
  651. ShipID shipid = pfsPlayer->GetShipID();
  652. SquadID squadID = psideOld->GetSquadID();
  653. IshipIGC * pshipPlayer = pfsPlayer->GetIGCShip();
  654. ImissionIGC * pMission = GetIGCMission();
  655. bool bTurnOnAutoaccept = false;
  656. bool bWasTeamLeader = false;
  657. pfsPlayer->GetPlayerScoreObject()->Disconnect(g.timeNow);
  658. SideID sidFlag = pshipPlayer->GetFlag();
  659. if (sidFlag != NA)
  660. {
  661. const Vector& p = pfsPlayer->GetIGCShip()->GetPosition();
  662. float lm = m_pMission->GetFloatConstant(c_fcidLensMultiplier);
  663. float r = 1.5f * m_pMission->GetFloatConstant(c_fcidRadiusUniverse);
  664. if (p.x*p.x + p.y*p.y + p.z*p.z/(lm*lm) > r*r)
  665. {
  666. ((FedSrvSiteBase*)m_psiteIGC)->RespawnFlag(sidFlag);
  667. }
  668. else
  669. {
  670. DataTreasureIGC dt;
  671. dt.treasureCode = c_tcFlag;
  672. dt.treasureID = sidFlag;
  673. dt.amount = 0;
  674. dt.createNow = false;
  675. dt.lifespan = 3600.0f * 24.0f * 10.0f; //10 days
  676. CreateTreasure(g.timeNow, pfsPlayer->GetIGCShip(), &dt, pfsPlayer->GetIGCShip()->GetPosition(), 100.0f);
  677. }
  678. }
  679. debugf("Player %s, ship=%d quitting side %d, mission=%x\n",
  680. pfsPlayer->GetName(), pfsPlayer->GetShipID(),
  681. pfsPlayer->GetSide() ? pfsPlayer->GetSide()->GetObjectID() : NA,
  682. GetCookie());
  683. pfsPlayer->ShipStatusExit();
  684. //They are leaving their team ... promote
  685. {
  686. const ShipListIGC* pshipsChildren = pfsPlayer->GetIGCShip()->GetChildShips();
  687. if (pshipsChildren->first())
  688. {
  689. IclusterIGC* pcluster = pfsPlayer->GetIGCShip()->GetCluster();
  690. IshipIGC* pship = pshipsChildren->first()->data();
  691. pship->Promote();
  692. ((CFSShip*)(pship->GetPrivateData()))->ShipStatusRecalculate();
  693. BEGIN_PFM_CREATE(g.fm, pfmPromote, S, PROMOTE)
  694. END_PFM_CREATE
  695. pfmPromote->shipidPromoted = pship->GetObjectID();
  696. if (pcluster)
  697. g.fm.SendMessages(GetGroupSectorFlying(pcluster), FM_GUARANTEED, FM_FLUSH); //GetGroupRealSides(),
  698. else
  699. g.fm.SendMessages(CFSSide::FromIGC(psideOld)->GetGroup(), FM_GUARANTEED, FM_FLUSH); //GetGroupRealSides(),
  700. }
  701. }
  702. m_misdef.rgcPlayers[sideidOld]--;
  703. CFSPlayer* pfsNewLeader = NULL;
  704. if (m_misdef.rgShipIDLeaders[sideidOld] == shipid)
  705. {
  706. //The person leaving the team was the team leader ...
  707. bWasTeamLeader = true;
  708. SideID iSideNewOwner = sideidOld; // side of person getting promoted, which is same team if anyone left
  709. ShipID shipidNewOwner = NA;
  710. bool fMissionOwner = false;
  711. int cPlayers = GetCountOfPlayers(psideOld, true);
  712. if (cPlayers == 1)
  713. {
  714. //There was one player -- the soon to be gone team leader
  715. if (STAGE_NOTSTARTED == m_misdef.stage)
  716. {
  717. //the game hasn't started yet ... accept everyone on the request list
  718. BEGIN_PFM_CREATE(g.fm, pfmAutoAccept, CS, AUTO_ACCEPT)
  719. END_PFM_CREATE
  720. pfmAutoAccept->iSide = sideidOld;
  721. pfmAutoAccept->fAutoAccept = true;
  722. g.fm.SendMessages(GetGroupMission(), FM_GUARANTEED, FM_FLUSH);
  723. bTurnOnAutoaccept = true;
  724. }
  725. else
  726. {
  727. // otherwise, no one accepted them to the team so I suppose they've
  728. // been implicitly rejected.
  729. RejectSideJoinRequests(psideOld);
  730. }
  731. }
  732. if (cPlayers > 1) // Other people still on the side--side count not adjusted yet
  733. {
  734. const ShipListIGC * plistShip = psideOld->GetShips();
  735. if (STAGE_STARTED == m_misdef.stage)
  736. {
  737. //Leader left the game in the middle ... pick a leader based on who has the
  738. //most people donating to them
  739. IshipIGC* pshipNewLeader = PickNewLeader(plistShip, pshipPlayer, 0);
  740. if (pshipNewLeader)
  741. {
  742. pfsNewLeader = ((CFSShip*)(pshipNewLeader->GetPrivateData()))->GetPlayer();
  743. shipidNewOwner = pshipNewLeader->GetObjectID();
  744. }
  745. }
  746. if (pfsNewLeader == NULL)
  747. {
  748. // Look for someone else on the team to be team leader
  749. // (there must be one), favoring someone who can lead the current squad.
  750. for (ShipLinkIGC * plinkShip = plistShip->first(); plinkShip; plinkShip = plinkShip->next())
  751. {
  752. if (plinkShip->data() != pshipPlayer && ISPLAYER(plinkShip->data()))
  753. {
  754. CFSPlayer* pfsPlayerTemp = ((CFSShip*)(plinkShip->data()->GetPrivateData()))->GetPlayer();
  755. if (pfsPlayerTemp->GetCanLeadSquad(squadID))
  756. {
  757. pfsNewLeader = pfsPlayerTemp;
  758. shipidNewOwner = plinkShip->data()->GetObjectID();
  759. break;
  760. }
  761. else if (!pfsNewLeader)
  762. {
  763. pfsNewLeader = pfsPlayerTemp;
  764. shipidNewOwner = plinkShip->data()->GetObjectID();
  765. }
  766. }
  767. }
  768. assert(pfsNewLeader);
  769. }
  770. SetLeaderID(iSideNewOwner, shipidNewOwner);
  771. if (m_misdef.iSideMissionOwner == sideidOld)
  772. {
  773. fMissionOwner = true;
  774. }
  775. }
  776. else // no one left on the team
  777. {
  778. // Then it better be auto-accept again
  779. SetLeaderID(sideidOld, NA);
  780. // also, better set the squad to NA if the game hasn't started
  781. // (but leave the squad for scoring purposes if it has started)
  782. if (STAGE_NOTSTARTED == m_misdef.stage)
  783. SetSideSquad(sideidOld, NA);
  784. if (m_misdef.iSideMissionOwner == sideidOld)
  785. {
  786. m_misdef.iSideMissionOwner = NA;
  787. // Now gotta look for another side to be mission owner
  788. const SideListIGC * plistSide = m_pMission->GetSides();
  789. for (SideLinkIGC * plinkSide = plistSide->first(); plinkSide; plinkSide = plinkSide->next())
  790. {
  791. IsideIGC * pside = plinkSide->data();
  792. if ((psideOld != pside) && HasPlayers(pside, true)) // found one
  793. {
  794. iSideNewOwner = pside->GetObjectID();
  795. shipidNewOwner = GetLeader(iSideNewOwner)->GetShipID();
  796. fMissionOwner = true;
  797. assert(m_misdef.rgShipIDLeaders[iSideNewOwner] == shipidNewOwner);
  798. m_misdef.iSideMissionOwner = iSideNewOwner;
  799. break;
  800. }
  801. }
  802. // if we don't have a new mission owner, unlock the lobby and the sides
  803. if (m_misdef.iSideMissionOwner == NA)
  804. {
  805. if (m_misdef.misparms.bLockLobby)
  806. {
  807. SetLockLobby(false);
  808. BEGIN_PFM_CREATE(g.fm, pfmLockLobby, CS, LOCK_LOBBY)
  809. END_PFM_CREATE
  810. pfmLockLobby->fLock = false;
  811. }
  812. if (m_misdef.misparms.bLockSides)
  813. {
  814. SetLockSides(false);
  815. BEGIN_PFM_CREATE(g.fm, pfmLockSides, CS, LOCK_SIDES)
  816. END_PFM_CREATE
  817. pfmLockSides->fLock = false;
  818. }
  819. }
  820. // No one's left in this side
  821. SetLeaderID(sideidOld, NA);
  822. }
  823. }
  824. if (shipidNewOwner != NA) // somebody's getting promoted
  825. {
  826. // announce new leader
  827. BEGIN_PFM_CREATE(g.fm, pfmSetTeamLeader, CS, SET_TEAM_LEADER)
  828. END_PFM_CREATE
  829. pfmSetTeamLeader->shipID = shipidNewOwner;
  830. pfmSetTeamLeader->sideID = iSideNewOwner;
  831. if (fMissionOwner)
  832. {
  833. BEGIN_PFM_CREATE(g.fm, pfmSetMissionOwner, CS, SET_MISSION_OWNER)
  834. END_PFM_CREATE
  835. pfmSetMissionOwner->shipID = shipidNewOwner;
  836. pfmSetMissionOwner->sideID = iSideNewOwner;
  837. }
  838. }
  839. }
  840. g.fm.SendMessages(GetGroupMission(), FM_GUARANTEED, FM_FLUSH);
  841. Money payday;
  842. if (STAGE_STARTED == m_misdef.stage || STAGE_STARTING == m_misdef.stage)
  843. {
  844. //The player was on a side and the game was going
  845. if (pfsNewLeader && (pfsNewLeader->GetIGCShip()->GetAutoDonate() == pshipPlayer))
  846. pfsNewLeader->SetAutoDonate(NULL, 0, false);
  847. //Anyone who was autodonating to this player is no longer autodonating to this player
  848. for (ShipLinkIGC* psl = psideOld->GetShips()->first(); (psl != NULL); psl = psl->next())
  849. {
  850. if ((psl->data()->GetAutoDonate() == pshipPlayer) &&
  851. ((pfsNewLeader == NULL) || (psl->data() != pfsNewLeader->GetIGCShip())))
  852. {
  853. ((CFSShip*)(psl->data()->GetPrivateData()))->GetPlayer()->SetAutoDonate(pfsNewLeader, 0, false);
  854. }
  855. }
  856. g.fm.SendMessages(CFSSide::FromIGC(psideOld)->GetGroup(), FM_GUARANTEED, FM_FLUSH);
  857. payday = pfsPlayer->GetMoney() + pfsPlayer->GetIGCShip()->GetValue();
  858. assert(STAGE_STARTING != m_misdef.stage || payday == 0);
  859. }
  860. else
  861. payday = 0;
  862. pfsPlayer->SetMoney(0);
  863. // announce the guy that we were called for. Make sure nothing got wacky
  864. BEGIN_PFM_CREATE(g.fm, pfmSideChange, CS, QUIT_SIDE)
  865. FM_VAR_PARM(szMessageParam, CB_ZTS)
  866. END_PFM_CREATE
  867. pfmSideChange->shipID = shipid;
  868. pfmSideChange->reason = reason;
  869. g.fm.SendMessages(GetGroupMission(), FM_GUARANTEED, FM_FLUSH);
  870. pfsPlayer->Reset(false);
  871. if (STAGE_STARTED != m_misdef.stage)
  872. pfsPlayer->GetIGCShip()->SetExperience(1.0f);
  873. pfsPlayer->SetSide(this, pMission->GetSide(SIDE_TEAMLOBBY));
  874. pfsPlayer->SetReady(true);
  875. GiveSideMoney(psideOld, payday);
  876. bool fDeactivate = (STAGE_NOTSTARTED != m_misdef.stage) && !HasPlayers(psideOld, false);
  877. if (fDeactivate)
  878. DeactivateSide(psideOld); // deactivate the side _after_ the side change
  879. else
  880. CheckForSideAllReady(psideOld); // its ready state may have changed
  881. // if the game has not started and the leader quit, make sure the squad memberships work
  882. if (bWasTeamLeader && STAGE_NOTSTARTED == m_misdef.stage && squadID != NA)
  883. MaintainSquadLeadership(sideidOld);
  884. if (bTurnOnAutoaccept)
  885. SetAutoAccept(psideOld, true);
  886. if (QSRIsBoot(reason) && (sideidOld >= 0))
  887. {
  888. unsigned char bannedSideMask = pfsPlayer->GetBannedSideMask() | SideMask(sideidOld);
  889. pfsPlayer->SetBannedSideMask(bannedSideMask);
  890. }
  891. }
  892. void CFSMission::AddInvitation(SideID sid, char * szPlayerName)
  893. {
  894. if(sid >= c_cSidesMax)
  895. return;
  896. m_pFSSides[sid]->AddInvitation(szPlayerName);
  897. }
  898. /*-------------------------------------------------------------------------
  899. * IsInvited
  900. *-------------------------------------------------------------------------
  901. */
  902. bool CFSMission::IsInvited(CFSPlayer * pPlayer)
  903. {
  904. assert(RequiresInvitation());
  905. for (std::vector<CFSSide*>::iterator _i(m_pFSSides.begin()); _i != m_pFSSides.end(); ++_i)
  906. {
  907. if(static_cast<CFSSide*>(*_i)->IsInvited(pPlayer))
  908. return true;
  909. }
  910. return false;
  911. }
  912. /*-------------------------------------------------------------------------
  913. * AddBallot
  914. *-------------------------------------------------------------------------
  915. */
  916. void CFSMission::AddBallot(Ballot * pBallot)
  917. {
  918. m_ballots.PushEnd(pBallot);
  919. }
  920. /*-------------------------------------------------------------------------
  921. * TallyVote
  922. *-------------------------------------------------------------------------
  923. */
  924. void CFSMission::TallyVote(CFSPlayer* pfsPlayer, BallotID ballotID, bool bVote)
  925. {
  926. for (BallotList::Iterator iter(m_ballots); !iter.End(); iter.Next())
  927. {
  928. if (iter.Value()->GetBallotID() == ballotID)
  929. {
  930. iter.Value()->CastVote(pfsPlayer, bVote);
  931. break;
  932. }
  933. }
  934. }
  935. /*-------------------------------------------------------------------------
  936. * RemovePlayerByName
  937. *-------------------------------------------------------------------------
  938. * Purpose:
  939. * Removes all players with the given character name from this mission,
  940. * if they are a member.
  941. *
  942. * Parameters:
  943. * character name to boot and the reason for booting them.
  944. *
  945. * Returns:
  946. * true if a player was booted
  947. */
  948. bool CFSMission::RemovePlayerByName(const char* szCharacterName, QuitSideReason reason, const char* szMessageParam)
  949. {
  950. ShipLinkIGC * pShiplink = GetIGCMission()->GetShips()->first();
  951. while (pShiplink)
  952. {
  953. CFSShip * pfsShip = (CFSShip *) pShiplink->data()->GetPrivateData();
  954. if (pfsShip->IsPlayer())
  955. {
  956. CFSPlayer * pfsPlayer = pfsShip->GetPlayer();
  957. if (_stricmp(pfsPlayer->GetName(), szCharacterName) == 0)
  958. {
  959. RemovePlayerFromMission(pfsShip->GetPlayer(), reason, szMessageParam);
  960. debugf("Booted duplicate character %s from mission %x.\n",
  961. pfsPlayer->GetName(), GetCookie());
  962. break;
  963. }
  964. }
  965. pShiplink = pShiplink->next();
  966. }
  967. return pShiplink != NULL;
  968. }
  969. /*-------------------------------------------------------------------------
  970. * RemovePlayerByCDKey
  971. *-------------------------------------------------------------------------
  972. * Purpose:
  973. * Removes all players with the given CD Key from this mission,
  974. * if they are a member.
  975. *
  976. * Parameters:
  977. * character CD Key to boot and the reason for booting them.
  978. *
  979. * Returns:
  980. * true if a player was booted
  981. */
  982. bool CFSMission::RemovePlayerByCDKey(const char* szCDKey, QuitSideReason reason, const char* szMessageParam)
  983. {
  984. ShipLinkIGC * pShiplink = GetIGCMission()->GetShips()->first();
  985. while (pShiplink)
  986. {
  987. CFSShip * pfsShip = (CFSShip *) pShiplink->data()->GetPrivateData();
  988. if (pfsShip->IsPlayer())
  989. {
  990. CFSPlayer * pfsPlayer = pfsShip->GetPlayer();
  991. if (_stricmp(pfsPlayer->GetCDKey(), szCDKey) == 0)
  992. {
  993. RemovePlayerFromMission(pfsShip->GetPlayer(), reason, szMessageParam);
  994. debugf("Booted character %s with duplicate CD Key %s from mission %x.\n",
  995. pfsPlayer->GetName(), pfsPlayer->GetCDKey(), GetCookie());
  996. break;
  997. }
  998. }
  999. pShiplink = pShiplink->next();
  1000. }
  1001. return pShiplink != NULL;
  1002. }
  1003. /*-------------------------------------------------------------------------
  1004. * GetCountOfPlayers
  1005. *-------------------------------------------------------------------------
  1006. * Purpose:
  1007. * Count up number of human players on side, or mission (when pside==NULL)
  1008. *
  1009. * Parameters:
  1010. * side to check, NULL for whole mission
  1011. *
  1012. * Returns:
  1013. * count of players
  1014. */
  1015. int CFSMission::GetCountOfPlayers(IsideIGC * pside, bool bCountGhosts)
  1016. {
  1017. int cPlayers = 0;
  1018. const ShipListIGC * plistShip = pside ? pside->GetShips() : m_pMission->GetShips();
  1019. for (ShipLinkIGC * plinkShip = plistShip->first(); plinkShip; plinkShip = plinkShip->next())
  1020. {
  1021. if (ISPLAYER(plinkShip->data()) && (bCountGhosts || !plinkShip->data()->IsGhost()))
  1022. cPlayers++;
  1023. }
  1024. return cPlayers;
  1025. }
  1026. /*-------------------------------------------------------------------------
  1027. * HasPlayers
  1028. *-------------------------------------------------------------------------
  1029. * Purpose:
  1030. * Like GetCountOfPlayers, but just determines whether there's anyone on
  1031. * a side, so slightly faster
  1032. *
  1033. * Parameters:
  1034. * side, or NULL for mission
  1035. *
  1036. * Returns:
  1037. * whether the side has any players
  1038. */
  1039. bool CFSMission::HasPlayers(IsideIGC * pside, bool bCountGhosts)
  1040. {
  1041. const ShipListIGC * plistShip = pside ? pside->GetShips() : m_pMission->GetShips();
  1042. for (ShipLinkIGC * plinkShip = plistShip->first(); plinkShip; plinkShip = plinkShip->next())
  1043. {
  1044. if (ISPLAYER(plinkShip->data()) && (bCountGhosts || !plinkShip->data()->IsGhost()))
  1045. return true;
  1046. }
  1047. return false;
  1048. }
  1049. IstationIGC * CFSMission::GetBase(IsideIGC * pside)
  1050. {
  1051. if (pside)
  1052. {
  1053. for (StationLinkIGC* psl = pside->GetStations()->first(); (psl != NULL); psl = psl->next())
  1054. {
  1055. IstationIGC* pstation = psl->data();
  1056. if (pstation->GetStationType()->HasCapability(c_sabmRestart))
  1057. return pstation;
  1058. }
  1059. }
  1060. return NULL;
  1061. }
  1062. //
  1063. // This probably should be in CFSSide
  1064. //
  1065. CFSPlayer * CFSMission::GetLeader(SideID sid)
  1066. {
  1067. assert (NA != sid);
  1068. if (sid == SIDE_TEAMLOBBY) // lobby side never has a leader
  1069. return NULL;
  1070. ShipID shipid = m_misdef.rgShipIDLeaders[sid];
  1071. return NA == shipid ? NULL : CFSShip::GetShipFromID(shipid)->GetPlayer();
  1072. }
  1073. //
  1074. // This probably should be in CFSSide
  1075. //
  1076. void CFSMission::SetLeaderID(SideID sideID, ShipID shipID)
  1077. {
  1078. if (m_misdef.rgShipIDLeaders[sideID] != NA)
  1079. {
  1080. CFSShip* pfsShip = CFSShip::GetShipFromID(m_misdef.rgShipIDLeaders[sideID]);
  1081. assert (pfsShip);
  1082. assert (pfsShip->IsPlayer());
  1083. pfsShip->GetPlayerScoreObject()->StopCommand(m_pMission->GetLastUpdate());
  1084. }
  1085. m_misdef.rgShipIDLeaders[sideID] = shipID;
  1086. if (shipID != NA)
  1087. {
  1088. CFSShip* pfsShip = CFSShip::GetShipFromID(shipID);
  1089. assert (pfsShip);
  1090. assert (pfsShip->IsPlayer());
  1091. pfsShip->GetPlayerScoreObject()->StartCommand(m_pMission->GetLastUpdate());
  1092. }
  1093. }
  1094. void CFSMission::SetLeader(CFSPlayer * pfsPlayer)
  1095. {
  1096. assert(pfsPlayer);
  1097. SideID sid = pfsPlayer->GetSide()->GetObjectID();
  1098. ShipID shipId = pfsPlayer->GetShipID();
  1099. assert(0 <= sid && sid < m_misdef.misparms.nTeams);
  1100. CFSPlayer * pfsOldLeader = GetLeader(sid);
  1101. assert(pfsOldLeader);
  1102. // take the old leader off of the command wing
  1103. if (pfsOldLeader->GetIGCShip()->GetWingID() == 0)
  1104. {
  1105. pfsOldLeader->GetIGCShip()->SetWingID(1);
  1106. BEGIN_PFM_CREATE(g.fm, pfmSetWingID, CS, SET_WINGID)
  1107. END_PFM_CREATE
  1108. pfmSetWingID->wingID = 1;
  1109. pfmSetWingID->shipID = pfsOldLeader->GetShipID();
  1110. pfmSetWingID->bCommanded = true;
  1111. }
  1112. // put them on the command wing
  1113. pfsPlayer->GetIGCShip()->SetWingID(0);
  1114. BEGIN_PFM_CREATE(g.fm, pfmSetWingID, CS, SET_WINGID)
  1115. END_PFM_CREATE
  1116. pfmSetWingID->wingID = 0;
  1117. pfmSetWingID->shipID = shipId;
  1118. pfmSetWingID->bCommanded = true;
  1119. g.fm.SendMessages(CFSSide::FromIGC(pfsPlayer->GetSide())->GetGroup(), FM_GUARANTEED, FM_FLUSH);
  1120. // change leaders
  1121. SetLeaderID(sid, shipId);
  1122. BEGIN_PFM_CREATE(g.fm, pfmSetTeamLeader, CS, SET_TEAM_LEADER)
  1123. END_PFM_CREATE
  1124. pfmSetTeamLeader->shipID = shipId;
  1125. pfmSetTeamLeader->sideID = sid;
  1126. g.fm.SendMessages(GetGroupMission(), FM_GUARANTEED, FM_FLUSH);
  1127. }
  1128. /*-------------------------------------------------------------------------
  1129. * SetSideName
  1130. *-------------------------------------------------------------------------
  1131. * Purpose:
  1132. * Set the name of a side and notifies clients
  1133. *
  1134. * Parameters:
  1135. * The side whose name should changed and the new name
  1136. */
  1137. void CFSMission::SetSideName(SideID sid, const char* szName)
  1138. {
  1139. assert(sid >= 0 && sid < c_cSidesMax);
  1140. assert(strlen(szName) < c_cbName);
  1141. IsideIGC* pside = m_pMission->GetSide(sid);
  1142. if (!pside)
  1143. return;
  1144. ZString strNameOld(m_misdef.rgszName[sid]);
  1145. if (szName != m_misdef.rgszName[sid])
  1146. {
  1147. strncpy(m_misdef.rgszName[sid], szName, c_cbName - 1);
  1148. m_misdef.rgszName[sid][c_cbName-1] = '\0';
  1149. m_pMission->GetSide(sid)->SetName(m_misdef.rgszName[sid]);
  1150. }
  1151. // tell the clients about the new side info
  1152. BEGIN_PFM_CREATE(g.fm, pfmSetTeamInfo, CS, SET_TEAM_INFO)
  1153. END_PFM_CREATE
  1154. pfmSetTeamInfo->sideID = sid;
  1155. pfmSetTeamInfo->squadID = pside->GetSquadID();
  1156. strcpy(pfmSetTeamInfo->SideName, m_misdef.rgszName[sid]);
  1157. g.fm.SendMessages(GetGroupMission(), FM_GUARANTEED, FM_FLUSH);
  1158. // Fire an AGCEvent to indicate the Team info change
  1159. LPCSTR pszGame = GetMissionDef()->misparms.strGameName;
  1160. long idGame = GetIGCMission() ? GetIGCMission()->GetMissionID() : -1;
  1161. long idTeam = GetIGCMission()->GetSide(sid)->GetUniqueID();
  1162. LPCSTR pszContext = GetIGCMission() ? GetIGCMission()->GetContextName() : NULL;
  1163. _AGCModule.TriggerContextEvent(NULL, EventID_TeamInfoChange, pszContext,
  1164. strNameOld, idTeam, -1, -1, 3,
  1165. "GameID" , VT_I4 , idGame,
  1166. "GameName" , VT_LPSTR, pszGame,
  1167. "NewTeamName", VT_LPSTR, szName);
  1168. };
  1169. /*-------------------------------------------------------------------------
  1170. * SetSideSquad
  1171. *-------------------------------------------------------------------------
  1172. * Purpose:
  1173. * Set the squad for a side, booting players who are not on that squad,
  1174. * changing the team name, and notifing players of the team's new squad.
  1175. *
  1176. * Parameters:
  1177. * The side whose squad should changed and the id of the new squad
  1178. */
  1179. void CFSMission::SetSideSquad(SideID sid, SquadID squadID)
  1180. {
  1181. assert(sid >= 0 && sid < c_cSidesMax);
  1182. IsideIGC* pside = m_pMission->GetSide(sid);
  1183. // if the squad doesn't need to be changed, don't touch it.
  1184. if (squadID == pside->GetSquadID())
  1185. return;
  1186. pside->SetSquadID(squadID);
  1187. // loop through the players and boot anyone who is not a member of this squad
  1188. if (squadID != NA)
  1189. {
  1190. ShipLinkIGC* pslNext;
  1191. for (ShipLinkIGC* psl = pside->GetShips()->first(); (psl != NULL); psl = pslNext)
  1192. {
  1193. pslNext = psl->next();
  1194. CFSShip* pcs = (CFSShip *)(psl->data()->GetPrivateData());
  1195. if (pcs->IsPlayer())
  1196. {
  1197. CFSPlayer* pfsPlayer = pcs->GetPlayer();
  1198. if (!pfsPlayer->GetIsMemberOfSquad(squadID))
  1199. {
  1200. RemovePlayerFromSide(pfsPlayer, QSR_SquadChange);
  1201. }
  1202. }
  1203. }
  1204. }
  1205. const char* szSquadName = NULL;
  1206. if (m_misdef.misparms.bLockTeamSettings)
  1207. {
  1208. // don't change the name
  1209. szSquadName = m_misdef.rgszName[sid];
  1210. }
  1211. else
  1212. {
  1213. CFSPlayer* pfsLeader = GetLeader(sid);
  1214. if (squadID != NA)
  1215. {
  1216. assert(pfsLeader);
  1217. for (SquadMembershipLink* pSquadLink = pfsLeader->GetSquadMembershipList()->first();
  1218. pSquadLink != NULL; pSquadLink = pSquadLink->next())
  1219. {
  1220. if (pSquadLink->data()->GetID() == squadID)
  1221. szSquadName = pSquadLink->data()->GetName();
  1222. }
  1223. }
  1224. assert((squadID == NA) == (szSquadName == NULL));
  1225. }
  1226. // use SetSideName to send the new squadID along with the new name
  1227. SetSideName(sid, szSquadName ? szSquadName : sideNames[sid]);
  1228. SetLobbyIsDirty();
  1229. };
  1230. /*-------------------------------------------------------------------------
  1231. * MaintainSquadLeadership
  1232. *-------------------------------------------------------------------------
  1233. * Purpose:
  1234. * Makes sure the team has a squand and the current leader can lead the team
  1235. * with the current squad. If not, it tries to fix this first by assigning
  1236. * a new squad and possibly a new leader.
  1237. *
  1238. * Parameters:
  1239. * The side to check
  1240. */
  1241. void CFSMission::MaintainSquadLeadership(SideID sid)
  1242. {
  1243. IsideIGC* pside = m_pMission->GetSide(sid);
  1244. SquadID squadID = pside->GetSquadID();
  1245. CFSPlayer* pfsLeader = GetLeader(sid);
  1246. // don't try anything if the team settings are locked.
  1247. if (m_misdef.misparms.bLockTeamSettings)
  1248. return;
  1249. if (pfsLeader && (squadID == NA || !pfsLeader->GetCanLeadSquad(squadID)))
  1250. {
  1251. // see which squad the current leader would prefer to lead
  1252. squadID = pfsLeader->GetPreferredSquadToLead();
  1253. // failing that, try to find a new leader
  1254. if (squadID == NA)
  1255. {
  1256. ShipLinkIGC* pslNext;
  1257. for (ShipLinkIGC* psl = pside->GetShips()->first(); psl != NULL && squadID == NA; psl = pslNext)
  1258. {
  1259. pslNext = psl->next();
  1260. CFSShip* pcs = (CFSShip *)(psl->data()->GetPrivateData());
  1261. if (pcs->IsPlayer())
  1262. {
  1263. CFSPlayer* pfsPlayer = pcs->GetPlayer();
  1264. squadID = pfsPlayer->GetPreferredSquadToLead();
  1265. pfsLeader = pfsPlayer;
  1266. }
  1267. if (squadID != NA)
  1268. {
  1269. // switch to the new leader who is qualified to lead
  1270. SetLeader(pfsLeader);
  1271. }
  1272. }
  1273. }
  1274. // set the team to its new squad
  1275. SetSideSquad(sid, squadID);
  1276. // if we couldn't find a squad leader, boot everyone on the team
  1277. if (squadID == NA)
  1278. {
  1279. ShipLinkIGC* pslNext;
  1280. for (ShipLinkIGC* psl = pside->GetShips()->first(); psl != NULL && squadID == NA; psl = pslNext)
  1281. {
  1282. pslNext = psl->next();
  1283. CFSShip* pcs = (CFSShip *)(psl->data()->GetPrivateData());
  1284. if (pcs->IsPlayer())
  1285. RemovePlayerFromSide(pcs->GetPlayer(), QSR_SquadChange);
  1286. }
  1287. }
  1288. }
  1289. }
  1290. /*-------------------------------------------------------------------------
  1291. * SetStage
  1292. *-------------------------------------------------------------------------
  1293. * Purpose:
  1294. * Tell the mission what stage it's in, and tell the clients
  1295. *
  1296. * Parameters:
  1297. * new stage
  1298. */
  1299. void CFSMission::SetStage(STAGE stage)
  1300. {
  1301. if (m_misdef.stage != stage)
  1302. {
  1303. assert(m_misdef.stage < stage || stage == STAGE_NOTSTARTED); // it's a one-way road (sort of)
  1304. debugf("SetStage, stage=%d, mission=%x\n", stage, GetCookie());
  1305. m_misdef.stage = stage;
  1306. m_misdef.fInProgress = stage == STAGE_STARTED;
  1307. BEGIN_PFM_CREATE(g.fm, pfmMissionStage, S, MISSION_STAGE)
  1308. END_PFM_CREATE
  1309. pfmMissionStage->stage = GetStage();
  1310. g.fm.SendMessages(GetGroupMission(), FM_GUARANTEED, FM_FLUSH);
  1311. m_pMission->SetMissionStage(stage);
  1312. SetLobbyIsDirty();
  1313. }
  1314. }
  1315. /*-------------------------------------------------------------------------
  1316. * GiveSideMoney
  1317. *-------------------------------------------------------------------------
  1318. * Purpose:
  1319. * Give a side some money, divided equally among all players
  1320. */
  1321. void CFSMission::GiveSideMoney(IsideIGC * pside, Money money)
  1322. {
  1323. assert (money >= 0);
  1324. if (money != 0)
  1325. {
  1326. SideID sideID = pside->GetObjectID();
  1327. m_rgMoney[sideID] += money;
  1328. DoPayday(pside);
  1329. }
  1330. }
  1331. /*-------------------------------------------------------------------------
  1332. * SetMissionParams
  1333. *-------------------------------------------------------------------------
  1334. * Purpose:
  1335. * Called when a client changes the mission parameters
  1336. */
  1337. void CFSMission::SetMissionParams(const MissionParams & misparmsNew)
  1338. {
  1339. MissionParams misparms = misparmsNew;
  1340. assert(GetStage() == STAGE_NOTSTARTED);
  1341. assert(misparms.Invalid() == NULL);
  1342. // don't even think about changing the IGC static stuff.
  1343. misparms.verIGCcore = m_misdef.misparms.verIGCcore;
  1344. strncpy(misparms.szIGCStaticFile, m_misdef.misparms.szIGCStaticFile, c_cbFileName);
  1345. // make sure it's properly marked as a club or non-club game too.
  1346. #if !defined(ALLSRV_STANDALONE)
  1347. misparms.bClubGame = true;
  1348. #else // !defined(ALLSRV_STANDALONE)
  1349. misparms.bClubGame = false;
  1350. #endif // !defined(ALLSRV_STANDALONE)
  1351. int numTeamsOld = m_misdef.misparms.nTeams;
  1352. if (numTeamsOld > misparms.nTeams)
  1353. {
  1354. // destroy the extra teams
  1355. for (SideID sideID = numTeamsOld - 1; sideID >= misparms.nTeams; sideID--)
  1356. {
  1357. // kick everyone off of the extra team
  1358. IsideIGC* pside = m_pMission->GetSide(sideID);
  1359. while (pside->GetShips()->first())
  1360. {
  1361. CFSShip* pship = (CFSShip*)(pside->GetShips()->first()->data()->GetPrivateData());
  1362. assert(pship->IsPlayer());
  1363. RemovePlayerFromSide(
  1364. pship->GetPlayer(),
  1365. QSR_SideDestroyed
  1366. );
  1367. }
  1368. }
  1369. }
  1370. bool bSquadChange = m_misdef.misparms.bSquadGame != misparms.bSquadGame;
  1371. m_misdef.misparms = misparms;
  1372. // set Mission Name here too so that AGC Admin tool reports the params while mission stage is STAGE_NOTSTARTED
  1373. m_pMission->SetMissionParams(&misparms);
  1374. if (numTeamsOld < misparms.nTeams)
  1375. {
  1376. // add any new teams
  1377. for (SideID sideID = numTeamsOld; sideID < misparms.nTeams; sideID++)
  1378. {
  1379. InitSide(sideID);
  1380. }
  1381. }
  1382. m_pMission->UpdateSides(Time::Now(), &m_misdef.misparms, m_misdef.rgszName);
  1383. // see if that changed the ready state of anyone.
  1384. for (SideLinkIGC* l = m_pMission->GetSides()->first(); l; l = l->next())
  1385. {
  1386. CheckForSideAllReady(l->data());
  1387. }
  1388. // boot any players who are no longer the right rank to play in this game
  1389. {
  1390. const ShipListIGC* pships = m_pMission->GetShips();
  1391. ShipLinkIGC* pshipLinkNext;
  1392. for (ShipLinkIGC* pshipLink = pships->first();
  1393. (pshipLink != NULL);
  1394. pshipLink = pshipLinkNext)
  1395. {
  1396. pshipLinkNext = pshipLink->next();
  1397. CFSPlayer* pfsPlayer = ((CFSShip*)(pshipLink->data()->GetPrivateData()))->GetPlayer();
  1398. RankID rank = pfsPlayer->GetPersistPlayerScore(NA)->GetRank();
  1399. if ((misparms.iMinRank > rank || misparms.iMaxRank < rank) && !pfsPlayer->CanCheat())
  1400. {
  1401. RemovePlayerFromMission(pfsPlayer, QSR_RankLimits);
  1402. }
  1403. }
  1404. }
  1405. // if the squad setting has changed, update the new squad state
  1406. if (bSquadChange)
  1407. {
  1408. for (SideID sideID = 0; sideID < misparms.nTeams; ++sideID)
  1409. {
  1410. if (misparms.bSquadGame)
  1411. MaintainSquadLeadership(sideID);
  1412. else
  1413. SetSideSquad(sideID, NA);
  1414. }
  1415. }
  1416. }
  1417. /*-------------------------------------------------------------------------
  1418. * ChangeCountdown
  1419. *-------------------------------------------------------------------------
  1420. * Purpose:
  1421. * Called when we want to change the countdown to starting the game
  1422. */
  1423. void CFSMission::DelayCountdown(float fDelayLength)
  1424. {
  1425. if (GetStage() == STAGE_NOTSTARTED || GetStage() == STAGE_STARTING)
  1426. {
  1427. // delay the game start time as appropriate.
  1428. m_misdef.misparms.timeStart += fDelayLength;
  1429. // forward the new start time to everyone
  1430. BEGIN_PFM_CREATE(g.fm, pfmStartTime, S, SET_START_TIME)
  1431. END_PFM_CREATE
  1432. pfmStartTime->timeStart = m_misdef.misparms.timeStart;
  1433. g.fm.SendMessages(GetGroupMission(), FM_GUARANTEED, FM_FLUSH);
  1434. }
  1435. }
  1436. /*-------------------------------------------------------------------------
  1437. * StartCountdown
  1438. *-------------------------------------------------------------------------
  1439. * Purpose:
  1440. * Called when we want to start the countdown to starting the game
  1441. */
  1442. void CFSMission::StartCountdown(float fCountdownLength)
  1443. {
  1444. if (GetStage() == STAGE_NOTSTARTED || GetStage() == STAGE_STARTING)
  1445. {
  1446. // set (or reset) the game start time as appropriate.
  1447. m_misdef.misparms.timeStart = Time::Now() + fCountdownLength;
  1448. // forward the new start time to everyone
  1449. BEGIN_PFM_CREATE(g.fm, pfmStartTime, S, SET_START_TIME)
  1450. END_PFM_CREATE
  1451. pfmStartTime->timeStart = m_misdef.misparms.timeStart;
  1452. g.fm.SendMessages(GetGroupMission(), FM_GUARANTEED, FM_FLUSH);
  1453. }
  1454. if (GetStage() == STAGE_NOTSTARTED)
  1455. {
  1456. // create the mission
  1457. {
  1458. for (SideLinkIGC* l = m_pMission->GetSides()->first(); l; l = l->next())
  1459. {
  1460. IsideIGC* pside = l->data();
  1461. if (m_misdef.misparms.bAllowDevelopments)
  1462. pside->SetGlobalAttributeSet(pside->GetCivilization()->GetBaseAttributes());
  1463. else
  1464. pside->ResetGlobalAttributeSet();
  1465. }
  1466. }
  1467. m_pMission->GenerateMission(Time::Now(), &m_misdef.misparms, m_pttbmAltered, m_pttbmNewSetting);
  1468. // tell each client their starting sector so they can display the mission briefing
  1469. {
  1470. for (SideLinkIGC* l = m_pMission->GetSides()->first(); l; l = l->next())
  1471. {
  1472. IsideIGC * pside = l->data();
  1473. // tell them their starting sector so they can display the mission briefing
  1474. bool bGenerateCivBriefing = m_strStoryText.IsEmpty();
  1475. const char* szBriefingText = bGenerateCivBriefing
  1476. ? GetBase(pside)->GetCluster()->GetName() : m_strStoryText;
  1477. BEGIN_PFM_CREATE(g.fm, pfmSetBriefingText, S, SET_BRIEFING_TEXT)
  1478. FM_VAR_PARM(szBriefingText, CB_ZTS)
  1479. END_PFM_CREATE
  1480. pfmSetBriefingText->fGenerateCivBriefing = bGenerateCivBriefing;
  1481. g.fm.SendMessages(CFSSide::FromIGC(pside)->GetGroup(), FM_GUARANTEED, FM_FLUSH);
  1482. }
  1483. }
  1484. // let the clients know that the game is starting
  1485. SetStage(STAGE_STARTING);
  1486. // create the dplay groups for the clusters
  1487. {
  1488. const ClusterListIGC * pclist = m_pMission->GetClusters();
  1489. for (ClusterLinkIGC * l = pclist->first(); (l != NULL); l = l->next())
  1490. {
  1491. IclusterIGC * pcluster = l->data();
  1492. CreateDPGroups(pcluster);
  1493. }
  1494. }
  1495. // set up each of the sides
  1496. {
  1497. const MissionParams* pmp = m_pMission->GetMissionParams();
  1498. float moneyStart = m_pMission->GetFloatConstant(c_fcidStartingMoney);
  1499. for (SideLinkIGC* l = m_pMission->GetSides()->first(); l; l = l->next())
  1500. {
  1501. IsideIGC * pside = l->data();
  1502. CFSPlayer* pfsLeader = GetLeader(pside->GetObjectID());
  1503. float moneyMultiplier = pmp->fStartingMoney + pside->GetCivilization()->GetBonusMoney();
  1504. m_rgMoney[pside->GetObjectID()] = moneyMultiplier > 0.0f
  1505. ? ((Money)(moneyStart * moneyMultiplier))
  1506. : ((Money)0);
  1507. //Set the leader to NULL first (so he is a legitimate autodonate target)
  1508. if (pfsLeader)
  1509. pfsLeader->SetAutoDonate(NULL, 0, false);
  1510. // set up the team's money, and set up everyone to autodonate to the team leader
  1511. const ShipListIGC* pships = pside->GetShips();
  1512. for (ShipLinkIGC* pshipLink = pships->first();
  1513. (pshipLink != NULL);
  1514. pshipLink = pshipLink->next())
  1515. {
  1516. CFSShip* pfsShip = ((CFSShip*)(pshipLink->data()->GetPrivateData()));
  1517. pfsShip->SetMoney(0);
  1518. pfsShip->GetPlayer()->SetTreasureObjectID(NA);
  1519. if ((pfsShip != pfsLeader) && pfsShip->IsPlayer())
  1520. pfsShip->GetPlayer()->SetAutoDonate(pfsLeader, 0, false);
  1521. }
  1522. g.fm.SendMessages(CFSSide::FromIGC(pside)->GetGroup(), FM_GUARANTEED, FM_FLUSH);
  1523. if (HasPlayers(pside, true))
  1524. {
  1525. // tell them about the world
  1526. SendMissionInfo(NULL, pside);
  1527. }
  1528. else
  1529. {
  1530. // don't penalize a squad who is not in the game
  1531. if (pside->GetSquadID() != NA)
  1532. SetSideSquad(pside->GetObjectID(), NA);
  1533. DeactivateSide(pside);
  1534. }
  1535. const StationListIGC* pstations = pside->GetStations();
  1536. assert (pstations->n() >= 1);
  1537. const int c_stationsMax = 20;
  1538. IstationIGC* stations[c_stationsMax];
  1539. int nStations = 0;
  1540. {
  1541. for (StationLinkIGC* psl = pstations->first(); (psl != NULL); psl = psl->next())
  1542. {
  1543. IstationIGC* pstation = psl->data();
  1544. if (pstation->GetStationType()->HasCapability(c_sabmRestart))
  1545. {
  1546. assert (nStations < c_stationsMax);
  1547. stations[nStations++] = pstation;
  1548. }
  1549. }
  1550. }
  1551. assert (nStations >= 1);
  1552. IstationIGC* pstation = stations[0];
  1553. assert (pstation);
  1554. if (nStations == 1)
  1555. {
  1556. //Start all ships at that station
  1557. for (pshipLink = pships->first();
  1558. (pshipLink != NULL);
  1559. pshipLink = pshipLink->next())
  1560. {
  1561. pshipLink->data()->SetStation(pstation);
  1562. ((CFSShip*)(pshipLink->data()->GetPrivateData()))->ShipStatusStart(pstation);
  1563. }
  1564. }
  1565. else
  1566. {
  1567. //Multiple stations for a side ... try to divy things up evenly without breaking wings up
  1568. //We need at least this many/station,
  1569. int minPerStation = pships->n() / (2 * nStations);
  1570. //and can't have more than this (assuming all the other stations have the min)
  1571. int maxPerStation = pships->n() - minPerStation * (nStations - 1);
  1572. //Count the number of players on each wing
  1573. int players[c_widMax] = {0, 0, 0, 0, 0,
  1574. 0, 0, 0, 0, 0};
  1575. int assignment[c_widMax] = {-2, -2, -2, -2, -2,
  1576. -2, -2, -2, -2, -2};
  1577. int unassignedWings = 0;
  1578. {
  1579. for (pshipLink = pships->first();
  1580. (pshipLink != NULL);
  1581. pshipLink = pshipLink->next())
  1582. {
  1583. IshipIGC* pship = pshipLink->data();
  1584. WingID wid = pship->GetWingID();
  1585. assert (wid >= 0);
  1586. assert (wid < c_widMax);
  1587. if (players[wid]++ == 0)
  1588. unassignedWings++;
  1589. }
  1590. }
  1591. int population[c_stationsMax];
  1592. {
  1593. for (int i = 0; (i < nStations); i++)
  1594. population[i] = 0;
  1595. }
  1596. while (unassignedWings-- > 0)
  1597. {
  1598. //Find the largest unassigned wing
  1599. int widMax = -1;
  1600. {
  1601. for (int i = 0; (i < c_widMax); i++)
  1602. {
  1603. if ((assignment[i] == -2) && ((widMax == -1) || (players[i] >= players[widMax])))
  1604. widMax = i;
  1605. }
  1606. }
  1607. assert (widMax != -1);
  1608. //Find the least populated station
  1609. int stationMin = 0;
  1610. {
  1611. for (int i = 1; (i < nStations); i++)
  1612. {
  1613. if (population[i] < population[stationMin])
  1614. stationMin = i;
  1615. }
  1616. }
  1617. if (population[stationMin] + players[widMax] <= maxPerStation)
  1618. {
  1619. population[stationMin] += players[widMax];
  1620. assignment[widMax] = stationMin;
  1621. }
  1622. else
  1623. assignment[widMax] = -1;
  1624. }
  1625. {
  1626. for (pshipLink = pships->first();
  1627. (pshipLink != NULL);
  1628. pshipLink = pshipLink->next())
  1629. {
  1630. IshipIGC* pship = pshipLink->data();
  1631. WingID wid = pship->GetWingID();
  1632. assert (wid >= 0);
  1633. assert (wid < c_widMax);
  1634. int stationMin;
  1635. if (assignment[wid] == -1)
  1636. {
  1637. stationMin = 0;
  1638. {
  1639. for (int i = 1; (i < nStations); i++)
  1640. {
  1641. if (population[i] < population[stationMin])
  1642. stationMin = i;
  1643. }
  1644. }
  1645. population[stationMin]++;
  1646. }
  1647. else
  1648. stationMin = assignment[wid];
  1649. assert (stationMin >= 0);
  1650. IstationIGC* pstation = stations[stationMin];
  1651. pshipLink->data()->SetStation(pstation);
  1652. ((CFSShip*)(pshipLink->data()->GetPrivateData()))->ShipStatusStart(pstation);
  1653. }
  1654. }
  1655. }
  1656. if (pmp->bAllowDevelopments)
  1657. {
  1658. //Create miners for each side in the sector with their home starbase
  1659. for (DroneTypeLinkIGC* pdl = m_pMission->GetDroneTypes()->first(); (pdl != NULL); pdl = pdl->next())
  1660. {
  1661. IdroneTypeIGC* pdt = pdl->data();
  1662. if ((pdt->GetPilotType() == c_ptMiner) && pstation->CanBuy(pdt))
  1663. {
  1664. for (int nMiner = 0; nMiner < m_misdef.misparms.nInitialMinersPerTeam; nMiner++)
  1665. {
  1666. CFSDrone * pfsDrone = CreateStockDrone(pdt, pside);
  1667. if (pfsDrone)
  1668. {
  1669. pfsDrone->GetIGCShip()->SetStation(pstation); //start them at a station just like everyone else
  1670. }
  1671. }
  1672. break;
  1673. }
  1674. }
  1675. }
  1676. }
  1677. }
  1678. }
  1679. }
  1680. /*-------------------------------------------------------------------------
  1681. * StartGame
  1682. *-------------------------------------------------------------------------
  1683. * Purpose:
  1684. * Called when we're ready to set the ships loose and actually start flying around
  1685. */
  1686. void CFSMission::StartGame()
  1687. {
  1688. if (GetStage() == STAGE_NOTSTARTED)
  1689. {
  1690. // transition through the STAGE_STARTING state for consistancy
  1691. StartCountdown(0.0f);
  1692. }
  1693. if (GetStage() == STAGE_STARTING)
  1694. {
  1695. SetStage(STAGE_STARTED);
  1696. {
  1697. //Connect all players score objects to their side score objects
  1698. for (ShipLinkIGC* psl = m_pMission->GetShips()->first(); (psl != NULL); psl = psl->next())
  1699. {
  1700. CFSShip* pfsship = (CFSShip*)(psl->data()->GetPrivateData());
  1701. IsideIGC* pside = psl->data()->GetSide();
  1702. if (pside->GetObjectID() >= 0)
  1703. {
  1704. pfsship->GetPlayerScoreObject()->Connect(g.timeNow);
  1705. }
  1706. }
  1707. }
  1708. m_timeNextPayday = g.timeNow + m_pMission->GetFloatConstant(c_fcidSecondsBetweenPaydays);
  1709. m_psideWon = NULL;
  1710. m_pszReason = NULL;
  1711. m_bDraw = false;
  1712. m_pMission->EnterGame();
  1713. // let everyone know that the game has started
  1714. BEGIN_PFM_CREATE(g.fm, pfmEnterGame, S, ENTER_GAME)
  1715. END_PFM_CREATE
  1716. g.fm.SendMessages(GetGroupRealSides(), FM_GUARANTEED, FM_FLUSH);
  1717. LPCSTR pszContext = GetIGCMission() ? GetIGCMission()->GetContextName() : NULL;
  1718. GetSite()->SendChat(NULL, CHAT_EVERYONE, NA, NA, "The game has started.");
  1719. _AGCModule.TriggerContextEvent(NULL, AllsrvEventID_GameStarted, pszContext, "", -1, -1, -1, 0);
  1720. DoPayday();
  1721. }
  1722. }
  1723. void CFSMission::DoPayday(void)
  1724. {
  1725. //Paydays for the various sides
  1726. const SideListIGC* psides = GetIGCMission()->GetSides();
  1727. for (SideLinkIGC* l = psides->first();
  1728. (l != NULL);
  1729. l = l->next())
  1730. {
  1731. DoPayday(l->data());
  1732. }
  1733. }
  1734. void CFSMission::DoPayday(IsideIGC* pside)
  1735. {
  1736. SideID sideID = pside->GetObjectID();
  1737. int np = GetCountOfPlayers(pside, true);
  1738. if (np != 0)
  1739. {
  1740. int delta = m_rgMoney[sideID] / np;
  1741. if (delta > 0)
  1742. {
  1743. BEGIN_PFM_CREATE(g.fm, pfmPayday, S, PAYDAY)
  1744. END_PFM_CREATE
  1745. pfmPayday->dMoney = Money(delta);
  1746. g.fm.SendMessages(CFSSide::FromIGC(pside)->GetGroup(), FM_GUARANTEED, FM_FLUSH);
  1747. m_rgMoney[sideID] -= delta * np;
  1748. const ShipListIGC * plistShip = pside->GetShips();
  1749. for(ShipLinkIGC * plinkShip = plistShip->first(); plinkShip; plinkShip = plinkShip->next())
  1750. {
  1751. CFSShip * pfsShip = (CFSShip *) plinkShip->data()->GetPrivateData();
  1752. if (pfsShip->IsPlayer())
  1753. {
  1754. CFSPlayer* pfsDonate = pfsShip->GetPlayer()->GetAutoDonate();
  1755. if (pfsDonate)
  1756. pfsDonate->SetMoney(pfsDonate->GetMoney() + delta);
  1757. else
  1758. pfsShip->SetMoney(pfsShip->GetMoney() + delta);
  1759. }
  1760. }
  1761. }
  1762. }
  1763. }
  1764. void CFSMission::DoTick(Time timeNow)
  1765. {
  1766. assert (GetStage() == STAGE_STARTED);
  1767. ImissionIGC* pm = GetIGCMission();
  1768. const MissionParams* pmp = pm->GetMissionParams();
  1769. // check for the timeout winning condition
  1770. {
  1771. if (pmp->IsCountdownGame())
  1772. {
  1773. if ((timeNow - pmp->timeStart) > pmp->GetCountDownTime())
  1774. {
  1775. //End the game ... side with the most kills wins (ties -> fewest deaths, ties -> most money)
  1776. IsideIGC* psideWin = NULL;
  1777. const char* pszClue;
  1778. if (pmp->IsConquestGame())
  1779. {
  1780. pszClue = "conquest";
  1781. int maxFlags = 0;
  1782. for (SideLinkIGC* psl = pm->GetSides()->first(); (psl != NULL); psl = psl->next())
  1783. {
  1784. IsideIGC* pside = psl->data();
  1785. int nFlags = 0;
  1786. for (StationLinkIGC* l = pside->GetStations()->first(); (l != NULL); l = l->next())
  1787. {
  1788. if (l->data()->GetBaseStationType()->HasCapability(c_sabmFlag))
  1789. nFlags++;
  1790. }
  1791. if (nFlags > maxFlags)
  1792. {
  1793. maxFlags = nFlags;
  1794. psideWin = pside;
  1795. }
  1796. else if (nFlags == maxFlags)
  1797. {
  1798. psideWin = NULL;
  1799. }
  1800. }
  1801. }
  1802. if ((psideWin == NULL) && pmp->IsTerritoryGame())
  1803. {
  1804. pszClue = "territory";
  1805. unsigned char maxSectors = 0;
  1806. for (SideLinkIGC* psl = pm->GetSides()->first(); (psl != NULL); psl = psl->next())
  1807. {
  1808. IsideIGC* pside = psl->data();
  1809. int nSectors = pside->GetTerritoryCount();
  1810. if (nSectors > maxSectors)
  1811. {
  1812. maxSectors = nSectors;
  1813. psideWin = pside;
  1814. }
  1815. else if (nSectors == maxSectors)
  1816. {
  1817. psideWin = NULL;
  1818. }
  1819. }
  1820. }
  1821. if ((psideWin == NULL) && pmp->IsProsperityGame())
  1822. {
  1823. pszClue = "prosperity";
  1824. DWORD maxTime = 0;
  1825. for (SideLinkIGC* psl = pm->GetSides()->first(); (psl != NULL); psl = psl->next())
  1826. {
  1827. IsideIGC* pside = psl->data();
  1828. IbucketIGC* pbucket = pside->GetBuckets()->last()->data();
  1829. assert ((pbucket->GetBucketType() == OT_development) &&
  1830. (pbucket->GetBuyable()->GetObjectID() == c_didTeamMoney));
  1831. DWORD time = pbucket->GetTime();
  1832. if (time > maxTime)
  1833. {
  1834. maxTime = time;
  1835. psideWin = pside;
  1836. }
  1837. else if (time == maxTime)
  1838. {
  1839. psideWin = NULL;
  1840. }
  1841. }
  1842. }
  1843. if ((psideWin == NULL) && pmp->IsArtifactsGame())
  1844. {
  1845. pszClue = "artifacts";
  1846. short maxFlags = 0;
  1847. for (SideLinkIGC* psl = pm->GetSides()->first(); (psl != NULL); psl = psl->next())
  1848. {
  1849. IsideIGC* pside = psl->data();
  1850. short nFlags = pside->GetArtifacts();
  1851. if (nFlags > maxFlags)
  1852. {
  1853. maxFlags = nFlags;
  1854. psideWin = pside;
  1855. }
  1856. else if (nFlags == maxFlags)
  1857. {
  1858. psideWin = NULL;
  1859. }
  1860. }
  1861. }
  1862. if ((psideWin == NULL) && pmp->IsFlagsGame())
  1863. {
  1864. pszClue = "flags";
  1865. short maxFlags = 0;
  1866. for (SideLinkIGC* psl = pm->GetSides()->first(); (psl != NULL); psl = psl->next())
  1867. {
  1868. IsideIGC* pside = psl->data();
  1869. short nFlags = pside->GetFlags();
  1870. if (nFlags > maxFlags)
  1871. {
  1872. maxFlags = nFlags;
  1873. psideWin = pside;
  1874. }
  1875. else if (nFlags == maxFlags)
  1876. {
  1877. psideWin = NULL;
  1878. }
  1879. }
  1880. }
  1881. if (psideWin == NULL)
  1882. {
  1883. pszClue = "kills";
  1884. int nKills = -1;
  1885. int nDeaths = INT_MAX;
  1886. for (SideLinkIGC* psl = pm->GetSides()->first(); (psl != NULL); psl = psl->next())
  1887. {
  1888. IsideIGC* pside = psl->data();
  1889. int k = pside->GetKills();
  1890. int d = pside->GetDeaths();
  1891. if ((k > nKills) ||
  1892. ((k == nKills) && (d < nDeaths)))
  1893. {
  1894. nKills = k;
  1895. nDeaths = d;
  1896. psideWin = pside;
  1897. }
  1898. else if ((k == nKills) && (d == nDeaths))
  1899. {
  1900. psideWin = NULL;
  1901. }
  1902. }
  1903. }
  1904. if (psideWin)
  1905. {
  1906. static char szReason[100];
  1907. sprintf(szReason, "The clock has stopped. The winner: %s by %s", psideWin->GetName(), pszClue);
  1908. GameOver(psideWin, szReason);
  1909. }
  1910. else
  1911. {
  1912. GameOver(NULL, "The clock has stopped. The game is a draw.");
  1913. }
  1914. }
  1915. }
  1916. }
  1917. if (m_psideWon || m_bDraw)
  1918. {
  1919. ProcessGameOver();
  1920. return;
  1921. }
  1922. if (timeNow >= m_timeNextPayday)
  1923. {
  1924. float dtPayday = m_pMission->GetFloatConstant(c_fcidSecondsBetweenPaydays);
  1925. float dt = dtPayday + (timeNow - m_timeNextPayday);
  1926. m_timeNextPayday = m_timeNextPayday + dtPayday;
  1927. if (pmp->bAllowDevelopments)
  1928. {
  1929. //Get the average pot size for all teams
  1930. float moneyIncome = m_pMission->GetFloatConstant(c_fcidIncome);
  1931. const SideListIGC* psides = GetIGCMission()->GetSides();
  1932. {
  1933. for (SideLinkIGC* l = psides->first();
  1934. (l != NULL);
  1935. l = l->next())
  1936. {
  1937. IsideIGC* pside = l->data();
  1938. SideID sideID = pside->GetObjectID();
  1939. //Calculate side income
  1940. m_rgMoney[sideID] += (Money)(moneyIncome * (1.0f + pside->GetCivilization()->GetIncomeMoney()));
  1941. Money income = 0;
  1942. for (StationLinkIGC* psl = pside->GetStations()->first();
  1943. (psl != NULL);
  1944. psl = psl->next())
  1945. {
  1946. income += psl->data()->GetStationType()->GetIncome();
  1947. }
  1948. m_rgMoney[sideID] += income;
  1949. }
  1950. }
  1951. //Paydays for the various sides
  1952. DoPayday();
  1953. }
  1954. {
  1955. //Newly created objects for the various sectors
  1956. for (ClusterLinkIGC* l = GetIGCMission()->GetClusters()->first();
  1957. (l != NULL);
  1958. l = l->next())
  1959. {
  1960. IclusterIGC* pcluster = l->data();
  1961. bool bHome = pcluster->GetHomeSector();
  1962. float treasures = pcluster->GetPendingTreasures() +
  1963. dt * (bHome
  1964. ? pmp->nPlayerSectorTreasureRate
  1965. : pmp->nNeutralSectorTreasureRate);
  1966. if (treasures >= 1.0f)
  1967. {
  1968. //Create the appropriate treasures for the
  1969. int n = int(treasures);
  1970. treasures -= float(n);
  1971. short tsi = bHome ? pmp->tsiPlayerRegenerate : pmp->tsiNeutralRegenerate;
  1972. for (int i = n; (i > 0); i--)
  1973. {
  1974. GetIGCMission()->GenerateTreasure(timeNow, pcluster, tsi);
  1975. }
  1976. }
  1977. pcluster->SetPendingTreasures(treasures);
  1978. }
  1979. }
  1980. }
  1981. // update any pending ballots
  1982. for (BallotList::Iterator iter(m_ballots); !iter.End();)
  1983. {
  1984. if (iter.Value()->Update(timeNow))
  1985. iter.Remove();
  1986. else
  1987. iter.Next();
  1988. }
  1989. }
  1990. /*-------------------------------------------------------------------------
  1991. * Vacate
  1992. *-------------------------------------------------------------------------
  1993. Purpose:
  1994. Kicks everyone out
  1995. Moves all players to lifepods in the null cluster and terminates drones
  1996. */
  1997. void CFSMission::Vacate(void)
  1998. {
  1999. const ShipListIGC * pShips = m_pMission->GetShips();
  2000. CFSShip * pfsShip = NULL;
  2001. ShipLinkIGC * pShiplink = NULL;
  2002. pShiplink = pShips->first();
  2003. while (pShiplink)
  2004. {
  2005. CFSShip * pfsShip = (CFSShip *) pShiplink->data()->GetPrivateData();
  2006. pShiplink = pShiplink->next();
  2007. if (!pfsShip->IsPlayer())
  2008. delete pfsShip->GetDrone();
  2009. else
  2010. {
  2011. pfsShip->Reset(true);
  2012. }
  2013. }
  2014. }
  2015. void CFSMission::SaveAsOldPlayer(CFSPlayer* pfsplayer, bool bBooted)
  2016. {
  2017. OldPlayerLink* popl = GetOldPlayerLink(pfsplayer->GetName());
  2018. if (!popl)
  2019. {
  2020. popl = new OldPlayerLink;
  2021. m_oldPlayers.last(popl);
  2022. }
  2023. OldPlayerInfo& opi = popl->data();
  2024. strcpy(opi.name, pfsplayer->GetName());
  2025. PlayerScoreObject* ppso = pfsplayer->GetPlayerScoreObject();
  2026. opi.pso = *ppso;
  2027. opi.sideID = pfsplayer->GetSide()->GetObjectID();
  2028. if ((GetStage() != STAGE_STARTED) ||
  2029. (m_pMission->GetMissionParams()->bAllowDefections))
  2030. {
  2031. opi.bannedSideMask = pfsplayer->GetBannedSideMask();
  2032. if (bBooted)
  2033. {
  2034. if (opi.sideID < 0)
  2035. {
  2036. opi.bannedSideMask = 0xff;
  2037. }
  2038. else
  2039. {
  2040. opi.bannedSideMask |= SideMask(opi.sideID);
  2041. }
  2042. }
  2043. }
  2044. else
  2045. {
  2046. //No defections
  2047. if (bBooted)
  2048. opi.bannedSideMask = 0xff;
  2049. else if (opi.sideID >= 0)
  2050. opi.bannedSideMask = ~SideMask(opi.sideID);
  2051. else
  2052. opi.bannedSideMask = pfsplayer->GetBannedSideMask();
  2053. }
  2054. opi.characterID = pfsplayer->GetCharacterID();
  2055. IclusterIGC* pcluster = pfsplayer->GetIGCShip()->GetCluster();
  2056. if (pcluster)
  2057. {
  2058. opi.pclusterLifepod = pcluster;
  2059. opi.positionLifepod = pfsplayer->GetIGCShip()->GetSourceShip()->GetPosition();
  2060. }
  2061. }
  2062. /*-------------------------------------------------------------------------
  2063. * GameOver
  2064. *-------------------------------------------------------------------------
  2065. Purpose:
  2066. Let someone forcibly end the game, even though no victory condition was met
  2067. Parameters:
  2068. The winning side
  2069. */
  2070. void CFSMission::GameOver(IsideIGC * psideWin, const char* pszReason)
  2071. {
  2072. m_psideWon = psideWin;
  2073. m_pszReason = pszReason;
  2074. m_bDraw = m_psideWon == NULL;
  2075. LPCSTR pszContext = GetIGCMission() ? GetIGCMission()->GetContextName() : NULL;
  2076. // the game will actually end when we get around to checking whether a team has won
  2077. _AGCModule.TriggerContextEvent(NULL, AllsrvEventID_GameEnded, pszContext,
  2078. "", -1, -1, -1, 1,
  2079. "Reason", VT_LPSTR, pszReason);
  2080. }
  2081. /*-------------------------------------------------------------------------
  2082. * MakeOverrideTechBits
  2083. *-------------------------------------------------------------------------
  2084. */
  2085. void CFSMission::MakeOverrideTechBits()
  2086. {
  2087. if (m_pttbmAltered == NULL)
  2088. {
  2089. m_pttbmAltered = new TechTreeBitMask[c_cSidesMax];
  2090. for (int i = 0; i < c_cSidesMax; ++i)
  2091. (m_pttbmAltered)[i].ClearAll();
  2092. }
  2093. if (m_pttbmNewSetting == NULL)
  2094. {
  2095. m_pttbmNewSetting = new TechTreeBitMask[c_cSidesMax];
  2096. for (int i = 0; i < c_cSidesMax; ++i)
  2097. (m_pttbmNewSetting)[i].ClearAll();
  2098. }
  2099. }
  2100. /*-------------------------------------------------------------------------
  2101. * RecordGameResults
  2102. *-------------------------------------------------------------------------
  2103. Purpose:
  2104. Records the results of the game to the database.
  2105. */
  2106. void CFSMission::RecordGameResults()
  2107. {
  2108. #if !defined(ALLSRV_STANDALONE)
  2109. // Create the database update query
  2110. CQGameResults* pquery = new CQGameResults(NULL); // don't need call-back notification
  2111. CQGameResultsData* pqd = pquery->GetData();
  2112. // Populate the query parameters
  2113. strncpy(pqd->szGameID , GetIGCMission()->GetContextName() , sizeofArray(pqd->szGameID));
  2114. strncpy(pqd->szName , m_misdef.misparms.strGameName , sizeofArray(pqd->szName));
  2115. strncpy(pqd->szWinningTeam, m_psideWon ? m_psideWon->GetName() : "", sizeofArray(pqd->szWinningTeam));
  2116. // make SURE they're NULL-terminated
  2117. pqd->szGameID[sizeofArray(pqd->szGameID) - 1] = '\0';
  2118. pqd->szName[sizeofArray(pqd->szName) - 1] = '\0';
  2119. pqd->szWinningTeam[sizeofArray(pqd->szWinningTeam) - 1] = '\0';
  2120. pqd->nWinningTeamID = m_psideWon ? m_psideWon->GetObjectID() : NA;
  2121. pqd->bIsGoalConquest = m_misdef.misparms.IsConquestGame();
  2122. pqd->bIsGoalCountdown = m_misdef.misparms.IsCountdownGame();
  2123. pqd->bIsGoalTeamKills = m_misdef.misparms.IsDeathMatchGame();
  2124. pqd->bIsGoalProsperity = m_misdef.misparms.IsProsperityGame();
  2125. pqd->bIsGoalArtifacts = m_misdef.misparms.IsArtifactsGame();
  2126. pqd->bIsGoalFlags = m_misdef.misparms.IsFlagsGame();
  2127. pqd->nGoalConquest = m_misdef.misparms.iGoalConquestPercentage;
  2128. pqd->nGoalCountdown = m_misdef.misparms.dtGameLength;
  2129. pqd->nGoalTeamKills = m_misdef.misparms.nGoalTeamKills;
  2130. pqd->fGoalProsperity = m_misdef.misparms.fGoalTeamMoney;
  2131. pqd->nGoalArtifacts = m_misdef.misparms.nGoalArtifactsCount;
  2132. pqd->nGoalFlags = m_misdef.misparms.nGoalFlagsCount;
  2133. pqd->nDuration = GetGameDuration();
  2134. // Post the query for async completion
  2135. g.sql.PostQuery(pquery);
  2136. // Iterate through each team of the game
  2137. const SideListIGC* pSides = GetIGCMission()->GetSides();
  2138. for (SideLinkIGC* itSide = pSides->first(); itSide; itSide = itSide->next())
  2139. {
  2140. IsideIGC* pside = itSide->data();
  2141. assert(pside);
  2142. // Record the team results
  2143. RecordTeamResults(pside);
  2144. }
  2145. // Iterate through each player that left the game before it ended
  2146. for (OldPlayerLink* itOld = m_oldPlayers.first(); itOld; itOld = itOld->next())
  2147. {
  2148. OldPlayerInfo& opi = itOld->data();
  2149. // Record the player results (if they played on a real side)
  2150. if (opi.sideID != SIDE_TEAMLOBBY)
  2151. RecordPlayerResults(opi.name, &opi.pso, opi.sideID);
  2152. }
  2153. #endif // !defined(ALLSRV_STANDALONE)
  2154. }
  2155. /*-------------------------------------------------------------------------
  2156. * RecordTeamResults
  2157. *-------------------------------------------------------------------------
  2158. Purpose:
  2159. Records the results of the team to the database.
  2160. */
  2161. void CFSMission::RecordTeamResults(IsideIGC* pside)
  2162. {
  2163. #if !defined(ALLSRV_STANDALONE)
  2164. // Create the database update query
  2165. CQTeamResults* pquery = new CQTeamResults(NULL);
  2166. CQTeamResultsData* pqd = pquery->GetData();
  2167. // Populate the query parameters
  2168. strncpy(pqd->szGameID , GetIGCMission()->GetContextName(), sizeofArray(pqd->szGameID));
  2169. strncpy(pqd->szName , pside->GetName() , sizeofArray(pqd->szName));
  2170. // make SURE they're NULL-terminated
  2171. pqd->szGameID[sizeofArray(pqd->szGameID) - 1] = '\0';
  2172. pqd->szName[sizeofArray(pqd->szName) - 1] = '\0';
  2173. pside->GetTechs().ToString(pqd->szTechs, sizeofArray(pqd->szTechs));
  2174. pqd->nCivID = pside->GetCivilization()->GetObjectID();
  2175. pqd->nTeamID = pside->GetObjectID();
  2176. pqd->cPlayerKills = pside->GetKills();
  2177. pqd->cBaseKills = pside->GetBaseKills();
  2178. pqd->cBaseCaptures = pside->GetBaseCaptures();
  2179. pqd->cDeaths = pside->GetDeaths();
  2180. pqd->cEjections = pside->GetEjections();
  2181. pqd->cFlags = pside->GetFlags();
  2182. pqd->cArtifacts = pside->GetArtifacts();
  2183. pqd->nConquestPercent = pside->GetConquestPercent();
  2184. pqd->nProsperityPercentBought = pside->GetProsperityPercentBought();
  2185. pqd->nProsperityPercentComplete = pside->GetProsperityPercentComplete();
  2186. if (pside->GetActiveF())
  2187. pqd->nTimeEndured = GetGameDuration();
  2188. else
  2189. pqd->nTimeEndured = pside->GetTimeEndured();
  2190. // Post the query for async completion
  2191. g.sql.PostQuery(pquery);
  2192. // Iterate through each player of the team
  2193. const ShipListIGC* pShips = pside->GetShips();
  2194. for (ShipLinkIGC* itShip = pShips->first(); itShip; itShip = itShip->next())
  2195. {
  2196. IshipIGC* pship = itShip->data();
  2197. assert(pship);
  2198. // Filter-out non-player ships
  2199. if (ISPLAYER(pship))
  2200. {
  2201. // Get the player score object
  2202. CFSPlayer* pfsPlayer = ((CFSShip*)(pship->GetPrivateData()))->GetPlayer();
  2203. PlayerScoreObject* ppso = pfsPlayer->GetPlayerScoreObject();
  2204. // Record the player results
  2205. RecordPlayerResults(pship->GetName(), ppso, pside->GetObjectID());
  2206. }
  2207. }
  2208. #endif // !defined(ALLSRV_STANDALONE)
  2209. }
  2210. /*-------------------------------------------------------------------------
  2211. * RecordPlayerResults
  2212. *-------------------------------------------------------------------------
  2213. Purpose:
  2214. Records the results of the player to the database.
  2215. */
  2216. void CFSMission::RecordPlayerResults(const char* pszName, PlayerScoreObject* ppso, SideID sid)
  2217. {
  2218. #if !defined(ALLSRV_STANDALONE)
  2219. // Create the database update query
  2220. CQPlayerResults* pquery = new CQPlayerResults(NULL);
  2221. CQPlayerResultsData* pqd = pquery->GetData();
  2222. // Populate the query parameters
  2223. strncpy(pqd->szGameID , GetIGCMission()->GetContextName(), sizeofArray(pqd->szGameID));
  2224. strncpy(pqd->szName , pszName , sizeofArray(pqd->szName) );
  2225. // make SURE they're NULL-terminated
  2226. pqd->szGameID[sizeofArray(pqd->szGameID) - 1] = '\0';
  2227. pqd->szName[sizeofArray(pqd->szName) - 1] = '\0';
  2228. pqd->nTeamID = sid;
  2229. pqd->cPlayerKills = ppso->GetPlayerKills();
  2230. pqd->cBuilderKills = ppso->GetBuilderKills();
  2231. pqd->cLayerKills = ppso->GetLayerKills();
  2232. pqd->cMinerKills = ppso->GetMinerKills();
  2233. pqd->cBaseKills = ppso->GetBaseKills();
  2234. pqd->cBaseCaptures = ppso->GetBaseCaptures();
  2235. pqd->cPilotBaseKills = ppso->GetPilotBaseKills();
  2236. pqd->cPilotBaseCaptures = ppso->GetPilotBaseCaptures();
  2237. pqd->cDeaths = ppso->GetDeaths();
  2238. pqd->cEjections = ppso->GetEjections();
  2239. pqd->cRescues = ppso->GetRescues();
  2240. pqd->cFlags = ppso->GetFlags();
  2241. pqd->cArtifacts = ppso->GetArtifacts();
  2242. pqd->cTechsRecovered = ppso->GetTechsRecovered();
  2243. pqd->cAlephsSpotted = ppso->GetWarpsSpotted();
  2244. pqd->cAsteroidsSpotted = ppso->GetAsteroidsSpotted();
  2245. pqd->fCombatRating = ppso->GetCombatRating();
  2246. pqd->fScore = ppso->GetScore();
  2247. pqd->nTimePlayed = ppso->GetTimePlayed();
  2248. // spew the player results that we're writing...
  2249. debugf("Writing player results: %s %s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %g %g %d\n",
  2250. pqd->szGameID,
  2251. pqd->szName,
  2252. pqd->nTeamID,
  2253. pqd->cPlayerKills,
  2254. pqd->cBuilderKills,
  2255. pqd->cLayerKills,
  2256. pqd->cMinerKills,
  2257. pqd->cBaseKills,
  2258. pqd->cBaseCaptures,
  2259. pqd->cPilotBaseKills,
  2260. pqd->cPilotBaseCaptures,
  2261. pqd->cDeaths,
  2262. pqd->cEjections,
  2263. pqd->cRescues,
  2264. pqd->cFlags,
  2265. pqd->cArtifacts,
  2266. pqd->cTechsRecovered,
  2267. pqd->cAlephsSpotted,
  2268. pqd->cAsteroidsSpotted,
  2269. (double)pqd->fCombatRating,
  2270. (double)pqd->fScore,
  2271. pqd->nTimePlayed
  2272. );
  2273. // Post the query for async completion
  2274. g.sql.PostQuery(pquery);
  2275. #endif // !defined(ALLSRV_STANDALONE)
  2276. }
  2277. /*-------------------------------------------------------------------------
  2278. * QueueGameoverMessage
  2279. *-------------------------------------------------------------------------
  2280. Purpose:
  2281. Tell a player or group of players that they are out of the game.
  2282. Parameters:
  2283. The winning side is in m_psideWon
  2284. */
  2285. void CFSMission::QueueGameoverMessage()
  2286. {
  2287. // queue the general game over stats and side stats
  2288. BEGIN_PFM_CREATE(g.fm, pfmGameOver, S, GAME_OVER)
  2289. FM_VAR_PARM((const char*)(m_pszReason ? m_pszReason : "Your side has been eliminated."), CB_ZTS)
  2290. END_PFM_CREATE
  2291. for (SideID sideID = 0; sideID < c_cSidesMax; sideID++)
  2292. {
  2293. IsideIGC* pside = m_pMission->GetSide(sideID);
  2294. strcpy(pfmGameOver->rgSides[sideID].sideName, pside ? pside->GetName() : "<bug>");
  2295. pfmGameOver->rgSides[sideID].civID = pside ? pside->GetCivilization()->GetObjectID() : -1;
  2296. pfmGameOver->rgSides[sideID].color = pside ? pside->GetColor() : Color(0.5, 0.5, 0.5);
  2297. pfmGameOver->rgSides[sideID].cKills = pside ? pside->GetKills() : 0;
  2298. pfmGameOver->rgSides[sideID].cDeaths = pside ? pside->GetDeaths() : 0;
  2299. pfmGameOver->rgSides[sideID].cEjections = pside ? pside->GetEjections() : 0;
  2300. pfmGameOver->rgSides[sideID].cBaseKills = pside ? pside->GetBaseKills() : 0;
  2301. pfmGameOver->rgSides[sideID].cBaseCaptures = pside ? pside->GetBaseCaptures() : 0;
  2302. pfmGameOver->rgSides[sideID].cFlags = pside ? pside->GetFlags() : 0;
  2303. pfmGameOver->rgSides[sideID].cArtifacts = pside ? pside->GetArtifacts() : 0;
  2304. };
  2305. pfmGameOver->nNumSides = m_pMission->GetSides()->n();
  2306. pfmGameOver->iSideWinner = m_psideWon ? m_psideWon->GetObjectID() : NA;
  2307. pfmGameOver->bEjectPods = m_misdef.misparms.bEjectPods;
  2308. // queue the player stats
  2309. const ShipListIGC * plistShip = m_pMission->GetShips();
  2310. int nNumPlayers = plistShip->n() + m_oldPlayers.n(); //Worst case estimate
  2311. size_t playerlistSize = nNumPlayers * sizeof(PlayerEndgameInfo);
  2312. PlayerEndgameInfo* playerlist = (PlayerEndgameInfo*)alloca(playerlistSize);
  2313. // get the current stats for all players
  2314. int nPlayerIndex = 0;
  2315. for (ShipLinkIGC * plinkShip = plistShip->first(); plinkShip; plinkShip = plinkShip->next())
  2316. {
  2317. if (ISPLAYER(plinkShip->data()))
  2318. {
  2319. CFSPlayer* pfsPlayer = ((CFSShip *)(plinkShip->data()->GetPrivateData()))->GetPlayer();
  2320. PlayerScoreObject* ppso = pfsPlayer->GetPlayerScoreObject();
  2321. if (ppso->GetTimePlayed() > 0.0f && pfsPlayer->GetSide()->GetObjectID() != SIDE_TEAMLOBBY)
  2322. {
  2323. strcpy(playerlist[nPlayerIndex].characterName, pfsPlayer->GetName());
  2324. playerlist[nPlayerIndex].scoring.Set(pfsPlayer->GetPlayerScoreObject());
  2325. playerlist[nPlayerIndex].stats = ppso->GetPersist();
  2326. playerlist[nPlayerIndex].sideId = pfsPlayer->GetSide()->GetObjectID();
  2327. nPlayerIndex++;
  2328. }
  2329. }
  2330. }
  2331. for (OldPlayerLink* popl = m_oldPlayers.first(); (popl != NULL); popl = popl->next())
  2332. {
  2333. OldPlayerInfo& opi = popl->data();
  2334. if (opi.pso.GetTimePlayed() != 0.0f && opi.sideID != SIDE_TEAMLOBBY)
  2335. {
  2336. strcpy(playerlist[nPlayerIndex].characterName, opi.name);
  2337. playerlist[nPlayerIndex].scoring.Set(&(opi.pso));
  2338. playerlist[nPlayerIndex].stats = opi.pso.GetPersist();
  2339. playerlist[nPlayerIndex].sideId = opi.sideID;
  2340. nPlayerIndex++;
  2341. }
  2342. }
  2343. assert(nPlayerIndex <= nNumPlayers);
  2344. // send the players in chunks of no more than 50 players at a time
  2345. const int nMaxPlayersPerMsg = 50;
  2346. while (nPlayerIndex > 0)
  2347. {
  2348. int nPlayers = min(nPlayerIndex, nMaxPlayersPerMsg);
  2349. nPlayerIndex -= nPlayers;
  2350. BEGIN_PFM_CREATE(g.fm, pfmGameOver, S, GAME_OVER_PLAYERS)
  2351. FM_VAR_PARM(playerlist + nPlayerIndex, nPlayers * sizeof(PlayerEndgameInfo))
  2352. END_PFM_CREATE
  2353. }
  2354. }
  2355. static float GetExpMult(float totalExp, float sideExp, int nSides)
  2356. {
  2357. const float minExpMult = 0.1f;
  2358. if ((nSides < 2) || (sideExp <= 0.0f))
  2359. return 1.0f;
  2360. float avgExp = (totalExp - sideExp) / float(nSides - 1);
  2361. if (sideExp <= avgExp)
  2362. return 1.0f;
  2363. float m = avgExp / sideExp;
  2364. return (m < minExpMult) ? minExpMult : m;
  2365. }
  2366. /*-------------------------------------------------------------------------
  2367. * ProcessGameOver
  2368. *-------------------------------------------------------------------------
  2369. Purpose:
  2370. Wrap things up when the game is actually over, not to be confused with
  2371. when the mission is over, which doesn't happen until everyone in it leaves.
  2372. Parameters:
  2373. The winning side is in m_psideWon
  2374. */
  2375. void CFSMission::ProcessGameOver()
  2376. {
  2377. const ShipListIGC * pShips = m_pMission->GetShips();
  2378. ShipLinkIGC * pShiplink;
  2379. m_flGameDuration = g.timeNow - m_misdef.misparms.timeStart;
  2380. //Calculate scores for all players and get a running average of time played.
  2381. float totalExperience = 0.0f;
  2382. float sideExperience[c_cSidesMax] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
  2383. float dtPlayed[c_cSidesMax] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
  2384. float scoreEarned[c_cSidesMax] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
  2385. PlayerScoreObject* commander[c_cSidesMax] = {NULL, NULL, NULL, NULL, NULL, NULL};
  2386. {
  2387. for (pShiplink = pShips->first(); pShiplink; pShiplink = pShiplink->next())
  2388. {
  2389. CFSShip * pfsShip = (CFSShip *) pShiplink->data()->GetPrivateData();
  2390. if (pfsShip->IsPlayer())
  2391. {
  2392. PlayerScoreObject* ppso = pfsShip->GetPlayerScoreObject();
  2393. if (ppso->Connected())
  2394. {
  2395. ppso->Disconnect(g.timeNow);
  2396. IsideIGC* pside = pfsShip->GetSide();
  2397. bool bCount = (ppso->GetTimePlayed() > 180.0f) && !m_bDraw;
  2398. ppso->EndGame(m_pMission,
  2399. (pside == m_psideWon) && bCount && (ppso->GetTimePlayed() > GetGameDuration() / 2.0f),
  2400. (pside != m_psideWon) && bCount);
  2401. SideID sid = pside->GetObjectID();
  2402. if (sid >= 0)
  2403. {
  2404. float e = ppso->GetTimePlayed() * ppso->GetPersist().GetScore();
  2405. totalExperience += e;
  2406. sideExperience[sid] += e;
  2407. scoreEarned[sid] += ppso->GetScore();
  2408. dtPlayed[sid] += ppso->GetTimePlayed();
  2409. }
  2410. if ((commander[sid] == NULL) ||
  2411. (commander[sid]->GetTimeCommanded() < ppso->GetTimeCommanded()))
  2412. {
  2413. commander[sid] = ppso;
  2414. }
  2415. }
  2416. }
  2417. }
  2418. }
  2419. {
  2420. SideID sidWin = m_psideWon ? m_psideWon->GetObjectID() : NA;
  2421. for (OldPlayerLink* popl = m_oldPlayers.first(); (popl != NULL); popl = popl->next())
  2422. {
  2423. OldPlayerInfo & opi = popl->data();
  2424. bool bCount = (opi.pso.GetTimePlayed() > 180.0f);
  2425. opi.pso.EndGame(m_pMission,
  2426. (opi.sideID == sidWin) && bCount && (opi.pso.GetTimePlayed() > GetGameDuration() / 2.0f),
  2427. (opi.sideID != sidWin) && bCount);
  2428. if (opi.sideID >= 0)
  2429. {
  2430. float e = opi.pso.GetTimePlayed() * opi.pso.GetPersist().GetScore();
  2431. totalExperience += e;
  2432. sideExperience[opi.sideID] += e;
  2433. scoreEarned[opi.sideID] += opi.pso.GetScore();
  2434. dtPlayed[opi.sideID] += opi.pso.GetTimePlayed();
  2435. if ((commander[opi.sideID] == NULL) ||
  2436. (commander[opi.sideID]->GetTimeCommanded() < opi.pso.GetTimeCommanded()))
  2437. {
  2438. commander[opi.sideID] = &(opi.pso);
  2439. }
  2440. }
  2441. }
  2442. }
  2443. //Get average exp/team
  2444. {
  2445. for (pShiplink = pShips->first(); pShiplink; pShiplink = pShiplink->next())
  2446. {
  2447. CFSShip * pfsShip = (CFSShip *) pShiplink->data()->GetPrivateData();
  2448. if (pfsShip->IsPlayer())
  2449. {
  2450. IsideIGC* pside = pfsShip->GetSide();
  2451. if (pside && pside->GetObjectID() != SIDE_TEAMLOBBY)
  2452. {
  2453. SideID sid = pside->GetObjectID();
  2454. float f = GetExpMult(totalExperience, sideExperience[sid], m_pMission->GetSides()->n());
  2455. if (f < 1.0f)
  2456. {
  2457. PlayerScoreObject* ppso = pfsShip->GetPlayerScoreObject();
  2458. ppso->SetScore(ppso->GetScore() * f);
  2459. }
  2460. }
  2461. }
  2462. }
  2463. for (OldPlayerLink* popl = m_oldPlayers.first(); (popl != NULL); popl = popl->next())
  2464. {
  2465. OldPlayerInfo & opi = popl->data();
  2466. if (opi.sideID != SIDE_TEAMLOBBY)
  2467. {
  2468. SideID sid = opi.sideID;
  2469. float f = GetExpMult(totalExperience, sideExperience[sid], m_pMission->GetSides()->n());
  2470. if (f < 1.0f)
  2471. {
  2472. opi.pso.SetScore(opi.pso.GetScore() * f);
  2473. }
  2474. }
  2475. }
  2476. }
  2477. // award points for commanding
  2478. {
  2479. for (SideLinkIGC* psl = m_pMission->GetSides()->first(); (psl != NULL); psl = psl->next())
  2480. {
  2481. IsideIGC* pside = psl->data();
  2482. SideID sideID = pside->GetObjectID();
  2483. PlayerScoreObject* ppso = commander[sideID];
  2484. if (ppso)
  2485. {
  2486. assert (dtPlayed[sideID] >= 0.0f);
  2487. //Average points earned per player minute
  2488. float commandScore = scoreEarned[sideID] / dtPlayed[sideID];
  2489. {
  2490. float f = GetExpMult(totalExperience, sideExperience[sideID], m_pMission->GetSides()->n());
  2491. if (f < 1.0f)
  2492. {
  2493. commandScore *= f;
  2494. }
  2495. }
  2496. ppso->SetCommanderScore(commandScore * ppso->GetTimePlayed());
  2497. }
  2498. }
  2499. }
  2500. //Save player scores
  2501. {
  2502. for (pShiplink = pShips->first(); pShiplink; pShiplink = pShiplink->next())
  2503. {
  2504. CFSShip * pfsShip = (CFSShip *) pShiplink->data()->GetPrivateData();
  2505. if (pfsShip->IsPlayer())
  2506. {
  2507. IsideIGC* pside = pfsShip->GetSide();
  2508. if (pside && pside->GetObjectID() != SIDE_TEAMLOBBY)
  2509. {
  2510. PlayerScoreObject* ppso = pfsShip->GetPlayerScoreObject();
  2511. SetCharStats(pfsShip->GetPlayer()->GetCharacterID(), pfsShip->GetPlayer(), pside, *ppso, this);
  2512. }
  2513. }
  2514. }
  2515. }
  2516. {
  2517. for (OldPlayerLink* popl = m_oldPlayers.first(); (popl != NULL); popl = popl->next())
  2518. {
  2519. OldPlayerInfo & opi = popl->data();
  2520. if (opi.sideID != SIDE_TEAMLOBBY)
  2521. SetCharStats(opi.characterID, NULL, GetIGCMission()->GetSide(opi.sideID), opi.pso, this);
  2522. }
  2523. }
  2524. // if this was a squads game, record the wins and losses for the squads
  2525. if (m_misdef.misparms.bSquadGame && m_misdef.misparms.bScoresCount && m_psideWon
  2526. && m_misdef.misparms.nTeams == 2
  2527. && m_pMission->GetSide(0)->GetSquadID() != m_pMission->GetSide(1)->GetSquadID())
  2528. {
  2529. RecordSquadGame(m_pMission->GetSides(), m_psideWon);
  2530. }
  2531. // Record the Game Results
  2532. if (m_misdef.misparms.bScoresCount) // Only if scores count
  2533. {
  2534. RecordGameResults();
  2535. }
  2536. // Queue the GameOver message to all connected users
  2537. g.fm.SetDefaultRecipient(GetGroupMission(), FM_GUARANTEED);
  2538. QueueGameoverMessage();
  2539. g.fm.SendMessages(GetGroupMission(), FM_GUARANTEED, FM_FLUSH);
  2540. {
  2541. for (pShiplink = pShips->first(); pShiplink; pShiplink = pShiplink->next())
  2542. {
  2543. CFSShip * pfsShip = (CFSShip *) pShiplink->data()->GetPrivateData();
  2544. if (pfsShip->IsPlayer())
  2545. {
  2546. pfsShip->GetPlayer()->SetLifepod(NULL, Vector::GetZero());
  2547. PlayerScoreObject* ppso = pfsShip->GetPlayerScoreObject();
  2548. ppso->Reset(true);
  2549. }
  2550. }
  2551. }
  2552. // Purge the old players list
  2553. m_oldPlayers.purge();
  2554. // clear the clusters and destroy the drones
  2555. Vacate();
  2556. // kill any pending votes
  2557. while (!m_ballots.IsEmpty())
  2558. delete m_ballots.PopFront();
  2559. /*GetSite()->SendChatf(NULL, CHAT_EVERYONE, NA,
  2560. NA, "The game is over. %s are victorious!.",
  2561. m_psideWon->GetName());
  2562. */
  2563. // set all of the players to unready
  2564. for (pShiplink = pShips->first(); pShiplink; pShiplink = pShiplink->next())
  2565. {
  2566. CFSShip * pfsShip = (CFSShip *) pShiplink->data()->GetPrivateData();
  2567. assert(pfsShip->IsPlayer());
  2568. // Review: since ready has changed to this cheezy "away from keyboard" thing,
  2569. // set everyone as not away from keyboard.
  2570. if (pfsShip->GetSide()->GetObjectID() != SIDE_TEAMLOBBY)
  2571. pfsShip->GetPlayer()->SetReady(true);
  2572. }
  2573. // Restart the game if the server is not paused.
  2574. bool bRestartable = !g.fPaused && m_misdef.misparms.bAllowRestart;
  2575. #if defined(ALLSRV_STANDALONE)
  2576. // HACK: for training missions, end the game and don't let it restart.
  2577. if (m_misdef.misparms.nTotalMaxPlayersPerGame == 1)
  2578. bRestartable = false;
  2579. #endif
  2580. SetStage(bRestartable ? STAGE_NOTSTARTED : STAGE_OVER); // set to STAGE_OVER if game should not restart.
  2581. // set all of the sides to active and not forced ready
  2582. for (SideID sideID = 0; sideID < m_misdef.misparms.nTeams; sideID++)
  2583. {
  2584. IsideIGC* pside = m_pMission->GetSide(sideID);
  2585. // break up teams that no longer have the leadership they need
  2586. if (pside->GetSquadID() != NA)
  2587. MaintainSquadLeadership(sideID);
  2588. m_misdef.rgfReady [sideID] = false;
  2589. m_misdef.rgfForceReady [sideID] = false;
  2590. m_misdef.rgfActive [sideID] = true;
  2591. m_rgMoney [sideID] = 0;
  2592. pside->SetActiveF(true);
  2593. CheckForSideAllReady(pside);
  2594. }
  2595. // reset the clusters, etc.
  2596. for (SideLinkIGC* psl = m_pMission->GetSides()->first(); (psl != NULL); psl = psl->next())
  2597. psl->data()->Reset();
  2598. m_pMission->ResetMission();
  2599. LPCSTR pszContext = GetIGCMission() ? GetIGCMission()->GetContextName() : NULL;
  2600. _AGCModule.TriggerContextEvent(NULL, AllsrvEventID_GameOver, pszContext,
  2601. "", -1, -1, -1, 0);
  2602. // if the game is an auto-restart game, reset the start time.
  2603. if (bRestartable && m_misdef.misparms.bAutoRestart)
  2604. {
  2605. m_misdef.misparms.timeStart = Time::Now() + m_misdef.misparms.fRestartCountdown;
  2606. // forward the new start time to everyone
  2607. BEGIN_PFM_CREATE(g.fm, pfmStartTime, S, SET_START_TIME)
  2608. END_PFM_CREATE
  2609. pfmStartTime->timeStart = m_misdef.misparms.timeStart;
  2610. g.fm.SendMessages(GetGroupMission(), FM_GUARANTEED, FM_FLUSH);
  2611. }
  2612. }
  2613. /*-------------------------------------------------------------------------
  2614. * CheckForVictory
  2615. *-------------------------------------------------------------------------
  2616. * Purpose:
  2617. * See if a side has won and handle it if they have
  2618. */
  2619. IsideIGC* CFSMission::CheckForVictoryByStationBuild(IsideIGC* psideTest)
  2620. {
  2621. return CheckForVictoryByStationKill(NULL, NULL);
  2622. }
  2623. IsideIGC* CFSMission::CheckForVictoryByStationCapture(IsideIGC* psideTest, IsideIGC* psideOld)
  2624. {
  2625. return CheckForVictoryByStationKill(NULL, psideOld);
  2626. }
  2627. IsideIGC* CFSMission::CheckForVictoryByStationKill(IstationIGC* pstationKilled, IsideIGC* psideOld)
  2628. {
  2629. IsideIGC* psideWon = NULL;
  2630. if ((STAGE_STARTED == GetStage()) && (m_psideWon == NULL))
  2631. {
  2632. const SideListIGC* psides = m_pMission->GetSides();
  2633. const MissionParams* pmp = m_pMission->GetMissionParams();
  2634. bool bChange = false;
  2635. if (pmp->IsConquestGame())
  2636. {
  2637. int nStationsTotal = 0;
  2638. assert (c_cSidesMax == 6);
  2639. int nStationsPerSide[c_cSidesMax] = {0, 0, 0, 0, 0, 0};
  2640. {
  2641. for (StationLinkIGC* psl = m_pMission->GetStations()->first();
  2642. (psl != NULL);
  2643. psl = psl->next())
  2644. {
  2645. IstationIGC* pstation = psl->data();
  2646. if ((pstation != pstationKilled) && pstation->GetBaseStationType()->HasCapability(c_sabmFlag))
  2647. {
  2648. nStationsTotal++;
  2649. nStationsPerSide[pstation->GetSide()->GetObjectID()]++;
  2650. }
  2651. }
  2652. }
  2653. if (nStationsTotal != 0)
  2654. {
  2655. const SideListIGC* psides = m_pMission->GetSides();
  2656. for (SideLinkIGC* l = psides->first(); (l != NULL); l = l->next())
  2657. {
  2658. IsideIGC* pside = l->data();
  2659. unsigned char conquest = (unsigned char)(100 * nStationsPerSide[pside->GetObjectID()] / nStationsTotal);
  2660. if (conquest != pside->GetConquestPercent())
  2661. {
  2662. bChange = true;
  2663. pside->SetConquestPercent(conquest);
  2664. if (conquest >= pmp->iGoalConquestPercentage)
  2665. psideWon = pside;
  2666. }
  2667. }
  2668. }
  2669. }
  2670. if (pmp->IsTerritoryGame() && (psideWon == NULL))
  2671. {
  2672. assert (c_cSidesMax == 6);
  2673. unsigned char nTerritoriesPerSide[c_cSidesMax] = {0, 0, 0, 0, 0, 0};
  2674. const ClusterListIGC* pclusters = m_pMission->GetClusters();
  2675. for (ClusterLinkIGC* pcl = pclusters->first(); (pcl != NULL); pcl = pcl->next())
  2676. {
  2677. IclusterIGC* pcluster = pcl->data();
  2678. IsideIGC* psideOwner = NULL;
  2679. StationLinkIGC* psl;
  2680. for (psl = pcluster->GetStations()->first(); (psl != NULL); psl = psl->next())
  2681. {
  2682. if (psl->data() != pstationKilled)
  2683. {
  2684. IsideIGC* pside = psl->data()->GetSide();
  2685. if (psideOwner == NULL)
  2686. psideOwner = pside;
  2687. else if (psideOwner != pside)
  2688. break;
  2689. }
  2690. }
  2691. if (psideOwner && (psl == NULL))
  2692. nTerritoriesPerSide[psideOwner->GetObjectID()]++;
  2693. }
  2694. unsigned char nThreashold = (unsigned char)((50 + pclusters->n() * pmp->iGoalTerritoryPercentage) / 100);
  2695. for (SideLinkIGC* l = psides->first(); (l != NULL); l = l->next())
  2696. {
  2697. IsideIGC* pside = l->data();
  2698. SideID sideID = pside->GetObjectID();
  2699. if (nTerritoriesPerSide[sideID] != pside->GetTerritoryCount())
  2700. {
  2701. bChange = true;
  2702. pside->SetTerritoryCount(nTerritoriesPerSide[sideID]);
  2703. if (nTerritoriesPerSide[sideID] >= nThreashold)
  2704. psideWon = pside;
  2705. }
  2706. }
  2707. }
  2708. //Broadcast the victory rankings to all sides
  2709. if (bChange)
  2710. {
  2711. BEGIN_PFM_CREATE(g.fm, pfmGameState, S, GAME_STATE)
  2712. END_PFM_CREATE
  2713. SideID sid = 0;
  2714. for (SideLinkIGC* l = psides->first(); (l != NULL); l = l->next())
  2715. {
  2716. assert (sid == l->data()->GetObjectID());
  2717. pfmGameState->conquest[sid] = l->data()->GetConquestPercent();
  2718. pfmGameState->territory[sid] = l->data()->GetTerritoryCount();
  2719. pfmGameState->nFlags[sid] = l->data()->GetFlags();
  2720. pfmGameState->nArtifacts[sid++] = l->data()->GetArtifacts();
  2721. }
  2722. g.fm.SendMessages(GetGroupRealSides(), FM_GUARANTEED, FM_FLUSH);
  2723. }
  2724. //Elimiation of all restart stations of all other sides is always a win
  2725. if ((psideWon == NULL) && psideOld)
  2726. {
  2727. StationLinkIGC* psl;
  2728. for (psl = psideOld->GetStations()->first();
  2729. (psl != NULL);
  2730. psl = psl->next())
  2731. {
  2732. IstationIGC* pstation = psl->data();
  2733. if ((pstation != pstationKilled) && pstation->GetStationType()->HasCapability(c_sabmRestart))
  2734. break;
  2735. }
  2736. if (psl == NULL)
  2737. {
  2738. //NYI last station gone ... need to deactivate side
  2739. DeactivateSide(psideOld);
  2740. }
  2741. }
  2742. }
  2743. return psideWon;
  2744. }
  2745. IsideIGC* CFSMission::CheckForVictoryByKills(IsideIGC* psideTest)
  2746. {
  2747. if ((STAGE_STARTED == GetStage()) && (m_psideWon == NULL))
  2748. {
  2749. const MissionParams* pmp = m_pMission->GetMissionParams();
  2750. if (pmp->IsDeathMatchGame())
  2751. {
  2752. if (psideTest->GetKills() >= pmp->nGoalTeamKills)
  2753. {
  2754. return psideTest;
  2755. }
  2756. }
  2757. }
  2758. return NULL;
  2759. }
  2760. IsideIGC* CFSMission::CheckForVictoryByInactiveSides(bool& bAllSidesInactive)
  2761. {
  2762. bAllSidesInactive = true;
  2763. IsideIGC* pSideWin = NULL;
  2764. const SideListIGC * plistSide = m_pMission->GetSides();
  2765. for (SideLinkIGC * plinkSide = plistSide->first(); plinkSide; plinkSide = plinkSide->next())
  2766. {
  2767. IsideIGC * pside = plinkSide->data();
  2768. if (HasPlayers(pside, false) && pside->GetActiveF()) // found one
  2769. {
  2770. bAllSidesInactive = false;
  2771. if (pSideWin)
  2772. {
  2773. pSideWin = NULL;
  2774. break;
  2775. }
  2776. else
  2777. pSideWin = pside;
  2778. }
  2779. }
  2780. #if defined(ALLSRV_STANDALONE)
  2781. // HACK: for training missions, don't end the game before it starts just
  2782. // because a side is inactive.
  2783. if (m_misdef.misparms.nTotalMaxPlayersPerGame == 1 && GetStage() == STAGE_STARTING)
  2784. {
  2785. pSideWin = NULL;
  2786. }
  2787. #endif
  2788. // HACK: for testing purposes, don't end the game before it's started if
  2789. // everyone still playing can cheat.
  2790. if (pSideWin && GetStage() == STAGE_STARTING)
  2791. {
  2792. bool bFoundNormalPlayer = false;
  2793. const ShipListIGC* pships = m_pMission->GetShips();
  2794. for (ShipLinkIGC* psl = pships->first(); (psl != NULL); psl = psl->next())
  2795. {
  2796. CFSShip* pfsShip = (CFSShip *)(psl->data()->GetPrivateData());
  2797. if (pfsShip->IsPlayer() && !pfsShip->GetPlayer()->CanCheat())
  2798. {
  2799. bFoundNormalPlayer = true;
  2800. break;
  2801. }
  2802. }
  2803. if (!bFoundNormalPlayer)
  2804. pSideWin = NULL;
  2805. }
  2806. return pSideWin;
  2807. }
  2808. IsideIGC* CFSMission::CheckForVictoryByFlags(IsideIGC* psideTest, SideID sidFlag)
  2809. {
  2810. if ((STAGE_STARTED == GetStage()) && (m_psideWon == NULL))
  2811. {
  2812. const MissionParams* pmp = m_pMission->GetMissionParams();
  2813. if (pmp->IsArtifactsGame() || pmp->IsFlagsGame())
  2814. {
  2815. BEGIN_PFM_CREATE(g.fm, pfmGameState, S, GAME_STATE)
  2816. END_PFM_CREATE
  2817. SideID sid = 0;
  2818. for (SideLinkIGC* l = m_pMission->GetSides()->first(); (l != NULL); l = l->next())
  2819. {
  2820. assert (sid == l->data()->GetObjectID());
  2821. pfmGameState->conquest[sid] = l->data()->GetConquestPercent();
  2822. pfmGameState->territory[sid] = l->data()->GetTerritoryCount();
  2823. pfmGameState->nFlags[sid] = l->data()->GetFlags();
  2824. pfmGameState->nArtifacts[sid++] = l->data()->GetArtifacts();
  2825. }
  2826. g.fm.SendMessages(GetGroupRealSides(), FM_GUARANTEED, FM_FLUSH);
  2827. if (sidFlag != SIDE_TEAMLOBBY)
  2828. {
  2829. if (psideTest->GetFlags() >= pmp->nGoalFlagsCount)
  2830. {
  2831. return psideTest;
  2832. }
  2833. }
  2834. else
  2835. {
  2836. if (psideTest->GetArtifacts() >= pmp->nGoalArtifactsCount)
  2837. {
  2838. return psideTest;
  2839. }
  2840. }
  2841. }
  2842. }
  2843. return NULL;
  2844. }
  2845. /*-------------------------------------------------------------------------
  2846. * CreateDPGroups
  2847. *-------------------------------------------------------------------------
  2848. * Purpose:
  2849. * create the groups for the cluster, and attach it to the IGC cluster
  2850. *
  2851. * Side Effects:
  2852. * These groups must be cleaned up manually before destroying the cluster!
  2853. */
  2854. void CFSMission::CreateDPGroups(IclusterIGC * pcluster)
  2855. {
  2856. ClusterGroups * pcg = new ClusterGroups;
  2857. char szDocked[] = "Everyone docked in sector ";
  2858. char szFlying[] = "Everyone flying in sector ";
  2859. char szBuff[max(sizeof(szDocked), sizeof(szFlying)) + c_cbName + 1];
  2860. wsprintf(szBuff, "%s%s", szDocked, pcluster->GetName());
  2861. pcg->pgrpClusterDocked = g.fm.CreateGroup(szBuff);
  2862. wsprintf(szBuff, "%s%s", szDocked, pcluster->GetName());
  2863. pcg->pgrpClusterFlying = g.fm.CreateGroup(szBuff);
  2864. ((CFSCluster*)pcluster->GetPrivateData())->SetClusterGroups(pcg);
  2865. }
  2866. /*-------------------------------------------------------------------------
  2867. * SendLobbyMissionInfo
  2868. *-------------------------------------------------------------------------
  2869. * Purpose:
  2870. * Send the player only the data they need while in the lobby. This
  2871. * does not include things like station and sector info.
  2872. *
  2873. * Parameters:
  2874. * Who the junk goes to
  2875. */
  2876. void CFSMission::SendLobbyMissionInfo(CFSPlayer * pfsPlayer)
  2877. {
  2878. ImissionIGC * pMission = GetIGCMission();
  2879. g.fm.SetDefaultRecipient((CFMRecipient*)(pfsPlayer->GetConnection()),
  2880. FM_GUARANTEED);
  2881. // tell them about the current mission info
  2882. g.fm.QueueExistingMsg(GetMissionDef());
  2883. // tell them that they have been accepted
  2884. BEGIN_PFM_CREATE(g.fm, pfmJoinedMission, S, JOINED_MISSION)
  2885. END_PFM_CREATE
  2886. pfmJoinedMission->shipID = pfsPlayer->GetShipID();
  2887. pfmJoinedMission->dwCookie = GetCookie();
  2888. // Export sides
  2889. const SideListIGC * pstlist = pMission->GetSides();
  2890. SideLinkIGC * pstlink;
  2891. for (pstlink = pstlist->first(); pstlink; pstlink = pstlink->next())
  2892. ExportObj(pstlink->data(), OT_side, NULL);
  2893. g.fm.SendMessages(NULL, FM_GUARANTEED, FM_FLUSH);
  2894. // tell them about all of the players
  2895. const ShipListIGC * pshiplist = pMission->GetShips();
  2896. for (ShipLinkIGC * pshiplink = pshiplist->first();
  2897. pshiplink;
  2898. pshiplink = pshiplink->next())
  2899. {
  2900. IshipIGC* pship = pshiplink->data();
  2901. CFSShip * pfsShip = (CFSShip *)(pship->GetPrivateData());
  2902. SendPlayerInfo(pfsPlayer, pfsShip, this, false);
  2903. }
  2904. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  2905. }
  2906. /*-------------------------------------------------------------------------
  2907. * QueueLobbyMissionInfo
  2908. *-------------------------------------------------------------------------
  2909. * Purpose:
  2910. * Queue up a shorter form of the mission def which is used for players
  2911. * who have not yet chosen a game.
  2912. *
  2913. * Parameters:
  2914. */
  2915. void CFSMission::QueueLobbyMissionInfo()
  2916. {
  2917. assert(g.fmLobby.IsConnected());
  2918. SquadID rgSquadIDs[c_cSidesMax];
  2919. int nSquadCount = 0;
  2920. // fill in the squad info for any squads that are playing
  2921. {
  2922. const SideListIGC * plistSide = m_pMission->GetSides();
  2923. for (SideLinkIGC * plinkSide = plistSide->first(); plinkSide; plinkSide = plinkSide->next())
  2924. {
  2925. SquadID squadID = plinkSide->data()->GetSquadID();
  2926. if (squadID != NA && 0 != plinkSide->data()->GetShips()->n()
  2927. && (STAGE_STARTED != GetStage() || plinkSide->data()->GetActiveF()))
  2928. {
  2929. rgSquadIDs[nSquadCount] = squadID;
  2930. ++nSquadCount;
  2931. }
  2932. }
  2933. }
  2934. BEGIN_PFM_CREATE(g.fmLobby, pfmLobbyMissionInfo, LS, LOBBYMISSIONINFO)
  2935. FM_VAR_PARM(m_misdef.misparms.strGameName, CB_ZTS)
  2936. FM_VAR_PARM(nSquadCount ? rgSquadIDs : NULL, nSquadCount * sizeof(SquadID))
  2937. FM_VAR_PARM((PCC)m_strDetailsFiles, CB_ZTS)
  2938. END_PFM_CREATE
  2939. pfmLobbyMissionInfo->dwCookie = GetCookie();
  2940. // adjust the clock time to be an offset from the current time (the lobby server will fix this)
  2941. pfmLobbyMissionInfo->dwStartTime = m_misdef.misparms.timeStart.clock() - Time::Now().clock();
  2942. // fill in the information on number of players and slots available
  2943. pfmLobbyMissionInfo->fGuaranteedSlotsAvailable = false;
  2944. pfmLobbyMissionInfo->fAnySlotsAvailable = false;
  2945. pfmLobbyMissionInfo->nNumPlayers = GetCountOfPlayers(NULL, false);
  2946. // if there might be a chance a player can join, check the teams to see if they can
  2947. if ((m_misdef.misparms.bAllowJoiners || STAGE_NOTSTARTED == GetStage())
  2948. && !m_misdef.misparms.bLockLobby && STAGE_OVER != GetStage())
  2949. {
  2950. const SideListIGC * plistSide = m_pMission->GetSides();
  2951. for (SideLinkIGC * plinkSide = plistSide->first(); plinkSide; plinkSide = plinkSide->next())
  2952. {
  2953. IsideIGC * pside = plinkSide->data();
  2954. int nNumPlayersOnSide = GetCountOfPlayers(pside, false);
  2955. int nAvailablePositions = m_misdef.misparms.nMaxPlayersPerTeam - nNumPlayersOnSide;
  2956. // if it is possible to join this side...
  2957. if ((STAGE_NOTSTARTED == GetStage() || (GetBase(pside) && pside->GetActiveF()))
  2958. && nAvailablePositions && STAGE_OVER != GetStage())
  2959. {
  2960. // track that
  2961. pfmLobbyMissionInfo->fAnySlotsAvailable = true;
  2962. // if a player is guaranteed to get in, track that too.
  2963. if (GetAutoAccept(pside)
  2964. || (m_misdef.fAutoAcceptLeaders && !GetLeader(pside->GetObjectID())))
  2965. {
  2966. pfmLobbyMissionInfo->fGuaranteedSlotsAvailable = true;
  2967. }
  2968. }
  2969. }
  2970. }
  2971. pfmLobbyMissionInfo->nTeams = m_misdef.misparms.nTeams;
  2972. pfmLobbyMissionInfo->nMinRank = m_misdef.misparms.iMinRank;
  2973. pfmLobbyMissionInfo->nMaxRank = m_misdef.misparms.iMaxRank;
  2974. pfmLobbyMissionInfo->nMaxPlayersPerGame = min(m_misdef.misparms.nTotalMaxPlayersPerGame,
  2975. m_misdef.misparms.nTeams
  2976. * m_misdef.misparms.nMaxPlayersPerTeam);
  2977. pfmLobbyMissionInfo->nMinPlayersPerTeam = m_misdef.misparms.nMinPlayersPerTeam;
  2978. pfmLobbyMissionInfo->nMaxPlayersPerTeam = m_misdef.misparms.nMaxPlayersPerTeam;
  2979. pfmLobbyMissionInfo->fInProgress = m_misdef.fInProgress;
  2980. pfmLobbyMissionInfo->fCountdownStarted = (GetStage() == STAGE_STARTING
  2981. || (GetStage() == STAGE_NOTSTARTED && m_misdef.misparms.bAutoRestart));
  2982. pfmLobbyMissionInfo->fMSArena = m_misdef.misparms.bObjectModelCreated;
  2983. pfmLobbyMissionInfo->fScoresCount = m_misdef.misparms.bScoresCount;
  2984. pfmLobbyMissionInfo->fInvulnerableStations = m_misdef.misparms.bInvulnerableStations;
  2985. pfmLobbyMissionInfo->fAllowDevelopments = m_misdef.misparms.bAllowDevelopments;
  2986. pfmLobbyMissionInfo->fLimitedLives = m_misdef.misparms.iLives != 0x7fff;
  2987. pfmLobbyMissionInfo->fConquest = m_misdef.misparms.IsConquestGame();
  2988. pfmLobbyMissionInfo->fDeathMatch = m_misdef.misparms.IsDeathMatchGame();
  2989. pfmLobbyMissionInfo->fCountdown = m_misdef.misparms.IsCountdownGame();
  2990. pfmLobbyMissionInfo->fProsperity = m_misdef.misparms.IsProsperityGame();
  2991. pfmLobbyMissionInfo->fArtifacts = m_misdef.misparms.IsArtifactsGame();
  2992. pfmLobbyMissionInfo->fFlags = m_misdef.misparms.IsFlagsGame();
  2993. pfmLobbyMissionInfo->fTerritorial = m_misdef.misparms.IsTerritoryGame();
  2994. pfmLobbyMissionInfo->fSquadGame = m_misdef.misparms.bSquadGame;
  2995. pfmLobbyMissionInfo->fEjectPods = m_misdef.misparms.bEjectPods;
  2996. }
  2997. /*-------------------------------------------------------------------------
  2998. * SendMissionInfo
  2999. *-------------------------------------------------------------------------
  3000. * Purpose:
  3001. * Send the player all the data they need to play in the mission.
  3002. * This is *different* from the info that just defined the mission on the mission board
  3003. *
  3004. * Parameters:
  3005. * Who the junk goes to (if pfsPlayer is NULL, send it to the entire side)
  3006. */
  3007. void CFSMission::SendMissionInfo(CFSPlayer * pfsPlayer, IsideIGC* pside)
  3008. {
  3009. ImissionIGC * pMission = GetIGCMission();
  3010. g.fm.SetDefaultRecipient(pfsPlayer ?
  3011. (CFMRecipient*) pfsPlayer->GetConnection() :
  3012. (CFMRecipient*) CFSSide::FromIGC(pside)->GetGroup(),
  3013. FM_GUARANTEED);
  3014. SideID sideID = pside->GetObjectID();
  3015. // Send all clusters, and what that side sees in them
  3016. const ClusterListIGC * pclstlist = pMission->GetClusters();
  3017. ClusterLinkIGC * pclstlink;
  3018. for (pclstlink = pclstlist->first(); pclstlink; pclstlink = pclstlink->next())
  3019. {
  3020. IclusterIGC * pcluster = pclstlink->data();
  3021. ExportObj(pcluster, OT_cluster, NULL);
  3022. // Export alephs
  3023. {
  3024. const WarpListIGC * pwarplist = pcluster->GetWarps();
  3025. WarpLinkIGC * pwarplink;
  3026. for (pwarplink = pwarplist->first(); pwarplink; pwarplink = pwarplink->next())
  3027. {
  3028. if (pwarplink->data()->SeenBySide(pside))
  3029. ExportObj(pwarplink->data(), OT_warp, NULL);
  3030. }
  3031. // Export aleph bombs (get all alephs first so all alephs have destinations)
  3032. for (pwarplink = pwarplist->first(); pwarplink; pwarplink = pwarplink->next())
  3033. {
  3034. const WarpBombList* plist = pwarplink->data()->GetBombs();
  3035. if (plist->first() && (pwarplink->data()->SeenBySide(pside)))
  3036. for (WarpBombLink* p = plist->first(); (p != NULL); p = p->next())
  3037. {
  3038. BEGIN_PFM_CREATE(g.fm, pfmWB, S, WARP_BOMB)
  3039. END_PFM_CREATE
  3040. pfmWB->timeExplosion = p->data().timeExplosion;
  3041. pfmWB->warpidBombed = pwarplink->data()->GetObjectID();
  3042. pfmWB->expendableidMissile = p->data().pmt->GetObjectID();
  3043. }
  3044. }
  3045. }
  3046. // Export planets
  3047. const AsteroidListIGC * pmdllist = pcluster->GetAsteroids();
  3048. AsteroidLinkIGC * pmdllink;
  3049. for (pmdllink = pmdllist->first(); pmdllink; pmdllink = pmdllink->next())
  3050. {
  3051. if (pmdllink->data()->SeenBySide(pside))
  3052. {
  3053. ExportObj(pmdllink->data(), OT_asteroid, NULL);
  3054. IbuildingEffectIGC* pbe = pmdllink->data()->GetBuildingEffect();
  3055. if (pbe)
  3056. ExportObj(pbe, OT_buildingEffect, NULL);
  3057. }
  3058. }
  3059. // Export stations
  3060. const StationListIGC * pstnlist = pcluster->GetStations();
  3061. StationLinkIGC * pstnlink;
  3062. for (pstnlink = pstnlist->first(); pstnlink; pstnlink = pstnlink->next())
  3063. {
  3064. if (pstnlink->data()->SeenBySide(pside))
  3065. ExportObj(pstnlink->data(), OT_station, NULL);
  3066. }
  3067. // Export treasure
  3068. const TreasureListIGC * ptlist = pcluster->GetTreasures();
  3069. TreasureLinkIGC * ptlink;
  3070. for (ptlink = ptlist->first(); ptlink; ptlink = ptlink->next())
  3071. {
  3072. if (ptlink->data()->SeenBySide(pside))
  3073. ExportObj(ptlink->data(), OT_treasure, NULL);
  3074. }
  3075. // Export mines
  3076. const MineListIGC * pmlist = pcluster->GetMines();
  3077. MineLinkIGC * pmlink;
  3078. for (pmlink = pmlist->first(); pmlink; pmlink = pmlink->next())
  3079. {
  3080. if (pmlink->data()->SeenBySide(pside))
  3081. ExportObj(pmlink->data(), OT_mine, NULL);
  3082. }
  3083. // Export probes
  3084. const ProbeListIGC * pplist = pcluster->GetProbes();
  3085. ProbeLinkIGC * pplink;
  3086. for (pplink = pplist->first(); pplink; pplink = pplink->next())
  3087. {
  3088. if (pplink->data()->SeenBySide(pside))
  3089. ExportObj(pplink->data(), OT_probe, NULL);
  3090. }
  3091. }
  3092. //For the player's side, update the money & completion state of all buckets
  3093. BEGIN_PFM_CREATE(g.fm, pfmCreateBuckets, S, CREATE_BUCKETS)
  3094. END_PFM_CREATE
  3095. pfmCreateBuckets->ttbmDevelopments = pside->GetDevelopmentTechs();
  3096. pfmCreateBuckets->ttbmInitial = pside->GetInitialTechs();
  3097. {
  3098. for (BucketLinkIGC* pbl = pside->GetBuckets()->first(); (pbl != NULL); pbl = pbl->next())
  3099. {
  3100. IbucketIGC* pbucket = pbl->data();
  3101. {
  3102. BEGIN_PFM_CREATE(g.fm, pfmBucketStatus, S, BUCKET_STATUS)
  3103. END_PFM_CREATE
  3104. pfmBucketStatus->timeTotal = pbucket->GetTime();
  3105. pfmBucketStatus->moneyTotal = pbucket->GetMoney();
  3106. pfmBucketStatus->iBucket = pbucket->GetObjectID();
  3107. pfmBucketStatus->sideID = sideID;
  3108. }
  3109. }
  3110. }
  3111. //Tell the player the money & autodonate for every other person on the side
  3112. for (ShipLinkIGC* psl = pside->GetShips()->first(); (psl != NULL); psl = psl->next())
  3113. {
  3114. IshipIGC* ps = psl->data();
  3115. if (!pfsPlayer || ps != pfsPlayer->GetIGCShip())
  3116. {
  3117. CFSShip* pcs = (CFSShip *)(ps->GetPrivateData());
  3118. if (pcs->IsPlayer())
  3119. {
  3120. CFSPlayer* pcp = pcs->GetPlayer();
  3121. IshipIGC* pshipDonate = ps->GetAutoDonate();
  3122. ShipID shipID = ps->GetObjectID();
  3123. BEGIN_PFM_CREATE(g.fm, pfmDonate, S, AUTODONATE)
  3124. END_PFM_CREATE
  3125. pfmDonate->sidDonateTo = pshipDonate ? pshipDonate->GetObjectID() : NA;
  3126. pfmDonate->sidDonateBy = shipID;
  3127. pfmDonate->amount = 0;
  3128. BEGIN_PFM_CREATE(g.fm, pfmMoney, S, SET_MONEY)
  3129. END_PFM_CREATE
  3130. pfmMoney->shipID = shipID;
  3131. pfmMoney->money = pcp->GetMoney();
  3132. }
  3133. }
  3134. }
  3135. // send the data on all known ships
  3136. {
  3137. const ShipListIGC * plistShip = m_pMission->GetShips();
  3138. for (ShipLinkIGC * plinkShip = plistShip->first(); plinkShip; plinkShip = plinkShip->next())
  3139. {
  3140. IshipIGC* pship = plinkShip->data();
  3141. if (pship->GetSide()->GetObjectID() >= 0)
  3142. {
  3143. CFSShip* pcs = (CFSShip *)(pship->GetPrivateData());
  3144. ShipStatus* pss = pcs->GetShipStatus(sideID);
  3145. //We know something about the ship, even if it is obsolete info
  3146. BEGIN_PFM_CREATE(g.fm, pfmShipStatus, S, SHIP_STATUS)
  3147. END_PFM_CREATE
  3148. pfmShipStatus->shipID = pship->GetObjectID();
  3149. pfmShipStatus->status = *pss;
  3150. }
  3151. }
  3152. }
  3153. {
  3154. //Broadcast the victory rankings to all sides
  3155. BEGIN_PFM_CREATE(g.fm, pfmGameState, S, GAME_STATE)
  3156. END_PFM_CREATE
  3157. SideID sid = 0;
  3158. for (SideLinkIGC* l = m_pMission->GetSides()->first(); (l != NULL); l = l->next())
  3159. {
  3160. assert (sid == l->data()->GetObjectID());
  3161. pfmGameState->conquest[sid] = l->data()->GetConquestPercent();
  3162. pfmGameState->territory[sid] = l->data()->GetTerritoryCount();
  3163. pfmGameState->nFlags[sid] = l->data()->GetFlags();
  3164. pfmGameState->nArtifacts[sid++] = l->data()->GetArtifacts();
  3165. }
  3166. }
  3167. g.fm.SendMessages(NULL, FM_GUARANTEED, FM_FLUSH); // default recipient
  3168. }
  3169. CFSMission * CFSMission::GetMission(DWORD dwCookie)
  3170. {
  3171. for (LinkFSMission * plinkFSMis = s_list.first(); plinkFSMis; plinkFSMis = plinkFSMis->next())
  3172. {
  3173. CFSMission * pfsMission = plinkFSMis->data();
  3174. if (pfsMission->GetCookie() == dwCookie)
  3175. return pfsMission;
  3176. }
  3177. return NULL;
  3178. }
  3179. CFSMission * CFSMission::GetMissionFromIGCMissionID(DWORD dwIGCMissionID)
  3180. {
  3181. for (LinkFSMission * plinkFSMis = s_list.first(); plinkFSMis; plinkFSMis = plinkFSMis->next())
  3182. {
  3183. CFSMission * pfsMission = plinkFSMis->data();
  3184. if (pfsMission->m_pMission->GetMissionID() == dwIGCMissionID)
  3185. return pfsMission;
  3186. }
  3187. return NULL;
  3188. }
  3189. /*-------------------------------------------------------------------------
  3190. * FAllReady
  3191. *-------------------------------------------------------------------------
  3192. * Purpose:
  3193. * See if all teams are ready, and if so, tell them to start
  3194. */
  3195. bool CFSMission::FAllReady()
  3196. {
  3197. {
  3198. //Not everyone is ready if sides are imbalanced
  3199. int minPlayers;
  3200. int maxPlayers;
  3201. SideLinkIGC* psl = m_pMission->GetSides()->first();
  3202. assert (psl);
  3203. minPlayers = maxPlayers = psl->data()->GetShips()->n();
  3204. while (true)
  3205. {
  3206. psl = psl->next();
  3207. if (psl == NULL)
  3208. break;
  3209. int n = psl->data()->GetShips()->n();
  3210. if (n < minPlayers)
  3211. minPlayers = n;
  3212. if (n > maxPlayers)
  3213. maxPlayers = n;
  3214. }
  3215. if ((minPlayers < m_misdef.misparms.nMinPlayersPerTeam) ||
  3216. (maxPlayers > m_misdef.misparms.nMaxPlayersPerTeam) ||
  3217. (minPlayers + m_misdef.misparms.iMaxImbalance < maxPlayers))
  3218. return false;
  3219. }
  3220. SideID iSide = m_misdef.misparms.nTeams;
  3221. while (iSide-- > 0 && GetReady(iSide))
  3222. ;
  3223. return iSide < 0;
  3224. }
  3225. /*-------------------------------------------------------------------------
  3226. * PickNewSide
  3227. *-------------------------------------------------------------------------
  3228. * Purpose:
  3229. * pick an available side for a new player (may return SIDE_TEAMLOBBY)
  3230. */
  3231. SideID CFSMission::PickNewSide(CFSPlayer* pfsPlayer, bool bAllowTeamLobby, unsigned char bannedSideMask)
  3232. {
  3233. if (bAllowTeamLobby && (GetStage() > STAGE_NOTSTARTED))
  3234. return SIDE_TEAMLOBBY;
  3235. IsideIGC * psideMostAvailablePositions = NULL;
  3236. int nMostAvailablePositions = 0;
  3237. // find the side with the most positions free that will automatically accept the player
  3238. const SideListIGC * plistSide = m_pMission->GetSides();
  3239. for (SideLinkIGC * plinkSide = plistSide->first(); plinkSide; plinkSide = plinkSide->next())
  3240. {
  3241. IsideIGC* pside = plinkSide->data();
  3242. if ((SideMask(pside) & bannedSideMask) == 0)
  3243. {
  3244. int nNumPlayers = GetCountOfPlayers(pside, false);
  3245. int nAvailablePositions = m_misdef.misparms.nMaxPlayersPerTeam - nNumPlayers;
  3246. if ((CheckPositionRequest(pfsPlayer, pside) == NA)
  3247. && nAvailablePositions > nMostAvailablePositions
  3248. && (GetAutoAccept(pside)
  3249. || (m_misdef.fAutoAcceptLeaders && !GetLeader(pside->GetObjectID()))))
  3250. {
  3251. // in a squad game, favor a team with my squad over an empty team
  3252. if (m_misdef.misparms.bSquadGame && nNumPlayers == 0)
  3253. {
  3254. if (psideMostAvailablePositions == NULL)
  3255. psideMostAvailablePositions = pside;
  3256. }
  3257. else
  3258. {
  3259. nMostAvailablePositions = nAvailablePositions;
  3260. psideMostAvailablePositions = pside;
  3261. }
  3262. }
  3263. }
  3264. }
  3265. // place suggest that side, or the team lobby if no side was found that would accept them
  3266. return psideMostAvailablePositions
  3267. ? psideMostAvailablePositions->GetObjectID()
  3268. : SIDE_TEAMLOBBY;
  3269. }
  3270. /*-------------------------------------------------------------------------
  3271. * PickNewCiv
  3272. *-------------------------------------------------------------------------
  3273. * Purpose:
  3274. * pick a civ for a new team, preferably one that is not in use
  3275. CivID CFSMission::PickNewCiv(SideID nSides, CivID rgCivs[])
  3276. {
  3277. //Pick a random, legal, least used civ
  3278. //Go over all civs and get the minimum count
  3279. int nCivMin;
  3280. int cCivMin = 0x7ffffff;
  3281. const int cCivsMax = 20;
  3282. assert (m_pMission->GetCivilizations()->n() < cCivsMax);
  3283. CivID civIDs[cCivsMax];
  3284. for (pcl = m_pMission->GetCivilizations()->first(); (pcl != NULL); pcl = pcl->next())
  3285. {
  3286. CivID civID = pcl->data()->GetObjectID();
  3287. //NYI hack to prevent special civs
  3288. if (civID < 100)
  3289. {
  3290. int cCiv = 0;
  3291. for (int i = nSides - 1; (i >= 0); i--)
  3292. {
  3293. if (civID == rgCivs[i])
  3294. cCiv++;
  3295. }
  3296. if (cCiv < cCivMin)
  3297. {
  3298. cCivMin = cCiv;
  3299. civIDs[0] = civID;
  3300. nCivMin = 0;
  3301. }
  3302. else if (cCiv == cCivMin)
  3303. {
  3304. civIDs[++nCivMin] = civID;
  3305. }
  3306. }
  3307. }
  3308. assert (cCivMin != 0x7ffffff);
  3309. return civIDs[RandomInt(0, nCivMin)];
  3310. }
  3311. */
  3312. DelPositionReqReason CFSMission::CheckPositionRequest(CFSPlayer * pfsPlayer, IsideIGC * pside)
  3313. {
  3314. assert(pside);
  3315. SideID sideID = pside->GetObjectID();
  3316. const MissionParams* pmp = m_pMission->GetMissionParams();
  3317. IsideIGC* psideCurrent = pfsPlayer->GetSide();
  3318. if (psideCurrent && psideCurrent->GetObjectID() != SIDE_TEAMLOBBY)
  3319. {
  3320. if ((!pmp->bAllowDefections) && (GetStage() == STAGE_STARTED))
  3321. return DPR_NoDefections;
  3322. else if (pmp->bLockSides)
  3323. return DPR_SidesLocked;
  3324. }
  3325. if (sideID != SIDE_TEAMLOBBY)
  3326. {
  3327. if (GetStage() == STAGE_OVER)
  3328. return DPR_ServerPaused;
  3329. if (pfsPlayer->GetBannedSideMask() & SideMask(sideID))
  3330. return DPR_Banned;
  3331. if (RequiresInvitation() && !CFSSide::FromIGC(pside)->IsInvited(pfsPlayer))
  3332. return DPR_PrivateGame;
  3333. int nNumPlayers = GetCountOfPlayers(pside, false);
  3334. int maxPlayers = pmp->nMaxPlayersPerTeam;
  3335. //Can they join the chosen side?
  3336. if ((STAGE_NOTSTARTED != GetStage()) && (pmp->iMaxImbalance != 0x7fff))
  3337. {
  3338. for (SideLinkIGC* psl = m_pMission->GetSides()->first(); (psl != NULL); psl = psl->next())
  3339. {
  3340. if (psl->data()->GetActiveF())
  3341. {
  3342. int count = GetCountOfPlayers(psl->data(), false) + pmp->iMaxImbalance;
  3343. if (count < maxPlayers)
  3344. maxPlayers = count;
  3345. }
  3346. }
  3347. }
  3348. if (STAGE_NOTSTARTED != GetStage())
  3349. {
  3350. if (!GetBase(pside))
  3351. return DPR_NoBase;
  3352. else if (!pside->GetActiveF())
  3353. return DPR_SideDefeated;
  3354. }
  3355. if (GetCountOfPlayers(pside, false) >= pmp->nMaxPlayersPerTeam)
  3356. return DPR_TeamFull;
  3357. else if (nNumPlayers >= maxPlayers)
  3358. return DPR_TeamBalance;
  3359. if (m_misdef.misparms.bSquadGame)
  3360. {
  3361. if (nNumPlayers > 0)
  3362. {
  3363. if (!pfsPlayer->GetIsMemberOfSquad(pside->GetSquadID()))
  3364. return DPR_WrongSquad;
  3365. }
  3366. else
  3367. {
  3368. if (pfsPlayer->GetPreferredSquadToLead() == NA)
  3369. return DPR_CantLeadSquad;
  3370. }
  3371. }
  3372. }
  3373. return (DelPositionReqReason)NA;
  3374. }
  3375. void CFSMission::RequestPosition(CFSPlayer * pfsPlayer, IsideIGC * pside, bool bRejoin)
  3376. {
  3377. assert(pside);
  3378. SideID sideID = pside->GetObjectID();
  3379. const MissionParams* pmp = m_pMission->GetMissionParams();
  3380. DelPositionReqReason reason = CheckPositionRequest(pfsPlayer, pside);
  3381. if (reason != NA)
  3382. {
  3383. //deny the request
  3384. BEGIN_PFM_CREATE(g.fm, pfmDelPosReq, CS, DELPOSITIONREQ)
  3385. END_PFM_CREATE
  3386. pfmDelPosReq->shipID = pfsPlayer->GetShipID();
  3387. pfmDelPosReq->iSide = sideID;
  3388. pfmDelPosReq->reason = reason;
  3389. g.fm.SendMessages(pfsPlayer->GetConnection(), FM_GUARANTEED, FM_FLUSH);
  3390. return;
  3391. }
  3392. if (pfsPlayer->GetSide()->GetObjectID() != SIDE_TEAMLOBBY)
  3393. {
  3394. RemovePlayerFromSide(pfsPlayer, QSR_SwitchingSides);
  3395. }
  3396. if (sideID != SIDE_TEAMLOBBY)
  3397. {
  3398. if ((!GetLeader(sideID) && m_misdef.fAutoAcceptLeaders)
  3399. || bRejoin
  3400. || m_misdef.rgfAutoAccept[sideID])
  3401. {
  3402. AddPlayerToSide(pfsPlayer, pside);
  3403. }
  3404. else // need permission
  3405. {
  3406. BEGIN_PFM_CREATE(g.fm, pfmSPositionReq, S, POSITIONREQ)
  3407. END_PFM_CREATE
  3408. pfmSPositionReq->shipID = pfsPlayer->GetShipID();
  3409. pfmSPositionReq->iSide = sideID;
  3410. g.fm.SendMessages(GetGroupMission(), FM_GUARANTEED, FM_FLUSH);
  3411. AddJoinRequest(pfsPlayer, pside);
  3412. }
  3413. }
  3414. }
  3415. /*-------------------------------------------------------------------------
  3416. * VacateStation
  3417. *-------------------------------------------------------------------------
  3418. * Purpose:
  3419. * Kick everyone out of a station, except the people on the owning side
  3420. */
  3421. void CFSMission::VacateStation(IstationIGC * pstation)
  3422. {
  3423. assert (pstation->GetMission() == m_pMission);
  3424. IsideIGC* psideGhost = NULL;
  3425. //Everyone goes (in the event of a capture: the person capturing has not docked yet)
  3426. const ShipListIGC* pships = pstation->GetShips();
  3427. ShipLinkIGC* pshiplink = pships->first();
  3428. while (pshiplink)
  3429. {
  3430. IshipIGC* pship = pshiplink->data();
  3431. if (pship->GetParentShip() == NULL)
  3432. {
  3433. if (pship->IsGhost())
  3434. {
  3435. //Ghosts ... move the ghosts in the next pass
  3436. psideGhost = pship->GetSide();
  3437. pshiplink = pshiplink->next();
  3438. }
  3439. else
  3440. {
  3441. //Force any kids who haven't undocked to undock
  3442. for (ShipLinkIGC* psl = pship->GetChildShips()->first();
  3443. (psl != NULL);
  3444. psl = psl->next())
  3445. {
  3446. psl->data()->SetStation(NULL);
  3447. }
  3448. pship->SetStation(NULL);
  3449. CFSShip* pfsship = (CFSShip*)(pship->GetPrivateData());
  3450. if (pfsship->IsPlayer())
  3451. pfsship->GetPlayer()->ForceLoadoutChange();
  3452. pshiplink = pships->first();
  3453. }
  3454. }
  3455. else
  3456. pshiplink = pshiplink->next();
  3457. }
  3458. if (psideGhost)
  3459. {
  3460. //Find an alternate base for the ghosts ... if they don't have one, don't worry: side
  3461. //will be eliminated
  3462. for (StationLinkIGC* psl = psideGhost->GetStations()->first(); (psl != NULL); psl = psl->next())
  3463. {
  3464. IstationIGC* ps = psl->data();
  3465. if ((ps != pstation) && ps->GetBaseStationType()->HasCapability(c_sabmRestart))
  3466. {
  3467. ShipLinkIGC* pshiplink = pships->first();
  3468. while (pshiplink)
  3469. {
  3470. IshipIGC* pship = pshiplink->data();
  3471. pshiplink = pshiplink->next();
  3472. assert (pship->GetParentShip() == NULL);
  3473. assert (pship->IsGhost());
  3474. pship->SetStation(ps);
  3475. }
  3476. break;
  3477. }
  3478. }
  3479. }
  3480. }
  3481. /*-------------------------------------------------------------------------
  3482. * AddJoinRequest
  3483. *-------------------------------------------------------------------------
  3484. * Purpose:
  3485. * When a player joins a team that has auto-accept off (or other blockers,
  3486. * such as not having a civ yet), a player is added to the join request list.
  3487. */
  3488. void CFSMission::AddJoinRequest(CFSPlayer * pfsPlayer, IsideIGC * pside)
  3489. {
  3490. JoinRequest * pjr = new JoinRequest;
  3491. pjr->pfsPlayer = pfsPlayer;
  3492. pjr->pSide = pside;
  3493. m_listJoinReq.last(pjr);
  3494. // must go through the lobby to change missions
  3495. assert(pfsPlayer->GetMission() == this);
  3496. }
  3497. /*-------------------------------------------------------------------------
  3498. * NotifyPlayerBoot
  3499. *-------------------------------------------------------------------------
  3500. * Purpose:
  3501. * Mark a player as off the side, both the remote player and the local igc player
  3502. *
  3503. * Paremeters:
  3504. * dwSendBootTo as defined next to declaration of this function in fsmission.h
  3505. */
  3506. void CFSMission::NotifyPlayerBoot(CFSPlayer * pfsPlayer, IsideIGC * pSide)
  3507. {
  3508. // Forward the remove request to everyone
  3509. BEGIN_PFM_CREATE(g.fm, pfmDelPosReq, CS, DELPOSITIONREQ)
  3510. END_PFM_CREATE
  3511. pfmDelPosReq->shipID = pfsPlayer->GetShipID();
  3512. pfmDelPosReq->iSide = pSide->GetObjectID();
  3513. pfmDelPosReq->reason = DPR_Rejected;
  3514. g.fm.SendMessages(GetGroupMission(), FM_GUARANTEED, FM_FLUSH);
  3515. }
  3516. /*-------------------------------------------------------------------------
  3517. * RejectSideJoinRequests
  3518. *-------------------------------------------------------------------------
  3519. * Purpose:
  3520. * Any pending requests on a side are rejected
  3521. *
  3522. * Side Effects:
  3523. * Those people are given the boot
  3524. */
  3525. bool CFSMission::RejectSideJoinRequests(IsideIGC * pSide)
  3526. {
  3527. bool fAny = false;
  3528. // Note: almost the same as loop in SetAutoAccept--maybe should combine 'em
  3529. LinkJoinReq* plinkNext;
  3530. for (LinkJoinReq* plinkJR = m_listJoinReq.first(); (plinkJR != NULL); plinkJR = plinkNext)
  3531. {
  3532. plinkNext = plinkJR->next();
  3533. JoinRequest * pjr = plinkJR->data();
  3534. if (pjr->pSide == pSide)
  3535. {
  3536. NotifyPlayerBoot(pjr->pfsPlayer, pjr->pSide);
  3537. delete plinkJR;
  3538. delete pjr;
  3539. fAny = true;
  3540. }
  3541. }
  3542. return fAny;
  3543. }
  3544. /*-------------------------------------------------------------------------
  3545. * RemoveJoinRequest
  3546. *-------------------------------------------------------------------------
  3547. * Purpose:
  3548. * Remove all stored requests for specified player
  3549. * A player comes off the join list when they are added to a team, or log off
  3550. *
  3551. * Parameters:
  3552. * pfsPlayer: Player to remove, or NULL for all players
  3553. * psideDest: Side the player is going to
  3554. *
  3555. * Returns: whether there were any requests to remove
  3556. */
  3557. bool CFSMission::RemoveJoinRequest(CFSPlayer * pfsPlayer, IsideIGC * psideDest)
  3558. {
  3559. bool fAny = false;
  3560. // Note: almost the same as loop in SetAutoAccept--maybe should combine 'em
  3561. LinkJoinReq* plinkNext;
  3562. for (LinkJoinReq* plinkJR = m_listJoinReq.first(); (plinkJR != NULL); plinkJR = plinkNext)
  3563. {
  3564. plinkNext = plinkJR->next();
  3565. JoinRequest * pjr = plinkJR->data();
  3566. if (pjr->pfsPlayer == pfsPlayer || !pfsPlayer)
  3567. {
  3568. // Let's tell everyone that the request is gone if the player isn't going to a team
  3569. if (!psideDest)
  3570. NotifyPlayerBoot(pjr->pfsPlayer, pjr->pSide);
  3571. delete plinkJR;
  3572. delete pjr;
  3573. fAny = true;
  3574. }
  3575. }
  3576. return fAny;
  3577. }
  3578. /*-------------------------------------------------------------------------
  3579. * SetAutoAccept
  3580. *-------------------------------------------------------------------------
  3581. * Purpose:
  3582. * Turn auto-accept on or off for a side.
  3583. * Slightly bastardized use: pass NULL for pside and false for fAccept to
  3584. * reject all pending requests on all sides
  3585. *
  3586. * Side Effects:
  3587. * If turning it on, any requests on the side are accepted.
  3588. */
  3589. void CFSMission::SetAutoAccept(IsideIGC * pside, bool fAccept)
  3590. {
  3591. assert(IMPLIES(!pside, !fAccept));
  3592. SideID sideID = pside ? pside->GetObjectID() : NA;
  3593. if (pside)
  3594. m_misdef.rgfAutoAccept[sideID] = fAccept;
  3595. // else the mission is going away altogether, so it doesn't matter
  3596. if (pside && fAccept || !pside)
  3597. {
  3598. LinkJoinReq* plinkNext;
  3599. for (LinkJoinReq* plinkJR = m_listJoinReq.first(); (plinkJR != NULL); plinkJR = plinkNext)
  3600. {
  3601. plinkNext = plinkJR->next();
  3602. JoinRequest * pjr = plinkJR->data();
  3603. if (pjr->pSide == pside || !pside)
  3604. {
  3605. delete plinkJR;
  3606. // If anyone has more than one request in, then they'll get more than one side change.
  3607. // That shouldn't happen.
  3608. CFSPlayer* pfsPlayer = pjr->pfsPlayer;
  3609. delete pjr;
  3610. if (pside)
  3611. {
  3612. // should never add the player back to the list, since autoaccept is on
  3613. RequestPosition(pfsPlayer, pside, false);
  3614. }
  3615. }
  3616. }
  3617. }
  3618. }
  3619. /*-------------------------------------------------------------------------
  3620. * SetLockLobby
  3621. *-------------------------------------------------------------------------
  3622. * Purpose:
  3623. * Lock/unlock the lobby for a game.
  3624. *
  3625. * Notes:
  3626. * Does not send the lock lobby message to the clients here, so you have
  3627. * to do it from the call site. Should we change this?
  3628. */
  3629. void CFSMission::SetLockLobby(bool bLock)
  3630. {
  3631. m_misdef.misparms.bLockLobby = bLock;
  3632. m_pMission->SetMissionParams(&m_misdef.misparms);
  3633. SetLobbyIsDirty();
  3634. }
  3635. /*-------------------------------------------------------------------------
  3636. * SetLockSides
  3637. *-------------------------------------------------------------------------
  3638. * Purpose:
  3639. * Lock/unlock the Sides for a game.
  3640. *
  3641. * Notes:
  3642. * Does not send the lock Sides message to the clients here, so you have
  3643. * to do it from the call site. Should we change this?
  3644. */
  3645. void CFSMission::SetLockSides(bool bLock)
  3646. {
  3647. m_misdef.misparms.bLockSides = bLock;
  3648. m_pMission->SetMissionParams(&m_misdef.misparms);
  3649. }
  3650. /*-------------------------------------------------------------------------
  3651. * RandomizeSides
  3652. *-------------------------------------------------------------------------
  3653. * Purpose:
  3654. * Assigns everyone in the mission to a random team except the team leaders
  3655. */
  3656. void CFSMission::RandomizeSides()
  3657. {
  3658. assert(GetStage() == STAGE_NOTSTARTED);
  3659. // Make sure the sides are not locked
  3660. // (no need to tell the clients, since we tell them when we lock the sides again below)
  3661. SetLockSides(false);
  3662. // turn on auto accept for all sides
  3663. {
  3664. for (SideLinkIGC* psl = m_pMission->GetSides()->first(); psl != NULL; psl = psl->next())
  3665. {
  3666. if (!GetAutoAccept(psl->data()))
  3667. {
  3668. SetAutoAccept(psl->data(), true);
  3669. BEGIN_PFM_CREATE(g.fm, pfmAutoAccept, CS, AUTO_ACCEPT)
  3670. END_PFM_CREATE
  3671. pfmAutoAccept->iSide = psl->data()->GetObjectID();
  3672. pfmAutoAccept->fAutoAccept = true;
  3673. g.fm.SendMessages(GetGroupMission(), FM_GUARANTEED, FM_DONT_FLUSH);
  3674. }
  3675. }
  3676. g.fm.PurgeOutBox();
  3677. }
  3678. // move everyone who's not a team leader to the lobby side.
  3679. {
  3680. const ShipListIGC* pships = m_pMission->GetShips();
  3681. ShipLinkIGC* pshipLinkNext;
  3682. for (ShipLinkIGC* pshipLink = pships->first();
  3683. (pshipLink != NULL);
  3684. pshipLink = pshipLinkNext)
  3685. {
  3686. pshipLinkNext = pshipLink->next();
  3687. CFSPlayer* pfsPlayer = ((CFSShip*)(pshipLink->data()->GetPrivateData()))->GetPlayer();
  3688. SideID sideID = pfsPlayer->GetSide()->GetObjectID();
  3689. // clear their banned side mask (it simplifies life)
  3690. pfsPlayer->SetBannedSideMask(0);
  3691. // if they are not already on the lobby side and not a team leader
  3692. if (sideID != SIDE_TEAMLOBBY && GetLeader(sideID) != pfsPlayer)
  3693. {
  3694. RemovePlayerFromSide(pfsPlayer, QSR_RandomizeSides);
  3695. }
  3696. }
  3697. }
  3698. // randomly put everyone back on a team
  3699. {
  3700. IsideIGC* psideLobby = m_pMission->GetSide(SIDE_TEAMLOBBY);
  3701. const ShipListIGC* pshipsLobby = psideLobby->GetShips();
  3702. bool bPickTeamFailed = false;
  3703. // this is O(n^2), but I think n is small enough and it's used rarely
  3704. // enough that it won't matter.
  3705. // randomly pick players and asign them to teams until we hit a player
  3706. // that can't be assigned to a team. At that point, the teams should
  3707. // be full.
  3708. while (pshipsLobby->n() > 0 && !bPickTeamFailed)
  3709. {
  3710. // pick a random player from the lobby side
  3711. CFSPlayer* pfsPlayer;
  3712. {
  3713. int nPlayerIndex = (int)random(0, pshipsLobby->n());
  3714. ShipLinkIGC* pshipLink;
  3715. for (pshipLink = pshipsLobby->first();
  3716. nPlayerIndex > 0;
  3717. pshipLink = pshipLink->next(), --nPlayerIndex)
  3718. {
  3719. }
  3720. pfsPlayer = ((CFSShip*)(pshipLink->data()->GetPrivateData()))->GetPlayer();
  3721. }
  3722. // try to find a side to assign them to.
  3723. SideID sideID = PickNewSide(pfsPlayer, true, pfsPlayer->GetBannedSideMask());
  3724. if (sideID == SIDE_TEAMLOBBY)
  3725. bPickTeamFailed = true;
  3726. else
  3727. AddPlayerToSide(pfsPlayer, m_pMission->GetSide(sideID));
  3728. }
  3729. }
  3730. // then lock the sides.
  3731. {
  3732. SetLockSides(true);
  3733. BEGIN_PFM_CREATE(g.fm, pfmLockSides, CS, LOCK_SIDES)
  3734. END_PFM_CREATE
  3735. pfmLockSides->fLock = true;
  3736. g.fm.SendMessages(GetGroupMission(), FM_GUARANTEED, FM_FLUSH);
  3737. }
  3738. }
  3739. void CFSMission::SetSideCiv(IsideIGC * pside, IcivilizationIGC * pciv)
  3740. {
  3741. pside->SetCivilization(pciv);
  3742. CivID civID = pciv->GetObjectID();
  3743. //m_misdef.rgCivID[pside->GetObjectID()] = civID;
  3744. //Go through all players on the team and adjust their persist score objects
  3745. // Their persist score now becomes their current score, and other stuff is reset
  3746. for (ShipLinkIGC* psl = pside->GetShips()->first(); (psl != NULL); psl = psl->next())
  3747. {
  3748. IshipIGC* pship = psl->data();
  3749. CFSShip* pfsship = (CFSShip*)(pship->GetPrivateData());
  3750. PlayerScoreObject* ppso = pfsship->GetPlayerScoreObject();
  3751. ppso->SetPersist(pfsship->GetPlayer()->GetPersistPlayerScore(civID));
  3752. }
  3753. }
  3754. /*-------------------------------------------------------------------------
  3755. * DeactivateSide
  3756. *-------------------------------------------------------------------------
  3757. * Purpose:
  3758. * Mark a side as inactive and tell everyone about it.
  3759. * Once a side is inactive, no one can join it
  3760. *
  3761. * Parameters:
  3762. * side to deactivate
  3763. */
  3764. void CFSMission::DeactivateSide(IsideIGC * pside)
  3765. {
  3766. if (!m_misdef.misparms.bAllowEmptyTeams)
  3767. {
  3768. assert(pside->GetMission() == m_pMission);
  3769. debugf("DeactivateSide side=%d.\n", pside->GetObjectID());
  3770. pside->SetActiveF(false);
  3771. SideID sideid = pside->GetObjectID();
  3772. m_misdef.rgfActive[sideid] =
  3773. m_misdef.rgfReady[sideid] = false;
  3774. pside->SetTimeEndured(max(0.0f, Time::Now() - m_misdef.misparms.timeStart));
  3775. //Eliminate all of the side's drones
  3776. const ShipListIGC* pships = pside->GetShips();
  3777. {
  3778. ShipLinkIGC* pslNext;
  3779. for (ShipLinkIGC* psl = pships->first(); (psl != NULL); psl = pslNext)
  3780. {
  3781. pslNext = psl->next();
  3782. IshipIGC* pship = psl->data();
  3783. if (pship->GetBaseHullType())
  3784. {
  3785. {
  3786. const PartListIGC* parts = pship->GetParts();
  3787. PartLinkIGC* plink;
  3788. while (plink = parts->first()) //Not ==
  3789. plink->data()->Terminate();
  3790. }
  3791. pship->SetAmmo(0);
  3792. pship->SetFuel(0.0f);
  3793. }
  3794. if (pship->GetPilotType() < c_ptPlayer)
  3795. {
  3796. m_psiteIGC->KillShipEvent(g.timeNow, pship, NULL, 0.0f, pship->GetPosition(), Vector::GetZero());
  3797. }
  3798. else
  3799. {
  3800. if ((pship->GetParentShip() == NULL) && (pship->GetCluster() != NULL))
  3801. m_psiteIGC->KillShipEvent(g.timeNow, pship, NULL, -1.0f, pship->GetPosition(), Vector::GetZero()); //"Negative damage" == force killed
  3802. pship->Reset(false);
  3803. ((CFSShip*)(pship->GetPrivateData()))->ShipStatusExit();
  3804. }
  3805. }
  3806. }
  3807. BEGIN_PFM_CREATE(g.fm, pfmSideInactive, S, SIDE_INACTIVE)
  3808. END_PFM_CREATE
  3809. pfmSideInactive->sideID = pside->GetObjectID();
  3810. g.fm.SendMessages(GetGroupMission(), FM_GUARANTEED, FM_FLUSH);
  3811. GetSite()->SendChatf(NULL, CHAT_EVERYONE, NA, NA, "%s is no more.", pside->GetName());
  3812. bool bAllSidesInactive;
  3813. IsideIGC* psideWin = CheckForVictoryByInactiveSides(bAllSidesInactive);
  3814. if (bAllSidesInactive)
  3815. {
  3816. // if the game has not started yet, start it so that a side can win.
  3817. if (GetStage() == STAGE_STARTING)
  3818. StartGame();
  3819. GameOver(NULL, "All sides were destroyed");
  3820. }
  3821. else if (psideWin)
  3822. {
  3823. // if the game has not started yet, start it so that a side can win.
  3824. if (GetStage() == STAGE_STARTING)
  3825. StartGame();
  3826. static char szReason[100]; //Make this static so we only need to keep a pointer to it around
  3827. sprintf(szReason, "%s won by outlasting all other sides.", psideWin->GetName());
  3828. GameOver(psideWin, szReason);
  3829. }
  3830. else if ((m_psideWon == NULL) && pships->n())
  3831. {
  3832. //Games not over for anyone but this team (only ships on the team will be players)
  3833. CFMGroup* pgroup = CFSSide::FromIGC(pside)->GetGroup();
  3834. g.fm.SetDefaultRecipient(pgroup, FM_GUARANTEED);
  3835. QueueGameoverMessage();
  3836. g.fm.SendMessages(pgroup, FM_GUARANTEED, FM_FLUSH);
  3837. }
  3838. }
  3839. }
  3840. /*-------------------------------------------------------------------------
  3841. * SetForceReady
  3842. *-------------------------------------------------------------------------
  3843. * Purpose:
  3844. * Set a side's force ready bit
  3845. *
  3846. * Side Effects:
  3847. * Ready state of side may change
  3848. */
  3849. void CFSMission::SetForceReady(SideID iSide, bool fForceReady)
  3850. {
  3851. m_misdef.rgfForceReady[iSide] = fForceReady;
  3852. if (fForceReady)
  3853. SetReady(iSide, true);
  3854. else
  3855. CheckForSideAllReady(m_pMission->GetSide(iSide));
  3856. }
  3857. /*-------------------------------------------------------------------------
  3858. * SetReady
  3859. *-------------------------------------------------------------------------
  3860. * Purpose:
  3861. * Mark a side as ready or not. No checking of player ready states,
  3862. * force ready bits, etc. is done here. That's why it's private
  3863. *
  3864. * Side Effects:
  3865. * If side is now ready, game may start
  3866. */
  3867. void CFSMission::SetReady(SideID iSide, bool fReady)
  3868. {
  3869. if (fReady != GetReady(iSide))
  3870. {
  3871. BEGIN_PFM_CREATE(g.fm, pfmTeamReady, S, TEAM_READY)
  3872. END_PFM_CREATE
  3873. pfmTeamReady->iSide = iSide;
  3874. pfmTeamReady->fReady =
  3875. m_misdef.rgfReady[iSide] = fReady; // dbl assignment
  3876. g.fm.SendMessages(GetGroupMission(), FM_GUARANTEED, FM_FLUSH);
  3877. }
  3878. }
  3879. /*-------------------------------------------------------------------------
  3880. * PlayerReadyChange
  3881. *-------------------------------------------------------------------------
  3882. * Purpose:
  3883. * Notification from a player that their ready status changed. This may
  3884. * change the ready status of the side
  3885. *
  3886. * Side Effects:
  3887. * A side's ready status may change
  3888. */
  3889. void CFSMission::PlayerReadyChange(CFSPlayer * pfsPlayer)
  3890. {
  3891. if (pfsPlayer->GetSide()->GetObjectID() != SIDE_TEAMLOBBY)
  3892. CheckForSideAllReady(pfsPlayer->GetSide());
  3893. }
  3894. /*-------------------------------------------------------------------------
  3895. * CheckForSideAllReady
  3896. *-------------------------------------------------------------------------
  3897. * Purpose:
  3898. * See if everyone on a side is ready. Mark the side as such (if not force ready)
  3899. */
  3900. void CFSMission::CheckForSideAllReady(IsideIGC * pside)
  3901. {
  3902. SideID sideid = pside->GetObjectID();
  3903. if (GetCountOfPlayers(pside, false) < m_misdef.misparms.nMinPlayersPerTeam)
  3904. SetReady(sideid, false);
  3905. else if (GetForceReady(pside->GetObjectID()))
  3906. SetReady(sideid, true);
  3907. else
  3908. {
  3909. bool fReady = true;
  3910. const ShipListIGC * plistShip = pside->GetShips();
  3911. for(ShipLinkIGC * plinkShip = plistShip->first(); plinkShip; plinkShip = plinkShip->next())
  3912. {
  3913. CFSShip * pfsShip = (CFSShip *) plinkShip->data()->GetPrivateData();
  3914. if (pfsShip->IsPlayer())
  3915. {
  3916. CFSPlayer * pfsPlayer = pfsShip->GetPlayer();
  3917. if (!pfsPlayer->GetReady())
  3918. {
  3919. fReady = false;
  3920. break;
  3921. }
  3922. }
  3923. }
  3924. SetReady(sideid, fReady);
  3925. }
  3926. }
  3927. /*-------------------------------------------------------------------------
  3928. * CreateCluster()
  3929. *-------------------------------------------------------------------------
  3930. * Purpose:
  3931. * Make a FSCluster.
  3932. */
  3933. void CFSMission::CreateCluster(IclusterIGC * pIclusterIGC)
  3934. {
  3935. CFSCluster * pCFSCluster = new CFSCluster(pIclusterIGC);
  3936. m_pFSClusters.push_back(pCFSCluster);
  3937. }
  3938. /*-------------------------------------------------------------------------
  3939. * DeleteCluster()
  3940. *-------------------------------------------------------------------------
  3941. * Purpose:
  3942. * Destroy a FSCluster, given a pointer to an IclusterIGC.
  3943. */
  3944. void CFSMission::DeleteCluster(IclusterIGC * pIclusterIGC)
  3945. {
  3946. CFSCluster * pFSCluster = (CFSCluster *) pIclusterIGC->GetPrivateData();
  3947. if (pFSCluster)
  3948. {
  3949. std::vector<CFSCluster*>::iterator i = std::find(m_pFSClusters.begin(), m_pFSClusters.end(), pFSCluster);
  3950. assert(i != m_pFSClusters.end()); // assert that we found the pointer to delete
  3951. delete static_cast<CFSCluster*>(*i);
  3952. m_pFSClusters.erase(i);
  3953. }
  3954. }
  3955. /*-------------------------------------------------------------------------
  3956. * CreateSide()
  3957. *-------------------------------------------------------------------------
  3958. * Purpose:
  3959. * Make a FSSide.
  3960. */
  3961. void CFSMission::CreateSide(IsideIGC * pIsideIGC)
  3962. {
  3963. CFSSide * pCFSSide = new CFSSide(pIsideIGC, this);
  3964. m_pFSSides.push_back(pCFSSide);
  3965. }
  3966. /*-------------------------------------------------------------------------
  3967. * DeleteSide()
  3968. *-------------------------------------------------------------------------
  3969. * Purpose:
  3970. * Destroy a FSSide, given a pointer to an IsideIGC.
  3971. */
  3972. void CFSMission::DeleteSide(IsideIGC * pIsideIGC)
  3973. {
  3974. CFSSide * pFSSide = (CFSSide *) pIsideIGC->GetPrivateData();
  3975. if (pFSSide)
  3976. {
  3977. std::vector<CFSSide*>::iterator i = std::find(m_pFSSides.begin(), m_pFSSides.end(), pFSSide);
  3978. assert(i != m_pFSSides.end()); // assert that we found the pointer to delete
  3979. delete (CFSSide*)(*i);
  3980. m_pFSSides.erase(i);
  3981. }
  3982. }
  3983. void CFSMission::UpdateLobby(Time now)
  3984. {
  3985. if (m_fLobbyDirty && (now - m_timeLastLobbyMissionInfo >= c_flUpdateTimeInterval)
  3986. && (GetCookie() != NULL || !g.fmLobby.IsConnected()))
  3987. {
  3988. m_fLobbyDirty = false;
  3989. if (g.fmLobby.IsConnected())
  3990. {
  3991. QueueLobbyMissionInfo();
  3992. g.fmLobby.SendMessages(g.fmLobby.GetServerConnection(), FM_GUARANTEED, FM_FLUSH);
  3993. }
  3994. m_timeLastLobbyMissionInfo = now;
  3995. // Let's overload this function and also update the session details
  3996. SetSessionDetails();
  3997. }
  3998. }
  3999. void CFSMission::SetLobbyIsDirty()
  4000. {
  4001. m_fLobbyDirty = true;
  4002. UpdateLobby(Time::Now());
  4003. }
  4004. // do any steps needed to update the current ballot. Returns true iff
  4005. // the ballot is no longer needed.
  4006. bool Ballot::Update(const Time& now)
  4007. {
  4008. if (m_bCanceled)
  4009. return true;
  4010. if (m_timeExpiration < now || AllVotesAreIn())
  4011. {
  4012. if (HasPassed())
  4013. OnPassed();
  4014. else
  4015. OnFailed();
  4016. return true;
  4017. }
  4018. else
  4019. return false;
  4020. }
  4021. // cancels the pending vote
  4022. void Ballot::Cancel()
  4023. {
  4024. if (!m_bCanceled)
  4025. {
  4026. m_bCanceled = true;
  4027. // REVIEW: should we send a cancelation message here?
  4028. }
  4029. }
  4030. // records the given vote from a player
  4031. void Ballot::CastVote(CFSPlayer* pfsPlayer, bool bVote)
  4032. {
  4033. // if they are in the list of valid voters
  4034. if (m_vShips.Remove(pfsPlayer->GetShipID()) != -1)
  4035. {
  4036. SideID sideID = pfsPlayer->GetSide()->GetObjectID();
  4037. assert(sideID >= 0);
  4038. // if they are on a valid side, tally the vote
  4039. if (sideID >= 0 && m_cAbstaining[sideID] > 0)
  4040. {
  4041. --m_cAbstaining[sideID];
  4042. if (bVote)
  4043. ++m_cInFavor[sideID];
  4044. else
  4045. ++m_cOpposed[sideID];
  4046. }
  4047. }
  4048. }
  4049. // gets the ID of this ballot
  4050. BallotID Ballot::GetBallotID()
  4051. {
  4052. return m_ballotID;
  4053. }
  4054. // initializes the ballot for a given vote proposed by a player to their team
  4055. void Ballot::Init(CFSPlayer* pfsInitiator, const ZString& strProposalName, const ZString& strBallotText)
  4056. {
  4057. assert(pfsInitiator);
  4058. // store some misc. info about the vote
  4059. float c_fVoteDuration = 30.0f;
  4060. m_pgroup = CFSSide::FromIGC(pfsInitiator->GetSide())->GetGroup();
  4061. m_pmission = pfsInitiator->GetMission();
  4062. m_chattarget = CHAT_TEAM;
  4063. m_groupID = pfsInitiator->GetSide()->GetObjectID();
  4064. m_strProposal = strProposalName;
  4065. m_ballotID = s_ballotIDNext++;
  4066. m_timeExpiration = Time::Now() + c_fVoteDuration;
  4067. m_bCanceled = false;
  4068. // zero out the counts
  4069. for (SideID sideID = 0; sideID < c_cSidesMax; ++sideID)
  4070. {
  4071. m_cAbstaining[sideID] = 0;
  4072. m_cInFavor[sideID] = 0;
  4073. m_cOpposed[sideID] = 0;
  4074. }
  4075. // add the players
  4076. assert(pfsInitiator->GetSide());
  4077. const ShipListIGC* shipsList = pfsInitiator->GetSide()->GetShips();
  4078. m_vShips.Reserve(shipsList->n());
  4079. SideID sideIDInitiator = m_groupID;
  4080. for (ShipLinkIGC* shipLink = shipsList->first(); shipLink; shipLink = shipLink->next())
  4081. {
  4082. CFSShip * pfsShip = (CFSShip*)shipLink->data()->GetPrivateData();
  4083. if (pfsShip->IsPlayer())
  4084. {
  4085. ++m_cAbstaining[sideIDInitiator];
  4086. m_vShips.PushEnd(pfsShip->GetShipID());
  4087. }
  4088. }
  4089. // tell the clients about the vote
  4090. BEGIN_PFM_CREATE(g.fm, pfmBallot, S, BALLOT)
  4091. FM_VAR_PARM((PCC)(strBallotText), CB_ZTS)
  4092. END_PFM_CREATE
  4093. pfmBallot->ballotID = m_ballotID;
  4094. pfmBallot->timeExpiration = m_timeExpiration;
  4095. pfmBallot->otInitiator = OT_ship;
  4096. pfmBallot->oidInitiator = pfsInitiator->GetShipID();
  4097. g.fm.SendMessages(m_pgroup, FM_GUARANTEED, FM_FLUSH);
  4098. // tally the vote for the initiator
  4099. CastVote(pfsInitiator, true);
  4100. }
  4101. // initializes the ballot for a given vote proposed by a team to all other teams
  4102. void Ballot::Init(CFSSide* pfsideInitiator, const ZString& strProposalName, const ZString& strBallotText)
  4103. {
  4104. assert(pfsideInitiator);
  4105. // store some misc. info about the vote
  4106. float c_fVoteDuration = 30.0f;
  4107. m_pgroup = pfsideInitiator->GetMission()->GetGroupRealSides();
  4108. m_pmission = pfsideInitiator->GetMission();
  4109. m_chattarget = CHAT_EVERYONE;
  4110. m_groupID = NA;
  4111. m_strProposal = strProposalName;
  4112. m_ballotID = s_ballotIDNext++;
  4113. m_timeExpiration = Time::Now() + c_fVoteDuration;
  4114. m_bCanceled = false;
  4115. // zero out the counts
  4116. for (SideID sideID = 0; sideID < c_cSidesMax; ++sideID)
  4117. {
  4118. m_cAbstaining[sideID] = 0;
  4119. m_cInFavor[sideID] = 0;
  4120. m_cOpposed[sideID] = 0;
  4121. }
  4122. // add the players
  4123. const ShipListIGC* shipsList = pfsideInitiator->GetMission()->GetIGCMission()->GetShips();
  4124. m_vShips.Reserve(shipsList->n());
  4125. SideID sideIDInitiator = pfsideInitiator->GetSideIGC()->GetObjectID();
  4126. assert(sideIDInitiator >= 0);
  4127. for (ShipLinkIGC* shipLink = shipsList->first(); shipLink; shipLink = shipLink->next())
  4128. {
  4129. CFSShip * pfsShip = (CFSShip*)shipLink->data()->GetPrivateData();
  4130. IsideIGC* pside = pfsShip->GetSide();
  4131. SideID sideIDPlayer = pside->GetObjectID();
  4132. if (pfsShip->IsPlayer() && pside->GetActiveF() && sideIDPlayer >= 0 && sideIDPlayer != sideIDInitiator)
  4133. {
  4134. ++m_cAbstaining[sideIDPlayer];
  4135. m_vShips.PushEnd(pfsShip->GetShipID());
  4136. }
  4137. }
  4138. // tell the clients about the vote
  4139. BEGIN_PFM_CREATE(g.fm, pfmBallot, S, BALLOT)
  4140. FM_VAR_PARM((PCC)(strBallotText), CB_ZTS)
  4141. END_PFM_CREATE
  4142. pfmBallot->ballotID = m_ballotID;
  4143. pfmBallot->timeExpiration = m_timeExpiration;
  4144. pfmBallot->otInitiator = OT_side;
  4145. pfmBallot->oidInitiator = pfsideInitiator->GetSideIGC()->GetObjectID();
  4146. g.fm.SendMessages(m_pgroup, FM_GUARANTEED, FM_FLUSH);
  4147. }
  4148. // gets a string describing the tally of votes
  4149. ZString Ballot::GetTallyString()
  4150. {
  4151. ZString strMessage;
  4152. bool bFirst = true;
  4153. for (SideID sideID = 0; sideID < c_cSidesMax; ++sideID)
  4154. {
  4155. // if there were any votes on this side
  4156. if (m_cAbstaining[sideID] + m_cInFavor[sideID] + m_cOpposed[sideID])
  4157. {
  4158. if (bFirst)
  4159. {
  4160. bFirst = false;
  4161. strMessage += "(";
  4162. }
  4163. else
  4164. strMessage += ", ";
  4165. // only display the team name if more than one team is voting on the issue
  4166. if (m_chattarget != CHAT_TEAM)
  4167. strMessage += ZString(m_pmission->GetIGCMission()->GetSide(sideID)->GetName()) + ":" ;
  4168. if (m_cInFavor[sideID])
  4169. strMessage += " " + ZString(m_cInFavor[sideID]) + " for";
  4170. if (m_cOpposed[sideID])
  4171. strMessage += " " + ZString(m_cOpposed[sideID]) + " against";
  4172. if (m_cAbstaining[sideID])
  4173. strMessage += " " + ZString(m_cAbstaining[sideID]) + " abstained";
  4174. }
  4175. }
  4176. if (!bFirst)
  4177. strMessage += ")";
  4178. return strMessage;
  4179. }
  4180. // performs the appropriate result when the vote passes
  4181. void Ballot::OnPassed()
  4182. {
  4183. ZString strMessage = m_strProposal + " has passed. " + GetTallyString();
  4184. m_pmission->GetSite()->SendChat(NULL, m_chattarget, m_groupID, NA,
  4185. strMessage, c_cidNone, NA, NA, NULL, true);
  4186. }
  4187. // performs the appropriate result when the vote passes
  4188. void Ballot::OnFailed()
  4189. {
  4190. ZString strMessage = m_strProposal + " has been defeated. " + GetTallyString();
  4191. m_pmission->GetSite()->SendChat(NULL, m_chattarget, m_groupID, NA,
  4192. strMessage, c_cidNone, NA, NA, NULL, true);
  4193. }
  4194. // tests whether the vote passes
  4195. bool Ballot::HasPassed()
  4196. {
  4197. for (SideID sideID = 0; sideID < c_cSidesMax; ++sideID)
  4198. {
  4199. // if there were any votes on this side
  4200. if (m_cAbstaining[sideID] + m_cInFavor[sideID] + m_cOpposed[sideID])
  4201. {
  4202. // if it didn't get a majority, it didn't pass.
  4203. if (m_cInFavor[sideID] <= m_cOpposed[sideID] + m_cAbstaining[sideID])
  4204. {
  4205. return false;
  4206. }
  4207. }
  4208. }
  4209. return true;
  4210. }
  4211. // tests to see if we have received all of the votes
  4212. bool Ballot::AllVotesAreIn()
  4213. {
  4214. for (SideID sideID = 0; sideID < c_cSidesMax; ++sideID)
  4215. {
  4216. if (m_cAbstaining[sideID] != 0)
  4217. return false;
  4218. }
  4219. return true;
  4220. }
  4221. BallotID Ballot::s_ballotIDNext = 0;
  4222. // a ballot used when a player suggests resigning
  4223. ResignBallot::ResignBallot(CFSPlayer* pfsInitiator)
  4224. {
  4225. m_pside = pfsInitiator->GetSide();
  4226. Init(pfsInitiator, pfsInitiator->GetName() + ZString("'s proposal to resign"), pfsInitiator->GetName() + ZString(" has proposed resigning. "));
  4227. }
  4228. void ResignBallot::OnPassed()
  4229. {
  4230. Ballot::OnPassed();
  4231. SideID sideID = m_pside->GetObjectID();
  4232. if (sideID >= 0 && STAGE_STARTED == m_pmission->GetStage())
  4233. {
  4234. m_pmission->DeactivateSide(m_pside);
  4235. }
  4236. }
  4237. // a ballot used when a player suggests offering a draw
  4238. OfferDrawBallot::OfferDrawBallot(CFSPlayer* pfsInitiator)
  4239. {
  4240. m_pfside = CFSSide::FromIGC(pfsInitiator->GetSide());
  4241. Init(pfsInitiator, pfsInitiator->GetName() + ZString("'s proposal to offer a draw"), pfsInitiator->GetName() + ZString(" has proposed offering a draw. "));
  4242. }
  4243. void OfferDrawBallot::OnPassed()
  4244. {
  4245. Ballot::OnPassed();
  4246. m_pmission->GetSite()->SendChat(NULL, m_chattarget, m_groupID, NA,
  4247. "A draw is being offered to the other teams.", c_cidNone, NA, NA, NULL, true);
  4248. m_pmission->AddBallot(new AcceptDrawBallot(m_pfside));
  4249. }
  4250. // a ballot used when one team offers a draw
  4251. AcceptDrawBallot::AcceptDrawBallot(CFSSide* pfsideInitiator)
  4252. {
  4253. Init(pfsideInitiator, pfsideInitiator->GetSideIGC()->GetName() + ZString("'s offer of a draw"), pfsideInitiator->GetSideIGC()->GetName() + ZString(" has offered a draw. "));
  4254. }
  4255. void AcceptDrawBallot::OnPassed()
  4256. {
  4257. Ballot::OnPassed();
  4258. m_pmission->GameOver(NULL, "The game was declared a draw");
  4259. }