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