Ticket #2258: library.js

File library.js, 12.5 KB (added by FeXoR, 11 years ago)
Line 
1
2/////////////////////////////////////////////////////////////////////////////////////////////
3// Constant definitions
4/////////////////////////////////////////////////////////////////////////////////////////////
5
6const PI = Math.PI;
7const TWO_PI = 2 * Math.PI;
8const TERRAIN_SEPARATOR = "|";
9const SEA_LEVEL = 20.0;
10const CELL_SIZE = 4;
11const HEIGHT_UNITS_PER_METRE = 732;
12const MIN_MAP_SIZE = 128;
13const MAX_MAP_SIZE = 512;
14const FALLBACK_CIV = "athen";
15
16/////////////////////////////////////////////////////////////////////////////////////////////
17// Utility functions
18/////////////////////////////////////////////////////////////////////////////////////////////
19
20function fractionToTiles(f)
21{
22 return getMapSize() * f;
23}
24
25function tilesToFraction(t)
26{
27 return t / getMapSize();
28}
29
30function fractionToSize(f)
31{
32 return getMapArea() * f;
33}
34
35function sizeToFraction(s)
36{
37 return s / getMapArea();
38}
39
40function scaleByMapSize(min, max)
41{
42 return min + ((max-min) * (getMapSize()-MIN_MAP_SIZE) / (MAX_MAP_SIZE-MIN_MAP_SIZE));
43}
44
45function cos(x)
46{
47 return Math.cos(x);
48}
49
50function sin(x)
51{
52 return Math.sin(x);
53}
54
55function abs(x) {
56 return Math.abs(x);
57}
58
59function round(x)
60{
61 return Math.round(x);
62}
63
64function lerp(a, b, t)
65{
66 return a + (b-a) * t;
67}
68
69function sqrt(x)
70{
71 return Math.sqrt(x);
72}
73
74function ceil(x)
75{
76 return Math.ceil(x);
77}
78
79function floor(x)
80{
81 return Math.floor(x);
82}
83
84function max(a, b)
85{
86 return a > b ? a : b;
87}
88
89function min(a, b)
90{
91 return a < b ? a : b;
92}
93
94function println(x)
95{
96 print(x);
97 print("\n");
98}
99
100function argsToArray(x)
101{
102 var numArgs = x.length;
103 if (numArgs != 1)
104 {
105 var ret = new Array(numArgs);
106 for (var i=0; i < numArgs; i++)
107 {
108 ret[i] = x[i];
109 }
110 return ret;
111 }
112 else
113 {
114 return x[0];
115 }
116}
117
118function chooseRand()
119{
120 if (arguments.length==0)
121 {
122 throw("chooseRand: requires at least 1 argument");
123 }
124 var ar = argsToArray(arguments);
125 return ar[randInt(ar.length)];
126}
127
128// "Inside-out" implementation of Fisher-Yates shuffle
129function shuffleArray(source)
130{
131 if (!source.length)
132 return [];
133
134 var result = [source[0]];
135 for (var i = 1; i < source.length; i++)
136 {
137 var j = randInt(0, i);
138 result[i] = result[j];
139 result[j] = source[i];
140 }
141 return result;
142}
143
144function createAreas(centeredPlacer, painter, constraint, num, retryFactor)
145{
146 if (retryFactor === undefined)
147 {
148 retryFactor = 10;
149 }
150
151 var maxFail = num * retryFactor;
152 var good = 0;
153 var bad = 0;
154 var result = [];
155 var halfSize = getMapSize()/2;
156
157 while(good < num && bad <= maxFail)
158 {
159 if (isCircularMap())
160 { // Polar coordinates
161 var r = halfSize * Math.sqrt(randFloat()); // uniform distribution
162 var theta = randFloat(0, 2 * PI);
163 centeredPlacer.x = Math.floor(r * Math.cos(theta)) + halfSize;
164 centeredPlacer.z = Math.floor(r * Math.sin(theta)) + halfSize;
165 }
166 else
167 { // Rectangular coordinates
168 centeredPlacer.x = randInt(getMapSize());
169 centeredPlacer.z = randInt(getMapSize());
170 }
171
172 var area = g_Map.createArea(centeredPlacer, painter, constraint);
173 if (area !== undefined)
174 {
175 good++;
176 result.push(area);
177 }
178 else
179 {
180 bad++;
181 }
182 }
183 return result;
184}
185
186function createAreasInAreas(centeredPlacer, painter, constraint, num, retryFactor, areas)
187{
188 if (retryFactor === undefined)
189 {
190 retryFactor = 10;
191 }
192
193 var maxFail = num * retryFactor;
194 var good = 0;
195 var bad = 0;
196 var result = [];
197 var numAreas = areas.length;
198
199 while(good < num && bad <= maxFail && numAreas)
200 {
201 // Choose random point from area
202 var i = randInt(numAreas);
203 var size = areas[i].points.length;
204 var pt = areas[i].points[randInt(size)];
205 centeredPlacer.x = pt.x;
206 centeredPlacer.z = pt.z;
207
208 var area = g_Map.createArea(centeredPlacer, painter, constraint);
209 if (area !== undefined)
210 {
211 good++;
212 result.push(area);
213 }
214 else
215 {
216 bad++;
217 }
218 }
219 return result;
220}
221
222function createObjectGroups(placer, player, constraint, num, retryFactor)
223{
224 if (retryFactor === undefined)
225 {
226 retryFactor = 10;
227 }
228
229 var maxFail = num * retryFactor;
230 var good = 0;
231 var bad = 0;
232 var halfSize = getMapSize()/2;
233 while(good < num && bad <= maxFail)
234 {
235 if (isCircularMap())
236 { // Polar coordinates
237 var r = halfSize * Math.sqrt(randFloat()); // uniform distribution
238 var theta = randFloat(0, 2 * PI);
239 placer.x = Math.floor(r * Math.cos(theta)) + halfSize;
240 placer.z = Math.floor(r * Math.sin(theta)) + halfSize;
241 }
242 else
243 { // Rectangular coordinates
244 placer.x = randInt(getMapSize());
245 placer.z = randInt(getMapSize());
246 }
247
248 var result = createObjectGroup(placer, player, constraint);
249 if (result !== undefined)
250 {
251 good++;
252 }
253 else
254 {
255 bad++;
256 }
257 }
258 return good;
259}
260
261function createObjectGroupsByAreas(placer, player, constraint, num, retryFactor, areas)
262{
263 if (retryFactor === undefined)
264 {
265 retryFactor = 10;
266 }
267
268 var maxFail = num * retryFactor;
269 var good = 0;
270 var bad = 0;
271 var numAreas = areas.length;
272
273 while(good < num && bad <= maxFail && numAreas)
274 {
275 // Choose random point from area
276 var i = randInt(numAreas);
277 var size = areas[i].points.length;
278 var pt = areas[i].points[randInt(size)];
279 placer.x = pt.x;
280 placer.z = pt.z;
281
282 var result = createObjectGroup(placer, player, constraint);
283 if (result !== undefined)
284 {
285 good++;
286 }
287 else
288 {
289 bad++;
290 }
291 }
292 return good;
293}
294
295function createTerrain(terrain)
296{
297 if (terrain instanceof Array)
298 {
299 var terrainList = [];
300
301 for (var i = 0; i < terrain.length; ++i)
302 {
303 terrainList.push(createTerrain(terrain[i]));
304 }
305
306 return new RandomTerrain(terrainList);
307 }
308 else
309 {
310 return createSimpleTerrain(terrain);
311 }
312}
313
314function createSimpleTerrain(terrain)
315{
316 if (typeof(terrain) == "string")
317 { // Split string by pipe | character, this allows specifying terrain + tree type in single string
318 var params = terrain.split(TERRAIN_SEPARATOR, 2);
319
320 if (params.length != 2)
321 {
322 return new SimpleTerrain(terrain);
323 }
324 else
325 {
326 return new SimpleTerrain(params[0], params[1]);
327 }
328 }
329 else
330 {
331 throw("createSimpleTerrain expects string as input, received "+terrain);
332 }
333}
334
335function placeObject(x, z, type, player, angle)
336{
337 g_Map.addObject(new Entity(type, player, x, z, angle));
338}
339
340function placeTerrain(x, z, terrain)
341{
342 // convert terrain param into terrain object
343 g_Map.placeTerrain(x, z, createTerrain(terrain));
344
345}
346
347function isCircularMap()
348{
349 return (g_MapSettings.CircularMap ? true : false);
350}
351
352/////////////////////////////////////////////////////////////////////////////////////////////
353// Access global map variable
354/////////////////////////////////////////////////////////////////////////////////////////////
355
356function createTileClass()
357{
358 return g_Map.createTileClass();
359}
360
361function getTileClass(id)
362{
363 // Check for valid class id
364 if (!g_Map.validClass(id))
365 {
366 return undefined;
367 }
368
369 return g_Map.tileClasses[id];
370}
371
372function createArea(placer, painter, constraint)
373{
374 return g_Map.createArea(placer, painter, constraint);
375}
376
377function createObjectGroup(placer, player, constraint)
378{
379 return g_Map.createObjectGroup(placer, player, constraint);
380}
381
382function getMapSize()
383{
384 return g_Map.size;
385}
386
387function getMapArea()
388{
389 return g_Map.size*g_Map.size;
390}
391
392function getNumPlayers()
393{
394 return g_MapSettings.PlayerData.length;
395}
396
397function getCivCode(player)
398{
399 if (g_MapSettings.PlayerData[player].Civ)
400 return g_MapSettings.PlayerData[player].Civ;
401
402 warn("undefined civ specified for player " + (player + 1) + ", falling back to '" + FALLBACK_CIV + "'");
403 return FALLBACK_CIV;
404}
405
406function areAllies(player1, player2)
407{
408 if ((g_MapSettings.PlayerData[player1].Team === undefined) || (g_MapSettings.PlayerData[player2].Team === undefined) || (g_MapSettings.PlayerData[player2].Team == -1) || (g_MapSettings.PlayerData[player1].Team == -1))
409 {
410 return false;
411 }
412 else
413 {
414 return (g_MapSettings.PlayerData[player1].Team === g_MapSettings.PlayerData[player2].Team);
415 }
416}
417
418function getPlayerTeam(player)
419{
420 if (g_MapSettings.PlayerData[player].Team === undefined)
421 {
422 return -1;
423 }
424 else
425 {
426 return g_MapSettings.PlayerData[player].Team;
427 }
428}
429
430function sortPlayers(source)
431{
432 if (!source.length)
433 return [];
434
435 var result = new Array(0);
436 var team = new Array(5);
437 for (var q = 0; q < 5; q++)
438 {
439 team[q] = new Array(1);
440 }
441
442 for (var i = -1; i < 4; i++)
443 {
444 for (var j = 0; j < source.length; j++)
445 {
446 if (getPlayerTeam(j) == i)
447 {
448 team[i+1].unshift(j+1);
449 }
450 }
451 team[i+1].pop();
452 result=result.concat(shuffleArray(team[i+1]))
453 }
454 return result;
455}
456
457function primeSortPlayers(source)
458{
459 if (!source.length)
460 return [];
461
462 var prime = new Array(source.length);
463
464 for (var i = 0; i < round(source.length/2); i++)
465 {
466 prime[2*i]=source[i];
467 prime[2*i+1]=source[source.length-1-i];
468 }
469
470 return prime;
471}
472
473function getStartingEntities(player)
474{
475 var civ = getCivCode(player);
476 if (!g_CivData[civ] || !g_CivData[civ].StartEntities || !g_CivData[civ].StartEntities.length)
477 {
478 warn("Invalid or unimplemented civ '"+civ+"' specified, falling back to '" + FALLBACK_CIV + "'");
479 civ = FALLBACK_CIV;
480 }
481
482 return g_CivData[civ].StartEntities;
483}
484
485function getHeight(x, z)
486{
487 return g_Map.getHeight(x, z);
488}
489
490function setHeight(x, z, height)
491{
492 g_Map.setHeight(x, z, height);
493}
494
495/////////////////////////////////////////////////////////////////////////////////////////////
496// Utility functions for classes
497/////////////////////////////////////////////////////////////////////////////////////////////
498
499
500// Add point to given class by id
501function addToClass(x, z, id)
502{
503 var tileClass = getTileClass(id);
504
505 if (tileClass !== null)
506 {
507 tileClass.add(x, z);
508 }
509}
510
511// Remove point from the given class by id
512function removeFromClass(x, z, id)
513{
514 var tileClass = getTileClass(id);
515
516 if (tileClass !== null)
517 {
518 tileClass.remove(x, z);
519 }
520}
521
522// Create a painter for the given class
523function paintClass(id)
524{
525 return new TileClassPainter(getTileClass(id));
526}
527
528// Create a painter for the given class
529function unPaintClass(id)
530{
531 return new TileClassUnPainter(getTileClass(id));
532}
533
534// Create an avoid constraint for the given classes by the given distances
535function avoidClasses(/*class1, dist1, class2, dist2, etc*/)
536{
537 var ar = new Array(arguments.length/2);
538 for (var i = 0; i < arguments.length/2; i++)
539 {
540 ar[i] = new AvoidTileClassConstraint(arguments[2*i], arguments[2*i+1]);
541 }
542
543 // Return single constraint
544 if (ar.length == 1)
545 {
546 return ar[0];
547 }
548 else
549 {
550 return new AndConstraint(ar);
551 }
552}
553
554// Create a stay constraint for the given classes by the given distances
555function stayClasses(/*class1, dist1, class2, dist2, etc*/)
556{
557 var ar = new Array(arguments.length/2);
558 for (var i = 0; i < arguments.length/2; i++)
559 {
560 ar[i] = new StayInTileClassConstraint(arguments[2*i], arguments[2*i+1]);
561 }
562
563 // Return single constraint
564 if (ar.length == 1)
565 {
566 return ar[0];
567 }
568 else
569 {
570 return new AndConstraint(ar);
571 }
572}
573
574// Create a border constraint for the given classes by the given distances
575function borderClasses(/*class1, idist1, odist1, class2, idist2, odist2, etc*/)
576{
577 var ar = new Array(arguments.length/3);
578 for (var i = 0; i < arguments.length/3; i++)
579 {
580 ar[i] = new BorderTileClassConstraint(arguments[3*i], arguments[3*i+1], arguments[3*i+2]);
581 }
582
583 // Return single constraint
584 if (ar.length == 1)
585 {
586 return ar[0];
587 }
588 else
589 {
590 return new AndConstraint(ar);
591 }
592}
593
594// Checks if the given tile is in class "id"
595function checkIfInClass(x, z, id)
596{
597 var tileClass = getTileClass(id);
598 if (tileClass !== null)
599 {
600 if (tileClass.countMembersInRadius(x, z, 1) !== null)
601 {
602 return tileClass.countMembersInRadius(x, z, 1);
603 }
604 else
605 {
606 return 0;
607 }
608 }
609 else
610 {
611 return 0;
612 }
613}
614
615
616// Returns the distance between 2 points
617function getDistance(x1, z1, x2, z2)
618{
619 return Math.pow(Math.pow(x1 - x2, 2) + Math.pow(z1 - z2, 2), 1/2);
620}
621
622// Returns the angle of the vector between point 1 and point 2. The angle is anticlockwise from the positive x axis.
623function getAngle(x1, z1, x2, z2)
624{
625 return Math.atan2(z2 - z1, x2 - x1);
626}
627
628// Returns the gradient of the line between point 1 and 2 in the form dz/dx
629function getGradient(x1, z1, x2, z2)
630{
631 if (x1 == x2 && z1 == z2)
632 {
633 return 0;
634 }
635 else
636 {
637 return (z1-z2)/(x1-x2);
638 }
639}
640
641function getTerrainTexture(x, y)
642{
643 return g_Map.getTexture(x, y);
644}
645