1 #include <algorithm>
2 #include "simulation/ElementCommon.h"
3 
4 static void initDeltaPos();
5 static void changeType(ELEMENT_CHANGETYPE_FUNC_ARGS);
6 
Element_ETRD()7 void Element::Element_ETRD()
8 {
9 	Identifier = "DEFAULT_PT_ETRD";
10 	Name = "ETRD";
11 	Colour = PIXPACK(0x404040);
12 	MenuVisible = 1;
13 	MenuSection = SC_ELEC;
14 	Enabled = 1;
15 
16 	Advection = 0.0f;
17 	AirDrag = 0.00f * CFDS;
18 	AirLoss = 0.90f;
19 	Loss = 0.00f;
20 	Collision = 0.0f;
21 	Gravity = 0.0f;
22 	Diffusion = 0.00f;
23 	HotAir = 0.000f	* CFDS;
24 	Falldown = 0;
25 
26 	Flammable = 0;
27 	Explosive = 0;
28 	Meltable = 0;
29 	Hardness = 1;
30 
31 	Weight = 100;
32 
33 	HeatConduct = 251;
34 	Description = "Electrode. Creates a surface that allows Plasma arcs. (Use sparingly)";
35 
36 	Properties = TYPE_SOLID|PROP_CONDUCTS|PROP_LIFE_DEC;
37 
38 	LowPressure = IPL;
39 	LowPressureTransition = NT;
40 	HighPressure = IPH;
41 	HighPressureTransition = NT;
42 	LowTemperature = ITL;
43 	LowTemperatureTransition = NT;
44 	HighTemperature = ITH;
45 	HighTemperatureTransition = NT;
46 
47 	ChangeType = &changeType;
48 
49 	initDeltaPos();
50 }
51 
changeType(ELEMENT_CHANGETYPE_FUNC_ARGS)52 static void changeType(ELEMENT_CHANGETYPE_FUNC_ARGS)
53 {
54 	if (sim->etrd_count_valid)
55 	{
56 		if (from == PT_ETRD && sim->parts[i].life == 0)
57 			sim->etrd_life0_count--;
58 		if (to == PT_ETRD && sim->parts[i].life == 0)
59 			sim->etrd_life0_count++;
60 	}
61 }
62 
63 class ETRD_deltaWithLength
64 {
65 public:
ETRD_deltaWithLength(ui::Point a,int b)66 	ETRD_deltaWithLength(ui::Point a, int b):
67 		d(a),
68 		length(b)
69 	{
70 
71 	}
72 
73 	ui::Point d;
74 	int length;
75 };
76 
77 const int maxLength = 12;
78 std::vector<ETRD_deltaWithLength> deltaPos;
79 
initDeltaPos()80 static void initDeltaPos()
81 {
82 	deltaPos.clear();
83 	for (int ry = -maxLength; ry <= maxLength; ry++)
84 		for (int rx = -maxLength; rx <= maxLength; rx++)
85 		{
86 			ui::Point d(rx, ry);
87 			if (std::abs(d.X) + std::abs(d.Y) <= maxLength)
88 				deltaPos.push_back(ETRD_deltaWithLength(d, std::abs(d.X) + std::abs(d.Y)));
89 		}
90 	std::stable_sort(deltaPos.begin(), deltaPos.end(), [](const ETRD_deltaWithLength &a, const ETRD_deltaWithLength &b) {
91 		return a.length < b.length;
92 	});
93 }
94 
Element_ETRD_nearestSparkablePart(Simulation * sim,int targetId)95 int Element_ETRD_nearestSparkablePart(Simulation *sim, int targetId)
96 {
97 	if (!sim->elementCount[PT_ETRD])
98 		return -1;
99 	if (sim->etrd_count_valid && sim->etrd_life0_count <= 0)
100 		return -1;
101 
102 	Particle *parts = sim->parts;
103 	int foundDistance = XRES + YRES;
104 	int foundI = -1;
105 	ui::Point targetPos = ui::Point(parts[targetId].x, parts[targetId].y);
106 
107 	if (sim->etrd_count_valid)
108 	{
109 		// countLife0 doesn't need recalculating, so just focus on finding the nearest particle
110 
111 		// If the simulation contains lots of particles, check near the target position first since going through all particles will be slow.
112 		// Threshold = number of positions checked, *2 because it's likely to access memory all over the place (less cache friendly) and there's extra logic needed
113 		// TODO: probably not optimal if excessive stacking is used
114 		if (sim->parts_lastActiveIndex > (int)deltaPos.size()*2)
115 		{
116 			for (std::vector<ETRD_deltaWithLength>::iterator iter = deltaPos.begin(), end = deltaPos.end(); iter != end; ++iter)
117 			{
118 				ETRD_deltaWithLength delta = (*iter);
119 				ui::Point checkPos = targetPos + delta.d;
120 				int checkDistance = delta.length;
121 				if (foundDistance < checkDistance)
122 				{
123 					// deltaPos is sorted in order of ascending length, so foundDistance < checkDistance means all later items are further away.
124 					break;
125 				}
126 				if (sim->InBounds(checkPos.X, checkPos.Y) && checkDistance <= foundDistance)
127 				{
128 					int r = sim->pmap[checkPos.Y][checkPos.X];
129 					if (r && TYP(r) == PT_ETRD && !parts[ID(r)].life && ID(r) != targetId && checkDistance < foundDistance)
130 					{
131 						foundDistance = checkDistance;
132 						foundI = ID(r);
133 					}
134 				}
135 			}
136 		}
137 		// If neighbor search didn't find a suitable particle, search all particles
138 		if (foundI < 0)
139 		{
140 			for (int i = 0; i <= sim->parts_lastActiveIndex; i++)
141 			{
142 				if (parts[i].type == PT_ETRD && !parts[i].life)
143 				{
144 					ui::Point checkPos = ui::Point(parts[i].x-targetPos.X, parts[i].y-targetPos.Y);
145 					int checkDistance = std::abs(checkPos.X) + std::abs(checkPos.Y);
146 					if (checkDistance < foundDistance && i != targetId)
147 					{
148 						foundDistance = checkDistance;
149 						foundI = i;
150 					}
151 				}
152 			}
153 		}
154 	}
155 	else
156 	{
157 		// Recalculate countLife0, and search for the closest suitable particle
158 		int countLife0 = 0;
159 		for (int i = 0; i <= sim->parts_lastActiveIndex; i++)
160 		{
161 			if (parts[i].type == PT_ETRD && !parts[i].life)
162 			{
163 				countLife0++;
164 				ui::Point checkPos = ui::Point(parts[i].x-targetPos.X, parts[i].y-targetPos.Y);
165 				int checkDistance = std::abs(checkPos.X) + std::abs(checkPos.Y);
166 				if (checkDistance < foundDistance && i != targetId)
167 				{
168 					foundDistance = checkDistance;
169 					foundI = i;
170 				}
171 			}
172 		}
173 		sim->etrd_life0_count = countLife0;
174 		sim->etrd_count_valid = true;
175 	}
176 	return foundI;
177 }
178