|
if not Platform.editor or not Platform.developer then |
|
return |
|
end |
|
|
|
local type_tile = const.TypeTileSize |
|
local pct_mul = 100 |
|
|
|
|
|
|
|
function Biome:CalcTypeMixingPreview() |
|
local preview = NewComputeGrid(256, 256, "U", 16) |
|
self:GetTypeMixingGrid(preview) |
|
self.TypeMixingPreview = preview |
|
return preview |
|
end |
|
|
|
function Biome:GetFilteredPrefabsPreview() |
|
return #(self:GetFilteredPrefabs() or empty_table) |
|
end |
|
|
|
function Biome:GetTypeMixingPreview() |
|
return self.TypeMixingPreview or self:CalcTypeMixingPreview() |
|
end |
|
|
|
local function RecalcTypeMixingPreview(ged) |
|
local parent = ged:GetParentOfKind("SelectedObject", "Biome") |
|
if parent then |
|
parent:CalcTypeMixingPreview() |
|
end |
|
end |
|
|
|
function BiomePrefabTypeWeight:OnAfterEditorNew(parent, ged, is_paste) |
|
RecalcTypeMixingPreview(ged) |
|
end |
|
|
|
function BiomePrefabTypeWeight:OnAfterEditorDelete(parent, ged) |
|
RecalcTypeMixingPreview(ged) |
|
end |
|
|
|
function BiomePrefabTypeWeight:OnEditorSetProperty(prop_id, old_value, ged) |
|
RecalcTypeMixingPreview(ged) |
|
end |
|
|
|
|
|
|
|
if FirstLoad then |
|
g_BiomeFiller = false |
|
end |
|
|
|
BiomeFiller.dbg_paused = false |
|
BiomeFiller.dbg_interrupt = false |
|
BiomeFiller.dbg_placed_prefabs = false |
|
BiomeFiller.dbg_prefab_types = false |
|
BiomeFiller.dbg_visible_prefabs = false |
|
BiomeFiller.dbg_placed_objects = false |
|
BiomeFiller.dbg_placed_poi = false |
|
BiomeFiller.dbg_obj_to_prefab_mark = false |
|
BiomeFiller.dbg_grid_thread = false |
|
BiomeFiller.dbg_inspect = false |
|
BiomeFiller.dbg_filter_tags = false |
|
BiomeFiller.dbg_filter_name = "" |
|
BiomeFiller.dbg_view_mark = 0 |
|
|
|
function BiomeFiller:DbgInit() |
|
g_BiomeFiller = self |
|
for _, prop in ipairs(self:GetProperties()) do |
|
if prop.category == "Results" then |
|
self[prop.id] = nil |
|
end |
|
end |
|
self.dbg_paused = nil |
|
self.dbg_interrupt = nil |
|
self.dbg_placed_prefabs = nil |
|
self.dbg_prefab_types = nil |
|
self.dbg_visible_prefabs = nil |
|
self.dbg_placed_objects = nil |
|
self.dbg_placed_poi = nil |
|
self.dbg_obj_to_prefab_mark = nil |
|
self.dbg_inspect = nil |
|
self.dbg_filter_tags = nil |
|
self.dbg_filter_name = nil |
|
self.dbg_view_mark = nil |
|
self:DbgClear() |
|
ObjModified(self) |
|
end |
|
|
|
function BiomeFiller:ActionTogglePause() |
|
self.dbg_paused = not self.dbg_paused |
|
Msg(self) |
|
end |
|
|
|
function BiomeFiller:ActionInterrupt() |
|
self.dbg_interrupt = true |
|
self.dbg_paused = false |
|
Msg(self) |
|
end |
|
|
|
local function DbgGetPalette(grid, name, prefab_list) |
|
local palette, remap = {}, {} |
|
local level_map = GridLevels(grid) |
|
local count = 0 |
|
if name == "Types" then |
|
local ptype_to_preset = PrefabTypeToPreset |
|
local idx_to_ptype = GetPrefabTypeList() |
|
local ptypes = {} |
|
for ptype_idx in pairs(level_map) do |
|
local ptype = idx_to_ptype[ptype_idx] |
|
if ptype then |
|
local val = ptypes[ptype] |
|
if not val then |
|
count = count + 1 |
|
val = count |
|
ptypes[ptype] = val |
|
local preset = ptype_to_preset[ptype] |
|
local color = preset and preset.OverlayColor or RandColor(xxhash(ptype)) |
|
palette[val] = color |
|
end |
|
remap[ptype_idx] = val |
|
end |
|
end |
|
else |
|
local i = 1 |
|
local minv, maxv = max_int, 0 |
|
for level in pairs(level_map) do |
|
if level ~= 0 then |
|
local val = count % 255 + 1 |
|
remap[level] = val |
|
minv = Min(minv, val) |
|
maxv = Max(maxv, val) |
|
count = count + 1 |
|
end |
|
end |
|
if count > 0 then |
|
palette[minv] = RGB(128, 128, 128) |
|
palette[maxv] = white |
|
if count > 2 then |
|
count = Min(255, count) |
|
local max_value = 1024 |
|
local max_hue = 0 |
|
local min_hue = max_value * 2 / 3 |
|
for val = minv, maxv do |
|
local hue = min_hue + (max_hue - min_hue) * (val - minv) / (maxv - minv) |
|
palette[val] = HSB(hue, max_value, max_value, max_value) |
|
end |
|
end |
|
end |
|
end |
|
return palette, remap |
|
end |
|
|
|
function BiomeFiller:DbgDone() |
|
self.Overlay = nil |
|
self.InspectMode = nil |
|
self.InspectFilter = nil |
|
self:DbgUpdateShow() |
|
self:DbgClear() |
|
GedObjectDeleted(self) |
|
end |
|
|
|
BiomeFiller.dbg_overlay_grid = false |
|
function BiomeFiller:DbgClear() |
|
DbgHideTerrainGrid(self.dbg_overlay_grid) |
|
self.dbg_overlay_grid = nil |
|
DbgClear() |
|
editor.ClearSel() |
|
end |
|
|
|
function BiomeFiller:OnEditorSetProperty(prop_id, ...) |
|
local prop = self:GetPropertyMetadata(prop_id) |
|
if prop and prop.update_dbg then |
|
self:DbgUpdateShow() |
|
end |
|
return GridOp.OnEditorSetProperty(self, prop_id, ...) |
|
end |
|
|
|
function BiomeFiller:GetGridPreview() |
|
local modes = self:GetGridModes() |
|
for key, value in pairs(self.PreviewSet) do |
|
if value then |
|
return modes[key] |
|
end |
|
end |
|
end |
|
|
|
BiomeFiller.dbg_palettes = false |
|
function BiomeFiller:DbgUpdate() |
|
self.dbg_palettes = false |
|
self:DbgUpdateShow() |
|
editor.ClearSel() |
|
end |
|
|
|
function BiomeFiller:DbgUpdatePalette(name) |
|
name = name or self:DbgGetOverlay() |
|
self.dbg_palettes = self.dbg_palettes or {} |
|
local grid = self:GetGridModes()[name] |
|
local info = self.dbg_palettes[name] or empty_table |
|
local edges = self.OverlayEdges |
|
local prev_grid, prev_edges = info[3], info[5] or false |
|
if grid and (prev_grid ~= grid or prev_edges ~= edges) then |
|
local palette, remap = DbgGetPalette(grid, name, self.PrefabList or empty_table) |
|
local edge |
|
if edges then |
|
edge = GridDest(grid) |
|
GridEdge(grid, edge) |
|
GridNot(edge) |
|
end |
|
local g = GridDest(grid) |
|
GridReplace(grid, g, remap) |
|
if edges then |
|
GridMulDiv(g, edge, 1) |
|
end |
|
info = { palette, remap, grid, g, edges } |
|
self.dbg_palettes[name] = info |
|
end |
|
return table.unpack(info) |
|
end |
|
|
|
function BiomeFiller:DbgGetColor(mark) |
|
local overlay = self:DbgGetOverlay() or "Marks" |
|
local palette, remap = self:DbgUpdatePalette(overlay) |
|
local pidx = remap[mark] |
|
return pidx and palette[pidx] |
|
end |
|
|
|
function BiomeFiller:DbgGetOverlay() |
|
for ov, value in pairs(self.Overlay) do |
|
if value then |
|
return ov |
|
end |
|
end |
|
end |
|
|
|
function BiomeFiller:DbgUpdateShow() |
|
local new_overlay = self:DbgGetOverlay() |
|
DeleteThread(self.dbg_grid_thread) |
|
if not new_overlay then |
|
DbgShowTerrainGrid(false) |
|
else |
|
self.dbg_grid_thread = CreateRealTimeThread(function() |
|
WaitNextFrame(1) |
|
local palette, remap, dgrid, g = self:DbgUpdatePalette(new_overlay) |
|
self.dbg_overlay_grid = g |
|
DbgShowTerrainGrid(g, palette) |
|
end) |
|
end |
|
|
|
local new_inspect = false |
|
for ins, value in pairs(self.InspectMode) do |
|
if value then |
|
new_inspect = ins |
|
break |
|
end |
|
end |
|
|
|
local filter_tags = false |
|
for tag, value in pairs(self.InspectFilter) do |
|
if value then |
|
filter_tags = table.create_set(filter_tags, tag, true) |
|
end |
|
end |
|
local filter_name = self.InspectPattern |
|
|
|
local prefab_to_tags = {} |
|
local function GetPrefabTags(prefab) |
|
local tags = prefab_to_tags[prefab] |
|
if tags == nil then |
|
local poi_tags = prefab.poi_type and table.get(PrefabPoiToPreset, prefab.poi_type, "Tags") |
|
local ptype_tags = prefab.type and table.get(PrefabTypeToPreset, prefab.type, "Tags") |
|
local marker_tags = prefab.tags |
|
tags = {} |
|
table.append(tags, ptype_tags) |
|
table.append(tags, marker_tags) |
|
table.append(tags, poi_tags) |
|
tags = table.keys(tags, true) |
|
prefab_to_tags[prefab] = #tags > 0 and tags or false |
|
end |
|
return tags |
|
end |
|
local function PrefabTagsToStr(tags, sep) |
|
return table.concat(tags, sep or ", ") |
|
end |
|
function DbgShowPrefab(pos, name, color, mark, rmin, rmax) |
|
local prefab = name and PrefabMarkers[name] |
|
local tags = prefab and GetPrefabTags(prefab) or empty_table |
|
if filter_tags then |
|
local found |
|
for _, tag in ipairs(tags) do |
|
if filter_tags[tag] then |
|
found = true |
|
break |
|
end |
|
end |
|
if not found then |
|
return |
|
end |
|
end |
|
if name and filter_name ~= "" and not string.find(name, filter_name) then |
|
return |
|
end |
|
if rmin then |
|
DbgAddCircle(pos, rmin * type_tile, color, -1, guim) |
|
if rmax then |
|
DbgAddCircle(pos, rmax * type_tile, color, -1, guim) |
|
end |
|
end |
|
DbgAddVector(pos, 50*guim, color) |
|
if name then |
|
if mark then |
|
name = name .. " (" .. mark .. ")" |
|
end |
|
local poi_type = prefab and prefab.poi_type or "" |
|
if poi_type ~= "" then |
|
name = name .. " " .. poi_type |
|
end |
|
if #tags > 0 then |
|
name = name .. " [" .. PrefabTagsToStr(tags) .. "]" |
|
end |
|
DbgAddText(name, ValidateZ(pos):AddZ(50*guim), color) |
|
end |
|
return true |
|
end |
|
|
|
if self.dbg_inspect ~= new_inspect |
|
or self.dbg_filter_name ~= filter_name |
|
or not table.equal_values(self.dbg_filter_tags, filter_tags) then |
|
self.dbg_inspect = new_inspect |
|
self.dbg_filter_tags = filter_tags |
|
self.dbg_filter_name = filter_name |
|
DbgClear() |
|
DbgStopInspect() |
|
if new_inspect == "Pos" then |
|
DbgInspectThread = CreateMapRealTimeThread(function() |
|
WaitNextFrame(1) |
|
local shown = 0 |
|
for mark, info in ipairs(self.PrefabList or empty_table) do |
|
local name, pos = table.unpack(info) |
|
local color = self:DbgGetColor(mark) |
|
if color and DbgShowPrefab(pos, name, color, mark) then |
|
shown = shown + 1 |
|
end |
|
end |
|
print("Shown", shown, "prefabs") |
|
end) |
|
elseif new_inspect == "POI" then |
|
DbgInspectThread = CreateMapRealTimeThread(function() |
|
WaitNextFrame(1) |
|
local poi_to_preset = PrefabPoiToPreset |
|
local shown = 0 |
|
for mark, info in ipairs(self.PrefabList or empty_table) do |
|
local name, pos = table.unpack(info) |
|
local prefab = PrefabMarkers[name] |
|
local poi_type = prefab and prefab.poi_type or "" |
|
local preset = poi_type ~= "" and poi_to_preset[poi_type] |
|
if preset and DbgShowPrefab(pos, name, preset.OverlayColor, mark, prefab.max_radius) then |
|
shown = shown + 1 |
|
end |
|
end |
|
print("Shown", shown, "prefabs") |
|
end) |
|
elseif new_inspect == "Rollover" then |
|
DbgInspectThread = CreateMapRealTimeThread(function() |
|
local last_mark, last_prefab, last_mark, last_click = 0 |
|
while DbgInspectThread == CurrentThread() do |
|
DbgClearColors() |
|
local mark = 0 |
|
if self.dbg_obj_to_prefab_mark then |
|
local solid, transparent = GetPreciseCursorObj() |
|
local obj = GetTopmostParent(transparent or solid) |
|
mark = obj and self.dbg_obj_to_prefab_mark[obj] or 0 |
|
if obj then |
|
DbgSetColor(obj, white) |
|
end |
|
end |
|
if mark == 0 and self.MarkGrid and self.PrefabList then |
|
local pos = DbgGetInspectPos() |
|
mark = GridGetMark(self.MarkGrid, pos) or 0 |
|
end |
|
if mark ~= last_mark then |
|
last_mark = mark |
|
DbgClear() |
|
local name, pos, color |
|
if mark ~= 0 then |
|
local info = self.PrefabList[mark] or empty_table |
|
name, pos = table.unpack(info) |
|
pos = pos:SetInvalidZ() |
|
color = self:DbgGetColor(mark) or white |
|
end |
|
if name then |
|
local prefab = PrefabMarkers[name] |
|
DbgShowPrefab(pos, name, color, mark, prefab.min_radius, prefab.max_radius) |
|
end |
|
last_prefab = name |
|
WaitNextFrame(1) |
|
else |
|
WaitNextFrame(10) |
|
end |
|
local tool = GetDialog("XSelectObjectsTool") |
|
local terrain_pos = tool and tool.last_mouse_click |
|
if not terrain_pos then |
|
last_click = nil |
|
elseif terrain_pos ~= last_click then |
|
last_click = terrain_pos |
|
local prefab = PrefabMarkers[last_prefab] or empty_table |
|
self.SelectedPrefab = last_prefab |
|
self.SelectedMark = last_mark |
|
self.SelectedTags = PrefabTagsToStr(GetPrefabTags(prefab)) |
|
self.SelectedPoi = prefab.poi_type |
|
self:DbgOnModified() |
|
end |
|
end |
|
end) |
|
end |
|
end |
|
end |
|
|
|
local function BiomeFiller_ObjModified(obj) |
|
ObjModified(obj) |
|
end |
|
|
|
function BiomeFiller:DbgOnModified() |
|
DelayedCall(20, BiomeFiller_ObjModified, self) |
|
end |
|
|
|
local function ChangeSortKey(key, ...) |
|
local keys = {...} |
|
local idx = table.find(keys, key) |
|
return idx and keys[idx + 1] or keys[1] |
|
end |
|
local function GetSortedList(list, key, ...) |
|
local keys = {...} |
|
local sort = {} |
|
for i=1,#keys do |
|
sort[i] = keys[i] == key and "*" or "" |
|
end |
|
list = list or {} |
|
local function CmpItemValues(a, b, k, ...) |
|
if not k then |
|
return |
|
end |
|
local va, vb = a[k], b[k] |
|
if va ~= vb then |
|
return va > vb |
|
end |
|
return CmpItemValues(a, b, ...) |
|
end |
|
table.sort(list, function(a, b) |
|
return CmpItemValues(a, b, key, table.unpack(keys)) |
|
end) |
|
return list, keys, sort |
|
end |
|
|
|
BiomeFiller.dbg_placed_objects_sort = "count" |
|
|
|
function BiomeFiller:ActionSortObjects() |
|
self.dbg_placed_objects_sort = ChangeSortKey(self.dbg_placed_objects_sort, "class", "count") |
|
self:DbgOnModified() |
|
end |
|
|
|
function BiomeFiller:GetPlacedObjects() |
|
local list, keys, sort = GetSortedList(self.dbg_placed_objects, self.dbg_placed_objects_sort, "class", "pct", "count") |
|
local tmp = { |
|
string.format("%35s | %6s | %s", table.unpack(sort)), |
|
string.format("%35s | %6s | %s", table.unpack(keys)), |
|
"-------------------------------------------------------------------------------------------", |
|
} |
|
for _, t in ipairs(list) do |
|
local pct = t.pct |
|
tmp[#tmp + 1] = string.format("%35s | %3d.%02d | %d", t.class, pct / pct_mul, pct % pct_mul, t.count) |
|
end |
|
return table.concat(tmp, "\n") |
|
end |
|
|
|
BiomeFiller.dbg_placed_prefabs_sort = "impact" |
|
function BiomeFiller:ActionSortPrefabs() |
|
self.dbg_placed_prefabs_sort = ChangeSortKey(self.dbg_placed_prefabs_sort, "name", "count", "objs", "grid", "impact") |
|
self:DbgOnModified() |
|
end |
|
function BiomeFiller:GetPlacedPrefabs() |
|
local list, keys, sort = GetSortedList(self.dbg_placed_prefabs, self.dbg_placed_prefabs_sort, "name", "count", "objs", "load", "place", "grid", "load", "place", "impact") |
|
local tmp = { |
|
string.format("%35s %5s %5s %5s %5s %5s %5s %5s %s", table.unpack(sort)), |
|
string.format("%35s %5s | %5s %5s %5s | %5s %5s %5s | %s", table.unpack(keys)), |
|
"-------------------------------------------------------------------------------------------", |
|
} |
|
for _, t in ipairs(list) do |
|
local prefab = t.prefab |
|
tmp[#tmp + 1] = string.format("%35s %5d | %5d %5d %5d | %5d %5d %5d | %d", |
|
t.name, t.count, |
|
t.objs, t.obj_load_time or 0, t.obj_place_time or 0, |
|
t.grid, t.grid_load_time or 0, t.grid_place_time or 0, |
|
t.impact) |
|
end |
|
return table.concat(tmp, "\n") |
|
end |
|
|
|
BiomeFiller.dbg_prefab_types_sort = "area" |
|
function BiomeFiller:ActionSortPrefabTypes() |
|
self.dbg_prefab_types_sort = ChangeSortKey(self.dbg_prefab_types_sort, "name", "area", "prefabs") |
|
self:DbgOnModified() |
|
end |
|
function BiomeFiller:GetPrefabTypes() |
|
local list, keys, sort = GetSortedList(self.dbg_prefab_types, self.dbg_prefab_types_sort, "name", "area", "prefabs") |
|
local tmp = { |
|
string.format("%35s %6s %s", table.unpack(sort)), |
|
string.format("%35s | %6s | %s", table.unpack(keys)), |
|
"-------------------------------------------------------------------------------------------", |
|
} |
|
for _, t in ipairs(list) do |
|
local prefab = t.prefab |
|
local area = t.area or 0 |
|
local fully_hidden = t.fully_hidden or 0 |
|
tmp[#tmp + 1] = string.format("%35s | %3d.%02d | %d", |
|
t.name, area / pct_mul, area % pct_mul, t.prefabs) |
|
end |
|
return table.concat(tmp, "\n") |
|
end |
|
|
|
BiomeFiller.dbg_visible_prefabs_sort = "visible_area" |
|
function BiomeFiller:ActionSortVisible() |
|
self.dbg_visible_prefabs_sort = ChangeSortKey(self.dbg_visible_prefabs_sort, "visible_area", "fully_hidden") |
|
self:DbgOnModified() |
|
end |
|
function BiomeFiller:GetVisiblePrefabs() |
|
local list, keys, sort = GetSortedList(self.dbg_visible_prefabs, self.dbg_visible_prefabs_sort, "name", "visible_area", "fully_hidden") |
|
local tmp = { |
|
string.format("%35s %12s %12s", table.unpack(sort)), |
|
string.format("%35s | %12s | %12s", table.unpack(keys)), |
|
"-------------------------------------------------------------------------------------------", |
|
} |
|
for _, t in ipairs(list) do |
|
local prefab = t.prefab |
|
local visible_area = t.visible_area or 0 |
|
local fully_hidden = t.fully_hidden or 0 |
|
tmp[#tmp + 1] = string.format("%35s | %9d.%02d | %9d.%02d", |
|
t.name, visible_area / pct_mul, visible_area % pct_mul, fully_hidden / pct_mul, fully_hidden % pct_mul) |
|
end |
|
return table.concat(tmp, "\n") |
|
end |
|
|
|
BiomeFiller.dbg_placed_poi_sort = "count" |
|
function BiomeFiller:ActionSortPoi() |
|
self.dbg_placed_poi_sort = ChangeSortKey(self.dbg_placed_poi_sort, "name", "count") |
|
self:DbgOnModified() |
|
end |
|
function BiomeFiller:GetPlacedPOI() |
|
local list, keys, sort = GetSortedList(self.dbg_placed_poi, self.dbg_placed_poi_sort, "name", "count") |
|
local tmp = { |
|
string.format("%35s %s", table.unpack(sort)), |
|
string.format("%35s | %s", table.unpack(keys)), |
|
"-------------------------------------------------------------------------------------------", |
|
} |
|
for _, t in ipairs(list) do |
|
local prefab = t.prefab |
|
tmp[#tmp + 1] = string.format("%35s | %d", t.name, t.count) |
|
end |
|
return table.concat(tmp, "\n") |
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function BiomeFiller:ViewInspectedPrefab() |
|
local pattern = self.InspectPattern |
|
if pattern == "" then |
|
print("No prefab name selected") |
|
return |
|
end |
|
local unpack = table.unpack |
|
local list = self.PrefabList or empty_table |
|
local last_mark = self.dbg_view_mark |
|
local mark, mark_pos, mark_radius, mark_name |
|
local count = #list |
|
local i = last_mark + 1 |
|
for n = 1, count do |
|
if i > count then i = 1 end |
|
local name, pos = unpack(list[i]) |
|
if string.find(name, pattern) then |
|
mark, mark_pos, mark_name = i, pos, name |
|
|
|
break |
|
end |
|
i = i + 1 |
|
end |
|
if not mark then |
|
print("No placed prefabs matching the pattern:", name) |
|
return |
|
end |
|
self.dbg_view_mark = mark |
|
local prefab = PrefabMarkers[mark_name] |
|
local prefab_radius = prefab and prefab.max_radius * type_tile |
|
ViewPos(mark_pos, 2 * prefab_radius) |
|
printf("Shown: %s [%d]", mark_name, mark) |
|
end |
|
|
|
function OnMsg.GedPropertyEdited(_, obj) |
|
if IsKindOf(obj, "NoisePreset") then |
|
ForEachPreset("Biome", function(biome) |
|
if biome.TypeMixingPreset == obj.id then |
|
ObjModified(biome) |
|
end |
|
end) |
|
end |
|
end |
|
|
|
function Biome:OnEditorSetProperty(prop_id, old_value, ged) |
|
if prop_id == "TypeMixingPreset" then |
|
self:CalcTypeMixingPreview() |
|
else |
|
local prop = self:GetPropertyMetadata(prop_id) |
|
if prop.recalc_curve then |
|
self["CalcCurve" .. prop.recalc_curve](self) |
|
end |
|
end |
|
end |
|
|
|
function OnMsg.ChangeMap() |
|
g_BiomeFiller = false |
|
end |
|
|