Zappy GUI
C++ graphic client: renderer, camera, network
Loading...
Searching...
No Matches
IncantationBehavior.cpp
Go to the documentation of this file.
2
3#include <cmath>
4#include <cstdlib>
5
6static float smoothstep(float t)
7{
8 t = t < 0.0f ? 0.0f : (t > 1.0f ? 1.0f : t);
9 return t * t * (3.0f - 2.0f * t);
10}
11
12static float lerpf(float a, float b, float t) { return a + (b - a) * t; }
13
14static Vector3 lerpv(Vector3 a, Vector3 b, float t)
15{
16 return {lerpf(a.x, b.x, t), lerpf(a.y, b.y, t), lerpf(a.z, b.z, t)};
17}
18
19// Shortest-path angle lerp
20static float lerpAngle(float a, float b, float t)
21{
22 float diff = b - a;
23 while (diff > PI) diff -= 2.0f * PI;
24 while (diff < -PI) diff += 2.0f * PI;
25 return a + diff * t;
26}
27
29 int totalPlayers, float tileCenterX, float tileCenterZ,
30 float tileSize, float serverTickRate)
31 : _visual(visual),
32 _player(player),
33 _isParticleOwner(playerIndex == 0),
34 _duration(300.0f / serverTickRate)
35{
36 _tileCenter = {tileCenterX, 0.0f, tileCenterZ};
37
38 float angle = playerIndex * (2.0f * PI / static_cast<float>(totalPlayers));
39 float radius = tileSize * CIRCLE_RADIUS;
40 _slotPos = {tileCenterX + std::cos(angle) * radius, 0.0f,
41 tileCenterZ + std::sin(angle) * radius};
42
43 _startPos = visual.pos;
44 _startAngle = visual.angle;
45
46 float dx = _slotPos.x - tileCenterX;
47 float dz = _slotPos.z - tileCenterZ;
48 // Model faces West at 0°, CW rotation. toAngle: W=0,S=90,E=180,N=270.
49 // atan2(-dz,-dx) maps slot direction (away from center) to model angle correctly.
50 _targetFaceAngle = std::atan2(dz, dx) * (180.0f / PI);
51}
52
54{
55 static const Color COLORS[] = {
56 {220, 220, 255, 200}, // soft white-blue
57 {180, 140, 255, 200}, // violet
58 {140, 220, 255, 200}, // cyan
59 };
60
61 _particles.clear();
62 for (int i = 0; i < PARTICLE_COUNT; i++) {
63 float angle = (static_cast<float>(static_cast<double>(rand()) / RAND_MAX)) * 2.0f * PI;
64 float spread = (static_cast<float>(static_cast<double>(rand()) / RAND_MAX)) * 0.15f;
65
66 Particle p;
67 p.pos = {_tileCenter.x + std::cos(angle) * spread, 0.1f,
68 _tileCenter.z + std::sin(angle) * spread};
69 p.vel = {(static_cast<float>(static_cast<double>(rand()) / RAND_MAX) - 0.5f) * 0.05f,
70 0.15f + (static_cast<float>(static_cast<double>(rand()) / RAND_MAX)) * 0.2f,
71 (static_cast<float>(static_cast<double>(rand()) / RAND_MAX) - 0.5f) * 0.05f};
72 p.color = COLORS[rand() % 3];
73 p.size = 0.03f + (static_cast<float>(static_cast<double>(rand()) / RAND_MAX)) * 0.04f;
74 p.alpha = 0.8f;
75 p.delay = (static_cast<float>(static_cast<double>(rand()) / RAND_MAX)) * 0.4f;
76 p.active = false;
77 _particles.push_back(p);
78 }
79 _particlesSpawned = true;
80}
81
83{
84 for (auto& p : _particles) {
85 if (!p.active) {
86 if (_elapsed >= p.delay) {
87 p.active = true;
88 p.alpha = 0.8f;
89 }
90 continue;
91 }
92 p.pos.x += p.vel.x * dt;
93 p.pos.y += p.vel.y * dt;
94 p.pos.z += p.vel.z * dt;
95 p.alpha -= dt * 0.35f;
96
97 // Recycle particle back to center when faded
98 if (p.alpha <= 0.0f) {
99 float angle = (static_cast<float>(static_cast<double>(rand()) / RAND_MAX)) * 2.0f * PI;
100 float spread = (static_cast<float>(static_cast<double>(rand()) / RAND_MAX)) * 0.15f;
101 p.pos = {_tileCenter.x + std::cos(angle) * spread, 0.1f,
102 _tileCenter.z + std::sin(angle) * spread};
103 p.vel.x = (static_cast<float>(static_cast<double>(rand()) / RAND_MAX) - 0.5f) * 0.05f;
104 p.vel.y = 0.15f + (static_cast<float>(static_cast<double>(rand()) / RAND_MAX)) * 0.2f;
105 p.vel.z = (static_cast<float>(static_cast<double>(rand()) / RAND_MAX) - 0.5f) * 0.05f;
106 p.alpha = 0.8f;
107 }
108 }
109}
110
112{
113 _elapsed += dt;
114
115 if (_phase == Phase::SPREAD) {
119
120 if (_elapsed >= SPREAD_DURATION) {
124 }
125 return;
126 }
127
128 if (_phase == Phase::HOLD) {
129 // Capture saved angle once (after TurnBehavior has finished)
130 if (!_angleSaved) {
132 _angleSaved = true;
133 }
136
137 if (_isParticleOwner) {
140 }
141
142 if (_player.incantationEnded) {
144 _elapsedEnd = 0.0f;
145 }
146 return;
147 }
148
149 // Phase::END
150 _elapsedEnd += dt;
154
155 if (_isParticleOwner) _particles.clear();
156}
157
159{
161 _particles.clear();
162 return true;
163 }
164 return false;
165}
static float smoothstep(float t)
static float lerpf(float a, float b, float t)
static float lerpAngle(float a, float b, float t)
static Vector3 lerpv(Vector3 a, Vector3 b, float t)
std::vector< Particle > _particles
void update(float dt) override
static constexpr float CIRCLE_RADIUS
static constexpr int PARTICLE_COUNT
void _updateParticles(float dt)
static constexpr float END_DURATION
bool isDone() const override
Returns true when the behavior has completed and can be removed.
static constexpr float SPREAD_DURATION
IncantationBehavior(VisualState &visual, Player &player, int playerIndex, int totalPlayers, float tileCenterX, float tileCenterZ, float tileSize, float serverTickRate)
Visual-only state for an entity, driven by behaviors each frame. Logical state (x,...
float alpha
Definition DrawTypes.hpp:10
Vector3 pos
Definition DrawTypes.hpp:6
Vector3 vel
Definition DrawTypes.hpp:7
bool active
Definition DrawTypes.hpp:12
float delay
Definition DrawTypes.hpp:11
Color color
Definition DrawTypes.hpp:8
float size
Definition DrawTypes.hpp:9