1 ////////////////////////////////////////////////////////////////////
2 // This file contains functionality to place walls on random maps //
3 ////////////////////////////////////////////////////////////////////
6 // Check if all wall placement methods work with wall elements with entity === undefined (some still might raise errors in that case)
7 // Rename wall elements to fit the entity names so that entity = "structures/" + "civ + "_" + wallElement.type in the common case (as far as possible)
8 // Perhaps add Roman army camp to style palisades and add upgraded/balanced default palisade fortress types matching civ default fortresses strength
9 // Perhaps add further wall elements cornerInHalf, cornerOutHalf (banding PI/4) and adjust default fortress types to better fit in the octagonal territory of a civil center
10 // Perhaps swap angle and width in WallElement class(?) definition
11 // Adjust argument order to be always the same:
12 // Coordinates (center/start/target)
13 // Wall element arguments (wall/wallPart/fortressType/cornerElement)
14 // playerId (optional, default is 0/gaia)
15 // wallStyle (optional, default is the players civ/"palisades for gaia")
16 // angle/orientation (optional, default is 0)
17 // other (all optional) arguments especially those hard to define (wallPartsAssortment, maybe make an own function for it)
18 // Some arguments don't clearly match to this concept:
19 // endWithFirst (wall or other)
20 // skipFirstWall (wall or other)
21 // gateOccurence (wall or other)
22 // numCorners (wall or other)
23 // skipFirstWall (wall or other)
24 // maxAngle (angle or other)
25 // maxBendOff (angle or other, unused ATM!!!)
28 // Add treasures to wall style "others"
29 // Adjust documentation
30 // Perhaps rename "endLeft" to "start" and "endRight" to "end"
31 // ?Use available civ-type wall elements rather than palisades: Remove "endLeft" and "endRight" as default wall elements and adjust default palisade fortress types?
32 // ?Remove "endRight", "endLeft" and adjust generic fortress types palisades?
33 // ?Think of something to enable splitting walls into two walls so more complex walls can be build and roads can have branches/crossroads?
34 // ?Readjust placement angle for wall elements with bending when used in linear/circular walls by their bending?
36 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
37 // WallElement class definition
39 // Concept: If placed unrotated the wall's course is towards positive Y (top) with "outside" right (+X) and "inside" left (-X) like unrotated entities has their drop-points right (in rmgen)
40 // The course of the wall will be changed by corners (bending != 0) and so the "inside"/"outside" direction
42 // type Descriptive string, example: "wallLong". NOTE: Not really needed. Mainly for custom wall elements and to get the wall element type in code
43 // entity Optional. Template name string of the entity to be placed, example: "structures/cart_wall_long". Default is undefined (No entity placed)
44 // angle Optional. The angle (float) added to place the entity so "outside" is right when the wall element is placed unrotated. Default is 0
45 // width Optional. How far this wall element lengthens the wall (float), if unrotated the Y space needed. Default is 0
46 // indent Optional. The lateral indentation of the entity, drawn "inside" (positive values) or pushed "outside" (negative values). Default is 0
47 // bending Optional. How the course of the wall is changed after this element, positive is bending "in"/left/counter clockwise (like entity placement)
48 // NOTE: Bending is not supported by all placement functions (see there)
49 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
50 function WallElement(type, entity, angle, width, indent, bending)
53 // Default wall element type documentation:
54 // Lengthening straight blocking (mainly left/right symmetric) wall elements (Walls and wall fortifications)
55 // "wall" A blocking straight wall element that mainly lengthens the wall, self-explanatory
56 // "wallShort" self-explanatory
57 // "wallLong" self-explanatory
58 // "tower" A blocking straight wall element with damage potential (but for palisades) that slightly lengthens the wall, example: wall tower, palisade tower(No attack)
59 // "wallFort" A blocking straight wall element with massive damage potential that lengthens the wall, example: fortress, palisade fort
60 // Lengthening straight non/custom blocking (mainly left/right symmetric) wall elements (Gates and entries)
61 // "gate" A blocking straight wall element with passability determined by owner, example: gate (Functionality not yet implemented)
62 // "entry" A non-blocking straight wall element (same width as gate) but without an actual template or just a flag/column/obelisk
63 // "entryTower" A non-blocking straight wall element (same width as gate) represented by a single (maybe indented) template, example: defence tower, wall tower, outpost, watchtower
64 // "entryFort" A non-blocking straight wall element represented by a single (maybe indented) template, example: fortress, palisade fort
65 // Bending wall elements (Wall corners)
66 // "cornerIn" A wall element bending the wall by PI/2 "inside" (left, +, see above), example: wall tower, palisade curve
67 // "cornerOut" A wall element bending the wall by PI/2 "outside" (right, -, see above), example: wall tower, palisade curve
68 // "cornerHalfIn" A wall element bending the wall by PI/4 "inside" (left, +, see above), example: wall tower, palisade curve. NOTE: Not yet implemented
69 // "cornerHalfOut" A wall element bending the wall by PI/4 "outside" (right, -, see above), example: wall tower, palisade curve. NOTE: Not yet implemented
70 // Zero length straight indented (mainly left/right symmetric) wall elements (Outposts/watchtowers and non-defensive base structures)
71 // "outpost" A zero-length wall element without bending far indented so it stands outside the wall, example: outpost, defence tower, watchtower
72 // "house" A zero-length wall element without bending far indented so it stands inside the wall that grants population bonus, example: house, hut, longhouse
73 // "barracks" A zero-length wall element without bending far indented so it stands inside the wall that grants unit production, example: barracks, tavern, ...
75 this.angle = angle !== undefined ? angle : 0;
76 this.width = width !== undefined ? width : 0;
77 this.indent = indent !== undefined ? indent : 0;
78 this.bending = bending !== undefined ? bending : 0;
81 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
82 // Fortress class definition
84 // A "fortress" here is a closed wall build of multiple wall elements attached together defined in Fortress.wall
85 // It's mainly the abstract shape defined in a Fortress instances wall because different styles can be used for it (see wallStyles)
87 // type Descriptive string, example: "tiny". Not really needed (WallTool.wallTypes["type string"] is used). Mainly for custom wall elements
88 // wall Optional. Array of wall element strings. Can be set afterwards. Default is an epty array.
89 // Example: ["entrance", "wall", "cornerIn", "wall", "gate", "wall", "entrance", "wall", "cornerIn", "wall", "gate", "wall", "cornerIn", "wall"]
90 // centerToFirstElement Optional. Object with properties "x" and "y" representing a vector from the visual center to the first wall element. Default is undefined
91 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
92 function Fortress(type, wall, centerToFirstElement)
94 this.type = type; // Only usefull to get the type of the actual fortress
95 this.wall = wall !== undefined ? wall : [];
96 this.centerToFirstElement = undefined;
99 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
100 // wallStyles data structure for default wall styles
102 // A wall style is an associative array with all wall elements of that style in it associated with the wall element type string
103 // wallStyles holds all the wall styles within an associative array with the civ string or another descriptive strings as key
104 // Examples: "athen", "rome_siege", "palisades", "fence", "road"
105 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
108 // Generic civ dependent wall style definition. "rome_siege" needs some tweek...
109 var wallScaleByType = {
125 for (var style in wallScaleByType)
128 if (style == "rome_siege")
131 wallStyles[style] = {
132 // Default wall elements
133 "tower": new WallElement("tower", "structures/" + style + "_wall_tower", PI, wallScaleByType[style]),
134 "endLeft": new WallElement("endLeft", "structures/" + style + "_wall_tower", PI, wallScaleByType[style]), // Same as tower. To be compatible with palisades...
135 "endRight": new WallElement("endRight", "structures/" + style + "_wall_tower", PI, wallScaleByType[style]), // Same as tower. To be compatible with palisades...
136 "cornerIn": new WallElement("cornerIn", "structures/" + style + "_wall_tower", 5/4*PI, 0, 0.35 * wallScaleByType[style], PI/2), // 2^0.5 / 4 ~= 0.35 ~= 1/3
137 "cornerOut": new WallElement("cornerOut", "structures/" + style + "_wall_tower", 3/4*PI, 0.71 * wallScaleByType[style], 0, -PI/2), // 2^0.5 / 2 ~= 0.71 ~= 2/3
138 "wallShort": new WallElement("wallShort", "structures/" + style + "_wall_short", 0, 2*wallScaleByType[style]),
139 "wall": new WallElement("wall", "structures/" + style + "_wall_medium", 0, 4*wallScaleByType[style]),
140 "wallMedium": new WallElement("wall", "structures/" + style + "_wall_medium", 0, 4*wallScaleByType[style]),
141 "wallLong": new WallElement("wallLong", "structures/" + style + "_wall_long", 0, 6*wallScaleByType[style]),
143 // Gate and entrance wall elements
144 "gate": new WallElement("gate", "structures/" + style + "_wall_gate", PI, 6*wallScaleByType[style]),
145 "entry": new WallElement("entry", undefined, 0, 6*wallScaleByType[style]),
146 "entryTower": new WallElement("entryTower", "structures/" + civ + "_defense_tower", PI, 6*wallScaleByType[style], -4*wallScaleByType[style]),
147 "entryFort": new WallElement("entryFort", "structures/" + civ + "_fortress", 0, 8*wallScaleByType[style], 6*wallScaleByType[style]),
149 // Defensive wall elements with 0 width outside the wall
150 "outpost": new WallElement("outpost", "structures/" + civ + "_outpost", PI, 0, -4*wallScaleByType[style]),
151 "defenseTower": new WallElement("defenseTower", "structures/" + civ + "_defense_tower", PI, 0, -4*wallScaleByType[style]),
153 // Base buildings wall elements with 0 width inside the wall
154 "barracks": new WallElement("barracks", "structures/" + civ + "_barracks", PI, 0, 4.5*wallScaleByType[style]),
155 "civilCentre": new WallElement("civilCentre", "structures/" + civ + "_civil_centre", PI, 0, 4.5*wallScaleByType[style]),
156 "farmstead": new WallElement("farmstead", "structures/" + civ + "_farmstead", PI, 0, 4.5*wallScaleByType[style]),
157 "field": new WallElement("field", "structures/" + civ + "_field", PI, 0, 4.5*wallScaleByType[style]),
158 "fortress": new WallElement("fortress", "structures/" + civ + "_fortress", PI, 0, 4.5*wallScaleByType[style]),
159 "house": new WallElement("house", "structures/" + civ + "_house", PI, 0, 4.5*wallScaleByType[style]),
160 "market": new WallElement("market", "structures/" + civ + "_market", PI, 0, 4.5*wallScaleByType[style]),
161 "storehouse": new WallElement("storehouse", "structures/" + civ + "_storehouse", PI, 0, 4.5*wallScaleByType[style]),
162 "temple": new WallElement("temple", "structures/" + civ + "_temple", PI, 0, 4.5*wallScaleByType[style]),
164 // Generic space/gap wall elements
165 "space1": new WallElement("space1", undefined, 0, 1*wallScaleByType[style]),
166 "space2": new WallElement("space2", undefined, 0, 2*wallScaleByType[style]),
167 "space3": new WallElement("space3", undefined, 0, 3*wallScaleByType[style]),
168 "space4": new WallElement("space4", undefined, 0, 4*wallScaleByType[style])
172 // Add wall fortresses for all generic styles
173 wallStyles.athen.wallFort = new WallElement("wallFort", "structures/athen_fortress", 2*PI/2, 5.1, 1.9);
174 wallStyles.brit.wallFort = new WallElement("wallFort", "structures/brit_fortress", PI, 2.8);
175 wallStyles.cart.wallFort = new WallElement("wallFort", "structures/cart_fortress", PI, 5.1, 1.6);
176 wallStyles.gaul.wallFort = new WallElement("wallFort", "structures/gaul_fortress", PI, 4.2, 1.5);
177 wallStyles.iber.wallFort = new WallElement("wallFort", "structures/iber_fortress", PI, 5, 0.2);
178 wallStyles.mace.wallFort = new WallElement("wallFort", "structures/mace_fortress", 2*PI/2, 5.1, 1.9);
179 wallStyles.maur.wallFort = new WallElement("wallFort", "structures/maur_fortress", PI, 5.5);
180 wallStyles.pers.wallFort = new WallElement("wallFort", "structures/pers_fortress", PI, 5.6, 1.9);
181 wallStyles.ptol.wallFort = new WallElement("wallFort", "structures/ptol_fortress", 2*PI/2, 5.1, 1.9);
182 wallStyles.rome.wallFort = new WallElement("wallFort", "structures/rome_fortress", PI, 6.3, 2.1);
183 wallStyles.sele.wallFort = new WallElement("wallFort", "structures/sele_fortress", 2*PI/2, 5.1, 1.9);
184 wallStyles.spart.wallFort = new WallElement("wallFort", "structures/spart_fortress", 2*PI/2, 5.1, 1.9);
186 // Adjust "rome_siege" style
187 wallStyles.rome_siege.wallFort = new WallElement("wallFort", "structures/rome_army_camp", PI, 7.2, 2);
188 wallStyles.rome_siege.entryFort = new WallElement("entryFort", "structures/rome_army_camp", PI, 12, 7);
189 wallStyles.rome_siege.house = new WallElement("house", "structures/rome_tent", PI, 0, 4);
191 // Add special wall styles not well to implement generic (and to show how custom styles can be added)
193 wallScaleByType.palisades = 0.55;
194 let gate = new WallElement("gate", "other/palisades_rocks_gate", PI, 3.6);
195 wallStyles.palisades = {
196 "wall": new WallElement("wall", "other/palisades_rocks_medium", 0, 2.3),
197 "wallMedium": new WallElement("wall", "other/palisades_rocks_medium", 0, 2.3),
198 "wallLong": new WallElement("wall", "other/palisades_rocks_long", 0, 3.5),
199 "wallShort": new WallElement("wall", "other/palisades_rocks_short", 0, 1.2),
200 "tower": new WallElement("tower", "other/palisades_rocks_tower", -PI/2, 0.7),
201 "wallFort": new WallElement("wallFort", "other/palisades_rocks_fort", PI, 1.7),
203 "entry": new WallElement("entry", undefined, gate.angle, gate.width),
204 "entryTower": new WallElement("entryTower", "other/palisades_rocks_watchtower", 0, gate.width, -3),
205 "entryFort": new WallElement("entryFort", "other/palisades_rocks_fort", PI, 6, 3),
206 "cornerIn": new WallElement("cornerIn", "other/palisades_rocks_curve", 3*PI/4, 2.1, 0.7, PI/2),
207 "cornerOut": new WallElement("cornerOut", "other/palisades_rocks_curve", 5*PI/4, 2.1, -0.7, -PI/2),
208 "outpost": new WallElement("outpost", "other/palisades_rocks_outpost", PI, 0, -2),
209 "house": new WallElement("house", "other/celt_hut", PI, 0, 5),
210 "barracks": new WallElement("barracks", "structures/gaul_tavern", PI, 0, 5),
211 "endRight": new WallElement("endRight", "other/palisades_rocks_end", -PI/2, 0.2),
212 "endLeft": new WallElement("endLeft", "other/palisades_rocks_end", PI/2, 0.2)
215 // NOTE: This is not a wall style in the common sense. Use with care!
217 "short": new WallElement("road", "actor|props/special/eyecandy/road_temperate_short.xml", PI/2, 4.5),
218 "long": new WallElement("road", "actor|props/special/eyecandy/road_temperate_long.xml", PI/2, 9.5),
220 // Correct width by -2*indent to fit xStraicht/corner
221 "cornerLeft": new WallElement("road", "actor|props/special/eyecandy/road_temperate_corner.xml", -PI/2, 4.5-2*1.25, 1.25, PI/2),
222 "cornerRight": new WallElement("road", "actor|props/special/eyecandy/road_temperate_corner.xml", 0, 4.5-2*1.25, -1.25, -PI/2),
223 "curveLeft": new WallElement("road", "actor|props/special/eyecandy/road_temperate_curve_small.xml", -PI/2, 4.5+2*0.2, -0.2, PI/2),
224 "curveRight": new WallElement("road", "actor|props/special/eyecandy/road_temperate_curve_small.xml", 0, 4.5+2*0.2, 0.2, -PI/2),
226 "start": new WallElement("road", "actor|props/special/eyecandy/road_temperate_end.xml", PI/2, 2),
227 "end": new WallElement("road", "actor|props/special/eyecandy/road_temperate_end.xml", -PI/2, 2),
228 "xStraight": new WallElement("road", "actor|props/special/eyecandy/road_temperate_intersect_x.xml", 0, 4.5),
229 "xLeft": new WallElement("road", "actor|props/special/eyecandy/road_temperate_intersect_x.xml", 0, 4.5, 0, PI/2),
230 "xRight": new WallElement("road", "actor|props/special/eyecandy/road_temperate_intersect_x.xml", 0, 4.5, 0, -PI/2),
231 "tLeft": new WallElement("road", "actor|props/special/eyecandy/road_temperate_intersect_T.xml", PI, 4.5, 1.25),
232 "tRight": new WallElement("road", "actor|props/special/eyecandy/road_temperate_intersect_T.xml", 0, 4.5, -1.25),
235 // NOTE: This is not a wall style in the common sense. Use with care!
237 "fence": new WallElement("fence", "other/fence_long", -PI/2, 3.1),
238 "fence_medium": new WallElement("fence", "other/fence_long", -PI/2, 3.1),
239 "fence_short": new WallElement("fence_short", "other/fence_short", -PI/2, 1.5),
240 "fence_stone": new WallElement("fence_stone", "other/fence_stone", -PI/2, 2.5),
241 "palisade": new WallElement("palisade", "other/palisades_rocks_short", 0, 1.2),
242 "column": new WallElement("column", "other/column_doric", 0, 1),
243 "obelisk": new WallElement("obelisk", "other/obelisk", 0, 2),
244 "spike": new WallElement("spike", "other/palisades_angle_spike", -PI/2, 1),
245 "bench": new WallElement("bench", "other/bench", PI/2, 1.5),
246 "benchForTable": new WallElement("benchForTable", "other/bench", 0, 0.5),
247 "table": new WallElement("table", "other/table_rectangle", 0, 1),
248 "table_square": new WallElement("table_square", "other/table_square", PI/2, 1),
249 "flag": new WallElement("flag", "special/rallypoint", PI, 1),
250 "standing_stone": new WallElement("standing_stone", "gaia/special_ruins_standing_stone", PI, 1),
251 "settlement": new WallElement("settlement", "gaia/special_settlement", PI, 6),
252 "gap": new WallElement("gap", undefined, 0, 2),
253 "gapSmall": new WallElement("gapSmall", undefined, 0, 1),
254 "gapLarge": new WallElement("gapLarge", undefined, 0, 4),
255 "cornerIn": new WallElement("cornerIn", undefined, 0, 0, 0, PI/2),
256 "cornerOut": new WallElement("cornerOut", undefined, 0, 0, 0, -PI/2)
259 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
260 // fortressTypes data structure for some default fortress types
262 // A fortress type is just an instance of the Fortress class with actually something in it
263 // fortressTypes holds all the fortresses within an associative array with a descriptive string as key (e.g. matching the map size)
264 // Examples: "tiny", "veryLarge"
265 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
266 var fortressTypes = {};
270 "tiny": ["gate", "tower", "wallShort", "cornerIn", "wallShort", "tower"],
271 "small": ["gate", "tower", "wall", "cornerIn", "wall", "tower"],
272 "medium": ["gate", "tower", "wallLong", "cornerIn", "wallLong", "tower"],
273 "normal": ["gate", "tower", "wall", "cornerIn", "wall",
274 "cornerOut", "wall", "cornerIn", "wall", "tower"],
275 "large": ["gate", "tower", "wallLong", "cornerIn", "wallLong",
276 "cornerOut", "wallLong", "cornerIn", "wallLong", "tower"],
277 "veryLarge": ["gate", "tower", "wall", "cornerIn", "wall", "cornerOut", "wallLong",
278 "cornerIn", "wallLong", "cornerOut", "wall", "cornerIn", "wall", "tower"],
279 "giant": ["gate", "tower", "wallLong", "cornerIn", "wallLong", "cornerOut", "wallLong",
280 "cornerIn", "wallLong", "cornerOut", "wallLong", "cornerIn", "wallLong", "tower"]
283 for (let type in wallParts)
285 fortressTypes[type] = new Fortress(type);
287 let wp = wallParts[type];
288 fortressTypes[type].wall = wp.concat(wp, wp, wp);
292 // Setup some better looking semi default fortresses for "palisades" style
293 for (let type in fortressTypes)
295 var newKey = type + "Palisades";
296 var oldWall = fortressTypes[type].wall;
297 fortressTypes[newKey] = new Fortress(newKey);
298 var fillTowersBetween = ["wallShort", "wall", "wallLong", "endLeft", "endRight", "cornerIn", "cornerOut"];
299 for (var j = 0; j < oldWall.length; j++)
301 fortressTypes[newKey].wall.push(oldWall[j]); // Only works if the first element is not in fillTowersBetween (e.g. entry or gate like it should be)
302 if (j+1 < oldWall.length)
303 if (fillTowersBetween.indexOf(oldWall[j]) > -1 && fillTowersBetween.indexOf(oldWall[j+1]) > -1) // ... > -1 means "exists" here
304 fortressTypes[newKey].wall.push("tower");
308 // Setup some balanced (to civ type fortresses) semi default fortresses for "palisades" style
311 // Add some "fortress types" for roads (will only work with style "road")
313 // ["start", "short", "xRight", "xLeft", "cornerLeft", "xStraight", "long", "xLeft", "xRight", "cornerRight", "tRight", "tLeft", "xRight", "xLeft", "curveLeft", "xStraight", "curveRight", "end"];
315 "road01": ["short", "curveLeft", "short", "curveLeft", "short", "curveLeft", "short", "curveLeft"],
316 "road02": ["short", "cornerLeft", "short", "cornerLeft", "short", "cornerLeft", "short", "cornerLeft"],
317 "road03": ["xStraight", "curveLeft", "xStraight", "curveLeft", "xStraight", "curveLeft", "xStraight", "curveLeft"],
318 "road04": ["start", "curveLeft", "tRight", "cornerLeft", "tRight", "curveRight", "short", "xRight", "curveLeft", "xRight", "short", "cornerLeft", "tRight", "short",
319 "curveLeft", "short", "tRight", "cornerLeft", "short", "xRight", "curveLeft", "xRight", "short", "curveRight", "tRight", "cornerLeft", "tRight", "curveLeft", "end"],
320 "road05": ["start", "tLeft", "short", "xRight",
321 "curveLeft", "xRight", "tRight", "cornerLeft", "tRight",
322 "curveLeft", "short", "tRight", "cornerLeft", "xRight",
323 "cornerLeft", "xRight", "short", "tRight", "curveLeft", "end"],
326 for (let type in roadTypes)
327 fortressTypes[type] = new Fortress(type, roadTypes[type]);
330 ///////////////////////////////
331 // Define some helper functions
332 ///////////////////////////////
334 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
337 // Returns a list of objects containing all information to place all the wall elements entities with placeObject (but the player ID)
338 // Placing the first wall element at startX/startY placed with an angle given by orientation
339 // An alignment can be used to get the "center" of a "wall" (more likely used for fortresses) with getCenterToFirstElement
340 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
341 function getWallAlignment(startX, startY, wall, style, orientation)
343 // Graciously handle arguments
344 if (wall === undefined)
346 if (!wallStyles.hasOwnProperty(style))
348 warn("Function getWallAlignment: Unknown style: " + style + ' (falling back to "athen")');
351 orientation = orientation || 0;
356 for (var i = 0; i < wall.length; i++)
358 var element = wallStyles[style][wall[i]];
359 if (element === undefined && i == 0)
360 warn("No valid wall element: " + wall[i]);
363 var placeX = wallX - element.indent * cos(orientation);
364 var placeY = wallY - element.indent * sin(orientation);
366 // Add wall elements entity placement arguments to the alignment
370 "entity": element.entity,
371 "angle": orientation + element.angle
374 // Preset vars for the next wall element
375 if (i+1 < wall.length)
377 orientation += element.bending;
378 var nextElement = wallStyles[style][wall[i+1]];
379 if (nextElement === undefined)
380 warn("No valid wall element: " + wall[i+1]);
381 var distance = (element.width + nextElement.width)/2;
382 // Corrections for elements with indent AND bending
383 var indent = element.indent;
384 var bending = element.bending;
385 if (bending !== 0 && indent !== 0)
387 // Indent correction to adjust distance
388 distance += indent*sin(bending);
389 // Indent correction to normalize indentation
390 wallX += indent * cos(orientation);
391 wallY += indent * sin(orientation);
393 // Set the next coordinates of the next element in the wall without indentation adjustment
394 wallX -= distance * sin(orientation);
395 wallY += distance * cos(orientation);
401 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
402 // getCenterToFirstElement
404 // Center calculation works like getting the center of mass assuming all wall elements have the same "weight"
406 // It returns the vector from the center to the first wall element
407 // Used to get centerToFirstElement of fortresses by default
408 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
409 function getCenterToFirstElement(alignment)
411 var centerToFirstElement = {"x": 0, "y": 0};
412 for (var i = 0; i < alignment.length; i++)
414 centerToFirstElement.x -= alignment[i].x/alignment.length;
415 centerToFirstElement.y -= alignment[i].y/alignment.length;
417 return centerToFirstElement;
420 //////////////////////////////////////////////////////////////////
423 // NOTE: Does not support bending wall elements like corners!
424 // e.g. used by placeIrregularPolygonalWall
425 //////////////////////////////////////////////////////////////////
426 function getWallLength(wall, style)
428 // Graciously handle arguments
429 if (wall === undefined)
431 if (!wallStyles.hasOwnProperty(style))
433 warn("Function getWallLength: Unknown style: " + style + ' (falling back to "athen")');
438 for (var i = 0; i < wall.length; i++)
439 length += wallStyles[style][wall[i]].width;
444 /////////////////////////////////////////////
445 // Define the different wall placer functions
446 /////////////////////////////////////////////
448 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
451 // Places a wall with wall elements attached to another like determined by WallElement properties.
453 // startX, startY Where the first wall element should be placed
454 // wall Array of wall element type strings. Example: ["endLeft", "wallLong", "tower", "wallLong", "endRight"]
455 // style Optional. Wall style string. Default is the civ of the given player, "palisades" for gaia
456 // playerId Optional. Number of the player the wall will be placed for. Default is 0 (gaia)
457 // orientation Optional. Angle the first wall element is placed. Default is 0
458 // 0 means "outside" or "front" of the wall is right (positive X) like placeObject
459 // It will then be build towards top/positive Y (if no bending wall elements like corners are used)
460 // Raising orientation means the wall is rotated counter-clockwise like placeObject
461 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
462 function placeWall(startX, startY, wall, style, playerId, orientation)
464 // Graciously handle arguments
465 if (wall === undefined)
467 playerId = playerId || 0;
468 if (!wallStyles.hasOwnProperty(style))
471 style = style || "palisades";
473 style = getCivCode(playerId-1);
475 orientation = orientation || 0;
477 // Get wall alignment
478 var AM = getWallAlignment(startX, startY, wall, style, orientation);
480 for (var iWall = 0; iWall < wall.length; iWall++)
482 var entity = AM[iWall].entity;
483 if (entity !== undefined)
484 placeObject(AM[iWall].x, AM[iWall].y, entity, playerId, AM[iWall].angle);
488 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
489 // placeCustomFortress
491 // Place a fortress (mainly a closed wall build like placeWall) centered at centerX/centerY
492 // The fortress wall should always start with the main entrance (like "entry" or "gate") to get the orientation right (like placeObject)
494 // fortress An instance of Fortress with a wall defined
495 // style Optional. Wall style string. Default is the civ of the given player, "palisades" for gaia
496 // playerId Optional. Number of the player the wall will be placed for. Default is 0 (gaia)
497 // orientation Optional. Angle the first wall element (should be a gate or entrance) is placed. Default is BUILDING_ORIENTATION
498 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
499 function placeCustomFortress(centerX, centerY, fortress, style, playerId = 0, orientation = BUILDING_ORIENTATION)
501 // Graciously handle arguments
502 fortress = fortress || fortressTypes.medium;
503 if (!wallStyles.hasOwnProperty(style))
506 style = style || "palisades";
508 style = getCivCode(playerId-1);
511 // Calculate center if fortress.centerToFirstElement is undefined (default)
512 var centerToFirstElement = fortress.centerToFirstElement;
513 if (centerToFirstElement === undefined)
514 centerToFirstElement = getCenterToFirstElement(getWallAlignment(0, 0, fortress.wall, style));
515 // Placing the fortress wall
516 var startX = centerX + centerToFirstElement.x * cos(orientation) - centerToFirstElement.y * sin(orientation);
517 var startY = centerY + centerToFirstElement.y * cos(orientation) + centerToFirstElement.x * sin(orientation);
518 placeWall(startX, startY, fortress.wall, style, playerId, orientation);
521 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
524 // Like placeCustomFortress just it takes type (a fortress type string, has to be in fortressTypes) instead of an instance of Fortress
525 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
526 function placeFortress(centerX, centerY, type, style, playerId, orientation)
528 // Graciously handle arguments
529 type = type || "medium";
530 playerId = playerId || 0;
531 if (!wallStyles.hasOwnProperty(style))
534 style = style || "palisades";
536 style = getCivCode(playerId-1);
538 orientation = orientation || 0;
540 // Call placeCustomFortress with the given arguments
541 placeCustomFortress(centerX, centerY, fortressTypes[type], style, playerId, orientation);
544 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
547 // Places a straight wall from a given coordinate to an other repeatedly using the wall parts.
549 // startX/startY Coordinate of the approximate beginning of the wall (Not the place of the first wall element)
550 // targetX/targetY Coordinate of the approximate ending of the wall (Not the place of the last wall element)
551 // wallPart Optional. An array of NON-BENDING wall element type strings. Default is ["tower", "wallLong"]
552 // style Optional. Wall style string. Default is the civ of the given player, "palisades" for gaia
553 // playerId Optional. Integer number of the player. Default is 0 (gaia)
554 // endWithFirst Optional. A boolean value. If true the 1st wall element in the wallPart array will finalize the wall. Default is true
556 // TODO: Maybe add angle offset for more generic looking?
557 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
558 function placeLinearWall(startX, startY, targetX, targetY, wallPart, style, playerId, endWithFirst)
560 // Setup optional arguments to the default
561 wallPart = wallPart || ["tower", "wallLong"];
562 playerId = playerId || 0;
563 if (!wallStyles.hasOwnProperty(style))
566 style = style || "palisades";
568 style = getCivCode(playerId-1);
570 endWithFirst = typeof endWithFirst == "undefined" ? true : endWithFirst;
573 for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++)
575 var bending = wallStyles[style][wallPart[elementIndex]].bending;
577 warn("Bending is not supported by placeLinearWall but a bending wall element is used: " + wallPart[elementIndex] + " -> wallStyles[style][wallPart[elementIndex]].entity");
579 // Setup number of wall parts
580 var totalLength = Math.euclidDistance2D(startX, startY, targetX, targetY);
581 var wallPartLength = 0;
582 for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++)
583 wallPartLength += wallStyles[style][wallPart[elementIndex]].width;
586 numParts = ceil((totalLength - wallStyles[style][wallPart[0]].width) / wallPartLength);
588 numParts = ceil(totalLength / wallPartLength);
589 // Setup scale factor
592 scaleFactor = totalLength / (numParts * wallPartLength + wallStyles[style][wallPart[0]].width);
594 scaleFactor = totalLength / (numParts * wallPartLength);
596 var wallAngle = getAngle(startX, startY, targetX, targetY); // NOTE: function "getAngle()" is about to be changed...
597 var placeAngle = wallAngle - PI/2;
598 // Place wall entities
601 for (var partIndex = 0; partIndex < numParts; partIndex++)
603 for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++)
605 var wallEle = wallStyles[style][wallPart[elementIndex]];
607 x += scaleFactor * wallEle.width/2 * cos(wallAngle);
608 y += scaleFactor * wallEle.width/2 * sin(wallAngle);
610 var placeX = x - wallEle.indent * sin(wallAngle);
611 var placeY = y + wallEle.indent * cos(wallAngle);
613 var entity = wallEle.entity;
614 if (entity !== undefined)
615 placeObject(placeX, placeY, entity, playerId, placeAngle + wallEle.angle);
616 x += scaleFactor * wallEle.width/2 * cos(wallAngle);
617 y += scaleFactor * wallEle.width/2 * sin(wallAngle);
622 var wallEle = wallStyles[style][wallPart[0]];
623 x += scaleFactor * wallEle.width/2 * cos(wallAngle);
624 y += scaleFactor * wallEle.width/2 * sin(wallAngle);
625 var entity = wallEle.entity;
626 if (entity !== undefined)
627 placeObject(x, y, entity, playerId, placeAngle + wallEle.angle);
631 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
634 // Place a circular wall of repeated wall elements given in the argument wallPart around centerX/centerY with the given radius
635 // The wall can be opened forming more an arc than a circle if maxAngle < 2*PI
636 // The orientation then determines where this open part faces (0 means right like unrotated building's drop-points)
638 // centerX/Y Coordinates of the circle's center
639 // radius How wide the circle should be (approximate, especially if maxBendOff != 0)
640 // wallPart Optional. An array of NON-BENDING wall element type strings. Default is ["tower", "wallLong"]
641 // style Optional. Wall style string. Default is the civ of the given player, "palisades" for gaia
642 // playerId Optional. Integer number of the player. Default is 0 (gaia)
643 // orientation Optional. Where the open part of the (circular) arc should face (if maxAngle is < 2*PI). Default is 0
644 // maxAngle Optional. How far the wall should circumvent the center. Default is 2*PI (full circle)
645 // endWithFirst Optional. Boolean. If true the 1st wall element in the wallPart array will finalize the wall. Default is false for full circles, else true
646 // maxBendOff Optional. How irregular the circle should be. 0 means regular circle, PI/2 means very irregular. Default is 0 (regular circle)
648 // NOTE: Don't use wall elements with bending like corners!
649 // TODO: Perhaps add eccentricity and maxBendOff functionality (untill now an unused argument)
650 // TODO: Perhaps add functionality for spirals
651 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
652 function placeCircularWall(centerX, centerY, radius, wallPart, style, playerId, orientation, maxAngle, endWithFirst, maxBendOff)
654 // Setup optional arguments to the default
655 wallPart = wallPart || ["tower", "wallLong"];
656 playerId = playerId || 0;
657 if (!wallStyles.hasOwnProperty(style))
660 style = style || "palisades";
662 style = getCivCode(playerId-1);
664 orientation = orientation || 0;
665 maxAngle = maxAngle || 2*PI;
667 if (endWithFirst === undefined)
668 endWithFirst = maxAngle < 2*PI - 0.001; // Can this be done better?
670 maxBendOff = maxBendOff || 0;
673 if (maxBendOff > PI/2 || maxBendOff < 0)
674 warn("placeCircularWall maxBendOff sould satisfy 0 < maxBendOff < PI/2 (~1.5) but it is: " + maxBendOff);
675 for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++)
677 var bending = wallStyles[style][wallPart[elementIndex]].bending;
679 warn("Bending is not supported by placeCircularWall but a bending wall element is used: " + wallPart[elementIndex]);
681 // Setup number of wall parts
682 var totalLength = maxAngle * radius;
683 var wallPartLength = 0;
684 for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++)
685 wallPartLength += wallStyles[style][wallPart[elementIndex]].width;
688 numParts = ceil((totalLength - wallStyles[style][wallPart[0]].width) / wallPartLength);
690 numParts = ceil(totalLength / wallPartLength);
692 // Setup scale factor
695 scaleFactor = totalLength / (numParts * wallPartLength + wallStyles[style][wallPart[0]].width);
697 scaleFactor = totalLength / (numParts * wallPartLength);
699 // Place wall entities
700 var actualAngle = orientation + (2*PI - maxAngle) / 2;
701 var x = centerX + radius*cos(actualAngle);
702 var y = centerY + radius*sin(actualAngle);
703 for (var partIndex = 0; partIndex < numParts; partIndex++)
704 for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++)
706 var wallEle = wallStyles[style][wallPart[elementIndex]];
708 var addAngle = scaleFactor * wallEle.width / radius;
709 var targetX = centerX + radius * cos(actualAngle + addAngle);
710 var targetY = centerY + radius * sin(actualAngle + addAngle);
711 var placeX = x + (targetX - x)/2;
712 var placeY = y + (targetY - y)/2;
713 var placeAngle = actualAngle + addAngle/2;
715 placeX -= wallEle.indent * cos(placeAngle);
716 placeY -= wallEle.indent * sin(placeAngle);
718 var entity = wallEle.entity;
719 if (entity !== undefined)
720 placeObject(placeX, placeY, entity, playerId, placeAngle + wallEle.angle);
721 // Prepare for the next wall element
722 actualAngle += addAngle;
723 x = centerX + radius*cos(actualAngle);
724 y = centerY + radius*sin(actualAngle);
729 var wallEle = wallStyles[style][wallPart[0]];
730 var addAngle = scaleFactor * wallEle.width / radius;
731 var targetX = centerX + radius * cos(actualAngle + addAngle);
732 var targetY = centerY + radius * sin(actualAngle + addAngle);
733 var placeX = x + (targetX - x)/2;
734 var placeY = y + (targetY - y)/2;
735 var placeAngle = actualAngle + addAngle/2;
736 placeObject(placeX, placeY, wallEle.entity, playerId, placeAngle + wallEle.angle);
740 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
741 // placePolygonalWall
743 // Place a polygonal wall of repeated wall elements given in the argument wallPart around centerX/centerY with the given radius
745 // centerX/Y Coordinates of the polygon's center
746 // radius How wide the circle should be in which the polygon fits
747 // wallPart Optional. An array of NON-BENDING wall element type strings. Default is ["wallLong", "tower"]
748 // cornerWallElement Optional. Wall element to be placed at the polygon's corners. Default is "tower"
749 // style Optional. Wall style string. Default is the civ of the given player, "palisades" for gaia
750 // playerId Optional. Integer number of the player. Default is 0 (gaia)
751 // orientation Optional. Angle from the center to the first linear wall part placed. Default is 0 (towards positive X/right)
752 // numCorners Optional. How many corners the polygon will have. Default is 8 (matching a civ centers territory)
753 // skipFirstWall Optional. Boolean. If the first linear wall part will be left opened as entrance. Default is true
755 // NOTE: Don't use wall elements with bending like corners!
756 // TODO: Replace skipFirstWall with firstWallPart to enable gate/defended entrance placement
757 // TODO: Check some arguments
758 // TODO: Add eccentricity and perhaps make it just call placeIrregularPolygonalWall with irregularity = 0
759 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
760 function placePolygonalWall(centerX, centerY, radius, wallPart, cornerWallElement, style, playerId, orientation, numCorners, skipFirstWall = true)
762 // Setup optional arguments to the default
763 wallPart = wallPart || ["wallLong", "tower"];
764 cornerWallElement = cornerWallElement || "tower"; // Don't use wide elements for this. Not supported well...
765 playerId = playerId || 0;
767 if (!wallStyles.hasOwnProperty(style))
770 style = style || "palisades";
772 style = getCivCode(playerId-1);
774 orientation = orientation || 0;
775 numCorners = numCorners || 8;
778 var angleAdd = 2*PI/numCorners;
779 var angleStart = orientation - angleAdd/2;
782 for (var i = 0; i < numCorners; i++)
783 corners.push([centerX + radius*cos(angleStart + i*angleAdd), centerY + radius*sin(angleStart + i*angleAdd)]);
784 // Place Corners and walls
785 for (var i = 0; i < numCorners; i++)
787 var angleToCorner = getAngle(corners[i][0], corners[i][1], centerX, centerY);
788 placeObject(corners[i][0], corners[i][1], wallStyles[style][cornerWallElement].entity, playerId, angleToCorner);
789 if (!skipFirstWall || i != 0)
791 // Adjustment to the corner element width (approximately)
792 corners[i][0] + wallStyles[style][cornerWallElement].width/2 * sin(angleToCorner + angleAdd/2), // startX
793 corners[i][1] - wallStyles[style][cornerWallElement].width/2 * cos(angleToCorner + angleAdd/2), // startY
794 corners[(i+1)%numCorners][0] - wallStyles[style][cornerWallElement].width/2 * sin(angleToCorner + angleAdd/2), // targetX
795 corners[(i+1)%numCorners][1] + wallStyles[style][cornerWallElement].width/2 * cos(angleToCorner + angleAdd/2), // targetY
796 wallPart, style, playerId);
800 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
801 // placeIrregularPolygonalWall
803 // Place an irregular polygonal wall of some wall parts to choose from around centerX/centerY with the given radius
805 // centerX/Y Coordinates of the polygon's center
806 // radius How wide the circle should be in which the polygon fits
807 // cornerWallElement Optional. Wall element to be placed at the polygon's corners. Default is "tower"
808 // style Optional. Wall style string. Default is the civ of the given player, "palisades" for gaia
809 // playerId Optional. Integer number of the player. Default is 0 (gaia)
810 // orientation Optional. Angle from the center to the first linear wall part placed. Default is 0 (towards positive X/right)
811 // numCorners Optional. How many corners the polygon will have. Default is 8 (matching a civ centers territory)
812 // irregularity Optional. How irregular the polygon will be. 0 means regular, 1 means VERY irregular. Default is 0.5
813 // skipFirstWall Optional. Boolean. If the first linear wall part will be left opened as entrance. Default is true
814 // wallPartsAssortment Optional. An array of wall part arrays to choose from for each linear wall connecting the corners. Default is hard to describe ^^
816 // NOTE: wallPartsAssortment is put to the end because it's hardest to set
817 // NOTE: Don't use wall elements with bending like corners!
818 // TODO: Replace skipFirstWall with firstWallPart to enable gate/defended entrance placement
819 // TODO: Check some arguments
820 // TODO: Perhaps add eccentricity
821 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
822 function placeIrregularPolygonalWall(centerX, centerY, radius, cornerWallElement, style, playerId, orientation, numCorners, irregularity, skipFirstWall, wallPartsAssortment)
824 // Setup optional arguments
825 playerId = playerId || 0;
826 if (!wallStyles.hasOwnProperty(style))
829 style = style || "palisades";
831 style = getCivCode(playerId-1);
834 // Generating a generic wall part assortment with each wall part including 1 gate lengthened by walls and towers
835 // NOTE: It might be a good idea to write an own function for that...
836 var defaultWallPartsAssortment = [["wallShort"], ["wall"], ["wallLong"], ["gate", "tower", "wallShort"]];
837 var centeredWallPart = ["gate"];
838 var extandingWallPartAssortment = [["tower", "wallLong"], ["tower", "wall"]];
839 defaultWallPartsAssortment.push(centeredWallPart);
840 for (var i = 0; i < extandingWallPartAssortment.length; i++)
842 var wallPart = centeredWallPart;
843 for (var j = 0; j < radius; j++)
846 wallPart = wallPart.concat(extandingWallPartAssortment[i]);
849 extandingWallPartAssortment[i].reverse();
850 wallPart = extandingWallPartAssortment[i].concat(wallPart);
851 extandingWallPartAssortment[i].reverse();
853 defaultWallPartsAssortment.push(wallPart);
856 // Setup optional arguments to the default
857 wallPartsAssortment = wallPartsAssortment || defaultWallPartsAssortment;
858 cornerWallElement = cornerWallElement || "tower"; // Don't use wide elements for this. Not supported well...
859 style = style || "palisades";
860 playerId = playerId || 0;
861 orientation = orientation || 0;
862 numCorners = numCorners || randIntInclusive(5, 7);
863 irregularity = irregularity || 0.5;
864 skipFirstWall = skipFirstWall || false;
867 var angleToCover = 2*PI;
868 var angleAddList = [];
869 for (var i = 0; i < numCorners; i++)
871 // Randomize covered angles. Variety scales down with raising angle though...
872 angleAddList.push(angleToCover/(numCorners-i) * (1 + randFloat(-irregularity, irregularity)));
873 angleToCover -= angleAddList[angleAddList.length - 1];
877 var angleActual = orientation - angleAddList[0]/2;
878 for (var i = 0; i < numCorners; i++)
880 corners.push([centerX + radius*cos(angleActual), centerY + radius*sin(angleActual)]);
881 if (i < numCorners - 1)
882 angleActual += angleAddList[i+1];
884 // Setup best wall parts for the different walls (a bit confusing naming...)
885 var wallPartLengths = [];
886 var maxWallPartLength = 0;
887 for (var partIndex = 0; partIndex < wallPartsAssortment.length; partIndex++)
889 var length = wallPartLengths[partIndex];
890 wallPartLengths.push(getWallLength(wallPartsAssortment[partIndex], style));
891 if (length > maxWallPartLength)
892 maxWallPartLength = length;
894 var wallPartList = []; // This is the list of the wall parts to use for the walls between the corners, not to confuse with wallPartsAssortment!
895 for (var i = 0; i < numCorners; i++)
897 var bestWallPart = []; // This is a simpel wall part not a wallPartsAssortment!
898 var bestWallLength = 99999999;
899 // NOTE: This is not exactly like the length the wall will be in the end. Has to be tweaked...
900 var wallLength = Math.euclidDistance2D(corners[i][0], corners[i][1], corners[(i + 1) % numCorners][0], corners[(i + 1) % numCorners][1]);
901 var numWallParts = ceil(wallLength/maxWallPartLength);
902 for (var partIndex = 0; partIndex < wallPartsAssortment.length; partIndex++)
904 var linearWallLength = numWallParts*wallPartLengths[partIndex];
905 if (linearWallLength < bestWallLength && linearWallLength > wallLength)
907 bestWallPart = wallPartsAssortment[partIndex];
908 bestWallLength = linearWallLength;
911 wallPartList.push(bestWallPart);
913 // Place Corners and walls
914 for (var i = 0; i < numCorners; i++)
916 var angleToCorner = getAngle(corners[i][0], corners[i][1], centerX, centerY);
917 placeObject(corners[i][0], corners[i][1], wallStyles[style][cornerWallElement].entity, playerId, angleToCorner);
918 if (!skipFirstWall || i != 0)
920 // Adjustment to the corner element width (approximately)
921 corners[i][0] + wallStyles[style][cornerWallElement].width/2 * sin(angleToCorner + angleAddList[i]/2), // startX
922 corners[i][1] - wallStyles[style][cornerWallElement].width/2 * cos(angleToCorner + angleAddList[i]/2), // startY
923 corners[(i+1)%numCorners][0] - wallStyles[style][cornerWallElement].width/2 * sin(angleToCorner + angleAddList[(i+1)%numCorners]/2), // targetX
924 corners[(i+1)%numCorners][1] + wallStyles[style][cornerWallElement].width/2 * cos(angleToCorner + angleAddList[(i+1)%numCorners]/2), // targetY
925 wallPartList[i], style, playerId, false);
929 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
930 // placeGenericFortress
932 // Places a generic fortress with towers at the edges connected with long walls and gates (entries until gates work)
933 // This is the default Iberian civ bonus starting wall
935 // centerX/Y The approximate center coordinates of the fortress
936 // radius The approximate radius of the wall to be placed
937 // playerId Optional. Integer number of the player. Default is 0 (gaia)
938 // style Optional. Wall style string. Default is the civ of the given player, "palisades" for gaia
939 // irregularity Optional. Float between 0 (circle) and 1 (very spiky), default is 1/2
940 // gateOccurence Optional. Integer number, every n-th walls will be a gate instead. Default is 3
941 // maxTrys Optional. How often the function tries to find a better fitting shape at max. Default is 100
942 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
943 function placeGenericFortress(centerX, centerY, radius, playerId, style, irregularity, gateOccurence, maxTrys)
945 // Setup optional arguments
946 radius = radius || 20;
947 playerId = playerId || 0;
948 if (!wallStyles.hasOwnProperty(style))
951 style = style || "palisades";
953 style = getCivCode(playerId - 1);
955 irregularity = irregularity || 1/2;
956 gateOccurence = gateOccurence || 3;
957 maxTrys = maxTrys || 100;
960 var startAngle = randFloat(0, 2*PI);
961 var actualOffX = radius*cos(startAngle);
962 var actualOffY = radius*sin(startAngle);
963 var actualAngle = startAngle;
964 var pointDistance = wallStyles[style].wallLong.width + wallStyles[style].tower.width;
965 // Searching for a well fitting point derivation
967 var bestPointDerivation = undefined;
968 var minOverlap = 1000;
969 var overlap = undefined;
970 while (tries < maxTrys && minOverlap > wallStyles[style].tower.width / 10)
972 var pointDerivation = [];
973 var distanceToTarget = 1000;
974 var targetReached = false;
975 while (!targetReached)
977 var indent = randFloat(-irregularity*pointDistance, irregularity*pointDistance);
978 var tmpAngle = getAngle(actualOffX, actualOffY,
979 (radius + indent)*cos(actualAngle + (pointDistance / radius)),
980 (radius + indent)*sin(actualAngle + (pointDistance / radius)));
981 actualOffX += pointDistance*cos(tmpAngle);
982 actualOffY += pointDistance*sin(tmpAngle);
983 actualAngle = getAngle(0, 0, actualOffX, actualOffY);
984 pointDerivation.push([actualOffX, actualOffY]);
985 distanceToTarget = Math.euclidDistance2D(actualOffX, actualOffY, ...pointDerivation[0]);
986 var numPoints = pointDerivation.length;
987 if (numPoints > 3 && distanceToTarget < pointDistance) // Could be done better...
989 targetReached = true;
990 overlap = pointDistance - Math.euclidDistance2D(...pointDerivation[numPoints - 1], ...pointDerivation[0]);
991 if (overlap < minOverlap)
993 minOverlap = overlap;
994 bestPointDerivation = pointDerivation;
1000 log("placeGenericFortress: Reduced overlap to " + minOverlap + " after " + tries + " tries");
1002 for (var pointIndex = 0; pointIndex < bestPointDerivation.length; pointIndex++)
1004 var startX = centerX + bestPointDerivation[pointIndex][0];
1005 var startY = centerY + bestPointDerivation[pointIndex][1];
1006 var targetX = centerX + bestPointDerivation[(pointIndex + 1) % bestPointDerivation.length][0];
1007 var targetY = centerY + bestPointDerivation[(pointIndex + 1) % bestPointDerivation.length][1];
1008 var angle = getAngle(startX, startY, targetX, targetY);
1009 var wallElement = "wallLong";
1010 if ((pointIndex + 1) % gateOccurence == 0)
1011 wallElement = "gate";
1012 var entity = wallStyles[style][wallElement].entity;
1015 startX + (Math.euclidDistance2D(startX, startY, targetX, targetY) / 2) * Math.cos(angle),
1016 startY + (Math.euclidDistance2D(startX, startY, targetX, targetY) / 2) * Math.sin(angle),
1019 angle - Math.PI / 2 + wallStyles[style][wallElement].angle);
1022 var startX = centerX + bestPointDerivation[(pointIndex + bestPointDerivation.length - 1) % bestPointDerivation.length][0];
1023 var startY = centerY + bestPointDerivation[(pointIndex + bestPointDerivation.length - 1) % bestPointDerivation.length][1];
1024 var angle = getAngle(startX, startY, targetX, targetY);
1026 centerX + bestPointDerivation[pointIndex][0],
1027 centerY + bestPointDerivation[pointIndex][1],
1028 wallStyles[style].tower.entity,
1030 angle - PI/2 + wallStyles[style].tower.angle);