From 1810d972f457f24af9a800dd275365703fa0d3b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Fri, 23 Jun 2023 17:39:05 +0200 Subject: [PATCH] Autosave copy of current editor map periodically to `auto` folder A copy of the map currently open in the editor is saved every 10 minutes to the `maps/auto` folder (interval configurable, see below). The automatically saved map uses the filename of the original map with an additional timestamp. Per map name 10 autosaves are kept in the `auto` folder before old autosaves will be deleted (number configurable, see below). Add config variable `ed_autosave_interval` (0 - 240, default 10) to configure the interval in minutes at which a copy of the current editor map is automatically saved to the 'auto' folder. Add config variable `ed_autosave_max` (0 - 1000, default 10) to configure the maximum number of autosaves that are kept per map name (0 = no limit). Autosaving will not take place in the 5 seconds immediately after the map was last modified by the user, to avoid interrupting the user with the autosave. This will only delay autosaving for up to 1 minute though, so autosaves are not prevented entirely, should the user continuously edit the map. When the editor is reopened after being closed for more than 10 seconds, the autosave timer will be adjusted to compensate for the time that was not spent on editing in the editor. When the map is saved manually by the user the autosave file is also updated, if it's outdated by at least half of the configured autosave interval. This ensures that autosaves are always available as a periodic backup of the map. When a copy of the current map is saved, this does not update the autosave and will also no longer reset the modified state. The modified state should reflect whether changes have been made that are not saved to the current map file. As saving a copy does not update the current file, the modified state should not be reset in this case. Closes #6693. --- src/engine/shared/storage.cpp | 1 + src/game/editor/auto_map.cpp | 2 +- src/game/editor/editor.cpp | 134 ++++++++++++++++++++++++++----- src/game/editor/editor.h | 24 ++++-- src/game/editor/io.cpp | 3 + src/game/editor/layer_quads.cpp | 12 +-- src/game/editor/layer_sounds.cpp | 6 +- src/game/editor/layer_tiles.cpp | 2 +- src/game/editor/popups.cpp | 30 +++---- src/game/variables.h | 2 + 10 files changed, 160 insertions(+), 56 deletions(-) diff --git a/src/engine/shared/storage.cpp b/src/engine/shared/storage.cpp index 929191733..4b01a3d1d 100644 --- a/src/engine/shared/storage.cpp +++ b/src/engine/shared/storage.cpp @@ -68,6 +68,7 @@ public: CreateFolder("screenshots/auto", TYPE_SAVE); CreateFolder("screenshots/auto/stats", TYPE_SAVE); CreateFolder("maps", TYPE_SAVE); + CreateFolder("maps/auto", TYPE_SAVE); CreateFolder("mapres", TYPE_SAVE); CreateFolder("downloadedmaps", TYPE_SAVE); CreateFolder("skins", TYPE_SAVE); diff --git a/src/game/editor/auto_map.cpp b/src/game/editor/auto_map.cpp index 4b8b481d5..d339a2601 100644 --- a/src/game/editor/auto_map.cpp +++ b/src/game/editor/auto_map.cpp @@ -470,7 +470,7 @@ void CAutoMapper::Proceed(CLayerTiles *pLayer, int ConfigID, int Seed, int SeedO for(int x = 0; x < pLayer->m_Width; x++) { CTile *pTile = &(pLayer->m_pTiles[y * pLayer->m_Width + x]); - m_pEditor->m_Map.m_Modified = true; + m_pEditor->m_Map.OnModify(); for(size_t i = 0; i < pRun->m_vIndexRules.size(); ++i) { diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index d855b71fa..c3cab662e 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -207,7 +208,7 @@ void CLayerGroup::Render() void CLayerGroup::AddLayer(CLayer *pLayer) { - m_pMap->m_Modified = true; + m_pMap->OnModify(); m_vpLayers.push_back(pLayer); } @@ -217,7 +218,7 @@ void CLayerGroup::DeleteLayer(int Index) return; delete m_vpLayers[Index]; m_vpLayers.erase(m_vpLayers.begin() + Index); - m_pMap->m_Modified = true; + m_pMap->OnModify(); } void CLayerGroup::DuplicateLayer(int Index) @@ -228,7 +229,7 @@ void CLayerGroup::DuplicateLayer(int Index) auto *pDup = m_vpLayers[Index]->Duplicate(); m_vpLayers.insert(m_vpLayers.begin() + Index + 1, pDup); - m_pMap->m_Modified = true; + m_pMap->OnModify(); } void CLayerGroup::GetSize(float *pWidth, float *pHeight) const @@ -252,7 +253,7 @@ int CLayerGroup::SwapLayers(int Index0, int Index1) return Index0; if(Index0 == Index1) return Index0; - m_pMap->m_Modified = true; + m_pMap->OnModify(); std::swap(m_vpLayers[Index0], m_vpLayers[Index1]); return Index1; } @@ -828,7 +829,7 @@ bool CEditor::CallbackAppendMap(const char *pFileName, int StorageType, void *pU bool CEditor::CallbackSaveMap(const char *pFileName, int StorageType, void *pUser) { CEditor *pEditor = static_cast(pUser); - char aBuf[1024]; + char aBuf[IO_MAX_PATH_LENGTH]; // add map extension if(!str_endswith(pFileName, ".map")) { @@ -836,25 +837,35 @@ bool CEditor::CallbackSaveMap(const char *pFileName, int StorageType, void *pUse pFileName = aBuf; } + // Save map to specified file if(pEditor->Save(pFileName)) { str_copy(pEditor->m_aFileName, pFileName); pEditor->m_ValidSaveFilename = StorageType == IStorage::TYPE_SAVE && pEditor->m_pFileDialogPath == pEditor->m_aFileDialogCurrentFolder; pEditor->m_Map.m_Modified = false; - pEditor->m_Dialog = DIALOG_NONE; - return true; } else { pEditor->ShowFileDialogError("Failed to save map to file '%s'.", pFileName); return false; } + + // Also update autosave if it's older than half the configured autosave interval, so we also have periodic backups. + const float Time = pEditor->Client()->GlobalTime(); + if(g_Config.m_EdAutosaveInterval > 0 && pEditor->m_Map.m_LastSaveTime < Time && Time - pEditor->m_Map.m_LastSaveTime > 30 * g_Config.m_EdAutosaveInterval) + { + if(!pEditor->PerformAutosave()) + return false; + } + + pEditor->m_Dialog = DIALOG_NONE; + return true; } bool CEditor::CallbackSaveCopyMap(const char *pFileName, int StorageType, void *pUser) { CEditor *pEditor = static_cast(pUser); - char aBuf[1024]; + char aBuf[IO_MAX_PATH_LENGTH]; // add map extension if(!str_endswith(pFileName, ".map")) { @@ -864,7 +875,6 @@ bool CEditor::CallbackSaveCopyMap(const char *pFileName, int StorageType, void * if(pEditor->Save(pFileName)) { - pEditor->m_Map.m_Modified = false; pEditor->m_Dialog = DIALOG_NONE; return true; } @@ -1524,7 +1534,7 @@ void CEditor::DoQuad(CQuad *pQuad, int Index) if(m_vSelectedLayers.size() == 1) { UI()->DisableMouseLock(); - m_Map.m_Modified = true; + m_Map.OnModify(); DeleteSelectedQuads(); } s_Operation = OP_NONE; @@ -3817,7 +3827,7 @@ void CEditor::RenderLayers(CUIRect LayersBox) m_SelectedGroup = GroupAfterDraggedLayer - 1; m_vSelectedLayers.clear(); m_vSelectedQuads.clear(); - m_Map.m_Modified = true; + m_Map.OnModify(); } } @@ -3834,7 +3844,7 @@ void CEditor::RenderLayers(CUIRect LayersBox) m_Map.m_vpGroups.insert(InsertPosition, pSelectedGroup); m_SelectedGroup = InsertPosition - m_Map.m_vpGroups.begin(); - m_Map.m_Modified = true; + m_Map.OnModify(); } if(MoveLayers || MoveGroup) @@ -5344,7 +5354,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) static int s_NewSoundButton = 0; if(DoButton_Editor(&s_NewSoundButton, "Sound+", 0, &Button, 0, "Creates a new sound envelope")) { - m_Map.m_Modified = true; + m_Map.OnModify(); pNewEnv = m_Map.NewEnvelope(1); } @@ -5353,7 +5363,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) static int s_New4dButton = 0; if(DoButton_Editor(&s_New4dButton, "Color+", 0, &Button, 0, "Creates a new color envelope")) { - m_Map.m_Modified = true; + m_Map.OnModify(); pNewEnv = m_Map.NewEnvelope(4); } @@ -5362,7 +5372,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) static int s_New2dButton = 0; if(DoButton_Editor(&s_New2dButton, "Pos.+", 0, &Button, 0, "Creates a new position envelope")) { - m_Map.m_Modified = true; + m_Map.OnModify(); pNewEnv = m_Map.NewEnvelope(3); } @@ -5473,7 +5483,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) s_NameInput.SetBuffer(pEnvelope->m_aName, sizeof(pEnvelope->m_aName)); if(DoEditBox(&s_NameInput, &Button, 10.0f, IGraphics::CORNER_ALL, "The name of the selected envelope")) { - m_Map.m_Modified = true; + m_Map.OnModify(); } } } @@ -5578,7 +5588,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) pEnvelope->AddPoint(Time, f2fx(Channels.r), f2fx(Channels.g), f2fx(Channels.b), f2fx(Channels.a)); - m_Map.m_Modified = true; + m_Map.OnModify(); } m_ShowEnvelopePreview = SHOWENV_SELECTED; @@ -5757,7 +5767,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) m_SelectedQuadEnvelope = m_SelectedEnvelope; m_ShowEnvelopePreview = SHOWENV_SELECTED; m_SelectedEnvelopePoint = i; - m_Map.m_Modified = true; + m_Map.OnModify(); } ColorMod = 100.0f; @@ -5787,7 +5797,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View) } pEnvelope->m_vPoints.erase(pEnvelope->m_vPoints.begin() + i); - m_Map.m_Modified = true; + m_Map.OnModify(); } m_ShowEnvelopePreview = SHOWENV_SELECTED; @@ -6507,6 +6517,10 @@ void CEditor::Reset(bool CreateDefault) m_MouseDeltaWy = 0; m_Map.m_Modified = false; + m_Map.m_ModifiedAuto = false; + m_Map.m_LastModifiedTime = -1.0f; + m_Map.m_LastSaveTime = Client()->GlobalTime(); + m_Map.m_LastAutosaveUpdateTime = -1.0f; m_ShowEnvelopePreview = SHOWENV_NONE; m_ShiftBy = 1; @@ -6625,12 +6639,19 @@ void CEditor::Goto(float X, float Y) m_WorldOffsetY = Y * 32; } +void CEditorMap::OnModify() +{ + m_Modified = true; + m_ModifiedAuto = true; + m_LastModifiedTime = m_pEditor->Client()->GlobalTime(); +} + void CEditorMap::DeleteEnvelope(int Index) { if(Index < 0 || Index >= (int)m_vpEnvelopes.size()) return; - m_Modified = true; + OnModify(); VisitEnvelopeReferences([Index](int &ElementIndex) { if(ElementIndex == Index) @@ -6651,7 +6672,7 @@ void CEditorMap::SwapEnvelopes(int Index0, int Index1) if(Index0 == Index1) return; - m_Modified = true; + OnModify(); VisitEnvelopeReferences([Index0, Index1](int &ElementIndex) { if(ElementIndex == Index0) @@ -6731,6 +6752,7 @@ void CEditorMap::Clean() m_pGameGroup = nullptr; m_Modified = false; + m_ModifiedAuto = false; m_pTeleLayer = nullptr; m_pSpeedupLayer = nullptr; @@ -6856,7 +6878,6 @@ void CEditor::Init() m_Brush.m_pMap = &m_Map; Reset(false); - m_Map.m_Modified = false; ResetMenuBackgroundPositions(); m_vpMenuBackgroundPositionNames.resize(CMenuBackground::NUM_POS); @@ -6966,6 +6987,74 @@ void CEditor::HandleCursorMovement() } } +void CEditor::HandleAutosave() +{ + const float Time = Client()->GlobalTime(); + const float LastAutosaveUpdateTime = m_Map.m_LastAutosaveUpdateTime; + m_Map.m_LastAutosaveUpdateTime = Time; + + if(g_Config.m_EdAutosaveInterval == 0) + return; // autosave disabled + if(!m_Map.m_ModifiedAuto || m_Map.m_LastModifiedTime < 0.0f) + return; // no unsaved changes + + // Add time to autosave timer if the editor was disabled for more than 10 seconds, + // to prevent autosave from immediately activating when the editor is activated + // after being deactivated for some time. + if(LastAutosaveUpdateTime >= 0.0f && Time - LastAutosaveUpdateTime > 10.0f) + { + m_Map.m_LastSaveTime += Time - LastAutosaveUpdateTime; + } + + // Check if autosave timer has expired. + if(m_Map.m_LastSaveTime >= Time || Time - m_Map.m_LastSaveTime < 60 * g_Config.m_EdAutosaveInterval) + return; + + // Wait for 5 seconds of no modification before saving, to prevent autosave + // from immediately activating when a map is first modified or while user is + // modifying the map, but don't delay the autosave for more than 1 minute. + if(Time - m_Map.m_LastModifiedTime < 5.0f && Time - m_Map.m_LastSaveTime < 60 * (g_Config.m_EdAutosaveInterval + 1)) + return; + + PerformAutosave(); +} + +bool CEditor::PerformAutosave() +{ + char aDate[20]; + char aAutosavePath[IO_MAX_PATH_LENGTH]; + str_timestamp(aDate, sizeof(aDate)); + char aFileNameNoExt[IO_MAX_PATH_LENGTH]; + if(m_aFileName[0] == '\0') + { + str_copy(aFileNameNoExt, "unnamed"); + } + else + { + const char *pFileName = fs_filename(m_aFileName); + str_truncate(aFileNameNoExt, sizeof(aFileNameNoExt), pFileName, str_length(pFileName) - str_length(".map")); + } + str_format(aAutosavePath, sizeof(aAutosavePath), "maps/auto/%s_%s.map", aFileNameNoExt, aDate); + + m_Map.m_LastSaveTime = Client()->GlobalTime(); + if(Save(aAutosavePath)) + { + m_Map.m_ModifiedAuto = false; + // Clean up autosaves + if(g_Config.m_EdAutosaveMax) + { + CFileCollection AutosavedMaps; + AutosavedMaps.Init(Storage(), "maps/auto", aFileNameNoExt, ".map", g_Config.m_EdAutosaveMax); + } + return true; + } + else + { + ShowFileDialogError("Failed to automatically save map to file '%s'.", aAutosavePath); + return false; + } +} + void CEditor::OnUpdate() { CUIElementBase::Init(UI()); // update static pointer because game and editor use separate UI @@ -6977,6 +7066,7 @@ void CEditor::OnUpdate() } HandleCursorMovement(); + HandleAutosave(); } void CEditor::OnRender() diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index 18228ae9b..b8f25e61b 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -315,7 +315,6 @@ class CEditorMap public: CEditor *m_pEditor; - bool m_Modified; CEditorMap() { @@ -327,6 +326,13 @@ public: Clean(); } + bool m_Modified; // unsaved changes in manual save + bool m_ModifiedAuto; // unsaved changes in autosave + float m_LastModifiedTime; + float m_LastSaveTime; + float m_LastAutosaveUpdateTime; + void OnModify(); + std::vector m_vpGroups; std::vector m_vpImages; std::vector m_vpEnvelopes; @@ -370,7 +376,7 @@ public: CEnvelope *NewEnvelope(int Channels) { - m_Modified = true; + OnModify(); CEnvelope *pEnv = new CEnvelope(Channels); m_vpEnvelopes.push_back(pEnv); return pEnv; @@ -383,7 +389,7 @@ public: CLayerGroup *NewGroup() { - m_Modified = true; + OnModify(); CLayerGroup *pGroup = new CLayerGroup; pGroup->m_pMap = this; m_vpGroups.push_back(pGroup); @@ -398,7 +404,7 @@ public: return Index0; if(Index0 == Index1) return Index0; - m_Modified = true; + OnModify(); std::swap(m_vpGroups[Index0], m_vpGroups[Index1]); return Index1; } @@ -407,28 +413,28 @@ public: { if(Index < 0 || Index >= (int)m_vpGroups.size()) return; - m_Modified = true; + OnModify(); delete m_vpGroups[Index]; m_vpGroups.erase(m_vpGroups.begin() + Index); } void ModifyImageIndex(INDEX_MODIFY_FUNC pfnFunc) { - m_Modified = true; + OnModify(); for(auto &pGroup : m_vpGroups) pGroup->ModifyImageIndex(pfnFunc); } void ModifyEnvelopeIndex(INDEX_MODIFY_FUNC pfnFunc) { - m_Modified = true; + OnModify(); for(auto &pGroup : m_vpGroups) pGroup->ModifyEnvelopeIndex(pfnFunc); } void ModifySoundIndex(INDEX_MODIFY_FUNC pfnFunc) { - m_Modified = true; + OnModify(); for(auto &pGroup : m_vpGroups) pGroup->ModifySoundIndex(pfnFunc); } @@ -850,6 +856,8 @@ public: void ResetMentions() override { m_Mentions = 0; } void HandleCursorMovement(); + void HandleAutosave(); + bool PerformAutosave(); CLayerGroup *m_apSavedBrushes[10]; diff --git a/src/game/editor/io.cpp b/src/game/editor/io.cpp index 54d4bff60..b0c58407b 100644 --- a/src/game/editor/io.cpp +++ b/src/game/editor/io.cpp @@ -976,6 +976,9 @@ bool CEditorMap::Load(const char *pFileName, int StorageType) return false; m_Modified = false; + m_ModifiedAuto = false; + m_LastModifiedTime = -1.0f; + m_LastSaveTime = m_pEditor->Client()->GlobalTime(); return true; } diff --git a/src/game/editor/layer_quads.cpp b/src/game/editor/layer_quads.cpp index 585ca776a..27dc484f4 100644 --- a/src/game/editor/layer_quads.cpp +++ b/src/game/editor/layer_quads.cpp @@ -37,7 +37,7 @@ void CLayerQuads::Render(bool QuadPicker) CQuad *CLayerQuads::NewQuad(int x, int y, int Width, int Height) { - m_pEditor->m_Map.m_Modified = true; + m_pEditor->m_Map.OnModify(); m_vQuads.emplace_back(); CQuad *pQuad = &m_vQuads[m_vQuads.size() - 1]; @@ -153,7 +153,7 @@ void CLayerQuads::BrushPlace(CLayer *pBrush, float wx, float wy) m_vQuads.push_back(n); } - m_pEditor->m_Map.m_Modified = true; + m_pEditor->m_Map.OnModify(); } void CLayerQuads::BrushFlipX() @@ -163,7 +163,7 @@ void CLayerQuads::BrushFlipX() std::swap(Quad.m_aPoints[0], Quad.m_aPoints[1]); std::swap(Quad.m_aPoints[2], Quad.m_aPoints[3]); } - m_pEditor->m_Map.m_Modified = true; + m_pEditor->m_Map.OnModify(); } void CLayerQuads::BrushFlipY() @@ -173,7 +173,7 @@ void CLayerQuads::BrushFlipY() std::swap(Quad.m_aPoints[0], Quad.m_aPoints[2]); std::swap(Quad.m_aPoints[1], Quad.m_aPoints[3]); } - m_pEditor->m_Map.m_Modified = true; + m_pEditor->m_Map.OnModify(); } void Rotate(vec2 *pCenter, vec2 *pPoint, float Rotation) @@ -236,7 +236,7 @@ CUI::EPopupMenuFunctionResult CLayerQuads::RenderProperties(CUIRect *pToolBox) int Prop = m_pEditor->DoProperties(pToolBox, aProps, s_aIds, &NewVal); if(Prop != -1) { - m_pEditor->m_Map.m_Modified = true; + m_pEditor->m_Map.OnModify(); } if(Prop == PROP_IMAGE) @@ -277,7 +277,7 @@ int CLayerQuads::SwapQuads(int Index0, int Index1) return Index0; if(Index0 == Index1) return Index0; - m_pEditor->m_Map.m_Modified = true; + m_pEditor->m_Map.OnModify(); std::swap(m_vQuads[Index0], m_vQuads[Index1]); return Index1; } diff --git a/src/game/editor/layer_sounds.cpp b/src/game/editor/layer_sounds.cpp index 1c236aa54..1f20f1def 100644 --- a/src/game/editor/layer_sounds.cpp +++ b/src/game/editor/layer_sounds.cpp @@ -101,7 +101,7 @@ void CLayerSounds::Render(bool Tileset) CSoundSource *CLayerSounds::NewSource(int x, int y) { - m_pEditor->m_Map.m_Modified = true; + m_pEditor->m_Map.OnModify(); m_vSources.emplace_back(); CSoundSource *pSource = &m_vSources[m_vSources.size() - 1]; @@ -179,7 +179,7 @@ void CLayerSounds::BrushPlace(CLayer *pBrush, float wx, float wy) m_vSources.push_back(n); } - m_pEditor->m_Map.m_Modified = true; + m_pEditor->m_Map.OnModify(); } CUI::EPopupMenuFunctionResult CLayerSounds::RenderProperties(CUIRect *pToolBox) @@ -200,7 +200,7 @@ CUI::EPopupMenuFunctionResult CLayerSounds::RenderProperties(CUIRect *pToolBox) int Prop = m_pEditor->DoProperties(pToolBox, aProps, s_aIds, &NewVal); if(Prop != -1) { - m_pEditor->m_Map.m_Modified = true; + m_pEditor->m_Map.OnModify(); } if(Prop == PROP_SOUND) diff --git a/src/game/editor/layer_tiles.cpp b/src/game/editor/layer_tiles.cpp index cbbbeed95..d093827ff 100644 --- a/src/game/editor/layer_tiles.cpp +++ b/src/game/editor/layer_tiles.cpp @@ -1079,7 +1079,7 @@ CUI::EPopupMenuFunctionResult CLayerTiles::RenderCommonProperties(SCommonPropSta void CLayerTiles::FlagModified(int x, int y, int w, int h) { - m_pEditor->m_Map.m_Modified = true; + m_pEditor->m_Map.OnModify(); if(m_Seed != 0 && m_AutoMapperConfig != -1 && m_AutoAutoMap && m_Image >= 0) { m_pEditor->m_Map.m_vpImages[m_Image]->m_AutoMapper.ProceedLocalized(this, m_AutoMapperConfig, m_Seed, x, y, w, h); diff --git a/src/game/editor/popups.cpp b/src/game/editor/popups.cpp index 2e7e68bcf..a9bdd0443 100644 --- a/src/game/editor/popups.cpp +++ b/src/game/editor/popups.cpp @@ -366,7 +366,7 @@ CUI::EPopupMenuFunctionResult CEditor::PopupGroup(void *pContext, CUIRect View, if(!Found) { pGameLayer->m_pTiles[y * pGameLayer->m_Width + x].m_Index = TILE_AIR; - pEditor->m_Map.m_Modified = true; + pEditor->m_Map.OnModify(); } } } @@ -512,7 +512,7 @@ CUI::EPopupMenuFunctionResult CEditor::PopupGroup(void *pContext, CUIRect View, static CLineInput s_NameInput; s_NameInput.SetBuffer(pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_aName, sizeof(pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_aName)); if(pEditor->DoEditBox(&s_NameInput, &Button, 10.0f)) - pEditor->m_Map.m_Modified = true; + pEditor->m_Map.OnModify(); } enum @@ -557,7 +557,7 @@ CUI::EPopupMenuFunctionResult CEditor::PopupGroup(void *pContext, CUIRect View, int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal); if(Prop != -1) { - pEditor->m_Map.m_Modified = true; + pEditor->m_Map.OnModify(); } if(Prop == PROP_ORDER) @@ -683,7 +683,7 @@ CUI::EPopupMenuFunctionResult CEditor::PopupLayer(void *pContext, CUIRect View, static CLineInput s_NameInput; s_NameInput.SetBuffer(pCurrentLayer->m_aName, sizeof(pCurrentLayer->m_aName)); if(pEditor->DoEditBox(&s_NameInput, &EditBox, 10.0f)) - pEditor->m_Map.m_Modified = true; + pEditor->m_Map.OnModify(); } // spacing if any button was rendered @@ -717,7 +717,7 @@ CUI::EPopupMenuFunctionResult CEditor::PopupLayer(void *pContext, CUIRect View, int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal); if(Prop != -1) { - pEditor->m_Map.m_Modified = true; + pEditor->m_Map.OnModify(); } if(Prop == PROP_ORDER) @@ -762,7 +762,7 @@ CUI::EPopupMenuFunctionResult CEditor::PopupQuad(void *pContext, CUIRect View, b { if(pLayer) { - pEditor->m_Map.m_Modified = true; + pEditor->m_Map.OnModify(); pEditor->DeleteSelectedQuads(); } return CUI::POPUP_CLOSE_CURRENT; @@ -802,7 +802,7 @@ CUI::EPopupMenuFunctionResult CEditor::PopupQuad(void *pContext, CUIRect View, b pQuad->m_aPoints[2].y = Top + Height; pQuad->m_aPoints[3].x = Right; pQuad->m_aPoints[3].y = Top + Height; - pEditor->m_Map.m_Modified = true; + pEditor->m_Map.OnModify(); } return CUI::POPUP_CLOSE_CURRENT; } @@ -821,7 +821,7 @@ CUI::EPopupMenuFunctionResult CEditor::PopupQuad(void *pContext, CUIRect View, b pQuad->m_aPoints[k].x = 1000.0f * (pQuad->m_aPoints[k].x / 1000); pQuad->m_aPoints[k].y = 1000.0f * (pQuad->m_aPoints[k].y / 1000); } - pEditor->m_Map.m_Modified = true; + pEditor->m_Map.OnModify(); } return CUI::POPUP_CLOSE_CURRENT; } @@ -859,7 +859,7 @@ CUI::EPopupMenuFunctionResult CEditor::PopupQuad(void *pContext, CUIRect View, b pQuad->m_aPoints[2].y = Bottom; pQuad->m_aPoints[3].x = Right; pQuad->m_aPoints[3].y = Bottom; - pEditor->m_Map.m_Modified = true; + pEditor->m_Map.OnModify(); } return CUI::POPUP_CLOSE_CURRENT; } @@ -904,7 +904,7 @@ CUI::EPopupMenuFunctionResult CEditor::PopupQuad(void *pContext, CUIRect View, b int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal); if(Prop != -1) { - pEditor->m_Map.m_Modified = true; + pEditor->m_Map.OnModify(); } const float OffsetX = i2fx(NewVal) - pCurrentQuad->m_aPoints[4].x; @@ -988,7 +988,7 @@ CUI::EPopupMenuFunctionResult CEditor::PopupSource(void *pContext, CUIRect View, CLayerSounds *pLayer = (CLayerSounds *)pEditor->GetSelectedLayerType(0, LAYERTYPE_SOUNDS); if(pLayer) { - pEditor->m_Map.m_Modified = true; + pEditor->m_Map.OnModify(); pLayer->m_vSources.erase(pLayer->m_vSources.begin() + pEditor->m_SelectedSource); pEditor->m_SelectedSource--; } @@ -1062,7 +1062,7 @@ CUI::EPopupMenuFunctionResult CEditor::PopupSource(void *pContext, CUIRect View, int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal); if(Prop != -1) { - pEditor->m_Map.m_Modified = true; + pEditor->m_Map.OnModify(); } if(Prop == PROP_POS_X) @@ -1145,7 +1145,7 @@ CUI::EPopupMenuFunctionResult CEditor::PopupSource(void *pContext, CUIRect View, Prop = pEditor->DoProperties(&View, aCircleProps, s_aCircleIds, &NewVal); if(Prop != -1) { - pEditor->m_Map.m_Modified = true; + pEditor->m_Map.OnModify(); } if(Prop == PROP_CIRCLE_RADIUS) @@ -1176,7 +1176,7 @@ CUI::EPopupMenuFunctionResult CEditor::PopupSource(void *pContext, CUIRect View, Prop = pEditor->DoProperties(&View, aRectangleProps, s_aRectangleIds, &NewVal); if(Prop != -1) { - pEditor->m_Map.m_Modified = true; + pEditor->m_Map.OnModify(); } if(Prop == PROP_RECTANGLE_WIDTH) @@ -1236,7 +1236,7 @@ CUI::EPopupMenuFunctionResult CEditor::PopupPoint(void *pContext, CUIRect View, int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal); if(Prop != -1) { - pEditor->m_Map.m_Modified = true; + pEditor->m_Map.OnModify(); } for(auto &pQuad : vpQuads) diff --git a/src/game/variables.h b/src/game/variables.h index 42a17857e..f67d3ff6d 100644 --- a/src/game/variables.h +++ b/src/game/variables.h @@ -90,6 +90,8 @@ MACRO_CONFIG_INT(ClDyncamFollowFactor, cl_dyncam_follow_factor, 60, 0, 200, CFGF MACRO_CONFIG_INT(ClDyncamSmoothness, cl_dyncam_smoothness, 0, 0, 100, CFGFLAG_CLIENT | CFGFLAG_SAVE | CFGFLAG_INSENSITIVE, "Transition amount of the camera movement, 0=instant, 100=slow and smooth") MACRO_CONFIG_INT(ClDyncamStabilizing, cl_dyncam_stabilizing, 0, 0, 100, CFGFLAG_CLIENT | CFGFLAG_SAVE | CFGFLAG_INSENSITIVE, "Amount of camera slowdown during fast cursor movement. High value can cause delay in camera movement") +MACRO_CONFIG_INT(EdAutosaveInterval, ed_autosave_interval, 10, 0, 240, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Interval in minutes at which a copy of the current editor map is automatically saved to the 'auto' folder (0 for off)") +MACRO_CONFIG_INT(EdAutosaveMax, ed_autosave_max, 10, 0, 1000, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Maximum number of autosaves that are kept per map name (0 = no limit)") MACRO_CONFIG_INT(EdSmoothZoomTime, ed_smooth_zoom_time, 250, 0, 5000, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Time of smooth zoom animation in the editor in ms (0 for off)") MACRO_CONFIG_INT(EdLimitMaxZoomLevel, ed_limit_max_zoom_level, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Specifies, if zooming in the editor should be limited or not (0 = no limit)") MACRO_CONFIG_INT(EdZoomTarget, ed_zoom_target, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Zoom to the current mouse target")