Remove rmgen euclidian distance helper function, refs rP20328 / D969.
[0ad.git] / binaries / data / mods / public / maps / random / rmgen / wall_builder.js
blobc93d31134faa0ced99bb88b8092092468fda09f0
1 ////////////////////////////////////////////////////////////////////
2 // This file contains functionality to place walls on random maps //
3 ////////////////////////////////////////////////////////////////////
5 // To do:
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!!!)
26 //              irregularity
27 //              maxTrys
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)
52         this.type = type;
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, ...
74         this.entity = entity;
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 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
106 var wallStyles = {};
108 // Generic civ dependent wall style definition. "rome_siege" needs some tweek...
109 var wallScaleByType = {
110         "athen": 1.5,
111         "brit": 1.5,
112         "cart": 1.8,
113         "gaul": 1.5,
114         "iber": 1.5,
115         "mace": 1.5,
116         "maur": 1.5,
117         "pers": 1.5,
118         "ptol": 1.5,
119         "rome": 1.5,
120         "sele": 1.5,
121         "spart": 1.5,
122         "rome_siege": 1.5
125 for (var style in wallScaleByType)
127         var civ = style;
128         if (style == "rome_siege")
129                 civ = "rome";
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])
169         };
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),
202         "gate": gate,
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!
216 wallStyles.road = {
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!
236 wallStyles.other = {
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 = {};
269         let wallParts = {
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"]
281         };
283         for (let type in wallParts)
284         {
285                 fortressTypes[type] = new Fortress(type);
287                 let wp = wallParts[type];
288                 fortressTypes[type].wall = wp.concat(wp, wp, wp);
289         }
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++)
300         {
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");
305         }
308 // Setup some balanced (to civ type fortresses) semi default fortresses for "palisades" style
309 // TODO
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"];
314         let roadTypes = {
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"],
324         };
326         for (let type in roadTypes)
327                 fortressTypes[type] = new Fortress(type, roadTypes[type]);
330 ///////////////////////////////
331 // Define some helper functions
332 ///////////////////////////////
334 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
335 //  getWallAlignment
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)
345                 wall = [];
346         if (!wallStyles.hasOwnProperty(style))
347         {
348                 warn("Function getWallAlignment: Unknown style: " + style + ' (falling back to "athen")');
349                 style = "athen";
350         }
351         orientation = orientation || 0;
353         var alignment = [];
354         var wallX = startX;
355         var wallY = startY;
356         for (var i = 0; i < wall.length; i++)
357         {
358                 var element = wallStyles[style][wall[i]];
359                 if (element === undefined && i == 0)
360                         warn("No valid wall element: " + wall[i]);
362                 // Indentation
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
367                 alignment.push({
368                         "x": placeX,
369                         "y": placeY,
370                         "entity": element.entity,
371                         "angle": orientation + element.angle
372                 });
374                 // Preset vars for the next wall element
375                 if (i+1 < wall.length)
376                 {
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)
386                         {
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);
392                         }
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);
396                 }
397         }
398         return alignment;
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++)
413         {
414                 centerToFirstElement.x -= alignment[i].x/alignment.length;
415                 centerToFirstElement.y -= alignment[i].y/alignment.length;
416         }
417         return centerToFirstElement;
420 //////////////////////////////////////////////////////////////////
421 //  getWallLength
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)
430                 wall = [];
431         if (!wallStyles.hasOwnProperty(style))
432         {
433                 warn("Function getWallLength: Unknown style: " + style + ' (falling back to "athen")');
434                 style = "athen";
435         }
437         var length = 0;
438         for (var i = 0; i < wall.length; i++)
439                 length += wallStyles[style][wall[i]].width;
441         return length;
444 /////////////////////////////////////////////
445 // Define the different wall placer functions
446 /////////////////////////////////////////////
448 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
449 //  placeWall
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)
466                 wall = [];
467         playerId = playerId || 0;
468         if (!wallStyles.hasOwnProperty(style))
469         {
470                 if (playerId == 0)
471                         style = style || "palisades";
472                 else
473                         style = getCivCode(playerId-1);
474         }
475         orientation = orientation || 0;
477         // Get wall alignment
478         var AM = getWallAlignment(startX, startY, wall, style, orientation);
479         // Place the wall
480         for (var iWall = 0; iWall < wall.length; iWall++)
481         {
482                 var entity = AM[iWall].entity;
483                 if (entity !== undefined)
484                         placeObject(AM[iWall].x, AM[iWall].y, entity, playerId, AM[iWall].angle);
485         }
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))
504         {
505                 if (playerId == 0)
506                         style = style || "palisades";
507                 else
508                         style = getCivCode(playerId-1);
509         }
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 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
522 //  placeFortress
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))
532         {
533                 if (playerId == 0)
534                         style = style || "palisades";
535                 else
536                         style = getCivCode(playerId-1);
537         }
538         orientation = orientation || 0;
540         // Call placeCustomFortress with the given arguments
541         placeCustomFortress(centerX, centerY, fortressTypes[type], style, playerId, orientation);
544 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
545 //  placeLinearWall
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))
564         {
565                 if (playerId == 0)
566                         style = style || "palisades";
567                 else
568                         style = getCivCode(playerId-1);
569         }
570         endWithFirst = typeof endWithFirst == "undefined" ? true : endWithFirst;
572         // Check arguments
573         for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++)
574         {
575                 var bending = wallStyles[style][wallPart[elementIndex]].bending;
576                 if (bending != 0)
577                         warn("Bending is not supported by placeLinearWall but a bending wall element is used: " + wallPart[elementIndex] + " -> wallStyles[style][wallPart[elementIndex]].entity");
578         }
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;
584         var numParts = 0;
585         if (endWithFirst)
586                 numParts = ceil((totalLength - wallStyles[style][wallPart[0]].width) / wallPartLength);
587         else
588                 numParts = ceil(totalLength / wallPartLength);
589         // Setup scale factor
590         var scaleFactor = 1;
591         if (endWithFirst)
592                 scaleFactor = totalLength / (numParts * wallPartLength + wallStyles[style][wallPart[0]].width);
593         else
594                 scaleFactor = totalLength / (numParts * wallPartLength);
595         // Setup angle
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
599         var x = startX;
600         var y = startY;
601         for (var partIndex = 0; partIndex < numParts; partIndex++)
602         {
603                 for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++)
604                 {
605                         var wallEle = wallStyles[style][wallPart[elementIndex]];
606                         // Width correction
607                         x += scaleFactor * wallEle.width/2 * cos(wallAngle);
608                         y += scaleFactor * wallEle.width/2 * sin(wallAngle);
609                         // Indent correction
610                         var placeX = x - wallEle.indent * sin(wallAngle);
611                         var placeY = y + wallEle.indent * cos(wallAngle);
612                         // Placement
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);
618                 }
619         }
620         if (endWithFirst)
621         {
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);
628         }
631 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
632 //  placeCircularWall
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))
658         {
659                 if (playerId == 0)
660                         style = style || "palisades";
661                 else
662                         style = getCivCode(playerId-1);
663         }
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;
672         // Check arguments
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++)
676         {
677                 var bending = wallStyles[style][wallPart[elementIndex]].bending;
678                 if (bending != 0)
679                         warn("Bending is not supported by placeCircularWall but a bending wall element is used: " + wallPart[elementIndex]);
680         }
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;
686         var numParts = 0;
687         if (endWithFirst)
688                 numParts = ceil((totalLength - wallStyles[style][wallPart[0]].width) / wallPartLength);
689         else
690                 numParts = ceil(totalLength / wallPartLength);
692         // Setup scale factor
693         var scaleFactor = 1;
694         if (endWithFirst)
695                 scaleFactor = totalLength / (numParts * wallPartLength + wallStyles[style][wallPart[0]].width);
696         else
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++)
705                 {
706                         var wallEle = wallStyles[style][wallPart[elementIndex]];
707                         // Width correction
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;
714                         // Indent correction
715                         placeX -= wallEle.indent * cos(placeAngle);
716                         placeY -= wallEle.indent * sin(placeAngle);
717                         // Placement
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);
725                 }
727         if (endWithFirst)
728         {
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);
737         }
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))
768         {
769                 if (playerId == 0)
770                         style = style || "palisades";
771                 else
772                         style = getCivCode(playerId-1);
773         }
774         orientation = orientation || 0;
775         numCorners = numCorners || 8;
777         // Setup angles
778         var angleAdd = 2*PI/numCorners;
779         var angleStart = orientation - angleAdd/2;
780         // Setup corners
781         var corners = [];
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++)
786         {
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)
790                         placeLinearWall(
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);
797         }
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))
827         {
828                 if (playerId == 0)
829                         style = style || "palisades";
830                 else
831                         style = getCivCode(playerId-1);
832         }
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++)
841         {
842                 var wallPart = centeredWallPart;
843                 for (var j = 0; j < radius; j++)
844                 {
845                         if (j%2 == 0)
846                                 wallPart = wallPart.concat(extandingWallPartAssortment[i]);
847                         else
848                         {
849                                 extandingWallPartAssortment[i].reverse();
850                                 wallPart = extandingWallPartAssortment[i].concat(wallPart);
851                                 extandingWallPartAssortment[i].reverse();
852                         }
853                         defaultWallPartsAssortment.push(wallPart);
854                 }
855         }
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;
866         // Setup angles
867         var angleToCover = 2*PI;
868         var angleAddList = [];
869         for (var i = 0; i < numCorners; i++)
870         {
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];
874         }
875         // Setup corners
876         var corners = [];
877         var angleActual = orientation - angleAddList[0]/2;
878         for (var i = 0; i < numCorners; i++)
879         {
880                 corners.push([centerX + radius*cos(angleActual), centerY + radius*sin(angleActual)]);
881                 if (i < numCorners - 1)
882                         angleActual += angleAddList[i+1];
883         }
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++)
888         {
889                 var length = wallPartLengths[partIndex];
890                 wallPartLengths.push(getWallLength(wallPartsAssortment[partIndex], style));
891                 if (length > maxWallPartLength)
892                         maxWallPartLength = length;
893         }
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++)
896         {
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++)
903                 {
904                         var linearWallLength = numWallParts*wallPartLengths[partIndex];
905                         if (linearWallLength < bestWallLength && linearWallLength > wallLength)
906                         {
907                                 bestWallPart = wallPartsAssortment[partIndex];
908                                 bestWallLength = linearWallLength;
909                         }
910                 }
911                 wallPartList.push(bestWallPart);
912         }
913         // Place Corners and walls
914         for (var i = 0; i < numCorners; i++)
915         {
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)
919                         placeLinearWall(
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);
926         }
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))
949         {
950                 if (playerId == 0)
951                         style = style || "palisades";
952                 else
953                         style = getCivCode(playerId - 1);
954         }
955         irregularity = irregularity || 1/2;
956         gateOccurence = gateOccurence || 3;
957         maxTrys = maxTrys || 100;
959         // Setup some vars
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
966         var tries = 0;
967         var bestPointDerivation = undefined;
968         var minOverlap = 1000;
969         var overlap = undefined;
970         while (tries < maxTrys && minOverlap > wallStyles[style].tower.width / 10)
971         {
972                 var pointDerivation = [];
973                 var distanceToTarget = 1000;
974                 var targetReached = false;
975                 while (!targetReached)
976                 {
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...
988                         {
989                                 targetReached = true;
990                                 overlap = pointDistance - Math.euclidDistance2D(...pointDerivation[numPoints - 1], ...pointDerivation[0]);
991                                 if (overlap < minOverlap)
992                                 {
993                                         minOverlap = overlap;
994                                         bestPointDerivation = pointDerivation;
995                                 }
996                         }
997                 }
998                 tries++;
999         }
1000         log("placeGenericFortress: Reduced overlap to " + minOverlap + " after " + tries + " tries");
1001         // Place wall
1002         for (var pointIndex = 0; pointIndex < bestPointDerivation.length; pointIndex++)
1003         {
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;
1013                 if (entity)
1014                         placeObject(
1015                                 startX + (Math.euclidDistance2D(startX, startY, targetX, targetY) / 2) * Math.cos(angle),
1016                                 startY + (Math.euclidDistance2D(startX, startY, targetX, targetY) / 2) * Math.sin(angle),
1017                                 entity,
1018                                 playerId,
1019                                 angle - Math.PI / 2 + wallStyles[style][wallElement].angle);
1021                 // Place tower
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);
1025                 placeObject(
1026                         centerX + bestPointDerivation[pointIndex][0],
1027                         centerY + bestPointDerivation[pointIndex][1],
1028                         wallStyles[style].tower.entity,
1029                         playerId,
1030                         angle - PI/2 + wallStyles[style].tower.angle);
1031         }