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