File size: 10,868 Bytes
b6a38d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
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("<Id> <style GedConsole>[<SortKey>]</style>"),

	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