Ticket #1449: wall_builder.js

File wall_builder.js, 53.9 KB (added by FeXoR, 12 years ago)

wall_builder.js (added the new wall placement)

Line 
1////////////////////////////////////////////////////////////////////
2// This file contains functionality to place walls on random maps //
3////////////////////////////////////////////////////////////////////
4
5// To do:
6// Check if placeGenericFortress always works without gaps for all races
7// Rename wall elements to fit he entity names so that entity = 'structures/' + 'civ + '_' + wallElement.type in the common case
8// Add roman army camp to style palisades and add upgraded default palisade fortress types matching civ default fortresses
9// Add further wall elements cornerHalfIn, cornerHalfOut and adjust default fortress types to better fit in the octagonal territory of a civil center
10// Add civil center, corral, farmstead, field, market, mill, temple
11// Adjust default fortress types
12// Add wall style 'roads'
13// Add trsures to 'others'
14// Adjust documentation
15// ?Use available civ-type wall elements rather than palisades: Remove 'endLeft' and 'endRight' as default wall elements and adjust default palisade fortress types?
16// ?Remove endRight, endLeft and adjust generic fortress types palisades?
17// ?Think of something to enable splitting walls into two walls so more complex walls can be build and roads can have branches/crossroads?
18// ?Readjust placement angle for wall elements with bending when used in linear/circular walls by their bending?
19
20
21///////////////////////////////
22// WallElement class definition
23///////////////////////////////
24
25// argument type: Descriptive string, example: 'wall'. NOTE: Not really needed. Mainly for custom wall elements and to get the wall element type in code.
26// argument entity: Optional. Template string to be placed, example: 'structures/cart_wall'. Default is undefined (No entity placed)
27// argument angle: Optional. Placement angle so that 'outside' is 'right' (towards positive X like a unit placed with angle 0). Default is 0 (0*PI)
28// argument width: Optional. The width it lengthens the wall, width because it's the needed space in a right angle to 'outside'. Default is 0
29// argument indent: Optional. The indentation means its drawn inside (positive values) or pushed outwards (negative values). Default is 0
30// NOTE: Bending is only used for fortresses and custom walls. Linear/circular walls walls use no/generic bending
31// argument bending: Optional. How the direction of the wall is changed after this element, positive is bending 'in' (counter clockwise like entity placement)
32function WallElement(type, entity, angle, width, indent, bending)
33{
34 // NOTE: Not all wall elements have a symetry. So there's an direction 'outside' (towards right/positive X by default)
35 // In this sense 'left'/'right' means left/right of you when standing upon the wall and look 'outside'
36 // The wall is build towards 'left' so the next wall element will be placed left of the previous one (towards positive Y by default)
37 // In this sense the wall's direction is left meaning the 'bending' of a corner is used for the element following the corner
38 // With 'inside' and 'outside' defined as above, corners bend 'in'/'out' meaning placemant angle is increased/decreased (counter clockwise like object placement)
39 this.type = type;
40 // Wall element type documentation:
41 // Enlengthening straight blocking (mainly left/right symetric) wall elements (Walls and wall fortifications)
42 // 'wall': A blocking straight wall element that mainly lengthens the wall, self-explanatory
43 // 'wallShort': self-explanatory
44 // 'wallLong': self-explanatory
45 // 'tower': A blocking straight wall element with damage potential (but for palisades) that slightly lengthens the wall, exsample: wall tower, palisade tower(No attack)
46 // 'wallFort': A blocking straight wall element with massive damage potential that lengthens the wall, exsample: fortress, palisade fort
47 // Enlengthening straight non/custom blocking (mainly left/right symetric) wall elements (Gates and entrys)
48 // 'gate': A blocking straight wall element with passability determined by owner, example: gate (Functionality not yet implemented)
49 // 'entry': A non-blocking straight wall element (same width as gate) but without an actual template or just a flag/column/obelisk
50 // 'entryTower': A non-blocking straight wall element (same width as gate) represented by a single (maybe indented) template, example: defense tower, wall tower, outpost, watchtower
51 // 'entryFort': A non-blocking straight wall element represented by a single (maybe indented) template, example: fortress, palisade fort
52 // Bending wall elements (Wall corners)
53 // 'cornerIn': A wall element bending the wall by PI/2 'inside' (left, +, see above), example: wall tower, palisade curve
54 // 'cornerOut': A wall element bending the wall by PI/2 'outside' (right, -, see above), example: wall tower, palisade curve
55 // 'cornerHalfIn': A wall element bending the wall by PI/4 'inside' (left, +, see above), example: wall tower, palisade curve. NOTE: Not yet implemented
56 // 'cornerHalfOut': A wall element bending the wall by PI/4 'outside' (right, -, see above), example: wall tower, palisade curve. NOTE: Not yet implemented
57 // Zero length straight indented (mainly left/right symetric) wall elements (Outposts/watchtowers and non-defensive base structures)
58 // 'outpost': A zero-length wall element without bending far indented so it stands outside the wall, exsample: outpost, defense tower, watchtower
59 // 'house': A zero-length wall element without bending far indented so it stands inside the wall that grants population bonus, exsample: house, hut, longhouse
60 // 'barracks': A zero-length wall element without bending far indented so it stands inside the wall that grants unit production, exsample: barracks, tavern, ...
61 this.entity = entity;
62 this.angle = (angle !== undefined) ? angle : 0*PI;
63 this.width = (width !== undefined) ? width : 0;
64 this.indent = (indent !== undefined) ? indent : 0;
65 this.bending = (bending !== undefined) ? bending : 0*PI;
66}
67
68
69////////////////////////////
70// Fortress class definition
71////////////////////////////
72
73// A list would do for symetric fortresses but if 'getCenter' don't do sufficient the center can be set manually
74// argument type: Descriptive string, example: 'tiny'. Not really needed (WallTool.wallTypes['type string'] is used). Mainly for custom wall elements.
75// argument wall: Optional. Array of wall element strings. Can be set afterwards. Default is an epty array.
76 // Example: ['entrance', 'wall', 'cornerIn', 'wall', 'gate', 'wall', 'entrance', 'wall', 'cornerIn', 'wall', 'gate', 'wall', 'cornerIn', 'wall']
77// argument center: Optional. Array of 2 floats determinig the vector from the center to the 1st wall element. Can be set afterwards. Default is [0, 0]. (REALLY???)
78 // NOTE: The center will be recalculated when WallTool.setFortress is called. To avoid this set WallTool.calculateCenter to false.
79function Fortress(type, wall, center)
80{
81 this.type = type; // Only usefull to get the type of the actual fortress (by 'WallTool.fortress.type')
82 this.wall = (wall !== undefined) ? wall : [];
83 this.center = [0, 0]; // X/Z offset (in default orientation) from first wall element to center, perhaps should be the other way around...
84}
85
86
87///////////////////////////////////////////////
88// Setup data structure for default wall styles
89///////////////////////////////////////////////
90
91// A wall style is an associative array with all wall elements of that style in it associated with the wall element type string.
92// wallStyles holds all the wall styles within an associative array while a wall style is associated with the civ string or another descriptive strings like 'palisades', 'fence', 'cart', 'celt'...
93var wallStyles = {};
94
95// Generic civ dependent wall style definition. 'rome_siege' needs some tweek...
96var wallScaleByType = {'athen' : 1.5, 'cart' : 1.8, 'celt' : 1.5, 'hele' : 1.5, 'iber' : 1.5, 'mace' : 1.5, 'pers' : 1.5, 'rome' : 1.5, 'spart' : 1.5, 'rome_siege' : 1.5};
97for (var style in wallScaleByType)
98{
99 var civ = style;
100 if (style == 'rome_siege')
101 civ = 'rome';
102 wallStyles[style] = {};
103 // Default wall elements
104 wallStyles[style]['tower'] = new WallElement('tower', 'structures/' + style + '_wall_tower', PI, wallScaleByType[style]);
105 wallStyles[style]['endLeft'] = new WallElement('endLeft', 'structures/' + style + '_wall_tower', PI, wallScaleByType[style]); // Same as tower. To be compatible with palisades...
106 wallStyles[style]['endRight'] = new WallElement('endRight', 'structures/' + style + '_wall_tower', PI, wallScaleByType[style]); // Same as tower. To be compatible with palisades...
107 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
108 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
109 wallStyles[style]['wallShort'] = new WallElement('wallShort', 'structures/' + style + '_wall_short', 0*PI, 2*wallScaleByType[style]);
110 wallStyles[style]['wall'] = new WallElement('wall', 'structures/' + style + '_wall_medium', 0*PI, 4*wallScaleByType[style]);
111 wallStyles[style]['wallMedium'] = new WallElement('wall', 'structures/' + style + '_wall_medium', 0*PI, 4*wallScaleByType[style]);
112 wallStyles[style]['wallLong'] = new WallElement('wallLong', 'structures/' + style + '_wall_long', 0*PI, 6*wallScaleByType[style]);
113 // Gate and entrance wall elements
114 if (style == 'cart')
115 var gateWidth = 3.5*wallScaleByType[style];
116 else if (style == 'celt')
117 var gateWidth = 4*wallScaleByType[style];
118 // else if (style == 'iber')
119 // var gateWidth = 5.5*wallScaleByType[style];
120 else
121 var gateWidth = 6*wallScaleByType[style];
122 wallStyles[style]['gate'] = new WallElement('gate', 'structures/' + style + '_wall_gate', 0*PI, gateWidth);
123 wallStyles[style]['entry'] = new WallElement('entry', undefined, 0*PI, gateWidth);
124 wallStyles[style]['entryTower'] = new WallElement('entryTower', 'structures/' + civ + '_defense_tower', PI, gateWidth, -4*wallScaleByType[style]);
125 wallStyles[style]['entryFort'] = new WallElement('entryFort', 'structures/' + civ + '_fortress', 0*PI, 8*wallScaleByType[style], 6*wallScaleByType[style]);
126 // Defensive wall elements with 0 width outside the wall
127 wallStyles[style]['outpost'] = new WallElement('outpost', 'structures/' + civ + '_outpost', PI, 0, -4*wallScaleByType[style]);
128 wallStyles[style]['defenseTower'] = new WallElement('defenseTower', 'structures/' + civ + '_defenseTower', PI, 0, -4*wallScaleByType[style]);
129 // Base buildings wall elements with 0 width inside the wall
130 wallStyles[style]['barracks'] = new WallElement('barracks', 'structures/' + civ + '_barracks', PI, 0, 4.5*wallScaleByType[style]);
131 wallStyles[style]['civilCentre'] = new WallElement('civilCentre', 'structures/' + civ + '_civil_centre', PI, 0, 4.5*wallScaleByType[style]);
132 wallStyles[style]['farmstead'] = new WallElement('farmstead', 'structures/' + civ + '_farmstead', PI, 0, 4.5*wallScaleByType[style]);
133 wallStyles[style]['field'] = new WallElement('field', 'structures/' + civ + '_field', PI, 0, 4.5*wallScaleByType[style]);
134 wallStyles[style]['fortress'] = new WallElement('fortress', 'structures/' + civ + '_fortress', PI, 0, 4.5*wallScaleByType[style]);
135 wallStyles[style]['house'] = new WallElement('house', 'structures/' + civ + '_house', PI, 0, 4.5*wallScaleByType[style]);
136 wallStyles[style]['market'] = new WallElement('market', 'structures/' + civ + '_market', PI, 0, 4.5*wallScaleByType[style]);
137 wallStyles[style]['mill'] = new WallElement('mill', 'structures/' + civ + '_mill', PI, 0, 4.5*wallScaleByType[style]);
138 wallStyles[style]['temple'] = new WallElement('temple', 'structures/' + civ + '_temple', PI, 0, 4.5*wallScaleByType[style]);
139 // Generic space/gap wall elements
140 wallStyles[style]['space1'] = new WallElement('space1', undefined, 0*PI, wallScaleByType[style]);
141 wallStyles[style]['space2'] = new WallElement('space2', undefined, 0*PI, 2*wallScaleByType[style]);
142 wallStyles[style]['space3'] = new WallElement('space3', undefined, 0*PI, 3*wallScaleByType[style]);
143 wallStyles[style]['space4'] = new WallElement('space4', undefined, 0*PI, 4*wallScaleByType[style]);
144}
145// Add wall fortresses for all generic styles
146wallStyles['athen']['wallFort'] = new WallElement('wallFort', 'structures/athen_fortress', 2*PI/2 /* PI/2 */, 5.1 /* 5.6 */, 1.9 /* 1.9 */);
147wallStyles['cart']['wallFort'] = new WallElement('wallFort', 'structures/cart_fortress', PI, 5.1, 1.6);
148wallStyles['celt']['wallFort'] = new WallElement('wallFort', 'structures/celt_fortress_g', PI, 4.2, 1.5);
149wallStyles['hele']['wallFort'] = new WallElement('wallFort', 'structures/hele_fortress', 2*PI/2 /* PI/2 */, 5.1 /* 5.6 */, 1.9 /* 1.9 */);
150wallStyles['iber']['wallFort'] = new WallElement('wallFort', 'structures/iber_fortress', PI, 5, 0.2);
151wallStyles['mace']['wallFort'] = new WallElement('wallFort', 'structures/mace_fortress', 2*PI/2 /* PI/2 */, 5.1 /* 5.6 */, 1.9 /* 1.9 */);
152wallStyles['pers']['wallFort'] = new WallElement('wallFort', 'structures/pers_fortress', PI, 5.6/*5.5*/, 1.9/*1.7*/);
153wallStyles['rome']['wallFort'] = new WallElement('wallFort', 'structures/rome_fortress', PI, 6.3, 2.1);
154wallStyles['spart']['wallFort'] = new WallElement('wallFort', 'structures/spart_fortress', 2*PI/2 /* PI/2 */, 5.1 /* 5.6 */, 1.9 /* 1.9 */);
155// Adjust 'rome_siege' style
156wallStyles['rome_siege']['wallFort'] = new WallElement('wallFort', 'structures/rome_army_camp', PI, 7.2, 2);
157wallStyles['rome_siege']['entryFort'] = new WallElement('entryFort', 'structures/rome_army_camp', PI, 12, 7);
158wallStyles['rome_siege']['house'] = new WallElement('house', 'structures/rome_tent', PI, 0, 4);
159// Add pseudo "theb" wall style (No actual entities available)
160wallStyles["theb"] = deepcopy(wallStyles["hele"]);
161
162// Add special wall styles not well to implement generic (and to show how custom styles can be added)
163
164// Add special wall style 'palisades'
165wallScaleByType['palisades'] = 0.55;
166wallStyles['palisades'] = {};
167wallStyles['palisades']['wall'] = new WallElement('wall', 'other/palisades_rocks_medium', 0*PI, 2.3);
168wallStyles['palisades']['wallMedium'] = new WallElement('wall', 'other/palisades_rocks_medium', 0*PI, 2.3);
169wallStyles['palisades']['wallLong'] = new WallElement('wall', 'other/palisades_rocks_long', 0*PI, 3.5);
170wallStyles['palisades']['wallShort'] = new WallElement('wall', 'other/palisades_rocks_short', 0*PI, 1.2);
171wallStyles['palisades']['tower'] = new WallElement('tower', 'other/palisades_rocks_tower', -PI/2, 0.7);
172wallStyles['palisades']['wallFort'] = new WallElement('wallFort', 'other/palisades_rocks_fort', PI, 1.7);
173wallStyles['palisades']['gate'] = new WallElement('gate', 'other/palisades_rocks_gate', PI, 3.6);
174wallStyles['palisades']['entry'] = new WallElement('entry', undefined, wallStyles['palisades']['gate'].angle, wallStyles['palisades']['gate'].width);
175wallStyles['palisades']['entryTower'] = new WallElement('entryTower', 'other/palisades_rocks_watchtower', 0*PI, wallStyles['palisades']['gate'].width, -3);
176wallStyles['palisades']['entryFort'] = new WallElement('entryFort', 'other/palisades_rocks_fort', PI, 6, 3);
177wallStyles['palisades']['cornerIn'] = new WallElement('cornerIn', 'other/palisades_rocks_curve', 3*PI/4, 2.1, 0.7, PI/2);
178wallStyles['palisades']['cornerOut'] = new WallElement('cornerOut', 'other/palisades_rocks_curve', 5*PI/4, 2.1, -0.7, -PI/2);
179wallStyles['palisades']['outpost'] = new WallElement('outpost', 'other/palisades_rocks_outpost', PI, 0, -2);
180wallStyles['palisades']['house'] = new WallElement('house', 'other/celt_hut', PI, 0, 5);
181wallStyles['palisades']['barracks'] = new WallElement('barracks', 'other/celt_tavern', PI, 0, 5);
182wallStyles['palisades']['endRight'] = new WallElement('endRight', 'other/palisades_rocks_end', -PI/2, 0.2);
183wallStyles['palisades']['endLeft'] = new WallElement('endLeft', 'other/palisades_rocks_end', PI/2, 0.2);
184
185// Add special wall style 'road'
186// NOTE: This is not a wall style in the common sense. Use with care!
187wallStyles['road'] = {};
188wallStyles['road']['short'] = new WallElement('road', "actor|props/special/eyecandy/road_temperate_short.xml", PI/2, 4.5);
189wallStyles['road']['long'] = new WallElement('road', "actor|props/special/eyecandy/road_temperate_long.xml", PI/2, 9.5);
190wallStyles['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
191wallStyles['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
192wallStyles['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
193wallStyles['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
194wallStyles['road']['start'] = new WallElement('road', "actor|props/special/eyecandy/road_temperate_end.xml", PI/2, 2);
195wallStyles['road']['end'] = new WallElement('road', "actor|props/special/eyecandy/road_temperate_end.xml", -PI/2, 2);
196wallStyles['road']['xStraight'] = new WallElement('road', "actor|props/special/eyecandy/road_temperate_intersect_x.xml", 0*PI, 4.5);
197wallStyles['road']['xLeft'] = new WallElement('road', "actor|props/special/eyecandy/road_temperate_intersect_x.xml", 0*PI, 4.5, 0, PI/2);
198wallStyles['road']['xRight'] = new WallElement('road', "actor|props/special/eyecandy/road_temperate_intersect_x.xml", 0*PI, 4.5, 0, -PI/2);
199wallStyles['road']['tLeft'] = new WallElement('road', "actor|props/special/eyecandy/road_temperate_intersect_T.xml", PI, 4.5, 1.25);
200wallStyles['road']['tRight'] = new WallElement('road', "actor|props/special/eyecandy/road_temperate_intersect_T.xml", 0*PI, 4.5, -1.25);
201
202// Add special wall element collection 'other'
203// NOTE: This is not a wall style in the common sense. Use with care!
204wallStyles['other'] = {};
205wallStyles['other']['fence'] = new WallElement('fence', 'other/fence_long', -PI/2, 3.1);
206wallStyles['other']['fence_medium'] = new WallElement('fence', 'other/fence_long', -PI/2, 3.1);
207wallStyles['other']['fence_short'] = new WallElement('fence_short', 'other/fence_short', -PI/2, 1.5);
208wallStyles['other']['fence_stone'] = new WallElement('fence_stone', 'other/fence_stone', -PI/2, 2.5);
209wallStyles['other']['palisade'] = new WallElement('palisade', 'other/palisades_rocks_short', 0, 1.2);
210wallStyles['other']['column'] = new WallElement('column', 'other/column_doric', 0, 1);
211wallStyles['other']['obelisk'] = new WallElement('obelisk', 'other/obelisk', 0, 2);
212wallStyles['other']['spike'] = new WallElement('spike', 'other/palisades_angle_spike', -PI/2, 1);
213wallStyles['other']['bench'] = new WallElement('bench', 'other/bench', PI/2, 1.5);
214wallStyles['other']['benchForTable'] = new WallElement('benchForTable', 'other/bench', 0, 0.5);
215wallStyles['other']['table'] = new WallElement('table', 'other/table_rectangle', 0, 1);
216wallStyles['other']['table_square'] = new WallElement('table_square', 'other/table_square', PI/2, 1);
217wallStyles['other']['flag'] = new WallElement('flag', 'special/rallypoint', PI, 1);
218wallStyles['other']['standing_stone'] = new WallElement('standing_stone', 'gaia/special_ruins_standing_stone', PI, 1);
219wallStyles['other']['settlement'] = new WallElement('settlement', 'gaia/special_settlement', PI, 6);
220wallStyles['other']['gap'] = new WallElement('gap', undefined, 0, 2);
221wallStyles['other']['gapSmall'] = new WallElement('gapSmall', undefined, 0, 1);
222wallStyles['other']['gapLarge'] = new WallElement('gapLarge', undefined, 0, 4);
223wallStyles['other']['cornerIn'] = new WallElement('cornerIn', undefined, 0, 0, 0, PI/2);
224wallStyles['other']['cornerOut'] = new WallElement('cornerOut', undefined, 0, 0, 0, -PI/2);
225
226
227///////////////////////////////////////////////////////
228// Setup data structure for some default fortress types
229///////////////////////////////////////////////////////
230
231// A fortress type is just an instance of the fortress class with actually something in it.
232// fortressTypes holds all the fortressess within an associative array while a fortress is associated with a descriptive string maching the map sizes, example: 'tiny', 'giant'
233var fortressTypes = {};
234// Setup some default fortress types
235// Add fortress type 'tiny'
236fortressTypes['tiny'] = new Fortress('tiny');
237var wallPart = ['entry', 'wall', 'cornerIn', 'wall'];
238fortressTypes['tiny'].wall = wallPart.concat(wallPart, wallPart, wallPart);
239// Add fortress type 'small'
240fortressTypes['small'] = new Fortress('small');
241var wallPart = ['entry', 'endLeft', 'wall', 'cornerIn', 'wall', 'endRight'];
242fortressTypes['small'].wall = wallPart.concat(wallPart, wallPart, wallPart);
243// Add fortress type 'medium'
244fortressTypes['medium'] = new Fortress('medium');
245var wallPart = ['entry', 'endLeft', 'wall', 'outpost', 'wall',
246 'cornerIn', 'wall', 'outpost', 'wall', 'endRight'];
247fortressTypes['medium'].wall = wallPart.concat(wallPart, wallPart, wallPart);
248// Add fortress type 'normal'
249fortressTypes['normal'] = new Fortress('normal');
250var wallPart = ['entry', 'endLeft', 'wall', 'tower', 'wall',
251 'cornerIn', 'wall', 'tower', 'wall', 'endRight'];
252fortressTypes['normal'].wall = wallPart.concat(wallPart, wallPart, wallPart);
253// Add fortress type 'large'
254fortressTypes['large'] = new Fortress('large');
255var wallPart = ['entry', 'endLeft', 'wall', 'outpost', 'wall', 'cornerIn', 'wall',
256 'cornerOut', 'wall', 'cornerIn', 'wall', 'outpost', 'wall', 'endRight'];
257fortressTypes['large'].wall = wallPart.concat(wallPart, wallPart, wallPart);
258// Add fortress type 'veryLarge'
259fortressTypes['veryLarge'] = new Fortress('veryLarge');
260var wallPart = ['entry', 'endLeft', 'wall', 'tower', 'wall', 'cornerIn', 'wall',
261 'cornerOut', 'wall', 'cornerIn', 'wall', 'tower', 'wall', 'endRight'];
262fortressTypes['veryLarge'].wall = wallPart.concat(wallPart, wallPart, wallPart);
263// Add fortress type 'giant'
264fortressTypes['giant'] = new Fortress('giant');
265var wallPart = ['entry', 'endLeft', 'wall', 'outpost', 'wall', 'cornerIn', 'wall', 'outpost', 'wall',
266 'cornerOut', 'wall', 'outpost', 'wall', 'cornerIn', 'wall', 'outpost', 'wall', 'endRight'];
267fortressTypes['giant'].wall = wallPart.concat(wallPart, wallPart, wallPart);
268
269// Add some "fortress types" for roads (will only work with style "road")
270// ["start", "short", "xRight", "xLeft", "cornerLeft", "xStraight", "long", "xLeft", "xRight", "cornerRight", "tRight", "tLeft", "xRight", "xLeft", "curveLeft", "xStraight", "curveRight", "end"];
271var wall = ["short", "curveLeft", "short", "curveLeft", "short", "curveLeft", "short", "curveLeft"];
272fortressTypes['road01'] = new Fortress('road01', wall);
273var wall = ["short", "cornerLeft", "short", "cornerLeft", "short", "cornerLeft", "short", "cornerLeft"];
274fortressTypes['road02'] = new Fortress('road02', wall);
275var wall = ["xStraight", "curveLeft", "xStraight", "curveLeft", "xStraight", "curveLeft", "xStraight", "curveLeft"];
276fortressTypes['road03'] = new Fortress('road03', wall);
277var wall = ["start", "curveLeft", "tRight", "cornerLeft", "tRight", "curveRight", "short", "xRight", "curveLeft", "xRight", "short", "cornerLeft", "tRight", "short",
278 "curveLeft", "short", "tRight", "cornerLeft", "short", "xRight", "curveLeft", "xRight", "short", "curveRight", "tRight", "cornerLeft", "tRight", "curveLeft", "end"];
279fortressTypes['road04'] = new Fortress('road04', wall);
280var wall = ["start", "tLeft", "short", "xRight",
281 "curveLeft", "xRight", "tRight", "cornerLeft", "tRight",
282 "curveLeft", "short", "tRight", "cornerLeft", "xRight",
283 "cornerLeft", "xRight", "short", "tRight", "curveLeft", "end"];
284fortressTypes['road05'] = new Fortress('road05', wall);
285
286// Add (some) iberian civ bonus fortress (The civ can still be set, just an uncommon default fortress)
287var wall = ['gate', 'tower', 'wall', 'cornerIn', 'wallLong', 'tower',
288 'gate', 'tower', 'wallLong', 'cornerIn', 'wall', 'tower',
289 'gate', 'wall', 'cornerIn', 'wall', 'cornerOut', 'wall', 'cornerIn', 'wallLong', 'tower',
290 'gate', 'tower', 'wallLong', 'cornerIn', 'wall', 'tower', 'wall', 'cornerIn', 'wall'];
291fortressTypes['iberCivBonus'] = new Fortress('iberCivBonus', wall);
292var wall = ['gate', 'tower', 'wall', 'cornerIn', 'wall', 'cornerOut', 'wall', 'cornerIn', 'wallLong', 'tower',
293 'gate', 'tower', 'wallLong', 'cornerIn', 'wall', 'tower', 'wall', 'cornerIn', 'wall', 'cornerOut',
294 'gate', 'tower', 'wall', 'cornerIn', 'wallLong', 'tower',
295 'gate', 'tower', 'wallLong', 'cornerIn', 'wall', 'tower'];
296fortressTypes['iberCivBonus2'] = new Fortress('iberCivBonus2', wall);
297var wall = ['gate', 'tower', 'wall', 'cornerOut', 'wallShort', 'cornerIn', 'wall', 'cornerIn', 'wallLong', 'cornerIn', 'wallShort', 'cornerOut', 'wall', 'tower',
298 'gate', 'tower', 'wallLong', 'cornerIn', 'wallLong', 'cornerIn', 'wallShort', 'cornerOut',
299 'gate', 'tower', 'wall', 'cornerIn', 'wall', 'cornerOut', 'wallShort', 'cornerIn', 'wall', 'tower',
300 'gate', 'tower', 'wallShort', 'cornerIn', 'wall', 'tower', 'wallShort', 'tower'];
301fortressTypes['iberCivBonus3'] = new Fortress('iberCivBonus3', wall);
302
303
304// Setup some semi default fortresses for 'palisades' style
305var fortressTypeKeys = ['tiny', 'small', 'medium', 'normal', 'large', 'veryLarge', 'giant'];
306for (var i = 0; i < fortressTypeKeys.length; i++)
307{
308 var newKey = fortressTypeKeys[i] + 'Palisades';
309 var oldWall = fortressTypes[fortressTypeKeys[i]].wall;
310 fortressTypes[newKey] = new Fortress(newKey, []); // [] just to make sure it's an array though it's an array by default
311 var fillTowersBetween = ['wall', 'endLeft', 'endRight', 'cornerIn', 'cornerOut'];
312 for (var j = 0; j < oldWall.length; j++)
313 {
314 fortressTypes[newKey].wall.push(oldWall[j]); // Only works if the first element is an entry or gate (not in fillTowersBetween)
315 if (j+1 < oldWall.length)
316 if (fillTowersBetween.indexOf(oldWall[j]) > -1 && fillTowersBetween.indexOf(oldWall[j+1]) > -1) // ... > -1 means 'exists' here
317 fortressTypes[newKey].wall.push('tower');
318 }
319}
320
321
322///////////////////////////////////////////////////////////////////////////////
323// Define some helper functions: getWallAlignment, getWallCenter, getWallLength
324///////////////////////////////////////////////////////////////////////////////
325
326// Get alignment of a wall
327// Returns a list of lists of most arguments needed to place the different wall elements for a given wall
328// Placing the first wall element at startX/startY placed with angle given by orientation
329// An alignement can be used to get the center of a 'wall' (more likely used for closed walls like fortresses) with the getWallCenter function
330function getWallAlignment(startX, startY, wall, style, orientation)
331{
332 orientation = (orientation || 0*PI);
333 var alignment = [];
334 var wallX = startX;
335 var wallY = startY;
336 for (var i = 0; i < wall.length; i++)
337 {
338 if (wallStyles[style][wall[i]] === undefined)
339 warn('No valid wall element: ' + wall[i]);
340 var element = wallStyles[style][wall[i]]
341 // Indentation
342 var placeX = wallX - element.indent * cos(orientation);
343 var placeY = wallY - element.indent * sin(orientation);
344 // Add element alignment
345 alignment.push([placeX, placeY, element.entity, orientation + element.angle]);
346 // Preset vars for the next wall element
347 if (i+1 < wall.length)
348 {
349 orientation += element.bending;
350 if (wallStyles[style][wall[i+1]] === undefined)
351 warn('No valid wall element: ' + wall[i+1]);
352 var nextElement = wallStyles[style][wall[i+1]];
353 var distance = (element.width + nextElement.width)/2;
354 // Corrections for elements with indent AND bending
355 if (element.bending !== 0 && element.indent !== 0)
356 {
357 // Indent correction to adjust distance
358 distance += element.indent*sin(element.bending);
359 // Indent correction to normalize indentation
360 wallX += element.indent * cos(orientation);
361 wallY += element.indent * sin(orientation);
362 }
363 // Set the next coordinates of the next element in the wall without indentation adjustment
364 wallX -= distance * sin(orientation);
365 wallY += distance * cos(orientation);
366 }
367 }
368 return alignment;
369}
370
371// Get the center of a wall (mainly usefull for closed walls like fortresses)
372// Center calculation works like getting the center of mass assuming all wall elements have the same 'waight'
373// It returns the vector (array [x, y]) from the first wall element to the center
374function getWallCenter(alignment)
375{
376 var x = 0;
377 var y = 0;
378 for (var i = 0; i < alignment.length; i++)
379 {
380 x += alignment[i][0]/alignment.length;
381 y += alignment[i][1]/alignment.length;
382 }
383 var center = [x, y];
384 return center;
385}
386
387// Get the length of a wall used by placeIrregularPolygonalWall
388// NOTE: Does not support bending wall elements like corners!
389function getWallLength(wall, style)
390{
391 var length = 0;
392 for (var i = 0; i < wall.length; i++)
393 {
394 length += wallStyles[style][wall[i]].width;
395 }
396 return length;
397}
398
399
400/////////////////////////////////////////////
401// Define the different wall placer functions
402/////////////////////////////////////////////
403
404/////////////////////////////////////////////////////////////////////////////////
405// Place simple wall starting with the first wall element placed at startX/startY
406/////////////////////////////////////////////////////////////////////////////////
407// orientation: 0 means 'outside' or 'front' of the wall is right (positive X) like placeObject
408// It will then be build towards top (positive Y) if no bending wall elements like corners are used
409// Raising orientation means the wall is rotated counter-clockwise like placeObject
410function placeWall(startX, startY, wall, style, playerId, orientation)
411{
412 var AM = getWallAlignment(startX, startY, wall, style, orientation);
413 for (var iWall = 0; iWall < wall.length; iWall++)
414 {
415 if (AM[iWall][2] !== undefined)
416 placeObject(AM[iWall][0], AM[iWall][1], AM[iWall][2], playerId, AM[iWall][3]);
417 }
418}
419
420//////////////////////////////////////////////////////////////////////////////////////////////////
421// Place a fortress (mainly a closed wall build like placeWall) with the center at centerX/centerY
422//////////////////////////////////////////////////////////////////////////////////////////////////
423// Should always start with the main entrance (like 'entry' or 'gate') to get the orientation right (like placeObject)
424function placeCustomFortress(centerX, centerY, fortress, style, playerId, orientation, scipGetCenter)
425{
426 if (!scipGetCenter)
427 {
428 var alignment = getWallAlignment(0, 0, fortress.wall, style);
429 var center = getWallCenter(alignment);
430 var startX = centerX - center[0] * cos(orientation) + center[1] * sin(orientation);
431 var startY = centerY - center[1] * cos(orientation) - center[0] * sin(orientation);
432 }
433 else
434 {
435 var startX = centerX + fortress.center[0];
436 var startY = centerY + fortress.center[1];
437 }
438 placeWall(startX, startY, fortress.wall, style, playerId, orientation)
439}
440
441function placeFortress(centerX, centerY, type, style, playerId, orientation, scipGetCenter)
442{
443 placeCustomFortress(centerX, centerY, fortressTypes[type], style, playerId, orientation, scipGetCenter);
444}
445
446//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
447// Place a linear wall of repeatant wall elements given in the argument wallPart from startX/startY to targetX/targetY
448//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
449// startX: x coordinate of the beginning of the wall
450// startY: y coordinate of the beginning of the wall
451// targetX: x coordinate of the ending of the wall
452// targetY: y coordinate of the ending of the wall
453// NOTE: Start and target coordinates are exact (besides the scale offset) meaning the wall will begin/end at the given coordinates (not the first/last entity is placed there)
454// wallPart: Optional. An array of wall element type strings (see WallElement.type). Default is ['wall']
455// NOTE: Don't use wall elements with bending like corners!
456// style: Optional. An wall style string (like defined in the 'wallStyles' dictionary). Default is 'palisades'
457// playerId: Optional. The walls owners player ID (like in the function 'placeObject' so 0 is gaia, 1 is the first player), Default is 0 (gaia)
458// endWithFirst: Optional. A boolean value. If true the 1st wall element in the wallPart array will finalize the wall. Default is true
459// TODO: Maybe add angle offset for more generic looking?
460function placeLinearWall(startX, startY, targetX, targetY, wallPart, style, playerId, endWithFirst)
461{
462 // Setup optional arguments to the default
463 wallPart = (wallPart || ['wall']);
464 style = (style || 'palisades');
465 playerId = (playerId || 0);
466 // endWithFirst = (endWithFirst || true);
467 endWithFirst = typeof endWithFirst == 'undefined' ? true : endWithFirst;
468 // Check arguments
469 for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++)
470 if (wallStyles[style][wallPart[elementIndex]].bending != 0)
471 warn("Bending is not supported by placeLinearWall but a bending wall element is used: " + wallPart[elementIndex] + " -> wallStyles[style][wallPart[elementIndex]].entity");
472 // Setup number of wall parts
473 var totalLength = getDistance(startX, startY, targetX, targetY);
474 var wallPartLength = 0;
475 for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++)
476 wallPartLength += wallStyles[style][wallPart[elementIndex]].width;
477 var numParts = 0;
478 if (endWithFirst == true)
479 numParts = ceil((totalLength - wallStyles[style][wallPart[0]].width) / wallPartLength)
480 else
481 numParts = ceil(totalLength / wallPartLength);
482 // Setup scale factor
483 var scaleFactor = 1;
484 if (endWithFirst == true)
485 scaleFactor = totalLength / (numParts * wallPartLength + wallStyles[style][wallPart[0]].width)
486 else
487 scaleFactor = totalLength / (numParts * wallPartLength);
488 // Setup angle
489 var wallAngle = getAngle(startX, startY, targetX, targetY); // NOTE: function 'getAngle()' is about to be changed...
490 var placeAngle = wallAngle - PI/2;
491 // Place wall entities
492 var x = startX;
493 var y = startY;
494 for (var partIndex = 0; partIndex < numParts; partIndex++)
495 {
496 for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++)
497 {
498 var wallEle = wallStyles[style][wallPart[elementIndex]];
499 // Width correction
500 x += scaleFactor * wallEle.width/2 * cos(wallAngle);
501 y += scaleFactor * wallEle.width/2 * sin(wallAngle);
502 // Indent correction
503 var placeX = x - wallEle.indent * sin(wallAngle);
504 var placeY = y + wallEle.indent * cos(wallAngle);
505 // Placement
506 if (wallEle.entity !== undefined)
507 placeObject(placeX, placeY, wallEle.entity, playerId, placeAngle + wallEle.angle);
508 x += scaleFactor * wallEle.width/2 * cos(wallAngle);
509 y += scaleFactor * wallEle.width/2 * sin(wallAngle);
510 }
511 }
512 if (endWithFirst == true)
513 {
514 var wallEle = wallStyles[style][wallPart[0]];
515 x += scaleFactor * wallEle.width/2 * cos(wallAngle);
516 y += scaleFactor * wallEle.width/2 * sin(wallAngle);
517 if (wallEle.entity !== undefined)
518 placeObject(x, y, wallEle.entity, playerId, placeAngle + wallEle.angle);
519 }
520}
521
522////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
523// Place a circular wall of repeatant wall elements given in the argument wallPart arround centerX/centerY with the given radius
524////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
525// The wall is not necessarily closed depending on the optional argument maxAngle (better name?)
526// NOTE: Don't use wall elements with bending like corners!
527// TODO: Add eccentricity and maxBendOff functionality (untill now an unused argument)
528function placeCircularWall(centerX, centerY, radius, wallPart, style, playerId, orientation, maxAngle, endWithFirst, maxBendOff)
529{
530 // Setup optional arguments to the default
531 wallPart = (wallPart || ['wall']);
532 style = (style || 'palisades');
533 playerId = (playerId || 0);
534 orientation = (orientation || 0);
535 maxAngle = (maxAngle || 2*PI);
536 if (endWithFirst === undefined)
537 {
538 if (maxAngle >= 2*PI - 0.001) // Can this be done better?
539 {
540 endWithFirst = false;
541 }
542 else
543 {
544 endWithFirst = true;
545 }
546 }
547 maxBendOff = (maxBendOff || 0);
548 // Check arguments
549 if (maxBendOff > PI/2 || maxBendOff < 0)
550 warn('placeCircularWall maxBendOff sould satisfy 0 < maxBendOff < PI/2 (~1.5) but it is: ' + maxBendOff);
551 for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++)
552 if (wallStyles[style][wallPart[elementIndex]].bending != 0)
553 warn("Bending is not supported by placeCircularWall but a bending wall element is used: " + wallPart[elementIndex]);
554 // Setup number of wall parts
555 var totalLength = maxAngle * radius;
556 var wallPartLength = 0;
557 for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++)
558 wallPartLength += wallStyles[style][wallPart[elementIndex]].width;
559 var numParts = 0;
560 if (endWithFirst == true)
561 {
562 numParts = ceil((totalLength - wallStyles[style][wallPart[0]].width) / wallPartLength);
563 }
564 else
565 {
566 numParts = ceil(totalLength / wallPartLength);
567 }
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 // Place wall entities
575 var actualAngle = orientation + (2*PI - maxAngle) / 2;
576 var x = centerX + radius*cos(actualAngle);
577 var y = centerY + radius*sin(actualAngle);
578 for (var partIndex = 0; partIndex < numParts; partIndex++)
579 {
580 for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++)
581 {
582 var wallEle = wallStyles[style][wallPart[elementIndex]];
583 // Width correction
584 var addAngle = scaleFactor * wallEle.width / radius;
585 var targetX = centerX + radius * cos(actualAngle + addAngle);
586 var targetY = centerY + radius * sin(actualAngle + addAngle);
587 var placeX = x + (targetX - x)/2;
588 var placeY = y + (targetY - y)/2;
589 var placeAngle = actualAngle + addAngle/2;
590 // Indent correction
591 placeX -= wallEle.indent * cos(placeAngle);
592 placeY -= wallEle.indent * sin(placeAngle);
593 // Placement
594 if (wallEle.entity !== undefined)
595 placeObject(placeX, placeY, wallEle.entity, playerId, placeAngle + wallEle.angle);
596 // Prepare for the next wall element
597 actualAngle += addAngle;
598 x = centerX + radius*cos(actualAngle);
599 y = centerY + radius*sin(actualAngle);
600 }
601 }
602 if (endWithFirst == true)
603 {
604 var wallEle = wallStyles[style][wallPart[0]];
605 var addAngle = scaleFactor * wallEle.width / radius;
606 var targetX = centerX + radius * cos(actualAngle + addAngle);
607 var targetY = centerY + radius * sin(actualAngle + addAngle);
608 var placeX = x + (targetX - x)/2;
609 var placeY = y + (targetY - y)/2;
610 var placeAngle = actualAngle + addAngle/2;
611 placeObject(placeX, placeY, wallEle.entity, playerId, placeAngle + wallEle.angle);
612 }
613}
614
615/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
616// Place a polygonal wall of repeatant wall elements given in the argument wallPart arround centerX/centerY with the given radius
617/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
618// NOTE: Don't use wall elements with bending like corners!
619// TODO: Check some arguments
620// TODO: Add eccentricity and perhaps make it call placeIrregularPolygonalWall with irregularity = 0
621function placePolygonalWall(centerX, centerY, radius, wallPart, cornerWallElement, style, playerId, orientation, numCorners, skipFirstWall)
622{
623 // Setup optional arguments to the default
624 wallPart = (wallPart || ['wall']);
625 cornerWallElement = (cornerWallElement || 'tower'); // Don't use wide elements for this. Not supported well...
626 style = (style || 'palisades');
627 playerId = (playerId || 0);
628 orientation = (orientation || 0);
629 numCorners = (numCorners || 8);
630 skipFirstWall = (skipFirstWall || false);
631 // Setup angles
632 var angleAdd = 2*PI/numCorners;
633 var angleStart = orientation - angleAdd/2;
634 // Setup corners
635 var corners = [];
636 for (var i = 0; i < numCorners; i++)
637 corners.push([centerX + radius*cos(angleStart + i*angleAdd), centerY + radius*sin(angleStart + i*angleAdd)]);
638 // Place Corners and walls
639 for (var i = 0; i < numCorners; i++)
640 {
641 var angleToCorner = getAngle(corners[i][0], corners[i][1], centerX, centerY);
642 placeObject(corners[i][0], corners[i][1], wallStyles[style][cornerWallElement].entity, playerId, angleToCorner);
643 if (!(skipFirstWall && i == 0))
644 {
645 placeLinearWall(
646 // Adjustment to the corner element width (approximately)
647 corners[i][0] + wallStyles[style][cornerWallElement].width/2 * sin(angleToCorner + angleAdd/2), // startX
648 corners[i][1] - wallStyles[style][cornerWallElement].width/2 * cos(angleToCorner + angleAdd/2), // startY
649 corners[(i+1)%numCorners][0] - wallStyles[style][cornerWallElement].width/2 * sin(angleToCorner + angleAdd/2), // targetX
650 corners[(i+1)%numCorners][1] + wallStyles[style][cornerWallElement].width/2 * cos(angleToCorner + angleAdd/2), // targetY
651 wallPart, style, playerId);
652 }
653 }
654}
655
656////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
657// Place an irregular polygonal wall of some wall parts to choose from arround centerX/centerY with the given radius
658////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
659// NOTE: wallPartList is put to the end because it's hardest to set
660// NOTE: Don't use wall elements with bending like corners!
661// TODO: Check some arguments
662// TODO: Add eccentricity
663function placeIrregularPolygonalWall(centerX, centerY, radius, cornerWallElement, style, playerId, orientation, numCorners, irregularity, skipFirstWall, wallPartsAssortment)
664{
665 // Generating a generic wall part assortment with each wall part including 1 gate enlengthend by walls and towers
666 // NOTE: It might be a good idea to write an own function for that...
667 var defaultWallPartsAssortment = [['wallShort'], ['wall'], ['wallLong'], ['gate', 'tower', 'wallShort']];
668 var centeredWallPart = ['gate']; // NOTE: Since gates are not functional yet entrys are used instead...
669 var extandingWallPartAssortment = [['tower', 'wallLong'], ['tower', 'wall']];
670 defaultWallPartsAssortment.push(centeredWallPart)
671 for (var i = 0; i < extandingWallPartAssortment.length; i++)
672 {
673 var wallPart = centeredWallPart;
674 for (var j = 0; j < radius; j++)
675 {
676 if (j%2 == 0)
677 wallPart = wallPart.concat(extandingWallPartAssortment[i]);
678 else
679 {
680 extandingWallPartAssortment[i].reverse();
681 wallPart = extandingWallPartAssortment[i].concat(wallPart);
682 extandingWallPartAssortment[i].reverse();
683 }
684 defaultWallPartsAssortment.push(wallPart);
685 }
686 }
687 // Setup optional arguments to the default
688 wallPartsAssortment = (wallPartsAssortment || defaultWallPartsAssortment);
689 cornerWallElement = (cornerWallElement || 'tower'); // Don't use wide elements for this. Not supported well...
690 style = (style || 'palisades');
691 playerId = (playerId || 0);
692 orientation = (orientation || 0);
693 numCorners = (numCorners || randInt(5, 7));
694 irregularity = (irregularity || 0.5);
695 skipFirstWall = (skipFirstWall || false);
696 // Setup angles
697 var angleToCover = 2*PI;
698 var angleAddList = [];
699 for (var i = 0; i < numCorners; i++)
700 {
701 // Randomize covered angles. Variety scales down with raising angle though...
702 angleAddList.push(angleToCover/(numCorners-i) * (1 + randFloat(-irregularity, irregularity)));
703 angleToCover -= angleAddList[angleAddList.length - 1];
704 }
705 // Setup corners
706 var corners = [];
707 var angleActual = orientation - angleAddList[0]/2;
708 for (var i = 0; i < numCorners; i++)
709 {
710 corners.push([centerX + radius*cos(angleActual), centerY + radius*sin(angleActual)]);
711 if (i < numCorners - 1)
712 angleActual += angleAddList[i+1]
713 }
714 // Setup best wall parts for the different walls (a bit confusing naming...)
715 var wallPartLengths = [];
716 var maxWallPartLength = 0;
717 for (var partIndex = 0; partIndex < wallPartsAssortment.length; partIndex++)
718 {
719 wallPartLengths.push(getWallLength(wallPartsAssortment[partIndex], style));
720 if (wallPartLengths[partIndex] > maxWallPartLength)
721 maxWallPartLength = wallPartLengths[partIndex];
722 }
723 var wallPartList = []; // This is the list of the wall parts to use for the walls between the corners, not to confuse with wallPartsAssortment!
724 for (var i = 0; i < numCorners; i++)
725 {
726 var bestWallPart = []; // This is a simpel wall part not a wallPartsAssortment!
727 var bestWallLength = 99999999;
728 // NOTE: This is not exsactly like the length the wall will be in the end. Has to be tweeked...
729 var wallLength = getDistance(corners[i][0], corners[i][1], corners[(i+1)%numCorners][0], corners[(i+1)%numCorners][1])
730 var numWallParts = ceil(wallLength/maxWallPartLength);
731 for (var partIndex = 0; partIndex < wallPartsAssortment.length; partIndex++)
732 {
733 if (numWallParts*wallPartLengths[partIndex] < bestWallLength && numWallParts*wallPartLengths[partIndex] > wallLength)
734 {
735 bestWallPart = wallPartsAssortment[partIndex];
736 bestWallLength = numWallParts*wallPartLengths[partIndex];
737 }
738 }
739 wallPartList.push(bestWallPart)
740 }
741 // Place Corners and walls
742 for (var i = 0; i < numCorners; i++)
743 {
744 var angleToCorner = getAngle(corners[i][0], corners[i][1], centerX, centerY);
745 placeObject(corners[i][0], corners[i][1], wallStyles[style][cornerWallElement].entity, playerId, angleToCorner);
746 if (!(skipFirstWall && i == 0))
747 {
748 placeLinearWall(
749 // Adjustment to the corner element width (approximately)
750 corners[i][0] + wallStyles[style][cornerWallElement].width/2 * sin(angleToCorner + angleAddList[i]/2), // startX
751 corners[i][1] - wallStyles[style][cornerWallElement].width/2 * cos(angleToCorner + angleAddList[i]/2), // startY
752 corners[(i+1)%numCorners][0] - wallStyles[style][cornerWallElement].width/2 * sin(angleToCorner + angleAddList[(i+1)%numCorners]/2), // targetX
753 corners[(i+1)%numCorners][1] + wallStyles[style][cornerWallElement].width/2 * cos(angleToCorner + angleAddList[(i+1)%numCorners]/2), // targetY
754 wallPartList[i], style, playerId, false);
755 }
756 }
757}
758
759////////////////////////////////////////////////////////////////////////////////////////////////////////
760//
761// Place generic fortress with towers at the edges connected with long walls and gates
762//
763// This is the default Iberian civ bonus starting wall
764//
765// style Which wall style should be used, default is the given players civ, palisades for gaia
766// irregularity How irregular the wall will be, 0 -> circle, 1 -> very spiky, default is 0.5
767// gateOccurence Integer number, every n walls will be gates instead, default is 3
768// maxTrys How often the function tries to find a better fitting shape at max, default is 100
769//
770////////////////////////////////////////////////////////////////////////////////////////////////////////
771function placeGenericFortress(centerX, centerY, radius, playerId, style, irregularity, gateOccurence, maxTrys)
772{
773 // Setup optional arguments
774 radius = (radius || 20);
775 playerId = (playerId || 0);
776 if (playerId == 0)
777 style = (style || "palisades");
778 else
779 style = (style || g_MapSettings.PlayerData[playerId-1].Civ);
780 irregularity = (irregularity || 0.5);
781 gateOccurence = (gateOccurence || 3);
782 maxTrys = (maxTrys || 100);
783 // Setup some vars
784 var startAngle = randFloat(0, 2*PI);
785 var actualOffX = radius*cos(startAngle);
786 var actualOffY = radius*sin(startAngle);
787 var actualAngle = startAngle;
788 var pointDistance = wallStyles[style]["wallLong"].width + wallStyles[style]["tower"].width;
789 // Searching for a well fitting point derivation
790 var tries = 0;
791 var bestPointDerivation = undefined;
792 var minOverlap = 1000;
793 var overlap = undefined;
794 while (tries < maxTrys && minOverlap > wallStyles[style]["tower"].width / 10)
795 {
796 var pointDerivation = [];
797 var distanceToTarget = 1000;
798 var targetReached = false;
799 while (!targetReached)
800 {
801 var indent = randFloat(-irregularity*pointDistance, irregularity*pointDistance);
802 var tmpAngle = getAngle(actualOffX, actualOffY,
803 (radius + indent)*cos(actualAngle + (pointDistance / radius)),
804 (radius + indent)*sin(actualAngle + (pointDistance / radius)));
805 actualOffX += pointDistance*cos(tmpAngle);
806 actualOffY += pointDistance*sin(tmpAngle);
807 actualAngle = getAngle(0, 0, actualOffX, actualOffY);
808 pointDerivation.push([actualOffX, actualOffY]);
809 distanceToTarget = getDistance(actualOffX, actualOffY, pointDerivation[0][0], pointDerivation[0][1]);
810 if ( pointDerivation.length > 3 && distanceToTarget < pointDistance) // Could be done better...
811 {
812 targetReached = true;
813 overlap = pointDistance - getDistance(pointDerivation[pointDerivation.length - 1][0], pointDerivation[pointDerivation.length - 1][1], pointDerivation[0][0], pointDerivation[0][1]);
814 if (overlap < minOverlap)
815 {
816 minOverlap = overlap;
817 bestPointDerivation = pointDerivation;
818 }
819 }
820 }
821 tries++;
822 }
823 log("placeGenericFortress: Reduced overlap to " + minOverlap + " after " + tries + " tries");
824 // Place wall
825 for (var pointIndex = 0; pointIndex < bestPointDerivation.length; pointIndex++)
826 {
827 var startX = centerX + bestPointDerivation[pointIndex][0];
828 var startY = centerY + bestPointDerivation[pointIndex][1];
829 var targetX = centerX + bestPointDerivation[(pointIndex + 1) % bestPointDerivation.length][0];
830 var targetY = centerY + bestPointDerivation[(pointIndex + 1) % bestPointDerivation.length][1];
831 var angle = getAngle(startX, startY, targetX, targetY);
832 var wallElement = "wallLong";
833 if ((pointIndex + 1) % gateOccurence == 0)
834 wallElement = "entry";
835 if (wallStyles[style][wallElement].entity)
836 {
837 placeObject(startX + (getDistance(startX, startY, targetX, targetY)/2)*cos(angle), // placeX
838 startY + (getDistance(startX, startY, targetX, targetY)/2)*sin(angle), // placeY
839 wallStyles[style][wallElement].entity, playerId, angle - PI/2 + wallStyles[style][wallElement].angle);
840 }
841 // Place tower
842 var startX = centerX + bestPointDerivation[(pointIndex + bestPointDerivation.length - 1) % bestPointDerivation.length][0];
843 var startY = centerY + bestPointDerivation[(pointIndex + bestPointDerivation.length - 1) % bestPointDerivation.length][1];
844 var angle = getAngle(startX, startY, targetX, targetY);
845 placeObject(centerX + bestPointDerivation[pointIndex][0], centerY + bestPointDerivation[pointIndex][1], wallStyles[style]["tower"].entity, playerId, angle - PI/2 + wallStyles[style]["tower"].angle);
846 }
847}
848
849/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
850// Place an generic 'organic' looking wall of repeatant wall elements given by wallPart roughly arround centerX/centerY with roughly the given radius
851/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
852// radius: A float seting the aproximate radius of the fortress
853// maxBendOff: Optional, The maximum random bending offset of the wall elements
854// NOTE: If maxBendOff > PI/3 the wall might not circumvent the center, if > PI/2 the wall might wind up in an infinite loop!!!
855// wallPart: Optional, An array of wall element string, example: ["tower", "wall"]
856// NOTE: Don't use wall elements with bending like corners!
857function placeOrganicFortress(centerX, centerY, radius, playerId, wallPart, style, maxBendOff)
858{
859 // Setup optional arguments to the default
860 if (wallPart === undefined)
861 wallPart = ['tower', 'wall', 'tower', 'entry', 'tower', 'wall'];
862 if (maxBendOff === undefined)
863 maxBendOff = 0;
864 // Check arguments for sane values
865 if (maxBendOff > PI/3 || maxBendOff < 0)
866 warn("placeOrganicFortress maxBendOff sould satisfy 0 < maxBendOff < PI/3 (~1) to ensure stability but it is: " + maxBendOff);
867 // Check wall part length against shortest theoretical total wall length (circle perimeter)
868 var wallPartLength = getWallLength(wallPart, style);
869 if (wallPartLength > PI*radius || wallPartLength <= 0)
870 warn("placeOrganicFortress: Given wall part is longer than half the circle perimeter of given radius: Half circle perimeter ~= " + round(PI*radius) +", wallPart total length ~= " + round(wallPartLength));
871 // Setting up some variables
872 var x = radius;
873 var y = 0;
874 var angle = 0;
875 var targetX = radius;
876 var targetY = 0;
877 var targetReached = false;
878 var eleIndex = 0;
879 // Start the evil while loope ^^
880 while (targetReached == false)
881 {
882 var wallElement = wallStyles[style][wallPart[eleIndex % wallPart.length]];
883 var eleWidth = wallElement.width; // Reducing this a bit to avoid gaps might be good
884 // Stabalized bendOff. TODO: Ensure positive eleAngleWidth!!!
885 var actualRadius = getDistance(centerX, centerY, centerX + x, centerY + y);
886 var bendOff = randFloat(-maxBendOff*radius/actualRadius, maxBendOff*actualRadius/radius);
887 // Element width in radians
888 var eleAngleWidth = eleWidth*cos(bendOff) / radius; // A/L = da/dl -> da = A * dl / L -> da = 2*PI * eleWidth / (2*PI * radius) -> da = eleWidth/radius
889 var eleAngle = angle + eleAngleWidth/2 + bendOff;
890 var placeX = x - eleWidth/2 * sin(eleAngle);
891 var placeY = y + eleWidth/2 * cos(eleAngle);
892 if (wallElement.entity)
893 placeObject(centerX + placeX, centerY + placeY, wallElement.entity, playerId, eleAngle + wallElement.angle);
894 x = placeX - eleWidth/2 * sin(eleAngle);
895 y = placeY + eleWidth/2 * cos(eleAngle);
896 angle += eleAngleWidth;
897 if (eleIndex % wallPart.length == 0 && eleIndex > wallPart.length && (getDistance(x, y, targetX, targetY) < wallPartLength || angle > 2*PI))
898 targetReached = true;
899 eleIndex++;
900 }
901 // Close wall with simple linear wall
902 if (getDistance(centerX + x, centerY + y, centerX + targetX, centerY + targetY) > wallStyles[style]['wallLong'].width)
903 placeLinearWall(centerX + x, centerY + y, centerX + targetX, centerY + targetY, ['wall', 'tower'], style, playerId, true);
904}