Objects/DoorController.lua

This script is used for all static in-game doors (i.e. all doors except vehicle or ropeway carrier doors). In theory, you can also use the script on vehicles, but we recommend to use AnimatedParts instead in that case.

  24  DoorController                      = DoorController or {};
  25  local DoorControllerClass           = Class(DoorController);

DoorController:load(collisionId, doorId, textOn, textOff, doorCloser, enableRwTimescale, soundConfiguration)

Gets called for each new DoorController, i.e. by the onCreate event.

  • collisionId (transform id): Object id of the door collision. This will be the control element of the door (which can be clicked by the player).
  • doorId (transform id): Object id of the Animation component. Make sure this is not the same object as the collision, i.e. collision and Animation must be separated.
  • textOn (string): Text that will be shown when the player hovers the collision and the door is closed (animation position = 0 %).
  • textOff (string): Text that will be shown when the player hovers the collision and the door is opened (animation position = 100 %).
  • doorCloser (optional bool): true if the door should close itself automatically if the player is more than 2 metres away
  • enableRwTimescale (optional bool): true if the door should be accelerated by ropeway timescale (will not be accelerated otherwise)
  • soundConfiguration (optional datatable): Configuration of sounds that should be played if the player interacts with the door. This is usually a lot of work and not necessary for mods.
  37  function DoorController:load(collisionId, doorId, textOn, textOff, doorCloser, enableRwTimescale, soundConfiguration)
  38      -- first store the main id
  39      self.collisionId                = collisionId;
  40      self.doorId                     = doorId;
  41      self.animLength                 = Animation.getLength(self.doorId);
  42      Animation.setAutoPlay(self.doorId, false);
  43  
  44      local x,y,z                     = getWorldPosition(doorId);
  45      self.savegameKey                = "doorController" .. string.format(":%s @ (%.4f, %.4f, %.4f)", getName(self.doorId), x,y,z);
  46      
  47      self.textOn                     = l10n.getDollar(textOn or "");
  48      self.textOff                    = l10n.getDollar(textOff or textOn);
  49      
  50      self.state                      = false;
  51      self.position                   = 0;
  52      self.enableRwTimescale          = enableRwTimescale; -- defines whether the door is influenced by the ropeway timescale (false or nil: real time only, not depending on timescale)
  53      self.targetPosition             = self.state and self.animLength or 0;
  54      self.doorCloser                 = getNoNil(doorCloser, true); -- closes the door automatically if player is more than 2 metres away
  55  
  56      if soundConfiguration ~= nil then
  57          self.soundConfiguration     = {};
  58  
  59          for n, v in pairs(soundConfiguration) do
  60              -- get all audio source lengths
  61              if
  62                  v.id ~= nil and v.id ~= 0 and
  63                  (v.triggerTime ~= nil or v.finishTime ~= nil)
  64              then
  65                  local tbl           = {};
  66                  tbl.id              = v.id;
  67                  tbl.direction       = getNoNil(v.direction,     1);     -- 1: state 0=>1, 2: state 1=>0
  68  
  69                  if v.triggerOnPress then
  70                      tbl.triggerOnPress  = true;
  71  
  72                  elseif v.triggerTime ~= nil then
  73                      tbl.triggerTime     = v.triggerTime;    -- time in seconds!
  74  
  75                  else
  76                      tbl.triggerTime     = (v.finishTime - AudioSource.getLength(tbl.id) * (tbl.direction == 1 and 1 or -1));
  77                  end;
  78                  
  79                  if tbl.triggerTime ~= nil then
  80                      tbl.triggerTime     =   clamp(tbl.triggerTime, 0, self.animLength);
  81                  end;
  82  
  83                  AudioSource.stop(tbl.id);
  84                  table.insert(self.soundConfiguration, tbl);
  85              end;
  86          end;
  87      end;
  88      
  89      Animation.sampleTime(self.doorId, self.position);
  90      
  91      if self.collisionId == 0 then
  92          print("Error while loading Door " .. tostring(getName(self.doorId)) .. ". Collision index is invalid.");
  93      end;
  94      self.buttonId                   = ControlElement.new(self.collisionId);
  95      ControlElement.setCallback(self.buttonId, 1, function() self:setState(not self.state); end);
  96      
  97      self:setState(false, nil, true);
  98  
  99      -- load the state from savegame
 100      -- we will also be registered to a destroy listener
 101      -- BUT as always: we will have to deregister the object ourselves
 102      g_scenario:addObjectAndLoad(self, self.doorId, self.savegameKey);
 103  end;

DoorController:destroy()

Is called if the door is destroyed (e.g. if the ropeway or prefab containing the door is deleted).

 107  function DoorController:destroy()
 108      if g_scenario ~= nil then
 109          g_scenario:removeObject(self, self.savegameKey);
 110      end;
 111  
 112      ControlElement.destroy(self.buttonId);
 113  end;

DoorController:saveToTable(savegame)

Saves the door state to the savegame. Note that this method is designed for static prefabs (used in in-game editor prefabs or ropeways), but not for physics-dependent objects (e.g. vehicles, carriers). This is also the reason why we recommend not to use DoorController inside vehicles.

 117  function DoorController:saveToTable(savegame)
 118      if savegame[self.savegameKey] ~= nil then
 119          print("Warning while saving to table: Multiple DoorControllers are using the key " .. tostring(self.savegameKey) .. ". All of them will have the same state when loading the savegame. Please avoid DoorControllers that are named the same and placed at the same spot.");
 120          return;
 121      end;
 122      savegame[self.savegameKey]      = self.state;
 123  end;

DoorController:loadFromTable(savegame)

Retrieves the information stored in the savegame and applies it to the door.

 127  function DoorController:loadFromTable(savegame)
 128      if savegame == nil then return end;
 129      
 130      local state                     = savegame[self.savegameKey];
 131      if state ~= nil then
 132          self:setState(state, true, true);
 133      end;
 134  end;

DoorController:setState(state, jump, noEvent)

Sets the state of the door controller.

  • state (bool): true if the door shall be open, otherwise false.
  • jump (optional bool): true if you want the door to jump to the new position.
  • noEvent (optional bool): true if this was triggered by the EventDoorController.

If necessary, updating the object is resumed (g_scenario:resumeUpdatingObject(self)).

 143  function DoorController:setState(state, jump, noEvent)
 144      self.state                      = state;
 145      self.targetPosition             = self.state and self.animLength or 0;
 146  
 147      -- jump there
 148      if jump then
 149          self.position               = self.targetPosition;
 150          Animation.sampleTime(self.doorId, self.position);
 151      end;
 152  
 153      ControlElement.setName(self.buttonId, self.state and self.textOff or self.textOn);
 154  
 155      g_scenario:resumeUpdatingObject(self);
 156  
 157      if self.soundConfiguration ~= nil then
 158          for k, v in pairs(self.soundConfiguration) do
 159              if v.triggerOnPress then
 160                  AudioSource.play(v.id);
 161              end;
 162          end;
 163      end;
 164  
 165      if not noEvent then
 166          EventDoorController:send(self, state, jump);
 167      end;
 168  end;

DoorController:update(dt)

Update function for updating the animation and handling the door closer (depending on self.doorCloser and the gameplay settings). If the call is no longer necessary (e.g. if no animation is taking place any more), the object will no longer be updated (g_scenario:pauseUpdatingObject(self)).

 172  function DoorController:update(dt)
 173      if g_isMaster and self.doorCloser and GameplaySettings.autoCloseDoors and self.state then
 174          local closeDoor             = true;
 175          local doorPosition          = VectorUtils.getWorldPosition(self.doorId);
 176  
 177          -- if any player is close the door, we shall not close it
 178          if g_isServer then
 179              for k, player in pairs (g_networkGame.players) do
 180                  local isNear        = player:getIsActive() and player:getSqrDistanceToVector(doorPosition) < 25;
 181                  if isNear then
 182                      closeDoor       = false;
 183                      break;
 184                  end;
 185              end;
 186  
 187          elseif g_scenario.player ~= nil then
 188              -- must be singleplayer, here we only have one player
 189              closeDoor               = g_scenario.player:getSqrDistanceToVector(doorPosition) >= 25;
 190          end;
 191  
 192          if closeDoor then
 193              self:setState(false);
 194          end;
 195      end;
 196      
 197      if self.position == self.targetPosition then
 198          if g_isClient or not (self.doorCloser and GameplaySettings.autoCloseDoors and self.state) then
 199              g_scenario:pauseUpdatingObject(self);
 200          end;
 201          return;
 202      end;
 203      
 204      -- we need to move
 205      if self.enableRwTimescale then
 206          dt                          = dt * g_scenario.environment.activeRwTimescale;
 207      end;
 208      local oldPosition               = self.position;
 209      self.position                   = Utils.moveTowards(self.position, self.targetPosition, dt);
 210  
 211      -- update sounds
 212      if self.soundConfiguration ~= nil then
 213          local direction             = oldPosition < self.position and 1 or 2;
 214          for k, v in pairs(self.soundConfiguration) do
 215              
 216              if v.triggerTime ~= nil and v.direction == direction then
 217                  if (oldPosition <= v.triggerTime and self.position >= v.triggerTime) or (oldPosition >= v.triggerTime and self.position <= v.triggerTime) then
 218                      AudioSource.play(v.id);
 219                  end;
 220              end;
 221          end;
 222      end;
 223  
 224  
 225      Animation.sampleTime(self.doorId, self.position);
 226  end;
 227  

DoorController:writeResync()

Resynchronizes all variables when a new player joins the game. The data sent by writeResync will be received by readResync.

 231  function DoorController:writeResync()
 232      streamWriteBool(self.state);
 233  end;
 234  

DoorController:readResync()

Resynchronizes all variables when a new player joins the game. The data sent by writeResync will be received by readResync.

 238  function DoorController:readResync()
 239      local state                     = streamReadBool();
 240      self:setState(state, true, true);
 241  end;

All contents of this page may be used for modding use for Winter Resort Simulator - Season 2 only. Any use exceeding this regulation is not permitted.

Copyright (C) HR Innoways, 2021. All Rights Reserved.