diff --git a/[editor]/edf/edf.lua b/[editor]/edf/edf.lua index ed7539179..23c6a29c7 100644 --- a/[editor]/edf/edf.lua +++ b/[editor]/edf/edf.lua @@ -1198,6 +1198,17 @@ function edfAddElementNodeData(node, resource) local validModels = xmlNodeGetAttribute(subnode, "validModels") dataFields[dname].validModels = validModels and split(validModels, ",") or dataFields[dname].validModels + --[[ Set to false to only save the value if it is not the default value, + useful to prevent map files from growing too much, + especially for properties that are not always required (default: true) + ]] + local persistDefault = xmlNodeGetAttribute(subnode, "persistDefault") + if persistDefault then + dataFields[dname].persistDefault = convert.boolean(persistDefault) + else + dataFields[dname].persistDefault = dataFields[dname].persistDefault or true + end + -- update the required flag (default: true) local requiredAttribute = xmlNodeGetAttribute(subnode,"required") if requiredAttribute then diff --git a/[editor]/edf/properties.lua b/[editor]/edf/properties.lua index 148b46457..7f4341502 100644 --- a/[editor]/edf/properties.lua +++ b/[editor]/edf/properties.lua @@ -37,6 +37,46 @@ propertyGetters = { else return "false" end + end, + moveSpeed = function(element) + local time = getElementData(element, "moveSpeed") + if time == nil or time == false then + return 1 + else + return time + end + end, + moveDelay = function(element) + local delay = getElementData(element, "moveDelay") + if delay == nil or delay == false then + return 0 + else + return delay + end + end, + moveX = function(element) + local offsetX = getElementData(element, "moveX") + if offsetX == nil or offsetX == false then + return 0 + else + return offsetX + end + end, + moveY = function(element) + local offsetY = getElementData(element, "moveY") + if offsetY == nil or offsetY == false then + return 0 + else + return offsetY + end + end, + moveZ = function(element) + local offsetZ = getElementData(element, "moveZ") + if offsetZ == nil or offsetZ == false then + return 0 + else + return offsetZ + end end }, ped = { diff --git a/[editor]/edf/properties_client.lua b/[editor]/edf/properties_client.lua index 9da717376..0de653d95 100644 --- a/[editor]/edf/properties_client.lua +++ b/[editor]/edf/properties_client.lua @@ -17,6 +17,46 @@ propertyGetters = { else return "false" end + end, + moveSpeed = function(element) + local time = getElementData(element, "moveSpeed") + if time == nil or time == false then + return 1 + else + return time + end + end, + moveDelay = function(element) + local delay = getElementData(element, "moveDelay") + if delay == nil or delay == false then + return 0 + else + return delay + end + end, + moveX = function(element) + local offsetX = getElementData(element, "moveX") + if offsetX == nil or offsetX == false then + return 0 + else + return offsetX + end + end, + moveY = function(element) + local offsetY = getElementData(element, "moveY") + if offsetY == nil or offsetY == false then + return 0 + else + return offsetY + end + end, + moveZ = function(element) + local offsetZ = getElementData(element, "moveZ") + if offsetZ == nil or offsetZ == false then + return 0 + else + return offsetZ + end end }, ped = { diff --git a/[editor]/editor_main/client/gridlines.lua b/[editor]/editor_main/client/gridlines.lua index 15590b892..fc8be095f 100644 --- a/[editor]/editor_main/client/gridlines.lua +++ b/[editor]/editor_main/client/gridlines.lua @@ -125,11 +125,135 @@ function drawXYZLines() drawLine({x,y,z},{zx,zy,zz},tocolor(0,0,200,200),thickness) end +--[[ + Draws the where the object will be moved to. +]] +function drawObjectMoveLines() + if not isElement(attachedToElement) then return end + if getElementType(attachedToElement) ~= "object" then return end + if getElementDimension(attachedToElement) ~= getElementDimension(localPlayer) then return end + local x,y,z = edf.edfGetElementPosition(attachedToElement) + if not x then return end + + local offsetX = edf.edfGetElementProperty(attachedToElement, "moveX") + local offsetY = edf.edfGetElementProperty(attachedToElement, "moveY") + local offsetZ = edf.edfGetElementProperty(attachedToElement, "moveZ") + + if offsetX and math.abs(offsetX) > 0 or offsetY and math.abs(offsetY) > 0 or offsetZ and math.abs(offsetZ) > 0 then + if not offsetX then offsetX = 0 end + if not offsetY then offsetY = 0 end + if not offsetZ then offsetZ = 0 end + + local speed = tonumber(edf.edfGetElementProperty(attachedToElement, "moveSpeed")) + if not speed then speed = 1 end + local delay = tonumber(edf.edfGetElementProperty(attachedToElement, "moveDelay")) + if not delay then delay = 0 end + local time = getDistanceBetweenPoints3D(x,y,z,x + offsetX,y + offsetY,z + offsetZ) / speed * 1000 + local lineStartPos = {x,y,z} + local lineEndPos = {x + offsetX,y + offsetY,z + offsetZ} + local timeNow = getTickCount() + + local progress = timeNow % (time + delay) / time + if progress > 1 then + progress = 1 + end + + local movementAnimationOffset = { + (lineEndPos[1] - lineStartPos[1]) * progress, + (lineEndPos[2] - lineStartPos[2]) * progress, + (lineEndPos[3] - lineStartPos[3]) * progress + } + + + local minX,minY,minZ,maxX,maxY,maxZ = edf.edfGetElementBoundingBox ( attachedToElement ) + if not minX then + local radius = edf.edfGetElementRadius ( attachedToElement ) + if radius then + minX,minY,minZ,maxX,maxY,maxZ = -radius,-radius,-radius,radius,radius,radius + end + end + + if minX and minY and minZ and maxX and maxY and maxZ then + -- Define the 8 corners in relative coordinates + local relativeCorners = { + {minX, minY, minZ}, -- 1: min corner + {maxX, minY, minZ}, -- 2 + {maxX, maxY, minZ}, -- 3 + {minX, maxY, minZ}, -- 4 + {minX, minY, maxZ}, -- 5 + {maxX, minY, maxZ}, -- 6 + {maxX, maxY, maxZ}, -- 7 + {minX, maxY, maxZ} -- 8: max corner + } + + -- Draw the bounding box moving to the destination position + do + local corners = {} + for i, relCorner in ipairs(relativeCorners) do + local worldX, worldY, worldZ = getPositionFromElementAtOffset(attachedToElement, relCorner[1], relCorner[2], relCorner[3]) + corners[i] = {worldX + movementAnimationOffset[1], worldY + movementAnimationOffset[2], worldZ + movementAnimationOffset[3]} + end + + local boxColor = tocolor(255, 255, 255, 100) + local lineWidth = 2 + + -- Bottom face (z = minZ) + dxDrawLine3D(corners[1][1], corners[1][2], corners[1][3], corners[2][1], corners[2][2], corners[2][3], boxColor, lineWidth) + dxDrawLine3D(corners[2][1], corners[2][2], corners[2][3], corners[3][1], corners[3][2], corners[3][3], boxColor, lineWidth) + dxDrawLine3D(corners[3][1], corners[3][2], corners[3][3], corners[4][1], corners[4][2], corners[4][3], boxColor, lineWidth) + dxDrawLine3D(corners[4][1], corners[4][2], corners[4][3], corners[1][1], corners[1][2], corners[1][3], boxColor, lineWidth) + + -- Top face (z = maxZ) + dxDrawLine3D(corners[5][1], corners[5][2], corners[5][3], corners[6][1], corners[6][2], corners[6][3], boxColor, lineWidth) + dxDrawLine3D(corners[6][1], corners[6][2], corners[6][3], corners[7][1], corners[7][2], corners[7][3], boxColor, lineWidth) + dxDrawLine3D(corners[7][1], corners[7][2], corners[7][3], corners[8][1], corners[8][2], corners[8][3], boxColor, lineWidth) + dxDrawLine3D(corners[8][1], corners[8][2], corners[8][3], corners[5][1], corners[5][2], corners[5][3], boxColor, lineWidth) + + -- Vertical edges connecting bottom to top + dxDrawLine3D(corners[1][1], corners[1][2], corners[1][3], corners[5][1], corners[5][2], corners[5][3], boxColor, lineWidth) + dxDrawLine3D(corners[2][1], corners[2][2], corners[2][3], corners[6][1], corners[6][2], corners[6][3], boxColor, lineWidth) + dxDrawLine3D(corners[3][1], corners[3][2], corners[3][3], corners[7][1], corners[7][2], corners[7][3], boxColor, lineWidth) + dxDrawLine3D(corners[4][1], corners[4][2], corners[4][3], corners[8][1], corners[8][2], corners[8][3], boxColor, lineWidth) + end + + -- Draw the bounding box at the destination position + do + local corners = {} + for i, relCorner in ipairs(relativeCorners) do + local worldX, worldY, worldZ = getPositionFromElementAtOffset(attachedToElement, relCorner[1], relCorner[2], relCorner[3]) + corners[i] = {worldX + offsetX, worldY + offsetY, worldZ + offsetZ} + end + + local boxColor = tocolor(255, 255, 0, 200) + local lineWidth = 2 + + -- Bottom face (z = minZ) + dxDrawLine3D(corners[1][1], corners[1][2], corners[1][3], corners[2][1], corners[2][2], corners[2][3], boxColor, lineWidth) + dxDrawLine3D(corners[2][1], corners[2][2], corners[2][3], corners[3][1], corners[3][2], corners[3][3], boxColor, lineWidth) + dxDrawLine3D(corners[3][1], corners[3][2], corners[3][3], corners[4][1], corners[4][2], corners[4][3], boxColor, lineWidth) + dxDrawLine3D(corners[4][1], corners[4][2], corners[4][3], corners[1][1], corners[1][2], corners[1][3], boxColor, lineWidth) + + -- Top face (z = maxZ) + dxDrawLine3D(corners[5][1], corners[5][2], corners[5][3], corners[6][1], corners[6][2], corners[6][3], boxColor, lineWidth) + dxDrawLine3D(corners[6][1], corners[6][2], corners[6][3], corners[7][1], corners[7][2], corners[7][3], boxColor, lineWidth) + dxDrawLine3D(corners[7][1], corners[7][2], corners[7][3], corners[8][1], corners[8][2], corners[8][3], boxColor, lineWidth) + dxDrawLine3D(corners[8][1], corners[8][2], corners[8][3], corners[5][1], corners[5][2], corners[5][3], boxColor, lineWidth) + + -- Vertical edges connecting bottom to top + dxDrawLine3D(corners[1][1], corners[1][2], corners[1][3], corners[5][1], corners[5][2], corners[5][3], boxColor, lineWidth) + dxDrawLine3D(corners[2][1], corners[2][2], corners[2][3], corners[6][1], corners[6][2], corners[6][3], boxColor, lineWidth) + dxDrawLine3D(corners[3][1], corners[3][2], corners[3][3], corners[7][1], corners[7][2], corners[7][3], boxColor, lineWidth) + dxDrawLine3D(corners[4][1], corners[4][2], corners[4][3], corners[8][1], corners[8][2], corners[8][3], boxColor, lineWidth) + end + end + end +end + function doBasicElementRenders() if not isElement(attachedToElement) then return end if exports["editor_gui"]:sx_getOptionData("enableBox") then renderGridlines() end if exports["editor_gui"]:sx_getOptionData("enableXYZlines") then drawXYZLines() end - + drawObjectMoveLines() end addEventHandler ( "onClientRender", root, doBasicElementRenders ) diff --git a/[editor]/editor_main/editor_main.edf b/[editor]/editor_main/editor_main.edf index cd3f20435..05e9a8ecf 100644 --- a/[editor]/editor_main/editor_main.edf +++ b/[editor]/editor_main/editor_main.edf @@ -11,6 +11,11 @@ + + + + + diff --git a/[editor]/editor_main/server/saveloadtest_server.lua b/[editor]/editor_main/server/saveloadtest_server.lua index 194850648..e9f566409 100644 --- a/[editor]/editor_main/server/saveloadtest_server.lua +++ b/[editor]/editor_main/server/saveloadtest_server.lua @@ -664,7 +664,8 @@ function createElementAttributesForSaving(xmlNode, element) -- Add an ID attribute first off xmlNodeSetAttribute(elementNode, "id", getElementID(element)) -- Dump raw properties from the getters - for dataField in pairs(loadedEDF[edf.edfGetCreatorResource(element)].elements[getElementType(element)].data) do + local dataFields = loadedEDF[edf.edfGetCreatorResource(element)].elements[getElementType(element)].data + for dataField, dataDefinition in pairs(dataFields) do if (dataField ~= "color1" and dataField ~= "color2" and dataField ~= "color3" and dataField ~= "color4") then local value if ( specialSyncers[dataField] ) then @@ -673,7 +674,9 @@ function createElementAttributesForSaving(xmlNode, element) value = edf.edfGetElementProperty(element, dataField) end if type(value) == "number" or type(value) == "string" then - xmlNodeSetAttribute(elementNode, dataField, value ) + if dataDefinition.persistDefault == true or dataDefinition.default ~= value then + xmlNodeSetAttribute(elementNode, dataField, value ) + end end end end @@ -706,7 +709,10 @@ function createElementAttributesForSaving(xmlNode, element) elseif ( dataName == "rotX" or dataName == "rotY" or dataName == "rotZ") then xmlNodeSetAttribute(elementNode, dataName, toAttribute(round(dataValue, 3))) elseif ( dataName ~= "color1" and dataName ~= "color2" and dataName ~= "color3" and dataName ~= "color4" and ( not specialSyncers[dataName] or dataValue ~= getWorkingDimension() ) ) then - xmlNodeSetAttribute(elementNode, dataName, toAttribute(dataValue)) + local dataDefinition = dataFields[dataName] + if not dataDefinition or dataDefinition.persistDefault == true or dataDefinition.default ~= dataValue then + xmlNodeSetAttribute(elementNode, dataName, toAttribute(dataValue)) + end end end -- Ensure that the element has a position set, else the map file can't load @@ -1026,9 +1032,8 @@ function onResourceStartOrStop(startedResource) if startEvent then local resourceName = getResourceName(startedResource) local useLODs = get(resourceName..".useLODs") - + local objectsTable = getElementsByType("object", source) if useLODs then - local objectsTable = getElementsByType("object", source) for objectID = 1, #objectsTable do local objectElement = objectsTable[objectID] @@ -1052,11 +1057,73 @@ function onResourceStartOrStop(startedResource) end end end + + for i = 1, #objectsTable do + local objectElement = objectsTable[i] + local x, y, z = getElementPosition(objectElement) + local offsetX = tonumber(getElementData(objectElement, "moveX")) + local offsetY = tonumber(getElementData(objectElement, "moveY")) + local offsetZ = tonumber(getElementData(objectElement, "moveZ")) + if (offsetX and math.abs(offsetX) > 0) or (offsetY and math.abs(offsetY) > 0) or (offsetZ and math.abs(offsetZ) > 0) then + if not offsetX then offsetX = 0 end + if not offsetY then offsetY = 0 end + if not offsetZ then offsetZ = 0 end + + local speed = tonumber(getElementData(objectElement, "moveSpeed")) or 1 + local delay = tonumber(getElementData(objectElement, "moveDelay")) or 0 + local time = getDistanceBetweenPoints3D(x,y,z,x + offsetX,y + offsetY,z + offsetZ) / speed * 1000 + + local currentPosX, currentPosY, currentPosZ = getElementPosition(objectElement) + local endPosX = currentPosX + offsetX + local endPosY = currentPosY + offsetY + local endPosZ = currentPosZ + offsetZ + local properties = { + moveTime = time, + delay = delay, + initialPosX = currentPosX, + initialPosY = currentPosY, + initialPosZ = currentPosZ, + endPosX = endPosX, + endPosY = endPosY, + endPosZ = endPosZ, + } + if delay > 0 then + setTimer(onObjectReachedInitialPosition, delay, 1, objectElement, properties) + else + onObjectReachedInitialPosition(objectElement, properties) + end + end + end end end addEventHandler("onResourceStart", resourceRoot, onResourceStartOrStop) addEventHandler("onResourceStop", resourceRoot, onResourceStartOrStop) +function onObjectReachedEndPosition(objectElement, properties) + if not isElement(objectElement) then return end + stopObject(objectElement) + local time = properties.moveTime + local delay = properties.delay + local initialPosX = properties.initialPosX + local initialPosY = properties.initialPosY + local initialPosZ = properties.initialPosZ + moveObject(objectElement, time, initialPosX, initialPosY, initialPosZ) + setTimer(onObjectReachedInitialPosition, time + delay, 1, objectElement, properties) +end + +function onObjectReachedInitialPosition(objectElement, properties) + if not isElement(objectElement) then return end + stopObject(objectElement) + local time = properties.moveTime + if not time then return end + local delay = properties.delay + local endPosX = properties.endPosX + local endPosY = properties.endPosY + local endPosZ = properties.endPosZ + moveObject(objectElement, time, endPosX, endPosY, endPosZ) + setTimer(onObjectReachedEndPosition, time + delay, 1, objectElement, properties) +end + local function onPlayerResourceStart(resourceElement) local mapResource = resourceElement == resource