Changes to save format and PIPE

Update save format to optionally store type as two bits
PIPE now stores element in ctype
Disallow uploading saves using two bytes in type or other fields
update save format to store pmapbits and automatically convert data
This commit is contained in:
jacob1 2017-12-31 23:55:41 -05:00
parent 0c8c4de125
commit b5159ab74e
9 changed files with 225 additions and 98 deletions

View File

@ -34,9 +34,9 @@
#define MOD_ID 0
#endif
#ifdef SNAPSHOT
#define FUTURE_SAVE_VERSION 92
#define FUTURE_MINOR_VERSION 1
#if defined(SNAPSHOT) || defined(DEBUG)
#define FUTURE_SAVE_VERSION 93
#define FUTURE_MINOR_VERSION 0
#endif
//VersionInfoEnd

View File

@ -1021,10 +1021,10 @@ RequestStatus Client::UploadSave(SaveInfo & save)
lastError = "Cannot serialize game save";
return RequestFailure;
}
#ifdef SNAPSHOT
else if (save.gameSave->fromNewerVersion && save.GetPublished())
#if defined(SNAPSHOT) || defined(DEBUG)
else if (save.gameSave->fromNewerVersion)
{
lastError = "Cannot publish save";
lastError = "Cannot upload save, incompatible with latest release version";
return RequestFailure;
}
#endif

View File

@ -28,6 +28,7 @@ GameSave::GameSave(GameSave & save):
edgeMode(save.edgeMode),
signs(save.signs),
palette(save.palette),
pmapbits(save.pmapbits),
expanded(save.expanded),
hasOriginalData(save.hasOriginalData),
originalData(save.originalData)
@ -141,6 +142,7 @@ GameSave::GameSave(char * data, int dataSize)
//Collapse();
}
// Called on every new GameSave, including the copy constructor
void GameSave::InitData()
{
blockMap = NULL;
@ -157,6 +159,7 @@ void GameSave::InitData()
authors.clear();
}
// Called on every new GameSave, except the copy constructor
void GameSave::InitVars()
{
waterEEnabled = false;
@ -168,6 +171,7 @@ void GameSave::InitVars()
airMode = 0;
edgeMode = 0;
translated.x = translated.y = 0;
pmapbits = 8; // default to 8 bits for older saves
}
bool GameSave::Collapsed()
@ -563,6 +567,7 @@ void GameSave::readOPS(char * data, int dataLength)
unsigned partsCount = 0;
unsigned int blockX, blockY, blockW, blockH, fullX, fullY, fullW, fullH;
int savedVersion = inputData[4];
bool fakeNewerVersion = false; // used for development builds only
bson b;
b.data = NULL;
@ -649,6 +654,7 @@ void GameSave::readOPS(char * data, int dataLength)
CheckBsonFieldInt(iter, "gravityMode", &gravityMode);
CheckBsonFieldInt(iter, "airMode", &airMode);
CheckBsonFieldInt(iter, "edgeMode", &edgeMode);
CheckBsonFieldInt(iter, "pmapbits", &pmapbits);
if (!strcmp(bson_iterator_key(&iter), "signs"))
{
if (bson_iterator_type(&iter)==BSON_ARRAY)
@ -739,7 +745,7 @@ void GameSave::readOPS(char * data, int dataLength)
fprintf(stderr, "Wrong type for %s\n", bson_iterator_key(&iter));
}
}
#ifdef SNAPSHOT
#if defined(SNAPSHOT) || defined(DEBUG)
if (major > FUTURE_SAVE_VERSION || (major == FUTURE_SAVE_VERSION && minor > FUTURE_MINOR_VERSION))
#else
if (major > SAVE_VERSION || (major == SAVE_VERSION && minor > MINOR_VERSION))
@ -749,6 +755,10 @@ void GameSave::readOPS(char * data, int dataLength)
errorMessage << "Save from a newer version: Requires version " << major << "." << minor;
throw ParseException(ParseException::WrongVersion, errorMessage.str());
}
#if defined(SNAPSHOT) || defined(DEBUG)
else if (major > SAVE_VERSION || (major == SAVE_VERSION && minor > MINOR_VERSION))
fakeNewerVersion = true;
#endif
}
else
{
@ -956,6 +966,10 @@ void GameSave::readOPS(char * data, int dataLength)
particles[newIndex].y = y;
i+=3;
// Read type (2nd byte)
if (fieldDescriptor & 0x4000)
particles[newIndex].type |= (((unsigned)partsData[i++]) << 8);
//Read temp
if(fieldDescriptor & 0x01)
{
@ -1185,6 +1199,16 @@ void GameSave::readOPS(char * data, int dataLength)
}
}
break;
case PT_PIPE:
case PT_PPIP:
if (savedVersion < 93 && !fakeNewerVersion)
{
if (particles[newIndex].ctype == 1)
particles[newIndex].tmp |= 0x00020000; //PFLAG_INITIALIZING
particles[newIndex].tmp |= (particles[newIndex].ctype-1)<<18;
particles[newIndex].ctype = particles[newIndex].tmp&0xFF;
}
break;
}
//note: PSv was used in version 77.0 and every version before, add something in PSv too if the element is that old
newIndex++;
@ -1841,6 +1865,14 @@ void GameSave::readPSv(char * saveDataChar, int dataLength)
}
}
}
// Version 93.0
if (particles[i-1].type == PT_PIPE || particles[i-1].type == PT_PPIP)
{
if (particles[i-1].ctype == 1)
particles[i-1].tmp |= 0x00020000; //PFLAG_INITIALIZING
particles[i-1].tmp |= (particles[i-1].ctype-1)<<18;
particles[i-1].ctype = particles[i-1].tmp&0xFF;
}
}
}
@ -2020,9 +2052,11 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
//Copy parts data
/* Field descriptor format:
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| pavg | tmp[3+4] | tmp2[2] | tmp2 | ctype[2] | vy | vx | dcololour | ctype[1] | tmp[2] | tmp[1] | life[2] | life[1] | temp dbl len|
| 0 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| RESERVED | type[2] | pavg | tmp[3+4] | tmp2[2] | tmp2 | ctype[2] | vy | vx | decorations | ctype[1] | tmp[2] | tmp[1] | life[2] | life[1] | temp dbl len |
life[2] means a second byte (for a 16 bit field) if life[1] is present
last bit is reserved. If necessary, use it to signify that fieldDescriptor will have another byte
That way, if we ever need a 17th bit, we won't have to change the save format
*/
auto partsData = std::unique_ptr<unsigned char[]>(new unsigned char[NPART * (sizeof(Particle)+1)]);
unsigned int partsDataLen = 0;
@ -2057,6 +2091,15 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
fieldDescLoc = partsDataLen++;
partsDataLen++;
// Extra type byte if necessary
if (particles[i].type & 0xFF00)
{
partsData[partsDataLen++] = particles[i].type >> 8;
fieldDesc |= 1 << 14;
RESTRICTVERSION(93, 0);
fromNewerVersion = true; // TODO: remove on 93.0 release
}
//Extra Temperature (2nd byte optional, 1st required), 1 to 2 bytes
//Store temperature as an offset of 21C(294.15K) or go into a 16byte int and store the whole thing
if(fabs(particles[i].temp-294.15f)<127)
@ -2193,12 +2236,33 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
|| particles[i].type == PT_RFRG || particles[i].type == PT_RFGL || particles[i].type == PT_LSNS)
{
RESTRICTVERSION(92, 0);
fromNewerVersion = true;
}
else if ((particles[i].type == PT_FRAY || particles[i].type == PT_INVIS) && particles[i].tmp)
{
RESTRICTVERSION(92, 0);
fromNewerVersion = true;
}
else if (particles[i].type == PT_PIPE || particles[i].type == PT_PPIP)
{
RESTRICTVERSION(93, 0);
fromNewerVersion = true; // TODO: remove on 93.0 release
}
if (PMAPBITS > 8)
{
if (Simulation::TypeInCtype(particles[i].type) && particles[i].ctype > 0xFF)
{
RESTRICTVERSION(93, 0);
fromNewerVersion = true; // TODO: remove on 93.0 release
}
else if (Simulation::TypeInTmp(particles[i].type) && particles[i].tmp > 0xFF)
{
RESTRICTVERSION(93, 0);
fromNewerVersion = true; // TODO: remove on 93.0 release
}
else if (Simulation::TypeInTmp2(particles[i].type) && particles[i].tmp2 > 0xFF)
{
RESTRICTVERSION(93, 0);
fromNewerVersion = true; // TODO: remove on 93.0 release
}
}
//Get the pmap entry for the next particle in the same position
@ -2286,9 +2350,7 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
bson_append_int(&b, "airMode", airMode);
bson_append_int(&b, "edgeMode", edgeMode);
//bson_append_int(&b, "leftSelectedElement", sl);
//bson_append_int(&b, "rightSelectedElement", sr);
//bson_append_int(&b, "activeMenu", active_menu);
bson_append_int(&b, "pmapbits", pmapbits);
if (partsData && partsDataLen)
{
bson_append_binary(&b, "parts", BSON_BIN_USER, (const char *)partsData.get(), partsDataLen);

View File

@ -78,6 +78,8 @@ public:
// author information
Json::Value authors;
int pmapbits;
GameSave();
GameSave(GameSave & save);
GameSave(int width, int height);

View File

@ -2292,8 +2292,6 @@ void GameView::OnDraw()
if (type)
{
int ctype = sample.particle.ctype;
if (type == PT_PIPE || type == PT_PPIP)
ctype = TYP(sample.particle.tmp);
if (type == PT_PHOT || type == PT_BIZR || type == PT_BIZRG || type == PT_BIZRS || type == PT_FILT || type == PT_BRAY || type == PT_C5)
wavelengthGfx = (ctype&0x3FFFFFFF);
@ -2322,7 +2320,7 @@ void GameView::OnDraw()
sampleInfo << " (" << ctype << ")";
// 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)
sampleInfo << " (" << c->ElementResolve(TYP(type), ID(ctype)) << ")";
sampleInfo << " (" << c->ElementResolve(TYP(ctype), ID(ctype)) << ")";
else if (c->IsValidElement(ctype))
sampleInfo << " (" << c->ElementResolve(ctype, -1) << ")";
else

View File

@ -51,7 +51,7 @@
#define OLD_PT_WIND 147
// Change this to change the amount of bits used to store type in pmap (and a few elements such as PIPE and CRAY)
#define PMAPBITS 8
#define PMAPBITS 9
#define PMAPMASK ((1<<PMAPBITS)-1)
#define ID(r) ((r)>>PMAPBITS)
#define TYP(r) ((r)&PMAPMASK)

View File

@ -33,7 +33,7 @@ int Simulation::Load(GameSave * save, bool includePressure)
int Simulation::Load(int fullX, int fullY, GameSave * save, bool includePressure)
{
int blockX, blockY, x, y, r;
int x, y, r;
if (!save)
return 1;
@ -47,10 +47,11 @@ int Simulation::Load(int fullX, int fullY, GameSave * save, bool includePressure
}
//Align to blockMap
blockX = (fullX + CELL/2)/CELL;
blockY = (fullY + CELL/2)/CELL;
int blockX = (fullX + CELL/2)/CELL;
int blockY = (fullY + CELL/2)/CELL;
fullX = blockX*CELL;
fullY = blockY*CELL;
unsigned int pmapmask = (1<<save->pmapbits)-1;
int partMap[PT_NUM];
for(int i = 0; i < PT_NUM; i++)
@ -102,16 +103,27 @@ int Simulation::Load(int fullX, int fullY, GameSave * save, bool includePressure
if (!elements[tempPart.type].Enabled)
continue;
if (tempPart.ctype > 0 && tempPart.ctype < PT_NUM)
if (tempPart.type == PT_CLNE || tempPart.type == PT_PCLN || tempPart.type == PT_BCLN || tempPart.type == PT_PBCN || tempPart.type == PT_STOR || tempPart.type == PT_CONV || tempPart.type == PT_STKM || tempPart.type == PT_STKM2 || tempPart.type == PT_FIGH || tempPart.type == PT_LAVA || tempPart.type == PT_SPRK || tempPart.type == PT_PSTN || tempPart.type == PT_CRAY || tempPart.type == PT_DTEC || tempPart.type == PT_DRAY)
// These store type in ctype, but are special because they store extra information in the bits after type
if (tempPart.type == PT_CRAY || tempPart.type == PT_DRAY || tempPart.type == PT_CONV)
{
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 (tempPart.ctype > 0 && tempPart.ctype < PT_NUM && TypeInCtype(tempPart.type))
{
tempPart.ctype = partMap[tempPart.ctype];
}
if (tempPart.type == PT_PIPE || tempPart.type == PT_PPIP || tempPart.type == PT_STOR)
// also stores extra bits past type (only STOR right now)
if (TypeInTmp(tempPart.type))
{
tempPart.tmp = partMap[TYP(tempPart.tmp)] | (tempPart.tmp&~PMAPMASK);
int tmp = tempPart.tmp & pmapmask;
int extra = tempPart.tmp >> save->pmapbits;
tempPart.tmp = PMAP(extra, tmp);
}
if (tempPart.type == PT_VIRS || tempPart.type == PT_VRSG || tempPart.type == PT_VRSS)
if (TypeInTmp2(tempPart.type))
{
if (tempPart.tmp2 > 0 && tempPart.tmp2 < PT_NUM)
tempPart.tmp2 = partMap[tempPart.tmp2];
@ -145,30 +157,27 @@ int Simulation::Load(int fullX, int fullY, GameSave * save, bool includePressure
elementCount[tempPart.type]++;
}
if (parts[i].type == PT_STKM)
switch (parts[i].type)
{
case PT_STKM:
Element_STKM::STKM_init_legs(this, &player, i);
player.spwn = 1;
player.elem = PT_DUST;
player.rocketBoots = false;
}
else if (parts[i].type == PT_STKM2)
{
break;
case PT_STKM2:
Element_STKM::STKM_init_legs(this, &player2, i);
player2.spwn = 1;
player2.elem = PT_DUST;
player2.rocketBoots = false;
}
else if (parts[i].type == PT_SPAWN)
{
break;
case PT_SPAWN:
player.spawnID = i;
}
else if (parts[i].type == PT_SPAWN2)
{
break;
case PT_SPAWN2:
player2.spawnID = i;
}
else if (parts[i].type == PT_FIGH)
{
break;
case PT_FIGH:
for (int fcount = 0; fcount < MAX_FIGHTERS; fcount++)
{
if(!fighters[fcount].spwn)
@ -182,11 +191,28 @@ int Simulation::Load(int fullX, int fullY, GameSave * save, bool includePressure
break;
}
}
}
else if (parts[i].type == PT_SOAP)
{
break;
case PT_SOAP:
soapList.insert(std::pair<unsigned int, unsigned int>(n, i));
break;
}
/*if (save->pmapbits != PMAPBITS)
{
unsigned int pmapmask = (1<<save->pmapbits)-1;
if (parts[i].type == PT_CRAY || parts[i].type == PT_DRAY || parts[i].type == PT_CONV)
{
int type = parts[i].ctype & pmapmask;
int data = parts[i].ctype >> save->pmapbits;
parts[i].ctype = PMAP(data, type);
}
else if (parts[i].type == PT_PIPE || parts[i].type == PT_PPIP)
{
int type = parts[i].tmp & pmapmask;
int data = parts[i].tmp >> save->pmapbits;
parts[i].tmp = PMAP(data, type);
}
}*/
}
parts_lastActiveIndex = NPART-1;
force_stacking_check = true;
@ -256,6 +282,25 @@ int Simulation::Load(int fullX, int fullY, GameSave * save, bool includePressure
return 0;
}
bool Simulation::TypeInCtype(int el)
{
return el == PT_CLNE || el == PT_PCLN || el == PT_BCLN || el == PT_PBCN ||
el == PT_STOR || el == PT_CONV || el == PT_STKM || el == PT_STKM2 ||
el == PT_FIGH || el == PT_LAVA || el == PT_SPRK || el == PT_PSTN ||
el == PT_CRAY || el == PT_DTEC || el == PT_DRAY || el == PT_PIPE ||
el == PT_PPIP;
}
bool Simulation::TypeInTmp(int el)
{
return el == PT_STOR;
}
bool Simulation::TypeInTmp2(int el)
{
return el == PT_VIRS || el == PT_VRSG || el == PT_VRSS;
}
GameSave * Simulation::Save(bool includePressure)
{
return Save(0, 0, XRES-1, YRES-1, includePressure);
@ -390,6 +435,7 @@ GameSave * Simulation::Save(int fullX, int fullY, int fullX2, int fullY2, bool i
}
SaveSimOptions(newSave);
newSave->pmapbits = PMAPBITS;
return newSave;
}
@ -2983,7 +3029,7 @@ void Simulation::part_change_type(int i, int x, int y, int t)//changes the type
}
//the function for creating a particle, use p=-1 for creating a new particle, -2 is from a brush, or a particle number to replace a particle.
//tv = Type (8 bits) + Var (24 bits), var is usually 0
//tv = Type (PMAPBITS bits) + Var (32-PMAPBITS bits), var is usually 0
int Simulation::create_part(int p, int x, int y, int t, int v)
{
int i;
@ -4503,9 +4549,9 @@ killed:
}
r = pmap[fin_y][fin_x];
if ((TYP(r)==PT_PIPE || TYP(r) == PT_PPIP) && !TYP(parts[ID(r)].tmp))
if ((TYP(r)==PT_PIPE || TYP(r) == PT_PPIP) && !TYP(parts[ID(r)].ctype))
{
parts[ID(r)].tmp = (parts[ID(r)].tmp&~PMAPMASK) | parts[i].type;
parts[ID(r)].ctype = parts[i].type;
parts[ID(r)].temp = parts[i].temp;
parts[ID(r)].tmp2 = parts[i].life;
parts[ID(r)].pavg[0] = parts[i].tmp;

View File

@ -216,7 +216,13 @@ public:
return (x>=0 && y>=0 && x<XRES && y<YRES);
}
// these don't really belong anywhere at the moment, so go here for loop edge mode
// Element IDs can be stored in other properties
// These functions return true if an element stores a particle type in a property
static bool TypeInCtype(int el);
static bool TypeInTmp(int el);
static bool TypeInTmp2(int el);
// These don't really belong anywhere at the moment, so go here for loop edge mode
static int remainder_p(int x, int y)
{
return (x % y) + (x>=0 ? 0 : y);

View File

@ -50,22 +50,27 @@ Element_PIPE::Element_PIPE()
memset(&tpart, 0, sizeof(Particle));
}
#define PFLAG_NORMALSPEED 0x00010000
// parts[].tmp flags
// trigger flags to be processed this frame (trigger flags for next frame are shifted 3 bits to the left):
#define PPIP_TMPFLAG_TRIGGER_ON 0x10000000
#define PPIP_TMPFLAG_TRIGGER_OFF 0x08000000
#define PPIP_TMPFLAG_TRIGGER_REVERSE 0x04000000
#define PPIP_TMPFLAG_TRIGGERS 0x1C000000
// current status of the pipe
#define PPIP_TMPFLAG_PAUSED 0x02000000
#define PPIP_TMPFLAG_REVERSED 0x01000000
// 0x000000FF element
// 0x00000100 is single pixel pipe
// 0x00000200 will transfer like a single pixel pipe when in forward mode
// 0x00001C00 forward single pixel pipe direction
// 0x00002000 will transfer like a single pixel pipe when in reverse mode
// 0x0001C000 reverse single pixel pipe direction
// 0x00060000 PIPE color data stored here
#define PFLAG_NORMALSPEED 0x00010000
#define PFLAG_INITIALIZING 0x00020000 // colors haven't been set yet
#define PFLAG_COLOR_RED 0x00040000
#define PFLAG_COLOR_GREEN 0x00080000
#define PFLAG_COLOR_BLUE 0x000C0000
#define PFLAG_COLORS 0x000C0000
#define PPIP_TMPFLAG_REVERSED 0x01000000
#define PPIP_TMPFLAG_PAUSED 0x02000000
#define PPIP_TMPFLAG_TRIGGER_REVERSE 0x04000000
#define PPIP_TMPFLAG_TRIGGER_OFF 0x08000000
#define PPIP_TMPFLAG_TRIGGER_ON 0x10000000
#define PPIP_TMPFLAG_TRIGGERS 0x1C000000
signed char pos_1_rx[] = {-1,-1,-1, 0, 0, 1, 1, 1};
signed char pos_1_ry[] = {-1, 0, 1,-1, 1,-1, 0, 1};
@ -75,8 +80,8 @@ int Element_PIPE::update(UPDATE_FUNC_ARGS)
{
int r, rx, ry, np;
int rnd, rndstore;
if (TYP(parts[i].tmp)>=PT_NUM || !sim->elements[TYP(parts[i].tmp)].Enabled)
parts[i].tmp &= ~PMAPMASK;
if (parts[i].ctype && !sim->elements[TYP(parts[i].ctype)].Enabled)
parts[i].ctype = 0;
if (parts[i].tmp & PPIP_TMPFLAG_TRIGGERS)
{
int pause_changed = 0;
@ -115,10 +120,9 @@ int Element_PIPE::update(UPDATE_FUNC_ARGS)
if (parts[i].tmp & PPIP_TMPFLAG_TRIGGER_REVERSE)
{
parts[i].tmp ^= PPIP_TMPFLAG_REVERSED;
if (parts[i].ctype == 2) //Switch colors so it goes in reverse
parts[i].ctype = 4;
else if (parts[i].ctype == 4)
parts[i].ctype = 2;
// Switch colors so it goes in reverse
if ((parts[i].tmp&PFLAG_COLORS) != PFLAG_COLOR_GREEN)
parts[i].tmp ^= PFLAG_COLOR_GREEN;
if (parts[i].tmp & 0x100) //Switch one pixel pipe direction
{
int coords = (parts[i].tmp>>13)&0xF;
@ -131,7 +135,7 @@ int Element_PIPE::update(UPDATE_FUNC_ARGS)
parts[i].tmp &= ~PPIP_TMPFLAG_TRIGGERS;
}
if (parts[i].ctype>=2 && parts[i].ctype<=4 && !(parts[i].tmp & PPIP_TMPFLAG_PAUSED))
if ((parts[i].tmp&PFLAG_COLORS) && !(parts[i].tmp & PPIP_TMPFLAG_PAUSED))
{
if (parts[i].life==3)
{
@ -146,9 +150,13 @@ int Element_PIPE::update(UPDATE_FUNC_ARGS)
r = pmap[y+ry][x+rx];
if (!r)
continue;
if ((TYP(r)==PT_PIPE || TYP(r) == PT_PPIP)&&parts[ID(r)].ctype==1)
if (TYP(r) != PT_PIPE && TYP(r) != PT_PPIP)
continue;
unsigned int nextColor = (((((parts[i].tmp&PFLAG_COLORS)>>18)+1)%3)+1)<<18;
if (parts[ID(r)].tmp&PFLAG_INITIALIZING)
{
parts[ID(r)].ctype = (((parts[i].ctype)%3)+2);//reverse
parts[ID(r)].tmp |= nextColor;
parts[ID(r)].tmp &= ~PFLAG_INITIALIZING;
parts[ID(r)].life = 6;
if (parts[i].tmp&0x100)//is a single pixel pipe
{
@ -160,7 +168,7 @@ int Element_PIPE::update(UPDATE_FUNC_ARGS)
neighborcount ++;
lastneighbor = ID(r);
}
else if ((TYP(r)==PT_PIPE || TYP(r) == PT_PPIP)&&parts[ID(r)].ctype!=(((parts[i].ctype-1)%3)+2))
else if ((parts[ID(r)].tmp&PFLAG_COLORS) != nextColor)
{
neighborcount ++;
lastneighbor = ID(r);
@ -193,23 +201,23 @@ int Element_PIPE::update(UPDATE_FUNC_ARGS)
r = pmap[y+ry][x+rx];
if(!r)
r = sim->photons[y+ry][x+rx];
if (surround_space && !r && TYP(parts[i].tmp)!=0) //creating at end
if (surround_space && !r && TYP(parts[i].ctype)) //creating at end
{
np = sim->create_part(-1, x+rx, y+ry, TYP(parts[i].tmp));
np = sim->create_part(-1, x+rx, y+ry, TYP(parts[i].ctype));
if (np!=-1)
{
transfer_pipe_to_part(sim, parts+i, parts+np);
}
}
//try eating particle at entrance
else if (TYP(parts[i].tmp) == 0 && (sim->elements[TYP(r)].Properties & (TYPE_PART | TYPE_LIQUID | TYPE_GAS | TYPE_ENERGY)))
else if (!TYP(parts[i].ctype) && (sim->elements[TYP(r)].Properties & (TYPE_PART | TYPE_LIQUID | TYPE_GAS | TYPE_ENERGY)))
{
if (TYP(r)==PT_SOAP)
Element_SOAP::detach(sim, ID(r));
transfer_part_to_pipe(parts+(ID(r)), parts+i);
sim->kill_part(ID(r));
}
else if (TYP(parts[i].tmp) == 0 && TYP(r)==PT_STOR && parts[ID(r)].tmp>0 && sim->IsValidElement(parts[ID(r)].tmp) && (sim->elements[parts[ID(r)].tmp].Properties & (TYPE_PART | TYPE_LIQUID | TYPE_GAS | TYPE_ENERGY)))
else if (!TYP(parts[i].ctype) && TYP(r)==PT_STOR && parts[ID(r)].tmp>0 && sim->IsValidElement(parts[ID(r)].tmp) && (sim->elements[parts[ID(r)].tmp].Properties & (TYPE_PART | TYPE_LIQUID | TYPE_GAS | TYPE_ENERGY)))
{
// STOR stores properties in the same places as PIPE does
transfer_pipe_to_pipe(parts+(ID(r)), parts+i);
@ -218,7 +226,7 @@ int Element_PIPE::update(UPDATE_FUNC_ARGS)
}
}
}
else if (!parts[i].ctype && parts[i].life<=10)
else if (!(parts[i].tmp&(PFLAG_COLORS|PFLAG_INITIALIZING)) && parts[i].life<=10)
{
// make a border
for (rx=-2; rx<3; rx++)
@ -229,16 +237,18 @@ int Element_PIPE::update(UPDATE_FUNC_ARGS)
r = pmap[y+ry][x+rx];
if (!r)
{
int index = sim->create_part(-1,x+rx,y+ry,PT_BRCK);//BRCK border, people didn't like DMND
// BRCK border
int index = sim->create_part(-1,x+rx,y+ry,PT_BRCK);
if (parts[i].type == PT_PPIP && index != -1)
parts[index].tmp = 1;
}
}
}
if (parts[i].life <= 1)
parts[i].ctype = 1;
parts[i].tmp |= PFLAG_INITIALIZING;
}
else if (parts[i].ctype==1)//wait for empty space before starting to generate automatic pipe pattern
// Wait for empty space before starting to generate automatic pipe pattern
else if (parts[i].tmp & PFLAG_INITIALIZING)
{
if (!parts[i].life)
{
@ -258,7 +268,7 @@ int Element_PIPE::update(UPDATE_FUNC_ARGS)
if (BOUNDS_CHECK && (rx || ry))
{
r = pmap[y+ry][x+rx];
if ((TYP(r)==PT_PIPE || TYP(r) == PT_PPIP) && parts[i].ctype==1 && parts[i].life )
if ((TYP(r)==PT_PIPE || TYP(r) == PT_PPIP) && parts[i].life)
issingle = 0;
}
if (issingle)
@ -266,7 +276,8 @@ int Element_PIPE::update(UPDATE_FUNC_ARGS)
}
else if (parts[i].life == 2)
{
parts[i].ctype = 2;
parts[i].tmp |= PFLAG_COLOR_RED;
parts[i].tmp &= ~PFLAG_INITIALIZING;
parts[i].life = 6;
}
}
@ -278,7 +289,7 @@ int Element_PIPE::update(UPDATE_FUNC_ARGS)
//#TPT-Directive ElementHeader Element_PIPE static int graphics(GRAPHICS_FUNC_ARGS)
int Element_PIPE::graphics(GRAPHICS_FUNC_ARGS)
{
int t = TYP(cpart->tmp);
int t = TYP(cpart->ctype);
if (t>0 && t<PT_NUM && ren->sim->elements[t].Enabled)
{
if (t == PT_STKM || t == PT_STKM2 || t == PT_FIGH)
@ -324,18 +335,19 @@ int Element_PIPE::graphics(GRAPHICS_FUNC_ARGS)
}
else
{
switch (cpart->ctype){
case 2:
switch (cpart->tmp & PFLAG_COLORS)
{
case PFLAG_COLOR_RED:
*colr = 50;
*colg = 1;
*colb = 1;
break;
case 3:
case PFLAG_COLOR_GREEN:
*colr = 1;
*colg = 50;
*colb = 1;
break;
case 4:
case PFLAG_COLOR_BLUE:
*colr = 1;
*colg = 1;
*colb = 50;
@ -350,12 +362,12 @@ int Element_PIPE::graphics(GRAPHICS_FUNC_ARGS)
//#TPT-Directive ElementHeader Element_PIPE static void transfer_pipe_to_part(Simulation * sim, Particle *pipe, Particle *part)
void Element_PIPE::transfer_pipe_to_part(Simulation * sim, Particle *pipe, Particle *part)
{
part->type = TYP(pipe->tmp);
part->type = TYP(pipe->ctype);
part->temp = pipe->temp;
part->life = pipe->tmp2;
part->tmp = pipe->pavg[0];
part->ctype = pipe->pavg[1];
pipe->tmp &= ~PMAPMASK;
pipe->ctype = 0;
if (!(sim->elements[part->type].Properties & TYPE_ENERGY))
{
@ -372,7 +384,7 @@ void Element_PIPE::transfer_pipe_to_part(Simulation * sim, Particle *pipe, Parti
//#TPT-Directive ElementHeader Element_PIPE static void transfer_part_to_pipe(Particle *part, Particle *pipe)
void Element_PIPE::transfer_part_to_pipe(Particle *part, Particle *pipe)
{
pipe->tmp = (pipe->tmp&~PMAPMASK) | part->type;
pipe->ctype = part->type;
pipe->temp = part->temp;
pipe->tmp2 = part->life;
pipe->pavg[0] = part->tmp;
@ -382,19 +394,20 @@ void Element_PIPE::transfer_part_to_pipe(Particle *part, Particle *pipe)
//#TPT-Directive ElementHeader Element_PIPE static void transfer_pipe_to_pipe(Particle *src, Particle *dest)
void Element_PIPE::transfer_pipe_to_pipe(Particle *src, Particle *dest)
{
dest->tmp = (dest->tmp&~PMAPMASK) | TYP(src->tmp);
dest->ctype = src->ctype;
dest->temp = src->temp;
dest->tmp2 = src->tmp2;
dest->pavg[0] = src->pavg[0];
dest->pavg[1] = src->pavg[1];
src->tmp &= ~PMAPMASK;
src->ctype = 0;
}
//#TPT-Directive ElementHeader Element_PIPE static void pushParticle(Simulation * sim, int i, int count, int original)
void Element_PIPE::pushParticle(Simulation * sim, int i, int count, int original)
{
int rndstore, rnd, rx, ry, r, x, y, np, q, notctype=(((sim->parts[i].ctype)%3)+2);
if (TYP(sim->parts[i].tmp) == 0 || count >= 2)//don't push if there is nothing there, max speed of 2 per frame
int rndstore, rnd, rx, ry, r, x, y, np, q;
unsigned int notctype = (((((sim->parts[i].tmp&PFLAG_COLORS)>>18)+1)%3)+1)<<18;
if (!TYP(sim->parts[i].ctype) || count >= 2)//don't push if there is nothing there, max speed of 2 per frame
return;
x = (int)(sim->parts[i].x+0.5f);
y = (int)(sim->parts[i].y+0.5f);
@ -415,7 +428,7 @@ void Element_PIPE::pushParticle(Simulation * sim, int i, int count, int original
r = sim->pmap[y+ry][x+rx];
if (!r)
continue;
else if ((TYP(r)==PT_PIPE || TYP(r) == PT_PPIP) && sim->parts[ID(r)].ctype!=notctype && TYP(sim->parts[ID(r)].tmp)==0)
else if ((TYP(r)==PT_PIPE || TYP(r) == PT_PPIP) && (sim->parts[ID(r)].tmp&PFLAG_COLORS) != notctype && !TYP(sim->parts[ID(r)].ctype))
{
transfer_pipe_to_pipe(sim->parts+i, sim->parts+(ID(r)));
if (ID(r) > original)
@ -445,7 +458,7 @@ void Element_PIPE::pushParticle(Simulation * sim, int i, int count, int original
{
int coords = 7 - ((sim->parts[i].tmp>>10)&7);
r = sim->pmap[y+ pos_1_ry[coords]][x+ pos_1_rx[coords]];
if ((TYP(r)==PT_PIPE || TYP(r) == PT_PPIP) && sim->parts[ID(r)].ctype!=notctype && TYP(sim->parts[ID(r)].tmp)==0)
if ((TYP(r)==PT_PIPE || TYP(r) == PT_PPIP) && (sim->parts[ID(r)].tmp&PFLAG_COLORS) != notctype && !TYP(sim->parts[ID(r)].ctype))
{
transfer_pipe_to_pipe(sim->parts+i, sim->parts+(ID(r)));
if (ID(r) > original)
@ -472,7 +485,7 @@ void Element_PIPE::pushParticle(Simulation * sim, int i, int count, int original
{
rx = pos_1_rx[coords];
ry = pos_1_ry[coords];
np = sim->create_part(-1,x+rx,y+ry,TYP(sim->parts[i].tmp));
np = sim->create_part(-1,x+rx,y+ry,TYP(sim->parts[i].ctype));
if (np!=-1)
{
transfer_pipe_to_part(sim, sim->parts+i, sim->parts+np);