File size: 9,063 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 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
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
|