Script v1_7_1_0
- AI
- Animals
- Collections
- Contracts
- Debug
- Economy
- Elements
- EnvironmentalScore
- Errors
- Events
- GUI
- Handtools
- Hud
- I3d
- Input
- Jobs
- Maps
- Materials
- Misc
- Objects
- Parameters
- Placeables
- Placement
- Player
- Shop
- Sounds
- Specialization
- Specializations
- AIConveyorBelt
- AIDrivable
- AIFieldWorker
- AIImplement
- AIJobVehicle
- AIVehicle
- AIVehicleObstacle
- AnimatedVehicle
- ArticulatedAxis
- Attachable
- AttacherJointControl
- AttacherJoints
- AutoLoader
- BaleGrab
- BaleLoader
- Baler
- BaleWrapper
- BaseMaterial
- BigBag
- BunkerSiloCompacter
- BunkerSiloInteractor
- CCTDrivable
- Combine
- ConnectionHoses
- ConveyorBelt
- Cover
- CrabSteering
- Crawlers
- CropSensor
- Cultivator
- Cutter
- Cylindered
- CylinderedFoldable
- Dashboard
- Dischargeable
- Drivable
- DynamicallyLoadedParts
- DynamicMountAttacher
- Enterable
- ExtendedAIVehicle
- ExtendedCombine
- ExtendedMotorized
- ExtendedMower
- ExtendedSowingMachine
- ExtendedSprayer
- ExtendedWearable
- FertilizingCultivator
- FertilizingSowingMachine
- FillTriggerVehicle
- FillUnit
- FillVolume
- Foldable
- FoliageBending
- ForageWagon
- FrontloaderAttacher
- FruitPreparer
- GroundAdjustedNodes
- GroundReference
- HeadlandAnimation
- Honk
- HookLiftContainer
- HookLiftTrailer
- IKChains
- InlineWrapper
- JigglingParts
- Leveler
- LicensePlates
- Lights
- LivestockTrailer
- Locomotive
- LogGrab
- ManureBarrel
- ManureSensor
- MixerWagon
- Motorized
- Mountable
- Mower
- Mulcher
- MultipleItemPurchase
- Pallet
- Pickup
- Pipe
- PlaceableAI
- PlaceableAnimatedObjects
- PlaceableBeehive
- PlaceableBeehivePalletSpa...
- PlaceableBunkerSilo
- PlaceableBuyingStation
- PlaceableCartridgePlayer
- PlaceableChargingStation
- PlaceableClearAreas
- PlaceableColorable
- PlaceableDeletedNodes
- PlaceableDoghouse
- PlaceableDynamicallyLoade...
- PlaceableFarmhouse
- PlaceableFence
- PlaceableFoliageAreas
- PlaceableGreenhouse
- PlaceableHighPressureWash...
- PlaceableHotspots
- PlaceableHusbandry
- PlaceableHusbandryAnimals
- PlaceableHusbandryFeeding...
- PlaceableHusbandryFence
- PlaceableHusbandryFood
- PlaceableHusbandryLiquidM...
- PlaceableHusbandryMilk
- PlaceableHusbandryPallets
- PlaceableHusbandryStraw
- PlaceableHusbandryWater
- PlaceableIncomePerHour
- PlaceableIndoorAreas
- PlaceableInfoTrigger
- PlaceableLeveling
- PlaceableLights
- PlaceableManureHeap
- PlaceablePlacement
- PlaceableProductionPoint
- PlaceableSellingStation
- PlaceableSilo
- PlaceableSiloExtension
- PlaceableSolarPanels
- PlaceableTipOcclusionArea...
- PlaceableTrainSystem
- PlaceableTriggerMarkers
- PlaceableVine
- PlaceableWardrobe
- PlaceableWeatherStation
- PlaceableWeighingStation
- PlaceableWindTurbine
- PlaceableWorkshop
- Plow
- PlowPacker
- PowerConsumer
- PowerTakeOffs
- PrecisionFarmingStatistic
- PushHandTool
- RandomlyMovingParts
- ReceivingHopper
- ReverseDriving
- Rideable
- RidgeMarker
- Roller
- Ropes
- RTKStation
- SaltSpreader
- SemiTrailerFront
- Shovel
- SlopeCompensation
- SmartAttach
- SoilSampler
- SowingMachine
- SpeedRotatingParts
- SplineVehicle
- Sprayer
- StonePicker
- StrawBlower
- StumpCutter
- SupportVehicle
- Suspensions
- Tedder
- TensionBeltObject
- TensionBelts
- TestAreas
- TipOccluder
- Trailer
- TreePlanter
- TreeSaplingPallet
- TreeSaw
- TurnOnVehicle
- VariableWorkWidth
- VehicleSettings
- VineCutter
- VineDetector
- VinePrepruner
- Washable
- WaterTrailer
- Wearable
- Weeder
- WeedSpotSpray
- Wheels
- WindBending
- Windrower
- Wipers
- WoodCrusher
- WoodHarvester
- WorkArea
- WorkMode
- WorkParticles
- StateMachine
- Statistics
- Tasks
- Triggers
- Utils
- Vehicles
Engine v1_7_1_0
- AI
- Animation
- Camera
- Entity
- Fillplanes
- general
- General
- I3D
- Input
- Lighting
- Math
- Network
- Node
- NoteNode
- Overlays
- Particle System
- Physics
- Rendering
- Scenegraph
- Shape
- Sound
- Spline
- String
- Terrain Detail
- Text Rendering
- Tire Track
- VoiceChat
- XML
Foundation Reference
Cylindered
DescriptionSpecialization for vehicles with dependent movable parts (e.g. cylinders)Functions
- actionEventInput
- allowLoadMovingToolStates
- checkMovingPartDirtyUpdateNode
- getAdditionalSchemaText
- getConsumingLoad
- getDischargeNodeEmptyFactor
- getDoConsumePtoPower
- getIsDynamicMountGrabOpened
- getIsMovingPartActive
- getIsMovingToolActive
- getMovingPartByNode
- getMovingToolByNode
- getMovingToolDashboardState
- getMovingToolMoveValue
- getMovingToolState
- getShovelNodeIsActive
- getTranslatingPartByNode
- getWearMultiplier
- initSpecialization
- isDetachAllowed
- limitInterpolator
- loadCopyLocalDirectionParts
- loadDependentAnimations
- loadDependentAttacherJoints
- loadDependentComponentJoints
- loadDependentMovingTools
- loadDependentParts
- loadDependentTranslatingParts
- loadDependentWheels
- loadDischargeNode
- loadDynamicMountGrabFromXML
- loadEasyArmControlFromXML
- loadExtraDependentParts
- loadMovingPartFromXML
- loadMovingToolFromXML
- loadObjectChangeValuesFromXML
- loadRotationBasedLimits
- loadShovelNode
- movingToolDashboardAttributes
- onAnimationPartChanged
- onDeactivate
- onDelete
- onDraw
- onLoad
- onLoadFinished
- onMovingPartSoundEvent
- onPostAttach
- onPostLoad
- onPostUpdate
- onPostUpdateTick
- onReadStream
- onReadUpdateStream
- onRegisterActionEvents
- onSelect
- onUnselect
- onUpdate
- onUpdateEnd
- onUpdateTick
- onVehicleSettingChanged
- onWriteStream
- onWriteUpdateStream
- prerequisitesPresent
- registerCopyLocalDirectionXMLPaths
- registerDependentAnimationXMLPaths
- registerDependentComponentJointXMLPaths
- registerEventListeners
- registerEvents
- registerFunctions
- registerOverwrittenFunctions
- resolveDependentPartData
- saveToXMLFile
- setAbsoluteToolRotation
- setAbsoluteToolTranslation
- setComponentJointFrame
- setDelayedData
- setDirty
- setIsEasyControlActive
- setMovingToolDirty
- setObjectChangeValues
- setToolAnimation
- setToolRotation
- setToolTranslation
- updateAttacherJoints
- updateComponentJoints
- updateCylinderedInitial
- updateDelayedTool
- updateDependentAnimations
- updateDependentToolLimits
- updateDirtyMovingParts
- updateEasyControl
- updateExtraDependentParts
- updateMovingPart
- updateMovingToolSoundEvents
- updateRotationBasedLimits
- updateWheels
actionEventInput
DescriptionDefinitionactionEventInput()Code
4118 | function Cylindered.actionEventInput(self, actionName, inputValue, callbackState, isAnalog, isMouse) |
4119 | local spec = self.spec_cylindered |
4120 | local tool = spec.movingTools[callbackState] |
4121 | |
4122 | if tool ~= nil then |
4123 | tool.lastInputTime = g_time |
4124 | |
4125 | local move |
4126 | if tool.invertAxis then |
4127 | move = -inputValue |
4128 | else |
4129 | move = inputValue |
4130 | end |
4131 | |
4132 | move = move * g_gameSettings:getValue(GameSettings.SETTING.VEHICLE_ARM_SENSITIVITY) |
4133 | if isMouse then |
4134 | -- revert dt scaling for mouse input |
4135 | move = move * 16.666 / g_currentDt * tool.mouseSpeedFactor |
4136 | |
4137 | -- allow only the input of the highest mouse axis value |
4138 | -- lock the move of the lower mouse axis value until it is higher than 0.75 or the value is higher than the doubled value of the other tool |
4139 | if tool.moveLocked then |
4140 | if math.abs(inputValue) < 0.75 then |
4141 | if math.abs(move) > math.abs(tool.lockTool.move) * 2 then |
4142 | tool.moveLocked = false |
4143 | else |
4144 | move = 0 |
4145 | end |
4146 | else |
4147 | tool.moveLocked = false |
4148 | end |
4149 | else |
4150 | local checkOtherTools = function(tools) |
4151 | for tool2Index, tool2 in ipairs(tools) do |
4152 | if tool2Index ~= callbackState then |
4153 | if tool2.move ~= nil and tool2.move ~= 0 then |
4154 | if math.abs(move) > math.abs(tool2.move) then |
4155 | tool2.move = 0 |
4156 | tool2.moveToSend = 0 |
4157 | tool2.moveLocked = true |
4158 | tool2.lockTool = tool |
4159 | else |
4160 | move = 0 |
4161 | tool.moveLocked = true |
4162 | tool.lockTool = tool2 |
4163 | end |
4164 | end |
4165 | end |
4166 | end |
4167 | end |
4168 | |
4169 | checkOtherTools(spec.movingTools) |
4170 | |
4171 | if self.getAttachedImplements ~= nil then |
4172 | for _, implement in pairs(self:getAttachedImplements()) do |
4173 | local vehicle = implement.object |
4174 | if vehicle.spec_cylindered ~= nil then |
4175 | checkOtherTools(vehicle.spec_cylindered.movingTools) |
4176 | end |
4177 | end |
4178 | end |
4179 | end |
4180 | end |
4181 | |
4182 | if move ~= tool.move then |
4183 | tool.move = move |
4184 | end |
4185 | |
4186 | if tool.move ~= tool.moveToSend then |
4187 | tool.moveToSend = tool.move |
4188 | self:raiseDirtyFlags(spec.cylinderedInputDirtyFlag) |
4189 | end |
4190 | |
4191 | tool.smoothedMove = tool.smoothedMove * 0.9 + move * 0.1 |
4192 | end |
4193 | end |
allowLoadMovingToolStates
DescriptionReturns if loading of moving tool stats from savegame is allowedDefinition
allowLoadMovingToolStates()Return Values
boolean | isAllowed | is allowed |
2887 | function Cylindered:allowLoadMovingToolStates(superFunc) |
2888 | return true |
2889 | end |
checkMovingPartDirtyUpdateNode
DescriptionDefinitioncheckMovingPartDirtyUpdateNode()Code
2789 | function Cylindered:checkMovingPartDirtyUpdateNode(node, movingPart) |
2790 | end |
getAdditionalSchemaText
DescriptionDefinitiongetAdditionalSchemaText()Code
3178 | function Cylindered:getAdditionalSchemaText(superFunc) |
3179 | local t = superFunc(self) |
3180 | if self.isClient then |
3181 | if self:getIsActiveForInput(true) then |
3182 | local spec = self.spec_cylindered |
3183 | if #spec.controlGroupNames > 1 and spec.currentControlGroupIndex ~= 0 then |
3184 | t = tostring(spec.currentControlGroupIndex) |
3185 | end |
3186 | end |
3187 | end |
3188 | |
3189 | return t |
3190 | end |
getConsumingLoad
DescriptionDefinitiongetConsumingLoad()Code
3215 | function Cylindered:getConsumingLoad(superFunc) |
3216 | local value, count = superFunc(self) |
3217 | |
3218 | local spec = self.spec_cylindered |
3219 | local loadPercentage = math.max(spec.powerConsumingTimer / spec.powerConsumingActiveTimeOffset, 0) |
3220 | return value + loadPercentage, count + 1 |
3221 | end |
getDischargeNodeEmptyFactor
DescriptionDefinitiongetDischargeNodeEmptyFactor()Code
3050 | function Cylindered:getDischargeNodeEmptyFactor(superFunc, dischargeNode) |
3051 | if dischargeNode.movingToolActivation == nil then |
3052 | return superFunc(self, dischargeNode) |
3053 | else |
3054 | local spec = self.spec_cylindered |
3055 | local movingToolActivation = dischargeNode.movingToolActivation |
3056 | |
3057 | local currentSpeed = superFunc(self, dischargeNode) |
3058 | |
3059 | local movingTool = spec.nodesToMovingTools[movingToolActivation.node] |
3060 | local state = MathUtil.clamp(Cylindered.getMovingToolState(self, movingTool), 0, 1) |
3061 | if movingToolActivation.isInverted then |
3062 | state = math.abs(state-1) |
3063 | end |
3064 | |
3065 | state = math.max(state-movingToolActivation.openOffset, 0) / movingToolActivation.openOffsetInv |
3066 | local speedFactor = MathUtil.clamp(state/movingToolActivation.openFactor, 0, 1) |
3067 | |
3068 | return currentSpeed * speedFactor |
3069 | end |
3070 | end |
getDoConsumePtoPower
DescriptionReturns if should consume pto powerDefinition
getDoConsumePtoPower()Return Values
boolean | consume | consumePtoPower |
3209 | function Cylindered:getDoConsumePtoPower(superFunc) |
3210 | return superFunc(self) or self.spec_cylindered.powerConsumingTimer > 0 |
3211 | end |
getIsDynamicMountGrabOpened
DescriptionDefinitiongetIsDynamicMountGrabOpened()Code
3134 | function Cylindered:getIsDynamicMountGrabOpened(superFunc, grab) |
3135 | local isActive = superFunc(self, grab) |
3136 | if not isActive or grab.movingToolActivation == nil then |
3137 | return isActive |
3138 | end |
3139 | |
3140 | |
3141 | local spec = self.spec_cylindered |
3142 | local movingToolActivation = grab.movingToolActivation |
3143 | local movingTool = spec.nodesToMovingTools[movingToolActivation.node] |
3144 | local state = Cylindered.getMovingToolState(self, movingTool) |
3145 | if movingToolActivation.isInverted then |
3146 | state = math.abs(state-1) |
3147 | end |
3148 | |
3149 | return state > movingToolActivation.openFactor |
3150 | end |
getIsMovingPartActive
DescriptionDefinitiongetIsMovingPartActive()Code
2927 | function Cylindered:getIsMovingPartActive(movingPart) |
2928 | return movingPart.isActive |
2929 | end |
getIsMovingToolActive
DescriptionDefinitiongetIsMovingToolActive()Code
2921 | function Cylindered:getIsMovingToolActive(movingTool) |
2922 | return movingTool.isActive |
2923 | end |
getMovingPartByNode
DescriptionDefinitiongetMovingPartByNode()Code
2899 | function Cylindered:getMovingPartByNode(node) |
2900 | return self.spec_cylindered.nodesToMovingParts[node] |
2901 | end |
getMovingToolByNode
DescriptionDefinitiongetMovingToolByNode()Code
2893 | function Cylindered:getMovingToolByNode(node) |
2894 | return self.spec_cylindered.nodesToMovingTools[node] |
2895 | end |
getMovingToolDashboardState
DescriptionDefinitiongetMovingToolDashboardState()Code
4197 | function Cylindered.getMovingToolDashboardState(self, dashboard) |
4198 | local vehicle = self |
4199 | |
4200 | if dashboard.attacherJointNode ~= nil then |
4201 | dashboard.attacherJointIndex = self:getAttacherJointIndexByNode(dashboard.attacherJointNode) |
4202 | dashboard.attacherJointNode = nil |
4203 | end |
4204 | |
4205 | if dashboard.attacherJointIndex ~= nil then |
4206 | local implement = self:getImplementFromAttacherJointIndex(dashboard.attacherJointIndex) |
4207 | if implement ~= nil then |
4208 | vehicle = implement.object |
4209 | else |
4210 | vehicle = nil |
4211 | end |
4212 | end |
4213 | |
4214 | if vehicle ~= nil then |
4215 | local spec = vehicle.spec_cylindered |
4216 | if spec ~= nil then |
4217 | for _, movingTool in ipairs(spec.movingTools) do |
4218 | if movingTool.axis == dashboard.axis then |
4219 | return (movingTool.smoothedMove + 1) / 2 |
4220 | end |
4221 | end |
4222 | end |
4223 | end |
4224 | |
4225 | return 0.5 |
4226 | end |
getMovingToolMoveValue
DescriptionDefinitiongetMovingToolMoveValue()Code
2933 | function Cylindered:getMovingToolMoveValue(movingTool) |
2934 | return movingTool.move + movingTool.externalMove |
2935 | end |
getMovingToolState
DescriptionReturns moving tool stateDefinition
getMovingToolState(table tool)Arguments
table | tool | tool |
float | state | state of moving tool [0..1] |
3504 | function Cylindered.getMovingToolState(self, tool) |
3505 | local state = 0 |
3506 | if tool.rotMax ~= nil and tool.rotMin ~= nil then |
3507 | state = (tool.curRot[tool.rotationAxis]-tool.rotMin) / (tool.rotMax-tool.rotMin) |
3508 | else |
3509 | if tool.transMax ~= nil and tool.transMin ~= nil then |
3510 | state = (tool.curTrans[tool.translationAxis]-tool.transMin) / (tool.transMax-tool.transMin) |
3511 | end |
3512 | end |
3513 | |
3514 | return state |
3515 | end |
getShovelNodeIsActive
DescriptionDefinitiongetShovelNodeIsActive()Code
3094 | function Cylindered:getShovelNodeIsActive(superFunc, shovelNode) |
3095 | local isActive = superFunc(self, shovelNode) |
3096 | if not isActive or shovelNode.movingToolActivation == nil then |
3097 | return isActive |
3098 | end |
3099 | |
3100 | |
3101 | local spec = self.spec_cylindered |
3102 | local movingToolActivation = shovelNode.movingToolActivation |
3103 | local movingTool = spec.nodesToMovingTools[movingToolActivation.node] |
3104 | local state = Cylindered.getMovingToolState(self, movingTool) |
3105 | if movingToolActivation.isInverted then |
3106 | state = math.abs(state-1) |
3107 | end |
3108 | |
3109 | return state > movingToolActivation.openFactor |
3110 | end |
getTranslatingPartByNode
DescriptionDefinitiongetTranslatingPartByNode()Code
2905 | function Cylindered:getTranslatingPartByNode(node) |
2906 | local spec = self.spec_cylindered |
2907 | for i=1, #spec.movingParts do |
2908 | local part = spec.movingParts[i] |
2909 | if part.translatingParts ~= nil then |
2910 | for j=1, #part.translatingParts do |
2911 | if part.translatingParts[j].node == node then |
2912 | return part.translatingParts[j] |
2913 | end |
2914 | end |
2915 | end |
2916 | end |
2917 | end |
getWearMultiplier
DescriptionReturns current wear multiplierDefinition
getWearMultiplier()Return Values
float | wearMultiplier | current wear multiplier |
3195 | function Cylindered:getWearMultiplier(superFunc) |
3196 | local spec = self.spec_cylindered |
3197 | local multiplier = superFunc(self) |
3198 | |
3199 | if spec.isHydraulicSamplePlaying then |
3200 | multiplier = multiplier + self:getWorkWearMultiplier() |
3201 | end |
3202 | |
3203 | return multiplier |
3204 | end |
initSpecialization
DescriptionDefinitioninitSpecialization()Code
57 | function Cylindered.initSpecialization() |
58 | local schema = Vehicle.xmlSchema |
59 | |
60 | schema:setXMLSpecializationType("Cylindered") |
61 | |
62 | SoundManager.registerSampleXMLPaths(schema, "vehicle.cylindered.sounds", "hydraulic") |
63 | schema:register(XMLValueType.TIME, "vehicle.cylindered.movingTools#powerConsumingActiveTimeOffset", "Power consumer deactivation delay. After the moving tool has not been moved this long it will no longer consume power.", 5) |
64 | |
65 | SoundManager.registerSampleXMLPaths(schema, "vehicle.cylindered.sounds", "actionSound(?)") |
66 | schema:register(XMLValueType.STRING, "vehicle.cylindered.sounds.actionSound(?)#actionNames", "Target actions on given nodes") |
67 | schema:register(XMLValueType.STRING, "vehicle.cylindered.sounds.actionSound(?)#nodes", "Nodes that can activate this sound on given action events") |
68 | schema:register(XMLValueType.FLOAT, "vehicle.cylindered.sounds.actionSound(?).pitch#dropOffFactor", "Factor that is applied to pitch while drop off time is active", 1) |
69 | schema:register(XMLValueType.FLOAT, "vehicle.cylindered.sounds.actionSound(?).pitch#dropOffTime", "After this time the sound will be deactivated", 0) |
70 | |
71 | local partKey = Cylindered.MOVING_PART_XML_KEY |
72 | |
73 | schema:register(XMLValueType.NODE_INDEX, partKey .. "#node", "Node") |
74 | schema:register(XMLValueType.NODE_INDEX, partKey .. "#referenceFrame", "Reference frame") |
75 | schema:register(XMLValueType.NODE_INDEX, partKey .. "#referencePoint", "Reference point") |
76 | |
77 | schema:register(XMLValueType.BOOL, partKey .. "#invertZ", "Invert Z axis", false) |
78 | schema:register(XMLValueType.BOOL, partKey .. "#scaleZ", "Allow Z axis scaling", false) |
79 | schema:register(XMLValueType.INT, partKey .. "#limitedAxis", "Limited axis") |
80 | schema:register(XMLValueType.BOOL, partKey .. "#isActiveDirty", "Part is permanently updated", false) |
81 | schema:register(XMLValueType.BOOL, partKey .. "#playSound", "Play hydraulic sound", false) |
82 | schema:register(XMLValueType.BOOL, partKey .. "#moveToReferenceFrame", "Move to reference frame", false) |
83 | schema:register(XMLValueType.BOOL, partKey .. "#doLineAlignment", "Do line alignment (line as ref point)", false) |
84 | schema:register(XMLValueType.BOOL, partKey .. "#doInversedLineAlignment", "Do inversed line alignment (line inside part and fixed ref point)", false) |
85 | schema:register(XMLValueType.FLOAT, partKey .. ".orientationLine#partLength", "Part length (Distance from part to line)", 0.5) |
86 | schema:register(XMLValueType.NODE_INDEX, partKey .. ".orientationLine#partLengthNode", "Node to measure the part length dynamically") |
87 | schema:register(XMLValueType.NODE_INDEX, partKey .. ".orientationLine.lineNode(?)#node", "Line node") |
88 | |
89 | schema:register(XMLValueType.BOOL, partKey .. "#doDirectionAlignment", "Do direction alignment", true) |
90 | schema:register(XMLValueType.BOOL, partKey .. "#doRotationAlignment", "Do rotation alignment", false) |
91 | schema:register(XMLValueType.FLOAT, partKey .. "#rotMultiplier", "Rotation multiplier for rotation alignment", 0) |
92 | |
93 | schema:register(XMLValueType.ANGLE, partKey .. "#minRot", "Min. rotation for limited axis") |
94 | schema:register(XMLValueType.ANGLE, partKey .. "#maxRot", "Max. rotation for limited axis") |
95 | |
96 | schema:register(XMLValueType.BOOL, partKey .. "#alignToWorldY", "Align part to world Y axis", false) |
97 | schema:register(XMLValueType.NODE_INDEX, partKey .. "#localReferencePoint", "Local reference point") |
98 | schema:register(XMLValueType.NODE_INDEX, partKey .. "#referenceDistancePoint", "Z translation will be used as reference distance") |
99 | schema:register(XMLValueType.FLOAT, partKey .. "#localReferenceDistance", "Predefined reference distance", "calculated automatically") |
100 | schema:register(XMLValueType.BOOL, partKey .. "#updateLocalReferenceDistance", "Update distance to local reference point", false) |
101 | schema:register(XMLValueType.BOOL, partKey .. "#dynamicLocalReferenceDistance", "Local reference distance will be calculated based on the initial distance and the localReferencePoint direction", false) |
102 | schema:register(XMLValueType.BOOL, partKey .. "#localReferenceTranslate", "Translate to local reference node", false) |
103 | schema:register(XMLValueType.FLOAT, partKey .. "#referenceDistanceThreshold", "Distance threshold to update moving part while isActiveDirty", 0) |
104 | schema:register(XMLValueType.BOOL, partKey .. "#useLocalOffset", "Use local offset", false) |
105 | schema:register(XMLValueType.FLOAT, partKey .. "#directionThreshold", "Direction threshold to update part if vehicle is inactive", 0.0001) |
106 | schema:register(XMLValueType.FLOAT, partKey .. "#directionThresholdActive", "Direction threshold to update part if vehicle is inactive", 0.0001) |
107 | schema:register(XMLValueType.STRING, partKey .. "#maxUpdateDistance", "Max. distance to vehicle root while isActiveDirty is set ('-' means unlimited)") |
108 | |
109 | schema:register(XMLValueType.BOOL, partKey .. "#smoothedDirectionScale", "If moving part is deactivated e.g. due to folding limits the direction is slowly interpolated back to the start direction depending on #smoothedDirectionTime", false) |
110 | schema:register(XMLValueType.TIME, partKey .. "#smoothedDirectionTime", "Defines how low it takes until the part is back in original direction (sec.)", 2) |
111 | |
112 | schema:register(XMLValueType.BOOL, partKey .. "#debug", "Enables debug rendering for this part", false) |
113 | |
114 | schema:register(XMLValueType.NODE_INDEX, partKey .. ".dependentPart(?)#node", "Dependent part") |
115 | schema:register(XMLValueType.STRING, partKey .. ".dependentPart(?)#maxUpdateDistance", "Max. distance to vehicle root to update dependent part ('-' means unlimited)", "-") |
116 | |
117 | schema:register(XMLValueType.BOOL, partKey .. "#divideTranslatingDistance", "If true all translating parts will move at the same time. If false they start to move in the order from the xml", true) |
118 | |
119 | schema:register(XMLValueType.NODE_INDEX, partKey .. ".translatingPart(?)#node", "Translating part") |
120 | schema:register(XMLValueType.FLOAT, partKey .. ".translatingPart(?)#referenceDistance", "Reference distance") |
121 | schema:register(XMLValueType.FLOAT, partKey .. ".translatingPart(?)#minZTrans", "Min. Z Translation") |
122 | schema:register(XMLValueType.FLOAT, partKey .. ".translatingPart(?)#maxZTrans", "Max. Z Translation") |
123 | schema:register(XMLValueType.BOOL, partKey .. ".translatingPart(?)#divideTranslatingDistance", "Define individual division per translating part. E.g. one part is extending without division and two other parts extend afterwards at the same speed.", "movingPart#divideTranslatingDistance") |
124 | |
125 | schema:register(XMLValueType.VECTOR_N, partKey .. "#wheelIndices", "List of wheel indices to update") |
126 | schema:register(XMLValueType.STRING, partKey .. "#wheelNodes", "List of wheel nodes to update") |
127 | schema:register(XMLValueType.BOOL, partKey .. ".inputAttacherJoint#value", "Update input attacher joint") |
128 | schema:register(XMLValueType.VECTOR_N, partKey .. ".attacherJoint#jointIndices", "List of attacher joints to update") |
129 | |
130 | Cylindered.registerDependentComponentJointXMLPaths(schema, partKey) |
131 | Cylindered.registerCopyLocalDirectionXMLPaths(schema, partKey) |
132 | Cylindered.registerDependentAnimationXMLPaths(schema, partKey) |
133 | |
134 | local toolKey = Cylindered.MOVING_TOOL_XML_KEY |
135 | |
136 | schema:register(XMLValueType.NODE_INDEX, toolKey .. "#node", "Node") |
137 | schema:register(XMLValueType.BOOL, toolKey .. "#isEasyControlTarget", "Is easy control target", false) |
138 | |
139 | schema:register(XMLValueType.ANGLE, toolKey .. ".rotation#rotSpeed", "Rotation speed") |
140 | schema:register(XMLValueType.ANGLE, toolKey .. ".rotation#rotAcceleration", "Rotation acceleration") |
141 | schema:register(XMLValueType.INT, toolKey .. ".rotation#rotationAxis", "Rotation axis", 1) |
142 | schema:register(XMLValueType.ANGLE, toolKey .. ".rotation#rotMax", "Max. rotation") |
143 | schema:register(XMLValueType.ANGLE, toolKey .. ".rotation#rotMin", "Min. rotation") |
144 | schema:register(XMLValueType.ANGLE, toolKey .. ".rotation#startRot", "Start rotation") |
145 | schema:register(XMLValueType.BOOL, toolKey .. ".rotation#syncMaxRotLimits", "Synchronize max. rotation limits", false) |
146 | schema:register(XMLValueType.BOOL, toolKey .. ".rotation#syncMinRotLimits", "Synchronize min. rotation limits", false) |
147 | schema:register(XMLValueType.INT, toolKey .. ".rotation#rotSendNumBits", "Number of bits to synchronize", 8) |
148 | schema:register(XMLValueType.ANGLE, toolKey .. ".rotation#attachRotMax", "Max. rotation value set during attach") |
149 | schema:register(XMLValueType.ANGLE, toolKey .. ".rotation#attachRotMin", "Min. rotation value set during attach") |
150 | schema:register(XMLValueType.ANGLE, toolKey .. ".rotation#detachingRotMaxLimit", "Max. rotation to detach vehicle") |
151 | schema:register(XMLValueType.ANGLE, toolKey .. ".rotation#detachingRotMinLimit", "Min. rotation to detach vehicle") |
152 | |
153 | schema:register(XMLValueType.FLOAT, toolKey .. ".translation#transSpeed", "Translation speed") |
154 | schema:register(XMLValueType.FLOAT, toolKey .. ".translation#transAcceleration", "Translation acceleration") |
155 | schema:register(XMLValueType.INT, toolKey .. ".translation#translationAxis", "Translation axis") |
156 | schema:register(XMLValueType.FLOAT, toolKey .. ".translation#transMax", "Max. translation") |
157 | schema:register(XMLValueType.FLOAT, toolKey .. ".translation#transMin", "Min. translation") |
158 | schema:register(XMLValueType.FLOAT, toolKey .. ".translation#startTrans", "Start translation") |
159 | schema:register(XMLValueType.FLOAT, toolKey .. ".translation#attachTransMax", "Max. translation value set during attach") |
160 | schema:register(XMLValueType.FLOAT, toolKey .. ".translation#attachTransMin", "Min. translation value set during attach") |
161 | schema:register(XMLValueType.FLOAT, toolKey .. ".translation#detachingTransMaxLimit", "Max. translation to detach vehicle") |
162 | schema:register(XMLValueType.FLOAT, toolKey .. ".translation#detachingTransMinLimit", "Min. translation to detach vehicle") |
163 | |
164 | schema:register(XMLValueType.BOOL, toolKey .. "#playSound", "Play sound", false) |
165 | |
166 | schema:register(XMLValueType.STRING, toolKey .. ".animation#animName", "Animation name") |
167 | schema:register(XMLValueType.FLOAT, toolKey .. ".animation#animSpeed", "Animation speed") |
168 | schema:register(XMLValueType.FLOAT, toolKey .. ".animation#animAcceleration", "Animation acceleration") |
169 | schema:register(XMLValueType.INT, toolKey .. ".animation#animSendNumBits", "Number of bits to synchronize", 8) |
170 | schema:register(XMLValueType.FLOAT, toolKey .. ".animation#animMaxTime", "Animation max. time", 1) |
171 | schema:register(XMLValueType.FLOAT, toolKey .. ".animation#animMinTime", "Animation min. time", 0) |
172 | schema:register(XMLValueType.FLOAT, toolKey .. ".animation#animStartTime", "Animation start time") |
173 | |
174 | schema:register(XMLValueType.STRING, toolKey .. ".controls#iconName", "Icon identifier") |
175 | schema:register(XMLValueType.INT, toolKey .. ".controls#groupIndex", "Control group index", 0) |
176 | schema:register(XMLValueType.STRING, toolKey .. ".controls#axis", "Input action name") |
177 | schema:register(XMLValueType.BOOL, toolKey .. ".controls#invertAxis", "Invert input axis", false) |
178 | schema:register(XMLValueType.FLOAT, toolKey .. ".controls#mouseSpeedFactor", "Mouse speed factor", 1) |
179 | schema:register(XMLValueType.BOOL, toolKey .. "#allowSaving", "Allow saving", true) |
180 | |
181 | schema:register(XMLValueType.BOOL, toolKey .. "#isIntitialDirty", "Is initial dirty", true) |
182 | schema:register(XMLValueType.NODE_INDEX, toolKey .. "#delayedNode", "Delayed node") |
183 | schema:register(XMLValueType.INT, toolKey .. "#delayedFrames", "Delayed frames", 3) |
184 | |
185 | schema:register(XMLValueType.BOOL, toolKey .. "#isConsumingPower", "While tool is moving the power consumer is set active", false) |
186 | |
187 | schema:register(XMLValueType.NODE_INDEX, toolKey .. ".dependentPart(?)#node", "Dependent part") |
188 | schema:register(XMLValueType.STRING, toolKey .. ".dependentPart(?)#maxUpdateDistance", "Max. distance to vehicle root to update dependent part ('-' means unlimited)", "-") |
189 | |
190 | schema:register(XMLValueType.VECTOR_N, toolKey .. "#wheelIndices", "List of wheel indices to update") |
191 | schema:register(XMLValueType.STRING, toolKey .. "#wheelNodes", "List of wheel nodes to update") |
192 | schema:register(XMLValueType.BOOL, toolKey .. ".inputAttacherJoint#value", "Update input attacher joint") |
193 | schema:register(XMLValueType.VECTOR_N, toolKey .. ".attacherJoint#jointIndices", "List of attacher joints to update") |
194 | |
195 | schema:register(XMLValueType.INT, toolKey .. "#fillUnitIndex", "Fill unit index") |
196 | schema:register(XMLValueType.FLOAT, toolKey .. "#minFillLevel", "Min. fill level") |
197 | schema:register(XMLValueType.FLOAT, toolKey .. "#maxFillLevel", "Max. fill level") |
198 | |
199 | schema:register(XMLValueType.FLOAT, toolKey .. "#foldMinLimit", "Min. fold time", 0) |
200 | schema:register(XMLValueType.FLOAT, toolKey .. "#foldMaxLimit", "Max. fold time", 1) |
201 | |
202 | Cylindered.registerDependentComponentJointXMLPaths(schema, toolKey) |
203 | Cylindered.registerDependentAnimationXMLPaths(schema, toolKey) |
204 | |
205 | schema:register(XMLValueType.NODE_INDEX, toolKey .. ".dependentMovingTool(?)#node", "Dependent part") |
206 | schema:register(XMLValueType.FLOAT, toolKey .. ".dependentMovingTool(?)#speedScale", "Speed scale") |
207 | schema:register(XMLValueType.BOOL, toolKey .. ".dependentMovingTool(?)#requiresMovement", "Requires movement", false) |
208 | schema:register(XMLValueType.ANGLE, toolKey .. ".dependentMovingTool(?).rotationBasedLimits.limit(?)#rotation", "Rotation") |
209 | schema:register(XMLValueType.ANGLE, toolKey .. ".dependentMovingTool(?).rotationBasedLimits.limit(?)#rotMin", "Min. rotation") |
210 | schema:register(XMLValueType.ANGLE, toolKey .. ".dependentMovingTool(?).rotationBasedLimits.limit(?)#rotMax", "Max. rotation") |
211 | schema:register(XMLValueType.FLOAT, toolKey .. ".dependentMovingTool(?).rotationBasedLimits.limit(?)#transMin", "Min. translation") |
212 | schema:register(XMLValueType.FLOAT, toolKey .. ".dependentMovingTool(?).rotationBasedLimits.limit(?)#transMax", "Max. translation") |
213 | schema:register(XMLValueType.VECTOR_2, toolKey .. ".dependentMovingTool(?)#minTransLimits", "Min. translation limits") |
214 | schema:register(XMLValueType.VECTOR_2, toolKey .. ".dependentMovingTool(?)#maxTransLimits", "Max. translation limits") |
215 | schema:register(XMLValueType.VECTOR_ROT_2, toolKey .. ".dependentMovingTool(?)#minRotLimits", "Min. rotation limits") |
216 | schema:register(XMLValueType.VECTOR_ROT_2, toolKey .. ".dependentMovingTool(?)#maxRotLimits", "Max. rotation limits") |
217 | |
218 | schema:register(XMLValueType.L10N_STRING, "vehicle.cylindered.movingTools.controlGroups.controlGroup(?)#name", "Control group name") |
219 | |
220 | schema:register(XMLValueType.NODE_INDEX, "vehicle.cylindered.movingTools.easyArmControl#rootNode", "Root node") |
221 | schema:register(XMLValueType.NODE_INDEX, "vehicle.cylindered.movingTools.easyArmControl#node", "Node") |
222 | schema:register(XMLValueType.NODE_INDEX, "vehicle.cylindered.movingTools.easyArmControl#targetNodeZ", "Z target node") |
223 | schema:register(XMLValueType.NODE_INDEX, "vehicle.cylindered.movingTools.easyArmControl#refNode", "Reference node") |
224 | schema:register(XMLValueType.FLOAT, "vehicle.cylindered.movingTools.easyArmControl#maxTotalDistance", "Max. total distance the arms can move from rootNode", "automatically calculated") |
225 | schema:register(XMLValueType.FLOAT, "vehicle.cylindered.movingTools.easyArmControl.targetMovement#speed", "Target node move speed", 1) |
226 | schema:register(XMLValueType.FLOAT, "vehicle.cylindered.movingTools.easyArmControl.targetMovement#acceleration", "Target node move acceleration", 50) |
227 | schema:register(XMLValueType.FLOAT, "vehicle.cylindered.movingTools.easyArmControl.zTranslationNodes#minMoveRatio", "Min. ratio between translation and rotation movement [0: only rotation, 1: only translation]", 0.2) |
228 | schema:register(XMLValueType.FLOAT, "vehicle.cylindered.movingTools.easyArmControl.zTranslationNodes#maxMoveRatio", "Max. ratio between translation and rotation movement [0: only rotation, 1: only translation]", 0.8) |
229 | schema:register(XMLValueType.FLOAT, "vehicle.cylindered.movingTools.easyArmControl.zTranslationNodes#moveRatioMinDir", "Defines direction value when the translation parts start to move", 0.0) |
230 | schema:register(XMLValueType.FLOAT, "vehicle.cylindered.movingTools.easyArmControl.zTranslationNodes#moveRatioMaxDir", "Defines direction value when the rotation parts stop to move", 1.0) |
231 | schema:register(XMLValueType.BOOL, "vehicle.cylindered.movingTools.easyArmControl.zTranslationNodes#allowNegativeTrans", "Allow translation movement if translation parts are pointing towards the root node", false) |
232 | schema:register(XMLValueType.FLOAT, "vehicle.cylindered.movingTools.easyArmControl.zTranslationNodes#minNegativeTrans", "Min. translation percentage when moving the translation parts into negative direction while they are pointing towards the root node", 0) |
233 | schema:register(XMLValueType.NODE_INDEX, "vehicle.cylindered.movingTools.easyArmControl.zTranslationNodes.zTranslationNode(?)#node", "Z translation node") |
234 | schema:register(XMLValueType.NODE_INDEX, "vehicle.cylindered.movingTools.easyArmControl.xRotationNodes.xRotationNode1#node", "X translation node") |
235 | schema:register(XMLValueType.NODE_INDEX, "vehicle.cylindered.movingTools.easyArmControl.xRotationNodes.xRotationNode2#node", "X translation node") |
236 | |
237 | Dashboard.registerDashboardXMLPaths(schema, "vehicle.cylindered.dashboards", "movingTool") |
238 | schema:register(XMLValueType.STRING, "vehicle.cylindered.dashboards.dashboard(?)#axis", "Moving tool input action name") |
239 | schema:register(XMLValueType.INT, "vehicle.cylindered.dashboards.dashboard(?)#attacherJointIndex", "Index of attacher joint that has to be connected") |
240 | schema:register(XMLValueType.NODE_INDEX, "vehicle.cylindered.dashboards.dashboard(?)#attacherJointNode", "Node of attacher joint that has to be connected") |
241 | |
242 | ObjectChangeUtil.addAdditionalObjectChangeXMLPaths(schema, function(_schema, key) |
243 | _schema:register(XMLValueType.ANGLE, key .. "#movingToolRotMaxActive", "Moving tool max. rotation if object change active") |
244 | _schema:register(XMLValueType.ANGLE, key .. "#movingToolRotMaxInactive", "Moving tool max. rotation if object change inactive") |
245 | _schema:register(XMLValueType.ANGLE, key .. "#movingToolRotMinActive", "Moving tool min. rotation if object change active") |
246 | _schema:register(XMLValueType.ANGLE, key .. "#movingToolRotMinInactive", "Moving tool min. rotation if object change inactive") |
247 | |
248 | _schema:register(XMLValueType.FLOAT, key .. "#movingToolTransMaxActive", "Moving tool max. translation if object change active") |
249 | _schema:register(XMLValueType.FLOAT, key .. "#movingToolTransMaxInactive", "Moving tool max. translation if object change inactive") |
250 | _schema:register(XMLValueType.FLOAT, key .. "#movingToolTransMinActive", "Moving tool min. translation if object change active") |
251 | _schema:register(XMLValueType.FLOAT, key .. "#movingToolTransMinInactive", "Moving tool min. translation if object change inactive") |
252 | |
253 | _schema:register(XMLValueType.BOOL, key .. "#movingPartUpdateActive", "moving part active state if object change active") |
254 | _schema:register(XMLValueType.BOOL, key .. "#movingPartUpdateInactive", "moving part active state if object change inactive") |
255 | end) |
256 | |
257 | schema:register(XMLValueType.NODE_INDEX, Dischargeable.DISCHARGE_NODE_XML_PATH .. ".movingToolActivation#node", "Moving tool node") |
258 | schema:register(XMLValueType.BOOL, Dischargeable.DISCHARGE_NODE_XML_PATH .. ".movingToolActivation#isInverted", "Activation is inverted", false) |
259 | schema:register(XMLValueType.FLOAT, Dischargeable.DISCHARGE_NODE_XML_PATH .. ".movingToolActivation#openFactor", "Open factor", 1) |
260 | schema:register(XMLValueType.FLOAT, Dischargeable.DISCHARGE_NODE_XML_PATH .. ".movingToolActivation#openOffset", "Open offset", 0) |
261 | |
262 | schema:register(XMLValueType.NODE_INDEX, Dischargeable.DISCHARGE_NODE_CONFIG_XML_PATH .. ".movingToolActivation#node", "Moving tool node") |
263 | schema:register(XMLValueType.BOOL, Dischargeable.DISCHARGE_NODE_CONFIG_XML_PATH .. ".movingToolActivation#isInverted", "Activation is inverted", false) |
264 | schema:register(XMLValueType.FLOAT, Dischargeable.DISCHARGE_NODE_CONFIG_XML_PATH .. ".movingToolActivation#openFactor", "Open factor", 1) |
265 | schema:register(XMLValueType.FLOAT, Dischargeable.DISCHARGE_NODE_CONFIG_XML_PATH .. ".movingToolActivation#openOffset", "Open offset", 0) |
266 | |
267 | schema:register(XMLValueType.NODE_INDEX, Shovel.SHOVEL_NODE_XML_KEY .. ".movingToolActivation#node", "Moving tool node") |
268 | schema:register(XMLValueType.BOOL, Shovel.SHOVEL_NODE_XML_KEY .. ".movingToolActivation#isInverted", "Activation is inverted", false) |
269 | schema:register(XMLValueType.FLOAT, Shovel.SHOVEL_NODE_XML_KEY .. ".movingToolActivation#openFactor", "Open factor", 1) |
270 | |
271 | schema:register(XMLValueType.NODE_INDEX, DynamicMountAttacher.DYNAMIC_MOUNT_GRAB_XML_PATH .. ".movingToolActivation#node", "Moving tool node") |
272 | schema:register(XMLValueType.BOOL, DynamicMountAttacher.DYNAMIC_MOUNT_GRAB_XML_PATH .. ".movingToolActivation#isInverted", "Activation is inverted", false) |
273 | schema:register(XMLValueType.FLOAT, DynamicMountAttacher.DYNAMIC_MOUNT_GRAB_XML_PATH .. ".movingToolActivation#openFactor", "Open factor", 1) |
274 | |
275 | schema:setXMLSpecializationType() |
276 | |
277 | local schemaSavegame = Vehicle.xmlSchemaSavegame |
278 | schemaSavegame:register(XMLValueType.FLOAT, "vehicles.vehicle(?).cylindered.movingTool(?)#translation", "Current translation value") |
279 | schemaSavegame:register(XMLValueType.ANGLE, "vehicles.vehicle(?).cylindered.movingTool(?)#rotation", "Current rotation in rad") |
280 | schemaSavegame:register(XMLValueType.FLOAT, "vehicles.vehicle(?).cylindered.movingTool(?)#animationTime", "Current animation time") |
281 | end |
isDetachAllowed
DescriptionReturns true if detach is allowedDefinition
isDetachAllowed()Return Values
boolean | detachAllowed | detach is allowed |
2940 | function Cylindered:isDetachAllowed(superFunc) |
2941 | local spec = self.spec_cylindered |
2942 | if spec.detachLockNodes ~= nil then |
2943 | for entry, data in pairs(spec.detachLockNodes) do |
2944 | local node = entry.node |
2945 | local rot = {getRotation(node)} |
2946 | |
2947 | if data.detachingRotMinLimit ~= nil and rot[entry.rotationAxis] < data.detachingRotMinLimit then |
2948 | return false, nil |
2949 | end |
2950 | if data.detachingRotMaxLimit ~= nil and rot[entry.rotationAxis] > data.detachingRotMaxLimit then |
2951 | return false, nil |
2952 | end |
2953 | |
2954 | local trans = {getTranslation(node)} |
2955 | if data.detachingTransMinLimit ~= nil and trans[entry.translationAxis] < data.detachingTransMinLimit then |
2956 | return false, nil |
2957 | end |
2958 | if data.detachingTransMaxLimit ~= nil and trans[entry.translationAxis] > data.detachingTransMaxLimit then |
2959 | return false, nil |
2960 | end |
2961 | end |
2962 | end |
2963 | |
2964 | return superFunc(self) |
2965 | end |
limitInterpolator
DescriptionDefinitionlimitInterpolator()Code
4052 | function Cylindered.limitInterpolator(first, second, alpha) |
4053 | local oneMinusAlpha = 1-alpha |
4054 | |
4055 | local rotMin = nil |
4056 | local rotMax = nil |
4057 | local transMin = nil |
4058 | local transMax = nil |
4059 | |
4060 | if first.rotMin ~= nil and second.rotMin ~= nil then |
4061 | rotMin = first.rotMin*alpha + second.rotMin*oneMinusAlpha |
4062 | end |
4063 | if first.rotMax ~= nil and second.rotMax ~= nil then |
4064 | rotMax = first.rotMax*alpha + second.rotMax*oneMinusAlpha |
4065 | end |
4066 | if first.transMin ~= nil and second.transMin ~= nil then |
4067 | transMin = first.minTrans*alpha + second.transMin*oneMinusAlpha |
4068 | end |
4069 | if first.transMax ~= nil and second.transMax ~= nil then |
4070 | transMax = first.transMax*alpha + second.transMax*oneMinusAlpha |
4071 | end |
4072 | |
4073 | return rotMin, rotMax, transMin, transMax |
4074 | end |
loadCopyLocalDirectionParts
DescriptionLoad copy local direction parts from xmlDefinition
loadCopyLocalDirectionParts(integer xmlFile, string baseName, table entry)Arguments
integer | xmlFile | id of xml object |
string | baseName | base name |
table | entry | entry to add |
2744 | function Cylindered:loadCopyLocalDirectionParts(xmlFile, baseName, entry) |
2745 | entry.copyLocalDirectionParts = {} |
2746 | local j = 0 |
2747 | while true do |
2748 | local refBaseName = baseName..string.format(".copyLocalDirectionPart(%d)", j) |
2749 | if not xmlFile:hasProperty(refBaseName) then |
2750 | break |
2751 | end |
2752 | |
2753 | XMLUtil.checkDeprecatedXMLElements(xmlFile, refBaseName.."#index", refBaseName.."#node") --FS15 to FS17 |
2754 | |
2755 | local node = xmlFile:getValue(refBaseName.."#node", nil, self.components, self.i3dMappings) |
2756 | if node ~= nil then |
2757 | local copyLocalDirectionPart = {} |
2758 | copyLocalDirectionPart.node = node |
2759 | copyLocalDirectionPart.dirScale = xmlFile:getValue(refBaseName.."#dirScale", nil, true) |
2760 | copyLocalDirectionPart.upScale = xmlFile:getValue(refBaseName.."#upScale", nil, true) |
2761 | |
2762 | self:loadDependentComponentJoints(xmlFile, refBaseName, copyLocalDirectionPart) |
2763 | |
2764 | table.insert(entry.copyLocalDirectionParts, copyLocalDirectionPart) |
2765 | end |
2766 | j = j + 1 |
2767 | end |
2768 | end |
loadDependentAnimations
DescriptionDefinitionloadDependentAnimations()Code
2696 | function Cylindered:loadDependentAnimations(xmlFile, baseName, entry) |
2697 | entry.dependentAnimations = {} |
2698 | |
2699 | local i = 0 |
2700 | while true do |
2701 | local baseKey = string.format("%s.dependentAnimation(%d)", baseName, i) |
2702 | if not xmlFile:hasProperty(baseKey) then |
2703 | break |
2704 | end |
2705 | |
2706 | local animationName = xmlFile:getValue(baseKey.."#name") |
2707 | if animationName ~= nil then |
2708 | local dependentAnimation = {} |
2709 | dependentAnimation.name = animationName |
2710 | dependentAnimation.lastPos = 0 |
2711 | |
2712 | dependentAnimation.translationAxis = xmlFile:getValue(baseKey.."#translationAxis") |
2713 | dependentAnimation.rotationAxis = xmlFile:getValue(baseKey.."#rotationAxis") |
2714 | |
2715 | dependentAnimation.node = entry.node |
2716 | local useTranslatingPartIndex = xmlFile:getValue(baseKey.."#useTranslatingPartIndex") |
2717 | if useTranslatingPartIndex ~= nil then |
2718 | if entry.translatingParts[useTranslatingPartIndex] ~= nil then |
2719 | dependentAnimation.node = entry.translatingParts[useTranslatingPartIndex].node |
2720 | end |
2721 | end |
2722 | |
2723 | dependentAnimation.minValue = xmlFile:getValue(baseKey.."#minValue") |
2724 | dependentAnimation.maxValue = xmlFile:getValue(baseKey.."#maxValue") |
2725 | if dependentAnimation.rotationAxis ~= nil then |
2726 | dependentAnimation.minValue = MathUtil.degToRad(dependentAnimation.minValue) |
2727 | dependentAnimation.maxValue = MathUtil.degToRad(dependentAnimation.maxValue) |
2728 | end |
2729 | |
2730 | dependentAnimation.invert = xmlFile:getValue(baseKey.."#invert", false) |
2731 | |
2732 | table.insert(entry.dependentAnimations, dependentAnimation) |
2733 | end |
2734 | |
2735 | i = i + 1 |
2736 | end |
2737 | end |
loadDependentAttacherJoints
DescriptionLoad attacher joints from xmlDefinition
loadDependentAttacherJoints(integer xmlFile, string baseName, table entry)Arguments
integer | xmlFile | id of xml object |
string | baseName | base name |
table | entry | entry to add |
2580 | function Cylindered:loadDependentAttacherJoints(xmlFile, baseName, entry) |
2581 | XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName.."#jointIndices", baseName..".attacherJoint#jointIndices") --FS15 to FS17 |
2582 | |
2583 | local indices = xmlFile:getValue(baseName.. ".attacherJoint#jointIndices", nil, true) |
2584 | if indices ~= nil then |
2585 | entry.attacherJoints = {} |
2586 | |
2587 | local availableAttacherJoints |
2588 | if self.getAttacherJoints ~= nil then |
2589 | availableAttacherJoints = self:getAttacherJoints() |
2590 | end |
2591 | if availableAttacherJoints ~= nil then |
2592 | for i=1, #indices do |
2593 | if availableAttacherJoints[indices[i]] ~= nil then |
2594 | table.insert(entry.attacherJoints, availableAttacherJoints[indices[i]]) |
2595 | else |
2596 | Logging.xmlWarning(xmlFile, "Invalid attacher joint index '%s' for '%s'!", indices[i], baseName) |
2597 | end |
2598 | end |
2599 | end |
2600 | end |
2601 | |
2602 | XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName.."#inputAttacherJoint", baseName..".inputAttacherJoint#value") --FS15 to FS17 |
2603 | |
2604 | entry.inputAttacherJoint = xmlFile:getValue(baseName.. ".inputAttacherJoint#value", false) |
2605 | end |
loadDependentComponentJoints
DescriptionLoad component joints from xmlDefinition
loadDependentComponentJoints(integer xmlFile, string baseName, table entry)Arguments
integer | xmlFile | id of xml object |
string | baseName | base name |
table | entry | entry to add |
2529 | function Cylindered:loadDependentComponentJoints(xmlFile, baseName, entry) |
2530 | if not self.isServer then |
2531 | return |
2532 | end |
2533 | |
2534 | entry.componentJoints = {} |
2535 | |
2536 | XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName.."#componentJointIndex", baseName..".componentJoint#index") --FS15 to FS17 |
2537 | XMLUtil.checkDeprecatedXMLElements(xmlFile, baseName.."#anchorActor", baseName..".componentJoint#anchorActor") --FS15 to FS17 |
2538 | |
2539 | local i = 0 |
2540 | while true do |
2541 | local key = baseName .. string.format(".componentJoint(%d)", i) |
2542 | if not xmlFile:hasProperty(key) then |
2543 | break |
2544 | end |
2545 | local index = xmlFile:getValue(key .. "#index") |
2546 | if index ~= nil and self.componentJoints[index] ~= nil then |
2547 | local anchorActor = xmlFile:getValue(key.."#anchorActor", 0) |
2548 | |
2549 | local componentJoint = self.componentJoints[index] |
2550 | |
2551 | local jointEntry = {} |
2552 | jointEntry.componentJoint = componentJoint |
2553 | jointEntry.anchorActor = anchorActor |
2554 | jointEntry.index = index |
2555 | |
2556 | local jointNode = componentJoint.jointNode |
2557 | if jointEntry.anchorActor == 1 then |
2558 | jointNode = componentJoint.jointNodeActor1 |
2559 | end |
2560 | |
2561 | local node = self.components[componentJoint.componentIndices[2]].node |
2562 | jointEntry.x, jointEntry.y, jointEntry.z = localToLocal(node, jointNode, 0,0,0) |
2563 | jointEntry.upX, jointEntry.upY, jointEntry.upZ = localDirectionToLocal(node, jointNode, 0,1,0) |
2564 | jointEntry.dirX, jointEntry.dirY, jointEntry.dirZ = localDirectionToLocal(node, jointNode, 0,0,1) |
2565 | |
2566 | table.insert(entry.componentJoints, jointEntry) |
2567 | else |
2568 | Logging.xmlWarning(xmlFile, "Invalid index for '%s'", key) |
2569 | end |
2570 | |
2571 | i = i + 1 |
2572 | end |
2573 | end |
loadDependentMovingTools
DescriptionLoad dependent moving tools from xmlDefinition
loadDependentMovingTools(integer xmlFile, string baseName, table entry)Arguments
integer | xmlFile | id of xml object |
string | baseName | base name |
table | entry | entry to add |
2404 | function Cylindered:loadDependentMovingTools(xmlFile, baseName, entry) |
2405 | entry.dependentMovingTools = {} |
2406 | |
2407 | local j = 0 |
2408 | while true do |
2409 | local refBaseName = baseName..string.format(".dependentMovingTool(%d)", j) |
2410 | if not xmlFile:hasProperty(refBaseName) then |
2411 | break |
2412 | end |
2413 | |
2414 | XMLUtil.checkDeprecatedXMLElements(xmlFile, refBaseName.."#index", refBaseName.."#index") --FS17 to FS19 |
2415 | |
2416 | local node = xmlFile:getValue(refBaseName.."#node", nil, self.components, self.i3dMappings) |
2417 | local speedScale = xmlFile:getValue(refBaseName.."#speedScale") |
2418 | local requiresMovement = xmlFile:getValue(refBaseName.."#requiresMovement", false) |
2419 | |
2420 | local rotationBasedLimits = AnimCurve.new(Cylindered.limitInterpolator) |
2421 | local found = false |
2422 | local i = 0 |
2423 | while true do |
2424 | local key = string.format("%s.limit(%d)", refBaseName..".rotationBasedLimits", i) |
2425 | if not xmlFile:hasProperty(key) then |
2426 | break |
2427 | end |
2428 | |
2429 | local keyFrame = self:loadRotationBasedLimits(xmlFile, key, entry) |
2430 | if keyFrame ~= nil then |
2431 | rotationBasedLimits:addKeyframe(keyFrame) |
2432 | found = true |
2433 | end |
2434 | i = i + 1 |
2435 | end |
2436 | if not found then |
2437 | rotationBasedLimits = nil |
2438 | end |
2439 | |
2440 | local minTransLimits = xmlFile:getValue(refBaseName.."#minTransLimits", nil, true) |
2441 | local maxTransLimits = xmlFile:getValue(refBaseName.."#maxTransLimits", nil, true) |
2442 | local minRotLimits = xmlFile:getValue(refBaseName.."#minRotLimits", nil, true) |
2443 | local maxRotLimits = xmlFile:getValue(refBaseName.."#maxRotLimits", nil, true) |
2444 | if node ~= nil and (rotationBasedLimits ~= nil or speedScale ~= nil or minTransLimits ~= nil or maxTransLimits ~= nil or minRotLimits ~= nil or maxRotLimits ~= nil) then |
2445 | local dependentTool = {} |
2446 | dependentTool.node = node |
2447 | dependentTool.rotationBasedLimits = rotationBasedLimits |
2448 | dependentTool.speedScale = speedScale |
2449 | dependentTool.requiresMovement = requiresMovement |
2450 | dependentTool.minTransLimits = minTransLimits |
2451 | dependentTool.maxTransLimits = maxTransLimits |
2452 | dependentTool.minRotLimits = minRotLimits |
2453 | dependentTool.maxRotLimits = maxRotLimits |
2454 | table.insert(entry.dependentMovingTools, dependentTool) |
2455 | end |
2456 | |
2457 | j = j + 1 |
2458 | end |
2459 | end |
loadDependentParts
DescriptionLoad dependent parts from xmlDefinition
loadDependentParts(integer xmlFile, string baseName, table entry)Arguments
integer | xmlFile | id of xml object |
string | baseName | base name |
table | entry | entry to add |
2466 | function Cylindered:loadDependentParts(xmlFile, baseName, entry) |
2467 | entry.dependentPartData = {} |
2468 | |
2469 | xmlFile:iterate(baseName .. ".dependentPart", function(_, key) |
2470 | XMLUtil.checkDeprecatedXMLElements(xmlFile, key.."#index", key.."#node") --FS17 to FS19 |
2471 | |
2472 | local dependentPart = {} |
2473 | dependentPart.node = xmlFile:getValue(key.."#node", nil, self.components, self.i3dMappings) |
2474 | if dependentPart.node ~= nil then |
2475 | dependentPart.maxUpdateDistance = xmlFile:getValue(key.."#maxUpdateDistance", "-") |
2476 | if dependentPart.maxUpdateDistance == "-" then |
2477 | dependentPart.maxUpdateDistance = math.huge |
2478 | else |
2479 | dependentPart.maxUpdateDistance = tonumber(dependentPart.maxUpdateDistance) |
2480 | end |
2481 | |
2482 | dependentPart.part = nil |
2483 | |
2484 | table.insert(entry.dependentPartData, dependentPart) |
2485 | end |
2486 | end) |
2487 | end |
loadDependentTranslatingParts
DescriptionLoad translating partsDefinition
loadDependentTranslatingParts(integer xmlFile, string baseName, table entry)Arguments
integer | xmlFile | id of xml object |
string | baseName | base name |
table | entry | entry to add |
2647 | function Cylindered:loadDependentTranslatingParts(xmlFile, baseName, entry) |
2648 | entry.translatingParts = {} |
2649 | if entry.referencePoint ~= nil then |
2650 | entry.divideTranslatingDistance = xmlFile:getValue(baseName .. "#divideTranslatingDistance", true) |
2651 | entry.translatingPartsDivider = 0 |
2652 | |
2653 | local j = 0 |
2654 | while true do |
2655 | local refBaseName = baseName..string.format(".translatingPart(%d)", j) |
2656 | if not xmlFile:hasProperty(refBaseName) then |
2657 | break |
2658 | end |
2659 | |
2660 | XMLUtil.checkDeprecatedXMLElements(xmlFile, refBaseName.."#index", refBaseName.."#node") --FS15 to FS17 |
2661 | |
2662 | local node = xmlFile:getValue(refBaseName.."#node", nil, self.components, self.i3dMappings) |
2663 | if node ~= nil then |
2664 | local transEntry = {} |
2665 | transEntry.node = node |
2666 | local x, y, z = getTranslation(node) |
2667 | transEntry.startPos = {x, y, z} |
2668 | transEntry.lastZ = z |
2669 | local _, _, refZ = worldToLocal(node, getWorldTranslation(entry.referencePoint)) |
2670 | transEntry.referenceDistance = xmlFile:getValue(refBaseName.."#referenceDistance", refZ) |
2671 | |
2672 | transEntry.minZTrans = xmlFile:getValue(refBaseName.."#minZTrans") |
2673 | transEntry.maxZTrans = xmlFile:getValue(refBaseName.."#maxZTrans") |
2674 | |
2675 | transEntry.divideTranslatingDistance = xmlFile:getValue(refBaseName .. "#divideTranslatingDistance", entry.divideTranslatingDistance) |
2676 | if transEntry.divideTranslatingDistance then |
2677 | entry.translatingPartsDivider = entry.translatingPartsDivider + 1 |
2678 | end |
2679 | |
2680 | table.insert(entry.translatingParts, transEntry) |
2681 | end |
2682 | |
2683 | j = j + 1 |
2684 | end |
2685 | end |
2686 | end |
loadDependentWheels
DescriptionLoad wheels from xmlDefinition
loadDependentWheels(integer xmlFile, string baseName, table entry)Arguments
integer | xmlFile | id of xml object |
string | baseName | base name |
table | entry | entry to add |
2612 | function Cylindered:loadDependentWheels(xmlFile, baseName, entry) |
2613 | if SpecializationUtil.hasSpecialization(Wheels, self.specializations) then |
2614 | local indices = xmlFile:getValue(baseName.. "#wheelIndices", nil, true) |
2615 | if indices ~= nil then |
2616 | entry.wheels = {} |
2617 | for _,wheelIndex in pairs(indices) do |
2618 | local wheel = self:getWheelFromWheelIndex(wheelIndex) |
2619 | if wheel ~= nil then |
2620 | table.insert(entry.wheels, wheel) |
2621 | else |
2622 | Logging.xmlWarning(xmlFile, "Invalid wheelIndex '%s' for '%s'!", wheelIndex, baseName) |
2623 | end |
2624 | end |
2625 | end |
2626 | |
2627 | local wheelNodesStr = xmlFile:getValue(baseName.. "#wheelNodes") |
2628 | if wheelNodesStr ~= nil and wheelNodesStr ~= "" then |
2629 | local wheelNodes = wheelNodesStr:split(" ") |
2630 | for i=1, #wheelNodes do |
2631 | local wheel = self:getWheelByWheelNode(wheelNodes[i]) |
2632 | if wheel ~= nil then |
2633 | table.insert(entry.wheels, wheel) |
2634 | else |
2635 | Logging.xmlWarning(xmlFile, "Invalid wheelNode '%s' for '%s'!", wheelNodes[i], baseName) |
2636 | end |
2637 | end |
2638 | end |
2639 | end |
2640 | end |
loadDischargeNode
DescriptionDefinitionloadDischargeNode()Code
3029 | function Cylindered:loadDischargeNode(superFunc, xmlFile, key, entry) |
3030 | if not superFunc(self, xmlFile, key, entry) then |
3031 | return false |
3032 | end |
3033 | |
3034 | local baseKey = key .. ".movingToolActivation" |
3035 | |
3036 | if xmlFile:hasProperty(baseKey) then |
3037 | entry.movingToolActivation = {} |
3038 | entry.movingToolActivation.node = xmlFile:getValue(baseKey.."#node", nil, self.components, self.i3dMappings) |
3039 | entry.movingToolActivation.isInverted = xmlFile:getValue(baseKey.."#isInverted", false) |
3040 | entry.movingToolActivation.openFactor = xmlFile:getValue(baseKey.."#openFactor", 1) |
3041 | entry.movingToolActivation.openOffset = xmlFile:getValue(baseKey.."#openOffset", 0) |
3042 | entry.movingToolActivation.openOffsetInv = 1-entry.movingToolActivation.openOffset |
3043 | end |
3044 | |
3045 | return true |
3046 | end |
loadDynamicMountGrabFromXML
DescriptionDefinitionloadDynamicMountGrabFromXML()Code
3114 | function Cylindered:loadDynamicMountGrabFromXML(superFunc, xmlFile, key, entry) |
3115 | if not superFunc(self, xmlFile, key, entry) then |
3116 | return false |
3117 | end |
3118 | |
3119 | local baseKey = key .. ".movingToolActivation" |
3120 | if not xmlFile:hasProperty(baseKey) then |
3121 | return true |
3122 | end |
3123 | |
3124 | entry.movingToolActivation = {} |
3125 | entry.movingToolActivation.node = xmlFile:getValue(baseKey.."#node", nil, self.components, self.i3dMappings) |
3126 | entry.movingToolActivation.isInverted = xmlFile:getValue(baseKey.."#isInverted", false) |
3127 | entry.movingToolActivation.openFactor = xmlFile:getValue(baseKey.."#openFactor", 1) |
3128 | |
3129 | return true |
3130 | end |
loadEasyArmControlFromXML
DescriptionDefinitionloadEasyArmControlFromXML()Code
1395 | function Cylindered:loadEasyArmControlFromXML(xmlFile, key, easyArmControl) |
1396 | XMLUtil.checkDeprecatedXMLElements(xmlFile, key..".xRotationNodes#maxDistance") --FS19 to FS22 |
1397 | XMLUtil.checkDeprecatedXMLElements(xmlFile, key..".xRotationNodes#transRotRatio") --FS19 to FS22 |
1398 | |
1399 | easyArmControl.rootNode = xmlFile:getValue(key.."#rootNode", nil, self.components, self.i3dMappings) |
1400 | easyArmControl.targetNodeY = xmlFile:getValue(key.."#node", nil, self.components, self.i3dMappings) |
1401 | easyArmControl.targetNodeZ = xmlFile:getValue(key.."#targetNodeZ", easyArmControl.targetNodeY, self.components, self.i3dMappings) |
1402 | easyArmControl.state = false |
1403 | |
1404 | if easyArmControl.targetNodeZ ~= nil and easyArmControl.targetNodeY ~= nil then |
1405 | local targetYTool = self:getMovingToolByNode(easyArmControl.targetNodeY) |
1406 | local targetZTool = self:getMovingToolByNode(easyArmControl.targetNodeZ) |
1407 | if targetYTool ~= nil and targetZTool ~= nil then |
1408 | easyArmControl.targetNode = easyArmControl.targetNodeZ |
1409 | if getParent(easyArmControl.targetNodeY) == easyArmControl.targetNodeZ then |
1410 | easyArmControl.targetNode = easyArmControl.targetNodeY |
1411 | end |
1412 | easyArmControl.targetRefNode = xmlFile:getValue(key.."#refNode", nil, self.components, self.i3dMappings) |
1413 | easyArmControl.lastValidPositionY = {getTranslation(easyArmControl.targetNodeY)} |
1414 | easyArmControl.lastValidPositionZ = {getTranslation(easyArmControl.targetNodeZ)} |
1415 | |
1416 | easyArmControl.moveSpeed = xmlFile:getValue(key..".targetMovement#speed", 1) / 1000 |
1417 | easyArmControl.moveAcceleration = xmlFile:getValue(key..".targetMovement#acceleration", 50) / (1000 * 1000) |
1418 | easyArmControl.lastSpeedY = 0 |
1419 | easyArmControl.lastSpeedZ = 0 |
1420 | |
1421 | easyArmControl.minTransMoveRatio = xmlFile:getValue(key..".zTranslationNodes#minMoveRatio", 0.2) |
1422 | easyArmControl.maxTransMoveRatio = xmlFile:getValue(key..".zTranslationNodes#maxMoveRatio", 0.8) |
1423 | easyArmControl.transMoveRatioMinDir = xmlFile:getValue(key..".zTranslationNodes#moveRatioMinDir", 0) |
1424 | easyArmControl.transMoveRatioMaxDir = xmlFile:getValue(key..".zTranslationNodes#moveRatioMaxDir", 1) |
1425 | easyArmControl.allowNegativeTrans = xmlFile:getValue(key..".zTranslationNodes#allowNegativeTrans", false) |
1426 | easyArmControl.minNegativeTrans = xmlFile:getValue(key..".zTranslationNodes#minNegativeTrans", 0) |
1427 | |
1428 | easyArmControl.zTranslationNodes = {} |
1429 | local maxTrans = 0 |
1430 | xmlFile:iterate(key..".zTranslationNodes.zTranslationNode", function (_, transKey) |
1431 | local node = xmlFile:getValue(transKey.."#node", nil, self.components, self.i3dMappings) |
1432 | if node ~= nil then |
1433 | local movingTool = self:getMovingToolByNode(node) |
1434 | if movingTool ~= nil then |
1435 | local maxDistance = math.abs(movingTool.transMin - movingTool.transMax) |
1436 | maxTrans = maxTrans + maxDistance |
1437 | movingTool.easyArmControlActive = false |
1438 | table.insert(easyArmControl.zTranslationNodes, {node=node, movingTool=movingTool, maxDistance=maxDistance, transFactor=0, startTranslation={getTranslation(node)}}) |
1439 | end |
1440 | end |
1441 | end) |
1442 | |
1443 | for _, translationNode in ipairs(easyArmControl.zTranslationNodes) do |
1444 | translationNode.transFactor = translationNode.maxDistance / maxTrans |
1445 | end |
1446 | |
1447 | easyArmControl.xRotationNodes = {} |
1448 | for i=1, 2 do |
1449 | local xRotKey = string.format("%s.xRotationNodes.xRotationNode%d", key, i) |
1450 | if not xmlFile:hasProperty(xRotKey) then |
1451 | Logging.xmlWarning(xmlFile, "Missing second xRotation node for easy control!") |
1452 | return false |
1453 | end |
1454 | |
1455 | XMLUtil.checkDeprecatedXMLElements(xmlFile, xRotKey.."#refNode") --FS19 to FS22 |
1456 | |
1457 | local node = xmlFile:getValue(xRotKey.."#node", nil, self.components, self.i3dMappings) |
1458 | if node ~= nil then |
1459 | local movingTool = self:getMovingToolByNode(node) |
1460 | if movingTool ~= nil then |
1461 | movingTool.easyArmControlActive = false |
1462 | table.insert(easyArmControl.xRotationNodes, {node=node, movingTool=movingTool, startRotation={getRotation(node)}}) |
1463 | end |
1464 | end |
1465 | end |
1466 | |
1467 | if easyArmControl.targetRefNode ~= nil then |
1468 | local xOffset, yOffset, _ = localToLocal(easyArmControl.targetRefNode, easyArmControl.xRotationNodes[2].node, 0, 0, 0) |
1469 | if math.abs(xOffset) > 0.0001 or math.abs(yOffset) > 0.0001 then |
1470 | Logging.xmlWarning(xmlFile, "Invalid position of '%s'. Offset to second xRotation node is not 0 on X or Y axis (x: %f y: %f)", key.."#refNode", xOffset, yOffset) |
1471 | return false |
1472 | end |
1473 | end |
1474 | |
1475 | local xOffset, yOffset, _ = localToLocal(easyArmControl.xRotationNodes[2].node, easyArmControl.xRotationNodes[1].node, 0, 0, 0) |
1476 | if math.abs(xOffset) > 0.0001 or math.abs(yOffset) > 0.0001 then |
1477 | Logging.xmlWarning(xmlFile, "Invalid position of xRotationNode2. Offset to second xRotationNode1 is not 0 on X or Y axis (x: %f y: %f)", xOffset, yOffset) |
1478 | return false |
1479 | end |
1480 | |
1481 | local rootOffset = calcDistanceFrom(easyArmControl.rootNode, easyArmControl.xRotationNodes[1].node) |
1482 | if rootOffset > 0.05 then |
1483 | Logging.xmlWarning(xmlFile, "Distance between easyArmControl rootNode and xRotationNode1 is to big (%.2f). They should be at the same position.", rootOffset) |
1484 | return false |
1485 | end |
1486 | |
1487 | easyArmControl.maxTotalDistance = xmlFile:getValue(key.."#maxTotalDistance") |
1488 | if easyArmControl.maxTotalDistance == nil then |
1489 | -- move nodes to the max length state |
1490 | for i=1, #easyArmControl.xRotationNodes do |
1491 | local xRotationNode = easyArmControl.xRotationNodes[i] |
1492 | local curRot = {getRotation(xRotationNode.node)} |
1493 | curRot[xRotationNode.movingTool.rotationAxis] = xRotationNode.movingTool.rotMin |
1494 | setRotation(xRotationNode.node, curRot[1], curRot[2], curRot[3]) |
1495 | end |
1496 | |
1497 | for i=1, #easyArmControl.zTranslationNodes do |
1498 | local zTranslationNode = easyArmControl.zTranslationNodes[i] |
1499 | local curTrans = {getTranslation(zTranslationNode.node)} |
1500 | curTrans[zTranslationNode.movingTool.translationAxis] = zTranslationNode.movingTool.transMax |
1501 | setTranslation(zTranslationNode.node, curTrans[1], curTrans[2], curTrans[3]) |
1502 | end |
1503 | |
1504 | -- calculate max distances |
1505 | easyArmControl.maxTotalDistance = calcDistanceFrom(easyArmControl.rootNode, easyArmControl.targetRefNode) |
1506 | easyArmControl.maxTransDistance = calcDistanceFrom(easyArmControl.xRotationNodes[#easyArmControl.xRotationNodes].node, easyArmControl.targetRefNode) |
1507 | |
1508 | -- reset nodes again to previous states |
1509 | for i=1, #easyArmControl.xRotationNodes do |
1510 | local xRotationNode = easyArmControl.xRotationNodes[i] |
1511 | setRotation(xRotationNode.node, xRotationNode.startRotation[1], xRotationNode.startRotation[2], xRotationNode.startRotation[3]) |
1512 | end |
1513 | |
1514 | for i=1, #easyArmControl.zTranslationNodes do |
1515 | local zTranslationNode = easyArmControl.zTranslationNodes[i] |
1516 | setTranslation(zTranslationNode.node, zTranslationNode.startTranslation[1], zTranslationNode.startTranslation[2], zTranslationNode.startTranslation[3]) |
1517 | end |
1518 | end |
1519 | else |
1520 | Logging.xmlError(xmlFile, "Missing moving tools for easy control targets!") |
1521 | return false |
1522 | end |
1523 | else |
1524 | Logging.xmlError(xmlFile, "Missing easy control targets!") |
1525 | return false |
1526 | end |
1527 | |
1528 | return true |
1529 | end |
loadExtraDependentParts
DescriptionDefinitionloadExtraDependentParts()Code
2690 | function Cylindered:loadExtraDependentParts(xmlFile, baseName, entry) |
2691 | return true |
2692 | end |
loadMovingPartFromXML
DescriptionDefinitionloadMovingPartFromXML()Code
2079 | function Cylindered:loadMovingPartFromXML(xmlFile, key, entry) |
2080 | XMLUtil.checkDeprecatedXMLElements(xmlFile, key.."#index", key.."#node") --FS17 to FS19 |
2081 | |
2082 | local node = xmlFile:getValue(key.."#node", nil, self.components, self.i3dMappings) |
2083 | local referenceFrame = xmlFile:getValue(key.."#referenceFrame", nil, self.components, self.i3dMappings) |
2084 | if node ~= nil and referenceFrame ~= nil then |
2085 | entry.referencePoint = xmlFile:getValue(key.."#referencePoint", nil, self.components, self.i3dMappings) |
2086 | entry.node = node |
2087 | entry.parent = getParent(node) |
2088 | entry.referenceFrame = referenceFrame |
2089 | entry.invertZ = xmlFile:getValue(key.."#invertZ", false) |
2090 | entry.scaleZ = xmlFile:getValue(key.."#scaleZ", false) |
2091 | entry.limitedAxis = xmlFile:getValue(key.."#limitedAxis") |
2092 | entry.isActiveDirty = xmlFile:getValue(key.."#isActiveDirty", false) |
2093 | entry.playSound = xmlFile:getValue(key.."#playSound", false) |
2094 | |
2095 | entry.moveToReferenceFrame = xmlFile:getValue(key.."#moveToReferenceFrame", false) |
2096 | if entry.moveToReferenceFrame then |
2097 | local x,y,z = worldToLocal(referenceFrame, getWorldTranslation(node)) |
2098 | entry.referenceFrameOffset = {x,y,z} |
2099 | end |
2100 | |
2101 | if entry.referenceFrame == entry.node then |
2102 | Logging.xmlWarning(xmlFile, "Reference frame equals moving part node. This can lead to bad behaviours! Node '%s' in '%s'.", getName(entry.node), key) |
2103 | end |
2104 | |
2105 | entry.doLineAlignment = xmlFile:getValue(key.."#doLineAlignment", false) |
2106 | entry.doInversedLineAlignment = xmlFile:getValue(key.."#doInversedLineAlignment", false) |
2107 | entry.partLength = xmlFile:getValue(key..".orientationLine#partLength", 0.5) |
2108 | entry.partLengthNode = xmlFile:getValue(key..".orientationLine#partLengthNode", nil, self.components, self.i3dMappings) |
2109 | entry.orientationLineNodes = {} |
2110 | local i = 0 |
2111 | while true do |
2112 | local pointKey = string.format("%s.orientationLine.lineNode(%d)", key, i) |
2113 | if not xmlFile:hasProperty(pointKey) then |
2114 | break |
2115 | end |
2116 | |
2117 | local lineNode = xmlFile:getValue(pointKey.."#node", nil, self.components, self.i3dMappings) |
2118 | table.insert(entry.orientationLineNodes, lineNode) |
2119 | |
2120 | i = i + 1 |
2121 | end |
2122 | |
2123 | entry.doDirectionAlignment = xmlFile:getValue(key.."#doDirectionAlignment", true) |
2124 | entry.doRotationAlignment = xmlFile:getValue(key.."#doRotationAlignment", false) |
2125 | entry.rotMultiplier = xmlFile:getValue(key.."#rotMultiplier", 0) |
2126 | |
2127 | local minRot = xmlFile:getValue(key.."#minRot") |
2128 | local maxRot = xmlFile:getValue(key.."#maxRot") |
2129 | if minRot ~= nil and maxRot ~= nil then |
2130 | if entry.limitedAxis ~= nil then |
2131 | entry.minRot = MathUtil.getValidLimit(minRot) |
2132 | entry.maxRot = MathUtil.getValidLimit(maxRot) |
2133 | else |
2134 | print("Warning: minRot/maxRot requires the use of limitedAxis in '"..self.configFileName.."'") |
2135 | end |
2136 | end |
2137 | entry.alignToWorldY = xmlFile:getValue(key.."#alignToWorldY", false) |
2138 | |
2139 | if entry.referencePoint ~= nil then |
2140 | local localReferencePoint = xmlFile:getValue(key.."#localReferencePoint", nil, self.components, self.i3dMappings) |
2141 | local refX, refY, refZ = worldToLocal(node, getWorldTranslation(entry.referencePoint)) |
2142 | if localReferencePoint ~= nil then |
2143 | local x,y,z = worldToLocal(node, getWorldTranslation(localReferencePoint)) |
2144 | |
2145 | entry.referenceDistance = MathUtil.vector3Length(refX-x, refY-y, refZ-z) |
2146 | entry.lastReferenceDistance = entry.referenceDistance |
2147 | entry.localReferencePoint = {x, y, z} |
2148 | |
2149 | local side = y*(refZ-z) - z*(refY-y) |
2150 | entry.localReferenceAngleSide = side |
2151 | entry.localReferencePointNode = localReferencePoint |
2152 | entry.updateLocalReferenceDistance = xmlFile:getValue(key.."#updateLocalReferenceDistance", false) |
2153 | entry.localReferenceTranslate = xmlFile:getValue(key.."#localReferenceTranslate", false) |
2154 | if entry.localReferenceTranslate then |
2155 | entry.localReferenceTranslation = { getTranslation(entry.node) } |
2156 | end |
2157 | |
2158 | entry.dynamicLocalReferenceDistance = xmlFile:getValue(key.."#dynamicLocalReferenceDistance", false) |
2159 | else |
2160 | entry.referenceDistance = 0 |
2161 | entry.localReferencePoint = {refX, refY, refZ} |
2162 | end |
2163 | entry.referenceDistanceThreshold = xmlFile:getValue(key.."#referenceDistanceThreshold", 0) |
2164 | |
2165 | entry.useLocalOffset = xmlFile:getValue(key.."#useLocalOffset", false) |
2166 | |
2167 | entry.referenceDistancePoint = xmlFile:getValue(key.."#referenceDistancePoint", nil, self.components, self.i3dMappings) |
2168 | |
2169 | entry.localReferenceDistance = xmlFile:getValue(key.."#localReferenceDistance", MathUtil.vector2Length(entry.localReferencePoint[2], entry.localReferencePoint[3])) |
2170 | |
2171 | self:loadDependentTranslatingParts(xmlFile, key, entry) |
2172 | end |
2173 | |
2174 | -- direction threshold for updateing the moving tools of the vehicle is entered/active |
2175 | entry.directionThreshold = xmlFile:getValue(key.."#directionThreshold", 0.0001) |
2176 | entry.directionThresholdActive = xmlFile:getValue(key.."#directionThresholdActive", 0.00001) |
2177 | entry.maxUpdateDistance = xmlFile:getValue(key.."#maxUpdateDistance", "-") |
2178 | if entry.maxUpdateDistance == "-" then |
2179 | entry.maxUpdateDistance = math.huge |
2180 | else |
2181 | entry.maxUpdateDistance = tonumber(entry.maxUpdateDistance) |
2182 | end |
2183 | |
2184 | if entry.isActiveDirty and (xmlFile:getString(key.."#maxUpdateDistance") == nil or entry.maxUpdateDistance == nil) then |
2185 | Logging.xmlWarning(xmlFile, 'No max. update distance set for isActiveDirty moving part (%s)! Use #maxUpdateDistance attribute.', key) |
2186 | end |
2187 | |
2188 | entry.smoothedDirectionScale = self.xmlFile:getValue(key.."#smoothedDirectionScale", false) |
2189 | entry.smoothedDirectionTime = 1 / (self.xmlFile:getValue(key.."#smoothedDirectionTime", 2)) |
2190 | entry.smoothedDirectionScaleAlpha = nil |
2191 | |
2192 | if entry.smoothedDirectionScale then |
2193 | entry.initialDirection = {localDirectionToLocal(entry.node, getParent(entry.node), 0, 0, 1)} |
2194 | end |
2195 | |
2196 | entry.debug = xmlFile:getValue(key.."#debug", false) |
2197 | if entry.debug then |
2198 | Logging.xmlWarning(xmlFile, "MovingPart debug enabled for moving part '%s'", key) |
2199 | end |
2200 | |
2201 | entry.lastDirection = {0, 0, 0} |
2202 | entry.lastUpVector = {0, 0, 0} |
2203 | |
2204 | entry.isDirty = false |
2205 | entry.isPart = true |
2206 | entry.isActive = true |
2207 | |
2208 | return true |
2209 | end |
2210 | |
2211 | return false |
2212 | end |
loadMovingToolFromXML
DescriptionDefinitionloadMovingToolFromXML()Code
2216 | function Cylindered:loadMovingToolFromXML(xmlFile, key, entry) |
2217 | local spec = self.spec_cylindered |
2218 | |
2219 | XMLUtil.checkDeprecatedXMLElements(xmlFile, key.."#index", key.."#node") --FS17 to FS19 |
2220 | |
2221 | local node = xmlFile:getValue(key.."#node", nil, self.components, self.i3dMappings) |
2222 | if node ~= nil then |
2223 | entry.node = node |
2224 | |
2225 | entry.externalMove = 0 |
2226 | entry.easyArmControlActive = true |
2227 | entry.isEasyControlTarget = xmlFile:getValue(key.."#isEasyControlTarget", false) |
2228 | |
2229 | entry.networkInterpolators = {} |
2230 | |
2231 | -- rotation |
2232 | XMLUtil.checkDeprecatedXMLElements(xmlFile, key.."#rotSpeed", key..".rotation#rotSpeed") --FS15 to FS17 |
2233 | |
2234 | local rotSpeed = xmlFile:getValue(key..".rotation#rotSpeed") |
2235 | if rotSpeed ~= nil then |
2236 | entry.rotSpeed = rotSpeed / 1000 |
2237 | end |
2238 | local rotAcceleration = xmlFile:getValue(key..".rotation#rotAcceleration") |
2239 | if rotAcceleration ~= nil then |
2240 | entry.rotAcceleration = rotAcceleration / (1000*1000) |
2241 | end |
2242 | entry.lastRotSpeed = 0 |
2243 | entry.rotMax = xmlFile:getValue(key..".rotation#rotMax") |
2244 | entry.rotMin = xmlFile:getValue(key..".rotation#rotMin") |
2245 | entry.syncMaxRotLimits = xmlFile:getValue(key..".rotation#syncMaxRotLimits", false) |
2246 | entry.syncMinRotLimits = xmlFile:getValue(key..".rotation#syncMinRotLimits", false) |
2247 | entry.rotSendNumBits = xmlFile:getValue(key..".rotation#rotSendNumBits", 8) |
2248 | entry.attachRotMax = xmlFile:getValue(key..".rotation#attachRotMax") |
2249 | entry.attachRotMin = xmlFile:getValue(key..".rotation#attachRotMin") |
2250 | |
2251 | -- translation |
2252 | XMLUtil.checkDeprecatedXMLElements(xmlFile, key.."#transSpeed", key..".rotation#transSpeed") --FS15 to FS17 |
2253 | |
2254 | local transSpeed = xmlFile:getValue(key..".translation#transSpeed") |
2255 | if transSpeed ~= nil then |
2256 | entry.transSpeed = transSpeed/1000 |
2257 | end |
2258 | local transAcceleration = xmlFile:getValue(key..".translation#transAcceleration") |
2259 | if transAcceleration ~= nil then |
2260 | entry.transAcceleration = transAcceleration/(1000*1000) |
2261 | end |
2262 | entry.lastTransSpeed = 0 |
2263 | entry.transMax = xmlFile:getValue(key..".translation#transMax") |
2264 | entry.transMin = xmlFile:getValue(key..".translation#transMin") |
2265 | entry.attachTransMax = xmlFile:getValue(key..".translation#attachTransMax") |
2266 | entry.attachTransMin = xmlFile:getValue(key..".translation#attachTransMin") |
2267 | entry.playSound = xmlFile:getValue(key.."#playSound", false) |
2268 | |
2269 | entry.isConsumingPower = xmlFile:getValue(key.."#isConsumingPower", false) |
2270 | |
2271 | -- animation |
2272 | if SpecializationUtil.hasSpecialization(AnimatedVehicle, self.specializations) then |
2273 | local animSpeed = xmlFile:getValue(key..".animation#animSpeed") |
2274 | if animSpeed ~= nil then |
2275 | entry.animSpeed = animSpeed / 1000 |
2276 | end |
2277 | local animAcceleration = xmlFile:getValue(key..".animation#animAcceleration") |
2278 | if animAcceleration ~= nil then |
2279 | entry.animAcceleration = animAcceleration / (1000*1000) |
2280 | end |
2281 | entry.curAnimTime = 0 |
2282 | entry.lastAnimSpeed = 0 |
2283 | entry.animName = xmlFile:getValue(key..".animation#animName") |
2284 | entry.animSendNumBits = xmlFile:getValue(key..".animation#animSendNumBits", 8) |
2285 | entry.animMaxTime = math.min(xmlFile:getValue(key..".animation#animMaxTime", 1.0), 1.0) |
2286 | entry.animMinTime = math.max(xmlFile:getValue(key..".animation#animMinTime", 0.0), 0.0) |
2287 | |
2288 | entry.animStartTime = xmlFile:getValue(key..".animation#animStartTime") |
2289 | if entry.animStartTime ~= nil then |
2290 | entry.curAnimTime = entry.animStartTime |
2291 | end |
2292 | |
2293 | entry.networkInterpolators.animation = InterpolatorValue.new(entry.curAnimTime) |
2294 | entry.networkInterpolators.animation:setMinMax(0, 1) |
2295 | end |
2296 | |
2297 | XMLUtil.checkDeprecatedXMLElements(xmlFile, key..".controls#iconFilename", key..".controls#iconName") --FS17 to FS19 |
2298 | |
2299 | local iconName = xmlFile:getValue(key .. ".controls#iconName") |
2300 | if iconName ~= nil then |
2301 | if InputHelpElement.AXIS_ICON[iconName] == nil then |
2302 | -- add the mod name as a prefix to match axis icon loading name collision avoidance |
2303 | iconName = (self.customEnvironment or "") .. iconName |
2304 | end |
2305 | |
2306 | entry.axisActionIcon = iconName |
2307 | end |
2308 | |
2309 | entry.controlGroupIndex = xmlFile:getValue(key .. ".controls#groupIndex", 0) |
2310 | if entry.controlGroupIndex ~= 0 then |
2311 | if spec.controlGroupNames[entry.controlGroupIndex] ~= nil then |
2312 | table.addElement(spec.controlGroups, entry.controlGroupIndex) |
2313 | else |
2314 | Logging.xmlWarning(xmlFile, "ControlGroup '%d' not defined for '%s'!", entry.controlGroupIndex, key) |
2315 | end |
2316 | end |
2317 | |
2318 | entry.axis = xmlFile:getValue(key..".controls#axis") |
2319 | if entry.axis ~= nil then |
2320 | entry.axisActionIndex = InputAction[entry.axis] |
2321 | end |
2322 | entry.invertAxis = xmlFile:getValue(key..".controls#invertAxis", false) |
2323 | entry.mouseSpeedFactor = xmlFile:getValue(key..".controls#mouseSpeedFactor", 1.0) |
2324 | |
2325 | if (entry.rotSpeed ~= nil or entry.transSpeed ~= nil or entry.animSpeed ~= nil) then |
2326 | entry.dirtyFlag = self:getNextDirtyFlag() |
2327 | entry.saving = xmlFile:getValue(key.."#allowSaving", true) |
2328 | end |
2329 | |
2330 | entry.isDirty = false |
2331 | entry.isIntitialDirty = xmlFile:getValue(key.."#isIntitialDirty", true) |
2332 | |
2333 | entry.rotationAxis = xmlFile:getValue(key..".rotation#rotationAxis", 1) |
2334 | entry.translationAxis = xmlFile:getValue(key..".translation#translationAxis", 3) |
2335 | |
2336 | local detachingRotMaxLimit = xmlFile:getValue(key..".rotation#detachingRotMaxLimit") |
2337 | local detachingRotMinLimit = xmlFile:getValue(key..".rotation#detachingRotMinLimit") |
2338 | local detachingTransMaxLimit = xmlFile:getValue(key..".translation#detachingTransMaxLimit") |
2339 | local detachingTransMinLimit = xmlFile:getValue(key..".translation#detachingTransMinLimit") |
2340 | if detachingRotMaxLimit ~= nil or detachingRotMinLimit ~= nil or detachingTransMaxLimit ~= nil or detachingTransMinLimit ~= nil then |
2341 | if spec.detachLockNodes == nil then |
2342 | spec.detachLockNodes = {} |
2343 | end |
2344 | |
2345 | local detachLock = {} |
2346 | detachLock.detachingRotMaxLimit = detachingRotMaxLimit |
2347 | detachLock.detachingRotMinLimit = detachingRotMinLimit |
2348 | detachLock.detachingTransMinLimit = detachingTransMinLimit |
2349 | detachLock.detachingTransMaxLimit = detachingTransMaxLimit |
2350 | |
2351 | spec.detachLockNodes[entry] = detachLock |
2352 | end |
2353 | |
2354 | |
2355 | local rx,ry,rz = getRotation(node) |
2356 | entry.curRot = {rx,ry,rz} |
2357 | local x,y,z = getTranslation(node) |
2358 | entry.curTrans = {x,y,z} |
2359 | |
2360 | entry.startRot = xmlFile:getValue(key..".rotation#startRot") |
2361 | entry.startTrans = xmlFile:getValue(key..".translation#startTrans") |
2362 | |
2363 | entry.move = 0 |
2364 | entry.moveToSend = 0 |
2365 | |
2366 | entry.smoothedMove = 0 |
2367 | entry.lastInputTime = 0 |
2368 | |
2369 | -- delayed node |
2370 | XMLUtil.checkDeprecatedXMLElements(xmlFile, key.."#delayedIndex", key.."#delayedNode") --FS17 to FS19 |
2371 | |
2372 | entry.delayedNode = xmlFile:getValue(key.."#delayedNode", nil, self.components, self.i3dMappings) |
2373 | if entry.delayedNode ~= nil then |
2374 | entry.delayedFrames = xmlFile:getValue(key.."#delayedFrames", 3) |
2375 | |
2376 | entry.currentDelayedData = {rot = {rx, ry, rz}, trans = {x, y, z}} |
2377 | entry.delayedHistroyData = {} |
2378 | for i=1, entry.delayedFrames do |
2379 | entry.delayedHistroyData[i] = {rot = {rx, ry, rz}, trans = {x, y, z}} |
2380 | end |
2381 | |
2382 | entry.delayedHistoryIndex = 0 |
2383 | end |
2384 | |
2385 | entry.networkInterpolators.translation = InterpolatorValue.new(entry.curTrans[entry.translationAxis]) |
2386 | entry.networkInterpolators.translation:setMinMax(entry.transMin, entry.transMax) |
2387 | entry.networkInterpolators.rotation = InterpolatorAngle.new(entry.curRot[entry.rotationAxis]) |
2388 | entry.networkInterpolators.rotation:setMinMax(entry.rotMin, entry.rotMax) |
2389 | entry.networkTimeInterpolator = InterpolationTime.new(1.2) |
2390 | |
2391 | entry.isTool = true |
2392 | |
2393 | return true |
2394 | end |
2395 | |
2396 | return false |
2397 | end |
loadObjectChangeValuesFromXML
DescriptionLoad object change from xmlDefinition
loadObjectChangeValuesFromXML(integer xmlFile, string key, integer node, table object)Arguments
integer | xmlFile | id of xml object |
string | key | key |
integer | node | node id |
table | object | object |
2973 | function Cylindered:loadObjectChangeValuesFromXML(superFunc, xmlFile, key, node, object) |
2974 | superFunc(self, xmlFile, key, node, object) |
2975 | |
2976 | local spec = self.spec_cylindered |
2977 | |
2978 | if spec.nodesToMovingTools ~= nil and spec.nodesToMovingTools[node] ~= nil then |
2979 | local movingTool = spec.nodesToMovingTools[node] |
2980 | object.movingToolRotMaxActive = xmlFile:getValue(key.."#movingToolRotMaxActive", movingTool.rotMax) |
2981 | object.movingToolRotMaxInactive = xmlFile:getValue(key.."#movingToolRotMaxInactive", movingTool.rotMax) |
2982 | object.movingToolRotMinActive = xmlFile:getValue(key.."#movingToolRotMinActive", movingTool.rotMin) |
2983 | object.movingToolRotMinInactive = xmlFile:getValue(key.."#movingToolRotMinInactive", movingTool.rotMin) |
2984 | |
2985 | object.movingToolTransMaxActive = xmlFile:getValue(key.."#movingToolTransMaxActive", movingTool.transMax) |
2986 | object.movingToolTransMaxInactive = xmlFile:getValue(key.."#movingToolTransMaxInactive", movingTool.transMax) |
2987 | object.movingToolTransMinActive = xmlFile:getValue(key.."#movingToolTransMinActive", movingTool.transMin) |
2988 | object.movingToolTransMinInactive = xmlFile:getValue(key.."#movingToolTransMinInactive", movingTool.transMin) |
2989 | end |
2990 | |
2991 | ObjectChangeUtil.loadValueType(object.values, xmlFile, key, "movingPartUpdate", nil, |
2992 | function(state) |
2993 | if self.getMovingPartByNode ~= nil then |
2994 | local movingPart = self:getMovingPartByNode(node) |
2995 | if movingPart ~= nil then |
2996 | movingPart.isActive = state |
2997 | end |
2998 | end |
2999 | end, false) |
3000 | end |
loadRotationBasedLimits
DescriptionDefinitionloadRotationBasedLimits()Code
2772 | function Cylindered:loadRotationBasedLimits(xmlFile, key, tool) |
2773 | local rotation = xmlFile:getValue(key.."#rotation") |
2774 | local rotMin = xmlFile:getValue(key.."#rotMin") |
2775 | local rotMax = xmlFile:getValue(key.."#rotMax") |
2776 | local transMin = xmlFile:getValue(key.."#transMin") |
2777 | local transMax = xmlFile:getValue(key.."#transMax") |
2778 | |
2779 | if rotation ~= nil and (rotMin ~= nil or rotMax ~= nil or transMin ~= nil or transMax ~= nil) then |
2780 | local time = (rotation-tool.rotMin) / (tool.rotMax-tool.rotMin) |
2781 | return {rotMin=rotMin, rotMax=rotMax, transMin=transMin, transMax=transMax, time=time} |
2782 | end |
2783 | |
2784 | return nil |
2785 | end |
loadShovelNode
DescriptionDefinitionloadShovelNode()Code
3074 | function Cylindered:loadShovelNode(superFunc, xmlFile, key, entry) |
3075 | if not superFunc(self, xmlFile, key, entry) then |
3076 | return false |
3077 | end |
3078 | |
3079 | local baseKey = key .. ".movingToolActivation" |
3080 | if not xmlFile:hasProperty(baseKey) then |
3081 | return true |
3082 | end |
3083 | |
3084 | entry.movingToolActivation = {} |
3085 | entry.movingToolActivation.node = xmlFile:getValue(baseKey.."#node", nil, self.components, self.i3dMappings) |
3086 | entry.movingToolActivation.isInverted = xmlFile:getValue(baseKey.."#isInverted", false) |
3087 | entry.movingToolActivation.openFactor = xmlFile:getValue(baseKey.."#openFactor", 1) |
3088 | |
3089 | return true |
3090 | end |
movingToolDashboardAttributes
DescriptionDefinitionmovingToolDashboardAttributes()Code
4230 | function Cylindered.movingToolDashboardAttributes(self, xmlFile, key, dashboard) |
4231 | dashboard.axis = xmlFile:getValue(key.."#axis") |
4232 | |
4233 | if dashboard.axis == nil then |
4234 | Logging.xmlWarning(xmlFile, "Misssing axis attribute for dashboard '%s'", key) |
4235 | return false |
4236 | end |
4237 | |
4238 | dashboard.attacherJointIndex = xmlFile:getValue(key.."#attacherJointIndex") |
4239 | dashboard.attacherJointNode = xmlFile:getValue(key.."#attacherJointNode", nil, self.components, self.i3dMappings) |
4240 | |
4241 | return true |
4242 | end |
onAnimationPartChanged
DescriptionCalled when animation part has changedDefinition
onAnimationPartChanged()Code
3327 | function Cylindered:onAnimationPartChanged(node) |
3328 | self:setMovingToolDirty(node) |
3329 | end |
onDeactivate
DescriptionCalled on deactivateDefinition
onDeactivate()Code
3317 | function Cylindered:onDeactivate() |
3318 | if self.isClient then |
3319 | local spec = self.spec_cylindered |
3320 | g_soundManager:stopSample(spec.samples.hydraulic) |
3321 | spec.isHydraulicSamplePlaying = false |
3322 | end |
3323 | end |
onDelete
DescriptionCalled on deletingDefinition
onDelete()Code
900 | function Cylindered:onDelete() |
901 | local spec = self.spec_cylindered |
902 | |
903 | g_soundManager:deleteSamples(spec.samples) |
904 | g_soundManager:deleteSamples(spec.actionSamples) |
905 | |
906 | if spec.movingTools ~= nil then |
907 | for _,movingTool in pairs(spec.movingTools) do |
908 | if movingTool.icon ~= nil then |
909 | movingTool.icon:delete() |
910 | movingTool.icon = nil |
911 | end |
912 | end |
913 | end |
914 | end |
onDraw
DescriptionDefinitiononDraw()Code
2066 | function Cylindered:onDraw(isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
2067 | local spec = self.spec_cylindered |
2068 | if #spec.controlGroupNames > 1 then |
2069 | if isActiveForInputIgnoreSelection then |
2070 | if spec.currentControlGroupIndex ~= 0 then |
2071 | g_currentMission:addExtraPrintText(string.format(g_i18n:getText("action_selectedControlGroup"), spec.controlGroupNames[spec.currentControlGroupIndex], spec.currentControlGroupIndex)) |
2072 | end |
2073 | end |
2074 | end |
2075 | end |
onLoad
DescriptionCalled on loadingDefinition
onLoad(table savegame)Arguments
table | savegame | savegame |
405 | function Cylindered:onLoad(savegame) |
406 | local spec = self.spec_cylindered |
407 | |
408 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.movingParts", "vehicle.cylindered.movingParts") --FS17 to FS19 |
409 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.movingTools", "vehicle.cylindered.movingTools") --FS17 to FS19 |
410 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.cylinderedHydraulicSound", "vehicle.cylindered.sounds.hydraulic") --FS17 to FS19 |
411 | |
412 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.cylindered.movingParts#isActiveDirtyTimeOffset") --FS19 to FS22 |
413 | |
414 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.cylindered.movingParts.sounds", "vehicle.cylindered.sounds") --FS19 to FS22 |
415 | |
416 | spec.activeDirtyMovingParts = {} |
417 | |
418 | local referenceNodes = {} |
419 | spec.nodesToMovingParts = {} |
420 | spec.movingParts = {} |
421 | self.anyMovingPartsDirty = false |
422 | spec.detachLockNodes = nil |
423 | local i = 0 |
424 | while true do |
425 | local partKey = string.format("vehicle.cylindered.movingParts.movingPart(%d)", i) |
426 | if not self.xmlFile:hasProperty(partKey) then |
427 | break |
428 | end |
429 | |
430 | local entry = {} |
431 | if self:loadMovingPartFromXML(self.xmlFile, partKey, entry) then |
432 | if referenceNodes[entry.node] == nil then |
433 | referenceNodes[entry.node] = {} |
434 | end |
435 | if spec.nodesToMovingParts[entry.node] == nil then |
436 | table.insert(referenceNodes[entry.node], entry) |
437 | |
438 | self:loadDependentParts(self.xmlFile, partKey, entry) |
439 | self:loadDependentComponentJoints(self.xmlFile, partKey, entry) |
440 | self:loadCopyLocalDirectionParts(self.xmlFile, partKey, entry) |
441 | self:loadExtraDependentParts(self.xmlFile, partKey, entry) |
442 | self:loadDependentAnimations(self.xmlFile, partKey, entry) |
443 | |
444 | entry.key = partKey |
445 | table.insert(spec.movingParts, entry) |
446 | |
447 | if entry.isActiveDirty then |
448 | table.insert(spec.activeDirtyMovingParts, entry) |
449 | end |
450 | |
451 | spec.nodesToMovingParts[entry.node] = entry |
452 | else |
453 | Logging.xmlWarning(self.xmlFile, "Moving part with node '%s' already exists!", getName(entry.node)) |
454 | end |
455 | end |
456 | |
457 | i = i + 1 |
458 | end |
459 | |
460 | if Cylindered.DIRTY_COLLISION_UPDATE_CHECK then |
461 | -- collect all active dirty moving parts which have directionThreshold not changed from xml |
462 | -- and check if any of these has collisions as children. If yes, print warning |
463 | local function collectDependentParts(part, target) |
464 | table.insert(target, part) |
465 | |
466 | if part.dependentPartNodes ~= nil then |
467 | for l=1, #part.dependentPartNodes do |
468 | local dependentMovingPart = spec.nodesToMovingParts[part.dependentPartNodes[l]] |
469 | if dependentMovingPart ~= nil then |
470 | table.insert(target, dependentMovingPart) |
471 | |
472 | collectDependentParts(dependentMovingPart, target) |
473 | end |
474 | end |
475 | end |
476 | end |
477 | |
478 | local function subCollisionErrorFunction(collisionNode, xmlFile, key) |
479 | if getHasClassId(collisionNode, ClassIds.SHAPE) then |
480 | Logging.xmlError(xmlFile, "Found collision '%s' as child of isActiveDirty movingPart '%s'. This can cause the vehicle to never sleep!", getName(collisionNode), key) |
481 | end |
482 | end |
483 | |
484 | spec.realActiveDirtyParts = {} |
485 | for j=1, #spec.movingParts do |
486 | local movingPart = spec.movingParts[j] |
487 | if movingPart.isActiveDirty and movingPart.directionThreshold == 0.0001 then |
488 | collectDependentParts(movingPart, spec.realActiveDirtyParts) |
489 | end |
490 | end |
491 | |
492 | for j=1, #spec.realActiveDirtyParts do |
493 | local part = spec.realActiveDirtyParts[j] |
494 | I3DUtil.checkForChildCollisions(part.node, subCollisionErrorFunction, self.xmlFile, getName(part.node)) |
495 | end |
496 | end |
497 | |
498 | spec.powerConsumingActiveTimeOffset = self.xmlFile:getValue("vehicle.cylindered.movingTools#powerConsumingActiveTimeOffset", 5) |
499 | spec.powerConsumingTimer = -1 |
500 | |
501 | -- find dependencies |
502 | for _, part in pairs(spec.movingParts) do |
503 | self:resolveDependentPartData(part.dependentPartData, referenceNodes) |
504 | end |
505 | |
506 | local function addMovingPart(part, newTable, allowDependentParts) |
507 | for _, addedPart in ipairs(newTable) do |
508 | if addedPart == part then |
509 | return |
510 | end |
511 | end |
512 | |
513 | if part.isDependentPart == true then |
514 | if allowDependentParts ~= true then |
515 | return |
516 | end |
517 | end |
518 | |
519 | table.insert(newTable, part) |
520 | |
521 | for _, depPart in pairs(part.dependentPartData) do |
522 | addMovingPart(depPart.part, newTable, true) |
523 | end |
524 | end |
525 | |
526 | local newParts = {} |
527 | for _, part in ipairs(spec.movingParts) do |
528 | addMovingPart(part, newParts) |
529 | end |
530 | spec.movingParts = newParts |
531 | |
532 | spec.controlGroups = {} |
533 | spec.controlGroupMapping = {} |
534 | spec.currentControlGroupIndex = 1 |
535 | spec.controlGroupNames = {} |
536 | i = 0 |
537 | while true do |
538 | local groupKey = string.format("vehicle.cylindered.movingTools.controlGroups.controlGroup(%d)", i) |
539 | if not self.xmlFile:hasProperty(groupKey) then |
540 | break |
541 | end |
542 | |
543 | local name = self.xmlFile:getValue(groupKey.."#name", "", self.customEnvironment, false) |
544 | if name ~= nil then |
545 | table.insert(spec.controlGroupNames, name) |
546 | end |
547 | |
548 | i = i + 1 |
549 | end |
550 | |
551 | spec.nodesToMovingTools = {} |
552 | spec.movingTools = {} |
553 | i = 0 |
554 | while true do |
555 | local toolKey = string.format("vehicle.cylindered.movingTools.movingTool(%d)", i) |
556 | if not self.xmlFile:hasProperty(toolKey) then |
557 | break |
558 | end |
559 | |
560 | local entry = {} |
561 | if self:loadMovingToolFromXML(self.xmlFile, toolKey, entry) then |
562 | if referenceNodes[entry.node] == nil then |
563 | referenceNodes[entry.node] = {} |
564 | end |
565 | |
566 | if spec.nodesToMovingTools[entry.node] == nil then |
567 | table.insert(referenceNodes[entry.node], entry) |
568 | |
569 | self:loadDependentMovingTools(self.xmlFile, toolKey, entry) |
570 | self:loadDependentParts(self.xmlFile, toolKey, entry) |
571 | self:loadDependentComponentJoints(self.xmlFile, toolKey, entry) |
572 | self:loadExtraDependentParts(self.xmlFile, toolKey, entry) |
573 | self:loadDependentAnimations(self.xmlFile, toolKey, entry) |
574 | |
575 | entry.isActive = true |
576 | entry.key = toolKey |
577 | table.insert(spec.movingTools, entry) |
578 | spec.nodesToMovingTools[entry.node] = entry |
579 | else |
580 | Logging.xmlWarning(self.xmlFile, "Moving tool with node '%s' already exists!", getName(entry.node)) |
581 | end |
582 | end |
583 | i = i + 1 |
584 | end |
585 | |
586 | local function sort(a, b) |
587 | return a < b |
588 | end |
589 | table.sort(spec.controlGroups, sort) |
590 | |
591 | for _, groupIndex in ipairs(spec.controlGroups) do |
592 | local subSelectionIndex = self:addSubselection(groupIndex) |
593 | spec.controlGroupMapping[subSelectionIndex] = groupIndex |
594 | end |
595 | |
596 | for _, part in pairs(spec.movingTools) do |
597 | self:resolveDependentPartData(part.dependentPartData, referenceNodes) |
598 | |
599 | for j=#part.dependentMovingTools, 1, -1 do |
600 | local dependentTool = part.dependentMovingTools[j] |
601 | local tool = spec.nodesToMovingTools[dependentTool.node] |
602 | if tool ~= nil then |
603 | dependentTool.movingTool = tool |
604 | else |
605 | Logging.xmlWarning(self.xmlFile, "Dependent moving tool '%s' not defined. Ignoring it!", getName(dependentTool.node)) |
606 | table.remove(part.dependentMovingTools, j) |
607 | end |
608 | end |
609 | end |
610 | |
611 | |
612 | local easyArmControlKey = "vehicle.cylindered.movingTools.easyArmControl" |
613 | if self.xmlFile:hasProperty(easyArmControlKey) then |
614 | local easyArmControl = {} |
615 | if self:loadEasyArmControlFromXML(self.xmlFile, easyArmControlKey, easyArmControl) then |
616 | spec.easyArmControl = easyArmControl |
617 | end |
618 | end |
619 | |
620 | spec.samples = {} |
621 | spec.actionSamples = {} |
622 | if self.isClient then |
623 | spec.samples.hydraulic = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.cylindered.sounds", "hydraulic", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self) |
624 | spec.isHydraulicSamplePlaying = false |
625 | |
626 | spec.nodesToSamples = {} |
627 | spec.activeSamples = {} |
628 | spec.endingSamples = {} |
629 | spec.endingSamplesBySample = {} |
630 | spec.startingSamples = {} |
631 | spec.startingSamplesBySample = {} |
632 | |
633 | i = 0 |
634 | while true do |
635 | local actionKey = string.format("actionSound(%d)", i) |
636 | local baseKey = "vehicle.cylindered.sounds." .. actionKey |
637 | if not self.xmlFile:hasProperty(baseKey) then |
638 | break |
639 | end |
640 | |
641 | local sample = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.cylindered.sounds", actionKey, self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self) |
642 | |
643 | if sample ~= nil then |
644 | local actionNamesStr = self.xmlFile:getValue(baseKey .. "#actionNames") |
645 | local actionNames = string.split(actionNamesStr:trim(), " ") |
646 | |
647 | local nodesStr = self.xmlFile:getValue(baseKey .. "#nodes") |
648 | local nodes = string.split(nodesStr, " ") |
649 | |
650 | for l=1, #nodes do |
651 | nodes[l] = I3DUtil.indexToObject(self.components, nodes[l], self.i3dMappings) |
652 | end |
653 | |
654 | for j=1, #actionNames do |
655 | local actionName = actionNames[j] |
656 | actionName = "SOUND_ACTION_" .. actionName:upper() |
657 | local action = Cylindered[actionName] |
658 | if action ~= nil then |
659 | for l=1, #nodes do |
660 | local node = nodes[l] |
661 | if node ~= nil then |
662 | if spec.nodesToSamples[node] == nil then |
663 | spec.nodesToSamples[node] = {} |
664 | end |
665 | |
666 | if spec.nodesToSamples[node][action] == nil then |
667 | spec.nodesToSamples[node][action] = {} |
668 | end |
669 | |
670 | local part = self:getMovingPartByNode(node) or self:getTranslatingPartByNode(node) or self:getMovingToolByNode(node) |
671 | if part ~= nil then |
672 | part.samplesByAction = spec.nodesToSamples[node] |
673 | else |
674 | Logging.xmlWarning(self.xmlFile, "Unable to find movingPart or translatingPart for node '%s'", getName(node)) |
675 | end |
676 | |
677 | table.insert(spec.nodesToSamples[node][action], sample) |
678 | end |
679 | end |
680 | else |
681 | Logging.xmlWarning(self.xmlFile, "Unable to find sound action '%s' for sound '%s'", actionName, baseKey) |
682 | end |
683 | end |
684 | |
685 | sample.dropOffFactor = self.xmlFile:getValue(baseKey .. ".pitch#dropOffFactor", 1) |
686 | sample.dropOffTime = self.xmlFile:getValue(baseKey .. ".pitch#dropOffTime", 0) * 1000 |
687 | |
688 | sample.actionNames = actionNames |
689 | sample.nodes = nodes |
690 | |
691 | table.insert(spec.actionSamples, sample) |
692 | end |
693 | |
694 | i = i + 1 |
695 | end |
696 | end |
697 | |
698 | if self.loadDashboardsFromXML ~= nil then |
699 | local dashboardData = {valueTypeToLoad = "movingTool", |
700 | valueObject = self, |
701 | valueFunc = Cylindered.getMovingToolDashboardState, |
702 | minFunc = 0, |
703 | maxFunc = 1, |
704 | additionalAttributesFunc = Cylindered.movingToolDashboardAttributes, |
705 | idleValue = 0.5} |
706 | |
707 | self:loadDashboardsFromXML(self.xmlFile, "vehicle.cylindered.dashboards", dashboardData) |
708 | end |
709 | |
710 | spec.cylinderedDirtyFlag = self:getNextDirtyFlag() |
711 | spec.cylinderedInputDirtyFlag = self:getNextDirtyFlag() |
712 | |
713 | self:registerVehicleSetting(GameSettings.SETTING.EASY_ARM_CONTROL, true) |
714 | |
715 | spec.isLoading = true |
716 | end |
onLoadFinished
DescriptionCalled after loadingDefinition
onLoadFinished(table savegame)Arguments
table | savegame | savegame |
886 | function Cylindered:onLoadFinished(savegame) |
887 | local spec = self.spec_cylindered |
888 | spec.isLoading = false |
889 | |
890 | for i=1, #spec.movingTools do |
891 | local tool = spec.movingTools[i] |
892 | if tool.delayedHistoryIndex ~= nil and tool.delayedHistoryIndex > 0 then |
893 | self:updateDelayedTool(tool, true) |
894 | end |
895 | end |
896 | end |
onMovingPartSoundEvent
DescriptionDefinitiononMovingPartSoundEvent()Code
1820 | function Cylindered:onMovingPartSoundEvent(part, action, type) |
1821 | if part.samplesByAction ~= nil then |
1822 | local samples = part.samplesByAction[action] |
1823 | if samples ~= nil then |
1824 | for i=1, #samples do |
1825 | local sample = samples[i] |
1826 | |
1827 | if type == Cylindered.SOUND_TYPE_EVENT then |
1828 | if sample.loops == 0 then |
1829 | sample.loops = 1 |
1830 | end |
1831 | |
1832 | g_soundManager:playSample(sample) |
1833 | elseif type == Cylindered.SOUND_TYPE_CONTINUES then |
1834 | if not g_soundManager:getIsSamplePlaying(sample) then |
1835 | g_soundManager:playSample(sample) |
1836 | |
1837 | sample.lastActivationTime = g_time |
1838 | sample.lastActivationPart = part |
1839 | |
1840 | local spec = self.spec_cylindered |
1841 | table.insert(spec.activeSamples, sample) |
1842 | else |
1843 | if sample.lastActivationPart == part then |
1844 | sample.lastActivationTime = g_time |
1845 | end |
1846 | end |
1847 | elseif type == Cylindered.SOUND_TYPE_ENDING then |
1848 | local spec = self.spec_cylindered |
1849 | if spec.endingSamplesBySample[sample] == nil then |
1850 | sample.lastActivationTime = g_time |
1851 | |
1852 | table.insert(spec.endingSamples, sample) |
1853 | spec.endingSamplesBySample[sample] = sample |
1854 | else |
1855 | sample.lastActivationTime = g_time |
1856 | end |
1857 | elseif type == Cylindered.SOUND_TYPE_STARTING then |
1858 | local spec = self.spec_cylindered |
1859 | if spec.startingSamplesBySample[sample] == nil then |
1860 | if sample.loops == 0 then |
1861 | sample.loops = 1 |
1862 | end |
1863 | |
1864 | g_soundManager:playSample(sample) |
1865 | |
1866 | sample.lastActivationTime = g_time |
1867 | |
1868 | table.insert(spec.startingSamples, sample) |
1869 | spec.startingSamplesBySample[sample] = sample |
1870 | else |
1871 | sample.lastActivationTime = g_time |
1872 | end |
1873 | end |
1874 | end |
1875 | end |
1876 | end |
1877 | end |
onPostAttach
DescriptionCalled if vehicle gets attachedDefinition
onPostAttach(table attacherVehicle, integer inputJointDescIndex, integer jointDescIndex)Arguments
table | attacherVehicle | attacher vehicle |
integer | inputJointDescIndex | index of input attacher joint |
integer | jointDescIndex | index of attacher joint it gets attached to |
3251 | function Cylindered:onPostAttach(attacherVehicle, inputJointDescIndex, jointDescIndex) |
3252 | local spec = self.spec_cylindered |
3253 | |
3254 | for _, tool in ipairs(spec.movingTools) do |
3255 | local changed = false |
3256 | if tool.transSpeed ~= nil then |
3257 | local trans = tool.curTrans[tool.translationAxis] |
3258 | |
3259 | local changedTrans = false |
3260 | if tool.attachTransMax ~= nil and trans > tool.attachTransMax then |
3261 | trans = tool.attachTransMax |
3262 | changedTrans = true |
3263 | elseif tool.attachTransMin ~= nil and trans < tool.attachTransMin then |
3264 | trans = tool.attachTransMin |
3265 | changedTrans = true |
3266 | end |
3267 | if changedTrans then |
3268 | tool.curTrans[tool.translationAxis] = trans |
3269 | setTranslation(tool.node, unpack(tool.curTrans)) |
3270 | changed = true |
3271 | end |
3272 | end |
3273 | if tool.rotSpeed ~= nil then |
3274 | local rot = tool.curRot[tool.rotationAxis] |
3275 | |
3276 | local changedRot = false |
3277 | if tool.attachRotMax ~= nil and rot > tool.attachRotMax then |
3278 | rot = tool.attachRotMax |
3279 | changedRot = true |
3280 | elseif tool.attachRotMin ~= nil and rot < tool.attachRotMin then |
3281 | rot = tool.attachRotMin |
3282 | changedRot = true |
3283 | end |
3284 | if changedRot then |
3285 | tool.curRot[tool.rotationAxis] = rot |
3286 | setRotation(tool.node, unpack(tool.curRot)) |
3287 | changed = true |
3288 | end |
3289 | end |
3290 | if changed then |
3291 | Cylindered.setDirty(self, tool) |
3292 | end |
3293 | end |
3294 | end |
onPostLoad
DescriptionCalled after loadingDefinition
onPostLoad(table savegame)Arguments
table | savegame | savegame |
721 | function Cylindered:onPostLoad(savegame) |
722 | local spec = self.spec_cylindered |
723 | |
724 | for _, tool in pairs(spec.movingTools) do |
725 | if self:getIsMovingToolActive(tool) then |
726 | if tool.startRot ~= nil then |
727 | tool.curRot[tool.rotationAxis] = tool.startRot |
728 | setRotation(tool.node, unpack(tool.curRot)) |
729 | end |
730 | if tool.startTrans ~= nil then |
731 | tool.curTrans[tool.translationAxis] = tool.startTrans |
732 | setTranslation(tool.node, unpack(tool.curTrans)) |
733 | end |
734 | |
735 | if tool.animStartTime ~= nil then |
736 | self:setAnimationTime(tool.animName, tool.animStartTime, nil, false) |
737 | end |
738 | |
739 | if tool.delayedNode ~= nil then |
740 | self:setDelayedData(tool, true) |
741 | end |
742 | |
743 | if tool.isIntitialDirty then |
744 | Cylindered.setDirty(self, tool) |
745 | end |
746 | end |
747 | end |
748 | |
749 | for _, part in pairs(spec.movingParts) do |
750 | self:loadDependentAttacherJoints(self.xmlFile, part.key, part) |
751 | self:loadDependentWheels(self.xmlFile, part.key, part) |
752 | end |
753 | |
754 | for _, tool in pairs(spec.movingTools) do |
755 | self:loadDependentAttacherJoints(self.xmlFile, tool.key, tool) |
756 | self:loadDependentWheels(self.xmlFile, tool.key, tool) |
757 | end |
758 | |
759 | if self:allowLoadMovingToolStates() then |
760 | if savegame ~= nil and not savegame.resetVehicles then |
761 | local i = 0 |
762 | for _, tool in ipairs(spec.movingTools) do |
763 | if tool.saving then |
764 | if self:getIsMovingToolActive(tool) then |
765 | local toolKey = string.format("%s.cylindered.movingTool(%d)", savegame.key, i) |
766 | local changed = false |
767 | if tool.transSpeed ~= nil then |
768 | local newTrans = savegame.xmlFile:getValue(toolKey.."#translation") |
769 | if newTrans ~= nil then |
770 | if tool.transMax ~= nil then |
771 | newTrans = math.min(newTrans, tool.transMax) |
772 | end |
773 | if tool.transMin ~= nil then |
774 | newTrans = math.max(newTrans, tool.transMin) |
775 | end |
776 | end |
777 | if newTrans ~= nil and math.abs(newTrans - tool.curTrans[tool.translationAxis]) > 0.0001 then |
778 | tool.curTrans = {getTranslation(tool.node)} |
779 | tool.curTrans[tool.translationAxis] = newTrans |
780 | setTranslation(tool.node, unpack(tool.curTrans)) |
781 | changed = true |
782 | end |
783 | end |
784 | if tool.rotSpeed ~= nil then |
785 | local newRot = savegame.xmlFile:getValue(toolKey.."#rotation") |
786 | if newRot ~= nil then |
787 | if tool.rotMax ~= nil then |
788 | newRot = math.min(newRot, tool.rotMax) |
789 | end |
790 | if tool.rotMin ~= nil then |
791 | newRot = math.max(newRot, tool.rotMin) |
792 | end |
793 | end |
794 | if newRot ~= nil and math.abs(newRot - tool.curRot[tool.rotationAxis]) > 0.0001 then |
795 | tool.curRot = {getRotation(tool.node)} |
796 | tool.curRot[tool.rotationAxis] = newRot |
797 | setRotation(tool.node, unpack(tool.curRot)) |
798 | changed = true |
799 | end |
800 | end |
801 | if tool.animSpeed ~= nil then |
802 | local animTime = savegame.xmlFile:getValue(toolKey.."#animationTime") |
803 | if animTime ~= nil then |
804 | if tool.animMinTime ~= nil then |
805 | animTime = math.max(animTime, tool.animMinTime) |
806 | end |
807 | if tool.animMaxTime ~= nil then |
808 | animTime = math.min(animTime, tool.animMaxTime) |
809 | end |
810 | |
811 | tool.curAnimTime = animTime |
812 | self:setAnimationTime(tool.animName, animTime, true, false) |
813 | end |
814 | end |
815 | if changed then |
816 | Cylindered.setDirty(self, tool) |
817 | end |
818 | |
819 | if tool.delayedNode ~= nil then |
820 | self:setDelayedData(tool, true) |
821 | end |
822 | end |
823 | i = i + 1 |
824 | end |
825 | |
826 | for _, dependentTool in pairs(tool.dependentMovingTools) do |
827 | Cylindered.updateRotationBasedLimits(self, tool, dependentTool) |
828 | end |
829 | end |
830 | end |
831 | end |
832 | |
833 | self:updateEasyControl(9999, true) |
834 | self:updateCylinderedInitial(false) |
835 | |
836 | local hasTools, hasParts = #spec.movingTools > 0, #spec.movingParts > 0 |
837 | if not hasTools then |
838 | SpecializationUtil.removeEventListener(self, "onReadStream", Cylindered) |
839 | SpecializationUtil.removeEventListener(self, "onWriteStream", Cylindered) |
840 | SpecializationUtil.removeEventListener(self, "onReadUpdateStream", Cylindered) |
841 | SpecializationUtil.removeEventListener(self, "onWriteUpdateStream", Cylindered) |
842 | SpecializationUtil.removeEventListener(self, "onUpdate", Cylindered) |
843 | |
844 | if not hasParts then |
845 | SpecializationUtil.removeEventListener(self, "onUpdateTick", Cylindered) |
846 | SpecializationUtil.removeEventListener(self, "onPostUpdate", Cylindered) |
847 | SpecializationUtil.removeEventListener(self, "onPostUpdateTick", Cylindered) |
848 | end |
849 | end |
850 | |
851 | if not self.isClient or not hasTools then |
852 | SpecializationUtil.removeEventListener(self, "onDraw", Cylindered) |
853 | SpecializationUtil.removeEventListener(self, "onRegisterActionEvents", Cylindered) |
854 | end |
855 | |
856 | -- check if functional nodes are included in moving parts with limited update distance |
857 | if g_isDevelopmentVersion then |
858 | local function checkPart(part) |
859 | I3DUtil.interateRecursively(part.node, function(child, depth) |
860 | self:checkMovingPartDirtyUpdateNode(child, part) |
861 | end, math.huge) |
862 | |
863 | if part.dependentPartData ~= nil then |
864 | for _, data in pairs(part.dependentPartData) do |
865 | if data.part ~= nil then |
866 | checkPart(data.part) |
867 | end |
868 | end |
869 | end |
870 | end |
871 | |
872 | for j=1, #spec.movingParts do |
873 | local movingPart = spec.movingParts[j] |
874 | if movingPart.isActiveDirty then |
875 | if movingPart.maxUpdateDistance ~= math.huge then |
876 | checkPart(movingPart) |
877 | end |
878 | end |
879 | end |
880 | end |
881 | end |
onPostUpdate
DescriptionDefinitiononPostUpdate()Code
1986 | function Cylindered:onPostUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
1987 | local spec = self.spec_cylindered |
1988 | for _, part in pairs(spec.activeDirtyMovingParts) do |
1989 | if self.currentUpdateDistance < part.maxUpdateDistance then |
1990 | Cylindered.setDirty(self, part) |
1991 | end |
1992 | end |
1993 | |
1994 | self:updateDirtyMovingParts(dt, true) |
1995 | end |
onPostUpdateTick
DescriptionDefinitiononPostUpdateTick()Code
1999 | function Cylindered:onPostUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
2000 | -- update the moving parts that have been set dirty in the updateTick, so they won't be one frame delayed |
2001 | self:updateDirtyMovingParts(dt, false) |
2002 | end |
onReadStream
DescriptionCalled on client side on joinDefinition
onReadStream(integer streamId, integer connection)Arguments
integer | streamId | streamId |
integer | connection | connection |
945 | function Cylindered:onReadStream(streamId, connection) |
946 | local spec = self.spec_cylindered |
947 | |
948 | if connection:getIsServer() then |
949 | if streamReadBool(streamId) then |
950 | for i=1, #spec.movingTools do |
951 | local tool = spec.movingTools[i] |
952 | if tool.dirtyFlag ~= nil then |
953 | tool.networkTimeInterpolator:reset() |
954 | if tool.transSpeed ~= nil then |
955 | local newTrans = streamReadFloat32(streamId) |
956 | tool.curTrans[tool.translationAxis] = newTrans |
957 | setTranslation(tool.node, unpack(tool.curTrans)) |
958 | tool.networkInterpolators.translation:setValue(tool.curTrans[tool.translationAxis]) |
959 | end |
960 | if tool.rotSpeed ~= nil then |
961 | local newRot = streamReadFloat32(streamId) |
962 | tool.curRot[tool.rotationAxis] = newRot |
963 | setRotation(tool.node, unpack(tool.curRot)) |
964 | tool.networkInterpolators.rotation:setAngle(newRot) |
965 | end |
966 | if tool.animSpeed ~= nil then |
967 | local newAnimTime = streamReadFloat32(streamId) |
968 | tool.curAnimTime = newAnimTime |
969 | self:setAnimationTime(tool.animName, tool.curAnimTime, nil, false) |
970 | tool.networkInterpolators.animation:setValue(newAnimTime) |
971 | end |
972 | if tool.delayedNode ~= nil then |
973 | self:setDelayedData(tool, true) |
974 | end |
975 | Cylindered.setDirty(self, tool) |
976 | end |
977 | end |
978 | end |
979 | end |
980 | end |
onReadUpdateStream
DescriptionCalled on on updateDefinition
onReadUpdateStream(integer streamId, integer timestamp, table connection)Arguments
integer | streamId | stream ID |
integer | timestamp | timestamp |
table | connection | connection |
1014 | function Cylindered:onReadUpdateStream(streamId, timestamp, connection) |
1015 | local spec = self.spec_cylindered |
1016 | |
1017 | -- if server, read input from client |
1018 | if not connection:getIsServer() then |
1019 | if streamReadBool(streamId) then |
1020 | for _, tool in ipairs(spec.movingTools) do |
1021 | if tool.axisActionIndex ~= nil then |
1022 | tool.move = (streamReadUIntN(streamId, 12) / 4095 * 2 - 1) * 5 |
1023 | if math.abs(tool.move) < 0.01 then |
1024 | tool.move = 0 |
1025 | end |
1026 | end |
1027 | end |
1028 | end |
1029 | else |
1030 | -- if client, read updated attributes |
1031 | if streamReadBool(streamId) then |
1032 | for _, tool in ipairs(spec.movingTools) do |
1033 | if tool.dirtyFlag ~= nil then |
1034 | if streamReadBool(streamId) then |
1035 | tool.networkTimeInterpolator:startNewPhaseNetwork() |
1036 | |
1037 | if tool.transSpeed ~= nil then |
1038 | local newTrans = streamReadFloat32(streamId) |
1039 | if math.abs(newTrans - tool.curTrans[tool.translationAxis]) > 0.0001 then |
1040 | tool.networkInterpolators.translation:setTargetValue(newTrans) |
1041 | end |
1042 | end |
1043 | if tool.rotSpeed ~= nil then |
1044 | local newRot |
1045 | if tool.rotMin == nil or tool.rotMax == nil then |
1046 | newRot = NetworkUtil.readCompressedAngle(streamId) |
1047 | else |
1048 | if tool.syncMinRotLimits then |
1049 | tool.rotMin = streamReadFloat32(streamId) |
1050 | end |
1051 | if tool.syncMaxRotLimits then |
1052 | tool.rotMax = streamReadFloat32(streamId) |
1053 | end |
1054 | |
1055 | tool.networkInterpolators.rotation:setMinMax(tool.rotMin, tool.rotMax) |
1056 | newRot = NetworkUtil.readCompressedRange(streamId, tool.rotMin, tool.rotMax, tool.rotSendNumBits) |
1057 | end |
1058 | if math.abs(newRot - tool.curRot[tool.rotationAxis]) > 0.0001 then |
1059 | tool.networkInterpolators.rotation:setTargetAngle(newRot) |
1060 | end |
1061 | end |
1062 | if tool.animSpeed ~= nil then |
1063 | local newAnimTime = NetworkUtil.readCompressedRange(streamId, tool.animMinTime, tool.animMaxTime, tool.animSendNumBits) |
1064 | if math.abs(newAnimTime - tool.curAnimTime) > 0.0001 then |
1065 | tool.networkInterpolators.animation:setTargetValue(newAnimTime) |
1066 | end |
1067 | end |
1068 | end |
1069 | end |
1070 | end |
1071 | end |
1072 | end |
1073 | end |
onRegisterActionEvents
DescriptionDefinitiononRegisterActionEvents()Code
3225 | function Cylindered:onRegisterActionEvents(isActiveForInput, isActiveForInputIgnoreSelection) |
3226 | if self.isClient then |
3227 | local spec = self.spec_cylindered |
3228 | self:clearActionEventsTable(spec.actionEvents) |
3229 | |
3230 | -- no check for selection since movingTools can be controlled globally |
3231 | -- sub selection moving tools always require the selection of the sub index |
3232 | if isActiveForInputIgnoreSelection then |
3233 | for i=1, #spec.movingTools do |
3234 | local movingTool = spec.movingTools[i] |
3235 | local isSelectedGroup = movingTool.controlGroupIndex == 0 or movingTool.controlGroupIndex == spec.currentControlGroupIndex |
3236 | local canBeControlled = (not g_gameSettings:getValue("easyArmControl") and not movingTool.isEasyControlTarget) or movingTool.easyArmControlActive |
3237 | if movingTool.axisActionIndex ~= nil and isSelectedGroup and canBeControlled then |
3238 | local _, actionEventId = self:addPoweredActionEvent(spec.actionEvents, movingTool.axisActionIndex, self, Cylindered.actionEventInput, false, false, true, true, i, movingTool.axisActionIcon) |
3239 | g_inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_NORMAL) |
3240 | end |
3241 | end |
3242 | end |
3243 | end |
3244 | end |
onSelect
DescriptionDefinitiononSelect()Code
3298 | function Cylindered:onSelect(subSelectionIndex) |
3299 | local spec = self.spec_cylindered |
3300 | local controlGroupIndex = spec.controlGroupMapping[subSelectionIndex] |
3301 | if controlGroupIndex ~= nil then |
3302 | spec.currentControlGroupIndex = controlGroupIndex |
3303 | else |
3304 | spec.currentControlGroupIndex = 0 |
3305 | end |
3306 | end |
onUnselect
DescriptionDefinitiononUnselect()Code
3310 | function Cylindered:onUnselect() |
3311 | local spec = self.spec_cylindered |
3312 | spec.currentControlGroupIndex = 0 |
3313 | end |
onUpdate
DescriptionCalled on updateDefinition
onUpdate(float dt, boolean isActive, boolean isActiveForInput, boolean isSelected)Arguments
float | dt | time since last call in ms |
boolean | isActive | true if vehicle is active |
boolean | isActiveForInput | true if vehicle is active for input |
boolean | isSelected | true if vehicle is selected |
1132 | function Cylindered:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
1133 | local spec = self.spec_cylindered |
1134 | |
1135 | spec.movingToolNeedsSound = false |
1136 | spec.movingPartNeedsSound = false |
1137 | |
1138 | self:updateEasyControl(dt) |
1139 | |
1140 | if self.isServer then |
1141 | for i=1, #spec.movingTools do |
1142 | local tool = spec.movingTools[i] |
1143 | |
1144 | local rotSpeed = 0 |
1145 | local transSpeed = 0 |
1146 | local animSpeed = 0 |
1147 | |
1148 | local move = self:getMovingToolMoveValue(tool) |
1149 | if math.abs(move) > 0 then |
1150 | tool.externalMove = 0 |
1151 | |
1152 | if tool.rotSpeed ~= nil then |
1153 | rotSpeed = move*tool.rotSpeed |
1154 | if tool.rotAcceleration ~= nil and math.abs(rotSpeed - tool.lastRotSpeed) >= tool.rotAcceleration*dt then |
1155 | if rotSpeed > tool.lastRotSpeed then |
1156 | rotSpeed = tool.lastRotSpeed + tool.rotAcceleration*dt |
1157 | else |
1158 | rotSpeed = tool.lastRotSpeed - tool.rotAcceleration*dt |
1159 | end |
1160 | end |
1161 | end |
1162 | if tool.transSpeed ~= nil then |
1163 | transSpeed = move*tool.transSpeed |
1164 | if tool.transAcceleration ~= nil and math.abs(transSpeed - tool.lastTransSpeed) >= tool.transAcceleration*dt then |
1165 | if transSpeed > tool.lastTransSpeed then |
1166 | transSpeed = tool.lastTransSpeed + tool.transAcceleration*dt |
1167 | else |
1168 | transSpeed = tool.lastTransSpeed - tool.transAcceleration*dt |
1169 | end |
1170 | end |
1171 | end |
1172 | if tool.animSpeed ~= nil then |
1173 | animSpeed = move*tool.animSpeed |
1174 | if tool.animAcceleration ~= nil and math.abs(animSpeed - tool.lastAnimSpeed) >= tool.animAcceleration*dt then |
1175 | if animSpeed > tool.lastAnimSpeed then |
1176 | animSpeed = tool.lastAnimSpeed + tool.animAcceleration*dt |
1177 | else |
1178 | animSpeed = tool.lastAnimSpeed - tool.animAcceleration*dt |
1179 | end |
1180 | end |
1181 | end |
1182 | else |
1183 | -- decelerate |
1184 | if tool.rotAcceleration ~= nil then |
1185 | if tool.lastRotSpeed < 0 then |
1186 | rotSpeed = math.min(tool.lastRotSpeed + tool.rotAcceleration*dt, 0) |
1187 | else |
1188 | rotSpeed = math.max(tool.lastRotSpeed - tool.rotAcceleration*dt, 0) |
1189 | end |
1190 | end |
1191 | if tool.transAcceleration ~= nil then |
1192 | if tool.lastTransSpeed < 0 then |
1193 | transSpeed = math.min(tool.lastTransSpeed + tool.transAcceleration*dt, 0) |
1194 | else |
1195 | transSpeed = math.max(tool.lastTransSpeed - tool.transAcceleration*dt, 0) |
1196 | end |
1197 | end |
1198 | if tool.animAcceleration ~= nil then |
1199 | if tool.lastAnimSpeed < 0 then |
1200 | animSpeed = math.min(tool.lastAnimSpeed + tool.animAcceleration*dt, 0) |
1201 | else |
1202 | animSpeed = math.max(tool.lastAnimSpeed - tool.animAcceleration*dt, 0) |
1203 | end |
1204 | end |
1205 | end |
1206 | |
1207 | local changed = false |
1208 | if rotSpeed ~= nil and rotSpeed ~= 0 then |
1209 | changed = changed or Cylindered.setToolRotation(self, tool, rotSpeed, dt) |
1210 | else |
1211 | tool.lastRotSpeed = 0 |
1212 | end |
1213 | if transSpeed ~= nil and transSpeed ~= 0 then |
1214 | changed = changed or Cylindered.setToolTranslation(self, tool, transSpeed, dt) |
1215 | else |
1216 | tool.lastTransSpeed = 0 |
1217 | end |
1218 | if animSpeed ~= nil and animSpeed ~= 0 then |
1219 | changed = changed or Cylindered.setToolAnimation(self, tool, animSpeed, dt) |
1220 | else |
1221 | tool.lastAnimSpeed = 0 |
1222 | end |
1223 | |
1224 | for _, dependentTool in pairs(tool.dependentMovingTools) do |
1225 | if dependentTool.speedScale ~= nil then |
1226 | local isAllowed = true |
1227 | if dependentTool.requiresMovement then |
1228 | if not changed then |
1229 | isAllowed = false |
1230 | end |
1231 | end |
1232 | |
1233 | if isAllowed then |
1234 | dependentTool.movingTool.externalMove = dependentTool.speedScale * tool.move |
1235 | end |
1236 | end |
1237 | |
1238 | Cylindered.updateRotationBasedLimits(self, tool, dependentTool) |
1239 | |
1240 | self:updateDependentToolLimits(tool, dependentTool) |
1241 | end |
1242 | |
1243 | if changed then |
1244 | if tool.playSound then |
1245 | spec.movingToolNeedsSound = true |
1246 | end |
1247 | Cylindered.setDirty(self, tool) |
1248 | tool.networkPositionIsDirty = true |
1249 | self:raiseDirtyFlags(tool.dirtyFlag) |
1250 | self:raiseDirtyFlags(spec.cylinderedDirtyFlag) |
1251 | |
1252 | -- keep moving tool at least 2 frames in a row network dirty, so the client will always recieve and set the final position of the tool |
1253 | tool.networkDirtyNextFrame = true |
1254 | |
1255 | if tool.isConsumingPower then |
1256 | spec.powerConsumingTimer = spec.powerConsumingActiveTimeOffset |
1257 | end |
1258 | else |
1259 | if tool.networkDirtyNextFrame then |
1260 | self:raiseDirtyFlags(tool.dirtyFlag) |
1261 | self:raiseDirtyFlags(spec.cylinderedDirtyFlag) |
1262 | tool.networkDirtyNextFrame = nil |
1263 | end |
1264 | end |
1265 | end |
1266 | else |
1267 | -- client side |
1268 | for i=1, #spec.movingTools do |
1269 | local tool = spec.movingTools[i] |
1270 | |
1271 | tool.networkTimeInterpolator:update(dt) |
1272 | local interpolationAlpha = tool.networkTimeInterpolator:getAlpha() |
1273 | local changed = false |
1274 | |
1275 | if self:getIsMovingToolActive(tool) then |
1276 | if tool.rotSpeed ~= nil then |
1277 | local newRot = tool.networkInterpolators.rotation:getInterpolatedValue(interpolationAlpha) |
1278 | if math.abs(newRot - tool.curRot[tool.rotationAxis]) > 0.0001 then |
1279 | changed = true |
1280 | tool.curRot[tool.rotationAxis] = newRot |
1281 | setRotation(tool.node, tool.curRot[1], tool.curRot[2], tool.curRot[3]) |
1282 | end |
1283 | end |
1284 | |
1285 | if tool.transSpeed ~= nil then |
1286 | local newTrans = tool.networkInterpolators.translation:getInterpolatedValue(interpolationAlpha) |
1287 | if math.abs(newTrans - tool.curTrans[tool.translationAxis]) > 0.0001 then |
1288 | changed = true |
1289 | tool.curTrans[tool.translationAxis] = newTrans |
1290 | setTranslation(tool.node, tool.curTrans[1], tool.curTrans[2], tool.curTrans[3]) |
1291 | end |
1292 | end |
1293 | |
1294 | if tool.animSpeed ~= nil then |
1295 | local newAnimTime = tool.networkInterpolators.animation:getInterpolatedValue(interpolationAlpha) |
1296 | if math.abs(newAnimTime - tool.curAnimTime) > 0.0001 then |
1297 | changed = true |
1298 | tool.curAnimTime = newAnimTime |
1299 | self:setAnimationTime(tool.animName, newAnimTime, nil, true) |
1300 | end |
1301 | end |
1302 | |
1303 | if changed then |
1304 | Cylindered.setDirty(self, tool) |
1305 | end |
1306 | end |
1307 | |
1308 | for _, dependentTool in pairs(tool.dependentMovingTools) do |
1309 | if not dependentTool.movingTool.syncMinRotLimits or not dependentTool.movingTool.syncMaxRotLimits then |
1310 | self:updateDependentToolLimits(tool, dependentTool) |
1311 | end |
1312 | end |
1313 | |
1314 | if tool.networkTimeInterpolator:isInterpolating() then |
1315 | self:raiseActive() |
1316 | end |
1317 | end |
1318 | end |
1319 | |
1320 | for i=1, #spec.movingTools do |
1321 | local tool = spec.movingTools[i] |
1322 | if tool.delayedHistoryIndex ~= nil and tool.delayedHistoryIndex > 0 then |
1323 | self:updateDelayedTool(tool) |
1324 | end |
1325 | |
1326 | if tool.smoothedMove ~= 0 then |
1327 | if tool.lastInputTime + 50 < g_time then |
1328 | tool.smoothedMove = 0 |
1329 | end |
1330 | end |
1331 | end |
1332 | |
1333 | if spec.powerConsumingTimer > 0 then |
1334 | spec.powerConsumingTimer = spec.powerConsumingTimer - dt |
1335 | end |
1336 | |
1337 | if next(spec.activeSamples) ~= nil then |
1338 | self:raiseActive() |
1339 | end |
1340 | end |
onUpdateEnd
DescriptionDefinitiononUpdateEnd()Code
1973 | function Cylindered:onUpdateEnd(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
1974 | -- force update of all moving parts independent of camera distance right before vehicles starts to sleep |
1975 | -- so if we get into the moving part update distance agan we are already in the right state without waking up the vehicle |
1976 | local spec = self.spec_cylindered |
1977 | for _, part in pairs(spec.activeDirtyMovingParts) do |
1978 | Cylindered.setDirty(self, part) |
1979 | end |
1980 | |
1981 | self:updateDirtyMovingParts(dt, true) |
1982 | end |
onUpdateTick
DescriptionDefinitiononUpdateTick()Code
1916 | function Cylindered:onUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
1917 | if self.isClient then |
1918 | local spec = self.spec_cylindered |
1919 | for _,movingTool in pairs(spec.movingTools) do |
1920 | if movingTool.axisActionIndex ~= nil then |
1921 | -- check only movingTools from selected control group since the other movingTools action events are not registered |
1922 | if spec.currentControlGroupIndex == movingTool.controlGroupIndex then |
1923 | local actionEvent = spec.actionEvents[movingTool.axisActionIndex] |
1924 | if actionEvent ~= nil then |
1925 | g_inputBinding:setActionEventActive(actionEvent.actionEventId, self:getIsMovingToolActive(movingTool)) |
1926 | end |
1927 | end |
1928 | end |
1929 | end |
1930 | |
1931 | for i=#spec.activeSamples, 1, -1 do |
1932 | local sample = spec.activeSamples[i] |
1933 | if sample.lastActivationTime + dt * 3 < g_time then |
1934 | if sample.lastActivationTime + dt * 3 + sample.dropOffTime >= g_time then |
1935 | if not sample.dropOffActive then |
1936 | sample.dropOffActive = true |
1937 | g_soundManager:setSamplePitchOffset(sample, g_soundManager:getCurrentSamplePitch(sample) * (sample.dropOffFactor - 1)) |
1938 | end |
1939 | else |
1940 | sample.dropOffActive = false |
1941 | g_soundManager:setSamplePitchOffset(sample, 0) |
1942 | g_soundManager:stopSample(sample) |
1943 | table.remove(spec.activeSamples, i) |
1944 | end |
1945 | end |
1946 | end |
1947 | |
1948 | for i=#spec.endingSamples, 1, -1 do |
1949 | local sample = spec.endingSamples[i] |
1950 | if sample.lastActivationTime + dt < g_time then |
1951 | if sample.loops == 0 then |
1952 | sample.loops = 1 |
1953 | end |
1954 | |
1955 | g_soundManager:playSample(sample) |
1956 | table.remove(spec.endingSamples, i) |
1957 | spec.endingSamplesBySample[sample] = nil |
1958 | end |
1959 | end |
1960 | |
1961 | for i=#spec.startingSamples, 1, -1 do |
1962 | local sample = spec.startingSamples[i] |
1963 | if sample.lastActivationTime + dt < g_time then |
1964 | table.remove(spec.startingSamples, i) |
1965 | spec.startingSamplesBySample[sample] = nil |
1966 | end |
1967 | end |
1968 | end |
1969 | end |
onVehicleSettingChanged
DescriptionCalled when vehicle settings changeDefinition
onVehicleSettingChanged()Code
3333 | function Cylindered:onVehicleSettingChanged(gameSettingId, state) |
3334 | if gameSettingId == GameSettings.SETTING.EASY_ARM_CONTROL then |
3335 | self:setIsEasyControlActive(state) |
3336 | end |
3337 | end |
onWriteStream
DescriptionCalled on server side on joinDefinition
onWriteStream(integer streamId, integer connection)Arguments
integer | streamId | streamId |
integer | connection | connection |
986 | function Cylindered:onWriteStream(streamId, connection) |
987 | local spec = self.spec_cylindered |
988 | |
989 | if not connection:getIsServer() then |
990 | if streamWriteBool(streamId, self:allowLoadMovingToolStates()) then |
991 | for i=1, #spec.movingTools do |
992 | local tool = spec.movingTools[i] |
993 | if tool.dirtyFlag ~= nil then |
994 | if tool.transSpeed ~= nil then |
995 | streamWriteFloat32(streamId, tool.curTrans[tool.translationAxis]) |
996 | end |
997 | if tool.rotSpeed ~= nil then |
998 | streamWriteFloat32(streamId, tool.curRot[tool.rotationAxis]) |
999 | end |
1000 | if tool.animSpeed ~= nil then |
1001 | streamWriteFloat32(streamId, tool.curAnimTime) |
1002 | end |
1003 | end |
1004 | end |
1005 | end |
1006 | end |
1007 | end |
onWriteUpdateStream
DescriptionCalled on on updateDefinition
onWriteUpdateStream(integer streamId, table connection, integer dirtyMask)Arguments
integer | streamId | stream ID |
table | connection | connection |
integer | dirtyMask | dirty mask |
1080 | function Cylindered:onWriteUpdateStream(streamId, connection, dirtyMask) |
1081 | local spec = self.spec_cylindered |
1082 | |
1083 | -- if client, send input to server |
1084 | if connection:getIsServer() then |
1085 | if streamWriteBool(streamId, bitAND(dirtyMask, spec.cylinderedInputDirtyFlag) ~= 0) then |
1086 | for _, tool in ipairs(spec.movingTools) do |
1087 | if tool.axisActionIndex ~= nil then |
1088 | local value = (MathUtil.clamp(tool.moveToSend / 5, -1, 1) + 1) / 2 * 4095 |
1089 | streamWriteUIntN(streamId, value, 12) |
1090 | end |
1091 | end |
1092 | end |
1093 | else |
1094 | -- if server, send updated attributes |
1095 | if streamWriteBool(streamId, bitAND(dirtyMask, spec.cylinderedDirtyFlag) ~= 0) then |
1096 | for _, tool in ipairs(spec.movingTools) do |
1097 | if tool.dirtyFlag ~= nil then |
1098 | if streamWriteBool(streamId, bitAND(dirtyMask, tool.dirtyFlag) ~= 0 and self:getIsMovingToolActive(tool)) then |
1099 | if tool.transSpeed ~= nil then |
1100 | streamWriteFloat32(streamId, tool.curTrans[tool.translationAxis]) |
1101 | end |
1102 | if tool.rotSpeed ~= nil then |
1103 | local rot = tool.curRot[tool.rotationAxis] |
1104 | if tool.rotMin == nil or tool.rotMax == nil then |
1105 | NetworkUtil.writeCompressedAngle(streamId, rot) |
1106 | else |
1107 | if tool.syncMinRotLimits then |
1108 | streamWriteFloat32(streamId, tool.rotMin) |
1109 | end |
1110 | if tool.syncMaxRotLimits then |
1111 | streamWriteFloat32(streamId, tool.rotMax) |
1112 | end |
1113 | NetworkUtil.writeCompressedRange(streamId, rot, tool.rotMin, tool.rotMax, tool.rotSendNumBits) |
1114 | end |
1115 | end |
1116 | if tool.animSpeed ~= nil then |
1117 | NetworkUtil.writeCompressedRange(streamId, tool.curAnimTime, tool.animMinTime, tool.animMaxTime, tool.animSendNumBits) |
1118 | end |
1119 | end |
1120 | end |
1121 | end |
1122 | end |
1123 | end |
1124 | end |
prerequisitesPresent
DescriptionChecks if all prerequisite specializations are loadedDefinition
prerequisitesPresent(table specializations)Arguments
table | specializations | specializations |
boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
51 | function Cylindered.prerequisitesPresent(specializations) |
52 | return SpecializationUtil.hasSpecialization(VehicleSettings, specializations) |
53 | end |
registerCopyLocalDirectionXMLPaths
DescriptionDefinitionregisterCopyLocalDirectionXMLPaths()Code
292 | function Cylindered.registerCopyLocalDirectionXMLPaths(schema, basePath) |
293 | schema:register(XMLValueType.NODE_INDEX, basePath .. ".copyLocalDirectionPart(?)#node", "Copy local direction part") |
294 | schema:register(XMLValueType.VECTOR_3, basePath .. ".copyLocalDirectionPart(?)#dirScale", "Direction scale") |
295 | schema:register(XMLValueType.VECTOR_3, basePath .. ".copyLocalDirectionPart(?)#upScale", "Up vector scale") |
296 | |
297 | Cylindered.registerDependentComponentJointXMLPaths(schema, basePath .. ".copyLocalDirectionPart(?)") |
298 | end |
registerDependentAnimationXMLPaths
DescriptionDefinitionregisterDependentAnimationXMLPaths()Code
302 | function Cylindered.registerDependentAnimationXMLPaths(schema, basePath) |
303 | schema:register(XMLValueType.STRING, basePath .. ".dependentAnimation(?)#name", "Dependent animation name") |
304 | schema:register(XMLValueType.INT, basePath .. ".dependentAnimation(?)#translationAxis", "Translation axis") |
305 | schema:register(XMLValueType.INT, basePath .. ".dependentAnimation(?)#rotationAxis", "Rotation axis") |
306 | schema:register(XMLValueType.INT, basePath .. ".dependentAnimation(?)#useTranslatingPartIndex", "Use translation part index") |
307 | schema:register(XMLValueType.FLOAT, basePath .. ".dependentAnimation(?)#minValue", "Min. reference value") |
308 | schema:register(XMLValueType.FLOAT, basePath .. ".dependentAnimation(?)#maxValue", "Max. reference value") |
309 | schema:register(XMLValueType.BOOL, basePath .. ".dependentAnimation(?)#invert", "Invert reference value", false) |
310 | end |
registerDependentComponentJointXMLPaths
DescriptionDefinitionregisterDependentComponentJointXMLPaths()Code
285 | function Cylindered.registerDependentComponentJointXMLPaths(schema, basePath) |
286 | schema:register(XMLValueType.INT, basePath .. ".componentJoint(?)#index", "Dependent component joint index") |
287 | schema:register(XMLValueType.INT, basePath .. ".componentJoint(?)#anchorActor", "Dependent component anchor actor") |
288 | end |
registerEventListeners
DescriptionDefinitionregisterEventListeners()Code
378 | function Cylindered.registerEventListeners(vehicleType) |
379 | SpecializationUtil.registerEventListener(vehicleType, "onLoad", Cylindered) |
380 | SpecializationUtil.registerEventListener(vehicleType, "onPostLoad", Cylindered) |
381 | SpecializationUtil.registerEventListener(vehicleType, "onLoadFinished", Cylindered) |
382 | SpecializationUtil.registerEventListener(vehicleType, "onDelete", Cylindered) |
383 | SpecializationUtil.registerEventListener(vehicleType, "onReadStream", Cylindered) |
384 | SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", Cylindered) |
385 | SpecializationUtil.registerEventListener(vehicleType, "onReadUpdateStream", Cylindered) |
386 | SpecializationUtil.registerEventListener(vehicleType, "onWriteUpdateStream", Cylindered) |
387 | SpecializationUtil.registerEventListener(vehicleType, "onUpdate", Cylindered) |
388 | SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", Cylindered) |
389 | SpecializationUtil.registerEventListener(vehicleType, "onUpdateEnd", Cylindered) |
390 | SpecializationUtil.registerEventListener(vehicleType, "onPostUpdate", Cylindered) |
391 | SpecializationUtil.registerEventListener(vehicleType, "onPostUpdateTick", Cylindered) |
392 | SpecializationUtil.registerEventListener(vehicleType, "onDraw", Cylindered) |
393 | SpecializationUtil.registerEventListener(vehicleType, "onRegisterActionEvents", Cylindered) |
394 | SpecializationUtil.registerEventListener(vehicleType, "onPostAttach", Cylindered) |
395 | SpecializationUtil.registerEventListener(vehicleType, "onSelect", Cylindered) |
396 | SpecializationUtil.registerEventListener(vehicleType, "onUnselect", Cylindered) |
397 | SpecializationUtil.registerEventListener(vehicleType, "onDeactivate", Cylindered) |
398 | SpecializationUtil.registerEventListener(vehicleType, "onAnimationPartChanged", Cylindered) |
399 | SpecializationUtil.registerEventListener(vehicleType, "onVehicleSettingChanged", Cylindered) |
400 | end |
registerEvents
DescriptionDefinitionregisterEvents()Code
314 | function Cylindered.registerEvents(vehicleType) |
315 | SpecializationUtil.registerEvent(vehicleType, "onMovingToolChanged") |
316 | end |
registerFunctions
DescriptionDefinitionregisterFunctions()Code
320 | function Cylindered.registerFunctions(vehicleType) |
321 | SpecializationUtil.registerFunction(vehicleType, "loadMovingPartFromXML", Cylindered.loadMovingPartFromXML) |
322 | SpecializationUtil.registerFunction(vehicleType, "loadMovingToolFromXML", Cylindered.loadMovingToolFromXML) |
323 | SpecializationUtil.registerFunction(vehicleType, "loadDependentMovingTools", Cylindered.loadDependentMovingTools) |
324 | SpecializationUtil.registerFunction(vehicleType, "loadEasyArmControlFromXML", Cylindered.loadEasyArmControlFromXML) |
325 | SpecializationUtil.registerFunction(vehicleType, "loadDependentParts", Cylindered.loadDependentParts) |
326 | SpecializationUtil.registerFunction(vehicleType, "resolveDependentPartData", Cylindered.resolveDependentPartData) |
327 | SpecializationUtil.registerFunction(vehicleType, "loadDependentComponentJoints", Cylindered.loadDependentComponentJoints) |
328 | SpecializationUtil.registerFunction(vehicleType, "loadDependentAttacherJoints", Cylindered.loadDependentAttacherJoints) |
329 | SpecializationUtil.registerFunction(vehicleType, "loadDependentWheels", Cylindered.loadDependentWheels) |
330 | SpecializationUtil.registerFunction(vehicleType, "loadDependentTranslatingParts", Cylindered.loadDependentTranslatingParts) |
331 | SpecializationUtil.registerFunction(vehicleType, "loadExtraDependentParts", Cylindered.loadExtraDependentParts) |
332 | SpecializationUtil.registerFunction(vehicleType, "loadDependentAnimations", Cylindered.loadDependentAnimations) |
333 | SpecializationUtil.registerFunction(vehicleType, "loadCopyLocalDirectionParts", Cylindered.loadCopyLocalDirectionParts) |
334 | SpecializationUtil.registerFunction(vehicleType, "loadRotationBasedLimits", Cylindered.loadRotationBasedLimits) |
335 | SpecializationUtil.registerFunction(vehicleType, "checkMovingPartDirtyUpdateNode", Cylindered.checkMovingPartDirtyUpdateNode) |
336 | SpecializationUtil.registerFunction(vehicleType, "updateDirtyMovingParts", Cylindered.updateDirtyMovingParts) |
337 | SpecializationUtil.registerFunction(vehicleType, "setMovingToolDirty", Cylindered.setMovingToolDirty) |
338 | SpecializationUtil.registerFunction(vehicleType, "updateCylinderedInitial", Cylindered.updateCylinderedInitial) |
339 | SpecializationUtil.registerFunction(vehicleType, "allowLoadMovingToolStates", Cylindered.allowLoadMovingToolStates) |
340 | SpecializationUtil.registerFunction(vehicleType, "getMovingToolByNode", Cylindered.getMovingToolByNode) |
341 | SpecializationUtil.registerFunction(vehicleType, "getMovingPartByNode", Cylindered.getMovingPartByNode) |
342 | SpecializationUtil.registerFunction(vehicleType, "getTranslatingPartByNode", Cylindered.getTranslatingPartByNode) |
343 | SpecializationUtil.registerFunction(vehicleType, "getIsMovingToolActive", Cylindered.getIsMovingToolActive) |
344 | SpecializationUtil.registerFunction(vehicleType, "getIsMovingPartActive", Cylindered.getIsMovingPartActive) |
345 | SpecializationUtil.registerFunction(vehicleType, "getMovingToolMoveValue", Cylindered.getMovingToolMoveValue) |
346 | SpecializationUtil.registerFunction(vehicleType, "setDelayedData", Cylindered.setDelayedData) |
347 | SpecializationUtil.registerFunction(vehicleType, "updateDelayedTool", Cylindered.updateDelayedTool) |
348 | SpecializationUtil.registerFunction(vehicleType, "updateEasyControl", Cylindered.updateEasyControl) |
349 | SpecializationUtil.registerFunction(vehicleType, "setIsEasyControlActive", Cylindered.setIsEasyControlActive) |
350 | SpecializationUtil.registerFunction(vehicleType, "updateExtraDependentParts", Cylindered.updateExtraDependentParts) |
351 | SpecializationUtil.registerFunction(vehicleType, "updateDependentAnimations", Cylindered.updateDependentAnimations) |
352 | SpecializationUtil.registerFunction(vehicleType, "updateDependentToolLimits", Cylindered.updateDependentToolLimits) |
353 | SpecializationUtil.registerFunction(vehicleType, "onMovingPartSoundEvent", Cylindered.onMovingPartSoundEvent) |
354 | SpecializationUtil.registerFunction(vehicleType, "updateMovingToolSoundEvents", Cylindered.updateMovingToolSoundEvents) |
355 | end |
registerOverwrittenFunctions
DescriptionDefinitionregisterOverwrittenFunctions()Code
359 | function Cylindered.registerOverwrittenFunctions(vehicleType) |
360 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "isDetachAllowed", Cylindered.isDetachAllowed) |
361 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "loadObjectChangeValuesFromXML", Cylindered.loadObjectChangeValuesFromXML) |
362 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "setObjectChangeValues", Cylindered.setObjectChangeValues) |
363 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "loadDischargeNode", Cylindered.loadDischargeNode) |
364 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getDischargeNodeEmptyFactor", Cylindered.getDischargeNodeEmptyFactor) |
365 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "loadShovelNode", Cylindered.loadShovelNode) |
366 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getShovelNodeIsActive", Cylindered.getShovelNodeIsActive) |
367 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "loadDynamicMountGrabFromXML", Cylindered.loadDynamicMountGrabFromXML) |
368 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getIsDynamicMountGrabOpened", Cylindered.getIsDynamicMountGrabOpened) |
369 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "setComponentJointFrame", Cylindered.setComponentJointFrame) |
370 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getAdditionalSchemaText", Cylindered.getAdditionalSchemaText) |
371 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getWearMultiplier", Cylindered.getWearMultiplier) |
372 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getDoConsumePtoPower", Cylindered.getDoConsumePtoPower) |
373 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getConsumingLoad", Cylindered.getConsumingLoad) |
374 | end |
resolveDependentPartData
DescriptionResolve loaded dependent part data into real moving parts and toolsDefinition
resolveDependentPartData(table dependentPartData, table referenceNodes)Arguments
table | dependentPartData | dependentPartData |
table | referenceNodes | referenceNodes |
2493 | function Cylindered:resolveDependentPartData(dependentPartData, referenceNodes) |
2494 | for _, dependentPart in pairs(dependentPartData) do |
2495 | if dependentPart.part == nil then |
2496 | if referenceNodes[dependentPart.node] ~= nil then |
2497 | for j=1, #referenceNodes[dependentPart.node] do |
2498 | local depPart = referenceNodes[dependentPart.node][j] |
2499 | if j == 1 then |
2500 | dependentPart.part = depPart |
2501 | depPart.isDependentPart = true |
2502 | else |
2503 | table.insert(dependentPartData, { |
2504 | node = dependentPart.node, |
2505 | maxUpdateDistance = dependentPart.maxUpdateDistance, |
2506 | part = depPart, |
2507 | }) |
2508 | |
2509 | depPart.isDependentPart = true |
2510 | end |
2511 | end |
2512 | end |
2513 | end |
2514 | end |
2515 | |
2516 | for j=#dependentPartData, 1, -1 do |
2517 | local data = dependentPartData[j] |
2518 | if data.part == nil then |
2519 | table.remove(dependentPartData, j) |
2520 | end |
2521 | end |
2522 | end |
saveToXMLFile
DescriptionDefinitionsaveToXMLFile()Code
918 | function Cylindered:saveToXMLFile(xmlFile, key, usedModNames) |
919 | local spec = self.spec_cylindered |
920 | |
921 | local index = 0 |
922 | for _, tool in ipairs(spec.movingTools) do |
923 | if tool.saving then |
924 | local toolKey = string.format("%s.movingTool(%d)", key, index) |
925 | |
926 | if tool.transSpeed ~= nil then |
927 | xmlFile:setValue(toolKey.."#translation", tool.curTrans[tool.translationAxis]) |
928 | end |
929 | if tool.rotSpeed ~= nil then |
930 | xmlFile:setValue(toolKey.."#rotation", tool.curRot[tool.rotationAxis]) |
931 | end |
932 | if tool.animSpeed ~= nil then |
933 | xmlFile:setValue(toolKey.."#animationTime", tool.curAnimTime) |
934 | end |
935 | |
936 | index = index + 1 |
937 | end |
938 | end |
939 | end |
setAbsoluteToolRotation
DescriptionSet absolute tool rotationDefinition
setAbsoluteToolRotation(table tool, float rotation, bool updateDelayedNodes)Arguments
table | tool | tool |
float | rotation | rotation |
bool | updateDelayedNodes | update delayed nodes |
3450 | function Cylindered.setAbsoluteToolRotation(self, tool, rotation, updateDelayedNodes) |
3451 | tool.curRot[1], tool.curRot[2], tool.curRot[3] = getRotation(tool.node) |
3452 | local oldRot = tool.curRot[tool.rotationAxis] |
3453 | if Cylindered.setToolRotation(self, tool, nil, 0, rotation - oldRot) then |
3454 | Cylindered.setDirty(self, tool) |
3455 | |
3456 | if updateDelayedNodes ~= nil and updateDelayedNodes then |
3457 | self:updateDelayedTool(tool) |
3458 | end |
3459 | |
3460 | self:raiseDirtyFlags(tool.dirtyFlag) |
3461 | self:raiseDirtyFlags(self.spec_cylindered.cylinderedDirtyFlag) |
3462 | end |
3463 | end |
setAbsoluteToolTranslation
DescriptionSet absolute tool translationDefinition
setAbsoluteToolTranslation(table tool, float translation)Arguments
table | tool | tool |
float | translation | translation |
3382 | function Cylindered.setAbsoluteToolTranslation(self, tool, translation) |
3383 | tool.curTrans[1], tool.curTrans[2], tool.curTrans[3] = getTranslation(tool.node) |
3384 | local oldTrans = tool.curTrans[tool.translationAxis] |
3385 | if Cylindered.setToolTranslation(self, tool, nil, 0, translation - oldTrans) then |
3386 | Cylindered.setDirty(self, tool) |
3387 | |
3388 | self:raiseDirtyFlags(tool.dirtyFlag) |
3389 | self:raiseDirtyFlags(self.spec_cylindered.cylinderedDirtyFlag) |
3390 | end |
3391 | end |
setComponentJointFrame
DescriptionDefinitionsetComponentJointFrame()Code
3154 | function Cylindered:setComponentJointFrame(superFunc, jointDesc, anchorActor) |
3155 | superFunc(self, jointDesc, anchorActor) |
3156 | |
3157 | -- update the joint to component offset |
3158 | local spec = self.spec_cylindered |
3159 | for _, movingTool in ipairs(spec.movingTools) do |
3160 | for _, componentJoint in ipairs(movingTool.componentJoints) do |
3161 | local componentJointDesc = self.componentJoints[componentJoint.index] |
3162 | |
3163 | local jointNode = componentJointDesc.jointNode |
3164 | if componentJoint.anchorActor == 1 then |
3165 | jointNode = componentJointDesc.jointNodeActor1 |
3166 | end |
3167 | |
3168 | local node = self.components[componentJointDesc.componentIndices[2]].node |
3169 | componentJoint.x, componentJoint.y, componentJoint.z = localToLocal(node, jointNode, 0,0,0) |
3170 | componentJoint.upX, componentJoint.upY, componentJoint.upZ = localDirectionToLocal(node, jointNode, 0,1,0) |
3171 | componentJoint.dirX, componentJoint.dirY, componentJoint.dirZ = localDirectionToLocal(node, jointNode, 0,0,1) |
3172 | end |
3173 | end |
3174 | end |
setDelayedData
DescriptionDefinitionsetDelayedData()Code
1345 | function Cylindered:setDelayedData(tool, immediate) |
1346 | local x, y, z = getTranslation(tool.node) |
1347 | local rx, ry, rz = getRotation(tool.node) |
1348 | |
1349 | tool.delayedHistroyData[tool.delayedFrames] = {rot = {rx, ry, rz}, trans = {x, y, z}} |
1350 | if immediate then |
1351 | for i=1, tool.delayedFrames - 1 do |
1352 | tool.delayedHistroyData[i] = tool.delayedHistroyData[tool.delayedFrames] |
1353 | end |
1354 | end |
1355 | |
1356 | tool.delayedHistoryIndex = tool.delayedFrames |
1357 | end |
setDirty
DescriptionSet dirtyDefinition
setDirty(table part)Arguments
table | part | part to set dirty |
3520 | function Cylindered.setDirty(self, part) |
3521 | if not part.isDirty or self.spec_cylindered.isLoading then -- during loading we always allow setting dirty since we do not reset it |
3522 | part.isDirty = true |
3523 | self.anyMovingPartsDirty = true |
3524 | |
3525 | if part.delayedNode ~= nil then |
3526 | self:setDelayedData(part) |
3527 | end |
3528 | |
3529 | -- on moving tools we update the wheels and attacher joints on dirty since the are updated from external influences (e.g. animations) |
3530 | -- on moving parts the part is updated first and then the wheels and attacher joints are updated |
3531 | if part.isTool then |
3532 | Cylindered.updateAttacherJoints(self, part) |
3533 | Cylindered.updateWheels(self, part) |
3534 | end |
3535 | |
3536 | for _, data in pairs(part.dependentPartData) do |
3537 | if self.currentUpdateDistance < data.maxUpdateDistance then |
3538 | Cylindered.setDirty(self, data.part) |
3539 | end |
3540 | end |
3541 | end |
3542 | end |
setIsEasyControlActive
DescriptionDefinitionsetIsEasyControlActive()Code
1711 | function Cylindered:setIsEasyControlActive(state) |
1712 | local spec = self.spec_cylindered |
1713 | if self.isServer then |
1714 | local easyArmControl = spec.easyArmControl |
1715 | if easyArmControl ~= nil then |
1716 | local targetYTool = self:getMovingToolByNode(easyArmControl.targetNodeY) |
1717 | local targetZTool = self:getMovingToolByNode(easyArmControl.targetNodeZ) |
1718 | |
1719 | if state then |
1720 | local origin = getParent(easyArmControl.targetNodeY) |
1721 | if origin == easyArmControl.targetNodeZ then |
1722 | origin = getParent(easyArmControl.targetNodeZ) |
1723 | end |
1724 | |
1725 | local _, y, _ = localToLocal(easyArmControl.targetRefNode, origin, 0, 0, 0) |
1726 | local _, oldY, _ = getTranslation(easyArmControl.targetNodeY) |
1727 | if Cylindered.setToolTranslation(self, targetYTool, nil, 0, y-oldY) then |
1728 | Cylindered.setDirty(self, targetYTool) |
1729 | end |
1730 | |
1731 | local z |
1732 | _, _, z = localToLocal(easyArmControl.targetRefNode, origin, 0, 0, 0) |
1733 | local _, _, oldZ = getTranslation(easyArmControl.targetNodeZ) |
1734 | if Cylindered.setToolTranslation(self, targetZTool, nil, 0, z-oldZ) then |
1735 | Cylindered.setDirty(self, targetZTool) |
1736 | end |
1737 | |
1738 | easyArmControl.lastValidPositionY[1], easyArmControl.lastValidPositionY[2], easyArmControl.lastValidPositionY[3] = getTranslation(easyArmControl.targetNodeY) |
1739 | easyArmControl.lastValidPositionZ[1], easyArmControl.lastValidPositionZ[2], easyArmControl.lastValidPositionZ[3] = getTranslation(easyArmControl.targetNodeZ) |
1740 | |
1741 | self:raiseDirtyFlags(spec.cylinderedDirtyFlag) |
1742 | end |
1743 | |
1744 | easyArmControl.state = state |
1745 | end |
1746 | end |
1747 | |
1748 | self:requestActionEventUpdate() |
1749 | end |
setMovingToolDirty
DescriptionSet moving tool dirtyDefinition
setMovingToolDirty(integer node, bool forceUpdate, float dt)Arguments
integer | node | node id |
bool | forceUpdate | force immediate update of moving tool and dependent parts |
float | dt | time since last call (only if forceUpdate is set) |
2797 | function Cylindered:setMovingToolDirty(node, forceUpdate, dt) |
2798 | local spec = self.spec_cylindered |
2799 | |
2800 | local tool = spec.nodesToMovingTools[node] |
2801 | if tool ~= nil then |
2802 | -- update curTrans and curRot values + moving tool action sounds |
2803 | if tool.transSpeed ~= nil then |
2804 | local oldTrans = tool.curTrans[tool.translationAxis] |
2805 | tool.curTrans[1], tool.curTrans[2], tool.curTrans[3] = getTranslation(tool.node) |
2806 | local newTrans = tool.curTrans[tool.translationAxis] |
2807 | |
2808 | local diff = newTrans - oldTrans |
2809 | if math.abs(diff) > 0.0001 then |
2810 | self:updateMovingToolSoundEvents(tool, diff > 0, math.abs(newTrans-(tool.transMax or math.huge)) < 0.0001 or math.abs(newTrans-(tool.transMin or math.huge)) < 0.0001, math.abs(oldTrans-(tool.transMax or math.huge)) < 0.0001 or math.abs(oldTrans-(tool.transMin or math.huge)) < 0.0001) |
2811 | end |
2812 | end |
2813 | |
2814 | if tool.rotSpeed ~= nil then |
2815 | local oldRot = tool.curRot[tool.rotationAxis] |
2816 | tool.curRot[1], tool.curRot[2], tool.curRot[3] = getRotation(tool.node) |
2817 | local newRot = tool.curRot[tool.rotationAxis] |
2818 | |
2819 | local diff = newRot - oldRot |
2820 | if math.abs(diff) > 0.0001 then |
2821 | self:updateMovingToolSoundEvents(tool, diff > 0, math.abs(newRot-(tool.rotMax or math.huge)) < 0.0001 or math.abs(newRot-(tool.rotMin or math.huge)) < 0.0001, math.abs(oldRot-(tool.rotMax or math.huge)) < 0.0001 or math.abs(oldRot-(tool.rotMin or math.huge)) < 0.0001) |
2822 | end |
2823 | end |
2824 | |
2825 | Cylindered.setDirty(self, tool) |
2826 | |
2827 | if not self.isServer and self.isClient then |
2828 | tool.networkInterpolators.translation:setValue(tool.curTrans[tool.translationAxis]) |
2829 | tool.networkInterpolators.rotation:setAngle(tool.curRot[tool.rotationAxis]) |
2830 | end |
2831 | |
2832 | if forceUpdate or (self.finishedFirstUpdate and not self.isActive) then |
2833 | self:updateDirtyMovingParts(dt or g_currentDt, true) |
2834 | end |
2835 | end |
2836 | end |
setObjectChangeValues
DescriptionSets object change valuesDefinition
setObjectChangeValues(table object, boolean isActive)Arguments
table | object | object |
boolean | isActive | is active |
3006 | function Cylindered:setObjectChangeValues(superFunc, object, isActive) |
3007 | superFunc(self, object, isActive) |
3008 | |
3009 | local spec = self.spec_cylindered |
3010 | |
3011 | if spec.nodesToMovingTools ~= nil and spec.nodesToMovingTools[object.node] ~= nil then |
3012 | local movingTool = spec.nodesToMovingTools[object.node] |
3013 | if isActive then |
3014 | movingTool.rotMax = object.movingToolRotMaxActive |
3015 | movingTool.rotMin = object.movingToolRotMinActive |
3016 | movingTool.transMax = object.movingToolTransMaxActive |
3017 | movingTool.transMin = object.movingToolTransMinActive |
3018 | else |
3019 | movingTool.rotMax = object.movingToolRotMaxInactive |
3020 | movingTool.rotMin = object.movingToolRotMinInactive |
3021 | movingTool.transMax = object.movingToolTransMaxInactive |
3022 | movingTool.transMin = object.movingToolTransMinInactive |
3023 | end |
3024 | end |
3025 | end |
setToolAnimation
DescriptionSet tool animationDefinition
setToolAnimation(table tool, float animSpeed, float dt)Arguments
table | tool | tool |
float | animSpeed | animation speed |
float | dt | time since last call in ms |
boolean | changed | animation changed |
3471 | function Cylindered.setToolAnimation(self, tool, animSpeed, dt) |
3472 | tool.curAnimTime = self:getAnimationTime(tool.animName) |
3473 | |
3474 | local newAnimTime = tool.curAnimTime+animSpeed*dt |
3475 | local oldAnimTime = tool.curAnimTime |
3476 | |
3477 | if tool.animMaxTime ~= nil then |
3478 | newAnimTime = math.min(newAnimTime, tool.animMaxTime) |
3479 | end |
3480 | if tool.animMinTime ~= nil then |
3481 | newAnimTime = math.max(newAnimTime, tool.animMinTime) |
3482 | end |
3483 | local diff = newAnimTime - tool.curAnimTime |
3484 | if dt ~= 0 then |
3485 | tool.lastAnimSpeed = diff / dt |
3486 | end |
3487 | if math.abs(diff) > 0.0001 then |
3488 | tool.curAnimTime = newAnimTime |
3489 | self:setAnimationTime(tool.animName, newAnimTime, nil, true) |
3490 | |
3491 | self:updateMovingToolSoundEvents(tool, diff > 0, newAnimTime == tool.animMaxTime or newAnimTime == tool.animMinTime or newAnimTime == 0 or newAnimTime == 1, oldAnimTime == tool.animMaxTime or oldAnimTime == tool.animMinTime or oldAnimTime == 0 or oldAnimTime == 1) |
3492 | |
3493 | SpecializationUtil.raiseEvent(self, "onMovingToolChanged", tool, animSpeed, dt) |
3494 | return true |
3495 | end |
3496 | |
3497 | return false |
3498 | end |
setToolRotation
DescriptionSet tool rotationDefinition
setToolRotation(table tool, float rotSpeed, float dt, float delta)Arguments
table | tool | tool |
float | rotSpeed | rotation speed |
float | dt | time since last call in ms |
float | delta | delta rotation |
boolean | changed | rotation changed |
3400 | function Cylindered.setToolRotation(self, tool, rotSpeed, dt, delta) |
3401 | tool.curRot[1], tool.curRot[2], tool.curRot[3] = getRotation(tool.node) |
3402 | local newRot = tool.curRot[tool.rotationAxis] |
3403 | local oldRot = newRot |
3404 | if rotSpeed ~= nil then |
3405 | newRot = newRot + rotSpeed*dt |
3406 | else |
3407 | newRot = newRot + delta |
3408 | end |
3409 | if tool.rotMax ~= nil then |
3410 | newRot = math.min(newRot, tool.rotMax) |
3411 | end |
3412 | if tool.rotMin ~= nil then |
3413 | newRot = math.max(newRot, tool.rotMin) |
3414 | end |
3415 | local diff = newRot - tool.curRot[tool.rotationAxis] |
3416 | if rotSpeed ~= nil then |
3417 | if dt ~= 0 then |
3418 | tool.lastRotSpeed = diff/dt |
3419 | end |
3420 | end |
3421 | |
3422 | if math.abs(diff) > 0.0001 then |
3423 | -- wrap if not limited |
3424 | if tool.rotMin == nil and tool.rotMax == nil then |
3425 | if newRot > 2*math.pi then |
3426 | newRot = newRot - 2*math.pi |
3427 | end |
3428 | if newRot < 0 then |
3429 | newRot = newRot + 2*math.pi |
3430 | end |
3431 | end |
3432 | tool.curRot[tool.rotationAxis] = newRot |
3433 | setRotation(tool.node, tool.curRot[1], tool.curRot[2], tool.curRot[3]) |
3434 | |
3435 | self:updateMovingToolSoundEvents(tool, diff > 0, newRot == tool.rotMax or newRot == tool.rotMin, oldRot == tool.rotMax or oldRot == tool.rotMin) |
3436 | |
3437 | SpecializationUtil.raiseEvent(self, "onMovingToolChanged", tool, rotSpeed, dt) |
3438 | |
3439 | return true |
3440 | end |
3441 | |
3442 | return false |
3443 | end |
setToolTranslation
DescriptionSet tool translationDefinition
setToolTranslation(table tool, float transSpeed, float dt)Arguments
table | tool | tool |
float | transSpeed | translation speed |
float | dt | time since last call in ms |
boolean | changed | translation changed |
3345 | function Cylindered.setToolTranslation(self, tool, transSpeed, dt, delta) |
3346 | tool.curTrans[1], tool.curTrans[2], tool.curTrans[3] = getTranslation(tool.node) |
3347 | local newTrans = tool.curTrans[tool.translationAxis] |
3348 | local oldTrans = newTrans |
3349 | if transSpeed ~= nil then |
3350 | newTrans = newTrans + transSpeed*dt |
3351 | else |
3352 | newTrans = newTrans + delta |
3353 | end |
3354 | if tool.transMax ~= nil then |
3355 | newTrans = math.min(newTrans, tool.transMax) |
3356 | end |
3357 | if tool.transMin ~= nil then |
3358 | newTrans = math.max(newTrans, tool.transMin) |
3359 | end |
3360 | local diff = newTrans - oldTrans |
3361 | if dt ~= 0 then |
3362 | tool.lastTransSpeed = diff/dt |
3363 | end |
3364 | if math.abs(diff) > 0.0001 then |
3365 | tool.curTrans[tool.translationAxis] = newTrans |
3366 | setTranslation(tool.node, tool.curTrans[1], tool.curTrans[2], tool.curTrans[3]) |
3367 | |
3368 | self:updateMovingToolSoundEvents(tool, diff > 0, newTrans == tool.transMax or newTrans == tool.transMin, oldTrans == tool.transMax or oldTrans == tool.transMin) |
3369 | |
3370 | SpecializationUtil.raiseEvent(self, "onMovingToolChanged", tool, transSpeed, dt) |
3371 | |
3372 | return true |
3373 | end |
3374 | |
3375 | return false |
3376 | end |
updateAttacherJoints
DescriptionUpdate attacher jointsDefinition
updateAttacherJoints(table entry)Arguments
table | entry | entry |
3998 | function Cylindered.updateAttacherJoints(self, entry) |
3999 | if self.isServer then |
4000 | if entry.attacherJoints ~= nil then |
4001 | for _,joint in ipairs(entry.attacherJoints) do |
4002 | if joint.jointIndex ~= 0 then |
4003 | setJointFrame(joint.jointIndex, 0, joint.jointTransform) |
4004 | end |
4005 | end |
4006 | end |
4007 | |
4008 | if entry.inputAttacherJoint then |
4009 | if self.getAttacherVehicle ~= nil then |
4010 | local attacherVehicle = self:getAttacherVehicle() |
4011 | if attacherVehicle ~= nil then |
4012 | |
4013 | local attacherJoints = attacherVehicle:getAttacherJoints() |
4014 | if attacherJoints ~= nil then |
4015 | |
4016 | local jointDescIndex = attacherVehicle:getAttacherJointIndexFromObject(self) |
4017 | if jointDescIndex ~= nil then |
4018 | local jointDesc = attacherJoints[jointDescIndex] |
4019 | |
4020 | local inputAttacherJoint = self:getActiveInputAttacherJoint() |
4021 | if inputAttacherJoint ~= nil then |
4022 | local xNew = jointDesc.jointOrigTrans[1] + jointDesc.jointPositionOffset[1] |
4023 | local yNew = jointDesc.jointOrigTrans[2] + jointDesc.jointPositionOffset[2] |
4024 | local zNew = jointDesc.jointOrigTrans[3] + jointDesc.jointPositionOffset[3] |
4025 | |
4026 | -- transform offset position to world coord and to jointTransform coord to get position offset dependend on angle and position |
4027 | local ox, oy, oz = getTranslation(jointDesc.jointTransform) |
4028 | setTranslation(jointDesc.jointTransform, unpack(jointDesc.jointOrigTrans)) |
4029 | local x, y, z = localToWorld(getParent(jointDesc.jointTransform), xNew, yNew, zNew) |
4030 | local x1, y1, z1 = worldToLocal(jointDesc.jointTransform, x, y, z) |
4031 | setTranslation(jointDesc.jointTransform, ox, oy, oz) |
4032 | |
4033 | -- transform it to implement position and angle |
4034 | x, y, z = localToWorld(inputAttacherJoint.node, x1, y1, z1) |
4035 | local x2, y2, z2 = worldToLocal(getParent(inputAttacherJoint.node), x, y, z) |
4036 | setTranslation(inputAttacherJoint.node, x2, y2, z2) |
4037 | |
4038 | setJointFrame(jointDesc.jointIndex, 1, inputAttacherJoint.node) |
4039 | |
4040 | setTranslation(inputAttacherJoint.node, unpack(inputAttacherJoint.jointOrigTrans)) |
4041 | end |
4042 | end |
4043 | end |
4044 | end |
4045 | end |
4046 | end |
4047 | end |
4048 | end |
updateComponentJoints
DescriptionUpdate component jointsDefinition
updateComponentJoints(table entry, boolean placeComponents)Arguments
table | entry | entry |
boolean | placeComponents | place components |
3969 | function Cylindered.updateComponentJoints(self, entry, placeComponents) |
3970 | if self.isServer then |
3971 | if entry.componentJoints ~= nil then |
3972 | for _,joint in ipairs(entry.componentJoints) do |
3973 | local componentJoint = joint.componentJoint |
3974 | |
3975 | local jointNode = componentJoint.jointNode |
3976 | if joint.anchorActor == 1 then |
3977 | jointNode = componentJoint.jointNodeActor1 |
3978 | end |
3979 | |
3980 | if placeComponents then |
3981 | local node = self.components[componentJoint.componentIndices[2]].node |
3982 | local x,y,z = localToWorld(jointNode, joint.x, joint.y, joint.z) |
3983 | local upX,upY,upZ = localDirectionToWorld(jointNode, joint.upX,joint.upY,joint.upZ) |
3984 | local dirX,dirY,dirZ = localDirectionToWorld(jointNode, joint.dirX,joint.dirY,joint.dirZ) |
3985 | setWorldTranslation(node, x,y,z) |
3986 | I3DUtil.setWorldDirection(node, dirX,dirY,dirZ, upX,upY,upZ) |
3987 | end |
3988 | |
3989 | self:setComponentJointFrame(componentJoint, joint.anchorActor) |
3990 | end |
3991 | end |
3992 | end |
3993 | end |
updateCylinderedInitial
DescriptionInitial update of cylinderedDefinition
updateCylinderedInitial(boolean placeComponents)Arguments
boolean | placeComponents | place components |
2841 | function Cylindered:updateCylinderedInitial(placeComponents, keepDirty) |
2842 | if placeComponents == nil then |
2843 | placeComponents = true |
2844 | end |
2845 | |
2846 | if keepDirty == nil then |
2847 | keepDirty = false |
2848 | end |
2849 | |
2850 | local spec = self.spec_cylindered |
2851 | |
2852 | for _, part in pairs(spec.activeDirtyMovingParts) do |
2853 | Cylindered.setDirty(self, part) |
2854 | end |
2855 | |
2856 | for _, tool in ipairs(spec.movingTools) do |
2857 | if tool.isDirty then |
2858 | Cylindered.updateWheels(self, tool) |
2859 | if self.isServer then |
2860 | Cylindered.updateComponentJoints(self, tool, placeComponents) |
2861 | end |
2862 | tool.isDirty = false or keepDirty |
2863 | end |
2864 | |
2865 | self:updateExtraDependentParts(tool, 9999) |
2866 | self:updateDependentAnimations(tool, 9999) |
2867 | end |
2868 | |
2869 | for _, part in ipairs(spec.movingParts) do |
2870 | local isActive = self:getIsMovingPartActive(part) |
2871 | if isActive or part.smoothedDirectionScale and part.smoothedDirectionScaleAlpha ~= 0 then |
2872 | if part.isDirty then |
2873 | Cylindered.updateMovingPart(self, part, placeComponents, nil, isActive, false) |
2874 | Cylindered.updateWheels(self, part) |
2875 | part.isDirty = false or keepDirty |
2876 | end |
2877 | |
2878 | self:updateExtraDependentParts(part, 9999) |
2879 | self:updateDependentAnimations(part, 9999) |
2880 | end |
2881 | end |
2882 | end |
updateDelayedTool
DescriptionDefinitionupdateDelayedTool()Code
1361 | function Cylindered:updateDelayedTool(tool, forceLastPosition) |
1362 | local spec = self.spec_cylindered |
1363 | |
1364 | if forceLastPosition ~= nil and forceLastPosition then |
1365 | for i=1, tool.delayedFrames - 1 do |
1366 | tool.delayedHistroyData[i] = tool.delayedHistroyData[tool.delayedFrames] |
1367 | end |
1368 | end |
1369 | |
1370 | local currentData = tool.delayedHistroyData[1] |
1371 | for i=1, tool.delayedFrames - 1 do |
1372 | tool.delayedHistroyData[i] = tool.delayedHistroyData[i + 1] |
1373 | end |
1374 | |
1375 | setRotation(tool.delayedNode, unpack(currentData.rot)) |
1376 | setTranslation(tool.delayedNode, unpack(currentData.trans)) |
1377 | |
1378 | -- local r, _, _ = getRotation(tool.node) |
1379 | -- log(string.format("%s: %.2f | %s: %.2f", getName(tool.node), math.deg(r), getName(tool.delayedNode), math.deg(currentData.rot[1]))) |
1380 | |
1381 | tool.delayedHistoryIndex = tool.delayedHistoryIndex - 1 |
1382 | |
1383 | local movingPart = spec.nodesToMovingParts[tool.delayedNode] |
1384 | local movingTool = spec.nodesToMovingTools[tool.delayedNode] |
1385 | if movingPart ~= nil then |
1386 | Cylindered.setDirty(self, movingPart) |
1387 | end |
1388 | if spec.nodesToMovingTools[tool.delayedNode] ~= nil then |
1389 | Cylindered.setDirty(self, movingTool) |
1390 | end |
1391 | end |
updateDependentAnimations
DescriptionDefinitionupdateDependentAnimations()Code
1758 | function Cylindered:updateDependentAnimations(part, dt) |
1759 | if #part.dependentAnimations > 0 then |
1760 | for _, dependentAnimation in ipairs(part.dependentAnimations) do |
1761 | local pos = 0 |
1762 | if dependentAnimation.translationAxis ~= nil then |
1763 | local retValues = {getTranslation(dependentAnimation.node)} |
1764 | pos = (retValues[dependentAnimation.translationAxis] - dependentAnimation.minValue) / (dependentAnimation.maxValue - dependentAnimation.minValue) |
1765 | end |
1766 | |
1767 | if dependentAnimation.rotationAxis ~= nil then |
1768 | local retValues = {getRotation(dependentAnimation.node)} |
1769 | pos = (retValues[dependentAnimation.rotationAxis] - dependentAnimation.minValue) / (dependentAnimation.maxValue - dependentAnimation.minValue) |
1770 | end |
1771 | |
1772 | pos = MathUtil.clamp(pos, 0, 1) |
1773 | if dependentAnimation.invert then |
1774 | pos = 1-pos |
1775 | end |
1776 | dependentAnimation.lastPos = pos |
1777 | |
1778 | self:setAnimationTime(dependentAnimation.name, pos, true) |
1779 | end |
1780 | end |
1781 | end |
updateDependentToolLimits
DescriptionDefinitionupdateDependentToolLimits()Code
1785 | function Cylindered:updateDependentToolLimits(tool, dependentTool) |
1786 | if dependentTool.minTransLimits ~= nil or dependentTool.maxTransLimits ~= nil then |
1787 | local state = Cylindered.getMovingToolState(self, tool) |
1788 | if dependentTool.minTransLimits ~= nil then |
1789 | dependentTool.movingTool.transMin = MathUtil.lerp(dependentTool.minTransLimits[1], dependentTool.minTransLimits[2], 1-state) |
1790 | end |
1791 | if dependentTool.maxTransLimits ~= nil then |
1792 | dependentTool.movingTool.transMax = MathUtil.lerp(dependentTool.maxTransLimits[1], dependentTool.maxTransLimits[2], 1-state) |
1793 | end |
1794 | local transLimitChanged = Cylindered.setToolTranslation(self, dependentTool.movingTool, 0, 0) |
1795 | if transLimitChanged then |
1796 | Cylindered.setDirty(self, dependentTool.movingTool) |
1797 | end |
1798 | end |
1799 | |
1800 | if dependentTool.minRotLimits ~= nil or dependentTool.maxRotLimits ~= nil then |
1801 | local state = Cylindered.getMovingToolState(self, tool) |
1802 | if dependentTool.minRotLimits ~= nil then |
1803 | dependentTool.movingTool.rotMin = MathUtil.lerp(dependentTool.minRotLimits[1], dependentTool.minRotLimits[2], 1-state) |
1804 | end |
1805 | if dependentTool.maxRotLimits ~= nil then |
1806 | dependentTool.movingTool.rotMax = MathUtil.lerp(dependentTool.maxRotLimits[1], dependentTool.maxRotLimits[2], 1-state) |
1807 | end |
1808 | |
1809 | dependentTool.movingTool.networkInterpolators.rotation:setMinMax(dependentTool.movingTool.rotMin, dependentTool.movingTool.rotMax) |
1810 | |
1811 | local rotLimitChanged = Cylindered.setToolRotation(self, dependentTool.movingTool, 0, 0) |
1812 | if rotLimitChanged then |
1813 | Cylindered.setDirty(self, dependentTool.movingTool) |
1814 | end |
1815 | end |
1816 | end |
updateDirtyMovingParts
DescriptionDefinitionupdateDirtyMovingParts()Code
2006 | function Cylindered:updateDirtyMovingParts(dt, updateSound) |
2007 | local spec = self.spec_cylindered |
2008 | |
2009 | for _, tool in pairs(spec.movingTools) do |
2010 | if tool.isDirty then |
2011 | if tool.playSound then |
2012 | spec.movingToolNeedsSound = true |
2013 | end |
2014 | if self.isServer then |
2015 | -- update component joint |
2016 | Cylindered.updateComponentJoints(self, tool, false) |
2017 | end |
2018 | self:updateExtraDependentParts(tool, dt) |
2019 | self:updateDependentAnimations(tool, dt) |
2020 | tool.isDirty = false |
2021 | end |
2022 | end |
2023 | |
2024 | if self.anyMovingPartsDirty then |
2025 | for i, part in ipairs(spec.movingParts) do |
2026 | if part.isDirty then |
2027 | local isActive = self:getIsMovingPartActive(part) |
2028 | if isActive or part.smoothedDirectionScale and part.smoothedDirectionScaleAlpha ~= 0 then |
2029 | Cylindered.updateMovingPart(self, part, false, nil, isActive) |
2030 | self:updateExtraDependentParts(part, dt) |
2031 | self:updateDependentAnimations(part, dt) |
2032 | if part.playSound then |
2033 | spec.cylinderedHydraulicSoundPartNumber = i |
2034 | spec.movingPartNeedsSound = true |
2035 | end |
2036 | end |
2037 | else |
2038 | if spec.isClient and spec.cylinderedHydraulicSoundPartNumber == i then |
2039 | spec.movingPartNeedsSound = false |
2040 | end |
2041 | end |
2042 | end |
2043 | self.anyMovingPartsDirty = false |
2044 | end |
2045 | |
2046 | if updateSound then |
2047 | if self.isClient then |
2048 | if spec.movingToolNeedsSound or spec.movingPartNeedsSound then |
2049 | if not spec.isHydraulicSamplePlaying then |
2050 | g_soundManager:playSample(spec.samples.hydraulic) |
2051 | spec.isHydraulicSamplePlaying = true |
2052 | end |
2053 | self:raiseActive() |
2054 | else |
2055 | if spec.isHydraulicSamplePlaying then |
2056 | g_soundManager:stopSample(spec.samples.hydraulic) |
2057 | spec.isHydraulicSamplePlaying = false |
2058 | end |
2059 | end |
2060 | end |
2061 | end |
2062 | end |
updateEasyControl
DescriptionDefinitionupdateEasyControl()Code
1533 | function Cylindered:updateEasyControl(dt, updateDelayedNodes) |
1534 | local spec = self.spec_cylindered |
1535 | local easyArmControl = spec.easyArmControl |
1536 | if easyArmControl ~= nil then |
1537 | local targetYTool = self:getMovingToolByNode(easyArmControl.targetNodeY) |
1538 | local targetZTool = self:getMovingToolByNode(easyArmControl.targetNodeZ) |
1539 | |
1540 | local hasChanged = false |
1541 | if targetYTool.move ~= 0 or targetZTool.move ~= 0 then |
1542 | hasChanged = true |
1543 | |
1544 | if (targetYTool.move ~= 0 and targetYTool.isConsumingPower) or (targetZTool.move ~= 0 and targetZTool.isConsumingPower) then |
1545 | spec.powerConsumingTimer = spec.powerConsumingActiveTimeOffset |
1546 | end |
1547 | end |
1548 | |
1549 | if self.isServer and easyArmControl.state and hasChanged then |
1550 | local transSpeedY = targetYTool.move * easyArmControl.moveSpeed |
1551 | if easyArmControl.moveAcceleration ~= nil and math.abs(transSpeedY - easyArmControl.lastSpeedY) >= easyArmControl.moveAcceleration * dt then |
1552 | if transSpeedY > easyArmControl.lastSpeedY then |
1553 | transSpeedY = easyArmControl.lastSpeedY + easyArmControl.moveAcceleration * dt |
1554 | else |
1555 | transSpeedY = easyArmControl.lastSpeedY - easyArmControl.moveAcceleration * dt |
1556 | end |
1557 | end |
1558 | |
1559 | local transSpeedZ = targetZTool.move * easyArmControl.moveSpeed |
1560 | if easyArmControl.moveAcceleration ~= nil and math.abs(transSpeedZ - easyArmControl.lastSpeedZ) >= easyArmControl.moveAcceleration * dt then |
1561 | if transSpeedZ > easyArmControl.lastSpeedZ then |
1562 | transSpeedZ = easyArmControl.lastSpeedZ + easyArmControl.moveAcceleration * dt |
1563 | else |
1564 | transSpeedZ = easyArmControl.lastSpeedZ - easyArmControl.moveAcceleration * dt |
1565 | end |
1566 | end |
1567 | |
1568 | easyArmControl.lastSpeedY = transSpeedY |
1569 | local moveY = transSpeedY * dt |
1570 | |
1571 | easyArmControl.lastSpeedZ = transSpeedZ |
1572 | local moveZ = transSpeedZ * dt |
1573 | |
1574 | -- target world position |
1575 | local worldTargetDirX, worldTargetDirY, worldTargetDirZ = localDirectionToWorld(easyArmControl.rootNode, 0, moveY, moveZ) |
1576 | local worldTargetX, worldTargetY, worldTargetZ = getWorldTranslation(easyArmControl.targetRefNode) |
1577 | worldTargetX, worldTargetY, worldTargetZ = worldTargetX + worldTargetDirX, worldTargetY + worldTargetDirY, worldTargetZ + worldTargetDirZ |
1578 | |
1579 | -- limit target position to max distance radius |
1580 | local locTargetX, locTargetY, locTargetZ = worldToLocal(easyArmControl.rootNode, worldTargetX, worldTargetY, worldTargetZ) |
1581 | local distanceToTarget = MathUtil.vector3Length(locTargetX, locTargetY, locTargetZ) |
1582 | |
1583 | local targetExceedFactor = easyArmControl.maxTotalDistance / distanceToTarget |
1584 | if targetExceedFactor < 1 then |
1585 | locTargetX, locTargetY, locTargetZ = locTargetX * targetExceedFactor, locTargetY * targetExceedFactor, locTargetZ * targetExceedFactor |
1586 | worldTargetX, worldTargetY, worldTargetZ = localToWorld(easyArmControl.rootNode, locTargetX, locTargetY, locTargetZ) |
1587 | |
1588 | distanceToTarget = easyArmControl.maxTotalDistance |
1589 | end |
1590 | |
1591 | -- distance from arm1 to arm2 and arm2 to end node |
1592 | local circleDistance1 = MathUtil.vector3Length(localToLocal(easyArmControl.xRotationNodes[2].node, easyArmControl.xRotationNodes[1].node, 0, 0, 0)) |
1593 | local _, _, circleDistance2 = localToLocal(easyArmControl.targetRefNode, easyArmControl.xRotationNodes[2].node, 0, 0, 0) |
1594 | |
1595 | -- circle center positions |
1596 | local circle1X, circle1Y, circle1Z = localToLocal(easyArmControl.xRotationNodes[1].node, easyArmControl.rootNode, 0, 0, 0) |
1597 | local circle2X, circle2Y, circle2Z = worldToLocal(easyArmControl.rootNode, worldTargetX, worldTargetY, worldTargetZ) |
1598 | |
1599 | --#debug local c1xw, c1yw, c1zw = localToWorld(easyArmControl.rootNode, circle1X, circle1Y, circle1Z) |
1600 | --#debug DebugUtil.drawDebugGizmoAtWorldPos(c1xw, c1yw, c1zw, 0, 0, 1, 0, 1, 0, "c1", false) |
1601 | --#debug local c2xw, c2yw, c2zw = localToWorld(easyArmControl.rootNode, circle2X, circle2Y, circle2Z) |
1602 | --#debug DebugUtil.drawDebugGizmoAtWorldPos(c2xw, c2yw, c2zw, 0, 0, 1, 0, 1, 0, "c2", false) |
1603 | |
1604 | --#debug DebugUtil.drawDebugGizmoAtWorldPos(c2xw, c2yw - 0.5, c2zw, 0, 0, 1, 0, 1, 0, string.format("move: %.2f %.2f", moveY / dt * 1000, moveZ / dt * 1000), false) |
1605 | |
1606 | local numZTranslationNodes = #easyArmControl.zTranslationNodes |
1607 | if numZTranslationNodes > 0 then |
1608 | -- get angle between input direction and translatio node direction |
1609 | local inputDirY, inputDirZ = MathUtil.vector2Normalize(math.abs(moveY), math.abs(moveZ)) |
1610 | if moveY == 0 and moveZ == 0 then |
1611 | inputDirY, inputDirZ = 0, 0 |
1612 | end |
1613 | local transDirX, transDirY, transDirZ = localDirectionToWorld(easyArmControl.zTranslationNodes[1].node, 0, 0, 1) |
1614 | transDirX, transDirY, transDirZ = worldDirectionToLocal(easyArmControl.rootNode, transDirX, transDirY, transDirZ) |
1615 | |
1616 | local difference = math.acos(MathUtil.dotProduct(0, inputDirY, inputDirZ, 0, transDirY, transDirZ)) |
1617 | if difference > (math.pi * 0.5) then |
1618 | difference = -difference + math.pi |
1619 | end |
1620 | |
1621 | -- move translation parts |
1622 | local rotTransRatio = 1 - (difference / (math.pi * 0.5)) -- 1: only trans / 0: only rot |
1623 | rotTransRatio = (rotTransRatio - easyArmControl.transMoveRatioMinDir) / (easyArmControl.transMoveRatioMaxDir - easyArmControl.transMoveRatioMinDir) |
1624 | rotTransRatio = MathUtil.clamp(rotTransRatio, easyArmControl.minTransMoveRatio, easyArmControl.maxTransMoveRatio) |
1625 | |
1626 | local _, _, targetZOffset = worldToLocal(easyArmControl.xRotationNodes[2].node, worldTargetX, worldTargetY, worldTargetZ) |
1627 | local zDifference = targetZOffset - circleDistance2 |
1628 | |
1629 | local minTransPct = 0 |
1630 | if not easyArmControl.allowNegativeTrans then |
1631 | if transDirZ < 0 then |
1632 | -- move the translation parts to the min position |
1633 | if zDifference > 0 then |
1634 | rotTransRatio = -2 |
1635 | end |
1636 | |
1637 | minTransPct = easyArmControl.minNegativeTrans * -transDirZ |
1638 | end |
1639 | end |
1640 | |
1641 | local transMove = zDifference * rotTransRatio |
1642 | |
1643 | --#debug DebugUtil.drawDebugGizmoAtWorldPos(c2xw, c2yw - 1, c2zw, 0, 0, 1, 0, 1, 0, string.format("transDirZ %.2f\n rotTransRatio: %.2f\n transMove: %.3f\n zDifference: %.3f\n", transDirZ, rotTransRatio, transMove / dt * 1000, zDifference / dt * 1000), false) |
1644 | |
1645 | for i=1, numZTranslationNodes do |
1646 | local zTranslationNode = easyArmControl.zTranslationNodes[i] |
1647 | local movingTool = zTranslationNode.movingTool |
1648 | |
1649 | local delta = transMove / numZTranslationNodes |
1650 | local currentTrans = movingTool.curTrans[movingTool.translationAxis] |
1651 | local transMin = (movingTool.transMax - movingTool.transMin) * minTransPct + movingTool.transMin |
1652 | local newTrans = MathUtil.clamp(currentTrans + delta, transMin, movingTool.transMax) |
1653 | local newDelta = newTrans - currentTrans |
1654 | |
1655 | Cylindered.setAbsoluteToolTranslation(self, movingTool, currentTrans + newDelta) |
1656 | |
1657 | --#debug DebugUtil.drawDebugNode(zTranslationNode.node, "trans"..i, false) |
1658 | end |
1659 | |
1660 | circleDistance2 = MathUtil.vector3Length(worldToLocal(easyArmControl.xRotationNodes[2].node, getWorldTranslation(easyArmControl.targetRefNode))) |
1661 | end |
1662 | |
1663 | --#debug DebugUtil.drawDebugCircleAtNode(easyArmControl.rootNode, circleDistance1, 200, {1, 1, 1, 1}, true, {0, circle1Y, circle1Z}) |
1664 | --#debug DebugUtil.drawDebugCircleAtNode(easyArmControl.rootNode, circleDistance2, 200, {1, 0, 1, 1}, true, {0, circle2Y, circle2Z}) |
1665 | |
1666 | -- calculate intersections |
1667 | local ix, iy, i2x, i2y = MathUtil.getCircleCircleIntersection(circle1Z, circle1Y, circleDistance1, circle2Z, circle2Y, circleDistance2) |
1668 | if ix ~= nil and iy ~= nil then |
1669 | local node1Tool = easyArmControl.xRotationNodes[1].movingTool |
1670 | local node2Tool = easyArmControl.xRotationNodes[2].movingTool |
1671 | |
1672 | -- rotation node 1 |
1673 | local node1Rotation = -math.atan2(iy, ix) |
1674 | local node1RotationClamped = MathUtil.clamp(node1Rotation, node1Tool.rotMin, node1Tool.rotMax) |
1675 | local node1Overrun = 0 -- node1Rotation - node1RotationClamped |
1676 | |
1677 | Cylindered.setAbsoluteToolRotation(self, easyArmControl.xRotationNodes[1].movingTool, node1RotationClamped, updateDelayedNodes) |
1678 | |
1679 | -- rotation node 2 |
1680 | local node2Rotation = math.pi - math.acos((circleDistance1*circleDistance1 + circleDistance2*circleDistance2 - distanceToTarget*distanceToTarget) / (2*circleDistance1*circleDistance2)) |
1681 | local node2RotationClamped = MathUtil.clamp(node2Rotation + node1Overrun, node2Tool.rotMin, node2Tool.rotMax) |
1682 | local node2Overrun = node2Rotation - node2RotationClamped |
1683 | |
1684 | Cylindered.setAbsoluteToolRotation(self, easyArmControl.xRotationNodes[2].movingTool, node2RotationClamped, updateDelayedNodes) |
1685 | |
1686 | -- rotation node 1 |
1687 | node1RotationClamped = MathUtil.clamp(node1RotationClamped + node2Overrun * 0.5, node1Tool.rotMin, node1Tool.rotMax) |
1688 | Cylindered.setAbsoluteToolRotation(self, easyArmControl.xRotationNodes[1].movingTool, node1RotationClamped, updateDelayedNodes) |
1689 | |
1690 | -- debug |
1691 | --#debug local dx0, dy0, dz0 = localToWorld(easyArmControl.rootNode, 0, 0, 0) |
1692 | --#debug local dx1, dy1, dz1 = localToWorld(easyArmControl.rootNode, 0, iy, ix) |
1693 | --#debug local dx2, dy2, dz2 = localToWorld(easyArmControl.rootNode, 0, i2y, i2x) |
1694 | --#debug DebugUtil.drawDebugGizmoAtWorldPos(dx1, dy1, dz1, 0, 0, 1, 0, 1, 0, "i1", false) |
1695 | --#debug DebugUtil.drawDebugGizmoAtWorldPos(dx2, dy2, dz2, 0, 0, 1, 0, 1, 0, "i2", false) |
1696 | --#debug drawDebugLine(dx0, dy0, dz0, 1, 0, 0, worldTargetX, worldTargetY, worldTargetZ, 1, 0, 0) -- root to target |
1697 | --#debug drawDebugLine(dx0, dy0, dz0, 1, 1, 0, dx1, dy1, dz1, 1, 1, 0) -- root to i1 |
1698 | --#debug drawDebugLine(dx1, dy1, dz1, 1, 1, 0, worldTargetX, worldTargetY, worldTargetZ, 1, 1, 0) -- i1 to target |
1699 | |
1700 | --#debug DebugUtil.drawDebugCircleAtNode(easyArmControl.rootNode, easyArmControl.maxTotalDistance, 200, {0, 1, 0, 1}, true) |
1701 | --#debug if easyArmControl.maxTransDistance ~= nil then |
1702 | --#debug DebugUtil.drawDebugCircleAtNode(easyArmControl.xRotationNodes[2].node, easyArmControl.maxTransDistance, 200, {0, 1, 0, 1}, true) |
1703 | --#debug end |
1704 | end |
1705 | end |
1706 | end |
1707 | end |
updateExtraDependentParts
DescriptionDefinitionupdateExtraDependentParts()Code
1753 | function Cylindered:updateExtraDependentParts(part, dt) |
1754 | end |
updateMovingPart
DescriptionUpdate moving partDefinition
updateMovingPart(table part, boolean placeComponents, boolean updateDependentParts, boolean isActive)Arguments
table | part | part |
boolean | placeComponents | place components |
boolean | updateDependentParts | update dependent parts |
boolean | isActive | moving part is active (default: true) |
3574 | function Cylindered.updateMovingPart(self, part, placeComponents, updateDependentParts, isActive, updateSounds) |
3575 | |
3576 | -- the local reference point must be referenceDistance away from the referencePoint |
3577 | local refX,refY,refZ |
3578 | local dirX, dirY, dirZ = 0,0,0 |
3579 | local changed, applyDirection = false, false |
3580 | if part.referencePoint ~= nil then |
3581 | if part.moveToReferenceFrame then |
3582 | local x,y,z = localToLocal(part.referenceFrame, getParent(part.node), part.referenceFrameOffset[1], part.referenceFrameOffset[2], part.referenceFrameOffset[3]) |
3583 | setTranslation(part.node, x,y,z) |
3584 | changed = true |
3585 | end |
3586 | refX,refY,refZ = getWorldTranslation(part.referencePoint) |
3587 | if part.referenceDistance == 0 then |
3588 | if part.useLocalOffset then |
3589 | local lx, ly, lz = worldToLocal(part.node, refX, refY, refZ) |
3590 | dirX, dirY, dirZ = localDirectionToWorld(part.node, lx-part.localReferencePoint[1], ly-part.localReferencePoint[2], lz) |
3591 | else |
3592 | local x,y,z = getWorldTranslation(part.node) |
3593 | dirX, dirY, dirZ = refX - x, refY-y, refZ-z |
3594 | end |
3595 | else |
3596 | if part.updateLocalReferenceDistance then |
3597 | local _,y,z = worldToLocal(part.node, getWorldTranslation(part.localReferencePointNode)) |
3598 | part.localReferenceDistance = MathUtil.vector2Length(y, z) |
3599 | end |
3600 | if part.referenceDistancePoint ~= nil then |
3601 | local _,_,z = worldToLocal(part.node, getWorldTranslation(part.referenceDistancePoint)) |
3602 | part.referenceDistance = z |
3603 | end |
3604 | |
3605 | if part.localReferenceTranslate then |
3606 | local _, ly, lz = worldToLocal(part.node, refX, refY, refZ) |
3607 | |
3608 | -- calculate line-circle intersection |
3609 | if math.abs(ly) < part.referenceDistance then |
3610 | local dz = math.sqrt(part.referenceDistance*part.referenceDistance - ly*ly) |
3611 | |
3612 | local z1 = (lz - dz) - part.localReferenceDistance |
3613 | local z2 = (lz + dz) - part.localReferenceDistance |
3614 | if math.abs(z2) < math.abs(z1) then |
3615 | z1 = z2 |
3616 | end |
3617 | local parentNode = getParent(part.node) |
3618 | local tx,ty,tz = unpack(part.localReferenceTranslation) |
3619 | local _, _, coz = localToLocal(parentNode, part.node, tx,ty,tz) |
3620 | local ox,oy,oz = localDirectionToLocal(part.node, parentNode, 0,0,z1-coz) |
3621 | setTranslation(part.node, tx+ox,ty+oy,tz+oz) |
3622 | changed = true |
3623 | end |
3624 | else |
3625 | local r1 = part.localReferenceDistance |
3626 | local r2 = part.referenceDistance |
3627 | |
3628 | if part.dynamicLocalReferenceDistance then |
3629 | local _, y1, z1 = worldToLocal(part.node, getWorldTranslation(part.localReferencePointNode)) |
3630 | local _, y2, z2 = worldToLocal(part.node, localToWorld(part.localReferencePointNode, 0, 0, part.referenceDistance)) |
3631 | |
3632 | r2 = MathUtil.vector2Length(y1-y2, z1-z2) |
3633 | end |
3634 | |
3635 | local _, ly, lz = worldToLocal(part.node, refX, refY, refZ) |
3636 | --print("intersect: "..ly .. " "..lz) |
3637 | local ix, iy, i2x, i2y = MathUtil.getCircleCircleIntersection(0,0, r1, ly, lz, r2) |
3638 | |
3639 | local allowUpdate = true |
3640 | if part.referenceDistanceThreshold > 0 then |
3641 | local lRefX, lRefY, lRefZ = worldToLocal(part.node, getWorldTranslation(part.referencePoint)) |
3642 | local x,y,z = worldToLocal(part.node, getWorldTranslation(part.localReferencePointNode)) |
3643 | local currentDistance = MathUtil.vector3Length(lRefX-x, lRefY-y, lRefZ-z) |
3644 | |
3645 | if math.abs(currentDistance-part.referenceDistance) < part.referenceDistanceThreshold then |
3646 | allowUpdate = false |
3647 | end |
3648 | end |
3649 | |
3650 | if allowUpdate and ix ~= nil then |
3651 | if i2x ~= nil then |
3652 | -- use the point which as the same angle side as the original configuration |
3653 | local side = ix*(lz-iy) - iy*(ly-ix) |
3654 | if (side < 0) ~= (part.localReferenceAngleSide < 0) then |
3655 | iy = i2y |
3656 | ix = i2x |
3657 | end |
3658 | end |
3659 | dirX, dirY, dirZ = localDirectionToWorld(part.node, 0, ix, iy) |
3660 | changed = true |
3661 | end |
3662 | end |
3663 | end |
3664 | |
3665 | if part.doInversedLineAlignment then |
3666 | if part.doInversedLineAlignmentRoot == nil then |
3667 | part.doInversedLineAlignmentRoot = createTransformGroup("inversedLineAlignmentRoot") |
3668 | link(getParent(part.node), part.doInversedLineAlignmentRoot, getChildIndex(part.node)) |
3669 | setTranslation(part.doInversedLineAlignmentRoot, getTranslation(part.node)) |
3670 | setRotation(part.doInversedLineAlignmentRoot, getRotation(part.node)) |
3671 | link(part.doInversedLineAlignmentRoot, part.node) |
3672 | setTranslation(part.node, 0, 0, 0) |
3673 | setRotation(part.node, 0, 0, 0) |
3674 | end |
3675 | |
3676 | for i=1, #part.orientationLineNodes-1 do |
3677 | local startNode = part.orientationLineNodes[i] |
3678 | local endNode = part.orientationLineNodes[i + 1] |
3679 | |
3680 | local _, sy, sz = localToLocal(startNode, part.node, 0, 0, 0) |
3681 | local _, ey, ez = localToLocal(endNode, part.node, 0, 0, 0) |
3682 | |
3683 | local minLength = MathUtil.vector2Length(sy, sz) |
3684 | local maxLength = MathUtil.vector2Length(ey, ez) |
3685 | local targetLength = calcDistanceFrom(part.referencePoint, part.doInversedLineAlignmentRoot) |
3686 | |
3687 | if targetLength >= minLength and targetLength <= maxLength then |
3688 | local alpha = (targetLength - minLength) / (maxLength - minLength) |
3689 | |
3690 | local ty = MathUtil.lerp(sy, ey, alpha) |
3691 | local tz = MathUtil.lerp(sz, ez, alpha) |
3692 | |
3693 | -- alignment root always pointing to reference point |
3694 | -- actual moving part is adjusting to local offset to current target point on the line |
3695 | |
3696 | local upX, upY, upZ = localDirectionToWorld(part.referenceFrame, 0, 1, 0) |
3697 | |
3698 | dirX, dirY, dirZ = localDirectionToWorld(part.doInversedLineAlignmentRoot, 0, -ty, tz) |
3699 | I3DUtil.setWorldDirection(part.node, dirX, dirY, dirZ, upX, upY, upZ, part.limitedAxis, part.minRot, part.maxRot) |
3700 | |
3701 | local x, y, z = getWorldTranslation(part.doInversedLineAlignmentRoot) |
3702 | dirX, dirY, dirZ = refX - x, refY - y, refZ - z |
3703 | I3DUtil.setWorldDirection(part.doInversedLineAlignmentRoot, dirX, dirY, dirZ, upX, upY, upZ, part.limitedAxis, part.minRot, part.maxRot) |
3704 | |
3705 | changed = true |
3706 | break |
3707 | end |
3708 | end |
3709 | end |
3710 | else |
3711 | if part.alignToWorldY then |
3712 | dirX, dirY, dirZ = localDirectionToWorld(getRootNode(), 0, 1, 0) |
3713 | |
3714 | local lDX, lDY, lDZ = worldDirectionToLocal(part.referenceFrame, dirX, dirY, dirZ) |
3715 | if lDZ < 0 then |
3716 | lDZ = -lDZ |
3717 | end |
3718 | dirX, dirY, dirZ = localDirectionToWorld(part.referenceFrame, lDX, lDY, lDZ) |
3719 | |
3720 | changed = true |
3721 | else |
3722 | dirX, dirY, dirZ = localDirectionToWorld(part.referenceFrame, 0, 0, 1) |
3723 | changed = true |
3724 | end |
3725 | if part.moveToReferenceFrame then |
3726 | local x,y,z = localToLocal(part.referenceFrame, getParent(part.node), part.referenceFrameOffset[1], part.referenceFrameOffset[2], part.referenceFrameOffset[3]) |
3727 | setTranslation(part.node, x,y,z) |
3728 | changed = true |
3729 | end |
3730 | |
3731 | if part.doLineAlignment then |
3732 | local foundPoint = false |
3733 | for i=1, #part.orientationLineNodes-1 do |
3734 | local startNode = part.orientationLineNodes[i] |
3735 | local endNode = part.orientationLineNodes[i+1] |
3736 | |
3737 | local _, sy, sz = localToLocal(startNode, part.referenceFrame, 0, 0, 0) |
3738 | local _, ey, ez = localToLocal(endNode, part.referenceFrame, 0, 0, 0) |
3739 | local _, cy, cz = localToLocal(part.node, part.referenceFrame, 0, 0, 0) |
3740 | |
3741 | local partLength = part.partLength |
3742 | if part.partLengthNode ~= nil then |
3743 | partLength = calcDistanceFrom(part.node, part.partLengthNode) |
3744 | end |
3745 | |
3746 | local hasIntersection, i1y, i1z, i2y, i2z = MathUtil.getCircleLineIntersection(cy, cz, partLength, sy, sz, ey, ez) |
3747 | if hasIntersection then |
3748 | local targetY, targetZ |
3749 | if not MathUtil.getIsOutOfBounds(i1y, sy, ey) and not MathUtil.getIsOutOfBounds(i1z, sz, ez) then |
3750 | targetY, targetZ = i1y, i1z |
3751 | foundPoint = true |
3752 | end |
3753 | if not MathUtil.getIsOutOfBounds(i2y, sy, ey) and not MathUtil.getIsOutOfBounds(i2z, sz, ez) then |
3754 | targetY, targetZ = i2y, i2z |
3755 | foundPoint = true |
3756 | end |
3757 | |
3758 | if foundPoint and not MathUtil.isNan(targetY) and not MathUtil.isNan(targetZ) then |
3759 | dirX, dirY, dirZ = localDirectionToWorld(part.referenceFrame, 0, targetY, targetZ) |
3760 | |
3761 | changed = true |
3762 | applyDirection = true |
3763 | break |
3764 | end |
3765 | end |
3766 | end |
3767 | end |
3768 | end |
3769 | |
3770 | if part.smoothedDirectionScale then |
3771 | if part.smoothedDirectionScaleAlpha == nil then |
3772 | part.smoothedDirectionScaleAlpha = isActive and 1 or 0 |
3773 | end |
3774 | |
3775 | local dt = g_currentDt or 9999 |
3776 | if isActive then |
3777 | part.smoothedDirectionScaleAlpha = math.min(part.smoothedDirectionScaleAlpha + dt * part.smoothedDirectionTime, 1) |
3778 | else |
3779 | part.smoothedDirectionScaleAlpha = math.max(part.smoothedDirectionScaleAlpha - dt * part.smoothedDirectionTime, 0) |
3780 | end |
3781 | |
3782 | local inDirX, inDirY, inDirZ = localDirectionToWorld(getParent(part.node), unpack(part.initialDirection)) |
3783 | dirX, dirY, dirZ = MathUtil.vector3Lerp(inDirX, inDirY, inDirZ, dirX, dirY, dirZ, part.smoothedDirectionScaleAlpha) |
3784 | end |
3785 | |
3786 | if (part.doDirectionAlignment or applyDirection) and (dirX ~= 0 or dirY ~= 0 or dirZ ~= 0) then |
3787 | local upX, upY, upZ = localDirectionToWorld(part.referenceFrame, 0, 1, 0) |
3788 | if part.invertZ then |
3789 | dirX = -dirX |
3790 | dirY = -dirY |
3791 | dirZ = -dirZ |
3792 | end |
3793 | |
3794 | local directionThreshold = part.directionThresholdActive |
3795 | if not self:getIsActive() then |
3796 | if part.directionThreshold ~= nil and part.directionThreshold > 0 then |
3797 | directionThreshold = part.directionThreshold |
3798 | end |
3799 | end |
3800 | |
3801 | local lDirX, lDirY, lDirZ = worldDirectionToLocal(part.parent, dirX, dirY, dirZ) |
3802 | local lastDirection, lastUpVector = part.lastDirection, part.lastUpVector |
3803 | if math.abs(lastDirection[1] - lDirX) > directionThreshold or |
3804 | math.abs(lastDirection[2] - lDirY) > directionThreshold or |
3805 | math.abs(lastDirection[3] - lDirZ) > directionThreshold or |
3806 | math.abs(lastUpVector[1] - upX) > directionThreshold or |
3807 | math.abs(lastUpVector[2] - upY) > directionThreshold or |
3808 | math.abs(lastUpVector[3] - upZ) > directionThreshold then |
3809 | |
3810 | I3DUtil.setWorldDirection(part.node, dirX, dirY, dirZ, upX, upY, upZ, part.limitedAxis, part.minRot, part.maxRot) |
3811 | |
3812 | if part.debug then |
3813 | local x, y, z = getWorldTranslation(part.node) |
3814 | local length, _ = 1 |
3815 | if part.referencePoint ~= nil then |
3816 | _, _, length = worldToLocal(part.node, refX, refY, refZ) |
3817 | end |
3818 | |
3819 | drawDebugLine(x, y, z, 1, 0, 0, x + dirX * length, y + dirY * length, z + dirZ * length, 0, 1, 0, true) |
3820 | end |
3821 | |
3822 | lastDirection[1], lastDirection[2], lastDirection[3] = lDirX, lDirY, lDirZ |
3823 | lastUpVector[1], lastUpVector[2], lastUpVector[3] = upX, upY, upZ |
3824 | |
3825 | changed = true |
3826 | else |
3827 | changed = false |
3828 | end |
3829 | |
3830 | if part.scaleZ and part.localReferenceDistance ~= nil then |
3831 | local len = MathUtil.vector3Length(dirX, dirY, dirZ) |
3832 | setScale(part.node, 1, 1, len/part.localReferenceDistance) |
3833 | |
3834 | if part.debug then |
3835 | DebugUtil.drawDebugNode(part.node, string.format("scale:%.2f", len/part.localReferenceDistance), false) |
3836 | end |
3837 | end |
3838 | end |
3839 | |
3840 | if part.doRotationAlignment then |
3841 | local x,y,z = getRotation(part.referenceFrame) |
3842 | setRotation(part.node, x*part.rotMultiplier, y*part.rotMultiplier, z*part.rotMultiplier) |
3843 | changed = true |
3844 | end |
3845 | |
3846 | if part.referencePoint ~= nil then |
3847 | local numTranslatingParts = #part.translatingParts |
3848 | local distanceDivider = math.max(part.translatingPartsDivider, 1) |
3849 | if numTranslatingParts > 0 then |
3850 | local _, _, dist = worldToLocal(part.node, refX, refY, refZ) |
3851 | for i=1,numTranslatingParts do |
3852 | local translatingPart = part.translatingParts[i] |
3853 | local newZ = (dist - translatingPart.referenceDistance) / (translatingPart.divideTranslatingDistance and distanceDivider or 1) |
3854 | |
3855 | if translatingPart.minZTrans ~= nil then |
3856 | newZ = math.max(translatingPart.minZTrans, newZ) |
3857 | end |
3858 | |
3859 | if translatingPart.maxZTrans ~= nil then |
3860 | newZ = math.min(translatingPart.maxZTrans, newZ) |
3861 | end |
3862 | |
3863 | if not translatingPart.divideTranslatingDistance then |
3864 | dist = dist - (newZ - translatingPart.startPos[3]) |
3865 | end |
3866 | |
3867 | local allowUpdate = true |
3868 | if part.referenceDistanceThreshold > 0 then |
3869 | if math.abs(translatingPart.lastZ-newZ) < part.referenceDistanceThreshold then |
3870 | allowUpdate = false |
3871 | end |
3872 | end |
3873 | |
3874 | if allowUpdate then |
3875 | if updateSounds ~= false then |
3876 | if part.samplesByAction ~= nil or translatingPart.samplesByAction ~= nil then |
3877 | if newZ ~= translatingPart.lastZ then |
3878 | if math.abs(translatingPart.lastZ - newZ) > 0.0001 then |
3879 | self:onMovingPartSoundEvent(part, Cylindered.SOUND_ACTION_TRANSLATING_END, Cylindered.SOUND_TYPE_ENDING) |
3880 | self:onMovingPartSoundEvent(translatingPart, Cylindered.SOUND_ACTION_TRANSLATING_END, Cylindered.SOUND_TYPE_ENDING) |
3881 | |
3882 | self:onMovingPartSoundEvent(part, Cylindered.SOUND_ACTION_TRANSLATING_START, Cylindered.SOUND_TYPE_STARTING) |
3883 | self:onMovingPartSoundEvent(translatingPart, Cylindered.SOUND_ACTION_TRANSLATING_START, Cylindered.SOUND_TYPE_STARTING) |
3884 | |
3885 | if newZ > translatingPart.lastZ + 0.0001 then |
3886 | self:onMovingPartSoundEvent(part, Cylindered.SOUND_ACTION_TRANSLATING_POS, Cylindered.SOUND_TYPE_CONTINUES) |
3887 | self:onMovingPartSoundEvent(translatingPart, Cylindered.SOUND_ACTION_TRANSLATING_POS, Cylindered.SOUND_TYPE_CONTINUES) |
3888 | |
3889 | self:onMovingPartSoundEvent(part, Cylindered.SOUND_ACTION_TRANSLATING_END_POS, Cylindered.SOUND_TYPE_ENDING) |
3890 | self:onMovingPartSoundEvent(translatingPart, Cylindered.SOUND_ACTION_TRANSLATING_END_POS, Cylindered.SOUND_TYPE_ENDING) |
3891 | |
3892 | self:onMovingPartSoundEvent(part, Cylindered.SOUND_ACTION_TRANSLATING_START_POS, Cylindered.SOUND_TYPE_STARTING) |
3893 | self:onMovingPartSoundEvent(translatingPart, Cylindered.SOUND_ACTION_TRANSLATING_START_POS, Cylindered.SOUND_TYPE_STARTING) |
3894 | elseif newZ < translatingPart.lastZ - 0.0001 then |
3895 | self:onMovingPartSoundEvent(part, Cylindered.SOUND_ACTION_TRANSLATING_NEG, Cylindered.SOUND_TYPE_CONTINUES) |
3896 | self:onMovingPartSoundEvent(translatingPart, Cylindered.SOUND_ACTION_TRANSLATING_NEG, Cylindered.SOUND_TYPE_CONTINUES) |
3897 | |
3898 | self:onMovingPartSoundEvent(part, Cylindered.SOUND_ACTION_TRANSLATING_END_NEG, Cylindered.SOUND_TYPE_ENDING) |
3899 | self:onMovingPartSoundEvent(translatingPart, Cylindered.SOUND_ACTION_TRANSLATING_END_NEG, Cylindered.SOUND_TYPE_ENDING) |
3900 | |
3901 | self:onMovingPartSoundEvent(part, Cylindered.SOUND_ACTION_TRANSLATING_START_NEG, Cylindered.SOUND_TYPE_STARTING) |
3902 | self:onMovingPartSoundEvent(translatingPart, Cylindered.SOUND_ACTION_TRANSLATING_START_NEG, Cylindered.SOUND_TYPE_STARTING) |
3903 | end |
3904 | end |
3905 | end |
3906 | end |
3907 | end |
3908 | |
3909 | translatingPart.lastZ = newZ |
3910 | setTranslation(translatingPart.node, translatingPart.startPos[1], translatingPart.startPos[2], newZ) |
3911 | changed = true |
3912 | end |
3913 | end |
3914 | end |
3915 | end |
3916 | |
3917 | if changed then |
3918 | if part.copyLocalDirectionParts ~= nil then |
3919 | for _,copyLocalDirectionPart in pairs(part.copyLocalDirectionParts) do |
3920 | local dx,dy,dz = localDirectionToWorld(part.node, 0,0,1) |
3921 | dx,dy,dz = worldDirectionToLocal(getParent(part.node), dx,dy,dz) |
3922 | dx = dx * copyLocalDirectionPart.dirScale[1] |
3923 | dy = dy * copyLocalDirectionPart.dirScale[2] |
3924 | dz = dz * copyLocalDirectionPart.dirScale[3] |
3925 | |
3926 | local ux,uy,uz = localDirectionToWorld(part.node, 0,1,0) |
3927 | ux,uy,uz = worldDirectionToLocal(getParent(part.node), ux,uy,uz) |
3928 | ux = ux * copyLocalDirectionPart.upScale[1] |
3929 | uy = uy * copyLocalDirectionPart.upScale[2] |
3930 | uz = uz * copyLocalDirectionPart.upScale[3] |
3931 | |
3932 | setDirection(copyLocalDirectionPart.node, dx,dy,dz, ux,uy,uz) |
3933 | |
3934 | if self.isServer then |
3935 | Cylindered.updateComponentJoints(self, copyLocalDirectionPart, placeComponents) |
3936 | end |
3937 | end |
3938 | end |
3939 | |
3940 | -- update component joint |
3941 | if self.isServer then |
3942 | Cylindered.updateComponentJoints(self, part, placeComponents) |
3943 | Cylindered.updateAttacherJoints(self, part) |
3944 | Cylindered.updateWheels(self, part) |
3945 | end |
3946 | |
3947 | Cylindered.updateWheels(self, part) |
3948 | end |
3949 | |
3950 | if updateDependentParts then |
3951 | for _, data in pairs(part.dependentPartData) do |
3952 | if self.currentUpdateDistance < data.maxUpdateDistance then |
3953 | local dependentPart = data.part |
3954 | local dependentIsActive = self:getIsMovingPartActive(dependentPart) |
3955 | if dependentIsActive or dependentPart.smoothedDirectionScale and dependentPart.smoothedDirectionScaleAlpha ~= 0 then |
3956 | Cylindered.updateMovingPart(self, dependentPart, placeComponents, updateDependentParts, dependentIsActive) |
3957 | end |
3958 | end |
3959 | end |
3960 | end |
3961 | |
3962 | part.isDirty = false |
3963 | end |
updateMovingToolSoundEvents
DescriptionDefinitionupdateMovingToolSoundEvents()Code
1881 | function Cylindered:updateMovingToolSoundEvents(tool, direction, hitLimit, wasAtLimit) |
1882 | if tool.samplesByAction ~= nil then |
1883 | self:onMovingPartSoundEvent(tool, Cylindered.SOUND_ACTION_TOOL_MOVE_END, Cylindered.SOUND_TYPE_ENDING) |
1884 | self:onMovingPartSoundEvent(tool, Cylindered.SOUND_ACTION_TOOL_MOVE_START, Cylindered.SOUND_TYPE_STARTING) |
1885 | |
1886 | if direction then |
1887 | self:onMovingPartSoundEvent(tool, Cylindered.SOUND_ACTION_TOOL_MOVE_POS, Cylindered.SOUND_TYPE_CONTINUES) |
1888 | self:onMovingPartSoundEvent(tool, Cylindered.SOUND_ACTION_TOOL_MOVE_END_POS, Cylindered.SOUND_TYPE_ENDING) |
1889 | self:onMovingPartSoundEvent(tool, Cylindered.SOUND_ACTION_TOOL_MOVE_START_POS, Cylindered.SOUND_TYPE_STARTING) |
1890 | |
1891 | if hitLimit then |
1892 | self:onMovingPartSoundEvent(tool, Cylindered.SOUND_ACTION_TOOL_MOVE_END_POS_LIMIT, Cylindered.SOUND_TYPE_ENDING) |
1893 | end |
1894 | |
1895 | if wasAtLimit then |
1896 | self:onMovingPartSoundEvent(tool, Cylindered.SOUND_ACTION_TOOL_MOVE_START_POS_LIMIT, Cylindered.SOUND_TYPE_STARTING) |
1897 | end |
1898 | else |
1899 | self:onMovingPartSoundEvent(tool, Cylindered.SOUND_ACTION_TOOL_MOVE_NEG, Cylindered.SOUND_TYPE_CONTINUES) |
1900 | self:onMovingPartSoundEvent(tool, Cylindered.SOUND_ACTION_TOOL_MOVE_END_NEG, Cylindered.SOUND_TYPE_ENDING) |
1901 | self:onMovingPartSoundEvent(tool, Cylindered.SOUND_ACTION_TOOL_MOVE_START_NEG, Cylindered.SOUND_TYPE_STARTING) |
1902 | |
1903 | if hitLimit then |
1904 | self:onMovingPartSoundEvent(tool, Cylindered.SOUND_ACTION_TOOL_MOVE_END_NEG_LIMIT, Cylindered.SOUND_TYPE_ENDING) |
1905 | end |
1906 | |
1907 | if wasAtLimit then |
1908 | self:onMovingPartSoundEvent(tool, Cylindered.SOUND_ACTION_TOOL_MOVE_START_NEG_LIMIT, Cylindered.SOUND_TYPE_STARTING) |
1909 | end |
1910 | end |
1911 | end |
1912 | end |
updateRotationBasedLimits
DescriptionDefinitionupdateRotationBasedLimits()Code
4078 | function Cylindered.updateRotationBasedLimits(self, tool, dependentTool) |
4079 | if dependentTool.rotationBasedLimits ~= nil then |
4080 | local state = Cylindered.getMovingToolState(self, tool) |
4081 | if dependentTool.rotationBasedLimits ~= nil then |
4082 | local minRot, maxRot, minTrans, maxTrans = dependentTool.rotationBasedLimits:get(state) |
4083 | |
4084 | if minRot ~= nil then |
4085 | dependentTool.movingTool.rotMin = minRot |
4086 | end |
4087 | if maxRot ~= nil then |
4088 | dependentTool.movingTool.rotMax = maxRot |
4089 | end |
4090 | if minTrans ~= nil then |
4091 | dependentTool.movingTool.transMin = minTrans |
4092 | end |
4093 | if maxTrans ~= nil then |
4094 | dependentTool.movingTool.transMax = maxTrans |
4095 | end |
4096 | |
4097 | local isDirty = false |
4098 | if minRot ~= nil or maxRot ~= nil then |
4099 | isDirty = isDirty or Cylindered.setToolRotation(self, dependentTool.movingTool, 0, 0) |
4100 | end |
4101 | if minTrans ~= nil or maxTrans ~= nil then |
4102 | isDirty = isDirty or Cylindered.setToolTranslation(self, dependentTool.movingTool, 0, 0) |
4103 | end |
4104 | |
4105 | if isDirty then |
4106 | Cylindered.setDirty(self, dependentTool.movingTool) |
4107 | |
4108 | -- sync the new limited rotation values and also the new rotation limits |
4109 | self:raiseDirtyFlags(dependentTool.movingTool.dirtyFlag) |
4110 | self:raiseDirtyFlags(self.spec_cylindered.cylinderedDirtyFlag) |
4111 | end |
4112 | end |
4113 | end |
4114 | end |
updateWheels
DescriptionUpdate wheel of partDefinition
updateWheels(table part)Arguments
table | part | part |
3547 | function Cylindered.updateWheels(self, part) |
3548 | if part.wheels ~= nil then |
3549 | for _, wheel in pairs(part.wheels) do |
3550 | wheel.positionX, wheel.positionY, wheel.positionZ = localToLocal(getParent(wheel.repr), wheel.node, wheel.startPositionX-wheel.steeringCenterOffsetX, wheel.startPositionY-wheel.steeringCenterOffsetY, wheel.startPositionZ-wheel.steeringCenterOffsetZ) |
3551 | if wheel.useReprDirection then |
3552 | wheel.directionX, wheel.directionY, wheel.directionZ = localDirectionToLocal(wheel.repr, wheel.node, 0,-1,0) |
3553 | wheel.axleX, wheel.axleY, wheel.axleZ = localDirectionToLocal(wheel.repr, wheel.node, 1,0,0) |
3554 | elseif wheel.useDriveNodeDirection then |
3555 | wheel.directionX, wheel.directionY, wheel.directionZ = localDirectionToLocal(wheel.driveNodeDirectionNode, wheel.node, 0,-1,0) |
3556 | wheel.axleX, wheel.axleY, wheel.axleZ = localDirectionToLocal(wheel.driveNodeDirectionNode, wheel.node, 1,0,0) |
3557 | end |
3558 | |
3559 | self:updateWheelBase(wheel) |
3560 | |
3561 | for j=1, #wheel.wheelChocks do |
3562 | self:updateWheelChockPosition(wheel.wheelChocks[j]) |
3563 | end |
3564 | end |
3565 | end |
3566 | end |