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