From 660d8e28779e78ee2a2f8d57dc715c2abbf552c1 Mon Sep 17 00:00:00 2001 From: SAMURAI <66764345+xesdoog@users.noreply.github.com> Date: Tue, 23 Jun 2026 00:15:46 +0100 Subject: [PATCH 1/4] fix: some fixes - Fix a few bugs. - Add an option to automatically clear CEO office clutter. --- SSV2/includes/classes/gta/CPlayerInfo.lua | 2 +- SSV2/includes/classes/gta/phFragInst.lua | 11 +- SSV2/includes/classes/gta/rlGamerInfo.lua | 47 ++--- SSV2/includes/data/config.lua | 20 +++ .../online/yim_resupplier/YimResupplierV3.lua | 8 +- .../businesses/BasicBusiness.lua | 6 +- .../yim_resupplier/businesses/BusinessHub.lua | 15 +- .../yim_resupplier/businesses/CarWash.lua | 20 ++- .../yim_resupplier/businesses/Factory.lua | 16 +- .../yim_resupplier/businesses/Office.lua | 169 ++++++++++++++++-- .../yim_resupplier/businesses/Warehouse.lua | 3 +- .../frontend/yim_resupplier/dashboard_ui.lua | 36 ++-- .../frontend/yim_resupplier/office_ui.lua | 144 ++++++++++++--- SSV2/includes/lib/class.lua | 53 +++--- SSV2/includes/lib/extensions/__init__.lua | 13 +- SSV2/includes/lib/translations/__hashmap.json | 21 ++- SSV2/includes/lib/translations/de-DE.lua | 21 ++- SSV2/includes/lib/translations/en-US.lua | 19 ++ SSV2/includes/lib/translations/es-ES.lua | 21 ++- SSV2/includes/lib/translations/fr-FR.lua | 21 ++- SSV2/includes/lib/translations/it-IT.lua | 21 ++- SSV2/includes/lib/translations/ja-JP.lua | 21 ++- SSV2/includes/lib/translations/ko-KR.lua | 21 ++- SSV2/includes/lib/translations/pl-PL.lua | 21 ++- SSV2/includes/lib/translations/pt-BR.lua | 21 ++- SSV2/includes/lib/translations/ru-RU.lua | 21 ++- SSV2/includes/lib/translations/zh-CN.lua | 21 ++- SSV2/includes/lib/translations/zh-TW.lua | 21 ++- SSV2/includes/modules/Accessor.lua | 3 +- SSV2/includes/modules/Color.lua | 73 ++++---- SSV2/includes/modules/LocalPlayer.lua | 3 + SSV2/includes/modules/Player.lua | 6 +- SSV2/includes/services/GUI.lua | 7 +- .../services/PlayerMoneyController.lua | 29 ++- SSV2/includes/services/Serializer.lua | 122 ++++++------- 35 files changed, 804 insertions(+), 273 deletions(-) diff --git a/SSV2/includes/classes/gta/CPlayerInfo.lua b/SSV2/includes/classes/gta/CPlayerInfo.lua index 8caaaeee..7bfccda4 100644 --- a/SSV2/includes/classes/gta/CPlayerInfo.lua +++ b/SSV2/includes/classes/gta/CPlayerInfo.lua @@ -59,7 +59,7 @@ end ---@return int64_t function CPlayerInfo:GetRockstarID() return self:__safecall(0, function() - return self.m_rl_gamer_info.m_rockstar_id:get_int() + return self.m_rl_gamer_info:GetRockstarID() end) end diff --git a/SSV2/includes/classes/gta/phFragInst.lua b/SSV2/includes/classes/gta/phFragInst.lua index d58fb0e0..18a149ab 100644 --- a/SSV2/includes/classes/gta/phFragInst.lua +++ b/SSV2/includes/classes/gta/phFragInst.lua @@ -10,6 +10,7 @@ -------------------------------------- -- Class: phFragInst -------------------------------------- +-- TODO: research this nonsensical thing or remove it. ---@class phFragInst ---@field protected m_ptr pointer ---@field public m_cache_entry pointer @@ -18,15 +19,7 @@ ---@field public m_obj_matrices pointer `rage::fMatrix44` ---@field public m_global_matrices pointer `rage::fMatrix44` ---@overload fun(addr: pointer): phFragInst -local phFragInst = {} -phFragInst.__index = phFragInst -phFragInst.__type = "phFragInst" ----@diagnostic disable-next-line: param-type-mismatch -setmetatable(phFragInst, { - __call = function(cls, ...) - return cls.new(...) - end, -}) +local phFragInst = Callable("phFragInst") ---@param ptr pointer ---@return phFragInst|nil diff --git a/SSV2/includes/classes/gta/rlGamerInfo.lua b/SSV2/includes/classes/gta/rlGamerInfo.lua index 0b6ec263..17b2b894 100644 --- a/SSV2/includes/classes/gta/rlGamerInfo.lua +++ b/SSV2/includes/classes/gta/rlGamerInfo.lua @@ -16,18 +16,11 @@ local CStructView = require("includes.classes.gta.CStructView") ---@field private m_decimal uint32_t ---@field private m_packed vec4 ---@overload fun(n: uint32_t): IPAddress -local IPAddress = {} -IPAddress.__index = IPAddress ----@diagnostic disable-next-line -setmetatable(IPAddress, { - __call = function(t, ...) - return t.new(...) - end -}) +local IPAddress = Callable("IPAddress", { ctor = function(t, n) return t:new(n) end }) ---@param n uint32_t ---@return IPAddress -function IPAddress.new(n) +function IPAddress:new(n) local packed = vec4:zero() if (n ~= 0) then packed = vec4:new( @@ -41,8 +34,7 @@ function IPAddress.new(n) return setmetatable({ m_decimal = n, m_packed = packed - ---@diagnostic disable-next-line - }, IPAddress) + }, self) end ---@return string @@ -60,14 +52,16 @@ end -- Class: rlGamerInfo -------------------------------------- ---@class rlGamerInfo : CStructBase ----@field m_peer_id pointer ----@field m_rockstar_id pointer ----@field m_external_ip pointer ----@field m_external_port pointer ----@field m_internal_ip pointer ----@field m_internal_port pointer ----@field m_nat_type pointer ----@field m_player_name pointer // 0x00DC +---@field private m_peer_id pointer +---@field private m_rockstar_id pointer +---@field private m_external_ip pointer +---@field private m_external_port pointer +---@field private m_internal_ip pointer +---@field private m_internal_port pointer +---@field private m_nat_type pointer +---@field private m_player_name pointer // 0x00DC +---@field private m_cached_extern_ipaddr IPAddress +---@field private m_cached_intern_ipaddr IPAddress ---@overload fun(ptr: pointer): rlGamerInfo local rlGamerInfo = CStructView("rlGamerInfo", 0x0F90) @@ -88,14 +82,25 @@ function rlGamerInfo.new(ptr) }, rlGamerInfo) end +---@return int64_t +function rlGamerInfo:GetRockstarID() + return self.m_rockstar_id:get_int() +end + ---@return IPAddress function rlGamerInfo:GetExternalIP() - return IPAddress(self.m_external_ip:get_dword()) + if (not self.m_cached_extern_ipaddr) then + self.m_cached_extern_ipaddr = IPAddress(self.m_external_ip:get_dword()) + end + return self.m_cached_extern_ipaddr end ---@return IPAddress function rlGamerInfo:GetInternalIP() - return IPAddress(self.m_internal_ip:get_dword()) + if (not self.m_cached_intern_ipaddr) then + self.m_cached_intern_ipaddr = IPAddress(self.m_internal_ip:get_dword()) + end + return self.m_cached_intern_ipaddr end ---@return string diff --git a/SSV2/includes/data/config.lua b/SSV2/includes/data/config.lua index 82432349..520cf8c3 100644 --- a/SSV2/includes/data/config.lua +++ b/SSV2/includes/data/config.lua @@ -340,6 +340,26 @@ local Config = { sy_disable_rob_weekly_cd = false, sy_disable_tow_cd = false, unsafe_feats_enabled = false, + office_clutter = { + auto_disable = false, + items = { + cash = false, + Swag_Silver = false, + Swag_Pills = false, + Swag_Med = false, + Swag_JewelWatch = false, + Swag_Ivory = false, + Swag_Guns = false, + Swag_Gems = false, + Swag_Furcoats = false, + Swag_electronic = false, + Swag_DrugStatue = false, + Swag_DrugBags = false, + Swag_Counterfeit = false, + Swag_Booze_cigs = false, + Swag_Art = false, + } + } }, yim_actions = { auto_close_ped_window = false, diff --git a/SSV2/includes/features/online/yim_resupplier/YimResupplierV3.lua b/SSV2/includes/features/online/yim_resupplier/YimResupplierV3.lua index 4776bcbb..9bf0b1ad 100644 --- a/SSV2/includes/features/online/yim_resupplier/YimResupplierV3.lua +++ b/SSV2/includes/features/online/yim_resupplier/YimResupplierV3.lua @@ -144,11 +144,15 @@ function YRV3:Reset(disable, reason) end function YRV3:Reload() + if (self.m_state == Enums.eYRState.RELOADING) then + return + end + + self.m_state = Enums.eYRState.RELOADING ThreadManager:Run(function() - self.m_state = Enums.eYRState.RELOADING self.m_total_sum = 0 self.m_total_sum_fmt = "$0" - sleep(1500) -- dummy busy wait to give the UI time to refresh + sleep(1500) -- dummy busy wait self:Reset() end) end diff --git a/SSV2/includes/features/online/yim_resupplier/businesses/BasicBusiness.lua b/SSV2/includes/features/online/yim_resupplier/businesses/BasicBusiness.lua index 5811caf5..2b0b0f3e 100644 --- a/SSV2/includes/features/online/yim_resupplier/businesses/BasicBusiness.lua +++ b/SSV2/includes/features/online/yim_resupplier/businesses/BasicBusiness.lua @@ -17,7 +17,7 @@ ---@field private m_name string ---@field private m_coords vec3 ---@field private m_generic_val_get_func fun(): anyval ----@field private m_generic_val_set_func function +---@field private m_generic_val_set_func fun(...: any) ---@field protected m_is_stale boolean local BasicBusiness = {} BasicBusiness.__index = BasicBusiness @@ -52,8 +52,8 @@ function BasicBusiness:GetGenericValue() return self.m_generic_val_get_func() end -function BasicBusiness:SetGenericValue() - self.m_generic_val_set_func() +function BasicBusiness:SetGenericValue(...) + self.m_generic_val_set_func(...) end return BasicBusiness diff --git a/SSV2/includes/features/online/yim_resupplier/businesses/BusinessHub.lua b/SSV2/includes/features/online/yim_resupplier/businesses/BusinessHub.lua index 7e13cf76..5154ca4d 100644 --- a/SSV2/includes/features/online/yim_resupplier/businesses/BusinessHub.lua +++ b/SSV2/includes/features/online/yim_resupplier/businesses/BusinessHub.lua @@ -36,9 +36,9 @@ BusinessHub.__index = BusinessHub ---@param opts HubOpts ---@return BusinessHub function BusinessHub.new(opts) - assert(type(opts.max_units) == "number", "Missing argument: max_units") local id = opts.id - assert(type(opts.id) == "number" and math.is_inrange(opts.id, 0, 6), "Invalid Business Hub id.") + assert(type(opts.max_units) == "number", "Missing argument: max_units") + assert(type(id) == "number" and math.is_inrange(id, 0, 6), "Invalid Business Hub id.") local base = BusinessBase.new(opts) local instance = setmetatable(base, BusinessHub) ---@cast instance BusinessHub @@ -100,7 +100,7 @@ end -- Actually wanted to improve UX with tech names using the return index -- -- but apparently only Yohan has a name. We should probably return a bool instead ----@return integer TechIndex A number between 0 and 5 or -1 if no one is assigned. +---@return integer techIndex A number between 0 and 5 or -1 if no one is assigned. function BusinessHub:GetAssignedTechIndex() for i = 0, 5 do if (self:IsTechAssignedToThis(i)) then @@ -192,7 +192,8 @@ end -- https://www.youtube.com/watch?v=-Gh1lTcwdGY ---@private function BusinessHub:InstantFillProduction() - self:TriggerProduction(self:GetMaxUnits() - 1) + self:SetProductCount(self:GetMaxUnits() - 1) + self:TriggerProduction() end ---@return boolean @@ -207,6 +208,11 @@ end ---@private function BusinessHub:LoopProduction() + if (self.m_fast_prod_running) then + return + end + + self.m_fast_prod_running = true ThreadManager:Run(function() while (self:IsValid() and self.fast_prod_enabled and not self:HasFullProduction()) do self:TriggerProduction() @@ -230,7 +236,6 @@ function BusinessHub:Update() return end - self.m_fast_prod_running = true self:LoopProduction() end end diff --git a/SSV2/includes/features/online/yim_resupplier/businesses/CarWash.lua b/SSV2/includes/features/online/yim_resupplier/businesses/CarWash.lua index bcf66cd6..b1447f68 100644 --- a/SSV2/includes/features/online/yim_resupplier/businesses/CarWash.lua +++ b/SSV2/includes/features/online/yim_resupplier/businesses/CarWash.lua @@ -148,9 +148,9 @@ CarWash.__index = CarWash ---@param opts BasicBusinessOpts ---@return CarWash function CarWash.new(opts) - local base = BasicBusiness.new(opts) - local instance = setmetatable(base, CarWash) ---@cast instance CarWash - local cashSafe = CashSafe.new({ + local base = BasicBusiness.new(opts) + local instance = setmetatable(base, CarWash) ---@cast instance CarWash + local cashSafe = CashSafe.new({ name = opts.name, cash_value_stat = "MPX_CWASH_SAFE_CASH_VALUE", paytime_stat = "MPX_CWASH_PAY_TIME_LEFT", @@ -159,6 +159,7 @@ function CarWash.new(opts) get_max_cash = function() return tunables.get_int("TYCOON_CAR_WASH_SAFE_MAX_STORAGE_AMOUNT") end, }) + local sgslObj = SGSL:Get(SGSL.data.car_wash_safe_global) local pidSize = sgslObj:GetOffset(1) local entryOffset = sgslObj:GetOffset(2) @@ -169,16 +170,18 @@ function CarWash.new(opts) :At(27) :At(2) - instance.m_safe = cashSafe - instance.m_duffle = CarWashDuffle.new({ + + instance.m_safe = cashSafe + instance.m_duffle = CarWashDuffle.new({ name = opts.name, cash_value_stat = "MPX_CAR_WASH_DUFFEL_VALUE", get_max_cash = function() return tunables.get_int(564305888) end }) - instance.m_subs = {} + + local subs = {} if (stats.get_int("MPX_SB_WEED_SHOP_OWNED") ~= 0) then - table.insert(instance.m_subs, CarWashSubBusiness.new({ + table.insert(subs, CarWashSubBusiness.new({ name = Game.GetLabelText("CELL_WSHOP"), coords = vec3:new(-1162.051147, -1564.757202, 4.410227), heat_packed_stat = 24925, @@ -188,7 +191,7 @@ function CarWash.new(opts) end if (stats.get_int("MPX_SB_HELI_TOURS_OWNED") ~= 0) then - table.insert(instance.m_subs, CarWashSubBusiness.new({ + table.insert(subs, CarWashSubBusiness.new({ name = Game.GetLabelText("CELL_HELIT"), coords = vec3:new(-753.524841, -1511.244751, 5.015130), heat_packed_stat = 24926, @@ -197,6 +200,7 @@ function CarWash.new(opts) })) end + instance.m_subs = subs return instance end diff --git a/SSV2/includes/features/online/yim_resupplier/businesses/Factory.lua b/SSV2/includes/features/online/yim_resupplier/businesses/Factory.lua index d99e1c21..940f1b1c 100644 --- a/SSV2/includes/features/online/yim_resupplier/businesses/Factory.lua +++ b/SSV2/includes/features/online/yim_resupplier/businesses/Factory.lua @@ -45,9 +45,9 @@ Factory.__index = Factory ---@param opts FactoryOpts ---@return Factory function Factory.new(opts) - assert(type(opts.max_units) == "number", "Missing argument: max_units") local id = opts.id - assert(type(opts.id) == "number" and math.is_inrange(opts.id, 0, 6), "Invalid Biker Business id.") + assert(type(opts.max_units) == "number", "Missing argument: max_units") + assert(type(id) == "number" and math.is_inrange(id, 0, 6), "Invalid Biker Business id.") local base = BusinessBase.new(opts) local instance = setmetatable(base, Factory) ---@cast instance Factory @@ -75,7 +75,7 @@ function Factory.new(opts) end function Factory:Reset() - self.fast_prod_enabled = false + self.fast_prod_enabled = false self.m_fast_prod_running = false self:ResetImpl() end @@ -176,6 +176,11 @@ end ---@private function Factory:LoopProduction() + if (self.m_fast_prod_running) then + return + end + + self.m_fast_prod_running = true ThreadManager:Run(function() while (self:IsValid() and self.fast_prod_enabled and not self:HasFullProduction()) do if (self:GetSuppliesCount() <= 25) then @@ -193,16 +198,13 @@ function Factory:LoopProduction() end function Factory:Update() - if (not self:IsValid() or not self:IsSetup()) then + if not (self:IsValid() and self:IsSetup()) then return end if (self.fast_prod_enabled and not self.m_fast_prod_running and not self:HasFullProduction()) then - self.m_fast_prod_running = true self:LoopProduction() end - - -- more stuff later end return Factory diff --git a/SSV2/includes/features/online/yim_resupplier/businesses/Office.lua b/SSV2/includes/features/online/yim_resupplier/businesses/Office.lua index ef3cdafb..fcee964d 100644 --- a/SSV2/includes/features/online/yim_resupplier/businesses/Office.lua +++ b/SSV2/includes/features/online/yim_resupplier/businesses/Office.lua @@ -7,12 +7,44 @@ -- * Provide a copy of or a link to the original license (GPL-3.0 or later); see LICENSE.md or . -local BusinessFront = require("BusinessFront") -local VehicleWarehouse = require("VehicleWarehouse") -local Warehouse = require("Warehouse") -local RawBusinessData = require("includes.data.yrv3_data") -local InteriorIDs = require("includes.data.refs").InteriorIDs - +local BusinessFront = require("BusinessFront") +local VehicleWarehouse = require("VehicleWarehouse") +local Warehouse = require("Warehouse") +local RawBusinessData = require("includes.data.yrv3_data") +local IPLPrefixes = { + "ex_sm_13_", + "ex_sm_15_", + "ex_dt1_02_", + "ex_dt1_11_", +} +local IPLVars = { + "office_01a", + "office_01b", + "office_01c", + "office_02a", + "office_02b", + "office_02c", + "office_03a", + "office_03b", + "office_03c", +} + +---@class OfficeClutterItemsParam +---@field cash boolean +---@field Swag_Silver? boolean +---@field Swag_Pills? boolean +---@field Swag_Med? boolean +---@field Swag_JewelWatch? boolean +---@field Swag_Ivory? boolean +---@field Swag_Guns? boolean +---@field Swag_Gems? boolean +---@field Swag_Furcoats? boolean +---@field Swag_electronic? boolean +---@field Swag_DrugStatue? boolean +---@field Swag_DrugBags? boolean +---@field Swag_Counterfeit? boolean +---@field Swag_Booze_cigs? boolean +---@field Swag_Art? boolean ---@class OfficeOpts : BusinessFrontOpts ---@field custom_name nil @@ -32,10 +64,13 @@ local InteriorIDs = require("includes.data.refs").InteriorIDs ---@field private m_id integer ---@field private m_name string ---@field private m_custom_name string +---@field private m_ipl_name string +---@field private m_interior_name_hash joaat_t ---@field private m_subs array ---@field private m_vehicle_warehouse? VehicleWarehouse ---@field private m_earnings_report OfficeEarningsReport ---@field private m_last_report_check_time milliseconds +---@field private m_last_known_interior integer local Office = setmetatable({}, BusinessFront) Office.__index = Office @@ -49,8 +84,12 @@ function Office.new(opts) customName = Game.GetLabelText("GB_REST_ACC") end - instance.m_custom_name = customName - instance.m_earnings_report = { + local iplPrefix = IPLPrefixes[opts.id or 1] + local iplSuffix = IPLVars[stats.get_int("MPX_PROP_OFFICE_VAR")] + instance.m_ipl_name = iplPrefix .. iplSuffix + instance.m_last_known_interior = 0 + instance.m_custom_name = customName + instance.m_earnings_report = { lifetime_buy_undertaken = 0, lifetime_buy_completed = 0, lifetime_sell_undertaken = 0, @@ -63,14 +102,38 @@ function Office.new(opts) instance:AddSubBusiness(i) end instance:CheckVehicleWarehouse() + --[[ + scr_function.add_script_function_hook("am_mp_property_int", "NO_OFFICE_CLUTTER", "2D 03 05 00 00 43 75 04 66 38", function(args, rets) + local iParam0 = args:get_int(0) + if (iParam0 == 0 or iParam0 == 1) then + rets:set_int(0, 0) + return false + end + return true + end) ]] return instance end function Office:Reset() + -- scr_function.remove_script_function_hook("am_mp_property_int", "NO_OFFICE_CLUTTER") self:ResetImpl() end +---@return string +function Office:GetIPLName() + return self.m_ipl_name +end + +---@return boolean +function Office:IsPlayerInside() + if (LocalPlayer:IsOutside()) then + return false + end + + return STREAMING.IS_IPL_ACTIVE(self.m_ipl_name) +end + ---@private function Office:CheckVehicleWarehouse() local ie_wh_prop = stats.get_int("MPX_PROP_IE_WAREHOUSE") @@ -204,9 +267,8 @@ function Office:Rename(newName) GPBD_FM_3:At(106):WriteString(newName, 64) end - local officeInt = InteriorIDs.INTERIOR_ID_OFFICE - if (LocalPlayer:GetInteriorID() == officeInt) then - INTERIOR.REFRESH_INTERIOR(officeInt) + if (self:IsPlayerInside()) then + INTERIOR.REFRESH_INTERIOR(LocalPlayer:GetInteriorID()) end local current = g_Name:ReadString() @@ -217,6 +279,81 @@ function Office:Rename(newName) end) end +---@private +---@param officeInt integer +---@return boolean +local function remove_office_cash_clutter_set(officeInt) + local out = false + for i = 1, 24 do + local suffix = i < 10 and "0" .. i or tostring(i) + local setName = "Cash_Set_" .. suffix + if (INTERIOR.IS_INTERIOR_ENTITY_SET_ACTIVE(officeInt, setName)) then + INTERIOR.DEACTIVATE_INTERIOR_ENTITY_SET(officeInt, setName) + out = true + end + end + return out +end + +---@private +---@param officeInt integer +---@param baseName string +---@return boolean +local function remove_office_clutter_set(officeInt, baseName) + local out = false + if (INTERIOR.IS_INTERIOR_ENTITY_SET_ACTIVE(officeInt, baseName)) then + INTERIOR.DEACTIVATE_INTERIOR_ENTITY_SET(officeInt, baseName) + out = true + end + + for i = 2, 3 do + local setName = baseName .. i + if (INTERIOR.IS_INTERIOR_ENTITY_SET_ACTIVE(officeInt, setName)) then + INTERIOR.DEACTIVATE_INTERIOR_ENTITY_SET(officeInt, setName) + out = true + end + end + + return out +end + +---@param clutter_t OfficeClutterItemsParam +function Office:RemoveClutter(clutter_t) + local interior = LocalPlayer:GetInteriorID() + if (interior == self.m_last_known_interior) then + return + end + + if (not self:IsPlayerInside()) then + self.m_last_known_interior = interior + return + end + + if (not PLAYER.IS_PLAYER_CONTROL_ON(LocalPlayer:GetID())) then + return + end + + local shouldRefresh = false + for name, bValue in pairs(clutter_t) do + if (not bValue) then + goto continue + end + + if (name == "cash") then + shouldRefresh = remove_office_cash_clutter_set(interior) + elseif (remove_office_clutter_set(interior, name)) then + shouldRefresh = true + end + + ::continue:: + end + + if (shouldRefresh) then + INTERIOR.REFRESH_INTERIOR(interior) + end + self.m_last_known_interior = interior +end + function Office:Update() if (self.m_vehicle_warehouse) then self.m_vehicle_warehouse:Update() @@ -228,9 +365,15 @@ function Office:Update() end end - if (Time.Millis() - self.m_last_report_check_time > 5000 and GUI:IsOpen()) then + local now_ms = Time.Millis() + if (now_ms - self.m_last_report_check_time > 5000 and GUI:IsOpen()) then self:UpdateEarningsReport() - self.m_last_report_check_time = Time.Millis() + self.m_last_report_check_time = now_ms + end + + local cfg = GVars.features.yrv3.office_clutter + if (cfg.auto_disable) then + self:RemoveClutter(cfg.items) end end diff --git a/SSV2/includes/features/online/yim_resupplier/businesses/Warehouse.lua b/SSV2/includes/features/online/yim_resupplier/businesses/Warehouse.lua index 069c3404..c8fc48cc 100644 --- a/SSV2/includes/features/online/yim_resupplier/businesses/Warehouse.lua +++ b/SSV2/includes/features/online/yim_resupplier/businesses/Warehouse.lua @@ -71,6 +71,7 @@ Enums.eWarehouseType = { ---@field coords vec3 ---@field size? integer Special Cargo only + -- Class representing a warehouse that stores valuable cargo. -- -- Can be `CEO Special Cargo Warehouse` or `Hangar`. @@ -86,7 +87,7 @@ Enums.eWarehouseType = { ---@field private m_assistant_sourcing_func function ---@field private m_prod_value_func fun(count: integer): integer ---@field public auto_fill boolean -local Warehouse = setmetatable({}, BusinessBase) +local Warehouse = setmetatable({}, BusinessBase) Warehouse.__index = Warehouse ---@param opts WarehouseOpts diff --git a/SSV2/includes/frontend/yim_resupplier/dashboard_ui.lua b/SSV2/includes/frontend/yim_resupplier/dashboard_ui.lua index a4dd2da4..36e6accb 100644 --- a/SSV2/includes/frontend/yim_resupplier/dashboard_ui.lua +++ b/SSV2/includes/frontend/yim_resupplier/dashboard_ui.lua @@ -11,39 +11,39 @@ local YRV3 = require("includes.features.online.yim_resupplier local drawKeyValue = require("includes.frontend.helpers.draw_kv") local drawTxnPopup = require("includes.frontend.yim_resupplier.helpers.withdraw_deposit_popup") local drawBossRegisterUI = require("includes.frontend.yim_resupplier.helpers.boss_register_combo") -local colMoneyGreen = Color("#85BB65") local moneyCardChildSize = vec2:new(200, 90) local moneyCardBtnSize = vec2:new(0, 35) local bossLabel = "" -local moneyCardColors = { - [0] = Color(0.033, 0.45, 0.15, 0.95), - [1] = Color(0.78, 0.78, 0.78, 0.90) -} - ----@param cardType 0|1 -- 0: wallet | 1: bank ----@param money integer ----@param formattedMoney string -local function drawMoneyCard(cardType, money, formattedMoney) +local colMoneyGreen = Color("#85BB65") +local colCardGreen = Color(0.033, 0.45, 0.15, 0.95) +local colCardWhite = Color(0.78, 0.78, 0.78, 0.90) +local moneyCardColors = { colCardGreen, colCardWhite } + +---@param cardType +---| 1: wallet +---| 2: bank +---@param amount integer +---@param amountFmt string +local function drawMoneyCard(cardType, amount, amountFmt) local outerWidth = ImGui.GetContentRegionAvail() - local label = (cardType == 0) and "YRV3_DASHBOARD_FUNDS_WALLET" or "YRV3_DASHBOARD_FUNDS_BANK" - local bgCol = moneyCardColors[cardType] + local label = (cardType == 1) and "YRV3_DASHBOARD_FUNDS_WALLET" or "YRV3_DASHBOARD_FUNDS_BANK" + local bgCol = moneyCardColors[cardType] or Color.WHITE ImGui.PushStyleColor(ImGuiCol.ChildBg, bgCol:AsFloat()) ImGui.PushStyleColor(ImGuiCol.Text, ImGui.GetAutoTextColor(bgCol):AsFloat()) ImGui.BeginChildEx(label, moneyCardChildSize, ImGuiChildFlags.AlwaysUseWindowPadding) - if (money > 9999999) then + if (amount > 9999999) then local availWidth, _ = ImGui.GetContentRegionAvail() ImGui.SetWindowFontScale(1.15) - if (ImGui.CalcTextSize(formattedMoney) > availWidth and outerWidth > moneyCardChildSize.x) then + if (ImGui.CalcTextSize(amountFmt) > availWidth and outerWidth > moneyCardChildSize.x) then moneyCardChildSize.x = moneyCardChildSize.x + 10 end - ImGui.SetWindowFontScale(1.0) end ImGui.SetWindowFontScale(0.75) ImGui.Text(_T(label)) ImGui.SetWindowFontScale(1.15) - ImGui.Text(formattedMoney) + ImGui.Text(amountFmt) ImGui.SetWindowFontScale(1.0) ImGui.Spacing() ImGui.EndChild() @@ -71,9 +71,9 @@ local function drawPortfolio() ImGui.Spacing() moneyCardChildSize.x = math.max(200, ImGui.GetContentRegionAvail() * 0.484) - drawMoneyCard(0, walletBal, walletFmt) + drawMoneyCard(1, walletBal, walletFmt) ImGui.SameLineIfAvail(moneyCardChildSize.x) - drawMoneyCard(1, bankBal, bankFmt) + drawMoneyCard(2, bankBal, bankFmt) local withdrawLabel = _T("YRV3_DASHBOARD_FUNDS_WITHDRAW") local depositLabel = _T("YRV3_DASHBOARD_FUNDS_DEPOSIT") diff --git a/SSV2/includes/frontend/yim_resupplier/office_ui.lua b/SSV2/includes/frontend/yim_resupplier/office_ui.lua index 058180b1..b6c74c0c 100644 --- a/SSV2/includes/frontend/yim_resupplier/office_ui.lua +++ b/SSV2/includes/frontend/yim_resupplier/office_ui.lua @@ -7,50 +7,123 @@ -- * Provide a copy of or a link to the original license (GPL-3.0 or later); see LICENSE.md or . -local YRV3 = require("includes.features.online.yim_resupplier.YimResupplierV3") -local measureBulletWidths = require("includes.frontend.helpers.measure_text_width") -local drawNamePlate = require("includes.frontend.yim_resupplier.nameplate_ui") -local drawVehicleWarehouse = require("includes.frontend.yim_resupplier.vehicle_warehouse_ui") -local drawWarehouse = require("includes.frontend.yim_resupplier.warehouse_ui") -local colMoneyGreen = Color("#85BB65") +local YRV3 = require("includes.features.online.yim_resupplier.YimResupplierV3") +local measureBulletWidths = require("includes.frontend.helpers.measure_text_width") +local drawNamePlate = require("includes.frontend.yim_resupplier.nameplate_ui") +local drawVehicleWarehouse = require("includes.frontend.yim_resupplier.vehicle_warehouse_ui") +local drawWarehouse = require("includes.frontend.yim_resupplier.warehouse_ui") +local colMoneyGreen = Color("#85BB65") ---@type array -local bulletWidths = {} -local showEarningsData = { v = false } -local earningDataIntBuff = nil -local earningDataIdx = 1 -local earningPopupName = "" -local newNameBuff = "" -local renamePopupLabel = "##renameCEO" -local shouldDrawRenamePopup = false -local earningData = { +local bulletWidths = {} +local clutterItemNameWidths = {} +local showEarningsData = { v = false } +local earningDataIntBuff = nil +local earningDataIdx = 1 +local earningPopupName = "" +local newNameBuff = "" +local popups = { + rename = false, + clutter = false +} +local earningData = { { label = "YRV3_LIFETIME_BUY_UNDERTAKEN", pstat = "MPX_LIFETIME_BUY_UNDERTAKEN", min = 0, max = 5e3, step = 1, step_fast = 100 }, { label = "YRV3_LIFETIME_BUY_COMPLETE", pstat = "MPX_LIFETIME_BUY_COMPLETE", min = 0, max = 5e3, step = 1, step_fast = 100 }, { label = "YRV3_LIFETIME_SELL_UNDERTAKEN", pstat = "MPX_LIFETIME_SELL_UNDERTAKEN", min = 0, max = 5e3, step = 1, step_fast = 100 }, { label = "YRV3_LIFETIME_SELL_COMPLETE", pstat = "MPX_LIFETIME_SELL_COMPLETE", min = 0, max = 5e3, step = 1, step_fast = 100 }, { label = "YRV3_LIFETIME_EARNINGS", pstat = "MPX_LIFETIME_CONTRA_EARNINGS", min = 0, max = 1e8, step = 1e5, step_fast = 1e6 }, } +local clutterNamesMap = { + ["cash"] = "YRV3_OFFICE_CLUTTER_CASH", + ["Swag_Silver"] = "YRV3_OFFICE_CLUTTER_SILVER", + ["Swag_Pills"] = "YRV3_OFFICE_CLUTTER_PILLS", + ["Swag_Med"] = "YRV3_OFFICE_CLUTTER_MEDS", + ["Swag_JewelWatch"] = "YRV3_OFFICE_CLUTTER_JEWEL", + ["Swag_Ivory"] = "YRV3_OFFICE_CLUTTER_IVORY", + ["Swag_Guns"] = "YRV3_OFFICE_CLUTTER_CUNS", + ["Swag_Gems"] = "YRV3_OFFICE_CLUTTER_GEMS", + ["Swag_Furcoats"] = "YRV3_OFFICE_CLUTTER_FURCOATS", + ["Swag_electronic"] = "YRV3_OFFICE_CLUTTER_ELECTRONICS", + ["Swag_DrugStatue"] = "YRV3_OFFICE_CLUTTER_DRUGSTATUES", + ["Swag_DrugBags"] = "YRV3_OFFICE_CLUTTER_DRUGBAGS", + ["Swag_Counterfeit"] = "YRV3_OFFICE_CLUTTER_COUNTERFEIT", + ["Swag_Booze_cigs"] = "YRV3_OFFICE_CLUTTER_BOOZE", + ["Swag_Art"] = "YRV3_OFFICE_CLUTTER_ART", +}; local clutterItemsCount = table.getlen(clutterNamesMap) ----@param offfice Office -local function drawRenamePopup(offfice) +---@param office Office +local function drawRenamePopup(office) ImGui.Spacing() newNameBuff = ImGui.InputTextWithHint("##newName", _T("GENERIC_NAME"), newNameBuff, 32) ImGui.SameLine() if (GUI:Button(_T("GENERIC_SAVE"))) then - offfice:Rename(newNameBuff) + office:Rename(newNameBuff) ImGui.CloseCurrentPopup() end ImGui.SameLine() if (GUI:Button(_T("GENERIC_CANCEL"))) then - newNameBuff = offfice:GetCustomName() + newNameBuff = office:GetCustomName() ImGui.CloseCurrentPopup() end ImGui.Spacing() end +---@param items table +---@return boolean +local function areAllItemsEnabled(items) + for _, bool in pairs(items) do + if (not bool) then + return false + end + end + return true +end + +local function drawOfficeClutterOptions() + local cfg = GVars.features.yrv3.office_clutter + local items = cfg.items + cfg.auto_disable = GUI:CustomToggle(_T("YRV3_OFFICE_CLUTTER_AUTO_CLEAR"), cfg.auto_disable, { + tooltip = _T("YRV3_OFFICE_CLUTTER_AUTO_CLEAR_TT"), + }) + + ImGui.Spacing() + ImGui.SeparatorText(_T("YRV3_OFFICE_CLUTTER_ITEM_SELECT")) + + local allEnabled = areAllItemsEnabled(items) + local btnLabel = allEnabled and "YRV3_CB_UNCHECK_ALL" or "YRV3_CB_CHECK_ALL" + if (GUI:Button(_T(btnLabel))) then + for name in pairs(items) do + items[name] = not allEnabled + end + end + + local langIndex = GVars.backend.language_index + local labelWidth = clutterItemNameWidths[langIndex] + if (not labelWidth) then + local labels = {} + for _, name in pairs(clutterNamesMap) do + table.insert(labels, _T(name)) + end + labelWidth = measureBulletWidths(labels, 60.0) + clutterItemNameWidths[langIndex] = labelWidth + end + + ImGui.Spacing() + local idx = 0 + for name, bValue in pairs(items) do + local label = _T(clutterNamesMap[name] or "GENERIC_UNKNOWN") + items[name] = GUI:CustomToggle(label, bValue) + idx = idx + 1 + + if (idx < clutterItemsCount and (idx & 1 == 1)) then + ImGui.SameLine(labelWidth) + end + end +end + ---@param data { label: string, pstat: string, min: integer, max: integer, step: integer, step_fast: integer } local function drawStatEdit(data) ImGui.Spacing() @@ -153,7 +226,12 @@ end local function contextCallback() if (ImGui.MenuItem(_T("GENERIC_RENAME"))) then - shouldDrawRenamePopup = true + popups.rename = true + ImGui.CloseCurrentPopup() + end + + if (ImGui.MenuItem(_T("YRV3_OFFICE_CLUTTER_LABEL"))) then + popups.clutter = true ImGui.CloseCurrentPopup() end end @@ -167,14 +245,19 @@ return function() drawNamePlate(office, { customName = office:GetCustomName(), contextMenuCallback = contextCallback }) - if (shouldDrawRenamePopup) then - newNameBuff = office:GetCustomName() - shouldDrawRenamePopup = false - ImGui.OpenPopup(renamePopupLabel) + if (popups.rename) then + popups.rename = false + newNameBuff = office:GetCustomName() + ImGui.OpenPopup("##renameCEO") + end + + if (popups.clutter) then + popups.clutter = false + ImGui.OpenPopup("##officeClutter") end if (ImGui.BeginPopupModal( - renamePopupLabel, + "##renameCEO", true, ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoSavedSettings @@ -184,6 +267,17 @@ return function() ImGui.EndPopup() end + if (ImGui.BeginPopupModal( + "##officeClutter", + true, + ImGuiWindowFlags.AlwaysAutoResize + | ImGuiWindowFlags.NoSavedSettings + | ImGuiWindowFlags.NoMove + )) then + drawOfficeClutterOptions() + ImGui.EndPopup() + end + ImGui.Spacing() local vehicleWarehouse = office:GetVehicleWarehouse() local cargoWarehouses = office:GetCargoWarehouses() diff --git a/SSV2/includes/lib/class.lua b/SSV2/includes/lib/class.lua index d39e1987..923924da 100644 --- a/SSV2/includes/lib/class.lua +++ b/SSV2/includes/lib/class.lua @@ -50,38 +50,35 @@ function Class(name, opts) end -- classes can be initialized directly without explicitly calling the constructor. - setmetatable( - cls, - { - __call = function(c, ...) - local instance - - if (base) then - if base.new then - instance = base.new(...) - elseif base.init then - instance = base:init(...) - end + setmetatable(cls, { + __call = function(c, ...) + local instance + + if (base) then + if base.new then + instance = base.new(...) + elseif base.init then + instance = base:init(...) end + end - if (c.new) then - instance = c.new(...) - elseif (c.init) then - instance = c:init(...) - else - instance = {} - end + if (c.new) then + instance = c.new(...) + elseif (c.init) then + instance = c:init(...) + else + instance = {} + end - if (type(instance) == "table") then - instance.__type = c.__type - setmetatable(instance, c) - end + if (type(instance) == "table") then + instance.__type = c.__type + setmetatable(instance, c) + end - return instance - end, - __index = base, - } - ) + return instance + end, + __index = base, + }) function cls:super() return self.__base or self diff --git a/SSV2/includes/lib/extensions/__init__.lua b/SSV2/includes/lib/extensions/__init__.lua index 82b18d63..1cdc8606 100644 --- a/SSV2/includes/lib/extensions/__init__.lua +++ b/SSV2/includes/lib/extensions/__init__.lua @@ -486,13 +486,20 @@ end -- Simply adds a number suffix if a file with the same name and extension already exists. ---@param base_name string ---@param extension string ----@return string +---@return string filename function GenerateUniqueFilename(base_name, extension) - local filename = _F("%s%s", base_name, extension) + if (not extension:startswith(".")) then + extension = "." .. extension + end + + local filename = base_name .. extension local suffix = 0 while (io.exists(filename)) do - suffix = suffix + 1 + suffix = suffix + 1 + end + + if (suffix > 0) then filename = _F("%s_%d%s", base_name, suffix, extension) end diff --git a/SSV2/includes/lib/translations/__hashmap.json b/SSV2/includes/lib/translations/__hashmap.json index d56e0c39..9d2e3682 100644 --- a/SSV2/includes/lib/translations/__hashmap.json +++ b/SSV2/includes/lib/translations/__hashmap.json @@ -985,5 +985,24 @@ "BSV2_ES_DRIVE_PROPERTY": 1818648912, "BSV2_ES_DRIVE_PROPERTY_TEXT": 1297815491, "GENERIC_RETRY": 2103860056, - "YRV3_UNSAFE_FEATS_FSL_ON_TXT": 4229971676 + "YRV3_UNSAFE_FEATS_FSL_ON_TXT": 4229971676, + "YRV3_OFFICE_CLUTTER_LABEL": 2934979922, + "YRV3_OFFICE_CLUTTER_AUTO_CLEAR": 508635405, + "YRV3_OFFICE_CLUTTER_AUTO_CLEAR_TT": 2426260190, + "YRV3_OFFICE_CLUTTER_ITEM_SELECT": 3297791676, + "YRV3_OFFICE_CLUTTER_CASH": 1776733566, + "YRV3_OFFICE_CLUTTER_SILVER": 2299237561, + "YRV3_OFFICE_CLUTTER_PILLS": 2552986490, + "YRV3_OFFICE_CLUTTER_MEDS": 81081872, + "YRV3_OFFICE_CLUTTER_JEWEL": 3629936205, + "YRV3_OFFICE_CLUTTER_IVORY": 1104289171, + "YRV3_OFFICE_CLUTTER_CUNS": 2151561516, + "YRV3_OFFICE_CLUTTER_GEMS": 534465050, + "YRV3_OFFICE_CLUTTER_FURCOATS": 3799463521, + "YRV3_OFFICE_CLUTTER_ELECTRONICS": 76642302, + "YRV3_OFFICE_CLUTTER_DRUGSTATUES": 403881731, + "YRV3_OFFICE_CLUTTER_DRUGBAGS": 668882719, + "YRV3_OFFICE_CLUTTER_COUNTERFEIT": 1739057289, + "YRV3_OFFICE_CLUTTER_BOOZE": 1350835526, + "YRV3_OFFICE_CLUTTER_ART": 2589536419 } \ No newline at end of file diff --git a/SSV2/includes/lib/translations/de-DE.lua b/SSV2/includes/lib/translations/de-DE.lua index 08b787a3..45090bc2 100644 --- a/SSV2/includes/lib/translations/de-DE.lua +++ b/SSV2/includes/lib/translations/de-DE.lua @@ -985,5 +985,24 @@ return { ["BSV2_ES_DRIVE_PROPERTY_TEXT"] = "Klicken Sie auf eine Immobilie, um dorthin zu reisen.", ["BSV2_ES_DRIVE_PROPERTY"] = "Eigene Immobilien", ["GENERIC_RETRY"] = "Wiederholen", - ["YRV3_UNSAFE_FEATS_FSL_ON_TXT"] = "Unsichere Funktionen sind bei Verwendung von FSL immer aktiviert." + ["YRV3_UNSAFE_FEATS_FSL_ON_TXT"] = "Unsichere Funktionen sind bei Verwendung von FSL immer aktiviert.", + ["YRV3_OFFICE_CLUTTER_LABEL"] = "Unordnung im Büro", + ["YRV3_OFFICE_CLUTTER_AUTO_CLEAR_TT"] = "Bereinigt Ihr Büro automatisch von allen ausgewählten Unordnungsgegenständen.", + ["YRV3_OFFICE_CLUTTER_AUTO_CLEAR"] = "Automatisches Löschen", + ["YRV3_OFFICE_CLUTTER_CUNS"] = "Waffen", + ["YRV3_OFFICE_CLUTTER_SILVER"] = "Silber", + ["YRV3_OFFICE_CLUTTER_ITEM_SELECT"] = "Zu löschende Elemente:", + ["YRV3_OFFICE_CLUTTER_ELECTRONICS"] = "Elektronik", + ["YRV3_OFFICE_CLUTTER_CASH"] = "Kasse", + ["YRV3_OFFICE_CLUTTER_MEDS"] = "Medikamente", + ["YRV3_OFFICE_CLUTTER_IVORY"] = "Elfenbein", + ["YRV3_OFFICE_CLUTTER_JEWEL"] = "Schmuck", + ["YRV3_OFFICE_CLUTTER_GEMS"] = "Edelsteine", + ["YRV3_OFFICE_CLUTTER_PILLS"] = "Pillen", + ["YRV3_OFFICE_CLUTTER_DRUGSTATUES"] = "Drogenstatuen", + ["YRV3_OFFICE_CLUTTER_FURCOATS"] = "Pelzmäntel", + ["YRV3_OFFICE_CLUTTER_DRUGBAGS"] = "Drogentaschen", + ["YRV3_OFFICE_CLUTTER_COUNTERFEIT"] = "Gefälschte Artikel", + ["YRV3_OFFICE_CLUTTER_ART"] = "Kunstwerk", + ["YRV3_OFFICE_CLUTTER_BOOZE"] = "Alkohol und Zigaretten" } diff --git a/SSV2/includes/lib/translations/en-US.lua b/SSV2/includes/lib/translations/en-US.lua index cecff4ce..c078d714 100644 --- a/SSV2/includes/lib/translations/en-US.lua +++ b/SSV2/includes/lib/translations/en-US.lua @@ -476,6 +476,25 @@ return { ["YRV3_EDIT_EARNINGS_DATA"] = "Edit Earnings Data", ["YRV3_BULK_SAFE_FILL_NONE"] = "No cash safes were filled. Either you don't own any safes or they are all full of cash.", ["YRV3_BULK_SAFE_FILL_SUCCESS_FMT"] = "Filled %d cash safes to max.", + ["YRV3_OFFICE_CLUTTER_LABEL"] = "Office Clutter", + ["YRV3_OFFICE_CLUTTER_AUTO_CLEAR"] = "Auto Clear", + ["YRV3_OFFICE_CLUTTER_AUTO_CLEAR_TT"] = "Automatically clears your office from all selected clutter items.", + ["YRV3_OFFICE_CLUTTER_ITEM_SELECT"] = "Items To Clear:", + ["YRV3_OFFICE_CLUTTER_CASH"] = "Cash", + ["YRV3_OFFICE_CLUTTER_SILVER"] = "Silver", + ["YRV3_OFFICE_CLUTTER_PILLS"] = "Pills", + ["YRV3_OFFICE_CLUTTER_MEDS"] = "Meds", + ["YRV3_OFFICE_CLUTTER_JEWEL"] = "Jewelry", + ["YRV3_OFFICE_CLUTTER_IVORY"] = "Ivory", + ["YRV3_OFFICE_CLUTTER_CUNS"] = "Weapons", + ["YRV3_OFFICE_CLUTTER_GEMS"] = "Gems", + ["YRV3_OFFICE_CLUTTER_FURCOATS"] = "Fur Coats", + ["YRV3_OFFICE_CLUTTER_ELECTRONICS"] = "Electronics", + ["YRV3_OFFICE_CLUTTER_DRUGSTATUES"] = "Drug Statues", + ["YRV3_OFFICE_CLUTTER_DRUGBAGS"] = "Drug Bags", + ["YRV3_OFFICE_CLUTTER_COUNTERFEIT"] = "Counterfeit Items", + ["YRV3_OFFICE_CLUTTER_BOOZE"] = "Alcohol & Cigarettes", + ["YRV3_OFFICE_CLUTTER_ART"] = "Artwork", --#endregion --#region Self diff --git a/SSV2/includes/lib/translations/es-ES.lua b/SSV2/includes/lib/translations/es-ES.lua index 71655ea6..0abfb440 100644 --- a/SSV2/includes/lib/translations/es-ES.lua +++ b/SSV2/includes/lib/translations/es-ES.lua @@ -985,5 +985,24 @@ return { ["BSV2_ES_DRIVE_PROPERTY"] = "Propiedades propias", ["BSV2_ES_DRIVE_PROPERTY_TEXT"] = "Haga clic en una propiedad para viajar a ella.", ["GENERIC_RETRY"] = "Rever", - ["YRV3_UNSAFE_FEATS_FSL_ON_TXT"] = "Las funciones inseguras siempre están habilitadas cuando se usa FSL." + ["YRV3_UNSAFE_FEATS_FSL_ON_TXT"] = "Las funciones inseguras siempre están habilitadas cuando se usa FSL.", + ["YRV3_OFFICE_CLUTTER_AUTO_CLEAR_TT"] = "Limpia automáticamente su oficina de todos los elementos desordenados seleccionados.", + ["YRV3_OFFICE_CLUTTER_ITEM_SELECT"] = "Elementos a borrar:", + ["YRV3_OFFICE_CLUTTER_AUTO_CLEAR"] = "Borrado automático", + ["YRV3_OFFICE_CLUTTER_SILVER"] = "Plata", + ["YRV3_OFFICE_CLUTTER_JEWEL"] = "Joyas", + ["YRV3_OFFICE_CLUTTER_CASH"] = "Dinero", + ["YRV3_OFFICE_CLUTTER_CUNS"] = "Armas", + ["YRV3_OFFICE_CLUTTER_PILLS"] = "Pastillas", + ["YRV3_OFFICE_CLUTTER_MEDS"] = "medicamentos", + ["YRV3_OFFICE_CLUTTER_ELECTRONICS"] = "Electrónica", + ["YRV3_OFFICE_CLUTTER_FURCOATS"] = "Abrigos de piel", + ["YRV3_OFFICE_CLUTTER_GEMS"] = "Gemas", + ["YRV3_OFFICE_CLUTTER_IVORY"] = "Marfil", + ["YRV3_OFFICE_CLUTTER_DRUGSTATUES"] = "Estatuas de drogas", + ["YRV3_OFFICE_CLUTTER_LABEL"] = "Desorden de oficina", + ["YRV3_OFFICE_CLUTTER_ART"] = "Obra de arte", + ["YRV3_OFFICE_CLUTTER_DRUGBAGS"] = "Bolsas de drogas", + ["YRV3_OFFICE_CLUTTER_COUNTERFEIT"] = "Artículos falsificados", + ["YRV3_OFFICE_CLUTTER_BOOZE"] = "Alcohol y cigarrillos" } diff --git a/SSV2/includes/lib/translations/fr-FR.lua b/SSV2/includes/lib/translations/fr-FR.lua index f3cd50b5..a04e00c8 100644 --- a/SSV2/includes/lib/translations/fr-FR.lua +++ b/SSV2/includes/lib/translations/fr-FR.lua @@ -985,5 +985,24 @@ return { ["BSV2_ES_DRIVE_PROPERTY"] = "Propriétés possédées", ["BSV2_ES_DRIVE_PROPERTY_TEXT"] = "Cliquez sur une propriété pour vous y rendre.", ["GENERIC_RETRY"] = "Réessayer", - ["YRV3_UNSAFE_FEATS_FSL_ON_TXT"] = "Les fonctionnalités non sécurisées sont toujours activées lors de l’utilisation de FSL." + ["YRV3_UNSAFE_FEATS_FSL_ON_TXT"] = "Les fonctionnalités non sécurisées sont toujours activées lors de l’utilisation de FSL.", + ["YRV3_OFFICE_CLUTTER_CASH"] = "Espèces", + ["YRV3_OFFICE_CLUTTER_CUNS"] = "Armes", + ["YRV3_OFFICE_CLUTTER_IVORY"] = "Ivoire", + ["YRV3_OFFICE_CLUTTER_ITEM_SELECT"] = "Éléments à effacer :", + ["YRV3_OFFICE_CLUTTER_ELECTRONICS"] = "Électronique", + ["YRV3_OFFICE_CLUTTER_JEWEL"] = "Bijoux", + ["YRV3_OFFICE_CLUTTER_AUTO_CLEAR_TT"] = "Efface automatiquement votre bureau de tous les éléments encombrés sélectionnés.", + ["YRV3_OFFICE_CLUTTER_SILVER"] = "Argent", + ["YRV3_OFFICE_CLUTTER_AUTO_CLEAR"] = "Effacement automatique", + ["YRV3_OFFICE_CLUTTER_FURCOATS"] = "Manteaux de fourrure", + ["YRV3_OFFICE_CLUTTER_MEDS"] = "Médicaments", + ["YRV3_OFFICE_CLUTTER_PILLS"] = "Pilules", + ["YRV3_OFFICE_CLUTTER_LABEL"] = "Encombrement du bureau", + ["YRV3_OFFICE_CLUTTER_DRUGSTATUES"] = "Statues de drogue", + ["YRV3_OFFICE_CLUTTER_DRUGBAGS"] = "Sacs de drogue", + ["YRV3_OFFICE_CLUTTER_GEMS"] = "Pierres précieuses", + ["YRV3_OFFICE_CLUTTER_ART"] = "Oeuvre", + ["YRV3_OFFICE_CLUTTER_COUNTERFEIT"] = "Articles contrefaits", + ["YRV3_OFFICE_CLUTTER_BOOZE"] = "Alcool et cigarettes" } diff --git a/SSV2/includes/lib/translations/it-IT.lua b/SSV2/includes/lib/translations/it-IT.lua index 3db21e27..5fb0ad18 100644 --- a/SSV2/includes/lib/translations/it-IT.lua +++ b/SSV2/includes/lib/translations/it-IT.lua @@ -985,5 +985,24 @@ return { ["BSV2_ES_DRIVE_PROPERTY_TEXT"] = "Fare clic su una proprietà per raggiungerla.", ["BSV2_ES_DRIVE_PROPERTY"] = "Proprietà di proprietà", ["GENERIC_RETRY"] = "Riprova", - ["YRV3_UNSAFE_FEATS_FSL_ON_TXT"] = "Le funzionalità non sicure sono sempre abilitate quando si utilizza FSL." + ["YRV3_UNSAFE_FEATS_FSL_ON_TXT"] = "Le funzionalità non sicure sono sempre abilitate quando si utilizza FSL.", + ["YRV3_OFFICE_CLUTTER_LABEL"] = "Disordine in ufficio", + ["YRV3_OFFICE_CLUTTER_AUTO_CLEAR"] = "Cancellazione automatica", + ["YRV3_OFFICE_CLUTTER_ITEM_SELECT"] = "Elementi da cancellare:", + ["YRV3_OFFICE_CLUTTER_MEDS"] = "Medicinali", + ["YRV3_OFFICE_CLUTTER_AUTO_CLEAR_TT"] = "Cancella automaticamente il tuo ufficio da tutti gli oggetti disordinati selezionati.", + ["YRV3_OFFICE_CLUTTER_PILLS"] = "Pillole", + ["YRV3_OFFICE_CLUTTER_CUNS"] = "Armi", + ["YRV3_OFFICE_CLUTTER_GEMS"] = "Gemme", + ["YRV3_OFFICE_CLUTTER_SILVER"] = "Argento", + ["YRV3_OFFICE_CLUTTER_IVORY"] = "Avorio", + ["YRV3_OFFICE_CLUTTER_JEWEL"] = "Gioielli", + ["YRV3_OFFICE_CLUTTER_CASH"] = "Contanti", + ["YRV3_OFFICE_CLUTTER_ELECTRONICS"] = "Elettronica", + ["YRV3_OFFICE_CLUTTER_ART"] = "Opera d'arte", + ["YRV3_OFFICE_CLUTTER_DRUGBAGS"] = "Borse per farmaci", + ["YRV3_OFFICE_CLUTTER_FURCOATS"] = "Cappotti di pelliccia", + ["YRV3_OFFICE_CLUTTER_BOOZE"] = "Alcol e sigarette", + ["YRV3_OFFICE_CLUTTER_COUNTERFEIT"] = "Articoli contraffatti", + ["YRV3_OFFICE_CLUTTER_DRUGSTATUES"] = "Statue della droga" } diff --git a/SSV2/includes/lib/translations/ja-JP.lua b/SSV2/includes/lib/translations/ja-JP.lua index 9aad5826..9f2394be 100644 --- a/SSV2/includes/lib/translations/ja-JP.lua +++ b/SSV2/includes/lib/translations/ja-JP.lua @@ -985,5 +985,24 @@ return { ["BSV2_ES_DRIVE_PROPERTY_TEXT"] = "物件をクリックしてその物件に移動します。", ["BSV2_ES_DRIVE_PROPERTY"] = "所有物件", ["GENERIC_RETRY"] = "リトライ", - ["YRV3_UNSAFE_FEATS_FSL_ON_TXT"] = "FSL を使用する場合、安全でない機能は常に有効になります。" + ["YRV3_UNSAFE_FEATS_FSL_ON_TXT"] = "FSL を使用する場合、安全でない機能は常に有効になります。", + ["YRV3_OFFICE_CLUTTER_PILLS"] = "丸薬", + ["YRV3_OFFICE_CLUTTER_CASH"] = "現金", + ["YRV3_OFFICE_CLUTTER_AUTO_CLEAR_TT"] = "選択したすべての散らかったアイテムをオフィスから自動的に取り除きます。", + ["YRV3_OFFICE_CLUTTER_JEWEL"] = "ジュエリー", + ["YRV3_OFFICE_CLUTTER_CUNS"] = "兵器", + ["YRV3_OFFICE_CLUTTER_GEMS"] = "宝石", + ["YRV3_OFFICE_CLUTTER_AUTO_CLEAR"] = "オートクリア", + ["YRV3_OFFICE_CLUTTER_LABEL"] = "オフィスの乱雑さ", + ["YRV3_OFFICE_CLUTTER_MEDS"] = "薬", + ["YRV3_OFFICE_CLUTTER_ELECTRONICS"] = "エレクトロニクス", + ["YRV3_OFFICE_CLUTTER_SILVER"] = "銀", + ["YRV3_OFFICE_CLUTTER_IVORY"] = "象牙", + ["YRV3_OFFICE_CLUTTER_FURCOATS"] = "毛皮コート", + ["YRV3_OFFICE_CLUTTER_ITEM_SELECT"] = "クリアする項目:", + ["YRV3_OFFICE_CLUTTER_ART"] = "アートワーク", + ["YRV3_OFFICE_CLUTTER_BOOZE"] = "アルコールとタバコ", + ["YRV3_OFFICE_CLUTTER_DRUGBAGS"] = "ドラッグバッグ", + ["YRV3_OFFICE_CLUTTER_DRUGSTATUES"] = "麻薬の彫像", + ["YRV3_OFFICE_CLUTTER_COUNTERFEIT"] = "偽造品" } diff --git a/SSV2/includes/lib/translations/ko-KR.lua b/SSV2/includes/lib/translations/ko-KR.lua index ab25b4b3..1367a634 100644 --- a/SSV2/includes/lib/translations/ko-KR.lua +++ b/SSV2/includes/lib/translations/ko-KR.lua @@ -985,5 +985,24 @@ return { ["BSV2_ES_DRIVE_PROPERTY"] = "소유 부동산", ["BSV2_ES_DRIVE_PROPERTY_TEXT"] = "해당 부동산을 클릭하면 해당 부동산으로 이동합니다.", ["GENERIC_RETRY"] = "다시 해 보다", - ["YRV3_UNSAFE_FEATS_FSL_ON_TXT"] = "FSL을 사용하면 안전하지 않은 기능이 항상 활성화됩니다." + ["YRV3_UNSAFE_FEATS_FSL_ON_TXT"] = "FSL을 사용하면 안전하지 않은 기능이 항상 활성화됩니다.", + ["YRV3_OFFICE_CLUTTER_LABEL"] = "사무실 혼란", + ["YRV3_OFFICE_CLUTTER_SILVER"] = "은", + ["YRV3_OFFICE_CLUTTER_JEWEL"] = "보석류", + ["YRV3_OFFICE_CLUTTER_AUTO_CLEAR_TT"] = "선택한 모든 복잡한 항목에서 사무실을 자동으로 지웁니다.", + ["YRV3_OFFICE_CLUTTER_ITEM_SELECT"] = "지울 항목:", + ["YRV3_OFFICE_CLUTTER_AUTO_CLEAR"] = "자동 클리어", + ["YRV3_OFFICE_CLUTTER_IVORY"] = "상아", + ["YRV3_OFFICE_CLUTTER_PILLS"] = "의사", + ["YRV3_OFFICE_CLUTTER_CASH"] = "현금", + ["YRV3_OFFICE_CLUTTER_MEDS"] = "약", + ["YRV3_OFFICE_CLUTTER_GEMS"] = "보석", + ["YRV3_OFFICE_CLUTTER_ELECTRONICS"] = "전자제품", + ["YRV3_OFFICE_CLUTTER_DRUGSTATUES"] = "마약 동상", + ["YRV3_OFFICE_CLUTTER_DRUGBAGS"] = "마약 가방", + ["YRV3_OFFICE_CLUTTER_CUNS"] = "무기", + ["YRV3_OFFICE_CLUTTER_COUNTERFEIT"] = "위조품", + ["YRV3_OFFICE_CLUTTER_FURCOATS"] = "모피 코트", + ["YRV3_OFFICE_CLUTTER_ART"] = "삽화", + ["YRV3_OFFICE_CLUTTER_BOOZE"] = "주류 및 담배" } diff --git a/SSV2/includes/lib/translations/pl-PL.lua b/SSV2/includes/lib/translations/pl-PL.lua index cff79df8..338fa979 100644 --- a/SSV2/includes/lib/translations/pl-PL.lua +++ b/SSV2/includes/lib/translations/pl-PL.lua @@ -985,5 +985,24 @@ return { ["BSV2_ES_DRIVE_PROPERTY"] = "Posiadane nieruchomości", ["BSV2_ES_DRIVE_PROPERTY_TEXT"] = "Kliknij nieruchomość, aby do niej udać się.", ["GENERIC_RETRY"] = "Spróbować ponownie", - ["YRV3_UNSAFE_FEATS_FSL_ON_TXT"] = "Niebezpieczne funkcje są zawsze włączone podczas korzystania z FSL." + ["YRV3_UNSAFE_FEATS_FSL_ON_TXT"] = "Niebezpieczne funkcje są zawsze włączone podczas korzystania z FSL.", + ["YRV3_OFFICE_CLUTTER_LABEL"] = "Bałagan w biurze", + ["YRV3_OFFICE_CLUTTER_AUTO_CLEAR"] = "Automatyczne czyszczenie", + ["YRV3_OFFICE_CLUTTER_ITEM_SELECT"] = "Elementy do wyczyszczenia:", + ["YRV3_OFFICE_CLUTTER_CASH"] = "Gotówka", + ["YRV3_OFFICE_CLUTTER_SILVER"] = "Srebrny", + ["YRV3_OFFICE_CLUTTER_JEWEL"] = "Biżuteria", + ["YRV3_OFFICE_CLUTTER_IVORY"] = "Kość słoniowa", + ["YRV3_OFFICE_CLUTTER_AUTO_CLEAR_TT"] = "Automatycznie czyści Twoje biuro ze wszystkich wybranych bałaganów.", + ["YRV3_OFFICE_CLUTTER_PILLS"] = "Pigułki", + ["YRV3_OFFICE_CLUTTER_MEDS"] = "Leki", + ["YRV3_OFFICE_CLUTTER_CUNS"] = "Broń", + ["YRV3_OFFICE_CLUTTER_GEMS"] = "Klejnoty", + ["YRV3_OFFICE_CLUTTER_ELECTRONICS"] = "Elektronika", + ["YRV3_OFFICE_CLUTTER_FURCOATS"] = "Futra", + ["YRV3_OFFICE_CLUTTER_DRUGSTATUES"] = "Posągi narkotyków", + ["YRV3_OFFICE_CLUTTER_COUNTERFEIT"] = "Podrabiane przedmioty", + ["YRV3_OFFICE_CLUTTER_BOOZE"] = "Alkohol i papierosy", + ["YRV3_OFFICE_CLUTTER_ART"] = "Grafika", + ["YRV3_OFFICE_CLUTTER_DRUGBAGS"] = "Torby na leki" } diff --git a/SSV2/includes/lib/translations/pt-BR.lua b/SSV2/includes/lib/translations/pt-BR.lua index ba19e580..5b42005f 100644 --- a/SSV2/includes/lib/translations/pt-BR.lua +++ b/SSV2/includes/lib/translations/pt-BR.lua @@ -985,5 +985,24 @@ return { ["BSV2_ES_DRIVE_PROPERTY"] = "Propriedades próprias", ["BSV2_ES_DRIVE_PROPERTY_TEXT"] = "Clique em uma propriedade para viajar até ela.", ["GENERIC_RETRY"] = "Tentar novamente", - ["YRV3_UNSAFE_FEATS_FSL_ON_TXT"] = "Recursos inseguros estão sempre habilitados ao usar FSL." + ["YRV3_UNSAFE_FEATS_FSL_ON_TXT"] = "Recursos inseguros estão sempre habilitados ao usar FSL.", + ["YRV3_OFFICE_CLUTTER_LABEL"] = "Desordem de escritório", + ["YRV3_OFFICE_CLUTTER_CASH"] = "Dinheiro", + ["YRV3_OFFICE_CLUTTER_SILVER"] = "Prata", + ["YRV3_OFFICE_CLUTTER_PILLS"] = "Comprimidos", + ["YRV3_OFFICE_CLUTTER_ITEM_SELECT"] = "Itens a serem limpos:", + ["YRV3_OFFICE_CLUTTER_JEWEL"] = "Joia", + ["YRV3_OFFICE_CLUTTER_AUTO_CLEAR_TT"] = "Limpa automaticamente seu escritório de todos os itens desorganizados selecionados.", + ["YRV3_OFFICE_CLUTTER_IVORY"] = "Marfim", + ["YRV3_OFFICE_CLUTTER_GEMS"] = "Gemas", + ["YRV3_OFFICE_CLUTTER_AUTO_CLEAR"] = "Limpeza automática", + ["YRV3_OFFICE_CLUTTER_MEDS"] = "Remédios", + ["YRV3_OFFICE_CLUTTER_CUNS"] = "Armas", + ["YRV3_OFFICE_CLUTTER_ELECTRONICS"] = "Eletrônica", + ["YRV3_OFFICE_CLUTTER_FURCOATS"] = "Casacos de pele", + ["YRV3_OFFICE_CLUTTER_DRUGSTATUES"] = "Estátuas de drogas", + ["YRV3_OFFICE_CLUTTER_BOOZE"] = "Álcool e Cigarros", + ["YRV3_OFFICE_CLUTTER_DRUGBAGS"] = "Sacos de drogas", + ["YRV3_OFFICE_CLUTTER_ART"] = "Arte", + ["YRV3_OFFICE_CLUTTER_COUNTERFEIT"] = "Itens Falsificados" } diff --git a/SSV2/includes/lib/translations/ru-RU.lua b/SSV2/includes/lib/translations/ru-RU.lua index 724f67f1..72918449 100644 --- a/SSV2/includes/lib/translations/ru-RU.lua +++ b/SSV2/includes/lib/translations/ru-RU.lua @@ -985,5 +985,24 @@ return { ["BSV2_ES_DRIVE_PROPERTY"] = "Собственная недвижимость", ["BSV2_ES_DRIVE_PROPERTY_TEXT"] = "Нажмите на объект, чтобы поехать к нему.", ["GENERIC_RETRY"] = "Повторить попытку", - ["YRV3_UNSAFE_FEATS_FSL_ON_TXT"] = "Небезопасные функции всегда включены при использовании FSL." + ["YRV3_UNSAFE_FEATS_FSL_ON_TXT"] = "Небезопасные функции всегда включены при использовании FSL.", + ["YRV3_OFFICE_CLUTTER_SILVER"] = "Серебро", + ["YRV3_OFFICE_CLUTTER_LABEL"] = "Офисный беспорядок", + ["YRV3_OFFICE_CLUTTER_ITEM_SELECT"] = "Предметы для очистки:", + ["YRV3_OFFICE_CLUTTER_CASH"] = "Наличные", + ["YRV3_OFFICE_CLUTTER_AUTO_CLEAR_TT"] = "Автоматически очищает ваш офис от всех выбранных предметов беспорядка.", + ["YRV3_OFFICE_CLUTTER_JEWEL"] = "Ювелирные изделия", + ["YRV3_OFFICE_CLUTTER_IVORY"] = "слоновая кость", + ["YRV3_OFFICE_CLUTTER_AUTO_CLEAR"] = "Автоматическая очистка", + ["YRV3_OFFICE_CLUTTER_PILLS"] = "Таблетки", + ["YRV3_OFFICE_CLUTTER_CUNS"] = "Оружие", + ["YRV3_OFFICE_CLUTTER_MEDS"] = "Лекарства", + ["YRV3_OFFICE_CLUTTER_GEMS"] = "Драгоценные камни", + ["YRV3_OFFICE_CLUTTER_ART"] = "произведение искусства", + ["YRV3_OFFICE_CLUTTER_ELECTRONICS"] = "Электроника", + ["YRV3_OFFICE_CLUTTER_FURCOATS"] = "Шубы", + ["YRV3_OFFICE_CLUTTER_DRUGSTATUES"] = "Статуи наркотиков", + ["YRV3_OFFICE_CLUTTER_COUNTERFEIT"] = "Поддельные товары", + ["YRV3_OFFICE_CLUTTER_BOOZE"] = "Алкоголь и сигареты", + ["YRV3_OFFICE_CLUTTER_DRUGBAGS"] = "Сумки для лекарств" } diff --git a/SSV2/includes/lib/translations/zh-CN.lua b/SSV2/includes/lib/translations/zh-CN.lua index c6fd9309..8a0e7c95 100644 --- a/SSV2/includes/lib/translations/zh-CN.lua +++ b/SSV2/includes/lib/translations/zh-CN.lua @@ -985,5 +985,24 @@ return { ["BSV2_ES_DRIVE_PROPERTY_TEXT"] = "单击某个属性即可前往该属性。", ["BSV2_ES_DRIVE_PROPERTY"] = "拥有的财产", ["GENERIC_RETRY"] = "重试", - ["YRV3_UNSAFE_FEATS_FSL_ON_TXT"] = "使用 FSL 时始终启用不安全功能。" + ["YRV3_UNSAFE_FEATS_FSL_ON_TXT"] = "使用 FSL 时始终启用不安全功能。", + ["YRV3_OFFICE_CLUTTER_ITEM_SELECT"] = "要清除的项目:", + ["YRV3_OFFICE_CLUTTER_AUTO_CLEAR_TT"] = "自动清除办公室中所有选定的杂乱物品。", + ["YRV3_OFFICE_CLUTTER_CASH"] = "现金", + ["YRV3_OFFICE_CLUTTER_PILLS"] = "药丸", + ["YRV3_OFFICE_CLUTTER_LABEL"] = "办公室杂乱", + ["YRV3_OFFICE_CLUTTER_MEDS"] = "药物", + ["YRV3_OFFICE_CLUTTER_AUTO_CLEAR"] = "自动清除", + ["YRV3_OFFICE_CLUTTER_IVORY"] = "象牙", + ["YRV3_OFFICE_CLUTTER_SILVER"] = "银", + ["YRV3_OFFICE_CLUTTER_GEMS"] = "宝石", + ["YRV3_OFFICE_CLUTTER_FURCOATS"] = "毛皮大衣", + ["YRV3_OFFICE_CLUTTER_ELECTRONICS"] = "电子产品", + ["YRV3_OFFICE_CLUTTER_JEWEL"] = "珠宝", + ["YRV3_OFFICE_CLUTTER_DRUGSTATUES"] = "毒品雕像", + ["YRV3_OFFICE_CLUTTER_COUNTERFEIT"] = "假冒商品", + ["YRV3_OFFICE_CLUTTER_BOOZE"] = "酒精和香烟", + ["YRV3_OFFICE_CLUTTER_DRUGBAGS"] = "药袋", + ["YRV3_OFFICE_CLUTTER_CUNS"] = "武器", + ["YRV3_OFFICE_CLUTTER_ART"] = "艺术品" } diff --git a/SSV2/includes/lib/translations/zh-TW.lua b/SSV2/includes/lib/translations/zh-TW.lua index f0ccbf6f..376382c3 100644 --- a/SSV2/includes/lib/translations/zh-TW.lua +++ b/SSV2/includes/lib/translations/zh-TW.lua @@ -985,5 +985,24 @@ return { ["BSV2_ES_DRIVE_PROPERTY_TEXT"] = "點選某個屬性即可前往該屬性。", ["BSV2_ES_DRIVE_PROPERTY"] = "擁有的財產", ["GENERIC_RETRY"] = "重試", - ["YRV3_UNSAFE_FEATS_FSL_ON_TXT"] = "使用 FSL 時始終啟用不安全功能。" + ["YRV3_UNSAFE_FEATS_FSL_ON_TXT"] = "使用 FSL 時始終啟用不安全功能。", + ["YRV3_OFFICE_CLUTTER_PILLS"] = "藥丸", + ["YRV3_OFFICE_CLUTTER_AUTO_CLEAR"] = "自動清除", + ["YRV3_OFFICE_CLUTTER_JEWEL"] = "珠寶", + ["YRV3_OFFICE_CLUTTER_CASH"] = "現金", + ["YRV3_OFFICE_CLUTTER_SILVER"] = "銀", + ["YRV3_OFFICE_CLUTTER_CUNS"] = "武器", + ["YRV3_OFFICE_CLUTTER_IVORY"] = "象牙", + ["YRV3_OFFICE_CLUTTER_GEMS"] = "寶石", + ["YRV3_OFFICE_CLUTTER_MEDS"] = "藥物", + ["YRV3_OFFICE_CLUTTER_FURCOATS"] = "毛皮大衣", + ["YRV3_OFFICE_CLUTTER_LABEL"] = "辦公室雜亂", + ["YRV3_OFFICE_CLUTTER_ITEM_SELECT"] = "要清除的項目:", + ["YRV3_OFFICE_CLUTTER_ELECTRONICS"] = "電子產品", + ["YRV3_OFFICE_CLUTTER_AUTO_CLEAR_TT"] = "自動清除辦公室中所有選定的雜物。", + ["YRV3_OFFICE_CLUTTER_DRUGBAGS"] = "藥袋", + ["YRV3_OFFICE_CLUTTER_ART"] = "藝術品", + ["YRV3_OFFICE_CLUTTER_DRUGSTATUES"] = "毒品雕像", + ["YRV3_OFFICE_CLUTTER_BOOZE"] = "酒精和香煙", + ["YRV3_OFFICE_CLUTTER_COUNTERFEIT"] = "仿冒商品" } diff --git a/SSV2/includes/modules/Accessor.lua b/SSV2/includes/modules/Accessor.lua index 66cf3b3b..897bc8f2 100644 --- a/SSV2/includes/modules/Accessor.lua +++ b/SSV2/includes/modules/Accessor.lua @@ -120,7 +120,6 @@ function Accessor:At(offset, size) end table.insert(newPath, offset) - return Accessor.new(self.m_index, self.m_type, self.m_script, newPath) end @@ -309,7 +308,7 @@ ScriptGlobal = Class("ScriptGlobal", { parent = Accessor }) function ScriptGlobal.new(address) local instance = Accessor.new(address, Enums.eAccessorType.GLOBAL) ---@diagnostic disable: undefined-field - instance.__index.__type = ScriptGlobal.__type + instance.__index.__type = "ScriptGlobal" ---@diagnostic disable-next-line return setmetatable(instance, ScriptGlobal) end diff --git a/SSV2/includes/modules/Color.lua b/SSV2/includes/modules/Color.lua index 7607f5d4..52f6ff17 100644 --- a/SSV2/includes/modules/Color.lua +++ b/SSV2/includes/modules/Color.lua @@ -8,6 +8,7 @@ local DEBUG_TRACK_SOURCE = false +local _f = _F or string.format ---@enum eColorType local eColorType = { @@ -48,7 +49,7 @@ local NamedColors = { ---@field private m_type eColorType ---@field private m_source table? ---@field private m_named_colors table ----@field private __raw_ctor fun(self: Color, r: float, g: float, b: float, a: float): Color +---@field private __raw_ctor fun(r: float, g: float, b: float, a: float): Color ---@field public r float ---@field public g float ---@field public b float @@ -64,11 +65,11 @@ local NamedColors = { ---@overload fun(r: integer, g: integer, b: integer, a: integer): Color ---@overload fun(r: float, g: float, b: float, a: float): Color local Color = Callable("Color", { - t_data = { m_named_colors = NamedColors }, - ctor = function(t, ...) return t.new(...) end + ctor = function(t, ...) + return t.new(...) + end }) - ---@param n number ---@return integer local function clamp_byte(n) @@ -116,7 +117,7 @@ local function process_params(...) return r / 255, g / 255, b / 255, a / 255, eColorType.U32 elseif (t == "string") then local key = v:lower() - local named = Color.m_named_colors[key] + local named = NamedColors[key] if (named) then return named[1], named[2], named[3], named[4], eColorType.NAMED end @@ -146,23 +147,25 @@ end ---@param b float ---@param a float ---@return Color -function Color:__raw_ctor(r, g, b, a) +function Color.__raw_ctor(r, g, b, a) return setmetatable({ - r = r, - g = g, - b = b, - a = a, - m_type = eColorType.FLOAT - }, self) + r = r, + g = g, + b = b, + a = a, + m_type = eColorType.FLOAT, + m_named_colors = NamedColors + ---@diagnostic disable-next-line + }, Color) end -- Constant basic colors for easy access. -Color.BLACK = Color:__raw_ctor(0, 0, 0, 1) -Color.WHITE = Color:__raw_ctor(1, 1, 1, 1) -Color.RED = Color:__raw_ctor(1, 0, 0, 1) -Color.GREEN = Color:__raw_ctor(0, 1, 0, 1) -Color.BLUE = Color:__raw_ctor(0, 0, 1, 1) +Color.BLACK = Color.__raw_ctor(0, 0, 0, 1) +Color.WHITE = Color.__raw_ctor(1, 1, 1, 1) +Color.RED = Color.__raw_ctor(1, 0, 0, 1) +Color.GREEN = Color.__raw_ctor(0, 1, 0, 1) +Color.BLUE = Color.__raw_ctor(0, 0, 1, 1) ------------------------------------------- @@ -178,7 +181,7 @@ Color.BLUE = Color:__raw_ctor(0, 0, 1, 1) ---@return Color function Color.new(...) local r, g, b, a, t = process_params(...) - local instance = Color:__raw_ctor(r, g, b, a) + local instance = Color.__raw_ctor(r, g, b, a) instance.m_type = t if (DEBUG_TRACK_SOURCE) then @@ -204,18 +207,19 @@ end function Color:RegisterNamedColor(name, ...) name = name:lower() - if (Color.m_named_colors[name]) then - log.fwarning("[Color]: '%s' already exists.", name) + if (NamedColors[name]) then + log.fwarning("[Color]: A color with the name '%s' already exists.", name) return end local ok, r, g, b, a = pcall(process_params, ...) if (not ok) then - log.fwarning("[Color]: Failed to register named color '%s'. Invalid color argument(s)", name) + log.fwarning("[Color]: Failed to register color with name '%s'. Invalid argument(s)", name) return end - Color.m_named_colors[name] = { r, g, b, a } + NamedColors[name] = { r, g, b, a } + self.m_named_colors = NamedColors end -- Returns the color in **RGBA** format [0 - 255]. @@ -247,7 +251,7 @@ end ---@return string function Color:AsHex() local r, g, b, a = self:AsRGBA() - return string.format("#%02X%02X%02X%02X", r, g, b, a) + return _f("#%02X%02X%02X%02X", r, g, b, a) end ---@param normalize? boolean Normalize to `[0 .. 1]` @@ -266,7 +270,7 @@ end ---@param c Color color to mix with ---@param f float factor function Color:Mix(c, f) - return Color:__raw_ctor( + return Color.__raw_ctor( self.r + (c.r - self.r) * f, self.g + (c.g - self.g) * f, self.b + (c.b - self.b) * f, @@ -287,7 +291,7 @@ function Color:Darken(f, includeAlpha) a = math.clamp(a - f, 0, 1) end - return Color:__raw_ctor(r, g, b, a) + return Color.__raw_ctor(r, g, b, a) end -- Returns a new instance. Does not mutate. @@ -303,7 +307,7 @@ function Color:Brighten(f, includeAlpha) a = math.clamp(a + f, 0, 1) end - return Color:__raw_ctor(r, g, b, a) + return Color.__raw_ctor(r, g, b, a) end -- Returns the luminance of the color *(brightness)*. @@ -409,7 +413,7 @@ function Color.FromHSV(h, s, v, a) r, g, b = c, 0, x end - return Color:__raw_ctor(r + m, g + m, b + m, a) + return Color.__raw_ctor(r + m, g + m, b + m, a) end --------------------------------------------------------------------------------- @@ -425,7 +429,7 @@ end ---@param right Color ---@return Color function Color:__add(right) - return Color:__raw_ctor( + return Color.__raw_ctor( math.min(self.r + right.r, 1), math.min(self.g + right.g, 1), math.min(self.b + right.b, 1), @@ -436,7 +440,7 @@ end ---@param right Color ---@return Color function Color:__sub(right) - return Color:__raw_ctor( + return Color.__raw_ctor( math.max(self.r - right.r, 0), math.max(self.g - right.g, 0), math.max(self.b - right.b, 0), @@ -447,7 +451,7 @@ end ---@param right Color ---@return Color function Color:__mul(right) - return Color:__raw_ctor( + return Color.__raw_ctor( math.min(self.r * right.r, 1), math.min(self.g * right.g, 1), math.min(self.b * right.b, 1), @@ -458,7 +462,7 @@ end ---@param right Color ---@return Color function Color:__div(right) - return Color:__raw_ctor( + return Color.__raw_ctor( right.r == 0 and 0 or self.r / right.r, right.g == 0 and 0 or self.g / right.g, right.b == 0 and 0 or self.b / right.b, @@ -469,7 +473,7 @@ end function Color:__tostring() local r, g, b, a = self:AsRGBA() - return string.format( + return _f( "[Color] Float: %.3f %.3f %.3f %.3f | RGBA: %d %d %d %d | Hex: %s | U32: 0x%X", self.r, self.g, self.b, self.a, r, g, b, a, @@ -481,6 +485,7 @@ end ------------------------------------------------------------------------------------------ -- Helpers for `Serializer` to seamlessly parse a color object to JSON and reconstruct it. +---@return { __type: "color", r: float, g: float, b: float, a: float } function Color:serialize() return { __type = "color", r = self.r, g = self.g, b = self.b, a = self.a } end @@ -489,10 +494,10 @@ end function Color.deserialize(t) if (type(t) ~= "table" or t.__type ~= "color") then log.warning("[Color]: Deserialization failed: invalid data!") - return Color:__raw_ctor(0, 0, 0, 0) + return Color.__raw_ctor(0, 0, 0, 0) end - return Color:__raw_ctor(t.r, t.g, t.b, t.a) + return Color.__raw_ctor(t.r, t.g, t.b, t.a) end if (Serializer) then diff --git a/SSV2/includes/modules/LocalPlayer.lua b/SSV2/includes/modules/LocalPlayer.lua index 9ba7df11..9df6c8dc 100644 --- a/SSV2/includes/modules/LocalPlayer.lua +++ b/SSV2/includes/modules/LocalPlayer.lua @@ -33,6 +33,9 @@ local YRV3 = require("includes.features.online.yim_resupplier.YimResuppl ---@field private m_money_controller PlayerMoneyController ---@field private m_clipsets { movement?: string, strafe?: string, weapon?: string } ---@field protected m_internal CPed +---@field new nil +---@field Create nil +---@field Delete nil ---@field SetAsNoLongerNeeded nil LocalPlayer = Class("LocalPlayer", { parent = Player }) diff --git a/SSV2/includes/modules/Player.lua b/SSV2/includes/modules/Player.lua index da121f0d..836db63d 100644 --- a/SSV2/includes/modules/Player.lua +++ b/SSV2/includes/modules/Player.lua @@ -21,15 +21,15 @@ ---@field private m_pid ID PlayerID ---@field public Resolve fun(self: Player) : CPed ---@field public super fun(self: Player): Ped +---@field Create nil +---@field Delete nil +---@field SetAsNoLongerNeeded nil ---@overload fun(p0: ID | handle): Player Player = Class("Player", { parent = Ped }) ----@override Player.Create = nil ----@override Player.Delete = nil ----@override Player.SetAsNoLongerNeeded = nil diff --git a/SSV2/includes/services/GUI.lua b/SSV2/includes/services/GUI.lua index 7881e215..6fcdf29b 100644 --- a/SSV2/includes/services/GUI.lua +++ b/SSV2/includes/services/GUI.lua @@ -889,10 +889,11 @@ function GUI:Text(text, opts) end local has_wrap_pos = type(opts.wrap_pos) == "number" - local has_color = IsInstance(opts.color, Color) + local color = opts.color + local has_color = color ~= nil - if (has_color) then - local r, g, b, a = opts.color:AsFloat() + if (has_color) then ---@cast color Color + local r, g, b, a = color:AsFloat() ImGui.PushStyleColor(ImGuiCol.Text, r, g, b, opts.alpha or a or 1) end diff --git a/SSV2/includes/services/PlayerMoneyController.lua b/SSV2/includes/services/PlayerMoneyController.lua index a136b4c0..3b7d1e6a 100644 --- a/SSV2/includes/services/PlayerMoneyController.lua +++ b/SSV2/includes/services/PlayerMoneyController.lua @@ -92,20 +92,25 @@ end ---@return boolean function PlayerMoneyController:UseTransactionSystem(amount, operation) if (NETSHOPPING.NET_GAMESERVER_TRANSACTION_IN_PROGRESS()) then - log.warning("Another transaction is in progress.") + log.error("Another transaction is in progress.") return false end local success, p0, p1 = false, 0, false success, p0, p1 = NETSHOPPING.NET_GAMESERVER_GET_SESSION_STATE_AND_STATUS(p0, p1) if (not success or p0 ~= 8) then - log.warning("Transaction failed!") + log.error("Transaction failed!") return false end local funcs_t = TxnSysFuncs[operation] + if (not funcs_t) then + log.error("Unknown operation!") + return false + end + if (not funcs_t.transfer(stats.get_character_index(), amount)) then - log.warning("Transaction failed!") + log.error("Transaction failed!") return false end @@ -117,13 +122,14 @@ function PlayerMoneyController:UseTransactionSystem(amount, operation) end if (status ~= 3) then - log.warning("Transaction failed!") + log.error("Transaction failed!") return false end return NETSHOPPING.NET_GAMESERVER_TRANSFER_CASH_SET_TELEMETRY_NONCE_SEED() end +-- ### Must be called in a fiber. ---@param amount integer function PlayerMoneyController:Withdraw(amount) if (self.m_bank_balance <= 0) then @@ -131,10 +137,13 @@ function PlayerMoneyController:Withdraw(amount) return end - if (amount <= 0) then return end -- probably unnecessary + if (amount <= 0) then -- probably unnecessary + Notifier:ShowError("Withdraw", "Invalid amount!") + return + end if (NETWORK.NETWORK_IS_ACTIVITY_SESSION()) then - log.warning("Monetary operations are not available in activity sessions.") + Notifier:ShowError("Withdraw", "Monetary operations are not available in activity sessions.") return end @@ -146,13 +155,17 @@ function PlayerMoneyController:Withdraw(amount) MONEY.WITHDRAW_VC(amount) end +-- ### Must be called in a fiber. ---@param amount integer function PlayerMoneyController:Deposit(amount) amount = math.min(amount, self.m_wallet_balance) - if (amount <= 0) then return end -- probably unnecessary + if (amount <= 0) then -- probably unnecessary + Notifier:ShowError("Deposit", "Invalid amount!") + return + end if (NETWORK.NETWORK_IS_ACTIVITY_SESSION()) then - log.warning("Monetary operations are not available in activity sessions.") + Notifier:ShowError("Deposit", "Monetary operations are not available in activity sessions.") return end diff --git a/SSV2/includes/services/Serializer.lua b/SSV2/includes/services/Serializer.lua index 84290481..08ed61a8 100644 --- a/SSV2/includes/services/Serializer.lua +++ b/SSV2/includes/services/Serializer.lua @@ -7,12 +7,13 @@ -- * Provide a copy of or a link to the original license (GPL-3.0 or later); see LICENSE.md or . -local Set = require("includes.classes.Set") -local JSON = require("includes.thirdparty.json.json")() -local B64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" -local XOR_KEY = "\xA3\x4F\xD2\x9B\x7E\xC1\xE8\x36\x5D\x0A\xF7\xB4\x6C\x2D\x89\x50\x1E\x73\xC9\xAF\x3B\x92\x58\xE0\x14\x7D\xA6\xCB\x81\x3F\xD5\x67" +local Set = require("includes.classes.Set") +local JSON = require("includes.thirdparty.json.json")() +local B64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" +local PUBLIC_XOR_KEY = "\xA3\x4F\xD2\x9B\x7E\xC1\xE8\x36\x5D\x0A\xF7\xB4\x6C\x2D\x89\x50\x1E\x73\xC9\xAF\x3B\x92\x58\xE0\x14\x7D\xA6\xCB\x81\x3F\xD5\x67" assert(JSON.VERSION == "20211016.28", "Bad Json package version.") + ---@enum eSerializerState local eSerializerState = { INIT = -1, @@ -76,41 +77,29 @@ function Serializer:init() if (self.m_initialized) then return self end if (_G.Serializer) then return _G.Serializer end - local instance = setmetatable({ - m_state = eSerializerState.INIT, - m_initialized = true, - m_setup = false, - m_dirty = false, - m_locked = false, - m_disabled = false, - m_registered_types = {}, - m_deferred_objects = {}, - m_moved_keys = { -- We gave these 2 updates to migrate. Should remove them in the next one - { path = "features.yim_actions.action_commands", file_name = "action_commands.json" }, - { path = "features.entity_forge.favorites", file_name = "forge_favorites.json" }, - { path = "features.entity_forge.forged_entities", file_name = "forged_entities.json" }, - }, - m_deprecated_keys = { -- same here - "features.bsv2", - "features.entity_forge", - }, - m_renamed_keys = { - { old_path = "features.yrv3.safe_loop_warn_ack", new_path = "features.unsafe_feats_enabled" } -- remove after 3 updates (current v1.9.4) - }, - m_lock_queue = {}, - m_key_states = {}, - ---@diagnostic disable-next-line - }, Serializer) + self.m_state = eSerializerState.INIT + self.m_initialized = true + self.m_setup = false + self.m_dirty = false + self.m_locked = false + self.m_disabled = false + self.m_registered_types = {} + self.m_deferred_objects = {} + self.m_lock_queue = {} + self.m_key_states = {} + self.m_moved_keys = {} + self.m_deprecated_keys = {} + self.m_renamed_keys = {} ThreadManager:RegisterLooped("SS_SERIALIZER", function() - instance:OnTick() + self:OnTick() end) Backend:RegisterEventCallback(Enums.eBackendEvent.RELOAD_UNLOAD, function() - instance:OnShutdown() + self:OnShutdown() end) - return instance + return self end ---@param script_name? string @@ -131,7 +120,7 @@ function Serializer:Setup(script_name, default_config, runtime_vars, varargs) self.m_default_config = default_config or { __version = Backend and Backend.__version or self.__version } self.m_file_name = _F("%s.json", filename) self.m_backup_file = _F("%s.bak", filename) - self.m_xor_key = varargs.encryption_key or XOR_KEY + self.m_xor_key = varargs.encryption_key or PUBLIC_XOR_KEY self.m_parsing_options = { pretty = varargs.pretty, indent = string.rep(" ", varargs.indent or 4), @@ -144,7 +133,7 @@ function Serializer:Setup(script_name, default_config, runtime_vars, varargs) local config_data = self:Read() if (type(config_data) ~= "table") then - log.warning("[Serializer]: Failed to read data! Persistent config will be disabled for this session.") + log.error("[Serializer]: Failed to read data! Persistent config will be disabled for this session.") self.m_disabled = true self.m_state = eSerializerState.SUSPENDED return self @@ -155,41 +144,39 @@ function Serializer:Setup(script_name, default_config, runtime_vars, varargs) _ENV.GVars = runtime_vars end - setmetatable( - runtime_vars, - { - __index = function(_, k) - local value = self.m_key_states[k] - if (value ~= nil) then - return value - end + setmetatable(runtime_vars, { + __index = function(_, k) + local value = self.m_key_states[k] + if (value ~= nil) then + return value + end - local default = self.m_default_config[value] - value = config_data[k] - if (value ~= nil) then - self.m_key_states[k] = value - self.m_dirty = true - return value - elseif (default ~= nil) then - self.m_key_states[k] = default - self.m_dirty = true - end + local default = self.m_default_config[value] + value = config_data[k] + if (value ~= nil) then + self.m_key_states[k] = value + self.m_dirty = true + return value + elseif (default ~= nil) then + self.m_key_states[k] = default + self.m_dirty = true + end - return self.m_key_states[k] - end, - __newindex = function(_, k, v) - if (self.m_default_config[k] == nil) then - local value = config_data[k] ~= nil and config_data[k] or v - self.m_key_states[k] = value - end + return self.m_key_states[k] + end, - if (self.m_key_states[k] ~= v) then - self.m_key_states[k] = v - self.m_dirty = true - end + __newindex = function(_, k, v) + if (self.m_default_config[k] == nil) then + local value = config_data[k] ~= nil and config_data[k] or v + self.m_key_states[k] = value end - } - ) + + if (self.m_key_states[k] ~= v) then + self.m_key_states[k] = v + self.m_dirty = true + end + end + }) for key, default_value in pairs(self.m_default_config) do local saved_value = config_data[key] @@ -334,12 +321,12 @@ end ---@return milliseconds function Serializer:GetLastWriteTime() - return self.m_last_write_time and self.m_last_write_time:Value() or 0 + return self.m_last_write_time:Value() end ---@return milliseconds function Serializer:GetTimeSinceLastFlush() - return self.m_last_write_time and self.m_last_write_time:Elapsed() or 0 + return self.m_last_write_time:Elapsed() end ---@return Config @@ -396,6 +383,7 @@ function Serializer:WithLock(fun) end ---@param value any +---@param seen? table ---@return any function Serializer:Preprocess(value, seen) seen = seen or {} From fb3a5fe6082b6b71cfc61f869b16143cf0644cf1 Mon Sep 17 00:00:00 2001 From: SAMURAI <66764345+xesdoog@users.noreply.github.com> Date: Tue, 23 Jun 2026 00:28:36 +0100 Subject: [PATCH 2/4] Update Office.lua --- .../features/online/yim_resupplier/businesses/Office.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/SSV2/includes/features/online/yim_resupplier/businesses/Office.lua b/SSV2/includes/features/online/yim_resupplier/businesses/Office.lua index fcee964d..61fac196 100644 --- a/SSV2/includes/features/online/yim_resupplier/businesses/Office.lua +++ b/SSV2/includes/features/online/yim_resupplier/businesses/Office.lua @@ -340,7 +340,9 @@ function Office:RemoveClutter(clutter_t) end if (name == "cash") then - shouldRefresh = remove_office_cash_clutter_set(interior) + if (remove_office_cash_clutter_set(interior)) then + shouldRefresh = true + end elseif (remove_office_clutter_set(interior, name)) then shouldRefresh = true end From 47384d2cf3e620cf2206925f22f32e2c9f9ecf4d Mon Sep 17 00:00:00 2001 From: SAMURAI <66764345+xesdoog@users.noreply.github.com> Date: Tue, 23 Jun 2026 00:45:33 +0100 Subject: [PATCH 3/4] chore(dev): sync YimLLSX --- dev/YimLLSX | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/YimLLSX b/dev/YimLLSX index 3028240d..b1e5304c 160000 --- a/dev/YimLLSX +++ b/dev/YimLLSX @@ -1 +1 @@ -Subproject commit 3028240d0eaa0fc75038ae0c127ce5e9dfa9430b +Subproject commit b1e5304c2c7805aaf93eebeafbf0cf0e42fe8433 From dba0f06f822e39722b5d353df50ba8d41f208706 Mon Sep 17 00:00:00 2001 From: SAMURAI <66764345+xesdoog@users.noreply.github.com> Date: Thu, 25 Jun 2026 20:47:59 +0100 Subject: [PATCH 4/4] feat(vehicle): add slider for fast vehciels - Add a slider for Fast Vehicles. - Add a slider for Fast Planes. closes #143 closes #144 --- SSV2/includes/data/config.lua | 2 ++ SSV2/includes/features/Speedometer.lua | 19 ++++++---- .../online/yim_resupplier/YimResupplierV3.lua | 9 ++++- .../features/vehicle/misc_vehicle.lua | 36 +++++++++---------- .../includes/frontend/vehicle/aircraft_ui.lua | 15 ++++++-- SSV2/includes/frontend/vehicle/vehicle_ui.lua | 15 +++++++- SSV2/includes/modules/Entity.lua | 7 +++- SSV2/includes/modules/PlayerVehicle.lua | 33 +++++++++-------- 8 files changed, 90 insertions(+), 46 deletions(-) diff --git a/SSV2/includes/data/config.lua b/SSV2/includes/data/config.lua index 520cf8c3..4af9e541 100644 --- a/SSV2/includes/data/config.lua +++ b/SSV2/includes/data/config.lua @@ -185,6 +185,7 @@ local Config = { subwoofer = false, horn_beams = false, fast_vehicles = false, + fast_vehicles_speed = 10, auto_brake_lights = false, iv_exit = false, no_wheel_recenter = false, @@ -205,6 +206,7 @@ local Config = { auto_lock_doors = false, cobra_maneuver = false, fast_jets = false, + fast_jets_speed = 150.0, no_jet_stall = false, no_turbulence = false, aircraft_mg = { diff --git a/SSV2/includes/features/Speedometer.lua b/SSV2/includes/features/Speedometer.lua index 3173f0e7..5c526472 100644 --- a/SSV2/includes/features/Speedometer.lua +++ b/SSV2/includes/features/Speedometer.lua @@ -9,6 +9,7 @@ local gui_registered = false local last_engine_state = true +local last_captured_top_spd = 0.0 local RUNTIME_COLORS = { INDICATOR_RED = Color(1.0, 0.215, 0.215, 1.0):AsU32(), INDICATOR_GREEN = Color(0.1, 0.91, 0.0, 1.0):AsU32(), @@ -88,21 +89,25 @@ local Speedometer = { }; Speedometer.__index = Speedometer ---@public -function Speedometer:UpdateState() +---@param PV PlayerVehicle +function Speedometer:UpdateState(PV) local state = self._state - local PV = LocalPlayer:GetVehicle() if not (PV and PV:IsValid()) then state.should_draw = false return end - local speed_modifier = Match(GVars.features.speedometer.speed_unit, UNIT_MULTIPLIERS) - local fast_vehicles = GVars.features.vehicle.fast_vehicles - local IsEngineOn = PV:IsEngineOn() + local speed_modifier = Match(GVars.features.speedometer.speed_unit, UNIT_MULTIPLIERS) + local IsEngineOn = PV:IsEngineOn() + local IsNosActive = PV:IsNOSActive() + if (not IsNosActive) then + last_captured_top_spd = PV:GetMaxSpeed() + end + state.PV = PV state.speed_modifier = speed_modifier state.CurrentSpeed = PV:GetSpeed() * speed_modifier - state.MaxSpeed = math.floor((PV:GetDefaultMaxSpeed() * (fast_vehicles and 1.8 or 1)) * speed_modifier) + state.MaxSpeed = math.floor(last_captured_top_spd * speed_modifier) state.CurrentAltitude = PV:GetHeightAboveGround() state.Manufacturer = PV:GetManufacturerName() state.IsEngineOn = IsEngineOn @@ -111,7 +116,7 @@ function Speedometer:UpdateState() state.IsABSEngaged = PV:IsABSEngaged() state.IsESCEngaged = PV:IsESCEngaged() state.IsESCDisabled = get_is_tc_esc_dsabled(PV) - state.IsNOSActive = PV:IsNOSActive() + state.IsNOSActive = IsNosActive state.IsAircraft = PV:IsPlane() or PV:IsHeli() state.IsSports = PV:IsSportsOrSuper() state.IsShootingFlares = PV.m_is_shooting_flares diff --git a/SSV2/includes/features/online/yim_resupplier/YimResupplierV3.lua b/SSV2/includes/features/online/yim_resupplier/YimResupplierV3.lua index 9bf0b1ad..b1feab36 100644 --- a/SSV2/includes/features/online/yim_resupplier/YimResupplierV3.lua +++ b/SSV2/includes/features/online/yim_resupplier/YimResupplierV3.lua @@ -130,7 +130,6 @@ function YRV3:Reset(disable, reason) self.m_last_autosell_check_time = 0 self.m_last_income_check_time = 0 self.m_last_business_update_time = 0 - self.m_businesses = { safes = {} } self.m_boss_types_avail = { { name = "GB_BOSS" --[[VIP]], id = 0 } } self.m_sell_script_running = false self.m_initial_data_done = false @@ -138,6 +137,14 @@ function YRV3:Reset(disable, reason) self.m_autosell_state = Enums.eAutoSellState.NONE self.m_state = disable and Enums.eYRState.ERROR or Enums.eYRState.IDLE + for _, business in self:BusinessIter() do + local reset = business.Reset + if (reset) then + ---@diagnostic disable-next-line + reset(business) + end + end; self.m_businesses = { safes = {} } + if (disable) then self:SetLastError(reason or "Unhandled Exception.") end diff --git a/SSV2/includes/features/vehicle/misc_vehicle.lua b/SSV2/includes/features/vehicle/misc_vehicle.lua index 06b4ee30..913c855b 100644 --- a/SSV2/includes/features/vehicle/misc_vehicle.lua +++ b/SSV2/includes/features/vehicle/misc_vehicle.lua @@ -109,8 +109,8 @@ function MiscVehicle:ShootExplosiveMG(enemiesOnly, targetEntity, startPos, endPo end function MiscVehicle:UpdateMachineGuns() - if (not GVars.features.vehicle.aircraft_mg.triggerbot - and not GVars.features.vehicle.aircraft_mg.manual_aim) then + local cfg = GVars.features.vehicle.aircraft_mg + if not (cfg.triggerbot or cfg.manual_aim) then return end @@ -120,8 +120,8 @@ function MiscVehicle:UpdateMachineGuns() local PV = self.m_entity local handle = PV:GetHandle() - local manualAim = GVars.features.vehicle.aircraft_mg.manual_aim - local triggerbotRange = GVars.features.vehicle.aircraft_mg.tiggerbot_range + local manualAim = cfg.manual_aim + local triggerbotRange = cfg.tiggerbot_range local playerPos = LocalPlayer:GetPos() local rotation = manualAim and CAM.GET_GAMEPLAY_CAM_ROT(2) or PV:GetRotation(2) local direction = rotation:to_direction() @@ -129,8 +129,8 @@ function MiscVehicle:UpdateMachineGuns() local destination = playerPos + direction * multiplier local hit, endCoords, entityHit = World:RayCast(playerPos, destination, -1, handle) - if (hit and GVars.features.vehicle.aircraft_mg.triggerbot and (ENTITY.IS_ENTITY_A_PED(entityHit) or ENTITY.IS_ENTITY_A_VEHICLE(entityHit))) then - local enemiesOnly = GVars.features.vehicle.aircraft_mg.enemies_only + if (hit and cfg.triggerbot and (ENTITY.IS_ENTITY_A_PED(entityHit) or ENTITY.IS_ENTITY_A_VEHICLE(entityHit))) then + local enemiesOnly = cfg.enemies_only local ped = Game.GetClosestPed(endCoords, 50, true) local veh = Game.GetClosestVehicle(endCoords, 50, handle) @@ -150,8 +150,8 @@ function MiscVehicle:UpdateMachineGuns() self:ShootExplosiveMG(false, 0, playerPos, endPos) end - local color = GVars.features.vehicle.aircraft_mg.marker_color - local markerSize = GVars.features.vehicle.aircraft_mg.marker_size + local color = cfg.marker_color + local markerSize = cfg.marker_size local markerDest = hit and endCoords or vec3:new( playerPos.x + direction.x * 50, playerPos.y + direction.y * 50, @@ -206,17 +206,18 @@ function MiscVehicle:DisableAirTurbulence() end function MiscVehicle:Update() - local PV = self.m_entity + local PV = self.m_entity local handle = PV:GetHandle() + local cfg = GVars.features.vehicle - if (GVars.features.vehicle.fast_jets and PV:IsPlane() and (VEHICLE.GET_VEHICLE_FLIGHT_NOZZLE_POSITION(handle) ~= 1.0)) then + if (cfg.fast_jets and PV:IsPlane() and (VEHICLE.GET_VEHICLE_FLIGHT_NOZZLE_POSITION(handle) ~= 1.0)) then local speed = PV:GetSpeed() local gearState = PV:GetLandingGearState() local rot = PV:GetRotation(2) local pitch = rot.x - local baseThrust = 2e4 - local minThrust = 5e3 - local maxSpeed = 164.0 + local baseThrust = 25e3 + local minThrust = 1e4 + local maxSpeed = cfg.fast_jets_speed or 150.0 local thrustMult = 1.0 if (pitch >= 60) then @@ -225,10 +226,7 @@ function MiscVehicle:Update() thrustMult = 1.4 end - if speed >= 72 and speed < maxSpeed - and PAD.IS_CONTROL_PRESSED(0, 87) - and gearState == Enums.eLandingGearState.RETRACTED - then + if (speed >= 70 and speed < maxSpeed and PAD.IS_CONTROL_PRESSED(0, 87) and gearState == Enums.eLandingGearState.RETRACTED) then local lerp = math.min(1.0, (speed) / (maxSpeed)) local thrust = math.min(minThrust, baseThrust * thrustMult * (1.0 - lerp)) ENTITY.APPLY_FORCE_TO_ENTITY_CENTER_OF_MASS( @@ -245,13 +243,13 @@ function MiscVehicle:Update() end end - if (GVars.features.vehicle.no_jet_stall) then + if (cfg.no_jet_stall) then if (PV:IsDriveable() and PV:GetEngineHealth() > 350 and PV:GetHeightAboveGround() > 5.0 and not PV:IsEngineOn()) then VEHICLE.SET_VEHICLE_ENGINE_ON(handle, true, true, false) end end - if (GVars.features.vehicle.no_turbulence) then + if (cfg.no_turbulence) then self:DisableAirTurbulence() end diff --git a/SSV2/includes/frontend/vehicle/aircraft_ui.lua b/SSV2/includes/frontend/vehicle/aircraft_ui.lua index a1b2e015..742a29bb 100644 --- a/SSV2/includes/frontend/vehicle/aircraft_ui.lua +++ b/SSV2/includes/frontend/vehicle/aircraft_ui.lua @@ -8,11 +8,11 @@ local flrs = require("includes.features.vehicle.flares") +Flares = LocalPlayer:GetVehicle():AddFeature(flrs) local autopilot_state_idx = 0 local autopilot_index_changed = false local autopilot_labels local planes_tab = GUI:RegisterNewTab(Enums.eTabID.TAB_VEHICLE, "SUBTAB_AIRCRAFT", nil, nil, true) -Flares = LocalPlayer:GetVehicle():AddFeature(flrs) local optionPopup = { flags = ImGuiWindowFlags.NoResize | ImGuiWindowFlags.AlwaysAutoResize, label = "##optionsPopup", @@ -50,7 +50,18 @@ planes_tab:AddBoolCommand("VEH_FAST_JETS", gvar_key = "features.vehicle.fast_jets", translate_label = true, meta = { description = "VEH_FAST_JETS_TT", isTranslatorLabel = true }, - register_command = true + register_command = true, + options_data = { + condition = function() return GVars.features.vehicle.fast_jets end, + callback = function() + optionPopup.callback = function() + local cfg = GVars.features.vehicle + cfg.fast_jets_speed = ImGui.SliderFloat("##speed", cfg.fast_jets_speed, 100.0, 300.0) + end + optionPopup.label = _T("VEH_FAST_JETS") + optionPopup.should_draw = true + end + } } ) planes_tab:AddBoolCommand("VEH_NO_JET_STALL", diff --git a/SSV2/includes/frontend/vehicle/vehicle_ui.lua b/SSV2/includes/frontend/vehicle/vehicle_ui.lua index f4009407..a70a2ce7 100644 --- a/SSV2/includes/frontend/vehicle/vehicle_ui.lua +++ b/SSV2/includes/frontend/vehicle/vehicle_ui.lua @@ -281,7 +281,20 @@ vehicleTab:AddBoolCommand("VEH_FAST_AF", { gvar_key = "features.vehicle.fast_vehicles", meta = { description = "VEH_FAST_AF_TT" }, - translate_label = true + translate_label = true, + options_data = { + condition = function() + return GVars.features.vehicle.fast_vehicles + end, + callback = function() + optionPopup.callback = function() + local cfg = GVars.features.vehicle + cfg.fast_vehicles_speed = ImGui.SliderFloat("##speed", cfg.fast_vehicles_speed, 10.0, 100.0) + end + optionPopup.label = _T("VEH_FAST_AF") + optionPopup.should_draw = true + end + } } ) vehicleTab:AddBoolCommand("VEH_NOS", diff --git a/SSV2/includes/modules/Entity.lua b/SSV2/includes/modules/Entity.lua index 039ab5e0..c9058db2 100644 --- a/SSV2/includes/modules/Entity.lua +++ b/SSV2/includes/modules/Entity.lua @@ -346,7 +346,12 @@ end ---@return boolean function Entity:IsOutside() - return self:GetInteriorID() == 0 + local interiorID = self:GetInteriorID() + if (interiorID == 0) then + return true + end + local groupID = INTERIOR.GET_INTERIOR_GROUP_ID(interiorID) + return groupID > 0 end ---@return joaat_t diff --git a/SSV2/includes/modules/PlayerVehicle.lua b/SSV2/includes/modules/PlayerVehicle.lua index 82d831fa..276d0310 100644 --- a/SSV2/includes/modules/PlayerVehicle.lua +++ b/SSV2/includes/modules/PlayerVehicle.lua @@ -48,6 +48,7 @@ local ManualGearbox = require("includes.features.vehicle.manual_gearbox") ---@field private m_threads array ---@field private m_default_max_speed float ---@field private m_has_loud_radio boolean +---@field private m_top_speed_modified boolean ---@field private m_generic_toggleables table ---@field public m_default_xenon_lights { enabled: boolean, index: integer } ---@field public m_default_tire_smoke { enabled: boolean, color: vec3 } @@ -193,19 +194,19 @@ function PlayerVehicle:Set(handle) self.m_autopilot.eligible = self:IsAircraft() - local feats_cfg = GVars.features.vehicle - if (feats_cfg.no_turbulence and VEHICLE.IS_THIS_MODEL_A_PLANE(new_model)) then + local cfg = GVars.features.vehicle + if (cfg.no_turbulence and VEHICLE.IS_THIS_MODEL_A_PLANE(new_model)) then VEHICLE.SET_PLANE_TURBULENCE_MULTIPLIER(handle, 0.0) end - if (feats_cfg.default_station.enabled) then - self:SetRadioStation(feats_cfg.default_station.station_name) + if (cfg.default_station.enabled) then + self:SetRadioStation(cfg.default_station.station_name) end self.m_flag_controller:ApplyPresets() self.m_stancer:OnNewVehicle() - if (feats_cfg.manual_gearbox.enabled) then + if (cfg.manual_gearbox.enabled) then self.m_manual_gearbox:OnNewVehicle() end -- self:ResumeThreads() @@ -607,42 +608,44 @@ function PlayerVehicle:Main() return end + local cfg = GVars.features.vehicle local handle = self:GetHandle() - if (GVars.features.vehicle.fast_vehicles) then - if (self:GetMaxSpeed() <= 80) then - VEHICLE.MODIFY_VEHICLE_TOP_SPEED(handle, 100) - end - elseif (self:GetMaxSpeed() >= 80) then + if (cfg.fast_vehicles) then + VEHICLE.MODIFY_VEHICLE_TOP_SPEED(handle, cfg.fast_vehicles_speed or 10) + self.m_top_speed_modified = true + elseif (self.m_top_speed_modified) then VEHICLE.MODIFY_VEHICLE_TOP_SPEED(handle, -1) + self.m_top_speed_modified = false end - if (GVars.features.vehicle.subwoofer and (not self:HasLoudRadio() or not self.m_generic_toggleables["subwoofer"])) then + if (cfg.subwoofer and (not self:HasLoudRadio() or not self.m_generic_toggleables["subwoofer"])) then self:AddGenericToggleable("subwoofer", function() self:ToggleSubwoofer(true) end, self.ToggleSubwoofer, { self, false }) end - if (GVars.features.vehicle.unbreakable_windows) then + if (cfg.unbreakable_windows) then local native = VEHICLE.SET_DONT_PROCESS_VEHICLE_GLASS self:AddGenericToggleable("strongwindows", function() native(handle, true) end, native, { handle, false }) end - if (GVars.features.vehicle.auto_brake_lights and LocalPlayer:IsDriving()) then + if (cfg.auto_brake_lights and LocalPlayer:IsDriving()) then if (self:IsDriveable() and self:IsEngineOn() and not self:IsMoving() and not PAD.IS_CONTROL_PRESSED(0, 72)) then VEHICLE.SET_VEHICLE_BRAKE_LIGHTS(handle, true) end end - self.m_is_flatbed = self:IsFlatbedTruck() + self.m_is_flatbed = self:IsFlatbedTruck() self.m_engine_swap_compatible = self:IsCar() + self.m_feat_mgr:Update() self:UpdateESC() self:AutoLock() self:HandleAutopilot() - Speedometer:UpdateState() + Speedometer:UpdateState(self) end -- Just for convenience so we don't have to remember patch names.