630 lines
17 KiB
Lua
630 lines
17 KiB
Lua
local UIWidgetBase = require_ex('Common/Core/UIWidgetBase')
|
|
local LuaNodeCache = require_ex('Common/Utils/LuaNodeCache')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot = HL.Class('SettlementDefenseMapRoot', UIWidgetBase)
|
|
|
|
local ENEMY_UPDATE_THREAD_INTERVAL = 0.1
|
|
local BUILDING_UPDATE_THREAD_INTERVAL = 0.5
|
|
|
|
|
|
SettlementDefenseMapRoot.m_towerDefenseGame = HL.Field(HL.Userdata)
|
|
|
|
|
|
SettlementDefenseMapRoot.m_settlementId = HL.Field(HL.Userdata)
|
|
|
|
|
|
SettlementDefenseMapRoot.m_basicUpdateThread = HL.Field(HL.Thread)
|
|
|
|
|
|
SettlementDefenseMapRoot.m_enemyUpdateThread = HL.Field(HL.Thread)
|
|
|
|
|
|
SettlementDefenseMapRoot.m_buildingUpdateThread = HL.Field(HL.Thread)
|
|
|
|
|
|
SettlementDefenseMapRoot.m_leftBottomPos = HL.Field(Vector2)
|
|
|
|
|
|
SettlementDefenseMapRoot.m_rightUpPos = HL.Field(Vector2)
|
|
|
|
|
|
SettlementDefenseMapRoot.m_centerPos = HL.Field(Vector2)
|
|
|
|
|
|
SettlementDefenseMapRoot.m_mapRectWidth = HL.Field(HL.Number) << -1
|
|
|
|
|
|
SettlementDefenseMapRoot.m_mapRectHeight = HL.Field(HL.Number) << -1
|
|
|
|
|
|
SettlementDefenseMapRoot.m_viewRectWidth = HL.Field(HL.Number) << -1
|
|
|
|
|
|
SettlementDefenseMapRoot.m_viewRectHeight = HL.Field(HL.Number) << -1
|
|
|
|
|
|
SettlementDefenseMapRoot.m_mapWidth = HL.Field(HL.Number) << -1
|
|
|
|
|
|
SettlementDefenseMapRoot.m_mapHeight = HL.Field(HL.Number) << -1
|
|
|
|
|
|
SettlementDefenseMapRoot.m_mapRectOffset = HL.Field(Vector2)
|
|
|
|
|
|
SettlementDefenseMapRoot.m_playerRectOffset = HL.Field(Vector2)
|
|
|
|
|
|
SettlementDefenseMapRoot.m_playerAngle = HL.Field(HL.Number) << -1
|
|
|
|
|
|
SettlementDefenseMapRoot.m_playerViewAngle = HL.Field(HL.Number) << -1
|
|
|
|
|
|
SettlementDefenseMapRoot.m_coreIconCache = HL.Field(LuaNodeCache)
|
|
|
|
|
|
SettlementDefenseMapRoot.m_spawnerIconCache = HL.Field(LuaNodeCache)
|
|
|
|
|
|
SettlementDefenseMapRoot.m_enemyIconCache = HL.Field(LuaNodeCache)
|
|
|
|
|
|
SettlementDefenseMapRoot.m_buildingIconCache = HL.Field(LuaNodeCache)
|
|
|
|
|
|
SettlementDefenseMapRoot.m_routeIconCache = HL.Field(LuaNodeCache)
|
|
|
|
|
|
SettlementDefenseMapRoot.m_enemyDataMap = HL.Field(HL.Table)
|
|
|
|
|
|
SettlementDefenseMapRoot.m_buildingDataMap = HL.Field(HL.Table)
|
|
|
|
|
|
SettlementDefenseMapRoot.m_isTransitFinished = HL.Field(HL.Boolean) << false
|
|
|
|
|
|
SettlementDefenseMapRoot.m_hpChangeCallbackList = HL.Field(HL.Table)
|
|
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot._OnFirstTimeInit = HL.Override() << function(self)
|
|
self:RegisterMessage(MessageConst.ON_TOWER_DEFENSE_DEFENDING_BATTLE_BUILDING_ADDED, function(args)
|
|
local nodeId = unpack(args)
|
|
self:_OnBuildingAdded(nodeId)
|
|
end)
|
|
self:RegisterMessage(MessageConst.ON_TOWER_DEFENSE_DEFENDING_BATTLE_BUILDING_REMOVED, function(args)
|
|
local nodeId = unpack(args)
|
|
self:_OnBuildingRemoved(nodeId)
|
|
end)
|
|
self:RegisterMessage(MessageConst.ON_ENEMY_SPAWNER_PREVIEW_ROUTE_CREATED, function(args)
|
|
local routeId = unpack(args)
|
|
self:_OnRouteCreated(routeId)
|
|
end)
|
|
self:RegisterMessage(MessageConst.ON_TOWER_DEFENSE_TRANSIT_FINISHED, function(args)
|
|
self:_OnTransitFinished()
|
|
end)
|
|
end
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot._OnDestroy = HL.Override() << function(self)
|
|
self.m_basicUpdateThread = self:_ClearCoroutine(self.m_basicUpdateThread)
|
|
self.m_enemyUpdateThread = self:_ClearCoroutine(self.m_enemyUpdateThread)
|
|
self.m_buildingUpdateThread = self:_ClearCoroutine(self.m_buildingUpdateThread)
|
|
|
|
if self.m_towerDefenseGame ~= nil then
|
|
local coreAbilitySystems = self.m_towerDefenseGame.tdCoreAbilitySystems
|
|
for coreAbilityIndex = 0, coreAbilitySystems.Count - 1 do
|
|
local coreAbilitySystem = coreAbilitySystems[coreAbilityIndex]
|
|
coreAbilitySystem.onHpChange:Remove(self.m_hpChangeCallbackList[coreAbilityIndex])
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot.InitSettlementDefenseMapRoot = HL.Method() << function(self)
|
|
self.m_towerDefenseGame = GameInstance.player.towerDefenseSystem.towerDefenseGame
|
|
self.m_enemyDataMap = {}
|
|
self.m_buildingDataMap = {}
|
|
self.m_hpChangeCallbackList = {}
|
|
|
|
self:_InitIconCache()
|
|
self:_InitMapRect()
|
|
self:_InitMapCores()
|
|
self:_InitMapSpawners()
|
|
self:_InitMapBuildings()
|
|
self:_InitMapUpdateThread()
|
|
|
|
self:_FirstTimeInit()
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot._InitIconCache = HL.Method() << function(self)
|
|
local originalIcon, iconRoot = self.view.originalIcon, self.view.iconRoot
|
|
|
|
self.m_coreIconCache = LuaNodeCache(originalIcon.coreIcon, iconRoot.coreRoot)
|
|
self.m_spawnerIconCache = LuaNodeCache(originalIcon.spawnerIcon, iconRoot.spawnerRoot)
|
|
self.m_enemyIconCache = LuaNodeCache(originalIcon.enemyIcon, iconRoot.enemyRoot)
|
|
self.m_buildingIconCache = LuaNodeCache(originalIcon.buildingIcon, iconRoot.buildingRoot)
|
|
self.m_routeIconCache = LuaNodeCache(originalIcon.routeIcon, iconRoot.routeRoot)
|
|
end
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot._InitMapRect = HL.Method() << function(self)
|
|
local activeTdId = GameInstance.player.towerDefenseSystem.activeTdId
|
|
if string.isEmpty(activeTdId) then
|
|
return
|
|
end
|
|
|
|
local levelSuccess, levelTableData = Tables.towerDefenseTable:TryGetValue(activeTdId)
|
|
if not levelSuccess then
|
|
return
|
|
end
|
|
|
|
local settlementId = levelTableData.settlementId
|
|
local mapSuccess, mapTableData = Tables.towerDefenseMapTable:TryGetValue(settlementId)
|
|
if not mapSuccess then
|
|
return
|
|
end
|
|
|
|
local mapSprite = self:LoadSprite(UIConst.UI_SPRITE_SETTLEMENT_DEFENSE_MAP, mapTableData.mapImage)
|
|
if mapSprite ~= nil then
|
|
self.view.map.sprite = mapSprite
|
|
self.view.map:SetNativeSize()
|
|
end
|
|
|
|
self.m_leftBottomPos = Vector2(mapTableData.leftBottomPos.x, mapTableData.leftBottomPos.z)
|
|
self.m_rightUpPos = Vector2(mapTableData.rightUpPos.x, mapTableData.rightUpPos.z)
|
|
local direction = self.m_rightUpPos - self.m_leftBottomPos
|
|
self.m_centerPos = direction / 2.0 + self.m_leftBottomPos
|
|
self.m_mapWidth = math.abs(direction.x)
|
|
self.m_mapHeight = math.abs(direction.y)
|
|
|
|
local mapRectScale = self.view.mapRect.localScale.x
|
|
self.m_mapRectWidth = self.view.mapRect.rect.width * mapRectScale
|
|
self.m_mapRectHeight = self.view.mapRect.rect.height * mapRectScale
|
|
|
|
self.m_viewRectWidth = self.view.viewRect.rect.width
|
|
self.m_viewRectHeight = self.view.viewRect.rect.height
|
|
end
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot._InitMapUpdateThread = HL.Method() << function(self)
|
|
self:_PreUpdateAndRefreshAll()
|
|
self.m_basicUpdateThread = self:_StartCoroutine(function()
|
|
while true do
|
|
local uiCtrl = self:GetUICtrl()
|
|
if uiCtrl ~= nil and uiCtrl:IsShow() then
|
|
self:_UpdateMapAndPlayerRectState()
|
|
self:_UpdatePlayerAndCameraRotation()
|
|
|
|
self:_RefreshMapRect()
|
|
self:_RefreshPlayerRect()
|
|
end
|
|
coroutine.step()
|
|
end
|
|
end)
|
|
|
|
self.m_enemyUpdateThread = self:_StartCoroutine(function()
|
|
while true do
|
|
local uiCtrl = self:GetUICtrl()
|
|
if uiCtrl ~= nil and uiCtrl:IsShow() then
|
|
self:_UpdateEnemyDataMap()
|
|
|
|
self:_RefreshEnemiesRect()
|
|
end
|
|
coroutine.wait(ENEMY_UPDATE_THREAD_INTERVAL)
|
|
end
|
|
end)
|
|
|
|
self.m_buildingUpdateThread = self:_StartCoroutine(function()
|
|
while true do
|
|
local uiCtrl = self:GetUICtrl()
|
|
if uiCtrl ~= nil and uiCtrl:IsShow() then
|
|
self:_UpdateBuildingDataMap()
|
|
|
|
self:_RefreshBuildingsState()
|
|
end
|
|
coroutine.wait(BUILDING_UPDATE_THREAD_INTERVAL)
|
|
end
|
|
end)
|
|
end
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot._InitMapCores = HL.Method() << function(self)
|
|
local coreAbilitySystems = self.m_towerDefenseGame.tdCoreAbilitySystems
|
|
for coreAbilityIndex = 0, coreAbilitySystems.Count - 1 do
|
|
local coreAbilitySystem = coreAbilitySystems[coreAbilityIndex]
|
|
local coreWorldPos = coreAbilitySystem.entity.position
|
|
local coreIcon = self.m_coreIconCache:Get()
|
|
self:_RefreshIconRectPos(coreWorldPos, coreIcon)
|
|
coreIcon.indexText.text = string.format("%d", LuaIndex(coreAbilityIndex))
|
|
|
|
local callback = function(entity, changedHp)
|
|
if changedHp < 0 then
|
|
coreIcon.animationWrapper:ClearTween()
|
|
coreIcon.animationWrapper:PlayWithTween(coreIcon.config.ATTACKED_ANIMATION_NAME)
|
|
end
|
|
end
|
|
self.m_hpChangeCallbackList[coreAbilityIndex] = callback
|
|
coreAbilitySystem.onHpChange:Add(callback)
|
|
end
|
|
end
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot._InitMapSpawners = HL.Method() << function(self)
|
|
|
|
|
|
|
|
local spawners = self.m_towerDefenseGame.gameData.portalEffectPosRot
|
|
if spawners == nil then
|
|
return
|
|
end
|
|
for spawnerIndex = 0, spawners.Count - 1 do
|
|
local spawnerWorldPos = spawners[spawnerIndex].position
|
|
local spawnerIcon = self.m_spawnerIconCache:Get()
|
|
self:_RefreshIconRectPos(spawnerWorldPos, spawnerIcon)
|
|
end
|
|
end
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot._InitMapBuildings = HL.Method() << function(self)
|
|
local buildings = self.m_towerDefenseGame.battleBuildings
|
|
for buildingIndex = 0, buildings.Count - 1 do
|
|
self:_BuildBuildingData(buildings[buildingIndex])
|
|
end
|
|
|
|
self:_RefreshBuildingsState()
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot._PreUpdateAndRefreshAll = HL.Method() << function(self)
|
|
self:_UpdateMapAndPlayerRectState()
|
|
self:_UpdatePlayerAndCameraRotation()
|
|
self:_RefreshMapRect()
|
|
self:_RefreshPlayerRect()
|
|
end
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot._UpdateMapAndPlayerRectState = HL.Method() << function(self)
|
|
local character = GameInstance.playerController.mainCharacter
|
|
if not NotNull(character.rootCom.transform) then
|
|
return
|
|
end
|
|
local characterPos = Vector2(character.position.x, character.position.z)
|
|
local worldPosOffset = characterPos - self.m_centerPos
|
|
local rectPosOffset = Vector2(
|
|
-worldPosOffset.x * self.m_mapRectWidth / self.m_mapWidth,
|
|
-worldPosOffset.y * self.m_mapRectHeight / self.m_mapHeight
|
|
)
|
|
|
|
|
|
|
|
self.m_playerRectOffset = Vector2.zero
|
|
local boundX = (self.m_mapRectWidth - self.m_viewRectWidth) / 2.0
|
|
boundX = math.max(boundX, 0)
|
|
if math.abs(rectPosOffset.x) > boundX then
|
|
local absX = math.abs(rectPosOffset.x) - boundX
|
|
self.m_playerRectOffset.x = (rectPosOffset.x > 0 and -1 or 1) * absX
|
|
rectPosOffset.x = rectPosOffset.x + self.m_playerRectOffset.x
|
|
end
|
|
local boundY = (self.m_mapRectHeight - self.m_viewRectHeight) / 2.0
|
|
boundY = math.max(boundY, 0)
|
|
if math.abs(rectPosOffset.y) > boundY then
|
|
local absY = math.abs(rectPosOffset.y) - boundY
|
|
self.m_playerRectOffset.y = (rectPosOffset.y > 0 and -1 or 1) * absY
|
|
rectPosOffset.y = rectPosOffset.y + self.m_playerRectOffset.y
|
|
end
|
|
|
|
self.m_mapRectOffset = rectPosOffset
|
|
end
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot._UpdatePlayerAndCameraRotation = HL.Method() << function(self)
|
|
local character = GameInstance.playerController.mainCharacter
|
|
if not NotNull(character.rootCom.transform) then
|
|
return
|
|
end
|
|
self.m_playerAngle = character.rootCom.transform.eulerAngles.y;
|
|
self.m_playerViewAngle = CameraManager.mainCamera.transform.eulerAngles.y;
|
|
end
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot._UpdateEnemyDataMap = HL.Method() << function(self)
|
|
local enemies = self.m_towerDefenseGame.enemies
|
|
for enemyIndex = 0, enemies.Count - 1 do
|
|
local enemyEntity = enemies[enemyIndex]:Lock()
|
|
if enemyEntity ~= nil then
|
|
local serverId = enemyEntity.serverId
|
|
local enemyData = self.m_enemyDataMap[serverId]
|
|
if enemyData == nil then
|
|
enemyData = {}
|
|
local enemyIcon = self.m_enemyIconCache:Get()
|
|
enemyData.icon = enemyIcon
|
|
self.m_enemyDataMap[serverId] = enemyData
|
|
|
|
|
|
local enemy = enemyEntity.enemy
|
|
local rank = enemy:GetEnemyRank()
|
|
local isElite = rank == CS.Beyond.Gameplay.EnemyRank.Elite or
|
|
rank == CS.Beyond.Gameplay.EnemyRank.Boss
|
|
enemyIcon.mobImage.gameObject:SetActive(not isElite)
|
|
enemyIcon.eliteImage.gameObject:SetActive(isElite)
|
|
end
|
|
enemyData.pos = enemyEntity.position
|
|
enemyData.isUpdated = true
|
|
end
|
|
end
|
|
|
|
for serverId, enemyData in pairs(self.m_enemyDataMap) do
|
|
if not enemyData.isUpdated then
|
|
|
|
local enemyIcon = enemyData.icon
|
|
self.m_enemyIconCache:Cache(enemyIcon)
|
|
self.m_enemyDataMap[serverId] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot._UpdateBuildingDataMap = HL.Method() << function(self)
|
|
for _, buildingData in pairs(self.m_buildingDataMap) do
|
|
local nodeId = buildingData.nodeId
|
|
local state = FactoryUtils.getBuildingStateType(nodeId)
|
|
buildingData.isBroken = state == GEnums.FacBuildingState.Broken
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot._RefreshMapRect = HL.Method() << function(self)
|
|
self.view.mapRect.anchoredPosition = self.m_mapRectOffset
|
|
end
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot._RefreshPlayerRect = HL.Method() << function(self)
|
|
local playerRect = self.view.playerRect
|
|
playerRect.rectTransform.anchoredPosition = self.m_playerRectOffset
|
|
playerRect.playerArrow.localEulerAngles = Vector3(0.0, 0.0, -self.m_playerAngle);
|
|
playerRect.playerView.localEulerAngles = Vector3(0.0, 0.0, -self.m_playerViewAngle);
|
|
end
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot._RefreshEnemiesRect = HL.Method() << function(self)
|
|
for _, enemyData in pairs(self.m_enemyDataMap) do
|
|
local worldPos = enemyData.pos
|
|
local enemyIcon = enemyData.icon
|
|
self:_RefreshIconRectPos(worldPos, enemyIcon)
|
|
enemyData.isUpdated = false
|
|
end
|
|
end
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot._RefreshBuildingsState = HL.Method() << function(self)
|
|
for _, buildingData in pairs(self.m_buildingDataMap) do
|
|
buildingData.icon.brokenNode.gameObject:SetActive(buildingData.isBroken)
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot._RefreshIconRectPos = HL.Method(Vector3, HL.Any) << function(self, worldPos, icon)
|
|
if icon == nil then
|
|
return
|
|
end
|
|
|
|
worldPos = Vector2(worldPos.x, worldPos.z)
|
|
local worldPosOffset = worldPos - self.m_centerPos
|
|
local rectPosOffset = Vector2(
|
|
worldPosOffset.x * self.m_mapRectWidth / self.m_mapWidth,
|
|
worldPosOffset.y * self.m_mapRectHeight / self.m_mapHeight
|
|
)
|
|
icon.rectTransform.anchoredPosition = rectPosOffset
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot._OnBuildingAdded = HL.Method(HL.Number) << function(self, nodeId)
|
|
self:_BuildBuildingData(nodeId)
|
|
end
|
|
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot._OnBuildingRemoved = HL.Method(HL.Number) << function(self, nodeId)
|
|
local buildingData = self.m_buildingDataMap[nodeId]
|
|
if buildingData == nil then
|
|
return
|
|
end
|
|
local icon = buildingData.icon
|
|
self.m_buildingIconCache:Cache(icon)
|
|
self.m_buildingDataMap[nodeId] = nil
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot._OnRouteCreated = HL.Method(HL.Number) << function(self, routeId)
|
|
if self.view.config.NEED_WAIT_TRANSIT_COMPLETE and not self.m_isTransitFinished then
|
|
return
|
|
end
|
|
|
|
self:_CreateRouteIcon(routeId)
|
|
end
|
|
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot._CreateRouteIcon = HL.Method(HL.Number) << function(self, routeId)
|
|
if routeId == nil then
|
|
return
|
|
end
|
|
|
|
local success, worldPos = self.m_towerDefenseGame:GetRouteStartPositionById(routeId)
|
|
if not success then
|
|
return
|
|
end
|
|
|
|
local routeIcon = self.m_routeIconCache:Get()
|
|
routeIcon.gameObject:SetActive(true)
|
|
self:_RefreshIconRectPos(worldPos, routeIcon)
|
|
routeIcon.animationWrapper:PlayInAnimation(function()
|
|
routeIcon.gameObject:SetActive(false)
|
|
self.m_routeIconCache:Cache(routeIcon)
|
|
end)
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot._OnTransitFinished = HL.Method() << function(self)
|
|
self.m_isTransitFinished = true
|
|
|
|
local waitList = self.m_towerDefenseGame.createdRoute
|
|
for _, routeId in cs_pairs(waitList) do
|
|
self:_CreateRouteIcon(routeId)
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
SettlementDefenseMapRoot._BuildBuildingData = HL.Method(HL.Number) << function(self, nodeId)
|
|
if nodeId == nil then
|
|
return
|
|
end
|
|
|
|
local nodeHandler = FactoryUtils.getBuildingNodeHandler(nodeId)
|
|
if nodeHandler == nil then
|
|
return
|
|
end
|
|
|
|
local buildingWorldPos = nodeHandler.transform.worldPosition
|
|
if buildingWorldPos.x < self.m_leftBottomPos.x or buildingWorldPos.x > self.m_rightUpPos.x then
|
|
return
|
|
end
|
|
if buildingWorldPos.z < self.m_leftBottomPos.y or buildingWorldPos.z > self.m_rightUpPos.y then
|
|
return
|
|
end
|
|
|
|
local buildingIcon = self.m_buildingIconCache:Get()
|
|
local state = FactoryUtils.getBuildingStateType(nodeId)
|
|
local isBroken = state == GEnums.FacBuildingState.Broken
|
|
self.m_buildingDataMap[nodeId] = {
|
|
nodeId = nodeId,
|
|
pos = buildingWorldPos,
|
|
icon = buildingIcon,
|
|
isBroken = isBroken,
|
|
}
|
|
|
|
self:_RefreshIconRectPos(buildingWorldPos, buildingIcon)
|
|
end
|
|
|
|
|
|
|
|
|
|
HL.Commit(SettlementDefenseMapRoot)
|
|
return SettlementDefenseMapRoot
|
|
|