1
0
forked from 0ad/0ad

Added garrison & repair button on the unit's command panel

This was SVN commit r8756.
This commit is contained in:
evanssthomas 2010-12-01 03:01:17 +00:00
parent 1a1bed1294
commit c09d6dcb83
8 changed files with 302 additions and 162 deletions

Binary file not shown.

Binary file not shown.

View File

@ -12,6 +12,11 @@ const SDLK_LALT = 308;
// TODO: these constants should be defined somewhere else instead, in
// case any other code wants to use them too
const ACTION_NONE = 0;
const ACTION_GARRISON = 1;
const ACTION_REPAIR = 2;
var preSelectedAction = ACTION_NONE;
var INPUT_NORMAL = 0;
var INPUT_SELECTING = 1;
var INPUT_BANDBOXING = 2;
@ -19,6 +24,7 @@ var INPUT_BUILDING_PLACEMENT = 3;
var INPUT_BUILDING_CLICK = 4;
var INPUT_BUILDING_DRAG = 5;
var INPUT_BATCHTRAINING = 6;
var INPUT_PRESELECTEDACTION = 7;
var inputState = INPUT_NORMAL;
@ -43,15 +49,18 @@ var prevClickedEntity = 0;
function updateCursor()
{
if (!mouseIsOverObject && inputState == INPUT_NORMAL)
if (!mouseIsOverObject)
{
var action = determineAction(mouseX, mouseY);
if (action)
if (inputState == INPUT_NORMAL || inputState == INPUT_PRESELECTEDACTION)
{
if (action.cursor)
if (action)
{
Engine.SetCursor(action.cursor);
return;
if (action.cursor)
{
Engine.SetCursor(action.cursor);
return;
}
}
}
}
@ -70,6 +79,109 @@ function findGatherType(gatherer, supply)
return undefined;
}
function getActionInfo(action, target)
{
var selection = g_Selection.toList();
// If the selection doesn't exist, no action
var entState = GetEntityState(selection[0]);
if (!entState)
return {"possible": false};
// If the selection isn't friendly units, no action
var player = Engine.GetPlayerID();
if (entState.player != player && !g_DevSettings.controlAll)
return {"possible": false};
// Work out whether the selection can have rally points
var haveRallyPoints = selection.every(function(ent) {
var entState = GetEntityState(ent);
return entState && entState.rallyPoint;
});
if (!target)
{
if (action == "set-rallypoint" && haveRallyPoints)
return {"possible": true};
else if (action == "move")
return {"possible": true};
else
return {"possible": false};
}
if (haveRallyPoints && selection.indexOf(target) != -1 && action == "unset-rallypoint")
return {"possible": true};
// Look at the first targeted entity
// (TODO: maybe we eventually want to look at more, and be more context-sensitive?
// e.g. prefer to attack an enemy unit, even if some friendly units are closer to the mouse)
var targetState = GetEntityState(target);
// If we selected buildings with rally points, and then click on one of those selected
// buildings, we should remove the rally point
//if (haveRallyPoints && selection.indexOf(target) != -1)
// return {"type": "unset-rallypoint"};
// Check if the target entity is a resource, dropsite, foundation, or enemy unit.
// Check if any entities in the selection can gather the requested resource,
// can return to the dropsite, can build the foundation, or can attack the enemy
var simState = Engine.GuiInterfaceCall("GetSimulationState");
for each (var entityID in selection)
{
var entState = GetEntityState(entityID);
if (!entState)
continue;
// Get entity owner diplomacy array
var diplomacy = simState.players[entState.player].diplomacy;
var playerOwned = ((targetState.player == entState.player)? true : false);
var enemyOwned = ((targetState.player != entState.player && targetState.player && diplomacy[targetState.player - 1] < 0)? true : false);
var gaiaOwned = ((targetState.player == 0)? true : false);
// Find the resource type we're carrying, if any
var carriedType = undefined;
if (entState.resourceCarrying && entState.resourceCarrying.length)
carriedType = entState.resourceCarrying[0].type;
switch (action)
{
case "garrison":
if (isUnit(entState) && targetState.garrisonHolder && playerOwned)
return {"possible": true};
break;
case "gather":
if (targetState.resourceSupply && (playerOwned || gaiaOwned))
{
var resource = findGatherType(entState.resourceGatherRates, targetState.resourceSupply);
if (resource)
return {"possible": true, "cursor": "action-gather-" + resource};
}
break;
case "returnresource":
if (targetState.resourceDropsite && playerOwned && carriedType && targetState.resourceDropsite.types.indexOf(carriedType) != -1)
return {"possible": true, "cursor": "action-return-" + carriedType};
break;
case "build":
if (targetState.foundation && entState.buildEntities && playerOwned)
return {"possible": true};
break;
case "repair":
if (entState.buildEntities && targetState.needsRepair && playerOwned)
return {"possible": true};
break;
case "attack":
if (entState.attack && (enemyOwned || gaiaOwned))
return {"possible": true};
}
}
if (action == "move")
return {"possible": true};
else
return {"possible": false};
}
/**
* Determine the context-sensitive action that should be performed when the mouse is at (x,y)
*/
@ -79,8 +191,11 @@ function determineAction(x, y, fromMinimap)
// No action if there's no selection
if (!selection.length)
{
preSelectedAction = ACTION_NONE;
return undefined;
}
// If the selection doesn't exist, no action
var entState = GetEntityState(selection[0]);
if (!entState)
@ -98,84 +213,64 @@ function determineAction(x, y, fromMinimap)
});
var targets = [];
var target = undefined;
var type = "none";
var cursor = "";
var targetState = undefined;
if (!fromMinimap)
targets = Engine.PickEntitiesAtPoint(x, y);
// If there's a target unit
if (targets.length)
{
// Look at the first targeted entity
// (TODO: maybe we eventually want to look at more, and be more context-sensitive?
// e.g. prefer to attack an enemy unit, even if some friendly units are closer to the mouse)
var targetState = GetEntityState(targets[0]);
// If we selected buildings with rally points, and then click on one of those selected
// buildings, we should remove the rally point
if (haveRallyPoints && selection.indexOf(targets[0]) != -1)
return {"type": "unset-rallypoint"};
// Check if the target entity is a resource, dropsite, foundation, or enemy unit.
// Check if any entities in the selection can gather the requested resource,
// can return to the dropsite, can build the foundation, or can attack the enemy
var simState = Engine.GuiInterfaceCall("GetSimulationState");
for each (var entityID in selection)
{
var entState = GetEntityState(entityID);
if (!entState)
continue;
// Get entity owner diplomacy array
var diplomacy = simState.players[entState.player].diplomacy;
var playerOwned = ((targetState.player == entState.player)? true : false);
var enemyOwned = ((targetState.player != entState.player && targetState.player && diplomacy[targetState.player - 1] < 0)? true : false);
var gaiaOwned = ((targetState.player == 0)? true : false);
// Find the resource type we're carrying, if any
var carriedType = undefined;
if (entState.resourceCarrying && entState.resourceCarrying.length)
carriedType = entState.resourceCarrying[0].type;
if (targetState.garrisonHolder && playerOwned && Engine.HotkeyIsPressed("session.garrison"))
{
return {"type": "garrison", "cursor": "action-garrison", "target": targets[0]};
}
else if (targetState.resourceSupply && (playerOwned || gaiaOwned))
{
// If the target is a resource and we have the right kind of resource gatherers selected, then gather
// If the target is a foundation and we have builders selected, then build (or repair)
// If the target is an enemy, then attack
var resource = findGatherType(entState.resourceGatherRates, targetState.resourceSupply);
if (resource)
return {"type": "gather", "cursor": "action-gather-"+resource, "target": targets[0]};
}
else if (targetState.resourceDropsite && playerOwned && carriedType &&
targetState.resourceDropsite.types.indexOf(carriedType) != -1)
{
return {"type": "returnresource", "cursor": "action-return-"+carriedType, "target": targets[0]};
}
else if (targetState.foundation && entState.buildEntities && playerOwned)
{
return {"type": "build", "cursor": "action-build", "target": targets[0]};
}
else if (entState.buildEntities && targetState.needsRepair && playerOwned)
{
return {"type": "build", "cursor": "action-repair", "target": targets[0]};
}
else if (entState.attack && (enemyOwned || gaiaOwned))
{ // TODO: Decide how we want to treat gaia
return {"type": "attack", "cursor": "action-attack", "target": targets[0]};
}
}
target = targets[0];
}
// If we don't do anything more specific:
// If all selected entities are buildings, set rally points, else walk
if (haveRallyPoints)
return {"type": "set-rallypoint"};
if (preSelectedAction != ACTION_NONE)
{
switch (preSelectedAction)
{
case ACTION_GARRISON:
if (getActionInfo("garrison", target).possible)
return {"type": "garrison", "cursor": "action-garrison", "target": target};
else
return {"type": "none", "cursor": "action-garrison-disabled", "target": undefined};
break;
case ACTION_REPAIR:
if (getActionInfo("repair", target).possible)
return {"type": "repair", "cursor": "action-repair", "target": target};
else
return {"type": "none", "cursor": "action-repair-disabled", "target": undefined};
break;
}
}
else if (Engine.HotkeyIsPressed("session.garrison"))
{
if (getActionInfo("garrison", target).possible)
return {"type": "garrison", "cursor": "action-garrison", "target": target};
else
return {"type": "none", "cursor": "action-garrison-disabled", "target": undefined};
}
else
return {"type": "move"};
{
var actionInfo = undefined;
if ((actionInfo = getActionInfo("gather", target)).possible)
return {"type": "gather", "cursor": actionInfo.cursor, "target": target};
else if ((actionInfo = getActionInfo("returnresource", target)).possible)
return {"type": "returnresource", "cursor": actionInfo.cursor, "target": target};
else if (getActionInfo("build", target).possible)
return {"type": "build", "cursor": "action-build", "target": target};
else if (getActionInfo("repair", target).possible)
return {"type": "build", "cursor": "action-repair", "target": target};
else if (getActionInfo("attack", target).possible)
return {"type": "attack", "cursor": "action-attack", "target": target};
else if(getActionInfo("set-rallypoint", target).possible)
return {"type": "set-rallypoint"};
else if(getActionInfo("unset-rallypoint", target).possible)
return {"type": "unset-rallypoint"};
else if (getActionInfo("move", target).possible)
return {"type": "move"};
}
return {"type": type, "cursor": cursor, "target": target};
}
@ -502,75 +597,32 @@ function handleInputAfterGui(ev)
var action = determineAction(ev.x, ev.y);
if (!action)
break;
var selection = g_Selection.toList();
// If shift is down, add the order to the unit's order queue instead
// of running it immediately
var queued = Engine.HotkeyIsPressed("session.queue");
switch (action.type)
{
case "move":
var target = Engine.GetTerrainAtPoint(ev.x, ev.y);
Engine.PostNetworkCommand({"type": "walk", "entities": selection, "x": target.x, "z": target.z, "queued": queued});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_walk", "entity": selection[0] });
return true;
case "attack":
Engine.PostNetworkCommand({"type": "attack", "entities": selection, "target": action.target, "queued": queued});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_attack", "entity": selection[0] });
return true;
case "build": // (same command as repair)
case "repair":
Engine.PostNetworkCommand({"type": "repair", "entities": selection, "target": action.target, "queued": queued});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_repair", "entity": selection[0] });
return true;
case "gather":
Engine.PostNetworkCommand({"type": "gather", "entities": selection, "target": action.target, "queued": queued});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_gather", "entity": selection[0] });
return true;
case "returnresource":
Engine.PostNetworkCommand({"type": "returnresource", "entities": selection, "target": action.target, "queued": queued});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_gather", "entity": selection[0] });
return true;
case "garrison":
Engine.PostNetworkCommand({"type": "garrison", "entities": selection, "target": action.target, "queued": queued});
//Need to play some sound here??
return true;
case "set-rallypoint":
var target = Engine.GetTerrainAtPoint(ev.x, ev.y);
Engine.PostNetworkCommand({"type": "set-rallypoint", "entities": selection, "x": target.x, "z": target.z});
// Display rally point at the new coordinates, to avoid display lag
Engine.GuiInterfaceCall("DisplayRallyPoint", {
"entities": selection,
"x": target.x,
"z": target.z
});
return true;
case "unset-rallypoint":
var target = Engine.GetTerrainAtPoint(ev.x, ev.y);
Engine.PostNetworkCommand({"type": "unset-rallypoint", "entities": selection});
// Remove displayed rally point
Engine.GuiInterfaceCall("DisplayRallyPoint", {
"entities": []
});
return true;
default:
error("Invalid action.type "+action.type);
}
return doAction(action, ev);
}
break;
}
break;
case INPUT_PRESELECTEDACTION:
switch (ev.type)
{
case "mousebuttondown":
if (ev.button == SDL_BUTTON_LEFT && preSelectedAction != ACTION_NONE)
{
var action = determineAction(ev.x, ev.y);
if (!action)
break;
preSelectedAction = ACTION_NONE;
inputState = INPUT_NORMAL;
return doAction(action, ev);
}
else if (ev.button == SDL_BUTTON_RIGHT && preSelectedAction != ACTION_NONE)
{
preSelectedAction = ACTION_NONE;
inputState = INPUT_NORMAL;
break;
}
}
break;
case INPUT_SELECTING:
switch (ev.type)
{
@ -725,6 +777,77 @@ function handleInputAfterGui(ev)
return false;
}
function doAction(action, ev)
{
var selection = g_Selection.toList();
// If shift is down, add the order to the unit's order queue instead
// of running it immediately
var queued = Engine.HotkeyIsPressed("session.queue");
switch (action.type)
{
case "move":
var target = Engine.GetTerrainAtPoint(ev.x, ev.y);
Engine.PostNetworkCommand({"type": "walk", "entities": selection, "x": target.x, "z": target.z, "queued": queued});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_walk", "entity": selection[0] });
return true;
case "attack":
Engine.PostNetworkCommand({"type": "attack", "entities": selection, "target": action.target, "queued": queued});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_attack", "entity": selection[0] });
return true;
case "build": // (same command as repair)
case "repair":
Engine.PostNetworkCommand({"type": "repair", "entities": selection, "target": action.target, "queued": queued});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_repair", "entity": selection[0] });
return true;
case "gather":
Engine.PostNetworkCommand({"type": "gather", "entities": selection, "target": action.target, "queued": queued});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_gather", "entity": selection[0] });
return true;
case "returnresource":
Engine.PostNetworkCommand({"type": "returnresource", "entities": selection, "target": action.target, "queued": queued});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_gather", "entity": selection[0] });
return true;
case "garrison":
Engine.PostNetworkCommand({"type": "garrison", "entities": selection, "target": action.target, "queued": queued});
//Need to play some sound here??
return true;
case "set-rallypoint":
var target = Engine.GetTerrainAtPoint(ev.x, ev.y);
Engine.PostNetworkCommand({"type": "set-rallypoint", "entities": selection, "x": target.x, "z": target.z});
// Display rally point at the new coordinates, to avoid display lag
Engine.GuiInterfaceCall("DisplayRallyPoint", {
"entities": selection,
"x": target.x,
"z": target.z
});
return true;
case "unset-rallypoint":
var target = Engine.GetTerrainAtPoint(ev.x, ev.y);
Engine.PostNetworkCommand({"type": "unset-rallypoint", "entities": selection});
// Remove displayed rally point
Engine.GuiInterfaceCall("DisplayRallyPoint", {
"entities": []
});
return true;
case "none":
return true;
default:
error("Invalid action.type "+action.type);
return false;
}
}
function handleMinimapEvent(target)
{
// Partly duplicated from handleInputAfterGui(), but with the input being
@ -873,6 +996,14 @@ function performCommand(entity, commandName)
g_SessionDialog.open("Delete", message, null, 340, 160, deleteFunction);
}
break;
case "garrison":
inputState = INPUT_PRESELECTEDACTION;
preSelectedAction = ACTION_GARRISON;
break;
case "repair":
inputState = INPUT_PRESELECTEDACTION;
preSelectedAction = ACTION_REPAIR;
break;
case "unload-all":
unloadAll(entity);
break;

View File

@ -143,9 +143,7 @@ function isUnit(entState)
{
var classes = entState.identity.classes;
if (classes && classes.length)
for (var i = 0; i < classes.length; i++)
if (classes[i] == "Unit")
return true;
return (classes.indexOf("Unit") != -1);
}
return false;
}
@ -156,9 +154,7 @@ function isAnimal(entState)
{
var classes = entState.identity.classes;
if (classes && classes.length)
for (var i = 0; i < classes.length; i++)
if (classes[i] == "Animal")
return true;
return (classes.indexOf("Animal") != -1);
}
return false;
}
@ -169,9 +165,7 @@ function isStructure(entState)
{
var classes = entState.identity.classes;
if (classes && classes.length)
for (var i = 0; i < classes.length; i++)
if (classes[i] == "Structure")
return true;
return (classes.indexOf("Structure") != -1);
}
return false;
}
@ -182,9 +176,7 @@ function isDefensive(entState)
{
var classes = entState.identity.classes;
if (classes && classes.length)
for (var i = 0; i < classes.length; i++)
if (classes[i] == "Defensive")
return true;
return (classes.indexOf("Defensive") != -1);
}
return false;
}
@ -259,6 +251,10 @@ function getCommandImage(commandName)
return "kill_small.png";
case "unload-all":
return "garrison.png";
case "garrison":
return "garrison.png";
case "repair":
return "repair.png";
default:
return "";
}
@ -290,6 +286,10 @@ function getEntityCommandsList(entState)
if (entState.garrisonHolder)
commands.push("unload-all");
commands.push("delete");
if (isUnit(entState))
commands.push("garrison");
if (entState.buildEntities)
commands.push("repair");
return commands;
}

View File

@ -234,6 +234,12 @@ var UnitFsmSpec = {
cmpFormation.Disband();
},
"Order.Garrison": function(msg) {
var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
cmpFormation.CallMemberFunction("Garrison", [msg.data.target, false]);
cmpFormation.Disband();
},
"IDLE": {
},
@ -1175,6 +1181,7 @@ UnitAI.prototype.ComputeWalkingDistance = function()
case "Gather":
case "ReturnResource":
case "Repair":
case "Garrison":
// Find the target unit's position
var cmpTargetPosition = Engine.QueryInterface(order.data.target, IID_Position);
if (!cmpTargetPosition || !cmpTargetPosition.IsInWorld())

View File

@ -175,15 +175,9 @@ function ProcessCommand(player, cmd)
var targetCmpOwnership = Engine.QueryInterface(cmd.target, IID_Ownership);
if (!targetCmpOwnership || targetCmpOwnership.GetOwner() != player)
break;
for each (var ent in cmd.entities)
{
var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
if (!cmpOwnership || cmpOwnership.GetOwner() != player)
break;
var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
if (cmpUnitAI)
cmpUnitAI.Garrison(cmd.target);
}
var cmpUnitAI = GetFormationUnitAI(cmd.entities);
if (cmpUnitAI)
cmpUnitAI.Garrison(cmd.target);
break;
case "unload":