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
4#include "projectile_data.h"
5
6#include <engine/shared/snapshot.h>
7#include <game/client/prediction/gameworld.h>
8#include <game/generated/protocol.h>
9
10#include <game/collision.h>
11
12bool UseProjectileExtraInfo(const CNetObj_Projectile *pProj)
13{
14 return pProj->m_VelY >= 0 && (pProj->m_VelY & LEGACYPROJECTILEFLAG_IS_DDNET) != 0;
15}
16
17CProjectileData ExtractProjectileInfo(int NetObjType, const void *pData, CGameWorld *pGameWorld, const CNetObj_EntityEx *pEntEx)
18{
19 CNetObj_Projectile *pProj = (CNetObj_Projectile *)pData;
20
21 if(NetObjType == NETOBJTYPE_DDNETPROJECTILE)
22 {
23 return ExtractProjectileInfoDDNet(pProj: (CNetObj_DDNetProjectile *)pData);
24 }
25 else if(NetObjType == NETOBJTYPE_DDRACEPROJECTILE || (NetObjType == NETOBJTYPE_PROJECTILE && UseProjectileExtraInfo(pProj)))
26 {
27 return ExtractProjectileInfoDDRace(pProj: (CNetObj_DDRaceProjectile *)pData, pGameWorld, pEntEx);
28 }
29
30 CProjectileData Result = {.m_StartPos: vec2(0, 0)};
31 Result.m_StartPos.x = pProj->m_X;
32 Result.m_StartPos.y = pProj->m_Y;
33 Result.m_StartVel.x = pProj->m_VelX / 100.0f;
34 Result.m_StartVel.y = pProj->m_VelY / 100.0f;
35 Result.m_Type = pProj->m_Type;
36 Result.m_StartTick = pProj->m_StartTick;
37 Result.m_ExtraInfo = false;
38 Result.m_Owner = -1;
39 Result.m_TuneZone = pGameWorld && pGameWorld->m_WorldConfig.m_UseTuneZones ? pGameWorld->Collision()->IsTune(Index: pGameWorld->Collision()->GetMapIndex(Pos: Result.m_StartPos)) : 0;
40 Result.m_SwitchNumber = pEntEx ? pEntEx->m_SwitchNumber : 0;
41 return Result;
42}
43
44CProjectileData ExtractProjectileInfoDDRace(const CNetObj_DDRaceProjectile *pProj, CGameWorld *pGameWorld, const CNetObj_EntityEx *pEntEx)
45{
46 CProjectileData Result = {.m_StartPos: vec2(0, 0)};
47
48 Result.m_StartPos.x = pProj->m_X / 100.0f;
49 Result.m_StartPos.y = pProj->m_Y / 100.0f;
50 float Angle = pProj->m_Angle / 1000000.0f;
51 Result.m_StartVel.x = std::sin(x: -Angle);
52 Result.m_StartVel.y = std::cos(x: -Angle);
53 Result.m_Type = pProj->m_Type;
54 Result.m_StartTick = pProj->m_StartTick;
55
56 Result.m_ExtraInfo = true;
57 Result.m_Owner = pProj->m_Data & 255;
58 if(pProj->m_Data & LEGACYPROJECTILEFLAG_NO_OWNER || Result.m_Owner < 0 || Result.m_Owner >= MAX_CLIENTS)
59 {
60 Result.m_Owner = -1;
61 }
62 // LEGACYPROJECTILEFLAG_BOUNCE_HORIZONTAL, LEGACYPROJECTILEFLAG_BOUNCE_VERTICAL
63 Result.m_Bouncing = (pProj->m_Data >> 10) & 3;
64 Result.m_Explosive = pProj->m_Data & LEGACYPROJECTILEFLAG_EXPLOSIVE;
65 Result.m_Freeze = pProj->m_Data & LEGACYPROJECTILEFLAG_FREEZE;
66 Result.m_TuneZone = pGameWorld && pGameWorld->m_WorldConfig.m_UseTuneZones ? pGameWorld->Collision()->IsTune(Index: pGameWorld->Collision()->GetMapIndex(Pos: Result.m_StartPos)) : 0;
67 Result.m_SwitchNumber = pEntEx ? pEntEx->m_SwitchNumber : 0;
68 return Result;
69}
70
71CProjectileData ExtractProjectileInfoDDNet(const CNetObj_DDNetProjectile *pProj)
72{
73 CProjectileData Result = {.m_StartPos: vec2(0, 0)};
74
75 Result.m_StartPos = vec2(pProj->m_X / 100.0f, pProj->m_Y / 100.0f);
76 Result.m_StartVel = vec2(pProj->m_VelX / 1e6f, pProj->m_VelY / 1e6f);
77
78 if(pProj->m_Flags & PROJECTILEFLAG_NORMALIZE_VEL)
79 {
80 Result.m_StartVel = normalize(v: Result.m_StartVel);
81 }
82
83 Result.m_Type = pProj->m_Type;
84 Result.m_StartTick = pProj->m_StartTick;
85
86 Result.m_ExtraInfo = true;
87 Result.m_Owner = pProj->m_Owner;
88 Result.m_SwitchNumber = pProj->m_SwitchNumber;
89 Result.m_TuneZone = pProj->m_TuneZone;
90
91 Result.m_Bouncing = 0;
92 if(pProj->m_Flags & PROJECTILEFLAG_BOUNCE_HORIZONTAL)
93 {
94 Result.m_Bouncing |= 1;
95 }
96 if(pProj->m_Flags & PROJECTILEFLAG_BOUNCE_VERTICAL)
97 {
98 Result.m_Bouncing |= 2;
99 }
100
101 Result.m_Explosive = pProj->m_Flags & PROJECTILEFLAG_EXPLOSIVE;
102 Result.m_Freeze = pProj->m_Flags & PROJECTILEFLAG_FREEZE;
103
104 return Result;
105}
106
107void SnapshotRemoveExtraProjectileInfo(CSnapshot *pSnap)
108{
109 for(int Index = 0; Index < pSnap->NumItems(); Index++)
110 {
111 const CSnapshotItem *pItem = pSnap->GetItem(Index);
112 if(pItem->Type() == NETOBJTYPE_PROJECTILE)
113 {
114 CNetObj_Projectile *pProj = (CNetObj_Projectile *)((void *)pItem->Data());
115 if(UseProjectileExtraInfo(pProj))
116 {
117 CProjectileData Data = ExtractProjectileInfo(NetObjType: NETOBJTYPE_PROJECTILE, pData: pProj, pGameWorld: nullptr, pEntEx: nullptr);
118 pProj->m_X = Data.m_StartPos.x;
119 pProj->m_Y = Data.m_StartPos.y;
120 pProj->m_VelX = (int)(Data.m_StartVel.x * 100.0f);
121 pProj->m_VelY = (int)(Data.m_StartVel.y * 100.0f);
122 }
123 }
124 }
125}
126