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 @@ -17,7 +17,8 @@ "" + "land" + "shore" + - "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."); }; /** @@ -111,7 +124,7 @@ } // Check obstructions and terrain passability - var passClassName = ""; + let passClassName = ""; switch (this.template.PlacementType) { case "shore": @@ -124,6 +137,11 @@ passClassName = "default-terrain-only"; break; + case "socket": + // Special passabilityClass assuming "unrestricted" and ignoring collisions with sockets. + passClassName = "socket"; + break; + case "land": default: passClassName = "building-land"; @@ -134,14 +152,20 @@ return result; // Fail + let ret; if (this.template.Category == "Wall") { - // for walls, only test the center point - var ret = cmpObstruction.CheckFoundation(passClassName, true); + // For walls, only test the center point. + ret = cmpObstruction.CheckFoundation(passClassName, true); + } + else if (passClassName == "socket") + { + // Verify no non-socket obstructions exist here. Ignore terrain passability. + ret = cmpObstruction.CheckFoundation(passClassName, false); } else { - var ret = cmpObstruction.CheckFoundation(passClassName, false); + ret = cmpObstruction.CheckFoundation(passClassName, false); } if (ret != "success") @@ -214,13 +238,13 @@ result.message = markForTranslation("%(name)s cannot be built in %(territoryType)s territory. Valid territories: %(validTerritories)s"); result.translateParameters.push("territoryType"); result.translateParameters.push("validTerritories"); - result.parameters.territoryType = {"context": "Territory type", "message": invalidTerritory}; + result.parameters.territoryType = { "context": "Territory type", "message": invalidTerritory }; // gui code will join this array to a string - result.parameters.validTerritories = {"context": "Territory type list", "list": this.GetTerritories()}; + result.parameters.validTerritories = { "context": "Territory type list", "list": this.GetTerritories() }; return result; // Fail } - // Check special requirements + // Check special requirements. if (this.template.PlacementType == "shore") { if (!cmpObstruction.CheckShorePlacement()) @@ -229,6 +253,15 @@ return result; // Fail } } + if (this.template.PlacementType == "socket") + { + if (!cmpObstruction.CheckSocketPlacement("false")) + { + // ToDo: Send the name of the socket where this building ought to be build upon. + result.message = markForTranslation("%(name)s must be built on a valid socket"); + return result; // Fail + } + } let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); 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,9 +1,17 @@ -function Settlement() {} - -Settlement.prototype.Schema = - ""; - -Engine.RegisterComponentType(IID_Settlement, "Settlement", Settlement); +class BuildSlot +{ + get Schema() + { + return "Specifies this is a building slot, a entity where a building can be placed upon." + + "" + + "" + + "true" + + "" + + "" + + "" + + "" + + ""; + } /* * TODO: the vague plan is that this should keep track of who currently owns the settlement, @@ -13,3 +21,63 @@ * 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. */ + + Init() + { + this.owner = INVALID_PLAYER; + }; + + /** + * Initialises construction, thus rendering this socket useless. + * + * @param {number} player - The player requesting the initialisation. + * + * @return {boolean} Whether the initialisation was successful. + */ + InitConstruction(player) + { + this.owner = player; + + 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.owner = INVALID_PLAYER; + + 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 owner. + * + * @return {number} The current owner of this build slot. + */ + GetOwner() + { + return this.owner; + } +} + +Engine.RegisterComponentType(IID_BuildSlot, "BuildSlot", BuildSlot); 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 playerId = 2; + +let cmpBuildSlot = ConstructComponent(buildSlotId, "BuildSlot", { + "HideUponUse": false +}); + +TS_ASSERT_UNEVAL_EQUALS(cmpBuildSlot.GetOwner(), INVALID_PLAYER); +TS_ASSERT_UNEVAL_EQUALS(cmpBuildSlot.InitConstruction(playerId), true); +TS_ASSERT_UNEVAL_EQUALS(cmpBuildSlot.GetOwner(), playerId); Index: binaries/data/mods/public/simulation/templates/template_formation.xml =================================================================== --- binaries/data/mods/public/simulation/templates/template_formation.xml +++ binaries/data/mods/public/simulation/templates/template_formation.xml @@ -43,6 +43,7 @@ false false false + false false false false Index: binaries/data/mods/public/simulation/templates/template_gaia.xml =================================================================== --- binaries/data/mods/public/simulation/templates/template_gaia.xml +++ binaries/data/mods/public/simulation/templates/template_gaia.xml @@ -13,6 +13,7 @@ true true true + false false false false Index: binaries/data/mods/public/simulation/templates/template_structure.xml =================================================================== --- binaries/data/mods/public/simulation/templates/template_structure.xml +++ binaries/data/mods/public/simulation/templates/template_structure.xml @@ -79,6 +79,7 @@ true true false + false false false false 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 @@ -2,6 +2,8 @@ House + socket + template_socket_house 300 Index: binaries/data/mods/public/simulation/templates/template_unit.xml =================================================================== --- binaries/data/mods/public/simulation/templates/template_unit.xml +++ binaries/data/mods/public/simulation/templates/template_unit.xml @@ -65,6 +65,7 @@ false false true + false false false false Index: source/simulation2/components/CCmpObstruction.cpp =================================================================== --- source/simulation2/components/CCmpObstruction.cpp +++ source/simulation2/components/CCmpObstruction.cpp @@ -164,6 +164,9 @@ "" "" "" + "" + "" + "" "" "" "" @@ -196,6 +199,8 @@ m_TemplateFlags |= ICmpObstructionManager::FLAG_BLOCK_CONSTRUCTION; if (paramNode.GetChild("DeleteUponConstruction").ToBool()) m_TemplateFlags |= ICmpObstructionManager::FLAG_DELETE_UPON_CONSTRUCTION; + if (paramNode.GetChild("IsSocket").ToBool()) + m_TemplateFlags |= ICmpObstructionManager::FLAG_IS_SOCKET; m_Flags = m_TemplateFlags; if (paramNode.GetChild("DisableBlockMovement").ToBool()) @@ -549,6 +554,20 @@ cmpWaterManager->GetWaterLevel( back.X, back.Y) - cmpTerrain->GetGroundLevel( back.X, back.Y) < fixed::FromInt(2); } + virtual bool CheckSocketPlacement(const std::string& slotTemplate) const + { + ICmpObstructionManager::ObstructionSquare s; + if (!GetObstructionSquare(s)) + return false; + + // Something useful ought to be checked here,,, + + if (slotTemplate == "true") + return true; + else + return false; + } + virtual EFoundationCheck CheckFoundation(const std::string& className) const { return CheckFoundation(className, false); @@ -576,8 +595,12 @@ return FOUNDATION_CHECK_FAIL_ERROR; } - // Get passability class - pass_class_t passClass = cmpPathfinder->GetPassabilityClass(className); + // Get passability class. Socketted buildings are allowed anywhere where the socket exists. + pass_class_t passClass; + if (className == "socket") + passClass = cmpPathfinder->GetPassabilityClass("unrestricted"); + else + passClass = cmpPathfinder->GetPassabilityClass(className); // Ignore collisions within the same control group, or with other non-foundation-blocking shapes. // Note that, since the control group for each entity defaults to the entity's ID, this is typically @@ -585,6 +608,10 @@ SkipControlGroupsRequireFlagObstructionFilter filter(m_ControlGroup, m_ControlGroup2, ICmpObstructionManager::FLAG_BLOCK_FOUNDATION); + if (className == "socket") + SkipControlGroupsRequireFlagObstructionFilter filter(m_ControlGroup, m_ControlGroup2, + ICmpObstructionManager::FLAG_IS_SOCKET); + if (m_Type == UNIT) return cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Y, m_Clearance, passClass, onlyCenterPoint); else Index: source/simulation2/components/ICmpObstruction.h =================================================================== --- source/simulation2/components/ICmpObstruction.h +++ source/simulation2/components/ICmpObstruction.h @@ -74,6 +74,11 @@ virtual bool CheckShorePlacement() const = 0; /** + * Test whether the square is within a socket of the right type. + */ + virtual bool CheckSocketPlacement(const std::string& socketTemplate) const = 0; + + /** * 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. Index: source/simulation2/components/ICmpObstruction.cpp =================================================================== --- source/simulation2/components/ICmpObstruction.cpp +++ source/simulation2/components/ICmpObstruction.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2018 Wildfire Games. +/* Copyright (C) 2019 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -48,6 +48,7 @@ BEGIN_INTERFACE_WRAPPER(Obstruction) DEFINE_INTERFACE_METHOD_CONST_0("GetUnitRadius", entity_pos_t, ICmpObstruction, GetUnitRadius) DEFINE_INTERFACE_METHOD_CONST_0("CheckShorePlacement", bool, ICmpObstruction, CheckShorePlacement) +DEFINE_INTERFACE_METHOD_CONST_1("CheckSocketPlacement", bool, ICmpObstruction, CheckSocketPlacement, std::string) DEFINE_INTERFACE_METHOD_CONST_2("CheckFoundation", std::string, ICmpObstruction, CheckFoundation_wrapper, std::string, bool) DEFINE_INTERFACE_METHOD_CONST_0("CheckDuplicateFoundation", bool, ICmpObstruction, CheckDuplicateFoundation) DEFINE_INTERFACE_METHOD_CONST_0("GetEntitiesBlockingConstruction", std::vector, ICmpObstruction, GetEntitiesBlockingConstruction) Index: source/simulation2/components/ICmpObstructionManager.h =================================================================== --- source/simulation2/components/ICmpObstructionManager.h +++ source/simulation2/components/ICmpObstructionManager.h @@ -86,7 +86,8 @@ FLAG_BLOCK_CONSTRUCTION = (1 << 2), // prevents buildings being constructed on this shape FLAG_BLOCK_PATHFINDING = (1 << 3), // prevents the tile pathfinder choosing paths through this shape FLAG_MOVING = (1 << 4), // indicates this unit is currently moving - FLAG_DELETE_UPON_CONSTRUCTION = (1 << 5) // this entity is deleted when construction of a building placed on top of this entity starts + FLAG_DELETE_UPON_CONSTRUCTION = (1 << 5), // this entity is deleted when construction of a building placed on top of this entity starts + FLAG_IS_SOCKET = (1 << 6) // this entity is a slot where construction of a building placed on top of this entity starts }; /** Index: source/simulation2/components/tests/test_ObstructionManager.h =================================================================== --- source/simulation2/components/tests/test_ObstructionManager.h +++ source/simulation2/components/tests/test_ObstructionManager.h @@ -35,6 +35,7 @@ virtual void SetUnitClearance(const entity_pos_t& UNUSED(clearance)) { } virtual bool IsControlPersistent() const { return true; } virtual bool CheckShorePlacement() const { return true; } + virtual bool CheckSocketPlacement(const std::string& UNUSED(socketTemplate)) const { return true; } virtual EFoundationCheck CheckFoundation(const std::string& UNUSED(className)) const { return EFoundationCheck(); } virtual EFoundationCheck CheckFoundation(const std::string& UNUSED(className), bool UNUSED(onlyCenterPoint)) const { return EFoundationCheck(); } virtual std::string CheckFoundation_wrapper(const std::string& UNUSED(className), bool UNUSED(onlyCenterPoint)) const { return std::string(); }