This Trac instance is not used for development anymore!

We migrated our development workflow to git and Gitea.
To test the future redirection, replace trac by ariadne in the page URL.

Ticket #2258: wall_builder.js

File wall_builder.js, 58.5 KB (added by FeXoR, 11 years ago)

Modified wall_builder.js (handle arguments more gratiously and sane, fixed many spelling errors (sorry for that))

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