#include "pch.h" #include "consoledata.h" #include "trekctrls.h" #include "loadout.h" #include "trekmdl.h" #include "training.h" #include "badwords.h" const int c_iRecipientOther = -1; const int c_iRecipientMe = 0; const int c_iRecipientSector = 1; const int c_iRecipientTeam = 2; const int c_iRecipientAll = 3; const int c_iRecipientWing0 = 4; //with subsequent wings following const int c_badMatch = 100000; const float c_minRectangleSize = 5.0f; static bool inline LegalRectangle(const Point& p1, const Point& p2) { return (fabs(p1.X() - p2.X()) > c_minRectangleSize) || (fabs(p1.Y() - p2.Y()) > c_minRectangleSize); } ////////////////////////////////////////////////////////////////////////////// // // ConsoleData // ////////////////////////////////////////////////////////////////////////////// const int c_maskMouseLeftDown = 0x01; const int c_maskMouseRightDown = 0x02; ////////////////////////////////////////////////////////////////////////////// // // PickImage // ////////////////////////////////////////////////////////////////////////////// class ConsoleDataImpl; class ConsolePickImage : public Image { private: MouseResult m_mouseresult; TRef m_pconsoleData; TRef m_pviewport; public: ConsolePickImage(Viewport* pviewport, ConsoleDataImpl* pconsoleData ); void SetHitTest(MouseResult mr); MouseResult HitTest(IInputProvider* pprovider, const Point& point, bool bCaptured); MouseResult Button(IInputProvider* pprovider, const Point& point, int button, bool bCaptured, bool bInside, bool bDown); void MouseMove(IInputProvider* pprovider, const Point& point, bool bCaptured, bool bInside); void MouseEnter(IInputProvider* pprovider, const Point& point); void MouseLeave(IInputProvider* pprovider); }; struct ChatSubject { ChatTarget m_ctRecipient; ObjectID m_oidRecipient; TRef m_pbaseRecipient; ShipListIGC m_shipsSubject; ChatSubject(ChatTarget ct) : m_ctRecipient(ct), m_oidRecipient(NA), m_pbaseRecipient(NULL) { } ~ChatSubject(void) { PurgeSubjects(); } void PurgeSubjects(void) { ShipLinkIGC* psl; while (psl = m_shipsSubject.first()) //Intentional = { psl->data()->Release(); delete psl; } } void AddSubject(IshipIGC* pship) { if (!m_shipsSubject.find(pship)) { pship->AddRef(); //Hack so that the client's ship is always at the front of the list //(so that it will correctly echo the chat in the chat history) if (pship == trekClient.GetShip()) m_shipsSubject.first(pship); else m_shipsSubject.last(pship); } } void DeleteSubject(IshipIGC* pship) { ShipLinkIGC* psl = m_shipsSubject.find(pship); if (psl) { pship->Release(); delete psl; } } void ToggleSubject(IshipIGC* pship) { ShipLinkIGC* psl = m_shipsSubject.find(pship); if (psl) { //Do not allow the last person to be toggled out of the group if (m_shipsSubject.n() > 1) { pship->Release(); delete psl; } } else { pship->AddRef(); if (pship == trekClient.GetShip()) m_shipsSubject.first(pship); else m_shipsSubject.last(pship); } if (m_shipsSubject.n() == 1) { m_ctRecipient = CHAT_INDIVIDUAL; m_pbaseRecipient = m_shipsSubject.first()->data(); m_oidRecipient = m_pbaseRecipient->GetObjectID(); } else { assert (m_shipsSubject.n() != 0); m_ctRecipient = CHAT_GROUP; m_pbaseRecipient = NULL; m_oidRecipient = NA; } } bool LegalCommand(CommandID cid) { bool bLegal; ShipLinkIGC* psl; for (psl = m_shipsSubject.first(); ((psl != NULL) && !psl->data()->LegalCommand(cid)); psl = psl->next()) { } bLegal = psl != NULL; return bLegal; } bool LegalCommand(CommandID cid, ImodelIGC* pmodelTarget) { bool bLegal; ShipLinkIGC* psl; for (psl = m_shipsSubject.first(); ((psl != NULL) && !psl->data()->LegalCommand(cid, pmodelTarget)); psl = psl->next()) { } bLegal = psl != NULL; return bLegal; } bool ContainsLayer(void) { bool bLegal; ShipLinkIGC* psl; for (psl = m_shipsSubject.first(); ((psl != NULL) && (psl->data()->GetPilotType() != c_ptLayer)); psl = psl->next()) { } bLegal = psl != NULL; return bLegal; } bool ContainsOnlyBuilders(void) { bool bLegal; ShipLinkIGC* psl; psl = m_shipsSubject.first(); if (psl == NULL) return false; while ((psl != NULL) && (psl->data()->GetPilotType() == c_ptBuilder)) { psl = psl->next(); } bLegal = psl == NULL; return bLegal; } void Reset(bool bStart = false) { if (m_ctRecipient == CHAT_GROUP) { ShipLinkIGC* pslNext; for (ShipLinkIGC* psl = m_shipsSubject.first(); (psl != NULL); psl = pslNext) { pslNext = psl->next(); if (psl->data()->GetMission() == NULL) delete psl; } if (m_shipsSubject.n() == 1) { m_ctRecipient = CHAT_INDIVIDUAL; m_pbaseRecipient = m_shipsSubject.first()->data(); m_oidRecipient = m_pbaseRecipient->GetObjectID(); } else if (m_shipsSubject.n() == 0) { m_ctRecipient = CHAT_TEAM; m_pbaseRecipient = NULL; m_oidRecipient = NA; } } else SetRecipient(m_ctRecipient, m_oidRecipient, m_pbaseRecipient); if (bStart && (m_ctRecipient == CHAT_INDIVIDUAL)) { IshipIGC* pship = (IshipIGC*)((IbaseIGC*)m_pbaseRecipient); assert (pship); if ((pship == trekClient.GetShip()) || (pship->GetPilotType() < c_ptPlayer)) { //Chats to a drone or myself convert to the team SetRecipient(CHAT_TEAM, NA, NULL); } } } void SetRecipient(ChatTarget ct, ObjectID oidRecipient, IbaseIGC* pbaseRecipient, const ShipListIGC* pships = NULL) { PurgeSubjects(); if ((ct != CHAT_NOSELECTION) && (ct != CHAT_ADMIN)) { if (ct == CHAT_GROUP) { for (ShipLinkIGC* psl = pships->first(); (psl != NULL); psl = psl->next()) { if (psl->data()->GetMission()) AddSubject(psl->data()); } if (m_shipsSubject.n() == 1) { ct = CHAT_INDIVIDUAL; pbaseRecipient = m_shipsSubject.first()->data(); oidRecipient = pbaseRecipient->GetObjectID(); } else if (m_shipsSubject.n() == 0) { ct = CHAT_TEAM; pbaseRecipient = NULL; oidRecipient = NA; } } else if (ct == CHAT_INDIVIDUAL) { if (pbaseRecipient->GetMission() != NULL) AddSubject((IshipIGC*)pbaseRecipient); else { ct = CHAT_TEAM; pbaseRecipient = NULL; oidRecipient = NA; } } else if (ct == CHAT_SHIP) { pbaseRecipient = NULL; oidRecipient = NA; IshipIGC* pship = trekClient.GetShip()->GetSourceShip(); const ShipListIGC* pships = pship->GetChildShips(); if (pships->n() == 0) { ct = CHAT_TEAM; } else { AddSubject(pship); for (ShipLinkIGC* psl = pships->first(); (psl != NULL); psl = psl->next()) AddSubject(psl->data()); } } if ((ct != CHAT_GROUP) && (ct != CHAT_INDIVIDUAL) && (ct != CHAT_SHIP)) { //Mark all legal recipients of this chat with an appropriate bit IsideIGC* psideFriendly = trekClient.GetShip()->GetSide(); WingID wid; const ShipListIGC* pships = psideFriendly->GetShips(); switch (ct) { case CHAT_TEAM: { pbaseRecipient = psideFriendly; assert (pbaseRecipient); oidRecipient = pbaseRecipient->GetObjectID(); } break; case CHAT_FRIENDLY_SECTOR: { if (!pbaseRecipient) { assert(oidRecipient == NA); pbaseRecipient = trekClient.GetChatCluster(); assert (pbaseRecipient); if (!pbaseRecipient) { ct = CHAT_TEAM; oidRecipient = NA; } else oidRecipient = pbaseRecipient->GetObjectID(); } else { assert(oidRecipient == pbaseRecipient->GetObjectID()); } } break; case CHAT_WING: { wid = trekClient.GetShip()->GetWingID(); if (oidRecipient == wid) oidRecipient = NA; else if (oidRecipient != NA) wid = oidRecipient; } break; case CHAT_EVERYONE: { pships = trekClient.m_pCoreIGC->GetShips(); } break; default: assert (false); } for (ShipLinkIGC* psl = pships->first(); (psl != NULL); psl = psl->next()) { IshipIGC* pship = psl->data(); bool bSubject = false; switch (ct) { case CHAT_EVERYONE: { bSubject = true; } break; case CHAT_ADMIN: case CHAT_LEADERS: { bSubject = false; //NYI } break; case CHAT_TEAM: { assert (pship->GetSide() == pbaseRecipient); bSubject = true; } break; case CHAT_FRIENDLY_SECTOR: { assert (pship->GetSide() == psideFriendly); PlayerInfo* ppi = (PlayerInfo*)(pship->GetPrivateData()); // XXX is this right? if (ppi) bSubject = (ppi->LastSeenSector() == oidRecipient); } break; case CHAT_WING: { assert (pship->GetSide() == psideFriendly); bSubject = (pship->GetWingID() == wid); } break; default: { assert (false); } } if (bSubject) AddSubject(pship); } } } m_ctRecipient = ct; m_oidRecipient = oidRecipient; m_pbaseRecipient = pbaseRecipient; } ChatSubject& operator = (const ChatSubject& chs) { SetRecipient(chs.m_ctRecipient, chs.m_oidRecipient, chs.m_pbaseRecipient, &chs.m_shipsSubject); return *this; } }; class ConsoleDataImpl : public ConsoleData, EventTargetContainer, TrekClientEventSink { friend class ConsoleImageImpl; private: ////////////////////////////////////////////////////////////////////////////// // // Data Members // ////////////////////////////////////////////////////////////////////////////// // name space for data binding TRef m_pns; TRef m_pnsDisplays; TRef m_peventSourceEveryFrame; // console mode TRef m_pnumberOverlayFlags; // data about me TRef m_pmodelDataMe; // data about the station which I am on TRef m_pmodelDataMyStation; // data for my current target TRef m_pmodelDataTarget; // data for my current objective //TRef m_pstringObjective; //TRef m_pmodelDataObjectiveTarget; //TRef m_pmodelDataObjectiveIssuer; // data for my current queued objective //TRef m_pstringQueuedObjective; //TRef m_pmodelDataQueuedObjectiveTarget; //TRef m_pmodelDataQueuedObjectiveIssuer; TRef m_pnumberAutopilotOn; TRef m_pnumberDetected; TRef m_pnumberExperience; TRef m_pnumberTargetExperience; TRef m_pnumberLivesOn; TRef m_pnumberKillsOn; TRef m_pnumberTargetInRangeFlag; TRef m_pnumberGotFlag; TRef m_pnumberGotArtifact; TRef m_pnumberSignature; // data for composed chat/cmd enum ComposeState { c_csNotComposing = 0, c_csComposeMouseCommand = 1, c_csComposeChat = 2, c_csComposeCommand = 3, c_csComposeShell = 4, c_csComposeQuick = 5 }; ComposeState m_csComposeState; bool m_bRecipientVisible; ChatSubject m_chsCommand; ChatSubject m_chsChat; ChatSubject m_chsQuick; ChatSubject* m_pchsCurrent; CommandID m_cidVerb; TRef m_pmodelTarget; ZString m_strTypedText; bool m_bMouseIn; TRef m_pnumberComposeState; TRef m_pnumberCommandID; TRef m_pstringChatRecipient; TRef m_pstringChatText; TRef m_pnumberBoardState; Vector m_positionStart; Vector m_positionStop; /* ZString m_strRecipient; ZString m_strMsg; ZString m_strVerb; CommandID m_idVerb; ObjectTypeMask m_otmVerbLegal; ZString m_strObject; ZString m_strComposedChat; TRef m_pstringDisplayedChat; TRef m_pstringDisplayedObject; TRef m_pnumberCommandMask; TRef m_pmodelDataObject; */ // data for radar mode TRef m_pnumberRadarMode; // data for my funds TRef m_pnumberMoney; // data for sector name TRef m_pstringSectorName; // data for the accepted order TRef m_pstringAcceptedOrder; TRef m_pstringQueuedOrder; // data for help tips TRef m_pstringTipText; TRef m_pstringCriticalTipText0; TRef m_pstringCriticalTipText1; TRef m_pstringCriticalTipText2; TRef m_pstringCriticalTipText3; Time m_timeLastTipSet; Time m_timeLastCriticalTipSet0; Time m_timeLastCriticalTipSet1; Time m_timeLastCriticalTipSet2; Time m_timeLastCriticalTipSet3; bool m_fTargetChanged; float m_rPreviousMissileLock; // data for the current vote TRef m_pstringCurrentVote; TRef m_pnumberHasVote; Point m_pointMouseStart; Point m_pointMouseStop; int m_maskMouseState; TRef m_pcolorTargetSide; OverlayMask m_omRecipientsVisible; TRef m_pbuttonbarRecipients; TRef m_pbuttonbarVerbs; TRef m_pcomboWing; // Lives remaining TRef m_pnumberLives; // Number of Kills TRef m_pnumberKills; public: ConsoleDataImpl(Viewport* pviewport, const char* pszFileName) : m_csComposeState(c_csNotComposing), m_rPreviousMissileLock(0.0f), m_fTargetChanged(false), m_timeLastTipSet(Time::Now()), m_timeLastCriticalTipSet0(Time::Now()), m_timeLastCriticalTipSet1(Time::Now()), m_timeLastCriticalTipSet2(Time::Now()), m_timeLastCriticalTipSet3(Time::Now()), m_maskMouseState(0), m_bMouseIn(false), m_chsQuick(CHAT_NOSELECTION), m_chsCommand(CHAT_NOSELECTION), m_chsChat(CHAT_TEAM), m_pchsCurrent(&m_chsCommand) { // create a namespace m_pns = GetModeler()->CreateNameSpace( "console", GetModeler()->GetNameSpace("gamepanes") ); m_pns->AddMember("OnEveryFrame", m_peventSourceEveryFrame = new EventSourceImpl()); // export all of our factories ModelData::ExportAccessors(m_pns); PartWrapper::ExportAccessors(m_pns); //console mode m_pns->AddMember("OverlayFlags", m_pnumberOverlayFlags = new ModifiableNumber((float)0)); // data about me m_pns->AddMember("Me", m_pmodelDataMe = new ModelData()); m_pns->AddMember("OnMeChange", m_pmodelDataMe->GetOnChangeEventSource()); // data about my current station m_pns->AddMember("MyStation", m_pmodelDataMyStation = new ModelData()); // data for my current target m_pns->AddMember("Target", m_pmodelDataTarget = new ModelData(NULL)); m_pns->AddMember("OnTargetChange", m_pmodelDataTarget->GetOnChangeEventSource()); m_pns->AddMember("targetSideColor", m_pcolorTargetSide = new ModifiableColorValue(Color::White())); // data for my current objective //m_pns->AddMember("Objective", m_pstringObjective = new ModifiableString("")); //m_pns->AddMember("ObjectiveTarget", m_pmodelDataObjectiveTarget = new ModelData()); //m_pns->AddMember("OnObjectiveTargetChange", m_pmodelDataObjectiveTarget->GetOnChangeEventSource()); //m_pns->AddMember("ObjectiveIssuer", m_pmodelDataObjectiveIssuer = new ModelData()); //m_pns->AddMember("OnObjectiveIssuerChange", m_pmodelDataObjectiveIssuer->GetOnChangeEventSource()); m_pns->AddMember("AutopilotOn", m_pnumberAutopilotOn = new ModifiableNumber(0)); m_pns->AddMember("LivesOn", m_pnumberLivesOn = new ModifiableNumber(0)); m_pns->AddMember("KillsOn", m_pnumberKillsOn = new ModifiableNumber(0)); m_pns->AddMember("BoardState", m_pnumberBoardState = new ModifiableNumber(0)); m_pns->AddMember("Detected", m_pnumberDetected = new ModifiableNumber(0)); m_pns->AddMember("Experience", m_pnumberExperience = new ModifiableNumber(0)); m_pns->AddMember("TargetExperience", m_pnumberTargetExperience = new ModifiableNumber(0)); m_pns->AddMember("TargetInRangeFlag", m_pnumberTargetInRangeFlag = new ModifiableNumber(0)); m_pns->AddMember("GotFlag", m_pnumberGotFlag = new ModifiableNumber(0)); m_pns->AddMember("GotArtifact", m_pnumberGotArtifact = new ModifiableNumber(0)); m_pns->AddMember("Signature", m_pnumberSignature = new ModifiableNumber(0)); // data for my current queued objective //m_pns->AddMember("QueuedObjective", m_pstringQueuedObjective = new ModifiableString("")); //m_pns->AddMember("QueuedObjectiveTarget", m_pmodelDataQueuedObjectiveTarget = new ModelData()); //m_pns->AddMember("OnQueuedObjectiveTargetChange", m_pmodelDataQueuedObjectiveTarget->GetOnChangeEventSource()); //m_pns->AddMember("QueuedObjectiveIssuer", m_pmodelDataQueuedObjectiveIssuer = new ModelData()); //m_pns->AddMember("OnQueuedObjectiveIssuerChange", m_pmodelDataQueuedObjectiveIssuer->GetOnChangeEventSource()); // data for composed chat/cmd m_pns->AddMember("ComposeState", m_pnumberComposeState = new ModifiableNumber(c_csNotComposing)); m_pns->AddMember("CommandVerb", m_pnumberCommandID = new ModifiableNumber(c_csNotComposing)); m_pns->AddMember("ChatRecipient", m_pstringChatRecipient = new ModifiableString("")); m_pns->AddMember("ChatText", m_pstringChatText = new ModifiableString("")); // data for my radar mode m_pns->AddMember("RadarMode", m_pnumberRadarMode = new ModifiableNumber(0)); // data for my funds m_pns->AddMember("Money", m_pnumberMoney = new ModifiableNumber(0)); // data for help line tips m_pns->AddMember("SectorName", m_pstringSectorName = new ModifiableString("")); m_pns->AddMember("AcceptedOrder", m_pstringAcceptedOrder = new ModifiableString("")); m_pns->AddMember("QueuedOrder", m_pstringQueuedOrder = new ModifiableString("")); // data for help line tips m_pns->AddMember("TipText", m_pstringTipText = new ModifiableString("")); m_pns->AddMember("CriticalTipText0", m_pstringCriticalTipText0 = new ModifiableString("")); m_pns->AddMember("CriticalTipText1", m_pstringCriticalTipText1 = new ModifiableString("")); m_pns->AddMember("CriticalTipText2", m_pstringCriticalTipText2 = new ModifiableString("")); m_pns->AddMember("CriticalTipText3", m_pstringCriticalTipText3 = new ModifiableString("")); // data for the current vote m_pns->AddMember("CurrentVote", m_pstringCurrentVote = new ModifiableString("")); m_pns->AddMember("HasVote", m_pnumberHasVote = new ModifiableNumber(0)); //lives remaining m_pns->AddMember("Lives", m_pnumberLives = new ModifiableNumber(0)); //number of kills m_pns->AddMember("Kills", m_pnumberKills = new ModifiableNumber(0)); // Verb & recipient button bars m_pnsDisplays = GetModeler()->GetNameSpace(pszFileName); m_omRecipientsVisible = OverlayMask(m_pnsDisplays->FindNumber("RecipientsVisibleOM", 0.0f)); CastTo(m_pbuttonbarRecipients, m_pnsDisplays->FindMember("RecipientsBB")); AddEventTarget(OnRecipientButton, m_pbuttonbarRecipients->GetEventSource()); CastTo(m_pbuttonbarVerbs, m_pnsDisplays->FindMember("VerbsBB")); AddEventTarget(OnVerbButton, m_pbuttonbarVerbs->GetEventSource()); CastTo(m_pcomboWing, m_pnsDisplays->FindMember("WingCombo")); for (WingID wingID = 0; wingID < c_widMax; wingID++) { m_pcomboWing->AddItem(c_pszWingName[wingID], wingID); } AddEventTarget(OnWingCombo, m_pcomboWing->GetEventSource()); } ~ConsoleDataImpl() { GetModeler()->UnloadNameSpace("console"); GetModeler()->UnloadNameSpace("teampane"); GetModeler()->UnloadNameSpace("teleportpane"); } const TRef& GetNameSpace(void) const { return m_pnsDisplays; } void Update(Time now) { IshipIGC* pshipParent = trekClient.GetShip()->GetParentShip(); m_pmodelDataMe->SetModel(trekClient.GetShip()->GetSourceShip()); m_pmodelDataMyStation->SetModel(trekClient.GetShip()->GetStation()); //m_pmodelDataObjectiveTarget->SetModel(trekClient.GetShip()->GetCommandTarget(c_cmdAccepted)); //m_pmodelDataQueuedObjectiveTarget->SetModel(trekClient.GetShip()->GetCommandTarget(c_cmdQueued)); //m_pstringObjective->SetValue(trekClient.GetShip()->GetCommandVerb(c_cmdAccepted)); m_peventSourceEveryFrame->Trigger(); m_pnumberRadarMode->SetValue((float)GetWindow()->GetRadarMode()); m_pnumberMoney->SetValue((float)trekClient.GetMoney()); m_pnumberAutopilotOn->SetValue(trekClient.autoPilot() ? 1.0f : 0.0f); if (!pshipParent) m_pnumberBoardState->SetValue(0); else if (trekClient.GetShip()->GetTurretID() == NA) m_pnumberBoardState->SetValue(1); else m_pnumberBoardState->SetValue(2); { PlayerInfo* ppi = (PlayerInfo*)(trekClient.GetShip()->GetPrivateData()); m_pnumberDetected->SetValue((ppi && !ppi->GetDetected()) ? 0.0f : 1.0f); m_pnumberLives->SetValue(trekClient.m_pCoreIGC->GetMissionParams()->iLives - ppi->MissionDeaths()); m_pnumberKills->SetValue(ppi->MissionKills()); if ((trekClient.m_pCoreIGC->GetMissionParams()->iLives - ppi->MissionDeaths()) >= 10) m_pnumberLivesOn->SetValue(0.0f); else m_pnumberLivesOn->SetValue(1.0f); if (trekClient.m_pCoreIGC->GetMissionParams()->IsDeathMatchGame()) m_pnumberKillsOn->SetValue(1.0f); else m_pnumberKillsOn->SetValue(0.0f); } // update my experience. { float experience = trekClient.GetShip()->GetExperienceMultiplier(); m_pnumberExperience->SetValue(100.0f * (experience - 1.0f)); } // update my target's experience { ImodelIGC* pmodelTarget = m_pmodelDataTarget->GetModel(); if (pmodelTarget && pmodelTarget->GetObjectType() == OT_ship) { m_pnumberTargetExperience->SetValue( 100.0f * (((IshipIGC*)pmodelTarget)->GetExperienceMultiplier() - 1.0f)); } else { m_pnumberTargetExperience->SetValue(0); } } { IshipIGC* pship = trekClient.GetShip()->GetSourceShip(); //pship->GetFlag() -2 = artifact, -1 = nothing, x = flag of side x switch (pship->GetFlag()) { case SIDE_TEAMLOBBY: m_pnumberGotFlag->SetValue(0.0f); m_pnumberGotArtifact->SetValue(1.0f); break; case NA: m_pnumberGotFlag->SetValue(0.0f); m_pnumberGotArtifact->SetValue(0.0f); break; default: m_pnumberGotFlag->SetValue(1.0f); m_pnumberGotArtifact->SetValue(0.0f); } m_pnumberSignature->SetValue(100.0f * pship->GetSignature() / pship->GetSide()->GetGlobalAttributeSet().GetAttribute(c_gaSignature)); } { IclusterIGC* pcluster = trekClient.GetCluster(); if (pcluster) m_pstringSectorName->SetValue(pcluster->GetName()); } { static char bfr[100]; CommandID cid = trekClient.GetShip()->GetCommandID(c_cmdAccepted); if (cid != c_cidNone) { strcpy(bfr, c_cdAllCommands[cid].szVerb); ImodelIGC* pmodel = trekClient.GetShip()->GetCommandTarget(c_cmdAccepted); if (pmodel) { int l = strlen(bfr); bfr[l] = ' '; strcpy(bfr + l + 1, GetModelName(pmodel)); } } else bfr[0] = '\0'; m_pstringAcceptedOrder->SetValue(bfr); } { static char bfr[100]; CommandID cid = trekClient.GetShip()->GetCommandID(c_cmdQueued); if (cid != c_cidNone) { strcpy(bfr, c_cdAllCommands[cid].szVerb); ImodelIGC* pmodel = trekClient.GetShip()->GetCommandTarget(c_cmdQueued); if (pmodel) { int l = strlen(bfr); bfr[l] = ' '; strcpy(bfr + l + 1, GetModelName(pmodel)); } } else bfr[0] = '\0'; m_pstringQueuedOrder->SetValue(bfr); } // turn off tip text if it is expired float fTipTextDuration = Training::IsTraining () ? 30.0f : 7.5f; if (now - m_timeLastCriticalTipSet0 >= fTipTextDuration) { m_pstringCriticalTipText0->SetValue(""); m_timeLastCriticalTipSet0 = now + 3600.0f; } if (now - m_timeLastCriticalTipSet1 >= fTipTextDuration) { m_pstringCriticalTipText1->SetValue(""); m_timeLastCriticalTipSet1 = now + 3600.0f; } if (now - m_timeLastCriticalTipSet2 >= fTipTextDuration) { m_pstringCriticalTipText2->SetValue(""); m_timeLastCriticalTipSet2 = now + 3600.0f; } if (now - m_timeLastCriticalTipSet3 >= fTipTextDuration) { m_pstringCriticalTipText3->SetValue(""); m_timeLastCriticalTipSet3 = now + 3600.0f; } float rMissileLock = 0.0f; ImagazineIGC* magazine = (ImagazineIGC*)(trekClient.GetShip()->GetMountedPart(ET_Magazine, 0)); if (magazine) rMissileLock = magazine->GetLock(); if (rMissileLock > 0.0f && rMissileLock < 1.0f && !(m_rPreviousMissileLock > 0.0f && m_rPreviousMissileLock < 1.0f)) trekClient.PostText(false, "Partial missile lock - center the target on screen to improve the lock."); else if (rMissileLock == 1.0f && m_rPreviousMissileLock != 1.0f) trekClient.PostText(false, "%s", (const char*)(ZString("Missile lock acquired - press [Ctrl]-[Space] to fire a ") + magazine->GetMissileType()->GetName() + " missile at the target.")); else if (rMissileLock == 0.0f && m_rPreviousMissileLock != 0.0f && !m_fTargetChanged) trekClient.PostText(false, "Missile lock lost."); m_rPreviousMissileLock = rMissileLock; m_fTargetChanged = false; BallotInfo* pballotinfo = trekClient.GetCurrentBallot(); m_pnumberHasVote->SetValue(pballotinfo ? 1.0f : 0.0f); m_pstringCurrentVote->SetValue(pballotinfo ? ZString(pballotinfo->GetBallotText()) : ZString()); /* if (m_bRecipientVisible) { int nShipsPerWing[c_widMax]; for (int i = 0; (i < c_widMax); i++) nShipsPerWing[i] = 0; for (ShipLinkIGC* psl = trekClient.GetSide()->GetShips()->first(); (psl != NULL); psl = psl->next()) { WingID wid = psl->data()->GetWingID(); if (wid >= 0) { assert (wid < c_widMax); nShipsPerWing[wid]++; } } for (int j = 0; (j < c_widMax); j++) { if (nShipsPerWing[j] == 0) { //Delete wing if it exists } else { m_pcomboWing->AddItem(c_pszWingName[j], j); } } m_pcomboWing->SetSelection(trekClient.GetShip()->GetWingID()); } */ } void SetInRange(bool bInRange) { m_pnumberTargetInRangeFlag->SetValue(bInRange ? 1.0f : 0.0f); } void SetTarget(ImodelIGC* pmodel) { if (m_pmodelDataTarget->GetModel() != pmodel) { m_fTargetChanged = true; m_pmodelDataTarget->SetModel(pmodel); } } void SetOverlayFlags(OverlayMask om) { m_pnumberOverlayFlags->SetValue((float)om); bool bNew = (om & m_omRecipientsVisible) != 0; if (bNew != m_bRecipientVisible) { m_chsCommand.SetRecipient(CHAT_INDIVIDUAL, trekClient.GetShipID(), trekClient.GetShip()); AdjustVerbs(&m_chsCommand); } m_bRecipientVisible = bNew; } OverlayMask GetOverlayFlags(void) const { return (OverlayMask)(m_pnumberOverlayFlags->GetValue()); } void OnSwitchViewMode() { GetWindow()->SetCursor(AWF_CURSOR_DEFAULT); m_maskMouseState = 0; if (m_csComposeState == c_csComposeMouseCommand) QuitComposing(); } void SetTipText(const ZString& strHelp) { m_timeLastTipSet = Time::Now(); m_pstringTipText->SetValue(strHelp); } void SetCriticalTipText(const ZString& strHelp) { if (!Training::IsTraining()) { // scroll the tip text and the help m_pstringCriticalTipText3->SetValue(m_pstringCriticalTipText2->GetValue()); m_pstringCriticalTipText2->SetValue(m_pstringCriticalTipText1->GetValue()); m_pstringCriticalTipText1->SetValue(m_pstringCriticalTipText0->GetValue()); m_timeLastCriticalTipSet3 = m_timeLastCriticalTipSet2; m_timeLastCriticalTipSet2 = m_timeLastCriticalTipSet1; m_timeLastCriticalTipSet1 = m_timeLastCriticalTipSet0; } m_pstringCriticalTipText0->SetValue(strHelp); m_timeLastCriticalTipSet0 = Time::Now(); // HACK: handle the case where the text will wrap by adding a blank // critical tip after it (a hack because of the max of 2 lines, // hardcoded font, and hardcoded width) static IEngineFont* pfont = GetModeler()->GetNameSpace("font")->FindFont("medBoldVerdana"); if (pfont->GetMaxTextLength(strHelp, 620, true) < strHelp.GetLength()) SetCriticalTipText(""); } void SetComposeState(ComposeState cs) { { IclusterIGC* pcluster = trekClient.GetCluster(); if (pcluster) { //Clear subject masks for all ships and legal objects for (ModelLinkIGC* pml = pcluster->GetPickableModels()->first(); (pml != NULL); pml = pml->next()) { ImodelIGC* pmodel = pml->data(); ThingSite* pts = pmodel->GetThingSite(); if (pts) { pts->SetMask(0); } } } } m_csComposeState = cs; m_pnumberComposeState->SetValue((float)(int)cs); } void SetVerb(CommandID cidVerb) { m_cidVerb = cidVerb; m_pnumberCommandID->SetValue((float)(int)cidVerb); } ComposeState GetComposeState() { return m_csComposeState; } void OnMoneyChange(PlayerInfo* pPlayerInfo) { } void OnDelPlayer(MissionInfo *pMissionDef, SideID sideID, PlayerInfo *pPlayerInfo, QuitSideReason reason, const char* szMessageParam) { if (!Training::IsTraining()) { if (pPlayerInfo == trekClient.GetPlayerInfo()) { // go back to the team screen, at least for the moment GetWindow()->screen(ScreenIDTeamScreen); } else if (m_csComposeState != c_csNotComposing) { //NYI ... need to handle the case where the person receiving a composed chat //goes away.. } } } bool OnBackButton() { if (!Training::IsTraining () && !trekClient.GetShip()->IsGhost()) GetWindow()->SetViewMode(trekClient.flyingF() ? TrekWindow::vmCombat : TrekWindow::vmHangar); return true; } bool OnRecipientButton(int nColumn) { if (m_csComposeState != c_csComposeShell) { ChatTarget ct; ObjectID oid; IbaseIGC* pbase; switch (nColumn) { case c_iRecipientMe: ct = CHAT_INDIVIDUAL; pbase = trekClient.GetShip(); oid = NA; break; case c_iRecipientTeam: ct = CHAT_TEAM; pbase = NULL; oid = NA; break; case c_iRecipientSector: ct = CHAT_FRIENDLY_SECTOR; pbase = NULL; oid = NA; break; case c_iRecipientAll: ct = CHAT_EVERYONE; pbase = NULL; oid = NA; break; default: ct = CHAT_WING; pbase = NULL; oid = nColumn - c_iRecipientWing0; } if (m_csComposeState == c_csNotComposing) StartComposing(c_csComposeChat, &m_chsChat); m_pchsCurrent->SetRecipient(ct, oid, pbase); UpdateComposedChat(); } return true; } bool OnWingCombo(int index) { OnRecipientButton(c_iRecipientWing0 + index); return true; } /* bool OnWingButton0() { OnRecipientButton(c_iRecipientWing0); return true; } bool OnWingButton1() { OnRecipientButton(c_iRecipientWing0); return true; } bool OnWingButton2() { OnRecipientButton(c_iRecipientWing0); return true; } bool OnWingButton3() { OnRecipientButton(c_iRecipientWing0); return true; } bool OnWingButton4() { OnRecipientButton(c_iRecipientWing0); return true; } bool OnWingButton5() { OnRecipientButton(c_iRecipientWing0); return true; } bool OnWingButton6() { OnRecipientButton(c_iRecipientWing0); return true; } bool OnWingButton7() { OnRecipientButton(c_iRecipientWing0); return true; } bool OnWingButton8() { OnRecipientButton(c_iRecipientWing0); return true; } bool OnWingButton9() { OnRecipientButton(c_iRecipientWing0); return true; } */ bool OnVerbButton(int nColumn) { if (m_csComposeState != c_csComposeShell) { trekClient.PlaySoundEffect(chatKeySound); if (m_csComposeState == c_csNotComposing) StartComposing(c_csComposeCommand, &m_chsCommand); else SetComposeState(c_csComposeCommand); SetVerb(CommandID(nColumn + c_cidAttack)); UpdateComposedChat(); } return true; } void UpdateComposedChat() { AdjustVerbs(m_pchsCurrent); if (m_csComposeState != c_csNotComposing) { GetWindow()->SetCommandAsteroid(0); //Fill in the typed text if (m_csComposeState != c_csComposeMouseCommand) { ZString displayedChat = m_strTypedText + "|"; if (m_csComposeState == c_csComposeCommand) { if (m_pmodelTarget) { //Fill in the rest of the chat text with the remainder of the picked name char bfr[c_cbName]; int lenText = m_strTypedText.GetLength(); strcpy(bfr, (m_pmodelTarget == trekClient.GetShip()) && (lenText <= 2) && (_strnicmp(m_strTypedText, "me", lenText) == 0) ? "me" : GetModelName(m_pmodelTarget)); int lenName = strlen(bfr); if (lenText < lenName) { //Convert the trailing characters to upper case for (int i = lenText; (i < lenName); i++) bfr[i] = toupper(bfr[i]); displayedChat += &bfr[lenText]; } } } m_pstringChatText->SetValue(displayedChat); } if (m_csComposeState != c_csComposeShell) { //Fill in the recipient static const char* pszEveryone = "All"; static const char* pszLeaders = "Leaders"; static const char* pszShip = "ship"; static const char* pszGroup = "Group"; static const char* pszAdmin = "Admin"; static const char* pszNone = "None"; const char* pszRecipient; switch (m_pchsCurrent->m_ctRecipient) { case CHAT_NOSELECTION: pszRecipient = pszNone; break; case CHAT_EVERYONE: pszRecipient = pszEveryone; break; case CHAT_GROUP: pszRecipient = pszGroup; { AsteroidAbilityBitMask aabm = c_aabmBuildable; for (ShipLinkIGC* psl = m_pchsCurrent->m_shipsSubject.first(); (psl != NULL); psl = psl->next()) { IshipIGC* pship = psl->data(); if (pship->GetPilotType() == c_ptBuilder) { aabm |= pship->GetOrdersABM(); } } GetWindow()->SetCommandAsteroid(aabm); } break; case CHAT_LEADERS: pszRecipient = pszLeaders; break; case CHAT_SHIP: pszRecipient = pszShip; break; case CHAT_TEAM: { IsideIGC* pside = m_pchsCurrent->m_pbaseRecipient == NULL ? trekClient.GetShip()->GetSide() : (IsideIGC*)((IbaseIGC*)m_pchsCurrent->m_pbaseRecipient); pszRecipient = pside->GetName(); } break; case CHAT_INDIVIDUAL: { assert (m_pchsCurrent->m_pbaseRecipient != NULL); IshipIGC* pship = (IshipIGC*)((IbaseIGC*)m_pchsCurrent->m_pbaseRecipient); pszRecipient = pship->GetName(); if (pship->GetPilotType() == c_ptBuilder) { GetWindow()->SetCommandAsteroid(pship->GetOrdersABM()); } } break; case CHAT_ADMIN: { pszRecipient = pszAdmin; } break; case CHAT_WING: { pszRecipient = c_pszWingName[m_pchsCurrent->m_oidRecipient == NA ? trekClient.GetShip()->GetWingID() : m_pchsCurrent->m_oidRecipient]; } break; case CHAT_ALL_SECTOR: { //NYI Need to distunguish between all and friendly } case CHAT_FRIENDLY_SECTOR: { IclusterIGC* pcluster; if (m_pchsCurrent->m_pbaseRecipient == NULL) { pcluster = trekClient.GetChatCluster(); assert (pcluster != NULL); if (pcluster) pszRecipient = pcluster->GetName(); else pszRecipient = ""; } else pszRecipient = ((IclusterIGC*)((IbaseIGC*)m_pchsCurrent->m_pbaseRecipient))->GetName(); } } m_pstringChatRecipient->SetValue(CensorBadWords (pszRecipient)); } } } void OnAddPlayer(MissionInfo *pMissionDef, SideID sideID, PlayerInfo *pPlayerInfo) { } void SendChatInternal(const char* pszText, int idSonicChat, CommandID cid, ImodelIGC* pmodel) { m_pchsCurrent->Reset(); if ((m_pchsCurrent->m_shipsSubject.n() != 0) || (m_pchsCurrent->m_ctRecipient == CHAT_ADMIN)) { ObjectType otTarget = NA; ObjectID oidTarget = NA; if (pmodel) { if (pmodel->GetMission()) { otTarget = pmodel->GetObjectType(); oidTarget = pmodel->GetObjectID(); } else pmodel = NULL; //Model has been terminated } if (m_pchsCurrent->m_ctRecipient == CHAT_GROUP) { ChatTarget ct = CHAT_GROUP; for (ShipLinkIGC* psl = m_pchsCurrent->m_shipsSubject.first(); (psl != NULL); psl = psl->next()) { IshipIGC* pship = psl->data(); trekClient.SendChat( trekClient.GetShip(), ct, pship->GetObjectID(), idSonicChat, pszText, cid, otTarget, oidTarget, pmodel ); ct = CHAT_GROUP_NOECHO; } } else { trekClient.SendChat( trekClient.GetShip(), m_pchsCurrent->m_ctRecipient, m_pchsCurrent->m_oidRecipient, idSonicChat, pszText, cid, otTarget, oidTarget, pmodel ); } } } void StartQuickChat(ChatTarget ctRecipient, ObjectID oidRecipient, IbaseIGC* pbaseRecipient) { if (m_csComposeState == c_csNotComposing) { if (ctRecipient != CHAT_NOSELECTION) { m_chsQuick.SetRecipient(ctRecipient, oidRecipient, pbaseRecipient); StartComposing(c_csComposeQuick, &m_chsQuick); } else StartComposing(c_csComposeQuick, &m_chsChat); } else { assert (m_pchsCurrent); if (ctRecipient != CHAT_NOSELECTION) m_pchsCurrent->SetRecipient(ctRecipient, oidRecipient, pbaseRecipient); SetComposeState(c_csComposeQuick); } UpdateComposedChat(); } void SendQuickChat(int idSonicChat, int ttMask, CommandID cid, AbilityBitMask aabm) { //m_pchsCurrent = &m_chsChat; if (cid == c_cidMyAccepted) cid = trekClient.GetShip()->GetCommandID(c_cmdAccepted); // // figure out the target // ImodelIGC* pmodel; if (cid != c_cidNone) { assert (ttMask); switch (ttMask) { case c_ttMyAccepted: case c_ttMyQueued: case c_ttMyTarget: assert (char(c_ttMyAccepted) == c_cmdAccepted); assert (char(c_ttMyQueued) == c_cmdQueued); assert (char(c_ttMyTarget) == c_cmdCurrent); pmodel = trekClient.GetShip()->GetCommandTarget(char(ttMask)); break; case c_ttWorstEnemy: { DamageTrack* pdt = trekClient.GetShip()->GetDamageTrack(); assert (pdt); DamageBucketLink* pdbl = pdt->GetDamageBuckets()->first(); pmodel = pdbl ? pdbl->data()->model() : NULL; } break; case c_ttMe: pmodel = trekClient.GetShip(); break; default: pmodel = trekClient.GetShip()->GetCommandTarget(c_cmdCurrent); if ((!pmodel) || (pmodel == trekClient.GetShip()) || (!FindableModel(pmodel, trekClient.GetShip()->GetSide(), ttMask, aabm)) || (!trekClient.GetShip()->CanSee(pmodel))) { pmodel = FindTarget(trekClient.GetShip(), ttMask, NULL, NULL, NULL, NULL, aabm); } } if (pmodel == NULL) cid = c_cidNone; } else pmodel = NULL; //Send the chat SendChatInternal(NULL, idSonicChat, cid, pmodel); QuitComposing(); } void SendChat() { if (m_csComposeState == c_csComposeChat) { assert (m_strTypedText.GetLength() != 0); SendChatInternal(m_strTypedText, NA, c_cidNone, NULL); } else if (m_csComposeState == c_csComposeShell) { trekClient.ParseShellCommand(m_strTypedText); } else { SendChatInternal( NULL, NA, m_cidVerb, m_pmodelTarget ); } QuitComposing(); } void AdjustVerbs(ChatSubject* pchs) { if (m_bRecipientVisible) { for (CommandID i = c_cidAttack; (i < c_cidMax); i++) m_pbuttonbarVerbs->SetEnabled(i - c_cidAttack, pchs->LegalCommand(i)); } } void QuitComposing() { // clean up the composing process SetComposeState(c_csNotComposing); SetChatObject(NULL); // default to the command target m_pchsCurrent = &m_chsCommand; AdjustVerbs(&m_chsCommand); GetWindow()->SetCommandAsteroid(0); } void StartComposing(ComposeState cs, ChatSubject* pchsTarget) { assert (cs != c_csNotComposing); assert (m_csComposeState == c_csNotComposing); // set the current chat target pchsTarget->Reset(cs == c_csComposeChat); m_pchsCurrent = pchsTarget; SetChatObject(NULL); m_strTypedText.SetEmpty(); // set the compose state SetComposeState(cs); } IshipIGC* PickShip(IclusterIGC* pcluster, const Point& pointPick) { IshipIGC* pshipMin = NULL; float distanceMin = FLT_MAX; Rect rectImage = GetWindow()->GetViewport()->GetViewRect()->GetValue(); Point point = pointPick - rectImage.Center(); for (ShipLinkIGC* psl = pcluster->GetShips()->first(); (psl != NULL); psl = psl->next()) { IshipIGC* pship = psl->data(); if (pship->GetVisibleF()) { ThingSite* psite = pship->GetThingSite(); if (psite) { const Point& pxy = psite->GetScreenPosition(); float r = psite->GetScreenRadius(); if ((pxy.X() + r >= point.X()) && (pxy.X() - r <= point.X()) && (pxy.Y() + r >= point.Y()) && (pxy.Y() - r <= point.Y())) { float dx = pxy.X() - point.X(); float dy = pxy.Y() - point.Y(); float d = dx*dx + dy*dy; if (d <= distanceMin) { pshipMin = pship; distanceMin = d; } } } } } return pshipMin; } void UpdatePick3D() { m_positionStop.x = m_positionStart.x; m_positionStop.y = m_positionStart.y; Camera* pcamera = GetWindow()->GetCamera(); const Orientation& o = pcamera->GetOrientation(); const Vector& b = o.GetBackward(); if ((b.z > 0.9f) || (b.z < -0.9f)) { //Too steep a view to making picking in 3D meaningful m_positionStop.z = 0.0f; } else { Rect rectImage = GetWindow()->GetViewport()->GetViewRect()->GetValue(); float halfwidth = (0.5f * rectImage.XSize()); const Vector& positionCamera = pcamera->GetPosition(); { Point pointStop = m_pointMouseStop - rectImage.Center(); Vector directionStop = pcamera->TransformImageToDirection(pointStop / halfwidth); float px = m_positionStart.x - positionCamera.x; float py = m_positionStart.y - positionCamera.y; m_positionStop.z = (px * directionStop.x * directionStop.z + py * directionStop.y * directionStop.z) / (directionStop.x * directionStop.x + directionStop.y * directionStop.y) + positionCamera.z; } } } void PickCluster(IclusterIGC* pcluster, int button) { if (m_csComposeState == c_csNotComposing) { StartComposing(c_csComposeChat, &m_chsChat); m_pchsCurrent->SetRecipient(CHAT_FRIENDLY_SECTOR, pcluster->GetObjectID(), pcluster); UpdateComposedChat(); } else { if (m_csComposeState != c_csComposeCommand) m_csComposeState = c_csComposeCommand; if ((m_pchsCurrent->m_ctRecipient == CHAT_INDIVIDUAL) && m_pchsCurrent->m_pbaseRecipient->GetMission()) { //For chats to living individuals, treat a single builder as a special case IshipIGC* pship = (IshipIGC*)(IbaseIGC*)(m_pchsCurrent->m_pbaseRecipient); if (pship->GetPilotType() == c_ptBuilder) { AbilityBitMask abmOrders = pship->GetOrdersABM(); if (abmOrders != c_aabmBuildable) { ImodelIGC* pmodel = FindTarget(pship, c_ttNeutral | c_ttAsteroid | c_ttLeastTargeted, NULL, pcluster, &(Vector::GetZero()), NULL, abmOrders); if (pmodel) { SetVerb(c_cidBuild); SetChatObject(pmodel); SendChat(); return; } } } } SetVerb(c_cidGoto); // create a cluster buoy in that sector DataBuoyIGC db; db.position = Vector(0, 0, 0); db.clusterID = pcluster->GetObjectID(); db.type = c_buoyCluster; IbuoyIGC* b = (IbuoyIGC*)(trekClient.m_pCoreIGC->CreateObject(trekClient.m_now, OT_buoy, &db, sizeof(db))); assert (b); SetChatObject(b); SendChat(); b->Release(); } } void PickShip(IshipIGC* pship) { assert(pship); if (m_csComposeState == c_csNotComposing) { //Wasn't composing ... start composing to the individual StartComposing(c_csComposeMouseCommand, &m_chsCommand); } m_pchsCurrent->SetRecipient(CHAT_INDIVIDUAL, pship->GetObjectID(), pship); UpdateComposedChat(); } bool IsComposingCommand() { return (m_csComposeState == c_csComposeMouseCommand || m_csComposeState == c_csComposeCommand); } IbaseIGC* GetPickedObject (void) { return m_pchsCurrent->m_pbaseRecipient; } /* NYI ... twizzle implementation once exact usage is defined void PickModel(ImodelIGC* pmodel, int button) { assert(pmodel); if ((m_csComposeState == c_csComposeCommand) || ((m_csComposeState == c_csComposeChat) && button == 1)) { if (m_csComposeState != c_csComposeCommand) { SetVerb(c_cidGoto); m_csComposeState = c_csComposeCommand; } // make this the object of the command SetChatObject(pmodel); SendChat(); } else { // if the model is a ship, make it the subject of a chat if (pmodel->GetObjectType() == OT_ship) { if (m_csComposeState == c_csNotComposing) StartComposing(c_csComposeCommand); m_pchsCurrent->SetRecipient(CHAT_INDIVIDUAL, pmodel->GetObjectID(), pmodel); UpdateComposedChat(); } } } */ ImodelIGC* ResolvePick(IclusterIGC* pcluster, const Point& pointPick, int button) { CommandID cid = (m_csComposeState == c_csComposeCommand) ? m_cidVerb : c_cidDefault; ChatSubject* pchs = (m_csComposeState == c_csNotComposing) ? &m_chsCommand : m_pchsCurrent; Rect rectImage = GetWindow()->GetViewport()->GetViewRect()->GetValue(); Point point = pointPick - rectImage.Center(); //Loop over all pickable objects in the sector ImodelIGC* pmodelMin = NULL; float distanceMin = FLT_MAX; for (ModelLinkIGC* pml = pcluster->GetPickableModels()->first(); (pml != NULL); pml = pml->next()) { ImodelIGC* pmodel = pml->data(); if (pmodel->GetVisibleF()) { bool bLegal; if (button != 1) bLegal = (pmodel->GetObjectType() == OT_ship) && (pmodel->GetSide() == trekClient.GetSide()); else bLegal = false; if ((button != 0) && (!bLegal)) bLegal = pchs->LegalCommand(cid, pmodel); if (bLegal) { ThingSite* psite = pmodel->GetThingSite(); if (psite) { const Point& pxy = psite->GetScreenPosition(); float r = psite->GetScreenRadius(); if ((pxy.X() + r >= point.X()) && (pxy.X() - r <= point.X()) && (pxy.Y() + r >= point.Y()) && (pxy.Y() - r <= point.Y())) { float dx = pxy.X() - point.X(); float dy = pxy.Y() - point.Y(); float d = dx*dx + dy*dy; if (d <= distanceMin) { pmodelMin = pmodel; distanceMin = d; } } } } } } return pmodelMin; } void PickObject(IclusterIGC* pcluster, const Point& pointPick, int button) { ImodelIGC* pmodelMin = ResolvePick(pcluster, pointPick, button); if (pmodelMin) { if (m_csComposeState != c_csComposeCommand) { if (m_csComposeState == c_csNotComposing) StartComposing(c_csComposeMouseCommand, &m_chsCommand); else m_csComposeState = c_csComposeCommand; SetVerb(c_cidDefault); } SetChatObject(pmodelMin); SendChat(); } else if (m_bRecipientVisible) { if ((m_csComposeState != c_csComposeCommand) || (m_cidVerb == c_cidGoto) || ((m_cidVerb == c_cidBuild) && m_pchsCurrent->ContainsLayer())) { //Pick an arbitrary point in space if (m_csComposeState != c_csComposeCommand) { if (m_csComposeState == c_csNotComposing) StartComposing(c_csComposeMouseCommand, &m_chsCommand); else m_csComposeState = c_csComposeCommand; SetVerb(c_cidDefault); } DataBuoyIGC db; UpdatePick3D(); db.position = m_positionStop; db.clusterID = trekClient.GetCluster()->GetObjectID(); db.type = c_buoyWaypoint; IbuoyIGC* b = (IbuoyIGC*)(trekClient.m_pCoreIGC->CreateObject(trekClient.m_now, OT_buoy, &db, sizeof(db))); assert (b); SetChatObject(b); SendChat(); b->Release(); } } else if (button == 0) { QuitComposing(); } } MouseResult Button(IInputProvider* pprovider, const Point& point, int button, bool bCaptured, bool bInside, bool bDown) { MouseResult rc = MouseResultHit(); IclusterIGC* pcluster = trekClient.GetCluster(); if (pcluster && (m_csComposeState != c_csComposeShell)) { if (bDown) { rc = MouseResultCapture(); m_pointMouseStart = m_pointMouseStop = point; { Camera* pcamera = GetWindow()->GetCamera(); Rect rectImage = GetWindow()->GetViewport()->GetViewRect()->GetValue(); float halfwidth = (0.5f * rectImage.XSize()); const Vector& positionCamera = pcamera->GetPosition(); { Point pointStart = m_pointMouseStart - rectImage.Center(); Vector directionStart = pcamera->TransformImageToDirection(pointStart / halfwidth); //(pc - t * dp).z == 0, solve for t float t = positionCamera.z / directionStart.z; m_positionStart = positionCamera - t * directionStart; } } if (button) m_maskMouseState |= c_maskMouseRightDown; else m_maskMouseState |= c_maskMouseLeftDown; } else if ((m_maskMouseState & (c_maskMouseLeftDown << button)) != 0) { if (button) { m_maskMouseState &= ~c_maskMouseRightDown; PickObject(pcluster, point, 1); } else { m_maskMouseState &= ~c_maskMouseLeftDown; if (m_csComposeState == c_csComposeCommand) PickObject(pcluster, point, 0); else if (LegalRectangle(m_pointMouseStart, m_pointMouseStop)) { ShipListIGC ships; float xMin; float xMax; if (m_pointMouseStart.X() < m_pointMouseStop.X()) { xMin = m_pointMouseStart.X(); xMax = m_pointMouseStop.X(); } else { xMin = m_pointMouseStop.X(); xMax = m_pointMouseStart.X(); } float yMin; float yMax; if (m_pointMouseStart.Y() < m_pointMouseStop.Y()) { yMin = m_pointMouseStart.Y(); yMax = m_pointMouseStop.Y(); } else { yMin = m_pointMouseStop.Y(); yMax = m_pointMouseStart.Y(); } Rect rectImage = GetWindow()->GetViewport()->GetViewRect()->GetValue(); Point pointCenter = rectImage.Center(); xMin -= pointCenter.X(); xMax -= pointCenter.X(); yMin -= pointCenter.Y(); yMax -= pointCenter.Y(); //Find all friendly ships in the selection rectangle IsideIGC* pside = trekClient.GetShip()->GetSide(); for (ShipLinkIGC* psl = pcluster->GetShips()->first(); (psl != NULL); psl = psl->next()) { IshipIGC* pship = psl->data(); if ((pship->GetSide() == pside) && pship->GetVisibleF()) { ThingSite* psite = pship->GetThingSite(); if (psite) { const Point& pxy = psite->GetScreenPosition(); float r = psite->GetScreenRadius(); if ((pxy.X() + r >= xMin) && (pxy.X() - r <= xMax) && (pxy.Y() + r >= yMin) && (pxy.Y() - r <= yMax)) { ships.last(pship); } } } } if (ships.n() != 0) { if (m_csComposeState == c_csNotComposing) StartComposing(c_csComposeMouseCommand, &m_chsCommand); m_pchsCurrent->SetRecipient(CHAT_GROUP, NA, NULL, &ships); UpdateComposedChat(); } else { m_pchsCurrent->SetRecipient(CHAT_NOSELECTION, NA, NULL); QuitComposing(); } } else { //Picked a single point IshipIGC* pship = PickShip(pcluster, point); if (pship) { //Got something ... what do we do with it? if (m_csComposeState == c_csNotComposing) { //Wasn't composing ... start composing to the individual StartComposing(c_csComposeMouseCommand, &m_chsCommand); m_pchsCurrent->SetRecipient(CHAT_INDIVIDUAL, pship->GetObjectID(), pship); } else if (GetKeyState(VK_SHIFT) & 0x8000) { //shift click ... add/remove to the list m_pchsCurrent->ToggleSubject(pship); } else { //normal click reset to the new recipient m_pchsCurrent->SetRecipient(CHAT_INDIVIDUAL, pship->GetObjectID(), pship); } UpdateComposedChat(); } else { //m_pchsCurrent->SetRecipient(CHAT_NOSELECTION, NA, NULL); //NYI why are we doing this? QuitComposing(); } } } if (!m_maskMouseState) rc = MouseResultRelease(); } } return rc; } void MouseMove(IInputProvider* pprovider, const Point& point, bool bCaptured, bool bInside) { m_pointMouseStop = point; } void MouseEnter(IInputProvider* pprovider, const Point& point) { m_bMouseIn = true; } void MouseLeave(IInputProvider* pprovider) { GetWindow()->SetCursor(AWF_CURSOR_DEFAULT); m_bMouseIn = false; } void RenderDropLine(Context* pcontext) { if ((((m_csComposeState != c_csComposeCommand) && (m_maskMouseState & c_maskMouseRightDown)) || ((m_csComposeState == c_csComposeCommand) && m_maskMouseState && ((m_cidVerb == c_cidGoto) || ((m_cidVerb == c_cidBuild) && m_pchsCurrent->ContainsLayer())))) && LegalRectangle(m_pointMouseStart, m_pointMouseStop)) { UpdatePick3D(); TVector vertices; TVector indices; const Color& color = m_positionStop.Z() > 0.0 ? CommandGeo::s_colorDropLineUp : CommandGeo::s_colorDropLineDown; indices.PushEnd(0); vertices.PushEnd(VertexL(m_positionStart.X(), m_positionStart.Y(), m_positionStart.Z(), color.R(), color.G(), color.B(), 1, 0, 0)); indices.PushEnd(1); vertices.PushEnd(VertexL(m_positionStop.X(), m_positionStop.Y(), m_positionStop.Z(), color.R(), color.G(), color.B(), 1, 0, 0)); pcontext->DrawLines(vertices, indices); } } bool DrawSelectionBox(void) const { return (m_maskMouseState & c_maskMouseLeftDown) && (m_csComposeState != c_csComposeCommand) && LegalRectangle(m_pointMouseStart, m_pointMouseStop); } void Render(Context* pcontext) { if (m_bMouseIn) { IclusterIGC* pcluster = trekClient.GetCluster(); if (pcluster) { if (DrawSelectionBox()) { pcontext->DrawRectangle(Rect(m_pointMouseStart, m_pointMouseStop), Color::White()); } //Is the mouse over a legitimate target const char* pszCursor = AWF_CURSOR_DEFAULT; { ImodelIGC* pmodelPick = ResolvePick(pcluster, m_pointMouseStop, -1); if (pmodelPick) { IsideIGC* psidePick = pmodelPick->GetSide(); bool bFriendly = (psidePick == trekClient.GetSide()); if (bFriendly && (pmodelPick->GetObjectType() == OT_ship)) { //Over a friendly ship: always show the select cursor pszCursor = AWF_CURSOR_SELECT; } else { CommandID cid; if (m_csComposeState == c_csComposeCommand) { //Composing a command ... always show the selected order if legal assert (m_pchsCurrent->LegalCommand(m_cidVerb, pmodelPick)); cid = m_cidVerb; } else if (m_pchsCurrent->m_ctRecipient == CHAT_INDIVIDUAL) { //Pick an appropriate default based on the recipient and the object IshipIGC* pship = (IshipIGC*)(IbaseIGC*)(m_pchsCurrent->m_pbaseRecipient); cid = pship->GetDefaultOrder(pmodelPick); } else cid = c_cidDefault; if (cid >= c_cidDefault) pszCursor = c_cdAllCommands[cid].szVerb; else pszCursor = AWF_CURSOR_DEFAULT; } } } GetWindow()->SetCursor(pszCursor); } } } bool IsTakingKeystrokes() { return (m_csComposeState > c_csComposeMouseCommand); } const ShipListIGC* GetSubjects(void) const { return (m_csComposeState != c_csNotComposing) && (m_csComposeState != c_csComposeShell) ? &(m_pchsCurrent->m_shipsSubject) : NULL; } bool OnChar(const KeyState& ks) { if (m_csComposeState > c_csComposeMouseCommand) { trekClient.PlaySoundEffect(chatKeySound); if (ks.bControl && (m_csComposeState != c_csComposeCommand) && m_strTypedText.IsEmpty()) { //Lets assume the 1st char of all commands are unique for (CommandID cid = c_cidAttack; (cid < c_cidMax); cid++) { if (ks.vk == (c_cdAllCommands[cid].szVerb[0] - 96)) { SetComposeState(c_csComposeCommand); SetVerb(cid); SetChatObject(NULL); UpdateComposedChat(); break; } } } else { switch (ks.vk) { case VK_RETURN: if (m_strTypedText.IsEmpty()) { QuitComposing(); } else { SendChat(); } break; case VK_ESCAPE: if (m_csComposeState == c_csComposeCommand) { m_strTypedText.SetEmpty(); SetComposeState(c_csComposeChat); UpdateComposedChat(); } else QuitComposing(); break; case VK_BACK: OnBackspace(); break; case VK_TAB: OnTab(); break; case '~': case '`': QuitComposing(); GetWindow()->OpenMainQuickChatMenu(); break; case '!': { if ((m_csComposeState == c_csComposeChat) && m_strTypedText.IsEmpty()) { m_strTypedText = ZString("!"); SetComposeState(c_csComposeShell); UpdateComposedChat(); break; } } //intentional no break default: OnPrintable(ks.vk); UpdateComposedChat(); break; } } } return true; } void StartChat() { trekClient.PlaySoundEffect(chatKeySound); if (m_csComposeState == c_csNotComposing) StartComposing(c_csComposeChat, &m_chsChat); else SetComposeState(c_csComposeChat); UpdateComposedChat(); } int MatchName(const char* pszText, const char* pszName) { assert (*pszText != '\0'); while (true) { if (*pszText == '\0') { //Ran out of typed text ... return the number of unmatched characters //in pszName return strlen(pszName); } else if (tolower(*pszText) != tolower(*pszName)) { //No match return c_badMatch; } pszText++; pszName++; } } void SetChatObject(ImodelIGC* pmodel) { if (m_pmodelTarget && (m_pmodelTarget->GetObjectType() == OT_buoy)) ((IbuoyIGC*)(ImodelIGC*)m_pmodelTarget)->ReleaseConsumer(); m_pmodelTarget = pmodel; if (pmodel && (pmodel->GetObjectType() == OT_buoy)) ((IbuoyIGC*)pmodel)->AddConsumer(); } void MatchTarget(void) { SetChatObject(NULL); const char* pszText = m_strTypedText; if (*pszText == '\0') return; ImodelIGC* pmodelBest; int scoreBest = c_badMatch; //Any nice big number //Find an acceptable thing to target //First ... try all ships in the universe { for (ShipLinkIGC* psl = trekClient.m_pCoreIGC->GetShips()->first(); (psl != NULL); psl = psl->next()) { IshipIGC* pship = psl->data(); if (m_pchsCurrent->LegalCommand(m_cidVerb, pship)) { int score = MatchName(pszText, pship->GetName()); if (score < scoreBest) { pmodelBest = pship; scoreBest = score; } } } } //Second ... try all known stations if (scoreBest > 0) { for (StationLinkIGC* psl = trekClient.m_pCoreIGC->GetStations()->first(); (psl != NULL); psl = psl->next()) { IstationIGC* pstation = psl->data(); if (m_pchsCurrent->LegalCommand(m_cidVerb, pstation)) { int score = MatchName(pszText, pstation->GetName()); if (score < scoreBest) { pmodelBest = pstation; scoreBest = score; } } } if (scoreBest > 0) { //Third ... try anything in the cluster IclusterIGC* pcluster = trekClient.GetCluster(); if (pcluster) { for (ModelLinkIGC* pml = pcluster->GetPickableModels()->first(); (pml != NULL); pml = pml->next()) { ImodelIGC* pmodel = pml->data(); if (m_pchsCurrent->LegalCommand(m_cidVerb, pmodel)) { int score = MatchName(pszText, GetModelName(pmodel)); if (score < scoreBest) { pmodelBest = pmodel; scoreBest = score; } } } } if (scoreBest > 0) { //Fourth ... try me if (m_pchsCurrent->LegalCommand(m_cidVerb, trekClient.GetShip())) { int score = MatchName(pszText, "me"); if (score < scoreBest) { pmodelBest = trekClient.GetShip(); scoreBest = score; } } if (scoreBest > 0) { //Fifth ... try all clusters IclusterIGC* pcluster = NULL; for (ClusterLinkIGC* pcl = trekClient.m_pCoreIGC->GetClusters()->first(); (pcl != NULL); pcl = pcl->next()) { IclusterIGC* pc = pcl->data(); int score = MatchName(pszText, pc->GetName()); if (score < scoreBest) { pcluster = pc; scoreBest = score; } } if (pcluster) { // create a cluster buoy in that sector DataBuoyIGC db; db.position = Vector(0, 0, 0); db.clusterID = pcluster->GetObjectID(); db.type = c_buoyCluster; pmodelBest = (IbuoyIGC*)(trekClient.m_pCoreIGC->CreateObject(trekClient.m_now, OT_buoy, &db, sizeof(db))); assert (pmodelBest); pmodelBest->Release(); } } } } } if (scoreBest != c_badMatch) SetChatObject(pmodelBest); } void OnBackspace() { assert (m_csComposeState > c_csComposeMouseCommand); if (m_strTypedText.GetLength() != 0) { if ((m_csComposeState == c_csComposeShell) && (m_strTypedText.GetLength() == 1)) { m_strTypedText.SetEmpty(); SetComposeState(c_csComposeChat); } else { m_strTypedText = m_strTypedText.LeftOf(1); if (m_csComposeState == c_csComposeCommand) MatchTarget(); } UpdateComposedChat(); } else if (m_csComposeState == c_csComposeCommand) { SetComposeState(c_csComposeChat); SetChatObject(NULL); } } void CycleChatTarget(void) { if (m_csComposeState != c_csNotComposing) { switch (m_pchsCurrent->m_ctRecipient) { case CHAT_WING: { if (m_csComposeState != c_csComposeChat) { m_pchsCurrent->SetRecipient(CHAT_INDIVIDUAL, NA, trekClient.GetShip()); break; } } //no break ... cant compose a chat to yourself so fall through to case CHAT_INDIVIDUAL: { if (trekClient.GetShip()->GetSourceShip()->GetChildShips()->n() != 0) { m_pchsCurrent->SetRecipient(CHAT_SHIP, NA, NULL); break; } } //no break ... alone in ship case CHAT_SHIP: { m_pchsCurrent->SetRecipient(CHAT_FRIENDLY_SECTOR, NA, NULL); if (m_pchsCurrent->m_shipsSubject.n() != 0) break; } //no break ... no one in the sector, so skip over it. case CHAT_FRIENDLY_SECTOR: { m_pchsCurrent->SetRecipient(CHAT_TEAM, NA, NULL); } break; case CHAT_TEAM: { m_pchsCurrent->SetRecipient(CHAT_EVERYONE, NA, NULL); } break; default: { m_pchsCurrent->SetRecipient(CHAT_WING, NA, NULL); } } } } void OnTab() { assert (m_csComposeState > c_csComposeMouseCommand); const char* pcc = m_strTypedText; if ((*pcc == '\0') || (*pcc == ' ')) { CycleChatTarget(); } else { //Try to match the typed text against a recipient if (MatchName(pcc, "admin") == 0) { SetChatObject(NULL); m_strTypedText.SetEmpty(); m_pchsCurrent->SetRecipient(CHAT_ADMIN, NA, NULL); } else { int scoreBest = c_badMatch; ChatTarget ct = CHAT_NOSELECTION; ObjectID oidRecipient; IbaseIGC* pbaseRecipient; //Try to match against a wing { for (WingID i = 0; (i < c_widMax); i++) { int score = MatchName(pcc, c_pszWingName[i]); if (score < scoreBest) { scoreBest = score; ct = CHAT_WING; oidRecipient = i; pbaseRecipient = NULL; } } } if (scoreBest > 0) { for (ClusterLinkIGC* pcl = trekClient.m_pCoreIGC->GetClusters()->first(); (pcl != NULL); pcl = pcl->next()) { IclusterIGC* pcluster = pcl->data(); int score = MatchName(pcc, pcluster->GetName()); if (score < scoreBest) { scoreBest = score; ct = CHAT_FRIENDLY_SECTOR; oidRecipient = pcluster->GetObjectID(); pbaseRecipient = pcluster; } } if (scoreBest > 0) { //Try to match against your team IsideIGC* pside = trekClient.GetSide(); int score = MatchName(pcc, pside->GetName()); if (score < scoreBest) { scoreBest = score; ct = CHAT_TEAM; oidRecipient = pside->GetObjectID(); pbaseRecipient = pside; } if (scoreBest > 0) { int score = MatchName(pcc, "me"); if (score < scoreBest) { scoreBest = score; ct = CHAT_INDIVIDUAL; oidRecipient = trekClient.GetShipID(); pbaseRecipient = trekClient.GetShip(); } if (scoreBest > 0) { int score = MatchName(pcc, "all"); if (score < scoreBest) { scoreBest = score; ct = CHAT_EVERYONE; oidRecipient = NA; pbaseRecipient = NULL; } if (scoreBest > 0) { if (trekClient.GetShip()->GetSourceShip()->GetChildShips()->n() != 0) { int score = MatchName(pcc, "ship"); if (score < scoreBest) { scoreBest = score; ct = CHAT_SHIP; oidRecipient = NA; pbaseRecipient = NULL; } } if (scoreBest > 0) { //Try to match against an individual on our side IsideIGC* pside = trekClient.GetSide(); for (ShipLinkIGC* psl = pside->GetShips()->first(); (psl != NULL); psl = psl->next()) { IshipIGC* pship = psl->data(); int score = MatchName(pcc, pship->GetName()); if (score < scoreBest) { scoreBest = score; ct = CHAT_INDIVIDUAL; oidRecipient = pship->GetObjectID(); pbaseRecipient = pship; } } // try to match against everyone else if (scoreBest > 0) { IsideIGC* psideMine = trekClient.GetSide(); for (ShipLinkIGC* psl = trekClient.m_pCoreIGC->GetShips()->first(); (psl != NULL); psl = psl->next()) { IshipIGC* pship = psl->data(); if (pship->GetSide() != psideMine) { int score = MatchName(pcc, pship->GetName()); if ((ct == CHAT_INDIVIDUAL) ? (score == 0) : (score < scoreBest)) { scoreBest = score; ct = CHAT_INDIVIDUAL; oidRecipient = pship->GetObjectID(); pbaseRecipient = pship; } } } } if (scoreBest > 0) { //Did not have a perfect match ... try a verb //Try to match against one of the verbs (but don't match default) for (CommandID cid = c_cidAttack; (cid < c_cidMax); cid++) { if (MatchName(pcc, c_cdAllCommands[cid].szVerb) == 0) { SetComposeState(c_csComposeCommand); SetVerb(cid); SetChatObject(NULL); m_strTypedText.SetEmpty(); UpdateComposedChat(); return; } } } } } } } } } if (scoreBest != c_badMatch) { SetChatObject(NULL); m_strTypedText.SetEmpty(); m_pchsCurrent->SetRecipient(ct, oidRecipient, pbaseRecipient); } else { CycleChatTarget(); } } } UpdateComposedChat(); } void OnPrintable(char ch) { assert (m_csComposeState > c_csComposeMouseCommand); m_strTypedText += ZString(ch, 1); if (m_csComposeState == c_csComposeCommand) { MatchTarget(); } } }; ////////////////////////////////////////////////////////////////////////////// // // DisplayImage // ////////////////////////////////////////////////////////////////////////////// class DisplayImage : public WrapImage, public IEventSink { protected: TRef m_pTransformImage; TRef m_pTransform; TRef m_peventSinkOffScreen; bool m_bVisible; bool m_bUndetectable; OverlayMask m_omVisible; protected: Number* GetTime() { return Number::Cast(GetChild(1)); } public: DisplayImage( Number* ptime, RectValue* prect, Image* pimage, int side, const Point& ptOffScreen, const Point& ptOnScreen, float dtimeSlide, OverlayMask omVisible, bool bUndetectable ) : WrapImage(Image::GetEmpty(), ptime), m_bVisible(false), m_omVisible(omVisible), m_bUndetectable(bUndetectable) { m_peventSinkOffScreen = IEventSink::CreateDelegate(this); m_pTransformImage = new TransformImage( pimage, m_pTransform = AnimatedTranslateTransform2::Create( ptime, prect, side, ptOffScreen, ptOnScreen, dtimeSlide ) ); m_pTransform->AddSink(m_peventSinkOffScreen); } ~DisplayImage(void) { m_pTransform->RemoveSink(m_peventSinkOffScreen); } void SetOverlayFlags(OverlayMask om) { Show( (om & m_omVisible) != 0); } void Show(bool bVisible) { if (m_bVisible != bVisible) { if (bVisible) { SetImage(m_pTransformImage); m_pTransform->MoveEnd(); } else { //NYI need ome way to tell the image that it no longer needs to be updated //(e.g. animate a static image moving off screen). m_pTransform->MoveStart(); } m_bVisible = bVisible; } } bool OnEvent(IEventSource* pevent) { SetImage(Image::GetEmpty()); return true; } ////////////////////////////////////////////////////////////////////////////// // // IMouseInput methods // ////////////////////////////////////////////////////////////////////////////// MouseResult HitTest(IInputProvider* pprovider, const Point& point, bool bCaptured) { MouseResult result = WrapImage::HitTest(pprovider, point, bCaptured); if (!m_bVisible || m_bUndetectable) { result.Clear(MouseResultHit()); } return result; } }; ////////////////////////////////////////////////////////////////////////////// // // ConsolePickImage // ////////////////////////////////////////////////////////////////////////////// ConsolePickImage::ConsolePickImage(Viewport* pviewport, ConsoleDataImpl* pconsoleData ) : Image(), m_pviewport(pviewport), m_pconsoleData(pconsoleData), m_mouseresult(MouseResultHit()) { assert (m_pconsoleData); } void ConsolePickImage::SetHitTest(MouseResult mr) { m_mouseresult = mr; } MouseResult ConsolePickImage::HitTest(IInputProvider* pprovider, const Point& point, bool bCaptured) { return m_mouseresult; } MouseResult ConsolePickImage::Button(IInputProvider* pprovider, const Point& point, int button, bool bCaptured, bool bInside, bool bDown) { /* const Rect& rect = m_pviewport->GetViewRect()->GetValue(); Vector direction = m_pviewport->GetCamera()->TransformImageToDirection( (point - rect.Center()) * (2.0f / rect.XSize()) ); */ return m_pconsoleData->Button(pprovider, point, button, bCaptured, bInside, bDown); } void ConsolePickImage::MouseMove(IInputProvider* pprovider, const Point& point, bool bCaptured, bool bInside) { m_pconsoleData->MouseMove(pprovider, point, bCaptured, bInside); } void ConsolePickImage::MouseEnter(IInputProvider* pprovider, const Point& point) { m_pconsoleData->MouseEnter(pprovider, point); } void ConsolePickImage::MouseLeave(IInputProvider* pprovider) { m_pconsoleData->MouseLeave(pprovider); } ////////////////////////////////////////////////////////////////////////////// // // ConsoleImage // ////////////////////////////////////////////////////////////////////////////// class ConsoleImageImpl : public ConsoleImage { private: TRef m_pconsoleData; TVector > m_vpdisplayImages; TRef m_pgroupDisplays; TRef m_pobjectTeamPane; TRef m_pobjectExpandedTeamPane; TRef m_pobjectTeleportPane; TRef m_pgsc; TRef m_pcomboCluster; TRef m_pbuttonBack; TRef m_pickimage; char m_szFileName[c_cbFileName]; public: ConsoleImageImpl(Engine* pengine, Viewport* pviewport) : ConsoleImage(pengine, pviewport) { m_szFileName[0] = '\0'; SetDisplayMDL("dialog"); } ~ConsoleImageImpl(void) { GetWindow()->SetChatListPane(NULL); } void StartChat() { m_pconsoleData->StartChat(); } void StartQuickChat(ChatTarget ctRecipient, ObjectID oidRecipient, IbaseIGC* pbaseRecipient) { m_pconsoleData->StartQuickChat(ctRecipient, oidRecipient, pbaseRecipient); } void SendQuickChat(int idSonicChat, int ttMask, CommandID cid, AbilityBitMask aabm) { m_pconsoleData->SendQuickChat(idSonicChat, ttMask, cid, aabm); } void QuitComposing(void) { m_pconsoleData->QuitComposing(); } void RenderDropLine(Context* pcontext) { if (m_pconsoleData) m_pconsoleData->RenderDropLine(pcontext); } bool DrawSelectionBox(void) const { return m_pconsoleData ? m_pconsoleData->DrawSelectionBox() : false; } void Render(Context* pcontext) { pcontext->PushState(); ConsoleImage::Render(pcontext); pcontext->PopState(); if (m_pconsoleData) m_pconsoleData->Render(pcontext); } void Update(Time now) { m_pconsoleData->Update(now); } void SetOverlayFlags(OverlayMask om) { if (GetOverlayFlags() != om) { trekClient.PlaySoundEffect(paneSlideSound); switch (GetWindow()->GetViewMode()) { case TrekWindow::vmHangar: case TrekWindow::vmLoadout: case TrekWindow::vmOverride: m_pickimage->SetHitTest(MouseResult()); break; default: m_pickimage->SetHitTest(MouseResultHit()); break; } } for (short i = 0; i < m_vpdisplayImages.GetCount(); i++) m_vpdisplayImages[i]->SetOverlayFlags(om); m_pconsoleData->SetOverlayFlags(om); } OverlayMask GetOverlayFlags(void) const { return m_pconsoleData ? m_pconsoleData->GetOverlayFlags() : 0; } void OnSwitchViewMode() { m_pconsoleData->OnSwitchViewMode(); } ConsoleData* GetConsoleData() { return m_pconsoleData; } bool IsComposing() { return m_pconsoleData->IsTakingKeystrokes(); } bool OnChar(const KeyState& ks) { return m_pconsoleData->OnChar(ks); } const ShipListIGC* GetSubjects(void) const { return m_pconsoleData->GetSubjects(); } void CycleChatTarget(void) { m_pconsoleData->CycleChatTarget(); m_pconsoleData->UpdateComposedChat(); } void SetInRange(bool bInRange) { m_pconsoleData->SetInRange(bInRange); } void SetDisplayMDL(const char* pszFileName) { if (strcmp(pszFileName, m_szFileName) == 0) return; assert (strlen(pszFileName) < c_cbFileName); strcpy(m_szFileName, pszFileName); OverlayMask om = GetOverlayFlags(); m_pconsoleData = NULL; m_vpdisplayImages = NULL; m_pgroupDisplays = NULL; m_pobjectTeamPane = NULL; m_pobjectExpandedTeamPane = NULL; m_pobjectTeleportPane = NULL; m_pcomboCluster = NULL; m_pbuttonBack = NULL; m_pickimage = NULL; m_pgroupDisplays = new GroupImage(); m_pconsoleData = new ConsoleDataImpl(this->GetViewport(), pszFileName); TRef pnsDisplays = m_pconsoleData->GetNameSpace(); // // Get Game The State Container // CastTo(m_pgsc, pnsDisplays->FindMember("gameStateContainer")); // // load dialog.mdl which contains the hud dialog descriptions // // back button CastTo(m_pbuttonBack, pnsDisplays->FindMember("backButtonPane")); m_pconsoleData->AddEventTarget(m_pconsoleData->OnBackButton, m_pbuttonBack->GetEventSource()); // // Add all of the loaded displays // IObjectList* plistDisplays; CastTo(plistDisplays, pnsDisplays->FindMember("dialogs")); plistDisplays->GetFirst(); while (plistDisplays->GetCurrent() != NULL) { TRef ppair; CastTo(ppair, plistDisplays->GetCurrent()); TRef pimage; CastTo(pimage, (Value*)ppair->GetNth(0)); int side = (int)GetNumber(ppair->GetNth(1)); TRef ppointOffScreen = PointValue::Cast(ppair->GetNth(2)); TRef ppointOnScreen = PointValue::Cast(ppair->GetNth(3)); float dtimeSlide = GetNumber(ppair->GetNth(4)); OverlayMask cmVisible = (OverlayMask)GetNumber(ppair->GetNth(5)); TRef pbooleanUndetectable = Boolean::Cast(ppair->GetLastNth(6)); CreateDisplayImage( pimage, side, ppointOffScreen->GetValue(), ppointOnScreen->GetValue(), dtimeSlide, cmVisible, pbooleanUndetectable->GetValue() ); plistDisplays->GetNext(); } // // Hook up the team pane // m_pobjectTeleportPane = CreateTeleportPane(GetModeler()); m_pobjectTeamPane = CreateTeamPane(GetModeler()); m_pobjectExpandedTeamPane = CreateExpandedTeamPane(GetModeler(), m_pobjectTeamPane); SetImage(m_pgroupDisplays); SetOverlayFlags(om); //Needs to go last so that it only captures mouse clicks missed by anything else m_pickimage = new ConsolePickImage(this->GetViewport(), m_pconsoleData); m_pgroupDisplays->AddImage(m_pickimage); // // Tell the TrekWindow about the chat pane // ChatListPane* pchatListPane; CastTo(pchatListPane, (Pane*)pnsDisplays->FindMember("chatListPane")); GetWindow()->SetChatListPane(pchatListPane); // unload the namespace so that the displays will go away when we are // finished with them. GetModeler()->UnloadNameSpace(pszFileName); } private: TRef GetGameStateContainer() { return m_pgsc; } TRef CreateDisplayImage( Image* pimage, int side, const Point& ptOffScreen, const Point& ptOnScreen, float dtimeSlide, OverlayMask omVisible, bool bUndetectable ) { TRef pdisplayImage = new DisplayImage( GetWindow()->GetTime(), GetWindow()->GetScreenRectValue(), pimage, side, ptOffScreen, ptOnScreen, dtimeSlide, omVisible, bUndetectable ); m_vpdisplayImages.PushEnd(pdisplayImage); m_pgroupDisplays->AddImage(pdisplayImage); return pdisplayImage; } }; TRef ConsoleImage::Create(Engine* pengine, Viewport* pviewport) { return new ConsoleImageImpl(pengine, pviewport); } void AddChildren(Pane* ppaneParent, ObjectStack& stack) { TRef plist; CastTo(plist, (IObject*)stack.Pop()); plist->GetFirst(); while (plist->GetCurrent() != NULL) { IObjectPair* ppair; CastTo(ppair, plist->GetCurrent()); TRef ppaneChild; CastTo(ppaneChild, ppair->GetFirst() ); TRef ppointChild; CastTo(ppointChild, ppair->GetSecond() ); ppaneParent->InsertAtBottom(ppaneChild); ppaneChild->SetOffset( WinPoint( (int)ppointChild->GetValue().X(), (int)ppointChild->GetValue().Y() ) ); plist->GetNext(); } }