ddnet/src/game/collision.cpp
Chairn a69dc599a9 Huge variable naming format
Fix pointer and pointer array variable naming

Huge renaming to match our rules

Used regex: (?!(return|delete)\b)\b\w+ (m_|ms_|g_|gs_|s_)[^a]\w+\[
            (?!(return|delete)\b)\b\w+ (?!(m_|ms_|g_|gs_|s_))[^a]\w+\[

Further format static variables

Format almost all pointer names accordingly

Used regex: (?!(return)\b)\b\w+
\*(?!(m_p|p|s_p|m_ap|s_ap|g_p|g_ap|ap|gs_ap|ms_ap|gs_p|ms_p))\w+\b[^:\(p]

clang-format

Fix CI fail

Fix misnamed non pointer as pointer and non array as array

Used regex: (?!(return|delete)\b)\b\w+ (m_|ms_|g_|gs_|s_)p\w+\b
            (?!return\b)\b\w+ (ms_|m_|g_|gs_|s_)a\w+\b[^\[]

clang-format

Revert to SCREAMING_SNAKE_CASE and reinstate dead code
2022-07-08 18:01:29 +02:00

1278 lines
32 KiB
C++

/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <base/math.h>
#include <base/system.h>
#include <base/vmath.h>
#include <antibot/antibot_data.h>
#include <cmath>
#include <engine/map.h>
#include <game/collision.h>
#include <game/layers.h>
#include <game/mapitems.h>
#include <engine/shared/config.h>
vec2 ClampVel(int MoveRestriction, vec2 Vel)
{
if(Vel.x > 0 && (MoveRestriction & CANTMOVE_RIGHT))
{
Vel.x = 0;
}
if(Vel.x < 0 && (MoveRestriction & CANTMOVE_LEFT))
{
Vel.x = 0;
}
if(Vel.y > 0 && (MoveRestriction & CANTMOVE_DOWN))
{
Vel.y = 0;
}
if(Vel.y < 0 && (MoveRestriction & CANTMOVE_UP))
{
Vel.y = 0;
}
return Vel;
}
CCollision::CCollision()
{
m_pTiles = 0;
m_Width = 0;
m_Height = 0;
m_pLayers = 0;
m_pTele = 0;
m_pSpeedup = 0;
m_pFront = 0;
m_pSwitch = 0;
m_pDoor = 0;
m_pTune = 0;
}
CCollision::~CCollision()
{
Dest();
}
void CCollision::Init(class CLayers *pLayers)
{
Dest();
m_HighestSwitchNumber = 0;
m_pLayers = pLayers;
m_Width = m_pLayers->GameLayer()->m_Width;
m_Height = m_pLayers->GameLayer()->m_Height;
m_pTiles = static_cast<CTile *>(m_pLayers->Map()->GetData(m_pLayers->GameLayer()->m_Data));
if(m_pLayers->TeleLayer())
{
unsigned int Size = m_pLayers->Map()->GetDataSize(m_pLayers->TeleLayer()->m_Tele);
if(Size >= (size_t)m_Width * m_Height * sizeof(CTeleTile))
m_pTele = static_cast<CTeleTile *>(m_pLayers->Map()->GetData(m_pLayers->TeleLayer()->m_Tele));
}
if(m_pLayers->SpeedupLayer())
{
unsigned int Size = m_pLayers->Map()->GetDataSize(m_pLayers->SpeedupLayer()->m_Speedup);
if(Size >= (size_t)m_Width * m_Height * sizeof(CSpeedupTile))
m_pSpeedup = static_cast<CSpeedupTile *>(m_pLayers->Map()->GetData(m_pLayers->SpeedupLayer()->m_Speedup));
}
if(m_pLayers->SwitchLayer())
{
unsigned int Size = m_pLayers->Map()->GetDataSize(m_pLayers->SwitchLayer()->m_Switch);
if(Size >= (size_t)m_Width * m_Height * sizeof(CSwitchTile))
m_pSwitch = static_cast<CSwitchTile *>(m_pLayers->Map()->GetData(m_pLayers->SwitchLayer()->m_Switch));
m_pDoor = new CDoorTile[m_Width * m_Height];
mem_zero(m_pDoor, (size_t)m_Width * m_Height * sizeof(CDoorTile));
}
else
{
m_pDoor = 0;
}
if(m_pLayers->TuneLayer())
{
unsigned int Size = m_pLayers->Map()->GetDataSize(m_pLayers->TuneLayer()->m_Tune);
if(Size >= (size_t)m_Width * m_Height * sizeof(CTuneTile))
m_pTune = static_cast<CTuneTile *>(m_pLayers->Map()->GetData(m_pLayers->TuneLayer()->m_Tune));
}
if(m_pLayers->FrontLayer())
{
unsigned int Size = m_pLayers->Map()->GetDataSize(m_pLayers->FrontLayer()->m_Front);
if(Size >= (size_t)m_Width * m_Height * sizeof(CTile))
m_pFront = static_cast<CTile *>(m_pLayers->Map()->GetData(m_pLayers->FrontLayer()->m_Front));
}
for(int i = 0; i < m_Width * m_Height; i++)
{
int Index;
if(m_pSwitch)
{
if(m_pSwitch[i].m_Number > m_HighestSwitchNumber)
m_HighestSwitchNumber = m_pSwitch[i].m_Number;
if(m_pSwitch[i].m_Number)
m_pDoor[i].m_Number = m_pSwitch[i].m_Number;
else
m_pDoor[i].m_Number = 0;
Index = m_pSwitch[i].m_Type;
if(Index <= TILE_NPH_ENABLE)
{
if((Index >= TILE_JUMP && Index <= TILE_SUBTRACT_TIME) || Index == TILE_ALLOW_TELE_GUN || Index == TILE_ALLOW_BLUE_TELE_GUN)
m_pSwitch[i].m_Type = Index;
else
m_pSwitch[i].m_Type = 0;
}
}
}
}
void CCollision::FillAntibot(CAntibotMapData *pMapData)
{
pMapData->m_Width = m_Width;
pMapData->m_Height = m_Height;
pMapData->m_pTiles = (unsigned char *)malloc((size_t)m_Width * m_Height);
for(int i = 0; i < m_Width * m_Height; i++)
{
pMapData->m_pTiles[i] = 0;
if(m_pTiles[i].m_Index >= TILE_SOLID && m_pTiles[i].m_Index <= TILE_NOLASER)
{
pMapData->m_pTiles[i] = m_pTiles[i].m_Index;
}
}
}
enum
{
MR_DIR_HERE = 0,
MR_DIR_RIGHT,
MR_DIR_DOWN,
MR_DIR_LEFT,
MR_DIR_UP,
NUM_MR_DIRS
};
static int GetMoveRestrictionsRaw(int Direction, int Tile, int Flags)
{
Flags = Flags & (TILEFLAG_VFLIP | TILEFLAG_HFLIP | TILEFLAG_ROTATE);
switch(Tile)
{
case TILE_STOP:
switch(Flags)
{
case ROTATION_0: return CANTMOVE_DOWN;
case ROTATION_90: return CANTMOVE_LEFT;
case ROTATION_180: return CANTMOVE_UP;
case ROTATION_270: return CANTMOVE_RIGHT;
case TILEFLAG_HFLIP ^ ROTATION_0: return CANTMOVE_UP;
case TILEFLAG_HFLIP ^ ROTATION_90: return CANTMOVE_RIGHT;
case TILEFLAG_HFLIP ^ ROTATION_180: return CANTMOVE_DOWN;
case TILEFLAG_HFLIP ^ ROTATION_270: return CANTMOVE_LEFT;
}
break;
case TILE_STOPS:
switch(Flags)
{
case ROTATION_0:
case ROTATION_180:
case TILEFLAG_HFLIP ^ ROTATION_0:
case TILEFLAG_HFLIP ^ ROTATION_180:
return CANTMOVE_DOWN | CANTMOVE_UP;
case ROTATION_90:
case ROTATION_270:
case TILEFLAG_HFLIP ^ ROTATION_90:
case TILEFLAG_HFLIP ^ ROTATION_270:
return CANTMOVE_LEFT | CANTMOVE_RIGHT;
}
break;
case TILE_STOPA:
return CANTMOVE_LEFT | CANTMOVE_RIGHT | CANTMOVE_UP | CANTMOVE_DOWN;
}
return 0;
}
static int GetMoveRestrictionsMask(int Direction)
{
switch(Direction)
{
case MR_DIR_HERE: return 0;
case MR_DIR_RIGHT: return CANTMOVE_RIGHT;
case MR_DIR_DOWN: return CANTMOVE_DOWN;
case MR_DIR_LEFT: return CANTMOVE_LEFT;
case MR_DIR_UP: return CANTMOVE_UP;
default: dbg_assert(false, "invalid dir");
}
return 0;
}
static int GetMoveRestrictions(int Direction, int Tile, int Flags)
{
int Result = GetMoveRestrictionsRaw(Direction, Tile, Flags);
// Generally, stoppers only have an effect if they block us from moving
// *onto* them. The one exception is one-way blockers, they can also
// block us from moving if we're on top of them.
if(Direction == MR_DIR_HERE && Tile == TILE_STOP)
{
return Result;
}
return Result & GetMoveRestrictionsMask(Direction);
}
int CCollision::GetMoveRestrictions(CALLBACK_SWITCHACTIVE pfnSwitchActive, void *pUser, vec2 Pos, float Distance, int OverrideCenterTileIndex)
{
static const vec2 DIRECTIONS[NUM_MR_DIRS] =
{
vec2(0, 0),
vec2(1, 0),
vec2(0, 1),
vec2(-1, 0),
vec2(0, -1)};
dbg_assert(0.0f <= Distance && Distance <= 32.0f, "invalid distance");
int Restrictions = 0;
for(int d = 0; d < NUM_MR_DIRS; d++)
{
vec2 ModPos = Pos + DIRECTIONS[d] * Distance;
int ModMapIndex = GetPureMapIndex(ModPos);
if(d == MR_DIR_HERE && OverrideCenterTileIndex >= 0)
{
ModMapIndex = OverrideCenterTileIndex;
}
for(int Front = 0; Front < 2; Front++)
{
int Tile;
int Flags;
if(!Front)
{
Tile = GetTileIndex(ModMapIndex);
Flags = GetTileFlags(ModMapIndex);
}
else
{
Tile = GetFTileIndex(ModMapIndex);
Flags = GetFTileFlags(ModMapIndex);
}
Restrictions |= ::GetMoveRestrictions(d, Tile, Flags);
}
if(pfnSwitchActive)
{
int TeleNumber = GetDTileNumber(ModMapIndex);
if(pfnSwitchActive(TeleNumber, pUser))
{
int Tile = GetDTileIndex(ModMapIndex);
int Flags = GetDTileFlags(ModMapIndex);
Restrictions |= ::GetMoveRestrictions(d, Tile, Flags);
}
}
}
return Restrictions;
}
int CCollision::GetTile(int x, int y) const
{
if(!m_pTiles)
return 0;
int Nx = clamp(x / 32, 0, m_Width - 1);
int Ny = clamp(y / 32, 0, m_Height - 1);
int pos = Ny * m_Width + Nx;
if(m_pTiles[pos].m_Index >= TILE_SOLID && m_pTiles[pos].m_Index <= TILE_NOLASER)
return m_pTiles[pos].m_Index;
return 0;
}
// TODO: rewrite this smarter!
int CCollision::IntersectLine(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision) const
{
float Distance = distance(Pos0, Pos1);
int End(Distance + 1);
vec2 Last = Pos0;
int ix = 0, iy = 0; // Temporary position for checking collision
for(int i = 0; i <= End; i++)
{
float a = i / (float)End;
vec2 Pos = mix(Pos0, Pos1, a);
ix = round_to_int(Pos.x);
iy = round_to_int(Pos.y);
if(CheckPoint(ix, iy))
{
if(pOutCollision)
*pOutCollision = Pos;
if(pOutBeforeCollision)
*pOutBeforeCollision = Last;
return GetCollisionAt(ix, iy);
}
Last = Pos;
}
if(pOutCollision)
*pOutCollision = Pos1;
if(pOutBeforeCollision)
*pOutBeforeCollision = Pos1;
return 0;
}
int CCollision::IntersectLineTeleHook(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision, int *pTeleNr) const
{
float Distance = distance(Pos0, Pos1);
int End(Distance + 1);
vec2 Last = Pos0;
int ix = 0, iy = 0; // Temporary position for checking collision
int dx = 0, dy = 0; // Offset for checking the "through" tile
ThroughOffset(Pos0, Pos1, &dx, &dy);
for(int i = 0; i <= End; i++)
{
float a = i / (float)End;
vec2 Pos = mix(Pos0, Pos1, a);
ix = round_to_int(Pos.x);
iy = round_to_int(Pos.y);
int Index = GetPureMapIndex(Pos);
if(g_Config.m_SvOldTeleportHook)
*pTeleNr = IsTeleport(Index);
else
*pTeleNr = IsTeleportHook(Index);
if(*pTeleNr)
{
if(pOutCollision)
*pOutCollision = Pos;
if(pOutBeforeCollision)
*pOutBeforeCollision = Last;
return TILE_TELEINHOOK;
}
int hit = 0;
if(CheckPoint(ix, iy))
{
if(!IsThrough(ix, iy, dx, dy, Pos0, Pos1))
hit = GetCollisionAt(ix, iy);
}
else if(IsHookBlocker(ix, iy, Pos0, Pos1))
{
hit = TILE_NOHOOK;
}
if(hit)
{
if(pOutCollision)
*pOutCollision = Pos;
if(pOutBeforeCollision)
*pOutBeforeCollision = Last;
return hit;
}
Last = Pos;
}
if(pOutCollision)
*pOutCollision = Pos1;
if(pOutBeforeCollision)
*pOutBeforeCollision = Pos1;
return 0;
}
int CCollision::IntersectLineTeleWeapon(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision, int *pTeleNr) const
{
float Distance = distance(Pos0, Pos1);
int End(Distance + 1);
vec2 Last = Pos0;
int ix = 0, iy = 0; // Temporary position for checking collision
for(int i = 0; i <= End; i++)
{
float a = i / (float)End;
vec2 Pos = mix(Pos0, Pos1, a);
ix = round_to_int(Pos.x);
iy = round_to_int(Pos.y);
int Index = GetPureMapIndex(Pos);
if(g_Config.m_SvOldTeleportWeapons)
*pTeleNr = IsTeleport(Index);
else
*pTeleNr = IsTeleportWeapon(Index);
if(*pTeleNr)
{
if(pOutCollision)
*pOutCollision = Pos;
if(pOutBeforeCollision)
*pOutBeforeCollision = Last;
return TILE_TELEINWEAPON;
}
if(CheckPoint(ix, iy))
{
if(pOutCollision)
*pOutCollision = Pos;
if(pOutBeforeCollision)
*pOutBeforeCollision = Last;
return GetCollisionAt(ix, iy);
}
Last = Pos;
}
if(pOutCollision)
*pOutCollision = Pos1;
if(pOutBeforeCollision)
*pOutBeforeCollision = Pos1;
return 0;
}
// TODO: OPT: rewrite this smarter!
void CCollision::MovePoint(vec2 *pInoutPos, vec2 *pInoutVel, float Elasticity, int *pBounces) const
{
if(pBounces)
*pBounces = 0;
vec2 Pos = *pInoutPos;
vec2 Vel = *pInoutVel;
if(CheckPoint(Pos + Vel))
{
int Affected = 0;
if(CheckPoint(Pos.x + Vel.x, Pos.y))
{
pInoutVel->x *= -Elasticity;
if(pBounces)
(*pBounces)++;
Affected++;
}
if(CheckPoint(Pos.x, Pos.y + Vel.y))
{
pInoutVel->y *= -Elasticity;
if(pBounces)
(*pBounces)++;
Affected++;
}
if(Affected == 0)
{
pInoutVel->x *= -Elasticity;
pInoutVel->y *= -Elasticity;
}
}
else
{
*pInoutPos = Pos + Vel;
}
}
bool CCollision::TestBox(vec2 Pos, vec2 Size) const
{
Size *= 0.5f;
if(CheckPoint(Pos.x - Size.x, Pos.y - Size.y))
return true;
if(CheckPoint(Pos.x + Size.x, Pos.y - Size.y))
return true;
if(CheckPoint(Pos.x - Size.x, Pos.y + Size.y))
return true;
if(CheckPoint(Pos.x + Size.x, Pos.y + Size.y))
return true;
return false;
}
void CCollision::MoveBox(vec2 *pInoutPos, vec2 *pInoutVel, vec2 Size, float Elasticity) const
{
// do the move
vec2 Pos = *pInoutPos;
vec2 Vel = *pInoutVel;
float Distance = length(Vel);
int Max = (int)Distance;
if(Distance > 0.00001f)
{
float Fraction = 1.0f / (float)(Max + 1);
for(int i = 0; i <= Max; i++)
{
// Early break as optimization to stop checking for collisions for
// large distances after the obstacles we have already hit reduced
// our speed to exactly 0.
if(Vel == vec2(0, 0))
{
break;
}
vec2 NewPos = Pos + Vel * Fraction; // TODO: this row is not nice
// Fraction can be very small and thus the calculation has no effect, no
// reason to continue calculating.
if(NewPos == Pos)
{
break;
}
if(TestBox(vec2(NewPos.x, NewPos.y), Size))
{
int Hits = 0;
if(TestBox(vec2(Pos.x, NewPos.y), Size))
{
NewPos.y = Pos.y;
Vel.y *= -Elasticity;
Hits++;
}
if(TestBox(vec2(NewPos.x, Pos.y), Size))
{
NewPos.x = Pos.x;
Vel.x *= -Elasticity;
Hits++;
}
// neither of the tests got a collision.
// this is a real _corner case_!
if(Hits == 0)
{
NewPos.y = Pos.y;
Vel.y *= -Elasticity;
NewPos.x = Pos.x;
Vel.x *= -Elasticity;
}
}
Pos = NewPos;
}
}
*pInoutPos = Pos;
*pInoutVel = Vel;
}
// DDRace
void CCollision::Dest()
{
delete[] m_pDoor;
m_pTiles = 0;
m_Width = 0;
m_Height = 0;
m_pLayers = 0;
m_pTele = 0;
m_pSpeedup = 0;
m_pFront = 0;
m_pSwitch = 0;
m_pTune = 0;
m_pDoor = 0;
}
int CCollision::IsSolid(int x, int y) const
{
int index = GetTile(x, y);
return index == TILE_SOLID || index == TILE_NOHOOK;
}
bool CCollision::IsThrough(int x, int y, int xoff, int yoff, vec2 pos0, vec2 pos1) const
{
int pos = GetPureMapIndex(x, y);
if(m_pFront && (m_pFront[pos].m_Index == TILE_THROUGH_ALL || m_pFront[pos].m_Index == TILE_THROUGH_CUT))
return true;
if(m_pFront && m_pFront[pos].m_Index == TILE_THROUGH_DIR && ((m_pFront[pos].m_Flags == ROTATION_0 && pos0.y > pos1.y) || (m_pFront[pos].m_Flags == ROTATION_90 && pos0.x < pos1.x) || (m_pFront[pos].m_Flags == ROTATION_180 && pos0.y < pos1.y) || (m_pFront[pos].m_Flags == ROTATION_270 && pos0.x > pos1.x)))
return true;
int offpos = GetPureMapIndex(x + xoff, y + yoff);
return m_pTiles[offpos].m_Index == TILE_THROUGH || (m_pFront && m_pFront[offpos].m_Index == TILE_THROUGH);
}
bool CCollision::IsHookBlocker(int x, int y, vec2 pos0, vec2 pos1) const
{
int pos = GetPureMapIndex(x, y);
if(m_pTiles[pos].m_Index == TILE_THROUGH_ALL || (m_pFront && m_pFront[pos].m_Index == TILE_THROUGH_ALL))
return true;
if(m_pTiles[pos].m_Index == TILE_THROUGH_DIR && ((m_pTiles[pos].m_Flags == ROTATION_0 && pos0.y < pos1.y) ||
(m_pTiles[pos].m_Flags == ROTATION_90 && pos0.x > pos1.x) ||
(m_pTiles[pos].m_Flags == ROTATION_180 && pos0.y > pos1.y) ||
(m_pTiles[pos].m_Flags == ROTATION_270 && pos0.x < pos1.x)))
return true;
if(m_pFront && m_pFront[pos].m_Index == TILE_THROUGH_DIR && ((m_pFront[pos].m_Flags == ROTATION_0 && pos0.y < pos1.y) || (m_pFront[pos].m_Flags == ROTATION_90 && pos0.x > pos1.x) || (m_pFront[pos].m_Flags == ROTATION_180 && pos0.y > pos1.y) || (m_pFront[pos].m_Flags == ROTATION_270 && pos0.x < pos1.x)))
return true;
return false;
}
int CCollision::IsWallJump(int Index) const
{
if(Index < 0)
return 0;
return m_pTiles[Index].m_Index == TILE_WALLJUMP;
}
int CCollision::IsNoLaser(int x, int y) const
{
return (CCollision::GetTile(x, y) == TILE_NOLASER);
}
int CCollision::IsFNoLaser(int x, int y) const
{
return (CCollision::GetFTile(x, y) == TILE_NOLASER);
}
int CCollision::IsTeleport(int Index) const
{
if(Index < 0 || !m_pTele)
return 0;
if(m_pTele[Index].m_Type == TILE_TELEIN)
return m_pTele[Index].m_Number;
return 0;
}
int CCollision::IsEvilTeleport(int Index) const
{
if(Index < 0)
return 0;
if(!m_pTele)
return 0;
if(m_pTele[Index].m_Type == TILE_TELEINEVIL)
return m_pTele[Index].m_Number;
return 0;
}
int CCollision::IsCheckTeleport(int Index) const
{
if(Index < 0)
return 0;
if(!m_pTele)
return 0;
if(m_pTele[Index].m_Type == TILE_TELECHECKIN)
return m_pTele[Index].m_Number;
return 0;
}
int CCollision::IsCheckEvilTeleport(int Index) const
{
if(Index < 0)
return 0;
if(!m_pTele)
return 0;
if(m_pTele[Index].m_Type == TILE_TELECHECKINEVIL)
return m_pTele[Index].m_Number;
return 0;
}
int CCollision::IsTeleCheckpoint(int Index) const
{
if(Index < 0)
return 0;
if(!m_pTele)
return 0;
if(m_pTele[Index].m_Type == TILE_TELECHECK)
return m_pTele[Index].m_Number;
return 0;
}
int CCollision::IsTeleportWeapon(int Index) const
{
if(Index < 0 || !m_pTele)
return 0;
if(m_pTele[Index].m_Type == TILE_TELEINWEAPON)
return m_pTele[Index].m_Number;
return 0;
}
int CCollision::IsTeleportHook(int Index) const
{
if(Index < 0 || !m_pTele)
return 0;
if(m_pTele[Index].m_Type == TILE_TELEINHOOK)
return m_pTele[Index].m_Number;
return 0;
}
int CCollision::IsSpeedup(int Index) const
{
if(Index < 0 || !m_pSpeedup)
return 0;
if(m_pSpeedup[Index].m_Force > 0)
return Index;
return 0;
}
int CCollision::IsTune(int Index) const
{
if(Index < 0 || !m_pTune)
return 0;
if(m_pTune[Index].m_Type)
return m_pTune[Index].m_Number;
return 0;
}
void CCollision::GetSpeedup(int Index, vec2 *pDir, int *pForce, int *pMaxSpeed) const
{
if(Index < 0 || !m_pSpeedup)
return;
float Angle = m_pSpeedup[Index].m_Angle * (pi / 180.0f);
*pForce = m_pSpeedup[Index].m_Force;
*pDir = vec2(cos(Angle), sin(Angle));
if(pMaxSpeed)
*pMaxSpeed = m_pSpeedup[Index].m_MaxSpeed;
}
int CCollision::GetSwitchType(int Index) const
{
if(Index < 0 || !m_pSwitch)
return 0;
if(m_pSwitch[Index].m_Type > 0)
return m_pSwitch[Index].m_Type;
return 0;
}
int CCollision::GetSwitchNumber(int Index) const
{
if(Index < 0 || !m_pSwitch)
return 0;
if(m_pSwitch[Index].m_Type > 0 && m_pSwitch[Index].m_Number > 0)
return m_pSwitch[Index].m_Number;
return 0;
}
int CCollision::GetSwitchDelay(int Index) const
{
if(Index < 0 || !m_pSwitch)
return 0;
if(m_pSwitch[Index].m_Type > 0)
return m_pSwitch[Index].m_Delay;
return 0;
}
int CCollision::IsMover(int x, int y, int *pFlags) const
{
int Nx = clamp(x / 32, 0, m_Width - 1);
int Ny = clamp(y / 32, 0, m_Height - 1);
int Index = m_pTiles[Ny * m_Width + Nx].m_Index;
*pFlags = m_pTiles[Ny * m_Width + Nx].m_Flags;
if(Index < 0)
return 0;
if(Index == TILE_CP || Index == TILE_CP_F)
return Index;
else
return 0;
}
vec2 CCollision::CpSpeed(int Index, int Flags) const
{
if(Index < 0)
return vec2(0, 0);
vec2 target;
if(Index == TILE_CP || Index == TILE_CP_F)
switch(Flags)
{
case ROTATION_0:
target.x = 0;
target.y = -4;
break;
case ROTATION_90:
target.x = 4;
target.y = 0;
break;
case ROTATION_180:
target.x = 0;
target.y = 4;
break;
case ROTATION_270:
target.x = -4;
target.y = 0;
break;
default:
target = vec2(0, 0);
break;
}
if(Index == TILE_CP_F)
target *= 4;
return target;
}
int CCollision::GetPureMapIndex(float x, float y) const
{
int Nx = clamp(round_to_int(x) / 32, 0, m_Width - 1);
int Ny = clamp(round_to_int(y) / 32, 0, m_Height - 1);
return Ny * m_Width + Nx;
}
bool CCollision::TileExists(int Index) const
{
if(Index < 0)
return false;
if((m_pTiles[Index].m_Index >= TILE_FREEZE && m_pTiles[Index].m_Index <= TILE_TELE_LASER_DISABLE) || (m_pTiles[Index].m_Index >= TILE_LFREEZE && m_pTiles[Index].m_Index <= TILE_LUNFREEZE))
return true;
if(m_pFront && ((m_pFront[Index].m_Index >= TILE_FREEZE && m_pFront[Index].m_Index <= TILE_TELE_LASER_DISABLE) || (m_pFront[Index].m_Index >= TILE_LFREEZE && m_pFront[Index].m_Index <= TILE_LUNFREEZE)))
return true;
if(m_pTele && (m_pTele[Index].m_Type == TILE_TELEIN || m_pTele[Index].m_Type == TILE_TELEINEVIL || m_pTele[Index].m_Type == TILE_TELECHECKINEVIL || m_pTele[Index].m_Type == TILE_TELECHECK || m_pTele[Index].m_Type == TILE_TELECHECKIN))
return true;
if(m_pSpeedup && m_pSpeedup[Index].m_Force > 0)
return true;
if(m_pDoor && m_pDoor[Index].m_Index)
return true;
if(m_pSwitch && m_pSwitch[Index].m_Type)
return true;
if(m_pTune && m_pTune[Index].m_Type)
return true;
return TileExistsNext(Index);
}
bool CCollision::TileExistsNext(int Index) const
{
if(Index < 0)
return false;
int TileOnTheLeft = (Index - 1 > 0) ? Index - 1 : Index;
int TileOnTheRight = (Index + 1 < m_Width * m_Height) ? Index + 1 : Index;
int TileBelow = (Index + m_Width < m_Width * m_Height) ? Index + m_Width : Index;
int TileAbove = (Index - m_Width > 0) ? Index - m_Width : Index;
if((m_pTiles[TileOnTheRight].m_Index == TILE_STOP && m_pTiles[TileOnTheRight].m_Flags == ROTATION_270) || (m_pTiles[TileOnTheLeft].m_Index == TILE_STOP && m_pTiles[TileOnTheLeft].m_Flags == ROTATION_90))
return true;
if((m_pTiles[TileBelow].m_Index == TILE_STOP && m_pTiles[TileBelow].m_Flags == ROTATION_0) || (m_pTiles[TileAbove].m_Index == TILE_STOP && m_pTiles[TileAbove].m_Flags == ROTATION_180))
return true;
if(m_pTiles[TileOnTheRight].m_Index == TILE_STOPA || m_pTiles[TileOnTheLeft].m_Index == TILE_STOPA || ((m_pTiles[TileOnTheRight].m_Index == TILE_STOPS || m_pTiles[TileOnTheLeft].m_Index == TILE_STOPS)))
return true;
if(m_pTiles[TileBelow].m_Index == TILE_STOPA || m_pTiles[TileAbove].m_Index == TILE_STOPA || ((m_pTiles[TileBelow].m_Index == TILE_STOPS || m_pTiles[TileAbove].m_Index == TILE_STOPS) && m_pTiles[TileBelow].m_Flags | ROTATION_180 | ROTATION_0))
return true;
if(m_pFront)
{
if(m_pFront[TileOnTheRight].m_Index == TILE_STOPA || m_pFront[TileOnTheLeft].m_Index == TILE_STOPA || ((m_pFront[TileOnTheRight].m_Index == TILE_STOPS || m_pFront[TileOnTheLeft].m_Index == TILE_STOPS)))
return true;
if(m_pFront[TileBelow].m_Index == TILE_STOPA || m_pFront[TileAbove].m_Index == TILE_STOPA || ((m_pFront[TileBelow].m_Index == TILE_STOPS || m_pFront[TileAbove].m_Index == TILE_STOPS) && m_pFront[TileBelow].m_Flags | ROTATION_180 | ROTATION_0))
return true;
if((m_pFront[TileOnTheRight].m_Index == TILE_STOP && m_pFront[TileOnTheRight].m_Flags == ROTATION_270) || (m_pFront[TileOnTheLeft].m_Index == TILE_STOP && m_pFront[TileOnTheLeft].m_Flags == ROTATION_90))
return true;
if((m_pFront[TileBelow].m_Index == TILE_STOP && m_pFront[TileBelow].m_Flags == ROTATION_0) || (m_pFront[TileAbove].m_Index == TILE_STOP && m_pFront[TileAbove].m_Flags == ROTATION_180))
return true;
}
if(m_pDoor)
{
if(m_pDoor[TileOnTheRight].m_Index == TILE_STOPA || m_pDoor[TileOnTheLeft].m_Index == TILE_STOPA || ((m_pDoor[TileOnTheRight].m_Index == TILE_STOPS || m_pDoor[TileOnTheLeft].m_Index == TILE_STOPS)))
return true;
if(m_pDoor[TileBelow].m_Index == TILE_STOPA || m_pDoor[TileAbove].m_Index == TILE_STOPA || ((m_pDoor[TileBelow].m_Index == TILE_STOPS || m_pDoor[TileAbove].m_Index == TILE_STOPS) && m_pDoor[TileBelow].m_Flags | ROTATION_180 | ROTATION_0))
return true;
if((m_pDoor[TileOnTheRight].m_Index == TILE_STOP && m_pDoor[TileOnTheRight].m_Flags == ROTATION_270) || (m_pDoor[TileOnTheLeft].m_Index == TILE_STOP && m_pDoor[TileOnTheLeft].m_Flags == ROTATION_90))
return true;
if((m_pDoor[TileBelow].m_Index == TILE_STOP && m_pDoor[TileBelow].m_Flags == ROTATION_0) || (m_pDoor[TileAbove].m_Index == TILE_STOP && m_pDoor[TileAbove].m_Flags == ROTATION_180))
return true;
}
return false;
}
int CCollision::GetMapIndex(vec2 Pos) const
{
int Nx = clamp((int)Pos.x / 32, 0, m_Width - 1);
int Ny = clamp((int)Pos.y / 32, 0, m_Height - 1);
int Index = Ny * m_Width + Nx;
if(TileExists(Index))
return Index;
else
return -1;
}
std::list<int> CCollision::GetMapIndices(vec2 PrevPos, vec2 Pos, unsigned MaxIndices) const
{
std::list<int> Indices;
float d = distance(PrevPos, Pos);
int End(d + 1);
if(!d)
{
int Nx = clamp((int)Pos.x / 32, 0, m_Width - 1);
int Ny = clamp((int)Pos.y / 32, 0, m_Height - 1);
int Index = Ny * m_Width + Nx;
if(TileExists(Index))
{
Indices.push_back(Index);
return Indices;
}
else
return Indices;
}
else
{
float a = 0.0f;
vec2 Tmp = vec2(0, 0);
int Nx = 0;
int Ny = 0;
int Index, LastIndex = 0;
for(int i = 0; i < End; i++)
{
a = i / d;
Tmp = mix(PrevPos, Pos, a);
Nx = clamp((int)Tmp.x / 32, 0, m_Width - 1);
Ny = clamp((int)Tmp.y / 32, 0, m_Height - 1);
Index = Ny * m_Width + Nx;
if(TileExists(Index) && LastIndex != Index)
{
if(MaxIndices && Indices.size() > MaxIndices)
return Indices;
Indices.push_back(Index);
LastIndex = Index;
}
}
return Indices;
}
}
vec2 CCollision::GetPos(int Index) const
{
if(Index < 0)
return vec2(0, 0);
int x = Index % m_Width;
int y = Index / m_Width;
return vec2(x * 32 + 16, y * 32 + 16);
}
int CCollision::GetTileIndex(int Index) const
{
if(Index < 0)
return 0;
return m_pTiles[Index].m_Index;
}
int CCollision::GetFTileIndex(int Index) const
{
if(Index < 0 || !m_pFront)
return 0;
return m_pFront[Index].m_Index;
}
int CCollision::GetTileFlags(int Index) const
{
if(Index < 0)
return 0;
return m_pTiles[Index].m_Flags;
}
int CCollision::GetFTileFlags(int Index) const
{
if(Index < 0 || !m_pFront)
return 0;
return m_pFront[Index].m_Flags;
}
int CCollision::GetIndex(int Nx, int Ny) const
{
return m_pTiles[Ny * m_Width + Nx].m_Index;
}
int CCollision::GetIndex(vec2 PrevPos, vec2 Pos) const
{
float Distance = distance(PrevPos, Pos);
if(!Distance)
{
int Nx = clamp((int)Pos.x / 32, 0, m_Width - 1);
int Ny = clamp((int)Pos.y / 32, 0, m_Height - 1);
if((m_pTele) ||
(m_pSpeedup && m_pSpeedup[Ny * m_Width + Nx].m_Force > 0))
{
return Ny * m_Width + Nx;
}
}
float a = 0.0f;
vec2 Tmp = vec2(0, 0);
int Nx = 0;
int Ny = 0;
for(int i = 0, id = (int)ceilf(Distance); i < id; i++)
{
a = (float)i / Distance;
Tmp = mix(PrevPos, Pos, a);
Nx = clamp((int)Tmp.x / 32, 0, m_Width - 1);
Ny = clamp((int)Tmp.y / 32, 0, m_Height - 1);
if((m_pTele) ||
(m_pSpeedup && m_pSpeedup[Ny * m_Width + Nx].m_Force > 0))
{
return Ny * m_Width + Nx;
}
}
return -1;
}
int CCollision::GetFIndex(int Nx, int Ny) const
{
if(!m_pFront)
return 0;
return m_pFront[Ny * m_Width + Nx].m_Index;
}
int CCollision::GetFTile(int x, int y) const
{
if(!m_pFront)
return 0;
int Nx = clamp(x / 32, 0, m_Width - 1);
int Ny = clamp(y / 32, 0, m_Height - 1);
if(m_pFront[Ny * m_Width + Nx].m_Index == TILE_DEATH || m_pFront[Ny * m_Width + Nx].m_Index == TILE_NOLASER)
return m_pFront[Ny * m_Width + Nx].m_Index;
else
return 0;
}
int CCollision::Entity(int x, int y, int Layer) const
{
if((0 > x || x >= m_Width) || (0 > y || y >= m_Height))
{
char aBuf[12];
switch(Layer)
{
case LAYER_GAME:
str_format(aBuf, sizeof(aBuf), "Game");
break;
case LAYER_FRONT:
str_format(aBuf, sizeof(aBuf), "Front");
break;
case LAYER_SWITCH:
str_format(aBuf, sizeof(aBuf), "Switch");
break;
case LAYER_TELE:
str_format(aBuf, sizeof(aBuf), "Tele");
break;
case LAYER_SPEEDUP:
str_format(aBuf, sizeof(aBuf), "Speedup");
break;
case LAYER_TUNE:
str_format(aBuf, sizeof(aBuf), "Tune");
break;
default:
str_format(aBuf, sizeof(aBuf), "Unknown");
}
dbg_msg("collision", "something is VERY wrong with the %s layer please report this at https://github.com/ddnet/ddnet, you will need to post the map as well and any steps that u think may have led to this", aBuf);
return 0;
}
switch(Layer)
{
case LAYER_GAME:
return m_pTiles[y * m_Width + x].m_Index - ENTITY_OFFSET;
case LAYER_FRONT:
return m_pFront[y * m_Width + x].m_Index - ENTITY_OFFSET;
case LAYER_SWITCH:
return m_pSwitch[y * m_Width + x].m_Type - ENTITY_OFFSET;
case LAYER_TELE:
return m_pTele[y * m_Width + x].m_Type - ENTITY_OFFSET;
case LAYER_SPEEDUP:
return m_pSpeedup[y * m_Width + x].m_Type - ENTITY_OFFSET;
case LAYER_TUNE:
return m_pTune[y * m_Width + x].m_Type - ENTITY_OFFSET;
default:
return 0;
break;
}
}
void CCollision::SetCollisionAt(float x, float y, int id)
{
int Nx = clamp(round_to_int(x) / 32, 0, m_Width - 1);
int Ny = clamp(round_to_int(y) / 32, 0, m_Height - 1);
m_pTiles[Ny * m_Width + Nx].m_Index = id;
}
void CCollision::SetDCollisionAt(float x, float y, int Type, int Flags, int Number)
{
if(!m_pDoor)
return;
int Nx = clamp(round_to_int(x) / 32, 0, m_Width - 1);
int Ny = clamp(round_to_int(y) / 32, 0, m_Height - 1);
m_pDoor[Ny * m_Width + Nx].m_Index = Type;
m_pDoor[Ny * m_Width + Nx].m_Flags = Flags;
m_pDoor[Ny * m_Width + Nx].m_Number = Number;
}
int CCollision::GetDTileIndex(int Index) const
{
if(!m_pDoor || Index < 0 || !m_pDoor[Index].m_Index)
return 0;
return m_pDoor[Index].m_Index;
}
int CCollision::GetDTileNumber(int Index) const
{
if(!m_pDoor || Index < 0 || !m_pDoor[Index].m_Index)
return 0;
if(m_pDoor[Index].m_Number)
return m_pDoor[Index].m_Number;
return 0;
}
int CCollision::GetDTileFlags(int Index) const
{
if(!m_pDoor || Index < 0 || !m_pDoor[Index].m_Index)
return 0;
return m_pDoor[Index].m_Flags;
}
void ThroughOffset(vec2 Pos0, vec2 Pos1, int *pOffsetX, int *pOffsetY)
{
float x = Pos0.x - Pos1.x;
float y = Pos0.y - Pos1.y;
if(fabs(x) > fabs(y))
{
if(x < 0)
{
*pOffsetX = -32;
*pOffsetY = 0;
}
else
{
*pOffsetX = 32;
*pOffsetY = 0;
}
}
else
{
if(y < 0)
{
*pOffsetX = 0;
*pOffsetY = -32;
}
else
{
*pOffsetX = 0;
*pOffsetY = 32;
}
}
}
int CCollision::IntersectNoLaser(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision) const
{
float d = distance(Pos0, Pos1);
vec2 Last = Pos0;
for(int i = 0, id = (int)ceilf(d); i < id; i++)
{
float a = (int)i / d;
vec2 Pos = mix(Pos0, Pos1, a);
int Nx = clamp(round_to_int(Pos.x) / 32, 0, m_Width - 1);
int Ny = clamp(round_to_int(Pos.y) / 32, 0, m_Height - 1);
if(GetIndex(Nx, Ny) == TILE_SOLID || GetIndex(Nx, Ny) == TILE_NOHOOK || GetIndex(Nx, Ny) == TILE_NOLASER || GetFIndex(Nx, Ny) == TILE_NOLASER)
{
if(pOutCollision)
*pOutCollision = Pos;
if(pOutBeforeCollision)
*pOutBeforeCollision = Last;
if(GetFIndex(Nx, Ny) == TILE_NOLASER)
return GetFCollisionAt(Pos.x, Pos.y);
else
return GetCollisionAt(Pos.x, Pos.y);
}
Last = Pos;
}
if(pOutCollision)
*pOutCollision = Pos1;
if(pOutBeforeCollision)
*pOutBeforeCollision = Pos1;
return 0;
}
int CCollision::IntersectNoLaserNW(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision) const
{
float d = distance(Pos0, Pos1);
vec2 Last = Pos0;
for(int i = 0, id = (int)ceilf(d); i < id; i++)
{
float a = (float)i / d;
vec2 Pos = mix(Pos0, Pos1, a);
if(IsNoLaser(round_to_int(Pos.x), round_to_int(Pos.y)) || IsFNoLaser(round_to_int(Pos.x), round_to_int(Pos.y)))
{
if(pOutCollision)
*pOutCollision = Pos;
if(pOutBeforeCollision)
*pOutBeforeCollision = Last;
if(IsNoLaser(round_to_int(Pos.x), round_to_int(Pos.y)))
return GetCollisionAt(Pos.x, Pos.y);
else
return GetFCollisionAt(Pos.x, Pos.y);
}
Last = Pos;
}
if(pOutCollision)
*pOutCollision = Pos1;
if(pOutBeforeCollision)
*pOutBeforeCollision = Pos1;
return 0;
}
int CCollision::IntersectAir(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision) const
{
float d = distance(Pos0, Pos1);
vec2 Last = Pos0;
for(int i = 0, id = (int)ceilf(d); i < id; i++)
{
float a = (float)i / d;
vec2 Pos = mix(Pos0, Pos1, a);
if(IsSolid(round_to_int(Pos.x), round_to_int(Pos.y)) || (!GetTile(round_to_int(Pos.x), round_to_int(Pos.y)) && !GetFTile(round_to_int(Pos.x), round_to_int(Pos.y))))
{
if(pOutCollision)
*pOutCollision = Pos;
if(pOutBeforeCollision)
*pOutBeforeCollision = Last;
if(!GetTile(round_to_int(Pos.x), round_to_int(Pos.y)) && !GetFTile(round_to_int(Pos.x), round_to_int(Pos.y)))
return -1;
else if(!GetTile(round_to_int(Pos.x), round_to_int(Pos.y)))
return GetTile(round_to_int(Pos.x), round_to_int(Pos.y));
else
return GetFTile(round_to_int(Pos.x), round_to_int(Pos.y));
}
Last = Pos;
}
if(pOutCollision)
*pOutCollision = Pos1;
if(pOutBeforeCollision)
*pOutBeforeCollision = Pos1;
return 0;
}
int CCollision::IsTimeCheckpoint(int Index) const
{
if(Index < 0)
return -1;
int z = m_pTiles[Index].m_Index;
if(z >= TILE_TIME_CHECKPOINT_FIRST && z <= TILE_TIME_CHECKPOINT_LAST)
return z - TILE_TIME_CHECKPOINT_FIRST;
return -1;
}
int CCollision::IsFTimeCheckpoint(int Index) const
{
if(Index < 0 || !m_pFront)
return -1;
int z = m_pFront[Index].m_Index;
if(z >= TILE_TIME_CHECKPOINT_FIRST && z <= TILE_TIME_CHECKPOINT_LAST)
return z - TILE_TIME_CHECKPOINT_FIRST;
return -1;
}