Spawn attackers at the gaia civic centers on Danubius, so as to make the more frightening and incentivize its destruction.

Differential Revision: https://code.wildfiregames.com/D380
Reviewed By: Sandarac
This was SVN commit r19707.
This commit is contained in:
elexis
2017-06-01 17:33:07 +00:00
parent f4ba646972
commit d724347812
2 changed files with 159 additions and 60 deletions
@@ -7,6 +7,8 @@ const triggerPointShipUnloadLeft = "special/trigger_point_C";
const triggerPointShipUnloadRight = "special/trigger_point_D";
const triggerPointLandPatrolLeft = "special/trigger_point_E";
const triggerPointLandPatrolRight = "special/trigger_point_F";
const triggerPointCCAttackerPatrolLeft = "special/trigger_point_G";
const triggerPointCCAttackerPatrolRight = "special/trigger_point_H";
// Terrain textures
const tRoad = "steppe_river_rocks";
@@ -123,11 +125,13 @@ InitMap();
const numPlayers = getNumPlayers();
const mapSize = getMapSize();
var clMiddle = createTileClass();
var clPlayer = createTileClass();
var clForest = createTileClass();
var clWater = createTileClass();
var clLand = [createTileClass(), createTileClass()];
var clLandPatrolPoint = [createTileClass(), createTileClass()];
var clCCAttackerPatrolPoint = [createTileClass(), createTileClass()];
var clShore = [createTileClass(), createTileClass()];
var clShoreUngarrisonPoint = [createTileClass(), createTileClass()];
var clShip = createTileClass();
@@ -156,8 +160,9 @@ var gallicCCTreasureCount = randIntInclusive(8, 12);
// How many treasures will be placed randomly on the map at most
var randomTreasureCount = randIntInclusive(0, 3 * numPlayers);
// Place a gaia village on small maps and larger
if (mapSize >= smallMapSize)
// Place a gallic village on small maps and larger
var gallicCC = mapSize >= smallMapSize;
if (gallicCC)
{
log("Creating gallic villages...");
let gaulCityRadius = 12;
@@ -186,7 +191,7 @@ if (mapSize >= smallMapSize)
// Radius of the meeting place
let mRadius = scaleByMapSize(4, 6);
// Create a path connecting the gaia city with a meeting place at the shoreline.
// Create a path connecting the gallic city with a meeting place at the shoreline.
// To avoid the path going through the palisade wall, start it at the gate, not at the city center.
let placer = new PathPlacer(
gX + gaulCityRadius * (i == 0 ? 1 : -1),
@@ -273,7 +278,7 @@ if (mapSize >= smallMapSize)
let wall = [
"gate", "hut", "palisade_tower", "wallLong", "wallLong",
"cornerIn", "defense_tower", "wallLong", "wallLong", "temple",
"palisade_tower", "wallLong", "house", "wallLong", "palisade_tower", "longhouse", "wallLong", "wallLong",
"palisade_tower", "wallLong", "house", "gate", "palisade_tower", "longhouse", "wallLong", "wallLong",
"cornerIn", "defense_tower", "wallLong", "tavern", "wallLong", "palisade_tower"];
wall = wall.concat(wall);
placeCustomFortress(gX, gZ, new Fortress("Geto-Dacian Tribal Confederation", wall), "gaul", 0, PI);
@@ -776,7 +781,9 @@ for (let i = 0; i < 2; ++i)
);
log("Creating patrol points for land attackers...");
addToClass(mapSize/2, mapSize/2, clMiddle);
for (let i = 0; i < 2; ++i)
{
createObjectGroups(
new SimpleGroup(
[new SimpleObject(
@@ -794,6 +801,35 @@ for (let i = 0; i < 2; ++i)
100
);
if (gallicCC)
createObjectGroups(
new SimpleGroup(
[new SimpleObject(
i == 0 ? triggerPointCCAttackerPatrolLeft : triggerPointCCAttackerPatrolRight,
1, 1,
0, 0)],
true,
clCCAttackerPatrolPoint[i]),
0,
[
// Don't avoid the forest, so that as many places as possible on the border are visited
avoidClasses(
clWater, 5,
clHill, 3,
clFood, 1,
clRock, 4,
clMetal, 4,
clPlayer, 15,
clGauls, 0,
clCCAttackerPatrolPoint[i], 5,
clMiddle, mapSize * 0.5 - 15),
stayClasses(clLand[i], 0)
],
10000,
100
);
}
log("Creating water logs...");
createObjectGroups(
new SimpleGroup([new SimpleObject(aWaterLog, 1, 1, 0, 0)], true, clWaterLog),
@@ -78,6 +78,16 @@ var gallicBuildingGarrison = [
* This can be accomplished by not wiping out players buildings entirely.
*/
/**
* Time in minutes between two consecutive waves spawned from the gaia civic centers, if they still exist.
*/
var ccAttackerInterval = t => randFloat(6, 8);
/**
* Number of attackers spawned at a civic center at t minutes ingame time.
*/
var ccAttackerCount = t => Math.min(20, Math.max(0, Math.round(t * 1.5)));
/**
* Time between two consecutive waves.
*/
@@ -189,6 +199,8 @@ var triggerPointUngarrisonLeft = "C";
var triggerPointUngarrisonRight = "D";
var triggerPointLandPatrolLeft = "E";
var triggerPointLandPatrolRight = "F";
var triggerPointCCAttackerPatrolLeft = "G";
var triggerPointCCAttackerPatrolRight = "H";
/**
* Which playerID to use for the opposing gallic reinforcements.
@@ -266,7 +278,7 @@ Trigger.prototype.SpawnAndGarrisonBuilding = function(gaiaEnts, targetClass, tem
/**
* Spawn units of the template at each gaia Civic Center and set them to defensive.
*/
Trigger.prototype.SpawnCCDefenders = function(gaiaEnts)
Trigger.prototype.SpawnInitialCCDefenders = function(gaiaEnts)
{
this.debugLog("To defend CCs, spawning " + uneval(ccDefenders));
@@ -276,12 +288,47 @@ Trigger.prototype.SpawnCCDefenders = function(gaiaEnts)
if (!cmpIdentity || !cmpIdentity.HasClass("CivCentre"))
continue;
this.civicCenters.push(gaiaEnt);
for (let ccDefender of ccDefenders)
for (let ent of TriggerHelper.SpawnUnits(gaiaEnt, ccDefender.template, ccDefender.count, gaulPlayer))
Engine.QueryInterface(ent, IID_UnitAI).SwitchToStance("defensive");
}
};
Trigger.prototype.SpawnCCAttackers = function()
{
let time = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer).GetTime() / 60 / 1000;
let mapSize = Engine.QueryInterface(SYSTEM_ENTITY, IID_Terrain).GetMapSize();
for (let gaiaCC of this.civicCenters)
{
let toSpawn = this.GetAttackerComposition(ccAttackerCount(time), false);
this.debugLog("Spawning civic center attackers at " + gaiaCC + ": " + uneval(toSpawn));
let ccAttackers = [];
for (let spawn of toSpawn)
{
let ents = TriggerHelper.SpawnUnits(gaiaCC, "units/" + spawn.template, spawn.count, gaulPlayer);
if (spawn.hero && ents[0])
this.heroes.push({ "template": spawn.template, "ent": ents[0] });
ccAttackers = ccAttackers.concat(ents);
}
let patrolPointRef = Engine.QueryInterface(gaiaCC, IID_Position).GetPosition2D().x < mapSize / 2 ?
triggerPointCCAttackerPatrolLeft :
triggerPointCCAttackerPatrolRight;
this.AttackAndPatrol(ccAttackers, unitTargetClass, patrolPointRef, "CCAttackers", false);
}
if (this.civicCenters.length)
this.DoAfterDelay(ccAttackerInterval() * 60 * 1000, "SpawnCCAttackers", {});
};
/**
* Remember most Humans present at the beginning of the match (before spawning any unit) and
* make them defensive.
@@ -351,7 +398,7 @@ Trigger.prototype.SpawnShips = function()
for (let ship of this.ships)
this.AttackAndPatrol([ship], shipTargetClass, triggerPointShipPatrol, "Ships");
this.DoAfterDelay(shipRespawnTime() * 60 * 1000, "SpawnShips", {});
this.DoAfterDelay(shipRespawnTime(time) * 60 * 1000, "SpawnShips", {});
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
cmpTimer.CancelTimer(this.fillShipsTimer);
@@ -359,61 +406,65 @@ Trigger.prototype.SpawnShips = function()
this.FillShips();
};
Trigger.prototype.GetAttackerComposition = function(attackerCount, addSiege)
{
let time = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer).GetTime() / 60 / 1000;
let toSpawn = [];
let remainder = attackerCount;
let siegeCount = addSiege ? Math.round(siegeRatio(time) * remainder) : 0;
if (siegeCount)
toSpawn.push({ "template": siegeTemplate, "count": siegeCount });
remainder -= siegeCount;
let heroTemplate = pickRandom(heroTemplates.filter(hTemp => this.heroes.every(hero => hTemp != hero.template)));
if (heroTemplate && remainder && randBool(heroProbability(time)))
{
toSpawn.push({ "template": heroTemplate, "count": 1, "hero": true });
--remainder;
}
let healerCount = Math.round(healerRatio(time) * remainder);
if (healerCount)
toSpawn.push({ "template": healerTemplate, "count": healerCount });
remainder -= healerCount;
let championCount = Math.round(championRatio(time) * remainder);
let championTemplateCounts = this.RandomAttackerTemplates(championTemplates, championCount);
for (let template in championTemplateCounts)
{
let count = championTemplateCounts[template];
toSpawn.push({ "template": template, "count": count });
championCount -= count;
remainder -= count;
}
let citizenTemplateCounts = this.RandomAttackerTemplates(citizenTemplates, remainder);
for (let template in citizenTemplateCounts)
{
let count = citizenTemplateCounts[template];
toSpawn.push({ "template": template, "count": count });
remainder -= count;
}
if (remainder != 0)
warn("Didn't spawn as many attackers as were intended (" + remainder + " remaining)");
return toSpawn;
};
Trigger.prototype.FillShips = function()
{
let time = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer).GetTime() / 60 / 1000;
let attackerCount = attackersPerShip(time);
for (let ship of this.ships)
{
let cmpGarrisonHolder = Engine.QueryInterface(ship, IID_GarrisonHolder);
if (!cmpGarrisonHolder)
continue;
let remainder = Math.max(0, attackerCount - cmpGarrisonHolder.GetEntities().length);
let toSpawn = [];
let siegeCount = Math.round(siegeRatio(time) * remainder);
if (siegeCount)
toSpawn.push({ "template": siegeTemplate, "count": siegeCount });
remainder -= siegeCount;
let heroTemplate = pickRandom(heroTemplates.filter(hTemp => this.heroes.every(hero => hTemp != hero.template)));
if (heroTemplate && remainder && randBool(heroProbability(time)))
{
toSpawn.push({ "template": heroTemplate, "count": 1, "hero": true });
--remainder;
}
let healerCount = Math.round(healerRatio(time) * remainder);
if (healerCount)
toSpawn.push({ "template": healerTemplate, "count": healerCount });
remainder -= healerCount;
let championCount = Math.round(championRatio(time) * remainder);
let championTemplateCounts = this.RandomAttackerTemplates(championTemplates, championCount);
for (let template in championTemplateCounts)
{
let count = championTemplateCounts[template];
toSpawn.push({ "template": template, "count": count });
championCount -= count;
remainder -= count;
}
let citizenTemplateCounts = this.RandomAttackerTemplates(citizenTemplates, remainder);
for (let template in citizenTemplateCounts)
{
let count = citizenTemplateCounts[template];
toSpawn.push({ "template": template, "count": count });
remainder -= count;
}
let toSpawn = this.GetAttackerComposition(Math.max(0, attackersPerShip(time) - cmpGarrisonHolder.GetEntities().length), true);
this.debugLog("Filling ship " + ship + " with " + uneval(toSpawn));
if (remainder != 0)
warn("Didn't spawn as many attackers as were intended (" + remainder + " remaining)");
for (let spawn of toSpawn)
{
// Don't use TriggerHelper.SpawnUnits here because that is too slow,
@@ -438,7 +489,7 @@ Trigger.prototype.FillShips = function()
/**
* Attack the closest enemy ships around, then patrol the sea.
*/
Trigger.prototype.AttackAndPatrol = function(attackers, targetClass, triggerPointRef, debugName)
Trigger.prototype.AttackAndPatrol = function(attackers, targetClass, triggerPointRef, debugName, attack = true)
{
if (!attackers.length)
return;
@@ -453,14 +504,15 @@ Trigger.prototype.AttackAndPatrol = function(attackers, targetClass, triggerPoin
this.debugLog(debugName + " " + uneval(attackers) + " attack " + uneval(targets));
for (let target of targets)
ProcessCommand(gaulPlayer, {
"type": "attack",
"entities": attackers,
"target": target,
"queued": true,
"allowCapture": false
});
if (attack)
for (let target of targets)
ProcessCommand(gaulPlayer, {
"type": "attack",
"entities": attackers,
"target": target,
"queued": true,
"allowCapture": false
});
let patrolTargets = shuffleArray(this.GetTriggerPoints(triggerPointRef)).slice(0, patrolCount);
this.debugLog(debugName + " " + uneval(attackers) + " patrol to " + uneval(patrolTargets));
@@ -617,6 +669,13 @@ Trigger.prototype.DanubiusOwnershipChange = function(data)
let heroIdx = this.heroes.findIndex(hero => hero.ent == data.entity);
if (ritualIdx != -1)
this.heroes.splice(heroIdx, 1);
let ccIdx = this.civicCenters.indexOf(data.entity);
if (ccIdx != -1)
{
this.debugLog("Gaia civic center " + data.entity + " destroyed");
this.civicCenters.splice(ccIdx, 1);
}
};
@@ -631,13 +690,17 @@ Trigger.prototype.DanubiusOwnershipChange = function(data)
cmpTrigger.ships = [];
cmpTrigger.heroes = [];
// Remember gaia CCs to spawn attackers from
cmpTrigger.civicCenters = [];
// Maps from gaia ship entity ID to ungarrison trigger point entity ID and land patrol triggerpoint name
cmpTrigger.shipTarget = {};
cmpTrigger.fillShipsTimer = undefined;
cmpTrigger.StartCelticRitual(gaiaEnts);
cmpTrigger.GarrisonAllGallicBuildings(gaiaEnts);
cmpTrigger.SpawnCCDefenders(gaiaEnts);
cmpTrigger.SpawnInitialCCDefenders(gaiaEnts);
cmpTrigger.SpawnCCAttackers(gaiaEnts);
cmpTrigger.SpawnShips();
cmpTrigger.DoAfterDelay(shipUngarrisonInterval() * 60 * 1000, "UngarrisonShipsOrder", {});