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