1 #include "simulation/ElementCommon.h"
2
3 static int update(UPDATE_FUNC_ARGS);
4 static int graphics(GRAPHICS_FUNC_ARGS);
5 static void create(ELEMENT_CREATE_FUNC_ARGS);
6 static void create_line_par(Simulation * sim, int x1, int y1, int x2, int y2, int c, int temp, int life, int tmp, int tmp2);
7
Element_LIGH()8 void Element::Element_LIGH()
9 {
10 Identifier = "DEFAULT_PT_LIGH";
11 Name = "LIGH";
12 Colour = PIXPACK(0xFFFFC0);
13 MenuVisible = 1;
14 MenuSection = SC_EXPLOSIVE;
15 Enabled = 1;
16
17 Advection = 0.0f;
18 AirDrag = 0.00f * CFDS;
19 AirLoss = 0.90f;
20 Loss = 0.00f;
21 Collision = 0.0f;
22 Gravity = 0.0f;
23 Diffusion = 0.00f;
24 HotAir = 0.000f * CFDS;
25 Falldown = 0;
26
27 Flammable = 0;
28 Explosive = 0;
29 Meltable = 0;
30 Hardness = 1;
31
32 Weight = 100;
33
34 HeatConduct = 0;
35 Description = "Lightning. Change the brush size to set the size of the lightning.";
36
37 Properties = TYPE_SOLID;
38
39 LowPressure = IPL;
40 LowPressureTransition = NT;
41 HighPressure = IPH;
42 HighPressureTransition = NT;
43 LowTemperature = ITL;
44 LowTemperatureTransition = NT;
45 HighTemperature = ITH;
46 HighTemperatureTransition = NT;
47
48 Update = &update;
49 Graphics = &graphics;
50 Create = &create;
51 }
52
53 constexpr float LIGHTING_POWER = 0.65f;
54
update(UPDATE_FUNC_ARGS)55 static int update(UPDATE_FUNC_ARGS)
56 {
57 /*
58 *
59 * tmp2:
60 * -1 - part will be removed
61 * 0 - "branches" of the lightning
62 * 1 - bending
63 * 2 - branching
64 * 3 - transfer spark or make destruction
65 * 4 - first pixel
66 *
67 * life - "thickness" of lighting (but anyway one pixel)
68 *
69 * tmp - angle of lighting, measured in degrees anticlockwise from the positive x direction
70 *
71 */
72 int r,rx,ry,rt, multipler, powderful;
73 float angle, angle2=-1;
74 powderful = parts[i].temp*(1+parts[i].life/40)*LIGHTING_POWER;
75 //Element_FIRE::update(UPDATE_FUNC_SUBCALL_ARGS);
76 if (sim->aheat_enable)
77 {
78 sim->hv[y/CELL][x/CELL] += powderful/50;
79 if (sim->hv[y/CELL][x/CELL] > MAX_TEMP)
80 sim->hv[y/CELL][x/CELL] = MAX_TEMP;
81 // If the LIGH was so powerful that it overflowed hv, set to max temp
82 else if (sim->hv[y/CELL][x/CELL] < 0)
83 sim->hv[y/CELL][x/CELL] = MAX_TEMP;
84 }
85
86 for (rx=-2; rx<3; rx++)
87 for (ry=-2; ry<3; ry++)
88 if (BOUNDS_CHECK && (rx || ry))
89 {
90 r = pmap[y+ry][x+rx];
91 if (!r)
92 continue;
93 rt = TYP(r);
94 if ((surround_space || sim->elements[rt].Explosive) &&
95 (rt!=PT_SPNG || parts[ID(r)].life==0) &&
96 sim->elements[rt].Flammable && RNG::Ref().chance(sim->elements[rt].Flammable + sim->pv[(y+ry)/CELL][(x+rx)/CELL] * 10.0f, 1000))
97 {
98 sim->part_change_type(ID(r),x+rx,y+ry,PT_FIRE);
99 parts[ID(r)].temp = restrict_flt(sim->elements[PT_FIRE].DefaultProperties.temp + (sim->elements[rt].Flammable/2), MIN_TEMP, MAX_TEMP);
100 parts[ID(r)].life = RNG::Ref().between(180, 259);
101 parts[ID(r)].tmp = parts[ID(r)].ctype = 0;
102 if (sim->elements[rt].Explosive)
103 sim->pv[y/CELL][x/CELL] += 0.25f * CFDS;
104 }
105 switch (rt)
106 {
107 case PT_LIGH:
108 case PT_TESC:
109 continue;
110 case PT_CLNE:
111 case PT_THDR:
112 case PT_DMND:
113 case PT_FIRE:
114 parts[ID(r)].temp = restrict_flt(parts[ID(r)].temp+powderful/10, MIN_TEMP, MAX_TEMP);
115 continue;
116 case PT_DEUT:
117 case PT_PLUT:
118 parts[ID(r)].temp = restrict_flt(parts[ID(r)].temp+powderful, MIN_TEMP, MAX_TEMP);
119 sim->pv[y/CELL][x/CELL] +=powderful/35;
120 if (RNG::Ref().chance(1, 3))
121 {
122 sim->part_change_type(ID(r),x+rx,y+ry,PT_NEUT);
123 parts[ID(r)].life = RNG::Ref().between(480, 959);
124 parts[ID(r)].vx = RNG::Ref().between(-5, 5);
125 parts[ID(r)].vy = RNG::Ref().between(-5, 5);
126 }
127 break;
128 case PT_COAL:
129 case PT_BCOL:
130 if (parts[ID(r)].life>100)
131 parts[ID(r)].life = 99;
132 break;
133 case PT_STKM:
134 if (sim->player.elem!=PT_LIGH)
135 parts[ID(r)].life-=powderful/100;
136 break;
137 case PT_STKM2:
138 if (sim->player2.elem!=PT_LIGH)
139 parts[ID(r)].life-=powderful/100;
140 break;
141 case PT_HEAC:
142 parts[ID(r)].temp = restrict_flt(parts[ID(r)].temp+powderful/10, MIN_TEMP, MAX_TEMP);
143 if (parts[ID(r)].temp > sim->elements[PT_HEAC].HighTemperature)
144 {
145 sim->part_change_type(ID(r), x+rx, y+ry, PT_LAVA);
146 parts[ID(r)].ctype = PT_HEAC;
147 }
148 break;
149 default:
150 break;
151 }
152 if ((sim->elements[TYP(r)].Properties&PROP_CONDUCTS) && parts[ID(r)].life==0)
153 sim->create_part(ID(r),x+rx,y+ry,PT_SPRK);
154 sim->pv[y/CELL][x/CELL] += powderful/400;
155 if (sim->elements[TYP(r)].HeatConduct) parts[ID(r)].temp = restrict_flt(parts[ID(r)].temp+powderful/1.3, MIN_TEMP, MAX_TEMP);
156 }
157 if (parts[i].tmp2==3)
158 {
159 parts[i].tmp2=0;
160 return 1;
161 }
162 else if (parts[i].tmp2<=-1)
163 {
164 sim->kill_part(i);
165 return 1;
166 }
167 else if (parts[i].tmp2<=0 || parts[i].life<=1)
168 {
169 if (parts[i].tmp2>0)
170 parts[i].tmp2=0;
171 parts[i].tmp2--;
172 return 1;
173 }
174
175 angle = (parts[i].tmp + RNG::Ref().between(-30, 30)) % 360;
176 multipler = parts[i].life * 1.5 + RNG::Ref().between(0, parts[i].life);
177 rx=cos(angle*M_PI/180)*multipler;
178 ry=-sin(angle*M_PI/180)*multipler;
179 create_line_par(sim, x, y, x+rx, y+ry, PT_LIGH, parts[i].temp, parts[i].life, angle, parts[i].tmp2);
180 if (parts[i].tmp2==2)// && pNear==-1)
181 {
182 angle2 = ((int)angle + RNG::Ref().between(-100, 100)) % 360;
183 rx=cos(angle2*M_PI/180)*multipler;
184 ry=-sin(angle2*M_PI/180)*multipler;
185 create_line_par(sim, x, y, x+rx, y+ry, PT_LIGH, parts[i].temp, parts[i].life, angle2, parts[i].tmp2);
186 }
187
188 parts[i].tmp2=-1;
189 return 1;
190 }
191
create_LIGH(Simulation * sim,int x,int y,int c,int temp,int life,int tmp,int tmp2,bool last)192 static bool create_LIGH(Simulation * sim, int x, int y, int c, int temp, int life, int tmp, int tmp2, bool last)
193 {
194 int p = sim->create_part(-1, x, y,c);
195 if (p != -1)
196 {
197 sim->parts[p].temp = temp;
198 sim->parts[p].tmp = tmp;
199 if (last)
200 {
201 sim->parts[p].tmp2 = 1 + (RNG::Ref().between(0, 199) > tmp2*tmp2/10+60);
202 sim->parts[p].life = (int)(life/1.5 - RNG::Ref().between(0, 1));
203 }
204 else
205 {
206 sim->parts[p].life = life;
207 sim->parts[p].tmp2 = 0;
208 }
209 }
210 else if (x >= 0 && x < XRES && y >= 0 && y < YRES)
211 {
212 int r = sim->pmap[y][x];
213 if (((TYP(r)==PT_VOID || (TYP(r)==PT_PVOD && sim->parts[ID(r)].life >= 10)) && (!sim->parts[ID(r)].ctype || (sim->parts[ID(r)].ctype==c)!=(sim->parts[ID(r)].tmp&1))) || TYP(r)==PT_BHOL || TYP(r)==PT_NBHL) // VOID, PVOD, VACU, and BHOL eat LIGH here
214 return true;
215 }
216 else return true;
217 return false;
218 }
219
create_line_par(Simulation * sim,int x1,int y1,int x2,int y2,int c,int temp,int life,int tmp,int tmp2)220 static void create_line_par(Simulation * sim, int x1, int y1, int x2, int y2, int c, int temp, int life, int tmp, int tmp2)
221 {
222 bool reverseXY = abs(y2-y1) > abs(x2-x1), back = false;
223 int x, y, dx, dy, Ystep;
224 float e = 0.0f, de;
225 if (reverseXY)
226 {
227 y = x1;
228 x1 = y1;
229 y1 = y;
230 y = x2;
231 x2 = y2;
232 y2 = y;
233 }
234 if (x1 > x2)
235 back = 1;
236 dx = x2 - x1;
237 dy = abs(y2 - y1);
238 if (dx)
239 de = dy/(float)dx;
240 else
241 de = 0.0f;
242 y = y1;
243 Ystep = (y1<y2) ? 1 : -1;
244 if (!back)
245 {
246 for (x = x1; x <= x2; x++)
247 {
248 bool ret;
249 if (reverseXY)
250 ret = create_LIGH(sim, y, x, c, temp, life, tmp, tmp2,x==x2);
251 else
252 ret = create_LIGH(sim, x, y, c, temp, life, tmp, tmp2,x==x2);
253 if (ret)
254 return;
255
256 e += de;
257 if (e >= 0.5f)
258 {
259 y += Ystep;
260 e -= 1.0f;
261 }
262 }
263 }
264 else
265 {
266 for (x = x1; x >= x2; x--)
267 {
268 bool ret;
269 if (reverseXY)
270 ret = create_LIGH(sim, y, x, c, temp, life, tmp, tmp2,x==x2);
271 else
272 ret = create_LIGH(sim, x, y, c, temp, life, tmp, tmp2,x==x2);
273 if (ret)
274 return;
275
276 e += de;
277 if (e <= -0.5f)
278 {
279 y += Ystep;
280 e += 1.0f;
281 }
282 }
283 }
284 }
285
graphics(GRAPHICS_FUNC_ARGS)286 static int graphics(GRAPHICS_FUNC_ARGS)
287 {
288 *firea = 120;
289 *firer = *colr = 235;
290 *fireg = *colg = 245;
291 *fireb = *colb = 255;
292 *pixel_mode |= PMODE_GLOW | FIRE_ADD;
293 return 1;
294 }
295
create(ELEMENT_CREATE_FUNC_ARGS)296 static void create(ELEMENT_CREATE_FUNC_ARGS)
297 {
298 float gx, gy, gsize;
299 if (v >= 0)
300 {
301 if (v > 55)
302 v = 55;
303 sim->parts[i].life = v;
304 }
305 else
306 sim->parts[i].life = 30;
307 sim->parts[i].temp = sim->parts[i].life * 150.0f; // temperature of the lightning shows the power of the lightning
308 sim->GetGravityField(x, y, 1.0f, 1.0f, gx, gy);
309 gsize = gx * gx + gy * gy;
310 if (gsize < 0.0016f)
311 {
312 float angle = RNG::Ref().between(0, 6283) * 0.001f; //(in radians, between 0 and 2*pi)
313 gsize = sqrtf(gsize);
314 // randomness in weak gravity fields (more randomness with weaker fields)
315 gx += cosf(angle) * (0.04f - gsize);
316 gy += sinf(angle) * (0.04f - gsize);
317 }
318 sim->parts[i].tmp = (static_cast<int>(atan2f(-gy, gx) * (180.0f / M_PI)) + RNG::Ref().between(-20, 20) + 360) % 360;
319 sim->parts[i].tmp2 = 4;
320 }
321