#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 && tsim->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; }