Merge pull request #440 from fedddddd/develop

v3.0.2
This commit is contained in:
fed 2023-03-04 23:05:10 +01:00 committed by GitHub
commit 684fe89968
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
84 changed files with 1725 additions and 306 deletions

View File

@ -33,6 +33,10 @@
* Freeing up RAM (close programs)
* Updating your GPU drivers
**Do NOT open issues to ask for support!**
The issues tab is meant for bugs that have to be fixed or features that should be implemented.
Any issue mentioning things like "h2-mod doesn't work" with no other information included will be closed.
## Compile from source
- Clone the Git repo. Do NOT download it as ZIP, that won't work.

View File

@ -1,2 +1 @@
require("credits")
require("mainmenu")

View File

@ -1,4 +0,0 @@
LUI.onmenuopen("main_campaign", function(menu)
local headertext = menu:getFirstDescendentById("header_text")
headertext:setText("H2-MOD")
end)

View File

@ -0,0 +1,2 @@
require("motd")
require("featured")

View File

@ -0,0 +1,581 @@
motd.getfeaturedtabtitle = function(index)
return motd.getfeaturedtab(index).tab_title or ""
end
local animmsfull = 150
local animms = animmsfull / 2
local minimized = false
LUI.onmenuopen("main_campaign", function(menu)
if (mods.getloaded() ~= nil or motd.getnumfeaturedtabs() <= 0) then
return
end
local left = 885
local featuredcontainer = LUI.UIElement.new({
leftAnchor = true,
topAnchor = true,
left = left,
top = 420,
})
featuredcontainer:registerAnimationState("hide", {
leftAnchor = true,
topAnchor = true,
left = left,
top = 570,
})
featuredcontainer:registerAnimationState("show", {
leftAnchor = true,
topAnchor = true,
left = left,
top = 420,
})
local featuredstencil = LUI.UIStencilText.new({
leftAnchor = true,
topAnchor = true,
height = 200,
width = GenericMenuDims.menu_width_standard + (10 + 12) * 2,
})
local featured = LUI.UIElement.new({
left = 20,
leftAnchor = true,
topAnchor = true,
bottomAnchor = true,
rightAnchor = true
})
local arrowcontainer = LUI.UIElement.new({
leftAnchor = true,
topAnchor = true,
bottomAnchor = true,
rightAnchor = true
})
arrowcontainer:registerAnimationState("hide", {
alpha = 0
})
arrowcontainer:registerAnimationState("show", {
alpha = 1
})
featured:addElement(arrowcontainer)
featuredstencil:registerAnimationState("hide", {
leftAnchor = true,
topAnchor = true,
height = GenericButtonSettings.Styles.FlatButton.height + 2,
width = GenericMenuDims.menu_width_standard + (10 + 12) * 2,
})
local istopmost = function()
return LUI.FlowManager.IsMenuTopmost(Engine.GetLuiRoot(), "main_campaign")
end
featuredstencil:registerAnimationState("show", {
leftAnchor = true,
topAnchor = true,
height = 200,
width = GenericMenuDims.menu_width_standard + (10 + 12) * 2,
})
local hoverelem = LUI.UIElement.new({
leftAnchor = true,
topAnchor = true,
top = -10,
height = 150 + GenericButtonSettings.Styles.FlatButton.height + 2 + 10,
width = GenericMenuDims.menu_width_standard + 50,
left = -25
})
local header = LUI.UIElement.new({
topAnchor = true,
leftAnchor = true,
width = GenericMenuDims.menu_width_standard,
height = GenericButtonSettings.Styles.FlatButton.height,
})
local headerbg = LUI.UIImage.new({
topAnchor = true,
leftAnchor = true,
bottomAnchor = true,
rightAnchor = true,
alpha = 0.55,
color = Colors.grey_14,
material = RegisterMaterial("white")
})
local headerbutton = LUI.UIImage.new({
leftAnchor = true,
topAnchor = true,
height = 10,
width = 10,
top = 10.5,
left = 10,
zRot = -90,
color = {
r = 0.6,
g = 0.6,
b = 0.6,
},
material = RegisterMaterial("widg_lobby_arrow")
})
headerbutton:registerEventHandler("mouseenter", function()
if (not istopmost()) then
return
end
Engine.PlaySound(CoD.SFX.MouseOver)
headerbutton:animateToState("focused")
end)
headerbutton:registerEventHandler("mouseleave", function()
headerbutton:animateToState("unfocused")
end)
headerbutton:registerAnimationState("down", {
zRot = -90
})
headerbutton:registerAnimationState("right", {
zRot = 0
})
headerbutton:registerAnimationState("focused", {
color = Colors.h2.yellow
})
headerbutton:registerAnimationState("unfocused", {
color = {
r = 0.6,
g = 0.6,
b = 0.6,
}
})
local tabcount = motd.getnumfeaturedtabs()
headerbutton:setHandleMouse(true)
headerbutton:registerEventHandler("leftmousedown", function()
Engine.PlaySound(CoD.SFX.MenuAccept)
headerbutton:processEvent({
name = "mouseleave"
})
minimized = not minimized
if (minimized) then
featuredstencil:animateToState("hide", animms)
featuredcontainer:animateToState("hide", animms)
headerbutton:animateToState("right", animms)
arrowcontainer:animateToState("hide", animms)
else
featuredstencil:animateToState("show", animms)
featuredcontainer:animateToState("show", animms)
headerbutton:animateToState("down", animms)
if (tabcount > 1) then
arrowcontainer:animateToState("show", animms)
end
end
end)
if (minimized) then
featuredstencil:animateToState("hide")
featuredcontainer:animateToState("hide")
headerbutton:animateToState("right")
arrowcontainer:animateToState("hide")
end
header:addElement(headerbg)
header:addElement(headerbutton)
local headertext = LUI.UIText.new({
leftAnchor = true,
left = GenericButtonSettings.Styles.FlatButton.text_padding_with_content + 10,
top = -CoD.TextSettings.Font19.Height / 2 + 1.5,
font = CoD.TextSettings.Font19.Font,
height = CoD.TextSettings.Font19.Height
})
local rightoffset = -15
local pips = {}
local focusindex = 0
local shiftto = nil
for i = 1, tabcount do
local pipmat = RegisterMaterial("h1_ui_featured_pip_unfocused")
local ratio = Engine.GetMaterialAspectRatio(pipmat)
local height = 14
local pip = LUI.UIImage.new({
topAnchor = true,
rightAnchor = true,
top = GenericButtonSettings.Styles.FlatButton.height / 2 - height / 2,
width = ratio * height,
height = height,
material = RegisterMaterial("h1_ui_featured_pip_unfocused"),
right = rightoffset
})
pip:setHandleMouse(true)
pip:registerEventHandler("mouseenter", function()
if (not istopmost() or minimized) then
return
end
Engine.PlaySound(CoD.SFX.MouseOver)
end)
pip:registerEventHandler("leftmousedown", function()
if (not istopmost() or minimized) then
return
end
Engine.PlaySound(CoD.SFX.MenuAccept)
shiftto(#pips - i)
end)
pip:registerAnimationState("focused", {
material = RegisterMaterial("h2_ui_featured_pip_focused"),
})
pip:registerAnimationState("unfocused", {
material = RegisterMaterial("h1_ui_featured_pip_unfocused"),
})
table.insert(pips, pip)
header:addElement(pip)
rightoffset = rightoffset - height
end
pips[#pips]:animateToState("focused")
headertext:setText(Engine.ToUpperCase(Engine.Localize("@LUA_MENU_FEATURED")))
local content = LUI.UIElement.new({
topAnchor = true,
leftAnchor = true,
top = GenericButtonSettings.Styles.FlatButton.height + 3,
width = GenericMenuDims.menu_width_standard,
height = 150,
})
local stencil = LUI.UIStencilText.new({
topAnchor = true,
leftAnchor = true,
bottomAnchor = true,
rightAnchor = true,
left = 1,
})
local imagelist = LUI.UIHorizontalList.new( {
left = 0,
leftAnchor = true,
width = (GenericMenuDims.menu_width_standard) * tabcount,
height = 150,
spacing = 0
} )
stencil:addElement(imagelist)
local contentborder = LUI.UIImage.new({
topAnchor = true,
leftAnchor = true,
bottomAnchor = true,
rightAnchor = true,
alpha = 0,
material = RegisterMaterial("h2_ui_btn_focused_stroke_square")
})
contentborder:registerAnimationState("unfocused", {
alpha = 0
})
contentborder:registerAnimationState("focused", {
alpha = 1
})
contentborder:setup9SliceImage()
local shift = function()
imagelist:registerAnimationState("move", {
leftAnchor = true,
left = (focusindex) * -GenericMenuDims.menu_width_standard
})
imagelist:animateToState("move", animmsfull)
end
shiftto = function(index)
local prevfocus = focusindex
focusindex = (index) % tabcount
pips[#pips - (prevfocus)]:animateToState("unfocused")
pips[#pips - (focusindex)]:animateToState("focused")
shift()
end
local shiftright = function()
local prevfocus = focusindex
focusindex = (focusindex + 1) % tabcount
pips[#pips - (prevfocus)]:animateToState("unfocused")
pips[#pips - (focusindex)]:animateToState("focused")
shift()
end
local shiftleft = function()
local prevfocus = focusindex
focusindex = (focusindex - 1) % tabcount
pips[#pips - (prevfocus)]:animateToState("unfocused")
pips[#pips - (focusindex)]:animateToState("focused")
shift()
end
for i = 1, tabcount do
local panel = LUI.UIElement.new({
topAnchor = true,
leftAnchor = true,
bottomAnchor = true,
width = GenericMenuDims.menu_width_standard,
})
local text = LUI.UIText.new({
bottomAnchor = true,
leftAnchor = true,
rightAnchor = true,
alignment = LUI.Alignment.Center,
bottom = (-CoD.TextSettings.Font21.Height / 2) + 2,
height = CoD.TextSettings.Font21.Height,
font = CoD.TextSettings.Font21.Font,
})
local textbg = LUI.UIImage.new({
bottomAnchor = true,
leftAnchor = true,
rightAnchor = true,
height = CoD.TextSettings.Font21.Height * 2,
alpha = 0.75,
material = RegisterMaterial("black")
})
text:setText(motd.getfeaturedtabtitle(i - 1))
local material = RegisterMaterial("featured_panel_thumbnail_" .. i)
local ratio = Engine.GetMaterialAspectRatio(material)
local width = GenericMenuDims.menu_width_standard
local height = width / ratio
local contentimage = LUI.UIImage.new({
topAnchor = true,
leftAnchor = true,
width = width,
height = height,
material = material,
})
panel:addElement(contentimage)
panel:addElement(textbg)
panel:addElement(text)
imagelist:addElement(panel)
end
local autoscrolltimer = LUI.UITimer.new(3000, "autoscroll")
featured:addElement(autoscrolltimer)
featured:registerEventHandler("autoscroll", function()
if (not minimized) then
shiftright()
end
end)
local addarrows = function()
local arrowmat = RegisterMaterial("h1_prestige_leftright_arrow")
local arrowratio = Engine.GetMaterialAspectRatio(arrowmat)
local height = 20
local width = arrowratio * height
local arrowleft = LUI.UIImage.new({
leftAnchor = true,
left = -width - 10,
top = -10,
height = height,
width = width,
alpha = 0,
material = RegisterMaterial("h1_prestige_leftright_arrow")
})
local arrowright = LUI.UIImage.new({
rightAnchor = true,
right = 0,
top = -10,
height = height,
width = width,
zRot = 180,
alpha = 0,
material = RegisterMaterial("h1_prestige_leftright_arrow")
})
local registeranimationstate = function(name, state)
arrowleft:registerAnimationState(name, state)
arrowright:registerAnimationState(name, state)
end
registeranimationstate("pulse", {
scale = -0.1
})
registeranimationstate("highlight", {
scale = 0
})
registeranimationstate("focused", {
alpha = 1
})
registeranimationstate("unfocused", {
alpha = 0
})
arrowleft:setHandleMouse(true)
arrowright:setHandleMouse(true)
local pulsearrow = function(arrow)
arrow:animateInSequence({
{
"highlight",
0
},
{
"pulse",
100
},
{
"highlight",
100
}
})
end
arrowleft:registerEventHandler("leftmousedown", function()
if (not istopmost()) then
return
end
pulsearrow(arrowleft)
Engine.PlaySound(CoD.SFX.MouseOver)
shiftleft()
end)
arrowright:registerEventHandler("leftmousedown", function()
if (not istopmost()) then
return
end
pulsearrow(arrowright)
Engine.PlaySound(CoD.SFX.MouseOver)
shiftright()
end)
arrowcontainer:addElement(arrowleft)
arrowcontainer:addElement(arrowright)
featured.arrowleft = arrowleft
featured.arrowright = arrowright
end
addarrows()
if (tabcount <= 1) then
arrowcontainer:animateToState("hide")
end
featured:registerEventHandler("focused", function()
contentborder:animateToState("focused", animms)
featured.arrowleft:animateToState("focused", animms)
featured.arrowright:animateToState("focused", animms)
LUI.UITimer.Stop(autoscrolltimer)
end)
featured:registerEventHandler("unfocused", function()
contentborder:animateToState("unfocused", animms)
featured.arrowleft:animateToState("unfocused", animms)
featured.arrowright:animateToState("unfocused", animms)
LUI.UITimer.Reset(autoscrolltimer)
end)
hoverelem:setHandleMouseMove(true)
hoverelem:registerEventHandler("mouseenter", function()
if (not istopmost()) then
return
end
featured:processEvent({
name = "focused",
dispatchChildren = true
})
end)
hoverelem:registerEventHandler("mouseleave", function()
featured:processEvent({
name = "unfocused",
dispatchChildren = true
})
end)
header:addElement(headertext)
header:addElement(LUI.DecoFrame.new(nil, LUI.DecoFrame.Grey))
featured:addElement(header)
content:addElement(stencil)
content:addElement(LUI.DecoFrame.new(nil, LUI.DecoFrame.Grey))
content:addElement(contentborder)
content:setHandleMouse(true)
content:registerEventHandler("leftmousedown", function()
if (not istopmost() or minimized) then
return
end
hoverelem:processEvent({
name = "mouseleave",
})
Engine.PlaySound(CoD.SFX.MenuAccept)
local data = motd.getfeaturedtab(focusindex)
data.popup_image = "featured_panel_" .. (focusindex + 1)
LUI.FlowManager.RequestPopupMenu( nil, "motd_main", true, nil, false, {
popupDataQueue = {data}
})
end)
content:registerEventHandler("mouseenter", function()
if (not istopmost() or minimized) then
return
end
Engine.PlaySound(CoD.SFX.MouseOver)
end)
featured:addElement(content)
featured:addElement(hoverelem)
featuredstencil:addElement(featured)
featuredcontainer:addElement(featuredstencil)
menu:addElement(featuredcontainer)
end)

View File

@ -0,0 +1,71 @@
require("LUI.common_menus.MarketingComms")
require("LUI.common_menus.MarketingPopup")
LUI.CustomMarketingPopups = {ShowDepotOnboardingPopupIfPossible = function() end}
LUI.MenuBuilder.registerPopupType("motd", function()
local data = motd.getmotd()
return LUI.MenuBuilder.BuildRegisteredType("motd_main", {
popupDataQueue = {data}
})
end)
LUI.onmenuopen("main_campaign", function(menu)
if (not motd.hasseentoday()) then
motd.sethasseentoday()
LUI.FlowManager.RequestPopupMenu(nil, "motd")
end
menu:AddHelp({
name = "add_button_helper_text",
button_ref = "button_alt2",
helper_text = Engine.Localize("@MENU_OPEN_MOTD"),
side = "right",
clickable = true
}, function()
LUI.FlowManager.RequestPopupMenu(nil, "motd")
end)
end)
LUI.common_menus.MarketingPopup.OnPopupAction = function(a1, a2)
local data = a1.popupData
if (type(data.link) == "string") then
game:openlink(data.link)
end
end
local marketingbase = LUI.MarketingPopup.Base
LUI.MarketingPopup.Base = function(a1, data, a3)
local haslink = data.popupAction ~= nil and game:islink(data.popupAction)
if (haslink) then
data.link = data.popupAction
data.popupAction = "depot"
end
local element = marketingbase(a1, data, a3)
local blur = element:getFirstDescendentById("generic_popup_screen_overlay_blur"):getNextSibling()
local parent = blur:getFirstChild():getNextSibling():getNextSibling():getNextSibling()
local image = parent:getFirstChild()
image:close()
local state = LUI.DeepCopy(image:getAnimationStateInC("default"))
local imagecontainer = LUI.UIStencilText.new(state)
local material = RegisterMaterial(data.image)
local ratio = Engine.GetMaterialAspectRatio(material)
local width = 525
local height = width / ratio
local image = LUI.UIImage.new({
leftAnchor = true,
topAnchor = true,
width = width,
height = height,
material = material
})
imagecontainer:addElement(image)
parent:addElement(imagecontainer)
return element
end

View File

@ -24,6 +24,14 @@ local function togglecustomfonts()
})
end
local function adjustverticaloffset(table, offset)
for k, v in pairs(table) do
if (table[k].y_offset ~= nil) then
table[k].y_offset = offset
end
end
end
LUI.MenuBuilder.registerType("choose_language_menu", function(a1)
local menu = LUI.MenuTemplate.new(a1, {
menu_title = "@LUA_MENU_CHOOSE_LANGUAGE",
@ -90,20 +98,23 @@ LUI.MenuBuilder.registerType("choose_language_menu", function(a1)
end)
-- fix for Y-offset in button text (russian_partial, default font)
if config.get("language") == "russian_partial" and config.get("disable_custom_fonts") then
if (config.get("language") == "russian_partial" and config.get("disable_custom_fonts")) then
LUI.UIButtonText.IsOffsetedLanguage = function()
return true
end
end
if not (config.get("disable_custom_fonts")) then
if (not (config.get("disable_custom_fonts"))) then
-- fix for Y-offset in button text (global patch, custom font)
adjustverticaloffset(GenericButtonSettings.Styles, 2)
adjustverticaloffset(GenericButtonSettings.Styles.GlassButton.SubStyles, 2)
LUI.UIButtonText.IsOffsetedLanguage = function()
return false
end
-- fix for ammo counter (polish/russian)
if not Engine.InFrontend() then
if (not Engine.InFrontend()) then
local weaponinfodef = LUI.MenuBuilder.m_definitions["WeaponInfoHudDef"]
LUI.MenuBuilder.m_definitions["WeaponInfoHudDef"] = function(...)
local rus = CoD.Language.Russian
@ -131,29 +142,29 @@ if not (config.get("disable_custom_fonts")) then
end
-- fix for ammo counter (global patch)
if language.isnonlatin() then
if (language.isnonlatin()) then
local scale = function(size)
return size * 720 / 1080
end
CoD.TextSettings.SP_HudAmmoStatusText = {
Font = RegisterFont("fonts/mix_gothic.ttf", 16),
Font = RegisterFont("fonts/bank.ttf", 16),
Height = 16
}
CoD.TextSettings.SP_HudAmmoCounterFont = {
Font = RegisterFont("fonts/mix_gothic.ttf", 34),
Font = RegisterFont("fonts/bank.ttf", 34),
Height = 34
}
CoD.TextSettings.HudAmmoCounterFont = {
Font = RegisterFont("fonts/mix_gothic.ttf", 105),
Font = RegisterFont("fonts/bank.ttf", 105),
Height = 64
}
-- forced gothic font for headers (arabic/slavic)
if language.isslavic() or language.isarabic() then
if (language.isslavic() or language.isarabic()) then
CoD.TextSettings.H2TitleFont = {
Font = RegisterFont("fonts/mix_gothic.ttf", 56),
Font = RegisterFont("fonts/bank.ttf", 56),
Height = scale(56)
}
end

View File

@ -0,0 +1 @@
ttf,fonts/defaultBold.otf
1 ttf fonts/defaultBold.otf

View File

@ -1,4 +1,6 @@
ara_h2_mod_common
ara_h2_mod_font_default_bold
cze_h2_mod_common
deu_h2_mod_common
eng_h2_mod_common
eng_h2_mod_patch_af_caves
@ -6,10 +8,15 @@ ens_h2_mod_common
ens_h2_mod_patch_af_caves
fra_h2_mod_common
h2_mod_common
h2_mod_font_bank
h2_mod_font_default
h2_mod_font_default_bold
h2_mod_font_mix
h2_mod_patch_af_caves
h2_mod_patch_dc_whitehouse
h2_mod_patch_ending
h2_mod_pre_gfx
h2_mod_ui
ita_h2_mod_common
jpf_h2_mod_common
jpp_h2_mod_common

View File

@ -0,0 +1 @@
ttf,fonts/bank.ttf
1 ttf fonts/bank.ttf

View File

@ -0,0 +1 @@
ttf,fonts/default.otf
1 ttf fonts/default.otf

View File

@ -0,0 +1 @@
ttf,fonts/defaultBold.otf
1 ttf fonts/defaultBold.otf

View File

@ -0,0 +1 @@
ttf,fonts/mix.ttf
1 ttf fonts/mix.ttf

View File

@ -1,5 +1,2 @@
stringtable,font_replacements.csv
ttf,fonts/ibmplexsansarabic-semibold_custom.ttf
ttf,fonts/mix.ttf
ttf,fonts/mix_gothic.ttf
ttf,fonts/mix_open.ttf
stringtable,font_zones.csv
localize,branding

1 stringtable font_replacements.csv font_zones.csv
2 ttf localize fonts/ibmplexsansarabic-semibold_custom.ttf branding
ttf fonts/mix.ttf
ttf fonts/mix_gothic.ttf
ttf fonts/mix_open.ttf

View File

@ -0,0 +1,21 @@
material,h2_ui_featured_pip_focused
material,h1_ui_featured_pip_unfocused
material,h2_ui_btn_focused_stroke_square
material,h1_prestige_leftright_arrow
material,featured_panel_1
material,featured_panel_2
material,featured_panel_3
material,featured_panel_4
material,featured_panel_5
material,featured_panel_6
material,featured_panel_7
material,featured_panel_8
material,featured_panel_thumbnail_1
material,featured_panel_thumbnail_2
material,featured_panel_thumbnail_3
material,featured_panel_thumbnail_4
material,featured_panel_thumbnail_5
material,featured_panel_thumbnail_6
material,featured_panel_thumbnail_7
material,featured_panel_thumbnail_8
material,motd_image
1 material h2_ui_featured_pip_focused
2 material h1_ui_featured_pip_unfocused
3 material h2_ui_btn_focused_stroke_square
4 material h1_prestige_leftright_arrow
5 material featured_panel_1
6 material featured_panel_2
7 material featured_panel_3
8 material featured_panel_4
9 material featured_panel_5
10 material featured_panel_6
11 material featured_panel_7
12 material featured_panel_8
13 material featured_panel_thumbnail_1
14 material featured_panel_thumbnail_2
15 material featured_panel_thumbnail_3
16 material featured_panel_thumbnail_4
17 material featured_panel_thumbnail_5
18 material featured_panel_thumbnail_6
19 material featured_panel_thumbnail_7
20 material featured_panel_thumbnail_8
21 material motd_image

BIN
data/zonetool/fonts/mix.ttf Normal file

Binary file not shown.

View File

@ -1,25 +0,0 @@
arabic,fonts/bank.ttf,fonts/mix_gothic.ttf,
arabic,fonts/default.otf,fonts/mix.ttf,
arabic,fonts/defaultBold.otf,fonts/ibmplexsansarabic-semibold_custom.ttf,
czech,fonts/bank.ttf,fonts/mix_gothic.ttf,
czech,fonts/default.otf,fonts/mix.ttf,
czech,fonts/defaultBold.otf,fonts/mix_open.ttf,
japanese_full,fonts/default.otf,fonts/mix.ttf,
japanese_full,fonts/defaultBold.otf,fonts/mix_open.ttf,
japanese_partial,fonts/default.otf,fonts/mix.ttf,
japanese_partial,fonts/defaultBold.otf,fonts/mix_open.ttf,
korean,fonts/default.otf,fonts/mix.ttf,
korean,fonts/defaultBold.otf,fonts/mix_open.ttf,
polish,fonts/bank.ttf,fonts/mix_gothic.ttf,
polish,fonts/default.otf,fonts/mix.ttf,
polish,fonts/defaultBold.otf,fonts/mix_open.ttf,
russian,fonts/bank.ttf,fonts/mix_gothic.ttf,
russian,fonts/default.otf,fonts/mix.ttf,
russian,fonts/defaultBold.otf,fonts/mix_open.ttf,
russian_partial,fonts/bank.ttf,fonts/mix_gothic.ttf,
russian_partial,fonts/default.otf,fonts/mix.ttf,
russian_partial,fonts/defaultBold.otf,fonts/mix_open.ttf,
simplified_chinese,fonts/default.otf,fonts/mix.ttf,
simplified_chinese,fonts/defaultBold.otf,fonts/mix_open.ttf,
traditional_chinese,fonts/default.otf,fonts/mix.ttf,
traditional_chinese,fonts/defaultBold.otf,fonts/mix_open.ttf,
1 arabic fonts/bank.ttf fonts/mix_gothic.ttf
2 arabic fonts/default.otf fonts/mix.ttf
3 arabic fonts/defaultBold.otf fonts/ibmplexsansarabic-semibold_custom.ttf
4 czech fonts/bank.ttf fonts/mix_gothic.ttf
5 czech fonts/default.otf fonts/mix.ttf
6 czech fonts/defaultBold.otf fonts/mix_open.ttf
7 japanese_full fonts/default.otf fonts/mix.ttf
8 japanese_full fonts/defaultBold.otf fonts/mix_open.ttf
9 japanese_partial fonts/default.otf fonts/mix.ttf
10 japanese_partial fonts/defaultBold.otf fonts/mix_open.ttf
11 korean fonts/default.otf fonts/mix.ttf
12 korean fonts/defaultBold.otf fonts/mix_open.ttf
13 polish fonts/bank.ttf fonts/mix_gothic.ttf
14 polish fonts/default.otf fonts/mix.ttf
15 polish fonts/defaultBold.otf fonts/mix_open.ttf
16 russian fonts/bank.ttf fonts/mix_gothic.ttf
17 russian fonts/default.otf fonts/mix.ttf
18 russian fonts/defaultBold.otf fonts/mix_open.ttf
19 russian_partial fonts/bank.ttf fonts/mix_gothic.ttf
20 russian_partial fonts/default.otf fonts/mix.ttf
21 russian_partial fonts/defaultBold.otf fonts/mix_open.ttf
22 simplified_chinese fonts/default.otf fonts/mix.ttf
23 simplified_chinese fonts/defaultBold.otf fonts/mix_open.ttf
24 traditional_chinese fonts/default.otf fonts/mix.ttf
25 traditional_chinese fonts/defaultBold.otf fonts/mix_open.ttf

View File

@ -0,0 +1,11 @@
*,h2_mod_font_mix
arabic,h2_mod_font_bank,h2_mod_font_default,h2_mod_font_default_bold
czech,h2_mod_font_bank,h2_mod_font_default,h2_mod_font_default_bold
japanese_full,h2_mod_font_default,h2_mod_font_default_bold
japanese_partial,h2_mod_font_default,h2_mod_font_default_bold
korean,h2_mod_font_default,h2_mod_font_default_bold
polish,h2_mod_font_bank,h2_mod_font_default,h2_mod_font_default_bold
russian,h2_mod_font_bank,h2_mod_font_default,h2_mod_font_default_bold
russian_partial,h2_mod_font_bank,h2_mod_font_default,h2_mod_font_default_bold
simplified_chinese,h2_mod_font_default,h2_mod_font_default_bold
traditional_chinese,h2_mod_font_default,h2_mod_font_default_bold
1 *,h2_mod_font_mix
2 arabic,h2_mod_font_bank,h2_mod_font_default,h2_mod_font_default_bold
3 czech,h2_mod_font_bank,h2_mod_font_default,h2_mod_font_default_bold
4 japanese_full,h2_mod_font_default,h2_mod_font_default_bold
5 japanese_partial,h2_mod_font_default,h2_mod_font_default_bold
6 korean,h2_mod_font_default,h2_mod_font_default_bold
7 polish,h2_mod_font_bank,h2_mod_font_default,h2_mod_font_default_bold
8 russian,h2_mod_font_bank,h2_mod_font_default,h2_mod_font_default_bold
9 russian_partial,h2_mod_font_bank,h2_mod_font_default,h2_mod_font_default_bold
10 simplified_chinese,h2_mod_font_default,h2_mod_font_default_bold
11 traditional_chinese,h2_mod_font_default,h2_mod_font_default_bold

View File

@ -0,0 +1,3 @@
{
"MENU_SP_CAMPAIGN": "H2-MOD"
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,26 @@
{
"name": "featured_panel_1",
"techniqueSet->name": "2d",
"gameFlags": 0,
"sortKey": 60,
"renderFlags": 0,
"textureAtlasRowCount": 1,
"textureAtlasColumnCount": 1,
"textureAtlasFrameBlend": 0,
"textureAtlasAsArray": 0,
"surfaceTypeBits": 0,
"cameraRegion": 12,
"materialType": 0,
"assetFlags": 0,
"constantTable": null,
"textureTable": [
{
"image": "featured_panel_1",
"semantic": 0,
"samplerState": 226,
"lastCharacter": 112,
"firstCharacter": 99,
"typeHash": 2695565377
}
]
}

View File

@ -0,0 +1,26 @@
{
"name": "featured_panel_2",
"techniqueSet->name": "2d",
"gameFlags": 0,
"sortKey": 60,
"renderFlags": 0,
"textureAtlasRowCount": 1,
"textureAtlasColumnCount": 1,
"textureAtlasFrameBlend": 0,
"textureAtlasAsArray": 0,
"surfaceTypeBits": 0,
"cameraRegion": 12,
"materialType": 0,
"assetFlags": 0,
"constantTable": null,
"textureTable": [
{
"image": "featured_panel_2",
"semantic": 0,
"samplerState": 226,
"lastCharacter": 112,
"firstCharacter": 99,
"typeHash": 2695565377
}
]
}

View File

@ -0,0 +1,26 @@
{
"name": "featured_panel_3",
"techniqueSet->name": "2d",
"gameFlags": 0,
"sortKey": 60,
"renderFlags": 0,
"textureAtlasRowCount": 1,
"textureAtlasColumnCount": 1,
"textureAtlasFrameBlend": 0,
"textureAtlasAsArray": 0,
"surfaceTypeBits": 0,
"cameraRegion": 12,
"materialType": 0,
"assetFlags": 0,
"constantTable": null,
"textureTable": [
{
"image": "featured_panel_3",
"semantic": 0,
"samplerState": 226,
"lastCharacter": 112,
"firstCharacter": 99,
"typeHash": 2695565377
}
]
}

View File

@ -0,0 +1,26 @@
{
"name": "featured_panel_4",
"techniqueSet->name": "2d",
"gameFlags": 0,
"sortKey": 60,
"renderFlags": 0,
"textureAtlasRowCount": 1,
"textureAtlasColumnCount": 1,
"textureAtlasFrameBlend": 0,
"textureAtlasAsArray": 0,
"surfaceTypeBits": 0,
"cameraRegion": 12,
"materialType": 0,
"assetFlags": 0,
"constantTable": null,
"textureTable": [
{
"image": "featured_panel_4",
"semantic": 0,
"samplerState": 226,
"lastCharacter": 112,
"firstCharacter": 99,
"typeHash": 2695565377
}
]
}

View File

@ -0,0 +1,26 @@
{
"name": "featured_panel_5",
"techniqueSet->name": "2d",
"gameFlags": 0,
"sortKey": 60,
"renderFlags": 0,
"textureAtlasRowCount": 1,
"textureAtlasColumnCount": 1,
"textureAtlasFrameBlend": 0,
"textureAtlasAsArray": 0,
"surfaceTypeBits": 0,
"cameraRegion": 12,
"materialType": 0,
"assetFlags": 0,
"constantTable": null,
"textureTable": [
{
"image": "featured_panel_5",
"semantic": 0,
"samplerState": 226,
"lastCharacter": 112,
"firstCharacter": 99,
"typeHash": 2695565377
}
]
}

View File

@ -0,0 +1,26 @@
{
"name": "featured_panel_6",
"techniqueSet->name": "2d",
"gameFlags": 0,
"sortKey": 60,
"renderFlags": 0,
"textureAtlasRowCount": 1,
"textureAtlasColumnCount": 1,
"textureAtlasFrameBlend": 0,
"textureAtlasAsArray": 0,
"surfaceTypeBits": 0,
"cameraRegion": 12,
"materialType": 0,
"assetFlags": 0,
"constantTable": null,
"textureTable": [
{
"image": "featured_panel_6",
"semantic": 0,
"samplerState": 226,
"lastCharacter": 112,
"firstCharacter": 99,
"typeHash": 2695565377
}
]
}

View File

@ -0,0 +1,26 @@
{
"name": "featured_panel_7",
"techniqueSet->name": "2d",
"gameFlags": 0,
"sortKey": 60,
"renderFlags": 0,
"textureAtlasRowCount": 1,
"textureAtlasColumnCount": 1,
"textureAtlasFrameBlend": 0,
"textureAtlasAsArray": 0,
"surfaceTypeBits": 0,
"cameraRegion": 12,
"materialType": 0,
"assetFlags": 0,
"constantTable": null,
"textureTable": [
{
"image": "featured_panel_7",
"semantic": 0,
"samplerState": 226,
"lastCharacter": 112,
"firstCharacter": 99,
"typeHash": 2695565377
}
]
}

View File

@ -0,0 +1,26 @@
{
"name": "featured_panel_8",
"techniqueSet->name": "2d",
"gameFlags": 0,
"sortKey": 60,
"renderFlags": 0,
"textureAtlasRowCount": 1,
"textureAtlasColumnCount": 1,
"textureAtlasFrameBlend": 0,
"textureAtlasAsArray": 0,
"surfaceTypeBits": 0,
"cameraRegion": 12,
"materialType": 0,
"assetFlags": 0,
"constantTable": null,
"textureTable": [
{
"image": "featured_panel_8",
"semantic": 0,
"samplerState": 226,
"lastCharacter": 112,
"firstCharacter": 99,
"typeHash": 2695565377
}
]
}

View File

@ -0,0 +1,26 @@
{
"name": "featured_panel_thumbnail_1",
"techniqueSet->name": "2d",
"gameFlags": 0,
"sortKey": 60,
"renderFlags": 0,
"textureAtlasRowCount": 1,
"textureAtlasColumnCount": 1,
"textureAtlasFrameBlend": 0,
"textureAtlasAsArray": 0,
"surfaceTypeBits": 0,
"cameraRegion": 12,
"materialType": 0,
"assetFlags": 0,
"constantTable": null,
"textureTable": [
{
"image": "featured_panel_thumbnail_1",
"semantic": 0,
"samplerState": 226,
"lastCharacter": 112,
"firstCharacter": 99,
"typeHash": 2695565377
}
]
}

View File

@ -0,0 +1,26 @@
{
"name": "featured_panel_thumbnail_2",
"techniqueSet->name": "2d",
"gameFlags": 0,
"sortKey": 60,
"renderFlags": 0,
"textureAtlasRowCount": 1,
"textureAtlasColumnCount": 1,
"textureAtlasFrameBlend": 0,
"textureAtlasAsArray": 0,
"surfaceTypeBits": 0,
"cameraRegion": 12,
"materialType": 0,
"assetFlags": 0,
"constantTable": null,
"textureTable": [
{
"image": "featured_panel_thumbnail_2",
"semantic": 0,
"samplerState": 226,
"lastCharacter": 112,
"firstCharacter": 99,
"typeHash": 2695565377
}
]
}

View File

@ -0,0 +1,26 @@
{
"name": "featured_panel_thumbnail_3",
"techniqueSet->name": "2d",
"gameFlags": 0,
"sortKey": 60,
"renderFlags": 0,
"textureAtlasRowCount": 1,
"textureAtlasColumnCount": 1,
"textureAtlasFrameBlend": 0,
"textureAtlasAsArray": 0,
"surfaceTypeBits": 0,
"cameraRegion": 12,
"materialType": 0,
"assetFlags": 0,
"constantTable": null,
"textureTable": [
{
"image": "featured_panel_thumbnail_3",
"semantic": 0,
"samplerState": 226,
"lastCharacter": 112,
"firstCharacter": 99,
"typeHash": 2695565377
}
]
}

View File

@ -0,0 +1,26 @@
{
"name": "featured_panel_thumbnail_4",
"techniqueSet->name": "2d",
"gameFlags": 0,
"sortKey": 60,
"renderFlags": 0,
"textureAtlasRowCount": 1,
"textureAtlasColumnCount": 1,
"textureAtlasFrameBlend": 0,
"textureAtlasAsArray": 0,
"surfaceTypeBits": 0,
"cameraRegion": 12,
"materialType": 0,
"assetFlags": 0,
"constantTable": null,
"textureTable": [
{
"image": "featured_panel_thumbnail_4",
"semantic": 0,
"samplerState": 226,
"lastCharacter": 112,
"firstCharacter": 99,
"typeHash": 2695565377
}
]
}

View File

@ -0,0 +1,26 @@
{
"name": "featured_panel_thumbnail_5",
"techniqueSet->name": "2d",
"gameFlags": 0,
"sortKey": 60,
"renderFlags": 0,
"textureAtlasRowCount": 1,
"textureAtlasColumnCount": 1,
"textureAtlasFrameBlend": 0,
"textureAtlasAsArray": 0,
"surfaceTypeBits": 0,
"cameraRegion": 12,
"materialType": 0,
"assetFlags": 0,
"constantTable": null,
"textureTable": [
{
"image": "featured_panel_thumbnail_5",
"semantic": 0,
"samplerState": 226,
"lastCharacter": 112,
"firstCharacter": 99,
"typeHash": 2695565377
}
]
}

View File

@ -0,0 +1,26 @@
{
"name": "featured_panel_thumbnail_6",
"techniqueSet->name": "2d",
"gameFlags": 0,
"sortKey": 60,
"renderFlags": 0,
"textureAtlasRowCount": 1,
"textureAtlasColumnCount": 1,
"textureAtlasFrameBlend": 0,
"textureAtlasAsArray": 0,
"surfaceTypeBits": 0,
"cameraRegion": 12,
"materialType": 0,
"assetFlags": 0,
"constantTable": null,
"textureTable": [
{
"image": "featured_panel_thumbnail_6",
"semantic": 0,
"samplerState": 226,
"lastCharacter": 112,
"firstCharacter": 99,
"typeHash": 2695565377
}
]
}

View File

@ -0,0 +1,26 @@
{
"name": "featured_panel_thumbnail_7",
"techniqueSet->name": "2d",
"gameFlags": 0,
"sortKey": 60,
"renderFlags": 0,
"textureAtlasRowCount": 1,
"textureAtlasColumnCount": 1,
"textureAtlasFrameBlend": 0,
"textureAtlasAsArray": 0,
"surfaceTypeBits": 0,
"cameraRegion": 12,
"materialType": 0,
"assetFlags": 0,
"constantTable": null,
"textureTable": [
{
"image": "featured_panel_thumbnail_7",
"semantic": 0,
"samplerState": 226,
"lastCharacter": 112,
"firstCharacter": 99,
"typeHash": 2695565377
}
]
}

View File

@ -0,0 +1,26 @@
{
"name": "featured_panel_thumbnail_8",
"techniqueSet->name": "2d",
"gameFlags": 0,
"sortKey": 60,
"renderFlags": 0,
"textureAtlasRowCount": 1,
"textureAtlasColumnCount": 1,
"textureAtlasFrameBlend": 0,
"textureAtlasAsArray": 0,
"surfaceTypeBits": 0,
"cameraRegion": 12,
"materialType": 0,
"assetFlags": 0,
"constantTable": null,
"textureTable": [
{
"image": "featured_panel_thumbnail_8",
"semantic": 0,
"samplerState": 226,
"lastCharacter": 112,
"firstCharacter": 99,
"typeHash": 2695565377
}
]
}

View File

@ -0,0 +1,26 @@
{
"name": "h1_prestige_leftright_arrow",
"techniqueSet->name": "2d",
"gameFlags": 0,
"sortKey": 60,
"renderFlags": 0,
"textureAtlasRowCount": 1,
"textureAtlasColumnCount": 1,
"textureAtlasFrameBlend": 0,
"textureAtlasAsArray": 0,
"surfaceTypeBits": 0,
"cameraRegion": 12,
"materialType": 0,
"assetFlags": 0,
"constantTable": null,
"textureTable": [
{
"image": "h1_prestige_leftright-arrow",
"semantic": 0,
"samplerState": 226,
"lastCharacter": 112,
"firstCharacter": 99,
"typeHash": 2695565377
}
]
}

View File

@ -0,0 +1,26 @@
{
"name": "h1_ui_featured_pip_unfocused",
"techniqueSet->name": "2d",
"gameFlags": 0,
"sortKey": 60,
"renderFlags": 0,
"textureAtlasRowCount": 1,
"textureAtlasColumnCount": 1,
"textureAtlasFrameBlend": 0,
"textureAtlasAsArray": 0,
"surfaceTypeBits": 0,
"cameraRegion": 12,
"materialType": 0,
"assetFlags": 0,
"constantTable": null,
"textureTable": [
{
"image": "h1_ui_featured_pip_unfocused",
"semantic": 0,
"samplerState": 226,
"lastCharacter": 112,
"firstCharacter": 99,
"typeHash": 2695565377
}
]
}

View File

@ -0,0 +1,26 @@
{
"name": "h2_ui_btn_focused_stroke_square",
"techniqueSet->name": "2d",
"gameFlags": 0,
"sortKey": 60,
"renderFlags": 0,
"textureAtlasRowCount": 1,
"textureAtlasColumnCount": 1,
"textureAtlasFrameBlend": 0,
"textureAtlasAsArray": 0,
"surfaceTypeBits": 0,
"cameraRegion": 12,
"materialType": 0,
"assetFlags": 0,
"constantTable": null,
"textureTable": [
{
"image": "h2_btn_focused_rect_stroke",
"semantic": 0,
"samplerState": 226,
"lastCharacter": 112,
"firstCharacter": 99,
"typeHash": 2695565377
}
]
}

View File

@ -0,0 +1,26 @@
{
"name": "h2_ui_featured_pip_focused",
"techniqueSet->name": "2d",
"gameFlags": 0,
"sortKey": 60,
"renderFlags": 0,
"textureAtlasRowCount": 1,
"textureAtlasColumnCount": 1,
"textureAtlasFrameBlend": 0,
"textureAtlasAsArray": 0,
"surfaceTypeBits": 0,
"cameraRegion": 12,
"materialType": 0,
"assetFlags": 0,
"constantTable": null,
"textureTable": [
{
"image": "h2_ui_featured_pip_focused",
"semantic": 0,
"samplerState": 226,
"lastCharacter": 112,
"firstCharacter": 99,
"typeHash": 2695565377
}
]
}

View File

@ -0,0 +1,26 @@
{
"name": "motd_image",
"techniqueSet->name": "2d",
"gameFlags": 0,
"sortKey": 60,
"renderFlags": 0,
"textureAtlasRowCount": 1,
"textureAtlasColumnCount": 1,
"textureAtlasFrameBlend": 0,
"textureAtlasAsArray": 0,
"surfaceTypeBits": 0,
"cameraRegion": 12,
"materialType": 0,
"assetFlags": 0,
"constantTable": null,
"textureTable": [
{
"image": "motd_image",
"semantic": 0,
"samplerState": 226,
"lastCharacter": 112,
"firstCharacter": 99,
"typeHash": 2695565377
}
]
}

View File

@ -116,5 +116,8 @@
"LUA_MENU_SP_LOCATION_OILRIG": "LUA_MENU_SP_LOCATION_OILRIG",
"LUA_MENU_SP_LOCATION_ROADKILL": "LUA_MENU_SP_LOCATION_ROADKILL",
"LUA_MENU_SP_LOCATION_TRAINER": "LUA_MENU_SP_LOCATION_TRAINER",
"LUA_MENU_SP_LOCATION_MUSEUM": "LUA_MENU_SP_LOCATION_MUSEUM"
"LUA_MENU_SP_LOCATION_MUSEUM": "LUA_MENU_SP_LOCATION_MUSEUM",
"DEPOT_GO_TO_THE_DEPOT": "Open link",
"MENU_OPEN_MOTD": "Open Message of the Day"
}

2
deps/asmjit vendored

@ -1 +1 @@
Subproject commit d4dfd8e86529465efc4365f27b125d90552c700e
Subproject commit 915186f6c5c2f5a4638e5cb97ccc23d741521a64

View File

@ -34,6 +34,7 @@ namespace config
{
{define_field("disable_custom_fonts", field_type::boolean, false)},
{define_field("language", field_type::string, language::get_default_language(), language::is_valid_language)},
{define_field("motd_last_seen", field_type::number_unsigned, 0)},
};
std::string get_config_file_path()

View File

@ -6,6 +6,7 @@
#include "command.hpp"
#include "console.hpp"
#include "mods.hpp"
#include "fonts.hpp"
#include <utils/hook.hpp>
#include <utils/concurrency.hpp>
@ -104,12 +105,14 @@ namespace fastfiles
a.jmp(0x140415E29);
}
bool try_load_zone(const std::string& name, bool localized, bool game = false)
bool try_add_zone(std::vector<game::XZoneInfo>& zones,
utils::memory::allocator& allocator, const std::string& name,
bool localized, bool game = false)
{
if (localized)
{
const auto language = game::SEH_GetCurrentLanguageCode();
try_load_zone(language + "_"s + name, false);
try_add_zone(zones, allocator, language + "_"s + name, false);
}
if (!fastfiles::exists(name))
@ -118,48 +121,73 @@ namespace fastfiles
}
game::XZoneInfo info{};
info.name = name.data();
info.name = allocator.duplicate_string(name);
info.allocFlags = (game ? game::DB_ZONE_GAME : game::DB_ZONE_COMMON) | game::DB_ZONE_CUSTOM;
info.freeFlags = 0;
game::DB_LoadXAssets(&info, 1u, game::DBSyncMode::DB_LOAD_ASYNC);
zones.push_back(info);
return true;
}
void load_mod_zones()
void add_mod_zones(std::vector<game::XZoneInfo>& zones, utils::memory::allocator& allocator)
{
try_load_zone("mod", true);
try_add_zone(zones, allocator, "mod", true);
const auto mod_zones = mods::get_mod_zones();
for (const auto& zone : mod_zones)
{
if (zone.alloc_flags & game::DB_ZONE_COMMON)
{
try_load_zone(zone.name, true);
try_add_zone(zones, allocator, zone.name, true);
}
}
}
void load_pre_gfx_zones(game::XZoneInfo* zoneInfo,
unsigned int zoneCount, game::DBSyncMode syncMode)
void push_zones(std::vector<game::XZoneInfo>& zones,
game::XZoneInfo* source, const size_t count)
{
for (auto i = 0; i < count; ++i)
{
zones.push_back(source[i]);
}
}
void load_pre_gfx_zones(game::XZoneInfo* zone_info,
unsigned int zone_count, game::DBSyncMode sync_mode)
{
// code_pre_gfx
try_load_zone("h2_mod_pre_gfx", true);
utils::memory::allocator allocator;
std::vector<game::XZoneInfo> zones;
try_add_zone(zones, allocator, "h2_mod_pre_gfx", true);
push_zones(zones, zone_info, zone_count);
game::DB_LoadXAssets(zoneInfo, zoneCount, syncMode);
game::DB_LoadXAssets(zones.data(), static_cast<int>(zones.size()), sync_mode);
fonts::load_font_zones();
}
void load_post_gfx_and_ui_and_common_zones(game::XZoneInfo* zoneInfo,
unsigned int zoneCount, game::DBSyncMode syncMode)
void load_post_gfx_and_ui_and_common_zones(game::XZoneInfo* zone_info,
unsigned int zone_count, game::DBSyncMode sync_mode)
{
// code_post_gfx_mp
// ui_mp
// common_mp
try_load_zone("h2_mod_common", true);
utils::memory::allocator allocator;
std::vector<game::XZoneInfo> zones;
game::DB_LoadXAssets(zoneInfo, zoneCount, syncMode);
try_add_zone(zones, allocator, "h2_mod_common", true);
for (auto i = 0u; i < zone_count; i++)
{
zones.push_back(zone_info[i]);
load_mod_zones();
if (zone_info[i].name == "code_post_gfx"s)
{
try_add_zone(zones, allocator, "h2_mod_ui", true);
}
}
add_mod_zones(zones, allocator);
game::DB_LoadXAssets(zones.data(), static_cast<int>(zones.size()), sync_mode);
}
constexpr unsigned int get_asset_type_size(const game::XAssetType type)
@ -432,7 +460,7 @@ namespace fastfiles
add_custom_level_load_zone(load, name, size_est);
}
void add_mod_zones(game::LevelLoad* load)
void add_mod_zones_to_load(game::LevelLoad* load)
{
const auto mod_zones = mods::get_mod_zones();
for (const auto& zone : mod_zones)
@ -450,7 +478,7 @@ namespace fastfiles
a.pushad64();
a.mov(rcx, rbx);
a.call_aligned(add_mod_zones);
a.call_aligned(add_mod_zones_to_load);
a.popad64();
a.mov(rcx, rdi);
@ -572,6 +600,27 @@ namespace fastfiles
});
}
bool try_load_zone(const std::string& name, bool localized, bool game)
{
if (localized)
{
const auto language = game::SEH_GetCurrentLanguageCode();
try_load_zone(language + "_"s + name, false);
}
if (!fastfiles::exists(name))
{
return false;
}
game::XZoneInfo info{};
info.name = name.data();
info.allocFlags = (game ? game::DB_ZONE_GAME : game::DB_ZONE_COMMON) | game::DB_ZONE_CUSTOM;
info.freeFlags = 0;
game::DB_LoadXAssets(&info, 1u, game::DBSyncMode::DB_LOAD_ASYNC);
return true;
}
class component final : public component_interface
{
public:
@ -631,7 +680,7 @@ namespace fastfiles
}
const auto name = params.get(1);
if (!try_load_zone(name, false))
if (!fastfiles::try_load_zone(name, false))
{
console::warn("loadzone: zone \"%s\" could not be found!\n", name);
}

View File

@ -1,6 +1,7 @@
#pragma once
#include "game/game.hpp"
#include <utils/memory.hpp>
namespace fastfiles
{
@ -8,4 +9,5 @@ namespace fastfiles
std::string get_current_fastfile();
bool exists(const std::string& zone);
bool try_load_zone(const std::string& name, bool localized, bool game = false);
}

View File

@ -7,6 +7,7 @@
#include "command.hpp"
#include "language.hpp"
#include "config.hpp"
#include "fastfiles.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
@ -41,182 +42,6 @@ namespace fonts
"bankshadowmore",
};
struct font_data_t
{
std::unordered_map<std::string, game::TTF*> fonts;
std::unordered_map<std::string, std::string> raw_fonts;
};
utils::memory::allocator font_allocator;
game::StringTable* get_font_replacements_table()
{
if (!game::DB_XAssetExists(game::ASSET_TYPE_STRINGTABLE, "font_replacements.csv"))
{
return nullptr;
}
return game::DB_FindXAssetHeader(game::ASSET_TYPE_STRINGTABLE, "font_replacements.csv", false).stringTable;
}
struct font_replacement
{
const char* target_font;
const char* new_font;
};
std::vector<font_replacement>& get_font_replacements()
{
static std::vector<font_replacement> replacements = {};
return replacements;
}
void load_font_replacements()
{
static auto loaded = false;
if (loaded)
{
return;
}
loaded = true;
auto& replacements = get_font_replacements();
const auto disabled = config::get<bool>("disable_custom_fonts");
if (disabled.has_value() && disabled.value() && language::current() != game::LANGUAGE_CZECH)
{
return;
}
const auto table = get_font_replacements_table();
if (table == nullptr)
{
return;
}
const auto current_language = language::current();
for (auto row = 0; row < table->rowCount; row++)
{
if (table->columnCount < 3)
{
continue;
}
const auto row_values = &table->values[(row * table->columnCount)];
const auto lang = row_values[0].string;
if (std::strcmp(lang, game::languages[current_language].name))
{
continue;
}
const auto font = utils::memory::get_allocator()->duplicate_string(row_values[1].string);
const auto replacement = utils::memory::get_allocator()->duplicate_string(row_values[2].string);
replacements.emplace_back(font, replacement);
}
return;
}
const char* get_font_replacement(const char* name)
{
const auto& replacements = get_font_replacements();
for (const auto& replacement : replacements)
{
if (!std::strcmp(name, replacement.target_font))
{
return replacement.new_font;
}
}
return name;
}
utils::concurrency::container<font_data_t> font_data;
game::TTF* create_font(const std::string& name, const std::string& data)
{
const auto font = utils::memory::get_allocator()->allocate<game::TTF>();
font->name = utils::memory::get_allocator()->duplicate_string(name);
font->buffer = utils::memory::get_allocator()->duplicate_string(data);
font->len = static_cast<int>(data.size());
font->fontFace = 0;
return font;
}
void free_font(game::TTF* font)
{
font_allocator.free(font->buffer);
font_allocator.free(font->name);
font_allocator.free(font);
}
game::TTF* load_font(const std::string& name)
{
return font_data.access<game::TTF*>([&](font_data_t& data_) -> game::TTF*
{
if (const auto i = data_.fonts.find(name); i != data_.fonts.end())
{
return i->second;
}
std::string data{};
if (const auto i = data_.raw_fonts.find(name); i != data_.raw_fonts.end())
{
data = i->second;
}
if (data.empty() && !filesystem::read_file(name, &data))
{
return nullptr;
}
const auto material = create_font(name, data);
data_.fonts[name] = material;
return material;
});
}
game::TTF* try_load_font(const std::string& name)
{
try
{
return load_font(name);
}
catch (const std::exception& e)
{
console::error("Failed to load font %s: %s\n", name.data(), e.what());
}
return nullptr;
}
game::TTF* db_find_xasset_header_stub(game::XAssetType type, const char* name, int create_default)
{
auto result = try_load_font(name);
if (result == nullptr)
{
result = game::DB_FindXAssetHeader(type, name, create_default).ttf;
}
return result;
}
utils::hook::detour r_register_font_hook;
void* r_register_font_stub(const char* name, int size)
{
const auto name_ = get_font_replacement(name);
return r_register_font_hook.invoke<void*>(name_, size);
}
utils::hook::detour cl_init_renderer_hook;
void* cl_init_renderer_stub()
{
load_font_replacements();
return cl_init_renderer_hook.invoke<void*>();
}
game::Font_s* bank_font = nullptr;
utils::hook::detour ui_get_font_handle_hook;
@ -336,27 +161,47 @@ namespace fonts
}
}
void add(const std::string& name, const std::string& data)
void load_font_zones()
{
font_data.access([&](font_data_t& data_)
const auto disabled = config::get<bool>("disable_custom_fonts");
const auto custom_fonts_disabled = disabled.has_value() && disabled.value() && !language::is_custom();
const auto table = game::DB_FindXAssetHeader(game::ASSET_TYPE_STRINGTABLE, "font_zones.csv", 0).stringTable;
if (table == nullptr)
{
data_.raw_fonts[name] = data;
});
return;
}
void clear()
const auto lang = language::current();
const std::string lang_name = game::languages[lang].name;
for (auto row = 0; row < table->rowCount; row++)
{
font_data.access([&](font_data_t& data_)
if (table->columnCount < 3)
{
for (auto& font : data_.fonts)
{
free_font(font.second);
continue;
}
font_allocator.clear();
data_.fonts.clear();
*reinterpret_cast<int*>(0x14EE3ACB8) = 0; // reset registered font count
});
const auto row_values = &table->values[(row * table->columnCount)];
const auto lang_value = row_values[0].string;
const auto is_replacement = lang_value != "*"s;
if (lang_value != lang_name && is_replacement)
{
continue;
}
if (custom_fonts_disabled && is_replacement)
{
continue;
}
for (auto col = 1; col < table->columnCount; col++)
{
const auto zone = row_values[col].string;
if (zone != nullptr)
{
fastfiles::try_load_zone(zone, true);
}
}
}
}
class component final : public component_interface
@ -364,10 +209,6 @@ namespace fonts
public:
void post_unpack() override
{
utils::hook::call(0x140747096, db_find_xasset_header_stub);
r_register_font_hook.create(0x140746FE0, r_register_font_stub);
cl_init_renderer_hook.create(0x1403D5AA0, cl_init_renderer_stub);
// add custom fonts to hud elem fonts
ui_asset_cache_hook.create(0x140606090, ui_asset_cache_stub);
ui_get_font_handle_hook.create(0x1406058F0, ui_get_font_handle_stub);

View File

@ -1,7 +1,9 @@
#pragma once
#include "game/game.hpp"
#include <utils/memory.hpp>
namespace fonts
{
void add(const std::string& name, const std::string& data);
void clear();
void load_font_zones();
}

View File

@ -12,6 +12,7 @@
#include <utils/image.hpp>
#include <utils/io.hpp>
#include <utils/concurrency.hpp>
#include <utils/http.hpp>
namespace images
{
@ -73,7 +74,7 @@ namespace images
return true;
}
void load_texture_stub(game::GfxImage* image, void* a2, int* a3)
void load_texture_stub(void* a1, game::GfxImage* image)
{
try
{
@ -87,7 +88,7 @@ namespace images
console::error("Failed to load image %s: %s\n", image->name, e.what());
}
load_texture_hook.invoke<void>(image, a2, a3);
load_texture_hook.invoke<void>(a1, image);
}
int setup_texture_stub(game::GfxImage* image, void* a2, void* a3)
@ -115,7 +116,7 @@ namespace images
void post_unpack() override
{
setup_texture_hook.create(0x1402A7940, setup_texture_stub);
load_texture_hook.create(0x1402A6690, load_texture_stub);
load_texture_hook.create(0x14074A390, load_texture_stub);
}
};
}

View File

@ -58,7 +58,6 @@ namespace mods
{
if (release_assets)
{
fonts::clear();
loadscreen::clear();
}

View File

@ -0,0 +1,256 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "motd.hpp"
#include "console.hpp"
#include "images.hpp"
#include "command.hpp"
#include <utils/string.hpp>
#include <utils/http.hpp>
#include <utils/concurrency.hpp>
#define MAX_FEATURED_TABS 8
namespace motd
{
namespace
{
utils::concurrency::container<links_map_t> links;
utils::concurrency::container<nlohmann::json, std::recursive_mutex> marketing;
std::unordered_map<std::string, std::string> image_cache;
std::optional<std::string> download_image(const std::string& url)
{
if (image_cache.contains(url))
{
return {image_cache.at(url)};
}
const auto res = utils::http::get_data(url);
if (res.has_value())
{
image_cache[url] = res.value();
}
return res;
}
void download_motd_image(nlohmann::json& data)
{
if (!data["motd"].is_object() || !data["motd"]["image_url"].is_string())
{
return;
}
const auto url = data["motd"]["image_url"].get<std::string>();
const auto image_data = download_image(url);
if (image_data.has_value())
{
const auto& image = image_data.value();
console::debug("Downloaded motd image\n");
images::override_texture("motd_image", image);
}
}
void download_featured_tabs_images(nlohmann::json& data)
{
if (!data["featured"].is_array())
{
return;
}
auto index = 0;
for (const auto& [key, tab] : data["featured"].items())
{
index++;
if (index >= MAX_FEATURED_TABS + 1)
{
return;
}
if (!tab.is_object() || !tab["image_url"].is_string())
{
continue;
}
const auto download_image_ = [&](const std::string& field, const std::string& image_name)
{
const auto url = tab[field].get<std::string>();
const auto image_data = download_image(url);
if (image_data.has_value())
{
const auto& image = image_data.value();
console::debug("Downloaded featured tab image %i\n", index);
images::override_texture(image_name + std::format("_{}", index), image);
}
};
download_image_("image_url", "featured_panel");
download_image_("thumbnail_url", "featured_panel_thumbnail");
}
}
void download_images(nlohmann::json& data)
{
if (!data.is_object())
{
return;
}
download_motd_image(data);
download_featured_tabs_images(data);
}
void init_links(links_map_t& map)
{
map =
{
{"github", "https://github.com/fedddddd/h2-mod"},
{"donate", "https://www.paypal.com/donate/?hosted_button_id=LM5BA9UABEV4Q"},
{"credits_1", "https://github.com/momo5502"},
{"credits_2", "https://github.com/VladWinner"},
{"credits_3", "https://github.com/diamante0018"},
{"credits_4", "https://github.com/JariKCoding"},
{"credits_5", "https://github.com/netadr"},
{"credits_6", "https://github.com/Joelrau"},
{"credits_7", "https://github.com/xensik"},
{"credits_8", "https://github.com/ZoneTool/zonetool"},
};
}
void add_links(nlohmann::json& data)
{
links.access([&](links_map_t& map)
{
init_links(map);
if (!data.is_object() || !data["links"].is_object())
{
return;
}
for (const auto& [link, url] : data["links"].items())
{
if (!url.is_string())
{
continue;
}
map.insert(std::make_pair(link, url.get<std::string>()));
}
});
}
void init(bool load_images = true)
{
links.access([](links_map_t& map)
{
init_links(map);
});
marketing.access([&](nlohmann::json& data)
{
image_cache.clear();
data.clear();
const auto marketing_data = utils::http::get_data("https://master.fed0001.xyz/h2-mod/marketing.json");
if (marketing_data.has_value())
{
try
{
const auto& value = marketing_data.value();
data = nlohmann::json::parse(value);
add_links(data);
if (load_images)
{
download_images(data);
}
}
catch (const std::exception& e)
{
console::error("Failed to load marketing.json: %s\n", e.what());
}
}
});
}
}
links_map_t get_links()
{
return links.access<links_map_t>([&](links_map_t& map)
{
return map;
});
}
int get_num_featured_tabs()
{
return marketing.access<nlohmann::json>([&](nlohmann::json& data)
-> nlohmann::json
{
if (!data.is_object() || !data["featured"].is_array())
{
return 0;
}
return std::min(MAX_FEATURED_TABS, static_cast<int>(data["featured"].size()));
});
}
nlohmann::json get_featured_tab(const int index)
{
return marketing.access<nlohmann::json>([&](nlohmann::json& data)
-> nlohmann::json
{
if (!data.is_object() || !data["featured"].is_array())
{
return {};
}
if (index >= data["featured"].size())
{
return {};
}
return data["featured"][index];
});
}
nlohmann::json get_motd()
{
return marketing.access<nlohmann::json>([](nlohmann::json& data)
-> nlohmann::json
{
if (!data.is_object() || !data["motd"].is_object())
{
return {};
}
return data["motd"];
});
}
class component final : public component_interface
{
public:
void post_unpack() override
{
init();
command::add("reloadmotd", []()
{
init(true);
});
command::add("reloadmotd_noimages", []()
{
init(false);
});
}
};
}
REGISTER_COMPONENT(motd::component)

View File

@ -0,0 +1,11 @@
#pragma once
namespace motd
{
using links_map_t = std::unordered_map<std::string, std::string>;
links_map_t get_links();
int get_num_featured_tabs();
nlohmann::json get_motd();
nlohmann::json get_featured_tab(const int index);
}

View File

@ -79,31 +79,6 @@ namespace patches
{
return game::Dvar_RegisterBool(hash, name, value, game::DVAR_FLAG_SAVED);
}
void gscr_cinematic_ingame_loop_resident_stub()
{
auto arg2 = 1.f;
auto arg1 = 1;
const auto num_params = game::Scr_GetNumParam();
if (!num_params)
{
game::Scr_ErrorInternal();
}
if (num_params >= 2)
{
arg2 = game::Scr_GetFloat(1);
}
if (num_params >= 3)
{
arg1 = game::Scr_GetInt(2);
}
const auto video = game::Scr_GetString(0);
utils::hook::invoke<void>(0x14071B740, video, arg1, arg2);
}
}
class component final : public component_interface
@ -146,8 +121,8 @@ namespace patches
// make snd_musicDisabledForCustomSoundtrack saved
utils::hook::call(0x1405D05FB, register_snd_music_stub);
// Fix broken function
utils::hook::jump(0x140502140, gscr_cinematic_ingame_loop_resident_stub);
// cinematicingameloopresident -> cinematicingameloop (fix ingame cinematics)
utils::hook::jump(0x140502140, 0x1405020C0);
}
};
}

View File

@ -15,6 +15,7 @@
#include "console.hpp"
#include "language.hpp"
#include "config.hpp"
#include "motd.hpp"
#include "game/ui_scripting/execution.hpp"
#include "game/scripting/execution.hpp"
@ -151,6 +152,7 @@ namespace ui_scripting
{
object[key] = json_to_lua(value);
}
return object;
}
if (json.is_array())
@ -161,6 +163,7 @@ namespace ui_scripting
{
array[index++] = json_to_lua(value);
}
return array;
}
if (json.is_boolean())
@ -331,20 +334,7 @@ namespace ui_scripting
game_type["openlink"] = [](const game&, const std::string& name)
{
static std::unordered_map<std::string, std::string> links =
{
{"github", "https://github.com/fedddddd/h2-mod"},
{"donate", "https://www.paypal.com/donate/?hosted_button_id=LM5BA9UABEV4Q"},
{"credits_1", "https://github.com/momo5502"},
{"credits_2", "https://github.com/VladWinner"},
{"credits_3", "https://github.com/diamante0018"},
{"credits_4", "https://github.com/JariKCoding"},
{"credits_5", "https://github.com/netadr"},
{"credits_6", "https://github.com/Joelrau"},
{"credits_7", "https://github.com/xensik"},
{"credits_8", "https://github.com/ZoneTool/zonetool"},
};
const auto links = motd::get_links();
const auto link = links.find(name);
if (link == links.end())
{
@ -354,6 +344,31 @@ namespace ui_scripting
ShellExecuteA(nullptr, "open", link->second.data(), nullptr, nullptr, SW_SHOWNORMAL);
};
game_type["getlinkurl"] = [](const game&, const std::string& name)
-> script_value
{
const auto links = motd::get_links();
const auto link = links.find(name);
if (link == links.end())
{
return script_value();
}
return link->second;
};
game_type["islink"] = [](const game&, const std::string& name)
{
const auto links = motd::get_links();
const auto link = links.find(name);
if (link == links.end())
{
return false;
}
return true;
};
lua["string"]["escapelocalization"] = [](const std::string& str)
{
return "\x1F"s.append(str);
@ -604,6 +619,41 @@ namespace ui_scripting
{
config::set(key, lua_to_json(value));
};
auto motd_table = table();
lua["motd"] = motd_table;
motd_table["getnumfeaturedtabs"] = motd::get_num_featured_tabs;
motd_table["getmotd"] = []()
{
return json_to_lua(motd::get_motd());
};
motd_table["getfeaturedtab"] = [](const int index)
{
return json_to_lua(motd::get_featured_tab(index));
};
motd_table["hasseentoday"] = []()
{
const auto last_seen = config::get<uint64_t>("motd_last_seen");
if (!last_seen.has_value())
{
return false;
}
const auto value = static_cast<time_t>(last_seen.value());
const auto before = std::chrono::floor<std::chrono::days>(std::chrono::system_clock::from_time_t(value));
const auto now = std::chrono::floor<std::chrono::days>(std::chrono::system_clock::now());
const auto diff = std::chrono::sys_days{now} - std::chrono::sys_days{before};
return diff.count() < 1;
};
motd_table["sethasseentoday"] = []()
{
const auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
config::set<uint64_t>("motd_last_seen", static_cast<uint64_t>(now));
};
}
void start()