Skip to content

Commit

Permalink
Implement doors
Browse files Browse the repository at this point in the history
  • Loading branch information
Kaffeine committed Aug 23, 2024
1 parent 084391f commit d14f116
Show file tree
Hide file tree
Showing 11 changed files with 311 additions and 3 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,8 @@ set_glob(GAME_SERVER GLOB_RECURSE src/game/server
infclass/entities/hero-flag.h
infclass/entities/ic-pickup.cpp
infclass/entities/ic-pickup.h
infclass/entities/ic_door.cpp
infclass/entities/ic_door.h
infclass/entities/infc-laser.cpp
infclass/entities/infc-laser.h
infclass/entities/infc-placed-object.cpp
Expand Down
114 changes: 112 additions & 2 deletions src/game/collision.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ void CCollision::Init(class CLayers *pLayers)
m_Height = m_pLayers->GameLayer()->m_Height;
m_pTiles = static_cast<CTile *>(m_pLayers->Map()->GetData(m_pLayers->GameLayer()->m_Data));

InitPhysicalLayer();
InitDoorsLayer();
InitTeleports();

if(m_pLayers->SpeedupLayer())
Expand All @@ -73,6 +75,41 @@ void CCollision::Init(class CLayers *pLayers)
}
}

constexpr EZonePhysics GetPhysicalTileByTile(int Tile)
{
switch(Tile)
{
case TILE_SOLID:
return EZonePhysics::Solid;
case TILE_NOHOOK:
return EZonePhysics::NoHook;
default:
break;
}

return EZonePhysics::Null;
}

void CCollision::InitPhysicalLayer()
{
mv_Physics.resize(m_Width * m_Height);
for(int Y = 0; Y < m_Height; ++Y)
{
for(int X = 0; X < m_Width; ++X)
{
int Index = Y * m_Width + X;
int Tile = m_pTiles[Index].m_Index;
mv_Physics[Index] = GetPhysicalTileByTile(Tile);
}
}
}

void CCollision::InitDoorsLayer()
{
mv_Doors.clear();
mv_Doors.resize(m_Width * m_Height);
}

void CCollision::InitTeleports()
{
if(!m_pLayers->TeleLayer())
Expand Down Expand Up @@ -218,6 +255,76 @@ int CCollision::GetMoveRestrictions(CALLBACK_SWITCHACTIVE pfnSwitchActive, void
return Restrictions;
}

void CCollision::SetDoorCollisionAt(int Index, bool HasDoor)
{
if (HasDoor)
{
mv_Doors[Index]++;
}
else
{
mv_Doors[Index]--;
}
}

void CCollision::SetDoorCollisionAt(vec2 Pos, bool HasDoor)
{
SetDoorCollisionAt(GetPureMapIndex(Pos), HasDoor);
}

int CCollision::GetDoorCollisionAt(vec2 Pos) const
{
int Nx = clamp(static_cast<int>(Pos.x) / 32, 0, m_Width - 1);
int Ny = clamp(static_cast<int>(Pos.y) / 32, 0, m_Height - 1);
int Index = Ny * m_Width + Nx;

return mv_Doors.at(Index);
}

int CCollision::IntersectLineWithDoors(vec2 From, vec2 To, vec2 *pOutCollision, vec2 *pOutBeforeCollision) const
{
vec2 Pos1Pos0 = To - From;
float Distance = length(Pos1Pos0);
int End(Distance + 1);
vec2 Last = From;

for(int i = 0; i < End; i++)
{
float a = i / Distance;
vec2 Pos = From + Pos1Pos0 * a;
if(GetDoorCollisionAt(Pos))
{
if(pOutCollision)
*pOutCollision = Pos;
if(pOutBeforeCollision)
*pOutBeforeCollision = Last;
return GetDoorCollisionAt(Pos);
}
Last = Pos;
}
if(pOutCollision)
*pOutCollision = To;
if(pOutBeforeCollision)
*pOutBeforeCollision = To;
return 0;
}

EZonePhysics CCollision::GetPhysicsTile(int x, int y) const
{
int Nx = clamp(x / 32, 0, m_Width - 1);
int Ny = clamp(y / 32, 0, m_Height - 1);
int Index = Ny * m_Width + Nx;

EZonePhysics Value = mv_Physics.at(Index);
if (Value == EZonePhysics::Null)
{
if(mv_Doors.at(Index))
return EZonePhysics::NoHook;
}

return Value;
}

int CCollision::GetTile(int x, int y) const
{
if(!m_pTiles)
Expand All @@ -230,6 +337,10 @@ int CCollision::GetTile(int x, int y) const
int Index = m_pTiles[pos].m_Index;
if(Index >= TILE_SOLID && Index <= TILE_NOLASER)
return Index;

if(mv_Doors.at(pos))
return TILE_NOHOOK;

return 0;
}

Expand Down Expand Up @@ -434,8 +545,7 @@ void CCollision::Dest()

bool CCollision::IsSolid(int x, int y) const
{
int index = GetTile(x, y);
return index == TILE_SOLID || index == TILE_NOHOOK;
return GetPhysicsTile(x, y) != EZonePhysics::Null;
}

int CCollision::IsSpeedup(int Index) const
Expand Down
16 changes: 15 additions & 1 deletion src/game/collision.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ enum
CANTMOVE_DOWN = 1 << 3,
};

enum class EZonePhysics : int8_t;

vec2 ClampVel(int MoveRestriction, vec2 Vel);

typedef bool (*CALLBACK_SWITCHACTIVE)(int Number, void *pUser);
Expand All @@ -29,6 +31,8 @@ struct ZoneData

class CCollision
{
std::vector<EZonePhysics> mv_Physics;
std::vector<int8_t> mv_Doors;
class CTile *m_pTiles;
int m_Width;
int m_Height;
Expand All @@ -39,7 +43,6 @@ class CCollision
array< array<int> > m_Zones;

bool IsSolid(int x, int y) const;
int GetTile(int x, int y) const;

public:
enum
Expand All @@ -52,6 +55,8 @@ class CCollision
CCollision();
~CCollision();
void Init(class CLayers *pLayers);
void InitPhysicalLayer();
void InitDoorsLayer();
void InitTeleports();

bool CheckPoint(float x, float y) const { return IsSolid(round_to_int(x), round(y)); }
Expand All @@ -74,6 +79,15 @@ class CCollision
return GetMoveRestrictions(0, 0, Pos, Distance);
}

EZonePhysics GetPhysicsTile(int x, int y) const;
int GetTile(int x, int y) const;
int GetFTile(int x, int y) const;

void SetDoorCollisionAt(int Index, bool HasDoor);
void SetDoorCollisionAt(vec2 Pos, bool HasDoor);
int GetDoorCollisionAt(vec2 Pos) const;
int IntersectLineWithDoors(vec2 From, vec2 To, vec2 *pOutCollision = nullptr, vec2 *pOutBeforeCollision = nullptr) const;

void SetTime(double Time) { m_Time = Time; }

//This function return an Handle to access all zone layers with the name "pName"
Expand Down
9 changes: 9 additions & 0 deletions src/game/mapitems.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#ifndef GAME_MAPITEMS_H
#define GAME_MAPITEMS_H

#include <cstdint>

// layer types
enum
{
Expand Down Expand Up @@ -221,6 +223,13 @@ enum
ZONE_BONUS_BONUS=1,
};

enum class EZonePhysics : int8_t
{
Null,
Solid = TILE_SOLID,
NoHook = TILE_NOHOOK,
};

enum class EZoneTele
{
Null,
Expand Down
2 changes: 2 additions & 0 deletions src/game/server/gameworld.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class CGameWorld
ENTTYPE_LASER_TELEPORT,
ENTTYPE_TURRET,
ENTTYPE_PLASMA,

ENTTYPE_DOOR,

NUM_ENTTYPES
};
Expand Down
128 changes: 128 additions & 0 deletions src/game/server/infclass/entities/ic_door.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#include "ic_door.h"

#include <engine/shared/config.h>
#include <game/server/gamecontext.h>

CDoor::CDoor(CGameContext *pGameContext, vec2 Pos, vec2 PosTo) :
CPlacedObject(pGameContext, CGameWorld::ENTTYPE_DOOR),
m_Open{true}
{
m_Pos = Pos;
m_Pos2 = PosTo;

m_InfClassObjectFlags = INFCLASS_OBJECT_FLAG_HAS_SECOND_POSITION;

SetOpen(false);
GameWorld()->InsertEntity(this);
}

void CDoor::Destroy()
{
SetOpen(true);

CPlacedObject::Destroy();
}

void CDoor::SetCollisions(bool Set)
{
const vec2 DoorVector = m_Pos2 - m_Pos;
const float Distance = length(DoorVector);

int PrevIndex = -1;
auto SetOncePerTile = [&](vec2 Pos)
{
int Index = GameServer()->Collision()->GetPureMapIndex(Pos);
if (Index == PrevIndex)
return;

PrevIndex = Index;
GameServer()->Collision()->SetDoorCollisionAt(Index, Set);
};

SetOncePerTile(m_Pos);
if(Distance > TileSizeF)
{
float Step = TileSize / 2;
vec2 NormalizedDoor = DoorVector / Distance;
float ProcessedDistance = Step;
while(ProcessedDistance < Distance)
{
SetOncePerTile(m_Pos + NormalizedDoor * ProcessedDistance);
ProcessedDistance += Step;
}
}
SetOncePerTile(m_Pos2);
}

void CDoor::Reset()
{
MarkForDestroy();
}

void CDoor::Snap(int SnappingClientId)
{
const std::optional<CViewParams> ViewParams = GetViewParams(GameServer(), SnappingClientId);

int SnappingClientVersion = GameServer()->GetClientVersion(SnappingClientId);

vec2 To = m_Pos;
vec2 From = IsOpen() ? m_Pos : m_Pos2;
int StartTick = 0;

if(SnappingClientVersion < VERSION_DDNET_ENTITY_NETOBJS)
{
StartTick = Server()->Tick();
}

const bool ForcedShowOpen = Config()->m_SvShowOpenDoors && IsOpen();
if(ForcedShowOpen)
{
From = m_Pos2;
}
int MaxY = Collision()->GetHeight() * TileSize;
if((From != To) && ViewParams.has_value())
{
if(m_Pos.x == m_Pos2.x)
{
const bool AutoextendTop = ((m_Pos.y < 32) || (m_Pos2.y < 32));
const bool AutoextendBottom = ((m_Pos.y >= MaxY) || (m_Pos2.y >= MaxY));

if(AutoextendTop)
{
const auto TopY = ViewParams->ViewPos.y - ViewParams->ShowDistance.y - 1000;
if(To.y < 32)
{
To.y = TopY;
}
else
{
From.y = TopY;
}
}
if(AutoextendBottom)
{
const auto BottomY = ViewParams->ViewPos.y + ViewParams->ShowDistance.y + 1000;
if(To.y >= MaxY)
{
To.y = BottomY;
}
else
{
From.y = BottomY;
}
}
}
}

GameServer()->SnapLaserObject(CSnapContext(SnappingClientVersion), GetId(), To, From, StartTick, -1, ForcedShowOpen ? LASERTYPE_FREEZE : LASERTYPE_DOOR);
// TODO: CGameContext::SnapSwitchers()
}

void CDoor::SetOpen(bool Open)
{
if (m_Open == Open)
return;

m_Open = Open;
SetCollisions(!Open);
}
27 changes: 27 additions & 0 deletions src/game/server/infclass/entities/ic_door.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* (c) Shereef Marzouk. See "licence DDRace.txt" and the readme.txt in the root of the distribution for more information. */
#ifndef GAME_SERVER_ENTITIES_DOOR_H
#define GAME_SERVER_ENTITIES_DOOR_H

#include <game/server/infclass/entities/infc-placed-object.h>

class CGameWorld;

class CDoor : public CPlacedObject
{
public:
CDoor(CGameContext *pGameContext, vec2 Pos, vec2 PosTo);

void Destroy() override;

void Reset() override;
void Snap(int SnappingClientId) override;

bool IsOpen() const { return m_Open; }
void SetOpen(bool Open);

protected:
void SetCollisions(bool Set);
bool m_Open = false;
};

#endif // GAME_SERVER_ENTITIES_DOOR_H
Loading

0 comments on commit d14f116

Please sign in to comment.