mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 01:58:19 +00:00
Merge #5063
5063: Reworked Draggers and Turrets AND show always hooks if they are in the field of view r=def- a=C0D3D3V Fixes #3622, #4723 #4798 and #5054 Here you can find a comparison video with 16.0.3: https://youtu.be/bYwLB1cEMI8 This now also includes the changes from https://github.com/ddnet/ddnet/pull/4980 So please also see my monologue here: https://github.com/ddnet/ddnet/pull/4980 Physic that got changed by this PR: - Solo players are not included in the calculation of the next team players to the dragger/turrets. This does not affect any map. - Turrets get switched correctly if players are solo - Plasma bullets fired by turrets on solo player only explode for solo players and the other way around (except if they unsolo) - Plasma bullets can no longer be intercepted by other teams - Turrets can shot now independently at the speed of sv_plasma_per_sec for every team and every solo player This should be tested by some more players before it get merged! This reduces the use of snap id's tested here: https://youtu.be/G3nVtdH0--Q from 16000 to 69 test with the new dragger: https://youtu.be/mzNrDHP7HQs I did not add the terminal of the server because you basically see no change in the used snap ids The videos are with cl_predict off Comparison of the new turrets: https://streamable.com/8us8lk left old turrets, right the new ones We should delete ranks on (that where made after 13 Jun 2019): https://ddnet.tw/maps/Increase-32-Your-32-Speed https://ddnet.tw/maps/Fall-32-into-32-the-32-Future https://ddnet.tw/maps/turboSeks ## Checklist - [x] Tested the change ingame - [x] Provided screenshots if it is a visual change - [ ] Tested in combination with possibly related configuration options - [ ] Written a unit test if it works standalone, system.c especially - [x] Considered possible null pointers and out of bounds array indexing - [only on 3 maps] Changed no physics that affect existing maps - [x] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional) Co-authored-by: c0d3d3v <c0d3d3v@mag-keinen-spam.de>
This commit is contained in:
commit
03f95108b4
|
@ -2280,6 +2280,8 @@ if(SERVER)
|
|||
entities/door.h
|
||||
entities/dragger.cpp
|
||||
entities/dragger.h
|
||||
entities/dragger_beam.cpp
|
||||
entities/dragger_beam.h
|
||||
entities/flag.cpp
|
||||
entities/flag.h
|
||||
entities/gun.cpp
|
||||
|
|
|
@ -145,16 +145,16 @@ typedef vector2_base<bool> bvec2;
|
|||
typedef vector2_base<int> ivec2;
|
||||
|
||||
template<typename T>
|
||||
inline bool closest_point_on_line(vector2_base<T> line_point0, vector2_base<T> line_point1, vector2_base<T> target_point, vector2_base<T> &out_pos)
|
||||
inline bool closest_point_on_line(vector2_base<T> line_pointA, vector2_base<T> line_pointB, vector2_base<T> target_point, vector2_base<T> &out_pos)
|
||||
{
|
||||
vector2_base<T> c = target_point - line_point0;
|
||||
vector2_base<T> v = (line_point1 - line_point0);
|
||||
T d = length(line_point0 - line_point1);
|
||||
if(d > 0)
|
||||
vector2_base<T> AB = line_pointB - line_pointA;
|
||||
T SquaredMagnitudeAB = dot(AB, AB);
|
||||
if(SquaredMagnitudeAB > 0)
|
||||
{
|
||||
v = normalize_pre_length<T>(v, d);
|
||||
T t = dot(v, c) / d;
|
||||
out_pos = mix(line_point0, line_point1, clamp(t, (T)0, (T)1));
|
||||
vector2_base<T> AP = target_point - line_pointA;
|
||||
T APdotAB = dot(AP, AB);
|
||||
T t = APdotAB / SquaredMagnitudeAB;
|
||||
out_pos = line_pointA + AB * clamp(t, (T)0, (T)1);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -65,7 +65,7 @@ void CSnapIDPool::Reset()
|
|||
for(int i = 0; i < MAX_IDS; i++)
|
||||
{
|
||||
m_aIDs[i].m_Next = i + 1;
|
||||
m_aIDs[i].m_State = 0;
|
||||
m_aIDs[i].m_State = ID_FREE;
|
||||
}
|
||||
|
||||
m_aIDs[MAX_IDS - 1].m_Next = -1;
|
||||
|
@ -82,7 +82,7 @@ void CSnapIDPool::RemoveFirstTimeout()
|
|||
|
||||
// add it to the free list
|
||||
m_aIDs[m_FirstTimed].m_Next = m_FirstFree;
|
||||
m_aIDs[m_FirstTimed].m_State = 0;
|
||||
m_aIDs[m_FirstTimed].m_State = ID_FREE;
|
||||
m_FirstFree = m_FirstTimed;
|
||||
|
||||
// remove it from the timed list
|
||||
|
@ -108,7 +108,7 @@ int CSnapIDPool::NewID()
|
|||
return ID;
|
||||
}
|
||||
m_FirstFree = m_aIDs[m_FirstFree].m_Next;
|
||||
m_aIDs[ID].m_State = 1;
|
||||
m_aIDs[ID].m_State = ID_ALLOCATED;
|
||||
m_Usage++;
|
||||
m_InUsage++;
|
||||
return ID;
|
||||
|
@ -125,10 +125,10 @@ void CSnapIDPool::FreeID(int ID)
|
|||
{
|
||||
if(ID < 0)
|
||||
return;
|
||||
dbg_assert(m_aIDs[ID].m_State == 1, "id is not allocated");
|
||||
dbg_assert(m_aIDs[ID].m_State == ID_ALLOCATED, "id is not allocated");
|
||||
|
||||
m_InUsage--;
|
||||
m_aIDs[ID].m_State = 2;
|
||||
m_aIDs[ID].m_State = ID_TIMED;
|
||||
m_aIDs[ID].m_Timeout = time_get() + time_freq() * 5;
|
||||
m_aIDs[ID].m_Next = -1;
|
||||
|
||||
|
|
|
@ -43,6 +43,14 @@ class CSnapIDPool
|
|||
MAX_IDS = 32 * 1024,
|
||||
};
|
||||
|
||||
// State of a Snap ID
|
||||
enum
|
||||
{
|
||||
ID_FREE = 0,
|
||||
ID_ALLOCATED = 1,
|
||||
ID_TIMED = 2,
|
||||
};
|
||||
|
||||
class CID
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -519,14 +519,6 @@ void CHud::RenderWarmupTimer()
|
|||
}
|
||||
}
|
||||
|
||||
void CHud::MapscreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup)
|
||||
{
|
||||
float Points[4];
|
||||
RenderTools()->MapscreenToWorld(CenterX, CenterY, pGroup->m_ParallaxX, pGroup->m_ParallaxY,
|
||||
pGroup->m_OffsetX, pGroup->m_OffsetY, Graphics()->ScreenAspect(), 1.0f, Points);
|
||||
Graphics()->MapScreen(Points[0], Points[1], Points[2], Points[3]);
|
||||
}
|
||||
|
||||
void CHud::RenderTextInfo()
|
||||
{
|
||||
if(g_Config.m_ClShowfps)
|
||||
|
@ -660,7 +652,7 @@ void CHud::RenderCursor()
|
|||
if(!m_pClient->m_Snap.m_pLocalCharacter || Client()->State() == IClient::STATE_DEMOPLAYBACK)
|
||||
return;
|
||||
|
||||
MapscreenToGroup(m_pClient->m_Camera.m_Center.x, m_pClient->m_Camera.m_Center.y, Layers()->GameGroup());
|
||||
RenderTools()->MapScreenToGroup(m_pClient->m_Camera.m_Center.x, m_pClient->m_Camera.m_Center.y, Layers()->GameGroup());
|
||||
|
||||
// render cursor
|
||||
int CurWeapon = m_pClient->m_Snap.m_pLocalCharacter->m_Weapon % NUM_WEAPONS;
|
||||
|
|
|
@ -65,8 +65,6 @@ class CHud : public CComponent
|
|||
void RenderWarmupTimer();
|
||||
void RenderLocalTime(float x);
|
||||
|
||||
void MapscreenToGroup(float CenterX, float CenterY, struct CMapItemGroup *PGroup);
|
||||
|
||||
static constexpr float MOVEMENT_INFORMATION_LINE_HEIGHT = 8.0f;
|
||||
|
||||
public:
|
||||
|
|
|
@ -55,14 +55,6 @@ void CMapLayers::EnvelopeUpdate()
|
|||
}
|
||||
}
|
||||
|
||||
void CMapLayers::MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup, float Zoom)
|
||||
{
|
||||
float Points[4];
|
||||
RenderTools()->MapscreenToWorld(CenterX, CenterY, pGroup->m_ParallaxX, pGroup->m_ParallaxY,
|
||||
pGroup->m_OffsetX, pGroup->m_OffsetY, Graphics()->ScreenAspect(), Zoom, Points);
|
||||
Graphics()->MapScreen(Points[0], Points[1], Points[2], Points[3]);
|
||||
}
|
||||
|
||||
void CMapLayers::EnvelopeEval(int TimeOffsetMillis, int Env, float *pChannels, void *pUser)
|
||||
{
|
||||
CMapLayers *pThis = (CMapLayers *)pUser;
|
||||
|
@ -1567,7 +1559,7 @@ void CMapLayers::OnRender()
|
|||
{
|
||||
// set clipping
|
||||
float Points[4];
|
||||
MapScreenToGroup(Center.x, Center.y, m_pLayers->GameGroup(), GetCurCamera()->m_Zoom);
|
||||
RenderTools()->MapScreenToGroup(Center.x, Center.y, m_pLayers->GameGroup(), GetCurCamera()->m_Zoom);
|
||||
Graphics()->GetScreen(&Points[0], &Points[1], &Points[2], &Points[3]);
|
||||
float x0 = (pGroup->m_ClipX - Points[0]) / (Points[2] - Points[0]);
|
||||
float y0 = (pGroup->m_ClipY - Points[1]) / (Points[3] - Points[1]);
|
||||
|
@ -1587,10 +1579,10 @@ void CMapLayers::OnRender()
|
|||
|
||||
if((!g_Config.m_ClZoomBackgroundLayers || m_Type == TYPE_FULL_DESIGN) && !pGroup->m_ParallaxX && !pGroup->m_ParallaxY)
|
||||
{
|
||||
MapScreenToGroup(Center.x, Center.y, pGroup, 1.0f);
|
||||
RenderTools()->MapScreenToGroup(Center.x, Center.y, pGroup, 1.0f);
|
||||
}
|
||||
else
|
||||
MapScreenToGroup(Center.x, Center.y, pGroup, GetCurCamera()->m_Zoom);
|
||||
RenderTools()->MapScreenToGroup(Center.x, Center.y, pGroup, GetCurCamera()->m_Zoom);
|
||||
|
||||
for(int l = 0; l < pGroup->m_NumLayers; l++)
|
||||
{
|
||||
|
|
|
@ -35,8 +35,6 @@ class CMapLayers : public CComponent
|
|||
|
||||
bool m_OnlineOnly;
|
||||
|
||||
void MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup, float Zoom = 1.0f);
|
||||
|
||||
struct STileLayerVisuals
|
||||
{
|
||||
STileLayerVisuals() :
|
||||
|
|
|
@ -14,13 +14,6 @@
|
|||
|
||||
#include "players.h"
|
||||
|
||||
void CNamePlates::MapscreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup)
|
||||
{
|
||||
float Points[4];
|
||||
RenderTools()->MapscreenToWorld(CenterX, CenterY, pGroup->m_ParallaxX, pGroup->m_ParallaxY, pGroup->m_OffsetX, pGroup->m_OffsetY, Graphics()->ScreenAspect(), 1.0f, Points);
|
||||
Graphics()->MapScreen(Points[0], Points[1], Points[2], Points[3]);
|
||||
}
|
||||
|
||||
void CNamePlates::RenderNameplate(
|
||||
const CNetObj_Character *pPrevChar,
|
||||
const CNetObj_Character *pPlayerChar,
|
||||
|
@ -109,7 +102,7 @@ void CNamePlates::RenderNameplatePos(vec2 Position, const CNetObj_PlayerInfo *pP
|
|||
// create nameplates at standard zoom
|
||||
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
|
||||
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
|
||||
MapscreenToGroup(m_pClient->m_Camera.m_Center.x, m_pClient->m_Camera.m_Center.y, Layers()->GameGroup());
|
||||
RenderTools()->MapScreenToGroup(m_pClient->m_Camera.m_Center.x, m_pClient->m_Camera.m_Center.y, Layers()->GameGroup());
|
||||
|
||||
m_aNamePlates[ClientID].m_NameTextWidth = TextRender()->TextWidth(0, FontSize, pName, -1, -1.0f);
|
||||
|
||||
|
@ -135,7 +128,7 @@ void CNamePlates::RenderNameplatePos(vec2 Position, const CNetObj_PlayerInfo *pP
|
|||
// create nameplates at standard zoom
|
||||
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
|
||||
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
|
||||
MapscreenToGroup(m_pClient->m_Camera.m_Center.x, m_pClient->m_Camera.m_Center.y, Layers()->GameGroup());
|
||||
RenderTools()->MapScreenToGroup(m_pClient->m_Camera.m_Center.x, m_pClient->m_Camera.m_Center.y, Layers()->GameGroup());
|
||||
|
||||
m_aNamePlates[ClientID].m_ClanNameTextWidth = TextRender()->TextWidth(0, FontSizeClan, pClan, -1, -1.0f);
|
||||
|
||||
|
|
|
@ -37,8 +37,6 @@ struct SPlayerNamePlate
|
|||
|
||||
class CNamePlates : public CComponent
|
||||
{
|
||||
void MapscreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup);
|
||||
|
||||
void RenderNameplate(
|
||||
const CNetObj_Character *pPrevChar,
|
||||
const CNetObj_Character *pPlayerChar,
|
||||
|
|
|
@ -2341,7 +2341,7 @@ void CGameClient::UpdatePrediction()
|
|||
pCopy->m_FreezeTime = 0;
|
||||
if(pCopy->Core()->m_HookedPlayer > 0)
|
||||
{
|
||||
pCopy->Core()->m_HookedPlayer = -1;
|
||||
pCopy->Core()->SetHookedPlayer(-1);
|
||||
pCopy->Core()->m_HookState = HOOK_IDLE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1152,7 +1152,7 @@ void CCharacter::ResetPrediction()
|
|||
}
|
||||
if(m_Core.m_HookedPlayer >= 0)
|
||||
{
|
||||
m_Core.m_HookedPlayer = -1;
|
||||
m_Core.SetHookedPlayer(-1);
|
||||
m_Core.m_HookState = HOOK_IDLE;
|
||||
}
|
||||
m_LastWeaponSwitchTick = 0;
|
||||
|
|
|
@ -274,7 +274,7 @@ void CGameWorld::ReleaseHooked(int ClientID)
|
|||
CCharacterCore *Core = pChr->Core();
|
||||
if(Core->m_HookedPlayer == ClientID)
|
||||
{
|
||||
Core->m_HookedPlayer = -1;
|
||||
Core->SetHookedPlayer(-1);
|
||||
Core->m_HookState = HOOK_RETRACTED;
|
||||
Core->m_TriggeredEvents |= COREEVENT_HOOK_RETRACT;
|
||||
}
|
||||
|
|
|
@ -752,7 +752,7 @@ void CRenderTools::CalcScreenParams(float Aspect, float Zoom, float *w, float *h
|
|||
*h *= Zoom;
|
||||
}
|
||||
|
||||
void CRenderTools::MapscreenToWorld(float CenterX, float CenterY, float ParallaxX, float ParallaxY,
|
||||
void CRenderTools::MapScreenToWorld(float CenterX, float CenterY, float ParallaxX, float ParallaxY,
|
||||
float OffsetX, float OffsetY, float Aspect, float Zoom, float *pPoints)
|
||||
{
|
||||
float Width, Height;
|
||||
|
@ -764,3 +764,11 @@ void CRenderTools::MapscreenToWorld(float CenterX, float CenterY, float Parallax
|
|||
pPoints[2] = pPoints[0] + Width;
|
||||
pPoints[3] = pPoints[1] + Height;
|
||||
}
|
||||
|
||||
void CRenderTools::MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup, float Zoom)
|
||||
{
|
||||
float Points[4];
|
||||
MapScreenToWorld(CenterX, CenterY, pGroup->m_ParallaxX, pGroup->m_ParallaxY,
|
||||
pGroup->m_OffsetX, pGroup->m_OffsetY, Graphics()->ScreenAspect(), Zoom, Points);
|
||||
Graphics()->MapScreen(Points[0], Points[1], Points[2], Points[3]);
|
||||
}
|
||||
|
|
|
@ -119,8 +119,9 @@ public:
|
|||
|
||||
// helpers
|
||||
void CalcScreenParams(float Aspect, float Zoom, float *w, float *h);
|
||||
void MapscreenToWorld(float CenterX, float CenterY, float ParallaxX, float ParallaxY,
|
||||
void MapScreenToWorld(float CenterX, float CenterY, float ParallaxX, float ParallaxY,
|
||||
float OffsetX, float OffsetY, float Aspect, float Zoom, float *pPoints);
|
||||
void MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup, float Zoom = 1.0f);
|
||||
|
||||
// DDRace
|
||||
|
||||
|
|
|
@ -128,7 +128,7 @@ void CLayerGroup::Convert(CUIRect *pRect)
|
|||
|
||||
void CLayerGroup::Mapping(float *pPoints)
|
||||
{
|
||||
m_pMap->m_pEditor->RenderTools()->MapscreenToWorld(
|
||||
m_pMap->m_pEditor->RenderTools()->MapScreenToWorld(
|
||||
m_pMap->m_pEditor->m_WorldOffsetX, m_pMap->m_pEditor->m_WorldOffsetY,
|
||||
m_ParallaxX, m_ParallaxY, m_OffsetX, m_OffsetY,
|
||||
m_pMap->m_pEditor->Graphics()->ScreenAspect(), m_pMap->m_pEditor->m_WorldZoom, pPoints);
|
||||
|
@ -2864,7 +2864,7 @@ void CEditor::DoMapEditor(CUIRect View)
|
|||
float aPoints[4];
|
||||
float Aspect = Start + (End - Start) * (i / (float)NumSteps);
|
||||
|
||||
RenderTools()->MapscreenToWorld(
|
||||
RenderTools()->MapScreenToWorld(
|
||||
m_WorldOffsetX, m_WorldOffsetY,
|
||||
100.0f, 100.0f, 0.0f, 0.0f, Aspect, 1.0f, aPoints);
|
||||
|
||||
|
@ -2906,7 +2906,7 @@ void CEditor::DoMapEditor(CUIRect View)
|
|||
float aAspects[] = {4.0f / 3.0f, 16.0f / 10.0f, 5.0f / 4.0f, 16.0f / 9.0f};
|
||||
float Aspect = aAspects[i];
|
||||
|
||||
RenderTools()->MapscreenToWorld(
|
||||
RenderTools()->MapScreenToWorld(
|
||||
m_WorldOffsetX, m_WorldOffsetY,
|
||||
100.0f, 100.0f, 0.0f, 0.0f, Aspect, 1.0f, aPoints);
|
||||
|
||||
|
@ -6156,7 +6156,7 @@ void CEditor::ZoomMouseTarget(float ZoomFactor)
|
|||
// zoom to the current mouse position
|
||||
// get absolute mouse position
|
||||
float aPoints[4];
|
||||
RenderTools()->MapscreenToWorld(
|
||||
RenderTools()->MapScreenToWorld(
|
||||
m_WorldOffsetX, m_WorldOffsetY,
|
||||
100.0f, 100.0f, 0.0f, 0.0f, Graphics()->ScreenAspect(), m_WorldZoom, aPoints);
|
||||
|
||||
|
|
|
@ -79,7 +79,8 @@ void CCharacterCore::Reset()
|
|||
m_HookDir = vec2(0, 0);
|
||||
m_HookTick = 0;
|
||||
m_HookState = HOOK_IDLE;
|
||||
m_HookedPlayer = -1;
|
||||
SetHookedPlayer(-1);
|
||||
m_AttachedPlayers.clear();
|
||||
m_Jumped = 0;
|
||||
m_JumpedTotal = 0;
|
||||
m_Jumps = 2;
|
||||
|
@ -197,14 +198,14 @@ void CCharacterCore::Tick(bool UseInput)
|
|||
m_HookState = HOOK_FLYING;
|
||||
m_HookPos = m_Pos + TargetDirection * PhysSize * 1.5f;
|
||||
m_HookDir = TargetDirection;
|
||||
m_HookedPlayer = -1;
|
||||
SetHookedPlayer(-1);
|
||||
m_HookTick = (float)SERVER_TICK_SPEED * (1.25f - m_Tuning.m_HookDuration);
|
||||
m_TriggeredEvents |= COREEVENT_HOOK_LAUNCH;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_HookedPlayer = -1;
|
||||
SetHookedPlayer(-1);
|
||||
m_HookState = HOOK_IDLE;
|
||||
m_HookPos = m_Pos;
|
||||
}
|
||||
|
@ -230,7 +231,7 @@ void CCharacterCore::Tick(bool UseInput)
|
|||
// do hook
|
||||
if(m_HookState == HOOK_IDLE)
|
||||
{
|
||||
m_HookedPlayer = -1;
|
||||
SetHookedPlayer(-1);
|
||||
m_HookState = HOOK_IDLE;
|
||||
m_HookPos = m_Pos;
|
||||
}
|
||||
|
@ -292,7 +293,7 @@ void CCharacterCore::Tick(bool UseInput)
|
|||
{
|
||||
m_TriggeredEvents |= COREEVENT_HOOK_ATTACH_PLAYER;
|
||||
m_HookState = HOOK_GRABBED;
|
||||
m_HookedPlayer = i;
|
||||
SetHookedPlayer(i);
|
||||
Distance = distance(m_HookPos, pCharCore->m_Pos);
|
||||
}
|
||||
}
|
||||
|
@ -317,7 +318,7 @@ void CCharacterCore::Tick(bool UseInput)
|
|||
if(GoingThroughTele && m_pWorld && m_pTeleOuts && !m_pTeleOuts->empty() && !(*m_pTeleOuts)[teleNr - 1].empty())
|
||||
{
|
||||
m_TriggeredEvents = 0;
|
||||
m_HookedPlayer = -1;
|
||||
SetHookedPlayer(-1);
|
||||
|
||||
m_NewHook = true;
|
||||
int RandomOut = m_pWorld->RandomOr0((*m_pTeleOuts)[teleNr - 1].size());
|
||||
|
@ -342,7 +343,7 @@ void CCharacterCore::Tick(bool UseInput)
|
|||
else
|
||||
{
|
||||
// release hook
|
||||
m_HookedPlayer = -1;
|
||||
SetHookedPlayer(-1);
|
||||
m_HookState = HOOK_RETRACTED;
|
||||
m_HookPos = m_Pos;
|
||||
}
|
||||
|
@ -379,7 +380,7 @@ void CCharacterCore::Tick(bool UseInput)
|
|||
m_HookTick++;
|
||||
if(m_HookedPlayer != -1 && (m_HookTick > SERVER_TICK_SPEED + SERVER_TICK_SPEED / 5 || (m_pWorld && !m_pWorld->m_apCharacters[m_HookedPlayer])))
|
||||
{
|
||||
m_HookedPlayer = -1;
|
||||
SetHookedPlayer(-1);
|
||||
m_HookState = HOOK_RETRACTED;
|
||||
m_HookPos = m_Pos;
|
||||
}
|
||||
|
@ -549,7 +550,7 @@ void CCharacterCore::ReadCharacterCore(const CNetObj_CharacterCore *pObjCore)
|
|||
m_HookPos.y = pObjCore->m_HookY;
|
||||
m_HookDir.x = pObjCore->m_HookDx / 256.0f;
|
||||
m_HookDir.y = pObjCore->m_HookDy / 256.0f;
|
||||
m_HookedPlayer = pObjCore->m_HookedPlayer;
|
||||
SetHookedPlayer(pObjCore->m_HookedPlayer);
|
||||
m_Jumped = pObjCore->m_Jumped;
|
||||
m_Direction = pObjCore->m_Direction;
|
||||
m_Angle = pObjCore->m_Angle;
|
||||
|
@ -617,6 +618,30 @@ void CCharacterCore::Quantize()
|
|||
ReadCharacterCore(&Core);
|
||||
}
|
||||
|
||||
void CCharacterCore::SetHookedPlayer(int HookedPlayer)
|
||||
{
|
||||
if(HookedPlayer != m_HookedPlayer)
|
||||
{
|
||||
if(m_HookedPlayer != -1 && m_Id != -1 && m_pWorld)
|
||||
{
|
||||
CCharacterCore *pCharCore = m_pWorld->m_apCharacters[m_HookedPlayer];
|
||||
if(pCharCore)
|
||||
{
|
||||
pCharCore->m_AttachedPlayers.erase(m_Id);
|
||||
}
|
||||
}
|
||||
if(HookedPlayer != -1 && m_Id != -1 && m_pWorld)
|
||||
{
|
||||
CCharacterCore *pCharCore = m_pWorld->m_apCharacters[HookedPlayer];
|
||||
if(pCharCore)
|
||||
{
|
||||
pCharCore->m_AttachedPlayers.insert(m_Id);
|
||||
}
|
||||
}
|
||||
m_HookedPlayer = HookedPlayer;
|
||||
}
|
||||
}
|
||||
|
||||
// DDRace
|
||||
|
||||
void CCharacterCore::SetTeamsCore(CTeamsCore *pTeams)
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <base/system.h>
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "collision.h"
|
||||
|
@ -221,6 +222,8 @@ public:
|
|||
int m_HookTick;
|
||||
int m_HookState;
|
||||
int m_HookedPlayer;
|
||||
std::set<int> m_AttachedPlayers;
|
||||
void SetHookedPlayer(int HookedPlayer);
|
||||
|
||||
int m_ActiveWeapon;
|
||||
struct WeaponStat
|
||||
|
|
|
@ -68,6 +68,7 @@ bool CCharacter::Spawn(CPlayer *pPlayer, vec2 Pos)
|
|||
m_Core.Init(&GameServer()->m_World.m_Core, GameServer()->Collision());
|
||||
m_Core.m_ActiveWeapon = WEAPON_GUN;
|
||||
m_Core.m_Pos = m_Pos;
|
||||
m_Core.m_Id = m_pPlayer->GetCID();
|
||||
GameServer()->m_World.m_Core.m_apCharacters[m_pPlayer->GetCID()] = &m_Core;
|
||||
|
||||
m_ReckoningTick = 0;
|
||||
|
@ -107,7 +108,7 @@ void CCharacter::SetWeapon(int W)
|
|||
m_LastWeapon = m_Core.m_ActiveWeapon;
|
||||
m_QueuedWeapon = -1;
|
||||
m_Core.m_ActiveWeapon = W;
|
||||
GameServer()->CreateSound(m_Pos, SOUND_WEAPON_SWITCH, Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID()));
|
||||
GameServer()->CreateSound(m_Pos, SOUND_WEAPON_SWITCH, TeamMask());
|
||||
|
||||
if(m_Core.m_ActiveWeapon < 0 || m_Core.m_ActiveWeapon >= NUM_WEAPONS)
|
||||
m_Core.m_ActiveWeapon = 0;
|
||||
|
@ -204,7 +205,7 @@ void CCharacter::HandleNinja()
|
|||
|
||||
if(NinjaTime % Server()->TickSpeed() == 0 && NinjaTime / Server()->TickSpeed() <= 5)
|
||||
{
|
||||
GameServer()->CreateDamageInd(m_Pos, 0, NinjaTime / Server()->TickSpeed(), Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID()));
|
||||
GameServer()->CreateDamageInd(m_Pos, 0, NinjaTime / Server()->TickSpeed(), TeamMask());
|
||||
}
|
||||
|
||||
m_Armor = clamp(10 - (NinjaTime / 15), 0, 10);
|
||||
|
@ -270,7 +271,7 @@ void CCharacter::HandleNinja()
|
|||
continue;
|
||||
|
||||
// Hit a player, give him damage and stuffs...
|
||||
GameServer()->CreateSound(aEnts[i]->m_Pos, SOUND_NINJA_HIT, Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID()));
|
||||
GameServer()->CreateSound(aEnts[i]->m_Pos, SOUND_NINJA_HIT, TeamMask());
|
||||
// set his velocity to fast upward (for now)
|
||||
if(m_NumObjectsHit < 10)
|
||||
m_apHitObjects[m_NumObjectsHit++] = aEnts[i];
|
||||
|
@ -385,7 +386,7 @@ void CCharacter::FireWeapon()
|
|||
if(m_PainSoundTimer <= 0 && !(m_LatestPrevInput.m_Fire & 1))
|
||||
{
|
||||
m_PainSoundTimer = 1 * Server()->TickSpeed();
|
||||
GameServer()->CreateSound(m_Pos, SOUND_PLAYER_PAIN_LONG, Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID()));
|
||||
GameServer()->CreateSound(m_Pos, SOUND_PLAYER_PAIN_LONG, TeamMask());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -407,7 +408,7 @@ void CCharacter::FireWeapon()
|
|||
{
|
||||
// reset objects Hit
|
||||
m_NumObjectsHit = 0;
|
||||
GameServer()->CreateSound(m_Pos, SOUND_HAMMER_FIRE, Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID()));
|
||||
GameServer()->CreateSound(m_Pos, SOUND_HAMMER_FIRE, TeamMask());
|
||||
|
||||
Antibot()->OnHammerFire(m_pPlayer->GetCID());
|
||||
|
||||
|
@ -429,9 +430,9 @@ void CCharacter::FireWeapon()
|
|||
|
||||
// set his velocity to fast upward (for now)
|
||||
if(length(pTarget->m_Pos - ProjStartPos) > 0.0f)
|
||||
GameServer()->CreateHammerHit(pTarget->m_Pos - normalize(pTarget->m_Pos - ProjStartPos) * GetProximityRadius() * 0.5f, Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID()));
|
||||
GameServer()->CreateHammerHit(pTarget->m_Pos - normalize(pTarget->m_Pos - ProjStartPos) * GetProximityRadius() * 0.5f, TeamMask());
|
||||
else
|
||||
GameServer()->CreateHammerHit(ProjStartPos, Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID()));
|
||||
GameServer()->CreateHammerHit(ProjStartPos, TeamMask());
|
||||
|
||||
vec2 Dir;
|
||||
if(length(pTarget->m_Pos - m_Pos) > 0.0f)
|
||||
|
@ -498,7 +499,7 @@ void CCharacter::FireWeapon()
|
|||
-1 //SoundImpact
|
||||
);
|
||||
|
||||
GameServer()->CreateSound(m_Pos, SOUND_GUN_FIRE, Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID()));
|
||||
GameServer()->CreateSound(m_Pos, SOUND_GUN_FIRE, TeamMask());
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -530,7 +531,7 @@ void CCharacter::FireWeapon()
|
|||
LaserReach = GameServer()->TuningList()[m_TuneZone].m_LaserReach;
|
||||
|
||||
new CLaser(&GameServer()->m_World, m_Pos, Direction, LaserReach, m_pPlayer->GetCID(), WEAPON_SHOTGUN);
|
||||
GameServer()->CreateSound(m_Pos, SOUND_SHOTGUN_FIRE, Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID()));
|
||||
GameServer()->CreateSound(m_Pos, SOUND_SHOTGUN_FIRE, TeamMask());
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -555,7 +556,7 @@ void CCharacter::FireWeapon()
|
|||
SOUND_GRENADE_EXPLODE //SoundImpact
|
||||
); //SoundImpact
|
||||
|
||||
GameServer()->CreateSound(m_Pos, SOUND_GRENADE_FIRE, Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID()));
|
||||
GameServer()->CreateSound(m_Pos, SOUND_GRENADE_FIRE, TeamMask());
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -568,7 +569,7 @@ void CCharacter::FireWeapon()
|
|||
LaserReach = GameServer()->TuningList()[m_TuneZone].m_LaserReach;
|
||||
|
||||
new CLaser(GameWorld(), m_Pos, Direction, LaserReach, m_pPlayer->GetCID(), WEAPON_LASER);
|
||||
GameServer()->CreateSound(m_Pos, SOUND_LASER_FIRE, Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID()));
|
||||
GameServer()->CreateSound(m_Pos, SOUND_LASER_FIRE, TeamMask());
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -581,7 +582,7 @@ void CCharacter::FireWeapon()
|
|||
m_Core.m_Ninja.m_CurrentMoveTime = g_pData->m_Weapons.m_Ninja.m_Movetime * Server()->TickSpeed() / 1000;
|
||||
m_Core.m_Ninja.m_OldVelAmount = length(m_Core.m_Vel);
|
||||
|
||||
GameServer()->CreateSound(m_Pos, SOUND_NINJA_FIRE, Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID()));
|
||||
GameServer()->CreateSound(m_Pos, SOUND_NINJA_FIRE, TeamMask());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -632,7 +633,7 @@ void CCharacter::GiveNinja()
|
|||
m_Core.m_ActiveWeapon = WEAPON_NINJA;
|
||||
|
||||
if(!m_Core.m_aWeapons[WEAPON_NINJA].m_Got)
|
||||
GameServer()->CreateSound(m_Pos, SOUND_PICKUP_NINJA, Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID()));
|
||||
GameServer()->CreateSound(m_Pos, SOUND_PICKUP_NINJA, TeamMask());
|
||||
}
|
||||
|
||||
void CCharacter::RemoveNinja()
|
||||
|
@ -690,7 +691,7 @@ void CCharacter::OnDirectInput(CNetObj_PlayerInput *pNewInput)
|
|||
|
||||
void CCharacter::ResetHook()
|
||||
{
|
||||
m_Core.m_HookedPlayer = -1;
|
||||
m_Core.SetHookedPlayer(-1);
|
||||
m_Core.m_HookState = HOOK_RETRACTED;
|
||||
m_Core.m_TriggeredEvents |= COREEVENT_HOOK_RETRACT;
|
||||
m_Core.m_HookPos = m_Core.m_Pos;
|
||||
|
@ -912,7 +913,7 @@ void CCharacter::Die(int Killer, int Weapon)
|
|||
Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, -1);
|
||||
|
||||
// a nice sound
|
||||
GameServer()->CreateSound(m_Pos, SOUND_PLAYER_DIE, Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID()));
|
||||
GameServer()->CreateSound(m_Pos, SOUND_PLAYER_DIE, TeamMask());
|
||||
|
||||
// this is to rate limit respawning to 3 secs
|
||||
m_pPlayer->m_PreviousDieTick = m_pPlayer->m_DieTick;
|
||||
|
@ -923,7 +924,7 @@ void CCharacter::Die(int Killer, int Weapon)
|
|||
|
||||
GameServer()->m_World.RemoveEntity(this);
|
||||
GameServer()->m_World.m_Core.m_apCharacters[m_pPlayer->GetCID()] = 0;
|
||||
GameServer()->CreateDeath(m_Pos, m_pPlayer->GetCID(), Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID()));
|
||||
GameServer()->CreateDeath(m_Pos, m_pPlayer->GetCID(), TeamMask());
|
||||
Teams()->OnCharacterDeath(GetPlayer()->GetCID(), Weapon);
|
||||
}
|
||||
|
||||
|
@ -1191,8 +1192,33 @@ void CCharacter::Snap(int SnappingClient)
|
|||
if(!Server()->Translate(ID, SnappingClient))
|
||||
return;
|
||||
|
||||
if(NetworkClipped(SnappingClient) || !CanSnapCharacter(SnappingClient))
|
||||
if(!CanSnapCharacter(SnappingClient))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// A player may not be clipped away if his hook or a hook attached to him is in the field of view
|
||||
bool PlayerAndHookNotInView = NetworkClippedLine(SnappingClient, m_Pos, m_Core.m_HookPos);
|
||||
bool AttachedHookInView = false;
|
||||
if(PlayerAndHookNotInView)
|
||||
{
|
||||
for(const auto &AttachedPlayerID : m_Core.m_AttachedPlayers)
|
||||
{
|
||||
CCharacter *OtherPlayer = GameServer()->GetPlayerChar(AttachedPlayerID);
|
||||
if(OtherPlayer && OtherPlayer->m_Core.m_HookedPlayer == ID)
|
||||
{
|
||||
if(!NetworkClippedLine(SnappingClient, m_Pos, OtherPlayer->m_Pos))
|
||||
{
|
||||
AttachedHookInView = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(PlayerAndHookNotInView && !AttachedHookInView)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SnapCharacter(SnappingClient, ID);
|
||||
|
||||
|
@ -2092,7 +2118,7 @@ void CCharacter::DDRaceTick()
|
|||
{
|
||||
if(m_FreezeTime % Server()->TickSpeed() == Server()->TickSpeed() - 1 || m_FreezeTime == -1)
|
||||
{
|
||||
GameServer()->CreateDamageInd(m_Pos, 0, (m_FreezeTime + 1) / Server()->TickSpeed(), Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID()));
|
||||
GameServer()->CreateDamageInd(m_Pos, 0, (m_FreezeTime + 1) / Server()->TickSpeed(), TeamMask());
|
||||
}
|
||||
if(m_FreezeTime > 0)
|
||||
m_FreezeTime--;
|
||||
|
@ -2201,12 +2227,12 @@ void CCharacter::DDRacePostCoreTick()
|
|||
// teleport gun
|
||||
if(m_TeleGunTeleport)
|
||||
{
|
||||
GameServer()->CreateDeath(m_Pos, m_pPlayer->GetCID(), Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID()));
|
||||
GameServer()->CreateDeath(m_Pos, m_pPlayer->GetCID(), TeamMask());
|
||||
m_Core.m_Pos = m_TeleGunPos;
|
||||
if(!m_IsBlueTeleGunTeleport)
|
||||
m_Core.m_Vel = vec2(0, 0);
|
||||
GameServer()->CreateDeath(m_TeleGunPos, m_pPlayer->GetCID(), Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID()));
|
||||
GameServer()->CreateSound(m_TeleGunPos, SOUND_WEAPON_SPAWN, Teams()->TeamMask(Team(), -1, m_pPlayer->GetCID()));
|
||||
GameServer()->CreateDeath(m_TeleGunPos, m_pPlayer->GetCID(), TeamMask());
|
||||
GameServer()->CreateSound(m_TeleGunPos, SOUND_WEAPON_SPAWN, TeamMask());
|
||||
m_TeleGunTeleport = false;
|
||||
m_IsBlueTeleGunTeleport = false;
|
||||
}
|
||||
|
@ -2402,5 +2428,5 @@ int64_t CCharacter::TeamMask()
|
|||
|
||||
void CCharacter::SwapClients(int Client1, int Client2)
|
||||
{
|
||||
m_Core.m_HookedPlayer = m_Core.m_HookedPlayer == Client1 ? Client2 : m_Core.m_HookedPlayer == Client2 ? Client1 : m_Core.m_HookedPlayer;
|
||||
m_Core.SetHookedPlayer(m_Core.m_HookedPlayer == Client1 ? Client2 : m_Core.m_HookedPlayer == Client2 ? Client1 : m_Core.m_HookedPlayer);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
/* (c) Shereef Marzouk. See "licence DDRace.txt" and the readme.txt in the root of the distribution for more information. */
|
||||
#include "dragger.h"
|
||||
#include <engine/config.h>
|
||||
#include <engine/server.h>
|
||||
#include <engine/shared/config.h>
|
||||
|
@ -11,144 +10,150 @@
|
|||
#include <game/version.h>
|
||||
|
||||
#include "character.h"
|
||||
#include "dragger.h"
|
||||
#include "dragger_beam.h"
|
||||
|
||||
CDragger::CDragger(CGameWorld *pGameWorld, vec2 Pos, float Strength, bool NW,
|
||||
int CaughtTeam, int Layer, int Number) :
|
||||
CDragger::CDragger(CGameWorld *pGameWorld, vec2 Pos, float Strength, bool IgnoreWalls, int Layer, int Number) :
|
||||
CEntity(pGameWorld, CGameWorld::ENTTYPE_LASER)
|
||||
{
|
||||
m_TargetID = -1;
|
||||
m_Layer = Layer;
|
||||
m_Number = Number;
|
||||
m_Pos = Pos;
|
||||
m_Strength = Strength;
|
||||
m_IgnoreWalls = IgnoreWalls;
|
||||
m_Layer = Layer;
|
||||
m_Number = Number;
|
||||
m_EvalTick = Server()->Tick();
|
||||
m_NW = NW;
|
||||
m_CaughtTeam = CaughtTeam;
|
||||
|
||||
for(auto &TargetId : m_TargetIdInTeam)
|
||||
{
|
||||
TargetId = -1;
|
||||
}
|
||||
mem_zero(m_apDraggerBeam, sizeof(m_apDraggerBeam));
|
||||
GameWorld()->InsertEntity(this);
|
||||
|
||||
for(int &SoloID : m_SoloIDs)
|
||||
{
|
||||
SoloID = -1;
|
||||
}
|
||||
|
||||
for(int &SoloEntID : m_SoloEntIDs)
|
||||
{
|
||||
SoloEntID = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void CDragger::Move()
|
||||
void CDragger::Tick()
|
||||
{
|
||||
if(m_TargetID >= 0)
|
||||
if(Server()->Tick() % int(Server()->TickSpeed() * 0.15f) == 0)
|
||||
{
|
||||
CCharacter *pTarget = GameServer()->GetPlayerChar(m_TargetID);
|
||||
if(!pTarget || pTarget->m_Super || pTarget->IsPaused() || (m_Layer == LAYER_SWITCH && m_Number && !GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[pTarget->Team()]))
|
||||
int Flags;
|
||||
m_EvalTick = Server()->Tick();
|
||||
int index = GameServer()->Collision()->IsMover(m_Pos.x, m_Pos.y, &Flags);
|
||||
if(index)
|
||||
{
|
||||
m_TargetID = -1;
|
||||
m_Core = GameServer()->Collision()->CpSpeed(index, Flags);
|
||||
}
|
||||
}
|
||||
m_Pos += m_Core;
|
||||
|
||||
CCharacter *TempEnts[MAX_CLIENTS];
|
||||
mem_zero(TempEnts, sizeof(TempEnts));
|
||||
|
||||
for(int &SoloEntID : m_SoloEntIDs)
|
||||
{
|
||||
SoloEntID = -1;
|
||||
}
|
||||
|
||||
int Num = GameServer()->m_World.FindEntities(m_Pos, g_Config.m_SvDraggerRange,
|
||||
(CEntity **)TempEnts, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER);
|
||||
|
||||
int Id = -1;
|
||||
int MinLen = 0;
|
||||
CCharacter *Temp;
|
||||
for(int i = 0; i < Num; i++)
|
||||
{
|
||||
Temp = TempEnts[i];
|
||||
m_SoloEntIDs[i] = Temp->GetPlayer()->GetCID();
|
||||
if(Temp->Team() != m_CaughtTeam)
|
||||
// Adopt the new position for all outgoing laser beams
|
||||
for(auto &DraggerBeam : m_apDraggerBeam)
|
||||
{
|
||||
m_SoloEntIDs[i] = -1;
|
||||
continue;
|
||||
}
|
||||
if(m_Layer == LAYER_SWITCH && m_Number && !GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[Temp->Team()])
|
||||
{
|
||||
m_SoloEntIDs[i] = -1;
|
||||
continue;
|
||||
}
|
||||
int Res =
|
||||
m_NW ?
|
||||
GameServer()->Collision()->IntersectNoLaserNW(m_Pos, Temp->m_Pos, 0, 0) :
|
||||
GameServer()->Collision()->IntersectNoLaser(m_Pos, Temp->m_Pos, 0, 0);
|
||||
|
||||
if(Res == 0)
|
||||
{
|
||||
int Len = length(Temp->m_Pos - m_Pos);
|
||||
if(MinLen == 0 || MinLen > Len)
|
||||
if(DraggerBeam != nullptr)
|
||||
{
|
||||
MinLen = Len;
|
||||
Id = i;
|
||||
DraggerBeam->SetPos(m_Pos);
|
||||
}
|
||||
|
||||
if(!Temp->Teams()->m_Core.GetSolo(Temp->GetPlayer()->GetCID()))
|
||||
m_SoloEntIDs[i] = -1;
|
||||
}
|
||||
else
|
||||
|
||||
LookForPlayersToDrag();
|
||||
}
|
||||
}
|
||||
|
||||
void CDragger::LookForPlayersToDrag()
|
||||
{
|
||||
// Create a list of players who are in the range of the dragger
|
||||
CCharacter *pPlayersInRange[MAX_CLIENTS];
|
||||
mem_zero(pPlayersInRange, sizeof(pPlayersInRange));
|
||||
|
||||
int NumPlayersInRange = GameServer()->m_World.FindEntities(m_Pos,
|
||||
g_Config.m_SvDraggerRange - CCharacter::ms_PhysSize,
|
||||
(CEntity **)pPlayersInRange, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER);
|
||||
|
||||
// The closest player (within range) in a team is selected as the target
|
||||
int ClosestTargetIdInTeam[MAX_CLIENTS];
|
||||
bool CanStillBeTeamTarget[MAX_CLIENTS];
|
||||
bool IsTarget[MAX_CLIENTS];
|
||||
int MinDistInTeam[MAX_CLIENTS];
|
||||
mem_zero(CanStillBeTeamTarget, sizeof(CanStillBeTeamTarget));
|
||||
mem_zero(MinDistInTeam, sizeof(MinDistInTeam));
|
||||
mem_zero(IsTarget, sizeof(IsTarget));
|
||||
for(int &TargetId : ClosestTargetIdInTeam)
|
||||
{
|
||||
TargetId = -1;
|
||||
}
|
||||
|
||||
for(int i = 0; i < NumPlayersInRange; i++)
|
||||
{
|
||||
CCharacter *pTarget = pPlayersInRange[i];
|
||||
const int &TargetTeam = pTarget->Team();
|
||||
|
||||
// Do not create a dragger beam for super player
|
||||
if(TargetTeam == TEAM_SUPER)
|
||||
{
|
||||
m_SoloEntIDs[i] = -1;
|
||||
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 &&
|
||||
!GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[TargetTeam])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Dragger beams can be created only for reachable, alive players
|
||||
int IsReachable =
|
||||
m_IgnoreWalls ?
|
||||
!GameServer()->Collision()->IntersectNoLaserNW(m_Pos, pTarget->m_Pos, 0, 0) :
|
||||
!GameServer()->Collision()->IntersectNoLaser(m_Pos, pTarget->m_Pos, 0, 0);
|
||||
if(IsReachable && pTarget->IsAlive())
|
||||
{
|
||||
const int &TargetClientId = pTarget->GetPlayer()->GetCID();
|
||||
// Solo players are dragged independently from the rest of the team
|
||||
if(pTarget->Teams()->m_Core.GetSolo(TargetClientId))
|
||||
{
|
||||
IsTarget[TargetClientId] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
int Distance = distance(pTarget->m_Pos, m_Pos);
|
||||
if(MinDistInTeam[TargetTeam] == 0 || MinDistInTeam[TargetTeam] > Distance)
|
||||
{
|
||||
MinDistInTeam[TargetTeam] = Distance;
|
||||
ClosestTargetIdInTeam[TargetTeam] = TargetClientId;
|
||||
}
|
||||
CanStillBeTeamTarget[TargetClientId] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(m_TargetID < 0)
|
||||
m_TargetID = Id != -1 ? TempEnts[Id]->GetPlayer()->GetCID() : -1;
|
||||
|
||||
if(m_TargetID >= 0)
|
||||
// Set the closest player for each team as a target if the team does not have a target player yet
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
const CCharacter *pTarget = GameServer()->GetPlayerChar(m_TargetID);
|
||||
for(auto &SoloEntID : m_SoloEntIDs)
|
||||
if((m_TargetIdInTeam[i] != -1 && !CanStillBeTeamTarget[m_TargetIdInTeam[i]]) || m_TargetIdInTeam[i] == -1)
|
||||
{
|
||||
if(GameServer()->GetPlayerChar(SoloEntID) == pTarget)
|
||||
SoloEntID = -1;
|
||||
m_TargetIdInTeam[i] = ClosestTargetIdInTeam[i];
|
||||
}
|
||||
if(m_TargetIdInTeam[i] != -1)
|
||||
{
|
||||
IsTarget[m_TargetIdInTeam[i]] = true;
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
// Create Dragger Beams which have not been created yet
|
||||
if(IsTarget[i] && m_apDraggerBeam[i] == nullptr)
|
||||
{
|
||||
m_apDraggerBeam[i] = new CDraggerBeam(&GameServer()->m_World, this, m_Pos, m_Strength, m_IgnoreWalls, i);
|
||||
}
|
||||
// Remove dragger beams that have not yet been deleted
|
||||
else if(!IsTarget[i] && m_apDraggerBeam[i] != nullptr)
|
||||
{
|
||||
m_apDraggerBeam[i]->Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CDragger::Drag()
|
||||
void CDragger::RemoveDraggerBeam(int ClientID)
|
||||
{
|
||||
if(m_TargetID < 0)
|
||||
return;
|
||||
|
||||
CCharacter *pTarget = GameServer()->GetPlayerChar(m_TargetID);
|
||||
|
||||
for(int i = -1; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
if(i >= 0)
|
||||
pTarget = GameServer()->GetPlayerChar(m_SoloEntIDs[i]);
|
||||
|
||||
if(!pTarget)
|
||||
continue;
|
||||
|
||||
int Res = 0;
|
||||
if(!m_NW)
|
||||
Res = GameServer()->Collision()->IntersectNoLaser(m_Pos,
|
||||
pTarget->m_Pos, 0, 0);
|
||||
else
|
||||
Res = GameServer()->Collision()->IntersectNoLaserNW(m_Pos,
|
||||
pTarget->m_Pos, 0, 0);
|
||||
if(Res || length(m_Pos - pTarget->m_Pos) > g_Config.m_SvDraggerRange)
|
||||
{
|
||||
pTarget = 0;
|
||||
if(i == -1)
|
||||
m_TargetID = -1;
|
||||
else
|
||||
m_SoloEntIDs[i] = -1;
|
||||
}
|
||||
else if(length(m_Pos - pTarget->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);
|
||||
}
|
||||
}
|
||||
m_apDraggerBeam[ClientID] = nullptr;
|
||||
}
|
||||
|
||||
void CDragger::Reset()
|
||||
|
@ -156,157 +161,80 @@ void CDragger::Reset()
|
|||
m_MarkedForDestroy = true;
|
||||
}
|
||||
|
||||
void CDragger::Tick()
|
||||
{
|
||||
if(((CGameControllerDDRace *)GameServer()->m_pController)->m_Teams.GetTeamState(m_CaughtTeam) == CGameTeams::TEAMSTATE_EMPTY)
|
||||
return;
|
||||
if(Server()->Tick() % int(Server()->TickSpeed() * 0.15f) == 0)
|
||||
{
|
||||
int Flags;
|
||||
m_EvalTick = Server()->Tick();
|
||||
int index = GameServer()->Collision()->IsMover(m_Pos.x, m_Pos.y,
|
||||
&Flags);
|
||||
if(index)
|
||||
{
|
||||
m_Core = GameServer()->Collision()->CpSpeed(index, Flags);
|
||||
}
|
||||
m_Pos += m_Core;
|
||||
Move();
|
||||
}
|
||||
Drag();
|
||||
}
|
||||
|
||||
void CDragger::Snap(int SnappingClient)
|
||||
{
|
||||
if(((CGameControllerDDRace *)GameServer()->m_pController)->m_Teams.GetTeamState(m_CaughtTeam) == CGameTeams::TEAMSTATE_EMPTY)
|
||||
// Only players with the dragger in their field of view or who want to see everything will receive the snap
|
||||
if(NetworkClipped(SnappingClient))
|
||||
return;
|
||||
|
||||
if(NetworkClipped(SnappingClient, m_Pos))
|
||||
return;
|
||||
// Send the dragger in its resting position if the player would not otherwise see a dragger beam
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
if(m_apDraggerBeam[i] != nullptr)
|
||||
{
|
||||
CCharacter *pChar = GameServer()->GetPlayerChar(i);
|
||||
if(pChar && pChar->CanSnapCharacter(SnappingClient))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CCharacter *pChar = GameServer()->GetPlayerChar(SnappingClient);
|
||||
|
||||
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)
|
||||
pChar = GameServer()->GetPlayerChar(GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID);
|
||||
|
||||
if(pChar && pChar->Team() != m_CaughtTeam)
|
||||
return;
|
||||
|
||||
int SnappingClientVersion = SnappingClient != SERVER_DEMO_CLIENT ? GameServer()->GetClientVersion(SnappingClient) : CLIENT_VERSIONNR;
|
||||
int SnappingClientVersion = SnappingClient != SERVER_DEMO_CLIENT ?
|
||||
GameServer()->GetClientVersion(SnappingClient) :
|
||||
CLIENT_VERSIONNR;
|
||||
|
||||
CNetObj_EntityEx *pEntData = 0;
|
||||
if(SnappingClientVersion >= VERSION_DDNET_SWITCH)
|
||||
{
|
||||
pEntData = static_cast<CNetObj_EntityEx *>(Server()->SnapNewItem(NETOBJTYPE_ENTITYEX, GetID(), sizeof(CNetObj_EntityEx)));
|
||||
pEntData = static_cast<CNetObj_EntityEx *>(Server()->SnapNewItem(NETOBJTYPE_ENTITYEX, GetID(),
|
||||
sizeof(CNetObj_EntityEx)));
|
||||
if(pEntData)
|
||||
{
|
||||
pEntData->m_SwitchNumber = m_Number;
|
||||
pEntData->m_Layer = m_Layer;
|
||||
pEntData->m_EntityClass = clamp(ENTITYCLASS_DRAGGER_WEAK + round_to_int(m_Strength) - 1, (int)ENTITYCLASS_DRAGGER_WEAK, (int)ENTITYCLASS_DRAGGER_STRONG);
|
||||
pEntData->m_EntityClass = clamp(ENTITYCLASS_DRAGGER_WEAK + round_to_int(m_Strength) - 1,
|
||||
(int)ENTITYCLASS_DRAGGER_WEAK, (int)ENTITYCLASS_DRAGGER_STRONG);
|
||||
}
|
||||
}
|
||||
|
||||
CCharacter *pTarget = m_TargetID < 0 ? 0 : GameServer()->GetPlayerChar(m_TargetID);
|
||||
|
||||
for(int &SoloID : m_SoloIDs)
|
||||
else
|
||||
{
|
||||
if(SoloID == -1)
|
||||
break;
|
||||
// Emulate turned off blinking dragger for old clients
|
||||
CCharacter *pChar = GameServer()->GetPlayerChar(SnappingClient);
|
||||
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)
|
||||
pChar = GameServer()->GetPlayerChar(GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID);
|
||||
|
||||
Server()->SnapFreeID(SoloID);
|
||||
SoloID = -1;
|
||||
int Tick = (Server()->Tick() % Server()->TickSpeed()) % 11;
|
||||
if(pChar && m_Layer == LAYER_SWITCH && m_Number > 0 &&
|
||||
!GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[pChar->Team()] && (!Tick))
|
||||
return;
|
||||
}
|
||||
|
||||
int pos = 0;
|
||||
CNetObj_Laser *obj = static_cast<CNetObj_Laser *>(Server()->SnapNewItem(
|
||||
NETOBJTYPE_LASER, GetID(), sizeof(CNetObj_Laser)));
|
||||
|
||||
for(int i = -1; i < MAX_CLIENTS; i++)
|
||||
if(!obj)
|
||||
return;
|
||||
|
||||
obj->m_X = (int)m_Pos.x;
|
||||
obj->m_Y = (int)m_Pos.y;
|
||||
obj->m_FromX = (int)m_Pos.x;
|
||||
obj->m_FromY = (int)m_Pos.y;
|
||||
|
||||
if(pEntData)
|
||||
{
|
||||
if(i >= 0)
|
||||
{
|
||||
pTarget = GameServer()->GetPlayerChar(m_SoloEntIDs[i]);
|
||||
|
||||
if(!pTarget)
|
||||
continue;
|
||||
}
|
||||
|
||||
if(pTarget && NetworkClipped(SnappingClient, pTarget->m_Pos))
|
||||
continue;
|
||||
|
||||
if(i != -1 || SnappingClientVersion < VERSION_DDNET_SWITCH)
|
||||
{
|
||||
int Tick = (Server()->Tick() % Server()->TickSpeed()) % 11;
|
||||
if(pChar && m_Layer == LAYER_SWITCH && m_Number && !GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[pChar->Team()] && (!Tick))
|
||||
continue;
|
||||
}
|
||||
|
||||
// send to spectators only active draggers and some inactive from team 0
|
||||
if(!pChar && !pTarget && m_CaughtTeam != 0)
|
||||
continue;
|
||||
|
||||
if(pChar && pTarget && pTarget->GetPlayer()->GetCID() != pChar->GetPlayer()->GetCID() && ((pChar->GetPlayer()->m_ShowOthers == SHOW_OTHERS_OFF && (pChar->Teams()->m_Core.GetSolo(SnappingClient) || pChar->Teams()->m_Core.GetSolo(pTarget->GetPlayer()->GetCID()))) || (pChar->GetPlayer()->m_ShowOthers == SHOW_OTHERS_ONLY_TEAM && !pTarget->SameTeam(SnappingClient))))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
CNetObj_Laser *obj;
|
||||
|
||||
if(i == -1)
|
||||
{
|
||||
obj = static_cast<CNetObj_Laser *>(Server()->SnapNewItem(
|
||||
NETOBJTYPE_LASER, GetID(), sizeof(CNetObj_Laser)));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_SoloIDs[pos] = Server()->SnapNewID();
|
||||
obj = static_cast<CNetObj_Laser *>(Server()->SnapNewItem( // TODO: Have to free IDs again?
|
||||
NETOBJTYPE_LASER, m_SoloIDs[pos], sizeof(CNetObj_Laser)));
|
||||
pos++;
|
||||
}
|
||||
|
||||
if(!obj)
|
||||
continue;
|
||||
obj->m_X = (int)m_Pos.x;
|
||||
obj->m_Y = (int)m_Pos.y;
|
||||
if(pTarget)
|
||||
{
|
||||
obj->m_FromX = (int)pTarget->m_Pos.x;
|
||||
obj->m_FromY = (int)pTarget->m_Pos.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
obj->m_FromX = (int)m_Pos.x;
|
||||
obj->m_FromY = (int)m_Pos.y;
|
||||
}
|
||||
|
||||
if(pEntData && i == -1)
|
||||
{
|
||||
obj->m_StartTick = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int StartTick = m_EvalTick;
|
||||
if(StartTick < Server()->Tick() - 4)
|
||||
StartTick = Server()->Tick() - 4;
|
||||
else if(StartTick > Server()->Tick())
|
||||
StartTick = Server()->Tick();
|
||||
obj->m_StartTick = StartTick;
|
||||
}
|
||||
obj->m_StartTick = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int StartTick = m_EvalTick;
|
||||
if(StartTick < Server()->Tick() - 4)
|
||||
StartTick = Server()->Tick() - 4;
|
||||
else if(StartTick > Server()->Tick())
|
||||
StartTick = Server()->Tick();
|
||||
obj->m_StartTick = StartTick;
|
||||
}
|
||||
}
|
||||
|
||||
CDraggerTeam::CDraggerTeam(CGameWorld *pGameWorld, vec2 Pos, float Strength,
|
||||
bool NW, int Layer, int Number)
|
||||
{
|
||||
for(int i = 0; i < MAX_CLIENTS; ++i)
|
||||
{
|
||||
m_Draggers[i] = new CDragger(pGameWorld, Pos, Strength, NW, i, Layer, Number);
|
||||
}
|
||||
}
|
||||
|
||||
//CDraggerTeam::~CDraggerTeam()
|
||||
//{
|
||||
// for (int i = 0; i < MAX_CLIENTS; ++i)
|
||||
// {
|
||||
// delete m_Draggers[i];
|
||||
// }
|
||||
//}
|
||||
|
|
|
@ -3,38 +3,43 @@
|
|||
#define GAME_SERVER_ENTITIES_DRAGGER_H
|
||||
|
||||
#include <game/server/entity.h>
|
||||
class CCharacter;
|
||||
class CDraggerBeam;
|
||||
|
||||
/**
|
||||
* Draggers generate dragger beams which pull players towards their center similar to a tractor beam
|
||||
*
|
||||
* A dragger will only generate one dragger beam per team for the closest player for whom the following criteria are met:
|
||||
* - The player is within the dragger range (sv_dragger_range).
|
||||
* - The player is not a super player
|
||||
* - The dragger is activated
|
||||
* - The dragger beam to be generated is not blocked by laser stoppers (or solid blocks if IgnoreWalls is set to false)
|
||||
* With the exception of solo players, for whom a dragger beam is always generated, regardless of the rest of the team,
|
||||
* if the above criteria are met. Solo players have no influence on the generation of the dragger beam for the rest
|
||||
* of the team.
|
||||
* A created dragger beam remains for the selected player until one of the criteria is no longer fulfilled. Only then
|
||||
* can a new dragger beam be created for that team, which may drag another team partner.
|
||||
*/
|
||||
class CDragger : public CEntity
|
||||
{
|
||||
// m_Core is the direction vector by which a dragger is shifted at each movement tick (every 150ms)
|
||||
vec2 m_Core;
|
||||
float m_Strength;
|
||||
bool m_IgnoreWalls;
|
||||
int m_EvalTick;
|
||||
void Move();
|
||||
void Drag();
|
||||
int m_TargetID;
|
||||
bool m_NW;
|
||||
int m_CaughtTeam;
|
||||
|
||||
int m_SoloEntIDs[MAX_CLIENTS];
|
||||
int m_SoloIDs[MAX_CLIENTS];
|
||||
int m_TargetIdInTeam[MAX_CLIENTS];
|
||||
CDraggerBeam *m_apDraggerBeam[MAX_CLIENTS];
|
||||
|
||||
void LookForPlayersToDrag();
|
||||
|
||||
public:
|
||||
CDragger(CGameWorld *pGameWorld, vec2 Pos, float Strength, bool NW,
|
||||
int CaughtTeam, int Layer = 0, int Number = 0);
|
||||
CDragger(CGameWorld *pGameWorld, vec2 Pos, float Strength, bool IgnoreWalls, int Layer = 0, int Number = 0);
|
||||
|
||||
void RemoveDraggerBeam(int ClientID);
|
||||
|
||||
void Reset() override;
|
||||
void Tick() override;
|
||||
void Snap(int SnappingClient) override;
|
||||
};
|
||||
|
||||
class CDraggerTeam
|
||||
{
|
||||
CDragger *m_Draggers[MAX_CLIENTS];
|
||||
|
||||
public:
|
||||
CDraggerTeam(CGameWorld *pGameWorld, vec2 Pos, float Strength, bool NW = false, int Layer = 0, int Number = 0);
|
||||
//~CDraggerTeam();
|
||||
};
|
||||
|
||||
#endif // GAME_SERVER_ENTITIES_DRAGGER_H
|
||||
|
|
115
src/game/server/entities/dragger_beam.cpp
Normal file
115
src/game/server/entities/dragger_beam.cpp
Normal file
|
@ -0,0 +1,115 @@
|
|||
/* See "licence DDRace.txt" and the readme.txt in the root of the distribution for more information. */
|
||||
#include "dragger_beam.h"
|
||||
#include "character.h"
|
||||
#include "dragger.h"
|
||||
#include <engine/config.h>
|
||||
#include <engine/server.h>
|
||||
#include <game/generated/protocol.h>
|
||||
#include <game/server/gamecontext.h>
|
||||
#include <game/server/gamemodes/DDRace.h>
|
||||
#include <game/server/player.h>
|
||||
#include <game/server/teams.h>
|
||||
|
||||
CDraggerBeam::CDraggerBeam(CGameWorld *pGameWorld, CDragger *pDragger, vec2 Pos, float Strength, bool IgnoreWalls,
|
||||
int ForClientID) :
|
||||
CEntity(pGameWorld, CGameWorld::ENTTYPE_LASER)
|
||||
{
|
||||
m_pDragger = pDragger;
|
||||
m_Pos = Pos;
|
||||
m_Strength = Strength;
|
||||
m_IgnoreWalls = IgnoreWalls;
|
||||
m_ForClientID = ForClientID;
|
||||
m_Active = true;
|
||||
m_EvalTick = Server()->Tick();
|
||||
|
||||
GameWorld()->InsertEntity(this);
|
||||
}
|
||||
|
||||
void CDraggerBeam::Tick()
|
||||
{
|
||||
if(!m_Active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Drag only if the player is reachable and alive
|
||||
CCharacter *pTarget = GameServer()->GetPlayerChar(m_ForClientID);
|
||||
if(!pTarget)
|
||||
{
|
||||
Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
int IsReachable =
|
||||
m_IgnoreWalls ?
|
||||
!GameServer()->Collision()->IntersectNoLaserNW(m_Pos, pTarget->m_Pos, 0, 0) :
|
||||
!GameServer()->Collision()->IntersectNoLaser(m_Pos, pTarget->m_Pos, 0, 0);
|
||||
// This check is necessary because the check in CDragger::LookForPlayersToDrag only happens every 150ms
|
||||
if(!IsReachable ||
|
||||
distance(pTarget->m_Pos, m_Pos) >= g_Config.m_SvDraggerRange || !pTarget->IsAlive())
|
||||
{
|
||||
Reset();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void CDraggerBeam::SetPos(vec2 Pos)
|
||||
{
|
||||
m_Pos = Pos;
|
||||
}
|
||||
|
||||
void CDraggerBeam::Reset()
|
||||
{
|
||||
m_MarkedForDestroy = true;
|
||||
m_Active = false;
|
||||
|
||||
m_pDragger->RemoveDraggerBeam(m_ForClientID);
|
||||
}
|
||||
|
||||
void CDraggerBeam::Snap(int SnappingClient)
|
||||
{
|
||||
if(!m_Active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Only players who can see the player attached to the dragger can see the dragger beam
|
||||
CCharacter *pTarget = GameServer()->GetPlayerChar(m_ForClientID);
|
||||
if(!pTarget->CanSnapCharacter(SnappingClient))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Only players with the dragger beam in their field of view or who want to see everything will receive the snap
|
||||
vec2 TargetPos = vec2(pTarget->m_Pos.x, pTarget->m_Pos.y);
|
||||
if(NetworkClippedLine(SnappingClient, m_Pos, TargetPos))
|
||||
{
|
||||
return;
|
||||
}
|
||||
CNetObj_Laser *obj = static_cast<CNetObj_Laser *>(
|
||||
Server()->SnapNewItem(NETOBJTYPE_LASER, GetID(), sizeof(CNetObj_Laser)));
|
||||
if(!obj)
|
||||
{
|
||||
return;
|
||||
}
|
||||
obj->m_X = (int)m_Pos.x;
|
||||
obj->m_Y = (int)m_Pos.y;
|
||||
obj->m_FromX = (int)TargetPos.x;
|
||||
obj->m_FromY = (int)TargetPos.y;
|
||||
|
||||
int StartTick = m_EvalTick;
|
||||
if(StartTick < Server()->Tick() - 4)
|
||||
{
|
||||
StartTick = Server()->Tick() - 4;
|
||||
}
|
||||
else if(StartTick > Server()->Tick())
|
||||
{
|
||||
StartTick = Server()->Tick();
|
||||
}
|
||||
obj->m_StartTick = StartTick;
|
||||
}
|
41
src/game/server/entities/dragger_beam.h
Normal file
41
src/game/server/entities/dragger_beam.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/* See "licence DDRace.txt" and the readme.txt in the root of the distribution for more information. */
|
||||
#ifndef GAME_SERVER_ENTITIES_DRAGGER_BEAM_H
|
||||
#define GAME_SERVER_ENTITIES_DRAGGER_BEAM_H
|
||||
|
||||
#include "dragger.h"
|
||||
#include <game/server/entity.h>
|
||||
|
||||
/**
|
||||
* Dragger beams pull a selected player towards their center
|
||||
*
|
||||
* Dragger beams are generated by a particular dragger for the player closest to it and for whom certain criteria are
|
||||
* met. Dragger beams exist until these criteria are no longer met. Dragger beams dissolve and automatically
|
||||
* de-register from their dragger source as soon as the player for whom they were created:
|
||||
* - is no longer alive
|
||||
* - is no longer in range (sv_dragger_range)
|
||||
* - can no longer be dragged because the beam is intercepted by a laser stopper (or if !IgnoreWalls by solid blocks)
|
||||
*
|
||||
* Dragger beams accelerate the selected player every tick towards their center. The length of the speed vector, which
|
||||
* is added to that of the player, depends only on the strength of the dragger and is between 1 and 3 units long. If
|
||||
* the player is in the center of the dragger, it will not accelerate.
|
||||
*/
|
||||
class CDraggerBeam : public CEntity
|
||||
{
|
||||
CDragger *m_pDragger;
|
||||
float m_Strength;
|
||||
bool m_IgnoreWalls;
|
||||
int m_ForClientID;
|
||||
int m_EvalTick;
|
||||
bool m_Active;
|
||||
|
||||
public:
|
||||
CDraggerBeam(CGameWorld *pGameWorld, CDragger *pDragger, vec2 Pos, float Strength, bool IgnoreWalls, int ForClientID);
|
||||
|
||||
void SetPos(vec2 Pos);
|
||||
|
||||
void Reset() override;
|
||||
void Tick() override;
|
||||
void Snap(int SnappingClient) override;
|
||||
};
|
||||
|
||||
#endif // GAME_SERVER_ENTITIES_DRAGGER_BEAM_H
|
|
@ -11,88 +11,21 @@
|
|||
#include "gun.h"
|
||||
#include "plasma.h"
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// CGun
|
||||
//////////////////////////////////////////////////
|
||||
CGun::CGun(CGameWorld *pGameWorld, vec2 Pos, bool Freeze, bool Explosive, int Layer, int Number) :
|
||||
CEntity(pGameWorld, CGameWorld::ENTTYPE_LASER)
|
||||
{
|
||||
m_Layer = Layer;
|
||||
m_Number = Number;
|
||||
m_LastFire = Server()->Tick();
|
||||
m_Pos = Pos;
|
||||
m_EvalTick = Server()->Tick();
|
||||
m_Freeze = Freeze;
|
||||
m_Explosive = Explosive;
|
||||
m_Layer = Layer;
|
||||
m_Number = Number;
|
||||
m_EvalTick = Server()->Tick();
|
||||
|
||||
mem_zero(m_LastFireTeam, sizeof(m_LastFireTeam));
|
||||
mem_zero(m_LastFireSolo, sizeof(m_LastFireSolo));
|
||||
GameWorld()->InsertEntity(this);
|
||||
}
|
||||
|
||||
void CGun::Fire()
|
||||
{
|
||||
CCharacter *Ents[MAX_CLIENTS];
|
||||
int IdInTeam[MAX_CLIENTS];
|
||||
int LenInTeam[MAX_CLIENTS];
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
IdInTeam[i] = -1;
|
||||
LenInTeam[i] = 0;
|
||||
}
|
||||
|
||||
int Num = -1;
|
||||
Num = GameServer()->m_World.FindEntities(m_Pos, g_Config.m_SvPlasmaRange, (CEntity **)Ents, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER);
|
||||
|
||||
for(int i = 0; i < Num; i++)
|
||||
{
|
||||
CCharacter *Target = Ents[i];
|
||||
//now gun doesn't affect on super
|
||||
if(Target->Team() == TEAM_SUPER)
|
||||
continue;
|
||||
if(m_Layer == LAYER_SWITCH && m_Number > 0 && !GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[Target->Team()])
|
||||
continue;
|
||||
int res = GameServer()->Collision()->IntersectLine(m_Pos, Target->m_Pos, 0, 0);
|
||||
if(!res)
|
||||
{
|
||||
int Len = length(Target->m_Pos - m_Pos);
|
||||
if(LenInTeam[Target->Team()] == 0 || LenInTeam[Target->Team()] > Len)
|
||||
{
|
||||
LenInTeam[Target->Team()] = Len;
|
||||
IdInTeam[Target->Team()] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
if(IdInTeam[i] != -1)
|
||||
{
|
||||
CCharacter *Target = Ents[IdInTeam[i]];
|
||||
new CPlasma(&GameServer()->m_World, m_Pos, normalize(Target->m_Pos - m_Pos), m_Freeze, m_Explosive, i);
|
||||
m_LastFire = Server()->Tick();
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < Num; i++)
|
||||
{
|
||||
CCharacter *Target = Ents[i];
|
||||
if(Target->IsAlive() && Target->Teams()->m_Core.GetSolo(Target->GetPlayer()->GetCID()))
|
||||
{
|
||||
if(IdInTeam[Target->Team()] != i)
|
||||
{
|
||||
int res = GameServer()->Collision()->IntersectLine(m_Pos, Target->m_Pos, 0, 0);
|
||||
if(!res)
|
||||
{
|
||||
new CPlasma(&GameServer()->m_World, m_Pos, normalize(Target->m_Pos - m_Pos), m_Freeze, m_Explosive, Target->Team());
|
||||
m_LastFire = Server()->Tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGun::Reset()
|
||||
{
|
||||
m_MarkedForDestroy = true;
|
||||
}
|
||||
|
||||
void CGun::Tick()
|
||||
{
|
||||
if(Server()->Tick() % int(Server()->TickSpeed() * 0.15f) == 0)
|
||||
|
@ -106,8 +39,105 @@ void CGun::Tick()
|
|||
}
|
||||
m_Pos += m_Core;
|
||||
}
|
||||
if(g_Config.m_SvPlasmaPerSec > 0 && m_LastFire + Server()->TickSpeed() / g_Config.m_SvPlasmaPerSec <= Server()->Tick())
|
||||
if(g_Config.m_SvPlasmaPerSec > 0)
|
||||
{
|
||||
Fire();
|
||||
}
|
||||
}
|
||||
|
||||
void CGun::Fire()
|
||||
{
|
||||
// Create a list of players who are in the range of the turret
|
||||
CCharacter *pPlayersInRange[MAX_CLIENTS];
|
||||
mem_zero(pPlayersInRange, sizeof(pPlayersInRange));
|
||||
|
||||
int NumPlayersInRange = GameServer()->m_World.FindEntities(m_Pos, g_Config.m_SvPlasmaRange,
|
||||
(CEntity **)pPlayersInRange, MAX_CLIENTS, CGameWorld::ENTTYPE_CHARACTER);
|
||||
|
||||
// The closest player (within range) in a team is selected as the target
|
||||
int TargetIdInTeam[MAX_CLIENTS];
|
||||
bool IsTarget[MAX_CLIENTS];
|
||||
int MinDistInTeam[MAX_CLIENTS];
|
||||
mem_zero(MinDistInTeam, sizeof(MinDistInTeam));
|
||||
mem_zero(IsTarget, sizeof(IsTarget));
|
||||
for(int &TargetId : TargetIdInTeam)
|
||||
{
|
||||
TargetId = -1;
|
||||
}
|
||||
|
||||
for(int i = 0; i < NumPlayersInRange; i++)
|
||||
{
|
||||
CCharacter *pTarget = pPlayersInRange[i];
|
||||
const int &TargetTeam = pTarget->Team();
|
||||
// Do not fire at super players
|
||||
if(TargetTeam == TEAM_SUPER)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// If the turret is disabled for the target's team, the turret will not fire
|
||||
if(m_Layer == LAYER_SWITCH && m_Number > 0 &&
|
||||
!GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[TargetTeam])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Turrets can only shoot at a speed of sv_plasma_per_sec
|
||||
const int &TargetClientId = pTarget->GetPlayer()->GetCID();
|
||||
const bool &TargetIsSolo = pTarget->Teams()->m_Core.GetSolo(TargetClientId);
|
||||
if((TargetIsSolo &&
|
||||
m_LastFireSolo[TargetClientId] + Server()->TickSpeed() / g_Config.m_SvPlasmaPerSec > Server()->Tick()) ||
|
||||
(!TargetIsSolo &&
|
||||
m_LastFireTeam[TargetTeam] + Server()->TickSpeed() / g_Config.m_SvPlasmaPerSec > Server()->Tick()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Turrets can shoot only at reachable, alive players
|
||||
int IsReachable = !GameServer()->Collision()->IntersectLine(m_Pos, pTarget->m_Pos, 0, 0);
|
||||
if(IsReachable && pTarget->IsAlive())
|
||||
{
|
||||
// Turrets fire on solo players regardless of the rest of the team
|
||||
if(TargetIsSolo)
|
||||
{
|
||||
IsTarget[TargetClientId] = true;
|
||||
m_LastFireSolo[TargetClientId] = Server()->Tick();
|
||||
}
|
||||
else
|
||||
{
|
||||
int Distance = distance(pTarget->m_Pos, m_Pos);
|
||||
if(MinDistInTeam[TargetTeam] == 0 || MinDistInTeam[TargetTeam] > Distance)
|
||||
{
|
||||
MinDistInTeam[TargetTeam] = Distance;
|
||||
TargetIdInTeam[TargetTeam] = TargetClientId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the closest player for each team as a target
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
if(TargetIdInTeam[i] != -1)
|
||||
{
|
||||
IsTarget[TargetIdInTeam[i]] = true;
|
||||
m_LastFireTeam[i] = Server()->Tick();
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
// Fire at each target
|
||||
if(IsTarget[i])
|
||||
{
|
||||
CCharacter *pTarget = GameServer()->GetPlayerChar(i);
|
||||
new CPlasma(&GameServer()->m_World, m_Pos, normalize(pTarget->m_Pos - m_Pos), m_Freeze, m_Explosive, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGun::Reset()
|
||||
{
|
||||
m_MarkedForDestroy = true;
|
||||
}
|
||||
|
||||
void CGun::Snap(int SnappingClient)
|
||||
|
@ -115,40 +145,49 @@ void CGun::Snap(int SnappingClient)
|
|||
if(NetworkClipped(SnappingClient))
|
||||
return;
|
||||
|
||||
CCharacter *Char = GameServer()->GetPlayerChar(SnappingClient);
|
||||
|
||||
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)
|
||||
Char = GameServer()->GetPlayerChar(GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID);
|
||||
|
||||
int SnappingClientVersion = SnappingClient != SERVER_DEMO_CLIENT ? GameServer()->GetClientVersion(SnappingClient) : CLIENT_VERSIONNR;
|
||||
int SnappingClientVersion = SnappingClient != SERVER_DEMO_CLIENT ?
|
||||
GameServer()->GetClientVersion(SnappingClient) :
|
||||
CLIENT_VERSIONNR;
|
||||
|
||||
CNetObj_EntityEx *pEntData = 0;
|
||||
if(SnappingClientVersion >= VERSION_DDNET_SWITCH)
|
||||
pEntData = static_cast<CNetObj_EntityEx *>(Server()->SnapNewItem(NETOBJTYPE_ENTITYEX, GetID(), sizeof(CNetObj_EntityEx)));
|
||||
|
||||
if(pEntData)
|
||||
{
|
||||
pEntData->m_SwitchNumber = m_Number;
|
||||
pEntData->m_Layer = m_Layer;
|
||||
pEntData = static_cast<CNetObj_EntityEx *>(Server()->SnapNewItem(NETOBJTYPE_ENTITYEX, GetID(),
|
||||
sizeof(CNetObj_EntityEx)));
|
||||
if(pEntData)
|
||||
{
|
||||
pEntData->m_SwitchNumber = m_Number;
|
||||
pEntData->m_Layer = m_Layer;
|
||||
|
||||
if(m_Explosive && !m_Freeze)
|
||||
pEntData->m_EntityClass = ENTITYCLASS_GUN_NORMAL;
|
||||
else if(m_Explosive && m_Freeze)
|
||||
pEntData->m_EntityClass = ENTITYCLASS_GUN_EXPLOSIVE;
|
||||
else if(!m_Explosive && m_Freeze)
|
||||
pEntData->m_EntityClass = ENTITYCLASS_GUN_FREEZE;
|
||||
else
|
||||
pEntData->m_EntityClass = ENTITYCLASS_GUN_UNFREEZE;
|
||||
if(m_Explosive && !m_Freeze)
|
||||
pEntData->m_EntityClass = ENTITYCLASS_GUN_NORMAL;
|
||||
else if(m_Explosive && m_Freeze)
|
||||
pEntData->m_EntityClass = ENTITYCLASS_GUN_EXPLOSIVE;
|
||||
else if(!m_Explosive && m_Freeze)
|
||||
pEntData->m_EntityClass = ENTITYCLASS_GUN_FREEZE;
|
||||
else
|
||||
pEntData->m_EntityClass = ENTITYCLASS_GUN_UNFREEZE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Emulate turned off blinking turret for old clients
|
||||
CCharacter *pChar = GameServer()->GetPlayerChar(SnappingClient);
|
||||
|
||||
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)
|
||||
pChar = GameServer()->GetPlayerChar(GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID);
|
||||
|
||||
int Tick = (Server()->Tick() % Server()->TickSpeed()) % 11;
|
||||
if(Char && Char->IsAlive() && (m_Layer == LAYER_SWITCH && m_Number > 0 && !GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[Char->Team()]) && (!Tick))
|
||||
if(pChar && m_Layer == LAYER_SWITCH && m_Number > 0 &&
|
||||
!GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[pChar->Team()] && (!Tick))
|
||||
return;
|
||||
}
|
||||
|
||||
CNetObj_Laser *pObj = static_cast<CNetObj_Laser *>(Server()->SnapNewItem(NETOBJTYPE_LASER, GetID(), sizeof(CNetObj_Laser)));
|
||||
CNetObj_Laser *pObj = static_cast<CNetObj_Laser *>(Server()->SnapNewItem(
|
||||
NETOBJTYPE_LASER, GetID(), sizeof(CNetObj_Laser)));
|
||||
|
||||
if(!pObj)
|
||||
return;
|
||||
|
|
|
@ -6,18 +6,30 @@
|
|||
#include <game/gamecore.h>
|
||||
#include <game/server/entity.h>
|
||||
|
||||
class CCharacter;
|
||||
|
||||
/**
|
||||
* Turrets (also referred to as Gun) fire plasma bullets at the nearest player
|
||||
*
|
||||
* A turret fires plasma bullets with a certain firing rate (sv_plasma_per_sec) at the closest player of a team for whom
|
||||
* the following criteria are met:
|
||||
* - The player is within the turret range (sv_plasma_range)
|
||||
* - The player is not a super player
|
||||
* - The turret is activated
|
||||
* - The initial trajectory of the plasma bullet to be generated would not be stopped by any solid block
|
||||
* With the exception of solo players, for whom plasma bullets will always be fired, regardless of the rest of the team,
|
||||
* if the above criteria are met. Solo players do not affect the generation of plasma bullets for the rest of the team.
|
||||
* The shooting rate of sv_plasma_per_sec is independent for each team and solo player and starts with the first tick
|
||||
* when a target player is selected.
|
||||
*/
|
||||
class CGun : public CEntity
|
||||
{
|
||||
int m_EvalTick;
|
||||
|
||||
vec2 m_Core;
|
||||
bool m_Freeze;
|
||||
bool m_Explosive;
|
||||
int m_EvalTick;
|
||||
int m_LastFireTeam[MAX_CLIENTS];
|
||||
int m_LastFireSolo[MAX_CLIENTS];
|
||||
|
||||
void Fire();
|
||||
int m_LastFire;
|
||||
|
||||
public:
|
||||
CGun(CGameWorld *pGameWorld, vec2 Pos, bool Freeze, bool Explosive, int Layer = 0, int Number = 0);
|
||||
|
|
|
@ -25,7 +25,7 @@ CLaser::CLaser(CGameWorld *pGameWorld, vec2 Pos, vec2 Direction, float StartEner
|
|||
m_IsBlueTeleport = false;
|
||||
m_TuneZone = GameServer()->Collision()->IsTune(GameServer()->Collision()->GetMapIndex(m_Pos));
|
||||
CCharacter *pOwnerChar = GameServer()->GetPlayerChar(m_Owner);
|
||||
m_TeamMask = pOwnerChar ? pOwnerChar->Teams()->TeamMask(pOwnerChar->Team(), -1, m_Owner) : 0;
|
||||
m_TeamMask = pOwnerChar ? pOwnerChar->TeamMask() : 0;
|
||||
m_BelongsToPracticeTeam = pOwnerChar && pOwnerChar->Teams()->IsPractice(pOwnerChar->Team());
|
||||
|
||||
GameWorld()->InsertEntity(this);
|
||||
|
@ -277,7 +277,7 @@ void CLaser::Snap(int SnappingClient)
|
|||
pOwnerChar = GameServer()->GetPlayerChar(m_Owner);
|
||||
|
||||
if(pOwnerChar && pOwnerChar->IsAlive())
|
||||
TeamMask = pOwnerChar->Teams()->TeamMask(pOwnerChar->Team(), -1, m_Owner);
|
||||
TeamMask = pOwnerChar->TeamMask();
|
||||
|
||||
if(SnappingClient != SERVER_DEMO_CLIENT && !CmaskIsSet(TeamMask, SnappingClient))
|
||||
return;
|
||||
|
|
|
@ -13,35 +13,40 @@
|
|||
const float PLASMA_ACCEL = 1.1f;
|
||||
|
||||
CPlasma::CPlasma(CGameWorld *pGameWorld, vec2 Pos, vec2 Dir, bool Freeze,
|
||||
bool Explosive, int ResponsibleTeam) :
|
||||
bool Explosive, int ForClientID) :
|
||||
CEntity(pGameWorld, CGameWorld::ENTTYPE_LASER)
|
||||
{
|
||||
m_Pos = Pos;
|
||||
m_Core = Dir;
|
||||
m_Freeze = Freeze;
|
||||
m_Explosive = Explosive;
|
||||
m_ForClientID = ForClientID;
|
||||
m_EvalTick = Server()->Tick();
|
||||
m_LifeTime = Server()->TickSpeed() * 1.5f;
|
||||
m_ResponsibleTeam = ResponsibleTeam;
|
||||
|
||||
GameWorld()->InsertEntity(this);
|
||||
}
|
||||
|
||||
bool CPlasma::HitCharacter()
|
||||
void CPlasma::Tick()
|
||||
{
|
||||
vec2 To2;
|
||||
CCharacter *Hit = GameServer()->m_World.IntersectCharacter(m_Pos,
|
||||
m_Pos + m_Core, 0.0f, To2);
|
||||
if(!Hit)
|
||||
return false;
|
||||
|
||||
if(Hit->Team() != m_ResponsibleTeam)
|
||||
return false;
|
||||
m_Freeze ? Hit->Freeze() : Hit->UnFreeze();
|
||||
if(m_Explosive)
|
||||
GameServer()->CreateExplosion(m_Pos, -1, WEAPON_GRENADE, true,
|
||||
m_ResponsibleTeam, Hit->Teams()->TeamMask(m_ResponsibleTeam));
|
||||
m_MarkedForDestroy = true;
|
||||
return true;
|
||||
// A plasma bullet has only a limited lifetime
|
||||
if(m_LifeTime == 0)
|
||||
{
|
||||
Reset();
|
||||
return;
|
||||
}
|
||||
CCharacter *pTarget = GameServer()->GetPlayerChar(m_ForClientID);
|
||||
// Without a target, a plasma bullet has no reason to live
|
||||
if(!pTarget)
|
||||
{
|
||||
Reset();
|
||||
return;
|
||||
}
|
||||
m_LifeTime--;
|
||||
Move();
|
||||
HitCharacter(pTarget);
|
||||
// Plasma bullets may explode twice if they would hit both a player and an obstacle in the next move step
|
||||
HitObstacle(pTarget);
|
||||
}
|
||||
|
||||
void CPlasma::Move()
|
||||
|
@ -50,62 +55,72 @@ void CPlasma::Move()
|
|||
m_Core *= PLASMA_ACCEL;
|
||||
}
|
||||
|
||||
bool CPlasma::HitCharacter(CCharacter *pTarget)
|
||||
{
|
||||
vec2 IntersectPos;
|
||||
CCharacter *HitPlayer = GameServer()->m_World.IntersectCharacter(
|
||||
m_Pos, m_Pos + m_Core, 0.0f, IntersectPos, 0, m_ForClientID);
|
||||
if(!HitPlayer)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Super player should not be able to stop the plasma bullets
|
||||
if(HitPlayer->Team() == TEAM_SUPER)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Freeze ? HitPlayer->Freeze() : HitPlayer->UnFreeze();
|
||||
if(m_Explosive)
|
||||
{
|
||||
// Plasma Turrets are very precise weapons only one tee gets speed from it,
|
||||
// other tees near the explosion remain unaffected
|
||||
GameServer()->CreateExplosion(
|
||||
m_Pos, m_ForClientID, WEAPON_GRENADE, true, pTarget->Team(), pTarget->TeamMask());
|
||||
}
|
||||
Reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPlasma::HitObstacle(CCharacter *pTarget)
|
||||
{
|
||||
// Check if the plasma bullet is stopped by a solid block or a laser stopper
|
||||
int HasIntersection = GameServer()->Collision()->IntersectNoLaser(m_Pos, m_Pos + m_Core, 0, 0);
|
||||
if(HasIntersection)
|
||||
{
|
||||
if(m_Explosive)
|
||||
{
|
||||
// Even in the case of an explosion due to a collision with obstacles, only one player is affected
|
||||
GameServer()->CreateExplosion(
|
||||
m_Pos, m_ForClientID, WEAPON_GRENADE, true, pTarget->Team(), pTarget->TeamMask());
|
||||
}
|
||||
Reset();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CPlasma::Reset()
|
||||
{
|
||||
m_MarkedForDestroy = true;
|
||||
}
|
||||
|
||||
void CPlasma::Tick()
|
||||
{
|
||||
if(m_LifeTime == 0)
|
||||
{
|
||||
Reset();
|
||||
return;
|
||||
}
|
||||
m_LifeTime--;
|
||||
Move();
|
||||
HitCharacter();
|
||||
|
||||
int Res = 0;
|
||||
Res = GameServer()->Collision()->IntersectNoLaser(m_Pos, m_Pos + m_Core, 0,
|
||||
0);
|
||||
if(Res)
|
||||
{
|
||||
if(m_Explosive)
|
||||
GameServer()->CreateExplosion(
|
||||
m_Pos,
|
||||
-1,
|
||||
WEAPON_GRENADE,
|
||||
true,
|
||||
m_ResponsibleTeam,
|
||||
((CGameControllerDDRace *)GameServer()->m_pController)->m_Teams.TeamMask(m_ResponsibleTeam));
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void CPlasma::Snap(int SnappingClient)
|
||||
{
|
||||
// Only players who can see the targeted player can see the plasma bullet
|
||||
CCharacter *pTarget = GameServer()->GetPlayerChar(m_ForClientID);
|
||||
if(!pTarget->CanSnapCharacter(SnappingClient))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Only players with the plasma bullet in their field of view or who want to see everything will receive the snap
|
||||
if(NetworkClipped(SnappingClient))
|
||||
return;
|
||||
CCharacter *SnapChar = GameServer()->GetPlayerChar(SnappingClient);
|
||||
CPlayer *SnapPlayer = SnappingClient != SERVER_DEMO_CLIENT ? GameServer()->m_apPlayers[SnappingClient] : 0;
|
||||
int Tick = (Server()->Tick() % Server()->TickSpeed()) % 11;
|
||||
|
||||
if(SnapChar && SnapChar->IsAlive() && (m_Layer == LAYER_SWITCH && m_Number > 0 && !GameServer()->Collision()->m_pSwitchers[m_Number].m_Status[SnapChar->Team()]) && (!Tick))
|
||||
return;
|
||||
|
||||
if(SnapPlayer && (SnapPlayer->GetTeam() == TEAM_SPECTATORS || SnapPlayer->IsPaused()) && SnapPlayer->m_SpectatorID != SPEC_FREEVIEW && GameServer()->GetPlayerChar(SnapPlayer->m_SpectatorID) && GameServer()->GetPlayerChar(SnapPlayer->m_SpectatorID)->Team() != m_ResponsibleTeam && SnapPlayer->m_ShowOthers != SHOW_OTHERS_ON)
|
||||
return;
|
||||
|
||||
if(SnapPlayer && SnapPlayer->GetTeam() != TEAM_SPECTATORS && !SnapPlayer->IsPaused() && SnapChar && SnapChar->Team() != m_ResponsibleTeam && SnapPlayer->m_ShowOthers != SHOW_OTHERS_ON)
|
||||
return;
|
||||
|
||||
if(SnapPlayer && (SnapPlayer->GetTeam() == TEAM_SPECTATORS || SnapPlayer->IsPaused()) && SnapPlayer->m_SpectatorID == SPEC_FREEVIEW && SnapChar && SnapChar->Team() != m_ResponsibleTeam && SnapPlayer->m_SpecTeam)
|
||||
return;
|
||||
|
||||
CNetObj_Laser *pObj = static_cast<CNetObj_Laser *>(Server()->SnapNewItem(
|
||||
NETOBJTYPE_LASER, GetID(), sizeof(CNetObj_Laser)));
|
||||
|
||||
if(!pObj)
|
||||
return;
|
||||
|
||||
|
|
|
@ -4,24 +4,38 @@
|
|||
|
||||
#include <game/server/entity.h>
|
||||
|
||||
class CGun;
|
||||
|
||||
/**
|
||||
* Plasma Bullets are projectiles fired from turrets at a specific target
|
||||
*
|
||||
* When hitting a tee, plasma bullets can either freeze or unfreeze the player
|
||||
* Also, plasma projectiles can explode on impact. However, the player affected by the explosion is not necessarily the
|
||||
* one the plasma collided with, but if the affected player is not a solo player, then the team-mate with the lowest
|
||||
* ClientId within the explosion range. Furthermore, the affected player does not feel the explosion at the point of
|
||||
* impact but at the last position of the plasma bullet. The same applies if a plasma bullet explodes due to a collision
|
||||
* with a laser stopper or a solid block
|
||||
* Plasma bullets move every tick in the assigned direction and then accelerate by the factor PLASMA_ACCEL
|
||||
* Plasma bullets can explode twice if they would hit both a player and an obstacle in the next movement step
|
||||
* Plasma bullets will stop existing as soon as:
|
||||
* - The player they were created for do no longer exist
|
||||
* - They have had a collision with a player, a solid block or a laser stopper
|
||||
* - Their life time of 1.5 seconds has expired
|
||||
*/
|
||||
class CPlasma : public CEntity
|
||||
{
|
||||
vec2 m_Core;
|
||||
int m_Freeze;
|
||||
bool m_Explosive;
|
||||
int m_ForClientID;
|
||||
int m_EvalTick;
|
||||
int m_LifeTime;
|
||||
|
||||
int m_ResponsibleTeam;
|
||||
int m_Freeze;
|
||||
|
||||
bool m_Explosive;
|
||||
bool HitCharacter();
|
||||
void Move();
|
||||
bool HitCharacter(CCharacter *pTarget);
|
||||
bool HitObstacle(CCharacter *pTarget);
|
||||
|
||||
public:
|
||||
CPlasma(CGameWorld *pGameWorld, vec2 Pos, vec2 Dir, bool Freeze,
|
||||
bool Explosive, int ResponsibleTeam);
|
||||
bool Explosive, int ForClientId);
|
||||
|
||||
void Reset() override;
|
||||
void Tick() override;
|
||||
|
|
|
@ -143,7 +143,7 @@ void CProjectile::Tick()
|
|||
}
|
||||
if(pOwnerChar && pOwnerChar->IsAlive())
|
||||
{
|
||||
TeamMask = pOwnerChar->Teams()->TeamMask(pOwnerChar->Team(), -1, m_Owner);
|
||||
TeamMask = pOwnerChar->TeamMask();
|
||||
}
|
||||
else if(m_Owner >= 0 && (m_Type != WEAPON_GRENADE || g_Config.m_SvDestroyBulletsOnDeath || m_BelongsToPracticeTeam))
|
||||
{
|
||||
|
@ -257,7 +257,7 @@ void CProjectile::Tick()
|
|||
TeamMask = -1LL;
|
||||
if(pOwnerChar && pOwnerChar->IsAlive())
|
||||
{
|
||||
TeamMask = pOwnerChar->Teams()->TeamMask(pOwnerChar->Team(), -1, m_Owner);
|
||||
TeamMask = pOwnerChar->TeamMask();
|
||||
}
|
||||
|
||||
GameServer()->CreateExplosion(ColPos, m_Owner, m_Type, m_Owner == -1, (!pOwnerChar ? -1 : pOwnerChar->Team()),
|
||||
|
@ -333,7 +333,7 @@ void CProjectile::Snap(int SnappingClient)
|
|||
pOwnerChar = GameServer()->GetPlayerChar(m_Owner);
|
||||
|
||||
if(pOwnerChar && pOwnerChar->IsAlive())
|
||||
TeamMask = pOwnerChar->Teams()->TeamMask(pOwnerChar->Team(), -1, m_Owner);
|
||||
TeamMask = pOwnerChar->TeamMask();
|
||||
|
||||
if(SnappingClient != SERVER_DEMO_CLIENT && m_Owner != -1 && !CmaskIsSet(TeamMask, SnappingClient))
|
||||
return;
|
||||
|
|
|
@ -39,6 +39,11 @@ bool CEntity::NetworkClipped(int SnappingClient, vec2 CheckPos) const
|
|||
return ::NetworkClipped(m_pGameWorld->GameServer(), SnappingClient, CheckPos);
|
||||
}
|
||||
|
||||
bool CEntity::NetworkClippedLine(int SnappingClient, vec2 StartPos, vec2 EndPos) const
|
||||
{
|
||||
return ::NetworkClippedLine(m_pGameWorld->GameServer(), SnappingClient, StartPos, EndPos);
|
||||
}
|
||||
|
||||
bool CEntity::GameLayerClipped(vec2 CheckPos)
|
||||
{
|
||||
return round_to_int(CheckPos.x) / 32 < -200 || round_to_int(CheckPos.x) / 32 > GameServer()->Collision()->GetWidth() + 200 ||
|
||||
|
@ -93,3 +98,25 @@ bool NetworkClipped(const CGameContext *pGameServer, int SnappingClient, vec2 Ch
|
|||
float dy = pGameServer->m_apPlayers[SnappingClient]->m_ViewPos.y - CheckPos.y;
|
||||
return absolute(dy) > pGameServer->m_apPlayers[SnappingClient]->m_ShowDistance.y;
|
||||
}
|
||||
|
||||
bool NetworkClippedLine(const CGameContext *pGameServer, int SnappingClient, vec2 StartPos, vec2 EndPos)
|
||||
{
|
||||
if(SnappingClient == SERVER_DEMO_CLIENT || pGameServer->m_apPlayers[SnappingClient]->m_ShowAll)
|
||||
return false;
|
||||
|
||||
vec2 &ViewPos = pGameServer->m_apPlayers[SnappingClient]->m_ViewPos;
|
||||
vec2 &ShowDistance = pGameServer->m_apPlayers[SnappingClient]->m_ShowDistance;
|
||||
|
||||
vec2 DistanceToLine, ClosestPoint;
|
||||
if(closest_point_on_line(StartPos, EndPos, ViewPos, ClosestPoint))
|
||||
{
|
||||
DistanceToLine = ViewPos - ClosestPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No line section was passed but two equal points
|
||||
DistanceToLine = ViewPos - StartPos;
|
||||
}
|
||||
float ClippDistance = maximum(ShowDistance.x, ShowDistance.y);
|
||||
return (absolute(DistanceToLine.x) > ClippDistance || absolute(DistanceToLine.y) > ClippDistance);
|
||||
}
|
|
@ -139,6 +139,7 @@ public: // TODO: Maybe make protected
|
|||
*/
|
||||
bool NetworkClipped(int SnappingClient) const;
|
||||
bool NetworkClipped(int SnappingClient, vec2 CheckPos) const;
|
||||
bool NetworkClippedLine(int SnappingClient, vec2 StartPos, vec2 EndPos) const;
|
||||
|
||||
bool GameLayerClipped(vec2 CheckPos);
|
||||
|
||||
|
@ -152,5 +153,6 @@ public: // TODO: Maybe make protected
|
|||
};
|
||||
|
||||
bool NetworkClipped(const CGameContext *pGameServer, int SnappingClient, vec2 CheckPos);
|
||||
bool NetworkClippedLine(const CGameContext *pGameServer, int SnappingClient, vec2 StartPos, vec2 EndPos);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -290,7 +290,7 @@ void CGameContext::CreateExplosion(vec2 Pos, int Owner, int Weapon, bool NoDamag
|
|||
if(!(int)Dmg)
|
||||
continue;
|
||||
|
||||
if((GetPlayerChar(Owner) ? !(GetPlayerChar(Owner)->m_Hit & CCharacter::DISABLE_HIT_GRENADE) : g_Config.m_SvHit || NoDamage) || Owner == apEnts[i]->GetPlayer()->GetCID())
|
||||
if((GetPlayerChar(Owner) ? !(GetPlayerChar(Owner)->m_Hit & CCharacter::DISABLE_HIT_GRENADE) : g_Config.m_SvHit) || NoDamage || Owner == apEnts[i]->GetPlayer()->GetCID())
|
||||
{
|
||||
if(Owner != -1 && apEnts[i]->IsAlive() && !apEnts[i]->CanCollide(Owner))
|
||||
continue;
|
||||
|
@ -298,8 +298,8 @@ void CGameContext::CreateExplosion(vec2 Pos, int Owner, int Weapon, bool NoDamag
|
|||
continue;
|
||||
|
||||
// Explode at most once per team
|
||||
int PlayerTeam = ((CGameControllerDDRace *)m_pController)->m_Teams.m_Core.Team(apEnts[i]->GetPlayer()->GetCID());
|
||||
if(GetPlayerChar(Owner) ? GetPlayerChar(Owner)->m_Hit & CCharacter::DISABLE_HIT_GRENADE : !g_Config.m_SvHit || NoDamage)
|
||||
int PlayerTeam = apEnts[i]->Team();
|
||||
if((GetPlayerChar(Owner) ? GetPlayerChar(Owner)->m_Hit & CCharacter::DISABLE_HIT_GRENADE : !g_Config.m_SvHit) || NoDamage)
|
||||
{
|
||||
if(!CmaskIsSet(TeamMask, PlayerTeam))
|
||||
continue;
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include "entities/dragger.h"
|
||||
#include "entities/gun.h"
|
||||
#include "entities/light.h"
|
||||
#include "entities/plasma.h"
|
||||
#include "entities/projectile.h"
|
||||
#include <game/layers.h>
|
||||
|
||||
|
@ -366,11 +365,11 @@ bool IGameController::OnEntity(int Index, vec2 Pos, int Layer, int Flags, int Nu
|
|||
}
|
||||
else if(Index >= ENTITY_DRAGGER_WEAK && Index <= ENTITY_DRAGGER_STRONG)
|
||||
{
|
||||
CDraggerTeam(&GameServer()->m_World, Pos, Index - ENTITY_DRAGGER_WEAK + 1, false, Layer, Number);
|
||||
new CDragger(&GameServer()->m_World, Pos, Index - ENTITY_DRAGGER_WEAK + 1, false, Layer, Number);
|
||||
}
|
||||
else if(Index >= ENTITY_DRAGGER_WEAK_NW && Index <= ENTITY_DRAGGER_STRONG_NW)
|
||||
{
|
||||
CDraggerTeam(&GameServer()->m_World, Pos, Index - ENTITY_DRAGGER_WEAK_NW + 1, true, Layer, Number);
|
||||
new CDragger(&GameServer()->m_World, Pos, Index - ENTITY_DRAGGER_WEAK_NW + 1, true, Layer, Number);
|
||||
}
|
||||
else if(Index == ENTITY_PLASMAE)
|
||||
{
|
||||
|
|
|
@ -416,7 +416,7 @@ void CGameWorld::ReleaseHooked(int ClientID)
|
|||
CCharacterCore *Core = pChr->Core();
|
||||
if(Core->m_HookedPlayer == ClientID && !pChr->m_Super)
|
||||
{
|
||||
Core->m_HookedPlayer = -1;
|
||||
Core->SetHookedPlayer(-1);
|
||||
Core->m_TriggeredEvents |= COREEVENT_HOOK_RETRACT;
|
||||
Core->m_HookState = HOOK_RETRACTED;
|
||||
}
|
||||
|
|
|
@ -186,12 +186,12 @@ void CSaveTee::Load(CCharacter *pChr, int Team, bool IsSwap)
|
|||
pChr->m_Core.m_HookState = m_HookState;
|
||||
if(m_HookedPlayer != -1 && pChr->Teams()->m_Core.Team(m_HookedPlayer) != Team)
|
||||
{
|
||||
pChr->m_Core.m_HookedPlayer = -1;
|
||||
pChr->m_Core.SetHookedPlayer(-1);
|
||||
pChr->m_Core.m_HookState = HOOK_FLYING;
|
||||
}
|
||||
else
|
||||
{
|
||||
pChr->m_Core.m_HookedPlayer = m_HookedPlayer;
|
||||
pChr->m_Core.SetHookedPlayer(m_HookedPlayer);
|
||||
}
|
||||
pChr->m_Core.m_NewHook = m_NewHook;
|
||||
pChr->m_SavedInput.m_Direction = m_InputDirection;
|
||||
|
|
Loading…
Reference in a new issue