1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3#include "pickup.h"
4
5#include "character.h"
6
7#include <generated/protocol.h>
8
9#include <game/client/pickup_data.h>
10#include <game/collision.h>
11#include <game/mapitems.h>
12
13static constexpr int gs_PickupPhysSize = 14;
14
15void CPickup::Tick()
16{
17 Move();
18 // Check if a player intersected us
19 CEntity *apEnts[MAX_CLIENTS];
20 int Num = GameWorld()->FindEntities(Pos: m_Pos, Radius: GetProximityRadius() + ms_CollisionExtraSize, ppEnts: apEnts, Max: MAX_CLIENTS, Type: CGameWorld::ENTTYPE_CHARACTER);
21 for(int i = 0; i < Num; ++i)
22 {
23 auto *pChr = static_cast<CCharacter *>(apEnts[i]);
24 if(pChr)
25 {
26 if(GameWorld()->m_WorldConfig.m_IsVanilla && distance(a: m_Pos, b: pChr->m_Pos) >= (GetProximityRadius() + ms_CollisionExtraSize) * 2) // pickup distance is shorter on vanilla due to using ClosestEntity
27 continue;
28 if(m_Layer == LAYER_SWITCH && m_Number > 0 && m_Number < (int)Switchers().size() && !Switchers()[m_Number].m_aStatus[pChr->Team()])
29 continue;
30 bool CreateSound = false;
31 // player picked us up, is someone was hooking us, let them go
32 switch(m_Type)
33 {
34 case POWERUP_HEALTH:
35 if(!GameWorld()->m_WorldConfig.m_PredictDDRace)
36 continue;
37 pChr->Freeze();
38 break;
39
40 case POWERUP_ARMOR:
41 if(!GameWorld()->m_WorldConfig.m_IsDDRace || !GameWorld()->m_WorldConfig.m_PredictDDRace)
42 continue;
43 if(pChr->IsSuper())
44 continue;
45 for(int j = WEAPON_SHOTGUN; j < NUM_WEAPONS; j++)
46 {
47 if(pChr->GetWeaponGot(Type: j))
48 {
49 pChr->SetWeaponGot(Type: j, Value: false);
50 pChr->SetWeaponAmmo(Type: j, Value: 0);
51 CreateSound = true;
52 }
53 }
54 pChr->SetNinjaActivationDir(vec2(0, 0));
55 pChr->SetNinjaActivationTick(-500);
56 pChr->SetNinjaCurrentMoveTime(0);
57 if(CreateSound)
58 pChr->SetLastWeapon(WEAPON_GUN);
59 if(pChr->GetActiveWeapon() >= WEAPON_SHOTGUN)
60 pChr->SetActiveWeapon(WEAPON_HAMMER);
61 break;
62
63 case POWERUP_ARMOR_SHOTGUN:
64 if(!GameWorld()->m_WorldConfig.m_IsDDRace || !GameWorld()->m_WorldConfig.m_PredictDDRace)
65 continue;
66 if(pChr->Team() == TEAM_SUPER)
67 continue;
68 if(pChr->GetWeaponGot(Type: WEAPON_SHOTGUN))
69 {
70 pChr->SetWeaponGot(Type: WEAPON_SHOTGUN, Value: false);
71 pChr->SetWeaponAmmo(Type: WEAPON_SHOTGUN, Value: 0);
72 pChr->SetLastWeapon(WEAPON_GUN);
73 }
74 if(pChr->GetActiveWeapon() == WEAPON_SHOTGUN)
75 pChr->SetActiveWeapon(WEAPON_HAMMER);
76 break;
77
78 case POWERUP_ARMOR_GRENADE:
79 if(!GameWorld()->m_WorldConfig.m_IsDDRace || !GameWorld()->m_WorldConfig.m_PredictDDRace)
80 continue;
81 if(pChr->Team() == TEAM_SUPER)
82 continue;
83 if(pChr->GetWeaponGot(Type: WEAPON_GRENADE))
84 {
85 pChr->SetWeaponGot(Type: WEAPON_GRENADE, Value: false);
86 pChr->SetWeaponAmmo(Type: WEAPON_GRENADE, Value: 0);
87 pChr->SetLastWeapon(WEAPON_GUN);
88 }
89 if(pChr->GetActiveWeapon() == WEAPON_GRENADE)
90 pChr->SetActiveWeapon(WEAPON_HAMMER);
91 break;
92
93 case POWERUP_ARMOR_NINJA:
94 if(!GameWorld()->m_WorldConfig.m_IsDDRace || !GameWorld()->m_WorldConfig.m_PredictDDRace)
95 continue;
96 if(pChr->Team() == TEAM_SUPER)
97 continue;
98 pChr->SetNinjaActivationDir(vec2(0, 0));
99 pChr->SetNinjaActivationTick(-500);
100 pChr->SetNinjaCurrentMoveTime(0);
101 break;
102
103 case POWERUP_ARMOR_LASER:
104 if(!GameWorld()->m_WorldConfig.m_IsDDRace || !GameWorld()->m_WorldConfig.m_PredictDDRace)
105 continue;
106 if(pChr->Team() == TEAM_SUPER)
107 continue;
108 if(pChr->GetWeaponGot(Type: WEAPON_LASER))
109 {
110 pChr->SetWeaponGot(Type: WEAPON_LASER, Value: false);
111 pChr->SetWeaponAmmo(Type: WEAPON_LASER, Value: 0);
112 pChr->SetLastWeapon(WEAPON_GUN);
113 }
114 if(pChr->GetActiveWeapon() == WEAPON_LASER)
115 pChr->SetActiveWeapon(WEAPON_HAMMER);
116 break;
117
118 case POWERUP_WEAPON:
119 if(m_Subtype >= 0 && m_Subtype < NUM_WEAPONS && (!pChr->GetWeaponGot(Type: m_Subtype) || pChr->GetWeaponAmmo(Type: m_Subtype) != -1))
120 pChr->GiveWeapon(Weapon: m_Subtype);
121 break;
122
123 case POWERUP_NINJA:
124 {
125 // activate ninja on target player
126 pChr->GiveNinja();
127 break;
128 }
129
130 default:
131 break;
132 };
133 }
134 }
135}
136
137void CPickup::Move()
138{
139 if(GameWorld()->GameTick() % (int)(GameWorld()->GameTickSpeed() * 0.15f) == 0)
140 {
141 if(Collision()->MoverSpeed(x: m_Pos.x, y: m_Pos.y, pSpeed: &m_Core))
142 {
143 m_IsCoreActive = true;
144 }
145 m_Pos += m_Core;
146 }
147}
148
149CPickup::CPickup(CGameWorld *pGameWorld, int Id, const CPickupData *pPickup) :
150 CEntity(pGameWorld, CGameWorld::ENTTYPE_PICKUP, vec2(0, 0), gs_PickupPhysSize)
151{
152 m_Pos = pPickup->m_Pos;
153 m_Type = pPickup->m_Type;
154 m_Subtype = pPickup->m_Subtype;
155 m_Core = vec2(0.f, 0.f);
156 m_IsCoreActive = false;
157 m_Id = Id;
158 m_Number = pPickup->m_SwitchNumber;
159 m_Layer = m_Number > 0 ? LAYER_SWITCH : LAYER_GAME;
160 m_Flags = pPickup->m_Flags;
161}
162
163void CPickup::FillInfo(CNetObj_Pickup *pPickup)
164{
165 pPickup->m_X = (int)m_Pos.x;
166 pPickup->m_Y = (int)m_Pos.y;
167 pPickup->m_Type = m_Type;
168 pPickup->m_Subtype = m_Subtype;
169}
170
171bool CPickup::Match(CPickup *pPickup)
172{
173 if(pPickup->m_Type != m_Type || pPickup->m_Subtype != m_Subtype)
174 return false;
175 if(distance(a: pPickup->m_Pos, b: m_Pos) > 2.0f)
176 return false;
177 return true;
178}
179