Vehicles/VehicleManager.lua

Together with Vehicle, VehicleManager is at the core of all scripts relating to vehicles. It is responsible for managing many features that are shared between all vehicles (such as switching vehicles using the Tab key and the distribution of various events such as update(dt)).

  32  VehicleManager                      = VehicleManager or {};

VehicleManager:setup()

Is called upon initialisation of the game. This will also set up SnowmakingManager.

  37  function VehicleManager:setup()
  38      self.vehicles                   = {};
  39      self.vehiclesByOrder            = {};
  40      self.vehicleTemplates           = {};
  41      self.allowSwitchVehicle         = true;
  42      self.currentVehicle             = nil; -- the vehicle this player is currently in
  43  
  44      self.vehicleSpawnPositons       = {};
  45      self.dummyFunction              = function() end;
  46  
  47      SnowmakingManager:setup();
  48  end;

VehicleManager:newFunction(functionName)

This function will return a new function which calls the function named functionName (string) for all vehicle scripts of the vehicle it is called on. Check the load() function of any vehicle script (e.g. RopeWinch) to see how it is used.

  51  function VehicleManager:newFunction(functionName)
  52      -- returns a function that calls all vehicle scripts
  53      
  54      return function(self, ...)
  55          -- call all vehicle scripts
  56          
  57          for _, script in pairs(self.vehicleScripts) do
  58              -- ensure its not nil
  59              if script[functionName] ~= nil then
  60                  script[functionName](self, ...);
  61              end;
  62          end;
  63      end;
  64  end;

VehicleManager:dummyFunction()

This function will return a dummy function that does not perform any action at all. This is easier than checking the functions against nil in some cases.

  68  function VehicleManager:dummyFunction()
  69      return self.dummyFunction;
  70  end;

VehicleManager:registerVehicleTemplate(typeName, template)

Registers a new vehicle template named typeName (string). The data regarding this template are stored within template (table).

  75  function VehicleManager:registerVehicleTemplate(typeName, template)
  76      self.vehicleTemplates[typeName] = template;
  77  end;

VehicleManager:registerVehicle(vehicle)

Registers a new vehicle instance vehicle (table of the instance).

  80  function VehicleManager:registerVehicle(vehicle)
  81      -- add this entry to vehicles list
  82      -- note: key is actually the vehicle value
  83      self.vehicles[vehicle.mainId or vehicle.id]     = vehicle;
  84      
  85      -- avoid double entries
  86      for n, vhc in pairs(self.vehiclesByOrder) do
  87          if vehicle == vhc then
  88              return;
  89          end;
  90      end;
  91      
  92      table.insert(self.vehiclesByOrder, vehicle);
  93  end;

VehicleManager:unregisterVehicle(vehicle)

Unregisters the vehicle instance vehicle (table of the instance). This is called in Vehicle:destroy() (see Vehicle).

  96  function VehicleManager:unregisterVehicle(vehicle)
  97      -- delete this entry
  98      if vehicle.mainId ~= nil then
  99          self.vehicles[vehicle.mainId]       = nil;
 100      end;
 101      if vehicle.id ~= nil then
 102          self.vehicles[vehicle.id]           = nil;
 103      end;
 104      
 105      for n, vhc in pairs(self.vehiclesByOrder) do
 106          if vehicle == vhc then
 107              table.remove(self.vehiclesByOrder, n);
 108              break;
 109          end;
 110      end;
 111  end;

VehicleManager:onEnterVehicle(vehicle)

Is called every time the player enters the vehicle vehicle (table of the instance).

 114  function VehicleManager:onEnterVehicle(vehicle)
 115      self.currentVehicle             = vehicle;
 116  
 117      -- reset player controller's parent
 118      PlayerController:setParent(0);
 119  
 120      -- micro lag: call this every now and then
 121      g_scenario:allowMicroLag();
 122  end;

VehicleManager:onLeaveVehicle(vehicle)

Is called every time the player leaves the vehicle vehicle (table of the instance).

 125  function VehicleManager:onLeaveVehicle(vehicle)
 126      if self.currentVehicle == vehicle then
 127          self.currentVehicle         = nil;
 128      end;
 129  end;

VehicleManager:switchVehicle(delta)

Is called every time the player wants to switch vehicles, e.g. by pressing the Tab key. delta (eiter larger 0 or less than 0). The function walks through all available vehicles in the given order and activates the next possible one.

 132  function VehicleManager:switchVehicle(delta)
 133      if not self.allowSwitchVehicle or not g_scenario.freeplayMode or IngameEditorGui.isActive then
 134          return;
 135      end;
 136  
 137      -- this is a bit slow, but simpler in programming
 138      local currentIndex              = 0;
 139      local vehicleCount              = 0;
 140      
 141      for i, vehicle in pairs(self.vehiclesByOrder) do
 142          
 143          -- count all vehicles that have driver seats
 144          if vehicle.driverSeat ~= nil and not vehicle.nonTabbable then
 145              vehicleCount            = vehicleCount + 1;
 146          end;
 147          
 148          -- determine current vehicle number in that list
 149          if vehicle == self.currentVehicle then
 150              currentIndex            = vehicleCount;
 151          end;
 152      end;
 153  
 154      if self.currentVehicle ~= nil then
 155          self.currentVehicle:leaveByTab();
 156      end;
 157      VehicleSeat.leaveActiveSeat();
 158      
 159      local newIndex                  = 1;
 160      -- determine the vehicle we want to jump into
 161      if currentIndex == 0 then
 162          newIndex                    = (delta > 0) and 1 or 0;
 163      else
 164          newIndex                    = currentIndex + delta;
 165      end;
 166      
 167      -- limit to bounds
 168      if newIndex > vehicleCount then
 169          newIndex                    = 1;
 170          
 171      elseif newIndex < 1 then
 172          newIndex                    = vehicleCount + newIndex;
 173      end;
 174      
 175      -- jump to that vehicle
 176      local n = 1;
 177      for i, vehicle in pairs(self.vehiclesByOrder) do
 178          
 179          -- count all vehicles that have driver seats
 180          if vehicle.driverSeat ~= nil and not vehicle.nonTabbable then
 181              if n == newIndex then
 182                  -- jump into this vehicle, then quit
 183                  vehicle:enterByTab();
 184                  return;
 185              end;
 186              
 187              n                       = n + 1;
 188          end;
 189      end;
 190  end;

VehicleManager:update(...)

This is directly called by the game's update function. It distributes the update(dt) call to all vehicles.

 193  function VehicleManager:update(...)
 194      -- switch vehicle function
 195      if InputMapper:getKeyDown(InputMapper.Vehicle_Switch_Inverse) then
 196          -- switch with inverse order
 197          self:switchVehicle(-1);
 198          
 199      elseif InputMapper:getKeyDown(InputMapper.Vehicle_Switch) then
 200          -- switch to next vehicle
 201          self:switchVehicle(1);
 202      end;
 203      
 204      for n, vehicle in pairs(self.vehicles) do
 205          if vehicle.update ~= nil then
 206              vehicle:update(...);
 207          end;
 208      end;
 209      for n, vehicle in pairs(self.vehicles) do
 210          if vehicle.postUpdate ~= nil then
 211              vehicle:postUpdate(...);
 212          end;
 213      end;
 214  
 215      if g_currentVehicleSeat ~= nil and g_currentVehicleSeat.update ~= nil then
 216          g_currentVehicleSeat:update(...);
 217      end;
 218  end;

VehicleManager:fixedUpdate(...)

This again is called directly by the game's fixedUpdate function. For performance reasons, the fixedUpdate(dt) function will only be distributed to the currently active vehicle.

 221  function VehicleManager:fixedUpdate(...)
 222      --[[for n, vehicle in pairs(self.vehicles) do
 223          if vehicle.fixedUpdate ~= nil then
 224              vehicle:fixedUpdate(...);
 225          end;
 226      end;
 
 227  
 228      if self.currentVehicle ~= nil and self.currentVehicle.fixedUpdate ~= nil then
 229          self.currentVehicle:fixedUpdate(...);
 230      end;
 231  
 232      if g_currentVehicleSeat ~= nil and g_currentVehicleSeat.fixedUpdate ~= nil then
 233          g_currentVehicleSeat:fixedUpdate(...);
 234      end;
 235  end;

VehicleManager:onGUI(...)

One more time, the function is called directly by the game's onGUI function.

 238  function VehicleManager:onGUI(...)
 239      for n, vehicle in pairs(self.vehicles) do
 240          if vehicle.onGUI ~= nil then
 241              vehicle:onGUI(...);
 242          end;
 243      end;
 244  end;

VehicleManager:spawnVehicle(templateName, x,y,z, rx,ry,rz)

Spawns a new vehicle of template name templateName (string) at position x,y,z (all float) with rotation rx,ry,rz (all float in degrees).

 256  function VehicleManager:spawnVehicle(templateName, x,y,z, rx,ry,rz)
 257      -- find the template
 258      local template                  = self.vehicleTemplates[templateName];
 259      
 260      if template == nil then
 261          print("Error VehicleManager: attempt to spawn non-existing vehicle with template name " .. tostring(templateName) .. ". Make sure you've installed all mods that are used in this savegame.");
 262          return nil;
 263      end;
 264      
 265      -- load the configuration
 266      local mod                       = ModLoader.mods[template.modName];
 267      local bundleId                  = 0;
 268      if mod ~= nil and mod.modFilename ~= nil then
 269          bundleId                    = Utils.loadAssetBundleFromFile(mod.modFilename);
 270      end;
 271      local dataTable                 = template.dataTable;
 272      
 273      local customClass               = dataTable.class or Vehicle;
 274      local instance                  = customClass:new(bundleId, modName, templateName, dataTable, vehicleId);
 275  
 276      instance:spawnAt(x,y,z, rx or 0, ry or 0, rz or 0)
 277      return instance;
 278  end;

VehicleManager:saveToTable()

Called directly by the scenario when the game is saved. The function passes the call on to each vehicle.

 281  function VehicleManager:saveToTable()
 282      local vehicles = {};
 283      for k, vehicle in pairs(VehicleManager.vehicles) do
 284          table.insert(vehicles, vehicle:saveToTable());
 285      end;
 286      return vehicles;
 287  end;

VehicleManager:loadFromTable(tbl)

Also called directly by the scenario when the game is loaded. The function spawns a new vehicle for each table entry and calls Vehicle:loadFromTable() on each vehicle.

 290  function VehicleManager:loadFromTable(tbl)
 291      if tbl == nil then return end;
 292      
 293      for k, vehicle in pairs(tbl) do
 294          local vehicleInstance       = VehicleManager:spawnVehicle(vehicle.templateName, vehicle.px,vehicle.py,vehicle.pz, vehicle.rx,vehicle.ry,vehicle.rz);
 295  
 296          -- check this against nil (nil will be returned if the template is invalid)
 297          if vehicleInstance ~= nil then
 298              vehicleInstance:loadFromTable(vehicle);
 299          end;
 300      end;
 301  end;

VehicleManager:addSpawnPosition(id, customVehicleRotation)

Permanently adds a spawn position to the game. Any time vehicles are bought or resetted, they will be placed on the first available spawn position.

 304  function VehicleManager:addSpawnPosition(id, customVehicleRotation)
 305      local pos                       = {};
 306      pos.startPosition               = VectorUtils.getWorldPosition(id);
 307      pos.endPosition                 = VectorUtils.getWorldPosition(getChildAt(id, 0));
 308      pos.width                       = (pos.endPosition - pos.startPosition):magnitude();
 309      pos.direction                   = (pos.endPosition - pos.startPosition):normalized();
 310  
 311      x,y,z                           = getWorldRotation(id);
 312      pos.ry                          = y;
 313  
 314      pos.vehicleRotation             = customVehicleRotation or y;
 315  
 316      table.insert(self.vehicleSpawnPositons, pos);
 317  end;

VehicleManager:newTempSpawnPosition(posBegin, posEnd, ry, customVehicleRotation)

Temporarily creates a new spawn position, e.g. if a tutorial mission is active.

 320  function VehicleManager:newTempSpawnPosition(posBegin, posEnd, ry, customVehicleRotation)
 321      local place                     = {};
 322      place.startPosition             = posBegin;
 323      place.endPosition               = posEnd;
 324      place.width                     = (posEnd - posBegin):magnitude();
 325      place.direction                 = (posEnd - posBegin):normalized();
 326  
 327      place.ry                        = ry or 0;
 328      place.vehicleRotation           = customVehicleRotation or place.ry;
 329  
 330      return place;
 331  end;

VehicleManager:addSpawnPositionOverride(place)

Temporarily adds a spawn position to the overrides. Any time a vehicle is bought or resetted, override spawn positions will be preferred over permanent spawn positions.

 334  function VehicleManager:addSpawnPositionOverride(place)
 335      if self.spawnPositionOverrides == nil then
 336          self.spawnPositionOverrides = {};
 337      end;
 338  
 339      table.insert(self.spawnPositionOverrides, place);
 340  end;

VehicleManager:cleanupSpawnPositionOverrides()

Removes any temporary spawn position overrides.

 343  function VehicleManager:cleanupSpawnPositionOverrides()
 344      self.spawnPositionOverrides     = nil;
 345  end;

VehicleManager:findPosition(width, length)

Returns the first suitable spawn position (spawn position overrides first, then permanent spawn positions).

 348  local copy                  = function(from, to)
 349      for k, v in ipairs(from) do
 350          table.insert(to, v);
 351      end;
 352  end;
 353  
 354  function VehicleManager:findPosition(width, length)
 355      local positions                 = {};
 356  
 357      if self.spawnPositionOverrides ~= nil then
 358          copy(self.spawnPositionOverrides, positions);
 359      end;
 360      
 361      copy(self.vehicleSpawnPositons, positions);
 362  
 363  
 364      local height                = 5;
 365      width, length               = width+0.5, length+1; -- leave a minimum gap
 366  
 367      for k, place in pairs(positions) do
 368          -- do some box casts there
 369  
 370          for i=width/2, place.width-width/2, 1 do
 371              local p                 = place.startPosition + place.direction:multiply(i);
 372              p.y                     = Utils.sampleTerrainHeight(p.x, p.z) + height/2;
 373  
 374              local isFull            = Utils.checkBox(Vector3:new(p.x, p.y, p.z), Vector3:new(width, height, length), place.ry);
 375  
 376              if not isFull then
 377                  return p.x,p.y,p.z, place.vehicleRotation;
 378              end;
 379          end;
 380      end;
 381  
 382      if #positions <= 0 and PlayerController.isActive then
 383          -- there are no spawn positions within the map!
 384          -- therefore spawn at player position
 385          local p                     = VectorUtils.getWorldPosition(PlayerController.transformId);
 386          p.y                         = Utils.sampleTerrainHeight(p.x, p.z) + height/2;
 387  
 388          local isFull                = Utils.checkBox(Vector3:new(p.x, p.y, p.z), Vector3:new(width, height, length), 0, (2^32) - 1 - 2^19 - 2^2);
 389  
 390          -- return this as backup position
 391          if not isFull then
 392              local rx,ry,rz          = getWorldRotation(PlayerController.transformId);
 393              return p.x,p.y,p.z, ry, true;
 394          end;
 395      end;
 396  
 397      -- nothing found if we actually arrive here (so nothing is returned)
 398  end;

VehicleManager:addMaintenanceExpenses(dayShare)

Charges maintenance expenses for all vehicles. The daily maintenance costs are multiplied by the factor dayShare (float, share of the day passed since the last call).

 401  function VehicleManager:addMaintenanceExpenses(dayShare)
 402      -- add maintenance cost for each vehicle
 403      for k, v in pairs(self.vehiclesByOrder) do
 404          local cost                  = v:getMaintenanceCost() or 0;
 405          g_scenario.accounting:addExpense("vehicles", -cost*dayShare);
 406      end;
 407  end;

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.