Fixes garrison behavior for ships - they no longer unload units underwater. See #893.

Fixes behavior when ships are destroyed (garrisoned units at the time
will also be killed).
Updates ship templates accordingly.

This was SVN commit r10020.
This commit is contained in:
historic_bruno 2011-08-17 01:43:23 +00:00
parent b9df1d3451
commit 7679d1073b
8 changed files with 132 additions and 58 deletions

View file

@ -11,7 +11,7 @@ GarrisonHolder.prototype.Schema =
"<text/>" +
"</element>" +
"<element name='EjectHealth'>" +
"<ref name='positiveDecimal'/>" +
"<ref name='nonNegativeDecimal'/>" +
"</element>" +
"<element name='BuffHeal'>" +
"<data type='positiveInteger'/>" +
@ -133,10 +133,29 @@ GarrisonHolder.prototype.Garrison = function(entity)
/**
* Simply eject the unit from the garrisoning entity without
* moving it
* Returns true if successful, false if not
*/
GarrisonHolder.prototype.Eject = function(entity)
GarrisonHolder.prototype.Eject = function(entity, forced)
{
var entityIndex = this.entities.indexOf(entity);
// Find spawning location
var cmpFootprint = Engine.QueryInterface(this.entity, IID_Footprint);
var pos = cmpFootprint.PickSpawnPoint(entity);
if (pos.y < 0)
{
// Error: couldn't find space satisfying the unit's passability criteria
if (forced)
{ // If ejection is forced, we need to continue, so use center of the building
var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
pos = cmpPosition.GetPosition();
}
else
{ // Fail
return false;
}
}
this.spaceOccupied -= 1;
this.entities.splice(entityIndex, 1);
@ -146,25 +165,11 @@ GarrisonHolder.prototype.Eject = function(entity)
cmpUnitAI.Ungarrison();
}
var cmpFootprint = Engine.QueryInterface(this.entity, IID_Footprint);
var pos = cmpFootprint.PickSpawnPoint(entity);
if (pos.y < 0)
{
// Whoops, something went wrong (maybe there wasn't any space to place the unit).
// What should we do here?
// For now, just move the unit into the middle of the building where it'll probably get stuck
var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
pos = cmpPosition.GetPosition();
var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
var notification = {"player": cmpPlayer.GetPlayerID(), "message": "Can't find free space to ungarrison unit"};
var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
cmpGUIInterface.PushNotification(notification);
}
var cmpNewPosition = Engine.QueryInterface(entity, IID_Position);
cmpNewPosition.JumpTo(pos.x, pos.z);
// TODO: what direction should they face in?
return true;
};
/**
@ -193,29 +198,51 @@ GarrisonHolder.prototype.OrderWalkToRallyPoint = function(entities)
/**
* Unload units from the garrisoning entity and order them
* to move to the Rally Point
* Returns true if successful, false if not
*/
GarrisonHolder.prototype.Unload = function(entity)
GarrisonHolder.prototype.Unload = function(entity, forced)
{
this.Eject(entity);
this.OrderWalkToRallyPoint([entity]);
this.UpdateGarrisonFlag();
if (this.Eject(entity, forced))
{
this.OrderWalkToRallyPoint([entity]);
this.UpdateGarrisonFlag();
return true;
}
return false;
};
/**
* Unload all units from the entity
* Returns true if all successful, false if not
*/
GarrisonHolder.prototype.UnloadAll = function()
GarrisonHolder.prototype.UnloadAll = function(forced)
{
// The entities list is saved to a temporary variable
// because during each loop an element is removed
// from the list
var entities = this.entities.splice(0);
// Make copy of entity list
var entities = [];
for each (var entity in this.entities)
{
entities.push(entity);
}
var ejectedEntities = [];
var success = true;
for each (var entity in entities)
{
this.Eject(entity);
if (this.Eject(entity, forced))
{
ejectedEntities.push(entity);
}
else
{
success = false;
}
}
this.OrderWalkToRallyPoint(entities);
this.OrderWalkToRallyPoint(ejectedEntities);
this.UpdateGarrisonFlag();
return success;
};
/**
@ -226,7 +253,26 @@ GarrisonHolder.prototype.OnHealthChanged = function(msg)
{
if (!this.HasEnoughHealth())
{
this.UnloadAll();
// We have to be careful of our passability
// ships: not land passable, so assume units have drowned in a shipwreck
// building: land passable, so units can be ejected freely
var classes = (Engine.QueryInterface(this.entity, IID_Identity)).GetClassesList();
if (classes.indexOf("Ship") != -1)
{ // Ship - kill all units
for each (var entity in this.entities)
{
var cmpHealth = Engine.QueryInterface(entity, IID_Health);
if (cmpHealth)
{
cmpHealth.Kill();
}
}
this.entities = [];
}
else
{ // Building - force ejection
this.UnloadAll(true);
}
}
};
@ -302,19 +348,34 @@ GarrisonHolder.prototype.OnDestroy = function()
*/
GarrisonHolder.prototype.OnGlobalOwnershipChanged = function(msg)
{
if (this.entities.indexOf(msg.entity) != -1)
var entityIndex = this.entities.indexOf(msg.entity);
if (entityIndex != -1)
{
// If the entity is dead, remove it directly instead of ejecting the corpse
var cmpHealth = Engine.QueryInterface(msg.entity, IID_Health);
if (cmpHealth && cmpHealth.GetHitpoints() == 0)
{
this.entities.splice(this.entities.indexOf(msg.entity), 1);
this.entities.splice(entityIndex, 1);
}
else
{
// Otherwise the unit probably got captured somehow and we don't want it
// any more, so eject it
this.Eject(msg.entity);
// We have to be careful of our passability
// ships: not land passable, assume unit was thrown overboard or something
// building: land passable, unit can be ejected freely
var classes = (Engine.QueryInterface(this.entity, IID_Identity)).GetClassesList();
if (classes.indexOf("Ship") != -1)
{ // Ship - kill unit
var cmpHealth = Engine.QueryInterface(msg.entity, IID_Health);
if (cmpHealth)
{
cmpHealth.Kill();
}
this.entities.splice(entityIndex, 1);
}
else
{ // Building - force ejection
this.Eject(msg.entity, true);
}
}
}
};
@ -324,9 +385,10 @@ GarrisonHolder.prototype.OnGlobalOwnershipChanged = function(msg)
*/
GarrisonHolder.prototype.OnGlobalEntityRenamed = function(msg)
{
if (this.entities.indexOf(msg.entity) != -1)
var entityIndex = this.entities.indexOf(msg.entity);
if (entityIndex != -1)
{
this.entities[this.entities.indexOf(msg.entity)] = msg.newentity;
this.entities[entityIndex] = msg.newentity;
}
};

View file

@ -262,8 +262,13 @@ function ProcessCommand(player, cmd)
if (CanControlUnit(cmd.garrisonHolder, player, controlAllUnits))
{
var cmpGarrisonHolder = Engine.QueryInterface(cmd.garrisonHolder, IID_GarrisonHolder);
if (cmpGarrisonHolder)
cmpGarrisonHolder.Unload(cmd.entity);
if (!cmpGarrisonHolder || !cmpGarrisonHolder.Unload(cmd.entity))
{
var cmpPlayer = QueryPlayerIDInterface(player, IID_Player);
var notification = {"player": cmpPlayer.GetPlayerID(), "message": "Unable to ungarrison unit"};
var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
cmpGUIInterface.PushNotification(notification);
}
}
break;
@ -271,7 +276,13 @@ function ProcessCommand(player, cmd)
if (CanControlUnit(cmd.garrisonHolder, player, controlAllUnits))
{
var cmpGarrisonHolder = Engine.QueryInterface(cmd.garrisonHolder, IID_GarrisonHolder);
cmpGarrisonHolder.UnloadAll();
if (!cmpGarrisonHolder || !cmpGarrisonHolder.UnloadAll())
{
var cmpPlayer = QueryPlayerIDInterface(player, IID_Player);
var notification = {"player": cmpPlayer.GetPlayerID(), "message": "Unable to ungarrison all units"};
var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
cmpGUIInterface.PushNotification(notification);
}
}
break;

View file

@ -25,6 +25,7 @@
</Health>
<Identity>
<GenericName>Ship</GenericName>
<Classes datatype="tokens">Ship</Classes>
</Identity>
<Obstruction>
<Unit radius="8.0"/>

View file

@ -30,9 +30,9 @@
</Cost>
<GarrisonHolder>
<Max>20</Max>
<EjectHealth>0.1</EjectHealth>
<List datatype="tokens">Support Infantry Cavalry</List>
<BuffHeal>1</BuffHeal>
<EjectHealth>0</EjectHealth>
<List datatype="tokens">Support Infantry Cavalry</List>
<BuffHeal>1</BuffHeal>
</GarrisonHolder>
<Health>
<Max>300</Max>
@ -40,7 +40,7 @@
<Identity>
<GenericName>Light Warship</GenericName>
<Tooltip>Light Warship.</Tooltip>
<Classes datatype="tokens">Warship</Classes>
<Classes datatype="tokens">Warship</Classes>
</Identity>
<ResourceGatherer disable=""/>
<Sound>

View file

@ -11,9 +11,9 @@
</Footprint>
<GarrisonHolder>
<Max>1</Max>
<EjectHealth>0.1</EjectHealth>
<List datatype="tokens">Support Infantry</List>
<BuffHeal>1</BuffHeal>
<EjectHealth>0</EjectHealth>
<List datatype="tokens">Support Infantry</List>
<BuffHeal>1</BuffHeal>
</GarrisonHolder>
<Identity>
<GenericName>Fishing Boat</GenericName>

View file

@ -13,9 +13,9 @@
</Cost>
<GarrisonHolder>
<Max>15</Max>
<EjectHealth>0.1</EjectHealth>
<List datatype="tokens">Support Infantry Cavalry</List>
<BuffHeal>1</BuffHeal>
<EjectHealth>0</EjectHealth>
<List datatype="tokens">Support Infantry Cavalry</List>
<BuffHeal>1</BuffHeal>
</GarrisonHolder>
<Health>
<Max>200</Max>

View file

@ -26,9 +26,9 @@
</Cost>
<GarrisonHolder>
<Max>50</Max>
<EjectHealth>0.1</EjectHealth>
<List datatype="tokens">Support Infantry Cavalry Siege</List>
<BuffHeal>1</BuffHeal>
<EjectHealth>0</EjectHealth>
<List datatype="tokens">Support Infantry Cavalry Siege</List>
<BuffHeal>1</BuffHeal>
</GarrisonHolder>
<Health>
<Max>500</Max>
@ -36,7 +36,7 @@
<Identity>
<GenericName>Heavy Warship</GenericName>
<Tooltip>Heavy Warship.</Tooltip>
<Classes datatype="tokens">Warship</Classes>
<Classes datatype="tokens">Warship</Classes>
</Identity>
<ResourceGatherer disable=""/>
<StatusBars>

View file

@ -30,9 +30,9 @@
</Cost>
<GarrisonHolder>
<Max>30</Max>
<EjectHealth>0.1</EjectHealth>
<List datatype="tokens">Support Infantry Cavalry Siege</List>
<BuffHeal>1</BuffHeal>
<EjectHealth>0</EjectHealth>
<List datatype="tokens">Support Infantry Cavalry Siege</List>
<BuffHeal>1</BuffHeal>
</GarrisonHolder>
<Health>
<Max>400</Max>
@ -40,7 +40,7 @@
<Identity>
<GenericName>Medium Warship</GenericName>
<Tooltip>Medium Warship.</Tooltip>
<Classes datatype="tokens">Warship</Classes>
<Classes datatype="tokens">Warship</Classes>
</Identity>
<ResourceGatherer disable=""/>
<Sound>