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 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(m_Subtype == WEAPON_GRENADE)
131 GameWorld()->CreatePredictedSound(Pos: m_Pos, SoundId: SOUND_PICKUP_GRENADE, Id: pChr->GetCid());
132 else if(m_Subtype == WEAPON_SHOTGUN)
133 GameWorld()->CreatePredictedSound(Pos: m_Pos, SoundId: SOUND_PICKUP_SHOTGUN, Id: pChr->GetCid());
134 else if(m_Subtype == WEAPON_LASER)
135 GameWorld()->CreatePredictedSound(Pos: m_Pos, SoundId: SOUND_PICKUP_SHOTGUN, Id: pChr->GetCid());
136 }
137 break;
138
139 case POWERUP_NINJA:
140 {
141 // activate ninja on target player
142 pChr->GiveNinja();
143 break;
144 }
145
146 default:
147 break;
148 };
149 }
150 }
151}
152
153void CPickup::Move()
154{
155 if(GameWorld()->GameTick() % (int)(GameWorld()->GameTickSpeed() * 0.15f) == 0)
156 {
157 if(Collision()->MoverSpeed(x: m_Pos.x, y: m_Pos.y, pSpeed: &m_Core))
158 {
159 m_IsCoreActive = true;
160 }
161 m_Pos += m_Core;
162 }
163}
164
165CPickup::CPickup(CGameWorld *pGameWorld, int Id, const CPickupData *pPickup) :
166 CEntity(pGameWorld, CGameWorld::ENTTYPE_PICKUP, vec2(0, 0), gs_PickupPhysSize)
167{
168 m_Pos = pPickup->m_Pos;
169 m_Type = pPickup->m_Type;
170 m_Subtype = pPickup->m_Subtype;
171 m_Core = vec2(0.f, 0.f);
172 m_IsCoreActive = false;
173 m_Id = Id;
174 m_Number = pPickup->m_SwitchNumber;
175 m_Layer = m_Number > 0 ? LAYER_SWITCH : LAYER_GAME;
176 m_Flags = pPickup->m_Flags;
177}
178
179void CPickup::FillInfo(CNetObj_Pickup *pPickup)
180{
181 pPickup->m_X = (int)m_Pos.x;
182 pPickup->m_Y = (int)m_Pos.y;
183 pPickup->m_Type = m_Type;
184 pPickup->m_Subtype = m_Subtype;
185}
186
187bool CPickup::Match(CPickup *pPickup)
188{
189 if(pPickup->m_Type != m_Type || pPickup->m_Subtype != m_Subtype)
190 return false;
191 if(distance(a: pPickup->m_Pos, b: m_Pos) > 2.0f)
192 return false;
193 return true;
194}
195