local height_tile = const.HeightTileSize local table_find = table.find local function CheckProp(name, value) value = value or false return function(self) return self[name] == value end end local OVRLP_DEL_NONE, OVRLP_DEL_ALL, OVRLP_DEL_IGNORE, OVRLP_DEL_PARTIAL, OVRLP_DEL_SINGLE = false, 0, 1, 2, 3 local function OnObjOverlapItems() return { { value = OVRLP_DEL_NONE, text = "Do nothing" }, { value = OVRLP_DEL_ALL, text = "Delete the entire collection" }, { value = OVRLP_DEL_IGNORE, text = "Delete ignoring collections" }, { value = OVRLP_DEL_PARTIAL, text = "Delete if the collection is outside" }, { value = OVRLP_DEL_SINGLE, text = "Delete if not in collection" }, } end function PrefabRadiusEstimItems() return { { value = "incircle", text = "Incircle (Min)", color = red }, { value = "excircle", text = "Excircle (Max)", color = green }, { value = "amean", text = "Arithmetic Mean (Average)", color = yellow }, { value = "gmean", text = "Geometric Mean (Ellipse)", color = blue }, { value = "bestfit", text = "Best Fit (Circle)", color = cyan }, } end function PrefabRadiusEstimators() return { incircle = function(prefab) return prefab.min_radius end, excircle = function(prefab) return prefab.max_radius end, amean = function(prefab) return (prefab.min_radius + prefab.max_radius) / 2 end, gmean = function(prefab) return sqrt(prefab.min_radius * prefab.max_radius) end, bestfit = function(prefab) return sqrt(prefab.total_area * 7 / 22) end, } end DefineClass.PrefabType = { __parents = { "Preset", }, properties = { { category = "General", id = "OnObjOverlap", name = "On Object Overlap", editor = "choice", default = OVRLP_DEL_ALL, items = OnObjOverlapItems }, { category = "General", id = "RespectBounds", name = "Respect Type Bounds", editor = "bool", default = true, help = "Disable prefab objects spill beyond the their prefab type boundaries. Doesn't affect POI prefabs as they can share multiple prefab types." }, { category = "General", id = "OverlapReduct", name = "Lim Excircle Overlap", editor = "number", default = 1, min = 0, max = 4, slider = true, help = "Prioritize prefabs with better incircle to excircle radius ratio (the best being 1, a perfect circle)" }, { category = "General", id = "FitEffort", name = "Prefab Fit Effort", editor = "number", default = 1, min = 0, max = 4, slider = true, help = "Prioritize prefabs fitting better the available space (using the radius estimate)" }, { category = "General", id = "RadiusEstim", name = "Radius Estimate", editor = "choice", default = "bestfit", items = PrefabRadiusEstimItems, help = "Used to estimate the prefab real form by a circle when fitting prefabs" }, { category = "General", id = "PlaceRadius", name = "Min Prefab Radius", editor = "number", default = height_tile, scale = "m", help = "Ignore prefabs with radius estimate below that value" }, { category = "General", id = "FitPasses", name = "Max Fit Passes", editor = "number", default = 5, min = 1, max = 5, slider = true, help = "Maximum number of prefab fitting passes" }, { category = "General", id = "MinFillRatio", name = "Min Fill Ratio (%)", editor = "number", default = 100, min = 0, max = 100, slider = true, help = "How much of the prefab type surface would be filled at least (actual value can be bigger, but not lesser)" }, { category = "General", id = "MaxFillError", name = "Max Fill Err (%)", editor = "number", default = 10, min = 0, max = 1000, scale = 10, slider = true, help = "How much of the prefab type surface specified to be filled could remain unfilled" }, { category = "General", id = "Tags", name = "Tags", editor = "set", default = empty_table, items = PrefabTagsCombo }, { category = "Terrain", id = "Transition", name = "Transition", editor = "number", default = 0, scale = "m", granularity = height_tile, help = "Transition zone for texture dithering" }, { category = "Terrain", id = "TexturingOrder", name = "Texturing Order", editor = "number", default = 0, help = "Sort key used when applying prefab type terrain. Types with equal order are compared based on the descending transition dist." }, { category = "Terrain", id = "TextureMain", name = "Main Texture", editor = "choice", default = "", items = GetTerrainNamesCombo(), }, { category = "Terrain", id = "GrassMain", name = "Main Grass (%)", editor = "number", default = 100, min = 0, max = 200, slider = true, no_edit = CheckProp("TextureMain", "") }, { category = "Terrain", id = "PreviewMain", name = "Main Preview", editor = "image", default = false, img_size = 128, img_box = 1, base_color_map = true, dont_save = true, no_edit = CheckProp("TextureMain", "") }, { category = "Terrain", id = "TextureFlow", name = "Flow Texture", editor = "choice", default = "", items = GetTerrainNamesCombo(), }, { category = "Terrain", id = "GrassFlow", name = "Flow Grass (%)", editor = "number", default = 100, min = 0, max = 200, slider = true, no_edit = CheckProp("TextureFlow", "") }, { category = "Terrain", id = "FlowStrength", name = "Flow Strength", editor = "number", default = 100, min = 0, max = 200, scale = 100, slider = true }, { category = "Terrain", id = "FlowContrast", name = "Flow Contrast", editor = "number", default = 100, min = 0, max = 300, scale = 100, slider = true }, { category = "Terrain", id = "PreviewFlow", name = "Flow Preview", editor = "image", default = false, img_size = 128, img_box = 1, base_color_map = true, dont_save = true, no_edit = CheckProp("TextureFlow", "") }, { category = "Terrain", id = "TextureNoise", name = "Noise Texture", editor = "choice", default = "", items = GetTerrainNamesCombo(), }, { category = "Terrain", id = "GrassNoise", name = "Noise Grass (%)", editor = "number", default = 100, min = 0, max = 200, slider = true, no_edit = CheckProp("TextureNoise", "") }, { category = "Terrain", id = "PreviewNoise", name = "Noise Preview", editor = "image", default = false, img_size = 128, img_box = 1, base_color_map = true, dont_save = true, no_edit = CheckProp("TextureNoise", "") }, { category = "Terrain", id = "HeightModulated",name = "Height Modulated", editor = "bool", default = false, }, { category = "Terrain", id = "NoiseStrength", name = "Noise Strength", editor = "number", default = 100, min = 0, max = 200, scale = 100, slider = true }, { category = "Terrain", id = "NoiseContrast", name = "Noise Contrast", editor = "number", default = 100, min = 0, max = 300, scale = 100, slider = true }, { category = "Terrain", id = "NoisePreset", name = "Noise Pattern", editor = "preset_id", default = "", preset_class = "NoisePreset" }, { category = "Terrain", id = "NoisePreview", name = "Noise Preview", editor = "grid", default = false, no_edit = function(self) return self.NoisePreset == "" end, frame = 1, min = 64, dont_save = true, read_only = true }, { category = "Editor", id = "OverlayColor", name = "Overlay Color", editor = "color", default = false, alpha = false }, { category = "Editor", id = "FillPrefabList", name = "Fill Prefab List", editor = "string_list", default = false, read_only = true, dont_save = true }, { category = "Editor", id = "POIPrefabList", name = "POI Prefab List", editor = "string_list", default = false, read_only = true, dont_save = true }, { category = "Editor", id = "POIList", name = "POI List", editor = "string_list", default = false, read_only = true, dont_save = true }, }, EditorMenubarName = "Prefab Types", EditorMenubar = "Map.Generate", EditorIcon = "CommonAssets/UI/Icons/puzzle.png", EditorView = Untranslated(" "), StoreAsTable = false, GlobalMap = "PrefabTypeToPreset", HasSortKey = true, GetPreviewMain = function(self) return GetTerrainTexturePreview(self.TextureMain) end, GetPreviewFlow = function(self) return GetTerrainTexturePreview(self.TextureFlow) end, GetPreviewNoise = function(self) return GetTerrainTexturePreview(self.TextureNoise) end, GetNoisePreview = function(self) return GetNoisePreview(self.NoisePreset) end, } function PrefabType:Compare(other) local sa, sb = self.SortKey, other.SortKey if sa ~= sb then return sa < sb end local ba, bb = self.RespectBounds and 1 or 0, other.RespectBounds and 1 or 0 if ba ~= bb then return ba > bb end local ooa, oob = (self.OnObjOverlap or -1), (other.OnObjOverlap or -1) if ooa ~= oob then return ooa > oob end return self.id < other.id end function PrefabType:GetPOIPrefabList() local names = {} local prefabs = PrefabMarkers or empty_table local poi_to_preset = PrefabPoiToPreset or empty_table local ptype = self.id for _, prefab in ipairs(prefabs) do local poi_type = prefab and prefab.poi_type or "" if poi_type ~= "" then local preset = poi_to_preset[poi_type] for _, group in pairs(preset and preset.PrefabTypeGroups) do if table_find(group.types, ptype) then names[#names + 1] = prefabs[prefab] break end end end end if #names == 0 then return end table.sort(names) return names end function PrefabType:GetPOIList() local ptype = self.id local list = {} for name, preset in pairs(PrefabPoiToPreset) do for _, group in pairs(preset.PrefabTypeGroups) do if table_find(group.types, ptype) then list[#list + 1] = name break end end end table.sort(list) return list end function PrefabType:GetFullPrefabList() local names = {} local prefabs = PrefabMarkers or empty_table local ptype = self.id for _, prefab in ipairs(prefabs) do local poi_type = prefab and prefab.poi_type or "" if poi_type == "" and (prefab.type == "" or prefab.type == ptype) then names[#names + 1] = prefabs[prefab] end end if #names == 0 then return end table.sort(names) return names end function GetPrefabTypeList() return table.keys(PrefabTypeToPreset, true) end function GetPrefabTypeTags(add_empty) local tags = {} for ptype, preset in pairs(PrefabTypeToPreset) do for tag in pairs(preset and preset.Tags or empty_table) do if tag ~= "" then tags[tag] = true end end end tags = table.keys(tags, true) if add_empty then table.insert(tags, 1, add_empty) end return tags end function OnMsg.GedPropertyEdited(_, obj) if IsKindOf(obj, "NoisePreset") then ForEachPreset("PrefabType", function(ptype) if ptype.NoisePreset == obj.id then ObjModified(ptype) end end) end end