diff --git a/.gitmodules b/.gitmodules index af79585d..2f46cb5f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -40,7 +40,7 @@ [submodule "deps/protobuf"] path = deps/protobuf url = https://github.com/protocolbuffers/protobuf.git - branch = 3.17.x + branch = 3.20.x [submodule "deps/zlib"] path = deps/zlib url = https://github.com/madler/zlib.git diff --git a/data/ui_scripts/custom_depot/__init__.lua b/data/ui_scripts/custom_depot/__init__.lua new file mode 100644 index 00000000..b140e2ef --- /dev/null +++ b/data/ui_scripts/custom_depot/__init__.lua @@ -0,0 +1,146 @@ +if game:issingleplayer() then + return +end + +-- from mpdepotbase.lua, global definition isn't working +InventoryCurrencyType = { + LaunchCredits = 1, + Credits = 2, + Parts = 3, + CoDPoints = 4, + Bonus = 5, + Max = 6 +} + +ItemRarity = { + Common = 0, + Rare = 1, + Legendary = 2, + Epic = 3 +} + +custom_depot = { + collection_details_menu = nil, + data = { + currencies = { + launchCredits = 0, -- LaunchCredits + credits = 0, -- Credits + parts = 0, -- Parts + codPoints = 0, -- CoDPoints + bonus = 0 -- Bonus + }, + items = {}, + reward_splashes = {}, + has_accepted_mod_eula = false, + has_seen_mod_eula = false + }, + directory_path = "players2/user", + file_name = "depot.json", + file_path = nil, + functions = {} +} + +custom_depot.file_path = string.format("%s/%s", custom_depot.directory_path, custom_depot.file_name) + +custom_depot.get_function = function(function_name) + if not function_name or not custom_depot.functions[function_name] then + return nil + end + + return custom_depot.functions[function_name] +end + +custom_depot.functions["save_depot_data"] = function() + io.writefile(custom_depot.file_path, json.encode(custom_depot.data), false) +end + +custom_depot.functions["load_depot_data"] = function() + if not io.directoryexists(custom_depot.directory_path) then + io.createdirectory(custom_depot.directory_path) + end + + if not io.fileexists(custom_depot.file_path) then + custom_depot.get_function("save_depot_data")() + end + + custom_depot.data = json.decode(io.readfile(custom_depot.file_path)) +end + +local function convert_currency_to_string(type) + if type == InventoryCurrencyType.LaunchCredits then + return "launchCredits" + elseif type == InventoryCurrencyType.Credits then + return "credits" + elseif type == InventoryCurrencyType.Parts then + return "parts" + elseif type == InventoryCurrencyType.CoDPoints then + return "codPoints" + elseif type == InventoryCurrencyType.Bonus then + return "bonus" + end +end + +custom_depot.functions["add_currency"] = function(currency_type, amount) + local type = convert_currency_to_string(currency_type) + custom_depot.data.currencies[type] = custom_depot.data.currencies[type] + amount +end + +custom_depot.functions["remove_currency"] = function(currency_type, amount) + local type = convert_currency_to_string(currency_type) + custom_depot.data.currencies[type] = custom_depot.data.currencies[type] - amount +end + +custom_depot.functions["get_currency"] = function(currency_type) + local type = convert_currency_to_string(currency_type) + + if not currency_type or not custom_depot.data.currencies[type] then + return nil + end + + return custom_depot.data.currencies[type] +end + +custom_depot.functions["add_item"] = function(item, value) + custom_depot.data.items[item] = value +end + +custom_depot.functions["has_item"] = function(item) + return custom_depot.data.items[item] ~= nil +end + +custom_depot.functions["add_reward_splash"] = function(item, value) + custom_depot.data.reward_splashes[item] = value +end + +custom_depot.functions["has_reward_splash"] = function(item) + return custom_depot.data.reward_splashes[item] ~= nil +end + +custom_depot.functions["has_accepted_mod_eula"] = function() + return custom_depot.data.has_accepted_mod_eula +end + +custom_depot.functions["set_has_accepted_mod_eula"] = function(value) + custom_depot.data.has_accepted_mod_eula = value + custom_depot.get_function("save_depot_data")() +end + +custom_depot.functions["has_seen_mod_eula"] = function() + return custom_depot.data.has_seen_mod_eula +end + +custom_depot.functions["set_has_seen_mod_eula"] = function(value) + custom_depot.data.has_seen_mod_eula = value + custom_depot.get_function("save_depot_data")() +end + +custom_depot.get_function("load_depot_data")() + +if Engine.InFrontend() then + require("mod_eula") + require("depot_override") +end + +if not Engine.InFrontend() then + require("scoreboard_override") +end diff --git a/data/ui_scripts/custom_depot/depot_override.lua b/data/ui_scripts/custom_depot/depot_override.lua new file mode 100644 index 00000000..886431c1 --- /dev/null +++ b/data/ui_scripts/custom_depot/depot_override.lua @@ -0,0 +1,332 @@ +GetCurrencyBalance = function(currency_type) + return custom_depot.get_function("get_currency")(currency_type) +end + +Inventory_PurchaseItem_orig = Engine.Inventory_PurchaseItem +Engine.Inventory_PurchaseItem = function(controller, item_guid, unk2) + if not custom_depot.get_function("has_item")(item_guid) then + custom_depot.get_function("add_item")(item_guid, true) + + local item_value = Engine.TableLookup(LootTable.File, LootTable.Cols.GUID, item_guid, LootTable.Cols.Value) + custom_depot.get_function("remove_currency")(InventoryCurrencyType.Parts, item_value) + + custom_depot.get_function("save_depot_data")() + + if custom_depot.collection_details_menu then + custom_depot.collection_details_menu:OnCraftedItem() + end + end + + return Inventory_PurchaseItem_orig(controller, item_guid, unk2) +end + +GetItemLockState_orig = Engine.GetItemLockState +Engine.GetItemLockState = function(controller, item_guid) + if custom_depot.get_function("has_item")(item_guid) then + return "Unlocked", 0, "" + end + + return GetItemLockState_orig(controller, item_guid) +end + +GetItemSet_orig = GetItemSet +GetItemSet = function(item_set_id) + local item_set = GetItemSet_orig(item_set_id) + local items_unlocked = 0 + + for k, v in pairs(item_set.setItems) do + if custom_depot.get_function("has_item")(v.guid) and (not v.isOwned or v.lockState == "Unlocked") then + v.isOwned = true + v.lockState = "Unlocked" + items_unlocked = items_unlocked + 1 + end + end + + if items_unlocked == #item_set.setItems then + if not item_set.completed then + item_set.completed = true + end + + if not custom_depot.get_function("has_item")(item_set.setReward.guid) then + custom_depot.get_function("add_item")(item_set.setReward.guid, true) + custom_depot.get_function("save_depot_data")() + + if custom_depot.collection_details_menu then + custom_depot.collection_details_menu:OnCompletedSet() + end + end + end + + item_set.numOwned = items_unlocked + return item_set +end + +GetItemSets_orig = GetItemSets +GetItemSets = function() + local item_sets = GetItemSets_orig() + local completed_sets = 0 + + for i = 1, #item_sets.seasons do + local seasons_completed_sets = 0 + local sets = item_sets.seasons[i].sets + local rewardData = item_sets.seasons[i].rewardData + + for i = 1, #sets do + if sets[i].completed then + completed_sets = completed_sets + 1 + seasons_completed_sets = seasons_completed_sets + 1 + end + end + + if item_sets.seasons[i].completedSets == #sets then + rewardData.setReward.isOwned = true + rewardData.setReward.lockState = "Unlocked" + rewardData.completed = true + + if not custom_depot.get_function("has_item")(rewardData.setReward.guid) then + custom_depot.get_function("add_item")(rewardData.setReward.guid, true) + custom_depot.get_function("save_depot_data")() + end + end + + item_sets.seasons[i].completedSets = seasons_completed_sets + end + + for k, v in pairs(item_sets.itemToSetMap) do + local items_unlocked = 0 + + for i = 1, #v.setItems do + if custom_depot.get_function("has_item")(v.setItems[i].guid) and + (not v.setItems[i].isOwned or v.setItems[i].lockState == "Unlocked") then + v.setItems[i].isOwned = true + v.setItems[i].lockState = "Unlocked" + items_unlocked = items_unlocked + 1 + end + end + + if items_unlocked == #v.setItems then + if not v.completed then + v.completed = true + completed_sets = completed_sets + 1 + end + + if not custom_depot.get_function("has_item")(v.setReward.guid) then + custom_depot.get_function("add_item")(v.setReward.guid, true) + custom_depot.get_function("save_depot_data")() + end + end + + v.numOwned = items_unlocked + end + + item_sets.completedSets = completed_sets + return item_sets +end + +IsContentPromoUnlocked_orig = IsContentPromoUnlocked +IsContentPromoUnlocked = function() + return true +end + +TryShowCollectionCompleted_orig = TryShowCollectionCompleted +TryShowCollectionCompleted = function(controller, reward_data, unk1) + if reward_data.completed then + if not custom_depot.get_function("has_reward_splash")(reward_data.setReward.guid) then + LUI.FlowManager.RequestAddMenu(nil, "MPDepotCollectionRewardSplash", true, controller, false, { + collectionData = reward_data + }) + + if custom_depot.collection_details_menu then + custom_depot.collection_details_menu:OnCompletedSet() + end + + custom_depot.get_function("add_reward_splash")(reward_data.setReward.guid, true) + custom_depot.get_function("save_depot_data")() + end + + return true + else + return false + end +end + +TryShowSeasonCompleted_orig = TryShowSeasonCompleted +TryShowSeasonCompleted = function(controller, reward_data, unk1) + if reward_data.completed then + if not custom_depot.get_function("has_reward_splash")(reward_data.setReward.guid) then + LUI.FlowManager.RequestAddMenu(nil, "MPDepotCollectionRewardSplash", true, controller, false, { + collectionData = reward_data + }) + + if custom_depot.collection_details_menu then + custom_depot.collection_details_menu:OnCompletedSet() + end + + custom_depot.get_function("add_reward_splash")(reward_data.setReward.guid, true) + custom_depot.get_function("save_depot_data")() + end + + return true + else + return false + end +end + +MPDepotCollectionDetailsMenu_orig = LUI.MenuBuilder.m_types_build["MPDepotCollectionDetailsMenu"] +MPDepotCollectionDetailsMenu = function(unk1, unk2) + custom_depot.collection_details_menu = MPDepotCollectionDetailsMenu_orig(unk1, unk2) + return custom_depot.collection_details_menu +end +LUI.MenuBuilder.m_types_build["MPDepotCollectionDetailsMenu"] = MPDepotCollectionDetailsMenu + +MPDepotOpenLootMenu_orig = LUI.MenuBuilder.m_types_build["MPDepotOpenLootMenu"] +MPDepotOpenLootMenu = function(unk1, unk2) + local open_loot_menu = MPDepotOpenLootMenu_orig(unk1, unk2) + + local supply_drop_orig = open_loot_menu.m_eventHandlers["supply_drop"] + open_loot_menu:registerEventHandler("supply_drop", function(f48_arg0, f48_arg1) + f48_arg1.success = true + f48_arg1.transaction = f48_arg0.supplyDropTransaction + f48_arg1.duplicateRefund = false + f48_arg1.items = {} + f48_arg1.currencies = {} + f48_arg1.replacements = {} + f48_arg1.cards = {} + + local supply_drop_price = LUI.MPDepot.GetSupplyDropPrice(f48_arg0.supplyDropType) + custom_depot.get_function("remove_currency")(supply_drop_price.type, supply_drop_price.amount) + custom_depot.get_function("save_depot_data")() + + for i = 1, unk2.crateType:find("_basic") and math.random(1, 2) or math.random(2, 3) do + local items_list = LUI.MPLootDropsBase.GetGenericItemList(x, LUI.MPDepot.LootDropsData[LUI.MPDepot + .SuppyDropLootStream[unk2.crateType]].lootTableColName) + local random_item = items_list[math.random(#items_list)] + + while random_item.inventoryItemType ~= Cac.InventoryItemType.Loot do + random_item = items_list[math.random(#items_list)] + end + + if random_item then + f48_arg1.items[i] = random_item.guid + end + end + + for i = 1, #f48_arg1.items do + if not custom_depot.get_function("has_item")(f48_arg1.items[i]) then + custom_depot.get_function("add_item")(f48_arg1.items[i], true) + else + local item_rarity = tonumber(Engine.TableLookup(LootTable.File, LootTable.Cols.GUID, f48_arg1.items[i], + LootTable.Cols.Rarity)) + local dismantled_amount = 0 + + if item_rarity == ItemRarity.Common then + dismantled_amount = math.random(1, 75) + elseif item_rarity == ItemRarity.Rare then + dismantled_amount = math.random(75, 155) + elseif item_rarity == ItemRarity.Legendary then + dismantled_amount = math.random(155, 260) + elseif item_rarity == ItemRarity.Epic then + dismantled_amount = math.random(260, 550) + end + + table.insert(f48_arg1.replacements, { + item_index = i, + currency = { + amount = dismantled_amount + } + }) + + custom_depot.get_function("add_currency")(InventoryCurrencyType.Parts, dismantled_amount) + end + end + + custom_depot.get_function("save_depot_data")() + supply_drop_orig(f48_arg0, f48_arg1) + end) + + local slow_purchase_transfer_orig = open_loot_menu.m_eventHandlers["slow_purchase_transfer"] + open_loot_menu:registerEventHandler("slow_purchase_transfer", function(f33_arg0, f33_arg1) + local f33_local0 = 0 + if f33_arg0.slowPurchaseTimer then + f33_arg0.slowPurchaseTimer:close() + f33_arg0.slowPurchaseTimer = nil + end + local f33_local1 = CoD.CreateState(-500, 0, 500, 20, CoD.AnchorTypes.Top) + f33_local1.font = CoD.TextSettings.BodyFont.Font + f33_local1.verticalAlignment = LUI.VerticalAlignment.Top + f33_local1.horizontalAlignment = LUI.HorizontalAlignment.Center + f33_local1.color = Colors.mw1_green + f33_local1.alpha = 1 + f33_arg0.slowPurchaseText = LUI.UIText.new(f33_local1) + f33_arg0:addElement(f33_arg0.slowPurchaseText) + f33_arg0.slowPurchaseText:setText(Engine.Localize("@DEPOT_TRANSFER_IN_PROGRESS_DOT")) + f33_arg0.slowPurchaseText.textState = 1 + f33_arg0.slowPurchaseText:registerEventHandler("update_slow_purchase_text", f0_local30) + f33_arg0.slowPurchaseText:addElement(LUI.UITimer.new(1000, "update_slow_purchase_text")) + local f33_local2 = LUI.MenuBuilder.BuildRegisteredType("progressBar") + f33_local2:registerAnimationState("default", { + topAnchor = false, + bottomAnchor = true, + leftAnchor = false, + rightAnchor = false, + top = 0, + height = 40 + }) + f33_local2:animateToState("default") + f33_arg0.slowPurchaseText:addElement(f33_local2) + f33_local2:animateFill(f33_local0) + f33_arg0.purchaseTimeoutTimer = LUI.UITimer.new(f33_local0, "abort_purchase_transfer", nil, true) + f33_arg0:addElement(f33_arg0.purchaseTimeoutTimer) + end) + + return open_loot_menu +end +LUI.MenuBuilder.m_types_build["MPDepotOpenLootMenu"] = MPDepotOpenLootMenu + +AddLootDropTabSelector_orig = LUI.MPDepotBase.AddLootDropTabSelector +LUI.MPDepotBase.AddLootDropTabSelector = function(unk1, unk2) + if not custom_depot.get_function("has_accepted_mod_eula")() then + local item_sets = GetItemSets() + + unk1:AddButtonWithInfo("depot_collections", "@DEPOT_COLLECTIONS", "MPDepotCollectionsMenu", nil, nil, + Engine.Localize("@MPUI_X_SLASH_Y", item_sets.completedSets, item_sets.numSets)) + + unk1:AddButtonWithInfo("depot_armory", "@DEPOT_ARMORY", "MPDepotArmoryMenu") + return + end + + AddLootDropTabSelector_orig(unk1, unk2) +end + +MPDepotMenu_orig = LUI.MenuBuilder.m_types_build["MPDepotMenu"] +MPDepotMenu = function(unk1, unk2) + local depot_menu = MPDepotMenu_orig(unk1, unk2) + + if not custom_depot.get_function("has_seen_mod_eula")() then + LUI.FlowManager.RequestAddMenu(nil, "mod_eula", true, 0, false, { + acceptCallback = function() + custom_depot.get_function("set_has_accepted_mod_eula")(true) + custom_depot.get_function("set_has_seen_mod_eula")(true) + LUI.FlowManager.RequestLeaveMenu(depot_menu) + end, + declineCallback = function() + custom_depot.get_function("set_has_accepted_mod_eula")(false) + custom_depot.get_function("set_has_seen_mod_eula")(true) + end + }) + end + + return depot_menu +end +LUI.MenuBuilder.m_types_build["MPDepotMenu"] = MPDepotMenu + +GetLootDataForRef_orig = LUI.InventoryUtils.GetLootDataForRef +LUI.InventoryUtils.GetLootDataForRef = function(f13_arg0, f13_arg1, f13_arg2, f13_arg3, f13_arg4) + local loot_data = GetLootDataForRef_orig(f13_arg0, f13_arg1, f13_arg2, f13_arg3, f13_arg4) + + if loot_data and custom_depot.get_function("has_item")(loot_data.guid) then + loot_data.lockState = "Unlocked" + end + + return loot_data +end diff --git a/data/ui_scripts/custom_depot/mod_eula.lua b/data/ui_scripts/custom_depot/mod_eula.lua new file mode 100644 index 00000000..31bae99b --- /dev/null +++ b/data/ui_scripts/custom_depot/mod_eula.lua @@ -0,0 +1,23 @@ +game:addlocalizedstring("CUSTOM_DEPOT_EULA_1", "Dear User,") +game:addlocalizedstring("CUSTOM_DEPOT_EULA_2", + "By using this feature, you acknowledge that you are over 18 years old, and that any sort of chance games / gambling are allowed in your country (even if they do not involve real money).") +game:addlocalizedstring("CUSTOM_DEPOT_EULA_3", + "The H1-Mod team is not responsible if you break the law within your country, and the sole responsibility will be upon you to respect the same.") +game:addlocalizedstring("CUSTOM_DEPOT_EULA_4", + "The H1-Mod team will never include real money transactions within the modified systems. The only way to get currency, should you wish to, is by playing the game.") +game:addlocalizedstring("CUSTOM_DEPOT_EULA_5", "Best Regards,") +game:addlocalizedstring("CUSTOM_DEPOT_EULA_6", "The H1-Mod Team.") + +local mod_eula = function(unk1, unk2) + return LUI.EULABase.new(CoD.CreateState(0, 0, 0, 0, CoD.AnchorTypes.All), { + textStrings = LUI.EULABase.CreateTextStrings("@CUSTOM_DEPOT_EULA_", 6), + declineCallback = function(unk3) + unk2.declineCallback(unk3) + end, + acceptCallback = function(unk4) + unk2.acceptCallback(unk4) + end + }) +end + +LUI.MenuBuilder.registerPopupType("mod_eula", mod_eula) diff --git a/data/ui_scripts/custom_depot/scoreboard_override.lua b/data/ui_scripts/custom_depot/scoreboard_override.lua new file mode 100644 index 00000000..97bd1ce4 --- /dev/null +++ b/data/ui_scripts/custom_depot/scoreboard_override.lua @@ -0,0 +1,56 @@ +-- from roundend.lua, dev comments says that the game["round_end"] array contains indexes for this table +local ending_reasons = {"MP_DRAW", "LUA_MENU_REPORT_DRAW", "MP_ROUND_WIN", "MP_ROUND_LOSS", "LUA_MENU_REPORT_VICTORY", + "LUA_MENU_REPORT_DEFEAT", "MP_HALFTIME", "MP_OVERTIME", "MP_ROUNDEND", "MP_INTERMISSION", + "MP_SWITCHING_SIDES", "MP_MATCH_BONUS_IS", "MP_MATCH_TIE", "MP_GAME_END", "SPLASHES_BLANK"} + +local function starts_with(str, start) + return str:sub(1, #start) == start +end + +local player_old_score = 0 + +local scoreboard_orig = LUI.MenuBuilder.m_types_build["scoreboard"] +local scoreboard = function(unk1, unk2) + local scoreboard = scoreboard_orig(unk1, unk2) + + scoreboard:registerOmnvarHandler("ui_round_end", function(f22_arg0, f22_arg1) + if GameX.IsRankedMatch() then + local player_score = 0 + + local gamemode = GameX.GetGameMode() + local player_stats = Game.GetPlayerScoreInfoAtRank(Game.GetPlayerTeam(), Game.GetPlayerScoreRanking()) + + --[[ + this will do the job for when its needed, aka when round loss/win occurs + this check may be true more than once cuz this callback happens 3 times, + but the player_old_score variable will stop us from adding more currency + ]] -- + local unlocalized_string = ending_reasons[Game.GetOmnvar("ui_round_end_title")] + local is_round_based = starts_with(unlocalized_string, "MP_ROUND") or IsGameTypeRoundBased(gamemode) + + if is_round_based or gamemode == "conf" or gamemode == "war" then + player_score = player_stats.score + else + player_score = player_stats.extrascore0 + end + + local currency_gain = math.floor((player_score - player_old_score) * 10 / 100) + + if currency_gain <= 0 then + return + end + + custom_depot.get_function("add_currency")(InventoryCurrencyType.Parts, currency_gain) + + if custom_depot.functions["has_accepted_mod_eula"]() then + custom_depot.get_function("add_currency")(InventoryCurrencyType.Credits, math.random(2, 3)) + end + + player_old_score = player_score + custom_depot.get_function("save_depot_data")() + end + end) + + return scoreboard +end +LUI.MenuBuilder.m_types_build["scoreboard"] = scoreboard diff --git a/data/ui_scripts/extra_gamemodes/__init__.lua b/data/ui_scripts/extra_gamemodes/__init__.lua new file mode 100644 index 00000000..6880b983 --- /dev/null +++ b/data/ui_scripts/extra_gamemodes/__init__.lua @@ -0,0 +1 @@ +-- this patch has been moved to ui_scripts/patches/gamemodes.lua diff --git a/data/ui_scripts/hud_info/settings.lua b/data/ui_scripts/hud_info/settings.lua index ed654422..e4ddabf5 100644 --- a/data/ui_scripts/hud_info/settings.lua +++ b/data/ui_scripts/hud_info/settings.lua @@ -9,6 +9,8 @@ game:addlocalizedstring("LUA_MENU_LATENCY_DESC", "Show server latency.") game:addlocalizedstring("LUA_MENU_RED_DOT_BRIGHTNESS", "Red dot Brightness") game:addlocalizedstring("LUA_MENU_RED_DOT_BRIGHTNESS_DESC", "Adjust the brightness of red dot reticles.") +game:replacelocalizedstring("MENU_SYSINFO_CUSTOMER_SUPPORT_URL", "https://h1.gg/") + function createdivider(menu, text) local element = LUI.UIElement.new({ leftAnchor = true, diff --git a/data/ui_scripts/no_mode_switch/__init__.lua b/data/ui_scripts/no_mode_switch/__init__.lua new file mode 100644 index 00000000..8761ea04 --- /dev/null +++ b/data/ui_scripts/no_mode_switch/__init__.lua @@ -0,0 +1 @@ +-- this patch has been moved to ui_scripts/patches/no_mode_switch.lua diff --git a/data/ui_scripts/patches/__init__.lua b/data/ui_scripts/patches/__init__.lua index 85c92abf..b5a19097 100644 --- a/data/ui_scripts/patches/__init__.lua +++ b/data/ui_scripts/patches/__init__.lua @@ -5,6 +5,8 @@ end if (Engine.InFrontend()) then require("shaderdialog") require("gamemodes") + require("no_mode_switch") + require("disable_useless_things") end -- defined in mp_hud/hudutils.lua diff --git a/data/ui_scripts/patches/disable_useless_things.lua b/data/ui_scripts/patches/disable_useless_things.lua new file mode 100644 index 00000000..59696748 --- /dev/null +++ b/data/ui_scripts/patches/disable_useless_things.lua @@ -0,0 +1,17 @@ +-- Disable CP +Engine.SetDvarInt("ui_enable_cp", 0) + +-- Disable CP store +Engine.SetDvarInt("ui_show_store", 0) + +-- Remove CoD account button +if Engine.IsMultiplayer() and CoD.IsCoDAccountRegistrationAvailableInMyRegion() then + LUI.removemenubutton("pc_controls", 4) +end + +-- Remove social button +LUI.MenuBuilder.m_definitions["online_friends_widget"] = function() + return { + type = "UIElement" + } +end diff --git a/data/ui_scripts/patches/no_mode_switch.lua b/data/ui_scripts/patches/no_mode_switch.lua new file mode 100644 index 00000000..41ec9e47 --- /dev/null +++ b/data/ui_scripts/patches/no_mode_switch.lua @@ -0,0 +1,13 @@ +LUI.MenuBuilder.m_definitions["main_choose_exe_popup_menu"] = function() + return { + type = "generic_yesno_popup", + id = "main_choose_exe_popup_menu_id", + properties = { + popup_title = Engine.Localize("@MENU_NOTICE"), + message_text = Engine.Localize("@MENU_QUIT_WARNING"), + yes_action = function() + Engine.Quit() + end + } + } +end diff --git a/deps/asmjit b/deps/asmjit index 35f92e87..c5984762 160000 --- a/deps/asmjit +++ b/deps/asmjit @@ -1 +1 @@ -Subproject commit 35f92e8706db78c6aa7482b5e1cdb59c5972a965 +Subproject commit c59847629d3a19da4d10f0be4ac33b43fc4a100f diff --git a/deps/curl b/deps/curl index e2e7f54b..9153ba70 160000 --- a/deps/curl +++ b/deps/curl @@ -1 +1 @@ -Subproject commit e2e7f54b7bea521fa8373095d0f43261a720cda0 +Subproject commit 9153ba708be87ed6e7c25e1b4864f86fadeb95ad diff --git a/deps/protobuf b/deps/protobuf index 5500c72c..6e9e6036 160000 --- a/deps/protobuf +++ b/deps/protobuf @@ -1 +1 @@ -Subproject commit 5500c72c5b616da9f0125bcfab513987a1226e2b +Subproject commit 6e9e60367d8744e86856590d8ea0e793c61deeec diff --git a/deps/zlib b/deps/zlib index ec3df002..2333419c 160000 --- a/deps/zlib +++ b/deps/zlib @@ -1 +1 @@ -Subproject commit ec3df00224d4b396e2ac6586ab5d25f673caa4c2 +Subproject commit 2333419cd76cb9ae5f15c9b240b16a2052b27691 diff --git a/src/client/component/bots.cpp b/src/client/component/bots.cpp index 896f00a7..3bb8382d 100644 --- a/src/client/component/bots.cpp +++ b/src/client/component/bots.cpp @@ -2,9 +2,11 @@ #include "loader/component_loader.hpp" #include "command.hpp" +#include "console.hpp" #include "scheduler.hpp" #include "network.hpp" #include "party.hpp" +#include "scripting.hpp" #include "game/game.hpp" #include "game/scripting/execution.hpp" @@ -12,6 +14,7 @@ #include #include #include +#include namespace bots { @@ -53,14 +56,12 @@ namespace bots return; } - static auto first_bot = true; - - const auto bot_name = game::SV_BotGetRandomName(); - const auto bot_ent = game::SV_AddBot(bot_name); + const auto* const bot_name = game::SV_BotGetRandomName(); + const auto* bot_ent = game::SV_AddBot(bot_name); if (bot_ent) { - spawn_bot(bot_ent->s.entityNum); + spawn_bot(bot_ent->s.number); } else { @@ -70,6 +71,50 @@ namespace bots }, scheduler::pipeline::server, 100ms); } } + + utils::hook::detour get_bot_name_hook; + std::vector bot_names{}; + + void load_bot_data() + { + static const char* bots_txt = "h1-mod/bots.txt"; + + std::string bots_content; + if (!utils::io::read_file(bots_txt, &bots_content)) + { + return; + } + + auto names = utils::string::split(bots_content, '\n'); + for (auto& name : names) + { + name = utils::string::replace(name, "\r", ""); + if (!name.empty()) + { + bot_names.emplace_back(name); + } + } + } + + size_t bot_id = 0; + + const char* get_random_bot_name() + { + if (bot_names.empty()) + { + load_bot_data(); + } + + // only use bot names once, no dupes in names + if (!bot_names.empty() && bot_id < bot_names.size()) + { + bot_id %= bot_names.size(); + const auto& entry = bot_names.at(bot_id++); + return utils::string::va("%.*s", static_cast(entry.size()), entry.data()); + } + + return get_bot_name_hook.invoke(); + } } class component final : public component_interface @@ -82,6 +127,8 @@ namespace bots return; } + get_bot_name_hook.create(game::SV_BotGetRandomName, get_random_bot_name); + command::add("spawnBot", [](const command::params& params) { if (!can_add()) @@ -102,6 +149,13 @@ namespace bots scheduler::once(add_bot, scheduler::pipeline::server, 100ms * i); } }); + + // Clear bot names and reset ID on game shutdown to allow new names to be added without restarting + scripting::on_shutdown([] + { + bot_names.clear(); + bot_id = 0; + }); } }; } diff --git a/src/client/component/command.cpp b/src/client/component/command.cpp index 6b4febb2..c26eea7e 100644 --- a/src/client/component/command.cpp +++ b/src/client/component/command.cpp @@ -1,6 +1,10 @@ #include #include "loader/component_loader.hpp" +#include "game/game.hpp" +#include "game/dvars.hpp" +#include "game/scripting/execution.hpp" + #include "command.hpp" #include "console.hpp" #include "game_console.hpp" @@ -8,11 +12,6 @@ #include "scheduler.hpp" #include "logfile.hpp" -#include "game/game.hpp" -#include "game/dvars.hpp" - -#include "game/scripting/execution.hpp" - #include #include #include @@ -39,8 +38,14 @@ namespace command } } - void client_command(const int client_num) + void client_command(const char client_num) { + if (game::mp::g_entities[client_num].client == nullptr) + { + // Client is not fully connected + return; + } + if (!logfile::client_command_stub(client_num)) { return; @@ -539,9 +544,9 @@ namespace command static void add_commands_generic() { add("quit", game::Quit); - add("crash", []() + add("crash", [] { - *reinterpret_cast(1) = 0; + *reinterpret_cast(1) = 0x12345678; }); add("commandDump", [](const params& argument) diff --git a/src/client/component/logfile.cpp b/src/client/component/logfile.cpp index 2e2b3e4f..64600ffe 100644 --- a/src/client/component/logfile.cpp +++ b/src/client/component/logfile.cpp @@ -32,7 +32,7 @@ namespace logfile return {}; } - const scripting::entity player{game::Scr_GetEntityId(ent->s.entityNum, 0)}; + const scripting::entity player{game::Scr_GetEntityId(ent->s.number, 0)}; return scripting::lua::convert(state, player); } @@ -262,7 +262,7 @@ namespace logfile scheduler::once([cmd, message, self, hidden]() { const scripting::entity level{*game::levelEntityId}; - const scripting::entity player{game::Scr_GetEntityId(self->s.entityNum, 0)}; + const scripting::entity player{game::Scr_GetEntityId(self->s.number, 0)}; scripting::notify(level, cmd, {player, message, hidden}); scripting::notify(player, cmd, {message, hidden}); diff --git a/src/client/component/lui.cpp b/src/client/component/lui.cpp index 0738d570..c99766c1 100644 --- a/src/client/component/lui.cpp +++ b/src/client/component/lui.cpp @@ -14,6 +14,8 @@ namespace lui namespace { uint64_t event_count{}; + uint64_t obituary_count{}; + bool begin_game_message_event_stub(int a1, const char* name, void* a3) { if (event_count > 30) @@ -27,6 +29,16 @@ namespace lui return utils::hook::invoke(0x2655A0_b, a1, name, a3); } + + void cg_entity_event_stub(void* a1, void* a2, unsigned int event_type, void* a4) + { + if (event_type == 140 && obituary_count++ >= 20) + { + return; + } + + utils::hook::invoke(0xF9400_b, a1, a2, event_type, a4); + } } class component final : public component_interface @@ -38,6 +50,7 @@ namespace lui { // Patch game message overflow utils::hook::call(0x266E6B_b, begin_game_message_event_stub); + utils::hook::call(0xEAC1C_b, cg_entity_event_stub); scheduler::loop([]() { @@ -46,6 +59,11 @@ namespace lui event_count--; } }, scheduler::pipeline::lui, 50ms); + + scheduler::loop([]() + { + obituary_count = 0; + }, scheduler::pipeline::lui, 0ms); } // Increase max extra LUI memory diff --git a/src/client/component/patches.cpp b/src/client/component/patches.cpp index 36208f5f..c84d6c01 100644 --- a/src/client/component/patches.cpp +++ b/src/client/component/patches.cpp @@ -67,7 +67,8 @@ namespace patches void set_client_dvar_from_server_stub(void* clientNum, void* cgameGlob, const char* dvar, const char* value) { - if (dvar == "cg_fov"s || dvar == "cg_fovMin"s) + const auto dvar_lowercase = utils::string::to_lower(dvar); + if (dvar_lowercase == "cg_fov"s || dvar_lowercase == "cg_fovMin"s) { return; } @@ -121,7 +122,13 @@ namespace patches command::params_sv params{}; const auto menu_id = atoi(params.get(1)); - const auto client = &svs_clients[ent->s.entityNum]; + const auto client = &svs_clients[ent->s.number]; + + // 13 => change class + if (menu_id == 13 && ent->client->team == game::mp::TEAM_SPECTATOR) + { + return; + } // 32 => "end_game" if (menu_id == 32 && client->header.remoteAddress.type != game::NA_LOOPBACK) @@ -318,6 +325,13 @@ namespace patches utils::hook::jump(SELECT_VALUE(0x511050_b, 0x620040_b), _aligned_realloc); } + // Uncheat protect gamepad-related dvars + dvars::override::register_float("gpad_button_deadzone", 0.13f, 0, 1, game::DVAR_FLAG_SAVED); + dvars::override::register_float("gpad_stick_deadzone_min", 0.2f, 0, 1, game::DVAR_FLAG_SAVED); + dvars::override::register_float("gpad_stick_deadzone_max", 0.01f, 0, 1, game::DVAR_FLAG_SAVED); + dvars::override::register_float("gpad_stick_pressed", 0.4f, 0, 1, game::DVAR_FLAG_SAVED); + dvars::override::register_float("gpad_stick_pressed_hysteresis", 0.1f, 0, 1, game::DVAR_FLAG_SAVED); + if (!game::environment::is_sp()) { patch_mp(); @@ -326,6 +340,9 @@ namespace patches static void patch_mp() { + // fix vid_restart crash + utils::hook::set(0x139680_b, 0xC3); + utils::hook::jump(0x5BB9C0_b, &live_get_local_client_name); // Disable data validation error popup @@ -409,6 +426,9 @@ namespace patches // Fix gamepad related crash cl_gamepad_scrolling_buttons_hook.create(0x133210_b, cl_gamepad_scrolling_buttons_stub); + + // Prevent the game from modifying Windows microphone volume (since voice chat isn't used) + utils::hook::set(0x5BEEA0_b, 0xC3); // Mixer_SetWaveInRecordLevels } }; } diff --git a/src/client/component/renderer.cpp b/src/client/component/renderer.cpp index 36fb142a..f2758790 100644 --- a/src/client/component/renderer.cpp +++ b/src/client/component/renderer.cpp @@ -49,12 +49,8 @@ namespace renderer { switch (dvars::r_fullbright->current.integer) { - case 4: - return 3; - case 3: - return 13; case 2: - return 25; + return 13; default: return game::TECHNIQUE_UNLIT; } @@ -148,7 +144,7 @@ namespace renderer return; } - dvars::r_fullbright = dvars::register_int("r_fullbright", 0, 0, 4, game::DVAR_FLAG_SAVED, "Toggles rendering without lighting"); + dvars::r_fullbright = dvars::register_int("r_fullbright", 0, 0, 2, game::DVAR_FLAG_SAVED, "Toggles rendering without lighting"); r_init_draw_method_hook.create(SELECT_VALUE(0x5467E0_b, 0x669580_b), &r_init_draw_method_stub); r_update_front_end_dvar_options_hook.create(SELECT_VALUE(0x583560_b, 0x6A78C0_b), &r_update_front_end_dvar_options_stub); diff --git a/src/client/component/scripting.cpp b/src/client/component/scripting.cpp index efb81f41..9bcd2bc9 100644 --- a/src/client/component/scripting.cpp +++ b/src/client/component/scripting.cpp @@ -44,6 +44,8 @@ namespace scripting game::dvar_t* g_dump_scripts; + std::vector> shutdown_callbacks; + void vm_notify_stub(const unsigned int notify_list_owner_id, const game::scr_string_t string_value, game::VariableValue* top) { @@ -94,6 +96,11 @@ namespace scripting script_function_table.clear(); } + for (const auto& callback : shutdown_callbacks) + { + callback(); + } + scripting::notify(*game::levelEntityId, "shutdownGame_called", {1}); lua::engine::stop(); return g_shutdown_game_hook.invoke(free_scripts); @@ -162,6 +169,11 @@ namespace scripting } } + void on_shutdown(const std::function& callback) + { + shutdown_callbacks.push_back(callback); + } + class component final : public component_interface { public: diff --git a/src/client/component/scripting.hpp b/src/client/component/scripting.hpp index 5794bff2..226b275c 100644 --- a/src/client/component/scripting.hpp +++ b/src/client/component/scripting.hpp @@ -8,4 +8,6 @@ namespace scripting extern std::unordered_map> fields_table; extern std::unordered_map> script_function_table; extern utils::concurrency::container shared_table; + + void on_shutdown(const std::function& callback); } \ No newline at end of file diff --git a/src/client/component/server_list.cpp b/src/client/component/server_list.cpp index c74c259c..9eb7fded 100644 --- a/src/client/component/server_list.cpp +++ b/src/client/component/server_list.cpp @@ -143,8 +143,9 @@ namespace server_list if (column == 2) { - return utils::string::va("%d/%d [%d]", servers[i].clients, servers[index].max_clients, - servers[i].bots); + const auto client_count = servers[i].clients - servers[i].bots; + return utils::string::va("%d/%d [%d]", client_count, servers[i].max_clients, + servers[i].clients); } if (column == 3) @@ -169,12 +170,20 @@ namespace server_list { std::stable_sort(servers.begin(), servers.end(), [](const server_info& a, const server_info& b) { - if (a.clients == b.clients) + const auto a_players = a.clients - a.bots; + const auto b_players = b.clients - b.bots; + + if (a_players == b_players) { - return a.ping < b.ping; + if (a.clients == b.clients) + { + return a.ping < b.ping; + } + + return a.clients > b.clients; } - return a.clients > b.clients; + return a_players > b_players; }); } diff --git a/src/client/game/dvars.cpp b/src/client/game/dvars.cpp index 31f18d81..7c343716 100644 --- a/src/client/game/dvars.cpp +++ b/src/client/game/dvars.cpp @@ -5040,6 +5040,11 @@ namespace dvars "Turn on debug lines for melee traces", generate_hash("melee_debug") }, + { + "mis_cheat", + "Set when level unlock cheat is performed", + generate_hash("mis_cheat") + }, { "migration_dvarErrors", "Whether to check for illegal script dvar changes.", diff --git a/src/client/game/scripting/array.hpp b/src/client/game/scripting/array.hpp index e78ace3b..64a9269b 100644 --- a/src/client/game/scripting/array.hpp +++ b/src/client/game/scripting/array.hpp @@ -76,6 +76,8 @@ namespace scripting { return { this->id_, this->get_value_id(key.as()) }; } + + throw std::runtime_error("Invalid key type"); } private: diff --git a/src/client/game/scripting/function_tables.cpp b/src/client/game/scripting/function_tables.cpp index a397741e..7425eb3e 100644 --- a/src/client/game/scripting/function_tables.cpp +++ b/src/client/game/scripting/function_tables.cpp @@ -1290,9 +1290,9 @@ namespace scripting {"_meth_8215", 0x8215}, // SP 0x2CB4A0 MP 0x44CB00 {"_meth_8216", 0x8216}, // SP 0x2CB5D0 MP 0x44CCA0 {"_meth_8217", 0x8217}, // SP 0x2CB720 MP 0x44CCF0 - {"circle", 0x8218}, // SP 0x2D0720 MP 0x439750 + {"circle", 0x8218}, // SP 0x2D0720 MP 0x439750 - iw6 name is WorldPointInReticle_Circle {"rect", 0x8219}, // SP 0x2D12D0 MP 0x439F20 - {"_meth_821a", 0x821A}, // SP 0x2D1850 MP 0x43A680 + {"getpointinbounds", 0x821A}, // SP 0x2D1850 MP 0x43A680 {"transfermarkstonewscriptmodel", 0x821B}, // SP 0x2D1FC0 MP 0x43B020 {"setwatersheeting", 0x821C}, // SP 0x2C47C0 MP 0x445400 {"_meth_821d", 0x821D}, // SP 0x2C02D0 MP 0x000000 @@ -1303,8 +1303,8 @@ namespace scripting {"_meth_8222", 0x8222}, // SP 0x2CDCF0 MP 0x437140 {"_meth_8223", 0x8223}, // SP 0x2CD9F0 MP 0x436F30 {"_meth_8224", 0x8224}, // SP 0x2CE3A0 MP 0x437750 - {"_meth_8225", 0x8225}, // SP 0x2D1E80 MP 0x438700 - {"_meth_8226", 0x8226}, // SP 0x2C2210 MP 0x4432D0 + {"isitemunlocked", 0x8225}, // SP 0x2D1E80 MP 0x438700 + {"getrankedplayerdata", 0x8226}, // SP 0x2C2210 MP 0x4432D0 {"vehicleturretcontroloff", 0x8227}, // SP 0x4E7230 MP 0x60E170 {"isturretready", 0x8228}, // SP 0x4E7310 MP 0x609890 {"_meth_8229", 0x8229}, // SP 0x4E7560 MP 0x609B20 @@ -1332,7 +1332,7 @@ namespace scripting {"_meth_8240", 0x8240}, // SP 0x2C4AD0 MP 0x000000 {"_meth_8241", 0x8241}, // SP 0x274CF0 MP 0x000000 {"_meth_8242", 0x8242}, // SP 0x2750F0 MP 0x000000 - {"_meth_8243", 0x8243}, // SP 0x2C2550 MP 0x443C20 + {"setcommonplayerdata", 0x8243}, // SP 0x2C2550 MP 0x443C20 - Could be also setrankedplayerdata {"_meth_8244", 0x8244}, // SP 0x2C2A00 MP 0x4442D0 {"_meth_8245", 0x8245}, // SP 0x2C3190 MP 0x444850 {"trackerupdate", 0x8246}, // SP 0x2C51C0 MP 0x447650 @@ -1455,7 +1455,7 @@ namespace scripting {"notsolid", 0x82BB}, // SP 0x2E3230 MP 0x452FA0 {"setcandamage", 0x82BC}, // SP 0x2E2300 MP 0x453ED0 {"setcanradiusdamage", 0x82BD}, // SP 0x2E2360 MP 0x453F30 - {"physicslaunchclient", 0x82BE}, // SP 0x2E23E0 MP 0x453FB0 + {"physicslaunch", 0x82BE}, // SP 0x2E23E0 MP 0x453FB0 {"setcardicon", 0x82BF}, // SP 0x000000 MP 0x411100 {"setcardnameplate", 0x82C0}, // SP 0x000000 MP 0x411110 {"setcarddisplayslot", 0x82C1}, // SP 0x000000 MP 0x411120 @@ -1492,11 +1492,11 @@ namespace scripting {"player_recoilscaleoff", 0x82E0}, // SP 0x28D920 MP 0x4092D0 {"weaponlockstart", 0x82E1}, // SP 0x28DA00 MP 0x409530 {"weaponlockfinalize", 0x82E2}, // SP 0x28DCC0 MP 0x4097A0 - {"disableautoreload", 0x82E3}, // SP 0x28DFC0 MP 0x409C50 + {"weaponlockfree", 0x82E3}, // SP 0x28DFC0 MP 0x409C50 - was disableautoreload {"setentertime", 0x82E4}, // SP 0x28E060 MP 0x409E60 {"usinggamepad", 0x82E5}, // SP 0x000000 MP 0x411680 {"_meth_82e6", 0x82E6}, // SP 0x000000 MP 0x411700 - {"_meth_82e7", 0x82E7}, // SP 0x000000 MP 0x411710 + {"issighted", 0x82E7}, // SP 0x000000 MP 0x411710 {"_meth_82e8", 0x82E8}, // SP 0x000000 MP 0x411730 {"_meth_82e9", 0x82E9}, // SP 0x000000 MP 0x411720 {"_meth_82ea", 0x82EA}, // SP 0x2905B0 MP 0x40BAD0 @@ -2012,7 +2012,7 @@ namespace scripting {"_meth_84fa", 0x84FA}, // SP 0x2C63F0 MP 0x44D230 {"_meth_84fb", 0x84FB}, // SP 0x2BF9D0 MP 0x447EB0 {"_meth_84fc", 0x84FC}, // SP 0x2C5650 MP 0x447220 - {"_meth_84fd", 0x84FD}, // SP 0x000000 MP 0x4109F0 + {"_meth_84fd", 0x84FD}, // SP 0x000000 MP 0x4109F0 - Could be setMLGCamVisibility or SetMLGSpectator {"_meth_84fe", 0x84FE}, // SP 0x000000 MP 0x0B90C0 {"_meth_84ff", 0x84FF}, // SP 0x000000 MP 0x0B9280 {"_meth_8500", 0x8500}, // SP 0x2C17E0 MP 0x443F90 @@ -2153,6 +2153,7 @@ namespace scripting std::unordered_map token_map = { + {"", 0x000}, {"pl#", 0x001}, {"-", 0x002}, {"radius`", 0x003}, @@ -2187,7 +2188,7 @@ namespace scripting {"ai_sight_line_group", 0x020}, {"aim_highest_bone", 0x021}, {"aim_vis_bone", 0x022}, - {"aiSpread", 0x023}, + {"aispread", 0x023}, {"aisquadmembers", 0x024}, {"alert", 0x025}, {"alertlevel", 0x026}, @@ -2669,10 +2670,10 @@ namespace scripting {"infinite_energy", 0x202}, {"info_notnull", 0x203}, {"info_player_start", 0x204}, - {"init", 0x205}, + {"not_init", 0x205}, {"initstructs", 0x206}, {"insolid", 0x207}, - {"intermission", 0x208}, + {"init", 0x208}, {"interval", 0x209}, {"inuse", 0x20A}, {"invalid_parent", 0x20B}, @@ -2764,10 +2765,10 @@ namespace scripting {"luinotifyserver", 0x261}, {"mag_eject", 0x262}, {"mag_eject_left", 0x263}, - {"main", 0x264}, + {"not_main", 0x264}, {"manual", 0x265}, {"manual_ai", 0x266}, - {"manual_change", 0x267}, + {"main", 0x267}, {"map", 0x268}, {"matchid", 0x269}, {"matchmakingsettingsversion", 0x26A}, @@ -2887,13 +2888,13 @@ namespace scripting {"operationsdeadline", 0x2DC}, {"oriented", 0x2DD}, {"orientto_complete", 0x2DE}, - {"origin", 0x2DF}, + {"_not_origin", 0x2DF}, {"other", 0x2E0}, {"over", 0x2E1}, - {"_not_owner", 0x2E2}, // was "owner" + {"origin", 0x2E2}, {"pacifist", 0x2E3}, {"pacifistwait", 0x2E4}, - {"owner", 0x2E5}, // was "pain" + {"owner", 0x2E5}, {"pantssize", 0x2E6}, {"parentindex", 0x2E7}, {"parentname", 0x2E8}, @@ -2925,10 +2926,10 @@ namespace scripting {"perkrestricted", 0x302}, {"perks", 0x303}, {"perkslots", 0x304}, - {"_not_pers", 0x305}, // was "pers" + {"_not_pers", 0x305}, {"persistentperksunlocked", 0x306}, {"persistentweaponsunlocked", 0x307}, - {"pers", 0x308}, // was "phone_off" + {"pers", 0x308}, {"phone_on", 0x309}, {"physics_finished", 0x30A}, {"physics_impact", 0x30B}, @@ -2952,10 +2953,10 @@ namespace scripting {"playerip", 0x31D}, {"playername", 0x31E}, {"playerpositions", 0x31F}, - {"players", 0x320}, - {"playerSpread", 0x321}, + {"_not_players", 0x320}, + {"playerspread", 0x321}, {"playerxuidhigh", 0x322}, - {"playerxuidlow", 0x323}, + {"players", 0x323}, {"playing", 0x324}, {"points", 0x325}, {"position", 0x326}, @@ -2981,10 +2982,10 @@ namespace scripting {"primaryfurniturekit", 0x33A}, {"primaryoffhand", 0x33B}, {"primaryreticle", 0x33C}, - {"primaryweapon", 0x33D}, + {"_not_primaryweapon", 0x33D}, {"privatematchactivesquadmember", 0x33E}, {"privatematchcustomclasses", 0x33F}, - {"privatematchsquadmembers", 0x340}, + {"primaryweapon", 0x340}, {"projectile_impact", 0x341}, {"projectile_impact_player", 0x342}, {"prone", 0x343}, @@ -3058,10 +3059,10 @@ namespace scripting {"scope_cap", 0x387}, {"scope_center", 0x388}, {"scope_top", 0x389}, - {"score", 0x38A}, + {"_not_score", 0x38A}, {"script", 0x38B}, {"script_brushmodel", 0x38C}, - {"script_clut", 0x38D}, + {"score", 0x38D}, {"script_context", 0x38E}, {"script_delay", 0x38F}, {"script_goal_changed", 0x390}, @@ -3096,10 +3097,10 @@ namespace scripting {"secondaryfurniturekit", 0x3AD}, {"secondaryoffhand", 0x3AE}, {"secondaryreticle", 0x3AF}, - {"secondaryweapon", 0x3B0}, + {"_not_secondaryweapon", 0x3B0}, {"sentry", 0x3B1}, {"sentry_manual", 0x3B2}, - {"sentry_offline", 0x3B3}, + {"secondaryweapon", 0x3B3}, {"servertimecount", 0x3B4}, {"servertimeexceedcount", 0x3B5}, {"servertimemax", 0x3B6}, @@ -3205,7 +3206,7 @@ namespace scripting {"suppressionduration", 0x41A}, {"suppressionmeter", 0x41B}, {"suppressionstarttime", 0x41C}, - {"suppressionTime", 0x41D}, + {"suppressiontime", 0x41D}, {"suppressionwait", 0x41E}, {"surfacetype", 0x41F}, {"surprisedbymedistsq", 0x420}, @@ -3312,10 +3313,10 @@ namespace scripting {"tag_sight_off", 0x485}, {"tag_sight_on", 0x486}, {"tag_stow_back_mid_attach", 0x487}, - {"_not_tag_stowed_back", 0x488}, // was "tag_stowed_back" + {"_not_tag_stowed_back", 0x488}, {"tag_stowed_hip_rear", 0x489}, {"tag_sync", 0x48A}, - {"tag_stowed_back", 0x48B}, // was "tag_tip" + {"tag_stowed_back", 0x48B}, {"tag_turret", 0x48C}, {"tag_turret_base", 0x48D}, {"tag_turret_pitch", 0x48E}, @@ -3340,10 +3341,10 @@ namespace scripting {"target", 0x4A1}, {"target_script_trigger", 0x4A2}, {"targetname", 0x4A3}, - {"_not_team", 0x4A4}, // was "team" + {"_not_team", 0x4A4}, {"team3", 0x4A5}, {"teambalanced", 0x4A6}, - {"team", 0x4A7}, // was "teammode_axisallies" + {"team", 0x4A7}, {"teammode_ffa", 0x4A8}, {"teammovewaittime", 0x4A9}, {"their_score", 0x4AA}, @@ -3370,10 +3371,10 @@ namespace scripting {"traversecost", 0x4BF}, {"traversesoonnotifydist", 0x4C0}, {"trend", 0x4C1}, - {"_not_trigger", 0x4C2}, // was "trigger" + {"_not_trigger", 0x4C2}, {"trigger_damage", 0x4C3}, {"trigger_use", 0x4C4}, - {"trigger", 0x4C5}, // was "trigger_use_touch" + {"trigger", 0x4C5}, {"truck_cam", 0x4C6}, {"turnrate", 0x4C7}, {"turret_deactivate", 0x4C8}, @@ -3492,97 +3493,423 @@ namespace scripting {"codescripts/struct", 0x53E}, {"codescripts/message", 0x53F}, {"maps/mp/gametypes/_callbacksetup", 0x540}, + {"_createfx", 0x575}, + {"_effect", 0x58F}, + {"_hasperk", 0x5CB}, + {"_objective_delete", 0x603}, + {"_unsetperk", 0x67B}, + {"ac130", 0x6CE}, + {"ac130player", 0x6D1}, + {"scorepercentagecutoff", 0x782}, + {"addspawnpoints", 0x82F}, + {"addstartspawnpoints", 0x831}, + {"addtocharactersarray", 0x848}, + {"addtoteam", 0x851}, + {"agent_funcs", 0x897}, + {"agent_gameparticipant", 0x898}, + {"agent_teamparticipant", 0x89F}, + {"agent_type", 0x8A0}, + {"agentarray", 0x8A1}, + {"airstrikeinprogress", 0x99C}, + {"allowuse", 0xAB2}, + {"allowvote", 0xAB4}, + {"anim_prop_models", 0xBEC}, + {"applyloadout", 0xCAE}, + {"artillerydangercenter", 0xD33}, + {"atbrinkofdeath", 0xD82}, + {"attackers", 0xE34}, + {"primaryprogressbartexty", 0xF88}, + {"audio", 0x10F0}, + {"avoidkillstreakonspawntimer", 0x11FC}, + {"basefontscale", 0x1309}, + {"bcsounds", 0x1358}, + {"beingrevived", 0x13AB}, + {"blockweapondrops", 0x14B5}, + {"bombsquadicons", 0x154C}, + {"bombsquadids", 0x154D}, + {"bot_funcs", 0x161B}, + {"breathingstoptime", 0x17E6}, + {"brinkofdeathkillstreak", 0x1818}, + {"greatestuniqueplayerkills", 0x18B1}, + {"c4explodethisframe", 0x1974}, + {"callback_playerdamage", 0x19F5}, + {"callbackplayerdamage", 0x19FE}, + {"candocombat", 0x1AC6}, + {"canperformclienttraces", 0x1AD6}, + {"carryflag", 0x1BB4}, + {"challengeinfo", 0x1C62}, + {"changingweapon", 0x1C87}, + {"characters", 0x1C8E}, + {"checkdynamicspawns", 0x1CFA}, + {"chopper", 0x1D48}, + {"class", 0x1E2E}, + {"classtweaks", 0x1E40}, + {"claymoredetectiondot", 0x1E44}, + {"claymoredetectiongraceperiod", 0x1E45}, + {"claymoredetectionmindist", 0x1E46}, + {"claymoredetonateradius", 0x1E47}, + {"clearonvictimdisconnect", 0x1EF9}, + {"clientid", 0x1F0F}, + {"clientmatchdataid", 0x1F10}, + {"clienttweakables", 0x1F12}, + {"combathigh", 0x20AB}, + {"concussionendtime", 0x20DF}, + {"conf_fx", 0x20E9}, + {"connecttime", 0x214F}, + {"console", 0x2153}, + {"createuseobject", 0x244C}, + {"curorigin", 0x24C8}, + {"curprogress", 0x24C9}, + {"currentweaponatspawn", 0x252E}, + {"damagedplayers", 0x259A}, + {"defaultvalue", 0x27A1}, + {"delayminetime", 0x27E9}, + {"deleteobjpoint", 0x2859}, + {"detectedexploit", 0x2991}, + {"detectexplosives", 0x2992}, + {"detectid", 0x2994}, + {"disabled", 0x2AFD}, + {"disabledoffhandweapons", 0x2B05}, + {"disabledusability", 0x2B0A}, + {"disabledweapon", 0x2B0B}, + {"disabledweaponswitch", 0x2B0C}, + {"disablespawning", 0x2B28}, + {"dogtags", 0x2CDF}, + {"doingfinalkillcamfx", 0x2CE6}, + {"doingsplash", 0x2CE9}, + {"dont_delete_grenades_on_next_spawn", 0x2D1E}, + {"drawfriend", 0x2DD7}, + {"droppeddeathweapon", 0x2F74}, + {"empendtime", 0x3082}, + {"entity_number", 0x3314}, + {"entityheadicons", 0x331A}, + {"finalkill", 0x373E}, + {"findboxcenter", 0x3779}, + {"flashduration", 0x38AE}, + {"flashendtime", 0x38B1}, + {"flashrumbleduration", 0x38C4}, + {"forfeitinprogress", 0x39DF}, + {"freeplayers", 0x3A2A}, + {"friendlydamage", 0x3A97}, + {"fx", 0x3B23}, + {"gameended", 0x3BDA}, + {"gameendtime", 0x3BDC}, + {"gamemodemodifyplayerdamage", 0x3BF6}, + {"gametweaks", 0x3C02}, + {"getnextobjid", 0x4041}, + {"getotherteam", 0x4067}, + {"getspawnpoint", 0x40D2}, + {"getspawnpoint_freeforall", 0x40D5}, + {"getteamspawnpoints", 0x411F}, + {"getweaponclass", 0x4167}, + {"giveloadout", 0x41E0}, + {"gotpullbacknotify", 0x428B}, + {"guid", 0x4450}, + {"gunner", 0x4473}, + {"hardcoremode", 0x46CA}, + {"hardpointtweaks", 0x46CE}, + {"hasdied", 0x4726}, + {"headmodel", 0x477D}, + {"healthoverlaycutoff", 0x478D}, + {"healthregendisabled", 0x478E}, + {"healthregenerationstreak", 0x4791}, + {"hits", 0x4926}, + {"hitsthismag", 0x4929}, + {"hostname", 0x4A3E}, + {"hud", 0x4AB3}, + {"hudtweaks", 0x4AFF}, + {"idflags", 0x4B56}, + {"idflags_no_knockback", 0x4B58}, + {"idflags_shield_explosive_impact", 0x4B5E}, + {"idflags_shield_explosive_impact_huge", 0x4B5F}, + {"idflags_shield_explosive_splash ", 0x4B60}, + {"idflags_stun", 0x4B61}, + {"idflagstime", 0x4B62}, + {"inc", 0x4C13}, + {"inframes", 0x4C67}, + {"ingraceperiod", 0x4C6D}, + {"init_animatedmodels", 0x4C77}, + {"initedentityheadicons", 0x4DB6}, + {"initializematchrules", 0x4DE0}, + {"initializetagpathvariables", 0x4DE3}, + {"initspawns", 0x4E26}, + {"inlaststand", 0x4E3C}, + {"inplayersmokescreen", 0x4E42}, + {"isactive", 0x50A6}, + {"isagent", 0x50AB}, + {"iscooked", 0x50E8}, + {"ishorde", 0x511D}, + {"iskillstreakweapon", 0x513D}, + {"isrocketcorpse", 0x5193}, + {"issniper", 0x51AB}, + {"istactical", 0x51C7}, + {"isteamspeaking", 0x51D0}, + {"joining_team", 0x528C}, + {"kill_streak", 0x533C}, + {"killcam", 0x534B}, + {"killcamlength", 0x534F}, + {"killedplayers", 0x5363}, + {"killstreakrounddelay", 0x53A2}, + {"killstreakspawnshield", 0x53A8}, + {"largeprojectiledamage", 0x54C0}, + {"lastclass", 0x5589}, + {"lastconcussedtime", 0x558B}, + {"lastkilldogtime", 0x558D}, + {"lastdamagewasfromenemy", 0x5591}, + {"lastdeathicon", 0x5592}, + {"lastdroppableweapon", 0x5598}, + {"lastflashedtime", 0x55A1}, + {"lastgrenadesuicidetime", 0x55A5}, + {"lasthittime", 0x55AB}, + {"lastkilledby", 0x55B1}, + {"lastkilltime", 0x55B6}, + {"lastprimaryweaponswaptime", 0x55CD}, + {"lastshotfiredtime", 0x55D9}, + {"laststatustime", 0x55FC}, + {"lastwave", 0x560E}, + {"leaving_team", 0x566D}, + {"lowertextfontsize", 0x58A8}, + {"lowertexty", 0x58A9}, + {"lowertextyalign", 0x58AA}, + {"plantedlethalequip", 0x5979}, + {"mapcenter", 0x5986}, + {"mapsize", 0x5990}, + {"matchbonus", 0x59DF}, + {"matchrules_damagemultiplier", 0x59E6}, + {"matchrules_vampirism", 0x59EB}, + {"maxclients", 0x5A29}, + {"maxevents", 0x5A37}, + {"maxfontscale", 0x5A39}, + {"maxkillstreaks", 0x5A3C}, + {"maxlives", 0x5A40}, + {"maxnumawardsperplayer", 0x5A41}, + {"maxlogclients", 0x5A42}, + {"maxnumchallengesperplayer", 0x5A45}, + {"maxperplayerexplosives", 0x5A4A}, + {"minedamagehalfheight", 0x5C51}, + {"minedamagemax", 0x5C53}, + {"minedamagemin", 0x5C54}, + {"minedamageradius", 0x5C56}, + {"minedetectiongraceperiod", 0x5C58}, + {"minedetectionheight", 0x5C59}, + {"minedetectionradius", 0x5C5A}, + {"mineselfdestructtime", 0x5C5F}, + {"missioncallbacks", 0x5CDC}, + {"modifyplayerdamage", 0x5D51}, + {"movespeedscaler", 0x5F7B}, + {"mp_createfx", 0x5FAC}, + {"multiteambased", 0x5FEC}, + {"objectivepointsmod", 0x6301}, + {"objectivescaler", 0x6303}, + {"objid", 0x6304}, + {"teamobjids", 0x6305}, + {"oldheadicon", 0x63D4}, + {"oldheadiconteam", 0x63D5}, + {"omaclasschanged", 0x6436}, + {"onforfeit", 0x64AF}, + {"onlinegame", 0x64B8}, + {"onnormaldeath", 0x64BF}, + {"onplayerconnectaudioinit", 0x64C9}, + {"onplayerscore", 0x64D5}, + {"onstartgametype", 0x64EC}, + {"onuse", 0x64F8}, + {"outframes", 0x65C4}, + {"participants", 0x669D}, + {"tookweaponfrom", 0x680B}, + {"placement", 0x6861}, + {"planemodel", 0x687C}, + {"playedstartingmusic", 0x6A41}, + {"player_speed", 0x6C19}, + {"playerhealth_regularregendelay", 0x6CC3}, + {"playertweaks", 0x6D74}, + {"bonusupdatetotal", 0x6E8A}, + {"primaryprogressbarfontsize", 0x6F85}, + {"primaryprogressbarheight", 0x6F86}, + {"primaryprogressbartextx", 0x6F87}, + {"primaryprogressbarwidth", 0x6F89}, + {"primaryprogressbarx", 0x6F8A}, + {"primaryprogressbary", 0x6F8B}, + {"qafinished", 0x7073}, + {"quickmessagetoall", 0x70A2}, + {"rankedmatch", 0x7137}, + {"ranktable", 0x713A}, + {"recentkillcount", 0x7260}, + {"recoilscale", 0x7268}, + {"regenspeed", 0x72D4}, + {"registerhalftimedvar", 0x72EF}, + {"registernumlivesdvar", 0x72F4}, + {"registerroundlimitdvar", 0x72F6}, + {"registerroundswitchdvar", 0x72F7}, + {"registerscorelimitdvar", 0x72F8}, + {"registertimelimitdvar", 0x72F9}, + {"registerwinlimitdvar", 0x72FE}, + {"reinitializematchrulesonmigration", 0x7307}, + {"removefromcharactersarray", 0x73A7}, + {"requiredmapaspectratio", 0x740C}, + {"reverb_settings", 0x74F1}, + {"riotshieldxpbullets", 0x7556}, + {"rules", 0x7674}, + {"scavenger_altmode", 0x785B}, + {"scavenger_secondary", 0x785C}, + {"script_accumulate", 0x792B}, + {"script_destructable_area", 0x799B}, + {"script_fxcommand", 0x79F0}, + {"script_fxid", 0x79F1}, + {"script_gametype_atdm", 0x79F5}, + {"script_gametype_ctf", 0x79F6}, + {"script_gametype_dm", 0x79F7}, + {"script_gametype_koth", 0x79F8}, + {"script_gametype_sd", 0x79FA}, + {"script_gametype_tdm", 0x79FB}, + {"script_threshold", 0x7AF4}, + {"setclass", 0x7F3B}, + {"setcommonrulesfrommatchrulesdata", 0x7F3F}, + {"setobjectivehinttext", 0x7FC3}, + {"setobjectivescoretext", 0x7FC4}, + {"setobjectivetext", 0x7FC5}, + {"setupcallbacks", 0x8301}, + {"setupminimap", 0x8324}, + {"setusetime", 0x834C}, + {"shieldbullethits", 0x84C5}, + {"shielddamage", 0x84C6}, + {"showingfinalkillcam", 0x8516}, + {"showtoteam", 0x8535}, + {"softlanding", 0x885F}, + {"softlandingtriggers", 0x8860}, + {"spamdelay", 0x88B9}, + {"spawndogtags", 0x899E}, + {"spawnmaxs", 0x89F3}, + {"spawnmins", 0x89F6}, + {"spawnpoints", 0x8A01}, + {"speakers", 0x8A19}, + {"spectateoverride", 0x8A4A}, + {"splashqueue", 0x8A6B}, + {"splitscreen", 0x8A7C}, + {"stingerlockstarttime", 0x8E48}, + {"stingerlostsightlinetime", 0x8E49}, + {"stingerstage", 0x8E59}, + {"stingertarget", 0x8E5A}, + {"stingeruseentered", 0x8E5C}, + {"stuckenemyentity", 0x8F6C}, + {"stunscaler", 0x8F77}, + {"suicides", 0x8FAF}, + {"switching_teams", 0x907E}, + {"tag_stowed_hip", 0x90D3}, + {"tagteamupdater", 0x910A}, + {"teambalance", 0x91E9}, + {"teambased", 0x91EB}, + {"teamkillsthisround", 0x91F3}, + {"teamnamelist", 0x91F7}, + {"teamprogressbarfontsize", 0x91FB}, + {"teamprogressbarheight", 0x91FC}, + {"teamprogressbartexty", 0x91FD}, + {"teamprogressbarwidth", 0x91FE}, + {"teamprogressbary", 0x91FF}, + {"teamspawnpoints", 0x9201}, + {"teamtweaks", 0x9205}, + {"throwinggrenade", 0x933E}, + {"timeplayed", 0x9372}, + {"trackingweaponname", 0x94FB}, + {"trigunderwater", 0x9822}, + {"tweakablesinitialized", 0x99E0}, + {"updateddmscores", 0x9B0F}, + {"userate", 0x9C10}, + {"usestartspawns", 0x9C14}, + {"usingremote", 0x9C34}, + {"v", 0x9C42}, + {"voice_count", 0x9D33}, + {"visuals", 0x9E9C}, + {"waitingtodeactivate", 0xA04B}, + {"wasaliveatmatchstart", 0xA1BE}, + {"waschained", 0xA1C0}, + {"wasdamaged", 0xA1C3}, + {"wasdamagedfrombulletpenetration", 0xA1C5}, + {"wasti", 0xA1D2}, + {"waswinning", 0xA1D4}, + {"waterdeletez", 0xA297}, + {"wavedelay", 0xA2AB}, + {"weaponattachments", 0xA2D3}, + {"weaponlist", 0xA2DD}, + {"weapontweaks", 0xA2F2}, + {"whizby_settings", 0xA315}, + {"scoreinfo", 0xA3A5}, + {"xpupdatetotal", 0xA3AB}, - // additional findings from gametype/map scripts - mikey (6/26/2022) - {"common_scripts/_fx", 0xA4FB}, + {"codescripts/character", 0xA4EF}, + {"common_scripts/_artcommon", 0xA4F0}, + {"common_scripts/_bcs_location_trigs", 0xA4F1}, + {"common_scripts/_createfx", 0xA4F2}, + {"common_scripts/_createfxmenu", 0xA4F3}, + {"common_scripts/_destructible", 0xA4F4}, + {"common_scripts/_dynamic_world", 0xA4F5}, + {"common_scripts/_elevator", 0xA4F6}, + {"common_scripts/_exploder", 0xA4F7}, + {"common_scripts/_fx", 0xA4F8}, {"common_scripts/_pipes", 0xA4F9}, {"common_scripts/utility", 0xA4FA}, - - {"QuickMessageToAll", 0x70a2}, - {"SetupCallbacks", 0x8301}, - {"_effect", 0x58f}, - {"_objective_delete", 0x603}, - {"addSpawnPoints", 0x82f}, - {"addStartSpawnPoints", 0x831}, - {"addToCharactersArray", 0x848}, - {"allowUse", 0xab2}, - {"applyLoadout", 0xcae}, // has applyLoadout notify like IW6's giveLoadout does at the end + similar logic - {"characters", 0x1c8e}, - {"checkDynamicSpawns", 0x1cfa}, - {"clearOnVictimDisconnect", 0x1ef9}, - {"conf_fx", 0x20e9}, - {"console", 0x2153}, - {"createUseObject", 0x244c}, - {"curOrigin", 0x24c8}, - {"deleteObjPoint", 0x2859}, - {"dogtags", 0x2cdf}, - {"finalKill", 0x373e}, - {"findBoxCenter", 0x3779}, - {"forfeitInProgress", 0x39df}, - {"gamemodeModifyPlayerDamage", 0x3bf6}, - {"getNextObjID", 0x4041}, - {"getOtherTeam", 0x4067}, - {"getSpawnPoint", 0x40d2}, - {"getSpawnpoint_FreeForAll", 0x40d5}, - {"getTeamSpawnPoints", 0x411f}, - {"giveLoadout", 0x41e0}, // this may not even be giveLoadout but it's a wrapper for it and it does the same logic so - {"guid", 0x4450}, - {"inGracePeriod", 0x4c6d}, - {"initSpawns", 0x4e26}, - {"initializeMatchRules", 0x4de0}, - {"initializeTagPathVariables", 0x4de3}, - {"mapCenter", 0x5986}, - {"maps/mp/_compass", 0xa731}, - {"maps/mp/_load", 0xa74c}, - {"maps/mp/_utility", 0xa764}, + {"maps/mp/_animatedmodels", 0xA72B}, + {"maps/mp/_areas", 0xA72C}, + {"maps/mp/_art", 0xA72D}, + {"maps/mp/_audio", 0xA72E}, + {"maps/mp/_awards", 0xA72F}, + {"maps/mp/_compass", 0xA731}, + {"maps/mp/_createfx", 0xA732}, + {"maps/mp/_crib", 0xA733}, + {"maps/mp/_destructables", 0xA734}, + {"maps/mp/_entityheadicons", 0xA737}, + {"maps/mp/_events", 0xA738}, + {"maps/mp/_flashgranades", 0xA747}, + {"maps/mp/_fx", 0xA748}, + {"maps/mp/_global_fx", 0xA749}, + {"maps/mp/_global_fx_code", 0xA74A}, + {"maps/mp/_load", 0xA74C}, + {"maps/mp/_matchdata", 0xA74E}, + {"maps/mp/_sun", 0xA752}, + {"maps/mp/_scope", 0xA755}, + {"maps/mp/_scoreboard", 0xA758}, + {"maps/mp/_shutter", 0xA759}, + {"maps/mp/_stinger", 0xA75B}, + {"maps/mp/_utility", 0xA764}, + {"maps/mp/_zipline", 0xA76A}, + {"maps/mp/gametypes/_battlechatter_mp", 0xA78A}, {"maps/mp/gametypes/_class", 0xA78B}, {"maps/mp/gametypes/_damage", 0xA78D}, + {"maps/mp/gametypes/_damagefeedback", 0xA78E}, + {"maps/mp/gametypes/_deathicons", 0xA78F}, + {"maps/mp/gametypes/_dev", 0xA790}, + {"maps/mp/gametypes/_equipment", 0xA791}, + {"maps/mp/gametypes/_friendicons", 0xA792}, + {"maps/mp/gametypes/_gamelogic", 0xA793}, {"maps/mp/gametypes/_gameobjects", 0xA794}, + {"maps/mp/gametypes/_gamescores", 0xA795}, + {"maps/mp/gametypes/_globalentities", 0xA796}, {"maps/mp/gametypes/_globallogic", 0xA797}, - {"maps/mp/gametypes/_menus", 0xa7a9}, - {"maps/mp/gametypes/_objpoints", 0xa7ac}, - {"maps/mp/gametypes/_spawnlogic", 0xa7b9}, - {"maps/mp/gametypes/_spawnscoring", 0xa7ba}, - {"matchRules_damageMultiplier", 0x59e6}, - {"matchRules_vampirism", 0x59eb}, - {"modifyPlayerDamage", 0x5d51}, - {"objId", 0x6304}, - {"onForfeit", 0x64af}, - {"onNormalDeath", 0x64bf}, - {"onPlayerScore", 0x64d5}, - {"onStartGameType", 0x64ec}, - {"onUse", 0x64f8}, - {"participants", 0x669d}, - {"reInitializeMatchRulesOnMigration", 0x7307}, - {"registerHalfTimeDvar", 0x72ef}, - {"registerNumLivesDvar", 0x72f4}, - {"registerRoundLimitDvar", 0x72f6}, - {"registerRoundSwitchDvar", 0x72f7}, - {"registerScoreLimitDvar", 0x72f8}, - {"registerTimeLimitDvar", 0x72f9}, - {"registerWinLimitDvar", 0x72fe}, - {"removeFromCharactersArray", 0x73a7}, - {"setClass", 0x7f3b}, - {"setCommonRulesFromMatchRulesData", 0x7f3f}, - {"setObjectiveHintText", 0x7fc3}, - {"setObjectiveScoreText", 0x7fc4}, - {"setObjectiveText", 0x7fc5}, - {"setUseTime", 0x834c}, - {"setupMiniMap", 0x8324}, - {"showToTeam", 0x8535}, - {"spawnDogTags", 0x899e}, - {"spawnMaxs", 0x89f3}, - {"spawnMins", 0x89f6}, - {"spawnPoints", 0x8a01}, - {"splitscreen", 0x8a7c}, - {"tag_stowed_hip", 0x90d3}, - {"tagTeamUpdater", 0x910a}, - {"teamBased", 0x91eb}, - {"teamNameList", 0x91f7}, - {"teamObjIds", 0x6305}, - {"teamSpawnPoints", 0x9201}, - {"v", 0x9c42}, - {"visuals", 0x9e9c}, - {"multiTeamBased", 0x5fec}, + {"maps/mp/gametypes/_hardpoints", 0xA798}, + {"maps/mp/gametypes/_healthoverlay", 0xA799}, + {"maps/mp/gametypes/_hostmigration", 0xA7A4}, + {"maps/mp/gametypes/_hud", 0xA7A5}, + {"maps/mp/gametypes/_hud_message", 0xA7A6}, + {"maps/mp/gametypes/_hud_util", 0xA7A7}, + {"maps/mp/gametypes/_killcam", 0xA7A8}, + {"maps/mp/gametypes/_menus", 0xA7A9}, + {"maos/mp/gametypes/_missions", 0xA7AA}, + {"maps/mp/gametypes/_music_and_dialog", 0xA7AB}, + {"maps/mp/gametypes/_objpoints", 0xA7AC}, + {"maps/mp/gametypes/_presistence", 0xA7AE}, + {"maps/mp/gametypes/_playerlogic", 0xA7B1}, + {"maps/mp/gametypes/_portable_radar", 0xA7B2}, + {"maps/mp/gametypes/_quickmessages", 0xA7B3}, + {"maps/mp/gametypes/_rank", 0xA7B4}, + {"maps/mp/gametypes/_portableaoegenerator", 0xA7B5}, + {"maps/mp/gametypes/_serversettings", 0xA7B6}, + {"maps/mp/gametypes/_shellshock", 0xA7B7}, + {"maps/mp/gametypes/_spawnlogic", 0xA7B9}, + {"maps/mp/gametypes/_spawnscoring", 0xA7BA}, + {"maps/mp/gametypes/_spectating", 0xA7BB}, + {"maps/mp/gametypes/_teams", 0xA7BC}, + {"maps/mp/gametypes/_tweakables", 0xA7BD}, + {"maps/mp/gametypes/_weapons", 0xA7BE}, + {"maps/mp/gametypes/_perkfunctions", 0xA7E5}, + {"maps/mp/gametypes/_perks", 0xA7E6}, }; } diff --git a/src/client/game/scripting/lua/context.cpp b/src/client/game/scripting/lua/context.cpp index 42140a86..12dd2851 100644 --- a/src/client/game/scripting/lua/context.cpp +++ b/src/client/game/scripting/lua/context.cpp @@ -10,14 +10,31 @@ #include "../../../component/logfile.hpp" #include "../../../component/scripting.hpp" #include "../../../component/fastfiles.hpp" +#include "../../../component/scheduler.hpp" #include #include +#include namespace scripting::lua { namespace { + struct http_request + { + sol::protected_function on_error; + sol::protected_function on_progress; + sol::protected_function on_load; + }; + + std::unordered_map http_requests; + + std::string get_as_string(const sol::this_state s, sol::object o) + { + sol::state_view state(s); + return state["tostring"](o); + } + vector normalize_vector(const vector& vec) { const auto length = sqrt( @@ -567,6 +584,162 @@ namespace scripting::lua return sol::lua_value{s, sol::lua_nil}; } }; + + static uint64_t task_id = 0; + + state["http"] = sol::table::create(state.lua_state()); + + state["http"]["get"] = [](const sol::this_state, const std::string& url, + const sol::protected_function& callback, sol::variadic_args va) + { + bool async = false; + + if (va.size() >= 1 && va[0].get_type() == sol::type::boolean) + { + async = va[0].as(); + } + + const auto cur_task_id = task_id++; + auto request_callbacks = &http_requests[cur_task_id]; + request_callbacks->on_load = callback; + + ::scheduler::once([url, cur_task_id]() + { + if (http_requests.find(cur_task_id) == http_requests.end()) + { + return; + } + + const auto data = utils::http::get_data(url); + ::scheduler::once([data, cur_task_id]() + { + if (http_requests.find(cur_task_id) == http_requests.end()) + { + return; + } + + const auto& request_callbacks_ = http_requests[cur_task_id]; + const auto has_value = data.has_value(); + handle_error(request_callbacks_.on_load(has_value ? data.value().buffer : "", has_value)); + http_requests.erase(cur_task_id); + }, ::scheduler::pipeline::server); + }, ::scheduler::pipeline::async); + }; + + state["http"]["request"] = [](const sol::this_state s, const std::string& url, sol::variadic_args va) + { + auto request = sol::table::create(s.lua_state()); + + std::string buffer{}; + std::string fields_string{}; + std::unordered_map headers_map; + + if (va.size() >= 1 && va[0].get_type() == sol::type::table) + { + const auto options = va[0].as(); + + const auto fields = options["parameters"]; + const auto body = options["body"]; + const auto headers = options["headers"]; + + if (fields.get_type() == sol::type::table) + { + const auto _fields = fields.get(); + + for (const auto& field : _fields) + { + const auto key = field.first.as(); + const auto value = get_as_string(s, field.second); + + fields_string += key + "=" + value + "&"; + } + } + + if (body.get_type() == sol::type::string) + { + fields_string = body.get(); + } + + if (headers.get_type() == sol::type::table) + { + const auto _headers = headers.get(); + + for (const auto& header : _headers) + { + const auto key = header.first.as(); + const auto value = get_as_string(s, header.second); + + headers_map[key] = value; + } + } + } + + request["onerror"] = []() {}; + request["onprogress"] = []() {}; + request["onload"] = []() {}; + + request["send"] = [url, fields_string, request, headers_map]() + { + const auto cur_task_id = task_id++; + auto request_callbacks = &http_requests[cur_task_id]; + request_callbacks->on_error = request["onerror"]; + request_callbacks->on_progress = request["onprogress"]; + request_callbacks->on_load = request["onload"]; + + ::scheduler::once([url, fields_string, cur_task_id, headers_map]() + { + if (http_requests.find(cur_task_id) == http_requests.end()) + { + return; + } + + const auto result = utils::http::get_data(url, fields_string, headers_map, [cur_task_id](size_t value) + { + ::scheduler::once([cur_task_id, value]() + { + if (http_requests.find(cur_task_id) == http_requests.end()) + { + return; + } + + const auto& request_callbacks_ = http_requests[cur_task_id]; + handle_error(request_callbacks_.on_progress(value)); + }, ::scheduler::pipeline::server); + }); + + ::scheduler::once([cur_task_id, result]() + { + if (http_requests.find(cur_task_id) == http_requests.end()) + { + return; + } + + const auto& request_callbacks_ = http_requests[cur_task_id]; + + if (!result.has_value()) + { + request_callbacks_.on_error("Unknown", -1); + return; + } + + const auto& http_result = result.value(); + + if (http_result.code == CURLE_OK) + { + handle_error(request_callbacks_.on_load(http_result.buffer)); + } + else + { + handle_error(request_callbacks_.on_error(curl_easy_strerror(http_result.code), http_result.code)); + } + + http_requests.erase(cur_task_id); + }, ::scheduler::pipeline::server); + }, ::scheduler::pipeline::async); + }; + + return request; + }; } } diff --git a/src/client/game/scripting/script_value.cpp b/src/client/game/scripting/script_value.cpp index b99d831f..371693c0 100644 --- a/src/client/game/scripting/script_value.cpp +++ b/src/client/game/scripting/script_value.cpp @@ -2,6 +2,7 @@ #include "script_value.hpp" #include "entity.hpp" #include "array.hpp" +#include "functions.hpp" namespace scripting { @@ -290,4 +291,39 @@ namespace scripting { return this->value_.get(); } + + std::string script_value::to_string() const + { + if (this->is()) + { + return utils::string::va("%i", this->as()); + } + + if (this->is()) + { + return utils::string::va("%f", this->as()); + } + + if (this->is()) + { + return this->as(); + } + + if (this->is()) + { + const auto vec = this->as(); + return utils::string::va("(%g, %g, %g)", + vec.get_x(), + vec.get_y(), + vec.get_z() + ); + } + + if (this->is>()) + { + return utils::string::va("[[ function ]]"); + } + + return this->type_name(); + } } diff --git a/src/client/game/scripting/script_value.hpp b/src/client/game/scripting/script_value.hpp index 27cae79f..d20a01c2 100644 --- a/src/client/game/scripting/script_value.hpp +++ b/src/client/game/scripting/script_value.hpp @@ -3,6 +3,8 @@ #include "variable_value.hpp" #include "vector.hpp" +#include + namespace scripting { class entity; @@ -32,6 +34,14 @@ namespace scripting template bool is() const; + // was gonna do this but no clue if this is the same on H1 so just return string (https://github.com/fedddddd/t6-gsc-utils/blob/main/src/game/scripting/script_value.hpp#L18) + std::string type_name() const + { + return utils::string::va("%s", this->get_raw().type); + } + + std::string to_string() const; + template T as() const { diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index 7ea4d8b4..a75ed125 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -1519,16 +1519,39 @@ namespace game int usesDelta; }; + enum sessionState_t + { + SESS_STATE_PLAYING = 0x0, + SESS_STATE_DEAD = 0x1, + SESS_STATE_SPECTATOR = 0x2, + SESS_STATE_INTERMISSION = 0x3, + }; + + enum team_t + { + TEAM_FREE = 0x0, + TEAM_BAD = 0x0, + TEAM_AXIS = 0x1, + TEAM_ALLIES = 0x2, + TEAM_SPECTATOR = 0x3, + TEAM_NUM_TEAMS = 0x4, + }; + struct gclient_s { char __pad0[2]; char pm_type; // 2 - char __pad1[18831]; + char __pad1[18573]; + sessionState_t sessionState; + char __pad2[220]; // 254 + team_t team; + char __pad3[30]; char name[32]; // 18834 - char __pad2[622]; + char __pad4[622]; int flags; // 19488 }; // size = ? + static_assert(offsetof(gclient_s, team) == 18800); static_assert(offsetof(gclient_s, name) == 18834); static_assert(offsetof(gclient_s, flags) == 19488); @@ -1542,7 +1565,7 @@ namespace game struct EntityState { - uint16_t entityNum; + uint16_t number; }; // size = ? #pragma pack(push, 1) diff --git a/src/client/resources/ui_scripts/common.lua b/src/client/resources/ui_scripts/common.lua index f106207c..2c7bbdf2 100644 --- a/src/client/resources/ui_scripts/common.lua +++ b/src/client/resources/ui_scripts/common.lua @@ -147,6 +147,24 @@ LUI.addmenubutton = function(name, data) end) end +LUI.removemenubutton = function(name, index) + LUI.onmenuopen(name, function(menu) + if (not menu.list) then + return + end + + local buttonlist = menu:getChildById(menu.type .. "_list") + local button = buttonlist.childRecord[string.format("%s_button_%s", name, index)] + buttonlist:removeElement(button) + + local hintbox = menu.optionTextInfo + menu:removeElement(hintbox) + + LUI.Options.InitScrollingList(menu.list, nil) + menu.optionTextInfo = LUI.Options.AddOptionTextInfo(menu) + end) +end + LUI.openmenu = function(menu, args) stack = args LUI.FlowManager.RequestAddMenu(nil, menu) diff --git a/src/common/utils/flags.cpp b/src/common/utils/flags.cpp index 09f13114..a90247ac 100644 --- a/src/common/utils/flags.cpp +++ b/src/common/utils/flags.cpp @@ -21,7 +21,8 @@ namespace utils::flags if (wide_flag[0] == L'-') { wide_flag.erase(wide_flag.begin()); - flags.emplace_back(string::convert(wide_flag)); + const auto flag = string::convert(wide_flag); + flags.emplace_back(string::to_lower(flag)); } } @@ -40,14 +41,7 @@ namespace utils::flags parsed = true; } - for (const auto& entry : enabled_flags) - { - if (string::to_lower(entry) == string::to_lower(flag)) - { - return true; - } - } - - return false; + return std::ranges::any_of(enabled_flags.cbegin(), enabled_flags.cend(), + [flag](const auto& elem) { return elem == string::to_lower(flag); }); } } diff --git a/src/common/utils/http.cpp b/src/common/utils/http.cpp index bdb3da42..06e6ac77 100644 --- a/src/common/utils/http.cpp +++ b/src/common/utils/http.cpp @@ -6,6 +6,32 @@ namespace utils::http { namespace { + struct progress_helper + { + const std::function* callback{}; + std::exception_ptr exception{}; + }; + + int progress_callback(void* clientp, const curl_off_t /*dltotal*/, const curl_off_t dlnow, const curl_off_t /*ultotal*/, const curl_off_t /*ulnow*/) + { + auto* helper = static_cast(clientp); + + try + { + if (*helper->callback) + { + (*helper->callback)(dlnow); + } + } + catch (...) + { + helper->exception = std::current_exception(); + return -1; + } + + return 0; + } + size_t write_callback(void* contents, const size_t size, const size_t nmemb, void* userp) { auto* buffer = static_cast(userp); @@ -16,7 +42,8 @@ namespace utils::http } } - std::optional get_data(const std::string& url) + std::optional get_data(const std::string& url, const std::string& fields, + const headers& headers, const std::function& callback) { curl_slist* header_list = nullptr; auto* curl = curl_easy_init(); @@ -31,13 +58,28 @@ namespace utils::http curl_easy_cleanup(curl); }); + for (const auto& header : headers) + { + auto data = header.first + ": " + header.second; + header_list = curl_slist_append(header_list, data.data()); + } + std::string buffer{}; + progress_helper helper{}; + helper.callback = &callback; curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list); curl_easy_setopt(curl, CURLOPT_URL, url.data()); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer); - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1); + curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback); + curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &helper); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); + + if (!fields.empty()) + { + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, fields.data()); + } const auto code = curl_easy_perform(curl); @@ -49,20 +91,24 @@ namespace utils::http return result; } - else - { - result result; - result.code = code; - return result; + if (helper.exception) + { + std::rethrow_exception(helper.exception); } + + result result; + result.code = code; + + return result; } - std::future> get_data_async(const std::string& url) + std::future> get_data_async(const std::string& url, const std::string& fields, + const headers& headers, const std::function& callback) { - return std::async(std::launch::async, [url]() + return std::async(std::launch::async, [url, fields, headers, callback]() { - return get_data(url); + return get_data(url, fields, headers, callback); }); } } diff --git a/src/common/utils/http.hpp b/src/common/utils/http.hpp index 7361a7b2..bb4c90e5 100644 --- a/src/common/utils/http.hpp +++ b/src/common/utils/http.hpp @@ -11,10 +11,14 @@ namespace utils::http { struct result { - CURLcode code; - std::string buffer; + CURLcode code{}; + std::string buffer{}; }; - std::optional get_data(const std::string& url); - std::future> get_data_async(const std::string& url); + using headers = std::unordered_map; + + std::optional get_data(const std::string& url, const std::string& fields = {}, + const headers& headers = {}, const std::function& callback = {}); + std::future> get_data_async(const std::string& url, const std::string& fields = {}, + const headers& headers = {}, const std::function& callback = {}); } diff --git a/src/common/utils/info_string.cpp b/src/common/utils/info_string.cpp index 3b0287e3..56ed56ab 100644 --- a/src/common/utils/info_string.cpp +++ b/src/common/utils/info_string.cpp @@ -26,7 +26,7 @@ namespace utils return value->second; } - return ""; + return {}; } void info_string::parse(std::string buffer) @@ -49,15 +49,15 @@ namespace utils { //auto first = true; std::string info_string; - for (auto i = this->key_value_pairs_.begin(); i != this->key_value_pairs_.end(); ++i) + for (const auto& [key, val] : this->key_value_pairs_) { //if (first) first = false; /*else*/ info_string.append("\\"); - info_string.append(i->first); // Key + info_string.append(key); info_string.append("\\"); - info_string.append(i->second); // Value + info_string.append(val); } return info_string; diff --git a/src/common/utils/io.cpp b/src/common/utils/io.cpp index 9b161d39..52083ac9 100644 --- a/src/common/utils/io.cpp +++ b/src/common/utils/io.cpp @@ -32,7 +32,7 @@ namespace utils::io if (stream.is_open()) { - stream.write(data.data(), data.size()); + stream.write(data.data(), static_cast(data.size())); stream.close(); return true; } diff --git a/src/common/utils/memory.cpp b/src/common/utils/memory.cpp index 99a03916..12b36e70 100644 --- a/src/common/utils/memory.cpp +++ b/src/common/utils/memory.cpp @@ -72,7 +72,7 @@ namespace utils void* memory::allocate(const size_t length) { - return calloc(length, 1); + return std::calloc(length, 1); } char* memory::duplicate_string(const std::string& string) @@ -86,7 +86,7 @@ namespace utils { if (data) { - ::free(data); + std::free(data); } } diff --git a/tools/protoc.exe b/tools/protoc.exe index 87bf60b6..cc258a52 100644 Binary files a/tools/protoc.exe and b/tools/protoc.exe differ