1 #include "simulation/ElementCommon.h"
2 //Temp particle used for graphics
3 Particle tpart;
4
5 int Element_PIPE_update(UPDATE_FUNC_ARGS);
6 int Element_PIPE_graphics(GRAPHICS_FUNC_ARGS);
7 void Element_PIPE_transfer_pipe_to_part(Simulation * sim, Particle *pipe, Particle *part, bool STOR);
8 static void transfer_part_to_pipe(Particle *part, Particle *pipe);
9 static void transfer_pipe_to_pipe(Particle *src, Particle *dest, bool STOR);
10 static void pushParticle(Simulation * sim, int i, int count, int original);
11 void Element_SOAP_detach(Simulation * sim, int i);
12
Element_PIPE()13 void Element::Element_PIPE()
14 {
15 Identifier = "DEFAULT_PT_PIPE";
16 Name = "PIPE";
17 Colour = PIXPACK(0x444444);
18 MenuVisible = 1;
19 MenuSection = SC_FORCE;
20 Enabled = 1;
21
22 Advection = 0.0f;
23 AirDrag = 0.00f * CFDS;
24 AirLoss = 0.95f;
25 Loss = 0.00f;
26 Collision = 0.0f;
27 Gravity = 0.0f;
28 Diffusion = 0.00f;
29 HotAir = 0.000f * CFDS;
30 Falldown = 0;
31
32 Flammable = 0;
33 Explosive = 0;
34 Meltable = 0;
35 Hardness = 0;
36
37 Weight = 100;
38
39 DefaultProperties.temp = 273.15f;
40 HeatConduct = 0;
41 Description = "PIPE, moves particles around. Once the BRCK generates, erase some for the exit. Then the PIPE generates and is usable.";
42
43 Properties = TYPE_SOLID|PROP_LIFE_DEC;
44
45 LowPressure = IPL;
46 LowPressureTransition = NT;
47 HighPressure = 10.0f;
48 HighPressureTransition = PT_BRMT;
49 LowTemperature = ITL;
50 LowTemperatureTransition = NT;
51 HighTemperature = ITH;
52 HighTemperatureTransition = NT;
53
54 DefaultProperties.life = 60;
55
56 Update = &Element_PIPE_update;
57 Graphics = &Element_PIPE_graphics;
58
59 memset(&tpart, 0, sizeof(Particle));
60 }
61
62 // 0x000000FF element
63 // 0x00000100 is single pixel pipe
64 // 0x00000200 will transfer like a single pixel pipe when in forward mode
65 // 0x00001C00 forward single pixel pipe direction
66 // 0x00002000 will transfer like a single pixel pipe when in reverse mode
67 // 0x0001C000 reverse single pixel pipe direction
68 // 0x000E0000 PIPE color data stored here
69
70 constexpr int PFLAG_NORMALSPEED = 0x00010000;
71 constexpr int PFLAG_INITIALIZING = 0x00020000; // colors haven't been set yet
72 constexpr int PFLAG_COLOR_RED = 0x00040000;
73 constexpr int PFLAG_COLOR_GREEN = 0x00080000;
74 constexpr int PFLAG_COLOR_BLUE = 0x000C0000;
75 constexpr int PFLAG_COLORS = 0x000C0000;
76
77 constexpr int PPIP_TMPFLAG_REVERSED = 0x01000000;
78 constexpr int PPIP_TMPFLAG_PAUSED = 0x02000000;
79 constexpr int PPIP_TMPFLAG_TRIGGER_REVERSE = 0x04000000;
80 constexpr int PPIP_TMPFLAG_TRIGGER_OFF = 0x08000000;
81 constexpr int PPIP_TMPFLAG_TRIGGER_ON = 0x10000000;
82 constexpr int PPIP_TMPFLAG_TRIGGERS = 0x1C000000;
83
84 signed char pos_1_rx[] = {-1,-1,-1, 0, 0, 1, 1, 1};
85 signed char pos_1_ry[] = {-1, 0, 1,-1, 1,-1, 0, 1};
86
prevColor(unsigned int flags)87 static unsigned int prevColor(unsigned int flags)
88 {
89 unsigned int color = flags & PFLAG_COLORS;
90 if (color == PFLAG_COLOR_RED)
91 return PFLAG_COLOR_GREEN;
92 else if (color == PFLAG_COLOR_GREEN)
93 return PFLAG_COLOR_BLUE;
94 else if (color == PFLAG_COLOR_BLUE)
95 return PFLAG_COLOR_RED;
96 return PFLAG_COLOR_RED;
97 }
98
nextColor(unsigned int flags)99 static unsigned int nextColor(unsigned int flags)
100 {
101 unsigned int color = flags & PFLAG_COLORS;
102 if (color == PFLAG_COLOR_RED)
103 return PFLAG_COLOR_BLUE;
104 else if (color == PFLAG_COLOR_BLUE)
105 color = PFLAG_COLOR_GREEN;
106 else if (color == PFLAG_COLOR_GREEN)
107 return PFLAG_COLOR_RED;
108 return PFLAG_COLOR_GREEN;
109 }
110
Element_PIPE_update(UPDATE_FUNC_ARGS)111 int Element_PIPE_update(UPDATE_FUNC_ARGS)
112 {
113 int r, rx, ry, np;
114 int rnd, rndstore;
115 if (parts[i].ctype && !sim->elements[TYP(parts[i].ctype)].Enabled)
116 parts[i].ctype = 0;
117 if (parts[i].tmp & PPIP_TMPFLAG_TRIGGERS)
118 {
119 int pause_changed = 0;
120 if (parts[i].tmp & PPIP_TMPFLAG_TRIGGER_ON) // TRIGGER_ON overrides TRIGGER_OFF
121 {
122 if (parts[i].tmp & PPIP_TMPFLAG_PAUSED)
123 pause_changed = 1;
124 parts[i].tmp &= ~PPIP_TMPFLAG_PAUSED;
125 }
126 else if (parts[i].tmp & PPIP_TMPFLAG_TRIGGER_OFF)
127 {
128 if (!(parts[i].tmp & PPIP_TMPFLAG_PAUSED))
129 pause_changed = 1;
130 parts[i].tmp |= PPIP_TMPFLAG_PAUSED;
131 }
132 if (pause_changed)
133 {
134 int rx, ry, r;
135 for (rx=-2; rx<3; rx++)
136 for (ry=-2; ry<3; ry++)
137 {
138 if (BOUNDS_CHECK && (rx || ry))
139 {
140 r = pmap[y+ry][x+rx];
141 if (TYP(r) == PT_BRCK)
142 {
143 if (parts[i].tmp & PPIP_TMPFLAG_PAUSED)
144 parts[ID(r)].tmp = 0;
145 else
146 parts[ID(r)].tmp = 1; //make surrounding BRCK glow
147 }
148 }
149 }
150 }
151
152 if (parts[i].tmp & PPIP_TMPFLAG_TRIGGER_REVERSE)
153 {
154 parts[i].tmp ^= PPIP_TMPFLAG_REVERSED;
155 // Switch colors so it goes in reverse
156 if ((parts[i].tmp&PFLAG_COLORS) != PFLAG_COLOR_GREEN)
157 parts[i].tmp ^= PFLAG_COLOR_GREEN;
158 if (parts[i].tmp & 0x100) //Switch one pixel pipe direction
159 {
160 int coords = (parts[i].tmp>>13)&0xF;
161 int coords2 = (parts[i].tmp>>9)&0xF;
162 parts[i].tmp &= ~0x1FE00;
163 parts[i].tmp |= coords<<9;
164 parts[i].tmp |= coords2<<13;
165 }
166 }
167
168 parts[i].tmp &= ~PPIP_TMPFLAG_TRIGGERS;
169 }
170 if ((parts[i].tmp&PFLAG_COLORS) && !(parts[i].tmp & PPIP_TMPFLAG_PAUSED))
171 {
172 if (parts[i].life==3)
173 {
174 int lastneighbor = -1;
175 int neighborcount = 0;
176 int count = 0;
177 // make automatic pipe pattern
178 for (rx=-1; rx<2; rx++)
179 for (ry=-1; ry<2; ry++)
180 if (BOUNDS_CHECK && (rx || ry))
181 {
182 count++;
183 r = pmap[y+ry][x+rx];
184 if (!r)
185 continue;
186 if (TYP(r) != PT_PIPE && TYP(r) != PT_PPIP)
187 continue;
188 unsigned int next = nextColor(parts[i].tmp);
189 unsigned int prev = prevColor(parts[i].tmp);
190 if (parts[ID(r)].tmp&PFLAG_INITIALIZING)
191 {
192 parts[ID(r)].tmp |= next;
193 parts[ID(r)].tmp &= ~PFLAG_INITIALIZING;
194 parts[ID(r)].life = 6;
195 // Is a single pixel pipe
196 if (parts[i].tmp&0x100)
197 {
198 // Will transfer to a single pixel pipe
199 parts[ID(r)].tmp |= 0x200;
200 // Coords of where it came from
201 parts[ID(r)].tmp |= (count - 1) << 10;
202 parts[i].tmp |= (8 - count) << 14;
203 parts[i].tmp |= 0x2000;
204 }
205 neighborcount ++;
206 lastneighbor = ID(r);
207 }
208 else if ((parts[ID(r)].tmp&PFLAG_COLORS) != prev)
209 {
210 neighborcount ++;
211 lastneighbor = ID(r);
212 }
213 }
214 if (neighborcount == 1)
215 parts[lastneighbor].tmp |= 0x100;
216 }
217 else
218 {
219 if (parts[i].flags&PFLAG_NORMALSPEED)//skip particle push to prevent particle number being higher causing speed up
220 {
221 parts[i].flags &= ~PFLAG_NORMALSPEED;
222 }
223 else
224 {
225 pushParticle(sim, i,0,i);
226 }
227
228 if (nt)//there is something besides PIPE around current particle
229 {
230 rndstore = RNG::Ref().gen();
231 rnd = rndstore&7;
232 //rndstore = rndstore>>3;
233 rx = pos_1_rx[rnd];
234 ry = pos_1_ry[rnd];
235 if (BOUNDS_CHECK)
236 {
237 r = pmap[y+ry][x+rx];
238 if(!r)
239 r = sim->photons[y+ry][x+rx];
240 if (surround_space && !r && TYP(parts[i].ctype)) //creating at end
241 {
242 np = sim->create_part(-1, x+rx, y+ry, TYP(parts[i].ctype));
243 if (np!=-1)
244 {
245 Element_PIPE_transfer_pipe_to_part(sim, parts+i, parts+np, false);
246 }
247 }
248 //try eating particle at entrance
249 else if (!TYP(parts[i].ctype) && (sim->elements[TYP(r)].Properties & (TYPE_PART | TYPE_LIQUID | TYPE_GAS | TYPE_ENERGY)))
250 {
251 if (TYP(r)==PT_SOAP)
252 Element_SOAP_detach(sim, ID(r));
253 transfer_part_to_pipe(parts+(ID(r)), parts+i);
254 sim->kill_part(ID(r));
255 }
256 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)))
257 {
258 // STOR stores properties in the same places as PIPE does
259 transfer_pipe_to_pipe(parts+(ID(r)), parts+i, true);
260 }
261 }
262 }
263 }
264 }
265 else if (!(parts[i].tmp&(PFLAG_COLORS|PFLAG_INITIALIZING)) && parts[i].life<=10)
266 {
267 // make a border
268 for (rx=-2; rx<3; rx++)
269 for (ry=-2; ry<3; ry++)
270 {
271 if (BOUNDS_CHECK && (rx || ry))
272 {
273 r = pmap[y+ry][x+rx];
274 if (!r)
275 {
276 // BRCK border
277 int index = sim->create_part(-1,x+rx,y+ry,PT_BRCK);
278 if (parts[i].type == PT_PPIP && index != -1)
279 parts[index].tmp = 1;
280 }
281 }
282 }
283 if (parts[i].life <= 1)
284 parts[i].tmp |= PFLAG_INITIALIZING;
285 }
286 // Wait for empty space before starting to generate automatic pipe pattern
287 else if (parts[i].tmp & PFLAG_INITIALIZING)
288 {
289 if (!parts[i].life)
290 {
291 for (rx=-1; rx<2; rx++)
292 for (ry=-1; ry<2; ry++)
293 if (BOUNDS_CHECK && (rx || ry))
294 {
295 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]))
296 parts[i].life=50;
297 }
298 }
299 else if (parts[i].life==5)//check for beginning of pipe single pixel
300 {
301 int issingle = 1;
302 for (rx=-1; rx<2; rx++)
303 for (ry=-1; ry<2; ry++)
304 if (BOUNDS_CHECK && (rx || ry))
305 {
306 r = pmap[y+ry][x+rx];
307 if ((TYP(r)==PT_PIPE || TYP(r) == PT_PPIP) && parts[i].life)
308 issingle = 0;
309 }
310 if (issingle)
311 parts[i].tmp |= 0x100;
312 }
313 else if (parts[i].life == 2)
314 {
315 parts[i].tmp |= PFLAG_COLOR_RED;
316 parts[i].tmp &= ~PFLAG_INITIALIZING;
317 parts[i].life = 6;
318 }
319 }
320 return 0;
321 }
322
Element_PIPE_graphics(GRAPHICS_FUNC_ARGS)323 int Element_PIPE_graphics(GRAPHICS_FUNC_ARGS)
324 {
325 int t = TYP(cpart->ctype);
326 if (t>0 && t<PT_NUM && ren->sim->elements[t].Enabled)
327 {
328 if (t == PT_STKM || t == PT_STKM2 || t == PT_FIGH)
329 return 0;
330 if (ren->graphicscache[t].isready)
331 {
332 *pixel_mode = ren->graphicscache[t].pixel_mode;
333 *cola = ren->graphicscache[t].cola;
334 *colr = ren->graphicscache[t].colr;
335 *colg = ren->graphicscache[t].colg;
336 *colb = ren->graphicscache[t].colb;
337 *firea = ren->graphicscache[t].firea;
338 *firer = ren->graphicscache[t].firer;
339 *fireg = ren->graphicscache[t].fireg;
340 *fireb = ren->graphicscache[t].fireb;
341 }
342 else
343 {
344 //Emulate the graphics of stored particle
345 tpart.type = t;
346 tpart.temp = cpart->temp;
347 tpart.life = cpart->tmp2;
348 tpart.tmp = cpart->pavg[0];
349 tpart.ctype = cpart->pavg[1];
350 if (t == PT_PHOT && tpart.ctype == 0x40000000)
351 tpart.ctype = 0x3FFFFFFF;
352
353 *colr = PIXR(ren->sim->elements[t].Colour);
354 *colg = PIXG(ren->sim->elements[t].Colour);
355 *colb = PIXB(ren->sim->elements[t].Colour);
356 if (ren->sim->elements[t].Graphics)
357 {
358 (*(ren->sim->elements[t].Graphics))(ren, &tpart, nx, ny, pixel_mode, cola, colr, colg, colb, firea, firer, fireg, fireb);
359 }
360 else
361 {
362 Element::defaultGraphics(ren, &tpart, nx, ny, pixel_mode, cola, colr, colg, colb, firea, firer, fireg, fireb);
363 }
364 }
365 //*colr = PIXR(elements[t].pcolors);
366 //*colg = PIXG(elements[t].pcolors);
367 //*colb = PIXB(elements[t].pcolors);
368 }
369 else
370 {
371 switch (cpart->tmp & PFLAG_COLORS)
372 {
373 case PFLAG_COLOR_RED:
374 *colr = 50;
375 *colg = 1;
376 *colb = 1;
377 break;
378 case PFLAG_COLOR_GREEN:
379 *colr = 1;
380 *colg = 50;
381 *colb = 1;
382 break;
383 case PFLAG_COLOR_BLUE:
384 *colr = 1;
385 *colg = 1;
386 *colb = 50;
387 break;
388 default:
389 break;
390 }
391 }
392 return 0;
393 }
394
Element_PIPE_transfer_pipe_to_part(Simulation * sim,Particle * pipe,Particle * part,bool STOR)395 void Element_PIPE_transfer_pipe_to_part(Simulation * sim, Particle *pipe, Particle *part, bool STOR)
396 {
397 // STOR also calls this function to move particles from STOR to PRTI
398 // PIPE was changed, so now PIPE and STOR don't use the same particle storage format
399 if (STOR)
400 {
401 part->type = TYP(pipe->tmp);
402 pipe->tmp = 0;
403 }
404 else
405 {
406 part->type = TYP(pipe->ctype);
407 pipe->ctype = 0;
408 }
409 part->temp = pipe->temp;
410 part->life = pipe->tmp2;
411 part->tmp = pipe->pavg[0];
412 part->ctype = pipe->pavg[1];
413
414 if (!(sim->elements[part->type].Properties & TYPE_ENERGY))
415 {
416 part->vx = 0.0f;
417 part->vy = 0.0f;
418 }
419 else if (part->type == PT_PHOT && part->ctype == 0x40000000)
420 part->ctype = 0x3FFFFFFF;
421 part->tmp2 = 0;
422 part->flags = 0;
423 part->dcolour = 0;
424 }
425
transfer_part_to_pipe(Particle * part,Particle * pipe)426 static void transfer_part_to_pipe(Particle *part, Particle *pipe)
427 {
428 pipe->ctype = part->type;
429 pipe->temp = part->temp;
430 pipe->tmp2 = part->life;
431 pipe->pavg[0] = part->tmp;
432 pipe->pavg[1] = part->ctype;
433 }
434
transfer_pipe_to_pipe(Particle * src,Particle * dest,bool STOR)435 static void transfer_pipe_to_pipe(Particle *src, Particle *dest, bool STOR)
436 {
437 // STOR to PIPE
438 if (STOR)
439 {
440 dest->ctype = src->tmp;
441 src->tmp = 0;
442 }
443 else
444 {
445 dest->ctype = src->ctype;
446 src->ctype = 0;
447 }
448 dest->temp = src->temp;
449 dest->tmp2 = src->tmp2;
450 dest->pavg[0] = src->pavg[0];
451 dest->pavg[1] = src->pavg[1];
452 }
453
pushParticle(Simulation * sim,int i,int count,int original)454 static void pushParticle(Simulation * sim, int i, int count, int original)
455 {
456 int rndstore, rnd, rx, ry, r, x, y, np, q;
457 unsigned int notctype = nextColor(sim->parts[i].tmp);
458 if (!TYP(sim->parts[i].ctype) || count >= 2)//don't push if there is nothing there, max speed of 2 per frame
459 return;
460 x = (int)(sim->parts[i].x+0.5f);
461 y = (int)(sim->parts[i].y+0.5f);
462 if( !(sim->parts[i].tmp&0x200) )
463 {
464 //normal random push
465 rndstore = RNG::Ref().gen();
466 // RAND_MAX is at least 32767 on all platforms i.e. pow(8,5)-1
467 // so can go 5 cycles without regenerating rndstore
468 // (although now we use our own randomizer so maybe should reevaluate all the rndstore usages in every element)
469 for (q=0; q<3; q++)//try to push 3 times
470 {
471 rnd = rndstore&7;
472 rndstore = rndstore>>3;
473 rx = pos_1_rx[rnd];
474 ry = pos_1_ry[rnd];
475 if (BOUNDS_CHECK)
476 {
477 r = sim->pmap[y+ry][x+rx];
478 if (!r)
479 continue;
480 else if ((TYP(r)==PT_PIPE || TYP(r) == PT_PPIP) && (sim->parts[ID(r)].tmp&PFLAG_COLORS) != notctype && !TYP(sim->parts[ID(r)].ctype))
481 {
482 transfer_pipe_to_pipe(sim->parts+i, sim->parts+(ID(r)), false);
483 if (ID(r) > original)
484 sim->parts[ID(r)].flags |= PFLAG_NORMALSPEED;//skip particle push, normalizes speed
485 count++;
486 pushParticle(sim, ID(r),count,original);
487 }
488 else if (TYP(r) == PT_PRTI) //Pass particles into PRTI for a pipe speed increase
489 {
490 int portaltmp = sim->parts[ID(r)].tmp;
491 if (portaltmp >= CHANNELS)
492 portaltmp = CHANNELS-1;
493 else if (portaltmp < 0)
494 portaltmp = 0;
495 for (int nnx = 0; nnx < 80; nnx++)
496 if (!sim->portalp[portaltmp][count][nnx].type)
497 {
498 Element_PIPE_transfer_pipe_to_part(sim, sim->parts+i, &(sim->portalp[portaltmp][count][nnx]), false);
499 count++;
500 break;
501 }
502 }
503 }
504 }
505 }
506 else //predefined 1 pixel thick pipe movement
507 {
508 int coords = 7 - ((sim->parts[i].tmp>>10)&7);
509 r = sim->pmap[y+ pos_1_ry[coords]][x+ pos_1_rx[coords]];
510 if ((TYP(r)==PT_PIPE || TYP(r) == PT_PPIP) && (sim->parts[ID(r)].tmp&PFLAG_COLORS) != notctype && !TYP(sim->parts[ID(r)].ctype))
511 {
512 transfer_pipe_to_pipe(sim->parts+i, sim->parts+(ID(r)), false);
513 if (ID(r) > original)
514 sim->parts[ID(r)].flags |= PFLAG_NORMALSPEED;//skip particle push, normalizes speed
515 count++;
516 pushParticle(sim, ID(r),count,original);
517 }
518 else if (TYP(r) == PT_PRTI) //Pass particles into PRTI for a pipe speed increase
519 {
520 int portaltmp = sim->parts[ID(r)].tmp;
521 if (portaltmp >= CHANNELS)
522 portaltmp = CHANNELS-1;
523 else if (portaltmp < 0)
524 portaltmp = 0;
525 for (int nnx = 0; nnx < 80; nnx++)
526 if (!sim->portalp[portaltmp][count][nnx].type)
527 {
528 Element_PIPE_transfer_pipe_to_part(sim, sim->parts+i, &(sim->portalp[portaltmp][count][nnx]), false);
529 count++;
530 break;
531 }
532 }
533 else if (!r) //Move particles out of pipe automatically, much faster at ends
534 {
535 rx = pos_1_rx[coords];
536 ry = pos_1_ry[coords];
537 np = sim->create_part(-1,x+rx,y+ry,TYP(sim->parts[i].ctype));
538 if (np!=-1)
539 {
540 Element_PIPE_transfer_pipe_to_part(sim, sim->parts+i, sim->parts+np, false);
541 }
542 }
543
544 }
545 return;
546 }
547