LUADOC - Farming Simulator 22

Script v1_7_1_0

Engine v1_7_1_0

Foundation Reference

PlaceableFence

Description
Specialization for placeables
Functions

addPickingNodesForSegment

Description
Definition
addPickingNodesForSegment()
Code
1341function PlaceableFence:addPickingNodesForSegment(segment)
1342 if segment == self.spec_fence.previewSegment then
1343 return
1344 end
1345
1346 if segment.group ~= nil then
1347 local objects = {}
1348 self:recursivelyAddPickingNodes(objects, segment.group)
1349
1350 for i = 1, #objects do
1351 g_currentMission:addNodeObject(objects[i], self)
1352 end
1353 end
1354
1355 -- Reset
1356 self.overlayColorNodes = nil
1357end

addSegment

Description
Definition
addSegment()
Code
584function PlaceableFence:addSegment(segment, sync)
585 local spec = self.spec_fence
586 spec.segments[#spec.segments + 1] = segment
587
588 self:generateSegmentPoles(segment, sync)
589end

addSegmentShapesToUpdate

Description
Definition
addSegmentShapesToUpdate()
Code
1316function PlaceableFence:addSegmentShapesToUpdate(segment)
1317 local spec = self.spec_fence
1318 spec.segmentsToUpdate[#spec.segmentsToUpdate + 1] = segment
1319
1320 self:raiseActive()
1321end

collectPickObjects

Description
Definition
collectPickObjects()
Code
1409function PlaceableFence:collectPickObjects(superFunc, node)
1410 ---Default picking objects is disabled
1411end

createSegment

Description
Definition
createSegment()
Code
569function PlaceableFence:createSegment(x1, z1, x2, z2, renderFirst, gateIndex)
570 return {
571 x1 = x1,
572 z1 = z1,
573 x2 = x2,
574 z2 = z2,
575 renderFirst = renderFirst, -- whether to render the first pole
576 renderLast = true,
577 gateIndex = gateIndex,
578 poles = {}, -- list of pole positions
579 }
580end

deletePanel

Description
Called by server or client directly on click
Definition
deletePanel()
Code
730function PlaceableFence:deletePanel(node)
731 if node == nil or node == 0 or getCollisionMask(node) == 0 then
732 return
733 end
734
735 local spec = self.spec_fence
736 local panel, _, segment, _, poleIndex = self:findRaycastInfo(node)
737 if panel == nil then
738 return nil
739 end
740
741 local segmentIndex = 1
742 for i = 1, #spec.segments do
743 if spec.segments[i] == segment then
744 segmentIndex = i
745 break
746 end
747 end
748
749 if self.isServer then
750 self:doDeletePanel(segment, segmentIndex, poleIndex)
751
752 g_server:broadcastEvent(PlaceableFenceRemoveSegmentEvent.new(self, segmentIndex, poleIndex), false)
753 else
754 -- Set collision mask so we cannot hit it again while it is in transport
755 setCollisionMask(node, 0)
756
757 -- Request change on server
758 g_client:getServerConnection():sendEvent(PlaceableFenceRemoveSegmentEvent.new(self, segmentIndex, poleIndex))
759 end
760
761 return true
762end

deleteSegment

Description
Delete a segment with its nodes.
Definition
deleteSegment()
Code
593function PlaceableFence:deleteSegment(segment)
594 local spec = self.spec_fence
595 if segment.animatedObject ~= nil then
596 segment.animatedObject:delete()
597 segment.animatedObject = nil
598 end
599
600 if segment.group ~= nil then
601 delete(segment.group)
602 segment.group = nil
603 end
604
605 table.removeElement(spec.segments, segment)
606
607 self:updateDirtyAreas(segment)
608end

doDeletePanel

Description
Definition
doDeletePanel()
Code
766function PlaceableFence:doDeletePanel(segment, segmentIndex, poleIndex)
767 if segment == nil or poleIndex > #segment.poles then
768 return
769 end
770
771 -- We mark any poles we delete as deleted when they might cause a
772 -- non-rendered pole in another segment.
773 local deletedPoles = {}
774
775 local originalSegment = {x1 = segment.x1, x2 = segment.x2, z1 = segment.x1, z2 = segment.z1}
776
777 local segmentSizeChanged = false
778
779 -- Start of segment: delete the pole by moving segment start to next pole
780 if poleIndex == 1 then
781 if segment.renderFirst then
782 deletedPoles[#deletedPoles + 1] = segment.poles[1]
783 deletedPoles[#deletedPoles + 1] = segment.poles[2]
784 end
785
786 -- If there is only 1 more pole, delete
787 if poleIndex + 2 == #segment.poles - 1 then
788 if segment.renderLast then
789 deletedPoles[#deletedPoles + 1] = segment.poles[3]
790 deletedPoles[#deletedPoles + 1] = segment.poles[4]
791 end
792
793 self:removePickingNodesForSegment(segment)
794 self:deleteSegment(segment)
795 else
796 segment.x1 = segment.poles[3]
797 segment.z1 = segment.poles[4]
798
799 segmentSizeChanged = true
800 segment.renderFirst = true
801 end
802
803 -- Next pole is last in line (this is the last panel)
804 elseif poleIndex + 2 == #segment.poles - 1 then
805 -- Shorten the segment
806
807 if segment.renderLast then
808 deletedPoles[#deletedPoles + 1] = segment.poles[#segment.poles - 1]
809 deletedPoles[#deletedPoles + 1] = segment.poles[#segment.poles]
810 end
811
812 segment.x2 = segment.poles[#segment.poles - 3]
813 segment.z2 = segment.poles[#segment.poles - 2]
814
815 segment.renderLast = true
816
817 segmentSizeChanged = true
818 else
819 -- Create second part
820 local newSegment = self:createSegment(segment.poles[poleIndex + 2], segment.poles[poleIndex + 3], segment.x2, segment.z2, true, nil)
821 newSegment.renderLast = segment.renderLast -- copy
822 newSegment.renderFirst = true -- we moved the start to an existing pole, so enable this pole
823
824 -- Adjust first part
825 segment.x2 = segment.poles[poleIndex]
826 segment.z2 = segment.poles[poleIndex + 1]
827 segment.renderLast = true
828
829 self:addSegment(newSegment)
830 segmentSizeChanged = true
831
832 -- Note: no poles were removed as only the panel visually is gone.
833 end
834
835 -- Do not update gates! They cannot resize anyway
836 if segmentSizeChanged then
837 self:generateSegmentPoles(segment, true)
838 end
839
840 -- For any pole that has changed, find if it matches the start or end of another segment
841 -- If it does mark that segment as needing to render the pole again. Only the first segment we find though
842 for i = 1, #deletedPoles, 2 do
843 local x, z = deletedPoles[i], deletedPoles[i + 1]
844 local neighborSegment, isStart = self:isPoleInAnySegment(x, z, segment)
845
846 if neighborSegment ~= nil then
847 if isStart then
848 neighborSegment.renderFirst = true
849 else
850 neighborSegment.renderLast = true
851 end
852
853 self:generateSegmentPoles(neighborSegment, true)
854 end
855 end
856
857 -- We need to update areas where panels are not anymore
858 self:updateDirtyAreas(originalSegment)
859
860 return true
861end

fakeRandomValueForPosition

Description
Generate a value [0,1] for a position that is always consistent but highly affected by the position
Definition
fakeRandomValueForPosition()
Code
1009function PlaceableFence:fakeRandomValueForPosition(x, y, z, n)
1010 local alpha = (x * 0.13 + z * 0.23) % 1
1011
1012 if n == nil then
1013 return alpha
1014 end
1015
1016 -- Integer from 1 to n (inclusive)
1017 return math.floor(alpha * (n - 1) + 0.5) + 1
1018end

findRaycastInfo

Description
Definition
findRaycastInfo()
Code
865function PlaceableFence:findRaycastInfo(node)
866 local spec = self.spec_fence
867 -- Raycasts hit collisions. Find the visual
868 -- This collision could be a pole or a panel! If it is the pole, just return the pole info
869
870 local collision = node
871 local panel = getParent(collision)
872
873 local panelVisuals = getChildAt(panel, 1)
874
875 -- Find which segment this is. We'll also find if the node was actually a panel
876 local segment = nil
877
878 -- Each panel has as parent the pole
879 local pole = getParent(panel)
880
881 -- Each pole has as parent the segment group
882 local sGroup = getParent(pole)
883 for si = 1, #spec.segments do
884 local seg = spec.segments[si]
885 if seg.group == sGroup then
886 segment = seg
887 break
888
889 -- Special case are gates because of doors or pole selection
890 elseif seg.group == pole and seg.gateIndex ~= nil then
891 segment = seg
892
893 -- The pole is actually the group
894 sGroup = pole
895
896 -- Thus update all references
897 pole = panel
898 -- First nodes are poles, last one is panel.
899 panel = getChildAt(sGroup, getNumOfChildren(sGroup) - 1)
900 panelVisuals = getChildAt(panel, 1) -- first is col, second is visuals
901
902 break
903 end
904 end
905
906 -- No segment then the requested node was not a panel
907 if segment == nil then
908 -- Try to find information assuming the node is a pole instead
909 collision = node
910 --panel = getChildAt(collision, 0)
911 pole = getParent(collision)
912
913 sGroup = getParent(pole)
914 for si = 1, #spec.segments do
915 local seg = spec.segments[si]
916 if seg.group == sGroup then
917 segment = seg
918 break
919 end
920 end
921
922 if segment == nil then
923 return nil
924 end
925
926 local poleIndex = getChildIndex(pole) * 2 + 1
927 return nil, nil, segment, pole, poleIndex
928 end
929
930 local poleIndex
931 if segment.gateIndex ~= nil then
932 poleIndex = 1
933 else
934 poleIndex = getChildIndex(pole) * 2 + 1
935 end
936
937 return panel, panelVisuals, segment, pole, poleIndex
938end

generateSegmentPoles

Description
Generate all poles for the segment In this setup, we add as many as possible whole-size elements. Then for the last, we make the fences equally sized.
Definition
generateSegmentPoles()
Code
1024function PlaceableFence:generateSegmentPoles(segment, sync)
1025 local spec = self.spec_fence
1026 local totalDistance = MathUtil.getPointPointDistance(segment.x1, segment.z1, segment.x2, segment.z2)
1027 local numWholeFences = math.max(math.floor(totalDistance / spec.panelLength) - 1, 0)
1028
1029 for i = 1, #segment.poles do
1030 segment.poles[i] = nil
1031 end
1032
1033 if totalDistance < 0.01 then
1034 return
1035 end
1036
1037 -- For gates we only show first and last pole
1038 if segment.gateIndex ~= nil then
1039 segment.poles[1] = segment.x1
1040 segment.poles[2] = segment.z1
1041 segment.poles[3] = segment.x2
1042 segment.poles[4] = segment.z2
1043 else
1044 local nextPole = 1
1045 for j = 0, numWholeFences do
1046 local alpha = (spec.panelLength * j) / totalDistance
1047
1048 segment.poles[nextPole] = MathUtil.lerp(segment.x1, segment.x2, alpha)
1049 segment.poles[nextPole + 1] = MathUtil.lerp(segment.z1, segment.z2, alpha)
1050 nextPole = nextPole + 2
1051 end
1052
1053 -- Final 2 posts
1054 local restDistance = totalDistance - (numWholeFences * spec.panelLength)
1055 local numRestFences = restDistance <= spec.panelLength * 1.2 and 1 or 2
1056 local restFenceSize = restDistance / numRestFences
1057
1058 for j = 0, numRestFences - 1 do
1059 local alpha = ((numWholeFences * spec.panelLength) + (j + 1) * restFenceSize) / totalDistance
1060
1061 segment.poles[nextPole] = MathUtil.lerp(segment.x1, segment.x2, alpha)
1062 segment.poles[nextPole + 1] = MathUtil.lerp(segment.z1, segment.z2, alpha)
1063 nextPole = nextPole + 2
1064 end
1065 end
1066
1067 if sync then
1068 self:removePickingNodesForSegment(segment)
1069 self:updateSegmentShapes(segment)
1070 self:addPickingNodesForSegment(segment)
1071 else
1072 self:addSegmentShapesToUpdate(segment)
1073 end
1074
1075 -- Segment was updated, tell dependent systems
1076 if spec.previewSegment ~= segment then
1077 self:updateDirtyAreas(segment)
1078 end
1079end

getAllowExtendingOnly

Description
Definition
getAllowExtendingOnly()
Code
706function PlaceableFence:getAllowExtendingOnly()
707 return self.spec_fence.allowExtendingOnly
708end

getBoundingCheckWidth

Description
Definition
getBoundingCheckWidth()
Code
684function PlaceableFence:getBoundingCheckWidth()
685 return self.spec_fence.boundingCheckWidth
686end

getDestructionMethod

Description
Deletion is in pieces so not instantly
Definition
getDestructionMethod()
Code
1391function PlaceableFence:getDestructionMethod(superFunc)
1392 return Placeable.DESTRUCTION.PER_NODE
1393end

getGate

Description
Definition
getGate()
Code
649function PlaceableFence:getGate(index)
650 local spec = self.spec_fence
651 return spec.gates[index]
652end

getHasParallelSnapping

Description
Definition
getHasParallelSnapping()
Code
718function PlaceableFence:getHasParallelSnapping()
719 return false
720end

getIsPanelLengthFixed

Description
Definition
getIsPanelLengthFixed()
Code
550function PlaceableFence:getIsPanelLengthFixed()
551 return self.spec_fence.panelLengthFixed
552end

getMaxCornerAngle

Description
Definition
getMaxCornerAngle()
Code
712function PlaceableFence:getMaxCornerAngle()
713 return self.spec_fence.maxCornerAngle
714end

getMaxVerticalAngle

Description
Definition
getMaxVerticalAngle()
Code
670function PlaceableFence:getMaxVerticalAngle()
671 local spec = self.spec_fence
672 return spec.maxVerticalAngle
673end

getMaxVerticalAngleAndYForPreview

Description
Get the difference in height and the angle
Definition
getMaxVerticalAngleAndYForPreview()
Code
504function PlaceableFence:getMaxVerticalAngleAndYForPreview()
505 local spec = self.spec_fence
506 local segment = spec.previewSegment
507 local maxAngle = 0
508 local minY, maxY = 1000, -1000
509
510 for i = 1, #segment.poles - 2, 2 do
511 local x1 = segment.poles[i]
512 local z1 = segment.poles[i+1]
513 local x2 = segment.poles[i+2]
514 local z2 = segment.poles[i+3]
515
516 local horizontalDifference = MathUtil.getPointPointDistance(x1, z1, x2, z2)
517
518 local y1 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x1, 0, z1)
519 local y2 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x2, 0, z2)
520 local heightDifference = math.abs(y1 - y2)
521
522 minY = math.min(minY, y1, y2)
523 maxY = math.max(maxY, y1, y2)
524
525 if horizontalDifference > 0 then
526 local angle = math.atan(heightDifference / horizontalDifference)
527 if angle > maxAngle then
528 maxAngle = angle
529 end
530 end
531 end
532
533 return maxAngle, minY, maxY
534end

getMaxVerticalGateAngle

Description
Definition
getMaxVerticalGateAngle()
Code
677function PlaceableFence:getMaxVerticalGateAngle()
678 local spec = self.spec_fence
679 return spec.maxVerticalGateAngle
680end

getNodesToDeleteForPanel

Description
Get the nodes that would be deleted if the given panel node would be Used for previewing
Definition
getNodesToDeleteForPanel()
Code
943function PlaceableFence:getNodesToDeleteForPanel(node)
944 local spec = self.spec_fence
945 local panel, panelVisuals, segment, pole, poleIndex = self:findRaycastInfo(node)
946 if panel == nil or node == 0 then
947 return nil
948 end
949
950 -- Collect nodes
951 local nodes = {
952 }
953
954 if segment.gateIndex ~= nil then
955 -- Add all doors
956 local gateInfo = spec.gates[segment.gateIndex]
957 for _, door in ipairs(gateInfo.doors) do
958 local doorNode = getChildAt(panel, door.node)
959 nodes[#nodes + 1] = getChildAt(doorNode, 0) -- visual
960 end
961 else
962 -- Add the panel
963 nodes[1] = panelVisuals
964 end
965
966 local function addPole(poleNode, x, z)
967 if self:isPoleInAnySegment(x, z, segment) == nil then
968 local visualPole = getChildAt(poleNode, 1)
969 if visualPole ~= 0 then
970 table.insert(nodes, visualPole)
971 end
972 end
973 end
974
975 -- If the pole of the panel is the first pole it would become lonely and should be removed
976 if poleIndex == 1 and segment.renderFirst then
977 addPole(pole, segment.poles[1], segment.poles[2])
978 end
979
980 -- If the pole after the panel-pole is the last pole, also delete it
981 if poleIndex + 2 == #segment.poles - 1 and segment.renderLast then
982 addPole(getChildAt(segment.group, #segment.poles / 2 - 1), segment.poles[#segment.poles - 1], segment.poles[#segment.poles])
983 end
984
985 return nodes
986end

getNumSequments

Description
Definition
getNumSequments()
Code
663function PlaceableFence:getNumSequments()
664 local spec = self.spec_fence
665 return #spec.segments
666end

getPanelLength

Description
Definition
getPanelLength()
Code
544function PlaceableFence:getPanelLength()
545 return self.spec_fence.panelLength
546end

getPoleNear

Description
Get a pole that is near given position or nil if none.
Definition
getPoleNear()
Return Values
x
z
distance
Code
399function PlaceableFence:getPoleNear(x, y, z, maxDistance)
400 local spec = self.spec_fence
401 spec.getPoleNearResult = nil
402 spec.getPoleNearResultSegment = nil
403 spec.getPoleNearResultDistance = math.huge
404 spec.getPoleNearResultPosition = {x, y, z}
405
406 overlapSphere(x, y, z, maxDistance, "getPoleNearOverlapCallback", self, CollisionFlag.STATIC_OBJECTS, false, true, true, false)
407
408 if spec.getPoleNearResult ~= nil then
409 local pole_x, pole_y, pole_z = getWorldTranslation(spec.getPoleNearResult)
410 return pole_x, pole_y, pole_z, spec.getPoleNearResult, spec.getPoleNearResultSegment
411 end
412
413 return nil
414end

getPoleNearOverlapCallback

Description
Definition
getPoleNearOverlapCallback()
Code
418function PlaceableFence:getPoleNearOverlapCallback(hitObjectId)
419 if hitObjectId == 0 or hitObjectId == g_currentMission.terrainRootNode then
420 return
421 end
422
423 local sGroup = getParent(getParent(hitObjectId))
424 local spec = self.spec_fence
425 local x, y, z = getWorldTranslation(hitObjectId)
426 local distance = MathUtil.vector3Length(x-spec.getPoleNearResultPosition[1], y-spec.getPoleNearResultPosition[2], z-spec.getPoleNearResultPosition[3])
427 if distance < spec.getPoleNearResultDistance then
428 for _, segment in ipairs(spec.segments) do
429 if segment.group == sGroup then
430 -- A gate has at least 3 parts: trigger, 1 gate, and gate-visuals (hinges)
431 -- We need to ignore gates so we attach to poles only
432 if getNumOfChildren(hitObjectId) < 3 then
433 spec.getPoleNearResult = hitObjectId
434 spec.getPoleNearResultSegment = segment
435 spec.getPoleNearResultDistance = distance
436 end
437 end
438 end
439 end
440
441 return true
442end

getPolePosition

Description
Get whether the node is a fence pole and if so, get its position
Definition
getPolePosition()
Code
446function PlaceableFence:getPolePosition(node, allowPanel)
447 local spec = self.spec_fence
448 local collision = node
449 local item = getParent(collision)
450
451 -- Parent of item can be a segment (for poles), or a pole (for panels)
452 local parent = getParent(item)
453
454 -- Panels are children of poles
455 local parent2
456 if allowPanel and parent ~= getRootNode() then
457 parent2 = getParent(parent)
458 end
459
460 for i = 1, #spec.segments do
461 local segment = spec.segments[i]
462
463 -- If group matches, the node is part of this fence
464 if parent == segment.group then
465 -- pole
466 local x, y, z = getWorldTranslation(item)
467 return x, y, z, segment
468 elseif parent2 == segment.group then
469 -- panel
470 local x, y, z = getWorldTranslation(parent) -- the pole
471 return x, y, z, segment
472 end
473 end
474
475 return nil
476end

getPoleShapeForPreview

Description
Get a pole shape for previewing. Caller must delete
Definition
getPoleShapeForPreview()
Code
480function PlaceableFence:getPoleShapeForPreview()
481 local spec = self.spec_fence
482 if spec.hasInvisiblePoles then
483 return nil
484 end
485
486 if #spec.poles > 0 then
487 if getNumOfChildren(spec.poles[1]) == 0 then
488 return nil
489 end
490
491 local pole = clone(spec.poles[1], false, false, false)
492 if pole == 0 then
493 return nil
494 end
495
496 return pole
497 else
498 return nil
499 end
500end

getPreviewSegment

Description
Definition
getPreviewSegment()
Code
642function PlaceableFence:getPreviewSegment()
643 local spec = self.spec_fence
644 return spec.previewSegment
645end

getSegment

Description
Definition
getSegment()
Code
656function PlaceableFence:getSegment(index)
657 local spec = self.spec_fence
658 return spec.segments[index]
659end

getSegmentLength

Description
Get the length of a segment
Definition
getSegmentLength()
Code
538function PlaceableFence:getSegmentLength(segment)
539 return MathUtil.getPointPointDistance(segment.x1, segment.z1, segment.x2, segment.z2)
540end

getSnapCheckDistance

Description
Definition
getSnapCheckDistance()
Code
700function PlaceableFence:getSnapCheckDistance()
701 return self.spec_fence.snapCheckDistance
702end

getSnapDistance

Description
Definition
getSnapDistance()
Code
690function PlaceableFence:getSnapDistance()
691 return self.spec_fence.snapDistance
692end

getSupportsParallelSnapping

Description
Definition
getSupportsParallelSnapping()
Code
724function PlaceableFence:getSupportsParallelSnapping()
725 return self.spec_fence.supportsParallelSnapping
726end

getTotalNumberOfPoles

Description
Get the total number of poles for this fence
Definition
getTotalNumberOfPoles()
Code
556function PlaceableFence:getTotalNumberOfPoles()
557 local spec = self.spec_fence
558 local total = 0
559
560 for s = 1, #spec.segments do
561 total = total + spec.segments[s].poles / 2
562 end
563
564 return total
565end

isPoleInAnySegment

Description
Get whether given exact pole position is in any segment. Only checks start and end of segment
Definition
isPoleInAnySegment()
Code
990function PlaceableFence:isPoleInAnySegment(x, z, ignoreSegment)
991 local spec = self.spec_fence
992 for i = 1, #spec.segments do
993 local segment = spec.segments[i]
994
995 if segment ~= ignoreSegment then
996 if math.abs(segment.x1 - x) < PlaceableFence.EPSILON and math.abs(segment.z1 - z) < PlaceableFence.EPSILON then
997 return segment, true, false
998 elseif math.abs(segment.x2 - x) < PlaceableFence.EPSILON and math.abs(segment.z2 - z) < PlaceableFence.EPSILON then
999 return segment, false, true
1000 end
1001 end
1002 end
1003
1004 return nil
1005end

loadFromXMLFile

Description
Definition
loadFromXMLFile()
Code
326function PlaceableFence:loadFromXMLFile(xmlFile, key)
327 local spec = self.spec_fence
328
329
330 xmlFile:iterate(key .. ".segments.segment", function(index, segmentKey)
331 local x1, z1 = xmlFile:getValue(segmentKey .. "#start")
332 local x2, z2 = xmlFile:getValue(segmentKey .. "#end")
333
334 if x1 ~= nil and z1 ~= nil and x2 ~= nil and z2 ~= nil then
335 local segment = {
336 x1 = x1,
337 z1 = z1,
338 x2 = x2,
339 z2 = z2,
340 renderFirst = xmlFile:getValue(segmentKey .. "#first", true),
341 renderLast = xmlFile:getValue(segmentKey .. "#last", true),
342 gateIndex = xmlFile:getValue(segmentKey .. "#gateIndex"),
343 poles = {}, -- generated
344 segmentKey = segmentKey
345 }
346
347 table.insert(spec.segments, segment)
348 else
349 Logging.xmlError(xmlFile, "Invalid segment position for '%s'. Ignoring segment!", segmentKey)
350 end
351 end)
352
353 -- Rebuild the fence
354 for i = 1, #spec.segments do
355 local segment = spec.segments[i]
356
357 self:generateSegmentPoles(segment, true)
358
359 if segment.gateIndex ~= nil and segment.animatedObject ~= nil then
360 segment.animatedObject:loadFromXMLFile(xmlFile, segment.segmentKey .. ".animatedObject")
361 end
362
363 segment.segmentKey = nil
364 end
365end

onDelete

Description
Definition
onDelete()
Code
222function PlaceableFence:onDelete()
223 local spec = self.spec_fence
224 if spec.animatedObjects ~= nil then
225 for _, animatedObject in ipairs(spec.animatedObjects) do
226 animatedObject:delete()
227 end
228 end
229end

onLoad

Description
Called on loading
Definition
onLoad(table savegame)
Arguments
tablesavegamesavegame
Code
148function PlaceableFence:onLoad(savegame)
149 local spec = self.spec_fence
150 local xmlFile = self.xmlFile
151
152 spec.pickObjects = {}
153 spec.segments = {}
154 spec.segmentsToUpdate = {}
155 spec.animatedObjects = {}
156 spec.previewSegment = nil
157 spec.panelLength = xmlFile:getValue("placeable.fence.panels#length")
158 spec.panelLengthFixed = xmlFile:getValue("placeable.fence.panels#fixedLength")
159 spec.maxVerticalAngle = xmlFile:getValue("placeable.fence#maxVerticalAngle", 35)
160 spec.maxVerticalGateAngle = xmlFile:getValue("placeable.fence#maxVerticalGateAngle", 5)
161 spec.hasInvisiblePoles = xmlFile:getValue("placeable.fence#hasInvisiblePoles", false)
162 spec.supportsParallelSnapping = xmlFile:getValue("placeable.fence#supportsParallelSnapping", false)
163
164 spec.boundingCheckWidth = xmlFile:getValue("placeable.fence#boundingCheckWidth", 0.25)
165 spec.snapDistance = xmlFile:getValue("placeable.fence#snapDistance", nil)
166 spec.snapAngle = xmlFile:getValue("placeable.fence#snapAngle", nil)
167 spec.snapCheckDistance = xmlFile:getValue("placeable.fence#snapCheckDistance", 0.25)
168 spec.allowExtendingOnly = xmlFile:getValue("placeable.fence#extendingOnly", false)
169 spec.maxCornerAngle = xmlFile:getValue("placeable.fence#maxCornerAngle", 180)
170
171 spec.poles = {}
172 local polesNode = xmlFile:getValue("placeable.fence.poles#node", nil, self.components, self.i3dMappings)
173 if polesNode ~= nil then
174 for i = 1, getNumOfChildren(polesNode) do
175 spec.poles[i] = getChildAt(polesNode, i - 1)
176 end
177 end
178
179 spec.panels = {}
180 local panelsNode = xmlFile:getValue("placeable.fence.panels#node", nil, self.components, self.i3dMappings)
181 if panelsNode ~= nil then
182 for i = 1, getNumOfChildren(panelsNode) do
183 spec.panels[i] = getChildAt(panelsNode, i - 1)
184 end
185 end
186
187 spec.gates = {}
188 xmlFile:iterate("placeable.fence.gate", function(_, key)
189 local node = xmlFile:getValue(key .. "#node", nil, self.components, self.i3dMappings)
190 if node ~= nil then
191 local doors = {}
192 xmlFile:iterate(key .. ".door", function(_, doorKey)
193 local doorNode = xmlFile:getValue(doorKey .. "#node")
194 if doorNode ~= nil then
195 table.insert(doors, {
196 node = doorNode,
197 rotation = xmlFile:getValue(doorKey .. "#openRotation", nil, true),
198 translation = xmlFile:getValue(doorKey .. "#openTranslation", nil, true),
199 })
200 else
201 Logging.xmlWarning(xmlFile, "Door node does not exist at %s", doorKey)
202 end
203 end)
204
205 table.insert(spec.gates, {
206 node = node,
207 length = xmlFile:getValue(key .. "#length", 1),
208 triggerNode = xmlFile:getValue(key .. "#triggerNode"),
209 openText = xmlFile:getValue(key .. "#openText", "action_openGate"),
210 closeText = xmlFile:getValue(key .. "#closeText", "action_closeGate"),
211 animationDuration = xmlFile:getValue(key .. "#openDuration", 3),
212 doors = doors,
213 })
214 else
215 Logging.xmlWarning(xmlFile, "Gate node does not exist at %s", key)
216 end
217 end)
218end

onReadStream

Description
Definition
onReadStream()
Code
233function PlaceableFence:onReadStream(streamId, connection)
234 local spec = self.spec_fence
235
236 local numSegments = streamReadInt32(streamId)
237 for i = 1, numSegments do
238 local segment = {}
239 segment.x1 = streamReadFloat32(streamId)
240 segment.z1 = streamReadFloat32(streamId)
241 segment.x2 = streamReadFloat32(streamId)
242 segment.z2 = streamReadFloat32(streamId)
243
244 segment.gateIndex = streamReadUInt8(streamId)
245 if segment.gateIndex == 0 then
246 segment.gateIndex = nil
247 end
248
249 segment.renderFirst = streamReadBool(streamId)
250 segment.renderLast = streamReadBool(streamId)
251
252 segment.poles = {} -- generated
253
254 table.insert(spec.segments, segment)
255 end
256
257 -- Rebuild the fence
258 for i = 1, numSegments do
259 local segment = spec.segments[i]
260
261 self:generateSegmentPoles(segment, true)
262
263 if segment.gateIndex ~= nil and segment.animatedObject ~= nil then
264 local animatedObject = segment.animatedObject
265
266 local animatedObjectId = NetworkUtil.readNodeObjectId(streamId)
267 animatedObject:readStream(streamId, connection)
268 g_client:finishRegisterObject(animatedObject, animatedObjectId)
269 end
270 end
271end

onUpdate

Description
Definition
onUpdate()
Code
308function PlaceableFence:onUpdate(dt)
309 self:updateSegmentUpdateQueue()
310end

onWriteStream

Description
Definition
onWriteStream()
Code
275function PlaceableFence:onWriteStream(streamId, connection)
276 local spec = self.spec_fence
277
278 local numSegments = #spec.segments
279 streamWriteInt32(streamId, numSegments)
280 for i = 1, numSegments do
281 local segment = spec.segments[i]
282
283 streamWriteFloat32(streamId, segment.x1)
284 streamWriteFloat32(streamId, segment.z1)
285 streamWriteFloat32(streamId, segment.x2)
286 streamWriteFloat32(streamId, segment.z2)
287
288 streamWriteUInt8(streamId, segment.gateIndex or 0)
289 streamWriteBool(streamId, segment.renderFirst)
290 streamWriteBool(streamId, segment.renderLast)
291 end
292
293 for i = 1, numSegments do
294 local segment = spec.segments[i]
295
296 if segment.gateIndex ~= nil and segment.animatedObject ~= nil then
297 local animatedObject = segment.animatedObject
298
299 NetworkUtil.writeNodeObjectId(streamId, NetworkUtil.getObjectId(animatedObject))
300 animatedObject:writeStream(streamId, connection)
301 g_server:registerObjectInStream(connection, animatedObject)
302 end
303 end
304end

performNodeDestruction

Description
Definition
performNodeDestruction()
Code
1403function PlaceableFence:performNodeDestruction(superFunc, node)
1404 return self:deletePanel(node)
1405end

prerequisitesPresent

Description
Checks if all prerequisite specializations are loaded
Definition
prerequisitesPresent(table specializations)
Arguments
tablespecializationsspecializations
Return Values
booleanhasPrerequisitetrue if all prerequisite specializations are loaded
Code
23function PlaceableFence.prerequisitesPresent(specializations)
24 return true
25end

previewNodeDestructionNodes

Description
Definition
previewNodeDestructionNodes()
Code
1397function PlaceableFence:previewNodeDestructionNodes(superFunc, node)
1398 return self:getNodesToDeleteForPanel(node)
1399end

recursivelyAddPickingNodes

Description
Definition
recursivelyAddPickingNodes()
Code
1378function PlaceableFence:recursivelyAddPickingNodes(objects, node)
1379 if getRigidBodyType(node) ~= RigidBodyType.NONE then
1380 table.insert(objects, node)
1381 end
1382
1383 local numChildren = getNumOfChildren(node)
1384 for i=1, numChildren do
1385 self:recursivelyAddPickingNodes(objects, getChildAt(node, i-1))
1386 end
1387end

registerEventListeners

Description
Definition
registerEventListeners()
Code
93function PlaceableFence.registerEventListeners(placeableType)
94 SpecializationUtil.registerEventListener(placeableType, "onLoad", PlaceableFence)
95 SpecializationUtil.registerEventListener(placeableType, "onDelete", PlaceableFence)
96 SpecializationUtil.registerEventListener(placeableType, "onReadStream", PlaceableFence)
97 SpecializationUtil.registerEventListener(placeableType, "onWriteStream", PlaceableFence)
98 SpecializationUtil.registerEventListener(placeableType, "onUpdate", PlaceableFence)
99end

registerEvents

Description
Definition
registerEvents()
Code
29function PlaceableFence.registerEvents(placeableType)
30 SpecializationUtil.registerEvent(placeableType, "onCreateSegmentPanel")
31end

registerFunctions

Description
Definition
registerFunctions()
Code
35function PlaceableFence.registerFunctions(placeableType)
36 SpecializationUtil.registerFunction(placeableType, "addSegment", PlaceableFence.addSegment)
37 SpecializationUtil.registerFunction(placeableType, "addSegmentShapesToUpdate", PlaceableFence.addSegmentShapesToUpdate)
38 SpecializationUtil.registerFunction(placeableType, "createSegment", PlaceableFence.createSegment)
39 SpecializationUtil.registerFunction(placeableType, "deletePanel", PlaceableFence.deletePanel)
40 SpecializationUtil.registerFunction(placeableType, "deleteSegment", PlaceableFence.deleteSegment)
41 SpecializationUtil.registerFunction(placeableType, "doDeletePanel", PlaceableFence.doDeletePanel)
42 SpecializationUtil.registerFunction(placeableType, "fakeRandomValueForPosition", PlaceableFence.fakeRandomValueForPosition)
43 SpecializationUtil.registerFunction(placeableType, "findRaycastInfo", PlaceableFence.findRaycastInfo)
44 SpecializationUtil.registerFunction(placeableType, "generateSegmentPoles", PlaceableFence.generateSegmentPoles)
45 SpecializationUtil.registerFunction(placeableType, "getGate", PlaceableFence.getGate)
46 SpecializationUtil.registerFunction(placeableType, "getMaxVerticalAngle", PlaceableFence.getMaxVerticalAngle)
47 SpecializationUtil.registerFunction(placeableType, "getMaxVerticalAngleAndYForPreview", PlaceableFence.getMaxVerticalAngleAndYForPreview)
48 SpecializationUtil.registerFunction(placeableType, "getMaxVerticalGateAngle", PlaceableFence.getMaxVerticalGateAngle)
49 SpecializationUtil.registerFunction(placeableType, "getNodesToDeleteForPanel", PlaceableFence.getNodesToDeleteForPanel)
50 SpecializationUtil.registerFunction(placeableType, "getNumSequments", PlaceableFence.getNumSequments)
51 SpecializationUtil.registerFunction(placeableType, "getPanelLength", PlaceableFence.getPanelLength)
52 SpecializationUtil.registerFunction(placeableType, "getIsPanelLengthFixed", PlaceableFence.getIsPanelLengthFixed)
53 SpecializationUtil.registerFunction(placeableType, "getPoleNear", PlaceableFence.getPoleNear)
54 SpecializationUtil.registerFunction(placeableType, "getPoleNearOverlapCallback", PlaceableFence.getPoleNearOverlapCallback)
55 SpecializationUtil.registerFunction(placeableType, "getPolePosition", PlaceableFence.getPolePosition)
56 SpecializationUtil.registerFunction(placeableType, "getPoleShapeForPreview", PlaceableFence.getPoleShapeForPreview)
57 SpecializationUtil.registerFunction(placeableType, "getPreviewSegment", PlaceableFence.getPreviewSegment)
58 SpecializationUtil.registerFunction(placeableType, "getSegment", PlaceableFence.getSegment)
59 SpecializationUtil.registerFunction(placeableType, "getSegmentLength", PlaceableFence.getSegmentLength)
60 SpecializationUtil.registerFunction(placeableType, "getTotalNumberOfPoles", PlaceableFence.getTotalNumberOfPoles)
61 SpecializationUtil.registerFunction(placeableType, "isPoleInAnySegment", PlaceableFence.isPoleInAnySegment)
62 SpecializationUtil.registerFunction(placeableType, "recursivelyAddPickingNodes", PlaceableFence.recursivelyAddPickingNodes)
63 SpecializationUtil.registerFunction(placeableType, "addPickingNodesForSegment", PlaceableFence.addPickingNodesForSegment)
64 SpecializationUtil.registerFunction(placeableType, "removePickingNodesForSegment", PlaceableFence.removePickingNodesForSegment)
65 SpecializationUtil.registerFunction(placeableType, "setPreviewSegment", PlaceableFence.setPreviewSegment)
66 SpecializationUtil.registerFunction(placeableType, "updatePanelVisuals", PlaceableFence.updatePanelVisuals)
67 SpecializationUtil.registerFunction(placeableType, "updateSegmentShapes", PlaceableFence.updateSegmentShapes)
68 SpecializationUtil.registerFunction(placeableType, "updateSegmentUpdateQueue", PlaceableFence.updateSegmentUpdateQueue)
69 SpecializationUtil.registerFunction(placeableType, "updateDirtyAreas", PlaceableFence.updateDirtyAreas)
70 SpecializationUtil.registerFunction(placeableType, "getSupportsParallelSnapping", PlaceableFence.getSupportsParallelSnapping)
71
72 SpecializationUtil.registerFunction(placeableType, "getBoundingCheckWidth", PlaceableFence.getBoundingCheckWidth)
73 SpecializationUtil.registerFunction(placeableType, "getSnapDistance", PlaceableFence.getSnapDistance)
74 SpecializationUtil.registerFunction(placeableType, "getSnapAngle", PlaceableFence.getSnapAngle)
75 SpecializationUtil.registerFunction(placeableType, "getSnapCheckDistance", PlaceableFence.getSnapCheckDistance)
76 SpecializationUtil.registerFunction(placeableType, "getAllowExtendingOnly", PlaceableFence.getAllowExtendingOnly)
77 SpecializationUtil.registerFunction(placeableType, "getMaxCornerAngle", PlaceableFence.getMaxCornerAngle)
78 SpecializationUtil.registerFunction(placeableType, "getHasParallelSnapping", PlaceableFence.getHasParallelSnapping)
79end

registerOverwrittenFunctions

Description
Definition
registerOverwrittenFunctions()
Code
83function PlaceableFence.registerOverwrittenFunctions(placeableType)
84 SpecializationUtil.registerOverwrittenFunction(placeableType, "collectPickObjects", PlaceableFence.collectPickObjects)
85 SpecializationUtil.registerOverwrittenFunction(placeableType, "getDestructionMethod", PlaceableFence.getDestructionMethod)
86 SpecializationUtil.registerOverwrittenFunction(placeableType, "performNodeDestruction", PlaceableFence.performNodeDestruction)
87 SpecializationUtil.registerOverwrittenFunction(placeableType, "previewNodeDestructionNodes", PlaceableFence.previewNodeDestructionNodes)
88 SpecializationUtil.registerOverwrittenFunction(placeableType, "setOwnerFarmId", PlaceableFence.setOwnerFarmId)
89end

registerSavegameXMLPaths

Description
Definition
registerSavegameXMLPaths()
Code
134function PlaceableFence.registerSavegameXMLPaths(schema, basePath)
135 schema:setXMLSpecializationType("Fence")
136 schema:register(XMLValueType.VECTOR_2, basePath .. ".segments.segment(?)#start", "Segment start position")
137 schema:register(XMLValueType.VECTOR_2, basePath .. ".segments.segment(?)#end", "Segment end position")
138 schema:register(XMLValueType.BOOL, basePath .. ".segments.segment(?)#first", "Segment has first pole visible", true)
139 schema:register(XMLValueType.BOOL, basePath .. ".segments.segment(?)#last", "Segment has last pole visible", true)
140 schema:register(XMLValueType.INT, basePath .. ".segments.segment(?)#gateIndex", "Gate index")
141 AnimatedObject.registerSavegameXMLPaths(schema, basePath .. ".segments.segment(?).animatedObject")
142 schema:setXMLSpecializationType()
143end

registerXMLPaths

Description
Definition
registerXMLPaths()
Code
103function PlaceableFence.registerXMLPaths(schema, basePath)
104 schema:setXMLSpecializationType("Fence")
105 schema:register(XMLValueType.NODE_INDEX, basePath .. ".fence.poles#node", "Group of pole variants")
106 schema:register(XMLValueType.NODE_INDEX, basePath .. ".fence.panels#node", "Group of panel variants")
107 schema:register(XMLValueType.FLOAT, basePath .. ".fence.panels#length", "Length of the panels", 1)
108 schema:register(XMLValueType.BOOL, basePath .. ".fence.panels#fixedLength", "Panel length is fixed", false)
109 schema:register(XMLValueType.ANGLE, basePath .. ".fence#maxVerticalAngle", "Maximum angle for vertical offset")
110 schema:register(XMLValueType.ANGLE, basePath .. ".fence#maxVerticalGateAngle", "Maximum angle for vertical offset with gates")
111 schema:register(XMLValueType.FLOAT, basePath .. ".fence#boundingCheckWidth", "With of the bounding box used to check collision", 0.25)
112 schema:register(XMLValueType.FLOAT, basePath .. ".fence#snapDistance", "Snap distance", nil)
113 schema:register(XMLValueType.INT, basePath .. ".fence#snapAngle", "Snap angle in degrees", nil)
114 schema:register(XMLValueType.FLOAT, basePath .. ".fence#snapCheckDistance", "Snap distance", nil)
115 schema:register(XMLValueType.BOOL, basePath .. ".fence#extendingOnly", "Whether to only allow extending a segment and no attaching to the center", false)
116 schema:register(XMLValueType.ANGLE, basePath .. ".fence#maxCornerAngle", "Maximum angle between two connected segments", 180)
117 schema:register(XMLValueType.BOOL, basePath .. ".fence#supportsParallelSnapping", "Whether parallel snapping is an option", false)
118 schema:register(XMLValueType.BOOL, basePath .. ".fence#hasInvisiblePoles", "Poles are not visible so another display method is used", false)
119 schema:register(XMLValueType.NODE_INDEX, basePath .. ".fence.gate(?)#node", "Gate node")
120 schema:register(XMLValueType.FLOAT, basePath .. ".fence.gate(?)#length", "Length of the gate from pole to pole", 1)
121 schema:register(XMLValueType.INT, basePath .. ".fence.gate(?)#triggerNode", "Gate trigger node index from gate node")
122 schema:register(XMLValueType.STRING, basePath .. ".fence.gate(?)#openText", "Action open text")
123 schema:register(XMLValueType.STRING, basePath .. ".fence.gate(?)#closeText", "Action close text")
124 schema:register(XMLValueType.FLOAT, basePath .. ".fence.gate(?)#openDuration", "Duration of animation in seconds")
125 schema:register(XMLValueType.INT, basePath .. ".fence.gate(?).door(?)#node", "Node of the door")
126 schema:register(XMLValueType.VECTOR_ROT, basePath .. ".fence.gate(?).door(?)#openRotation", "Rotation of the node when fully open")
127 schema:register(XMLValueType.VECTOR_TRANS, basePath .. ".fence.gate(?).door(?)#openTranslation", "Translation of the node when fully open")
128 AnimatedObjectBuilder.registerXMLPaths(schema, basePath .. ".fence.gate(?)")
129 schema:setXMLSpecializationType()
130end

removePickingNodesForSegment

Description
Definition
removePickingNodesForSegment()
Code
1361function PlaceableFence:removePickingNodesForSegment(segment)
1362 if segment == self.spec_fence.previewSegment then
1363 return
1364 end
1365
1366 if segment.group ~= nil then
1367 local objects = {}
1368 self:recursivelyAddPickingNodes(objects, segment.group)
1369
1370 for i = 1, #objects do
1371 g_currentMission:removeNodeObject(objects[i])
1372 end
1373 end
1374end

saveToXMLFile

Description
Definition
saveToXMLFile()
Code
370function PlaceableFence:saveToXMLFile(xmlFile, key, usedModNames)
371 local spec = self.spec_fence
372
373 xmlFile:setTable(key .. ".segments.segment", spec.segments, function(path, segment, _)
374 xmlFile:setValue(path .. "#start", segment.x1, segment.z1)
375 xmlFile:setValue(path .. "#end", segment.x2, segment.z2)
376
377 if segment.gateIndex ~= nil then
378 xmlFile:setValue(path .. "#gateIndex", segment.gateIndex)
379 if segment.animatedObject ~= nil then
380 segment.animatedObject:saveToXMLFile(xmlFile, path .. ".animatedObject", usedModNames)
381 end
382 end
383
384 -- No need to save default value
385 if not segment.renderFirst then
386 xmlFile:setValue(path .. "#first", false)
387 end
388 if not segment.renderLast then
389 xmlFile:setValue(path .. "#last", false)
390 end
391 end)
392end

setOwnerFarmId

Description
Definition
setOwnerFarmId()
Code
314function PlaceableFence:setOwnerFarmId(superFunc, ownerFarmId, noEventSend)
315 local spec = self.spec_fence
316
317 superFunc(self, ownerFarmId, noEventSend)
318
319 for _, animatedObject in ipairs(spec.animatedObjects) do
320 animatedObject:setOwnerFarmId(ownerFarmId, true)
321 end
322end

setPreviewSegment

Description
Definition
setPreviewSegment()
Code
624function PlaceableFence:setPreviewSegment(segment)
625 local spec = self.spec_fence
626
627 -- Delete old preview nodes
628 if spec.previewSegment ~= nil and spec.previewSegment.group ~= nil and segment ~= spec.previewSegment then
629 delete(spec.previewSegment.group)
630 spec.previewSegment.group = nil
631 end
632
633 spec.previewSegment = segment
634
635 if segment ~= nil then
636 self:generateSegmentPoles(segment, false)
637 end
638end

updateDirtyAreas

Description
Definition
updateDirtyAreas()
Code
612function PlaceableFence:updateDirtyAreas(segment)
613 local minX = math.min(segment.x1, segment.x2)
614 local maxX = math.max(segment.x1, segment.x2)
615 local minZ = math.min(segment.z1, segment.z2)
616 local maxZ = math.max(segment.z1, segment.z2)
617
618 g_densityMapHeightManager:setCollisionMapAreaDirty(minX, minZ, maxX, maxZ, true)
619 g_currentMission.aiSystem:setAreaDirty(minX, maxX, minZ, maxZ)
620end

updateSegmentShapes

Description
Definition
updateSegmentShapes()
Code
1083function PlaceableFence:updateSegmentShapes(segment)
1084 local spec = self.spec_fence
1085 local isPreviewSegment = segment == spec.previewSegment
1086 local enablePhysics = not isPreviewSegment
1087
1088 -- Delete AO before deleting nodes so trigger is handled correctly and no double-delete occurs
1089 local gateTime
1090 if segment.animatedObject ~= nil then
1091 gateTime = segment.animatedObject.animation.time
1092 segment.animatedObject:delete()
1093 segment.animatedObject = nil
1094 end
1095
1096 -- Create a group for all segment nodes so we can delete them at once when re-generating
1097 if segment.group ~= nil then
1098 delete(segment.group)
1099 end
1100 segment.group = createTransformGroup("fence_segment")
1101 link(self.rootNode, segment.group)
1102
1103 -- Create a pole for every xz pair.
1104 for i = 1, #segment.poles, 2 do
1105 local x, z = segment.poles[i], segment.poles[i+1]
1106 local y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x, 0, z)
1107
1108 -- If there is a first pole, use the shape. Otherwise use an empty TG so we can still rotate
1109 local pole
1110 local poleIsFake = false
1111 if #spec.poles > 0 and (i > 1 or segment.renderFirst) and (i < #segment.poles - 2 or segment.renderLast) then
1112 local poleIndex = self:fakeRandomValueForPosition(x, y, z, #spec.poles)
1113 pole = clone(spec.poles[poleIndex], false, false, false)
1114 else
1115 pole = createTransformGroup("fence_firstPole")
1116 poleIsFake = true
1117 end
1118 link(segment.group, pole)
1119
1120 -- Set position
1121 setWorldTranslation(pole, x, y, z)
1122
1123 if segment.gateIndex ~= nil then
1124 -- Poles for gates: always rotate to the other side
1125 local prevX, prevZ = segment.poles[(i+2) % 4], segment.poles[(i+2) % 4 + 1]
1126 local dx, dz = x - prevX, z - prevZ
1127 local rotY = math.atan2(dx, dz) + math.pi
1128
1129 setWorldRotation(pole, 0, rotY, 0)
1130
1131 if enablePhysics and not poleIsFake then
1132 addToPhysics(getChildAt(pole, 0))
1133 end
1134 elseif i < #segment.poles - 2 then
1135 -- Next pole exists: connect it with a panel and rotate this one properly, but not if these are gate poles
1136 -- Find position of the next pole so we can match it visually
1137 local nextX, nextZ = segment.poles[i+2], segment.poles[i+3]
1138 local nextY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, nextX, 0, nextZ)
1139
1140 local dx, dy, dz = x - nextX, y - nextY, z - nextZ
1141 local rotY = math.atan2(dx, dz) + math.pi
1142
1143 -- Pole rotates into direction of fence
1144 setWorldRotation(pole, 0, rotY, 0)
1145
1146 -- Find a panel and connect
1147 local panelIndex = self:fakeRandomValueForPosition(x, y, z, #spec.panels)
1148 local panel = clone(spec.panels[panelIndex], false, false, false)
1149 link(pole, panel)
1150
1151 -- Scale fence to fit next pole. This is length on XZ plane only, as shader will transform along Y
1152 local fenceLength = MathUtil.getPointPointDistance(x, z, nextX, nextZ)
1153
1154 -- Adjust panel distortion to match height of next fence
1155 self:updatePanelVisuals(panel, dy, segment, i, fenceLength)
1156
1157 -- Adjust collision by rotating it to match terrain inclination
1158 local col = getChildAt(panel, 0)
1159
1160 local xDir, yDir, zDir = 0, -dy, fenceLength
1161 xDir, yDir, zDir = MathUtil.vector3Normalize(xDir, yDir, zDir)
1162 local length = math.sqrt(dx*dx + dy*dy + dz*dz)
1163
1164 local offset = (length - fenceLength) * 0.5
1165 local colX, colY, colZ = getTranslation(col)
1166 colX = colX + xDir * offset
1167 colY = colY + yDir * offset
1168 colZ = colZ + zDir * offset
1169
1170 setDirection(col, xDir, yDir, zDir, 0, 1, 0)
1171 setTranslation(col, colX, colY, colZ)
1172
1173 if enablePhysics then
1174 addToPhysics(col)
1175 end
1176
1177 SpecializationUtil.raiseEvent(self, "onCreateSegmentPanel", isPreviewSegment, segment, panel, i, dy)
1178
1179 if enablePhysics and not poleIsFake then
1180 addToPhysics(getChildAt(pole, 0))
1181 end
1182 elseif segment.renderLast then
1183 -- End of the segment. We could look up the next segment but that is expensive
1184 -- Instead, just align to the previous pole. If no previous pole, it is a single
1185 -- pole and any rotation is fine so we can leave it.
1186 if i > 2 then
1187 local prevX, prevZ = segment.poles[i-2], segment.poles[i-1]
1188 local dx, dz = x - prevX, z - prevZ
1189 local rotY = math.atan2(dx, dz) + math.pi
1190
1191 setWorldRotation(pole, 0, rotY, 0)
1192
1193 if enablePhysics and not poleIsFake then
1194 addToPhysics(getChildAt(pole, 0))
1195 end
1196 end
1197 end
1198 end
1199
1200 if segment.gateIndex ~= nil then
1201 local gateInfo = spec.gates[segment.gateIndex]
1202
1203 local gate = clone(gateInfo.node, false, false, false)
1204 link(segment.group, gate)
1205
1206 local segementTerrainY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, segment.x1, 0, segment.z1)
1207 setWorldTranslation(gate, segment.x1, segementTerrainY, segment.z1)
1208
1209 local dx, dz = segment.x1 - segment.x2, segment.z1 - segment.z2
1210 local rotY = math.atan2(dx, dz) + math.pi
1211 setWorldRotation(gate, 0, rotY, 0)
1212
1213 -- Only build animation on final placement
1214 if not isPreviewSegment then
1215 local animatedObject = AnimatedObject.new(self.isServer, self.isClient)
1216 animatedObject:setOwnerFarmId(self:getOwnerFarmId(), false)
1217
1218 -- Note: two gates can start from the same pole, and then their end node can be at the same x or z. So need all 4
1219 local saveId = string.format("AnimatedObject_%s_gate_%d_%d_%d_%d", self.configFileName, segment.x1, segment.z1, segment.x2, segment.x2)
1220 local builder = animatedObject:builder(self.configFileName, saveId)
1221
1222 for _, door in ipairs(gateInfo.doors) do
1223 local doorNode = getChildAt(gate, door.node)
1224 builder:addSimplePart(doorNode, door.rotation, door.translation)
1225 addToPhysics(doorNode)
1226 end
1227
1228 local triggerNode = getChildAt(gate, gateInfo.triggerNode)
1229 builder:setTrigger(triggerNode)
1230 addToPhysics(triggerNode)
1231
1232 builder:setActions("ACTIVATE_HANDTOOL", gateInfo.openText, nil, gateInfo.closeText)
1233 builder:setDuration(gateInfo.animationDuration * 1000)
1234
1235 if self.xmlFile == nil then
1236 self.xmlFile = XMLFile.load("fence", self.configFileName)
1237 end
1238 builder:setSounds(self.xmlFile.handle, string.format("placeable.fence.gate(%d).sounds", segment.gateIndex - 1), gate)
1239
1240 if not builder:build() then
1241 animatedObject:delete()
1242 else
1243 animatedObject:register(true)
1244
1245 table.insert(spec.animatedObjects, animatedObject)
1246 segment.animatedObject = animatedObject
1247
1248 if gateTime ~= nil then
1249 animatedObject:setAnimTime(gateTime, true)
1250 end
1251
1252 if self.isServer then
1253 -- Send one event to all clients with new AO so it is synced
1254 for i = 1, #spec.segments do
1255 if spec.segments[i] == segment then
1256 g_server:broadcastEvent(PlaceableFenceAddGateEvent.new(self, i, animatedObject), false, nil, self)
1257 break
1258 end
1259 end
1260 end
1261 end
1262 else
1263 -- In preview we show the gate at a slightly open angle to indicate open/close direction
1264 for _, door in ipairs(gateInfo.doors) do
1265 local doorNode = getChildAt(gate, door.node)
1266
1267 local alpha = 0.3
1268
1269 if door.translation ~= nil then
1270 local x1, y1, z1 = getTranslation(doorNode)
1271 local x2, y2, z2 = unpack(door.translation)
1272
1273 setTranslation(doorNode, x1 + (x2 - x1) * alpha, y1 + (y2 - y1) * alpha, z1 + (z2 - z1) * alpha)
1274 end
1275 if door.rotation ~= nil then
1276 local x1, y1, z1 = getRotation(doorNode)
1277 local x2, y2, z2 = unpack(door.rotation)
1278
1279 setRotation(doorNode, x1 + (x2 - x1) * alpha, y1 + (y2 - y1) * alpha, z1 + (z2 - z1) * alpha)
1280 end
1281 end
1282 end
1283 end
1284
1285 -- if enablePhysics then
1286 -- addToPhysics(segment.group)
1287 -- end
1288end

updateSegmentUpdateQueue

Description
Definition
updateSegmentUpdateQueue()
Code
1325function PlaceableFence:updateSegmentUpdateQueue()
1326 local spec = self.spec_fence
1327 if #spec.segmentsToUpdate > 0 then
1328 local segment = spec.segmentsToUpdate[1]
1329 table.remove(spec.segmentsToUpdate, 1)
1330
1331 self:removePickingNodesForSegment(segment)
1332 self:updateSegmentShapes(segment)
1333 self:addPickingNodesForSegment(segment)
1334
1335 self:raiseActive()
1336 end
1337end