clintlib.cpp 120 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069
  1. #include "pch.h"
  2. ALLOC_MSG_LIST;
  3. bool g_bCheckFiles = false;
  4. /////////////////////////////////////////////////////////////////////////////
  5. // ClientEventSource
  6. class ClientEventSource : public IClientEventSource
  7. {
  8. private:
  9. TList<TRef<IClientEventSink> > m_listSinks;
  10. void RemoveDeadSinks()
  11. {
  12. // remove all of the NULL elements from the list
  13. while (m_listSinks.Remove(NULL))
  14. {
  15. }
  16. }
  17. public:
  18. void AddSink(IClientEventSink* psink)
  19. {
  20. m_listSinks.PushFront(psink);
  21. }
  22. void RemoveSink(IClientEventSink* psink)
  23. {
  24. // in order to let us remove the current sync while walking the list of
  25. // sinks, just set the sink to NULL for the moment.
  26. m_listSinks.Replace(psink, NULL);
  27. }
  28. #define ForEachSink(fnc) \
  29. TList<TRef<IClientEventSink> >::Iterator iter(m_listSinks); \
  30. while (!iter.End()) \
  31. { \
  32. if (iter.Value() != NULL) \
  33. iter.Value()->fnc; \
  34. iter.Next(); \
  35. } \
  36. RemoveDeadSinks(); \
  37. void OnAddMission(MissionInfo* pMissionInfo)
  38. {
  39. ForEachSink( OnAddMission(pMissionInfo) )
  40. }
  41. void OnMissionCountdown(MissionInfo* pMissionInfo)
  42. {
  43. ForEachSink( OnMissionCountdown(pMissionInfo) )
  44. }
  45. void OnMissionStarted(MissionInfo* pMissionInfo)
  46. {
  47. ForEachSink( OnMissionStarted(pMissionInfo) )
  48. }
  49. void OnMissionEnded(MissionInfo* pMissionInfo)
  50. {
  51. ForEachSink( OnMissionEnded(pMissionInfo) )
  52. }
  53. void OnLockLobby(bool bLock)
  54. {
  55. ForEachSink( OnLockLobby(bLock) )
  56. }
  57. void OnLockSides(bool bLock)
  58. {
  59. ForEachSink( OnLockSides(bLock) )
  60. }
  61. void OnFoundPlayer(MissionInfo* pMissionInfo)
  62. {
  63. ForEachSink( OnFoundPlayer(pMissionInfo) )
  64. }
  65. void OnEnterMission()
  66. {
  67. ForEachSink( OnEnterMission() )
  68. }
  69. void OnDelMission(MissionInfo* pMissionInfo)
  70. {
  71. ForEachSink( OnDelMission(pMissionInfo) )
  72. }
  73. void OnAddPlayer(MissionInfo* pMissionInfo, SideID sideID, PlayerInfo* pPlayerInfo)
  74. {
  75. ForEachSink( OnAddPlayer(pMissionInfo, sideID, pPlayerInfo) )
  76. }
  77. void OnDelPlayer(MissionInfo* pMissionInfo, SideID sideID, PlayerInfo* pPlayerInfo, QuitSideReason reason, const char* szMessageParam)
  78. {
  79. ForEachSink( OnDelPlayer(pMissionInfo, sideID, pPlayerInfo, reason, szMessageParam) )
  80. }
  81. void OnAddRequest(MissionInfo* pMissionInfo, SideID sideID, PlayerInfo* pPlayerInfo)
  82. {
  83. ForEachSink( OnAddRequest(pMissionInfo, sideID, pPlayerInfo) )
  84. }
  85. void OnDelRequest(MissionInfo* pMissionInfo, SideID sideID, PlayerInfo* pPlayerInfo, DelPositionReqReason reason)
  86. {
  87. ForEachSink( OnDelRequest(pMissionInfo, sideID, pPlayerInfo, reason) )
  88. }
  89. void OnTeamInactive(MissionInfo* pMissionInfo, SideID sideID)
  90. {
  91. ForEachSink( OnTeamInactive(pMissionInfo, sideID) )
  92. }
  93. void OnTeamReadyChange(MissionInfo* pMissionInfo, SideID sideID, bool fTeamReady)
  94. {
  95. ForEachSink( OnTeamReadyChange(pMissionInfo, sideID, fTeamReady) )
  96. }
  97. void OnTeamForceReadyChange(MissionInfo* pMissionInfo, SideID sideID, bool fTeamForceReady)
  98. {
  99. ForEachSink( OnTeamForceReadyChange(pMissionInfo, sideID, fTeamForceReady) )
  100. }
  101. void OnTeamAutoAcceptChange(MissionInfo* pMissionInfo, SideID sideID, bool fAutoAccept)
  102. {
  103. ForEachSink( OnTeamAutoAcceptChange(pMissionInfo, sideID, fAutoAccept) )
  104. }
  105. void OnTeamCivChange(MissionInfo* pMissionInfo, SideID sideID, CivID civID)
  106. {
  107. ForEachSink( OnTeamCivChange(pMissionInfo, sideID, civID) )
  108. }
  109. void OnTeamNameChange(MissionInfo* pMissionInfo, SideID sideID)
  110. {
  111. ForEachSink( OnTeamNameChange(pMissionInfo, sideID) )
  112. }
  113. void OnPlayerStatusChange(MissionInfo* pMissionInfo, SideID sideID, PlayerInfo* pPlayerInfo)
  114. {
  115. ForEachSink( OnPlayerStatusChange(pMissionInfo, sideID, pPlayerInfo) )
  116. }
  117. void OnMoneyChange(PlayerInfo* pPlayerInfo)
  118. {
  119. ForEachSink( OnMoneyChange(pPlayerInfo) )
  120. }
  121. void OnBucketChange(BucketChange bc, IbucketIGC* b)
  122. {
  123. ForEachSink( OnBucketChange(bc, b) )
  124. }
  125. void OnTechTreeChanged(SideID sid)
  126. {
  127. ForEachSink( OnTechTreeChanged(sid) )
  128. }
  129. void OnStationCaptured(StationID stationID, SideID sideID)
  130. {
  131. ForEachSink( OnStationCaptured(stationID, sideID) )
  132. }
  133. void OnModelTerminated(ImodelIGC* pmodel)
  134. {
  135. ForEachSink( OnModelTerminated(pmodel) )
  136. }
  137. void OnLoadoutChanged(IpartIGC* ppart, LoadoutChange lc)
  138. {
  139. ForEachSink( OnLoadoutChanged(ppart, lc) )
  140. }
  141. void OnTurretStateChanging(bool bTurret)
  142. {
  143. ForEachSink( OnTurretStateChanging(bTurret) )
  144. }
  145. void OnPurchaseCompleted(bool bAllPartsBought)
  146. {
  147. ForEachSink( OnPurchaseCompleted(bAllPartsBought) )
  148. }
  149. void OnShipStatusChange(PlayerInfo* pplayer)
  150. {
  151. ForEachSink( OnShipStatusChange(pplayer) )
  152. }
  153. void OnBoardShip(IshipIGC* pshipChild, IshipIGC* pshipParent)
  154. {
  155. ForEachSink( OnBoardShip(pshipChild, pshipParent) )
  156. }
  157. void OnBoardFailed(IshipIGC* pshipParent)
  158. {
  159. ForEachSink( OnBoardFailed(pshipParent) )
  160. }
  161. void OnDiscoveredStation(IstationIGC* pstation)
  162. {
  163. ForEachSink( OnDiscoveredStation(pstation) )
  164. }
  165. void OnDiscoveredAsteroid(IasteroidIGC* pasteroid)
  166. {
  167. ForEachSink( OnDiscoveredAsteroid(pasteroid) )
  168. }
  169. void OnClusterChanged(IclusterIGC* pcluster)
  170. {
  171. ForEachSink( OnClusterChanged(pcluster) )
  172. }
  173. void OnGameoverStats()
  174. {
  175. ForEachSink( OnGameoverStats() )
  176. }
  177. void OnGameoverPlayers()
  178. {
  179. ForEachSink( OnGameoverPlayers() )
  180. }
  181. void OnDeleteChatMessage(ChatInfo* pchatInfo)
  182. {
  183. ForEachSink( OnDeleteChatMessage(pchatInfo) )
  184. }
  185. void OnNewChatMessage()
  186. {
  187. ForEachSink( OnNewChatMessage() )
  188. }
  189. void OnClearChat()
  190. {
  191. ForEachSink( OnClearChat() )
  192. }
  193. void OnChatMessageChange()
  194. {
  195. ForEachSink( OnChatMessageChange() )
  196. }
  197. void OnLostConnection(const char * szReason)
  198. {
  199. ForEachSink( OnLostConnection(szReason) )
  200. }
  201. void OnEnterModalState()
  202. {
  203. ForEachSink( OnEnterModalState() )
  204. }
  205. void OnLeaveModalState()
  206. {
  207. ForEachSink( OnLeaveModalState() )
  208. }
  209. void OnLogonClub()
  210. {
  211. ForEachSink( OnLogonClub() )
  212. }
  213. void OnLogonClubFailed(bool bRetry, const char * szReason)
  214. {
  215. ForEachSink( OnLogonClubFailed(bRetry, szReason) )
  216. }
  217. void OnLogonLobby()
  218. {
  219. ForEachSink( OnLogonLobby() )
  220. }
  221. void OnLogonLobbyFailed(bool bRetry, const char * szReason)
  222. {
  223. ForEachSink( OnLogonLobbyFailed(bRetry, szReason) )
  224. }
  225. void OnLogonGameServer()
  226. {
  227. ForEachSink( OnLogonGameServer() )
  228. }
  229. void OnLogonGameServerFailed(bool bRetry, const char * szReason)
  230. {
  231. ForEachSink( OnLogonGameServerFailed(bRetry, szReason) )
  232. }
  233. };
  234. /////////////////////////////////////////////////////////////////////////////
  235. // IClientEventSink
  236. class ClientEventSinkDelegate : public IClientEventSink {
  237. private:
  238. IClientEventSink* m_psink;
  239. public:
  240. ClientEventSinkDelegate(IClientEventSink* psink) :
  241. m_psink(psink)
  242. {
  243. }
  244. void OnAddMission(MissionInfo* pMissionInfo)
  245. {
  246. m_psink->OnAddMission(pMissionInfo);
  247. }
  248. void OnMissionCountdown(MissionInfo* pMissionInfo)
  249. {
  250. m_psink->OnMissionCountdown(pMissionInfo);
  251. }
  252. void OnMissionStarted(MissionInfo* pMissionInfo)
  253. {
  254. m_psink->OnMissionStarted(pMissionInfo);
  255. }
  256. void OnMissionEnded(MissionInfo* pMissionInfo)
  257. {
  258. m_psink->OnMissionEnded(pMissionInfo);
  259. }
  260. void OnLockLobby(bool bLock)
  261. {
  262. m_psink->OnLockLobby(bLock);
  263. }
  264. void OnLockSides(bool bLock)
  265. {
  266. m_psink->OnLockSides(bLock);
  267. }
  268. void OnFoundPlayer(MissionInfo* pMissionDef)
  269. {
  270. m_psink->OnFoundPlayer(pMissionDef);
  271. }
  272. void OnEnterMission()
  273. {
  274. m_psink->OnEnterMission();
  275. }
  276. void OnDelMission(MissionInfo* pMissionInfo)
  277. {
  278. m_psink->OnDelMission(pMissionInfo);
  279. }
  280. void OnAddPlayer(MissionInfo* pMissionInfo, SideID sideID, PlayerInfo* pPlayerInfo)
  281. {
  282. m_psink->OnAddPlayer(pMissionInfo, sideID, pPlayerInfo);
  283. }
  284. void OnDelPlayer(MissionInfo* pMissionInfo, SideID sideID, PlayerInfo* pPlayerInfo, QuitSideReason reason, const char* szMessageParam)
  285. {
  286. m_psink->OnDelPlayer(pMissionInfo, sideID, pPlayerInfo, reason, szMessageParam);
  287. }
  288. void OnAddRequest(MissionInfo* pMissionInfo, SideID sideID, PlayerInfo* pPlayerInfo)
  289. {
  290. m_psink->OnAddRequest(pMissionInfo, sideID, pPlayerInfo);
  291. }
  292. void OnDelRequest(MissionInfo* pMissionInfo, SideID sideID, PlayerInfo* pPlayerInfo, DelPositionReqReason reason)
  293. {
  294. m_psink->OnDelRequest(pMissionInfo, sideID, pPlayerInfo, reason);
  295. }
  296. void OnTeamInactive(MissionInfo* pMissionInfo, SideID sideID)
  297. {
  298. m_psink->OnTeamInactive(pMissionInfo, sideID);
  299. }
  300. void OnTeamReadyChange(MissionInfo* pMissionInfo, SideID sideID, bool fTeamReady)
  301. {
  302. m_psink->OnTeamReadyChange(pMissionInfo, sideID, fTeamReady);
  303. }
  304. void OnTeamForceReadyChange(MissionInfo* pMissionInfo, SideID sideID, bool fTeamForceReady)
  305. {
  306. m_psink->OnTeamForceReadyChange(pMissionInfo, sideID, fTeamForceReady);
  307. }
  308. void OnTeamAutoAcceptChange(MissionInfo* pMissionInfo, SideID sideID, bool fAutoAccept)
  309. {
  310. m_psink->OnTeamAutoAcceptChange(pMissionInfo, sideID, fAutoAccept);
  311. }
  312. void OnTeamCivChange(MissionInfo* pMissionInfo, SideID sideID, CivID civID)
  313. {
  314. m_psink->OnTeamCivChange(pMissionInfo, sideID, civID);
  315. }
  316. void OnTeamNameChange(MissionInfo* pMissionInfo, SideID sideID)
  317. {
  318. m_psink->OnTeamNameChange(pMissionInfo, sideID);
  319. }
  320. void OnPlayerStatusChange(MissionInfo* pMissionInfo, SideID sideID, PlayerInfo* pPlayerInfo)
  321. {
  322. m_psink->OnPlayerStatusChange(pMissionInfo, sideID, pPlayerInfo);
  323. }
  324. void OnMoneyChange(PlayerInfo* pPlayerInfo)
  325. {
  326. m_psink->OnMoneyChange(pPlayerInfo);
  327. }
  328. void OnBucketChange(BucketChange bc, IbucketIGC* b)
  329. {
  330. m_psink->OnBucketChange(bc, b);
  331. }
  332. void OnTechTreeChanged(SideID sid)
  333. {
  334. m_psink->OnTechTreeChanged(sid);
  335. }
  336. void OnStationCaptured(StationID stationID, SideID sideID)
  337. {
  338. m_psink->OnStationCaptured(stationID, sideID);
  339. }
  340. void OnModelTerminated(ImodelIGC* pmodel)
  341. {
  342. m_psink->OnModelTerminated(pmodel);
  343. }
  344. void OnLoadoutChanged(IpartIGC* ppart, LoadoutChange lc)
  345. {
  346. m_psink->OnLoadoutChanged(ppart, lc);
  347. }
  348. void OnTurretStateChanging(bool bTurret)
  349. {
  350. m_psink->OnTurretStateChanging(bTurret);
  351. }
  352. void OnPurchaseCompleted(bool bAllPartsBought)
  353. {
  354. m_psink->OnPurchaseCompleted(bAllPartsBought);
  355. }
  356. void OnShipStatusChange(PlayerInfo* pplayer)
  357. {
  358. m_psink->OnShipStatusChange(pplayer);
  359. }
  360. void OnBoardShip(IshipIGC* pshipChild, IshipIGC* pshipParent)
  361. {
  362. m_psink->OnBoardShip(pshipChild, pshipParent);
  363. }
  364. void OnBoardFailed(IshipIGC* pshipParent)
  365. {
  366. m_psink->OnBoardFailed(pshipParent);
  367. }
  368. void OnDiscoveredStation(IstationIGC* pstation)
  369. {
  370. m_psink->OnDiscoveredStation(pstation);
  371. }
  372. void OnDiscoveredAsteroid(IasteroidIGC* pasteroid)
  373. {
  374. m_psink->OnDiscoveredAsteroid(pasteroid);
  375. }
  376. void OnClusterChanged(IclusterIGC* pcluster)
  377. {
  378. m_psink->OnClusterChanged(pcluster);
  379. }
  380. void OnGameoverStats()
  381. {
  382. m_psink->OnGameoverStats();
  383. }
  384. void OnGameoverPlayers()
  385. {
  386. m_psink->OnGameoverPlayers();
  387. }
  388. void OnDeleteChatMessage(ChatInfo* pchatInfo)
  389. {
  390. m_psink->OnDeleteChatMessage(pchatInfo);
  391. }
  392. void OnNewChatMessage()
  393. {
  394. m_psink->OnNewChatMessage();
  395. }
  396. void OnClearChat()
  397. {
  398. m_psink->OnClearChat();
  399. }
  400. void OnChatMessageChange()
  401. {
  402. m_psink->OnChatMessageChange();
  403. }
  404. void OnLostConnection(const char* szReason)
  405. {
  406. m_psink->OnLostConnection(szReason);
  407. }
  408. void OnEnterModalState()
  409. {
  410. m_psink->OnEnterModalState();
  411. }
  412. void OnLeaveModalState()
  413. {
  414. m_psink->OnLeaveModalState();
  415. }
  416. void OnLogonClub()
  417. {
  418. m_psink->OnLogonClub();
  419. }
  420. void OnLogonClubFailed(bool bRetry, const char * szReason)
  421. {
  422. m_psink->OnLogonClubFailed(bRetry, szReason);
  423. }
  424. void OnLogonLobby()
  425. {
  426. m_psink->OnLogonLobby();
  427. }
  428. void OnLogonLobbyFailed(bool bRetry, const char * szReason)
  429. {
  430. m_psink->OnLogonLobbyFailed(bRetry, szReason);
  431. }
  432. void OnLogonGameServer()
  433. {
  434. m_psink->OnLogonGameServer();
  435. }
  436. void OnLogonGameServerFailed(bool bRetry, const char * szReason)
  437. {
  438. m_psink->OnLogonGameServerFailed(bRetry, szReason);
  439. }
  440. };
  441. TRef<IClientEventSink> IClientEventSink::CreateDelegate(IClientEventSink* psink)
  442. {
  443. return new ClientEventSinkDelegate(psink);
  444. }
  445. /////////////////////////////////////////////////////////////////////////////
  446. // ChatInfo
  447. void ChatInfo::SetChat(ChatTarget ctRecipient,
  448. const ZString& strText,
  449. CommandID cid,
  450. ImodelIGC* pmodelTarget,
  451. const Color& color,
  452. bool bFromPlayer,
  453. bool bFromObjectModel,
  454. bool bFromLeader)
  455. {
  456. m_ctRecipient = ctRecipient;
  457. m_cidCommand = cid;
  458. m_pmodelTarget = pmodelTarget;
  459. m_strMessage = strText;
  460. m_colorMessage = color;
  461. m_bFromPlayer = bFromPlayer;
  462. m_bFromObjectModel = bFromObjectModel;
  463. m_bFromLeader = bFromLeader;
  464. }
  465. void ChatInfo::ClearTarget(void)
  466. {
  467. m_cidCommand = c_cidNone;
  468. if (m_pmodelTarget->GetObjectType() != OT_buoy)
  469. {
  470. m_strMessage += " <dead>";
  471. }
  472. m_pmodelTarget = NULL;
  473. }
  474. /////////////////////////////////////////////////////////////////////////////
  475. // MissionInfo
  476. MissionInfo::MissionInfo(DWORD dwCookie) :
  477. m_sideLobby(SIDE_TEAMLOBBY)
  478. {
  479. m_pfmMissionDef = new FMD_S_MISSIONDEF;
  480. memset(m_pfmMissionDef, 0, sizeof(FMD_S_MISSIONDEF));
  481. m_pfmMissionDef->dwCookie = dwCookie;
  482. m_fGuaranteedSlotsAvailable = false;
  483. m_fAnySlotsAvailable = false;
  484. m_fCountdownStarted = false;
  485. m_nNumPlayers = 0;
  486. }
  487. MissionInfo::~MissionInfo()
  488. {
  489. if (m_pfmMissionDef)
  490. delete m_pfmMissionDef;
  491. TMapListWrapper<SideID, SideInfo*>::Iterator iterSideInfo(m_mapSideInfo);
  492. while (!iterSideInfo.End())
  493. {
  494. delete iterSideInfo.Value();
  495. iterSideInfo.Next();
  496. }
  497. }
  498. void MissionInfo::Update(FMD_S_MISSIONDEF* pfmMissionDef)
  499. {
  500. int numSidesOld = NumSides();
  501. memcpy(m_pfmMissionDef, pfmMissionDef, sizeof(FMD_S_MISSIONDEF));
  502. // create or destroy sides, as needed.
  503. if (NumSides() > numSidesOld)
  504. {
  505. // create the new sides the easy way
  506. for(SideID sideID = numSidesOld; sideID < NumSides(); sideID++)
  507. {
  508. GetSideInfo(sideID);
  509. }
  510. }
  511. else
  512. {
  513. // destroy the old sides
  514. for (SideID sideID = numSidesOld - 1; sideID >= NumSides(); sideID--)
  515. {
  516. m_mapSideInfo.Remove(sideID);
  517. }
  518. }
  519. // signal the sides list in case something has changed.
  520. m_mapSideInfo.GetSink()();
  521. }
  522. void MissionInfo::Update(FMD_LS_LOBBYMISSIONINFO* pfmLobbyMissionInfo)
  523. {
  524. int numSidesOld = NumSides();
  525. Strncpy(m_pfmMissionDef->misparms.strGameName, FM_VAR_REF(pfmLobbyMissionInfo, szGameName), c_cbGameName);
  526. m_pfmMissionDef->misparms.strGameName[c_cbGameName - 1] = '\0';
  527. m_strGameDetailsFiles = (FM_VAR_REF(pfmLobbyMissionInfo, szGameDetailsFiles) != NULL)
  528. ? FM_VAR_REF(pfmLobbyMissionInfo, szGameDetailsFiles) : "";
  529. m_pfmMissionDef->misparms.nTeams = pfmLobbyMissionInfo->nTeams;
  530. m_pfmMissionDef->misparms.nMinPlayersPerTeam = pfmLobbyMissionInfo->nMinPlayersPerTeam;
  531. m_pfmMissionDef->misparms.nMaxPlayersPerTeam = pfmLobbyMissionInfo->nMaxPlayersPerTeam;
  532. m_pfmMissionDef->misparms.iMinRank = pfmLobbyMissionInfo->nMinRank;
  533. m_pfmMissionDef->misparms.iMaxRank = pfmLobbyMissionInfo->nMaxRank;
  534. m_pfmMissionDef->misparms.timeStart = Time(pfmLobbyMissionInfo->dwStartTime);
  535. m_nNumPlayers = pfmLobbyMissionInfo->nNumPlayers;
  536. m_fGuaranteedSlotsAvailable = pfmLobbyMissionInfo->fGuaranteedSlotsAvailable;
  537. m_fAnySlotsAvailable = pfmLobbyMissionInfo->fAnySlotsAvailable;
  538. m_nNumPlayers = pfmLobbyMissionInfo->nNumPlayers;
  539. SetInProgress(pfmLobbyMissionInfo->fInProgress);
  540. SetCountdownStarted(pfmLobbyMissionInfo->fCountdownStarted);
  541. m_pfmMissionDef->misparms.bScoresCount = pfmLobbyMissionInfo->fScoresCount;
  542. m_pfmMissionDef->misparms.bAllowDevelopments = pfmLobbyMissionInfo->fAllowDevelopments;
  543. m_pfmMissionDef->misparms.bInvulnerableStations = pfmLobbyMissionInfo->fInvulnerableStations;
  544. m_pfmMissionDef->misparms.bObjectModelCreated = pfmLobbyMissionInfo->fMSArena;
  545. m_pfmMissionDef->misparms.nTotalMaxPlayersPerGame = pfmLobbyMissionInfo->nMaxPlayersPerGame;
  546. m_pfmMissionDef->misparms.bSquadGame = pfmLobbyMissionInfo->fSquadGame;
  547. m_pfmMissionDef->misparms.bEjectPods = pfmLobbyMissionInfo->fEjectPods;
  548. if (pfmLobbyMissionInfo->fLimitedLives && m_pfmMissionDef->misparms.iLives == 0x7fff)
  549. m_pfmMissionDef->misparms.iLives = 1;
  550. else if (!pfmLobbyMissionInfo->fLimitedLives && m_pfmMissionDef->misparms.iLives != 0x7fff)
  551. m_pfmMissionDef->misparms.iLives = 0x7fff;
  552. if (pfmLobbyMissionInfo->fConquest && !m_pfmMissionDef->misparms.IsConquestGame())
  553. m_pfmMissionDef->misparms.iGoalConquestPercentage = 100;
  554. else if (!pfmLobbyMissionInfo->fConquest && m_pfmMissionDef->misparms.IsConquestGame())
  555. m_pfmMissionDef->misparms.iGoalConquestPercentage = 0;
  556. if (pfmLobbyMissionInfo->fDeathMatch && !m_pfmMissionDef->misparms.IsDeathMatchGame())
  557. m_pfmMissionDef->misparms.nGoalTeamKills = 1;
  558. else if (!pfmLobbyMissionInfo->fDeathMatch && m_pfmMissionDef->misparms.IsDeathMatchGame())
  559. m_pfmMissionDef->misparms.nGoalTeamKills = 0;
  560. if (pfmLobbyMissionInfo->fCountdown && !m_pfmMissionDef->misparms.IsCountdownGame())
  561. m_pfmMissionDef->misparms.dtGameLength = 600;
  562. else if (!pfmLobbyMissionInfo->fCountdown && m_pfmMissionDef->misparms.IsCountdownGame())
  563. m_pfmMissionDef->misparms.dtGameLength = 0;
  564. if (pfmLobbyMissionInfo->fProsperity && !m_pfmMissionDef->misparms.IsProsperityGame())
  565. m_pfmMissionDef->misparms.fGoalTeamMoney = 100;
  566. else if (!pfmLobbyMissionInfo->fProsperity && m_pfmMissionDef->misparms.IsProsperityGame())
  567. m_pfmMissionDef->misparms.fGoalTeamMoney = 0;
  568. if (pfmLobbyMissionInfo->fArtifacts && !m_pfmMissionDef->misparms.IsArtifactsGame())
  569. m_pfmMissionDef->misparms.nGoalArtifactsCount = 10;
  570. else if (!pfmLobbyMissionInfo->fArtifacts && m_pfmMissionDef->misparms.IsArtifactsGame())
  571. m_pfmMissionDef->misparms.nGoalArtifactsCount = 0;
  572. if (pfmLobbyMissionInfo->fFlags && !m_pfmMissionDef->misparms.IsFlagsGame())
  573. m_pfmMissionDef->misparms.nGoalFlagsCount = 10;
  574. else if (!pfmLobbyMissionInfo->fFlags && m_pfmMissionDef->misparms.IsFlagsGame())
  575. m_pfmMissionDef->misparms.nGoalFlagsCount = 0;
  576. if (pfmLobbyMissionInfo->fTerritorial && !m_pfmMissionDef->misparms.IsTerritoryGame())
  577. m_pfmMissionDef->misparms.iGoalTerritoryPercentage = 70;
  578. else if (!pfmLobbyMissionInfo->fTerritorial && m_pfmMissionDef->misparms.IsTerritoryGame())
  579. m_pfmMissionDef->misparms.iGoalTerritoryPercentage = 100;
  580. // create or destroy sides, as needed.
  581. if (NumSides() > numSidesOld)
  582. {
  583. // create the new sides the easy way
  584. for(SideID sideID = numSidesOld; sideID < NumSides(); sideID++)
  585. {
  586. GetSideInfo(sideID);
  587. }
  588. }
  589. else
  590. {
  591. // destroy the old sides
  592. for (SideID sideID = numSidesOld - 1; sideID >= NumSides(); sideID--)
  593. {
  594. m_mapSideInfo.Remove(sideID);
  595. }
  596. }
  597. // copy the squad info
  598. {
  599. SideID sideID;
  600. SquadID* vSquadIDs = (SquadID*)FM_VAR_REF(pfmLobbyMissionInfo, rgSquadIDs);
  601. int numSquads = pfmLobbyMissionInfo->cbrgSquadIDs / sizeof(SquadID);
  602. assert(numSquads < c_cSidesMax);
  603. for (sideID = 0; sideID < numSquads; sideID++)
  604. squadIDs[sideID] = vSquadIDs[sideID];
  605. for (; sideID < c_cSidesMax; sideID++)
  606. squadIDs[sideID] = NA;
  607. }
  608. // signal the sides list in case something has changed.
  609. m_mapSideInfo.GetSink()();
  610. }
  611. void MissionInfo::UpdateStartTime(Time timeStart)
  612. {
  613. m_pfmMissionDef->misparms.timeStart = timeStart;
  614. }
  615. int MissionInfo::GuaranteedPositions()
  616. {
  617. SideID sideID;
  618. int nTotal = 0;
  619. for(sideID = 0; sideID < NumSides(); sideID++)
  620. {
  621. if (SideActive(sideID) && (SideAutoAccept(sideID)
  622. || (SideNumPlayers(sideID) == 0 && AutoAcceptLeaders())))
  623. {
  624. nTotal += SideMaxPlayers(sideID) - SideNumPlayers(sideID);
  625. }
  626. }
  627. return nTotal;
  628. }
  629. void MissionInfo::PurgePlayers()
  630. {
  631. TMapListWrapper<SideID, SideInfo*>::Iterator iterSideInfo(m_mapSideInfo);
  632. while (!iterSideInfo.End())
  633. {
  634. delete iterSideInfo.Value();
  635. iterSideInfo.Next();
  636. }
  637. m_mapSideInfo.SetEmpty();
  638. for (SideID id = 0; id < NumSides(); id++)
  639. {
  640. m_pfmMissionDef->rgcPlayers[id] = 0;
  641. }
  642. }
  643. bool MissionInfo::HasSquad(SquadID squadID)
  644. {
  645. for (SideID id = 0; id < NumSides(); id++)
  646. {
  647. if (squadIDs[id] == squadID)
  648. return true;
  649. }
  650. return false;
  651. }
  652. void MissionInfo::SetSideLeader(PlayerInfo* pPlayerInfo)
  653. {
  654. m_pfmMissionDef->rgShipIDLeaders[pPlayerInfo->SideID()] = pPlayerInfo->ShipID();
  655. if (pPlayerInfo->IsMissionOwner())
  656. m_pfmMissionDef->iSideMissionOwner = pPlayerInfo->SideID();
  657. GetSideInfo(pPlayerInfo->SideID())->GetMembers().GetSink()();
  658. }
  659. bool MissionInfo::FindPlayer(SideID sideID, ShipID shipID)
  660. {
  661. ZAssert(sideID != NA);
  662. ZAssert(shipID >= 0);
  663. return GetSideInfo(sideID)->FindPlayer(shipID);
  664. }
  665. void MissionInfo::AddPlayer(PlayerInfo* pPlayerInfo)
  666. {
  667. SideID sideID = pPlayerInfo->SideID();
  668. ShipID shipID = pPlayerInfo->ShipID();
  669. ZAssert(sideID != NA);
  670. ZAssert(shipID >= 0);
  671. SideInfo* psideInfo = GetSideInfo(sideID);
  672. if (!psideInfo->FindPlayer(shipID))
  673. {
  674. if ((sideID >= 0) && pPlayerInfo->IsHuman())
  675. m_pfmMissionDef->rgcPlayers[sideID]++;
  676. psideInfo->RemoveRequest(shipID);
  677. psideInfo->AddPlayer(shipID);
  678. m_mapSideInfo.GetSink()();
  679. }
  680. }
  681. void MissionInfo::RemovePlayer(PlayerInfo* pPlayerInfo)
  682. {
  683. SideID sideID = pPlayerInfo->SideID();
  684. ShipID shipID = pPlayerInfo->ShipID();
  685. ZAssert(sideID != NA);
  686. ZAssert(shipID >= 0);
  687. if (sideID != SIDE_TEAMLOBBY)
  688. {
  689. // update the team owner info if the team owner is quiting
  690. if (m_pfmMissionDef->rgShipIDLeaders[sideID] == pPlayerInfo->ShipID())
  691. {
  692. pPlayerInfo->SetTeamLeader(false);
  693. m_pfmMissionDef->rgShipIDLeaders[sideID] = NA;
  694. if (m_pfmMissionDef->iSideMissionOwner == sideID)
  695. {
  696. m_pfmMissionDef->iSideMissionOwner = NA;
  697. pPlayerInfo->SetMissionOwner(false);
  698. }
  699. }
  700. }
  701. SideInfo* psideInfo = GetSideInfo(sideID);
  702. if (psideInfo->FindPlayer(shipID))
  703. {
  704. if ((sideID >= 0) && pPlayerInfo->IsHuman())
  705. m_pfmMissionDef->rgcPlayers[sideID]--;
  706. psideInfo->RemovePlayer(shipID);
  707. m_mapSideInfo.GetSink()();
  708. }
  709. }
  710. bool MissionInfo::FindRequest(SideID sideID, ShipID shipID)
  711. {
  712. ZAssert(sideID != NA);
  713. ZAssert(shipID >= 0);
  714. if (sideID == SIDE_TEAMLOBBY)
  715. return false;
  716. return GetSideInfo(sideID)->FindRequest(shipID);
  717. }
  718. void MissionInfo::AddRequest(SideID sideID, ShipID shipID)
  719. {
  720. ZAssert(sideID != NA);
  721. ZAssert(shipID >= 0);
  722. if (sideID == SIDE_TEAMLOBBY)
  723. return;
  724. SideInfo* psideInfo = GetSideInfo(sideID);
  725. ZAssert(psideInfo);
  726. if (!psideInfo->FindPlayer(shipID) && !psideInfo->FindRequest(shipID))
  727. psideInfo->AddRequest(shipID);
  728. m_mapSideInfo.GetSink()();
  729. }
  730. void MissionInfo::RemoveRequest(SideID sideID, ShipID shipID)
  731. {
  732. ZAssert(sideID != NA);
  733. ZAssert(shipID >= 0);
  734. if (sideID == SIDE_TEAMLOBBY)
  735. return;
  736. GetSideInfo(sideID)->RemoveRequest(shipID);
  737. m_mapSideInfo.GetSink()();
  738. }
  739. SideInfo* MissionInfo::GetSideInfo(SideID sideID)
  740. {
  741. ZAssert(sideID != NA);
  742. SideInfo* pSideInfo = NULL;
  743. if (sideID >= m_pfmMissionDef->misparms.nTeams)
  744. {
  745. assert(false);
  746. return NULL;
  747. }
  748. else if (!m_mapSideInfo.Find(sideID, pSideInfo) && sideID != SIDE_TEAMLOBBY)
  749. {
  750. pSideInfo = new SideInfo(sideID);
  751. m_mapSideInfo.Set(sideID, pSideInfo);
  752. }
  753. else if (sideID == SIDE_TEAMLOBBY)
  754. {
  755. return &m_sideLobby;
  756. }
  757. return pSideInfo;
  758. }
  759. List* MissionInfo::GetSideList()
  760. {
  761. // make sure we have a complete sides list
  762. for (SideID id = 0; id < NumSides(); id++)
  763. GetSideInfo(id);
  764. GetSideInfo(SIDE_TEAMLOBBY);
  765. return new ListDelegate(&m_mapSideInfo);
  766. }
  767. /////////////////////////////////////////////////////////////////////////////
  768. // PlayerInfo
  769. PlayerInfo::PlayerInfo(void) :
  770. m_pship(NULL),
  771. m_vPersistPlayerScores(NULL),
  772. m_cPersistPlayerScores(0),
  773. m_bMute(false)
  774. {
  775. }
  776. PlayerInfo::~PlayerInfo()
  777. {
  778. if (m_vPersistPlayerScores)
  779. delete m_vPersistPlayerScores;
  780. ResetShipStatus();
  781. }
  782. void PlayerInfo::Set(FMD_S_PLAYERINFO* pfmPlayerInfo)
  783. {
  784. if (m_vPersistPlayerScores)
  785. delete m_vPersistPlayerScores;
  786. memcpy(&m_fmPlayerInfo, pfmPlayerInfo, sizeof(m_fmPlayerInfo));
  787. m_cPersistPlayerScores = pfmPlayerInfo->cbrgPersistPlayerScores / sizeof(PersistPlayerScoreObject);
  788. if (m_cPersistPlayerScores > 0)
  789. {
  790. PersistPlayerScoreObject* vSrcScores
  791. = (PersistPlayerScoreObject*)FM_VAR_REF(pfmPlayerInfo, rgPersistPlayerScores);
  792. m_vPersistPlayerScores = new PersistPlayerScoreObject[m_cPersistPlayerScores];
  793. for (int i = 0; i < m_cPersistPlayerScores; i++)
  794. m_vPersistPlayerScores[i] = vSrcScores[i];
  795. }
  796. else
  797. {
  798. m_vPersistPlayerScores = NULL;
  799. }
  800. }
  801. const PersistPlayerScoreObject& PlayerInfo::GetPersistScore(CivID civId) const
  802. {
  803. for (int i = 0; i < m_cPersistPlayerScores; i++)
  804. {
  805. if (m_vPersistPlayerScores[i].GetCivID() == civId)
  806. return m_vPersistPlayerScores[i];
  807. }
  808. return m_persist;
  809. }
  810. void PlayerInfo::UpdateScore(PersistPlayerScoreObject& ppso)
  811. {
  812. CivID civId = ppso.GetCivID();
  813. if (civId != NA)
  814. {
  815. int iCivScore;
  816. // look for the ppso for this civ
  817. for (iCivScore = 0; iCivScore < m_cPersistPlayerScores; ++iCivScore)
  818. {
  819. if (m_vPersistPlayerScores[iCivScore].GetCivID() == civId)
  820. break;
  821. }
  822. // if the given civ was not found, create a new entry for it.
  823. if (iCivScore >= m_cPersistPlayerScores)
  824. {
  825. PersistPlayerScoreObject* vppsoNew
  826. = new PersistPlayerScoreObject[m_cPersistPlayerScores + 1];
  827. for (int i = 0; i < m_cPersistPlayerScores; ++i)
  828. vppsoNew[i] = m_vPersistPlayerScores[i];
  829. if (m_vPersistPlayerScores)
  830. delete m_vPersistPlayerScores;
  831. m_vPersistPlayerScores = vppsoNew;
  832. ++m_cPersistPlayerScores;
  833. }
  834. // copy the stats
  835. m_vPersistPlayerScores[iCivScore] = ppso;
  836. }
  837. }
  838. CivID PlayerInfo::GetCivID() const
  839. {
  840. if (SideID() < 0)
  841. return NA;
  842. else
  843. return GetShip()->GetSide()->GetCivilization()->GetObjectID();
  844. }
  845. /////////////////////////////////////////////////////////////////////////////
  846. // BaseClient
  847. BaseClient::BaseClient(void)
  848. :
  849. m_pCoreIGC(CreateMission()),
  850. m_lastSend(Time::Now()),
  851. m_lastLagCheck(Time::Now()),
  852. m_serverOffset(0),
  853. m_serverOffsetValidF(false),
  854. m_selectedWeapon(0),
  855. m_messageType(c_mtNone),
  856. m_cookie(NA),
  857. m_plinkSelectedChat(NULL),
  858. m_pPlayerInfo(NULL),
  859. m_pMissionInfo(NULL),
  860. m_fLoggedOn(false),
  861. m_fLoggedOnToLobby(false),
  862. m_fLoggedOnToClub(false),
  863. m_fm(this),
  864. m_fmLobby(this),
  865. m_fmClub(this),
  866. m_ship(NULL),
  867. m_pshipLastSender(NULL),
  868. m_moneyLastRequest(0),
  869. m_sync(1.0f),
  870. m_viewCluster(NULL),
  871. m_cUnansweredPings(0),
  872. m_pAutoDownload(NULL),
  873. m_fZoneClub(false),
  874. m_fIsLobbied(false),
  875. m_dwCookieToJoin(NA),
  876. m_bInGame(false),
  877. m_bWaitingForGameRestart(false),
  878. m_cRankInfo(0),
  879. m_vRankInfo(NULL),
  880. m_cStaticMapInfo(0),
  881. m_vStaticMapInfo(NULL),
  882. m_lockdownCriteria(0),
  883. m_lobbyServerOffset(0),
  884. m_plistFindServerResults(NULL),
  885. m_strCDKey("")
  886. {
  887. CoInitialize(NULL);
  888. m_timeLastPing = m_lastSend;
  889. m_plistMissions = new ListDelegate(&m_mapMissions);
  890. m_pClientEventSource = new ClientEventSource();
  891. m_szCharName[0] = '\0';
  892. m_szClubCharName[0] = '\0';
  893. m_szLobbyCharName[0] = '\0';
  894. m_szIGCStaticFile[0] = '\0';
  895. m_pMissionInfo = NULL;
  896. }
  897. BaseClient::~BaseClient(void)
  898. {
  899. #ifdef USEAUTH
  900. FreeZoneAuthClient();
  901. #endif
  902. CoUninitialize();
  903. TMapListWrapper<DWORD, MissionInfo*>::Iterator iterMissions(m_mapMissions);
  904. while (!iterMissions.End())
  905. {
  906. delete iterMissions.Value();
  907. iterMissions.Next();
  908. }
  909. if (m_pMissionInfo)
  910. delete m_pMissionInfo;
  911. if (m_ci.pZoneTicket)
  912. HeapFree(GetProcessHeap(), 0, m_ci.pZoneTicket);
  913. m_fm.Shutdown();
  914. if (m_pAutoDownload)
  915. delete m_pAutoDownload;
  916. if (m_vRankInfo)
  917. delete [] m_vRankInfo;
  918. m_vRankInfo = NULL;
  919. if (m_vStaticMapInfo)
  920. delete [] m_vStaticMapInfo;
  921. m_vStaticMapInfo = NULL;
  922. }
  923. void BaseClient::Initialize(Time timeNow)
  924. {
  925. assert (m_pCoreIGC);
  926. //Now that we have a mission, create a (no-hull) ship for the player
  927. CreateDummyShip();
  928. m_pCoreIGC->Initialize(timeNow, this);
  929. }
  930. void BaseClient::Reinitialize(Time timeNow)
  931. {
  932. BaseClient::Terminate();
  933. // dont change m_pClientEventSource... too many things in WinTrek are holding on to it
  934. // and don't get reinitialized
  935. ZAssert(m_pCoreIGC == NULL);
  936. m_pCoreIGC = CreateMission();
  937. m_lastSend = timeNow;
  938. m_lastLagCheck = timeNow;
  939. m_timeLastPing = timeNow;
  940. m_serverOffset = 0;
  941. m_serverOffsetValidF = false;
  942. m_selectedWeapon = NULL;
  943. m_messageType = c_mtNone;
  944. m_cookie = NA;
  945. m_plinkSelectedChat = NULL;
  946. m_pPlayerInfo = NULL;
  947. if (m_pMissionInfo)
  948. delete m_pMissionInfo;
  949. m_pMissionInfo = NULL;
  950. m_fLoggedOn = false;
  951. m_pchaffLastCreated = NULL;
  952. m_pshipLastSender = NULL;
  953. m_moneyLastRequest = 0;
  954. m_mapBucketStatusArray.SetEmpty();
  955. m_listPlayers.purge(true);
  956. m_chatList.purge(true);
  957. Initialize(timeNow);
  958. }
  959. void BaseClient::Terminate(void)
  960. {
  961. assert (m_pCoreIGC);
  962. Disconnect();
  963. FlushGameState();
  964. }
  965. void BaseClient::FlushGameState()
  966. {
  967. m_pchaffLastCreated = NULL;
  968. if (m_ship)
  969. m_ship->AddRef();
  970. //Offline, we need to explicitly terminate all of the ships
  971. {
  972. const ShipListIGC* ships = m_pCoreIGC->GetShips();
  973. ShipLinkIGC* l;
  974. while ((l = ships->first()) != NULL)
  975. {
  976. l->data()->Terminate();
  977. }
  978. }
  979. m_listPlayers.purge();
  980. m_pCoreIGC->Terminate();
  981. // Destroy the dummy ship
  982. if (m_ship)
  983. {
  984. DestroyDummyShip();
  985. m_ship->Release();
  986. m_ship = NULL;
  987. }
  988. delete m_pCoreIGC;
  989. m_pCoreIGC = NULL;
  990. m_bInGame = false;
  991. }
  992. HRESULT BaseClient::ConnectToServer(ConnectInfo & ci, DWORD dwCookie, Time now, const char* szPassword, bool bStandalonePrivate)
  993. {
  994. // connect to a particular *game* on this server
  995. HRESULT hr = S_OK;
  996. // review: in the event of retry logon after logon failure
  997. // we need to treat this as a reconnect and dump first connection...
  998. // we should see about reusing the connection
  999. assert(IFF(ci.cbZoneTicket > 0, ci.pZoneTicket));
  1000. Reinitialize(now);
  1001. assert (!m_fm.IsConnected() && !m_fLoggedOn);
  1002. // this does everything up to, and including, creating a new player
  1003. if (ci.guidSession != GUID_NULL)
  1004. {
  1005. assert(ci.strServer.IsEmpty());
  1006. hr = m_fm.JoinSessionInstance(ci.guidSession, ci.szName);
  1007. }
  1008. else
  1009. {
  1010. if (bStandalonePrivate)
  1011. hr = m_fm.JoinSession(FEDSRV_STANDALONE_PRIVATE_GUID, ci.strServer, ci.szName);
  1012. else
  1013. hr = m_fm.JoinSession(FEDSRV_GUID, ci.strServer, ci.szName);
  1014. }
  1015. // TODO: Remove this when we are ready to enforce CD Keys
  1016. if (m_strCDKey.IsEmpty())
  1017. m_strCDKey = ZString(ci.szName).ToUpper();
  1018. if (m_fm.IsConnected())
  1019. {
  1020. ZSucceeded(hr);
  1021. // Let's formally announce ourselves to the server
  1022. SetMessageType(c_mtGuaranteed);
  1023. BEGIN_PFM_CREATE(m_fm, pfmLogon, C, LOGONREQ)
  1024. FM_VAR_PARM(ci.szName, CB_ZTS)
  1025. FM_VAR_PARM(ci.pZoneTicket, ci.cbZoneTicket)
  1026. FM_VAR_PARM((PCC)m_strCDKey.Scramble(bStandalonePrivate ? "corrupt wave file" : ci.szName), CB_ZTS)
  1027. FM_VAR_PARM(szPassword, CB_ZTS)
  1028. END_PFM_CREATE
  1029. pfmLogon->fedsrvVer = MSGVER;
  1030. pfmLogon->dwCookie = dwCookie;
  1031. //pfmLogon->zgs = m_fm.GetEncryptedZoneTicket();
  1032. pfmLogon->time = Time::Now ();
  1033. debugf("Logging on to game server \"%s\"...\n",
  1034. ci.strServer.IsEmpty() ? "" : (LPCSTR)ci.strServer);
  1035. SendMessages();
  1036. }
  1037. retailf("$$MSRGuard:Set:UserName=%s\n", ci.szName);
  1038. m_cUnansweredPings = 0;
  1039. m_serverOffsetValidF = false;
  1040. return hr;
  1041. }
  1042. HRESULT BaseClient::ConnectToLobby(ConnectInfo * pci) // pci is NULL if relogging in
  1043. {
  1044. HRESULT hr = S_OK;
  1045. // review: in the event of retry logon after logon failure
  1046. // we need to treat this as a reconnect and dump first connection...
  1047. // we should see about reusing the connection
  1048. if (pci)
  1049. {
  1050. if (m_ci.pZoneTicket)
  1051. HeapFree(GetProcessHeap(), 0, m_ci.pZoneTicket);
  1052. m_ci = *pci;
  1053. }
  1054. // but we always use config-specified lobby server. Is this too restrictive to derived clients?
  1055. m_ci.strServer = GetIsZoneClub() ? GetCfgInfo().strClubLobby : GetCfgInfo().strPublicLobby;
  1056. assert(IFF(m_ci.cbZoneTicket > 0, m_ci.pZoneTicket));
  1057. // TODO: Remove this when we are ready to enforce CD Keys
  1058. if (m_strCDKey.IsEmpty())
  1059. m_strCDKey = ZString(m_ci.szName).ToUpper();
  1060. if (m_fmLobby.IsConnected())
  1061. return S_OK;
  1062. assert(m_fLoggedOnToLobby == false);
  1063. TMapListWrapper<DWORD, MissionInfo*>::Iterator iterMissions(m_mapMissions);
  1064. while (!iterMissions.End())
  1065. {
  1066. delete iterMissions.Value();
  1067. iterMissions.Next();
  1068. }
  1069. m_mapMissions.SetEmpty();
  1070. hr = m_fmLobby.JoinSession(GetIsZoneClub() ? FEDLOBBYCLIENTS_GUID : FEDFREELOBBYCLIENTS_GUID, m_ci.strServer, m_ci.szName);
  1071. assert(IFF(m_fmLobby.IsConnected(), SUCCEEDED(hr)));
  1072. if (m_fmLobby.IsConnected())
  1073. {
  1074. DWORD dwTime = Time::Now().clock();
  1075. int crcFileList = (g_bCheckFiles ? 0 : FileCRC("FileList.txt", NULL));
  1076. char * szEncryptionKey = (char *)_alloca(strlen(CL_LOGON_KEY) + 30);
  1077. wsprintf(szEncryptionKey, CL_LOGON_KEY, dwTime, m_ci.szName);
  1078. // Let's formally announce ourselves to the server
  1079. BEGIN_PFM_CREATE(m_fmLobby, pfmLogon, C, LOGON_LOBBY)
  1080. FM_VAR_PARM(m_ci.pZoneTicket, m_ci.cbZoneTicket)
  1081. FM_VAR_PARM(PCC(m_strCDKey.Scramble(szEncryptionKey)), CB_ZTS)
  1082. END_PFM_CREATE
  1083. pfmLogon->verLobby = LOBBYVER;
  1084. pfmLogon->crcFileList = crcFileList;
  1085. pfmLogon->dwTime = dwTime;
  1086. lstrcpy(pfmLogon->szName, m_ci.szName);
  1087. // do art update--see ConnectToServer
  1088. debugf("Logging on to lobby \"%s\"...\n",
  1089. m_ci.strServer.IsEmpty() ? "" : (LPCSTR)m_ci.strServer);
  1090. lstrcpy(m_szLobbyCharName, m_ci.szName);
  1091. SendLobbyMessages();
  1092. }
  1093. retailf("$$MSRGuard:Set:UserName=%s\n", m_szLobbyCharName);
  1094. m_cUnansweredPings = 0;
  1095. m_serverOffsetValidF = false;
  1096. return hr;
  1097. }
  1098. HRESULT BaseClient::ConnectToClub(ConnectInfo * pci) // pci is NULL if relogging in
  1099. {
  1100. HRESULT hr = S_OK;
  1101. if (m_fmClub.IsConnected())
  1102. return S_OK;
  1103. // review: in the event of retry logon after logon failure
  1104. // we need to treat this as a reconnect and dump first connection...
  1105. // we should see about reusing the connection
  1106. if (pci)
  1107. {
  1108. if (m_ci.pZoneTicket)
  1109. HeapFree(GetProcessHeap(), 0, m_ci.pZoneTicket);
  1110. m_ci = *pci;
  1111. }
  1112. assert(IFF(m_ci.cbZoneTicket > 0, m_ci.pZoneTicket));
  1113. if (m_fmClub.IsConnected())
  1114. return S_OK;
  1115. assert(m_fLoggedOnToClub == false);
  1116. hr = m_fmClub.JoinSession(FEDCLUB_GUID, m_ci.strServer, m_ci.szName);
  1117. assert(IFF(m_fmClub.IsConnected(), SUCCEEDED(hr)));
  1118. if (m_fmClub.IsConnected())
  1119. {
  1120. // Let's formally announce ourselves to the server
  1121. BEGIN_PFM_CREATE(m_fmClub, pfmLogon, C, LOGON_CLUB)
  1122. FM_VAR_PARM(pci->szName, CB_ZTS)
  1123. FM_VAR_PARM(m_ci.pZoneTicket, m_ci.cbZoneTicket)
  1124. END_PFM_CREATE
  1125. pfmLogon->verClub = ALLCLUBVER;
  1126. debugf("Logging on to Club Server...\n");
  1127. lstrcpy(m_szClubCharName, pci->szName);
  1128. SendClubMessages();
  1129. }
  1130. m_cUnansweredPings = 0;
  1131. m_serverOffsetValidF = false;
  1132. return hr;
  1133. }
  1134. void BaseClient::FindStandaloneServersByName(const char* szName, TList<TRef<LANServerInfo> >& listResults)
  1135. {
  1136. listResults.SetEmpty();
  1137. m_plistFindServerResults = &listResults;
  1138. m_fm.EnumSessions(FEDSRV_STANDALONE_PRIVATE_GUID, szName);
  1139. m_plistFindServerResults = NULL;
  1140. };
  1141. void BaseClient::SetCDKey(const ZString& strCDKey)
  1142. {
  1143. m_strCDKey = strCDKey.ToUpper();
  1144. }
  1145. void BaseClient::HandleAutoDownload(DWORD dwTimeAlloted)
  1146. {
  1147. if (m_pAutoDownload)
  1148. {
  1149. m_pAutoDownload->HandleAutoDownload(dwTimeAlloted);
  1150. }
  1151. }
  1152. IweaponIGC* BaseClient::GetWeapon(void)
  1153. {
  1154. return (IweaponIGC*)m_ship->GetMountedPart(ET_Weapon, m_selectedWeapon);
  1155. }
  1156. void BaseClient::SetAutoPilot(bool autoPilot)
  1157. {
  1158. m_ship->SetAutopilot(autoPilot);
  1159. }
  1160. void BaseClient::DisconnectLobby()
  1161. {
  1162. m_fLoggedOnToLobby = false;
  1163. if (m_fmLobby.IsConnected())
  1164. m_fmLobby.Shutdown();
  1165. }
  1166. void BaseClient::DisconnectClub()
  1167. {
  1168. m_fLoggedOnToClub = false;
  1169. if (m_fmClub.IsConnected())
  1170. m_fmClub.Shutdown();
  1171. m_szClubCharName[0] = '\0';
  1172. }
  1173. void BaseClient::Disconnect(void)
  1174. {
  1175. if (m_fLoggedOn)
  1176. {
  1177. SetMessageType(c_mtGuaranteed);
  1178. BEGIN_PFM_CREATE(m_fm, pfmLogoff, CS, LOGOFF)
  1179. END_PFM_CREATE
  1180. SendMessages();
  1181. // let's just spin for a few seconds handling messages directly from here, and wait until we either see our logoff
  1182. // come back (m_fLoggedOn = false) or until we get sick of waiting. This will ensure that the server cleanly kills
  1183. // this player. Obviously the ui will be hung during this time.
  1184. // Ok, that was a nice idea, but entering the receive loop re-entrantly was not safe, so we're just going to wait,
  1185. // and hope that the message gets out. The only risk here is that if the server doesn't get the logoff, it will think
  1186. // the player was dropped instead of cleanly logged off.
  1187. Sleep(500);
  1188. }
  1189. if (m_fm.IsConnected())
  1190. {
  1191. m_fm.Shutdown();
  1192. m_fLoggedOn = false;
  1193. }
  1194. m_szCharName[0] = '\0';
  1195. m_szIGCStaticFile[0] = '\0';
  1196. }
  1197. void BaseClient::SetPlayerInfo(PlayerInfo* p)
  1198. {
  1199. assert (p);
  1200. m_pPlayerInfo = p;
  1201. //Set the fields that are derived from the player's info
  1202. m_ship->SetObjectID(p->ShipID());
  1203. m_ship->SetName(p->CharacterName());
  1204. assert (p->SideID() == NA);
  1205. assert (m_ship->GetSide() == NULL);
  1206. }
  1207. void BaseClient::SetViewCluster(IclusterIGC* pcluster, const Vector* pposition)
  1208. {
  1209. m_viewCluster = pcluster;
  1210. }
  1211. void BaseClient::ResetShip(void)
  1212. {
  1213. assert (GetSide());
  1214. //Lose all of the parts
  1215. {
  1216. const PartListIGC* plist = m_ship->GetParts();
  1217. PartLinkIGC* plink;
  1218. while (plink = plist->first()) //Not ==
  1219. {
  1220. plink->data()->Terminate();
  1221. }
  1222. }
  1223. m_ship->SetBaseHullType(GetSide()->GetCivilization()->GetLifepod());
  1224. //Do not set these before setting the hull (since we might not have has a hull and that would
  1225. //trigger the 0 mass assert).
  1226. m_ship->SetAmmo(0);
  1227. m_ship->SetFuel(0.0f);
  1228. m_pmodelServerTarget = NULL;
  1229. }
  1230. void BaseClient::ResetClusterScanners(IsideIGC* pside)
  1231. {
  1232. SideID sid = pside->GetObjectID();
  1233. //Because the player joined a side the scanner information for each "our" side is not correct
  1234. //fix that problem
  1235. {
  1236. //Stations ...
  1237. for (StationLinkIGC* l = pside->GetStations()->first();
  1238. (l != NULL);
  1239. l = l->next())
  1240. {
  1241. IstationIGC* pstation = l->data();
  1242. pstation->GetCluster()->GetClusterSite()->AddScanner(sid, pstation);
  1243. }
  1244. }
  1245. {
  1246. //Probes ...
  1247. }
  1248. }
  1249. void BaseClient::BuyLoadout(IshipIGC* pshipLoadout, bool bLaunch)
  1250. {
  1251. assert(pshipLoadout);
  1252. if (m_fm.IsConnected ())
  1253. {
  1254. SetMessageType(c_mtGuaranteed);
  1255. BEGIN_PFM_CREATE(m_fm, pfmBuyLoadout, C, BUY_LOADOUT)
  1256. FM_VAR_PARM(NULL, pshipLoadout->ExportShipLoadout(NULL))
  1257. END_PFM_CREATE
  1258. assert (pshipLoadout->GetBaseHullType());
  1259. pshipLoadout->ExportShipLoadout((ShipLoadout*)(FM_VAR_REF(pfmBuyLoadout, loadout)));
  1260. pfmBuyLoadout->fLaunch = bLaunch;
  1261. if (bLaunch)
  1262. StartLockDown("Preparing ship for launch....", lockdownLoadout);
  1263. else
  1264. StartLockDown("Loading out ship....", lockdownLoadout);
  1265. }
  1266. else
  1267. {
  1268. // I'm training here
  1269. short size = pshipLoadout->ExportShipLoadout(NULL);
  1270. void* ptr = new char[size];
  1271. pshipLoadout->ExportShipLoadout (static_cast <ShipLoadout*> (ptr));
  1272. m_ship->PurchaseShipLoadout (size, static_cast <ShipLoadout*> (ptr));
  1273. m_pClientEventSource->OnPurchaseCompleted (true);
  1274. delete ptr;
  1275. }
  1276. }
  1277. IshipIGC* BaseClient::CopyCurrentShip(void)
  1278. {
  1279. assert (m_ship);
  1280. TRef<IshipIGC> pshipLoadout = CreateEmptyShip();
  1281. //Copy the hull
  1282. pshipLoadout->SetBaseHullType(m_ship->GetBaseHullType());
  1283. //Copy the parts
  1284. for (PartLinkIGC* ppl = m_ship->GetParts()->first(); (ppl != NULL); ppl = ppl->next())
  1285. {
  1286. IpartIGC* ppart = ppl->data();
  1287. pshipLoadout->CreateAndAddPart(ppart->GetPartType(), ppart->GetMountID(), ppart->GetAmount());
  1288. }
  1289. return pshipLoadout;
  1290. }
  1291. IshipIGC* BaseClient::CreateEmptyShip(ShipID sid)
  1292. {
  1293. DataShipIGC ds;
  1294. ds.hullID = NA;
  1295. ds.shipID = sid;
  1296. ds.nParts = 0;
  1297. ds.sideID = SIDE_TEAMLOBBY;
  1298. ds.name[0] = '\0';
  1299. //ds.wingID = 0;
  1300. ds.pilotType = c_ptCheatPlayer;
  1301. ds.abmOrders = 0;
  1302. ds.nDeaths = 0;
  1303. ds.nEjections = 0;
  1304. ds.nKills = 0;
  1305. ds.baseObjectID = NA;
  1306. IshipIGC* pship = (IshipIGC*)(m_pCoreIGC->CreateObject(m_lastSend, OT_ship, &ds, sizeof(ds)));
  1307. //pship->SetSide(GetCore()->GetSide(SIDE_TEAMLOBBY));
  1308. return pship;
  1309. }
  1310. void BaseClient::SetMoney(Money m)
  1311. {
  1312. assert(m >= 0);
  1313. // XXX still a hack until I work out exactly how to get the player info set up
  1314. if (m_pPlayerInfo)
  1315. {
  1316. m_pPlayerInfo->SetMoney(m);
  1317. m_pClientEventSource->OnMoneyChange(m_pPlayerInfo);
  1318. }
  1319. if (m_pMissionInfo)
  1320. {
  1321. SideInfo* psideinfo = m_pMissionInfo->GetSideInfo(GetSideID());
  1322. if (psideinfo)
  1323. {
  1324. psideinfo->GetMembers().GetSink()();
  1325. }
  1326. }
  1327. }
  1328. bool BaseClient::SendUpdate(Time now)
  1329. {
  1330. bool fSent = false;
  1331. if (flyingF() && m_fm.IsConnected())
  1332. {
  1333. if (m_ship->GetParentShip() == NULL)
  1334. {
  1335. static const int smSendUpdateNow = weaponsMaskIGC | selectedWeaponMaskIGC |
  1336. coastButtonIGC |
  1337. backwardButtonIGC | forwardButtonIGC |
  1338. leftButtonIGC | rightButtonIGC |
  1339. upButtonIGC | downButtonIGC |
  1340. afterburnerButtonIGC;
  1341. //int stateM = m_ship->GetStateM();
  1342. if ((now >= m_lastSend + c_dsecUpdateClient) /* || ((m_oldStateM ^ stateM) & smSendUpdateNow) */)
  1343. {
  1344. ZAssert(m_ship->GetPosition().LengthSquared());
  1345. m_lastSend = now;
  1346. /*
  1347. ** We only "own" one object which needs to be updated -- the player's ship --
  1348. ** so just send an update for it.
  1349. */
  1350. SetMessageType(c_mtNonGuaranteed);
  1351. BEGIN_PFM_CREATE(m_fm, pfmShip, C, SHIP_UPDATE)
  1352. END_PFM_CREATE
  1353. m_ship->ExportShipUpdate(&(pfmShip->shipupdate));
  1354. pfmShip->cookie = m_cookie;
  1355. pfmShip->timeUpdate = ServerTimeFromClientTime(m_ship->GetLastUpdate());
  1356. //m_oldStateM = stateM;
  1357. fSent = true;
  1358. }
  1359. }
  1360. else
  1361. {
  1362. static const int smSendUpdateNow = weaponsMaskIGC;
  1363. //int stateM = m_ship->GetStateM();
  1364. if ((now >= m_lastSend + c_dsecUpdateClient) /* || ((m_oldStateM ^ stateM) & smSendUpdateNow)*/)
  1365. {
  1366. m_lastSend = now;
  1367. /*
  1368. ** We only "own" one object which needs to be updated -- the player's ship --
  1369. ** so just send an update for it.
  1370. */
  1371. SetMessageType(c_mtNonGuaranteed);
  1372. int stateM = m_ship->GetStateM();
  1373. if (stateM & weaponsMaskIGC)
  1374. {
  1375. //Turret is shooting ... facing matters
  1376. BEGIN_PFM_CREATE(m_fm, pfmTurret, C, ACTIVE_TURRET_UPDATE)
  1377. END_PFM_CREATE
  1378. m_ship->ExportShipUpdate(&(pfmTurret->atu));
  1379. pfmTurret->timeUpdate = ServerTimeFromClientTime(m_ship->GetLastUpdate());
  1380. }
  1381. else
  1382. {
  1383. //Turret is not shooting ... facing doesn't matter so don't bother to send it
  1384. BEGIN_PFM_CREATE(m_fm, pfmTurret, C, INACTIVE_TURRET_UPDATE)
  1385. END_PFM_CREATE
  1386. }
  1387. //m_oldStateM = stateM;
  1388. fSent = true;
  1389. }
  1390. }
  1391. }
  1392. return(fSent);
  1393. }
  1394. void BaseClient::SetMessageType(MessageType messageType)
  1395. {
  1396. // If the message type was None, there shldn't be anything in the message outbox
  1397. // CURTC: I didn't write this, and I don't like it :-)
  1398. assert(IMPLIES(m_messageType == c_mtNone, m_fm.CbUsedSpaceInOutbox() == 0));
  1399. if (messageType != m_messageType)
  1400. {
  1401. SendMessages();
  1402. m_messageType = messageType;
  1403. }
  1404. }
  1405. void BaseClient::SendMessages(void)
  1406. {
  1407. // Message type should be none iff there is nothing in the outbox
  1408. assert (IMPLIES((m_messageType == c_mtNone), (m_fm.CbUsedSpaceInOutbox() == 0)));
  1409. if (m_messageType != c_mtNone)
  1410. {
  1411. ZSucceeded(m_fm.SendMessages(m_fm.GetServerConnection(),
  1412. m_messageType == c_mtGuaranteed ? FM_GUARANTEED : FM_NOT_GUARANTEED,
  1413. FM_FLUSH));
  1414. m_messageType = c_mtNone;
  1415. }
  1416. }
  1417. void BaseClient::NextWeapon(void)
  1418. {
  1419. assert (m_ship);
  1420. if (m_ship->GetParentShip() == NULL)
  1421. {
  1422. Mount nHardpoints = m_ship->GetHullType()->GetMaxFixedWeapons();
  1423. if (nHardpoints > 0)
  1424. {
  1425. Mount startWeapon = m_selectedWeapon;
  1426. IpartIGC* p;
  1427. do
  1428. {
  1429. m_selectedWeapon = (m_selectedWeapon + 1) % nHardpoints;
  1430. p = m_ship->GetMountedPart(ET_Weapon, m_selectedWeapon);
  1431. }
  1432. while ((p == NULL) && (m_selectedWeapon != startWeapon));
  1433. }
  1434. }
  1435. }
  1436. void BaseClient::PreviousWeapon(void)
  1437. {
  1438. assert (m_ship);
  1439. if (m_ship->GetParentShip() == NULL)
  1440. {
  1441. Mount nHardpoints = m_ship->GetHullType()->GetMaxFixedWeapons();
  1442. if (nHardpoints > 0)
  1443. {
  1444. Mount startWeapon = m_selectedWeapon;
  1445. IpartIGC* p;
  1446. do
  1447. {
  1448. m_selectedWeapon =
  1449. m_selectedWeapon ? (m_selectedWeapon - 1) : nHardpoints - 1;
  1450. p = m_ship->GetMountedPart(ET_Weapon, m_selectedWeapon);
  1451. }
  1452. while ((p == NULL) && (m_selectedWeapon != startWeapon));
  1453. }
  1454. }
  1455. }
  1456. void BaseClient::SetSelectedWeapon(Mount id)
  1457. {
  1458. assert (m_ship);
  1459. if (m_ship->GetParentShip() == NULL)
  1460. {
  1461. Mount nHardpoints = m_ship->GetHullType()->GetMaxFixedWeapons();
  1462. if (id < nHardpoints && m_ship->GetMountedPart(ET_Weapon, id))
  1463. {
  1464. m_selectedWeapon = id;
  1465. }
  1466. }
  1467. }
  1468. void BaseClient::JoinMission(MissionInfo * pMission, const char* szMissionPassword)
  1469. {
  1470. assert (pMission);
  1471. BEGIN_PFM_CREATE(m_fmLobby, pfmJoinGameReq, C, JOIN_GAME_REQ)
  1472. END_PFM_CREATE
  1473. pfmJoinGameReq->dwCookie = pMission->GetCookie();
  1474. SendLobbyMessages();
  1475. m_dwCookieToJoin = pMission->GetCookie();
  1476. assert(strlen(szMissionPassword) < c_cbGamePassword);
  1477. strncpy(m_strPasswordToJoin, szMissionPassword, c_cbGamePassword);
  1478. m_strPasswordToJoin[c_cbGamePassword - 1] = '\0';
  1479. // waiting for FM_L_JOIN_GAME_ACK. When we get that we can join it
  1480. }
  1481. void BaseClient::CreateMissionReq()
  1482. {
  1483. //No need to set message type for non m_fm messages
  1484. BEGIN_PFM_CREATE(m_fmLobby, pfmCreateMission, C, CREATE_MISSION_REQ)
  1485. END_PFM_CREATE
  1486. SendLobbyMessages();
  1487. // now we wait for FM_L_CREATE_MISSION_ACK
  1488. }
  1489. #ifdef markcu
  1490. const int g_cPingsTimeout = int(unsigned(1<<31)-1); // close to infinity as possible for int
  1491. #else
  1492. const int g_cPingsTimeout = 5;
  1493. #endif
  1494. const DWORD g_sDeadTime = 30; // 30 seconds
  1495. void BaseClient::CheckServerLag(Time now)
  1496. {
  1497. const float maxLagCheck = 6.0f;
  1498. const float minLagCheck = 1.0f;
  1499. if (m_fm.IsConnected())
  1500. {
  1501. //if (0 == m_timeLastPing.clock()) // start the ping clock once we get a ship
  1502. //m_timeLastPing = now;
  1503. if (m_lastLagCheck + maxLagCheck <= now)
  1504. {
  1505. // debugf("DPlay latency=%dms\n", m_fm.GetLatency(m_fm.GetServerConnection())); -- this doesn't report accurate values
  1506. // if we haven't received x pings in a row and it's been at least g_sDeadTime since the last message, we're a goner
  1507. if (m_cUnansweredPings > g_cPingsTimeout && (now - m_timeLastServerMessage) >= g_sDeadTime)
  1508. {
  1509. // what should that number be?
  1510. OnSessionLost("Pings aren't coming back.", &m_fm);
  1511. }
  1512. else
  1513. {
  1514. m_lastLagCheck = now;
  1515. // make sure we get an update on the server offset even if the
  1516. // connection is poor.
  1517. bool bGuaranteed = m_cUnansweredPings > g_cPingsTimeout;
  1518. SetMessageType(bGuaranteed ? c_mtGuaranteed : c_mtNonGuaranteed);
  1519. BEGIN_PFM_CREATE(m_fm, pfmPing, CS, PING)
  1520. END_PFM_CREATE
  1521. pfmPing->fmg = bGuaranteed ? FM_GUARANTEED : FM_NOT_GUARANTEED;
  1522. pfmPing->timeClient = Time::Now();
  1523. SendMessages();
  1524. m_cUnansweredPings++;
  1525. }
  1526. }
  1527. }
  1528. }
  1529. HRESULT BaseClient::ReceiveMessages(void)
  1530. {
  1531. HRESULT hr;
  1532. if (m_fm.IsConnected())
  1533. hr = m_fm.ReceiveMessages();
  1534. else
  1535. hr = S_OK;
  1536. if (m_fmLobby.IsConnected())
  1537. {
  1538. if (FAILED(hr)) // preserve fail sign
  1539. m_fmLobby.ReceiveMessages();
  1540. else
  1541. hr = m_fmLobby.ReceiveMessages();
  1542. }
  1543. if (m_fmClub.IsConnected())
  1544. {
  1545. if (FAILED(hr)) // preserve fail sign
  1546. m_fmClub.ReceiveMessages();
  1547. else
  1548. hr = m_fmClub.ReceiveMessages();
  1549. }
  1550. return(hr);
  1551. }
  1552. HRESULT BaseClient::OnSessionLost(char * szReason, FedMessaging * pthis)
  1553. {
  1554. debugf("OnSessionLost. %s. lastUpdate=%d now=%d Time::Now()=%d\n",
  1555. szReason, m_lastUpdate.clock(), m_now.clock(), Time::Now().clock());
  1556. m_fLoggedOn = false; // we don't want to try and log off, cause the link is dead
  1557. if (pthis == &m_fm)
  1558. Disconnect();
  1559. else if (pthis == &m_fmClub)
  1560. DisconnectClub();
  1561. else if (pthis == &m_fmLobby)
  1562. DisconnectLobby();
  1563. else
  1564. assert(false);
  1565. return(S_OK);
  1566. }
  1567. void BaseClient::OnMessageNAK(FedMessaging * pthis, DWORD dwTime, CFMRecipient * prcp)
  1568. {
  1569. // we can't recover from this, so...
  1570. OnSessionLost("guaranteed message couldn't be delivered", pthis);
  1571. }
  1572. HRESULT BaseClient::OnSessionLost(FedMessaging * pthis)
  1573. {
  1574. return OnSessionLost("DPlay session terminated", pthis);
  1575. }
  1576. void BaseClient::OnSessionFound(FedMessaging * pthis, FMSessionDesc * pSessionDesc)
  1577. {
  1578. m_plistFindServerResults->PushEnd(
  1579. new LANServerInfo(pSessionDesc->GetInstance(), pSessionDesc->GetGameName(),
  1580. pSessionDesc->GetNumPlayers(), pSessionDesc->GetMaxPlayers())
  1581. );
  1582. }
  1583. void BaseClient::ReceiveChat(IshipIGC* pshipSender,
  1584. ChatTarget ctRecipient,
  1585. ObjectID oidRecipient,
  1586. SoundID voiceOver,
  1587. const char* pszText,
  1588. CommandID cid,
  1589. ObjectType otTarget,
  1590. ObjectID oidTarget,
  1591. ImodelIGC* pmodelTarget,
  1592. bool bObjectModel)
  1593. {
  1594. m_pClientEventSource->OnNewChatMessage();
  1595. }
  1596. /*
  1597. void BaseClient::OfflineCommandToDrone (const ChatData* pcd, const DataBuoyIGC* pdb, CFSShip* pfsSender, IshipIGC* pshipTo, ImodelIGC** ppmodelTarget)
  1598. {
  1599. assert (pfsSender);
  1600. IsideIGC* pside = pshipTo->GetSide();
  1601. if ((pcd->commandID != c_cidNone) &&
  1602. (pfsSender->GetIGCShip()->GetSide() == pside))
  1603. {
  1604. if (*ppmodelTarget == NULL)
  1605. {
  1606. if (pdb)
  1607. {
  1608. //Create a buoy for this chat message
  1609. *ppmodelTarget = (ImodelIGC*)(pfsMission->GetIGCMission()->CreateObject(g.timeNow, OT_buoy, pdb, sizeof(*pdb)));
  1610. assert (*ppmodelTarget);
  1611. ((IbuoyIGC*)*ppmodelTarget)->AddConsumer();
  1612. }
  1613. else
  1614. {
  1615. *ppmodelTarget = pfsMission->GetIGCMission()->GetModel(otTarget, oidTarget);
  1616. }
  1617. }
  1618. CommandID cid = pcd->commandID;
  1619. if ((cid == c_cidDefault) && (*ppmodelTarget != NULL))
  1620. {
  1621. cid = c_cidGoto;
  1622. PilotType pt = pshipTo->GetPilotType();
  1623. switch (pt)
  1624. {
  1625. case c_ptMiner:
  1626. case c_ptBuilder:
  1627. {
  1628. if (((*ppmodelTarget)->GetObjectType() == OT_asteroid) &&
  1629. ((IasteroidIGC*)(*ppmodelTarget))->HasCapability(pshipTo->GetOrdersABM()))
  1630. {
  1631. cid = (pt == c_ptMiner) ? c_cidMine : c_cidBuild;
  1632. }
  1633. }
  1634. break;
  1635. case c_ptLayer:
  1636. {
  1637. if ((*ppmodelTarget)->GetObjectType() == OT_buoy)
  1638. {
  1639. cid = c_cidBuild;
  1640. }
  1641. }
  1642. break;
  1643. case c_ptWingman:
  1644. {
  1645. IsideIGC* psideTarget = (*ppmodelTarget)->GetSide();
  1646. if (psideTarget == pside)
  1647. cid = c_cidDefend;
  1648. else
  1649. cid = c_cidAttack;
  1650. }
  1651. }
  1652. }
  1653. if (pshipTo->LegalCommand(cid, *ppmodelTarget))
  1654. {
  1655. pfsMission->GetSite()->SendChat(pshipTo, CHAT_INDIVIDUAL, pfsSender->GetIGCShip()->GetObjectID(),
  1656. voAffirmativeSound, "Affirmative.");
  1657. if (cid == c_cidJoin)
  1658. {
  1659. //Join the targets wing
  1660. assert ((*ppmodelTarget)->GetObjectType() == OT_ship);
  1661. WingID wid = ((IshipIGC*)*ppmodelTarget)->GetWingID();
  1662. pshipTo->SetWingID(wid);
  1663. BEGIN_PFM_CREATE(g.fm, pfmWingChange, CS, SET_WINGID)
  1664. END_PFM_CREATE
  1665. pfmWingChange->shipID = pshipTo->GetObjectID();
  1666. pfmWingChange->wingID = wid;
  1667. g.fm.SendMessages(CFSSide::FromIGC(pside)->GetGroup(), FM_GUARANTEED, FM_FLUSH);
  1668. }
  1669. else
  1670. {
  1671. //Set both current and accepted commands
  1672. pshipTo->SetCommand(c_cmdCurrent,
  1673. *ppmodelTarget,
  1674. cid);
  1675. pshipTo->SetCommand(c_cmdAccepted,
  1676. *ppmodelTarget,
  1677. cid);
  1678. }
  1679. }
  1680. else
  1681. {
  1682. pfsMission->GetSite()->SendChat(pshipTo, CHAT_INDIVIDUAL, pfsSender->GetIGCShip()->GetObjectID(),
  1683. voNegativeSound, "Negative.");
  1684. }
  1685. }
  1686. }
  1687. */
  1688. bool BaseClient::ParseShellCommand(const char* pszCommand)
  1689. {
  1690. bool bCommand = false;
  1691. if (_strnicmp(pszCommand, "!mute ", 6) == 0)
  1692. {
  1693. PlayerInfo* ppi = FindPlayer(pszCommand + 6);
  1694. if (ppi)
  1695. ppi->SetMute(true);
  1696. bCommand = true;
  1697. }
  1698. else if (_strnicmp(pszCommand, "!unmute ", 8) == 0)
  1699. {
  1700. PlayerInfo* ppi = FindPlayer(pszCommand + 8);
  1701. if (ppi)
  1702. ppi->SetMute(false);
  1703. bCommand = true;
  1704. }
  1705. #ifdef _DEBUG
  1706. else if (_strnicmp(pszCommand, "!chatcountdown ", 8) == 0)
  1707. {
  1708. int i = atoi(pszCommand + strlen("!chatcountdown "));
  1709. char cbTemp[20];
  1710. for (; i > 0; i--)
  1711. {
  1712. SendChat(m_ship, CHAT_EVERYONE, NA, NA, _itoa(i, cbTemp, 10));
  1713. // send the messages every so often so that we don't overflow the
  1714. // send buffer
  1715. if (i % 16 == 0)
  1716. SendMessages();
  1717. }
  1718. bCommand = true;
  1719. }
  1720. #endif
  1721. return bCommand;
  1722. }
  1723. void BaseClient::SendChat(IshipIGC* pshipSender,
  1724. ChatTarget ctRecipient,
  1725. ObjectID oidRecipient,
  1726. SoundID soundID,
  1727. const char* pszText,
  1728. CommandID cid,
  1729. ObjectType otTarget,
  1730. ObjectID oidTarget,
  1731. ImodelIGC* pmodelTarget,
  1732. bool bObjectModel)
  1733. {
  1734. if (oidRecipient == NA)
  1735. {
  1736. //Convert from NA to an actual ID based on the player's ship
  1737. switch (ctRecipient)
  1738. {
  1739. case CHAT_INDIVIDUAL:
  1740. oidRecipient = m_ship->GetObjectID();
  1741. break;
  1742. case CHAT_WING:
  1743. oidRecipient = m_ship->GetWingID();
  1744. break;
  1745. case CHAT_TEAM:
  1746. oidRecipient = m_ship->GetSide()->GetObjectID();
  1747. break;
  1748. case CHAT_ALL_SECTOR:
  1749. case CHAT_FRIENDLY_SECTOR:
  1750. {
  1751. IclusterIGC* pcluster = GetChatCluster();
  1752. if (pcluster)
  1753. oidRecipient = pcluster->GetObjectID();
  1754. else
  1755. {
  1756. oidRecipient = m_ship->GetSide()->GetObjectID();
  1757. ctRecipient = CHAT_TEAM;
  1758. }
  1759. }
  1760. break;
  1761. }
  1762. }
  1763. if ((ctRecipient == CHAT_INDIVIDUAL) || (ctRecipient == CHAT_GROUP) || (ctRecipient == CHAT_GROUP_NOECHO))
  1764. {
  1765. IshipIGC* pship = m_pCoreIGC->GetShip(oidRecipient);
  1766. if ((pship == NULL) || (pship->GetMission() == NULL))
  1767. return; //Don't bother to send to the dead
  1768. // do the right thing for training missions
  1769. PilotType pilotType = pship->GetPilotType();
  1770. if ((!m_fm.IsConnected ()) && (pilotType < c_ptPlayer) && (cid != c_cidNone))
  1771. {
  1772. if ((cid == c_cidDefault) && (pmodelTarget != NULL))
  1773. {
  1774. cid = c_cidGoto;
  1775. if (ctRecipient == CHAT_INDIVIDUAL)
  1776. {
  1777. switch (pilotType)
  1778. {
  1779. case c_ptMiner:
  1780. case c_ptBuilder:
  1781. {
  1782. if ((pmodelTarget->GetObjectType() == OT_asteroid) && ((IasteroidIGC*) pmodelTarget)->HasCapability(pship->GetOrdersABM()))
  1783. {
  1784. cid = (pilotType == c_ptMiner) ? c_cidMine : c_cidBuild;
  1785. }
  1786. }
  1787. break;
  1788. case c_ptLayer:
  1789. {
  1790. if (pmodelTarget->GetObjectType() == OT_buoy)
  1791. {
  1792. cid = c_cidBuild;
  1793. }
  1794. }
  1795. break;
  1796. case c_ptWingman:
  1797. {
  1798. if (pmodelTarget->GetSide() == pship->GetSide ())
  1799. cid = c_cidDefend;
  1800. else
  1801. cid = c_cidAttack;
  1802. }
  1803. }
  1804. }
  1805. }
  1806. pship->SetCommand (c_cmdCurrent, pmodelTarget, cid);
  1807. pship->SetCommand (c_cmdAccepted, pmodelTarget, cid);
  1808. //ReceiveChat (m_ship, ctRecipient, oidRecipient, soundID, pszText, cid, otTarget, oidTarget, pmodelTarget);
  1809. }
  1810. }
  1811. //Don't bother sending messages to the server if there is no server or the message it to ourself
  1812. if (m_fm.IsConnected() &&
  1813. (ctRecipient != CHAT_NOSELECTION) &&
  1814. ((ctRecipient != CHAT_INDIVIDUAL) ||
  1815. (oidRecipient != m_ship->GetObjectID())))
  1816. {
  1817. SetMessageType(BaseClient::c_mtGuaranteed);
  1818. ChatData* pcd;
  1819. if (otTarget == OT_buoy)
  1820. {
  1821. BEGIN_PFM_CREATE(m_fm, pfmChatBuoy, CS, CHATBUOY)
  1822. FM_VAR_PARM(pszText, CB_ZTS)
  1823. END_PFM_CREATE
  1824. pcd = &(pfmChatBuoy->cd);
  1825. assert (pmodelTarget);
  1826. pmodelTarget->Export(&(pfmChatBuoy->db));
  1827. }
  1828. else
  1829. {
  1830. BEGIN_PFM_CREATE(m_fm, pfmChatMessage, CS, CHATMESSAGE)
  1831. FM_VAR_PARM(pszText, CB_ZTS)
  1832. END_PFM_CREATE
  1833. pcd = &(pfmChatMessage->cd);
  1834. pfmChatMessage->otTarget = otTarget;
  1835. pfmChatMessage->oidTarget = oidTarget;
  1836. }
  1837. //Don't bother to set the sender
  1838. pcd->chatTarget = ctRecipient;
  1839. pcd->oidRecipient = oidRecipient;
  1840. pcd->commandID = cid;
  1841. pcd->voiceOver = soundID;
  1842. pcd->bObjectModel = bObjectModel;
  1843. }
  1844. //We always get chats we send
  1845. if (ctRecipient != CHAT_GROUP_NOECHO)
  1846. {
  1847. ReceiveChat(pshipSender,
  1848. ctRecipient, oidRecipient,
  1849. soundID, pszText,
  1850. cid, otTarget, oidTarget, pmodelTarget, bObjectModel);
  1851. }
  1852. }
  1853. void BaseClient::ScrollChatUp(void)
  1854. {
  1855. if (m_plinkSelectedChat == NULL)
  1856. m_plinkSelectedChat = m_chatList.last();
  1857. else if (m_plinkSelectedChat != m_chatList.first())
  1858. m_plinkSelectedChat = m_plinkSelectedChat->txen();
  1859. }
  1860. void BaseClient::ScrollChatDown(void)
  1861. {
  1862. if (m_plinkSelectedChat != NULL)
  1863. m_plinkSelectedChat = m_plinkSelectedChat->next();
  1864. }
  1865. ChatInfo* BaseClient::GetCurrentMessage(void)
  1866. {
  1867. ChatLink* l = (m_plinkSelectedChat != NULL)
  1868. ? m_plinkSelectedChat
  1869. : m_chatList.last();
  1870. return l ? &(l->data()) : NULL;
  1871. }
  1872. BallotInfo* BaseClient::GetCurrentBallot()
  1873. {
  1874. Time now = Time::Now();
  1875. // remove any expired ballots before the first valid ballot
  1876. while (!m_listBallots.IsEmpty() && m_listBallots.GetFront().GetBallotExpirationTime() < now)
  1877. m_listBallots.PopFront();
  1878. if (m_listBallots.IsEmpty())
  1879. return NULL;
  1880. else
  1881. return &(m_listBallots.GetFront());
  1882. }
  1883. void BaseClient::Vote(bool bAgree)
  1884. {
  1885. if (!m_listBallots.IsEmpty())
  1886. {
  1887. // compose a vote message
  1888. SetMessageType(c_mtGuaranteed);
  1889. BEGIN_PFM_CREATE(m_fm, pfmVote, C, VOTE)
  1890. END_PFM_CREATE
  1891. pfmVote->ballotID = m_listBallots.GetFront().GetBallotID();
  1892. pfmVote->bAgree = bAgree;
  1893. m_listBallots.PopFront();
  1894. }
  1895. }
  1896. void BaseClient::SkipCurrentBallot()
  1897. {
  1898. if (!m_listBallots.IsEmpty())
  1899. {
  1900. m_listBallots.PopFront();
  1901. }
  1902. }
  1903. Money BaseClient::GetBucketStatus(StationID stationID, short iBucket)
  1904. {
  1905. TRef<BucketStatusArray> prgStatus;
  1906. if (m_mapBucketStatusArray.Find(stationID, prgStatus))
  1907. return (*prgStatus)[iBucket];
  1908. else
  1909. return 0;
  1910. }
  1911. Money BaseClient::AddMoneyToBucket(IbucketIGC* b,
  1912. Money m)
  1913. {
  1914. assert (b);
  1915. Money spent = b->AddMoney(m);
  1916. SetMoney(GetMoney() - spent);
  1917. if (m_fm.IsConnected())
  1918. {
  1919. SetMessageType(c_mtGuaranteed);
  1920. BEGIN_PFM_CREATE(m_fm, pfmBD, C, BUCKET_DONATE)
  1921. END_PFM_CREATE
  1922. pfmBD->moneyGiven = spent;
  1923. pfmBD->iBucket = b->GetObjectID();
  1924. }
  1925. return spent;
  1926. }
  1927. void BaseClient::DonateMoney(PlayerInfo* pPlayerInfo, Money money)
  1928. {
  1929. if (m_fm.IsConnected())
  1930. {
  1931. SetMessageType(c_mtGuaranteed);
  1932. BEGIN_PFM_CREATE(m_fm, pfmDonate, C, PLAYER_DONATE)
  1933. END_PFM_CREATE
  1934. pfmDonate->moneyGiven = money;
  1935. pfmDonate->shipID = pPlayerInfo->ShipID();
  1936. }
  1937. // the recipients money changes
  1938. pPlayerInfo->SetMoney(pPlayerInfo->GetMoney() + money);
  1939. m_pClientEventSource->OnMoneyChange(pPlayerInfo);
  1940. // and my money changes
  1941. SetMoney(GetMoney() - money);
  1942. }
  1943. MissionInfo* BaseClient::GetLobbyMission(DWORD dwCookie)
  1944. {
  1945. MissionInfo* pMissionInfo = NULL;
  1946. if (!m_mapMissions.Find(dwCookie, pMissionInfo))
  1947. {
  1948. pMissionInfo = new MissionInfo(dwCookie);
  1949. m_mapMissions.Set(dwCookie, pMissionInfo);
  1950. }
  1951. return pMissionInfo;
  1952. }
  1953. PlayerLink* BaseClient::FindPlayerLink(ShipID shipID)
  1954. {
  1955. PlayerLink* l = m_listPlayers.first();
  1956. while ((l != NULL) && (l->data().ShipID() != shipID))
  1957. l = l->next();
  1958. return l;
  1959. }
  1960. PlayerInfo* BaseClient::FindAndCreatePlayerLink(ShipID shipID)
  1961. {
  1962. PlayerLink* l = FindPlayerLink(shipID);
  1963. if (l == NULL)
  1964. {
  1965. l = new PlayerLink;
  1966. m_listPlayers.last(l);
  1967. }
  1968. return &(l->data ());
  1969. }
  1970. PlayerInfo* BaseClient::FindPlayer(ShipID shipID)
  1971. {
  1972. PlayerLink* l = FindPlayerLink(shipID);
  1973. return l ? &(l->data()) : NULL;
  1974. }
  1975. PlayerInfo* BaseClient::FindPlayer(const char* szName)
  1976. {
  1977. PlayerInfo* p = NULL;
  1978. for (PlayerLink* l = m_listPlayers.first(); (l != NULL); l = l->next())
  1979. {
  1980. if (_stricmp(l->data().CharacterName(), szName) == 0)
  1981. {
  1982. p = &(l->data());
  1983. break;
  1984. }
  1985. }
  1986. return p;
  1987. }
  1988. PlayerInfo* BaseClient::FindPlayerByPrefix(const char* szNamePrefix)
  1989. {
  1990. PlayerInfo* p = NULL;
  1991. int lenNamePrefix = strlen(szNamePrefix);
  1992. for (PlayerLink* l = m_listPlayers.first(); (l != NULL); l = l->next())
  1993. {
  1994. PlayerInfo& player = l->data();
  1995. if (_strnicmp(player.CharacterName(), szNamePrefix, lenNamePrefix) == 0)
  1996. {
  1997. p = &player;
  1998. break;
  1999. }
  2000. }
  2001. return p;
  2002. }
  2003. namespace {
  2004. // remove trailing whitespaces from a string
  2005. void RemoveTrailingSpaces(char* sz)
  2006. {
  2007. for (int nIndex = strlen(sz); nIndex > 0 && sz[nIndex - 1] == ' '; --nIndex)
  2008. {
  2009. sz[nIndex - 1] = '\0';
  2010. }
  2011. };
  2012. }
  2013. ZString BaseClient::LookupRankName(RankID rank, CivID civ)
  2014. {
  2015. const char* szRankNameTemplate = "Unknown (%d)";
  2016. int nClosestRank = -1;
  2017. if (m_cRankInfo <= 0 || !GetIsZoneClub())
  2018. {
  2019. assert(!m_fm.IsConnected() || !GetIsZoneClub());
  2020. szRankNameTemplate = "";
  2021. }
  2022. else
  2023. {
  2024. // 'slow', but probably still fast enough
  2025. for (int iEntry = 0; iEntry < m_cRankInfo; iEntry++)
  2026. {
  2027. if (m_vRankInfo[iEntry].civ == civ
  2028. && m_vRankInfo[iEntry].rank <= rank
  2029. && m_vRankInfo[iEntry].rank >= nClosestRank)
  2030. {
  2031. szRankNameTemplate = m_vRankInfo[iEntry].RankName;
  2032. nClosestRank = m_vRankInfo[iEntry].rank;
  2033. }
  2034. }
  2035. assert(nClosestRank >= 0);
  2036. }
  2037. char cbTemp[c_cbName + 8];
  2038. wsprintf(cbTemp, szRankNameTemplate, rank - nClosestRank + 1);
  2039. RemoveTrailingSpaces(cbTemp);
  2040. return cbTemp;
  2041. }
  2042. List* BaseClient::GetMissionList()
  2043. {
  2044. return m_plistMissions;
  2045. }
  2046. void BaseClient::QuitMission()
  2047. {
  2048. // Create and queue the message to the server
  2049. BaseClient::SetMessageType(c_mtGuaranteed);
  2050. BEGIN_PFM_CREATE(*GetNetwork(), pfmQuitMission, CS, QUIT_MISSION)
  2051. END_PFM_CREATE
  2052. pfmQuitMission->shipID = GetShipID();
  2053. pfmQuitMission->reason = QSR_Quit;
  2054. }
  2055. bool BaseClient::MyMissionInProgress()
  2056. {
  2057. if (m_pMissionInfo)
  2058. return m_pMissionInfo->InProgress();
  2059. else
  2060. return false;
  2061. }
  2062. void BaseClient::SaveSquadMemberships(const char* szCharacterName)
  2063. {
  2064. }
  2065. void BaseClient::RestoreSquadMemberships(const char* szCharacterName)
  2066. {
  2067. }
  2068. bool BaseClient::HasPlayerSquad(MissionInfo* pMission)
  2069. {
  2070. for (TList<SquadMembership>::Iterator iterSquad(m_squadmemberships);
  2071. !iterSquad.End(); iterSquad.Next())
  2072. {
  2073. if (pMission->HasSquad(iterSquad.Value().GetID()))
  2074. return true;
  2075. }
  2076. return false;
  2077. }
  2078. void BaseClient::SendAllMissions(IClientEventSink* pSink)
  2079. {
  2080. TMapListWrapper<DWORD, MissionInfo*>::Iterator iterMissions(m_mapMissions);
  2081. MissionInfo* pMissionInfo;
  2082. while (!iterMissions.End())
  2083. {
  2084. pMissionInfo = iterMissions.Value();
  2085. pSink->OnAddMission(pMissionInfo);
  2086. iterMissions.Next();
  2087. }
  2088. }
  2089. void BaseClient::SendAllPlayers(IClientEventSink* pSink, MissionInfo* pMissionInfo, SideID sideID)
  2090. {
  2091. if (pMissionInfo)
  2092. {
  2093. ShipList::Iterator iterPlayers(pMissionInfo->GetSideInfo(sideID)->GetMembers());
  2094. PlayerInfo* pPlayerInfo;
  2095. ShipID shipID;
  2096. while (!iterPlayers.End())
  2097. {
  2098. shipID = iterPlayers.Value();
  2099. if (pPlayerInfo = FindPlayer(shipID))
  2100. pSink->OnAddPlayer(pMissionInfo, sideID, pPlayerInfo);
  2101. iterPlayers.Next();
  2102. }
  2103. }
  2104. }
  2105. void BaseClient::SendAllRequests(IClientEventSink* pSink, MissionInfo* pMissionInfo, SideID sideID)
  2106. {
  2107. ShipList::Iterator iterRequests(pMissionInfo->GetSideInfo(sideID)->GetRequests());
  2108. PlayerInfo* pPlayerInfo;
  2109. ShipID shipID;
  2110. while (!iterRequests.End())
  2111. {
  2112. shipID = iterRequests.Value();
  2113. if (pPlayerInfo = FindPlayer(shipID))
  2114. pSink->OnAddRequest(pMissionInfo, sideID, pPlayerInfo);
  2115. iterRequests.Next();
  2116. }
  2117. }
  2118. //
  2119. // IgcSite implementation.
  2120. //
  2121. void BaseClient::TerminateModelEvent(ImodelIGC* pModel)
  2122. {
  2123. ObjectType type = pModel->GetObjectType();
  2124. if (type != OT_projectile) //can never target projectiles
  2125. {
  2126. //Clean up any references to the model
  2127. {
  2128. bool fChanges = false;
  2129. for (ChatLink* l = m_chatList.first();
  2130. (l != NULL);
  2131. l = l->next())
  2132. {
  2133. ChatInfo& cw = l->data();
  2134. if (cw.GetTarget() == pModel)
  2135. {
  2136. cw.ClearTarget();
  2137. fChanges = true;
  2138. }
  2139. }
  2140. if (fChanges)
  2141. m_pClientEventSource->OnChatMessageChange();
  2142. }
  2143. for (Command i = 0; (i < c_cmdMax); i++)
  2144. {
  2145. if (m_ship->GetCommandTarget(i) == pModel)
  2146. m_ship->SetCommand(i, NULL, c_cidNone);
  2147. }
  2148. if (pModel == m_pmodelServerTarget)
  2149. m_pmodelServerTarget = NULL;
  2150. }
  2151. m_pClientEventSource->OnModelTerminated(pModel);
  2152. }
  2153. void BaseClient::LoadoutChangeEvent(IshipIGC* pship, IpartIGC* ppart, LoadoutChange lc)
  2154. {
  2155. if (m_ship->GetSourceShip() == pship)
  2156. m_pClientEventSource->OnLoadoutChanged(ppart, lc);
  2157. }
  2158. void BaseClient::BucketChangeEvent(BucketChange bc, IbucketIGC* b)
  2159. {
  2160. m_pClientEventSource->OnBucketChange(bc, b);
  2161. }
  2162. void BaseClient::SideBuildingTechChange(IsideIGC* s)
  2163. {
  2164. SideID sid = s->GetObjectID();
  2165. m_pClientEventSource->OnTechTreeChanged(sid);
  2166. }
  2167. void BaseClient::SideDevelopmentTechChange(IsideIGC* s)
  2168. {
  2169. SideID sid = s->GetObjectID();
  2170. m_pClientEventSource->OnTechTreeChanged(sid);
  2171. }
  2172. void BaseClient::StationTypeChange(IstationIGC* s)
  2173. {
  2174. SideID sid = s->GetSide()->GetObjectID();
  2175. m_pClientEventSource->OnTechTreeChanged(sid);
  2176. }
  2177. static ItreasureIGC* CreateTreasure(BaseClient* pClient, Time now, IshipIGC* pship, IpartIGC* p, IpartTypeIGC* ppt, const Vector& position, float dv)
  2178. {
  2179. assert (ppt);
  2180. assert (dv > 1.0f);
  2181. assert (pship);
  2182. DataTreasureIGC dt;
  2183. dt.treasureCode = c_tcPart;
  2184. dt.treasureID = ppt->GetObjectID();
  2185. dt.objectID = pClient->m_pCoreIGC->GenerateNewTreasureID();
  2186. dt.p0 = position;
  2187. Vector direction;
  2188. {
  2189. //Get a nice random 3D direction
  2190. direction.z = random(-1.0f, 1.0f);
  2191. float yaw = random(0.0f, pi);
  2192. float cosPitch = (float)sqrt(1.0f - direction.z * direction.z);
  2193. direction.x = cos(yaw) * cosPitch;
  2194. direction.y = sin(yaw) * cosPitch;
  2195. }
  2196. float radius = pship->GetRadius() + 10.0f;
  2197. dt.p0 += radius * direction;
  2198. dt.v0 = direction * dv + pship->GetVelocity();
  2199. switch (ppt->GetEquipmentType())
  2200. {
  2201. case ET_Magazine:
  2202. case ET_Dispenser:
  2203. {
  2204. assert (p);
  2205. dt.amount = ((IlauncherIGC*)p)->GetAmount();
  2206. }
  2207. break;
  2208. case ET_Pack:
  2209. {
  2210. DataPackTypeIGC* pdp = (DataPackTypeIGC*)(ppt->GetData());
  2211. dt.amount = pdp->amount;
  2212. }
  2213. break;
  2214. default:
  2215. dt.amount = 0;
  2216. }
  2217. dt.lifespan = 600.0f;
  2218. IclusterIGC* pcluster = pship->GetCluster();
  2219. dt.clusterID = pcluster->GetObjectID();
  2220. dt.createNow = false;
  2221. dt.time0 = pClient->ServerTimeFromClientTime(now);
  2222. ItreasureIGC* t = (ItreasureIGC *)
  2223. pClient->m_pCoreIGC->CreateObject(now, OT_treasure,
  2224. &dt, sizeof(dt));
  2225. //Note: bad form releasing a pointer before we return it but we know it will
  2226. //stick around since it is in a cluster.
  2227. assert (t);
  2228. t->Release();
  2229. return t;
  2230. }
  2231. void BaseClient::KillAsteroidEvent(IasteroidIGC* pasteroid, bool explodeF)
  2232. {
  2233. if (!m_fm.IsConnected())
  2234. {
  2235. if (explodeF)
  2236. pasteroid->GetCluster()->GetClusterSite()->AddExplosion(pasteroid, c_etAsteroid);
  2237. pasteroid->Terminate();
  2238. }
  2239. }
  2240. void BaseClient::KillProbeEvent(IprobeIGC* pprobe)
  2241. {
  2242. if (!m_fm.IsConnected())
  2243. pprobe->Terminate();
  2244. }
  2245. void BaseClient::KillMineEvent(ImineIGC* pmine)
  2246. {
  2247. if (!m_fm.IsConnected())
  2248. pmine->Terminate();
  2249. }
  2250. void BaseClient::KillMissileEvent(ImissileIGC* pmissile, const Vector& position)
  2251. {
  2252. if (!m_fm.IsConnected())
  2253. {
  2254. pmissile->Explode(position);
  2255. pmissile->Terminate();
  2256. }
  2257. else
  2258. pmissile->Disarm();
  2259. }
  2260. void BaseClient::KillShipEvent(Time now, IshipIGC* pShip, ImodelIGC* pLauncher, float flAmount, const Vector& p1, const Vector& p2)
  2261. {
  2262. if (!m_fm.IsConnected())
  2263. {
  2264. // commented because these are causing performance problems after a while BSW 10/27/1999
  2265. /*
  2266. //Blow the parts into space
  2267. {
  2268. const PartListIGC* plist = pShip->GetParts();
  2269. PartLinkIGC* plink;
  2270. while (plink = plist->first()) //Not ==
  2271. {
  2272. IpartIGC* p = plink->data();
  2273. CreateTreasure(this, now, pShip, p, p->GetPartType(),
  2274. p1, 100.0f);
  2275. p->Terminate();
  2276. }
  2277. //Treasures for ammo
  2278. {
  2279. int ammo = pShip->GetAmmo();
  2280. if (ammo > 0)
  2281. {
  2282. IpartTypeIGC* pptAmmo = m_pCoreIGC->GetAmmoPack();
  2283. assert (pptAmmo);
  2284. ItreasureIGC* t = CreateTreasure(this, now, pShip, NULL, pptAmmo, p1, 100.0f);
  2285. assert (t);
  2286. int a = t->GetAmount();
  2287. assert (a > 0);
  2288. if (ammo <= a)
  2289. {
  2290. t->SetAmount((short)ammo);
  2291. }
  2292. pShip->SetAmmo(0);
  2293. }
  2294. }
  2295. //Ditto for fuel
  2296. {
  2297. int fuel = int(pShip->GetFuel());
  2298. if (fuel > 0)
  2299. {
  2300. IpartTypeIGC* pptFuel = m_pCoreIGC->GetFuelPack();
  2301. assert (pptFuel);
  2302. ItreasureIGC* t = CreateTreasure(this, now, pShip, NULL, pptFuel, p1, 100.0f);
  2303. assert (t);
  2304. int f = t->GetAmount();
  2305. assert (f > 0);
  2306. if (fuel <= f)
  2307. {
  2308. t->SetAmount((short)fuel);
  2309. }
  2310. }
  2311. pShip->SetFuel(0.0f);
  2312. }
  2313. }
  2314. */
  2315. if (pShip == m_ship->GetSourceShip())
  2316. {
  2317. //Eject the player
  2318. Vector v = pShip->GetVelocity();
  2319. pShip->SetBaseHullType(pShip->GetSide()->GetCivilization()->GetLifepod());
  2320. Vector f = Vector::RandomDirection();
  2321. Orientation o;
  2322. if (pLauncher)
  2323. {
  2324. if (pLauncher->GetCluster() == pShip->GetCluster())
  2325. {
  2326. Vector dp = (pLauncher->GetPosition() - p1).Normalize();
  2327. o.Set(dp);
  2328. f = (f + dp * 8.0f).Normalize();
  2329. }
  2330. else
  2331. {
  2332. o.Set(f);
  2333. }
  2334. }
  2335. else
  2336. {
  2337. Vector dp = (p2 - p1).Normalize();
  2338. o.Set(dp);
  2339. f = (f + dp * 8.0f).Normalize();
  2340. }
  2341. v -= f * 100.0f;
  2342. //Put a spin on the ship as it leaves the bad guys.
  2343. pShip->SetCurrentTurnRate(c_axisRoll, pi * 2.0f);
  2344. pShip->SetPosition(p1);
  2345. pShip->SetVelocity(v);
  2346. pShip->SetOrientation(o);
  2347. pShip->SetBB(now, now, 0.0f);
  2348. EjectPlayer(pLauncher);
  2349. }
  2350. else
  2351. {
  2352. PlayerInfo* pPlayerInfo = reinterpret_cast<PlayerInfo*> (pShip->GetPrivateData ());
  2353. RemovePlayerFromSide (pPlayerInfo, QSR_Quit);
  2354. RemovePlayerFromMission (pPlayerInfo, QSR_Quit);
  2355. }
  2356. }
  2357. }
  2358. void BaseClient::DamageStationEvent(IstationIGC* pStation,
  2359. ImodelIGC* pLauncher,
  2360. DamageTypeID type,
  2361. float flAmount,
  2362. float flLeakage)
  2363. {
  2364. if ((NULL != pStation->GetCluster()) &&
  2365. (pStation->GetCluster() == GetCluster()))
  2366. {
  2367. if (pStation->GetShieldFraction() < 0.0f)
  2368. this->PlaySoundEffect(otherHullHitSound, pStation);
  2369. else
  2370. this->PlaySoundEffect(otherShieldHitSound, pStation);
  2371. }
  2372. }
  2373. void BaseClient::KillStationEvent(IstationIGC* pStation,
  2374. ImodelIGC* pLauncher,
  2375. float flAmount,
  2376. float flLeakage)
  2377. {
  2378. if (!m_fm.IsConnected())
  2379. {
  2380. pStation->GetCluster()->GetClusterSite()->AddExplosion(pStation, c_etLargeStation);
  2381. pStation->Terminate();
  2382. }
  2383. }
  2384. void BaseClient::FireMissile(IshipIGC* pShip,
  2385. ImagazineIGC* pMagazine,
  2386. Time timeFired,
  2387. ImodelIGC* pTarget,
  2388. float flLock)
  2389. {
  2390. assert (pShip == m_ship);
  2391. if (pTarget &&
  2392. ((pTarget->GetCluster() != m_ship->GetCluster()) ||
  2393. !m_ship->CanSee(pTarget)))
  2394. pTarget = NULL;
  2395. this->PlaySoundEffect(pMagazine->GetMissileType()->GetLaunchSound(), pShip);
  2396. this->PlayFFEffect(effectFire);
  2397. IclusterIGC* pCluster = pShip->GetCluster();
  2398. assert (pCluster);
  2399. const Vector& myVelocity = pShip->GetVelocity();
  2400. const Orientation& myOrientation = pShip->GetOrientation();
  2401. Vector myPosition = pShip->GetPosition() +
  2402. pMagazine->GetEmissionPt() * myOrientation;
  2403. int iNumMissiles = pMagazine->GetLaunchCount ();
  2404. int iNumRemainingMissiles = pMagazine->GetAmount ();
  2405. Time lastUpdate = pShip->GetLastUpdate();
  2406. if (iNumRemainingMissiles < iNumMissiles)
  2407. iNumMissiles = iNumRemainingMissiles;
  2408. iNumRemainingMissiles -= iNumMissiles;
  2409. DataMissileIGC dataMissile;
  2410. dataMissile.pmissiletype = pMagazine->GetMissileType();
  2411. dataMissile.pLauncher = pShip;
  2412. dataMissile.pTarget = pTarget;
  2413. dataMissile.pCluster = pCluster;
  2414. dataMissile.lock = flLock;
  2415. Vector vecLaunchPosition = myPosition + (myVelocity * (timeFired - lastUpdate));
  2416. Vector vecLaunchForward = myOrientation.GetForward();
  2417. Vector vecLaunchVelocity = myVelocity;
  2418. MissileLaunchData* missileLaunchData = new MissileLaunchData[iNumMissiles];
  2419. // disperse the missiles over the launch region
  2420. float fDispersion = dataMissile.pmissiletype->GetDispersion();
  2421. float fDispersionBase = -fDispersion;
  2422. float fUniformDispersion = (2.0f * fDispersion) / (float) iNumMissiles;
  2423. float fHalfUniformDispersion = fUniformDispersion * 0.5f;
  2424. for (int i = 0; i < iNumMissiles; i++)
  2425. {
  2426. missileLaunchData[i].vecForward = vecLaunchForward;
  2427. if (fDispersion != 0.0f)
  2428. {
  2429. float fRandomDispersion = (random (-0.75f, 0.75f) * fHalfUniformDispersion) + (fDispersionBase + fHalfUniformDispersion);
  2430. fDispersionBase += fUniformDispersion;
  2431. missileLaunchData[i].vecForward += fRandomDispersion * myOrientation.GetRight();
  2432. missileLaunchData[i].vecForward += (random (-0.1f, 0.1f) * fDispersion) * myOrientation.GetUp();
  2433. missileLaunchData[i].vecForward.SetNormalize();
  2434. }
  2435. ZRetailAssert(missileLaunchData[i].vecForward * missileLaunchData[i].vecForward >= 0.95f);
  2436. ZRetailAssert(missileLaunchData[i].vecForward * missileLaunchData[i].vecForward <= 1.05f);
  2437. missileLaunchData[i].vecPosition = vecLaunchPosition + (missileLaunchData[i].vecForward * (dataMissile.pmissiletype->GetInitialSpeed() * random (0.1f, 0.2f)));
  2438. ZRetailAssert(missileLaunchData[i].vecPosition * missileLaunchData[i].vecPosition >= 0.0f);
  2439. ZRetailAssert(missileLaunchData[i].vecPosition * missileLaunchData[i].vecPosition <= 4.0e10f);
  2440. missileLaunchData[i].vecVelocity = vecLaunchVelocity + (dataMissile.pmissiletype->GetInitialSpeed() * missileLaunchData[i].vecForward);
  2441. ZRetailAssert(missileLaunchData[i].vecVelocity * missileLaunchData[i].vecVelocity >= 0.0f);
  2442. ZRetailAssert(missileLaunchData[i].vecVelocity * missileLaunchData[i].vecVelocity <= 1.0e6);
  2443. }
  2444. if (!m_fm.IsConnected())
  2445. {
  2446. for (i = 0; i < iNumMissiles; i++)
  2447. {
  2448. dataMissile.position = missileLaunchData[i].vecPosition;
  2449. dataMissile.forward = missileLaunchData[i].vecForward;
  2450. dataMissile.velocity = missileLaunchData[i].vecVelocity;
  2451. dataMissile.missileID = m_pCoreIGC->GenerateNewMissileID ();
  2452. dataMissile.bDud = false;
  2453. ImissileIGC* m = (ImissileIGC*)(m_pCoreIGC->CreateObject(timeFired, OT_missile, &dataMissile, sizeof(dataMissile)));
  2454. assert (m != NULL);
  2455. m->Release();
  2456. }
  2457. }
  2458. else
  2459. {
  2460. //Tell the server (& everyone else) to fire a missile
  2461. SetMessageType(c_mtGuaranteed);
  2462. BEGIN_PFM_CREATE(m_fm, pfmFM, CS, FIRE_MISSILE)
  2463. FM_VAR_PARM(missileLaunchData, sizeof(MissileLaunchData) * iNumMissiles)
  2464. END_PFM_CREATE
  2465. if (NULL != pTarget)
  2466. {
  2467. pfmFM->targetType = pTarget->GetObjectType();
  2468. pfmFM->targetID = pTarget->GetObjectID();
  2469. }
  2470. else
  2471. {
  2472. pfmFM->targetType = OT_invalid;
  2473. pfmFM->targetID = NA;
  2474. }
  2475. pfmFM->clusterID = pCluster->GetObjectID();
  2476. pfmFM->lock = flLock;
  2477. pfmFM->timeFired = ServerTimeFromClientTime(timeFired);
  2478. //Don't bother to set the ship, missile or missile type ID ... the server will set them
  2479. }
  2480. delete missileLaunchData;
  2481. pMagazine->SetAmount (iNumRemainingMissiles);
  2482. if (0 == iNumRemainingMissiles)
  2483. {
  2484. //
  2485. // Nothing left ... nuke it (which may also cause it to be
  2486. // released & deleted).
  2487. //
  2488. if (Reload(pShip, pMagazine, ET_Magazine))
  2489. {
  2490. PlayNotificationSound(salReloadingMissilesSound, GetShip());
  2491. PlaySoundEffect(startReloadSound, GetShip());
  2492. PostText(false, "Reloading missiles...");
  2493. }
  2494. else
  2495. {
  2496. PlayNotificationSound(salMissilesDepletedSound, GetShip());
  2497. PostText(false, "Missiles depleted.");
  2498. }
  2499. }
  2500. }
  2501. void BaseClient::FireExpendable(IshipIGC* pShip,
  2502. IdispenserIGC* pDispenser,
  2503. Time timeFired)
  2504. {
  2505. assert (pShip == m_ship);
  2506. this->PlayFFEffect(effectFire, pShip);
  2507. IclusterIGC* pCluster = pShip->GetCluster();
  2508. assert (pCluster);
  2509. IexpendableTypeIGC* pet = pDispenser->GetExpendableType();
  2510. ObjectType type = pet->GetObjectType();
  2511. switch (type)
  2512. {
  2513. case OT_chaffType:
  2514. this->PlaySoundEffect(deployChaffSound, pShip);
  2515. break;
  2516. case OT_mineType:
  2517. this->PlaySoundEffect(deployMineSound, pShip);
  2518. break;
  2519. case OT_probeType:
  2520. this->PlaySoundEffect(deployProbeSound, pShip);
  2521. break;
  2522. }
  2523. if (!m_fm.IsConnected())
  2524. {
  2525. const Vector& myPosition = pShip->GetPosition();
  2526. const Vector& myVelocity = pShip->GetVelocity();
  2527. if (type == OT_chaffType)
  2528. {
  2529. assert (type == OT_chaffType);
  2530. //Drop the chaff "behind" the player's ship
  2531. DataChaffIGC dataChaff;
  2532. dataChaff.time0 = timeFired;
  2533. dataChaff.p0 = myPosition;
  2534. dataChaff.v0 = myVelocity + pShip->GetOrientation().GetUp() * 25.0f;
  2535. dataChaff.pcluster = pCluster;
  2536. dataChaff.pchafftype = (IchaffTypeIGC*)pet;
  2537. IchaffIGC* c = (IchaffIGC*)(m_pCoreIGC->CreateObject(timeFired,
  2538. OT_chaff,
  2539. &dataChaff,
  2540. sizeof(dataChaff)));
  2541. assert (c != NULL);
  2542. c->Release();
  2543. }
  2544. else
  2545. {
  2546. float speed2 = myVelocity.LengthSquared();
  2547. float offset = (pet->GetRadius() + pShip->GetRadius()) + 5.0f;
  2548. Vector displace = (speed2 < 1.0f)
  2549. ? (pShip->GetOrientation().GetBackward() * offset)
  2550. : (myVelocity * (-offset / float(sqrt(speed2))));
  2551. Vector launchPosition = myPosition + displace;
  2552. if (type == OT_mineType)
  2553. {
  2554. DataMineIGC dataMine;
  2555. dataMine.pminetype = (ImineTypeIGC*)pet;
  2556. dataMine.time0 = timeFired + 3.0f; //And the count shall be 3.
  2557. dataMine.p0 = launchPosition;
  2558. dataMine.exportF = false;
  2559. dataMine.pshipLauncher = pShip;
  2560. dataMine.pcluster = pCluster;
  2561. dataMine.mineID = m_pCoreIGC->GenerateNewMineID();
  2562. ImineIGC* m = (ImineIGC*)(m_pCoreIGC->CreateObject(timeFired,
  2563. OT_mine,
  2564. &dataMine,
  2565. sizeof(dataMine)));
  2566. assert (m != NULL);
  2567. m->Release();
  2568. }
  2569. else if (type == OT_probeType)
  2570. {
  2571. //Drop the probe "behind" the player's ship
  2572. DataProbeIGC dataProbe;
  2573. dataProbe.pprobetype = (IprobeTypeIGC*)pet;
  2574. dataProbe.time0 = timeFired;
  2575. dataProbe.p0 = launchPosition;
  2576. dataProbe.exportF = false;
  2577. dataProbe.pcluster = pCluster;
  2578. dataProbe.pside = pShip->GetSide();
  2579. dataProbe.pship = pShip;
  2580. dataProbe.pmodelTarget = pShip->GetCommandTarget(c_cmdCurrent);
  2581. dataProbe.probeID = m_pCoreIGC->GenerateNewProbeID();
  2582. IprobeIGC* p = (IprobeIGC*)(m_pCoreIGC->CreateObject(timeFired,
  2583. OT_probe,
  2584. &dataProbe,
  2585. sizeof(dataProbe)));
  2586. assert (p != NULL);
  2587. p->Release();
  2588. }
  2589. }
  2590. }
  2591. else
  2592. {
  2593. //Tell the server to fire an expendable
  2594. SetMessageType(c_mtGuaranteed);
  2595. BEGIN_PFM_CREATE(m_fm, pfmFE, C, FIRE_EXPENDABLE)
  2596. END_PFM_CREATE
  2597. pfmFE->et = pet->GetEquipmentType();
  2598. }
  2599. short amount = pDispenser->GetAmount() - 1;
  2600. pDispenser->SetAmount(amount);
  2601. if (0 == amount)
  2602. {
  2603. //
  2604. // Nothing left ... nuke it (which may also cause it to be
  2605. // released & deleted).
  2606. //
  2607. if (Reload(pShip, pDispenser, pet->GetEquipmentType()))
  2608. {
  2609. switch (pet->GetObjectType())
  2610. {
  2611. case OT_chaffType:
  2612. PlayNotificationSound(salReloadingChaffSound, GetShip());
  2613. PostText(false, "Reloading chaff...");
  2614. break;
  2615. default:
  2616. PlayNotificationSound(salReloadingDispenserSound, GetShip());
  2617. PostText(false, "Reloading dispenser...");
  2618. break;
  2619. }
  2620. PlaySoundEffect(startReloadSound, GetShip());
  2621. }
  2622. else
  2623. {
  2624. switch (pet->GetObjectType())
  2625. {
  2626. case OT_chaffType:
  2627. PlayNotificationSound(salChaffDepletedSound, GetShip());
  2628. PostText(false, "Chaff depleted.");
  2629. break;
  2630. default:
  2631. PlayNotificationSound(salDispenserEmptySound, GetShip());
  2632. PostText(false, "Dispenser empty.");
  2633. break;
  2634. }
  2635. }
  2636. }
  2637. }
  2638. bool BaseClient::Reload(IshipIGC* pship, IlauncherIGC* plauncher, EquipmentType type)
  2639. {
  2640. int nReloads = 0;
  2641. if (pship == m_ship)
  2642. {
  2643. const int c_MaxReloads = 8;
  2644. ReloadData reloads[c_MaxReloads];
  2645. const IhullTypeIGC* pht = pship->GetHullType();
  2646. switch (type)
  2647. {
  2648. case ET_Weapon:
  2649. case ET_Afterburner:
  2650. {
  2651. PackType ptDesired;
  2652. short ammo;
  2653. short maxAmmo;
  2654. if (type == ET_Weapon)
  2655. {
  2656. ptDesired = c_packAmmo;
  2657. ammo = pship->GetAmmo();
  2658. maxAmmo = pht->GetMaxAmmo();
  2659. }
  2660. else
  2661. {
  2662. ptDesired = c_packFuel;
  2663. ammo = short(pship->GetFuel());
  2664. maxAmmo = short(pht->GetMaxFuel());
  2665. }
  2666. while (ammo < maxAmmo)
  2667. {
  2668. /*
  2669. //Find the ammo/fuel pack with the least amount of ammo/fuel in it (first if all the same)
  2670. IpackIGC* ppack = NULL;
  2671. short amount = 0x7fff;
  2672. {
  2673. for (Mount i = -1; (i >= -c_maxCargo); i--)
  2674. {
  2675. IpartIGC* ppart = pship->GetMountedPart(NA, i);
  2676. if (ppart && (ppart->GetObjectType() == OT_pack))
  2677. {
  2678. IpackIGC* p = (IpackIGC*)ppart;
  2679. if (p->GetPackType() == ptDesired)
  2680. {
  2681. short a = p->GetAmount();
  2682. if (a < amount)
  2683. {
  2684. ppack = p;
  2685. amount = a;
  2686. }
  2687. }
  2688. }
  2689. }
  2690. }
  2691. */
  2692. // Find the last ammo/fuel pack (avoids mysterious behavior in inventory pane)
  2693. IpackIGC* ppack = NULL;
  2694. short amount = 0x7fff;
  2695. {
  2696. for (Mount i = -1; (i >= -c_maxCargo); i--)
  2697. {
  2698. IpartIGC* ppart = pship->GetMountedPart(NA, i);
  2699. if (ppart && (ppart->GetObjectType() == OT_pack))
  2700. {
  2701. IpackIGC* p = (IpackIGC*)ppart;
  2702. if (p->GetPackType() == ptDesired)
  2703. {
  2704. ppack = p;
  2705. amount = p->GetAmount();
  2706. }
  2707. }
  2708. }
  2709. }
  2710. if (ppack)
  2711. {
  2712. reloads[nReloads].mount = ppack->GetMountID();
  2713. short transfer;
  2714. if (ammo + amount > maxAmmo)
  2715. {
  2716. //There will be stuff left in the pack
  2717. transfer = maxAmmo - ammo;
  2718. ammo = maxAmmo;
  2719. ppack->SetAmount(amount - transfer);
  2720. }
  2721. else
  2722. {
  2723. //Entire pack gets trasnfered
  2724. transfer = NA;
  2725. ammo += amount;
  2726. ppack->Terminate();
  2727. }
  2728. reloads[nReloads++].amountTransfered = transfer;
  2729. }
  2730. else
  2731. break;
  2732. }
  2733. if (nReloads != 0)
  2734. {
  2735. if (type == ET_Weapon)
  2736. {
  2737. pship->SetAmmo(ammo);
  2738. //disable all mounted weapons that use ammo
  2739. Mount maxWeapons = pht->GetMaxWeapons();
  2740. for (Mount i = 0; (i < maxWeapons); i++)
  2741. {
  2742. IweaponIGC* pw = (IweaponIGC*)(pship->GetMountedPart(ET_Weapon, i));
  2743. if (pw && (pw->GetAmmoPerShot() != 0))
  2744. pw->SetMountedFraction(0.0f);
  2745. }
  2746. }
  2747. else
  2748. {
  2749. pship->SetFuel(float(ammo));
  2750. //disable the afterburner
  2751. IpartIGC* p = pship->GetMountedPart(ET_Afterburner, 0);
  2752. if (p)
  2753. p->SetMountedFraction(0.0f);
  2754. }
  2755. }
  2756. }
  2757. break;
  2758. case ET_Magazine:
  2759. case ET_Dispenser:
  2760. case ET_ChaffLauncher:
  2761. {
  2762. IpartTypeIGC* pparttype;
  2763. short ammo;
  2764. short maxAmmo;
  2765. if (plauncher)
  2766. {
  2767. pparttype = plauncher->GetPartType();
  2768. ammo = plauncher->GetAmount();
  2769. maxAmmo = ((IlauncherTypeIGC*)pparttype)->GetAmount(pship);
  2770. }
  2771. else
  2772. {
  2773. pparttype = NULL;
  2774. ammo = 0;
  2775. maxAmmo = 0x7fff;
  2776. }
  2777. while (ammo < maxAmmo)
  2778. {
  2779. IlauncherIGC* plauncherReload = NULL;
  2780. IpartTypeIGC* pptReload;
  2781. short amount = 0x7fff;
  2782. {
  2783. for (Mount mount = -1; (mount >= -c_maxCargo); mount--)
  2784. {
  2785. IpartIGC* p = pship->GetMountedPart(NA, mount);
  2786. if (p && (p->GetEquipmentType() == type))
  2787. {
  2788. IpartTypeIGC* ppt = p->GetPartType();
  2789. if (pht->CanMount(ppt, 0))
  2790. {
  2791. short a = ((IlauncherIGC*)p)->GetAmount();
  2792. assert (a > 0);
  2793. if (pparttype == NULL)
  2794. {
  2795. //when one has nothing ... one will pick anything
  2796. reloads[nReloads].amountTransfered = NA;
  2797. reloads[nReloads++].mount = mount;
  2798. if (plauncher)
  2799. plauncher->Terminate();
  2800. p->SetMountID(0);
  2801. plauncher = (IlauncherIGC*)p;
  2802. pparttype = plauncher->GetPartType();
  2803. ammo = plauncher->GetAmount();
  2804. assert (ammo > 0);
  2805. maxAmmo = ((IlauncherTypeIGC*)pparttype)->GetAmount(pship);
  2806. assert (plauncherReload == NULL);
  2807. if (ammo == maxAmmo)
  2808. break; //No point in continuing if we are full up
  2809. }
  2810. else if ((ppt == pparttype) /*&& (a < amount)*/)
  2811. {
  2812. plauncherReload = (IlauncherIGC*)p;
  2813. pptReload = ppt;
  2814. amount = a;
  2815. }
  2816. }
  2817. }
  2818. }
  2819. }
  2820. if (plauncherReload)
  2821. {
  2822. reloads[nReloads].mount = plauncherReload->GetMountID();
  2823. short transfer;
  2824. if (ammo + amount <= maxAmmo)
  2825. {
  2826. transfer = amount;
  2827. plauncherReload->Terminate();
  2828. }
  2829. else
  2830. {
  2831. transfer = maxAmmo - ammo;
  2832. plauncherReload->SetAmount(amount - transfer);
  2833. }
  2834. ammo += transfer;
  2835. plauncher->SetAmount(ammo);
  2836. plauncher->SetMountedFraction(0.0f);
  2837. plauncher->ResetTimeLoaded();
  2838. reloads[nReloads++].amountTransfered = transfer;
  2839. }
  2840. else if (pparttype && (ammo == 0))
  2841. {
  2842. //Couldn't find a way to reload the existing launcher
  2843. //try again being less picky
  2844. assert (plauncher);
  2845. pparttype = NULL;
  2846. maxAmmo = 0x7fff;
  2847. }
  2848. else
  2849. break;
  2850. }
  2851. }
  2852. break;
  2853. }
  2854. if (m_fm.IsConnected())
  2855. {
  2856. if (nReloads > 0)
  2857. {
  2858. int size = sizeof(ReloadData) * nReloads;
  2859. SetMessageType(c_mtGuaranteed);
  2860. BEGIN_PFM_CREATE(m_fm, pfmReload, CS, RELOAD)
  2861. FM_VAR_PARM(NULL, size)
  2862. END_PFM_CREATE
  2863. memcpy(FM_VAR_REF(pfmReload, rgReloads), reloads, size);
  2864. }
  2865. else if (plauncher && (plauncher->GetAmount() == 0))
  2866. {
  2867. SetMessageType(c_mtGuaranteed);
  2868. BEGIN_PFM_CREATE(m_fm, pfmDrop, CS, DROP_PART)
  2869. END_PFM_CREATE
  2870. pfmDrop->et = plauncher->GetEquipmentType();
  2871. pfmDrop->mount = plauncher->GetMountID();
  2872. plauncher->Terminate();
  2873. }
  2874. }
  2875. }
  2876. return nReloads > 0;
  2877. }
  2878. void BaseClient::OnQuitSide()
  2879. {
  2880. m_bInGame = false;
  2881. m_bWaitingForGameRestart = false;
  2882. if (IsLockedDown())
  2883. EndLockDown(lockdownDonating | lockdownLoadout | lockdownTeleporting);
  2884. // clear any team chats, in case they join another team
  2885. ChatLink* lChat = m_chatList.first();
  2886. while (lChat != NULL)
  2887. {
  2888. ChatLink* lChatPrev = lChat;
  2889. lChat = lChat->next();
  2890. if (lChatPrev->data().GetChatTarget() != CHAT_EVERYONE)
  2891. delete lChatPrev;
  2892. }
  2893. // nuke any saved player status information
  2894. // (in case they rejoin this game on a different side)
  2895. PlayerLink* l = m_listPlayers.first();
  2896. while (l != NULL)
  2897. {
  2898. PlayerInfo& playerCurrent = l->data();
  2899. playerCurrent.ResetShipStatus();
  2900. l = l->next();
  2901. }
  2902. assert (m_ship->GetStation() == NULL);
  2903. assert (m_ship->GetCluster() == NULL);
  2904. {
  2905. // set all ships to the null sector
  2906. ShipLinkIGC* plinkShip = m_pCoreIGC->GetShips()->first();
  2907. while (plinkShip)
  2908. {
  2909. plinkShip->data()->SetCluster(NULL);
  2910. plinkShip = plinkShip->next();
  2911. }
  2912. }
  2913. // nuke all clusters
  2914. SetViewCluster(NULL);
  2915. m_pCoreIGC->ResetMission();
  2916. // nuke any pending votes
  2917. m_listBallots.SetEmpty();
  2918. };
  2919. void BaseClient::OnJoinSide()
  2920. {
  2921. ResetShip();
  2922. m_strBriefingText.SetEmpty();
  2923. m_bGenerateCivBriefing = false;
  2924. if (m_pPlayerInfo->SideID() != SIDE_TEAMLOBBY)
  2925. SetMoney(m_pPlayerInfo->GetMoney());
  2926. if (m_pMissionInfo->InProgress())
  2927. {
  2928. m_pCoreIGC->SetMissionStage(STAGE_STARTED);
  2929. }
  2930. m_pClientEventSource->OnChatMessageChange();
  2931. m_plinkSelectedChat = NULL;
  2932. // if this is the lobby side, we need to manually send the OnAddPlayer message
  2933. if (m_pPlayerInfo->SideID() == SIDE_TEAMLOBBY)
  2934. m_pClientEventSource->OnAddPlayer(m_pMissionInfo, m_pPlayerInfo->SideID(), m_pPlayerInfo);
  2935. };
  2936. void BaseClient::OnQuitMission(QuitSideReason reason, const char* szMessageParam)
  2937. {
  2938. Disconnect();
  2939. // clear chat messages
  2940. m_chatList.purge(true);
  2941. m_pClientEventSource->OnClearChat();
  2942. m_ship->AddRef();
  2943. {
  2944. // destroy all ships
  2945. ShipLinkIGC* plinkShip = m_pCoreIGC->GetShips()->first();
  2946. while (plinkShip)
  2947. {
  2948. ShipLinkIGC* lNext = plinkShip->next();
  2949. IshipIGC* s = plinkShip->data();
  2950. plinkShip->data()->Terminate();
  2951. plinkShip = lNext;
  2952. }
  2953. }
  2954. m_ship->Release();
  2955. CreateDummyShip();
  2956. {
  2957. // and sides
  2958. const SideListIGC * pSides = m_pCoreIGC->GetSides();
  2959. SideLinkIGC * pSidelink = NULL;
  2960. while (pSidelink = pSides->first())
  2961. pSidelink->data()->Terminate();
  2962. }
  2963. {
  2964. // and players...
  2965. m_listPlayers.purge(true);
  2966. m_pPlayerInfo = NULL;
  2967. }
  2968. delete m_pMissionInfo;
  2969. m_pMissionInfo = NULL;
  2970. };
  2971. void BaseClient::OnEnterGame()
  2972. {
  2973. m_pCoreIGC->EnterGame();
  2974. m_bInGame = true;
  2975. m_pClientEventSource->OnEnterMission();
  2976. };
  2977. void BaseClient::CreateDummyShip()
  2978. {
  2979. // create a (no-hull) ship for the player
  2980. DataShipIGC ds;
  2981. ds.hullID = NA;
  2982. ds.shipID = NA;
  2983. ds.nParts = 0;
  2984. ds.sideID = NA;
  2985. ds.name[0] = '\0';
  2986. //ds.wingID = 0;
  2987. ds.pilotType = c_ptCheatPlayer;
  2988. ds.abmOrders = 0;
  2989. ds.nDeaths = 0;
  2990. ds.nEjections = 0;
  2991. ds.nKills = 0;
  2992. ds.baseObjectID = NA;
  2993. // Allow the ship data to be modified by derived classes
  2994. ModifyShipData(&ds);
  2995. m_ship = (IshipIGC*)(m_pCoreIGC->CreateObject(m_lastSend, OT_ship, &ds, sizeof(ds)));
  2996. assert (m_ship);
  2997. m_ship->Release();
  2998. };
  2999. void BaseClient::DestroyDummyShip()
  3000. {
  3001. // Base class does nothing
  3002. }
  3003. void BaseClient::RemovePlayerFromSide(PlayerInfo* pPlayerInfo, QuitSideReason reason, const char* szMessageParam)
  3004. {
  3005. if (!pPlayerInfo)
  3006. {
  3007. assert(false);
  3008. return;
  3009. }
  3010. ZAssert(pPlayerInfo->SideID() != SIDE_TEAMLOBBY && pPlayerInfo->SideID() != NA);
  3011. SideID sideOld = pPlayerInfo->SideID();
  3012. ZAssert(pPlayerInfo->SideID() != NA);
  3013. pPlayerInfo->SetReady(true);
  3014. pPlayerInfo->SetTeamLeader(false);
  3015. pPlayerInfo->SetMissionOwner(false);
  3016. pPlayerInfo->Reset(false);
  3017. if (STAGE_STARTED != m_pMissionInfo->GetStage())
  3018. pPlayerInfo->GetShip()->SetExperience(1.0f);
  3019. m_pClientEventSource->OnMoneyChange(pPlayerInfo);
  3020. m_pMissionInfo->RemovePlayer(pPlayerInfo);
  3021. m_pClientEventSource->OnDelPlayer(m_pMissionInfo, pPlayerInfo->SideID(), pPlayerInfo, reason);
  3022. IshipIGC* pship = m_pCoreIGC->GetShip(pPlayerInfo->ShipID());
  3023. pship->SetSide(NULL);
  3024. if (pPlayerInfo == m_pPlayerInfo)
  3025. {
  3026. OnQuitSide();
  3027. }
  3028. else
  3029. {
  3030. if (pPlayerInfo->IsHuman() && (m_pMissionInfo->GetStage() == STAGE_STARTED) && (m_fm.IsConnected ()))
  3031. {
  3032. ZString msg;
  3033. // tell the player that someone has just quit their team
  3034. if (m_pPlayerInfo->SideID() == sideOld)
  3035. {
  3036. switch (reason)
  3037. {
  3038. case QSR_LeaderBooted:
  3039. msg = pPlayerInfo->CharacterName() + ZString(" was booted from the team by the team leader.");
  3040. ReceiveChat(NULL, CHAT_TEAM, NA, salQuitSound, msg, c_cidNone, NA, NA);
  3041. break;
  3042. case QSR_OwnerBooted:
  3043. msg = pPlayerInfo->CharacterName() + ZString(" was booted from the game by the mission owner.");
  3044. ReceiveChat(NULL, CHAT_TEAM, NA, salQuitSound, msg, c_cidNone, NA, NA);
  3045. break;
  3046. case QSR_AdminBooted:
  3047. msg = pPlayerInfo->CharacterName() + ZString(" was booted from the team by an admin.");
  3048. ReceiveChat(NULL, CHAT_TEAM, NA, salQuitSound, msg, c_cidNone, NA, NA);
  3049. break;
  3050. case QSR_ServerShutdown:
  3051. break;
  3052. case QSR_SquadChange:
  3053. msg = pPlayerInfo->CharacterName() + ZString(" was booted because of a squad change.");
  3054. ReceiveChat(NULL, CHAT_TEAM, NA, salQuitSound, msg, c_cidNone, NA, NA);
  3055. break;
  3056. case QSR_SideDestroyed:
  3057. break;
  3058. case QSR_RankLimits:
  3059. msg = pPlayerInfo->CharacterName() + ZString(" was booted because he/she did not meet the new rank limits.");
  3060. ReceiveChat(NULL, CHAT_TEAM, NA, salQuitSound, msg, c_cidNone, NA, NA);
  3061. break;
  3062. case QSR_TeamSizeLimits:
  3063. msg = pPlayerInfo->CharacterName() + ZString(" was booted to reduce the team size within it's new limits.");
  3064. ReceiveChat(NULL, CHAT_TEAM, NA, salQuitSound, msg, c_cidNone, NA, NA);
  3065. break;
  3066. case QSR_Quit:
  3067. msg = pPlayerInfo->CharacterName() + ZString(" has quit.");
  3068. ReceiveChat(NULL, CHAT_TEAM, NA, salQuitSound, msg, c_cidNone, NA, NA);
  3069. break;
  3070. case QSR_SwitchingSides:
  3071. msg = pPlayerInfo->CharacterName() + ZString(" is switching sides.");
  3072. ReceiveChat(NULL, CHAT_TEAM, NA, salChangeSidesSound, msg, c_cidNone, NA, NA);
  3073. break;
  3074. case QSR_DuplicateLocalLogon:
  3075. case QSR_DuplicateCDKey:
  3076. // don't send a chat in case they were dropped.
  3077. //msg = pPlayerInfo->CharacterName() + ZString(" has been booted due to a duplicate logon.");
  3078. //ReceiveChat(NULL, CHAT_TEAM, NA, salQuitSound, msg, c_cidNone, NA, NA);
  3079. break;
  3080. case QSR_RandomizeSides:
  3081. break;
  3082. case QSR_DuplicateRemoteLogon:
  3083. msg = pPlayerInfo->CharacterName() + ZString(" has been booted due to a duplicate logon.");
  3084. ReceiveChat(NULL, CHAT_TEAM, NA, salQuitSound, msg, c_cidNone, NA, NA);
  3085. break;
  3086. default:
  3087. ZAssert(false);
  3088. // intentional fallthrough
  3089. case QSR_LinkDead:
  3090. msg = pPlayerInfo->CharacterName() + ZString(" is MIA.");
  3091. ReceiveChat(NULL, CHAT_TEAM, NA, salMIASound, msg, c_cidNone, NA, NA);
  3092. break;
  3093. };
  3094. }
  3095. else
  3096. {
  3097. msg = pPlayerInfo->CharacterName() + ZString(" has left ")
  3098. + GetCore()->GetSide(sideOld)->GetName() + ZString(".");
  3099. ReceiveChat(NULL, CHAT_TEAM, NA, salEnemyLeavesSound, msg, c_cidNone, NA, NA);
  3100. }
  3101. }
  3102. }
  3103. pPlayerInfo->SetTeamLeader(false);
  3104. pPlayerInfo->SetMissionOwner(false);
  3105. pPlayerInfo->SetSideID(SIDE_TEAMLOBBY);
  3106. pPlayerInfo->SetMoney(0);
  3107. IsideIGC* pside = m_pCoreIGC->GetSide(SIDE_TEAMLOBBY);
  3108. pship->SetSide(pside);
  3109. if (pPlayerInfo == m_pPlayerInfo)
  3110. {
  3111. ResetShip();
  3112. m_pClientEventSource->OnChatMessageChange();
  3113. m_plinkSelectedChat = NULL;
  3114. }
  3115. m_pMissionInfo->AddPlayer(pPlayerInfo);
  3116. // if this is the lobby side, we need to manually send the OnAddPlayer message
  3117. if (m_pPlayerInfo->SideID() == SIDE_TEAMLOBBY)
  3118. m_pClientEventSource->OnAddPlayer(m_pMissionInfo, pPlayerInfo->SideID(), pPlayerInfo);
  3119. }
  3120. void BaseClient::RemovePlayerFromMission(PlayerInfo* pPlayerInfo, QuitSideReason reason, const char* szMessageParam)
  3121. {
  3122. if (!pPlayerInfo)
  3123. {
  3124. assert(false);
  3125. return;
  3126. }
  3127. // must call RemovePlayerFromSide first if the player is not on the team lobby
  3128. ZAssert(pPlayerInfo->SideID() == SIDE_TEAMLOBBY);
  3129. assert(pPlayerInfo);
  3130. m_pMissionInfo->RemovePlayer(pPlayerInfo);
  3131. pPlayerInfo->SetReady(true);
  3132. if (pPlayerInfo == m_pPlayerInfo)
  3133. {
  3134. // we need to manually send the DelPlayerMessage
  3135. m_pClientEventSource->OnDelPlayer(m_pMissionInfo, pPlayerInfo->SideID(), pPlayerInfo, reason);
  3136. }
  3137. pPlayerInfo->SetSideID(NA);
  3138. IshipIGC* pship = pPlayerInfo->GetShip();
  3139. pship->SetSide(NULL);
  3140. if (pPlayerInfo != m_pPlayerInfo)
  3141. {
  3142. // destroy their ship and player info
  3143. pship->Terminate();
  3144. PlayerLink* l = FindPlayerLink(pPlayerInfo->ShipID());
  3145. delete l;
  3146. }
  3147. else
  3148. {
  3149. // we are quitting the mission...
  3150. OnQuitMission(reason, szMessageParam);
  3151. }
  3152. }
  3153. void BaseClient::AddPlayerToMission(PlayerInfo* pPlayerInfo)
  3154. {
  3155. assert(pPlayerInfo);
  3156. pPlayerInfo->SetSideID(SIDE_TEAMLOBBY);
  3157. IshipIGC* pship = pPlayerInfo->GetShip();
  3158. IsideIGC* pside = m_pCoreIGC->GetSide(SIDE_TEAMLOBBY);
  3159. pship->SetSide(pside);
  3160. if (pPlayerInfo == m_pPlayerInfo)
  3161. {
  3162. m_pCoreIGC->SetMissionParams(&(m_pMissionInfo->GetMissionParams()));
  3163. m_pCoreIGC->UpdateSides(Time::Now(), &(m_pMissionInfo->GetMissionParams()), m_pMissionInfo->GetMissionDef().rgszName);
  3164. OnJoinSide();
  3165. }
  3166. m_pMissionInfo->AddPlayer(pPlayerInfo);
  3167. }
  3168. void BaseClient::AddPlayerToSide(PlayerInfo* pPlayerInfo, SideID sideID)
  3169. {
  3170. assert(pPlayerInfo);
  3171. assert(pPlayerInfo->SideID() == SIDE_TEAMLOBBY);
  3172. SideID sideOld = pPlayerInfo->SideID();
  3173. m_pMissionInfo->RemovePlayer(pPlayerInfo);
  3174. assert(pPlayerInfo->GetMoney() == 0);
  3175. pPlayerInfo->SetReady(true);
  3176. if (pPlayerInfo == m_pPlayerInfo)
  3177. {
  3178. OnQuitSide();
  3179. }
  3180. pPlayerInfo->SetSideID(sideID);
  3181. IshipIGC* pship = pPlayerInfo->GetShip();
  3182. IsideIGC* pside = m_pCoreIGC->GetSide(sideID);
  3183. pship->SetSide(pside);
  3184. if (pPlayerInfo == m_pPlayerInfo)
  3185. {
  3186. OnJoinSide();
  3187. }
  3188. m_pMissionInfo->AddPlayer(pPlayerInfo);
  3189. if (pPlayerInfo->IsTeamLeader())
  3190. m_pMissionInfo->SetSideLeader(pPlayerInfo);
  3191. // now officially on team so remove any requests to this mission
  3192. if (m_pMissionInfo->FindRequest(pPlayerInfo->SideID(), pPlayerInfo->ShipID()))
  3193. m_pMissionInfo->RemoveRequest(pPlayerInfo->SideID(), pPlayerInfo->ShipID());
  3194. // don't notify... notification assumed in OnAddPlayer
  3195. // update the last seen info
  3196. pPlayerInfo->ResetShipStatus();
  3197. m_pClientEventSource->OnAddPlayer(m_pMissionInfo, pPlayerInfo->SideID(), pPlayerInfo);
  3198. if (pPlayerInfo->IsHuman() && m_pMissionInfo->GetStage() == STAGE_STARTED && m_fm.IsConnected())
  3199. {
  3200. // tell the players that someone has just joined their team
  3201. if ((m_pPlayerInfo != pPlayerInfo) && (m_pPlayerInfo->SideID() == pPlayerInfo->SideID()))
  3202. {
  3203. ZString msg = pPlayerInfo->CharacterName() + ZString(" has joined your team.");
  3204. ReceiveChat(NULL, CHAT_TEAM, NA, salRecruitsArrivedSound, msg, c_cidNone, NA, NA);
  3205. }
  3206. else if (m_pPlayerInfo && GetSide() && GetSideID() != SIDE_TEAMLOBBY)
  3207. {
  3208. ZString msg = pPlayerInfo->CharacterName() + ZString(" has joined ")
  3209. + pside->GetName() + ZString(".");
  3210. ReceiveChat(NULL, CHAT_TEAM, NA, salEnemyJoinersSound, msg, c_cidNone, NA, NA);
  3211. }
  3212. }
  3213. }
  3214. /*-------------------------------------------------------------------------
  3215. * ResetStaticData
  3216. *-------------------------------------------------------------------------
  3217. Purpose:
  3218. Out with the old IGC data, in with the new
  3219. Parameters:
  3220. IGC staic file to load
  3221. Returns:
  3222. whether data set was successfully loaded
  3223. Side Effects:
  3224. Old data set is nuked (better not have any references to IGC stuff when calling this)
  3225. */
  3226. static void DoDecrypt(int size, char* pdata)
  3227. {
  3228. DWORD encrypt = 0;
  3229. //Do a rolling XOR to demunge the data
  3230. for (int i = 0; (i < size); i += 4)
  3231. {
  3232. DWORD* p = (DWORD*)(pdata + size);
  3233. encrypt = *p = *p ^ encrypt;
  3234. }
  3235. }
  3236. bool BaseClient::ResetStaticData(const char * szIGCStaticFile, ImissionIGC** ppStaticIGC, Time tNow, bool bEncrypt)
  3237. {
  3238. assert(ppStaticIGC);
  3239. if (&m_pCoreIGC == ppStaticIGC)
  3240. {
  3241. FlushGameState();
  3242. *ppStaticIGC = CreateMission();
  3243. Initialize(tNow);
  3244. }
  3245. else
  3246. {
  3247. // In this special case, the static mission must have already been created and initialized
  3248. assert(*ppStaticIGC);
  3249. }
  3250. // copy the core name
  3251. lstrcpy(m_szIGCStaticFile, szIGCStaticFile);
  3252. // here we load the static core data
  3253. int iStaticCoreVersion = LoadIGCStaticCore (szIGCStaticFile, *ppStaticIGC, true); // first just check version
  3254. // only reload the static igc core if this game uses a different one
  3255. if (iStaticCoreVersion != m_pMissionInfo->GetIGCStaticVer())
  3256. {
  3257. return false;
  3258. }
  3259. // actually load the data
  3260. LoadIGCStaticCore (szIGCStaticFile, *ppStaticIGC, false, bEncrypt ? DoDecrypt : NULL); // then actually load the data
  3261. return true;
  3262. }
  3263. void BaseClient::StartLockDown(const ZString& strReason, LockdownCriteria criteria)
  3264. {
  3265. ZAssert((m_lockdownCriteria & criteria) == 0);
  3266. m_lockdownCriteria |= criteria;
  3267. m_strLockDownReason = strReason;
  3268. }
  3269. void BaseClient::EndLockDown(LockdownCriteria criteria)
  3270. {
  3271. ZAssert(m_lockdownCriteria != 0);
  3272. m_lockdownCriteria &= ~criteria;
  3273. }
  3274. void BaseClient::BoardShip(IshipIGC* pship)
  3275. {
  3276. SetMessageType(BaseClient::c_mtGuaranteed);
  3277. BEGIN_PFM_CREATE(m_fm, pfmBoardShip, C, BOARD_SHIP)
  3278. END_PFM_CREATE
  3279. pfmBoardShip->sidParent = pship ? pship->GetObjectID() : NA;
  3280. if (pship)
  3281. {
  3282. assert(GetShip()->GetParentShip() == NULL);
  3283. StartLockDown("Boarding " + ZString(pship->GetName()) + "'s ship....", lockdownTeleporting);
  3284. }
  3285. else
  3286. {
  3287. IshipIGC* pshipParent = GetShip()->GetParentShip();
  3288. assert(pshipParent);
  3289. StartLockDown("Disembarking from " + ZString(pshipParent->GetName()) + "'s ship....", lockdownTeleporting);
  3290. }
  3291. }
  3292. void CfgInfo::Load(const char * szConfig)
  3293. {
  3294. const char * c_szCfgApp = "Allegiance";
  3295. char szStr[128]; // random number;
  3296. GetPrivateProfileString(c_szCfgApp, "ZAuth", "auth.zone.com",
  3297. szStr, sizeof(szStr), szConfig);
  3298. strZAuth = szStr;
  3299. GetPrivateProfileString(c_szCfgApp, "ClubLobby", "",
  3300. szStr, sizeof(szStr), szConfig);
  3301. strClubLobby = szStr;
  3302. GetPrivateProfileString(c_szCfgApp, "PublicLobby", "",
  3303. szStr, sizeof(szStr), szConfig);
  3304. strPublicLobby = szStr;
  3305. GetPrivateProfileString(c_szCfgApp, "Club", "",
  3306. szStr, sizeof(szStr), szConfig);
  3307. strClub = szStr;
  3308. GetPrivateProfileString(c_szCfgApp, "ClubMessageURL", "http://fdl.msn.com/zone/allegiance/messageoftheday.mdl", // http://a-markcu1/test/messageoftheday.mdl
  3309. szStr, sizeof(szStr), szConfig);
  3310. strClubMessageURL = szStr;
  3311. GetPrivateProfileString(c_szCfgApp, "PublicMessageURL", "http://fdl.msn.com/zone/allegiance/messageoftheday.mdl", // http://a-markcu1/test/messageoftheday.mdl
  3312. szStr, sizeof(szStr), szConfig);
  3313. strPublicMessageURL = szStr;
  3314. GetPrivateProfileString(c_szCfgApp, "ZoneEventsURL", "", szStr, sizeof(szStr), szConfig);
  3315. strZoneEventsURL = szStr;
  3316. GetPrivateProfileString(c_szCfgApp, "ZoneEventDetailsURL", "http://fdl.msn.com/zone/allegiance/zoneevents.mdl",
  3317. szStr, sizeof(szStr), szConfig);
  3318. strZoneEventDetailsURL = szStr;
  3319. GetPrivateProfileString(c_szCfgApp, "TrainingURL", "http://www.zone.com/allegiance/downloads.asp",
  3320. szStr, sizeof(szStr), szConfig);
  3321. strTrainingURL = szStr;
  3322. GetPrivateProfileString(c_szCfgApp, "PassportUpdateURL", "http://www.zone.com/allegiance/passportupdate.html",
  3323. szStr, sizeof(szStr), szConfig);
  3324. strPassportUpdateURL = szStr;
  3325. GetPrivateProfileString(c_szCfgApp, "ZoneAuthGUID", "{00000000-0000-0000-C000-000000000046}",
  3326. szStr, sizeof(szStr), szConfig);
  3327. int cbStr = strlen(szStr) + 1;
  3328. LPOLESTR wszStr = (LPOLESTR)_alloca(sizeof(OLECHAR) * cbStr);
  3329. MultiByteToWideChar(CP_ACP, 0, PCC(szStr), cbStr, wszStr, cbStr);
  3330. if (cbStr == 1 || !ZSucceeded(IIDFromString(wszStr, &guidZoneAuth)))
  3331. {
  3332. guidZoneAuth = GUID_NULL;
  3333. }
  3334. GetPrivateProfileString(c_szCfgApp, "FilelistCRC", "0",
  3335. szStr, sizeof(szStr), szConfig);
  3336. _strupr(szStr);
  3337. crcFileList = UTL::hextoi(szStr);
  3338. if (crcFileList == 0)
  3339. debugf("FilelistCRC is missing or invalid from %s.\n", szConfig);
  3340. GetPrivateProfileString(c_szCfgApp, "FilelistSize", "0",
  3341. szStr, sizeof(szStr), szConfig);
  3342. nFilelistSize = atoi(szStr);
  3343. if (nFilelistSize == 0)
  3344. debugf("FilelistSize is missing or invalid from %s.\n", szConfig);
  3345. GetPrivateProfileString(c_szCfgApp, "FilelistSite", "",
  3346. szStr, sizeof(szStr), szConfig);
  3347. strFilelistSite = szStr;
  3348. GetPrivateProfileString(c_szCfgApp, "FilelistDirectory", "",
  3349. szStr, sizeof(szStr), szConfig);
  3350. strFilelistDirectory = szStr;
  3351. GetPrivateProfileString(c_szCfgApp, "ClubMessageCRC", "0",
  3352. szStr, sizeof(szStr), szConfig);
  3353. _strupr(szStr);
  3354. crcClubMessageFile = UTL::hextoi(szStr);
  3355. GetPrivateProfileString(c_szCfgApp, "PublicMessageCRC", "0",
  3356. szStr, sizeof(szStr), szConfig);
  3357. _strupr(szStr);
  3358. crcPublicMessageFile = UTL::hextoi(szStr);
  3359. GetPrivateProfileString(c_szCfgApp, "UsePassport", "0",
  3360. szStr, sizeof(szStr), szConfig);
  3361. bUsePassport = atoi(szStr) != 0;
  3362. }
  3363. void _debugf(const char* format, ...)
  3364. {
  3365. const size_t size = 512;
  3366. char bfr[size];
  3367. va_list vl;
  3368. va_start(vl, format);
  3369. _vsnprintf(bfr, size, format, vl);
  3370. va_end(vl);
  3371. debugf(bfr);
  3372. }