local cardinal_dirs = {"east", "west", "north", "south"} local dir_angle = { ["east"] = 90 * 60, ["west"] = 270 * 60, ["south"] = 180 * 60, ["north"] = 0 } local function GetPlaneDir(plane) for _, dir in ipairs(cardinal_dirs) do if string.match(plane:lower(), dir) then return dir end end end MapVar("s_BorderBuildingAutoAttachesRequired", 0) MapVar("s_BorderBuildingAutoAttachesCreated", 0) DefineClass.BorderBuilding = { __parents = {"AutoAttachObject", "EditorCallbackObject", "EditorSelectedObject"}, properties = { {category = "Windows Auto Attaches", id = "east", name = "East", editor = "bool", default = true, help = "Uncheck to remove window auto attaches from this side"}, {category = "Windows Auto Attaches", id = "west", name = "West", editor = "bool", default = true, help = "Uncheck to remove window auto attaches from this side"}, {category = "Windows Auto Attaches", id = "north", name = "North", editor = "bool", default = true, help = "Uncheck to remove window auto attaches from this side"}, {category = "Windows Auto Attaches", id = "south", name = "South", editor = "bool", default = true, help = "Uncheck to remove window auto attaches from this side"}, {category = "Windows Auto Attaches", id = "Recalc", editor = "buttons", default = false, buttons = { { name = "Recalc Visible Sides", func = function(self) self:CalcVisibleSides() end}, { name = "Turn All Sides ON", func = function(self) self:TurnAllSides(true) end}, { name = "Turn All Sides OFF", func = function(self) self:TurnAllSides(false) end}, }, }, }, texts = false, } function BorderBuilding:ShouldAttach(attach) s_BorderBuildingAutoAttachesRequired = s_BorderBuildingAutoAttachesRequired + 1 local spot_ann = self:GetSpotAnnotation(attach.spot_idx) if not spot_ann then return true end local dir = GetPlaneDir(spot_ann) if not dir then return true end return self[dir] end function BorderBuilding:OnAttachCreated(attach, spot) s_BorderBuildingAutoAttachesCreated = s_BorderBuildingAutoAttachesCreated + 1 if IsKindOfClasses(attach, "WindowTunnelObject", "Door") then attach.AttachLight = false end end function BorderBuilding:UpdateAutoAttaches() self:SetAutoAttachMode(self:GetAutoAttachMode()) end function BorderBuilding:GatherWallWindows() local spots_used = {} local spots = table.imap(AutoAttachPresets[self.class], function(attach) spots_used[attach.name] = true end) local spots = table.keys(spots_used) local planes = {} local sides = {} for _, spot_name in ipairs(spots) do local first, last = self:GetSpotRange(spot_name) for spot_idx = first, last do local spot_pos = self:GetSpotPos(spot_idx) local spot_ann = self:GetSpotAnnotation(spot_idx) if spot_ann then local dir = GetPlaneDir(spot_ann) if dir then local spot = {pos = spot_pos, name = spot_name, idx = spot_idx} planes[spot_ann] = planes[spot_ann] or {dir = dir} table.insert(planes[spot_ann], spot) sides[dir] = sides[dir] or {} table.insert(sides[dir], spot) end end end end return planes, sides end function BorderBuilding:CalcVisibleSides() local center = GetMapBox():Center() local planes, sides = self:GatherWallWindows() local side_count = {} for plane_name, plane in pairs(planes) do local dir = GetPlaneDir(plane_name) local angle = self:GetAngle() + dir_angle[dir] local plane_norm = Rotate(point(4096, 0), angle) local plane_data = side_count[dir] or {total = 0, visible = 0} side_count[dir] = plane_data plane_data.total = plane_data.total + #plane if Dot(center - plane[1].pos, plane_norm) > 0 then plane_data.visible = plane_data.visible + #plane end end for dir, side in pairs(side_count) do self:SetProperty(dir, side.visible > side.total / 2) end ObjModified(self) self:UpdateAutoAttaches() end function BorderBuilding:TurnAllSides(state) for _, dir in pairs(cardinal_dirs) do self:SetProperty(dir, state) end ObjModified(self) self:UpdateAutoAttaches() end function BorderBuilding:OnEditorSetProperty(prop_id) if table.find(cardinal_dirs, prop_id) then self:UpdateAutoAttaches() end end function BorderBuilding:DelayedRecalcAutoAttaches() DelayedCall(500, function(self) self:CalcVisibleSides() self:UpdateAutoAttaches() end, self) end BorderBuilding.EditorCallbackMove = BorderBuilding.DelayedRecalcAutoAttaches BorderBuilding.EditorCallbackRotate = BorderBuilding.DelayedRecalcAutoAttaches BorderBuilding.EditorCallbackPlace = BorderBuilding.DelayedRecalcAutoAttaches function BorderBuilding:EditorSelect(selected) if selected then local center = self:GetPos() local high_z = self:GetObjectBBox():sizez() + guim local _, sides = self:GatherWallWindows() for dir, spots in pairs(sides) do local pos = point30 for _, spot in pairs(spots) do pos = pos + spot.pos - center end pos = (pos / #spots):SetZ(high_z) local text = PlaceText(dir:upper(), pos, const.clrGreen) self:Attach(text) text:SetAttachOffset(pos) self.texts = self.texts or {} table.insert(self.texts, text) end elseif self.texts then for _, text in ipairs(self.texts) do if IsValid(text) then text:Detach() DoneObject(text) end end self.texts = false end end function BorderBuildingsRecalcVisibleSides() s_BorderBuildingAutoAttachesRequired = 0 s_BorderBuildingAutoAttachesCreated = 0 local buildings = 0 MapForEach("map", "BorderBuilding", function(bld) buildings = buildings + 1 bld:CalcVisibleSides() bld:UpdateAutoAttaches() end) printf("BorderBuilding(s): %d, Auto Attaches Created/Required: %d/%d(%d%%)", buildings, s_BorderBuildingAutoAttachesCreated, s_BorderBuildingAutoAttachesRequired, 100 * s_BorderBuildingAutoAttachesCreated / s_BorderBuildingAutoAttachesRequired) end function SelectionBorderBuildingToggleWall(dir) for _, obj in ipairs(editor.GetSel() or empty_table) do if IsKindOf(obj, "BorderBuilding") then obj:SetProperty(dir, not obj:GetProperty(dir)) obj:UpdateAutoAttaches() end end end