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
6#include <game/generated/protocol.h>
7#include <game/mapitems.h>
8#include <game/teamscore.h>
9
10#include <game/server/gamecontext.h>
11#include <game/server/player.h>
12
13static constexpr int gs_PickupPhysSize = 14;
14
15CPickup::CPickup(CGameWorld *pGameWorld, int Type, int SubType, int Layer, int Number) :
16 CEntity(pGameWorld, CGameWorld::ENTTYPE_PICKUP, vec2(0, 0), gs_PickupPhysSize)
17{
18 m_Core = vec2(0.0f, 0.0f);
19 m_Type = Type;
20 m_Subtype = SubType;
21
22 m_Layer = Layer;
23 m_Number = Number;
24
25 GameWorld()->InsertEntity(pEntity: this);
26}
27
28void CPickup::Reset()
29{
30 m_MarkedForDestroy = true;
31}
32
33void CPickup::Tick()
34{
35 Move();
36
37 // Check if a player intersected us
38 CEntity *apEnts[MAX_CLIENTS];
39 int Num = GameWorld()->FindEntities(Pos: m_Pos, Radius: GetProximityRadius() + ms_CollisionExtraSize, ppEnts: apEnts, Max: MAX_CLIENTS, Type: CGameWorld::ENTTYPE_CHARACTER);
40 for(int i = 0; i < Num; ++i)
41 {
42 auto *pChr = static_cast<CCharacter *>(apEnts[i]);
43
44 if(pChr && pChr->IsAlive())
45 {
46 if(m_Layer == LAYER_SWITCH && m_Number > 0 && !Switchers()[m_Number].m_aStatus[pChr->Team()])
47 continue;
48 bool Sound = false;
49 // player picked us up, is someone was hooking us, let them go
50 switch(m_Type)
51 {
52 case POWERUP_HEALTH:
53 if(pChr->Freeze())
54 GameServer()->CreateSound(Pos: m_Pos, Sound: SOUND_PICKUP_HEALTH, Mask: pChr->TeamMask());
55 break;
56
57 case POWERUP_ARMOR:
58 if(pChr->Team() == TEAM_SUPER)
59 continue;
60 for(int j = WEAPON_SHOTGUN; j < NUM_WEAPONS; j++)
61 {
62 if(pChr->GetWeaponGot(Type: j))
63 {
64 pChr->SetWeaponGot(Type: j, Value: false);
65 pChr->SetWeaponAmmo(Type: j, Value: 0);
66 Sound = true;
67 }
68 }
69 pChr->SetNinjaActivationDir(vec2(0, 0));
70 pChr->SetNinjaActivationTick(-500);
71 pChr->SetNinjaCurrentMoveTime(0);
72 if(Sound)
73 {
74 pChr->SetLastWeapon(WEAPON_GUN);
75 GameServer()->CreateSound(Pos: m_Pos, Sound: SOUND_PICKUP_ARMOR, Mask: pChr->TeamMask());
76 }
77 if(pChr->GetActiveWeapon() >= WEAPON_SHOTGUN)
78 pChr->SetActiveWeapon(WEAPON_HAMMER);
79 break;
80
81 case POWERUP_ARMOR_SHOTGUN:
82 if(pChr->Team() == TEAM_SUPER)
83 continue;
84 if(pChr->GetWeaponGot(Type: WEAPON_SHOTGUN))
85 {
86 pChr->SetWeaponGot(Type: WEAPON_SHOTGUN, Value: false);
87 pChr->SetWeaponAmmo(Type: WEAPON_SHOTGUN, Value: 0);
88 pChr->SetLastWeapon(WEAPON_GUN);
89 GameServer()->CreateSound(Pos: m_Pos, Sound: SOUND_PICKUP_ARMOR, Mask: pChr->TeamMask());
90 }
91 if(pChr->GetActiveWeapon() == WEAPON_SHOTGUN)
92 pChr->SetActiveWeapon(WEAPON_HAMMER);
93 break;
94
95 case POWERUP_ARMOR_GRENADE:
96 if(pChr->Team() == TEAM_SUPER)
97 continue;
98 if(pChr->GetWeaponGot(Type: WEAPON_GRENADE))
99 {
100 pChr->SetWeaponGot(Type: WEAPON_GRENADE, Value: false);
101 pChr->SetWeaponAmmo(Type: WEAPON_GRENADE, Value: 0);
102 pChr->SetLastWeapon(WEAPON_GUN);
103 GameServer()->CreateSound(Pos: m_Pos, Sound: SOUND_PICKUP_ARMOR, Mask: pChr->TeamMask());
104 }
105 if(pChr->GetActiveWeapon() == WEAPON_GRENADE)
106 pChr->SetActiveWeapon(WEAPON_HAMMER);
107 break;
108
109 case POWERUP_ARMOR_NINJA:
110 if(pChr->Team() == TEAM_SUPER)
111 continue;
112 pChr->SetNinjaActivationDir(vec2(0, 0));
113 pChr->SetNinjaActivationTick(-500);
114 pChr->SetNinjaCurrentMoveTime(0);
115 break;
116
117 case POWERUP_ARMOR_LASER:
118 if(pChr->Team() == TEAM_SUPER)
119 continue;
120 if(pChr->GetWeaponGot(Type: WEAPON_LASER))
121 {
122 pChr->SetWeaponGot(Type: WEAPON_LASER, Value: false);
123 pChr->SetWeaponAmmo(Type: WEAPON_LASER, Value: 0);
124 pChr->SetLastWeapon(WEAPON_GUN);
125 GameServer()->CreateSound(Pos: m_Pos, Sound: SOUND_PICKUP_ARMOR, Mask: pChr->TeamMask());
126 }
127 if(pChr->GetActiveWeapon() == WEAPON_LASER)
128 pChr->SetActiveWeapon(WEAPON_HAMMER);
129 break;
130
131 case POWERUP_WEAPON:
132
133 if(m_Subtype >= 0 && m_Subtype < NUM_WEAPONS && (!pChr->GetWeaponGot(Type: m_Subtype) || pChr->GetWeaponAmmo(Type: m_Subtype) != -1))
134 {
135 pChr->GiveWeapon(Weapon: m_Subtype);
136
137 if(m_Subtype == WEAPON_GRENADE)
138 GameServer()->CreateSound(Pos: m_Pos, Sound: SOUND_PICKUP_GRENADE, Mask: pChr->TeamMask());
139 else if(m_Subtype == WEAPON_SHOTGUN)
140 GameServer()->CreateSound(Pos: m_Pos, Sound: SOUND_PICKUP_SHOTGUN, Mask: pChr->TeamMask());
141 else if(m_Subtype == WEAPON_LASER)
142 GameServer()->CreateSound(Pos: m_Pos, Sound: SOUND_PICKUP_SHOTGUN, Mask: pChr->TeamMask());
143
144 if(pChr->GetPlayer())
145 GameServer()->SendWeaponPickup(ClientId: pChr->GetPlayer()->GetCid(), Weapon: m_Subtype);
146 }
147 break;
148
149 case POWERUP_NINJA:
150 {
151 // activate ninja on target player
152 pChr->GiveNinja();
153 break;
154 }
155 default:
156 break;
157 };
158 }
159 }
160}
161
162void CPickup::TickPaused()
163{
164}
165
166void CPickup::Snap(int SnappingClient)
167{
168 if(NetworkClipped(SnappingClient))
169 return;
170
171 int SnappingClientVersion = GameServer()->GetClientVersion(ClientId: SnappingClient);
172 bool Sixup = Server()->IsSixup(ClientId: SnappingClient);
173
174 if(SnappingClientVersion < VERSION_DDNET_ENTITY_NETOBJS)
175 {
176 CCharacter *pChar = GameServer()->GetPlayerChar(ClientId: SnappingClient);
177
178 if(SnappingClient != SERVER_DEMO_CLIENT && (GameServer()->m_apPlayers[SnappingClient]->GetTeam() == TEAM_SPECTATORS || GameServer()->m_apPlayers[SnappingClient]->IsPaused()) && GameServer()->m_apPlayers[SnappingClient]->m_SpectatorId != SPEC_FREEVIEW)
179 pChar = GameServer()->GetPlayerChar(ClientId: GameServer()->m_apPlayers[SnappingClient]->m_SpectatorId);
180
181 int Tick = (Server()->Tick() % Server()->TickSpeed()) % 11;
182 if(pChar && pChar->IsAlive() && m_Layer == LAYER_SWITCH && m_Number > 0 && !Switchers()[m_Number].m_aStatus[pChar->Team()] && !Tick)
183 return;
184 }
185
186 GameServer()->SnapPickup(Context: CSnapContext(SnappingClientVersion, Sixup), SnapId: GetId(), Pos: m_Pos, Type: m_Type, SubType: m_Subtype, SwitchNumber: m_Number);
187}
188
189void CPickup::Move()
190{
191 if(Server()->Tick() % (int)(Server()->TickSpeed() * 0.15f) == 0)
192 {
193 int Flags;
194 int index = GameServer()->Collision()->IsMover(x: m_Pos.x, y: m_Pos.y, pFlags: &Flags);
195 if(index)
196 {
197 m_Core = GameServer()->Collision()->CpSpeed(index, Flags);
198 }
199 m_Pos += m_Core;
200 }
201}
202