162 changes: 66 additions & 96 deletions src/game/client/components/menus_settings.cpp
Expand Up @@ -438,6 +438,36 @@ void CMenus::RefreshSkins()
}
}

void CMenus::RandomSkin()
{
static const float s_aSchemes[] = {1.0f / 2.0f, 1.0f / 3.0f, 1.0f / -3.0f, 1.0f / 12.0f, 1.0f / -12.0f}; // complementary, triadic, analogous

float GoalSat = random_float(0.3f, 1.0f);
float MaxBodyLht = 1.0f - GoalSat * GoalSat; // max allowed lightness before we start losing saturation

ColorHSLA Body;
Body.h = random_float();
Body.l = random_float(0.0f, MaxBodyLht);
Body.s = clamp(GoalSat * GoalSat / (1.0f - Body.l), 0.0f, 1.0f);

ColorHSLA Feet;
Feet.h = std::fmod(Body.h + s_aSchemes[rand() % std::size(s_aSchemes)], 1.0f);
Feet.l = random_float();
Feet.s = clamp(GoalSat * GoalSat / (1.0f - Feet.l), 0.0f, 1.0f);

const char *pRandomSkinName = CSkins::VANILLA_SKINS[rand() % (std::size(CSkins::VANILLA_SKINS) - 2)]; // last 2 skins are x_ninja and x_spec

char *pSkinName = !m_Dummy ? g_Config.m_ClPlayerSkin : g_Config.m_ClDummySkin;
unsigned *pColorBody = !m_Dummy ? &g_Config.m_ClPlayerColorBody : &g_Config.m_ClDummyColorBody;
unsigned *pColorFeet = !m_Dummy ? &g_Config.m_ClPlayerColorFeet : &g_Config.m_ClDummyColorFeet;

mem_copy(pSkinName, pRandomSkinName, sizeof(g_Config.m_ClPlayerSkin));
*pColorBody = Body.Pack(false);
*pColorFeet = Feet.Pack(false);

SetNeedSendInfo();
}

void CMenus::Con_AddFavoriteSkin(IConsole::IResult *pResult, void *pUserData)
{
auto *pSelf = (CMenus *)pUserData;
Expand Down Expand Up @@ -601,7 +631,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
OwnSkinInfo.m_Size = 50.0f;

MainView.HSplitTop(50.0f, &Label, &MainView);
Label.VSplitLeft(230.0f, &Label, 0);
Label.VSplitLeft(260.0f, &Label, 0);
CAnimState *pIdleState = CAnimState::GetIdle();
vec2 OffsetToMid;
RenderTools()->GetRenderTeeOffsetToRenderedTee(pIdleState, &OwnSkinInfo, OffsetToMid);
Expand Down Expand Up @@ -661,6 +691,8 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
RenderTools()->RenderTee(pIdleState, &OwnSkinInfo, CurrentEyeEmote, vec2(1, 0), vec2(EyesTee.x + 25.0f, EyesTee.y + EyesTee.h / 2.0f + OffsetToMid.y));
}

Label.VSplitRight(34.0f, &Label, &Button);

static float s_OffsetSkin = 0.0f;
static int s_ClearButton = 0;
SUIExEditBoxProperties EditProps;
Expand All @@ -670,6 +702,22 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
SetNeedSendInfo();
}

// random skin button
Button.VSplitRight(30.0f, 0, &Button);
static CButtonContainer s_RandomSkinButtonID;
static const char *s_apDice[] = {"\xef\x94\xa5", "\xef\x94\xa8", "\xef\x94\xa7", "\xef\x94\xa4", "\xef\x94\xa3", "\xef\x94\xa6"};
static int s_CurrentDie = rand() % std::size(s_apDice);
TextRender()->SetCurFont(TextRender()->GetFont(TEXT_FONT_ICON_FONT));
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
if(DoButton_Menu(&s_RandomSkinButtonID, s_apDice[s_CurrentDie], 1, &Button, nullptr, IGraphics::CORNER_ALL, 5.0f, -0.2f))
{
RandomSkin();
s_CurrentDie = rand() % std::size(s_apDice);
}
TextRender()->SetRenderFlags(0);
TextRender()->SetCurFont(nullptr);
GameClient()->m_Tooltips.DoToolTip(&s_RandomSkinButtonID, &Button, Localize("Create a random skin"));

// custom color selector
MainView.HSplitTop(20.0f + RenderEyesBelow * 25.0f, 0, &MainView);
MainView.HSplitTop(20.0f, &Button, &MainView);
Expand Down Expand Up @@ -819,7 +867,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
}
if(g_Config.m_Debug)
{
ColorRGBA BloodColor = *pUseCustomColor ? color_cast<ColorRGBA>(ColorHSLA(*pColorBody)) : pSkinToBeDraw->m_BloodColor;
ColorRGBA BloodColor = *pUseCustomColor ? color_cast<ColorRGBA>(ColorHSLA(*pColorBody).UnclampLighting()) : pSkinToBeDraw->m_BloodColor;
Graphics()->TextureClear();
Graphics()->QuadsBegin();
Graphics()->SetColor(BloodColor.r, BloodColor.g, BloodColor.b, 1.0f);
Expand Down Expand Up @@ -1508,10 +1556,9 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
{
s_WasInit = true;

Graphics()->AddWindowResizeListener([&](void *pUser) {
Graphics()->AddWindowPropChangeListener([]() {
s_ModesReload = true;
},
this);
});
}

if(s_ModesReload || g_Config.m_GfxDisplayAllVideoModes != s_InitDisplayAllVideoModes)
Expand Down Expand Up @@ -1694,7 +1741,7 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
CUIRect Text;
MainView.HSplitTop(20.0f, 0, &MainView);
MainView.HSplitTop(20.0f, &Text, &MainView);
//text.VSplitLeft(15.0f, 0, &text);
// text.VSplitLeft(15.0f, 0, &text);
UI()->DoLabel(&Text, Localize("UI Color"), 14.0f, TEXTALIGN_LEFT);
CUIRect HSLBar = MainView;
RenderHSLScrollbars(&HSLBar, &g_Config.m_UiColor, true);
Expand Down Expand Up @@ -2027,109 +2074,32 @@ void CMenus::RenderSettingsSound(CUIRect MainView)
}
}

class CLanguage
{
public:
CLanguage() = default;
CLanguage(const char *pName, const char *pFileName, int Code) :
m_Name(pName), m_FileName(pFileName), m_CountryCode(Code) {}

std::string m_Name;
std::string m_FileName;
int m_CountryCode;

bool operator<(const CLanguage &Other) const { return m_Name < Other.m_Name; }
};

void LoadLanguageIndexfile(IStorage *pStorage, IConsole *pConsole, std::vector<CLanguage> &vLanguages)
{
const char *pFilename = "languages/index.txt";
IOHANDLE File = pStorage->OpenFile(pFilename, IOFLAG_READ | IOFLAG_SKIP_BOM, IStorage::TYPE_ALL);
if(!File)
{
char aBuf[128];
str_format(aBuf, sizeof(aBuf), "couldn't open index file '%s'", pFilename);
pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", aBuf);
return;
}

char aOrigin[128];
char aReplacement[128];
CLineReader LineReader;
LineReader.Init(File);
char *pLine;
while((pLine = LineReader.Get()))
{
if(!str_length(pLine) || pLine[0] == '#') // skip empty lines and comments
continue;

str_copy(aOrigin, pLine);

pLine = LineReader.Get();
if(!pLine)
{
pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", "unexpected end of index file");
break;
}

if(pLine[0] != '=' || pLine[1] != '=' || pLine[2] != ' ')
{
char aBuf[128];
str_format(aBuf, sizeof(aBuf), "malform replacement for index '%s'", aOrigin);
pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", aBuf);
(void)LineReader.Get();
continue;
}
str_copy(aReplacement, pLine + 3);

pLine = LineReader.Get();
if(!pLine)
{
pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", "unexpected end of index file");
break;
}

if(pLine[0] != '=' || pLine[1] != '=' || pLine[2] != ' ')
{
char aBuf[128];
str_format(aBuf, sizeof(aBuf), "malform replacement for index '%s'", aOrigin);
pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", aBuf);
continue;
}

char aFileName[IO_MAX_PATH_LENGTH];
str_format(aFileName, sizeof(aFileName), "languages/%s.txt", aOrigin);
vLanguages.emplace_back(aReplacement, aFileName, str_toint(pLine + 3));
}
io_close(File);
}

bool CMenus::RenderLanguageSelection(CUIRect MainView)
{
static int s_SelectedLanguage = -1;
static std::vector<CLanguage> s_vLanguages;
static int s_SelectedLanguage = -2; // -2 = unloaded, -1 = unset
static CListBox s_ListBox;

if(s_vLanguages.empty())
if(s_SelectedLanguage == -2)
{
s_vLanguages.emplace_back("English", "", 826);
LoadLanguageIndexfile(Storage(), Console(), s_vLanguages);
std::sort(s_vLanguages.begin(), s_vLanguages.end());
for(size_t i = 0; i < s_vLanguages.size(); i++)
if(str_comp(s_vLanguages[i].m_FileName.c_str(), g_Config.m_ClLanguagefile) == 0)
s_SelectedLanguage = -1;
for(size_t i = 0; i < g_Localization.Languages().size(); i++)
{
if(str_comp(g_Localization.Languages()[i].m_FileName.c_str(), g_Config.m_ClLanguagefile) == 0)
{
s_SelectedLanguage = i;
s_ListBox.ScrollToSelected();
break;
}
}
}

const int OldSelected = s_SelectedLanguage;

s_ListBox.DoStart(24.0f, s_vLanguages.size(), 1, 3, s_SelectedLanguage, &MainView, true);
s_ListBox.DoStart(24.0f, g_Localization.Languages().size(), 1, 3, s_SelectedLanguage, &MainView, true);

for(auto &Language : s_vLanguages)
for(const auto &Language : g_Localization.Languages())
{
const CListboxItem Item = s_ListBox.DoNextItem(&Language.m_Name, s_SelectedLanguage != -1 && !str_comp(s_vLanguages[s_SelectedLanguage].m_Name.c_str(), Language.m_Name.c_str()));
const CListboxItem Item = s_ListBox.DoNextItem(&Language.m_Name, s_SelectedLanguage != -1 && !str_comp(g_Localization.Languages()[s_SelectedLanguage].m_Name.c_str(), Language.m_Name.c_str()));
if(!Item.m_Visible)
continue;

Expand All @@ -2149,8 +2119,8 @@ bool CMenus::RenderLanguageSelection(CUIRect MainView)

if(OldSelected != s_SelectedLanguage)
{
str_copy(g_Config.m_ClLanguagefile, s_vLanguages[s_SelectedLanguage].m_FileName.c_str());
g_Localization.Load(s_vLanguages[s_SelectedLanguage].m_FileName.c_str(), Storage(), Console());
str_copy(g_Config.m_ClLanguagefile, g_Localization.Languages()[s_SelectedLanguage].m_FileName.c_str());
g_Localization.Load(g_Localization.Languages()[s_SelectedLanguage].m_FileName.c_str(), Storage(), Console());
GameClient()->OnLanguageChange();
}

Expand Down
16 changes: 9 additions & 7 deletions src/game/client/components/menus_start.cpp
Expand Up @@ -134,9 +134,9 @@ void CMenus::RenderStartMenu(CUIRect MainView)
Menu.HSplitBottom(5.0f, &Menu, 0); // little space
Menu.HSplitBottom(40.0f, &Menu, &Button);
static CButtonContainer s_LocalServerButton;
if(DoButton_Menu(&s_LocalServerButton, m_ServerProcess.Process ? Localize("Stop server") : Localize("Run server"), 0, &Button, g_Config.m_ClShowStartMenuImages ? "local_server" : 0, IGraphics::CORNER_ALL, Rounding, 0.5f, vec4(0.0f, 0.0f, 0.0f, 0.5f), m_ServerProcess.Process ? vec4(0.0f, 1.0f, 0.0f, 0.25f) : vec4(0.0f, 0.0f, 0.0f, 0.25f)) || (CheckHotKey(KEY_R) && Input()->KeyPress(KEY_R)))
if(DoButton_Menu(&s_LocalServerButton, m_ServerProcess.m_Process ? Localize("Stop server") : Localize("Run server"), 0, &Button, g_Config.m_ClShowStartMenuImages ? "local_server" : 0, IGraphics::CORNER_ALL, Rounding, 0.5f, vec4(0.0f, 0.0f, 0.0f, 0.5f), m_ServerProcess.m_Process ? vec4(0.0f, 1.0f, 0.0f, 0.25f) : vec4(0.0f, 0.0f, 0.0f, 0.25f)) || (CheckHotKey(KEY_R) && Input()->KeyPress(KEY_R)))
{
if(m_ServerProcess.Process)
if(m_ServerProcess.m_Process)
{
KillServer();
}
Expand All @@ -147,12 +147,12 @@ void CMenus::RenderStartMenu(CUIRect MainView)
// No / in binary path means to search in $PATH, so it is expected that the file can't be opened. Just try executing anyway.
if(str_find(aBuf, "/") == 0)
{
m_ServerProcess.Process = shell_execute(aBuf);
m_ServerProcess.m_Process = shell_execute(aBuf);
}
else if(IOHANDLE File = io_open(aBuf, IOFLAG_READ))
{
io_close(File);
m_ServerProcess.Process = shell_execute(aBuf);
m_ServerProcess.m_Process = shell_execute(aBuf);
}
else
{
Expand Down Expand Up @@ -291,9 +291,11 @@ void CMenus::RenderStartMenu(CUIRect MainView)

void CMenus::KillServer()
{
if(m_ServerProcess.Process)
if(m_ServerProcess.m_Process)
{
kill_process(m_ServerProcess.Process);
m_ServerProcess.Process = 0;
if(kill_process(m_ServerProcess.m_Process))
{
m_ServerProcess.m_Process = INVALID_PROCESS;
}
}
}
69 changes: 55 additions & 14 deletions src/game/client/components/motd.cpp
Expand Up @@ -10,12 +10,21 @@

#include "motd.h"

CMotd::CMotd()
{
m_aServerMotd[0] = '\0';
m_ServerMotdTime = 0;
m_ServerMotdUpdateTime = 0;
}

void CMotd::Clear()
{
m_ServerMotdTime = 0;
Graphics()->DeleteQuadContainer(m_RectQuadContainer);
TextRender()->DeleteTextContainer(m_TextContainerIndex);
}

bool CMotd::IsActive()
bool CMotd::IsActive() const
{
return time() < m_ServerMotdTime;
}
Expand All @@ -26,24 +35,54 @@ void CMotd::OnStateChange(int NewState, int OldState)
Clear();
}

void CMotd::OnWindowResize()
{
Graphics()->DeleteQuadContainer(m_RectQuadContainer);
TextRender()->DeleteTextContainer(m_TextContainerIndex);
}

void CMotd::OnRender()
{
if(!IsActive())
return;

float Width = 400 * 3.0f * Graphics()->ScreenAspect();
float Height = 400 * 3.0f;
const float FontSize = 32.0f; // also the size of the margin and rect rounding
const float ScreenHeight = 40.0f * FontSize; // multiple of the font size to get perfect alignment
const float ScreenWidth = ScreenHeight * Graphics()->ScreenAspect();
Graphics()->MapScreen(0.0f, 0.0f, ScreenWidth, ScreenHeight);

Graphics()->MapScreen(0, 0, Width, Height);
const float RectHeight = 26.0f * FontSize;
const float RectWidth = 630.0f + 2.0f * FontSize;
const float RectX = ScreenWidth / 2.0f - RectWidth / 2.0f;
const float RectY = 160.0f;

float h = 800.0f;
float w = 650.0f;
float x = Width / 2 - w / 2;
float y = 150.0f;
if(m_RectQuadContainer == -1)
{
Graphics()->SetColor(0.0f, 0.0f, 0.0f, 0.5f);
m_RectQuadContainer = Graphics()->CreateRectQuadContainer(RectX, RectY, RectWidth, RectHeight, FontSize, IGraphics::CORNER_ALL);
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
}

if(m_RectQuadContainer != -1)
{
Graphics()->TextureClear();
Graphics()->RenderQuadContainer(m_RectQuadContainer, -1);
}

Graphics()->DrawRect(x, y, w, h, ColorRGBA(0.0f, 0.0f, 0.0f, 0.5f), IGraphics::CORNER_ALL, 40.0f);
const float TextWidth = RectWidth - 2.0f * FontSize;
const float TextX = RectX + FontSize;
const float TextY = RectY + FontSize;

if(m_TextContainerIndex == -1)
{
CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, TextX, TextY, FontSize, TEXTFLAG_RENDER);
Cursor.m_LineWidth = TextWidth;
TextRender()->CreateTextContainer(m_TextContainerIndex, &Cursor, ServerMotd());
}

TextRender()->Text(x + 40.0f, y + 40.0f, 32.0f, m_aServerMotd, w - 80.0f);
if(m_TextContainerIndex != -1)
TextRender()->RenderTextContainer(m_TextContainerIndex, TextRender()->DefaultTextColor(), TextRender()->DefaultTextOutlineColor());
}

void CMotd::OnMessage(int MsgType, void *pRawMsg)
Expand All @@ -53,13 +92,13 @@ void CMotd::OnMessage(int MsgType, void *pRawMsg)

if(MsgType == NETMSGTYPE_SV_MOTD)
{
CNetMsg_Sv_Motd *pMsg = (CNetMsg_Sv_Motd *)pRawMsg;
const CNetMsg_Sv_Motd *pMsg = static_cast<CNetMsg_Sv_Motd *>(pRawMsg);

// copy it manually to process all \n
const char *pMsgStr = pMsg->m_pMessage;
int MotdLen = str_length(pMsgStr) + 1;
const size_t MotdLen = str_length(pMsgStr) + 1;
const char *pLast = m_aServerMotd; // for console printing
for(int i = 0, k = 0; i < MotdLen && k < (int)sizeof(m_aServerMotd); i++, k++)
for(size_t i = 0, k = 0; i < MotdLen && k < sizeof(m_aServerMotd); i++, k++)
{
// handle incoming "\\n"
if(pMsgStr[i] == '\\' && pMsgStr[i + 1] == 'n')
Expand All @@ -83,10 +122,12 @@ void CMotd::OnMessage(int MsgType, void *pRawMsg)
if(g_Config.m_ClPrintMotd && *pLast != '\0')
m_pClient->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "motd", pLast, color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClMessageHighlightColor)));

m_ServerMotdUpdateTime = time();
if(m_aServerMotd[0] && g_Config.m_ClMotdTime)
m_ServerMotdTime = time() + time_freq() * g_Config.m_ClMotdTime;
m_ServerMotdTime = m_ServerMotdUpdateTime + time_freq() * g_Config.m_ClMotdTime;
else
m_ServerMotdTime = 0;
TextRender()->DeleteTextContainer(m_TextContainerIndex);
}
}

Expand Down
17 changes: 13 additions & 4 deletions src/game/client/components/motd.h
Expand Up @@ -2,22 +2,31 @@
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#ifndef GAME_CLIENT_COMPONENTS_MOTD_H
#define GAME_CLIENT_COMPONENTS_MOTD_H

#include <engine/shared/config.h>

#include <game/client/component.h>

class CMotd : public CComponent
{
// motd
char m_aServerMotd[std::size(g_Config.m_SvMotd)];
int64_t m_ServerMotdTime;
int64_t m_ServerMotdUpdateTime;
int m_RectQuadContainer = -1;
int m_TextContainerIndex = -1;

public:
char m_aServerMotd[900];

CMotd();
virtual int Sizeof() const override { return sizeof(*this); }

const char *ServerMotd() const { return m_aServerMotd; }
int64_t ServerMotdUpdateTime() const { return m_ServerMotdUpdateTime; }
void Clear();
bool IsActive();
bool IsActive() const;

virtual void OnRender() override;
virtual void OnStateChange(int NewState, int OldState) override;
virtual void OnWindowResize() override;
virtual void OnMessage(int MsgType, void *pRawMsg) override;
virtual bool OnInput(IInput::CEvent Event) override;
};
Expand Down
2 changes: 1 addition & 1 deletion src/game/client/components/nameplates.cpp
Expand Up @@ -84,7 +84,7 @@ void CNamePlates::RenderNameplatePos(vec2 Position, const CNetObj_PlayerInfo *pP
{
float a = 1;
if(g_Config.m_ClNameplatesAlways == 0)
a = clamp(1 - powf(distance(m_pClient->m_Controls.m_aTargetPos[g_Config.m_ClDummy], Position) / 200.0f, 16.0f), 0.0f, 1.0f);
a = clamp(1 - std::pow(distance(m_pClient->m_Controls.m_aTargetPos[g_Config.m_ClDummy], Position) / 200.0f, 16.0f), 0.0f, 1.0f);

const char *pName = m_pClient->m_aClients[pPlayerInfo->m_ClientID].m_aName;
if(str_comp(pName, m_aNamePlates[ClientID].m_aName) != 0 || FontSize != m_aNamePlates[ClientID].m_NameTextFontSize)
Expand Down
4 changes: 2 additions & 2 deletions src/game/client/components/particles.cpp
Expand Up @@ -107,7 +107,7 @@ void CParticles::Update(float TimePassed)
vec2 Vel = m_aParticles[i].m_Vel * TimePassed;
if(m_aParticles[i].m_Collides)
{
Collision()->MovePoint(&m_aParticles[i].m_Pos, &Vel, 0.1f + 0.9f * random_float(), NULL);
Collision()->MovePoint(&m_aParticles[i].m_Pos, &Vel, random_float(0.1f, 1.0f), NULL);
}
else
{
Expand Down Expand Up @@ -195,7 +195,7 @@ bool CParticles::ParticleIsVisibleOnScreen(const vec2 &CurPos, float CurSize)
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);

// for simplicity assume the worst case rotation, that increases the bounding box around the particle by its diagonal
const float SqrtOf2 = sqrtf(2);
const float SqrtOf2 = std::sqrt(2);
CurSize = SqrtOf2 * CurSize;

// always uses the mid of the particle
Expand Down
16 changes: 5 additions & 11 deletions src/game/client/components/players.cpp
Expand Up @@ -57,13 +57,7 @@ void CPlayers::RenderHand(CTeeRenderInfo *pInfo, vec2 CenterPos, vec2 Dir, float
}
}

inline float NormalizeAngular(float f)
{
return fmod(f + pi * 2, pi * 2);
}

inline float AngularMixDirection(float Src, float Dst) { return sinf(Dst - Src) > 0 ? 1 : -1; }
inline float AngularDistance(float Src, float Dst) { return asinf(sinf(Dst - Src)); }
inline float AngularMixDirection(float Src, float Dst) { return std::sin(Dst - Src) > 0 ? 1 : -1; }

inline float AngularApproach(float Src, float Dst, float Amount)
{
Expand Down Expand Up @@ -418,8 +412,8 @@ void CPlayers::RenderPlayer(
bool Inactive = m_pClient->m_aClients[ClientID].m_Afk || m_pClient->m_aClients[ClientID].m_Paused;

// evaluate animation
float WalkTime = fmod(Position.x, 100.0f) / 100.0f;
float RunTime = fmod(Position.x, 200.0f) / 200.0f;
float WalkTime = std::fmod(Position.x, 100.0f) / 100.0f;
float RunTime = std::fmod(Position.x, 200.0f) / 200.0f;

// Don't do a moon walk outside the left border
if(WalkTime < 0)
Expand Down Expand Up @@ -589,7 +583,7 @@ void CPlayers::RenderPlayer(
Recoil = 0;
float a = AttackTicksPassed / 5.0f;
if(a < 1)
Recoil = sinf(a * pi);
Recoil = std::sin(a * pi);
WeaponPosition = Position + Dir * g_pData->m_Weapons.m_aId[CurrentWeapon].m_Offsetx - Dir * Recoil * 10.0f;
WeaponPosition.y += g_pData->m_Weapons.m_aId[CurrentWeapon].m_Offsety;
if(IsSit)
Expand Down Expand Up @@ -726,7 +720,7 @@ void CPlayers::RenderPlayer(
if(SinceStart < Client()->GameTickSpeed() / 5)
Wiggle = SinceStart / (Client()->GameTickSpeed() / 5.0f);

float WiggleAngle = sinf(5 * Wiggle);
float WiggleAngle = std::sin(5 * Wiggle);

Graphics()->QuadsSetRotation(pi / 6 * WiggleAngle);

Expand Down
2 changes: 1 addition & 1 deletion src/game/client/components/scoreboard.cpp
Expand Up @@ -402,7 +402,7 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch
if(pInfo->m_Score == -9999)
aBuf[0] = 0;
else
str_time((int64_t)abs(pInfo->m_Score) * 100, TIME_HOURS, aBuf, sizeof(aBuf));
str_time((int64_t)absolute(pInfo->m_Score) * 100, TIME_HOURS, aBuf, sizeof(aBuf));
}
else
str_format(aBuf, sizeof(aBuf), "%d", clamp(pInfo->m_Score, -999, 99999));
Expand Down
7 changes: 1 addition & 6 deletions src/game/client/components/skins.cpp
Expand Up @@ -17,12 +17,7 @@

#include "skins.h"

static const char *VANILLA_SKINS[] = {"bluekitty", "bluestripe", "brownbear",
"cammo", "cammostripes", "coala", "default", "limekitty",
"pinky", "redbopp", "redstripe", "saddo", "toptri",
"twinbop", "twintri", "warpaint", "x_ninja", "x_spec"};

static bool IsVanillaSkin(const char *pName)
bool CSkins::IsVanillaSkin(const char *pName)
{
return std::any_of(std::begin(VANILLA_SKINS), std::end(VANILLA_SKINS), [pName](const char *pVanillaSkin) { return str_comp(pName, pVanillaSkin) == 0; });
}
Expand Down
7 changes: 7 additions & 0 deletions src/game/client/components/skins.h
Expand Up @@ -69,6 +69,13 @@ class CSkins : public CComponent

bool IsDownloadingSkins() { return m_DownloadingSkins; }

static bool IsVanillaSkin(const char *pName);

constexpr static const char *VANILLA_SKINS[] = {"bluekitty", "bluestripe", "brownbear",
"cammo", "cammostripes", "coala", "default", "limekitty",
"pinky", "redbopp", "redstripe", "saddo", "toptri",
"twinbop", "twintri", "warpaint", "x_ninja", "x_spec"};

private:
std::unordered_map<std::string_view, std::unique_ptr<CSkin>> m_Skins;
std::unordered_map<std::string_view, std::unique_ptr<CDownloadSkin>> m_DownloadSkins;
Expand Down
27 changes: 12 additions & 15 deletions src/game/client/gameclient.cpp
Expand Up @@ -225,7 +225,7 @@ void CGameClient::OnInit()

m_pGraphics = Kernel()->RequestInterface<IGraphics>();

m_pGraphics->AddWindowResizeListener(OnWindowResizeCB, this);
m_pGraphics->AddWindowResizeListener([this] { OnWindowResize(); });

// propagate pointers
m_UI.Init(Kernel());
Expand All @@ -243,6 +243,9 @@ void CGameClient::OnInit()
}

// set the language
g_Localization.LoadIndexfile(Storage(), Console());
if(g_Config.m_ClShowWelcome)
g_Localization.SelectDefaultLanguage(Console(), g_Config.m_ClLanguagefile, sizeof(g_Config.m_ClLanguagefile));
g_Localization.Load(g_Config.m_ClLanguagefile, Storage(), Console());

// TODO: this should be different
Expand Down Expand Up @@ -903,12 +906,6 @@ void CGameClient::OnWindowResize()
TextRender()->OnWindowResize();
}

void CGameClient::OnWindowResizeCB(void *pUser)
{
CGameClient *pClient = (CGameClient *)pUser;
pClient->OnWindowResize();
}

void CGameClient::OnLanguageChange()
{
UI()->OnLanguageChange();
Expand Down Expand Up @@ -1873,7 +1870,7 @@ void CGameClient::OnPredict()
static vec2 s_aLastPos[MAX_CLIENTS] = {{0, 0}};
static bool s_aLastActive[MAX_CLIENTS] = {false};

if(g_Config.m_ClAntiPingSmooth && Predict() && AntiPingPlayers() && m_NewTick && abs(m_PredictedTick - Client()->PredGameTick(g_Config.m_ClDummy)) <= 1 && abs(Client()->GameTick(g_Config.m_ClDummy) - Client()->PrevGameTick(g_Config.m_ClDummy)) <= 2)
if(g_Config.m_ClAntiPingSmooth && Predict() && AntiPingPlayers() && m_NewTick && absolute(m_PredictedTick - Client()->PredGameTick(g_Config.m_ClDummy)) <= 1 && absolute(Client()->GameTick(g_Config.m_ClDummy) - Client()->PrevGameTick(g_Config.m_ClDummy)) <= 2)
{
int PredTime = clamp(Client()->GetPredictionTime(), 0, 800);
float SmoothPace = 4 - 1.5f * PredTime / 800.f; // smoothing pace (a lower value will make the smoothing quicker)
Expand All @@ -1899,21 +1896,21 @@ void CGameClient::OnPredict()
for(int j = 0; j < 2; j++)
{
aMixAmount[j] = 1.0f;
if(fabs(PredErr[j]) > 0.05f)
if(absolute(PredErr[j]) > 0.05f)
{
aMixAmount[j] = 0.0f;
if(fabs(RenderDiff[j]) > 0.01f)
if(absolute(RenderDiff[j]) > 0.01f)
{
aMixAmount[j] = 1.f - clamp(RenderDiff[j] / PredDiff[j], 0.f, 1.f);
aMixAmount[j] = 1.f - powf(1.f - aMixAmount[j], 1 / 1.2f);
aMixAmount[j] = 1.f - std::pow(1.f - aMixAmount[j], 1 / 1.2f);
}
}
int64_t TimePassed = time_get() - m_aClients[i].m_aSmoothStart[j];
if(in_range(TimePassed, (int64_t)0, Len - 1))
aMixAmount[j] = minimum(aMixAmount[j], (float)(TimePassed / (double)Len));
}
for(int j = 0; j < 2; j++)
if(fabs(RenderDiff[j]) < 0.01f && fabs(PredDiff[j]) < 0.01f && fabs(m_aClients[i].m_PrevPredicted.m_Pos[j] - m_aClients[i].m_Predicted.m_Pos[j]) < 0.01f && aMixAmount[j] > aMixAmount[j ^ 1])
if(absolute(RenderDiff[j]) < 0.01f && absolute(PredDiff[j]) < 0.01f && absolute(m_aClients[i].m_PrevPredicted.m_Pos[j] - m_aClients[i].m_Predicted.m_Pos[j]) < 0.01f && aMixAmount[j] > aMixAmount[j ^ 1])
aMixAmount[j] = aMixAmount[j ^ 1];
for(int j = 0; j < 2; j++)
{
Expand Down Expand Up @@ -2385,7 +2382,7 @@ void CGameClient::UpdatePrediction()
}

// advance the gameworld to the current gametick
if(pLocalChar && abs(m_GameWorld.GameTick() - Client()->GameTick(g_Config.m_ClDummy)) < SERVER_TICK_SPEED)
if(pLocalChar && absolute(m_GameWorld.GameTick() - Client()->GameTick(g_Config.m_ClDummy)) < SERVER_TICK_SPEED)
{
for(int Tick = m_GameWorld.GameTick() + 1; Tick <= Client()->GameTick(g_Config.m_ClDummy); Tick++)
{
Expand Down Expand Up @@ -2518,7 +2515,7 @@ void CGameClient::DetectStrongHook()
int ToPlayer = m_Snap.m_aCharacters[FromPlayer].m_Prev.m_HookedPlayer;
if(ToPlayer < 0 || ToPlayer >= MAX_CLIENTS || !m_Snap.m_aCharacters[ToPlayer].m_Active || ToPlayer != m_Snap.m_aCharacters[FromPlayer].m_Cur.m_HookedPlayer)
continue;
if(abs(minimum(s_aLastUpdateTick[ToPlayer], s_aLastUpdateTick[FromPlayer]) - Client()->GameTick(g_Config.m_ClDummy)) < SERVER_TICK_SPEED / 4)
if(absolute(minimum(s_aLastUpdateTick[ToPlayer], s_aLastUpdateTick[FromPlayer]) - Client()->GameTick(g_Config.m_ClDummy)) < SERVER_TICK_SPEED / 4)
continue;
if(m_Snap.m_aCharacters[FromPlayer].m_Prev.m_Direction != m_Snap.m_aCharacters[FromPlayer].m_Cur.m_Direction || m_Snap.m_aCharacters[ToPlayer].m_Prev.m_Direction != m_Snap.m_aCharacters[ToPlayer].m_Cur.m_Direction)
continue;
Expand Down Expand Up @@ -2603,7 +2600,7 @@ vec2 CGameClient::GetSmoothPos(int ClientID)
int64_t TimePassed = Now - m_aClients[ClientID].m_aSmoothStart[i];
if(in_range(TimePassed, (int64_t)0, Len - 1))
{
float MixAmount = 1.f - powf(1.f - TimePassed / (float)Len, 1.2f);
float MixAmount = 1.f - std::pow(1.f - TimePassed / (float)Len, 1.2f);
int SmoothTick;
float SmoothIntra;
Client()->GetSmoothTick(&SmoothTick, &SmoothIntra, MixAmount);
Expand Down
1 change: 0 additions & 1 deletion src/game/client/gameclient.h
Expand Up @@ -474,7 +474,6 @@ class CGameClient : public IGameClient
virtual void OnFlagGrab(int TeamID);

void OnWindowResize();
static void OnWindowResizeCB(void *pUser);

void OnLanguageChange();

Expand Down
38 changes: 19 additions & 19 deletions src/game/client/prediction/entities/character.cpp
Expand Up @@ -402,7 +402,7 @@ void CCharacter::FireWeapon()
WEAPON_SHOTGUN, //Type
GetCID(), //Owner
ProjStartPos, //Pos
vec2(cosf(a), sinf(a)) * Speed, //Dir
direction(a) * Speed, //Dir
(int)(GameWorld()->GameTickSpeed() * Tuning()->m_ShotgunLifetime), //Span
false, //Freeze
false, //Explosive
Expand Down Expand Up @@ -489,7 +489,7 @@ void CCharacter::GiveNinja()
{
m_Core.m_Ninja.m_ActivationTick = GameWorld()->GameTick();
m_Core.m_aWeapons[WEAPON_NINJA].m_Got = true;
if(m_FreezeTime > 0)
if(m_FreezeTime == 0)
m_Core.m_aWeapons[WEAPON_NINJA].m_Ammo = -1;
if(m_Core.m_ActiveWeapon != WEAPON_NINJA)
m_LastWeapon = m_Core.m_ActiveWeapon;
Expand Down Expand Up @@ -643,36 +643,36 @@ void CCharacter::HandleSkippableTiles(int Index)
if(MaxSpeed > 0)
{
if(Direction.x > 0.0000001f)
SpeederAngle = -atan(Direction.y / Direction.x);
SpeederAngle = -std::atan(Direction.y / Direction.x);
else if(Direction.x < 0.0000001f)
SpeederAngle = atan(Direction.y / Direction.x) + 2.0f * asin(1.0f);
SpeederAngle = std::atan(Direction.y / Direction.x) + 2.0f * std::asin(1.0f);
else if(Direction.y > 0.0000001f)
SpeederAngle = asin(1.0f);
SpeederAngle = std::asin(1.0f);
else
SpeederAngle = asin(-1.0f);
SpeederAngle = std::asin(-1.0f);

if(SpeederAngle < 0)
SpeederAngle = 4.0f * asin(1.0f) + SpeederAngle;
SpeederAngle = 4.0f * std::asin(1.0f) + SpeederAngle;

if(TempVel.x > 0.0000001f)
TeeAngle = -atan(TempVel.y / TempVel.x);
TeeAngle = -std::atan(TempVel.y / TempVel.x);
else if(TempVel.x < 0.0000001f)
TeeAngle = atan(TempVel.y / TempVel.x) + 2.0f * asin(1.0f);
TeeAngle = std::atan(TempVel.y / TempVel.x) + 2.0f * std::asin(1.0f);
else if(TempVel.y > 0.0000001f)
TeeAngle = asin(1.0f);
TeeAngle = std::asin(1.0f);
else
TeeAngle = asin(-1.0f);
TeeAngle = std::asin(-1.0f);

if(TeeAngle < 0)
TeeAngle = 4.0f * asin(1.0f) + TeeAngle;
TeeAngle = 4.0f * std::asin(1.0f) + TeeAngle;

TeeSpeed = sqrt(pow(TempVel.x, 2) + pow(TempVel.y, 2));
TeeSpeed = std::sqrt(std::pow(TempVel.x, 2) + std::pow(TempVel.y, 2));

DiffAngle = SpeederAngle - TeeAngle;
SpeedLeft = MaxSpeed / 5.0f - cos(DiffAngle) * TeeSpeed;
if(abs((int)SpeedLeft) > Force && SpeedLeft > 0.0000001f)
SpeedLeft = MaxSpeed / 5.0f - std::cos(DiffAngle) * TeeSpeed;
if(absolute((int)SpeedLeft) > Force && SpeedLeft > 0.0000001f)
TempVel += Direction * Force;
else if(abs((int)SpeedLeft) > Force)
else if(absolute((int)SpeedLeft) > Force)
TempVel += Direction * -Force;
else
TempVel += Direction * SpeedLeft;
Expand Down Expand Up @@ -1205,7 +1205,7 @@ void CCharacter::Read(CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtende
{
if(m_FreezeTime == 0)
Freeze();
m_FreezeTime = pExtended->m_FreezeEnd - GameWorld()->GameTick();
m_FreezeTime = maximum(1, pExtended->m_FreezeEnd - GameWorld()->GameTick());
}
else if(pExtended->m_FreezeEnd == -1)
m_Core.m_DeepFrozen = true;
Expand Down Expand Up @@ -1334,8 +1334,8 @@ void CCharacter::Read(CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtende
}
else
{
m_Input.m_TargetX = m_SavedInput.m_TargetX = cosf(pChar->m_Angle / 256.0f) * 256.0f;
m_Input.m_TargetY = m_SavedInput.m_TargetY = sinf(pChar->m_Angle / 256.0f) * 256.0f;
m_Input.m_TargetX = m_SavedInput.m_TargetX = std::cos(pChar->m_Angle / 256.0f) * 256.0f;
m_Input.m_TargetY = m_SavedInput.m_TargetY = std::sin(pChar->m_Angle / 256.0f) * 256.0f;
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/game/client/prediction/entities/projectile.cpp
Expand Up @@ -122,9 +122,9 @@ void CProjectile::Tick()
m_Direction.x = -m_Direction.x;
else if(m_Bouncing == 2)
m_Direction.y = -m_Direction.y;
if(fabs(m_Direction.x) < 1e-6f)
if(absolute(m_Direction.x) < 1e-6f)
m_Direction.x = 0;
if(fabs(m_Direction.y) < 1e-6f)
if(absolute(m_Direction.y) < 1e-6f)
m_Direction.y = 0;
m_Pos += m_Direction;
}
Expand Down Expand Up @@ -172,7 +172,7 @@ CProjectile::CProjectile(CGameWorld *pGameWorld, int ID, CProjectileData *pProj,
m_Owner = -1;
m_Bouncing = 0;
m_Freeze = false;
m_Explosive = (pProj->m_Type == WEAPON_GRENADE) && (fabs(1.0f - length(m_Direction)) < 0.015f);
m_Explosive = (pProj->m_Type == WEAPON_GRENADE) && (absolute(1.0f - length(m_Direction)) < 0.015f);
}
m_Type = pProj->m_Type;
m_StartTick = pProj->m_StartTick;
Expand Down
2 changes: 1 addition & 1 deletion src/game/client/prediction/gameworld.cpp
Expand Up @@ -421,7 +421,7 @@ void CGameWorld::NetObjAdd(int ObjID, int ObjType, const void *pObjData, const C
}
CProjectile NetProj = CProjectile(this, ObjID, &Data, pDataEx);

if(NetProj.m_Type != WEAPON_SHOTGUN && fabs(length(NetProj.m_Direction) - 1.f) > 0.02f) // workaround to skip grenades on ball mod
if(NetProj.m_Type != WEAPON_SHOTGUN && absolute(length(NetProj.m_Direction) - 1.f) > 0.02f) // workaround to skip grenades on ball mod
return;

if(CProjectile *pProj = (CProjectile *)GetEntity(ObjID, ENTTYPE_PROJECTILE))
Expand Down
4 changes: 2 additions & 2 deletions src/game/client/projectile_data.cpp
Expand Up @@ -43,8 +43,8 @@ CProjectileData ExtractProjectileInfoDDNet(const CNetObj_DDNetProjectile *pProj,
Result.m_StartPos.x = pProj->m_X / 100.0f;
Result.m_StartPos.y = pProj->m_Y / 100.0f;
float Angle = pProj->m_Angle / 1000000.0f;
Result.m_StartVel.x = sin(-Angle);
Result.m_StartVel.y = cos(-Angle);
Result.m_StartVel.x = std::sin(-Angle);
Result.m_StartVel.y = std::cos(-Angle);
Result.m_Type = pProj->m_Type;
Result.m_StartTick = pProj->m_StartTick;

Expand Down
4 changes: 2 additions & 2 deletions src/game/client/render.cpp
Expand Up @@ -111,7 +111,7 @@ void CRenderTools::GetSpriteScale(int Id, float &ScaleX, float &ScaleY)

void CRenderTools::GetSpriteScaleImpl(int Width, int Height, float &ScaleX, float &ScaleY)
{
float f = sqrtf(Height * Height + Width * Width);
const float f = length(vec2(Width, Height));
ScaleX = Width / f;
ScaleY = Height / f;
}
Expand Down Expand Up @@ -381,7 +381,7 @@ void CRenderTools::CalcScreenParams(float Aspect, float Zoom, float *pWidth, flo
const float WMax = 1500;
const float HMax = 1050;

float f = sqrtf(Amount) / sqrtf(Aspect);
const float f = std::sqrt(Amount) / std::sqrt(Aspect);
*pWidth = f * Aspect;
*pHeight = f;

Expand Down
4 changes: 2 additions & 2 deletions src/game/client/render_map.cpp
Expand Up @@ -86,8 +86,8 @@ static void Rotate(CPoint *pCenter, CPoint *pPoint, float Rotation)
{
int x = pPoint->x - pCenter->x;
int y = pPoint->y - pCenter->y;
pPoint->x = (int)(x * cosf(Rotation) - y * sinf(Rotation) + pCenter->x);
pPoint->y = (int)(x * sinf(Rotation) + y * cosf(Rotation) + pCenter->y);
pPoint->x = (int)(x * std::cos(Rotation) - y * std::sin(Rotation) + pCenter->x);
pPoint->y = (int)(x * std::sin(Rotation) + y * std::cos(Rotation) + pCenter->y);
}

void CRenderTools::RenderQuads(CQuad *pQuads, int NumQuads, int RenderFlags, ENVELOPE_EVAL pfnEval, void *pUser)
Expand Down
64 changes: 64 additions & 0 deletions src/game/client/ui.cpp
Expand Up @@ -401,6 +401,67 @@ int CUI::DoButtonLogic(const void *pID, int Checked, const CUIRect *pRect)
return ReturnValue;
}

int CUI::DoDraggableButtonLogic(const void *pID, int Checked, const CUIRect *pRect, bool *pClicked, bool *pAbrupted)
{
// logic
int ReturnValue = 0;
const bool Inside = MouseHovered(pRect);
static int s_ButtonUsed = -1;

if(pClicked != nullptr)
*pClicked = false;
if(pAbrupted != nullptr)
*pAbrupted = false;

if(CheckActiveItem(pID))
{
if(s_ButtonUsed == 0)
{
if(Checked >= 0)
ReturnValue = 1 + s_ButtonUsed;
if(!MouseButton(s_ButtonUsed))
{
if(pClicked != nullptr)
*pClicked = true;
SetActiveItem(nullptr);
s_ButtonUsed = -1;
}
if(MouseButton(1))
{
if(pAbrupted != nullptr)
*pAbrupted = true;
SetActiveItem(nullptr);
s_ButtonUsed = -1;
}
}
else if(s_ButtonUsed > 0 && !MouseButton(s_ButtonUsed))
{
if(Inside && Checked >= 0)
ReturnValue = 1 + s_ButtonUsed;
if(pClicked != nullptr)
*pClicked = true;
SetActiveItem(nullptr);
s_ButtonUsed = -1;
}
}
else if(HotItem() == pID)
{
for(int i = 0; i < 3; ++i)
{
if(MouseButton(i))
{
SetActiveItem(pID);
s_ButtonUsed = i;
}
}
}

if(Inside && !MouseButton(0) && !MouseButton(1) && !MouseButton(2))
SetHotItem(pID);

return ReturnValue;
}

int CUI::DoPickerLogic(const void *pID, const CUIRect *pRect, float *pX, float *pY)
{
if(MouseHovered(pRect))
Expand All @@ -415,6 +476,9 @@ int CUI::DoPickerLogic(const void *pID, const CUIRect *pRect, float *pX, float *
if(!CheckActiveItem(pID))
return 0;

if(Input()->ShiftIsPressed())
m_MouseSlow = true;

if(pX)
*pX = clamp(m_MouseX - pRect->x, 0.0f, pRect->w);
if(pY)
Expand Down
5 changes: 3 additions & 2 deletions src/game/client/ui.h
Expand Up @@ -67,7 +67,7 @@ class CLogarithmicScrollbarScale : public IScrollbarScale
Min += m_MinAdjustment;
Max += m_MinAdjustment;
}
return (log(AbsoluteValue) - log(Min)) / (float)(log(Max) - log(Min));
return (std::log(AbsoluteValue) - std::log(Min)) / (float)(std::log(Max) - std::log(Min));
}
int ToAbsolute(float RelativeValue, int Min, int Max) const override
{
Expand All @@ -78,7 +78,7 @@ class CLogarithmicScrollbarScale : public IScrollbarScale
Max += m_MinAdjustment;
ResultAdjustment = -m_MinAdjustment;
}
return round_to_int(exp(RelativeValue * (log(Max) - log(Min)) + log(Min))) + ResultAdjustment;
return round_to_int(std::exp(RelativeValue * (std::log(Max) - std::log(Min)) + std::log(Min))) + ResultAdjustment;
}
};

Expand Down Expand Up @@ -341,6 +341,7 @@ class CUI
inline bool IsClipped() const { return !m_vClips.empty(); }

int DoButtonLogic(const void *pID, int Checked, const CUIRect *pRect);
int DoDraggableButtonLogic(const void *pID, int Checked, const CUIRect *pRect, bool *pClicked, bool *pAbrupted);
int DoPickerLogic(const void *pID, const CUIRect *pRect, float *pX, float *pY);
void DoSmoothScrollLogic(float *pScrollOffset, float *pScrollOffsetChange, float ViewPortSize, float TotalSize, float ScrollSpeed = 10.0f);

Expand Down
64 changes: 49 additions & 15 deletions src/game/client/ui_scrollregion.cpp
Expand Up @@ -13,10 +13,15 @@ CScrollRegion::CScrollRegion()
{
m_ScrollY = 0.0f;
m_ContentH = 0.0f;
m_RequestScrollY = -1.0f;
m_ScrollDirection = SCROLLRELATIVE_NONE;
m_ScrollSpeedMultiplier = 1.0f;

m_AnimTimeMax = 0.0f;
m_AnimTime = 0.0f;
m_AnimInitScrollY = 0.0f;
m_AnimTargetScrollY = 0.0f;
m_RequestScrollY = -1.0f;

m_ContentScrollOff = vec2(0.0f, 0.0f);
m_Params = CScrollRegionParams();
}
Expand Down Expand Up @@ -68,23 +73,30 @@ void CScrollRegion::End()
CUIRect RegionRect = m_ClipRect;
RegionRect.w += m_Params.m_ScrollbarWidth;

const float AnimationDuration = g_Config.m_UiSmoothScrollTime / 1000.0f;

if(UI()->Enabled() && UI()->MouseHovered(&RegionRect))
if(m_ScrollDirection != SCROLLRELATIVE_NONE || (UI()->Enabled() && UI()->MouseHovered(&RegionRect)))
{
const bool IsPageScroll = Input()->AltIsPressed();
const float ScrollUnit = IsPageScroll ? m_ClipRect.h : m_Params.m_ScrollUnit;
bool ProgrammaticScroll = false;
if(UI()->ConsumeHotkey(CUI::HOTKEY_SCROLL_UP))
{
m_AnimTime = AnimationDuration;
m_AnimInitScrollY = m_ScrollY;
m_AnimTargetScrollY -= ScrollUnit;
}
m_ScrollDirection = SCROLLRELATIVE_UP;
else if(UI()->ConsumeHotkey(CUI::HOTKEY_SCROLL_DOWN))
m_ScrollDirection = SCROLLRELATIVE_DOWN;
else
ProgrammaticScroll = true;

if(!ProgrammaticScroll)
m_ScrollSpeedMultiplier = 1.0f;

if(m_ScrollDirection != SCROLLRELATIVE_NONE)
{
m_AnimTime = AnimationDuration;
const bool IsPageScroll = Input()->AltIsPressed();
const float ScrollUnit = IsPageScroll && !ProgrammaticScroll ? m_ClipRect.h : m_Params.m_ScrollUnit;

m_AnimTimeMax = g_Config.m_UiSmoothScrollTime / 1000.0f;
m_AnimTime = m_AnimTimeMax;
m_AnimInitScrollY = m_ScrollY;
m_AnimTargetScrollY += ScrollUnit;
m_AnimTargetScrollY = (ProgrammaticScroll ? m_ScrollY : m_AnimTargetScrollY) + (int)m_ScrollDirection * ScrollUnit * m_ScrollSpeedMultiplier;
m_ScrollDirection = SCROLLRELATIVE_NONE;
m_ScrollSpeedMultiplier = 1.0f;
}
}

Expand All @@ -111,7 +123,7 @@ void CScrollRegion::End()
if(m_AnimTime > 0.0f)
{
m_AnimTime -= Client()->RenderFrameTime();
float AnimProgress = (1.0f - powf(m_AnimTime / AnimationDuration, 3.0f)); // cubic ease out
float AnimProgress = (1.0f - std::pow(m_AnimTime / m_AnimTimeMax, 3.0f)); // cubic ease out
m_ScrollY = m_AnimInitScrollY + (m_AnimTargetScrollY - m_AnimInitScrollY) * AnimProgress;
}
else
Expand Down Expand Up @@ -173,7 +185,7 @@ bool CScrollRegion::AddRect(const CUIRect &Rect, bool ShouldScrollHere)
{
m_LastAddedRect = Rect;
// Round up and add 1 to fix pixel clipping at the end of the scrolling area
m_ContentH = maximum(ceilf(Rect.y + Rect.h - (m_ClipRect.y + m_ContentScrollOff.y)) + 1.0f, m_ContentH);
m_ContentH = maximum(std::ceil(Rect.y + Rect.h - (m_ClipRect.y + m_ContentScrollOff.y)) + 1.0f, m_ContentH);
if(ShouldScrollHere)
ScrollHere();
return !IsRectClipped(Rect);
Expand Down Expand Up @@ -205,6 +217,28 @@ void CScrollRegion::ScrollHere(EScrollOption Option)
}
}

void CScrollRegion::ScrollRelative(EScrollRelative Direction, float SpeedMultiplier)
{
m_ScrollDirection = Direction;
m_ScrollSpeedMultiplier = SpeedMultiplier;
}

void CScrollRegion::DoEdgeScrolling()
{
if(!IsScrollbarShown())
return;

const float ScrollBorderSize = 20.0f;
const float MaxScrollMultiplier = 2.0f;
const float ScrollSpeedFactor = MaxScrollMultiplier / ScrollBorderSize;
const float TopScrollPosition = m_ClipRect.y + ScrollBorderSize;
const float BottomScrollPosition = m_ClipRect.y + m_ClipRect.h - ScrollBorderSize;
if(UI()->MouseY() < TopScrollPosition)
ScrollRelative(SCROLLRELATIVE_UP, minimum(MaxScrollMultiplier, (TopScrollPosition - UI()->MouseY()) * ScrollSpeedFactor));
else if(UI()->MouseY() > BottomScrollPosition)
ScrollRelative(SCROLLRELATIVE_DOWN, minimum(MaxScrollMultiplier, (UI()->MouseY() - BottomScrollPosition) * ScrollSpeedFactor));
}

bool CScrollRegion::IsRectClipped(const CUIRect &Rect) const
{
return (m_ClipRect.x > (Rect.x + Rect.w) || (m_ClipRect.x + m_ClipRect.w) < Rect.x || m_ClipRect.y > (Rect.y + Rect.h) || (m_ClipRect.y + m_ClipRect.h) < Rect.y);
Expand Down
14 changes: 14 additions & 0 deletions src/game/client/ui_scrollregion.h
Expand Up @@ -86,11 +86,22 @@ struct CScrollRegionParams
// Instances of CScrollRegion must be static, as member addresses are used as UI item IDs
class CScrollRegion : private CUIElementBase
{
public:
enum EScrollRelative
{
SCROLLRELATIVE_UP = -1,
SCROLLRELATIVE_NONE = 0,
SCROLLRELATIVE_DOWN = 1,
};

private:
float m_ScrollY;
float m_ContentH;
float m_RequestScrollY; // [0, ContentHeight]
EScrollRelative m_ScrollDirection;
float m_ScrollSpeedMultiplier;

float m_AnimTimeMax;
float m_AnimTime;
float m_AnimInitScrollY;
float m_AnimTargetScrollY;
Expand All @@ -115,6 +126,9 @@ class CScrollRegion : private CUIElementBase
void End();
bool AddRect(const CUIRect &Rect, bool ShouldScrollHere = false); // returns true if the added rect is visible (not clipped)
void ScrollHere(EScrollOption Option = SCROLLHERE_KEEP_IN_VIEW);
void ScrollRelative(EScrollRelative Direction, float SpeedMultiplier = 1.0f);
const CUIRect *ClipRect() const { return &m_ClipRect; }
void DoEdgeScrolling();
bool IsRectClipped(const CUIRect &Rect) const;
bool IsScrollbarShown() const;
bool IsAnimating() const;
Expand Down
12 changes: 6 additions & 6 deletions src/game/collision.cpp
Expand Up @@ -724,7 +724,7 @@ void CCollision::GetSpeedup(int Index, vec2 *pDir, int *pForce, int *pMaxSpeed)
return;
float Angle = m_pSpeedup[Index].m_Angle * (pi / 180.0f);
*pForce = m_pSpeedup[Index].m_Force;
*pDir = vec2(cos(Angle), sin(Angle));
*pDir = direction(Angle);
if(pMaxSpeed)
*pMaxSpeed = m_pSpeedup[Index].m_MaxSpeed;
}
Expand Down Expand Up @@ -993,7 +993,7 @@ int CCollision::GetIndex(vec2 PrevPos, vec2 Pos) const
}
}

for(int i = 0, id = (int)ceilf(Distance); i < id; i++)
for(int i = 0, id = std::ceil(Distance); i < id; i++)
{
float a = (float)i / Distance;
vec2 Tmp = mix(PrevPos, Pos, a);
Expand Down Expand Up @@ -1126,7 +1126,7 @@ void ThroughOffset(vec2 Pos0, vec2 Pos1, int *pOffsetX, int *pOffsetY)
{
float x = Pos0.x - Pos1.x;
float y = Pos0.y - Pos1.y;
if(fabs(x) > fabs(y))
if(absolute(x) > absolute(y))
{
if(x < 0)
{
Expand Down Expand Up @@ -1159,7 +1159,7 @@ int CCollision::IntersectNoLaser(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2
float d = distance(Pos0, Pos1);
vec2 Last = Pos0;

for(int i = 0, id = (int)ceilf(d); i < id; i++)
for(int i = 0, id = std::ceil(d); i < id; i++)
{
float a = (int)i / d;
vec2 Pos = mix(Pos0, Pos1, a);
Expand Down Expand Up @@ -1190,7 +1190,7 @@ int CCollision::IntersectNoLaserNW(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, ve
float d = distance(Pos0, Pos1);
vec2 Last = Pos0;

for(int i = 0, id = (int)ceilf(d); i < id; i++)
for(int i = 0, id = std::ceil(d); i < id; i++)
{
float a = (float)i / d;
vec2 Pos = mix(Pos0, Pos1, a);
Expand Down Expand Up @@ -1219,7 +1219,7 @@ int CCollision::IntersectAir(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pO
float d = distance(Pos0, Pos1);
vec2 Last = Pos0;

for(int i = 0, id = (int)ceilf(d); i < id; i++)
for(int i = 0, id = std::ceil(d); i < id; i++)
{
float a = (float)i / d;
vec2 Pos = mix(Pos0, Pos1, a);
Expand Down
856 changes: 704 additions & 152 deletions src/game/editor/editor.cpp

Large diffs are not rendered by default.

108 changes: 92 additions & 16 deletions src/game/editor/editor.h
Expand Up @@ -301,6 +301,7 @@ class CEditorImage : public CImageInfo
{
m_pEditor = pEditor;
m_aName[0] = 0;
m_Texture.Invalidate();
m_External = 0;
m_Width = 0;
m_Height = 0;
Expand Down Expand Up @@ -746,6 +747,13 @@ class CEditor : public IEditor

int GetTextureUsageFlag();

enum EPreviewImageState
{
PREVIEWIMAGE_UNLOADED,
PREVIEWIMAGE_LOADED,
PREVIEWIMAGE_ERROR,
};

public:
class IInput *Input() { return m_pInput; }
class IClient *Client() { return m_pClient; }
Expand All @@ -767,6 +775,13 @@ class CEditor : public IEditor
m_pTextRender = nullptr;
m_pSound = nullptr;

m_EntitiesTexture.Invalidate();
m_FrontTexture.Invalidate();
m_TeleTexture.Invalidate();
m_SpeedupTexture.Invalidate();
m_SwitchTexture.Invalidate();
m_TuneTexture.Invalidate();

m_Mode = MODE_LAYERS;
m_Dialog = 0;
m_EditBoxActive = 0;
Expand Down Expand Up @@ -799,6 +814,9 @@ class CEditor : public IEditor
m_FileDialogOpening = false;
m_FilesSelectedIndex = -1;

m_FilePreviewImage.Invalidate();
m_PreviewImageState = PREVIEWIMAGE_UNLOADED;

m_SelectEntitiesImage = "DDNet";

m_WorldOffsetX = 0;
Expand Down Expand Up @@ -838,6 +856,10 @@ class CEditor : public IEditor
m_QuadKnifeActive = false;
m_QuadKnifeCount = 0;

m_CheckerTexture.Invalidate();
m_BackgroundTexture.Invalidate();
m_CursorTexture.Invalidate();

m_CommandBox = 0.0f;
m_aSettingsCommand[0] = 0;

Expand Down Expand Up @@ -876,7 +898,9 @@ class CEditor : public IEditor
void FilelistPopulate(int StorageType, bool KeepSelection = false);
void InvokeFileDialog(int StorageType, int FileType, const char *pTitle, const char *pButtonText,
const char *pBasepath, const char *pDefaultName,
void (*pfnFunc)(const char *pFilename, int StorageType, void *pUser), void *pUser);
bool (*pfnFunc)(const char *pFilename, int StorageType, void *pUser), void *pUser);
void ShowFileDialogError(const char *pFormat, ...)
GNUC_ATTRIBUTE((format(printf, 2, 3)));

void Reset(bool CreateDefault = true);
bool Save(const char *pFilename) override;
Expand Down Expand Up @@ -951,7 +975,7 @@ class CEditor : public IEditor
int m_FileDialogLastPopulatedStorageType;
const char *m_pFileDialogTitle;
const char *m_pFileDialogButtonText;
void (*m_pfnFileDialogFunc)(const char *pFileName, int StorageType, void *pUser);
bool (*m_pfnFileDialogFunc)(const char *pFileName, int StorageType, void *pUser);
void *m_pFileDialogUser;
char m_aFileDialogFileName[IO_MAX_PATH_LENGTH];
char m_aFileDialogCurrentFolder[IO_MAX_PATH_LENGTH];
Expand All @@ -962,9 +986,8 @@ class CEditor : public IEditor
int m_FileDialogFileType;
int m_FilesSelectedIndex;
char m_aFileDialogNewFolderName[IO_MAX_PATH_LENGTH];
char m_aFileDialogErrString[64];
IGraphics::CTextureHandle m_FilePreviewImage;
bool m_PreviewImageIsLoaded;
EPreviewImageState m_PreviewImageState;
CImageInfo m_FilePreviewImageInfo;
bool m_FileDialogOpening;

Expand All @@ -975,12 +998,63 @@ class CEditor : public IEditor
bool m_IsDir;
bool m_IsLink;
int m_StorageType;

bool operator<(const CFilelistItem &Other) const { return !str_comp(m_aFilename, "..") ? true : !str_comp(Other.m_aFilename, "..") ? false : m_IsDir && !Other.m_IsDir ? true : !m_IsDir && Other.m_IsDir ? false : str_comp_filenames(m_aFilename, Other.m_aFilename) < 0; }
time_t m_TimeModified;
};
std::vector<CFilelistItem> m_vCompleteFileList;
std::vector<const CFilelistItem *> m_vpFilteredFileList;

static bool CompareFilenameAscending(const CFilelistItem *pLhs, const CFilelistItem *pRhs)
{
if(str_comp(pLhs->m_aFilename, "..") == 0)
return true;
if(str_comp(pRhs->m_aFilename, "..") == 0)
return false;
if(pLhs->m_IsDir != pRhs->m_IsDir)
return pLhs->m_IsDir;
return str_comp_filenames(pLhs->m_aName, pRhs->m_aName) < 0;
}

static bool CompareFilenameDescending(const CFilelistItem *pLhs, const CFilelistItem *pRhs)
{
if(str_comp(pLhs->m_aFilename, "..") == 0)
return true;
if(str_comp(pRhs->m_aFilename, "..") == 0)
return false;
if(pLhs->m_IsDir != pRhs->m_IsDir)
return pLhs->m_IsDir;
return str_comp_filenames(pLhs->m_aName, pRhs->m_aName) > 0;
}

static bool CompareTimeModifiedAscending(const CFilelistItem *pLhs, const CFilelistItem *pRhs)
{
if(str_comp(pLhs->m_aFilename, "..") == 0)
return true;
if(str_comp(pRhs->m_aFilename, "..") == 0)
return false;
if(pLhs->m_IsLink || pRhs->m_IsLink)
return pLhs->m_IsLink;
if(pLhs->m_IsDir != pRhs->m_IsDir)
return pLhs->m_IsDir;
return pLhs->m_TimeModified < pRhs->m_TimeModified;
}

static bool CompareTimeModifiedDescending(const CFilelistItem *pLhs, const CFilelistItem *pRhs)
{
if(str_comp(pLhs->m_aFilename, "..") == 0)
return true;
if(str_comp(pRhs->m_aFilename, "..") == 0)
return false;
if(pLhs->m_IsLink || pRhs->m_IsLink)
return pLhs->m_IsLink;
if(pLhs->m_IsDir != pRhs->m_IsDir)
return pLhs->m_IsDir;
return pLhs->m_TimeModified > pRhs->m_TimeModified;
}

void SortFilteredFileList();
int m_SortByFilename = 1;
int m_SortByTimeModified = 0;

std::vector<std::string> m_vSelectEntitiesFiles;
std::string m_SelectEntitiesImage;

Expand Down Expand Up @@ -1089,6 +1163,8 @@ class CEditor : public IEditor

int DoButton_ColorPicker(const void *pID, const CUIRect *pRect, ColorRGBA *pColor, const char *pToolTip = nullptr);

int DoButton_DraggableEx(const void *pID, const char *pText, int Checked, const CUIRect *pRect, bool *pClicked, bool *pAbrupted, int Flags, const char *pToolTip = nullptr, int Corners = IGraphics::CORNER_ALL, float FontSize = 10.0f, int AlignVert = 1);

bool DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, float *pOffset, bool Hidden = false, int Corners = IGraphics::CORNER_ALL, const char *pToolTip = nullptr);
bool DoClearableEditBox(void *pID, void *pClearID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, float *pOffset, bool Hidden = false, int Corners = IGraphics::CORNER_ALL, const char *pToolTip = nullptr);

Expand All @@ -1103,7 +1179,7 @@ class CEditor : public IEditor
bool UiPopupExists(void *pID);
bool UiPopupOpen();

int UiDoValueSelector(void *pID, CUIRect *pRect, const char *pLabel, int Current, int Min, int Max, int Step, float Scale, const char *pToolTip, bool IsDegree = false, bool IsHex = false, int corners = IGraphics::CORNER_ALL, ColorRGBA *pColor = nullptr);
int UiDoValueSelector(void *pID, CUIRect *pRect, const char *pLabel, int Current, int Min, int Max, int Step, float Scale, const char *pToolTip, bool IsDegree = false, bool IsHex = false, int corners = IGraphics::CORNER_ALL, ColorRGBA *pColor = nullptr, bool ShowValue = true);

static int PopupGroup(CEditor *pEditor, CUIRect View, void *pContext);

Expand Down Expand Up @@ -1134,7 +1210,7 @@ class CEditor : public IEditor
{
static constexpr float POPUP_MAX_WIDTH = 200.0f;
static constexpr float POPUP_FONT_SIZE = 10.0f;
char m_aMessage[256];
char m_aMessage[1024];
ColorRGBA m_TextColor;

void DefaultColor(class ITextRender *pTextRender);
Expand Down Expand Up @@ -1180,10 +1256,10 @@ class CEditor : public IEditor
static int PopupSelection(CEditor *pEditor, CUIRect View, void *pContext);
void ShowPopupSelection(float X, float Y, SSelectionPopupContext *pContext);

static void CallbackOpenMap(const char *pFileName, int StorageType, void *pUser);
static void CallbackAppendMap(const char *pFileName, int StorageType, void *pUser);
static void CallbackSaveMap(const char *pFileName, int StorageType, void *pUser);
static void CallbackSaveCopyMap(const char *pFileName, int StorageType, void *pUser);
static bool CallbackOpenMap(const char *pFileName, int StorageType, void *pUser);
static bool CallbackAppendMap(const char *pFileName, int StorageType, void *pUser);
static bool CallbackSaveMap(const char *pFileName, int StorageType, void *pUser);
static bool CallbackSaveCopyMap(const char *pFileName, int StorageType, void *pUser);

void PopupSelectImageInvoke(int Current, float x, float y);
int PopupSelectImageResult();
Expand Down Expand Up @@ -1212,10 +1288,10 @@ class CEditor : public IEditor
void DoQuad(CQuad *pQuad, int Index);
ColorRGBA GetButtonColor(const void *pID, int Checked);

static void ReplaceImage(const char *pFilename, int StorageType, void *pUser);
static void ReplaceSound(const char *pFileName, int StorageType, void *pUser);
static void AddImage(const char *pFilename, int StorageType, void *pUser);
static void AddSound(const char *pFileName, int StorageType, void *pUser);
static bool ReplaceImage(const char *pFilename, int StorageType, void *pUser);
static bool ReplaceSound(const char *pFileName, int StorageType, void *pUser);
static bool AddImage(const char *pFilename, int StorageType, void *pUser);
static bool AddSound(const char *pFileName, int StorageType, void *pUser);

bool IsEnvelopeUsed(int EnvelopeIndex) const;
void RemoveUnusedEnvelopes();
Expand Down
4 changes: 2 additions & 2 deletions src/game/editor/layer_quads.cpp
Expand Up @@ -180,8 +180,8 @@ void Rotate(vec2 *pCenter, vec2 *pPoint, float Rotation)
{
float x = pPoint->x - pCenter->x;
float y = pPoint->y - pCenter->y;
pPoint->x = x * cosf(Rotation) - y * sinf(Rotation) + pCenter->x;
pPoint->y = x * sinf(Rotation) + y * cosf(Rotation) + pCenter->y;
pPoint->x = x * std::cos(Rotation) - y * std::sin(Rotation) + pCenter->x;
pPoint->y = x * std::sin(Rotation) + y * std::cos(Rotation) + pCenter->y;
}

void CLayerQuads::BrushRotate(float Amount)
Expand Down
7 changes: 6 additions & 1 deletion src/game/editor/layer_tiles.cpp
Expand Up @@ -18,6 +18,7 @@ CLayerTiles::CLayerTiles(int w, int h)
m_aName[0] = '\0';
m_Width = w;
m_Height = h;
m_Texture.Invalidate();
m_Image = -1;
m_Game = 0;
m_Color.r = 255;
Expand Down Expand Up @@ -910,7 +911,7 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox)
m_pEditor->m_PopupEventType = m_pEditor->POPEVENT_IMAGEDIV16;
m_pEditor->m_PopupEventActivated = true;

m_Texture = IGraphics::CTextureHandle();
m_Texture.Invalidate();
m_Image = -1;
}
}
Expand Down Expand Up @@ -1311,6 +1312,10 @@ bool CLayerTele::ContainsElementWithId(int Id)
{
for(int x = 0; x < m_Width; ++x)
{
if(m_pTeleTile[y * m_Width + x].m_Type == TILE_TELECHECKIN)
continue;
if(m_pTeleTile[y * m_Width + x].m_Type == TILE_TELECHECKINEVIL)
continue;
if(m_pTeleTile[y * m_Width + x].m_Number == Id)
{
return true;
Expand Down
110 changes: 47 additions & 63 deletions src/game/editor/popups.cpp
Expand Up @@ -86,7 +86,7 @@ void CEditor::UiDoPopupMenu()
r.Draw(ColorRGBA(0, 0, 0, 0.75f), Corners, 3.0f);
r.Margin(4.0f, &r);

if(s_UiPopups[i].m_pfnFunc(this, r, s_UiPopups[i].m_pContext) || Input()->KeyPress(KEY_ESCAPE))
if(s_UiPopups[i].m_pfnFunc(this, r, s_UiPopups[i].m_pContext) || UI()->ConsumeHotkey(CUI::HOTKEY_ESCAPE))
UiClosePopupMenus(1);
}
}
Expand Down Expand Up @@ -703,10 +703,10 @@ int CEditor::PopupQuad(CEditor *pEditor, CUIRect View, void *pContext)
if(Prop == PROP_POS_ENV)
{
int Index = clamp(NewVal - 1, -1, (int)pEditor->m_Map.m_vpEnvelopes.size() - 1);
int Step = (Index - pQuad->m_PosEnv) % 2;
if(Step != 0)
int StepDirection = Index < pQuad->m_PosEnv ? -1 : 1;
if(StepDirection != 0)
{
for(; Index >= -1 && Index < (int)pEditor->m_Map.m_vpEnvelopes.size(); Index += Step)
for(; Index >= -1 && Index < (int)pEditor->m_Map.m_vpEnvelopes.size(); Index += StepDirection)
if(Index == -1 || pEditor->m_Map.m_vpEnvelopes[Index]->m_Channels == 3)
{
pQuad->m_PosEnv = Index;
Expand All @@ -719,10 +719,10 @@ int CEditor::PopupQuad(CEditor *pEditor, CUIRect View, void *pContext)
if(Prop == PROP_COLOR_ENV)
{
int Index = clamp(NewVal - 1, -1, (int)pEditor->m_Map.m_vpEnvelopes.size() - 1);
int Step = (Index - pQuad->m_ColorEnv) % 2;
if(Step != 0)
int StepDirection = Index < pQuad->m_ColorEnv ? -1 : 1;
if(StepDirection != 0)
{
for(; Index >= -1 && Index < (int)pEditor->m_Map.m_vpEnvelopes.size(); Index += Step)
for(; Index >= -1 && Index < (int)pEditor->m_Map.m_vpEnvelopes.size(); Index += StepDirection)
if(Index == -1 || pEditor->m_Map.m_vpEnvelopes[Index]->m_Channels == 4)
{
pQuad->m_ColorEnv = Index;
Expand Down Expand Up @@ -842,10 +842,10 @@ int CEditor::PopupSource(CEditor *pEditor, CUIRect View, void *pContext)
if(Prop == PROP_POS_ENV)
{
int Index = clamp(NewVal - 1, -1, (int)pEditor->m_Map.m_vpEnvelopes.size() - 1);
int Step = (Index - pSource->m_PosEnv) % 2;
if(Step != 0)
int StepDirection = Index < pSource->m_PosEnv ? -1 : 1;
if(StepDirection != 0)
{
for(; Index >= -1 && Index < (int)pEditor->m_Map.m_vpEnvelopes.size(); Index += Step)
for(; Index >= -1 && Index < (int)pEditor->m_Map.m_vpEnvelopes.size(); Index += StepDirection)
if(Index == -1 || pEditor->m_Map.m_vpEnvelopes[Index]->m_Channels == 3)
{
pSource->m_PosEnv = Index;
Expand All @@ -858,10 +858,10 @@ int CEditor::PopupSource(CEditor *pEditor, CUIRect View, void *pContext)
if(Prop == PROP_SOUND_ENV)
{
int Index = clamp(NewVal - 1, -1, (int)pEditor->m_Map.m_vpEnvelopes.size() - 1);
int Step = (Index - pSource->m_SoundEnv) % 2;
if(Step != 0)
int StepDirection = Index < pSource->m_SoundEnv ? -1 : 1;
if(StepDirection != 0)
{
for(; Index >= -1 && Index < (int)pEditor->m_Map.m_vpEnvelopes.size(); Index += Step)
for(; Index >= -1 && Index < (int)pEditor->m_Map.m_vpEnvelopes.size(); Index += StepDirection)
if(Index == -1 || pEditor->m_Map.m_vpEnvelopes[Index]->m_Channels == 1)
{
pSource->m_SoundEnv = Index;
Expand Down Expand Up @@ -1034,61 +1034,42 @@ int CEditor::PopupNewFolder(CEditor *pEditor, CUIRect View, void *pContext)
View.HSplitBottom(10.0f, &View, nullptr);
View.HSplitBottom(20.0f, &View, &ButtonBar);

if(pEditor->m_aFileDialogErrString[0] == 0)
{
// interaction box
View.HSplitBottom(40.0f, &View, nullptr);
View.VMargin(40.0f, &View);
View.HSplitBottom(20.0f, &View, &Label);
static float s_FolderBox = 0;
pEditor->DoEditBox(&s_FolderBox, &Label, pEditor->m_aFileDialogNewFolderName, sizeof(pEditor->m_aFileDialogNewFolderName), 15.0f, &s_FolderBox);
View.HSplitBottom(20.0f, &View, &Label);
pEditor->UI()->DoLabel(&Label, "Name:", 10.0f, TEXTALIGN_LEFT);
// interaction box
View.HSplitBottom(40.0f, &View, nullptr);
View.VMargin(40.0f, &View);
View.HSplitBottom(20.0f, &View, &Label);
static float s_FolderBox = 0;
pEditor->DoEditBox(&s_FolderBox, &Label, pEditor->m_aFileDialogNewFolderName, sizeof(pEditor->m_aFileDialogNewFolderName), 15.0f, &s_FolderBox);
View.HSplitBottom(20.0f, &View, &Label);
pEditor->UI()->DoLabel(&Label, "Name:", 10.0f, TEXTALIGN_LEFT);

// button bar
ButtonBar.VSplitLeft(30.0f, nullptr, &ButtonBar);
ButtonBar.VSplitLeft(110.0f, &Label, &ButtonBar);
static int s_CreateButton = 0;
if(pEditor->DoButton_Editor(&s_CreateButton, "Create", 0, &Label, 0, nullptr) || pEditor->Input()->KeyPress(KEY_RETURN) || pEditor->Input()->KeyPress(KEY_KP_ENTER))
// button bar
ButtonBar.VSplitLeft(30.0f, nullptr, &ButtonBar);
ButtonBar.VSplitLeft(110.0f, &Label, &ButtonBar);
static int s_CreateButton = 0;
if(pEditor->DoButton_Editor(&s_CreateButton, "Create", 0, &Label, 0, nullptr) || pEditor->Input()->KeyPress(KEY_RETURN) || pEditor->Input()->KeyPress(KEY_KP_ENTER))
{
// create the folder
if(pEditor->m_aFileDialogNewFolderName[0])
{
// create the folder
if(pEditor->m_aFileDialogNewFolderName[0])
char aBuf[512];
str_format(aBuf, sizeof(aBuf), "%s/%s", pEditor->m_pFileDialogPath, pEditor->m_aFileDialogNewFolderName);
if(pEditor->Storage()->CreateFolder(aBuf, IStorage::TYPE_SAVE))
{
char aBuf[512];
str_format(aBuf, sizeof(aBuf), "%s/%s", pEditor->m_pFileDialogPath, pEditor->m_aFileDialogNewFolderName);
if(pEditor->Storage()->CreateFolder(aBuf, IStorage::TYPE_SAVE))
{
pEditor->FilelistPopulate(IStorage::TYPE_SAVE);
return 1;
}
else
str_copy(pEditor->m_aFileDialogErrString, "Unable to create the folder", sizeof(pEditor->m_aFileDialogErrString));
pEditor->FilelistPopulate(IStorage::TYPE_SAVE);
return 1;
}
else
{
pEditor->ShowFileDialogError("Failed to create the folder '%s'.", aBuf);
}
}
ButtonBar.VSplitRight(30.0f, &ButtonBar, nullptr);
ButtonBar.VSplitRight(110.0f, &ButtonBar, &Label);
static int s_AbortButton = 0;
if(pEditor->DoButton_Editor(&s_AbortButton, "Abort", 0, &Label, 0, nullptr))
return 1;
}
else
{
// error text
View.HSplitTop(30.0f, nullptr, &View);
View.VMargin(40.0f, &View);
View.HSplitTop(20.0f, &Label, &View);
pEditor->UI()->DoLabel(&Label, "Error:", 10.0f, TEXTALIGN_LEFT);
View.HSplitTop(20.0f, &Label, &View);
SLabelProperties Props;
Props.m_MaxWidth = View.w;
pEditor->UI()->DoLabel(&Label, "Unable to create the folder", 10.0f, TEXTALIGN_LEFT, Props);

// button
ButtonBar.VMargin(ButtonBar.w / 2.0f - 55.0f, &ButtonBar);
static int s_CreateButton = 0;
if(pEditor->DoButton_Editor(&s_CreateButton, "Ok", 0, &ButtonBar, 0, nullptr))
return 1;
}
ButtonBar.VSplitRight(30.0f, &ButtonBar, nullptr);
ButtonBar.VSplitRight(110.0f, &ButtonBar, &Label);
static int s_AbortButton = 0;
if(pEditor->DoButton_Editor(&s_AbortButton, "Abort", 0, &Label, 0, nullptr))
return 1;

return 0;
}
Expand Down Expand Up @@ -1233,7 +1214,10 @@ int CEditor::PopupEvent(CEditor *pEditor, CUIRect View, void *pContext)
pEditor->m_aFileName[0] = 0;
}
else if(pEditor->m_PopupEventType == POPEVENT_SAVE)
CEditor::CallbackSaveMap(pEditor->m_aFileSaveName, IStorage::TYPE_SAVE, pEditor);
{
if(!CallbackSaveMap(pEditor->m_aFileSaveName, IStorage::TYPE_SAVE, pEditor))
return 0; // don't close this popup on error, because it would close the error popup instead
}
else if(pEditor->m_PopupEventType == POPEVENT_PLACE_BORDER_TILES)
pEditor->PlaceBorderTiles();
pEditor->m_PopupEventWasActivated = false;
Expand Down
2 changes: 1 addition & 1 deletion src/game/gamecore.cpp
Expand Up @@ -80,7 +80,7 @@ float VelocityRamp(float Value, float Start, float Range, float Curvature)
{
if(Value < Start)
return 1.0f;
return 1.0f / powf(Curvature, (Value - Start) / Range);
return 1.0f / std::pow(Curvature, (Value - Start) / Range);
}

void CCharacterCore::Init(CWorldCore *pWorld, CCollision *pCollision, CTeamsCore *pTeams, std::map<int, std::vector<vec2>> *pTeleOuts)
Expand Down
168 changes: 166 additions & 2 deletions src/game/localization.cpp
Expand Up @@ -35,9 +35,168 @@ CLocalizationDatabase::CLocalizationDatabase()
m_CurrentVersion = 0;
}

void CLocalizationDatabase::AddString(const char *pOrgStr, const char *pNewStr, const char *pContext)
void CLocalizationDatabase::LoadIndexfile(IStorage *pStorage, IConsole *pConsole)
{
m_vStrings.emplace_back(str_quickhash(pOrgStr), str_quickhash(pContext), m_StringsHeap.StoreString(*pNewStr ? pNewStr : pOrgStr));
m_vLanguages.clear();

const std::vector<std::string> vEnglishLanguageCodes = {"en"};
m_vLanguages.emplace_back("English", "", 826, vEnglishLanguageCodes);

const char *pFilename = "languages/index.txt";
IOHANDLE File = pStorage->OpenFile(pFilename, IOFLAG_READ | IOFLAG_SKIP_BOM, IStorage::TYPE_ALL);
if(!File)
{
char aBuf[64 + IO_MAX_PATH_LENGTH];
str_format(aBuf, sizeof(aBuf), "Couldn't open index file '%s'", pFilename);
pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", aBuf);
return;
}

CLineReader LineReader;
LineReader.Init(File);

const char *pLine;
while((pLine = LineReader.Get()))
{
if(!str_length(pLine) || pLine[0] == '#') // skip empty lines and comments
continue;

char aEnglishName[128];
str_copy(aEnglishName, pLine);

pLine = LineReader.Get();
if(!pLine)
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "Unexpected end of index file after language '%s'", aEnglishName);
pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", aBuf);
break;
}
if(!str_startswith(pLine, "== "))
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "Missing native name for language '%s'", aEnglishName);
pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", aBuf);
(void)LineReader.Get();
(void)LineReader.Get();
continue;
}
char aNativeName[128];
str_copy(aNativeName, pLine + 3);

pLine = LineReader.Get();
if(!pLine)
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "Unexpected end of index file after language '%s'", aEnglishName);
pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", aBuf);
break;
}
if(!str_startswith(pLine, "== "))
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "Missing country code for language '%s'", aEnglishName);
pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", aBuf);
(void)LineReader.Get();
continue;
}
char aCountryCode[128];
str_copy(aCountryCode, pLine + 3);

pLine = LineReader.Get();
if(!pLine)
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "Unexpected end of index file after language '%s'", aEnglishName);
pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", aBuf);
break;
}
if(!str_startswith(pLine, "== "))
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "Missing language codes for language '%s'", aEnglishName);
pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", aBuf);
continue;
}
const char *pLanguageCodes = pLine + 3;
char aLanguageCode[256];
std::vector<std::string> vLanguageCodes;
while((pLanguageCodes = str_next_token(pLanguageCodes, ";", aLanguageCode, sizeof(aLanguageCode))))
{
if(aLanguageCode[0])
{
vLanguageCodes.emplace_back(aLanguageCode);
}
}
if(vLanguageCodes.empty())
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "At least one language code required for language '%s'", aEnglishName);
pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", aBuf);
continue;
}

char aFileName[IO_MAX_PATH_LENGTH];
str_format(aFileName, sizeof(aFileName), "languages/%s.txt", aEnglishName);
m_vLanguages.emplace_back(aNativeName, aFileName, str_toint(aCountryCode), vLanguageCodes);
}

io_close(File);

std::sort(m_vLanguages.begin(), m_vLanguages.end());
}

void CLocalizationDatabase::SelectDefaultLanguage(IConsole *pConsole, char *pFilename, size_t Length) const
{
char aLocaleStr[128];
os_locale_str(aLocaleStr, sizeof(aLocaleStr));

char aBuf[256];
str_format(aBuf, sizeof(aBuf), "Choosing default language based on user locale '%s'", aLocaleStr);
pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", aBuf);

while(true)
{
const CLanguage *pPrefixMatch = nullptr;
for(const auto &Language : Languages())
{
for(const auto &LanguageCode : Language.m_vLanguageCodes)
{
if(LanguageCode == aLocaleStr)
{
// Exact match found, use it immediately
str_copy(pFilename, Language.m_FileName.c_str(), Length);
return;
}
else if(LanguageCode.rfind(aLocaleStr, 0) == 0)
{
// Locale is prefix of language code, e.g. locale is "en" and current language is "en-US"
pPrefixMatch = &Language;
}
}
}
// Use prefix match if no exact match was found
if(pPrefixMatch)
{
str_copy(pFilename, pPrefixMatch->m_FileName.c_str(), Length);
return;
}

// Remove last segment of locale string and try again with more generic locale, e.g. "en-US" -> "en"
int i = str_length(aLocaleStr) - 1;
for(; i >= 0; --i)
{
if(aLocaleStr[i] == '-')
{
aLocaleStr[i] = '\0';
break;
}
}

// Stop if no more locale segments are left
if(i == 0)
break;
}
}

bool CLocalizationDatabase::Load(const char *pFilename, IStorage *pStorage, IConsole *pConsole)
Expand Down Expand Up @@ -119,6 +278,11 @@ bool CLocalizationDatabase::Load(const char *pFilename, IStorage *pStorage, ICon
return true;
}

void CLocalizationDatabase::AddString(const char *pOrgStr, const char *pNewStr, const char *pContext)
{
m_vStrings.emplace_back(str_quickhash(pOrgStr), str_quickhash(pContext), m_StringsHeap.StoreString(*pNewStr ? pNewStr : pOrgStr));
}

const char *CLocalizationDatabase::FindString(unsigned Hash, unsigned ContextHash) const
{
CString String;
Expand Down
23 changes: 23 additions & 0 deletions src/game/localization.h
Expand Up @@ -4,9 +4,27 @@
#define GAME_LOCALIZATION_H

#include <base/system.h> // GNUC_ATTRIBUTE

#include <engine/shared/memheap.h>

#include <string>
#include <vector>

class CLanguage
{
public:
CLanguage() = default;
CLanguage(const char *pName, const char *pFileName, int Code, const std::vector<std::string> &vLanguageCodes) :
m_Name(pName), m_FileName(pFileName), m_CountryCode(Code), m_vLanguageCodes(vLanguageCodes) {}

std::string m_Name;
std::string m_FileName;
int m_CountryCode;
std::vector<std::string> m_vLanguageCodes;

bool operator<(const CLanguage &Other) const { return m_Name < Other.m_Name; }
};

class CLocalizationDatabase
{
class CString
Expand All @@ -27,6 +45,7 @@ class CLocalizationDatabase
bool operator==(const CString &Other) const { return m_Hash == Other.m_Hash && m_ContextHash == Other.m_ContextHash; }
};

std::vector<CLanguage> m_vLanguages;
std::vector<CString> m_vStrings;
CHeap m_StringsHeap;
int m_VersionCounter;
Expand All @@ -35,6 +54,10 @@ class CLocalizationDatabase
public:
CLocalizationDatabase();

void LoadIndexfile(class IStorage *pStorage, class IConsole *pConsole);
const std::vector<CLanguage> &Languages() const { return m_vLanguages; }
void SelectDefaultLanguage(class IConsole *pConsole, char *pFilename, size_t Length) const;

bool Load(const char *pFilename, class IStorage *pStorage, class IConsole *pConsole);

int Version() const { return m_CurrentVersion; }
Expand Down
2 changes: 1 addition & 1 deletion src/game/prng.h
@@ -1,7 +1,7 @@
#ifndef GAME_PRNG_H
#define GAME_PRNG_H

#include <stdint.h>
#include <cstdint>

class CPrng
{
Expand Down
2 changes: 1 addition & 1 deletion src/game/server/ddracechat.cpp
Expand Up @@ -45,7 +45,7 @@ void CGameContext::ConCredits(IConsole::IResult *pResult, void *pUserData)
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "chatresp",
"sjrc6, Cellegen, srdante, Nouaa, Voxel, luk51,");
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "chatresp",
"Vy0x2, Avolicious, louis & others.");
"Vy0x2, Avolicious, louis, Marmare314 & others.");
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "chatresp",
"Based on DDRace by the DDRace developers,");
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "chatresp",
Expand Down
30 changes: 15 additions & 15 deletions src/game/server/entities/character.cpp
Expand Up @@ -1022,7 +1022,7 @@ void CCharacter::SnapCharacter(int SnappingClient, int ID)
Health = m_Health;
Armor = m_Armor;
if(m_Core.m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo > 0)
AmmoCount = (m_FreezeTime > 0) ? m_Core.m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo : 0;
AmmoCount = (m_FreezeTime == 0) ? m_Core.m_aWeapons[m_Core.m_ActiveWeapon].m_Ammo : 0;
}

if(GetPlayer()->IsAfk() || GetPlayer()->IsPaused())
Expand Down Expand Up @@ -1330,36 +1330,36 @@ void CCharacter::HandleSkippableTiles(int Index)
if(MaxSpeed > 0)
{
if(Direction.x > 0.0000001f)
SpeederAngle = -atan(Direction.y / Direction.x);
SpeederAngle = -std::atan(Direction.y / Direction.x);
else if(Direction.x < 0.0000001f)
SpeederAngle = atan(Direction.y / Direction.x) + 2.0f * asin(1.0f);
SpeederAngle = std::atan(Direction.y / Direction.x) + 2.0f * std::asin(1.0f);
else if(Direction.y > 0.0000001f)
SpeederAngle = asin(1.0f);
SpeederAngle = std::asin(1.0f);
else
SpeederAngle = asin(-1.0f);
SpeederAngle = std::asin(-1.0f);

if(SpeederAngle < 0)
SpeederAngle = 4.0f * asin(1.0f) + SpeederAngle;
SpeederAngle = 4.0f * std::asin(1.0f) + SpeederAngle;

if(TempVel.x > 0.0000001f)
TeeAngle = -atan(TempVel.y / TempVel.x);
TeeAngle = -std::atan(TempVel.y / TempVel.x);
else if(TempVel.x < 0.0000001f)
TeeAngle = atan(TempVel.y / TempVel.x) + 2.0f * asin(1.0f);
TeeAngle = std::atan(TempVel.y / TempVel.x) + 2.0f * std::asin(1.0f);
else if(TempVel.y > 0.0000001f)
TeeAngle = asin(1.0f);
TeeAngle = std::asin(1.0f);
else
TeeAngle = asin(-1.0f);
TeeAngle = std::asin(-1.0f);

if(TeeAngle < 0)
TeeAngle = 4.0f * asin(1.0f) + TeeAngle;
TeeAngle = 4.0f * std::asin(1.0f) + TeeAngle;

TeeSpeed = sqrt(pow(TempVel.x, 2) + pow(TempVel.y, 2));
TeeSpeed = std::sqrt(std::pow(TempVel.x, 2) + std::pow(TempVel.y, 2));

DiffAngle = SpeederAngle - TeeAngle;
SpeedLeft = MaxSpeed / 5.0f - cos(DiffAngle) * TeeSpeed;
if(abs((int)SpeedLeft) > Force && SpeedLeft > 0.0000001f)
SpeedLeft = MaxSpeed / 5.0f - std::cos(DiffAngle) * TeeSpeed;
if(absolute((int)SpeedLeft) > Force && SpeedLeft > 0.0000001f)
TempVel += Direction * Force;
else if(abs((int)SpeedLeft) > Force)
else if(absolute((int)SpeedLeft) > Force)
TempVel += Direction * -Force;
else
TempVel += Direction * SpeedLeft;
Expand Down
2 changes: 1 addition & 1 deletion src/game/server/entities/door.cpp
Expand Up @@ -16,7 +16,7 @@ CDoor::CDoor(CGameWorld *pGameWorld, vec2 Pos, float Rotation, int Length,
m_Number = Number;
m_Pos = Pos;
m_Length = Length;
m_Direction = vec2(sin(Rotation), cos(Rotation));
m_Direction = vec2(std::sin(Rotation), std::cos(Rotation));
vec2 To = Pos + normalize(m_Direction) * m_Length;

GameServer()->Collision()->IntersectNoLaser(Pos, To, &this->m_To, 0);
Expand Down
2 changes: 1 addition & 1 deletion src/game/server/entities/light.cpp
Expand Up @@ -71,7 +71,7 @@ void CLight::Move()
void CLight::Step()
{
Move();
vec2 dir(sin(m_Rotation), cos(m_Rotation));
vec2 dir(std::sin(m_Rotation), std::cos(m_Rotation));
vec2 to2 = m_Pos + normalize(dir) * m_CurveLength;
GameServer()->Collision()->IntersectNoLaser(m_Pos, to2, &m_To, 0);
}
Expand Down
2 changes: 1 addition & 1 deletion src/game/server/entities/pickup.cpp
Expand Up @@ -173,7 +173,7 @@ void CPickup::Snap(int SnappingClient)
pChar = GameServer()->GetPlayerChar(GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID);

int SnappingClientVersion = GameServer()->GetClientVersion(SnappingClient);
bool Sixup = SnappingClientVersion == VERSION_NONE ? Server()->IsSixup(SnappingClient) : false;
bool Sixup = Server()->IsSixup(SnappingClient);

CNetObj_EntityEx *pEntData = 0;
if(SnappingClientVersion >= VERSION_DDNET_SWITCH && (m_Layer == LAYER_SWITCH || length(m_Core) > 0))
Expand Down
8 changes: 4 additions & 4 deletions src/game/server/entities/projectile.cpp
Expand Up @@ -225,9 +225,9 @@ void CProjectile::Tick()
m_Direction.x = -m_Direction.x;
else if(m_Bouncing == 2)
m_Direction.y = -m_Direction.y;
if(fabs(m_Direction.x) < 1e-6f)
if(absolute(m_Direction.x) < 1e-6f)
m_Direction.x = 0;
if(fabs(m_Direction.y) < 1e-6f)
if(absolute(m_Direction.y) < 1e-6f)
m_Direction.y = 0;
m_Pos += m_Direction;
}
Expand Down Expand Up @@ -374,7 +374,7 @@ void CProjectile::SetBouncing(int Value)
bool CProjectile::FillExtraInfo(CNetObj_DDNetProjectile *pProj)
{
const int MaxPos = 0x7fffffff / 100;
if(abs((int)m_Pos.y) + 1 >= MaxPos || abs((int)m_Pos.x) + 1 >= MaxPos)
if(absolute((int)m_Pos.y) + 1 >= MaxPos || absolute((int)m_Pos.x) + 1 >= MaxPos)
{
//If the modified data would be too large to fit in an integer, send normal data instead
return false;
Expand All @@ -383,7 +383,7 @@ bool CProjectile::FillExtraInfo(CNetObj_DDNetProjectile *pProj)
float Angle = -atan2f(m_Direction.x, m_Direction.y);

int Data = 0;
Data |= (abs(m_Owner) & 255) << 0;
Data |= (absolute(m_Owner) & 255) << 0;
if(m_Owner < 0)
Data |= PROJECTILEFLAG_NO_OWNER;
//This bit tells the client to use the extra info
Expand Down
2 changes: 1 addition & 1 deletion src/game/server/eventhandler.h
Expand Up @@ -3,7 +3,7 @@
#ifndef GAME_SERVER_EVENTHANDLER_H
#define GAME_SERVER_EVENTHANDLER_H

#include <stdint.h>
#include <cstdint>

#include <engine/shared/protocol.h>

Expand Down
30 changes: 7 additions & 23 deletions src/game/server/gamecontext.cpp
Expand Up @@ -2689,7 +2689,7 @@ void CGameContext::ConToggleTuneParam(IConsole::IResult *pResult, void *pUserDat
return;
}

float NewValue = fabs(OldValue - pResult->GetFloat(1)) < 0.0001f ? pResult->GetFloat(2) : pResult->GetFloat(1);
float NewValue = absolute(OldValue - pResult->GetFloat(1)) < 0.0001f ? pResult->GetFloat(2) : pResult->GetFloat(1);

pSelf->Tuning()->Set(pParamName, NewValue);
pSelf->Tuning()->Get(pParamName, &NewValue);
Expand Down Expand Up @@ -3869,27 +3869,6 @@ const char *CGameContext::NetVersion() const { return GAME_NETVERSION; }

IGameServer *CreateGameServer() { return new CGameContext; }

bool CGameContext::PlayerCollision()
{
float Temp;
m_Tuning.Get("player_collision", &Temp);
return Temp != 0.0f;
}

bool CGameContext::PlayerHooking()
{
float Temp;
m_Tuning.Get("player_hooking", &Temp);
return Temp != 0.0f;
}

float CGameContext::PlayerJetpack()
{
float Temp;
m_Tuning.Get("player_jetpack", &Temp);
return Temp;
}

void CGameContext::OnSetAuthed(int ClientID, int Level)
{
if(m_apPlayers[ClientID])
Expand Down Expand Up @@ -4367,6 +4346,11 @@ bool CGameContext::RateLimitPlayerMapVote(int ClientID)

void CGameContext::OnUpdatePlayerServerInfo(char *aBuf, int BufSize, int ID)
{
if(BufSize <= 0)
return;

aBuf[0] = '\0';

if(!m_apPlayers[ID])
return;

Expand Down Expand Up @@ -4416,7 +4400,7 @@ void CGameContext::OnUpdatePlayerServerInfo(char *aBuf, int BufSize, int ID)
if(TeeInfo.m_aUseCustomColors[i])
{
str_format(aPartBuf, sizeof(aPartBuf),
",color:%d",
",\"color\":%d",
TeeInfo.m_aSkinPartColors[i]);
str_append(aJsonSkin, aPartBuf, sizeof(aJsonSkin));
}
Expand Down
3 changes: 0 additions & 3 deletions src/game/server/gamecontext.h
Expand Up @@ -514,9 +514,6 @@ class CGameContext : public IGameServer

void SendRecord(int ClientID);
void OnSetAuthed(int ClientID, int Level) override;
virtual bool PlayerCollision();
virtual bool PlayerHooking();
virtual float PlayerJetpack();

void ResetTuning();
};
Expand Down
4 changes: 2 additions & 2 deletions src/game/server/gamecontroller.cpp
Expand Up @@ -227,7 +227,7 @@ bool IGameController::OnEntity(int Index, int x, int y, int Layer, int Flags, bo
WEAPON_SHOTGUN, //Type
-1, //Owner
Pos, //Pos
vec2(sin(Deg), cos(Deg)), //Dir
vec2(std::sin(Deg), std::cos(Deg)), //Dir
-2, //Span
true, //Freeze
true, //Explosive
Expand All @@ -253,7 +253,7 @@ bool IGameController::OnEntity(int Index, int x, int y, int Layer, int Flags, bo
WEAPON_SHOTGUN, //Type
-1, //Owner
Pos, //Pos
vec2(sin(Deg), cos(Deg)), //Dir
vec2(std::sin(Deg), std::cos(Deg)), //Dir
-2, //Span
true, //Freeze
false, //Explosive
Expand Down
64 changes: 16 additions & 48 deletions src/game/server/gameworld.cpp
Expand Up @@ -207,12 +207,16 @@ void CGameWorld::UpdatePlayerMaps()
{
if(!Server()->ClientIngame(i))
continue;
if(Server()->GetClientVersion(i) >= VERSION_DDNET_OLD)
continue;
int *pMap = Server()->GetIdMap(i);

// compute distances
for(int j = 0; j < MAX_CLIENTS; j++)
{
Dist[j].second = j;
if(j == i)
continue;
if(!Server()->ClientIngame(j) || !GameServer()->m_apPlayers[j])
{
Dist[j].first = 1e10;
Expand All @@ -224,65 +228,29 @@ void CGameWorld::UpdatePlayerMaps()
Dist[j].first = 1e9;
continue;
}
// copypasted chunk from character.cpp Snap() follows
CCharacter *pSnapChar = GameServer()->GetPlayerChar(i);
if(pSnapChar && !pSnapChar->IsSuper() &&
!GameServer()->m_apPlayers[i]->IsPaused() && GameServer()->m_apPlayers[i]->GetTeam() != TEAM_SPECTATORS &&
!pChr->CanCollide(i) &&
(!GameServer()->m_apPlayers[i] ||
GameServer()->m_apPlayers[i]->GetClientVersion() == VERSION_VANILLA ||
(GameServer()->m_apPlayers[i]->GetClientVersion() >= VERSION_DDRACE &&
(GameServer()->m_apPlayers[i]->m_ShowOthers == SHOW_OTHERS_OFF ||
(GameServer()->m_apPlayers[i]->m_ShowOthers == SHOW_OTHERS_ONLY_TEAM && !GameServer()->m_apPlayers[i]->GetCharacter()->SameTeam(j))))))
if(!pChr->CanSnapCharacter(i))
Dist[j].first = 1e8;
else
Dist[j].first = 0;

Dist[j].first += distance(GameServer()->m_apPlayers[i]->m_ViewPos, GameServer()->m_apPlayers[j]->GetCharacter()->m_Pos);
Dist[j].first = length_squared(GameServer()->m_apPlayers[i]->m_ViewPos - pChr->m_Pos);
}

// always send the player themselves
Dist[i].first = 0;

// compute reverse map
int aReverseMap[MAX_CLIENTS];
for(int &j : aReverseMap)
{
j = -1;
}
for(int j = 0; j < VANILLA_MAX_CLIENTS; j++)
{
if(pMap[j] == -1)
continue;
if(Dist[pMap[j]].first > 5e9f)
pMap[j] = -1;
else
aReverseMap[pMap[j]] = j;
}
// always send the player themselves, even if all in same position
Dist[i].first = -1;

std::nth_element(&Dist[0], &Dist[VANILLA_MAX_CLIENTS - 1], &Dist[MAX_CLIENTS], distCompare);

int Mapc = 0;
int Demand = 0;
int Index = 1; // exclude self client id
for(int j = 0; j < VANILLA_MAX_CLIENTS - 1; j++)
{
int k = Dist[j].second;
if(aReverseMap[k] != -1 || Dist[j].first > 5e9f)
pMap[j + 1] = -1; // also fill player with empty name to say chat msgs
if(Dist[j].second == i || Dist[j].first > 5e9f)
continue;
while(Mapc < VANILLA_MAX_CLIENTS && pMap[Mapc] != -1)
Mapc++;
if(Mapc < VANILLA_MAX_CLIENTS - 1)
pMap[Mapc] = k;
else
Demand++;
pMap[Index++] = Dist[j].second;
}
for(int j = MAX_CLIENTS - 1; j > VANILLA_MAX_CLIENTS - 2; j--)
{
int k = Dist[j].second;
if(aReverseMap[k] != -1 && Demand-- > 0)
pMap[aReverseMap[k]] = -1;
}
pMap[VANILLA_MAX_CLIENTS - 1] = -1; // player with empty name to say chat msgs

// sort by real client ids, guarantee order on distance changes, O(Nlog(N)) worst case
// sort just clients in game always except first (self client id) and last (fake client id) indexes
std::sort(&pMap[1], &pMap[minimum(Index, VANILLA_MAX_CLIENTS - 1)]);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/game/server/player.cpp
Expand Up @@ -337,7 +337,7 @@ void CPlayer::Snap(int SnappingClient)

int SnappingClientVersion = GameServer()->GetClientVersion(SnappingClient);
int Latency = SnappingClient == SERVER_DEMO_CLIENT ? m_Latency.m_Min : GameServer()->m_apPlayers[SnappingClient]->m_aCurLatency[m_ClientID];
int Score = abs(m_Score) * -1;
int Score = absolute(m_Score) * -1;

// send 0 if times of others are not shown
if(SnappingClient != m_ClientID && g_Config.m_SvHideScore)
Expand Down
2 changes: 1 addition & 1 deletion src/game/server/save.cpp
Expand Up @@ -17,7 +17,7 @@ void CSaveTee::Save(CCharacter *pChr)
str_copy(m_aName, pChr->Server()->ClientName(m_ClientID), sizeof(m_aName));

m_Alive = pChr->m_Alive;
m_Paused = abs(pChr->m_pPlayer->IsPaused());
m_Paused = absolute(pChr->m_pPlayer->IsPaused());
m_NeededFaketuning = pChr->m_NeededFaketuning;

m_TeeStarted = pChr->Teams()->TeeStarted(m_ClientID);
Expand Down
8 changes: 4 additions & 4 deletions src/game/server/scoreworker.cpp
Expand Up @@ -914,7 +914,7 @@ bool CScoreWorker::ShowTop(IDbConnection *pSqlServer, const ISqlData *pGameData,
const auto *pData = dynamic_cast<const CSqlPlayerRequest *>(pGameData);
auto *pResult = dynamic_cast<CScorePlayerResult *>(pGameData->m_pResult.get());

int LimitStart = maximum(abs(pData->m_Offset) - 1, 0);
int LimitStart = maximum(absolute(pData->m_Offset) - 1, 0);
const char *pOrder = pData->m_Offset >= 0 ? "ASC" : "DESC";
const char *pAny = "%";

Expand Down Expand Up @@ -1002,7 +1002,7 @@ bool CScoreWorker::ShowTeamTop5(IDbConnection *pSqlServer, const ISqlData *pGame
auto *pResult = dynamic_cast<CScorePlayerResult *>(pGameData->m_pResult.get());
auto *paMessages = pResult->m_Data.m_aaMessages;

int LimitStart = maximum(abs(pData->m_Offset) - 1, 0);
int LimitStart = maximum(absolute(pData->m_Offset) - 1, 0);
const char *pOrder = pData->m_Offset >= 0 ? "ASC" : "DESC";

// check sort method
Expand Down Expand Up @@ -1089,7 +1089,7 @@ bool CScoreWorker::ShowPlayerTeamTop5(IDbConnection *pSqlServer, const ISqlData
auto *pResult = dynamic_cast<CScorePlayerResult *>(pGameData->m_pResult.get());
auto *paMessages = pResult->m_Data.m_aaMessages;

int LimitStart = maximum(abs(pData->m_Offset) - 1, 0);
int LimitStart = maximum(absolute(pData->m_Offset) - 1, 0);
const char *pOrder = pData->m_Offset >= 0 ? "ASC" : "DESC";

// check sort method
Expand Down Expand Up @@ -1181,7 +1181,7 @@ bool CScoreWorker::ShowTimes(IDbConnection *pSqlServer, const ISqlData *pGameDat
auto *pResult = dynamic_cast<CScorePlayerResult *>(pGameData->m_pResult.get());
auto *paMessages = pResult->m_Data.m_aaMessages;

int LimitStart = maximum(abs(pData->m_Offset) - 1, 0);
int LimitStart = maximum(absolute(pData->m_Offset) - 1, 0);
const char *pOrder = pData->m_Offset >= 0 ? "DESC" : "ASC";

char aCurrentTimestamp[512];
Expand Down
2 changes: 1 addition & 1 deletion src/game/server/teams.cpp
Expand Up @@ -680,7 +680,7 @@ void CGameTeams::OnFinish(CPlayer *Player, float Time, const char *pTimestamp)
else
GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf, -1., CGameContext::CHAT_SIX);

float Diff = fabs(Time - pData->m_BestTime);
float Diff = absolute(Time - pData->m_BestTime);

if(Time - pData->m_BestTime < 0)
{
Expand Down
2 changes: 1 addition & 1 deletion src/game/server/teehistorian.h
Expand Up @@ -6,7 +6,7 @@
#include <engine/shared/protocol.h>
#include <game/generated/protocol.h>

#include <time.h>
#include <ctime>

class CConfig;
class CTuningParams;
Expand Down
4 changes: 2 additions & 2 deletions src/game/version.h
Expand Up @@ -3,11 +3,11 @@
#ifndef GAME_VERSION_H
#define GAME_VERSION_H
#ifndef GAME_RELEASE_VERSION
#define GAME_RELEASE_VERSION "16.7.2"
#define GAME_RELEASE_VERSION "16.9"
#endif
#define GAME_VERSION "0.6.4, " GAME_RELEASE_VERSION
#define GAME_NETVERSION "0.6 626fce9a778df4d4"
#define CLIENT_VERSIONNR 16072
#define CLIENT_VERSIONNR 16090
extern const char *GIT_SHORTREV_HASH;
#define GAME_NAME "DDNet"
#endif
2 changes: 1 addition & 1 deletion src/steam/steam_api_flat.h
Expand Up @@ -3,7 +3,7 @@

#include <base/dynamic.h>

#include <stdint.h>
#include <cstdint>

#ifndef STEAMAPI
#define STEAMAPI DYNAMIC_IMPORT
Expand Down
2 changes: 1 addition & 1 deletion src/test/color.cpp
Expand Up @@ -17,7 +17,7 @@ TEST(Color, HRHConv)
EXPECT_FLOAT_EQ(hsl.l, hsl2.l);
else
{
EXPECT_NEAR(fmod(hsl.h, 1.0f), fmod(hsl2.h, 1.0f), 0.001f);
EXPECT_NEAR(std::fmod(hsl.h, 1.0f), std::fmod(hsl2.h, 1.0f), 0.001f);
EXPECT_NEAR(hsl.s, hsl2.s, 0.0001f);
EXPECT_FLOAT_EQ(hsl.l, hsl2.l);
}
Expand Down
7 changes: 7 additions & 0 deletions src/test/os.cpp
Expand Up @@ -9,3 +9,10 @@ TEST(Os, VersionStr)
EXPECT_FALSE(os_version_str(aVersion, sizeof(aVersion)));
EXPECT_STRNE(aVersion, "");
}

TEST(Os, LocaleStr)
{
char aLocale[128];
os_locale_str(aLocale, sizeof(aLocale));
EXPECT_STRNE(aLocale, "");
}
3 changes: 3 additions & 0 deletions src/test/str.cpp
Expand Up @@ -685,6 +685,9 @@ TEST(Str, TimeFloat)

EXPECT_EQ(str_time_float(12.16, TIME_HOURS_CENTISECS, aBuf, sizeof(aBuf)), 8);
EXPECT_STREQ(aBuf, "00:12.16");

EXPECT_EQ(str_time_float(22.995, TIME_MINS, aBuf, sizeof(aBuf)), 5);
EXPECT_STREQ(aBuf, "00:22");
}

TEST(Str, HasCc)
Expand Down
2 changes: 1 addition & 1 deletion src/tools/map_create_pixelart.cpp
Expand Up @@ -83,7 +83,7 @@ bool CreatePixelArt(const char aFilenames[3][64], const int aLayerID[2], const i
if(!pQuadLayer)
return false;

int MaxNewQuads = ceil((Img.m_Width * Img.m_Height) / aPixelSizes[0]);
int MaxNewQuads = std::ceil((Img.m_Width * Img.m_Height) / aPixelSizes[0]);
CQuad *pQuads = new CQuad[pQuadLayer->m_NumQuads + MaxNewQuads];

InsertCurrentQuads(InputMap, pQuadLayer, pQuads);
Expand Down
4 changes: 2 additions & 2 deletions src/tools/map_find_env.cpp
Expand Up @@ -46,7 +46,7 @@ bool GetLayerGroupIDs(CDataFileReader &InputMap, const int LayerNumber, int &Gro

int FxToTilePos(const int FxPos)
{
return (int)floor(fx2f(FxPos) / 32);
return std::floor(fx2f(FxPos) / 32);
}

bool GetEnvelopedQuads(const CQuad *pQuads, const int NumQuads, const int EnvID, const int GroupID, const int LayerID, int &QuadsCounter, EnvelopedQuad pEnvQuads[1024])
Expand Down Expand Up @@ -79,7 +79,7 @@ void PrintEnvelopedQuads(const EnvelopedQuad pEnvQuads[1024], const int EnvID, c

dbg_msg("map_find_env", "Found %d quads with env number #%d:", QuadsCounter, EnvID + 1);
for(int i = 0; i < QuadsCounter; i++)
dbg_msg("map_find_env", "%*d. Group: #%d - Layer: #%d - Pos: %d,%d", (int)(log10(abs(QuadsCounter))) + 1, i + 1, pEnvQuads[i].m_GroupID, pEnvQuads[i].m_LayerID, pEnvQuads[i].m_TilePosX, pEnvQuads[i].m_TilePosY);
dbg_msg("map_find_env", "%*d. Group: #%d - Layer: #%d - Pos: %d,%d", (int)(std::log10(absolute(QuadsCounter))) + 1, i + 1, pEnvQuads[i].m_GroupID, pEnvQuads[i].m_LayerID, pEnvQuads[i].m_TilePosX, pEnvQuads[i].m_TilePosY);
}

bool FindEnv(const char aFilename[64], const int EnvID)
Expand Down
18 changes: 9 additions & 9 deletions src/tools/map_replace_area.cpp
Expand Up @@ -499,7 +499,7 @@ void SetExtendedArea(MapObject &Ob)
{
float aInspectedArea[2];
if(GetLineIntersection(Ob.m_aaBaseArea[i], Ob.m_aaScreenOffset[i], aInspectedArea))
memcpy(Ob.m_aaExtendedArea[i], aInspectedArea, sizeof(float[2]));
mem_copy(Ob.m_aaExtendedArea[i], aInspectedArea, sizeof(float[2]));
continue;
}

Expand All @@ -520,13 +520,13 @@ bool GetVisibleArea(const float aaGameArea[2][2], const MapObject &Ob, float aaV
SetInexistent((float *)aaVisibleArea, 4);

float aaInspectedArea[2][2];
memcpy(aaInspectedArea, aaGameArea, sizeof(float[2][2]));
mem_copy(aaInspectedArea, aaGameArea, sizeof(float[2][2]));

for(int i = 0; i < 2; i++)
{
if(Ob.m_aSpeed[i] == 1)
{
memcpy(aaInspectedArea[i], Ob.m_aaExtendedArea[i], sizeof(float[2]));
mem_copy(aaInspectedArea[i], Ob.m_aaExtendedArea[i], sizeof(float[2]));
continue;
}

Expand All @@ -538,7 +538,7 @@ bool GetVisibleArea(const float aaGameArea[2][2], const MapObject &Ob, float aaV
}

if(aaVisibleArea)
memcpy(aaVisibleArea, aaInspectedArea, sizeof(float[2][2]));
mem_copy(aaVisibleArea, aaInspectedArea, sizeof(float[2][2]));

return true;
}
Expand Down Expand Up @@ -594,8 +594,8 @@ void GetGameAreaDistance(const float aaaGameAreas[2][2][2], const MapObject aObs
void GetGameAreaDistance(const float aaaGameAreas[2][2][2], const MapObject aObs[2], const float aaVisibleArea[2][2], float aDistance[2])
{
float aaaVisibleAreas[2][2][2];
memcpy(aaaVisibleAreas[0], aaVisibleArea[0], sizeof(float[2][2]));
memcpy(aaaVisibleAreas[1], aaVisibleArea[0], sizeof(float[2][2]));
mem_copy(aaaVisibleAreas[0], aaVisibleArea[0], sizeof(float[2][2]));
mem_copy(aaaVisibleAreas[1], aaVisibleArea[0], sizeof(float[2][2]));
GetGameAreaDistance(aaaGameAreas, aObs, aaaVisibleAreas, aDistance);
}

Expand All @@ -618,8 +618,8 @@ void ConvertToTiles(const float aaArea[2][2], int aaTiles[2][2])
{
for(int i = 0; i < 2; i++)
{
aaTiles[i][0] = floor((floor(aaArea[i][0] * 100.0f) / 100.0f) / 32.0f);
aaTiles[i][1] = ceil((floor(aaArea[i][1] * 100.0f) / 100.0f) / 32.0f);
aaTiles[i][0] = std::floor((std::floor(aaArea[i][0] * 100.0f) / 100.0f) / 32.0f);
aaTiles[i][1] = std::ceil((std::floor(aaArea[i][1] * 100.0f) / 100.0f) / 32.0f);
}
}

Expand All @@ -636,7 +636,7 @@ bool GetLineIntersection(const float aLine1[2], const float aLine2[2], float aIn
return false;

if(aIntersection)
memcpy(aIntersection, aBorders, sizeof(float[2]));
mem_copy(aIntersection, aBorders, sizeof(float[2]));

return true;
}
Expand Down
57 changes: 33 additions & 24 deletions src/tools/twping.cpp
@@ -1,73 +1,82 @@
#include <base/math.h>
#include <base/logger.h>
#include <base/system.h>
#include <cstdio>

#include <engine/shared/masterserver.h>
#include <engine/shared/network.h>
#include <engine/shared/packer.h>

static CNetClient g_NetOp; // main
#include <chrono>

int main(int argc, const char **argv)
{
CCmdlineFix CmdlineFix(&argc, &argv);

secure_random_init();
log_set_global_logger_default();

net_init();
NETADDR BindAddr;
mem_zero(&BindAddr, sizeof(BindAddr));
BindAddr.type = NETTYPE_ALL;
g_NetOp.Open(BindAddr);

CNetClient NetClient;
NetClient.Open(BindAddr);

if(argc != 2)
{
fprintf(stderr, "usage: %s server[:port] (default port: 8303)\n", argv[0]);
return 1;
log_error("twping", "usage: %s server[:port] (default port: 8303)", argv[0]);
return -1;
}

NETADDR Addr;
if(net_host_lookup(argv[1], &Addr, NETTYPE_ALL))
{
fprintf(stderr, "host lookup failed\n");
return 1;
log_error("twping", "host lookup failed");
return -1;
}

if(Addr.port == 0)
Addr.port = 8303;

const int CurToken = rand() % 256;
unsigned char aBuffer[sizeof(SERVERBROWSE_GETINFO) + 1];
CNetChunk Packet;

mem_copy(aBuffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO));

int CurToken = rand() % 256;
aBuffer[sizeof(SERVERBROWSE_GETINFO)] = CurToken;

CNetChunk Packet;
Packet.m_ClientID = -1;
Packet.m_Address = Addr;
Packet.m_Flags = NETSENDFLAG_CONNLESS;
Packet.m_DataSize = sizeof(aBuffer);
Packet.m_pData = aBuffer;

g_NetOp.Send(&Packet);
NetClient.Send(&Packet);

int64_t startTime = time_get();
const int64_t StartTime = time_get();

net_socket_read_wait(g_NetOp.m_Socket, 1000000);
using namespace std::chrono_literals;
const std::chrono::nanoseconds Timeout = std::chrono::nanoseconds(1s);
net_socket_read_wait(NetClient.m_Socket, Timeout);

g_NetOp.Update();
NetClient.Update();

while(g_NetOp.Recv(&Packet))
while(NetClient.Recv(&Packet))
{
if(Packet.m_DataSize >= (int)sizeof(SERVERBROWSE_INFO) && mem_comp(Packet.m_pData, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)) == 0)
{
// we got ze info
CUnpacker Up;

Up.Reset((unsigned char *)Packet.m_pData + sizeof(SERVERBROWSE_INFO), Packet.m_DataSize - sizeof(SERVERBROWSE_INFO));
int Token = str_toint(Up.GetString());
CUnpacker Unpacker;
Unpacker.Reset((unsigned char *)Packet.m_pData + sizeof(SERVERBROWSE_INFO), Packet.m_DataSize - sizeof(SERVERBROWSE_INFO));
int Token = str_toint(Unpacker.GetString());
if(Token != CurToken)
continue;

int64_t endTime = time_get();
printf("%g ms\n", (double)(endTime - startTime) / time_freq() * 1000);
const int64_t EndTime = time_get();
log_info("twping", "%g ms", (double)(EndTime - StartTime) / time_freq() * 1000);
return 0;
}
}
return 0;

log_info("twping", "timeout (%" PRId64 " ms)", std::chrono::duration_cast<std::chrono::milliseconds>(Timeout).count());
return 1;
}