Merge branch 'ddnet:master' into patch-1

This commit is contained in:
veydzh3r 2024-09-01 11:57:42 +03:00 committed by GitHub
commit a8616ef2ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 981 additions and 322 deletions

View file

@ -2462,6 +2462,7 @@ if(CLIENT)
editor_trackers.cpp
editor_trackers.h
editor_ui.h
enums.h
explanations.cpp
layer_selector.cpp
layer_selector.h
@ -2500,8 +2501,13 @@ if(CLIENT)
mapitems/sound.cpp
mapitems/sound.h
popups.cpp
prompt.cpp
prompt.h
proof_mode.cpp
proof_mode.h
quick_action.h
quick_actions.cpp
quick_actions.h
smooth_value.cpp
smooth_value.h
tileart.cpp

View file

@ -631,7 +631,7 @@ Loading DDNet Client
== Загрузка DDNet Client
Normal message
== Обычное с.
== Обычное
Connecting dummy
== Подключение дамми
@ -643,10 +643,10 @@ Save ghost
== Сохранять тень
DDNet Client updated!
== DDNet Client обновлён!
== DDNet клиент обновлён!
Highlighted message
== Выделенное с.
== Выделенное
Demo
== Демо
@ -724,7 +724,7 @@ Downloading %s:
== Скачивание %s:
Update failed! Check log…
== Ошибка. Проверьте логи
== Не удалось обновиться! Подробности в логах
Restart
== Рестарт
@ -844,7 +844,7 @@ DDNet
== DDNet
Friend message
== Дружеское с.
== Дружеское
Save the best demo of each race
== Сохранять лучшее демо каждой карты
@ -916,10 +916,10 @@ Ratio
== Соотношение
Net
== Сеть
== Сальдо
FPM
== FPM
== У/мин
Spree
== Серия
@ -940,7 +940,7 @@ Grabs
== 9+ упоминаний
Client message
== Клиентское с.
== Клиентское
Warning
== Предупреждение
@ -973,7 +973,7 @@ Run server
== Запустить сервер
Server executable not found, can't run server
== Файл сервера не найден, невозможно запустить
== Файл сервера не найден, не удалось запустить сервер
Editor
== Редактор
@ -1883,7 +1883,7 @@ Could not resolve connect address '%s'. See local console for details.
== Не удалось определить адрес подключения '%s'. Подробности в локальной консоли.
Connect address error
== Ошибка подключения сервера
== Ошибка адреса подключения
Could not connect dummy
== Невозможно подключить дамми
@ -1892,34 +1892,34 @@ Dummy is not allowed on this server
== Дамми не разрешен на этом сервере
Please wait…
== Пожалуйста подождите…
== Пожалуйста, подождите…
Show client IDs (scoreboard, chat, spectator)
== Показывать ID клиента (Табло, чат, наблюдатель)
== Показывать ID клиента (табло, чат, наблюдатель)
Normal:
== Обычный:
Team:
== Команда:
== В команде:
Dummy settings
== Настройки дамми
Toggle to edit your dummy settings
== Нажмите чтоб изменить настройки дамми
== Нажмите, чтобы изменить настройки дамми
Randomize
== Перемешать
== Случайный
Are you sure that you want to delete '%s'?
== Вы уверены что хотите удалить '%s'?
== Вы уверены, что хотите удалить '%s'?
Delete skin
== Удалить скин
Basic
== Базовый
== Пресеты
Custom
== Кастомизация

View file

@ -7,7 +7,7 @@
# 3edcxzaq1 2020-06-25 00:00:00
# cur.ie 2020-09-28 00:00:00
# simpygirl 2022-02-20 00:00:00
# furo 2024-07-17 00:00:00
# furo 2024-08-29 00:00:00
##### /authors #####
##### translated strings #####
@ -1868,52 +1868,52 @@ https://wiki.ddnet.org/wiki/Mapping
== https://wiki.ddnet.org/wiki/Mapping
Could not resolve connect address '%s'. See local console for details.
==
== Kunde inte förstå anslutnings adress '%s'. Se den lokala konsolen för detaljer.
Connect address error
==
== Anslutnings problem
Could not connect dummy
==
== Kunde inte ansluta dummy
Dummy is not allowed on this server
==
== Dummy är inte tillåten på denna server
Please wait…
==
== Vänligen vänta…
Show client IDs (scoreboard, chat, spectator)
==
== Visa klient IDen (poänglistan, chatt, åskadarmeny)
Normal:
==
== Normal:
Team:
==
== Lag:
Dummy settings
==
== Dummy inställningar
Toggle to edit your dummy settings
==
== Växla för att ändra dina dummy inställningar
Randomize
==
== Slumpa
Are you sure that you want to delete '%s'?
==
== Är du säker att du vill ta bort '%s'?
Delete skin
==
== Ta bort skin
Basic
==
== Enkel
Custom
==
== Anpassa
Unable to delete skin
==
== Kunde inte ta bort skin
Customize
==
== Ändra

View file

@ -228,10 +228,8 @@ int CSound::Init()
return -1;
}
m_MixingRate = g_Config.m_SndRate;
SDL_AudioSpec Format, FormatOut;
Format.freq = m_MixingRate;
Format.freq = g_Config.m_SndRate;
Format.format = AUDIO_S16;
Format.channels = 2;
Format.samples = g_Config.m_SndBufferSize;
@ -239,7 +237,7 @@ int CSound::Init()
Format.userdata = this;
// Open the audio device and start playing sound!
m_Device = SDL_OpenAudioDevice(nullptr, 0, &Format, &FormatOut, 0);
m_Device = SDL_OpenAudioDevice(nullptr, 0, &Format, &FormatOut, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
if(m_Device == 0)
{
dbg_msg("sound", "unable to open audio: %s", SDL_GetError());
@ -248,6 +246,7 @@ int CSound::Init()
else
dbg_msg("sound", "sound init successful using audio driver '%s'", SDL_GetCurrentAudioDriver());
m_MixingRate = FormatOut.freq;
m_MaxFrames = FormatOut.samples * 2;
#if defined(CONF_VIDEORECORDER)
m_MaxFrames = maximum<uint32_t>(m_MaxFrames, 1024 * 2); // make the buffer bigger just in case

View file

@ -53,7 +53,7 @@ public:
virtual int GetInteger(unsigned Index) const = 0;
virtual float GetFloat(unsigned Index) const = 0;
virtual const char *GetString(unsigned Index) const = 0;
virtual ColorHSLA GetColor(unsigned Index, bool Light) const = 0;
virtual std::optional<ColorHSLA> GetColor(unsigned Index, bool Light) const = 0;
virtual void RemoveArgument(unsigned Index) = 0;

View file

@ -111,14 +111,16 @@ void SIntConfigVariable::ResetToOld()
void SColorConfigVariable::CommandCallback(IConsole::IResult *pResult, void *pUserData)
{
SColorConfigVariable *pData = static_cast<SColorConfigVariable *>(pUserData);
char aBuf[IConsole::CMDLINE_LENGTH + 64];
if(pResult->NumArguments())
{
if(pData->CheckReadOnly())
return;
const ColorHSLA Color = pResult->GetColor(0, pData->m_Light);
const unsigned Value = Color.Pack(pData->m_Light ? 0.5f : 0.0f, pData->m_Alpha);
const auto Color = pResult->GetColor(0, pData->m_Light);
if(Color)
{
const unsigned Value = Color->Pack(pData->m_Light ? 0.5f : 0.0f, pData->m_Alpha);
*pData->m_pVariable = Value;
if(pResult->m_ClientId != IConsole::CLIENT_ID_GAME)
@ -126,7 +128,12 @@ void SColorConfigVariable::CommandCallback(IConsole::IResult *pResult, void *pUs
}
else
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "%s is not a valid color.", pResult->GetString(0));
pData->m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "config", aBuf);
}
}
else
{
str_format(aBuf, sizeof(aBuf), "Value: %u", *pData->m_pVariable);
pData->m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "config", aBuf);
@ -493,8 +500,8 @@ void CConfigManager::Con_Toggle(IConsole::IResult *pResult, void *pUserData)
{
SColorConfigVariable *pColorVariable = static_cast<SColorConfigVariable *>(pVariable);
const float Darkest = pColorVariable->m_Light ? 0.5f : 0.0f;
const ColorHSLA Value = *pColorVariable->m_pVariable == pResult->GetColor(1, pColorVariable->m_Light).Pack(Darkest, pColorVariable->m_Alpha) ? pResult->GetColor(2, pColorVariable->m_Light) : pResult->GetColor(1, pColorVariable->m_Light);
pColorVariable->SetValue(Value.Pack(Darkest, pColorVariable->m_Alpha));
const std::optional<ColorHSLA> Value = *pColorVariable->m_pVariable == pResult->GetColor(1, pColorVariable->m_Light).value_or(ColorHSLA(0, 0, 0)).Pack(Darkest, pColorVariable->m_Alpha) ? pResult->GetColor(2, pColorVariable->m_Light) : pResult->GetColor(1, pColorVariable->m_Light);
pColorVariable->SetValue(Value.value_or(ColorHSLA(0, 0, 0)).Pack(Darkest, pColorVariable->m_Alpha));
}
else if(pVariable->m_Type == SConfigVariable::VAR_STRING)
{

View file

@ -40,22 +40,29 @@ float CConsole::CResult::GetFloat(unsigned Index) const
return str_tofloat(m_apArgs[Index]);
}
ColorHSLA CConsole::CResult::GetColor(unsigned Index, bool Light) const
std::optional<ColorHSLA> CConsole::CResult::GetColor(unsigned Index, bool Light) const
{
if(Index >= m_NumArgs)
return ColorHSLA(0, 0, 0);
return std::nullopt;
const char *pStr = m_apArgs[Index];
if(str_isallnum(pStr) || ((pStr[0] == '-' || pStr[0] == '+') && str_isallnum(pStr + 1))) // Teeworlds Color (Packed HSL)
{
const ColorHSLA Hsla = ColorHSLA(str_toulong_base(pStr, 10), true);
unsigned long Value = str_toulong_base(pStr, 10);
if(Value == std::numeric_limits<unsigned long>::max())
return std::nullopt;
const ColorHSLA Hsla = ColorHSLA(Value, true);
if(Light)
return Hsla.UnclampLighting();
return Hsla;
}
else if(*pStr == '$') // Hex RGB/RGBA
{
return color_cast<ColorHSLA>(color_parse<ColorRGBA>(pStr + 1).value_or(ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f)));
auto ParsedColor = color_parse<ColorRGBA>(pStr + 1);
if(ParsedColor)
return color_cast<ColorHSLA>(ParsedColor.value());
else
return std::nullopt;
}
else if(!str_comp_nocase(pStr, "red"))
return ColorHSLA(0.0f / 6.0f, 1, .5f);
@ -76,7 +83,7 @@ ColorHSLA CConsole::CResult::GetColor(unsigned Index, bool Light) const
else if(!str_comp_nocase(pStr, "black"))
return ColorHSLA(0, 0, 0);
return ColorHSLA(0, 0, 0);
return std::nullopt;
}
const IConsole::CCommandInfo *CConsole::CCommand::NextCommandInfo(int AccessLevel, int FlagMask) const
@ -129,12 +136,12 @@ int CConsole::ParseStart(CResult *pResult, const char *pString, int Length)
return 0;
}
int CConsole::ParseArgs(CResult *pResult, const char *pFormat)
int CConsole::ParseArgs(CResult *pResult, const char *pFormat, FCommandCallback pfnCallback)
{
char Command = *pFormat;
char *pStr;
int Optional = 0;
int Error = 0;
int Error = PARSEARGS_OK;
pResult->ResetVictim();
@ -155,7 +162,7 @@ int CConsole::ParseArgs(CResult *pResult, const char *pFormat)
{
if(!Optional)
{
Error = 1;
Error = PARSEARGS_MISSING_VALUE;
break;
}
@ -191,7 +198,7 @@ int CConsole::ParseArgs(CResult *pResult, const char *pFormat)
pStr++; // skip due to escape
}
else if(pStr[0] == 0)
return 1; // return error
return PARSEARGS_MISSING_VALUE; // return error
*pDst = *pStr;
pDst++;
@ -215,13 +222,7 @@ int CConsole::ParseArgs(CResult *pResult, const char *pFormat)
if(Command == 'r') // rest of the string
break;
else if(Command == 'v') // validate victim
pStr = str_skip_to_whitespace(pStr);
else if(Command == 'i') // validate int
pStr = str_skip_to_whitespace(pStr);
else if(Command == 'f') // validate float
pStr = str_skip_to_whitespace(pStr);
else if(Command == 's') // validate string
else if(Command == 'v' || Command == 'i' || Command == 'f' || Command == 's')
pStr = str_skip_to_whitespace(pStr);
if(pStr[0] != 0) // check for end of string
@ -230,6 +231,32 @@ int CConsole::ParseArgs(CResult *pResult, const char *pFormat)
pStr++;
}
// validate args
if(Command == 'i')
{
// don't validate colors here
if(pfnCallback != &SColorConfigVariable::CommandCallback)
{
int Value;
if(!str_toint(pResult->GetString(pResult->NumArguments() - 1), &Value) ||
Value == std::numeric_limits<int>::max() || Value == std::numeric_limits<int>::min())
{
Error = PARSEARGS_INVALID_INTEGER;
break;
}
}
}
else if(Command == 'f')
{
float Value;
if(!str_tofloat(pResult->GetString(pResult->NumArguments() - 1), &Value) ||
Value == std::numeric_limits<float>::max() || Value == std::numeric_limits<float>::min())
{
Error = PARSEARGS_INVALID_FLOAT;
break;
}
}
if(pVictim)
{
pResult->SetVictim(pVictim);
@ -487,9 +514,14 @@ void CConsole::ExecuteLineStroked(int Stroke, const char *pStr, int ClientId, bo
if(Stroke || IsStrokeCommand)
{
if(ParseArgs(&Result, pCommand->m_pParams))
if(int Error = ParseArgs(&Result, pCommand->m_pParams, pCommand->m_pfnCallback))
{
char aBuf[TEMPCMD_NAME_LENGTH + TEMPCMD_PARAMS_LENGTH + 32];
char aBuf[CMDLINE_LENGTH + 64];
if(Error == PARSEARGS_INVALID_INTEGER)
str_format(aBuf, sizeof(aBuf), "%s is not a valid integer.", Result.GetString(Result.NumArguments() - 1));
else if(Error == PARSEARGS_INVALID_FLOAT)
str_format(aBuf, sizeof(aBuf), "%s is not a valid decimal number.", Result.GetString(Result.NumArguments() - 1));
else
str_format(aBuf, sizeof(aBuf), "Invalid arguments. Usage: %s %s", pCommand->m_pName, pCommand->m_pParams);
Print(OUTPUT_LEVEL_STANDARD, "chatresp", aBuf);
}

View file

@ -115,7 +115,7 @@ class CConsole : public IConsole
const char *GetString(unsigned Index) const override;
int GetInteger(unsigned Index) const override;
float GetFloat(unsigned Index) const override;
ColorHSLA GetColor(unsigned Index, bool Light) const override;
std::optional<ColorHSLA> GetColor(unsigned Index, bool Light) const override;
void RemoveArgument(unsigned Index) override
{
@ -144,7 +144,16 @@ class CConsole : public IConsole
};
int ParseStart(CResult *pResult, const char *pString, int Length);
int ParseArgs(CResult *pResult, const char *pFormat);
enum
{
PARSEARGS_OK = 0,
PARSEARGS_MISSING_VALUE,
PARSEARGS_INVALID_INTEGER,
PARSEARGS_INVALID_FLOAT,
};
int ParseArgs(CResult *pResult, const char *pFormat, FCommandCallback pfnCallback = 0);
/*
this function will set pFormat to the next parameter (i,s,r,v,?) it contains and

View file

@ -1074,11 +1074,9 @@ void CEditor::DoToolbarLayers(CUIRect ToolBar)
// proof button
TB_Top.VSplitLeft(40.0f, &Button, &TB_Top);
static int s_ProofButton = 0;
if(DoButton_Ex(&s_ProofButton, "Proof", MapView()->ProofMode()->IsEnabled(), &Button, 0, "[ctrl+p] Toggles proof borders. These borders represent the area that a player can see with default zoom.", IGraphics::CORNER_L) ||
(m_Dialog == DIALOG_NONE && CLineInput::GetActiveInput() == nullptr && Input()->KeyPress(KEY_P) && ModPressed))
if(DoButton_Ex(&m_QuickActionProof, m_QuickActionProof.Label(), m_QuickActionProof.Active(), &Button, 0, m_QuickActionProof.Description(), IGraphics::CORNER_L))
{
MapView()->ProofMode()->Toggle();
m_QuickActionProof.Call();
}
TB_Top.VSplitLeft(14.0f, &Button, &TB_Top);
@ -1254,10 +1252,9 @@ void CEditor::DoToolbarLayers(CUIRect ToolBar)
// refocus button
{
TB_Bottom.VSplitLeft(50.0f, &Button, &TB_Bottom);
static int s_RefocusButton = 0;
int FocusButtonChecked = MapView()->IsFocused() ? -1 : 1;
if(DoButton_Editor(&s_RefocusButton, "Refocus", FocusButtonChecked, &Button, 0, "[HOME] Restore map focus") || (m_Dialog == DIALOG_NONE && CLineInput::GetActiveInput() == nullptr && Input()->KeyPress(KEY_HOME)))
MapView()->Focus();
if(DoButton_Editor(&m_QuickActionRefocus, m_QuickActionRefocus.Label(), FocusButtonChecked, &Button, 0, m_QuickActionRefocus.Description()) || (m_Dialog == DIALOG_NONE && CLineInput::GetActiveInput() == nullptr && Input()->KeyPress(KEY_HOME)))
m_QuickActionRefocus.Call();
TB_Bottom.VSplitLeft(5.0f, nullptr, &TB_Bottom);
}
@ -4302,12 +4299,9 @@ void CEditor::RenderLayers(CUIRect LayersBox)
if(s_ScrollRegion.AddRect(AddGroupButton))
{
AddGroupButton.HSplitTop(RowHeight, &AddGroupButton, 0);
static int s_AddGroupButton = 0;
if(DoButton_Editor(&s_AddGroupButton, "Add group", 0, &AddGroupButton, IGraphics::CORNER_R, "Adds a new group"))
if(DoButton_Editor(&m_QuickActionAddGroup, m_QuickActionAddGroup.Label(), 0, &AddGroupButton, IGraphics::CORNER_R, m_QuickActionAddGroup.Description()))
{
m_Map.NewGroup();
m_SelectedGroup = m_Map.m_vpGroups.size() - 1;
m_EditorHistory.RecordAction(std::make_shared<CEditorActionGroup>(this, m_SelectedGroup, false));
m_QuickActionAddGroup.Call();
}
}
@ -4806,8 +4800,8 @@ void CEditor::RenderImagesList(CUIRect ToolBox)
{
AddImageButton.HSplitTop(5.0f, nullptr, &AddImageButton);
AddImageButton.HSplitTop(RowHeight, &AddImageButton, nullptr);
if(DoButton_Editor(&s_AddImageButton, "Add", 0, &AddImageButton, 0, "Load a new image to use in the map"))
InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_IMG, "Add Image", "Add", "mapres", false, AddImage, this);
if(DoButton_Editor(&s_AddImageButton, m_QuickActionAddImage.Label(), 0, &AddImageButton, 0, m_QuickActionAddImage.Description()))
m_QuickActionAddImage.Call();
}
s_ScrollRegion.End();
}
@ -5750,9 +5744,9 @@ void CEditor::RenderStatusbar(CUIRect View, CUIRect *pTooltipRect)
CUIRect Button;
View.VSplitRight(100.0f, &View, &Button);
static int s_EnvelopeButton = 0;
if(DoButton_Editor(&s_EnvelopeButton, "Envelopes", ButtonsDisabled ? -1 : m_ActiveExtraEditor == EXTRAEDITOR_ENVELOPES, &Button, 0, "Toggles the envelope editor.") == 1)
if(DoButton_Editor(&s_EnvelopeButton, m_QuickActionEnvelopes.Label(), m_QuickActionEnvelopes.Color(), &Button, 0, m_QuickActionEnvelopes.Description()) == 1)
{
m_ActiveExtraEditor = m_ActiveExtraEditor == EXTRAEDITOR_ENVELOPES ? EXTRAEDITOR_NONE : EXTRAEDITOR_ENVELOPES;
m_QuickActionEnvelopes.Call();
}
View.VSplitRight(10.0f, &View, nullptr);
@ -7919,7 +7913,7 @@ void CEditor::Render()
InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", true, CallbackSaveCopyMap, this);
// ctrl+shift+s to save as
else if(Input()->KeyPress(KEY_S) && ModPressed && ShiftPressed)
InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", true, CallbackSaveMap, this);
m_QuickActionSaveAs.Call();
// ctrl+s to save
else if(Input()->KeyPress(KEY_S) && ModPressed)
{
@ -8362,6 +8356,7 @@ void CEditor::Init()
m_vComponents.emplace_back(m_MapView);
m_vComponents.emplace_back(m_MapSettingsBackend);
m_vComponents.emplace_back(m_LayerSelector);
m_vComponents.emplace_back(m_Prompt);
for(CEditorComponent &Component : m_vComponents)
Component.OnInit(this);

View file

@ -11,6 +11,7 @@
#include <game/client/ui_listbox.h>
#include <game/mapitems.h>
#include <game/editor/enums.h>
#include <game/editor/mapitems/envelope.h>
#include <game/editor/mapitems/layer.h>
#include <game/editor/mapitems/layer_front.h>
@ -38,6 +39,8 @@
#include "layer_selector.h"
#include "map_view.h"
#include "smooth_value.h"
#include <game/editor/prompt.h>
#include <game/editor/quick_action.h>
#include <deque>
#include <functional>
@ -60,7 +63,8 @@ enum
DIALOG_NONE = 0,
DIALOG_FILE,
DIALOG_MAPSETTINGS_ERROR
DIALOG_MAPSETTINGS_ERROR,
DIALOG_QUICK_PROMPT,
};
class CEditorImage;
@ -278,6 +282,7 @@ class CEditor : public IEditor
std::vector<std::reference_wrapper<CEditorComponent>> m_vComponents;
CMapView m_MapView;
CLayerSelector m_LayerSelector;
CPrompt m_Prompt;
bool m_EditorWasUsedBefore = false;
@ -319,7 +324,20 @@ public:
const CMapView *MapView() const { return &m_MapView; }
CLayerSelector *LayerSelector() { return &m_LayerSelector; }
void FillGameTiles(EGameTileOp FillTile) const;
bool CanFillGameTiles() const;
void AddGroup();
void AddTileLayer();
void LayerSelectImage();
bool IsNonGameTileLayerSelected() const;
#define REGISTER_QUICK_ACTION(name, text, callback, disabled, active, button_color, description) CQuickAction m_QuickAction##name;
#include <game/editor/quick_actions.h>
#undef REGISTER_QUICK_ACTION
CEditor() :
#define REGISTER_QUICK_ACTION(name, text, callback, disabled, active, button_color, description) m_QuickAction##name(text, description, callback, disabled, active, button_color),
#include <game/editor/quick_actions.h>
#undef REGISTER_QUICK_ACTION
m_ZoomEnvelopeX(1.0f, 0.1f, 600.0f),
m_ZoomEnvelopeY(640.0f, 0.1f, 32000.0f),
m_MapSettingsCommandContext(m_MapSettingsBackend.NewContext(&m_SettingsCommandInput))

36
src/game/editor/enums.h Normal file
View file

@ -0,0 +1,36 @@
#ifndef GAME_EDITOR_ENUMS_H
#define GAME_EDITOR_ENUMS_H
constexpr const char *g_apGametileOpNames[] = {
"Air",
"Hookable",
"Death",
"Unhookable",
"Hookthrough",
"Freeze",
"Unfreeze",
"Deep Freeze",
"Deep Unfreeze",
"Blue Check-Tele",
"Red Check-Tele",
"Live Freeze",
"Live Unfreeze",
};
enum class EGameTileOp
{
AIR,
HOOKABLE,
DEATH,
UNHOOKABLE,
HOOKTHROUGH,
FREEZE,
UNFREEZE,
DEEP_FREEZE,
DEEP_UNFREEZE,
BLUE_CHECK_TELE,
RED_CHECK_TELE,
LIVE_FREEZE,
LIVE_UNFREEZE,
};
#endif

View file

@ -6,6 +6,7 @@
#include <engine/shared/map.h>
#include <game/editor/editor.h>
#include <game/editor/editor_actions.h>
#include <game/editor/enums.h>
#include <iterator>
#include <numeric>
@ -693,50 +694,41 @@ void CLayerTiles::ShowInfo()
Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
}
CUi::EPopupMenuFunctionResult CLayerTiles::RenderProperties(CUIRect *pToolBox)
void CLayerTiles::FillGameTiles(EGameTileOp Fill)
{
CUIRect Button;
const bool EntitiesLayer = IsEntitiesLayer();
if(!CanFillGameTiles())
return;
std::shared_ptr<CLayerGroup> pGroup = m_pEditor->m_Map.m_vpGroups[m_pEditor->m_SelectedGroup];
// Game tiles can only be constructed if the layer is relative to the game layer
if(!EntitiesLayer && !(pGroup->m_OffsetX % 32) && !(pGroup->m_OffsetY % 32) && pGroup->m_ParallaxX == 100 && pGroup->m_ParallaxY == 100)
int Result = (int)Fill;
switch(Fill)
{
pToolBox->HSplitBottom(12.0f, pToolBox, &Button);
static int s_GameTilesButton = 0;
if(m_pEditor->DoButton_Editor(&s_GameTilesButton, "Game tiles", 0, &Button, 0, "Constructs game tiles from this layer"))
m_pEditor->PopupSelectGametileOpInvoke(m_pEditor->Ui()->MouseX(), m_pEditor->Ui()->MouseY());
const int Selected = m_pEditor->PopupSelectGameTileOpResult();
int Result = Selected;
switch(Selected)
{
case 4:
case EGameTileOp::HOOKTHROUGH:
Result = TILE_THROUGH_CUT;
break;
case 5:
case EGameTileOp::FREEZE:
Result = TILE_FREEZE;
break;
case 6:
case EGameTileOp::UNFREEZE:
Result = TILE_UNFREEZE;
break;
case 7:
case EGameTileOp::DEEP_FREEZE:
Result = TILE_DFREEZE;
break;
case 8:
case EGameTileOp::DEEP_UNFREEZE:
Result = TILE_DUNFREEZE;
break;
case 9:
case EGameTileOp::BLUE_CHECK_TELE:
Result = TILE_TELECHECKIN;
break;
case 10:
case EGameTileOp::RED_CHECK_TELE:
Result = TILE_TELECHECKINEVIL;
break;
case 11:
case EGameTileOp::LIVE_FREEZE:
Result = TILE_LFREEZE;
break;
case 12:
case EGameTileOp::LIVE_UNFREEZE:
Result = TILE_LUNFREEZE;
break;
default:
@ -747,22 +739,6 @@ CUi::EPopupMenuFunctionResult CLayerTiles::RenderProperties(CUIRect *pToolBox)
const int OffsetX = -pGroup->m_OffsetX / 32;
const int OffsetY = -pGroup->m_OffsetY / 32;
static const char *s_apGametileOpNames[] = {
"Air",
"Hookable",
"Death",
"Unhookable",
"Hookthrough",
"Freeze",
"Unfreeze",
"Deep Freeze",
"Deep Unfreeze",
"Blue Check-Tele",
"Red Check-Tele",
"Live Freeze",
"Live Unfreeze",
};
std::vector<std::shared_ptr<IEditorAction>> vpActions;
std::shared_ptr<CLayerTiles> pGLayer = m_pEditor->m_Map.m_pGameLayer;
int GameLayerIndex = std::find(m_pEditor->m_Map.m_pGameGroup->m_vpLayers.begin(), m_pEditor->m_Map.m_pGameGroup->m_vpLayers.end(), pGLayer) - m_pEditor->m_Map.m_pGameGroup->m_vpLayers.begin();
@ -805,7 +781,7 @@ CUi::EPopupMenuFunctionResult CLayerTiles::RenderProperties(CUIRect *pToolBox)
vpActions.push_back(std::make_shared<CEditorBrushDrawAction>(m_pEditor, m_pEditor->m_SelectedGroup));
char aDisplay[256];
str_format(aDisplay, sizeof(aDisplay), "Construct '%s' game tiles (x%d)", s_apGametileOpNames[Selected], Changes);
str_format(aDisplay, sizeof(aDisplay), "Construct '%s' game tiles (x%d)", g_apGametileOpNames[(int)Fill], Changes);
m_pEditor->m_EditorHistory.RecordAction(std::make_shared<CEditorActionBulk>(m_pEditor, vpActions, aDisplay, true));
}
else
@ -905,6 +881,34 @@ CUi::EPopupMenuFunctionResult CLayerTiles::RenderProperties(CUIRect *pToolBox)
m_pEditor->m_EditorHistory.RecordAction(std::make_shared<CEditorActionBulk>(m_pEditor, vpActions, aDisplay, true));
}
}
}
bool CLayerTiles::CanFillGameTiles() const
{
const bool EntitiesLayer = IsEntitiesLayer();
if(EntitiesLayer)
return false;
std::shared_ptr<CLayerGroup> pGroup = m_pEditor->m_Map.m_vpGroups[m_pEditor->m_SelectedGroup];
// Game tiles can only be constructed if the layer is relative to the game layer
return !(pGroup->m_OffsetX % 32) && !(pGroup->m_OffsetY % 32) && pGroup->m_ParallaxX == 100 && pGroup->m_ParallaxY == 100;
}
CUi::EPopupMenuFunctionResult CLayerTiles::RenderProperties(CUIRect *pToolBox)
{
CUIRect Button;
const bool EntitiesLayer = IsEntitiesLayer();
if(CanFillGameTiles())
{
pToolBox->HSplitBottom(12.0f, pToolBox, &Button);
static int s_GameTilesButton = 0;
if(m_pEditor->DoButton_Editor(&s_GameTilesButton, "Game tiles", 0, &Button, 0, "Constructs game tiles from this layer"))
m_pEditor->PopupSelectGametileOpInvoke(m_pEditor->Ui()->MouseX(), m_pEditor->Ui()->MouseY());
const int Selected = m_pEditor->PopupSelectGameTileOpResult();
FillGameTiles((EGameTileOp)Selected);
}
if(m_pEditor->m_Map.m_pGameLayer.get() != this)

View file

@ -2,6 +2,7 @@
#define GAME_EDITOR_MAPITEMS_LAYER_TILES_H
#include <game/editor/editor_trackers.h>
#include <game/editor/enums.h>
#include <map>
#include "layer.h"
@ -122,6 +123,8 @@ public:
void BrushSelecting(CUIRect Rect) override;
int BrushGrab(std::shared_ptr<CLayerGroup> pBrush, CUIRect Rect) override;
void FillSelection(bool Empty, std::shared_ptr<CLayer> pBrush, CUIRect Rect) override;
void FillGameTiles(EGameTileOp Fill);
bool CanFillGameTiles() const;
void BrushDraw(std::shared_ptr<CLayer> pBrush, float wx, float wy) override;
void BrushFlipX() override;
void BrushFlipY() override;

View file

@ -68,17 +68,9 @@ CUi::EPopupMenuFunctionResult CEditor::PopupMenuFile(void *pContext, CUIRect Vie
View.HSplitTop(2.0f, nullptr, &View);
View.HSplitTop(12.0f, &Slot, &View);
if(pEditor->DoButton_MenuItem(&s_OpenCurrentMapButton, "Load Current Map", 0, &Slot, 0, "Opens the current in game map for editing (ctrl+alt+l)"))
if(pEditor->DoButton_MenuItem(&s_OpenCurrentMapButton, pEditor->m_QuickActionLoadCurrentMap.Label(), 0, &Slot, 0, pEditor->m_QuickActionLoadCurrentMap.Description()))
{
if(pEditor->HasUnsavedData())
{
pEditor->m_PopupEventType = POPEVENT_LOADCURRENT;
pEditor->m_PopupEventActivated = true;
}
else
{
pEditor->LoadCurrentMap();
}
pEditor->m_QuickActionLoadCurrentMap.Call();
return CUi::POPUP_CLOSE_CURRENT;
}
@ -107,9 +99,9 @@ CUi::EPopupMenuFunctionResult CEditor::PopupMenuFile(void *pContext, CUIRect Vie
View.HSplitTop(2.0f, nullptr, &View);
View.HSplitTop(12.0f, &Slot, &View);
if(pEditor->DoButton_MenuItem(&s_SaveAsButton, "Save As", 0, &Slot, 0, "Saves the current map under a new name (ctrl+shift+s)"))
if(pEditor->DoButton_MenuItem(&s_SaveAsButton, pEditor->m_QuickActionSaveAs.Label(), 0, &Slot, 0, pEditor->m_QuickActionSaveAs.Description()))
{
pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", true, CEditor::CallbackSaveMap, pEditor);
pEditor->m_QuickActionSaveAs.Call();
return CUi::POPUP_CLOSE_CURRENT;
}
@ -314,20 +306,20 @@ CUi::EPopupMenuFunctionResult CEditor::PopupMenuSettings(void *pContext, CUIRect
static int s_ButtonOff = 0;
static int s_ButtonDec = 0;
static int s_ButtonHex = 0;
if(pEditor->DoButton_Ex(&s_ButtonOff, "Off", pEditor->m_ShowTileInfo == SHOW_TILE_OFF, &Off, 0, "Do not show tile information", IGraphics::CORNER_L))
CQuickAction *pAction = &pEditor->m_QuickActionShowInfoOff;
if(pEditor->DoButton_Ex(&s_ButtonOff, pAction->LabelShort(), pAction->Active(), &Off, 0, pAction->Description(), IGraphics::CORNER_L))
{
pEditor->m_ShowTileInfo = SHOW_TILE_OFF;
pEditor->m_ShowEnvelopePreview = SHOWENV_NONE;
pAction->Call();
}
if(pEditor->DoButton_Ex(&s_ButtonDec, "Dec", pEditor->m_ShowTileInfo == SHOW_TILE_DECIMAL, &Dec, 0, "[ctrl+i] Show tile information", IGraphics::CORNER_NONE))
pAction = &pEditor->m_QuickActionShowInfoDec;
if(pEditor->DoButton_Ex(&s_ButtonDec, pAction->LabelShort(), pAction->Active(), &Dec, 0, pAction->Description(), IGraphics::CORNER_NONE))
{
pEditor->m_ShowTileInfo = SHOW_TILE_DECIMAL;
pEditor->m_ShowEnvelopePreview = SHOWENV_NONE;
pAction->Call();
}
if(pEditor->DoButton_Ex(&s_ButtonHex, "Hex", pEditor->m_ShowTileInfo == SHOW_TILE_HEXADECIMAL, &Hex, 0, "[ctrl+shift+i] Show tile information in hexadecimal", IGraphics::CORNER_R))
pAction = &pEditor->m_QuickActionShowInfoHex;
if(pEditor->DoButton_Ex(&s_ButtonHex, pAction->LabelShort(), pAction->Active(), &Hex, 0, pAction->Description(), IGraphics::CORNER_R))
{
pEditor->m_ShowTileInfo = SHOW_TILE_HEXADECIMAL;
pEditor->m_ShowEnvelopePreview = SHOWENV_NONE;
pAction->Call();
}
}
@ -578,16 +570,9 @@ CUi::EPopupMenuFunctionResult CEditor::PopupGroup(void *pContext, CUIRect View,
// new tile layer
View.HSplitBottom(5.0f, &View, nullptr);
View.HSplitBottom(12.0f, &View, &Button);
static int s_NewTileLayerButton = 0;
if(pEditor->DoButton_Editor(&s_NewTileLayerButton, "Add tile layer", 0, &Button, 0, "Creates a new tile layer"))
if(pEditor->DoButton_Editor(&pEditor->m_QuickActionAddTileLayer, pEditor->m_QuickActionAddTileLayer.Label(), 0, &Button, 0, pEditor->m_QuickActionAddTileLayer.Description()))
{
std::shared_ptr<CLayer> pTileLayer = std::make_shared<CLayerTiles>(pEditor, pEditor->m_Map.m_pGameLayer->m_Width, pEditor->m_Map.m_pGameLayer->m_Height);
pTileLayer->m_pEditor = pEditor;
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->AddLayer(pTileLayer);
int LayerIndex = pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_vpLayers.size() - 1;
pEditor->SelectLayer(LayerIndex);
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_Collapse = false;
pEditor->m_EditorHistory.RecordAction(std::make_shared<CEditorActionAddLayer>(pEditor, pEditor->m_SelectedGroup, LayerIndex));
pEditor->m_QuickActionAddTileLayer.Call();
return CUi::POPUP_CLOSE_CURRENT;
}

169
src/game/editor/prompt.cpp Normal file
View file

@ -0,0 +1,169 @@
#include <engine/keys.h>
#include <game/client/ui_listbox.h>
#include <game/editor/quick_action.h>
#include "editor.h"
#include "prompt.h"
bool FuzzyMatch(const char *pHaystack, const char *pNeedle)
{
if(!pNeedle || !pNeedle[0])
return false;
char aBuf[2] = {0};
const char *pHit = pHaystack;
int NeedleLen = str_length(pNeedle);
for(int i = 0; i < NeedleLen; i++)
{
if(!pHit)
return false;
aBuf[0] = pNeedle[i];
pHit = str_find_nocase(pHit, aBuf);
if(pHit)
pHit++;
}
return pHit;
}
bool CPrompt::IsActive()
{
return CEditorComponent::IsActive() || Editor()->m_Dialog == DIALOG_QUICK_PROMPT;
}
void CPrompt::SetActive()
{
Editor()->m_Dialog = DIALOG_QUICK_PROMPT;
CEditorComponent::SetActive();
Ui()->SetActiveItem(&m_PromptInput);
}
void CPrompt::SetInactive()
{
m_ResetFilterResults = true;
m_PromptInput.Clear();
if(Editor()->m_Dialog == DIALOG_QUICK_PROMPT)
Editor()->m_Dialog = DIALOG_NONE;
CEditorComponent::SetInactive();
}
bool CPrompt::OnInput(const IInput::CEvent &Event)
{
if(Input()->ModifierIsPressed() && Input()->KeyIsPressed(KEY_P))
{
SetActive();
}
return false;
}
void CPrompt::OnInit(CEditor *pEditor)
{
CEditorComponent::OnInit(pEditor);
#define REGISTER_QUICK_ACTION(name, text, callback, disabled, active, button_color, description) m_vQuickActions.emplace_back(&Editor()->m_QuickAction##name);
#include <game/editor/quick_actions.h>
#undef REGISTER_QUICK_ACTION
}
void CPrompt::OnRender(CUIRect _)
{
if(!IsActive())
return;
if(Ui()->ConsumeHotkey(CUi::HOTKEY_ESCAPE))
{
SetInactive();
return;
}
static CListBox s_ListBox;
CUIRect Prompt, PromptBox;
CUIRect Suggestions;
Ui()->MapScreen();
CUIRect Overlay = *Ui()->Screen();
Overlay.Draw(ColorRGBA(0, 0, 0, 0.33f), IGraphics::CORNER_NONE, 0.0f);
CUIRect Background;
Overlay.VMargin(150.0f, &Background);
Background.HMargin(50.0f, &Background);
Background.Draw(ColorRGBA(0, 0, 0, 0.80f), IGraphics::CORNER_ALL, 5.0f);
Background.Margin(10.0f, &Prompt);
Prompt.VSplitMid(nullptr, &PromptBox);
Prompt.HSplitTop(16.0f, &PromptBox, &Suggestions);
PromptBox.Draw(ColorRGBA(0, 0, 0, 0.75f), IGraphics::CORNER_ALL, 2.0f);
Suggestions.y += 6.0f;
if(Ui()->DoClearableEditBox(&m_PromptInput, &PromptBox, 10.0f) || m_ResetFilterResults)
{
m_PromptSelectedIndex = 0;
m_vpFilteredPromptList.clear();
if(m_ResetFilterResults && m_pLastAction && !m_pLastAction->Disabled())
{
m_vpFilteredPromptList.push_back(m_pLastAction);
}
for(auto *pQuickAction : m_vQuickActions)
{
if(pQuickAction->Disabled())
continue;
if(m_PromptInput.IsEmpty() || FuzzyMatch(pQuickAction->Label(), m_PromptInput.GetString()))
{
bool Skip = false;
if(m_ResetFilterResults)
if(pQuickAction == m_pLastAction)
Skip = true;
if(!Skip)
m_vpFilteredPromptList.push_back(pQuickAction);
}
}
m_ResetFilterResults = false;
}
s_ListBox.SetActive(!Ui()->IsPopupOpen());
s_ListBox.DoStart(15.0f, m_vpFilteredPromptList.size(), 1, 5, m_PromptSelectedIndex, &Suggestions, false);
for(size_t i = 0; i < m_vpFilteredPromptList.size(); i++)
{
const CListboxItem Item = s_ListBox.DoNextItem(m_vpFilteredPromptList[i], m_PromptSelectedIndex >= 0 && (size_t)m_PromptSelectedIndex == i);
if(!Item.m_Visible)
continue;
CUIRect LabelColumn, DescColumn;
Item.m_Rect.VSplitLeft(5.0f, nullptr, &LabelColumn);
LabelColumn.VSplitLeft(100.0f, &LabelColumn, &DescColumn);
LabelColumn.VSplitRight(5.0f, &LabelColumn, nullptr);
SLabelProperties Props;
Props.m_MaxWidth = LabelColumn.w;
Props.m_EllipsisAtEnd = true;
Ui()->DoLabel(&LabelColumn, m_vpFilteredPromptList[i]->Label(), 10.0f, TEXTALIGN_ML, Props);
Props.m_MaxWidth = DescColumn.w;
ColorRGBA DescColor = TextRender()->DefaultTextColor();
DescColor.a = Item.m_Selected ? 1.0f : 0.8f;
TextRender()->TextColor(DescColor);
Ui()->DoLabel(&DescColumn, m_vpFilteredPromptList[i]->Description(), 10.0f, TEXTALIGN_MR, Props);
TextRender()->TextColor(TextRender()->DefaultTextColor());
}
const int NewSelected = s_ListBox.DoEnd();
if(m_PromptSelectedIndex != NewSelected)
{
m_PromptSelectedIndex = NewSelected;
}
if(s_ListBox.WasItemActivated())
{
if(m_PromptSelectedIndex >= 0)
{
CQuickAction *pBtn = m_vpFilteredPromptList[m_PromptSelectedIndex];
SetInactive();
pBtn->Call();
m_pLastAction = pBtn;
}
}
}

29
src/game/editor/prompt.h Normal file
View file

@ -0,0 +1,29 @@
#ifndef GAME_EDITOR_PROMPT_H
#define GAME_EDITOR_PROMPT_H
#include <game/client/lineinput.h>
#include <game/client/ui_rect.h>
#include <game/editor/quick_action.h>
#include "component.h"
class CPrompt : public CEditorComponent
{
bool m_ResetFilterResults = true;
CQuickAction *m_pLastAction = nullptr;
int m_PromptSelectedIndex = -1;
std::vector<CQuickAction *> m_vpFilteredPromptList;
std::vector<CQuickAction *> m_vQuickActions;
CLineInputBuffered<512> m_PromptInput;
public:
void OnInit(CEditor *pEditor) override;
bool OnInput(const IInput::CEvent &Event) override;
void OnRender(CUIRect _) override;
bool IsActive();
void SetActive();
void SetInactive();
};
#endif

View file

@ -0,0 +1,69 @@
#ifndef GAME_EDITOR_QUICK_ACTION_H
#define GAME_EDITOR_QUICK_ACTION_H
#include <functional>
#include <utility>
typedef std::function<void()> FButtonClickCallback;
typedef std::function<bool()> FButtonDisabledCallback;
typedef std::function<bool()> FButtonActiveCallback;
typedef std::function<int()> FButtonColorCallback;
class CQuickAction
{
private:
const char *m_pLabel;
const char *m_pDescription;
FButtonClickCallback m_pfnCallback;
FButtonDisabledCallback m_pfnDisabledCallback;
FButtonActiveCallback m_pfnActiveCallback;
FButtonColorCallback m_pfnColorCallback;
public:
CQuickAction(
const char *pLabel,
const char *pDescription,
FButtonClickCallback pfnCallback,
FButtonDisabledCallback pfnDisabledCallback,
FButtonActiveCallback pfnActiveCallback,
FButtonColorCallback pfnColorCallback) :
m_pLabel(pLabel),
m_pDescription(pDescription),
m_pfnCallback(std::move(pfnCallback)),
m_pfnDisabledCallback(std::move(pfnDisabledCallback)),
m_pfnActiveCallback(std::move(pfnActiveCallback)),
m_pfnColorCallback(std::move(pfnColorCallback))
{
}
// code to run when the action is triggered
void Call() const { m_pfnCallback(); }
// bool that indicates if the action can be performed not or not
bool Disabled() { return m_pfnDisabledCallback(); }
// bool that indicates if the action is currently running
// only applies to actions that can be turned on or off like proof borders
bool Active() { return m_pfnActiveCallback(); }
// color "enum" that represents the state of the quick actions button
// used as Checked argument for DoButton_Editor()
int Color() { return m_pfnColorCallback(); }
const char *Label() const { return m_pLabel; }
// skips to the part of the label after the first colon
// useful for buttons that only show the state
const char *LabelShort() const
{
const char *pShort = str_find(m_pLabel, ": ");
if(!pShort)
return m_pLabel;
return pShort + 2;
}
const char *Description() const { return m_pDescription; }
};
#endif

View file

@ -0,0 +1,71 @@
#include <game/mapitems.h>
#include "editor.h"
#include "editor_actions.h"
void CEditor::FillGameTiles(EGameTileOp FillTile) const
{
std::shared_ptr<CLayerTiles> pTileLayer = std::static_pointer_cast<CLayerTiles>(GetSelectedLayerType(0, LAYERTYPE_TILES));
if(pTileLayer)
pTileLayer->FillGameTiles(FillTile);
}
bool CEditor::CanFillGameTiles() const
{
std::shared_ptr<CLayerTiles> pTileLayer = std::static_pointer_cast<CLayerTiles>(GetSelectedLayerType(0, LAYERTYPE_TILES));
if(pTileLayer)
return pTileLayer->CanFillGameTiles();
return false;
}
void CEditor::AddGroup()
{
m_Map.NewGroup();
m_SelectedGroup = m_Map.m_vpGroups.size() - 1;
m_EditorHistory.RecordAction(std::make_shared<CEditorActionGroup>(this, m_SelectedGroup, false));
}
void CEditor::AddTileLayer()
{
std::shared_ptr<CLayer> pTileLayer = std::make_shared<CLayerTiles>(this, m_Map.m_pGameLayer->m_Width, m_Map.m_pGameLayer->m_Height);
pTileLayer->m_pEditor = this;
m_Map.m_vpGroups[m_SelectedGroup]->AddLayer(pTileLayer);
int LayerIndex = m_Map.m_vpGroups[m_SelectedGroup]->m_vpLayers.size() - 1;
SelectLayer(LayerIndex);
m_Map.m_vpGroups[m_SelectedGroup]->m_Collapse = false;
m_EditorHistory.RecordAction(std::make_shared<CEditorActionAddLayer>(this, m_SelectedGroup, LayerIndex));
}
bool CEditor::IsNonGameTileLayerSelected() const
{
std::shared_ptr<CLayer> pLayer = GetSelectedLayer(0);
if(!pLayer)
return false;
if(pLayer->m_Type != LAYERTYPE_TILES)
return false;
if(
pLayer == m_Map.m_pGameLayer ||
pLayer == m_Map.m_pFrontLayer ||
pLayer == m_Map.m_pSwitchLayer ||
pLayer == m_Map.m_pTeleLayer ||
pLayer == m_Map.m_pSpeedupLayer ||
pLayer == m_Map.m_pTuneLayer)
return false;
return true;
}
void CEditor::LayerSelectImage()
{
if(!IsNonGameTileLayerSelected())
return;
std::shared_ptr<CLayer> pLayer = GetSelectedLayer(0);
std::shared_ptr<CLayerTiles> pTiles = std::static_pointer_cast<CLayerTiles>(pLayer);
static SLayerPopupContext s_LayerPopupContext = {};
s_LayerPopupContext.m_pEditor = this;
Ui()->DoPopupMenu(&s_LayerPopupContext, Ui()->MouseX(), Ui()->MouseY(), 120, 270, &s_LayerPopupContext, PopupLayer);
PopupSelectImageInvoke(pTiles->m_Image, Ui()->MouseX(), Ui()->MouseY());
}

View file

@ -0,0 +1,213 @@
// This file can be included several times.
#ifndef REGISTER_QUICK_ACTION
#define REGISTER_QUICK_ACTION(name, text, callback, disabled, active, button_color, description)
#endif
#define ALWAYS_FALSE []() -> bool { return false; }
#define DEFAULT_BTN []() -> int { return -1; }
REGISTER_QUICK_ACTION(
GameTilesAir,
"Game tiles: Air",
[&]() { FillGameTiles(EGameTileOp::AIR); },
[&]() -> bool { return !CanFillGameTiles(); },
ALWAYS_FALSE,
DEFAULT_BTN,
"")
REGISTER_QUICK_ACTION(
GameTilesHookable,
"Game tiles: Hookable",
[&]() { FillGameTiles(EGameTileOp::HOOKABLE); },
[&]() -> bool { return !CanFillGameTiles(); },
ALWAYS_FALSE,
DEFAULT_BTN,
"")
REGISTER_QUICK_ACTION(
GameTilesDeath,
"Game tiles: Death",
[&]() { FillGameTiles(EGameTileOp::DEATH); },
[&]() -> bool { return !CanFillGameTiles(); },
ALWAYS_FALSE,
DEFAULT_BTN,
"")
REGISTER_QUICK_ACTION(
GameTilesUnhookable,
"Game tiles: Unhookable",
[&]() { FillGameTiles(EGameTileOp::UNHOOKABLE); },
[&]() -> bool { return !CanFillGameTiles(); },
ALWAYS_FALSE,
DEFAULT_BTN,
"")
REGISTER_QUICK_ACTION(
GameTilesHookthrough,
"Game tiles: Hookthrough",
[&]() { FillGameTiles(EGameTileOp::HOOKTHROUGH); },
[&]() -> bool { return !CanFillGameTiles(); },
ALWAYS_FALSE,
DEFAULT_BTN,
"")
REGISTER_QUICK_ACTION(
GameTilesFreeze,
"Game tiles: Freeze",
[&]() { FillGameTiles(EGameTileOp::FREEZE); },
[&]() -> bool { return !CanFillGameTiles(); },
ALWAYS_FALSE,
DEFAULT_BTN,
"")
REGISTER_QUICK_ACTION(
GameTilesUnfreeze,
"Game tiles: Unfreeze",
[&]() { FillGameTiles(EGameTileOp::UNFREEZE); },
[&]() -> bool { return !CanFillGameTiles(); },
ALWAYS_FALSE,
DEFAULT_BTN,
"")
REGISTER_QUICK_ACTION(
GameTilesDeepFreeze,
"Game tiles: Deep Freeze",
[&]() { FillGameTiles(EGameTileOp::DEEP_FREEZE); },
[&]() -> bool { return !CanFillGameTiles(); },
ALWAYS_FALSE,
DEFAULT_BTN,
"")
REGISTER_QUICK_ACTION(
GameTilesDeepUnfreeze,
"Game tiles: Deep Unfreeze",
[&]() { FillGameTiles(EGameTileOp::DEEP_UNFREEZE); },
[&]() -> bool { return !CanFillGameTiles(); },
ALWAYS_FALSE,
DEFAULT_BTN,
"")
REGISTER_QUICK_ACTION(
GameTilesBlueCheckTele,
"Game tiles: Blue Check Tele",
[&]() { FillGameTiles(EGameTileOp::BLUE_CHECK_TELE); },
[&]() -> bool { return !CanFillGameTiles(); },
ALWAYS_FALSE,
DEFAULT_BTN,
"")
REGISTER_QUICK_ACTION(
GameTilesRedCheckTele,
"Game tiles: Red Check Tele",
[&]() { FillGameTiles(EGameTileOp::RED_CHECK_TELE); },
[&]() -> bool { return !CanFillGameTiles(); },
ALWAYS_FALSE,
DEFAULT_BTN,
"")
REGISTER_QUICK_ACTION(
GameTilesLiveFreeze,
"Game tiles: Live Freeze",
[&]() { FillGameTiles(EGameTileOp::LIVE_FREEZE); },
[&]() -> bool { return !CanFillGameTiles(); },
ALWAYS_FALSE,
DEFAULT_BTN,
"")
REGISTER_QUICK_ACTION(
GameTilesLiveUnfreeze,
"Game tiles: Live Unfreeze",
[&]() { FillGameTiles(EGameTileOp::LIVE_UNFREEZE); },
[&]() -> bool { return !CanFillGameTiles(); },
ALWAYS_FALSE,
DEFAULT_BTN,
"")
REGISTER_QUICK_ACTION(
AddGroup, "Add group", [&]() { AddGroup(); }, ALWAYS_FALSE, ALWAYS_FALSE, DEFAULT_BTN, "Adds a new group")
REGISTER_QUICK_ACTION(
Refocus, "Refocus", [&]() { MapView()->Focus(); }, ALWAYS_FALSE, ALWAYS_FALSE, DEFAULT_BTN, "[HOME] Restore map focus")
REGISTER_QUICK_ACTION(
Proof,
"Proof",
[&]() { MapView()->ProofMode()->Toggle(); },
ALWAYS_FALSE,
[&]() -> bool { return MapView()->ProofMode()->IsEnabled(); },
DEFAULT_BTN,
"Toggles proof borders. These borders represent the area that a player can see with default zoom.")
REGISTER_QUICK_ACTION(
AddTileLayer, "Add tile layer", [&]() { AddTileLayer(); }, ALWAYS_FALSE, ALWAYS_FALSE, DEFAULT_BTN, "Creates a new tile layer.")
REGISTER_QUICK_ACTION(
SaveAs,
"Save As",
[&]() { InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save As", "maps", true, CEditor::CallbackSaveMap, this); },
ALWAYS_FALSE,
ALWAYS_FALSE,
DEFAULT_BTN,
"Saves the current map under a new name (ctrl+shift+s)")
REGISTER_QUICK_ACTION(
LoadCurrentMap,
"Load Current Map",
[&]() {
if(HasUnsavedData())
{
m_PopupEventType = POPEVENT_LOADCURRENT;
m_PopupEventActivated = true;
}
else
{
LoadCurrentMap();
}
},
ALWAYS_FALSE,
ALWAYS_FALSE,
DEFAULT_BTN,
"Opens the current in game map for editing (ctrl+alt+l)")
REGISTER_QUICK_ACTION(
Envelopes,
"Envelopes",
[&]() { m_ActiveExtraEditor = m_ActiveExtraEditor == EXTRAEDITOR_ENVELOPES ? EXTRAEDITOR_NONE : EXTRAEDITOR_ENVELOPES; },
ALWAYS_FALSE,
ALWAYS_FALSE,
[&]() -> int { return m_ShowPicker ? -1 : m_ActiveExtraEditor == EXTRAEDITOR_ENVELOPES; },
"Toggles the envelope editor.")
REGISTER_QUICK_ACTION(
AddImage,
"Add Image",
[&]() { InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_IMG, "Add Image", "Add", "mapres", false, AddImage, this); },
ALWAYS_FALSE,
ALWAYS_FALSE,
DEFAULT_BTN,
"Load a new image to use in the map")
REGISTER_QUICK_ACTION(
LayerPropAddImage,
"Layer: Add Image",
[&]() { LayerSelectImage(); },
[&]() -> bool { return !IsNonGameTileLayerSelected(); },
ALWAYS_FALSE,
DEFAULT_BTN,
"Pick mapres image for currently selected layer")
REGISTER_QUICK_ACTION(
ShowInfoOff,
"Show Info: Off",
[&]() {
m_ShowTileInfo = SHOW_TILE_OFF;
m_ShowEnvelopePreview = SHOWENV_NONE;
},
ALWAYS_FALSE,
[&]() -> bool { return m_ShowTileInfo == SHOW_TILE_OFF; },
DEFAULT_BTN,
"Do not show tile information")
REGISTER_QUICK_ACTION(
ShowInfoDec,
"Show Info: Dec",
[&]() {
m_ShowTileInfo = SHOW_TILE_DECIMAL;
m_ShowEnvelopePreview = SHOWENV_NONE;
},
ALWAYS_FALSE,
[&]() -> bool { return m_ShowTileInfo == SHOW_TILE_DECIMAL; },
DEFAULT_BTN,
"[ctrl+i] Show tile information")
REGISTER_QUICK_ACTION(
ShowInfoHex,
"Show Info: Hex",
[&]() {
m_ShowTileInfo = SHOW_TILE_HEXADECIMAL;
m_ShowEnvelopePreview = SHOWENV_NONE;
},
ALWAYS_FALSE,
[&]() -> bool { return m_ShowTileInfo == SHOW_TILE_HEXADECIMAL; },
DEFAULT_BTN,
"[ctrl+shift+i] Show tile information in hexadecimal")
#undef ALWAYS_FALSE
#undef DEFAULT_BTN

View file

@ -260,7 +260,7 @@ void CCharacter::HandleNinja()
GameServer()->CreateDamageInd(m_Pos, 0, NinjaTime / Server()->TickSpeed(), TeamMask() & GameServer()->ClientsMaskExcludeClientVersionAndHigher(VERSION_DDNET_NEW_HUD));
}
m_Armor = clamp(10 - (NinjaTime / 15), 0, 10);
GameServer()->m_pController->SetArmorProgress(this, NinjaTime);
// force ninja Weapon
SetWeapon(WEAPON_NINJA);
@ -442,7 +442,7 @@ void CCharacter::FireWeapon()
if(m_PainSoundTimer <= 0 && !(m_LatestPrevInput.m_Fire & 1))
{
m_PainSoundTimer = 1 * Server()->TickSpeed();
GameServer()->CreateSound(m_Pos, SOUND_PLAYER_PAIN_LONG, TeamMask());
GameServer()->CreateSound(m_Pos, SOUND_PLAYER_PAIN_LONG, TeamMask()); // NOLINT(clang-analyzer-unix.Malloc)
}
return;
}
@ -459,7 +459,7 @@ void CCharacter::FireWeapon()
{
// reset objects Hit
m_NumObjectsHit = 0;
GameServer()->CreateSound(m_Pos, SOUND_HAMMER_FIRE, TeamMask());
GameServer()->CreateSound(m_Pos, SOUND_HAMMER_FIRE, TeamMask()); // NOLINT(clang-analyzer-unix.Malloc)
Antibot()->OnHammerFire(m_pPlayer->GetCid());
@ -567,7 +567,7 @@ void CCharacter::FireWeapon()
MouseTarget // MouseTarget
);
GameServer()->CreateSound(m_Pos, SOUND_GRENADE_FIRE, TeamMask());
GameServer()->CreateSound(m_Pos, SOUND_GRENADE_FIRE, TeamMask()); // NOLINT(clang-analyzer-unix.Malloc)
}
break;
@ -589,7 +589,7 @@ void CCharacter::FireWeapon()
m_Core.m_Ninja.m_CurrentMoveTime = g_pData->m_Weapons.m_Ninja.m_Movetime * Server()->TickSpeed() / 1000;
m_Core.m_Ninja.m_OldVelAmount = length(m_Core.m_Vel);
GameServer()->CreateSound(m_Pos, SOUND_NINJA_FIRE, TeamMask());
GameServer()->CreateSound(m_Pos, SOUND_NINJA_FIRE, TeamMask()); // NOLINT(clang-analyzer-unix.Malloc)
}
break;
}
@ -2055,7 +2055,7 @@ void CCharacter::ForceSetRescue(int RescueMode)
void CCharacter::DDRaceTick()
{
mem_copy(&m_Input, &m_SavedInput, sizeof(m_Input));
m_Armor = clamp(10 - (m_FreezeTime / 15), 0, 10);
GameServer()->m_pController->SetArmorProgress(this, m_FreezeTime);
if(m_Input.m_Direction != 0 || m_Input.m_Jump != 0)
m_LastMove = Server()->Tick();

View file

@ -94,6 +94,7 @@ bool CLaser::HitCharacter(vec2 From, vec2 To)
{
pHit->UnFreeze();
}
pHit->TakeDamage(vec2(0, 0), 0, m_Owner, m_Type);
return true;
}

View file

@ -177,6 +177,8 @@ void CProjectile::Tick()
pChr->Freeze();
}
}
else if(pTargetChr)
pTargetChr->TakeDamage(vec2(0, 0), 0, m_Owner, m_Type);
if(pOwnerChar && !GameLayerClipped(ColPos) &&
((m_Type == WEAPON_GRENADE && pOwnerChar->HasTelegunGrenade()) || (m_Type == WEAPON_GUN && pOwnerChar->HasTelegunGun())))

View file

@ -95,6 +95,7 @@ public:
virtual void OnCharacterSpawn(class CCharacter *pChr);
virtual void HandleCharacterTiles(class CCharacter *pChr, int MapIndex);
virtual void SetArmorProgress(CCharacter *pCharacer, int Progress){};
/*
Function: OnEntity

View file

@ -112,6 +112,11 @@ void CGameControllerDDRace::HandleCharacterTiles(CCharacter *pChr, int MapIndex)
}
}
void CGameControllerDDRace::SetArmorProgress(CCharacter *pCharacer, int Progress)
{
pCharacer->SetArmor(clamp(10 - (Progress / 15), 0, 10));
}
void CGameControllerDDRace::OnPlayerConnect(CPlayer *pPlayer)
{
IGameController::OnPlayerConnect(pPlayer);

View file

@ -13,6 +13,7 @@ public:
CScore *Score();
void HandleCharacterTiles(class CCharacter *pChr, int MapIndex) override;
void SetArmorProgress(CCharacter *pCharacer, int Progress) override;
void OnPlayerConnect(class CPlayer *pPlayer) override;
void OnPlayerDisconnect(class CPlayer *pPlayer, const char *pReason) override;

View file

@ -78,16 +78,16 @@ void CTeeInfo::ToSixup()
{
int ColorBody = ColorHSLA(m_ColorBody).UnclampLighting().Pack(ms_DarkestLGT7);
int ColorFeet = ColorHSLA(m_ColorFeet).UnclampLighting().Pack(ms_DarkestLGT7);
m_aUseCustomColors[0] = true;
m_aUseCustomColors[1] = true;
m_aUseCustomColors[2] = true;
m_aUseCustomColors[3] = true;
m_aUseCustomColors[4] = true;
m_aSkinPartColors[0] = ColorBody;
m_aSkinPartColors[1] = 0x22FFFFFF;
m_aSkinPartColors[2] = ColorBody;
m_aSkinPartColors[3] = ColorBody;
m_aSkinPartColors[4] = ColorFeet;
m_aUseCustomColors[protocol7::SKINPART_BODY] = true;
m_aUseCustomColors[protocol7::SKINPART_MARKING] = true;
m_aUseCustomColors[protocol7::SKINPART_DECORATION] = true;
m_aUseCustomColors[protocol7::SKINPART_HANDS] = true;
m_aUseCustomColors[protocol7::SKINPART_FEET] = true;
m_aSkinPartColors[protocol7::SKINPART_BODY] = ColorBody;
m_aSkinPartColors[protocol7::SKINPART_MARKING] = 0x22FFFFFF;
m_aSkinPartColors[protocol7::SKINPART_DECORATION] = ColorBody;
m_aSkinPartColors[protocol7::SKINPART_HANDS] = ColorBody;
m_aSkinPartColors[protocol7::SKINPART_FEET] = ColorFeet;
}
}
@ -137,6 +137,10 @@ void CTeeInfo::FromSixup()
str_copy(m_aSkinName, g_aStdSkins[BestSkin].m_aSkinName, sizeof(m_aSkinName));
m_UseCustomColor = true;
m_ColorBody = ColorHSLA(m_aUseCustomColors[0] ? m_aSkinPartColors[0] : 255).UnclampLighting(ms_DarkestLGT7).Pack(ColorHSLA::DARKEST_LGT);
m_ColorFeet = ColorHSLA(m_aUseCustomColors[4] ? m_aSkinPartColors[4] : 255).UnclampLighting(ms_DarkestLGT7).Pack(ColorHSLA::DARKEST_LGT);
m_ColorBody = ColorHSLA(m_aUseCustomColors[protocol7::SKINPART_BODY] ? m_aSkinPartColors[protocol7::SKINPART_BODY] : 255)
.UnclampLighting(ms_DarkestLGT7)
.Pack(ColorHSLA::DARKEST_LGT);
m_ColorFeet = ColorHSLA(m_aUseCustomColors[protocol7::SKINPART_FEET] ? m_aSkinPartColors[protocol7::SKINPART_FEET] : 255)
.UnclampLighting(ms_DarkestLGT7)
.Pack(ColorHSLA::DARKEST_LGT);
}

View file

@ -109,8 +109,8 @@ void cxxbridge1$IConsole_IResult$GetString(const ::IConsole_IResult &self, ::std
}
void cxxbridge1$IConsole_IResult$GetColor(const ::IConsole_IResult &self, ::std::uint32_t Index, bool Light, ::ColorHSLA *return$) noexcept {
::ColorHSLA (::IConsole_IResult::*GetColor$)(::std::uint32_t, bool) const = &::IConsole_IResult::GetColor;
new (return$) ::ColorHSLA((self.*GetColor$)(Index, Light));
std::optional<::ColorHSLA> (::IConsole_IResult::*GetColor$)(::std::uint32_t, bool) const = &::IConsole_IResult::GetColor;
new(return$)::ColorHSLA((self.*GetColor$)(Index, Light).value_or(::ColorHSLA(0, 0, 0)));
}
::std::int32_t cxxbridge1$IConsole_IResult$NumArguments(const ::IConsole_IResult &self) noexcept {