Skip to content

Commit b556c12

Browse files
committed
Prediction: Create infclass objects
1 parent 422b04c commit b556c12

File tree

9 files changed

+294
-2
lines changed

9 files changed

+294
-2
lines changed

CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2425,6 +2425,10 @@ if(CLIENT)
24252425
prediction/entities/character.h
24262426
prediction/entities/dragger.cpp
24272427
prediction/entities/dragger.h
2428+
prediction/entities/ic_entity.cpp
2429+
prediction/entities/ic_entity.h
2430+
prediction/entities/ic_placed_object.cpp
2431+
prediction/entities/ic_placed_object.h
24282432
prediction/entities/laser.cpp
24292433
prediction/entities/laser.h
24302434
prediction/entities/pickup.cpp
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#include "ic_entity.h"
2+
3+
CIcEntity::CIcEntity(CGameWorld *pGameWorld, int Id, int ObjectType, vec2 Pos, std::optional<int> Owner,
4+
int ProximityRadius) :
5+
CEntity(pGameWorld, ObjectType, Pos, ProximityRadius)
6+
{
7+
SetOwner(Owner.value_or(-1));
8+
m_Id = Id;
9+
}
10+
11+
bool CIcEntity::Match(const CIcEntity *pEntity) const
12+
{
13+
return m_Owner == pEntity->m_Owner && m_Pos == pEntity->m_Pos;
14+
}
15+
16+
void CIcEntity::Read(const CIcEntity &Source)
17+
{
18+
m_EndTick = Source.m_EndTick;
19+
}
20+
21+
void CIcEntity::SetStartTick(int Tick)
22+
{
23+
24+
}
25+
26+
void CIcEntity::SetEndTick(int Tick)
27+
{
28+
29+
}
30+
31+
void CIcEntity::SetOwner(int ClientId)
32+
{
33+
if(ClientId < 0)
34+
{
35+
m_Owner.reset();
36+
}
37+
else
38+
{
39+
m_Owner = ClientId;
40+
}
41+
}
42+
43+
CCharacter *CIcEntity::GetOwnerCharacter()
44+
{
45+
return m_Owner.has_value() ? GameWorld()->GetCharacterById(m_Owner.value()) : nullptr;
46+
}
47+
48+
void CIcEntity::Tick()
49+
{
50+
if(m_EndTick.has_value() && (GameWorld()->GameTick() >= m_EndTick))
51+
{
52+
GameWorld()->DestroyEntity(this);
53+
return;
54+
}
55+
56+
m_Pos += m_Velocity;
57+
}
58+
59+
void CIcEntity::MoveTo(const vec2 &Position)
60+
{
61+
SetPos(Position);
62+
}
63+
64+
void CIcEntity::SetPos(const vec2 &Position)
65+
{
66+
m_Pos = Position;
67+
}
68+
69+
float CIcEntity::GetLifespan() const
70+
{
71+
if (!m_EndTick.has_value())
72+
return -1;
73+
74+
int RemainingTicks = m_EndTick.value_or(0) - GameWorld()->GameTick();
75+
return RemainingTicks <= 0 ? 0 : RemainingTicks / static_cast<float>(GameWorld()->GameTickSpeed());
76+
}
77+
78+
void CIcEntity::SetLifespan(float Lifespan)
79+
{
80+
m_EndTick = GameWorld()->GameTick() + GameWorld()->GameTickSpeed() * Lifespan;
81+
}
82+
83+
void CIcEntity::ResetLifespan()
84+
{
85+
m_EndTick.reset();
86+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#ifndef GAME_CLIENT_ENTITIES_IC_ENTITY_H
2+
#define GAME_CLIENT_ENTITIES_IC_ENTITY_H
3+
4+
#include <game/client/prediction/entity.h>
5+
6+
inline constexpr int TileSize = 32;
7+
inline constexpr float TileSizeF = 32.0f;
8+
inline constexpr vec2 Tile2DSize{TileSizeF, TileSizeF};
9+
inline constexpr vec2 Tile2DHalfSize{TileSizeF / 2, TileSizeF / 2};
10+
11+
class CIcEntity : public CEntity
12+
{
13+
public:
14+
CIcEntity(CGameWorld *pGameWorld, int Id, int ObjectType, vec2 Pos = vec2(), std::optional<int> Owner = std::nullopt,
15+
int ProximityRadius = 0);
16+
17+
using CEntity::GameWorld;
18+
const CGameWorld *GameWorld() const { return m_pGameWorld; }
19+
20+
bool Match(const CIcEntity *pEntity) const;
21+
void Read(const CIcEntity &Source);
22+
23+
int GetSnapData1() const { return m_SnapData1; }
24+
void SetSnapData1(int Value) { m_SnapData1 = Value; }
25+
26+
void SetStartTick(int Tick);
27+
void SetEndTick(int Tick);
28+
29+
bool HasOwner() const { return m_Owner.has_value(); }
30+
int GetOwner() const { return m_Owner.value_or(-1); }
31+
void SetOwner(int ClientId);
32+
33+
CCharacter *GetOwnerCharacter();
34+
35+
void Tick() override;
36+
37+
virtual void MoveTo(const vec2 &Position);
38+
void SetPos(const vec2 &Position);
39+
40+
vec2 GetVelocity() const { return m_Velocity; }
41+
void SetVelocity(vec2 Velocity) { m_Velocity = Velocity; }
42+
43+
float GetLifespan() const;
44+
void SetLifespan(float Lifespan);
45+
void ResetLifespan();
46+
std::optional<int> GetEndTick() const { return m_EndTick; }
47+
48+
protected:
49+
std::optional<int> m_Owner;
50+
vec2 m_Velocity{};
51+
std::optional<int> m_EndTick;
52+
int m_SnapData1{};
53+
};
54+
55+
#endif // GAME_CLIENT_ENTITIES_IC_ENTITY_H
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#include "ic_placed_object.h"
2+
3+
EIcObjectType GetIcObjectTypeFromInt(int Value)
4+
{
5+
if (Value < 0 || Value > static_cast<int>(EIcObjectType::TURRET))
6+
return EIcObjectType::CUSTOM;
7+
8+
return static_cast<EIcObjectType>(Value);
9+
}
10+
11+
CIcPlacedObject::CIcPlacedObject(CGameWorld *pGameWorld, int Id, EIcObjectType ObjectType, vec2 Pos, int Owner, int ProximityRadius) :
12+
CIcEntity(pGameWorld, Id, CGameWorld::ENTTYPE_IC_PLACED_OBJECT, Pos, Owner, ProximityRadius),
13+
m_IcObjectType(ObjectType)
14+
{
15+
}
16+
17+
bool CIcPlacedObject::Match(const CIcPlacedObject *pEntity) const
18+
{
19+
if (!CIcEntity::Match(pEntity))
20+
return false;
21+
22+
if (m_IcObjectType != pEntity->m_IcObjectType)
23+
return false;
24+
25+
return true;
26+
}
27+
28+
void CIcPlacedObject::Read(const CIcPlacedObject &Source)
29+
{
30+
CIcEntity::Read(Source);
31+
m_Pos2 = Source.m_Pos2;
32+
m_InfClassObjectFlags = Source.m_InfClassObjectFlags;
33+
}
34+
35+
void CIcPlacedObject::MoveTo(const vec2 &Position)
36+
{
37+
if(m_Pos2.has_value())
38+
{
39+
vec2 Difference = Position - m_Pos;
40+
CIcEntity::MoveTo(Position);
41+
m_Pos2 = m_Pos2.value() + Difference;
42+
}
43+
}
44+
45+
void CIcPlacedObject::SetSecondPosition(vec2 Position)
46+
{
47+
m_Pos2 = Position;
48+
m_InfClassObjectFlags = INFCLASS_OBJECT_FLAG_HAS_SECOND_POSITION;
49+
}
50+
51+
void CIcPlacedObject::Tick()
52+
{
53+
CIcEntity::Tick();
54+
55+
if(m_Pos2.has_value())
56+
{
57+
m_Pos2.value() += m_Velocity;
58+
}
59+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#pragma once
2+
3+
#include <game/client/prediction/entities/ic_entity.h>
4+
5+
enum class EIcObjectType
6+
{
7+
CUSTOM,
8+
LASER_WALL,
9+
LOOPER_WALL,
10+
SOLDIER_BOMB,
11+
SCIENTIST_MINE,
12+
BIOLOGIST_MINE,
13+
MERCENARY_BOMB,
14+
TURRET,
15+
};
16+
17+
EIcObjectType GetIcObjectTypeFromInt(int value);
18+
19+
class CIcPlacedObject : public CIcEntity
20+
{
21+
public:
22+
CIcPlacedObject(CGameWorld *pGameWorld, int Id, EIcObjectType ObjectType, vec2 Pos, int Owner, int ProximityRadius);
23+
24+
bool Match(const CIcPlacedObject *pEntity) const;
25+
void Read(const CIcPlacedObject &Source);
26+
27+
void MoveTo(const vec2 &Position) override;
28+
29+
bool HasSecondPosition() const { return m_Pos2.has_value(); }
30+
vec2 SecondPosition() const { return m_Pos2.value_or(m_Pos); }
31+
void SetSecondPosition(vec2 Position);
32+
33+
EIcObjectType IcObjectType() const { return m_IcObjectType; }
34+
void Tick() override;
35+
36+
protected:
37+
std::optional<vec2> m_Pos2;
38+
EIcObjectType m_IcObjectType{};
39+
int m_InfClassObjectFlags = 0;
40+
};

src/game/client/prediction/entities/laser.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
/* If you are missing that file, acquire a complete release at teeworlds.com. */
33
#include "laser.h"
44
#include "character.h"
5+
#include "ic_placed_object.h"
6+
57
#include <game/client/laser_data.h>
68
#include <game/collision.h>
79
#include <game/generated/protocol.h>

src/game/client/prediction/entity.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ class CEntity
4242
float GetProximityRadius() const { return m_ProximityRadius; }
4343
virtual bool CanCollide(int ClientId) { return true; }
4444

45+
virtual void MarkForDestroy() { m_MarkedForDestroy = true; }
46+
4547
void Destroy() { delete this; }
4648
virtual void PreTick() {}
4749
virtual void Tick() {}

src/game/client/prediction/gameworld.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "gameworld.h"
55
#include "entities/character.h"
66
#include "entities/dragger.h"
7+
#include "entities/ic_placed_object.h"
78
#include "entities/laser.h"
89
#include "entities/pickup.h"
910
#include "entities/projectile.h"
@@ -12,6 +13,7 @@
1213
#include <game/client/laser_data.h>
1314
#include <game/client/pickup_data.h>
1415
#include <game/client/projectile_data.h>
16+
#include <game/generated/protocol.h>
1517
#include <game/mapbugs.h>
1618
#include <game/mapitems.h>
1719

@@ -124,6 +126,11 @@ void CGameWorld::InsertEntity(CEntity *pEnt, bool Last)
124126
}
125127
}
126128

129+
void CGameWorld::DestroyEntity(CEntity *pEnt)
130+
{
131+
pEnt->MarkForDestroy();
132+
}
133+
127134
void CGameWorld::RemoveEntity(CEntity *pEnt)
128135
{
129136
// not in the list
@@ -549,7 +556,32 @@ void CGameWorld::NetObjAdd(int ObjId, int ObjType, const void *pObjData, const C
549556
}
550557
else if(ObjType == NETOBJTYPE_INFCLASSOBJECT)
551558
{
559+
const CNetObj_InfClassObject *pObject = static_cast<const CNetObj_InfClassObject *>(pObjData);
560+
vec2 Pos(pObject->m_X, pObject->m_Y);
561+
float Radius = fx2f(pObject->m_ProximityRadius);
562+
EIcObjectType IcObjectType = GetIcObjectTypeFromInt(pObject->m_Type);
563+
564+
CIcPlacedObject NetObject(this, ObjId, IcObjectType, Pos, pObject->m_Owner, Radius);
565+
if(pObject->m_Flags & INFCLASS_OBJECT_FLAG_HAS_SECOND_POSITION)
566+
{
567+
vec2 Pos2(pObject->m_X2, pObject->m_Y2);
568+
NetObject.SetSecondPosition(Pos2);
569+
}
570+
NetObject.SetStartTick(pObject->m_StartTick);
571+
NetObject.SetEndTick(pObject->m_EndTick);
572+
NetObject.SetSnapData1(pObject->m_Data1);
552573

574+
CIcPlacedObject *pExisting = static_cast<CIcPlacedObject *>(GetEntity(ObjId, ENTTYPE_IC_PLACED_OBJECT));
575+
if (pExisting && pExisting->Match(&NetObject))
576+
{
577+
pExisting->Keep();
578+
pExisting->Read(NetObject);
579+
}
580+
else
581+
{
582+
CIcPlacedObject *pEnt = new CIcPlacedObject(NetObject);
583+
InsertEntity(pEnt);
584+
}
553585
}
554586
}
555587

@@ -632,6 +664,8 @@ void CGameWorld::CopyWorld(CGameWorld *pFrom)
632664
pCopy = new CCharacter(*((CCharacter *)pEnt));
633665
else if(Type == ENTTYPE_PICKUP)
634666
pCopy = new CPickup(*((CPickup *)pEnt));
667+
else if(Type == ENTTYPE_IC_PLACED_OBJECT)
668+
pCopy = new CIcPlacedObject(*((CIcPlacedObject *)pEnt));
635669
if(pCopy)
636670
{
637671
pCopy->m_pParent = pEnt;

src/game/client/prediction/gameworld.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class CGameWorld
2929
ENTTYPE_PICKUP,
3030
ENTTYPE_FLAG,
3131
ENTTYPE_CHARACTER,
32+
ENTTYPE_IC_PLACED_OBJECT,
3233
NUM_ENTTYPES
3334
};
3435

@@ -44,6 +45,15 @@ class CGameWorld
4445
CCharacter *IntersectCharacter(vec2 Pos0, vec2 Pos1, float Radius, vec2 &NewPos, const CCharacter *pNotThis = nullptr, int CollideWith = -1, const CCharacter *pThisOnly = nullptr);
4546
CEntity *IntersectEntity(vec2 Pos0, vec2 Pos1, float Radius, int Type, vec2 &NewPos, const CEntity *pNotThis = nullptr, int CollideWith = -1, const CEntity *pThisOnly = nullptr);
4647
void InsertEntity(CEntity *pEntity, bool Last = false);
48+
49+
/*
50+
Function: destroy_entity
51+
Destroys an entity in the world.
52+
53+
Arguments:
54+
entity - Entity to destroy
55+
*/
56+
void DestroyEntity(CEntity *pEntity);
4757
void RemoveEntity(CEntity *pEntity);
4858
void RemoveCharacter(CCharacter *pChar);
4959
void Tick();
@@ -56,8 +66,8 @@ class CGameWorld
5666
CCollision *m_pCollision;
5767

5868
// getter for server variables
59-
int GameTick() { return m_GameTick; }
60-
int GameTickSpeed() { return SERVER_TICK_SPEED; }
69+
int GameTick() const { return m_GameTick; }
70+
int GameTickSpeed() const { return SERVER_TICK_SPEED; }
6171
CCollision *Collision() { return m_pCollision; }
6272
CTeamsCore *Teams() { return &m_Teams; }
6373
std::vector<SSwitchers> &Switchers() { return m_Core.m_vSwitchers; }

0 commit comments

Comments
 (0)