parent
02b26a9da3
commit
ba72dc7a22
@ -1298,6 +1298,16 @@ void GameSave::readOPS(char * data, int dataLength)
|
|||||||
particles[newIndex].tmp = 0;
|
particles[newIndex].tmp = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case PT_LIFE:
|
||||||
|
if (savedVersion < 95 || minorVersion < 1)
|
||||||
|
{
|
||||||
|
if (particles[newIndex].ctype >= 0 && particles[newIndex].ctype < NGOL)
|
||||||
|
{
|
||||||
|
particles[newIndex].tmp2 = particles[newIndex].tmp;
|
||||||
|
particles[newIndex].dcolour = builtinGol[particles[newIndex].ctype].colour;
|
||||||
|
particles[newIndex].tmp = builtinGol[particles[newIndex].ctype].colour2;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//note: PSv was used in version 77.0 and every version before, add something in PSv too if the element is that old
|
//note: PSv was used in version 77.0 and every version before, add something in PSv too if the element is that old
|
||||||
newIndex++;
|
newIndex++;
|
||||||
@ -1359,10 +1369,6 @@ void GameSave::readPSv(char * saveDataChar, int dataLength)
|
|||||||
char tempSignText[255];
|
char tempSignText[255];
|
||||||
sign tempSign("", 0, 0, sign::Left);
|
sign tempSign("", 0, 0, sign::Left);
|
||||||
|
|
||||||
//Gol data used to read older saves
|
|
||||||
std::vector<int> goltype = LoadGOLTypes();
|
|
||||||
std::vector<std::array<int, 10> > grule = LoadGOLRules();
|
|
||||||
|
|
||||||
std::vector<Element> elements = GetElements();
|
std::vector<Element> elements = GetElements();
|
||||||
|
|
||||||
//New file header uses PSv, replacing fuC. This is to detect if the client uses a new save format for temperatures
|
//New file header uses PSv, replacing fuC. This is to detect if the client uses a new save format for temperatures
|
||||||
@ -1658,9 +1664,9 @@ void GameSave::readPSv(char * saveDataChar, int dataLength)
|
|||||||
ttv |= (data[p++]);
|
ttv |= (data[p++]);
|
||||||
particles[i-1].tmp = ttv;
|
particles[i-1].tmp = ttv;
|
||||||
if (ver<53 && !particles[i-1].tmp)
|
if (ver<53 && !particles[i-1].tmp)
|
||||||
for (q = 1; q<=NGOL; q++) {
|
for (q = 0; q < NGOL; q++) {
|
||||||
if (particles[i-1].type==goltype[q-1] && grule[q][9]==2)
|
if (particles[i-1].type==builtinGol[q].oldtype && (builtinGol[q].ruleset >> 17)==0)
|
||||||
particles[i-1].tmp = grule[q][9]-1;
|
particles[i-1].tmp = (builtinGol[q].ruleset >> 17)+1;
|
||||||
}
|
}
|
||||||
if (ver>=51 && ver<53 && particles[i-1].type==PT_PBCN)
|
if (ver>=51 && ver<53 && particles[i-1].type==PT_PBCN)
|
||||||
{
|
{
|
||||||
@ -1844,7 +1850,7 @@ void GameSave::readPSv(char * saveDataChar, int dataLength)
|
|||||||
//Replace old GOL
|
//Replace old GOL
|
||||||
particles[i-1].type = PT_LIFE;
|
particles[i-1].type = PT_LIFE;
|
||||||
for (gnum = 0; gnum<NGOL; gnum++){
|
for (gnum = 0; gnum<NGOL; gnum++){
|
||||||
if (ty==goltype[gnum])
|
if (ty==builtinGol[gnum].oldtype)
|
||||||
particles[i-1].ctype = gnum;
|
particles[i-1].ctype = gnum;
|
||||||
}
|
}
|
||||||
ty = PT_LIFE;
|
ty = PT_LIFE;
|
||||||
@ -1852,13 +1858,23 @@ void GameSave::readPSv(char * saveDataChar, int dataLength)
|
|||||||
if(ver<52 && (ty==PT_CLNE || ty==PT_PCLN || ty==PT_BCLN)){
|
if(ver<52 && (ty==PT_CLNE || ty==PT_PCLN || ty==PT_BCLN)){
|
||||||
//Replace old GOL ctypes in clone
|
//Replace old GOL ctypes in clone
|
||||||
for (gnum = 0; gnum<NGOL; gnum++){
|
for (gnum = 0; gnum<NGOL; gnum++){
|
||||||
if (particles[i-1].ctype==goltype[gnum])
|
if (particles[i-1].ctype==builtinGol[gnum].oldtype)
|
||||||
{
|
{
|
||||||
particles[i-1].ctype = PT_LIFE;
|
particles[i-1].ctype = PT_LIFE;
|
||||||
particles[i-1].tmp = gnum;
|
particles[i-1].tmp = gnum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (particles[i-1].type == PT_LIFE)
|
||||||
|
{
|
||||||
|
particles[i-1].tmp2 = particles[i-1].tmp;
|
||||||
|
particles[i-1].tmp = 0;
|
||||||
|
if (particles[i-1].ctype >= 0 && particles[i-1].ctype < NGOL)
|
||||||
|
{
|
||||||
|
particles[i-1].dcolour = builtinGol[particles[i-1].ctype].colour;
|
||||||
|
particles[i-1].tmp = builtinGol[particles[i-1].ctype].colour2;
|
||||||
|
}
|
||||||
|
}
|
||||||
if(ty==PT_LCRY){
|
if(ty==PT_LCRY){
|
||||||
if(ver<67)
|
if(ver<67)
|
||||||
{
|
{
|
||||||
@ -2269,7 +2285,7 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Dcolour (optional), 4 bytes
|
//Dcolour (optional), 4 bytes
|
||||||
if(particles[i].dcolour && (particles[i].dcolour & 0xFF000000))
|
if(particles[i].dcolour && (particles[i].dcolour & 0xFF000000 || particles[i].type == PT_LIFE))
|
||||||
{
|
{
|
||||||
fieldDesc |= 1 << 6;
|
fieldDesc |= 1 << 6;
|
||||||
partsData[partsDataLen++] = (particles[i].dcolour&0xFF000000)>>24;
|
partsData[partsDataLen++] = (particles[i].dcolour&0xFF000000)>>24;
|
||||||
@ -2411,6 +2427,10 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
|
|||||||
RESTRICTVERSION(95, 0);
|
RESTRICTVERSION(95, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (particles[i].type == PT_LIFE)
|
||||||
|
{
|
||||||
|
RESTRICTVERSION(95, 1);
|
||||||
|
}
|
||||||
|
|
||||||
//Get the pmap entry for the next particle in the same position
|
//Get the pmap entry for the next particle in the same position
|
||||||
i = partsPosLink[i];
|
i = partsPosLink[i];
|
||||||
|
214
src/gui/game/GOLTool.cpp
Normal file
214
src/gui/game/GOLTool.cpp
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
#include "Tool.h"
|
||||||
|
|
||||||
|
#include "simulation/GOLString.h"
|
||||||
|
|
||||||
|
#include "client/Client.h"
|
||||||
|
#include "common/tpt-rand.h"
|
||||||
|
|
||||||
|
#include "gui/Style.h"
|
||||||
|
#include "gui/game/GameModel.h"
|
||||||
|
#include "gui/interface/Window.h"
|
||||||
|
#include "gui/interface/Button.h"
|
||||||
|
#include "gui/interface/Label.h"
|
||||||
|
#include "gui/interface/Textbox.h"
|
||||||
|
#include "gui/dialogues/ErrorMessage.h"
|
||||||
|
#include "gui/colourpicker/ColourPickerActivity.h"
|
||||||
|
|
||||||
|
#include "graphics/Graphics.h"
|
||||||
|
|
||||||
|
class GOLWindow: public ui::Window
|
||||||
|
{
|
||||||
|
void UpdateGradient();
|
||||||
|
|
||||||
|
public:
|
||||||
|
ui::Colour highColour, lowColour;
|
||||||
|
ui::Button *highColourButton, *lowColourButton;
|
||||||
|
ui::Textbox *nameField, *ruleField;
|
||||||
|
GOLTool * tool;
|
||||||
|
Simulation *sim;
|
||||||
|
int toolSelection;
|
||||||
|
GOLWindow(GOLTool *tool_, Simulation *sim, int toolSelection, int rule, int colour1, int colour2);
|
||||||
|
void Validate();
|
||||||
|
void OnDraw() override;
|
||||||
|
void OnTryExit(ExitMethod method) override;
|
||||||
|
virtual ~GOLWindow() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
GOLWindow::GOLWindow(GOLTool * tool_, Simulation *sim_, int toolSelection, int rule, int colour1, int colour2):
|
||||||
|
ui::Window(ui::Point(-1, -1), ui::Point(200, 108)),
|
||||||
|
tool(tool_),
|
||||||
|
sim(sim_),
|
||||||
|
toolSelection(toolSelection)
|
||||||
|
{
|
||||||
|
ui::Label * messageLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 14), "Edit custom GOL type");
|
||||||
|
messageLabel->SetTextColour(style::Colour::InformationTitle);
|
||||||
|
messageLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
|
||||||
|
messageLabel->Appearance.VerticalAlign = ui::Appearance::AlignTop;
|
||||||
|
AddComponent(messageLabel);
|
||||||
|
|
||||||
|
auto *okayButton = new ui::Button(ui::Point(0, Size.Y-17), ui::Point(Size.X, 17), "OK");
|
||||||
|
okayButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
|
||||||
|
okayButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
|
||||||
|
okayButton->Appearance.BorderInactive = ui::Colour(200, 200, 200);
|
||||||
|
okayButton->SetActionCallback({ [this] {
|
||||||
|
CloseActiveWindow();
|
||||||
|
if (nameField->GetText().length() && ruleField->GetText().length())
|
||||||
|
{
|
||||||
|
Validate();
|
||||||
|
}
|
||||||
|
SelfDestruct();
|
||||||
|
} });
|
||||||
|
AddComponent(okayButton);
|
||||||
|
SetOkayButton(okayButton);
|
||||||
|
|
||||||
|
nameField = new ui::Textbox(ui::Point(8, 25), ui::Point(Size.X-16, 16), "", "[name]");
|
||||||
|
nameField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
|
||||||
|
nameField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
|
||||||
|
AddComponent(nameField);
|
||||||
|
FocusComponent(nameField);
|
||||||
|
|
||||||
|
ruleField = new ui::Textbox(ui::Point(8, 46), ui::Point(Size.X-16, 16), "", "[rule]");
|
||||||
|
ruleField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
|
||||||
|
ruleField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
|
||||||
|
AddComponent(ruleField);
|
||||||
|
FocusComponent(ruleField);
|
||||||
|
|
||||||
|
highColourButton = new ui::Button(ui::Point(8, 67), ui::Point(16, 16), "");
|
||||||
|
highColourButton->SetActionCallback({ [this] {
|
||||||
|
new ColourPickerActivity(highColour, [this](ui::Colour colour) {
|
||||||
|
highColour = colour;
|
||||||
|
UpdateGradient();
|
||||||
|
});
|
||||||
|
} });
|
||||||
|
AddComponent(highColourButton);
|
||||||
|
|
||||||
|
lowColourButton = new ui::Button(ui::Point(Size.X - 24, 67), ui::Point(16, 16), "");
|
||||||
|
lowColourButton->SetActionCallback({ [this] {
|
||||||
|
new ColourPickerActivity(lowColour, [this](ui::Colour colour) {
|
||||||
|
lowColour = colour;
|
||||||
|
UpdateGradient();
|
||||||
|
});
|
||||||
|
} });
|
||||||
|
AddComponent(lowColourButton);
|
||||||
|
|
||||||
|
if (rule)
|
||||||
|
{
|
||||||
|
ruleField->SetText(SerialiseGOLRule(rule));
|
||||||
|
nameField->SetText("");
|
||||||
|
highColour.Red = PIXR(colour1);
|
||||||
|
highColour.Green = PIXG(colour1);
|
||||||
|
highColour.Blue = PIXB(colour1);
|
||||||
|
lowColour.Red = PIXR(colour2);
|
||||||
|
lowColour.Green = PIXG(colour2);
|
||||||
|
lowColour.Blue = PIXB(colour2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ruleField->SetText(Client::Ref().GetPrefString("CustomGOL.Rule", "B3/S23"));
|
||||||
|
nameField->SetText(Client::Ref().GetPrefString("CustomGOL.Name", "CGOL"));
|
||||||
|
highColour.Red = RNG::Ref().between(0x80, 0xFF);
|
||||||
|
highColour.Green = RNG::Ref().between(0x80, 0xFF);
|
||||||
|
highColour.Blue = RNG::Ref().between(0x80, 0xFF);
|
||||||
|
lowColour.Red = RNG::Ref().between(0x00, 0x7F);
|
||||||
|
lowColour.Green = RNG::Ref().between(0x00, 0x7F);
|
||||||
|
lowColour.Blue = RNG::Ref().between(0x00, 0x7F);
|
||||||
|
}
|
||||||
|
highColour.Alpha = 255;
|
||||||
|
lowColour.Alpha = 255;
|
||||||
|
UpdateGradient();
|
||||||
|
|
||||||
|
MakeActiveWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GOLWindow::UpdateGradient()
|
||||||
|
{
|
||||||
|
highColourButton->Appearance.BackgroundInactive = highColour;
|
||||||
|
highColourButton->Appearance.BackgroundHover = highColour;
|
||||||
|
lowColourButton->Appearance.BackgroundInactive = lowColour;
|
||||||
|
lowColourButton->Appearance.BackgroundHover = lowColour;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GOLWindow::Validate()
|
||||||
|
{
|
||||||
|
tool->selectGOLType.clear();
|
||||||
|
auto nameString = nameField->GetText();
|
||||||
|
auto ruleString = ruleField->GetText();
|
||||||
|
if (!ValidateGOLName(nameString))
|
||||||
|
{
|
||||||
|
new ErrorMessage("Could not add GOL type", "Invalid name provided");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nameString = nameString.ToUpper();
|
||||||
|
int rule = ParseGOLString(ruleString);
|
||||||
|
if (rule == -1)
|
||||||
|
{
|
||||||
|
new ErrorMessage("Could not add GOL type", "Invalid rule provided");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ruleString = SerialiseGOLRule(rule); // * Make it canonical.
|
||||||
|
|
||||||
|
Client::Ref().SetPrefUnicode("CustomGOL.Name", nameString);
|
||||||
|
Client::Ref().SetPrefUnicode("CustomGOL.Rule", ruleString);
|
||||||
|
|
||||||
|
auto customGOLTypes = Client::Ref().GetPrefByteStringArray("CustomGOL.Types");
|
||||||
|
Json::Value newCustomGOLTypes(Json::arrayValue);
|
||||||
|
bool nameTaken = false;
|
||||||
|
for (auto gol : customGOLTypes)
|
||||||
|
{
|
||||||
|
auto parts = gol.FromUtf8().PartitionBy(' ');
|
||||||
|
if (parts.size())
|
||||||
|
{
|
||||||
|
if (parts[0] == nameString)
|
||||||
|
{
|
||||||
|
nameTaken = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newCustomGOLTypes.append(gol);
|
||||||
|
}
|
||||||
|
if (nameTaken)
|
||||||
|
{
|
||||||
|
new ErrorMessage("Could not add GOL type", "Name already taken");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb;
|
||||||
|
auto colour1 = (((highColour.Red << 8) | highColour.Green) << 8) | highColour.Blue;
|
||||||
|
auto colour2 = (((lowColour.Red << 8) | lowColour.Green) << 8) | lowColour.Blue;
|
||||||
|
sb << nameString << " " << ruleString << " " << colour1 << " " << colour2;
|
||||||
|
newCustomGOLTypes.append(sb.Build().ToUtf8());
|
||||||
|
Client::Ref().SetPref("CustomGOL.Types", newCustomGOLTypes);
|
||||||
|
tool->gameModel->SelectNextIdentifier = "DEFAULT_PT_LIFECUST_" + nameString.ToAscii();
|
||||||
|
tool->gameModel->SelectNextTool = toolSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GOLWindow::OnTryExit(ExitMethod method)
|
||||||
|
{
|
||||||
|
CloseActiveWindow();
|
||||||
|
SelfDestruct();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GOLWindow::OnDraw()
|
||||||
|
{
|
||||||
|
Graphics * g = GetGraphics();
|
||||||
|
|
||||||
|
g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3);
|
||||||
|
g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 200, 200, 200, 255);
|
||||||
|
|
||||||
|
int width = Size.X - 60;
|
||||||
|
for (int xx = 0; xx < width; ++xx)
|
||||||
|
{
|
||||||
|
auto f = xx / (float)width;
|
||||||
|
for (int yy = 0; yy < 16; ++yy)
|
||||||
|
{
|
||||||
|
int rr = highColour.Red * (1.f - f) + lowColour.Red * f;
|
||||||
|
int gg = highColour.Green * (1.f - f) + lowColour.Green * f;
|
||||||
|
int bb = highColour.Blue * (1.f - f) + lowColour.Blue * f;
|
||||||
|
g->blendpixel(Position.X + xx + 30, Position.Y + yy + 67, rr, gg, bb, 255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GOLTool::OpenWindow(Simulation *sim, int toolSelection, int rule, int colour1, int colour2)
|
||||||
|
{
|
||||||
|
new GOLWindow(this, sim, toolSelection, rule, colour1, colour2);
|
||||||
|
}
|
@ -779,6 +779,12 @@ void GameController::Tick()
|
|||||||
#endif
|
#endif
|
||||||
firstTick = false;
|
firstTick = false;
|
||||||
}
|
}
|
||||||
|
if (gameModel->SelectNextIdentifier.length())
|
||||||
|
{
|
||||||
|
gameModel->BuildMenus();
|
||||||
|
gameModel->SetActiveTool(gameModel->SelectNextTool, gameModel->GetToolFromIdentifier(gameModel->SelectNextIdentifier));
|
||||||
|
gameModel->SelectNextIdentifier.clear();
|
||||||
|
}
|
||||||
for(std::vector<DebugInfo*>::iterator iter = debugInfo.begin(), end = debugInfo.end(); iter != end; iter++)
|
for(std::vector<DebugInfo*>::iterator iter = debugInfo.begin(), end = debugInfo.end(); iter != end; iter++)
|
||||||
{
|
{
|
||||||
if ((*iter)->debugID & debugFlags)
|
if ((*iter)->debugID & debugFlags)
|
||||||
@ -1114,6 +1120,10 @@ void GameController::SetActiveTool(int toolSelection, Tool * tool)
|
|||||||
}
|
}
|
||||||
if(tool->GetIdentifier() == "DEFAULT_UI_PROPERTY")
|
if(tool->GetIdentifier() == "DEFAULT_UI_PROPERTY")
|
||||||
((PropertyTool *)tool)->OpenWindow(gameModel->GetSimulation());
|
((PropertyTool *)tool)->OpenWindow(gameModel->GetSimulation());
|
||||||
|
if(tool->GetIdentifier() == "DEFAULT_UI_ADDLIFE")
|
||||||
|
{
|
||||||
|
((GOLTool *)tool)->OpenWindow(gameModel->GetSimulation(), toolSelection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameController::SetActiveTool(int toolSelection, ByteString identifier)
|
void GameController::SetActiveTool(int toolSelection, ByteString identifier)
|
||||||
@ -1672,3 +1682,8 @@ bool GameController::GetMouseClickRequired()
|
|||||||
{
|
{
|
||||||
return gameModel->GetMouseClickRequired();
|
return gameModel->GetMouseClickRequired();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameController::RemoveCustomGOLType(const ByteString &identifier)
|
||||||
|
{
|
||||||
|
gameModel->RemoveCustomGOLType(identifier);
|
||||||
|
}
|
||||||
|
@ -174,6 +174,8 @@ public:
|
|||||||
void NotifyNewNotification(Client * sender, std::pair<String, ByteString> notification) override;
|
void NotifyNewNotification(Client * sender, std::pair<String, ByteString> notification) override;
|
||||||
void RunUpdater();
|
void RunUpdater();
|
||||||
bool GetMouseClickRequired();
|
bool GetMouseClickRequired();
|
||||||
|
|
||||||
|
void RemoveCustomGOLType(const ByteString &identifier);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // GAMECONTROLLER_H
|
#endif // GAMECONTROLLER_H
|
||||||
|
@ -27,11 +27,13 @@
|
|||||||
#include "simulation/Gravity.h"
|
#include "simulation/Gravity.h"
|
||||||
#include "simulation/ElementGraphics.h"
|
#include "simulation/ElementGraphics.h"
|
||||||
#include "simulation/ElementClasses.h"
|
#include "simulation/ElementClasses.h"
|
||||||
|
#include "simulation/GOLString.h"
|
||||||
|
|
||||||
#include "gui/game/DecorationTool.h"
|
#include "gui/game/DecorationTool.h"
|
||||||
#include "gui/interface/Engine.h"
|
#include "gui/interface/Engine.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
GameModel::GameModel():
|
GameModel::GameModel():
|
||||||
clipboard(NULL),
|
clipboard(NULL),
|
||||||
@ -303,9 +305,55 @@ void GameModel::BuildMenus()
|
|||||||
//Build menu for GOL types
|
//Build menu for GOL types
|
||||||
for(int i = 0; i < NGOL; i++)
|
for(int i = 0; i < NGOL; i++)
|
||||||
{
|
{
|
||||||
Tool * tempTool = new ElementTool(PT_LIFE|PMAPID(i), sim->gmenu[i].name, sim->gmenu[i].description, PIXR(sim->gmenu[i].colour), PIXG(sim->gmenu[i].colour), PIXB(sim->gmenu[i].colour), "DEFAULT_PT_LIFE_"+sim->gmenu[i].name.ToAscii());
|
Tool * tempTool = new ElementTool(PT_LIFE|PMAPID(i), builtinGol[i].name, builtinGol[i].description, PIXR(builtinGol[i].colour), PIXG(builtinGol[i].colour), PIXB(builtinGol[i].colour), "DEFAULT_PT_LIFE_"+builtinGol[i].name.ToAscii());
|
||||||
menuList[SC_LIFE]->AddTool(tempTool);
|
menuList[SC_LIFE]->AddTool(tempTool);
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
auto customGOLTypes = Client::Ref().GetPrefByteStringArray("CustomGOL.Types");
|
||||||
|
Json::Value validatedCustomLifeTypes(Json::arrayValue);
|
||||||
|
std::vector<Simulation::CustomGOLData> newCustomGol;
|
||||||
|
for (auto gol : customGOLTypes)
|
||||||
|
{
|
||||||
|
auto parts = gol.FromUtf8().PartitionBy(' ');
|
||||||
|
if (parts.size() != 4)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Simulation::CustomGOLData gd;
|
||||||
|
gd.nameString = parts[0];
|
||||||
|
gd.ruleString = parts[1];
|
||||||
|
auto &colour1String = parts[2];
|
||||||
|
auto &colour2String = parts[3];
|
||||||
|
if (!ValidateGOLName(gd.nameString))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
gd.rule = ParseGOLString(gd.ruleString);
|
||||||
|
if (gd.rule == -1)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
gd.colour1 = colour1String.ToNumber<int>();
|
||||||
|
gd.colour2 = colour2String.ToNumber<int>();
|
||||||
|
}
|
||||||
|
catch (std::exception &)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
newCustomGol.push_back(gd);
|
||||||
|
validatedCustomLifeTypes.append(gol);
|
||||||
|
}
|
||||||
|
// All custom rules that fail validation will be removed
|
||||||
|
Client::Ref().SetPref("CustomGOL.Types", validatedCustomLifeTypes);
|
||||||
|
for (auto &gd : newCustomGol)
|
||||||
|
{
|
||||||
|
Tool * tempTool = new ElementTool(PT_LIFE|PMAPID(gd.rule), gd.nameString, "Custom GOL type: " + gd.ruleString, PIXR(gd.colour1), PIXG(gd.colour1), PIXB(gd.colour1), "DEFAULT_PT_LIFECUST_"+gd.nameString.ToAscii(), NULL);
|
||||||
|
menuList[SC_LIFE]->AddTool(tempTool);
|
||||||
|
}
|
||||||
|
sim->SetCustomGOL(newCustomGol);
|
||||||
|
}
|
||||||
|
|
||||||
//Build other menus from wall data
|
//Build other menus from wall data
|
||||||
for(int i = 0; i < UI_WALLCOUNT; i++)
|
for(int i = 0; i < UI_WALLCOUNT; i++)
|
||||||
@ -331,9 +379,10 @@ void GameModel::BuildMenus()
|
|||||||
}
|
}
|
||||||
//Add special sign and prop tools
|
//Add special sign and prop tools
|
||||||
menuList[SC_TOOL]->AddTool(new WindTool(0, "WIND", "Creates air movement.", 64, 64, 64, "DEFAULT_UI_WIND"));
|
menuList[SC_TOOL]->AddTool(new WindTool(0, "WIND", "Creates air movement.", 64, 64, 64, "DEFAULT_UI_WIND"));
|
||||||
menuList[SC_TOOL]->AddTool(new PropertyTool());
|
menuList[SC_TOOL]->AddTool(new PropertyTool(this));
|
||||||
menuList[SC_TOOL]->AddTool(new SignTool(this));
|
menuList[SC_TOOL]->AddTool(new SignTool(this));
|
||||||
menuList[SC_TOOL]->AddTool(new SampleTool(this));
|
menuList[SC_TOOL]->AddTool(new SampleTool(this));
|
||||||
|
menuList[SC_LIFE]->AddTool(new GOLTool(this));
|
||||||
|
|
||||||
//Add decoration tools to menu
|
//Add decoration tools to menu
|
||||||
menuList[SC_DECO]->AddTool(new DecorationTool(ren, DECO_ADD, "ADD", "Colour blending: Add.", 0, 0, 0, "DEFAULT_DECOR_ADD"));
|
menuList[SC_DECO]->AddTool(new DecorationTool(ren, DECO_ADD, "ADD", "Colour blending: Add.", 0, 0, 0, "DEFAULT_DECOR_ADD"));
|
||||||
@ -1374,3 +1423,27 @@ void GameModel::SetPerfectCircle(bool perfectCircle)
|
|||||||
BuildBrushList();
|
BuildBrushList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameModel::RemoveCustomGOLType(const ByteString &identifier)
|
||||||
|
{
|
||||||
|
auto customGOLTypes = Client::Ref().GetPrefByteStringArray("CustomGOL.Types");
|
||||||
|
Json::Value newCustomGOLTypes(Json::arrayValue);
|
||||||
|
for (auto gol : customGOLTypes)
|
||||||
|
{
|
||||||
|
auto parts = gol.PartitionBy(' ');
|
||||||
|
bool remove = false;
|
||||||
|
if (parts.size())
|
||||||
|
{
|
||||||
|
if ("DEFAULT_PT_LIFECUST_" + parts[0] == identifier)
|
||||||
|
{
|
||||||
|
remove = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!remove)
|
||||||
|
{
|
||||||
|
newCustomGOLTypes.append(gol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Client::Ref().SetPref("CustomGOL.Types", newCustomGOLTypes);
|
||||||
|
BuildMenus();
|
||||||
|
}
|
||||||
|
@ -219,6 +219,11 @@ public:
|
|||||||
std::vector<Notification*> GetNotifications();
|
std::vector<Notification*> GetNotifications();
|
||||||
void AddNotification(Notification * notification);
|
void AddNotification(Notification * notification);
|
||||||
void RemoveNotification(Notification * notification);
|
void RemoveNotification(Notification * notification);
|
||||||
|
|
||||||
|
void RemoveCustomGOLType(const ByteString &identifier);
|
||||||
|
|
||||||
|
ByteString SelectNextIdentifier;
|
||||||
|
int SelectNextTool;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // GAMEMODEL_H
|
#endif // GAMEMODEL_H
|
||||||
|
@ -585,8 +585,19 @@ void GameView::NotifyToolListChanged(GameModel * sender)
|
|||||||
}
|
}
|
||||||
else if (tempButton->GetSelectionState() == 1)
|
else if (tempButton->GetSelectionState() == 1)
|
||||||
{
|
{
|
||||||
Favorite::Ref().RemoveFavorite(tool->GetIdentifier());
|
auto identifier = tool->GetIdentifier();
|
||||||
c->RebuildFavoritesMenu();
|
if (Favorite::Ref().IsFavorite(identifier))
|
||||||
|
{
|
||||||
|
Favorite::Ref().RemoveFavorite(identifier);
|
||||||
|
c->RebuildFavoritesMenu();
|
||||||
|
}
|
||||||
|
else if (identifier.BeginsWith("DEFAULT_PT_LIFECUST_"))
|
||||||
|
{
|
||||||
|
if (ConfirmPrompt::Blocking("Remove custom GOL type", "Are you sure you want to remove " + identifier.Substr(20).FromUtf8() + "?"))
|
||||||
|
{
|
||||||
|
c->RemoveCustomGOLType(identifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -2154,6 +2165,8 @@ void GameView::OnDraw()
|
|||||||
// Some elements store extra LIFE info in upper bits of ctype, instead of tmp/tmp2
|
// Some elements store extra LIFE info in upper bits of ctype, instead of tmp/tmp2
|
||||||
else if (type == PT_CRAY || type == PT_DRAY || type == PT_CONV)
|
else if (type == PT_CRAY || type == PT_DRAY || type == PT_CONV)
|
||||||
sampleInfo << " (" << c->ElementResolve(TYP(ctype), ID(ctype)) << ")";
|
sampleInfo << " (" << c->ElementResolve(TYP(ctype), ID(ctype)) << ")";
|
||||||
|
else if (type == PT_CLNE || type == PT_BCLN || type == PT_PCLN || type == PT_PBCN)
|
||||||
|
sampleInfo << " (" << c->ElementResolve(ctype, sample.particle.tmp) << ")";
|
||||||
else if (c->IsValidElement(ctype))
|
else if (c->IsValidElement(ctype))
|
||||||
sampleInfo << " (" << c->ElementResolve(ctype, -1) << ")";
|
sampleInfo << " (" << c->ElementResolve(ctype, -1) << ")";
|
||||||
else
|
else
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
#include "Tool.h"
|
#include "Tool.h"
|
||||||
|
|
||||||
#include "client/Client.h"
|
#include "client/Client.h"
|
||||||
|
#include "Menu.h"
|
||||||
|
|
||||||
|
#include "gui/game/GameModel.h"
|
||||||
#include "gui/Style.h"
|
#include "gui/Style.h"
|
||||||
#include "gui/game/Brush.h"
|
#include "gui/game/Brush.h"
|
||||||
#include "gui/interface/Window.h"
|
#include "gui/interface/Window.h"
|
||||||
@ -12,7 +14,10 @@
|
|||||||
#include "gui/interface/Keys.h"
|
#include "gui/interface/Keys.h"
|
||||||
#include "gui/dialogues/ErrorMessage.h"
|
#include "gui/dialogues/ErrorMessage.h"
|
||||||
|
|
||||||
|
#include "simulation/GOLString.h"
|
||||||
|
#include "simulation/BuiltinGOL.h"
|
||||||
#include "simulation/Simulation.h"
|
#include "simulation/Simulation.h"
|
||||||
|
#include "simulation/SimulationData.h"
|
||||||
|
|
||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
|
|
||||||
@ -83,7 +88,7 @@ void PropertyWindow::SetProperty()
|
|||||||
{
|
{
|
||||||
if(property->GetOption().second!=-1 && textField->GetText().length() > 0)
|
if(property->GetOption().second!=-1 && textField->GetText().length() > 0)
|
||||||
{
|
{
|
||||||
String value = textField->GetText();
|
String value = textField->GetText().ToUpper();
|
||||||
try {
|
try {
|
||||||
switch(properties[property->GetOption().second].Type)
|
switch(properties[property->GetOption().second].Type)
|
||||||
{
|
{
|
||||||
@ -91,7 +96,7 @@ void PropertyWindow::SetProperty()
|
|||||||
case StructProperty::ParticleType:
|
case StructProperty::ParticleType:
|
||||||
{
|
{
|
||||||
int v;
|
int v;
|
||||||
if(value.length() > 2 && value.BeginsWith("0x"))
|
if(value.length() > 2 && value.BeginsWith("0X"))
|
||||||
{
|
{
|
||||||
//0xC0FFEE
|
//0xC0FFEE
|
||||||
v = value.Substr(2).ToNumber<unsigned int>(Format::Hex());
|
v = value.Substr(2).ToNumber<unsigned int>(Format::Hex());
|
||||||
@ -101,18 +106,32 @@ void PropertyWindow::SetProperty()
|
|||||||
//#C0FFEE
|
//#C0FFEE
|
||||||
v = value.Substr(1).ToNumber<unsigned int>(Format::Hex());
|
v = value.Substr(1).ToNumber<unsigned int>(Format::Hex());
|
||||||
}
|
}
|
||||||
|
else if (value.length() > 1 && value.BeginsWith("B") && value.Contains("/"))
|
||||||
|
{
|
||||||
|
v = ParseGOLString(value);
|
||||||
|
if (v == -1)
|
||||||
|
{
|
||||||
|
class InvalidGOLString : public std::exception
|
||||||
|
{
|
||||||
|
};
|
||||||
|
throw InvalidGOLString();
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int type;
|
v = sim->GetParticleType(value.ToUtf8());
|
||||||
if ((type = sim->GetParticleType(value.ToUtf8())) != -1)
|
if (v == -1)
|
||||||
{
|
{
|
||||||
v = type;
|
for (auto *elementTool : tool->gameModel->GetMenuList()[SC_LIFE]->GetToolList())
|
||||||
|
{
|
||||||
#ifdef DEBUG
|
if (elementTool && elementTool->GetName() == value)
|
||||||
std::cout << "Got type from particle name" << std::endl;
|
{
|
||||||
#endif
|
v = ID(elementTool->GetToolID());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
if (v == -1)
|
||||||
{
|
{
|
||||||
v = value.ToNumber<int>();
|
v = value.ToNumber<int>();
|
||||||
}
|
}
|
||||||
@ -134,7 +153,7 @@ void PropertyWindow::SetProperty()
|
|||||||
case StructProperty::UInteger:
|
case StructProperty::UInteger:
|
||||||
{
|
{
|
||||||
unsigned int v;
|
unsigned int v;
|
||||||
if(value.length() > 2 && value.BeginsWith("0x"))
|
if(value.length() > 2 && value.BeginsWith("0X"))
|
||||||
{
|
{
|
||||||
//0xC0FFEE
|
//0xC0FFEE
|
||||||
v = value.Substr(2).ToNumber<unsigned int>(Format::Hex());
|
v = value.Substr(2).ToNumber<unsigned int>(Format::Hex());
|
||||||
|
@ -38,36 +38,37 @@ void SampleTool::Draw(Simulation * sim, Brush * brush, ui::Point position)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int particleType = 0;
|
Particle *part = nullptr;
|
||||||
int particleCtype = 0;
|
|
||||||
if (sim->photons[position.Y][position.X])
|
if (sim->photons[position.Y][position.X])
|
||||||
{
|
{
|
||||||
particleType = sim->parts[ID(sim->photons[position.Y][position.X])].type;
|
part = &sim->parts[ID(sim->photons[position.Y][position.X])];
|
||||||
particleCtype = sim->parts[ID(sim->pmap[position.Y][position.X])].ctype;
|
|
||||||
}
|
}
|
||||||
else if (sim->pmap[position.Y][position.X])
|
else if (sim->pmap[position.Y][position.X])
|
||||||
{
|
{
|
||||||
particleType = sim->parts[ID(sim->pmap[position.Y][position.X])].type;
|
part = &sim->parts[ID(sim->pmap[position.Y][position.X])];
|
||||||
particleCtype = sim->parts[ID(sim->pmap[position.Y][position.X])].ctype;
|
|
||||||
}
|
}
|
||||||
|
if (part)
|
||||||
if(particleType)
|
|
||||||
{
|
{
|
||||||
if(particleType == PT_LIFE)
|
if (part->type == PT_LIFE)
|
||||||
{
|
{
|
||||||
Menu * lifeMenu = gameModel->GetMenuList()[SC_LIFE];
|
bool found = false;
|
||||||
std::vector<Tool*> elementTools = lifeMenu->GetToolList();
|
for (auto *elementTool : gameModel->GetMenuList()[SC_LIFE]->GetToolList())
|
||||||
|
|
||||||
for(std::vector<Tool*>::iterator iter = elementTools.begin(), end = elementTools.end(); iter != end; ++iter)
|
|
||||||
{
|
{
|
||||||
Tool * elementTool = *iter;
|
if (elementTool && ID(elementTool->GetToolID()) == part->ctype)
|
||||||
if(elementTool && ID(elementTool->GetToolID()) == particleCtype)
|
{
|
||||||
gameModel->SetActiveTool(0, elementTool);
|
gameModel->SetActiveTool(0, elementTool);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
((GOLTool *)(gameModel->GetToolFromIdentifier("DEFAULT_UI_ADDLIFE")))->OpenWindow(gameModel->GetSimulation(), 0, part->ctype, part->dcolour, part->tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Tool * elementTool = gameModel->GetElementTool(particleType);
|
Tool * elementTool = gameModel->GetElementTool(part->type);
|
||||||
if(elementTool)
|
if(elementTool)
|
||||||
gameModel->SetActiveTool(0, elementTool);
|
gameModel->SetActiveTool(0, elementTool);
|
||||||
}
|
}
|
||||||
|
@ -81,8 +81,10 @@ public:
|
|||||||
class PropertyTool: public Tool
|
class PropertyTool: public Tool
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PropertyTool():
|
GameModel * gameModel;
|
||||||
Tool(0, "PROP", "Property Drawing Tool. Use to alter the properties of elements in the field.", 0xfe, 0xa9, 0x00, "DEFAULT_UI_PROPERTY", NULL)
|
PropertyTool(GameModel *model):
|
||||||
|
Tool(0, "PROP", "Property Drawing Tool. Use to alter the properties of elements in the field.", 0xfe, 0xa9, 0x00, "DEFAULT_UI_PROPERTY", NULL),
|
||||||
|
gameModel(model)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
StructProperty::PropertyType propType;
|
StructProperty::PropertyType propType;
|
||||||
@ -100,6 +102,25 @@ public:
|
|||||||
void DrawFill(Simulation * sim, Brush * brush, ui::Point position) override;
|
void DrawFill(Simulation * sim, Brush * brush, ui::Point position) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class GOLTool: public Tool
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
String selectGOLType;
|
||||||
|
GameModel * gameModel;
|
||||||
|
GOLTool(GameModel * gameModel):
|
||||||
|
Tool(0, "CUST", "Add a new custom GOL type. (Use ctrl+shift+rightclick to remove them)", 0xfe, 0xa9, 0x00, "DEFAULT_UI_ADDLIFE", NULL),
|
||||||
|
gameModel(gameModel)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void OpenWindow(Simulation *sim, int toolSelection, int rule = 0, int colour1 = 0, int colour2 = 0);
|
||||||
|
virtual ~GOLTool() {}
|
||||||
|
void Click(Simulation * sim, Brush * brush, ui::Point position) override { }
|
||||||
|
void Draw(Simulation *sim, Brush *brush, ui::Point position) override { };
|
||||||
|
void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false) override { };
|
||||||
|
void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) override { };
|
||||||
|
void DrawFill(Simulation * sim, Brush * brush, ui::Point position) override { };
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class ElementTool: public Tool
|
class ElementTool: public Tool
|
||||||
{
|
{
|
||||||
|
14
src/simulation/BuiltinGOL.h
Normal file
14
src/simulation/BuiltinGOL.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "graphics/Pixel.h"
|
||||||
|
#include "common/String.h"
|
||||||
|
|
||||||
|
struct BuiltinGOL
|
||||||
|
{
|
||||||
|
String name;
|
||||||
|
int oldtype;
|
||||||
|
int ruleset;
|
||||||
|
pixel colour, colour2;
|
||||||
|
int goltype;
|
||||||
|
String description;
|
||||||
|
};
|
@ -250,7 +250,7 @@ bool Element::ctypeDrawVInTmp(CTYPEDRAW_FUNC_ARGS)
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (t == PT_LIFE && v >= 0 && v < NGOL)
|
if (t == PT_LIFE)
|
||||||
{
|
{
|
||||||
sim->parts[i].tmp = v;
|
sim->parts[i].tmp = v;
|
||||||
}
|
}
|
||||||
@ -263,7 +263,7 @@ bool Element::ctypeDrawVInCtype(CTYPEDRAW_FUNC_ARGS)
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (t == PT_LIFE && v >= 0 && v < NGOL)
|
if (t == PT_LIFE)
|
||||||
{
|
{
|
||||||
sim->parts[i].ctype |= PMAPID(v);
|
sim->parts[i].ctype |= PMAPID(v);
|
||||||
}
|
}
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
#ifndef The_Powder_Toy_GOLMenu_h
|
|
||||||
#define The_Powder_Toy_GOLMenu_h
|
|
||||||
|
|
||||||
struct gol_menu
|
|
||||||
{
|
|
||||||
String name;
|
|
||||||
pixel colour;
|
|
||||||
int goltype;
|
|
||||||
String description;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
91
src/simulation/GOLString.cpp
Normal file
91
src/simulation/GOLString.cpp
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#include "GOLString.h"
|
||||||
|
|
||||||
|
int ParseGOLString(const String &value)
|
||||||
|
{
|
||||||
|
// * Most likely a GOL string.
|
||||||
|
auto it = value.begin() + 1;
|
||||||
|
auto begin = 0U;
|
||||||
|
auto stay = 0U;
|
||||||
|
auto states = 2U;
|
||||||
|
|
||||||
|
// Scan 'B' section, must be between 1 and 8
|
||||||
|
for (; it != value.end() && it[0] >= '1' && it[0] <= '8'; ++it)
|
||||||
|
{
|
||||||
|
begin |= 1U << (it[0] - '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must have a /S immediately afterwards
|
||||||
|
if (it < value.end() - 1 && it[0] == '/' && it[1] == 'S')
|
||||||
|
{
|
||||||
|
it += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan 'S' section, must be between 0 and 8
|
||||||
|
for (; it != value.end() && it[0] >= '0' && it[0] <= '8'; ++it)
|
||||||
|
{
|
||||||
|
stay |= 1U << (it[0] - '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optionally can have a 3rd section, with the number of frames to remain after dying
|
||||||
|
if (it != value.end())
|
||||||
|
{
|
||||||
|
if (it[0] == '/')
|
||||||
|
{
|
||||||
|
it += 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
states = String(it, value.end()).ToNumber<unsigned int>(true);
|
||||||
|
if (states < 2 || states > 17)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stay | (begin << 8) | ((states - 2) << 17);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ValidateGOLName(const String &value)
|
||||||
|
{
|
||||||
|
bool nameOk = true;
|
||||||
|
for (auto ch : value)
|
||||||
|
{
|
||||||
|
if (!((ch >= '0' && ch < '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch == '-')))
|
||||||
|
{
|
||||||
|
nameOk = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nameOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
String SerialiseGOLRule(int rule)
|
||||||
|
{
|
||||||
|
StringBuilder golName;
|
||||||
|
golName << "B";
|
||||||
|
for (int i = 1; i < 9; ++i)
|
||||||
|
{
|
||||||
|
if ((rule >> (i + 8)) & 1)
|
||||||
|
{
|
||||||
|
golName << char('0' + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
golName << "/S";
|
||||||
|
for (int i = 0; i < 9; ++i)
|
||||||
|
{
|
||||||
|
if ((rule >> i) & 1)
|
||||||
|
{
|
||||||
|
golName << char('0' + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((rule >> 17) & 0xF)
|
||||||
|
{
|
||||||
|
golName << "/" << ((rule >> 17) & 0xF) + 2;
|
||||||
|
}
|
||||||
|
return golName.Build();
|
||||||
|
}
|
8
src/simulation/GOLString.h
Normal file
8
src/simulation/GOLString.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/String.h"
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
bool ValidateGOLName(const String &value);
|
||||||
|
int ParseGOLString(const String &value);
|
||||||
|
String SerialiseGOLRule(int rule);
|
@ -21,6 +21,7 @@
|
|||||||
#include "ToolClasses.h"
|
#include "ToolClasses.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "SimulationData.h"
|
#include "SimulationData.h"
|
||||||
|
#include "GOLString.h"
|
||||||
|
|
||||||
#include "graphics/Renderer.h"
|
#include "graphics/Renderer.h"
|
||||||
|
|
||||||
@ -2250,7 +2251,7 @@ void Simulation::clear_sim(void)
|
|||||||
memset(fvy, 0, sizeof(fvy));
|
memset(fvy, 0, sizeof(fvy));
|
||||||
memset(photons, 0, sizeof(photons));
|
memset(photons, 0, sizeof(photons));
|
||||||
memset(wireless, 0, sizeof(wireless));
|
memset(wireless, 0, sizeof(wireless));
|
||||||
memset(gol2, 0, sizeof(gol2));
|
memset(gol, 0, sizeof(gol));
|
||||||
memset(portalp, 0, sizeof(portalp));
|
memset(portalp, 0, sizeof(portalp));
|
||||||
memset(fighters, 0, sizeof(fighters));
|
memset(fighters, 0, sizeof(fighters));
|
||||||
std::fill(elementCount, elementCount+PT_NUM, 0);
|
std::fill(elementCount, elementCount+PT_NUM, 0);
|
||||||
@ -4659,120 +4660,6 @@ int Simulation::GetParticleType(ByteString type)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Simulation::SimulateGoL()
|
|
||||||
{
|
|
||||||
CGOL = 0;
|
|
||||||
//TODO: maybe this should only loop through active particles
|
|
||||||
for (int ny = CELL; ny < YRES-CELL; ny++)
|
|
||||||
{
|
|
||||||
//go through every particle and set neighbor map
|
|
||||||
for (int nx = CELL; nx < XRES-CELL; nx++)
|
|
||||||
{
|
|
||||||
int r = pmap[ny][nx];
|
|
||||||
if (!r)
|
|
||||||
{
|
|
||||||
gol[ny][nx] = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (TYP(r) == PT_LIFE)
|
|
||||||
{
|
|
||||||
int golnum = parts[ID(r)].ctype + 1;
|
|
||||||
if (golnum <= 0 || golnum > NGOL)
|
|
||||||
{
|
|
||||||
kill_part(ID(r));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
gol[ny][nx] = golnum;
|
|
||||||
if (parts[ID(r)].tmp == grule[golnum][9]-1)
|
|
||||||
{
|
|
||||||
for (int nnx = -1; nnx < 2; nnx++)
|
|
||||||
{
|
|
||||||
//it will count itself as its own neighbor, which is needed, but will have 1 extra for delete check
|
|
||||||
for (int nny = -1; nny < 2; nny++)
|
|
||||||
{
|
|
||||||
int adx = ((nx+nnx+XRES-3*CELL)%(XRES-2*CELL))+CELL;
|
|
||||||
int ady = ((ny+nny+YRES-3*CELL)%(YRES-2*CELL))+CELL;
|
|
||||||
int rt = pmap[ady][adx];
|
|
||||||
if (!rt || TYP(rt) == PT_LIFE)
|
|
||||||
{
|
|
||||||
//the total neighbor count is in 0
|
|
||||||
gol2[ady][adx][0] ++;
|
|
||||||
//insert golnum into neighbor table
|
|
||||||
for (int i = 1; i < 9; i++)
|
|
||||||
{
|
|
||||||
if (!gol2[ady][adx][i])
|
|
||||||
{
|
|
||||||
gol2[ady][adx][i] = (golnum<<4)+1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if((gol2[ady][adx][i]>>4)==golnum)
|
|
||||||
{
|
|
||||||
gol2[ady][adx][i]++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!(bmap[ny/CELL][nx/CELL] == WL_STASIS && emap[ny/CELL][nx/CELL] < 8))
|
|
||||||
{
|
|
||||||
parts[ID(r)].tmp --;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int ny = CELL; ny < YRES-CELL; ny++)
|
|
||||||
{
|
|
||||||
//go through every particle again, but check neighbor map, then update particles
|
|
||||||
for (int nx = CELL; nx < XRES-CELL; nx++)
|
|
||||||
{
|
|
||||||
int r = pmap[ny][nx];
|
|
||||||
if (r && TYP(r)!=PT_LIFE)
|
|
||||||
continue;
|
|
||||||
int neighbors = gol2[ny][nx][0];
|
|
||||||
if (neighbors)
|
|
||||||
{
|
|
||||||
if (!(bmap[ny/CELL][nx/CELL] == WL_STASIS && emap[ny/CELL][nx/CELL] < 8))
|
|
||||||
{
|
|
||||||
int golnum = gol[ny][nx];
|
|
||||||
if (!r)
|
|
||||||
{
|
|
||||||
//Find which type we can try and create
|
|
||||||
int creategol = 0xFF;
|
|
||||||
for (int i = 1; i < 9; i++)
|
|
||||||
{
|
|
||||||
if (!gol2[ny][nx][i]) break;
|
|
||||||
golnum = (gol2[ny][nx][i]>>4);
|
|
||||||
if (grule[golnum][neighbors]>= 2 && (gol2[ny][nx][i]&0xF) >= (neighbors%2)+neighbors/2)
|
|
||||||
{
|
|
||||||
if (golnum < creategol)
|
|
||||||
creategol = golnum;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (creategol < 0xFF)
|
|
||||||
create_part(-1, nx, ny, PT_LIFE, creategol-1);
|
|
||||||
}
|
|
||||||
else if (grule[golnum][neighbors-1] == 0 || grule[golnum][neighbors-1] == 2)//subtract 1 because it counted itself
|
|
||||||
{
|
|
||||||
if (parts[ID(r)].tmp == grule[golnum][9]-1)
|
|
||||||
parts[ID(r)].tmp--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int z = 0; z < 9; z++)
|
|
||||||
gol2[ny][nx][z] = 0;//this improves performance A LOT compared to the memset, i was getting ~23 more fps with this.
|
|
||||||
}
|
|
||||||
//we still need to kill things with 0 neighbors (higher state life)
|
|
||||||
if (r && parts[ID(r)].tmp <= 0)
|
|
||||||
kill_part(ID(r));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//memset(gol2, 0, sizeof(gol2));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Simulation::RecalcFreeParticles(bool do_life_dec)
|
void Simulation::RecalcFreeParticles(bool do_life_dec)
|
||||||
{
|
{
|
||||||
int x, y, t;
|
int x, y, t;
|
||||||
@ -4870,6 +4757,175 @@ void Simulation::RecalcFreeParticles(bool do_life_dec)
|
|||||||
elementRecount = false;
|
elementRecount = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Simulation::SimulateGoL()
|
||||||
|
{
|
||||||
|
CGOL = 0;
|
||||||
|
for (int i = 0; i <= parts_lastActiveIndex; ++i)
|
||||||
|
{
|
||||||
|
auto &part = parts[i];
|
||||||
|
if (part.type != PT_LIFE)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto x = int(part.x + 0.5f);
|
||||||
|
auto y = int(part.y + 0.5f);
|
||||||
|
if (x < CELL || y < CELL || x >= XRES - CELL || y >= YRES - CELL)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
unsigned int golnum = part.ctype;
|
||||||
|
unsigned int ruleset = golnum;
|
||||||
|
if (golnum < NGOL)
|
||||||
|
{
|
||||||
|
ruleset = builtinGol[golnum].ruleset;
|
||||||
|
golnum += 1;
|
||||||
|
}
|
||||||
|
if (part.tmp2 == int((ruleset >> 17) & 0xF) + 1)
|
||||||
|
{
|
||||||
|
for (int yy = -1; yy <= 1; ++yy)
|
||||||
|
{
|
||||||
|
for (int xx = -1; xx <= 1; ++xx)
|
||||||
|
{
|
||||||
|
if (xx || yy)
|
||||||
|
{
|
||||||
|
// * Calculate address of the neighbourList, taking wraparound
|
||||||
|
// into account. The fact that the GOL space is 2 CELL's worth
|
||||||
|
// narrower in both dimensions than the simulation area makes
|
||||||
|
// this a bit awkward.
|
||||||
|
int ax = ((x + xx + XRES - 3 * CELL) % (XRES - 2 * CELL)) + CELL;
|
||||||
|
int ay = ((y + yy + YRES - 3 * CELL) % (YRES - 2 * CELL)) + CELL;
|
||||||
|
unsigned int (&neighbourList)[5] = gol[ay][ax];
|
||||||
|
// * Bump overall neighbour counter (bits 30..28) for the entire list.
|
||||||
|
neighbourList[0] += 1U << 28;
|
||||||
|
for (int l = 0; l < 5; ++l)
|
||||||
|
{
|
||||||
|
auto neighbourRuleset = neighbourList[l] & 0x001FFFFFU;
|
||||||
|
if (neighbourRuleset == golnum)
|
||||||
|
{
|
||||||
|
// * Bump population counter (bits 23..21) of the
|
||||||
|
// same kind of cell.
|
||||||
|
neighbourList[l] += 1U << 21;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (neighbourRuleset == 0)
|
||||||
|
{
|
||||||
|
// * Add the new kind of cell to the population. Both counters
|
||||||
|
// have a bias of -1, so they're intentionally initialised
|
||||||
|
// to 0 instead of 1 here. This is all so they can both
|
||||||
|
// fit in 3 bits.
|
||||||
|
neighbourList[l] = ((yy & 3) << 26) | ((xx & 3) << 24) | golnum;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// * If after 5 iterations the cell still hasn't contributed
|
||||||
|
// to a list entry, it's surely a 6th kind of cell, meaning
|
||||||
|
// there could be at most 3 of it in the neighbourhood,
|
||||||
|
// as there are already 5 other kinds of cells present in
|
||||||
|
// the list. This in turn means that it couldn't possibly
|
||||||
|
// win the population ratio-based contest later on.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!(bmap[y / CELL][x / CELL] == WL_STASIS && emap[y / CELL][x / CELL] < 8))
|
||||||
|
{
|
||||||
|
part.tmp2 -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int y = CELL; y < YRES - CELL; ++y)
|
||||||
|
{
|
||||||
|
for (int x = CELL; x < XRES - CELL; ++x)
|
||||||
|
{
|
||||||
|
int r = pmap[y][x];
|
||||||
|
if (r && TYP(r) != PT_LIFE)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
unsigned int (&neighbourList)[5] = gol[y][x];
|
||||||
|
auto nl0 = neighbourList[0];
|
||||||
|
if (r || nl0)
|
||||||
|
{
|
||||||
|
// * Get overall neighbour count (bits 30..28).
|
||||||
|
unsigned int neighbours = nl0 ? ((nl0 >> 28) & 7) + 1 : 0;
|
||||||
|
if (!(bmap[y / CELL][x / CELL] == WL_STASIS && emap[y / CELL][x / CELL] < 8))
|
||||||
|
{
|
||||||
|
if (r)
|
||||||
|
{
|
||||||
|
auto &part = parts[ID(r)];
|
||||||
|
unsigned int ruleset = part.ctype;
|
||||||
|
if (ruleset < NGOL)
|
||||||
|
{
|
||||||
|
ruleset = builtinGol[ruleset].ruleset;
|
||||||
|
}
|
||||||
|
if (!((ruleset >> neighbours) & 1) && part.tmp2 == int(ruleset >> 17) + 1)
|
||||||
|
{
|
||||||
|
// * Start death sequence.
|
||||||
|
part.tmp2 -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unsigned int golnumToCreate = 0xFFFFFFFFU;
|
||||||
|
unsigned int createFromEntry = 0U;
|
||||||
|
unsigned int majority = neighbours / 2 + neighbours % 2;
|
||||||
|
for (int l = 0; l < 5; ++l)
|
||||||
|
{
|
||||||
|
auto golnum = neighbourList[l] & 0x001FFFFFU;
|
||||||
|
if (!golnum)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto ruleset = golnum;
|
||||||
|
if (golnum - 1 < NGOL)
|
||||||
|
{
|
||||||
|
ruleset = builtinGol[golnum - 1].ruleset;
|
||||||
|
golnum -= 1;
|
||||||
|
}
|
||||||
|
if ((ruleset >> (neighbours + 8)) & 1 && ((neighbourList[l] >> 21) & 7) + 1 >= majority && golnum < golnumToCreate)
|
||||||
|
{
|
||||||
|
golnumToCreate = golnum;
|
||||||
|
createFromEntry = neighbourList[l];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (golnumToCreate != 0xFFFFFFFFU)
|
||||||
|
{
|
||||||
|
// * 0x200000: No need to look for colours, they'll be set later anyway.
|
||||||
|
int i = create_part(-1, x, y, PT_LIFE, golnumToCreate | 0x200000);
|
||||||
|
int xx = (createFromEntry >> 24) & 3;
|
||||||
|
int yy = (createFromEntry >> 26) & 3;
|
||||||
|
if (xx == 3) xx = -1;
|
||||||
|
if (yy == 3) yy = -1;
|
||||||
|
int ax = ((x - xx + XRES - 3 * CELL) % (XRES - 2 * CELL)) + CELL;
|
||||||
|
int ay = ((y - yy + YRES - 3 * CELL) % (YRES - 2 * CELL)) + CELL;
|
||||||
|
auto &sample = parts[ID(pmap[ay][ax])];
|
||||||
|
parts[i].dcolour = sample.dcolour;
|
||||||
|
parts[i].tmp = sample.tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int l = 0; l < 5 && neighbourList[l]; ++l)
|
||||||
|
{
|
||||||
|
neighbourList[l] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int y = CELL; y < YRES - CELL; ++y)
|
||||||
|
{
|
||||||
|
for (int x = CELL; x < XRES - CELL; ++x)
|
||||||
|
{
|
||||||
|
int r = pmap[y][x];
|
||||||
|
if (r && TYP(r) == PT_LIFE && parts[ID(r)].tmp2 <= 0)
|
||||||
|
{
|
||||||
|
kill_part(ID(r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Simulation::CheckStacking()
|
void Simulation::CheckStacking()
|
||||||
{
|
{
|
||||||
bool excessive_stacking_found = false;
|
bool excessive_stacking_found = false;
|
||||||
@ -5205,8 +5261,6 @@ Simulation::Simulation():
|
|||||||
platent = LoadLatent();
|
platent = LoadLatent();
|
||||||
std::copy(GetElements().begin(), GetElements().end(), elements.begin());
|
std::copy(GetElements().begin(), GetElements().end(), elements.begin());
|
||||||
tools = GetTools();
|
tools = GetTools();
|
||||||
grule = LoadGOLRules();
|
|
||||||
gmenu = LoadGOLMenu();
|
|
||||||
|
|
||||||
player.comm = 0;
|
player.comm = 0;
|
||||||
player2.comm = 0;
|
player2.comm = 0;
|
||||||
@ -5217,11 +5271,39 @@ Simulation::Simulation():
|
|||||||
grav->gravity_mask();
|
grav->gravity_mask();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Simulation::CustomGOLData *Simulation::GetCustomGOLByRule(int rule) const
|
||||||
|
{
|
||||||
|
// * Binary search. customGol is already sorted, see SetCustomGOL.
|
||||||
|
auto it = std::lower_bound(customGol.begin(), customGol.end(), rule, [](const CustomGOLData &item, int rule) {
|
||||||
|
return item.rule < rule;
|
||||||
|
});
|
||||||
|
if (it != customGol.end() && !(rule < it->rule))
|
||||||
|
{
|
||||||
|
return &*it;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Simulation::SetCustomGOL(std::vector<CustomGOLData> newCustomGol)
|
||||||
|
{
|
||||||
|
std::sort(newCustomGol.begin(), newCustomGol.end());
|
||||||
|
customGol = newCustomGol;
|
||||||
|
}
|
||||||
|
|
||||||
String Simulation::ElementResolve(int type, int ctype)
|
String Simulation::ElementResolve(int type, int ctype)
|
||||||
{
|
{
|
||||||
if (type == PT_LIFE && ctype >= 0 && ctype < NGOL)
|
if (type == PT_LIFE)
|
||||||
{
|
{
|
||||||
return gmenu[ctype].name;
|
if (ctype >= 0 && ctype < NGOL)
|
||||||
|
{
|
||||||
|
return builtinGol[ctype].name;
|
||||||
|
}
|
||||||
|
auto *cgol = GetCustomGOLByRule(ctype);
|
||||||
|
if (cgol)
|
||||||
|
{
|
||||||
|
return cgol->nameString;
|
||||||
|
}
|
||||||
|
return SerialiseGOLRule(ctype);
|
||||||
}
|
}
|
||||||
else if (type >= 0 && type < PT_NUM)
|
else if (type >= 0 && type < PT_NUM)
|
||||||
{
|
{
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
#include "WallType.h"
|
#include "WallType.h"
|
||||||
#include "Sign.h"
|
#include "Sign.h"
|
||||||
#include "ElementDefs.h"
|
#include "ElementDefs.h"
|
||||||
#include "GOLMenu.h"
|
#include "BuiltinGOL.h"
|
||||||
#include "MenuSection.h"
|
#include "MenuSection.h"
|
||||||
|
|
||||||
#include "CoordStack.h"
|
#include "CoordStack.h"
|
||||||
@ -46,9 +46,6 @@ public:
|
|||||||
std::vector<SimTool> tools;
|
std::vector<SimTool> tools;
|
||||||
std::vector<unsigned int> platent;
|
std::vector<unsigned int> platent;
|
||||||
std::vector<wall_type> wtypes;
|
std::vector<wall_type> wtypes;
|
||||||
std::vector<gol_menu> gmenu;
|
|
||||||
std::vector<int> goltype;
|
|
||||||
std::vector<std::array<int, 10> > grule;
|
|
||||||
std::vector<menu_section> msections;
|
std::vector<menu_section> msections;
|
||||||
|
|
||||||
int currentTick;
|
int currentTick;
|
||||||
@ -83,8 +80,7 @@ public:
|
|||||||
//Gol sim
|
//Gol sim
|
||||||
int CGOL;
|
int CGOL;
|
||||||
int GSPEED;
|
int GSPEED;
|
||||||
unsigned char gol[YRES][XRES];
|
unsigned int gol[YRES][XRES][5];
|
||||||
unsigned short gol2[YRES][XRES][9];
|
|
||||||
//Air sim
|
//Air sim
|
||||||
float (*vx)[XRES/CELL];
|
float (*vx)[XRES/CELL];
|
||||||
float (*vy)[XRES/CELL];
|
float (*vy)[XRES/CELL];
|
||||||
@ -224,6 +220,25 @@ public:
|
|||||||
String ElementResolve(int type, int ctype);
|
String ElementResolve(int type, int ctype);
|
||||||
String BasicParticleInfo(Particle const &sample_part);
|
String BasicParticleInfo(Particle const &sample_part);
|
||||||
|
|
||||||
|
|
||||||
|
struct CustomGOLData
|
||||||
|
{
|
||||||
|
int rule, colour1, colour2;
|
||||||
|
String nameString, ruleString;
|
||||||
|
|
||||||
|
inline bool operator <(const CustomGOLData &other) const
|
||||||
|
{
|
||||||
|
return rule < other.rule;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<CustomGOLData> customGol;
|
||||||
|
|
||||||
|
public:
|
||||||
|
const CustomGOLData *GetCustomGOLByRule(int rule) const;
|
||||||
|
void SetCustomGOL(std::vector<CustomGOLData> newCustomGol);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CoordStack& getCoordStackSingleton();
|
CoordStack& getCoordStackSingleton();
|
||||||
};
|
};
|
||||||
|
@ -4,106 +4,48 @@
|
|||||||
#include "ElementDefs.h"
|
#include "ElementDefs.h"
|
||||||
#include "ElementClasses.h"
|
#include "ElementClasses.h"
|
||||||
|
|
||||||
#include "GOLMenu.h"
|
#include "BuiltinGOL.h"
|
||||||
#include "WallType.h"
|
#include "WallType.h"
|
||||||
#include "MenuSection.h"
|
#include "MenuSection.h"
|
||||||
|
|
||||||
#include "graphics/Renderer.h"
|
#include "graphics/Renderer.h"
|
||||||
|
|
||||||
std::vector<gol_menu> LoadGOLMenu()
|
const BuiltinGOL builtinGol[NGOL] = {
|
||||||
{
|
// * Ruleset:
|
||||||
return
|
// * bits x = 8..0: stay if x neighbours present
|
||||||
std::vector<gol_menu>{
|
// * bits x = 16..9: begin if x-8 neighbours present
|
||||||
{"GOL", PIXPACK(0x0CAC00), 0, String("Game Of Life: Begin 3/Stay 23")},
|
// * bits 20..17: 4-bit unsigned int encoding the number of states minus 2; 2 states is
|
||||||
{"HLIF", PIXPACK(0xFF0000), 1, String("High Life: B36/S23")},
|
// encoded as 0, 3 states as 1, etc.
|
||||||
{"ASIM", PIXPACK(0x0000FF), 2, String("Assimilation: B345/S4567")},
|
// * states are kind of long until a cell dies; normal ones use two states (living and dead),
|
||||||
{"2x2", PIXPACK(0xFFFF00), 3, String("2x2: B36/S125")},
|
// for others the intermediate states live but do nothing
|
||||||
{"DANI", PIXPACK(0x00FFFF), 4, String("Day and Night: B3678/S34678")},
|
// * the ruleset constants below look 20-bit, but rulesets actually consist of 21
|
||||||
{"AMOE", PIXPACK(0xFF00FF), 5, String("Amoeba: B357/S1358")},
|
// bits of data; bit 20 just happens to not be set for any of the built-in types,
|
||||||
{"MOVE", PIXPACK(0xFFFFFF), 6, String("'Move' particles. Does not move things.. it is a life type: B368/S245")},
|
// as none of them have 10 or more states
|
||||||
{"PGOL", PIXPACK(0xE05010), 7, String("Pseudo Life: B357/S238")},
|
{ "GOL", GT_GOL , 0x0080C, PIXPACK(0x0CAC00), PIXPACK(0x0CAC00), NGT_GOL, String("Game Of Life: Begin 3/Stay 23") },
|
||||||
{"DMOE", PIXPACK(0x500000), 8, String("Diamoeba: B35678/S5678")},
|
{ "HLIF", GT_HLIF, 0x0480C, PIXPACK(0xFF0000), PIXPACK(0xFF0000), NGT_HLIF, String("High Life: B36/S23") },
|
||||||
{"34", PIXPACK(0x500050), 9, String("34: B34/S34")},
|
{ "ASIM", GT_ASIM, 0x038F0, PIXPACK(0x0000FF), PIXPACK(0x0000FF), NGT_ASIM, String("Assimilation: B345/S4567") },
|
||||||
{"LLIF", PIXPACK(0x505050), 10, String("Long Life: B345/S5")},
|
{ "2X2", GT_2x2 , 0x04826, PIXPACK(0xFFFF00), PIXPACK(0xFFFF00), NGT_2x2, String("2X2: B36/S125") },
|
||||||
{"STAN", PIXPACK(0x5000FF), 11, String("Stains: B3678/S235678")},
|
{ "DANI", GT_DANI, 0x1C9D8, PIXPACK(0x00FFFF), PIXPACK(0x00FFFF), NGT_DANI, String("Day and Night: B3678/S34678") },
|
||||||
{"SEED", PIXPACK(0xFBEC7D), 12, String("Seeds: B2/S")},
|
{ "AMOE", GT_AMOE, 0x0A92A, PIXPACK(0xFF00FF), PIXPACK(0xFF00FF), NGT_AMOE, String("Amoeba: B357/S1358") },
|
||||||
{"MAZE", PIXPACK(0xA8E4A0), 13, String("Maze: B3/S12345")},
|
{ "MOVE", GT_MOVE, 0x14834, PIXPACK(0xFFFFFF), PIXPACK(0xFFFFFF), NGT_MOVE, String("'Move' particles. Does not move things.. it is a life type: B368/S245") },
|
||||||
{"COAG", PIXPACK(0x9ACD32), 14, String("Coagulations: B378/S235678")},
|
{ "PGOL", GT_PGOL, 0x0A90C, PIXPACK(0xE05010), PIXPACK(0xE05010), NGT_PGOL, String("Pseudo Life: B357/S238") },
|
||||||
{"WALL", PIXPACK(0x0047AB), 15, String("Walled cities: B45678/S2345")},
|
{ "DMOE", GT_DMOE, 0x1E9E0, PIXPACK(0x500000), PIXPACK(0x500000), NGT_DMOE, String("Diamoeba: B35678/S5678") },
|
||||||
{"GNAR", PIXPACK(0xE5B73B), 16, String("Gnarl: B1/S1")},
|
{ "3-4", GT_34 , 0x01818, PIXPACK(0x500050), PIXPACK(0x500050), NGT_34, String("3-4: B34/S34") },
|
||||||
{"REPL", PIXPACK(0x259588), 17, String("Replicator: B1357/S1357")},
|
{ "LLIF", GT_LLIF, 0x03820, PIXPACK(0x505050), PIXPACK(0x505050), NGT_LLIF, String("Long Life: B345/S5") },
|
||||||
{"MYST", PIXPACK(0x0C3C00), 18, String("Mystery: B3458/S05678")},
|
{ "STAN", GT_STAN, 0x1C9EC, PIXPACK(0x5000FF), PIXPACK(0x5000FF), NGT_STAN, String("Stains: B3678/S235678") },
|
||||||
{"LOTE", PIXPACK(0xFF0000), 19, String("Living on the Edge: B37/S3458/4")},
|
{ "SEED", GT_SEED, 0x00400, PIXPACK(0xFBEC7D), PIXPACK(0xFBEC7D), NGT_SEED, String("Seeds: B2/S") },
|
||||||
{"FRG2", PIXPACK(0x00FF00), 20, String("Like Frogs rule: B3/S124/3")},
|
{ "MAZE", GT_MAZE, 0x0083E, PIXPACK(0xA8E4A0), PIXPACK(0xA8E4A0), NGT_MAZE, String("Maze: B3/S12345") },
|
||||||
{"STAR", PIXPACK(0x0000FF), 21, String("Like Star Wars rule: B278/S3456/6")},
|
{ "COAG", GT_COAG, 0x189EC, PIXPACK(0x9ACD32), PIXPACK(0x9ACD32), NGT_COAG, String("Coagulations: B378/S235678") },
|
||||||
{"FROG", PIXPACK(0x00AA00), 22, String("Frogs: B34/S12/3")},
|
{ "WALL", GT_WALL, 0x1F03C, PIXPACK(0x0047AB), PIXPACK(0x0047AB), NGT_WALL, String("Walled cities: B45678/S2345") },
|
||||||
{"BRAN", PIXPACK(0xCCCC00), 23, String("Brian 6: B246/S6/3")}
|
{ "GNAR", GT_GNAR, 0x00202, PIXPACK(0xE5B73B), PIXPACK(0xE5B73B), NGT_GNAR, String("Gnarl: B1/S1") },
|
||||||
};
|
{ "REPL", GT_REPL, 0x0AAAA, PIXPACK(0x259588), PIXPACK(0x259588), NGT_REPL, String("Replicator: B1357/S1357") },
|
||||||
}
|
{ "MYST", GT_MYST, 0x139E1, PIXPACK(0x0C3C00), PIXPACK(0x0C3C00), NGT_MYST, String("Mystery: B3458/S05678") },
|
||||||
|
{ "LOTE", GT_LOTE, 0x48938, PIXPACK(0xFF0000), PIXPACK(0xFFFF00), NGT_LOTE, String("Living on the Edge: B37/S3458/4") },
|
||||||
std::vector<std::array<int, 10> > LoadGOLRules()
|
{ "FRG2", GT_FRG2, 0x20816, PIXPACK(0x006432), PIXPACK(0x00FF5A), NGT_FRG2, String("Like Frogs rule: B3/S124/3") },
|
||||||
{
|
{ "STAR", GT_STAR, 0x98478, PIXPACK(0x000040), PIXPACK(0x0000E6), NGT_STAR, String("Like Star Wars rule: B278/S3456/6") },
|
||||||
return
|
{ "FROG", GT_FROG, 0x21806, PIXPACK(0x006400), PIXPACK(0x00FF00), NGT_FROG, String("Frogs: B34/S12/3") },
|
||||||
std::vector<std::array<int, 10> >{
|
{ "BRAN", GT_BRAN, 0x25440, PIXPACK(0xFFFF00), PIXPACK(0x969600), NGT_BRAN, String("Brian 6: B246/S6/3" )}
|
||||||
// 0,1,2,3,4,5,6,7,8,STATES live=1 spawn=2 spawn&live=3 States are kind of how long until it dies, normal ones use two states(living,dead) for others the intermediate states live but do nothing
|
};
|
||||||
{0,0,0,0,0,0,0,0,0,2},//blank
|
|
||||||
{0,0,1,3,0,0,0,0,0,2},//GOL
|
|
||||||
{0,0,1,3,0,0,2,0,0,2},//HLIF
|
|
||||||
{0,0,0,2,3,3,1,1,0,2},//ASIM
|
|
||||||
{0,1,1,2,0,1,2,0,0,2},//2x2
|
|
||||||
{0,0,0,3,1,0,3,3,3,2},//DANI
|
|
||||||
{0,1,0,3,0,3,0,2,1,2},//AMOE
|
|
||||||
{0,0,1,2,1,1,2,0,2,2},//MOVE
|
|
||||||
{0,0,1,3,0,2,0,2,1,2},//PGOL
|
|
||||||
{0,0,0,2,0,3,3,3,3,2},//DMOE
|
|
||||||
{0,0,0,3,3,0,0,0,0,2},//34
|
|
||||||
{0,0,0,2,2,3,0,0,0,2},//LLIF
|
|
||||||
{0,0,1,3,0,1,3,3,3,2},//STAN
|
|
||||||
{0,0,2,0,0,0,0,0,0,2},//SEED
|
|
||||||
{0,1,1,3,1,1,0,0,0,2},//MAZE
|
|
||||||
{0,0,1,3,0,1,1,3,3,2},//COAG
|
|
||||||
{0,0,1,1,3,3,2,2,2,2},//WALL
|
|
||||||
{0,3,0,0,0,0,0,0,0,2},//GNAR
|
|
||||||
{0,3,0,3,0,3,0,3,0,2},//REPL
|
|
||||||
{1,0,0,2,2,3,1,1,3,2},//MYST
|
|
||||||
{0,0,0,3,1,1,0,2,1,4},//LOTE
|
|
||||||
{0,1,1,2,1,0,0,0,0,3},//FRG2
|
|
||||||
{0,0,2,1,1,1,1,2,2,6},//STAR
|
|
||||||
{0,1,1,2,2,0,0,0,0,3},//FROG
|
|
||||||
{0,0,2,0,2,0,3,0,0,3},//BRAN
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<int> LoadGOLTypes()
|
|
||||||
{
|
|
||||||
return
|
|
||||||
std::vector<int>{
|
|
||||||
GT_GOL,
|
|
||||||
GT_HLIF,
|
|
||||||
GT_ASIM,
|
|
||||||
GT_2x2,
|
|
||||||
GT_DANI,
|
|
||||||
GT_AMOE,
|
|
||||||
GT_MOVE,
|
|
||||||
GT_PGOL,
|
|
||||||
GT_DMOE,
|
|
||||||
GT_34,
|
|
||||||
GT_LLIF,
|
|
||||||
GT_STAN,
|
|
||||||
GT_SEED,
|
|
||||||
GT_MAZE,
|
|
||||||
GT_COAG,
|
|
||||||
GT_WALL,
|
|
||||||
GT_GNAR,
|
|
||||||
GT_REPL,
|
|
||||||
GT_MYST,
|
|
||||||
GT_LOTE,
|
|
||||||
GT_FRG2,
|
|
||||||
GT_STAR,
|
|
||||||
GT_FROG,
|
|
||||||
GT_BRAN,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<wall_type> LoadWalls()
|
std::vector<wall_type> LoadWalls()
|
||||||
{
|
{
|
||||||
|
@ -137,17 +137,13 @@ struct part_type;
|
|||||||
struct part_transition;
|
struct part_transition;
|
||||||
|
|
||||||
struct wall_type;
|
struct wall_type;
|
||||||
struct gol_menu;
|
struct BuiltinGOL;
|
||||||
struct menu_section;
|
struct menu_section;
|
||||||
|
|
||||||
class SimTool;
|
class SimTool;
|
||||||
class Element;
|
class Element;
|
||||||
|
|
||||||
std::vector<gol_menu> LoadGOLMenu();
|
extern const BuiltinGOL builtinGol[];
|
||||||
|
|
||||||
std::vector<int> LoadGOLTypes();
|
|
||||||
|
|
||||||
std::vector<std::array<int, 10> > LoadGOLRules();
|
|
||||||
|
|
||||||
std::vector<wall_type> LoadWalls();
|
std::vector<wall_type> LoadWalls();
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ static int update(UPDATE_FUNC_ARGS)
|
|||||||
parts[i].vx += ADVECTION*sim->vx[y/CELL][x/CELL];
|
parts[i].vx += ADVECTION*sim->vx[y/CELL][x/CELL];
|
||||||
parts[i].vy += ADVECTION*sim->vy[y/CELL][x/CELL];
|
parts[i].vy += ADVECTION*sim->vy[y/CELL][x/CELL];
|
||||||
}
|
}
|
||||||
if (parts[i].ctype<=0 || parts[i].ctype>=PT_NUM || !sim->elements[parts[i].ctype].Enabled || (parts[i].ctype==PT_LIFE && (parts[i].tmp<0 || parts[i].tmp>=NGOL)))
|
if (parts[i].ctype<=0 || parts[i].ctype>=PT_NUM || !sim->elements[parts[i].ctype].Enabled)
|
||||||
{
|
{
|
||||||
int r, rx, ry, rt;
|
int r, rx, ry, rt;
|
||||||
for (rx=-1; rx<2; rx++)
|
for (rx=-1; rx<2; rx++)
|
||||||
|
@ -48,7 +48,7 @@ void Element::Element_CLNE()
|
|||||||
|
|
||||||
static int update(UPDATE_FUNC_ARGS)
|
static int update(UPDATE_FUNC_ARGS)
|
||||||
{
|
{
|
||||||
if (parts[i].ctype<=0 || parts[i].ctype>=PT_NUM || !sim->elements[parts[i].ctype].Enabled || (parts[i].ctype==PT_LIFE && (parts[i].tmp<0 || parts[i].tmp>=NGOL)))
|
if (parts[i].ctype<=0 || parts[i].ctype>=PT_NUM || !sim->elements[parts[i].ctype].Enabled)
|
||||||
{
|
{
|
||||||
int r, rx, ry, rt;
|
int r, rx, ry, rt;
|
||||||
for (rx=-1; rx<2; rx++)
|
for (rx=-1; rx<2; rx++)
|
||||||
|
@ -49,8 +49,8 @@ void Element::Element_CONV()
|
|||||||
static int update(UPDATE_FUNC_ARGS)
|
static int update(UPDATE_FUNC_ARGS)
|
||||||
{
|
{
|
||||||
int r, rx, ry;
|
int r, rx, ry;
|
||||||
int ctype = TYP(parts[i].ctype), ctypeExtra = ID(parts[i].ctype);
|
int ctype = TYP(parts[i].ctype);
|
||||||
if (ctype<=0 || ctype>=PT_NUM || !sim->elements[ctype].Enabled || ctype==PT_CONV || (ctype==PT_LIFE && (ctypeExtra<0 || ctypeExtra>=NGOL)))
|
if (ctype<=0 || ctype>=PT_NUM || !sim->elements[ctype].Enabled || ctype==PT_CONV)
|
||||||
{
|
{
|
||||||
for (rx=-1; rx<2; rx++)
|
for (rx=-1; rx<2; rx++)
|
||||||
for (ry=-1; ry<2; ry++)
|
for (ry=-1; ry<2; ry++)
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
#include "simulation/ElementCommon.h"
|
#include "simulation/ElementCommon.h"
|
||||||
|
|
||||||
bool Element_GOL_colourInit = false;
|
|
||||||
pixel Element_GOL_colour[NGOL];
|
|
||||||
|
|
||||||
static int graphics(GRAPHICS_FUNC_ARGS);
|
static int graphics(GRAPHICS_FUNC_ARGS);
|
||||||
static void create(ELEMENT_CREATE_FUNC_ARGS);
|
static void create(ELEMENT_CREATE_FUNC_ARGS);
|
||||||
|
|
||||||
@ -49,82 +46,59 @@ void Element::Element_LIFE()
|
|||||||
|
|
||||||
Graphics = &graphics;
|
Graphics = &graphics;
|
||||||
Create = &create;
|
Create = &create;
|
||||||
|
|
||||||
if (!Element_GOL_colourInit)
|
|
||||||
{
|
|
||||||
Element_GOL_colourInit = true;
|
|
||||||
|
|
||||||
std::vector<gol_menu> golMenuT = LoadGOLMenu();
|
|
||||||
for(int i = 0; i < NGOL; i++)
|
|
||||||
{
|
|
||||||
Element_GOL_colour[i] = golMenuT[i].colour;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int graphics(GRAPHICS_FUNC_ARGS)
|
static int graphics(GRAPHICS_FUNC_ARGS)
|
||||||
{
|
{
|
||||||
pixel pc;
|
auto colour1 = cpart->dcolour;
|
||||||
if (cpart->ctype==NGT_LOTE)//colors for life states
|
auto colour2 = cpart->tmp;
|
||||||
|
if (!colour1)
|
||||||
{
|
{
|
||||||
if (cpart->tmp==2)
|
colour1 = PIXPACK(0xFFFFFF);
|
||||||
pc = PIXRGB(255, 128, 0);
|
}
|
||||||
else if (cpart->tmp==1)
|
auto ruleset = cpart->ctype;
|
||||||
pc = PIXRGB(255, 255, 0);
|
if (ruleset >= 0 && ruleset < NGOL)
|
||||||
|
{
|
||||||
|
ruleset = builtinGol[ruleset].ruleset;
|
||||||
|
}
|
||||||
|
if (!ren->blackDecorations)
|
||||||
|
{
|
||||||
|
auto states = ((ruleset >> 17) & 0xF) + 2;
|
||||||
|
if (states == 2)
|
||||||
|
{
|
||||||
|
*colr = PIXR(colour1);
|
||||||
|
*colg = PIXG(colour1);
|
||||||
|
*colb = PIXB(colour1);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
pc = PIXRGB(255, 0, 0);
|
{
|
||||||
|
auto mul = (cpart->tmp2 - 1) / float(states - 2);
|
||||||
|
*colr = PIXR(colour1) * mul + PIXR(colour2) * (1.f - mul);
|
||||||
|
*colg = PIXG(colour1) * mul + PIXG(colour2) * (1.f - mul);
|
||||||
|
*colb = PIXB(colour1) * mul + PIXB(colour2) * (1.f - mul);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (cpart->ctype==NGT_FRG2)//colors for life states
|
*pixel_mode |= NO_DECO;
|
||||||
{
|
|
||||||
if (cpart->tmp==2)
|
|
||||||
pc = PIXRGB(0, 100, 50);
|
|
||||||
else
|
|
||||||
pc = PIXRGB(0, 255, 90);
|
|
||||||
}
|
|
||||||
else if (cpart->ctype==NGT_STAR)//colors for life states
|
|
||||||
{
|
|
||||||
if (cpart->tmp==4)
|
|
||||||
pc = PIXRGB(0, 0, 128);
|
|
||||||
else if (cpart->tmp==3)
|
|
||||||
pc = PIXRGB(0, 0, 150);
|
|
||||||
else if (cpart->tmp==2)
|
|
||||||
pc = PIXRGB(0, 0, 190);
|
|
||||||
else if (cpart->tmp==1)
|
|
||||||
pc = PIXRGB(0, 0, 230);
|
|
||||||
else
|
|
||||||
pc = PIXRGB(0, 0, 70);
|
|
||||||
}
|
|
||||||
else if (cpart->ctype==NGT_FROG)//colors for life states
|
|
||||||
{
|
|
||||||
if (cpart->tmp==2)
|
|
||||||
pc = PIXRGB(0, 100, 0);
|
|
||||||
else
|
|
||||||
pc = PIXRGB(0, 255, 0);
|
|
||||||
}
|
|
||||||
else if (cpart->ctype==NGT_BRAN)//colors for life states
|
|
||||||
{
|
|
||||||
if (cpart->tmp==1)
|
|
||||||
pc = PIXRGB(150, 150, 0);
|
|
||||||
else
|
|
||||||
pc = PIXRGB(255, 255, 0);
|
|
||||||
}
|
|
||||||
else if (cpart->ctype >= 0 && cpart->ctype < NGOL)
|
|
||||||
{
|
|
||||||
pc = Element_GOL_colour[cpart->ctype];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
pc = ren->sim->elements[cpart->type].Colour;
|
|
||||||
*colr = PIXR(pc);
|
|
||||||
*colg = PIXG(pc);
|
|
||||||
*colb = PIXB(pc);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void create(ELEMENT_CREATE_FUNC_ARGS)
|
static void create(ELEMENT_CREATE_FUNC_ARGS)
|
||||||
{
|
{
|
||||||
if (v >= 0 && v < NGOL)
|
sim->parts[i].ctype = v & 0x1FFFFF;
|
||||||
|
if (v < NGOL)
|
||||||
{
|
{
|
||||||
sim->parts[i].tmp = sim->grule[v+1][9] - 1;
|
sim->parts[i].dcolour = builtinGol[v].colour;
|
||||||
sim->parts[i].ctype = v;
|
sim->parts[i].tmp = builtinGol[v].colour2;
|
||||||
|
v = builtinGol[v].ruleset;
|
||||||
}
|
}
|
||||||
|
else if (!(v & 0x200000)) // * 0x200000: No need to look for colours, they'll be set later anyway.
|
||||||
|
{
|
||||||
|
auto *cgol = sim->GetCustomGOLByRule(v & 0x1FFFFF);
|
||||||
|
if (cgol)
|
||||||
|
{
|
||||||
|
sim->parts[i].dcolour = cgol->colour1;
|
||||||
|
sim->parts[i].tmp = cgol->colour2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sim->parts[i].tmp2 = ((v >> 17) & 0xF) + 1;
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ static int update(UPDATE_FUNC_ARGS)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (parts[i].ctype<=0 || parts[i].ctype>=PT_NUM || !sim->elements[parts[i].ctype].Enabled || (parts[i].ctype==PT_LIFE && (parts[i].tmp<0 || parts[i].tmp>=NGOL)))
|
if (parts[i].ctype<=0 || parts[i].ctype>=PT_NUM || !sim->elements[parts[i].ctype].Enabled)
|
||||||
for (rx=-1; rx<2; rx++)
|
for (rx=-1; rx<2; rx++)
|
||||||
for (ry=-1; ry<2; ry++)
|
for (ry=-1; ry<2; ry++)
|
||||||
if (BOUNDS_CHECK)
|
if (BOUNDS_CHECK)
|
||||||
|
@ -79,7 +79,7 @@ static int update(UPDATE_FUNC_ARGS)
|
|||||||
parts[i].life = 10;
|
parts[i].life = 10;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (parts[i].ctype<=0 || parts[i].ctype>=PT_NUM || !sim->elements[parts[i].ctype].Enabled || (parts[i].ctype==PT_LIFE && (parts[i].tmp<0 || parts[i].tmp>=NGOL)))
|
if (parts[i].ctype<=0 || parts[i].ctype>=PT_NUM || !sim->elements[parts[i].ctype].Enabled)
|
||||||
for (rx=-1; rx<2; rx++)
|
for (rx=-1; rx<2; rx++)
|
||||||
for (ry=-1; ry<2; ry++)
|
for (ry=-1; ry<2; ry++)
|
||||||
if (BOUNDS_CHECK)
|
if (BOUNDS_CHECK)
|
||||||
|
Loading…
Reference in New Issue
Block a user