Ticket #2942: wall_builder.js

File wall_builder.js, 58.6 KB (added by FeXoR, 9 years ago)

Adding Seleucids wall style for RMGen lib wallbuilder.js (changes included in the patch)

Line 
1////////////////////////////////////////////////////////////////////
2// This file contains functionality to place walls on random maps //
3////////////////////////////////////////////////////////////////////
4
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?
35
36
37//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
38// WallElement class definition
39//
40// 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)
41// The course of the wall will be changed by corners (bending != 0) and so the "inside"/"outside" direction
42//
43// type Descriptive string, example: "wallLong". NOTE: Not really needed. Mainly for custom wall elements and to get the wall element type in code
44// entity Optional. Template name string of the entity to be placed, example: "structures/cart_wall_long". Default is undefined (No entity placed)
45// angle Optional. The angle (float) added to place the entity so "outside" is right when the wall element is placed unrotated. Default is 0
46// width Optional. How far this wall element lengthens the wall (float), if unrotated the Y space needed. Default is 0
47// indent Optional. The lateral indentation of the entity, drawn "inside" (positive values) or pushed "outside" (negative values). Default is 0
48// bending Optional. How the course of the wall is changed after this element, positive is bending "in"/left/counter clockwise (like entity placement)
49// NOTE: Bending is not supported by all placement functions (see there)
50//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
51function WallElement(type, entity, angle, width, indent, bending)
52{
53 this.type = type;
54 // Default wall element type documentation:
55 // Lengthening straight blocking (mainly left/right symmetric) wall elements (Walls and wall fortifications)
56 // "wall" A blocking straight wall element that mainly lengthens the wall, self-explanatory
57 // "wallShort" self-explanatory
58 // "wallLong" self-explanatory
59 // "tower" A blocking straight wall element with damage potential (but for palisades) that slightly lengthens the wall, example: wall tower, palisade tower(No attack)
60 // "wallFort" A blocking straight wall element with massive damage potential that lengthens the wall, example: fortress, palisade fort
61 // Lengthening straight non/custom blocking (mainly left/right symmetric) wall elements (Gates and entries)
62 // "gate" A blocking straight wall element with passability determined by owner, example: gate (Functionality not yet implemented)
63 // "entry" A non-blocking straight wall element (same width as gate) but without an actual template or just a flag/column/obelisk
64 // "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
65 // "entryFort" A non-blocking straight wall element represented by a single (maybe indented) template, example: fortress, palisade fort
66 // Bending wall elements (Wall corners)
67 // "cornerIn" A wall element bending the wall by PI/2 "inside" (left, +, see above), example: wall tower, palisade curve
68 // "cornerOut" A wall element bending the wall by PI/2 "outside" (right, -, see above), example: wall tower, palisade curve
69 // "cornerHalfIn" A wall element bending the wall by PI/4 "inside" (left, +, see above), example: wall tower, palisade curve. NOTE: Not yet implemented
70 // "cornerHalfOut" A wall element bending the wall by PI/4 "outside" (right, -, see above), example: wall tower, palisade curve. NOTE: Not yet implemented
71 // Zero length straight indented (mainly left/right symmetric) wall elements (Outposts/watchtowers and non-defensive base structures)
72 // "outpost" A zero-length wall element without bending far indented so it stands outside the wall, example: outpost, defence tower, watchtower
73 // "house" A zero-length wall element without bending far indented so it stands inside the wall that grants population bonus, example: house, hut, longhouse
74 // "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.entity = entity;
76 this.angle = (angle !== undefined) ? angle : 0*PI;
77 this.width = (width !== undefined) ? width : 0;
78 this.indent = (indent !== undefined) ? indent : 0;
79 this.bending = (bending !== undefined) ? bending : 0*PI;
80}
81
82/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
83// Fortress class definition
84//
85// A "fortress" here is a closed wall build of multiple wall elements attached together defined in Fortress.wall
86// It's mainly the abstract shape defined in a Fortress instances wall because different styles can be used for it (see wallStyles)
87//
88// type Descriptive string, example: "tiny". Not really needed (WallTool.wallTypes["type string"] is used). Mainly for custom wall elements
89// wall Optional. Array of wall element strings. Can be set afterwards. Default is an epty array.
90// Example: ["entrance", "wall", "cornerIn", "wall", "gate", "wall", "entrance", "wall", "cornerIn", "wall", "gate", "wall", "cornerIn", "wall"]
91// centerToFirstElement Optional. Object with properties "x" and "y" representing a vector from the visual center to the first wall element. Default is undefined
92/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
93function Fortress(type, wall, centerToFirstElement)
94{
95 this.type = type; // Only usefull to get the type of the actual fortress
96 this.wall = (wall !== undefined) ? wall : [];
97 this.centerToFirstElement = undefined;
98}
99
100
101////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
102// wallStyles data structure for default wall styles
103//
104// A wall style is an associative array with all wall elements of that style in it associated with the wall element type string
105// wallStyles holds all the wall styles within an associative array with the civ string or another descriptive strings as key
106// Examples: "athen", "rome_siege", "palisades", "fence", "road"
107////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
108var wallStyles = {};
109
110// Generic civ dependent wall style definition. "rome_siege" needs some tweek...
111var wallScaleByType = {"athen" : 1.5, "brit" : 1.5, "cart" : 1.8, "celt" : 1.5, "gaul" : 1.5, "hele" : 1.5, "iber" : 1.5, "mace" : 1.5, "maur": 1.5, "pers" : 1.5, "ptol" : 1.5, "rome" : 1.5, "sele" : 1.5, "spart" : 1.5, "rome_siege" : 1.5};
112for (var style in wallScaleByType)
113{
114 var civ = style;
115 if (style == "rome_siege")
116 civ = "rome";
117 wallStyles[style] = {};
118 // Default wall elements
119 wallStyles[style]["tower"] = new WallElement("tower", "structures/" + style + "_wall_tower", PI, wallScaleByType[style]);
120 wallStyles[style]["endLeft"] = new WallElement("endLeft", "structures/" + style + "_wall_tower", PI, wallScaleByType[style]); // Same as tower. To be compatible with palisades...
121 wallStyles[style]["endRight"] = new WallElement("endRight", "structures/" + style + "_wall_tower", PI, wallScaleByType[style]); // Same as tower. To be compatible with palisades...
122 wallStyles[style]["cornerIn"] = new WallElement("cornerIn", "structures/" + style + "_wall_tower", 5*PI/4, 0, 0.35*wallScaleByType[style], PI/2); // 2^0.5 / 4 ~= 0.35 ~= 1/3
123 wallStyles[style]["cornerOut"] = new WallElement("cornerOut", "structures/" + style + "_wall_tower", 3*PI/4, 0.71*wallScaleByType[style], 0, -PI/2); // // 2^0.5 / 2 ~= 0.71 ~= 2/3
124 wallStyles[style]["wallShort"] = new WallElement("wallShort", "structures/" + style + "_wall_short", 0*PI, 2*wallScaleByType[style]);
125 wallStyles[style]["wall"] = new WallElement("wall", "structures/" + style + "_wall_medium", 0*PI, 4*wallScaleByType[style]);
126 wallStyles[style]["wallMedium"] = new WallElement("wall", "structures/" + style + "_wall_medium", 0*PI, 4*wallScaleByType[style]);
127 wallStyles[style]["wallLong"] = new WallElement("wallLong", "structures/" + style + "_wall_long", 0*PI, 6*wallScaleByType[style]);
128 // Gate and entrance wall elements
129 var gateWidth = 6*wallScaleByType[style];
130 wallStyles[style]["gate"] = new WallElement("gate", "structures/" + style + "_wall_gate", PI, gateWidth);
131 wallStyles[style]["entry"] = new WallElement("entry", undefined, 0*PI, gateWidth);
132 wallStyles[style]["entryTower"] = new WallElement("entryTower", "structures/" + civ + "_defense_tower", PI, gateWidth, -4*wallScaleByType[style]);
133 wallStyles[style]["entryFort"] = new WallElement("entryFort", "structures/" + civ + "_fortress", 0*PI, 8*wallScaleByType[style], 6*wallScaleByType[style]);
134 // Defensive wall elements with 0 width outside the wall
135 wallStyles[style]["outpost"] = new WallElement("outpost", "structures/" + civ + "_outpost", PI, 0, -4*wallScaleByType[style]);
136 wallStyles[style]["defenseTower"] = new WallElement("defenseTower", "structures/" + civ + "_defense_tower", PI, 0, -4*wallScaleByType[style]);
137 // Base buildings wall elements with 0 width inside the wall
138 wallStyles[style]["barracks"] = new WallElement("barracks", "structures/" + civ + "_barracks", PI, 0, 4.5*wallScaleByType[style]);
139 wallStyles[style]["civilCentre"] = new WallElement("civilCentre", "structures/" + civ + "_civil_centre", PI, 0, 4.5*wallScaleByType[style]);
140 wallStyles[style]["farmstead"] = new WallElement("farmstead", "structures/" + civ + "_farmstead", PI, 0, 4.5*wallScaleByType[style]);
141 wallStyles[style]["field"] = new WallElement("field", "structures/" + civ + "_field", PI, 0, 4.5*wallScaleByType[style]);
142 wallStyles[style]["fortress"] = new WallElement("fortress", "structures/" + civ + "_fortress", PI, 0, 4.5*wallScaleByType[style]);
143 wallStyles[style]["house"] = new WallElement("house", "structures/" + civ + "_house", PI, 0, 4.5*wallScaleByType[style]);
144 wallStyles[style]["market"] = new WallElement("market", "structures/" + civ + "_market", PI, 0, 4.5*wallScaleByType[style]);
145 wallStyles[style]["storehouse"] = new WallElement("storehouse", "structures/" + civ + "_storehouse", PI, 0, 4.5*wallScaleByType[style]);
146 wallStyles[style]["temple"] = new WallElement("temple", "structures/" + civ + "_temple", PI, 0, 4.5*wallScaleByType[style]);
147 // Generic space/gap wall elements
148 wallStyles[style]["space1"] = new WallElement("space1", undefined, 0*PI, wallScaleByType[style]);
149 wallStyles[style]["space2"] = new WallElement("space2", undefined, 0*PI, 2*wallScaleByType[style]);
150 wallStyles[style]["space3"] = new WallElement("space3", undefined, 0*PI, 3*wallScaleByType[style]);
151 wallStyles[style]["space4"] = new WallElement("space4", undefined, 0*PI, 4*wallScaleByType[style]);
152}
153// Add wall fortresses for all generic styles
154wallStyles["athen"]["wallFort"] = new WallElement("wallFort", "structures/athen_fortress", 2*PI/2 /* PI/2 */, 5.1 /* 5.6 */, 1.9 /* 1.9 */);
155wallStyles["brit"]["wallFort"] = new WallElement("wallFort", "structures/brit_fortress", PI, 2.8);
156wallStyles["cart"]["wallFort"] = new WallElement("wallFort", "structures/cart_fortress", PI, 5.1, 1.6);
157wallStyles["celt"]["wallFort"] = new WallElement("wallFort", "structures/celt_fortress_g", PI, 4.2, 1.5);
158wallStyles["gaul"]["wallFort"] = new WallElement("wallFort", "structures/gaul_fortress", PI, 4.2, 1.5);
159wallStyles["hele"]["wallFort"] = new WallElement("wallFort", "structures/hele_fortress", 2*PI/2 /* PI/2 */, 5.1 /* 5.6 */, 1.9 /* 1.9 */);
160wallStyles["iber"]["wallFort"] = new WallElement("wallFort", "structures/iber_fortress", PI, 5, 0.2);
161wallStyles["mace"]["wallFort"] = new WallElement("wallFort", "structures/mace_fortress", 2*PI/2 /* PI/2 */, 5.1 /* 5.6 */, 1.9 /* 1.9 */);
162wallStyles["maur"]["wallFort"] = new WallElement("wallFort", "structures/maur_fortress", PI, 5.5);
163wallStyles["pers"]["wallFort"] = new WallElement("wallFort", "structures/pers_fortress", PI, 5.6/*5.5*/, 1.9/*1.7*/);
164wallStyles["ptol"]["wallFort"] = new WallElement("wallFort", "structures/ptol_fortress", 2*PI/2 /* PI/2 */, 5.1 /* 5.6 */, 1.9 /* 1.9 */);
165wallStyles["rome"]["wallFort"] = new WallElement("wallFort", "structures/rome_fortress", PI, 6.3, 2.1);
166wallStyles["sele"]["wallFort"] = new WallElement("wallFort", "structures/hele_fortress", 2*PI/2 /* PI/2 */, 5.1 /* 5.6 */, 1.9 /* 1.9 */);
167wallStyles["spart"]["wallFort"] = new WallElement("wallFort", "structures/spart_fortress", 2*PI/2 /* PI/2 */, 5.1 /* 5.6 */, 1.9 /* 1.9 */);
168// Adjust "rome_siege" style
169wallStyles["rome_siege"]["wallFort"] = new WallElement("wallFort", "structures/rome_army_camp", PI, 7.2, 2);
170wallStyles["rome_siege"]["entryFort"] = new WallElement("entryFort", "structures/rome_army_camp", PI, 12, 7);
171wallStyles["rome_siege"]["house"] = new WallElement("house", "structures/rome_tent", PI, 0, 4);
172
173// Add special wall styles not well to implement generic (and to show how custom styles can be added)
174
175// Add wall style "palisades"
176wallScaleByType["palisades"] = 0.55;
177wallStyles["palisades"] = {};
178wallStyles["palisades"]["wall"] = new WallElement("wall", "other/palisades_rocks_medium", 0*PI, 2.3);
179wallStyles["palisades"]["wallMedium"] = new WallElement("wall", "other/palisades_rocks_medium", 0*PI, 2.3);
180wallStyles["palisades"]["wallLong"] = new WallElement("wall", "other/palisades_rocks_long", 0*PI, 3.5);
181wallStyles["palisades"]["wallShort"] = new WallElement("wall", "other/palisades_rocks_short", 0*PI, 1.2);
182wallStyles["palisades"]["tower"] = new WallElement("tower", "other/palisades_rocks_tower", -PI/2, 0.7);
183wallStyles["palisades"]["wallFort"] = new WallElement("wallFort", "other/palisades_rocks_fort", PI, 1.7);
184wallStyles["palisades"]["gate"] = new WallElement("gate", "other/palisades_rocks_gate", PI, 3.6);
185wallStyles["palisades"]["entry"] = new WallElement("entry", undefined, wallStyles["palisades"]["gate"].angle, wallStyles["palisades"]["gate"].width);
186wallStyles["palisades"]["entryTower"] = new WallElement("entryTower", "other/palisades_rocks_watchtower", 0*PI, wallStyles["palisades"]["gate"].width, -3);
187wallStyles["palisades"]["entryFort"] = new WallElement("entryFort", "other/palisades_rocks_fort", PI, 6, 3);
188wallStyles["palisades"]["cornerIn"] = new WallElement("cornerIn", "other/palisades_rocks_curve", 3*PI/4, 2.1, 0.7, PI/2);
189wallStyles["palisades"]["cornerOut"] = new WallElement("cornerOut", "other/palisades_rocks_curve", 5*PI/4, 2.1, -0.7, -PI/2);
190wallStyles["palisades"]["outpost"] = new WallElement("outpost", "other/palisades_rocks_outpost", PI, 0, -2);
191wallStyles["palisades"]["house"] = new WallElement("house", "other/celt_hut", PI, 0, 5);
192wallStyles["palisades"]["barracks"] = new WallElement("barracks", "structures/gaul_tavern", PI, 0, 5);
193wallStyles["palisades"]["endRight"] = new WallElement("endRight", "other/palisades_rocks_end", -PI/2, 0.2);
194wallStyles["palisades"]["endLeft"] = new WallElement("endLeft", "other/palisades_rocks_end", PI/2, 0.2);
195
196// Add special wall style "road"
197// NOTE: This is not a wall style in the common sense. Use with care!
198wallStyles["road"] = {};
199wallStyles["road"]["short"] = new WallElement("road", "actor|props/special/eyecandy/road_temperate_short.xml", PI/2, 4.5);
200wallStyles["road"]["long"] = new WallElement("road", "actor|props/special/eyecandy/road_temperate_long.xml", PI/2, 9.5);
201wallStyles["road"]["cornerLeft"] = new WallElement("road", "actor|props/special/eyecandy/road_temperate_corner.xml", -PI/2, 4.5-2*1.25, 1.25, PI/2); // Correct width by -2*indent to fit xStraicht/corner
202wallStyles["road"]["cornerRight"] = new WallElement("road", "actor|props/special/eyecandy/road_temperate_corner.xml", 0*PI, 4.5-2*1.25, -1.25, -PI/2); // Correct width by -2*indent to fit xStraicht/corner
203wallStyles["road"]["curveLeft"] = new WallElement("road", "actor|props/special/eyecandy/road_temperate_curve_small.xml", -PI/2, 4.5+2*0.2, -0.2, PI/2); // Correct width by -2*indent to fit xStraicht/corner
204wallStyles["road"]["curveRight"] = new WallElement("road", "actor|props/special/eyecandy/road_temperate_curve_small.xml", 0*PI, 4.5+2*0.2, 0.2, -PI/2); // Correct width by -2*indent to fit xStraicht/corner
205wallStyles["road"]["start"] = new WallElement("road", "actor|props/special/eyecandy/road_temperate_end.xml", PI/2, 2);
206wallStyles["road"]["end"] = new WallElement("road", "actor|props/special/eyecandy/road_temperate_end.xml", -PI/2, 2);
207wallStyles["road"]["xStraight"] = new WallElement("road", "actor|props/special/eyecandy/road_temperate_intersect_x.xml", 0*PI, 4.5);
208wallStyles["road"]["xLeft"] = new WallElement("road", "actor|props/special/eyecandy/road_temperate_intersect_x.xml", 0*PI, 4.5, 0, PI/2);
209wallStyles["road"]["xRight"] = new WallElement("road", "actor|props/special/eyecandy/road_temperate_intersect_x.xml", 0*PI, 4.5, 0, -PI/2);
210wallStyles["road"]["tLeft"] = new WallElement("road", "actor|props/special/eyecandy/road_temperate_intersect_T.xml", PI, 4.5, 1.25);
211wallStyles["road"]["tRight"] = new WallElement("road", "actor|props/special/eyecandy/road_temperate_intersect_T.xml", 0*PI, 4.5, -1.25);
212
213// Add special wall element collection "other"
214// NOTE: This is not a wall style in the common sense. Use with care!
215wallStyles["other"] = {};
216wallStyles["other"]["fence"] = new WallElement("fence", "other/fence_long", -PI/2, 3.1);
217wallStyles["other"]["fence_medium"] = new WallElement("fence", "other/fence_long", -PI/2, 3.1);
218wallStyles["other"]["fence_short"] = new WallElement("fence_short", "other/fence_short", -PI/2, 1.5);
219wallStyles["other"]["fence_stone"] = new WallElement("fence_stone", "other/fence_stone", -PI/2, 2.5);
220wallStyles["other"]["palisade"] = new WallElement("palisade", "other/palisades_rocks_short", 0, 1.2);
221wallStyles["other"]["column"] = new WallElement("column", "other/column_doric", 0, 1);
222wallStyles["other"]["obelisk"] = new WallElement("obelisk", "other/obelisk", 0, 2);
223wallStyles["other"]["spike"] = new WallElement("spike", "other/palisades_angle_spike", -PI/2, 1);
224wallStyles["other"]["bench"] = new WallElement("bench", "other/bench", PI/2, 1.5);
225wallStyles["other"]["benchForTable"] = new WallElement("benchForTable", "other/bench", 0, 0.5);
226wallStyles["other"]["table"] = new WallElement("table", "other/table_rectangle", 0, 1);
227wallStyles["other"]["table_square"] = new WallElement("table_square", "other/table_square", PI/2, 1);
228wallStyles["other"]["flag"] = new WallElement("flag", "special/rallypoint", PI, 1);
229wallStyles["other"]["standing_stone"] = new WallElement("standing_stone", "gaia/special_ruins_standing_stone", PI, 1);
230wallStyles["other"]["settlement"] = new WallElement("settlement", "gaia/special_settlement", PI, 6);
231wallStyles["other"]["gap"] = new WallElement("gap", undefined, 0, 2);
232wallStyles["other"]["gapSmall"] = new WallElement("gapSmall", undefined, 0, 1);
233wallStyles["other"]["gapLarge"] = new WallElement("gapLarge", undefined, 0, 4);
234wallStyles["other"]["cornerIn"] = new WallElement("cornerIn", undefined, 0, 0, 0, PI/2);
235wallStyles["other"]["cornerOut"] = new WallElement("cornerOut", undefined, 0, 0, 0, -PI/2);
236
237
238////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
239// fortressTypes data structure for some default fortress types
240//
241// A fortress type is just an instance of the Fortress class with actually something in it
242// fortressTypes holds all the fortresses within an associative array with a descriptive string as key (e.g. matching the map size)
243// Examples: "tiny", "veryLarge"
244////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
245var fortressTypes = {};
246// Setup some default fortress types
247// Add fortress type "tiny"
248fortressTypes["tiny"] = new Fortress("tiny");
249var wallPart = ["gate", "tower", "wallShort", "cornerIn", "wallShort", "tower"];
250fortressTypes["tiny"].wall = wallPart.concat(wallPart, wallPart, wallPart);
251// Add fortress type "small"
252fortressTypes["small"] = new Fortress("small");
253var wallPart = ["gate", "tower", "wall", "cornerIn", "wall", "tower"];
254fortressTypes["small"].wall = wallPart.concat(wallPart, wallPart, wallPart);
255// Add fortress type "medium"
256fortressTypes["medium"] = new Fortress("medium");
257var wallPart = ["gate", "tower", "wallLong", "cornerIn", "wallLong", "tower"];
258fortressTypes["medium"].wall = wallPart.concat(wallPart, wallPart, wallPart);
259// Add fortress type "normal"
260fortressTypes["normal"] = new Fortress("normal");
261var wallPart = ["gate", "tower", "wall", "cornerIn", "wall", "cornerOut", "wall", "cornerIn", "wall", "tower"];
262fortressTypes["normal"].wall = wallPart.concat(wallPart, wallPart, wallPart);
263// Add fortress type "large"
264fortressTypes["large"] = new Fortress("large");
265var wallPart = ["gate", "tower", "wallLong", "cornerIn", "wallLong", "cornerOut", "wallLong", "cornerIn", "wallLong", "tower"];
266fortressTypes["large"].wall = wallPart.concat(wallPart, wallPart, wallPart);
267// Add fortress type "veryLarge"
268fortressTypes["veryLarge"] = new Fortress("veryLarge");
269var wallPart = ["gate", "tower", "wall", "cornerIn", "wall", "cornerOut", "wallLong", "cornerIn", "wallLong", "cornerOut", "wall", "cornerIn", "wall", "tower"];
270fortressTypes["veryLarge"].wall = wallPart.concat(wallPart, wallPart, wallPart);
271// Add fortress type "giant"
272fortressTypes["giant"] = new Fortress("giant");
273var wallPart = ["gate", "tower", "wallLong", "cornerIn", "wallLong", "cornerOut", "wallLong", "cornerIn", "wallLong", "cornerOut", "wallLong", "cornerIn", "wallLong", "tower"];
274fortressTypes["giant"].wall = wallPart.concat(wallPart, wallPart, wallPart);
275
276// Setup some better looking semi default fortresses for "palisades" style
277var fortressTypeKeys = ["tiny", "small", "medium", "normal", "large", "veryLarge", "giant"];
278for (var i = 0; i < fortressTypeKeys.length; i++)
279{
280 var newKey = fortressTypeKeys[i] + "Palisades";
281 var oldWall = fortressTypes[fortressTypeKeys[i]].wall;
282 fortressTypes[newKey] = new Fortress(newKey);
283 var fillTowersBetween = ["wallShort", "wall", "wallLong", "endLeft", "endRight", "cornerIn", "cornerOut"];
284 for (var j = 0; j < oldWall.length; j++)
285 {
286 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)
287 if (j+1 < oldWall.length)
288 if (fillTowersBetween.indexOf(oldWall[j]) > -1 && fillTowersBetween.indexOf(oldWall[j+1]) > -1) // ... > -1 means "exists" here
289 fortressTypes[newKey].wall.push("tower");
290 }
291}
292
293// Setup some balanced (to civ type fortresses) semi default fortresses for "palisades" style
294// TODO
295
296// Add some "fortress types" for roads (will only work with style "road")
297// ["start", "short", "xRight", "xLeft", "cornerLeft", "xStraight", "long", "xLeft", "xRight", "cornerRight", "tRight", "tLeft", "xRight", "xLeft", "curveLeft", "xStraight", "curveRight", "end"];
298var wall = ["short", "curveLeft", "short", "curveLeft", "short", "curveLeft", "short", "curveLeft"];
299fortressTypes["road01"] = new Fortress("road01", wall);
300var wall = ["short", "cornerLeft", "short", "cornerLeft", "short", "cornerLeft", "short", "cornerLeft"];
301fortressTypes["road02"] = new Fortress("road02", wall);
302var wall = ["xStraight", "curveLeft", "xStraight", "curveLeft", "xStraight", "curveLeft", "xStraight", "curveLeft"];
303fortressTypes["road03"] = new Fortress("road03", wall);
304var wall = ["start", "curveLeft", "tRight", "cornerLeft", "tRight", "curveRight", "short", "xRight", "curveLeft", "xRight", "short", "cornerLeft", "tRight", "short",
305 "curveLeft", "short", "tRight", "cornerLeft", "short", "xRight", "curveLeft", "xRight", "short", "curveRight", "tRight", "cornerLeft", "tRight", "curveLeft", "end"];
306fortressTypes["road04"] = new Fortress("road04", wall);
307var wall = ["start", "tLeft", "short", "xRight",
308 "curveLeft", "xRight", "tRight", "cornerLeft", "tRight",
309 "curveLeft", "short", "tRight", "cornerLeft", "xRight",
310 "cornerLeft", "xRight", "short", "tRight", "curveLeft", "end"];
311fortressTypes["road05"] = new Fortress("road05", wall);
312
313
314///////////////////////////////
315// Define some helper functions
316///////////////////////////////
317
318/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
319// getWallAlignment
320//
321// Returns a list of objects containing all information to place all the wall elements entities with placeObject (but the player ID)
322// Placing the first wall element at startX/startY placed with an angle given by orientation
323// An alignment can be used to get the "center" of a "wall" (more likely used for fortresses) with getCenterToFirstElement
324/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
325function getWallAlignment(startX, startY, wall, style, orientation)
326{
327 // Graciously handle arguments
328 if (wall === undefined)
329 wall = [];
330 if (!wallStyles.hasOwnProperty(style))
331 {
332 warn("Function getWallAlignment: Unknown style: " + style + ' (falling back to "athen")');
333 style = "athen";
334 }
335 orientation = (orientation || 0);
336
337 var alignment = [];
338 var wallX = startX;
339 var wallY = startY;
340 for (var i = 0; i < wall.length; i++)
341 {
342 var element = wallStyles[style][wall[i]];
343 if (element === undefined && i == 0)
344 warn("No valid wall element: " + wall[i]);
345 // Indentation
346 var placeX = wallX - element.indent * cos(orientation);
347 var placeY = wallY - element.indent * sin(orientation);
348 // Add wall elements entity placement arguments to the alignment
349 alignment.push({"x": placeX, "y": placeY, "entity": element.entity, "angle":orientation + element.angle});
350 // Preset vars for the next wall element
351 if (i+1 < wall.length)
352 {
353 orientation += element.bending;
354 var nextElement = wallStyles[style][wall[i+1]];
355 if (nextElement === undefined)
356 warn("No valid wall element: " + wall[i+1]);
357 var distance = (element.width + nextElement.width)/2;
358 // Corrections for elements with indent AND bending
359 var indent = element.indent;
360 var bending = element.bending;
361 if (bending !== 0 && indent !== 0)
362 {
363 // Indent correction to adjust distance
364 distance += indent*sin(bending);
365 // Indent correction to normalize indentation
366 wallX += indent * cos(orientation);
367 wallY += indent * sin(orientation);
368 }
369 // Set the next coordinates of the next element in the wall without indentation adjustment
370 wallX -= distance * sin(orientation);
371 wallY += distance * cos(orientation);
372 }
373 }
374 return alignment;
375}
376
377//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
378// getCenterToFirstElement
379//
380// Center calculation works like getting the center of mass assuming all wall elements have the same "weight"
381//
382// It returns the vector from the center to the first wall element
383// Used to get centerToFirstElement of fortresses by default
384//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
385function getCenterToFirstElement(alignment)
386{
387 var centerToFirstElement = {"x": 0, "y": 0};
388 for (var i = 0; i < alignment.length; i++)
389 {
390 centerToFirstElement.x -= alignment[i].x/alignment.length;
391 centerToFirstElement.y -= alignment[i].y/alignment.length;
392 }
393 return centerToFirstElement;
394}
395
396//////////////////////////////////////////////////////////////////
397// getWallLength
398//
399// NOTE: Does not support bending wall elements like corners!
400// e.g. used by placeIrregularPolygonalWall
401//////////////////////////////////////////////////////////////////
402function getWallLength(wall, style)
403{
404 // Graciously handle arguments
405 if (wall === undefined)
406 wall = [];
407 if (!wallStyles.hasOwnProperty(style))
408 {
409 warn("Function getWallLength: Unknown style: " + style + ' (falling back to "athen")');
410 style = "athen";
411 }
412
413 var length = 0;
414 for (var i = 0; i < wall.length; i++)
415 {
416 length += wallStyles[style][wall[i]].width;
417 }
418 return length;
419}
420
421
422/////////////////////////////////////////////
423// Define the different wall placer functions
424/////////////////////////////////////////////
425
426/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
427// placeWall
428//
429// Places a wall with wall elements attached to another like determined by WallElement properties.
430//
431// startX, startY Where the first wall element should be placed
432// wall Array of wall element type strings. Example: ["endLeft", "wallLong", "tower", "wallLong", "endRight"]
433// style Optional. Wall style string. Default is the civ of the given player, "palisades" for gaia
434// playerId Optional. Number of the player the wall will be placed for. Default is 0 (gaia)
435// orientation Optional. Angle the first wall element is placed. Default is 0
436// 0 means "outside" or "front" of the wall is right (positive X) like placeObject
437// It will then be build towards top/positive Y (if no bending wall elements like corners are used)
438// Raising orientation means the wall is rotated counter-clockwise like placeObject
439/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
440function placeWall(startX, startY, wall, style, playerId, orientation)
441{
442 // Graciously handle arguments
443 if (wall === undefined)
444 wall = [];
445 playerId = (playerId || 0);
446 if (!wallStyles.hasOwnProperty(style))
447 {
448 if (playerId == 0)
449 style = (style || "palisades");
450 else
451 style = (getCivCode(playerId-1));
452 }
453 orientation = (orientation || 0);
454
455 // Get wall alignment
456 var AM = getWallAlignment(startX, startY, wall, style, orientation);
457 // Place the wall
458 for (var iWall = 0; iWall < wall.length; iWall++)
459 {
460 var entity = AM[iWall].entity;
461 if (entity !== undefined)
462 placeObject(AM[iWall].x, AM[iWall].y, entity, playerId, AM[iWall].angle);
463 }
464}
465
466/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
467// placeCustomFortress
468//
469// Place a fortress (mainly a closed wall build like placeWall) centered at centerX/centerY
470// The fortress wall should always start with the main entrance (like "entry" or "gate") to get the orientation right (like placeObject)
471//
472// fortress An instance of Fortress with a wall defined
473// style Optional. Wall style string. Default is the civ of the given player, "palisades" for gaia
474// playerId Optional. Number of the player the wall will be placed for. Default is 0 (gaia)
475// orientation Optional. Angle the first wall element (should be a gate or entrance) is placed. Default is 0
476/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
477function placeCustomFortress(centerX, centerY, fortress, style, playerId, orientation)
478{
479 // Graciously handle arguments
480 fortress = (fortress || fortressTypes["medium"]);
481 playerId = (playerId || 0);
482 if (!wallStyles.hasOwnProperty(style))
483 {
484 if (playerId == 0)
485 style = (style || "palisades");
486 else
487 style = (getCivCode(playerId-1));
488 }
489 orientation = (orientation || 0);
490
491 // Calculate center if fortress.centerToFirstElement is undefined (default)
492 var centerToFirstElement = fortress.centerToFirstElement;
493 if (centerToFirstElement === undefined)
494 centerToFirstElement = getCenterToFirstElement(getWallAlignment(0, 0, fortress.wall, style));
495 // Placing the fortress wall
496 var startX = centerX + centerToFirstElement.x * cos(orientation) - centerToFirstElement.y * sin(orientation);
497 var startY = centerY + centerToFirstElement.y * cos(orientation) + centerToFirstElement.x * sin(orientation);
498 placeWall(startX, startY, fortress.wall, style, playerId, orientation)
499}
500
501///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
502// placeFortress
503//
504// Like placeCustomFortress just it takes type (a fortress type string, has to be in fortressTypes) instead of an instance of Fortress
505///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
506function placeFortress(centerX, centerY, type, style, playerId, orientation)
507{
508 // Graciously handle arguments
509 type = (type || "medium");
510 playerId = (playerId || 0);
511 if (!wallStyles.hasOwnProperty(style))
512 {
513 if (playerId == 0)
514 style = (style || "palisades");
515 else
516 style = (getCivCode(playerId-1));
517 }
518 orientation = (orientation || 0);
519
520 // Call placeCustomFortress with the given arguments
521 placeCustomFortress(centerX, centerY, fortressTypes[type], style, playerId, orientation);
522}
523
524//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
525// placeLinearWall
526//
527// Places a straight wall from a given coordinate to an other repeatedly using the wall parts.
528//
529// startX/startY Coordinate of the approximate beginning of the wall (Not the place of the first wall element)
530// targetX/targetY Coordinate of the approximate ending of the wall (Not the place of the last wall element)
531// wallPart Optional. An array of NON-BENDING wall element type strings. Default is ["tower", "wallLong"]
532// style Optional. Wall style string. Default is the civ of the given player, "palisades" for gaia
533// playerId Optional. Integer number of the player. Default is 0 (gaia)
534// endWithFirst Optional. A boolean value. If true the 1st wall element in the wallPart array will finalize the wall. Default is true
535//
536// TODO: Maybe add angle offset for more generic looking?
537//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
538function placeLinearWall(startX, startY, targetX, targetY, wallPart, style, playerId, endWithFirst)
539{
540 // Setup optional arguments to the default
541 wallPart = (wallPart || ["tower", "wallLong"]);
542 playerId = (playerId || 0);
543 if (!wallStyles.hasOwnProperty(style))
544 {
545 if (playerId == 0)
546 style = (style || "palisades");
547 else
548 style = (getCivCode(playerId-1));
549 }
550 endWithFirst = typeof endWithFirst == "undefined" ? true : endWithFirst;
551
552 // Check arguments
553 for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++)
554 {
555 var bending = wallStyles[style][wallPart[elementIndex]].bending;
556 if (bending != 0)
557 warn("Bending is not supported by placeLinearWall but a bending wall element is used: " + wallPart[elementIndex] + " -> wallStyles[style][wallPart[elementIndex]].entity");
558 }
559 // Setup number of wall parts
560 var totalLength = getDistance(startX, startY, targetX, targetY);
561 var wallPartLength = 0;
562 for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++)
563 wallPartLength += wallStyles[style][wallPart[elementIndex]].width;
564 var numParts = 0;
565 if (endWithFirst == true)
566 numParts = ceil((totalLength - wallStyles[style][wallPart[0]].width) / wallPartLength);
567 else
568 numParts = ceil(totalLength / wallPartLength);
569 // Setup scale factor
570 var scaleFactor = 1;
571 if (endWithFirst == true)
572 scaleFactor = totalLength / (numParts * wallPartLength + wallStyles[style][wallPart[0]].width);
573 else
574 scaleFactor = totalLength / (numParts * wallPartLength);
575 // Setup angle
576 var wallAngle = getAngle(startX, startY, targetX, targetY); // NOTE: function "getAngle()" is about to be changed...
577 var placeAngle = wallAngle - PI/2;
578 // Place wall entities
579 var x = startX;
580 var y = startY;
581 for (var partIndex = 0; partIndex < numParts; partIndex++)
582 {
583 for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++)
584 {
585 var wallEle = wallStyles[style][wallPart[elementIndex]];
586 // Width correction
587 x += scaleFactor * wallEle.width/2 * cos(wallAngle);
588 y += scaleFactor * wallEle.width/2 * sin(wallAngle);
589 // Indent correction
590 var placeX = x - wallEle.indent * sin(wallAngle);
591 var placeY = y + wallEle.indent * cos(wallAngle);
592 // Placement
593 var entity = wallEle.entity;
594 if (entity !== undefined)
595 placeObject(placeX, placeY, entity, playerId, placeAngle + wallEle.angle);
596 x += scaleFactor * wallEle.width/2 * cos(wallAngle);
597 y += scaleFactor * wallEle.width/2 * sin(wallAngle);
598 }
599 }
600 if (endWithFirst == true)
601 {
602 var wallEle = wallStyles[style][wallPart[0]];
603 x += scaleFactor * wallEle.width/2 * cos(wallAngle);
604 y += scaleFactor * wallEle.width/2 * sin(wallAngle);
605 var entity = wallEle.entity;
606 if (entity !== undefined)
607 placeObject(x, y, entity, playerId, placeAngle + wallEle.angle);
608 }
609}
610
611/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
612// placeCircularWall
613//
614// Place a circular wall of repeated wall elements given in the argument wallPart around centerX/centerY with the given radius
615// The wall can be opened forming more an arc than a circle if maxAngle < 2*PI
616// The orientation then determines where this open part faces (0 means right like unrotated building's drop-points)
617//
618// centerX/Y Coordinates of the circle's center
619// radius How wide the circle should be (approximate, especially if maxBendOff != 0)
620// wallPart Optional. An array of NON-BENDING wall element type strings. Default is ["tower", "wallLong"]
621// style Optional. Wall style string. Default is the civ of the given player, "palisades" for gaia
622// playerId Optional. Integer number of the player. Default is 0 (gaia)
623// orientation Optional. Where the open part of the (circular) arc should face (if maxAngle is < 2*PI). Default is 0
624// maxAngle Optional. How far the wall should circumvent the center. Default is 2*PI (full circle)
625// 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
626// maxBendOff Optional. How irregular the circle should be. 0 means regular circle, PI/2 means very irregular. Default is 0 (regular circle)
627//
628// NOTE: Don't use wall elements with bending like corners!
629// TODO: Perhaps add eccentricity and maxBendOff functionality (untill now an unused argument)
630// TODO: Perhaps add functionality for spirals
631/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
632function placeCircularWall(centerX, centerY, radius, wallPart, style, playerId, orientation, maxAngle, endWithFirst, maxBendOff)
633{
634 // Setup optional arguments to the default
635 wallPart = (wallPart || ["tower", "wallLong"]);
636 playerId = (playerId || 0);
637 if (!wallStyles.hasOwnProperty(style))
638 {
639 if (playerId == 0)
640 style = (style || "palisades");
641 else
642 style = (getCivCode(playerId-1));
643 }
644 orientation = (orientation || 0);
645 maxAngle = (maxAngle || 2*PI);
646 if (endWithFirst === undefined)
647 {
648 if (maxAngle >= 2*PI - 0.001) // Can this be done better?
649 endWithFirst = false;
650 else
651 endWithFirst = true;
652 }
653 maxBendOff = (maxBendOff || 0);
654
655 // Check arguments
656 if (maxBendOff > PI/2 || maxBendOff < 0)
657 warn("placeCircularWall maxBendOff sould satisfy 0 < maxBendOff < PI/2 (~1.5) but it is: " + maxBendOff);
658 for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++)
659 {
660 var bending = wallStyles[style][wallPart[elementIndex]].bending;
661 if (bending != 0)
662 warn("Bending is not supported by placeCircularWall but a bending wall element is used: " + wallPart[elementIndex]);
663 }
664 // Setup number of wall parts
665 var totalLength = maxAngle * radius;
666 var wallPartLength = 0;
667 for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++)
668 wallPartLength += wallStyles[style][wallPart[elementIndex]].width;
669 var numParts = 0;
670 if (endWithFirst == true)
671 {
672 numParts = ceil((totalLength - wallStyles[style][wallPart[0]].width) / wallPartLength);
673 }
674 else
675 {
676 numParts = ceil(totalLength / wallPartLength);
677 }
678 // Setup scale factor
679 var scaleFactor = 1;
680 if (endWithFirst == true)
681 scaleFactor = totalLength / (numParts * wallPartLength + wallStyles[style][wallPart[0]].width);
682 else
683 scaleFactor = totalLength / (numParts * wallPartLength);
684 // Place wall entities
685 var actualAngle = orientation + (2*PI - maxAngle) / 2;
686 var x = centerX + radius*cos(actualAngle);
687 var y = centerY + radius*sin(actualAngle);
688 for (var partIndex = 0; partIndex < numParts; partIndex++)
689 {
690 for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++)
691 {
692 var wallEle = wallStyles[style][wallPart[elementIndex]];
693 // Width correction
694 var addAngle = scaleFactor * wallEle.width / radius;
695 var targetX = centerX + radius * cos(actualAngle + addAngle);
696 var targetY = centerY + radius * sin(actualAngle + addAngle);
697 var placeX = x + (targetX - x)/2;
698 var placeY = y + (targetY - y)/2;
699 var placeAngle = actualAngle + addAngle/2;
700 // Indent correction
701 placeX -= wallEle.indent * cos(placeAngle);
702 placeY -= wallEle.indent * sin(placeAngle);
703 // Placement
704 var entity = wallEle.entity;
705 if (entity !== undefined)
706 placeObject(placeX, placeY, entity, playerId, placeAngle + wallEle.angle);
707 // Prepare for the next wall element
708 actualAngle += addAngle;
709 x = centerX + radius*cos(actualAngle);
710 y = centerY + radius*sin(actualAngle);
711 }
712 }
713 if (endWithFirst == true)
714 {
715 var wallEle = wallStyles[style][wallPart[0]];
716 var addAngle = scaleFactor * wallEle.width / radius;
717 var targetX = centerX + radius * cos(actualAngle + addAngle);
718 var targetY = centerY + radius * sin(actualAngle + addAngle);
719 var placeX = x + (targetX - x)/2;
720 var placeY = y + (targetY - y)/2;
721 var placeAngle = actualAngle + addAngle/2;
722 placeObject(placeX, placeY, wallEle.entity, playerId, placeAngle + wallEle.angle);
723 }
724}
725
726/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
727// placePolygonalWall
728//
729// Place a polygonal wall of repeated wall elements given in the argument wallPart around centerX/centerY with the given radius
730//
731// centerX/Y Coordinates of the polygon's center
732// radius How wide the circle should be in which the polygon fits
733// wallPart Optional. An array of NON-BENDING wall element type strings. Default is ["wallLong", "tower"]
734// cornerWallElement Optional. Wall element to be placed at the polygon's corners. Default is "tower"
735// style Optional. Wall style string. Default is the civ of the given player, "palisades" for gaia
736// playerId Optional. Integer number of the player. Default is 0 (gaia)
737// orientation Optional. Angle from the center to the first linear wall part placed. Default is 0 (towards positive X/right)
738// numCorners Optional. How many corners the polygon will have. Default is 8 (matching a civ centers territory)
739// skipFirstWall Optional. Boolean. If the first linear wall part will be left opened as entrance. Default is true
740//
741// NOTE: Don't use wall elements with bending like corners!
742// TODO: Replace skipFirstWall with firstWallPart to enable gate/defended entrance placement
743// TODO: Check some arguments
744// TODO: Add eccentricity and perhaps make it just call placeIrregularPolygonalWall with irregularity = 0
745/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
746function placePolygonalWall(centerX, centerY, radius, wallPart, cornerWallElement, style, playerId, orientation, numCorners, skipFirstWall)
747{
748 // Setup optional arguments to the default
749 wallPart = (wallPart || ["wallLong", "tower"]);
750 cornerWallElement = (cornerWallElement || "tower"); // Don't use wide elements for this. Not supported well...
751 playerId = (playerId || 0);
752 if (!wallStyles.hasOwnProperty(style))
753 {
754 if (playerId == 0)
755 style = (style || "palisades");
756 else
757 style = (getCivCode(playerId-1));
758 }
759 orientation = (orientation || 0);
760 numCorners = (numCorners || 8);
761 skipFirstWall = (skipFirstWall || true);
762
763 // Setup angles
764 var angleAdd = 2*PI/numCorners;
765 var angleStart = orientation - angleAdd/2;
766 // Setup corners
767 var corners = [];
768 for (var i = 0; i < numCorners; i++)
769 corners.push([centerX + radius*cos(angleStart + i*angleAdd), centerY + radius*sin(angleStart + i*angleAdd)]);
770 // Place Corners and walls
771 for (var i = 0; i < numCorners; i++)
772 {
773 var angleToCorner = getAngle(corners[i][0], corners[i][1], centerX, centerY);
774 placeObject(corners[i][0], corners[i][1], wallStyles[style][cornerWallElement].entity, playerId, angleToCorner);
775 if (!(skipFirstWall && i == 0))
776 {
777 placeLinearWall(
778 // Adjustment to the corner element width (approximately)
779 corners[i][0] + wallStyles[style][cornerWallElement].width/2 * sin(angleToCorner + angleAdd/2), // startX
780 corners[i][1] - wallStyles[style][cornerWallElement].width/2 * cos(angleToCorner + angleAdd/2), // startY
781 corners[(i+1)%numCorners][0] - wallStyles[style][cornerWallElement].width/2 * sin(angleToCorner + angleAdd/2), // targetX
782 corners[(i+1)%numCorners][1] + wallStyles[style][cornerWallElement].width/2 * cos(angleToCorner + angleAdd/2), // targetY
783 wallPart, style, playerId);
784 }
785 }
786}
787
788////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
789// placeIrregularPolygonalWall
790//
791// Place an irregular polygonal wall of some wall parts to choose from around centerX/centerY with the given radius
792//
793// centerX/Y Coordinates of the polygon's center
794// radius How wide the circle should be in which the polygon fits
795// cornerWallElement Optional. Wall element to be placed at the polygon's corners. Default is "tower"
796// style Optional. Wall style string. Default is the civ of the given player, "palisades" for gaia
797// playerId Optional. Integer number of the player. Default is 0 (gaia)
798// orientation Optional. Angle from the center to the first linear wall part placed. Default is 0 (towards positive X/right)
799// numCorners Optional. How many corners the polygon will have. Default is 8 (matching a civ centers territory)
800// irregularity Optional. How irregular the polygon will be. 0 means regular, 1 means VERY irregular. Default is 0.5
801// skipFirstWall Optional. Boolean. If the first linear wall part will be left opened as entrance. Default is true
802// wallPartsAssortment Optional. An array of wall part arrays to choose from for each linear wall connecting the corners. Default is hard to describe ^^
803//
804// NOTE: wallPartsAssortment is put to the end because it's hardest to set
805// NOTE: Don't use wall elements with bending like corners!
806// TODO: Replace skipFirstWall with firstWallPart to enable gate/defended entrance placement
807// TODO: Check some arguments
808// TODO: Perhaps add eccentricity
809////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
810function placeIrregularPolygonalWall(centerX, centerY, radius, cornerWallElement, style, playerId, orientation, numCorners, irregularity, skipFirstWall, wallPartsAssortment)
811{
812 // Setup optional arguments
813 playerId = (playerId || 0);
814 if (!wallStyles.hasOwnProperty(style))
815 {
816 if (playerId == 0)
817 style = (style || "palisades");
818 else
819 style = (getCivCode(playerId-1));
820 }
821
822 // Generating a generic wall part assortment with each wall part including 1 gate lengthened by walls and towers
823 // NOTE: It might be a good idea to write an own function for that...
824 var defaultWallPartsAssortment = [["wallShort"], ["wall"], ["wallLong"], ["gate", "tower", "wallShort"]];
825 var centeredWallPart = ["gate"];
826 var extandingWallPartAssortment = [["tower", "wallLong"], ["tower", "wall"]];
827 defaultWallPartsAssortment.push(centeredWallPart);
828 for (var i = 0; i < extandingWallPartAssortment.length; i++)
829 {
830 var wallPart = centeredWallPart;
831 for (var j = 0; j < radius; j++)
832 {
833 if (j%2 == 0)
834 wallPart = wallPart.concat(extandingWallPartAssortment[i]);
835 else
836 {
837 extandingWallPartAssortment[i].reverse();
838 wallPart = extandingWallPartAssortment[i].concat(wallPart);
839 extandingWallPartAssortment[i].reverse();
840 }
841 defaultWallPartsAssortment.push(wallPart);
842 }
843 }
844 // Setup optional arguments to the default
845 wallPartsAssortment = (wallPartsAssortment || defaultWallPartsAssortment);
846 cornerWallElement = (cornerWallElement || "tower"); // Don't use wide elements for this. Not supported well...
847 style = (style || "palisades");
848 playerId = (playerId || 0);
849 orientation = (orientation || 0);
850 numCorners = (numCorners || randInt(5, 7));
851 irregularity = (irregularity || 0.5);
852 skipFirstWall = (skipFirstWall || false);
853 // Setup angles
854 var angleToCover = 2*PI;
855 var angleAddList = [];
856 for (var i = 0; i < numCorners; i++)
857 {
858 // Randomize covered angles. Variety scales down with raising angle though...
859 angleAddList.push(angleToCover/(numCorners-i) * (1 + randFloat(-irregularity, irregularity)));
860 angleToCover -= angleAddList[angleAddList.length - 1];
861 }
862 // Setup corners
863 var corners = [];
864 var angleActual = orientation - angleAddList[0]/2;
865 for (var i = 0; i < numCorners; i++)
866 {
867 corners.push([centerX + radius*cos(angleActual), centerY + radius*sin(angleActual)]);
868 if (i < numCorners - 1)
869 angleActual += angleAddList[i+1];
870 }
871 // Setup best wall parts for the different walls (a bit confusing naming...)
872 var wallPartLengths = [];
873 var maxWallPartLength = 0;
874 for (var partIndex = 0; partIndex < wallPartsAssortment.length; partIndex++)
875 {
876 var length = wallPartLengths[partIndex];
877 wallPartLengths.push(getWallLength(wallPartsAssortment[partIndex], style));
878 if (length > maxWallPartLength)
879 maxWallPartLength = length;
880 }
881 var wallPartList = []; // This is the list of the wall parts to use for the walls between the corners, not to confuse with wallPartsAssortment!
882 for (var i = 0; i < numCorners; i++)
883 {
884 var bestWallPart = []; // This is a simpel wall part not a wallPartsAssortment!
885 var bestWallLength = 99999999;
886 // NOTE: This is not exactly like the length the wall will be in the end. Has to be tweaked...
887 var wallLength = getDistance(corners[i][0], corners[i][1], corners[(i+1)%numCorners][0], corners[(i+1)%numCorners][1]);
888 var numWallParts = ceil(wallLength/maxWallPartLength);
889 for (var partIndex = 0; partIndex < wallPartsAssortment.length; partIndex++)
890 {
891 var linearWallLength = numWallParts*wallPartLengths[partIndex];
892 if (linearWallLength < bestWallLength && linearWallLength > wallLength)
893 {
894 bestWallPart = wallPartsAssortment[partIndex];
895 bestWallLength = linearWallLength;
896 }
897 }
898 wallPartList.push(bestWallPart);
899 }
900 // Place Corners and walls
901 for (var i = 0; i < numCorners; i++)
902 {
903 var angleToCorner = getAngle(corners[i][0], corners[i][1], centerX, centerY);
904 placeObject(corners[i][0], corners[i][1], wallStyles[style][cornerWallElement].entity, playerId, angleToCorner);
905 if (!(skipFirstWall && i == 0))
906 {
907 placeLinearWall(
908 // Adjustment to the corner element width (approximately)
909 corners[i][0] + wallStyles[style][cornerWallElement].width/2 * sin(angleToCorner + angleAddList[i]/2), // startX
910 corners[i][1] - wallStyles[style][cornerWallElement].width/2 * cos(angleToCorner + angleAddList[i]/2), // startY
911 corners[(i+1)%numCorners][0] - wallStyles[style][cornerWallElement].width/2 * sin(angleToCorner + angleAddList[(i+1)%numCorners]/2), // targetX
912 corners[(i+1)%numCorners][1] + wallStyles[style][cornerWallElement].width/2 * cos(angleToCorner + angleAddList[(i+1)%numCorners]/2), // targetY
913 wallPartList[i], style, playerId, false);
914 }
915 }
916}
917
918//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
919// placeGenericFortress
920//
921// Places a generic fortress with towers at the edges connected with long walls and gates (entries until gates work)
922// This is the default Iberian civ bonus starting wall
923//
924// centerX/Y The approximate center coordinates of the fortress
925// radius The approximate radius of the wall to be placed
926// playerId Optional. Integer number of the player. Default is 0 (gaia)
927// style Optional. Wall style string. Default is the civ of the given player, "palisades" for gaia
928// irregularity Optional. Float between 0 (circle) and 1 (very spiky), default is 1/2
929// gateOccurence Optional. Integer number, every n-th walls will be a gate instead. Default is 3
930// maxTrys Optional. How often the function tries to find a better fitting shape at max. Default is 100
931//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
932function placeGenericFortress(centerX, centerY, radius, playerId, style, irregularity, gateOccurence, maxTrys)
933{
934 // Setup optional arguments
935 radius = (radius || 20);
936 playerId = (playerId || 0);
937 if (!wallStyles.hasOwnProperty(style))
938 {
939 if (playerId == 0)
940 style = (style || "palisades");
941 else
942 style = (getCivCode(playerId-1));
943 }
944 irregularity = (irregularity || 1/2);
945 gateOccurence = (gateOccurence || 3);
946 maxTrys = (maxTrys || 100);
947
948 // Setup some vars
949 var startAngle = randFloat(0, 2*PI);
950 var actualOffX = radius*cos(startAngle);
951 var actualOffY = radius*sin(startAngle);
952 var actualAngle = startAngle;
953 var pointDistance = wallStyles[style]["wallLong"].width + wallStyles[style]["tower"].width;
954 // Searching for a well fitting point derivation
955 var tries = 0;
956 var bestPointDerivation = undefined;
957 var minOverlap = 1000;
958 var overlap = undefined;
959 while (tries < maxTrys && minOverlap > wallStyles[style]["tower"].width / 10)
960 {
961 var pointDerivation = [];
962 var distanceToTarget = 1000;
963 var targetReached = false;
964 while (!targetReached)
965 {
966 var indent = randFloat(-irregularity*pointDistance, irregularity*pointDistance);
967 var tmpAngle = getAngle(actualOffX, actualOffY,
968 (radius + indent)*cos(actualAngle + (pointDistance / radius)),
969 (radius + indent)*sin(actualAngle + (pointDistance / radius)));
970 actualOffX += pointDistance*cos(tmpAngle);
971 actualOffY += pointDistance*sin(tmpAngle);
972 actualAngle = getAngle(0, 0, actualOffX, actualOffY);
973 pointDerivation.push([actualOffX, actualOffY]);
974 distanceToTarget = getDistance(actualOffX, actualOffY, pointDerivation[0][0], pointDerivation[0][1]);
975 var numPoints = pointDerivation.length;
976 if (numPoints > 3 && distanceToTarget < pointDistance) // Could be done better...
977 {
978 targetReached = true;
979 overlap = pointDistance - getDistance(pointDerivation[numPoints - 1][0], pointDerivation[numPoints - 1][1], pointDerivation[0][0], pointDerivation[0][1]);
980 if (overlap < minOverlap)
981 {
982 minOverlap = overlap;
983 bestPointDerivation = pointDerivation;
984 }
985 }
986 }
987 tries++;
988 }
989 log("placeGenericFortress: Reduced overlap to " + minOverlap + " after " + tries + " tries");
990 // Place wall
991 for (var pointIndex = 0; pointIndex < bestPointDerivation.length; pointIndex++)
992 {
993 var startX = centerX + bestPointDerivation[pointIndex][0];
994 var startY = centerY + bestPointDerivation[pointIndex][1];
995 var targetX = centerX + bestPointDerivation[(pointIndex + 1) % bestPointDerivation.length][0];
996 var targetY = centerY + bestPointDerivation[(pointIndex + 1) % bestPointDerivation.length][1];
997 var angle = getAngle(startX, startY, targetX, targetY);
998 var wallElement = "wallLong";
999 if ((pointIndex + 1) % gateOccurence == 0)
1000 wallElement = "gate";
1001 var entity = wallStyles[style][wallElement].entity;
1002 if (entity)
1003 {
1004 placeObject(startX + (getDistance(startX, startY, targetX, targetY)/2)*cos(angle), // placeX
1005 startY + (getDistance(startX, startY, targetX, targetY)/2)*sin(angle), // placeY
1006 entity, playerId, angle - PI/2 + wallStyles[style][wallElement].angle);
1007 }
1008 // Place tower
1009 var startX = centerX + bestPointDerivation[(pointIndex + bestPointDerivation.length - 1) % bestPointDerivation.length][0];
1010 var startY = centerY + bestPointDerivation[(pointIndex + bestPointDerivation.length - 1) % bestPointDerivation.length][1];
1011 var angle = getAngle(startX, startY, targetX, targetY);
1012 placeObject(centerX + bestPointDerivation[pointIndex][0], centerY + bestPointDerivation[pointIndex][1], wallStyles[style]["tower"].entity, playerId, angle - PI/2 + wallStyles[style]["tower"].angle);
1013 }
1014}