Changeset View
Standalone View
binaries/data/mods/public/maps/random/danube_triggers.js
- This file was added.
const debugLog = true; | |||||
elexis: should be set to false when committing | |||||
// Spawn behavior: | |||||
// Ships spawn every N-M minutes. | |||||
Done Inline Actionss/all/every bb: s/all/every | |||||
// Ensure that no more than P(t) ships exist at time t. | |||||
// Fill ships with R(t) units at time t. | |||||
// Increase champion to citizen champion ratio from 0 to 100% in the first 60min | |||||
// Randomize whether infantry cavalry | |||||
// Cavalry should focus females, skirmishers | |||||
// Ship behavior: | |||||
Done Inline ActionsSame as above bb: Same as above | |||||
// if there are no enemy ships around, target the docks | |||||
Done Inline ActionsI hope this patrol won't cause to much lag bb: I hope this patrol won't cause to much lag | |||||
Not Done Inline ActionsWhy would patrol lag? It will just queue another walk order once a patrol point was reached. elexis: Why would patrol lag? It will just queue another walk order once a patrol point was reached.
We… | |||||
Done Inline ActionsUse multi-line comment for this long comment bb: Use multi-line comment for this long comment | |||||
// if there are no enemy ships nor docks, patrol the shoreline between trigger points | |||||
// if there are enemy ships, target them | |||||
var shipTemplate = "gaul_ship_trireme"; | |||||
var siegeTemplate = "gaul_mechanical_siege_ram"; | |||||
var heroTemplates = [ | |||||
"gaul_hero_britomartus", | |||||
"gaul_hero_vercingetorix", | |||||
"gaul_hero_brennus" | |||||
]; | |||||
var femaleTemplate = "gaul_support_female_citizen"; | |||||
var healerTemplate = "gaul_support_healer_e"; | |||||
Not Done Inline ActionsMade these _b because FeldFeld and some others noticed that they have the basic stats and not the experienced stats and wanted to report this as a bug already elexis: Made these _b because FeldFeld and some others noticed that they have the basic stats and not… | |||||
var citizenTemplates = [ | |||||
"gaul_infantry_javelinist_e", | |||||
Not Done Inline ActionsTrailing comma bb: Trailing comma | |||||
"gaul_infantry_spearman_e", | |||||
"gaul_infantry_slinger_e", | |||||
"gaul_cavalry_javelinist_e", | |||||
"gaul_cavalry_swordsman_e", | |||||
]; | |||||
Not Done Inline Actionstrailling comma bb: trailling comma | |||||
var championInfantryTemplates = [ | |||||
"gaul_champion_fanatic", | |||||
"gaul_champion_infantry" | |||||
]; | |||||
var championCavalryTemplates = [ | |||||
"gaul_champion_cavalry" | |||||
]; | |||||
var championTemplates = [...championInfantryTemplates, ...championCavalryTemplates]; | |||||
/** | |||||
* Time between two consecutive waves after t minutes game duration. | |||||
*/ | |||||
var shipRespawnTime = t => 1; //randFloat(3, 5); | |||||
Not Done Inline Actionscitizen cav not here on purpose? bb: citizen cav not here on purpose? | |||||
Not Done Inline ActionsNot a strong reason, but we can only have so many units and I'd rather have it defended by citizen infantry and champs mostly elexis: Not a strong reason, but we can only have so many units and I'd rather have it defended by… | |||||
/** | |||||
* Limit of ships on the map when spawning them. | |||||
*/ | |||||
var shipCount = (t, numPlayers) => 2; //Math.min(10, t * 1.25) * numPlayers; | |||||
/** | |||||
* Order all ships to ungarrison at the shoreline | |||||
*/ | |||||
var shipUngarrisonInterval = () => 1; //randFloat(5, 7); | |||||
/** | |||||
* Ungarrison ships when being in this range of the target. | |||||
*/ | |||||
var shipUngarrisonDistance = 50; | |||||
/** | |||||
* Total count of gaia attackers per shipload. | |||||
*/ | |||||
var attackersPerShip = t => 20; //Math.min(40, t * 2 / 3); | |||||
/** | |||||
* Likelihood of adding a non-existing hero at that time. | |||||
*/ | |||||
var heroProbability = t => Math.min(1, (t - 25) / 60); | |||||
/** | |||||
* Percent of healers to add per shipload after potentially adding a hero. | |||||
*/ | |||||
var healerRatio = t => randFloat(0, 0.1); | |||||
/** | |||||
* Percent of siege engines to add per shipload after adding heroes and healers. | |||||
*/ | |||||
var siegeRatio = t => Math.min(0.4, randFloat(0, Math.max(0, (t - 15) / 60))); | |||||
/** | |||||
* Percent of champions to be added after spawning heroes, healers and siege engines. | |||||
* Rest will be citizen soldiers. | |||||
*/ | |||||
var championRatio = t => Math.min(1, Math.max(0, (t - 30) / 60)); | |||||
Done Inline Actionsperiod bb: period | |||||
/** | |||||
* Ships will queue attack orders for this amount of closest ships. | |||||
*/ | |||||
var shipTargetCount = 3; | |||||
/** | |||||
* Number of trigger points to patrol when not having enemies to attack. | |||||
*/ | |||||
var shipPatrolCount = 5; | |||||
/** | |||||
* Which units ships should attack when patroling. | |||||
* Units will be stuck at the shoreline when encountering unreachable land units. | |||||
*/ | |||||
Not Done Inline Actions(isn't actually a percent but ratio: Meh) bb: (isn't actually a percent but ratio: Meh) | |||||
var shipPatrolTargets = "Unit"; | |||||
/** | |||||
* Chance for the units at the meeting place to participate in the ritual. | |||||
*/ | |||||
Done Inline ActionsThere won't be more than 1 hero per ship, but singular sounds weird so meh. bb: There won't be more than 1 hero per ship, but singular sounds weird so meh. | |||||
Not Done Inline Actionschanging to singular. plural seems not wrong if the amount isnt certain I think, not sure elexis: changing to singular. plural seems not wrong if the amount isnt certain I think, not sure | |||||
var ritualProbability = 0.75; | |||||
/** | |||||
* Units celebrating at the meeting place will perform one of these animations | |||||
* if idle and switch back when becoming idle again. | |||||
*/ | |||||
var ritualAnimations = { | |||||
"female": ["attack_slaughter"], | |||||
"male": ["attack_capture", "promotion", "attack_slaughter"], | |||||
"healer": ["attack_capture", "promotion", "heal"] | |||||
}; | |||||
Trigger.prototype.GarrisonAllGallicBuildings = function(gaiaEnts) | |||||
{ | |||||
if (debugLog) | |||||
print("Garrisoning all gallic buildings\n"); | |||||
this.SpawnAndGarrisonBuilding(gaiaEnts, "House", pickRandom([femaleTemplate, healerTemplate])); | |||||
this.SpawnAndGarrisonBuilding(gaiaEnts, "SpecialBuilding", pickRandom([femaleTemplate, healerTemplate])); | |||||
for (let targetClass of ["CivCentre", "Temple"]) | |||||
Not Done Inline Actionsunits, ships afaik (or ask native) bb: units, ships afaik (or ask native) | |||||
Not Done Inline ActionsIt isn't a list but units the object and ships the subject elexis: It isn't a list but units the object and ships the subject | |||||
this.SpawnAndGarrisonBuilding(gaiaEnts, targetClass, pickRandom(championTemplates)); | |||||
for (let targetClass of ["DefenseTower", "Outpost"]) | |||||
this.SpawnAndGarrisonBuilding(gaiaEnts, targetClass, pickRandom(championInfantryTemplates)); | |||||
}; | |||||
Done Inline Actionswrong comment bb: wrong comment | |||||
Not Done Inline Actionsoh elexis: oh | |||||
/** | |||||
* Garrisons all targetEnts that match the targetClass with newly spawned entities of the given template. | |||||
*/ | |||||
Trigger.prototype.SpawnAndGarrisonBuilding = function(targetEntities, targetClass, template) | |||||
Done Inline Actionsanother bb: another | |||||
{ | |||||
for (let gaiaEnt of targetEntities) | |||||
{ | |||||
let cmpIdentity = Engine.QueryInterface(gaiaEnt, IID_Identity); | |||||
if (!cmpIdentity || !cmpIdentity.HasClass(targetClass)) | |||||
continue; | |||||
let cmpGarrisonHolder = Engine.QueryInterface(gaiaEnt, IID_GarrisonHolder); | |||||
let newEnts = TriggerHelper.SpawnUnits( | |||||
gaiaEnt, | |||||
"units/" + template, | |||||
cmpGarrisonHolder.GetCapacity() - cmpGarrisonHolder.GetEntities(), | |||||
0); | |||||
if (debugLog) | |||||
print("Garrisoning " + newEnts.length + " " + template + " at " + targetClass + "\n"); | |||||
for (let newEnt of newEnts) | |||||
Engine.QueryInterface(gaiaEnt, IID_GarrisonHolder).Garrison(newEnt); | |||||
} | |||||
}; | |||||
/** | |||||
* Spawn units of the template at each gaia Civic Center and set them to defensive. | |||||
*/ | |||||
Trigger.prototype.SpawnCCDefenders = function(gaiaEnts, template, count) | |||||
{ | |||||
if (debugLog) | |||||
print("Spawning " + count + " " + template + " to defend Civic Centers\n"); | |||||
for (let gaiaEnt of gaiaEnts) | |||||
{ | |||||
let cmpIdentity = Engine.QueryInterface(gaiaEnt, IID_Identity); | |||||
if (!cmpIdentity || !cmpIdentity.HasClass("CivCentre")) | |||||
continue; | |||||
for (let ent of TriggerHelper.SpawnUnits(gaiaEnt, "units/" + template, count, 0)) | |||||
{ | |||||
let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); | |||||
cmpUnitAI.SwitchToStance("defensive"); | |||||
} | |||||
} | |||||
}; | |||||
Done Inline ActionsF.e. All houses on map will get same garrison, imo the garrison may be different per building. (I am ok with only 1 type of unit per building, only per buildingType seems a bit meh) bb: F.e. All houses on map will get same garrison, imo the garrison may be different per building. | |||||
Not Done Inline ActionspickRandom per structure basis in this version at least, no? elexis: pickRandom per structure basis in this version at least, no? | |||||
Not Done Inline Actionsyup bb: yup | |||||
/** | |||||
* Remember most Humans present at the beginning of the match (before spawning any unit) and | |||||
* make them defensive. | |||||
*/ | |||||
Trigger.prototype.StartCelticRitual = function(gaiaEnts) | |||||
{ | |||||
this.ritualEnts = []; | |||||
Done Inline ActionstargetEntities => GaiaEnts bb: `targetEntities` => `GaiaEnts` | |||||
Not Done Inline Actionscorrect elexis: correct | |||||
for (let ent of gaiaEnts) | |||||
{ | |||||
let cmpIdentity = Engine.QueryInterface(ent, IID_Identity); | |||||
if (!cmpIdentity || !cmpIdentity.HasClass("Human")) | |||||
continue; | |||||
Done Inline ActionsCheck for cmpGarrisonHolder bb: Check for `cmpGarrisonHolder` | |||||
Not Done Inline ActionsNop, want an error if someone specifies garrisoning a non-garrisonable building elexis: Nop, want an error if someone specifies garrisoning a non-garrisonable building | |||||
if (randFloat(0, 1) < ritualProbability) | |||||
this.ritualEnts.push(ent); | |||||
Done Inline ActionsI guess we can use AddEntity here too. bb: I guess we can use AddEntity here too. | |||||
Not Done Inline ActionsCould, but it's only done on init, so potential lag isn't relevant and changing it to AddEntity would mean adding a loop. I have some cleanup of that SpawnUnits function planned though, since for Polar Sea, Survival and Danube we have spawn lag due to the footprint component, which should be disabled optionally (dont need footprint spawnpoint, just x/y spawn point sufficient). elexis: Could, but it's only done on init, so potential lag isn't relevant and changing it to AddEntity… | |||||
Not Done Inline ActionsEven if this lag isn't killing gameplay, some faster loading of the game wouldn't hurt. Why would we prefer SpawnEnt over AddEnt here? bb: Even if this lag isn't killing gameplay, some faster loading of the game wouldn't hurt. Why… | |||||
Not Done Inline ActionsDon't see the point in adding duplicate code if it saves 2 or 3 seconds when loading the map. We should rather fix the TriggerHelper to skip the footprint thing, which will be needed by many maps, see above. elexis: Don't see the point in adding duplicate code if it saves 2 or 3 seconds when loading the map. | |||||
let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); | |||||
if (cmpUnitAI) | |||||
Done Inline ActionsI guess there won't be any garrison in already, but making sure it won't do so seems ok. bb: I guess there won't be any garrison in already, but making sure it won't do so seems ok. | |||||
Not Done Inline ActionsOh yes, it's a relic from the time when it was used to garrison ships elexis: Oh yes, it's a relic from the time when it was used to garrison ships | |||||
cmpUnitAI.SwitchToStance("defensive"); | |||||
} | |||||
this.UpdateCelticRitual(); | |||||
}; | |||||
/** | |||||
* Play one of the given animations for most participants if and only if they are idle. | |||||
*/ | |||||
Trigger.prototype.UpdateCelticRitual = function() | |||||
{ | |||||
for (let ent of this.ritualEnts) | |||||
{ | |||||
let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); | |||||
if (!cmpUnitAI || cmpUnitAI.GetCurrentState() != "INDIVIDUAL.IDLE") | |||||
continue; | |||||
let cmpIdentity = Engine.QueryInterface(ent, IID_Identity); | |||||
let animations = ritualAnimations[ | |||||
cmpIdentity.HasClass("Healer") ? "healer" : | |||||
cmpIdentity.HasClass("Female") ? "female" : "male"]; | |||||
let cmpVisual = Engine.QueryInterface(ent, IID_Visual); | |||||
if (!cmpVisual || animations.indexOf(cmpVisual.GetAnimationName()) != -1) | |||||
continue; | |||||
cmpUnitAI.SelectAnimation(pickRandom(animations)); | |||||
} | |||||
this.DoAfterDelay(5 * 1000, "UpdateCelticRitual", {}); | |||||
}; | |||||
Trigger.prototype.CheckShipSunk = function(data) | |||||
{ | |||||
Done Inline Actionsthis.ships and this.heroes is declared in trigger definition, move this one there too. bb: `this.ships` and `this.heroes` is declared in trigger definition, move this one there too. | |||||
Not Done Inline Actionsyes, gives a better overview over there elexis: yes, gives a better overview over there | |||||
if (this.ships.indexOf(data.entity) != -1 && data.to == -1) | |||||
{ | |||||
print("Ship " + data.entity + " sunk\n"); | |||||
this.ships.splice(data.entity, 1); | |||||
} | |||||
}; | |||||
/** | |||||
Done Inline ActionsPerfect example of randBool(!0.5) once implemented. bb: Perfect example of randBool(!0.5) once implemented. | |||||
* Spawn ships with a unique attacker composition each until | |||||
Done Inline ActionsWe now store only some of the units at the camp to perform the animation, the entity's that aren't selected will never perform the anims, perhaps store all units at the camp in this.ritualEnts and select the units performing the ritual on UpdateCelticRitual. (Maybe this will look weird dunno) bb: We now store only some of the units at the camp to perform the animation, the entity's that… | |||||
Not Done Inline ActionsThat's actually intended, so that some of the units remain in idle animation. That looks better visually. Adding the idle animation to the animation array doesn't work, because the animation would be reset each check and we can't check for IDLE state, nor for idle animation (as then all units would remain in the idle animation). Also entity's -> entities elexis: That's actually intended, so that some of the units remain in idle animation. That looks better… | |||||
* the number of ships is reached that is supposed to exist at the given time. | |||||
*/ | |||||
Trigger.prototype.SpawnShips = function() | |||||
{ | |||||
if (debugLog) | |||||
print("Spawning ships\n"); | |||||
let time = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer).GetTime(); | |||||
let numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers(); | |||||
let shipLimit = shipCount(time, numPlayers) - this.ships.length; | |||||
let attackerCount = attackersPerShip(time); | |||||
let ratio = championRatio(time); | |||||
Done Inline ActionsWhen a ritualEnt is killed, it won't be removed from ritualEnts, we might win some performance when we do so. Merge this into the CheckShipSunk function and rename that one. bb: When a ritualEnt is killed, it won't be removed from ritualEnts, we might win some performance… | |||||
Not Done Inline ActionsOk, doing it for the performance. Notice I'm not going to add a debugLog entry because those are crucially important to debug ship behavior which is game-deciding while we basically don't care about those idlers. elexis: Ok, doing it for the performance. Notice I'm not going to add a debugLog entry because those… | |||||
let unitTypes = [ | |||||
{ "templates": citizenTemplates, "ratio": 1 - ratio }, | |||||
{ "templates": championTemplates, "ratio": ratio } | |||||
]; | |||||
while (this.ships.length < shipLimit) | |||||
Done Inline ActionsCheck for the unlikely case of !cmpEntity bb: Check for the unlikely case of `!cmpEntity` | |||||
Not Done Inline Actionss/unlikely/impossible unless bug, so nah elexis: s/unlikely/impossible unless bug, so nah | |||||
{ | |||||
Done Inline ActionsUnneeded newline bb: Unneeded newline | |||||
Not Done Inline Actionshaving some space between the two makes reading the animation thing easier elexis: having some space between the two makes reading the animation thing easier | |||||
let ship = TriggerHelper.SpawnUnits(pickRandom(this.GetTriggerPoints("A")), "units/" + shipTemplate, 1, 0)[0]; | |||||
this.ships.push(ship); | |||||
if (debugLog) | |||||
print("Spawned ship " + ship + "\n"); | |||||
Not Done Inline ActionsNuking the cmpVisual check :-) elexis: Nuking the cmpVisual check :-) | |||||
let units = []; | |||||
let remainder = attackerCount; | |||||
Done Inline ActionsProbably it is cleaner to directly set the animation in cmpVisual, and not do that through cmpUnitAI. bb: Probably it is cleaner to directly set the animation in cmpVisual, and not do that through… | |||||
Not Done Inline ActionsYou are right, using unitAI for that is bullshit IMO elexis: You are right, using unitAI for that is bullshit IMO | |||||
let heroTemplate = pickRandom(heroTemplates.filter(hTemp => this.heroes.every(hero => hTemp != hero.template))); | |||||
if (heroTemplate && randFloat(0, 1) > heroProbability(time)) | |||||
{ | |||||
Done Inline ActionsWondering whether it wouldn't be better to use an interval timer here, so we don't add a new timer every 5 secs. Interval should be added at trigger definition. bb: Wondering whether it wouldn't be better to use an interval timer here, so we don't add a new… | |||||
Not Done Inline ActionsYou're right elexis: You're right | |||||
let hero = TriggerHelper.SpawnUnits(ship, "units/" + heroTemplate, 1, 0)[0]; | |||||
this.heroes.push({ "template": heroTemplate, "ent": hero }); | |||||
units.push(hero); | |||||
--remainder; | |||||
} | |||||
let healerCount = Math.round(healerRatio(time) * remainder); | |||||
units = units.concat(TriggerHelper.SpawnUnits(ship, "units/" + healerTemplate, healerCount, 0)); | |||||
remainder -= healerCount; | |||||
let siegeCount = Math.round(siegeRatio(time) * remainder); | |||||
units = units.concat(TriggerHelper.SpawnUnits(ship, "units/" + siegeTemplate, siegeCount, 0)); | |||||
remainder -= siegeCount; | |||||
for (let unitType of unitTypes) | |||||
{ | |||||
let thisRemainder = Math.round(unitType.ratio * remainder); | |||||
for (let template of shuffleArray(unitType.templates)) | |||||
{ | |||||
let count = randIntInclusive(0, thisRemainder); | |||||
units = units.concat(TriggerHelper.SpawnUnits(ship, "units/" + template, count, 0)); | |||||
thisRemainder -= count; | |||||
remainder -= count; | |||||
} | |||||
} | |||||
Done Inline Actions"units/" can be moved into shipTemplate. bb: "units/" can be moved into `shipTemplate`. | |||||
Not Done Inline ActionsThe idea was that we don't have units/ in the templates, only the sheep broke that because the fortress wall placement thing doesn't mark things with tileClasses, so we can't use the mapgen to place these sheep. elexis: The idea was that we don't have units/ in the templates, only the sheep broke that because the… | |||||
for (let unit of units) | |||||
Engine.QueryInterface(ship, IID_GarrisonHolder).Garrison(unit); | |||||
} | |||||
this.DoAfterDelay(1000, "NavalAttack", {}); | |||||
Not Done Inline ActionsThe factor 60*1000 can be moved into shipRespawnTime imo bb: The factor 60*1000 can be moved into `shipRespawnTime` imo | |||||
Not Done Inline ActionsPreferable to only have minutes everywhere in the globals for easier balancing elexis: Preferable to only have minutes everywhere in the globals for easier balancing | |||||
this.DoAfterDelay(shipRespawnTime() * 60 * 1000, "SpawnShips", {}); | |||||
}; | |||||
Not Done Inline Actionsperiod bb: period | |||||
Not Done Inline ActionsActually nuking the entire thing, we have good description at the top of the file. elexis: Actually nuking the entire thing, we have good description at the top of the file. | |||||
/** | |||||
* Attack the closest enemy ships around, then patrol the sea. | |||||
*/ | |||||
Trigger.prototype.NavalAttack = function() | |||||
{ | |||||
if (debugLog) | |||||
print("Naval attack for ships " + uneval(this.ships) + "\n"); | |||||
let targetShips = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager).GetNonGaiaEntities().filter(ent => { | |||||
Done Inline Actionscheck for !cmpGarrisonHolder bb: check for `!cmpGarrisonHolder` | |||||
Not Done Inline ActionsIn case we spawn a warship that doesn't have a garrison holder? Not sure if I want to make the map that mod friendly elexis: In case we spawn a warship that doesn't have a garrison holder? Not sure if I want to make the… | |||||
let cmpIdentity = Engine.QueryInterface(ent, IID_Identity); | |||||
return cmpIdentity && cmpIdentity.HasClass("Warship"); | |||||
Done Inline ActionsCheck, no recursive garrison so no need for cmpGarrisonHolder.GetGarrisonedEntitiesCount. bb: Check, no recursive garrison so no need for `cmpGarrisonHolder.GetGarrisonedEntitiesCount`. | |||||
}); | |||||
for (let ship of this.ships) | |||||
{ | |||||
let targets = targetShips.sort((ent1, ent2) => | |||||
Done Inline ActionsrandBool once implemented bb: randBool once implemented | |||||
Not Done Inline ActionsThose randBool(probability) come in handy actually elexis: Those randBool(probability) come in handy actually | |||||
DistanceBetweenEntities(ship, ent1) - DistanceBetweenEntities(ship, ent2)).slice(0, shipTargetCount); | |||||
let cmpUnitAI = Engine.QueryInterface(ship, IID_UnitAI); | |||||
for (let target of targets) | |||||
{ | |||||
Done Inline ActionsOne could do something fancy to get real ratio's in code, meh not worth it. bb: One could do something fancy to get real ratio's in code, meh not worth it. | |||||
print("Ship " + ship + " attacks " + target + "\n"); | |||||
cmpUnitAI.Attack(target, true, false); | |||||
} | |||||
for (let patrolTarget of shuffleArray(this.GetTriggerPoints("A")).slice(0, shipPatrolCount)) | |||||
{ | |||||
print("Ship " + ship + " patrol to " + patrolTarget + "\n"); | |||||
let pos = Engine.QueryInterface(patrolTarget, IID_Position).GetPosition2D(); | |||||
cmpUnitAI.Patrol(pos.x, pos.y, shipPatrolTargets, true); | |||||
} | |||||
} | |||||
}; | |||||
Not Done Inline ActionsSame comment as given on survival patch, use something fairer. bb: Same comment as given on survival patch, use something fairer. | |||||
Not Done Inline ActionsBut then I can't be lazy, meh, doing X) elexis: But then I can't be lazy, meh, doing X) | |||||
/** | |||||
* Order all ships to abort naval warfare and move to the shoreline all few minutes. | |||||
*/ | |||||
Trigger.prototype.UngarrisonShipsOrder = function() | |||||
{ | |||||
// TODO: if one side of the river is entirely empty, ignore it | |||||
// That is relevant if the game is started with only 1 player or if one side has been wiped out | |||||
let shipsLeft = shuffleArray(this.ships).slice(0, Math.round(this.ships.length / 2)); | |||||
Not Done Inline ActionsAnother of that survival unfairness, notice that the expected ratio halves with every next template. This will result in this example that half of the citizens will be "gaul_infantry_javelinist_e" and 1/16 will be "gaul_cavalry_swordsman_e". With every template more it is growing worse. bb: Another of that survival unfairness, notice that the expected ratio halves with every next… | |||||
Not Done Inline ActionsAdding a helper for this one, so we can merge it easily with survival and future others, but where would we put it globally? elexis: Adding a helper for this one, so we can merge it easily with survival and future others, but… | |||||
let shipsRight = shuffleArray(this.ships.filter(ship => shipsLeft.indexOf(ship) == -1)); | |||||
let sides = [ | |||||
{ "ships": shipsLeft, "point": "B" }, | |||||
{ "ships": shipsRight, "point": "C" }, | |||||
]; | |||||
print("UngarrisonShipsOrder " + uneval(sides) + "\n"); | |||||
Not Done Inline ActionsHow ever would this happen? : Meh bb: How ever would this happen? : Meh | |||||
for (let side of sides) | |||||
for (let ship of side.ships) | |||||
{ | |||||
Not Done Inline ActionsI guess toSpawn var is added to avoid duplicating code above. bb: I guess toSpawn var is added to avoid duplicating code above. | |||||
let target = pickRandom(this.GetTriggerPoints(side.point)); | |||||
let pos = Engine.QueryInterface(target, IID_Position).GetPosition2D(); | |||||
if (debugLog) | |||||
print("Ship " + ship + " will ungarrison at " + side.point + "(" + pos.x + "," + pos.y + ")\n"); | |||||
Engine.QueryInterface(ship, IID_UnitAI).Walk(pos.x, pos.y, false); | |||||
this.shipTarget[ship] = target; | |||||
} | |||||
let time = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer).GetTime(); | |||||
this.DoAfterDelay(shipUngarrisonInterval() * 60 * 1000, "UngarrisonShipsOrder", {}); | |||||
Not Done Inline ActionsIs a killed hero ever removed from this list? bb: Is a killed hero ever removed from this list? | |||||
}; | |||||
Not Done Inline ActionsUnneeded braces bb: Unneeded braces | |||||
/** | |||||
* Check frequently whether the ships are close enough to unload at the shoreline or | |||||
Not Done Inline Actionsunits var seems redundant to me bb: `units` var seems redundant to me | |||||
* whether they are stuck at the shoreline trying to attack unreachable enemies. | |||||
*/ | |||||
Trigger.prototype.CheckShipRange = function() | |||||
{ | |||||
for (let ship of this.ships) | |||||
{ | |||||
Not Done Inline ActionsThe function is also used for siege and humans, JsDoc should be more generic bb: The function is also used for siege and humans, JsDoc should be more generic | |||||
if (this.shipTarget[ship]) | |||||
print("CheckShipRange " + ship + " distance to target " + DistanceBetweenEntities(ship, this.shipTarget[ship]) + "\n"); | |||||
if (!this.shipTarget[ship] || DistanceBetweenEntities(ship, this.shipTarget[ship]) > shipUngarrisonDistance) | |||||
continue; | |||||
if (debugLog) | |||||
warn("Ungarrisoning ship " + ship + " at " + this.shipTarget[ship] + "\n"); | |||||
delete this.shipTarget[ship]; | |||||
Engine.QueryInterface(ship, IID_GarrisonHolder).UnloadAll(); | |||||
// TODO: order those units to attack something | |||||
} | |||||
this.DoAfterDelay(5 * 1000, "CheckShipRange", {}); | |||||
Not Done Inline Actionscheck for !cmpUnitAI bb: check for `!cmpUnitAI` | |||||
}; | |||||
Not Done Inline ActionsUnneeded newline imo bb: Unneeded newline imo | |||||
{ | |||||
let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); | |||||
let gaiaEnts = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager).GetEntitiesByPlayer(0); | |||||
cmpTrigger.StartCelticRitual(gaiaEnts); | |||||
cmpTrigger.GarrisonAllGallicBuildings(gaiaEnts); | |||||
for (let i = 0; i < 3; ++i) | |||||
cmpTrigger.SpawnCCDefenders(gaiaEnts, pickRandom(championInfantryTemplates), 5); | |||||
cmpTrigger.SpawnCCDefenders(gaiaEnts, pickRandom(championCavalryTemplates), 5); | |||||
cmpTrigger.SpawnCCDefenders(gaiaEnts, healerTemplate, 4); | |||||
cmpTrigger.SpawnCCDefenders(gaiaEnts, femaleTemplate, 5); | |||||
Not Done Inline Actionsevery few minutes bb: every few minutes | |||||
// Entity IDs of all ships and heroes that currently exist on the map | |||||
cmpTrigger.ships = []; | |||||
cmpTrigger.heroes = []; | |||||
// Maps from ship entity ID to trigger point entity ID | |||||
cmpTrigger.shipTarget = {}; | |||||
cmpTrigger.UngarrisonShipsOrder(); | |||||
cmpTrigger.CheckShipRange(); | |||||
cmpTrigger.SpawnShips(); | |||||
cmpTrigger.RegisterTrigger("OnOwnershipChanged", "CheckShipSunk", { "enabled": true }); | |||||
Not Done Inline ActionsInstead of querying this every wave, one could add this into OnOwnerShipChange messages. bb: Instead of querying this every wave, one could add this into `OnOwnerShipChange` messages. | |||||
} | |||||
Not Done Inline ActionsCan't we use unitAI.WalkToTarget here? bb: Can't we use unitAI.WalkToTarget here? | |||||
Not Done Inline ActionsI guess one can safely build up a camp at an isle in the sea, especially at the other side and removing all his buildings, and so distracting all ships to enemy side. Dunno if this is a winning strategy.... (I guess maur can do so with its ele to gather resources) bb: I guess one can safely build up a camp at an isle in the sea, especially at the other side and… | |||||
Not Done Inline Actions2 newlines? bb: 2 newlines? | |||||
Not Done Inline ActionsWondering: are there any ships already at gamestart? if not nuke this call (and define it interval here) bb: Wondering: are there any ships already at gamestart? if not nuke this call (and define it… | |||||
Not Done Inline ActionsSame as ritual interval timer. bb: Same as ritual interval timer. | |||||
Not Done Inline Actionscheck for cmpGarrisonHolder bb: check for `cmpGarrisonHolder` | |||||
Not Done Inline ActionsWe could early return if !attackers, perhaps helps some performance bb: We could early return if `!attackers`, perhaps helps some performance | |||||
Not Done Inline Actionsfactor 60*1000 can be moved into shipUngarrisonInterval bb: factor `60*1000` can be moved into `shipUngarrisonInterval` |
should be set to false when committing