Zappy GUI
C++ graphic client: renderer, camera, network
Loading...
Searching...
No Matches
RaylibRenderer.cpp
Go to the documentation of this file.
1#define RLIGHTS_IMPLEMENTATION
2#include "RaylibRenderer.hpp"
3
4#include <algorithm>
5#include <cfloat>
6#include <cmath>
7
18
20{
23 _pendingSpeed.reset();
25 SetTraceLogLevel(LOG_WARNING);
26 SetConfigFlags(FLAG_WINDOW_RESIZABLE);
27 InitWindow(800, 600, "Zappy");
28 SetTargetFPS(60);
29
30 if (!TextRenderer::loadFont(std::string(FONT_PATH)))
31 TraceLog(LOG_WARNING, "Failed to load font %s, using default", FONT_PATH.data());
32
33 if (_savedWindow.valid) {
34 SetWindowMonitor(_savedWindow.monitor);
35 SetWindowSize(_savedWindow.width, _savedWindow.height);
36 SetWindowPosition(static_cast<int>(_savedWindow.position.x),
37 static_cast<int>(_savedWindow.position.y));
38 if (_savedWindow.fullscreen) ToggleFullscreen();
39 }
40
41 _cam.init(10, 10);
42
43 _background = std::make_unique<SpaceSkybox>();
44 _background->init();
45
47
48 SetTraceLogLevel(LOG_ERROR);
49 _playerModel = LoadModel(PLAYER_MODEL_PATH.data());
50 SetTraceLogLevel(LOG_WARNING);
51 if (_playerModel.meshCount == 0)
52 throw std::runtime_error("Failed to load player model: " + std::string(PLAYER_MODEL_PATH));
53 for (int i = 0; i < _playerModel.materialCount && i < 6; i++)
54 _playerModelBaseMats[i] = _playerModel.materials[i].maps[MATERIAL_MAP_DIFFUSE].color;
55
56 SetTraceLogLevel(LOG_ERROR);
57 _eggModel = LoadModel(EGG_MODEL_PATH.data());
58 SetTraceLogLevel(LOG_WARNING);
59 if (_eggModel.meshCount == 0)
60 throw std::runtime_error("Failed to load egg model: " + std::string(EGG_MODEL_PATH));
61 // set mat0 to a grayish white color
62 _eggModel.materials[0].maps[MATERIAL_MAP_DIFFUSE].color = {235, 235, 235, 255};
63 for (int i = 0; i < _eggModel.materialCount && i < 2; i++)
64 _eggModelBaseMats[i] = _eggModel.materials[i].maps[MATERIAL_MAP_DIFFUSE].color;
65
66 SetTraceLogLevel(LOG_ERROR);
67 _foodModel = LoadModel(FOOD_MODEL_PATH.data());
68 SetTraceLogLevel(LOG_WARNING);
69 if (_foodModel.meshCount == 0)
70 throw std::runtime_error("Failed to load food model: " + std::string(FOOD_MODEL_PATH));
71
72 SetTraceLogLevel(LOG_ERROR);
73 _crystalModel = LoadModel(CRYSTAL_MODEL_PATH.data());
74 SetTraceLogLevel(LOG_WARNING);
75 if (_crystalModel.meshCount == 0)
76 throw std::runtime_error("Failed to load crystal model: " +
77 std::string(CRYSTAL_MODEL_PATH));
78
80 LoadShader("gui/assets/shaders/lighting.vs", "gui/assets/shaders/lighting.fs");
81 _shaderViewPosLoc = GetShaderLocation(_lightingShader, "viewPos");
82
83 for (int i = 0; i < _foodModel.materialCount; i++)
84 _foodModel.materials[i].shader = _lightingShader;
85 for (int i = 0; i < _crystalModel.materialCount; i++)
86 _crystalModel.materials[i].shader = _lightingShader;
87
88 _sun = CreateLight(LIGHT_DIRECTIONAL, {0.0f, 1000.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, WHITE,
90
91 // Boost ambient so unlit faces aren't black
92 float ambient[4] = {1.0f, 1.0f, 1.0f, 1.0f};
93 SetShaderValue(_lightingShader, GetShaderLocation(_lightingShader, "ambient"), ambient,
94 SHADER_UNIFORM_VEC4);
95}
96
98{
100 _updateSelection(GetFrameTime());
101 _cam.update(GetFrameTime(), _state->world.width, _state->world.height, &_state->world);
102
103 if (_background) {
104 float scaledDelta = GetFrameTime();
105 if (_state && _state->timeUnit > 0) scaledDelta *= (_state->timeUnit / 100.0f);
106 _background->update(scaledDelta);
107 }
108
109 // Update shader camera position for specular lighting
110 float camPos[3] = {_cam.camera().position.x, _cam.camera().position.y,
111 _cam.camera().position.z};
112 SetShaderValue(_lightingShader, _shaderViewPosLoc, camPos, SHADER_UNIFORM_VEC3);
113
114 BeginDrawing();
115 ClearBackground(BLACK);
116
117 BeginMode3D(_cam.camera());
118 _render3D();
119
120 EndMode3D();
121
122 if (_state) {
126 _hudWidget.setTeamColorFunc([this](const std::string& t) { return _getTeamColor(t); });
128 }
129
130 _render2D();
131 EndDrawing();
132}
133
135{
136 if (IsKeyPressed(KEY_F)) {
138 }
139
141
142 {
145 _entityTooltip.setTeamColorFunc([this](const std::string& t) { return _getTeamColor(t); });
148 if (int fid = _entityTooltip.popFollowRequest(); fid != -1) {
149 if (fid == -2) {
151 } else {
152 _cam.startFollow(fid);
153 }
154 }
155
156 _playerPanel.setWorld(_state ? &_state->world : nullptr);
158 if (auto pSel = _playerPanel.getPendingSelection()) {
159 _selection = *pSel;
160 }
161 }
162
164 if (auto speed = _speedSlider.getPendingSpeedChange()) {
165 _pendingSpeed = speed;
166 }
167 }
168
169 int sh = GetScreenHeight();
170 Rectangle panelRect = {10.0f, static_cast<float>(sh - SpeedSlider::PANEL_HEIGHT - 10),
171 static_cast<float>(SpeedSlider::PANEL_WIDTH),
172 static_cast<float>(SpeedSlider::PANEL_HEIGHT)};
173 Vector2 mouse = GetMousePosition();
174 if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && !_cam.isFreecamActive() &&
175 !_cam.isFollowActive() && !CheckCollisionPointRec(mouse, panelRect) &&
178 }
179
180 if (_cam.isFollowActive())
182 }
183
184 if (IsKeyPressed(KEY_L)) {
185 auto lang = I18n::getLanguage();
187 }
188
189 if (IsKeyPressed(KEY_F3)) {
192 }
193}
194
195bool RaylibRenderer::shouldClose() { return WindowShouldClose() || _winScreen.quitRequested(); }
196
197void RaylibRenderer::setDevMode(bool dev, int port, const std::string& machine)
198{
199 _devMode = dev;
200 _devPort = port;
201 _devMachine = machine;
202 _hudWidget.setDevMode(dev, port, machine);
203}
204
206{
207 if (_playerModel.meshCount > 0) UnloadModel(_playerModel);
208 if (_eggModel.meshCount > 0) UnloadModel(_eggModel);
209 if (_foodModel.meshCount > 0) UnloadModel(_foodModel);
210 if (_crystalModel.meshCount > 0) UnloadModel(_crystalModel);
211 if (_lightingShader.id > 0) UnloadShader(_lightingShader);
213 if (_background) _background->unload();
214
215 _savedWindow = {
216 .width = GetScreenWidth(),
217 .height = GetScreenHeight(),
218 .position = GetWindowPosition(),
219 .monitor = GetCurrentMonitor(),
220 .fullscreen = IsWindowFullscreen(),
221 .valid = true,
222 };
223
224 CloseWindow();
225}
226
228{
229 if (_background) _background->draw(_cam.camera());
231
232 for (auto& [id, player] : _state->world.players) {
233 player.visual.update(GetFrameTime());
234 Vector3 worldPos = player.visual.pos;
235 EntityRenderer::drawPlayer(worldPos, _getTeamColor(player.team), player.visual.angle,
237 _drawBehaviorParticles(player.visual);
238 }
239
240 for (auto& [id, player] : _state->world.dyingPlayers) {
241 player.visual.update(GetFrameTime());
242 Vector3 worldPos = player.visual.pos;
243 EntityRenderer::drawPlayer(worldPos, _getTeamColor(player.team), player.visual.angle,
245 PLAYER_MODEL_SIZE * player.visual.scale);
246 _drawBehaviorParticles(player.visual);
247 }
249
251 for (auto& [id, egg] : _state->world.eggs) {
252 egg.visual.update(GetFrameTime());
253 Vector3 worldPos = RenderingHelper::tileToWorld(egg.x, egg.y, _state->world.width,
255 int slot = _tileSlotMap.eggSlot(id);
256 if (slot >= 0) {
257 auto [dx, dz] = TileSlotMap::slotOffset(slot);
258 worldPos.x += dx * TILE_SIZE;
259 worldPos.z += dz * TILE_SIZE;
260 }
261 EntityRenderer::drawEgg(worldPos, _getTeamColor(egg.team), _eggModel, egg.rotation,
262 _eggModelBaseMats, EGG_MODEL_SIZE * egg.visual.scale);
263 _drawBehaviorParticles(egg.visual);
264 }
265
266 for (int x = 0; x < _state->world.width; x++) {
267 for (int y = 0; y < _state->world.height; y++) {
268 const Resources& res = _state->world.at(x, y);
269 auto slotIndices = _tileSlotMap.updateResourceSlots(x, y, res);
270 std::array<float, 7> rotations;
271 for (int i = 0; i < 7; i++) rotations[i] = _tileSlotMap.resourceRotation(x, y, i);
273 res, slotIndices, rotations,
275 TILE_SIZE),
278 }
279 }
280
282}
283
285{
286 for (const auto& b : visual.behaviors) {
287 const auto* ab = dynamic_cast<const ADrawableBehavior*>(b.get());
288 if (!ab) continue;
289 for (const auto& line : ab->getLines()) {
290 if (line.alpha <= 0.0f) continue;
291 Color c = {line.color.r, line.color.g, line.color.b,
292 static_cast<unsigned char>(line.alpha * 255)};
293 DrawLine3D(line.a, line.b, c);
294 }
295 for (const auto& p : ab->getParticles()) {
296 if (!p.active) continue;
297 Color c = {p.color.r, p.color.g, p.color.b, static_cast<unsigned char>(p.alpha * 255)};
298 DrawSphere(p.pos, p.size, c);
299 }
300 }
301}
302
304{
305 for (auto& group : _groupPlayersByVisualProximity()) {
306 std::sort(group.begin(), group.end(),
307 [](const Player* a, const Player* b) { return a->level > b->level; });
308
309 Vector3 worldPos = _groupLabelAnchor(group);
310 worldPos.y = PLAYER_MODEL_SIZE * 2.0f;
311 Vector2 screenPos = GetWorldToScreen(worldPos, _cam.camera());
312
313 auto builder = TooltipRenderer::create()
315 .setBackgroundColor({180, 180, 180, 255})
316 .setBackgroundAlpha(160)
317 .setBorderColor(BLACK)
319 .setPadding(4)
321
322 for (const Player* p : group)
323 builder.addLine(
324 std::string(I18n::get(I18n::Key::PLAYER_HEAD_LEVEL)) + std::to_string(p->level),
325 _getTeamColor(p->team));
326
327 builder.draw(screenPos);
328 }
329
332 _entityTooltip.setTeamColorFunc([this](const std::string& t) { return _getTeamColor(t); });
335
336 if (_state) {
339 [this](const std::string& teamName) { return _getTeamColor(teamName); });
341 }
342
344
345 if (_state && !_state->winnerTeam.empty()) {
350 }
351}
352
358
360{
362
363 switch (_selection.type) {
368 break;
369
371 if (_state->world.players.find(_selection.id) != _state->world.players.end()) {
372 const Player& player = _state->world.players.at(_selection.id);
373 BoundingBox bbox = GetModelBoundingBox(_playerModel);
374 float topY = bbox.max.y * PLAYER_MODEL_SIZE;
375 _drawSelectionArrow(player.visual.pos, topY);
376 }
377 break;
378
380 if (_state->world.eggs.find(_selection.id) != _state->world.eggs.end()) {
381 const Egg& egg = _state->world.eggs.at(_selection.id);
382 Vector3 eggPos = RenderingHelper::tileToWorld(egg.x, egg.y, _state->world.width,
384 int slot = _tileSlotMap.eggSlot(_selection.id);
385 if (slot >= 0) {
386 auto [dx, dz] = TileSlotMap::slotOffset(slot);
387 eggPos.x += dx * TILE_SIZE;
388 eggPos.z += dz * TILE_SIZE;
389 }
390 BoundingBox bbox = GetModelBoundingBox(_eggModel);
391 float topY = bbox.max.y * EGG_MODEL_SIZE * egg.visual.scale;
392 _drawSelectionArrow(eggPos, topY);
393 }
394 break;
395 default:
396 return;
397 }
398}
399
401{
402 auto val = _pendingSpeed;
403 _pendingSpeed.reset();
404 return val;
405}
406
408{
409 if (_teamColors.size() == _state->world.teams.size()) return;
410 _teamColors.clear();
411 for (const auto& teamName : _state->world.teams)
413}
414
415Color RaylibRenderer::_getTeamColor(const std::string& teamName)
416{
417 auto it = _teamColors.find(teamName);
418 if (it != _teamColors.end()) return it->second;
419 // fallback for teams not in tna list (shouldn't happen)
420 return WHITE;
421}
422
423int RaylibRenderer::_getScaledFontSize(int baseFontSize) const
424{
425 // Scale based on height: 600px = 1.0x, 1200px = 2.0x
426 // Clamp between 0.5x and 2.5x
427
428 int screenHeight = GetScreenHeight();
429 float scale = screenHeight / 600.0f;
430 scale = std::max(0.5f, std::min(scale, 2.5f));
431 return static_cast<int>(baseFontSize * scale);
432}
433
447
448void RaylibRenderer::_updateSelection([[maybe_unused]] float deltaTime) {}
449
450std::vector<std::vector<const Player*>> RaylibRenderer::_groupPlayersByVisualProximity() const
451{
452 constexpr float thresh = TILE_SIZE / 4.0f;
453 constexpr float threshSq = thresh * thresh;
454
455 std::vector<const Player*> all;
456 all.reserve(_state->world.players.size());
457 for (const auto& [id, player] : _state->world.players) all.push_back(&player);
458
459 std::vector<bool> assigned(all.size(), false);
460 std::vector<std::vector<const Player*>> groups;
461
462 for (size_t i = 0; i < all.size(); i++) {
463 if (assigned[i]) continue;
464 std::vector<const Player*> group = {all[i]};
465 assigned[i] = true;
466 const Vector3& pi = all[i]->visual.pos;
467 for (size_t j = i + 1; j < all.size(); j++) {
468 if (assigned[j]) continue;
469 const Vector3& pj = all[j]->visual.pos;
470 float dx = pi.x - pj.x;
471 float dz = pi.z - pj.z;
472 bool nearbyVisual = dx * dx + dz * dz < threshSq;
473 bool sameIncant = all[i]->incanting && all[j]->incanting && all[i]->x == all[j]->x &&
474 all[i]->y == all[j]->y;
475 if (nearbyVisual || sameIncant) {
476 group.push_back(all[j]);
477 assigned[j] = true;
478 }
479 }
480 groups.push_back(std::move(group));
481 }
482 return groups;
483}
484
485Vector3 RaylibRenderer::_groupLabelAnchor(const std::vector<const Player*>& group) const
486{
487 if (group.size() > 1) {
488 const Player* ref = group[0];
489 bool allIncantingOnSameTile = ref->incanting;
490 for (size_t k = 1; allIncantingOnSameTile && k < group.size(); k++)
491 allIncantingOnSameTile =
492 group[k]->incanting && group[k]->x == ref->x && group[k]->y == ref->y;
493 if (allIncantingOnSameTile)
494 return RenderingHelper::tileToWorld(ref->x, ref->y, _state->world.width,
496 }
497 return group[0]->visual.pos;
498}
499
500void RaylibRenderer::_drawSelectionArrow(Vector3 basePos, float modelTopY) const
501{
502 // bob up and down using a sine wave
503 float bob = sinf(static_cast<float>(GetTime()) * 4.0f) * 0.08f;
504
505 float arrowBase = basePos.y + modelTopY + 0.05f + bob;
506 float shaftHeight = 0.18f;
507 float shaftRadius = 0.03f;
508 float headHeight = 0.14f;
509 float headRadius = 0.08f;
510
511 // shaft sits above the arrowhead
512 Vector3 shaftBot = {basePos.x, arrowBase + headHeight, basePos.z};
513 DrawCylinder(shaftBot, shaftRadius, shaftRadius, shaftHeight, 8, SELECTION_COLOR);
514
515 // cone: startPos at bottom, wide base there, tip (radius=0) at top → points down toward entity
516 Vector3 coneBottom = {basePos.x, arrowBase, basePos.z};
517 DrawCylinder(coneBottom, headRadius, 0.0f, headHeight, 8, SELECTION_COLOR);
518}
End-game overlay widget.
Base class for behaviors that produce visual drawables: sphere particles and line segments....
const GameState * _state
Definition ARenderer.hpp:18
void init(float worldWidth, float worldHeight)
int followedPlayerId() const
bool isFollowActive() const
bool isFreecamActive() const
void update(float dt, float worldWidth, float worldHeight, const WorldState *world)
void startFollow(int playerId)
const Camera3D & camera() const
static Color getTeamColor(int index)
Returns a unique color for a team based on its index. Will repeat if more teams than palette size.
static void drawEgg(Vector3 &worldPos, Color teamColor, Model &model, float rotation=0.0f, const Color *baseMats=nullptr, float modelSize=0.3f)
Draws an egg at the given world position.
static void drawResources(const Resources &resources, const std::array< int, 7 > &slotIndices, const std::array< float, 7 > &rotations, const Vector3 &tileCenter, float tileSize, Model &foodModel, float foodModelSize, Model &crystalModel, float crystalModelSize, float baseSize=0.15f)
Draws all resources for a tile using precomputed slot indices.
static void drawPlayer(Vector3 &worldPos, Color teamColor, float rotation, Model &model, const Color *baseMats, float modelSize=0.4f)
Draws a player at the given world position.
void setSelection(const SelectionFinder::Selection &sel)
int popFollowRequest()
Returns and clears the pending follow target player id. Positive id = start follow....
void setTeamColorFunc(std::function< Color(const std::string &)> func)
void setFollowActive(bool active)
Tells the widget whether follow mode is currently active (affects button label/color).
void draw(int scaledFontSize) const override
Draws the widget.
bool handleInput() override
Processes user input.
bool isFollowButtonHovered() const
True when the follow button is hovered — use to block raycasts.
void setWorld(const WorldState *world)
unsigned int serverUptimeSeconds
Definition GameState.hpp:23
WorldState world
Definition GameState.hpp:17
int64_t gameDurationTicks
Definition GameState.hpp:21
std::string winnerTeam
Definition GameState.hpp:19
int gameDurationSeconds
Definition GameState.hpp:20
static void drawTiles(int width, int height, float tileSize)
Draws filled tile quads for the entire grid in a checkerboard pattern.
static void drawTileHighlight(int tileX, int tileY, int worldWidth, int worldHeight, float tileSize, Color color, float lineThickness=1.0f)
Draws a highlight outline around a specific tile.
void setServerUptime(unsigned int uptime)
Definition HudWidget.cpp:17
void setTeamColorFunc(std::function< Color(const std::string &)> func)
Definition HudWidget.cpp:18
void setDevMode(bool dev, int port, const std::string &machine)
Definition HudWidget.cpp:10
void draw(int scaledFontSize) const override
Draws the widget.
Definition HudWidget.cpp:25
void setWorld(const WorldState *world)
Definition HudWidget.cpp:9
void setTimeUnit(int timeUnit)
Definition HudWidget.cpp:16
static const char * get(Key key)
Definition I18n.hpp:86
@ PLAYER_HEAD_LEVEL
static void setLanguage(Language lang)
Definition I18n.hpp:83
static Language getLanguage()
Definition I18n.hpp:84
void setTeamColorFunc(std::function< Color(const std::string &)> func)
Sets the team color resolution function.
bool isOpen() const
Checks if panel is currently open.
void draw(int scaledFontSize) const override
Draws the panel and player list.
bool handleInput() override
Processes mouse clicks on player rows.
void setWorld(const WorldState *world)
Sets the world state pointer.
std::optional< SelectionFinder::Selection > getPendingSelection()
Gets and clears any pending selection made by the user.
void render() override
SelectionFinder::Selection _selection
int _getScaledFontSize(int baseFontSize) const
EntityTooltipWidget _entityTooltip
static constexpr std::string_view FOOD_MODEL_PATH
void _updateSelection(float deltaTime)
SpeedSlider _speedSlider
std::unordered_map< std::string, Color > _teamColors
Color _playerModelBaseMats[6]
std::unique_ptr< IBackground > _background
std::optional< int > _pendingSpeed
void _drawSelectionHighlight()
static constexpr float EGG_MODEL_SIZE
std::string _devMachine
static constexpr std::string_view CRYSTAL_MODEL_PATH
Color _eggModelBaseMats[2]
std::vector< std::vector< const Player * > > _groupPlayersByVisualProximity() const
static constexpr std::string_view FONT_PATH
void init() override
Vector3 _groupLabelAnchor(const std::vector< const Player * > &group) const
static constexpr float CRYSTAL_MODEL_SIZE
static constexpr float RESOURCE_SPHERE_BASE_SIZE
static constexpr std::string_view PLAYER_MODEL_PATH
static constexpr float PLAYER_MODEL_SIZE
std::optional< int > getPendingSpeedChange() override
bool shouldClose() override
void _drawSelectionArrow(Vector3 basePos, float modelTopY) const
static constexpr Color SELECTION_COLOR
Color _getTeamColor(const std::string &teamName)
void handleInput() override
WindowSnapshot _savedWindow
void shutdown() override
static constexpr float FOOD_MODEL_SIZE
CameraController _cam
PlayerPanel _playerPanel
void _drawBehaviorParticles(const VisualState &visual)
static constexpr std::string_view EGG_MODEL_PATH
static constexpr float TILE_SIZE
void setDevMode(bool dev, int port, const std::string &machine) override
static constexpr float SELECTION_LINE_THICKNESS
TileSlotMap _tileSlotMap
static Vector3 tileToWorld(int tileX, int tileY, int worldWidth, int worldHeight, float tileSize)
Converts tile coordinates to world coordinates (centered on tile).
static Selection findFromRay(const Ray &ray, const GameState &state, float tileSize, const Model &playerModel, float playerModelSize, const Model &eggModel, float eggModelSize, const TileSlotMap &slotMap)
Performs raycast and finds closest entity.
static Selection getEmptySelection()
Returns an empty selection (type None).
static constexpr int PANEL_HEIGHT
void draw(int scaledFontSize) const override
Draws the slider panel at the bottom-left of the screen.
std::optional< int > getPendingSpeedChange()
Returns and clears any pending speed change.
void syncFromServer(int serverTimeUnit)
Snaps to the nearest step index matching serverTimeUnit. No-op if already initialized or serverTimeUn...
void reset()
Resets slider state. Call on reconnect so it re-syncs from the new server.
static constexpr int PANEL_WIDTH
bool handleInput() override
Processes mouse input.
static bool loadFont(const std::string &path)
static void unloadFont()
static std::pair< float, float > slotOffset(int slotIndex)
Returns the XZ offset pair {dx, dz} for a slot index (0-7).
float resourceRotation(int tileX, int tileY, int resourceType) const
Returns the stable random Y rotation for a resource slot, or 0 if absent.
void syncEggs(const std::unordered_map< int, Egg > &eggs)
Syncs egg slot assignments against the current egg map. Assigns slots to new eggs,...
std::array< int, 7 > updateResourceSlots(int tileX, int tileY, const Resources &resources)
Updates slot assignments for all resource types on a tile. Assigns a slot when count goes 0→nonzero,...
void clear()
Resets all state. Call on reconnect.
int eggSlot(int eggId) const
Returns the slot index for an egg id, or -1 if unknown.
Builder & setFontSize(int size)
Sets the font size for all text.
Builder & setPadding(int padding)
Sets the internal padding.
Builder & setBorderColor(Color color)
Sets the border color.
Builder & addLine(const std::string &text, Color color=WHITE)
Adds a single-color line to the tooltip.
void draw(Vector2 position)
Draws the tooltip at the given position.
Builder & setBackgroundColor(Color color)
Sets the background color (ignoring alpha).
Builder & setBorderThickness(int thickness)
Sets the border thickness.
Builder & setAnchor(Anchor anchor)
Sets the anchor point for positioning.
static Builder create()
Creates a new tooltip builder.
Visual-only state for an entity, driven by behaviors each frame. Logical state (x,...
std::vector< std::unique_ptr< IBehavior > > behaviors
bool quitRequested() const
Returns true once the Quit button has been clicked (latched — stays true).
Definition WinScreen.hpp:52
void setDuration(int seconds, int64_t ticks)
Sets the win duration (server-authoritative, from gwt).
Definition WinScreen.cpp:44
void reset()
Definition WinScreen.hpp:33
void draw(int scaledFontSize) const override
Draws the widget.
Definition WinScreen.cpp:93
void setWinner(const std::string &team, Color color)
Sets the winning team name and highlight color.
Definition WinScreen.cpp:38
bool handleInput() override
Handles button input. Always returns true (overlay blocks all input).
Definition WinScreen.cpp:86
Resources & at(int x, int y)
Accesses the resources at a specific tile coordinate (x, y).
std::vector< std::string > teams
std::unordered_map< int, Player > dyingPlayers
std::unordered_map< int, Egg > eggs
std::unordered_map< int, Player > players
void purgeDyingPlayers() const
int y
int x