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