Line data Source code
1 : #include "core/state/StateManager.hpp"
2 :
3 : #include "core/entities/Weapon.hpp"
4 : #include "core/math/Calc.hpp"
5 : #include "core/physics/Particles.hpp"
6 : #include "core/physics/SoldierSkeletonPhysics.hpp"
7 : #include "core/state/Control.hpp"
8 : #include "core/entities/WeaponParametersFactory.hpp"
9 :
10 : #include "core/types/ItemType.hpp"
11 : #include "spdlog/spdlog.h"
12 : #include <algorithm>
13 : #include <utility>
14 :
15 : namespace Soldank
16 : {
17 : // TODO: Move these somewhere
18 : const int SECOND = 60;
19 : const int GUN_RADIUS = 10;
20 : const int BOW_RADIUS = 20;
21 : const int KIT_RADIUS = 12;
22 : const int STAT_RADIUS = 15;
23 : const int FLAG_RADIUS = 19;
24 : const int FLAG_TIMEOUT = SECOND * 25;
25 : const int GUN_TIMEOUT = SECOND * 20;
26 : // TODO: why the duplication?
27 : const int WAYPOINT_TIMEOUT_SMALL = SECOND * 5 + 20; // = 320
28 : const int WAYPOINT_TIMEOUT_BIG = SECOND * 8; // = 480
29 : const float FLAGTHROW_POWER = 4.225;
30 : const int SOLDIER_START_HEALTH = 150;
31 : const int SOLDIER_DEFAULT_VEST = 100;
32 :
33 0 : WeaponType ItemTypeToWeaponType(ItemType item_type)
34 : {
35 0 : switch (item_type) {
36 0 : case ItemType::USSOCOM:
37 0 : return WeaponType::USSOCOM;
38 0 : case ItemType::DesertEagles:
39 0 : return WeaponType::DesertEagles;
40 0 : case ItemType::MP5:
41 0 : return WeaponType::MP5;
42 0 : case ItemType::Ak74:
43 0 : return WeaponType::Ak74;
44 0 : case ItemType::SteyrAUG:
45 0 : return WeaponType::SteyrAUG;
46 0 : case ItemType::Spas12:
47 0 : return WeaponType::Spas12;
48 0 : case ItemType::Ruger77:
49 0 : return WeaponType::Ruger77;
50 0 : case ItemType::M79:
51 0 : return WeaponType::M79;
52 0 : case ItemType::Barrett:
53 0 : return WeaponType::Barrett;
54 0 : case ItemType::Minimi:
55 0 : return WeaponType::Minimi;
56 0 : case ItemType::Minigun:
57 0 : return WeaponType::Minigun;
58 0 : case ItemType::Bow:
59 0 : return WeaponType::Bow;
60 0 : case ItemType::Knife:
61 0 : return WeaponType::Knife;
62 0 : case ItemType::Chainsaw:
63 0 : return WeaponType::Chainsaw;
64 0 : case ItemType::LAW:
65 0 : return WeaponType::LAW;
66 0 : case ItemType::AlphaFlag:
67 : case ItemType::BravoFlag:
68 : case ItemType::PointmatchFlag:
69 : case ItemType::MedicalKit:
70 : case ItemType::GrenadeKit:
71 : case ItemType::FlamerKit:
72 : case ItemType::PredatorKit:
73 : case ItemType::VestKit:
74 : case ItemType::BerserkKit:
75 : case ItemType::ClusterKit:
76 : case ItemType::Parachute:
77 : case ItemType::M2:
78 0 : break;
79 : }
80 :
81 0 : spdlog::critical("Invalid ItemType to WeaponType conversion");
82 : std::unreachable();
83 : }
84 :
85 0 : StateManager::StateManager(AnimationDataManager& animation_data_manager,
86 0 : std::shared_ptr<ParticleSystem> skeleton)
87 0 : : state_(animation_data_manager, std::move(skeleton))
88 : {
89 0 : }
90 :
91 0 : void StateManager::ChangeSoldierControlActionState(std::uint8_t soldier_id,
92 : ControlActionType control_action_type,
93 : bool new_state)
94 : {
95 0 : Soldier& soldier = GetSoldierRef(soldier_id);
96 0 : auto get_action_field = [](Control& soldier_control,
97 : ControlActionType control_action_type) -> bool& {
98 0 : switch (control_action_type) {
99 0 : case ControlActionType::MoveLeft:
100 0 : return soldier_control.left;
101 0 : case ControlActionType::MoveRight:
102 0 : return soldier_control.right;
103 0 : case ControlActionType::Jump:
104 0 : return soldier_control.up;
105 0 : case ControlActionType::Crouch:
106 0 : return soldier_control.down;
107 0 : case ControlActionType::Fire:
108 0 : return soldier_control.fire;
109 0 : case ControlActionType::UseJets:
110 0 : return soldier_control.jets;
111 0 : case ControlActionType::ChangeWeapon:
112 0 : return soldier_control.change;
113 0 : case ControlActionType::ThrowGrenade:
114 0 : return soldier_control.throw_grenade;
115 0 : case ControlActionType::DropWeapon:
116 0 : return soldier_control.drop;
117 0 : case ControlActionType::Reload:
118 0 : return soldier_control.reload;
119 0 : case ControlActionType::Prone:
120 0 : return soldier_control.prone;
121 0 : case ControlActionType::ThrowFlag:
122 0 : return soldier_control.flag_throw;
123 : }
124 : };
125 0 : bool& target_field_to_change = get_action_field(soldier.control, control_action_type);
126 0 : target_field_to_change = new_state;
127 0 : }
128 :
129 0 : void StateManager::SoldierControlApply(
130 : std::uint8_t soldier_id,
131 : const std::function<void(const Soldier& soldier, Control& control)>& apply_function)
132 : {
133 0 : Soldier& soldier = GetSoldierRef(soldier_id);
134 0 : apply_function(soldier, soldier.control);
135 0 : }
136 :
137 0 : void StateManager::ChangeSoldierMouseMapPosition(std::uint8_t soldier_id,
138 : glm::ivec2 new_mouse_position)
139 : {
140 0 : Soldier& soldier = GetSoldierRef(soldier_id);
141 0 : soldier.control.mouse_aim_x = new_mouse_position.x;
142 0 : soldier.control.mouse_aim_y = new_mouse_position.y;
143 0 : }
144 :
145 0 : void StateManager::SwitchSoldierWeapon(std::uint8_t soldier_id)
146 : {
147 0 : Soldier& soldier = GetSoldierRef(soldier_id);
148 0 : int new_active_weapon = (soldier.active_weapon + 1) % 2;
149 0 : soldier.active_weapon = new_active_weapon;
150 : // weapons[new_active_weapon].start_up_time_count =
151 : // weapons[new_active_weapon].GetWeaponParameters().start_up_time;
152 0 : soldier.weapons[new_active_weapon].ResetStartUpTimeCount();
153 : // weapons[new_active_weapon].reload_time_prev = weapons[new_active_weapon].reload_time_count;
154 0 : soldier.weapons[new_active_weapon].SetReloadTimePrev(
155 0 : soldier.weapons[new_active_weapon].GetReloadTimeCount());
156 0 : }
157 :
158 0 : void StateManager::ChangeSoldierPrimaryWeapon(std::uint8_t soldier_id, WeaponType new_weapon_type)
159 : {
160 0 : Soldier& soldier = GetSoldierRef(soldier_id);
161 0 : auto new_weapon_parameters = WeaponParametersFactory::GetParameters(new_weapon_type, false);
162 0 : soldier.weapons[soldier.active_weapon] = new_weapon_parameters;
163 0 : }
164 :
165 0 : void StateManager::SoldierPickupWeapon(std::uint8_t soldier_id, const Item& item)
166 : {
167 0 : Soldier& soldier = GetSoldierRef(soldier_id);
168 0 : WeaponType new_weapon_type = ItemTypeToWeaponType(item.style);
169 0 : auto new_weapon_parameters = WeaponParametersFactory::GetParameters(new_weapon_type, false);
170 0 : new_weapon_parameters.ammo = item.ammo_count;
171 0 : soldier.weapons[soldier.active_weapon] = new_weapon_parameters;
172 0 : }
173 :
174 0 : void StateManager::SoldierPickupKit(std::uint8_t soldier_id, std::uint8_t item_id)
175 : {
176 0 : Soldier& soldier = GetSoldierRef(soldier_id);
177 0 : Item& item = GetItemRef(item_id);
178 :
179 0 : if (item.style == ItemType::MedicalKit) {
180 0 : if (soldier.health >= SOLDIER_START_HEALTH) {
181 0 : return;
182 : }
183 :
184 0 : soldier.health = SOLDIER_START_HEALTH;
185 0 : item.active = false;
186 : }
187 :
188 0 : if (item.style == ItemType::GrenadeKit) {
189 0 : if (soldier.weapons[2].GetWeaponParameters().kind == WeaponType::ClusterGrenade &&
190 0 : soldier.weapons[2].GetAmmoCount() > 0) {
191 : // Don't pickup normal grenades if Soldier has any Cluster grenades already
192 0 : return;
193 : }
194 :
195 0 : if (soldier.weapons[2].GetAmmoCount() >= soldier.weapons[2].GetWeaponParameters().ammo) {
196 : // Don't pickup if Soldier has all the grenades (or more)
197 0 : return;
198 : }
199 :
200 0 : soldier.weapons[2] = WeaponParametersFactory::GetParameters(WeaponType::FragGrenade, false);
201 0 : item.active = false;
202 : }
203 :
204 0 : if (item.style == ItemType::FlamerKit) {
205 : // TODO
206 : }
207 :
208 0 : if (item.style == ItemType::PredatorKit) {
209 : // TODO
210 : }
211 :
212 0 : if (item.style == ItemType::VestKit) {
213 0 : soldier.vest = SOLDIER_DEFAULT_VEST;
214 0 : item.active = false;
215 : }
216 :
217 0 : if (item.style == ItemType::BerserkKit) {
218 : // TODO
219 : }
220 :
221 0 : if (item.style == ItemType::ClusterKit) {
222 0 : if (soldier.weapons[2].GetWeaponParameters().kind == WeaponType::ClusterGrenade &&
223 0 : soldier.weapons[2].GetAmmoCount() > 0) {
224 : // Don't pickup more cluster grenades if Soldier has already some
225 0 : return;
226 : }
227 :
228 0 : soldier.weapons[2] =
229 0 : WeaponParametersFactory::GetParameters(WeaponType::ClusterGrenade, false);
230 0 : item.active = false;
231 : }
232 : }
233 :
234 0 : void StateManager::MoveSoldier(std::uint8_t soldier_id, const glm::vec2& move_offset)
235 : {
236 0 : Soldier& soldier = GetSoldierRef(soldier_id);
237 0 : glm::vec2 new_soldier_position = soldier.particle.position + move_offset;
238 0 : soldier.particle.position = new_soldier_position;
239 0 : soldier.particle.old_position = new_soldier_position;
240 0 : RepositionSoldierSkeletonParts(soldier);
241 0 : }
242 :
243 0 : void StateManager::SetSoldierPosition(std::uint8_t soldier_id, const glm::vec2& new_position)
244 : {
245 0 : Soldier& soldier = GetSoldierRef(soldier_id);
246 0 : soldier.particle.position = new_position;
247 0 : soldier.particle.old_position = new_position;
248 0 : RepositionSoldierSkeletonParts(soldier);
249 0 : }
250 :
251 0 : void StateManager::ThrowSoldierFlags(std::uint8_t soldier_id)
252 : {
253 0 : Soldier& soldier = GetSoldierRef(soldier_id);
254 0 : for (auto item_it = state_.items.begin(); item_it != state_.items.end(); ++item_it) {
255 0 : if (IsItemTypeFlag(item_it->style)) {
256 0 : glm::vec2 aim_direction = GetSoldierAimDirection(soldier_id);
257 0 : aim_direction = Calc::Vec2Scale(aim_direction, FLAGTHROW_POWER);
258 :
259 0 : glm::vec2 flagger_offset = Calc::Vec2Scale(aim_direction, 5);
260 :
261 0 : glm::vec2 flagger_velocity = aim_direction + soldier.particle.GetVelocity();
262 :
263 0 : glm::vec2 new_pos_diff = flagger_offset + flagger_velocity;
264 0 : glm::vec2 look_point_1 = item_it->skeleton->GetPos(1) + new_pos_diff;
265 0 : glm::vec2 future_point_1 = look_point_1 + glm::vec2{ -10, -8 };
266 0 : glm::vec2 future_point_2 = look_point_1 + glm::vec2{ 10, -8 };
267 0 : glm::vec2 future_point_3 = look_point_1 + glm::vec2{ -10, 8 };
268 0 : glm::vec2 future_point_4 = look_point_1 + glm::vec2{ 10, 8 };
269 :
270 : glm::vec2 perp;
271 0 : float distance = 0.0F;
272 :
273 0 : glm::vec2 look_point_2 = item_it->skeleton->GetPos(2) + new_pos_diff;
274 0 : glm::vec2 look_point_3 = item_it->skeleton->GetPos(3) + new_pos_diff;
275 0 : glm::vec2 look_point_4 = item_it->skeleton->GetPos(4) + new_pos_diff;
276 :
277 0 : if (!state_.map.RayCast(
278 0 : soldier.skeleton->GetPos(15), look_point_2, distance, 200, false, true, false) &&
279 0 : !state_.map.RayCast(
280 0 : soldier.skeleton->GetPos(15), look_point_3, distance, 200, false, true, false) &&
281 0 : !state_.map.RayCast(
282 0 : soldier.skeleton->GetPos(15), look_point_4, distance, 200, false, true, false) &&
283 0 : !state_.map.CollisionTest(future_point_1, perp, true) &&
284 0 : !state_.map.CollisionTest(future_point_2, perp, true) &&
285 0 : !state_.map.CollisionTest(future_point_3, perp, true) &&
286 0 : !state_.map.CollisionTest(future_point_4, perp, true)) {
287 :
288 0 : for (unsigned int j = 1; j <= 4; ++j) {
289 : // Apply offset from flagger
290 0 : item_it->skeleton->SetPos(j, item_it->skeleton->GetPos(j) + flagger_offset);
291 :
292 : // Apply velocities
293 0 : item_it->skeleton->SetPos(j, item_it->skeleton->GetPos(j) + flagger_velocity);
294 0 : item_it->skeleton->SetOldPos(j,
295 0 : item_it->skeleton->GetPos(j) - flagger_velocity);
296 : }
297 :
298 : // Add some spin for visual effect
299 0 : perp = { -flagger_velocity.y, flagger_velocity.x };
300 0 : perp = Calc::Vec2Normalize(perp);
301 0 : perp = Calc::Vec2Scale(perp, soldier.direction);
302 0 : item_it->skeleton->SetPos(1, item_it->skeleton->GetPos(1) - perp);
303 0 : item_it->skeleton->SetPos(2, item_it->skeleton->GetPos(2) + perp);
304 :
305 : // Release the flag
306 0 : item_it->static_type = false;
307 0 : item_it->holding_soldier_id = 0;
308 0 : soldier.is_holding_flags = false;
309 : }
310 : }
311 : }
312 0 : }
313 :
314 0 : glm::vec2 StateManager::GetSoldierAimDirection(std::uint8_t soldier_id)
315 : {
316 0 : Soldier& soldier = GetSoldierRef(soldier_id);
317 0 : glm::vec2 mouse_aim = { soldier.control.mouse_aim_x, soldier.control.mouse_aim_y };
318 :
319 0 : glm::vec2 aim_direction = mouse_aim - soldier.skeleton->GetPos(15);
320 0 : aim_direction = Calc::Vec2Normalize(aim_direction);
321 :
322 0 : return aim_direction;
323 : }
324 :
325 0 : void StateManager::TransformSoldier(std::uint8_t soldier_id,
326 : const std::function<void(Soldier&)>& transform_soldier_function)
327 : {
328 0 : Soldier& soldier = GetSoldierRef(soldier_id);
329 0 : if (!soldier.active) {
330 0 : spdlog::warn("Trying to transform inactive soldier of id: {}", soldier_id);
331 0 : return;
332 : }
333 0 : transform_soldier_function(soldier);
334 : }
335 :
336 0 : void StateManager::TransformSoldiers(
337 : const std::function<void(Soldier&)>& transform_soldier_function)
338 : {
339 0 : for (auto& soldier : state_.soldiers) {
340 0 : if (!soldier.active) {
341 0 : continue;
342 : }
343 :
344 0 : transform_soldier_function(soldier);
345 : }
346 0 : }
347 :
348 0 : const Soldier& StateManager::GetSoldier(std::uint8_t soldier_id) const
349 : {
350 0 : if (soldier_id >= state_.soldiers.size()) {
351 0 : spdlog::critical("Trying to access soldier of invalid id: {}", soldier_id);
352 : std::unreachable();
353 : }
354 :
355 0 : return state_.soldiers.at(soldier_id);
356 : }
357 :
358 0 : const Soldier& StateManager::CreateSoldier(std::optional<unsigned int> force_soldier_id)
359 : {
360 0 : unsigned int new_soldier_id = NAN;
361 :
362 0 : if (!force_soldier_id.has_value()) {
363 0 : unsigned int free_soldier_id = 0;
364 0 : for (const auto& soldier : state_.soldiers) {
365 0 : if (!soldier.active) {
366 0 : break;
367 : }
368 :
369 0 : ++free_soldier_id;
370 : }
371 :
372 0 : new_soldier_id = free_soldier_id;
373 : } else {
374 0 : new_soldier_id = *force_soldier_id;
375 : }
376 :
377 : std::vector<Weapon> weapons{
378 0 : { WeaponParametersFactory::GetParameters(WeaponType::DesertEagles, false) },
379 0 : { WeaponParametersFactory::GetParameters(WeaponType::Knife, false) },
380 0 : { WeaponParametersFactory::GetParameters(WeaponType::FragGrenade, false) }
381 0 : };
382 :
383 0 : spdlog::debug("new_soldier_id {}", new_soldier_id);
384 0 : state_.soldiers.at(new_soldier_id).SetDefaultValues();
385 0 : state_.soldiers.at(new_soldier_id).active = true;
386 0 : state_.soldiers.at(new_soldier_id).id = new_soldier_id;
387 :
388 0 : return state_.soldiers.back();
389 0 : }
390 :
391 0 : glm::vec2 StateManager::SpawnSoldier(unsigned int soldier_id,
392 : std::optional<glm::vec2> spawn_position)
393 : {
394 0 : glm::vec2 initial_player_position{ 0.0F, 0.0F };
395 0 : if (spawn_position.has_value()) {
396 0 : initial_player_position = *spawn_position;
397 : } else {
398 0 : std::vector<glm::vec2> possible_spawn_point_positions;
399 0 : for (const auto& spawn_point : state_.map.GetSpawnPoints()) {
400 0 : if (spawn_point.type == PMSSpawnPointType::General ||
401 0 : spawn_point.type == PMSSpawnPointType::Alpha ||
402 0 : spawn_point.type == PMSSpawnPointType::Bravo ||
403 0 : spawn_point.type == PMSSpawnPointType::Charlie ||
404 0 : spawn_point.type == PMSSpawnPointType::Delta) {
405 :
406 0 : possible_spawn_point_positions.emplace_back(spawn_point.x, spawn_point.y);
407 : }
408 : }
409 :
410 0 : if (possible_spawn_point_positions.empty()) {
411 0 : for (const auto& spawn_point : state_.map.GetSpawnPoints()) {
412 0 : possible_spawn_point_positions.emplace_back(spawn_point.x, spawn_point.y);
413 : }
414 : }
415 :
416 0 : if (!possible_spawn_point_positions.empty()) {
417 : std::uniform_int_distribution<unsigned int> spawnpoint_id_random_distribution(
418 0 : 0, possible_spawn_point_positions.size() - 1);
419 :
420 : unsigned int random_spawnpoint_id =
421 0 : spawnpoint_id_random_distribution(mersenne_twister_engine_);
422 :
423 0 : initial_player_position = possible_spawn_point_positions.at(random_spawnpoint_id);
424 : }
425 0 : }
426 :
427 0 : auto& soldier = GetSoldierRef(soldier_id);
428 0 : soldier.particle.position = initial_player_position;
429 0 : soldier.particle.old_position = initial_player_position;
430 0 : soldier.active = true;
431 0 : soldier.particle.active = true;
432 0 : soldier.health = 150.0;
433 0 : soldier.dead_meat = false;
434 0 : soldier.weapons[0] = WeaponParametersFactory::GetParameters(soldier.weapon_choices[0], false);
435 0 : soldier.weapons[1] = WeaponParametersFactory::GetParameters(soldier.weapon_choices[1], false);
436 0 : soldier.active_weapon = 0;
437 0 : RepositionSoldierSkeletonParts(soldier);
438 :
439 0 : return initial_player_position;
440 : }
441 :
442 0 : void StateManager::ForEachSoldier(
443 : const std::function<void(const Soldier& soldier)>& for_each_soldier_function) const
444 : {
445 0 : for (const Soldier& soldier : state_.soldiers) {
446 0 : if (!soldier.active) {
447 0 : continue;
448 : }
449 :
450 0 : for_each_soldier_function(soldier);
451 : }
452 0 : }
453 :
454 0 : void StateManager::ForSoldier(
455 : std::uint8_t soldier_id,
456 : const std::function<void(const Soldier& soldier)>& for_soldier_function) const
457 : {
458 0 : if (soldier_id >= state_.soldiers.size()) {
459 0 : spdlog::critical("Trying to access soldier of invalid id: {}", soldier_id);
460 : std::unreachable();
461 : }
462 :
463 0 : for_soldier_function(state_.soldiers.at(soldier_id));
464 0 : }
465 :
466 0 : const Soldier* StateManager::FindSoldier(
467 : const std::function<bool(const Soldier& soldier)>& predicate) const
468 : {
469 0 : for (const Soldier& soldier : state_.soldiers) {
470 0 : if (!soldier.active) {
471 0 : continue;
472 : }
473 :
474 0 : if (predicate(soldier)) {
475 0 : return &soldier;
476 : }
477 : }
478 :
479 0 : return nullptr;
480 : }
481 :
482 0 : void StateManager::RemoveSoldier(std::uint8_t soldier_id)
483 : {
484 0 : if (soldier_id >= state_.soldiers.size()) {
485 0 : spdlog::critical("Trying to access soldier of invalid id: {}", soldier_id);
486 : std::unreachable();
487 : }
488 :
489 0 : state_.soldiers.at(soldier_id).active = false;
490 0 : }
491 :
492 0 : std::size_t StateManager::GetSoldiersCount() const
493 : {
494 0 : return state_.soldiers.size();
495 : }
496 :
497 0 : void StateManager::EnqueueNewProjectile(const BulletParams& bullet_params)
498 : {
499 0 : bullet_emitter_.push_back(bullet_params);
500 0 : }
501 :
502 0 : void StateManager::CreateProjectile(const BulletParams& bullet_params)
503 : {
504 0 : bool is_bullet_created = false;
505 0 : for (auto& bullet : state_.bullets) {
506 0 : if (bullet.active) {
507 0 : continue;
508 : }
509 :
510 0 : bullet = bullet_params;
511 0 : bullet.active = true;
512 0 : is_bullet_created = true;
513 :
514 0 : break;
515 : }
516 :
517 0 : if (!is_bullet_created) {
518 0 : spdlog::warn("Could not create a new projectile because the limit is exceeded");
519 : }
520 0 : }
521 :
522 0 : const std::vector<BulletParams>& StateManager::GetBulletEmitter() const
523 : {
524 0 : return bullet_emitter_;
525 : }
526 :
527 0 : void StateManager::ClearBulletEmitter()
528 : {
529 0 : bullet_emitter_.clear();
530 0 : }
531 :
532 0 : void StateManager::ForEachBullet(
533 : const std::function<void(const Bullet& bullet)>& for_each_bullet_function) const
534 : {
535 0 : for (const auto& bullet : state_.bullets) {
536 0 : if (!bullet.active) {
537 0 : continue;
538 : }
539 :
540 0 : for_each_bullet_function(bullet);
541 : }
542 0 : }
543 :
544 0 : std::size_t StateManager::GetBulletsCount() const
545 : {
546 0 : std::size_t bullets_count = 0;
547 :
548 0 : for (const auto& bullet : state_.bullets) {
549 0 : if (!bullet.active) {
550 0 : continue;
551 : }
552 :
553 0 : ++bullets_count;
554 : }
555 :
556 0 : return bullets_count;
557 : }
558 :
559 0 : void StateManager::TransformBullets(
560 : const std::function<void(Bullet& bullet)>& transform_bullet_function)
561 : {
562 0 : for (auto& bullet : state_.bullets) {
563 0 : if (!bullet.active) {
564 0 : continue;
565 : }
566 :
567 0 : transform_bullet_function(bullet);
568 : }
569 0 : }
570 :
571 0 : Item& StateManager::CreateItem(glm::vec2 position, std::uint8_t owner_id, ItemType style)
572 : {
573 0 : std::uint8_t new_id = 0;
574 0 : for (const auto& item : state_.items) {
575 0 : if (!item.active) {
576 0 : break;
577 : }
578 :
579 0 : ++new_id;
580 : }
581 :
582 0 : Item new_item;
583 0 : new_item.active = true;
584 0 : new_item.style = style;
585 0 : new_item.id = new_id;
586 0 : new_item.holding_soldier_id = 0;
587 0 : new_item.owner = owner_id;
588 0 : new_item.time_out = 0;
589 : // new_item.skeleton = std::make_shared<ParticleSystem>();
590 0 : new_item.static_type = false;
591 0 : new_item.in_base = false;
592 0 : new_item.flipped = false;
593 :
594 0 : for (std::uint8_t& i : new_item.collide_count) {
595 0 : i = 0;
596 : }
597 :
598 0 : if (owner_id != 255 && owner_id != 0) {
599 0 : Soldier& soldier = GetSoldierRef(owner_id);
600 0 : if (soldier.direction == -1) {
601 0 : new_item.flipped = true;
602 : }
603 : }
604 :
605 : // TODO: handle this better
606 0 : float particle_scale = 1.0F;
607 :
608 0 : switch (style) {
609 0 : case ItemType::USSOCOM:
610 0 : particle_scale = 1.0F;
611 0 : break;
612 0 : case ItemType::DesertEagles:
613 0 : particle_scale = 1.1F;
614 0 : break;
615 0 : case ItemType::Knife:
616 0 : particle_scale = 1.8F;
617 0 : break;
618 0 : case ItemType::MedicalKit:
619 : case ItemType::GrenadeKit:
620 : case ItemType::FlamerKit:
621 : case ItemType::PredatorKit:
622 : case ItemType::VestKit:
623 : case ItemType::BerserkKit:
624 : case ItemType::ClusterKit:
625 0 : particle_scale = 2.15F;
626 0 : break;
627 0 : case ItemType::MP5:
628 0 : particle_scale = 2.2F;
629 0 : break;
630 0 : case ItemType::M79:
631 : case ItemType::Chainsaw:
632 : case ItemType::LAW:
633 0 : particle_scale = 2.8F;
634 0 : break;
635 0 : case ItemType::Spas12:
636 : case ItemType::Ruger77:
637 0 : particle_scale = 3.6F;
638 0 : break;
639 0 : case ItemType::Ak74:
640 : case ItemType::SteyrAUG:
641 0 : particle_scale = 3.7F;
642 0 : break;
643 0 : case ItemType::Minimi:
644 0 : particle_scale = 3.9F;
645 0 : break;
646 0 : case ItemType::AlphaFlag:
647 : case ItemType::BravoFlag:
648 : case ItemType::PointmatchFlag:
649 : case ItemType::M2:
650 0 : particle_scale = 4.0F;
651 0 : break;
652 0 : case ItemType::Barrett:
653 0 : particle_scale = 4.3F;
654 0 : break;
655 0 : case ItemType::Bow:
656 : case ItemType::Parachute:
657 0 : particle_scale = 5.0F;
658 0 : break;
659 0 : case ItemType::Minigun:
660 0 : particle_scale = 5.5F;
661 0 : break;
662 : }
663 :
664 0 : switch (style) {
665 0 : case ItemType::AlphaFlag:
666 : case ItemType::BravoFlag:
667 : case ItemType::PointmatchFlag: {
668 0 : new_item.skeleton = ParticleSystem::Load(ParticleSystemType::Flag, particle_scale);
669 0 : new_item.radius = FLAG_RADIUS;
670 0 : new_item.time_out = FLAG_TIMEOUT;
671 0 : new_item.collide_with_bullets = true;
672 : // TODO: inf flag doesn't collide
673 0 : break;
674 : }
675 0 : case ItemType::DesertEagles:
676 : case ItemType::MP5:
677 : case ItemType::Ak74:
678 : case ItemType::SteyrAUG:
679 : case ItemType::Spas12:
680 : case ItemType::Ruger77:
681 : case ItemType::M79:
682 : case ItemType::Barrett:
683 : case ItemType::Minimi:
684 : case ItemType::Minigun:
685 : case ItemType::USSOCOM:
686 : case ItemType::Knife:
687 : case ItemType::Chainsaw:
688 : case ItemType::LAW:
689 : case ItemType::Bow: // TODO: bow has different condition
690 0 : new_item.skeleton = ParticleSystem::Load(ParticleSystemType::Weapon, particle_scale);
691 : // new_item.skeleton->VDamping = 0.989;
692 : // new_item.skeleton->GravityMultiplier = 1.07;
693 0 : new_item.radius = GUN_RADIUS;
694 0 : new_item.time_out = GUN_TIMEOUT;
695 : // new_item.interest : = DEFAULT_INTEREST_TIME;
696 0 : new_item.collide_with_bullets = true; // TODO: sv_guns_collide.Value;
697 0 : break;
698 0 : case ItemType::FlamerKit:
699 : case ItemType::PredatorKit:
700 : case ItemType::BerserkKit:
701 : case ItemType::MedicalKit:
702 : case ItemType::ClusterKit:
703 : case ItemType::VestKit:
704 : case ItemType::GrenadeKit:
705 0 : new_item.skeleton = ParticleSystem::Load(ParticleSystemType::Kit, particle_scale);
706 : // new_item.skeleton->VDamping = 0.989;
707 : // new_item.skeleton->GravityMultiplier = 1.07;
708 0 : new_item.radius = KIT_RADIUS;
709 0 : new_item.time_out = FLAG_TIMEOUT; // TODO
710 : // new_item.interest : = DEFAULT_INTEREST_TIME;
711 0 : new_item.collide_with_bullets = true; // TODO: sv_kits_collide.Value;
712 0 : break;
713 0 : case ItemType::Parachute:
714 0 : new_item.skeleton = ParticleSystem::Load(ParticleSystemType::Parachute, particle_scale);
715 0 : new_item.time_out = 3600;
716 0 : break;
717 0 : case ItemType::M2:
718 : new_item.skeleton =
719 0 : ParticleSystem::Load(ParticleSystemType::StationaryGun, particle_scale);
720 0 : new_item.time_out = 60;
721 0 : new_item.radius = STAT_RADIUS;
722 0 : new_item.collide_with_bullets = false;
723 0 : break;
724 : }
725 :
726 0 : if (new_id >= state_.items.size()) {
727 0 : spdlog::critical("Trying to create a new item but array is full, id: {}", new_id);
728 : }
729 :
730 0 : state_.items.at(new_id) = new_item;
731 0 : SetItemPosition(new_id, position);
732 :
733 0 : return state_.items.back();
734 0 : }
735 :
736 0 : void StateManager::SetItemPosition(unsigned int id, glm::vec2 new_position)
737 : {
738 0 : Item& item = state_.items.at(id);
739 0 : glm::vec2 direction = new_position - item.skeleton->GetPos(1);
740 0 : MoveItemIntoDirection(id, direction);
741 0 : }
742 :
743 0 : void StateManager::MoveItemIntoDirection(unsigned int id, glm::vec2 direction)
744 : {
745 0 : Item& item = state_.items.at(id);
746 0 : for (unsigned int i = 1; i <= item.skeleton->GetParticles().size(); ++i) {
747 0 : glm::vec2 new_position = item.skeleton->GetPos(i) + direction;
748 0 : item.skeleton->SetPos(i, new_position);
749 0 : item.skeleton->SetOldPos(i, new_position);
750 : }
751 0 : }
752 :
753 0 : void StateManager::TransformItems(const std::function<void(Item& item)>& transform_item_function)
754 : {
755 0 : for (auto& item : state_.items) {
756 0 : if (!item.active) {
757 0 : continue;
758 : }
759 :
760 0 : transform_item_function(item);
761 : }
762 0 : }
763 :
764 0 : void StateManager::ForEachItem(
765 : const std::function<void(const Item& item)>& for_each_item_function) const
766 : {
767 0 : for (const Item& item : state_.items) {
768 0 : if (!item.active) {
769 0 : continue;
770 : }
771 :
772 0 : for_each_item_function(item);
773 : }
774 0 : }
775 :
776 0 : Soldier& StateManager::GetSoldierRef(std::uint8_t soldier_id)
777 : {
778 0 : if (soldier_id >= state_.soldiers.size()) {
779 0 : spdlog::critical("Trying to access soldier of invalid id: {}", soldier_id);
780 : std::unreachable();
781 : }
782 :
783 0 : return state_.soldiers.at(soldier_id);
784 : }
785 :
786 0 : Item& StateManager::GetItemRef(std::uint8_t item_id)
787 : {
788 0 : if (item_id >= state_.items.size()) {
789 0 : spdlog::critical("Trying to access item of invalid id: {}", item_id);
790 : std::unreachable();
791 : }
792 :
793 0 : return state_.items.at(item_id);
794 : }
795 : } // namespace Soldank
|