Source: extinct_volcano_triggers.js

/**
* @class
 * 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;

/**
 * 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 safe place!"),
	markForTranslation("We have to find dry ground, our lands will drown 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/champion_infantry_swordsman_02";

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;

	print("DEBUG [" + Math.round(TriggerHelper.GetMinutes()) + "] " + 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("Tower"))
			continue;

		let cmpGarrisonHolder = Engine.QueryInterface(gaiaEnt, IID_GarrisonHolder);
		if (!cmpGarrisonHolder)
			continue;

		TriggerHelper.SpawnGarrisonedUnits(gaiaEnt, garrisonedUnits, cmpGarrisonHolder.GetCapacity(), 0);
	}
};

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.GetGaiaAndNonGaiaEntities())
	{
		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 : (InitAttributes.settings.SeaLevelRiseTime || 0);
	let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
	cmpTrigger.GarrisonWoodenTowers();
	cmpTrigger.DoAfterDelay((waterRiseTime - waterRiseNotificationDuration) * 60 * 1000, "RaisingWaterNotification", {});
	cmpTrigger.DoAfterDelay(waterRiseTime * 60 * 1000, "RaiseWaterLevelStep", {});
}