forked from 0ad/0ad
Basic regicide gamemode. Based on patch by Sandarac, refs #2160.
Spawn a random hero at gamestart and defeat the player if the hero dies. Consider nomad maps where units spawn without buildings or start on a ship. Correct a comment for TriggerHelper.SpawnUnits. Make shuffleArray available to GUI and simulation and use it to randomize heroes. This was SVN commit r18544.
This commit is contained in:
parent
34c26767ca
commit
8dddd369ef
@ -16,3 +16,21 @@ function clone(o)
|
||||
r[key] = clone(o[key]);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* "Inside-out" implementation of Fisher-Yates shuffle
|
||||
*/
|
||||
function shuffleArray(source)
|
||||
{
|
||||
if (!source.length)
|
||||
return [];
|
||||
|
||||
let result = [source[0]];
|
||||
for (let i = 1; i < source.length; ++i)
|
||||
{
|
||||
let j = Math.floor(Math.random() * i);
|
||||
result[i] = result[j];
|
||||
result[j] = source[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ const FALLBACK_CIV = "athen";
|
||||
/**
|
||||
* Constants needed for heightmap_manipulation.js
|
||||
*/
|
||||
const MAX_HEIGHT_RANGE = 0xFFFF / HEIGHT_UNITS_PER_METRE // Engine limit, Roughly 700 meters
|
||||
const MAX_HEIGHT_RANGE = 0xFFFF / HEIGHT_UNITS_PER_METRE; // Engine limit, Roughly 700 meters
|
||||
const MIN_HEIGHT = - SEA_LEVEL;
|
||||
const MAX_HEIGHT = MAX_HEIGHT_RANGE - SEA_LEVEL;
|
||||
// Default angle for buildings
|
||||
@ -91,24 +91,6 @@ function min(a, b)
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
/**
|
||||
* "Inside-out" implementation of Fisher-Yates shuffle
|
||||
*/
|
||||
function shuffleArray(source)
|
||||
{
|
||||
if (!source.length)
|
||||
return [];
|
||||
|
||||
let result = [source[0]];
|
||||
for (let i = 1; i < source.length; ++i)
|
||||
{
|
||||
let j = randInt(0, i);
|
||||
result[i] = result[j];
|
||||
result[j] = source[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retries the given function with those arguments as often as specified.
|
||||
*/
|
||||
|
104
binaries/data/mods/public/maps/scripts/Regicide.js
Normal file
104
binaries/data/mods/public/maps/scripts/Regicide.js
Normal file
@ -0,0 +1,104 @@
|
||||
Trigger.prototype.CheckRegicideDefeat = function(data)
|
||||
{
|
||||
if (data.entity == this.regicideHeroes[data.from])
|
||||
TriggerHelper.DefeatPlayer(data.from);
|
||||
};
|
||||
|
||||
Trigger.prototype.InitRegicideGame = function(msg)
|
||||
{
|
||||
let playersCivs = [];
|
||||
for (let playerID = 1; playerID < TriggerHelper.GetNumberOfPlayers(); ++playerID)
|
||||
playersCivs[playerID] = QueryPlayerIDInterface(playerID).GetCiv();
|
||||
|
||||
// Get all hero templates of these civs
|
||||
let heroTemplates = {};
|
||||
let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
|
||||
for (let templateName of cmpTemplateManager.FindAllTemplates(false))
|
||||
{
|
||||
if (templateName.substring(0,6) != "units/")
|
||||
continue;
|
||||
|
||||
let identity = cmpTemplateManager.GetTemplate(templateName).Identity;
|
||||
let classes = GetIdentityClasses(identity);
|
||||
|
||||
if (classes.indexOf("Hero") == -1 ||
|
||||
playersCivs.every(civ => civ != identity.Civ))
|
||||
continue;
|
||||
|
||||
if (!heroTemplates[identity.Civ])
|
||||
heroTemplates[identity.Civ] = [];
|
||||
|
||||
if (heroTemplates[identity.Civ].indexOf(templateName) == -1)
|
||||
heroTemplates[identity.Civ].push({
|
||||
"templateName": templateName,
|
||||
"classes": classes
|
||||
});
|
||||
}
|
||||
|
||||
// Sort available spawn points by preference
|
||||
let spawnPreference = ["Ship", "Structure", "CivilCentre"];
|
||||
let getSpawnPreference = entity => {
|
||||
let cmpIdentity = Engine.QueryInterface(entity, IID_Identity);
|
||||
let classes = cmpIdentity.GetClassesList();
|
||||
return spawnPreference.findIndex(className => classes.indexOf(className) != -1);
|
||||
};
|
||||
|
||||
// Attempt to spawn one hero per player
|
||||
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
||||
for (let playerID = 1; playerID < TriggerHelper.GetNumberOfPlayers(); ++playerID)
|
||||
{
|
||||
let spawnPoints = cmpRangeManager.GetEntitiesByPlayer(playerID).sort((entity1, entity2) =>
|
||||
getSpawnPreference(entity2) - getSpawnPreference(entity1));
|
||||
|
||||
this.regicideHeroes[playerID] = this.SpawnRegicideHero(playerID, heroTemplates[playersCivs[playerID]], spawnPoints);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Spawn a random hero at one of the given locations (which are checked in order).
|
||||
* Garrison it if the location is a ship.
|
||||
*
|
||||
* @param spawnPoints - entity IDs at which to spawn
|
||||
*/
|
||||
Trigger.prototype.SpawnRegicideHero = function(playerID, heroTemplates, spawnPoints)
|
||||
{
|
||||
for (let heroTemplate of shuffleArray(heroTemplates))
|
||||
for (let spawnPoint of spawnPoints)
|
||||
{
|
||||
let cmpPosition = Engine.QueryInterface(spawnPoint, IID_Position);
|
||||
if (!cmpPosition || !cmpPosition.IsInWorld())
|
||||
continue;
|
||||
|
||||
// Consider nomad maps where units start on a ship
|
||||
let isShip = TriggerHelper.EntityHasClass(spawnPoint, "Ship");
|
||||
if (isShip)
|
||||
{
|
||||
let cmpGarrisonHolder = Engine.QueryInterface(spawnPoint, IID_GarrisonHolder);
|
||||
if (cmpGarrisonHolder.IsFull() ||
|
||||
!MatchesClassList(heroTemplate.classes, cmpGarrisonHolder.GetAllowedClasses()))
|
||||
continue;
|
||||
}
|
||||
|
||||
let hero = TriggerHelper.SpawnUnits(spawnPoint, heroTemplate.templateName, 1, playerID);
|
||||
if (!hero.length)
|
||||
continue;
|
||||
|
||||
hero = hero[0];
|
||||
|
||||
if (isShip)
|
||||
{
|
||||
let cmpUnitAI = Engine.QueryInterface(hero, IID_UnitAI);
|
||||
cmpUnitAI.Garrison(spawnPoint);
|
||||
}
|
||||
|
||||
return hero;
|
||||
}
|
||||
|
||||
error("Couldn't spawn hero for player " + playerID);
|
||||
return undefined;
|
||||
};
|
||||
|
||||
let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
|
||||
cmpTrigger.regicideHeroes = [];
|
||||
cmpTrigger.DoAfterDelay(0, "InitRegicideGame", {});
|
||||
cmpTrigger.RegisterTrigger("OnOwnershipChanged", "CheckRegicideDefeat", { "enabled": true });
|
@ -22,8 +22,8 @@ TriggerHelper.GetOwner = function(ent)
|
||||
};
|
||||
|
||||
/**
|
||||
* Can be used to "force" a building to spawn a group of entities.
|
||||
* Only works for buildings that can already train units.
|
||||
* Can be used to "force" a building/unit to spawn a group of entities.
|
||||
*
|
||||
* @param source Entity id of the point where they will be spawned from
|
||||
* @param template Name of the template
|
||||
* @param count Number of units to spawn
|
||||
|
@ -0,0 +1,15 @@
|
||||
{
|
||||
"TranslatedKeys": ["Title", "Description"],
|
||||
"Data":
|
||||
{
|
||||
"Title": "Regicide",
|
||||
"Description": "Defeat opponents by killing their hero",
|
||||
"Scripts":
|
||||
[
|
||||
"scripts/TriggerHelper.js",
|
||||
"scripts/ConquestCommon.js",
|
||||
"scripts/Conquest.js",
|
||||
"scripts/Regicide.js"
|
||||
]
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015 Wildfire Games.
|
||||
/* Copyright (C) 2016 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -148,6 +148,7 @@ void MapSettingsControl::CreateWidgets()
|
||||
gameTypes.Add(_T("conquest_units"));
|
||||
gameTypes.Add(_T("wonder"));
|
||||
gameTypes.Add(_T("endless"));
|
||||
gameTypes.Add(_T("regicide"));
|
||||
|
||||
wxFlexGridSizer* gridSizer = new wxFlexGridSizer(2, 5, 5);
|
||||
gridSizer->AddGrowableCol(1);
|
||||
|
Loading…
Reference in New Issue
Block a user