This repository has been archived on 2025-03-20. You can view files and clone it, but cannot push or open issues or pull requests.
The-Powder-Toy/src/simulation/elements/PIPE.cpp
Tamás Bálint Misius a13c29875f
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.
2023-02-28 12:43:45 +01:00

580 lines
16 KiB
C++

#include "simulation/ElementCommon.h"
int Element_PIPE_update(UPDATE_FUNC_ARGS);
int Element_PIPE_graphics(GRAPHICS_FUNC_ARGS);
void Element_PIPE_transfer_pipe_to_part(Simulation * sim, Particle *pipe, Particle *part, bool STOR);
static void transfer_part_to_pipe(Particle *part, Particle *pipe);
static void transfer_pipe_to_pipe(Particle *src, Particle *dest, bool STOR);
static void pushParticle(Simulation * sim, int i, int count, int original);
void Element_SOAP_detach(Simulation * sim, int i);
void Element::Element_PIPE()
{
Identifier = "DEFAULT_PT_PIPE";
Name = "PIPE";
Colour = PIXPACK(0x444444);
MenuVisible = 1;
MenuSection = SC_FORCE;
Enabled = 1;
Advection = 0.0f;
AirDrag = 0.00f * CFDS;
AirLoss = 0.95f;
Loss = 0.00f;
Collision = 0.0f;
Gravity = 0.0f;
Diffusion = 0.00f;
HotAir = 0.000f * CFDS;
Falldown = 0;
Flammable = 0;
Explosive = 0;
Meltable = 0;
Hardness = 0;
Weight = 100;
DefaultProperties.temp = 273.15f;
HeatConduct = 0;
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;
HighPressure = 10.0f;
HighPressureTransition = PT_BRMT;
LowTemperature = ITL;
LowTemperatureTransition = NT;
HighTemperature = ITH;
HighTemperatureTransition = NT;
DefaultProperties.life = 60;
Update = &Element_PIPE_update;
Graphics = &Element_PIPE_graphics;
}
// 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
// 0x000E0000 PIPE color data stored here
constexpr int PFLAG_NORMALSPEED = 0x00010000;
constexpr int PFLAG_INITIALIZING = 0x00020000; // colors haven't been set yet
constexpr int PFLAG_COLOR_RED = 0x00040000;
constexpr int PFLAG_COLOR_GREEN = 0x00080000;
constexpr int PFLAG_COLOR_BLUE = 0x000C0000;
constexpr int PFLAG_COLORS = 0x000C0000;
constexpr int PPIP_TMPFLAG_REVERSED = 0x01000000;
constexpr int PPIP_TMPFLAG_PAUSED = 0x02000000;
constexpr int PPIP_TMPFLAG_TRIGGER_REVERSE = 0x04000000;
constexpr int PPIP_TMPFLAG_TRIGGER_OFF = 0x08000000;
constexpr int PPIP_TMPFLAG_TRIGGER_ON = 0x10000000;
constexpr int 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 };
static void transformPatch(Particle &part, const int (&patch)[8])
{
if (part.tmp & 0x00000200) part.tmp = (part.tmp & 0xFFFFE3FF) | (patch[(part.tmp & 0x00001C00) >> 10] << 10);
if (part.tmp & 0x00002000) part.tmp = (part.tmp & 0xFFFE3FFF) | (patch[(part.tmp & 0x0001C000) >> 14] << 14);
}
void Element_PIPE_patchR(Particle &part)
{
// 035 -> 210
// 1 6 -> 4 3
// 247 -> 765
const int patchR[] = { 2, 4, 7, 1, 6, 0, 3, 5 };
transformPatch(part, patchR);
}
void Element_PIPE_patchH(Particle &part)
{
// 035 -> 530
// 1 6 -> 6 1
// 247 -> 742
const int patchH[] = { 5, 6, 7, 3, 4, 0, 1, 2 };
transformPatch(part, patchH);
}
void Element_PIPE_patchV(Particle &part)
{
// 035 -> 247
// 1 6 -> 1 6
// 247 -> 035
const int patchV[] = { 2, 1, 0, 4, 3, 7, 6, 5 };
transformPatch(part, patchV);
}
static unsigned int prevColor(unsigned int flags)
{
unsigned int color = flags & PFLAG_COLORS;
if (color == PFLAG_COLOR_RED)
return PFLAG_COLOR_GREEN;
else if (color == PFLAG_COLOR_GREEN)
return PFLAG_COLOR_BLUE;
else if (color == PFLAG_COLOR_BLUE)
return PFLAG_COLOR_RED;
return PFLAG_COLOR_RED;
}
static unsigned int nextColor(unsigned int flags)
{
unsigned int color = flags & PFLAG_COLORS;
if (color == PFLAG_COLOR_RED)
return PFLAG_COLOR_BLUE;
else if (color == PFLAG_COLOR_BLUE)
color = PFLAG_COLOR_GREEN;
else if (color == PFLAG_COLOR_GREEN)
return PFLAG_COLOR_RED;
return PFLAG_COLOR_GREEN;
}
int Element_PIPE_update(UPDATE_FUNC_ARGS)
{
int r, rx, ry, np;
int rnd, rndstore;
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;
if (parts[i].tmp & PPIP_TMPFLAG_TRIGGER_ON) // TRIGGER_ON overrides TRIGGER_OFF
{
if (parts[i].tmp & PPIP_TMPFLAG_PAUSED)
pause_changed = 1;
parts[i].tmp &= ~PPIP_TMPFLAG_PAUSED;
}
else if (parts[i].tmp & PPIP_TMPFLAG_TRIGGER_OFF)
{
if (!(parts[i].tmp & PPIP_TMPFLAG_PAUSED))
pause_changed = 1;
parts[i].tmp |= PPIP_TMPFLAG_PAUSED;
}
if (pause_changed)
{
int rx, ry, r;
for (rx=-2; rx<3; rx++)
for (ry=-2; ry<3; ry++)
{
if (BOUNDS_CHECK && (rx || ry))
{
r = pmap[y+ry][x+rx];
if (TYP(r) == PT_BRCK)
{
if (parts[i].tmp & PPIP_TMPFLAG_PAUSED)
parts[ID(r)].tmp = 0;
else
parts[ID(r)].tmp = 1; //make surrounding BRCK glow
}
}
}
}
if (parts[i].tmp & PPIP_TMPFLAG_TRIGGER_REVERSE)
{
parts[i].tmp ^= PPIP_TMPFLAG_REVERSED;
// 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;
int coords2 = (parts[i].tmp>>9)&0xF;
parts[i].tmp &= ~0x1FE00;
parts[i].tmp |= coords<<9;
parts[i].tmp |= coords2<<13;
}
}
parts[i].tmp &= ~PPIP_TMPFLAG_TRIGGERS;
}
if ((parts[i].tmp&PFLAG_COLORS) && !(parts[i].tmp & PPIP_TMPFLAG_PAUSED))
{
if (parts[i].life==3)
{
int lastneighbor = -1;
int neighborcount = 0;
int count = 0;
// make automatic pipe pattern
for (rx=-1; rx<2; rx++)
for (ry=-1; ry<2; ry++)
if (BOUNDS_CHECK && (rx || ry))
{
count++;
r = pmap[y+ry][x+rx];
if (!r)
continue;
if (TYP(r) != PT_PIPE && TYP(r) != PT_PPIP)
continue;
unsigned int next = nextColor(parts[i].tmp);
unsigned int prev = prevColor(parts[i].tmp);
if (parts[ID(r)].tmp&PFLAG_INITIALIZING)
{
parts[ID(r)].tmp |= next;
parts[ID(r)].tmp &= ~PFLAG_INITIALIZING;
parts[ID(r)].life = 6;
// Is a single pixel pipe
if (parts[i].tmp&0x100)
{
// Will transfer to a single pixel pipe
parts[ID(r)].tmp |= 0x200;
// Coords of where it came from
parts[ID(r)].tmp |= (count - 1) << 10;
parts[i].tmp |= (8 - count) << 14;
parts[i].tmp |= 0x2000;
}
neighborcount ++;
lastneighbor = ID(r);
}
else if ((parts[ID(r)].tmp&PFLAG_COLORS) != prev)
{
neighborcount ++;
lastneighbor = ID(r);
}
}
if (neighborcount == 1)
parts[lastneighbor].tmp |= 0x100;
}
else
{
if (parts[i].flags&PFLAG_NORMALSPEED)//skip particle push to prevent particle number being higher causing speed up
{
parts[i].flags &= ~PFLAG_NORMALSPEED;
}
else
{
pushParticle(sim, i,0,i);
}
if (nt)//there is something besides PIPE around current particle
{
rndstore = RNG::Ref().gen();
rnd = rndstore&7;
//rndstore = rndstore>>3;
rx = pos_1_rx[rnd];
ry = pos_1_ry[rnd];
if (BOUNDS_CHECK)
{
r = pmap[y+ry][x+rx];
if(!r)
r = sim->photons[y+ry][x+rx];
if (surround_space && !r && TYP(parts[i].ctype)) //creating at end
{
np = sim->create_part(-1, x+rx, y+ry, TYP(parts[i].ctype));
if (np!=-1)
{
Element_PIPE_transfer_pipe_to_part(sim, parts+i, parts+np, false);
}
}
//try eating particle at entrance
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].ctype) && TYP(r)==PT_STOR && sim->IsElement(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, true);
}
}
}
}
}
else if (!(parts[i].tmp&(PFLAG_COLORS|PFLAG_INITIALIZING)) && parts[i].life<=10)
{
// make a border
for (rx=-2; rx<3; rx++)
for (ry=-2; ry<3; ry++)
{
if (BOUNDS_CHECK && (rx || ry))
{
r = pmap[y+ry][x+rx];
if (!r)
{
// 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].tmp |= PFLAG_INITIALIZING;
}
// Wait for empty space before starting to generate automatic pipe pattern
else if (parts[i].tmp & PFLAG_INITIALIZING)
{
if (!parts[i].life)
{
for (rx=-1; rx<2; rx++)
for (ry=-1; ry<2; ry++)
if (BOUNDS_CHECK && (rx || ry))
{
if (!pmap[y+ry][x+rx] && sim->bmap[(y+ry)/CELL][(x+rx)/CELL]!=WL_ALLOWAIR && sim->bmap[(y+ry)/CELL][(x+rx)/CELL]!=WL_WALL && sim->bmap[(y+ry)/CELL][(x+rx)/CELL]!=WL_WALLELEC && (sim->bmap[(y+ry)/CELL][(x+rx)/CELL]!=WL_EWALL || sim->emap[(y+ry)/CELL][(x+rx)/CELL]))
parts[i].life=50;
}
}
else if (parts[i].life==5)//check for beginning of pipe single pixel
{
int issingle = 1;
for (rx=-1; rx<2; rx++)
for (ry=-1; ry<2; ry++)
if (BOUNDS_CHECK && (rx || ry))
{
r = pmap[y+ry][x+rx];
if ((TYP(r)==PT_PIPE || TYP(r) == PT_PPIP) && parts[i].life)
issingle = 0;
}
if (issingle)
parts[i].tmp |= 0x100;
}
else if (parts[i].life == 2)
{
parts[i].tmp |= PFLAG_COLOR_RED;
parts[i].tmp &= ~PFLAG_INITIALIZING;
parts[i].life = 6;
}
}
return 0;
}
int Element_PIPE_graphics(GRAPHICS_FUNC_ARGS)
{
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)
return 0;
if (ren->graphicscache[t].isready)
{
*pixel_mode = ren->graphicscache[t].pixel_mode;
*cola = ren->graphicscache[t].cola;
*colr = ren->graphicscache[t].colr;
*colg = ren->graphicscache[t].colg;
*colb = ren->graphicscache[t].colb;
*firea = ren->graphicscache[t].firea;
*firer = ren->graphicscache[t].firer;
*fireg = ren->graphicscache[t].fireg;
*fireb = ren->graphicscache[t].fireb;
}
else
{
// Temp particle used for graphics.
Particle tpart = *cpart;
// Emulate the graphics of stored particle.
memset(cpart, 0, sizeof(Particle));
cpart->type = t;
cpart->temp = tpart.temp;
cpart->life = tpart.tmp2;
cpart->tmp = tpart.tmp3;
cpart->ctype = tpart.tmp4;
*colr = PIXR(ren->sim->elements[t].Colour);
*colg = PIXG(ren->sim->elements[t].Colour);
*colb = PIXB(ren->sim->elements[t].Colour);
if (ren->sim->elements[t].Graphics)
{
(*(ren->sim->elements[t].Graphics))(ren, cpart, nx, ny, pixel_mode, cola, colr, colg, colb, firea, firer, fireg, fireb);
}
else
{
Element::defaultGraphics(ren, cpart, nx, ny, pixel_mode, cola, colr, colg, colb, firea, firer, fireg, fireb);
}
// Restore original particle data.
*cpart = tpart;
}
//*colr = PIXR(elements[t].pcolors);
//*colg = PIXG(elements[t].pcolors);
//*colb = PIXB(elements[t].pcolors);
}
else
{
switch (cpart->tmp & PFLAG_COLORS)
{
case PFLAG_COLOR_RED:
*colr = 50;
*colg = 1;
*colb = 1;
break;
case PFLAG_COLOR_GREEN:
*colr = 1;
*colg = 50;
*colb = 1;
break;
case PFLAG_COLOR_BLUE:
*colr = 1;
*colg = 1;
*colb = 50;
break;
default:
break;
}
}
return 0;
}
void Element_PIPE_transfer_pipe_to_part(Simulation * sim, Particle *pipe, Particle *part, bool STOR)
{
// STOR also calls this function to move particles from STOR to PRTI
// PIPE was changed, so now PIPE and STOR don't use the same particle storage format
if (STOR)
{
part->type = TYP(pipe->tmp);
pipe->tmp = 0;
}
else
{
part->type = TYP(pipe->ctype);
pipe->ctype = 0;
}
part->temp = pipe->temp;
part->life = pipe->tmp2;
part->tmp = pipe->tmp3;
part->ctype = pipe->tmp4;
if (!(sim->elements[part->type].Properties & TYPE_ENERGY))
{
part->vx = 0.0f;
part->vy = 0.0f;
}
part->tmp2 = 0;
part->flags = 0;
part->dcolour = 0;
}
static void transfer_part_to_pipe(Particle *part, Particle *pipe)
{
pipe->ctype = part->type;
pipe->temp = part->temp;
pipe->tmp2 = part->life;
pipe->tmp3 = part->tmp;
pipe->tmp4 = part->ctype;
}
static void transfer_pipe_to_pipe(Particle *src, Particle *dest, bool STOR)
{
// STOR to PIPE
if (STOR)
{
dest->ctype = src->tmp;
src->tmp = 0;
}
else
{
dest->ctype = src->ctype;
src->ctype = 0;
}
dest->temp = src->temp;
dest->tmp2 = src->tmp2;
dest->tmp3 = src->tmp3;
dest->tmp4 = src->tmp4;
}
static void pushParticle(Simulation * sim, int i, int count, int original)
{
int rndstore, rnd, rx, ry, r, x, y, np, q;
unsigned int notctype = nextColor(sim->parts[i].tmp);
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);
if( !(sim->parts[i].tmp&0x200) )
{
//normal random push
rndstore = RNG::Ref().gen();
// RAND_MAX is at least 32767 on all platforms i.e. pow(8,5)-1
// so can go 5 cycles without regenerating rndstore
// (although now we use our own randomizer so maybe should reevaluate all the rndstore usages in every element)
for (q=0; q<3; q++)//try to push 3 times
{
rnd = rndstore&7;
rndstore = rndstore>>3;
rx = pos_1_rx[rnd];
ry = pos_1_ry[rnd];
if (BOUNDS_CHECK)
{
r = sim->pmap[y+ry][x+rx];
if (!r)
continue;
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)), false);
if (ID(r) > original)
sim->parts[ID(r)].flags |= PFLAG_NORMALSPEED;//skip particle push, normalizes speed
count++;
pushParticle(sim, ID(r),count,original);
}
else if (TYP(r) == PT_PRTI) //Pass particles into PRTI for a pipe speed increase
{
int portaltmp = sim->parts[ID(r)].tmp;
if (portaltmp >= CHANNELS)
portaltmp = CHANNELS-1;
else if (portaltmp < 0)
portaltmp = 0;
for (int nnx = 0; nnx < 80; nnx++)
if (!sim->portalp[portaltmp][count][nnx].type)
{
Element_PIPE_transfer_pipe_to_part(sim, sim->parts+i, &(sim->portalp[portaltmp][count][nnx]), false);
count++;
break;
}
}
}
}
}
else //predefined 1 pixel thick pipe movement
{
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)].tmp&PFLAG_COLORS) != notctype && !TYP(sim->parts[ID(r)].ctype))
{
transfer_pipe_to_pipe(sim->parts+i, sim->parts+(ID(r)), false);
if (ID(r) > original)
sim->parts[ID(r)].flags |= PFLAG_NORMALSPEED;//skip particle push, normalizes speed
count++;
pushParticle(sim, ID(r),count,original);
}
else if (TYP(r) == PT_PRTI) //Pass particles into PRTI for a pipe speed increase
{
int portaltmp = sim->parts[ID(r)].tmp;
if (portaltmp >= CHANNELS)
portaltmp = CHANNELS-1;
else if (portaltmp < 0)
portaltmp = 0;
for (int nnx = 0; nnx < 80; nnx++)
if (!sim->portalp[portaltmp][count][nnx].type)
{
Element_PIPE_transfer_pipe_to_part(sim, sim->parts+i, &(sim->portalp[portaltmp][count][nnx]), false);
count++;
break;
}
}
else if (!r) //Move particles out of pipe automatically, much faster at ends
{
rx = pos_1_rx[coords];
ry = pos_1_ry[coords];
np = sim->create_part(-1,x+rx,y+ry,TYP(sim->parts[i].ctype));
if (np!=-1)
{
Element_PIPE_transfer_pipe_to_part(sim, sim->parts+i, sim->parts+np, false);
}
}
}
return;
}