Index: binaries/data/mods/public/globalscripts/Templates.js
===================================================================
--- binaries/data/mods/public/globalscripts/Templates.js
+++ binaries/data/mods/public/globalscripts/Templates.js
@@ -273,6 +273,9 @@
if (template.BuildRestrictions.Distance.MaxDistance)
ret.buildRestrictions.distance.max = getEntityValue("BuildRestrictions/Distance/MaxDistance");
}
+
+ if (template.BuildRestrictions.Sockets)
+ ret.buildRestrictions.sockets = template.BuildRestrictions.Sockets._string.split(/\s+/);
}
if (template.TrainingRestrictions)
Index: binaries/data/mods/public/gui/session/input.js
===================================================================
--- binaries/data/mods/public/gui/session/input.js
+++ binaries/data/mods/public/gui/session/input.js
@@ -131,11 +131,18 @@
{
if (placementSupport.template && placementSupport.position)
{
- var result = Engine.GuiInterfaceCall("SetBuildingPlacementPreview", {
+ let result = Engine.GuiInterfaceCall("SetBuildingPlacementPreview", {
"template": placementSupport.template,
- "x": placementSupport.position.x,
- "z": placementSupport.position.z,
+ "position": {
+ "x": placementSupport.position.x,
+ "z": placementSupport.position.z,
+ },
"angle": placementSupport.angle,
+ "snapClasses": placementSupport.socketSnapClasses,
+ // Ideally we'd only get entities with certain class.
+ "snapEntities": placementSupport.socketSnapClasses &&
+ placementSupport.socketSnapClasses.length &&
+ Engine.GetEntitiesWithStaticObstructionOnScreen(),
"actorSeed": placementSupport.actorSeed
});
@@ -176,7 +183,7 @@
placementSupport.tooltipMessage = sprintf(translatePlural("Basic range: %(range)s meter", "Basic range: %(range)s meters", range), { "range": range }) + "\n" +
sprintf(translatePlural("Average bonus range: %(range)s meter", "Average bonus range: %(range)s meters", averageRange), { "range": averageRange });
}
- return true;
+ return result;
}
}
else if (placementSupport.mode === "wall")
@@ -296,7 +303,8 @@
return false;
}
- if (!updateBuildingPlacementPreview())
+ let buildingPlacementInfo = updateBuildingPlacementPreview();
+ if (!buildingPlacementInfo)
{
// invalid location - don't build it
// TODO: play a sound?
@@ -308,14 +316,15 @@
Engine.PostNetworkCommand({
"type": "construct",
"template": placementSupport.template,
- "x": placementSupport.position.x,
- "z": placementSupport.position.z,
+ "x": buildingPlacementInfo.x,
+ "z": buildingPlacementInfo.z,
"angle": placementSupport.angle,
"actorSeed": placementSupport.actorSeed,
"entities": selection,
"autorepair": true,
"autocontinue": true,
- "queued": queued
+ "queued": queued,
+ "snapEntity": buildingPlacementInfo.snappedEnt
});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_build", "entity": selection[0] });
@@ -729,20 +738,15 @@
placementSupport.SetDefaultAngle();
}
- let snapData = Engine.GuiInterfaceCall("GetFoundationSnapData", {
+ Engine.GuiInterfaceCall("SetFoundationSnapData", {
"template": placementSupport.template,
"x": placementSupport.position.x,
"z": placementSupport.position.z,
"angle": placementSupport.angle,
"snapToEdges": isSnapToEdgesEnabled() && Engine.GetEdgesOfStaticObstructionsOnScreenNearTo(
- placementSupport.position.x, placementSupport.position.z)
+ placementSupport.position.x, placementSupport.position.z),
+ "targetObject": placementSupport
});
- if (snapData)
- {
- placementSupport.angle = snapData.angle;
- placementSupport.position.x = snapData.x;
- placementSupport.position.z = snapData.z;
- }
updateBuildingPlacementPreview();
break;
@@ -1055,19 +1059,14 @@
return true;
}
- let snapData = Engine.GuiInterfaceCall("GetFoundationSnapData", {
+ Engine.GuiInterfaceCall("SetFoundationSnapData", {
"template": placementSupport.template,
"x": placementSupport.position.x,
"z": placementSupport.position.z,
"snapToEdges": isSnapToEdgesEnabled() && Engine.GetEdgesOfStaticObstructionsOnScreenNearTo(
- placementSupport.position.x, placementSupport.position.z)
+ placementSupport.position.x, placementSupport.position.z),
+ "targetObject": placementSupport
});
- if (snapData)
- {
- placementSupport.angle = snapData.angle;
- placementSupport.position.x = snapData.x;
- placementSupport.position.z = snapData.z;
- }
}
updateBuildingPlacementPreview(); // includes an update of the snap entity candidates
@@ -1086,22 +1085,15 @@
{
placementSupport.position = Engine.GetTerrainAtScreenPoint(ev.x, ev.y);
- if (isSnapToEdgesEnabled())
- {
- let snapData = Engine.GuiInterfaceCall("GetFoundationSnapData", {
- "template": placementSupport.template,
- "x": placementSupport.position.x,
- "z": placementSupport.position.z,
- "snapToEdges": Engine.GetEdgesOfStaticObstructionsOnScreenNearTo(
- placementSupport.position.x, placementSupport.position.z)
- });
- if (snapData)
- {
- placementSupport.angle = snapData.angle;
- placementSupport.position.x = snapData.x;
- placementSupport.position.z = snapData.z;
- }
- }
+ Engine.GuiInterfaceCall("SetFoundationSnapData", {
+ "template": placementSupport.template,
+ "x": placementSupport.position.x,
+ "z": placementSupport.position.z,
+ "snapEntities": placementSupport.socketSnapEntities,
+ "snapToEdges": isSnapToEdgesEnabled() && Engine.GetEdgesOfStaticObstructionsOnScreenNearTo(
+ placementSupport.position.x, placementSupport.position.z),
+ "targetObject": placementSupport
+ });
g_DragStart = new Vector2D(ev.x, ev.y);
inputState = INPUT_BUILDING_CLICK;
@@ -1311,6 +1303,7 @@
placementSupport.mode = "building";
placementSupport.template = buildTemplate;
inputState = INPUT_BUILDING_PLACEMENT;
+ placementSupport.socketSnapClasses = templateData.buildRestrictions.sockets;
}
if (templateData.attack &&
Index: binaries/data/mods/public/gui/session/placement.js
===================================================================
--- binaries/data/mods/public/gui/session/placement.js
+++ binaries/data/mods/public/gui/session/placement.js
@@ -15,6 +15,8 @@
this.template = null;
this.tooltipMessage = ""; // tooltip text to show while the user is placing a structure
this.tooltipError = false;
+ this.socketClasses = null;
+ this.socketSnapEntities = null;
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;
Index: binaries/data/mods/public/simulation/components/BuildRestrictions.js
===================================================================
--- binaries/data/mods/public/simulation/components/BuildRestrictions.js
+++ binaries/data/mods/public/simulation/components/BuildRestrictions.js
@@ -18,6 +18,7 @@
"land" +
"shore" +
"land-shore"+
+ "socket"+
"" +
"" +
"" +
@@ -45,11 +46,23 @@
"" +
"" +
"" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "tokens" +
+ "" +
+ "" +
+ "" +
"";
BuildRestrictions.prototype.Init = function()
{
this.territories = this.template.Territory.split(/\s+/);
+ if (this.template.Sockets)
+ this.sockets = this.template.Sockets._string.split(/\s+/);
+ if (this.template.PlacementType == "socket" && !this.sockets)
+ warn("Placement type 'Socket' without a socket specified, this building (" + this.entity + ") can never be built.");
};
/**
@@ -144,7 +157,8 @@
var ret = cmpObstruction.CheckFoundation(passClassName, false);
}
- if (ret != "success")
+ // When a socket is needed obstruction ought to be ignored.
+ if (ret != "success" && this.template.PlacementType != "socket")
{
switch (ret)
{
@@ -230,6 +244,12 @@
}
}
+ if (this.template.PlacementType == "socket" && !this.CheckSocketPlacement(pos))
+ {
+ result.message = markForTranslation("%(name)s must be built on a valid socket.");
+ return result;
+ }
+
let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
let templateName = cmpTemplateManager.GetCurrentTemplateName(this.entity);
@@ -298,6 +318,24 @@
return result;
};
+/**
+ * Whether this entity is placed on the correct socket.
+ */
+BuildRestrictions.prototype.CheckSocketPlacement = function()
+{
+ let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
+ let classes = this.sockets;
+
+ let filter = function(id)
+ {
+ let cmpIdentity = Engine.QueryInterface(id, IID_Identity);
+ return cmpIdentity && MatchesClassList(classes, cmpIdentity.GetClassesList());
+ };
+ let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
+
+ return cmpRangeManager.ExecuteQuery(this.entity, 0, 0, cmpPlayerManager.GetAllPlayers(), IID_BuildSlot).some(filter);
+};
+
BuildRestrictions.prototype.GetCategory = function()
{
return this.template.Category;
Index: binaries/data/mods/public/simulation/components/BuildSlot.js
===================================================================
--- binaries/data/mods/public/simulation/components/BuildSlot.js
+++ binaries/data/mods/public/simulation/components/BuildSlot.js
@@ -1,10 +1,3 @@
-function Settlement() {}
-
-Settlement.prototype.Schema =
- "";
-
-Engine.RegisterComponentType(IID_Settlement, "Settlement", Settlement);
-
/*
* TODO: the vague plan is that this should keep track of who currently owns the settlement,
* and some other code can detect this (or get notified of changes) when it needs to.
@@ -13,3 +6,98 @@
* tell us that its player owns us, and move us back into our original position when the building
* is destroyed. Don't know if that's a sensible plan, though.
*/
+class BuildSlot
+{
+ Init()
+ {
+ this.occupant = INVALID_ENTITY;
+ }
+
+ /**
+ * Initialises construction, thus rendering this socket useless.
+ *
+ * @param {number} player - The player requesting the initialisation.
+ * @param {number} entity - The entity being built on/in this slot.
+ *
+ * @return {boolean} Whether the initialisation was successful.
+ */
+ InitConstruction(entity)
+ {
+ this.occupant = entity;
+
+ if (this.template.HideUponUse != "true")
+ return true;
+
+ let cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
+ if (!cmpPosition)
+ return false;
+
+ this.previousPosition = cmpPosition.GetPosition();
+ cmpPosition.MoveOutOfWorld();
+
+ return true;
+ }
+
+ /**
+ * Resets this socket by setting the owner to -1 and moving back to its former position.
+ */
+ Reset()
+ {
+ this.occupant = INVALID_ENTITY;
+
+ if (!this.previousPosition)
+ return;
+
+ let cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
+ if (!cmpPosition)
+ return;
+
+ cmpPosition.JumpTo(this.previousPosition.x, this.previousPosition.z);
+ delete this.previousPosition;
+ }
+
+ /**
+ * Get the current occupant, i.e. the building placed upon this socket.
+ *
+ * @return {number} The current occupant of this build slot.
+ */
+ GetOccupant()
+ {
+ return this.occupant;
+ }
+}
+
+BuildSlot.prototype.Schema =
+ "Specifies this is a building slot, an entity where a structure can be placed upon." +
+ "" +
+ "" +
+ "true" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "";
+
+BuildSlot.prototype.OnGlobalEntityRenamed = function(msg)
+{
+ if (msg.entity != this.occupant)
+ return;
+
+ // Our occupant died, reset our state.
+ if (msg.newentity == INVALID_ENTITY)
+ this.Reset();
+ else
+ this.occupant = msg.newentity;
+};
+
+BuildSlot.prototype.OnGlobalOwnershipChanged = function(msg)
+{
+ if (msg.entity != this.occupant)
+ return;
+
+ // Our occupant died, reset our state.
+ if (msg.to == INVALID_PLAYER)
+ this.Reset();
+};
+
+Engine.RegisterComponentType(IID_BuildSlot, "BuildSlot", BuildSlot);
Index: binaries/data/mods/public/simulation/components/GuiInterface.js
===================================================================
--- binaries/data/mods/public/simulation/components/GuiInterface.js
+++ binaries/data/mods/public/simulation/components/GuiInterface.js
@@ -1041,6 +1041,18 @@
"translateParameters": [],
};
+ let snapData;
+ if (cmd.snapEntities)
+ {
+ this.SetFoundationSnapData(player, {
+ "x": cmd.position.x,
+ "z": cmd.position.z,
+ "template": cmd.template,
+ "snapEntities": cmd.snapEntities,
+ "targetObject": cmd
+ });
+ }
+
// See if we're changing template
if (!this.placementEntity || this.placementEntity[0] != cmd.template)
{
@@ -1063,7 +1075,7 @@
let pos = Engine.QueryInterface(ent, IID_Position);
if (pos)
{
- pos.JumpTo(cmd.x, cmd.z);
+ pos.JumpTo(cmd.position.x, cmd.position.z);
pos.SetYRotation(cmd.angle);
}
@@ -1077,6 +1089,11 @@
else
result = cmpBuildRestrictions.CheckPlacement();
+ result.x = cmd.position.x;
+ result.z = cmd.position.z;
+ if (cmd.snapped && cmd.snappedEnt)
+ result.snappedEnt = cmd.snappedEnt
+
let cmpRangeOverlayManager = Engine.QueryInterface(ent, IID_RangeOverlayManager);
if (cmpRangeOverlayManager)
cmpRangeOverlayManager.SetEnabled(true, this.enabledVisualRangeOverlayTypes);
@@ -1164,14 +1181,14 @@
let wallSet = cmd.wallSet;
let start = {
- "pos": cmd.start,
+ "position": cmd.start,
"angle": 0,
"snapped": false, // did the start position snap to anything?
"snappedEnt": INVALID_ENTITY, // if we snapped, was it to an entity? if yes, holds that entity's ID
};
let end = {
- "pos": cmd.end,
+ "position": cmd.end,
"angle": 0,
"snapped": false, // did the start position snap to anything?
"snappedEnt": INVALID_ENTITY, // if we snapped, was it to an entity? if yes, holds that entity's ID
@@ -1237,54 +1254,34 @@
}
// prevent division by zero errors further on if the start and end positions are the same
- if (end.pos && (start.pos.x === end.pos.x && start.pos.z === end.pos.z))
- end.pos = undefined;
+ if (end.position && (start.position.x === end.position.x && start.position.z === end.position.z))
+ end.position = undefined;
// See if we need to snap the start and/or end coordinates to any of our list of snap entities. Note that, despite the list
// of snapping candidate entities, it might still snap to e.g. terrain features. Use the "ent" key in the returned snapping
- // data to determine whether it snapped to an entity (if any), and to which one (see GetFoundationSnapData).
+ // data to determine whether it snapped to an entity (if any), and to which one (see SetFoundationSnapData).
if (cmd.snapEntities)
{
let snapRadius = this.placementWallEntities[wallSet.templates.tower].templateData.wallPiece.length * 0.5; // determined through trial and error
- let startSnapData = this.GetFoundationSnapData(player, {
- "x": start.pos.x,
- "z": start.pos.z,
+ this.SetFoundationSnapData(player, {
+ "x": start.position.x,
+ "z": start.position.z,
"template": wallSet.templates.tower,
"snapEntities": cmd.snapEntities,
"snapRadius": snapRadius,
+ "targetObject": start
});
- if (startSnapData)
+ if (end.position)
{
- start.pos.x = startSnapData.x;
- start.pos.z = startSnapData.z;
- start.angle = startSnapData.angle;
- start.snapped = true;
-
- if (startSnapData.ent)
- start.snappedEnt = startSnapData.ent;
- }
-
- if (end.pos)
- {
- let endSnapData = this.GetFoundationSnapData(player, {
- "x": end.pos.x,
- "z": end.pos.z,
+ this.SetFoundationSnapData(player, {
+ "x": end.position.x,
+ "z": end.position.z,
"template": wallSet.templates.tower,
"snapEntities": cmd.snapEntities,
"snapRadius": snapRadius,
+ "targetObject": end
});
-
- if (endSnapData)
- {
- end.pos.x = endSnapData.x;
- end.pos.z = endSnapData.z;
- end.angle = endSnapData.angle;
- end.snapped = true;
-
- if (endSnapData.ent)
- end.snappedEnt = endSnapData.ent;
- }
}
}
@@ -1302,7 +1299,7 @@
result.cost[res] = 0;
let previewEntities = [];
- if (end.pos)
+ if (end.position)
previewEntities = GetWallPlacement(this.placementWallEntities, wallSet, start, end); // see helpers/Walls.js
// For wall placement, we may (and usually do) need to have wall pieces overlap each other more than would
@@ -1337,7 +1334,7 @@
if (cmpPosition)
previewEntities.unshift({
"template": wallSet.templates.tower,
- "pos": start.pos,
+ "position": start.position,
"angle": cmpPosition.GetRotation().y,
"controlGroups": [startEntObstruction ? startEntObstruction.GetControlGroup() : undefined],
"excludeFromResult": true, // preview only, must not appear in the result
@@ -1363,12 +1360,12 @@
// the simulation updates, we fake it by reusing the last angle and hope the player doesn't notice.
previewEntities.unshift({
"template": wallSet.templates.tower,
- "pos": start.pos,
+ "position": start.position,
"angle": previewEntities.length > 0 ? previewEntities[0].angle : this.placementWallLastAngle
});
}
- if (end.pos)
+ if (end.position)
{
// Analogous to the starting side case above
if (end.snappedEnt && end.snappedEnt != INVALID_ENTITY)
@@ -1394,7 +1391,7 @@
if (cmpPosition)
previewEntities.push({
"template": wallSet.templates.tower,
- "pos": end.pos,
+ "position": end.position,
"angle": cmpPosition.GetRotation().y,
"controlGroups": [endEntObstruction ? endEntObstruction.GetControlGroup() : undefined],
"excludeFromResult": true
@@ -1404,7 +1401,7 @@
else
previewEntities.push({
"template": wallSet.templates.tower,
- "pos": end.pos,
+ "position": end.position,
"angle": previewEntities.length > 0 ? previewEntities[previewEntities.length-1].angle : this.placementWallLastAngle
});
}
@@ -1460,7 +1457,7 @@
let cmpPosition = Engine.QueryInterface(ent, IID_Position);
if (cmpPosition)
{
- cmpPosition.JumpTo(entInfo.pos.x, entInfo.pos.z);
+ cmpPosition.JumpTo(entInfo.position.x, entInfo.position.z);
cmpPosition.SetYRotation(entInfo.angle);
// if this piece is a tower, then it should have a Y position that is at least as high as its surrounding pieces
@@ -1470,10 +1467,10 @@
let terrainGroundNext = null;
if (i > 0)
- terrainGroundPrev = cmpTerrain.GetGroundLevel(previewEntities[i-1].pos.x, previewEntities[i-1].pos.z);
+ terrainGroundPrev = cmpTerrain.GetGroundLevel(previewEntities[i-1].position.x, previewEntities[i-1].position.z);
if (i < previewEntities.length - 1)
- terrainGroundNext = cmpTerrain.GetGroundLevel(previewEntities[i+1].pos.x, previewEntities[i+1].pos.z);
+ terrainGroundNext = cmpTerrain.GetGroundLevel(previewEntities[i+1].position.x, previewEntities[i+1].position.z);
if (terrainGroundPrev != null || terrainGroundNext != null)
{
@@ -1564,8 +1561,8 @@
{
result.pieces.push({
"template": tpl,
- "x": entInfo.pos.x,
- "z": entInfo.pos.z,
+ "x": entInfo.position.x,
+ "z": entInfo.position.z,
"angle": entInfo.angle,
});
this.placementWallLastAngle = entInfo.angle;
@@ -1605,7 +1602,7 @@
// We should only return that we snapped to an entity if all pieces up until that entity can be validly constructed,
// i.e. are included in result.pieces (see docs for the result object).
- if (end.pos && end.snappedEnt && end.snappedEnt != INVALID_ENTITY && allPiecesValid)
+ if (end.position && end.snappedEnt && end.snappedEnt != INVALID_ENTITY && allPiecesValid)
result.endSnappedEnt = end.snappedEnt;
return result;
@@ -1625,16 +1622,23 @@
* holding the ID of the entity that was snapped to.
* @param data.snapRadius Optional; when used in conjunction with data.snapEntities, indicates the circle radius around an entity that
* {data.x, data.z} must be located within to have it snap to that entity.
+ * Defaults to approximately half the obstruction size.
+ * @param data.targetObject The object to assign the Snap data to.
*/
-GuiInterface.prototype.GetFoundationSnapData = function(player, data)
+GuiInterface.prototype.SetFoundationSnapData = function(player, data)
{
let template = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager).GetTemplate(data.template);
if (!template)
{
- warn("[GetFoundationSnapData] Failed to load template '" + data.template + "'");
- return false;
+ warn("[SetFoundationSnapData] Failed to load template '" + data.template + "'");
+ return;
}
+ if (data.snapEntities && !data.snapRadius)
+ data.snapRadius = Math.max(
+ template.Obstruction.Static["@depth"] / 2,
+ template.Obstruction.Static["@width"] / 2);
+
if (data.snapEntities && data.snapRadius && data.snapRadius > 0)
{
// see if {data.x, data.z} is inside the snap radius of any of the snap entities; and if so, to which it is closest
@@ -1659,37 +1663,48 @@
{
minDist2 = dist2;
minDistEntitySnapData = {
- "x": pos.x,
- "z": pos.z,
- "angle": cmpPosition.GetRotation().y,
- "ent": ent
+ "x": pos.x,
+ "z": pos.z,
+ "angle": cmpPosition.GetRotation().y,
+ "ent": ent
};
}
}
if (minDistEntitySnapData != null)
- return minDistEntitySnapData;
+ {
+ data.targetObject.position.x = minDistEntitySnapData.x;
+ data.targetObject.position.z = minDistEntitySnapData.z;
+ data.targetObject.angle = minDistEntitySnapData.angle;
+ data.targetObject.snappedEnt = minDistEntitySnapData.ent;
+ data.targetObject.snapped = true;
+ return;
+ }
}
if (data.snapToEdges)
{
let position = this.obstructionSnap.getPosition(data, template);
if (position)
- return position;
+ {
+ data.targetObject.position.x = position.x;
+ data.targetObject.position.z = position.z;
+ data.targetObject.angle = position.angle;
+ return;
+ }
}
if (template.BuildRestrictions.PlacementType == "shore")
{
let angle = GetDockAngle(template, data.x, data.z);
if (angle !== undefined)
- return {
- "x": data.x,
- "z": data.z,
- "angle": angle
- };
+ {
+ data.targetObject.position.x = data.x;
+ data.targetObject.position.z = data.z;
+ data.targetObject.angle = angle;
+ return;
+ }
}
-
- return false;
};
GuiInterface.prototype.PlaySound = function(player, data)
@@ -1984,7 +1999,7 @@
"AddTargetMarker": 1,
"SetBuildingPlacementPreview": 1,
"SetWallPlacementPreview": 1,
- "GetFoundationSnapData": 1,
+ "SetFoundationSnapData": 1,
"PlaySound": 1,
"FindIdleUnits": 1,
"HasIdleUnits": 1,
Index: binaries/data/mods/public/simulation/components/Settlement.js
===================================================================
--- binaries/data/mods/public/simulation/components/Settlement.js
+++ binaries/data/mods/public/simulation/components/Settlement.js
@@ -1,15 +0,0 @@
-function Settlement() {}
-
-Settlement.prototype.Schema =
- "";
-
-Engine.RegisterComponentType(IID_Settlement, "Settlement", Settlement);
-
-/*
- * TODO: the vague plan is that this should keep track of who currently owns the settlement,
- * and some other code can detect this (or get notified of changes) when it needs to.
- * A civcenter's BuildRestrictions component will see that it's being built on this settlement,
- * call MoveOutOfWorld on us (so we're invisible and only the building is visible/selectable),
- * tell us that its player owns us, and move us back into our original position when the building
- * is destroyed. Don't know if that's a sensible plan, though.
- */
Index: binaries/data/mods/public/simulation/components/interfaces/BuildSlot.js
===================================================================
--- /dev/null
+++ binaries/data/mods/public/simulation/components/interfaces/BuildSlot.js
@@ -0,0 +1 @@
+Engine.RegisterInterface("BuildSlot");
Index: binaries/data/mods/public/simulation/components/tests/test_BuildSlot.js
===================================================================
--- /dev/null
+++ binaries/data/mods/public/simulation/components/tests/test_BuildSlot.js
@@ -0,0 +1,13 @@
+Engine.LoadComponentScript("interfaces/BuildSlot.js");
+Engine.LoadComponentScript("BuildSlot.js");
+
+const buildSlotId = 1;
+const buildingId = 2;
+
+let cmpBuildSlot = ConstructComponent(buildSlotId, "BuildSlot", {
+ "HideUponUse": "false"
+});
+
+TS_ASSERT_UNEVAL_EQUALS(cmpBuildSlot.GetOccupant(), INVALID_ENTITY);
+TS_ASSERT_UNEVAL_EQUALS(cmpBuildSlot.InitConstruction(buildingId), true);
+TS_ASSERT_UNEVAL_EQUALS(cmpBuildSlot.GetOccupant(), buildingId);
Index: binaries/data/mods/public/simulation/helpers/Commands.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/Commands.js
+++ binaries/data/mods/public/simulation/helpers/Commands.js
@@ -1136,6 +1136,13 @@
if (cmpVisual && cmd.actorSeed !== undefined)
cmpVisual.SetActorSeed(cmd.actorSeed);
+ if (cmd.snapEntity)
+ {
+ let cmpBuildSlot = Engine.QueryInterface(cmd.snapEntity, IID_BuildSlot);
+ if (cmpBuildSlot)
+ cmpBuildSlot.InitConstruction(ent);
+ }
+
// Initialise the foundation
var cmpFoundation = Engine.QueryInterface(ent, IID_Foundation);
cmpFoundation.InitialiseConstruction(player, cmd.template);
Index: binaries/data/mods/public/simulation/helpers/Walls.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/Walls.js
+++ binaries/data/mods/public/simulation/helpers/Walls.js
@@ -1,5 +1,5 @@
/**
- * Returns the wall piece entities needed to construct a wall between start.pos and end.pos. Assumes start.pos != end.pos.
+ * Returns the wall piece entities needed to construct a wall between start.position and end.position. Assumes start.pos != end.position.
* The result is an array of objects, each one containing the following information about a single wall piece entity:
* - 'template': the template name of the entity
* - 'pos': position of the entity, as an object with keys 'x' and 'z'
@@ -28,8 +28,8 @@
let towerWidth = placementData[wallSet.templates.tower].templateData.wallPiece.length;
let dir = {
- "x": end.pos.x - start.pos.x,
- "z": end.pos.z - start.pos.z
+ "x": end.position.x - start.position.x,
+ "z": end.position.z - start.position.z
};
let len = Math.sqrt(dir.x * dir.x + dir.z * dir.z);
@@ -80,9 +80,9 @@
result.push({
"template": placedEntity.template,
- "pos": {
- "x": start.pos.x + (progress + spacing + placedEntity.len/2) * dirNormalized.x,
- "z": start.pos.z + (progress + spacing + placedEntity.len/2) * dirNormalized.z
+ "position": {
+ "x": start.position.x + (progress + spacing + placedEntity.len/2) * dirNormalized.x,
+ "z": start.position.z + (progress + spacing + placedEntity.len/2) * dirNormalized.z
},
"angle": angle,
});
@@ -91,9 +91,9 @@
{
result.push({
"template": wallSet.templates.tower,
- "pos": {
- "x": start.pos.x + (progress + placedEntity.len + 2 * spacing) * dirNormalized.x,
- "z": start.pos.z + (progress + placedEntity.len + 2 * spacing) * dirNormalized.z
+ "position": {
+ "x": start.position.x + (progress + placedEntity.len + 2 * spacing) * dirNormalized.x,
+ "z": start.position.z + (progress + placedEntity.len + 2 * spacing) * dirNormalized.z
},
"angle": angle,
});
Index: binaries/data/mods/public/simulation/templates/template_socket.xml
===================================================================
--- /dev/null
+++ binaries/data/mods/public/simulation/templates/template_socket.xml
@@ -0,0 +1,93 @@
+
+
+
+
+ land
+ own
+ special
+
+
+ true
+
+
+ 500
+ 0.5
+ 5.0
+
+
+ 0
+ 0
+ 1
+
+ 0
+ 0
+ 0
+ 0
+
+
+
+ 10
+
+ 0.85
+ 0.65
+ 0.35
+
+ corpse
+ 0
+ 0
+ true
+
+
+ gaia
+ Socket
+ socket_house
+ true
+
+
+ true
+ true
+ true
+ true
+ false
+ false
+ false
+ false
+
+
+
+
+
+ 0
+ upright
+ false
+ 0.0
+ 6.0
+
+
+
+
+ outline_border.png
+ outline_border_mask.png
+ 0.4
+
+
+
+
+ 6.0
+ 0.6
+ 12.0
+
+
+ true
+ false
+ false
+ false
+
+
+
+ false
+ true
+ false
+ structures/fndn_3x3.xml
+
+
Index: binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml
+++ binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml
@@ -1,7 +1,9 @@
+ socket
House
+ socket_house
300
Index: binaries/data/mods/public/simulation/templates/template_unit_infantry.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_infantry.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_infantry.xml
@@ -24,6 +24,7 @@
1.0
+ template_socket
structures/{civ}_civil_centre
structures/{civ}_crannog
structures/{civ}_military_colony