Ticket #1311: wall_builder.js

File wall_builder.js, 36.8 KB (added by FeXoR, 12 years ago)
Line 
1////////////////////////////////////////////////////////////////////
2// This file contains functionality to place walls on random maps //
3////////////////////////////////////////////////////////////////////
4
5// To do:
6// Add some more checks and warnings for example if wall elements with bending are used for linear/circular wall placement
7// Add some more documentation
8// Add further wall elements cornerHalfIn, cornerHalfOut and adjust default fortress types
9// Add wall style 'roads'
10// Add trsures to 'others'
11
12// WallElement class definition
13function WallElement(type, entity, angle, width, indent, bending)
14// argument type: Descriptive string, example: 'wall'. NOTE: Not really needed. Mainly for custom wall elements and to get the wall element type in code.
15// argument entity: Optional. Template string to be placed, example: 'structures/cart_wall'. Default is undefined (No entity placed)
16// 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)
17// 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
18// argument indent: Optional. The indentation means its drawn inside (positive values) or pushed outwards (negative values). Default is 0
19// NOTE: Bending is only used for fortresses and custom walls. Linear/circular walls walls use no/generic bending
20// argument bending: Optional. How the direction of the wall is changed after this element, positive is bending 'in' (counter clockwise like entity placement)
21{
22 // NOTE: Not all wall elements have a symetry. So there's an direction 'outside' (towards right/positive X by default)
23 // In this sense 'left'/'right' means left/right of you when you stand upon the wall and look 'outside'
24 // The wall is build towards 'left' so the next wall element will be placed left of the previous one (towards positive Y by default)
25 // In this sense the wall's direction is left meaning the 'bending' of a corner is used for the element following the corner
26 // With 'inside' and 'outside' defined as above, corners bend 'in'/'out' meaning placemant angle is increased/decreased (counter clockwise like object placement)
27 this.type = type;
28 // Wall element type documentation:
29 // Enlengthening straight blocking (mainly left/right symetric) wall elements (Walls and wall fortifications)
30 // 'wall': A blocking straight wall element that mainly lengthens the wall, self-explanatory
31 // 'wallShort': self-explanatory. NOTE: Not implemented yet, waiting for finalized templates...
32 // 'wallLong': self-explanatory. NOTE: Not implemented yet, waiting for finalized templates...
33 // 'tower': A blocking straight wall element with damage potential (but for palisades) that slightly lengthens the wall, exsample: wall tower, palisade tower(No attack)
34 // 'wallFort': A blocking straight wall element with massive damage potential that lengthens the wall, exsample: fortress, palisade fort
35 // Enlengthening straight non/custom blocking (mainly left/right symetric) wall elements (Gates and entrys)
36 // 'gate': A blocking straight wall element with passability determined by owner, example: gate (Functionality not yet implemented)
37 // 'entry': A wall element like the gate but without an actual template or just a flag/column/obelisk
38 // 'entryTower': A non-blocking straight wall element represented by a single (maybe indented) template, example: defense tower, wall tower, outpost, watchtower
39 // 'entryFort': A non-blocking straight wall element represented by a single (maybe indented) template, example: fortress, palisade fort
40 // Enlengthening straight blocking non-left/right-symetric wall elements (Wall endings/closings)
41 // 'endLeft': A straight wall element that (only visually) finalizes the wall to the left (e.g. right of entrys), example: wall tower, palisade ending
42 // 'endRight': A straight wall element that (only visually) finalizes the wall to the right (e.g. left of entrys), example: wall tower, palisade ending
43 // Bending wall elements (Wall corners)
44 // 'cornerIn': A wall element bending the wall by PI/2 (90°) 'inside' (left, +, see above), example: wall tower, palisade curve
45 // 'cornerOut': A wall element bending the wall by PI/2 (90°) 'outside' (right, -, see above), example: wall tower, palisade curve
46 // 'cornerHalfIn': A wall element bending the wall by PI/4 (45°) 'inside' (left, +, see above), example: wall tower, palisade curve. NOTE: Not yet implemented
47 // 'cornerHalfOut': A wall element bending the wall by PI/4 (45°) 'outside' (right, -, see above), example: wall tower, palisade curve. NOTE: Not yet implemented
48 // Zero length straight indented (mainly left/right symetric) wall elements (Outposts/watchtowers and non-defensive base structures)
49 // 'outpost': A zero-length wall element without bending far indented so it stands outside the wall, exsample: outpost, defense tower, watchtower
50 // 'house': A zero-length wall element without bending far indented so it stands inside the wall that grants population bonus, exsample: house, hut, longhouse
51 // 'barracks': A zero-length wall element without bending far indented so it stands inside the wall that grants unit production, exsample: barracks, tavern, ...
52 this.entity = entity;
53 this.angle = 0*PI;
54 if (angle !== undefined)
55 this.angle = angle;
56 this.width = 0;
57 if (width !== undefined)
58 this.width = width;
59 this.indent = 0;
60 if (indent !== undefined)
61 this.indent = indent;
62 this.bending = 0*PI;
63 if (bending !== undefined)
64 this.bending = bending;
65};
66
67// Fortress class definition. A list would do for symetric fortresses but if 'getCenter' don't do sufficient the center can be set manually
68function Fortress(type, wall, center)
69// argument type: Descriptive string, example: 'tiny'. Not really needed (WallTool.wallTypes['type string'] is used). Mainly for custom wall elements.
70// argument wall: Optional. Array of wall element strings. Can be set afterwards. Default is an epty array.
71 // Example: ['entrance', 'wall', 'cornerIn', 'wall', 'gate', 'wall', 'entrance', 'wall', 'cornerIn', 'wall', 'gate', 'wall', 'cornerIn', 'wall']
72// 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???)
73 // NOTE: The center will be recalculated when WallTool.setFortress is called. To avoid this set WallTool.calculateCenter to false.
74{
75 this.type = type; // Only usefull to get the type of the actual fortress (by 'WallTool.fortress.type')
76 this.wall = [];
77 if (wall !== undefined)
78 this.wall = wall;
79 this.center = [0, 0]; // X/Z offset (in default orientation) from first wall element to center, perhaps should be the other way around...
80};
81
82
83// Setup data structure for some default wall styles.
84// A wall style is an associative array with all wall elements of that style in it associated with the wall element type string.
85// 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'...
86var wallStyles = {};
87// Add civilisation wall style 'cart'
88wallStyles['cart'] = {};
89wallStyles['cart']['wall'] = new WallElement('wall', 'structures/cart_wall', 0*PI, 6.2);
90wallStyles['cart']['tower'] = new WallElement('tower', 'structures/cart_wall_tower', PI, 2.7);
91wallStyles['cart']['wallFort'] = new WallElement('wallFort', 'structures/cart_fortress', PI, 5.1, 1.6);
92wallStyles['cart']['gate'] = new WallElement('gate', 'structures/cart_wall_gate', 0*PI, 6.2);
93wallStyles['cart']['entry'] = new WallElement('entry', undefined, wallStyles['cart']['gate'].angle, wallStyles['cart']['gate'].width);
94wallStyles['cart']['entryTower'] = new WallElement('entryTower', 'structures/cart_defense_tower', PI, 6.2, -5);
95wallStyles['cart']['entryFort'] = new WallElement('entryFort', 'structures/cart_fortress', PI, 12, 7);
96wallStyles['cart']['endRight'] = new WallElement('endRight', wallStyles['cart']['tower'].entity, wallStyles['cart']['tower'].angle, wallStyles['cart']['tower'].width);
97wallStyles['cart']['endLeft'] = new WallElement('endLeft', wallStyles['cart']['tower'].entity, wallStyles['cart']['tower'].angle, wallStyles['cart']['tower'].width);
98wallStyles['cart']['cornerIn'] = new WallElement('cornerIn', 'structures/cart_wall_tower', 5*PI/4, 0, 0.8, PI/2);
99wallStyles['cart']['cornerOut'] = new WallElement('cornerOut', 'structures/cart_wall_tower', 3*PI/4, 1.3 /*1.6*/, 0, -PI/2);
100wallStyles['cart']['outpost'] = new WallElement('outpost', 'structures/cart_outpost', PI, 0, -5);
101wallStyles['cart']['house'] = new WallElement('house', 'structures/cart_house', PI, 0, 7);
102wallStyles['cart']['barracks'] = new WallElement('barracks', 'structures/cart_barracks', PI, 0, 7);
103// Add civilisation wall style 'celt'
104wallStyles['celt'] = {};
105wallStyles['celt']['wall'] = new WallElement('wall', 'structures/celt_wall', 0*PI, 5.9);
106wallStyles['celt']['tower'] = new WallElement('tower', 'structures/celt_wall_tower', PI, 1.9);
107wallStyles['celt']['wallFort'] = new WallElement('wallFort', 'structures/celt_fortress_g', PI, 4.2, 1.5);
108wallStyles['celt']['gate'] = new WallElement('gate', 'structures/celt_wall_gate', 0*PI, 5.6);
109wallStyles['celt']['entry'] = new WallElement('entry', undefined, wallStyles['celt']['gate'].angle, wallStyles['celt']['gate'].width);
110wallStyles['celt']['entryTower'] = new WallElement('entryTower', 'structures/celt_defense_tower', PI, 6, -5);
111wallStyles['celt']['entryFort'] = new WallElement('entryFort', 'structures/celt_fortress_b', PI, 8, 5);
112wallStyles['celt']['endRight'] = new WallElement('endRight', wallStyles['celt']['tower'].entity, wallStyles['celt']['tower'].angle, wallStyles['celt']['tower'].width);
113wallStyles['celt']['endLeft'] = new WallElement('endLeft', wallStyles['celt']['tower'].entity, wallStyles['celt']['tower'].angle, wallStyles['celt']['tower'].width);
114wallStyles['celt']['cornerIn'] = new WallElement('cornerIn', 'structures/celt_wall_tower', 5*PI/4, 0, 0.7, PI/2);
115wallStyles['celt']['cornerOut'] = new WallElement('cornerOut', 'structures/celt_wall_tower', 3*PI/4, 1.3, 0, -PI/2);
116wallStyles['celt']['outpost'] = new WallElement('outpost', 'structures/celt_outpost', PI, 0, -5);
117wallStyles['celt']['house'] = new WallElement('house', 'structures/celt_house', PI, 0, 3);
118wallStyles['celt']['barracks'] = new WallElement('barracks', 'structures/celt_barracks', PI, 0, 7);
119// Add civilisation wall style 'hele'
120wallStyles['hele'] = {};
121wallStyles['hele']['wall'] = new WallElement('wall', 'structures/hele_wall', 0*PI, 5.95);
122wallStyles['hele']['tower'] = new WallElement('tower', 'structures/hele_wall_tower', PI, 1.5);
123wallStyles['hele']['wallFort'] = new WallElement('wallFort', 'structures/hele_fortress', 2*PI/2 /* PI/2 */, 5.1 /* 5.6 */, 1.9 /* 1.9 */);
124wallStyles['hele']['gate'] = new WallElement('gate', 'structures/hele_wall_gate', 0*PI, 9.1);
125wallStyles['hele']['entry'] = new WallElement('entry', undefined, wallStyles['hele']['gate'].angle, wallStyles['hele']['gate'].width);
126wallStyles['hele']['entryTower'] = new WallElement('entry', 'structures/hele_defense_tower', PI, 9.1, -5);
127wallStyles['hele']['entryFort'] = new WallElement('entryFort', 'structures/hele_fortress', 5*PI/2, 12, 7);
128wallStyles['hele']['endRight'] = new WallElement('endRight', wallStyles['hele']['tower'].entity, wallStyles['hele']['tower'].angle, wallStyles['hele']['tower'].width);
129wallStyles['hele']['endLeft'] = new WallElement('endLeft', wallStyles['hele']['tower'].entity, wallStyles['hele']['tower'].angle, wallStyles['hele']['tower'].width);
130wallStyles['hele']['cornerIn'] = new WallElement('cornerIn', 'structures/hele_wall_tower', 5*PI/4, 0, 0.5, PI/2);
131wallStyles['hele']['cornerOut'] = new WallElement('cornerOut', 'structures/hele_wall_tower', 3*PI/4, 1, 0, -PI/2);
132wallStyles['hele']['outpost'] = new WallElement('outpost', 'structures/hele_outpost', PI, 0, -5);
133wallStyles['hele']['house'] = new WallElement('house', 'structures/hele_house', 3*PI/2, 0, 6);
134wallStyles['hele']['barracks'] = new WallElement('barracks', 'structures/hele_barracks', PI, 0, 6);
135// Add civilisation wall style 'iber'
136wallStyles['iber'] = {};
137wallStyles['iber']['wall'] = new WallElement('wall', 'structures/iber_wall', 0*PI, 6);
138wallStyles['iber']['tower'] = new WallElement('tower', 'structures/iber_wall_tower', PI, 1.7);
139wallStyles['iber']['wallFort'] = new WallElement('wallFort', 'structures/iber_fortress', PI, 4.6, 0.7);
140wallStyles['iber']['gate'] = new WallElement('gate', 'structures/iber_wall_gate', 0*PI, 7.9);
141wallStyles['iber']['entry'] = new WallElement('entry', undefined, wallStyles['iber']['gate'].angle, wallStyles['iber']['gate'].width);
142wallStyles['iber']['entryTower'] = new WallElement('entryTower', 'structures/iber_wall_tower', PI, 6.9, -5);
143wallStyles['iber']['entryFort'] = new WallElement('entryFort', 'structures/iber_fortress', PI/2, 12, 7);
144wallStyles['iber']['endRight'] = new WallElement('endRight', wallStyles['iber']['tower'].entity, wallStyles['iber']['tower'].angle, wallStyles['iber']['tower'].width);
145wallStyles['iber']['endLeft'] = new WallElement('endLeft', wallStyles['iber']['tower'].entity, wallStyles['iber']['tower'].angle, wallStyles['iber']['tower'].width);
146wallStyles['iber']['cornerIn'] = new WallElement('cornerIn', 'structures/iber_wall_tower', 5*PI/4, 1.7, 0, PI/2);
147wallStyles['iber']['cornerOut'] = new WallElement('cornerOut', 'structures/iber_wall_tower', 3*PI/4, 1.7, 0, -PI/2);
148wallStyles['iber']['outpost'] = new WallElement('outpost', 'structures/iber_outpost', PI, 0, -5);
149wallStyles['iber']['house'] = new WallElement('house', 'structures/iber_house', PI, 0, 4);
150wallStyles['iber']['barracks'] = new WallElement('barracks', 'structures/iber_barracks', PI, 0, 7);
151// Add civilisation wall style 'pers'
152wallStyles['pers'] = {};
153wallStyles['pers']['wall'] = new WallElement('wall', 'structures/pers_wall', 0*PI, 5.9);
154wallStyles['pers']['tower'] = new WallElement('tower', 'structures/pers_wall_tower', PI, 1.7);
155wallStyles['pers']['wallFort'] = new WallElement('wallFort', 'structures/pers_fortress', PI, 5.6/*5.5*/, 1.9/*1.7*/);
156wallStyles['pers']['gate'] = new WallElement('gate', 'structures/pers_wall_gate', 0*PI, 6);
157wallStyles['pers']['entry'] = new WallElement('entry', undefined, wallStyles['pers']['gate'].angle, wallStyles['pers']['gate'].width);
158wallStyles['pers']['entryTower'] = new WallElement('entry', 'structures/pers_defense_tower', PI, 6, -5);
159wallStyles['pers']['entryFort'] = new WallElement('entryFort', 'structures/pers_fortress', PI, 12, 7);
160wallStyles['pers']['endRight'] = new WallElement('endRight', wallStyles['pers']['tower'].entity, wallStyles['pers']['tower'].angle, wallStyles['pers']['tower'].width);
161wallStyles['pers']['endLeft'] = new WallElement('endLeft', wallStyles['pers']['tower'].entity, wallStyles['pers']['tower'].angle, wallStyles['pers']['tower'].width);
162wallStyles['pers']['cornerIn'] = new WallElement('cornerIn', 'structures/pers_wall_tower', 5*PI/4, 0.2, 0.5, PI/2);
163wallStyles['pers']['cornerOut'] = new WallElement('cornerOut', 'structures/pers_wall_tower', 3*PI/4, 0.8, 0, -PI/2);
164wallStyles['pers']['outpost'] = new WallElement('outpost', 'structures/pers_outpost', PI, 0, -5);
165wallStyles['pers']['house'] = new WallElement('house', 'structures/pers_house', PI, 0, 6);
166wallStyles['pers']['barracks'] = new WallElement('barracks', 'structures/pers_barracks', PI, 0, 7);
167// Add civilisation wall style 'rome'
168wallStyles['rome'] = {};
169wallStyles['rome']['wall'] = new WallElement('wall', 'structures/rome_wall', 0*PI, 5.9);
170wallStyles['rome']['tower'] = new WallElement('tower', 'structures/rome_wall_tower', PI, 2.1);
171wallStyles['rome']['wallFort'] = new WallElement('wallFort', 'structures/rome_fortress', PI, 6.3, 2.1);
172wallStyles['rome']['gate'] = new WallElement('gate', 'structures/rome_wall_gate', 0*PI, 5.9);
173wallStyles['rome']['entry'] = new WallElement('entry', undefined, wallStyles['rome']['gate'].angle, wallStyles['rome']['gate'].width);
174wallStyles['rome']['entryTower'] = new WallElement('entryTower', 'structures/rome_defense_tower', PI, 5.9, -5);
175wallStyles['rome']['entryFort'] = new WallElement('entryFort', 'structures/rome_fortress', PI, 12, 7);
176wallStyles['rome']['endRight'] = new WallElement('endRight', wallStyles['rome']['tower'].entity, wallStyles['rome']['tower'].angle, wallStyles['rome']['tower'].width);
177wallStyles['rome']['endLeft'] = new WallElement('endLeft', wallStyles['rome']['tower'].entity, wallStyles['rome']['tower'].angle, wallStyles['rome']['tower'].width);
178wallStyles['rome']['cornerIn'] = new WallElement('cornerIn', 'structures/rome_wall_tower', 5*PI/4, 0, 0.7, PI/2);
179wallStyles['rome']['cornerOut'] = new WallElement('cornerOut', 'structures/rome_wall_tower', 3*PI/4, 1.1, 0, -PI/2);
180wallStyles['rome']['outpost'] = new WallElement('outpost', 'structures/rome_outpost', PI, 0, -5);
181wallStyles['rome']['house'] = new WallElement('house', 'structures/rome_house', PI, 0, 7);
182wallStyles['rome']['barracks'] = new WallElement('barracks', 'structures/rome_barracks', PI, 0, 6);
183// Add special wall style 'romeSiege'
184wallStyles['romeSiege'] = {};
185wallStyles['romeSiege']['wall'] = new WallElement('wall', 'structures/rome_siege_wall', 0*PI, 6.2);
186wallStyles['romeSiege']['tower'] = new WallElement('tower', 'structures/rome_siege_wall_tower', PI, 0);
187wallStyles['romeSiege']['wallFort'] = new WallElement('wallFort', 'structures/rome_army_camp', PI, 7.2, 2);
188wallStyles['romeSiege']['gate'] = new WallElement('gate', 'structures/rome_siege_wall_gate', 0*PI, 5.9);
189wallStyles['romeSiege']['entry'] = new WallElement('entry', undefined, wallStyles['romeSiege']['gate'].angle, wallStyles['romeSiege']['gate'].width);
190wallStyles['romeSiege']['entryTower'] = new WallElement('entryTower', 'structures/rome_defense_tower', PI, 5.9, -4);
191wallStyles['romeSiege']['entryFort'] = new WallElement('entryFort', 'structures/rome_army_camp', PI, 12, 7);
192wallStyles['romeSiege']['endRight'] = new WallElement('endRight', wallStyles['romeSiege']['tower'].entity, wallStyles['romeSiege']['tower'].angle, wallStyles['romeSiege']['tower'].width);
193wallStyles['romeSiege']['endLeft'] = new WallElement('endLeft', wallStyles['romeSiege']['tower'].entity, wallStyles['romeSiege']['tower'].angle, wallStyles['romeSiege']['tower'].width);
194wallStyles['romeSiege']['cornerIn'] = new WallElement('cornerIn', 'structures/rome_defense_tower', 5*PI/4, 0.1, 0.3, PI/2);
195wallStyles['romeSiege']['cornerIn2'] = new WallElement('cornerIn2', undefined, 5*PI/4, -1.6, 0, PI/2); // Grafic glitch
196wallStyles['romeSiege']['cornerOut'] = new WallElement('cornerOut', 'structures/rome_siege_wall_tower', 3*PI/4, 1.4, -0.2, -PI/2);
197wallStyles['romeSiege']['outpost'] = new WallElement('outpost', 'structures/rome_outpost', PI, 0, -5);
198wallStyles['romeSiege']['house'] = new WallElement('house', 'structures/rome_tent', PI, 0, 4);
199wallStyles['romeSiege']['barracks'] = new WallElement('barracks', 'structures/rome_barracks', PI, 0, 6);
200// Add special wall style 'palisades'
201wallStyles['palisades'] = {};
202wallStyles['palisades']['wall'] = new WallElement('wall', 'other/palisades_rocks_straight', -PI/2, 2.5);
203wallStyles['palisades']['tower'] = new WallElement('tower', 'other/palisades_rocks_tower', -PI/2, 0.7);
204wallStyles['palisades']['wallFort'] = new WallElement('wallFort', 'other/palisades_rocks_fort', PI, 1.7);
205wallStyles['palisades']['gate'] = new WallElement('gate', 'other/palisades_rocks_gate', 0*PI, 3.6);
206wallStyles['palisades']['entry'] = new WallElement('entry', undefined, wallStyles['palisades']['gate'].angle, wallStyles['palisades']['gate'].width);
207wallStyles['palisades']['entryTower'] = new WallElement('entryTower', 'other/palisades_rocks_watchtower', 0*PI, 3.6, -2);
208wallStyles['palisades']['entryFort'] = new WallElement('entryFort', 'other/palisades_rocks_fort', PI, 6, 3);
209wallStyles['palisades']['endRight'] = new WallElement('endRight', 'other/palisades_rocks_end', -PI/2, 0.2);
210wallStyles['palisades']['endLeft'] = new WallElement('endLeft', 'other/palisades_rocks_end', PI/2, 0.2);
211wallStyles['palisades']['cornerIn'] = new WallElement('cornerIn', 'other/palisades_rocks_curve', 3*PI/4, 2.1, 0.7, PI/2);
212wallStyles['palisades']['cornerOut'] = new WallElement('cornerOut', 'other/palisades_rocks_curve', 5*PI/4, 2.1, -0.7, -PI/2);
213wallStyles['palisades']['outpost'] = new WallElement('outpost', 'other/palisades_rocks_outpost', PI, 0, -2);
214wallStyles['palisades']['house'] = new WallElement('house', 'other/celt_hut', PI, 0, 5);
215wallStyles['palisades']['barracks'] = new WallElement('barracks', 'other/celt_tavern', PI, 0, 5);
216// Add special wall style 'road'
217// Add special wall element collection 'other'
218// NOTE: This is not a wall style in the common sense. Use with care!
219wallStyles['other'] = {};
220wallStyles['other']['fence'] = new WallElement('fence', 'other/fence_long', -PI/2, 3.1);
221wallStyles['other']['fence_short'] = new WallElement('fence_short', 'other/fence_short', -PI/2, 1.5);
222wallStyles['other']['fence_stone'] = new WallElement('fence_stone', 'other/fence_stone', -PI/2, 2.5);
223wallStyles['other']['palisade'] = new WallElement('palisade', 'other/palisades_rocks_short', 0, 1.2);
224wallStyles['other']['column'] = new WallElement('column', 'other/column_doric', 0, 1);
225wallStyles['other']['obelisk'] = new WallElement('obelisk', 'other/obelisk', 0, 2);
226wallStyles['other']['spike'] = new WallElement('spike', 'other/palisades_angle_spike', -PI/2, 1);
227wallStyles['other']['bench'] = new WallElement('bench', 'other/bench', PI/2, 1.5);
228wallStyles['other']['benchForTable'] = new WallElement('benchForTable', 'other/bench', 0, 0.5);
229wallStyles['other']['table'] = new WallElement('table', 'other/table_rectangle', 0, 1);
230wallStyles['other']['table_square'] = new WallElement('table_square', 'other/table_square', PI/2, 1);
231wallStyles['other']['flag'] = new WallElement('flag', 'special/rallypoint', PI, 1);
232wallStyles['other']['standing_stone'] = new WallElement('standing_stone', 'gaia/special_ruins_standing_stone', PI, 1);
233wallStyles['other']['settlement'] = new WallElement('settlement', 'gaia/special_settlement', PI, 6);
234wallStyles['other']['gap'] = new WallElement('gap', undefined, 0, 2);
235wallStyles['other']['gapSmall'] = new WallElement('gapSmall', undefined, 0, 1);
236wallStyles['other']['gapLarge'] = new WallElement('gapLarge', undefined, 0, 4);
237wallStyles['other']['cornerIn'] = new WallElement('cornerIn', undefined, 0, 0, 0, PI/2);
238wallStyles['other']['cornerOut'] = new WallElement('cornerOut', undefined, 0, 0, 0, -PI/2);
239
240
241// Setup data structure for some default fortress types
242// A fortress type is just an instance of the fortress class with actually something in it.
243// 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'
244var fortressTypes = {};
245// Setup some default fortress types
246// Add fortress type 'tiny'
247fortressTypes['tiny'] = new Fortress('tiny');
248var wallPart = ['entry', 'endLeft', 'wall', 'cornerIn', 'wall', 'endRight'];
249fortressTypes['tiny'].wall = wallPart.concat(wallPart, wallPart, wallPart);
250// Add fortress type 'small'
251fortressTypes['small'] = new Fortress('small');
252var wallPart = ['entry', 'endLeft', 'wall', 'outpost', 'wall',
253 'cornerIn', 'wall', 'outpost', 'wall', 'endRight'];
254fortressTypes['small'].wall = wallPart.concat(wallPart, wallPart, wallPart);
255// Add fortress type 'medium'
256fortressTypes['medium'] = new Fortress('medium');
257var wallPart = ['entry', 'endLeft', 'wall', 'tower', 'wall',
258 'cornerIn', 'wall', 'tower', 'wall', 'endRight'];
259fortressTypes['medium'].wall = wallPart.concat(wallPart, wallPart, wallPart);
260// Add fortress type 'normal'
261fortressTypes['normal'] = new Fortress('normal');
262var wallPart = ['entry', 'endLeft', 'wall', 'outpost', 'wall', 'cornerIn', 'wall',
263 'cornerOut', 'wall', 'cornerIn', 'wall', 'outpost', 'wall', 'endRight'];
264fortressTypes['normal'].wall = wallPart.concat(wallPart, wallPart, wallPart);
265// Add fortress type 'large'
266fortressTypes['large'] = new Fortress('large');
267var wallPart = ['entry', 'endLeft', 'wall', 'tower', 'wall', 'cornerIn', 'wall',
268 'cornerOut', 'wall', 'cornerIn', 'wall', 'tower', 'wall', 'endRight'];
269fortressTypes['large'].wall = wallPart.concat(wallPart, wallPart, wallPart);
270// Add fortress type 'veryLarge'
271fortressTypes['veryLarge'] = new Fortress('veryLarge');
272var wallPart = ['entry', 'endLeft', 'wall', 'outpost', 'wall', 'cornerIn', 'wall', 'outpost', 'wall',
273 'cornerOut', 'wall', 'outpost', 'wall', 'cornerIn', 'wall', 'outpost', 'wall', 'endRight'];
274fortressTypes['veryLarge'].wall = wallPart.concat(wallPart, wallPart, wallPart);
275// Add fortress type 'giant'
276fortressTypes['giant'] = new Fortress('giant');
277var wallPart = ['entry', 'endLeft', 'wall', 'tower', 'wall', 'cornerIn', 'wall', 'tower', 'wall',
278 'cornerOut', 'wall', 'tower', 'wall', 'cornerIn', 'wall', 'tower', 'wall', 'endRight'];
279fortressTypes['giant'].wall = wallPart.concat(wallPart, wallPart, wallPart);
280
281// Setup some semi default fortresses for 'palisades' style
282var fortressTypeKeys = ['tiny', 'small', 'medium', 'normal', 'large', 'veryLarge', 'giant']
283for (var i = 0; i < fortressTypeKeys.length; i++)
284{
285 var newKey = fortressTypeKeys[i] + 'Palisades';
286 var oldWall = fortressTypes[fortressTypeKeys[i]].wall;
287 fortressTypes[newKey] = new Fortress(newKey, []); // [] just to make sure it's an array though it's an array by default
288 var fillTowersBetween = ['wall', 'endLeft', 'endRight', 'cornerIn', 'cornerOut'];
289 for (var j = 0; j < oldWall.length; j++)
290 {
291 fortressTypes[newKey].wall.push(oldWall[j]); // Only works if the first element is an entry or gate (not in fillTowersBetween)
292 if (j+1 < oldWall.length)
293 if (fillTowersBetween.indexOf(oldWall[j]) > -1 && fillTowersBetween.indexOf(oldWall[j+1]) > -1) // ... > -1 means 'exsists' here
294 fortressTypes[newKey].wall.push('tower');
295 };
296};
297
298
299// Define the abstract getWallAlignment and getWallCenter function
300
301// Get alignment of a wall
302// Returns a list of lists of most arguments needed to place the different wall elements for a given wall
303// Placing the first wall element at startX/startY placed with angle given by orientation
304// An alignement can be used to get the center of a 'wall' (more likely used for closed walls like fortresses) with the getWallCenter function
305function getWallAlignment(startX, startY, wall, style, orientation)
306{
307 if (orientation == undefined)
308 orientation = 0;
309 var alignment = []
310 var wallX = startX;
311 var wallY = startY;
312 for (var i = 0; i < wall.length; i++)
313 {
314 if (wallStyles[style][wall[i]] == undefined)
315 warn('No valid wall element: ' + wall[i]);
316 var element = wallStyles[style][wall[i]]
317 // Indentation
318 var placeX = wallX - element.indent * cos(orientation);
319 var placeY = wallY - element.indent * sin(orientation);
320 // Add element alignment
321 alignment.push([placeX, placeY, element.entity, orientation + element.angle]);
322 // Preset vars for the next wall element
323 if (i+1 < wall.length)
324 {
325 orientation += element.bending;
326 if (wallStyles[style][wall[i+1]] == undefined)
327 warn('No valid wall element: ' + wall[i+1]);
328 var nextElement = wallStyles[style][wall[i+1]];
329 var distance = (element.width + nextElement.width)/2;
330 // Corrections for elements with indent AND bending
331 if (element.bending !== 0 && element.indent !== 0)
332 {
333 // Indent correction to adjust distance
334 distance += element.indent*sin(element.bending);
335 // Indent correction to normalize indentation
336 wallX += element.indent * cos(orientation);
337 wallY += element.indent * sin(orientation);
338 };
339 // Set the next coordinates of the next element in the wall (meaning without indentation adjustment)
340 wallX -= distance * sin(orientation);
341 wallY += distance * cos(orientation);
342 };
343 };
344 return alignment;
345};
346
347// Get the center of a wall (mainly usefull for closed walls like fortresses)
348// Center calculation works like getting the center of mass assuming all wall elements have the same 'waight'
349// It returns the vector (array [x, y]) from the first wall element to the center
350// So if used to preset the center in an instance of the fortress class use the negative values (I think)
351function getWallCenter(alignment)
352{
353 var x = 0;
354 var y = 0;
355 for (var i = 0; i < alignment.length; i++)
356 {
357 x += alignment[i][0]/alignment.length;
358 y += alignment[i][1]/alignment.length;
359 };
360 var center = [x, y];
361 return center;
362};
363
364
365// Define the different wall placer functions
366
367// Place simple wall starting with the first wall element placed at startX/startY
368function placeWall(startX, startY, wall, style, playerId, orientation)
369// orientation: 0 means 'outside' or 'front' of the wall is right (positive X) like placeObject
370// It will then be build towards top (positive Y) if no bending wall elements like corners are used
371// Raising orientation means the wall is rotated counter-clockwise like placeObject
372{
373 var AM = getWallAlignment(startX, startY, wall, style, orientation);
374 for (var iWall = 0; iWall < wall.length; iWall++)
375 {
376 if (AM[iWall][2] !== undefined)
377 placeObject(AM[iWall][0], AM[iWall][1], AM[iWall][2], playerId, AM[iWall][3]);
378 };
379};
380
381// Place a fortress (mainly a closed wall build like placeWall) with the center at centerX/centerY
382// Should always start with the main entrance (like 'entry' or 'gate') to get the orientation right (like placeObject)
383function placeCustomFortress(centerX, centerY, fortress, style, playerId, orientation, scipGetCenter)
384{
385 if (scipGetCenter == undefined)
386 scipGetCenter = false;
387 if (scipGetCenter == false)
388 {
389 var alignment = getWallAlignment(0, 0, fortress.wall, style);
390 var center = getWallCenter(alignment);
391 var startX = centerX - center[0] * cos(orientation) - center[1] * sin(orientation);
392 var startY = centerY - center[1] * cos(orientation) - center[0] * sin(orientation);
393 }
394 else
395 {
396 var startX = centerX + fortress.center[0];
397 var startY = centerY + fortress.center[1];
398 };
399 placeWall(startX, startY, fortress.wall, style, playerId, orientation)
400};
401
402function placeFortress(centerX, centerY, type, style, playerId, orientation, scipGetCenter)
403 placeCustomFortress(centerX, centerY, fortressTypes[type], style, playerId, orientation, scipGetCenter);
404
405// Place a linear wall of repeatant wall elements given in the argument wallPart from startX/startY to targetX/targetY
406// This method has no clearly defined 'inside' or 'outside' so no indentation supported yet (see below)
407function placeLinearWall(startX, startY, targetX, targetY, wallPart, style, playerId, endWithFirst)
408// startX: x coordinate of the beginning of the wall
409// startY: y coordinate of the beginning of the wall
410// targetX: x coordinate of the ending of the wall
411// targetY: y coordinate of the ending of the wall
412// 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)
413// wallPart: Optional. An array of wall element type strings (see WallElement.type). Default is ['wall']
414// NOTE: Don't use wall elements with bending like corners!
415// style: Optional. An wall style string (like defined in the 'wallStyles' dictionary). Default is 'palisades'
416// 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)
417// endWithFirst: Optional. A boolean value. If true the 1st wall element in the wallPart array will finalize the wall. Default is true
418{
419 // Setup optional arguments to the default
420 if (wallPart == undefined)
421 wallPart = ['wall'];
422 if (style == undefined)
423 style = 'palisades';
424 if (playerId == undefined)
425 playerId = 0;
426 if (endWithFirst == undefined)
427 endWithFirst = true;
428 // Setup number of wall parts
429 var totalLength = getDistance(startX, startY, targetX, targetY);
430 var wallPartLength = 0;
431 for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++)
432 wallPartLength += wallStyles[style][wallPart[elementIndex]].width;
433 var numParts = 0;
434 if (endWithFirst == true)
435 numParts = ceil((totalLength - wallStyles[style][wallPart[0]].width) / wallPartLength)
436 else
437 numParts = ceil(totalLength / wallPartLength);
438 // Setup scale factor
439 var scaleFactor = 1;
440 if (endWithFirst == true)
441 scaleFactor = totalLength / (numParts * wallPartLength + wallStyles[style][wallPart[0]].width)
442 else
443 scaleFactor = totalLength / (numParts * wallPartLength);
444 // Setup angle
445 var wallAngle = getAngle(startX, startY, targetX, targetY); // NOTE: function 'getAngle()' is about to be changed...
446 var placeAngle = wallAngle - PI/2;
447 // Place wall entities
448 var x = startX;
449 var y = startY;
450 for (var partIndex = 0; partIndex < numParts; partIndex++)
451 {
452 for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++)
453 {
454 var wallEle = wallStyles[style][wallPart[elementIndex]];
455 // Width correction
456 x += scaleFactor * wallEle.width/2 * cos(wallAngle);
457 y += scaleFactor * wallEle.width/2 * sin(wallAngle);
458 // Indent correction
459 var placeX = x - wallEle.indent * sin(wallAngle);
460 var placeY = y + wallEle.indent * cos(wallAngle);
461 // Placement
462 if (wallEle.entity !== undefined)
463 placeObject(placeX, placeY, wallEle.entity, playerId, placeAngle + wallEle.angle);
464 x += scaleFactor * wallEle.width/2 * cos(wallAngle);
465 y += scaleFactor * wallEle.width/2 * sin(wallAngle);
466 };
467 };
468 if (endWithFirst == true)
469 {
470 var wallEle = wallStyles[style][wallPart[0]];
471 x += scaleFactor * wallEle.width/2 * cos(wallAngle);
472 y += scaleFactor * wallEle.width/2 * sin(wallAngle);
473 if (wallEle.entity !== undefined)
474 placeObject(x, y, wallEle.entity, playerId, placeAngle + wallEle.angle);
475 };
476};
477
478// Place a circular wall of repeatant wall elements given in the argument wallPart arround centerX/centerY with the given radius
479// The wall is not necessarily closed depending on the optional argument maxAngle (better name?)
480function placeCircularWall(centerX, centerY, radius, wallPart, style, playerId, orientation, maxAngle, endWithFirst, maxBendOff)
481// NOTE: Don't use wall elements with bending like corners!
482{
483 // Setup optional arguments to the default
484 if (wallPart == undefined)
485 wallPart = ['wall'];
486 if (style == undefined)
487 style = 'palisades';
488 if (playerId == undefined)
489 playerId = 0;
490 if (orientation == undefined)
491 orientation = 0;
492 if (maxAngle == undefined)
493 maxAngle = 2*PI;
494 if (endWithFirst == undefined)
495 {
496 if (maxAngle >= 2*PI - 0.001) // Can this be done better?
497 {
498 endWithFirst = false;
499 }
500 else
501 {
502 endWithFirst = true;
503 };
504 };
505 if (maxBendOff == undefined)
506 maxBendOff = 0;
507 if (maxBendOff > PI/2 || maxBendOff < 0)
508 warn('placeCircularWall maxBendOff sould satisfy 0 < maxBendOff < PI/2 (~1.5) but it is: ' + maxBendOff);
509 // Setup number of wall parts
510 var totalLength = maxAngle * radius;
511 var wallPartLength = 0;
512 for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++)
513 wallPartLength += wallStyles[style][wallPart[elementIndex]].width;
514 var numParts = 0;
515 if (endWithFirst == true)
516 {
517 numParts = ceil((totalLength - wallStyles[style][wallPart[0]].width) / wallPartLength);
518 }
519 else
520 {
521 numParts = ceil(totalLength / wallPartLength);
522 };
523 // Setup scale factor
524 var scaleFactor = 1;
525 if (endWithFirst == true)
526 scaleFactor = totalLength / (numParts * wallPartLength + wallStyles[style][wallPart[0]].width)
527 else
528 scaleFactor = totalLength / (numParts * wallPartLength);
529 // Place wall entities
530 var actualAngle = orientation + (2*PI - maxAngle) / 2;
531 var x = centerX + radius*cos(actualAngle);
532 var y = centerY + radius*sin(actualAngle);
533 for (var partIndex = 0; partIndex < numParts; partIndex++)
534 {
535 for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++)
536 {
537 var wallEle = wallStyles[style][wallPart[elementIndex]];
538 // Width correction
539 var addAngle = scaleFactor * wallEle.width / radius;
540 var targetX = centerX + radius * cos(actualAngle + addAngle);
541 var targetY = centerY + radius * sin(actualAngle + addAngle);
542 var placeX = x + (targetX - x)/2;
543 var placeY = y + (targetY - y)/2;
544 var placeAngle = actualAngle + addAngle/2;
545 // Indent correction
546 placeX -= wallEle.indent * cos(placeAngle);
547 placeY -= wallEle.indent * sin(placeAngle);
548 // Placement
549 if (wallEle.entity !== undefined)
550 placeObject(placeX, placeY, wallEle.entity, playerId, placeAngle + wallEle.angle);
551 // Prepare for the next wall element
552 actualAngle += addAngle;
553 x = centerX + radius*cos(actualAngle);
554 y = centerY + radius*sin(actualAngle);
555 };
556 };
557 if (endWithFirst == true)
558 {
559 var wallEle = wallStyles[style][wallPart[0]];
560 var addAngle = scaleFactor * wallEle.width / radius;
561 var targetX = centerX + radius * cos(actualAngle + addAngle);
562 var targetY = centerY + radius * sin(actualAngle + addAngle);
563 var placeX = x + (targetX - x)/2;
564 var placeY = y + (targetY - y)/2;
565 var placeAngle = actualAngle + addAngle/2;
566 placeObject(placeX, placeY, wallEle.entity, playerId, placeAngle + wallEle.angle);
567 };
568};