Line data Source code
1 0 : var API3 = function(m)
2 : {
3 :
4 : /**
5 : * The map module.
6 : * Copied with changes from QuantumState's original for qBot, it's a component for storing 8 bit values.
7 : */
8 :
9 : /** The function needs to be named too because of the copyConstructor functionality */
10 0 : m.Map = function Map(sharedScript, type, originalMap, actualCopy)
11 : {
12 : // get the correct dimensions according to the map type
13 0 : let map = type == "territory" || type == "resource" ? sharedScript.territoryMap : sharedScript.passabilityMap;
14 0 : this.width = map.width;
15 0 : this.height = map.height;
16 0 : this.cellSize = map.cellSize;
17 0 : this.length = this.width * this.height;
18 :
19 0 : this.maxVal = 255;
20 :
21 : // sanity check
22 0 : if (originalMap && originalMap.length != this.length)
23 0 : warn("AI map size incompatibility with type " + type + ": original " + originalMap.length + " new " + this.length);
24 :
25 0 : if (originalMap && actualCopy)
26 : {
27 0 : this.map = new Uint8Array(this.length);
28 0 : for (let i = 0; i < this.length; ++i)
29 0 : this.map[i] = originalMap[i];
30 : }
31 0 : else if (originalMap)
32 0 : this.map = originalMap;
33 : else
34 0 : this.map = new Uint8Array(this.length);
35 : };
36 :
37 0 : m.Map.prototype.setMaxVal = function(val)
38 : {
39 0 : this.maxVal = val;
40 : };
41 :
42 0 : m.Map.prototype.gamePosToMapPos = function(p)
43 : {
44 0 : return [Math.floor(p[0]/this.cellSize), Math.floor(p[1]/this.cellSize)];
45 : };
46 :
47 0 : m.Map.prototype.point = function(p)
48 : {
49 0 : let q = this.gamePosToMapPos(p);
50 0 : q[0] = q[0] >= this.width ? this.width-1 : q[0] < 0 ? 0 : q[0];
51 0 : q[1] = q[1] >= this.width ? this.width-1 : q[1] < 0 ? 0 : q[1];
52 0 : return this.map[q[0] + this.width * q[1]];
53 : };
54 :
55 0 : m.Map.prototype.runLoop = function(x0, x1, y0, y1, cx, cy, maxDist2, func)
56 : {
57 0 : for (let y = y0; y < y1; ++y)
58 : {
59 0 : const dy2 = (y - cy) * (y - cy);
60 0 : const yw = y * this.width;
61 0 : for (let x = x0; x < x1; ++x)
62 : {
63 0 : const dx = x - cx;
64 0 : const r2 = dx * dx + dy2;
65 0 : if (r2 >= maxDist2)
66 0 : continue;
67 0 : const w = x + yw;
68 0 : this.set(w, func(w, r2));
69 : }
70 : }
71 : };
72 :
73 0 : m.Map.prototype.addInfluence = function(cx, cy, maxDist, strength, type = "linear")
74 : {
75 0 : strength = strength ? strength : maxDist;
76 :
77 0 : const x0 = Math.floor(Math.max(0, cx - maxDist));
78 0 : const y0 = Math.floor(Math.max(0, cy - maxDist));
79 0 : const x1 = Math.floor(Math.min(this.width-1, cx + maxDist));
80 0 : const y1 = Math.floor(Math.min(this.height-1, cy + maxDist));
81 0 : const maxDist2 = maxDist * maxDist;
82 :
83 0 : if (type == "linear")
84 : {
85 0 : const str = strength / maxDist;
86 0 : this.runLoop(x0, x1, y0, y1, cx, cy, maxDist2, (w, r2) => this.map[w] + str * (maxDist - Math.sqrt(r2)));
87 : }
88 0 : else if (type == "quadratic")
89 : {
90 0 : const str = strength / maxDist2;
91 0 : this.runLoop(x0, x1, y0, y1, cx, cy, maxDist2, (w, r2) => this.map[w] + str * (maxDist2 - r2));
92 : }
93 : else
94 0 : this.runLoop(x0, x1, y0, y1, cx, cy, maxDist2, (w, r2) => this.map[w] + strength);
95 :
96 : };
97 :
98 0 : m.Map.prototype.multiplyInfluence = function(cx, cy, maxDist, strength, type = "constant")
99 : {
100 0 : strength = strength ? +strength : +maxDist;
101 :
102 0 : const x0 = Math.max(0, cx - maxDist);
103 0 : const y0 = Math.max(0, cy - maxDist);
104 0 : const x1 = Math.min(this.width, cx + maxDist);
105 0 : const y1 = Math.min(this.height, cy + maxDist);
106 0 : const maxDist2 = maxDist * maxDist;
107 :
108 0 : if (type == "linear")
109 : {
110 0 : const str = strength / maxDist;
111 0 : this.runLoop(x0, x1, y0, y1, cx, cy, maxDist2, (w, r2) => str * (maxDist - Math.sqrt(r2)) * this.map[w]);
112 : }
113 0 : else if (type == "quadratic")
114 : {
115 0 : const str = strength / maxDist2;
116 0 : this.runLoop(x0, x1, y0, y1, cx, cy, maxDist2, (w, r2) => str * (maxDist2 - r2) * this.map[w]);
117 : }
118 : else
119 0 : this.runLoop(x0, x1, y0, y1, cx, cy, maxDist2, (w, r2) => this.map[w] * strength);
120 : };
121 :
122 : /** add to current map by the parameter map pixelwise */
123 0 : m.Map.prototype.add = function(map)
124 : {
125 0 : for (let i = 0; i < this.length; ++i)
126 0 : this.set(i, this.map[i] + map.map[i]);
127 : };
128 :
129 : /** Set the value taking overflow into account */
130 0 : m.Map.prototype.set = function(i, value)
131 : {
132 0 : this.map[i] = value < 0 ? 0 : value > this.maxVal ? this.maxVal : value;
133 : };
134 :
135 : /** Find the best non-obstructed tile */
136 0 : m.Map.prototype.findBestTile = function(radius, obstruction)
137 : {
138 : let bestIdx;
139 0 : let bestVal = 0;
140 0 : for (let j = 0; j < this.length; ++j)
141 : {
142 0 : if (this.map[j] <= bestVal)
143 0 : continue;
144 0 : let i = this.getNonObstructedTile(j, radius, obstruction);
145 0 : if (i < 0)
146 0 : continue;
147 0 : bestVal = this.map[j];
148 0 : bestIdx = i;
149 : }
150 :
151 0 : return { "idx": bestIdx, "val": bestVal };
152 : };
153 :
154 : /** return any non obstructed (small) tile inside the (big) tile i from obstruction map */
155 0 : m.Map.prototype.getNonObstructedTile = function(i, radius, obstruction)
156 : {
157 0 : let ratio = this.cellSize / obstruction.cellSize;
158 0 : let ix = (i % this.width) * ratio;
159 0 : let iy = Math.floor(i / this.width) * ratio;
160 0 : let w = obstruction.width;
161 0 : let r2 = radius * radius;
162 : let lastPoint;
163 0 : for (let kx = ix; kx < ix + ratio; ++kx)
164 : {
165 0 : if (kx < radius || kx >= w - radius)
166 0 : continue;
167 0 : for (let ky = iy; ky < iy + ratio; ++ky)
168 : {
169 0 : if (ky < radius || ky >= w - radius)
170 0 : continue;
171 0 : if (lastPoint && (kx - lastPoint.x)*(kx - lastPoint.x) + (ky - lastPoint.y)*(ky - lastPoint.y) < r2)
172 0 : continue;
173 0 : lastPoint = obstruction.isObstructedTile(kx, ky, radius);
174 0 : if (!lastPoint)
175 0 : return kx + ky*w;
176 : }
177 : }
178 0 : return -1;
179 : };
180 :
181 : /** return true if the area centered on tile kx-ky and with radius is obstructed */
182 0 : m.Map.prototype.isObstructedTile = function(kx, ky, radius)
183 : {
184 0 : let w = this.width;
185 0 : if (kx < radius || kx >= w - radius || ky < radius || ky >= w - radius || this.map[kx+ky*w] == 0)
186 0 : return { "x": kx, "y": ky };
187 0 : if (!this.pattern || this.pattern[0] != radius)
188 : {
189 0 : this.pattern = [radius];
190 0 : let r2 = radius * radius;
191 0 : for (let i = 1; i <= radius; ++i)
192 0 : this.pattern.push(Math.floor(Math.sqrt(r2 - (i-0.5)*(i-0.5)) + 0.5));
193 : }
194 0 : for (let dy = 0; dy <= radius; ++dy)
195 : {
196 0 : let dxmax = this.pattern[dy];
197 0 : let xp = kx + (ky + dy)*w;
198 0 : let xm = kx + (ky - dy)*w;
199 0 : for (let dx = 0; dx <= dxmax; ++dx)
200 : {
201 0 : if (this.map[xp + dx] == 0)
202 0 : return { "x": kx + dx, "y": ky + dy };
203 0 : if (this.map[xm + dx] == 0)
204 0 : return { "x": kx + dx, "y": ky - dy };
205 0 : if (this.map[xp - dx] == 0)
206 0 : return { "x": kx - dx, "y": ky + dy };
207 0 : if (this.map[xm - dx] == 0)
208 0 : return { "x": kx - dx, "y": ky - dy };
209 : }
210 : }
211 0 : return null;
212 : };
213 :
214 0 : m.Map.prototype.dumpIm = function(name = "default.png", threshold = this.maxVal)
215 : {
216 0 : Engine.DumpImage(name, this.map, this.width, this.height, threshold);
217 : };
218 :
219 0 : return m;
220 :
221 : }(API3);
|