myspace / CommonLua /MapGen /PlacePrefabMarker.lua
sirnii's picture
Upload 1816 files
b6a38d7 verified
raw
history blame
9.06 kB
local function GetPrefabItems(self)
local items = {}
local PrefabMarkers = PrefabMarkers
for _, prefab in ipairs(self:FilterPrefabs()) do
items[#items + 1] = PrefabMarkers[prefab]
end
table.sort(items)
table.insert(items, 1, "")
return items
end
DefineClass.PlacePrefabLogic = {
__parents = { "PropertyObject" },
properties = {
{ category = "Prefab", id = "FixedPrefab", name = "Fixed Prefabs", editor = "string_list", default = false, items = GetPrefabItems, no_validate = true, buttons = {{name = "Test", func = "TestPlacePrefab"}}},
{ category = "Prefab", id = "PrefabPOIType", name = "Prefab POI Type", editor = "preset_id", default = "", preset_class = "PrefabPOI" },
{ category = "Prefab", id = "PrefabType", name = "Prefab Type", editor = "preset_id", default = "", preset_class = "PrefabType" },
{ category = "Prefab", id = "PrefabTagsAny", name = "Prefab Tags Any", editor = "set", default = empty_table, items = function() return PrefabTagsCombo() end, three_state = true },
{ category = "Prefab", id = "PrefabTagsAll", name = "Prefab Tags All", editor = "set", default = empty_table, items = function() return PrefabTagsCombo() end, three_state = true },
{ category = "Prefab", id = "MaxPrefabRadius", name = "Max Allowed Radius", editor = "number", default = 0, scale = "m" },
{ category = "Prefab", id = "FixAtCenter", name = "Fix At Center", editor = "bool", default = true, help = "Allow the prefab to be spawned anywhere inside the max radius" },
{ category = "Prefab", id = "RandAngle", name = "Rand Angle", editor = "number", default = 0, scale = "deg" },
{ category = "Prefab", id = "PlacedName", name = "Placed Name", editor = "text", default = "", read_only = true, dont_save = true, buttons = {{name = "Goto", func = "GotoPrefabAction"}} },
{ category = "Prefab", id = "PlaceError", name = "Place Error", editor = "text", default = "", read_only = true, dont_save = true },
{ category = "Prefab", id = "PrefabCount", name = "Prefab Count", editor = "number", default = 0, read_only = true, dont_save = true },
},
reserved_locations = false,
}
function PlacePrefabLogic:SetFixedPrefab(prefab)
if type(prefab) == "string" then
prefab = { prefab }
end
self.FixedPrefab = prefab
end
function PlacePrefabLogic:FilterPrefabs(params, all_prefabs)
all_prefabs = all_prefabs or PrefabMarkers
local prefabs = {}
local poi_type = params and params.poi_type or self.PrefabPOIType or ""
local ptype = params and params.prefab_type or self.PrefabType or ""
local max_radius = params and params.max_radius or self.MaxPrefabRadius or 0
local tags_any = params and params.tags_any or self.PrefabTagsAny
local tags_all = params and params.tags_all or self.PrefabTagsAll
local type_tile = const.TypeTileSize
for _, prefab in ipairs(all_prefabs) do
if (poi_type == "" or prefab.poi_type == poi_type)
and (ptype == "" or prefab.type == "" or prefab.type == ptype)
and (max_radius == 0 or prefab.max_radius * type_tile <= max_radius)
and MatchThreeStateSet(prefab.tags, tags_any, tags_all)
then
prefabs[#prefabs + 1] = prefab
end
end
return prefabs
end
function PlacePrefabLogic:GetPrefabs(params)
local fixed_prefabs = params and params.name or self.FixedPrefab
if fixed_prefabs then
local prefabs = {}
if type(fixed_prefabs) == "string" then
fixed_prefabs = { fixed_prefabs }
end
for _, name in ipairs(fixed_prefabs) do
local prefab = PrefabMarkers[name]
if not prefab then
StoreErrorSource(self, "No such prefab:", name)
else
prefabs[#prefabs + 1] = prefab
end
end
if params and params.filter_fixed then
local tags_any = params and params.tags_any or self.PrefabTagsAny
local tags_all = params and params.tags_all or self.PrefabTagsAll
for i = #prefabs,1,-1 do
if not MatchThreeStateSet(prefabs[i].tags, tags_any, tags_all) then
table.remove_rotate(prefabs, i)
end
end
end
if #prefabs > 0 then
return prefabs
end
end
return self:FilterPrefabs(params)
end
function PlacePrefabLogic:GetError()
if mapdata.IsPrefabMap and self:GetPrefabCount() == 0 then
return "No matching prefabs found!"
end
end
function PlacePrefabLogic:GetPrefabCount(params)
return #self:GetPrefabs(params)
end
function PlacePrefabLogic:ReserveLocation(pos, radius)
self.reserved_locations = table.create_add(self.reserved_locations, {pos, radius})
end
function PlacePrefabLogic:GetReservedRatio()
local max_radius = self.MaxPrefabRadius
if max_radius <= 0 then
return 100
end
local radius_sum2 = 0
for _, info in ipairs(self.reserved_locations) do
local radius = info[2]
radius_sum2 = radius * radius
end
return 100 * sqrt(radius_sum2) / max_radius
end
function PlacePrefabLogic:CheckReservedLocations(pos, radius)
--DbgClear(true) DbgAddCircle(self, self.MaxPrefabRadius, yellow) DbgAddCircle(pos, radius, blue)
for _, info in ipairs(self.reserved_locations) do
if IsCloser2D(pos, info[1], radius + info[2]) then
--DbgAddCircle(info[1], info[2], red)
return
end
end
--DbgAddVector(pos)
return true
end
function PlacePrefabLogic:GetPrefabLoc(seed, params)
seed = seed or InteractionRand(nil, "PlacePrefab")
local name, pos, angle, prefab, idx
local prefabs = self:GetPrefabs(params)
local retry
while true do
local idx
if #prefabs > 1 then
prefab, idx, seed = table.weighted_rand(prefabs, "weight", seed)
else
prefab = prefabs[1]
end
assert(prefab)
if not prefab then
return
end
pos = params and params.pos
if not pos then
pos = self:GetVisualPos()
if not self.FixAtCenter then
local reserved_radius
if params and params.avoid_reserved_locations and self.reserved_locations then
reserved_radius = (prefab.min_radius + prefab.max_radius) * const.TypeTileSize / 2
end
local radius = prefab.max_radius * const.TypeTileSize
local free_dist = self.MaxPrefabRadius - radius
if free_dist > 0 then
local center = pos
pos = false
local retries = params and params.avoid_reserved_retries or 16
for i=1,retries do
local ra, rr
ra, seed = BraidRandom(seed, 360*60)
rr, seed = BraidRandom(seed, free_dist)
local pos_i = RotateRadius(rr, ra, center)
if not reserved_radius or self:CheckReservedLocations(pos_i, reserved_radius) then
pos = pos_i
break
end
end
elseif reserved_radius and not self:CheckReservedLocations(pos, reserved_radius) then
pos = false
end
end
end
if pos then
name = PrefabMarkers[prefab]
angle = params and params.angle
if not angle then
angle = self:GetAngle()
local rand_angle = self.RandAngle
if rand_angle > 0 then
local desired_angle = params and params.desired_angle
if desired_angle then
local angle_diff = AngleDiff(desired_angle, angle)
if abs(angle_diff) <= rand_angle then
angle = desired_angle
else
local min_angle, max_angle = angle - rand_angle, angle + rand_angle
if abs(AngleDiff(desired_angle, min_angle)) < abs(AngleDiff(desired_angle, max_angle)) then
angle = min_angle
else
angle = max_angle
end
end
else
local da
da, seed = BraidRandom(seed, -rand_angle, rand_angle)
angle = angle + da
end
end
end
return name, pos, angle, prefab, seed
end
if #prefabs == 1 then
return
end
table.remove_rotate(prefabs, idx)
end
end
function PlacePrefabLogic:PlacePrefab(seed, params)
local success, err, objs, inv_bbox
local name, pos, angle, prefab, seed = self:GetPrefabLoc(seed, params)
if not name then
err = "No matching prefabs found!"
else
success, err, objs, inv_bbox = procall(PlacePrefab, name, pos, angle, seed, params)
end
self.PlaceError = err
self.PlacedName = name
ObjModified(self)
return err, objs, pos, prefab, name, inv_bbox
end
function PlacePrefabLogic:EditorCallbackGenerate(generator, object_source, placed_objects, prefab_list)
local mark = placed_objects[self]
local info = mark and prefab_list[mark]
local ptype = info and info[4]
if ptype then
self.PrefabType = ptype
end
end
----
DefineClass.PlacePrefabMarker = {
__parents = { "RadiusMarker", "PlacePrefabLogic", "PrefabSourceInfo" },
editor_text_color = RGB(50, 50, 100),
editor_color = RGB(150, 150, 0),
}
function PlacePrefabMarker:GetMeshRadius()
local max_radius = self.MaxPrefabRadius
for _, prefab in ipairs(self:GetPrefabs()) do
max_radius = Max(max_radius, prefab.max_radius)
end
return max_radius
end
function PlacePrefabMarker:OnEditorSetProperty(prop_id, old_value, ged)
local meta = self:GetPropertyMetadata(prop_id)
if meta and meta.category == "Prefab" then
self:UpdateMeshRadius()
end
end
function PlacePrefabMarker:TestPlacePrefab()
local err, objs = self:PlacePrefab(AsyncRand(), {
create_undo = true,
})
if err then
print(err)
end
end