Index: ps/trunk/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/extinctvolcano.png =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: ps/trunk/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/extinctvolcano.png =================================================================== --- ps/trunk/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/extinctvolcano.png (nonexistent) +++ ps/trunk/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/extinctvolcano.png (revision 19635) Property changes on: ps/trunk/binaries/data/mods/public/art/textures/ui/session/icons/mappreview/extinctvolcano.png ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/maps/random/extinct_volcano.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/extinct_volcano.js (nonexistent) +++ ps/trunk/binaries/data/mods/public/maps/random/extinct_volcano.js (revision 19635) @@ -0,0 +1,513 @@ +RMS.LoadLibrary("rmgen"); + +const tHillDark = "cliff volcanic light"; +const tHillMedium1 = "ocean_rock_a"; +const tHillMedium2 = "ocean_rock_b"; +const tHillVeryDark = ["cliff volcanic coarse", "cave_walls"]; +const tRoad = "road1"; +const tRoadWild = "road1"; +const tForestFloor1 = tHillMedium1; +const tForestFloor2 = tHillMedium2; +const tGrassA = "cliff volcanic light"; +const tGrassB = "ocean_rock_a"; +const tGrass3 = "temp_grass_plants"; +const tGrassPatchBlend = "temp_grass_long_b"; +const tGrassPatch = ["temp_grass_d", "temp_grass_clovers"]; +const tShoreBlend = "cliff volcanic light"; +const tShore = "ocean_rock_a"; +const tWater = "ocean_rock_b"; + +// Gaia entities +const oTree = "gaia/flora_tree_dead"; +const oTree2 = "gaia/flora_tree_euro_beech"; +const oTree3 = "gaia/flora_tree_oak"; +const oTree4 = "gaia/flora_tree_oak_dead"; +const oBush = "gaia/flora_bush_temperate"; +const oFruitBush = "gaia/flora_bush_berry"; +const oRabbit = "gaia/fauna_rabbit"; +const oGoat = "gaia/fauna_goat"; +const oBear = "gaia/fauna_bear"; +const oStoneLarge = "gaia/geology_stonemine_temperate_quarry"; +const oStoneSmall = "gaia/geology_stone_temperate"; +const oMetalLarge = "gaia/geology_metal_temperate_slabs"; +const oTower = "other/palisades_rocks_fort"; + +// Decorative props +const aRockLarge = "actor|geology/stone_granite_med.xml"; +const aRockMedium = "actor|geology/stone_granite_med.xml"; +const aBushMedium = "actor|props/flora/bush_tempe_me.xml"; +const aBushSmall = "actor|props/flora/bush_tempe_sm.xml"; +const aGrass = "actor|props/flora/grass_soft_large_tall.xml"; +const aGrassShort = "actor|props/flora/grass_soft_large.xml"; + +const pForestD = [ + tForestFloor1 + TERRAIN_SEPARATOR + oTree, + tForestFloor2 + TERRAIN_SEPARATOR + oTree2, + tForestFloor1 +]; + +const pForestP = [ + tForestFloor1 + TERRAIN_SEPARATOR + oTree3, + tForestFloor2 + TERRAIN_SEPARATOR + oTree4, + tForestFloor1 +]; + +log("Initializing map..."); +InitMap(); + +var P_FOREST = 0.7; +var totalTrees = scaleByMapSize(1200, 3000); +var numForest = totalTrees * P_FOREST; +var numStragglers = totalTrees * (1.0 - P_FOREST); + +var numPlayers = getNumPlayers(); +var mapSize = getMapSize(); +var mapArea = mapSize * mapSize; + +// create tile classes +var clPlayer = createTileClass(); +var clHill = createTileClass(); +var clFood = createTileClass(); +var clForest = createTileClass(); +var clWater = createTileClass(); +var clDirt = createTileClass(); +var clGrass = createTileClass(); +var clRock = createTileClass(); +var clMetal = createTileClass(); +var clBaseResource = createTileClass(); +var clBumps = createTileClass(); +var clTower = createTileClass(); + +var ccMountainHeight = 25; + +// randomize player order +var playerIDs = []; +for (let i = 0; i < numPlayers; ++i) + playerIDs.push(i+1); +playerIDs = sortPlayers(playerIDs); + +// Place players +var startAngle = randFloat(0, 2 * PI); +for (let i = 0; i < numPlayers; ++i) +{ + let playerAngle = startAngle + i * 2 * PI / numPlayers; + let playerX = 0.5 + 0.35 * Math.cos(playerAngle); + let playerZ = 0.5 + 0.35 * Math.sin(playerAngle); + + let id = playerIDs[i]; + log("Creating base for player " + id + "..."); + let radius = scaleByMapSize(15, 25); + + let fx = fractionToTiles(playerX); + let fz = fractionToTiles(playerZ); + let ix = Math.round(fx); + let iz = Math.round(fz); + + // This one consists of many bumps, creating an omnidirectional ramp + createMountain( + ccMountainHeight, + Math.floor(scaleByMapSize(15, 15)), + Math.floor(scaleByMapSize(15, 15)), + Math.floor(scaleByMapSize(4, 10)), + avoidClasses(), + ix, + iz, + tHillDark, + clPlayer, + 14); + + // Flatten the initial CC area + let hillSize = PI * radius * radius; + createArea( + new ClumpPlacer(hillSize, 0.95, 0.6, 10, ix, iz), + [ + new LayeredPainter([tHillVeryDark, tHillMedium1], [radius]), + new SmoothElevationPainter(ELEVATION_SET, ccMountainHeight, radius), + paintClass(clPlayer) + ], + null); + + // Create the city patch + let cityRadius = radius / 3; + createArea( + new ClumpPlacer(PI * cityRadius * cityRadius, 0.6, 0.3, 10, ix, iz), + new LayeredPainter([tRoadWild, tRoad], [1]), + null); + + placeCivDefaultEntities(fx, fz, id, { 'iberWall': 'towers' }); + + // Create metal mine + let mAngle = randFloat(0, 2 * PI); + let mDist = 12; + createObjectGroup( + new SimpleGroup( + [new SimpleObject(oMetalLarge, 1, 1, 0, 0)], + true, + clBaseResource, + Math.round(fx + mDist * Math.cos(mAngle)), + Math.round(fz + mDist * Math.sin(mAngle))), + 0); + + // Create stone mines + mAngle += randFloat(PI/4, PI/3); + createObjectGroup( + new SimpleGroup( + [new SimpleObject(oStoneLarge, 1, 1, 0, 2)], + true, + clBaseResource, + Math.round(fx + mDist * Math.cos(mAngle)), + Math.round(fz + mDist * Math.sin(mAngle))), + 0); + + placeDefaultChicken(fx, fz, clBaseResource); + + // Create berry bushes + mAngle += randFloat(PI/4, PI/2); + let bbDist = 12; + createObjectGroup( + new SimpleGroup( + [new SimpleObject(oFruitBush, 5, 5, 0, 3)], + true, + clBaseResource, + Math.round(fx + bbDist * Math.cos(mAngle)), + Math.round(fz + bbDist * Math.sin(mAngle))), + 0); + + // Create starting trees + let num = Math.floor(hillSize / 60); + let tries = 10; + for (let x = 0; x < tries; ++x) + { + let tAngle = mAngle + randFloat(PI/3, PI/4); + let tDist = randFloat(10, 12); + if (createObjectGroup( + new SimpleGroup( + [new SimpleObject(oTree2, num, num, 0, 3)], + false, + clBaseResource, + Math.round(fx + tDist * Math.cos(tAngle)), + Math.round(fz + tDist * Math.sin(tAngle))), + 0, + avoidClasses(clBaseResource, 3))) + { + break; + } + } +} +RMS.SetProgress(15); + +createVolcano(0.5, 0.5, clHill, tHillVeryDark, undefined, false, ELEVATION_SET); +RMS.SetProgress(20); + +log("Creating lakes..."); +createAreas( + new ChainPlacer(5, 6, Math.floor(scaleByMapSize(10, 14)), 0.1), + [ + new LayeredPainter([tShoreBlend, tShore, tWater], [1, 1]), + new SmoothElevationPainter(ELEVATION_SET, -4, 3), + paintClass(clWater) + ], + avoidClasses(clPlayer, 0, clHill, 2, clWater, 12), + Math.round(scaleByMapSize(6, 12))); +RMS.SetProgress(25); + +createBumps(avoidClasses(clPlayer, 0, clHill, 0), scaleByMapSize(50, 300), 1, 10, 3, 0, scaleByMapSize(4, 10)); +paintTileClassBasedOnHeight(10, 100, 0, clBumps); +RMS.SetProgress(30); + +log("Creating hills..."); +createAreas( + new ClumpPlacer(scaleByMapSize(20, 150), 0.2, 0.1, 1), + [ + new LayeredPainter([tHillDark, tHillDark, tHillDark], [2, 2]), + new SmoothElevationPainter(ELEVATION_SET, 18, 2), + paintClass(clHill) + ], + avoidClasses(clPlayer, 0, clHill, 15, clWater, 2, clBaseResource, 2), + scaleByMapSize(2, 8) * numPlayers); +RMS.SetProgress(35); + +log("Creating forests..."); +var types = [ + [[tGrassB, tGrassA, pForestD], [tGrassB, pForestD]], + [[tGrassB, tGrassA, pForestP], [tGrassB, pForestP]] +]; +var size = numForest / (scaleByMapSize(4, 12) * numPlayers); +var num = Math.floor(size / types.length); +for (let i = 0; i < types.length; ++i) + createAreas( + new ClumpPlacer(numForest / num, 0.1, 0.1, 1), + [ + new LayeredPainter(types[i], [2]), + paintClass(clForest) + ], + avoidClasses( + clPlayer, 4, + clForest, 10, + clHill, 0, + clWater, 2), + num); +RMS.SetProgress(40); + +log("Creating hill patches..."); +for (let size of [scaleByMapSize(3, 48), scaleByMapSize(5, 84), scaleByMapSize(8, 128)]) + for (let type of [[tHillMedium1, tHillDark], [tHillDark, tHillMedium2], [tHillMedium1, tHillMedium2]]) + createAreas( + new ClumpPlacer(size, 0.3, 0.06, 0.5), + [ + new LayeredPainter(type, [1]), + paintClass(clGrass) + ], + avoidClasses( + clWater, 3, + clForest, 0, + clHill, 0, + clBumps, 0, + clPlayer, 0), + scaleByMapSize(20, 80)); +RMS.SetProgress(45); + +log("Creating grass patches..."); +createLayeredPatches( + [scaleByMapSize(2, 4), scaleByMapSize(3, 7), scaleByMapSize(5, 15)], + [tGrassPatchBlend, tGrassPatch], + [1], + avoidClasses( + clWater, 1, + clForest, 0, + clHill, 0, + clGrass, 5, + clBumps, 0, + clPlayer, 0), + clDirt); +RMS.SetProgress(50); + +log("Creating stone mines..."); +createObjectGroups( + new SimpleGroup( + [ + new SimpleObject(oStoneSmall, 0, 2, 0, 4), + new SimpleObject(oStoneLarge, 1, 1, 0, 4) + ], + true, + clRock), + 0, + [ + stayClasses(clBumps, 1), + avoidClasses( + clWater, 3, + clForest, 1, + clPlayer, 0, + clRock, 15, + clHill, 0) + ], + 100, + 100); +RMS.SetProgress(55); + +log("Creating small stone quarries..."); +createObjectGroups( + new SimpleGroup([new SimpleObject(oStoneSmall, 2, 5, 1, 3)], true, clRock), + 0, + [ + stayClasses(clBumps, 1), + avoidClasses( + clWater, 3, + clForest, 1, + clPlayer, 0, + clRock, 15, + clHill, 0) + ], + 100, + 100); +RMS.SetProgress(60); + +log("Creating metal mines..."); +createObjectGroups( + new SimpleGroup([new SimpleObject(oMetalLarge, 1, 1, 0, 4)], true, clMetal), + 0, + [ + stayClasses(clBumps, 1), + avoidClasses( + clWater, 3, + clForest, 1, + clPlayer, 0, + clMetal, 15, + clRock, 10, + clHill, 0) + ], + 100, + 100); +RMS.SetProgress(65); + +log("Creating towers..."); +createObjectGroups( + new SimpleGroup([new SimpleObject(oTower, 1, 1, 0, 4)], true, clTower), + 0, + [ + stayClasses(clBumps, 3), + avoidClasses( + clMetal, 5, + clRock, 5, + clHill, 0, + clTower, 60, + clPlayer, 10, + clForest, 2) + ], + 500, + 1); +RMS.SetProgress(67); + +createDecoration( + [ + [new SimpleObject(aGrassShort, 1, 2, 0, 1)], + [ + new SimpleObject(aGrass, 2, 4, 0, 1.8), + new SimpleObject(aGrassShort, 3, 6, 1.2, 2.5) + ], + [ + new SimpleObject(aBushMedium, 1, 2, 0, 2), + new SimpleObject(aBushSmall, 2, 4, 0, 2) + ] + ], + [ + scaleByMapSize(15, 200), + scaleByMapSize(15, 200), + scaleByMapSize(15, 200) + ], + [ + stayClasses(clGrass, 0), + avoidClasses( + clWater, 0, + clForest, 0, + clPlayer, 0, + clHill, 0) + ]); +RMS.SetProgress(70); + +createDecoration( + [ + [ + new SimpleObject(aRockMedium, 1, 3, 0, 1) + ], + [ + new SimpleObject(aRockLarge, 1, 2, 0, 1), + new SimpleObject(aRockMedium, 1, 3, 0, 2) + ] + ], + [ + scaleByMapSize(15, 250), + scaleByMapSize(15, 150) + ], + avoidClasses( + clWater, 0, + clForest, 0, + clPlayer, 0, + clHill, 0 + )); +RMS.SetProgress(75); + +createFood( + [ + [new SimpleObject(oRabbit, 5, 7, 2, 4)], + [new SimpleObject(oGoat, 3, 5, 2, 4)] + ], + [ + scaleByMapSize(1, 6) * numPlayers, + scaleByMapSize(3, 10) * numPlayers + ], + [ + avoidClasses( + clWater, 1, + clForest, 0, + clPlayer, 0, + clHill, 1, + clFood, 20) + ], + clFood); +RMS.SetProgress(78); + +createFood( + [ + [new SimpleObject(oBear, 1, 1, 0, 2)] + ], + [ + 3 * numPlayers + ], + [ + avoidClasses( + clWater, 1, + clForest, 0, + clPlayer, 0, + clHill, 1, + clFood, 20 + ), + stayClasses(clForest, 2) + ], + clFood); +RMS.SetProgress(81); + +createFood( + [ + [new SimpleObject(oFruitBush, 1, 2, 0, 4)] + ], + [ + 3 * numPlayers + ], + [stayClasses(clGrass, 1), avoidClasses(clWater, 1, clForest, 0, clPlayer, 0, clHill, 1, clFood, 10)], + clFood); +RMS.SetProgress(85); + +log("Creating straggler trees and bushes..."); +var types = [oTree, oTree2, oTree3, oTree4, oBush]; +var num = Math.floor(numStragglers / types.length); +for (let type of types) + createObjectGroups( + new SimpleGroup( + [new SimpleObject(type, 1, 1, 0, 3)], + true, + clForest), + 0, + [ + stayClasses(clGrass, 1), + avoidClasses( + clWater, 5, + clForest, 1, + clHill, 1, + clPlayer, 0, + clMetal, 1, + clRock, 1) + ], + num); + +RMS.SetProgress(90); + +log("Creating straggler bushes..."); +createObjectGroups( + new SimpleGroup( + [new SimpleObject(oBush, 1, 3, 0, 3)], + true, + clForest + ), + 0, + [ + stayClasses(clGrass, 3), + avoidClasses( + clWater, 1, + clForest, 1, + clPlayer, 0, + clMetal, 1, + clRock, 1) + ], + numStragglers); +RMS.SetProgress(95); + +setWaterType("lake"); +setWaterWaviness(2); +setWaterColor(0.1, 0.13, 0.15); +setWaterTint(0.058, 0.05, 0.035); +setWaterMurkiness(0.9); + +setPPEffect("hdr"); + +ExportMap(); Property changes on: ps/trunk/binaries/data/mods/public/maps/random/extinct_volcano.js ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/maps/random/extinct_volcano.json =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/extinct_volcano.json (nonexistent) +++ ps/trunk/binaries/data/mods/public/maps/random/extinct_volcano.json (revision 19635) @@ -0,0 +1,19 @@ +{ + "settings" : { + "Name" : "Extinct Volcano", + "Script" : "extinct_volcano.js", + "Description" : "[color=\"red\"]IMPORTANT NOTE: AI PLAYERS DO NOT WORK WITH THIS MAP[/color]\n\nA once fertile valley... desolated by the eruption of the long-dormant volcano in the heart of the region. Following years of empty, scorched deadness, signs of life started reappearing and spreading. Now the land is half-way to the full lushness of its former era. Alas, it is not to be: following a long stretch of drought, interminable rains have set in in the higher regions to the north. Water levels are rising at drastic levels, slowly forcing players to seek the high ground of the lesser, extinct volcanoes or the now again dormant great cone.", + "DisabledTemplates": [ + "structures/ptol_lighthouse" + ], + "BaseTerrain" : "ocean_rock_a", + "BaseHeight" : 1, + "Keywords": ["new", "trigger"], + "CircularMap" : true, + "Preview" : "extinctvolcano.png", + "TriggerScripts": [ + "scripts/TriggerHelper.js", + "random/extinct_volcano_triggers.js" + ] + } +} Property changes on: ps/trunk/binaries/data/mods/public/maps/random/extinct_volcano.json ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/maps/random/extinct_volcano_triggers.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/extinct_volcano_triggers.js (nonexistent) +++ ps/trunk/binaries/data/mods/public/maps/random/extinct_volcano_triggers.js (revision 19635) @@ -0,0 +1,200 @@ +/** + * Whether to log the water levels and which units became killed or transformed to visual actors. + */ +var debugLog = false; + +/** + * Whether to rise the water to the maximum level in a minute or two. + */ +var debugWaterRise = false; + +/** + * Time in minutes when the water level starts to rise. + * Allow players to build up the economy and military for some time. + */ +var waterRiseStartTime = [22, 26]; + +/** + * Duration in minutes for which the notification will be shown that states that the water will rise soon. + */ +var waterRiseNotificationDuration = 1; + +/** + * Time in minutes between increases of the water level. + * If the water rises too fast, the hills are of no strategic importance, + * building structures would be pointless. + * + * At height 27, most trees are not gatherable anymore and enemies not reachable. + * At height 37 most hills are barely usable. + * + * At min 30 stuff at the ground level should not be gatherable anymore. + * At min 45 CC should be destroyed. + * + * Notice regular and military docks will raise with the water! + */ +var waterIncreaseTime = [0.5, 1]; + +/** + * Number of meters the waterheight increases each step. + * Each time the water level is changed, the pathfinder grids have to be recomputed. + * Therefore raising the level should occur as rarely as possible, i.e. have the value + * as big as possible, but as small as needed to keep it visually authentic. + */ +var waterLevelIncreaseHeight = 1; + +/** + * At which height to stop increasing the water level. + * Since players can survive on ships, don't endlessly raise the water. + */ +var maxWaterLevel = 70; + +/** + * Let buildings, relics and siege engines become actors, but kill organic units. + */ +var drownClass = "Organic"; + +/** + * Maximum height that units and structures can be submerged before drowning or becoming destructed. + */ +var drownHeight = 1; + +/** + * One of these warnings is printed some minutes before the water level starts to rise. + */ +var waterWarningTexts = [ + markForTranslation("It keeps on raining, we will have to evacuate soon!"), + markForTranslation("The rivers are standing high, we need to find a save place!"), + markForTranslation("We have to find dry ground, our lands will drawn soon!"), + markForTranslation("The lakes start swallowing the land, we have to find shelter!") +]; + +/** + * Units to be garrisoned in the wooden towers. + */ +var garrisonedUnits = "units/rome_legionnaire_marian"; + +Trigger.prototype.RaisingWaterNotification = function() +{ + Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface).AddTimeNotification({ + "message": pickRandom(waterWarningTexts), + "translateMessage": true + }, waterRiseNotificationDuration * 60 * 1000); +}; + +Trigger.prototype.DebugLog = function(txt) +{ + if (!debugLog) + return; + + let time = Math.round(Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer).GetTime() / 60 / 1000); + print("DEBUG [" + time + "] " + txt + "\n"); +}; + + +Trigger.prototype.GarrisonWoodenTowers = function() +{ + for (let gaiaEnt of Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager).GetEntitiesByPlayer(0)) + { + let cmpIdentity = Engine.QueryInterface(gaiaEnt, IID_Identity); + if (!cmpIdentity || !cmpIdentity.HasClass("DefenseTower")) + continue; + + let cmpGarrisonHolder = Engine.QueryInterface(gaiaEnt, IID_GarrisonHolder); + if (!cmpGarrisonHolder) + continue; + + for (let newEnt of TriggerHelper.SpawnUnits(gaiaEnt, garrisonedUnits, cmpGarrisonHolder.GetCapacity(), 0)) + if (Engine.QueryInterface(gaiaEnt, IID_GarrisonHolder).Garrison(newEnt)) + Engine.QueryInterface(newEnt, IID_UnitAI).Autogarrison(gaiaEnt); + } +}; + +Trigger.prototype.RaiseWaterLevelStep = function() +{ + let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); + let time = cmpTimer.GetTime(); + let cmpWaterManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_WaterManager); + let newLevel = cmpWaterManager.GetWaterLevel() + waterLevelIncreaseHeight; + cmpWaterManager.SetWaterLevel(newLevel); + this.DebugLog("Raising water level to " + Math.round(newLevel) + " took " + (cmpTimer.GetTime() - time)); + + if (newLevel < maxWaterLevel) + this.DoAfterDelay((debugWaterRise ? 10 : randFloat(...waterIncreaseTime) * 60) * 1000, "RaiseWaterLevelStep", {}); + else + this.DebugLog("Water reached final level"); + + let actorTemplates = {}; + let killedTemplates = {}; + + let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); + let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); + + for (let ent of [...cmpRangeManager.GetEntitiesByPlayer(0), ...cmpRangeManager.GetNonGaiaEntities()]) + { + let cmpPosition = Engine.QueryInterface(ent, IID_Position); + if (!cmpPosition || !cmpPosition.IsInWorld()) + continue; + + let pos = cmpPosition.GetPosition(); + if (pos.y + drownHeight >= newLevel) + continue; + + let cmpIdentity = Engine.QueryInterface(ent, IID_Identity); + if (!cmpIdentity) + continue; + + let templateName = cmpTemplateManager.GetCurrentTemplateName(ent); + + // Animals and units drown + let cmpHealth = Engine.QueryInterface(ent, IID_Health); + if (cmpHealth && cmpIdentity.HasClass(drownClass)) + { + cmpHealth.Kill(); + + if (debugLog) + killedTemplates[templateName] = (killedTemplates[templateName] || 0) + 1; + + continue; + } + + // Resources and buildings become actors + // Do not use ChangeEntityTemplate for performance and + // because we don't need nor want the effects of MT_EntityRenamed + + let cmpVisualActor = Engine.QueryInterface(ent, IID_Visual); + if (!cmpVisualActor) + continue; + + let height = cmpPosition.GetHeightOffset(); + let rot = cmpPosition.GetRotation(); + + let actorTemplate = cmpTemplateManager.GetTemplate(templateName).VisualActor.Actor; + let seed = cmpVisualActor.GetActorSeed(); + Engine.DestroyEntity(ent); + + let newEnt = Engine.AddEntity("actor|" + actorTemplate); + Engine.QueryInterface(newEnt, IID_Visual).SetActorSeed(seed); + + let cmpNewPos = Engine.QueryInterface(newEnt, IID_Position); + cmpNewPos.JumpTo(pos.x, pos.z); + cmpNewPos.SetHeightOffset(height); + cmpNewPos.SetXZRotation(rot.x, rot.z); + cmpNewPos.SetYRotation(rot.y); + + if (debugLog) + actorTemplates[templateName] = (actorTemplates[templateName] || 0) + 1; + } + + this.DebugLog("Checking entities took " + (cmpTimer.GetTime() - time)); + this.DebugLog("Killed: " + uneval(killedTemplates)); + this.DebugLog("Converted to actors: " + uneval(actorTemplates)); +}; + + +{ + let waterRiseTime = debugWaterRise ? 0 : randFloat(...waterRiseStartTime); + let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); + cmpTrigger.GarrisonWoodenTowers(); + cmpTrigger.DoAfterDelay((waterRiseTime - waterRiseNotificationDuration) * 60 * 1000, "RaisingWaterNotification", {}); + cmpTrigger.DoAfterDelay(waterRiseTime * 60 * 1000, "RaiseWaterLevelStep", {}); +} Property changes on: ps/trunk/binaries/data/mods/public/maps/random/extinct_volcano_triggers.js ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/maps/random/rmgen/misc.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/rmgen/misc.js (revision 19634) +++ ps/trunk/binaries/data/mods/public/maps/random/rmgen/misc.js (revision 19635) @@ -1,804 +1,886 @@ ///////////////////////////////////////////////////////////////////////////////////////// // passageMaker // // Function for creating shallow water between two given points by changing the heiight of all tiles in // the path with height less than or equal to "maxheight" to "height" // // x1,z1: Starting point of path // x2,z2: Ending point of path // width: Width of the shallow // maxheight: Maximum height that it changes // height: Height of the shallow // smooth: smooth elevation in borders // tileclass: (Optianal) - Adds those tiles to the class given // terrain: (Optional) - Changes the texture of the elevated land // ///////////////////////////////////////////////////////////////////////////////////////// function passageMaker(x1, z1, x2, z2, width, maxheight, height, smooth, tileclass, terrain, riverheight) { var tchm = TILE_CENTERED_HEIGHT_MAP; TILE_CENTERED_HEIGHT_MAP = true; var mapSize = g_Map.size; for (var ix = 0; ix < mapSize; ix++) { for (var iz = 0; iz < mapSize; iz++) { var a = z1-z2; var b = x2-x1; var c = (z1*(x1-x2))-(x1*(z1-z2)); var dis = abs(a*ix + b*iz + c)/sqrt(a*a + b*b); var k = (a*ix + b*iz + c)/(a*a + b*b); var my = iz-(b*k); var inline = 0; if (b == 0) { dis = abs(ix-x1); if ((iz <= Math.max(z1,z2))&&(iz >= Math.min(z1,z2))) { inline = 1; } } else { if ((my <= Math.max(z1,z2))&&(my >= Math.min(z1,z2))) { inline = 1; } } if ((dis <= width)&&(inline)) { if(g_Map.getHeight(ix, iz) <= maxheight) { if (dis > width - smooth) { g_Map.setHeight(ix, iz, ((width - dis)*(height)+(riverheight)*(smooth - width + dis))/(smooth)); } else if (dis <= width - smooth) { g_Map.setHeight(ix, iz, height); } if (tileclass !== undefined) { addToClass(ix, iz, tileclass); } if (terrain !== undefined) { placeTerrain(ix, iz, terrain); } } } } } TILE_CENTERED_HEIGHT_MAP = tchm; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //rndRiver is a fuction that creates random values useful for making a jagged river. // //it works the same as sin or cos function. the only difference is that it's period is 1 instead of 2*pi //it needs the "seed" parameter to use it to make random curves that don't get broken. //seed must be created using randFloat(). or else it won't work // // f: Input: Same as angle in a sine function // seed: Random Seed: Best to implement is to use randFloat() // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function rndRiver(f, seed) { var rndRq = seed; var rndRw = rndRq; var rndRe = 0; var rndRr = f-floor(f); var rndRa = 0; for (var rndRx=0; rndRx<=floor(f); rndRx++) { rndRw = 10*(rndRw-floor(rndRw)); } if (rndRx%2==0) { var rndRs = -1; } else { var rndRs = 1; } rndRe = (floor(rndRw))%5; if (rndRe==0) { rndRa = (rndRs)*2.3*(rndRr)*(rndRr-1)*(rndRr-0.5)*(rndRr-0.5); } else if (rndRe==1) { rndRa = (rndRs)*2.6*(rndRr)*(rndRr-1)*(rndRr-0.3)*(rndRr-0.7); } else if (rndRe==2) { rndRa = (rndRs)*22*(rndRr)*(rndRr-1)*(rndRr-0.2)*(rndRr-0.3)*(rndRr-0.3)*(rndRr-0.8); } else if (rndRe==3) { rndRa = (rndRs)*180*(rndRr)*(rndRr-1)*(rndRr-0.2)*(rndRr-0.2)*(rndRr-0.4)*(rndRr-0.6)*(rndRr-0.6)*(rndRr-0.8); } else if (rndRe==4) { rndRa = (rndRs)*2.6*(rndRr)*(rndRr-1)*(rndRr-0.5)*(rndRr-0.7); } return rndRa; } ///////////////////////////////////////////////////////////////////////////////////////// // createStartingPlayerEntities // // Creates the starting player entities // fx&fz: position of player base // playerid: id of player // civEntities: use getStartingEntities(id-1) fo this one // orientation: orientation of the main base building, default is BUILDING_ORIENTATION // /////////////////////////////////////////////////////////////////////////////////////////// function createStartingPlayerEntities(fx, fz, playerid, civEntities, orientation = BUILDING_ORIENTATION) { var uDist = 6; var uSpace = 2; placeObject(fx, fz, civEntities[0].Template, playerid, orientation); for (var j = 1; j < civEntities.length; ++j) { var uAngle = orientation - PI * (2-j) / 2; var count = (civEntities[j].Count !== undefined ? civEntities[j].Count : 1); for (var numberofentities = 0; numberofentities < count; numberofentities++) { var ux = fx + uDist * cos(uAngle) + numberofentities * uSpace * cos(uAngle + PI/2) - (0.75 * uSpace * floor(count / 2) * cos(uAngle + PI/2)); var uz = fz + uDist * sin(uAngle) + numberofentities * uSpace * sin(uAngle + PI/2) - (0.75 * uSpace * floor(count / 2) * sin(uAngle + PI/2)); placeObject(ux, uz, civEntities[j].Template, playerid, uAngle); } } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // placeCivDefaultEntities // // Creates the default starting player entities depending on the players civ // fx&fy: position of player base // playerid: id of player // kwargs: Takes some optional keyword arguments to tweek things // 'iberWall': may be false, 'walls' (default) or 'towers'. Determines the defensive structures Iberians get as civ bonus // 'orientation': angle of the main base building, default is BUILDING_ORIENTATION // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function placeCivDefaultEntities(fx, fz, playerid, kwargs = {}) { // Unpack kwargs var iberWall = 'walls'; if (getMapSize() <= 128) iberWall = false; if ('iberWall' in kwargs) iberWall = kwargs['iberWall']; var orientation = BUILDING_ORIENTATION; if ('orientation' in kwargs) orientation = kwargs['orientation']; // Place default civ starting entities var civ = getCivCode(playerid-1); var civEntities = getStartingEntities(playerid-1); var uDist = 6; var uSpace = 2; placeObject(fx, fz, civEntities[0].Template, playerid, orientation); for (var j = 1; j < civEntities.length; ++j) { var uAngle = orientation - PI * (2-j) / 2; var count = (civEntities[j].Count !== undefined ? civEntities[j].Count : 1); for (var numberofentities = 0; numberofentities < count; numberofentities++) { var ux = fx + uDist * cos(uAngle) + numberofentities * uSpace * cos(uAngle + PI/2) - (0.75 * uSpace * floor(count / 2) * cos(uAngle + PI/2)); var uz = fz + uDist * sin(uAngle) + numberofentities * uSpace * sin(uAngle + PI/2) - (0.75 * uSpace * floor(count / 2) * sin(uAngle + PI/2)); placeObject(ux, uz, civEntities[j].Template, playerid, uAngle); } } // Add defensive structiures for Iberians as their civ bonus if (civ == 'iber' && iberWall != false) { if (iberWall == 'towers') placePolygonalWall(fx, fz, 15, ['entry'], 'tower', civ, playerid, orientation, 7); else placeGenericFortress(fx, fz, 20/*radius*/, playerid); } } function placeDefaultChicken(playerX, playerZ, tileClass, constraint = undefined, template = "gaia/fauna_chicken") { for (let j = 0; j < 2; ++j) for (var tries = 0; tries < 10; ++tries) { let aAngle = randFloat(0, TWO_PI); // Roman and ptolemian civic centers have a big footprint! let aDist = 9; let aX = round(playerX + aDist * cos(aAngle)); let aZ = round(playerZ + aDist * sin(aAngle)); let group = new SimpleGroup( [new SimpleObject(template, 5,5, 0,2)], true, tileClass, aX, aZ ); if (createObjectGroup(group, 0, constraint)) break; } } /** * Typically used for placing grass tufts around the civic centers. */ function placeDefaultDecoratives(playerX, playerZ, template, tileclass, radius, constraint = undefined) { for (let i = 0; i < PI * radius * radius / 250; ++i) { let angle = randFloat(0, 2 * PI); let dist = radius - randIntInclusive(5, 11); createObjectGroup( new SimpleGroup( [new SimpleObject(template, 2, 5, 0, 1, -PI/8, PI/8)], false, tileclass, Math.round(playerX + dist * Math.cos(angle)), Math.round(playerZ + dist * Math.sin(angle)) ), 0, constraint); } } ///////////////////////////////////////////////////////////////////////////////////////// // paintTerrainBasedOnHeight // // paints the tiles which have a height between minheight and maxheight with the given terrain // minheight: minimum height of the tile // maxheight: maximum height of the tile // mode: accepts 4 values. 0 means the it will select tiles with height more than minheight and less than maxheight. // 1 means it selects tiles with height more than or equal to minheight and less than max height. 2 means more than // minheight and less than or equal to maxheight. 3 means more than or equal to minheight and less than or equal to maxheight // terrain: intended terrain texture // /////////////////////////////////////////////////////////////////////////////////////////// function paintTerrainBasedOnHeight(minheight, maxheight, mode, terrain) { var mSize = g_Map.size; for (var qx = 0; qx < mSize; qx++) { for (var qz = 0; qz < mSize; qz++) { if (mode == 0) { if ((g_Map.getHeight(qx, qz) > minheight)&&(g_Map.getHeight(qx, qz) < maxheight)) { placeTerrain(qx, qz, terrain); } } else if (mode == 1) { if ((g_Map.getHeight(qx, qz) >= minheight)&&(g_Map.getHeight(qx, qz) < maxheight)) { placeTerrain(qx, qz, terrain); } } else if (mode == 2) { if ((g_Map.getHeight(qx, qz) > minheight)&&(g_Map.getHeight(qx, qz) <= maxheight)) { placeTerrain(qx, qz, terrain); } } else if (mode == 3) { if ((g_Map.getHeight(qx, qz) >= minheight)&&(g_Map.getHeight(qx, qz) <= maxheight)) { placeTerrain(qx, qz, terrain); } } } } } ///////////////////////////////////////////////////////////////////////////////////////// // paintTileClassBasedOnHeight and unPaintTileClassBasedOnHeight // // paints or unpaints the tiles which have a height between minheight and maxheight with the given tile class // minheight: minimum height of the tile // maxheight: maximum height of the tile // mode: accepts 4 values. 0 means the it will select tiles with height more than minheight and less than maxheight. // 1 means it selects tiles with height more than or equal to minheight and less than max height. 2 means more than // minheight and less than or equal to maxheight. 3 means more than or equal to minheight and less than or equal to maxheight // tileclass: intended tile class // /////////////////////////////////////////////////////////////////////////////////////////// function paintTileClassBasedOnHeight(minheight, maxheight, mode, tileclass) { var mSize = g_Map.size; for (var qx = 0; qx < mSize; qx++) { for (var qz = 0; qz < mSize; qz++) { if (mode == 0) { if ((g_Map.getHeight(qx, qz) > minheight)&&(g_Map.getHeight(qx, qz) < maxheight)) { addToClass(qx, qz, tileclass); } } else if (mode == 1) { if ((g_Map.getHeight(qx, qz) >= minheight)&&(g_Map.getHeight(qx, qz) < maxheight)) { addToClass(qx, qz, tileclass); } } else if (mode == 2) { if ((g_Map.getHeight(qx, qz) > minheight)&&(g_Map.getHeight(qx, qz) <= maxheight)) { addToClass(qx, qz, tileclass); } } else if (mode == 3) { if ((g_Map.getHeight(qx, qz) >= minheight)&&(g_Map.getHeight(qx, qz) <= maxheight)) { addToClass(qx, qz, tileclass); } } } } } function unPaintTileClassBasedOnHeight(minheight, maxheight, mode, tileclass) { var mSize = g_Map.size; for (var qx = 0; qx < mSize; qx++) { for (var qz = 0; qz < mSize; qz++) { if (mode == 0) { if ((g_Map.getHeight(qx, qz) > minheight)&&(g_Map.getHeight(qx, qz) < maxheight)) { removeFromClass(qx, qz, tileclass); } } else if (mode == 1) { if ((g_Map.getHeight(qx, qz) >= minheight)&&(g_Map.getHeight(qx, qz) < maxheight)) { removeFromClass(qx, qz, tileclass); } } else if (mode == 2) { if ((g_Map.getHeight(qx, qz) > minheight)&&(g_Map.getHeight(qx, qz) <= maxheight)) { removeFromClass(qx, qz, tileclass); } } else if (mode == 3) { if ((g_Map.getHeight(qx, qz) >= minheight)&&(g_Map.getHeight(qx, qz) <= maxheight)) { removeFromClass(qx, qz, tileclass); } } } } } ///////////////////////////////////////////////////////////////////////////////////////// // getTIPIADBON // // "get The Intended Point In A Direction Based On Height" // gets the N'th point with a specific height in a line and returns it as a [x, y] array // startPoint: [x, y] array defining the start point // endPoint: [x, y] array defining the ending point // heightRange: [min, max] array defining the range which the height of the intended point can be. includes both "min" and "max" // step: how much tile units per turn should the search go. more value means faster but less accurate // n: how many points to skip before ending the search. skips """n-1 points""". // /////////////////////////////////////////////////////////////////////////////////////////// function getTIPIADBON(startPoint, endPoint, heightRange, step, n) { var stepX = step*(endPoint[0]-startPoint[0])/(sqrt((endPoint[0]-startPoint[0])*(endPoint[0]-startPoint[0]) + step*(endPoint[1]-startPoint[1])*(endPoint[1]-startPoint[1]))); var stepY = step*(endPoint[1]-startPoint[1])/(sqrt((endPoint[0]-startPoint[0])*(endPoint[0]-startPoint[0]) + step*(endPoint[1]-startPoint[1])*(endPoint[1]-startPoint[1]))); var y = startPoint[1]; var checked = 0; for (var x = startPoint[0]; true; x += stepX) { if ((floor(x) < g_Map.size)||(floor(y) < g_Map.size)) { if ((g_Map.getHeight(floor(x), floor(y)) <= heightRange[1])&&(g_Map.getHeight(floor(x), floor(y)) >= heightRange[0])) { ++checked; } if (checked >= n) { return [x, y]; } } y += stepY; if ((y > endPoint[1])&&(stepY>0)) break; if ((y < endPoint[1])&&(stepY<0)) break; if ((x > endPoint[1])&&(stepX>0)) break; if ((x < endPoint[1])&&(stepX<0)) break; } return undefined; } ///////////////////////////////////////////////////////////////////////////////////////// // doIntersect // // determines if two lines with the width "width" intersect or collide with each other // x1, y1, x2, y2: determine the position of the first line // x3, y3, x4, y4: determine the position of the second line // width: determines the width of the lines // /////////////////////////////////////////////////////////////////////////////////////////// function checkIfIntersect (x1, y1, x2, y2, x3, y3, x4, y4, width) { if (x1 == x2) { if (((x3 - x1) < width) || ((x4 - x2) < width)) return true; } else { var m = (y1 - y2) / (x1 - x2); var b = y1 - m * x1; var m2 = sqrt(m * m + 1); if ((Math.abs((y3 - x3 * m - b)/m2) < width) || (Math.abs((y4 - x4 * m - b)/m2) < width)) return true; //neccessary for some situations. if (x3 == x4) { if (((x1 - x3) < width) || ((x2 - x4) < width)) return true; } else { var m = (y3 - y4) / (x3 - x4); var b = y3 - m * x3; var m2 = sqrt(m * m + 1); if ((Math.abs((y1 - x1 * m - b)/m2) < width) || (Math.abs((y2 - x2 * m - b)/m2) < width)) return true; } } var s = ((x1 - x2) * (y3 - y1) - (y1 - y2) * (x3 - x1)), p = ((x1 - x2) * (y4 - y1) - (y1 - y2) * (x4 - x1)); if ((s * p) <= 0) { s = ((x3 - x4) * (y1 - y3) - (y3 - y4) * (x1 - x3)); p = ((x3 - x4) * (y2 - y3) - (y3 - y4) * (x2 - x3)); if ((s * p) <= 0) return true; } return false; } ///////////////////////////////////////////////////////////////////////////////////////// // distanceOfPointFromLine // // returns the distance of a point from a line // x1, y1, x2, y2: determine the position of the line // x3, y3: determine the position of the point // /////////////////////////////////////////////////////////////////////////////////////////// function distanceOfPointFromLine (x1, y1, x2, y2, x3, y3) { if (x1 == x2) { return Math.abs(x3 - x1); } else if (y1 == y2) { return Math.abs(y3 - y1); } else { var m = (y1 - y2) / (x1 - x2); var b = y1 - m * x1; var m2 = sqrt(m * m + 1); return Math.abs((y3 - x3 * m - b)/m2); } } ///////////////////////////////////////////////////////////////////////////////////////// // createRamp // // creates a ramp from point (x1, y1) to (x2, y2). // x1, y1, x2, y2: determine the position of the start and end of the ramp // minHeight, maxHeight: determine the height levels of the start and end point // width: determines the width of the ramp // smoothLevel: determines the smooth level around the edges of the ramp // mainTerrain: (Optional) determines the terrain texture for the ramp // edgeTerrain: (Optional) determines the terrain texture for the edges // tileclass: (Optional) adds the ramp to this tile class // /////////////////////////////////////////////////////////////////////////////////////////// function createRamp (x1, y1, x2, y2, minHeight, maxHeight, width, smoothLevel, mainTerrain, edgeTerrain, tileclass) { var halfWidth = width / 2; var mapSize = g_Map.size; if (y1 == y2) { var x3 = x2; var y3 = y2 + halfWidth; } else { var m = (x1 - x2) / (y1 - y2); var b = y2 + m * x2; var x3 = x2 + halfWidth; var y3 = - m * x3 + b; } var minBoundX = (x1 <= x2 ? (x1 > halfWidth ? x1 - halfWidth : 0) : (x2 > halfWidth ? x2 - halfWidth : 0)); var maxBoundX = (x1 >= x2 ? (x1 < mapSize - halfWidth ? x1 + halfWidth : mapSize) : (x2 < mapSize - halfWidth ? x2 + halfWidth : mapSize)); var minBoundY = (y1 <= y2 ? (y1 > halfWidth ? y1 - halfWidth : 0) : (y2 > halfWidth ? y2 - halfWidth : 0)); var maxBoundY = (y1 >= y2 ? (x1 < mapSize - halfWidth ? y1 + halfWidth : mapSize) : (y2 < mapSize - halfWidth ? y2 + halfWidth : mapSize)); for (var x = minBoundX; x < maxBoundX; ++x) { for (var y = minBoundY; y < maxBoundY; ++y) { var lDist = distanceOfPointFromLine(x3, y3, x2, y2, x, y); var sDist = distanceOfPointFromLine(x1, y1, x2, y2, x, y); var rampLength = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); if (lDist <= rampLength && sDist <= halfWidth) { var h = ((rampLength - lDist) * maxHeight + lDist * minHeight) / rampLength; if (sDist >= halfWidth - smoothLevel) { h = (h - minHeight) * (halfWidth - sDist) / smoothLevel + minHeight; if (edgeTerrain !== undefined) placeTerrain(x, y, edgeTerrain); } else { if (mainTerrain !== undefined) placeTerrain(x, y, mainTerrain); } if (tileclass !== undefined) addToClass(x, y, tileclass); if((g_Map.getHeight(floor(x), floor(y)) < h) && (h <= maxHeight)) g_Map.setHeight(x, y, h); } } } } ///////////////////////////////////////////////////////////////////////////////////////// // createMountain // // creates a mountain using a tecnique very similar to chain placer. // /////////////////////////////////////////////////////////////////////////////////////////// function createMountain(maxHeight, minRadius, maxRadius, numCircles, constraint, x, z, terrain, tileclass, fcc, q) { fcc = (fcc !== undefined ? fcc : 0); q = (q !== undefined ? q : []); // checking for an array of constraints if (constraint instanceof Array) { var constraintArray = constraint; constraint = new AndConstraint(constraintArray); } // Preliminary bounds check if (!g_Map.inMapBounds(x, z) || !constraint.allows(x, z)) { return; } var size = getMapSize(); var queueEmpty = (q.length ? false : true); var gotRet = new Array(size); for (var i = 0; i < size; ++i) { gotRet[i] = new Array(size); for (var j = 0; j < size; ++j) { gotRet[i][j] = -1; } } --size; if (minRadius < 1) minRadius = 1; if (minRadius > maxRadius) minRadius = maxRadius; var edges = [[x, z]]; var circles = []; for (var i = 0; i < numCircles; ++i) { var badPoint = false; var [cx, cz] = pickRandom(edges); if (queueEmpty) var radius = randIntInclusive(minRadius, maxRadius); else { var radius = q.pop(); queueEmpty = (q.length ? false : true); } var sx = cx - radius, lx = cx + radius; var sz = cz - radius, lz = cz + radius; sx = (sx < 0 ? 0 : sx); sz = (sz < 0 ? 0 : sz); lx = (lx > size ? size : lx); lz = (lz > size ? size : lz); var radius2 = radius * radius; var dx, dz, distance2; //log (uneval([sx, sz, lx, lz])); for (var ix = sx; ix <= lx; ++ix) { for (var iz = sz; iz <= lz; ++ iz) { dx = ix - cx; dz = iz - cz; distance2 = dx * dx + dz * dz; if (dx * dx + dz * dz <= radius2) { if (g_Map.inMapBounds(ix, iz)) { if (!constraint.allows(ix, iz)) { badPoint = true; break; } var state = gotRet[ix][iz]; if (state == -1) { gotRet[ix][iz] = -2; } else if (state >= 0) { var s = edges.splice(state, 1); gotRet[ix][iz] = -2; var edgesLength = edges.length; for (var k = state; k < edges.length; ++k) { --gotRet[edges[k][0]][edges[k][1]]; } } } } } if (badPoint) break; } if (badPoint) continue; else circles.push([cx, cz, radius]); for (var ix = sx; ix <= lx; ++ix) { for (var iz = sz; iz <= lz; ++ iz) { if (fcc) if ((x - ix) > fcc || (ix - x) > fcc || (z - iz) > fcc || (iz - z) > fcc) continue; if (gotRet[ix][iz] == -2) { if (ix > 0) { if (gotRet[ix-1][iz] == -1) { edges.push([ix, iz]); gotRet[ix][iz] = edges.length - 1; continue; } } if (iz > 0) { if (gotRet[ix][iz-1] == -1) { edges.push([ix, iz]); gotRet[ix][iz] = edges.length - 1; continue; } } if (ix < size) { if (gotRet[ix+1][iz] == -1) { edges.push([ix, iz]); gotRet[ix][iz] = edges.length - 1; continue; } } if (iz < size) { if (gotRet[ix][iz+1] == -1) { edges.push([ix, iz]); gotRet[ix][iz] = edges.length - 1; continue; } } } } } } var numFinalCircles = circles.length; for (var i = 0; i < numFinalCircles; ++i) { var point = circles[i]; var cx = point[0], cz = point[1], radius = point[2]; var sx = cx - radius, lx = cx + radius; var sz = cz - radius, lz = cz + radius; sx = (sx < 0 ? 0 : sx); sz = (sz < 0 ? 0 : sz); lx = (lx > size ? size : lx); lz = (lz > size ? size : lz); var radius2 = radius * radius; var dx, dz, distance2; var clumpHeight = radius / maxRadius * maxHeight * randFloat(0.8, 1.2); for (var ix = sx; ix <= lx; ++ix) { for (var iz = sz; iz <= lz; ++ iz) { dx = ix - cx; dz = iz - cz; distance2 = dx * dx + dz * dz; var newHeight = Math.round((Math.sin(PI * (2 * ((radius - Math.sqrt(distance2)) / radius) / 3 - 1/6)) + 0.5) * 2/3 * clumpHeight) + randIntInclusive(0, 2); if (dx * dx + dz * dz <= radius2) { if (g_Map.getHeight(ix, iz) < newHeight) g_Map.setHeight(ix, iz, newHeight); else if (g_Map.getHeight(ix, iz) >= newHeight && g_Map.getHeight(ix, iz) < newHeight + 4) g_Map.setHeight(ix, iz, newHeight + 4); if (terrain !== undefined) placeTerrain(ix, iz, terrain); if (tileclass !== undefined) addToClass(ix, iz, tileclass); } } } } } + +/** + * Generates a volcano mountain. Smoke and lava are optional. + * + * @param {number} fx - Horizontal coordinate of the center. + * @param {number} fz - Horizontal coordinate of the center. + * @param {number} tileClass - Painted onto every tile that is occupied by the volcano. + * @param {string} terrainTexture - The texture painted onto the volcano hill. + * @param {array} lavaTextures - Three different textures for the interior, from the outside to the inside. + * @param {boolean} smoke - Whether to place smoke particles. + * @param {number} elevationType - Elevation painter type, ELEVATION_SET = absolute or ELEVATION_MODIFY = relative. + */ +function createVolcano(fx, fz, tileClass, terrainTexture, lavaTextures, smoke, elevationType) +{ + log("Creating volcano"); + + let ix = Math.round(fractionToTiles(fx)); + let iz = Math.round(fractionToTiles(fz)); + + let baseSize = mapArea / scaleByMapSize(1, 8); + + let coherence = 0.7; + let smoothness = 0.05; + let failFraction = 100; + let steepness = 3; + + let clLava = createTileClass(); + + let layers = [ + { + "clumps": 0.067, + "elevation": 15, + "tileClass": tileClass + }, + { + "clumps": 0.05, + "elevation": 25, + "tileClass": createTileClass() + }, + { + "clumps": 0.02, + "elevation": 45, + "tileClass": createTileClass() + }, + { + "clumps": 0.011, + "elevation": 62, + "tileClass": createTileClass() + }, + { + "clumps": 0.003, + "elevation": 42, + "tileClass": clLava, + "painter": lavaTextures && new LayeredPainter([terrainTexture, ...lavaTextures], [1, 1, 1]), + "steepness": 1 + } + ]; + + for (let i = 0; i < layers.length; ++i) + createArea( + new ClumpPlacer(baseSize * layers[i].clumps, coherence, smoothness, failFraction, ix, iz), + [ + layers[i].painter || new LayeredPainter([terrainTexture, terrainTexture], [3]), + new SmoothElevationPainter(elevationType, layers[i].elevation, layers[i].steepness || steepness), + paintClass(layers[i].tileClass) + ], + i == 0 ? null : stayClasses(layers[i - 1].tileClass, 1)); + + if (smoke) + { + let num = Math.floor(baseSize * 0.002); + createObjectGroup( + new SimpleGroup( + [new SimpleObject("actor|particle/smoke.xml", num, num, 0, 7)], + false, + clLava, + ix, + iz), + 0, + stayClasses(tileClass, 1)); + } +} Index: ps/trunk/binaries/data/mods/public/maps/random/volcanic_lands.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/volcanic_lands.js (revision 19634) +++ ps/trunk/binaries/data/mods/public/maps/random/volcanic_lands.js (revision 19635) @@ -1,377 +1,296 @@ RMS.LoadLibrary("rmgen"); var tGrass = ["cliff volcanic light", "ocean_rock_a", "ocean_rock_b"]; var tGrassA = "cliff volcanic light"; var tGrassB = "ocean_rock_a"; var tGrassC = "ocean_rock_b"; var tCliff = ["cliff volcanic coarse", "cave_walls"]; var tRoad = "road1"; var tRoadWild = "road1"; var tLava1 = "LavaTest05"; var tLava2 = "LavaTest04"; var tLava3 = "LavaTest03"; // gaia entities var oTree = "gaia/flora_tree_dead"; var oStoneLarge = "gaia/geology_stonemine_alpine_quarry"; var oStoneSmall = "gaia/geology_stone_alpine_a"; var oMetalLarge = "gaia/geology_metal_alpine_slabs"; // decorative props var aRockLarge = "actor|geology/stone_granite_med.xml"; var aRockMedium = "actor|geology/stone_granite_med.xml"; -var aSmoke = "actor|particle/smoke.xml"; var pForestD = [tGrassC + TERRAIN_SEPARATOR + oTree, tGrassC]; var pForestP = [tGrassB + TERRAIN_SEPARATOR + oTree, tGrassB]; log("Initializing map..."); InitMap(); var numPlayers = getNumPlayers(); var mapSize = getMapSize(); var mapArea = mapSize*mapSize; // create tile classes var clPlayer = createTileClass(); var clHill = createTileClass(); -var clHill2 = createTileClass(); -var clHill3 = createTileClass(); -var clHill4 = createTileClass(); var clForest = createTileClass(); var clDirt = createTileClass(); var clRock = createTileClass(); var clMetal = createTileClass(); var clBaseResource = createTileClass(); // randomize player order var playerIDs = []; for (var i = 0; i < numPlayers; i++) playerIDs.push(i+1); playerIDs = sortPlayers(playerIDs); // place players var playerX = new Array(numPlayers); var playerZ = new Array(numPlayers); var playerAngle = new Array(numPlayers); var startAngle = randFloat(0, TWO_PI); for (var i = 0; i < numPlayers; i++) { playerAngle[i] = startAngle + i*TWO_PI/numPlayers; playerX[i] = 0.5 + 0.35*cos(playerAngle[i]); playerZ[i] = 0.5 + 0.35*sin(playerAngle[i]); } for (var i = 0; i < numPlayers; i++) { var id = playerIDs[i]; log("Creating base for player " + id + "..."); var radius = scaleByMapSize(15,25); // get the x and z in tiles var fx = fractionToTiles(playerX[i]); var fz = fractionToTiles(playerZ[i]); var ix = round(fx); var iz = round(fz); addToClass(ix, iz, clPlayer); addToClass(ix+5, iz, clPlayer); addToClass(ix, iz+5, clPlayer); addToClass(ix-5, iz, clPlayer); addToClass(ix, iz-5, clPlayer); // create the city patch var cityRadius = radius/3; var placer = new ClumpPlacer(PI*cityRadius*cityRadius, 0.6, 0.3, 10, ix, iz); var painter = new LayeredPainter([tRoadWild, tRoad], [1]); createArea(placer, painter, null); // create starting units placeCivDefaultEntities(fx, fz, id); // create metal mine var bbAngle = randFloat(0, TWO_PI); var bbDist = 12; var mAngle = bbAngle; while(abs(mAngle - bbAngle) < PI/3) mAngle = randFloat(0, TWO_PI); var mDist = 12; var mX = round(fx + mDist * cos(mAngle)); var mZ = round(fz + mDist * sin(mAngle)); var group = new SimpleGroup( [new SimpleObject(oMetalLarge, 1,1, 0,0)], true, clBaseResource, mX, mZ ); createObjectGroup(group, 0); // create stone mines mAngle += randFloat(PI/8, PI/4); mX = round(fx + mDist * cos(mAngle)); mZ = round(fz + mDist * sin(mAngle)); group = new SimpleGroup( [new SimpleObject(oStoneLarge, 1,1, 0,2)], true, clBaseResource, mX, mZ ); createObjectGroup(group, 0); // create starting trees var hillSize = PI * radius * radius; var num = floor(hillSize / 60); var tries = 10; for (var x = 0; x < tries; ++x) { var tAngle = randFloat(-PI/3, 4*PI/3); var tDist = randFloat(12, 13); var tX = round(fx + tDist * cos(tAngle)); var tZ = round(fz + tDist * sin(tAngle)); group = new SimpleGroup( [new SimpleObject(oTree, num, num, 0, 3)], false, clBaseResource, tX, tZ ); if (createObjectGroup(group, 0, avoidClasses(clBaseResource, 2))) break; } } - RMS.SetProgress(15); -log("Creating volcano"); -var fx = fractionToTiles(0.5); -var fz = fractionToTiles(0.5); -var ix = round(fx); -var iz = round(fz); -var div = scaleByMapSize(1,8); -var placer = new ClumpPlacer(mapArea * 0.067 / div, 0.7, 0.05, 100, ix, iz); -var terrainPainter = new LayeredPainter( - [tCliff, tCliff], // terrains - [3] // widths -); -var elevationPainter = new SmoothElevationPainter( - ELEVATION_SET, // type - 15, // elevation - 3 // blend radius -); -createArea(placer, [terrainPainter, elevationPainter, paintClass(clHill)], null); - -var placer = new ClumpPlacer(mapArea * 0.05 / div, 0.7, 0.05, 100, ix, iz); -var terrainPainter = new LayeredPainter( - [tCliff, tCliff], // terrains - [3] // widths -); -var elevationPainter = new SmoothElevationPainter( - ELEVATION_SET, // type - 25, // elevation - 3 // blend radius -); -createArea(placer, [terrainPainter, elevationPainter, paintClass(clHill2)], stayClasses(clHill, 1)); - -var placer = new ClumpPlacer(mapArea * 0.02 / div, 0.7, 0.05, 100, ix, iz); -var terrainPainter = new LayeredPainter( - [tCliff, tCliff], // terrains - [3] // widths -); -var elevationPainter = new SmoothElevationPainter( - ELEVATION_SET, // type - 45, // elevation - 3 // blend radius -); -createArea(placer, [terrainPainter, elevationPainter, paintClass(clHill3)], stayClasses(clHill2, 1)); - -var placer = new ClumpPlacer(mapArea * 0.011 / div, 0.7, 0.05, 100, ix, iz); -var terrainPainter = new LayeredPainter( - [tCliff, tCliff], // terrains - [3] // widths -); -var elevationPainter = new SmoothElevationPainter( - ELEVATION_SET, // type - 62, // elevation - 3 // blend radius -); -createArea(placer, [terrainPainter, elevationPainter, paintClass(clHill4)], stayClasses(clHill3, 1)); - -var placer = new ClumpPlacer(mapArea * 0.003 / div, 0.7, 0.05, 100, ix, iz); -var terrainPainter = new LayeredPainter( - [tCliff, tLava1, tLava2, tLava3], // terrains - [1, 1, 1] // widths -); -var elevationPainter = new SmoothElevationPainter( - ELEVATION_SET, // type - 42, // elevation - 1 // blend radius -); -createArea(placer, [terrainPainter, elevationPainter, paintClass(clHill4)], stayClasses(clHill4, 1)); - -var num = floor(mapArea * 0.03 / 15 / div); -var tX = round(fx); -var tZ = round(fz); -var group = new SimpleGroup( - [new SimpleObject(aSmoke, num, num, 0,7)], - false, clBaseResource, tX, tZ -); -createObjectGroup(group, 0, stayClasses(clHill4,1)); - +createVolcano(0.5, 0.5, clHill, tCliff, [tLava1, tLava2, tLava3], true, ELEVATION_SET); RMS.SetProgress(45); log("Creating hills..."); -placer = new ClumpPlacer(scaleByMapSize(20, 150), 0.2, 0.1, 1); -terrainPainter = new LayeredPainter( - [tCliff, tGrass], // terrains - [2] // widths -); -elevationPainter = new SmoothElevationPainter(ELEVATION_SET, 18, 2); createAreas( - placer, - [terrainPainter, elevationPainter, paintClass(clHill)], + new ClumpPlacer(scaleByMapSize(20, 150), 0.2, 0.1, 1), + [ + new LayeredPainter([tCliff, tGrass], [2]), + new SmoothElevationPainter(ELEVATION_SET, 18, 2), + paintClass(clHill) + ], avoidClasses(clPlayer, 12, clHill, 15, clBaseResource, 2), scaleByMapSize(2, 8) * numPlayers ); // calculate desired number of trees for map (based on size) var MIN_TREES = 200; var MAX_TREES = 1250; var P_FOREST = 0.7; var totalTrees = scaleByMapSize(MIN_TREES, MAX_TREES); var numForest = totalTrees * P_FOREST; var numStragglers = totalTrees * (1.0 - P_FOREST); log("Creating forests..."); var types = [ [[tGrassB, tGrassA, pForestD], [tGrassB, pForestD]], [[tGrassB, tGrassA, pForestP], [tGrassB, pForestP]] ]; // some variation var size = numForest / (scaleByMapSize(2,8) * numPlayers); var num = floor(size / types.length); for (var i = 0; i < types.length; ++i) { placer = new ClumpPlacer(numForest / num, 0.1, 0.1, 1); painter = new LayeredPainter( types[i], // terrains [2] // widths ); createAreas( placer, [painter, paintClass(clForest)], avoidClasses(clPlayer, 12, clForest, 10, clHill, 0), num ); } RMS.SetProgress(70); log("Creating dirt patches..."); var sizes = [scaleByMapSize(3, 48), scaleByMapSize(5, 84), scaleByMapSize(8, 128)]; for (var i = 0; i < sizes.length; i++) { placer = new ClumpPlacer(sizes[i], 0.3, 0.06, 0.5); painter = new LayeredPainter( [tGrassA,tGrassA], // terrains [1] // widths ); createAreas( placer, [painter, paintClass(clDirt)], avoidClasses(clForest, 0, clHill, 0, clPlayer, 12), scaleByMapSize(20, 80) ); } var sizes = [scaleByMapSize(3, 48), scaleByMapSize(5, 84), scaleByMapSize(8, 128)]; for (var i = 0; i < sizes.length; i++) { placer = new ClumpPlacer(sizes[i], 0.3, 0.06, 0.5); painter = new LayeredPainter( [tGrassB,tGrassB], // terrains [1] // widths ); createAreas( placer, [painter, paintClass(clDirt)], avoidClasses(clForest, 0, clHill, 0, clPlayer, 12), scaleByMapSize(20, 80) ); } var sizes = [scaleByMapSize(3, 48), scaleByMapSize(5, 84), scaleByMapSize(8, 128)]; for (var i = 0; i < sizes.length; i++) { placer = new ClumpPlacer(sizes[i], 0.3, 0.06, 0.5); painter = new LayeredPainter( [tGrassC,tGrassC], // terrains [1] // widths ); createAreas( placer, [painter, paintClass(clDirt)], avoidClasses(clForest, 0, clHill, 0, clPlayer, 12), scaleByMapSize(20, 80) ); } log("Creating stone mines..."); group = new SimpleGroup([new SimpleObject(oStoneSmall, 0,2, 0,4), new SimpleObject(oStoneLarge, 1,1, 0,4)], true, clRock); createObjectGroups(group, 0, avoidClasses(clForest, 1, clPlayer, 10, clRock, 10, clHill, 1), scaleByMapSize(4,16), 100 ); // create small stone quarries group = new SimpleGroup([new SimpleObject(oStoneSmall, 2,5, 1,3)], true, clRock); createObjectGroups(group, 0, avoidClasses(clForest, 1, clPlayer, 10, clRock, 10, clHill, 1), scaleByMapSize(4,16), 100 ); log("Creating metal mines..."); group = new SimpleGroup([new SimpleObject(oMetalLarge, 1,1, 0,4)], true, clMetal); createObjectGroups(group, 0, avoidClasses(clForest, 1, clPlayer, 10, clMetal, 10, clRock, 5, clHill, 1), scaleByMapSize(4,16), 100 ); RMS.SetProgress(90); log("Creating small decorative rocks..."); group = new SimpleGroup( [new SimpleObject(aRockMedium, 1,3, 0,1)], true ); createObjectGroups( group, 0, avoidClasses(clForest, 0, clPlayer, 0, clHill, 0), scaleByMapSize(16, 262), 50 ); log("Creating large decorative rocks..."); group = new SimpleGroup( [new SimpleObject(aRockLarge, 1,2, 0,1), new SimpleObject(aRockMedium, 1,3, 0,2)], true ); createObjectGroups( group, 0, avoidClasses(clForest, 0, clPlayer, 0, clHill, 0), scaleByMapSize(8, 131), 50 ); RMS.SetProgress(95); log("Creating straggler trees..."); var types = [oTree]; // some variation var num = floor(numStragglers / types.length); for (var i = 0; i < types.length; ++i) { group = new SimpleGroup( [new SimpleObject(types[i], 1,1, 0,3)], true, clForest ); createObjectGroups(group, 0, avoidClasses(clForest, 1, clHill, 1, clPlayer, 12, clMetal, 6, clRock, 6), num ); } ExportMap();