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