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 PICKUP_PHYSICS_RADIUS = 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 if(pChr->Freeze())
38 GameWorld()->CreatePredictedSound(Pos: m_Pos, SoundId: SOUND_PICKUP_HEALTH, Id: pChr->GetCid());
39 break;
40
41 case POWERUP_ARMOR:
42 if(!GameWorld()->m_WorldConfig.m_IsDDRace || !GameWorld()->m_WorldConfig.m_PredictDDRace)
43 continue;
44 if(pChr->IsSuper())
45 continue;
46 for(int j = WEAPON_SHOTGUN; j < NUM_WEAPONS; j++)
47 {
48 if(pChr->GetWeaponGot(Type: j))
49 {
50 pChr->SetWeaponGot(Type: j, Value: false);
51 pChr->SetWeaponAmmo(Type: j, Value: 0);
52 CreateSound = true;
53 }
54 }
55 pChr->SetNinjaActivationDir(vec2(0, 0));
56 pChr->SetNinjaActivationTick(-500);
57 pChr->SetNinjaCurrentMoveTime(0);
58 if(CreateSound)
59 {
60 pChr->SetLastWeapon(WEAPON_GUN);
61 GameWorld()->CreatePredictedSound(Pos: m_Pos, SoundId: SOUND_PICKUP_ARMOR, Id: pChr->GetCid());
62 }
63 if(pChr->GetActiveWeapon() >= WEAPON_SHOTGUN)
64 pChr->SetActiveWeapon(WEAPON_HAMMER);
65 break;
66
67 case POWERUP_ARMOR_SHOTGUN:
68 if(!GameWorld()->m_WorldConfig.m_IsDDRace || !GameWorld()->m_WorldConfig.m_PredictDDRace)
69 continue;
70 if(pChr->Team() == TEAM_SUPER)
71 continue;
72 if(pChr->GetWeaponGot(Type: WEAPON_SHOTGUN))
73 {
74 pChr->SetWeaponGot(Type: WEAPON_SHOTGUN, Value: false);
75 pChr->SetWeaponAmmo(Type: WEAPON_SHOTGUN, Value: 0);
76 pChr->SetLastWeapon(WEAPON_GUN);
77 GameWorld()->CreatePredictedSound(Pos: m_Pos, SoundId: SOUND_PICKUP_ARMOR, Id: pChr->GetCid());
78 }
79 if(pChr->GetActiveWeapon() == WEAPON_SHOTGUN)
80 pChr->SetActiveWeapon(WEAPON_HAMMER);
81 break;
82
83 case POWERUP_ARMOR_GRENADE:
84 if(!GameWorld()->m_WorldConfig.m_IsDDRace || !GameWorld()->m_WorldConfig.m_PredictDDRace)
85 continue;
86 if(pChr->Team() == TEAM_SUPER)
87 continue;
88 if(pChr->GetWeaponGot(Type: WEAPON_GRENADE))
89 {
90 pChr->SetWeaponGot(Type: WEAPON_GRENADE, Value: false);
91 pChr->SetWeaponAmmo(Type: WEAPON_GRENADE, Value: 0);
92 pChr->SetLastWeapon(WEAPON_GUN);
93 GameWorld()->CreatePredictedSound(Pos: m_Pos, SoundId: SOUND_PICKUP_ARMOR, Id: pChr->GetCid());
94 }
95 if(pChr->GetActiveWeapon() == WEAPON_GRENADE)
96 pChr->SetActiveWeapon(WEAPON_HAMMER);
97 break;
98
99 case POWERUP_ARMOR_NINJA:
100 if(!GameWorld()->m_WorldConfig.m_IsDDRace || !GameWorld()->m_WorldConfig.m_PredictDDRace)
101 continue;
102 if(pChr->Team() == TEAM_SUPER)
103 continue;
104 pChr->SetNinjaActivationDir(vec2(0, 0));
105 pChr->SetNinjaActivationTick(-500);
106 pChr->SetNinjaCurrentMoveTime(0);
107 break;
108
109 case POWERUP_ARMOR_LASER:
110 if(!GameWorld()->m_WorldConfig.m_IsDDRace || !GameWorld()->m_WorldConfig.m_PredictDDRace)
111 continue;
112 if(pChr->Team() == TEAM_SUPER)
113 continue;
114 if(pChr->GetWeaponGot(Type: WEAPON_LASER))
115 {
116 pChr->SetWeaponGot(Type: WEAPON_LASER, Value: false);
117 pChr->SetWeaponAmmo(Type: WEAPON_LASER, Value: 0);
118 pChr->SetLastWeapon(WEAPON_GUN);
119 GameWorld()->CreatePredictedSound(Pos: m_Pos, SoundId: SOUND_PICKUP_ARMOR, Id: pChr->GetCid());
120 }
121 if(pChr->GetActiveWeapon() == WEAPON_LASER)
122 pChr->SetActiveWeapon(WEAPON_HAMMER);
123 break;
124
125 case POWERUP_WEAPON:
126 if(m_Subtype >= 0 && m_Subtype < NUM_WEAPONS && (!pChr->GetWeaponGot(Type: m_Subtype) || pChr->GetWeaponAmmo(Type: m_Subtype) != -1))
127 {
128 pChr->GiveWeapon(Weapon: m_Subtype);
129
130 if(GameWorld()->m_WorldConfig.m_IsDDRace && GameWorld()->m_WorldConfig.m_PredictDDRace)
131 {
132 if(m_Subtype == WEAPON_GRENADE)
133 GameWorld()->CreatePredictedSound(Pos: m_Pos, SoundId: SOUND_PICKUP_GRENADE, Id: pChr->GetCid());
134 else if(m_Subtype == WEAPON_SHOTGUN)
135 GameWorld()->CreatePredictedSound(Pos: m_Pos, SoundId: SOUND_PICKUP_SHOTGUN, Id: pChr->GetCid());
136 else if(m_Subtype == WEAPON_LASER)
137 GameWorld()->CreatePredictedSound(Pos: m_Pos, SoundId: SOUND_PICKUP_SHOTGUN, Id: pChr->GetCid());
138 }
139 }
140 break;
141
142 case POWERUP_NINJA:
143 {
144 // activate ninja on target player
145 pChr->GiveNinja();
146 break;
147 }
148
149 default:
150 break;
151 };
152 }
153 }
154}
155
156void CPickup::Move()
157{
158 if(GameWorld()->GameTick() % (int)(GameWorld()->GameTickSpeed() * 0.15f) == 0)
159 {
160 if(Collision()->MoverSpeed(x: m_Pos.x, y: m_Pos.y, pSpeed: &m_Core))
161 {
162 m_IsCoreActive = true;
163 }
164 m_Pos += m_Core;
165 }
166}
167
168CPickup::CPickup(CGameWorld *pGameWorld, int Id, const CPickupData *pPickup) :
169 CEntity(pGameWorld, CGameWorld::ENTTYPE_PICKUP, vec2(0, 0), PICKUP_PHYSICS_RADIUS)
170{
171 m_Pos = pPickup->m_Pos;
172 m_Type = pPickup->m_Type;
173 m_Subtype = pPickup->m_Subtype;
174 m_Core = vec2(0.f, 0.f);
175 m_IsCoreActive = false;
176 m_Id = Id;
177 m_Number = pPickup->m_SwitchNumber;
178 m_Layer = m_Number > 0 ? LAYER_SWITCH : LAYER_GAME;
179 m_Flags = pPickup->m_Flags;
180}
181
182void CPickup::FillInfo(CNetObj_Pickup *pPickup)
183{
184 pPickup->m_X = (int)m_Pos.x;
185 pPickup->m_Y = (int)m_Pos.y;
186 pPickup->m_Type = m_Type;
187 pPickup->m_Subtype = m_Subtype;
188}
189
190bool CPickup::Match(CPickup *pPickup)
191{
192 if(pPickup->m_Type != m_Type || pPickup->m_Subtype != m_Subtype)
193 return false;
194 if(distance(a: pPickup->m_Pos, b: m_Pos) > 2.0f)
195 return false;
196 return true;
197}
198