ddnet/src/game/collision.cpp

205 lines
4 KiB
C++
Raw Normal View History

2010-11-20 10:37:14 +00:00
/* (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/system.h>
2010-05-29 07:25:38 +00:00
#include <base/math.h>
#include <base/vmath.h>
#include <math.h>
2010-05-29 07:25:38 +00:00
#include <engine/map.h>
#include <engine/kernel.h>
2010-05-29 07:25:38 +00:00
#include <game/mapitems.h>
#include <game/layers.h>
#include <game/collision.h>
2010-05-29 07:25:38 +00:00
CCollision::CCollision()
{
m_pTiles = 0;
m_Width = 0;
m_Height = 0;
m_pLayers = 0;
}
2010-05-29 07:25:38 +00:00
void CCollision::Init(class CLayers *pLayers)
{
2010-05-29 07:25:38 +00:00
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));
2010-05-29 07:25:38 +00:00
for(int i = 0; i < m_Width*m_Height; i++)
2008-09-23 14:38:13 +00:00
{
2010-05-29 07:25:38 +00:00
int Index = m_pTiles[i].m_Index;
2010-05-29 07:25:38 +00:00
if(Index > 128)
2008-09-23 14:38:13 +00:00
continue;
2010-05-29 07:25:38 +00:00
switch(Index)
{
case TILE_DEATH:
m_pTiles[i].m_Index = COLFLAG_DEATH;
break;
case TILE_SOLID:
m_pTiles[i].m_Index = COLFLAG_SOLID;
break;
case TILE_NOHOOK:
m_pTiles[i].m_Index = COLFLAG_SOLID|COLFLAG_NOHOOK;
break;
default:
m_pTiles[i].m_Index = 0;
}
2008-09-23 14:38:13 +00:00
}
}
int CCollision::GetTile(int x, int y)
{
int Nx = clamp(x/32, 0, m_Width-1);
int Ny = clamp(y/32, 0, m_Height-1);
return m_pTiles[Ny*m_Width+Nx].m_Index > 128 ? 0 : m_pTiles[Ny*m_Width+Nx].m_Index;
2008-09-23 14:10:05 +00:00
}
bool CCollision::IsTileSolid(int x, int y)
2008-09-23 14:10:05 +00:00
{
return GetTile(x, y)&COLFLAG_SOLID;
}
// TODO: rewrite this smarter!
2010-05-29 07:25:38 +00:00
int CCollision::IntersectLine(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision)
{
float Distance = distance(Pos0, Pos1);
int End(Distance+1);
2010-05-29 07:25:38 +00:00
vec2 Last = Pos0;
for(int i = 0; i < End; i++)
{
2011-02-13 06:53:25 +00:00
float a = i/Distance;
vec2 Pos = mix(Pos0, Pos1, a);
2010-05-29 07:25:38 +00:00
if(CheckPoint(Pos.x, Pos.y))
{
2010-05-29 07:25:38 +00:00
if(pOutCollision)
*pOutCollision = Pos;
if(pOutBeforeCollision)
*pOutBeforeCollision = Last;
return GetCollisionAt(Pos.x, Pos.y);
}
2010-05-29 07:25:38 +00:00
Last = Pos;
}
2010-05-29 07:25:38 +00:00
if(pOutCollision)
*pOutCollision = Pos1;
if(pOutBeforeCollision)
*pOutBeforeCollision = Pos1;
2008-09-23 14:38:13 +00:00
return 0;
}
2010-05-29 07:25:38 +00:00
// TODO: OPT: rewrite this smarter!
void CCollision::MovePoint(vec2 *pInoutPos, vec2 *pInoutVel, float Elasticity, int *pBounces)
{
if(pBounces)
*pBounces = 0;
2010-05-29 07:25:38 +00:00
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)++;
2010-05-29 07:25:38 +00:00
Affected++;
}
if(CheckPoint(Pos.x, Pos.y + Vel.y))
{
pInoutVel->y *= -Elasticity;
if(pBounces)
(*pBounces)++;
2010-05-29 07:25:38 +00:00
Affected++;
}
2010-05-29 07:25:38 +00:00
if(Affected == 0)
{
pInoutVel->x *= -Elasticity;
pInoutVel->y *= -Elasticity;
}
}
else
{
*pInoutPos = Pos + Vel;
}
}
bool CCollision::TestBox(vec2 Pos, vec2 Size)
{
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)
{
// do the move
vec2 Pos = *pInoutPos;
vec2 Vel = *pInoutVel;
2010-05-29 07:25:38 +00:00
float Distance = length(Vel);
int Max = (int)Distance;
2010-05-29 07:25:38 +00:00
if(Distance > 0.00001f)
{
//vec2 old_pos = pos;
float Fraction = 1.0f/(float)(Max+1);
for(int i = 0; i <= Max; i++)
{
//float amount = i/(float)max;
//if(max == 0)
//amount = 0;
2010-05-29 07:25:38 +00:00
vec2 NewPos = Pos + Vel*Fraction; // TODO: this row is not nice
2010-05-29 07:25:38 +00:00
if(TestBox(vec2(NewPos.x, NewPos.y), Size))
{
int Hits = 0;
2010-05-29 07:25:38 +00:00
if(TestBox(vec2(Pos.x, NewPos.y), Size))
{
NewPos.y = Pos.y;
Vel.y *= -Elasticity;
Hits++;
}
2010-05-29 07:25:38 +00:00
if(TestBox(vec2(NewPos.x, Pos.y), Size))
{
NewPos.x = Pos.x;
Vel.x *= -Elasticity;
Hits++;
}
2010-05-29 07:25:38 +00:00
// 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;
}
}
2010-05-29 07:25:38 +00:00
Pos = NewPos;
}
}
2010-05-29 07:25:38 +00:00
*pInoutPos = Pos;
*pInoutVel = Vel;
}