Init Commit
This commit is contained in:
BIN
42/icon.png
Normal file
BIN
42/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.8 KiB |
240
42/media/lua/client/RVReassingRoomAddon.lua
Normal file
240
42/media/lua/client/RVReassingRoomAddon.lua
Normal file
@@ -0,0 +1,240 @@
|
||||
-- RVReassignAddon.lua
|
||||
if isServer() then return end
|
||||
|
||||
local RVReassignAddon = {}
|
||||
local SERVER_MODULE = "PROJECTRVTools"
|
||||
local SERVER_COMMAND_REASSIGN = "ReassignVehicleToCurrentRoom"
|
||||
local CLIENT_COMMAND_RESULT = "ReassignVehicleResult"
|
||||
|
||||
-- Get the room type based on the player's coordinates
|
||||
local function getRoomTypeAtPosition(x, y, z)
|
||||
local RV = require("RVVehicleTypes")
|
||||
local VehicleTypes = RV.VehicleTypes
|
||||
|
||||
for roomType, typeDef in pairs(VehicleTypes) do
|
||||
for _, room in ipairs(typeDef.rooms) do
|
||||
local roomEndX = room.x + (typeDef.roomWidth or 2)
|
||||
local roomEndY = room.y + (typeDef.roomHeight or 3)
|
||||
|
||||
if x >= room.x and x < roomEndX and
|
||||
y >= room.y and y < roomEndY and
|
||||
z == (room.z or 0) then
|
||||
return roomType, room, typeDef
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil, nil, nil
|
||||
end
|
||||
|
||||
-- Reassign the vehicle to the current room
|
||||
local function reassignVehicleToCurrentRoom(player)
|
||||
local modData = ModData.getOrCreate("modPROJECTRVInterior")
|
||||
local pmd = player:getModData()
|
||||
|
||||
-- Verify the player has a recently tracked vehicle
|
||||
if not pmd.projectRV_playerId then
|
||||
player:Say("No recent vehicle is registered.")
|
||||
return
|
||||
end
|
||||
|
||||
local playerData = modData.Players and modData.Players[pmd.projectRV_playerId]
|
||||
if not playerData or not playerData.VehicleId then
|
||||
player:Say("No recent vehicle was found.")
|
||||
return
|
||||
end
|
||||
|
||||
local vehicleId = playerData.VehicleId
|
||||
local originalVehicleType = playerData.RoomType or "normal"
|
||||
|
||||
if not vehicleId then
|
||||
player:Say("Vehicle data is incomplete.")
|
||||
return
|
||||
end
|
||||
|
||||
-- Get the current room where the player is standing
|
||||
local x, y, z = player:getX(), player:getY(), player:getZ()
|
||||
local newRoomType, newRoom, newTypeDef = getRoomTypeAtPosition(x, y, z)
|
||||
|
||||
if not newRoom then
|
||||
player:Say("You are not in a valid room.")
|
||||
return
|
||||
end
|
||||
|
||||
-- Remove the previous assignment from the original room type table
|
||||
local originalAssignedKey = (originalVehicleType == "normal") and "AssignedRooms" or ("AssignedRooms" .. originalVehicleType)
|
||||
if modData[originalAssignedKey] then
|
||||
modData[originalAssignedKey][vehicleId] = nil
|
||||
end
|
||||
|
||||
-- Assign the new room in the new type table
|
||||
local newAssignedKey = (newRoomType == "normal") and "AssignedRooms" or ("AssignedRooms" .. newRoomType)
|
||||
modData[newAssignedKey] = modData[newAssignedKey] or {}
|
||||
modData[newAssignedKey][vehicleId] = newRoom
|
||||
|
||||
-- Update player-linked vehicle type data
|
||||
playerData.RoomType = newRoomType
|
||||
playerData.ActualRoom = newRoom
|
||||
|
||||
-- Update vehicle modData type if the vehicle is currently loaded
|
||||
local vehicles = getCell():getVehicles()
|
||||
for i = 0, vehicles:size() - 1 do
|
||||
local vehicle = vehicles:get(i)
|
||||
local vmd = vehicle:getModData()
|
||||
if vmd.projectRV_uniqueId and tostring(vmd.projectRV_uniqueId) == vehicleId then
|
||||
vmd.projectRV_type = newRoomType
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- Also persist the override for cases where the vehicle is not loaded
|
||||
modData.VehicleTypeOverrides = modData.VehicleTypeOverrides or {}
|
||||
modData.VehicleTypeOverrides[vehicleId] = newRoomType
|
||||
|
||||
player:Say("Vehicle reassigned to room type: " .. newRoomType)
|
||||
|
||||
-- Debug
|
||||
print(string.format("[RVReassign] Vehicle %s (original: %s) reassigned to room type %s: x=%d, y=%d, z=%d",
|
||||
vehicleId, originalVehicleType, newRoomType, newRoom.x, newRoom.y, newRoom.z))
|
||||
end
|
||||
|
||||
local function requestReassignVehicleToCurrentRoom(player)
|
||||
if isClient() then
|
||||
sendClientCommand(SERVER_MODULE, SERVER_COMMAND_REASSIGN, {})
|
||||
return
|
||||
end
|
||||
|
||||
reassignVehicleToCurrentRoom(player)
|
||||
end
|
||||
|
||||
-- Check whether the player is inside a valid room
|
||||
local function isPlayerInValidRoom(player)
|
||||
local x, y, z = player:getX(), player:getY(), player:getZ()
|
||||
local roomType, room, typeDef = getRoomTypeAtPosition(x, y, z)
|
||||
return roomType ~= nil, roomType, room
|
||||
end
|
||||
|
||||
-- Get info for the player's most recent tracked vehicle
|
||||
local function getLastVehicleInfo(player)
|
||||
local modData = ModData.getOrCreate("modPROJECTRVInterior")
|
||||
local pmd = player:getModData()
|
||||
|
||||
if not pmd.projectRV_playerId then
|
||||
return nil, nil, nil
|
||||
end
|
||||
|
||||
local playerData = modData.Players and modData.Players[pmd.projectRV_playerId]
|
||||
if not playerData then
|
||||
return nil, nil, nil
|
||||
end
|
||||
|
||||
return playerData.VehicleId, playerData.RoomType, playerData.ActualRoom
|
||||
end
|
||||
|
||||
local function canUseReassignInMP(player)
|
||||
if not isClient() then
|
||||
return false
|
||||
end
|
||||
|
||||
local pmd = player:getModData()
|
||||
return pmd and pmd.projectRV_playerId ~= nil
|
||||
end
|
||||
|
||||
-- Main function that adds the context menu option
|
||||
local function addReassignOption(player, context)
|
||||
local inRoom, roomType, room = isPlayerInValidRoom(player)
|
||||
|
||||
if not inRoom then
|
||||
return
|
||||
end
|
||||
|
||||
local vehicleId, currentRoomType, currentRoom = getLastVehicleInfo(player)
|
||||
|
||||
if not vehicleId and not canUseReassignInMP(player) then
|
||||
return
|
||||
end
|
||||
|
||||
-- Add the option to the context menu
|
||||
local optionText
|
||||
if currentRoomType and currentRoomType ~= roomType then
|
||||
optionText = getText("ContextMenu_ReassignVehicleToRoomDifferent") or
|
||||
string.format("Reassign vehicle (%s) to this room (%s)", currentRoomType, roomType)
|
||||
else
|
||||
optionText = getText("ContextMenu_ReassignVehicleToRoom") or "Reassign vehicle to this room"
|
||||
end
|
||||
|
||||
context:addOption(optionText, player, requestReassignVehicleToCurrentRoom)
|
||||
end
|
||||
|
||||
-- Hook for world-object context menu
|
||||
local function onFillWorldObjectContextMenu(player, context, worldObjects)
|
||||
local playerObj = getSpecificPlayer(player)
|
||||
if playerObj then
|
||||
addReassignOption(playerObj, context)
|
||||
end
|
||||
end
|
||||
|
||||
-- Hook for inventory context menu
|
||||
local function onFillInventoryObjectContextMenu(player, context, items)
|
||||
local playerObj = getSpecificPlayer(player)
|
||||
if playerObj then
|
||||
addReassignOption(playerObj, context)
|
||||
end
|
||||
end
|
||||
|
||||
-- Apply saved type overrides when a vehicle is created/loaded
|
||||
local function onVehicleCreate(vehicle)
|
||||
local modData = ModData.getOrCreate("modPROJECTRVInterior")
|
||||
local vmd = vehicle:getModData()
|
||||
|
||||
if vmd.projectRV_uniqueId and modData.VehicleTypeOverrides then
|
||||
local vehicleId = tostring(vmd.projectRV_uniqueId)
|
||||
if modData.VehicleTypeOverrides[vehicleId] then
|
||||
vmd.projectRV_type = modData.VehicleTypeOverrides[vehicleId]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function onServerCommand(module, command, args)
|
||||
if module ~= SERVER_MODULE or command ~= CLIENT_COMMAND_RESULT then
|
||||
return
|
||||
end
|
||||
|
||||
local player = getPlayer()
|
||||
if not player then
|
||||
return
|
||||
end
|
||||
|
||||
if args and args.message then
|
||||
player:Say(tostring(args.message))
|
||||
end
|
||||
end
|
||||
|
||||
-- Initialize the addon
|
||||
local function initReassignAddon()
|
||||
Events.OnFillWorldObjectContextMenu.Add(onFillWorldObjectContextMenu)
|
||||
Events.OnFillInventoryObjectContextMenu.Add(onFillInventoryObjectContextMenu)
|
||||
Events.OnVehicleCreate.Add(onVehicleCreate)
|
||||
Events.OnServerCommand.Add(onServerCommand)
|
||||
|
||||
print("[RVReassignAddon] Vehicle reassignment addon loaded successfully")
|
||||
end
|
||||
|
||||
-- Load fallback translations if needed
|
||||
local function loadTranslations()
|
||||
if getText and getText("ContextMenu_ReassignVehicleToRoom") == "ContextMenu_ReassignVehicleToRoom" then
|
||||
-- If translation key is missing, define English fallback values
|
||||
-- Add additional localized values here if needed
|
||||
local translations = {
|
||||
ContextMenu_ReassignVehicleToRoom = "Reassign vehicle to this room",
|
||||
ContextMenu_ReassignVehicleToRoomDifferent = "Reassign vehicle to this room (type change)"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
-- Initialize when the game starts
|
||||
Events.OnGameStart.Add(function()
|
||||
loadTranslations()
|
||||
initReassignAddon()
|
||||
end)
|
||||
|
||||
return RVReassignAddon
|
||||
137
42/media/lua/client/remove_street_tiles_progressive.lua
Normal file
137
42/media/lua/client/remove_street_tiles_progressive.lua
Normal file
@@ -0,0 +1,137 @@
|
||||
-- remove_street_tiles_progressive.lua
|
||||
-- Place in media/lua/client/
|
||||
-- Removes tiles in radius-6 "blocks" per tick until total radius ~60 is covered.
|
||||
|
||||
local BLOCK_RADIUS = 6 -- radius processed each tick
|
||||
local TOTAL_RADIUS = 60 -- final total radius
|
||||
local BLOCK_STEP = (BLOCK_RADIUS * 2) + 1 -- 13 when BLOCK_RADIUS=6
|
||||
|
||||
local function makeBlocksList(totalRadius, step)
|
||||
local blocks = {}
|
||||
local start = -totalRadius
|
||||
local finish = totalRadius
|
||||
for oy = start, finish, step do
|
||||
for ox = start, finish, step do
|
||||
table.insert(blocks, {ox = ox, oy = oy})
|
||||
end
|
||||
end
|
||||
return blocks
|
||||
end
|
||||
|
||||
local function tryRemoveObjFromSquare(sq, obj)
|
||||
if not sq or not obj then return false end
|
||||
local deleted = false
|
||||
if sq.RemoveTileObject then
|
||||
pcall(function() sq:RemoveTileObject(obj) end)
|
||||
deleted = true
|
||||
end
|
||||
if not deleted and sq.transmitRemoveItemFromSquare then
|
||||
pcall(function() sq:transmitRemoveItemFromSquare(obj) end)
|
||||
deleted = true
|
||||
end
|
||||
if not deleted and obj.removeFromWorld then
|
||||
pcall(function() obj:removeFromWorld() end)
|
||||
deleted = true
|
||||
end
|
||||
return deleted
|
||||
end
|
||||
|
||||
local function processBlockAt(centerX, centerY, centerZ, blockOffsetX, blockOffsetY, player, stats)
|
||||
-- blockOffset are offsets relative to center (e.g. -60, -47, -34, ...)
|
||||
local baseX = centerX + blockOffsetX
|
||||
local baseY = centerY + blockOffsetY
|
||||
for dx = -BLOCK_RADIUS, BLOCK_RADIUS do
|
||||
for dy = -BLOCK_RADIUS, BLOCK_RADIUS do
|
||||
local x = baseX + dx
|
||||
local y = baseY + dy
|
||||
local sq = pcall(function() return getCell():getGridSquare(x, y, centerZ) end) and getCell():getGridSquare(x, y, centerZ) or nil
|
||||
if sq then
|
||||
local objs = nil
|
||||
local ok = pcall(function() objs = sq:getObjects() end)
|
||||
if ok and objs and objs.size and objs:size() > 0 then
|
||||
local n = objs:size()
|
||||
for i = n - 1, 0, -1 do
|
||||
local obj = nil
|
||||
local ok2, o = pcall(function() return objs:get(i) end)
|
||||
obj = ok2 and o or nil
|
||||
if obj then
|
||||
local ok3, spriteName = pcall(function()
|
||||
local spr = obj.getSprite and obj:getSprite()
|
||||
return (spr and spr.getName) and spr:getName() or nil
|
||||
end)
|
||||
spriteName = ok3 and spriteName or nil
|
||||
if spriteName and string.find(string.lower(tostring(spriteName)), "street", 1, false) then
|
||||
if tryRemoveObjFromSquare(sq, obj) then
|
||||
stats.removed = stats.removed + 1
|
||||
if stats.examples < stats.maxExamples then
|
||||
print(string.format("REMOVE_PROGRESS: removed @ %d,%d -> '%s'", x, y, tostring(spriteName)))
|
||||
stats.examples = stats.examples + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function startProgressiveRemoval(player)
|
||||
if not player then return end
|
||||
local px = math.floor(player:getX())
|
||||
local py = math.floor(player:getY())
|
||||
local pz = math.floor(player:getZ() or 0)
|
||||
|
||||
local blocks = makeBlocksList(TOTAL_RADIUS, BLOCK_STEP)
|
||||
local totalBlocks = #blocks
|
||||
if totalBlocks == 0 then
|
||||
if player.Say then player:Say("Nothing to process.") end
|
||||
return
|
||||
end
|
||||
|
||||
local stats = { removed = 0, examples = 0, maxExamples = 10 }
|
||||
local index = 1
|
||||
|
||||
if player.Say then player:Say("Starting progressive removal (radius "..tostring(TOTAL_RADIUS)..").") end
|
||||
print("REMOVE_PROGRESS: start at "..px..","..py.." totalBlocks="..tostring(totalBlocks))
|
||||
|
||||
local function onTick()
|
||||
-- Process one block per tick
|
||||
if index > totalBlocks then
|
||||
Events.OnTick.Remove(onTick)
|
||||
local msg = "Removal complete: removed "..tostring(stats.removed).." tiles (filter 'street')."
|
||||
print("REMOVE_PROGRESS: "..msg)
|
||||
if player.Say then player:Say(msg) end
|
||||
return
|
||||
end
|
||||
|
||||
local b = blocks[index]
|
||||
processBlockAt(px, py, pz, b.ox, b.oy, player, stats)
|
||||
|
||||
-- Periodic message every 10 blocks
|
||||
if index % 10 == 0 then
|
||||
local progressMsg = string.format("Progress: block %d/%d - removed so far: %d", index, totalBlocks, stats.removed)
|
||||
print("REMOVE_PROGRESS: "..progressMsg)
|
||||
if player.Say then player:Say(progressMsg) end
|
||||
end
|
||||
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
Events.OnTick.Add(onTick)
|
||||
end
|
||||
|
||||
-- Hook into the world context menu
|
||||
local function onFillWorldObjectContextMenu(playerOrNum, context, worldObjects, test)
|
||||
if test then return end
|
||||
local player
|
||||
if type(playerOrNum) == "number" then player = getSpecificPlayer(playerOrNum) else player = playerOrNum end
|
||||
if not player then return end
|
||||
|
||||
context:addOption("Progressively remove 'street' tiles (radius "..tostring(TOTAL_RADIUS)..")", worldObjects, function()
|
||||
startProgressiveRemoval(player)
|
||||
end)
|
||||
end
|
||||
|
||||
Events.OnFillWorldObjectContextMenu.Add(onFillWorldObjectContextMenu)
|
||||
125
42/media/lua/server/RVReassignRoomServer.lua
Normal file
125
42/media/lua/server/RVReassignRoomServer.lua
Normal file
@@ -0,0 +1,125 @@
|
||||
if not isServer() then return end
|
||||
|
||||
local SERVER_MODULE = "PROJECTRVTools"
|
||||
local SERVER_COMMAND_REASSIGN = "ReassignVehicleToCurrentRoom"
|
||||
local CLIENT_COMMAND_RESULT = "ReassignVehicleResult"
|
||||
|
||||
local function getRoomTypeAtPosition(x, y, z)
|
||||
local RV = require("RVVehicleTypes")
|
||||
local vehicleTypes = RV.VehicleTypes
|
||||
|
||||
for roomType, typeDef in pairs(vehicleTypes) do
|
||||
for _, room in ipairs(typeDef.rooms) do
|
||||
local roomEndX = room.x + (typeDef.roomWidth or 2)
|
||||
local roomEndY = room.y + (typeDef.roomHeight or 3)
|
||||
|
||||
if x >= room.x and x < roomEndX and
|
||||
y >= room.y and y < roomEndY and
|
||||
z == (room.z or 0) then
|
||||
return roomType, room
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
local function findVehicleByPersistentId(vehicleId)
|
||||
local cell = getCell()
|
||||
if not cell or not cell.getVehicles then
|
||||
return nil
|
||||
end
|
||||
|
||||
local vehicles = cell:getVehicles()
|
||||
if not vehicles then
|
||||
return nil
|
||||
end
|
||||
|
||||
for i = 0, vehicles:size() - 1 do
|
||||
local vehicle = vehicles:get(i)
|
||||
if vehicle then
|
||||
local vmd = vehicle:getModData()
|
||||
if vmd and vmd.projectRV_uniqueId and tostring(vmd.projectRV_uniqueId) == vehicleId then
|
||||
return vehicle
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local function reassignVehicleToCurrentRoom(player)
|
||||
local modData = ModData.getOrCreate("modPROJECTRVInterior")
|
||||
local pmd = player and player:getModData() or nil
|
||||
|
||||
if not pmd or not pmd.projectRV_playerId then
|
||||
return false, "No recent vehicle registered."
|
||||
end
|
||||
|
||||
local playerData = modData.Players and modData.Players[pmd.projectRV_playerId]
|
||||
if not playerData or not playerData.VehicleId then
|
||||
return false, "No recent vehicle found."
|
||||
end
|
||||
|
||||
local vehicleId = tostring(playerData.VehicleId)
|
||||
local originalVehicleType = playerData.RoomType or "normal"
|
||||
local x, y, z = player:getX(), player:getY(), player:getZ()
|
||||
local newRoomType, newRoom = getRoomTypeAtPosition(x, y, z)
|
||||
|
||||
if not newRoom then
|
||||
return false, "You are not in a valid RV room."
|
||||
end
|
||||
|
||||
local originalAssignedKey = (originalVehicleType == "normal") and "AssignedRooms" or ("AssignedRooms" .. originalVehicleType)
|
||||
if modData[originalAssignedKey] then
|
||||
modData[originalAssignedKey][vehicleId] = nil
|
||||
end
|
||||
|
||||
local newAssignedKey = (newRoomType == "normal") and "AssignedRooms" or ("AssignedRooms" .. newRoomType)
|
||||
modData[newAssignedKey] = modData[newAssignedKey] or {}
|
||||
modData[newAssignedKey][vehicleId] = newRoom
|
||||
|
||||
playerData.RoomType = newRoomType
|
||||
playerData.ActualRoom = newRoom
|
||||
|
||||
modData.VehicleTypeOverrides = modData.VehicleTypeOverrides or {}
|
||||
modData.VehicleTypeOverrides[vehicleId] = newRoomType
|
||||
|
||||
local vehicle = findVehicleByPersistentId(vehicleId)
|
||||
if vehicle then
|
||||
local vmd = vehicle:getModData()
|
||||
vmd.projectRV_type = newRoomType
|
||||
if vehicle.transmitModData then
|
||||
vehicle:transmitModData()
|
||||
end
|
||||
end
|
||||
|
||||
if ModData and ModData.transmit then
|
||||
ModData.transmit("modPROJECTRVInterior")
|
||||
end
|
||||
|
||||
print(string.format(
|
||||
"[RVReassignServer] Vehicle %s moved from type %s to %s at x=%d y=%d z=%d",
|
||||
vehicleId, tostring(originalVehicleType), tostring(newRoomType), newRoom.x, newRoom.y, newRoom.z
|
||||
))
|
||||
|
||||
return true, "Vehicle reassigned to room type: " .. tostring(newRoomType)
|
||||
end
|
||||
|
||||
local function onClientCommand(module, command, player, args)
|
||||
if module ~= SERVER_MODULE then
|
||||
return
|
||||
end
|
||||
|
||||
if command == SERVER_COMMAND_REASSIGN then
|
||||
local ok, message = reassignVehicleToCurrentRoom(player)
|
||||
if player then
|
||||
sendServerCommand(player, SERVER_MODULE, CLIENT_COMMAND_RESULT, {
|
||||
ok = ok,
|
||||
message = message
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Events.OnClientCommand.Add(onClientCommand)
|
||||
7
42/mod.info
Normal file
7
42/mod.info
Normal file
@@ -0,0 +1,7 @@
|
||||
version=2.0
|
||||
name=[B42]Project RV Tools
|
||||
id=PROJECTRVTools42
|
||||
description=RV Tools
|
||||
icon=icon.png
|
||||
poster=poster.png
|
||||
require=\PROJECTRVInterior42
|
||||
BIN
42/poster.png
Normal file
BIN
42/poster.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
0
common/placeholder.txt
Normal file
0
common/placeholder.txt
Normal file
Reference in New Issue
Block a user