2 changes: 1 addition & 1 deletion src/game/client/components/scoreboard.cpp
Expand Up @@ -509,7 +509,7 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch
ColorRGBA rgb = color_cast<ColorRGBA>(ColorHSLA((300.0f - clamp(pInfo->m_Latency, 0, 300)) / 1000.0f, 1.0f, 0.5f));
TextRender()->TextColor(rgb);
}
str_format(aBuf, sizeof(aBuf), "%d", clamp(pInfo->m_Latency, 0, 1000));
str_format(aBuf, sizeof(aBuf), "%d", clamp(pInfo->m_Latency, 0, 999));
tw = TextRender()->TextWidth(nullptr, FontSize, aBuf, -1);
TextRender()->SetCursor(&Cursor, PingOffset+PingLength-tw, y + (LineHeight - FontSize) / 2.f, FontSize, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END);
Cursor.m_LineWidth = PingLength;
Expand Down
76 changes: 73 additions & 3 deletions src/game/client/gameclient.cpp
Expand Up @@ -239,6 +239,8 @@ void CGameClient::OnConsoleInit()
Console()->Register("swap_teams", "", CFGFLAG_SERVER, 0, 0, "Swap the current teams");
Console()->Register("shuffle_teams", "", CFGFLAG_SERVER, 0, 0, "Shuffle the current teams");

// register tune zone command to allow the client prediction to load tunezones from the map
Console()->Register("tune_zone", "i[zone] s[tuning] i[value]", CFGFLAG_CLIENT|CFGFLAG_GAME, ConTuneZone, this, "Tune in zone a variable to value");

for(int i = 0; i < m_All.m_Num; i++)
m_All.m_paComponents[i]->m_pClient = this;
Expand Down Expand Up @@ -350,6 +352,7 @@ void CGameClient::OnInit()

m_GameWorld.m_GameTickSpeed = SERVER_TICK_SPEED;
m_GameWorld.m_pCollision = Collision();
m_GameWorld.m_pTuningList = m_aTuningList;

m_pMapimages->SetTextureScale(g_Config.m_ClTextEntitiesSize);
}
Expand Down Expand Up @@ -488,6 +491,7 @@ void CGameClient::OnConnected()
m_GameWorld.m_WorldConfig.m_InfiniteAmmo = true;
for(int i = 0; i < MAX_CLIENTS; i++)
m_aLastWorldCharacters[i].m_Alive = false;
LoadMapSettings();
}

void CGameClient::OnReset()
Expand Down Expand Up @@ -1197,6 +1201,8 @@ void CGameClient::OnNewSnapshot()
const CNetObj_DDNetPlayer *pInfo = (const CNetObj_DDNetPlayer *)pData;
m_aClients[Item.m_ID].m_AuthLevel = pInfo->m_AuthLevel;
m_aClients[Item.m_ID].m_Afk = pInfo->m_Flags & EXPLAYERFLAG_AFK;
m_aClients[Item.m_ID].m_Paused = pInfo->m_Flags & EXPLAYERFLAG_PAUSED;
m_aClients[Item.m_ID].m_Spec = pInfo->m_Flags & EXPLAYERFLAG_SPEC;
}
else if(Item.m_Type == NETOBJTYPE_CHARACTER)
{
Expand Down Expand Up @@ -1757,7 +1763,7 @@ void CGameClient::CClientData::Reset()
m_aName[0] = 0;
m_aClan[0] = 0;
m_Country = -1;
m_SkinID = 0;
m_SkinID = -1;
m_Team = 0;
m_Angle = 0;
m_Emoticon = 0;
Expand All @@ -1768,7 +1774,9 @@ void CGameClient::CClientData::Reset()
m_Foe = false;
m_AuthLevel = AUTHED_NO;
m_Afk = false;
m_SkinInfo.m_Texture = g_GameClient.m_pSkins->Get(0)->m_ColorTexture;
m_Paused = false;
m_Spec = false;
m_SkinInfo.m_Texture = g_GameClient.m_pSkins->Get(-1)->m_OrgTexture;
m_SkinInfo.m_ColorBody = ColorRGBA(1,1,1);
m_SkinInfo.m_ColorFeet = ColorRGBA(1,1,1);

Expand Down Expand Up @@ -1999,7 +2007,12 @@ void CGameClient::UpdatePrediction()
m_GameWorld.m_WorldConfig.m_InfiniteAmmo = false;
m_GameWorld.m_WorldConfig.m_IsSolo = !m_Snap.m_aCharacters[m_Snap.m_LocalClientID].m_HasExtendedData && !m_Tuning[g_Config.m_ClDummy].m_PlayerCollision && !m_Tuning[g_Config.m_ClDummy].m_PlayerHooking;

m_GameWorld.m_Core.m_Tuning[g_Config.m_ClDummy] = m_Tuning[g_Config.m_ClDummy];
// update the tuning/tunezone at the local character position with the latest tunings received before the new snapshot
int TuneZone = Collision()->IsTune(Collision()->GetMapIndex(vec2(m_Snap.m_pLocalCharacter->m_X, m_Snap.m_pLocalCharacter->m_Y)));
if(!TuneZone || !m_GameWorld.m_WorldConfig.m_PredictTiles)
m_GameWorld.m_Tuning[g_Config.m_ClDummy] = m_Tuning[g_Config.m_ClDummy];
else
m_GameWorld.TuningList()[TuneZone] = m_GameWorld.m_Core.m_Tuning[g_Config.m_ClDummy] = m_Tuning[g_Config.m_ClDummy];

// restore characters from previously saved ones if they temporarily left the snapshot
for(int i = 0; i < MAX_CLIENTS; i++)
Expand Down Expand Up @@ -2299,3 +2312,60 @@ bool CGameClient::IsOtherTeam(int ClientID)

return m_Teams.Team(ClientID) != m_Teams.Team(m_Snap.m_LocalClientID);
}

void CGameClient::LoadMapSettings()
{
// Reset Tunezones
CTuningParams TuningParams;
for(int i = 0; i < NUM_TUNEZONES; i++)
{
TuningList()[i] = TuningParams;
TuningList()[i].Set("gun_curvature", 0);
TuningList()[i].Set("gun_speed", 1400);
TuningList()[i].Set("shotgun_curvature", 0);
TuningList()[i].Set("shotgun_speed", 500);
TuningList()[i].Set("shotgun_speeddiff", 0);
}

// Load map tunings
IMap *pMap = Kernel()->RequestInterface<IMap>();
int Start, Num;
pMap->GetType(MAPITEMTYPE_INFO, &Start, &Num);
for(int i = Start; i < Start + Num; i++)
{
int ItemID;
CMapItemInfoSettings *pItem = (CMapItemInfoSettings *)pMap->GetItem(i, 0, &ItemID);
int ItemSize = pMap->GetItemSize(i);
if(!pItem || ItemID != 0)
continue;

if(ItemSize < (int)sizeof(CMapItemInfoSettings))
break;
if(!(pItem->m_Settings > -1))
break;

int Size = pMap->GetDataSize(pItem->m_Settings);
char *pSettings = (char *)pMap->GetData(pItem->m_Settings);
char *pNext = pSettings;
dbg_msg("New tune ","%s", pNext);
while(pNext < pSettings + Size)
{
int StrSize = str_length(pNext) + 1;
Console()->ExecuteLine(pNext, IConsole::CLIENT_ID_GAME);
pNext += StrSize;
}
pMap->UnloadData(pItem->m_Settings);
break;
}
}

void CGameClient::ConTuneZone(IConsole::IResult *pResult, void *pUserData)
{
CGameClient *pSelf = (CGameClient *)pUserData;
int List = pResult->GetInteger(0);
const char *pParamName = pResult->GetString(1);
float NewValue = pResult->GetFloat(2);

if(List >= 0 && List < NUM_TUNEZONES)
pSelf->TuningList()[List].Set(pParamName, NewValue);
}
12 changes: 12 additions & 0 deletions src/game/client/gameclient.h
Expand Up @@ -154,6 +154,8 @@ class CGameClient : public IGameClient
static void ConchainSpecialDummy(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainClTextEntitiesSize(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);

static void ConTuneZone(IConsole::IResult *pResult, void *pUserData);

public:
IKernel *Kernel() { return IInterface::Kernel(); }
IEngine *Engine() const { return m_pEngine; }
Expand Down Expand Up @@ -305,6 +307,8 @@ class CGameClient : public IGameClient

int m_AuthLevel;
bool m_Afk;
bool m_Paused;
bool m_Spec;

void UpdateRenderInfo();
void Reset();
Expand Down Expand Up @@ -465,6 +469,14 @@ class CGameClient : public IGameClient

CCharOrder m_CharOrder;
class CCharacter m_aLastWorldCharacters[MAX_CLIENTS];

enum
{
NUM_TUNEZONES = 256
};
void LoadMapSettings();
CTuningParams m_aTuningList[NUM_TUNEZONES];
CTuningParams *TuningList() { return m_aTuningList; }
};

ColorRGBA CalculateNameColor(ColorHSLA TextColorHSL);
Expand Down
125 changes: 44 additions & 81 deletions src/game/client/prediction/entities/character.cpp
Expand Up @@ -38,15 +38,11 @@ bool CCharacter::IsGrounded()
if(Collision()->CheckPoint(m_Pos.x-m_ProximityRadius/2, m_Pos.y+m_ProximityRadius/2+5))
return true;

int index = Collision()->GetPureMapIndex(vec2(m_Pos.x, m_Pos.y+m_ProximityRadius/2+4));
int tile = Collision()->GetTileIndex(index);
int flags = Collision()->GetTileFlags(index);
if(tile == TILE_STOPA || (tile == TILE_STOP && flags == ROTATION_0) || (tile ==TILE_STOPS && (flags == ROTATION_0 || flags == ROTATION_180)))
return true;
tile = Collision()->GetFTileIndex(index);
flags = Collision()->GetFTileFlags(index);
if(tile == TILE_STOPA || (tile == TILE_STOP && flags == ROTATION_0) || (tile ==TILE_STOPS && (flags == ROTATION_0 || flags == ROTATION_180)))
int MoveRestrictionsBelow = Collision()->GetMoveRestrictions(m_Pos + vec2(0, m_ProximityRadius / 2 + 4), 0.0f);
if(MoveRestrictionsBelow&CANTMOVE_DOWN)
{
return true;
}

return false;
}
Expand Down Expand Up @@ -87,7 +83,9 @@ void CCharacter::HandleJetpack()
{
if (m_Jetpack)
{
float Strength = m_LastJetpackStrength;
float Strength = GetTuning(m_TuneZone)->m_JetpackStrength;
if(!m_TuneZone)
Strength = m_LastJetpackStrength;
TakeDamage(Direction * -1.0f * (Strength / 100.0f / 6.11f), g_pData->m_Weapons.m_Hammer.m_pBase->m_Damage, GetCID(), m_Core.m_ActiveWeapon);
}
}
Expand Down Expand Up @@ -324,10 +322,10 @@ void CCharacter::FireWeapon()
else
Dir = vec2(0.f, -1.f);

float Strength = Tuning()->m_HammerStrength;
float Strength = GetTuning(m_TuneZone)->m_HammerStrength;

vec2 Temp = pTarget->m_Core.m_Vel + normalize(Dir + vec2(0.f, -1.1f)) * 10.0f;
pTarget->Core()->LimitForce(&Temp);
Temp = pTarget->Core()->LimitVel(Temp);
Temp -= pTarget->m_Core.m_Vel;

vec2 Force = vec2(0.f, -1.0f) + Temp;
Expand Down Expand Up @@ -365,8 +363,8 @@ void CCharacter::FireWeapon()
{
if (!m_Jetpack)
{
int Lifetime;
Lifetime = (int)(GameWorld()->GameTickSpeed()*Tuning()->m_GunLifetime);
int Lifetime = (int)(GameWorld()->GameTickSpeed()*GetTuning(m_TuneZone)->m_GunLifetime);

new CProjectile
(
GameWorld(),
Expand All @@ -388,23 +386,22 @@ void CCharacter::FireWeapon()
{
if(GameWorld()->m_WorldConfig.m_IsVanilla)
{

int ShotSpread = 2;
for(int i = -ShotSpread; i <= ShotSpread; ++i)
{
float Spreading[] = {-0.185f, -0.070f, 0, 0.070f, 0.185f};
float a = GetAngle(Direction);
a += Spreading[i+2];
float v = 1-(absolute(i)/(float)ShotSpread);
float Speed = mix((float)GameWorld()->Tuning()->m_ShotgunSpeeddiff, 1.0f, v);
float Speed = mix((float)Tuning()->m_ShotgunSpeeddiff, 1.0f, v);
new CProjectile
(
GameWorld(),
WEAPON_SHOTGUN,//Type
GetCID(),//Owner
ProjStartPos,//Pos
vec2(cosf(a), sinf(a))*Speed,//Dir
(int)(GameWorld()->GameTickSpeed()*GameWorld()->Tuning()->m_ShotgunLifetime),//Span
(int)(GameWorld()->GameTickSpeed()*Tuning()->m_ShotgunLifetime),//Span
0,//Freeze
0,//Explosive
0,//Force
Expand All @@ -415,14 +412,16 @@ void CCharacter::FireWeapon()
}
else if(GameWorld()->m_WorldConfig.m_IsDDRace)
{
float LaserReach = Tuning()->m_LaserReach;
float LaserReach = GetTuning(m_TuneZone)->m_LaserReach;

new CLaser(GameWorld(), m_Pos, Direction, LaserReach, GetCID(), WEAPON_SHOTGUN);
}
} break;

case WEAPON_GRENADE:
{
int Lifetime = (int)(GameWorld()->GameTickSpeed()*Tuning()->m_GrenadeLifetime);
int Lifetime = (int)(GameWorld()->GameTickSpeed()*GetTuning(m_TuneZone)->m_GrenadeLifetime);

new CProjectile
(
GameWorld(),
Expand All @@ -441,7 +440,8 @@ void CCharacter::FireWeapon()

case WEAPON_RIFLE:
{
float LaserReach = Tuning()->m_LaserReach;
float LaserReach = GetTuning(m_TuneZone)->m_LaserReach;

new CLaser(GameWorld(), m_Pos, Direction, LaserReach, GetCID(), WEAPON_RIFLE);
} break;

Expand All @@ -462,7 +462,8 @@ void CCharacter::FireWeapon()
if(!m_ReloadTimer)
{
float FireDelay;
Tuning()->Get(38 + m_Core.m_ActiveWeapon, &FireDelay);
GetTuning(m_TuneZone)->Get(38 + m_Core.m_ActiveWeapon, &FireDelay);

m_ReloadTimer = FireDelay * GameWorld()->GameTickSpeed() / 1000;
}
}
Expand Down Expand Up @@ -648,79 +649,29 @@ void CCharacter::HandleSkippableTiles(int Index)
}
else
TempVel += Direction * Force;
m_Core.LimitForce(&TempVel);
m_Core.m_Vel = TempVel;
m_Core.m_Vel = m_Core.LimitVel(TempVel);
}
}
}

bool CCharacter::IsSwitchActiveCb(int Number, void *pUser)
{
CCharacter *pThis = (CCharacter *)pUser;
CCollision *pCollision = pThis->Collision();
return pCollision->m_pSwitchers && pCollision->m_pSwitchers[Number].m_Status[pThis->Team()] && pThis->Team() != TEAM_SUPER;
}

void CCharacter::HandleTiles(int Index)
{
int MapIndex = Index;
float Offset = 4.0f;
int MapIndexL = Collision()->GetPureMapIndex(vec2(m_Pos.x + (m_ProximityRadius / 2) + Offset, m_Pos.y));
int MapIndexR = Collision()->GetPureMapIndex(vec2(m_Pos.x - (m_ProximityRadius / 2) - Offset, m_Pos.y));
int MapIndexT = Collision()->GetPureMapIndex(vec2(m_Pos.x, m_Pos.y + (m_ProximityRadius / 2) + Offset));
int MapIndexB = Collision()->GetPureMapIndex(vec2(m_Pos.x, m_Pos.y - (m_ProximityRadius / 2) - Offset));
m_TileIndex = Collision()->GetTileIndex(MapIndex);
m_TileFlags = Collision()->GetTileFlags(MapIndex);
m_TileIndexL = Collision()->GetTileIndex(MapIndexL);
m_TileFlagsL = Collision()->GetTileFlags(MapIndexL);
m_TileIndexR = Collision()->GetTileIndex(MapIndexR);
m_TileFlagsR = Collision()->GetTileFlags(MapIndexR);
m_TileIndexB = Collision()->GetTileIndex(MapIndexB);
m_TileFlagsB = Collision()->GetTileFlags(MapIndexB);
m_TileIndexT = Collision()->GetTileIndex(MapIndexT);
m_TileFlagsT = Collision()->GetTileFlags(MapIndexT);
m_TileFIndex = Collision()->GetFTileIndex(MapIndex);
m_TileFFlags = Collision()->GetFTileFlags(MapIndex);
m_TileFIndexL = Collision()->GetFTileIndex(MapIndexL);
m_TileFFlagsL = Collision()->GetFTileFlags(MapIndexL);
m_TileFIndexR = Collision()->GetFTileIndex(MapIndexR);
m_TileFFlagsR = Collision()->GetFTileFlags(MapIndexR);
m_TileFIndexB = Collision()->GetFTileIndex(MapIndexB);
m_TileFFlagsB = Collision()->GetFTileFlags(MapIndexB);
m_TileFIndexT = Collision()->GetFTileIndex(MapIndexT);
m_TileFFlagsT = Collision()->GetFTileFlags(MapIndexT);//
m_TileSIndex = (Collision()->m_pSwitchers && Collision()->m_pSwitchers[Collision()->GetDTileNumber(MapIndex)].m_Status[Team()])?(Team() != TEAM_SUPER)? Collision()->GetDTileIndex(MapIndex) : 0 : 0;
m_TileSFlags = (Collision()->m_pSwitchers && Collision()->m_pSwitchers[Collision()->GetDTileNumber(MapIndex)].m_Status[Team()])?(Team() != TEAM_SUPER)? Collision()->GetDTileFlags(MapIndex) : 0 : 0;
m_TileSIndexL = (Collision()->m_pSwitchers && Collision()->m_pSwitchers[Collision()->GetDTileNumber(MapIndexL)].m_Status[Team()])?(Team() != TEAM_SUPER)? Collision()->GetDTileIndex(MapIndexL) : 0 : 0;
m_TileSFlagsL = (Collision()->m_pSwitchers && Collision()->m_pSwitchers[Collision()->GetDTileNumber(MapIndexL)].m_Status[Team()])?(Team() != TEAM_SUPER)? Collision()->GetDTileFlags(MapIndexL) : 0 : 0;
m_TileSIndexR = (Collision()->m_pSwitchers && Collision()->m_pSwitchers[Collision()->GetDTileNumber(MapIndexR)].m_Status[Team()])?(Team() != TEAM_SUPER)? Collision()->GetDTileIndex(MapIndexR) : 0 : 0;
m_TileSFlagsR = (Collision()->m_pSwitchers && Collision()->m_pSwitchers[Collision()->GetDTileNumber(MapIndexR)].m_Status[Team()])?(Team() != TEAM_SUPER)? Collision()->GetDTileFlags(MapIndexR) : 0 : 0;
m_TileSIndexB = (Collision()->m_pSwitchers && Collision()->m_pSwitchers[Collision()->GetDTileNumber(MapIndexB)].m_Status[Team()])?(Team() != TEAM_SUPER)? Collision()->GetDTileIndex(MapIndexB) : 0 : 0;
m_TileSFlagsB = (Collision()->m_pSwitchers && Collision()->m_pSwitchers[Collision()->GetDTileNumber(MapIndexB)].m_Status[Team()])?(Team() != TEAM_SUPER)? Collision()->GetDTileFlags(MapIndexB) : 0 : 0;
m_TileSIndexT = (Collision()->m_pSwitchers && Collision()->m_pSwitchers[Collision()->GetDTileNumber(MapIndexT)].m_Status[Team()])?(Team() != TEAM_SUPER)? Collision()->GetDTileIndex(MapIndexT) : 0 : 0;
m_TileSFlagsT = (Collision()->m_pSwitchers && Collision()->m_pSwitchers[Collision()->GetDTileNumber(MapIndexT)].m_Status[Team()])?(Team() != TEAM_SUPER)? Collision()->GetDTileFlags(MapIndexT) : 0 : 0;
m_MoveRestrictions = Collision()->GetMoveRestrictions(IsSwitchActiveCb, this, m_Pos);

// stopper
if(((m_TileIndex == TILE_STOP && m_TileFlags == ROTATION_270) || (m_TileIndexL == TILE_STOP && m_TileFlagsL == ROTATION_270) || (m_TileIndexL == TILE_STOPS && (m_TileFlagsL == ROTATION_90 || m_TileFlagsL ==ROTATION_270)) || (m_TileIndexL == TILE_STOPA) || (m_TileFIndex == TILE_STOP && m_TileFFlags == ROTATION_270) || (m_TileFIndexL == TILE_STOP && m_TileFFlagsL == ROTATION_270) || (m_TileFIndexL == TILE_STOPS && (m_TileFFlagsL == ROTATION_90 || m_TileFFlagsL == ROTATION_270)) || (m_TileFIndexL == TILE_STOPA) || (m_TileSIndex == TILE_STOP && m_TileSFlags == ROTATION_270) || (m_TileSIndexL == TILE_STOP && m_TileSFlagsL == ROTATION_270) || (m_TileSIndexL == TILE_STOPS && (m_TileSFlagsL == ROTATION_90 || m_TileSFlagsL == ROTATION_270)) || (m_TileSIndexL == TILE_STOPA)) && m_Core.m_Vel.x > 0)
m_Core.m_Vel = ClampVel(m_MoveRestrictions, m_Core.m_Vel);
if(m_MoveRestrictions&CANTMOVE_DOWN)
{
if((int)Collision()->GetPos(MapIndexL).x)
if((int)Collision()->GetPos(MapIndexL).x < (int)m_Core.m_Pos.x)
m_Core.m_Pos = m_PrevPos;
m_Core.m_Vel.x = 0;
}
if(((m_TileIndex == TILE_STOP && m_TileFlags == ROTATION_90) || (m_TileIndexR == TILE_STOP && m_TileFlagsR == ROTATION_90) || (m_TileIndexR == TILE_STOPS && (m_TileFlagsR == ROTATION_90 || m_TileFlagsR == ROTATION_270)) || (m_TileIndexR == TILE_STOPA) || (m_TileFIndex == TILE_STOP && m_TileFFlags == ROTATION_90) || (m_TileFIndexR == TILE_STOP && m_TileFFlagsR == ROTATION_90) || (m_TileFIndexR == TILE_STOPS && (m_TileFFlagsR == ROTATION_90 || m_TileFFlagsR == ROTATION_270)) || (m_TileFIndexR == TILE_STOPA) || (m_TileSIndex == TILE_STOP && m_TileSFlags == ROTATION_90) || (m_TileSIndexR == TILE_STOP && m_TileSFlagsR == ROTATION_90) || (m_TileSIndexR == TILE_STOPS && (m_TileSFlagsR == ROTATION_90 || m_TileSFlagsR == ROTATION_270)) || (m_TileSIndexR == TILE_STOPA)) && m_Core.m_Vel.x < 0)
{
if((int)Collision()->GetPos(MapIndexR).x)
if((int)Collision()->GetPos(MapIndexR).x > (int)m_Core.m_Pos.x)
m_Core.m_Pos = m_PrevPos;
m_Core.m_Vel.x = 0;
}
if(((m_TileIndex == TILE_STOP && m_TileFlags == ROTATION_180) || (m_TileIndexB == TILE_STOP && m_TileFlagsB == ROTATION_180) || (m_TileIndexB == TILE_STOPS && (m_TileFlagsB == ROTATION_0 || m_TileFlagsB == ROTATION_180)) || (m_TileIndexB == TILE_STOPA) || (m_TileFIndex == TILE_STOP && m_TileFFlags == ROTATION_180) || (m_TileFIndexB == TILE_STOP && m_TileFFlagsB == ROTATION_180) || (m_TileFIndexB == TILE_STOPS && (m_TileFFlagsB == ROTATION_0 || m_TileFFlagsB == ROTATION_180)) || (m_TileFIndexB == TILE_STOPA) || (m_TileSIndex == TILE_STOP && m_TileSFlags == ROTATION_180) || (m_TileSIndexB == TILE_STOP && m_TileSFlagsB == ROTATION_180) || (m_TileSIndexB == TILE_STOPS && (m_TileSFlagsB == ROTATION_0 || m_TileSFlagsB == ROTATION_180)) || (m_TileSIndexB == TILE_STOPA)) && m_Core.m_Vel.y < 0)
{
if((int)Collision()->GetPos(MapIndexB).y)
if((int)Collision()->GetPos(MapIndexB).y > (int)m_Core.m_Pos.y)
m_Core.m_Pos = m_PrevPos;
m_Core.m_Vel.y = 0;
}
if(((m_TileIndex == TILE_STOP && m_TileFlags == ROTATION_0) || (m_TileIndexT == TILE_STOP && m_TileFlagsT == ROTATION_0) || (m_TileIndexT == TILE_STOPS && (m_TileFlagsT == ROTATION_0 || m_TileFlagsT == ROTATION_180)) || (m_TileIndexT == TILE_STOPA) || (m_TileFIndex == TILE_STOP && m_TileFFlags == ROTATION_0) || (m_TileFIndexT == TILE_STOP && m_TileFFlagsT == ROTATION_0) || (m_TileFIndexT == TILE_STOPS && (m_TileFFlagsT == ROTATION_0 || m_TileFFlagsT == ROTATION_180)) || (m_TileFIndexT == TILE_STOPA) || (m_TileSIndex == TILE_STOP && m_TileSFlags == ROTATION_0) || (m_TileSIndexT == TILE_STOP && m_TileSFlagsT == ROTATION_0) || (m_TileSIndexT == TILE_STOPS && (m_TileSFlagsT == ROTATION_0 || m_TileSFlagsT == ROTATION_180)) || (m_TileSIndexT == TILE_STOPA)) && m_Core.m_Vel.y > 0)
{
if((int)Collision()->GetPos(MapIndexT).y)
if((int)Collision()->GetPos(MapIndexT).y < (int)m_Core.m_Pos.y)
m_Core.m_Pos = m_PrevPos;
m_Core.m_Vel.y = 0;
m_Core.m_Jumped = 0;
m_Core.m_JumpedTotal = 0;
}
Expand Down Expand Up @@ -886,6 +837,14 @@ void CCharacter::HandleTiles(int Index)
}
}

void CCharacter::HandleTuneLayer()
{
int CurrentIndex = Collision()->GetMapIndex(m_Pos);
m_TuneZone = GameWorld()->m_WorldConfig.m_PredictTiles ? Collision()->IsTune(CurrentIndex) : 0;

m_Core.m_pWorld->m_Tuning[g_Config.m_ClDummy] = *GetTuning(m_TuneZone); // throw tunings (from specific zone if in a tunezone) into gamecore
}

void CCharacter::DDRaceTick()
{
mem_copy(&m_Input, &m_SavedInput, sizeof(m_Input));
Expand All @@ -904,6 +863,8 @@ void CCharacter::DDRaceTick()
if (m_FreezeTime == 1)
UnFreeze();
}

HandleTuneLayer();
}

void CCharacter::DDRacePostCoreTick()
Expand Down Expand Up @@ -1185,6 +1146,8 @@ void CCharacter::Read(CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtende
m_LastSnapWeapon = pChar->m_Weapon;
m_Alive = true;

m_TuneZone = GameWorld()->m_WorldConfig.m_PredictTiles ? Collision()->IsTune(Collision()->GetMapIndex(m_Pos)) : 0;

// set the current weapon
if(pChar->m_Weapon != WEAPON_NINJA)
{
Expand Down
39 changes: 8 additions & 31 deletions src/game/client/prediction/entities/character.h
Expand Up @@ -88,49 +88,24 @@ class CCharacter : public CEntity
DISABLE_HIT_RIFLE=8
};
int m_Hit;
int m_TuneZone;
vec2 m_PrevPos;
vec2 m_PrevPrevPos;
int m_TeleCheckpoint;

int m_TileIndex;
int m_TileFlags;
int m_TileFIndex;
int m_TileFFlags;
int m_TileSIndex;
int m_TileSFlags;
int m_TileIndexL;
int m_TileFlagsL;
int m_TileFIndexL;
int m_TileFFlagsL;
int m_TileSIndexL;
int m_TileSFlagsL;
int m_TileIndexR;
int m_TileFlagsR;
int m_TileFIndexR;
int m_TileFFlagsR;
int m_TileSIndexR;
int m_TileSFlagsR;
int m_TileIndexT;
int m_TileFlagsT;
int m_TileFIndexT;
int m_TileFFlagsT;
int m_TileSIndexT;
int m_TileSFlagsT;
int m_TileIndexB;
int m_TileFlagsB;
int m_TileFIndexB;
int m_TileFFlagsB;
int m_TileSIndexB;
int m_TileSFlagsB;

int m_MoveRestrictions;
bool m_LastRefillJumps;

// Setters/Getters because i don't want to modify vanilla vars access modifiers
int GetLastWeapon() { return m_LastWeapon; };
void SetLastWeapon(int LastWeap) {m_LastWeapon = LastWeap; };
void SetLastWeapon(int LastWeap) { m_LastWeapon = LastWeap; };
int GetActiveWeapon() { return m_Core.m_ActiveWeapon; };
void SetActiveWeapon(int ActiveWeap) {m_Core.m_ActiveWeapon = ActiveWeap; };
void SetActiveWeapon(int ActiveWeap) { m_Core.m_ActiveWeapon = ActiveWeap; };
CCharacterCore GetCore() { return m_Core; };
void SetCore(CCharacterCore Core) {m_Core = Core; };
void SetCore(CCharacterCore Core) { m_Core = Core; };
CCharacterCore* Core() { return &m_Core; };
bool GetWeaponGot(int Type) { return m_aWeapons[Type].m_Got; };
void SetWeaponGot(int Type, bool Value) { m_aWeapons[Type].m_Got = Value; };
Expand Down Expand Up @@ -203,10 +178,12 @@ class CCharacter : public CEntity

// DDRace

static bool IsSwitchActiveCb(int Number, void *pUser);
void HandleTiles(int Index);
void HandleSkippableTiles(int Index);
void DDRaceTick();
void DDRacePostCoreTick();
void HandleTuneLayer();

int m_StrongWeakID;
};
Expand Down
22 changes: 8 additions & 14 deletions src/game/client/prediction/entities/laser.cpp
Expand Up @@ -20,6 +20,7 @@ CLaser::CLaser(CGameWorld *pGameWorld, vec2 Pos, vec2 Direction, float StartEner
m_TelePos = vec2(0,0);
m_WasTele = false;
m_Type = Type;
m_TuneZone = GameWorld()->m_WorldConfig.m_PredictTiles ? Collision()->IsTune(Collision()->GetMapIndex(m_Pos)) : 0;
GameWorld()->InsertEntity(this);
DoBounce();
}
Expand All @@ -45,21 +46,13 @@ bool CLaser::HitCharacter(vec2 From, vec2 To)
{
vec2 Temp;

float Strength = GameWorld()->Tuning()->m_ShotgunStrength;;
float Strength = GetTuning(m_TuneZone)->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;
pHit->Core()->m_Vel = ClampVel(pHit->m_MoveRestrictions, Temp);
}
else if (m_Type == WEAPON_RIFLE)
{
Expand Down Expand Up @@ -119,12 +112,12 @@ void CLaser::DoBounce()
m_Pos = TempPos;
m_Dir = normalize(TempDir);

m_Energy -= distance(m_From, m_Pos) + GameWorld()->Tuning()->m_LaserBounceCost;
m_Energy -= distance(m_From, m_Pos) + GetTuning(m_TuneZone)->m_LaserBounceCost;

m_Bounces++;
m_WasTele = false;

int BounceNum = GameWorld()->Tuning()->m_LaserBounceNum;
int BounceNum = GetTuning(m_TuneZone)->m_LaserBounceNum;

if(m_Bounces > BounceNum)
m_Energy = -1;
Expand All @@ -143,7 +136,7 @@ void CLaser::DoBounce()

void CLaser::Tick()
{
float Delay = GameWorld()->Tuning()->m_LaserBounceDelay;
float Delay = GetTuning(m_TuneZone)->m_LaserBounceDelay;

if(GameWorld()->m_WorldConfig.m_IsVanilla) // predict old physics on vanilla 0.6 servers
{
Expand All @@ -165,8 +158,9 @@ CLaser::CLaser(CGameWorld *pGameWorld, int ID, CNetObj_Laser *pLaser)
m_From.x = pLaser->m_FromX;
m_From.y = pLaser->m_FromY;
m_EvalTick = pLaser->m_StartTick;
m_TuneZone = GameWorld()->m_WorldConfig.m_PredictTiles ? Collision()->IsTune(Collision()->GetMapIndex(m_Pos)) : 0;
m_Owner = -2;
m_Energy = pGameWorld->Tuning()->m_LaserReach;
m_Energy = GetTuning(m_TuneZone)->m_LaserReach;
if(pGameWorld->m_WorldConfig.m_IsFNG && m_Energy < 10.f)
m_Energy = 800.0f;

Expand Down
1 change: 1 addition & 0 deletions src/game/client/prediction/entities/laser.h
Expand Up @@ -38,6 +38,7 @@ class CLaser : public CEntity

vec2 m_PrevPos;
int m_Type;
int m_TuneZone;
};

#endif
22 changes: 13 additions & 9 deletions src/game/client/prediction/entities/projectile.cpp
Expand Up @@ -38,29 +38,32 @@ CProjectile::CProjectile
m_Number = Number;
m_Freeze = Freeze;

m_TuneZone = GameWorld()->m_WorldConfig.m_PredictTiles ? Collision()->IsTune(Collision()->GetMapIndex(m_Pos)) : 0;

GameWorld()->InsertEntity(this);
}

vec2 CProjectile::GetPos(float Time)
{
float Curvature = 0;
float Speed = 0;
CTuningParams *pTuning = GetTuning(m_TuneZone);

switch(m_Type)
{
case WEAPON_GRENADE:
Curvature = Tuning()->m_GrenadeCurvature;
Speed = Tuning()->m_GrenadeSpeed;
Curvature = pTuning->m_GrenadeCurvature;
Speed = pTuning->m_GrenadeSpeed;
break;

case WEAPON_SHOTGUN:
Curvature = Tuning()->m_ShotgunCurvature;
Speed = Tuning()->m_ShotgunSpeed;
Curvature = pTuning->m_ShotgunCurvature;
Speed = pTuning->m_ShotgunSpeed;
break;

case WEAPON_GUN:
Curvature = Tuning()->m_GunCurvature;
Speed = Tuning()->m_GunSpeed;
Curvature = pTuning->m_GunCurvature;
Speed = pTuning->m_GunSpeed;
break;
}

Expand Down Expand Up @@ -169,18 +172,19 @@ CProjectile::CProjectile(CGameWorld *pGameWorld, int ID, CNetObj_Projectile *pPr
}
m_Type = m_Weapon = pProj->m_Type;
m_StartTick = pProj->m_StartTick;
m_TuneZone = GameWorld()->m_WorldConfig.m_PredictTiles ? Collision()->IsTune(Collision()->GetMapIndex(m_Pos)) : 0;

int Lifetime = 20 * GameWorld()->GameTickSpeed();
m_SoundImpact = -1;
if(m_Weapon == WEAPON_GRENADE)
{
Lifetime = pGameWorld->Tuning()->m_GrenadeLifetime * GameWorld()->GameTickSpeed();
Lifetime = GetTuning(m_TuneZone)->m_GrenadeLifetime * GameWorld()->GameTickSpeed();
m_SoundImpact = SOUND_GRENADE_EXPLODE;
}
else if(m_Weapon == WEAPON_GUN)
Lifetime = pGameWorld->Tuning()->m_GunLifetime * GameWorld()->GameTickSpeed();
Lifetime = GetTuning(m_TuneZone)->m_GunLifetime * GameWorld()->GameTickSpeed();
else if(m_Weapon == WEAPON_SHOTGUN && !GameWorld()->m_WorldConfig.m_IsDDRace)
Lifetime = pGameWorld->Tuning()->m_ShotgunLifetime * GameWorld()->GameTickSpeed();
Lifetime = GetTuning(m_TuneZone)->m_ShotgunLifetime * GameWorld()->GameTickSpeed();
m_LifeSpan = Lifetime - (pGameWorld->GameTick() - m_StartTick);
m_ID = ID;
}
Expand Down
1 change: 1 addition & 0 deletions src/game/client/prediction/entities/projectile.h
Expand Up @@ -59,6 +59,7 @@ class CProjectile : public CEntity

int m_Bouncing;
bool m_Freeze;
int m_TuneZone;
};

#endif
2 changes: 2 additions & 0 deletions src/game/client/prediction/entity.h
Expand Up @@ -40,6 +40,8 @@ class CEntity

class CGameWorld *GameWorld() { return m_pGameWorld; }
CTuningParams *Tuning() { return GameWorld()->Tuning(); }
CTuningParams *TuningList() { return GameWorld()->TuningList(); }
CTuningParams *GetTuning(int i) { return GameWorld()->GetTuning(i); }
class CCollision *Collision() { return GameWorld()->Collision(); }
CEntity *TypeNext() { return m_pNextTypeEntity; }
CEntity *TypePrev() { return m_pPrevTypeEntity; }
Expand Down
9 changes: 8 additions & 1 deletion src/game/client/prediction/gameworld.cpp
Expand Up @@ -223,6 +223,9 @@ CCharacter *CGameWorld::IntersectCharacter(vec2 Pos0, vec2 Pos1, float Radius, v
if(p == pNotThis)
continue;

if(pThisOnly && p != pThisOnly)
continue;

if(CollideWith != -1 && !p->CanCollide(CollideWith))
continue;

Expand Down Expand Up @@ -281,7 +284,7 @@ void CGameWorld::ReleaseHooked(int ClientID)

CTuningParams *CGameWorld::Tuning()
{
return &m_Core.m_Tuning[g_Config.m_ClDummy];
return &m_Tuning[g_Config.m_ClDummy];
}

CEntity *CGameWorld::GetEntity(int ID, int EntType)
Expand Down Expand Up @@ -518,7 +521,11 @@ void CGameWorld::CopyWorld(CGameWorld *pFrom)
m_pCollision = pFrom->m_pCollision;
m_WorldConfig = pFrom->m_WorldConfig;
for(int i = 0; i < 2; i++)
{
m_Core.m_Tuning[i] = pFrom->m_Core.m_Tuning[i];
m_Tuning[i] = pFrom->m_Tuning[i];
}
m_pTuningList = pFrom->m_pTuningList;
m_Teams = pFrom->m_Teams;
// delete the previous entities
for(int i = 0; i < NUM_ENTTYPES; i++)
Expand Down
5 changes: 5 additions & 0 deletions src/game/client/prediction/gameworld.h
Expand Up @@ -88,6 +88,11 @@ class CGameWorld
CEntity *FindMatch(int ObjID, int ObjType, const void *pObjData);
void Clear();

CTuningParams m_Tuning[2];
CTuningParams *m_pTuningList;
CTuningParams *TuningList() { return m_pTuningList; }
CTuningParams *GetTuning(int i) { return i == 0 ? Tuning() : &TuningList()[i]; }

private:
void RemoveEntities();

Expand Down
148 changes: 148 additions & 0 deletions src/game/collision.cpp
Expand Up @@ -14,6 +14,27 @@

#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;
Expand Down Expand Up @@ -131,6 +152,133 @@ void CCollision::Init(class CLayers *pLayers)
}
}

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)
{
if(!m_pTiles)
Expand Down
18 changes: 18 additions & 0 deletions src/game/collision.h
Expand Up @@ -8,6 +8,18 @@

#include <list>

enum
{
CANTMOVE_LEFT=1<<0,
CANTMOVE_RIGHT=1<<1,
CANTMOVE_UP=1<<2,
CANTMOVE_DOWN=1<<3,
};

vec2 ClampVel(int MoveRestriction, vec2 Vel);

typedef bool (*CALLBACK_SWITCHACTIVE)(int Number, void *pUser);

class CCollision
{
class CTile *m_pTiles;
Expand Down Expand Up @@ -48,6 +60,12 @@ class CCollision
int GetIndex(vec2 PrevPos, vec2 Pos);
int GetFIndex(int x, int y);

int GetMoveRestrictions(CALLBACK_SWITCHACTIVE pfnSwitchActive, void *pUser, vec2 Pos, float Distance = 18.0f, int OverrideCenterTileIndex = -1);
int GetMoveRestrictions(vec2 Pos, float Distance = 18.0f)
{
return GetMoveRestrictions(0, 0, Pos, Distance);
}

int GetTile(int x, int y);
int GetFTile(int x, int y);
int Entity(int x, int y, int Layer);
Expand Down
34 changes: 16 additions & 18 deletions src/game/editor/editor.cpp
Expand Up @@ -828,6 +828,12 @@ int CEditor::UiDoValueSelector(void *pID, CUIRect *pRect, const char *pLabel, in
s_Value = fmod(s_Value, Scale);
Current += Step*Count;
Current = clamp(Current, Min, Max);

// Constrain to discrete steps
if(Count > 0)
Current = Current / Step * Step;
else
Current = round_ceil(Current / (float)Step) * Step;
}
}
if(pToolTip && !s_TextMode)
Expand Down Expand Up @@ -3045,30 +3051,22 @@ int CEditor::DoProperties(CUIRect *pToolBox, CProperty *pProps, int *pIDs, int *
Shifter.VSplitRight(10.0f, &Shifter, &Inc);
Shifter.VSplitLeft(10.0f, &Dec, &Shifter);
bool Shift = Input()->KeyIsPressed(KEY_LSHIFT) || Input()->KeyIsPressed(KEY_RSHIFT);
int Step = Shift ? 1 : 45;
int Value = pProps[i].m_Value;
const void *activeItem = UI()->ActiveItem();
if(!Shift && UI()->MouseButton(0) && (activeItem == &pIDs[i] || activeItem == &pIDs[i]+1 || activeItem == &pIDs[i]+2))
Value = (Value / 45) * 45;
int NewValue = UiDoValueSelector(&pIDs[i], &Shifter, "", Value, pProps[i].m_Min, Shift ? pProps[i].m_Max : 315, Shift ? 1 : 45, Shift ? 1.0f : 10.0f, "Use left mouse button to drag and change the value. Hold shift to be more precise. Rightclick to edit as text.", false, false, 0);
if(NewValue != pProps[i].m_Value)
{
*pNewVal = NewValue;
Change = i;
}

int NewValue = UiDoValueSelector(&pIDs[i], &Shifter, "", Value, pProps[i].m_Min, pProps[i].m_Max, Shift ? 1 : 45, Shift ? 1.0f : 10.0f, "Use left mouse button to drag and change the value. Hold shift to be more precise. Rightclick to edit as text.", false, false, 0);
if(DoButton_ButtonDec(&pIDs[i]+1, 0, 0, &Dec, 0, "Decrease"))
{
if(!Shift)
*pNewVal = pProps[i].m_Value - 45;
else
*pNewVal = pProps[i].m_Value - 1;
Change = i;
NewValue = (round_ceil((pProps[i].m_Value / (float)Step)) - 1) * Step;
if(NewValue < 0)
NewValue += 360;
}
if(DoButton_ButtonInc(&pIDs[i]+ 2, 0, 0, &Inc, 0, "Increase"))
NewValue = (pProps[i].m_Value + Step) / Step * Step;

if(NewValue != pProps[i].m_Value)
{
if(!Shift)
*pNewVal = pProps[i].m_Value + 45;
else
*pNewVal = pProps[i].m_Value + 1;
*pNewVal = NewValue % 360;
Change = i;
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/game/editor/editor.h
Expand Up @@ -1094,9 +1094,9 @@ class CLayerSpeedup : public CLayerTiles
~CLayerSpeedup();

CSpeedupTile *m_pSpeedupTile;
unsigned char m_SpeedupForce;
unsigned char m_SpeedupMaxSpeed;
unsigned char m_SpeedupAngle;
int m_SpeedupForce;
int m_SpeedupMaxSpeed;
int m_SpeedupAngle;

virtual void Resize(int NewW, int NewH);
virtual void Shift(int Direction);
Expand Down
85 changes: 12 additions & 73 deletions src/game/gamecore.cpp
Expand Up @@ -124,41 +124,7 @@ void CCharacterCore::Reset()
void CCharacterCore::Tick(bool UseInput)
{
float PhysSize = 28.0f;
int MapIndex = Collision()->GetPureMapIndex(m_Pos);
int MapIndexL = Collision()->GetPureMapIndex(vec2(m_Pos.x + (28/2)+4,m_Pos.y));
int MapIndexR = Collision()->GetPureMapIndex(vec2(m_Pos.x - (28/2)-4,m_Pos.y));
int MapIndexT = Collision()->GetPureMapIndex(vec2(m_Pos.x,m_Pos.y + (28/2)+4));
int MapIndexB = Collision()->GetPureMapIndex(vec2(m_Pos.x,m_Pos.y - (28/2)-4));
m_TileIndex = Collision()->GetTileIndex(MapIndex);
m_TileFlags = Collision()->GetTileFlags(MapIndex);
m_TileIndexL = Collision()->GetTileIndex(MapIndexL);
m_TileFlagsL = Collision()->GetTileFlags(MapIndexL);
m_TileIndexR = Collision()->GetTileIndex(MapIndexR);
m_TileFlagsR = Collision()->GetTileFlags(MapIndexR);
m_TileIndexB = Collision()->GetTileIndex(MapIndexB);
m_TileFlagsB = Collision()->GetTileFlags(MapIndexB);
m_TileIndexT = Collision()->GetTileIndex(MapIndexT);
m_TileFlagsT = Collision()->GetTileFlags(MapIndexT);
m_TileFIndex = Collision()->GetFTileIndex(MapIndex);
m_TileFFlags = Collision()->GetFTileFlags(MapIndex);
m_TileFIndexL = Collision()->GetFTileIndex(MapIndexL);
m_TileFFlagsL = Collision()->GetFTileFlags(MapIndexL);
m_TileFIndexR = Collision()->GetFTileIndex(MapIndexR);
m_TileFFlagsR = Collision()->GetFTileFlags(MapIndexR);
m_TileFIndexB = Collision()->GetFTileIndex(MapIndexB);
m_TileFFlagsB = Collision()->GetFTileFlags(MapIndexB);
m_TileFIndexT = Collision()->GetFTileIndex(MapIndexT);
m_TileFFlagsT = Collision()->GetFTileFlags(MapIndexT);
m_TileSIndex = (UseInput && IsRightTeam(MapIndex))?Collision()->GetDTileIndex(MapIndex):0;
m_TileSFlags = (UseInput && IsRightTeam(MapIndex))?Collision()->GetDTileFlags(MapIndex):0;
m_TileSIndexL = (UseInput && IsRightTeam(MapIndexL))?Collision()->GetDTileIndex(MapIndexL):0;
m_TileSFlagsL = (UseInput && IsRightTeam(MapIndexL))?Collision()->GetDTileFlags(MapIndexL):0;
m_TileSIndexR = (UseInput && IsRightTeam(MapIndexR))?Collision()->GetDTileIndex(MapIndexR):0;
m_TileSFlagsR = (UseInput && IsRightTeam(MapIndexR))?Collision()->GetDTileFlags(MapIndexR):0;
m_TileSIndexB = (UseInput && IsRightTeam(MapIndexB))?Collision()->GetDTileIndex(MapIndexB):0;
m_TileSFlagsB = (UseInput && IsRightTeam(MapIndexB))?Collision()->GetDTileFlags(MapIndexB):0;
m_TileSIndexT = (UseInput && IsRightTeam(MapIndexT))?Collision()->GetDTileIndex(MapIndexT):0;
m_TileSFlagsT = (UseInput && IsRightTeam(MapIndexT))?Collision()->GetDTileFlags(MapIndexT):0;
m_MoveRestrictions = m_pCollision->GetMoveRestrictions(UseInput ? IsSwitchActiveCb : 0, this, m_Pos);
m_TriggeredEvents = 0;

// get ground state
Expand Down Expand Up @@ -462,32 +428,15 @@ void CCharacterCore::Tick(bool UseInput)
float Accel = m_pWorld->m_Tuning[g_Config.m_ClDummy].m_HookDragAccel * (Distance/m_pWorld->m_Tuning[g_Config.m_ClDummy].m_HookLength);
float DragSpeed = m_pWorld->m_Tuning[g_Config.m_ClDummy].m_HookDragSpeed;

vec2 Temp;
// add force to the hooked player
vec2 Temp = pCharCore->m_Vel;
Temp.x = SaturatedAdd(-DragSpeed, DragSpeed, pCharCore->m_Vel.x, Accel*Dir.x*1.5f);
Temp.y = SaturatedAdd(-DragSpeed, DragSpeed, pCharCore->m_Vel.y, Accel*Dir.y*1.5f);
if(Temp.x > 0 && ((pCharCore->m_TileIndex == TILE_STOP && pCharCore->m_TileFlags == ROTATION_270) || (pCharCore->m_TileIndexL == TILE_STOP && pCharCore->m_TileFlagsL == ROTATION_270) || (pCharCore->m_TileIndexL == TILE_STOPS && (pCharCore->m_TileFlagsL == ROTATION_90 || pCharCore->m_TileFlagsL ==ROTATION_270)) || (pCharCore->m_TileIndexL == TILE_STOPA) || (pCharCore->m_TileFIndex == TILE_STOP && pCharCore->m_TileFFlags == ROTATION_270) || (pCharCore->m_TileFIndexL == TILE_STOP && pCharCore->m_TileFFlagsL == ROTATION_270) || (pCharCore->m_TileFIndexL == TILE_STOPS && (pCharCore->m_TileFFlagsL == ROTATION_90 || pCharCore->m_TileFFlagsL == ROTATION_270)) || (pCharCore->m_TileFIndexL == TILE_STOPA) || (pCharCore->m_TileSIndex == TILE_STOP && pCharCore->m_TileSFlags == ROTATION_270) || (pCharCore->m_TileSIndexL == TILE_STOP && pCharCore->m_TileSFlagsL == ROTATION_270) || (pCharCore->m_TileSIndexL == TILE_STOPS && (pCharCore->m_TileSFlagsL == ROTATION_90 || pCharCore->m_TileSFlagsL == ROTATION_270)) || (pCharCore->m_TileSIndexL == TILE_STOPA)))
Temp.x = 0;
if(Temp.x < 0 && ((pCharCore->m_TileIndex == TILE_STOP && pCharCore->m_TileFlags == ROTATION_90) || (pCharCore->m_TileIndexR == TILE_STOP && pCharCore->m_TileFlagsR == ROTATION_90) || (pCharCore->m_TileIndexR == TILE_STOPS && (pCharCore->m_TileFlagsR == ROTATION_90 || pCharCore->m_TileFlagsR == ROTATION_270)) || (pCharCore->m_TileIndexR == TILE_STOPA) || (pCharCore->m_TileFIndex == TILE_STOP && pCharCore->m_TileFFlags == ROTATION_90) || (pCharCore->m_TileFIndexR == TILE_STOP && pCharCore->m_TileFFlagsR == ROTATION_90) || (pCharCore->m_TileFIndexR == TILE_STOPS && (pCharCore->m_TileFFlagsR == ROTATION_90 || pCharCore->m_TileFFlagsR == ROTATION_270)) || (pCharCore->m_TileFIndexR == TILE_STOPA) || (pCharCore->m_TileSIndex == TILE_STOP && pCharCore->m_TileSFlags == ROTATION_90) || (pCharCore->m_TileSIndexR == TILE_STOP && pCharCore->m_TileSFlagsR == ROTATION_90) || (pCharCore->m_TileSIndexR == TILE_STOPS && (pCharCore->m_TileSFlagsR == ROTATION_90 || pCharCore->m_TileSFlagsR == ROTATION_270)) || (pCharCore->m_TileSIndexR == TILE_STOPA)))
Temp.x = 0;
if(Temp.y < 0 && ((pCharCore->m_TileIndex == TILE_STOP && pCharCore->m_TileFlags == ROTATION_180) || (pCharCore->m_TileIndexB == TILE_STOP && pCharCore->m_TileFlagsB == ROTATION_180) || (pCharCore->m_TileIndexB == TILE_STOPS && (pCharCore->m_TileFlagsB == ROTATION_0 || pCharCore->m_TileFlagsB == ROTATION_180)) || (pCharCore->m_TileIndexB == TILE_STOPA) || (pCharCore->m_TileFIndex == TILE_STOP && pCharCore->m_TileFFlags == ROTATION_180) || (pCharCore->m_TileFIndexB == TILE_STOP && pCharCore->m_TileFFlagsB == ROTATION_180) || (pCharCore->m_TileFIndexB == TILE_STOPS && (pCharCore->m_TileFFlagsB == ROTATION_0 || pCharCore->m_TileFFlagsB == ROTATION_180)) || (pCharCore->m_TileFIndexB == TILE_STOPA) || (pCharCore->m_TileSIndex == TILE_STOP && pCharCore->m_TileSFlags == ROTATION_180) || (pCharCore->m_TileSIndexB == TILE_STOP && pCharCore->m_TileSFlagsB == ROTATION_180) || (pCharCore->m_TileSIndexB == TILE_STOPS && (pCharCore->m_TileSFlagsB == ROTATION_0 || pCharCore->m_TileSFlagsB == ROTATION_180)) || (pCharCore->m_TileSIndexB == TILE_STOPA)))
Temp.y = 0;
if(Temp.y > 0 && ((pCharCore->m_TileIndex == TILE_STOP && pCharCore->m_TileFlags == ROTATION_0) || (pCharCore->m_TileIndexT == TILE_STOP && pCharCore->m_TileFlagsT == ROTATION_0) || (pCharCore->m_TileIndexT == TILE_STOPS && (pCharCore->m_TileFlagsT == ROTATION_0 || pCharCore->m_TileFlagsT == ROTATION_180)) || (pCharCore->m_TileIndexT == TILE_STOPA) || (pCharCore->m_TileFIndex == TILE_STOP && pCharCore->m_TileFFlags == ROTATION_0) || (pCharCore->m_TileFIndexT == TILE_STOP && pCharCore->m_TileFFlagsT == ROTATION_0) || (pCharCore->m_TileFIndexT == TILE_STOPS && (pCharCore->m_TileFFlagsT == ROTATION_0 || pCharCore->m_TileFFlagsT == ROTATION_180)) || (pCharCore->m_TileFIndexT == TILE_STOPA) || (pCharCore->m_TileSIndex == TILE_STOP && pCharCore->m_TileSFlags == ROTATION_0) || (pCharCore->m_TileSIndexT == TILE_STOP && pCharCore->m_TileSFlagsT == ROTATION_0) || (pCharCore->m_TileSIndexT == TILE_STOPS && (pCharCore->m_TileSFlagsT == ROTATION_0 || pCharCore->m_TileSFlagsT == ROTATION_180)) || (pCharCore->m_TileSIndexT == TILE_STOPA)))
Temp.y = 0;

pCharCore->m_Vel = ClampVel(pCharCore->m_MoveRestrictions, Temp);
// add a little bit force to the guy who has the grip
pCharCore->m_Vel = Temp;
Temp.x = SaturatedAdd(-DragSpeed, DragSpeed, m_Vel.x, -Accel*Dir.x*0.25f);
Temp.y = SaturatedAdd(-DragSpeed, DragSpeed, m_Vel.y, -Accel*Dir.y*0.25f);
if(Temp.x > 0 && ((m_TileIndex == TILE_STOP && m_TileFlags == ROTATION_270) || (m_TileIndexL == TILE_STOP && m_TileFlagsL == ROTATION_270) || (m_TileIndexL == TILE_STOPS && (m_TileFlagsL == ROTATION_90 || m_TileFlagsL ==ROTATION_270)) || (m_TileIndexL == TILE_STOPA) || (m_TileFIndex == TILE_STOP && m_TileFFlags == ROTATION_270) || (m_TileFIndexL == TILE_STOP && m_TileFFlagsL == ROTATION_270) || (m_TileFIndexL == TILE_STOPS && (m_TileFFlagsL == ROTATION_90 || m_TileFFlagsL == ROTATION_270)) || (m_TileFIndexL == TILE_STOPA) || (m_TileSIndex == TILE_STOP && m_TileSFlags == ROTATION_270) || (m_TileSIndexL == TILE_STOP && m_TileSFlagsL == ROTATION_270) || (m_TileSIndexL == TILE_STOPS && (m_TileSFlagsL == ROTATION_90 || m_TileSFlagsL == ROTATION_270)) || (m_TileSIndexL == TILE_STOPA)))
Temp.x = 0;
if(Temp.x < 0 && ((m_TileIndex == TILE_STOP && m_TileFlags == ROTATION_90) || (m_TileIndexR == TILE_STOP && m_TileFlagsR == ROTATION_90) || (m_TileIndexR == TILE_STOPS && (m_TileFlagsR == ROTATION_90 || m_TileFlagsR == ROTATION_270)) || (m_TileIndexR == TILE_STOPA) || (m_TileFIndex == TILE_STOP && m_TileFFlags == ROTATION_90) || (m_TileFIndexR == TILE_STOP && m_TileFFlagsR == ROTATION_90) || (m_TileFIndexR == TILE_STOPS && (m_TileFFlagsR == ROTATION_90 || m_TileFFlagsR == ROTATION_270)) || (m_TileFIndexR == TILE_STOPA) || (m_TileSIndex == TILE_STOP && m_TileSFlags == ROTATION_90) || (m_TileSIndexR == TILE_STOP && m_TileSFlagsR == ROTATION_90) || (m_TileSIndexR == TILE_STOPS && (m_TileSFlagsR == ROTATION_90 || m_TileSFlagsR == ROTATION_270)) || (m_TileSIndexR == TILE_STOPA)))
Temp.x = 0;
if(Temp.y < 0 && ((m_TileIndex == TILE_STOP && m_TileFlags == ROTATION_180) || (m_TileIndexB == TILE_STOP && m_TileFlagsB == ROTATION_180) || (m_TileIndexB == TILE_STOPS && (m_TileFlagsB == ROTATION_0 || m_TileFlagsB == ROTATION_180)) || (m_TileIndexB == TILE_STOPA) || (m_TileFIndex == TILE_STOP && m_TileFFlags == ROTATION_180) || (m_TileFIndexB == TILE_STOP && m_TileFFlagsB == ROTATION_180) || (m_TileFIndexB == TILE_STOPS && (m_TileFFlagsB == ROTATION_0 || m_TileFFlagsB == ROTATION_180)) || (m_TileFIndexB == TILE_STOPA) || (m_TileSIndex == TILE_STOP && m_TileSFlags == ROTATION_180) || (m_TileSIndexB == TILE_STOP && m_TileSFlagsB == ROTATION_180) || (m_TileSIndexB == TILE_STOPS && (m_TileSFlagsB == ROTATION_0 || m_TileSFlagsB == ROTATION_180)) || (m_TileSIndexB == TILE_STOPA)))
Temp.y = 0;
if(Temp.y > 0 && ((m_TileIndex == TILE_STOP && m_TileFlags == ROTATION_0) || (m_TileIndexT == TILE_STOP && m_TileFlagsT == ROTATION_0) || (m_TileIndexT == TILE_STOPS && (m_TileFlagsT == ROTATION_0 || m_TileFlagsT == ROTATION_180)) || (m_TileIndexT == TILE_STOPA) || (m_TileFIndex == TILE_STOP && m_TileFFlags == ROTATION_0) || (m_TileFIndexT == TILE_STOP && m_TileFFlagsT == ROTATION_0) || (m_TileFIndexT == TILE_STOPS && (m_TileFFlagsT == ROTATION_0 || m_TileFFlagsT == ROTATION_180)) || (m_TileFIndexT == TILE_STOPA) || (m_TileSIndex == TILE_STOP && m_TileSFlags == ROTATION_0) || (m_TileSIndexT == TILE_STOP && m_TileSFlagsT == ROTATION_0) || (m_TileSIndexT == TILE_STOPS && (m_TileSFlagsT == ROTATION_0 || m_TileSFlagsT == ROTATION_180)) || (m_TileSIndexT == TILE_STOPA)))
Temp.y = 0;
m_Vel = Temp;
m_Vel = ClampVel(m_MoveRestrictions, Temp);
}
}
}
Expand Down Expand Up @@ -643,31 +592,21 @@ void CCharacterCore::Quantize()

// DDRace

bool CCharacterCore::IsRightTeam(int MapIndex)
bool CCharacterCore::IsSwitchActiveCb(int Number, void *pUser)
{
if(Collision()->m_pSwitchers)
if(m_pTeams->Team(m_Id) != (m_pTeams->m_IsDDRace16 ? VANILLA_TEAM_SUPER : TEAM_SUPER))
return Collision()->m_pSwitchers[Collision()->GetDTileNumber(MapIndex)].m_Status[m_pTeams->Team(m_Id)];
CCharacterCore *pThis = (CCharacterCore *)pUser;
if(pThis->Collision()->m_pSwitchers)
if(pThis->m_pTeams->Team(pThis->m_Id) != (pThis->m_pTeams->m_IsDDRace16 ? VANILLA_TEAM_SUPER : TEAM_SUPER))
return pThis->Collision()->m_pSwitchers[Number].m_Status[pThis->m_pTeams->Team(pThis->m_Id)];
return false;
}

void CCharacterCore::LimitForce(vec2 *Force)
vec2 CCharacterCore::LimitVel(vec2 Vel)
{
vec2 Temp = *Force;
if(Temp.x > 0 && ((m_TileIndex == TILE_STOP && m_TileFlags == ROTATION_270) || (m_TileIndexL == TILE_STOP && m_TileFlagsL == ROTATION_270) || (m_TileIndexL == TILE_STOPS && (m_TileFlagsL == ROTATION_90 || m_TileFlagsL ==ROTATION_270)) || (m_TileIndexL == TILE_STOPA) || (m_TileFIndex == TILE_STOP && m_TileFFlags == ROTATION_270) || (m_TileFIndexL == TILE_STOP && m_TileFFlagsL == ROTATION_270) || (m_TileFIndexL == TILE_STOPS && (m_TileFFlagsL == ROTATION_90 || m_TileFFlagsL == ROTATION_270)) || (m_TileFIndexL == TILE_STOPA) || (m_TileSIndex == TILE_STOP && m_TileSFlags == ROTATION_270) || (m_TileSIndexL == TILE_STOP && m_TileSFlagsL == ROTATION_270) || (m_TileSIndexL == TILE_STOPS && (m_TileSFlagsL == ROTATION_90 || m_TileSFlagsL == ROTATION_270)) || (m_TileSIndexL == TILE_STOPA)))
Temp.x = 0;
if(Temp.x < 0 && ((m_TileIndex == TILE_STOP && m_TileFlags == ROTATION_90) || (m_TileIndexR == TILE_STOP && m_TileFlagsR == ROTATION_90) || (m_TileIndexR == TILE_STOPS && (m_TileFlagsR == ROTATION_90 || m_TileFlagsR == ROTATION_270)) || (m_TileIndexR == TILE_STOPA) || (m_TileFIndex == TILE_STOP && m_TileFFlags == ROTATION_90) || (m_TileFIndexR == TILE_STOP && m_TileFFlagsR == ROTATION_90) || (m_TileFIndexR == TILE_STOPS && (m_TileFFlagsR == ROTATION_90 || m_TileFFlagsR == ROTATION_270)) || (m_TileFIndexR == TILE_STOPA) || (m_TileSIndex == TILE_STOP && m_TileSFlags == ROTATION_90) || (m_TileSIndexR == TILE_STOP && m_TileSFlagsR == ROTATION_90) || (m_TileSIndexR == TILE_STOPS && (m_TileSFlagsR == ROTATION_90 || m_TileSFlagsR == ROTATION_270)) || (m_TileSIndexR == TILE_STOPA)))
Temp.x = 0;
if(Temp.y < 0 && ((m_TileIndex == TILE_STOP && m_TileFlags == ROTATION_180) || (m_TileIndexB == TILE_STOP && m_TileFlagsB == ROTATION_180) || (m_TileIndexB == TILE_STOPS && (m_TileFlagsB == ROTATION_0 || m_TileFlagsB == ROTATION_180)) || (m_TileIndexB == TILE_STOPA) || (m_TileFIndex == TILE_STOP && m_TileFFlags == ROTATION_180) || (m_TileFIndexB == TILE_STOP && m_TileFFlagsB == ROTATION_180) || (m_TileFIndexB == TILE_STOPS && (m_TileFFlagsB == ROTATION_0 || m_TileFFlagsB == ROTATION_180)) || (m_TileFIndexB == TILE_STOPA) || (m_TileSIndex == TILE_STOP && m_TileSFlags == ROTATION_180) || (m_TileSIndexB == TILE_STOP && m_TileSFlagsB == ROTATION_180) || (m_TileSIndexB == TILE_STOPS && (m_TileSFlagsB == ROTATION_0 || m_TileSFlagsB == ROTATION_180)) || (m_TileSIndexB == TILE_STOPA)))
Temp.y = 0;
if(Temp.y > 0 && ((m_TileIndex == TILE_STOP && m_TileFlags == ROTATION_0) || (m_TileIndexT == TILE_STOP && m_TileFlagsT == ROTATION_0) || (m_TileIndexT == TILE_STOPS && (m_TileFlagsT == ROTATION_0 || m_TileFlagsT == ROTATION_180)) || (m_TileIndexT == TILE_STOPA) || (m_TileFIndex == TILE_STOP && m_TileFFlags == ROTATION_0) || (m_TileFIndexT == TILE_STOP && m_TileFFlagsT == ROTATION_0) || (m_TileFIndexT == TILE_STOPS && (m_TileFFlagsT == ROTATION_0 || m_TileFFlagsT == ROTATION_180)) || (m_TileFIndexT == TILE_STOPA) || (m_TileSIndex == TILE_STOP && m_TileSFlags == ROTATION_0) || (m_TileSIndexT == TILE_STOP && m_TileSFlagsT == ROTATION_0) || (m_TileSIndexT == TILE_STOPS && (m_TileSFlagsT == ROTATION_0 || m_TileSFlagsT == ROTATION_180)) || (m_TileSIndexT == TILE_STOPA)))
Temp.y = 0;
*Force = Temp;
return ClampVel(m_MoveRestrictions, Vel);
}

void CCharacterCore::ApplyForce(vec2 Force)
{
vec2 Temp = m_Vel + Force;
LimitForce(&Temp);
m_Vel = Temp;
m_Vel = LimitVel(m_Vel + Force);
}
37 changes: 5 additions & 32 deletions src/game/gamecore.h
Expand Up @@ -250,43 +250,16 @@ class CCharacterCore
int m_FreezeEnd;
bool m_DeepFrozen;

void LimitForce(vec2 *Force);
// Caps the given velocity according to the current set of stoppers
// that the character is affected by.
vec2 LimitVel(vec2 Vel);
void ApplyForce(vec2 Force);

private:

CTeamsCore *m_pTeams;
int m_TileIndex;
int m_TileFlags;
int m_TileFIndex;
int m_TileFFlags;
int m_TileSIndex;
int m_TileSFlags;
int m_TileIndexL;
int m_TileFlagsL;
int m_TileFIndexL;
int m_TileFFlagsL;
int m_TileSIndexL;
int m_TileSFlagsL;
int m_TileIndexR;
int m_TileFlagsR;
int m_TileFIndexR;
int m_TileFFlagsR;
int m_TileSIndexR;
int m_TileSFlagsR;
int m_TileIndexT;
int m_TileFlagsT;
int m_TileFIndexT;
int m_TileFFlagsT;
int m_TileSIndexT;
int m_TileSFlagsT;
int m_TileIndexB;
int m_TileFlagsB;
int m_TileFIndexB;
int m_TileFFlagsB;
int m_TileSIndexB;
int m_TileSFlagsB;
bool IsRightTeam(int MapIndex);
int m_MoveRestrictions;
static bool IsSwitchActiveCb(int Number, void *pUser);
};

//input count
Expand Down
3 changes: 3 additions & 0 deletions src/game/server/ddracecommands.cpp
Expand Up @@ -661,6 +661,9 @@ void CGameContext::ConSetDDRTeam(IConsole::IResult *pResult, void *pUserData)
int Target = pResult->GetVictim();
int Team = pResult->GetInteger(1);

if (Team < TEAM_FLOCK || Team >= TEAM_SUPER)
return;

if(pController->m_Teams.m_Core.Team(Target) && pController->m_Teams.GetDDRaceState(pSelf->m_apPlayers[Target]) == DDRACE_STARTED)
pSelf->m_apPlayers[Target]->KillCharacter(WEAPON_SELF);

Expand Down
166 changes: 45 additions & 121 deletions src/game/server/entities/character.cpp

Large diffs are not rendered by default.

53 changes: 15 additions & 38 deletions src/game/server/entities/character.h
Expand Up @@ -83,7 +83,7 @@ class CCharacter : public CEntity

void Rescue();

int NeededFaketuning() {return m_NeededFaketuning;}
int NeededFaketuning() { return m_NeededFaketuning;}
bool IsAlive() const { return m_Alive; }
bool IsPaused() const { return m_Paused; }
class CPlayer *GetPlayer() { return m_pPlayer; }
Expand Down Expand Up @@ -159,7 +159,7 @@ class CCharacter : public CEntity

// DDRace


static bool IsSwitchActiveCb(int Number, void *pUser);
void HandleTiles(int Index);
float m_Time;
int m_LastBroadcast;
Expand Down Expand Up @@ -193,6 +193,7 @@ class CCharacter : public CEntity
int m_TeamBeforeSuper;
int m_FreezeTime;
int m_FreezeTick;
bool m_FrozenLastTick;
bool m_DeepFreeze;
bool m_EndlessHook;
bool m_FreezeHammer;
Expand All @@ -217,59 +218,31 @@ class CCharacter : public CEntity
int m_CpLastBroadcast;
float m_CpCurrent[25];
int m_TileIndex;
int m_TileFlags;
int m_TileFIndex;
int m_TileFFlags;
int m_TileSIndex;
int m_TileSFlags;
int m_TileIndexL;
int m_TileFlagsL;
int m_TileFIndexL;
int m_TileFFlagsL;
int m_TileSIndexL;
int m_TileSFlagsL;
int m_TileIndexR;
int m_TileFlagsR;
int m_TileFIndexR;
int m_TileFFlagsR;
int m_TileSIndexR;
int m_TileSFlagsR;
int m_TileIndexT;
int m_TileFlagsT;
int m_TileFIndexT;
int m_TileFFlagsT;
int m_TileSIndexT;
int m_TileSFlagsT;
int m_TileIndexB;
int m_TileFlagsB;
int m_TileFIndexB;
int m_TileFFlagsB;
int m_TileSIndexB;
int m_TileSFlagsB;

int m_MoveRestrictions;

vec2 m_Intersection;
int64 m_LastStartWarning;
int64 m_LastRescue;
bool m_LastRefillJumps;
bool m_LastPenalty;
bool m_LastBonus;
bool m_HasTeleGun;
bool m_HasTeleGrenade;
bool m_HasTeleLaser;
vec2 m_TeleGunPos;
bool m_TeleGunTeleport;
bool m_IsBlueTeleGunTeleport;
int m_StrongWeakID;

// Setters/Getters because i don't want to modify vanilla vars access modifiers
int GetLastWeapon() { return m_LastWeapon; };
void SetLastWeapon(int LastWeap) {m_LastWeapon = LastWeap; };
void SetLastWeapon(int LastWeap) { m_LastWeapon = LastWeap; };
int GetActiveWeapon() { return m_Core.m_ActiveWeapon; };
void SetActiveWeapon(int ActiveWeap) {m_Core.m_ActiveWeapon = ActiveWeap; };
void SetLastAction(int LastAction) {m_LastAction = LastAction; };
void SetActiveWeapon(int ActiveWeap) { m_Core.m_ActiveWeapon = ActiveWeap; };
void SetLastAction(int LastAction) { m_LastAction = LastAction; };
int GetArmor() { return m_Armor; };
void SetArmor(int Armor) {m_Armor = Armor; };
void SetArmor(int Armor) { m_Armor = Armor; };
CCharacterCore GetCore() { return m_Core; };
void SetCore(CCharacterCore Core) {m_Core = Core; };
void SetCore(CCharacterCore Core) { m_Core = Core; };
CCharacterCore* Core() { return &m_Core; };
bool GetWeaponGot(int Type) { return m_aWeapons[Type].m_Got; };
void SetWeaponGot(int Type, bool Value) { m_aWeapons[Type].m_Got = Value; };
Expand All @@ -281,6 +254,10 @@ class CCharacter : public CEntity
void SetNinjaActivationDir(vec2 ActivationDir) { m_Ninja.m_ActivationDir = ActivationDir; };
void SetNinjaActivationTick(int ActivationTick) { m_Ninja.m_ActivationTick = ActivationTick; };
void SetNinjaCurrentMoveTime(int CurrentMoveTime) { m_Ninja.m_CurrentMoveTime = CurrentMoveTime; };

bool HasTelegunGun() { return m_Core.m_HasTelegunGun; };
bool HasTelegunGrenade() { return m_Core.m_HasTelegunGrenade; };
bool HasTelegunLaser() { return m_Core.m_HasTelegunLaser; };
};

enum
Expand Down
121 changes: 2 additions & 119 deletions src/game/server/entities/dragger.cpp
Expand Up @@ -127,125 +127,8 @@ void CDragger::Drag()
}
else if (length(m_Pos - Target->m_Pos) > 28)
{
vec2 Temp = Target->Core()->m_Vel
+ (normalize(m_Pos - Target->m_Pos) * m_Strength);
if (Temp.x > 0
&& ((Target->m_TileIndex == TILE_STOP
&& Target->m_TileFlags == ROTATION_270)
|| (Target->m_TileIndexL == TILE_STOP
&& Target->m_TileFlagsL == ROTATION_270)
|| (Target->m_TileIndexL == TILE_STOPS
&& (Target->m_TileFlagsL == ROTATION_90
|| Target->m_TileFlagsL
== ROTATION_270))
|| (Target->m_TileIndexL == TILE_STOPA)
|| (Target->m_TileFIndex == TILE_STOP
&& Target->m_TileFFlags == ROTATION_270)
|| (Target->m_TileFIndexL == TILE_STOP
&& Target->m_TileFFlagsL == ROTATION_270)
|| (Target->m_TileFIndexL == TILE_STOPS
&& (Target->m_TileFFlagsL == ROTATION_90
|| Target->m_TileFFlagsL
== ROTATION_270))
|| (Target->m_TileFIndexL == TILE_STOPA)
|| (Target->m_TileSIndex == TILE_STOP
&& Target->m_TileSFlags == ROTATION_270)
|| (Target->m_TileSIndexL == TILE_STOP
&& Target->m_TileSFlagsL == ROTATION_270)
|| (Target->m_TileSIndexL == TILE_STOPS
&& (Target->m_TileSFlagsL == ROTATION_90
|| Target->m_TileSFlagsL
== ROTATION_270))
|| (Target->m_TileSIndexL == TILE_STOPA)))
Temp.x = 0;
if (Temp.x < 0
&& ((Target->m_TileIndex == TILE_STOP
&& Target->m_TileFlags == ROTATION_90)
|| (Target->m_TileIndexR == TILE_STOP
&& Target->m_TileFlagsR == ROTATION_90)
|| (Target->m_TileIndexR == TILE_STOPS
&& (Target->m_TileFlagsR == ROTATION_90
|| Target->m_TileFlagsR
== ROTATION_270))
|| (Target->m_TileIndexR == TILE_STOPA)
|| (Target->m_TileFIndex == TILE_STOP
&& Target->m_TileFFlags == ROTATION_90)
|| (Target->m_TileFIndexR == TILE_STOP
&& Target->m_TileFFlagsR == ROTATION_90)
|| (Target->m_TileFIndexR == TILE_STOPS
&& (Target->m_TileFFlagsR == ROTATION_90
|| Target->m_TileFFlagsR
== ROTATION_270))
|| (Target->m_TileFIndexR == TILE_STOPA)
|| (Target->m_TileSIndex == TILE_STOP
&& Target->m_TileSFlags == ROTATION_90)
|| (Target->m_TileSIndexR == TILE_STOP
&& Target->m_TileSFlagsR == ROTATION_90)
|| (Target->m_TileSIndexR == TILE_STOPS
&& (Target->m_TileSFlagsR == ROTATION_90
|| Target->m_TileSFlagsR
== ROTATION_270))
|| (Target->m_TileSIndexR == TILE_STOPA)))
Temp.x = 0;
if (Temp.y < 0
&& ((Target->m_TileIndex == TILE_STOP
&& Target->m_TileFlags == ROTATION_180)
|| (Target->m_TileIndexB == TILE_STOP
&& Target->m_TileFlagsB == ROTATION_180)
|| (Target->m_TileIndexB == TILE_STOPS
&& (Target->m_TileFlagsB == ROTATION_0
|| Target->m_TileFlagsB
== ROTATION_180))
|| (Target->m_TileIndexB == TILE_STOPA)
|| (Target->m_TileFIndex == TILE_STOP
&& Target->m_TileFFlags == ROTATION_180)
|| (Target->m_TileFIndexB == TILE_STOP
&& Target->m_TileFFlagsB == ROTATION_180)
|| (Target->m_TileFIndexB == TILE_STOPS
&& (Target->m_TileFFlagsB == ROTATION_0
|| Target->m_TileFFlagsB
== ROTATION_180))
|| (Target->m_TileFIndexB == TILE_STOPA)
|| (Target->m_TileSIndex == TILE_STOP
&& Target->m_TileSFlags == ROTATION_180)
|| (Target->m_TileSIndexB == TILE_STOP
&& Target->m_TileSFlagsB == ROTATION_180)
|| (Target->m_TileSIndexB == TILE_STOPS
&& (Target->m_TileSFlagsB == ROTATION_0
|| Target->m_TileSFlagsB
== ROTATION_180))
|| (Target->m_TileSIndexB == TILE_STOPA)))
Temp.y = 0;
if (Temp.y > 0
&& ((Target->m_TileIndex == TILE_STOP
&& Target->m_TileFlags == ROTATION_0)
|| (Target->m_TileIndexT == TILE_STOP
&& Target->m_TileFlagsT == ROTATION_0)
|| (Target->m_TileIndexT == TILE_STOPS
&& (Target->m_TileFlagsT == ROTATION_0
|| Target->m_TileFlagsT
== ROTATION_180))
|| (Target->m_TileIndexT == TILE_STOPA)
|| (Target->m_TileFIndex == TILE_STOP
&& Target->m_TileFFlags == ROTATION_0)
|| (Target->m_TileFIndexT == TILE_STOP
&& Target->m_TileFFlagsT == ROTATION_0)
|| (Target->m_TileFIndexT == TILE_STOPS
&& (Target->m_TileFFlagsT == ROTATION_0
|| Target->m_TileFFlagsT
== ROTATION_180))
|| (Target->m_TileFIndexT == TILE_STOPA)
|| (Target->m_TileSIndex == TILE_STOP
&& Target->m_TileSFlags == ROTATION_0)
|| (Target->m_TileSIndexT == TILE_STOP
&& Target->m_TileSFlagsT == ROTATION_0)
|| (Target->m_TileSIndexT == TILE_STOPS
&& (Target->m_TileSFlagsT == ROTATION_0
|| Target->m_TileSFlagsT
== ROTATION_180))
|| (Target->m_TileSIndexT == TILE_STOPA)))
Temp.y = 0;
Target->Core()->m_Vel = Temp;
vec2 Temp = Target->Core()->m_Vel + (normalize(m_Pos - Target->m_Pos) * m_Strength);
Target->Core()->m_Vel = ClampVel(Target->m_MoveRestrictions, Temp);
}
}
}
Expand Down
12 changes: 2 additions & 10 deletions src/game/server/entities/laser.cpp
Expand Up @@ -62,15 +62,7 @@ bool CLaser::HitCharacter(vec2 From, vec2 To)
Temp = pHit->Core()->m_Vel + normalize(pOwnerChar->Core()->m_Pos - pHit->Core()->m_Pos) * Strength;
else
Temp = pHit->Core()->m_Vel;
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;
pHit->Core()->m_Vel = ClampVel(pHit->m_MoveRestrictions, Temp);
}
else if (m_Type == WEAPON_RIFLE)
{
Expand Down Expand Up @@ -169,7 +161,7 @@ void CLaser::DoBounce()

CCharacter *pOwnerChar = GameServer()->GetPlayerChar(m_Owner);
if (m_Owner >= 0 && m_Energy <= 0 && m_Pos && !m_TeleportCancelled && pOwnerChar &&
pOwnerChar->IsAlive() && pOwnerChar->m_HasTeleLaser && m_Type == WEAPON_RIFLE)
pOwnerChar->IsAlive() && pOwnerChar->HasTelegunLaser() && m_Type == WEAPON_RIFLE)
{
vec2 PossiblePos;
bool Found = false;
Expand Down
5 changes: 2 additions & 3 deletions src/game/server/entities/projectile.cpp
Expand Up @@ -28,7 +28,6 @@ CProjectile::CProjectile
m_Type = Type;
m_Pos = Pos;
m_Direction = Dir;
m_InitialLifeSpan = Span;
m_LifeSpan = Span;
m_Owner = Owner;
m_Force = Force;
Expand Down Expand Up @@ -154,7 +153,7 @@ void CProjectile::Tick()
if(m_Explosive/*??*/ && (!pTargetChr || (pTargetChr && (!m_Freeze || (m_Type == WEAPON_SHOTGUN && Collide)))))
{
int Number = 1;
if(GameServer()->EmulateBug(BUG_GRENADE_DOUBLEEXPLOSION) && m_LifeSpan == -1 && m_InitialLifeSpan == 0)
if(GameServer()->EmulateBug(BUG_GRENADE_DOUBLEEXPLOSION) && m_LifeSpan == -1)
{
Number = 2;
}
Expand All @@ -176,7 +175,7 @@ void CProjectile::Tick()
}

if (pOwnerChar && ColPos && !GameLayerClipped(ColPos) &&
((m_Type == WEAPON_GRENADE && pOwnerChar->m_HasTeleGrenade) || (m_Type == WEAPON_GUN && pOwnerChar->m_HasTeleGun)))
((m_Type == WEAPON_GRENADE && pOwnerChar->HasTelegunGrenade()) || (m_Type == WEAPON_GUN && pOwnerChar->HasTelegunGun())))
{
int MapIndex = GameServer()->Collision()->GetPureMapIndex(pTargetChr ? pTargetChr->m_Pos : ColPos);
int TileFIndex = GameServer()->Collision()->GetFTileIndex(MapIndex);
Expand Down
1 change: 0 additions & 1 deletion src/game/server/entities/projectile.h
Expand Up @@ -32,7 +32,6 @@ class CProjectile : public CEntity

private:
vec2 m_Direction;
int m_InitialLifeSpan;
int m_LifeSpan;
int m_Owner;
int m_Type;
Expand Down
2 changes: 1 addition & 1 deletion src/game/server/entity.cpp
Expand Up @@ -57,7 +57,7 @@ bool CEntity::GameLayerClipped(vec2 CheckPos)

bool CEntity::GetNearestAirPos(vec2 Pos, vec2 PrevPos, vec2* pOutPos)
{
while (GameServer()->Collision()->CheckPoint(Pos))
for(int k = 0; k < 16 && GameServer()->Collision()->CheckPoint(Pos); k++)
{
Pos -= normalize(PrevPos - Pos);
}
Expand Down
12 changes: 4 additions & 8 deletions src/game/server/gamecontext.cpp
Expand Up @@ -1422,7 +1422,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
pOption->m_aDescription, aReason);
str_format(aDesc, sizeof(aDesc), "%s", pOption->m_aDescription);

if((str_startswith(pOption->m_aCommand, "random_map") || str_startswith(pOption->m_aCommand, "random_unfinished_map")) && str_length(aReason) == 1 && aReason[0] >= '1' && aReason[0] <= '5')
if((str_startswith(pOption->m_aCommand, "random_map") || str_startswith(pOption->m_aCommand, "random_unfinished_map")) && str_length(aReason) == 1 && aReason[0] >= '0' && aReason[0] <= '5')
{
int Stars = aReason[0] - '0';
str_format(aCmd, sizeof(aCmd), "%s %d", pOption->m_aCommand, Stars);
Expand Down Expand Up @@ -2176,9 +2176,7 @@ void CGameContext::ConRandomMap(IConsole::IResult *pResult, void *pUserData)
{
CGameContext *pSelf = (CGameContext *)pUserData;

int Stars = 0;
if(pResult->NumArguments())
Stars = pResult->GetInteger(0);
int Stars = pResult->NumArguments() ? pResult->GetInteger(0) : -1;

pSelf->m_pScore->RandomMap(&pSelf->m_pRandomMapResult, pSelf->m_VoteCreator, Stars);
}
Expand All @@ -2187,11 +2185,9 @@ void CGameContext::ConRandomUnfinishedMap(IConsole::IResult *pResult, void *pUse
{
CGameContext *pSelf = (CGameContext *)pUserData;

int stars = 0;
if (pResult->NumArguments())
stars = pResult->GetInteger(0);
int Stars = pResult->NumArguments() ? pResult->GetInteger(0) : -1;

pSelf->m_pScore->RandomUnfinishedMap(&pSelf->m_pRandomMapResult, pSelf->m_VoteCreator, stars);
pSelf->m_pScore->RandomUnfinishedMap(&pSelf->m_pRandomMapResult, pSelf->m_VoteCreator, Stars);
}

void CGameContext::ConRestart(IConsole::IResult *pResult, void *pUserData)
Expand Down
7 changes: 1 addition & 6 deletions src/game/server/gamecontext.h
Expand Up @@ -374,15 +374,10 @@ class CGameContext : public IGameServer
NETADDR m_Addr;
int m_Expire;
};
struct CVoteMute
{
NETADDR m_Addr;
int m_Expire;
};

CMute m_aMutes[MAX_MUTES];
int m_NumMutes;
CVoteMute m_aVoteMutes[MAX_VOTE_MUTES];
CMute m_aVoteMutes[MAX_VOTE_MUTES];
int m_NumVoteMutes;
bool TryMute(const NETADDR *pAddr, int Secs);
void Mute(const NETADDR *pAddr, int Secs, const char *pDisplayName);
Expand Down
3 changes: 3 additions & 0 deletions src/game/server/gameworld.cpp
Expand Up @@ -304,6 +304,9 @@ CCharacter *CGameWorld::IntersectCharacter(vec2 Pos0, vec2 Pos1, float Radius, v
if(p == pNotThis)
continue;

if(pThisOnly && p != pThisOnly)
continue;

if(CollideWith != -1 && !p->CanCollide(CollideWith))
continue;

Expand Down
31 changes: 22 additions & 9 deletions src/game/server/player.cpp
Expand Up @@ -21,14 +21,13 @@ CPlayer::CPlayer(CGameContext *pGameServer, int ClientID, int Team)
m_pGameServer = pGameServer;
m_ClientID = ClientID;
m_Team = GameServer()->m_pController->ClampTeam(Team);
m_pCharacter = 0;
m_NumInputs = 0;
m_KillMe = 0;
Reset();
}

CPlayer::~CPlayer()
{
delete m_pLastTarget;
delete m_pCharacter;
m_pCharacter = 0;
}
Expand Down Expand Up @@ -57,19 +56,20 @@ void CPlayer::Reset()
// DDRace

m_LastCommandPos = 0;
m_LastPlaytime = time_get();
m_LastPlaytime = 0;
m_Sent1stAfkWarning = 0;
m_Sent2ndAfkWarning = 0;
m_ChatScore = 0;
m_Moderating = false;
m_EyeEmote = true;
m_TimerType = (g_Config.m_SvDefaultTimerType == CPlayer::TIMERTYPE_GAMETIMER || g_Config.m_SvDefaultTimerType == CPlayer::TIMERTYPE_GAMETIMER_AND_BROADCAST) ? CPlayer::TIMERTYPE_BROADCAST : g_Config.m_SvDefaultTimerType;
m_DefEmote = EMOTE_NORMAL;
m_Afk = false;
m_Afk = true;
m_LastWhisperTo = -1;
m_LastSetSpectatorMode = 0;
m_TimeoutCode[0] = '\0';

delete m_pLastTarget;
m_pLastTarget = nullptr;
m_TuneZone = 0;
m_TuneZoneOld = m_TuneZone;
m_Halloween = false;
Expand Down Expand Up @@ -197,7 +197,7 @@ void CPlayer::Tick()
if(!GameServer()->m_World.m_Paused)
{
int EarliestRespawnTick = m_PreviousDieTick+Server()->TickSpeed()*3;
int RespawnTick = maximum(m_DieTick, EarliestRespawnTick);
int RespawnTick = maximum(m_DieTick, EarliestRespawnTick)+2;
if(!m_pCharacter && RespawnTick <= Server()->Tick())
m_Spawning = true;

Expand Down Expand Up @@ -324,7 +324,13 @@ void CPlayer::Snap(int SnappingClient)
return;

pDDNetPlayer->m_AuthLevel = Server()->GetAuthedState(id);
pDDNetPlayer->m_Flags = m_Afk ? EXPLAYERFLAG_AFK : 0;
pDDNetPlayer->m_Flags = 0;
if(m_Afk)
pDDNetPlayer->m_Flags |= EXPLAYERFLAG_AFK;
if(m_Paused == PAUSE_SPEC)
pDDNetPlayer->m_Flags |= EXPLAYERFLAG_SPEC;
if(m_Paused == PAUSE_PAUSED)
pDDNetPlayer->m_Flags |= EXPLAYERFLAG_PAUSED;
}

void CPlayer::FakeSnap()
Expand Down Expand Up @@ -651,10 +657,17 @@ void CPlayer::AfkVoteTimer(CNetObj_PlayerInput *NewTarget)
if(g_Config.m_SvMaxAfkVoteTime == 0)
return;

if(mem_comp(NewTarget, &m_LastTarget, sizeof(CNetObj_PlayerInput)) != 0)
if(!m_pLastTarget)
{
m_pLastTarget = new CNetObj_PlayerInput(*NewTarget);
m_LastPlaytime = 0;
m_Afk = true;
return;
}
else if(mem_comp(NewTarget, m_pLastTarget, sizeof(CNetObj_PlayerInput)) != 0)
{
m_LastPlaytime = time_get();
mem_copy(&m_LastTarget, NewTarget, sizeof(CNetObj_PlayerInput));
mem_copy(m_pLastTarget, NewTarget, sizeof(CNetObj_PlayerInput));
}
else if(m_LastPlaytime < time_get()-time_freq()*g_Config.m_SvMaxAfkVoteTime)
{
Expand Down
4 changes: 2 additions & 2 deletions src/game/server/player.h
Expand Up @@ -141,7 +141,7 @@ class CPlayer
PAUSE_PAUSED,
PAUSE_SPEC
};

enum
{
TIMERTYPE_GAMETIMER=0,
Expand Down Expand Up @@ -183,7 +183,7 @@ class CPlayer
bool m_LastBroadcastImportance;
int m_LastTarget_x;
int m_LastTarget_y;
CNetObj_PlayerInput m_LastTarget;
CNetObj_PlayerInput *m_pLastTarget;
int m_Sent1stAfkWarning; // afk timer's 1st warning after 50% of sv_max_afk_time
int m_Sent2ndAfkWarning; // afk timer's 2nd warning after 90% of sv_max_afk_time
char m_pAfkMsg[160];
Expand Down
22 changes: 11 additions & 11 deletions src/game/server/save.cpp
Expand Up @@ -69,9 +69,9 @@ void CSaveTee::save(CCharacter *pChr)

m_NotEligibleForFinish = pChr->m_pPlayer->m_NotEligibleForFinish;

m_HasTeleGun = pChr->m_HasTeleGun;
m_HasTeleGrenade = pChr->m_HasTeleGrenade;
m_HasTeleLaser = pChr->m_HasTeleLaser;
m_HasTelegunGun = pChr->m_Core.m_HasTelegunGun;
m_HasTelegunGrenade = pChr->m_Core.m_HasTelegunGrenade;
m_HasTelegunLaser = pChr->m_Core.m_HasTelegunLaser;

// Core
m_CorePos = pChr->m_Core.m_Pos;
Expand Down Expand Up @@ -147,9 +147,9 @@ void CSaveTee::load(CCharacter *pChr, int Team)

pChr->m_pPlayer->m_NotEligibleForFinish = pChr->m_pPlayer->m_NotEligibleForFinish || m_NotEligibleForFinish;

pChr->m_HasTeleGun = m_HasTeleGun;
pChr->m_HasTeleLaser = m_HasTeleLaser;
pChr->m_HasTeleGrenade = m_HasTeleGrenade;
pChr->m_Core.m_HasTelegunGun = m_HasTelegunGun;
pChr->m_Core.m_HasTelegunLaser = m_HasTelegunLaser;
pChr->m_Core.m_HasTelegunGrenade = m_HasTelegunGrenade;

// Core
pChr->m_Core.m_Pos = m_CorePos;
Expand Down Expand Up @@ -181,23 +181,23 @@ void CSaveTee::load(CCharacter *pChr, int Team)

char* CSaveTee::GetString()
{
str_format(m_String, sizeof(m_String), "%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%f\t%f\t%d\t%d\t%d\t%d\t%d\t%d\t%f\t%f\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%d\t%d\t%d\t%d\t%s", m_name, m_Alive, m_Paused, m_NeededFaketuning, m_TeeFinished, m_IsSolo, m_aWeapons[0].m_AmmoRegenStart, m_aWeapons[0].m_Ammo, m_aWeapons[0].m_Ammocost, m_aWeapons[0].m_Got, m_aWeapons[1].m_AmmoRegenStart, m_aWeapons[1].m_Ammo, m_aWeapons[1].m_Ammocost, m_aWeapons[1].m_Got, m_aWeapons[2].m_AmmoRegenStart, m_aWeapons[2].m_Ammo, m_aWeapons[2].m_Ammocost, m_aWeapons[2].m_Got, m_aWeapons[3].m_AmmoRegenStart, m_aWeapons[3].m_Ammo, m_aWeapons[3].m_Ammocost, m_aWeapons[3].m_Got, m_aWeapons[4].m_AmmoRegenStart, m_aWeapons[4].m_Ammo, m_aWeapons[4].m_Ammocost, m_aWeapons[4].m_Got, m_aWeapons[5].m_AmmoRegenStart, m_aWeapons[5].m_Ammo, m_aWeapons[5].m_Ammocost, m_aWeapons[5].m_Got, m_LastWeapon, m_QueuedWeapon, m_SuperJump, m_Jetpack, m_NinjaJetpack, m_FreezeTime, m_FreezeTick, m_DeepFreeze, m_EndlessHook, m_DDRaceState, m_Hit, m_Collision, m_TuneZone, m_TuneZoneOld, m_Hook, m_Time, (int)m_Pos.x, (int)m_Pos.y, (int)m_PrevPos.x, (int)m_PrevPos.y, m_TeleCheckpoint, m_LastPenalty, (int)m_CorePos.x, (int)m_CorePos.y, m_Vel.x, m_Vel.y, m_ActiveWeapon, m_Jumped, m_JumpedTotal, m_Jumps, (int)m_HookPos.x, (int)m_HookPos.y, m_HookDir.x, m_HookDir.y, (int)m_HookTeleBase.x, (int)m_HookTeleBase.y, m_HookTick, m_HookState, m_CpTime, m_CpActive, m_CpLastBroadcast, m_CpCurrent[0], m_CpCurrent[1], m_CpCurrent[2], m_CpCurrent[3], m_CpCurrent[4], m_CpCurrent[5], m_CpCurrent[6], m_CpCurrent[7], m_CpCurrent[8], m_CpCurrent[9], m_CpCurrent[10], m_CpCurrent[11], m_CpCurrent[12], m_CpCurrent[13], m_CpCurrent[14], m_CpCurrent[15], m_CpCurrent[16], m_CpCurrent[17], m_CpCurrent[18], m_CpCurrent[19], m_CpCurrent[20], m_CpCurrent[21], m_CpCurrent[22], m_CpCurrent[23], m_CpCurrent[24], m_NotEligibleForFinish, m_HasTeleGun, m_HasTeleLaser, m_HasTeleGrenade, aGameUuid);
str_format(m_String, sizeof(m_String), "%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%f\t%f\t%d\t%d\t%d\t%d\t%d\t%d\t%f\t%f\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%d\t%d\t%d\t%d\t%s", m_name, m_Alive, m_Paused, m_NeededFaketuning, m_TeeFinished, m_IsSolo, m_aWeapons[0].m_AmmoRegenStart, m_aWeapons[0].m_Ammo, m_aWeapons[0].m_Ammocost, m_aWeapons[0].m_Got, m_aWeapons[1].m_AmmoRegenStart, m_aWeapons[1].m_Ammo, m_aWeapons[1].m_Ammocost, m_aWeapons[1].m_Got, m_aWeapons[2].m_AmmoRegenStart, m_aWeapons[2].m_Ammo, m_aWeapons[2].m_Ammocost, m_aWeapons[2].m_Got, m_aWeapons[3].m_AmmoRegenStart, m_aWeapons[3].m_Ammo, m_aWeapons[3].m_Ammocost, m_aWeapons[3].m_Got, m_aWeapons[4].m_AmmoRegenStart, m_aWeapons[4].m_Ammo, m_aWeapons[4].m_Ammocost, m_aWeapons[4].m_Got, m_aWeapons[5].m_AmmoRegenStart, m_aWeapons[5].m_Ammo, m_aWeapons[5].m_Ammocost, m_aWeapons[5].m_Got, m_LastWeapon, m_QueuedWeapon, m_SuperJump, m_Jetpack, m_NinjaJetpack, m_FreezeTime, m_FreezeTick, m_DeepFreeze, m_EndlessHook, m_DDRaceState, m_Hit, m_Collision, m_TuneZone, m_TuneZoneOld, m_Hook, m_Time, (int)m_Pos.x, (int)m_Pos.y, (int)m_PrevPos.x, (int)m_PrevPos.y, m_TeleCheckpoint, m_LastPenalty, (int)m_CorePos.x, (int)m_CorePos.y, m_Vel.x, m_Vel.y, m_ActiveWeapon, m_Jumped, m_JumpedTotal, m_Jumps, (int)m_HookPos.x, (int)m_HookPos.y, m_HookDir.x, m_HookDir.y, (int)m_HookTeleBase.x, (int)m_HookTeleBase.y, m_HookTick, m_HookState, m_CpTime, m_CpActive, m_CpLastBroadcast, m_CpCurrent[0], m_CpCurrent[1], m_CpCurrent[2], m_CpCurrent[3], m_CpCurrent[4], m_CpCurrent[5], m_CpCurrent[6], m_CpCurrent[7], m_CpCurrent[8], m_CpCurrent[9], m_CpCurrent[10], m_CpCurrent[11], m_CpCurrent[12], m_CpCurrent[13], m_CpCurrent[14], m_CpCurrent[15], m_CpCurrent[16], m_CpCurrent[17], m_CpCurrent[18], m_CpCurrent[19], m_CpCurrent[20], m_CpCurrent[21], m_CpCurrent[22], m_CpCurrent[23], m_CpCurrent[24], m_NotEligibleForFinish, m_HasTelegunGun, m_HasTelegunLaser, m_HasTelegunGrenade, aGameUuid);
return m_String;
}

int CSaveTee::LoadString(char* String)
{
int Num;
Num = sscanf(String, "%[^\t]\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%f\t%f\t%f\t%f\t%d\t%d\t%f\t%f\t%f\t%f\t%d\t%d\t%d\t%d\t%f\t%f\t%f\t%f\t%f\t%f\t%d\t%d\t%d\t%d\t%d\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%dt%d\t%d\t%d\t%*s", m_name, &m_Alive, &m_Paused, &m_NeededFaketuning, &m_TeeFinished, &m_IsSolo, &m_aWeapons[0].m_AmmoRegenStart, &m_aWeapons[0].m_Ammo, &m_aWeapons[0].m_Ammocost, &m_aWeapons[0].m_Got, &m_aWeapons[1].m_AmmoRegenStart, &m_aWeapons[1].m_Ammo, &m_aWeapons[1].m_Ammocost, &m_aWeapons[1].m_Got, &m_aWeapons[2].m_AmmoRegenStart, &m_aWeapons[2].m_Ammo, &m_aWeapons[2].m_Ammocost, &m_aWeapons[2].m_Got, &m_aWeapons[3].m_AmmoRegenStart, &m_aWeapons[3].m_Ammo, &m_aWeapons[3].m_Ammocost, &m_aWeapons[3].m_Got, &m_aWeapons[4].m_AmmoRegenStart, &m_aWeapons[4].m_Ammo, &m_aWeapons[4].m_Ammocost, &m_aWeapons[4].m_Got, &m_aWeapons[5].m_AmmoRegenStart, &m_aWeapons[5].m_Ammo, &m_aWeapons[5].m_Ammocost, &m_aWeapons[5].m_Got, &m_LastWeapon, &m_QueuedWeapon, &m_SuperJump, &m_Jetpack, &m_NinjaJetpack, &m_FreezeTime, &m_FreezeTick, &m_DeepFreeze, &m_EndlessHook, &m_DDRaceState, &m_Hit, &m_Collision, &m_TuneZone, &m_TuneZoneOld, &m_Hook, &m_Time, &m_Pos.x, &m_Pos.y, &m_PrevPos.x, &m_PrevPos.y, &m_TeleCheckpoint, &m_LastPenalty, &m_CorePos.x, &m_CorePos.y, &m_Vel.x, &m_Vel.y, &m_ActiveWeapon, &m_Jumped, &m_JumpedTotal, &m_Jumps, &m_HookPos.x, &m_HookPos.y, &m_HookDir.x, &m_HookDir.y, &m_HookTeleBase.x, &m_HookTeleBase.y, &m_HookTick, &m_HookState, &m_CpTime, &m_CpActive, &m_CpLastBroadcast, &m_CpCurrent[0], &m_CpCurrent[1], &m_CpCurrent[2], &m_CpCurrent[3], &m_CpCurrent[4], &m_CpCurrent[5], &m_CpCurrent[6], &m_CpCurrent[7], &m_CpCurrent[8], &m_CpCurrent[9], &m_CpCurrent[10], &m_CpCurrent[11], &m_CpCurrent[12], &m_CpCurrent[13], &m_CpCurrent[14], &m_CpCurrent[15], &m_CpCurrent[16], &m_CpCurrent[17], &m_CpCurrent[18], &m_CpCurrent[19], &m_CpCurrent[20], &m_CpCurrent[21], &m_CpCurrent[22], &m_CpCurrent[23], &m_CpCurrent[24], &m_NotEligibleForFinish, &m_HasTeleGun, &m_HasTeleLaser, &m_HasTeleGrenade);
Num = sscanf(String, "%[^\t]\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%f\t%f\t%f\t%f\t%d\t%d\t%f\t%f\t%f\t%f\t%d\t%d\t%d\t%d\t%f\t%f\t%f\t%f\t%f\t%f\t%d\t%d\t%d\t%d\t%d\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%d\t%d\t%d\t%d\t%*s", m_name, &m_Alive, &m_Paused, &m_NeededFaketuning, &m_TeeFinished, &m_IsSolo, &m_aWeapons[0].m_AmmoRegenStart, &m_aWeapons[0].m_Ammo, &m_aWeapons[0].m_Ammocost, &m_aWeapons[0].m_Got, &m_aWeapons[1].m_AmmoRegenStart, &m_aWeapons[1].m_Ammo, &m_aWeapons[1].m_Ammocost, &m_aWeapons[1].m_Got, &m_aWeapons[2].m_AmmoRegenStart, &m_aWeapons[2].m_Ammo, &m_aWeapons[2].m_Ammocost, &m_aWeapons[2].m_Got, &m_aWeapons[3].m_AmmoRegenStart, &m_aWeapons[3].m_Ammo, &m_aWeapons[3].m_Ammocost, &m_aWeapons[3].m_Got, &m_aWeapons[4].m_AmmoRegenStart, &m_aWeapons[4].m_Ammo, &m_aWeapons[4].m_Ammocost, &m_aWeapons[4].m_Got, &m_aWeapons[5].m_AmmoRegenStart, &m_aWeapons[5].m_Ammo, &m_aWeapons[5].m_Ammocost, &m_aWeapons[5].m_Got, &m_LastWeapon, &m_QueuedWeapon, &m_SuperJump, &m_Jetpack, &m_NinjaJetpack, &m_FreezeTime, &m_FreezeTick, &m_DeepFreeze, &m_EndlessHook, &m_DDRaceState, &m_Hit, &m_Collision, &m_TuneZone, &m_TuneZoneOld, &m_Hook, &m_Time, &m_Pos.x, &m_Pos.y, &m_PrevPos.x, &m_PrevPos.y, &m_TeleCheckpoint, &m_LastPenalty, &m_CorePos.x, &m_CorePos.y, &m_Vel.x, &m_Vel.y, &m_ActiveWeapon, &m_Jumped, &m_JumpedTotal, &m_Jumps, &m_HookPos.x, &m_HookPos.y, &m_HookDir.x, &m_HookDir.y, &m_HookTeleBase.x, &m_HookTeleBase.y, &m_HookTick, &m_HookState, &m_CpTime, &m_CpActive, &m_CpLastBroadcast, &m_CpCurrent[0], &m_CpCurrent[1], &m_CpCurrent[2], &m_CpCurrent[3], &m_CpCurrent[4], &m_CpCurrent[5], &m_CpCurrent[6], &m_CpCurrent[7], &m_CpCurrent[8], &m_CpCurrent[9], &m_CpCurrent[10], &m_CpCurrent[11], &m_CpCurrent[12], &m_CpCurrent[13], &m_CpCurrent[14], &m_CpCurrent[15], &m_CpCurrent[16], &m_CpCurrent[17], &m_CpCurrent[18], &m_CpCurrent[19], &m_CpCurrent[20], &m_CpCurrent[21], &m_CpCurrent[22], &m_CpCurrent[23], &m_CpCurrent[24], &m_NotEligibleForFinish, &m_HasTelegunGun, &m_HasTelegunLaser, &m_HasTelegunGrenade);
switch(Num) // Don't forget to update this when you save / load more / less.
{
case 96:
m_NotEligibleForFinish = false;
// fallthrough
case 97:
m_HasTeleGrenade = 0;
m_HasTeleLaser = 0;
m_HasTeleGun = 0;
m_HasTelegunGrenade = 0;
m_HasTelegunLaser = 0;
m_HasTelegunGun = 0;
// fallthrough
case 100:
return 0;
Expand Down
8 changes: 4 additions & 4 deletions src/game/server/save.h
Expand Up @@ -68,9 +68,9 @@ class CSaveTee

int m_NotEligibleForFinish;

int m_HasTeleGun;
int m_HasTeleGrenade;
int m_HasTeleLaser;
int m_HasTelegunGun;
int m_HasTelegunGrenade;
int m_HasTelegunLaser;

// Core
vec2 m_CorePos;
Expand All @@ -94,7 +94,7 @@ class CSaveTeam
CSaveTeam(IGameController* Controller);
~CSaveTeam();
char* GetString();
int GetMembersCount() {return m_MembersCount;}
int GetMembersCount() { return m_MembersCount; }
int LoadString(const char* String);
int save(int Team);
int load(int Team);
Expand Down
10 changes: 5 additions & 5 deletions src/game/server/score/file_score.cpp
Expand Up @@ -41,16 +41,16 @@ CFileScore::~CFileScore()
lock_unlock(gs_ScoreLock);
}

std::string SaveFile()
std::string CFileScore::SaveFile()
{
std::ostringstream oss;
char aBuf[256];
str_copy(aBuf, g_Config.m_SvMap, sizeof(aBuf));
str_copy(aBuf, Server()->GetMapName(), sizeof(aBuf));
for(int i = 0; i < 256; i++) if(aBuf[i] == '/') aBuf[i] = '-';
if (g_Config.m_SvScoreFolder[0])
oss << g_Config.m_SvScoreFolder << "/" << aBuf << "_record.dtb";
else
oss << g_Config.m_SvMap << "_record.dtb";
oss << Server()->GetMapName() << "_record.dtb";
return oss.str();
}

Expand All @@ -69,10 +69,10 @@ void CFileScore::SaveScoreThread(void *pUser)
CFileScore *pSelf = (CFileScore *) pUser;
lock_wait(gs_ScoreLock);
std::fstream f;
f.open(SaveFile().c_str(), std::ios::out);
f.open(pSelf->SaveFile().c_str(), std::ios::out);
if(f.fail())
{
dbg_msg("filescore", "opening '%s' for writing failed", SaveFile().c_str());
dbg_msg("filescore", "opening '%s' for writing failed", pSelf->SaveFile().c_str());
}
else
{
Expand Down
3 changes: 3 additions & 0 deletions src/game/server/score/file_score.h
Expand Up @@ -86,6 +86,9 @@ class CFileScore: public IScore
virtual void LoadTeam(const char* Code, int ClientID);

virtual void OnShutdown();

private:
std::string SaveFile();
};

#endif // GAME_SERVER_SCORE_FILE_SCORE_H
4 changes: 2 additions & 2 deletions src/game/server/score/sql_score.cpp
Expand Up @@ -1277,7 +1277,7 @@ bool CSqlScore::RandomMapThread(CSqlServer* pSqlServer, const CSqlData *pGameDat
try
{
char aBuf[512];
if(pData->m_Num)
if(pData->m_Num >= 0)
str_format(aBuf, sizeof(aBuf), "select * from %s_maps where Server = \"%s\" and Map != \"%s\" and Stars = \"%d\" order by RAND() limit 1;", pSqlServer->GetPrefix(), g_Config.m_SvServerType, g_Config.m_SvMap, pData->m_Num);
else
str_format(aBuf, sizeof(aBuf), "select * from %s_maps where Server = \"%s\" and Map != \"%s\" order by RAND() limit 1;", pSqlServer->GetPrefix(), g_Config.m_SvServerType, g_Config.m_SvMap);
Expand Down Expand Up @@ -1335,7 +1335,7 @@ bool CSqlScore::RandomUnfinishedMapThread(CSqlServer* pSqlServer, const CSqlData
try
{
char aBuf[512];
if(pData->m_Num)
if(pData->m_Num >= 0)
str_format(aBuf, sizeof(aBuf), "select * from %s_maps where Server = \"%s\" and Map != \"%s\" and Stars = \"%d\" and not exists (select * from %s_race where Name = \"%s\" and %s_race.Map = %s_maps.Map) order by RAND() limit 1;", pSqlServer->GetPrefix(), g_Config.m_SvServerType, g_Config.m_SvMap, pData->m_Num, pSqlServer->GetPrefix(), pData->m_Name.ClrStr(), pSqlServer->GetPrefix(), pSqlServer->GetPrefix());
else
str_format(aBuf, sizeof(aBuf), "select * from %s_maps where Server = \"%s\" and Map != \"%s\" and not exists (select * from %s_race where Name = \"%s\" and %s_race.Map = %s_maps.Map) order by RAND() limit 1;", pSqlServer->GetPrefix(), g_Config.m_SvServerType, g_Config.m_SvMap, pSqlServer->GetPrefix(), pData->m_Name.ClrStr(), pSqlServer->GetPrefix(), pSqlServer->GetPrefix());
Expand Down
2 changes: 0 additions & 2 deletions src/game/server/teams.cpp
Expand Up @@ -646,8 +646,6 @@ void CGameTeams::OnCharacterSpawn(int ClientID)

void CGameTeams::OnCharacterDeath(int ClientID, int Weapon)
{
GameServer()->m_apPlayers[ClientID]->Respawn(); // queue the spawn as kill tiles don't

m_Core.SetSolo(ClientID, false);

int Team = m_Core.Team(ClientID);
Expand Down
2 changes: 1 addition & 1 deletion src/game/variables.h
Expand Up @@ -88,7 +88,7 @@ MACRO_CONFIG_STR(ClPlayerSkin, player_skin, 24, "default", CFGFLAG_CLIENT|CFGFLA
MACRO_CONFIG_STR(ClSkinPrefix, cl_skin_prefix, 100, "", CFGFLAG_CLIENT|CFGFLAG_SAVE, "Replace the skins by skins with this prefix (e.g. kitty, coala, santa)")
MACRO_CONFIG_INT(ClFatSkins, cl_fat_skins, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Enable fat skins")

MACRO_CONFIG_INT(UiPage, ui_page, 9, 0, 11, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface page")
MACRO_CONFIG_INT(UiPage, ui_page, 9, 0, 12, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface page")
MACRO_CONFIG_INT(UiToolboxPage, ui_toolbox_page, 0, 0, 2, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Toolbox page")
MACRO_CONFIG_STR(UiServerAddress, ui_server_address, 64, "localhost:8303", CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface server address")
MACRO_CONFIG_INT(UiScale, ui_scale, 100, 50, 150, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Interface scale")
Expand Down
6 changes: 3 additions & 3 deletions src/game/version.h
Expand Up @@ -2,9 +2,9 @@
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#ifndef GAME_VERSION_H
#define GAME_VERSION_H
#define GAME_VERSION "0.6.4, 12.6.1"
#define GAME_VERSION "0.6.4, 12.7"
#define GAME_NETVERSION "0.6 626fce9a778df4d4"
#define GAME_RELEASE_VERSION "12.6.1"
#define CLIENT_VERSIONNR 12061
#define GAME_RELEASE_VERSION "12.7"
#define CLIENT_VERSIONNR 12070
extern const char *GIT_SHORTREV_HASH;
#endif