Don't mangle custom element types in life, ctype, tmp{,2,3,4}

Achieved by adding a new element property called CarriesTypeIn, whose bits signal to save loading code which properties of particles of the element class in question carry element IDs. The bits in this property are numbered the same way as sim.FIELD_* constants for consistency. One would signal from Lua that a custom element carries element IDs in its tmp like this:

	elem.property(id, "CarriesTypeIn", 2 ^ sim.FIELD_TMP)

"Carrying an element ID in a property" is to be interpreted as follows: the property is treated as a combination of a PMAPBITS-bit (so, currently 9-bit) unsigned integer lower part holding an element ID and a 32-PMAPBITS-bit (so, currently 23-bit) signed integer upper part holding whatever makes sense for the element. CONV, for example, uses this signed integer in its ctype as the extra "v" parameter for particle creation.
This commit is contained in:
Tamás Bálint Misius 2023-02-26 22:40:16 +01:00
parent 2e2c3181b5
commit a13c29875f
No known key found for this signature in database
GPG Key ID: 5B472A12F6ECA9F2
29 changed files with 102 additions and 63 deletions

View File

@ -1250,7 +1250,7 @@ void GameSave::readPSv(const std::vector<char> &dataVec)
char tempSignText[255];
sign tempSign("", 0, 0, sign::Left);
std::vector<Element> elements = GetElements();
auto &elements = GetElements();
//New file header uses PSv, replacing fuC. This is to detect if the client uses a new save format for temperatures
//This creates a problem for old clients, that display and "corrupt" error instead of a "newer version" error
@ -2046,6 +2046,9 @@ std::pair<bool, std::vector<char>> GameSave::serialiseOPS() const
That way, if we ever need a 25th bit, we won't have to change the save format
*/
auto &elements = GetElements();
auto &possiblyCarriesType = Particle::PossiblyCarriesType();
auto &properties = Particle::GetProperties();
// Allocate enough space to store all Particles and 3 bytes on top of that per Particle, for the field descriptors.
// In practice, a Particle will never need as much space in the save as in memory; this is just an upper bound to simplify allocation.
std::vector<unsigned char> partsData(NPART * (sizeof(Particle)+3));
@ -2273,17 +2276,16 @@ std::pair<bool, std::vector<char>> GameSave::serialiseOPS() const
}
if (PMAPBITS > 8)
{
if (TypeInCtype(particles[i].type, particles[i].ctype) && particles[i].ctype > 0xFF)
for (auto index : possiblyCarriesType)
{
RESTRICTVERSION(93, 0);
}
else if (TypeInTmp(particles[i].type) && particles[i].tmp > 0xFF)
{
RESTRICTVERSION(93, 0);
}
else if (TypeInTmp2(particles[i].type, particles[i].tmp2) && particles[i].tmp2 > 0xFF)
{
RESTRICTVERSION(93, 0);
if (elements[particles[i].type].CarriesTypeIn & (1U << index))
{
auto *prop = reinterpret_cast<const int *>(reinterpret_cast<const char *>(&particles[i]) + properties[index].Offset);
if (TYP(*prop) > 0xFF)
{
RESTRICTVERSION(93, 0);
}
}
}
}
if (particles[i].type == PT_LDTC)
@ -2684,26 +2686,6 @@ static void ConvertJsonToBson(bson *b, Json::Value j, int depth)
}
}
bool GameSave::TypeInCtype(int type, int ctype)
{
return ctype >= 0 && ctype < PT_NUM &&
(type == PT_CLNE || type == PT_PCLN || type == PT_BCLN || type == PT_PBCN ||
type == PT_STOR || type == PT_CONV || type == PT_STKM || type == PT_STKM2 ||
type == PT_FIGH || type == PT_LAVA || type == PT_SPRK || type == PT_PSTN ||
type == PT_CRAY || type == PT_DTEC || type == PT_DRAY || type == PT_PIPE ||
type == PT_PPIP || type == PT_LDTC);
}
bool GameSave::TypeInTmp(int type)
{
return type == PT_STOR;
}
bool GameSave::TypeInTmp2(int type, int tmp2)
{
return (type == PT_VIRS || type == PT_VRSG || type == PT_VRSS) && (tmp2 >= 0 && tmp2 < PT_NUM);
}
bool GameSave::PressureInTmp3(int type)
{
return type == PT_QRTZ || type == PT_GLAS || type == PT_TUNG;

View File

@ -141,9 +141,6 @@ public:
void Expand(const std::vector<char> &data);
static bool TypeInCtype(int type, int ctype);
static bool TypeInTmp(int type);
static bool TypeInTmp2(int type, int tmp2);
static bool PressureInTmp3(int type);
GameSave& operator << (Particle &v);

View File

@ -35,6 +35,8 @@ void initLegacyProps()
legacyPropNames.insert(std::pair<ByteString, StructProperty>("menu", prop));
else if (prop.Name == "PhotonReflectWavelengths")
continue;
else if (prop.Name == "CarriesTypeIn")
continue;
else if (prop.Name == "Temperature")
legacyPropNames.insert(std::pair<ByteString, StructProperty>("heat", prop));
else if (prop.Name == "HeatConduct")

View File

@ -32,6 +32,7 @@ Element::Element():
Description("No description"),
Properties(TYPE_SOLID),
CarriesTypeIn(0),
LowPressure(IPL),
LowPressureTransition(NT),
@ -75,6 +76,7 @@ std::vector<StructProperty> const &Element::GetProperties()
{ "Meltable", StructProperty::Integer, offsetof(Element, Meltable ) },
{ "Hardness", StructProperty::Integer, offsetof(Element, Hardness ) },
{ "PhotonReflectWavelengths", StructProperty::UInteger, offsetof(Element, PhotonReflectWavelengths ) },
{ "CarriesTypeIn", StructProperty::UInteger, offsetof(Element, CarriesTypeIn ) },
{ "Weight", StructProperty::Integer, offsetof(Element, Weight ) },
{ "Temperature", StructProperty::Float, offsetof(Element, DefaultProperties.temp ) },
{ "HeatConduct", StructProperty::UChar, offsetof(Element, HeatConduct ) },

View File

@ -38,6 +38,7 @@ public:
unsigned char HeatConduct;
String Description;
unsigned int Properties;
unsigned int CarriesTypeIn;
float LowPressure;
int LowPressureTransition;

View File

@ -1,5 +1,6 @@
#include "Particle.h"
#include <cstddef>
#include <cassert>
std::vector<StructProperty> const &Particle::GetProperties()
{
@ -31,3 +32,31 @@ std::vector<StructPropertyAlias> const &Particle::GetPropertyAliases()
};
return aliases;
}
std::vector<int> const &Particle::PossiblyCarriesType()
{
struct DoOnce
{
std::vector<int> indices = {
FIELD_LIFE,
FIELD_CTYPE,
FIELD_TMP,
FIELD_TMP2,
FIELD_TMP3,
FIELD_TMP4,
};
DoOnce()
{
auto &properties = GetProperties();
for (auto index : indices)
{
// code that depends on PossiblyCarriesType only knows how to set ints
assert(properties[index].Type == StructProperty::Integer ||
properties[index].Type == StructProperty::ParticleType);
}
}
};
static DoOnce doOnce;
return doOnce.indices;
}

View File

@ -18,4 +18,13 @@ struct Particle
by higher-level processes referring to them by name such as Lua or the property tool **/
static std::vector<StructProperty> const &GetProperties();
static std::vector<StructPropertyAlias> const &GetPropertyAliases();
static std::vector<int> const &PossiblyCarriesType();
};
// important: these are indices into the vector returned by Particle::GetProperties, not indices into Particle
constexpr unsigned int FIELD_LIFE = 1;
constexpr unsigned int FIELD_CTYPE = 2;
constexpr unsigned int FIELD_TMP = 9;
constexpr unsigned int FIELD_TMP2 = 10;
constexpr unsigned int FIELD_TMP3 = 11;
constexpr unsigned int FIELD_TMP4 = 12;

View File

@ -65,6 +65,9 @@ int Simulation::Load(const GameSave * originalSave, bool includePressure, int fu
RecalcFreeParticles(false);
auto &possiblyCarriesType = Particle::PossiblyCarriesType();
auto &properties = Particle::GetProperties();
bool doFullScan = false;
for (int n = 0; n < NPART && n < save->particlesCount; n++)
{
@ -89,30 +92,19 @@ int Simulation::Load(const GameSave * originalSave, bool includePressure, int fu
continue;
}
type = partMap[type];
// These store type in ctype, but are special because they store extra information in the bits after type
if (type == PT_CRAY || type == PT_DRAY || type == PT_CONV)
for (auto index : possiblyCarriesType)
{
int ctype = tempPart->ctype & pmapmask;
int extra = tempPart->ctype >> save->pmapbits;
if (ctype >= 0 && ctype < PT_NUM)
ctype = partMap[ctype];
tempPart->ctype = PMAP(extra, ctype);
}
else if (GameSave::TypeInCtype(type, tempPart->ctype))
{
tempPart->ctype = partMap[tempPart->ctype];
}
// also stores extra bits past type (only STOR right now)
if (GameSave::TypeInTmp(type))
{
int tmp = tempPart->tmp & pmapmask;
int extra = tempPart->tmp >> save->pmapbits;
tmp = partMap[TYP(tmp)];
tempPart->tmp = PMAP(extra, tmp);
}
if (GameSave::TypeInTmp2(type, tempPart->tmp2))
{
tempPart->tmp2 = partMap[tempPart->tmp2];
if (elements[type].CarriesTypeIn & (1U << index))
{
auto *prop = reinterpret_cast<int *>(reinterpret_cast<char *>(tempPart) + properties[index].Offset);
auto carriedType = *prop & int(pmapmask);
auto extra = *prop >> save->pmapbits;
if (carriedType >= 0 && carriedType < PT_NUM)
{
carriedType = partMap[carriedType];
}
*prop = PMAP(extra, carriedType);
}
}
// Ensure we can spawn this element
@ -377,6 +369,8 @@ GameSave * Simulation::Save(bool includePressure, int fullX, int fullY, int full
blockH = blockY2-blockY;
GameSave * newSave = new GameSave(blockW, blockH);
auto &possiblyCarriesType = Particle::PossiblyCarriesType();
auto &properties = Particle::GetProperties();
int storedParts = 0;
int elementCount[PT_NUM];
@ -403,12 +397,14 @@ GameSave * Simulation::Save(bool includePressure, int fullX, int fullY, int full
elementCount[tempPart.type]++;
paletteSet.insert(tempPart.type);
if (GameSave::TypeInCtype(tempPart.type, tempPart.ctype))
paletteSet.insert(tempPart.ctype);
if (GameSave::TypeInTmp(tempPart.type))
paletteSet.insert(TYP(tempPart.tmp));
if (GameSave::TypeInTmp2(tempPart.type, tempPart.tmp2))
paletteSet.insert(tempPart.tmp2);
for (auto index : possiblyCarriesType)
{
if (elements[tempPart.type].CarriesTypeIn & (1U << index))
{
auto *prop = reinterpret_cast<const int *>(reinterpret_cast<const char *>(&tempPart) + properties[index].Offset);
paletteSet.insert(TYP(*prop));
}
}
}
}
}

View File

@ -32,6 +32,7 @@ void Element::Element_BCLN()
Description = "Breakable Clone.";
Properties = TYPE_SOLID | PROP_LIFE_DEC | PROP_LIFE_KILL_DEC | PROP_NOCTYPEDRAW;
CarriesTypeIn = 1U << FIELD_CTYPE;
LowPressure = IPL;
LowPressureTransition = NT;

View File

@ -32,6 +32,7 @@ void Element::Element_CLNE()
Description = "Clone. Duplicates any particles it touches.";
Properties = TYPE_SOLID | PROP_NOCTYPEDRAW;
CarriesTypeIn = 1U << FIELD_CTYPE;
LowPressure = IPL;
LowPressureTransition = NT;

View File

@ -32,6 +32,7 @@ void Element::Element_CONV()
Description = "Converter. Converts everything into whatever it first touches.";
Properties = TYPE_SOLID | PROP_NOCTYPEDRAW;
CarriesTypeIn = (1U << FIELD_CTYPE) | (1U << FIELD_TMP);
LowPressure = IPL;
LowPressureTransition = NT;

View File

@ -34,6 +34,7 @@ void Element::Element_CRAY()
Description = "Particle Ray Emitter. Creates a beam of particles set by its ctype, with a range set by tmp.";
Properties = TYPE_SOLID;
CarriesTypeIn = 1U << FIELD_CTYPE;
LowPressure = IPL;
LowPressureTransition = NT;

View File

@ -32,6 +32,7 @@ void Element::Element_DRAY()
Description = "Duplicator ray. Replicates a line of particles in front of it.";
Properties = TYPE_SOLID;
CarriesTypeIn = 1U << FIELD_CTYPE;
LowPressure = IPL;
LowPressureTransition = NT;

View File

@ -32,6 +32,7 @@ void Element::Element_DTEC()
Description = "Detector, creates a spark when something with its ctype is nearby.";
Properties = TYPE_SOLID;
CarriesTypeIn = 1U << FIELD_CTYPE;
LowPressure = IPL;
LowPressureTransition = NT;

View File

@ -43,6 +43,7 @@ void Element::Element_FIGH()
Description = "Fighter. Tries to kill stickmen. You must first give it an element to kill him with.";
Properties = PROP_NOCTYPEDRAW;
CarriesTypeIn = 1U << FIELD_CTYPE;
LowPressure = IPL;
LowPressureTransition = NT;

View File

@ -36,6 +36,7 @@ void Element::Element_LAVA()
Description = "Molten lava. Ignites flammable materials. Generated when metals and other materials melt, solidifies when cold.";
Properties = TYPE_LIQUID|PROP_LIFE_DEC;
CarriesTypeIn = 1U << FIELD_CTYPE;
LowPressure = IPL;
LowPressureTransition = NT;

View File

@ -33,6 +33,7 @@ void Element::Element_LDTC()
Description = "Linear detector. Scans in 8 directions for particles with its ctype and creates a spark on the opposite side.";
Properties = TYPE_SOLID | PROP_NOCTYPEDRAW;
CarriesTypeIn = 1U << FIELD_CTYPE;
LowPressure = IPL;
LowPressureTransition = NT;

View File

@ -34,6 +34,7 @@ void Element::Element_PBCN()
Description = "Powered breakable clone.";
Properties = TYPE_SOLID | PROP_NOCTYPEDRAW;
CarriesTypeIn = 1U << FIELD_CTYPE;
LowPressure = IPL;
LowPressureTransition = NT;

View File

@ -34,6 +34,7 @@ void Element::Element_PCLN()
Description = "Powered clone. When activated, duplicates any particles it touches.";
Properties = TYPE_SOLID | PROP_NOCTYPEDRAW;
CarriesTypeIn = 1U << FIELD_CTYPE;
LowPressure = IPL;
LowPressureTransition = NT;

View File

@ -39,6 +39,7 @@ void Element::Element_PIPE()
Description = "PIPE, moves particles around. Once the BRCK generates, erase some for the exit. Then the PIPE generates and is usable.";
Properties = TYPE_SOLID|PROP_LIFE_DEC;
CarriesTypeIn = 1U << FIELD_CTYPE;
LowPressure = IPL;
LowPressureTransition = NT;

View File

@ -34,6 +34,7 @@ void Element::Element_PPIP()
Description = "Powered version of PIPE, use PSCN/NSCN to Activate/Deactivate.";
Properties = TYPE_SOLID|PROP_LIFE_DEC;
CarriesTypeIn = 1U << FIELD_CTYPE;
LowPressure = IPL;
LowPressureTransition = NT;

View File

@ -39,6 +39,7 @@ void Element::Element_PSTN()
Description = "Piston, extends and pushes particles.";
Properties = TYPE_SOLID;
CarriesTypeIn = 1U << FIELD_CTYPE;
LowPressure = IPL;
LowPressureTransition = NT;

View File

@ -35,6 +35,7 @@ void Element::Element_SPRK()
Description = "Electricity. The basis of all electronics in TPT, travels along wires and other conductive elements.";
Properties = TYPE_SOLID|PROP_LIFE_DEC;
CarriesTypeIn = 1U << FIELD_CTYPE;
LowPressure = IPL;
LowPressureTransition = NT;

View File

@ -42,6 +42,7 @@ void Element::Element_STKM()
Description = "Stickman. Don't kill him! Control with the arrow keys.";
Properties = PROP_NOCTYPEDRAW;
CarriesTypeIn = 1U << FIELD_CTYPE;
LowPressure = IPL;
LowPressureTransition = NT;

View File

@ -40,6 +40,7 @@ void Element::Element_STKM2()
Description = "Second stickman. Don't kill him! Control with wasd.";
Properties = PROP_NOCTYPEDRAW;
CarriesTypeIn = 1U << FIELD_CTYPE;
LowPressure = IPL;
LowPressureTransition = NT;

View File

@ -35,6 +35,7 @@ void Element::Element_STOR()
Description = "Storage. Captures and stores a single particle. Releases when charged with PSCN, also passes to PIPE.";
Properties = TYPE_SOLID | PROP_NOCTYPEDRAW;
CarriesTypeIn = (1U << FIELD_CTYPE) | (1U << FIELD_TMP);
LowPressure = IPL;
LowPressureTransition = NT;

View File

@ -34,6 +34,7 @@ void Element::Element_VIRS()
Description = "Virus. Turns everything it touches into virus.";
Properties = TYPE_LIQUID|PROP_DEADLY;
CarriesTypeIn = 1U << FIELD_TMP2;
LowPressure = IPL;
LowPressureTransition = NT;

View File

@ -34,6 +34,7 @@ void Element::Element_VRSG()
Description = "Gas Virus. Turns everything it touches into virus.";
Properties = TYPE_GAS|PROP_DEADLY;
CarriesTypeIn = 1U << FIELD_TMP2;
LowPressure = IPL;
LowPressureTransition = NT;

View File

@ -34,6 +34,7 @@ void Element::Element_VRSS()
Description = "Solid Virus. Turns everything it touches into virus.";
Properties = TYPE_SOLID|PROP_DEADLY;
CarriesTypeIn = 1U << FIELD_TMP2;
LowPressure = IPL;
LowPressureTransition = NT;