mirror of
https://github.com/The-Powder-Toy/The-Powder-Toy.git
synced 2025-08-22 16:02:54 +02:00
Move custom GOL management to GameModel
Also use RGB<uint8_t> for storing custom colors.
This commit is contained in:
@@ -511,34 +511,6 @@ void Client::SaveAuthorInfo(Json::Value *saveInto)
|
||||
}
|
||||
}
|
||||
|
||||
bool AddCustomGol(String ruleString, String nameString, unsigned int highColor, unsigned int lowColor)
|
||||
{
|
||||
auto &prefs = GlobalPrefs::Ref();
|
||||
auto customGOLTypes = prefs.Get("CustomGOL.Types", std::vector<ByteString>{});
|
||||
std::vector<ByteString> newCustomGOLTypes;
|
||||
bool nameTaken = false;
|
||||
for (auto gol : customGOLTypes)
|
||||
{
|
||||
auto parts = gol.FromUtf8().PartitionBy(' ');
|
||||
if (parts.size())
|
||||
{
|
||||
if (parts[0] == nameString)
|
||||
{
|
||||
nameTaken = true;
|
||||
}
|
||||
}
|
||||
newCustomGOLTypes.push_back(gol);
|
||||
}
|
||||
if (nameTaken)
|
||||
return false;
|
||||
|
||||
StringBuilder sb;
|
||||
sb << nameString << " " << ruleString << " " << highColor << " " << lowColor;
|
||||
newCustomGOLTypes.push_back(sb.Build().ToUtf8());
|
||||
prefs.Set("CustomGOL.Types", newCustomGOLTypes);
|
||||
return true;
|
||||
}
|
||||
|
||||
String Client::DoMigration(ByteString fromDir, ByteString toDir)
|
||||
{
|
||||
if (fromDir.at(fromDir.length() - 1) != '/')
|
||||
|
@@ -105,5 +105,3 @@ public:
|
||||
|
||||
String DoMigration(ByteString fromDir, ByteString toDir);
|
||||
};
|
||||
|
||||
bool AddCustomGol(String ruleString, String nameString, unsigned int highColor, unsigned int lowColor);
|
||||
|
@@ -24,6 +24,8 @@ struct alignas(alignof(uint32_t) > alignof(T) ? alignof(uint32_t) : alignof(T))
|
||||
{
|
||||
T Blue, Green, Red;
|
||||
|
||||
constexpr RGB() = default;
|
||||
|
||||
constexpr RGB(T r, T g, T b):
|
||||
Blue(b),
|
||||
Green(g),
|
||||
@@ -121,6 +123,8 @@ struct alignas(alignof(uint32_t) > alignof(T) ? alignof(uint32_t) : alignof(T))
|
||||
{
|
||||
T Blue, Green, Red, Alpha;
|
||||
|
||||
constexpr RGBA() = default;
|
||||
|
||||
constexpr RGBA(T r, T g, T b, T a):
|
||||
Blue(b),
|
||||
Green(g),
|
||||
|
@@ -1719,9 +1719,9 @@ bool GameController::GetMouseClickRequired()
|
||||
return gameModel->GetMouseClickRequired();
|
||||
}
|
||||
|
||||
void GameController::RemoveCustomGOLType(const ByteString &identifier)
|
||||
void GameController::RemoveCustomGol(const ByteString &identifier)
|
||||
{
|
||||
gameModel->RemoveCustomGOLType(identifier);
|
||||
gameModel->RemoveCustomGol(identifier);
|
||||
}
|
||||
|
||||
void GameController::BeforeSimDraw()
|
||||
|
@@ -201,7 +201,7 @@ public:
|
||||
void RunUpdater(UpdateInfo info);
|
||||
bool GetMouseClickRequired();
|
||||
|
||||
void RemoveCustomGOLType(const ByteString &identifier);
|
||||
void RemoveCustomGol(const ByteString &identifier);
|
||||
|
||||
void BeforeSimDraw();
|
||||
void AfterSimDraw();
|
||||
|
@@ -137,6 +137,7 @@ GameModel::GameModel():
|
||||
currentUser = Client::Ref().GetAuthUser();
|
||||
}
|
||||
|
||||
LoadCustomGol();
|
||||
BuildMenus();
|
||||
|
||||
perfectCircle = prefs.Get("PerfectCircleBrush", true);
|
||||
@@ -312,60 +313,10 @@ void GameModel::BuildMenus()
|
||||
Tool * tempTool = AddTool<ElementTool>(PT_LIFE|PMAPID(i), builtinGol[i].name, builtinGol[i].description, builtinGol[i].colour, "DEFAULT_PT_LIFE_"+builtinGol[i].name.ToAscii());
|
||||
menuList[SC_LIFE]->AddTool(tempTool);
|
||||
}
|
||||
for (auto &gd : sd.GetCustomGol())
|
||||
{
|
||||
auto &prefs = GlobalPrefs::Ref();
|
||||
auto customGOLTypes = prefs.Get("CustomGOL.Types", std::vector<ByteString>{});
|
||||
std::vector<ByteString> validatedCustomLifeTypes;
|
||||
std::vector<CustomGOLData> newCustomGol;
|
||||
bool removedAny = false;
|
||||
for (auto gol : customGOLTypes)
|
||||
{
|
||||
auto parts = gol.FromUtf8().PartitionBy(' ');
|
||||
if (parts.size() != 4)
|
||||
{
|
||||
removedAny = true;
|
||||
continue;
|
||||
}
|
||||
CustomGOLData gd;
|
||||
gd.nameString = parts[0];
|
||||
gd.ruleString = parts[1];
|
||||
auto &colour1String = parts[2];
|
||||
auto &colour2String = parts[3];
|
||||
if (!ValidateGOLName(gd.nameString))
|
||||
{
|
||||
removedAny = true;
|
||||
continue;
|
||||
}
|
||||
gd.rule = ParseGOLString(gd.ruleString);
|
||||
if (gd.rule == -1)
|
||||
{
|
||||
removedAny = true;
|
||||
continue;
|
||||
}
|
||||
try
|
||||
{
|
||||
gd.colour1 = colour1String.ToNumber<int>();
|
||||
gd.colour2 = colour2String.ToNumber<int>();
|
||||
}
|
||||
catch (std::exception &)
|
||||
{
|
||||
removedAny = true;
|
||||
continue;
|
||||
}
|
||||
newCustomGol.push_back(gd);
|
||||
validatedCustomLifeTypes.push_back(gol);
|
||||
}
|
||||
if (removedAny)
|
||||
{
|
||||
// All custom rules that fail validation will be removed
|
||||
prefs.Set("CustomGOL.Types", validatedCustomLifeTypes);
|
||||
}
|
||||
for (auto &gd : newCustomGol)
|
||||
{
|
||||
Tool * tempTool = AddTool<ElementTool>(PT_LIFE|PMAPID(gd.rule), gd.nameString, "Custom GOL type: " + gd.ruleString, RGB<uint8_t>::Unpack(gd.colour1), "DEFAULT_PT_LIFECUST_"+gd.nameString.ToAscii(), nullptr);
|
||||
menuList[SC_LIFE]->AddTool(tempTool);
|
||||
}
|
||||
sd.SetCustomGOL(newCustomGol);
|
||||
Tool * tempTool = AddTool<ElementTool>(PT_LIFE|PMAPID(gd.rule), gd.nameString, "Custom GOL type: " + SerialiseGOLRule(gd.rule), gd.colour1, "DEFAULT_PT_LIFECUST_"+gd.nameString.ToAscii(), nullptr);
|
||||
menuList[SC_LIFE]->AddTool(tempTool);
|
||||
}
|
||||
|
||||
//Build other menus from wall data
|
||||
@@ -1664,28 +1615,129 @@ void GameModel::SetPerfectCircle(bool perfectCircle)
|
||||
}
|
||||
}
|
||||
|
||||
bool GameModel::RemoveCustomGOLType(const ByteString &identifier)
|
||||
bool GameModel::AddCustomGol(String ruleString, String nameString, RGB<uint8_t> color1, RGB<uint8_t> color2)
|
||||
{
|
||||
if (auto gd = CheckCustomGol(ruleString, nameString, color1, color2))
|
||||
{
|
||||
auto &sd = SimulationData::Ref();
|
||||
auto newCustomGol = sd.GetCustomGol();
|
||||
newCustomGol.push_back(*gd);
|
||||
sd.SetCustomGOL(newCustomGol);
|
||||
SaveCustomGol();
|
||||
BuildMenus();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GameModel::RemoveCustomGol(const ByteString &identifier)
|
||||
{
|
||||
bool removedAny = false;
|
||||
auto &prefs = GlobalPrefs::Ref();
|
||||
auto customGOLTypes = prefs.Get("CustomGOL.Types", std::vector<ByteString>{});
|
||||
std::vector<ByteString> newCustomGOLTypes;
|
||||
for (auto gol : customGOLTypes)
|
||||
std::vector<CustomGOLData> newCustomGol;
|
||||
auto &sd = SimulationData::Ref();
|
||||
for (auto gol : sd.GetCustomGol())
|
||||
{
|
||||
auto parts = gol.PartitionBy(' ');
|
||||
if (parts.size() && "DEFAULT_PT_LIFECUST_" + parts[0] == identifier)
|
||||
if ("DEFAULT_PT_LIFECUST_" + gol.nameString == identifier.FromUtf8())
|
||||
{
|
||||
removedAny = true;
|
||||
}
|
||||
else
|
||||
newCustomGOLTypes.push_back(gol);
|
||||
{
|
||||
newCustomGol.push_back(gol);
|
||||
}
|
||||
}
|
||||
if (removedAny)
|
||||
{
|
||||
prefs.Set("CustomGOL.Types", newCustomGOLTypes);
|
||||
sd.SetCustomGOL(newCustomGol);
|
||||
BuildMenus();
|
||||
SaveCustomGol();
|
||||
}
|
||||
BuildMenus();
|
||||
return removedAny;
|
||||
}
|
||||
|
||||
void GameModel::LoadCustomGol()
|
||||
{
|
||||
auto &prefs = GlobalPrefs::Ref();
|
||||
auto customGOLTypes = prefs.Get("CustomGOL.Types", std::vector<ByteString>{});
|
||||
bool removedAny = false;
|
||||
std::vector<CustomGOLData> newCustomGol;
|
||||
for (auto gol : customGOLTypes)
|
||||
{
|
||||
auto parts = gol.FromUtf8().PartitionBy(' ');
|
||||
if (parts.size() != 4)
|
||||
{
|
||||
removedAny = true;
|
||||
continue;
|
||||
}
|
||||
auto nameString = parts[0];
|
||||
auto ruleString = parts[1];
|
||||
auto &colour1String = parts[2];
|
||||
auto &colour2String = parts[3];
|
||||
RGB<uint8_t> color1;
|
||||
RGB<uint8_t> color2;
|
||||
try
|
||||
{
|
||||
color1 = RGB<uint8_t>::Unpack(colour1String.ToNumber<int>());
|
||||
color2 = RGB<uint8_t>::Unpack(colour2String.ToNumber<int>());
|
||||
}
|
||||
catch (std::exception &)
|
||||
{
|
||||
removedAny = true;
|
||||
continue;
|
||||
}
|
||||
if (auto gd = CheckCustomGol(ruleString, nameString, color1, color2))
|
||||
{
|
||||
newCustomGol.push_back(*gd);
|
||||
}
|
||||
else
|
||||
{
|
||||
removedAny = true;
|
||||
}
|
||||
}
|
||||
auto &sd = SimulationData::Ref();
|
||||
sd.SetCustomGOL(newCustomGol);
|
||||
if (removedAny)
|
||||
{
|
||||
SaveCustomGol();
|
||||
}
|
||||
}
|
||||
|
||||
void GameModel::SaveCustomGol()
|
||||
{
|
||||
auto &prefs = GlobalPrefs::Ref();
|
||||
std::vector<ByteString> newCustomGOLTypes;
|
||||
auto &sd = SimulationData::Ref();
|
||||
for (auto &gd : sd.GetCustomGol())
|
||||
{
|
||||
StringBuilder sb;
|
||||
sb << gd.nameString << " " << SerialiseGOLRule(gd.rule) << " " << gd.colour1.Pack() << " " << gd.colour2.Pack();
|
||||
newCustomGOLTypes.push_back(sb.Build().ToUtf8());
|
||||
}
|
||||
prefs.Set("CustomGOL.Types", newCustomGOLTypes);
|
||||
}
|
||||
|
||||
std::optional<CustomGOLData> GameModel::CheckCustomGol(String ruleString, String nameString, RGB<uint8_t> color1, RGB<uint8_t> color2)
|
||||
{
|
||||
if (!ValidateGOLName(nameString))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
auto rule = ParseGOLString(ruleString);
|
||||
if (rule == -1)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
auto &sd = SimulationData::Ref();
|
||||
for (auto &gd : sd.GetCustomGol())
|
||||
{
|
||||
if (gd.nameString == nameString)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
return CustomGOLData{ rule, color1, color2, nameString };
|
||||
}
|
||||
|
||||
void GameModel::UpdateUpTo(int upTo)
|
||||
{
|
||||
if (upTo < sim->debug_nextToUpdate)
|
||||
|
@@ -3,6 +3,7 @@
|
||||
#include "client/User.h"
|
||||
#include "gui/interface/Point.h"
|
||||
#include "graphics/RendererSettings.h"
|
||||
#include "simulation/CustomGOLData.h"
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
@@ -295,7 +296,11 @@ public:
|
||||
void AddNotification(Notification * notification);
|
||||
void RemoveNotification(Notification * notification);
|
||||
|
||||
bool RemoveCustomGOLType(const ByteString &identifier);
|
||||
bool AddCustomGol(String ruleString, String nameString, RGB<uint8_t> color1, RGB<uint8_t> color2);
|
||||
bool RemoveCustomGol(const ByteString &identifier);
|
||||
void LoadCustomGol();
|
||||
void SaveCustomGol();
|
||||
std::optional<CustomGOLData> CheckCustomGol(String ruleString, String nameString, RGB<uint8_t> color1, RGB<uint8_t> color2);
|
||||
|
||||
ByteString SelectNextIdentifier;
|
||||
int SelectNextTool;
|
||||
|
@@ -602,7 +602,7 @@ void GameView::NotifyToolListChanged(GameModel * sender)
|
||||
else if (identifier.BeginsWith("DEFAULT_PT_LIFECUST_"))
|
||||
{
|
||||
new ConfirmPrompt("Remove custom GOL type", "Are you sure you want to remove " + identifier.Substr(20).FromUtf8() + "?", { [this, identifier]() {
|
||||
c->RemoveCustomGOLType(identifier);
|
||||
c->RemoveCustomGol(identifier);
|
||||
} });
|
||||
}
|
||||
}
|
||||
|
@@ -159,9 +159,7 @@ void GOLWindow::validate()
|
||||
prefs.Set("CustomGOL.Rule", ruleString);
|
||||
}
|
||||
|
||||
auto color1 = (((highColour.Red << 8) | highColour.Green) << 8) | highColour.Blue;
|
||||
auto color2 = (((lowColour.Red << 8) | lowColour.Green) << 8) | lowColour.Blue;
|
||||
if (!AddCustomGol(ruleString, nameString, color1, color2))
|
||||
if (!gameModel.AddCustomGol(ruleString, nameString, highColour.NoAlpha(), lowColour.NoAlpha()))
|
||||
{
|
||||
new ErrorMessage("Could not add GOL type", "Name already taken");
|
||||
return;
|
||||
|
@@ -1489,13 +1489,13 @@ static int listCustomGol(lua_State *L)
|
||||
lua_newtable(L);
|
||||
tpt_lua_pushString(L, cgol.nameString);
|
||||
lua_setfield(L, -2, "name");
|
||||
tpt_lua_pushString(L, cgol.ruleString);
|
||||
tpt_lua_pushString(L, SerialiseGOLRule(cgol.rule));
|
||||
lua_setfield(L, -2, "rulestr");
|
||||
lua_pushnumber(L, cgol.rule);
|
||||
lua_setfield(L, -2, "rule");
|
||||
lua_pushnumber(L, cgol.colour1);
|
||||
lua_pushnumber(L, cgol.colour1.Pack());
|
||||
lua_setfield(L, -2, "color1");
|
||||
lua_pushnumber(L, cgol.colour2);
|
||||
lua_pushnumber(L, cgol.colour2.Pack());
|
||||
lua_setfield(L, -2, "color2");
|
||||
lua_rawseti(L, -2, ++i);
|
||||
}
|
||||
@@ -1529,10 +1529,9 @@ static int addCustomGol(lua_State *L)
|
||||
if (sd.GetCustomGOLByRule(rule))
|
||||
return luaL_error(L, "This Custom GoL rule already exists");
|
||||
|
||||
if (!AddCustomGol(ruleString, nameString, color1, color2))
|
||||
return luaL_error(L, "Duplicate name, cannot add");
|
||||
auto *lsi = GetLSI();
|
||||
lsi->gameModel->BuildMenus();
|
||||
if (!lsi->gameModel->AddCustomGol(ruleString, nameString, RGB<uint8_t>::Unpack(color1), RGB<uint8_t>::Unpack(color2)))
|
||||
return luaL_error(L, "Duplicate name, cannot add");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1540,9 +1539,7 @@ static int removeCustomGol(lua_State *L)
|
||||
{
|
||||
auto *lsi = GetLSI();
|
||||
ByteString nameString = tpt_lua_checkByteString(L, 1);
|
||||
bool removedAny = lsi->gameModel->RemoveCustomGOLType("DEFAULT_PT_LIFECUST_" + nameString);
|
||||
if (removedAny)
|
||||
lsi->gameModel->BuildMenus();
|
||||
bool removedAny = lsi->gameModel->RemoveCustomGol("DEFAULT_PT_LIFECUST_" + nameString);
|
||||
lua_pushboolean(L, removedAny);
|
||||
return 1;
|
||||
}
|
||||
|
15
src/simulation/CustomGOLData.h
Normal file
15
src/simulation/CustomGOLData.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include "graphics/Pixel.h"
|
||||
#include "common/String.h"
|
||||
|
||||
struct CustomGOLData
|
||||
{
|
||||
int rule;
|
||||
RGB<uint8_t> colour1, colour2;
|
||||
String nameString;
|
||||
|
||||
inline bool operator <(const CustomGOLData &other) const
|
||||
{
|
||||
return rule < other.rule;
|
||||
}
|
||||
};
|
@@ -9,6 +9,7 @@
|
||||
#include "Particle.h"
|
||||
#include "WallType.h"
|
||||
#include "graphics/gcache_item.h"
|
||||
#include "CustomGOLData.h"
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
@@ -140,17 +141,6 @@ constexpr int NGT_BRAN = 23;
|
||||
constexpr auto REPLACE_MODE = UINT32_C(0x00000001);
|
||||
constexpr auto SPECIFIC_DELETE = UINT32_C(0x00000002);
|
||||
|
||||
struct CustomGOLData
|
||||
{
|
||||
int rule, colour1, colour2;
|
||||
String nameString, ruleString;
|
||||
|
||||
inline bool operator <(const CustomGOLData &other) const
|
||||
{
|
||||
return rule < other.rule;
|
||||
}
|
||||
};
|
||||
|
||||
class SimulationData : public ExplicitSingleton<SimulationData>
|
||||
{
|
||||
public:
|
||||
|
@@ -111,8 +111,8 @@ static void create(ELEMENT_CREATE_FUNC_ARGS)
|
||||
auto *cgol = sd.GetCustomGOLByRule(v);
|
||||
if (cgol)
|
||||
{
|
||||
sim->parts[i].dcolour = cgol->colour1;
|
||||
sim->parts[i].tmp = cgol->colour2;
|
||||
sim->parts[i].dcolour = cgol->colour1.Pack();
|
||||
sim->parts[i].tmp = cgol->colour2.Pack();
|
||||
}
|
||||
}
|
||||
sim->parts[i].tmp2 = ((v >> 17) & 0xF) + 1;
|
||||
|
Reference in New Issue
Block a user