#include "StarTeamBar.hpp" #include "StarMainInterface.hpp" #include "StarJsonExtra.hpp" #include "StarRoot.hpp" #include "StarUniverseClient.hpp" #include "StarGuiReader.hpp" #include "StarButtonWidget.hpp" #include "StarTeamClient.hpp" #include "StarImageWidget.hpp" #include "StarProgressWidget.hpp" #include "StarTextBoxWidget.hpp" #include "StarLabelWidget.hpp" #include "StarPlayer.hpp" #include "StarAssets.hpp" #include "StarWorldClient.hpp" #include "StarPortraitWidget.hpp" #include "StarMathCommon.hpp" namespace Star { TeamBar::TeamBar(MainInterface* mainInterface, UniverseClientPtr client) { m_mainInterface = mainInterface; m_client = client; m_guiContext = GuiContext::singletonPtr(); auto assets = Root::singleton().assets(); m_teamInvite = make_shared(this); m_teamInvitation = make_shared(this); m_teamMemberMenu = make_shared(this); m_nameStyle = assets->json("/interface.config:teamBarNameStyle"); m_nameStyle.fontSize = assets->json("/interface.config:font.nameSize").toInt(); m_nameOffset = jsonToVec2F(assets->json("/interface.config:nameOffset")); GuiReader reader; reader.registerCallback("inviteButton", [this](Widget*) { inviteButton(); }); reader.registerCallback("showSelfMenu", [this](Widget*) { if (!m_client->teamClient()->isMemberOfTeam()) return; auto position = jsonToVec2I(Root::singleton().assets()->json("/interface/windowconfig/teambar.config:selfMenuOffset")); position[1] += windowHeight() / m_guiContext->interfaceScale(); showMemberMenu(m_client->mainPlayer()->clientContext()->playerUuid(), position); }); reader.construct(assets->json("/interface/windowconfig/teambar.config:paneLayout"), this); m_healthBar = fetchChild("healthBar"); m_energyBar = fetchChild("energyBar"); m_foodBar = fetchChild("foodBar"); m_nameLabel = fetchChild("name"); m_energyBarColor = jsonToColor(assets->json("/interface/windowconfig/teambar.config:energyBarColor")); m_energyBarRegenMixColor = jsonToColor(assets->json("/interface/windowconfig/teambar.config:energyBarRegenMixColor")); m_energyBarUnusableColor = jsonToColor(assets->json("/interface/windowconfig/teambar.config:energyBarUnusableColor")); m_energyBar->setColor(m_energyBarColor); auto playerPortrait = fetchChild("portrait"); playerPortrait->setEntity(as(m_client->mainPlayer())); updatePlayerResources(); disableScissoring(); } bool TeamBar::sendEvent(InputEvent const& event) { if (event.is() && (event.get().mouseButton == MouseButton::Left || event.get().mouseButton == MouseButton::Right)) { if (m_teamMemberMenu->isDisplayed() && !m_teamMemberMenu->inMember(*context()->mousePosition(event))) m_teamMemberMenu->dismiss(); } return Pane::sendEvent(event); } void TeamBar::invitePlayer(String const& playerName) { m_client->teamClient()->invitePlayer(playerName); } void TeamBar::acceptInvitation(Uuid const& inviterUuid) { m_client->teamClient()->acceptInvitation(inviterUuid); } void TeamBar::update(float dt) { Pane::update(dt); updatePlayerResources(); auto teamClient = m_client->teamClient(); if (!m_teamInvitation->active()) { if (teamClient->hasInvitationPending()) { auto invitation = teamClient->pullInvitation(); m_teamInvitation->open(invitation.first, invitation.second); if (!m_teamInvitation->isDisplayed()) m_mainInterface->paneManager()->displayPane(PaneLayer::Window, m_teamInvitation); } } if (teamClient->currentTeam() && !teamClient->isTeamLeader()) m_teamInvite->dismiss(); fetchChild("leader")->setVisibility(teamClient->isTeamLeader()); buildTeamBar(); } void TeamBar::updatePlayerResources() { auto player = m_client->mainPlayer(); m_healthBar->setCurrentProgressLevel(player->healthPercentage()); m_energyBar->setCurrentProgressLevel(player->energyPercentage()); if (player->modeConfig().hunger) { m_foodBar->setCurrentProgressLevel(player->foodPercentage()); auto assets = Root::singleton().assets(); if (player->foodPercentage() <= assets->json("/player.config:foodLowThreshold").toFloat()) { float flashTime = assets->json("/interface/windowconfig/teambar.config:foodBarFlashTime").toFloat(); if (fmod(Time::monotonicTime(), flashTime * 2) < flashTime) m_foodBar->setOverlay(assets->json("/interface/windowconfig/teambar.config:foodBarFlashOverlay").toString()); else m_foodBar->setOverlay(""); } else { m_foodBar->setOverlay(""); } m_foodBar->show(); } else { m_foodBar->hide(); } if (player->energyLocked()) { m_energyBar->setColor(m_energyBarUnusableColor); } else { m_energyBar->setColor(m_energyBarColor.mix(m_energyBarRegenMixColor, player->energyRegenBlockPercent())); } m_nameLabel->setText(player->name()); } void TeamBar::inviteButton() { if (!m_teamInvite->isDisplayed()) m_mainInterface->paneManager()->displayPane(PaneLayer::Window, m_teamInvite); } void TeamBar::buildTeamBar() { auto teamClient = m_client->teamClient(); auto player = m_client->mainPlayer(); auto list = fetchChild("list"); auto assets = Root::singleton().assets(); Vec2I offset; size_t controlIndex = 0; float portraitScale = assets->json("/interface/windowconfig/teambar.config:memberPortraitScale").toFloat(); int memberSize = assets->json("/interface/windowconfig/teambar.config:memberSize").toInt(); int memberSpacing = assets->json("/interface/windowconfig/teambar.config:memberSpacing").toInt(); Uuid myUuid = player->clientContext()->playerUuid(); for (auto member : teamClient->members()) { if (member.uuid == myUuid) { continue; } String cellName = toString(controlIndex); WidgetPtr cell = list->fetchChild(cellName); if (!cell) { GuiReader reader; cell = make_shared(); cell->disableScissoring(); cell->markAsContainer(); reader.registerCallback("showMemberMenu", [this](Widget* widget) { auto position = widget->screenPosition() + jsonToVec2I(Root::singleton().assets()->json("/interface/windowconfig/teambar.config:memberMenuOffset")); showMemberMenu(Uuid(widget->parent()->data().toString()), position); }); reader.construct(assets->json("/interface/windowconfig/teambar.config:entry"), cell.get()); list->addChild(cellName, cell); } offset[1] -= memberSize; cell->setPosition(offset); cell->setData(member.uuid.hex()); cell->show(); if (!teamClient->isTeamLeader(member.uuid)) cell->fetchChild("leader")->hide(); else cell->fetchChild("leader")->show(); List drawables = member.portrait; Drawable::scaleAll(drawables, portraitScale); cell->fetchChild("portrait")->setDrawables(std::move(drawables)); if (member.world == m_client->playerWorld() && m_client->worldClient()) { auto mpos = member.position; if (auto entity = m_client->worldClient()->entity(member.entity)) mpos = entity->position(); auto direction = m_client->worldClient()->geometry().diff(mpos, player->position()); auto compassImage = cell->fetchChild("compass"); compassImage->setRotation(direction.angle() - Constants::pi / 2.0f); compassImage->show(); cell->fetchChild("compassoffworld")->hide(); } else { cell->fetchChild("compass")->hide(); cell->fetchChild("compassoffworld")->show(); } cell->fetchChild("healthBar")->setCurrentProgressLevel(member.healthPercentage); cell->fetchChild("energyBar")->setCurrentProgressLevel(member.energyPercentage); offset[1] -= memberSpacing; controlIndex++; } auto inviteButton = fetchChild("inviteButton"); auto noInviteImage = fetchChild("noInviteImage"); Vec2I inviteOffset = list->position() + offset; inviteButton->setPosition(inviteOffset - Vec2I{0, inviteButton->size()[1]}); noInviteImage->setPosition(inviteOffset - Vec2I{0, noInviteImage->size()[1]}); bool couldInvite = (!teamClient->currentTeam() || teamClient->isTeamLeader()) && m_client->teamClient()->members().size() < Root::singleton().configuration()->get("maxTeamSize").toUInt(); inviteButton->setVisibility(couldInvite); inviteButton->setEnabled(!m_teamInvitation->active()); noInviteImage->setVisibility(!couldInvite); while (true) { String cellName = toString(controlIndex); WidgetPtr cell = list->fetchChild(cellName); if (!cell) break; cell->hide(); controlIndex++; } } void TeamBar::showMemberMenu(Uuid memberUuid, Vec2I position) { m_teamMemberMenu->open(memberUuid, position); if (!m_teamMemberMenu->isDisplayed()) m_mainInterface->paneManager()->displayPane(PaneLayer::Window, m_teamMemberMenu); } TeamInvite::TeamInvite(TeamBar* owner) { m_owner = owner; GuiReader reader; auto assets = Root::singleton().assets(); reader.registerCallback("ok", [this](Widget*) { ok(); }); reader.registerCallback("close", [this](Widget*) { close(); }); reader.registerCallback("name", [](Widget*) {}); reader.construct(assets->json("/interface/windowconfig/teaminvite.config:paneLayout"), this); dismiss(); } void TeamInvite::show() { Pane::show(); fetchChild("name")->setText("", false); fetchChild("name")->focus(); } void TeamInvite::ok() { m_owner->invitePlayer(fetchChild("name")->getText()); dismiss(); } void TeamInvite::close() { dismiss(); } TeamInvitation::TeamInvitation(TeamBar* owner) { m_owner = owner; GuiReader reader; auto assets = Root::singleton().assets(); reader.registerCallback("ok", [this](Widget*) { ok(); }); reader.registerCallback("close", [this](Widget*) { close(); }); reader.construct(assets->json("/interface/windowconfig/teaminvitation.config:paneLayout"), this); dismiss(); } void TeamInvitation::open(Uuid const& inviterUuid, String const& inviterName) { if (active()) return; m_inviterUuid = inviterUuid; fetchChild("inviterName")->setText(inviterName); Pane::show(); } void TeamInvitation::ok() { m_owner->acceptInvitation(m_inviterUuid); dismiss(); } void TeamInvitation::close() { dismiss(); } TeamMemberMenu::TeamMemberMenu(TeamBar* owner) { m_owner = owner; auto assets = Root::singleton().assets(); GuiReader reader; reader.registerCallback("beamToShip", [this](Widget*) { beamToShip(); }); reader.registerCallback("close", [this](Widget*) { close(); }); reader.registerCallback("makeLeader", [this](Widget*) { makeLeader(); }); reader.registerCallback("removeFromTeam", [this](Widget*) { removeFromTeam(); }); reader.construct(assets->json("/interface/windowconfig/teammembermenu.config:paneLayout"), this); } void TeamMemberMenu::open(Uuid memberUuid, Vec2I position) { if (active()) return; auto assets = Root::singleton().assets(); setPosition(position); m_memberUuid = memberUuid; auto members = m_owner->m_client->teamClient()->members(); for (auto member : members) { if (member.uuid == m_memberUuid) { fetchChild("name")->setText(member.name); break; } } updateWidgets(); Pane::show(); } void TeamMemberMenu::update(float dt) { auto stillValid = false; auto members = m_owner->m_client->teamClient()->members(); for (auto member : members) { if (member.uuid == m_memberUuid) { stillValid = true; m_canBeam = member.warpMode != WarpMode::None && m_owner->m_client->canBeamToTeamShip(); } } if (!stillValid) { close(); return; } updateWidgets(); Pane::update(dt); } void TeamMemberMenu::updateWidgets() { bool isLeader = m_owner->m_client->teamClient()->isTeamLeader(); bool isSelf = m_owner->m_client->mainPlayer()->clientContext()->playerUuid() == m_memberUuid; fetchChild("beamToShip")->setEnabled(m_canBeam); fetchChild("makeLeader")->setEnabled(isLeader && !isSelf); fetchChild("removeFromTeam")->setEnabled(isLeader || isSelf); auto assets = Root::singleton().assets(); if (isSelf) fetchChild("removeFromTeam")->setText(assets->json("/interface/windowconfig/teammembermenu.config:removeSelfText").toString()); else fetchChild("removeFromTeam")->setText(assets->json("/interface/windowconfig/teammembermenu.config:removeOtherText").toString()); } void TeamMemberMenu::beamToShip() { if (m_canBeam) m_owner->m_mainInterface->warpTo(WarpToWorld{ClientShipWorldId(m_memberUuid), {}}); dismiss(); } void TeamMemberMenu::close() { dismiss(); } void TeamMemberMenu::makeLeader() { m_owner->m_client->teamClient()->makeLeader(m_memberUuid); dismiss(); } void TeamMemberMenu::removeFromTeam() { m_owner->m_client->teamClient()->removeFromTeam(m_memberUuid); dismiss(); } }