appmsg.cpp 127 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441
  1. #include "pch.h"
  2. #include <limits.h>
  3. //
  4. // Helper function for handling ship updates.
  5. //
  6. //
  7. // Big function to handle default messaging.
  8. // Three possible return codes:
  9. // S_OK message handled
  10. // S_FALSE message not handled
  11. // E_FAIL error, abort
  12. HRESULT BaseClient::HandleMsg(FEDMESSAGE* pfm,
  13. Time lastUpdate, Time now)
  14. {
  15. HRESULT hr = S_OK; //handled by default
  16. #ifdef DUMPMSGS
  17. debugf("Received message type %d of length %d\n", pfm->fmid, pfm->cbmsg);
  18. #endif // DUMPMSGS
  19. // keep track of the last server message, but only count unreliable
  20. // messages while the client is in flight.
  21. if (!m_ship || !IsInGame() || GetCluster() == NULL || pfm->fmid == FM_CS_PING
  22. || pfm->fmid == FM_S_HEAVY_SHIPS_UPDATE || pfm->fmid == FM_S_LIGHT_SHIPS_UPDATE)
  23. {
  24. m_timeLastServerMessage = now;
  25. }
  26. switch(pfm->fmid)
  27. {
  28. case FM_S_LOGONACK:
  29. {
  30. CASTPFM(pfmLogonAck, S, LOGONACK, pfm);
  31. if (pfmLogonAck->fValidated)
  32. {
  33. debugf("I am ship %s, shipid=%d\n", FM_VAR_REF(pfmLogonAck, CharName_OR_FailureReason), pfmLogonAck->shipID);
  34. lstrcpy(m_szCharName, FM_VAR_REF(pfmLogonAck, CharName_OR_FailureReason)); // remember who we are
  35. m_fLoggedOn = true;
  36. SetCookie(pfmLogonAck->cookie);
  37. // set the client-server time offset properly
  38. {
  39. m_cUnansweredPings = 0;
  40. DWORD newLag = 100; // reasonable default value
  41. //Try a new way of doing the correction
  42. DWORD newOffset = pfmLogonAck->timeServer.clock() - now.clock() + newLag;
  43. m_serverOffsetValidF = true;
  44. m_serverLag = newLag;
  45. m_serverOffset = newOffset;
  46. m_timeLastPing = now;
  47. m_timeLastPingServer = pfmLogonAck->timeServer;
  48. }
  49. }
  50. //
  51. // If we need to download art files, then
  52. // don't ack logon until done
  53. //
  54. char * szFailureReason = pfmLogonAck->fValidated ? NULL : FM_VAR_REF(pfmLogonAck, CharName_OR_FailureReason);
  55. OnLogonAck(pfmLogonAck->fValidated, pfmLogonAck->fRetry, szFailureReason);
  56. }
  57. break;
  58. case FM_S_URLROOT:
  59. {
  60. CASTPFM(pfmUrlRoot, S, URLROOT, pfm);
  61. UTL::SetUrlRoot(FM_VAR_REF(pfmUrlRoot, UrlRoot));
  62. }
  63. break;
  64. case FM_S_EXPORT:
  65. {
  66. if (IsWaitingForGameRestart())
  67. break;
  68. CASTPFM(pfmExport, S, EXPORT, pfm);
  69. IObject* u = m_pCoreIGC->CreateObject(lastUpdate,
  70. pfmExport->objecttype,
  71. FM_VAR_REF(pfmExport, exportData),
  72. pfmExport->cbexportData);
  73. if (u)
  74. u->Release();
  75. else
  76. {
  77. //Station exports are allowed to "fail" because they may simply update an existing station.
  78. //Ships are allowed to fail because sometimes an existing ship will be updated.
  79. assert ((pfmExport->objecttype == OT_station) || (pfmExport->objecttype == OT_ship));
  80. }
  81. Sleep(0);
  82. }
  83. break;
  84. case FM_S_POSTER:
  85. {
  86. // I think this message is no longer used.
  87. assert(false);
  88. /*
  89. CASTPFM(pfmPoster, S, POSTER, pfm);
  90. IclusterIGC * cluster =
  91. m_pCoreIGC->GetCluster(pfmPoster->sectorID);
  92. if (cluster)
  93. {
  94. float cosLatr = cos(pfmPoster->latitude);
  95. Vector position(cosLatr * cos(pfmPoster->longitude),
  96. cosLatr * sin(pfmPoster->longitude),
  97. sin(pfmPoster->latitude));
  98. cluster->GetClusterSite()->AddPoster(
  99. pfmPoster->textureName,
  100. position,
  101. pfmPoster->radius);
  102. }
  103. */
  104. }
  105. break;
  106. case FM_S_RELAUNCH_SHIP:
  107. {
  108. if (!IsInGame())
  109. break;
  110. CASTPFM(pfmRelaunch, S, RELAUNCH_SHIP, pfm);
  111. IshipIGC* pShip = m_pCoreIGC->GetShip(pfmRelaunch->shipID);
  112. if (pShip)
  113. {
  114. IshipIGC* pshipParent = m_pCoreIGC->GetShip(pfmRelaunch->carrierID);
  115. assert (pshipParent);
  116. pShip->SetParentShip(NULL);
  117. pShip->ProcessShipLoadout(pfmRelaunch->cbloadout, (const ShipLoadout*)(FM_VAR_REF(pfmRelaunch, loadout)), true);
  118. pShip->SetAmmo(SHRT_MAX);
  119. pShip->SetFuel(FLT_MAX);
  120. pShip->SetEnergy(pShip->GetHullType()->GetMaxEnergy());
  121. pShip->SetPosition(pfmRelaunch->position);
  122. pShip->SetVelocity(pfmRelaunch->velocity);
  123. {
  124. Orientation o;
  125. pfmRelaunch->orientation.Export(&o);
  126. pShip->SetOrientation(o);
  127. }
  128. if (pShip == m_ship)
  129. {
  130. SetCookie(pfmRelaunch->cookie);
  131. PlayNotificationSound(salRepairedAtCarrierSound, GetShip());
  132. OverrideCamera(pshipParent);
  133. }
  134. }
  135. }
  136. break;
  137. case FM_S_EJECT:
  138. {
  139. if (!IsInGame())
  140. break;
  141. IclusterIGC* pcluster = GetCluster();
  142. if (pcluster)
  143. {
  144. CASTPFM(pfmEject, S, EJECT, pfm);
  145. IshipIGC* pShip = m_pCoreIGC->GetShip(pfmEject->shipID);
  146. if (pShip)
  147. {
  148. if (pShip->GetParentShip())
  149. {
  150. pShip->SetParentShip(NULL);
  151. m_pClientEventSource->OnBoardShip(pShip, NULL);
  152. }
  153. else
  154. {
  155. //Ship 'died' .
  156. assert (pShip->GetChildShips()->n() == 0);
  157. {
  158. const PartListIGC* plist = pShip->GetParts();
  159. PartLinkIGC* plink;
  160. while (plink = plist->first()) //Not ==
  161. plink->data()->Terminate();
  162. }
  163. //and fuel and ammo
  164. pShip->SetAmmo(0);
  165. pShip->SetFuel(0.0f);
  166. }
  167. {
  168. {
  169. for (MissileLinkIGC* pml = pcluster->GetMissiles()->first();
  170. (pml != NULL);
  171. pml = pml->next())
  172. {
  173. ImissileIGC* pmissile = pml->data();
  174. if (pmissile->GetTarget() == pShip)
  175. pmissile->SetTarget(NULL);
  176. }
  177. }
  178. //Eject the player
  179. pShip->SetBaseHullType(pShip->GetSide()->GetCivilization()->GetLifepod());
  180. //Put a spin on the ship as it leaves the bad guys.
  181. //Note ... this needs to match the code in fedsrv.cpp
  182. pShip->SetCurrentTurnRate(c_axisRoll, pi * 2.0f);
  183. pShip->SetPosition(pfmEject->position);
  184. pShip->SetVelocity(pfmEject->velocity);
  185. {
  186. Orientation o(pfmEject->forward);
  187. pShip->SetOrientation(o);
  188. }
  189. }
  190. if (pShip == m_ship)
  191. {
  192. SetCookie(pfmEject->cookie);
  193. ImodelIGC* pNearestBase = FindTarget (pShip, c_ttStation | c_ttFriendly | c_ttAnyCluster | c_ttProbe, pShip->GetCommandTarget (c_cmdCurrent), pcluster, &pfmEject->position, NULL, c_sabmRescue | c_sabmRescueAny | c_sabmLand);
  194. pShip->SetCommand (c_cmdCurrent, pNearestBase, c_cidGoto);
  195. SetAutoPilot (true);
  196. bInitTrekJoyStick = true;
  197. PlayNotificationSound(salAutopilotEngageSound, GetShip());
  198. }
  199. }
  200. }
  201. }
  202. break;
  203. case FM_S_DESTROY_TREASURE:
  204. {
  205. if (!IsInGame())
  206. break;
  207. CASTPFM(pfmTreasure, S, DESTROY_TREASURE, pfm);
  208. {
  209. IclusterIGC* c = m_pCoreIGC->GetCluster(pfmTreasure->sectorID);
  210. assert (c);
  211. ItreasureIGC* treasure = c->GetTreasure(pfmTreasure->treasureID);
  212. if (treasure)
  213. treasure->Terminate();
  214. }
  215. }
  216. break;
  217. case FM_S_ACQUIRE_TREASURE:
  218. {
  219. if (!IsInGame())
  220. break;
  221. CASTPFM(pfmTreasure, S, ACQUIRE_TREASURE, pfm);
  222. switch (pfmTreasure->treasureCode)
  223. {
  224. case c_tcPart:
  225. {
  226. SetMessageType(BaseClient::c_mtGuaranteed);
  227. BEGIN_PFM_CREATE(m_fm, pfmAck, C, TREASURE_ACK)
  228. END_PFM_CREATE
  229. pfmAck->mountID = m_ship->HitTreasure(c_tcPart, pfmTreasure->treasureID, pfmTreasure->amount);
  230. }
  231. break;
  232. case c_tcPowerup:
  233. PlaySoundEffect(pickUpPowerupSound, GetShip());
  234. break;
  235. case c_tcDevelopment:
  236. PlaySoundEffect(pickUpDevelopmentSound, GetShip());
  237. break;
  238. case c_tcCash:
  239. {
  240. PlaySoundEffect(pickUpCashSound, GetShip());
  241. IshipIGC* pshipDonate = m_ship->GetAutoDonate();
  242. if (pshipDonate)
  243. PostText(false, "You donated your reward of $%d to %s.", pfmTreasure->amount, pshipDonate->GetName());
  244. else
  245. PostText(false, "You received a reward of $%d.", pfmTreasure->amount);
  246. }
  247. break;
  248. case c_tcFlag:
  249. PlaySoundEffect(pickUpDevelopmentSound, GetShip()); //NYI pickUpFlagSound
  250. break;
  251. }
  252. }
  253. break;
  254. case FM_S_PLAYER_RESCUED:
  255. {
  256. CASTPFM(pfmPlayerRescued, S, PLAYER_RESCUED, pfm);
  257. ShipID sid = GetShipID();
  258. if (pfmPlayerRescued->shipIDRescuer == sid)
  259. {
  260. // I just picked someone up.
  261. PlayerInfo* pplayerRescuee = FindPlayer(pfmPlayerRescued->shipIDRescuee);
  262. if (!pplayerRescuee)
  263. {
  264. assert(false);
  265. break;
  266. }
  267. PostText(false, "You rescued %s.", pplayerRescuee->CharacterName());
  268. PlaySoundEffect(rescuePlayerSound, GetShip());
  269. }
  270. else if (pfmPlayerRescued->shipIDRescuee == sid)
  271. {
  272. // I was just teleported back to base.
  273. PlayerInfo* pplayerRescuer = FindPlayer(pfmPlayerRescued->shipIDRescuer);
  274. if (!pplayerRescuer)
  275. {
  276. assert(false);
  277. break;
  278. }
  279. PostText(true, "%s rescued you.", pplayerRescuer->CharacterName());
  280. PlaySoundEffect(jumpSound);
  281. }
  282. }
  283. break;
  284. case FM_S_BALLOT:
  285. {
  286. CASTPFM(pfmBallot, S, BALLOT, pfm);
  287. // if I'm not the one who called this vote...
  288. if (!(pfmBallot->otInitiator == OT_ship && pfmBallot->oidInitiator == GetShipID())
  289. && !(pfmBallot->otInitiator == OT_side && pfmBallot->oidInitiator == GetSideID()))
  290. {
  291. // then propose the issue.
  292. m_listBallots.PushEnd(BallotInfo(
  293. (char*)(FM_VAR_REF(pfmBallot, BallotText)) + ZString("Press [Y] to vote yes, [N] to vote no."),
  294. pfmBallot->ballotID,
  295. ClientTimeFromServerTime(pfmBallot->timeExpiration)
  296. ));
  297. }
  298. }
  299. break;
  300. case FM_S_CANCEL_BALLOT:
  301. {
  302. CASTPFM(pfmCancelBallot, S, CANCEL_BALLOT, pfm);
  303. // destroy any ballot in the queue with this ID
  304. BallotList::Iterator iterBallot(m_listBallots);
  305. while (!iterBallot.End())
  306. {
  307. if (iterBallot.Value().GetBallotID() == pfmCancelBallot->ballotID)
  308. iterBallot.Remove();
  309. else
  310. iterBallot.Next();
  311. }
  312. }
  313. break;
  314. case FM_CS_PING:
  315. {
  316. CASTPFM(pfmPing, CS, PING, pfm);
  317. assert (now >= pfmPing->timeClient);
  318. //
  319. // Assume lag evenly split here to there & there to here.
  320. //
  321. DWORD newLag = (now.clock() - pfmPing->timeClient.clock()) >> 1;
  322. if (newLag < 1000)
  323. {
  324. m_cUnansweredPings = 0;
  325. //Try a new way of doing the correction
  326. //DWORD newOffset = pfmPing->timeServer.clock() - pfmPing->timeClient.clock();
  327. DWORD newOffset = pfmPing->timeServer.clock() - now.clock() + newLag;
  328. if (m_serverOffsetValidF)
  329. {
  330. float currentSync = float(now.clock() - m_timeLastPing.clock()) /
  331. float(pfmPing->timeServer.clock() - m_timeLastPingServer.clock());
  332. m_sync = m_sync * 3/4 + currentSync/4;
  333. if (newLag > m_serverLag)
  334. {
  335. //new lag is worse ... heavily weight the old lag
  336. m_serverOffset += ((int)(newOffset - m_serverOffset)) >> 3;
  337. m_serverLag = (m_serverLag * 7 + newLag) >> 3;
  338. }
  339. else
  340. {
  341. //new lag is better ... average the old & new
  342. m_serverOffset += ((int)(newOffset - m_serverOffset)) >> 1;
  343. m_serverLag = (m_serverLag + newLag) >> 1;
  344. }
  345. }
  346. else
  347. {
  348. m_serverOffsetValidF = true;
  349. m_serverLag = newLag;
  350. m_serverOffset = newOffset;
  351. }
  352. m_timeLastPing = now;
  353. m_timeLastPingServer = pfmPing->timeServer;
  354. }
  355. }
  356. break;
  357. case FM_S_VIEW_CLUSTER:
  358. {
  359. if (!IsInGame())
  360. break;
  361. CASTPFM(pfmViewCluster, S, VIEW_CLUSTER, pfm);
  362. IclusterIGC* pcluster = m_pCoreIGC->GetCluster(pfmViewCluster->clusterID);
  363. assert (pcluster);
  364. SetViewCluster(pcluster, pfmViewCluster->bUsePosition ? &(pfmViewCluster->position) : NULL);
  365. }
  366. break;
  367. case FM_S_SHIP_DELETE:
  368. {
  369. CASTPFM(pfmShipDelete, S, SHIP_DELETE, pfm);
  370. //
  371. // Which ship went away ...
  372. //
  373. if (pfmShipDelete->shipID != GetShipID())
  374. {
  375. IshipIGC* ship = m_pCoreIGC->GetShip(pfmShipDelete->shipID);
  376. if (ship)
  377. {
  378. //
  379. // Found a ship ... why'd it go?
  380. //
  381. switch (pfmShipDelete->sdr)
  382. {
  383. case SDR_TERMINATE:
  384. case SDR_KILLED:
  385. {
  386. ship->SetCluster(NULL);
  387. }
  388. break;
  389. default:
  390. {
  391. //
  392. // The ship is no longer visible to the player,
  393. // "cache" it by moving it the null sector.
  394. // Ignore for your parent ship since this is handled
  395. // by getting a docked or set cluster message for
  396. // the parent.
  397. if (m_ship->GetParentShip() != ship)
  398. ship->SetCluster(NULL);
  399. }
  400. }
  401. }
  402. if (pfmShipDelete->sdr == SDR_LOGGEDOFF)
  403. {
  404. //The player is gone ... nuke their player info (if it exists)
  405. PlayerLink* l = FindPlayerLink(pfmShipDelete->shipID);
  406. if (l)
  407. {
  408. PlayerInfo* pPlayerInfo = &(l->data());
  409. assert (pPlayerInfo);
  410. debugf("Logging off ship for %s, ID=%d\n",
  411. pPlayerInfo->CharacterName(), pPlayerInfo->ShipID());
  412. RemovePlayerFromSide(pPlayerInfo, QSR_Quit);
  413. RemovePlayerFromMission(pPlayerInfo, QSR_Quit);
  414. }
  415. }
  416. }
  417. }
  418. break;
  419. case FM_S_STATION_DESTROYED:
  420. {
  421. if (!IsInGame())
  422. break;
  423. CASTPFM(pfmStation, S, STATION_DESTROYED, pfm);
  424. IstationIGC * station = m_pCoreIGC->GetStation(
  425. pfmStation->stationID);
  426. if (station)
  427. {
  428. station->GetCluster()->GetClusterSite()->AddExplosion(station,
  429. station->GetStationType()->HasCapability(c_sabmFlag)
  430. ? c_etLargeStation
  431. : c_etSmallStation);
  432. if (pfmStation->launcher != NA)
  433. {
  434. IshipIGC * pship = GetCore()->GetShip(pfmStation->launcher);
  435. PostText(true, START_COLOR_STRING "%s" END_COLOR_STRING " destroyed " START_COLOR_STRING "%s's %s" END_COLOR_STRING " in %s.",
  436. (PCC) ConvertColorToString (pship ? pship->GetSide ()->GetColor () : Color::White ()),
  437. (pship ? pship->GetName() : "Unknown ship"),
  438. (PCC) ConvertColorToString (station->GetSide ()->GetColor ()),
  439. station->GetSide()->GetName(),
  440. station->GetName(),
  441. station->GetCluster()->GetName()
  442. );
  443. }
  444. else
  445. PostText(true, START_COLOR_STRING "%s's %s" END_COLOR_STRING " in %s was destroyed.",
  446. (PCC) ConvertColorToString (station->GetSide()->GetColor ()),
  447. station->GetSide()->GetName(),
  448. station->GetName(),
  449. station->GetCluster()->GetName());
  450. if (station->GetSide() == GetSide())
  451. PlaySoundEffect(station->GetStationType()->GetDestroyedSound());
  452. else
  453. PlaySoundEffect(station->GetStationType()->GetEnemyDestroyedSound());
  454. station->Terminate();
  455. }
  456. }
  457. break;
  458. case FM_S_STATIONS_UPDATE:
  459. {
  460. if (!IsInGame())
  461. break;
  462. CASTPFM(pfmStation, S, STATIONS_UPDATE, pfm);
  463. IclusterIGC* pcluster = GetChatCluster();
  464. if (pcluster)
  465. {
  466. for (int i = int(pfmStation->cbrgStationStates /
  467. sizeof(StationState)) - 1; (i >= 0); i--)
  468. {
  469. StationState* pss = ((StationState*)(FM_VAR_REF(pfmStation, rgStationStates))) + i;
  470. IstationIGC * station = pcluster->GetStation(pss->stationID);
  471. if (station)
  472. {
  473. station->SetFraction(pss->bpHullFraction);
  474. station->SetShieldFraction(pss->bpShieldFraction);
  475. }
  476. }
  477. }
  478. }
  479. break;
  480. case FM_S_PROBES_UPDATE:
  481. {
  482. if (!IsInGame())
  483. break;
  484. CASTPFM(pfmProbe, S, PROBES_UPDATE, pfm);
  485. IclusterIGC* pcluster = GetCluster();
  486. if (pcluster)
  487. {
  488. for (int i = int(pfmProbe->cbrgProbeStates /
  489. sizeof(ProbeState)) - 1; (i >= 0); i--)
  490. {
  491. ProbeState* pps = ((ProbeState*)(FM_VAR_REF(pfmProbe, rgProbeStates))) + i;
  492. IprobeIGC * probe = pcluster->GetProbe(pps->probeID);
  493. if (probe)
  494. probe->SetFraction(pps->bpFraction);
  495. }
  496. }
  497. }
  498. break;
  499. case FM_S_ASTEROIDS_UPDATE:
  500. {
  501. if (!IsInGame())
  502. break;
  503. CASTPFM(pfmAsteroids, S, ASTEROIDS_UPDATE, pfm);
  504. IclusterIGC* pcluster = GetCluster();
  505. if (pcluster)
  506. {
  507. for (int i = int(pfmAsteroids->cbrgAsteroidStates /
  508. sizeof(AsteroidState)) - 1; (i >= 0); i--)
  509. {
  510. AsteroidState* pas = ((AsteroidState*)(FM_VAR_REF(pfmAsteroids, rgAsteroidStates))) + i;
  511. IasteroidIGC* asteroid = pcluster->GetAsteroid(pas->asteroidID);
  512. if (asteroid)
  513. {
  514. Orientation o;
  515. pas->co.Export(&o);
  516. asteroid->SetOrientation(o);
  517. asteroid->SetOre(float(pas->ore));
  518. asteroid->SetFraction(pas->bpFraction);
  519. }
  520. }
  521. }
  522. }
  523. break;
  524. case FM_S_STATION_CAPTURE:
  525. {
  526. if (!IsInGame())
  527. break;
  528. CASTPFM(pfmStationCapture, S, STATION_CAPTURE, pfm);
  529. IstationIGC * station = m_pCoreIGC->GetStation(pfmStationCapture->stationID);
  530. if (station)
  531. {
  532. TRef<BucketStatusArray> prgStatus;
  533. /* NYI: Used to be ... is the new code still valid?
  534. if ((pfmStationCapture->iSide != GetSide()->GetObjectID()) &&
  535. m_mapBucketStatusArray.Find(pfmStationCapture->stationID, prgStatus))
  536. */
  537. if ((pfmStationCapture->sidOld == GetSide()->GetObjectID()) &&
  538. m_mapBucketStatusArray.Find(pfmStationCapture->stationID, prgStatus))
  539. {
  540. // we lost the station... purge the buckets
  541. m_mapBucketStatusArray.Remove(pfmStationCapture->stationID);
  542. }
  543. m_pClientEventSource->OnStationCaptured(pfmStationCapture->stationID, pfmStationCapture->sidNew);
  544. IsideIGC* psideNew = m_pCoreIGC->GetSide(pfmStationCapture->sidNew);
  545. PostText(true, START_COLOR_STRING "%s" END_COLOR_STRING " captured " START_COLOR_STRING "%s's %s" END_COLOR_STRING " in %s.",
  546. (PCC) ConvertColorToString (GetCore()->GetShip(pfmStationCapture->shipIDCredit)->GetSide ()->GetColor ()),
  547. GetCore()->GetShip(pfmStationCapture->shipIDCredit)->GetName(),
  548. (PCC) ConvertColorToString (station->GetSide()->GetColor ()),
  549. station->GetSide()->GetName(),
  550. station->GetName(),
  551. station->GetCluster()->GetName()
  552. );
  553. if (pfmStationCapture->sidOld == GetSideID())
  554. PlaySoundEffect(station->GetStationType()->GetCapturedSound());
  555. else if (pfmStationCapture->sidNew == GetSideID())
  556. PlaySoundEffect(station->GetStationType()->GetEnemyCapturedSound());
  557. station->SetSide(psideNew);
  558. }
  559. }
  560. break;
  561. case FM_S_LIGHT_SHIPS_UPDATE:
  562. {
  563. if (GetCluster())
  564. {
  565. CASTPFM(pfmLight, S, LIGHT_SHIPS_UPDATE, pfm);
  566. ShipID myShipID = m_ship->GetObjectID();
  567. ShipID* pitu = (ShipID*)(FM_VAR_REF(pfmLight, rgInactiveTurretUpdates));
  568. if (pitu)
  569. {
  570. ShipID* pituMax = (ShipID*)((char*)pitu + pfmLight->cbrgInactiveTurretUpdates);
  571. //Process the inactive turret updates ..
  572. while (pitu < pituMax)
  573. {
  574. ShipID shipID = *(pitu++);
  575. if (shipID != myShipID)
  576. {
  577. IshipIGC* pship = m_pCoreIGC->GetShip(shipID);
  578. //The following checks shouldn't be needed ... but since these messages
  579. //can arrive in any order, paranoia isn't bad
  580. if (pship && pship->GetParentShip() && pship->GetCluster())
  581. pship->SetStateM(0); //Stop shooting
  582. }
  583. }
  584. }
  585. ServerLightShipUpdate* plsu = (ServerLightShipUpdate*)(FM_VAR_REF(pfmLight, rgLightShipUpdates));
  586. if (plsu)
  587. {
  588. ServerLightShipUpdate* plsuMax = (ServerLightShipUpdate*)((char*)plsu + pfmLight->cbrgLightShipUpdates);
  589. //Process the light ship updates
  590. while (plsu < plsuMax)
  591. {
  592. ServerLightShipUpdate& lsu = *(plsu++);
  593. //Never get updates for yourself
  594. assert (lsu.shipID != myShipID);
  595. IshipIGC* pship = m_pCoreIGC->GetShip(lsu.shipID);
  596. if (pship && (pship->GetParentShip() == NULL) && pship->GetCluster())
  597. {
  598. pship->ProcessShipUpdate(lsu);
  599. }
  600. }
  601. }
  602. }
  603. }
  604. break;
  605. case FM_S_HEAVY_SHIPS_UPDATE:
  606. {
  607. if (GetCluster())
  608. {
  609. CASTPFM(pfmHeavy, S, HEAVY_SHIPS_UPDATE, pfm);
  610. if (m_pmodelServerTarget && m_pmodelServerTarget->GetCluster() && (pfmHeavy->bpTargetHull.GetChar() != 255))
  611. {
  612. switch(m_pmodelServerTarget->GetObjectType())
  613. {
  614. case OT_ship:
  615. {
  616. IshipIGC* pship = ((IshipIGC*)(ImodelIGC*)m_pmodelServerTarget)->GetSourceShip();
  617. pship->SetFraction(pfmHeavy->bpTargetHull);
  618. IshieldIGC* pshield = (IshieldIGC*)(pship->GetMountedPart(ET_Shield, 0));
  619. if (pshield)
  620. pshield->SetFraction(pfmHeavy->bpTargetShield);
  621. }
  622. break;
  623. case OT_station:
  624. {
  625. IstationIGC* pstation = (IstationIGC*)(ImodelIGC*)m_pmodelServerTarget;
  626. pstation->SetFraction(pfmHeavy->bpTargetHull);
  627. pstation->SetShieldFraction(pfmHeavy->bpTargetShield);
  628. }
  629. break;
  630. case OT_probe:
  631. case OT_asteroid:
  632. case OT_missile:
  633. {
  634. ((IdamageIGC*)(ImodelIGC*)m_pmodelServerTarget)->SetFraction(pfmHeavy->bpTargetHull);
  635. }
  636. }
  637. }
  638. ServerActiveTurretUpdate* patu = (ServerActiveTurretUpdate*)(FM_VAR_REF(pfmHeavy, rgActiveTurretUpdates));
  639. if (patu)
  640. {
  641. ServerActiveTurretUpdate* patuMax = (ServerActiveTurretUpdate*)((char*)patu + pfmHeavy->cbrgActiveTurretUpdates);
  642. //Process the active turret updates ..
  643. while (patu < patuMax)
  644. {
  645. ServerActiveTurretUpdate& atu= *(patu++);
  646. assert (atu.shipID != m_ship->GetObjectID());
  647. IshipIGC* pship = m_pCoreIGC->GetShip(atu.shipID);
  648. //The following checks shouldn't be needed ... but since these messages
  649. //can arrive in any order, paranoia isn't bad
  650. if (pship && pship->GetParentShip() && pship->GetCluster())
  651. {
  652. pship->ProcessShipUpdate(pfmHeavy->timeReference, atu);
  653. }
  654. }
  655. }
  656. ServerHeavyShipUpdate* phsu = (ServerHeavyShipUpdate*)(FM_VAR_REF(pfmHeavy, rgHeavyShipUpdates));
  657. if (phsu)
  658. {
  659. ServerHeavyShipUpdate* phsuMax = (ServerHeavyShipUpdate*)((char*)phsu + pfmHeavy->cbrgHeavyShipUpdates);
  660. //Process the heavy ship updates
  661. while (phsu < phsuMax)
  662. {
  663. ServerHeavyShipUpdate& hsu = *(phsu++);
  664. //Never get updates for yourself
  665. assert (hsu.shipID != m_ship->GetObjectID());
  666. IshipIGC* pship = m_pCoreIGC->GetShip(hsu.shipID);
  667. if (pship && (pship->GetParentShip() == NULL) && pship->GetCluster())
  668. {
  669. pship->ProcessShipUpdate(pfmHeavy->timeReference, pfmHeavy->positionReference, hsu);
  670. }
  671. }
  672. }
  673. m_ship->GetSourceShip()->ProcessFractions(pfmHeavy->fractions);
  674. }
  675. }
  676. break;
  677. case FM_S_PROMOTE:
  678. {
  679. if (!IsInGame())
  680. break;
  681. CASTPFM(pfmPromote, S, PROMOTE, pfm);
  682. IshipIGC* pship = m_pCoreIGC->GetShip(pfmPromote->shipidPromoted);
  683. assert (pship);
  684. IshipIGC* pshipParent = pship->GetParentShip();
  685. if (pshipParent)
  686. {
  687. pship->Promote();
  688. m_pClientEventSource->OnBoardShip(pship, NULL);
  689. m_pClientEventSource->OnBoardShip(pshipParent, pship);
  690. if (pshipParent == m_ship)
  691. PostText(true, "You have been demoted to a turret gunner");
  692. else if (pship == m_ship)
  693. PostText(true, "You have been promoted to pilot");
  694. }
  695. }
  696. break;
  697. case FM_S_WARP_BOMB:
  698. {
  699. if (!IsInGame())
  700. break;
  701. CASTPFM(pfmWarpBomb, S, WARP_BOMB, pfm);
  702. IwarpIGC* pwarp = m_pCoreIGC->GetWarp(pfmWarpBomb->warpidBombed);
  703. if (pwarp)
  704. {
  705. ImissileTypeIGC* pmt = (ImissileTypeIGC*)(m_pCoreIGC->GetExpendableType(pfmWarpBomb->expendableidMissile));
  706. assert (pmt);
  707. assert (pmt->GetObjectType() == OT_missileType);
  708. pwarp->AddBomb(ClientTimeFromServerTime(pfmWarpBomb->timeExplosion), pmt);
  709. IclusterIGC* pclusterMe = GetCluster();
  710. IclusterIGC* pc1 = pwarp->GetCluster();
  711. IclusterIGC* pc2 = pwarp->GetDestination()->GetCluster();
  712. if ((pclusterMe == pc1) || (pclusterMe == pc2))
  713. PostText(true, "Aleph to %s destabilized\n", (pclusterMe == pc1) ? pc2->GetName() : pc1->GetName());
  714. /*
  715. DamageTypeID dtid = pmt->GetDamageType();
  716. float p = pmt->GetPower();
  717. float r = pmt->GetBlastRadius();
  718. IclusterIGC* pcluster = pwarp->GetCluster();
  719. pcluster->CreateExplosion(dtid,
  720. p,
  721. r,
  722. c_etBigShip,
  723. pcluster->GetLastUpdate(),
  724. pwarp->GetPosition(),
  725. NULL);
  726. pwarp = pwarp->GetDestination();
  727. pcluster = pwarp->GetCluster();
  728. pcluster->CreateExplosion(dtid,
  729. p,
  730. r,
  731. c_etBigShip,
  732. pcluster->GetLastUpdate(),
  733. pwarp->GetPosition(),
  734. NULL);
  735. */
  736. }
  737. }
  738. break;
  739. case FM_S_RIPCORD_ACTIVATE:
  740. {
  741. if (!IsInGame())
  742. break;
  743. CASTPFM(pfmRipcordActivate, S, RIPCORD_ACTIVATE, pfm);
  744. IshipIGC* pshipSource = m_ship->GetSourceShip();
  745. if (pfmRipcordActivate->shipidRipcord == pshipSource->GetObjectID())
  746. {
  747. //We are ripcording
  748. ImodelIGC* pmodelRipcord = m_pCoreIGC->GetModel(pfmRipcordActivate->otRipcord,
  749. pfmRipcordActivate->oidRipcord);
  750. IclusterIGC* pclusterDesired = m_pCoreIGC->GetCluster(pfmRipcordActivate->sidRipcord);
  751. assert (pclusterDesired);
  752. assert (pmodelRipcord);
  753. IclusterIGC* pclusterRipcord = pmodelRipcord->GetCluster();
  754. if (pclusterRipcord == NULL)
  755. {
  756. assert (pmodelRipcord->GetObjectType() == OT_ship);
  757. PlayerInfo* ppi = (PlayerInfo*)(((IshipIGC*)pmodelRipcord)->GetPrivateData());
  758. assert (ppi->StatusIsCurrent());
  759. pclusterRipcord = m_pCoreIGC->GetCluster(ppi->LastSeenSector());
  760. assert (pclusterRipcord);
  761. }
  762. const char* name = pclusterRipcord->GetName();
  763. char bfr[100];
  764. if (pclusterRipcord != pclusterDesired)
  765. sprintf(bfr, "Ripcording to %s, which is closest to %s",
  766. name, pclusterDesired->GetName());
  767. else
  768. sprintf(bfr, "Ripcording to %s", name);
  769. PostText(true, bfr);
  770. if (pmodelRipcord != pshipSource->GetRipcordModel())
  771. {
  772. pshipSource->SetRipcordModel(pmodelRipcord);
  773. pshipSource->ResetRipcordTimeLeft();
  774. }
  775. // set up the ripcord effect
  776. pshipSource->GetThingSite ()->SetTimeUntilRipcord (pshipSource->GetRipcordTimeLeft ());
  777. }
  778. else
  779. {
  780. //Someone else is ripcording ... just set the model cause we
  781. //really don't care
  782. IshipIGC* pship = m_pCoreIGC->GetShip(pfmRipcordActivate->shipidRipcord);
  783. //We need a valid model that will stick around at least as long as the
  784. //ship. Hmmm ... lets see.
  785. pship->SetRipcordModel(pship);
  786. // set up the ripcord effect
  787. pship->ResetRipcordTimeLeft ();
  788. pship->GetThingSite ()->SetTimeUntilRipcord (pship->GetRipcordTimeLeft ());
  789. }
  790. }
  791. break;
  792. case FM_S_RIPCORD_DENIED:
  793. {
  794. if (!IsInGame())
  795. break;
  796. PlayNotificationSound(salNoRipcordSound, m_ship);
  797. }
  798. break;
  799. case FM_S_RIPCORD_ABORTED:
  800. {
  801. if (!IsInGame())
  802. break;
  803. CASTPFM(pfmRipcordAborted, S, RIPCORD_ABORTED, pfm);
  804. IshipIGC* pship = m_pCoreIGC->GetShip(pfmRipcordAborted->shipidRipcord);
  805. assert (pship);
  806. pship->SetRipcordModel(NULL);
  807. if (pship == m_ship->GetSourceShip())
  808. PlayNotificationSound(salRipcordAbortedSound, pship);
  809. // clear the ripcord effect
  810. pship->GetThingSite ()->SetTimeUntilRipcord (-1.0f);
  811. }
  812. break;
  813. case FM_S_SET_CLUSTER:
  814. {
  815. CASTPFM(pfmSetCluster, S, SET_CLUSTER, pfm);
  816. if (IsLockedDown())
  817. EndLockDown(lockdownLoadout | lockdownTeleporting);
  818. // cancel any pending disembarks (it's now a bad idea to step out of the airlock)
  819. m_bLaunchAfterDisembark = false;
  820. m_sidBoardAfterDisembark = NA;
  821. m_sidTeleportAfterDisembark = NA;
  822. IclusterIGC* pcluster = m_pCoreIGC->GetCluster(pfmSetCluster->sectorID);
  823. assert (pcluster);
  824. //The ship update is either for us or for our parent ship
  825. IshipIGC* pship = m_ship->GetSourceShip();
  826. //Pretend the server sends a ship delete message for everything the player could see
  827. {
  828. //We could use the old cluster ... but modifying the contents of a list
  829. const ShipListIGC* ships = m_pCoreIGC->GetShips();
  830. assert (ships);
  831. for (ShipLinkIGC* l = ships->first();
  832. (l != NULL);
  833. l = l->next())
  834. {
  835. IshipIGC* s = l->data();
  836. if ((s != pship) && (s != m_ship))
  837. s->SetCluster(NULL);
  838. }
  839. }
  840. {
  841. //Reset the ship's trail
  842. assert (pship->GetThingSite());
  843. pship->GetThingSite()->SetTrailColor(GetShip()->GetSide()->GetColor());
  844. IclusterIGC* pclusterOld;
  845. Orientation orientationOld;
  846. if (pship != m_ship)
  847. {
  848. pclusterOld = NULL;
  849. orientationOld = pship->GetOrientation();
  850. }
  851. Time time = pcluster->GetLastUpdate();
  852. pship->SetLastUpdate(time);
  853. pfmSetCluster->shipupdate.time = ClientTimeFromServerTime(pfmSetCluster->shipupdate.time);
  854. pship->ProcessShipUpdate(pfmSetCluster->shipupdate);
  855. if (m_ship->GetStation() != NULL)
  856. {
  857. assert (m_ship->GetCluster() == NULL);
  858. m_ship->SetStation(NULL); //This will call IIgcSite::ChangeStation()
  859. }
  860. else
  861. {
  862. pclusterOld = m_ship->GetCluster();
  863. if (pclusterOld != NULL)
  864. {
  865. //Hack ... play the sound effect for an aleph since the player
  866. //did not undock.
  867. PlaySoundEffect(jumpSound);
  868. }
  869. }
  870. assert (m_ship->GetStation() == NULL);
  871. if (pship == m_ship)
  872. {
  873. //Set the cookie
  874. SetCookie(pfmSetCluster->cookie);
  875. }
  876. else
  877. {
  878. //Adjust our orientation
  879. Mount tid = m_ship->GetTurretID();
  880. if (tid != NA)
  881. {
  882. if (pclusterOld == NULL)
  883. {
  884. Orientation oTurret = pship->GetHullType()->GetWeaponOrientation(tid) *
  885. pship->GetOrientation();
  886. m_ship->SetOrientation(oTurret);
  887. }
  888. else
  889. {
  890. //Preserve the old orientation relative to the ship
  891. m_ship->SetOrientation(pship->GetOrientation().TimesInverse(orientationOld) * m_ship->GetOrientation());
  892. }
  893. }
  894. }
  895. pship->SetCluster(pcluster);
  896. }
  897. }
  898. break;
  899. case FM_S_DOCKED:
  900. {
  901. CASTPFM(pfmDocked, S, DOCKED, pfm);
  902. //Pretend the server sends a ship delete message for everything the player could see
  903. {
  904. //We could use the old cluster ... but modifying the contents of a list
  905. const ShipListIGC* ships = m_pCoreIGC->GetShips();
  906. assert (ships);
  907. for (ShipLinkIGC* l = ships->first();
  908. (l != NULL);
  909. l = l->next())
  910. {
  911. IshipIGC* s = l->data();
  912. if (s != m_ship)
  913. s->SetCluster(NULL);
  914. }
  915. }
  916. m_ship->SetStation(m_pCoreIGC->GetStation(pfmDocked->stationID));
  917. assert (m_ship->GetCluster() == NULL);
  918. }
  919. break;
  920. case FM_S_SINGLE_SHIP_UPDATE:
  921. {
  922. if (IsWaitingForGameRestart())
  923. break;
  924. IclusterIGC* pcluster = GetCluster();
  925. if (pcluster)
  926. {
  927. CASTPFM(pfmSSU, S, SINGLE_SHIP_UPDATE, pfm);
  928. //Ignore single ship updates for ourself
  929. if (m_ship->GetObjectID() != pfmSSU->shipupdate.shipID)
  930. {
  931. //Ignore single shup updates for our parent (this data comes via SetCluster).
  932. IshipIGC* pshipParent = m_ship->GetParentShip();
  933. if ((pshipParent == NULL) ||
  934. (pshipParent->GetObjectID() != pfmSSU->shipupdate.shipID))
  935. {
  936. IshipIGC* ship = m_pCoreIGC->GetShip(pfmSSU->shipupdate.shipID);
  937. assert (ship);
  938. assert (ship->GetBaseHullType());
  939. //Never get single ship updates for passengers
  940. assert (ship->GetParentShip() == NULL);
  941. //Reset the ship's trail
  942. assert (ship->GetThingSite());
  943. ship->GetThingSite()->SetTrailColor(ship->GetSide()->GetColor());
  944. //Force the updates to be processed, even if it is out of sync with the local time
  945. Time time = pcluster->GetLastUpdate();
  946. ship->SetLastUpdate(time);
  947. pfmSSU->shipupdate.time = ClientTimeFromServerTime(pfmSSU->shipupdate.time);
  948. ship->ProcessShipUpdate(pfmSSU->shipupdate);
  949. ship->SetRipcordModel(pfmSSU->bIsRipcording ? ship : NULL); //Just has to be a valid pointer
  950. {
  951. ImodelIGC* pmodel = m_pCoreIGC->GetModel(pfmSSU->otTarget, pfmSSU->oidTarget);
  952. ship->SetCommand(c_cmdCurrent, pmodel, c_cidNone);
  953. }
  954. if (ship->GetCluster() == NULL)
  955. {
  956. // The ship has moved to the same cluster as the player
  957. debugf("Moving %s/%d to %s\n",
  958. ship->GetName(), ship->GetObjectID(),
  959. pcluster->GetName());
  960. ship->SetCluster(pcluster);
  961. ship->SetLastUpdate(time);
  962. }
  963. else
  964. assert (ship->GetCluster() == pcluster);
  965. }
  966. }
  967. }
  968. }
  969. break;
  970. case FM_S_SHIP_RESET:
  971. {
  972. CASTPFM(pfmShipReset, S, SHIP_RESET, pfm);
  973. if (m_ship->GetCluster() && (m_ship->GetParentShip() == NULL))
  974. {
  975. pfmShipReset->shipupdate.time = ClientTimeFromServerTime(pfmShipReset->shipupdate.time);
  976. m_ship->ProcessShipUpdate(pfmShipReset->shipupdate, false);
  977. SetCookie(pfmShipReset->cookie);
  978. }
  979. }
  980. break;
  981. case FM_S_AUTODONATE:
  982. {
  983. CASTPFM(pfmAutoDonate, S, AUTODONATE, pfm);
  984. IshipIGC* pshipBy = m_ship->GetSide()->GetShip(pfmAutoDonate->sidDonateBy);
  985. assert (pshipBy);
  986. IshipIGC* pshipTo = pfmAutoDonate->sidDonateTo == NA
  987. ? NULL
  988. : m_ship->GetSide()->GetShip(pfmAutoDonate->sidDonateTo);
  989. IshipIGC* pshipOld = pshipBy->GetAutoDonate();
  990. pshipBy->SetAutoDonate(pshipTo);
  991. if ((m_pCoreIGC->GetMissionStage() == STAGE_STARTED) && (pshipOld != pshipTo))
  992. {
  993. if (pshipBy == GetShip())
  994. {
  995. if (pshipTo)
  996. {
  997. char bfr[100];
  998. sprintf(bfr, "You have started donating your income to %s", pshipTo->GetName());
  999. PostText(true, bfr);
  1000. }
  1001. else
  1002. {
  1003. PostText(true, "You have stopped donating your income to %s", pshipOld->GetName());
  1004. }
  1005. }
  1006. else if (pshipTo == GetShip())
  1007. {
  1008. char bfr[100];
  1009. sprintf(bfr, "%s is now donating to you", pshipBy->GetName());
  1010. PostText(true, bfr);
  1011. // count donors, and play the designated investor sound if we've gone from 0 to 1
  1012. int nNumDonors = 0;
  1013. for (ShipLinkIGC* psl = GetSide()->GetShips()->first();
  1014. (psl != NULL);
  1015. psl = psl->next())
  1016. {
  1017. if (psl->data()->GetAutoDonate() == GetShip())
  1018. nNumDonors++;
  1019. }
  1020. if (nNumDonors == 1)
  1021. PlaySoundEffect(investorSound);
  1022. }
  1023. else if (pshipOld == GetShip())
  1024. {
  1025. char bfr[100];
  1026. sprintf(bfr, "%s has stopped donating to you", pshipBy->GetName());
  1027. PostText(true, bfr);
  1028. }
  1029. }
  1030. if (pfmAutoDonate->amount != 0)
  1031. {
  1032. if (pshipTo)
  1033. {
  1034. //An autodonation to another player ... increase the target's money
  1035. PlayerInfo* ppiTo = (PlayerInfo*)(pshipTo->GetPrivateData());
  1036. ppiTo->SetMoney(ppiTo->GetMoney() + pfmAutoDonate->amount);
  1037. m_pClientEventSource->OnMoneyChange(ppiTo);
  1038. if (pshipBy != m_ship)
  1039. {
  1040. //Someone other than me autodonated ... subtract their money
  1041. PlayerInfo* ppiBy = (PlayerInfo*)(pshipBy->GetPrivateData());
  1042. ppiBy->SetMoney(ppiBy->GetMoney() - pfmAutoDonate->amount);
  1043. m_pClientEventSource->OnMoneyChange(ppiBy);
  1044. }
  1045. }
  1046. else if (pshipBy == m_ship)
  1047. {
  1048. //We made a request and it was denied ... get a refund.
  1049. SetMoney(GetMoney() + pfmAutoDonate->amount);
  1050. m_pClientEventSource->OnMoneyChange(m_pPlayerInfo);
  1051. }
  1052. }
  1053. SideInfo* psideinfo = m_pMissionInfo->GetSideInfo(GetSideID());
  1054. if (psideinfo)
  1055. {
  1056. psideinfo->GetMembers().GetSink()();
  1057. }
  1058. }
  1059. break;
  1060. case FM_S_PAYDAY:
  1061. {
  1062. if (!IsInGame())
  1063. break;
  1064. CASTPFM(pfmPayday, S, PAYDAY, pfm);
  1065. Money moneyReceived = 0;
  1066. for (ShipLinkIGC* psl = GetSide()->GetShips()->first();
  1067. (psl != NULL);
  1068. psl = psl->next())
  1069. {
  1070. IshipIGC* pship = psl->data();
  1071. if (pship->GetPilotType() >= c_ptPlayer)
  1072. {
  1073. IshipIGC* pshipDonate = pship->GetAutoDonate();
  1074. if (pshipDonate)
  1075. {
  1076. if (pshipDonate == m_ship)
  1077. moneyReceived += pfmPayday->dMoney;
  1078. }
  1079. else
  1080. pshipDonate = pship;
  1081. PlayerInfo* ppi = (PlayerInfo*)(pshipDonate->GetPrivateData());
  1082. ppi->SetMoney(ppi->GetMoney() + pfmPayday->dMoney);
  1083. m_pClientEventSource->OnMoneyChange(ppi);
  1084. }
  1085. }
  1086. m_pMissionInfo->GetSideInfo(GetSide()->GetObjectID())->GetMembers().GetSink()();
  1087. PlaySoundEffect(paydaySound);
  1088. {
  1089. IshipIGC* pshipDonate = m_ship->GetAutoDonate();
  1090. if (pshipDonate)
  1091. PostText(false, "You donated your payday of $%d to %s.", pfmPayday->dMoney, pshipDonate->GetName());
  1092. else if (moneyReceived == 0)
  1093. PostText(false, "You received a payday of $%d.", pfmPayday->dMoney);
  1094. else
  1095. PostText(false, "You received $%d in pay and donations.", pfmPayday->dMoney + moneyReceived);
  1096. }
  1097. }
  1098. break;
  1099. case FM_S_MONEY_CHANGE:
  1100. {
  1101. if (!IsInGame())
  1102. break;
  1103. CASTPFM(pfmMoney, S, MONEY_CHANGE, pfm);
  1104. //Ignore any transactions we initiated.
  1105. ShipID sid = GetShipID();
  1106. if (sid != pfmMoney->sidFrom)
  1107. {
  1108. IshipIGC* pshipTo = m_pCoreIGC->GetShip(pfmMoney->sidTo);
  1109. assert (pshipTo);
  1110. {
  1111. PlayerInfo* ppiTo = (PlayerInfo*)(pshipTo->GetPrivateData());
  1112. ppiTo->SetMoney(ppiTo->GetMoney() + pfmMoney->dMoney);
  1113. m_pClientEventSource->OnMoneyChange(ppiTo);
  1114. }
  1115. if ((pfmMoney->sidTo != pfmMoney->sidFrom) && (pfmMoney->sidFrom != NA))
  1116. {
  1117. //This was a donation from one player to another
  1118. IshipIGC* pshipFrom = m_pCoreIGC->GetShip(pfmMoney->sidFrom);
  1119. assert (pshipFrom);
  1120. {
  1121. PlayerInfo* ppiFrom = (PlayerInfo*)(pshipFrom->GetPrivateData());
  1122. ppiFrom->SetMoney(ppiFrom->GetMoney() - pfmMoney->dMoney);
  1123. m_pClientEventSource->OnMoneyChange(ppiFrom);
  1124. if (pfmMoney->sidTo == sid)
  1125. PostText(false, "%s gave you $%d. You now have $%d.",
  1126. ppiFrom->CharacterName(), pfmMoney->dMoney, MyPlayerInfo()->GetMoney());
  1127. }
  1128. }
  1129. m_pMissionInfo->GetSideInfo(GetSide()->GetObjectID())->GetMembers().GetSink()();
  1130. }
  1131. }
  1132. break;
  1133. case FM_S_SET_MONEY:
  1134. {
  1135. CASTPFM(pfmMoney, S, SET_MONEY, pfm);
  1136. IshipIGC* pship = m_pCoreIGC->GetShip(pfmMoney->shipID);
  1137. assert (pship);
  1138. {
  1139. PlayerInfo* ppi = (PlayerInfo*)(pship->GetPrivateData());
  1140. ppi->SetMoney(pfmMoney->money);
  1141. m_pClientEventSource->OnMoneyChange(ppi);
  1142. }
  1143. m_pMissionInfo->GetSideInfo(GetSide()->GetObjectID())->GetMembers().GetSink()();
  1144. }
  1145. break;
  1146. case FM_S_ADD_PART:
  1147. {
  1148. if (!IsInGame())
  1149. break;
  1150. CASTPFM(pfmAddPart, S, ADD_PART, pfm);
  1151. if (pfmAddPart->shipID != m_ship->GetObjectID())
  1152. {
  1153. IshipIGC* pShip = m_pCoreIGC->GetShip(pfmAddPart->shipID);
  1154. assert (pShip);
  1155. assert (pShip->GetParentShip() == NULL);
  1156. assert (pShip->GetBaseHullType());
  1157. IpartTypeIGC* ppt = m_pCoreIGC->GetPartType(pfmAddPart->newPartData.partID);
  1158. assert (ppt);
  1159. IpartIGC* ppart = pShip->GetMountedPart(ppt->GetEquipmentType(), pfmAddPart->newPartData.mountID);
  1160. if (ppart)
  1161. {
  1162. assert (ppart->GetPartType() == ppt);
  1163. ppart->SetAmount(ppart->GetAmount() + pfmAddPart->newPartData.amount);
  1164. }
  1165. else
  1166. pShip->CreateAndAddPart(ppt, pfmAddPart->newPartData.mountID, pfmAddPart->newPartData.amount);
  1167. }
  1168. }
  1169. break;
  1170. case FM_CS_DROP_PART:
  1171. {
  1172. if (!IsInGame())
  1173. break;
  1174. CASTPFM(pfmDropPart, CS, DROP_PART, pfm);
  1175. if (pfmDropPart->shipID != m_ship->GetObjectID())
  1176. {
  1177. IshipIGC* pShip = m_pCoreIGC->GetShip(pfmDropPart->shipID);
  1178. assert (pShip);
  1179. assert (pShip->GetParentShip() == NULL);
  1180. assert (pShip->GetBaseHullType());
  1181. IpartIGC* ppart = pShip->GetMountedPart(pfmDropPart->et, pfmDropPart->mount);
  1182. assert (ppart);
  1183. ppart->Terminate();
  1184. }
  1185. }
  1186. break;
  1187. case FM_CS_SWAP_PART:
  1188. {
  1189. if (!IsInGame())
  1190. break;
  1191. CASTPFM(pfmSwapPart, CS, SWAP_PART, pfm);
  1192. if (pfmSwapPart->shipID != m_ship->GetObjectID())
  1193. {
  1194. IshipIGC* pShip = m_pCoreIGC->GetShip(pfmSwapPart->shipID);
  1195. assert (pShip);
  1196. assert (pShip->GetParentShip() == NULL);
  1197. assert (pShip->GetBaseHullType());
  1198. assert (pfmSwapPart->mountNew >= -c_maxCargo);
  1199. IpartIGC* ppart = pShip->GetMountedPart(pfmSwapPart->etOld, pfmSwapPart->mountOld);
  1200. assert (ppart);
  1201. IpartIGC* ppartNew = pShip->GetMountedPart(pfmSwapPart->etOld, pfmSwapPart->mountNew);
  1202. if (ppartNew)
  1203. {
  1204. ppart->SetMountID(c_mountNA);
  1205. ppartNew->SetMountID(pfmSwapPart->mountOld);
  1206. }
  1207. ppart->SetMountID(pfmSwapPart->mountNew);
  1208. }
  1209. }
  1210. break;
  1211. case FM_CS_RELOAD:
  1212. {
  1213. if (!IsInGame())
  1214. break;
  1215. CASTPFM(pfmReload, CS, RELOAD, pfm);
  1216. if (pfmReload->shipID != m_ship->GetObjectID())
  1217. {
  1218. IshipIGC* pship = m_pCoreIGC->GetShip(pfmReload->shipID);
  1219. assert (pship);
  1220. ReloadData* prlNext = (ReloadData*)FM_VAR_REF(pfmReload, rgReloads);
  1221. int nReloads = pfmReload->cbrgReloads / sizeof(ReloadData);
  1222. ReloadData* prlStop = prlNext + nReloads;
  1223. while (prlNext < prlStop)
  1224. {
  1225. assert (prlNext->mount < 0);
  1226. assert (prlNext->mount >= -c_maxCargo);
  1227. IpartIGC* ppart = pship->GetMountedPart(NA, prlNext->mount);
  1228. ObjectType type = ppart->GetObjectType();
  1229. if (type == OT_pack)
  1230. {
  1231. IpackIGC* ppack = (IpackIGC*)ppart;
  1232. PackType packtype = ppack->GetPackType();
  1233. short amount = ppack->GetAmount();
  1234. if (prlNext->amountTransfered == NA)
  1235. {
  1236. OnReload(ppart, true);
  1237. ppack->Terminate();
  1238. }
  1239. else
  1240. {
  1241. OnReload(ppart, false);
  1242. assert (prlNext->amountTransfered > 0);
  1243. assert (prlNext->amountTransfered < amount);
  1244. ppack->SetAmount(amount - prlNext->amountTransfered);
  1245. amount = prlNext->amountTransfered;
  1246. }
  1247. if (packtype == c_packAmmo)
  1248. {
  1249. pship->SetAmmo(pship->GetAmmo() + amount);
  1250. //disable all mounted weapons that use ammo
  1251. Mount maxWeapons = pship->GetHullType()->GetMaxWeapons();
  1252. for (Mount i = 0; (i < maxWeapons); i++)
  1253. {
  1254. IweaponIGC* pw = (IweaponIGC*)(pship->GetMountedPart(ET_Weapon, i));
  1255. if (pw && (pw->GetAmmoPerShot() != 0))
  1256. pw->SetMountedFraction(0.0f);
  1257. }
  1258. }
  1259. else
  1260. {
  1261. assert (packtype == c_packFuel);
  1262. pship->SetFuel(pship->GetFuel() + float(amount));
  1263. IpartIGC* pa = pship->GetMountedPart(ET_Afterburner, 0);
  1264. if (pa)
  1265. pa->SetMountedFraction(0.0f);
  1266. }
  1267. }
  1268. else
  1269. {
  1270. assert (IlauncherIGC::IsLauncher(type));
  1271. IlauncherIGC* plauncher = (IlauncherIGC*)ppart;
  1272. IlauncherIGC* plauncherMounted = (IlauncherIGC*)(pship->GetMountedPart(plauncher->GetEquipmentType(), 0));
  1273. if (prlNext->amountTransfered == NA)
  1274. {
  1275. OnReload(ppart, true);
  1276. assert ((plauncherMounted == NULL) || (plauncherMounted->GetAmount() == 0));
  1277. if (plauncherMounted)
  1278. plauncherMounted->Terminate();
  1279. plauncher->SetMountID(0);
  1280. }
  1281. else
  1282. {
  1283. assert (prlNext->amountTransfered <= plauncher->GetAmount());
  1284. assert (plauncherMounted);
  1285. short amount = plauncher->GetAmount();
  1286. if (amount == prlNext->amountTransfered)
  1287. {
  1288. OnReload(ppart, true);
  1289. plauncher->Terminate();
  1290. }
  1291. else
  1292. {
  1293. OnReload(ppart, false);
  1294. plauncher->SetAmount(amount - prlNext->amountTransfered);
  1295. }
  1296. plauncherMounted->SetAmount(plauncherMounted->GetAmount() + prlNext->amountTransfered);
  1297. plauncherMounted->SetMountedFraction(0.0f);
  1298. plauncherMounted->ResetTimeLoaded();
  1299. }
  1300. }
  1301. prlNext++;
  1302. }
  1303. }
  1304. }
  1305. break;
  1306. case FM_CS_FIRE_MISSILE:
  1307. {
  1308. if (!IsInGame())
  1309. break;
  1310. CASTPFM(pfmFireMissile, CS, FIRE_MISSILE, pfm);
  1311. DataMissileIGC dm;
  1312. dm.pLauncher = m_pCoreIGC->GetShip(pfmFireMissile->launcherID);
  1313. if (dm.pLauncher)
  1314. {
  1315. dm.pmissiletype = (ImissileTypeIGC*)(m_pCoreIGC->GetExpendableType(pfmFireMissile->missiletypeID));
  1316. dm.pTarget = m_pCoreIGC->GetModel(pfmFireMissile->targetType, pfmFireMissile->targetID);
  1317. dm.pCluster = m_pCoreIGC->GetCluster(pfmFireMissile->clusterID);
  1318. dm.lock = pfmFireMissile->lock;
  1319. Time timeFired = ClientTimeFromServerTime(pfmFireMissile->timeFired);
  1320. int iNumMissiles = pfmFireMissile->cbmissileLaunchData / sizeof(MissileLaunchData);
  1321. MissileLaunchData* pMissileLaunchData = (MissileLaunchData*) (FM_VAR_REF(pfmFireMissile, missileLaunchData));
  1322. for (int i = 0; i < iNumMissiles; i++)
  1323. {
  1324. dm.position = pMissileLaunchData[i].vecPosition;
  1325. dm.velocity = pMissileLaunchData[i].vecVelocity;
  1326. dm.forward = pMissileLaunchData[i].vecForward;
  1327. dm.missileID = pMissileLaunchData[i].missileID;
  1328. dm.bDud = pfmFireMissile->bDud;
  1329. ImissileIGC* m = (ImissileIGC*)(m_pCoreIGC->CreateObject(timeFired, OT_missile, &dm, sizeof(dm)));
  1330. if (m)
  1331. m->Release();
  1332. }
  1333. //The player immediately adjusts their own count on launching the missile so don't update
  1334. //their count.
  1335. if (pfmFireMissile->launcherID != m_ship->GetObjectID())
  1336. {
  1337. ImagazineIGC* pmagazine = (ImagazineIGC*)(dm.pLauncher->GetMountedPart(ET_Magazine, 0));
  1338. if (pmagazine)
  1339. {
  1340. short amount = pmagazine->GetAmount() - iNumMissiles;
  1341. assert (amount >= 0);
  1342. pmagazine->SetAmount(amount);
  1343. }
  1344. }
  1345. }
  1346. }
  1347. break;
  1348. case FM_S_FIRE_EXPENDABLE:
  1349. {
  1350. if (!IsInGame())
  1351. break;
  1352. CASTPFM(pfmFireExpendable, S, FIRE_EXPENDABLE, pfm);
  1353. //Ignore messages that you fired a mine since you already decremented the mine count
  1354. if (pfmFireExpendable->launcherID != m_ship->GetObjectID())
  1355. {
  1356. IshipIGC* pshipLauncher = m_pCoreIGC->GetShip(pfmFireExpendable->launcherID);
  1357. if (pshipLauncher)
  1358. {
  1359. IdispenserIGC* pdispenser = (IdispenserIGC*)(pshipLauncher->GetMountedPart(pfmFireExpendable->equipmentType, 0));
  1360. if (pdispenser)
  1361. {
  1362. short amount = pdispenser->GetAmount() - 1;
  1363. assert (amount >= 0);
  1364. pdispenser->SetAmount(amount);
  1365. }
  1366. }
  1367. }
  1368. }
  1369. break;
  1370. case FM_S_CREATE_CHAFF:
  1371. {
  1372. CASTPFM(pfmChaff, S, CREATE_CHAFF, pfm);
  1373. DataChaffIGC dc;
  1374. if (GetCluster())
  1375. {
  1376. dc.time0 = pfmChaff->time0;
  1377. dc.p0 = pfmChaff->p0;
  1378. dc.v0 = pfmChaff->v0;
  1379. dc.pchafftype = (IchaffTypeIGC*)(m_pCoreIGC->GetExpendableType(pfmChaff->etid));
  1380. assert (dc.pchafftype->GetObjectType() == OT_chaffType);
  1381. dc.pcluster = GetCluster();
  1382. IchaffIGC* c = (IchaffIGC*)(m_pCoreIGC->CreateObject(lastUpdate, OT_chaff, &dc, sizeof(dc)));
  1383. assert (c);
  1384. m_pchaffLastCreated = c;
  1385. c->Release();
  1386. }
  1387. }
  1388. break;
  1389. case FM_S_MISSILE_SPOOFED:
  1390. {
  1391. CASTPFM(pfmMissileSpoofed, S, MISSILE_SPOOFED, pfm);
  1392. // need to make sure we have a cluster because of a race condition
  1393. // when changing view clusters.
  1394. if (GetCluster())
  1395. {
  1396. ImissileIGC* pmissile = GetCluster()->GetMissile(pfmMissileSpoofed->missileID);
  1397. if (pmissile)
  1398. {
  1399. assert (m_pchaffLastCreated);
  1400. pmissile->SetTarget(m_pchaffLastCreated);
  1401. }
  1402. }
  1403. }
  1404. break;
  1405. case FM_S_END_SPOOFING:
  1406. {
  1407. m_pchaffLastCreated = NULL;
  1408. }
  1409. break;
  1410. case FM_S_ASTEROID_DESTROYED:
  1411. {
  1412. if (!IsInGame())
  1413. break;
  1414. CASTPFM(pfmAD, S, ASTEROID_DESTROYED, pfm);
  1415. IclusterIGC* pcluster = m_pCoreIGC->GetCluster(pfmAD->clusterID);
  1416. assert (pcluster);
  1417. IasteroidIGC* pasteroid = pcluster->GetAsteroid(pfmAD->asteroidID);
  1418. if (pasteroid)
  1419. {
  1420. if (pfmAD->explodeF)
  1421. pasteroid->GetCluster()->GetClusterSite()->AddExplosion(pasteroid, c_etAsteroid);
  1422. else
  1423. {
  1424. //Pre-terminate the building effect so that terminating the asteroid does not terminate the effect
  1425. //If the server is really lagged (30 seconds or more), the building effect will terminate locally
  1426. //before we get the message. This isn't good, but we shouldn't crash.
  1427. if (pasteroid->GetBuildingEffect())
  1428. pasteroid->SetBuildingEffect(NULL);
  1429. }
  1430. pasteroid->Terminate();
  1431. }
  1432. }
  1433. break;
  1434. case FM_S_ASTEROID_DRAINED:
  1435. {
  1436. if (!IsInGame())
  1437. break;
  1438. CASTPFM(pfmAD, S, ASTEROID_DRAINED, pfm);
  1439. IclusterIGC* pcluster = m_pCoreIGC->GetCluster(pfmAD->clusterID);
  1440. assert (pcluster);
  1441. IasteroidIGC* pasteroid = pcluster->GetAsteroid(pfmAD->asteroidID);
  1442. if (pasteroid)
  1443. {
  1444. pasteroid->SetOre(0.0f);
  1445. }
  1446. }
  1447. break;
  1448. case FM_S_PROBE_DESTROYED:
  1449. {
  1450. if (!IsInGame())
  1451. break;
  1452. CASTPFM(pfmPD, S, PROBE_DESTROYED, pfm);
  1453. IclusterIGC* pcluster = m_pCoreIGC->GetCluster(pfmPD->clusterID);
  1454. assert (pcluster);
  1455. IprobeIGC* pprobe = pcluster->GetProbe(pfmPD->probeID);
  1456. if (pprobe)
  1457. pprobe->Terminate();
  1458. }
  1459. break;
  1460. case FM_S_BUILDINGEFFECT_DESTROYED:
  1461. {
  1462. if (!IsInGame())
  1463. break;
  1464. CASTPFM(pfmBED, S, BUILDINGEFFECT_DESTROYED, pfm);
  1465. IasteroidIGC* pasteroid = m_pCoreIGC->GetAsteroid(pfmBED->asteroidID);
  1466. if (pasteroid)
  1467. {
  1468. IbuildingEffectIGC* pbe = pasteroid->GetBuildingEffect();
  1469. if (pbe)
  1470. pbe->Terminate();
  1471. }
  1472. }
  1473. break;
  1474. case FM_S_MISSILE_DESTROYED:
  1475. {
  1476. if (!IsInGame())
  1477. break;
  1478. CASTPFM(pfmMD, S, MISSILE_DESTROYED, pfm);
  1479. IclusterIGC* pcluster = m_pCoreIGC->GetCluster(pfmMD->clusterID);
  1480. assert (pcluster);
  1481. ImissileIGC* pmissile = pcluster->GetMissile(pfmMD->missileID);
  1482. if (pmissile)
  1483. {
  1484. if (pfmMD->position != Vector::GetZero())
  1485. pmissile->Explode(pfmMD->position);
  1486. pmissile->Terminate();
  1487. }
  1488. }
  1489. break;
  1490. case FM_S_MINE_DESTROYED:
  1491. {
  1492. if (!IsInGame())
  1493. break;
  1494. CASTPFM(pfmMD, S, MINE_DESTROYED, pfm);
  1495. IclusterIGC* pcluster = m_pCoreIGC->GetCluster(pfmMD->clusterID);
  1496. assert (pcluster);
  1497. ImineIGC* pmine = pcluster->GetMine(pfmMD->mineID);
  1498. if (pmine)
  1499. pmine->Terminate();
  1500. }
  1501. break;
  1502. case FM_CS_ORDER_CHANGE:
  1503. {
  1504. if (!IsInGame())
  1505. break;
  1506. SideInfo* psideinfo = m_pMissionInfo->GetSideInfo(GetSideID());
  1507. if (psideinfo)
  1508. {
  1509. CASTPFM(pfmOC, CS, ORDER_CHANGE, pfm);
  1510. IshipIGC* pship = m_pCoreIGC->GetShip(pfmOC->shipID);
  1511. if (pship)
  1512. {
  1513. ImodelIGC* pmodel = m_pCoreIGC->GetModel(pfmOC->objectType, pfmOC->objectID);
  1514. if (pship != m_ship)
  1515. pship->SetCommand(pfmOC->command, pmodel, pfmOC->commandID);
  1516. else if (pfmOC->command == c_cmdCurrent)
  1517. m_pmodelServerTarget = pmodel;
  1518. psideinfo->GetMembers().GetSink()();
  1519. }
  1520. }
  1521. }
  1522. break;
  1523. case FM_S_TELEPORT_ACK:
  1524. {
  1525. if (!IsInGame())
  1526. break;
  1527. if (IsLockedDown())
  1528. EndLockDown(lockdownTeleporting);
  1529. CASTPFM(pfmAck, S, TELEPORT_ACK, pfm);
  1530. if (pfmAck->stationID != NA)
  1531. {
  1532. IstationIGC* pstation = m_pCoreIGC->GetStation(pfmAck->stationID);
  1533. assert (pstation);
  1534. m_ship->SetStation(pstation);
  1535. if (pfmAck->bNewHull)
  1536. {
  1537. //If the ship had children, it doesn't now
  1538. {
  1539. const ShipListIGC* pshipsChildren = m_ship->GetChildShips();
  1540. ShipLinkIGC* psl;
  1541. while (psl = pshipsChildren->first()) //intentional assignment
  1542. {
  1543. IshipIGC* pshipChild = psl->data();
  1544. pshipChild->SetParentShip(NULL);
  1545. m_pClientEventSource->OnBoardShip(pshipChild, NULL);
  1546. }
  1547. }
  1548. const PartListIGC* pparts = m_ship->GetParts();
  1549. PartLinkIGC* ppl;
  1550. while (ppl = pparts->first()) //Intentional
  1551. ppl->data()->Terminate();
  1552. ReplaceLoadout(pstation);
  1553. }
  1554. PlaySoundEffect(personalJumpSound);
  1555. }
  1556. }
  1557. break;
  1558. case FM_S_BOARD_NACK:
  1559. {
  1560. if (!IsInGame())
  1561. break;
  1562. if (IsLockedDown())
  1563. EndLockDown(lockdownTeleporting);
  1564. CASTPFM(pfmBoardNack, S, BOARD_NACK, pfm);
  1565. IshipIGC* pshipRequestedParent = m_pCoreIGC->GetShip(pfmBoardNack->sidRequestedParent);
  1566. m_pClientEventSource->OnBoardFailed(pshipRequestedParent);
  1567. }
  1568. break;
  1569. case FM_S_LEAVE_SHIP:
  1570. {
  1571. if (!IsInGame())
  1572. break;
  1573. CASTPFM(pfmLeave, S, LEAVE_SHIP, pfm);
  1574. IshipIGC* pshipChild = m_pCoreIGC->GetShip(pfmLeave->sidChild);
  1575. assert (pshipChild);
  1576. pshipChild->SetParentShip(NULL);
  1577. m_pClientEventSource->OnBoardShip(pshipChild, NULL);
  1578. if (pshipChild == m_ship)
  1579. {
  1580. assert (pshipChild->GetBaseHullType() == NULL);
  1581. assert (pshipChild->GetParts()->n() == 0);
  1582. if (IsLockedDown())
  1583. EndLockDown(lockdownTeleporting);
  1584. pshipChild->SetBaseHullType(pshipChild->GetSide()->GetCivilization()->GetLifepod());
  1585. ReplaceLoadout(m_ship->GetStation(), m_bLaunchAfterDisembark);
  1586. m_bLaunchAfterDisembark = false;
  1587. }
  1588. else if (pshipChild->GetParentShip() == m_ship)
  1589. PostText(true, "%s has left your ship.", pshipChild->GetName());
  1590. }
  1591. break;
  1592. case FM_S_ENTER_LIFEPOD:
  1593. {
  1594. if (!IsInGame())
  1595. break;
  1596. CASTPFM(pfmEnter, S, ENTER_LIFEPOD, pfm);
  1597. /*
  1598. IshipIGC* pship = m_pCoreIGC->GetShip(pfmEnter->shipID);
  1599. assert (pship);
  1600. */
  1601. m_ship->SetParentShip(NULL);
  1602. m_pClientEventSource->OnBoardShip(m_ship, NULL);
  1603. //If the ship had children, it doesn't now
  1604. {
  1605. const ShipListIGC* pshipsChildren = m_ship->GetChildShips();
  1606. ShipLinkIGC* psl;
  1607. while (psl = pshipsChildren->first()) //intentional assignment
  1608. {
  1609. IshipIGC* pshipChild = psl->data();
  1610. pshipChild->SetParentShip(NULL);
  1611. m_pClientEventSource->OnBoardShip(pshipChild, NULL);
  1612. }
  1613. }
  1614. {
  1615. const PartListIGC* pparts = m_ship->GetParts();
  1616. PartLinkIGC* ppl;
  1617. while (ppl = pparts->first()) //Intentional
  1618. ppl->data()->Terminate();
  1619. }
  1620. m_ship->SetBaseHullType(m_ship->GetSide()->GetCivilization()->GetLifepod());
  1621. }
  1622. break;
  1623. case FM_CS_LOGOFF:
  1624. {
  1625. m_fLoggedOn = false;
  1626. Disconnect(); // we could already be calling from Disconnect, if the user Alt-F4ed, but this is handled
  1627. break;
  1628. }
  1629. case FM_S_LOADOUT_CHANGE:
  1630. {
  1631. if (IsWaitingForGameRestart())
  1632. break;
  1633. CASTPFM(pfmLC, S, LOADOUT_CHANGE, pfm);
  1634. IshipIGC* pship = pfmLC->sidShip == NA
  1635. ? m_ship
  1636. : m_pCoreIGC->GetShip(pfmLC->sidShip);
  1637. assert (pship);
  1638. //debugf("Loadout change for %s/%d\n", pship->GetName(), pfmLC->sidShip);
  1639. //If the ship was a passenger, now it is not
  1640. pship->SetParentShip(NULL);
  1641. m_pClientEventSource->OnBoardShip(pship, NULL);
  1642. //If the ship had children, it doesn't now
  1643. {
  1644. const ShipListIGC* pshipsChildren = pship->GetChildShips();
  1645. ShipLinkIGC* psl;
  1646. while (psl = pshipsChildren->first()) //intentional assignment
  1647. {
  1648. IshipIGC* pshipChild = psl->data();
  1649. pshipChild->SetParentShip(NULL);
  1650. m_pClientEventSource->OnBoardShip(pshipChild, NULL);
  1651. }
  1652. }
  1653. //Ignore most part changes for ourselves
  1654. if (pfmLC->sidShip != GetShipID())
  1655. {
  1656. pship->ProcessShipLoadout(pfmLC->cbloadout,
  1657. (const ShipLoadout*)(FM_VAR_REF(pfmLC, loadout)), (pship->GetCluster() == NULL));
  1658. }
  1659. //But always process the passenger data (even for ourself)
  1660. {
  1661. const PassengerData* ppassengersLC = (const PassengerData*)(FM_VAR_REF(pfmLC, rgPassengers));
  1662. const PassengerData* pstop = (const PassengerData*)(((char*)ppassengersLC) + pfmLC->cbrgPassengers);
  1663. for (const PassengerData* ppd = ppassengersLC; (ppd < pstop); ppd++)
  1664. {
  1665. IshipIGC* pshipChild = m_pCoreIGC->GetShip(ppd->shipID);
  1666. assert (pshipChild);
  1667. if (pshipChild == m_ship)
  1668. {
  1669. SetAutoPilot(false);
  1670. if ((ppd->turretID != NA) && (m_ship->GetCluster() != NULL) && (pshipChild->GetTurretID() != ppd->turretID))
  1671. {
  1672. Orientation oTurret = pship->GetHullType()->GetWeaponOrientation(ppd->turretID) *
  1673. pship->GetOrientation();
  1674. pshipChild->SetOrientation(oTurret);
  1675. }
  1676. }
  1677. else
  1678. {
  1679. assert (pshipChild->GetAutopilot() == false);
  1680. }
  1681. pshipChild->SetParentShip(pship);
  1682. assert (pshipChild->GetBaseHullType() == NULL);
  1683. assert (pshipChild->GetParts()->n() == 0);
  1684. if (pship == m_ship && m_ship->GetCluster() == NULL)
  1685. {
  1686. if (ppd->turretID != NA)
  1687. PostText(true, "%s has boarded your ship as a turret.", pshipChild->GetName());
  1688. else
  1689. PostText(true, "%s has boarded your ship as an observer.", pshipChild->GetName());
  1690. PlaySoundEffect(boardSound);
  1691. }
  1692. pshipChild->SetTurretID(ppd->turretID);
  1693. m_pClientEventSource->OnBoardShip(pshipChild, pship);
  1694. if (pshipChild == m_ship && IsLockedDown())
  1695. EndLockDown(lockdownTeleporting);
  1696. }
  1697. }
  1698. //We were just out of a ship while at a station
  1699. assert ((m_ship->GetParentShip() != NULL) || (m_ship->GetBaseHullType() != NULL));
  1700. }
  1701. break;
  1702. /* NYI
  1703. case FM_S_ACTIVE_TURRET_UPDATE:
  1704. {
  1705. if (!IsInGame())
  1706. break;
  1707. CASTPFM(pfmATU, S, ACTIVE_TURRET_UPDATE, pfm);
  1708. if (m_ship->GetObjectID() != pfmATU->shipID)
  1709. {
  1710. Time time = ClientTimeFromServerTime(pfmATU->timeUpdate);
  1711. IshipIGC* ship = m_pCoreIGC->GetShip(pfmATU->shipID);
  1712. assert (ship);
  1713. if (ship->GetTurretID() != NA)
  1714. {
  1715. assert (ship->GetParentShip());
  1716. ship->ProcessShipUpdate(time, pfmATU->atu);
  1717. }
  1718. }
  1719. }
  1720. break;
  1721. case FM_S_INACTIVE_TURRET_UPDATE:
  1722. {
  1723. if (!IsInGame())
  1724. break;
  1725. CASTPFM(pfmITU, S, INACTIVE_TURRET_UPDATE, pfm);
  1726. if (m_ship->GetObjectID() != pfmITU->shipID)
  1727. {
  1728. IshipIGC* ship = m_pCoreIGC->GetShip(pfmITU->shipID);
  1729. assert (ship);
  1730. assert (ship->GetParentShip());
  1731. ship->SetStateM(lastUpdate, 0);
  1732. }
  1733. }
  1734. break;
  1735. */
  1736. case FM_CS_SET_WINGID:
  1737. {
  1738. CASTPFM(pfmSW, CS, SET_WINGID, pfm);
  1739. IshipIGC* pship = m_ship->GetSide()->GetShip(pfmSW->shipID);
  1740. assert (pship);
  1741. if ((pship != m_ship) || pfmSW->bCommanded)
  1742. {
  1743. pship->SetWingID(pfmSW->wingID);
  1744. if (pfmSW->bCommanded && (pship == m_ship))
  1745. PostText(true, "You have been assigned to wing %s", c_pszWingName[pfmSW->wingID]);
  1746. }
  1747. m_pMissionInfo->GetSideInfo(pship->GetSide()->GetObjectID())->GetMembers().GetSink()();
  1748. }
  1749. break;
  1750. case FM_S_JOINED_MISSION:
  1751. {
  1752. CASTPFM(pfmJoinedMission, S, JOINED_MISSION, pfm);
  1753. assert(m_pMissionInfo != NULL);
  1754. // make sure this was what we intended to join if we were joining from the lobby
  1755. assert(m_dwCookieToJoin == pfmJoinedMission->dwCookie || !m_fmLobby.IsConnected());
  1756. debugf("I am ship %d\n", pfmJoinedMission->shipID);
  1757. // Make sure we have the right static core
  1758. if (lstrcmp(m_pMissionInfo->GetIGCStaticFile(), m_szIGCStaticFile) != 0
  1759. && !ResetStaticData(m_pMissionInfo->GetIGCStaticFile(), &m_pCoreIGC, now, GetIsZoneClub()))
  1760. {
  1761. // we have the wrong file - let's bail.
  1762. Disconnect();
  1763. // not logged on yet, so we have to do this part manually
  1764. delete m_pMissionInfo;
  1765. m_pMissionInfo = NULL;
  1766. OnLogonAck(false, false, "The client and server data files are out of sync. Please restart and go to a games list to auto-update "
  1767. "the latest files. If this doesn't work, try deleting the file 'filelist.txt' from the install directory and restarting the application.");
  1768. }
  1769. else
  1770. {
  1771. // update myself
  1772. PlayerInfo* pPlayerInfo = m_pPlayerInfo;
  1773. if (!pPlayerInfo)
  1774. {
  1775. PlayerLink* l = new PlayerLink;
  1776. m_listPlayers.last(l);
  1777. pPlayerInfo = &(l->data());
  1778. }
  1779. pPlayerInfo->SetMission(m_pCoreIGC);
  1780. FMD_S_PLAYERINFO fmPlayerInfo;
  1781. memset(&fmPlayerInfo, 0, sizeof(fmPlayerInfo));
  1782. strcpy(fmPlayerInfo.CharacterName, "<error>");
  1783. fmPlayerInfo.shipID = pfmJoinedMission->shipID;
  1784. fmPlayerInfo.iSide = NA;
  1785. pPlayerInfo->Set(&fmPlayerInfo);
  1786. SetPlayerInfo(pPlayerInfo);
  1787. assert (m_pPlayerInfo == pPlayerInfo);
  1788. pPlayerInfo->SetShip(m_ship);
  1789. ZAssert(m_pPlayerInfo);
  1790. if (m_pPlayerInfo->ShipID() != pfmJoinedMission->shipID)
  1791. SetPlayerInfo(FindPlayer(pfmJoinedMission->shipID));
  1792. // we will be sent the list of players right after this message
  1793. m_pMissionInfo->PurgePlayers();
  1794. }
  1795. break;
  1796. }
  1797. case FM_S_PLAYERINFO:
  1798. {
  1799. if (NULL == m_pCoreIGC->GetSide(SIDE_TEAMLOBBY))
  1800. {
  1801. assert(false);
  1802. OnSessionLost("Unexpected message received.", &m_fm);
  1803. }
  1804. CASTPFM(pfmPlayerInfo, S, PLAYERINFO, pfm);
  1805. PlayerLink* l = FindPlayerLink(pfmPlayerInfo->shipID);
  1806. debugf("Got Player Info for %s\n", pfmPlayerInfo->CharacterName);
  1807. // start with the player not on a side
  1808. SideID sideID = pfmPlayerInfo->iSide;
  1809. bool bReady = pfmPlayerInfo->fReady;
  1810. pfmPlayerInfo->iSide = NA;
  1811. if (l != NULL)
  1812. l->data().Set(pfmPlayerInfo);
  1813. else
  1814. {
  1815. l = new PlayerLink;
  1816. l->data().Set(pfmPlayerInfo);
  1817. m_listPlayers.last(l);
  1818. }
  1819. ZAssert(l);
  1820. PlayerInfo* pPlayerInfo = &(l->data());
  1821. pPlayerInfo->SetMission(m_pCoreIGC);
  1822. {
  1823. DataShipIGC ds;
  1824. ds.hullID = NA;
  1825. ds.shipID = pfmPlayerInfo->shipID;
  1826. ds.nParts = 0;
  1827. ds.sideID = NA;
  1828. //ds.wingID = pfmPlayerInfo->wingID;
  1829. ds.nDeaths = pfmPlayerInfo->nDeaths;
  1830. ds.nEjections = pfmPlayerInfo->nEjections;
  1831. ds.nKills = pfmPlayerInfo->nKills;
  1832. ds.pilotType = pfmPlayerInfo->pilotType;
  1833. ds.abmOrders = pfmPlayerInfo->abmOrders;
  1834. ds.baseObjectID = pfmPlayerInfo->baseObjectID;
  1835. strcpy(ds.name, pfmPlayerInfo->CharacterName);
  1836. IshipIGC* pship = (IshipIGC*)(m_pCoreIGC->CreateObject(m_lastSend, OT_ship, &ds, sizeof(ds)));
  1837. if (pship != NULL)
  1838. {
  1839. //Slightly bad form: releasing pship here and releasing it below but ships
  1840. //are never deleted on their first release (since they added to the side and
  1841. //mission lists)
  1842. pship->Release();
  1843. }
  1844. else
  1845. {
  1846. //The ship must already exist ... get it for the player info
  1847. //NYI does this happen for anything other than the player's ship?
  1848. pship = m_pCoreIGC->GetShip(pfmPlayerInfo->shipID);
  1849. assert (pship);
  1850. }
  1851. pPlayerInfo->SetShip(pship);
  1852. pship->SetExperience(pfmPlayerInfo->fExperience);
  1853. }
  1854. // add this player to their team
  1855. AddPlayerToMission(pPlayerInfo);
  1856. if (sideID != SIDE_TEAMLOBBY)
  1857. {
  1858. AddPlayerToSide(pPlayerInfo, sideID);
  1859. pPlayerInfo->SetReady(bReady);
  1860. }
  1861. debugf("PlayerInfo for %s, ship=%d, side=%d\n", pPlayerInfo->CharacterName(),
  1862. pPlayerInfo->ShipID(), pPlayerInfo->SideID());
  1863. break;
  1864. }
  1865. case FM_CS_PLAYER_READY:
  1866. {
  1867. ZAssert(m_pMissionInfo);
  1868. CASTPFM(pfmPlayerReady, CS, PLAYER_READY, pfm);
  1869. PlayerInfo* pPlayerInfo = FindPlayer(pfmPlayerReady->shipID);
  1870. if (pPlayerInfo)
  1871. {
  1872. pPlayerInfo->Update(pfmPlayerReady);
  1873. if (pPlayerInfo->IsHuman())
  1874. m_pClientEventSource->OnPlayerStatusChange(m_pMissionInfo, pPlayerInfo->SideID(), pPlayerInfo);
  1875. // mark the player list as having changed
  1876. m_pMissionInfo->GetSideInfo(pPlayerInfo->SideID())->GetMembers().GetSink()();
  1877. }
  1878. break;
  1879. }
  1880. case FM_S_MISSION_STAGE:
  1881. {
  1882. CASTPFM(pfmMissionStage, S, MISSION_STAGE, pfm);
  1883. m_pMissionInfo->SetStage(pfmMissionStage->stage);
  1884. switch (pfmMissionStage->stage)
  1885. {
  1886. case STAGE_NOTSTARTED:
  1887. {
  1888. m_pMissionInfo->SetInProgress(false);
  1889. m_pMissionInfo->SetCountdownStarted(false);
  1890. m_bWaitingForGameRestart = false;
  1891. m_pClientEventSource->OnMissionEnded(m_pMissionInfo);
  1892. break;
  1893. }
  1894. case STAGE_STARTING:
  1895. {
  1896. m_pMissionInfo->SetCountdownStarted(true);
  1897. m_pClientEventSource->OnMissionCountdown(m_pMissionInfo);
  1898. break;
  1899. }
  1900. case STAGE_STARTED:
  1901. {
  1902. m_pMissionInfo->SetCountdownStarted(false);
  1903. m_pMissionInfo->SetInProgress(true);
  1904. assert (memcmp(m_pCoreIGC->GetMissionParams(),
  1905. &m_pMissionInfo->GetMissionParams(),
  1906. sizeof(m_pMissionInfo->GetMissionParams())) == 0);
  1907. m_pCoreIGC->SetMissionStage(STAGE_STARTED);
  1908. m_pClientEventSource->OnMissionStarted(m_pMissionInfo);
  1909. break;
  1910. }
  1911. case STAGE_OVER:
  1912. {
  1913. m_pMissionInfo->SetInProgress(false);
  1914. m_bWaitingForGameRestart = false;
  1915. m_pClientEventSource->OnMissionEnded(m_pMissionInfo);
  1916. break;
  1917. }
  1918. case STAGE_TERMINATE:
  1919. {
  1920. RemovePlayerFromSide(m_pPlayerInfo, QSR_Quit);
  1921. RemovePlayerFromMission(m_pPlayerInfo, QSR_Quit);
  1922. break;
  1923. }
  1924. default:
  1925. {
  1926. assert(false);
  1927. }
  1928. }
  1929. m_mapMissions.GetSink()();
  1930. break;
  1931. }
  1932. case FM_S_CREATE_BUCKETS:
  1933. {
  1934. m_pCoreIGC->SetMissionParams(&m_pMissionInfo->GetMissionParams());
  1935. IsideIGC* pside = GetSide();
  1936. assert (pside);
  1937. assert (pside->GetObjectID() != SIDE_TEAMLOBBY);
  1938. {
  1939. //Hack copy side attributes over to lobby side
  1940. m_pCoreIGC->GetSide(SIDE_TEAMLOBBY)->SetGlobalAttributeSet(pside->GetGlobalAttributeSet());
  1941. CASTPFM(pfmCB, S, CREATE_BUCKETS, pfm);
  1942. pside->SetDevelopmentTechs(pfmCB->ttbmDevelopments);
  1943. pside->SetInitialTechs(pfmCB->ttbmInitial);
  1944. pside->CreateBuckets();
  1945. }
  1946. }
  1947. break;
  1948. case FM_S_GAME_STATE:
  1949. {
  1950. CASTPFM(pfmGS, S, GAME_STATE, pfm);
  1951. SideID sid = 0;
  1952. for (SideLinkIGC* l = m_pCoreIGC->GetSides()->first(); (l != NULL); l = l->next())
  1953. {
  1954. assert (sid == l->data()->GetObjectID());
  1955. l->data()->SetConquestPercent(pfmGS->conquest[sid]);
  1956. l->data()->SetTerritoryCount(pfmGS->territory[sid]);
  1957. l->data()->SetFlags(pfmGS->nFlags[sid]);
  1958. l->data()->SetArtifacts(pfmGS->nArtifacts[sid++]);
  1959. }
  1960. }
  1961. break;
  1962. case FM_S_MISSIONDEF:
  1963. {
  1964. CASTPFM(pfmMissionDef, S, MISSIONDEF, pfm);
  1965. pfmMissionDef->misparms.timeStart = ClientTimeFromServerTime(pfmMissionDef->misparms.timeStart);
  1966. if (m_pMissionInfo == NULL)
  1967. {
  1968. m_pMissionInfo = new MissionInfo(pfmMissionDef->dwCookie);
  1969. m_pMissionInfo->Update(pfmMissionDef);
  1970. }
  1971. else
  1972. {
  1973. m_pMissionInfo->Update(pfmMissionDef);
  1974. m_pCoreIGC->SetMissionParams(&pfmMissionDef->misparms);
  1975. m_pCoreIGC->UpdateSides(Time::Now(), &(m_pMissionInfo->GetMissionParams()), pfmMissionDef->rgszName);
  1976. }
  1977. m_pClientEventSource->OnAddMission(m_pMissionInfo);
  1978. break;
  1979. }
  1980. case FM_S_SIDE_TECH_CHANGE:
  1981. {
  1982. CASTPFM(pfmSTC, S, SIDE_TECH_CHANGE, pfm);
  1983. IsideIGC* pSide = m_pCoreIGC->GetSide(pfmSTC->sideID);
  1984. assert (pSide);
  1985. pSide->SetDevelopmentTechs(pfmSTC->ttbmDevelopments);
  1986. }
  1987. break;
  1988. case FM_S_SIDE_ATTRIBUTE_CHANGE:
  1989. {
  1990. CASTPFM(pfmSAC, S, SIDE_ATTRIBUTE_CHANGE, pfm);
  1991. IsideIGC* pSide = m_pCoreIGC->GetSide(pfmSAC->sideID);
  1992. assert (pSide);
  1993. pSide->SetGlobalAttributeSet(pfmSAC->gasAttributes);
  1994. // HACK: copy the global attributes to the lobby side for loadout
  1995. if (pSide == GetSide())
  1996. GetCore()->GetSide(SIDE_TEAMLOBBY)->SetGlobalAttributeSet(pfmSAC->gasAttributes);
  1997. }
  1998. break;
  1999. case FM_S_BUCKET_STATUS:
  2000. {
  2001. CASTPFM(pfmBucketStatus, S, BUCKET_STATUS, pfm);
  2002. IsideIGC* pside = m_pCoreIGC->GetSide(pfmBucketStatus->sideID);
  2003. assert (pside);
  2004. IbucketIGC* b = pside->GetBucket(pfmBucketStatus->iBucket);
  2005. if (b)
  2006. b->SetTimeAndMoney(pfmBucketStatus->timeTotal,
  2007. pfmBucketStatus->moneyTotal);
  2008. break;
  2009. }
  2010. case FM_S_POSITIONREQ: // sent to team leader to request a pos
  2011. {
  2012. CASTPFM(pfmPosReq, S, POSITIONREQ, pfm);
  2013. PlayerInfo* ppi = FindPlayer(pfmPosReq->shipID);
  2014. m_pMissionInfo->AddRequest(pfmPosReq->iSide, pfmPosReq->shipID);
  2015. m_pClientEventSource->OnAddRequest(m_pMissionInfo, pfmPosReq->iSide, ppi);
  2016. if (MyPlayerInfo()->IsTeamLeader() && (pfmPosReq->iSide == GetSideID()))
  2017. PostText(true, "%s wishes to join your team", ppi->CharacterName());
  2018. PlaySoundEffect(salPlayerWaitingSound);
  2019. break;
  2020. }
  2021. case FM_CS_DELPOSITIONREQ:
  2022. {
  2023. CASTPFM(pfmDelPositionReq, CS, DELPOSITIONREQ, pfm);
  2024. PlayerInfo* pPlayerInfo = FindPlayer(pfmDelPositionReq->shipID);
  2025. if (pPlayerInfo && pfmDelPositionReq->iSide < m_pMissionInfo->GetMissionParams().nTeams
  2026. && m_pMissionInfo->FindRequest(pfmDelPositionReq->iSide, pfmDelPositionReq->shipID))
  2027. {
  2028. m_pMissionInfo->RemoveRequest(pfmDelPositionReq->iSide, pfmDelPositionReq->shipID);
  2029. }
  2030. m_pClientEventSource->OnDelRequest(m_pMissionInfo, pfmDelPositionReq->iSide, pPlayerInfo, pfmDelPositionReq->reason);
  2031. break;
  2032. }
  2033. case FM_CS_QUIT_MISSION:
  2034. {
  2035. if (m_pMissionInfo)
  2036. {
  2037. // someone is quiting...
  2038. CASTPFM(pfmQuitMission, CS, QUIT_MISSION, pfm);
  2039. PlayerInfo* pPlayerInfo = FindPlayer(pfmQuitMission->shipID);
  2040. if (!pPlayerInfo || pPlayerInfo->SideID() == NA)
  2041. {
  2042. assert(false);
  2043. break;
  2044. }
  2045. if (pPlayerInfo->SideID() != SIDE_TEAMLOBBY)
  2046. {
  2047. assert(false);
  2048. RemovePlayerFromSide(pPlayerInfo, QSR_Quit);
  2049. }
  2050. SideID sideIDOld = pPlayerInfo->SideID();
  2051. RemovePlayerFromMission(pPlayerInfo, pfmQuitMission->reason, FM_VAR_REF(pfmQuitMission, szMessageParam));
  2052. // review: use a new message
  2053. m_pClientEventSource->OnDelRequest(m_pMissionInfo, sideIDOld, pPlayerInfo, DPR_Canceled);
  2054. }
  2055. break;
  2056. }
  2057. case FM_CS_CHANGE_TEAM_CIV:
  2058. {
  2059. CASTPFM(pfmChangeCiv, CS, CHANGE_TEAM_CIV, pfm);
  2060. //m_pMissionInfo->SetSideCivID(pfmChangeCiv->iSide, pfmChangeCiv->civID);
  2061. m_pCoreIGC->GetSide(pfmChangeCiv->iSide)->SetCivilization(m_pCoreIGC->GetCivilization(pfmChangeCiv->civID));
  2062. m_pClientEventSource->OnTeamCivChange(m_pMissionInfo, pfmChangeCiv->iSide, pfmChangeCiv->civID);
  2063. break;
  2064. }
  2065. case FM_CS_SET_TEAM_INFO:
  2066. {
  2067. CASTPFM(pfmSetTeamInfo, CS, SET_TEAM_INFO, pfm);
  2068. IsideIGC* pside = m_pCoreIGC->GetSide(pfmSetTeamInfo->sideID);
  2069. assert(pside);
  2070. pside->SetSquadID(pfmSetTeamInfo->squadID);
  2071. pside->SetName(pfmSetTeamInfo->SideName);
  2072. m_pMissionInfo->SetSideName(pfmSetTeamInfo->sideID, pfmSetTeamInfo->SideName);
  2073. m_pClientEventSource->OnTeamNameChange(m_pMissionInfo, pfmSetTeamInfo->sideID);
  2074. break;
  2075. }
  2076. case FM_S_TEAM_READY:
  2077. {
  2078. CASTPFM(pfmTeamReady, S, TEAM_READY, pfm);
  2079. m_pMissionInfo->SetSideReady(pfmTeamReady->iSide, pfmTeamReady->fReady);
  2080. m_pClientEventSource->OnTeamReadyChange(m_pMissionInfo, pfmTeamReady->iSide, pfmTeamReady->fReady);
  2081. break;
  2082. }
  2083. case FM_S_SIDE_INACTIVE:
  2084. {
  2085. CASTPFM(pfmSideInactive, S, SIDE_INACTIVE, pfm);
  2086. debugf("Side inactive, side=%d\n", pfmSideInactive->sideID);
  2087. m_pMissionInfo->SetSideActive(pfmSideInactive->sideID, false);
  2088. m_pClientEventSource->OnTeamInactive(m_pMissionInfo, pfmSideInactive->sideID);
  2089. m_mapMissions.GetSink()();
  2090. break;
  2091. }
  2092. case FM_CS_FORCE_TEAM_READY:
  2093. {
  2094. CASTPFM(pfmTeamForceReady, CS, FORCE_TEAM_READY, pfm);
  2095. m_pMissionInfo->SetSideForceReady(pfmTeamForceReady->iSide, pfmTeamForceReady->fForceReady);
  2096. m_pClientEventSource->OnTeamForceReadyChange(m_pMissionInfo, pfmTeamForceReady->iSide, pfmTeamForceReady->fForceReady);
  2097. }
  2098. break;
  2099. case FM_CS_AUTO_ACCEPT:
  2100. {
  2101. CASTPFM(pfmAutoAccept, CS, AUTO_ACCEPT, pfm);
  2102. m_pMissionInfo->SetSideAutoAccept(pfmAutoAccept->iSide, pfmAutoAccept->fAutoAccept);
  2103. m_pClientEventSource->OnTeamAutoAcceptChange(m_pMissionInfo, pfmAutoAccept->iSide, pfmAutoAccept->fAutoAccept);
  2104. m_mapMissions.GetSink()();
  2105. }
  2106. break;
  2107. case FM_CS_LOCK_LOBBY:
  2108. {
  2109. CASTPFM(pfmLockLobby, CS, LOCK_LOBBY, pfm);
  2110. m_pMissionInfo->SetLockLobby(pfmLockLobby->fLock);
  2111. m_pClientEventSource->OnLockLobby(pfmLockLobby->fLock);
  2112. m_mapMissions.GetSink()();
  2113. }
  2114. break;
  2115. case FM_CS_LOCK_SIDES:
  2116. {
  2117. CASTPFM(pfmLockSides, CS, LOCK_SIDES, pfm);
  2118. m_pMissionInfo->SetLockSides(pfmLockSides->fLock);
  2119. m_pClientEventSource->OnLockSides(pfmLockSides->fLock);
  2120. m_mapMissions.GetSink()();
  2121. }
  2122. break;
  2123. case FM_CS_REQUEST_MONEY:
  2124. {
  2125. CASTPFM(pfmRequest, CS, REQUEST_MONEY, pfm);
  2126. IshipIGC* pship = m_ship->GetSide()->GetShip(pfmRequest->shipidRequest);
  2127. if (pship)
  2128. {
  2129. GetMoneyRequest(pship, pfmRequest->amount, pfmRequest->hidFor);
  2130. }
  2131. }
  2132. break;
  2133. case FM_CS_CHATMESSAGE:
  2134. {
  2135. CASTPFM(pfmChat, CS, CHATMESSAGE, pfm);
  2136. //Ignore chats we sent ... we will have already handled them when sent
  2137. if (pfmChat->cd.sidSender != m_ship->GetObjectID())
  2138. {
  2139. ReceiveChat(m_pCoreIGC->GetShip(pfmChat->cd.sidSender),
  2140. pfmChat->cd.chatTarget,
  2141. pfmChat->cd.oidRecipient,
  2142. pfmChat->cd.voiceOver,
  2143. FM_VAR_REF(pfmChat, Message),
  2144. pfmChat->cd.commandID,
  2145. pfmChat->otTarget,
  2146. pfmChat->oidTarget,
  2147. NULL,
  2148. pfmChat->cd.bObjectModel);
  2149. }
  2150. break;
  2151. }
  2152. case FM_CS_CHATBUOY:
  2153. {
  2154. if (!IsInGame())
  2155. break;
  2156. CASTPFM(pfmBuoy, CS, CHATBUOY, pfm);
  2157. if (pfmBuoy->cd.sidSender != m_ship->GetObjectID())
  2158. {
  2159. //Create a buoy for this chat message
  2160. IbaseIGC* b = m_pCoreIGC->CreateObject(lastUpdate, OT_buoy, &(pfmBuoy->db), sizeof(pfmBuoy->db));
  2161. assert (b);
  2162. ((IbuoyIGC*)b)->AddConsumer();
  2163. ReceiveChat(m_pCoreIGC->GetShip(pfmBuoy->cd.sidSender),
  2164. pfmBuoy->cd.chatTarget,
  2165. pfmBuoy->cd.oidRecipient,
  2166. pfmBuoy->cd.voiceOver,
  2167. FM_VAR_REF(pfmBuoy, Message),
  2168. pfmBuoy->cd.commandID,
  2169. OT_buoy,
  2170. b->GetObjectID(),
  2171. (ImodelIGC*)b,
  2172. pfmBuoy->cd.bObjectModel);
  2173. ((IbuoyIGC*)b)->ReleaseConsumer();
  2174. b->Release();
  2175. }
  2176. }
  2177. break;
  2178. case FM_CS_MISSIONPARAMS:
  2179. {
  2180. assert(false);
  2181. CASTPFM(pfmMissionParams, CS, MISSIONPARAMS, pfm);
  2182. pfmMissionParams->missionparams.timeStart = ClientTimeFromServerTime(pfmMissionParams->missionparams.timeStart);
  2183. m_pCoreIGC->SetMissionParams(&(pfmMissionParams->missionparams));
  2184. }
  2185. break;
  2186. case FM_S_SET_START_TIME:
  2187. {
  2188. CASTPFM(pfmStartTime, S, SET_START_TIME, pfm);
  2189. ZAssert(MyMission());
  2190. Time timeStart = ClientTimeFromServerTime(pfmStartTime->timeStart);
  2191. MyMission()->UpdateStartTime(timeStart);
  2192. m_pCoreIGC->SetStartTime(timeStart);
  2193. }
  2194. break;
  2195. case FM_S_SHIP_STATUS:
  2196. {
  2197. if (IsWaitingForGameRestart())
  2198. break;
  2199. CASTPFM(pfmShipStatus, S, SHIP_STATUS, pfm);
  2200. PlayerInfo* pPlayerInfo = FindPlayer(pfmShipStatus->shipID);
  2201. if (pPlayerInfo && m_pMissionInfo)
  2202. {
  2203. if ((pfmShipStatus->status.GetSectorID() != NA) &&
  2204. (pfmShipStatus->status.GetSectorID() != pPlayerInfo->LastSeenSector()))
  2205. {
  2206. ShipWarped(pPlayerInfo->GetShip(),
  2207. pPlayerInfo->LastSeenSector(),
  2208. pfmShipStatus->status.GetSectorID());
  2209. }
  2210. // update the last seen info
  2211. pPlayerInfo->SetShipStatus(pfmShipStatus->status);
  2212. // update any lists using this info
  2213. m_pMissionInfo->GetSideInfo(pPlayerInfo->SideID())->GetMembers().GetSink()();
  2214. m_pClientEventSource->OnShipStatusChange(pPlayerInfo);
  2215. if ((pfmShipStatus->status.GetState() == c_ssTurret) !=
  2216. (pPlayerInfo->LastSeenState() == c_ssTurret))
  2217. {
  2218. m_pClientEventSource->OnTurretStateChanging(pfmShipStatus->status.GetState() == c_ssTurret);
  2219. }
  2220. }
  2221. break;
  2222. }
  2223. case FM_S_KILL_SHIP:
  2224. {
  2225. CASTPFM(pfmKillShip, S, KILL_SHIP, pfm);
  2226. PlayerInfo* pPlayerInfo = FindPlayer(pfmKillShip->shipID);
  2227. ZAssert(pPlayerInfo);
  2228. ImodelIGC* pmodelKiller = m_pCoreIGC->GetModel(pfmKillShip->typeCredit, pfmKillShip->idCredit);
  2229. PlayerInfo* pLauncherInfo = ((pfmKillShip->typeCredit == OT_ship) && pmodelKiller)
  2230. ? (PlayerInfo*)(((IshipIGC*)pmodelKiller)->GetPrivateData())
  2231. : NULL;
  2232. IsideIGC* pside = GetSide();
  2233. SideID sideID = pside->GetObjectID();
  2234. ShipID shipID = GetShipID();
  2235. IshipIGC* pship = pPlayerInfo->GetShip();
  2236. assert (pship);
  2237. TargetKilled(pship);
  2238. if (pfmKillShip->bDeathCredit)
  2239. {
  2240. pPlayerInfo->AddDeath();
  2241. pPlayerInfo->GetShip()->SetExperience(0.0f);
  2242. }
  2243. else
  2244. pPlayerInfo->AddEjection();
  2245. MyMission()->GetSideInfo(pPlayerInfo->SideID())->GetMembers().GetSink()();
  2246. const char* pszVictim = pPlayerInfo->CharacterName();
  2247. bool bConstructorOrMiner;
  2248. {
  2249. PilotType pt = pship->GetPilotType();
  2250. bConstructorOrMiner = (pt == c_ptBuilder) ||
  2251. (pt == c_ptMiner) ||
  2252. (pt == c_ptCarrier);
  2253. }
  2254. if (!pLauncherInfo)
  2255. {
  2256. if (pfmKillShip->bKillCredit)
  2257. {
  2258. assert (pfmKillShip->typeCredit != NA);
  2259. IsideIGC* pside = pmodelKiller ? pmodelKiller->GetSide() : m_pCoreIGC->GetSide(pfmKillShip->sideidKiller);
  2260. assert (pside);
  2261. pside->AddKill();
  2262. }
  2263. if (pfmKillShip->shipID == shipID)
  2264. PostText(true, pfmKillShip->bDeathCredit ? "You were killed" : "You ejected");
  2265. else if (bConstructorOrMiner)
  2266. PostText(pPlayerInfo->SideID() == GetSideID(), START_COLOR_STRING "%s" END_COLOR_STRING " was killed", (PCC) ConvertColorToString (m_pCoreIGC->GetSide(pPlayerInfo->SideID())->GetColor ()), pszVictim);
  2267. else
  2268. PostText(false, pfmKillShip->bDeathCredit ? START_COLOR_STRING "%s" END_COLOR_STRING " was killed" : START_COLOR_STRING "%s" END_COLOR_STRING " ejected", (PCC) ConvertColorToString (m_pCoreIGC->GetSide(pPlayerInfo->SideID())->GetColor ()), pszVictim);
  2269. }
  2270. else
  2271. {
  2272. const char* pszLauncher = pLauncherInfo->CharacterName();
  2273. if (pfmKillShip->bKillCredit)
  2274. {
  2275. pLauncherInfo->AddKill();
  2276. if (pLauncherInfo->GetShip()->GetSide() != pship->GetSide())
  2277. pLauncherInfo->GetShip()->AddExperience();
  2278. }
  2279. MyMission()->GetSideInfo(pLauncherInfo->SideID())->GetMembers().GetSink()();
  2280. // if we can see the victim or if the perpatrator or victim
  2281. // is on our side...
  2282. if (pfmKillShip->shipID == shipID)
  2283. PostText(true,
  2284. pfmKillShip->bDeathCredit
  2285. ? "You were killed by " START_COLOR_STRING "%s" END_COLOR_STRING
  2286. : "You were forced to eject by " START_COLOR_STRING "%s" END_COLOR_STRING,
  2287. (PCC) ConvertColorToString (pLauncherInfo->GetShip()->GetSide()->GetColor ()), pszLauncher);
  2288. else if (pmodelKiller == m_ship)
  2289. PostText(true,
  2290. pfmKillShip->bDeathCredit
  2291. ? "You killed " START_COLOR_STRING "%s" END_COLOR_STRING
  2292. : "You forced " START_COLOR_STRING "%s" END_COLOR_STRING " to eject",
  2293. (PCC) ConvertColorToString (pPlayerInfo->GetShip()->GetSide()->GetColor ()), pszVictim);
  2294. else
  2295. {
  2296. if (bConstructorOrMiner && pPlayerInfo->SideID() != GetSideID())
  2297. PostText(bConstructorOrMiner,
  2298. pfmKillShip->bDeathCredit
  2299. ? (START_COLOR_STRING "%s %s" END_COLOR_STRING " was killed by %s")
  2300. : (START_COLOR_STRING "%s %s" END_COLOR_STRING " was forced to eject by %s"),
  2301. (PCC) ConvertColorToString (pPlayerInfo->GetShip()->GetSide()->GetColor ()),
  2302. pPlayerInfo->GetShip()->GetSide()->GetName(),
  2303. pszVictim, pszLauncher);
  2304. else
  2305. PostText(bConstructorOrMiner,
  2306. pfmKillShip->bDeathCredit
  2307. ? "%s was killed by %s"
  2308. : "%s was forced to eject by %s", pszVictim, pszLauncher);
  2309. }
  2310. }
  2311. IclusterIGC * pCluster = pship->GetCluster();
  2312. if (NULL != pCluster)
  2313. {
  2314. IhullTypeIGC* pht = pship->GetBaseHullType();
  2315. if (pht)
  2316. pCluster->GetClusterSite()->AddExplosion(pship,
  2317. pht->HasCapability(c_habmFighter)
  2318. ? c_etSmallShip
  2319. : c_etBigShip);
  2320. }
  2321. // for drones, play the drone destruction sound
  2322. if (!pPlayerInfo->IsHuman())
  2323. {
  2324. if (sideID == pPlayerInfo->SideID())
  2325. {
  2326. SoundID destroyedSound;
  2327. switch (pship->GetPilotType())
  2328. {
  2329. case c_ptCarrier: //NYI need carrier destroyed sound
  2330. destroyedSound = carrierDestroyedSound;
  2331. break;
  2332. case c_ptMiner:
  2333. destroyedSound = minerDestroyedSound;
  2334. break;
  2335. case c_ptBuilder:
  2336. destroyedSound =
  2337. ((IstationTypeIGC*)(IbaseIGC*)(pship->GetBaseData()))
  2338. ->GetConstructorDestroyedSound();
  2339. break;
  2340. case c_ptLayer:
  2341. if (pship->GetBaseData()->GetObjectType() == OT_mineType)
  2342. destroyedSound = minefieldLayerDestroyedSound;
  2343. else
  2344. destroyedSound = towerLayerDestroyedSound;
  2345. break;
  2346. default:
  2347. assert(false);
  2348. case c_ptWingman:
  2349. destroyedSound = towerLayerDestroyedSound;
  2350. break;
  2351. }
  2352. PlaySoundEffect(destroyedSound);
  2353. }
  2354. }
  2355. else if ((pship == m_ship) && pfmKillShip->bDeathCredit)
  2356. {
  2357. //The player died ... set up the gloat camera
  2358. EjectPlayer(m_pCoreIGC->GetModel(pfmKillShip->typeCredit, pfmKillShip->idCredit));
  2359. }
  2360. ChangeGameState();
  2361. }
  2362. break;
  2363. case FM_S_JOIN_SIDE:
  2364. {
  2365. CASTPFM(pfmJoinSide, S, JOIN_SIDE, pfm);
  2366. PlayerInfo* pplayer = FindPlayer(pfmJoinSide->shipID);
  2367. AddPlayerToSide(pplayer, pfmJoinSide->sideID);
  2368. }
  2369. break;
  2370. /*
  2371. case FM_S_SET_EXPERIENCE:
  2372. {
  2373. CASTPFM(pfmSet, S, SET_EXPERIENCE, pfm);
  2374. IshipIGC* pship = m_pCoreIGC->GetShip(pfmSet->shipID);
  2375. assert (pship);
  2376. pship->SetExperience(pfmSet->fExperience);
  2377. }
  2378. break;
  2379. */
  2380. case FM_CS_SET_TEAM_LEADER:
  2381. {
  2382. CASTPFM(pfmSetTeamLeader, CS, SET_TEAM_LEADER, pfm);
  2383. PlayerInfo* pplayer = FindPlayer(pfmSetTeamLeader->shipID);
  2384. // demote the previous team leader (if any)
  2385. if (MyMission()->SideLeaderShipID(pplayer->SideID()) != NA)
  2386. {
  2387. PlayerInfo* pplayerOld = FindPlayer(MyMission()->SideLeaderShipID(pplayer->SideID()));
  2388. pplayerOld->SetTeamLeader(false);
  2389. pplayerOld->SetMissionOwner(false);
  2390. m_pClientEventSource->OnPlayerStatusChange(MyMission(), pplayerOld->SideID(), pplayerOld);
  2391. }
  2392. pplayer->SetTeamLeader(true);
  2393. pplayer->SetMissionOwner(MyMission()->MissionOwnerSideID() == pplayer->SideID());
  2394. MyMission()->SetSideLeader(pplayer);
  2395. if ((m_pCoreIGC->GetMissionStage() == STAGE_STARTED) &&
  2396. pplayer->SideID() == MyPlayerInfo()->SideID())
  2397. {
  2398. //Leader on our team has switched
  2399. if (pplayer == MyPlayerInfo())
  2400. PostText(true, "You have become the team leader");
  2401. else
  2402. {
  2403. char bfr[100];
  2404. sprintf(bfr, "%s has become the team leader", pplayer->CharacterName());
  2405. PostText(true, bfr);
  2406. }
  2407. }
  2408. if (pplayer == MyPlayerInfo())
  2409. PlaySoundEffect(commanderSound);
  2410. m_pClientEventSource->OnPlayerStatusChange(MyMission(), pplayer->SideID(), pplayer);
  2411. }
  2412. break;
  2413. case FM_CS_SET_MISSION_OWNER:
  2414. {
  2415. CASTPFM(pfmSetMissionOwner, CS, SET_MISSION_OWNER, pfm);
  2416. // demote the previous mission owner (if any)
  2417. if (MyMission()->MissionOwnerSideID() != NA)
  2418. {
  2419. PlayerInfo* pplayerOld = FindPlayer(MyMission()->MissionOwnerShipID());
  2420. pplayerOld->SetMissionOwner(false);
  2421. m_pClientEventSource->OnPlayerStatusChange(MyMission(), pplayerOld->SideID(), pplayerOld);
  2422. }
  2423. PlayerInfo* pplayer = FindPlayer(pfmSetMissionOwner->shipID);
  2424. ZAssert(pplayer->IsTeamLeader());
  2425. pplayer->SetMissionOwner(true);
  2426. MyMission()->SetSideLeader(pplayer);
  2427. m_pClientEventSource->OnPlayerStatusChange(MyMission(), pplayer->SideID(), pplayer);
  2428. }
  2429. break;
  2430. case FM_CS_QUIT_SIDE:
  2431. {
  2432. if (m_pMissionInfo)
  2433. {
  2434. CASTPFM(pfmQuitSide, CS, QUIT_SIDE, pfm);
  2435. PlayerInfo* pplayer = FindPlayer(pfmQuitSide->shipID);
  2436. RemovePlayerFromSide(pplayer, pfmQuitSide->reason, FM_VAR_REF(pfmQuitSide, szMessageParam));
  2437. }
  2438. }
  2439. break;
  2440. case FM_S_SQUAD_INFO:
  2441. case FM_S_SQUAD_DETAILS:
  2442. case FM_S_SQUAD_DETAILS_PLAYER:
  2443. case FM_S_SQUAD_LOG_INFO:
  2444. case FM_S_SQUAD_DELETED:
  2445. case FM_S_SQUAD_TEXT_MESSAGE:
  2446. case FM_S_SQUAD_CREATE_ACK:
  2447. {
  2448. ForwardSquadMessage(pfm); // forward message to squads screen
  2449. }
  2450. break;
  2451. case FM_S_CHARACTER_INFO_GENERAL:
  2452. case FM_S_CHARACTER_INFO_BY_CIV:
  2453. case FM_S_CHARACTER_INFO_MEDAL:
  2454. {
  2455. ForwardCharInfoMessage(pfm); // forward message to character info screen
  2456. }
  2457. break;
  2458. case FM_S_SQUAD_INFO_DUDEX:
  2459. {
  2460. // forward messages that go to either of these screens
  2461. ForwardSquadMessage(pfm);
  2462. ForwardCharInfoMessage(pfm);
  2463. }
  2464. break;
  2465. case FM_S_LEADER_BOARD_QUERY_FAIL:
  2466. case FM_S_LEADER_BOARD_LIST:
  2467. {
  2468. ForwardLeaderBoardMessage(pfm); // forward message to leader board screen
  2469. }
  2470. break;
  2471. case FM_S_ENTER_GAME:
  2472. {
  2473. ClearLoadout ();
  2474. OnEnterGame();
  2475. }
  2476. break;
  2477. case FM_S_GAME_OVER:
  2478. {
  2479. m_bInGame = false;
  2480. m_bWaitingForGameRestart = true;
  2481. m_strBriefingText.SetEmpty();
  2482. m_bGenerateCivBriefing = false;
  2483. CASTPFM(pfmGameOver, S, GAME_OVER, pfm);
  2484. if (IsLockedDown())
  2485. EndLockDown(lockdownDonating | lockdownLoadout | lockdownTeleporting);
  2486. SetViewCluster(NULL);
  2487. GetShip()->SetCluster(NULL);
  2488. // set all of the players to unready with 0 money
  2489. for (PlayerLink* ppl = GetPlayerList()->first();
  2490. (ppl != NULL);
  2491. ppl = ppl->next())
  2492. {
  2493. // review: since we are trying this silly away from keyboard thing,
  2494. // set everyone as ready (= not away from keyboard)
  2495. ppl->data().SetReady(true);
  2496. ppl->data().Reset(true);
  2497. }
  2498. // set all of the sides to active and not forced ready
  2499. for (SideID sid = 0; sid < MyMission()->NumSides(); sid++)
  2500. {
  2501. MyMission()->SetSideActive(sid, true);
  2502. GetCore()->GetSide(sid)->SetActiveF(true);
  2503. MyMission()->SetSideForceReady(sid, false);
  2504. MyMission()->SetSideReady(sid, true);
  2505. }
  2506. // nuke any pending votes
  2507. m_listBallots.SetEmpty();
  2508. for (SideLinkIGC* psl = GetCore()->GetSides()->first(); (psl != NULL); psl = psl->next())
  2509. psl->data()->Reset();
  2510. GetCore()->ResetMission();
  2511. }
  2512. break;
  2513. case FM_S_GAME_OVER_PLAYERS:
  2514. {
  2515. CASTPFM(pfmGameOverPlayers, S, GAME_OVER_PLAYERS, pfm);
  2516. // update the scores of all of the players
  2517. int cPlayerEndgameInfo = pfmGameOverPlayers->cbrgPlayerInfo / sizeof(PlayerEndgameInfo);
  2518. PlayerEndgameInfo* vPlayerEndgameInfo
  2519. = (PlayerEndgameInfo*)FM_VAR_REF(pfmGameOverPlayers, rgPlayerInfo);
  2520. for (int i = 0; i < cPlayerEndgameInfo; i++)
  2521. {
  2522. PlayerInfo* pplayer = FindPlayer(vPlayerEndgameInfo[i].characterName);
  2523. if (pplayer)
  2524. pplayer->UpdateScore(vPlayerEndgameInfo[i].stats);
  2525. }
  2526. }
  2527. break;
  2528. case FM_S_GAIN_FLAG:
  2529. {
  2530. if (IsWaitingForGameRestart())
  2531. break;
  2532. CASTPFM(pfmGain, S, GAIN_FLAG, pfm);
  2533. IshipIGC* pship = m_pCoreIGC->GetShip(pfmGain->shipidRecipient);
  2534. assert (pship);
  2535. assert ((pship->GetFlag() == NA) || (pfmGain->sideidFlag == NA));
  2536. pship->SetFlag(pfmGain->sideidFlag);
  2537. if (pfmGain->sideidFlag != NA)
  2538. {
  2539. if (pship->GetSide() == GetSide())
  2540. {
  2541. if (pfmGain->sideidFlag != SIDE_TEAMLOBBY)
  2542. {
  2543. PostText(true, "%s has stolen " START_COLOR_STRING "%s's" END_COLOR_STRING " flag.",
  2544. pship->GetName(),
  2545. (PCC) ConvertColorToString (GetCore()->GetSide(pfmGain->sideidFlag)->GetColor ()),
  2546. GetCore()->GetSide(pfmGain->sideidFlag)->GetName()
  2547. );
  2548. PlaySoundEffect(enemyFlagLostSound);
  2549. }
  2550. else
  2551. {
  2552. PostText(true, "%s has found an artifact.",
  2553. pship->GetName());
  2554. PlaySoundEffect(artifactFoundSound);
  2555. }
  2556. }
  2557. else if (pfmGain->sideidFlag > 0 && pfmGain->sideidFlag == GetSideID())
  2558. {
  2559. ZString color = ConvertColorToString (pship->GetSide()->GetColor ());
  2560. PostText(true, START_COLOR_STRING "%s" END_COLOR_STRING " of " START_COLOR_STRING "%s" END_COLOR_STRING " has stolen our flag.",
  2561. (PCC) color,
  2562. pship->GetName(),
  2563. (PCC) color,
  2564. pship->GetSide()->GetName()
  2565. );
  2566. PlaySoundEffect(flagLostSound);
  2567. }
  2568. }
  2569. }
  2570. break;
  2571. case FM_S_BUY_LOADOUT_ACK:
  2572. {
  2573. CASTPFM(pfmBuyLoadoutAck, S, BUY_LOADOUT_ACK, pfm);
  2574. if (!pfmBuyLoadoutAck->fLaunch && IsLockedDown())
  2575. EndLockDown(lockdownLoadout);
  2576. m_ship->ProcessShipLoadout(pfmBuyLoadoutAck->cbloadout, (const ShipLoadout*)(FM_VAR_REF(pfmBuyLoadoutAck, loadout)), true);
  2577. SaveLoadout();
  2578. m_pClientEventSource->OnPurchaseCompleted(pfmBuyLoadoutAck->fBoughtEverything);
  2579. // if they wanted to board a new ship after disembarking, we are
  2580. // finally at a safe place to do so.
  2581. if (m_sidBoardAfterDisembark != NA)
  2582. {
  2583. IshipIGC* pshipNewParent = FindPlayer(m_sidBoardAfterDisembark)->GetShip();
  2584. if (pshipNewParent)
  2585. {
  2586. BoardShip(pshipNewParent);
  2587. }
  2588. m_sidBoardAfterDisembark = NA;
  2589. }
  2590. // otherwise, if they wanted to teleport we can do that now too.
  2591. else if (m_sidTeleportAfterDisembark != NA)
  2592. {
  2593. IstationIGC* pstation = GetCore()->GetStation(m_sidTeleportAfterDisembark);
  2594. if (pstation != GetShip()->GetStation())
  2595. {
  2596. SetMessageType(BaseClient::c_mtGuaranteed);
  2597. BEGIN_PFM_CREATE(m_fm, pfmDocked, C, DOCKED)
  2598. END_PFM_CREATE
  2599. pfmDocked->stationID = m_sidTeleportAfterDisembark;
  2600. StartLockDown("Teleporting to " + ZString(pstation->GetName()) + "....", lockdownTeleporting);
  2601. }
  2602. m_sidTeleportAfterDisembark = NA;
  2603. }
  2604. }
  2605. break;
  2606. case FM_S_OBJECT_SPOTTED:
  2607. {
  2608. if (!IsInGame())
  2609. break;
  2610. CASTPFM(pfmObjectSpotted, S, OBJECT_SPOTTED, pfm);
  2611. ZString strSpotterName;
  2612. switch (pfmObjectSpotted->otSpotter)
  2613. {
  2614. case OT_station:
  2615. strSpotterName = ZString("Your ")
  2616. + GetCore()->GetStation(pfmObjectSpotted->oidSpotter)->GetName()
  2617. + " has";
  2618. break;
  2619. case OT_probe:
  2620. strSpotterName = "One of your team's probes has";
  2621. break;
  2622. case OT_ship:
  2623. if (pfmObjectSpotted->oidSpotter == GetShipID())
  2624. strSpotterName = "You've";
  2625. else
  2626. strSpotterName = GetCore()->GetShip(pfmObjectSpotted->oidSpotter)->GetName() + ZString(" has");
  2627. break;
  2628. default:
  2629. assert(false);
  2630. strSpotterName = "<bug> has";
  2631. break;
  2632. }
  2633. switch (pfmObjectSpotted->otObject)
  2634. {
  2635. case OT_station:
  2636. {
  2637. IstationIGC* pstation = GetCore()->GetStation(pfmObjectSpotted->oidObject);
  2638. assert(pstation);
  2639. PostText(GetShip()->GetWingID() == 0, strSpotterName + " discovered an enemy " + pstation->GetName() + " in sector " + pstation->GetCluster()->GetName());
  2640. }
  2641. break;
  2642. case OT_asteroid:
  2643. {
  2644. IasteroidIGC* pasteroid = GetCore()->GetAsteroid(pfmObjectSpotted->oidObject);
  2645. assert(pasteroid);
  2646. PostText(GetShip()->GetWingID() == 0, strSpotterName + " discovered a " + IasteroidIGC::GetTypeName(pasteroid->GetCapabilities()) + " asteroid in sector " + pasteroid->GetCluster()->GetName());
  2647. }
  2648. break;
  2649. case OT_warp:
  2650. {
  2651. IwarpIGC* paleph = GetCore()->GetWarp(pfmObjectSpotted->oidObject);
  2652. assert(paleph);
  2653. PostText(GetShip()->GetWingID() == 0, strSpotterName + " discovered an aleph from sector " + paleph->GetCluster()->GetName() + " to sector " + paleph->GetDestination()->GetCluster()->GetName());
  2654. }
  2655. break;
  2656. default:
  2657. assert(false);
  2658. break;
  2659. }
  2660. }
  2661. break;
  2662. case FM_S_DEV_COMPLETED:
  2663. {
  2664. CASTPFM(pfmDevelopmentCompleted, S, DEV_COMPLETED, pfm);
  2665. IdevelopmentIGC* pdevelopment = GetCore()->GetDevelopment(pfmDevelopmentCompleted->devId);
  2666. assert(pdevelopment);
  2667. PostText(true, "%s research completed", pdevelopment->GetName());
  2668. PlaySoundEffect(pdevelopment->GetCompletionSound());
  2669. }
  2670. break;
  2671. case FM_S_SET_BRIEFING_TEXT:
  2672. {
  2673. CASTPFM(pfmSetBriefingText, S, SET_BRIEFING_TEXT, pfm);
  2674. m_strBriefingText = FM_VAR_REF(pfmSetBriefingText, text);
  2675. m_bGenerateCivBriefing = pfmSetBriefingText->fGenerateCivBriefing;
  2676. }
  2677. break;
  2678. case FM_LS_SQUAD_MEMBERSHIPS:
  2679. {
  2680. CASTPFM(pfmSquadMemberships, LS, SQUAD_MEMBERSHIPS, pfm);
  2681. SquadMembership* vSquadMemberships
  2682. = (SquadMembership*)FM_VAR_REF(pfmSquadMemberships, squadMemberships);
  2683. m_squadmemberships.SetEmpty();
  2684. for (int iSquad = 0; iSquad < pfmSquadMemberships->cSquadMemberships; iSquad++)
  2685. {
  2686. m_squadmemberships.PushEnd(vSquadMemberships[iSquad]);
  2687. }
  2688. SaveSquadMemberships(m_szCharName);
  2689. m_mapMissions.GetSink()();
  2690. }
  2691. break;
  2692. case FM_S_STATIC_MAP_INFO:
  2693. {
  2694. CASTPFM(pfmStaticMapInfo, S, STATIC_MAP_INFO, pfm);
  2695. // copy the new static map info into our internal table
  2696. if (m_vStaticMapInfo)
  2697. delete [] m_vStaticMapInfo;
  2698. m_cStaticMapInfo = pfmStaticMapInfo->cbmaps / sizeof(StaticMapInfo);
  2699. if (m_cStaticMapInfo > 0)
  2700. {
  2701. m_vStaticMapInfo = new StaticMapInfo[m_cStaticMapInfo];
  2702. memcpy(m_vStaticMapInfo, FM_VAR_REF(pfmStaticMapInfo, maps), m_cStaticMapInfo * sizeof(StaticMapInfo));
  2703. }
  2704. else
  2705. m_vStaticMapInfo = NULL;
  2706. }
  2707. break;
  2708. case FM_S_RANK_INFO:
  2709. {
  2710. CASTPFM(pfmRankInfo, S, RANK_INFO, pfm);
  2711. // copy the new rank info into our internal table
  2712. if (m_vRankInfo)
  2713. delete [] m_vRankInfo;
  2714. m_cRankInfo = pfmRankInfo->cRanks;
  2715. m_vRankInfo = new RankInfo[m_cRankInfo];
  2716. memcpy(m_vRankInfo, FM_VAR_REF(pfmRankInfo, ranks), m_cRankInfo * sizeof(RankInfo));
  2717. }
  2718. break;
  2719. case FM_S_CLUB_RANK_INFO:
  2720. {
  2721. CASTPFM(pfmRankInfo, S, CLUB_RANK_INFO, pfm);
  2722. // copy the new rank info into our internal table
  2723. if (m_vRankInfo)
  2724. delete [] m_vRankInfo;
  2725. m_cRankInfo = pfmRankInfo->cRanks;
  2726. m_vRankInfo = new RankInfo[m_cRankInfo];
  2727. memcpy(m_vRankInfo, FM_VAR_REF(pfmRankInfo, ranks), m_cRankInfo * sizeof(RankInfo));
  2728. }
  2729. break;
  2730. // ************ Lobby Messages *************
  2731. case FM_L_AUTO_UPDATE_INFO:
  2732. {
  2733. if (m_pAutoDownload == NULL)
  2734. m_pAutoDownload = CreateAutoDownload();
  2735. IAutoUpdateSink * pAutoUpdateSink = OnBeginAutoUpdate(NULL, true);
  2736. assert(pAutoUpdateSink);
  2737. CASTPFM(pfmServerInfo, L, AUTO_UPDATE_INFO, pfm);
  2738. m_pAutoDownload->SetFTPSite(FM_VAR_REF(pfmServerInfo, FTPSite),
  2739. FM_VAR_REF(pfmServerInfo, FTPInitialDirectory),
  2740. FM_VAR_REF(pfmServerInfo, FTPAccount),
  2741. FM_VAR_REF(pfmServerInfo, FTPPassword));
  2742. m_pAutoDownload->SetOfficialFileListAttributes(pfmServerInfo->crcFileList,
  2743. pfmServerInfo->nFileListSize);
  2744. // Disconnect during download
  2745. DisconnectLobby();
  2746. m_pAutoDownload->SetArtPath(GetArtPath());
  2747. //
  2748. // Let's do it!
  2749. //
  2750. m_pAutoDownload->BeginUpdate(pAutoUpdateSink, ShouldCheckFiles(), false);
  2751. // m_pAutoDownload could be NULL at this point, if the autodownload system decided
  2752. // not to do a download after all. This can happen if there is an error or if
  2753. // the client was already up-to-date.
  2754. }
  2755. break;
  2756. case FM_L_CREATE_MISSION_ACK:
  2757. {
  2758. CASTPFM(pfmCreateMissionAck, L, CREATE_MISSION_ACK, pfm);
  2759. // we remember it, so that if we're told to join a mission, we can be sure it's for the one we want to join,
  2760. m_dwCookieToJoin = pfmCreateMissionAck->dwCookie;
  2761. m_strPasswordToJoin[0] = '\0';
  2762. }
  2763. break;
  2764. case FM_LS_LOBBYMISSIONINFO:
  2765. {
  2766. if (!LoggedOnToLobby())
  2767. break;
  2768. CASTPFM(pfmLobbyMissionInfo, LS, LOBBYMISSIONINFO, pfm);
  2769. // The time offset may be off by as much as the maximum server
  2770. // latency, but that's OK for just displaying a timer. Let's
  2771. // make sure we never assume a starting time in the future, however.
  2772. pfmLobbyMissionInfo->dwStartTime += m_lobbyServerOffset;
  2773. if (now - Time(pfmLobbyMissionInfo->dwStartTime) < 0 && !pfmLobbyMissionInfo->fCountdownStarted)
  2774. pfmLobbyMissionInfo->dwStartTime = now.clock();
  2775. MissionInfo * pMissionInfo = GetLobbyMission(pfmLobbyMissionInfo->dwCookie);
  2776. assert(pMissionInfo);
  2777. pMissionInfo->Update(pfmLobbyMissionInfo);
  2778. m_mapMissions.GetSink()();
  2779. m_pClientEventSource->OnAddMission(pMissionInfo);
  2780. break;
  2781. }
  2782. case FM_L_JOIN_MISSION:
  2783. {
  2784. CASTPFM(pfmJoinMission, L, JOIN_MISSION, pfm);
  2785. if (m_dwCookieToJoin = pfmJoinMission->dwCookie) // this is our number--join up
  2786. {
  2787. m_ci.strServer = pfmJoinMission->szServer;
  2788. m_ci.guidSession = GUID_NULL;
  2789. ConnectToServer(m_ci, pfmJoinMission->dwCookie, now, m_strPasswordToJoin, false);
  2790. }
  2791. else
  2792. {
  2793. OnLogonAck(false, false, "<bug>: Received a spurious join request.");
  2794. }
  2795. break;
  2796. }
  2797. case FM_L_CREATE_MISSION_NACK:
  2798. {
  2799. OnLogonAck(false, false, "There are no available servers on which to create a game.");
  2800. }
  2801. break;
  2802. case FM_L_JOIN_GAME_NACK:
  2803. {
  2804. OnLogonAck(false, false, "There is no more room on this server.");
  2805. }
  2806. break;
  2807. case FM_LS_MISSION_GONE:
  2808. {
  2809. CASTPFM(pfmMissionGone, LS, MISSION_GONE, pfm);
  2810. MissionInfo * pMission = GetLobbyMission(pfmMissionGone->dwCookie);
  2811. if (m_pMissionInfo != NULL && pMission->GetCookie() == MyMission()->GetCookie())
  2812. {
  2813. if (GetSideID() != NA)
  2814. {
  2815. if (GetSideID() != SIDE_TEAMLOBBY)
  2816. RemovePlayerFromSide(m_pPlayerInfo, QSR_Quit);
  2817. RemovePlayerFromMission(m_pPlayerInfo, QSR_Quit);
  2818. }
  2819. else
  2820. {
  2821. delete m_pMissionInfo;
  2822. m_pMissionInfo = NULL;
  2823. }
  2824. }
  2825. m_pClientEventSource->OnDelMission(pMission);
  2826. m_mapMissions.Remove(pfmMissionGone->dwCookie);
  2827. delete pMission;
  2828. }
  2829. break;
  2830. case FM_L_LOGON_ACK:
  2831. {
  2832. CASTPFM(pfmLogonAck, L, LOGON_ACK, pfm);
  2833. m_fLoggedOnToLobby = true;
  2834. m_lobbyServerOffset = pfmLogonAck->dwTimeOffset;
  2835. OnLogonLobbyAck(true, false, NULL);
  2836. }
  2837. break;
  2838. case FM_L_LOGON_NACK:
  2839. {
  2840. CASTPFM(pfmLogonNack, L, LOGON_NACK, pfm);
  2841. OnLogonLobbyAck(false, pfmLogonNack->fRetry, FM_VAR_REF(pfmLogonNack, Reason));
  2842. assert(m_fLoggedOnToLobby == false);
  2843. m_szLobbyCharName[0] = '\0';
  2844. }
  2845. break;
  2846. case FM_S_LOGON_CLUB_ACK:
  2847. {
  2848. CASTPFM(pfmLogonAck, S, LOGON_CLUB_ACK, pfm);
  2849. SetZoneClubID(pfmLogonAck->nMemberID);
  2850. m_fLoggedOnToClub = true;
  2851. OnLogonClubAck(true, false, NULL);
  2852. }
  2853. break;
  2854. case FM_S_LOGON_CLUB_NACK:
  2855. {
  2856. CASTPFM(pfmLogonNack, S, LOGON_CLUB_NACK, pfm);
  2857. OnLogonClubAck(false, pfmLogonNack->fRetry, FM_VAR_REF(pfmLogonNack, Reason));
  2858. assert(m_fLoggedOnToClub == false);
  2859. m_szClubCharName[0] = '\0';
  2860. }
  2861. break;
  2862. case FM_L_FOUND_PLAYER:
  2863. {
  2864. CASTPFM(pfmFoundPlayer, L, FOUND_PLAYER, pfm);
  2865. MissionInfo* pMissionInfo;
  2866. if (pfmFoundPlayer->dwCookie == NA)
  2867. pMissionInfo = NULL;
  2868. else
  2869. pMissionInfo = GetLobbyMission(pfmFoundPlayer->dwCookie);
  2870. m_pClientEventSource->OnFoundPlayer(pMissionInfo);
  2871. }
  2872. break;
  2873. default:
  2874. {
  2875. // Note: we can't assert here because there are some messages
  2876. // which are only handled in TrekWindowImpl::HandleMessage. TrekIGC.cpp
  2877. // already asserts that one of the two handles the message, however.
  2878. //ZError("unknown message");
  2879. hr = S_FALSE;
  2880. }
  2881. }
  2882. return (hr);
  2883. }