ddnet/src/game/client/prediction/entities/laser.cpp

205 lines
8.7 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 <game/generated/protocol.h>
#include "character.h"
#include "laser.h"
#include <engine/shared/config.h>
CLaser::CLaser(CGameWorld *pGameWorld, vec2 Pos, vec2 Direction, float StartEnergy, int Owner, int Type)
: CEntity(pGameWorld, CGameWorld::ENTTYPE_LASER)
{
m_Pos = Pos;
m_Owner = Owner;
m_Energy = StartEnergy;
if(pGameWorld->m_WorldConfig.m_IsFNG && m_Energy < 10.f)
m_Energy = 800.0f;
m_Dir = Direction;
m_Bounces = 0;
m_EvalTick = 0;
m_TelePos = vec2(0,0);
m_WasTele = false;
m_Type = Type;
GameWorld()->InsertEntity(this);
DoBounce();
}
bool CLaser::HitCharacter(vec2 From, vec2 To)
{
vec2 At;
CCharacter *pOwnerChar = GameWorld()->GetCharacterByID(m_Owner);
CCharacter *pHit;
bool pDontHitSelf = g_Config.m_SvOldLaser || (m_Bounces == 0 && !m_WasTele);
if(pOwnerChar ? (!(pOwnerChar->m_Hit&CCharacter::DISABLE_HIT_RIFLE) && m_Type == WEAPON_RIFLE) || (!(pOwnerChar->m_Hit&CCharacter::DISABLE_HIT_SHOTGUN) && m_Type == WEAPON_SHOTGUN) : g_Config.m_SvHit)
pHit = GameWorld()->IntersectCharacter(m_Pos, To, 0.f, At, pDontHitSelf ? pOwnerChar : 0, m_Owner);
else
pHit = GameWorld()->IntersectCharacter(m_Pos, To, 0.f, At, pDontHitSelf ? pOwnerChar : 0, m_Owner, pOwnerChar);
if(!pHit || (pHit == pOwnerChar && g_Config.m_SvOldLaser) || (pHit != pOwnerChar && pOwnerChar ? (pOwnerChar->m_Hit&CCharacter::DISABLE_HIT_RIFLE && m_Type == WEAPON_RIFLE) || (pOwnerChar->m_Hit&CCharacter::DISABLE_HIT_SHOTGUN && m_Type == WEAPON_SHOTGUN) : !g_Config.m_SvHit))
return false;
m_From = From;
m_Pos = At;
m_Energy = -1;
if (m_Type == WEAPON_SHOTGUN)
{
vec2 Temp;
float Strength = GameWorld()->Tuning()->m_ShotgunStrength;;
if(!g_Config.m_SvOldLaser)
Temp = pHit->Core()->m_Vel + normalize(m_PrevPos - pHit->Core()->m_Pos) * Strength;
else
Temp = pHit->Core()->m_Vel + normalize(pOwnerChar->Core()->m_Pos - pHit->Core()->m_Pos) * Strength;
if(Temp.x > 0 && ((pHit->m_TileIndex == TILE_STOP && pHit->m_TileFlags == ROTATION_270) || (pHit->m_TileIndexL == TILE_STOP && pHit->m_TileFlagsL == ROTATION_270) || (pHit->m_TileIndexL == TILE_STOPS && (pHit->m_TileFlagsL == ROTATION_90 || pHit->m_TileFlagsL ==ROTATION_270)) || (pHit->m_TileIndexL == TILE_STOPA) || (pHit->m_TileFIndex == TILE_STOP && pHit->m_TileFFlags == ROTATION_270) || (pHit->m_TileFIndexL == TILE_STOP && pHit->m_TileFFlagsL == ROTATION_270) || (pHit->m_TileFIndexL == TILE_STOPS && (pHit->m_TileFFlagsL == ROTATION_90 || pHit->m_TileFFlagsL == ROTATION_270)) || (pHit->m_TileFIndexL == TILE_STOPA) || (pHit->m_TileSIndex == TILE_STOP && pHit->m_TileSFlags == ROTATION_270) || (pHit->m_TileSIndexL == TILE_STOP && pHit->m_TileSFlagsL == ROTATION_270) || (pHit->m_TileSIndexL == TILE_STOPS && (pHit->m_TileSFlagsL == ROTATION_90 || pHit->m_TileSFlagsL == ROTATION_270)) || (pHit->m_TileSIndexL == TILE_STOPA)))
Temp.x = 0;
if(Temp.x < 0 && ((pHit->m_TileIndex == TILE_STOP && pHit->m_TileFlags == ROTATION_90) || (pHit->m_TileIndexR == TILE_STOP && pHit->m_TileFlagsR == ROTATION_90) || (pHit->m_TileIndexR == TILE_STOPS && (pHit->m_TileFlagsR == ROTATION_90 || pHit->m_TileFlagsR == ROTATION_270)) || (pHit->m_TileIndexR == TILE_STOPA) || (pHit->m_TileFIndex == TILE_STOP && pHit->m_TileFFlags == ROTATION_90) || (pHit->m_TileFIndexR == TILE_STOP && pHit->m_TileFFlagsR == ROTATION_90) || (pHit->m_TileFIndexR == TILE_STOPS && (pHit->m_TileFFlagsR == ROTATION_90 || pHit->m_TileFFlagsR == ROTATION_270)) || (pHit->m_TileFIndexR == TILE_STOPA) || (pHit->m_TileSIndex == TILE_STOP && pHit->m_TileSFlags == ROTATION_90) || (pHit->m_TileSIndexR == TILE_STOP && pHit->m_TileSFlagsR == ROTATION_90) || (pHit->m_TileSIndexR == TILE_STOPS && (pHit->m_TileSFlagsR == ROTATION_90 || pHit->m_TileSFlagsR == ROTATION_270)) || (pHit->m_TileSIndexR == TILE_STOPA)))
Temp.x = 0;
if(Temp.y < 0 && ((pHit->m_TileIndex == TILE_STOP && pHit->m_TileFlags == ROTATION_180) || (pHit->m_TileIndexB == TILE_STOP && pHit->m_TileFlagsB == ROTATION_180) || (pHit->m_TileIndexB == TILE_STOPS && (pHit->m_TileFlagsB == ROTATION_0 || pHit->m_TileFlagsB == ROTATION_180)) || (pHit->m_TileIndexB == TILE_STOPA) || (pHit->m_TileFIndex == TILE_STOP && pHit->m_TileFFlags == ROTATION_180) || (pHit->m_TileFIndexB == TILE_STOP && pHit->m_TileFFlagsB == ROTATION_180) || (pHit->m_TileFIndexB == TILE_STOPS && (pHit->m_TileFFlagsB == ROTATION_0 || pHit->m_TileFFlagsB == ROTATION_180)) || (pHit->m_TileFIndexB == TILE_STOPA) || (pHit->m_TileSIndex == TILE_STOP && pHit->m_TileSFlags == ROTATION_180) || (pHit->m_TileSIndexB == TILE_STOP && pHit->m_TileSFlagsB == ROTATION_180) || (pHit->m_TileSIndexB == TILE_STOPS && (pHit->m_TileSFlagsB == ROTATION_0 || pHit->m_TileSFlagsB == ROTATION_180)) || (pHit->m_TileSIndexB == TILE_STOPA)))
Temp.y = 0;
if(Temp.y > 0 && ((pHit->m_TileIndex == TILE_STOP && pHit->m_TileFlags == ROTATION_0) || (pHit->m_TileIndexT == TILE_STOP && pHit->m_TileFlagsT == ROTATION_0) || (pHit->m_TileIndexT == TILE_STOPS && (pHit->m_TileFlagsT == ROTATION_0 || pHit->m_TileFlagsT == ROTATION_180)) || (pHit->m_TileIndexT == TILE_STOPA) || (pHit->m_TileFIndex == TILE_STOP && pHit->m_TileFFlags == ROTATION_0) || (pHit->m_TileFIndexT == TILE_STOP && pHit->m_TileFFlagsT == ROTATION_0) || (pHit->m_TileFIndexT == TILE_STOPS && (pHit->m_TileFFlagsT == ROTATION_0 || pHit->m_TileFFlagsT == ROTATION_180)) || (pHit->m_TileFIndexT == TILE_STOPA) || (pHit->m_TileSIndex == TILE_STOP && pHit->m_TileSFlags == ROTATION_0) || (pHit->m_TileSIndexT == TILE_STOP && pHit->m_TileSFlagsT == ROTATION_0) || (pHit->m_TileSIndexT == TILE_STOPS && (pHit->m_TileSFlagsT == ROTATION_0 || pHit->m_TileSFlagsT == ROTATION_180)) || (pHit->m_TileSIndexT == TILE_STOPA)))
Temp.y = 0;
pHit->Core()->m_Vel = Temp;
}
else if (m_Type == WEAPON_RIFLE)
{
pHit->UnFreeze();
}
return true;
}
void CLaser::DoBounce()
{
m_EvalTick = GameWorld()->GameTick();
if(m_Energy < 0)
{
GameWorld()->DestroyEntity(this);
return;
}
m_PrevPos = m_Pos;
vec2 Coltile;
int Res;
int z;
if (m_WasTele)
{
m_PrevPos = m_TelePos;
m_Pos = m_TelePos;
m_TelePos = vec2(0,0);
}
vec2 To = m_Pos + m_Dir * m_Energy;
Res = Collision()->IntersectLineTeleWeapon(m_Pos, To, &Coltile, &To, &z);
if(Res)
{
if(!HitCharacter(m_Pos, To))
{
// intersected
m_From = m_Pos;
m_Pos = To;
vec2 TempPos = m_Pos;
vec2 TempDir = m_Dir * 4.0f;
int f = 0;
if(Res == -1)
{
f = Collision()->GetTile(round_to_int(Coltile.x), round_to_int(Coltile.y));
Collision()->SetCollisionAt(round_to_int(Coltile.x), round_to_int(Coltile.y), TILE_SOLID);
}
Collision()->MovePoint(&TempPos, &TempDir, 1.0f, 0);
if(Res == -1)
{
Collision()->SetCollisionAt(round_to_int(Coltile.x), round_to_int(Coltile.y), f);
}
m_Pos = TempPos;
m_Dir = normalize(TempDir);
m_Energy -= distance(m_From, m_Pos) + GameWorld()->Tuning()->m_LaserBounceCost;
m_Bounces++;
m_WasTele = false;
int BounceNum = GameWorld()->Tuning()->m_LaserBounceNum;
if(m_Bounces > BounceNum)
m_Energy = -1;
}
}
else
{
if(!HitCharacter(m_Pos, To))
{
m_From = m_Pos;
m_Pos = To;
m_Energy = -1;
}
}
}
void CLaser::Tick()
{
float Delay = GameWorld()->Tuning()->m_LaserBounceDelay;
if(GameWorld()->m_WorldConfig.m_IsVanilla) // predict old physics on vanilla 0.6 servers
{
if(GameWorld()->GameTick() > m_EvalTick+(GameWorld()->GameTickSpeed()*Delay/1000.0f))
DoBounce();
}
else
{
if((GameWorld()->GameTick() - m_EvalTick) > (GameWorld()->GameTickSpeed()*Delay/1000.0f))
DoBounce();
}
}
CLaser::CLaser(CGameWorld *pGameWorld, int ID, CNetObj_Laser *pLaser)
: CEntity(pGameWorld, CGameWorld::ENTTYPE_LASER)
{
m_Pos.x = pLaser->m_X;
m_Pos.y = pLaser->m_Y;
m_From.x = pLaser->m_FromX;
m_From.y = pLaser->m_FromY;
m_EvalTick = pLaser->m_StartTick;
m_Owner = -2;
m_Energy = pGameWorld->Tuning()->m_LaserReach;
if(pGameWorld->m_WorldConfig.m_IsFNG && m_Energy < 10.f)
m_Energy = 800.0f;
m_Dir = m_Pos - m_From;
if(length(m_Pos - m_From) > 0.001f)
m_Dir = normalize(m_Dir);
else
m_Energy = 0;
m_Type = WEAPON_RIFLE;
m_PrevPos = m_From;
m_ID = ID;
}
void CLaser::FillInfo(CNetObj_Laser *pLaser)
{
pLaser->m_X = (int)m_Pos.x;
pLaser->m_Y = (int)m_Pos.y;
pLaser->m_FromX = (int)m_From.x;
pLaser->m_FromY = (int)m_From.y;
pLaser->m_StartTick = m_EvalTick;
}
bool CLaser::Match(CLaser *pLaser)
{
if(pLaser->m_EvalTick != m_EvalTick)
return false;
if(distance(pLaser->m_From, m_From) > 2.f)
return false;
const vec2 ThisDiff = m_Pos - m_From;
const vec2 OtherDiff = pLaser->m_Pos - pLaser->m_From;
const float DirError = distance(normalize(OtherDiff)*length(ThisDiff), ThisDiff);
if(DirError > 2.f)
return false;
return true;
}