mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 01:58:19 +00:00
Add dragger prediction
This commit is contained in:
parent
2d3221492d
commit
8121abf877
|
@ -2260,6 +2260,8 @@ if(CLIENT)
|
|||
pickup_data.h
|
||||
prediction/entities/character.cpp
|
||||
prediction/entities/character.h
|
||||
prediction/entities/dragger.cpp
|
||||
prediction/entities/dragger.h
|
||||
prediction/entities/laser.cpp
|
||||
prediction/entities/laser.h
|
||||
prediction/entities/pickup.cpp
|
||||
|
|
161
src/game/client/prediction/entities/dragger.cpp
Normal file
161
src/game/client/prediction/entities/dragger.cpp
Normal file
|
@ -0,0 +1,161 @@
|
|||
/* (c) Shereef Marzouk. See "licence DDRace.txt" and the readme.txt in the root of the distribution for more information. */
|
||||
#include "dragger.h"
|
||||
#include "character.h"
|
||||
|
||||
#include <engine/shared/config.h>
|
||||
|
||||
#include <game/client/laser_data.h>
|
||||
#include <game/collision.h>
|
||||
#include <game/generated/protocol.h>
|
||||
#include <game/mapitems.h>
|
||||
|
||||
void CDragger::Tick()
|
||||
{
|
||||
if(GameWorld()->GameTick() % (int)(GameWorld()->GameTickSpeed() * 0.15f) == 0)
|
||||
{
|
||||
int Flags;
|
||||
int index = Collision()->IsMover(m_Pos.x, m_Pos.y, &Flags);
|
||||
if(index)
|
||||
{
|
||||
m_Core = Collision()->CpSpeed(index, Flags);
|
||||
}
|
||||
m_Pos += m_Core;
|
||||
|
||||
LookForPlayersToDrag();
|
||||
}
|
||||
|
||||
DraggerBeamTick();
|
||||
}
|
||||
|
||||
void CDragger::LookForPlayersToDrag()
|
||||
{
|
||||
// Create a list of players who are in the range of the dragger
|
||||
CEntity *apPlayersInRange[MAX_CLIENTS];
|
||||
mem_zero(apPlayersInRange, sizeof(apPlayersInRange));
|
||||
|
||||
int NumPlayersInRange = GameWorld()->FindEntities(m_Pos,
|
||||
g_Config.m_SvDraggerRange - CCharacterCore::PhysicalSize(),
|
||||
apPlayersInRange, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER);
|
||||
|
||||
// The closest player (within range) in a team is selected as the target
|
||||
int ClosestTargetId = -1;
|
||||
bool CanStillBeTeamTarget = false;
|
||||
int MinDistInTeam = 0;
|
||||
|
||||
for(int i = 0; i < NumPlayersInRange; i++)
|
||||
{
|
||||
CCharacter *pTarget = static_cast<CCharacter *>(apPlayersInRange[i]);
|
||||
const int &TargetTeam = pTarget->Team();
|
||||
|
||||
// Do not create a dragger beam for super player
|
||||
if(TargetTeam == TEAM_SUPER)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// If the dragger is disabled for the target's team, no dragger beam will be generated
|
||||
if(m_Layer == LAYER_SWITCH && m_Number > 0 &&
|
||||
!Switchers()[m_Number].m_aStatus[TargetTeam])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Dragger beams can be created only for reachable, alive players
|
||||
int IsReachable =
|
||||
m_IgnoreWalls ?
|
||||
!Collision()->IntersectNoLaserNW(m_Pos, pTarget->m_Pos, 0, 0) :
|
||||
!Collision()->IntersectNoLaser(m_Pos, pTarget->m_Pos, 0, 0);
|
||||
if(IsReachable)
|
||||
{
|
||||
const int &TargetClientId = pTarget->GetCID();
|
||||
int Distance = distance(pTarget->m_Pos, m_Pos);
|
||||
if(MinDistInTeam == 0 || MinDistInTeam > Distance)
|
||||
{
|
||||
MinDistInTeam = Distance;
|
||||
ClosestTargetId = TargetClientId;
|
||||
}
|
||||
if(TargetClientId == m_TargetId)
|
||||
{
|
||||
CanStillBeTeamTarget = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the closest player for each team as a target if the team does not have a target player yet
|
||||
if((m_TargetId != -1 && !CanStillBeTeamTarget) || m_TargetId == -1)
|
||||
{
|
||||
m_TargetId = ClosestTargetId;
|
||||
}
|
||||
}
|
||||
|
||||
void CDragger::DraggerBeamReset()
|
||||
{
|
||||
m_TargetId = -1;
|
||||
}
|
||||
|
||||
void CDragger::DraggerBeamTick()
|
||||
{
|
||||
CCharacter *pTarget = GameWorld()->GetCharacterByID(m_TargetId);
|
||||
if(!pTarget)
|
||||
{
|
||||
DraggerBeamReset();
|
||||
return;
|
||||
}
|
||||
|
||||
if(GameWorld()->GameTick() % (int)(GameWorld()->GameTickSpeed() * 0.15f) == 0)
|
||||
{
|
||||
if(m_Layer == LAYER_SWITCH && m_Number > 0 &&
|
||||
!Switchers()[m_Number].m_aStatus[pTarget->Team()])
|
||||
{
|
||||
DraggerBeamReset();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// When the dragger can no longer reach the target player, the dragger beam dissolves
|
||||
int IsReachable =
|
||||
m_IgnoreWalls ?
|
||||
!Collision()->IntersectNoLaserNW(m_Pos, pTarget->m_Pos, 0, 0) :
|
||||
!Collision()->IntersectNoLaser(m_Pos, pTarget->m_Pos, 0, 0);
|
||||
if(!IsReachable || distance(pTarget->m_Pos, m_Pos) >= g_Config.m_SvDraggerRange)
|
||||
{
|
||||
DraggerBeamReset();
|
||||
return;
|
||||
}
|
||||
// In the center of the dragger a tee does not experience speed-up
|
||||
else if(distance(pTarget->m_Pos, m_Pos) > 28)
|
||||
{
|
||||
vec2 Temp = pTarget->Core()->m_Vel + (normalize(m_Pos - pTarget->m_Pos) * m_Strength);
|
||||
pTarget->Core()->m_Vel = ClampVel(pTarget->m_MoveRestrictions, Temp);
|
||||
}
|
||||
}
|
||||
|
||||
CDragger::CDragger(CGameWorld *pGameWorld, int ID, const CLaserData *pData) :
|
||||
CEntity(pGameWorld, CGameWorld::ENTTYPE_DRAGGER)
|
||||
{
|
||||
m_Core = vec2(0.f, 0.f);
|
||||
m_ID = ID;
|
||||
m_TargetId = -1;
|
||||
|
||||
m_Strength = 0;
|
||||
m_IgnoreWalls = false;
|
||||
if(0 <= pData->m_Subtype && pData->m_Subtype < NUM_LASERDRAGGERTYPES)
|
||||
{
|
||||
m_IgnoreWalls = (pData->m_Subtype & 1);
|
||||
m_Strength = (pData->m_Subtype >> 1) + 1;
|
||||
}
|
||||
m_Number = pData->m_SwitchNumber;
|
||||
m_Layer = m_Number > 0 ? LAYER_SWITCH : LAYER_GAME;
|
||||
|
||||
Read(pData);
|
||||
}
|
||||
|
||||
void CDragger::Read(const CLaserData *pData)
|
||||
{
|
||||
m_Pos = pData->m_From;
|
||||
m_TargetId = pData->m_Owner;
|
||||
}
|
||||
|
||||
bool CDragger::Match(CDragger *pDragger)
|
||||
{
|
||||
return pDragger->m_Strength == m_Strength && pDragger->m_Number == m_Number && pDragger->m_IgnoreWalls == m_IgnoreWalls;
|
||||
}
|
29
src/game/client/prediction/entities/dragger.h
Normal file
29
src/game/client/prediction/entities/dragger.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/* (c) Shereef Marzouk. See "licence DDRace.txt" and the readme.txt in the root of the distribution for more information. */
|
||||
#ifndef GAME_CLIENT_PREDICTION_ENTITIES_DRAGGER_H
|
||||
#define GAME_CLIENT_PREDICTION_ENTITIES_DRAGGER_H
|
||||
|
||||
#include <game/client/prediction/entity.h>
|
||||
|
||||
class CLaserData;
|
||||
|
||||
class CDragger : public CEntity
|
||||
{
|
||||
vec2 m_Core;
|
||||
float m_Strength;
|
||||
bool m_IgnoreWalls;
|
||||
int m_TargetId;
|
||||
|
||||
void LookForPlayersToDrag();
|
||||
void DraggerBeamTick();
|
||||
void DraggerBeamReset();
|
||||
|
||||
public:
|
||||
CDragger(CGameWorld *pGameWorld, int ID, const CLaserData *pData);
|
||||
bool Match(CDragger *pDragger);
|
||||
void Read(const CLaserData *pData);
|
||||
float GetStrength() { return m_Strength; }
|
||||
|
||||
void Tick() override;
|
||||
};
|
||||
|
||||
#endif // GAME_CLIENT_PREDICTION_ENTITIES_DRAGGER_H
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "gameworld.h"
|
||||
#include "entities/character.h"
|
||||
#include "entities/dragger.h"
|
||||
#include "entities/laser.h"
|
||||
#include "entities/pickup.h"
|
||||
#include "entities/projectile.h"
|
||||
|
@ -497,37 +498,54 @@ void CGameWorld::NetObjAdd(int ObjID, int ObjType, const void *pObjData, const C
|
|||
else if((ObjType == NETOBJTYPE_LASER || ObjType == NETOBJTYPE_DDNETLASER) && m_WorldConfig.m_PredictWeapons)
|
||||
{
|
||||
CLaserData Data = ExtractLaserInfo(ObjType, pObjData, this, pDataEx);
|
||||
if((Data.m_Type >= 0 && Data.m_Type != LASERTYPE_RIFLE && Data.m_Type != LASERTYPE_SHOTGUN) || !IsLocalTeam(Data.m_Owner))
|
||||
{
|
||||
if(!IsLocalTeam(Data.m_Owner))
|
||||
return;
|
||||
}
|
||||
|
||||
CLaser NetLaser = CLaser(this, ObjID, &Data);
|
||||
CLaser *pMatching = 0;
|
||||
if(CLaser *pLaser = dynamic_cast<CLaser *>(GetEntity(ObjID, ENTTYPE_LASER)))
|
||||
if(NetLaser.Match(pLaser))
|
||||
pMatching = pLaser;
|
||||
if(!pMatching)
|
||||
if(Data.m_Type == LASERTYPE_RIFLE || Data.m_Type == LASERTYPE_SHOTGUN || Data.m_Type < 0)
|
||||
{
|
||||
for(CEntity *pEnt = FindFirst(CGameWorld::ENTTYPE_LASER); pEnt; pEnt = pEnt->TypeNext())
|
||||
{
|
||||
auto *const pLaser = dynamic_cast<CLaser *>(pEnt);
|
||||
if(pLaser && pLaser->m_ID == -1 && NetLaser.Match(pLaser))
|
||||
{
|
||||
CLaser NetLaser = CLaser(this, ObjID, &Data);
|
||||
CLaser *pMatching = 0;
|
||||
if(CLaser *pLaser = dynamic_cast<CLaser *>(GetEntity(ObjID, ENTTYPE_LASER)))
|
||||
if(NetLaser.Match(pLaser))
|
||||
pMatching = pLaser;
|
||||
pMatching->m_ID = ObjID;
|
||||
break;
|
||||
if(!pMatching)
|
||||
{
|
||||
for(CEntity *pEnt = FindFirst(CGameWorld::ENTTYPE_LASER); pEnt; pEnt = pEnt->TypeNext())
|
||||
{
|
||||
auto *const pLaser = dynamic_cast<CLaser *>(pEnt);
|
||||
if(pLaser && pLaser->m_ID == -1 && NetLaser.Match(pLaser))
|
||||
{
|
||||
pMatching = pLaser;
|
||||
pMatching->m_ID = ObjID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(pMatching)
|
||||
{
|
||||
pMatching->Keep();
|
||||
if(distance(NetLaser.m_From, NetLaser.m_Pos) < distance(pMatching->m_From, pMatching->m_Pos) - 2.f)
|
||||
{
|
||||
// if the laser stopped earlier than predicted, set the energy to 0
|
||||
pMatching->m_Energy = 0.f;
|
||||
pMatching->m_Pos = NetLaser.m_Pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(pMatching)
|
||||
else if(Data.m_Type == LASERTYPE_DRAGGER)
|
||||
{
|
||||
pMatching->Keep();
|
||||
if(distance(NetLaser.m_From, NetLaser.m_Pos) < distance(pMatching->m_From, pMatching->m_Pos) - 2.f)
|
||||
CDragger NetDragger = CDragger(this, ObjID, &Data);
|
||||
if(NetDragger.GetStrength() > 0)
|
||||
{
|
||||
// if the laser stopped earlier than predicted, set the energy to 0
|
||||
pMatching->m_Energy = 0.f;
|
||||
pMatching->m_Pos = NetLaser.m_Pos;
|
||||
auto *pDragger = dynamic_cast<CDragger *>(GetEntity(ObjID, ENTTYPE_DRAGGER));
|
||||
if(pDragger && NetDragger.Match(pDragger))
|
||||
{
|
||||
pDragger->Keep();
|
||||
pDragger->Read(&Data);
|
||||
return;
|
||||
}
|
||||
CEntity *pEnt = new CDragger(NetDragger);
|
||||
InsertEntity(pEnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -606,6 +624,8 @@ void CGameWorld::CopyWorld(CGameWorld *pFrom)
|
|||
pCopy = new CProjectile(*((CProjectile *)pEnt));
|
||||
else if(Type == ENTTYPE_LASER)
|
||||
pCopy = new CLaser(*((CLaser *)pEnt));
|
||||
else if(Type == ENTTYPE_DRAGGER)
|
||||
pCopy = new CDragger(*((CDragger *)pEnt));
|
||||
else if(Type == ENTTYPE_CHARACTER)
|
||||
pCopy = new CCharacter(*((CCharacter *)pEnt));
|
||||
else if(Type == ENTTYPE_PICKUP)
|
||||
|
@ -650,10 +670,21 @@ CEntity *CGameWorld::FindMatch(int ObjID, int ObjType, const void *pObjData)
|
|||
case NETOBJTYPE_DDNETLASER:
|
||||
{
|
||||
CLaserData Data = ExtractLaserInfo(ObjType, pObjData, this, nullptr);
|
||||
CLaser *pEnt = (CLaser *)GetEntity(ObjID, ENTTYPE_LASER);
|
||||
if(pEnt && CLaser(this, ObjID, &Data).Match(pEnt))
|
||||
if(Data.m_Type == LASERTYPE_RIFLE || Data.m_Type == LASERTYPE_SHOTGUN)
|
||||
{
|
||||
return pEnt;
|
||||
CLaser *pEnt = (CLaser *)GetEntity(ObjID, ENTTYPE_LASER);
|
||||
if(pEnt && CLaser(this, ObjID, &Data).Match(pEnt))
|
||||
{
|
||||
return pEnt;
|
||||
}
|
||||
}
|
||||
else if(Data.m_Type == LASERTYPE_DRAGGER)
|
||||
{
|
||||
CDragger *pEnt = (CDragger *)GetEntity(ObjID, ENTTYPE_DRAGGER);
|
||||
if(pEnt && CDragger(this, ObjID, &Data).Match(pEnt))
|
||||
{
|
||||
return pEnt;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,11 @@ public:
|
|||
{
|
||||
ENTTYPE_PROJECTILE = 0,
|
||||
ENTTYPE_LASER,
|
||||
ENTTYPE_DOOR,
|
||||
ENTTYPE_DRAGGER,
|
||||
ENTTYPE_LIGHT,
|
||||
ENTTYPE_GUN,
|
||||
ENTTYPE_PLASMA,
|
||||
ENTTYPE_PICKUP,
|
||||
ENTTYPE_FLAG,
|
||||
ENTTYPE_CHARACTER,
|
||||
|
|
Loading…
Reference in a new issue