1 #include "common/tpt-minmax.h"
2 #include "simulation/ElementCommon.h"
3 
4 struct StackData;
5 static int update(UPDATE_FUNC_ARGS);
6 static int graphics(GRAPHICS_FUNC_ARGS);
7 static bool ctypeDraw(CTYPEDRAW_FUNC_ARGS);
8 static StackData CanMoveStack(Simulation * sim, int stackX, int stackY, int directionX, int directionY, int maxSize, int amount, bool retract, int block);
9 static int MoveStack(Simulation * sim, int stackX, int stackY, int directionX, int directionY, int maxSize, int amount, bool retract, int block, bool sticky, int callDepth = 0);
10 
Element_PSTN()11 void Element::Element_PSTN()
12 {
13 	Identifier = "DEFAULT_PT_PSTN";
14 	Name = "PSTN";
15 	Colour = PIXPACK(0xAA9999);
16 	MenuVisible = 1;
17 	MenuSection = SC_FORCE;
18 	Enabled = 1;
19 
20 	Advection = 0.0f;
21 	AirDrag = 0.00f * CFDS;
22 	AirLoss = 0.90f;
23 	Loss = 0.00f;
24 	Collision = 0.0f;
25 	Gravity = 0.0f;
26 	Diffusion = 0.00f;
27 	HotAir = 0.000f	* CFDS;
28 	Falldown = 0;
29 
30 	Flammable = 0;
31 	Explosive = 0;
32 	Meltable = 0;
33 	Hardness = 0;
34 
35 	Weight = 100;
36 
37 	DefaultProperties.temp = 10.0f + 273.15f;
38 	HeatConduct = 0;
39 	Description = "Piston, extends and pushes particles.";
40 
41 	Properties = TYPE_SOLID;
42 
43 	LowPressure = IPL;
44 	LowPressureTransition = NT;
45 	HighPressure = IPH;
46 	HighPressureTransition = NT;
47 	LowTemperature = ITL;
48 	LowTemperatureTransition = NT;
49 	HighTemperature = ITH;
50 	HighTemperatureTransition = NT;
51 
52 	Update = &update;
53 	Graphics = &graphics;
54 	CtypeDraw = &ctypeDraw;
55 }
56 
57 struct StackData
58 {
59 	int pushed;
60 	int spaces;
61 
StackDataStackData62 	StackData(int pushed, int spaces):
63 		pushed(pushed),
64 		spaces(spaces)
65 	{
66 	}
67 };
68 
69 int tempParts[XRES];
70 
71 constexpr int PISTON_INACTIVE   = 0x00;
72 constexpr int PISTON_RETRACT    = 0x01;
73 constexpr int PISTON_EXTEND     = 0x02;
74 constexpr int MAX_FRAME         = 0x0F;
75 constexpr int DEFAULT_LIMIT     = 0x1F;
76 constexpr int DEFAULT_ARM_LIMIT = 0xFF;
77 
update(UPDATE_FUNC_ARGS)78 static int update(UPDATE_FUNC_ARGS)
79 {
80  	if(parts[i].life)
81  		return 0;
82  	int maxSize = parts[i].tmp ? parts[i].tmp : DEFAULT_LIMIT;
83  	int armLimit = parts[i].tmp2 ? parts[i].tmp2 : DEFAULT_ARM_LIMIT;
84  	int state = 0;
85 	int r, nxx, nyy, nxi, nyi, rx, ry;
86 	int directionX = 0, directionY = 0;
87 	if (state == PISTON_INACTIVE) {
88 		for (rx=-2; rx<3; rx++)
89 			for (ry=-2; ry<3; ry++)
90 				if (BOUNDS_CHECK && (rx || ry) && (!rx || !ry))
91 				{
92 					r = pmap[y+ry][x+rx];
93 					if (!r)
94 						continue;
95 					if (TYP(r)==PT_SPRK && parts[ID(r)].life==3) {
96 						if(parts[ID(r)].ctype == PT_PSCN)
97 							state = PISTON_EXTEND;
98 						else
99 							state = PISTON_RETRACT;
100 					}
101 				}
102 	}
103 	if(state == PISTON_EXTEND || state == PISTON_RETRACT) {
104 		for (rx=-1; rx<2; rx++)
105 			for (ry=-1; ry<2; ry++)
106 				if (BOUNDS_CHECK && (rx || ry) && (!rx || !ry))
107 				{
108 					r = pmap[y+ry][x+rx];
109 					if (!r)
110 						continue;
111 					if (TYP(r) == PT_PSTN && !parts[ID(r)].life)
112 					{
113 						bool movedPiston = false;
114 						bool foundEnd = false;
115 						int pistonEndX, pistonEndY;
116 						int pistonCount = -1;// number of PSTN particles minus 1
117 						int newSpace = 0;
118 						int armCount = 0;
119 						directionX = rx;
120 						directionY = ry;
121 						for (nxx = 0, nyy = 0, nxi = directionX, nyi = directionY; ; nyy += nyi, nxx += nxi) {
122 							if (!(x+nxx<XRES && y+nyy<YRES && x+nxx >= 0 && y+nyy >= 0)) {
123 								break;
124 							}
125 							r = pmap[y+nyy][x+nxx];
126 							if(TYP(r)==PT_PSTN)
127 							{
128 								if(parts[ID(r)].life)
129 									armCount++;
130 								else if (armCount)
131 								{
132 									pistonEndX = x+nxx;
133 									pistonEndY = y+nyy;
134 									foundEnd = true;
135 									break;
136 								}
137 								else
138 								{
139 									pistonCount += floor((parts[ID(r)].temp-268.15)/10);// How many tens of degrees above 0 C, rounded to nearest ten degrees. Can be negative.
140 								}
141 							}
142 							else if (nxx==0 && nyy==0)
143 							{
144 								// compatibility with BAD THINGS: starting PSTN layered underneath other particles
145 								// (in v90, it started scanning from the neighbouring particle, so could not break out of loop at offset=(0,0))
146 								pistonCount += floor((parts[i].temp-268.15)/10);
147 								continue;
148 							}
149 							else
150 							{
151 								pistonEndX = x+nxx;
152 								pistonEndY = y+nyy;
153 								foundEnd = true;
154 								break;
155 							}
156 						}
157 						if(foundEnd) {
158 							if(state == PISTON_EXTEND) {
159 								if(armCount+pistonCount > armLimit)
160 									pistonCount = armLimit-armCount;
161 								if(pistonCount > 0) {
162 									newSpace = MoveStack(sim, pistonEndX, pistonEndY, directionX, directionY, maxSize, pistonCount, false, parts[i].ctype, true);
163 									if(newSpace) {
164 										//Create new piston section
165 										for(int j = 0; j < newSpace; j++) {
166 											int nr = sim->create_part(-3, pistonEndX+(nxi*j), pistonEndY+(nyi*j), PT_PSTN);
167 											if (nr > -1) {
168 												parts[nr].life = 1;
169 												if (parts[i].dcolour)
170 												{
171 													int colour=parts[i].dcolour;
172 													parts[nr].dcolour=(colour&0xFF000000)|std::max((colour&0xFF0000)-0x3C0000,0)|std::max((colour&0xFF00)-0x3C00,0)|std::max((colour&0xFF)-0x3C,0);
173 												}
174 											}
175 										}
176 										movedPiston =  true;
177 									}
178 								}
179 							} else if(state == PISTON_RETRACT) {
180 								if(pistonCount > armCount)
181 									pistonCount = armCount;
182 								if(armCount && pistonCount > 0) {
183 									MoveStack(sim, pistonEndX, pistonEndY, directionX, directionY, maxSize, pistonCount, true, parts[i].ctype, true);
184 									movedPiston = true;
185 								}
186 							}
187 						}
188 						if (movedPiston)
189 							return 0;
190 					}
191 				}
192 
193 	}
194 	return 0;
195 }
196 
CanMoveStack(Simulation * sim,int stackX,int stackY,int directionX,int directionY,int maxSize,int amount,bool retract,int block)197 static StackData CanMoveStack(Simulation * sim, int stackX, int stackY, int directionX, int directionY, int maxSize, int amount, bool retract, int block)
198 {
199 	int posX, posY, r, spaces = 0, currentPos = 0;
200 	if (amount <= 0)
201 		return StackData(0, 0);
202 	for (posX = stackX, posY = stackY; currentPos < maxSize + amount && currentPos < XRES-1; posX += directionX, posY += directionY)
203 	{
204 		if (!(posX < XRES && posY < YRES && posX >= 0 && posY >= 0))
205 			break;
206 
207 		r = sim->pmap[posY][posX];
208 		if (sim->IsWallBlocking(posX, posY, 0) || (block && TYP(r) == block))
209 			return StackData(currentPos - spaces, spaces);
210 		if (!r)
211 		{
212 			spaces++;
213 			tempParts[currentPos++] = -1;
214 			if (spaces >= amount)
215 				break;
216 		}
217 		else
218 		{
219 			if (currentPos - spaces < maxSize && (!retract || (TYP(r) == PT_FRME && posX == stackX && posY == stackY)))
220 				tempParts[currentPos++] = ID(r);
221 			else
222 				return StackData(currentPos - spaces, spaces);
223 		}
224 	}
225 	return StackData(currentPos - spaces, spaces);
226 }
227 
MoveStack(Simulation * sim,int stackX,int stackY,int directionX,int directionY,int maxSize,int amount,bool retract,int block,bool sticky,int callDepth)228 static int MoveStack(Simulation * sim, int stackX, int stackY, int directionX, int directionY, int maxSize, int amount, bool retract, int block, bool sticky, int callDepth)
229 {
230 	int posX, posY, r;
231 	r = sim->pmap[stackY][stackX];
232 	if(!callDepth && TYP(r) == PT_FRME) {
233 		int newY = !!directionX, newX = !!directionY;
234 		int realDirectionX = retract?-directionX:directionX;
235 		int realDirectionY = retract?-directionY:directionY;
236 		int maxRight = MAX_FRAME, maxLeft = MAX_FRAME;
237 
238 		//check if we can push all the FRME
239 		for(int c = retract; c < MAX_FRAME; c++) {
240 			posY = stackY + (c*newY);
241 			posX = stackX + (c*newX);
242 			if (posX < XRES && posY < YRES && posX >= 0 && posY >= 0 && TYP(sim->pmap[posY][posX]) == PT_FRME) {
243 				int spaces = CanMoveStack(sim, posX, posY, realDirectionX, realDirectionY, maxSize, amount, retract, block).spaces;
244 				if(spaces < amount)
245 					amount = spaces;
246 			} else {
247 				maxRight = c;
248 				break;
249 			}
250 		}
251 		for(int c = 1; c < MAX_FRAME; c++) {
252 			posY = stackY - (c*newY);
253 			posX = stackX - (c*newX);
254 			if (posX < XRES && posY < YRES && posX >= 0 && posY >= 0 && TYP(sim->pmap[posY][posX]) == PT_FRME) {
255 				int spaces = CanMoveStack(sim, posX, posY, realDirectionX, realDirectionY, maxSize, amount, retract, block).spaces;
256 				if(spaces < amount)
257 					amount = spaces;
258 			} else {
259 				maxLeft = c;
260 				break;
261 			}
262 		}
263 
264 		//If the piston is pushing frame, iterate out from the centre to the edge and push everything resting on frame
265 		for(int c = 1; c < maxRight; c++) {
266 			posY = stackY + (c*newY);
267 			posX = stackX + (c*newX);
268 			MoveStack(sim, posX, posY, directionX, directionY, maxSize, amount, retract, block, !sim->parts[ID(sim->pmap[posY][posX])].tmp, 1);
269 		}
270 		for(int c = 1; c < maxLeft; c++) {
271 			posY = stackY - (c*newY);
272 			posX = stackX - (c*newX);
273 			MoveStack(sim, posX, posY, directionX, directionY, maxSize, amount, retract, block, !sim->parts[ID(sim->pmap[posY][posX])].tmp, 1);
274 		}
275 
276 		//Remove arm section if retracting with FRME
277 		if (retract)
278 			for(int j = 1; j <= amount; j++)
279 				sim->kill_part(ID(sim->pmap[stackY+(directionY*-j)][stackX+(directionX*-j)]));
280 		return MoveStack(sim, stackX, stackY, directionX, directionY, maxSize, amount, retract, block, !sim->parts[ID(sim->pmap[stackY][stackX])].tmp, 1);
281 	}
282 	if(retract){
283 		bool foundParts = false;
284 		//Remove arm section if retracting without FRME
285 		if (!callDepth)
286 			for(int j = 1; j <= amount; j++)
287 				sim->kill_part(ID(sim->pmap[stackY+(directionY*-j)][stackX+(directionX*-j)]));
288 		int currentPos = 0;
289 		for(posX = stackX, posY = stackY; currentPos < maxSize && currentPos < XRES-1; posX += directionX, posY += directionY) {
290 			if (!(posX < XRES && posY < YRES && posX >= 0 && posY >= 0)) {
291 				break;
292 			}
293 			r = sim->pmap[posY][posX];
294 			if(!r || TYP(r) == block || (!sticky && TYP(r) != PT_FRME)) {
295 				break;
296 			} else {
297 				foundParts = true;
298 				tempParts[currentPos++] = ID(r);
299 			}
300 		}
301 		if(foundParts) {
302 			//Move particles
303 			for(int j = 0; j < currentPos; j++) {
304 				int jP = tempParts[j];
305 				int srcX = (int)(sim->parts[jP].x + 0.5f), srcY = (int)(sim->parts[jP].y + 0.5f);
306 				int destX = srcX-directionX*amount, destY = srcY-directionY*amount;
307 				sim->pmap[srcY][srcX] = 0;
308 				sim->parts[jP].x = destX;
309 				sim->parts[jP].y = destY;
310 				sim->pmap[destY][destX] = PMAP(jP, sim->parts[jP].type);
311 			}
312 			return amount;
313 		}
314 	} else {
315 		StackData stackData = CanMoveStack(sim, stackX, stackY, directionX, directionY, maxSize, amount, retract, block);
316 		int currentPos = stackData.pushed + stackData.spaces;
317 		if(currentPos){
318 			//Move particles
319 			int possibleMovement = 0;
320 			for(int j = currentPos-1; j >= 0; j--) {
321 				int jP = tempParts[j];
322 				if(jP < 0) {
323 					possibleMovement++;
324 					continue;
325 				}
326 				if(!possibleMovement)
327 					continue;
328 				int srcX = (int)(sim->parts[jP].x + 0.5f), srcY = (int)(sim->parts[jP].y + 0.5f);
329 				int destX = srcX+directionX*possibleMovement, destY = srcY+directionY*possibleMovement;
330 				sim->pmap[srcY][srcX] = 0;
331 				sim->parts[jP].x = destX;
332 				sim->parts[jP].y = destY;
333 				sim->pmap[destY][destX] = PMAP(jP, sim->parts[jP].type);
334 			}
335 			return possibleMovement;
336 		}
337 	}
338 	return 0;
339 }
340 
graphics(GRAPHICS_FUNC_ARGS)341 static int graphics(GRAPHICS_FUNC_ARGS)
342 {
343 	if(cpart->life)
344 	{
345 		*colr -= 60;
346 		*colg -= 60;
347 	}
348 	return 0;
349 }
350 
ctypeDraw(CTYPEDRAW_FUNC_ARGS)351 static bool ctypeDraw(CTYPEDRAW_FUNC_ARGS)
352 {
353 	if (t == PT_FRME)
354 	{
355 		return false;
356 	}
357 	return Element::basicCtypeDraw(CTYPEDRAW_FUNC_SUBCALL_ARGS);
358 }
359