diff --git a/binaries/data/mods/public/maps/random/rmgen/library.js b/binaries/data/mods/public/maps/random/rmgen/library.js index 917173473d..8d15596b2b 100644 --- a/binaries/data/mods/public/maps/random/rmgen/library.js +++ b/binaries/data/mods/public/maps/random/rmgen/library.js @@ -400,106 +400,6 @@ function getPlayerTeam(player) return g_MapSettings.PlayerData[player+1].Team; } -/** - * Sorts an array of player IDs by team index. Players without teams come first. - * Randomize order for players of the same team. - */ -function sortPlayers(playerIndices) -{ - return shuffleArray(playerIndices).sort((p1, p2) => getPlayerTeam(p1 - 1) - getPlayerTeam(p2 - 1)); -} - -/** - * Mix player indices but sort by team. - * - * @returns {Array} - every item is an array of player indices - */ -function sortAllPlayers() -{ - let playerIDs = []; - for (let i = 0; i < getNumPlayers(); ++i) - playerIDs.push(i+1); - - return sortPlayers(playerIDs); -} - -function primeSortPlayers(playerIndices) -{ - if (!playerIndices.length) - return []; - - let prime = []; - for (let i = 0; i < Math.ceil(playerIndices.length / 2); ++i) - { - prime.push(playerIndices[i]); - prime.push(playerIndices[playerIndices.length - 1 - i]); - } - - return prime; -} - -function primeSortAllPlayers() -{ - return primeSortPlayers(sortAllPlayers()); -} - -function radialPlayerPlacement(percentRadius = 0.35) -{ - let playerIDs = sortAllPlayers(); - - let playerX = []; - let playerZ = []; - let playerAngle = []; - - let startAngle = randFloat(0, TWO_PI); - - for (let i = 0; i < getNumPlayers(); ++i) - { - playerAngle[i] = startAngle + i * TWO_PI / getNumPlayers(); - playerX[i] = 0.5 + percentRadius * Math.cos(playerAngle[i]); - playerZ[i] = 0.5 + percentRadius * Math.sin(playerAngle[i]); - } - - return [playerIDs, playerX, playerZ, playerAngle, startAngle]; -} - -/** - * Returns an array of percent numbers indicating the player location on river maps. - * For example [0.2, 0.2, 0.4, 0.4, 0.6, 0.6, 0.8, 0.8] for a 4v4 or - * [0.25, 0.33, 0.5, 0.67, 0.75] for a 2v3. - */ -function placePlayersRiver() -{ - let playerPos = []; - let numPlayers = getNumPlayers(); - let numPlayersEven = numPlayers % 2 == 0; - - for (let i = 0; i < numPlayers; ++i) - { - let currentPlayerEven = i % 2 == 0; - - let offsetDivident = numPlayersEven || currentPlayerEven ? (i + 1) % 2 : 0; - let offsetDivisor = numPlayersEven ? 0 : currentPlayerEven ? +1 : -1; - - playerPos[i] = ((i - 1 + offsetDivident) / 2 + 1) / ((numPlayers + offsetDivisor) / 2 + 1); - } - - return playerPos; -} - -function getStartingEntities(player) -{ - let civ = getCivCode(player); - - if (!g_CivData[civ] || !g_CivData[civ].StartEntities || !g_CivData[civ].StartEntities.length) - { - warn("Invalid or unimplemented civ '"+civ+"' specified, falling back to '" + FALLBACK_CIV + "'"); - civ = FALLBACK_CIV; - } - - return g_CivData[civ].StartEntities; -} - function getHeight(x, z) { return g_Map.getHeight(x, z); @@ -621,17 +521,6 @@ function getTerrainTexture(x, y) return g_Map.getTexture(x, y); } -function addCivicCenterAreaToClass(ix, iz, tileClass) -{ - addToClass(ix, iz, tileClass); - - addToClass(ix, iz + 5, tileClass); - addToClass(ix, iz - 5, tileClass); - - addToClass(ix + 5, iz, tileClass); - addToClass(ix - 5, iz, tileClass); -} - /** * Returns the order to go through the points for the shortest closed path (array of indices) * @param {array} [points] - Points to be sorted of the form { "x": x_value, "y": y_value } diff --git a/binaries/data/mods/public/maps/random/rmgen/math.js b/binaries/data/mods/public/maps/random/rmgen/math.js index e694bf38c9..284b722bd8 100644 --- a/binaries/data/mods/public/maps/random/rmgen/math.js +++ b/binaries/data/mods/public/maps/random/rmgen/math.js @@ -12,6 +12,9 @@ function getAngle(x1, z1, x2, z2) return Math.atan2(z2 - z1, x2 - x1); } +/** + * Revolve the given point around the given rotation center. + */ function rotateCoordinates(x, z, angle, centerX = 0.5, centerZ = 0.5) { let sin = Math.sin(angle); @@ -22,6 +25,26 @@ function rotateCoordinates(x, z, angle, centerX = 0.5, centerZ = 0.5) sin * (x - centerX) + cos * (z - centerZ) + centerZ ]; } + +/** + * Get pointCount points equidistantly located on a circle. + */ +function distributePointsOnCircle(pointCount, startAngle, radius, centerX, centerZ) +{ + let x = []; + let z = []; + let angle = []; + + for (let i = 0; i < pointCount; ++i) + { + angle[i] = startAngle + 2 * Math.PI * i / pointCount; + x[i] = centerX + radius * Math.cos(angle[i]); + z[i] = centerZ + radius * Math.sin(angle[i]); + } + + return [x, z, angle]; +} + /** * Returns the distance of a point from a line. */ diff --git a/binaries/data/mods/public/maps/random/rmgen/player.js b/binaries/data/mods/public/maps/random/rmgen/player.js index 875ce268aa..fccadc841c 100644 --- a/binaries/data/mods/public/maps/random/rmgen/player.js +++ b/binaries/data/mods/public/maps/random/rmgen/player.js @@ -1,3 +1,23 @@ +/** + * @file These functions locate and place the starting entities of players. + */ + +/** + * Gets the default starting entities for the civ of the given player, as defined by the civ file. + */ +function getStartingEntities(playerID) +{ + let civ = getCivCode(playerID); + + if (!g_CivData[civ] || !g_CivData[civ].StartEntities || !g_CivData[civ].StartEntities.length) + { + warn("Invalid or unimplemented civ '" + civ + "' specified, falling back to '" + FALLBACK_CIV + "'"); + civ = FALLBACK_CIV; + } + + return g_CivData[civ].StartEntities; +} + /** * Places the given entities at the given location (typically a civic center and starting units). * @param civEntities - An array of objects with the Template property and optionally a Count property. @@ -47,3 +67,97 @@ function placeCivDefaultEntities(fx, fz, playerID, kwargs, dist = 6, orientation placeGenericFortress(fx, fz, 20, playerID); } } + +/** + * Marks the corner and center tiles of an area that is about the size of a Civic Center with the given TileClass. + * Used to prevent resource collisions with the Civic Center. + */ +function addCivicCenterAreaToClass(ix, iz, tileClass) +{ + addToClass(ix, iz, tileClass); + + addToClass(ix, iz + 5, tileClass); + addToClass(ix, iz - 5, tileClass); + + addToClass(ix + 5, iz, tileClass); + addToClass(ix - 5, iz, tileClass); +} + +/** + * Sorts an array of player IDs by team index. Players without teams come first. + * Randomize order for players of the same team. + */ +function sortPlayers(playerIDs) +{ + return shuffleArray(playerIDs).sort((p1, p2) => getPlayerTeam(p1 - 1) - getPlayerTeam(p2 - 1)); +} + +/** + * Randomize playerIDs but sort by team. + * + * @returns {Array} - every item is an array of player indices + */ +function sortAllPlayers() +{ + let playerIDs = []; + for (let i = 0; i < getNumPlayers(); ++i) + playerIDs.push(i+1); + + return sortPlayers(playerIDs); +} + +/** + * Rearrange order so that teams of neighboring players alternate (if the given IDs are sorted by team). + */ +function primeSortPlayers(playerIDs) +{ + if (!playerIDs.length) + return []; + + let prime = []; + for (let i = 0; i < Math.ceil(playerIDs.length / 2); ++i) + { + prime.push(playerIDs[i]); + prime.push(playerIDs[playerIDs.length - 1 - i]); + } + + return prime; +} + +function primeSortAllPlayers() +{ + return primeSortPlayers(sortAllPlayers()); +} + +/** + * Determine player starting positions on a circular pattern. + */ +function radialPlayerPlacement(radius = 0.35, startingAngle = undefined, centerX = 0.5, centerZ = 0.5) +{ + let startAngle = startingAngle !== undefined ? startingAngle : randFloat(0, 2 * Math.PI); + return [sortAllPlayers(), ...distributePointsOnCircle(getNumPlayers(), startAngle, radius, centerX, centerZ), startAngle]; +} + +/** + * Returns an array of percent numbers indicating the player location on river maps. + * For example [0.2, 0.2, 0.4, 0.4, 0.6, 0.6, 0.8, 0.8] for a 4v4 or + * [0.25, 0.33, 0.5, 0.67, 0.75] for a 2v3. + */ +function placePlayersRiver() +{ + let playerPos = []; + let numPlayers = getNumPlayers(); + let numPlayersEven = numPlayers % 2 == 0; + + for (let i = 0; i < numPlayers; ++i) + { + let currentPlayerEven = i % 2 == 0; + + let offsetDivident = numPlayersEven || currentPlayerEven ? (i + 1) % 2 : 0; + let offsetDivisor = numPlayersEven ? 0 : currentPlayerEven ? +1 : -1; + + playerPos[i] = ((i - 1 + offsetDivident) / 2 + 1) / ((numPlayers + offsetDivisor) / 2 + 1); + } + + return playerPos; +}