146 lines
4.3 KiB
Lua
146 lines
4.3 KiB
Lua
------------------------------
|
|
-- 'BaseClass' private code --
|
|
------------------------------
|
|
|
|
---------------------------------------------------------------------------------------------------------
|
|
-- copy default implementation
|
|
|
|
-- privates, used by baseClass.copy :
|
|
local srcToCopyMapping = {} -- map each instance in the src tree to its shallow vopy
|
|
|
|
local function isInstance(inst)
|
|
return (type(inst) == "table" or type(inst) == "userdata") and inst.InstanceId ~= nil
|
|
end
|
|
|
|
local function isArray(inst)
|
|
return type(inst) == "table" and inst.InstanceId == nil
|
|
end
|
|
|
|
local function isInstanceArray(array)
|
|
return type(array) == "userdata" and array.Size ~= nil
|
|
end
|
|
|
|
|
|
--------------------------------------------------------------
|
|
-- create canonical copy
|
|
local function canonicalCopy(node)
|
|
if isInstance(node) then
|
|
assert(node.Class ~= nil)
|
|
local newNode = {}
|
|
for k, v in specPairs(node) do
|
|
-- special case for refId in class definition
|
|
--inspect(node:getClass())
|
|
local prop = r2.Classes[node.Class].NameToProp[k]
|
|
if prop and prop.Type == "RefId" then
|
|
newNode[k] = r2.RefId(v)
|
|
else
|
|
newNode[k] = canonicalCopy(v)
|
|
end
|
|
end
|
|
return newNode
|
|
elseif isInstanceArray(node) then
|
|
local newArray = {}
|
|
for k, v in specPairs(node) do
|
|
newArray[k] = canonicalCopy(v)
|
|
end
|
|
return newArray
|
|
else
|
|
return node
|
|
end
|
|
end
|
|
|
|
--------------------------------------------------------------
|
|
-- clone an object into clipboard, refid are unmodified
|
|
local function cloneNode(node, mapping)
|
|
if isInstance(node) then
|
|
assert(node.Class ~= nil)
|
|
local newNode = r2.newComponent(node.Class)
|
|
for k, v in pairs(node) do
|
|
-- special case for refId in class definition
|
|
--inspect(node:getClass())
|
|
local prop = r2.Classes[node.Class].NameToProp[k]
|
|
if prop and prop.Type == "RefId" then
|
|
newNode[k] = r2.RefId(v)
|
|
elseif k ~= "InstanceId" and k ~= "Class" then
|
|
newNode[k] = cloneNode(v, mapping)
|
|
end
|
|
end
|
|
mapping[node.InstanceId] = newNode.InstanceId
|
|
return newNode
|
|
elseif isArray(node) then
|
|
local newArray = {}
|
|
-- workaround here : if there are only numbers in the table keys, then sort and insert
|
|
-- this allow to preserve order when duplicating a road / place
|
|
local plainArray = true
|
|
for k, v in pairs(node) do
|
|
if type(k) ~= "number" then
|
|
plainArray = false
|
|
break
|
|
end
|
|
end
|
|
if plainArray then
|
|
local indices = {}
|
|
for k, v in pairs(node) do
|
|
table.insert(indices, k)
|
|
end
|
|
table.sort(indices)
|
|
for i = 1, table.getn(indices) do
|
|
table.insert(newArray, cloneNode(node[indices[i]], mapping))
|
|
end
|
|
else
|
|
for k, v in pairs(node) do
|
|
newArray[k] = cloneNode(v, mapping)
|
|
end
|
|
end
|
|
return newArray
|
|
else
|
|
return node
|
|
end
|
|
end
|
|
|
|
--------------------------------------------------------------
|
|
-- remap refid for internal refs, external refs are left unmodified
|
|
local function renameRefIds(node, mapping)
|
|
if isInstance(node) then
|
|
assert(node.Class ~= nil)
|
|
for k, v in pairs(node) do
|
|
-- special case for refId in class definition
|
|
local prop = r2.Classes[node.Class].NameToProp[k]
|
|
if prop and prop.Type == "RefId" then
|
|
--debugInfo("***" .. tostring(v))
|
|
if mapping[tostring(v)] ~= nil then
|
|
--debugInfo("###")
|
|
node[k] = mapping[tostring(v)] -- remap internal ref
|
|
end
|
|
else
|
|
renameRefIds(v, mapping)
|
|
end
|
|
end
|
|
mapping[node] = newNode
|
|
elseif type(node) == "table" then
|
|
for k, v in specPairs(node) do
|
|
renameRefIds(v, mapping)
|
|
end
|
|
return newArray
|
|
end
|
|
end
|
|
|
|
function baseClass.copy(this)
|
|
return canonicalCopy(this)
|
|
end
|
|
|
|
function baseClass.newCopy(this)
|
|
table.clear(srcToCopyMapping)
|
|
local result = cloneNode(this, srcToCopyMapping)
|
|
renameRefIds(result, srcToCopyMapping)
|
|
-- if the name is a generated one, create a new name
|
|
if result.Name ~= nil and r2:isPostFixedByNumber(ucstring(result.Name)) then
|
|
result.Name = r2:genInstanceName(ucstring(result.Name)):toUtf8()
|
|
end
|
|
|
|
-- the copy of a undeletable object is a deltable object
|
|
if result.Deletable ~= nil and result.Deletable==0 then
|
|
result.Deletable = 1
|
|
end
|
|
return result
|
|
end
|