osb/source/frontend/StarTeamBar.cpp
Kae 4b0bc220e4 Support for changing the game's timescale
Context-specific (like per-world) timescales can also be added later
2023-07-21 00:58:49 +10:00

397 lines
13 KiB
C++

#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<TeamInvite>(this);
m_teamInvitation = make_shared<TeamInvitation>(this);
m_teamMemberMenu = make_shared<TeamMemberMenu>(this);
m_nameFontSize = 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()->serverUuid(), position);
});
reader.construct(assets->json("/interface/windowconfig/teambar.config:paneLayout"), this);
m_healthBar = fetchChild<ProgressWidget>("healthBar");
m_energyBar = fetchChild<ProgressWidget>("energyBar");
m_foodBar = fetchChild<ProgressWidget>("foodBar");
m_nameLabel = fetchChild<LabelWidget>("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<PortraitWidget>("portrait");
playerPortrait->setEntity(as<PortraitEntity>(m_client->mainPlayer()));
updatePlayerResources();
disableScissoring();
}
bool TeamBar::sendEvent(InputEvent const& event) {
if (event.is<MouseButtonDownEvent>()
&& (event.get<MouseButtonDownEvent>().mouseButton == MouseButton::Left || event.get<MouseButtonDownEvent>().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<ImageWidget>("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("");
}
} 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(m_client->mainPlayer()->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;
size_t memberIndex = 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()->serverUuid();
for (auto member : teamClient->members()) {
if (member.uuid == myUuid) {
memberIndex++;
continue;
}
String cellName = toString(controlIndex);
WidgetPtr cell = list->fetchChild(cellName);
if (!cell) {
GuiReader reader;
cell = make_shared<Widget>();
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<ImageWidget>("leader")->hide();
else
cell->fetchChild<ImageWidget>("leader")->show();
List<Drawable> drawables = member.portrait;
Drawable::scaleAll(drawables, portraitScale);
cell->fetchChild<ImageWidget>("portrait")->setDrawables(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<ImageWidget>("compass");
compassImage->setRotation(direction.angle() - Constants::pi / 2.0f);
compassImage->show();
cell->fetchChild<ImageWidget>("compassoffworld")->hide();
} else {
cell->fetchChild<ImageWidget>("compass")->hide();
cell->fetchChild<ImageWidget>("compassoffworld")->show();
}
cell->fetchChild<ProgressWidget>("healthBar")->setCurrentProgressLevel(member.healthPercentage);
cell->fetchChild<ProgressWidget>("energyBar")->setCurrentProgressLevel(member.energyPercentage);
offset[1] -= memberSpacing;
controlIndex++;
memberIndex++;
}
auto inviteButton = fetchChild<ButtonWidget>("inviteButton");
auto noInviteImage = fetchChild<ImageWidget>("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<TextBoxWidget>("name")->setText("", false);
fetchChild<TextBoxWidget>("name")->focus();
}
void TeamInvite::ok() {
m_owner->invitePlayer(fetchChild<TextBoxWidget>("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<LabelWidget>("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<LabelWidget>("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()->serverUuid() == m_memberUuid;
fetchChild<ButtonWidget>("beamToShip")->setEnabled(m_canBeam);
fetchChild<ButtonWidget>("makeLeader")->setEnabled(isLeader && !isSelf);
fetchChild<ButtonWidget>("removeFromTeam")->setEnabled(isLeader || isSelf);
auto assets = Root::singleton().assets();
if (isSelf)
fetchChild<ButtonWidget>("removeFromTeam")->setText(assets->json("/interface/windowconfig/teammembermenu.config:removeSelfText").toString());
else
fetchChild<ButtonWidget>("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();
}
}