Adds notification tooltips for building placement, fixes #921.
Tweaks info tooltip borders and padding to improve readability. This was SVN commit r13191.
This commit is contained in:
parent
6e6a25dbce
commit
ca92e50048
@ -74,7 +74,17 @@
|
||||
<image
|
||||
backcolor="0 0 0 191"
|
||||
size="0 0 100% 100%"
|
||||
border="false"
|
||||
border="true"
|
||||
bordercolor="white"
|
||||
/>
|
||||
</sprite>
|
||||
|
||||
<sprite name="BackgroundErrorTooltip">
|
||||
<image
|
||||
backcolor="0 0 0 191"
|
||||
size="0 0 100% 100%"
|
||||
border="true"
|
||||
bordercolor="red"
|
||||
/>
|
||||
</sprite>
|
||||
|
||||
|
@ -82,16 +82,20 @@ function updateCursorAndTooltip()
|
||||
if (!tooltipSet)
|
||||
informationTooltip.hidden = true;
|
||||
|
||||
var wallDragTooltip = getGUIObjectByName("wallDragTooltip");
|
||||
if (placementSupport.wallDragTooltip)
|
||||
var placementTooltip = getGUIObjectByName("placementTooltip");
|
||||
if (placementSupport.tooltipMessage)
|
||||
{
|
||||
wallDragTooltip.caption = placementSupport.wallDragTooltip;
|
||||
wallDragTooltip.hidden = false;
|
||||
if (placementSupport.tooltipError)
|
||||
placementTooltip.sprite = "BackgroundErrorTooltip";
|
||||
else
|
||||
placementTooltip.sprite = "BackgroundInformationTooltip";
|
||||
placementTooltip.caption = placementSupport.tooltipMessage;
|
||||
placementTooltip.hidden = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
wallDragTooltip.caption = "";
|
||||
wallDragTooltip.hidden = true;
|
||||
placementTooltip.caption = "";
|
||||
placementTooltip.hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,13 +109,18 @@ function updateBuildingPlacementPreview()
|
||||
{
|
||||
if (placementSupport.template && placementSupport.position)
|
||||
{
|
||||
return Engine.GuiInterfaceCall("SetBuildingPlacementPreview", {
|
||||
var result = Engine.GuiInterfaceCall("SetBuildingPlacementPreview", {
|
||||
"template": placementSupport.template,
|
||||
"x": placementSupport.position.x,
|
||||
"z": placementSupport.position.z,
|
||||
"angle": placementSupport.angle,
|
||||
"actorSeed": placementSupport.actorSeed
|
||||
});
|
||||
|
||||
// Show placement info tooltip if invalid position
|
||||
placementSupport.tooltipError = !result.success;
|
||||
placementSupport.tooltipMessage = result.success ? "" : result.message;
|
||||
return result.success;
|
||||
}
|
||||
}
|
||||
else if (placementSupport.mode === "wall")
|
||||
@ -862,10 +871,10 @@ function handleInputBeforeGui(ev, hoveredObject)
|
||||
|
||||
if (result && result.cost)
|
||||
{
|
||||
placementSupport.wallDragTooltip = getEntityCostTooltip(result);
|
||||
placementSupport.tooltipMessage = getEntityCostTooltip(result);
|
||||
var neededResources = Engine.GuiInterfaceCall("GetNeededResources", result.cost);
|
||||
if (neededResources)
|
||||
placementSupport.wallDragTooltip += getNeededResourcesTooltip(neededResources);
|
||||
placementSupport.tooltipMessage += getNeededResourcesTooltip(neededResources);
|
||||
}
|
||||
|
||||
break;
|
||||
@ -892,7 +901,7 @@ function handleInputBeforeGui(ev, hoveredObject)
|
||||
}
|
||||
else
|
||||
{
|
||||
placementSupport.wallDragTooltip = "Cannot build wall here!";
|
||||
placementSupport.tooltipMessage = "Cannot build wall here!";
|
||||
}
|
||||
|
||||
updateBuildingPlacementPreview();
|
||||
|
@ -13,11 +13,12 @@ PlacementSupport.prototype.Reset = function()
|
||||
this.mode = null;
|
||||
this.position = null;
|
||||
this.template = null;
|
||||
this.tooltipMessage = ""; // tooltip text to show while the user is placing a building
|
||||
this.tooltipError = false;
|
||||
this.wallSet = null; // maps types of wall pieces ("tower", "long", "short", ...) to template names
|
||||
this.wallSnapEntities = null; // list of candidate entities to snap the starting and (!) ending positions to when building walls
|
||||
this.wallEndPosition = null;
|
||||
this.wallSnapEntitiesIncludeOffscreen = false; // should the next update of the snap candidate list include offscreen towers?
|
||||
this.wallDragTooltip = null; // tooltip text while the user is draggin the wall. Used to indicate the current cost to build the wall.
|
||||
|
||||
this.SetDefaultAngle();
|
||||
this.RandomizeActorSeed();
|
||||
|
@ -688,9 +688,9 @@
|
||||
<object name="informationTooltip" type="tooltip" independent="true" style="informationTooltip"/>
|
||||
|
||||
<!-- ================================ ================================ -->
|
||||
<!-- Wall-dragging tooltip. Shows the total cost of building a wall while the player is dragging it. -->
|
||||
<!-- Building placement info tooltip -->
|
||||
<!-- ================================ ================================ -->
|
||||
<object name="wallDragTooltip" type="tooltip" independent="true" style="informationTooltip"/>
|
||||
<object name="placementTooltip" type="tooltip" independent="true" style="informationTooltip"/>
|
||||
|
||||
<!-- ================================ ================================ -->
|
||||
<!-- START of BOTTOM PANEL -->
|
||||
|
@ -206,7 +206,7 @@
|
||||
|
||||
<style name="informationTooltip"
|
||||
anchor="top"
|
||||
buffer_zone="4"
|
||||
buffer_zone="6"
|
||||
font="serif-bold-14"
|
||||
maxwidth="300"
|
||||
offset="16 32"
|
||||
|
@ -71,11 +71,50 @@ BuildRestrictions.prototype.Init = function()
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true iff this entity can be built at its current position.
|
||||
* Checks whether building placement is valid
|
||||
* 1. Visibility is not hidden (may be fogged or visible)
|
||||
* 2. Check foundation
|
||||
* a. Doesn't obstruct foundation-blocking entities
|
||||
* b. On valid terrain, based on passability class
|
||||
* 3. Territory type is allowed
|
||||
* 4. Dock is on shoreline and facing into water
|
||||
* 5. Distance constraints satisfied
|
||||
*
|
||||
* Returns result object:
|
||||
* {
|
||||
* "success": true iff the placement is valid, else false
|
||||
* "message": message to display in UI for invalid placement, else empty string
|
||||
* }
|
||||
*/
|
||||
BuildRestrictions.prototype.CheckPlacement = function()
|
||||
{
|
||||
// TODO: Return error code for invalid placement, which can be handled by the UI
|
||||
var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
|
||||
var name = cmpIdentity ? cmpIdentity.GetGenericName() : "Building";
|
||||
|
||||
var result = {
|
||||
"success": false,
|
||||
"message": name+" cannot be built due to unknown error",
|
||||
};
|
||||
|
||||
// TODO: AI has no visibility info
|
||||
var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
|
||||
if (!cmpPlayer.IsAI())
|
||||
{
|
||||
// Check whether it's in a visible or fogged region
|
||||
// tell GetLosVisibility to force RetainInFog because preview entities set this to false,
|
||||
// which would show them as hidden instead of fogged
|
||||
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
||||
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
|
||||
if (!cmpRangeManager || !cmpOwnership)
|
||||
return result; // Fail
|
||||
|
||||
var explored = (cmpRangeManager.GetLosVisibility(this.entity, cmpOwnership.GetOwner(), true) != "hidden");
|
||||
if (!explored)
|
||||
{
|
||||
result.message = name+" cannot be built in unexplored area";
|
||||
return result; // Fail
|
||||
}
|
||||
}
|
||||
|
||||
// Check obstructions and terrain passability
|
||||
var passClassName = "";
|
||||
@ -91,20 +130,35 @@ BuildRestrictions.prototype.CheckPlacement = function()
|
||||
}
|
||||
|
||||
var cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction);
|
||||
if (!cmpObstruction || !cmpObstruction.CheckFoundation(passClassName))
|
||||
if (!cmpObstruction)
|
||||
return result; // Fail
|
||||
|
||||
var ret = cmpObstruction.CheckFoundation(passClassName);
|
||||
if (ret != "success")
|
||||
{
|
||||
return false; // Fail
|
||||
switch (ret)
|
||||
{
|
||||
case "fail_error":
|
||||
case "fail_no_obstruction":
|
||||
error("CheckPlacement: Error returned from CheckFoundation");
|
||||
break;
|
||||
case "fail_obstructs_foundation":
|
||||
result.message = name+" cannot be built on another building or resource";
|
||||
break;
|
||||
case "fail_terrain_class":
|
||||
// TODO: be more specific and/or list valid terrain?
|
||||
result.message = name+" cannot be built on invalid terrain";
|
||||
}
|
||||
return result; // Fail
|
||||
}
|
||||
|
||||
|
||||
// Check territory restrictions
|
||||
var cmpTerritoryManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TerritoryManager);
|
||||
var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
|
||||
var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
|
||||
if (!(cmpTerritoryManager && cmpPlayer && cmpPosition && cmpPosition.IsInWorld()))
|
||||
{
|
||||
return false; // Fail
|
||||
}
|
||||
|
||||
return result; // Fail
|
||||
|
||||
var pos = cmpPosition.GetPosition2D();
|
||||
var tileOwner = cmpTerritoryManager.GetOwner(pos.x, pos.y);
|
||||
var isOwn = (tileOwner == cmpPlayer.GetPlayerID());
|
||||
@ -112,15 +166,26 @@ BuildRestrictions.prototype.CheckPlacement = function()
|
||||
var isAlly = !isOwn && cmpPlayer.IsAlly(tileOwner);
|
||||
// We count neutral players as enemies, so you can't build in their territory.
|
||||
var isEnemy = !isNeutral && (cmpPlayer.IsEnemy(tileOwner) || cmpPlayer.IsNeutral(tileOwner));
|
||||
|
||||
if ((isAlly && !this.HasTerritory("ally"))
|
||||
|| (isOwn && !this.HasTerritory("own"))
|
||||
|| (isNeutral && !this.HasTerritory("neutral"))
|
||||
|| (isEnemy && !this.HasTerritory("enemy")))
|
||||
|
||||
var territoryFail = true;
|
||||
var territoryType = "";
|
||||
if (isAlly && !this.HasTerritory("ally"))
|
||||
territoryType = "ally";
|
||||
else if (isOwn && !this.HasTerritory("own"))
|
||||
territoryType = "own";
|
||||
else if (isNeutral && !this.HasTerritory("neutral"))
|
||||
territoryType = "neutral";
|
||||
else if (isEnemy && !this.HasTerritory("enemy"))
|
||||
territoryType = "enemy";
|
||||
else
|
||||
territoryFail = false
|
||||
|
||||
if (territoryFail)
|
||||
{
|
||||
return false; // Fail
|
||||
result.message = name+" cannot be built in "+territoryType+" territory. Valid territories: " + this.GetTerritories().join(", ");
|
||||
return result; // Fail
|
||||
}
|
||||
|
||||
|
||||
// Check special requirements
|
||||
if (this.template.Category == "Dock")
|
||||
{
|
||||
@ -130,39 +195,32 @@ BuildRestrictions.prototype.CheckPlacement = function()
|
||||
// so it's correct even if the criteria changes for these units
|
||||
var cmpFootprint = Engine.QueryInterface(this.entity, IID_Footprint);
|
||||
if (!cmpFootprint)
|
||||
{
|
||||
return false; // Fail
|
||||
}
|
||||
|
||||
return result; // Fail
|
||||
|
||||
// Get building's footprint
|
||||
var shape = cmpFootprint.GetShape();
|
||||
var halfSize = 0;
|
||||
if (shape.type == "square")
|
||||
{
|
||||
halfSize = shape.depth/2;
|
||||
}
|
||||
else if (shape.type == "circle")
|
||||
{
|
||||
halfSize = shape.radius;
|
||||
}
|
||||
|
||||
|
||||
var cmpTerrain = Engine.QueryInterface(SYSTEM_ENTITY, IID_Terrain);
|
||||
var cmpWaterManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_WaterManager);
|
||||
if (!cmpTerrain || !cmpWaterManager)
|
||||
{
|
||||
return false; // Fail
|
||||
}
|
||||
|
||||
return result; // Fail
|
||||
|
||||
var ang = cmpPosition.GetRotation().y;
|
||||
var sz = halfSize * Math.sin(ang);
|
||||
var cz = halfSize * Math.cos(ang);
|
||||
if ((cmpWaterManager.GetWaterLevel(pos.x + sz, pos.y + cz) - cmpTerrain.GetGroundLevel(pos.x + sz, pos.y + cz)) < 1.0 // front
|
||||
|| (cmpWaterManager.GetWaterLevel(pos.x - sz, pos.y - cz) - cmpTerrain.GetGroundLevel(pos.x - sz, pos.y - cz)) > 2.0) // back
|
||||
{
|
||||
return false; // Fail
|
||||
result.message = name+" must be built on a valid shoreline";
|
||||
return result; // Fail
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Check distance restriction
|
||||
if (this.template.Distance)
|
||||
{
|
||||
@ -189,16 +247,23 @@ BuildRestrictions.prototype.CheckPlacement = function()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((this.template.Distance.MinDistance && nearest < this.template.Distance.MinDistance)
|
||||
|| (this.template.Distance.MaxDistance && nearest > this.template.Distance.MaxDistance))
|
||||
|
||||
if (this.template.Distance.MinDistance && nearest < +this.template.Distance.MinDistance)
|
||||
{
|
||||
return false; // Fail
|
||||
result.message = name+" too close to a "+this.GetCategory()+", must be at least "+ +this.template.Distance.MinDistance+" units away";
|
||||
return result; // Fail
|
||||
}
|
||||
else if (this.template.Distance.MaxDistance && nearest > +this.template.Distance.MaxDistance)
|
||||
{
|
||||
result.message = name+" too far away from a "+this.GetCategory()+", must be within "+ +this.template.Distance.MaxDistance+" units";
|
||||
return result; // Fail
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Success
|
||||
return true;
|
||||
result.success = true;
|
||||
result.message = "";
|
||||
return result;
|
||||
};
|
||||
|
||||
BuildRestrictions.prototype.GetCategory = function()
|
||||
|
@ -798,10 +798,19 @@ GuiInterface.prototype.DisplayRallyPoint = function(player, cmd)
|
||||
* cmd.template is the name of the entity template, or "" to disable the preview.
|
||||
* cmd.x, cmd.z, cmd.angle give the location.
|
||||
*
|
||||
* Returns true if the placement is okay (everything is valid and the entity is not obstructed by others).
|
||||
* Returns result object from CheckPlacement:
|
||||
* {
|
||||
* "success": true iff the placement is valid, else false
|
||||
* "message": message to display in UI for invalid placement, else empty string
|
||||
* }
|
||||
*/
|
||||
GuiInterface.prototype.SetBuildingPlacementPreview = function(player, cmd)
|
||||
{
|
||||
var result = {
|
||||
"success": false,
|
||||
"message": "",
|
||||
}
|
||||
|
||||
// See if we're changing template
|
||||
if (!this.placementEntity || this.placementEntity[0] != cmd.template)
|
||||
{
|
||||
@ -832,26 +841,15 @@ GuiInterface.prototype.SetBuildingPlacementPreview = function(player, cmd)
|
||||
pos.SetYRotation(cmd.angle);
|
||||
}
|
||||
|
||||
// Check whether it's in a visible or fogged region
|
||||
// tell GetLosVisibility to force RetainInFog because preview entities set this to false,
|
||||
// which would show them as hidden instead of fogged
|
||||
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
||||
var visible = (cmpRangeManager && cmpRangeManager.GetLosVisibility(ent, player, true) != "hidden");
|
||||
var validPlacement = false;
|
||||
|
||||
var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
|
||||
cmpOwnership.SetOwner(player);
|
||||
|
||||
if (visible)
|
||||
{ // Check whether it's obstructed by other entities or invalid terrain
|
||||
var cmpBuildRestrictions = Engine.QueryInterface(ent, IID_BuildRestrictions);
|
||||
if (!cmpBuildRestrictions)
|
||||
error("cmpBuildRestrictions not defined");
|
||||
|
||||
validPlacement = (cmpBuildRestrictions && cmpBuildRestrictions.CheckPlacement());
|
||||
}
|
||||
|
||||
var ok = (visible && validPlacement);
|
||||
// Check whether building placement is valid
|
||||
var cmpBuildRestrictions = Engine.QueryInterface(ent, IID_BuildRestrictions);
|
||||
if (!cmpBuildRestrictions)
|
||||
error("cmpBuildRestrictions not defined");
|
||||
else
|
||||
result = cmpBuildRestrictions.CheckPlacement();
|
||||
|
||||
// Set it to a red shade if this is an invalid location
|
||||
var cmpVisual = Engine.QueryInterface(ent, IID_Visual);
|
||||
@ -859,17 +857,15 @@ GuiInterface.prototype.SetBuildingPlacementPreview = function(player, cmd)
|
||||
{
|
||||
if (cmd.actorSeed !== undefined)
|
||||
cmpVisual.SetActorSeed(cmd.actorSeed);
|
||||
|
||||
if (!ok)
|
||||
|
||||
if (!result.success)
|
||||
cmpVisual.SetShadingColour(1.4, 0.4, 0.4, 1);
|
||||
else
|
||||
cmpVisual.SetShadingColour(1, 1, 1, 1);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
return false;
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1317,7 +1313,8 @@ GuiInterface.prototype.SetWallPlacementPreview = function(player, cmd)
|
||||
continue;
|
||||
}
|
||||
|
||||
validPlacement = (cmpBuildRestrictions && cmpBuildRestrictions.CheckPlacement());
|
||||
// TODO: Handle results of CheckPlacement
|
||||
validPlacement = (cmpBuildRestrictions && cmpBuildRestrictions.CheckPlacement().success);
|
||||
|
||||
// If a wall piece has two control groups, it's likely a segment that spans
|
||||
// between two existing towers. To avoid placing a duplicate wall segment,
|
||||
|
@ -133,4 +133,9 @@ Identity.prototype.GetSelectionGroupName = function()
|
||||
return (this.template.SelectionGroupName || "");
|
||||
};
|
||||
|
||||
Identity.prototype.GetGenericName = function()
|
||||
{
|
||||
return this.template.GenericName;
|
||||
};
|
||||
|
||||
Engine.RegisterComponentType(IID_Identity, "Identity", Identity);
|
||||
|
@ -601,22 +601,28 @@ function TryConstructBuilding(player, cmpPlayer, controlAllUnits, cmd)
|
||||
var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
|
||||
cmpOwnership.SetOwner(player);
|
||||
|
||||
// Check whether it's obstructed by other entities or invalid terrain
|
||||
// Check whether building placement is valid
|
||||
var cmpBuildRestrictions = Engine.QueryInterface(ent, IID_BuildRestrictions);
|
||||
if (!cmpBuildRestrictions || !cmpBuildRestrictions.CheckPlacement())
|
||||
if (cmpBuildRestrictions)
|
||||
{
|
||||
if (g_DebugCommands)
|
||||
var ret = cmpBuildRestrictions.CheckPlacement();
|
||||
if (!ret.success)
|
||||
{
|
||||
warn("Invalid command: build restrictions check failed for player "+player+": "+uneval(cmd));
|
||||
if (g_DebugCommands)
|
||||
{
|
||||
warn("Invalid command: build restrictions check failed for player "+player+": "+uneval(cmd));
|
||||
}
|
||||
|
||||
var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
||||
cmpGuiInterface.PushNotification({ "player": player, "message": ret.message });
|
||||
|
||||
// Remove the foundation because the construction was aborted
|
||||
Engine.DestroyEntity(ent);
|
||||
return false;
|
||||
}
|
||||
|
||||
var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
||||
cmpGuiInterface.PushNotification({ "player": player, "message": "Building site was obstructed" });
|
||||
|
||||
// Remove the foundation because the construction was aborted
|
||||
Engine.DestroyEntity(ent);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
error("cmpBuildRestrictions not defined");
|
||||
|
||||
// Check entity limits
|
||||
var cmpEntityLimits = QueryPlayerIDInterface(player, IID_EntityLimits);
|
||||
@ -647,29 +653,6 @@ function TryConstructBuilding(player, cmpPlayer, controlAllUnits, cmd)
|
||||
|
||||
// Remove the foundation because the construction was aborted
|
||||
Engine.DestroyEntity(ent);
|
||||
}
|
||||
|
||||
// TODO: AI has no visibility info
|
||||
if (!cmpPlayer.IsAI())
|
||||
{
|
||||
// Check whether it's in a visible or fogged region
|
||||
// tell GetLosVisibility to force RetainInFog because preview entities set this to false,
|
||||
// which would show them as hidden instead of fogged
|
||||
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
||||
var visible = (cmpRangeManager && cmpRangeManager.GetLosVisibility(ent, player, true) != "hidden");
|
||||
if (!visible)
|
||||
{
|
||||
if (g_DebugCommands)
|
||||
{
|
||||
warn("Invalid command: foundation visibility check failed for player "+player+": "+uneval(cmd));
|
||||
}
|
||||
|
||||
var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
||||
cmpGuiInterface.PushNotification({ "player": player, "message": "Building site was not visible" });
|
||||
|
||||
Engine.DestroyEntity(ent);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// We need the cost after tech modifications
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2012 Wildfire Games.
|
||||
/* Copyright (C) 2013 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -186,7 +186,7 @@ public:
|
||||
CFixedVector3D pos (initialPos.X + s.Multiply(radius), fixed::Zero(), initialPos.Y + c.Multiply(radius));
|
||||
|
||||
SkipTagObstructionFilter filter(spawnedTag); // ignore collisions with the spawned entity
|
||||
if (cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Z, spawnedRadius, spawnedPass))
|
||||
if (cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Z, spawnedRadius, spawnedPass) == ICmpObstruction::FOUNDATION_CHECK_SUCCESS)
|
||||
return pos; // this position is okay, so return it
|
||||
}
|
||||
}
|
||||
@ -241,7 +241,7 @@ public:
|
||||
CFixedVector2D pos (center + dir*i);
|
||||
|
||||
SkipTagObstructionFilter filter(spawnedTag); // ignore collisions with the spawned entity
|
||||
if (cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Y, spawnedRadius, spawnedPass))
|
||||
if (cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Y, spawnedRadius, spawnedPass) == ICmpObstruction::FOUNDATION_CHECK_SUCCESS)
|
||||
return CFixedVector3D(pos.X, fixed::Zero(), pos.Y); // this position is okay, so return it
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2012 Wildfire Games.
|
||||
/* Copyright (C) 2013 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -478,26 +478,26 @@ public:
|
||||
return m_ControlPersist;
|
||||
}
|
||||
|
||||
virtual bool CheckFoundation(std::string className)
|
||||
virtual EFoundationCheck CheckFoundation(std::string className)
|
||||
{
|
||||
CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), GetEntityId());
|
||||
if (!cmpPosition)
|
||||
return false; // error
|
||||
return FOUNDATION_CHECK_FAIL_ERROR; // error
|
||||
|
||||
if (!cmpPosition->IsInWorld())
|
||||
return false; // no obstruction
|
||||
return FOUNDATION_CHECK_FAIL_NO_OBSTRUCTION; // no obstruction
|
||||
|
||||
CFixedVector2D pos = cmpPosition->GetPosition2D();
|
||||
|
||||
CmpPtr<ICmpPathfinder> cmpPathfinder(GetSimContext(), SYSTEM_ENTITY);
|
||||
if (!cmpPathfinder)
|
||||
return false; // error
|
||||
return FOUNDATION_CHECK_FAIL_ERROR; // error
|
||||
|
||||
// required precondition to use SkipControlGroupsRequireFlagObstructionFilter
|
||||
if (m_ControlGroup == INVALID_ENTITY)
|
||||
{
|
||||
LOGERROR(L"[CmpObstruction] Cannot test for foundation obstructions; primary control group must be valid");
|
||||
return false;
|
||||
return FOUNDATION_CHECK_FAIL_ERROR;
|
||||
}
|
||||
|
||||
// Get passability class
|
||||
@ -509,11 +509,9 @@ public:
|
||||
SkipControlGroupsRequireFlagObstructionFilter filter(m_ControlGroup, m_ControlGroup2,
|
||||
ICmpObstructionManager::FLAG_BLOCK_FOUNDATION);
|
||||
|
||||
if (m_Type == STATIC)
|
||||
return cmpPathfinder->CheckBuildingPlacement(filter, pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, GetEntityId(), passClass);
|
||||
else if (m_Type == UNIT)
|
||||
if (m_Type == UNIT)
|
||||
return cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Y, m_Size0, passClass);
|
||||
else
|
||||
else
|
||||
return cmpPathfinder->CheckBuildingPlacement(filter, pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, GetEntityId(), passClass);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2012 Wildfire Games.
|
||||
/* Copyright (C) 2013 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -656,16 +656,16 @@ void CCmpPathfinder::ProcessSameTurnMoves()
|
||||
}
|
||||
}
|
||||
|
||||
bool CCmpPathfinder::CheckUnitPlacement(const IObstructionTestFilter& filter,
|
||||
ICmpObstruction::EFoundationCheck CCmpPathfinder::CheckUnitPlacement(const IObstructionTestFilter& filter,
|
||||
entity_pos_t x, entity_pos_t z, entity_pos_t r, pass_class_t passClass)
|
||||
{
|
||||
// Check unit obstruction
|
||||
CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY);
|
||||
if (!cmpObstructionManager)
|
||||
return false;
|
||||
return ICmpObstruction::FOUNDATION_CHECK_FAIL_ERROR;
|
||||
|
||||
if (cmpObstructionManager->TestUnitShape(filter, x, z, r, NULL))
|
||||
return false;
|
||||
return ICmpObstruction::FOUNDATION_CHECK_FAIL_OBSTRUCTS_FOUNDATION;
|
||||
|
||||
// Test against terrain:
|
||||
|
||||
@ -680,36 +680,33 @@ bool CCmpPathfinder::CheckUnitPlacement(const IObstructionTestFilter& filter,
|
||||
{
|
||||
if (!IS_TERRAIN_PASSABLE(m_Grid->get(i,j), passClass))
|
||||
{
|
||||
return false;
|
||||
return ICmpObstruction::FOUNDATION_CHECK_FAIL_TERRAIN_CLASS;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return ICmpObstruction::FOUNDATION_CHECK_SUCCESS;
|
||||
}
|
||||
|
||||
bool CCmpPathfinder::CheckBuildingPlacement(const IObstructionTestFilter& filter,
|
||||
ICmpObstruction::EFoundationCheck CCmpPathfinder::CheckBuildingPlacement(const IObstructionTestFilter& filter,
|
||||
entity_pos_t x, entity_pos_t z, entity_pos_t a, entity_pos_t w,
|
||||
entity_pos_t h, entity_id_t id, pass_class_t passClass)
|
||||
{
|
||||
// Check unit obstruction
|
||||
CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY);
|
||||
if (!cmpObstructionManager)
|
||||
return false;
|
||||
return ICmpObstruction::FOUNDATION_CHECK_FAIL_ERROR;
|
||||
|
||||
if (cmpObstructionManager->TestStaticShape(filter, x, z, a, w, h, NULL))
|
||||
return false;
|
||||
return ICmpObstruction::FOUNDATION_CHECK_FAIL_OBSTRUCTS_FOUNDATION;
|
||||
|
||||
// Test against terrain:
|
||||
|
||||
UpdateGrid();
|
||||
|
||||
CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), id);
|
||||
if (!cmpObstruction)
|
||||
return false;
|
||||
|
||||
ICmpObstructionManager::ObstructionSquare square;
|
||||
if (!cmpObstruction->GetObstructionSquare(square))
|
||||
return false;
|
||||
CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), id);
|
||||
if (!cmpObstruction || !cmpObstruction->GetObstructionSquare(square))
|
||||
return ICmpObstruction::FOUNDATION_CHECK_FAIL_NO_OBSTRUCTION;
|
||||
|
||||
// Expand bounds by 1/sqrt(2) tile (multiply by TERRAIN_TILE_SIZE since we want world coordinates)
|
||||
entity_pos_t expand = entity_pos_t::FromInt(2).Sqrt().Multiply(entity_pos_t::FromInt(TERRAIN_TILE_SIZE / 2));
|
||||
@ -728,10 +725,10 @@ bool CCmpPathfinder::CheckBuildingPlacement(const IObstructionTestFilter& filter
|
||||
if (Geometry::PointIsInSquare(CFixedVector2D(x - square.x, z - square.z), square.u, square.v, halfSize)
|
||||
&& !IS_TERRAIN_PASSABLE(m_Grid->get(i,j), passClass))
|
||||
{
|
||||
return false;
|
||||
return ICmpObstruction::FOUNDATION_CHECK_FAIL_TERRAIN_CLASS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return ICmpObstruction::FOUNDATION_CHECK_SUCCESS;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
/* Copyright (C) 2013 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -260,9 +260,9 @@ public:
|
||||
|
||||
virtual bool CheckMovement(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, entity_pos_t r, pass_class_t passClass);
|
||||
|
||||
virtual bool CheckUnitPlacement(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, pass_class_t passClass);
|
||||
virtual ICmpObstruction::EFoundationCheck CheckUnitPlacement(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, pass_class_t passClass);
|
||||
|
||||
virtual bool CheckBuildingPlacement(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t a, entity_pos_t w, entity_pos_t h, entity_id_t id, pass_class_t passClass);
|
||||
virtual ICmpObstruction::EFoundationCheck CheckBuildingPlacement(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t a, entity_pos_t w, entity_pos_t h, entity_id_t id, pass_class_t passClass);
|
||||
|
||||
virtual void FinishAsyncRequests();
|
||||
|
||||
|
@ -501,6 +501,7 @@ void CCmpTemplateManager::CopyPreviewSubset(CParamNode& out, const CParamNode& i
|
||||
// and safe (i.e. won't do anything that affects the synchronised simulation state), so additions
|
||||
// to this list should be carefully considered
|
||||
std::set<std::string> permittedComponentTypes;
|
||||
permittedComponentTypes.insert("Identity");
|
||||
permittedComponentTypes.insert("Ownership");
|
||||
permittedComponentTypes.insert("Position");
|
||||
permittedComponentTypes.insert("VisualActor");
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2012 Wildfire Games.
|
||||
/* Copyright (C) 2013 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -21,9 +21,33 @@
|
||||
|
||||
#include "simulation2/system/InterfaceScripted.h"
|
||||
|
||||
#include "simulation2/system/SimContext.h"
|
||||
|
||||
std::string ICmpObstruction::CheckFoundation_wrapper(std::string className)
|
||||
{
|
||||
EFoundationCheck check = CheckFoundation(className);
|
||||
|
||||
switch (check)
|
||||
{
|
||||
case FOUNDATION_CHECK_SUCCESS:
|
||||
return "success";
|
||||
case FOUNDATION_CHECK_FAIL_ERROR:
|
||||
return "fail_error";
|
||||
case FOUNDATION_CHECK_FAIL_NO_OBSTRUCTION:
|
||||
return "fail_no_obstruction";
|
||||
case FOUNDATION_CHECK_FAIL_OBSTRUCTS_FOUNDATION:
|
||||
return "fail_obstructs_foundation";
|
||||
case FOUNDATION_CHECK_FAIL_TERRAIN_CLASS:
|
||||
return "fail_terrain_class";
|
||||
default:
|
||||
debug_warn(L"Unexpected result from CheckFoundation");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
BEGIN_INTERFACE_WRAPPER(Obstruction)
|
||||
DEFINE_INTERFACE_METHOD_0("GetUnitRadius", entity_pos_t, ICmpObstruction, GetUnitRadius)
|
||||
DEFINE_INTERFACE_METHOD_1("CheckFoundation", bool, ICmpObstruction, CheckFoundation, std::string)
|
||||
DEFINE_INTERFACE_METHOD_1("CheckFoundation", std::string, ICmpObstruction, CheckFoundation_wrapper, std::string)
|
||||
DEFINE_INTERFACE_METHOD_0("CheckDuplicateFoundation", bool, ICmpObstruction, CheckDuplicateFoundation)
|
||||
DEFINE_INTERFACE_METHOD_2("GetEntityCollisions", std::vector<entity_id_t>, ICmpObstruction, GetEntityCollisions, bool, bool)
|
||||
DEFINE_INTERFACE_METHOD_1("SetActive", void, ICmpObstruction, SetActive, bool)
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2012 Wildfire Games.
|
||||
/* Copyright (C) 2013 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -30,6 +30,14 @@ class ICmpObstruction : public IComponent
|
||||
{
|
||||
public:
|
||||
|
||||
enum EFoundationCheck {
|
||||
FOUNDATION_CHECK_SUCCESS,
|
||||
FOUNDATION_CHECK_FAIL_ERROR,
|
||||
FOUNDATION_CHECK_FAIL_NO_OBSTRUCTION,
|
||||
FOUNDATION_CHECK_FAIL_OBSTRUCTS_FOUNDATION,
|
||||
FOUNDATION_CHECK_FAIL_TERRAIN_CLASS
|
||||
};
|
||||
|
||||
virtual ICmpObstructionManager::tag_t GetObstruction() = 0;
|
||||
|
||||
/**
|
||||
@ -47,9 +55,16 @@ public:
|
||||
* Test whether this entity is colliding with any obstruction that are set to
|
||||
* block the creation of foundations.
|
||||
* @param ignoredEntities List of entities to ignore during the test.
|
||||
* @return true if foundation is valid (not obstructed)
|
||||
* @return FOUNDATION_CHECK_SUCCESS if check passes, else an EFoundationCheck
|
||||
* value describing the type of failure.
|
||||
*/
|
||||
virtual bool CheckFoundation(std::string className) = 0;
|
||||
virtual EFoundationCheck CheckFoundation(std::string className) = 0;
|
||||
|
||||
/**
|
||||
* CheckFoundation wrapper for script calls, to return friendly strings instead of an EFoundationCheck.
|
||||
* @return "success" if check passes, else a string describing the type of failure.
|
||||
*/
|
||||
virtual std::string CheckFoundation_wrapper(std::string className);
|
||||
|
||||
/**
|
||||
* Test whether this entity is colliding with any obstructions that share its
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
/* Copyright (C) 2013 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -20,6 +20,7 @@
|
||||
|
||||
#include "simulation2/system/Interface.h"
|
||||
|
||||
#include "simulation2/components/ICmpObstruction.h"
|
||||
#include "simulation2/helpers/Position.h"
|
||||
|
||||
#include "maths/FixedVector2D.h"
|
||||
@ -152,16 +153,18 @@ public:
|
||||
/**
|
||||
* Check whether a unit placed here is valid and doesn't hit any obstructions
|
||||
* or impassable terrain.
|
||||
* Returns true if the placement is okay.
|
||||
* @return ICmpObstruction::FOUNDATION_CHECK_SUCCESS if the placement is okay, else
|
||||
* a value describing the type of failure.
|
||||
*/
|
||||
virtual bool CheckUnitPlacement(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, pass_class_t passClass) = 0;
|
||||
virtual ICmpObstruction::EFoundationCheck CheckUnitPlacement(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, pass_class_t passClass) = 0;
|
||||
|
||||
/**
|
||||
* Check whether a building placed here is valid and doesn't hit any obstructions
|
||||
* or impassable terrain.
|
||||
* Returns true if the placement is okay.
|
||||
* @return ICmpObstruction::FOUNDATION_CHECK_SUCCESS if the placement is okay, else
|
||||
* a value describing the type of failure.
|
||||
*/
|
||||
virtual bool CheckBuildingPlacement(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t a, entity_pos_t w, entity_pos_t h, entity_id_t id, pass_class_t passClass) = 0;
|
||||
virtual ICmpObstruction::EFoundationCheck CheckBuildingPlacement(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t a, entity_pos_t w, entity_pos_t h, entity_id_t id, pass_class_t passClass) = 0;
|
||||
|
||||
/**
|
||||
* Toggle the storage and rendering of debug info.
|
||||
|
Loading…
Reference in New Issue
Block a user