-- Do nothing in multiplayer on the server (isServer always returns false in single player) -- 在服务器的多人游戏中不执行任何操作(在单人游戏中,isServer始终返回false) if isServer() then return end

-- The speed returned for a parked vehicle can sometimes end up non-zero, so set a threshold for 'stationary' -- 停放的车辆返回的速度有时可能不为零,因此设置'静止'的阈值。 local stationaryThreshold = 0.1


-- Entering vehicles

--- Test if its unsafe for a player to enter their vehicle interior, based on the sandbox settings. This can only be --- tested client-side because IsoCell:getNearestVisibleZombie() appears to be client only. -- 根据沙盒设置测试是否不安全让玩家进入车辆内部。这只能在客户端测试,因为IsoCell:getNearestVisibleZombie()似乎只在客户端。 local function getPlayerHassledByZombiesReason(vehicle, player) if math.abs(vehicle:getCurrentSpeedKmHour()) >= stationaryThreshold then -- Zombies nearby shouldn't stop someone entering if the vehicle is on the move. -- 如果车辆在移动,附近的僵尸不应该阻止任何人进入 return nil end if SandboxVars.RVInterior.NotWhenChased and player:getStats():getNumChasingZombies() > 0 then return getText('UI_zombiesChasing') end local zombie = getCell():getNearestVisibleZombie(player:getPlayerNum()) if SandboxVars.RVInterior.SafeZombieDistance > 0 and zombie and zombie:getSquare():getMovingObjects():indexOf(zombie) >= 0 then -- to ignore deleted zombies -- 忽略已删除的僵尸 local distance = IsoUtils.DistanceToSquared(zombie:getX(), zombie:getY(), zombie:getZ(), player:getX(), player:getY(), player:getZ()) if distance < SandboxVars.RVInterior.SafeZombieDistance * SandboxVars.RVInterior.SafeZombieDistance then return getText('UI_zombiesNearby') end end return nil end

-- Callback to signal the server that a player is trying to enter a vehicle interior -- 回调以向服务器发出信号,表明玩家正在尝试进入车辆内部 local function onPlayerEnterInterior(player) -- Turns out you can exit a vehicle while still interacting with the radial menu, so re-check validity. -- 事实证明,您可以在仍然与径向菜单交互时退出车辆,因此请重新检查有效性 local vehicle = player:getVehicle() if not RVInterior.vehicleHasInteriorParameters(vehicle) then return end local hassled = getPlayerHassledByZombiesReason(vehicle, player) if hassled then player:Say(getText('UI_cantEnter')) player:Say(hassled) elseif RVInterior.isPlayerTrespassingInInterior(vehicle, player) then player:Say(getText('UI_cantEnter')) player:Say(getText('UI_noEnterInteriorSafehouse')) elseif vehicle:isDriver(player) and math.abs(vehicle:getCurrentSpeedKmHour()) >= stationaryThreshold then player:Say(getText('UI_cantEnter')) player:Say(getText('UI_vehicleMoving')) else sendClientCommand(player, 'RVInterior','clientStartEnterInterior',{ vehicleId = vehicle:getId() }) end end

-- Loads the textures. -- 加载纹理 local function loadRVInteriorTextures() getTexture('media/textures/rvInteriorEnter.png') -- Radial Menu UI - enter (normal) getTexture('media/textures/rvInteriorEnterGrey.png') -- Radial Menu UI - enter (disabled) end Events.OnGameBoot.Add(loadRVInteriorTextures)

-- Save the default function so we can invoke it from within our wrapper -- 保存默认函数,以便我们可以在包装器中调用它 if not RVInterior.oldShowRadialMenu then RVInterior.oldShowRadialMenu = ISVehicleMenu.showRadialMenu end if not RVInterior.old_ISVehicleMenu_showRadialMenuOutside then RVInterior.old_ISVehicleMenu_showRadialMenuOutside = ISVehicleMenu.showRadialMenuOutside end

-- Wrap the current version of showRadialMenu with our own version. -- 使用我们自己的版本包装 showRadialMenu 的当前版本 function ISVehicleMenu.showRadialMenu(player) RVInterior.oldShowRadialMenu(player) RVInterior.showRadialMenu(player) end

function RVInterior.addRadialMenu(player) if player:isNPC() then return end local vehicle = ISVehicleMenu.getVehicleToInteractWith(player) if not vehicle then return end local vehicleName = vehicle:getScript():getFullName() if not vehicleName then return end

-- If they're inside a supported vehicle type, add the radial option to enter the interior.
-- 如果他们在受支持的车辆类型中,则添加进入内部的径向选项
if RVInterior.vehicleHasInteriorParameters(vehicle) and vehicle then
    local menu = getPlayerRadialMenu(player:getPlayerNum())
    local texture = getTexture('media/textures/rvInteriorEnter.png')
    if getPlayerHassledByZombiesReason(vehicle, player) or
            RVInterior.isPlayerTrespassingInInterior(vehicle, player) or
            (vehicle:isDriver(player) and math.abs(vehicle:getCurrentSpeedKmHour()) >= stationaryThreshold) then
        -- Grey out the enter option if they can't enter
        -- 如果他们不能进入,则使进入选项变灰
        texture = getTexture('media/textures/rvInteriorEnterGrey.png')
    end
    return vehicle, vehicleName, menu, texture
else return end

end

function RVInterior.showRadialMenu(player) if not RVInterior.addRadialMenu(player) then return end local vehicle, vehicleName, menu, texture = RVInterior.addRadialMenu(player) if player:isSeatedInVehicle() then if not RVInterior.canEnterFromBackList[vehicleName] then menu:addSlice(getText('UI_enterrvinterior'), texture, onPlayerEnterInterior, player) end end end

function ISVehicleMenu.showRadialMenuOutside(player) RVInterior.old_ISVehicleMenu_showRadialMenuOutside(player) if not RVInterior.addRadialMenu(player) then return end local vehicle, vehicleName, menu, texture = RVInterior.addRadialMenu(player) if RVInterior.canEnterFromBackList[vehicleName] then local part = vehicle:getUseablePart(player) if not part then return end if part:getId() == 'TrunkDoor' or part:getId() == 'DoorRear' then menu:addSlice(getText('UI_enterrvinterior'), texture, onPlayerEnterInterior, player) end end end


-- Exiting vehicles

local function onPlayerSelectExit(player) if player:isSitOnGround() then player:setSitOnGround(false) end sendClientCommand(player, 'RVInterior','clientStartExitInterior', {}) end

local function addExitOption(playerId, context) local player = getSpecificPlayer(playerId) if RVInterior.playerInsideInterior(player) then context:addOption(getText('UI_exitrvinterior'), player, onPlayerSelectExit) end end

Events.OnFillWorldObjectContextMenu.Add(addExitOption)


-- Keyboard hotkey

local keyData = { key = Keyboard.KEY_NONE, -- No default value, user needs to configure it. name = 'RV_INTERIOR_ENTER', -- Maps to UI_optionscreen_binding_RV_INTERIOR_ENTER in Translate files }

if ModOptions and ModOptions.getInstance then ModOptions:AddKeyBinding('[Vehicle]', keyData); end

local function onKeyPressed(keyCode) if keyCode == keyData.key then local player = getSpecificPlayer(0) -- Keyboard shortcuts are just for the first player if RVInterior.playerInsideInterior(player) then onPlayerSelectExit(player) else onPlayerEnterInterior(player) end end end

Events.OnKeyPressed.Add(onKeyPressed)


-- Server commands

local serverCommandHandlers = {}

local function onServerCommand(module, command, arguments) if module ~= 'RVInterior' then return end if serverCommandHandlers[command] then -- This peculiar expression works to get the player on this client from the onlineID in both SP and MP -- 此奇特的表达式适用于从SP和MP中的onlineID获取此客户端上的玩家 local player = getSpecificPlayer(arguments.playerId % 4) serverCommandHandlers[command](player, arguments) end end

Events.OnServerCommand.Add(onServerCommand)

serverCommandHandlers.serverNoFreeInteriors = function(player) player:Say(getText('UI_noFreeInteriors')) end

--- Jump to the vehicle entry point -- 跳到车辆入口点 local function sendPlayerToEntryCoordinates(player, vehicleName, interiorInstance) local coordinates = RVInterior.getInteriorCoordinates(vehicleName, interiorInstance) -- Current coordinates -- 当前坐标 player:setX(coordinates.x + 0.5) player:setY(coordinates.y + 0.5) player:setZ(coordinates.z) -- Last coordinates, for movement calculation purposes -- 最后一个坐标,用于运动计算 player:setLx(player:getX()) player:setLy(player:getY()) player:setLz(player:getZ()) -- Change facing -- 改变朝向 player:faceLocation(coordinates.x, coordinates.y - 1) end

local playerSeats = {} --- Handle the player entering a vehicle interior -- 处理玩家进入车辆内部 serverCommandHandlers.serverEnterInterior = function(player, arguments) local vehicle = player:getVehicle() local vehicleName = vehicle:getScript():getFullName() playerSeats[player:getOnlineID()] = vehicle:getSeat(player) if arguments.isBoat and TickControl.main then -- Disable Aquatsar's onTick which makes players in water get wet etc. -- 禁用 Aquatsar 的 onTick,它会使玩家在水中变湿等 Events.OnTick.Remove(TickControl.main) end vehicle:exit(player) if ChimeTick then -- Remove the Vehicle Light Chime mod's onTick callback, if present. -- 删除 Vehicle Light Chime 模块的 onTick 回调(如果存在)。 Events.OnTick.Remove(ChimeTick) end getPlayerVehicleDashboard(player:getPlayerNum()):setVehicle(nil) sendPlayerToEntryCoordinates(player, vehicleName, arguments.interiorInstance) -- Wait for things to load before telling the server that we've entered. -- 等待加载完成后,再告诉服务器我们已经进入 local retries = 500 local playerEnteredInteriorClosure playerEnteredInteriorClosure = function() -- After a player has entered an interior, wait until at least the tile they're on has loaded and then tell -- the server to update the fuel in the generator. -- 在玩家进入内部后,等待至少他们所在的瓷砖加载完成,然后告诉服务器更新发电机的燃料 local square = player:getCurrentSquare() if square == nil then retries = retries - 1 if retries == 0 then -- The interior map is not present - ask the player if they want to return to the vehicle. -- 内部地图不存在 - 询问玩家是否要返回车辆。 local mapsMissingDialogCallback = function (_this, button) if button.internal == 'YES' then onPlayerSelectExit(player) Events.OnPlayerUpdate.Remove(playerEnteredInteriorClosure) else -- Reset the retries counter -- 重置重试计数器 retries = 500 end end local mapsMissingDialog = ISModalDialog:new(0, 0, 250, 150, getText('UI_rvInteriorMapsMissing'), true, nil, mapsMissingDialogCallback) mapsMissingDialog:initialise() mapsMissingDialog:addToUIManager() if JoypadState.players[player:getPlayerNum()+1] then setJoypadFocus(player:getPlayerNum(), mapsMissingDialog) end end return end if arguments.isBoat and TickControl.main then -- Re-enable Aquatsar's onTick -- 重新启用 Aquatsar 的 onTick Events.OnTick.Add(TickControl.main) end sendClientCommand(player, 'RVInterior', 'clientFinishEnterInterior', {}) Events.OnPlayerUpdate.Remove(playerEnteredInteriorClosure) end Events.OnPlayerUpdate.Add(playerEnteredInteriorClosure) end

-- Test if a vehicle matches the expected name and interior instance. -- 测试车辆是否与预期名称和内部实例匹配 local function doesVehicleMatch(vehicle, vehicleName, interiorInstance) local canonicalVehicleName = RVInterior.getVehicleName(vehicle:getScript():getFullName(), true) if canonicalVehicleName ~= vehicleName then return false end local modData = RVInterior.getVehicleModData(vehicle, RVInterior.getInteriorParameters(canonicalVehicleName)) return modData and interiorInstance == modData.interiorInstance end

--- Handle the player leaving an interior -- 处理玩家离开内部 serverCommandHandlers.serverLeaveInterior = function(player, arguments) player:setX(arguments.x) player:setY(arguments.y) player:setZ(arguments.z) player:setLx(arguments.x) player:setLy(arguments.y) player:setLz(arguments.z) if arguments.isBoat and TickControl.main then -- Disable Aquatsar's onTick which makes players in water get wet etc. -- 禁用 Aquatsar 的 onTick,它会使玩家在水中变湿等 Events.OnTick.Remove(TickControl.main) end local retries = 500 local vehicleIndex = 0 local playerLeftInteriorClosure playerLeftInteriorClosure = function () -- Wait until at least the tile they're now on has loaded before trying to find the vehicle. -- 等待至少他们现在所在的瓷砖加载完成,然后再尝试查找车辆 local square = player:getCurrentSquare() if square == nil then return end -- Check if we've been trying for too long, and give up if so. -- 检查我们是否尝试了太长时间,如果是,则放弃 retries = retries - 1 if retries <= 0 then print('RVInterior WARNING: serverLeaveInterior unable to find vehicle, giving up.') if arguments.isBoat and TickControl.main then -- Re-enable Aquatsar's onTick -- 重新启用 Aquatsar 的 onTick Events.OnTick.Add(TickControl.main) end Events.OnPlayerUpdate.Remove(playerLeftInteriorClosure) end -- Try to locate the correct vehicle as they're loaded into memory. -- 尝试在它们加载到内存中时找到正确的车辆 local allVehicles = player:getCell():getVehicles() if allVehicles:size() == 0 then return elseif vehicleIndex >= allVehicles:size() then vehicleIndex = 0 end local vehicle = allVehicles:get(vehicleIndex) vehicleIndex = vehicleIndex + 1 if not doesVehicleMatch(vehicle, arguments.vehicleName, arguments.interiorInstance) then return end local seat = playerSeats[player:getOnlineID()] or vehicle:getScript():getPassengerCount() - 1 while seat >= 0 and (not vehicle:isSeatInstalled(seat) or vehicle:isSeatOccupied(seat)) do if seat == playerSeats[player:getOnlineID()] then seat = vehicle:getScript():getPassengerCount() - 1 playerSeats[player:getOnlineID()] = nil else seat = seat - 1 end end if seat >= 0 then -- We need to get the passenger position manually, as the two-argument vehicle:enter function only works -- if the seat they're trying to enter has a door leading to the outside. -- 我们需要手动获取乘客位置,因为两个参数的 vehicle:enter 函数只在他们试图进入的座位有通往外部的门时才起作用 local position = vehicle:getPassengerPosition(seat, 'inside') if position and position:getOffset() then vehicle:enter(seat, player, position:getOffset()) triggerEvent('OnEnterVehicle', player) else print('RVInterior WARNING: exiting an interior of ' .. vehicle:getScript():getFullName() .. ', couldn't get the inside offset for seat ', seat) end else -- They'll end up outside (clipped into) the vehicle. -- 他们最终会出现在车外(被剪裁到)车辆中 print('RVInterior WARNING: player exiting an interior didn't find an installed unoccupied seat.') end if arguments.isBoat and TickControl.main then -- Re-enable Aquatsar's onTick -- 重新启用 Aquatsar 的 onTick Events.OnTick.Add(TickControl.main) end sendClientCommand(player, 'RVInterior', 'clientFinishExitInterior', { vehicleId = vehicle:getId() }) Events.OnPlayerUpdate.Remove(playerLeftInteriorClosure) end Events.OnPlayerUpdate.Add(playerLeftInteriorClosure) end


-- Update modData as vehicle with interiors move.

local function watchIfPlayerDriving(player) local vehicle = player:getVehicle() -- Test if they're driving a vehicle which has (the potential to have) an interior. -- 测试他们是否驾驶着(可能拥有)内部的车辆 if RVInterior.vehicleHasInteriorParameters(vehicle) and vehicle:isDriver(player) then local delay = 0 local travel local updateDrivingVehicleClosure updateDrivingVehicleClosure = function() -- Only check for updates once every 100 ticks. -- 每 100 个刻度只检查一次更新 if delay > 0 then delay = delay - 1 return end delay = 100 local speed = math.abs(vehicle:getCurrentSpeedKmHour()) -- If the vehicle starts moving, update its position immediately and every 70 meters. -- If the vehicle stops, update its position if it was previously moving. -- 如果车辆开始移动,立即更新其位置,每 70 米更新一次。 -- 如果车辆停止,如果之前正在移动,则更新其位置 if (speed >= stationaryThreshold and (not travel or travel:DistTo(player) > 70)) or (speed < stationaryThreshold and travel) then sendClientCommand('RVInterior', 'updateVehiclePosition', { vehicleId = vehicle:getId() }) if speed < stationaryThreshold then travel = nil else travel = vehicle:getSquare() end end -- If they're no longer driving and the vehicle is stationary, stop updating the vehicle position. -- 如果他们不再驾驶,并且车辆处于静止状态,则停止更新车辆位置 if not vehicle:isDriver(player) and not travel then Events.OnPlayerUpdate.Remove(updateDrivingVehicleClosure) end end Events.OnPlayerUpdate.Add(updateDrivingVehicleClosure) end end

Events.OnEnterVehicle.Add(watchIfPlayerDriving) Events.OnSwitchVehicleSeat.Add(watchIfPlayerDriving)


-- Other events

--- Test if the player has spawned inside a vehicle interior (e.g. respawned in a safehouse on MP), and put them at the --- normal vehicle interior entry point if so. -- 测试玩家是否在车辆内部生成(例如,在MP上的安全屋中重生),如果是,则将它们放在正常的车辆内部入口点 local function onPlayerRespawnInVehicleInterior(player) local interior = RVInterior.calculatePlayerInteriorInstance(player) if not interior then -- They didn't start inside a vehicle interior -- 他们没有从车辆内部开始 return end print('RVInterior info: OnNewGame spawned player inside a vehicle interior for vehicle ', interior.vehicleName, ' instance ', interior.interiorInstance) -- Jump them to the normal entry point, as if they'd just entered. -- 将他们跳到正常的入口点,就好像他们刚进入一样 sendPlayerToEntryCoordinates(player, interior.vehicleName, interior.interiorInstance) end

Events.OnNewGame.Add(onPlayerRespawnInVehicleInterior);


原文地址: https://www.cveoy.top/t/topic/nnZ1 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录