Index: ps/trunk/binaries/data/mods/public/maps/random/rmgen/wall_builder.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/rmgen/wall_builder.js (revision 20545) +++ ps/trunk/binaries/data/mods/public/maps/random/rmgen/wall_builder.js (revision 20546) @@ -1,1032 +1,1032 @@ //////////////////////////////////////////////////////////////////// // This file contains functionality to place walls on random maps // //////////////////////////////////////////////////////////////////// // To do: // Check if all wall placement methods work with wall elements with entity === undefined (some still might raise errors in that case) // Rename wall elements to fit the entity names so that entity = "structures/" + "civ + "_" + wallElement.type in the common case (as far as possible) // Perhaps add Roman army camp to style palisades and add upgraded/balanced default palisade fortress types matching civ default fortresses strength // Perhaps add further wall elements cornerInHalf, cornerOutHalf (banding PI/4) and adjust default fortress types to better fit in the octagonal territory of a civil center // Perhaps swap angle and width in WallElement class(?) definition // Adjust argument order to be always the same: // Coordinates (center/start/target) // Wall element arguments (wall/wallPart/fortressType/cornerElement) // playerId (optional, default is 0/gaia) // wallStyle (optional, default is the players civ/"palisades for gaia") // angle/orientation (optional, default is 0) // other (all optional) arguments especially those hard to define (wallPartsAssortment, maybe make an own function for it) // Some arguments don't clearly match to this concept: // endWithFirst (wall or other) // skipFirstWall (wall or other) // gateOccurence (wall or other) // numCorners (wall or other) // skipFirstWall (wall or other) // maxAngle (angle or other) // maxBendOff (angle or other, unused ATM!!!) // irregularity // maxTrys // Add treasures to wall style "others" // Adjust documentation // Perhaps rename "endLeft" to "start" and "endRight" to "end" // ?Use available civ-type wall elements rather than palisades: Remove "endLeft" and "endRight" as default wall elements and adjust default palisade fortress types? // ?Remove "endRight", "endLeft" and adjust generic fortress types palisades? // ?Think of something to enable splitting walls into two walls so more complex walls can be build and roads can have branches/crossroads? // ?Readjust placement angle for wall elements with bending when used in linear/circular walls by their bending? ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // WallElement class definition // // Concept: If placed unrotated the wall's course is towards positive Y (top) with "outside" right (+X) and "inside" left (-X) like unrotated entities has their drop-points right (in rmgen) // The course of the wall will be changed by corners (bending != 0) and so the "inside"/"outside" direction // // type Descriptive string, example: "wallLong". NOTE: Not really needed. Mainly for custom wall elements and to get the wall element type in code // entity Optional. Template name string of the entity to be placed, example: "structures/cart_wall_long". Default is undefined (No entity placed) // angle Optional. The angle (float) added to place the entity so "outside" is right when the wall element is placed unrotated. Default is 0 // width Optional. How far this wall element lengthens the wall (float), if unrotated the Y space needed. Default is 0 // indent Optional. The lateral indentation of the entity, drawn "inside" (positive values) or pushed "outside" (negative values). Default is 0 // bending Optional. How the course of the wall is changed after this element, positive is bending "in"/left/counter clockwise (like entity placement) // NOTE: Bending is not supported by all placement functions (see there) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function WallElement(type, entity, angle, width, indent, bending) { this.type = type; // Default wall element type documentation: // Lengthening straight blocking (mainly left/right symmetric) wall elements (Walls and wall fortifications) // "wall" A blocking straight wall element that mainly lengthens the wall, self-explanatory // "wallShort" self-explanatory // "wallLong" self-explanatory // "tower" A blocking straight wall element with damage potential (but for palisades) that slightly lengthens the wall, example: wall tower, palisade tower(No attack) // "wallFort" A blocking straight wall element with massive damage potential that lengthens the wall, example: fortress, palisade fort // Lengthening straight non/custom blocking (mainly left/right symmetric) wall elements (Gates and entries) // "gate" A blocking straight wall element with passability determined by owner, example: gate (Functionality not yet implemented) // "entry" A non-blocking straight wall element (same width as gate) but without an actual template or just a flag/column/obelisk // "entryTower" A non-blocking straight wall element (same width as gate) represented by a single (maybe indented) template, example: defence tower, wall tower, outpost, watchtower // "entryFort" A non-blocking straight wall element represented by a single (maybe indented) template, example: fortress, palisade fort // Bending wall elements (Wall corners) // "cornerIn" A wall element bending the wall by PI/2 "inside" (left, +, see above), example: wall tower, palisade curve // "cornerOut" A wall element bending the wall by PI/2 "outside" (right, -, see above), example: wall tower, palisade curve // "cornerHalfIn" A wall element bending the wall by PI/4 "inside" (left, +, see above), example: wall tower, palisade curve. NOTE: Not yet implemented // "cornerHalfOut" A wall element bending the wall by PI/4 "outside" (right, -, see above), example: wall tower, palisade curve. NOTE: Not yet implemented // Zero length straight indented (mainly left/right symmetric) wall elements (Outposts/watchtowers and non-defensive base structures) // "outpost" A zero-length wall element without bending far indented so it stands outside the wall, example: outpost, defence tower, watchtower // "house" A zero-length wall element without bending far indented so it stands inside the wall that grants population bonus, example: house, hut, longhouse // "barracks" A zero-length wall element without bending far indented so it stands inside the wall that grants unit production, example: barracks, tavern, ... this.entity = entity; this.angle = angle !== undefined ? angle : 0; this.width = width !== undefined ? width : 0; this.indent = indent !== undefined ? indent : 0; this.bending = bending !== undefined ? bending : 0; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Fortress class definition // // A "fortress" here is a closed wall build of multiple wall elements attached together defined in Fortress.wall // It's mainly the abstract shape defined in a Fortress instances wall because different styles can be used for it (see wallStyles) // // type Descriptive string, example: "tiny". Not really needed (WallTool.wallTypes["type string"] is used). Mainly for custom wall elements // wall Optional. Array of wall element strings. Can be set afterwards. Default is an epty array. // Example: ["entrance", "wall", "cornerIn", "wall", "gate", "wall", "entrance", "wall", "cornerIn", "wall", "gate", "wall", "cornerIn", "wall"] // centerToFirstElement Optional. Object with properties "x" and "y" representing a vector from the visual center to the first wall element. Default is undefined ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function Fortress(type, wall, centerToFirstElement) { this.type = type; // Only usefull to get the type of the actual fortress this.wall = wall !== undefined ? wall : []; this.centerToFirstElement = undefined; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // wallStyles data structure for default wall styles // // A wall style is an associative array with all wall elements of that style in it associated with the wall element type string // wallStyles holds all the wall styles within an associative array with the civ string or another descriptive strings as key // Examples: "athen", "rome_siege", "palisades", "fence", "road" //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// var wallStyles = {}; // Generic civ dependent wall style definition. "rome_siege" needs some tweek... var wallScaleByType = { "athen": 1.5, "brit": 1.5, "cart": 1.8, "gaul": 1.5, "iber": 1.5, "mace": 1.5, "maur": 1.5, "pers": 1.5, "ptol": 1.5, "rome": 1.5, "sele": 1.5, "spart": 1.5, "rome_siege": 1.5 }; -for (var style in wallScaleByType) +for (let style in wallScaleByType) { - var civ = style; + let civ = style; if (style == "rome_siege") civ = "rome"; wallStyles[style] = { // Default wall elements "tower": new WallElement("tower", "structures/" + style + "_wall_tower", PI, wallScaleByType[style]), "endLeft": new WallElement("endLeft", "structures/" + style + "_wall_tower", PI, wallScaleByType[style]), // Same as tower. To be compatible with palisades... "endRight": new WallElement("endRight", "structures/" + style + "_wall_tower", PI, wallScaleByType[style]), // Same as tower. To be compatible with palisades... "cornerIn": new WallElement("cornerIn", "structures/" + style + "_wall_tower", 5/4*PI, 0, 0.35 * wallScaleByType[style], PI/2), // 2^0.5 / 4 ~= 0.35 ~= 1/3 "cornerOut": new WallElement("cornerOut", "structures/" + style + "_wall_tower", 3/4*PI, 0.71 * wallScaleByType[style], 0, -PI/2), // 2^0.5 / 2 ~= 0.71 ~= 2/3 "wallShort": new WallElement("wallShort", "structures/" + style + "_wall_short", 0, 2*wallScaleByType[style]), "wall": new WallElement("wall", "structures/" + style + "_wall_medium", 0, 4*wallScaleByType[style]), "wallMedium": new WallElement("wall", "structures/" + style + "_wall_medium", 0, 4*wallScaleByType[style]), "wallLong": new WallElement("wallLong", "structures/" + style + "_wall_long", 0, 6*wallScaleByType[style]), // Gate and entrance wall elements "gate": new WallElement("gate", "structures/" + style + "_wall_gate", PI, 6*wallScaleByType[style]), "entry": new WallElement("entry", undefined, 0, 6*wallScaleByType[style]), "entryTower": new WallElement("entryTower", "structures/" + civ + "_defense_tower", PI, 6*wallScaleByType[style], -4*wallScaleByType[style]), "entryFort": new WallElement("entryFort", "structures/" + civ + "_fortress", 0, 8*wallScaleByType[style], 6*wallScaleByType[style]), // Defensive wall elements with 0 width outside the wall "outpost": new WallElement("outpost", "structures/" + civ + "_outpost", PI, 0, -4*wallScaleByType[style]), "defenseTower": new WallElement("defenseTower", "structures/" + civ + "_defense_tower", PI, 0, -4*wallScaleByType[style]), // Base buildings wall elements with 0 width inside the wall "barracks": new WallElement("barracks", "structures/" + civ + "_barracks", PI, 0, 4.5*wallScaleByType[style]), "civilCentre": new WallElement("civilCentre", "structures/" + civ + "_civil_centre", PI, 0, 4.5*wallScaleByType[style]), "farmstead": new WallElement("farmstead", "structures/" + civ + "_farmstead", PI, 0, 4.5*wallScaleByType[style]), "field": new WallElement("field", "structures/" + civ + "_field", PI, 0, 4.5*wallScaleByType[style]), "fortress": new WallElement("fortress", "structures/" + civ + "_fortress", PI, 0, 4.5*wallScaleByType[style]), "house": new WallElement("house", "structures/" + civ + "_house", PI, 0, 4.5*wallScaleByType[style]), "market": new WallElement("market", "structures/" + civ + "_market", PI, 0, 4.5*wallScaleByType[style]), "storehouse": new WallElement("storehouse", "structures/" + civ + "_storehouse", PI, 0, 4.5*wallScaleByType[style]), "temple": new WallElement("temple", "structures/" + civ + "_temple", PI, 0, 4.5*wallScaleByType[style]), // Generic space/gap wall elements "space1": new WallElement("space1", undefined, 0, 1*wallScaleByType[style]), "space2": new WallElement("space2", undefined, 0, 2*wallScaleByType[style]), "space3": new WallElement("space3", undefined, 0, 3*wallScaleByType[style]), "space4": new WallElement("space4", undefined, 0, 4*wallScaleByType[style]) }; } // Add wall fortresses for all generic styles wallStyles.athen.wallFort = new WallElement("wallFort", "structures/athen_fortress", 2*PI/2, 5.1, 1.9); wallStyles.brit.wallFort = new WallElement("wallFort", "structures/brit_fortress", PI, 2.8); wallStyles.cart.wallFort = new WallElement("wallFort", "structures/cart_fortress", PI, 5.1, 1.6); wallStyles.gaul.wallFort = new WallElement("wallFort", "structures/gaul_fortress", PI, 4.2, 1.5); wallStyles.iber.wallFort = new WallElement("wallFort", "structures/iber_fortress", PI, 5, 0.2); wallStyles.mace.wallFort = new WallElement("wallFort", "structures/mace_fortress", 2*PI/2, 5.1, 1.9); wallStyles.maur.wallFort = new WallElement("wallFort", "structures/maur_fortress", PI, 5.5); wallStyles.pers.wallFort = new WallElement("wallFort", "structures/pers_fortress", PI, 5.6, 1.9); wallStyles.ptol.wallFort = new WallElement("wallFort", "structures/ptol_fortress", 2*PI/2, 5.1, 1.9); wallStyles.rome.wallFort = new WallElement("wallFort", "structures/rome_fortress", PI, 6.3, 2.1); wallStyles.sele.wallFort = new WallElement("wallFort", "structures/sele_fortress", 2*PI/2, 5.1, 1.9); wallStyles.spart.wallFort = new WallElement("wallFort", "structures/spart_fortress", 2*PI/2, 5.1, 1.9); // Adjust "rome_siege" style wallStyles.rome_siege.wallFort = new WallElement("wallFort", "structures/rome_army_camp", PI, 7.2, 2); wallStyles.rome_siege.entryFort = new WallElement("entryFort", "structures/rome_army_camp", PI, 12, 7); wallStyles.rome_siege.house = new WallElement("house", "structures/rome_tent", PI, 0, 4); // Add special wall styles not well to implement generic (and to show how custom styles can be added) wallScaleByType.palisades = 0.55; let gate = new WallElement("gate", "other/palisades_rocks_gate", PI, 3.6); wallStyles.palisades = { "wall": new WallElement("wall", "other/palisades_rocks_medium", 0, 2.3), "wallMedium": new WallElement("wall", "other/palisades_rocks_medium", 0, 2.3), "wallLong": new WallElement("wall", "other/palisades_rocks_long", 0, 3.5), "wallShort": new WallElement("wall", "other/palisades_rocks_short", 0, 1.2), "tower": new WallElement("tower", "other/palisades_rocks_tower", -PI/2, 0.7), "wallFort": new WallElement("wallFort", "other/palisades_rocks_fort", PI, 1.7), "gate": gate, "entry": new WallElement("entry", undefined, gate.angle, gate.width), "entryTower": new WallElement("entryTower", "other/palisades_rocks_watchtower", 0, gate.width, -3), "entryFort": new WallElement("entryFort", "other/palisades_rocks_fort", PI, 6, 3), "cornerIn": new WallElement("cornerIn", "other/palisades_rocks_curve", 3*PI/4, 2.1, 0.7, PI/2), "cornerOut": new WallElement("cornerOut", "other/palisades_rocks_curve", 5*PI/4, 2.1, -0.7, -PI/2), "outpost": new WallElement("outpost", "other/palisades_rocks_outpost", PI, 0, -2), "house": new WallElement("house", "other/celt_hut", PI, 0, 5), "barracks": new WallElement("barracks", "structures/gaul_tavern", PI, 0, 5), "endRight": new WallElement("endRight", "other/palisades_rocks_end", -PI/2, 0.2), "endLeft": new WallElement("endLeft", "other/palisades_rocks_end", PI/2, 0.2) }; // NOTE: This is not a wall style in the common sense. Use with care! wallStyles.road = { "short": new WallElement("road", "actor|props/special/eyecandy/road_temperate_short.xml", PI/2, 4.5), "long": new WallElement("road", "actor|props/special/eyecandy/road_temperate_long.xml", PI/2, 9.5), // Correct width by -2*indent to fit xStraicht/corner "cornerLeft": new WallElement("road", "actor|props/special/eyecandy/road_temperate_corner.xml", -PI/2, 4.5-2*1.25, 1.25, PI/2), "cornerRight": new WallElement("road", "actor|props/special/eyecandy/road_temperate_corner.xml", 0, 4.5-2*1.25, -1.25, -PI/2), "curveLeft": new WallElement("road", "actor|props/special/eyecandy/road_temperate_curve_small.xml", -PI/2, 4.5+2*0.2, -0.2, PI/2), "curveRight": new WallElement("road", "actor|props/special/eyecandy/road_temperate_curve_small.xml", 0, 4.5+2*0.2, 0.2, -PI/2), "start": new WallElement("road", "actor|props/special/eyecandy/road_temperate_end.xml", PI/2, 2), "end": new WallElement("road", "actor|props/special/eyecandy/road_temperate_end.xml", -PI/2, 2), "xStraight": new WallElement("road", "actor|props/special/eyecandy/road_temperate_intersect_x.xml", 0, 4.5), "xLeft": new WallElement("road", "actor|props/special/eyecandy/road_temperate_intersect_x.xml", 0, 4.5, 0, PI/2), "xRight": new WallElement("road", "actor|props/special/eyecandy/road_temperate_intersect_x.xml", 0, 4.5, 0, -PI/2), "tLeft": new WallElement("road", "actor|props/special/eyecandy/road_temperate_intersect_T.xml", PI, 4.5, 1.25), "tRight": new WallElement("road", "actor|props/special/eyecandy/road_temperate_intersect_T.xml", 0, 4.5, -1.25), }; // NOTE: This is not a wall style in the common sense. Use with care! wallStyles.other = { "fence": new WallElement("fence", "other/fence_long", -PI/2, 3.1), "fence_medium": new WallElement("fence", "other/fence_long", -PI/2, 3.1), "fence_short": new WallElement("fence_short", "other/fence_short", -PI/2, 1.5), "fence_stone": new WallElement("fence_stone", "other/fence_stone", -PI/2, 2.5), "palisade": new WallElement("palisade", "other/palisades_rocks_short", 0, 1.2), "column": new WallElement("column", "other/column_doric", 0, 1), "obelisk": new WallElement("obelisk", "other/obelisk", 0, 2), "spike": new WallElement("spike", "other/palisades_angle_spike", -PI/2, 1), "bench": new WallElement("bench", "other/bench", PI/2, 1.5), "benchForTable": new WallElement("benchForTable", "other/bench", 0, 0.5), "table": new WallElement("table", "other/table_rectangle", 0, 1), "table_square": new WallElement("table_square", "other/table_square", PI/2, 1), "flag": new WallElement("flag", "special/rallypoint", PI, 1), "standing_stone": new WallElement("standing_stone", "gaia/special_ruins_standing_stone", PI, 1), "settlement": new WallElement("settlement", "gaia/special_settlement", PI, 6), "gap": new WallElement("gap", undefined, 0, 2), "gapSmall": new WallElement("gapSmall", undefined, 0, 1), "gapLarge": new WallElement("gapLarge", undefined, 0, 4), "cornerIn": new WallElement("cornerIn", undefined, 0, 0, 0, PI/2), "cornerOut": new WallElement("cornerOut", undefined, 0, 0, 0, -PI/2) }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // fortressTypes data structure for some default fortress types // // A fortress type is just an instance of the Fortress class with actually something in it // fortressTypes holds all the fortresses within an associative array with a descriptive string as key (e.g. matching the map size) // Examples: "tiny", "veryLarge" //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// var fortressTypes = {}; { let wallParts = { "tiny": ["gate", "tower", "wallShort", "cornerIn", "wallShort", "tower"], "small": ["gate", "tower", "wall", "cornerIn", "wall", "tower"], "medium": ["gate", "tower", "wallLong", "cornerIn", "wallLong", "tower"], "normal": ["gate", "tower", "wall", "cornerIn", "wall", "cornerOut", "wall", "cornerIn", "wall", "tower"], "large": ["gate", "tower", "wallLong", "cornerIn", "wallLong", "cornerOut", "wallLong", "cornerIn", "wallLong", "tower"], "veryLarge": ["gate", "tower", "wall", "cornerIn", "wall", "cornerOut", "wallLong", "cornerIn", "wallLong", "cornerOut", "wall", "cornerIn", "wall", "tower"], "giant": ["gate", "tower", "wallLong", "cornerIn", "wallLong", "cornerOut", "wallLong", "cornerIn", "wallLong", "cornerOut", "wallLong", "cornerIn", "wallLong", "tower"] }; for (let type in wallParts) { fortressTypes[type] = new Fortress(type); let wp = wallParts[type]; fortressTypes[type].wall = wp.concat(wp, wp, wp); } } // Setup some better looking semi default fortresses for "palisades" style for (let type in fortressTypes) { - var newKey = type + "Palisades"; - var oldWall = fortressTypes[type].wall; + let newKey = type + "Palisades"; + let oldWall = fortressTypes[type].wall; fortressTypes[newKey] = new Fortress(newKey); - var fillTowersBetween = ["wallShort", "wall", "wallLong", "endLeft", "endRight", "cornerIn", "cornerOut"]; - for (var j = 0; j < oldWall.length; j++) + let fillTowersBetween = ["wallShort", "wall", "wallLong", "endLeft", "endRight", "cornerIn", "cornerOut"]; + for (let j = 0; j < oldWall.length; ++j) { fortressTypes[newKey].wall.push(oldWall[j]); // Only works if the first element is not in fillTowersBetween (e.g. entry or gate like it should be) if (j+1 < oldWall.length) if (fillTowersBetween.indexOf(oldWall[j]) > -1 && fillTowersBetween.indexOf(oldWall[j+1]) > -1) // ... > -1 means "exists" here fortressTypes[newKey].wall.push("tower"); } } // Setup some balanced (to civ type fortresses) semi default fortresses for "palisades" style // TODO // Add some "fortress types" for roads (will only work with style "road") { // ["start", "short", "xRight", "xLeft", "cornerLeft", "xStraight", "long", "xLeft", "xRight", "cornerRight", "tRight", "tLeft", "xRight", "xLeft", "curveLeft", "xStraight", "curveRight", "end"]; let roadTypes = { "road01": ["short", "curveLeft", "short", "curveLeft", "short", "curveLeft", "short", "curveLeft"], "road02": ["short", "cornerLeft", "short", "cornerLeft", "short", "cornerLeft", "short", "cornerLeft"], "road03": ["xStraight", "curveLeft", "xStraight", "curveLeft", "xStraight", "curveLeft", "xStraight", "curveLeft"], "road04": ["start", "curveLeft", "tRight", "cornerLeft", "tRight", "curveRight", "short", "xRight", "curveLeft", "xRight", "short", "cornerLeft", "tRight", "short", "curveLeft", "short", "tRight", "cornerLeft", "short", "xRight", "curveLeft", "xRight", "short", "curveRight", "tRight", "cornerLeft", "tRight", "curveLeft", "end"], "road05": ["start", "tLeft", "short", "xRight", "curveLeft", "xRight", "tRight", "cornerLeft", "tRight", "curveLeft", "short", "tRight", "cornerLeft", "xRight", "cornerLeft", "xRight", "short", "tRight", "curveLeft", "end"], }; for (let type in roadTypes) fortressTypes[type] = new Fortress(type, roadTypes[type]); } /////////////////////////////// // Define some helper functions /////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // getWallAlignment // // Returns a list of objects containing all information to place all the wall elements entities with placeObject (but the player ID) // Placing the first wall element at startX/startY placed with an angle given by orientation // An alignment can be used to get the "center" of a "wall" (more likely used for fortresses) with getCenterToFirstElement ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function getWallAlignment(startX, startY, wall, style, orientation) { // Graciously handle arguments if (wall === undefined) wall = []; if (!wallStyles.hasOwnProperty(style)) { warn("Function getWallAlignment: Unknown style: " + style + ' (falling back to "athen")'); style = "athen"; } orientation = orientation || 0; - var alignment = []; - var wallX = startX; - var wallY = startY; - for (var i = 0; i < wall.length; i++) + let alignment = []; + let wallX = startX; + let wallY = startY; + for (let i = 0; i < wall.length; ++i) { - var element = wallStyles[style][wall[i]]; + let element = wallStyles[style][wall[i]]; if (element === undefined && i == 0) warn("No valid wall element: " + wall[i]); // Indentation - var placeX = wallX - element.indent * cos(orientation); - var placeY = wallY - element.indent * sin(orientation); + let placeX = wallX - element.indent * cos(orientation); + let placeY = wallY - element.indent * sin(orientation); // Add wall elements entity placement arguments to the alignment alignment.push({ "x": placeX, "y": placeY, "entity": element.entity, "angle": orientation + element.angle }); // Preset vars for the next wall element if (i+1 < wall.length) { orientation += element.bending; - var nextElement = wallStyles[style][wall[i+1]]; + let nextElement = wallStyles[style][wall[i+1]]; if (nextElement === undefined) warn("No valid wall element: " + wall[i+1]); - var distance = (element.width + nextElement.width)/2; + let distance = (element.width + nextElement.width)/2; // Corrections for elements with indent AND bending - var indent = element.indent; - var bending = element.bending; + let indent = element.indent; + let bending = element.bending; if (bending !== 0 && indent !== 0) { // Indent correction to adjust distance distance += indent*sin(bending); // Indent correction to normalize indentation wallX += indent * cos(orientation); wallY += indent * sin(orientation); } // Set the next coordinates of the next element in the wall without indentation adjustment wallX -= distance * sin(orientation); wallY += distance * cos(orientation); } } return alignment; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // getCenterToFirstElement // // Center calculation works like getting the center of mass assuming all wall elements have the same "weight" // // It returns the vector from the center to the first wall element // Used to get centerToFirstElement of fortresses by default ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function getCenterToFirstElement(alignment) { - var centerToFirstElement = {"x": 0, "y": 0}; - for (var i = 0; i < alignment.length; i++) + let centerToFirstElement = { "x": 0, "y": 0 }; + for (let i = 0; i < alignment.length; ++i) { centerToFirstElement.x -= alignment[i].x/alignment.length; centerToFirstElement.y -= alignment[i].y/alignment.length; } return centerToFirstElement; } ////////////////////////////////////////////////////////////////// // getWallLength // // NOTE: Does not support bending wall elements like corners! // e.g. used by placeIrregularPolygonalWall ////////////////////////////////////////////////////////////////// function getWallLength(wall, style) { // Graciously handle arguments if (wall === undefined) wall = []; if (!wallStyles.hasOwnProperty(style)) { warn("Function getWallLength: Unknown style: " + style + ' (falling back to "athen")'); style = "athen"; } - var length = 0; - for (var i = 0; i < wall.length; i++) + let length = 0; + for (let i = 0; i < wall.length; ++i) length += wallStyles[style][wall[i]].width; return length; } ///////////////////////////////////////////// // Define the different wall placer functions ///////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // placeWall // // Places a wall with wall elements attached to another like determined by WallElement properties. // // startX, startY Where the first wall element should be placed // wall Array of wall element type strings. Example: ["endLeft", "wallLong", "tower", "wallLong", "endRight"] // style Optional. Wall style string. Default is the civ of the given player, "palisades" for gaia // playerId Optional. Number of the player the wall will be placed for. Default is 0 (gaia) // orientation Optional. Angle the first wall element is placed. Default is 0 // 0 means "outside" or "front" of the wall is right (positive X) like placeObject // It will then be build towards top/positive Y (if no bending wall elements like corners are used) // Raising orientation means the wall is rotated counter-clockwise like placeObject ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function placeWall(startX, startY, wall, style, playerId, orientation) { // Graciously handle arguments if (wall === undefined) wall = []; playerId = playerId || 0; if (!wallStyles.hasOwnProperty(style)) { if (playerId == 0) style = style || "palisades"; else style = getCivCode(playerId); } orientation = orientation || 0; // Get wall alignment - var AM = getWallAlignment(startX, startY, wall, style, orientation); + let AM = getWallAlignment(startX, startY, wall, style, orientation); // Place the wall - for (var iWall = 0; iWall < wall.length; iWall++) + for (let iWall = 0; iWall < wall.length; ++iWall) { - var entity = AM[iWall].entity; + let entity = AM[iWall].entity; if (entity !== undefined) placeObject(AM[iWall].x, AM[iWall].y, entity, playerId, AM[iWall].angle); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // placeCustomFortress // // Place a fortress (mainly a closed wall build like placeWall) centered at centerX/centerY // The fortress wall should always start with the main entrance (like "entry" or "gate") to get the orientation right (like placeObject) // // fortress An instance of Fortress with a wall defined // style Optional. Wall style string. Default is the civ of the given player, "palisades" for gaia // playerId Optional. Number of the player the wall will be placed for. Default is 0 (gaia) // orientation Optional. Angle the first wall element (should be a gate or entrance) is placed. Default is BUILDING_ORIENTATION ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function placeCustomFortress(centerX, centerY, fortress, style, playerId = 0, orientation = BUILDING_ORIENTATION) { // Graciously handle arguments fortress = fortress || fortressTypes.medium; if (!wallStyles.hasOwnProperty(style)) { if (playerId == 0) style = style || "palisades"; else style = getCivCode(playerId); } // Calculate center if fortress.centerToFirstElement is undefined (default) - var centerToFirstElement = fortress.centerToFirstElement; + let centerToFirstElement = fortress.centerToFirstElement; if (centerToFirstElement === undefined) centerToFirstElement = getCenterToFirstElement(getWallAlignment(0, 0, fortress.wall, style)); // Placing the fortress wall - var startX = centerX + centerToFirstElement.x * cos(orientation) - centerToFirstElement.y * sin(orientation); - var startY = centerY + centerToFirstElement.y * cos(orientation) + centerToFirstElement.x * sin(orientation); + let startX = centerX + centerToFirstElement.x * cos(orientation) - centerToFirstElement.y * sin(orientation); + let startY = centerY + centerToFirstElement.y * cos(orientation) + centerToFirstElement.x * sin(orientation); placeWall(startX, startY, fortress.wall, style, playerId, orientation); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // placeFortress // // Like placeCustomFortress just it takes type (a fortress type string, has to be in fortressTypes) instead of an instance of Fortress /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function placeFortress(centerX, centerY, type, style, playerId, orientation) { // Graciously handle arguments type = type || "medium"; playerId = playerId || 0; if (!wallStyles.hasOwnProperty(style)) { if (playerId == 0) style = style || "palisades"; else style = getCivCode(playerId); } orientation = orientation || 0; // Call placeCustomFortress with the given arguments placeCustomFortress(centerX, centerY, fortressTypes[type], style, playerId, orientation); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // placeLinearWall // // Places a straight wall from a given coordinate to an other repeatedly using the wall parts. // // startX/startY Coordinate of the approximate beginning of the wall (Not the place of the first wall element) // targetX/targetY Coordinate of the approximate ending of the wall (Not the place of the last wall element) // wallPart Optional. An array of NON-BENDING wall element type strings. Default is ["tower", "wallLong"] // style Optional. Wall style string. Default is the civ of the given player, "palisades" for gaia // playerId Optional. Integer number of the player. Default is 0 (gaia) // endWithFirst Optional. A boolean value. If true the 1st wall element in the wallPart array will finalize the wall. Default is true // // TODO: Maybe add angle offset for more generic looking? ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function placeLinearWall(startX, startY, targetX, targetY, wallPart, style, playerId, endWithFirst) { // Setup optional arguments to the default wallPart = wallPart || ["tower", "wallLong"]; playerId = playerId || 0; if (!wallStyles.hasOwnProperty(style)) { if (playerId == 0) style = style || "palisades"; else style = getCivCode(playerId); } endWithFirst = typeof endWithFirst == "undefined" ? true : endWithFirst; // Check arguments - for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++) + for (let elementIndex = 0; elementIndex < wallPart.length; ++elementIndex) { - var bending = wallStyles[style][wallPart[elementIndex]].bending; + let bending = wallStyles[style][wallPart[elementIndex]].bending; if (bending != 0) warn("Bending is not supported by placeLinearWall but a bending wall element is used: " + wallPart[elementIndex] + " -> wallStyles[style][wallPart[elementIndex]].entity"); } // Setup number of wall parts - var totalLength = Math.euclidDistance2D(startX, startY, targetX, targetY); - var wallPartLength = 0; - for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++) + let totalLength = Math.euclidDistance2D(startX, startY, targetX, targetY); + let wallPartLength = 0; + for (let elementIndex = 0; elementIndex < wallPart.length; ++elementIndex) wallPartLength += wallStyles[style][wallPart[elementIndex]].width; - var numParts = 0; + let numParts = 0; if (endWithFirst) numParts = ceil((totalLength - wallStyles[style][wallPart[0]].width) / wallPartLength); else numParts = ceil(totalLength / wallPartLength); // Setup scale factor - var scaleFactor = 1; + let scaleFactor = 1; if (endWithFirst) scaleFactor = totalLength / (numParts * wallPartLength + wallStyles[style][wallPart[0]].width); else scaleFactor = totalLength / (numParts * wallPartLength); // Setup angle - var wallAngle = getAngle(startX, startY, targetX, targetY); // NOTE: function "getAngle()" is about to be changed... - var placeAngle = wallAngle - PI/2; + let wallAngle = getAngle(startX, startY, targetX, targetY); // NOTE: function "getAngle()" is about to be changed... + let placeAngle = wallAngle - PI/2; // Place wall entities - var x = startX; - var y = startY; - for (var partIndex = 0; partIndex < numParts; partIndex++) + let x = startX; + let y = startY; + for (let partIndex = 0; partIndex < numParts; ++partIndex) { - for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++) + for (let elementIndex = 0; elementIndex < wallPart.length; ++elementIndex) { - var wallEle = wallStyles[style][wallPart[elementIndex]]; + let wallEle = wallStyles[style][wallPart[elementIndex]]; // Width correction x += scaleFactor * wallEle.width/2 * cos(wallAngle); y += scaleFactor * wallEle.width/2 * sin(wallAngle); // Indent correction - var placeX = x - wallEle.indent * sin(wallAngle); - var placeY = y + wallEle.indent * cos(wallAngle); + let placeX = x - wallEle.indent * sin(wallAngle); + let placeY = y + wallEle.indent * cos(wallAngle); // Placement - var entity = wallEle.entity; + let entity = wallEle.entity; if (entity !== undefined) placeObject(placeX, placeY, entity, playerId, placeAngle + wallEle.angle); x += scaleFactor * wallEle.width/2 * cos(wallAngle); y += scaleFactor * wallEle.width/2 * sin(wallAngle); } } if (endWithFirst) { - var wallEle = wallStyles[style][wallPart[0]]; + let wallEle = wallStyles[style][wallPart[0]]; x += scaleFactor * wallEle.width/2 * cos(wallAngle); y += scaleFactor * wallEle.width/2 * sin(wallAngle); - var entity = wallEle.entity; + let entity = wallEle.entity; if (entity !== undefined) placeObject(x, y, entity, playerId, placeAngle + wallEle.angle); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // placeCircularWall // // Place a circular wall of repeated wall elements given in the argument wallPart around centerX/centerY with the given radius // The wall can be opened forming more an arc than a circle if maxAngle < 2*PI // The orientation then determines where this open part faces (0 means right like unrotated building's drop-points) // // centerX/Y Coordinates of the circle's center // radius How wide the circle should be (approximate, especially if maxBendOff != 0) // wallPart Optional. An array of NON-BENDING wall element type strings. Default is ["tower", "wallLong"] // style Optional. Wall style string. Default is the civ of the given player, "palisades" for gaia // playerId Optional. Integer number of the player. Default is 0 (gaia) // orientation Optional. Where the open part of the (circular) arc should face (if maxAngle is < 2*PI). Default is 0 // maxAngle Optional. How far the wall should circumvent the center. Default is 2*PI (full circle) // endWithFirst Optional. Boolean. If true the 1st wall element in the wallPart array will finalize the wall. Default is false for full circles, else true // maxBendOff Optional. How irregular the circle should be. 0 means regular circle, PI/2 means very irregular. Default is 0 (regular circle) // // NOTE: Don't use wall elements with bending like corners! // TODO: Perhaps add eccentricity and maxBendOff functionality (untill now an unused argument) // TODO: Perhaps add functionality for spirals ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function placeCircularWall(centerX, centerY, radius, wallPart, style, playerId, orientation, maxAngle, endWithFirst, maxBendOff) { // Setup optional arguments to the default wallPart = wallPart || ["tower", "wallLong"]; playerId = playerId || 0; if (!wallStyles.hasOwnProperty(style)) { if (playerId == 0) style = style || "palisades"; else style = getCivCode(playerId); } orientation = orientation || 0; maxAngle = maxAngle || 2*PI; if (endWithFirst === undefined) endWithFirst = maxAngle < 2*PI - 0.001; // Can this be done better? maxBendOff = maxBendOff || 0; // Check arguments if (maxBendOff > PI/2 || maxBendOff < 0) warn("placeCircularWall maxBendOff sould satisfy 0 < maxBendOff < PI/2 (~1.5) but it is: " + maxBendOff); - for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++) + for (let elementIndex = 0; elementIndex < wallPart.length; ++elementIndex) { - var bending = wallStyles[style][wallPart[elementIndex]].bending; + let bending = wallStyles[style][wallPart[elementIndex]].bending; if (bending != 0) warn("Bending is not supported by placeCircularWall but a bending wall element is used: " + wallPart[elementIndex]); } // Setup number of wall parts - var totalLength = maxAngle * radius; - var wallPartLength = 0; - for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++) + let totalLength = maxAngle * radius; + let wallPartLength = 0; + for (let elementIndex = 0; elementIndex < wallPart.length; ++elementIndex) wallPartLength += wallStyles[style][wallPart[elementIndex]].width; - var numParts = 0; + let numParts = 0; if (endWithFirst) numParts = ceil((totalLength - wallStyles[style][wallPart[0]].width) / wallPartLength); else numParts = ceil(totalLength / wallPartLength); // Setup scale factor - var scaleFactor = 1; + let scaleFactor = 1; if (endWithFirst) scaleFactor = totalLength / (numParts * wallPartLength + wallStyles[style][wallPart[0]].width); else scaleFactor = totalLength / (numParts * wallPartLength); // Place wall entities - var actualAngle = orientation + (2*PI - maxAngle) / 2; - var x = centerX + radius*cos(actualAngle); - var y = centerY + radius*sin(actualAngle); - for (var partIndex = 0; partIndex < numParts; partIndex++) - for (var elementIndex = 0; elementIndex < wallPart.length; elementIndex++) + let actualAngle = orientation + (2*PI - maxAngle) / 2; + let x = centerX + radius*cos(actualAngle); + let y = centerY + radius*sin(actualAngle); + for (let partIndex = 0; partIndex < numParts; ++partIndex) + for (let elementIndex = 0; elementIndex < wallPart.length; ++elementIndex) { - var wallEle = wallStyles[style][wallPart[elementIndex]]; + let wallEle = wallStyles[style][wallPart[elementIndex]]; // Width correction - var addAngle = scaleFactor * wallEle.width / radius; - var targetX = centerX + radius * cos(actualAngle + addAngle); - var targetY = centerY + radius * sin(actualAngle + addAngle); - var placeX = x + (targetX - x)/2; - var placeY = y + (targetY - y)/2; - var placeAngle = actualAngle + addAngle/2; + let addAngle = scaleFactor * wallEle.width / radius; + let targetX = centerX + radius * cos(actualAngle + addAngle); + let targetY = centerY + radius * sin(actualAngle + addAngle); + let placeX = x + (targetX - x)/2; + let placeY = y + (targetY - y)/2; + let placeAngle = actualAngle + addAngle/2; // Indent correction placeX -= wallEle.indent * cos(placeAngle); placeY -= wallEle.indent * sin(placeAngle); // Placement - var entity = wallEle.entity; + let entity = wallEle.entity; if (entity !== undefined) placeObject(placeX, placeY, entity, playerId, placeAngle + wallEle.angle); // Prepare for the next wall element actualAngle += addAngle; x = centerX + radius*cos(actualAngle); y = centerY + radius*sin(actualAngle); } if (endWithFirst) { - var wallEle = wallStyles[style][wallPart[0]]; - var addAngle = scaleFactor * wallEle.width / radius; - var targetX = centerX + radius * cos(actualAngle + addAngle); - var targetY = centerY + radius * sin(actualAngle + addAngle); - var placeX = x + (targetX - x)/2; - var placeY = y + (targetY - y)/2; - var placeAngle = actualAngle + addAngle/2; + let wallEle = wallStyles[style][wallPart[0]]; + let addAngle = scaleFactor * wallEle.width / radius; + let targetX = centerX + radius * cos(actualAngle + addAngle); + let targetY = centerY + radius * sin(actualAngle + addAngle); + let placeX = x + (targetX - x)/2; + let placeY = y + (targetY - y)/2; + let placeAngle = actualAngle + addAngle/2; placeObject(placeX, placeY, wallEle.entity, playerId, placeAngle + wallEle.angle); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // placePolygonalWall // // Place a polygonal wall of repeated wall elements given in the argument wallPart around centerX/centerY with the given radius // // centerX/Y Coordinates of the polygon's center // radius How wide the circle should be in which the polygon fits // wallPart Optional. An array of NON-BENDING wall element type strings. Default is ["wallLong", "tower"] // cornerWallElement Optional. Wall element to be placed at the polygon's corners. Default is "tower" // style Optional. Wall style string. Default is the civ of the given player, "palisades" for gaia // playerId Optional. Integer number of the player. Default is 0 (gaia) // orientation Optional. Angle from the center to the first linear wall part placed. Default is 0 (towards positive X/right) // numCorners Optional. How many corners the polygon will have. Default is 8 (matching a civ centers territory) // skipFirstWall Optional. Boolean. If the first linear wall part will be left opened as entrance. Default is true // // NOTE: Don't use wall elements with bending like corners! // TODO: Replace skipFirstWall with firstWallPart to enable gate/defended entrance placement // TODO: Check some arguments // TODO: Add eccentricity and perhaps make it just call placeIrregularPolygonalWall with irregularity = 0 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function placePolygonalWall(centerX, centerY, radius, wallPart, cornerWallElement, style, playerId, orientation, numCorners, skipFirstWall = true) { // Setup optional arguments to the default wallPart = wallPart || ["wallLong", "tower"]; cornerWallElement = cornerWallElement || "tower"; // Don't use wide elements for this. Not supported well... playerId = playerId || 0; if (!wallStyles.hasOwnProperty(style)) { if (playerId == 0) style = style || "palisades"; else style = getCivCode(playerId); } orientation = orientation || 0; numCorners = numCorners || 8; // Setup angles - var angleAdd = 2*PI/numCorners; - var angleStart = orientation - angleAdd/2; + let angleAdd = 2*PI/numCorners; + let angleStart = orientation - angleAdd/2; // Setup corners - var corners = []; - for (var i = 0; i < numCorners; i++) + let corners = []; + for (let i = 0; i < numCorners; ++i) corners.push([centerX + radius*cos(angleStart + i*angleAdd), centerY + radius*sin(angleStart + i*angleAdd)]); // Place Corners and walls - for (var i = 0; i < numCorners; i++) + for (let i = 0; i < numCorners; ++i) { - var angleToCorner = getAngle(corners[i][0], corners[i][1], centerX, centerY); + let angleToCorner = getAngle(corners[i][0], corners[i][1], centerX, centerY); placeObject(corners[i][0], corners[i][1], wallStyles[style][cornerWallElement].entity, playerId, angleToCorner); if (!skipFirstWall || i != 0) placeLinearWall( // Adjustment to the corner element width (approximately) corners[i][0] + wallStyles[style][cornerWallElement].width/2 * sin(angleToCorner + angleAdd/2), // startX corners[i][1] - wallStyles[style][cornerWallElement].width/2 * cos(angleToCorner + angleAdd/2), // startY corners[(i+1)%numCorners][0] - wallStyles[style][cornerWallElement].width/2 * sin(angleToCorner + angleAdd/2), // targetX corners[(i+1)%numCorners][1] + wallStyles[style][cornerWallElement].width/2 * cos(angleToCorner + angleAdd/2), // targetY wallPart, style, playerId); } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // placeIrregularPolygonalWall // // Place an irregular polygonal wall of some wall parts to choose from around centerX/centerY with the given radius // // centerX/Y Coordinates of the polygon's center // radius How wide the circle should be in which the polygon fits // cornerWallElement Optional. Wall element to be placed at the polygon's corners. Default is "tower" // style Optional. Wall style string. Default is the civ of the given player, "palisades" for gaia // playerId Optional. Integer number of the player. Default is 0 (gaia) // orientation Optional. Angle from the center to the first linear wall part placed. Default is 0 (towards positive X/right) // numCorners Optional. How many corners the polygon will have. Default is 8 (matching a civ centers territory) // irregularity Optional. How irregular the polygon will be. 0 means regular, 1 means VERY irregular. Default is 0.5 // skipFirstWall Optional. Boolean. If the first linear wall part will be left opened as entrance. Default is true // wallPartsAssortment Optional. An array of wall part arrays to choose from for each linear wall connecting the corners. Default is hard to describe ^^ // // NOTE: wallPartsAssortment is put to the end because it's hardest to set // NOTE: Don't use wall elements with bending like corners! // TODO: Replace skipFirstWall with firstWallPart to enable gate/defended entrance placement // TODO: Check some arguments // TODO: Perhaps add eccentricity //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function placeIrregularPolygonalWall(centerX, centerY, radius, cornerWallElement, style, playerId, orientation, numCorners, irregularity, skipFirstWall, wallPartsAssortment) { // Setup optional arguments playerId = playerId || 0; if (!wallStyles.hasOwnProperty(style)) { if (playerId == 0) style = style || "palisades"; else style = getCivCode(playerId); } // Generating a generic wall part assortment with each wall part including 1 gate lengthened by walls and towers // NOTE: It might be a good idea to write an own function for that... - var defaultWallPartsAssortment = [["wallShort"], ["wall"], ["wallLong"], ["gate", "tower", "wallShort"]]; - var centeredWallPart = ["gate"]; - var extandingWallPartAssortment = [["tower", "wallLong"], ["tower", "wall"]]; + let defaultWallPartsAssortment = [["wallShort"], ["wall"], ["wallLong"], ["gate", "tower", "wallShort"]]; + let centeredWallPart = ["gate"]; + let extandingWallPartAssortment = [["tower", "wallLong"], ["tower", "wall"]]; defaultWallPartsAssortment.push(centeredWallPart); - for (var i = 0; i < extandingWallPartAssortment.length; i++) + for (let i = 0; i < extandingWallPartAssortment.length; ++i) { - var wallPart = centeredWallPart; - for (var j = 0; j < radius; j++) + let wallPart = centeredWallPart; + for (let j = 0; j < radius; ++j) { if (j%2 == 0) wallPart = wallPart.concat(extandingWallPartAssortment[i]); else { extandingWallPartAssortment[i].reverse(); wallPart = extandingWallPartAssortment[i].concat(wallPart); extandingWallPartAssortment[i].reverse(); } defaultWallPartsAssortment.push(wallPart); } } // Setup optional arguments to the default wallPartsAssortment = wallPartsAssortment || defaultWallPartsAssortment; cornerWallElement = cornerWallElement || "tower"; // Don't use wide elements for this. Not supported well... style = style || "palisades"; playerId = playerId || 0; orientation = orientation || 0; numCorners = numCorners || randIntInclusive(5, 7); irregularity = irregularity || 0.5; skipFirstWall = skipFirstWall || false; // Setup angles - var angleToCover = 2*PI; - var angleAddList = []; - for (var i = 0; i < numCorners; i++) + let angleToCover = 2*PI; + let angleAddList = []; + for (let i = 0; i < numCorners; ++i) { // Randomize covered angles. Variety scales down with raising angle though... angleAddList.push(angleToCover/(numCorners-i) * (1 + randFloat(-irregularity, irregularity))); angleToCover -= angleAddList[angleAddList.length - 1]; } // Setup corners - var corners = []; - var angleActual = orientation - angleAddList[0]/2; - for (var i = 0; i < numCorners; i++) + let corners = []; + let angleActual = orientation - angleAddList[0]/2; + for (let i = 0; i < numCorners; ++i) { corners.push([centerX + radius*cos(angleActual), centerY + radius*sin(angleActual)]); if (i < numCorners - 1) angleActual += angleAddList[i+1]; } // Setup best wall parts for the different walls (a bit confusing naming...) - var wallPartLengths = []; - var maxWallPartLength = 0; - for (var partIndex = 0; partIndex < wallPartsAssortment.length; partIndex++) + let wallPartLengths = []; + let maxWallPartLength = 0; + for (let partIndex = 0; partIndex < wallPartsAssortment.length; ++partIndex) { - var length = wallPartLengths[partIndex]; + let length = wallPartLengths[partIndex]; wallPartLengths.push(getWallLength(wallPartsAssortment[partIndex], style)); if (length > maxWallPartLength) maxWallPartLength = length; } - var wallPartList = []; // This is the list of the wall parts to use for the walls between the corners, not to confuse with wallPartsAssortment! - for (var i = 0; i < numCorners; i++) + let wallPartList = []; // This is the list of the wall parts to use for the walls between the corners, not to confuse with wallPartsAssortment! + for (let i = 0; i < numCorners; ++i) { - var bestWallPart = []; // This is a simpel wall part not a wallPartsAssortment! - var bestWallLength = 99999999; + let bestWallPart = []; // This is a simpel wall part not a wallPartsAssortment! + let bestWallLength = 99999999; // NOTE: This is not exactly like the length the wall will be in the end. Has to be tweaked... - var wallLength = Math.euclidDistance2D(corners[i][0], corners[i][1], corners[(i + 1) % numCorners][0], corners[(i + 1) % numCorners][1]); - var numWallParts = ceil(wallLength/maxWallPartLength); - for (var partIndex = 0; partIndex < wallPartsAssortment.length; partIndex++) + let wallLength = Math.euclidDistance2D(corners[i][0], corners[i][1], corners[(i + 1) % numCorners][0], corners[(i + 1) % numCorners][1]); + let numWallParts = ceil(wallLength/maxWallPartLength); + for (let partIndex = 0; partIndex < wallPartsAssortment.length; ++partIndex) { - var linearWallLength = numWallParts*wallPartLengths[partIndex]; + let linearWallLength = numWallParts*wallPartLengths[partIndex]; if (linearWallLength < bestWallLength && linearWallLength > wallLength) { bestWallPart = wallPartsAssortment[partIndex]; bestWallLength = linearWallLength; } } wallPartList.push(bestWallPart); } // Place Corners and walls - for (var i = 0; i < numCorners; i++) + for (let i = 0; i < numCorners; ++i) { - var angleToCorner = getAngle(corners[i][0], corners[i][1], centerX, centerY); + let angleToCorner = getAngle(corners[i][0], corners[i][1], centerX, centerY); placeObject(corners[i][0], corners[i][1], wallStyles[style][cornerWallElement].entity, playerId, angleToCorner); if (!skipFirstWall || i != 0) placeLinearWall( // Adjustment to the corner element width (approximately) corners[i][0] + wallStyles[style][cornerWallElement].width/2 * sin(angleToCorner + angleAddList[i]/2), // startX corners[i][1] - wallStyles[style][cornerWallElement].width/2 * cos(angleToCorner + angleAddList[i]/2), // startY corners[(i+1)%numCorners][0] - wallStyles[style][cornerWallElement].width/2 * sin(angleToCorner + angleAddList[(i+1)%numCorners]/2), // targetX corners[(i+1)%numCorners][1] + wallStyles[style][cornerWallElement].width/2 * cos(angleToCorner + angleAddList[(i+1)%numCorners]/2), // targetY wallPartList[i], style, playerId, false); } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // placeGenericFortress // // Places a generic fortress with towers at the edges connected with long walls and gates (entries until gates work) // This is the default Iberian civ bonus starting wall // // centerX/Y The approximate center coordinates of the fortress // radius The approximate radius of the wall to be placed // playerId Optional. Integer number of the player. Default is 0 (gaia) // style Optional. Wall style string. Default is the civ of the given player, "palisades" for gaia // irregularity Optional. Float between 0 (circle) and 1 (very spiky), default is 1/2 // gateOccurence Optional. Integer number, every n-th walls will be a gate instead. Default is 3 // maxTrys Optional. How often the function tries to find a better fitting shape at max. Default is 100 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function placeGenericFortress(centerX, centerY, radius, playerId, style, irregularity, gateOccurence, maxTrys) { // Setup optional arguments radius = radius || 20; playerId = playerId || 0; if (!wallStyles.hasOwnProperty(style)) { if (playerId == 0) style = style || "palisades"; else style = getCivCode(playerId); } irregularity = irregularity || 1/2; gateOccurence = gateOccurence || 3; maxTrys = maxTrys || 100; // Setup some vars - var startAngle = randFloat(0, 2*PI); - var actualOffX = radius*cos(startAngle); - var actualOffY = radius*sin(startAngle); - var actualAngle = startAngle; - var pointDistance = wallStyles[style].wallLong.width + wallStyles[style].tower.width; + let startAngle = randFloat(0, 2*PI); + let actualOffX = radius*cos(startAngle); + let actualOffY = radius*sin(startAngle); + let actualAngle = startAngle; + let pointDistance = wallStyles[style].wallLong.width + wallStyles[style].tower.width; // Searching for a well fitting point derivation - var tries = 0; - var bestPointDerivation = undefined; - var minOverlap = 1000; - var overlap = undefined; + let tries = 0; + let bestPointDerivation = undefined; + let minOverlap = 1000; + let overlap = undefined; while (tries < maxTrys && minOverlap > wallStyles[style].tower.width / 10) { - var pointDerivation = []; - var distanceToTarget = 1000; - var targetReached = false; + let pointDerivation = []; + let distanceToTarget = 1000; + let targetReached = false; while (!targetReached) { - var indent = randFloat(-irregularity*pointDistance, irregularity*pointDistance); - var tmpAngle = getAngle(actualOffX, actualOffY, + let indent = randFloat(-irregularity*pointDistance, irregularity*pointDistance); + let tmpAngle = getAngle(actualOffX, actualOffY, (radius + indent)*cos(actualAngle + (pointDistance / radius)), (radius + indent)*sin(actualAngle + (pointDistance / radius))); actualOffX += pointDistance*cos(tmpAngle); actualOffY += pointDistance*sin(tmpAngle); actualAngle = getAngle(0, 0, actualOffX, actualOffY); pointDerivation.push([actualOffX, actualOffY]); distanceToTarget = Math.euclidDistance2D(actualOffX, actualOffY, ...pointDerivation[0]); - var numPoints = pointDerivation.length; + let numPoints = pointDerivation.length; if (numPoints > 3 && distanceToTarget < pointDistance) // Could be done better... { targetReached = true; overlap = pointDistance - Math.euclidDistance2D(...pointDerivation[numPoints - 1], ...pointDerivation[0]); if (overlap < minOverlap) { minOverlap = overlap; bestPointDerivation = pointDerivation; } } } - tries++; + +tries; } log("placeGenericFortress: Reduced overlap to " + minOverlap + " after " + tries + " tries"); // Place wall - for (var pointIndex = 0; pointIndex < bestPointDerivation.length; pointIndex++) + for (let pointIndex = 0; pointIndex < bestPointDerivation.length; ++pointIndex) { - var startX = centerX + bestPointDerivation[pointIndex][0]; - var startY = centerY + bestPointDerivation[pointIndex][1]; - var targetX = centerX + bestPointDerivation[(pointIndex + 1) % bestPointDerivation.length][0]; - var targetY = centerY + bestPointDerivation[(pointIndex + 1) % bestPointDerivation.length][1]; - var angle = getAngle(startX, startY, targetX, targetY); - var wallElement = "wallLong"; + let startX = centerX + bestPointDerivation[pointIndex][0]; + let startY = centerY + bestPointDerivation[pointIndex][1]; + let targetX = centerX + bestPointDerivation[(pointIndex + 1) % bestPointDerivation.length][0]; + let targetY = centerY + bestPointDerivation[(pointIndex + 1) % bestPointDerivation.length][1]; + let angle = getAngle(startX, startY, targetX, targetY); + let wallElement = "wallLong"; if ((pointIndex + 1) % gateOccurence == 0) wallElement = "gate"; - var entity = wallStyles[style][wallElement].entity; + let entity = wallStyles[style][wallElement].entity; if (entity) placeObject( startX + (Math.euclidDistance2D(startX, startY, targetX, targetY) / 2) * Math.cos(angle), startY + (Math.euclidDistance2D(startX, startY, targetX, targetY) / 2) * Math.sin(angle), entity, playerId, angle - Math.PI / 2 + wallStyles[style][wallElement].angle); // Place tower - var startX = centerX + bestPointDerivation[(pointIndex + bestPointDerivation.length - 1) % bestPointDerivation.length][0]; - var startY = centerY + bestPointDerivation[(pointIndex + bestPointDerivation.length - 1) % bestPointDerivation.length][1]; - var angle = getAngle(startX, startY, targetX, targetY); + let startX = centerX + bestPointDerivation[(pointIndex + bestPointDerivation.length - 1) % bestPointDerivation.length][0]; + let startY = centerY + bestPointDerivation[(pointIndex + bestPointDerivation.length - 1) % bestPointDerivation.length][1]; + let angle = getAngle(startX, startY, targetX, targetY); placeObject( centerX + bestPointDerivation[pointIndex][0], centerY + bestPointDerivation[pointIndex][1], wallStyles[style].tower.entity, playerId, angle - PI/2 + wallStyles[style].tower.angle); } } Index: ps/trunk/binaries/data/mods/public/maps/random/wall_demo.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/wall_demo.js (revision 20545) +++ ps/trunk/binaries/data/mods/public/maps/random/wall_demo.js (revision 20546) @@ -1,162 +1,162 @@ Engine.LoadLibrary("rmgen"); InitMap(); var mapSize = getMapSize(); //////////////////////////////////////// // Demonstration code for wall placement //////////////////////////////////////// // Some general notes to the arguments: // First all the place functions take the coordinates needed to place the wall // X and Y coordinate are taken in seperate arguments like in placeObject // Their meaning differs for different placement methods but are mainly self explanatory // placeLinearWall takes 4 arguments here (2 coordinates) for startX, startY, targetX and targetY // The next argument is always the 'wall' definition, an array of wall element type strings in most cases // That looks like ['endLeft', 'wall', 'tower', 'wall', 'endRight', 'entry', 'endLeft', 'wall', 'tower', 'wall', 'endRight'] // For placeCircularWall and placeLinearWall only wall parts are needed like: ['tower', 'wall'] // They will automatically end with the first wall element if that makes sense (e.g. the wall is not closed) // NOTE: They take further optional arguments to adjust this behaviour (See the wall_builder.js for that) // placeFortress just takes a fortress type string that includes the wall definition // The default fortress type strings are made for easy placement of predefined fortresses // They are chosen like map sizes: 'tiny', 'small', 'medium', 'normal', 'large', 'veryLarge' and 'giant' // NOTE: To place a custom fortress use placeCustomFortress instead // It takes an instance of the Fortress class instead of the default fortress type strings // The next argument is always the wall style string // Wall styles are chosen by strings so the civ strings got by getCivCode() can be used // Other styles may be present as well but besides the civ styles only 'palisades' includes all wall element types (yet) // The next argument is always the index of the player that owns the wall. // 0 is Gaia, 1 is Player 1 (default color blue), 2 is Player 2 (default color red), ... // The next argument is an angle defining the orientation of the wall // placeLinearWall does not need an angle since it's defined by startX/Y and targetX/Y // Orientation works like the angle argument in placeObject // 0 is always right (towards positive X) // Raising the angle will rotate the wall counter-clockwise (mathmatical positive in default 2D) // PI/2 faces top (positive Y) // Orientation might be a little confusing for placeWall since it defines where the wall has its 'front' or 'outside' not the direction it will be build to. // It's because all other methods work like that and it's intuitive there // That means the walls outside by default (orientation = 0) faces positive X and (without bending wall elements) will be build towards positive Y // Some other arguments are taken but all of them are optional and in most cases not needed // One example is maxAngle for placeCircularWall that defines how far the wall will circumvent the center. Default is 2*PI which makes a full circle // General wall placement setup const distToMapBorder = 5; const distToOtherWalls = 10; var buildableMapSize = mapSize - 2 * distToMapBorder; var actualX = distToMapBorder; var actualY = distToMapBorder; // Wall styles are chosen by strings so the civ strings got by getCivCode() can be used // Other styles may be present as well but besides the civ styles only 'palisades' includes all wall element types (yet) const wallStyleList = ["athen", "brit", "cart", "gaul", "iber", "mace", "maur", "pers", "ptol", "rome", "sele", "spart", "rome_siege", "palisades"]; //////////////////////////////////////// // Custom wall placement (element based) //////////////////////////////////////// var wall = ['endLeft', 'wallLong', 'tower', 'wall', 'outpost', 'wall', 'cornerOut', 'wall', 'cornerIn', 'wall', 'house', 'endRight', 'entryTower', 'endLeft', 'wallShort', 'barracks', 'gate', 'tower', 'wall', 'wallFort', 'wall', 'endRight']; -for (var styleIndex = 0; styleIndex < wallStyleList.length; styleIndex++) +for (let styleIndex = 0; styleIndex < wallStyleList.length; ++styleIndex) { - var startX = actualX + styleIndex * buildableMapSize/wallStyleList.length; // X coordinate of the first wall element - var startY = actualY; // Y coordinate of the first wall element - var style = wallStyleList[styleIndex]; // // The wall's style like 'cart', 'iber', 'pers', 'rome', 'romeSiege' or 'palisades' - var orientation = styleIndex * PI/64; // Orientation of the first wall element. 0 means 'outside' or 'front' is right (positive X, like object placement) + let startX = actualX + styleIndex * buildableMapSize/wallStyleList.length; // X coordinate of the first wall element + let startY = actualY; // Y coordinate of the first wall element + let style = wallStyleList[styleIndex]; // // The wall's style like 'cart', 'iber', 'pers', 'rome', 'romeSiege' or 'palisades' + let orientation = styleIndex * PI/64; // Orientation of the first wall element. 0 means 'outside' or 'front' is right (positive X, like object placement) // That means the wall will be build towards top (positive Y) if no corners are used - var playerId = 0; // Owner of the wall (like in placeObject). 0 is Gaia, 1 is Player 1 (default color blue), ... + let playerId = 0; // Owner of the wall (like in placeObject). 0 is Gaia, 1 is Player 1 (default color blue), ... placeWall(startX, startY, wall, style, playerId, orientation); // Actually placing the wall } actualX = distToMapBorder; // Reset actualX actualY += 80 + distToOtherWalls; // Increase actualY for next wall placement method ////////////////////////////////////////////////////////////// // Default fortress placement (chosen by fortress type string) ////////////////////////////////////////////////////////////// var fortressRadius = 15; // The space the fortresses take in average. Just for design of this map -for (var styleIndex = 0; styleIndex < wallStyleList.length; styleIndex++) +for (let styleIndex = 0; styleIndex < wallStyleList.length; ++styleIndex) { - var centerX = actualX + fortressRadius + styleIndex * buildableMapSize/wallStyleList.length; // X coordinate of the center of the fortress - var centerY = actualY + fortressRadius; // Y coordinate of the center of the fortress - var type = 'tiny'; // Default fortress types are like map sizes: 'tiny', 'small', 'medium', 'large', 'veryLarge', 'giant' - var style = wallStyleList[styleIndex]; // The wall's style like 'cart', 'iber', 'pers', 'rome', 'romeSiege' or 'palisades' - var playerId = 0; // Owner of the wall. 0 is Gaia, 1 is Player 1 (default color blue), ... - var orientation = styleIndex * PI/32; // Where the 'main entrance' of the fortress should face (like in placeObject). All fortresses walls should start with an entrance + let centerX = actualX + fortressRadius + styleIndex * buildableMapSize/wallStyleList.length; // X coordinate of the center of the fortress + let centerY = actualY + fortressRadius; // Y coordinate of the center of the fortress + let type = 'tiny'; // Default fortress types are like map sizes: 'tiny', 'small', 'medium', 'large', 'veryLarge', 'giant' + let style = wallStyleList[styleIndex]; // The wall's style like 'cart', 'iber', 'pers', 'rome', 'romeSiege' or 'palisades' + let playerId = 0; // Owner of the wall. 0 is Gaia, 1 is Player 1 (default color blue), ... + let orientation = styleIndex * PI/32; // Where the 'main entrance' of the fortress should face (like in placeObject). All fortresses walls should start with an entrance placeFortress(centerX, centerY, type, style, playerId, orientation); // Actually placing the fortress placeObject(centerX, centerY, 'other/obelisk', 0, 0*PI); // Place visual marker to see the center of the fortress } actualX = distToMapBorder; // Reset actualX actualY += 2 * fortressRadius + 2 * distToOtherWalls; // Increase actualY for next wall placement method ////////////////////////// // Circular wall placement ////////////////////////// // NOTE: Don't use bending wall elements like corners here! var radius = min((mapSize - actualY - distToOtherWalls) / 3, (buildableMapSize / wallStyleList.length - distToOtherWalls) / 2); // The radius of wall circle var centerY = actualY + radius; // Y coordinate of the center of the wall circle var orientation = 0; // Where the wall circle will be open if maxAngle < 2*PI, see below. Otherwise where the first wall element will be placed -for (var styleIndex = 0; styleIndex < wallStyleList.length; styleIndex++) +for (let styleIndex = 0; styleIndex < wallStyleList.length; ++styleIndex) { - var centerX = actualX + radius + styleIndex * buildableMapSize/wallStyleList.length; // X coordinate of the center of the wall circle - var playerId = 0; // Player ID of the player owning the wall, 0 is Gaia, 1 is the first player (default blue), ... - var wallPart = ['tower', 'wall', 'house']; // List of wall elements the wall will be build of. Optional, default id ['wall'] - var style = wallStyleList[styleIndex]; // The wall's style like 'cart', 'iber', 'pers', 'rome', 'romeSiege' or 'palisades' - var maxAngle = PI/2 * (styleIndex%3 + 2); // How far the wall should circumvent the center + let centerX = actualX + radius + styleIndex * buildableMapSize/wallStyleList.length; // X coordinate of the center of the wall circle + let playerId = 0; // Player ID of the player owning the wall, 0 is Gaia, 1 is the first player (default blue), ... + let wallPart = ['tower', 'wall', 'house']; // List of wall elements the wall will be build of. Optional, default id ['wall'] + let style = wallStyleList[styleIndex]; // The wall's style like 'cart', 'iber', 'pers', 'rome', 'romeSiege' or 'palisades' + let maxAngle = PI/2 * (styleIndex%3 + 2); // How far the wall should circumvent the center placeCircularWall(centerX, centerY, radius, wallPart, style, playerId, orientation, maxAngle); // Actually placing the wall placeObject(centerX, centerY, 'other/obelisk', 0, 0*PI); // Place visual marker to see the center of the wall circle orientation += PI/16; // Increasing orientation to see how rotation works (like for object placement) } actualX = distToMapBorder; // Reset actualX actualY += 2 * radius + distToOtherWalls; // Increase actualY for next wall placement method /////////////////////////// // Polygonal wall placement /////////////////////////// // NOTE: Don't use bending wall elements like corners here! var radius = min((mapSize - actualY - distToOtherWalls) / 2, (buildableMapSize / wallStyleList.length - distToOtherWalls) / 2); // The radius of wall polygons var centerY = actualY + radius; // Y coordinate of the center of the wall polygon var orientation = 0; // Where the wall circle will be open if ???, see below. Otherwise where the first wall will be placed -for (var styleIndex = 0; styleIndex < wallStyleList.length; styleIndex++) +for (let styleIndex = 0; styleIndex < wallStyleList.length; ++styleIndex) { - var centerX = actualX + radius + styleIndex * buildableMapSize/wallStyleList.length; // X coordinate of the center of the wall circle - var playerId = 0; // Player ID of the player owning the wall, 0 is Gaia, 1 is the first player (default blue), ... - var cornerWallElement = 'tower'; // With wall element type will be uset for the corners of the polygon - var wallPart = ['wall', 'tower']; // List of wall elements the wall will be build of. Optional, default id ['wall'] - var style = wallStyleList[styleIndex]; // The wall's style like 'cart', 'iber', 'pers', 'rome', 'romeSiege' or 'palisades' - var numCorners = (styleIndex)%6 + 3; // How many corners the plogon will have - var skipFirstWall = true; // If the wall should be open towards orientation + let centerX = actualX + radius + styleIndex * buildableMapSize/wallStyleList.length; // X coordinate of the center of the wall circle + let playerId = 0; // Player ID of the player owning the wall, 0 is Gaia, 1 is the first player (default blue), ... + let cornerWallElement = 'tower'; // With wall element type will be uset for the corners of the polygon + let wallPart = ['wall', 'tower']; // List of wall elements the wall will be build of. Optional, default id ['wall'] + let style = wallStyleList[styleIndex]; // The wall's style like 'cart', 'iber', 'pers', 'rome', 'romeSiege' or 'palisades' + let numCorners = (styleIndex)%6 + 3; // How many corners the plogon will have + let skipFirstWall = true; // If the wall should be open towards orientation placePolygonalWall(centerX, centerY, radius, wallPart, cornerWallElement, style, playerId, orientation, numCorners, skipFirstWall); placeObject(centerX, centerY, 'other/obelisk', 0, 0*PI); // Place visual marker to see the center of the wall circle orientation += PI/16; // Increasing orientation to see how rotation works (like for object placement) } actualX = distToMapBorder; // Reset actualX actualY += 2 * radius + distToOtherWalls; // Increase actualY for next wall placement method //////////////////////// // Linear wall placement //////////////////////// // NOTE: Don't use bending wall elements like corners here! var maxWallLength = (mapSize - actualY - distToMapBorder - distToOtherWalls); // Just for this maps design. How long the longest wall will be var numWallsPerStyle = floor(buildableMapSize / distToOtherWalls / wallStyleList.length); // Just for this maps design. How many walls of the same style will be placed -for (var styleIndex = 0; styleIndex < wallStyleList.length; styleIndex++) - for (var wallIndex = 0; wallIndex < numWallsPerStyle; wallIndex++) +for (let styleIndex = 0; styleIndex < wallStyleList.length; ++styleIndex) + for (let wallIndex = 0; wallIndex < numWallsPerStyle; ++wallIndex) { - var startX = actualX + (styleIndex * numWallsPerStyle + wallIndex) * distToOtherWalls; // X coordinate the wall will start from - var startY = actualY; // Y coordinate the wall will start from - var endX = startX; // X coordinate the wall will end - var endY = actualY + (wallIndex + 1) * maxWallLength/numWallsPerStyle; // Y coordinate the wall will end - var playerId = 0; // Player ID of the player owning the wall, 0 is Gaia, 1 is the first player (default blue), ... - var wallPart = ['tower', 'wall']; // List of wall elements the wall will be build of - var style = wallStyleList[styleIndex]; // The wall's style like 'cart', 'iber', 'pers', 'rome', 'romeSiege' or 'palisades' + let startX = actualX + (styleIndex * numWallsPerStyle + wallIndex) * distToOtherWalls; // X coordinate the wall will start from + let startY = actualY; // Y coordinate the wall will start from + let endX = startX; // X coordinate the wall will end + let endY = actualY + (wallIndex + 1) * maxWallLength/numWallsPerStyle; // Y coordinate the wall will end + let playerId = 0; // Player ID of the player owning the wall, 0 is Gaia, 1 is the first player (default blue), ... + let wallPart = ['tower', 'wall']; // List of wall elements the wall will be build of + let style = wallStyleList[styleIndex]; // The wall's style like 'cart', 'iber', 'pers', 'rome', 'romeSiege' or 'palisades' placeLinearWall(startX, startY, endX, endY, wallPart, style, playerId); // Actually placing the wall // placeObject(startX, startY, 'other/obelisk', 0, 0*PI); // Place visual marker to see where exsactly the wall begins // placeObject(endX, endY, 'other/obelisk', 0, 0*PI); // Place visual marker to see where exsactly the wall ends } actualX = distToMapBorder; // Reset actualX actualY += maxWallLength + distToOtherWalls; // Increase actualY for next wall placement method ExportMap();