meta data for this page
Vehicles/VehicleScripts/RopeWinch.lua
RopeWinch implements all features required by snowcats that are equipped with winch devices (such as the PistenBully 600 W). These vehicles can then be attached to all anchor points (of type AnchorPoint). Also, there is a VehicleAnchor script which allows for having winch anchor points mounted to vehicles.
Similar to CylinderAnimator, most performance-relevant operations are done by C# code. All RopeWinch
functions that are called, but not documented here, are written in C# (e.g. RopeWinch.new
or RopeWinch.setIntakeRotation
).
26 RopeWinch = RopeWinch or {}; 27 RopeWinch.anchorPoints = RopeWinch.anchorPoints or {};
RopeWinch.registerAnchorPoint(name, obj)
Register an AnchorPoint so that it can be used by RopeWinch.
Note that anchor points are always indexed by their name
(a string) as passed to this function!
32 function RopeWinch.registerAnchorPoint(name, obj) 33 RopeWinch.anchorPoints[name or ""] = obj; 34 end;
RopeWinch.unregisterAnchorPoint(name)
Unregister an AnchorPoint
38 function RopeWinch.unregisterAnchorPoint(name) 39 RopeWinch.anchorPoints[name or ""] = nil; 40 end;
RopeWinch:load(dataTable)
We need to load some data from our configuration table (dataTable
).
First, we set up all custom functions that this script requires. Note that VehicleManager:newFunction()
does not allow for return values, therefore we have to link some functions directly.
Then, we read in configuration data and apply some defaults.
47 function RopeWinch:load(dataTable) 48 self.attachWinchToAnchorPoint = VehicleManager:newFunction("attachWinchToAnchorPoint"); 49 self.setIsRopeTensed = VehicleManager:newFunction("setIsRopeTensed"); 50 self.ropeWinchConnectorCallback = VehicleManager:newFunction("ropeWinchConnectorCallback"); 51 52 self.getRopeWinchConnectorActive= RopeWinch.getRopeWinchConnectorActive; 53 self.getMayRopeWinchDisconnect = RopeWinch.getMayRopeWinchDisconnect; 54 55 if dataTable.ropeWinch ~= nil then 56 local v = dataTable.ropeWinch; 57 self.winchRoot = getChild(self.id, v.winchRoot or ""); 58 self.winchIntake = getChild(self.id, v.winchIntake or ""); 59 local ropeDiameter = v.ropeDiameter or 0.011; 60 local ropeWeightPerMeter = v.weightPerMeter or 45; 61 62 self.ropeWinchNode = RopeWinch.new(self.id, self.winchRoot, self.winchIntake, ropeDiameter, ropeWeightPerMeter); 63 self.anchorPointName = nil; 64 65 if v.intakeDefaultRotation ~= nil then 66 RopeWinch.setIntakeRotation(self.ropeWinchNode, v.intakeDefaultRotation); 67 end; 68 if v.detachedHookIndex ~= nil then 69 RopeWinch.setDetachedId(self.ropeWinchNode, getChild(self.id, v.detachedHookIndex)); 70 end; 71 if v.attachedHookIndex ~= nil and v.attachedHookTargetIndex ~= nil then 72 RopeWinch.setHook(self.ropeWinchNode, getChild(self.id, v.attachedHookIndex), getChild(self.id, v.attachedHookTargetIndex)); 73 end; 74 75 -- tensioning 76 self.ropeWinchForceMin = v.winchForceMin or 1200; 77 self.ropeWinchForceMax = v.winchForceMax or 46000; 78 self.ropeWinchForceSpeed = 1/math.max(v.forceToggleDuration or 1, 0.1); 79 self.ropeWinchForceLerp = 0; 80 self.ropeWinchForceTarget = 0; 81 82 -- motorized rotating 83 self.winchRotateSpeed = v.winchRotateSpeed or 30; 84 self.winchRotateAcc = v.winchRotateAcc or 30; 85 self.winchCurrentRotSpeed = 0; 86 87 self.soloForwardStiffness = v.soloForwardStiffness; 88 self.soloSidewaysStiffness = v.soloSidewaysStiffness; 89 90 self.maxForwardStiffness = v.maxForwardStiffness; 91 self.maxSidewaysStiffness = v.maxSidewaysStiffness; 92 93 -- control button for connecting 94 self.ropeWinchConnector = getChild(self.id, v.connector or ""); 95 96 ControlElement.new(self.ropeWinchConnector); 97 ControlElement.setCallback(self.ropeWinchConnector, 1, 98 function() self:ropeWinchConnectorCallback(); end 99 ); 100 ControlElement.setActiveCallback(self.ropeWinchConnector, 101 function() return self:getRopeWinchConnectorActive(); end 102 ); 103 104 ControlElement.setName(self.ropeWinchConnector, l10n.get("WinchAnchor_Attach")); 105 end; 106 107 self:setIsRopeTensed(false); 108 end;
RopeWinch:saveToTable(tbl)
All RopeWinch features are correctly saved into the savegame (i.e. winch rotation, active forces and active anchor point).
112 function RopeWinch:saveToTable(tbl) 113 if tbl == nil then return end; 114 if self.ropeWinchNode == nil then return end; 115 116 local rx,ry,rz = getRotation(self.winchRoot); 117 118 tbl.ropeWinchForceLerp = self.ropeWinchForceLerp; 119 tbl.ropeWinchForceTarget = self.ropeWinchForceTarget; 120 tbl.anchorPointName = self.anchorPointName; 121 tbl.winchRotationY = ry; 122 end;
RopeWinch:loadFromTable(tbl)
Of course, we also need to restore some data from the savegame.
Note that the vehicle will be attached to the anchor point not directly upon loading, but rather during the very next update
call.
128 function RopeWinch:loadFromTable(tbl) 129 if tbl == nil then return end; 130 if self.ropeWinchNode == nil then return end; 131 132 if tbl.winchRotationY ~= nil then 133 setRotationY(self.winchRoot, tbl.winchRotationY); 134 end; 135 136 -- return if rope winch is not saved correctly 137 if tbl.ropeWinchForceLerp == nil then return end; 138 self.ropeWinchForceLerp = getNoNil(tbl.ropeWinchForceLerp, self.ropeWinchForceLerp); 139 self.ropeWinchForceTarget = getNoNil(tbl.ropeWinchForceTarget, self.ropeWinchForceTarget); 140 141 if tbl.anchorPointName ~= nil then 142 -- attach it at the very beginning of the next frame (when loading has already been finished) 143 self.attachNextFrameTo = tbl.anchorPointName; 144 end; 145 end;
RopeWinch:onReset()
Reset the rope winch as well if the vehicle is resetted. Of course, it also makes sense to disconnect from any anchor point in this case.
149 function RopeWinch:onReset() 150 if self.ropeWinchNode == nil then return end; 151 152 self.ropeWinchForceLerp = 0; 153 self:setIsRopeTensed(false); 154 155 -- disconnect from anchor 156 if self.anchorPointName ~= nil then 157 local anchorPoint = RopeWinch.anchorPoints[self.anchorPointName or ""]; 158 159 if anchorPoint ~= nil then 160 anchorPoint:disconnect(true); 161 end; 162 self:attachWinchToAnchorPoint(nil); 163 end; 164 165 AnchorPoint.activeVehicle = nil; 166 167 setRotationY(self.winchRoot, 0); 168 end;
RopeWinch:setIsRopeTensed(isTensed)
Applies whether the rope is tensed or not, depending on isTensed
(bool).
172 function RopeWinch:setIsRopeTensed(isTensed) 173 if self.ropeWinchNode == nil then return end; 174 175 self.ropeWinchForceTarget = isTensed and 1 or 0; 176 177 local currentForce = lerp(self.ropeWinchForceMin, self.ropeWinchForceMax, self.ropeWinchForceLerp); 178 RopeWinch.setTensioningForce(self.ropeWinchNode, currentForce); 179 RopeWinch.setSegmentLength(self.ropeWinchNode, lerp(0.5, 2, self.ropeWinchForceLerp)); 180 181 -- apply stiffness to the wheels 182 if self.applyWheelStiffness == nil then return end; 183 184 if isTensed then 185 if self.maxForwardStiffness ~= nil then 186 self:applyWheelStiffness(self.maxForwardStiffness, self.maxSidewaysStiffness); 187 end; 188 else 189 if self.soloForwardStiffness ~= nil then 190 self:applyWheelStiffness(self.soloForwardStiffness, self.soloSidewaysStiffness); 191 end; 192 end; 193 end;
RopeWinch:attachWinchToAnchorPoint(anchorPointName)
Attach the winch to the anchor point specified in anchorPointName
(string). anchorPointName
can also be nil to disconnect from any active anchor point.
197 function RopeWinch:attachWinchToAnchorPoint(anchorPointName) 198 -- note: this function is ALWAYS called by the anchor point itself 199 if self.ropeWinchNode == nil then return end; 200 201 if self.anchorPointName ~= nil then 202 -- disconnect from there 203 RopeWinch.setTargetObject(self.ropeWinchNode, 0); 204 self.anchorPointName = nil; 205 end; 206 207 if anchorPointName == nil then 208 -- no attacher point set, so let's loose our rope by force 209 self:setIsRopeTensed(false); 210 return; 211 end; 212 213 local anchorPoint = RopeWinch.anchorPoints[anchorPointName or ""]; 214 215 if anchorPoint == nil then return end; 216 217 -- there is an anchor point indeed existing, yay 218 self.anchorPointName = anchorPointName; 219 RopeWinch.setTargetObject(self.ropeWinchNode, anchorPoint.ropeTargetId); 220 end;
RopeWinch:update(dt)
Reads in some input and passes it on to the C# functions (if necessary) every frame.
224 function RopeWinch:update(dt) 225 if self.ropeWinchNode == nil then return end; 226 227 if self.attachNextFrameTo ~= nil then 228 229 local anchorPoint = RopeWinch.anchorPoints[self.attachNextFrameTo]; 230 231 if anchorPoint ~= nil then 232 -- this will automatically call all functions on our vehicle to attach it there 233 anchorPoint:attach(self); 234 end; 235 236 self:setIsRopeTensed(self.ropeWinchForceTarget > 0.5); 237 self.attachNextFrameTo = nil; 238 end; 239 240 if not self.isActive then return end; 241 242 local rotSpeedTarget = 0; 243 244 if self.anchorPointName == "player" then 245 if not PlayerController.isActive then 246 -- urgently disconnect! 247 self:attachWinchToAnchorPoint(nil); 248 end; 249 end; 250 251 if self:getIsInputActive() then 252 if self.anchorPointName ~= nil and self.anchorPointName ~= "player" then 253 if InputMapper:getKeyDown(InputMapper.VehicleSnowcat_TenseWinch) then 254 self:setIsRopeTensed(self.ropeWinchForceTarget < 0.5); 255 end; 256 g_GUI:addKeyHint(InputMapper.VehicleSnowcat_TenseWinch, l10n.get(self.ropeWinchForceTarget > 0.5 and "Input_VehicleSnowcat_TenseWinch1" or "Input_VehicleSnowcat_TenseWinch0")); 257 end; 258 259 if self.anchorPointName == nil then 260 -- player may now move the winch if he wants to 261 if InputMapper:getKey(InputMapper.VehicleSnowcat_TurnWinchLeft) then 262 -- turn left 263 rotSpeedTarget = -self.winchRotateSpeed; 264 elseif InputMapper:getKey(InputMapper.VehicleSnowcat_TurnWinchRight) then 265 -- turn right 266 rotSpeedTarget = self.winchRotateSpeed; 267 end; 268 end; 269 end; 270 271 -- update winch force 272 if math.abs(self.ropeWinchForceTarget - self.ropeWinchForceLerp) > 1e-3 then 273 local scale = clamp(0.2, 1, 1-self.ropeWinchForceLerp); 274 self.ropeWinchForceLerp = Utils.moveTowards(self.ropeWinchForceLerp, self.ropeWinchForceTarget, scale * self.ropeWinchForceSpeed*dt); 275 276 local currentForce = lerp(self.ropeWinchForceMin, self.ropeWinchForceMax, self.ropeWinchForceLerp); 277 RopeWinch.setTensioningForce(self.ropeWinchNode, currentForce); 278 RopeWinch.setSegmentLength(self.ropeWinchNode, lerp(0.5, 2, self.ropeWinchForceLerp)); 279 end; 280 281 if self.anchorPointName ~= nil then 282 rotSpeedTarget = 0; 283 self.winchCurrentRotSpeed = 0; 284 end; 285 286 self.winchCurrentRotSpeed = Utils.moveTowards(self.winchCurrentRotSpeed, rotSpeedTarget, self.winchRotateAcc*3*dt); 287 288 if math.abs(self.winchCurrentRotSpeed) > 1e-3 then 289 local rx, ry, rz = getRotation(self.winchRoot); 290 setRotationY(self.winchRoot, ry + self.winchCurrentRotSpeed * dt); 291 end; 292 end;
RopeWinch:getMayRopeWinchDisconnect()
Returns whether the winch may be disconnected right now (bool).
296 function RopeWinch:getMayRopeWinchDisconnect() 297 if self.anchorPointName ~= nil and self.anchorPointName ~= "player" then 298 -- we're attached to a real attacher 299 return self.ropeWinchForceLerp < 0.1; 300 end; 301 return true; 302 end;
RopeWinch:getRopeWinchConnectorActive()
Returns whether the control element located at the winch intake shall be active or not (bool).
306 function RopeWinch:getRopeWinchConnectorActive() 307 if self.anchorPointName ~= nil and self.anchorPointName ~= "player" then 308 -- don't click there if we're attached 309 return false; 310 end; 311 if self.anchorPointName == "player" then 312 -- player may always decouple! 313 return true; 314 end; 315 316 -- also don't click there if there's force on the rope 317 if self.ropeWinchForceLerp > 0.1 then 318 return false; 319 end; 320 321 -- show only if player controller is active 322 return PlayerController.isActive and (AnchorPoint.activeVehicle == nil or (AnchorPoint.activeVehicle == self and self.anchorPointName == nil)); 323 end;
RopeWinch:ropeWinchConnectorCallback()
This is called every time the player clicks onto the control element at the winch intake. It may indicate that the player either wants to take the rope (and attach it to an anchor point) or to take in the rope.
328 function RopeWinch:ropeWinchConnectorCallback() 329 -- rope connector was hit 330 if AnchorPoint.activeVehicle == self then 331 -- turn off winch, no longer available for attaching 332 self:attachWinchToAnchorPoint(nil); 333 AnchorPoint.activeVehicle = nil; 334 335 else 336 -- player wants to attach us 337 AnchorPoint.activeVehicle = self; 338 self:attachWinchToAnchorPoint("player"); 339 340 if g_scenario.tutorialAgent ~= nil then 341 g_scenario.tutorialAgent:callByScript("attachRopeWinch"); 342 end; 343 end; 344 end;
RopeWinch:onEnter()
Shows an info message when the player enters a winch vehicle for the first time.
348 function RopeWinch:onEnter() 349 -- shoot out a help message 350 if g_scenario.tutorialAgent ~= nil then 351 g_scenario.tutorialAgent:callByScript("winchSnowcat"); 352 end; 353 end;
Copyright
All contents of this page may be used for modding use for Winter Resort Simulator only. Any use exceeding this regulation is not permitted.
Copyright (C) HR Innoways, 2020. All Rights Reserved.