From fd60d84609a97a86f465cdf1c5d808f0b092fda3 Mon Sep 17 00:00:00 2001 From: Jed- Date: Wed, 18 Sep 2024 18:05:19 +0200 Subject: [PATCH 01/68] TTF font and Unicode support All text is now rendered with TTF fonts using `pangocairo`, and UTF-8 strings are supported natively. Add a dependency on `pango >= 1.46`, `cairo >= 1.18` and `fontconfig`. Player names are restricted to a subset of Latin, Cyrillic and Greek characters in order to prevent abuse. The rest of the UI, console and particles can use any Unicode characters without restriction. In addition to the color codes `\f0` through `\f9`, more codes have been added to support basic markup: - \fb...\fB = bold - \fi...\fI = italic - \fu...\fU = underline - \ft...\fT = strikethrough - \fo...\fO = overline where `...` is the part of the string to apply the markup to. Using TTF fonts: - each TTF file must be registered with the `registerfont` command before it can be used - the `font` command is used to define a font family (and optionally style) to use in-game - several commands are available to customize each font, see `config/font.cfg` for how to use them - the `fontalias` command has been removed Console changes: - add the `conshadow` (0, 255, 255) variable: adds a shadow to console text for increased visibility - change the range of `conscale` from (0.001, 0.45, 1000) to (0.5, 1, 2) Text input changes: - add the `cursorblink` (0, 750, 2000) variable to customize the cursor's blink speed (0 = disable blinking) - add the `cursorcolor` (0xFFFFFF) variable to customize the cursor's color - the cursor does not blink while the user is typing Text particle changes: - the font to use for each particle can be specified as an argument to `particle_text()` - add the `particle_hud_text()` function: renders a text particle as a hud mark, similar to `particle_hud_mark()`, this is now used to render the `GOAL` label in CTF mode UI changes: - `uitext` and related commands accept a `fancy` argument, used for shadows and outlines, and a `language` argument, which may be necessary to render text in certain languages correctly. The language argument must be in `RFC 3066` format. - valid values for `fancy`: 0 = nothing; 1 = shadow; 2 = outline; 3 = shadow + outline - remove the `uitextrows` variable; UI text scale can still be configured with `uiscale` - add the `uifps` (0, 60, 1000) variable: controls how often text elements check for text changes (0 = check every frame, may impact performace) Miscellaneous changes: - change the `filtertext` function to take a `flags` argument rather than a series of booleans - remove the `tabify` and `textbright` commands - remove the `tessfont` tool Global font settings (users may change these to their liking): - fontantialias (0, 1, 1): toggle font antialiasing (only grayscale antialiasing is supported) - fonthinting (-1, -1, 3): set font hinting (-1 = system default, 0 = disabled, 1 = slight, 2 = medium, 3 = full) Settings for individual fonts (to use after the `font` command, refer to `config/font.cfg`): - fontweight [-3..6]: < 0 = lighter, > 0 = bolder - fontstretch [-4..4]: < 0 = tighter, > 0 = wider; many fonts don't support this, use `fontletterspacing` instead - fontstyle [0..2]: 0 = normal, 1 = oblique, 2 = italic - fontsmallcaps 0|1 - fontletterspacing val: `val` is a floating point value, measured in points; < 0 = tighter, > 0 = wider - fontfeatures [...features]: OpenType features, in CSS `font-feature-settings` format - fontvariations "variations": for OpenType variable fonts, format: "AXIS1=VALUE,AXIS2=VALUE..." --- config/font.cfg | 30 +- config/glsl/hud.cfg | 51 +- config/glsl/particle.cfg | 9 +- config/ui/hud/editstats.cfg | 4 +- config/ui/hud/gamehud.cfg | 8 +- config/ui/hud/geo_prefab.cfg | 2 +- config/ui/hud/mapmodel_browser.cfg | 8 +- config/ui/hud/texture_browser.cfg | 6 +- config/ui/lib.cfg | 26 +- config/ui/libnew.cfg | 2 +- config/ui/menus/server_browser.cfg | 2 +- config/ui/permanent.cfg | 4 +- config/ui/style.cfg | 2 - source/Makefile | 39 +- source/engine/command.cpp | 133 +++-- source/engine/console.cpp | 166 ++++-- source/engine/engine.h | 42 +- source/engine/main.cpp | 65 ++- source/engine/movie.cpp | 4 +- source/engine/rendergl.cpp | 14 +- source/engine/renderparticles.cpp | 58 +- source/engine/rendertext.cpp | 859 ++++++++++++++++++----------- source/engine/server.cpp | 27 +- source/engine/serverbrowser.cpp | 6 +- source/engine/shader.cpp | 19 +- source/engine/textedit.h | 68 ++- source/engine/ui.cpp | 248 ++++++--- source/game/ctf.h | 2 +- source/game/game.cpp | 2 +- source/game/game.h | 3 +- source/game/gameclient.cpp | 28 +- source/game/gameserver.cpp | 24 +- source/game/geoip.h | 10 +- source/game/render.cpp | 4 +- source/game/scoreboard.cpp | 2 +- source/shared/iengine.h | 36 +- source/shared/stream.cpp | 413 +------------- source/shared/tessfont.c | 764 ------------------------- source/shared/tools.cpp | 55 +- source/shared/tools.h | 111 ++-- source/shared/unicode.h | 188 +++++++ 41 files changed, 1555 insertions(+), 1989 deletions(-) delete mode 100644 source/shared/tessfont.c create mode 100644 source/shared/unicode.h diff --git a/config/font.cfg b/config/font.cfg index f38a43679..8732eb6a1 100644 --- a/config/font.cfg +++ b/config/font.cfg @@ -1,14 +1,26 @@ // Execute dedicated game fonts here -loopfiles f "data/interface/font" cfg [ - exec (+s "data/interface/font/" $f ".cfg") -] +// registerfont "filename" // "family name" -// Change the two following lines to define default font -fontalias "default" "main" -fontalias "default.ol" "main.ol" +// font "name" "family name" + // fontweight -3..6 // < 0 = thinner, > 0 = thicker + // fontstretch -4..4 // < 0 = tighter, > 0 = wider ; many fonts don't support this + // fontstyle 0|1|2 // 0 = normal, 1 = oblique, 2 = italic + // fontsmallcaps 0|1 + // fontletterspacing val // float; < 0 = tighter, > 0 = wider + // fontfeatures [...features] // OpenType features (in CSS format) + // fontvariations "variations" // for variable fonts; format: "AXIS1=VALUE,AXIS2=VALUE..." -// Shorthand aliases -fontalias "def" "default" -fontalias "def.ol" "default.ol" +registerfont "data/interface/font/InterVariable.ttf" // Inter Variable +registerfont "data/interface/font/InterVariable-Italic.ttf" // Inter Variable Italic +registerfont "data/interface/font/DejaVuSansMono.ttf" // DejaVu Sans Mono +registerfont "data/interface/font/Oxanium-VariableFont_wght.ttf" // Oxanium +registerfont "data/interface/font/webdings.ttf" // Webdings + +font "default" "Inter Variable, Sans" + fontfeatures ["ss04", "cv12", "cv13"] +font "mono" "DejaVu Sans Mono, Monospace" +font "wide" "Oxanium, Inter Variable, Sans" + fontweight 4 +font "webdings" "Webdings" \ No newline at end of file diff --git a/config/glsl/hud.cfg b/config/glsl/hud.cfg index 59e5c4714..b1b6d9451 100644 --- a/config/glsl/hud.cfg +++ b/config/glsl/hud.cfg @@ -28,33 +28,6 @@ shader 0 "hud" [ } ] -shader 0 "hudtext" [ - attribute vec4 vvertex, vcolor; - attribute vec2 vtexcoord0; - uniform mat4 hudmatrix; - varying vec2 texcoord0; - varying vec4 colorscale; - void main(void) - { - gl_Position = hudmatrix * vvertex; - texcoord0 = vtexcoord0; - colorscale = vcolor; - } -] [ - uniform sampler2D tex0; - uniform vec4 textparams; - varying vec2 texcoord0; - varying vec4 colorscale; - fragdata(0) vec4 fragcolor; - void main(void) - { - float dist = texture2D(tex0, texcoord0).r; - float border = smoothstep(textparams.x, textparams.y, dist); - float outline = smoothstep(textparams.z, textparams.w, dist); - fragcolor = vec4(colorscale.rgb * outline, colorscale.a * border); - } -] - shader 0 "hudrgb" [ attribute vec4 vvertex, vcolor; attribute vec2 vtexcoord0; @@ -152,6 +125,30 @@ shader 0 "hudrect" [ } ] +shader 0 "hudtext" [ + attribute vec4 vvertex, vcolor; + attribute vec2 vtexcoord0; + uniform mat4 hudmatrix; + varying vec2 texcoord0; + varying vec4 colorscale; + void main(void) + { + gl_Position = hudmatrix * vvertex; + texcoord0 = vtexcoord0; + colorscale = vcolor; + } +] [ + uniform sampler2DRect tex0; + varying vec2 texcoord0; + varying vec4 colorscale; + fragdata(0) vec4 fragcolor; + void main(void) + { + fragcolor = colorscale * texture2DRect(tex0, texcoord0); + if(fragcolor.a != 0) fragcolor.rgb = fragcolor.rgb / fragcolor.a; + } +] + shader 0 "hud3d" [ attribute vec4 vvertex, vcolor; attribute vec3 vtexcoord0; diff --git a/config/glsl/particle.cfg b/config/glsl/particle.cfg index 2345e2db7..1e018ee87 100644 --- a/config/glsl/particle.cfg +++ b/config/glsl/particle.cfg @@ -107,17 +107,14 @@ shader 0 "particletext" [ color = vec4(vcolor.rgb * ldrscale, vcolor.a); } ] [ - uniform vec4 textparams; - uniform sampler2D tex0; + uniform sampler2DRect tex0; varying vec4 color; varying vec2 texcoord0; fragdata(0) vec4 fragcolor; void main(void) { - float dist = texture2D(tex0, texcoord0).r; - float border = smoothstep(textparams.x, textparams.y, dist); - float outline = smoothstep(textparams.z, textparams.w, dist); - fragcolor = vec4(color.rgb * outline, color.a * border); + fragcolor = color * texture2DRect(tex0, texcoord0); + if(fragcolor.a != 0) fragcolor.rgb /= fragcolor.a; } ] diff --git a/config/ui/hud/editstats.cfg b/config/ui/hud/editstats.cfg index f31f5a2d0..3db8d9eb8 100644 --- a/config/ui/hud/editstats.cfg +++ b/config/ui/hud/editstats.cfg @@ -333,8 +333,8 @@ uiLiteMenu "edithud" [ uigrid 2 $uiPad:3XL $uiPad:L [ uihlist $uiPad:XL [ uitext "WTR" 0.5 - uitext (tabify (format "%1k" $editstatwtr) 1) 0.5 - uitext (tabify (format "(%1%%)" $editstatvtr) 2) 0.5 + uitext (format "%1k" $editstatwtr) 0.5 + uitext (format "(%1%%)" $editstatvtr) 0.5 ] uihlist $uiPad:XL [ uitext "WVT" 0.5 diff --git a/config/ui/hud/gamehud.cfg b/config/ui/hud/gamehud.cfg index 37cd5a392..f62a3dcdf 100644 --- a/config/ui/hud/gamehud.cfg +++ b/config/ui/hud/gamehud.cfg @@ -735,7 +735,7 @@ newui "scoreboard" [ uiFastImg (fade (? $arg1 0.5 0.25)) "hud/" "shelf" "" $uiPad:5XL uiFastImg "hud/" "glow" "" $uiPad:5XL push n (+ $n 1) [ - uifontcolortext "def.ol" $n (|A! (? $arg1 0xB0 0x60)) 0.55 + uifontcolortext "default" $n (|A! (? $arg1 0xB0 0x60)) 0.55 ] ] ; uialign- -1 ] @@ -786,14 +786,14 @@ newui ".killUI" [ uifill 0 0.3 t = (getrespawnwait) uispace 0.02 0.02 [ - uifonttext "wide.ol" $lasthudkillinfo $uiPad:USS + uifonttext "wide" $lasthudkillinfo $uiPad:USS ] uispace 0 0 [ uioffset 0 $uiPad:D2XL- [ if (m_invasion $getmode) [ - uifonttext "wide.ol" (+s "^f4Lives remaining: " $getclientlives) $uiPad:USS + uifonttext "wide" (+s "^f4Lives remaining: " $getclientlives) $uiPad:USS ] [ - uifontcolortext "wide.ol" (? $t $t " ") (? (< (getmillis) (+ 100 (getlastspawnattempt))) $c_red $c_white) 1 + uifontcolortext "wide" (? $t $t " ") (? (< (getmillis) (+ 100 (getlastspawnattempt))) $c_red $c_white) 1 ] ] ] diff --git a/config/ui/hud/geo_prefab.cfg b/config/ui/hud/geo_prefab.cfg index 4659d9832..3de59c16d 100644 --- a/config/ui/hud/geo_prefab.cfg +++ b/config/ui/hud/geo_prefab.cfg @@ -24,7 +24,7 @@ uiLiteMenu "geo_prefab" [ uiclamp*e uispace $uiPad:S $uiPad:O3 [ uialign -1 -1 - uifontcolortext "def.ol" $i (|A! 0x98) 0.55 + uifontcolortext "default" $i (|A! 0x98) 0.55 ] uihover [ if (!= $i $.UI_obrsel) [ diff --git a/config/ui/hud/mapmodel_browser.cfg b/config/ui/hud/mapmodel_browser.cfg index e23157223..dcc7e1e34 100644 --- a/config/ui/hud/mapmodel_browser.cfg +++ b/config/ui/hud/mapmodel_browser.cfg @@ -54,11 +54,11 @@ uiLiteMenu "mapmodel_browser" [ uimodelpreview (mapmodelname $i 1) "mapmodel" uiclamp*e uispace $uiPad:S $uiPad:O3 [ - uifonttext "def.ol" $i 0.55 + uifonttext "default" $i 0.55 ] ; uialign- -1 -1 if (iskeyheld "LSHIFT") [ uispace $uiPad:S 0 [ - uifonttext "webdings.ol" "q" 0.65 + uifonttext "webdings" "q" 0.65 ] ; uialign- -1 1 uirelease [ clearmodel (+s "mapmodel/" (mapmodelname $i)) ] ] [ @@ -77,14 +77,14 @@ uiLiteMenu "mapmodel_browser" [ uimodelpreview (mapmodelname $i 1) "mapmodel" uiclamp*e uispace $uiPad:S $uiPad:O3 [ - uifontcolortext "def.ol" $i (|A! 0xA0) 0.55 + uifontcolortext "default" $i (|A! 0xA0) 0.55 ] ; uialign- -1 -1 uihover [ .mm_selidx = $n ; uiSetMillis ] uioutline $c_line1 $.z $.z ] if (mapmodelloaded $i) [ uispace $uiPad:S $uiPad:O3 [ - uifonttext "webdings.ol" "a" 0.55 + uifonttext "webdings" "a" 0.55 ] ; uialign- 1 -1 ] uiHoverOnce [ uiHoverSound ] diff --git a/config/ui/hud/texture_browser.cfg b/config/ui/hud/texture_browser.cfg index 13c14293b..3757f6632 100644 --- a/config/ui/hud/texture_browser.cfg +++ b/config/ui/hud/texture_browser.cfg @@ -29,7 +29,7 @@ uiLiteMenu "texture_browser" [ ] uispace $uiPad:S $uiPad:O3 [ uialign -1 -1 - uifontcolortext "def.ol" $i (|A! 0x98) 0.55 + uifontcolortext "default" $i (|A! 0x98) 0.55 ] ] uipress [ uiSetMillis ] @@ -83,7 +83,7 @@ uiLiteMenu "texture_browser" [ uiclip 0 $uiPad:6XL [ uicolortext "/" $c_gray 1.2 ] ] // text takes up a lot of vertical space, have to "cut" it uifill $uiPad:DXS 0 [ - uifonttext "def.ol" (max $.tb_totalpg 1) 0.7 + uifonttext "default" (max $.tb_totalpg 1) 0.7 uialign- -1 1 ] uiclamp*y @@ -110,7 +110,7 @@ uiLiteMenu "texture_browser" [ uislotview $i uiclamp*e uispace $uiPad:S $uiPad:O3 [ - uifontcolortext "def.ol" $i (|A! 0xA0) 0.55 + uifontcolortext "default" $i (|A! 0xA0) 0.55 ] ; uialign- -1 -1 uirelease [ settex (getslottex $i) diff --git a/config/ui/lib.cfg b/config/ui/lib.cfg index 702f8681f..1f3885e86 100644 --- a/config/ui/lib.cfg +++ b/config/ui/lib.cfg @@ -45,8 +45,8 @@ uiclamp*x = [ uiclamp* 1 1 0 0 ] uiclamp*y = [ uiclamp* 0 0 1 1 ] uiclamp*e = [ uiclamp* 1 1 1 1 ] -uifonttext = [ uifont $arg1 [ uitext $arg2 $arg3 ] ] -uifontcolortext = [ uifont $arg1 [ uicolortext $arg2 $arg3 $arg4 ] ] +uifonttext = [ uifont $arg1 [ uitext $arg2 $arg3 $arg4 ] ] +uifontcolortext = [ uifont $arg1 [ uicolortext $arg2 $arg3 $arg4 $arg5 ] ] changeui = [ if $arg2 [] [ arg2 = $uiname ] @@ -793,23 +793,17 @@ uiButton = [ // uiFancyText FONT TEXT SIZE 0xCOLOR TRANSPARENCY uiFancyText = [ - if (=s $arg1 "") [ arg1 = "def" ] + if (=s $arg1 "") [ arg1 = "default" ] if $arg4 [] [ arg4 = $c_white ] if (< $numargs 5) [ arg5 = 1 ] - uigroup [ - uioffset $uiPad:O1- $uiPad:O1 [ - uifontcolortext [@arg1.ol] (stripcolors $arg2) (|A (*fA $arg5 0x20)) $arg3 - uifontcolortext $arg1 (stripcolors $arg2) (|A (*fA $arg5)) $arg3 - ] - uioffset $uiPad:O1 $uiPad:O1- [ - uifontcolortext $arg1 $arg2 (|A (*fA $arg5) $arg4) $arg3 - ] + uigroup [ // DO NOT REMOVE! + uifontcolortext $arg1 $arg2 (|A (*fA $arg5) $arg4) $arg3 1 ] ] // uiEmbossText FONT TEXT SIZE 0xCOLOR uiEmbossText = [ - if (=s $arg1 "") [ arg1 = "def" ] + if (=s $arg1 "") [ arg1 = "default" ] uigroup [ uioffset $uiPad:O2- $uiPad:O2 [ uifontcolortext $arg1 (stripcolors $arg2) 0 $arg3 @@ -851,7 +845,7 @@ uiVerSld = [ ] ; uiclamp-x uivlist $uiPad:M- [ loop i (strlen $arg6) [ - uifontcolortext "def.ol" (substr $arg6 $i 1) (|A! (? $arg9 0xA0 0x60)) 0.65 + uifontcolortext "default" (substr $arg6 $i 1) (|A! (? $arg9 0xA0 0x60)) 0.65 ] ] ] @@ -880,7 +874,7 @@ uiHorSld = [ uiclamp*y ] ] ; uiclamp-y - uifontcolortext "def.ol" $arg6 (|A! (? $arg9 0xA0 0x60)) 0.65 + uifontcolortext "default" $arg6 (|A! (? $arg9 0xA0 0x60)) 0.65 ] uiclamp*e ] @@ -910,7 +904,7 @@ uiVerColorSld = [ ] ; uiclamp-x uivlist $uiPad:M- [ loop i (strlen $$arg1) [ - uifontcolortext "wide.ol" (substr $$arg1 $i 1) (|A! (? $arg9 0xA0 0x60)) 0.7 + uifontcolortext "wide" (substr $$arg1 $i 1) (|A! (? $arg9 0xA0 0x60)) 0.7 ] ] ] @@ -940,7 +934,7 @@ uiHorColorSld = [ uiclamp*y ] ] ; uiclamp-y - uifontcolortext "wide.ol" $$arg1 (|A! (? $arg9 0xA0 0x60)) 0.7 + uifontcolortext "wide" $$arg1 (|A! (? $arg9 0xA0 0x60)) 0.7 ] uiclamp*e ] diff --git a/config/ui/libnew.cfg b/config/ui/libnew.cfg index 11e312742..8cfa6e6c9 100644 --- a/config/ui/libnew.cfg +++ b/config/ui/libnew.cfg @@ -170,7 +170,7 @@ uiSliderH = [ uiFastImgStretched "" "shadow2" "" "" [ uiclamp.x ] uiclamp*e if $arg9 [ arg10 = 0xA0FFFFFF ] [ arg10 = 0x60FFFFFF ] - uiFontColorText "def.ol" $arg6 0.65 $arg10 + uiFontColorText "default" $arg6 0.65 $arg10 ] ; uiclamp-y ] ] ; uiclamp-y diff --git a/config/ui/menus/server_browser.cfg b/config/ui/menus/server_browser.cfg index c1f3a0f57..07cb060eb 100644 --- a/config/ui/menus/server_browser.cfg +++ b/config/ui/menus/server_browser.cfg @@ -163,7 +163,7 @@ uiServer = [ uigroup [ uifill $uiPad:DS uiimage (+s "data/interface/ui/" (servinfomastermodeicon $arg1) ".png") $uiPad:DSS $uiPad:DSS - uispace 0 $uiPad:L [ uifonttext "wide.ol" (servinfomodename $arg1) 0.5 ] + uispace 0 $uiPad:L [ uifonttext "wide" (servinfomodename $arg1) 0.5 ] uialign- 0 1 ] uivlist $uiPad:S [ diff --git a/config/ui/permanent.cfg b/config/ui/permanent.cfg index cb04915a9..8fc00f00b 100644 --- a/config/ui/permanent.cfg +++ b/config/ui/permanent.cfg @@ -28,8 +28,8 @@ newui "permanent" [ uivlist $uiPad:3XL- [ uiFastImg "" "badge_tesseract" "" "" $uiPad:D2XL uivlist $uiPad:M- [ - uifontcolortext "wide.ol" "TESSERACT" $c_cyan_t 0.6 - uifontcolortext "wide.ol" "ENGINE" $c_cyan_t 0.7 + uifontcolortext "wide" "TESSERACT" $c_cyan_t 0.6 + uifontcolortext "wide" "ENGINE" $c_cyan_t 0.7 ] ] ] diff --git a/config/ui/style.cfg b/config/ui/style.cfg index eb34dde05..6a7ffb9b0 100644 --- a/config/ui/style.cfg +++ b/config/ui/style.cfg @@ -3,8 +3,6 @@ // Text Brightness & Default Paddings // /////////////////////////////////////////////////////////////////////////////// -textbright 95 - // ultra series uiPad:UXL = 1.2 // DXL * 10 uiPad:UL = 1.0 // DL * 10 diff --git a/source/Makefile b/source/Makefile index 0c8e593ce..bf8b93e12 100644 --- a/source/Makefile +++ b/source/Makefile @@ -56,8 +56,8 @@ CLIENT_LIBS= -F../bin/valhalla.app/Contents/Frameworks/ -framework SDL2 -framewo CLIENT_LIBS+= -framework SDL2_mixer -framework CoreAudio -framework AudioToolbox CLIENT_LIBS+= -framework AudioUnit -framework OpenGL -framework Cocoa -lz -Lenet -lenet else -CLIENT_INCLUDES= $(INCLUDES) -I/usr/X11R6/include `sdl2-config --cflags` -CLIENT_LIBS= -Lenet -lenet -L/usr/X11R6/lib -lX11 `sdl2-config --libs` -lSDL2_image -lSDL2_mixer -lz -lGL +CLIENT_INCLUDES= $(INCLUDES) -I/usr/X11R6/include `sdl2-config --cflags` `pkg-config --cflags pangocairo` `pkg-config --cflags fontconfig` +CLIENT_LIBS= -Lenet -lenet -L/usr/X11R6/lib -lX11 `sdl2-config --libs` -lSDL2_image -lSDL2_mixer `pkg-config --libs pangocairo` `pkg-config --libs fontconfig` -lz -lGL endif endif ifeq ($(PLATFORM),LINUX) @@ -220,12 +220,6 @@ server: libenet $(SERVER_OBJS) master: libenet $(MASTER_OBJS) $(CXX) $(CXXFLAGS) -o tess_master $(MASTER_OBJS) $(MASTER_LIBS) -shared/tessfont.o: shared/tessfont.c - $(CXX) $(CXXFLAGS) -c -o $@ $< `freetype-config --cflags` - -tessfont: shared/tessfont.o - $(CXX) $(CXXFLAGS) -o tessfont shared/tessfont.o `freetype-config --libs` -lz - ifneq (,$(findstring DARWIN,$(PLATFORM))) install: client cp tess_client ../bin/valhalla.app/Contents/MacOS/valhalla_universal @@ -267,6 +261,7 @@ shared/stream.o: engine/sound.h shared/iengine.h shared/igame.h shared/tools.o: shared/cube.h shared/tools.h shared/geom.h shared/ents.h shared/tools.o: shared/command.h shared/glexts.h shared/glemu.h shared/tools.o: engine/sound.h shared/iengine.h shared/igame.h +shared/tools.o: shared/unicode.h shared/zip.o: shared/cube.h shared/tools.h shared/geom.h shared/ents.h shared/zip.o: shared/command.h shared/glexts.h shared/glemu.h engine/sound.h shared/zip.o: shared/iengine.h shared/igame.h @@ -290,16 +285,16 @@ engine/client.o: shared/ents.h shared/command.h shared/glexts.h engine/client.o: shared/glemu.h engine/sound.h shared/iengine.h engine/client.o: shared/igame.h engine/world.h engine/octa.h engine/light.h engine/client.o: engine/texture.h engine/bih.h engine/model.h -engine/command.o: engine/engine.h shared/cube.h shared/tools.h shared/geom.h +engine/command.o: shared/unicode.h shared/cube.h shared/tools.h shared/geom.h engine/command.o: shared/ents.h shared/command.h shared/glexts.h engine/command.o: shared/glemu.h engine/sound.h shared/iengine.h -engine/command.o: shared/igame.h engine/world.h engine/octa.h engine/light.h -engine/command.o: engine/texture.h engine/bih.h engine/model.h -engine/console.o: engine/engine.h shared/cube.h shared/tools.h shared/geom.h +engine/command.o: shared/igame.h engine/engine.h engine/world.h engine/octa.h +engine/command.o: engine/light.h engine/texture.h engine/bih.h engine/model.h +engine/console.o: shared/unicode.h shared/cube.h shared/tools.h shared/geom.h engine/console.o: shared/ents.h shared/command.h shared/glexts.h engine/console.o: shared/glemu.h engine/sound.h shared/iengine.h -engine/console.o: shared/igame.h engine/world.h engine/octa.h engine/light.h -engine/console.o: engine/texture.h engine/bih.h engine/model.h +engine/console.o: shared/igame.h engine/engine.h engine/world.h engine/octa.h +engine/console.o: engine/light.h engine/texture.h engine/bih.h engine/model.h engine/dynlight.o: engine/engine.h shared/cube.h shared/tools.h shared/geom.h engine/dynlight.o: shared/ents.h shared/command.h shared/glexts.h engine/dynlight.o: shared/glemu.h engine/sound.h shared/iengine.h @@ -400,12 +395,12 @@ engine/rendersky.o: shared/glexts.h shared/glemu.h engine/sound.h engine/rendersky.o: shared/iengine.h shared/igame.h engine/world.h engine/rendersky.o: engine/octa.h engine/light.h engine/texture.h engine/rendersky.o: engine/bih.h engine/model.h -engine/rendertext.o: engine/engine.h shared/cube.h shared/tools.h +engine/rendertext.o: shared/unicode.h shared/cube.h shared/tools.h engine/rendertext.o: shared/geom.h shared/ents.h shared/command.h engine/rendertext.o: shared/glexts.h shared/glemu.h engine/sound.h -engine/rendertext.o: shared/iengine.h shared/igame.h engine/world.h -engine/rendertext.o: engine/octa.h engine/light.h engine/texture.h -engine/rendertext.o: engine/bih.h engine/model.h +engine/rendertext.o: shared/iengine.h shared/igame.h engine/engine.h +engine/rendertext.o: engine/world.h engine/octa.h engine/light.h +engine/rendertext.o: engine/texture.h engine/bih.h engine/model.h engine/renderva.o: engine/engine.h shared/cube.h shared/tools.h shared/geom.h engine/renderva.o: shared/ents.h shared/command.h shared/glexts.h engine/renderva.o: shared/glemu.h engine/sound.h shared/iengine.h @@ -446,7 +441,7 @@ engine/ui.o: engine/engine.h shared/cube.h shared/tools.h shared/geom.h engine/ui.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h engine/ui.o: engine/sound.h shared/iengine.h shared/igame.h engine/world.h engine/ui.o: engine/octa.h engine/light.h engine/texture.h engine/bih.h -engine/ui.o: engine/model.h engine/textedit.h +engine/ui.o: engine/model.h engine/textedit.h shared/unicode.h engine/liquid.o: engine/engine.h shared/cube.h shared/tools.h shared/geom.h engine/liquid.o: shared/ents.h shared/command.h shared/glexts.h engine/liquid.o: shared/glemu.h engine/sound.h shared/iengine.h @@ -534,11 +529,11 @@ standalone/shared/stream.o: shared/ents.h shared/command.h engine/sound.h standalone/shared/stream.o: shared/iengine.h shared/igame.h standalone/shared/tools.o: shared/cube.h shared/tools.h shared/geom.h standalone/shared/tools.o: shared/ents.h shared/command.h engine/sound.h -standalone/shared/tools.o: shared/iengine.h shared/igame.h -standalone/engine/command.o: engine/engine.h shared/cube.h shared/tools.h +standalone/shared/tools.o: shared/iengine.h shared/igame.h shared/unicode.h +standalone/engine/command.o: shared/unicode.h shared/cube.h shared/tools.h standalone/engine/command.o: shared/geom.h shared/ents.h shared/command.h standalone/engine/command.o: engine/sound.h shared/iengine.h shared/igame.h -standalone/engine/command.o: engine/world.h +standalone/engine/command.o: engine/engine.h engine/world.h standalone/engine/server.o: engine/engine.h shared/cube.h shared/tools.h standalone/engine/server.o: shared/geom.h shared/ents.h shared/command.h standalone/engine/server.o: engine/sound.h shared/iengine.h shared/igame.h diff --git a/source/engine/command.cpp b/source/engine/command.cpp index a9e30e045..469c0f9c7 100644 --- a/source/engine/command.cpp +++ b/source/engine/command.cpp @@ -1,6 +1,7 @@ // command.cpp: implements the parsing and execution of a tiny script language which // is largely backwards compatible with the quake console language. +#include "unicode.h" #include "engine.h" hashnameset idents; // contains ALL vars/commands/aliases @@ -924,22 +925,34 @@ const char *parsestring(const char *p) int unescapestring(char *dst, const char *src, const char *end) { char *start = dst; - while(src < end) + uint c; + uint s = uni_getchar(src, c); + for(char *p = (char *)src; src < end; p += s, s = uni_getchar(p, c)) { - int c = *src++; + if(iscubecntrl(c) || c == 0xFFFD) + { + src += s; + continue; + } if(c == '^') { - if(src >= end) break; - int e = *src++; - switch(e) + if(src+1 >= end) break; + switch(*(src+1)) { - case 'n': *dst++ = '\n'; break; - case 't': *dst++ = '\t'; break; - case 'f': *dst++ = '\f'; break; - default: *dst++ = e; break; + case 'n': *dst++ = '\n'; ++src; ++p; break; + case 'r': *dst++ = '\r'; ++src; ++p; break; + case 't': *dst++ = '\t'; ++src; ++p; break; + case 'f': *dst++ = '\f'; ++src; ++p; break; + case '^': *dst++ = '^' ; ++src; ++p; break; + default: break; } + ++src; + } + else + { + loopi(s) *dst++ = p[i]; + src += s; } - else *dst++ = c; } *dst = '\0'; return dst - start; @@ -3161,21 +3174,29 @@ bool execfile(const char *cfgfile, bool msg) } ICOMMAND(exec, "sb", (char *file, int *msg), intret(execfile(file, *msg != 0) ? 1 : 0)); -const char *escapestring(const char *s) +const char *escapestring(const char *str) { stridx = (stridx + 1)%4; vector &buf = strbuf[stridx]; buf.setsize(0); buf.add('"'); - for(; *s; s++) switch(*s) + uint c; + uint s = uni_getchar(str, c); + for(char *p = (char *)str; c; p += s, s = uni_getchar(p, c)) switch(c) { case '\n': buf.put("^n", 2); break; + case '\r': buf.put("^r", 2); break; case '\t': buf.put("^t", 2); break; case '\f': buf.put("^f", 2); break; case '"': buf.put("^\"", 2); break; case '^': buf.put("^^", 2); break; - default: buf.add(*s); break; + default: + { + if(iscubecntrl(c) || c == 0xFFFD) break; // filter out control characters + buf.put(p, s); break; + } } + buf.put("\"\0", 2); return buf.getbuf(); } @@ -3215,7 +3236,7 @@ bool validateblock(const char *s) #ifndef STANDALONE void writecfg(const char *name) { - stream *f = openutf8file(path(name && name[0] ? name : game::savedconfig(), true), "w"); + stream *f = openfile(path(name && name[0] ? name : game::savedconfig(), true), "w"); if(!f) return; f->printf("// automatically written on exit, DO NOT MODIFY\n// delete this file to have %s overwrite these settings\n// modify settings in game, or put settings in %s to override anything\n\n", game::defaultconfig(), game::autoexec()); game::writeclientinfo(f); @@ -3668,30 +3689,39 @@ COMMAND(at, "si1V"); void substr(char *s, int *start, int *count, int *numargs) { - int len = strlen(s), offset = clamp(*start, 0, len); - commandret->setstr(newstring(&s[offset], *numargs >= 3 ? clamp(*count, 0, len - offset) : len - offset)); + int len = strlen(s), offset = clamp(uni_offset(s, *start), 0, len); + int n = *numargs >= 3 ? clamp(uni_offset(s, *start + *count) - offset, 0, len - offset) : len - offset; + commandret->setstr(newstring(&s[offset], n)); } COMMAND(substr, "siiN"); void chopstr(char *s, int *lim, char *ellipsis) { - int len = strlen(s), maxlen = abs(*lim); - if(len > maxlen) + const int uni_len = uni_strlen(s); + int maxlen = abs(*lim); + if(uni_len > maxlen) { - int elen = strlen(ellipsis); - maxlen = max(maxlen, elen); - char *chopped = newstring(maxlen); - if(*lim < 0) + const int elen = strlen(ellipsis), uni_elen = uni_strlen(ellipsis); + maxlen = max(maxlen, uni_elen); + char *chopped = newstring(4 * maxlen); + if(*lim < 0) // strchop "abcdef" -3 "" => def ; strchop "abcdef" -3 "AB" => ABf ; strchop "abcdef" -3 "ABCD" => ABCD { + uint _c; + const int offset = uni_noffset(s, maxlen - uni_elen); + int n = uni_offset(s + offset, maxlen - uni_elen); + n += uni_getchar(s + offset + n, _c); + memcpy(chopped, ellipsis, elen); - memcpy(&chopped[elen], &s[len - (maxlen - elen)], maxlen - elen); + memcpy(&chopped[elen], &s[offset], n); + chopped[elen+n] = '\0'; } - else + else // strchop "abcdef" 3 "" => abc ; strchop "abcdef" 3 "AB" => aAB ; strchop "abcdef" 3 "ABCD" => ABCD { - memcpy(chopped, s, maxlen - elen); - memcpy(&chopped[maxlen - elen], ellipsis, elen); + const int n = uni_offset(s, maxlen - uni_elen); + memcpy(chopped, s, n); + memcpy(&chopped[n], ellipsis, elen); + chopped[elen+n] = '\0'; } - chopped[maxlen] = '\0'; commandret->setstr(chopped); } else result(s); @@ -3713,7 +3743,7 @@ ICOMMAND(stripcolors, "s", (char *s), { int len = strlen(s); char *d = newstring(len); - filtertext(d, s, false, true, true, false, len); + filtertext(d, s, T_NEWLINES | T_WHITESPACE, len); stringret(d); }); @@ -4395,7 +4425,7 @@ CMPSCMD(>=s, >=); ICOMMAND(echo, "C", (char *s), conoutf(CON_ECHO, "\f1%s", s)); ICOMMAND(error, "C", (char *s), conoutf(CON_ERROR, "%s", s)); -ICOMMAND(strstr, "ss", (char *a, char *b), { char *s = strstr(a, b); intret(s ? s-a : -1); }); +ICOMMAND(strstr, "ss", (char *a, char *b), { char *s = strstr(a, b); intret(s ? uni_index(a, s-a) : -1); }); ICOMMAND(strrstr, "ss", (char *a, char *b), { if(!b[0]) intret(strlen(a)); @@ -4403,14 +4433,17 @@ ICOMMAND(strrstr, "ss", (char *a, char *b), { char *last = NULL; for(char *cur = a; char *s = strstr(cur, b); last = s, cur = s+1); - intret(last ? last-a : -1); + intret(last ? uni_index(a, last-a) : -1); } }); -ICOMMAND(strlen, "s", (char *s), intret(strlen(s))); -ICOMMAND(strcode, "si", (char *s, int *i), intret(*i > 0 ? (memchr(s, 0, *i) ? 0 : uchar(s[*i])) : uchar(s[0]))); -ICOMMAND(codestr, "i", (int *i), { char *s = newstring(1); s[0] = char(*i); s[1] = '\0'; stringret(s); }); -ICOMMAND(struni, "si", (char *s, int *i), intret(*i > 0 ? (memchr(s, 0, *i) ? 0 : cube2uni(s[*i])) : cube2uni(s[0]))); -ICOMMAND(unistr, "i", (int *i), { char *s = newstring(1); s[0] = uni2cube(*i); s[1] = '\0'; stringret(s); }); +ICOMMAND(strlen, "s", (char *s), intret(uni_strlen(s))); +ICOMMAND(strcode, "si", (char *s, int *i), intret(uni_charat(s, *i))); +ICOMMAND(codestr, "i", (int *i), +{ + char *dst = newstring(4); + uni_code2str(*i, dst); + stringret(dst); +}) int naturalsort(const char *a, const char *b) { @@ -4438,18 +4471,20 @@ int naturalsort(const char *a, const char *b) } ICOMMAND(naturalsort, "ss", (char *a, char *b), intret(naturalsort(a,b)<=0)); -#define STRMAPCOMMAND(name, map) \ - ICOMMAND(name, "s", (char *s), \ - { \ - int len = strlen(s); \ - char *m = newstring(len); \ - loopi(len) m[i] = map(s[i]); \ - m[len] = '\0'; \ - stringret(m); \ - }) - -STRMAPCOMMAND(strlower, cubelower); -STRMAPCOMMAND(strupper, cubeupper); +ICOMMAND(strlower, "s", (char *s), { + int len = strlen(s); + char *m = newstring(len); + uni_strlower(s, m); + m[len] = '\0'; + stringret(m); +}); +ICOMMAND(strupper, "s", (char *s), { + int len = strlen(s); + char *m = newstring(len); + uni_strupper(s, m); + m[len] = '\0'; + stringret(m); +}); char *strreplace(const char *s, const char *oldval, const char *newval, const char *newval2) { @@ -4480,8 +4515,8 @@ ICOMMAND(strreplace, "ssss", (char *s, char *o, char *n, char *n2), commandret-> void strsplice(const char *s, const char *vals, int *skip, int *count) { int slen = strlen(s), vlen = strlen(vals), - offset = clamp(*skip, 0, slen), - len = clamp(*count, 0, slen - offset); + offset = clamp(uni_offset(s, *skip), 0, slen), + len = clamp(uni_offset(s+offset, *count), 0, slen - offset); char *p = newstring(slen - len + vlen); if(offset) memcpy(p, s, offset); if(vlen) memcpy(&p[offset], vals, vlen); diff --git a/source/engine/console.cpp b/source/engine/console.cpp index a78cfb9b0..f1c483031 100644 --- a/source/engine/console.cpp +++ b/source/engine/console.cpp @@ -1,18 +1,27 @@ // console.cpp: the console buffer, its display, and command line control +#include "unicode.h" #include "engine.h" #define MAXCONLINES 1000 -struct cline { char *line; int type, outtime; }; +struct cline { char *line; int type, outtime; float fontsize, w; textinfo info; }; reversequeue conlines; -int commandmillis = -1; +int commandmillis = -1, inputmillis = 0; string commandbuf; char *commandaction = NULL, *commandprompt = NULL; enum { CF_COMPLETE = 1<<0, CF_EXECUTE = 1<<1 }; int commandflags = 0, commandpos = -1; -VARFP(maxcon, 10, 200, MAXCONLINES, { while(conlines.length() > maxcon) delete[] conlines.pop().line; }); +VARFP(maxcon, 10, 200, MAXCONLINES, +{ + while(conlines.length() > maxcon) + { + cline &cl = conlines.pop(); + delete[] cl.line; + if(cl.info.tex) glDeleteTextures(1, &cl.info.tex); + } +}); #define CONSTRLEN 512 @@ -42,6 +51,11 @@ const char *getprefix(int type) void conline(int type, const char *sf) // add a line to the console buffer { + if(!conlines.length()) + { + // initialize the queue with zeroes + memset(conlines.data, 0, MAXCONLINES * sizeof(cline)); + } char *buf = NULL; if(type&CON_TAG_MASK) for(int i = conlines.length()-1; i >= max(conlines.length()-contags, 0); i--) { @@ -55,9 +69,16 @@ void conline(int type, const char *sf) // add a line to the console buffer } if(!buf) buf = conlines.length() >= maxcon ? conlines.remove().line : newstring("", CONSTRLEN-1); cline &cl = conlines.add(); + if(cl.info.tex) + { + glDeleteTextures(1, &cl.info.tex); + cl.info.tex = 0; + } cl.line = buf; cl.type = type; cl.outtime = totalmillis; // for how long to keep line on screen + cl.fontsize = cl.w = 0; + cl.info = {0, 0, 0}; defformatstring(prefixedsf, "%s%s", getprefix(type), sf); copystring(cl.line, prefixedsf, CONSTRLEN); } @@ -82,6 +103,9 @@ ICOMMAND(fullconsole, "iN$", (int *val, int *numargs, ident *id), }); ICOMMAND(toggleconsole, "", (), UI::toggleui("fullconsole")); +// applies a black shadow to console text to improve visibility, the value controls the intensity of the shadow +VARP(conshadow, 0, 255, 255); + float rendercommand(float x, float y, float w) { if(commandmillis < 0) return 0; @@ -90,11 +114,25 @@ float rendercommand(float x, float y, float w) const char *prompt = commandprompt ? commandprompt : ">"; formatstring(buf, "%s %s", prompt, commandbuf); - float width, height; - text_boundsf(buf, width, height, w); - y -= height; - draw_text(buf, x, y, 0xFF, 0xFF, 0xFF, 0xFF, commandpos>=0 ? commandpos+1 + strlen(prompt) : strlen(buf), w); - return height; + pushfont(); + setfont("default"); + textinfo info; + prepare_text(buf, info, w, bvec(255, 255, 255), commandpos>=0 ? commandpos+1 + strlen(prompt) : strlen(buf)); + y -= info.h; + + if(info.tex) + { + if(conshadow) + { + const float d = 3.f / 4.f * conscale; + draw_text(info, x - d, y + d, conshadow, true); + } + draw_text(info, x, y); + glDeleteTextures(1, &info.tex); + } + + popfont(); + return info.h; } VARP(consize, 0, 5, 100); @@ -129,9 +167,32 @@ void setconskip(int &skip, int filter, int n) ICOMMAND(conskip, "i", (int *n), setconskip(conskip, UI::uivisible("fullconsole") ? fullconfilter : confilter, *n)); ICOMMAND(miniconskip, "i", (int *n), setconskip(miniconskip, miniconfilter, *n)); -ICOMMAND(clearconsole, "", (), { while(conlines.length()) delete[] conlines.pop().line; }); +ICOMMAND(clearconsole, "", (), +{ + while(conlines.length()) + { + logoutf("len=%d", conlines.length()); + cline &cl = conlines.pop(); + delete[] cl.line; + if(cl.info.tex) + { + logoutf("deleting texture %d", cl.info.tex); + glDeleteTextures(1, &cl.info.tex); + } + } +}); + +// free conline textures, necessary when changing font settings or calling `resetgl()` +void clearconsoletextures() +{ + loopv(conlines) if(conlines[i].info.tex) + { + glDeleteTextures(1, &conlines[i].info.tex); + conlines[i].info.tex = 0; + } +} -float drawconlines(int conskip, int confade, float conwidth, float conheight, float conoff, int filter, float y = 0, int dir = 1) +float drawconlines(int conskip, int confade, float conwidth, float conheight, float conoff, int maxlines, int filter, float y = 0, int dir = 1) { filter &= CON_FLAGS; int numl = conlines.length(), offset = min(conskip, numl); @@ -147,15 +208,25 @@ float drawconlines(int conskip, int confade, float conwidth, float conheight, fl } int totalheight = 0; + int n = 0; loopi(numl) //determine visible height { // shuffle backwards to fill if necessary int idx = offset+i < numl ? offset+i : --offset; if(!(conlines[idx].type&filter)) continue; char *line = conlines[idx].line; - float width, height; - text_boundsf(line, width, height, conwidth); - if(totalheight + height > conheight) { numl = i; if(offset == idx) ++offset; break; } + int width, height; + if(conlines[idx].w != conwidth || conlines[idx].fontsize != fontsize || !conlines[idx].info.tex) + { + measure_text(line, conwidth, width, height); + } + else + { + width = conlines[idx].info.w; + height = conlines[idx].info.h; + } + if(maxlines > 0) { if(++n > maxlines) { numl = i; if(offset == idx) ++offset; break; } } + else if(totalheight + height > conheight) { numl = i; if(offset == idx) ++offset; break; } totalheight += height; } if(dir > 0) y = conoff; @@ -164,32 +235,54 @@ float drawconlines(int conskip, int confade, float conwidth, float conheight, fl int idx = offset + (dir > 0 ? numl-i-1 : i); if(!(conlines[idx].type&filter)) continue; char *line = conlines[idx].line; - float width, height; - text_boundsf(line, width, height, conwidth); - if(dir <= 0) y -= height; - draw_text(line, conoff, y, 0xFF, 0xFF, 0xFF, 0xFF, -1, conwidth); - if(dir > 0) y += height; + textinfo &info = conlines[idx].info; + if(conlines[idx].w != conwidth || conlines[idx].fontsize != fontsize || !info.tex) + { + if(info.tex) glDeleteTextures(1, &info.tex); + prepare_text(line, info, conwidth); + conlines[idx].w = conwidth; + conlines[idx].fontsize = fontsize; + } + if(dir <= 0) y -= info.h; + if(info.tex) + { + if(conshadow) + { + const float d = 3.f / 4.f * conscale; + draw_text(info, conoff-d, y+d, conshadow, true); + } + draw_text(info, conoff, y); + } + if(dir > 0) y += info.h; } return y+conoff; } float renderfullconsole(float w, float h) { - float conpad = FONTH/2, + pushfont(); + setfont("default"); + setfontsize(hudh * conscale / CONSOLETEXTROWS); + float conpad = FONTH*1.5/2, conheight = h - 2*conpad, conwidth = w - 2*conpad; - drawconlines(conskip, 0, conwidth, conheight, conpad, fullconfilter); + drawconlines(conskip, 0, conwidth, conheight, conpad, 0, fullconfilter); + popfont(); return conheight + 2*conpad; } float renderconsole(float w, float h, float abovehud) { - float conpad = FONTH/2, - conheight = min(float(FONTH*consize), h - 2*conpad), + float conpad = FONTH*1.5/2, + conheight = min(float(FONTH*1.5*consize), h - 2*conpad), conwidth = w - 2*conpad - game::clipconsole(w, h); - float y = drawconlines(conskip, confade, conwidth, conheight, conpad, confilter); + pushfont(); + setfont("default"); + setfontsize(hudh * conscale / CONSOLETEXTROWS); + float y = drawconlines(conskip, confade, conwidth, conheight, conpad, consize, confilter); if(miniconsize && miniconwidth) - drawconlines(miniconskip, miniconfade, (miniconwidth*(w - 2*conpad))/100, min(float(FONTH*miniconsize), abovehud - y), conpad, miniconfilter, abovehud, -1); + drawconlines(miniconskip, miniconfade, (miniconwidth*(w - 2*conpad))/100, min(float(FONTH*1.5*miniconsize), abovehud - y), conpad, miniconsize, miniconfilter, abovehud, -1); + popfont(); return y; } @@ -317,6 +410,7 @@ VARP(fullconsolecommand, 0, 1, 1); void inputcommand(char *init, char *action = NULL, char *prompt = NULL, char *flags = NULL) // turns input to the command line on or off { + inputmillis = totalmillis; commandmillis = init ? totalmillis : -1; textinput(commandmillis >= 0, TI_CONSOLE); keyrepeat(commandmillis >= 0, KR_CONSOLE); @@ -359,6 +453,7 @@ struct hline void restore() { + inputmillis = totalmillis; copystring(commandbuf, buf); if(commandpos >= (int)strlen(commandbuf)) commandpos = -1; DELETEA(commandaction); @@ -490,6 +585,7 @@ void execbind(keym &k, bool isdown) bool consoleinput(const char *str, int len) { if(commandmillis < 0) return false; + inputmillis = totalmillis; resetcomplete(); int cmdlen = (int)strlen(commandbuf), cmdspace = int(sizeof(commandbuf)) - (cmdlen+1); @@ -514,10 +610,7 @@ void pasteconsole() if(!SDL_HasClipboardText()) return; char *cb = SDL_GetClipboardText(); if(!cb) return; - string paste; - size_t decoded = decodeutf8((uchar *)paste, sizeof(paste)-1, (const uchar *)cb, strlen(cb)); - paste[decoded] = '\0'; - consoleinput(paste, decoded); + consoleinput(cb, strlen(cb)); SDL_free(cb); } @@ -546,6 +639,7 @@ static char *skipwordrev(char *s, int n = -1) bool consolekey(int code, bool isdown) { if(commandmillis < 0) return false; + inputmillis = totalmillis; #ifdef __APPLE__ #define MOD_KEYS (KMOD_LGUI|KMOD_RGUI) @@ -575,7 +669,9 @@ bool consolekey(int code, bool isdown) { int len = (int)strlen(commandbuf); if(commandpos<0) break; - int end = commandpos+1; + uint _codepoint; + const int s = uni_getchar(&commandbuf[commandpos], _codepoint); + int end = commandpos+s; if(SDL_GetModState()&SKIP_KEYS) end = skipword(&commandbuf[commandpos]) - commandbuf; memmove(&commandbuf[commandpos], &commandbuf[end], len + 1 - end); resetcomplete(); @@ -587,7 +683,7 @@ bool consolekey(int code, bool isdown) { int len = (int)strlen(commandbuf), i = commandpos>=0 ? commandpos : len; if(i<1) break; - int start = i-1; + int start = i - uni_prevchar(commandbuf, i); if(SDL_GetModState()&SKIP_KEYS) start = skipwordrev(commandbuf, i) - commandbuf; memmove(&commandbuf[start], &commandbuf[i], len - i + 1); resetcomplete(); @@ -598,15 +694,19 @@ bool consolekey(int code, bool isdown) case SDLK_LEFT: if(SDL_GetModState()&SKIP_KEYS) commandpos = skipwordrev(commandbuf, commandpos) - commandbuf; - else if(commandpos>0) commandpos--; - else if(commandpos<0) commandpos = (int)strlen(commandbuf)-1; + else if(commandpos>0) commandpos -= uni_prevchar(commandbuf, commandpos); + else if(commandpos<0) commandpos = (int)strlen(commandbuf) - uni_prevchar(commandbuf, strlen(commandbuf)); break; case SDLK_RIGHT: if(commandpos>=0) { if(SDL_GetModState()&SKIP_KEYS) commandpos = skipword(&commandbuf[commandpos]) - commandbuf; - else ++commandpos; + else + { + uint _codepoint; + commandpos += uni_getchar(&commandbuf[commandpos], _codepoint); + } if(commandpos>=(int)strlen(commandbuf)) commandpos = -1; } break; diff --git a/source/engine/engine.h b/source/engine/engine.h index 8dc0a8165..e99449010 100644 --- a/source/engine/engine.h +++ b/source/engine/engine.h @@ -33,39 +33,38 @@ extern int screenw, screenh, renderw, renderh, hudw, hudh; extern vector entgroup; // rendertext -struct font +struct textinfo { - struct charinfo - { - float x, y, w, h, offsetx, offsety, advance; - int tex; - }; - - char *name; - vector texs; - vector chars; - int charoffset, defaultw, defaulth, scale; - float bordermin, bordermax, outlinemin, outlinemax; - - font() : name(NULL) {} - ~font() { DELETEA(name); } + GLuint tex; + int w, h; }; -#define FONTH (curfont->scale) +#define FONTH (fontsize) #define FONTW (FONTH/2) #define MINRESW 640 #define MINRESH 480 -extern font *curfont; +// number of text lines to fill the whole screen (higher = smaller text) +#define CONSOLETEXTROWS 45 +#define LOADSCREENTEXTROWS 45 +#define UITEXTROWS 36 +#define PARTICLETEXTROWS 15 // NOTE: particles use a different scale + extern Shader *textshader; extern const matrix4x3 *textmatrix; extern float textscale; -extern font *findfont(const char *name); +extern bool init_pangocairo(); +extern void done_pangocairo(); +extern int getcurfontid(); +extern void gettextres(int &w, int &h); +extern void draw_text(textinfo info, float left, float top, int a = 255, bool black = false); +extern void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_color = bvec(255, 255, 255), int cursor = -1, float outline = 0, bvec outline_color = bvec(0, 0, 0), const char *language = NULL); +extern void prepare_text_particle(const char *str, textinfo &info, bvec initial_color = bvec(255, 255, 255), float outline = 0, bvec outline_color = bvec(0, 0, 0), const char *language = NULL); +extern int text_visible(const char *str, float hitx, float hity, int maxwidth, const char *language = NULL); +extern void text_pos(const char *str, int cursor, int &cx, int &cy, int maxwidth, const char *language = NULL); extern void reloadfonts(); -static inline void setfont(font *f) { if(f) curfont = f; } - // texture extern int hwtexsize, hwcubetexsize, hwmaxaniso, maxtexsize, hwtexunits, hwvtexunits; @@ -581,6 +580,7 @@ extern void clearsleep(bool clearoverrides = true); // console extern float conscale; +extern int inputmillis; extern void processkey(int code, bool isdown, int modstate = 0); extern void processtextinput(const char *str, int len); @@ -596,6 +596,7 @@ extern const char *addreleaseaction(char *s); extern tagval *addreleaseaction(ident *id, int numargs); extern void writebinds(stream *f); extern void writecompletions(stream *f); +extern void clearconsoletextures(); // main enum @@ -743,6 +744,7 @@ namespace UI void update(); void render(); void cleanup(); + void cleartext(); } // menus diff --git a/source/engine/main.cpp b/source/engine/main.cpp index 84ae52c00..6ce6b5264 100644 --- a/source/engine/main.cpp +++ b/source/engine/main.cpp @@ -28,6 +28,7 @@ void cleanup() if(screen) SDL_SetWindowFullscreen(screen, 0); #endif SDL_Quit(); + done_pangocairo(); } extern void writeinitcfg(); @@ -67,6 +68,7 @@ void fatal(const char *s, ...) // failure exit #endif } SDL_Quit(); + done_pangocairo(); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Tesseract fatal error", msg, NULL); } } @@ -107,7 +109,7 @@ VARFN(screenh, scr_h, SCR_MINH, -1, SCR_MAXH, initwarning("screen resolution")); void writeinitcfg() { - stream *f = openutf8file("config/init.cfg", "w"); + stream *f = openfile("config/init.cfg", "w"); if(!f) return; f->printf("// automatically written on exit, DO NOT MODIFY\n// modify settings in game\n"); extern int fullscreen; @@ -207,22 +209,32 @@ void renderbackgroundview(int w, int h, const char *caption, Texture *mapshot, c if(caption) { - int tw = text_width(caption); + setfontsize(h * 1.5 / LOADSCREENTEXTROWS); + textinfo i_caption; + pushfont(); + setfont("wide"); + prepare_text(caption, i_caption, 0); + popfont(); + int tw = i_caption.w; float tsz = 0.04f*lw/FONTH, tx = 0.5f*(w - tw*tsz), ty = h - 0.075f*1.5f*lw - FONTH*tsz; - pushhudtranslate(tx, ty, tsz); - draw_text(caption, 0, 0); - pophudmatrix(); + + draw_text(i_caption, tx, ty); + glDeleteTextures(1, &i_caption.tex); } if(mapshot || mapname) { - float infowidth = 14*FONTH, sz = 0.35f*lw, + setfontsize(h * 1 / LOADSCREENTEXTROWS); + textinfo i_info, i_name; + float infowidth = 0.5f*lw, sz = 0.35f*lw, msz = (0.85f*lw - sz)/(infowidth + FONTH), x = 0.5f*w, ly = 0.5f*lw, y = (0.5f*(h*0.5f - ly) + ly) - sz/15, - mx = 0, my = 0, mw = 0, mh = 0; + mx = 0, my = 0, mw = 0;//, mh = 0; if(mapinfo) { - text_boundsf(mapinfo, mw, mh, infowidth); + prepare_text(mapinfo, i_info, infowidth); + mw = i_info.w; + //mh = i_info.h; x -= 0.5f*mw*msz; if(mapshot && mapshot!=notexture) { @@ -239,17 +251,22 @@ void renderbackgroundview(int w, int h, const char *caption, Texture *mapshot, c } if(mapname) { - float tw = text_widthf(mapname), tsz = sz/(8*FONTH), tx = max(0.5f*(mw*msz - tw*tsz), 0.0f); - pushhudtranslate(x+mx+tx, y, tsz); - draw_text(mapname, 0, 0); - pophudmatrix(); + setfontsize(h * 1.5 / LOADSCREENTEXTROWS); + pushfont(); + setfont("wide"); + prepare_text(mapname, i_name, 0); + popfont(); + float /*tw = i_name.w,*/ tsz = sz/(8*FONTH)/*, tx = max(0.5f*(mw*msz - tw*tsz), 0.0f)*/; + + //draw_text(i_name, x+mx+tx, y); + draw_text(i_name, x + sz + (infowidth - i_name.w) / 2, y); + glDeleteTextures(1, &i_name.tex); my = 1.5f*FONTH*tsz; } if(mapinfo) { - pushhudtranslate(x+mx, y+my, msz); - draw_text(mapinfo, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, -1, infowidth); - pophudmatrix(); + draw_text(i_info, x+mx, y+my); + glDeleteTextures(1, &i_info.tex); } } @@ -339,13 +356,15 @@ void renderprogressview(int w, int h, float bar, const char *text) // also use if(text) { - int tw = text_width(text); + setfontsize(h * 0.8 / LOADSCREENTEXTROWS); + textinfo i_text; + prepare_text(text, i_text, 0, bvec(255, 255, 255)); + int tw = i_text.w; float tsz = bh*0.6f/FONTH; if(tw*tsz > mw) tsz = mw/tw; - pushhudtranslate(bx+sw, by + (bh - FONTH*tsz)/2, tsz); - draw_text(text, 0, 0); - pophudmatrix(); + draw_text(i_text, bx+sw, by+ (bh - FONTH*tsz)/2); + glDeleteTextures(1, &i_text.tex); } glDisable(GL_BLEND); @@ -676,6 +695,7 @@ void resetgl() !reloadtexture("data/interface/loading_frame.png") || !reloadtexture("data/interface/loading_bar.png")) fatal("Failed to reload core texture"); + clearconsoletextures(); reloadfonts(); inbetweenframes = true; renderbackground("Initializing"); @@ -821,9 +841,7 @@ void checkinput() case SDL_TEXTINPUT: if(textinputmask && int(event.text.timestamp-textinputtime) >= textinputfilter) { - uchar buf[SDL_TEXTINPUTEVENT_TEXT_SIZE+1]; - size_t len = decodeutf8(buf, sizeof(buf)-1, (const uchar *)event.text.text, strlen(event.text.text)); - if(len > 0) { buf[len] = '\0'; processtextinput((const char *)buf, len); } + processtextinput(event.text.text, strlen(event.text.text)); } break; @@ -1198,6 +1216,9 @@ int main(int argc, char **argv) notexture = textureload("data/texture/world/base/notexture.png"); if(!notexture) fatal("Could not find core textures"); + logoutf("init: pangocairo"); + if(!init_pangocairo()) fatal("Could not initialize pangocairo"); + logoutf("init: console"); if(!execfile("config/stdlib.cfg", false)) fatal("Cannot find required configuration files (\"/config/stdlib.cfg\")!\nYou might not be running from the main folder."); // this is the first file we load. if(!execfile("config/font.cfg", false)) fatal("Cannot find font definitions"); diff --git a/source/engine/movie.cpp b/source/engine/movie.cpp index 78b28e018..3d552eed8 100644 --- a/source/engine/movie.cpp +++ b/source/engine/movie.cpp @@ -1128,7 +1128,6 @@ namespace recorder gettextres(w, h); hudmatrix.ortho(0, w, h, 0, -1, 1); - hudmatrix.scale(1/3.0f, 1/3.0f, 1); resethudmatrix(); glEnable(GL_BLEND); @@ -1139,7 +1138,8 @@ namespace recorder else if(totalsize >= 1e6) { totalsize /= 1e6; unit = "MB"; } else totalsize /= 1e3; - draw_textf("recorded %.1f%s %d%%", w*3-10*FONTH, h*3-FONTH-FONTH*3/2, totalsize, unit, int(calcquality()*100)); + setfontsize(hudh * conscale / CONSOLETEXTROWS); + draw_textf("recorded %.1f%s %d%%", w-10*FONTH, FONTH-FONTH/2, totalsize, unit, int(calcquality()*100)); glDisable(GL_BLEND); } diff --git a/source/engine/rendergl.cpp b/source/engine/rendergl.cpp index 62634cb02..72124f3c0 100644 --- a/source/engine/rendergl.cpp +++ b/source/engine/rendergl.cpp @@ -2807,7 +2807,7 @@ VARP(showfps, 0, 0, 1); VARP(showfpsrange, 0, 0, 1); VAR(statrate, 1, 200, 1000); -FVARP(conscale, 1e-3f, 0.45f, 1e3f); +FVARP(conscale, 0.5f, 1.f, 2.f); void resethudshader() { @@ -2827,7 +2827,8 @@ void gl_drawhud() resethudshader(); pushfont(); - setfont("default.ol"); + setfont("default"); + setfontsize(hudh * conscale / CONSOLETEXTROWS); debuglights(); @@ -2835,12 +2836,12 @@ void gl_drawhud() debugparticles(); - float conw = w/conscale, conh = h/conscale, abovehud = conh - FONTH; + float conw = w, conh = h, abovehud = conh - FONTH; if(showhud && !mainmenu) { if(showstats) { - pushhudscale(conscale); + setfontsize(hudh * conscale / CONSOLETEXTROWS); int roffset = 0; if(showfps) @@ -2879,8 +2880,6 @@ void gl_drawhud() roffset += FONTH; } } - - pophudmatrix(); } rendertexturepanel(w, h); @@ -2888,13 +2887,12 @@ void gl_drawhud() abovehud = min(abovehud, conh*UI::abovehud()); - pushhudscale(conscale); + setfontsize(hudh * conscale / CONSOLETEXTROWS); abovehud -= rendercommand(FONTH/2, abovehud - FONTH/2, conw-FONTH); if(showhud && !(UI::uivisible("main") || UI::uivisible("fullconsole"))) { renderconsole(conw, conh, abovehud - FONTH/2); } - pophudmatrix(); drawcrosshair(w, h); diff --git a/source/engine/renderparticles.cpp b/source/engine/renderparticles.cpp index 7571831ba..00d0698a4 100644 --- a/source/engine/renderparticles.cpp +++ b/source/engine/renderparticles.cpp @@ -134,7 +134,12 @@ struct particle float size; union { - const char *text; + struct + { + const char *text; + const char *font; + const char *language; + }; float val; physent *owner; struct @@ -479,6 +484,7 @@ static meterrenderer meters(PT_METER), metervs(PT_METERVS); struct textrenderer : listrenderer { + textrenderer(int type = 0) : listrenderer(type|PT_TEXT|PT_LERP|PT_SHADER|PT_NOLAYER) {} @@ -486,26 +492,37 @@ struct textrenderer : listrenderer void startrender() { textshader = particletextshader; - - pushfont(); - setfont("wide.ol"); } void endrender() { textshader = NULL; - - popfont(); } void killpart(listparticle *p) { - if(p->text && p->flags&1) delete[] p->text; + if(p->flags&1) + { + if(p->text) delete[] p->text; + if(p->font) delete[] p->font; + if(p->language) delete[] p->language; + } } void renderpart(listparticle *p, const vec &o, const vec &d, int blend, int ts) { - float scale = p->size/80.0f, xoff = -text_width(p->text)/2, yoff = 0; + pushfont(); + setfont(p->font); + setfontsize(hudh / PARTICLETEXTROWS); + + textinfo info; + prepare_text_particle(p->text, info, p->color, 1, bvec(0, 0, 0), p->language); + if(!info.tex) + { + popfont(); + return; + } + float scale = p->size/80.0f, xoff = -info.w/2, yoff = -info.h/2; if((type&0xFF)==PT_TEXTUP) { xoff += detrnd((size_t)p, 100)-50; yoff -= detrnd((size_t)p, 101); } matrix4x3 m(camright, vec(camup).neg(), vec(camdir).neg(), o); @@ -513,7 +530,8 @@ struct textrenderer : listrenderer m.translate(xoff, yoff, 50); textmatrix = &m; - draw_text(p->text, 0, 0, p->color.r, p->color.g, p->color.b, blend); + draw_text(info, 0, 0, blend); + popfont(); textmatrix = NULL; } }; @@ -1096,20 +1114,24 @@ void particle_trail(int type, int fade, const vec &s, const vec &e, int color, f VARP(particletext, 0, 1, 1); VARP(maxparticletextdistance, 0, 64, 10000); -void particle_text(const vec &s, const char *t, int type, int fade, int color, float size, int gravity) +void particle_text(const vec &s, const char *t, int type, int fade, int color, float size, int gravity, const char *font, const char *language) { if(!canaddparticles()) return; if(!particletext || camera1->o.dist(s) > maxparticletextdistance) return; particle *p = newparticle(s, vec(0, 0, 1), fade, type, color, size, gravity); p->text = t; + p->font = font; + p->language = language; } -void particle_textcopy(const vec &s, const char *t, int type, int fade, int color, float size, int gravity) +void particle_textcopy(const vec &s, const char *t, int type, int fade, int color, float size, int gravity, const char *font, const char *language) { if(!canaddparticles()) return; if(!particletext || camera1->o.dist(s) > maxparticletextdistance) return; particle *p = newparticle(s, vec(0, 0, 1), fade, type, color, size, gravity); p->text = newstring(t); + p->font = newstring(font); + p->language = newstring(language ? language : ""); p->flags = 1; } @@ -1134,6 +1156,20 @@ void particle_hud_mark(const vec &s, int ix, int iy, int type, int fade, int col p->flags |= ix | (iy<<2); } +void particle_hud_text(const vec &s, const char *t, int type, int fade, int color, float size, const char *font, const char *language) +{ + if(!canaddparticles()) return; + vec o; + if(camera1->o.dist(s) <= hudmarkdist && raycubelos(s, camera1->o, o)) return; + o = s; + vec camera = camera1->o; + o.sub(camera).normalize(); + particle *p = newparticle(s, vec(0, 0, 1), fade, type, color, size * camera1->o.dist(s) / 80, 0); + p->text = t; + p->font = font; + p->language = language; +} + void particle_meter(const vec &s, float val, int type, int fade, int color, int color2, float size) { if(!canaddparticles()) return; diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index 56d81215e..38e15f7a2 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -1,434 +1,627 @@ +#include "unicode.h" #include "engine.h" -static hashnameset fonts; -static font *fontdef = NULL; -static int fontdeftex = 0; +#include +#include +#include -font *curfont = NULL; -int curfonttex = 0; +static int fontid = 0; // used by UI for change detection +float fontsize = 0; // pixel height of the current font +const matrix4x3 *textmatrix = NULL; // used for text particles +Shader *textshader = NULL; // used for text particles -void newfont(char *name, char *tex, int *defaultw, int *defaulth, int *scale) -{ - font *f = &fonts[name]; - if(!f->name) f->name = newstring(name); - f->texs.shrink(0); - f->texs.add(textureload(tex)); - f->chars.shrink(0); - f->charoffset = '!'; - f->defaultw = *defaultw; - f->defaulth = *defaulth; - f->scale = *scale > 0 ? *scale : f->defaulth; - f->bordermin = 0.49f; - f->bordermax = 0.5f; - f->outlinemin = -1; - f->outlinemax = 0; - - fontdef = f; - fontdeftex = 0; -} +static cairo_font_options_t *options = NULL; // global font options +static cairo_surface_t *dummy_surface = NULL; // used to measure text -void fontborder(float *bordermin, float *bordermax) +static cairo_antialias_t antialias_ = CAIRO_ANTIALIAS_GRAY; +static cairo_hint_style_t hintstyle_= CAIRO_HINT_STYLE_DEFAULT; +VARFP(fontantialias, 0, 1, 1, +{ + antialias_ = fontantialias ? CAIRO_ANTIALIAS_GRAY : CAIRO_ANTIALIAS_NONE; + if(options) cairo_font_options_set_antialias(options, antialias_); + clearconsoletextures(); + reloadfonts(); +}); +VARFP(fonthinting, -1, -1, 3, { - if(!fontdef) return; + switch(fonthinting) + { + case 0: hintstyle_ = CAIRO_HINT_STYLE_NONE ; break; + case 1: hintstyle_ = CAIRO_HINT_STYLE_SLIGHT ; break; + case 2: hintstyle_ = CAIRO_HINT_STYLE_MEDIUM ; break; + case 3: hintstyle_ = CAIRO_HINT_STYLE_FULL ; break; + default: hintstyle_ = CAIRO_HINT_STYLE_DEFAULT; + } + if(options) cairo_font_options_set_hint_style(options, hintstyle_); + clearconsoletextures(); + reloadfonts(); +}) - fontdef->bordermin = *bordermin; - fontdef->bordermax = max(*bordermax, *bordermin+0.01f); -} +VARFP(cursorblink, 0, 750, 2000, { cursorblink = cursorblink ? max(250, cursorblink) : 0; }); +CVARP(cursorcolor, 0xFFFFFF); -void fontoutline(float *outlinemin, float *outlinemax) +// a cache for rendered text particles +struct partinfo { - if(!fontdef) return; - - fontdef->outlinemin = min(*outlinemin, *outlinemax-0.01f); - fontdef->outlinemax = *outlinemax; + textinfo ti; + partinfo() { ti.tex = 0; } +}; +static hashtable particle_cache; +static vector particle_queue; +static void clear_text_particles() +{ + enumerate(particle_cache, partinfo, info, { if(info.ti.tex) glDeleteTextures(1, &info.ti.tex); }); + particle_cache.clear(); + particle_queue.setsize(0); } -void fontoffset(char *c) +// register a TTF file +// NOTE: on Windows and MacOs, this assumes that pango was compiled with fontconfig support, +// and that the `PANGOCAIRO_BACKEND` env var is set to `fc` or `fontconfig` +void addfontfile(const char *filename) { - if(!fontdef) return; - - fontdef->charoffset = c[0]; + const char *found = findfile(filename, "rb"); + if(!found || !found[0]) return; + FcConfigAppFontAddFile(FcConfigGetCurrent(), (const unsigned char *)found); } +COMMANDN(registerfont, addfontfile, "s"); -void fontscale(int *scale) +struct font { - if(!fontdef) return; - - fontdef->scale = *scale > 0 ? *scale : fontdef->defaulth; -} + char *name; + int id; + string features; // OpenType features + float letter_spacing; + PangoFontDescription *desc; + + font() : name(NULL), letter_spacing(0), desc(NULL) { features[0] = '\0'; }; + ~font() + { + DELETEA(name); + if(desc) pango_font_description_free(desc); + } +}; +static font *curfont = NULL, *lastfont = NULL; +static hashnameset fonts; +static vector fontstack; -void fonttex(char *s) +void newfont(const char *name, const char *family) { - if(!fontdef) return; + font *f = &fonts[name]; + if(!f->name) f->name = newstring(name); + if(!f->desc) f->desc = pango_font_description_from_string(family); + f->id = fontid++; - Texture *t = textureload(s); - loopv(fontdef->texs) if(fontdef->texs[i] == t) { fontdeftex = i; return; } - fontdeftex = fontdef->texs.length(); - fontdef->texs.add(t); + lastfont = f; } +COMMANDN(font, newfont, "ss"); -void fontchar(float *x, float *y, float *w, float *h, float *offsetx, float *offsety, float *advance) +void fontweight(int *weight) { - if(!fontdef) return; - - font::charinfo &c = fontdef->chars.add(); - c.x = *x; - c.y = *y; - c.w = *w ? *w : fontdef->defaultw; - c.h = *h ? *h : fontdef->defaulth; - c.offsetx = *offsetx; - c.offsety = *offsety; - c.advance = *advance ? *advance : c.offsetx + c.w; - c.tex = fontdeftex; + if(!lastfont || !lastfont->desc) return; + PangoWeight w; + switch(*weight) + { + case -2: w = PANGO_WEIGHT_ULTRALIGHT; break; + case -1: w = PANGO_WEIGHT_LIGHT; break; + case 1: w = PANGO_WEIGHT_MEDIUM; break; + case 2: w = PANGO_WEIGHT_SEMIBOLD; break; + case 3: w = PANGO_WEIGHT_BOLD; break; + case 4: w = PANGO_WEIGHT_ULTRABOLD; break; + case 5: w = PANGO_WEIGHT_HEAVY; break; + default: w = *weight < 0 ? PANGO_WEIGHT_THIN : (*weight > 0 ? PANGO_WEIGHT_ULTRAHEAVY : PANGO_WEIGHT_NORMAL); + } + pango_font_description_set_weight(lastfont->desc, w); } +COMMAND(fontweight, "i"); -void fontskip(int *n) +void fontstretch(int *stretch) { - if(!fontdef) return; - loopi(max(*n, 1)) + if(!lastfont || !lastfont->desc) return; + PangoStretch s; + switch(*stretch) { - font::charinfo &c = fontdef->chars.add(); - c.x = c.y = c.w = c.h = c.offsetx = c.offsety = c.advance = 0; - c.tex = 0; + case -3: s = PANGO_STRETCH_EXTRA_CONDENSED; break; + case -2: s = PANGO_STRETCH_CONDENSED; break; + case -1: s = PANGO_STRETCH_SEMI_CONDENSED; break; + case 0: s = PANGO_STRETCH_NORMAL; break; + case 1: s = PANGO_STRETCH_SEMI_EXPANDED; break; + case 2: s = PANGO_STRETCH_EXPANDED; break; + case 3: s = PANGO_STRETCH_EXTRA_EXPANDED; break; + default: s = *stretch < 0 ? PANGO_STRETCH_ULTRA_CONDENSED : PANGO_STRETCH_ULTRA_EXPANDED; } + pango_font_description_set_stretch(lastfont->desc, s); } +COMMAND(fontstretch, "i"); -COMMANDN(font, newfont, "ssiii"); -COMMAND(fontborder, "ff"); -COMMAND(fontoutline, "ff"); -COMMAND(fontoffset, "s"); -COMMAND(fontscale, "i"); -COMMAND(fonttex, "s"); -COMMAND(fontchar, "fffffff"); -COMMAND(fontskip, "i"); +// 0 = normal, 1 = oblique, 2 = italic +void fontstyle(int *style) +{ + if(!lastfont || !lastfont->desc) return; + PangoStyle s; + switch(*style) + { + case 1: s = PANGO_STYLE_OBLIQUE; break; + case 2: s = PANGO_STYLE_ITALIC; break; + default: s = PANGO_STYLE_NORMAL; + } + pango_font_description_set_style(lastfont->desc, s); +} +COMMAND(fontstyle, "i"); -void fontalias(const char *dst, const char *src) +void fontsmallcaps(int *val) { - font *s = fonts.access(src); - if(!s) return; - font *d = &fonts[dst]; - if(!d->name) d->name = newstring(dst); - d->texs = s->texs; - d->chars = s->chars; - d->charoffset = s->charoffset; - d->defaultw = s->defaultw; - d->defaulth = s->defaulth; - d->scale = s->scale; - d->bordermin = s->bordermin; - d->bordermax = s->bordermax; - d->outlinemin = s->outlinemin; - d->outlinemax = s->outlinemax; - - fontdef = d; - fontdeftex = d->texs.length()-1; + if(!lastfont || !lastfont->desc) return; + pango_font_description_set_variant(lastfont->desc, *val ? PANGO_VARIANT_SMALL_CAPS : PANGO_VARIANT_NORMAL); } +COMMAND(fontsmallcaps, "i"); + +void fontletterspacing(float *val) { if(lastfont) lastfont->letter_spacing = *val; } +COMMAND(fontletterspacing, "f"); -COMMAND(fontalias, "ss"); +void fontfeatures(char *features) { if(lastfont) copystring(lastfont->features, features, MAXSTRLEN); } +COMMAND(fontfeatures, "s"); -font *findfont(const char *name) +void fontvariations(char *variations) { - return fonts.access(name); + if(!lastfont || !lastfont->desc) return; + pango_font_description_set_variations(lastfont->desc, variations); } +COMMAND(fontvariations, "s"); -bool setfont(const char *name) +static inline bool setfont(font *f) { - font *f = fonts.access(name); if(!f) return false; curfont = f; return true; } - -static vector fontstack; - -void pushfont() +bool setfont(const char *name) { - fontstack.add(curfont); + font *f = fonts.access(name); + return setfont(f); } - +void pushfont() { fontstack.add(curfont); } bool popfont() { if(fontstack.empty()) return false; curfont = fontstack.pop(); return true; } +int getcurfontid() { return curfont->id; } -void gettextres(int &w, int &h) +bool init_pangocairo() { - if(w < MINRESW || h < MINRESH) - { - if(MINRESW > w*MINRESH/h) - { - h = h*MINRESW/w; - w = MINRESW; - } - else - { - w = w*MINRESH/h; - h = MINRESH; - } - } + options = cairo_font_options_create(); + if(!options) return false; + cairo_font_options_set_antialias(options, antialias_); + cairo_font_options_set_hint_style(options, hintstyle_); + cairo_font_options_set_hint_metrics(options, CAIRO_HINT_METRICS_ON); + cairo_font_options_set_color_mode(options, CAIRO_COLOR_MODE_COLOR); // cairo 1.18 + + dummy_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0); + if(!dummy_surface) return false; + + defformatstring(msg, "Text rendering: Pango %s, Cairo %s", pango_version_string(), cairo_version_string()); + conoutf(CON_INIT, msg); + return true; } -float text_widthf(const char *str) +void done_pangocairo() { - float width, height; - text_boundsf(str, width, height); - return width; + fonts.clear(); + if(options) cairo_font_options_destroy(options); + if(dummy_surface) cairo_surface_destroy(dummy_surface); } -#define FONTTAB (4*FONTW) -#define TEXTTAB(x) ((int((x)/FONTTAB)+1.0f)*FONTTAB) - -void tabify(const char *str, int *numtabs) +static inline bvec get_text_color(char c, bvec def) { - int tw = max(*numtabs, 0)*FONTTAB-1, tabs = 0; - for(float w = text_widthf(str); w <= tw; w = TEXTTAB(w)) ++tabs; - int len = strlen(str); - char *tstr = newstring(len + tabs); - memcpy(tstr, str, len); - memset(&tstr[len], '\t', tabs); - tstr[len+tabs] = '\0'; - stringret(tstr); + switch(c) + { + case '0': return bvec( 64, 255, 128); // green: player talk + case '1': return bvec( 96, 160, 255); // blue: "echo" command + case '2': return bvec(255, 192, 64); // yellow: gameplay messages + case '3': return bvec(255, 64, 64); // red: important errors + case '4': return bvec(128, 128, 128); // gray + case '5': return bvec(192, 64, 192); // magenta + case '6': return bvec(255, 128, 0); // orange + case '7': return bvec(255, 255, 255); // white + case '8': return bvec( 0, 255, 255); // cyan + case '9': return bvec(255, 192, 203); // pink + } + return def; // provided color: everything else } - -COMMAND(tabify, "si"); - -void draw_textf(const char *fstr, float left, float top, ...) +//stack[sp] is current color index +static inline bvec text_color(char c, char *stack, int size, int &sp, bvec color) { - defvformatstring(str, top, fstr); - draw_text(str, left, top); + if(c=='s') // save color + { + c = stack[sp]; + if(sp 0) --sp; c = stack[sp]; } // restore color + else stack[sp] = c; + return get_text_color(c, color); + } + return color; } -const matrix4x3 *textmatrix = NULL; -float textscale = 1; - -static float draw_char(Texture *&tex, int c, float x, float y, float scale) +#define MARKUP_CASE(x, X, pango_attr_func, PANGO_ATTR, name) \ + case x: \ + if(begin_##name < 0) begin_##name = j; \ + ++n_##name; \ + break; \ + case X: \ + if(begin_##name >= 0) \ + { \ + if(n_##name >= 0) --n_##name; \ + if(n_##name) break; \ + m = pango_attr_func(PANGO_ATTR); \ + m->start_index = begin_##name; \ + m->end_index = j; \ + pango_attr_list_insert(list, m); \ + begin_##name = -1; \ + } \ + break; + +// adds a string to the layout parsing basic markup (\f codes) +// NOTE: `markup` is the original string with \f codes; `text` is the stripped version without \f codes +static inline void add_text_to_layout(const char *markup, int len, PangoLayout *layout, bvec initial_color, int *map_markup_to_text, int *map_text_to_markup, const char *language) { - font::charinfo &info = curfont->chars[c-curfont->charoffset]; - if(tex != curfont->texs[info.tex]) - { - xtraverts += gle::end(); - tex = curfont->texs[info.tex]; - setusedtexture(tex); - } + char *text = newstring(len); - x *= textscale; - y *= textscale; - scale *= textscale; + bvec tcolor = initial_color; + char colorstack[10]; + colorstack[0] = 'c'; + int cpos = 0; - float x1 = x + scale*info.offsetx, - y1 = y + scale*info.offsety, - x2 = x + scale*(info.offsetx + info.w), - y2 = y + scale*(info.offsety + info.h), - tx1 = info.x / tex->xs, - ty1 = info.y / tex->ys, - tx2 = (info.x + info.w) / tex->xs, - ty2 = (info.y + info.h) / tex->ys; + PangoAttrList *list = pango_attr_list_new(); + PangoAttribute *attr; // colors and global features + PangoAttribute *m; // markup - if(textmatrix) + // OpenType features + if(curfont->features[0]) { - gle::attrib(textmatrix->transform(vec2(x1, y1))); gle::attribf(tx1, ty1); - gle::attrib(textmatrix->transform(vec2(x2, y1))); gle::attribf(tx2, ty1); - gle::attrib(textmatrix->transform(vec2(x2, y2))); gle::attribf(tx2, ty2); - gle::attrib(textmatrix->transform(vec2(x1, y2))); gle::attribf(tx1, ty2); + attr = pango_attr_font_features_new(curfont->features); // pango 1.38 + pango_attr_list_insert(list, attr); } - else + + // letter spacing + if(curfont->letter_spacing != 0) { - gle::attribf(x1, y1); gle::attribf(tx1, ty1); - gle::attribf(x2, y1); gle::attribf(tx2, ty1); - gle::attribf(x2, y2); gle::attribf(tx2, ty2); - gle::attribf(x1, y2); gle::attribf(tx1, ty2); + attr = pango_attr_letter_spacing_new(curfont->letter_spacing * PANGO_SCALE); // pango 1.6 + pango_attr_list_insert(list, attr); } - return scale*info.advance; -} - -VARP(textbright, 0, 85, 100); - -//stack[sp] is current color index -static void text_color(char c, char *stack, int size, int &sp, bvec color, int a) -{ - if(c=='s') // save color + // language + if(language) { - c = stack[sp]; - if(spstart_index = 0; + + int begin_bold = -1, begin_italic = -1, begin_underline = -1, begin_strikethrough = -1, begin_overline = -1; + int n_bold = 0, n_italic = 0, n_underline = 0, n_strikethrough = 0, n_overline = 0; + + // parse markup + int i = 0, j = 0; + for(; i < len; ++i) { - xtraverts += gle::end(); - if(c=='r') { if(sp > 0) --sp; c = stack[sp]; } // restore color - else stack[sp] = c; - switch(c) + if(markup[i] == '\f' && i < (len - 1)) { - case '0': color = bvec( 64, 255, 128); break; // green: player talk - case '1': color = bvec( 96, 160, 255); break; // blue: "echo" command - case '2': color = bvec(255, 192, 64); break; // yellow: gameplay messages - case '3': color = bvec(255, 64, 64); break; // red: important errors - case '4': color = bvec(128, 128, 128); break; // gray - case '5': color = bvec(192, 64, 192); break; // magenta - case '6': color = bvec(255, 128, 0); break; // orange - case '7': color = bvec(255, 255, 255); break; // white - case '8': color = bvec( 0, 255, 255); break; // cyan - case '9': color = bvec(255, 192, 203); break; // pink - default: gle::color(color, a); return; // provided color: everything else + switch(markup[i+1]) + { + MARKUP_CASE('b', 'B', pango_attr_weight_new , PANGO_WEIGHT_BOLD , bold); + MARKUP_CASE('i', 'I', pango_attr_style_new , PANGO_STYLE_ITALIC , italic); + MARKUP_CASE('u', 'U', pango_attr_underline_new , PANGO_UNDERLINE_SINGLE, underline); + MARKUP_CASE('t', 'T', pango_attr_strikethrough_new, TRUE , strikethrough); + MARKUP_CASE('o', 'O', pango_attr_overline_new , PANGO_OVERLINE_SINGLE , overline); // pango 1.46 + default: + { + tcolor = text_color(markup[i+1], colorstack, sizeof(colorstack), cpos, initial_color); + if(attr) + { + attr->end_index = j + 1; + pango_attr_list_insert(list, attr); + attr = NULL; + } + } + } + if(map_markup_to_text) map_markup_to_text[i] = j; + ++i; + continue; + } + if(!attr) + { + attr = pango_attr_foreground_new(tcolor.r * 257, tcolor.g * 257, tcolor.b * 257); + if(attr) attr->start_index = j; } - if(textbright != 100) color.scale(textbright, 100); - gle::color(color, a); + if(map_markup_to_text) map_markup_to_text[i] = j; + if(map_text_to_markup) map_text_to_markup[j] = i; + text[j++] = markup[i]; } + text[j] = '\0'; + if(map_markup_to_text) map_markup_to_text[i] = j; + + if(attr) + { + attr->end_index = j; + pango_attr_list_insert(list, attr); + } + if(begin_bold >= 0) + { + m = pango_attr_weight_new(PANGO_WEIGHT_BOLD); + m->start_index = begin_bold; m->end_index = j; + pango_attr_list_insert(list, m); + } + if(begin_italic >= 0) + { + m = pango_attr_style_new(PANGO_STYLE_ITALIC); + m->start_index = begin_italic; m->end_index = j; + pango_attr_list_insert(list, m); + } + if(begin_underline >= 0) + { + m = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE); + m->start_index = begin_underline; m->end_index = j; + pango_attr_list_insert(list, m); + } + if(begin_strikethrough >= 0) + { + m = pango_attr_strikethrough_new(TRUE); + m->start_index = begin_strikethrough; m->end_index = j; + pango_attr_list_insert(list, m); + } + if(begin_overline >= 0) + { + m = pango_attr_overline_new(PANGO_OVERLINE_SINGLE); + m->start_index = begin_overline; m->end_index = j; + pango_attr_list_insert(list, m); + } + + pango_layout_set_text(layout, text, -1); + delete[] text; + pango_layout_set_attributes(layout, list); + pango_attr_list_unref(list); } +#undef MARKUP_CASE + +static inline PangoLayout *measure_text_internal(const char *str, int len, int maxwidth, bvec initial_color, int &width, int &height, int *map_markup_to_text, int *map_text_to_markup, const char *language) +{ + // create cairo context + cairo_t *cr = cairo_create(dummy_surface); + cairo_set_font_options(cr, options); + + // create layout + PangoLayout *layout = pango_cairo_create_layout(cr); + if(!layout) + { + width = height = 0; + cairo_destroy(cr); + return NULL; + } + + // set font family and size + pango_font_description_set_absolute_size(curfont->desc, fontsize * PANGO_SCALE); // pango 1.8 + pango_layout_set_font_description(layout, curfont->desc); -#define TEXTSKELETON \ - float y = 0, x = 0, scale = curfont->scale/float(curfont->defaulth);\ - int i;\ - for(i = 0; str[i]; i++)\ - {\ - TEXTINDEX(i)\ - int c = uchar(str[i]);\ - if(c=='\t') { x = TEXTTAB(x); TEXTWHITE(i) }\ - else if(c==' ') { x += scale*curfont->defaultw; TEXTWHITE(i) }\ - else if(c=='\n') { TEXTLINE(i) x = 0; y += FONTH; }\ - else if(c=='\f') { if(str[i+1]) { i++; TEXTCOLOR(i) }}\ - else if(curfont->chars.inrange(c-curfont->charoffset))\ - {\ - float cw = scale*curfont->chars[c-curfont->charoffset].advance;\ - if(cw <= 0) continue;\ - if(maxwidth >= 0)\ - {\ - int j = i;\ - float w = cw;\ - for(; str[i+1]; i++)\ - {\ - int c = uchar(str[i+1]);\ - if(c=='\f') { if(str[i+2]) i++; continue; }\ - if(!curfont->chars.inrange(c-curfont->charoffset)) break;\ - float cw = scale*curfont->chars[c-curfont->charoffset].advance;\ - if(cw <= 0 || w + cw > maxwidth) break;\ - w += cw;\ - }\ - if(x + w > maxwidth && x > 0) { (void)j; TEXTLINE(j-1) x = 0; y += FONTH; }\ - TEXTWORD\ - }\ - else { TEXTCHAR(i) }\ - }\ + // set maximum length for line wrapping + if(maxwidth > 0) + { + pango_layout_set_width(layout, maxwidth * PANGO_SCALE); + pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); } -//all the chars are guaranteed to be either drawable or color commands -#define TEXTWORDSKELETON \ - for(; j <= i; j++)\ - {\ - TEXTINDEX(j)\ - int c = uchar(str[j]);\ - if(c=='\f') { if(str[j+1]) { j++; TEXTCOLOR(j) }}\ - else { float cw = scale*curfont->chars[c-curfont->charoffset].advance; TEXTCHAR(j) }\ - } + add_text_to_layout(str, len, layout, initial_color, map_markup_to_text, map_text_to_markup, language); -#define TEXTEND(cursor) if(cursor >= i) { do { TEXTINDEX(cursor); } while(0); } + // get pixel size + pango_layout_get_pixel_size(layout, &width, &height); -int text_visible(const char *str, float hitx, float hity, int maxwidth) + cairo_destroy(cr); + return layout; +} +void measure_text(const char *str, int maxwidth, int &width, int &height, const char *language) { - #define TEXTINDEX(idx) - #define TEXTWHITE(idx) if(y+FONTH > hity && x >= hitx) return idx; - #define TEXTLINE(idx) if(y+FONTH > hity) return idx; - #define TEXTCOLOR(idx) - #define TEXTCHAR(idx) x += cw; TEXTWHITE(idx) - #define TEXTWORD TEXTWORDSKELETON - TEXTSKELETON - #undef TEXTINDEX - #undef TEXTWHITE - #undef TEXTLINE - #undef TEXTCOLOR - #undef TEXTCHAR - #undef TEXTWORD - return i; + PangoLayout *layout = measure_text_internal(str, strlen(str), maxwidth, bvec(0, 0, 0), width, height, NULL, NULL, language); + if(layout) g_object_unref(layout); } -//inverse of text_visible -void text_posf(const char *str, int cursor, float &cx, float &cy, int maxwidth) +void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_color, int cursor, float outline, bvec outline_color, const char *language) { - #define TEXTINDEX(idx) if(idx == cursor) { cx = x; cy = y; break; } - #define TEXTWHITE(idx) - #define TEXTLINE(idx) - #define TEXTCOLOR(idx) - #define TEXTCHAR(idx) x += cw; - #define TEXTWORD TEXTWORDSKELETON if(i >= cursor) break; - cx = cy = 0; - TEXTSKELETON - TEXTEND(cursor) - #undef TEXTINDEX - #undef TEXTWHITE - #undef TEXTLINE - #undef TEXTCOLOR - #undef TEXTCHAR - #undef TEXTWORD -} + // get dimensions and pango layout + int width, height; + const int len = strlen(str); + int map_markup_to_text[len+1]; + PangoLayout *layout = measure_text_internal(str, len, maxwidth, initial_color, width, height, cursor >= 0 ? map_markup_to_text : NULL, NULL, language); + if(!layout) { info = {0, 0, 0}; return; } + if(!width || !height) { g_object_unref(layout); info = {0, 0, 0}; return; } + + // create surface and cairo context + if(cursor >= 0) width += max(4.f, fontsize); // make space for the cursor + cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + cairo_t *cr = cairo_create(surface); + cairo_set_font_options(cr, options); + + // draw text onto the surface + if(outline) + { + cairo_set_source_rgba(cr, outline_color.r / 255.f, outline_color.g / 255.f, outline_color.b / 255.f, 1.0); + cairo_set_line_width(cr, 2 * outline); + pango_cairo_layout_path(cr, layout); + cairo_stroke(cr); + } + cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0); + pango_cairo_show_layout(cr, layout); + + // add the cursor + if(cursor >= 0 && ((totalmillis - inputmillis <= cursorblink) || !cursorblink || ((totalmillis - inputmillis) % (2*cursorblink)) <= cursorblink)) + { + cursor = min((int)strlen(pango_layout_get_text(layout)), map_markup_to_text[cursor]); + PangoRectangle cursor_rect; + pango_layout_get_cursor_pos(layout, cursor, &cursor_rect, NULL); + + const float curw = max(1.f, fontsize / 16); -void text_boundsf(const char *str, float &width, float &height, int maxwidth) + cairo_rectangle(cr, cursor_rect.x / PANGO_SCALE, cursor_rect.y / PANGO_SCALE, curw, cursor_rect.height / PANGO_SCALE); + cairo_set_source_rgba(cr, cursorcolor.r / 255.f, cursorcolor.g / 255.f, cursorcolor.b / 255.f, 1.0); + cairo_fill(cr); + } + + // create and upload texture + glGenTextures(1, &info.tex); + if(!info.tex) + { + info = {0, 0, 0}; + g_object_unref(layout); + cairo_destroy(cr); + cairo_surface_destroy(surface); + return; + } + glBindTexture(GL_TEXTURE_RECTANGLE, info.tex); + glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_COMPRESSED_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, cairo_image_surface_get_data(surface)); + info.w = width; + info.h = height; + + // clean up + g_object_unref(layout); + cairo_destroy(cr); + cairo_surface_destroy(surface); +} +void prepare_text_particle(const char *str, textinfo &info, bvec initial_color, float outline, bvec outline_color, const char *language) { - #define TEXTINDEX(idx) - #define TEXTWHITE(idx) - #define TEXTLINE(idx) if(x > width) width = x; - #define TEXTCOLOR(idx) - #define TEXTCHAR(idx) x += cw; - #define TEXTWORD x += w; - width = 0; - TEXTSKELETON - height = y + FONTH; - TEXTLINE(_) - #undef TEXTINDEX - #undef TEXTWHITE - #undef TEXTLINE - #undef TEXTCOLOR - #undef TEXTCHAR - #undef TEXTWORD + const int c = initial_color.tohexcolor(), d = outline_color.tohexcolor(); + const char *l = language ? language : ""; + uint key = crc32(0, (const Bytef *)str, strlen(str)) + curfont->id; + key = crc32(key, (const Bytef *)(&outline), sizeof(float)); + key = crc32(key, (const Bytef *)(&c), sizeof(int)); + key = crc32(key, (const Bytef *)(&d), sizeof(int)); + key = crc32(key, (const Bytef *)l, strlen(l)); + partinfo &p = particle_cache[key]; + if(p.ti.tex) + { + info = p.ti; + return; + } + prepare_text(str, p.ti, 0, initial_color, -1, outline, outline_color, language); + if(!p.ti.tex) { info = {0, 0, 0}; return; } + if(particle_queue.length() >= 256) + { + const uint oldkey = particle_queue[0]; + partinfo &oldp = particle_cache[oldkey]; + if(oldp.ti.tex) glDeleteTextures(1, &oldp.ti.tex); + particle_cache.remove(oldkey); + particle_queue.remove(0); + } + particle_queue.add(key); + info = p.ti; } -Shader *textshader = NULL; - -void draw_text(const char *str, float left, float top, int r, int g, int b, int a, int cursor, int maxwidth) +// draw text to the screen +void draw_text(textinfo info, float left, float top, int a, bool black) { - #define TEXTINDEX(idx) if(idx == cursor) { cx = x; cy = y; } - #define TEXTWHITE(idx) - #define TEXTLINE(idx) - #define TEXTCOLOR(idx) if(usecolor) text_color(str[idx], colorstack, sizeof(colorstack), colorpos, color, a); - #define TEXTCHAR(idx) draw_char(tex, c, left+x, top+y, scale); x += cw; - #define TEXTWORD TEXTWORDSKELETON - char colorstack[10]; - colorstack[0] = '\0'; //indicate user color - bvec color(r, g, b); - if(textbright != 100) color.scale(textbright, 100); - int colorpos = 0; - float cx = -FONTW, cy = 0; - bool usecolor = true; - if(a < 0) { usecolor = false; a = -a; } - Texture *tex = curfont->texs[0]; - (textshader ? textshader : hudtextshader)->set(); - LOCALPARAMF(textparams, curfont->bordermin, curfont->bordermax, curfont->outlinemin, curfont->outlinemax); + const int w = info.w, h = info.h; + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - setusedtexture(tex); - gle::color(color, a); + + if(textshader) textshader->set(); // text particles + else hudtextshader->set(); // UI text + + gle::color(black ? bvec(0, 0, 0) : bvec(255*a/255.f, 255*a/255.f, 255*a/255.f), a); + glBindTexture(GL_TEXTURE_RECTANGLE, info.tex); gle::defvertex(textmatrix ? 3 : 2); gle::deftexcoord0(); - gle::begin(GL_QUADS); - TEXTSKELETON - TEXTEND(cursor) - xtraverts += gle::end(); - if(cursor >= 0 && (totalmillis/350)&1) + + // NOTE: `GL_TRIANGLE_STRIP` does not work with `textmatrix` so we have to use `GL_QUADS` instead + gle::begin(textmatrix ? GL_QUADS : GL_TRIANGLE_STRIP); + if(textmatrix) // text particle inside the world + { + gle::attrib(textmatrix->transform(vec2(left , top ))); gle::attribf(0, 0); + gle::attrib(textmatrix->transform(vec2(left+w, top ))); gle::attribf(w, 0); + gle::attrib(textmatrix->transform(vec2(left+w, top+h))); gle::attribf(w, h); + gle::attrib(textmatrix->transform(vec2(left , top+h))); gle::attribf(0, h); + } + else // UI text { - gle::color(color, a); - if(maxwidth >= 0 && cx >= maxwidth && cx > 0) { cx = 0; cy += FONTH; } - draw_char(tex, '_', left+cx, top+cy, scale); - xtraverts += gle::end(); + gle::attribf(left+w, top ); gle::attribf(w, 0); + gle::attribf(left , top ); gle::attribf(0, 0); + gle::attribf(left+w, top+h); gle::attribf(w, h); + gle::attribf(left , top+h); gle::attribf(0, h); } - #undef TEXTINDEX - #undef TEXTWHITE - #undef TEXTLINE - #undef TEXTCOLOR - #undef TEXTCHAR - #undef TEXTWORD + gle::end(); + // NOTE: `info.tex` is not deleted here! +} +void draw_text(const char *str, float left, float top, int r, int g, int b, int a, const char *language) +{ + textinfo info; + prepare_text(str, info, 0, bvec(r, g, b), -1, 0, bvec(0, 0, 0), language); + if(!info.tex) return; + draw_text(info, left, top, a); + glDeleteTextures(1, &info.tex); +} +void draw_textf(const char *fstr, float left, float top, ...) +{ + defvformatstring(str, top, fstr); + draw_text(str, left, top); +} + +void gettextres(int &w, int &h) +{ + if(w < MINRESW || h < MINRESH) + { + if(MINRESW > w*MINRESH/h) + { + h = h*MINRESW/w; + w = MINRESW; + } + else + { + w = w*MINRESH/h; + h = MINRESH; + } + } +} + +// used by the text editor +int text_visible(const char *str, float hitx, float hity, int maxwidth, const char *language) +{ + int width, height; + const int len = strlen(str); + if(!len) return 0; + int map_text_to_markup[len+1]; + PangoLayout *layout = measure_text_internal(str, len, maxwidth, bvec(0, 0, 0), width, height, NULL, map_text_to_markup, language); + if(!layout) return len; + if(!width || !height) { g_object_unref(layout); return len; } + + int index; + const int res = pango_layout_xy_to_index(layout, hitx * PANGO_SCALE, hity * PANGO_SCALE, &index, NULL); + g_object_unref(layout); + if(!res) return len; + return map_text_to_markup[index]; } -void reloadfonts() +// used by the text editor +void text_pos(const char *str, int cursor, int &cx, int &cy, int maxwidth, const char *language) { - enumerate(fonts, font, f, loopv(f.texs) { if(!reloadtexture(f.texs[i])) { fatal("Failed to reload font texture"); }}); + int width, height; + const int len = strlen(str); + if(!len) { cx = cy = 0; return; } + int map_markup_to_text[len+1]; + PangoLayout *layout = measure_text_internal(str, len, maxwidth, bvec(0, 0, 0), width, height, map_markup_to_text, NULL, language); + if(!layout) { cx = cy = 0; return; } + if(!width || !height) { g_object_unref(layout); cx = cy = 0; return; } + + cursor = max(0, min((int)strlen(pango_layout_get_text(layout)), map_markup_to_text[cursor])); + PangoRectangle pos; + pango_layout_index_to_pos(layout, cursor, &pos); + cx = pos.x / PANGO_SCALE; + cy = pos.y / PANGO_SCALE; + + g_object_unref(layout); } +void reloadfonts() { clear_text_particles(); UI::cleartext(); } \ No newline at end of file diff --git a/source/engine/server.cpp b/source/engine/server.cpp index 5176a81bb..579fed8a8 100644 --- a/source/engine/server.cpp +++ b/source/engine/server.cpp @@ -45,17 +45,13 @@ void logoutf(const char *fmt, ...) va_end(args); } - static void writelog(FILE *file, const char *buf) { - static uchar ubuf[512]; - size_t len = strlen(buf), carry = 0; - while(carry < len) - { - size_t numu = encodeutf8(ubuf, sizeof(ubuf)-1, &((const uchar *)buf)[carry], len - carry, &carry); - if(carry >= len) ubuf[numu++] = '\n'; - fwrite(ubuf, 1, numu, file); - } + static char ubuf[512]; + filteruni(ubuf, buf, 511); + const size_t len = min((size_t)511, strlen(ubuf)); + ubuf[len] = '\n'; + fwrite(ubuf, 1, len+1, file); } static void writelogv(FILE *file, const char *fmt, va_list args) @@ -810,14 +806,11 @@ static BOOL WINAPI consolehandler(DWORD dwCtrlType) static void writeline(logline &line) { - static uchar ubuf[512]; - size_t len = strlen(line.buf), carry = 0; - while(carry < len) - { - size_t numu = encodeutf8(ubuf, sizeof(ubuf), &((uchar *)line.buf)[carry], len - carry, &carry); - DWORD written = 0; - WriteConsole(outhandle, ubuf, numu, &written, NULL); - } + static char ubuf[512]; + filteruni(ubuf, line.buf, 511); + const size_t len = min((size_t)511, strlen(ubuf)); + DWORD written; + WriteConsole(outhandle, ubuf, len, &written, NULL); } static void setupconsole() diff --git a/source/engine/serverbrowser.cpp b/source/engine/serverbrowser.cpp index 88c535f4b..3e83c9fd1 100644 --- a/source/engine/serverbrowser.cpp +++ b/source/engine/serverbrowser.cpp @@ -542,9 +542,9 @@ void checkpings() si->attr.setsize(0); loopj(numattr) { int attr = getint(p); if(p.overread()) break; si->attr.add(attr); } getstring(text, p); - filtertext(si->map, text, false, false, false); + filtertext(si->map, text, T_NONE); getstring(text, p); - filtertext(si->desc, text, true, false); + filtertext(si->desc, text, T_COLORS | T_WHITESPACE); if(p.remaining()) { si->gameversion = getint(p); @@ -733,7 +733,7 @@ COMMAND(initservers, ""); void writeservercfg() { if(!game::savedservers()) return; - stream *f = openutf8file(path(game::savedservers(), true), "w"); + stream *f = openfile(path(game::savedservers(), true), "w"); if(!f) return; int kept = 0; loopv(servers) diff --git a/source/engine/shader.cpp b/source/engine/shader.cpp index 922a9c16f..c4f34b9d8 100644 --- a/source/engine/shader.cpp +++ b/source/engine/shader.cpp @@ -1031,21 +1031,20 @@ void setupshaders() "uniform mat4 hudmatrix;\n" "varying vec2 texcoord0;\n" "varying vec4 colorscale;\n" - "void main(void) {\n" + "void main(void)\n" + "{\n" " gl_Position = hudmatrix * vvertex;\n" - " texcoord0 = vtexcoord0;\n" + " texcoord0 = vtexcoord0;\n" " colorscale = vcolor;\n" "}\n", - "uniform sampler2D tex0;\n" - "uniform vec4 textparams;\n" + "uniform sampler2DRect tex0;\n" "varying vec2 texcoord0;\n" "varying vec4 colorscale;\n" "fragdata(0) vec4 fragcolor;\n" - "void main(void) {\n" - " float dist = texture2D(tex0, texcoord0).r;\n" - " float border = smoothstep(textparams.x, textparams.y, dist);\n" - " float outline = smoothstep(textparams.z, textparams.w, dist);\n" - " fragcolor = vec4(colorscale.rgb * outline, colorscale.a * border);\n" + "void main(void)\n" + "{\n" + " fragcolor = colorscale * texture2DRect(tex0, texcoord0);\n" + " if(fragcolor.a != 0) fragcolor.rgb /= fragcolor.a;\n" "}\n"); hudnotextureshader = newshader(0, "hudnotexture", "attribute vec4 vvertex, vcolor;\n" @@ -1546,7 +1545,7 @@ void cleanupshaders() cleanuppostfx(true); loadedshaders = false; - nullshader = hudshader = hudnotextureshader = NULL; + nullshader = hudshader = hudtextshader = hudnotextureshader = NULL; enumerate(shaders, Shader, s, s.cleanup()); Shader::lastshader = NULL; glUseProgram_(0); diff --git a/source/engine/textedit.h b/source/engine/textedit.h index d97c9abfe..7be2d499a 100644 --- a/source/engine/textedit.h +++ b/source/engine/textedit.h @@ -1,3 +1,4 @@ +#include "unicode.h" struct editline { @@ -178,6 +179,7 @@ struct editor void clear(const char *init = "") { + inputmillis = totalmillis; cx = cy = 0; mark(false); loopv(lines) lines[i].clear(); @@ -193,7 +195,7 @@ struct editor void updateheight() { int width; - text_bounds(lines[0].text, width, pixelheight, pixelwidth); + measure_text(lines[0].text, pixelwidth, width, pixelheight); } void setfile(const char *fname) @@ -206,7 +208,7 @@ struct editor { if(!filename) return; clear(NULL); - stream *file = openutf8file(filename, "r"); + stream *file = openfile(filename, "r"); if(file) { while(lines.add().read(file, maxx) && (maxy < 0 || lines.length() <= maxy)); @@ -219,7 +221,7 @@ struct editor void save() { if(!filename) return; - stream *file = openutf8file(filename, "w"); + stream *file = openfile(filename, "w"); if(!file) return; loopv(lines) file->putline(lines[i].text); delete file; @@ -348,6 +350,7 @@ struct editor bool del() // removes the current selection (if any) { + inputmillis = totalmillis; int sx, sy, ex, ey; if(!region(sx, sy, ex, ey)) { @@ -447,16 +450,19 @@ struct editor void scrollup() { + inputmillis = totalmillis; cy--; } void scrolldown() { + inputmillis = totalmillis; cy++; } void key(int code) { + inputmillis = totalmillis; switch(code) { case SDLK_UP: @@ -475,7 +481,7 @@ struct editor int x, y, width, height; char *str = currentline().text; text_pos(str, cx, x, y, pixelwidth); - text_bounds(str, width, height, pixelwidth); + measure_text(str, pixelwidth, width, height); y += FONTH; if(y < height) { cx = text_visible(str, x, y, pixelwidth); break; } } @@ -494,16 +500,28 @@ struct editor cx = cy = INT_MAX; break; case SDLK_LEFT: - cx--; + { + editline ¤t = currentline(); + cx -= uni_prevchar(current.text, cx); break; + } case SDLK_RIGHT: - cx++; + { + editline ¤t = currentline(); + uint _codepoint; + cx += uni_getchar(¤t.text[cx], _codepoint); break; + } case SDLK_DELETE: if(!del()) { editline ¤t = currentline(); - if(cx < current.len) current.del(cx, 1); + if(cx < current.len) + { + uint _codepoint; + const int s = uni_getchar(¤t.text[cx], _codepoint); + current.del(cx, s); + } else if(cy < lines.length()-1) { //combine with next line current.append(lines[cy+1].text); @@ -515,7 +533,12 @@ struct editor if(!del()) { editline ¤t = currentline(); - if(cx > 0) current.del(--cx, 1); + if(cx > 0) + { + const int s = uni_prevchar(current.text, cx); + cx -= s; + current.del(cx, s); + } else if(cy > 0) { //combine with previous line cx = lines[cy-1].len; @@ -543,12 +566,13 @@ struct editor void hit(int hitx, int hity, bool dragged) { + inputmillis = totalmillis; int maxwidth = linewrap?pixelwidth:-1; int h = 0; for(int i = scrolly; i < lines.length(); i++) { int width, height; - text_bounds(lines[i].text, width, height, maxwidth); + measure_text(lines[i].text, maxwidth, width, height); if(h + height > pixelheight) break; if(hity >= h && hity <= h+height) @@ -568,7 +592,7 @@ struct editor for(int ph = pixelheight; slines > 0 && ph > 0;) { int width, height; - text_bounds(lines[slines-1].text, width, height, maxwidth); + measure_text(lines[slines-1].text, maxwidth, width, height); if(height > ph) break; ph -= height; slines--; @@ -592,7 +616,7 @@ struct editor for(int i = cy; i >= scrolly; i--) { int width, height; - text_bounds(lines[i].text, width, height, maxwidth); + measure_text(lines[i].text, maxwidth, width, height); if(h + height > pixelheight) { scrolly = i+1; break; } h += height; } @@ -609,7 +633,7 @@ struct editor for(int i = scrolly; i < maxy; i++) { int width, height; - text_bounds(lines[i].text, width, height, maxwidth); + measure_text(lines[i].text, maxwidth, width, height); if(h + height > pixelheight) { maxy = i; break; } if(i == sy) psy += h; if(i == ey) { pey += h; break; } @@ -658,24 +682,28 @@ struct editor int h = 0; for(int i = scrolly; i < lines.length(); i++) { - int width, height; - text_bounds(lines[i].text, width, height, maxwidth); - if(h + height > pixelheight) break; + textinfo info; + prepare_text(lines[i].text, info, maxwidth, bvec(255, 255, 255), hit&&(cy==i)?cx:-1); + if(h + info.h > pixelheight) break; - draw_text(lines[i].text, x, y+h, color>>16, (color>>8)&0xFF, color&0xFF, 0xFF, hit&&(cy==i)?cx:-1, maxwidth); - if(linewrap && height > FONTH) // line wrap indicator + if(info.tex) + { + draw_text(info, x, y); + glDeleteTextures(1, &info.tex); + } + if(linewrap && info.h > FONTH) // line wrap indicator { hudnotextureshader->set(); gle::colorub(0x80, 0xA0, 0x80); gle::defvertex(2); gle::begin(GL_TRIANGLE_STRIP); gle::attribf(x, y+h+FONTH); - gle::attribf(x, y+h+height); + gle::attribf(x, y+h+info.h); gle::attribf(x-FONTW/2, y+h+FONTH); - gle::attribf(x-FONTW/2, y+h+height); + gle::attribf(x-FONTW/2, y+h+info.h); gle::end(); } - h+=height; + h+=info.h; } } }; diff --git a/source/engine/ui.cpp b/source/engine/ui.cpp index 174e5fe5d..f6214b160 100644 --- a/source/engine/ui.cpp +++ b/source/engine/ui.cpp @@ -186,6 +186,7 @@ namespace UI void modblend() { changeblend(BLEND_MOD, GL_ZERO, GL_SRC_COLOR); } FVARP(uiscale, 0.5f, 1.0f, 1.5f); + VARP(uifps, 0, 60, 1000); struct Object { @@ -291,6 +292,23 @@ namespace UI }); } + // called when the window is closed + virtual void hide() + { + loopchildren(o, + { + o->hide(); + }); + } + + virtual void cleartext() + { + loopchildren(o, + { + o->cleartext(); + }); + } + void adjustchildrento(float px, float py, float pw, float ph) { loopchildren(o, o->adjustlayout(px, py, pw, ph)); @@ -626,6 +644,10 @@ namespace UI void hide() { + loopchildren(o, + { + o->hide(); + }); if(onhide) execute(onhide); } @@ -805,6 +827,8 @@ namespace UI resetstate(); } + void hide() {}; + bool show(Window *w) { if(children.find(w) >= 0) return false; @@ -1940,8 +1964,6 @@ namespace UI } }; - // default size of text in terms of rows per screenful - VARP(uitextrows, 1, 24, 200); FVAR(uitextscale, 1, 0, 0); #define SETSTR(dst, src) do { \ @@ -1949,24 +1971,45 @@ namespace UI else dst = newstring(src); \ } while(0) + // NOTE: `scale` is the text height in screenfuls at `uiscale 1` struct Text : Object { float scale, wrap; Color color; + textinfo info; + int fontid, lastchange; + int fancy; // 0 = none, 1 = shadow, 2 = outline, 3 = shadow+outline + char *language; + bool changed; + uint crc; // string hash used for change detection - void setup(float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1) + Text() : info({0, 0, 0}), lastchange(0), fancy(0), language(NULL), crc(0) {} + + void setup(float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1, int fancy_ = 0, const char *language_ = "") { Object::setup(); + changed = false; + float newscale = scale_ * uiscale; + + int curfontid = getcurfontid(); + if(newscale != scale || wrap_ != wrap || fontid != curfontid || fancy_ != fancy || (!language || strcmp(language_, language)) || (color_.r != color.r || color_.g != color.g || color_.b != color.b)) + { + changed = true; + lastchange = totalmillis; + } - scale = scale_ * uiscale; + scale = newscale; color = color_; wrap = wrap_; + fancy = fancy_; + SETSTR(language, language_); + fontid = curfontid; } static const char *typestr() { return "#Text"; } const char *gettype() const { return typestr(); } - float drawscale() const { return scale / FONTH; } + float drawscale() const { return 1.f / hudh; } virtual const char *getstr() const { return ""; } @@ -1976,20 +2019,60 @@ namespace UI changedraw(CHANGE_SHADER | CHANGE_COLOR); - float oldscale = textscale; - textscale = drawscale(); - draw_text(getstr(), sx/textscale, sy/textscale, color.r, color.g, color.b, color.a, -1, wrap >= 0 ? int(wrap/textscale) : -1); - textscale = oldscale; + setfontsize(scale * hudh); + + const float textscale = drawscale(), + x = round(sx/textscale), y = round(sy/textscale); + pushhudscale(textscale); + if(fancy&1) // shadow + { + draw_text(info, x-0.001/textscale, y+0.001/textscale, color.a, true); + } + draw_text(info, x, y, color.a); + pophudmatrix(); + } + + void hide() { cleartext(); } + + void cleartext() + { + if(info.tex) + { + glDeleteTextures(1, &info.tex); + info.tex = 0; + } } + ~Text() { cleartext(); delete[] language; } + void layout() { Object::layout(); - float k = drawscale(), tw, th; - text_boundsf(getstr(), tw, th, wrap >= 0 ? int(wrap/k) : -1); - w = max(w, tw*k); - h = max(h, th*k); + setfontsize(scale * hudh); + + float k = drawscale(); + + // text changes are detected here + const char *text = getstr(); + if(!uifps || (totalmillis - lastchange >= 1000/uifps)) + { + const uint crc_new = crc32(0, (const Bytef *)text, strlen(text)); + if(crc_new != crc) + { + changed = true; + lastchange = totalmillis; + } + crc = crc_new; + } + if(changed && info.tex) cleartext(); + + if(!info.tex) + { + prepare_text(text, info, int(wrap/k), bvec(color.r, color.g, color.b), -1, fancy >= 2 ? 1.0 : 0, bvec(0, 0, 0), language); + } + w = max(w, info.w*k); + h = max(h, info.h*k); } }; @@ -2000,9 +2083,9 @@ namespace UI TextString() : str(NULL) {} ~TextString() { delete[] str; } - void setup(const char *str_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1) + void setup(const char *str_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1, int fancy_ = 0, const char *language_ = "") { - Text::setup(scale_, color_, wrap_); + Text::setup(scale_, color_, wrap_, fancy_, language_); SETSTR(str, str_); } @@ -2020,9 +2103,9 @@ namespace UI TextInt() : val(0) { str[0] = '0'; str[1] = '\0'; } - void setup(int val_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1) + void setup(int val_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1, int fancy_ = 0, const char *language_ = "") { - Text::setup(scale_, color_, wrap_); + Text::setup(scale_, color_, wrap_, fancy_, language_); if(val != val_) { val = val_; intformat(str, val, sizeof(str)); } } @@ -2040,9 +2123,9 @@ namespace UI TextFloat() : val(0) { memcpy(str, "0.0", 4); } - void setup(float val_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1) + void setup(float val_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1, int fancy_ = 0, const char *language_ = "") { - Text::setup(scale_, color_, wrap_); + Text::setup(scale_, color_, wrap_, fancy_, language_); if(val != val_) { val = val_; floatformat(str, val, sizeof(str)); } } @@ -2055,78 +2138,82 @@ namespace UI struct Font : Object { - ::font *font; + string font; - Font() : font(NULL) {} + Font() { font[0] = '\0'; } void setup(const char *name) { Object::setup(); - - if(!font || !strcmp(font->name, name)) font = findfont(name); + copystring(font, name, MAXSTRLEN); } + #define WITHFONT(body) do { \ + pushfont(); \ + setfont(font); \ + body; \ + popfont(); \ + } while(0); \ + void layout() { - pushfont(); - setfont(font); - Object::layout(); - popfont(); + WITHFONT({ + Object::layout(); + }); } void draw(float sx, float sy) { - pushfont(); - setfont(font); - Object::draw(sx, sy); - popfont(); + WITHFONT({ + Object::draw(sx, sy); + }); } void buildchildren(uint *contents) { - pushfont(); - setfont(font); - Object::buildchildren(contents); - popfont(); + WITHFONT({ + Object::buildchildren(contents); + }); } #define DOSTATE(flags, func) \ void func##children(float cx, float cy, int mask, bool inside, int setflags) \ { \ - pushfont(); \ - setfont(font); \ - Object::func##children(cx, cy, mask, inside, setflags); \ - popfont(); \ - } + WITHFONT({ \ + Object::func##children(cx, cy, mask, inside, setflags); \ + }); \ + } \ DOSTATES #undef DOSTATE bool rawkey(int code, bool isdown) { - pushfont(); - setfont(font); - bool result = Object::rawkey(code, isdown); - popfont(); + bool result; + WITHFONT({ + result = Object::rawkey(code, isdown); + }); return result; } bool key(int code, bool isdown) { - pushfont(); - setfont(font); - bool result = Object::key(code, isdown); - popfont(); + bool result; + WITHFONT({ + result = Object::key(code, isdown); + }); return result; } bool textinput(const char *str, int len) { - pushfont(); - setfont(font); - bool result = Object::textinput(str, len); - popfont(); + bool result; + WITHFONT({ + result = Object::textinput(str, len); + }); return result; } + + #undef WITHFONT }; float uicontextscale = 0; @@ -2150,7 +2237,7 @@ namespace UI changedraw(CHANGE_SHADER | CHANGE_COLOR); - float k = drawscale(); + float k = drawscale() / uiscale; pushhudtranslate(sx, sy, k); renderfullconsole(w/k, h/k); pophudmatrix(); @@ -2668,6 +2755,7 @@ namespace UI void setup(const char *name, int length, int height, float scale_ = 1, const char *initval = NULL, int mode = EDITORUSED, const char *keyfilter_ = NULL) { Object::setup(); + setfontsize(scale * hudh); editor *edit_ = useeditor(name, mode, false, initval); if(edit_ != edit) { @@ -2682,7 +2770,7 @@ namespace UI edit->maxy = height <= 0 ? 1 : -1; edit->pixelwidth = abs(length)*FONTW; if(edit->linewrap && edit->maxy == 1) edit->updateheight(); - else edit->pixelheight = FONTH*max(height, 1); + else edit->pixelheight = FONTH*1.5*max(height, 1); scale = scale_; if(keyfilter_) SETSTR(keyfilter, keyfilter_); else DELETEA(keyfilter); @@ -2713,7 +2801,7 @@ namespace UI return true; } - float drawscale() const { return scale / FONTH; } + float drawscale() const { return 1.f / hudh; } void draw(float sx, float sy) { @@ -2721,6 +2809,8 @@ namespace UI edit->rendered = true; + setfontsize(scale * hudh); + float k = drawscale(); pushhudtranslate(sx, sy, k); @@ -2735,6 +2825,8 @@ namespace UI { Object::layout(); + setfontsize(scale * hudh); + float k = drawscale(); w = max(w, (edit->pixelwidth + FONTW)*k); h = max(h, edit->pixelheight*k); @@ -2757,6 +2849,8 @@ namespace UI { if(isfocus()) { + setfontsize(scale * hudh); + float k = drawscale(); bool dragged = max(fabs(cx - offsetx), fabs(cy - offsety)) > (FONTH/8.0f)*k; edit->hit(int(floor(cx/k - FONTW/2)), int(floor(cy/k)), dragged); @@ -2872,7 +2966,7 @@ namespace UI if(isfocus() && !hasstate(STATE_HOVER)) commit(); if(changed) { - if(id == id_) setsval(id, edit->lines[0].text, onchange); + if(edit->lines.length()) if(id == id_) setsval(id, edit->lines[0].text, onchange); changed = false; } bool shouldfree = false; @@ -3447,24 +3541,25 @@ namespace UI ICOMMAND(uimodcircle, "ife", (int *c, float *size, uint *children), BUILD(Circle, o, o->setup(Color(*c), *size, Circle::MODULATE), children)); - static inline void buildtext(tagval &t, float scale, float scalemod, const Color &color, float wrap, uint *children) + static inline void buildtext(tagval &t, float scale, float scalemod, const Color &color, float wrap, uint *children, int fancy, const char *language) { if(scale <= 0) scale = 1; scale *= scalemod; + if(!language) language = ""; switch(t.type) { case VAL_INT: - BUILD(TextInt, o, o->setup(t.i, scale, color, wrap), children); + BUILD(TextInt, o, o->setup(t.i, scale, color, wrap, fancy, language), children); break; case VAL_FLOAT: - BUILD(TextFloat, o, o->setup(t.f, scale, color, wrap), children); + BUILD(TextFloat, o, o->setup(t.f, scale, color, wrap, fancy, language), children); break; case VAL_CSTR: case VAL_MACRO: case VAL_STR: if(t.s[0]) { - BUILD(TextString, o, o->setup(t.s, scale, color, wrap), children); + BUILD(TextString, o, o->setup(t.s, scale, color, wrap, fancy, language), children); break; } // fall-through @@ -3474,35 +3569,35 @@ namespace UI } } - ICOMMAND(uicolortext, "tife", (tagval *text, int *c, float *scale, uint *children), - buildtext(*text, *scale, uitextscale, Color(*c), -1, children)); + ICOMMAND(uicolortext, "tifise", (tagval *text, int *c, float *scale, int *fancy, const char *language, uint *children), + buildtext(*text, *scale, uitextscale, Color(*c), -1, children, *fancy, language)); - ICOMMAND(uitext, "tfe", (tagval *text, float *scale, uint *children), - buildtext(*text, *scale, uitextscale, Color(255, 255, 255), -1, children)); + ICOMMAND(uitext, "tfise", (tagval *text, float *scale, int *fancy, const char *language, uint *children), + buildtext(*text, *scale, uitextscale, Color(255, 255, 255), -1, children, *fancy, language)); ICOMMAND(uitextfill, "ffe", (float *minw, float *minh, uint *children), BUILD(Filler, o, o->setup(*minw * uitextscale*0.5f, *minh * uitextscale), children)); - ICOMMAND(uiwrapcolortext, "tfife", (tagval *text, float *wrap, int *c, float *scale, uint *children), - buildtext(*text, *scale, uitextscale, Color(*c), *wrap, children)); + ICOMMAND(uiwrapcolortext, "tfifse", (tagval *text, float *wrap, int *c, float *scale, int *fancy, const char *language, uint *children), + buildtext(*text, *scale, uitextscale, Color(*c), *wrap, children, *fancy, language)); - ICOMMAND(uiwraptext, "tffe", (tagval *text, float *wrap, float *scale, uint *children), - buildtext(*text, *scale, uitextscale, Color(255, 255, 255), *wrap, children)); + ICOMMAND(uiwraptext, "tffse", (tagval *text, float *wrap, float *scale, int *fancy, const char *language, uint *children), + buildtext(*text, *scale, uitextscale, Color(255, 255, 255), *wrap, children, *fancy, language)); ICOMMAND(uicolorcontext, "tife", (tagval *text, int *c, float *scale, uint *children), - buildtext(*text, *scale, FONTH*uicontextscale, Color(*c), -1, children)); + buildtext(*text, *scale, FONTH*uicontextscale, Color(*c), -1, children, 0, "")); ICOMMAND(uicontext, "tfe", (tagval *text, float *scale, uint *children), - buildtext(*text, *scale, FONTH*uicontextscale, Color(255, 255, 255), -1, children)); + buildtext(*text, *scale, FONTH*uicontextscale, Color(255, 255, 255), -1, children, 0, "")); ICOMMAND(uicontextfill, "ffe", (float *minw, float *minh, uint *children), BUILD(Filler, o, o->setup(*minw * FONTH*uicontextscale*0.5f, *minh * FONTH*uicontextscale), children)); ICOMMAND(uiwrapcolorcontext, "tfife", (tagval *text, float *wrap, int *c, float *scale, uint *children), - buildtext(*text, *scale, FONTH*uicontextscale, Color(*c), *wrap, children)); + buildtext(*text, *scale, FONTH*uicontextscale, Color(*c), *wrap, children, 0, "")); ICOMMAND(uiwrapcontext, "tffe", (tagval *text, float *wrap, float *scale, uint *children), - buildtext(*text, *scale, FONTH*uicontextscale, Color(255, 255, 255), *wrap, children)); + buildtext(*text, *scale, FONTH*uicontextscale, Color(255, 255, 255), *wrap, children, 0, "")); ICOMMAND(uitexteditor, "siifsie", (char *name, int *length, int *height, float *scale, char *initval, int *mode, uint *children), BUILD(TextEditor, o, o->setup(name, *length, *height, (*scale <= 0 ? 1 : *scale) * uitextscale, initval, *mode <= 0 ? EDITORFOREVER : *mode), children)); @@ -3678,14 +3773,19 @@ namespace UI DELETEP(world); } + void cleartext() + { + world->cleartext(); + } + void calctextscale() { - uitextscale = 1.0f/uitextrows; + uitextscale = 1.0f/UITEXTROWS; int tw = hudw, th = hudh; if(forceaspect) tw = int(ceil(th*forceaspect)); gettextres(tw, th); - uicontextscale = conscale/th * uiscale; + uicontextscale = 1.f/th * uiscale; } void update() diff --git a/source/game/ctf.h b/source/game/ctf.h index 717e3c1f4..148d9d79c 100644 --- a/source/game/ctf.h +++ b/source/game/ctf.h @@ -447,7 +447,7 @@ struct ctfclientmode : clientmode if(hud->holdingflag && f.team == hud->team) { vec base = f.spawnloc; - particle_hud_mark(base, 2, 1, PART_GAME_ICONS, 1, teamtextcolor[hud->team], 5.0f); + particle_hud_text(base, "GOAL", PART_TEXT, 1, teamtextcolor[hud->team], 5.0f, "wide"); } else if(f.owner) { diff --git a/source/game/game.cpp b/source/game/game.cpp index 019a385af..99d881a5e 100644 --- a/source/game/game.cpp +++ b/source/game/game.cpp @@ -843,7 +843,7 @@ namespace game void initclient() { self = spawnstate(new gameent); - filtertext(self->name, "player", false, false, true, false, MAXNAMELEN); + filtertext(self->name, "player", T_WHITESPACE | T_NAME, MAXNAMELEN, MAXNAMEUNILEN); players.add(self); } diff --git a/source/game/game.h b/source/game/game.h index b4302fc8d..114280b96 100644 --- a/source/game/game.h +++ b/source/game/game.h @@ -383,7 +383,8 @@ struct gamestate #include "monster.h" -const int MAXNAMELEN = 15; +const int MAXNAMELEN = 60; // bytes +const int MAXNAMEUNILEN = 15; // unicode characters const int MAXCOUNTRYCODELEN = 8; const int MAXTEAMS = 2; diff --git a/source/game/gameclient.cpp b/source/game/gameclient.cpp index 6f727427b..40fa2f531 100644 --- a/source/game/gameclient.cpp +++ b/source/game/gameclient.cpp @@ -175,7 +175,7 @@ namespace game void switchname(const char* name) { - filtertext(self->name, name, false, false, true, false, MAXNAMELEN); + filtertext(self->name, name, T_WHITESPACE | T_NAME, MAXNAMELEN, MAXNAMEUNILEN); if (!self->name[0]) copystring(self->name, "player"); if (!isvalidname(self->name)) { @@ -1651,11 +1651,11 @@ namespace game break; } getstring(text, p); - filtertext(text, text, false, false, false, false, MAXCOUNTRYCODELEN); + filtertext(text, text, T_NONE, MAXCOUNTRYCODELEN); validcountrycode(d->country_code, text); getstring(text, p); - filtertext(d->country_name, text, false, false, true, false, MAXSTRLEN); + filtertext(d->country_name, text, T_WHITESPACE, MAXSTRLEN); break; } @@ -1711,10 +1711,10 @@ namespace game int cn = getint(p); gameent *d = getclient(cn); getstring(text, p); - filtertext(text, text, false, false, true, true); + filtertext(text, text, T_WHITESPACE | T_FORCESPACE); if(!d || isignored(d->clientnum)) break; if(d->state!=CS_DEAD && d->state!=CS_SPECTATOR) - particle_textcopy(d->abovehead(), text, PART_TEXT, 2000, 0x32FF64, 4.0f, -8); + particle_textcopy(d->abovehead(), text, PART_TEXT, 2000, 0x32FF64, 4.0f, -8, "default"); conoutf(CON_CHAT, "%s: \fs%s%s\fr", teamcolorname(d), chatcolor(d), text); if(chatsound == 1) playsound(S_CHAT); break; @@ -1725,13 +1725,13 @@ namespace game int tcn = getint(p); gameent *t = getclient(tcn); getstring(text, p); - filtertext(text, text, false, false, true, true); + filtertext(text, text, T_WHITESPACE | T_FORCESPACE); int sound = getint(p); if(!t || isignored(t->clientnum)) break; if(sound >= 0 && (t->state != CS_DEAD || t->state != CS_SPECTATOR)) playsound(sound, t); int team = validteam(t->team) ? t->team : 0; if(t->state!=CS_DEAD) - particle_textcopy(t->abovehead(), text, PART_TEXT, 2000, teamtextcolor[team], 4.0f, -8); + particle_textcopy(t->abovehead(), text, PART_TEXT, 2000, teamtextcolor[team], 4.0f, -8, "default"); conoutf(CON_TEAMCHAT, "%s \fs%s(team)\fr: \fs%s%s\fr", teamcolorname(t), teamtextcode[t->team], teamtextcode[t->team], text); if(chatsound == 1) playsound(S_CHAT); break; @@ -1742,7 +1742,7 @@ namespace game int scn = getint(p); gameent *s = getclient(scn); getstring(text, p); - filtertext(text, text, false, false, true, true); + filtertext(text, text, T_WHITESPACE | T_FORCESPACE); if(!s || isignored(s->clientnum)) break; conoutf(CON_CHAT, "%s \fs\f5(whisper)\fr: \fs\f5%s\fr", teamcolorname(s), text); if(chatsound) playsound(S_CHAT); @@ -1801,7 +1801,7 @@ namespace game break; } getstring(text, p); - filtertext(text, text, false, false, true, false, MAXNAMELEN); + filtertext(text, text, T_WHITESPACE | T_NAME, MAXNAMELEN, MAXNAMEUNILEN); if(!text[0]) copystring(text, "player"); // if no text is specified for the name change, change to default name if(d->name[0]) // already connected but the client changed their name { @@ -1826,11 +1826,11 @@ namespace game d->playercolor = getint(p); getstring(text, p); - filtertext(text, text, false, false, false, false, MAXCOUNTRYCODELEN); + filtertext(text, text, T_NONE, MAXCOUNTRYCODELEN); validcountrycode(d->country_code, text); getstring(text, p); - filtertext(text, text, false, false, true, false, MAXSTRLEN); + filtertext(text, text, T_WHITESPACE, MAXSTRLEN); copystring(d->country_name, text); break; @@ -1840,7 +1840,7 @@ namespace game getstring(text, p); if(d) { - filtertext(text, text, false, false, true, false, MAXNAMELEN); + filtertext(text, text, T_WHITESPACE | T_NAME, MAXNAMELEN, MAXNAMEUNILEN); if(!text[0]) copystring(text, "player"); if(strcmp(text, d->name)) { @@ -2189,7 +2189,7 @@ namespace game int type = getint(p); getstring(text, p); string name; - filtertext(name, text, false, false, false); + filtertext(name, text, T_NONE); ident *id = getident(name); switch(type) { @@ -2455,7 +2455,7 @@ namespace game int bn = getint(p), on = getint(p), at = getint(p), sk = clamp(getint(p), 1, 101), pm = getint(p), col = getint(p), team = getint(p); string name; getstring(text, p); - filtertext(name, text, false, false, false, false, MAXNAMELEN); + filtertext(name, text, T_NAME, MAXNAMELEN, MAXNAMEUNILEN); gameent *b = newclient(bn); if(!b) break; ai::init(b, at, on, sk, bn, pm, col, name, team); diff --git a/source/game/gameserver.cpp b/source/game/gameserver.cpp index bcf73f8dd..788dfd3ea 100644 --- a/source/game/gameserver.cpp +++ b/source/game/gameserver.cpp @@ -3530,7 +3530,7 @@ namespace server ci->cleanauth(); if(!nextauthreq) nextauthreq = 1; ci->authreq = nextauthreq++; - filtertext(ci->authname, user, false, false, false, false, 100); + filtertext(ci->authname, user, T_NONE, 100); copystring(ci->authdesc, desc); if(ci->authdesc[0]) { @@ -3733,13 +3733,13 @@ namespace server case N_CONNECT: { getstring(text, p); - filtertext(text, text, false, false, true, false, MAXNAMELEN); + filtertext(text, text, T_WHITESPACE | T_NAME, MAXNAMELEN, MAXNAMEUNILEN); if(!text[0]) copystring(text, "player"); copystring(ci->name, text, MAXNAMELEN+1); ci->playermodel = getint(p); ci->playercolor = getint(p); getstring(text, p); - filtertext(ci->preferred_flag, text, false, false, false, false, MAXCOUNTRYCODELEN); + filtertext(ci->preferred_flag, text, T_NONE, MAXCOUNTRYCODELEN); string password, authdesc, authname; getstring(password, p, sizeof(password)); @@ -4073,7 +4073,7 @@ namespace server { getstring(text, p); if(cq->mute) break; - filtertext(text, text, false, false, true, true); + filtertext(text, text, T_WHITESPACE | T_FORCESPACE); loopv(clients) { clientinfo *c = clients[i]; @@ -4090,7 +4090,7 @@ namespace server getstring(text, p); int sound = getint(p); if(!ci || !cq || cq->mute || !m_teammode || !validteam(cq->team) || cq->state.state==CS_SPECTATOR || (m_round && cq->state.state==CS_DEAD)) break; - filtertext(text, text, false, false, true, true); + filtertext(text, text, T_WHITESPACE | T_FORCESPACE); loopv(clients) { clientinfo *t = clients[i]; @@ -4106,7 +4106,7 @@ namespace server int rcn = getint(p); getstring(text, p); if(!cq || cq->mute) break; - filtertext(text, text, false, false); + filtertext(text, text, T_WHITESPACE); clientinfo *recipient = NULL; loopv(clients) { @@ -4125,7 +4125,7 @@ namespace server { QUEUE_MSG; getstring(text, p); - filtertext(ci->name, text, false, false, true, false, MAXNAMELEN); + filtertext(ci->name, text, T_WHITESPACE | T_NAME, MAXNAMELEN, MAXNAMEUNILEN); if (!isvalidname(ci->name)) { copystring(ci->name, "player"); @@ -4164,7 +4164,7 @@ namespace server case N_MAPVOTE: { getstring(text, p); - filtertext(text, text, false, false, true, false); + filtertext(text, text, T_WHITESPACE); fixmapname(text); int reqmode = getint(p), reqmuts = getint(p); vote(text, reqmode, reqmuts, sender); @@ -4281,7 +4281,7 @@ namespace server { int victim = getint(p); getstring(text, p); - filtertext(text, text); + filtertext(text, text, T_NEWLINES | T_WHITESPACE); trykick(ci, victim, text); break; } @@ -4290,7 +4290,7 @@ namespace server { int victim = getint(p), val = getint(p); getstring(text, p); - filtertext(text, text); + filtertext(text, text, T_NEWLINES | T_WHITESPACE); trymute(ci, victim, val, text); break; } @@ -4459,7 +4459,7 @@ namespace server getstring(name, p, sizeof(name)); int victim = getint(p); getstring(text, p); - filtertext(text, text); + filtertext(text, text, T_NEWLINES | T_WHITESPACE); int authpriv = PRIV_AUTH; if(desc[0]) { @@ -4577,7 +4577,7 @@ namespace server case N_COUNTRY: { getstring(text, p); - filtertext(ci->preferred_flag, text, false, false, false, false, MAXCOUNTRYCODELEN); + filtertext(ci->preferred_flag, text, T_NONE, MAXCOUNTRYCODELEN); geoip_set_custom_flag(ci->preferred_flag, ci->country_code, ci->country_name, ci->customflag_code, ci->customflag_name); packetbuf q(MAXTRANS, ENET_PACKET_FLAG_RELIABLE); putint(q, N_COUNTRY); diff --git a/source/game/geoip.h b/source/game/geoip.h index 2c6110659..120393597 100644 --- a/source/game/geoip.h +++ b/source/game/geoip.h @@ -56,7 +56,6 @@ void geoip_lookup_ip(enet_uint32 ip, char *dst_country_code, char *dst_country_n #ifdef HAVE_MAXMINDDB static string text; - static uchar buf[MAXSTRLEN]; if(!mmdb) return; int error; @@ -75,18 +74,15 @@ void geoip_lookup_ip(enet_uint32 ip, char *dst_country_code, char *dst_country_n if(MMDB_SUCCESS == error && data.has_data && MMDB_DATA_TYPE_UTF8_STRING == data.type && data.data_size >= 2) { copystring(text, data.utf8_string, data.data_size+1); - filtertext(dst_country_code, text, false, false, false, false, MAXCOUNTRYCODELEN); + filtertext(dst_country_code, text, T_NONE, MAXCOUNTRYCODELEN); } // get country name error = MMDB_get_value(&result.entry, &data, "country", "names", "en", NULL); if(MMDB_SUCCESS == error && data.has_data && MMDB_DATA_TYPE_UTF8_STRING == data.type) { - size_t len = decodeutf8(buf, sizeof(buf)-1, (const uchar *)data.utf8_string, data.data_size); - if(len > 0) { - buf[len] = 0; - copystring(dst_country_name, (const char*)buf, len+1); - } + copystring(text, data.utf8_string, data.data_size <= MAXSTRLEN ? data.data_size : MAXSTRLEN); + filtertext(dst_country_name, text, T_WHITESPACE | T_FORCESPACE, MAXSTRLEN); } } #endif diff --git a/source/game/render.cpp b/source/game/render.cpp index b69416762..92070a498 100644 --- a/source/game/render.cpp +++ b/source/game/render.cpp @@ -193,7 +193,7 @@ namespace game } if(strcmp(self->preferred_flag, customflag) != 0) { - filtertext(self->preferred_flag, customflag, false, false, false, false, MAXCOUNTRYCODELEN); + filtertext(self->preferred_flag, customflag, T_NONE, MAXCOUNTRYCODELEN); addmsg(N_COUNTRY, "rs", self->preferred_flag); } } @@ -492,7 +492,7 @@ namespace game else if (d->state == CS_ALIVE && !hidenames()) { int team = m_teammode && validteam(d->team) ? d->team : 0; - particle_text(pos, d->info, PART_TEXT, 1, teamtextcolor[team], 2.0f); + particle_text(pos, d->info, PART_TEXT, 1, teamtextcolor[team], 2.0f, 0, "wide"); } booteffect(d); } diff --git a/source/game/scoreboard.cpp b/source/game/scoreboard.cpp index 8523bafb3..e899da518 100644 --- a/source/game/scoreboard.cpp +++ b/source/game/scoreboard.cpp @@ -180,7 +180,7 @@ namespace game { if(servdesc[0]) { - filtertext(servdesc, servdesc, true, false); + filtertext(servdesc, servdesc, T_COLORS | T_WHITESPACE); result(servdesc); } else diff --git a/source/shared/iengine.h b/source/shared/iengine.h index a733b267d..6f606c7e6 100644 --- a/source/shared/iengine.h +++ b/source/shared/iengine.h @@ -251,37 +251,14 @@ extern void drawquad(float x, float y, float w, float h, float tx1 = 0, float ty extern int currentversion; // rendertext +extern float fontsize; extern bool setfont(const char *name); +static inline void setfontsize(float size) { fontsize = size; } extern void pushfont(); extern bool popfont(); -extern void gettextres(int &w, int &h); -extern void draw_text(const char *str, float left, float top, int r = 255, int g = 255, int b = 255, int a = 255, int cursor = -1, int maxwidth = -1); +extern void draw_text(const char *str, float left, float top, int r = 255, int g = 255, int b = 255, int a = 255, const char *language = NULL); extern void draw_textf(const char *fstr, float left, float top, ...) PRINTFARGS(1, 4); -extern float text_widthf(const char *str); -extern void text_boundsf(const char *str, float &width, float &height, int maxwidth = -1); -extern int text_visible(const char *str, float hitx, float hity, int maxwidth); -extern void text_posf(const char *str, int cursor, float &cx, float &cy, int maxwidth); - -static inline int text_width(const char *str) -{ - return int(ceil(text_widthf(str))); -} - -static inline void text_bounds(const char *str, int &width, int &height, int maxwidth = -1) -{ - float widthf, heightf; - text_boundsf(str, widthf, heightf, maxwidth); - width = int(ceil(widthf)); - height = int(ceil(heightf)); -} - -static inline void text_pos(const char *str, int cursor, int &cx, int &cy, int maxwidth) -{ - float cxf, cyf; - text_posf(str, cursor, cxf, cyf, maxwidth); - cx = int(cxf); - cy = int(cyf); -} +extern void measure_text(const char *str, int maxwidth, int &width, int &height, const char *language = NULL); // texture @@ -359,10 +336,11 @@ extern void regular_particle_splash(int type, int num, int fade, const vec &p, i extern void regular_particle_flame(int type, const vec &p, float radius, float height, int color, int density = 3, float scale = 2.0f, float speed = 200.0f, float fade = 600.0f, int gravity = -15); extern void particle_splash(int type, int num, int fade, const vec &p, int color = 0xFFFFFF, float size = 1.0f, int radius = 150, int gravity = 2); extern void particle_trail(int type, int fade, const vec &from, const vec &to, int color = 0xFFFFFF, float size = 1.0f, int gravity = 20); -extern void particle_text(const vec &s, const char *t, int type, int fade = 2000, int color = 0xFFFFFF, float size = 2.0f, int gravity = 0); -extern void particle_textcopy(const vec &s, const char *t, int type, int fade = 2000, int color = 0xFFFFFF, float size = 2.0f, int gravity = 0); +extern void particle_text(const vec &s, const char *t, int type, int fade = 2000, int color = 0xFFFFFF, float size = 2.0f, int gravity = 0, const char *font = "wide", const char *language = NULL); +extern void particle_textcopy(const vec &s, const char *t, int type, int fade = 2000, int color = 0xFFFFFF, float size = 2.0f, int gravity = 0, const char *font = "wide", const char *language = NULL); extern void particle_icon(const vec &s, int ix, int iy, int type, int fade = 2000, int color = 0xFFFFFF, float size = 2.0f, int gravity = 0); extern void particle_hud_mark(const vec &s, int ix, int iy, int type, int fade, int color, float size); +extern void particle_hud_text(const vec &s, const char *t, int type, int fade, int color, float size, const char *font, const char *language = NULL); extern void particle_meter(const vec &s, float val, int type, int fade = 1, int color = 0xFFFFFF, int color2 = 0xFFFFF, float size = 2.0f); extern void particle_flare(const vec &p, const vec &dest, int fade, int type, int color = 0xFFFFFF, float size = 0.28f, physent *owner = NULL); extern void particle_fireball(const vec &dest, float max, int type, int fade = -1, int color = 0xFFFFFF, float size = 4.0f); diff --git a/source/shared/stream.cpp b/source/shared/stream.cpp index 1253b953f..1462393ef 100644 --- a/source/shared/stream.cpp +++ b/source/shared/stream.cpp @@ -28,223 +28,10 @@ void conoutf(int type, int tag, const char *fmt, ...) ///////////////////////// character conversion /////////////// -#define CUBECTYPE(s, p, d, a, A, u, U) \ - 0, U, U, U, U, U, U, U, U, s, s, s, s, s, U, U, \ - U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, \ - s, p, p, p, p, p, p, p, p, p, p, p, p, p, p, p, \ - d, d, d, d, d, d, d, d, d, d, p, p, p, p, p, p, \ - p, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, \ - A, A, A, A, A, A, A, A, A, A, A, p, p, p, p, p, \ - p, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, \ - a, a, a, a, a, a, a, a, a, a, a, p, p, p, p, U, \ - U, u, u, u, u, u, u, u, u, u, u, u, u, u, u, u, \ - u, u, u, u, u, u, u, u, u, u, u, u, u, u, u, U, \ - u, U, u, U, u, U, u, U, u, U, u, U, u, U, u, U, \ - u, U, u, U, u, U, u, U, u, U, u, U, u, U, u, U, \ - u, U, u, U, u, U, u, U, U, u, U, u, U, u, U, U, \ - U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, \ - U, U, U, U, u, u, u, u, u, u, u, u, u, u, u, u, \ - u, u, u, u, u, u, u, u, u, u, u, u, u, u, U, u - -extern const uchar cubectype[256] = +static inline int uni_charlower(char c) { - CUBECTYPE(CT_SPACE, - CT_PRINT, - CT_PRINT|CT_DIGIT, - CT_PRINT|CT_ALPHA|CT_LOWER, - CT_PRINT|CT_ALPHA|CT_UPPER, - CT_PRINT|CT_UNICODE|CT_ALPHA|CT_LOWER, - CT_PRINT|CT_UNICODE|CT_ALPHA|CT_UPPER) -}; -extern const int cube2unichars[256] = -{ - 0, 192, 193, 194, 195, 196, 197, 198, 199, 9, 10, 11, 12, 13, 200, 201, - 202, 203, 204, 205, 206, 207, 209, 210, 211, 212, 213, 214, 216, 217, 218, 219, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 220, - 221, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, - 238, 239, 241, 242, 243, 244, 245, 246, 248, 249, 250, 251, 252, 253, 255, 0x104, - 0x105, 0x106, 0x107, 0x10C, 0x10D, 0x10E, 0x10F, 0x118, 0x119, 0x11A, 0x11B, 0x11E, 0x11F, 0x130, 0x131, 0x141, - 0x142, 0x143, 0x144, 0x147, 0x148, 0x150, 0x151, 0x152, 0x153, 0x158, 0x159, 0x15A, 0x15B, 0x15E, 0x15F, 0x160, - 0x161, 0x164, 0x165, 0x16E, 0x16F, 0x170, 0x171, 0x178, 0x179, 0x17A, 0x17B, 0x17C, 0x17D, 0x17E, 0x404, 0x411, - 0x413, 0x414, 0x416, 0x417, 0x418, 0x419, 0x41B, 0x41F, 0x423, 0x424, 0x426, 0x427, 0x428, 0x429, 0x42A, 0x42B, - 0x42C, 0x42D, 0x42E, 0x42F, 0x431, 0x432, 0x433, 0x434, 0x436, 0x437, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, - 0x43F, 0x442, 0x444, 0x446, 0x447, 0x448, 0x449, 0x44A, 0x44B, 0x44C, 0x44D, 0x44E, 0x44F, 0x454, 0x490, 0x491 -}; -extern const int uni2cubeoffsets[8] = -{ - 0, 256, 658, 658, 512, 658, 658, 658 -}; -extern const uchar uni2cubechars[878] = -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 10, 11, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 2, 3, 4, 5, 6, 7, 8, 14, 15, 16, 17, 18, 19, 20, 21, 0, 22, 23, 24, 25, 26, 27, 0, 28, 29, 30, 31, 127, 128, 0, 129, - 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 0, 146, 147, 148, 149, 150, 151, 0, 152, 153, 154, 155, 156, 157, 0, 158, - 0, 0, 0, 0, 159, 160, 161, 162, 0, 0, 0, 0, 163, 164, 165, 166, 0, 0, 0, 0, 0, 0, 0, 0, 167, 168, 169, 170, 0, 0, 171, 172, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 173, 174, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 175, 176, 177, 178, 0, 0, 179, 180, 0, 0, 0, 0, 0, 0, 0, 181, 182, 183, 184, 0, 0, 0, 0, 185, 186, 187, 188, 0, 0, 189, 190, - 191, 192, 0, 0, 193, 194, 0, 0, 0, 0, 0, 0, 0, 0, 195, 196, 197, 198, 0, 0, 0, 0, 0, 0, 199, 200, 201, 202, 203, 204, 205, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 17, 0, 0, 206, 83, 73, 21, 74, 0, 0, 0, 0, 0, 0, 0, 65, 207, 66, 208, 209, 69, 210, 211, 212, 213, 75, 214, 77, 72, 79, 215, - 80, 67, 84, 216, 217, 88, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 97, 228, 229, 230, 231, 101, 232, 233, 234, 235, 236, 237, 238, 239, 111, 240, - 112, 99, 241, 121, 242, 120, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 0, 141, 0, 0, 253, 115, 105, 145, 106, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; -extern const uchar cubelowerchars[256] = -{ - 0, 130, 131, 132, 133, 134, 135, 136, 137, 9, 10, 11, 12, 13, 138, 139, - 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 156, - 157, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, - 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 160, - 160, 162, 162, 164, 164, 166, 166, 168, 168, 170, 170, 172, 172, 105, 174, 176, - 176, 178, 178, 180, 180, 182, 182, 184, 184, 186, 186, 188, 188, 190, 190, 192, - 192, 194, 194, 196, 196, 198, 198, 158, 201, 201, 203, 203, 205, 205, 206, 207, - 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, - 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, - 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 -}; -extern const uchar cubeupperchars[256] = -{ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, - 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, - 128, 129, 1, 2, 3, 4, 5, 6, 7, 8, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 127, 128, 199, 159, - 159, 161, 161, 163, 163, 165, 165, 167, 167, 169, 169, 171, 171, 173, 73, 175, - 175, 177, 177, 179, 179, 181, 181, 183, 183, 185, 185, 187, 187, 189, 189, 191, - 191, 193, 193, 195, 195, 197, 197, 199, 200, 200, 202, 202, 204, 204, 206, 207, - 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, - 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, - 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 -}; - -size_t decodeutf8(uchar *dstbuf, size_t dstlen, const uchar *srcbuf, size_t srclen, size_t *carry) -{ - uchar *dst = dstbuf, *dstend = &dstbuf[dstlen]; - const uchar *src = srcbuf, *srcend = &srcbuf[srclen]; - if(dstbuf == srcbuf) - { - int len = min(dstlen, srclen); - for(const uchar *end4 = &srcbuf[len&~3]; src < end4; src += 4) if(*(const int *)src & 0x80808080) goto decode; - for(const uchar *end = &srcbuf[len]; src < end; src++) if(*src & 0x80) goto decode; - if(carry) *carry += len; - return len; - } - -decode: - dst += src - srcbuf; - while(src < srcend && dst < dstend) - { - int c = *src++; - if(c < 0x80) *dst++ = c; - else if(c >= 0xC0) - { - int uni; - if(c >= 0xE0) - { - if(c >= 0xF0) - { - if(c >= 0xF8) - { - if(c >= 0xFC) - { - if(c >= 0xFE) continue; - uni = c&1; if(srcend - src < 5) break; - c = *src; if((c&0xC0) != 0x80) continue; src++; uni = (uni<<6) | (c&0x3F); - } - else { uni = c&3; if(srcend - src < 4) break; } - c = *src; if((c&0xC0) != 0x80) continue; src++; uni = (uni<<6) | (c&0x3F); - } - else { uni = c&7; if(srcend - src < 3) break; } - c = *src; if((c&0xC0) != 0x80) continue; src++; uni = (uni<<6) | (c&0x3F); - } - else { uni = c&0xF; if(srcend - src < 2) break; } - c = *src; if((c&0xC0) != 0x80) continue; src++; uni = (uni<<6) | (c&0x3F); - } - else { uni = c&0x1F; if(srcend - src < 1) break; } - c = *src; if((c&0xC0) != 0x80) continue; src++; uni = (uni<<6) | (c&0x3F); - c = uni2cube(uni); - if(!c) continue; - *dst++ = c; - } - } - if(carry) *carry += src - srcbuf; - return dst - dstbuf; -} - -size_t encodeutf8(uchar *dstbuf, size_t dstlen, const uchar *srcbuf, size_t srclen, size_t *carry) -{ - uchar *dst = dstbuf, *dstend = &dstbuf[dstlen]; - const uchar *src = srcbuf, *srcend = &srcbuf[srclen]; - if(src < srcend && dst < dstend) do - { - int uni = cube2uni(*src); - if(uni <= 0x7F) - { - if(dst >= dstend) goto done; - const uchar *end = min(srcend, &src[dstend-dst]); - do - { - if(uni == '\f') - { - if(++src >= srcend) goto done; - goto uni1; - } - *dst++ = uni; - if(++src >= end) goto done; - uni = cube2uni(*src); - } - while(uni <= 0x7F); - } - if(uni <= 0x7FF) { if(dst + 2 > dstend) goto done; *dst++ = 0xC0 | (uni>>6); goto uni2; } - else if(uni <= 0xFFFF) { if(dst + 3 > dstend) goto done; *dst++ = 0xE0 | (uni>>12); goto uni3; } - else if(uni <= 0x1FFFFF) { if(dst + 4 > dstend) goto done; *dst++ = 0xF0 | (uni>>18); goto uni4; } - else if(uni <= 0x3FFFFFF) { if(dst + 5 > dstend) goto done; *dst++ = 0xF8 | (uni>>24); goto uni5; } - else if(uni <= 0x7FFFFFFF) { if(dst + 6 > dstend) goto done; *dst++ = 0xFC | (uni>>30); goto uni6; } - else goto uni1; - uni6: *dst++ = 0x80 | ((uni>>24)&0x3F); - uni5: *dst++ = 0x80 | ((uni>>18)&0x3F); - uni4: *dst++ = 0x80 | ((uni>>12)&0x3F); - uni3: *dst++ = 0x80 | ((uni>>6)&0x3F); - uni2: *dst++ = 0x80 | (uni&0x3F); - uni1:; - } - while(++src < srcend); - -done: - if(carry) *carry += src - srcbuf; - return dst - dstbuf; + if(c >= 'A' && c <= 'Z') return 'a' + (c - 'A'); + return c; } int cubecasecmp(const char *s1, const char *s2, int n) @@ -252,7 +39,7 @@ int cubecasecmp(const char *s1, const char *s2, int n) if(!s1 || !s2) return !s2 - !s1; while(n-- > 0) { - int c1 = cubelower(*s1++), c2 = cubelower(*s2++); + int c1 = uni_charlower(*s1++), c2 = uni_charlower(*s2++); if(c1 != c2) return c1 - c2; if(!c1) break; } @@ -263,7 +50,7 @@ char *cubecasefind(const char *haystack, const char *needle) { if(haystack && needle) for(const char *h = haystack, *n = needle;;) { - int hc = cubelower(*h++), nc = cubelower(*n++); + int hc = uni_charlower(*h++), nc = uni_charlower(*n++); if(!nc) return (char*)h - (n - needle); if(hc != nc) { @@ -1057,178 +844,6 @@ struct gzstream : stream } }; -struct utf8stream : stream -{ - enum - { - BUFSIZE = 4096 - }; - stream *file; - offset pos; - size_t bufread, bufcarry, buflen; - bool reading, writing, autoclose; - uchar buf[BUFSIZE]; - - utf8stream() : file(NULL), pos(0), bufread(0), bufcarry(0), buflen(0), reading(false), writing(false), autoclose(false) - { - } - - ~utf8stream() - { - close(); - } - - bool readbuf(size_t size = BUFSIZE) - { - if(bufread >= bufcarry) { if(bufcarry > 0 && bufcarry < buflen) memmove(buf, &buf[bufcarry], buflen - bufcarry); buflen -= bufcarry; bufread = bufcarry = 0; } - size_t n = file->read(&buf[buflen], min(size, BUFSIZE - buflen)); - if(n <= 0) return false; - buflen += n; - size_t carry = bufcarry; - bufcarry += decodeutf8(&buf[bufcarry], BUFSIZE-bufcarry, &buf[bufcarry], buflen-bufcarry, &carry); - if(carry > bufcarry && carry < buflen) { memmove(&buf[bufcarry], &buf[carry], buflen - carry); buflen -= carry - bufcarry; } - return true; - } - - bool checkheader() - { - size_t n = file->read(buf, 3); - if(n == 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF) return true; - buflen = n; - return false; - } - - bool open(stream *f, const char *mode, bool needclose) - { - if(file) return false; - for(; *mode; mode++) - { - if(*mode=='r') { reading = true; break; } - else if(*mode=='w') { writing = true; break; } - } - if(!reading && !writing) return false; - - file = f; - - if(reading) checkheader(); - - autoclose = needclose; - return true; - } - - void finishreading() - { - if(!reading) return; - } - - void stopreading() - { - if(!reading) return; - reading = false; - } - - void stopwriting() - { - if(!writing) return; - writing = false; - } - - void close() - { - stopreading(); - stopwriting(); - if(autoclose) DELETEP(file); - } - - bool end() { return !reading && !writing; } - offset tell() { return reading || writing ? pos : offset(-1); } - - bool seek(offset off, int whence) - { - if(writing || !reading) return false; - - if(whence == SEEK_END) - { - uchar skip[512]; - while(read(skip, sizeof(skip)) == sizeof(skip)); - return !off; - } - else if(whence == SEEK_CUR) off += pos; - - if(off >= pos) off -= pos; - else if(off < 0 || !file->seek(0, SEEK_SET)) return false; - else - { - bufread = bufcarry = buflen = 0; - pos = 0; - checkheader(); - } - - uchar skip[512]; - while(off > 0) - { - size_t skipped = (size_t)min(off, (offset)sizeof(skip)); - if(read(skip, skipped) != skipped) { stopreading(); return false; } - off -= skipped; - } - - return true; - } - - size_t read(void *dst, size_t len) - { - if(!reading || !dst || !len) return 0; - size_t next = 0; - while(next < len) - { - if(bufread >= bufcarry) { if(readbuf(BUFSIZE)) continue; stopreading(); break; } - size_t n = min(len - next, bufcarry - bufread); - memcpy(&((uchar *)dst)[next], &buf[bufread], n); - next += n; - bufread += n; - } - pos += next; - return next; - } - - bool getline(char *dst, size_t len) - { - if(!reading || !dst || !len) return false; - --len; - size_t next = 0; - while(next < len) - { - if(bufread >= bufcarry) { if(readbuf(BUFSIZE)) continue; stopreading(); if(!next) return false; break; } - size_t n = min(len - next, bufcarry - bufread); - uchar *endline = (uchar *)memchr(&buf[bufread], '\n', n); - if(endline) { n = endline+1 - &buf[bufread]; len = next + n; } - memcpy(&((uchar *)dst)[next], &buf[bufread], n); - next += n; - bufread += n; - } - dst[next] = '\0'; - pos += next; - return true; - } - - size_t write(const void *src, size_t len) - { - if(!writing || !src || !len) return 0; - uchar dst[512]; - size_t next = 0; - while(next < len) - { - size_t carry = 0, n = encodeutf8(dst, sizeof(dst), &((uchar *)src)[next], len - next, &carry); - if(n > 0 && file->write(dst, n) != n) { stopwriting(); break; } - next += carry; - } - pos += next; - return next; - } - - bool flush() { return file->flush(); } -}; - stream *openrawfile(const char *filename, const char *mode) { const char *found = findfile(filename, mode); @@ -1264,16 +879,7 @@ stream *opengzfile(const char *filename, const char *mode, stream *file, int lev return gz; } -stream *openutf8file(const char *filename, const char *mode, stream *file) -{ - stream *source = file ? file : openfile(filename, mode); - if(!source) return NULL; - utf8stream *utf8 = new utf8stream; - if(!utf8->open(source, mode, !file)) { if(!file) delete source; delete utf8; return NULL; } - return utf8; -} - -char *loadfile(const char *fn, size_t *size, bool utf8) +char *loadfile(const char *fn, size_t *size) { stream *f = openfile(fn, "rb"); if(!f) return NULL; @@ -1283,16 +889,9 @@ char *loadfile(const char *fn, size_t *size, bool utf8) char *buf = new (false) char[len+1]; if(!buf) { delete f; return NULL; } size_t offset = 0; - if(utf8 && len >= 3) - { - if(f->read(buf, 3) != 3) { delete f; delete[] buf; return NULL; } - if(((uchar *)buf)[0] == 0xEF && ((uchar *)buf)[1] == 0xBB && ((uchar *)buf)[2] == 0xBF) len -= 3; - else offset += 3; - } size_t rlen = f->read(&buf[offset], len-offset); delete f; if(rlen != len-offset) { delete[] buf; return NULL; } - if(utf8) len = decodeutf8((uchar *)buf, len, (uchar *)buf, len); buf[len] = '\0'; if(size!=NULL) *size = len; return buf; diff --git a/source/shared/tessfont.c b/source/shared/tessfont.c deleted file mode 100644 index 9934a9399..000000000 --- a/source/shared/tessfont.c +++ /dev/null @@ -1,764 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include FT_FREETYPE_H -#include FT_STROKER_H -#include FT_GLYPH_H - -typedef unsigned char uchar; -typedef unsigned short ushort; -typedef unsigned int uint; - -static inline int imin(int a, int b) { return a < b ? a : b; } -static inline int imax(int a, int b) { return a > b ? a : b; } -static inline int iclamp(int n, int l, int h) { return imax(l, imin(n, h)); } -static inline float fclamp(float n, float l, float h) { return fmax(l, fmin(n, h)); } - -void fatal(const char *fmt, ...) -{ - va_list v; - va_start(v, fmt); - vfprintf(stderr, fmt, v); - va_end(v); - fputc('\n', stderr); - - exit(EXIT_FAILURE); -} - -uint bigswap(uint n) -{ - const int islittleendian = 1; - return *(const uchar *)&islittleendian ? (n<<24) | (n>>24) | ((n>>8)&0xFF00) | ((n<<8)&0xFF0000) : n; -} - -size_t writebig(FILE *f, uint n) -{ - n = bigswap(n); - return fwrite(&n, 1, sizeof(n), f); -} - -void writepngchunk(FILE *f, const char *type, uchar *data, uint len) -{ - uint crc; - writebig(f, len); - fwrite(type, 1, 4, f); - fwrite(data, 1, len, f); - - crc = crc32(0, Z_NULL, 0); - crc = crc32(crc, (const Bytef *)type, 4); - if(data) crc = crc32(crc, data, len); - writebig(f, crc); -} - -struct pngihdr -{ - uint width, height; - uchar bitdepth, colortype, compress, filter, interlace; -}; - -void savepng(const char *filename, uchar *data, int w, int h, int bpp, int flip) -{ - const uchar signature[] = { 137, 80, 78, 71, 13, 10, 26, 10 }; - struct pngihdr ihdr; - FILE *f; - long idat; - uint len, crc; - z_stream z; - uchar buf[1<<12]; - int i, j; - - memset(&ihdr, 0, sizeof(ihdr)); - ihdr.width = bigswap(w); - ihdr.height = bigswap(h); - ihdr.bitdepth = 8; - switch(bpp) - { - case 1: ihdr.colortype = 0; break; - case 2: ihdr.colortype = 4; break; - case 3: ihdr.colortype = 2; break; - case 4: ihdr.colortype = 6; break; - default: fatal("tessfont: invalid PNG bpp"); return; - } - f = fopen(filename, "wb"); - if(!f) { fatal("tessfont: could not write to %s", filename); return; } - - fwrite(signature, 1, sizeof(signature), f); - - writepngchunk(f, "IHDR", (uchar *)&ihdr, 13); - - idat = ftell(f); - len = 0; - fwrite("\0\0\0\0IDAT", 1, 8, f); - crc = crc32(0, Z_NULL, 0); - crc = crc32(crc, (const Bytef *)"IDAT", 4); - - z.zalloc = NULL; - z.zfree = NULL; - z.opaque = NULL; - - if(deflateInit(&z, Z_BEST_COMPRESSION) != Z_OK) - goto error; - - z.next_out = (Bytef *)buf; - z.avail_out = sizeof(buf); - - for(i = 0; i < h; i++) - { - uchar filter = 0; - for(j = 0; j < 2; j++) - { - z.next_in = j ? (Bytef *)data + (flip ? h-i-1 : i)*w*bpp : (Bytef *)&filter; - z.avail_in = j ? w*bpp : 1; - while(z.avail_in > 0) - { - if(deflate(&z, Z_NO_FLUSH) != Z_OK) goto cleanuperror; - #define FLUSHZ do { \ - int flush = sizeof(buf) - z.avail_out; \ - crc = crc32(crc, buf, flush); \ - len += flush; \ - fwrite(buf, 1, flush, f); \ - z.next_out = (Bytef *)buf; \ - z.avail_out = sizeof(buf); \ - } while(0) - FLUSHZ; - } - } - } - - for(;;) - { - int err = deflate(&z, Z_FINISH); - if(err != Z_OK && err != Z_STREAM_END) goto cleanuperror; - FLUSHZ; - if(err == Z_STREAM_END) break; - } - - deflateEnd(&z); - - fseek(f, idat, SEEK_SET); - writebig(f, len); - fseek(f, 0, SEEK_END); - writebig(f, crc); - - writepngchunk(f, "IEND", NULL, 0); - - fclose(f); - return; - -cleanuperror: - deflateEnd(&z); - -error: - fclose(f); - - fatal("tessfont: failed saving PNG to %s", filename); -} - -enum -{ - CT_PRINT = 1<<0, - CT_SPACE = 1<<1, - CT_DIGIT = 1<<2, - CT_ALPHA = 1<<3, - CT_LOWER = 1<<4, - CT_UPPER = 1<<5, - CT_UNICODE = 1<<6 -}; -#define CUBECTYPE(s, p, d, a, A, u, U) \ - 0, U, U, U, U, U, U, U, U, s, s, s, s, s, U, U, \ - U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, \ - s, p, p, p, p, p, p, p, p, p, p, p, p, p, p, p, \ - d, d, d, d, d, d, d, d, d, d, p, p, p, p, p, p, \ - p, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, \ - A, A, A, A, A, A, A, A, A, A, A, p, p, p, p, p, \ - p, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, \ - a, a, a, a, a, a, a, a, a, a, a, p, p, p, p, U, \ - U, u, u, u, u, u, u, u, u, u, u, u, u, u, u, u, \ - u, u, u, u, u, u, u, u, u, u, u, u, u, u, u, U, \ - u, U, u, U, u, U, u, U, u, U, u, U, u, U, u, U, \ - u, U, u, U, u, U, u, U, u, U, u, U, u, U, u, U, \ - u, U, u, U, u, U, u, U, U, u, U, u, U, u, U, U, \ - U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, \ - U, U, U, U, u, u, u, u, u, u, u, u, u, u, u, u, \ - u, u, u, u, u, u, u, u, u, u, u, u, u, u, U, u -const uchar cubectype[256] = -{ - CUBECTYPE(CT_SPACE, - CT_PRINT, - CT_PRINT|CT_DIGIT, - CT_PRINT|CT_ALPHA|CT_LOWER, - CT_PRINT|CT_ALPHA|CT_UPPER, - CT_PRINT|CT_UNICODE|CT_ALPHA|CT_LOWER, - CT_PRINT|CT_UNICODE|CT_ALPHA|CT_UPPER) -}; -int iscubeprint(uchar c) { return cubectype[c]&CT_PRINT; } -int iscubespace(uchar c) { return cubectype[c]&CT_SPACE; } -int iscubealpha(uchar c) { return cubectype[c]&CT_ALPHA; } -int iscubealnum(uchar c) { return cubectype[c]&(CT_ALPHA|CT_DIGIT); } -int iscubelower(uchar c) { return cubectype[c]&CT_LOWER; } -int iscubeupper(uchar c) { return cubectype[c]&CT_UPPER; } -const int cube2unichars[256] = -{ - 0, 192, 193, 194, 195, 196, 197, 198, 199, 9, 10, 11, 12, 13, 200, 201, - 202, 203, 204, 205, 206, 207, 209, 210, 211, 212, 213, 214, 216, 217, 218, 219, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 220, - 221, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, - 238, 239, 241, 242, 243, 244, 245, 246, 248, 249, 250, 251, 252, 253, 255, 0x104, - 0x105, 0x106, 0x107, 0x10C, 0x10D, 0x10E, 0x10F, 0x118, 0x119, 0x11A, 0x11B, 0x11E, 0x11F, 0x130, 0x131, 0x141, - 0x142, 0x143, 0x144, 0x147, 0x148, 0x150, 0x151, 0x152, 0x153, 0x158, 0x159, 0x15A, 0x15B, 0x15E, 0x15F, 0x160, - 0x161, 0x164, 0x165, 0x16E, 0x16F, 0x170, 0x171, 0x178, 0x179, 0x17A, 0x17B, 0x17C, 0x17D, 0x17E, 0x404, 0x411, - 0x413, 0x414, 0x416, 0x417, 0x418, 0x419, 0x41B, 0x41F, 0x423, 0x424, 0x426, 0x427, 0x428, 0x429, 0x42A, 0x42B, - 0x42C, 0x42D, 0x42E, 0x42F, 0x431, 0x432, 0x433, 0x434, 0x436, 0x437, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, - 0x43F, 0x442, 0x444, 0x446, 0x447, 0x448, 0x449, 0x44A, 0x44B, 0x44C, 0x44D, 0x44E, 0x44F, 0x454, 0x490, 0x491 -}; -int cube2uni(uchar c) -{ - return cube2unichars[c]; -} - -const char *encodeutf8(int uni) -{ - static char buf[7]; - char *dst = buf; - if(uni <= 0x7F) { *dst++ = uni; goto uni1; } - else if(uni <= 0x7FF) { *dst++ = 0xC0 | (uni>>6); goto uni2; } - else if(uni <= 0xFFFF) { *dst++ = 0xE0 | (uni>>12); goto uni3; } - else if(uni <= 0x1FFFFF) { *dst++ = 0xF0 | (uni>>18); goto uni4; } - else if(uni <= 0x3FFFFFF) { *dst++ = 0xF8 | (uni>>24); goto uni5; } - else if(uni <= 0x7FFFFFFF) { *dst++ = 0xFC | (uni>>30); goto uni6; } - else goto uni1; -uni6: *dst++ = 0x80 | ((uni>>24)&0x3F); -uni5: *dst++ = 0x80 | ((uni>>18)&0x3F); -uni4: *dst++ = 0x80 | ((uni>>12)&0x3F); -uni3: *dst++ = 0x80 | ((uni>>6)&0x3F); -uni2: *dst++ = 0x80 | (uni&0x3F); -uni1: *dst++ = '\0'; - return buf; -} - -struct fontchar { int code, uni, tex, x, y, sdfradius, sdfpitch, sdfx, sdfy, sdfw, sdfh; float w, h, left, top, advance; FT_BitmapGlyph glyph; uchar *sdf; }; - -const char *texdir = ""; - -const char *texfilename(const char *name, int texnum) -{ - static char file[256]; - snprintf(file, sizeof(file), "%s%d.png", name, texnum); - return file; -} - -const char *texname(const char *name, int texnum) -{ - static char file[512]; - snprintf(file, sizeof(file), "%s%s", texdir, texfilename(name, texnum)); - return file; -} - -static int sdist; -static inline void searchdist(int x, int y, int cx, int cy) -{ - int dx = cx - x, dy = cy - y, dist = dx*dx + dy*dy; - if(dist < sdist) sdist = dist; -} - -static int sx1, ex1; -static inline int search1(int x, int y, int w, int radius, int cy, uchar *src) -{ - int cx = imin(ex1, x)-1; - if(cx >= sx1) - { - uchar *bsrc = &src[cx>>3]; - int bx = cx&~7, bits = *bsrc; - if(bits==0xFF) cx = bx-1; - else - { - bits >>= 7-(cx&7); - if(bx <= sx1) - { - for(; cx >= sx1; cx--, bits >>= 1) if(!(bits&1)) { sx1 = cx+1; searchdist(x, y, cx, cy); goto foundbelow1; } - goto foundbelow1; - } - else for(; cx >= bx; cx--, bits >>= 1) if(!(bits&1)) { sx1 = cx+1; searchdist(x, y, cx, cy); goto foundbelow1; } - } - while(cx >= sx1) - { - bits = *--bsrc; - if(bits==0xFF) cx -= 8; - else if((bx = cx - 7) <= sx1) - { - for(; cx >= sx1; cx--, bits >>= 1) if(!(bits&1)) { sx1 = cx+1; searchdist(x, y, cx, cy); goto foundbelow1; } - goto foundbelow1; - } - else for(; cx >= bx; cx--, bits >>= 1) if(!(bits&1)) { sx1 = cx+1; searchdist(x, y, cx, cy); goto foundbelow1; } - - } - } -foundbelow1: - cx = imax(sx1, x); - if(cx < ex1) - { - uchar *bsrc = &src[cx>>3]; - int bx = (cx&~7) + 8, bits = *bsrc; - if(bits==0xFF) cx = bx; - else - { - bits <<= cx&7; - if(bx >= ex1) - { - for(; cx < ex1; cx++, bits <<= 1) if(!(bits&0x80)) { ex1 = cx-1; searchdist(x, y, cx, cy); goto foundabove1; } - goto foundabove1; - } - else for(; cx < bx; cx++, bits <<= 1) if(!(bits&0x80)) { ex1 = cx-1; searchdist(x, y, cx, cy); goto foundabove1; } - } - while(cx < ex1) - { - bits = *++bsrc; - if(bits==0xFF) cx += 8; - else if((bx = cx + 8) >= ex1) - { - for(; cx < ex1; cx++, bits <<= 1) if(!(bits&0x80)) { ex1 = cx-1; searchdist(x, y, cx, cy); goto foundabove1; } - goto foundabove1; - } - else for(; cx < bx; cx++, bits <<= 1) if(!(bits&0x80)) { ex1 = cx-1; searchdist(x, y, cx, cy); goto foundabove1; } - } - } -foundabove1: - if(x - radius < 0) searchdist(x, y, -1, cy); - if(x + radius > w) searchdist(x, y, w, cy); - return sx1 < ex1; -} - -static int sx0, ex0; -static inline int search0(int x, int y, int w, int radius, int cy, uchar *src) -{ - int cx = imin(ex0, x)-1; - if(cx >= sx0) - { - uchar *bsrc = &src[cx>>3]; - int bx = cx&~7, bits = *bsrc; - if(!bits) cx = bx-1; - else - { - bits >>= 7-(cx&7); - if(bx <= sx0) - { - for(; cx >= sx0; cx--, bits >>= 1) if(bits&1) { sx0 = cx+1; searchdist(x, y, cx, cy); goto foundbelow0; } - goto foundbelow0; - } - else for(; cx >= bx; cx--, bits >>= 1) if(bits&1) { sx0 = cx+1; searchdist(x, y, cx, cy); goto foundbelow0; } - } - while(cx >= sx0) - { - bits = *--bsrc; - if(!bits) cx -= 8; - else if((bx = cx - 7) <= sx0) - { - for(; cx >= sx0; cx--, bits >>= 1) if(bits&1) { sx0 = cx+1; searchdist(x, y, cx, cy); goto foundbelow0; } - goto foundbelow0; - } - else for(; cx >= bx; cx--, bits >>= 1) if(bits&1) { sx0 = cx+1; searchdist(x, y, cx, cy); goto foundbelow0; } - } - } -foundbelow0: - cx = imax(sx0, x); - if(cx < ex0) - { - uchar *bsrc = &src[cx>>3]; - int bx = (cx&~7) + 8, bits = *bsrc; - if(!bits) cx = bx; - else - { - bits <<= cx&7; - if(bx >= ex0) - { - for(; cx < ex0; cx++, bits <<= 1) if(bits&0x80) { ex0 = cx-1; searchdist(x, y, cx, cy); goto foundabove0; } - goto foundabove0; - } - else for(; cx < bx; cx++, bits <<= 1) if(bits&0x80) { ex0 = cx-1; searchdist(x, y, cx, cy); goto foundabove0; } - } - while(cx < ex0) - { - bits = *++bsrc; - if(!bits) cx += 8; - else if((bx = cx + 8) >= ex0) - { - for(; cx < ex0; cx++, bits <<= 1) if(bits&0x80) { ex0 = cx-1; searchdist(x, y, cx, cy); goto foundabove0; } - goto foundabove0; - } - else for(; cx < bx; cx++, bits <<= 1) if(bits&0x80) { ex0 = cx-1; searchdist(x, y, cx, cy); goto foundabove0; } - } - } -foundabove0: - return sx0 < ex0; -} - -#define SUPERSAMPLE_MIN 1 -#define SUPERSAMPLE_MAX 32 -static int supersample = SUPERSAMPLE_MIN; - -void gensdf(struct fontchar *c) -{ - int w = c->glyph->bitmap.width, h = c->glyph->bitmap.rows, radius = c->sdfradius*supersample; - int dx, dy, dw = (w + 2*radius + supersample-1)/supersample, dh = (h + 2*radius + supersample-1)/supersample; - int x, y, x1 = INT_MAX, y1 = INT_MAX, x2 = INT_MIN, y2 = INT_MIN; - uchar *dst = (uchar *)malloc(dw*dh), *src; - if(!dst) fatal("tessfont: failed allocating signed distance field"); - c->sdfpitch = dw; - c->sdf = dst; - for(dy = 0; dy < dh; dy++) - for(dx = 0; dx < dw; dx++) - { - double total = 0; - for(y = dy*supersample - radius; y < (dy+1)*supersample - radius; y++) - for(x = dx*supersample - radius; x < (dx+1)*supersample - radius; x++) - { - int sx = imax(x - radius, 0), sy = imax(y - radius, 0), ex = imin(x + radius, w), ey = imin(y + radius, h), cy, val = 0; - if(y >= 0 && y < h && x >= 0 && x < w) - { - uchar *center = (uchar *)c->glyph->bitmap.buffer + y*c->glyph->bitmap.pitch; - val = (center[x>>3]<<(x&7))&0x80; - } - sdist = INT_MAX; - if(val) - { - for(cy = imin(ey, y)-1, sx1 = sx, ex1 = ex, src = (uchar *)c->glyph->bitmap.buffer + cy*c->glyph->bitmap.pitch; - cy >= sy && search1(x, y, w, radius, cy, src); - cy--, src -= c->glyph->bitmap.pitch); - for(cy = imax(sy, y), sx1 = sx, ex1 = ex, src = (uchar *)c->glyph->bitmap.buffer + cy*c->glyph->bitmap.pitch; - cy < ey && search1(x, y, w, radius, cy, src); - cy++, src += c->glyph->bitmap.pitch); - if(y - radius < 0) searchdist(x, y, x, -1); - if(y + radius > h) searchdist(x, y, x, h); - } - else - { - for(cy = imin(ey, y)-1, sx0 = sx, ex0 = ex, src = (uchar *)c->glyph->bitmap.buffer + cy*c->glyph->bitmap.pitch; - cy >= sy && search0(x, y, w, radius, cy, src); - cy--, src -= c->glyph->bitmap.pitch); - for(cy = imax(sy, y), sx0 = sx, ex0 = ex, src = (uchar *)c->glyph->bitmap.buffer + cy*c->glyph->bitmap.pitch; - cy < ey && search0(x, y, w, radius, cy, src); - cy++, src += c->glyph->bitmap.pitch); - } - if(val) total += sqrt(sdist); - else total -= sqrt(sdist); - } - *dst = (uchar)iclamp((int)round(127.5 + (127.5/(supersample*supersample))*total/radius), 0, 255); - if(*dst) - { - x1 = imin(x1, dx); - y1 = imin(y1, dy); - x2 = imax(x2, dx); - y2 = imax(y2, dy); - } - dst++; - } - if(x1 <= x2 && y1 <= y2) - { - c->sdfx = x1; - c->sdfy = y1; - c->sdfw = x2 - x1 + 1; - c->sdfh = y2 - y1 + 1; - } -} - -void writetexs(const char *name, struct fontchar *chars, int numchars, int numtexs, int tw, int th) -{ - int tex; - uchar *pixels = (uchar *)malloc(tw*th); - if(!pixels) fatal("tessfont: failed allocating textures"); - for(tex = 0; tex < numtexs; tex++) - { - const char *file = texfilename(name, tex); - int texchars = 0, i; - uchar *dst, *src; - memset(pixels, 0, tw*th); - for(i = 0; i < numchars; i++) - { - struct fontchar *c = &chars[i]; - int y; - if(c->tex != tex) continue; - texchars++; - dst = &pixels[c->y*tw + c->x]; - src = c->sdf + c->sdfy*c->sdfpitch + c->sdfx; - for(y = 0; y < c->sdfh; y++) - { - memcpy(dst, src, c->sdfw); - dst += tw; - src += c->sdfpitch; - } - } - printf("tessfont: writing %d chars to %s\n", texchars, file); - savepng(file, pixels, tw, th, 1, 0); - } - free(pixels); -} - -static float offsetx = 0, offsety = 0, border = 0, border2 = 0, outline = 0, outline2 = 0; -static int scale = 0; - -void writecfg(const char *name, struct fontchar *chars, int numchars, float x1, float y1, float x2, float y2, int sw, int sh, int argc, char **argv) -{ - FILE *f; - char file[256]; - int i, lastcode = 0, lasttex = 0; - snprintf(file, sizeof(file), "%s.cfg", name); - f = fopen(file, "w"); - if(!f) fatal("tessfont: failed writing %s", file); - printf("tessfont: writing %d chars to %s\n", numchars, file); - fprintf(f, "//"); - for(i = 1; i < argc; i++) - fprintf(f, " %s", argv[i]); - fprintf(f, "\n"); - fprintf(f, "font \"%s\" \"%s\" %d %d\n", name, texname(name, 0), sw, sh); - if(scale > 0) fprintf(f, "fontscale %d\n", scale); - if(border2) fprintf(f, "fontborder %g %g\n", border, border2); - else if(border) fprintf(f, "fontborder %g\n", border); - if(outline2) fprintf(f, "fontoutline %g %g\n", outline, outline2); - else if(outline) fprintf(f, "fontoutline %g\n", outline); - for(i = 0; i < numchars; i++) - { - struct fontchar *c = &chars[i]; - if(!lastcode && lastcode < c->code) - { - fprintf(f, "fontoffset \"%s\"\n", encodeutf8(c->uni)); - lastcode = c->code; - } - else if(lastcode < c->code) - { - if(lastcode + 1 == c->code) - fprintf(f, "fontskip // %d\n", lastcode); - else - fprintf(f, "fontskip %d // %d .. %d\n", c->code - lastcode, lastcode, c->code-1); - lastcode = c->code; - } - if(lasttex != c->tex) - { - fprintf(f, "\nfonttex \"%s\"\n", texname(name, c->tex)); - lasttex = c->tex; - } - float offx = c->sdfx-c->sdfradius + c->left + offsetx, offy = c->sdfy-c->sdfradius + y2-c->top + offsety; - if(c->code != c->uni) - fprintf(f, "fontchar %d %d %d %d %g %g %g // %s (%d -> 0x%X)\n", c->x, c->y, c->sdfw, c->sdfh, offx, offy, c->advance, encodeutf8(c->uni), c->code, c->uni); - else - fprintf(f, "fontchar %d %d %d %d %g %g %g // %s (%d)\n", c->x, c->y, c->sdfw, c->sdfh, offx, offy, c->advance, encodeutf8(c->uni), c->code); - lastcode++; - } - fclose(f); -} - -int groupchar(int c) -{ - switch(c) - { - case 0x152: case 0x153: case 0x178: return 1; - } - if(c < 127 || c >= 0x2000) return 0; - if(c < 0x100) return 1; - if(c < 0x400) return 2; - return 3; -} - -int sortchars(const void *x, const void *y) -{ - const struct fontchar *xc = *(const struct fontchar **)x, *yc = *(const struct fontchar **)y; - int xg = groupchar(xc->uni), yg = groupchar(yc->uni); - if(xg < yg) return -1; - if(xg > yg) return 1; - if(xc->sdfh != yc->sdfh) return yc->sdfh - xc->sdfh; - if(xc->sdfw != yc->sdfw) return yc->sdfw - xc->sdfw; - return yc->uni - xc->uni; -} - -int scorechar(struct fontchar *f, int pad, int tw, int th, int rw, int rh, int ry) -{ - int score = 0; - if(rw + f->sdfw > tw) { ry += rh + pad; score = 1; } - if(ry + f->sdfh > th) score = 2; - return score; -} - -int main(int argc, char **argv) -{ - FT_Library l; - FT_Face f; - int i, radius, pad, w, h, tw, th, c, numgen = 0, trial = -2, rw = 0, rh = 0, ry = 0, sw = 0, sh = 0; - float advance, x1 = INT_MAX, x2 = INT_MIN, y1 = INT_MAX, y2 = INT_MIN, w2 = 0, h2 = 0; - time_t starttime, endtime; - struct fontchar chars[256]; - struct fontchar *order[256]; - int numchars = 0, numtex = 0; - if(argc < 13) - fatal("Usage: tessfont infile outfile supersample border[:border2[:outline:outline2]] radius pad offsetx[:offsety] advance charwidth charheight texwidth texheight [spacewidth spaceheight scale texdir]"); - supersample = iclamp(atoi(argv[3]), SUPERSAMPLE_MIN, SUPERSAMPLE_MAX); - sscanf(argv[4], "%f:%f:%f:%f", &border, &border2, &outline, &outline2); - radius = atoi(argv[5]); - pad = atoi(argv[6]); - sscanf(argv[7], "%f:%f", &offsetx, &offsety); - advance = atof(argv[8]); - w = atoi(argv[9]); - h = atoi(argv[10]); - tw = atoi(argv[11]); - th = atoi(argv[12]); - if(argc > 13) sw = atoi(argv[13]); - if(argc > 14) sh = atoi(argv[14]); - if(argc > 15) scale = atoi(argv[15]); - if(argc > 16) texdir = argv[16]; - if(FT_Init_FreeType(&l)) - fatal("tessfont: failed initing freetype"); - if(FT_New_Face(l, argv[1], 0, &f) || - FT_Set_Charmap(f, f->charmaps[0]) || - FT_Set_Pixel_Sizes(f, w*supersample, h*supersample)) - fatal("tessfont: failed loading font %s", argv[1]); - setbuf(stdout, NULL); - starttime = time(NULL); - for(c = 0; c < 256; c++) if(iscubeprint(c)) - { - FT_Glyph p; - FT_BitmapGlyph b; - struct fontchar *dst = &chars[numchars]; - dst->code = c; - dst->uni = cube2uni(c); - if(FT_Load_Char(f, dst->uni, FT_LOAD_TARGET_MONO)) - fatal("tessfont: failed loading character %s", encodeutf8(dst->uni)); - FT_Get_Glyph(f->glyph, &p); - FT_Glyph_To_Bitmap(&p, FT_RENDER_MODE_MONO, 0, 1); - b = (FT_BitmapGlyph)p; - dst->tex = -1; - dst->x = INT_MIN; - dst->y = INT_MIN; - dst->w = b->bitmap.width/(float)supersample; - dst->h = b->bitmap.rows/(float)supersample; - dst->left = b->left/(float)supersample; - dst->top = b->top/(float)supersample; - dst->advance = offsetx + p->advance.x/(float)(supersample<<16) + advance; - dst->glyph = b; - dst->sdfradius = radius; - dst->sdf = NULL; - dst->sdfpitch = 0; - dst->sdfx = 0; - dst->sdfy = 0; - dst->sdfw = 0; - dst->sdfh = 0; - order[numchars++] = dst; - if(!numgen) printf("tessfont: generating %d", dst->code); - else printf(" %d", dst->code); - numgen += dst->code >= 100 ? 4 : (dst->code >= 10 ? 3 : 2); - if(numgen > 50) { printf("\n"); numgen = 0; } - gensdf(dst); - } - if(numgen) printf("\n"); - qsort(order, numchars, sizeof(order[0]), sortchars); - for(i = 0; i < numchars;) - { - struct fontchar *dst; - int j, k, trial0, prevscore, dstscore, fitscore; - for(trial0 = trial, prevscore = -1; (trial -= 2) >= trial0-512;) - { - int g, fw = rw, fh = rh, fy = ry, curscore = 0, reused = 0; - for(j = i; j < numchars; j++) - { - dst = order[j]; - if(dst->tex >= 0 || dst->tex <= trial) continue; - g = groupchar(dst->uni); - dstscore = scorechar(dst, pad, tw, th, fw, fh, fy); - for(k = j; k < numchars; k++) - { - struct fontchar *fit = order[k]; - if(fit->tex >= 0 || fit->tex <= trial) continue; - if(fit->tex >= trial0 && groupchar(fit->uni) != g) break; - fitscore = scorechar(fit, pad, tw, th, fw, fh, fy); - if(fitscore < dstscore || (fitscore == dstscore && fit->sdfh > dst->sdfh)) - { - dst = fit; - dstscore = fitscore; - } - } - if(fw + dst->sdfw > tw) - { - fy += fh + pad; - fw = fh = 0; - } - if(fy + dst->sdfh > th) - { - fy = fw = fh = 0; - if(curscore > 0) break; - } - if(dst->tex >= trial+1 && dst->tex <= trial+2) { dst->tex = trial; reused++; } - else dst->tex = trial; - fw += dst->sdfw + pad; - fh = imax(fh, dst->sdfh); - if(dst != order[j]) --j; - curscore++; - } - if(reused < prevscore || curscore <= prevscore) break; - prevscore = curscore; - } - for(; i < numchars; i++) - { - dst = order[i]; - if(dst->tex >= 0) continue; - dstscore = scorechar(dst, pad, tw, th, rw, rh, ry); - for(j = i; j < numchars; j++) - { - struct fontchar *fit = order[j]; - if(fit->tex < trial || fit->tex > trial+2) continue; - fitscore = scorechar(fit, pad, tw, th, rw, rh, ry); - if(fitscore < dstscore || (fitscore == dstscore && fit->sdfh > dst->sdfh)) - { - dst = fit; - dstscore = fitscore; - } - } - if(dst->tex < trial || dst->tex > trial+2) break; - if(rw + dst->sdfw > tw) - { - ry += rh + pad; - rw = rh = 0; - } - if(ry + dst->sdfh > th) - { - ry = rw = rh = 0; - numtex++; - } - dst->tex = numtex; - dst->x = rw; - dst->y = ry; - rw += dst->sdfw + pad; - rh = imax(rh, dst->sdfh); - y1 = fmin(y1, dst->top - dst->h); - y2 = fmax(y2, dst->top); - x1 = fmin(x1, dst->left); - x2 = fmax(x2, dst->left + dst->w); - w2 = fmax(w2, dst->w); - h2 = fmax(h2, dst->h); - if(dst != order[i]) --i; - } - } - if(rh > 0) numtex++; - if(sh <= 0) sh = (int)ceil(y2 - y1); - if(sw <= 0) sw = sh/3; - endtime = time(NULL); - writetexs(argv[2], chars, numchars, numtex, tw, th); - writecfg(argv[2], chars, numchars, x1, y1, x2, y2, sw, sh, argc, argv); - for(i = 0; i < numchars; i++) - { - struct fontchar *c = &chars[i]; - FT_Done_Glyph((FT_Glyph)c->glyph); - if(c->sdf) free(c->sdf); - } - FT_Done_FreeType(l); - printf("tessfont: (%g, %g) .. (%g, %g) = (%g, %g) / (%g, %g), %d texs, %d secs\n", x1, y1, x2, y2, x2 - x1, y2 - y1, w2, h2, numtex, (int)(endtime - starttime)); - return EXIT_SUCCESS; -} - diff --git a/source/shared/tools.cpp b/source/shared/tools.cpp index e6b2ac9f9..8a88d4839 100644 --- a/source/shared/tools.cpp +++ b/source/shared/tools.cpp @@ -1,6 +1,7 @@ // implementation of generic tools #include "cube.h" +#include "unicode.h" void *operator new(size_t size) { @@ -192,22 +193,58 @@ void getstring(char *text, ucharbuf &p, size_t len) while(*t++); } -void filtertext(char *dst, const char *src, bool colors, bool newlines, bool whitespace, bool forcespace, size_t len) +void filtertext(char *dst, const char *src, uint flags, size_t len, int unilen) { - for(int c = uchar(*src); c; c = uchar(*++src)) + uint c; + size_t s = uni_getchar(src, c); + for(char *p = (char *)src; c; p += s, s = uni_getchar(p, c)) { - if((!colors && c == '\f') || (!newlines && c == '\n')) + if(!(flags&T_COLORS) && c == '\f') { - if(!*++src) break; + if(!*(++p)) break; continue; } - if(!iscubeprint(c)) + if(!(flags&T_NEWLINES) && (c == '\n' || c == '\r')) { - if(!iscubespace(c) || !whitespace) continue; - if(forcespace) c = ' '; + if(!*(p+1)) break; + continue; + } + if(!((flags&T_NAME) ? iscubenamesafe(c) : (!iscubecntrl(c))) && c != '\f' && c != '\n' && c != '\r') + { + if(!iscubespace(c) || !(flags&T_WHITESPACE)) continue; + if(flags&T_FORCESPACE) + { + *dst++ = ' '; + len--; + unilen--; + continue; + } + } + s = min(s, len); // prevent overflow + loopi(s) *dst++ = p[i]; + len -= s; + unilen--; + if(!len || !unilen) break; + } + *dst = '\0'; +} +// like `filtertext` but only strips colors and control characters, used for logs +void filteruni(char *dst, const char *src, size_t len) +{ + uint c; + size_t s = uni_getchar(src, c); + for(char *p = (char *)src; c; p += s, s = uni_getchar(p, c)) + { + if(c == '\f') + { + if(!*(++p)) break; + continue; } - *dst++ = c; - if(!--len) break; + if(iscubecntrl(c) || c == 0xFFFD) continue; + s = min(s, len); // prevent overflow + loopi(s) *dst++ = p[i]; + len -= s; + if(!len) break; } *dst = '\0'; } diff --git a/source/shared/tools.h b/source/shared/tools.h index 314b391e6..c605700d6 100644 --- a/source/shared/tools.h +++ b/source/shared/tools.h @@ -1352,47 +1352,79 @@ struct streambuf size_t length() { return s->size(); } }; +// for `filtertext` enum { - CT_PRINT = 1<<0, - CT_SPACE = 1<<1, - CT_DIGIT = 1<<2, - CT_ALPHA = 1<<3, - CT_LOWER = 1<<4, - CT_UPPER = 1<<5, - CT_UNICODE = 1<<6 + T_NONE = 0, + T_COLORS = 1<<0, + T_NEWLINES = 1<<1, + T_WHITESPACE = 1<<2, + T_FORCESPACE = 1<<3, + T_NAME = 1<<4 }; -extern const uchar cubectype[256]; -static inline int iscubeprint(uchar c) { return cubectype[c]&CT_PRINT; } -static inline int iscubespace(uchar c) { return cubectype[c]&CT_SPACE; } -static inline int iscubealpha(uchar c) { return cubectype[c]&CT_ALPHA; } -static inline int iscubealnum(uchar c) { return cubectype[c]&(CT_ALPHA|CT_DIGIT); } -static inline int iscubelower(uchar c) { return cubectype[c]&CT_LOWER; } -static inline int iscubeupper(uchar c) { return cubectype[c]&CT_UPPER; } -static inline int iscubepunct(uchar c) { return cubectype[c] == CT_PRINT; } -static inline int cube2uni(uchar c) -{ - extern const int cube2unichars[256]; - return cube2unichars[c]; -} -static inline uchar uni2cube(int c) -{ - extern const int uni2cubeoffsets[8]; - extern const uchar uni2cubechars[]; - return uint(c) <= 0x7FF ? uni2cubechars[uni2cubeoffsets[c>>8] + (c&0xFF)] : 0; -} -static inline uchar cubelower(uchar c) + +/* (enable all of unicode for now) +// characters that can be shown in the console +static inline int iscubeprint(uint c) { - extern const uchar cubelowerchars[256]; - return cubelowerchars[c]; -} -static inline uchar cubeupper(uchar c) + return (c >= 0x0021 && c <= 0x007E) // ASCII [94] + + || (c >= 0x00A1 && c <= 0x00AC) // (exclude 0x00AD soft hyphen) + || (c >= 0x00AE && c <= 0x00FF) // Latin-1 Supplement [94] + + || (c >= 0x0100 && c <= 0x017F) // Latin Extended-A [128] + || (c >= 0x0180 && c <= 0x024F) // Latin Extended-B [208] + + || (c >= 0x0370 && c <= 0x0377) + || (c >= 0x037A && c <= 0x037F) + || (c >= 0x0384 && c <= 0x038A) + || (c == 0x038C) + || (c >= 0x038E && c <= 0x03A1) + || (c >= 0x03A3 && c <= 0x03FF) // Greek and Coptic [135] + + || (c >= 0x0400 && c <= 0x0482) // (exclude combining cyrillic characters) + || (c >= 0x048A && c <= 0x04FF) // Cyrillic [249] + + || (c >= 0x0500 && c <= 0x052F) // Cyrillic Supplement [48] + || (c >= 0x1E00 && c <= 0x1EFF) // Latin Extended Additional [256] + + || (c >= 0x20A0 && c <= 0x20C0) // Currency Symbols [33] + ; +}*/ +static inline int iscubespace(uint c) { return c == ' ' || c == '\n' || c == '\r' || c == '\t' ? 1 : 0; } +static inline int iscubealpha(uint c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ? 1 : 0; } +static inline int iscubealnum(uint c) { return (c >= '0' && c <= '9') || iscubealpha(c) ? 1 : 0; } +static inline int iscubelower(uint c) { return (c >= 'a' && c <= 'z') ? 1 : 0; } +static inline int iscubeupper(uint c) { return (c >= 'A' && c <= 'Z') ? 1 : 0; } +static inline int iscubecntrl(uint c) { return (c <= 0x08 || c == 0x0B || (c >= 0x0E && c <= 0x1F) || (c >= 0x7F && c <= 0x9F)) ? 1 : 0; } +static inline int iscubepunct(uint c) { return (c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') ? 1 : 0; } +// a subeset of console characters that are allowed in player names +// the default font should contain glyphs for all of these +static inline int iscubenamesafe(uint c) { - extern const uchar cubeupperchars[256]; - return cubeupperchars[c]; + return (c >= 0x0021 && c <= 0x007E) // ASCII + + || (c >= 0x00A1 && c <= 0x00AC) + || (c >= 0x00AE && c <= 0x00FF) // Latin-1 Supplement + + || (c >= 0x0100 && c <= 0x0131) + || (c >= 0x0134 && c <= 0x0148) + || (c >= 0x014A && c <= 0x017F) // Latin Extended-A (exclude 0x131, 0x132 and 0x149) + + || (c >= 0x0218 && c <= 0x021B) // Latin Extended-B: Romanian + + || (c == 0x0386) + || (c >= 0x0388 && c <= 0x038A) + || (c == 0x038C) + || (c >= 0x038E && c <= 0x03A1) + || (c >= 0x03A3 && c <= 0x03CE) // Greek and Coptic + + || (c >= 0x0400 && c <= 0x045F) // Cyrillic + + || (c >= 0x1EA0 && c <= 0x1EF9) // Latin Extended Additional: Vietnamese + ; } -extern size_t decodeutf8(uchar *dst, size_t dstlen, const uchar *src, size_t srclen, size_t *carry = NULL); -extern size_t encodeutf8(uchar *dstbuf, size_t dstlen, const uchar *srcbuf, size_t srclen, size_t *carry = NULL); + extern int cubecasecmp(const char *s1, const char *s2, int n = INT_MAX); static inline bool cubecaseequal(const char *s1, const char *s2, int n = INT_MAX) { return !cubecasecmp(s1, s2, n); } extern char *cubecasefind(const char *haystack, const char *needle); @@ -1416,8 +1448,7 @@ extern stream *openzipfile(const char *filename, const char *mode); extern stream *openfile(const char *filename, const char *mode); extern stream *opentempfile(const char *filename, const char *mode); extern stream *opengzfile(const char *filename, const char *mode, stream *file = NULL, int level = Z_BEST_COMPRESSION); -extern stream *openutf8file(const char *filename, const char *mode, stream *file = NULL); -extern char *loadfile(const char *fn, size_t *size, bool utf8 = true); +extern char *loadfile(const char *fn, size_t *size); extern bool listdir(const char *dir, bool rel, const char *ext, vector &files); extern int listfiles(const char *dir, const char *ext, vector &files); extern int listzipfiles(const char *dir, const char *ext, vector &files); @@ -1441,8 +1472,10 @@ extern void sendstring(const char *t, packetbuf &p); extern void sendstring(const char *t, vector &p); extern void getstring(char *t, ucharbuf &p, size_t len); template static inline void getstring(char (&t)[N], ucharbuf &p) { getstring(t, p, N); } -extern void filtertext(char *dst, const char *src, bool colors, bool newlines, bool whitespace, bool forcespace, size_t len); -template static inline void filtertext(char (&dst)[N], const char *src, bool colors = false, bool newlines = true, bool whitespace = true, bool forcespace = false) { filtertext(dst, src, colors, newlines, whitespace, forcespace, N-1); } +extern void filtertext(char *dst, const char *src, uint flags, size_t len, int unilen = -1); +template static inline void filtertext(char (&dst)[N], const char *src, uint flags) { filtertext(dst, src, flags, N-1, N-1); } +extern void filteruni(char *dst, const char *src, size_t len); +template static inline void filteruni(char (&dst)[N], const char *src) { filteruni(dst, src, N-1); } struct ipmask { diff --git a/source/shared/unicode.h b/source/shared/unicode.h new file mode 100644 index 000000000..482281f8d --- /dev/null +++ b/source/shared/unicode.h @@ -0,0 +1,188 @@ +// UTF-8 support + +#include "cube.h" + +// reads the next character into `codepoint` and returns the number of bytes it takes up +static inline int uni_getchar(const char *str, uint &codepoint) +{ + const uchar *p = (const uchar *)str; + if(p[0] <= 0x7F) + { + codepoint = p[0]; + return 1; + } + if(p[0] >> 5 == 0x06 && p[1] >> 6 == 0x02) + { + codepoint = ((p[0] & 0x1F) << 6) | (p[1] & 0x3F); + if(codepoint < 0x80) codepoint = 0xFFFD; + return 2; + } + if(p[0] >> 4 == 0x0E && p[1] >> 6 == 0x02 && p[2] >> 6 == 0x02) + { + codepoint = ((p[0] & 0x0F) << 12) | ((p[1] & 0x3F) << 6) | (p[2] & 0x3F); + if(codepoint < 0x800) codepoint = 0xFFFD; + return 3; + } + if(p[0] >> 3 == 0x1E && p[1] >> 6 == 0x02 && p[2] >> 6 == 0x02 && p[3] >> 6 == 0x02) + { + codepoint = ((p[0] & 0x07) << 18) | ((p[1] & 0x3F) << 12) | ((p[2] & 0x3F) << 6) | (p[3] & 0x3F); + if(codepoint < 0x10000 || codepoint > 0x10FFFF) codepoint = 0xFFFD; + return 4; + } + codepoint = 0xFFFD; + return 1; +} + +// returns the number of bytes composing the previous char in the string +static inline int uni_prevchar(const char *str, int i) +{ + if(i <= 0) return 0; + const uchar *p = (const uchar *)str; + if(p[i-1] <= 0x7F) return 1; + if(i >= 2 && p[i-2] >> 5 == 0x06 && p[i-1] >> 6 == 0x02) return 2; + if(i >= 3 && p[i-3] >> 4 == 0x0E && p[i-2] >> 6 == 0x02 && p[i-1] >> 6 == 0x02) return 3; + if(i >= 4 && p[i-4] >> 3 == 0x1E && p[i-3] >> 6 == 0x02 && p[i-2] >> 6 == 0x02 && p[i-1] >> 6 == 0x02) return 4; + return 1; +} + +// encodes a single codepoint +static inline int uni_code2str(uint codepoint, char *dst) +{ + if(codepoint <= 0x007F) + { + dst[0] = codepoint; + dst[1] = '\0'; + return 1; + } + if(codepoint <= 0x07FF) + { + dst[0] = 0xC0 | (codepoint >> 6); + dst[1] = 0x80 | (codepoint & 0x3F); + dst[2] = '\0'; + return 2; + } + if(codepoint <= 0xFFFF) + { + dst[0] = 0xE0 | (codepoint >> 12); + dst[1] = 0x80 | ((codepoint >> 6) & 0x3F); + dst[2] = 0x80 | (codepoint & 0x3F); + dst[3] = '\0'; + return 3; + } + if(codepoint <= 0x10FFFF) + { + dst[0] = 0xF0 | (codepoint >> 18); + dst[1] = 0x80 | ((codepoint >> 12) & 0x3F); + dst[2] = 0x80 | ((codepoint >> 6) & 0x3F); + dst[3] = 0x80 | (codepoint & 0x3F); + dst[4] = '\0'; + return 4; + } + dst[0] = '\0'; + return 0; +} + +static inline uint uni_strlen(const char *str) +{ + uint c, len = 0; + const char *p = str; + while(*p) + { + p += uni_getchar(p, c); + ++len; + } + return len; +} + +// returns the codepoint of the character at index `ix` +static inline uint uni_charat(const char *str, int ix) +{ + if(ix >= 0) + { + const char *p = str; + uint c; + loopi(ix) + { + if(!*p) return 0; + p += uni_getchar(p, c); + } + if(!*p) return 0; + uni_getchar(p, c); + return c; + } + + uint len = strlen(str), b = 0; + const char *p = str+len; + loopi(-ix) + { + b += uni_prevchar(str, len-b); + } + uint c; + uni_getchar(p-b, c); + return c; +} + +// offset of the character at index `ix` +static inline int uni_offset(const char *str, uint ix) +{ + const char *p = str; + uint c; + loopi(ix) + { + if(!*p) break; + p += uni_getchar(p, c); + } + return (p - str); +} +// same but starting from the end of the string (str[-ix]) +static inline int uni_noffset(const char *str, uint ix) +{ + const char *p = str + strlen(str); + loopi(ix) + { + if(p <= str) break; + p -= uni_prevchar(str, p - str); + } + return (p - str); +} + +// returns the index of the unicode character at offset `i` +static inline int uni_index(const char *str, uint i) +{ + const char *p = str, *end = str + strlen(str); + uint _c; + int ret = 0; + loopj(i) + { + if(p >= end || p >= str + i) return ret; + p += uni_getchar(p, _c); + ret++; + } + return ret; +} + +// only supports ASCII +static inline void uni_strlower(char *src, char *dst) +{ + for(uchar *p = (uchar *)src; *p; ++p) + { + if(*p >= 'A' && *p <= 'Z') + { + *dst = 'a' + (*p - 'A'); + } + else *dst = *p; + ++dst; + } +} +static inline void uni_strupper(char *src, char *dst) +{ + for(uchar *p = (uchar *)src; *p; ++p) + { + if(*p >= 'a' && *p <= 'z') + { + *dst = 'A' + (*p - 'a'); + } + else *dst = *p; + ++dst; + } +} \ No newline at end of file From c5ac22e1ebf302421ec2b9e1f4b5418cccec0e1a Mon Sep 17 00:00:00 2001 From: Jed- Date: Mon, 21 Oct 2024 13:32:28 +0200 Subject: [PATCH 02/68] Alignment and justification settings for `uiwraptext` --- source/engine/engine.h | 6 ++--- source/engine/rendertext.cpp | 44 +++++++++++++++++++------------- source/engine/ui.cpp | 49 +++++++++++++++++++----------------- source/shared/iengine.h | 4 +-- 4 files changed, 57 insertions(+), 46 deletions(-) diff --git a/source/engine/engine.h b/source/engine/engine.h index e99449010..d2e55c246 100644 --- a/source/engine/engine.h +++ b/source/engine/engine.h @@ -59,10 +59,10 @@ extern void done_pangocairo(); extern int getcurfontid(); extern void gettextres(int &w, int &h); extern void draw_text(textinfo info, float left, float top, int a = 255, bool black = false); -extern void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_color = bvec(255, 255, 255), int cursor = -1, float outline = 0, bvec outline_color = bvec(0, 0, 0), const char *language = NULL); +extern void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_color = bvec(255, 255, 255), int cursor = -1, float outline = 0, bvec outline_color = bvec(0, 0, 0), int align = -1, int justify = 0, const char *language = NULL); extern void prepare_text_particle(const char *str, textinfo &info, bvec initial_color = bvec(255, 255, 255), float outline = 0, bvec outline_color = bvec(0, 0, 0), const char *language = NULL); -extern int text_visible(const char *str, float hitx, float hity, int maxwidth, const char *language = NULL); -extern void text_pos(const char *str, int cursor, int &cx, int &cy, int maxwidth, const char *language = NULL); +extern int text_visible(const char *str, float hitx, float hity, int maxwidth, int align = -1, int justify = 0, const char *language = NULL); +extern void text_pos(const char *str, int cursor, int &cx, int &cy, int maxwidth, int align = -1, int justify = 0, const char *language = NULL); extern void reloadfonts(); // texture diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index 38e15f7a2..8417b2282 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -391,7 +391,7 @@ static inline void add_text_to_layout(const char *markup, int len, PangoLayout * } #undef MARKUP_CASE -static inline PangoLayout *measure_text_internal(const char *str, int len, int maxwidth, bvec initial_color, int &width, int &height, int *map_markup_to_text, int *map_text_to_markup, const char *language) +static inline PangoLayout *measure_text_internal(const char *str, int len, int maxwidth, int align, int justify, bvec initial_color, int &width, int &height, int &offset, int *map_markup_to_text, int *map_text_to_markup, const char *language) { // create cairo context cairo_t *cr = cairo_create(dummy_surface); @@ -401,7 +401,7 @@ static inline PangoLayout *measure_text_internal(const char *str, int len, int m PangoLayout *layout = pango_cairo_create_layout(cr); if(!layout) { - width = height = 0; + width = height = offset = 0; cairo_destroy(cr); return NULL; } @@ -410,34 +410,41 @@ static inline PangoLayout *measure_text_internal(const char *str, int len, int m pango_font_description_set_absolute_size(curfont->desc, fontsize * PANGO_SCALE); // pango 1.8 pango_layout_set_font_description(layout, curfont->desc); - // set maximum length for line wrapping + // line wrapping: set maximum width, alignment and justification if(maxwidth > 0) { pango_layout_set_width(layout, maxwidth * PANGO_SCALE); pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); + pango_layout_set_alignment(layout, align > 0 ? PANGO_ALIGN_RIGHT : align < 0 ? PANGO_ALIGN_LEFT : PANGO_ALIGN_CENTER); + pango_layout_set_justify(layout, justify ? TRUE : FALSE); } add_text_to_layout(str, len, layout, initial_color, map_markup_to_text, map_text_to_markup, language); // get pixel size - pango_layout_get_pixel_size(layout, &width, &height); + PangoRectangle r; + pango_layout_get_extents(layout, NULL, &r); + width = r.width / PANGO_SCALE; + height = r.height / PANGO_SCALE; + offset = -r.x / PANGO_SCALE; cairo_destroy(cr); return layout; } -void measure_text(const char *str, int maxwidth, int &width, int &height, const char *language) +void measure_text(const char *str, int maxwidth, int &width, int &height, int align, int justify, const char *language) { - PangoLayout *layout = measure_text_internal(str, strlen(str), maxwidth, bvec(0, 0, 0), width, height, NULL, NULL, language); + int _offset; + PangoLayout *layout = measure_text_internal(str, strlen(str), maxwidth, align, justify, bvec(0, 0, 0), width, height, _offset, NULL, NULL, language); if(layout) g_object_unref(layout); } -void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_color, int cursor, float outline, bvec outline_color, const char *language) +void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_color, int cursor, float outline, bvec outline_color, int align, int justify, const char *language) { // get dimensions and pango layout - int width, height; + int width, height, offset; const int len = strlen(str); int map_markup_to_text[len+1]; - PangoLayout *layout = measure_text_internal(str, len, maxwidth, initial_color, width, height, cursor >= 0 ? map_markup_to_text : NULL, NULL, language); + PangoLayout *layout = measure_text_internal(str, len, maxwidth, align, justify, initial_color, width, height, offset, cursor >= 0 ? map_markup_to_text : NULL, NULL, language); if(!layout) { info = {0, 0, 0}; return; } if(!width || !height) { g_object_unref(layout); info = {0, 0, 0}; return; } @@ -446,6 +453,7 @@ void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_co cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); cairo_t *cr = cairo_create(surface); cairo_set_font_options(cr, options); + cairo_move_to(cr, offset, 0); // draw text onto the surface if(outline) @@ -507,7 +515,7 @@ void prepare_text_particle(const char *str, textinfo &info, bvec initial_color, info = p.ti; return; } - prepare_text(str, p.ti, 0, initial_color, -1, outline, outline_color, language); + prepare_text(str, p.ti, 0, initial_color, -1, outline, outline_color, -1, 0, language); if(!p.ti.tex) { info = {0, 0, 0}; return; } if(particle_queue.length() >= 256) { @@ -555,10 +563,10 @@ void draw_text(textinfo info, float left, float top, int a, bool black) gle::end(); // NOTE: `info.tex` is not deleted here! } -void draw_text(const char *str, float left, float top, int r, int g, int b, int a, const char *language) +void draw_text(const char *str, float left, float top, int r, int g, int b, int a, int maxwidth, int align, int justify, const char *language) { textinfo info; - prepare_text(str, info, 0, bvec(r, g, b), -1, 0, bvec(0, 0, 0), language); + prepare_text(str, info, maxwidth, bvec(r, g, b), -1, 0, bvec(0, 0, 0), align, justify, language); if(!info.tex) return; draw_text(info, left, top, a); glDeleteTextures(1, &info.tex); @@ -587,13 +595,13 @@ void gettextres(int &w, int &h) } // used by the text editor -int text_visible(const char *str, float hitx, float hity, int maxwidth, const char *language) +int text_visible(const char *str, float hitx, float hity, int maxwidth, int align, int justify, const char *language) { - int width, height; + int width, height, _offset; const int len = strlen(str); if(!len) return 0; int map_text_to_markup[len+1]; - PangoLayout *layout = measure_text_internal(str, len, maxwidth, bvec(0, 0, 0), width, height, NULL, map_text_to_markup, language); + PangoLayout *layout = measure_text_internal(str, len, maxwidth, align, justify, bvec(0, 0, 0), width, height, _offset, NULL, map_text_to_markup, language); if(!layout) return len; if(!width || !height) { g_object_unref(layout); return len; } @@ -605,13 +613,13 @@ int text_visible(const char *str, float hitx, float hity, int maxwidth, const ch } // used by the text editor -void text_pos(const char *str, int cursor, int &cx, int &cy, int maxwidth, const char *language) +void text_pos(const char *str, int cursor, int &cx, int &cy, int maxwidth, int align, int justify, const char *language) { - int width, height; + int width, height, _offset; const int len = strlen(str); if(!len) { cx = cy = 0; return; } int map_markup_to_text[len+1]; - PangoLayout *layout = measure_text_internal(str, len, maxwidth, bvec(0, 0, 0), width, height, map_markup_to_text, NULL, language); + PangoLayout *layout = measure_text_internal(str, len, maxwidth, align, justify, bvec(0, 0, 0), width, height, _offset, map_markup_to_text, NULL, language); if(!layout) { cx = cy = 0; return; } if(!width || !height) { g_object_unref(layout); cx = cy = 0; return; } diff --git a/source/engine/ui.cpp b/source/engine/ui.cpp index f6214b160..bf4a9c396 100644 --- a/source/engine/ui.cpp +++ b/source/engine/ui.cpp @@ -1979,20 +1979,21 @@ namespace UI textinfo info; int fontid, lastchange; int fancy; // 0 = none, 1 = shadow, 2 = outline, 3 = shadow+outline + int align, justify; char *language; bool changed; uint crc; // string hash used for change detection Text() : info({0, 0, 0}), lastchange(0), fancy(0), language(NULL), crc(0) {} - void setup(float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1, int fancy_ = 0, const char *language_ = "") + void setup(float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1, int fancy_ = 0, int align_ = -1, int justify_ = 0, const char *language_ = "") { Object::setup(); changed = false; float newscale = scale_ * uiscale; int curfontid = getcurfontid(); - if(newscale != scale || wrap_ != wrap || fontid != curfontid || fancy_ != fancy || (!language || strcmp(language_, language)) || (color_.r != color.r || color_.g != color.g || color_.b != color.b)) + if(newscale != scale || wrap_ != wrap || fontid != curfontid || fancy_ != fancy || align_ != align || justify_ != justify || (!language || strcmp(language_, language)) || (color_.r != color.r || color_.g != color.g || color_.b != color.b)) { changed = true; lastchange = totalmillis; @@ -2002,6 +2003,8 @@ namespace UI color = color_; wrap = wrap_; fancy = fancy_; + align = align_; + justify = justify_; SETSTR(language, language_); fontid = curfontid; } @@ -2069,7 +2072,7 @@ namespace UI if(!info.tex) { - prepare_text(text, info, int(wrap/k), bvec(color.r, color.g, color.b), -1, fancy >= 2 ? 1.0 : 0, bvec(0, 0, 0), language); + prepare_text(text, info, int(wrap/k), bvec(color.r, color.g, color.b), -1, fancy >= 2 ? 1.0 : 0, bvec(0, 0, 0), align, justify, language); } w = max(w, info.w*k); h = max(h, info.h*k); @@ -2083,9 +2086,9 @@ namespace UI TextString() : str(NULL) {} ~TextString() { delete[] str; } - void setup(const char *str_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1, int fancy_ = 0, const char *language_ = "") + void setup(const char *str_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1, int fancy_ = 0, int align_ = -1, int justify_ = 0, const char *language_ = "") { - Text::setup(scale_, color_, wrap_, fancy_, language_); + Text::setup(scale_, color_, wrap_, fancy_, align_, justify_, language_); SETSTR(str, str_); } @@ -2103,9 +2106,9 @@ namespace UI TextInt() : val(0) { str[0] = '0'; str[1] = '\0'; } - void setup(int val_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1, int fancy_ = 0, const char *language_ = "") + void setup(int val_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1, int fancy_ = 0, int align_ = -1, int justify_ = 0, const char *language_ = "") { - Text::setup(scale_, color_, wrap_, fancy_, language_); + Text::setup(scale_, color_, wrap_, fancy_, align_, justify_, language_); if(val != val_) { val = val_; intformat(str, val, sizeof(str)); } } @@ -2123,9 +2126,9 @@ namespace UI TextFloat() : val(0) { memcpy(str, "0.0", 4); } - void setup(float val_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1, int fancy_ = 0, const char *language_ = "") + void setup(float val_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1, int fancy_ = 0, int align_ = -1, int justify_ = 0, const char *language_ = "") { - Text::setup(scale_, color_, wrap_, fancy_, language_); + Text::setup(scale_, color_, wrap_, fancy_, align_, justify_, language_); if(val != val_) { val = val_; floatformat(str, val, sizeof(str)); } } @@ -3541,7 +3544,7 @@ namespace UI ICOMMAND(uimodcircle, "ife", (int *c, float *size, uint *children), BUILD(Circle, o, o->setup(Color(*c), *size, Circle::MODULATE), children)); - static inline void buildtext(tagval &t, float scale, float scalemod, const Color &color, float wrap, uint *children, int fancy, const char *language) + static inline void buildtext(tagval &t, float scale, float scalemod, const Color &color, float wrap, uint *children, int fancy, int align, int justify, const char *language) { if(scale <= 0) scale = 1; scale *= scalemod; @@ -3549,17 +3552,17 @@ namespace UI switch(t.type) { case VAL_INT: - BUILD(TextInt, o, o->setup(t.i, scale, color, wrap, fancy, language), children); + BUILD(TextInt, o, o->setup(t.i, scale, color, wrap, fancy, align, justify, language), children); break; case VAL_FLOAT: - BUILD(TextFloat, o, o->setup(t.f, scale, color, wrap, fancy, language), children); + BUILD(TextFloat, o, o->setup(t.f, scale, color, wrap, fancy, align, justify, language), children); break; case VAL_CSTR: case VAL_MACRO: case VAL_STR: if(t.s[0]) { - BUILD(TextString, o, o->setup(t.s, scale, color, wrap, fancy, language), children); + BUILD(TextString, o, o->setup(t.s, scale, color, wrap, fancy, align, justify, language), children); break; } // fall-through @@ -3570,34 +3573,34 @@ namespace UI } ICOMMAND(uicolortext, "tifise", (tagval *text, int *c, float *scale, int *fancy, const char *language, uint *children), - buildtext(*text, *scale, uitextscale, Color(*c), -1, children, *fancy, language)); + buildtext(*text, *scale, uitextscale, Color(*c), -1, children, *fancy, -1, 0, language)); ICOMMAND(uitext, "tfise", (tagval *text, float *scale, int *fancy, const char *language, uint *children), - buildtext(*text, *scale, uitextscale, Color(255, 255, 255), -1, children, *fancy, language)); + buildtext(*text, *scale, uitextscale, Color(255, 255, 255), -1, children, *fancy, -1, 0, language)); ICOMMAND(uitextfill, "ffe", (float *minw, float *minh, uint *children), BUILD(Filler, o, o->setup(*minw * uitextscale*0.5f, *minh * uitextscale), children)); - ICOMMAND(uiwrapcolortext, "tfifse", (tagval *text, float *wrap, int *c, float *scale, int *fancy, const char *language, uint *children), - buildtext(*text, *scale, uitextscale, Color(*c), *wrap, children, *fancy, language)); + ICOMMAND(uiwrapcolortext, "tfifiiise", (tagval *text, float *wrap, int *c, float *scale, int *fancy, int *align, int *justify, const char *language, uint *children), + buildtext(*text, *scale, uitextscale, Color(*c), *wrap, children, *fancy, *align, *justify, language)); - ICOMMAND(uiwraptext, "tffse", (tagval *text, float *wrap, float *scale, int *fancy, const char *language, uint *children), - buildtext(*text, *scale, uitextscale, Color(255, 255, 255), *wrap, children, *fancy, language)); + ICOMMAND(uiwraptext, "tffiiise", (tagval *text, float *wrap, float *scale, int *fancy, int *align, int *justify, const char *language, uint *children), + buildtext(*text, *scale, uitextscale, Color(255, 255, 255), *wrap, children, *fancy, *align, *justify, language)); ICOMMAND(uicolorcontext, "tife", (tagval *text, int *c, float *scale, uint *children), - buildtext(*text, *scale, FONTH*uicontextscale, Color(*c), -1, children, 0, "")); + buildtext(*text, *scale, FONTH*uicontextscale, Color(*c), -1, children, 0, -1, 0, "")); ICOMMAND(uicontext, "tfe", (tagval *text, float *scale, uint *children), - buildtext(*text, *scale, FONTH*uicontextscale, Color(255, 255, 255), -1, children, 0, "")); + buildtext(*text, *scale, FONTH*uicontextscale, Color(255, 255, 255), -1, children, 0, -1, 0, "")); ICOMMAND(uicontextfill, "ffe", (float *minw, float *minh, uint *children), BUILD(Filler, o, o->setup(*minw * FONTH*uicontextscale*0.5f, *minh * FONTH*uicontextscale), children)); ICOMMAND(uiwrapcolorcontext, "tfife", (tagval *text, float *wrap, int *c, float *scale, uint *children), - buildtext(*text, *scale, FONTH*uicontextscale, Color(*c), *wrap, children, 0, "")); + buildtext(*text, *scale, FONTH*uicontextscale, Color(*c), *wrap, children, 0, -1, 0, "")); ICOMMAND(uiwrapcontext, "tffe", (tagval *text, float *wrap, float *scale, uint *children), - buildtext(*text, *scale, FONTH*uicontextscale, Color(255, 255, 255), *wrap, children, 0, "")); + buildtext(*text, *scale, FONTH*uicontextscale, Color(255, 255, 255), *wrap, children, 0, -1, 0, "")); ICOMMAND(uitexteditor, "siifsie", (char *name, int *length, int *height, float *scale, char *initval, int *mode, uint *children), BUILD(TextEditor, o, o->setup(name, *length, *height, (*scale <= 0 ? 1 : *scale) * uitextscale, initval, *mode <= 0 ? EDITORFOREVER : *mode), children)); diff --git a/source/shared/iengine.h b/source/shared/iengine.h index 6f606c7e6..85637e4da 100644 --- a/source/shared/iengine.h +++ b/source/shared/iengine.h @@ -256,9 +256,9 @@ extern bool setfont(const char *name); static inline void setfontsize(float size) { fontsize = size; } extern void pushfont(); extern bool popfont(); -extern void draw_text(const char *str, float left, float top, int r = 255, int g = 255, int b = 255, int a = 255, const char *language = NULL); +extern void draw_text(const char *str, float left, float top, int r = 255, int g = 255, int b = 255, int a = 255, int maxwidth = 0, int align = -1, int justify = 0, const char *language = NULL); extern void draw_textf(const char *fstr, float left, float top, ...) PRINTFARGS(1, 4); -extern void measure_text(const char *str, int maxwidth, int &width, int &height, const char *language = NULL); +extern void measure_text(const char *str, int maxwidth, int &width, int &height, int align = -1, int justify = 0, const char *language = NULL); // texture From ed09aef453353e9508a26b1c14cab5df1ae72536 Mon Sep 17 00:00:00 2001 From: Jed- Date: Mon, 21 Oct 2024 14:10:33 +0200 Subject: [PATCH 03/68] Cleanup --- config/glsl/hud.cfg | 48 ++++++++++++------------- source/engine/shader.cpp | 2 +- source/engine/ui.cpp | 77 ++++++++++++++++++---------------------- 3 files changed, 60 insertions(+), 67 deletions(-) diff --git a/config/glsl/hud.cfg b/config/glsl/hud.cfg index b1b6d9451..9f294c978 100644 --- a/config/glsl/hud.cfg +++ b/config/glsl/hud.cfg @@ -28,6 +28,30 @@ shader 0 "hud" [ } ] +shader 0 "hudtext" [ + attribute vec4 vvertex, vcolor; + attribute vec2 vtexcoord0; + uniform mat4 hudmatrix; + varying vec2 texcoord0; + varying vec4 colorscale; + void main(void) + { + gl_Position = hudmatrix * vvertex; + texcoord0 = vtexcoord0; + colorscale = vcolor; + } +] [ + uniform sampler2DRect tex0; + varying vec2 texcoord0; + varying vec4 colorscale; + fragdata(0) vec4 fragcolor; + void main(void) + { + fragcolor = colorscale * texture2DRect(tex0, texcoord0); + if(fragcolor.a != 0) fragcolor.rgb /= fragcolor.a; + } +] + shader 0 "hudrgb" [ attribute vec4 vvertex, vcolor; attribute vec2 vtexcoord0; @@ -125,30 +149,6 @@ shader 0 "hudrect" [ } ] -shader 0 "hudtext" [ - attribute vec4 vvertex, vcolor; - attribute vec2 vtexcoord0; - uniform mat4 hudmatrix; - varying vec2 texcoord0; - varying vec4 colorscale; - void main(void) - { - gl_Position = hudmatrix * vvertex; - texcoord0 = vtexcoord0; - colorscale = vcolor; - } -] [ - uniform sampler2DRect tex0; - varying vec2 texcoord0; - varying vec4 colorscale; - fragdata(0) vec4 fragcolor; - void main(void) - { - fragcolor = colorscale * texture2DRect(tex0, texcoord0); - if(fragcolor.a != 0) fragcolor.rgb = fragcolor.rgb / fragcolor.a; - } -] - shader 0 "hud3d" [ attribute vec4 vvertex, vcolor; attribute vec3 vtexcoord0; diff --git a/source/engine/shader.cpp b/source/engine/shader.cpp index c4f34b9d8..8ddbebed1 100644 --- a/source/engine/shader.cpp +++ b/source/engine/shader.cpp @@ -1034,7 +1034,7 @@ void setupshaders() "void main(void)\n" "{\n" " gl_Position = hudmatrix * vvertex;\n" - " texcoord0 = vtexcoord0;\n" + " texcoord0 = vtexcoord0;\n" " colorscale = vcolor;\n" "}\n", "uniform sampler2DRect tex0;\n" diff --git a/source/engine/ui.cpp b/source/engine/ui.cpp index bf4a9c396..bc847d41f 100644 --- a/source/engine/ui.cpp +++ b/source/engine/ui.cpp @@ -644,10 +644,7 @@ namespace UI void hide() { - loopchildren(o, - { - o->hide(); - }); + Object::hide(); if(onhide) execute(onhide); } @@ -827,7 +824,7 @@ namespace UI resetstate(); } - void hide() {}; + void hide() { Object::hide(); }; bool show(Window *w) { @@ -2035,7 +2032,7 @@ namespace UI pophudmatrix(); } - void hide() { cleartext(); } + void hide() { Object::hide(); cleartext(); } void cleartext() { @@ -2141,82 +2138,78 @@ namespace UI struct Font : Object { - string font; + char *font; - Font() { font[0] = '\0'; } + Font() : font(NULL) {} + ~Font() { delete[] font; } void setup(const char *name) { Object::setup(); - copystring(font, name, MAXSTRLEN); + SETSTR(font, name); } - #define WITHFONT(body) do { \ - pushfont(); \ - setfont(font); \ - body; \ - popfont(); \ - } while(0); \ - void layout() { - WITHFONT({ - Object::layout(); - }); + pushfont(); + setfont(font); + Object::layout(); + popfont(); } void draw(float sx, float sy) { - WITHFONT({ - Object::draw(sx, sy); - }); + pushfont(); + setfont(font); + Object::draw(sx, sy); + popfont(); } void buildchildren(uint *contents) { - WITHFONT({ - Object::buildchildren(contents); - }); + pushfont(); + setfont(font); + Object::buildchildren(contents); + popfont(); } #define DOSTATE(flags, func) \ void func##children(float cx, float cy, int mask, bool inside, int setflags) \ { \ - WITHFONT({ \ - Object::func##children(cx, cy, mask, inside, setflags); \ - }); \ + pushfont(); \ + setfont(font); \ + Object::func##children(cx, cy, mask, inside, setflags); \ + popfont(); \ } \ DOSTATES #undef DOSTATE bool rawkey(int code, bool isdown) { - bool result; - WITHFONT({ - result = Object::rawkey(code, isdown); - }); + pushfont(); + setfont(font); + bool result = Object::rawkey(code, isdown); + popfont(); return result; } bool key(int code, bool isdown) { - bool result; - WITHFONT({ - result = Object::key(code, isdown); - }); + pushfont(); + setfont(font); + bool result = Object::key(code, isdown); + popfont(); return result; } bool textinput(const char *str, int len) { - bool result; - WITHFONT({ - result = Object::textinput(str, len); - }); + pushfont(); + setfont(font); + bool result = Object::textinput(str, len); + popfont(); return result; } - - #undef WITHFONT }; float uicontextscale = 0; From 9a629ad9bf1dcfb95601b1198f1cab248c59719a Mon Sep 17 00:00:00 2001 From: Jed- Date: Sun, 3 Nov 2024 12:38:47 +0100 Subject: [PATCH 04/68] Replace "wide" font and fix compilation --- config/font.cfg | 9 +++++---- source/engine/renderparticles.cpp | 5 ++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/config/font.cfg b/config/font.cfg index 8732eb6a1..7e3cde6b3 100644 --- a/config/font.cfg +++ b/config/font.cfg @@ -15,12 +15,13 @@ registerfont "data/interface/font/InterVariable.ttf" // Inter Variable registerfont "data/interface/font/InterVariable-Italic.ttf" // Inter Variable Italic registerfont "data/interface/font/DejaVuSansMono.ttf" // DejaVu Sans Mono -registerfont "data/interface/font/Oxanium-VariableFont_wght.ttf" // Oxanium +registerfont "data/interface/font/Jura-VariableFont_wght.ttf" // Jura registerfont "data/interface/font/webdings.ttf" // Webdings font "default" "Inter Variable, Sans" - fontfeatures ["ss04", "cv12", "cv13"] + fontfeatures ["ss04", "cv12", "cv13", "tnum"] font "mono" "DejaVu Sans Mono, Monospace" -font "wide" "Oxanium, Inter Variable, Sans" - fontweight 4 +font "wide" "Jura, Inter Variable, Sans" + fontfeatures ["liga", "calt", "zero"] + fontweight 3 font "webdings" "Webdings" \ No newline at end of file diff --git a/source/engine/renderparticles.cpp b/source/engine/renderparticles.cpp index 94da4108f..f02a9488b 100644 --- a/source/engine/renderparticles.cpp +++ b/source/engine/renderparticles.cpp @@ -1167,7 +1167,10 @@ void particle_hud_text(const vec &s, const char *t, int type, int fade, int colo { if(!canaddparticles()) return; vec o; - if(camera1->o.dist(s) <= hudmarkdist && raycubelos(s, camera1->o, o)) return; + if((camera1->o.dist(s) <= hudmarkmindist && raycubelos(s, camera1->o, o)) || camera1->o.dist(s) > hudmarkmaxdist) + { + return; + } o = s; vec camera = camera1->o; o.sub(camera).normalize(); From 77249cd6710efe11eb00857c9636e4663b079c8d Mon Sep 17 00:00:00 2001 From: Jed- Date: Sun, 3 Nov 2024 13:07:42 +0100 Subject: [PATCH 05/68] Make room for the outline --- source/engine/rendertext.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index 8417b2282..8ef69bd0d 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -450,10 +450,13 @@ void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_co // create surface and cairo context if(cursor >= 0) width += max(4.f, fontsize); // make space for the cursor + int outline_offset = ceil(outline); + width += 2 * outline_offset; + height += 2 * outline_offset; cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); cairo_t *cr = cairo_create(surface); cairo_set_font_options(cr, options); - cairo_move_to(cr, offset, 0); + cairo_move_to(cr, offset + outline_offset, outline_offset); // draw text onto the surface if(outline) @@ -464,6 +467,7 @@ void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_co cairo_stroke(cr); } cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0); + cairo_move_to(cr, offset + outline_offset, outline_offset); pango_cairo_show_layout(cr, layout); // add the cursor @@ -475,7 +479,7 @@ void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_co const float curw = max(1.f, fontsize / 16); - cairo_rectangle(cr, cursor_rect.x / PANGO_SCALE, cursor_rect.y / PANGO_SCALE, curw, cursor_rect.height / PANGO_SCALE); + cairo_rectangle(cr, cursor_rect.x / PANGO_SCALE + outline_offset, cursor_rect.y / PANGO_SCALE + outline_offset, curw, cursor_rect.height / PANGO_SCALE); cairo_set_source_rgba(cr, cursorcolor.r / 255.f, cursorcolor.g / 255.f, cursorcolor.b / 255.f, 1.0); cairo_fill(cr); } From c1e1495c08ea2d06de44ad175c7fc2705b99ca47 Mon Sep 17 00:00:00 2001 From: Jed- Date: Sun, 3 Nov 2024 13:17:03 +0100 Subject: [PATCH 06/68] Cleanup --- config/ui/lib.cfg | 4 +--- source/engine/command.cpp | 4 ++-- source/shared/tools.cpp | 4 ++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/config/ui/lib.cfg b/config/ui/lib.cfg index 1f3885e86..e2882b366 100644 --- a/config/ui/lib.cfg +++ b/config/ui/lib.cfg @@ -796,9 +796,7 @@ uiFancyText = [ if (=s $arg1 "") [ arg1 = "default" ] if $arg4 [] [ arg4 = $c_white ] if (< $numargs 5) [ arg5 = 1 ] - uigroup [ // DO NOT REMOVE! - uifontcolortext $arg1 $arg2 (|A (*fA $arg5) $arg4) $arg3 1 - ] + uifontcolortext $arg1 $arg2 (|A (*fA $arg5) $arg4) $arg3 1 ] // uiEmbossText FONT TEXT SIZE 0xCOLOR diff --git a/source/engine/command.cpp b/source/engine/command.cpp index 469c0f9c7..f8377a29f 100644 --- a/source/engine/command.cpp +++ b/source/engine/command.cpp @@ -927,7 +927,7 @@ int unescapestring(char *dst, const char *src, const char *end) char *start = dst; uint c; uint s = uni_getchar(src, c); - for(char *p = (char *)src; src < end; p += s, s = uni_getchar(p, c)) + for(const char *p = src; src < end; p += s, s = uni_getchar(p, c)) { if(iscubecntrl(c) || c == 0xFFFD) { @@ -3182,7 +3182,7 @@ const char *escapestring(const char *str) buf.add('"'); uint c; uint s = uni_getchar(str, c); - for(char *p = (char *)str; c; p += s, s = uni_getchar(p, c)) switch(c) + for(const char *p = str; c; p += s, s = uni_getchar(p, c)) switch(c) { case '\n': buf.put("^n", 2); break; case '\r': buf.put("^r", 2); break; diff --git a/source/shared/tools.cpp b/source/shared/tools.cpp index 8a88d4839..95c0c3aa5 100644 --- a/source/shared/tools.cpp +++ b/source/shared/tools.cpp @@ -197,7 +197,7 @@ void filtertext(char *dst, const char *src, uint flags, size_t len, int unilen) { uint c; size_t s = uni_getchar(src, c); - for(char *p = (char *)src; c; p += s, s = uni_getchar(p, c)) + for(const char *p = src; c; p += s, s = uni_getchar(p, c)) { if(!(flags&T_COLORS) && c == '\f') { @@ -233,7 +233,7 @@ void filteruni(char *dst, const char *src, size_t len) { uint c; size_t s = uni_getchar(src, c); - for(char *p = (char *)src; c; p += s, s = uni_getchar(p, c)) + for(const char *p = src; c; p += s, s = uni_getchar(p, c)) { if(c == '\f') { From 0fa441f704548a73c065089391ab9a2077bde7e7 Mon Sep 17 00:00:00 2001 From: Jed- Date: Sun, 3 Nov 2024 15:33:37 +0100 Subject: [PATCH 07/68] Add commands: `uiwrapalign`, `uijustify`, `uishadow`, `uifontoutline`, `uilanguage` --- config/ui/lib.cfg | 12 ++- source/engine/engine.h | 4 +- source/engine/rendertext.cpp | 11 +-- source/engine/ui.cpp | 159 +++++++++++++++++++++++++++-------- 4 files changed, 140 insertions(+), 46 deletions(-) diff --git a/config/ui/lib.cfg b/config/ui/lib.cfg index e2882b366..fe55a3bdf 100644 --- a/config/ui/lib.cfg +++ b/config/ui/lib.cfg @@ -45,8 +45,8 @@ uiclamp*x = [ uiclamp* 1 1 0 0 ] uiclamp*y = [ uiclamp* 0 0 1 1 ] uiclamp*e = [ uiclamp* 1 1 1 1 ] -uifonttext = [ uifont $arg1 [ uitext $arg2 $arg3 $arg4 ] ] -uifontcolortext = [ uifont $arg1 [ uicolortext $arg2 $arg3 $arg4 $arg5 ] ] +uifonttext = [ uifont $arg1 [ uitext $arg2 $arg3 ] ] +uifontcolortext = [ uifont $arg1 [ uicolortext $arg2 $arg3 $arg4 ] ] changeui = [ if $arg2 [] [ arg2 = $uiname ] @@ -796,7 +796,13 @@ uiFancyText = [ if (=s $arg1 "") [ arg1 = "default" ] if $arg4 [] [ arg4 = $c_white ] if (< $numargs 5) [ arg5 = 1 ] - uifontcolortext $arg1 $arg2 (|A (*fA $arg5) $arg4) $arg3 1 + uifont $arg1 [ + uifontoutline 1 0x20 [ + uishadow 255 [ + uicolortext $arg2 (|A (*fA $arg5) $arg4) $arg3 + ] + ] + ] ] // uiEmbossText FONT TEXT SIZE 0xCOLOR diff --git a/source/engine/engine.h b/source/engine/engine.h index d2e55c246..bdcf6f357 100644 --- a/source/engine/engine.h +++ b/source/engine/engine.h @@ -59,8 +59,8 @@ extern void done_pangocairo(); extern int getcurfontid(); extern void gettextres(int &w, int &h); extern void draw_text(textinfo info, float left, float top, int a = 255, bool black = false); -extern void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_color = bvec(255, 255, 255), int cursor = -1, float outline = 0, bvec outline_color = bvec(0, 0, 0), int align = -1, int justify = 0, const char *language = NULL); -extern void prepare_text_particle(const char *str, textinfo &info, bvec initial_color = bvec(255, 255, 255), float outline = 0, bvec outline_color = bvec(0, 0, 0), const char *language = NULL); +extern void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_color = bvec(255, 255, 255), int cursor = -1, float outline = 0, bvec outline_color = bvec(0, 0, 0), int outline_alpha = 255, int align = -1, int justify = 0, const char *language = NULL); +extern void prepare_text_particle(const char *str, textinfo &info, bvec initial_color = bvec(255, 255, 255), float outline = 0, bvec outline_color = bvec(0, 0, 0), int outline_alpha = 255, const char *language = NULL); extern int text_visible(const char *str, float hitx, float hity, int maxwidth, int align = -1, int justify = 0, const char *language = NULL); extern void text_pos(const char *str, int cursor, int &cx, int &cy, int maxwidth, int align = -1, int justify = 0, const char *language = NULL); extern void reloadfonts(); diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index 8ef69bd0d..10a553d78 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -438,7 +438,7 @@ void measure_text(const char *str, int maxwidth, int &width, int &height, int al if(layout) g_object_unref(layout); } -void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_color, int cursor, float outline, bvec outline_color, int align, int justify, const char *language) +void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_color, int cursor, float outline, bvec outline_color, int outline_alpha, int align, int justify, const char *language) { // get dimensions and pango layout int width, height, offset; @@ -461,7 +461,8 @@ void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_co // draw text onto the surface if(outline) { - cairo_set_source_rgba(cr, outline_color.r / 255.f, outline_color.g / 255.f, outline_color.b / 255.f, 1.0); + cairo_set_source_rgba(cr, outline_color.r / 255.f, outline_color.g / 255.f, outline_color.b / 255.f, outline_alpha / 255.f); + cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND); cairo_set_line_width(cr, 2 * outline); pango_cairo_layout_path(cr, layout); cairo_stroke(cr); @@ -504,7 +505,7 @@ void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_co cairo_destroy(cr); cairo_surface_destroy(surface); } -void prepare_text_particle(const char *str, textinfo &info, bvec initial_color, float outline, bvec outline_color, const char *language) +void prepare_text_particle(const char *str, textinfo &info, bvec initial_color, float outline, bvec outline_color, int outline_alpha, const char *language) { const int c = initial_color.tohexcolor(), d = outline_color.tohexcolor(); const char *l = language ? language : ""; @@ -519,7 +520,7 @@ void prepare_text_particle(const char *str, textinfo &info, bvec initial_color, info = p.ti; return; } - prepare_text(str, p.ti, 0, initial_color, -1, outline, outline_color, -1, 0, language); + prepare_text(str, p.ti, 0, initial_color, -1, outline, outline_color, outline_alpha, -1, 0, language); if(!p.ti.tex) { info = {0, 0, 0}; return; } if(particle_queue.length() >= 256) { @@ -570,7 +571,7 @@ void draw_text(textinfo info, float left, float top, int a, bool black) void draw_text(const char *str, float left, float top, int r, int g, int b, int a, int maxwidth, int align, int justify, const char *language) { textinfo info; - prepare_text(str, info, maxwidth, bvec(r, g, b), -1, 0, bvec(0, 0, 0), align, justify, language); + prepare_text(str, info, maxwidth, bvec(r, g, b), -1, 0, bvec(0, 0, 0), 0, align, justify, language); if(!info.tex) return; draw_text(info, left, top, a); glDeleteTextures(1, &info.tex); diff --git a/source/engine/ui.cpp b/source/engine/ui.cpp index 3e373373c..82ed94fcf 100644 --- a/source/engine/ui.cpp +++ b/source/engine/ui.cpp @@ -1968,6 +1968,77 @@ namespace UI else dst = newstring(src); \ } while(0) + static int curwrapalign = -1, curjustify = 0, curshadow = 0, curfontoutlinealpha = 0; + float curfontoutline = 0.f; + static const char *curlanguage = newstring(""); + + #define WITHTEXTATTR(name, tmp, val, body) \ + tmp = cur##name; \ + cur##name = val; \ + body; \ + cur##name = tmp; + + struct WrapAlign : Object + { + static const char *typestr() { return "#WrapAlign"; } + const char *gettype() const { return typestr(); } + + int val, tmp; + void setup(int val_) { val = val_; } + void layout() { WITHTEXTATTR(wrapalign, tmp, val, Object::layout()); } + void draw(float sx, float sy) { WITHTEXTATTR(wrapalign, tmp, val, Object::draw(sx, sy)); } + void buildchildren(uint *contents) { WITHTEXTATTR(wrapalign, tmp, val, Object::buildchildren(contents)); } + }; + struct Justify : Object + { + static const char *typestr() { return "#Justify"; } + const char *gettype() const { return typestr(); } + + int val, tmp; + void setup(int val_) { val = val_; } + void layout() { WITHTEXTATTR(justify, tmp, val, Object::layout()); } + void draw(float sx, float sy) { WITHTEXTATTR(justify, tmp, val, Object::draw(sx, sy)); } + void buildchildren(uint *contents) { WITHTEXTATTR(justify, tmp, val, Object::buildchildren(contents)); } + }; + struct Shadow : Object + { + static const char *typestr() { return "#Shadow"; } + const char *gettype() const { return typestr(); } + + int val, tmp; + void setup(int val_) { val = val_; } + void layout() { WITHTEXTATTR(shadow, tmp, val, Object::layout()); } + void draw(float sx, float sy) { WITHTEXTATTR(shadow, tmp, val, Object::draw(sx, sy)); } + void buildchildren(uint *contents) { WITHTEXTATTR(shadow, tmp, val, Object::buildchildren(contents)); } + }; + struct FontOutline : Object + { + static const char *typestr() { return "#FontOutline"; } + const char *gettype() const { return typestr(); } + + float val, tmp; + int aval, atmp; + void setup(float val_, int aval_) { val = val_; aval = aval_;} + void layout() { WITHTEXTATTR(fontoutline, tmp, val, WITHTEXTATTR(fontoutlinealpha, atmp, aval, Object::layout())); } + void draw(float sx, float sy) { WITHTEXTATTR(fontoutline, tmp, val, WITHTEXTATTR(fontoutlinealpha, atmp, aval, Object::draw(sx, sy))); } + void buildchildren(uint *contents) { WITHTEXTATTR(fontoutline, tmp, val, WITHTEXTATTR(fontoutlinealpha, atmp, aval, Object::buildchildren(contents))); } + }; + struct Language : Object + { + static const char *typestr() { return "#Language"; } + const char *gettype() const { return typestr(); } + + const char *val, *tmp; + Language() : val(NULL), tmp(NULL) {} + ~Language() { DELETEA(val); DELETEA(tmp); } + void setup(const char *val_) { SETSTR(val, val_); } + void layout() { SETSTR(tmp, curlanguage); SETSTR(curlanguage, val); Object::layout(); SETSTR(curlanguage, tmp); } + void draw(float sx, float sy) { SETSTR(tmp, curlanguage); SETSTR(curlanguage, val); Object::draw(sx, sy); SETSTR(curlanguage, tmp); } + void buildchildren(uint *contents) { SETSTR(tmp, curlanguage); SETSTR(curlanguage, val); Object::buildchildren(contents); SETSTR(curlanguage, tmp); } + }; + + #undef WITHTEXTATTR + // NOTE: `scale` is the text height in screenfuls at `uiscale 1` struct Text : Object { @@ -1975,22 +2046,22 @@ namespace UI Color color; textinfo info; int fontid, lastchange; - int fancy; // 0 = none, 1 = shadow, 2 = outline, 3 = shadow+outline - int align, justify; - char *language; + int align, justify, shadow, outlinealpha; + float outline; + const char *language; bool changed; uint crc; // string hash used for change detection - Text() : info({0, 0, 0}), lastchange(0), fancy(0), language(NULL), crc(0) {} + Text() : info({0, 0, 0}), lastchange(0), align(curwrapalign), justify(curjustify), shadow(curshadow), outlinealpha(curfontoutlinealpha), outline(curfontoutline), language(NULL), crc(0) {} - void setup(float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1, int fancy_ = 0, int align_ = -1, int justify_ = 0, const char *language_ = "") + void setup(float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1) { Object::setup(); changed = false; float newscale = scale_ * uiscale; int curfontid = getcurfontid(); - if(newscale != scale || wrap_ != wrap || fontid != curfontid || fancy_ != fancy || align_ != align || justify_ != justify || (!language || strcmp(language_, language)) || (color_.r != color.r || color_.g != color.g || color_.b != color.b)) + if(newscale != scale || wrap_ != wrap || fontid != curfontid || curwrapalign != align || curjustify != justify || curshadow != shadow || curfontoutline != outline || curfontoutlinealpha != outlinealpha || (!language || strcmp(curlanguage, language)) || (color_.r != color.r || color_.g != color.g || color_.b != color.b)) { changed = true; lastchange = totalmillis; @@ -1999,10 +2070,12 @@ namespace UI scale = newscale; color = color_; wrap = wrap_; - fancy = fancy_; - align = align_; - justify = justify_; - SETSTR(language, language_); + align = curwrapalign; + justify = curjustify; + shadow = curshadow; + outline = curfontoutline; + outlinealpha = curfontoutlinealpha; + SETSTR(language, curlanguage); fontid = curfontid; } @@ -2024,9 +2097,9 @@ namespace UI const float textscale = drawscale(), x = round(sx/textscale), y = round(sy/textscale); pushhudscale(textscale); - if(fancy&1) // shadow + if(shadow) { - draw_text(info, x-0.001/textscale, y+0.001/textscale, color.a, true); + draw_text(info, x-0.001/textscale, y+0.001/textscale, (color.a < shadow ? color.a : shadow), true); } draw_text(info, x, y, color.a); pophudmatrix(); @@ -2069,7 +2142,7 @@ namespace UI if(!info.tex) { - prepare_text(text, info, int(wrap/k), bvec(color.r, color.g, color.b), -1, fancy >= 2 ? 1.0 : 0, bvec(0, 0, 0), align, justify, language); + prepare_text(text, info, int(wrap/k), bvec(color.r, color.g, color.b), -1, outline * FONTH / 16.f, bvec(0, 0, 0), outlinealpha, align, justify, language); } w = max(w, info.w*k); h = max(h, info.h*k); @@ -2083,9 +2156,9 @@ namespace UI TextString() : str(NULL) {} ~TextString() { delete[] str; } - void setup(const char *str_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1, int fancy_ = 0, int align_ = -1, int justify_ = 0, const char *language_ = "") + void setup(const char *str_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1) { - Text::setup(scale_, color_, wrap_, fancy_, align_, justify_, language_); + Text::setup(scale_, color_, wrap_); SETSTR(str, str_); } @@ -2103,9 +2176,9 @@ namespace UI TextInt() : val(0) { str[0] = '0'; str[1] = '\0'; } - void setup(int val_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1, int fancy_ = 0, int align_ = -1, int justify_ = 0, const char *language_ = "") + void setup(int val_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1) { - Text::setup(scale_, color_, wrap_, fancy_, align_, justify_, language_); + Text::setup(scale_, color_, wrap_); if(val != val_) { val = val_; intformat(str, val, sizeof(str)); } } @@ -2123,9 +2196,9 @@ namespace UI TextFloat() : val(0) { memcpy(str, "0.0", 4); } - void setup(float val_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1, int fancy_ = 0, int align_ = -1, int justify_ = 0, const char *language_ = "") + void setup(float val_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1) { - Text::setup(scale_, color_, wrap_, fancy_, align_, justify_, language_); + Text::setup(scale_, color_, wrap_); if(val != val_) { val = val_; floatformat(str, val, sizeof(str)); } } @@ -3540,25 +3613,39 @@ namespace UI ICOMMAND(uimodcircle, "ife", (int *c, float *size, uint *children), BUILD(Circle, o, o->setup(Color(*c), *size, Circle::MODULATE), children)); - static inline void buildtext(tagval &t, float scale, float scalemod, const Color &color, float wrap, uint *children, int fancy, int align, int justify, const char *language) + ICOMMAND(uiwrapalign, "ie", (int *val, uint *children), + BUILD(WrapAlign, o, o->setup(*val), children)); + + ICOMMAND(uijustify, "ie", (int *val, uint *children), + BUILD(Justify, o, o->setup(*val), children)); + + ICOMMAND(uishadow, "ie", (int *val, uint *children), + BUILD(Shadow, o, o->setup(*val), children)); + + ICOMMAND(uifontoutline, "fie", (float *val, int *a, uint *children), + BUILD(FontOutline, o, o->setup(*val, *a), children)); + + ICOMMAND(uilanguage, "se", (char *val, uint *children), + BUILD(Language, o, o->setup(val), children)); + + static inline void buildtext(tagval &t, float scale, float scalemod, const Color &color, float wrap, uint *children) { if(scale <= 0) scale = 1; scale *= scalemod; - if(!language) language = ""; switch(t.type) { case VAL_INT: - BUILD(TextInt, o, o->setup(t.i, scale, color, wrap, fancy, align, justify, language), children); + BUILD(TextInt, o, o->setup(t.i, scale, color, wrap), children); break; case VAL_FLOAT: - BUILD(TextFloat, o, o->setup(t.f, scale, color, wrap, fancy, align, justify, language), children); + BUILD(TextFloat, o, o->setup(t.f, scale, color, wrap), children); break; case VAL_CSTR: case VAL_MACRO: case VAL_STR: if(t.s[0]) { - BUILD(TextString, o, o->setup(t.s, scale, color, wrap, fancy, align, justify, language), children); + BUILD(TextString, o, o->setup(t.s, scale, color, wrap), children); break; } // fall-through @@ -3568,35 +3655,35 @@ namespace UI } } - ICOMMAND(uicolortext, "tifise", (tagval *text, int *c, float *scale, int *fancy, const char *language, uint *children), - buildtext(*text, *scale, uitextscale, Color(*c), -1, children, *fancy, -1, 0, language)); + ICOMMAND(uicolortext, "tife", (tagval *text, int *c, float *scale, uint *children), + buildtext(*text, *scale, uitextscale, Color(*c), -1, children)); - ICOMMAND(uitext, "tfise", (tagval *text, float *scale, int *fancy, const char *language, uint *children), - buildtext(*text, *scale, uitextscale, Color(255, 255, 255), -1, children, *fancy, -1, 0, language)); + ICOMMAND(uitext, "tfe", (tagval *text, float *scale, uint *children), + buildtext(*text, *scale, uitextscale, Color(255, 255, 255), -1, children)); ICOMMAND(uitextfill, "ffe", (float *minw, float *minh, uint *children), BUILD(Filler, o, o->setup(*minw * uitextscale*0.5f, *minh * uitextscale), children)); - ICOMMAND(uiwrapcolortext, "tfifiiise", (tagval *text, float *wrap, int *c, float *scale, int *fancy, int *align, int *justify, const char *language, uint *children), - buildtext(*text, *scale, uitextscale, Color(*c), *wrap, children, *fancy, *align, *justify, language)); + ICOMMAND(uiwrapcolortext, "tfife", (tagval *text, float *wrap, int *c, float *scale, uint *children), + buildtext(*text, *scale, uitextscale, Color(*c), *wrap, children)); - ICOMMAND(uiwraptext, "tffiiise", (tagval *text, float *wrap, float *scale, int *fancy, int *align, int *justify, const char *language, uint *children), - buildtext(*text, *scale, uitextscale, Color(255, 255, 255), *wrap, children, *fancy, *align, *justify, language)); + ICOMMAND(uiwraptext, "tffe", (tagval *text, float *wrap, float *scale, uint *children), + buildtext(*text, *scale, uitextscale, Color(255, 255, 255), *wrap, children)); ICOMMAND(uicolorcontext, "tife", (tagval *text, int *c, float *scale, uint *children), - buildtext(*text, *scale, FONTH*uicontextscale, Color(*c), -1, children, 0, -1, 0, "")); + buildtext(*text, *scale, FONTH*uicontextscale, Color(*c), -1, children)); ICOMMAND(uicontext, "tfe", (tagval *text, float *scale, uint *children), - buildtext(*text, *scale, FONTH*uicontextscale, Color(255, 255, 255), -1, children, 0, -1, 0, "")); + buildtext(*text, *scale, FONTH*uicontextscale, Color(255, 255, 255), -1, children)); ICOMMAND(uicontextfill, "ffe", (float *minw, float *minh, uint *children), BUILD(Filler, o, o->setup(*minw * FONTH*uicontextscale*0.5f, *minh * FONTH*uicontextscale), children)); ICOMMAND(uiwrapcolorcontext, "tfife", (tagval *text, float *wrap, int *c, float *scale, uint *children), - buildtext(*text, *scale, FONTH*uicontextscale, Color(*c), *wrap, children, 0, -1, 0, "")); + buildtext(*text, *scale, FONTH*uicontextscale, Color(*c), *wrap, children)); ICOMMAND(uiwrapcontext, "tffe", (tagval *text, float *wrap, float *scale, uint *children), - buildtext(*text, *scale, FONTH*uicontextscale, Color(255, 255, 255), *wrap, children, 0, -1, 0, "")); + buildtext(*text, *scale, FONTH*uicontextscale, Color(255, 255, 255), *wrap, children)); ICOMMAND(uitexteditor, "siifsie", (char *name, int *length, int *height, float *scale, char *initval, int *mode, uint *children), BUILD(TextEditor, o, o->setup(name, *length, *height, (*scale <= 0 ? 1 : *scale) * uitextscale, initval, *mode <= 0 ? EDITORFOREVER : *mode), children)); From 478660d81f7079c1c942eb9405268b2f28c49c6c Mon Sep 17 00:00:00 2001 From: Jed- Date: Sun, 3 Nov 2024 15:59:14 +0100 Subject: [PATCH 08/68] Restore font outlines --- config/ui/hud/gamehud.cfg | 26 +++++++++++++++----------- config/ui/hud/mapmodel_browser.cfg | 8 ++++---- config/ui/hud/texture_browser.cfg | 10 +++++++--- config/ui/lib.cfg | 24 ++++++++++++++++-------- config/ui/libnew.cfg | 4 +++- config/ui/menus/server_browser.cfg | 2 +- config/ui/permanent.cfg | 8 +++++--- source/engine/renderparticles.cpp | 2 +- 8 files changed, 52 insertions(+), 32 deletions(-) diff --git a/config/ui/hud/gamehud.cfg b/config/ui/hud/gamehud.cfg index f62a3dcdf..42f5ca6c7 100644 --- a/config/ui/hud/gamehud.cfg +++ b/config/ui/hud/gamehud.cfg @@ -735,7 +735,9 @@ newui "scoreboard" [ uiFastImg (fade (? $arg1 0.5 0.25)) "hud/" "shelf" "" $uiPad:5XL uiFastImg "hud/" "glow" "" $uiPad:5XL push n (+ $n 1) [ - uifontcolortext "default" $n (|A! (? $arg1 0xB0 0x60)) 0.55 + uifontoutline 1 0x7F [ + uifontcolortext "default" $n (|A! (? $arg1 0xB0 0x60)) 0.55 + ] ] ] ; uialign- -1 ] @@ -785,16 +787,18 @@ newui ".killUI" [ uivlist 0 [ uifill 0 0.3 t = (getrespawnwait) - uispace 0.02 0.02 [ - uifonttext "wide" $lasthudkillinfo $uiPad:USS - ] - uispace 0 0 [ - uioffset 0 $uiPad:D2XL- [ - if (m_invasion $getmode) [ - uifonttext "wide" (+s "^f4Lives remaining: " $getclientlives) $uiPad:USS - ] [ - uifontcolortext "wide" (? $t $t " ") (? (< (getmillis) (+ 100 (getlastspawnattempt))) $c_red $c_white) 1 - ] + uifontoutline 1 0x7F [ + uispace 0.02 0.02 [ + uifonttext "wide" $lasthudkillinfo $uiPad:USS + ] + uispace 0 0 [ + uioffset 0 $uiPad:D2XL- [ + if (m_invasion $getmode) [ + uifonttext "wide" (+s "^f4Lives remaining: " $getclientlives) $uiPad:USS + ] [ + uifontcolortext "wide" (? $t $t " ") (? (< (getmillis) (+ 100 (getlastspawnattempt))) $c_red $c_white) 1 + ] + ] ] ] ] diff --git a/config/ui/hud/mapmodel_browser.cfg b/config/ui/hud/mapmodel_browser.cfg index dcc7e1e34..08f8a6c19 100644 --- a/config/ui/hud/mapmodel_browser.cfg +++ b/config/ui/hud/mapmodel_browser.cfg @@ -54,11 +54,11 @@ uiLiteMenu "mapmodel_browser" [ uimodelpreview (mapmodelname $i 1) "mapmodel" uiclamp*e uispace $uiPad:S $uiPad:O3 [ - uifonttext "default" $i 0.55 + uifontoutline 1 0x7F [ uifonttext "default" $i 0.55 ] ] ; uialign- -1 -1 if (iskeyheld "LSHIFT") [ uispace $uiPad:S 0 [ - uifonttext "webdings" "q" 0.65 + uifontoutline 1 0x7F [ uifonttext "webdings" "q" 0.65 ] ] ; uialign- -1 1 uirelease [ clearmodel (+s "mapmodel/" (mapmodelname $i)) ] ] [ @@ -77,14 +77,14 @@ uiLiteMenu "mapmodel_browser" [ uimodelpreview (mapmodelname $i 1) "mapmodel" uiclamp*e uispace $uiPad:S $uiPad:O3 [ - uifontcolortext "default" $i (|A! 0xA0) 0.55 + uifontoutline 1 0x7F [ uifontcolortext "default" $i (|A! 0xA0) 0.55 ] ] ; uialign- -1 -1 uihover [ .mm_selidx = $n ; uiSetMillis ] uioutline $c_line1 $.z $.z ] if (mapmodelloaded $i) [ uispace $uiPad:S $uiPad:O3 [ - uifonttext "webdings" "a" 0.55 + uifontoutline 1 0x7F [ uifonttext "webdings" "a" 0.55 ] ] ; uialign- 1 -1 ] uiHoverOnce [ uiHoverSound ] diff --git a/config/ui/hud/texture_browser.cfg b/config/ui/hud/texture_browser.cfg index 3757f6632..8061c0d25 100644 --- a/config/ui/hud/texture_browser.cfg +++ b/config/ui/hud/texture_browser.cfg @@ -29,7 +29,9 @@ uiLiteMenu "texture_browser" [ ] uispace $uiPad:S $uiPad:O3 [ uialign -1 -1 - uifontcolortext "default" $i (|A! 0x98) 0.55 + uifontoutline 1 0x7F [ + uifontcolortext "default" $i (|A! 0x98) 0.55 + ] ] ] uipress [ uiSetMillis ] @@ -83,7 +85,7 @@ uiLiteMenu "texture_browser" [ uiclip 0 $uiPad:6XL [ uicolortext "/" $c_gray 1.2 ] ] // text takes up a lot of vertical space, have to "cut" it uifill $uiPad:DXS 0 [ - uifonttext "default" (max $.tb_totalpg 1) 0.7 + uifonttext "mono" (max $.tb_totalpg 1) 0.7 uialign- -1 1 ] uiclamp*y @@ -110,7 +112,9 @@ uiLiteMenu "texture_browser" [ uislotview $i uiclamp*e uispace $uiPad:S $uiPad:O3 [ - uifontcolortext "default" $i (|A! 0xA0) 0.55 + uifontoutline 1 0x7F [ + uifontcolortext "default" $i (|A! 0xA0) 0.55 + ] ] ; uialign- -1 -1 uirelease [ settex (getslottex $i) diff --git a/config/ui/lib.cfg b/config/ui/lib.cfg index fe55a3bdf..fde3ee2e5 100644 --- a/config/ui/lib.cfg +++ b/config/ui/lib.cfg @@ -847,9 +847,11 @@ uiVerSld = [ uiclamp*x ] ] ; uiclamp-x - uivlist $uiPad:M- [ - loop i (strlen $arg6) [ - uifontcolortext "default" (substr $arg6 $i 1) (|A! (? $arg9 0xA0 0x60)) 0.65 + uifontoutline 1 0xFF [ + uivlist $uiPad:M- [ + loop i (strlen $arg6) [ + uifontcolortext "default" (substr $arg6 $i 1) (|A! (? $arg9 0xA0 0x60)) 0.65 + ] ] ] ] @@ -878,7 +880,9 @@ uiHorSld = [ uiclamp*y ] ] ; uiclamp-y - uifontcolortext "default" $arg6 (|A! (? $arg9 0xA0 0x60)) 0.65 + uifontoutline 1 0xFF [ + uifontcolortext "default" $arg6 (|A! (? $arg9 0xA0 0x60)) 0.65 + ] ] uiclamp*e ] @@ -906,9 +910,11 @@ uiVerColorSld = [ uiclamp*x ] ] ; uiclamp-x - uivlist $uiPad:M- [ - loop i (strlen $$arg1) [ - uifontcolortext "wide" (substr $$arg1 $i 1) (|A! (? $arg9 0xA0 0x60)) 0.7 + uifontoutline 1 0xFF [ + uivlist $uiPad:M- [ + loop i (strlen $$arg1) [ + uifontcolortext "wide" (substr $$arg1 $i 1) (|A! (? $arg9 0xA0 0x60)) 0.7 + ] ] ] ] @@ -938,7 +944,9 @@ uiHorColorSld = [ uiclamp*y ] ] ; uiclamp-y - uifontcolortext "wide" $$arg1 (|A! (? $arg9 0xA0 0x60)) 0.7 + uifontoutline 1 0xFF [ + uifontcolortext "wide" $$arg1 (|A! (? $arg9 0xA0 0x60)) 0.7 + ] ] uiclamp*e ] diff --git a/config/ui/libnew.cfg b/config/ui/libnew.cfg index 8cfa6e6c9..ea98d4869 100644 --- a/config/ui/libnew.cfg +++ b/config/ui/libnew.cfg @@ -170,7 +170,9 @@ uiSliderH = [ uiFastImgStretched "" "shadow2" "" "" [ uiclamp.x ] uiclamp*e if $arg9 [ arg10 = 0xA0FFFFFF ] [ arg10 = 0x60FFFFFF ] - uiFontColorText "default" $arg6 0.65 $arg10 + uifontoutline 1 0xFF [ + uiFontColorText "default" $arg6 0.65 $arg10 + ] ] ; uiclamp-y ] ] ; uiclamp-y diff --git a/config/ui/menus/server_browser.cfg b/config/ui/menus/server_browser.cfg index 07cb060eb..6af1696f2 100644 --- a/config/ui/menus/server_browser.cfg +++ b/config/ui/menus/server_browser.cfg @@ -163,7 +163,7 @@ uiServer = [ uigroup [ uifill $uiPad:DS uiimage (+s "data/interface/ui/" (servinfomastermodeicon $arg1) ".png") $uiPad:DSS $uiPad:DSS - uispace 0 $uiPad:L [ uifonttext "wide" (servinfomodename $arg1) 0.5 ] + uispace 0 $uiPad:L [ uifontoutline 1 0x7F [ uifonttext "wide" (servinfomodename $arg1) 0.5 ] ] uialign- 0 1 ] uivlist $uiPad:S [ diff --git a/config/ui/permanent.cfg b/config/ui/permanent.cfg index 8fc00f00b..10c117c91 100644 --- a/config/ui/permanent.cfg +++ b/config/ui/permanent.cfg @@ -27,9 +27,11 @@ newui "permanent" [ uispace $uiPad:4XL $uiPad:4XL [ uivlist $uiPad:3XL- [ uiFastImg "" "badge_tesseract" "" "" $uiPad:D2XL - uivlist $uiPad:M- [ - uifontcolortext "wide" "TESSERACT" $c_cyan_t 0.6 - uifontcolortext "wide" "ENGINE" $c_cyan_t 0.7 + uifontoutline 1 0x7F [ + uivlist $uiPad:M- [ + uifontcolortext "wide" "TESSERACT" $c_cyan_t 0.6 + uifontcolortext "wide" "ENGINE" $c_cyan_t 0.7 + ] ] ] ] diff --git a/source/engine/renderparticles.cpp b/source/engine/renderparticles.cpp index f02a9488b..98e8ab52e 100644 --- a/source/engine/renderparticles.cpp +++ b/source/engine/renderparticles.cpp @@ -516,7 +516,7 @@ struct textrenderer : listrenderer setfontsize(hudh / PARTICLETEXTROWS); textinfo info; - prepare_text_particle(p->text, info, p->color, 1, bvec(0, 0, 0), p->language); + prepare_text_particle(p->text, info, p->color, FONTH / 32.f, bvec(0, 0, 0), 255, p->language); if(!info.tex) { popfont(); From 9ec29e8bd432ed1e72f2b2fa71bb8535e40bd1c1 Mon Sep 17 00:00:00 2001 From: Jed- Date: Sun, 3 Nov 2024 16:08:24 +0100 Subject: [PATCH 09/68] Add `conoutline` variable --- source/engine/console.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/source/engine/console.cpp b/source/engine/console.cpp index f1c483031..0b47114ba 100644 --- a/source/engine/console.cpp +++ b/source/engine/console.cpp @@ -103,8 +103,9 @@ ICOMMAND(fullconsole, "iN$", (int *val, int *numargs, ident *id), }); ICOMMAND(toggleconsole, "", (), UI::toggleui("fullconsole")); -// applies a black shadow to console text to improve visibility, the value controls the intensity of the shadow -VARP(conshadow, 0, 255, 255); +// apply a black shadow or outline to console text to improve visibility +VARFP(conshadow, 0, 255, 255, clearconsoletextures()); +VARFP(conoutline, 0, 0, 255, clearconsoletextures()); float rendercommand(float x, float y, float w) { @@ -117,7 +118,7 @@ float rendercommand(float x, float y, float w) pushfont(); setfont("default"); textinfo info; - prepare_text(buf, info, w, bvec(255, 255, 255), commandpos>=0 ? commandpos+1 + strlen(prompt) : strlen(buf)); + prepare_text(buf, info, w, bvec(255, 255, 255), commandpos>=0 ? commandpos+1 + strlen(prompt) : strlen(buf), conoutline ? ceil(FONTH / 32.f) : 0, bvec(0, 0, 0), conoutline); y -= info.h; if(info.tex) @@ -239,7 +240,7 @@ float drawconlines(int conskip, int confade, float conwidth, float conheight, fl if(conlines[idx].w != conwidth || conlines[idx].fontsize != fontsize || !info.tex) { if(info.tex) glDeleteTextures(1, &info.tex); - prepare_text(line, info, conwidth); + prepare_text(line, info, conwidth, bvec(255, 255, 255), -1, conoutline ? ceil(FONTH / 32.f) : 0, bvec(0, 0, 0), conoutline); conlines[idx].w = conwidth; conlines[idx].fontsize = fontsize; } From fed430969f0f096dbafce9301072f72bde2e7f51 Mon Sep 17 00:00:00 2001 From: Jed- Date: Sun, 3 Nov 2024 16:12:57 +0100 Subject: [PATCH 10/68] Remove overline support Drop the minimum supported version of `pango` to 1.38 Remove unnecessary `#include` --- source/Makefile | 8 ++++---- source/engine/rendertext.cpp | 12 ++---------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/source/Makefile b/source/Makefile index bf8b93e12..b23ee6ca1 100644 --- a/source/Makefile +++ b/source/Makefile @@ -395,12 +395,12 @@ engine/rendersky.o: shared/glexts.h shared/glemu.h engine/sound.h engine/rendersky.o: shared/iengine.h shared/igame.h engine/world.h engine/rendersky.o: engine/octa.h engine/light.h engine/texture.h engine/rendersky.o: engine/bih.h engine/model.h -engine/rendertext.o: shared/unicode.h shared/cube.h shared/tools.h +engine/rendertext.o: engine/engine.h shared/cube.h shared/tools.h engine/rendertext.o: shared/geom.h shared/ents.h shared/command.h engine/rendertext.o: shared/glexts.h shared/glemu.h engine/sound.h -engine/rendertext.o: shared/iengine.h shared/igame.h engine/engine.h -engine/rendertext.o: engine/world.h engine/octa.h engine/light.h -engine/rendertext.o: engine/texture.h engine/bih.h engine/model.h +engine/rendertext.o: shared/iengine.h shared/igame.h engine/world.h +engine/rendertext.o: engine/octa.h engine/light.h engine/texture.h +engine/rendertext.o: engine/bih.h engine/model.h engine/renderva.o: engine/engine.h shared/cube.h shared/tools.h shared/geom.h engine/renderva.o: shared/ents.h shared/command.h shared/glexts.h engine/renderva.o: shared/glemu.h engine/sound.h shared/iengine.h diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index 10a553d78..17ded6783 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -1,4 +1,3 @@ -#include "unicode.h" #include "engine.h" #include @@ -305,8 +304,8 @@ static inline void add_text_to_layout(const char *markup, int len, PangoLayout * attr = pango_attr_foreground_new(tcolor.r * 257, tcolor.g * 257, tcolor.b * 257); if(attr) attr->start_index = 0; - int begin_bold = -1, begin_italic = -1, begin_underline = -1, begin_strikethrough = -1, begin_overline = -1; - int n_bold = 0, n_italic = 0, n_underline = 0, n_strikethrough = 0, n_overline = 0; + int begin_bold = -1, begin_italic = -1, begin_underline = -1, begin_strikethrough = -1; + int n_bold = 0, n_italic = 0, n_underline = 0, n_strikethrough = 0; // parse markup int i = 0, j = 0; @@ -320,7 +319,6 @@ static inline void add_text_to_layout(const char *markup, int len, PangoLayout * MARKUP_CASE('i', 'I', pango_attr_style_new , PANGO_STYLE_ITALIC , italic); MARKUP_CASE('u', 'U', pango_attr_underline_new , PANGO_UNDERLINE_SINGLE, underline); MARKUP_CASE('t', 'T', pango_attr_strikethrough_new, TRUE , strikethrough); - MARKUP_CASE('o', 'O', pango_attr_overline_new , PANGO_OVERLINE_SINGLE , overline); // pango 1.46 default: { tcolor = text_color(markup[i+1], colorstack, sizeof(colorstack), cpos, initial_color); @@ -377,12 +375,6 @@ static inline void add_text_to_layout(const char *markup, int len, PangoLayout * m->start_index = begin_strikethrough; m->end_index = j; pango_attr_list_insert(list, m); } - if(begin_overline >= 0) - { - m = pango_attr_overline_new(PANGO_OVERLINE_SINGLE); - m->start_index = begin_overline; m->end_index = j; - pango_attr_list_insert(list, m); - } pango_layout_set_text(layout, text, -1); delete[] text; From 7b7743f7ffe69ee18e711e46276b0639a32a6201 Mon Sep 17 00:00:00 2001 From: Jed- Date: Sun, 3 Nov 2024 16:34:46 +0100 Subject: [PATCH 11/68] Remove `fontletterspacing` --- config/font.cfg | 1 - source/engine/rendertext.cpp | 13 +------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/config/font.cfg b/config/font.cfg index 7e3cde6b3..a16bb8245 100644 --- a/config/font.cfg +++ b/config/font.cfg @@ -8,7 +8,6 @@ // fontstretch -4..4 // < 0 = tighter, > 0 = wider ; many fonts don't support this // fontstyle 0|1|2 // 0 = normal, 1 = oblique, 2 = italic // fontsmallcaps 0|1 - // fontletterspacing val // float; < 0 = tighter, > 0 = wider // fontfeatures [...features] // OpenType features (in CSS format) // fontvariations "variations" // for variable fonts; format: "AXIS1=VALUE,AXIS2=VALUE..." diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index 17ded6783..827ff2313 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -70,10 +70,9 @@ struct font char *name; int id; string features; // OpenType features - float letter_spacing; PangoFontDescription *desc; - font() : name(NULL), letter_spacing(0), desc(NULL) { features[0] = '\0'; }; + font() : name(NULL), desc(NULL) { features[0] = '\0'; }; ~font() { DELETEA(name); @@ -155,9 +154,6 @@ void fontsmallcaps(int *val) } COMMAND(fontsmallcaps, "i"); -void fontletterspacing(float *val) { if(lastfont) lastfont->letter_spacing = *val; } -COMMAND(fontletterspacing, "f"); - void fontfeatures(char *features) { if(lastfont) copystring(lastfont->features, features, MAXSTRLEN); } COMMAND(fontfeatures, "s"); @@ -286,13 +282,6 @@ static inline void add_text_to_layout(const char *markup, int len, PangoLayout * pango_attr_list_insert(list, attr); } - // letter spacing - if(curfont->letter_spacing != 0) - { - attr = pango_attr_letter_spacing_new(curfont->letter_spacing * PANGO_SCALE); // pango 1.6 - pango_attr_list_insert(list, attr); - } - // language if(language) { From f670ccddcaed05ee422bb29698ec04e2063cb7a5 Mon Sep 17 00:00:00 2001 From: Jed- Date: Sun, 3 Nov 2024 16:36:10 +0100 Subject: [PATCH 12/68] Missed one --- config/ui/hud/geo_prefab.cfg | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/config/ui/hud/geo_prefab.cfg b/config/ui/hud/geo_prefab.cfg index 3de59c16d..9e8eb1182 100644 --- a/config/ui/hud/geo_prefab.cfg +++ b/config/ui/hud/geo_prefab.cfg @@ -24,7 +24,9 @@ uiLiteMenu "geo_prefab" [ uiclamp*e uispace $uiPad:S $uiPad:O3 [ uialign -1 -1 - uifontcolortext "default" $i (|A! 0x98) 0.55 + uifontoutline 1 0x7F [ + uifontcolortext "default" $i (|A! 0x98) 0.55 + ] ] uihover [ if (!= $i $.UI_obrsel) [ From 9c84f9c03a802e66b7d7ffa2280616c4360b24ae Mon Sep 17 00:00:00 2001 From: Jed- Date: Sun, 3 Nov 2024 16:38:20 +0100 Subject: [PATCH 13/68] Remove debug logs --- source/engine/console.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/engine/console.cpp b/source/engine/console.cpp index 0b47114ba..7e907cde1 100644 --- a/source/engine/console.cpp +++ b/source/engine/console.cpp @@ -172,12 +172,10 @@ ICOMMAND(clearconsole, "", (), { while(conlines.length()) { - logoutf("len=%d", conlines.length()); cline &cl = conlines.pop(); delete[] cl.line; if(cl.info.tex) { - logoutf("deleting texture %d", cl.info.tex); glDeleteTextures(1, &cl.info.tex); } } From e21e2f9f70df714dff3af57a54fda08c0a04d42b Mon Sep 17 00:00:00 2001 From: Jed- Date: Mon, 4 Nov 2024 14:57:51 +0100 Subject: [PATCH 14/68] Mark player names as duplicates if they are visually identical, even if the strings are different --- source/Makefile | 77 +++++++++++++++++------------- source/game/game.cpp | 6 +-- source/game/game.h | 15 ++++++ source/game/gameserver.cpp | 4 +- source/shared/tools.h | 97 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 160 insertions(+), 39 deletions(-) diff --git a/source/Makefile b/source/Makefile index b23ee6ca1..f404a0503 100644 --- a/source/Makefile +++ b/source/Makefile @@ -459,54 +459,62 @@ engine/worldio.o: shared/igame.h engine/world.h engine/octa.h engine/light.h engine/worldio.o: engine/texture.h engine/bih.h engine/model.h game/ai.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/ai.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h -game/ai.o: engine/sound.h shared/iengine.h shared/igame.h game/weapon.h -game/ai.o: game/ai.h game/gamemode.h game/entity.h game/monster.h +game/ai.o: engine/sound.h shared/iengine.h shared/igame.h shared/unicode.h +game/ai.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h +game/ai.o: game/monster.h game/gameclient.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/gameclient.o: shared/ents.h shared/command.h shared/glexts.h game/gameclient.o: shared/glemu.h engine/sound.h shared/iengine.h -game/gameclient.o: shared/igame.h game/weapon.h game/ai.h game/gamemode.h -game/gameclient.o: game/entity.h game/monster.h game/ctf.h game/elimination.h +game/gameclient.o: shared/igame.h shared/unicode.h game/weapon.h game/ai.h +game/gameclient.o: game/gamemode.h game/entity.h game/monster.h game/ctf.h +game/gameclient.o: game/elimination.h game/entity.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/entity.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h -game/entity.o: engine/sound.h shared/iengine.h shared/igame.h game/weapon.h -game/entity.o: game/ai.h game/gamemode.h game/entity.h game/monster.h +game/entity.o: engine/sound.h shared/iengine.h shared/igame.h +game/entity.o: shared/unicode.h game/weapon.h game/ai.h game/gamemode.h +game/entity.o: game/entity.h game/monster.h game/game.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/game.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h -game/game.o: engine/sound.h shared/iengine.h shared/igame.h game/weapon.h -game/game.o: game/ai.h game/gamemode.h game/entity.h game/monster.h +game/game.o: engine/sound.h shared/iengine.h shared/igame.h shared/unicode.h +game/game.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h +game/game.o: game/monster.h game/render.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/render.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h -game/render.o: engine/sound.h shared/iengine.h shared/igame.h game/weapon.h -game/render.o: game/ai.h game/gamemode.h game/entity.h game/monster.h +game/render.o: engine/sound.h shared/iengine.h shared/igame.h +game/render.o: shared/unicode.h game/weapon.h game/ai.h game/gamemode.h +game/render.o: game/entity.h game/monster.h game/scoreboard.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/scoreboard.o: shared/ents.h shared/command.h shared/glexts.h game/scoreboard.o: shared/glemu.h engine/sound.h shared/iengine.h -game/scoreboard.o: shared/igame.h game/weapon.h game/ai.h game/gamemode.h -game/scoreboard.o: game/entity.h game/monster.h +game/scoreboard.o: shared/igame.h shared/unicode.h game/weapon.h game/ai.h +game/scoreboard.o: game/gamemode.h game/entity.h game/monster.h game/gameserver.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/gameserver.o: shared/ents.h shared/command.h shared/glexts.h game/gameserver.o: shared/glemu.h engine/sound.h shared/iengine.h -game/gameserver.o: shared/igame.h game/weapon.h game/ai.h game/gamemode.h -game/gameserver.o: game/entity.h game/monster.h game/geoip.h game/ctf.h -game/gameserver.o: game/elimination.h game/extinfo.h game/aimanager.h +game/gameserver.o: shared/igame.h shared/unicode.h game/weapon.h game/ai.h +game/gameserver.o: game/gamemode.h game/entity.h game/monster.h game/geoip.h +game/gameserver.o: game/ctf.h game/elimination.h game/extinfo.h +game/gameserver.o: game/aimanager.h game/waypoint.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/waypoint.o: shared/ents.h shared/command.h shared/glexts.h game/waypoint.o: shared/glemu.h engine/sound.h shared/iengine.h -game/waypoint.o: shared/igame.h game/weapon.h game/ai.h game/gamemode.h -game/waypoint.o: game/entity.h game/monster.h +game/waypoint.o: shared/igame.h shared/unicode.h game/weapon.h game/ai.h +game/waypoint.o: game/gamemode.h game/entity.h game/monster.h game/monster.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/monster.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h -game/monster.o: engine/sound.h shared/iengine.h shared/igame.h game/weapon.h -game/monster.o: game/ai.h game/gamemode.h game/entity.h game/monster.h +game/monster.o: engine/sound.h shared/iengine.h shared/igame.h +game/monster.o: shared/unicode.h game/weapon.h game/ai.h game/gamemode.h +game/monster.o: game/entity.h game/monster.h game/weapon.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/weapon.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h -game/weapon.o: engine/sound.h shared/iengine.h shared/igame.h game/weapon.h -game/weapon.o: game/ai.h game/gamemode.h game/entity.h game/monster.h +game/weapon.o: engine/sound.h shared/iengine.h shared/igame.h +game/weapon.o: shared/unicode.h game/weapon.h game/ai.h game/gamemode.h +game/weapon.o: game/entity.h game/monster.h game/gamephysics.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/gamephysics.o: shared/ents.h shared/command.h shared/glexts.h game/gamephysics.o: shared/glemu.h engine/sound.h shared/iengine.h -game/gamephysics.o: shared/igame.h game/weapon.h game/ai.h game/gamemode.h -game/gamephysics.o: game/entity.h game/monster.h +game/gamephysics.o: shared/igame.h shared/unicode.h game/weapon.h game/ai.h +game/gamephysics.o: game/gamemode.h game/entity.h game/monster.h shared/cube.h.gch: shared/tools.h shared/geom.h shared/ents.h shared/cube.h.gch: shared/command.h shared/glexts.h shared/glemu.h @@ -518,8 +526,9 @@ engine/engine.h.gch: engine/world.h engine/octa.h engine/light.h engine/engine.h.gch: engine/texture.h engine/bih.h engine/model.h game/game.h.gch: shared/cube.h shared/tools.h shared/geom.h shared/ents.h game/game.h.gch: shared/command.h shared/glexts.h shared/glemu.h -game/game.h.gch: engine/sound.h shared/iengine.h shared/igame.h game/weapon.h -game/game.h.gch: game/ai.h game/gamemode.h game/entity.h game/monster.h +game/game.h.gch: engine/sound.h shared/iengine.h shared/igame.h +game/game.h.gch: shared/unicode.h game/weapon.h game/ai.h game/gamemode.h +game/game.h.gch: game/entity.h game/monster.h standalone/shared/crypto.o: shared/cube.h shared/tools.h shared/geom.h standalone/shared/crypto.o: shared/ents.h shared/command.h engine/sound.h @@ -545,15 +554,15 @@ standalone/engine/worldio.o: engine/world.h standalone/game/entity.o: game/game.h shared/cube.h shared/tools.h standalone/game/entity.o: shared/geom.h shared/ents.h shared/command.h standalone/game/entity.o: engine/sound.h shared/iengine.h shared/igame.h -standalone/game/entity.o: game/weapon.h game/ai.h game/gamemode.h -standalone/game/entity.o: game/entity.h game/monster.h +standalone/game/entity.o: shared/unicode.h game/weapon.h game/ai.h +standalone/game/entity.o: game/gamemode.h game/entity.h game/monster.h standalone/game/gameserver.o: game/game.h shared/cube.h shared/tools.h standalone/game/gameserver.o: shared/geom.h shared/ents.h shared/command.h standalone/game/gameserver.o: engine/sound.h shared/iengine.h shared/igame.h -standalone/game/gameserver.o: game/weapon.h game/ai.h game/gamemode.h -standalone/game/gameserver.o: game/entity.h game/monster.h game/geoip.h -standalone/game/gameserver.o: game/ctf.h game/elimination.h game/extinfo.h -standalone/game/gameserver.o: game/aimanager.h +standalone/game/gameserver.o: shared/unicode.h game/weapon.h game/ai.h +standalone/game/gameserver.o: game/gamemode.h game/entity.h game/monster.h +standalone/game/gameserver.o: game/geoip.h game/ctf.h game/elimination.h +standalone/game/gameserver.o: game/extinfo.h game/aimanager.h standalone/engine/master.o: shared/cube.h shared/tools.h shared/geom.h standalone/engine/master.o: shared/ents.h shared/command.h engine/sound.h standalone/engine/master.o: shared/iengine.h shared/igame.h @@ -567,6 +576,6 @@ standalone/engine/engine.h.gch: shared/iengine.h shared/igame.h standalone/engine/engine.h.gch: engine/world.h standalone/game/game.h.gch: shared/cube.h shared/tools.h shared/geom.h standalone/game/game.h.gch: shared/ents.h shared/command.h engine/sound.h -standalone/game/game.h.gch: shared/iengine.h shared/igame.h game/weapon.h -standalone/game/game.h.gch: game/ai.h game/gamemode.h game/entity.h -standalone/game/game.h.gch: game/monster.h +standalone/game/game.h.gch: shared/iengine.h shared/igame.h shared/unicode.h +standalone/game/game.h.gch: game/weapon.h game/ai.h game/gamemode.h +standalone/game/game.h.gch: game/entity.h game/monster.h diff --git a/source/game/game.cpp b/source/game/game.cpp index 9d4a7ddce..437547f7d 100644 --- a/source/game/game.cpp +++ b/source/game/game.cpp @@ -998,11 +998,11 @@ namespace game return NULL; } - bool duplicatename(gameent *d, const char *name = NULL, const char *alt = NULL) + inline bool duplicatename(gameent *d, const char *name = NULL, const char *alt = NULL) { if(!name) name = d->name; - if(alt && d != self && !strcmp(name, alt)) return true; - loopv(players) if(d!=players[i] && !strcmp(name, players[i]->name)) return true; + if(alt && d != self && duplicatestring(name, alt)) return true; + loopv(players) if(d!=players[i] && duplicatestring(name, players[i]->name)) return true; return false; } diff --git a/source/game/game.h b/source/game/game.h index a9248c6d5..03e0c456a 100644 --- a/source/game/game.h +++ b/source/game/game.h @@ -2,6 +2,7 @@ #define __GAME_H__ #include "cube.h" +#include "unicode.h" // animations @@ -609,6 +610,20 @@ namespace physics extern int liquidtransition(physent* d, int material, bool isinwater); } +// checks if two strings are visually identical or very similar +static inline bool duplicatestring(const char *a, const char *b) +{ + const uint alen = uni_strlen(a); + if(alen != uni_strlen(b)) return false; + uint c, d; + size_t s = uni_getchar(a, c), z = uni_getchar(b, d); + for(const char *p = a, *q = b; c; p += s, q += z, s = uni_getchar(p, c), z = uni_getchar(q, d)) + { + if(homoglyph(c) != homoglyph(d)) return false; + } + return true; +} + namespace game { extern int gamemode, mutators; diff --git a/source/game/gameserver.cpp b/source/game/gameserver.cpp index edfc3f796..b48f81949 100644 --- a/source/game/gameserver.cpp +++ b/source/game/gameserver.cpp @@ -860,10 +860,10 @@ namespace server return n; } - bool duplicatename(clientinfo *ci, const char *name) + inline bool duplicatename(clientinfo *ci, const char *name) { if(!name) name = ci->name; - loopv(clients) if(clients[i]!=ci && !strcmp(name, clients[i]->name)) return true; + loopv(clients) if(clients[i]!=ci && duplicatestring(name, clients[i]->name)) return true; return false; } diff --git a/source/shared/tools.h b/source/shared/tools.h index c605700d6..0678b3a2e 100644 --- a/source/shared/tools.h +++ b/source/shared/tools.h @@ -1424,6 +1424,103 @@ static inline int iscubenamesafe(uint c) || (c >= 0x1EA0 && c <= 0x1EF9) // Latin Extended Additional: Vietnamese ; } +// used to detect duplicate names +static inline uint homoglyph(uint c) +{ + switch(c) + { + // latin => latin + case 'l' : return 'I'; + case 0x138: return 'k'; + case 0x13A: return 0xED; + case 0x15E: return 0x218; + case 0x15F: return 0x219; + case 0x162: return 0x21A; + case 0x163: return 0x21B; + + // greek => latin + case 0x391: return 'A'; + case 0x392: return 'B'; + case 0x395: return 'E'; + case 0x396: return 'Z'; + case 0x397: return 'H'; + case 0x399: return 'I'; + case 0x39A: return 'K'; + case 0x39C: return 'M'; + case 0x39D: return 'N'; + case 0x39F: return 'O'; + case 0x3A1: return 'P'; + case 0x3A4: return 'T'; + case 0x3A5: return 'Y'; + case 0x3A7: return 'X'; + case 0x3AA: return 0xCF; + case 0x3AB: return 0x178; + case 0x3AE: return 0x144; + case 0x3AF: return 0xED; + case 0x3B2: return 0xDF; + case 0x3B3: return 'y'; + case 0x3B7: return 'n'; + case 0x3B9: return 0x131; + case 0x3BA: return 'k'; + case 0x3BD: return 'v'; + case 0x3BF: return 'o'; + case 0x3C1: return 'p'; + case 0x3C2: return 0xE7; + case 0x3C5: return 'u'; + case 0x3C7: return 'x'; + case 0x3CA: return 0xEF; + case 0x3CB: return 0xFC; + case 0x3CC: return 0xF3; + case 0x3CD: return 0xFA; + + // cyrillic => latin + case 0x400: return 0xC8; + case 0x401: return 0xCB; + case 0x405: return 'S'; + case 0x406: return 'I'; + case 0x407: return 0xCF; + case 0x408: return 'J'; + case 0x410: return 'A'; + case 0x412: return 'B'; + case 0x415: return 'E'; + case 0x417: return '3'; + case 0x41A: return 'K'; + case 0x41C: return 'M'; + case 0x41D: return 'H'; + case 0x41E: return 'O'; + case 0x420: return 'P'; + case 0x421: return 'C'; + case 0x422: return 'T'; + case 0x425: return 'X'; + case 0x430: return 'a'; + case 0x431: return '6'; + case 0x435: return 'e'; + case 0x43A: return 'k'; + case 0x43E: return 'o'; + case 0x440: return 'p'; + case 0x441: return 'c'; + case 0x443: return 'y'; + case 0x445: return 'x'; + case 0x450: return 0xE8; + case 0x451: return 0xEB; + case 0x455: return 's'; + case 0x456: return 'i'; + case 0x457: return 0xEF; + case 0x458: return 'j'; + case 0x45B: return 0x127; + + // cyrillic => greek + case 0x413: return 0x393; + case 0x41B: return 0x39B; + case 0x41F: return 0x3A0; + case 0x424: return 0x3A6; + case 0x444: return 0x3C6; + + // greek => greek + case 0xB5: return 0x3BC; + } + return c; +} extern int cubecasecmp(const char *s1, const char *s2, int n = INT_MAX); static inline bool cubecaseequal(const char *s1, const char *s2, int n = INT_MAX) { return !cubecasecmp(s1, s2, n); } From 3d8dea2bad6edd6f11f62f78225a2d172cebb203 Mon Sep 17 00:00:00 2001 From: Jed- Date: Mon, 4 Nov 2024 15:04:10 +0100 Subject: [PATCH 15/68] Full console doesn't need shadow or outline --- source/engine/console.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/source/engine/console.cpp b/source/engine/console.cpp index 7e907cde1..67b6b60a6 100644 --- a/source/engine/console.cpp +++ b/source/engine/console.cpp @@ -191,7 +191,7 @@ void clearconsoletextures() } } -float drawconlines(int conskip, int confade, float conwidth, float conheight, float conoff, int maxlines, int filter, float y = 0, int dir = 1) +float drawconlines(int conskip, int confade, float conwidth, float conheight, float conoff, int maxlines, int shadow, int outline, int filter, float y = 0, int dir = 1) { filter &= CON_FLAGS; int numl = conlines.length(), offset = min(conskip, numl); @@ -238,17 +238,17 @@ float drawconlines(int conskip, int confade, float conwidth, float conheight, fl if(conlines[idx].w != conwidth || conlines[idx].fontsize != fontsize || !info.tex) { if(info.tex) glDeleteTextures(1, &info.tex); - prepare_text(line, info, conwidth, bvec(255, 255, 255), -1, conoutline ? ceil(FONTH / 32.f) : 0, bvec(0, 0, 0), conoutline); + prepare_text(line, info, conwidth, bvec(255, 255, 255), -1, outline ? ceil(FONTH / 32.f) : 0, bvec(0, 0, 0), outline); conlines[idx].w = conwidth; conlines[idx].fontsize = fontsize; } if(dir <= 0) y -= info.h; if(info.tex) { - if(conshadow) + if(shadow) { const float d = 3.f / 4.f * conscale; - draw_text(info, conoff-d, y+d, conshadow, true); + draw_text(info, conoff-d, y+d, shadow, true); } draw_text(info, conoff, y); } @@ -265,7 +265,7 @@ float renderfullconsole(float w, float h) float conpad = FONTH*1.5/2, conheight = h - 2*conpad, conwidth = w - 2*conpad; - drawconlines(conskip, 0, conwidth, conheight, conpad, 0, fullconfilter); + drawconlines(conskip, 0, conwidth, conheight, conpad, 0, 0, 0, fullconfilter); popfont(); return conheight + 2*conpad; } @@ -278,9 +278,9 @@ float renderconsole(float w, float h, float abovehud) pushfont(); setfont("default"); setfontsize(hudh * conscale / CONSOLETEXTROWS); - float y = drawconlines(conskip, confade, conwidth, conheight, conpad, consize, confilter); + float y = drawconlines(conskip, confade, conwidth, conheight, conpad, consize, conshadow, conoutline, confilter); if(miniconsize && miniconwidth) - drawconlines(miniconskip, miniconfade, (miniconwidth*(w - 2*conpad))/100, min(float(FONTH*1.5*miniconsize), abovehud - y), conpad, miniconsize, miniconfilter, abovehud, -1); + drawconlines(miniconskip, miniconfade, (miniconwidth*(w - 2*conpad))/100, min(float(FONTH*1.5*miniconsize), abovehud - y), conpad, miniconsize, conshadow, conoutline, miniconfilter, abovehud, -1); popfont(); return y; } From 65d167b004f9363114430ad2265daa717dd1c2c6 Mon Sep 17 00:00:00 2001 From: Jed- Date: Mon, 4 Nov 2024 15:25:14 +0100 Subject: [PATCH 16/68] Draw the outline around the cursor --- source/engine/rendertext.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index 827ff2313..b878ab481 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -462,6 +462,11 @@ void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_co const float curw = max(1.f, fontsize / 16); cairo_rectangle(cr, cursor_rect.x / PANGO_SCALE + outline_offset, cursor_rect.y / PANGO_SCALE + outline_offset, curw, cursor_rect.height / PANGO_SCALE); + if(outline) + { + cairo_set_source_rgba(cr, outline_color.r / 255.f, outline_color.g / 255, outline_color.b / 255.f, outline_alpha / 255.f); + cairo_stroke_preserve(cr); + } cairo_set_source_rgba(cr, cursorcolor.r / 255.f, cursorcolor.g / 255.f, cursorcolor.b / 255.f, 1.0); cairo_fill(cr); } From b9882125dd399ecaf143bee95737957b3664462b Mon Sep 17 00:00:00 2001 From: Jed- Date: Mon, 4 Nov 2024 16:18:08 +0100 Subject: [PATCH 17/68] Use official data from Unicode --- source/shared/tools.h | 173 +++++++++++++++++++----------------------- 1 file changed, 80 insertions(+), 93 deletions(-) diff --git a/source/shared/tools.h b/source/shared/tools.h index 0678b3a2e..98f2a77d9 100644 --- a/source/shared/tools.h +++ b/source/shared/tools.h @@ -1425,101 +1425,88 @@ static inline int iscubenamesafe(uint c) ; } // used to detect duplicate names +// source: https://www.unicode.org/Public/security/latest/confusables.txt static inline uint homoglyph(uint c) { - switch(c) - { - // latin => latin - case 'l' : return 'I'; - case 0x138: return 'k'; - case 0x13A: return 0xED; - case 0x15E: return 0x218; - case 0x15F: return 0x219; - case 0x162: return 0x21A; - case 0x163: return 0x21B; - - // greek => latin - case 0x391: return 'A'; - case 0x392: return 'B'; - case 0x395: return 'E'; - case 0x396: return 'Z'; - case 0x397: return 'H'; - case 0x399: return 'I'; - case 0x39A: return 'K'; - case 0x39C: return 'M'; - case 0x39D: return 'N'; - case 0x39F: return 'O'; - case 0x3A1: return 'P'; - case 0x3A4: return 'T'; - case 0x3A5: return 'Y'; - case 0x3A7: return 'X'; - case 0x3AA: return 0xCF; - case 0x3AB: return 0x178; - case 0x3AE: return 0x144; - case 0x3AF: return 0xED; - case 0x3B2: return 0xDF; - case 0x3B3: return 'y'; - case 0x3B7: return 'n'; - case 0x3B9: return 0x131; - case 0x3BA: return 'k'; - case 0x3BD: return 'v'; - case 0x3BF: return 'o'; - case 0x3C1: return 'p'; - case 0x3C2: return 0xE7; - case 0x3C5: return 'u'; - case 0x3C7: return 'x'; - case 0x3CA: return 0xEF; - case 0x3CB: return 0xFC; - case 0x3CC: return 0xF3; - case 0x3CD: return 0xFA; - - // cyrillic => latin - case 0x400: return 0xC8; - case 0x401: return 0xCB; - case 0x405: return 'S'; - case 0x406: return 'I'; - case 0x407: return 0xCF; - case 0x408: return 'J'; - case 0x410: return 'A'; - case 0x412: return 'B'; - case 0x415: return 'E'; - case 0x417: return '3'; - case 0x41A: return 'K'; - case 0x41C: return 'M'; - case 0x41D: return 'H'; - case 0x41E: return 'O'; - case 0x420: return 'P'; - case 0x421: return 'C'; - case 0x422: return 'T'; - case 0x425: return 'X'; - case 0x430: return 'a'; - case 0x431: return '6'; - case 0x435: return 'e'; - case 0x43A: return 'k'; - case 0x43E: return 'o'; - case 0x440: return 'p'; - case 0x441: return 'c'; - case 0x443: return 'y'; - case 0x445: return 'x'; - case 0x450: return 0xE8; - case 0x451: return 0xEB; - case 0x455: return 's'; - case 0x456: return 'i'; - case 0x457: return 0xEF; - case 0x458: return 'j'; - case 0x45B: return 0x127; - - // cyrillic => greek - case 0x413: return 0x393; - case 0x41B: return 0x39B; - case 0x41F: return 0x3A0; - case 0x424: return 0x3A6; - case 0x444: return 0x3C6; - - // greek => greek - case 0xB5: return 0x3BC; - } - return c; + switch(c) + { + case 0x00B8: return 0x002C; + case 0x0060: return 0x0027; + case 0x00B4: return 0x0027; + case 0x0417: return 0x0033; + case 0x0431: return 0x0036; + case 0x03B1: return 0x0061; + case 0x0430: return 0x0061; + case 0x0391: return 0x0041; + case 0x0410: return 0x0041; + case 0x042C: return 0x0062; + case 0x0392: return 0x0042; + case 0x0412: return 0x0042; + case 0x0441: return 0x0063; + case 0x0421: return 0x0043; + case 0x0435: return 0x0065; + case 0x0395: return 0x0045; + case 0x0415: return 0x0045; + case 0x011B: return 0x0115; + case 0x011A: return 0x0114; + case 0x017F: return 0x0066; + case 0x0397: return 0x0048; + case 0x041D: return 0x0048; + case 0x0131: return 0x0069; + case 0x03B9: return 0x0069; + case 0x0456: return 0x0069; + case 0x0458: return 0x006A; + case 0x0408: return 0x004A; + case 0x039A: return 0x004B; + case 0x041A: return 0x004B; + case 0x007C: return 0x006C; + case 0x0031: return 0x006C; + case 0x0049: return 0x006C; + case 0x0399: return 0x006C; + case 0x0406: return 0x006C; + case 0x039C: return 0x004D; + case 0x041C: return 0x004D; + case 0x039D: return 0x004E; + case 0x03BF: return 0x006F; + case 0x03C3: return 0x006F; + case 0x043E: return 0x006F; + case 0x0030: return 0x004F; + case 0x039F: return 0x004F; + case 0x041E: return 0x004F; + case 0x0150: return 0x00D6; + case 0x03C1: return 0x0070; + case 0x0440: return 0x0070; + case 0x03A1: return 0x0050; + case 0x0420: return 0x0050; + case 0x03BA: return 0x0138; + case 0x043A: return 0x0138; + case 0x0433: return 0x0072; + case 0x0455: return 0x0073; + case 0x0405: return 0x0053; + case 0x03B2: return 0x00DF; + case 0x03A4: return 0x0054; + case 0x0422: return 0x0054; + case 0x021A: return 0x0162; + case 0x03C5: return 0x0075; + case 0x03BD: return 0x0076; + case 0x00D7: return 0x0078; + case 0x0445: return 0x0078; + case 0x03A7: return 0x0058; + case 0x0425: return 0x0058; + case 0x03B3: return 0x0079; + case 0x0443: return 0x0079; + case 0x03A5: return 0x0059; + case 0x0423: return 0x0059; + case 0x0396: return 0x005A; + case 0x0413: return 0x0393; + case 0x00B5: return 0x03BC; + case 0x043F: return 0x03C0; + case 0x041F: return 0x03A0; + case 0x0424: return 0x03A6; + case 0x0419: return 0x040D; + case 0x045D: return 0x0439; + } + return c; } extern int cubecasecmp(const char *s1, const char *s2, int n = INT_MAX); From 7493b9a4947fb5ff914462cf79e6a32243a1acc0 Mon Sep 17 00:00:00 2001 From: Jed- Date: Mon, 4 Nov 2024 17:27:36 +0100 Subject: [PATCH 18/68] Add `uinofallback` command to disable falling back to system fonts --- source/engine/engine.h | 8 ++++---- source/engine/rendertext.cpp | 37 +++++++++++++++++++++--------------- source/engine/ui.cpp | 32 +++++++++++++++++++++++-------- source/shared/iengine.h | 4 ++-- 4 files changed, 52 insertions(+), 29 deletions(-) diff --git a/source/engine/engine.h b/source/engine/engine.h index bdcf6f357..cda393807 100644 --- a/source/engine/engine.h +++ b/source/engine/engine.h @@ -59,10 +59,10 @@ extern void done_pangocairo(); extern int getcurfontid(); extern void gettextres(int &w, int &h); extern void draw_text(textinfo info, float left, float top, int a = 255, bool black = false); -extern void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_color = bvec(255, 255, 255), int cursor = -1, float outline = 0, bvec outline_color = bvec(0, 0, 0), int outline_alpha = 255, int align = -1, int justify = 0, const char *language = NULL); -extern void prepare_text_particle(const char *str, textinfo &info, bvec initial_color = bvec(255, 255, 255), float outline = 0, bvec outline_color = bvec(0, 0, 0), int outline_alpha = 255, const char *language = NULL); -extern int text_visible(const char *str, float hitx, float hity, int maxwidth, int align = -1, int justify = 0, const char *language = NULL); -extern void text_pos(const char *str, int cursor, int &cx, int &cy, int maxwidth, int align = -1, int justify = 0, const char *language = NULL); +extern void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_color = bvec(255, 255, 255), int cursor = -1, float outline = 0, bvec outline_color = bvec(0, 0, 0), int outline_alpha = 255, int align = -1, int justify = 0, const char *language = NULL, bool no_fallback = false); +extern void prepare_text_particle(const char *str, textinfo &info, bvec initial_color = bvec(255, 255, 255), float outline = 0, bvec outline_color = bvec(0, 0, 0), int outline_alpha = 255, const char *language = NULL, bool no_fallback = false); +extern int text_visible(const char *str, float hitx, float hity, int maxwidth, int align = -1, int justify = 0, const char *language = NULL, bool no_fallback = false); +extern void text_pos(const char *str, int cursor, int &cx, int &cy, int maxwidth, int align = -1, int justify = 0, const char *language = NULL, bool no_fallback = false); extern void reloadfonts(); // texture diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index b878ab481..08e6985e9 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -262,7 +262,7 @@ static inline bvec text_color(char c, char *stack, int size, int &sp, bvec color // adds a string to the layout parsing basic markup (\f codes) // NOTE: `markup` is the original string with \f codes; `text` is the stripped version without \f codes -static inline void add_text_to_layout(const char *markup, int len, PangoLayout *layout, bvec initial_color, int *map_markup_to_text, int *map_text_to_markup, const char *language) +static inline void add_text_to_layout(const char *markup, int len, PangoLayout *layout, bvec initial_color, int *map_markup_to_text, int *map_text_to_markup, const char *language, bool no_fallback) { char *text = newstring(len); @@ -282,6 +282,13 @@ static inline void add_text_to_layout(const char *markup, int len, PangoLayout * pango_attr_list_insert(list, attr); } + // no fallback to system fonts + if(no_fallback) + { + attr = pango_attr_fallback_new(FALSE); + pango_attr_list_insert(list, attr); + } + // language if(language) { @@ -372,7 +379,7 @@ static inline void add_text_to_layout(const char *markup, int len, PangoLayout * } #undef MARKUP_CASE -static inline PangoLayout *measure_text_internal(const char *str, int len, int maxwidth, int align, int justify, bvec initial_color, int &width, int &height, int &offset, int *map_markup_to_text, int *map_text_to_markup, const char *language) +static inline PangoLayout *measure_text_internal(const char *str, int len, int maxwidth, int align, int justify, bvec initial_color, int &width, int &height, int &offset, int *map_markup_to_text, int *map_text_to_markup, const char *language, bool no_fallback) { // create cairo context cairo_t *cr = cairo_create(dummy_surface); @@ -400,7 +407,7 @@ static inline PangoLayout *measure_text_internal(const char *str, int len, int m pango_layout_set_justify(layout, justify ? TRUE : FALSE); } - add_text_to_layout(str, len, layout, initial_color, map_markup_to_text, map_text_to_markup, language); + add_text_to_layout(str, len, layout, initial_color, map_markup_to_text, map_text_to_markup, language, no_fallback); // get pixel size PangoRectangle r; @@ -412,20 +419,20 @@ static inline PangoLayout *measure_text_internal(const char *str, int len, int m cairo_destroy(cr); return layout; } -void measure_text(const char *str, int maxwidth, int &width, int &height, int align, int justify, const char *language) +void measure_text(const char *str, int maxwidth, int &width, int &height, int align, int justify, const char *language, bool no_fallback) { int _offset; - PangoLayout *layout = measure_text_internal(str, strlen(str), maxwidth, align, justify, bvec(0, 0, 0), width, height, _offset, NULL, NULL, language); + PangoLayout *layout = measure_text_internal(str, strlen(str), maxwidth, align, justify, bvec(0, 0, 0), width, height, _offset, NULL, NULL, language, no_fallback); if(layout) g_object_unref(layout); } -void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_color, int cursor, float outline, bvec outline_color, int outline_alpha, int align, int justify, const char *language) +void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_color, int cursor, float outline, bvec outline_color, int outline_alpha, int align, int justify, const char *language, bool no_fallback) { // get dimensions and pango layout int width, height, offset; const int len = strlen(str); int map_markup_to_text[len+1]; - PangoLayout *layout = measure_text_internal(str, len, maxwidth, align, justify, initial_color, width, height, offset, cursor >= 0 ? map_markup_to_text : NULL, NULL, language); + PangoLayout *layout = measure_text_internal(str, len, maxwidth, align, justify, initial_color, width, height, offset, cursor >= 0 ? map_markup_to_text : NULL, NULL, language, no_fallback); if(!layout) { info = {0, 0, 0}; return; } if(!width || !height) { g_object_unref(layout); info = {0, 0, 0}; return; } @@ -491,7 +498,7 @@ void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_co cairo_destroy(cr); cairo_surface_destroy(surface); } -void prepare_text_particle(const char *str, textinfo &info, bvec initial_color, float outline, bvec outline_color, int outline_alpha, const char *language) +void prepare_text_particle(const char *str, textinfo &info, bvec initial_color, float outline, bvec outline_color, int outline_alpha, const char *language, bool no_fallback) { const int c = initial_color.tohexcolor(), d = outline_color.tohexcolor(); const char *l = language ? language : ""; @@ -506,7 +513,7 @@ void prepare_text_particle(const char *str, textinfo &info, bvec initial_color, info = p.ti; return; } - prepare_text(str, p.ti, 0, initial_color, -1, outline, outline_color, outline_alpha, -1, 0, language); + prepare_text(str, p.ti, 0, initial_color, -1, outline, outline_color, outline_alpha, -1, 0, language, no_fallback); if(!p.ti.tex) { info = {0, 0, 0}; return; } if(particle_queue.length() >= 256) { @@ -554,10 +561,10 @@ void draw_text(textinfo info, float left, float top, int a, bool black) gle::end(); // NOTE: `info.tex` is not deleted here! } -void draw_text(const char *str, float left, float top, int r, int g, int b, int a, int maxwidth, int align, int justify, const char *language) +void draw_text(const char *str, float left, float top, int r, int g, int b, int a, int maxwidth, int align, int justify, const char *language, bool no_fallback) { textinfo info; - prepare_text(str, info, maxwidth, bvec(r, g, b), -1, 0, bvec(0, 0, 0), 0, align, justify, language); + prepare_text(str, info, maxwidth, bvec(r, g, b), -1, 0, bvec(0, 0, 0), 0, align, justify, language, no_fallback); if(!info.tex) return; draw_text(info, left, top, a); glDeleteTextures(1, &info.tex); @@ -586,13 +593,13 @@ void gettextres(int &w, int &h) } // used by the text editor -int text_visible(const char *str, float hitx, float hity, int maxwidth, int align, int justify, const char *language) +int text_visible(const char *str, float hitx, float hity, int maxwidth, int align, int justify, const char *language, bool no_fallback) { int width, height, _offset; const int len = strlen(str); if(!len) return 0; int map_text_to_markup[len+1]; - PangoLayout *layout = measure_text_internal(str, len, maxwidth, align, justify, bvec(0, 0, 0), width, height, _offset, NULL, map_text_to_markup, language); + PangoLayout *layout = measure_text_internal(str, len, maxwidth, align, justify, bvec(0, 0, 0), width, height, _offset, NULL, map_text_to_markup, language, no_fallback); if(!layout) return len; if(!width || !height) { g_object_unref(layout); return len; } @@ -604,13 +611,13 @@ int text_visible(const char *str, float hitx, float hity, int maxwidth, int alig } // used by the text editor -void text_pos(const char *str, int cursor, int &cx, int &cy, int maxwidth, int align, int justify, const char *language) +void text_pos(const char *str, int cursor, int &cx, int &cy, int maxwidth, int align, int justify, const char *language, bool no_fallback) { int width, height, _offset; const int len = strlen(str); if(!len) { cx = cy = 0; return; } int map_markup_to_text[len+1]; - PangoLayout *layout = measure_text_internal(str, len, maxwidth, align, justify, bvec(0, 0, 0), width, height, _offset, map_markup_to_text, NULL, language); + PangoLayout *layout = measure_text_internal(str, len, maxwidth, align, justify, bvec(0, 0, 0), width, height, _offset, map_markup_to_text, NULL, language, no_fallback); if(!layout) { cx = cy = 0; return; } if(!width || !height) { g_object_unref(layout); cx = cy = 0; return; } diff --git a/source/engine/ui.cpp b/source/engine/ui.cpp index 82ed94fcf..7ca9a2804 100644 --- a/source/engine/ui.cpp +++ b/source/engine/ui.cpp @@ -1970,6 +1970,7 @@ namespace UI static int curwrapalign = -1, curjustify = 0, curshadow = 0, curfontoutlinealpha = 0; float curfontoutline = 0.f; + bool curnofallback = false; static const char *curlanguage = newstring(""); #define WITHTEXTATTR(name, tmp, val, body) \ @@ -2018,11 +2019,21 @@ namespace UI float val, tmp; int aval, atmp; - void setup(float val_, int aval_) { val = val_; aval = aval_;} - void layout() { WITHTEXTATTR(fontoutline, tmp, val, WITHTEXTATTR(fontoutlinealpha, atmp, aval, Object::layout())); } - void draw(float sx, float sy) { WITHTEXTATTR(fontoutline, tmp, val, WITHTEXTATTR(fontoutlinealpha, atmp, aval, Object::draw(sx, sy))); } + void setup(float val_, int aval_) { val = val_; aval = aval_; } + void layout() { WITHTEXTATTR(fontoutline, tmp, val, WITHTEXTATTR(fontoutlinealpha, atmp, aval, Object::layout())); } + void draw(float sx, float sy) { WITHTEXTATTR(fontoutline, tmp, val, WITHTEXTATTR(fontoutlinealpha, atmp, aval, Object::draw(sx, sy))); } void buildchildren(uint *contents) { WITHTEXTATTR(fontoutline, tmp, val, WITHTEXTATTR(fontoutlinealpha, atmp, aval, Object::buildchildren(contents))); } }; + struct NoFallback : Object + { + static const char *typestr() { return "#NoFallback"; } + const char *gettype() const { return typestr(); } + + bool tmp; + void layout() { tmp = curnofallback; curnofallback = true; Object::layout(); curnofallback = tmp; } + void draw(float sx, float sy) { tmp = curnofallback; curnofallback = true; Object::draw(sx, sy); curnofallback = tmp; } + void buildchildren(uint *contents) { tmp = curnofallback; curnofallback = true; Object::buildchildren(contents); curnofallback = tmp; } + }; struct Language : Object { static const char *typestr() { return "#Language"; } @@ -2032,8 +2043,8 @@ namespace UI Language() : val(NULL), tmp(NULL) {} ~Language() { DELETEA(val); DELETEA(tmp); } void setup(const char *val_) { SETSTR(val, val_); } - void layout() { SETSTR(tmp, curlanguage); SETSTR(curlanguage, val); Object::layout(); SETSTR(curlanguage, tmp); } - void draw(float sx, float sy) { SETSTR(tmp, curlanguage); SETSTR(curlanguage, val); Object::draw(sx, sy); SETSTR(curlanguage, tmp); } + void layout() { SETSTR(tmp, curlanguage); SETSTR(curlanguage, val); Object::layout(); SETSTR(curlanguage, tmp); } + void draw(float sx, float sy) { SETSTR(tmp, curlanguage); SETSTR(curlanguage, val); Object::draw(sx, sy); SETSTR(curlanguage, tmp); } void buildchildren(uint *contents) { SETSTR(tmp, curlanguage); SETSTR(curlanguage, val); Object::buildchildren(contents); SETSTR(curlanguage, tmp); } }; @@ -2048,11 +2059,12 @@ namespace UI int fontid, lastchange; int align, justify, shadow, outlinealpha; float outline; + bool nofallback; const char *language; bool changed; uint crc; // string hash used for change detection - Text() : info({0, 0, 0}), lastchange(0), align(curwrapalign), justify(curjustify), shadow(curshadow), outlinealpha(curfontoutlinealpha), outline(curfontoutline), language(NULL), crc(0) {} + Text() : info({0, 0, 0}), lastchange(0), align(curwrapalign), justify(curjustify), shadow(curshadow), outlinealpha(curfontoutlinealpha), outline(curfontoutline), nofallback(curnofallback), language(NULL), crc(0) {} void setup(float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1) { @@ -2061,7 +2073,7 @@ namespace UI float newscale = scale_ * uiscale; int curfontid = getcurfontid(); - if(newscale != scale || wrap_ != wrap || fontid != curfontid || curwrapalign != align || curjustify != justify || curshadow != shadow || curfontoutline != outline || curfontoutlinealpha != outlinealpha || (!language || strcmp(curlanguage, language)) || (color_.r != color.r || color_.g != color.g || color_.b != color.b)) + if(newscale != scale || wrap_ != wrap || fontid != curfontid || curwrapalign != align || curjustify != justify || curshadow != shadow || curfontoutline != outline || curfontoutlinealpha != outlinealpha || curnofallback != nofallback || (!language || strcmp(curlanguage, language)) || (color_.r != color.r || color_.g != color.g || color_.b != color.b)) { changed = true; lastchange = totalmillis; @@ -2075,6 +2087,7 @@ namespace UI shadow = curshadow; outline = curfontoutline; outlinealpha = curfontoutlinealpha; + nofallback = curnofallback; SETSTR(language, curlanguage); fontid = curfontid; } @@ -2142,7 +2155,7 @@ namespace UI if(!info.tex) { - prepare_text(text, info, int(wrap/k), bvec(color.r, color.g, color.b), -1, outline * FONTH / 16.f, bvec(0, 0, 0), outlinealpha, align, justify, language); + prepare_text(text, info, int(wrap/k), bvec(color.r, color.g, color.b), -1, outline * FONTH / 16.f, bvec(0, 0, 0), outlinealpha, align, justify, language, nofallback); } w = max(w, info.w*k); h = max(h, info.h*k); @@ -3625,6 +3638,9 @@ namespace UI ICOMMAND(uifontoutline, "fie", (float *val, int *a, uint *children), BUILD(FontOutline, o, o->setup(*val, *a), children)); + ICOMMAND(uinofallback, "e", (uint *children), + BUILD(NoFallback, o, o->setup(), children)); + ICOMMAND(uilanguage, "se", (char *val, uint *children), BUILD(Language, o, o->setup(val), children)); diff --git a/source/shared/iengine.h b/source/shared/iengine.h index 5b03831eb..b91fb2357 100644 --- a/source/shared/iengine.h +++ b/source/shared/iengine.h @@ -256,9 +256,9 @@ extern bool setfont(const char *name); static inline void setfontsize(float size) { fontsize = size; } extern void pushfont(); extern bool popfont(); -extern void draw_text(const char *str, float left, float top, int r = 255, int g = 255, int b = 255, int a = 255, int maxwidth = 0, int align = -1, int justify = 0, const char *language = NULL); +extern void draw_text(const char *str, float left, float top, int r = 255, int g = 255, int b = 255, int a = 255, int maxwidth = 0, int align = -1, int justify = 0, const char *language = NULL, bool no_fallback = false); extern void draw_textf(const char *fstr, float left, float top, ...) PRINTFARGS(1, 4); -extern void measure_text(const char *str, int maxwidth, int &width, int &height, int align = -1, int justify = 0, const char *language = NULL); +extern void measure_text(const char *str, int maxwidth, int &width, int &height, int align = -1, int justify = 0, const char *language = NULL, bool no_fallback = false); // texture From dc5ba9ffc3cb601391aaada8a9dd1d6065a7113f Mon Sep 17 00:00:00 2001 From: Jed- Date: Mon, 4 Nov 2024 17:29:05 +0100 Subject: [PATCH 19/68] Don't allow a certain codepoint in player names --- source/shared/tools.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/shared/tools.h b/source/shared/tools.h index 98f2a77d9..fa8a8eb4e 100644 --- a/source/shared/tools.h +++ b/source/shared/tools.h @@ -1409,7 +1409,7 @@ static inline int iscubenamesafe(uint c) || (c >= 0x0100 && c <= 0x0131) || (c >= 0x0134 && c <= 0x0148) - || (c >= 0x014A && c <= 0x017F) // Latin Extended-A (exclude 0x131, 0x132 and 0x149) + || (c >= 0x014A && c <= 0x017E) // Latin Extended-A (exclude 0x131, 0x132, 0x149 and 0x17F) || (c >= 0x0218 && c <= 0x021B) // Latin Extended-B: Romanian @@ -1449,7 +1449,6 @@ static inline uint homoglyph(uint c) case 0x0415: return 0x0045; case 0x011B: return 0x0115; case 0x011A: return 0x0114; - case 0x017F: return 0x0066; case 0x0397: return 0x0048; case 0x041D: return 0x0048; case 0x0131: return 0x0069; From 8ccb0f92c85c967b0513f6521a7a5185cfe895eb Mon Sep 17 00:00:00 2001 From: Jed- Date: Mon, 4 Nov 2024 19:18:08 +0100 Subject: [PATCH 20/68] Add `textcolor` command to customize text colors from cubescript --- config/ui/style.cfg | 11 +++++++++++ source/engine/rendertext.cpp | 28 ++++++++++------------------ 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/config/ui/style.cfg b/config/ui/style.cfg index 6a7ffb9b0..a50796e57 100644 --- a/config/ui/style.cfg +++ b/config/ui/style.cfg @@ -3,6 +3,17 @@ // Text Brightness & Default Paddings // /////////////////////////////////////////////////////////////////////////////// +textcolor 0 0x40FF80 // green: player talk +textcolor 1 0x60A0FF // blue: "echo" command +textcolor 2 0xFFC040 // yellow: gameplay messages +textcolor 3 0xFF4040 // red: important errors +textcolor 4 0x808080 // gray +textcolor 5 0xC040C0 // magenta +textcolor 6 0xFF8000 // orange +textcolor 7 0xFFFFFF // white +textcolor 8 0x00FFFF // cyan +textcolor 9 0xFFC0CB // pink + // ultra series uiPad:UXL = 1.2 // DXL * 10 uiPad:UL = 1.0 // DL * 10 diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index 08e6985e9..7031ce708 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -9,6 +9,13 @@ float fontsize = 0; // pixel height of the current font const matrix4x3 *textmatrix = NULL; // used for text particles Shader *textshader = NULL; // used for text particles +// text colors +static bvec palette[10]; +ICOMMAND(textcolor, "ii", (int *i, int *c), +{ + if(*i >= 0 && *i <= 9) palette[*i] = bvec::hexcolor(*c); +}); + static cairo_font_options_t *options = NULL; // global font options static cairo_surface_t *dummy_surface = NULL; // used to measure text @@ -196,6 +203,8 @@ bool init_pangocairo() dummy_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0); if(!dummy_surface) return false; + loopi(10) palette[i] = bvec(255, 255, 255); + defformatstring(msg, "Text rendering: Pango %s, Cairo %s", pango_version_string(), cairo_version_string()); conoutf(CON_INIT, msg); return true; @@ -208,23 +217,6 @@ void done_pangocairo() if(dummy_surface) cairo_surface_destroy(dummy_surface); } -static inline bvec get_text_color(char c, bvec def) -{ - switch(c) - { - case '0': return bvec( 64, 255, 128); // green: player talk - case '1': return bvec( 96, 160, 255); // blue: "echo" command - case '2': return bvec(255, 192, 64); // yellow: gameplay messages - case '3': return bvec(255, 64, 64); // red: important errors - case '4': return bvec(128, 128, 128); // gray - case '5': return bvec(192, 64, 192); // magenta - case '6': return bvec(255, 128, 0); // orange - case '7': return bvec(255, 255, 255); // white - case '8': return bvec( 0, 255, 255); // cyan - case '9': return bvec(255, 192, 203); // pink - } - return def; // provided color: everything else -} //stack[sp] is current color index static inline bvec text_color(char c, char *stack, int size, int &sp, bvec color) { @@ -237,7 +229,7 @@ static inline bvec text_color(char c, char *stack, int size, int &sp, bvec color { if(c=='r') { if(sp > 0) --sp; c = stack[sp]; } // restore color else stack[sp] = c; - return get_text_color(c, color); + if(c >= '0' && c <= '9') return palette[c - '0']; } return color; } From d09c83d7a898af04e82b2a47943b32da0a4f71c4 Mon Sep 17 00:00:00 2001 From: Jed- Date: Mon, 4 Nov 2024 19:29:30 +0100 Subject: [PATCH 21/68] Redraw text after changing palette colors --- source/engine/rendertext.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index 7031ce708..68b886b84 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -14,6 +14,8 @@ static bvec palette[10]; ICOMMAND(textcolor, "ii", (int *i, int *c), { if(*i >= 0 && *i <= 9) palette[*i] = bvec::hexcolor(*c); + clearconsoletextures(); + reloadfonts(); }); static cairo_font_options_t *options = NULL; // global font options From 08d653dc8d6e41cb5623241ffd803dfad4d580c1 Mon Sep 17 00:00:00 2001 From: Jed- Date: Mon, 4 Nov 2024 19:34:46 +0100 Subject: [PATCH 22/68] Automatically load all fonts in the font directory --- config/font.cfg | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/config/font.cfg b/config/font.cfg index a16bb8245..f825e1bed 100644 --- a/config/font.cfg +++ b/config/font.cfg @@ -1,7 +1,9 @@ -// Execute dedicated game fonts here +// Configure dedicated game fonts here -// registerfont "filename" // "family name" +loopfiles f "data/interface/font" "ttf" [ + registerfont (+s "data/interface/font/" $f ".ttf") +] // font "name" "family name" // fontweight -3..6 // < 0 = thinner, > 0 = thicker @@ -11,12 +13,6 @@ // fontfeatures [...features] // OpenType features (in CSS format) // fontvariations "variations" // for variable fonts; format: "AXIS1=VALUE,AXIS2=VALUE..." -registerfont "data/interface/font/InterVariable.ttf" // Inter Variable -registerfont "data/interface/font/InterVariable-Italic.ttf" // Inter Variable Italic -registerfont "data/interface/font/DejaVuSansMono.ttf" // DejaVu Sans Mono -registerfont "data/interface/font/Jura-VariableFont_wght.ttf" // Jura -registerfont "data/interface/font/webdings.ttf" // Webdings - font "default" "Inter Variable, Sans" fontfeatures ["ss04", "cv12", "cv13", "tnum"] font "mono" "DejaVu Sans Mono, Monospace" From 188d00fb48371e1e543c2d1aa35f6e4623041fc5 Mon Sep 17 00:00:00 2001 From: Jed- Date: Fri, 8 Nov 2024 15:49:42 +0100 Subject: [PATCH 23/68] Remove argument from `uijustify` --- source/engine/ui.cpp | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/source/engine/ui.cpp b/source/engine/ui.cpp index 7ca9a2804..f5fcc7149 100644 --- a/source/engine/ui.cpp +++ b/source/engine/ui.cpp @@ -1968,9 +1968,9 @@ namespace UI else dst = newstring(src); \ } while(0) - static int curwrapalign = -1, curjustify = 0, curshadow = 0, curfontoutlinealpha = 0; + static int curwrapalign = -1, curshadow = 0, curfontoutlinealpha = 0; float curfontoutline = 0.f; - bool curnofallback = false; + bool curjustify = false, curnofallback = false; static const char *curlanguage = newstring(""); #define WITHTEXTATTR(name, tmp, val, body) \ @@ -1995,11 +1995,10 @@ namespace UI static const char *typestr() { return "#Justify"; } const char *gettype() const { return typestr(); } - int val, tmp; - void setup(int val_) { val = val_; } - void layout() { WITHTEXTATTR(justify, tmp, val, Object::layout()); } - void draw(float sx, float sy) { WITHTEXTATTR(justify, tmp, val, Object::draw(sx, sy)); } - void buildchildren(uint *contents) { WITHTEXTATTR(justify, tmp, val, Object::buildchildren(contents)); } + bool tmp; + void layout() { tmp = curjustify; curjustify = true; Object::layout(); curjustify = tmp; } + void draw(float sx, float sy) { tmp = curjustify; curjustify = true; Object::draw(sx, sy); curjustify = tmp; } + void buildchildren(uint *contents) { tmp = curjustify; curjustify = true; Object::buildchildren(contents); curjustify = tmp; } }; struct Shadow : Object { @@ -2057,14 +2056,14 @@ namespace UI Color color; textinfo info; int fontid, lastchange; - int align, justify, shadow, outlinealpha; + int align, shadow, outlinealpha; float outline; - bool nofallback; + bool justify, nofallback; const char *language; bool changed; uint crc; // string hash used for change detection - Text() : info({0, 0, 0}), lastchange(0), align(curwrapalign), justify(curjustify), shadow(curshadow), outlinealpha(curfontoutlinealpha), outline(curfontoutline), nofallback(curnofallback), language(NULL), crc(0) {} + Text() : info({0, 0, 0}), lastchange(0), align(curwrapalign), shadow(curshadow), outlinealpha(curfontoutlinealpha), outline(curfontoutline), justify(curjustify), nofallback(curnofallback), language(NULL), crc(0) {} void setup(float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1) { @@ -3629,8 +3628,8 @@ namespace UI ICOMMAND(uiwrapalign, "ie", (int *val, uint *children), BUILD(WrapAlign, o, o->setup(*val), children)); - ICOMMAND(uijustify, "ie", (int *val, uint *children), - BUILD(Justify, o, o->setup(*val), children)); + ICOMMAND(uijustify, "e", (uint *children), + BUILD(Justify, o, o->setup(), children)); ICOMMAND(uishadow, "ie", (int *val, uint *children), BUILD(Shadow, o, o->setup(*val), children)); From 3e50f3f1cae22f36a626c1c073b7ffabb6b4acb3 Mon Sep 17 00:00:00 2001 From: Jed- Date: Fri, 8 Nov 2024 16:17:50 +0100 Subject: [PATCH 24/68] Use overloaded operators for color comparisons; always respect `uifps` --- source/engine/ui.cpp | 47 ++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/source/engine/ui.cpp b/source/engine/ui.cpp index f5fcc7149..101c1175f 100644 --- a/source/engine/ui.cpp +++ b/source/engine/ui.cpp @@ -1329,6 +1329,15 @@ namespace UI void attrib() { gle::attribub(r, g, b, a); } static void def() { gle::defcolor(4, GL_UNSIGNED_BYTE); } + + bool operator==(const Color &other) const + { + return r == other.r && g == other.g && b == other.b && a == other.a; + } + bool operator!=(const Color &other) const + { + return r != other.r || g != other.g || b != other.b || a != other.a; + } }; struct FillColor : Target @@ -2069,26 +2078,38 @@ namespace UI { Object::setup(); changed = false; - float newscale = scale_ * uiscale; - - int curfontid = getcurfontid(); - if(newscale != scale || wrap_ != wrap || fontid != curfontid || curwrapalign != align || curjustify != justify || curshadow != shadow || curfontoutline != outline || curfontoutlinealpha != outlinealpha || curnofallback != nofallback || (!language || strcmp(curlanguage, language)) || (color_.r != color.r || color_.g != color.g || color_.b != color.b)) + const float newscale = scale_ * uiscale; + + const int curfontid = getcurfontid(); + if(!uifps || (totalmillis - lastchange >= 1000/uifps)) if( + newscale != scale || + color_ != color || + wrap_ != wrap || + fontid != curfontid || + curwrapalign != align || + curjustify != justify || + curshadow != shadow || + curfontoutline != outline || + curfontoutlinealpha != outlinealpha || + curnofallback != nofallback || + (!language || strcmp(curlanguage, language)) + ) { changed = true; lastchange = totalmillis; } - scale = newscale; - color = color_; - wrap = wrap_; - align = curwrapalign; - justify = curjustify; - shadow = curshadow; - outline = curfontoutline; + scale = newscale; + color = color_; + wrap = wrap_; + fontid = curfontid; + align = curwrapalign; + justify = curjustify; + shadow = curshadow; + outline = curfontoutline; outlinealpha = curfontoutlinealpha; - nofallback = curnofallback; + nofallback = curnofallback; SETSTR(language, curlanguage); - fontid = curfontid; } static const char *typestr() { return "#Text"; } From cc317aaca128bf0f8ff9b84660b05e53ccebe473 Mon Sep 17 00:00:00 2001 From: Jed- Date: Fri, 8 Nov 2024 19:13:21 +0100 Subject: [PATCH 25/68] Move text function declarations to `engine.h` and rename arguments --- source/engine/console.cpp | 4 +- source/engine/engine.h | 23 +++++++-- source/engine/renderparticles.cpp | 2 +- source/engine/rendertext.cpp | 81 +++++++++++++++---------------- source/engine/ui.cpp | 2 +- source/shared/iengine.h | 10 ---- 6 files changed, 60 insertions(+), 62 deletions(-) diff --git a/source/engine/console.cpp b/source/engine/console.cpp index 67b6b60a6..101835166 100644 --- a/source/engine/console.cpp +++ b/source/engine/console.cpp @@ -118,7 +118,7 @@ float rendercommand(float x, float y, float w) pushfont(); setfont("default"); textinfo info; - prepare_text(buf, info, w, bvec(255, 255, 255), commandpos>=0 ? commandpos+1 + strlen(prompt) : strlen(buf), conoutline ? ceil(FONTH / 32.f) : 0, bvec(0, 0, 0), conoutline); + prepare_text(buf, info, w, bvec(255, 255, 255), commandpos>=0 ? commandpos+1 + strlen(prompt) : strlen(buf), conoutline ? ceil(FONTH / 32.f) : 0, bvec4(0, 0, 0, conoutline)); y -= info.h; if(info.tex) @@ -238,7 +238,7 @@ float drawconlines(int conskip, int confade, float conwidth, float conheight, fl if(conlines[idx].w != conwidth || conlines[idx].fontsize != fontsize || !info.tex) { if(info.tex) glDeleteTextures(1, &info.tex); - prepare_text(line, info, conwidth, bvec(255, 255, 255), -1, outline ? ceil(FONTH / 32.f) : 0, bvec(0, 0, 0), outline); + prepare_text(line, info, conwidth, bvec(255, 255, 255), -1, outline ? ceil(FONTH / 32.f) : 0, bvec4(0, 0, 0, outline)); conlines[idx].w = conwidth; conlines[idx].fontsize = fontsize; } diff --git a/source/engine/engine.h b/source/engine/engine.h index cda393807..692bf6916 100644 --- a/source/engine/engine.h +++ b/source/engine/engine.h @@ -53,18 +53,31 @@ struct textinfo extern Shader *textshader; extern const matrix4x3 *textmatrix; extern float textscale; +extern float fontsize; extern bool init_pangocairo(); extern void done_pangocairo(); extern int getcurfontid(); +extern bool setfont(const char *name); +extern void pushfont(); +extern bool popfont(); +extern void measure_text(const char *str, int maxw, int &w, int &h, int align = -1, int justify = 0, const char *lang = NULL, bool no_fallback = false); +extern void prepare_text(const char *str, textinfo &info, int maxw, bvec color = bvec(255, 255, 255), int cursor = -1, float outline = 0, bvec4 ol_color = bvec4(0, 0, 0, 0), int align = -1, int justify = 0, const char *lang = NULL, bool no_fallback = false); +extern void prepare_text_particle(const char *str, textinfo &info, bvec color = bvec(255, 255, 255), float outline = 0, bvec4 ol_color = bvec4(0, 0, 0, 0), const char *lang = NULL, bool no_fallback = false); +extern void draw_text(const textinfo &info, float left, float top, int a = 255, bool black = false); +extern void draw_text(const char *str, float left, float top, bvec color = bvec(255, 255, 255), int a = 255, int maxw = 0, int align = -1, int justify = 0, const char *lang = NULL, bool no_fallback = false); extern void gettextres(int &w, int &h); -extern void draw_text(textinfo info, float left, float top, int a = 255, bool black = false); -extern void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_color = bvec(255, 255, 255), int cursor = -1, float outline = 0, bvec outline_color = bvec(0, 0, 0), int outline_alpha = 255, int align = -1, int justify = 0, const char *language = NULL, bool no_fallback = false); -extern void prepare_text_particle(const char *str, textinfo &info, bvec initial_color = bvec(255, 255, 255), float outline = 0, bvec outline_color = bvec(0, 0, 0), int outline_alpha = 255, const char *language = NULL, bool no_fallback = false); -extern int text_visible(const char *str, float hitx, float hity, int maxwidth, int align = -1, int justify = 0, const char *language = NULL, bool no_fallback = false); -extern void text_pos(const char *str, int cursor, int &cx, int &cy, int maxwidth, int align = -1, int justify = 0, const char *language = NULL, bool no_fallback = false); +extern int text_visible(const char *str, float hitx, float hity, int maxw, int align = -1, int justify = 0, const char *lang = NULL, bool no_fallback = false); +extern void text_pos(const char *str, int cursor, int &cx, int &cy, int maxw, int align = -1, int justify = 0, const char *lang = NULL, bool no_fallback = false); extern void reloadfonts(); +static inline void setfontsize(float size) { fontsize = size; } +static inline void draw_textf(const char *fstr, float left, float top, ...) +{ + defvformatstring(str, top, fstr); + draw_text(str, left, top); +} + // texture extern int hwtexsize, hwcubetexsize, hwmaxaniso, maxtexsize, hwtexunits, hwvtexunits; diff --git a/source/engine/renderparticles.cpp b/source/engine/renderparticles.cpp index 98e8ab52e..65e1e8abf 100644 --- a/source/engine/renderparticles.cpp +++ b/source/engine/renderparticles.cpp @@ -516,7 +516,7 @@ struct textrenderer : listrenderer setfontsize(hudh / PARTICLETEXTROWS); textinfo info; - prepare_text_particle(p->text, info, p->color, FONTH / 32.f, bvec(0, 0, 0), 255, p->language); + prepare_text_particle(p->text, info, p->color, FONTH / 32.f, bvec4(0, 0, 0, 255), p->language); if(!info.tex) { popfont(); diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index 68b886b84..a5ac94f95 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -256,11 +256,11 @@ static inline bvec text_color(char c, char *stack, int size, int &sp, bvec color // adds a string to the layout parsing basic markup (\f codes) // NOTE: `markup` is the original string with \f codes; `text` is the stripped version without \f codes -static inline void add_text_to_layout(const char *markup, int len, PangoLayout *layout, bvec initial_color, int *map_markup_to_text, int *map_text_to_markup, const char *language, bool no_fallback) +static inline void add_text_to_layout(const char *markup, int len, PangoLayout *layout, bvec color, int *map_markup_to_text, int *map_text_to_markup, const char *language, bool no_fallback) { char *text = newstring(len); - bvec tcolor = initial_color; + bvec tcolor = color; char colorstack[10]; colorstack[0] = 'c'; int cpos = 0; @@ -311,7 +311,7 @@ static inline void add_text_to_layout(const char *markup, int len, PangoLayout * MARKUP_CASE('t', 'T', pango_attr_strikethrough_new, TRUE , strikethrough); default: { - tcolor = text_color(markup[i+1], colorstack, sizeof(colorstack), cpos, initial_color); + tcolor = text_color(markup[i+1], colorstack, sizeof(colorstack), cpos, color); if(attr) { attr->end_index = j + 1; @@ -373,7 +373,7 @@ static inline void add_text_to_layout(const char *markup, int len, PangoLayout * } #undef MARKUP_CASE -static inline PangoLayout *measure_text_internal(const char *str, int len, int maxwidth, int align, int justify, bvec initial_color, int &width, int &height, int &offset, int *map_markup_to_text, int *map_text_to_markup, const char *language, bool no_fallback) +static inline PangoLayout *measure_text_internal(const char *str, int len, int maxw, int align, int justify, bvec color, int &w, int &h, int &offset, int *map_markup_to_text, int *map_text_to_markup, const char *lang, bool no_fallback) { // create cairo context cairo_t *cr = cairo_create(dummy_surface); @@ -383,7 +383,7 @@ static inline PangoLayout *measure_text_internal(const char *str, int len, int m PangoLayout *layout = pango_cairo_create_layout(cr); if(!layout) { - width = height = offset = 0; + w = h = offset = 0; cairo_destroy(cr); return NULL; } @@ -393,82 +393,82 @@ static inline PangoLayout *measure_text_internal(const char *str, int len, int m pango_layout_set_font_description(layout, curfont->desc); // line wrapping: set maximum width, alignment and justification - if(maxwidth > 0) + if(maxw > 0) { - pango_layout_set_width(layout, maxwidth * PANGO_SCALE); + pango_layout_set_width(layout, maxw * PANGO_SCALE); pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); pango_layout_set_alignment(layout, align > 0 ? PANGO_ALIGN_RIGHT : align < 0 ? PANGO_ALIGN_LEFT : PANGO_ALIGN_CENTER); pango_layout_set_justify(layout, justify ? TRUE : FALSE); } - add_text_to_layout(str, len, layout, initial_color, map_markup_to_text, map_text_to_markup, language, no_fallback); + // fill layout + add_text_to_layout(str, len, layout, color, map_markup_to_text, map_text_to_markup, lang, no_fallback); // get pixel size PangoRectangle r; pango_layout_get_extents(layout, NULL, &r); - width = r.width / PANGO_SCALE; - height = r.height / PANGO_SCALE; + w = r.width / PANGO_SCALE; + h = r.height / PANGO_SCALE; offset = -r.x / PANGO_SCALE; cairo_destroy(cr); return layout; } -void measure_text(const char *str, int maxwidth, int &width, int &height, int align, int justify, const char *language, bool no_fallback) +void measure_text(const char *str, int maxw, int &w, int &h, int align, int justify, const char *lang, bool no_fallback) { int _offset; - PangoLayout *layout = measure_text_internal(str, strlen(str), maxwidth, align, justify, bvec(0, 0, 0), width, height, _offset, NULL, NULL, language, no_fallback); + PangoLayout *layout = measure_text_internal(str, strlen(str), maxw, align, justify, bvec(0, 0, 0), w, h, _offset, NULL, NULL, lang, no_fallback); if(layout) g_object_unref(layout); } -void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_color, int cursor, float outline, bvec outline_color, int outline_alpha, int align, int justify, const char *language, bool no_fallback) +void prepare_text(const char *str, textinfo &info, int maxw, bvec color, int cursor, float outline, bvec4 ol_color, int align, int justify, const char *lang, bool no_fallback) { // get dimensions and pango layout int width, height, offset; const int len = strlen(str); int map_markup_to_text[len+1]; - PangoLayout *layout = measure_text_internal(str, len, maxwidth, align, justify, initial_color, width, height, offset, cursor >= 0 ? map_markup_to_text : NULL, NULL, language, no_fallback); + PangoLayout *layout = measure_text_internal(str, len, maxw, align, justify, color, width, height, offset, cursor >= 0 ? map_markup_to_text : NULL, NULL, lang, no_fallback); if(!layout) { info = {0, 0, 0}; return; } if(!width || !height) { g_object_unref(layout); info = {0, 0, 0}; return; } // create surface and cairo context if(cursor >= 0) width += max(4.f, fontsize); // make space for the cursor - int outline_offset = ceil(outline); - width += 2 * outline_offset; - height += 2 * outline_offset; + const int ol_offset = ceil(outline); + width += 2 * ol_offset; + height += 2 * ol_offset; cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); cairo_t *cr = cairo_create(surface); cairo_set_font_options(cr, options); - cairo_move_to(cr, offset + outline_offset, outline_offset); + cairo_move_to(cr, offset + ol_offset, ol_offset); // draw text onto the surface if(outline) { - cairo_set_source_rgba(cr, outline_color.r / 255.f, outline_color.g / 255.f, outline_color.b / 255.f, outline_alpha / 255.f); + cairo_set_source_rgba(cr, ol_color.r/255.f, ol_color.g/255.f, ol_color.b/255.f, ol_color.a/255.f); cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND); cairo_set_line_width(cr, 2 * outline); pango_cairo_layout_path(cr, layout); cairo_stroke(cr); } cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0); - cairo_move_to(cr, offset + outline_offset, outline_offset); + cairo_move_to(cr, offset + ol_offset, ol_offset); pango_cairo_show_layout(cr, layout); // add the cursor if(cursor >= 0 && ((totalmillis - inputmillis <= cursorblink) || !cursorblink || ((totalmillis - inputmillis) % (2*cursorblink)) <= cursorblink)) { cursor = min((int)strlen(pango_layout_get_text(layout)), map_markup_to_text[cursor]); - PangoRectangle cursor_rect; - pango_layout_get_cursor_pos(layout, cursor, &cursor_rect, NULL); + PangoRectangle cur_rect; + pango_layout_get_cursor_pos(layout, cursor, &cur_rect, NULL); const float curw = max(1.f, fontsize / 16); - - cairo_rectangle(cr, cursor_rect.x / PANGO_SCALE + outline_offset, cursor_rect.y / PANGO_SCALE + outline_offset, curw, cursor_rect.height / PANGO_SCALE); + cairo_rectangle(cr, cur_rect.x/PANGO_SCALE+ol_offset, cur_rect.y/PANGO_SCALE+ol_offset, curw, cur_rect.height/PANGO_SCALE); if(outline) { - cairo_set_source_rgba(cr, outline_color.r / 255.f, outline_color.g / 255, outline_color.b / 255.f, outline_alpha / 255.f); + cairo_set_source_rgba(cr, ol_color.r/255.f, ol_color.g/255, ol_color.b/255.f, ol_color.a/255.f); cairo_stroke_preserve(cr); } - cairo_set_source_rgba(cr, cursorcolor.r / 255.f, cursorcolor.g / 255.f, cursorcolor.b / 255.f, 1.0); + cairo_set_source_rgba(cr, cursorcolor.r/255.f, cursorcolor.g/255.f, cursorcolor.b/255.f, 1.0); cairo_fill(cr); } @@ -492,14 +492,14 @@ void prepare_text(const char *str, textinfo &info, int maxwidth, bvec initial_co cairo_destroy(cr); cairo_surface_destroy(surface); } -void prepare_text_particle(const char *str, textinfo &info, bvec initial_color, float outline, bvec outline_color, int outline_alpha, const char *language, bool no_fallback) +void prepare_text_particle(const char *str, textinfo &info, bvec color, float outline, bvec4 ol_color, const char *lang, bool no_fallback) { - const int c = initial_color.tohexcolor(), d = outline_color.tohexcolor(); - const char *l = language ? language : ""; + const int c = color.tohexcolor(); + const char *l = lang ? lang : ""; uint key = crc32(0, (const Bytef *)str, strlen(str)) + curfont->id; key = crc32(key, (const Bytef *)(&outline), sizeof(float)); key = crc32(key, (const Bytef *)(&c), sizeof(int)); - key = crc32(key, (const Bytef *)(&d), sizeof(int)); + key = crc32(key, (const Bytef *)(&ol_color.mask), sizeof(uint)); key = crc32(key, (const Bytef *)l, strlen(l)); partinfo &p = particle_cache[key]; if(p.ti.tex) @@ -507,7 +507,7 @@ void prepare_text_particle(const char *str, textinfo &info, bvec initial_color, info = p.ti; return; } - prepare_text(str, p.ti, 0, initial_color, -1, outline, outline_color, outline_alpha, -1, 0, language, no_fallback); + prepare_text(str, p.ti, 0, color, -1, outline, ol_color, -1, 0, lang, no_fallback); if(!p.ti.tex) { info = {0, 0, 0}; return; } if(particle_queue.length() >= 256) { @@ -522,7 +522,7 @@ void prepare_text_particle(const char *str, textinfo &info, bvec initial_color, } // draw text to the screen -void draw_text(textinfo info, float left, float top, int a, bool black) +void draw_text(const textinfo &info, float left, float top, int a, bool black) { const int w = info.w, h = info.h; @@ -555,19 +555,14 @@ void draw_text(textinfo info, float left, float top, int a, bool black) gle::end(); // NOTE: `info.tex` is not deleted here! } -void draw_text(const char *str, float left, float top, int r, int g, int b, int a, int maxwidth, int align, int justify, const char *language, bool no_fallback) +void draw_text(const char *str, float left, float top, bvec color, int a, int maxw, int align, int justify, const char *lang, bool no_fallback) { textinfo info; - prepare_text(str, info, maxwidth, bvec(r, g, b), -1, 0, bvec(0, 0, 0), 0, align, justify, language, no_fallback); + prepare_text(str, info, maxw, color, -1, 0, bvec4(color, a), align, justify, lang, no_fallback); if(!info.tex) return; draw_text(info, left, top, a); glDeleteTextures(1, &info.tex); } -void draw_textf(const char *fstr, float left, float top, ...) -{ - defvformatstring(str, top, fstr); - draw_text(str, left, top); -} void gettextres(int &w, int &h) { @@ -587,13 +582,13 @@ void gettextres(int &w, int &h) } // used by the text editor -int text_visible(const char *str, float hitx, float hity, int maxwidth, int align, int justify, const char *language, bool no_fallback) +int text_visible(const char *str, float hitx, float hity, int maxw, int align, int justify, const char *lang, bool no_fallback) { int width, height, _offset; const int len = strlen(str); if(!len) return 0; int map_text_to_markup[len+1]; - PangoLayout *layout = measure_text_internal(str, len, maxwidth, align, justify, bvec(0, 0, 0), width, height, _offset, NULL, map_text_to_markup, language, no_fallback); + PangoLayout *layout = measure_text_internal(str, len, maxw, align, justify, bvec(0, 0, 0), width, height, _offset, NULL, map_text_to_markup, lang, no_fallback); if(!layout) return len; if(!width || !height) { g_object_unref(layout); return len; } @@ -605,13 +600,13 @@ int text_visible(const char *str, float hitx, float hity, int maxwidth, int alig } // used by the text editor -void text_pos(const char *str, int cursor, int &cx, int &cy, int maxwidth, int align, int justify, const char *language, bool no_fallback) +void text_pos(const char *str, int cursor, int &cx, int &cy, int maxw, int align, int justify, const char *lang, bool no_fallback) { int width, height, _offset; const int len = strlen(str); if(!len) { cx = cy = 0; return; } int map_markup_to_text[len+1]; - PangoLayout *layout = measure_text_internal(str, len, maxwidth, align, justify, bvec(0, 0, 0), width, height, _offset, map_markup_to_text, NULL, language, no_fallback); + PangoLayout *layout = measure_text_internal(str, len, maxw, align, justify, bvec(0, 0, 0), width, height, _offset, map_markup_to_text, NULL, lang, no_fallback); if(!layout) { cx = cy = 0; return; } if(!width || !height) { g_object_unref(layout); cx = cy = 0; return; } diff --git a/source/engine/ui.cpp b/source/engine/ui.cpp index 101c1175f..1814f6653 100644 --- a/source/engine/ui.cpp +++ b/source/engine/ui.cpp @@ -2175,7 +2175,7 @@ namespace UI if(!info.tex) { - prepare_text(text, info, int(wrap/k), bvec(color.r, color.g, color.b), -1, outline * FONTH / 16.f, bvec(0, 0, 0), outlinealpha, align, justify, language, nofallback); + prepare_text(text, info, int(wrap/k), bvec(color.r, color.g, color.b), -1, outline * FONTH / 16.f, bvec4(0, 0, 0, outlinealpha), align, justify, language, nofallback); } w = max(w, info.w*k); h = max(h, info.h*k); diff --git a/source/shared/iengine.h b/source/shared/iengine.h index b91fb2357..40574df7a 100644 --- a/source/shared/iengine.h +++ b/source/shared/iengine.h @@ -250,16 +250,6 @@ extern void fatal(const char *s, ...) PRINTFARGS(1, 2); extern void drawquad(float x, float y, float w, float h, float tx1 = 0, float ty1 = 0, float tx2 = 1, float ty2 = 1, bool flipx = false, bool flipy = false); extern int currentversion; -// rendertext -extern float fontsize; -extern bool setfont(const char *name); -static inline void setfontsize(float size) { fontsize = size; } -extern void pushfont(); -extern bool popfont(); -extern void draw_text(const char *str, float left, float top, int r = 255, int g = 255, int b = 255, int a = 255, int maxwidth = 0, int align = -1, int justify = 0, const char *language = NULL, bool no_fallback = false); -extern void draw_textf(const char *fstr, float left, float top, ...) PRINTFARGS(1, 4); -extern void measure_text(const char *str, int maxwidth, int &width, int &height, int align = -1, int justify = 0, const char *language = NULL, bool no_fallback = false); - // texture struct VSlot; From 55827987459f64fb776018291430d0d20e1ff3eb Mon Sep 17 00:00:00 2001 From: Jed- Date: Fri, 8 Nov 2024 22:53:57 +0100 Subject: [PATCH 26/68] Fix change detection --- source/engine/ui.cpp | 69 ++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/source/engine/ui.cpp b/source/engine/ui.cpp index 1814f6653..050171acf 100644 --- a/source/engine/ui.cpp +++ b/source/engine/ui.cpp @@ -185,8 +185,10 @@ namespace UI void resetblend() { changeblend(BLEND_ALPHA, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } void modblend() { changeblend(BLEND_MOD, GL_ZERO, GL_SRC_COLOR); } + static int uimillis = 0; + FVARP(uiscale, 0.5f, 1.0f, 1.5f); - VARP(uifps, 0, 60, 1000); + VARFP(uifps, 0, 60, 1000, { uimillis = uifps ? (1000/uifps) : 0; }); struct Object { @@ -2072,7 +2074,7 @@ namespace UI bool changed; uint crc; // string hash used for change detection - Text() : info({0, 0, 0}), lastchange(0), align(curwrapalign), shadow(curshadow), outlinealpha(curfontoutlinealpha), outline(curfontoutline), justify(curjustify), nofallback(curnofallback), language(NULL), crc(0) {} + Text() : scale(0), wrap(0), color(0), info({0, 0, 0}), lastchange(0), align(curwrapalign), shadow(curshadow), outlinealpha(curfontoutlinealpha), outline(curfontoutline), justify(curjustify), nofallback(curnofallback), language(NULL), crc(0) {} void setup(float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1) { @@ -2081,35 +2083,38 @@ namespace UI const float newscale = scale_ * uiscale; const int curfontid = getcurfontid(); - if(!uifps || (totalmillis - lastchange >= 1000/uifps)) if( - newscale != scale || - color_ != color || - wrap_ != wrap || - fontid != curfontid || - curwrapalign != align || - curjustify != justify || - curshadow != shadow || - curfontoutline != outline || - curfontoutlinealpha != outlinealpha || - curnofallback != nofallback || - (!language || strcmp(curlanguage, language)) - ) - { - changed = true; - lastchange = totalmillis; - } + if(!uimillis || (totalmillis - lastchange >= uimillis) || !lastchange) + { + if( + newscale != scale || + color_ != color || + wrap_ != wrap || + fontid != curfontid || + curwrapalign != align || + curjustify != justify || + curshadow != shadow || + curfontoutline != outline || + curfontoutlinealpha != outlinealpha || + curnofallback != nofallback || + (!language || strcmp(curlanguage, language)) + ) + { + changed = true; + lastchange = totalmillis; + } - scale = newscale; - color = color_; - wrap = wrap_; - fontid = curfontid; - align = curwrapalign; - justify = curjustify; - shadow = curshadow; - outline = curfontoutline; - outlinealpha = curfontoutlinealpha; - nofallback = curnofallback; - SETSTR(language, curlanguage); + scale = newscale; + color = color_; + wrap = wrap_; + fontid = curfontid; + align = curwrapalign; + justify = curjustify; + shadow = curshadow; + outline = curfontoutline; + outlinealpha = curfontoutlinealpha; + nofallback = curnofallback; + SETSTR(language, curlanguage); + } } static const char *typestr() { return "#Text"; } @@ -2161,15 +2166,15 @@ namespace UI // text changes are detected here const char *text = getstr(); - if(!uifps || (totalmillis - lastchange >= 1000/uifps)) + if(!uimillis || (totalmillis - lastchange >= uimillis) || !crc) { const uint crc_new = crc32(0, (const Bytef *)text, strlen(text)); if(crc_new != crc) { changed = true; lastchange = totalmillis; + crc = crc_new; } - crc = crc_new; } if(changed && info.tex) cleartext(); From acdb4dc6ddb18abbb427bd03d5a358b038766a97 Mon Sep 17 00:00:00 2001 From: Jed- Date: Sat, 9 Nov 2024 13:54:22 +0100 Subject: [PATCH 27/68] Update Makefile --- source/Makefile | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/source/Makefile b/source/Makefile index be41d5c3c..c47ad6e49 100644 --- a/source/Makefile +++ b/source/Makefile @@ -516,14 +516,12 @@ game/gamephysics.o: shared/ents.h shared/command.h shared/glexts.h game/gamephysics.o: shared/glemu.h engine/sound.h shared/iengine.h game/gamephysics.o: shared/igame.h shared/unicode.h game/weapon.h game/ai.h game/gamephysics.o: game/gamemode.h game/entity.h game/monster.h -game/gamephysics.o: shared/igame.h game/weapon.h game/ai.h game/gamemode.h -game/gamephysics.o: game/entity.h game/monster.h game/hud.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/hud.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h -game/hud.o: engine/sound.h shared/iengine.h shared/igame.h game/weapon.h -game/hud.o: game/ai.h game/gamemode.h game/entity.h game/monster.h -game/hud.o: engine/engine.h engine/world.h engine/octa.h engine/light.h -game/hud.o: engine/texture.h engine/bih.h engine/model.h +game/hud.o: engine/sound.h shared/iengine.h shared/igame.h shared/unicode.h +game/hud.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h +game/hud.o: game/monster.h engine/engine.h engine/world.h engine/octa.h +game/hud.o: engine/light.h engine/texture.h engine/bih.h engine/model.h shared/cube.h.gch: shared/tools.h shared/geom.h shared/ents.h shared/cube.h.gch: shared/command.h shared/glexts.h shared/glemu.h From c9f8d1ea6c1af5ffa726bafcde32678194e0bf0a Mon Sep 17 00:00:00 2001 From: Jed- Date: Sat, 16 Nov 2024 14:30:10 +0100 Subject: [PATCH 28/68] Post-merge whitespace fixes --- config/ui/hud/gamehud.cfg | 40 +++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/config/ui/hud/gamehud.cfg b/config/ui/hud/gamehud.cfg index 80fe21d15..b9188f4d6 100644 --- a/config/ui/hud/gamehud.cfg +++ b/config/ui/hud/gamehud.cfg @@ -779,30 +779,30 @@ newui "scoreboard" [ ////////////////////////////////////////////////////////////////////////////////////////////////////////////// newui ".killUI" [ - local t - uiallowinput 0 - if (&& [! $isdead] [> $getmillis $.killUImillis]) [ + local t + uiallowinput 0 + if (&& [! $isdead] [> $getmillis $.killUImillis]) [ hideui ".killUI" ] [ - uivlist 0 [ - uifill 0 0.3 - t = (getrespawnwait) + uivlist 0 [ + uifill 0 0.3 + t = (getrespawnwait) uifontoutline 1 0x7F [ - uispace 0.02 0.02 [ - uifonttext "wide" $lasthudkillinfo $uiPad:US - ] - uispace 0 0 [ - uioffset 0 $uiPad:D2XL- [ - if (m_invasion $getmode) [ - uifonttext "wide" (+s "^f4Lives remaining: " $getclientlives) $uiPad:USS - ] [ - uifontcolortext "wide" (? $t $t " ") (? (< (getmillis) (+ 100 (getlastspawnattempt))) $c_red $c_white) 1 - ] + uispace 0.02 0.02 [ + uifonttext "wide" $lasthudkillinfo $uiPad:US + ] + uispace 0 0 [ + uioffset 0 $uiPad:D2XL- [ + if (m_invasion $getmode) [ + uifonttext "wide" (+s "^f4Lives remaining: " $getclientlives) $uiPad:USS + ] [ + uifontcolortext "wide" (? $t $t " ") (? (< (getmillis) (+ 100 (getlastspawnattempt))) $c_red $c_white) 1 + ] ] - ] - ] - ] - ] + ] + ] + ] + ] ] [ .killUImillis = (+ $getmillis 3500) ] From 78d403fae30e8a444b428279b1362fc5b1100b17 Mon Sep 17 00:00:00 2001 From: Jed- Date: Sat, 16 Nov 2024 14:44:14 +0100 Subject: [PATCH 29/68] Add fonts --- config/font.cfg | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/config/font.cfg b/config/font.cfg index f825e1bed..9c5281ae9 100644 --- a/config/font.cfg +++ b/config/font.cfg @@ -15,8 +15,11 @@ loopfiles f "data/interface/font" "ttf" [ font "default" "Inter Variable, Sans" fontfeatures ["ss04", "cv12", "cv13", "tnum"] -font "mono" "DejaVu Sans Mono, Monospace" -font "wide" "Jura, Inter Variable, Sans" +font "mono" "Roboto Mono, Monospace" +font "bold" "Jura, Inter Variable, Sans" fontfeatures ["liga", "calt", "zero"] fontweight 3 -font "webdings" "Webdings" \ No newline at end of file +font "wide" "Tektur, Inter Variable, Sans" +font "modernpics" "ModernPictograms" + +uiFontList = [ default mono bold wide modernpics ] \ No newline at end of file From 7dc790389c1d608a1cc2dfd59187f2c74ef769e7 Mon Sep 17 00:00:00 2001 From: Jed- Date: Sat, 16 Nov 2024 14:53:25 +0100 Subject: [PATCH 30/68] Tweak font sizes --- config/ui/hud/gamehud.cfg | 4 ++-- source/engine/engine.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/ui/hud/gamehud.cfg b/config/ui/hud/gamehud.cfg index b9188f4d6..82bf2691c 100644 --- a/config/ui/hud/gamehud.cfg +++ b/config/ui/hud/gamehud.cfg @@ -581,12 +581,12 @@ newui "scoreboard" [ uispace $uiPad:M $uiPad:M- [ uiFancyText "wide" @@@( at "FFA Aesir Vanir" $arg1 - ) 1.4 @@@(INT:TRANS ( + ) 1.25 @@@(INT:TRANS ( at [0x40D060 0x4060D0 0xD04040] $arg1 ) % 80) ] ; uialign- @@(? $bflip 1 -1) -1 uispace $uiPad:O5 (*f $uiPad:O5 2.9) [ - uiFancyText "wide" (getteamscore @@@arg1) 1 @@@( + uiFancyText "wide" (getteamscore @@@arg1) 0.85 @@@( at [0x40D060 0x4060D0 0xD04040] $arg1 ) ] ; uialign- @@(? $bflip -1 1) -1 diff --git a/source/engine/engine.h b/source/engine/engine.h index a21233986..70bf8dd86 100644 --- a/source/engine/engine.h +++ b/source/engine/engine.h @@ -47,7 +47,7 @@ struct textinfo // number of text lines to fill the whole screen (higher = smaller text) #define CONSOLETEXTROWS 45 #define LOADSCREENTEXTROWS 45 -#define UITEXTROWS 36 +#define UITEXTROWS 32 #define PARTICLETEXTROWS 15 // NOTE: particles use a different scale extern Shader *textshader; From dad15550156482028c1fcb988368797503c08ebf Mon Sep 17 00:00:00 2001 From: Jed- Date: Sat, 16 Nov 2024 15:33:03 +0100 Subject: [PATCH 31/68] Apply `conshadow` and `conoutline` in more places --- source/engine/console.cpp | 55 +++++++++++++++++++++++++------------- source/engine/engine.h | 7 +++++ source/engine/movie.cpp | 2 +- source/engine/rendergl.cpp | 10 +++---- 4 files changed, 50 insertions(+), 24 deletions(-) diff --git a/source/engine/console.cpp b/source/engine/console.cpp index 101835166..c7c2e5374 100644 --- a/source/engine/console.cpp +++ b/source/engine/console.cpp @@ -107,6 +107,33 @@ ICOMMAND(toggleconsole, "", (), UI::toggleui("fullconsole")); VARFP(conshadow, 0, 255, 255, clearconsoletextures()); VARFP(conoutline, 0, 0, 255, clearconsoletextures()); +inline void prepare_console_text(const char *str, textinfo &info, int maxw, int cursor) +{ + prepare_text(str, info, maxw, bvec(255, 255, 255), cursor, conoutline ? ceil(FONTH / 32.f) : 0, bvec4(0, 0, 0, conoutline)); +} +inline void draw_console_text(const textinfo &info, float left, float top) +{ + if(conshadow) + { + const float d = 3.f / 4.f * conscale; + draw_text(info, left - d, top + d, conshadow, true); + } + draw_text(info, left, top); +} +void draw_console_text(const char *str, float left, float top, int maxw, int cursor) +{ + textinfo info; + prepare_console_text(str, info, maxw, cursor); + if(!info.tex) return; + if(conshadow) + { + const float d = 3.f / 4.f * conscale; + draw_text(info, left - d, top + d, conshadow, true); + } + draw_text(info, left, top); + glDeleteTextures(1, &info.tex); +} + float rendercommand(float x, float y, float w) { if(commandmillis < 0) return 0; @@ -118,17 +145,12 @@ float rendercommand(float x, float y, float w) pushfont(); setfont("default"); textinfo info; - prepare_text(buf, info, w, bvec(255, 255, 255), commandpos>=0 ? commandpos+1 + strlen(prompt) : strlen(buf), conoutline ? ceil(FONTH / 32.f) : 0, bvec4(0, 0, 0, conoutline)); + prepare_console_text(buf, info, w, commandpos>=0 ? commandpos+1 + strlen(prompt) : strlen(buf)); y -= info.h; if(info.tex) { - if(conshadow) - { - const float d = 3.f / 4.f * conscale; - draw_text(info, x - d, y + d, conshadow, true); - } - draw_text(info, x, y); + draw_console_text(info, x, y); glDeleteTextures(1, &info.tex); } @@ -191,7 +213,7 @@ void clearconsoletextures() } } -float drawconlines(int conskip, int confade, float conwidth, float conheight, float conoff, int maxlines, int shadow, int outline, int filter, float y = 0, int dir = 1) +float drawconlines(int conskip, int confade, float conwidth, float conheight, float conoff, int maxlines, bool full, int filter, float y = 0, int dir = 1) { filter &= CON_FLAGS; int numl = conlines.length(), offset = min(conskip, numl); @@ -238,19 +260,16 @@ float drawconlines(int conskip, int confade, float conwidth, float conheight, fl if(conlines[idx].w != conwidth || conlines[idx].fontsize != fontsize || !info.tex) { if(info.tex) glDeleteTextures(1, &info.tex); - prepare_text(line, info, conwidth, bvec(255, 255, 255), -1, outline ? ceil(FONTH / 32.f) : 0, bvec4(0, 0, 0, outline)); + if(!full) prepare_console_text(line, info, conwidth, -1); + else prepare_text(line, info, conwidth, bvec(255, 255, 255), -1); conlines[idx].w = conwidth; conlines[idx].fontsize = fontsize; } if(dir <= 0) y -= info.h; if(info.tex) { - if(shadow) - { - const float d = 3.f / 4.f * conscale; - draw_text(info, conoff-d, y+d, shadow, true); - } - draw_text(info, conoff, y); + if(!full) draw_console_text(info, conoff, y); + else draw_text(info, conoff, y); } if(dir > 0) y += info.h; } @@ -265,7 +284,7 @@ float renderfullconsole(float w, float h) float conpad = FONTH*1.5/2, conheight = h - 2*conpad, conwidth = w - 2*conpad; - drawconlines(conskip, 0, conwidth, conheight, conpad, 0, 0, 0, fullconfilter); + drawconlines(conskip, 0, conwidth, conheight, conpad, 0, true, fullconfilter); popfont(); return conheight + 2*conpad; } @@ -278,9 +297,9 @@ float renderconsole(float w, float h, float abovehud) pushfont(); setfont("default"); setfontsize(hudh * conscale / CONSOLETEXTROWS); - float y = drawconlines(conskip, confade, conwidth, conheight, conpad, consize, conshadow, conoutline, confilter); + float y = drawconlines(conskip, confade, conwidth, conheight, conpad, consize, false, confilter); if(miniconsize && miniconwidth) - drawconlines(miniconskip, miniconfade, (miniconwidth*(w - 2*conpad))/100, min(float(FONTH*1.5*miniconsize), abovehud - y), conpad, miniconsize, conshadow, conoutline, miniconfilter, abovehud, -1); + drawconlines(miniconskip, miniconfade, (miniconwidth*(w - 2*conpad))/100, min(float(FONTH*1.5*miniconsize), abovehud - y), conpad, miniconsize, false, miniconfilter, abovehud, -1); popfont(); return y; } diff --git a/source/engine/engine.h b/source/engine/engine.h index 70bf8dd86..2b4991c03 100644 --- a/source/engine/engine.h +++ b/source/engine/engine.h @@ -71,12 +71,19 @@ extern int text_visible(const char *str, float hitx, float hity, int maxw, int extern void text_pos(const char *str, int cursor, int &cx, int &cy, int maxw, int align = -1, int justify = 0, const char *lang = NULL, bool no_fallback = false); extern void reloadfonts(); +extern void draw_console_text(const char *str, float left, float top, int maxw = 0, int cursor = -1); + static inline void setfontsize(float size) { fontsize = size; } static inline void draw_textf(const char *fstr, float left, float top, ...) { defvformatstring(str, top, fstr); draw_text(str, left, top); } +static inline void draw_console_textf(const char *fstr, float left, float top, ...) +{ + defvformatstring(str, top, fstr); + draw_console_text(str, left, top); +} // texture extern int hwtexsize, hwcubetexsize, hwmaxaniso, maxtexsize, hwtexunits, hwvtexunits; diff --git a/source/engine/movie.cpp b/source/engine/movie.cpp index 3d552eed8..8678fad8f 100644 --- a/source/engine/movie.cpp +++ b/source/engine/movie.cpp @@ -1139,7 +1139,7 @@ namespace recorder else totalsize /= 1e3; setfontsize(hudh * conscale / CONSOLETEXTROWS); - draw_textf("recorded %.1f%s %d%%", w-10*FONTH, FONTH-FONTH/2, totalsize, unit, int(calcquality()*100)); + draw_console_textf("recorded %.1f%s %d%%", w-10*FONTH, FONTH-FONTH/2, totalsize, unit, int(calcquality()*100)); glDisable(GL_BLEND); } diff --git a/source/engine/rendergl.cpp b/source/engine/rendergl.cpp index c59b6ffad..2472ff638 100644 --- a/source/engine/rendergl.cpp +++ b/source/engine/rendergl.cpp @@ -1183,7 +1183,7 @@ void printtimers(int conw, int conh) { static int printmillis = 0; if(totalmillis - lastprint >= 200) printmillis = framemillis; - draw_textf("frame time %i ms", conw-20*FONTH, conh-FONTH*3/2-offset*9*FONTH/8, printmillis); + draw_console_textf("frame time %i ms", conw-20*FONTH, conh-FONTH*3/2-offset*9*FONTH/8, printmillis); offset++; } if(usetimers) loopv(timerorder) @@ -1191,7 +1191,7 @@ void printtimers(int conw, int conh) timer &t = timers[timerorder[i]]; if(t.print < 0 ? t.result >= 0 : totalmillis - lastprint >= 200) t.print = t.result; if(t.print < 0 || (t.gpu && !(t.waiting&(1<= 200) lastprint = totalmillis; @@ -2414,8 +2414,8 @@ void gl_drawhud() int nextfps[3]; getfps(nextfps[0], nextfps[1], nextfps[2]); loopi(3) if(prevfps[i]==curfps[i]) curfps[i] = nextfps[i]; - if(showfpsrange) draw_textf("fps %d+%d-%d", conw-7*FONTH, conh-FONTH*3/2, curfps[0], curfps[1], curfps[2]); - else draw_textf("fps %d", conw-5*FONTH, conh-FONTH*3/2, curfps[0]); + if(showfpsrange) draw_console_textf("fps %d+%d-%d", conw-7*FONTH, conh-FONTH*3/2, curfps[0], curfps[1], curfps[2]); + else draw_console_textf("fps %d", conw-5*FONTH, conh-FONTH*3/2, curfps[0]); roffset += FONTH; } @@ -2435,7 +2435,7 @@ void gl_drawhud() const char *src = &buf[!wallclock24 && buf[0]=='0' ? 1 : 0]; while(*src) *dst++ = tolower(*src++); *dst++ = '\0'; - draw_text(buf, conw-5*FONTH, conh-FONTH*3/2-roffset); + draw_console_text(buf, conw-5*FONTH, conh-FONTH*3/2-roffset); roffset += FONTH; } } From ae73ca2167d87c953ca64f88639a164dd96623da Mon Sep 17 00:00:00 2001 From: Jed- Date: Sat, 16 Nov 2024 15:36:36 +0100 Subject: [PATCH 32/68] Tweak font sizes --- config/ui/hud/gamehud.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/ui/hud/gamehud.cfg b/config/ui/hud/gamehud.cfg index 82bf2691c..da2e50580 100644 --- a/config/ui/hud/gamehud.cfg +++ b/config/ui/hud/gamehud.cfg @@ -402,7 +402,7 @@ linear:HSV = [ uiShadowedImg "" "hud/" $arg4 2 $y ] ] uiclip 0 $y [ uioffset 0 (*f $y -0.15) [ - uiFancyText "wide" $arg2 (*f $arg1 3.125) $arg6 + uiFancyText "wide" $arg2 (*f $arg1 2.875) $arg6 ] ] ] ; uialign- -1 ] ; uiclamp-x From 5d6b06446bb1b737b32161188f1976f2853f48dc Mon Sep 17 00:00:00 2001 From: Jed- Date: Sun, 24 Nov 2024 14:15:36 +0100 Subject: [PATCH 33/68] Fix UB --- source/engine/engine.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/engine/engine.h b/source/engine/engine.h index c03cb6b8d..3d4e893f1 100644 --- a/source/engine/engine.h +++ b/source/engine/engine.h @@ -74,12 +74,12 @@ extern void reloadfonts(); extern void draw_console_text(const char *str, float left, float top, int maxw = 0, int cursor = -1); static inline void setfontsize(float size) { fontsize = size; } -static inline void draw_textf(const char *fstr, float left, float top, ...) +static inline void draw_textf(const char *fstr, double left, double top, ...) { defvformatstring(str, top, fstr); draw_text(str, left, top); } -static inline void draw_console_textf(const char *fstr, float left, float top, ...) +static inline void draw_console_textf(const char *fstr, double left, double top, ...) { defvformatstring(str, top, fstr); draw_console_text(str, left, top); From 65a46b6a73c2ec2fc14a923eeaf5e59990b9b5b9 Mon Sep 17 00:00:00 2001 From: Jed- Date: Sun, 24 Nov 2024 14:25:42 +0100 Subject: [PATCH 34/68] Avoid variable-length arrays --- source/engine/rendertext.cpp | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index a5ac94f95..d3e0a80e3 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -426,10 +426,10 @@ void prepare_text(const char *str, textinfo &info, int maxw, bvec color, int cur // get dimensions and pango layout int width, height, offset; const int len = strlen(str); - int map_markup_to_text[len+1]; + int *map_markup_to_text = new int[len+1]; PangoLayout *layout = measure_text_internal(str, len, maxw, align, justify, color, width, height, offset, cursor >= 0 ? map_markup_to_text : NULL, NULL, lang, no_fallback); - if(!layout) { info = {0, 0, 0}; return; } - if(!width || !height) { g_object_unref(layout); info = {0, 0, 0}; return; } + if(!layout) { info = {0, 0, 0}; delete[] map_markup_to_text; return; } + if(!width || !height) { g_object_unref(layout); info = {0, 0, 0}; delete[] map_markup_to_text; return; } // create surface and cairo context if(cursor >= 0) width += max(4.f, fontsize); // make space for the cursor @@ -480,6 +480,7 @@ void prepare_text(const char *str, textinfo &info, int maxw, bvec color, int cur g_object_unref(layout); cairo_destroy(cr); cairo_surface_destroy(surface); + delete[] map_markup_to_text; return; } glBindTexture(GL_TEXTURE_RECTANGLE, info.tex); @@ -491,6 +492,7 @@ void prepare_text(const char *str, textinfo &info, int maxw, bvec color, int cur g_object_unref(layout); cairo_destroy(cr); cairo_surface_destroy(surface); + delete[] map_markup_to_text; } void prepare_text_particle(const char *str, textinfo &info, bvec color, float outline, bvec4 ol_color, const char *lang, bool no_fallback) { @@ -587,16 +589,18 @@ int text_visible(const char *str, float hitx, float hity, int maxw, int align, i int width, height, _offset; const int len = strlen(str); if(!len) return 0; - int map_text_to_markup[len+1]; + int *map_text_to_markup = new int[len+1]; PangoLayout *layout = measure_text_internal(str, len, maxw, align, justify, bvec(0, 0, 0), width, height, _offset, NULL, map_text_to_markup, lang, no_fallback); - if(!layout) return len; - if(!width || !height) { g_object_unref(layout); return len; } + if(!layout) { delete[] map_text_to_markup; return len; } + if(!width || !height) { g_object_unref(layout); delete[] map_text_to_markup; return len; } int index; const int res = pango_layout_xy_to_index(layout, hitx * PANGO_SCALE, hity * PANGO_SCALE, &index, NULL); g_object_unref(layout); - if(!res) return len; - return map_text_to_markup[index]; + if(!res) { delete[] map_text_to_markup; return len; } + const int ret = map_text_to_markup[index]; + delete[] map_text_to_markup; + return ret; } // used by the text editor @@ -605,10 +609,10 @@ void text_pos(const char *str, int cursor, int &cx, int &cy, int maxw, int align int width, height, _offset; const int len = strlen(str); if(!len) { cx = cy = 0; return; } - int map_markup_to_text[len+1]; + int *map_markup_to_text = new int[len+1]; PangoLayout *layout = measure_text_internal(str, len, maxw, align, justify, bvec(0, 0, 0), width, height, _offset, map_markup_to_text, NULL, lang, no_fallback); - if(!layout) { cx = cy = 0; return; } - if(!width || !height) { g_object_unref(layout); cx = cy = 0; return; } + if(!layout) { cx = cy = 0; delete[] map_markup_to_text; return; } + if(!width || !height) { g_object_unref(layout); cx = cy = 0; delete[] map_markup_to_text; return; } cursor = max(0, min((int)strlen(pango_layout_get_text(layout)), map_markup_to_text[cursor])); PangoRectangle pos; @@ -617,6 +621,7 @@ void text_pos(const char *str, int cursor, int &cx, int &cy, int maxw, int align cy = pos.y / PANGO_SCALE; g_object_unref(layout); + delete[] map_markup_to_text; } void reloadfonts() { clear_text_particles(); UI::cleartext(); } \ No newline at end of file From 15386ce29a318bb6bd35a43154b515ffae42b5ec Mon Sep 17 00:00:00 2001 From: Jed- Date: Sun, 24 Nov 2024 14:27:13 +0100 Subject: [PATCH 35/68] Minor cleanup --- source/engine/rendertext.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index d3e0a80e3..dc4b7fee6 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -207,8 +207,7 @@ bool init_pangocairo() loopi(10) palette[i] = bvec(255, 255, 255); - defformatstring(msg, "Text rendering: Pango %s, Cairo %s", pango_version_string(), cairo_version_string()); - conoutf(CON_INIT, msg); + conoutf(CON_INIT, "Text rendering: Pango %s, Cairo %s", pango_version_string(), cairo_version_string()); return true; } From 7b9094cf509052db08841bd91807a0adea8d38cf Mon Sep 17 00:00:00 2001 From: Jed- Date: Sun, 24 Nov 2024 14:41:11 +0100 Subject: [PATCH 36/68] Move console text functions to rendertext.cpp --- source/engine/console.cpp | 31 ------------------------------- source/engine/engine.h | 2 ++ source/engine/rendertext.cpp | 30 ++++++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/source/engine/console.cpp b/source/engine/console.cpp index a74dfca9c..934793bc7 100644 --- a/source/engine/console.cpp +++ b/source/engine/console.cpp @@ -103,37 +103,6 @@ ICOMMAND(fullconsole, "iN$", (int *val, int *numargs, ident *id), }); ICOMMAND(toggleconsole, "", (), UI::toggleui("fullconsole")); -// apply a black shadow or outline to console text to improve visibility -VARFP(conshadow, 0, 255, 255, clearconsoletextures()); -VARFP(conoutline, 0, 0, 255, clearconsoletextures()); - -inline void prepare_console_text(const char *str, textinfo &info, int maxw, int cursor) -{ - prepare_text(str, info, maxw, bvec(255, 255, 255), cursor, conoutline ? ceil(FONTH / 32.f) : 0, bvec4(0, 0, 0, conoutline)); -} -inline void draw_console_text(const textinfo &info, float left, float top) -{ - if(conshadow) - { - const float d = 3.f / 4.f * conscale; - draw_text(info, left - d, top + d, conshadow, true); - } - draw_text(info, left, top); -} -void draw_console_text(const char *str, float left, float top, int maxw, int cursor) -{ - textinfo info; - prepare_console_text(str, info, maxw, cursor); - if(!info.tex) return; - if(conshadow) - { - const float d = 3.f / 4.f * conscale; - draw_text(info, left - d, top + d, conshadow, true); - } - draw_text(info, left, top); - glDeleteTextures(1, &info.tex); -} - float rendercommand(float x, float y, float w) { if(commandmillis < 0) return 0; diff --git a/source/engine/engine.h b/source/engine/engine.h index 3d4e893f1..09be82675 100644 --- a/source/engine/engine.h +++ b/source/engine/engine.h @@ -71,6 +71,8 @@ extern int text_visible(const char *str, float hitx, float hity, int maxw, int extern void text_pos(const char *str, int cursor, int &cx, int &cy, int maxw, int align = -1, int justify = 0, const char *lang = NULL, bool no_fallback = false); extern void reloadfonts(); +extern void prepare_console_text(const char *str, textinfo &info, int maxw, int cursor); +extern void draw_console_text(const textinfo &info, float left, float top); extern void draw_console_text(const char *str, float left, float top, int maxw = 0, int cursor = -1); static inline void setfontsize(float size) { fontsize = size; } diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index dc4b7fee6..96d8b11b3 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -9,6 +9,10 @@ float fontsize = 0; // pixel height of the current font const matrix4x3 *textmatrix = NULL; // used for text particles Shader *textshader = NULL; // used for text particles +// apply a black shadow or outline to console text to improve visibility +VARFP(conshadow, 0, 255, 255, clearconsoletextures()); +VARFP(conoutline, 0, 0, 255, clearconsoletextures()); + // text colors static bvec palette[10]; ICOMMAND(textcolor, "ii", (int *i, int *c), @@ -564,6 +568,32 @@ void draw_text(const char *str, float left, float top, bvec color, int a, int ma draw_text(info, left, top, a); glDeleteTextures(1, &info.tex); } +void prepare_console_text(const char *str, textinfo &info, int maxw, int cursor) +{ + prepare_text(str, info, maxw, bvec(255, 255, 255), cursor, conoutline ? ceil(FONTH / 32.f) : 0, bvec4(0, 0, 0, conoutline)); +} +void draw_console_text(const textinfo &info, float left, float top) +{ + if(conshadow) + { + const float d = 3.f / 4.f * conscale; + draw_text(info, left - d, top + d, conshadow, true); + } + draw_text(info, left, top); +} +void draw_console_text(const char *str, float left, float top, int maxw, int cursor) +{ + textinfo info; + prepare_console_text(str, info, maxw, cursor); + if(!info.tex) return; + if(conshadow) + { + const float d = 3.f / 4.f * conscale; + draw_text(info, left - d, top + d, conshadow, true); + } + draw_text(info, left, top); + glDeleteTextures(1, &info.tex); +} void gettextres(int &w, int &h) { From 51bad8c9b3c6cecefcfb8ae40ec8ebc054dcea0e Mon Sep 17 00:00:00 2001 From: Jed- Date: Wed, 27 Nov 2024 12:45:10 +0100 Subject: [PATCH 37/68] Refactor Bump minimum standard version to C++11 --- source/engine/console.cpp | 66 ++-- source/engine/engine.h | 119 ++++++-- source/engine/main.cpp | 42 ++- source/engine/movie.cpp | 4 +- source/engine/rendergl.cpp | 12 +- source/engine/renderparticles.cpp | 11 +- source/engine/rendertext.cpp | 487 +++++++++++++++++------------- source/engine/textedit.h | 46 ++- source/engine/ui.cpp | 31 +- source/shared/tools.h | 12 +- 10 files changed, 460 insertions(+), 370 deletions(-) diff --git a/source/engine/console.cpp b/source/engine/console.cpp index 934793bc7..24de055c3 100644 --- a/source/engine/console.cpp +++ b/source/engine/console.cpp @@ -4,7 +4,7 @@ #include "engine.h" #define MAXCONLINES 1000 -struct cline { char *line; int type, outtime; float fontsize, w; textinfo info; }; +struct cline { char *line; int type, outtime; float fontsize, w; text::Label label; }; reversequeue conlines; int commandmillis = -1, inputmillis = 0; @@ -19,7 +19,6 @@ VARFP(maxcon, 10, 200, MAXCONLINES, { cline &cl = conlines.pop(); delete[] cl.line; - if(cl.info.tex) glDeleteTextures(1, &cl.info.tex); } }); @@ -63,22 +62,19 @@ void conline(int type, const char *sf) // add a line to the console buffer if(!(prev&CON_TAG_MASK)) break; if(type == prev) { - buf = conlines.remove(i).line; + buf = conlines[i].line; + //buf = conlines.remove(i).line; + conlines.remove(i); break; } } if(!buf) buf = conlines.length() >= maxcon ? conlines.remove().line : newstring("", CONSTRLEN-1); cline &cl = conlines.add(); - if(cl.info.tex) - { - glDeleteTextures(1, &cl.info.tex); - cl.info.tex = 0; - } + cl.label.clear(); cl.line = buf; cl.type = type; cl.outtime = totalmillis; // for how long to keep line on screen cl.fontsize = cl.w = 0; - cl.info = {0, 0, 0}; defformatstring(prefixedsf, "%s%s", getprefix(type), sf); copystring(cl.line, prefixedsf, CONSTRLEN); } @@ -113,18 +109,13 @@ float rendercommand(float x, float y, float w) pushfont(); setfont("default"); - textinfo info; - prepare_console_text(buf, info, w, commandpos>=0 ? commandpos+1 + strlen(prompt) : strlen(buf)); - y -= info.h; + const text::Label label = text::prepare_for_console(buf, w, commandpos>=0 ? commandpos+1 + strlen(prompt) : strlen(buf)); + y -= label.height(); - if(info.tex) - { - draw_console_text(info, x, y); - glDeleteTextures(1, &info.tex); - } + label.draw_as_console(x, y); popfont(); - return info.h; + return label.height(); } VARP(consize, 0, 5, 100); @@ -165,21 +156,14 @@ ICOMMAND(clearconsole, "", (), { cline &cl = conlines.pop(); delete[] cl.line; - if(cl.info.tex) - { - glDeleteTextures(1, &cl.info.tex); - } + cl.label.clear(); } }); // free conline textures, necessary when changing font settings or calling `resetgl()` void clearconsoletextures() { - loopv(conlines) if(conlines[i].info.tex) - { - glDeleteTextures(1, &conlines[i].info.tex); - conlines[i].info.tex = 0; - } + loopv(conlines) conlines[i].label.clear(); } float drawconlines(int conskip, int confade, float conwidth, float conheight, float conoff, int maxlines, bool full, int filter, float y = 0, int dir = 1) @@ -206,14 +190,15 @@ float drawconlines(int conskip, int confade, float conwidth, float conheight, fl if(!(conlines[idx].type&filter)) continue; char *line = conlines[idx].line; int width, height; - if(conlines[idx].w != conwidth || conlines[idx].fontsize != fontsize || !conlines[idx].info.tex) + const text::Label& label = conlines[idx].label; + if(conlines[idx].w != conwidth || conlines[idx].fontsize != fontsize || !label.valid()) { - measure_text(line, conwidth, width, height); + text::measure(line, conwidth, width, height); } else { - width = conlines[idx].info.w; - height = conlines[idx].info.h; + width = label.width(); + height = label.height(); } if(maxlines > 0) { if(++n > maxlines) { numl = i; if(offset == idx) ++offset; break; } } else if(totalheight + height > conheight) { numl = i; if(offset == idx) ++offset; break; } @@ -225,22 +210,21 @@ float drawconlines(int conskip, int confade, float conwidth, float conheight, fl int idx = offset + (dir > 0 ? numl-i-1 : i); if(!(conlines[idx].type&filter)) continue; char *line = conlines[idx].line; - textinfo &info = conlines[idx].info; - if(conlines[idx].w != conwidth || conlines[idx].fontsize != fontsize || !info.tex) + text::Label& label = conlines[idx].label; + if(conlines[idx].w != conwidth || conlines[idx].fontsize != fontsize || !label.valid()) { - if(info.tex) glDeleteTextures(1, &info.tex); - if(!full) prepare_console_text(line, info, conwidth, -1); - else prepare_text(line, info, conwidth, bvec(255, 255, 255), -1); + if(!full) label = text::prepare_for_console(line, conwidth, -1); + else label = text::prepare(line, conwidth, bvec(255, 255, 255), -1); conlines[idx].w = conwidth; conlines[idx].fontsize = fontsize; } - if(dir <= 0) y -= info.h; - if(info.tex) + if(dir <= 0) y -= label.height(); + if(label.valid()) { - if(!full) draw_console_text(info, conoff, y); - else draw_text(info, conoff, y); + if(!full) label.draw_as_console(conoff, y); + else label.draw(conoff, y); } - if(dir > 0) y += info.h; + if(dir > 0) y += label.height(); } return y+conoff; } diff --git a/source/engine/engine.h b/source/engine/engine.h index 09be82675..cc6fe1708 100644 --- a/source/engine/engine.h +++ b/source/engine/engine.h @@ -33,12 +33,6 @@ extern int screenw, screenh, renderw, renderh, hudw, hudh; extern vector entgroup; // rendertext -struct textinfo -{ - GLuint tex; - int w, h; -}; - #define FONTH (fontsize) #define FONTW (FONTH/2) #define MINRESW 640 @@ -61,30 +55,101 @@ extern int getcurfontid(); extern bool setfont(const char *name); extern void pushfont(); extern bool popfont(); -extern void measure_text(const char *str, int maxw, int &w, int &h, int align = -1, int justify = 0, const char *lang = NULL, bool no_fallback = false); -extern void prepare_text(const char *str, textinfo &info, int maxw, bvec color = bvec(255, 255, 255), int cursor = -1, float outline = 0, bvec4 ol_color = bvec4(0, 0, 0, 0), int align = -1, int justify = 0, const char *lang = NULL, bool no_fallback = false); -extern void prepare_text_particle(const char *str, textinfo &info, bvec color = bvec(255, 255, 255), float outline = 0, bvec4 ol_color = bvec4(0, 0, 0, 0), const char *lang = NULL, bool no_fallback = false); -extern void draw_text(const textinfo &info, float left, float top, int a = 255, bool black = false); -extern void draw_text(const char *str, float left, float top, bvec color = bvec(255, 255, 255), int a = 255, int maxw = 0, int align = -1, int justify = 0, const char *lang = NULL, bool no_fallback = false); -extern void gettextres(int &w, int &h); -extern int text_visible(const char *str, float hitx, float hity, int maxw, int align = -1, int justify = 0, const char *lang = NULL, bool no_fallback = false); -extern void text_pos(const char *str, int cursor, int &cx, int &cy, int maxw, int align = -1, int justify = 0, const char *lang = NULL, bool no_fallback = false); extern void reloadfonts(); - -extern void prepare_console_text(const char *str, textinfo &info, int maxw, int cursor); -extern void draw_console_text(const textinfo &info, float left, float top); -extern void draw_console_text(const char *str, float left, float top, int maxw = 0, int cursor = -1); - static inline void setfontsize(float size) { fontsize = size; } -static inline void draw_textf(const char *fstr, double left, double top, ...) -{ - defvformatstring(str, top, fstr); - draw_text(str, left, top); -} -static inline void draw_console_textf(const char *fstr, double left, double top, ...) + +namespace text { - defvformatstring(str, top, fstr); - draw_console_text(str, left, top); + // A rendered text label, ready to be drawn + class Label + { + GLuint tex; + int w, h; + + public: + Label(); + ~Label(); + Label(const Label&) = delete; + Label(Label&&) noexcept; + Label& operator=(const Label&) = delete; + Label& operator=(Label&&) noexcept; + + bool valid() const { return tex != 0; } + int width() const { return w; } + int height() const { return h; } + void clear(); + + void draw(double left, double top, + int alpha = 255, + bool black = false + ) const; + void draw_as_console(double left, double top) const; + + friend Label prepare(const char *, int, bvec, int, float, bvec4, int, int, const char *, bool); + }; + + void measure(const char *str, int maxw, int &w, int &h, + int align = -1, + int justify = 0, + const char *lang = NULL, + bool no_fallback = false + ); + Label prepare(const char *str, int maxw, + bvec color = bvec(255, 255, 255), + int cursor = -1, + float outline = 0, + bvec4 ol_color = bvec4(0, 0, 0, 0), + int align = -1, + int justify = 0, + const char *lang = NULL, + bool no_fallback = false + ); + Label prepare_for_console(const char *str, int maxw, int cursor); + const Label& prepare_for_particle(const char *str, + bvec color = bvec(255, 255, 255), + float outline = 0, + bvec4 ol_color = bvec4(0, 0, 0, 0), + const char *lang = NULL, + bool no_fallback = false + ); + + void draw(const char *str, double left, double top, + bvec color = bvec(255, 255, 255), + int a = 255, + int maxw = 0, + int align = -1, + int justify = 0, + const char *lang = NULL, + bool no_fallback = false + ); + static inline void draw_fmt(const char *fstr, double left, double top, ...) + { + defvformatstring(str, top, fstr); + draw(str, left, top); + } + void draw_as_console(const char *str, double left, double top, + int maxw = 0, + int cursor = -1 + ); + static inline void draw_as_console_fmt(const char *fstr, double left, double top, ...) + { + defvformatstring(str, top, fstr); + draw_as_console(str, left, top); + } + + void getres(int &w, int &h); + int visible(const char *str, float hitx, float hity, int maxw, + int align = -1, + int justify = 0, + const char *lang = NULL, + bool no_fallback = false + ); + void pos(const char *str, int cursor, int &cx, int &cy, int maxw, + int align = -1, + int justify = 0, + const char *lang = NULL, + bool no_fallback = false + ); } // texture diff --git a/source/engine/main.cpp b/source/engine/main.cpp index 5d6cdbeb8..2736e6326 100644 --- a/source/engine/main.cpp +++ b/source/engine/main.cpp @@ -210,31 +210,28 @@ void renderbackgroundview(int w, int h, const char *caption, Texture *mapshot, c if(caption) { setfontsize(h * 1.5 / LOADSCREENTEXTROWS); - textinfo i_caption; pushfont(); setfont("wide"); - prepare_text(caption, i_caption, 0); + const text::Label caption_label = text::prepare(caption, 0); popfont(); - int tw = i_caption.w; - float tsz = 0.04f*lw/FONTH, + const int tw = caption_label.width(); + const float tsz = 0.04f*lw/FONTH, tx = 0.5f*(w - tw*tsz), ty = h - 0.075f*1.5f*lw - FONTH*tsz; - draw_text(i_caption, tx, ty); - glDeleteTextures(1, &i_caption.tex); + caption_label.draw(tx, ty); } if(mapshot || mapname) { setfontsize(h * 1 / LOADSCREENTEXTROWS); - textinfo i_info, i_name; + text::Label info_label; float infowidth = 0.5f*lw, sz = 0.35f*lw, msz = (0.85f*lw - sz)/(infowidth + FONTH), x = 0.5f*w, ly = 0.5f*lw, y = (0.5f*(h*0.5f - ly) + ly) - sz/15, - mx = 0, my = 0, mw = 0;//, mh = 0; + mx = 0, my = 0, mw = 0; if(mapinfo) { - prepare_text(mapinfo, i_info, infowidth); - mw = i_info.w; - //mh = i_info.h; + info_label = text::prepare(mapinfo, infowidth); + mw = info_label.width(); x -= 0.5f*mw*msz; if(mapshot && mapshot!=notexture) { @@ -254,19 +251,16 @@ void renderbackgroundview(int w, int h, const char *caption, Texture *mapshot, c setfontsize(h * 1.5 / LOADSCREENTEXTROWS); pushfont(); setfont("wide"); - prepare_text(mapname, i_name, 0); + const text::Label name_label = text::prepare(mapname, 0); popfont(); - float /*tw = i_name.w,*/ tsz = sz/(8*FONTH)/*, tx = max(0.5f*(mw*msz - tw*tsz), 0.0f)*/; + float tsz = sz/(8*FONTH); - //draw_text(i_name, x+mx+tx, y); - draw_text(i_name, x + sz + (infowidth - i_name.w) / 2, y); - glDeleteTextures(1, &i_name.tex); + name_label.draw(x + sz + (infowidth - name_label.width()) / 2, y); my = 1.5f*FONTH*tsz; } if(mapinfo) { - draw_text(i_info, x+mx, y+my); - glDeleteTextures(1, &i_info.tex); + info_label.draw(x+mx, y+my); } } @@ -295,7 +289,7 @@ void renderbackground(const char *caption, Texture *mapshot, const char *mapname int w = hudw, h = hudh; if(forceaspect) w = int(ceil(h*forceaspect)); getbackgroundres(w, h); - gettextres(w, h); + text::getres(w, h); if(force) { @@ -357,14 +351,12 @@ void renderprogressview(int w, int h, float bar, const char *text) // also use if(text) { setfontsize(h * 0.8 / LOADSCREENTEXTROWS); - textinfo i_text; - prepare_text(text, i_text, 0, bvec(255, 255, 255)); - int tw = i_text.w; + const text::Label label = text::prepare(text, 0, bvec(255, 255, 255)); + const int tw = label.width(); float tsz = bh*0.6f/FONTH; if(tw*tsz > mw) tsz = mw/tw; - draw_text(i_text, bx+sw, by+ (bh - FONTH*tsz)/2); - glDeleteTextures(1, &i_text.tex); + label.draw(bx+sw, by+ (bh - FONTH * tsz)/2); } glDisable(GL_BLEND); @@ -393,7 +385,7 @@ void renderprogress(float bar, const char *text, bool background) // also used int w = hudw, h = hudh; if(forceaspect) w = int(ceil(h*forceaspect)); getbackgroundres(w, h); - gettextres(w, h); + text::getres(w, h); extern int mesa_swap_bug, curvsync; bool forcebackground = progressbackground || (mesa_swap_bug && (curvsync || totalmillis==1)); diff --git a/source/engine/movie.cpp b/source/engine/movie.cpp index 8678fad8f..5f67fca78 100644 --- a/source/engine/movie.cpp +++ b/source/engine/movie.cpp @@ -1125,7 +1125,7 @@ namespace recorder { int w = hudw, h = hudh; if(forceaspect) w = int(ceil(h*forceaspect)); - gettextres(w, h); + text::getres(w, h); hudmatrix.ortho(0, w, h, 0, -1, 1); resethudmatrix(); @@ -1139,7 +1139,7 @@ namespace recorder else totalsize /= 1e3; setfontsize(hudh * conscale / CONSOLETEXTROWS); - draw_console_textf("recorded %.1f%s %d%%", w-10*FONTH, FONTH-FONTH/2, totalsize, unit, int(calcquality()*100)); + text::draw_as_console_fmt("recorded %.1f%s %d%%", w-10*FONTH, FONTH-FONTH/2, totalsize, unit, int(calcquality()*100)); glDisable(GL_BLEND); } diff --git a/source/engine/rendergl.cpp b/source/engine/rendergl.cpp index 2472ff638..d6a09ca9a 100644 --- a/source/engine/rendergl.cpp +++ b/source/engine/rendergl.cpp @@ -1183,7 +1183,7 @@ void printtimers(int conw, int conh) { static int printmillis = 0; if(totalmillis - lastprint >= 200) printmillis = framemillis; - draw_console_textf("frame time %i ms", conw-20*FONTH, conh-FONTH*3/2-offset*9*FONTH/8, printmillis); + text::draw_as_console_fmt("frame time %i ms", conw-20*FONTH, conh-FONTH*3/2-offset*9*FONTH/8, printmillis); offset++; } if(usetimers) loopv(timerorder) @@ -1191,7 +1191,7 @@ void printtimers(int conw, int conh) timer &t = timers[timerorder[i]]; if(t.print < 0 ? t.result >= 0 : totalmillis - lastprint >= 200) t.print = t.result; if(t.print < 0 || (t.gpu && !(t.waiting&(1<= 200) lastprint = totalmillis; @@ -2379,7 +2379,7 @@ void gl_drawhud() int w = hudw, h = hudh; if(forceaspect) w = int(ceil(h*forceaspect)); - gettextres(w, h); + text::getres(w, h); hudmatrix.ortho(0, w, h, 0, -1, 1); resethudmatrix(); @@ -2414,8 +2414,8 @@ void gl_drawhud() int nextfps[3]; getfps(nextfps[0], nextfps[1], nextfps[2]); loopi(3) if(prevfps[i]==curfps[i]) curfps[i] = nextfps[i]; - if(showfpsrange) draw_console_textf("fps %d+%d-%d", conw-7*FONTH, conh-FONTH*3/2, curfps[0], curfps[1], curfps[2]); - else draw_console_textf("fps %d", conw-5*FONTH, conh-FONTH*3/2, curfps[0]); + if(showfpsrange) text::draw_as_console_fmt("fps %d+%d-%d", conw-7*FONTH, conh-FONTH*3/2, curfps[0], curfps[1], curfps[2]); + else text::draw_as_console_fmt("fps %d", conw-5*FONTH, conh-FONTH*3/2, curfps[0]); roffset += FONTH; } @@ -2435,7 +2435,7 @@ void gl_drawhud() const char *src = &buf[!wallclock24 && buf[0]=='0' ? 1 : 0]; while(*src) *dst++ = tolower(*src++); *dst++ = '\0'; - draw_console_text(buf, conw-5*FONTH, conh-FONTH*3/2-roffset); + text::draw_as_console_fmt(buf, conw-5*FONTH, conh-FONTH*3/2-roffset); roffset += FONTH; } } diff --git a/source/engine/renderparticles.cpp b/source/engine/renderparticles.cpp index 65e1e8abf..a494301b2 100644 --- a/source/engine/renderparticles.cpp +++ b/source/engine/renderparticles.cpp @@ -515,14 +515,13 @@ struct textrenderer : listrenderer setfont(p->font); setfontsize(hudh / PARTICLETEXTROWS); - textinfo info; - prepare_text_particle(p->text, info, p->color, FONTH / 32.f, bvec4(0, 0, 0, 255), p->language); - if(!info.tex) + const text::Label& label = text::prepare_for_particle(p->text, p->color, FONTH / 32.f, bvec4(0, 0, 0, 255), p->language); + if(!label.valid()) { popfont(); return; } - float scale = p->size/80.0f, xoff = -info.w/2, yoff = -info.h/2; + float scale = p->size/80.0f, xoff = -label.width()/2, yoff = -label.width()/2; if((type&0xFF)==PT_TEXTUP) { xoff += detrnd((size_t)p, 100)-50; yoff -= detrnd((size_t)p, 101); } matrix4x3 m(camright, vec(camup).neg(), vec(camdir).neg(), o); @@ -530,7 +529,7 @@ struct textrenderer : listrenderer m.translate(xoff, yoff, 50); textmatrix = &m; - draw_text(info, 0, 0, blend); + label.draw(0, 0, blend); popfont(); textmatrix = NULL; } @@ -955,7 +954,7 @@ void debugparticles() pushhudmatrix(); hudmatrix.ortho(0, FONTH*n*2*vieww/float(viewh), FONTH*n*2, 0, -1, 1); // squeeze into top-left corner flushhudmatrix(); - loopi(n) draw_text(parts[i]->info, FONTH, (i+n/2)*FONTH); + loopi(n) text::draw(parts[i]->info, FONTH, (i+n/2)*FONTH); pophudmatrix(); } diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index 96d8b11b3..91721f326 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -53,16 +53,11 @@ VARFP(cursorblink, 0, 750, 2000, { cursorblink = cursorblink ? max(250, cursorbl CVARP(cursorcolor, 0xFFFFFF); // a cache for rendered text particles -struct partinfo -{ - textinfo ti; - partinfo() { ti.tex = 0; } -}; -static hashtable particle_cache; +static hashtable particle_cache; static vector particle_queue; static void clear_text_particles() { - enumerate(particle_cache, partinfo, info, { if(info.ti.tex) glDeleteTextures(1, &info.ti.tex); }); + enumerate(particle_cache, text::Label, label, { label.clear(); }); particle_cache.clear(); particle_queue.setsize(0); } @@ -259,7 +254,7 @@ static inline bvec text_color(char c, char *stack, int size, int &sp, bvec color // adds a string to the layout parsing basic markup (\f codes) // NOTE: `markup` is the original string with \f codes; `text` is the stripped version without \f codes -static inline void add_text_to_layout(const char *markup, int len, PangoLayout *layout, bvec color, int *map_markup_to_text, int *map_text_to_markup, const char *language, bool no_fallback) +static void add_text_to_layout(const char *markup, int len, PangoLayout *layout, bvec color, int *map_markup_to_text, int *map_text_to_markup, const char *language, bool no_fallback) { char *text = newstring(len); @@ -315,12 +310,11 @@ static inline void add_text_to_layout(const char *markup, int len, PangoLayout * default: { tcolor = text_color(markup[i+1], colorstack, sizeof(colorstack), cpos, color); - if(attr) - { - attr->end_index = j + 1; - pango_attr_list_insert(list, attr); - attr = NULL; - } + if(!attr) break; + + attr->end_index = j + 1; + pango_attr_list_insert(list, attr); + attr = NULL; } } if(map_markup_to_text) map_markup_to_text[i] = j; @@ -376,7 +370,7 @@ static inline void add_text_to_layout(const char *markup, int len, PangoLayout * } #undef MARKUP_CASE -static inline PangoLayout *measure_text_internal(const char *str, int len, int maxw, int align, int justify, bvec color, int &w, int &h, int &offset, int *map_markup_to_text, int *map_text_to_markup, const char *lang, bool no_fallback) +static PangoLayout *measure_text_internal(const char *str, int len, int maxw, int align, int justify, bvec color, int &w, int &h, int &offset, int *map_markup_to_text, int *map_text_to_markup, const char *lang, bool no_fallback) { // create cairo context cairo_t *cr = cairo_create(dummy_surface); @@ -417,240 +411,303 @@ static inline PangoLayout *measure_text_internal(const char *str, int len, int m cairo_destroy(cr); return layout; } -void measure_text(const char *str, int maxw, int &w, int &h, int align, int justify, const char *lang, bool no_fallback) -{ - int _offset; - PangoLayout *layout = measure_text_internal(str, strlen(str), maxw, align, justify, bvec(0, 0, 0), w, h, _offset, NULL, NULL, lang, no_fallback); - if(layout) g_object_unref(layout); -} -void prepare_text(const char *str, textinfo &info, int maxw, bvec color, int cursor, float outline, bvec4 ol_color, int align, int justify, const char *lang, bool no_fallback) -{ - // get dimensions and pango layout - int width, height, offset; - const int len = strlen(str); - int *map_markup_to_text = new int[len+1]; - PangoLayout *layout = measure_text_internal(str, len, maxw, align, justify, color, width, height, offset, cursor >= 0 ? map_markup_to_text : NULL, NULL, lang, no_fallback); - if(!layout) { info = {0, 0, 0}; delete[] map_markup_to_text; return; } - if(!width || !height) { g_object_unref(layout); info = {0, 0, 0}; delete[] map_markup_to_text; return; } - - // create surface and cairo context - if(cursor >= 0) width += max(4.f, fontsize); // make space for the cursor - const int ol_offset = ceil(outline); - width += 2 * ol_offset; - height += 2 * ol_offset; - cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); - cairo_t *cr = cairo_create(surface); - cairo_set_font_options(cr, options); - cairo_move_to(cr, offset + ol_offset, ol_offset); +void reloadfonts() { clear_text_particles(); UI::cleartext(); } - // draw text onto the surface - if(outline) +namespace text +{ + Label::Label() : tex(0) {}; + Label::~Label() + { + if(tex != 0) glDeleteTextures(1, &tex); + } + Label::Label(Label&& other) noexcept : tex(other.tex), w(other.w), h(other.h) + { + other.tex = 0; + } + Label& Label::operator=(Label&& other) noexcept + { + if(tex != 0) glDeleteTextures(1, &tex); + tex = other.tex; + w = other.w; + h = other.h; + other.tex = 0; + return *this; + } + void Label::clear() { - cairo_set_source_rgba(cr, ol_color.r/255.f, ol_color.g/255.f, ol_color.b/255.f, ol_color.a/255.f); - cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND); - cairo_set_line_width(cr, 2 * outline); - pango_cairo_layout_path(cr, layout); - cairo_stroke(cr); + if(tex != 0) + { + glDeleteTextures(1, &tex); + tex = 0; + } } - cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0); - cairo_move_to(cr, offset + ol_offset, ol_offset); - pango_cairo_show_layout(cr, layout); - // add the cursor - if(cursor >= 0 && ((totalmillis - inputmillis <= cursorblink) || !cursorblink || ((totalmillis - inputmillis) % (2*cursorblink)) <= cursorblink)) + void Label::draw(double left, double top, int a, bool black) const { - cursor = min((int)strlen(pango_layout_get_text(layout)), map_markup_to_text[cursor]); - PangoRectangle cur_rect; - pango_layout_get_cursor_pos(layout, cursor, &cur_rect, NULL); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - const float curw = max(1.f, fontsize / 16); - cairo_rectangle(cr, cur_rect.x/PANGO_SCALE+ol_offset, cur_rect.y/PANGO_SCALE+ol_offset, curw, cur_rect.height/PANGO_SCALE); - if(outline) + if(textshader) textshader->set(); // text particles + else hudtextshader->set(); // UI text + + gle::color(black ? bvec(0, 0, 0) : bvec(255*a/255.f, 255*a/255.f, 255*a/255.f), a); + glBindTexture(GL_TEXTURE_RECTANGLE, tex); + gle::defvertex(textmatrix ? 3 : 2); + gle::deftexcoord0(); + + // NOTE: `GL_TRIANGLE_STRIP` does not work with `textmatrix` so we have to use `GL_QUADS` instead + gle::begin(textmatrix ? GL_QUADS : GL_TRIANGLE_STRIP); + if(textmatrix) // text particle inside the world { - cairo_set_source_rgba(cr, ol_color.r/255.f, ol_color.g/255, ol_color.b/255.f, ol_color.a/255.f); - cairo_stroke_preserve(cr); + gle::attrib(textmatrix->transform(vec2(left , top ))); gle::attribf(0, 0); + gle::attrib(textmatrix->transform(vec2(left+w, top ))); gle::attribf(w, 0); + gle::attrib(textmatrix->transform(vec2(left+w, top+h))); gle::attribf(w, h); + gle::attrib(textmatrix->transform(vec2(left , top+h))); gle::attribf(0, h); } - cairo_set_source_rgba(cr, cursorcolor.r/255.f, cursorcolor.g/255.f, cursorcolor.b/255.f, 1.0); - cairo_fill(cr); + else // UI text + { + gle::attribf(left+w, top ); gle::attribf(w, 0); + gle::attribf(left , top ); gle::attribf(0, 0); + gle::attribf(left+w, top+h); gle::attribf(w, h); + gle::attribf(left , top+h); gle::attribf(0, h); + } + gle::end(); } - // create and upload texture - glGenTextures(1, &info.tex); - if(!info.tex) + void Label::draw_as_console(double left, double top) const { - info = {0, 0, 0}; - g_object_unref(layout); - cairo_destroy(cr); - cairo_surface_destroy(surface); - delete[] map_markup_to_text; - return; + if(conshadow) + { + const double d = 3.f / 4.f * conscale; + draw(left - d, top + d, conshadow, true); + } + draw(left, top); } - glBindTexture(GL_TEXTURE_RECTANGLE, info.tex); - glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_COMPRESSED_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, cairo_image_surface_get_data(surface)); - info.w = width; - info.h = height; - // clean up - g_object_unref(layout); - cairo_destroy(cr); - cairo_surface_destroy(surface); - delete[] map_markup_to_text; -} -void prepare_text_particle(const char *str, textinfo &info, bvec color, float outline, bvec4 ol_color, const char *lang, bool no_fallback) -{ - const int c = color.tohexcolor(); - const char *l = lang ? lang : ""; - uint key = crc32(0, (const Bytef *)str, strlen(str)) + curfont->id; - key = crc32(key, (const Bytef *)(&outline), sizeof(float)); - key = crc32(key, (const Bytef *)(&c), sizeof(int)); - key = crc32(key, (const Bytef *)(&ol_color.mask), sizeof(uint)); - key = crc32(key, (const Bytef *)l, strlen(l)); - partinfo &p = particle_cache[key]; - if(p.ti.tex) - { - info = p.ti; - return; - } - prepare_text(str, p.ti, 0, color, -1, outline, ol_color, -1, 0, lang, no_fallback); - if(!p.ti.tex) { info = {0, 0, 0}; return; } - if(particle_queue.length() >= 256) - { - const uint oldkey = particle_queue[0]; - partinfo &oldp = particle_cache[oldkey]; - if(oldp.ti.tex) glDeleteTextures(1, &oldp.ti.tex); - particle_cache.remove(oldkey); - particle_queue.remove(0); - } - particle_queue.add(key); - info = p.ti; -} + void measure(const char *str, int maxw, int &w, int &h, int align, int justify, const char *lang, bool no_fallback) + { + int _offset; + PangoLayout *layout = measure_text_internal(str, strlen(str), maxw, align, justify, bvec(0, 0, 0), w, h, _offset, NULL, NULL, lang, no_fallback); + if(layout) g_object_unref(layout); + } -// draw text to the screen -void draw_text(const textinfo &info, float left, float top, int a, bool black) -{ - const int w = info.w, h = info.h; + Label prepare(const char *str, int maxw, bvec color, int cursor, float outline, bvec4 ol_color, int align, int justify, const char *lang, bool no_fallback) + { + Label label; + + // measure text dimensions and create pango layout + int width, height, offset; + const int len = strlen(str); + int *map_markup_to_text = new int[len+1]; + PangoLayout *layout = measure_text_internal(str, len, maxw, align, justify, color, width, height, offset, cursor >= 0 ? map_markup_to_text : NULL, NULL, lang, no_fallback); + if(!layout) + { + delete[] map_markup_to_text; + return label; + } + if(!width || !height) + { + g_object_unref(layout); + delete[] map_markup_to_text; + return label; + } - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + // create surface and cairo context + if(cursor >= 0) width += max(4.f, fontsize); // make space for the cursor + const int ol_offset = ceil(outline); + width += 2 * ol_offset; + height += 2 * ol_offset; + cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + cairo_t *cr = cairo_create(surface); + cairo_set_font_options(cr, options); + cairo_move_to(cr, offset + ol_offset, ol_offset); + + // draw text onto the surface + if(outline) + { + cairo_set_source_rgba(cr, ol_color.r/255.f, ol_color.g/255.f, ol_color.b/255.f, ol_color.a/255.f); + cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND); + cairo_set_line_width(cr, 2 * outline); + pango_cairo_layout_path(cr, layout); + cairo_stroke(cr); + } + cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0); + cairo_move_to(cr, offset + ol_offset, ol_offset); + pango_cairo_show_layout(cr, layout); + + // add the cursor + if(cursor >= 0 && ((totalmillis - inputmillis <= cursorblink) || !cursorblink || ((totalmillis - inputmillis) % (2*cursorblink)) <= cursorblink)) + { + cursor = min((int)strlen(pango_layout_get_text(layout)), map_markup_to_text[cursor]); + PangoRectangle cur_rect; + pango_layout_get_cursor_pos(layout, cursor, &cur_rect, NULL); + + const float curw = max(1.f, fontsize / 16); + cairo_rectangle(cr, cur_rect.x/PANGO_SCALE+ol_offset, cur_rect.y/PANGO_SCALE+ol_offset, curw, cur_rect.height/PANGO_SCALE); + if(outline) + { + cairo_set_source_rgba(cr, ol_color.r/255.f, ol_color.g/255, ol_color.b/255.f, ol_color.a/255.f); + cairo_stroke_preserve(cr); + } + cairo_set_source_rgba(cr, cursorcolor.r/255.f, cursorcolor.g/255.f, cursorcolor.b/255.f, 1.0); + cairo_fill(cr); + } + + // create and upload texture + glGenTextures(1, &label.tex); + if(!label.tex) + { + g_object_unref(layout); + cairo_destroy(cr); + cairo_surface_destroy(surface); + delete[] map_markup_to_text; + return label; + } + glBindTexture(GL_TEXTURE_RECTANGLE, label.tex); + glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_COMPRESSED_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, cairo_image_surface_get_data(surface)); + label.w = width; + label.h = height; - if(textshader) textshader->set(); // text particles - else hudtextshader->set(); // UI text + // clean up + g_object_unref(layout); + cairo_destroy(cr); + cairo_surface_destroy(surface); + delete[] map_markup_to_text; + return label; + } - gle::color(black ? bvec(0, 0, 0) : bvec(255*a/255.f, 255*a/255.f, 255*a/255.f), a); - glBindTexture(GL_TEXTURE_RECTANGLE, info.tex); - gle::defvertex(textmatrix ? 3 : 2); - gle::deftexcoord0(); + Label prepare_for_console(const char *str, int maxw, int cursor) + { + return prepare(str, maxw, bvec(255, 255, 255), cursor, conoutline ? ceil(FONTH / 32.f) : 0, bvec4(0, 0, 0, conoutline)); + } - // NOTE: `GL_TRIANGLE_STRIP` does not work with `textmatrix` so we have to use `GL_QUADS` instead - gle::begin(textmatrix ? GL_QUADS : GL_TRIANGLE_STRIP); - if(textmatrix) // text particle inside the world + const Label& prepare_for_particle(const char *str, bvec color, float outline, bvec4 ol_color, const char *lang, bool no_fallback) { - gle::attrib(textmatrix->transform(vec2(left , top ))); gle::attribf(0, 0); - gle::attrib(textmatrix->transform(vec2(left+w, top ))); gle::attribf(w, 0); - gle::attrib(textmatrix->transform(vec2(left+w, top+h))); gle::attribf(w, h); - gle::attrib(textmatrix->transform(vec2(left , top+h))); gle::attribf(0, h); + const int c = color.tohexcolor(); + const char *l = lang ? lang : ""; + uint + key = crc32(0 , (const Bytef *)str, strlen(str)) + curfont->id; + key = crc32(key, (const Bytef *)(&outline), sizeof(float)); + key = crc32(key, (const Bytef *)(&c), sizeof(int)); + key = crc32(key, (const Bytef *)(&ol_color.mask), sizeof(uint)); + key = crc32(key, (const Bytef *)l, strlen(l)); + Label &label = particle_cache[key]; + if(label.valid()) return label; + label = prepare(str, 0, color, -1, outline, ol_color, -1, 0, lang, no_fallback); + if(particle_queue.length() >= 256) + { + const uint oldkey = particle_queue[0]; + particle_cache[oldkey].clear(); + particle_cache.remove(oldkey); + particle_queue.remove(0); + } + particle_queue.add(key); + return label; } - else // UI text + + void draw(const char *str, double left, double top, bvec color, int a, int maxw, int align, int justify, const char *lang, bool no_fallback) { - gle::attribf(left+w, top ); gle::attribf(w, 0); - gle::attribf(left , top ); gle::attribf(0, 0); - gle::attribf(left+w, top+h); gle::attribf(w, h); - gle::attribf(left , top+h); gle::attribf(0, h); + const Label label = prepare(str, maxw, color, -1, 0, bvec4(color, a), align, justify, lang, no_fallback); + if(label.valid()) label.draw(left, top, a); } - gle::end(); - // NOTE: `info.tex` is not deleted here! -} -void draw_text(const char *str, float left, float top, bvec color, int a, int maxw, int align, int justify, const char *lang, bool no_fallback) -{ - textinfo info; - prepare_text(str, info, maxw, color, -1, 0, bvec4(color, a), align, justify, lang, no_fallback); - if(!info.tex) return; - draw_text(info, left, top, a); - glDeleteTextures(1, &info.tex); -} -void prepare_console_text(const char *str, textinfo &info, int maxw, int cursor) -{ - prepare_text(str, info, maxw, bvec(255, 255, 255), cursor, conoutline ? ceil(FONTH / 32.f) : 0, bvec4(0, 0, 0, conoutline)); -} -void draw_console_text(const textinfo &info, float left, float top) -{ - if(conshadow) + + void draw_as_console(const char *str, double left, double top, int maxw, int cursor) { - const float d = 3.f / 4.f * conscale; - draw_text(info, left - d, top + d, conshadow, true); + const Label label = prepare_for_console(str, maxw, cursor); + if(label.valid()) + { + if(conshadow) + { + const double d = 3.f / 4.f * conscale; + label.draw(left - d, top + d, conshadow, true); + } + label.draw(left, top); + } } - draw_text(info, left, top); -} -void draw_console_text(const char *str, float left, float top, int maxw, int cursor) -{ - textinfo info; - prepare_console_text(str, info, maxw, cursor); - if(!info.tex) return; - if(conshadow) + + void getres(int &w, int &h) { - const float d = 3.f / 4.f * conscale; - draw_text(info, left - d, top + d, conshadow, true); + if(w < MINRESW || h < MINRESH) + { + if(MINRESW > w*MINRESH/h) + { + h = h*MINRESW/w; + w = MINRESW; + } + else + { + w = w*MINRESH/h; + h = MINRESH; + } + } } - draw_text(info, left, top); - glDeleteTextures(1, &info.tex); -} -void gettextres(int &w, int &h) -{ - if(w < MINRESW || h < MINRESH) + // used by the text editor + int visible(const char *str, float hitx, float hity, int maxw, int align, int justify, const char *lang, bool no_fallback) { - if(MINRESW > w*MINRESH/h) + int width, height, _offset; + const int len = strlen(str); + if(!len) return 0; + int *map_text_to_markup = new int[len+1]; + PangoLayout *layout = measure_text_internal(str, len, maxw, align, justify, bvec(0, 0, 0), width, height, _offset, NULL, map_text_to_markup, lang, no_fallback); + if(!layout) { - h = h*MINRESW/w; - w = MINRESW; + delete[] map_text_to_markup; + return len; } - else + if(!width || !height) { - w = w*MINRESH/h; - h = MINRESH; + g_object_unref(layout); + delete[] map_text_to_markup; + return len; } + + int index; + const int res = pango_layout_xy_to_index(layout, hitx * PANGO_SCALE, hity * PANGO_SCALE, &index, NULL); + g_object_unref(layout); + if(!res) + { + delete[] map_text_to_markup; + return len; + } + const int ret = map_text_to_markup[index]; + delete[] map_text_to_markup; + return ret; } -} -// used by the text editor -int text_visible(const char *str, float hitx, float hity, int maxw, int align, int justify, const char *lang, bool no_fallback) -{ - int width, height, _offset; - const int len = strlen(str); - if(!len) return 0; - int *map_text_to_markup = new int[len+1]; - PangoLayout *layout = measure_text_internal(str, len, maxw, align, justify, bvec(0, 0, 0), width, height, _offset, NULL, map_text_to_markup, lang, no_fallback); - if(!layout) { delete[] map_text_to_markup; return len; } - if(!width || !height) { g_object_unref(layout); delete[] map_text_to_markup; return len; } - - int index; - const int res = pango_layout_xy_to_index(layout, hitx * PANGO_SCALE, hity * PANGO_SCALE, &index, NULL); - g_object_unref(layout); - if(!res) { delete[] map_text_to_markup; return len; } - const int ret = map_text_to_markup[index]; - delete[] map_text_to_markup; - return ret; -} + // used by the text editor + void pos(const char *str, int cursor, int &cx, int &cy, int maxw, int align, int justify, const char *lang, bool no_fallback) + { + int width, height, _offset; + const int len = strlen(str); + if(!len) + { + cx = cy = 0; + return; + } + int *map_markup_to_text = new int[len+1]; + PangoLayout *layout = measure_text_internal(str, len, maxw, align, justify, bvec(0, 0, 0), width, height, _offset, map_markup_to_text, NULL, lang, no_fallback); + if(!layout) + { + cx = cy = 0; + delete[] map_markup_to_text; + return; + } + if(!width || !height) + { + g_object_unref(layout); + cx = cy = 0; + delete[] map_markup_to_text; + return; + } -// used by the text editor -void text_pos(const char *str, int cursor, int &cx, int &cy, int maxw, int align, int justify, const char *lang, bool no_fallback) -{ - int width, height, _offset; - const int len = strlen(str); - if(!len) { cx = cy = 0; return; } - int *map_markup_to_text = new int[len+1]; - PangoLayout *layout = measure_text_internal(str, len, maxw, align, justify, bvec(0, 0, 0), width, height, _offset, map_markup_to_text, NULL, lang, no_fallback); - if(!layout) { cx = cy = 0; delete[] map_markup_to_text; return; } - if(!width || !height) { g_object_unref(layout); cx = cy = 0; delete[] map_markup_to_text; return; } - - cursor = max(0, min((int)strlen(pango_layout_get_text(layout)), map_markup_to_text[cursor])); - PangoRectangle pos; - pango_layout_index_to_pos(layout, cursor, &pos); - cx = pos.x / PANGO_SCALE; - cy = pos.y / PANGO_SCALE; - - g_object_unref(layout); - delete[] map_markup_to_text; -} + cursor = max(0, min((int)strlen(pango_layout_get_text(layout)), map_markup_to_text[cursor])); + PangoRectangle pos; + pango_layout_index_to_pos(layout, cursor, &pos); + cx = pos.x / PANGO_SCALE; + cy = pos.y / PANGO_SCALE; -void reloadfonts() { clear_text_particles(); UI::cleartext(); } \ No newline at end of file + g_object_unref(layout); + delete[] map_markup_to_text; + } +} // namespace text \ No newline at end of file diff --git a/source/engine/textedit.h b/source/engine/textedit.h index 7be2d499a..6fed35ad1 100644 --- a/source/engine/textedit.h +++ b/source/engine/textedit.h @@ -195,7 +195,7 @@ struct editor void updateheight() { int width; - measure_text(lines[0].text, pixelwidth, width, pixelheight); + text::measure(lines[0].text, pixelwidth, width, pixelheight); } void setfile(const char *fname) @@ -470,8 +470,8 @@ struct editor { int x, y; char *str = currentline().text; - text_pos(str, cx+1, x, y, pixelwidth); - if(y > 0) { cx = text_visible(str, x, y-FONTH, pixelwidth); break; } + text::pos(str, cx+1, x, y, pixelwidth); + if(y > 0) { cx = text::visible(str, x, y-FONTH, pixelwidth); break; } } cy--; break; @@ -480,10 +480,10 @@ struct editor { int x, y, width, height; char *str = currentline().text; - text_pos(str, cx, x, y, pixelwidth); - measure_text(str, pixelwidth, width, height); + text::pos(str, cx, x, y, pixelwidth); + text::measure(str, pixelwidth, width, height); y += FONTH; - if(y < height) { cx = text_visible(str, x, y, pixelwidth); break; } + if(y < height) { cx = text::visible(str, x, y, pixelwidth); break; } } cy++; break; @@ -572,12 +572,12 @@ struct editor for(int i = scrolly; i < lines.length(); i++) { int width, height; - measure_text(lines[i].text, maxwidth, width, height); + text::measure(lines[i].text, maxwidth, width, height); if(h + height > pixelheight) break; if(hity >= h && hity <= h+height) { - int x = text_visible(lines[i].text, hitx, hity-h, maxwidth); + int x = text::visible(lines[i].text, hitx, hity-h, maxwidth); if(dragged) { mx = x; my = i; } else { cx = x; cy = i; }; break; } @@ -592,7 +592,7 @@ struct editor for(int ph = pixelheight; slines > 0 && ph > 0;) { int width, height; - measure_text(lines[slines-1].text, maxwidth, width, height); + text::measure(lines[slines-1].text, maxwidth, width, height); if(height > ph) break; ph -= height; slines--; @@ -616,7 +616,7 @@ struct editor for(int i = cy; i >= scrolly; i--) { int width, height; - measure_text(lines[i].text, maxwidth, width, height); + text::measure(lines[i].text, maxwidth, width, height); if(h + height > pixelheight) { scrolly = i+1; break; } h += height; } @@ -626,14 +626,14 @@ struct editor { // convert from cursor coords into pixel coords int psx, psy, pex, pey; - text_pos(lines[sy].text, sx, psx, psy, maxwidth); - text_pos(lines[ey].text, ex, pex, pey, maxwidth); + text::pos(lines[sy].text, sx, psx, psy, maxwidth); + text::pos(lines[ey].text, ex, pex, pey, maxwidth); int maxy = lines.length(); int h = 0; for(int i = scrolly; i < maxy; i++) { int width, height; - measure_text(lines[i].text, maxwidth, width, height); + text::measure(lines[i].text, maxwidth, width, height); if(h + height > pixelheight) { maxy = i; break; } if(i == sy) psy += h; if(i == ey) { pey += h; break; } @@ -682,28 +682,24 @@ struct editor int h = 0; for(int i = scrolly; i < lines.length(); i++) { - textinfo info; - prepare_text(lines[i].text, info, maxwidth, bvec(255, 255, 255), hit&&(cy==i)?cx:-1); - if(h + info.h > pixelheight) break; + const text::Label label = text::prepare(lines[i].text, maxwidth, bvec(255, 255, 255), hit&&(cy==i)?cx:-1); + if(h + label.height() > pixelheight) break; - if(info.tex) - { - draw_text(info, x, y); - glDeleteTextures(1, &info.tex); - } - if(linewrap && info.h > FONTH) // line wrap indicator + if(label.valid()) label.draw(x, y); + + if(linewrap && label.height() > FONTH) // line wrap indicator { hudnotextureshader->set(); gle::colorub(0x80, 0xA0, 0x80); gle::defvertex(2); gle::begin(GL_TRIANGLE_STRIP); gle::attribf(x, y+h+FONTH); - gle::attribf(x, y+h+info.h); + gle::attribf(x, y+h+label.height()); gle::attribf(x-FONTW/2, y+h+FONTH); - gle::attribf(x-FONTW/2, y+h+info.h); + gle::attribf(x-FONTW/2, y+h+label.height()); gle::end(); } - h+=info.h; + h+=label.height(); } } }; diff --git a/source/engine/ui.cpp b/source/engine/ui.cpp index 418c76116..4290ea7aa 100644 --- a/source/engine/ui.cpp +++ b/source/engine/ui.cpp @@ -2065,7 +2065,7 @@ namespace UI { float scale, wrap; Color color; - textinfo info; + text::Label label; int fontid, lastchange; int align, shadow, outlinealpha; float outline; @@ -2074,7 +2074,7 @@ namespace UI bool changed; uint crc; // string hash used for change detection - Text() : scale(0), wrap(0), color(0), info({0, 0, 0}), lastchange(0), align(curwrapalign), shadow(curshadow), outlinealpha(curfontoutlinealpha), outline(curfontoutline), justify(curjustify), nofallback(curnofallback), language(NULL), crc(0) {} + Text() : scale(0), wrap(0), color(0), lastchange(0), align(curwrapalign), shadow(curshadow), outlinealpha(curfontoutlinealpha), outline(curfontoutline), justify(curjustify), nofallback(curnofallback), language(NULL), crc(0) {} void setup(float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1) { @@ -2137,24 +2137,21 @@ namespace UI pushhudscale(textscale); if(shadow) { - draw_text(info, x-0.001/textscale, y+0.001/textscale, (color.a < shadow ? color.a : shadow), true); + label.draw(x-0.001/textscale, y+0.001/textscale, (color.a < shadow ? color.a : shadow), true); } - draw_text(info, x, y, color.a); + label.draw(x, y, color.a); pophudmatrix(); } - void hide() { Object::hide(); cleartext(); } + void hide() { Object::hide(); label.clear(); } void cleartext() { - if(info.tex) - { - glDeleteTextures(1, &info.tex); - info.tex = 0; - } + Object::cleartext(); + label.clear(); } - ~Text() { cleartext(); delete[] language; } + ~Text() { label.clear(); delete[] language; } void layout() { @@ -2176,14 +2173,14 @@ namespace UI crc = crc_new; } } - if(changed && info.tex) cleartext(); + if(changed && label.valid()) label.clear(); - if(!info.tex) + if(!label.valid()) { - prepare_text(text, info, int(wrap/k), bvec(color.r, color.g, color.b), -1, outline * FONTH / 16.f, bvec4(0, 0, 0, outlinealpha), align, justify, language, nofallback); + label = text::prepare(text, int(wrap/k), bvec(color.r, color.g, color.b), -1, outline * FONTH / 16.f, bvec4(0, 0, 0, outlinealpha), align, justify, language, nofallback); } - w = max(w, info.w*k); - h = max(h, info.h*k); + w = max(w, label.width()*k); + h = max(h, label.height()*k); } }; @@ -3918,7 +3915,7 @@ namespace UI int tw = hudw, th = hudh; if(forceaspect) tw = int(ceil(th*forceaspect)); - gettextres(tw, th); + text::getres(tw, th); uicontextscale = 1.f/th * uiscale; } diff --git a/source/shared/tools.h b/source/shared/tools.h index fa8a8eb4e..b62676442 100644 --- a/source/shared/tools.h +++ b/source/shared/tools.h @@ -1215,14 +1215,14 @@ template struct queue T remove(int offset) { - T val = removing(offset); - if(head+offset >= SIZE) for(int i = head+offset - SIZE + 1; i < tail; i++) data[i-1] = data[i]; - else if(head < tail) for(int i = head+offset + 1; i < tail; i++) data[i-1] = data[i]; + T val = (T&&)(removing(offset)); + if(head+offset >= SIZE) for(int i = head+offset - SIZE + 1; i < tail; i++) data[i-1] = (T&&)(data[i]); + else if(head < tail) for(int i = head+offset + 1; i < tail; i++) data[i-1] = (T&&)(data[i]); else { - for(int i = head+offset + 1; i < SIZE; i++) data[i-1] = data[i]; - data[SIZE-1] = data[0]; - for(int i = 1; i < tail; i++) data[i-1] = data[i]; + for(int i = head+offset + 1; i < SIZE; i++) data[i-1] = (T&&)(data[i]); + data[SIZE-1] = (T&&)(data[0]); + for(int i = 1; i < tail; i++) data[i-1] = (T&&)(data[i]); } tail--; if(tail < 0) tail += SIZE; From dcc1b005e46bbbad5df069388d6d5e31cbddbe8b Mon Sep 17 00:00:00 2001 From: Jed- Date: Fri, 29 Nov 2024 16:26:35 +0100 Subject: [PATCH 38/68] Allow `uitext` to detect clicks and key presses Added commands: - strbytelen str - char2byteindex str ix - byte2charindex str ix - getclipboard - holdingctrl - uicursorindex - uikeycode - uitextinput - uienabletextinput - uidisabletextinput - resetcursorblink --- source/engine/command.cpp | 5 +- source/engine/console.cpp | 25 +++++++++ source/engine/engine.h | 18 +++++- source/engine/rendertext.cpp | 92 +++++++++++++++++++++--------- source/engine/ui.cpp | 105 +++++++++++++++++++++++++++-------- source/shared/unicode.h | 2 +- 6 files changed, 193 insertions(+), 54 deletions(-) diff --git a/source/engine/command.cpp b/source/engine/command.cpp index d62046250..2ff5c371d 100644 --- a/source/engine/command.cpp +++ b/source/engine/command.cpp @@ -3709,7 +3709,7 @@ void chopstr(char *s, int *lim, char *ellipsis) if(*lim < 0) // strchop "abcdef" -3 "" => def ; strchop "abcdef" -3 "AB" => ABf ; strchop "abcdef" -3 "ABCD" => ABCD { uint _c; - const int offset = uni_noffset(s, maxlen - uni_elen); + const int offset = uni_negoffset(s, maxlen - uni_elen); int n = uni_offset(s + offset, maxlen - uni_elen); n += uni_getchar(s + offset + n, _c); @@ -4446,6 +4446,9 @@ ICOMMAND(codestr, "i", (int *i), uni_code2str(*i, dst); stringret(dst); }) +ICOMMAND(strbytelen, "s", (char *s), intret(strlen(s))); +ICOMMAND(char2byteindex, "si", (char *s, int *i), intret(uni_offset(s, *i))); +ICOMMAND(byte2charindex, "si", (char *s, int *i), intret(uni_index(s, *i))); int naturalsort(const char *a, const char *b) { diff --git a/source/engine/console.cpp b/source/engine/console.cpp index 24de055c3..5b345dd4a 100644 --- a/source/engine/console.cpp +++ b/source/engine/console.cpp @@ -632,6 +632,28 @@ void pasteconsole() SDL_free(cb); } +// returns the contents of the clipboard +ICOMMAND(getclipboard, "", (), +{ + if(!SDL_HasClipboardText()) + { + result(""); + } + else + { + char *cb = SDL_GetClipboardText(); + if(!cb) + { + result(""); + } + else + { + result(cb); + SDL_free(cb); + } + } +}); + static char *skipword(char *s) { while(int c = *s++) if(!iscubespace(c)) @@ -795,6 +817,9 @@ bool consolekey(int code, bool isdown) return true; } +// returns 1 if the user is holding either of the CTRL keys +ICOMMAND(holdingctrl, "", (), intret(SDL_GetModState()&MOD_KEYS ? 1 : 0)); + void processtextinput(const char *str, int len) { if(!UI::textinput(str, len)) diff --git a/source/engine/engine.h b/source/engine/engine.h index cc6fe1708..f3efaf29f 100644 --- a/source/engine/engine.h +++ b/source/engine/engine.h @@ -58,6 +58,7 @@ extern bool popfont(); extern void reloadfonts(); static inline void setfontsize(float size) { fontsize = size; } +struct _PangoLayout; namespace text { // A rendered text label, ready to be drawn @@ -65,6 +66,10 @@ namespace text { GLuint tex; int w, h; + int ox, oy; // offsets + _PangoLayout *layout; + int *map_markup_to_text; + int *map_text_to_markup; public: Label(); @@ -85,15 +90,21 @@ namespace text ) const; void draw_as_console(double left, double top) const; - friend Label prepare(const char *, int, bvec, int, float, bvec4, int, int, const char *, bool); + // do not call if the label was not prepared with `keep_layout=true` + int xy_to_index(float x, float y) const; + + friend Label prepare(const char *, int, bvec, int, float, bvec4, int, int, const char *, bool, bool); }; + // measure text before creating the label + // TODO: consider removing in favor of `Label::prepare()` void measure(const char *str, int maxw, int &w, int &h, int align = -1, int justify = 0, const char *lang = NULL, bool no_fallback = false ); + Label prepare(const char *str, int maxw, bvec color = bvec(255, 255, 255), int cursor = -1, @@ -101,8 +112,9 @@ namespace text bvec4 ol_color = bvec4(0, 0, 0, 0), int align = -1, int justify = 0, - const char *lang = NULL, - bool no_fallback = false + const char *lang = NULL, // language code, used for text shaping + bool no_fallback = false, // don't use fallback fonts for unavailable glyphs + bool keep_layout = false // use only if you need to call `xy_to_index()` ); Label prepare_for_console(const char *str, int maxw, int cursor); const Label& prepare_for_particle(const char *str, diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index 91721f326..9591faefe 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -332,6 +332,7 @@ static void add_text_to_layout(const char *markup, int len, PangoLayout *layout, } text[j] = '\0'; if(map_markup_to_text) map_markup_to_text[i] = j; + if(map_text_to_markup) map_text_to_markup[j] = i; if(attr) { @@ -416,22 +417,40 @@ void reloadfonts() { clear_text_particles(); UI::cleartext(); } namespace text { - Label::Label() : tex(0) {}; + Label::Label() : tex(0), layout(nullptr), map_markup_to_text(nullptr), map_text_to_markup(nullptr) {}; Label::~Label() { + delete[] map_markup_to_text; + delete[] map_text_to_markup; + if(layout) g_object_unref(layout); if(tex != 0) glDeleteTextures(1, &tex); } - Label::Label(Label&& other) noexcept : tex(other.tex), w(other.w), h(other.h) + Label::Label(Label&& other) noexcept : tex(other.tex), w(other.w), h(other.h), ox(other.ox), oy(other.oy), layout(other.layout), map_markup_to_text(other.map_markup_to_text), map_text_to_markup(other.map_text_to_markup) { + other.map_markup_to_text = nullptr; + other.map_text_to_markup = nullptr; + other.layout = nullptr; other.tex = 0; } Label& Label::operator=(Label&& other) noexcept { + if(&other == this) return *this; + delete[] map_markup_to_text; + delete[] map_text_to_markup; + if(layout) g_object_unref(layout); if(tex != 0) glDeleteTextures(1, &tex); tex = other.tex; w = other.w; h = other.h; + ox = other.ox; + oy = other.oy; + layout = other.layout; + map_markup_to_text = other.map_markup_to_text; + map_text_to_markup = other.map_text_to_markup; other.tex = 0; + other.layout = nullptr; + other.map_markup_to_text = nullptr; + other.map_text_to_markup = nullptr; return *this; } void Label::clear() @@ -441,6 +460,15 @@ namespace text glDeleteTextures(1, &tex); tex = 0; } + if(layout != nullptr) + { + g_object_unref(layout); + layout = nullptr; + } + delete[] map_markup_to_text; + delete[] map_text_to_markup; + map_markup_to_text = nullptr; + map_text_to_markup = nullptr; } void Label::draw(double left, double top, int a, bool black) const @@ -484,6 +512,19 @@ namespace text draw(left, top); } + // TODO: apply x/y offsets + int Label::xy_to_index(float x, float y) const + { + const int px = (x - ox) * PANGO_SCALE, py = (y - oy) * PANGO_SCALE; + int ix, _trailing; + if(!pango_layout_xy_to_index(layout, px, py, &ix, &_trailing)) + { + // user clicked outside of the label: set the cursor to the end of the string + ix = strlen(pango_layout_get_text(layout)); + } + return map_text_to_markup ? map_text_to_markup[ix] : ix; + } + void measure(const char *str, int maxw, int &w, int &h, int align, int justify, const char *lang, bool no_fallback) { int _offset; @@ -491,24 +532,18 @@ namespace text if(layout) g_object_unref(layout); } - Label prepare(const char *str, int maxw, bvec color, int cursor, float outline, bvec4 ol_color, int align, int justify, const char *lang, bool no_fallback) + Label prepare(const char *str, int maxw, bvec color, int cursor, float outline, bvec4 ol_color, int align, int justify, const char *lang, bool no_fallback, bool keep_layout) { Label label; // measure text dimensions and create pango layout int width, height, offset; const int len = strlen(str); - int *map_markup_to_text = new int[len+1]; - PangoLayout *layout = measure_text_internal(str, len, maxw, align, justify, color, width, height, offset, cursor >= 0 ? map_markup_to_text : NULL, NULL, lang, no_fallback); - if(!layout) + if(cursor >= 0 || keep_layout) label.map_markup_to_text = new int[len+1]; + if(keep_layout) label.map_text_to_markup = new int[len+1]; + label.layout = measure_text_internal(str, len, maxw, align, justify, color, width, height, offset, label.map_markup_to_text ? label.map_markup_to_text : NULL, label.map_text_to_markup ? label.map_text_to_markup : NULL, lang, no_fallback); + if(!label.layout || !width || !height) { - delete[] map_markup_to_text; - return label; - } - if(!width || !height) - { - g_object_unref(layout); - delete[] map_markup_to_text; return label; } @@ -522,25 +557,23 @@ namespace text cairo_set_font_options(cr, options); cairo_move_to(cr, offset + ol_offset, ol_offset); - // draw text onto the surface + // draw text outline if(outline) { cairo_set_source_rgba(cr, ol_color.r/255.f, ol_color.g/255.f, ol_color.b/255.f, ol_color.a/255.f); cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND); cairo_set_line_width(cr, 2 * outline); - pango_cairo_layout_path(cr, layout); + pango_cairo_layout_path(cr, label.layout); cairo_stroke(cr); } - cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0); - cairo_move_to(cr, offset + ol_offset, ol_offset); - pango_cairo_show_layout(cr, layout); - // add the cursor + // draw cursor outline and the cursor itself on top of it if(cursor >= 0 && ((totalmillis - inputmillis <= cursorblink) || !cursorblink || ((totalmillis - inputmillis) % (2*cursorblink)) <= cursorblink)) { - cursor = min((int)strlen(pango_layout_get_text(layout)), map_markup_to_text[cursor]); + if(cursor > len) cursor = len; + cursor = min((int)strlen(pango_layout_get_text(label.layout)), label.map_markup_to_text[cursor]); PangoRectangle cur_rect; - pango_layout_get_cursor_pos(layout, cursor, &cur_rect, NULL); + pango_layout_get_cursor_pos(label.layout, cursor, &cur_rect, NULL); const float curw = max(1.f, fontsize / 16); cairo_rectangle(cr, cur_rect.x/PANGO_SCALE+ol_offset, cur_rect.y/PANGO_SCALE+ol_offset, curw, cur_rect.height/PANGO_SCALE); @@ -553,26 +586,35 @@ namespace text cairo_fill(cr); } + // draw text on top of everything + cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0); + cairo_move_to(cr, offset + ol_offset, ol_offset); + pango_cairo_show_layout(cr, label.layout); + + if(!keep_layout) + { + g_object_unref(label.layout); + label.layout = nullptr; + } + // create and upload texture glGenTextures(1, &label.tex); if(!label.tex) { - g_object_unref(layout); cairo_destroy(cr); cairo_surface_destroy(surface); - delete[] map_markup_to_text; return label; } glBindTexture(GL_TEXTURE_RECTANGLE, label.tex); glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_COMPRESSED_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, cairo_image_surface_get_data(surface)); label.w = width; label.h = height; + label.ox = offset + ol_offset; + label.oy = ol_offset; // clean up - g_object_unref(layout); cairo_destroy(cr); cairo_surface_destroy(surface); - delete[] map_markup_to_text; return label; } diff --git a/source/engine/ui.cpp b/source/engine/ui.cpp index 4290ea7aa..0e087cc96 100644 --- a/source/engine/ui.cpp +++ b/source/engine/ui.cpp @@ -839,6 +839,8 @@ namespace UI void hide(Window *w, int index) { + ::textinput(false, TI_GUI); + ::keyrepeat(false, KR_GUI); children.remove(index); childstate = 0; loopchildren(o, childstate |= o->state | o->childstate); @@ -2060,6 +2062,26 @@ namespace UI #undef WITHTEXTATTR + static int uicursorindex = -1; + ICOMMAND(uicursorindex, "", (), intret(uicursorindex)); + + // makes the text input cursor stop blinking for a short while, call this when setting the cursor position from cubescript + ICOMMAND(resetcursorblink, "", (), { inputmillis = totalmillis; }); + + string uikeycode, uitextinput; + ICOMMAND(uikeycode, "", (), result(uikeycode)); + ICOMMAND(uitextinput, "", (), result(uitextinput)); + ICOMMAND(uienabletextinput, "", (), + { + ::textinput(true, TI_GUI); + ::keyrepeat(true, KR_GUI); + }); + ICOMMAND(uidisabletextinput, "", (), + { + ::textinput(false, TI_GUI); + ::keyrepeat(false, KR_GUI); + }) + // NOTE: `scale` is the text height in screenfuls at `uiscale 1` struct Text : Object { @@ -2071,12 +2093,14 @@ namespace UI float outline; bool justify, nofallback; const char *language; + int cursor; + bool has_cursor; bool changed; uint crc; // string hash used for change detection - Text() : scale(0), wrap(0), color(0), lastchange(0), align(curwrapalign), shadow(curshadow), outlinealpha(curfontoutlinealpha), outline(curfontoutline), justify(curjustify), nofallback(curnofallback), language(NULL), crc(0) {} + Text() : scale(0), wrap(0), color(0), lastchange(0), align(curwrapalign), shadow(curshadow), outlinealpha(curfontoutlinealpha), outline(curfontoutline), justify(curjustify), nofallback(curnofallback), language(NULL), cursor(-1), has_cursor(false), crc(0) {} - void setup(float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1) + void setup(float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1, int cursor_ = -1, bool has_cursor_ = false) { Object::setup(); changed = false; @@ -2096,7 +2120,9 @@ namespace UI curfontoutline != outline || curfontoutlinealpha != outlinealpha || curnofallback != nofallback || - (!language || strcmp(curlanguage, language)) + (!language || strcmp(curlanguage, language)) || + cursor_ != cursor || + cursor_ >= 0 // ensures the cursor blinks ) { changed = true; @@ -2114,7 +2140,34 @@ namespace UI outlinealpha = curfontoutlinealpha; nofallback = curnofallback; SETSTR(language, curlanguage); + cursor = cursor_; } + has_cursor = has_cursor_; + } + + void press(float cx, float cy) + { + if(!has_cursor) return; + const float k = drawscale(); + uicursorindex = label.xy_to_index(cx/k, cy/k); + } + + bool key(int code, bool isdown) + { + if(Object::key(code, isdown)) return true; + if(!isdown || cursor < 0) return false; + const char *keyname = getkeyname(code); + if(!keyname) return false; + copystring(uikeycode, keyname); + return true; + } + + bool textinput(const char *str, int len) + { + if(Object::textinput(str, len)) return true; + if(cursor < 0) return false; + copystring(uitextinput, str, len+1); + return true; } static const char *typestr() { return "#Text"; } @@ -2151,12 +2204,16 @@ namespace UI label.clear(); } - ~Text() { label.clear(); delete[] language; } + ~Text() { delete[] language; } void layout() { Object::layout(); + uicursorindex = -1; + copystring(uikeycode, ""); + copystring(uitextinput, ""); + setfontsize(scale * hudh); float k = drawscale(); @@ -2177,7 +2234,7 @@ namespace UI if(!label.valid()) { - label = text::prepare(text, int(wrap/k), bvec(color.r, color.g, color.b), -1, outline * FONTH / 16.f, bvec4(0, 0, 0, outlinealpha), align, justify, language, nofallback); + label = text::prepare(text, int(wrap/k), bvec(color.r, color.g, color.b), cursor, outline * FONTH / 16.f, bvec4(0, 0, 0, outlinealpha), align, justify, language, nofallback, /*keep_layout=*/has_cursor); } w = max(w, label.width()*k); h = max(h, label.height()*k); @@ -2191,9 +2248,9 @@ namespace UI TextString() : str(NULL) {} ~TextString() { delete[] str; } - void setup(const char *str_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1) + void setup(const char *str_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1, int cursor_ = -1, bool has_cursor_ = false) { - Text::setup(scale_, color_, wrap_); + Text::setup(scale_, color_, wrap_, cursor_, has_cursor_); SETSTR(str, str_); } @@ -2211,9 +2268,9 @@ namespace UI TextInt() : val(0) { str[0] = '0'; str[1] = '\0'; } - void setup(int val_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1) + void setup(int val_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1, int cursor_ = -1, bool has_cursor_ = false) { - Text::setup(scale_, color_, wrap_); + Text::setup(scale_, color_, wrap_, cursor_, has_cursor_); if(val != val_) { val = val_; intformat(str, val, sizeof(str)); } } @@ -2231,9 +2288,9 @@ namespace UI TextFloat() : val(0) { memcpy(str, "0.0", 4); } - void setup(float val_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1) + void setup(float val_, float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1, int cursor_ = -1, bool has_cursor_ = false) { - Text::setup(scale_, color_, wrap_); + Text::setup(scale_, color_, wrap_, cursor_, has_cursor_); if(val != val_) { val = val_; floatformat(str, val, sizeof(str)); } } @@ -3673,24 +3730,24 @@ namespace UI ICOMMAND(uilanguage, "se", (char *val, uint *children), BUILD(Language, o, o->setup(val), children)); - static inline void buildtext(tagval &t, float scale, float scalemod, const Color &color, float wrap, uint *children) + static inline void buildtext(tagval &t, float scale, float scalemod, const Color &color, float wrap, int cursor, bool has_cursor, uint *children) { if(scale <= 0) scale = 1; scale *= scalemod; switch(t.type) { case VAL_INT: - BUILD(TextInt, o, o->setup(t.i, scale, color, wrap), children); + BUILD(TextInt, o, o->setup(t.i, scale, color, wrap, cursor, has_cursor), children); break; case VAL_FLOAT: - BUILD(TextFloat, o, o->setup(t.f, scale, color, wrap), children); + BUILD(TextFloat, o, o->setup(t.f, scale, color, wrap, cursor, has_cursor), children); break; case VAL_CSTR: case VAL_MACRO: case VAL_STR: if(t.s[0]) { - BUILD(TextString, o, o->setup(t.s, scale, color, wrap), children); + BUILD(TextString, o, o->setup(t.s, scale, color, wrap, cursor, has_cursor), children); break; } // fall-through @@ -3701,34 +3758,34 @@ namespace UI } ICOMMAND(uicolortext, "tife", (tagval *text, int *c, float *scale, uint *children), - buildtext(*text, *scale, uitextscale, Color(*c), -1, children)); + buildtext(*text, *scale, uitextscale, Color(*c), -1, -1, false, children)); - ICOMMAND(uitext, "tfe", (tagval *text, float *scale, uint *children), - buildtext(*text, *scale, uitextscale, Color(255, 255, 255), -1, children)); + ICOMMAND(uitext, "tfieN", (tagval *text, float *scale, int *cursor, uint *children, int *numargs), + buildtext(*text, *scale, uitextscale, Color(255, 255, 255), -1, *numargs >= 3 ? *cursor : -1, *numargs >= 3, children)); ICOMMAND(uitextfill, "ffe", (float *minw, float *minh, uint *children), BUILD(Filler, o, o->setup(*minw * uitextscale*0.5f, *minh * uitextscale), children)); ICOMMAND(uiwrapcolortext, "tfife", (tagval *text, float *wrap, int *c, float *scale, uint *children), - buildtext(*text, *scale, uitextscale, Color(*c), *wrap, children)); + buildtext(*text, *scale, uitextscale, Color(*c), *wrap, -1, false, children)); ICOMMAND(uiwraptext, "tffe", (tagval *text, float *wrap, float *scale, uint *children), - buildtext(*text, *scale, uitextscale, Color(255, 255, 255), *wrap, children)); + buildtext(*text, *scale, uitextscale, Color(255, 255, 255), *wrap, -1, false, children)); ICOMMAND(uicolorcontext, "tife", (tagval *text, int *c, float *scale, uint *children), - buildtext(*text, *scale, FONTH*uicontextscale, Color(*c), -1, children)); + buildtext(*text, *scale, FONTH*uicontextscale, Color(*c), -1, -1, false, children)); ICOMMAND(uicontext, "tfe", (tagval *text, float *scale, uint *children), - buildtext(*text, *scale, FONTH*uicontextscale, Color(255, 255, 255), -1, children)); + buildtext(*text, *scale, FONTH*uicontextscale, Color(255, 255, 255), -1, -1, false, children)); ICOMMAND(uicontextfill, "ffe", (float *minw, float *minh, uint *children), BUILD(Filler, o, o->setup(*minw * FONTH*uicontextscale*0.5f, *minh * FONTH*uicontextscale), children)); ICOMMAND(uiwrapcolorcontext, "tfife", (tagval *text, float *wrap, int *c, float *scale, uint *children), - buildtext(*text, *scale, FONTH*uicontextscale, Color(*c), *wrap, children)); + buildtext(*text, *scale, FONTH*uicontextscale, Color(*c), *wrap, -1, false, children)); ICOMMAND(uiwrapcontext, "tffe", (tagval *text, float *wrap, float *scale, uint *children), - buildtext(*text, *scale, FONTH*uicontextscale, Color(255, 255, 255), *wrap, children)); + buildtext(*text, *scale, FONTH*uicontextscale, Color(255, 255, 255), *wrap, -1, false, children)); ICOMMAND(uitexteditor, "siifsie", (char *name, int *length, int *height, float *scale, char *initval, int *mode, uint *children), BUILD(TextEditor, o, o->setup(name, *length, *height, (*scale <= 0 ? 1 : *scale) * uitextscale, initval, *mode <= 0 ? EDITORFOREVER : *mode), children)); diff --git a/source/shared/unicode.h b/source/shared/unicode.h index 482281f8d..d46cd0b4b 100644 --- a/source/shared/unicode.h +++ b/source/shared/unicode.h @@ -135,7 +135,7 @@ static inline int uni_offset(const char *str, uint ix) return (p - str); } // same but starting from the end of the string (str[-ix]) -static inline int uni_noffset(const char *str, uint ix) +static inline int uni_negoffset(const char *str, uint ix) { const char *p = str + strlen(str); loopi(ix) From 80cf0b6ec7ef1f8541085d473e1a7eab8dbef1dd Mon Sep 17 00:00:00 2001 From: Jed- Date: Sat, 30 Nov 2024 14:17:26 +0100 Subject: [PATCH 39/68] Convert tabs to spaces --- config/ui/hud/editstats.cfg | 658 +++++++++---------- config/ui/hud/gamehud.cfg | 972 ++++++++++++++--------------- config/ui/hud/geo_prefab.cfg | 96 +-- config/ui/hud/mapmodel_browser.cfg | 264 ++++---- config/ui/hud/texture_browser.cfg | 334 +++++----- config/ui/lib.cfg | 260 ++++---- config/ui/libnew.cfg | 258 ++++---- config/ui/permanent.cfg | 158 ++--- 8 files changed, 1500 insertions(+), 1500 deletions(-) diff --git a/config/ui/hud/editstats.cfg b/config/ui/hud/editstats.cfg index a46c2be5e..58bb3042a 100644 --- a/config/ui/hud/editstats.cfg +++ b/config/ui/hud/editstats.cfg @@ -1,236 +1,236 @@ uiLiteMenu "edithud" [ - uiallowinput (iskeyheld "LALT") - uieschide 0 - //uiabovehud - uialign -1 1 + uiallowinput (iskeyheld "LALT") + uieschide 0 + //uiabovehud + uialign -1 1 ] [ - uihlist $uiPad:L [ - uifill 0.1 - if $enthavesel [ - local ea0 ea1 ea2 ea3 ea4 - loop i 5 [ [ea@i] = (entattr $i) ] - cases $enttype "particles" [ - uiEntCell "1" [ - uiOptionSldWrp ea0 [ - 0 "Fire" - 1 "Steam" - 2 "Fountain" - 3 "Explosion" - 4 "Flare" - 5 "Cap. Bar" - 6 "Versus Bar" - 7 "Lightning" - 9 "Smoke Vent" - 10 "Water Drip" - 11 "Ghost Fire" - 12 "Smoke" - 13 "Snow" - 14 "Spark" - 32 "Lens #1" - 33 "Lens #2" - 34 "Lens #3" - 35 "Lens #4" - ] [ entset "particles" $ea0 ] - ] "Type" - cond (> (indexof "0 11 12" $ea0) -1) [ - uiEntCell "2" [ - uiHorSld ea1 0 1000 1 [ entattr 1 $ea1 ] - ] "Width" - uiEntCell "3" [ - uiHorSld ea2 0 1000 1 [ entattr 2 $ea2 ] - ] "Height" - uiEntCell "4" [ - uiEntColor12B 3 - ] - ] (> (indexof "5 6" $ea0) -1) [ - uiEntCell "2" [ - uiHorSld ea1 0 100 [ entattr 1 $ea1 ] - ] "Percentage" - uiEntCell "3" [ - uiEntColor12B 2 - ] - if (= $ea0 6) [ - uiEntCell "4" [ - uiEntColor12B 3 - ] - ] - ] (> (indexof "32 33 34 35" $ea0) -1) [ - uiEntCell "2 3 4" [ - uiEntColor24B - ] - ] (> (indexof "4 7 9 10 13" $ea0) -1) [ - uiEntCell "2" [ // XXX needs work - // NADA HERE - ] "Shape" - uiEntCell "3" [ - uiHorSld ea2 0 200 1 [ entattr 2 $ea2 ] - ] "Size" - uiEntCell "4" [ - uiEntColor12B 3 - ] - uiEntCell "5" [ - uiHorSld ea4 0 50000 200 [ entattr 4 $ea4 ] - ] "Fade Time" - ] (= $ea0 1) [ - uiEntCell "2" [ - uiOptionSldWrp ea1 [ - -3 "Center" - 0 "+Z" - 1 "+Y" - 2 "+X" - 3 "-Z" - 4 "-Y" - 5 "-X" - ] [ entattr 1 $ea1 ] - ] "Orientation" - ] (= $ea0 2) [ - uiEntCell "2" [ - uiOptionSldWrp ea1 [ - 0 "+Z" - 1 "+Y" - 2 "+X" - 3 "-Z" - 4 "-Y" - 5 "-X" - ] [ entattr 1 $ea1 ] - ] "Orientation" - uiEntCell "3" [ - uiEntColor12B 2 - ] - ] (= $ea0 3) [ - uiEntCell "2" [ - uiHorSld ea1 -4 100 1 [ entattr 1 $ea1 ] - ] "Size" - if (> $ea0 1) [ - uiEntCell "3" [ - uiEntColor12B 2 - ] - ] - ] - ] "light" [ - uiEntCell "1" [ - uiHorSld ea0 -1 500 1 [ entattr 0 $ea0 ] - ] "Radius" - uiEntCell "2 3 4" [ - uiEntColor24B - ] "" 0 - uiEntCell "5" [ // XXX needs work - uiButton "hold2 $c_diag1 d d" [ uiCheckbox [& $ea4 1] ; uitext "Shadowless" 0.6 ] 0 0 [ entattr 4 (^ $ea4 1) ] -1 - uiButton "hold2 $c_diag1 d d" [ uiCheckbox [& $ea4 2] ; uitext "Static" 0.6 ] 0 0 [ entattr 4 (^ $ea4 2) ] -1 - uiButton "hold2 $c_diag1 d d" [ uiCheckbox [& $ea4 4] ; uitext "Volumetric" 0.6 ] 0 0 [ entattr 4 (^ $ea4 4) ] -1 - ] "Properties" 0 - ] "spotlight" [ - uiEntCell "1" [ - uiHorSld ea0 1 85 1 [ entattr 0 $ea0 ] - ] "Cone Angle" - ] "envmap" [ - uiEntCell "1" [ - uiHorSld ea0 1 500 1 [ entattr 0 $ea0 ] - ] "Size" - ] "jumppad" [ - uiEntCell "1" [ - uiHorSld ea0 -100 100 1 [ entattr 0 $ea0 ] - ] "Axis Z" - uiEntCell "2" [ - uiHorSld ea1 -100 100 1 [ entattr 1 $ea1 ] - ] "Axis Y" - uiEntCell "3" [ - uiHorSld ea2 -100 100 1 [ entattr 2 $ea2 ] - ] "Axis X" - uiEntCell "4" [ - uiHorSld ea3 -1 (- $nummapsounds 1) 1 [ entattr 3 $ea3 ] - ] "Sound ID#" - ] "teleport" [ - uiEntCell "1" [ - uiHorSld ea0 0 49 1 [ entattr 0 $ea0 ] - ] "Teleport ID#" - uiEntCell "2" [ - uiHorSld ea1 -1 (- $nummapmodels 1) 1 [ entattr 1 $ea1 ] - ] "Model ID#" - uiEntCell "3" [ // XXX needs work - uiHorSld ea2 0 49 1 [ entattr 2 $ea2 ] - ] "Trigger ID#" // can_teleport_# = [..] - uiEntCell "4" [ - uiHorSld ea3 -1 (- $nummapsounds 1) 1 [ entattr 3 $ea3 ] - ] "Sound ID#" - ] "teledest" [ - uiEntCell "1" [ - uiHorSld ea0 0 360 5 [ entattr 0 $ea0 ] - ] "Orientation" - uiEntCell "2" [ - uiHorSld ea1 0 49 1 [ entattr 1 $ea1 ] - ] "Teleport ID#" - uiEntCell "3" [ - uiHorSld ea2 0 1 1 [ entattr 2 $ea2 ] - ] "Keep Velocity" - ] "mapmodel" [ - uiEntCell "1" [ - uiHorSld ea0 -1 (- $nummapmodels 1) 1 [ entattr 0 $ea0 ] - ] "Model ID#" - uiEntCell "2" [ - uiHorSld ea1 0 360 1 [ entattr 1 $ea1 ] - ] "Yaw" - uiEntCell "3" [ - uiHorSld ea2 0 360 1 [ entattr 2 $ea2 ] - ] "Pitch" - uiEntCell "4" [ - uiHorSld ea3 0 360 1 [ entattr 3 $ea3 ] - ] "Roll" - uiEntCell "5" [ - uiHorSld ea4 0 500 1 [ entattr 4 $ea4 ] - ] "Scale" - ] "decal" [ - uiEntCell "1" [ - uiHorSld ea0 0 (- $numdecalslots 1) 1 [ entattr 0 $ea0 ] - ] "Decal ID#" - uiEntCell "2" [ - uiHorSld ea1 0 360 1 [ entattr 1 $ea1 ] - ] "Yaw" - uiEntCell "3" [ - uiHorSld ea2 0 360 1 [ entattr 2 $ea2 ] - ] "Pitch" - uiEntCell "4" [ - uiHorSld ea3 0 360 1 [ entattr 3 $ea3 ] - ] "Roll" - uiEntCell "5" [ - uiHorSld ea4 1 200 1 [ entattr 4 $ea4 ] - ] "Scale" - ] "sound" [ - uiEntCell "1" [ - uiHorSld ea0 0 (- $nummapsounds 1) 1 [ entattr 0 $ea0 ] - ] "Sound ID#" - uiEntCell "2" [ - uiHorSld ea1 1 500 1 [ entattr 1 $ea1 ] - ] "Radius" - uiEntCell "3" [ - uiHorSld ea2 1 500 1 [ entattr 2 $ea2 ] - ] "Attenuation" - uiEntCell "4" [ - uiHorSld ea3 1 500 1 [ entattr 3 $ea3 ] - ] "Delay" - ] "playerstart" [ - uiEntCell "1" [ - uiHorSld ea0 0 360 5 [ entattr 0 $ea0 ] - ] "Orientation" - uiEntCell "2" [ // XXX needs work - uiButton "hold2 $c_diag1 d d" [ uiRadio [= $ea1 0] ; uitext "No Team" 0.6 ] 0 0 [ entattr 1 0 ] -1 - uiButton "hold2 $c_diag1 d d" [ uiRadio [= $ea1 1] ; uitext "Team ^f8Aesir" 0.6 ] 0 0 [ entattr 1 1 ] -1 - uiButton "hold2 $c_diag1 d d" [ uiRadio [= $ea1 2] ; uitext "Team ^f3Vanir" 0.6 ] 0 0 [ entattr 1 2 ] -1 - ] "Team" 0 + uihlist $uiPad:L [ + uifill 0.1 + if $enthavesel [ + local ea0 ea1 ea2 ea3 ea4 + loop i 5 [ [ea@i] = (entattr $i) ] + cases $enttype "particles" [ + uiEntCell "1" [ + uiOptionSldWrp ea0 [ + 0 "Fire" + 1 "Steam" + 2 "Fountain" + 3 "Explosion" + 4 "Flare" + 5 "Cap. Bar" + 6 "Versus Bar" + 7 "Lightning" + 9 "Smoke Vent" + 10 "Water Drip" + 11 "Ghost Fire" + 12 "Smoke" + 13 "Snow" + 14 "Spark" + 32 "Lens #1" + 33 "Lens #2" + 34 "Lens #3" + 35 "Lens #4" + ] [ entset "particles" $ea0 ] + ] "Type" + cond (> (indexof "0 11 12" $ea0) -1) [ + uiEntCell "2" [ + uiHorSld ea1 0 1000 1 [ entattr 1 $ea1 ] + ] "Width" + uiEntCell "3" [ + uiHorSld ea2 0 1000 1 [ entattr 2 $ea2 ] + ] "Height" + uiEntCell "4" [ + uiEntColor12B 3 + ] + ] (> (indexof "5 6" $ea0) -1) [ + uiEntCell "2" [ + uiHorSld ea1 0 100 [ entattr 1 $ea1 ] + ] "Percentage" + uiEntCell "3" [ + uiEntColor12B 2 + ] + if (= $ea0 6) [ + uiEntCell "4" [ + uiEntColor12B 3 + ] + ] + ] (> (indexof "32 33 34 35" $ea0) -1) [ + uiEntCell "2 3 4" [ + uiEntColor24B + ] + ] (> (indexof "4 7 9 10 13" $ea0) -1) [ + uiEntCell "2" [ // XXX needs work + // NADA HERE + ] "Shape" + uiEntCell "3" [ + uiHorSld ea2 0 200 1 [ entattr 2 $ea2 ] + ] "Size" + uiEntCell "4" [ + uiEntColor12B 3 + ] + uiEntCell "5" [ + uiHorSld ea4 0 50000 200 [ entattr 4 $ea4 ] + ] "Fade Time" + ] (= $ea0 1) [ + uiEntCell "2" [ + uiOptionSldWrp ea1 [ + -3 "Center" + 0 "+Z" + 1 "+Y" + 2 "+X" + 3 "-Z" + 4 "-Y" + 5 "-X" + ] [ entattr 1 $ea1 ] + ] "Orientation" + ] (= $ea0 2) [ + uiEntCell "2" [ + uiOptionSldWrp ea1 [ + 0 "+Z" + 1 "+Y" + 2 "+X" + 3 "-Z" + 4 "-Y" + 5 "-X" + ] [ entattr 1 $ea1 ] + ] "Orientation" + uiEntCell "3" [ + uiEntColor12B 2 + ] + ] (= $ea0 3) [ + uiEntCell "2" [ + uiHorSld ea1 -4 100 1 [ entattr 1 $ea1 ] + ] "Size" + if (> $ea0 1) [ + uiEntCell "3" [ + uiEntColor12B 2 + ] + ] + ] + ] "light" [ + uiEntCell "1" [ + uiHorSld ea0 -1 500 1 [ entattr 0 $ea0 ] + ] "Radius" + uiEntCell "2 3 4" [ + uiEntColor24B + ] "" 0 + uiEntCell "5" [ // XXX needs work + uiButton "hold2 $c_diag1 d d" [ uiCheckbox [& $ea4 1] ; uitext "Shadowless" 0.6 ] 0 0 [ entattr 4 (^ $ea4 1) ] -1 + uiButton "hold2 $c_diag1 d d" [ uiCheckbox [& $ea4 2] ; uitext "Static" 0.6 ] 0 0 [ entattr 4 (^ $ea4 2) ] -1 + uiButton "hold2 $c_diag1 d d" [ uiCheckbox [& $ea4 4] ; uitext "Volumetric" 0.6 ] 0 0 [ entattr 4 (^ $ea4 4) ] -1 + ] "Properties" 0 + ] "spotlight" [ + uiEntCell "1" [ + uiHorSld ea0 1 85 1 [ entattr 0 $ea0 ] + ] "Cone Angle" + ] "envmap" [ + uiEntCell "1" [ + uiHorSld ea0 1 500 1 [ entattr 0 $ea0 ] + ] "Size" + ] "jumppad" [ + uiEntCell "1" [ + uiHorSld ea0 -100 100 1 [ entattr 0 $ea0 ] + ] "Axis Z" + uiEntCell "2" [ + uiHorSld ea1 -100 100 1 [ entattr 1 $ea1 ] + ] "Axis Y" + uiEntCell "3" [ + uiHorSld ea2 -100 100 1 [ entattr 2 $ea2 ] + ] "Axis X" + uiEntCell "4" [ + uiHorSld ea3 -1 (- $nummapsounds 1) 1 [ entattr 3 $ea3 ] + ] "Sound ID#" + ] "teleport" [ + uiEntCell "1" [ + uiHorSld ea0 0 49 1 [ entattr 0 $ea0 ] + ] "Teleport ID#" + uiEntCell "2" [ + uiHorSld ea1 -1 (- $nummapmodels 1) 1 [ entattr 1 $ea1 ] + ] "Model ID#" + uiEntCell "3" [ // XXX needs work + uiHorSld ea2 0 49 1 [ entattr 2 $ea2 ] + ] "Trigger ID#" // can_teleport_# = [..] + uiEntCell "4" [ + uiHorSld ea3 -1 (- $nummapsounds 1) 1 [ entattr 3 $ea3 ] + ] "Sound ID#" + ] "teledest" [ + uiEntCell "1" [ + uiHorSld ea0 0 360 5 [ entattr 0 $ea0 ] + ] "Orientation" + uiEntCell "2" [ + uiHorSld ea1 0 49 1 [ entattr 1 $ea1 ] + ] "Teleport ID#" + uiEntCell "3" [ + uiHorSld ea2 0 1 1 [ entattr 2 $ea2 ] + ] "Keep Velocity" + ] "mapmodel" [ + uiEntCell "1" [ + uiHorSld ea0 -1 (- $nummapmodels 1) 1 [ entattr 0 $ea0 ] + ] "Model ID#" + uiEntCell "2" [ + uiHorSld ea1 0 360 1 [ entattr 1 $ea1 ] + ] "Yaw" + uiEntCell "3" [ + uiHorSld ea2 0 360 1 [ entattr 2 $ea2 ] + ] "Pitch" + uiEntCell "4" [ + uiHorSld ea3 0 360 1 [ entattr 3 $ea3 ] + ] "Roll" + uiEntCell "5" [ + uiHorSld ea4 0 500 1 [ entattr 4 $ea4 ] + ] "Scale" + ] "decal" [ + uiEntCell "1" [ + uiHorSld ea0 0 (- $numdecalslots 1) 1 [ entattr 0 $ea0 ] + ] "Decal ID#" + uiEntCell "2" [ + uiHorSld ea1 0 360 1 [ entattr 1 $ea1 ] + ] "Yaw" + uiEntCell "3" [ + uiHorSld ea2 0 360 1 [ entattr 2 $ea2 ] + ] "Pitch" + uiEntCell "4" [ + uiHorSld ea3 0 360 1 [ entattr 3 $ea3 ] + ] "Roll" + uiEntCell "5" [ + uiHorSld ea4 1 200 1 [ entattr 4 $ea4 ] + ] "Scale" + ] "sound" [ + uiEntCell "1" [ + uiHorSld ea0 0 (- $nummapsounds 1) 1 [ entattr 0 $ea0 ] + ] "Sound ID#" + uiEntCell "2" [ + uiHorSld ea1 1 500 1 [ entattr 1 $ea1 ] + ] "Radius" + uiEntCell "3" [ + uiHorSld ea2 1 500 1 [ entattr 2 $ea2 ] + ] "Attenuation" + uiEntCell "4" [ + uiHorSld ea3 1 500 1 [ entattr 3 $ea3 ] + ] "Delay" + ] "playerstart" [ + uiEntCell "1" [ + uiHorSld ea0 0 360 5 [ entattr 0 $ea0 ] + ] "Orientation" + uiEntCell "2" [ // XXX needs work + uiButton "hold2 $c_diag1 d d" [ uiRadio [= $ea1 0] ; uitext "No Team" 0.6 ] 0 0 [ entattr 1 0 ] -1 + uiButton "hold2 $c_diag1 d d" [ uiRadio [= $ea1 1] ; uitext "Team ^f8Aesir" 0.6 ] 0 0 [ entattr 1 1 ] -1 + uiButton "hold2 $c_diag1 d d" [ uiRadio [= $ea1 2] ; uitext "Team ^f3Vanir" 0.6 ] 0 0 [ entattr 1 2 ] -1 + ] "Team" 0 ] cases $enttype "flag" [ uiEntCell "1" [ - uiHorSld ea0 0 360 5 [ entattr 0 $ea0 ] + uiHorSld ea0 0 360 5 [ entattr 0 $ea0 ] ] "Orientation" - uiEntCell "2" [ // XXX needs work - uiButton "hold2 $c_diag1 d d" [ uiRadio [= $ea1 1] ; uitext "Team ^f8Aesir" 0.6 ] 0 0 [ entattr 1 1 ] -1 - uiButton "hold2 $c_diag1 d d" [ uiRadio [= $ea1 2] ; uitext "Team ^f3Vanir" 0.6 ] 0 0 [ entattr 1 2 ] -1 + uiEntCell "2" [ // XXX needs work + uiButton "hold2 $c_diag1 d d" [ uiRadio [= $ea1 1] ; uitext "Team ^f8Aesir" 0.6 ] 0 0 [ entattr 1 1 ] -1 + uiButton "hold2 $c_diag1 d d" [ uiRadio [= $ea1 2] ; uitext "Team ^f3Vanir" 0.6 ] 0 0 [ entattr 1 2 ] -1 ] "Team" 0 ] "trigger" [ uiEntCell "1" [ uiHorSld ea0 0 49 1 [ entattr 0 $ea0 ] - ] "Trigger ID#" + ] "Trigger ID#" uiEntCell "2" [ uiHorSld ea1 -1 (- $nummapmodels 1) 1 [ entattr 1 $ea1 ] ] "Model ID#" @@ -257,115 +257,115 @@ uiLiteMenu "edithud" [ uiHorSld ea3 0 1 1 [ entattr 4 $ea1 ] ] "Active" ] - ] [ + ] [ - // uitext (+s $entget " :^f0 " $enthavesel " ^f7selected") 0.6 // XXX wheeeeere shall i put this... + // uitext (+s $entget " :^f0 " $enthavesel " ^f7selected") 0.6 // XXX wheeeeere shall i put this... - uivlist $uiPad:L [ - cond $hmapedit [ - uiHudCell 0.62 0.036 [ - uihlist $uiPad:L [ - uialign -1 - uiclamp.y - uihlist $uiPad:O3 [ - uiKeybox $uiPad:SXL $uiPad:4XL [ uitext "Ctrl" 0.5 ] - uiKeybox $uiPad:4XL $uiPad:4XL [ uitext "H" 0.5 ] - ] - uiBar 0 1 - uitext "Toggle Heightmapping Mode" 0.6 - ] - ] $uiPad:L 0 - uiHudCell 0.62 0.036 [ - uihlist $uiPad:L [ - uialign -1 - uiclamp.y - uihlist 0 [ - uiKeybox $uiPad:4XL $uiPad:4XL [ uitext "H" 0.5 ] - uiFastImg "" "ui/" "io/" "mouseM" 0.026 - ] - uiBar 0 1 - uiFastImg "" "ui/" "brush" "" 0.024 - uitext $hbrushname 0.6 - ] - ] $uiPad:L 0 - ] $blendpaintmode [ - uiHudCell 0.62 0.036 [ - uihlist $uiPad:L [ - uialign -1 - uiclamp.y - uihlist uiPad:O3 [ - uiKeybox $uiPad:SXL $uiPad:4XL [ uitext "Ctrl" 0.5 ] - uiKeybox $uiPad:4XL $uiPad:4XL [ uitext "B" 0.5 ] - ] - uiBar 0 1 - uitext "Toggle Blendpainting Mode" 0.6 - ] - ] $uiPad:L 0 - uihlist $uiPad:L [ - uiHudCell 0.28 0.036 [ - uihlist $uiPad:L [ - uialign -1 - uiclamp.y - uihlist 0 [ - uiKeybox $uiPad:4XL $uiPad:4XL [ uitext "B" 0.5 ] - uiFastImg "" "ui/" "io/" "mouseM" 0.026 - ] - uiBar 0 1 - uiFastImg "" "ui/" "color" "" 0.024 - uitext (at $blendpaintmodes $blendpaintmode) 0.6 - ] - ] $uiPad:L 0 - uiHudCell 0.33 0.036 [ - uihlist $uiPad:L [ - uialign -1 - uiclamp.y - uiFastImg "" "ui/" "io/" "mouseM" 0.026 - uiBar 0 1 - uiFastImg "" "ui/" "brush" "" 0.024 - uitext (getblendbrushname $curblendbrush) 0.6 - ] - ] $uiPad:L 0 - ] - ] [ - uiHudCell 0.62 0.082 [ - uivlist $uiPad:L [ - uialign -1 -1 - uitext (format "cube %1%2" $selchildcount (if $showmaterials [selchildmat ": "])) 0.5 - uigrid 2 $uiPad:3XL $uiPad:L [ - uihlist $uiPad:XL [ - uitext "WTR" 0.5 - uitext (format "%1k" $editstatwtr) 0.5 - uitext (format "(%1%%)" $editstatvtr) 0.5 - ] - uihlist $uiPad:XL [ - uitext "WVT" 0.5 - uitext (format "%1k" $editstatwvt) 0.5 - uitext (format "(%1%%)" $editstatvvt) 0.5 - ] - ] - uialign* -1 - ] - ] - ] - uiHudCell 0.62 0.036 [ - uihlist $uiPad:L [ - uialign -1 - uifill 0.036 0 [ - uitext (max 0 $getseltex) 0.6 - uialign- 1 - ] - uiTriangle 0 0 $uiPad:L $uiPad:L 270 - uitext (+s "^f0" (gettexname $getseltex)) 0.6 - ] - ] $uiPad:L 0 - ] - uiHudCell 0.128 0.128 [ - uivslotview (max 0 $getseltex) - uiclamp-e - ] - ] - uialign* -2 1 - ] + uivlist $uiPad:L [ + cond $hmapedit [ + uiHudCell 0.62 0.036 [ + uihlist $uiPad:L [ + uialign -1 + uiclamp.y + uihlist $uiPad:O3 [ + uiKeybox $uiPad:SXL $uiPad:4XL [ uitext "Ctrl" 0.5 ] + uiKeybox $uiPad:4XL $uiPad:4XL [ uitext "H" 0.5 ] + ] + uiBar 0 1 + uitext "Toggle Heightmapping Mode" 0.6 + ] + ] $uiPad:L 0 + uiHudCell 0.62 0.036 [ + uihlist $uiPad:L [ + uialign -1 + uiclamp.y + uihlist 0 [ + uiKeybox $uiPad:4XL $uiPad:4XL [ uitext "H" 0.5 ] + uiFastImg "" "ui/" "io/" "mouseM" 0.026 + ] + uiBar 0 1 + uiFastImg "" "ui/" "brush" "" 0.024 + uitext $hbrushname 0.6 + ] + ] $uiPad:L 0 + ] $blendpaintmode [ + uiHudCell 0.62 0.036 [ + uihlist $uiPad:L [ + uialign -1 + uiclamp.y + uihlist uiPad:O3 [ + uiKeybox $uiPad:SXL $uiPad:4XL [ uitext "Ctrl" 0.5 ] + uiKeybox $uiPad:4XL $uiPad:4XL [ uitext "B" 0.5 ] + ] + uiBar 0 1 + uitext "Toggle Blendpainting Mode" 0.6 + ] + ] $uiPad:L 0 + uihlist $uiPad:L [ + uiHudCell 0.28 0.036 [ + uihlist $uiPad:L [ + uialign -1 + uiclamp.y + uihlist 0 [ + uiKeybox $uiPad:4XL $uiPad:4XL [ uitext "B" 0.5 ] + uiFastImg "" "ui/" "io/" "mouseM" 0.026 + ] + uiBar 0 1 + uiFastImg "" "ui/" "color" "" 0.024 + uitext (at $blendpaintmodes $blendpaintmode) 0.6 + ] + ] $uiPad:L 0 + uiHudCell 0.33 0.036 [ + uihlist $uiPad:L [ + uialign -1 + uiclamp.y + uiFastImg "" "ui/" "io/" "mouseM" 0.026 + uiBar 0 1 + uiFastImg "" "ui/" "brush" "" 0.024 + uitext (getblendbrushname $curblendbrush) 0.6 + ] + ] $uiPad:L 0 + ] + ] [ + uiHudCell 0.62 0.082 [ + uivlist $uiPad:L [ + uialign -1 -1 + uitext (format "cube %1%2" $selchildcount (if $showmaterials [selchildmat ": "])) 0.5 + uigrid 2 $uiPad:3XL $uiPad:L [ + uihlist $uiPad:XL [ + uitext "WTR" 0.5 + uitext (format "%1k" $editstatwtr) 0.5 + uitext (format "(%1%%)" $editstatvtr) 0.5 + ] + uihlist $uiPad:XL [ + uitext "WVT" 0.5 + uitext (format "%1k" $editstatwvt) 0.5 + uitext (format "(%1%%)" $editstatvvt) 0.5 + ] + ] + uialign* -1 + ] + ] + ] + uiHudCell 0.62 0.036 [ + uihlist $uiPad:L [ + uialign -1 + uifill 0.036 0 [ + uitext (max 0 $getseltex) 0.6 + uialign- 1 + ] + uiTriangle 0 0 $uiPad:L $uiPad:L 270 + uitext (+s "^f0" (gettexname $getseltex)) 0.6 + ] + ] $uiPad:L 0 + ] + uiHudCell 0.128 0.128 [ + uivslotview (max 0 $getseltex) + uiclamp-e + ] + ] + uialign* -2 1 + ] ] diff --git a/config/ui/hud/gamehud.cfg b/config/ui/hud/gamehud.cfg index 37449b3cf..d08405dda 100644 --- a/config/ui/hud/gamehud.cfg +++ b/config/ui/hud/gamehud.cfg @@ -4,47 +4,47 @@ /////////////////////////////////////////////////////////////////////////////// newui "gamehud" [ - uiclamp.e - if (|| $mainmenu (! $showhud)) [ uiallowinput 0 ] [ + uiclamp.e + if (|| $mainmenu (! $showhud)) [ uiallowinput 0 ] [ ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // EDITHUD DISPLAY GROUP // ////////////////////////////////////////////////////////////////////////////////////////////////////////////// - if $editing [ - uieschide 0 - uispace $uiPad:M $uiPad:M [ - uihlist $uiPad:M [ - if (iskeyheld "LALT") [ - uiallowinput 1 - uiHudCell $uiPad:DM $uiPad:DXL [ - uiFastImg "" "ui/" "config" "" $uiPad:SXL - ] - ] [ - uiallowinput 0 - uiHudCell $uiPad:DM $uiPad:DXL [ - uivlist 0 [ - uiFastImg "" "ui/" "speed" "" $uiPad:6XL - uitext $floatspeed 0.6 - uifill 0 $uiPad:S - uigroup [ - uiFastImg "" "ui/" "grid" "" $uiPad:6XL - uitext $gridpower 0.5 - uialign- 1 1 - ] - ] - ] - ] - ] - ] ; uialign- -1 1 - - ] [ + if $editing [ + uieschide 0 + uispace $uiPad:M $uiPad:M [ + uihlist $uiPad:M [ + if (iskeyheld "LALT") [ + uiallowinput 1 + uiHudCell $uiPad:DM $uiPad:DXL [ + uiFastImg "" "ui/" "config" "" $uiPad:SXL + ] + ] [ + uiallowinput 0 + uiHudCell $uiPad:DM $uiPad:DXL [ + uivlist 0 [ + uiFastImg "" "ui/" "speed" "" $uiPad:6XL + uitext $floatspeed 0.6 + uifill 0 $uiPad:S + uigroup [ + uiFastImg "" "ui/" "grid" "" $uiPad:6XL + uitext $gridpower 0.5 + uialign- 1 1 + ] + ] + ] + ] + ] + ] ; uialign- -1 1 + + ] [ .gamehud ] - ] + ] ] [ - uiBitsHUD = 0 - showui "hud_" + uiBitsHUD = 0 + showui "hud_" ] .gamehud = [ @@ -125,15 +125,15 @@ vec2rotatey = [-f (*f (cos $arg3) $arg2) (*f (sin $arg3) $arg1)] // Y cos(@) - X radar_icon = [ local dirx diry dist r_dirx r_diry bx by if $arg6 [] [ arg6 = 0.014 ] - dirx = (*f (divf 1 $calcradarscale) (-f $getcamx $arg1)) - diry = (*f (divf 1 $calcradarscale) (-f $getcamy $arg2)) - dist = (sqrt (+f (*f $dirx $dirx) (*f $diry $diry))) + dirx = (*f (divf 1 $calcradarscale) (-f $getcamx $arg1)) + diry = (*f (divf 1 $calcradarscale) (-f $getcamy $arg2)) + dist = (sqrt (+f (*f $dirx $dirx) (*f $diry $diry))) if (>=f $dist 0.9) [ - dirx = (*f (divf 0.9 $dist) $dirx) - diry = (*f (divf 0.9 $dist) $diry) + dirx = (*f (divf 0.9 $dist) $dirx) + diry = (*f (divf 0.9 $dist) $diry) ] - r_dirx = (vec2rotatex $dirx $diry $getcamyaw) - r_diry = (vec2rotatey $dirx $diry $getcamyaw) + r_dirx = (vec2rotatex $dirx $diry $getcamyaw) + r_diry = (vec2rotatey $dirx $diry $getcamyaw) bx = (+f $arg7 (*f $MINIMAP_SIZE $r_dirx)) by = (+f $arg8 (*f $MINIMAP_SIZE $r_diry)) uispace -1 -1 [ uioffset $bx $by [ uiFastImgRotated $arg9 "hud/" $arg5 "" $arg4 $arg6 ] ] @@ -159,52 +159,52 @@ radar_icon = [ ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // MINIMAP DISPLAY GROUP // ////////////////////////////////////////////////////////////////////////////////////////////////////////////// - if (&& $showminimap [m_ctf $getmode]) [ - uispace $uiPad:L $uiPad:L [ - local cteam - - if (>= $getfollow 0) [ - cteam = (getclientteam $getfollow) - ] [ cteam = $getteam ] - - uiminimap $getcamx $getcamy $getcamyaw $MINIMAP_SIZE 16 // minimap background - uiFastImg "" "hud/" "radar" "" (+f $MINIMAP_SIZE 0.005) // minimap border - - // flag bases - loop i $numflags [ - do [ radar_icon @(flagbase $i) 0 (+s "blip_" (? (= (flagteam $i) 1) "blue" "red")) 0.01 ] - ] - - // players - looplist cn (listclients 1 1) [ - if (&& $radarteammates [! (isspectator $cn)] [= $cteam (getclientteam $cn)] [!=s $cn (? $isspectator $getfollow $getclientnum)] [! (getclientflag $cn)]) [ - if (isdead $cn) [ - do [ radar_icon @(getclientpos $cn) 0 (+s "blip_" (? (= (getclientteam $cn) 1) "blue" "red") "_dead") ] - ] [ - do [ radar_icon @(getclientpos $cn) (- $getcamyaw (getclientyaw $cn)) (+s "blip_" (? (= (getclientteam $cn) 1) "blue" "red") "_alive") ] - ] - ] - ] - - // flags - loop i $numflags [ - case (flagstate $i) 0 [ - // flag is at base - do [ radar_icon @(flagbase $i) 0 (+s "blip_" (? (= (flagteam $i) 1) "blue" "red") "_flag") 0.02 0.017 -0.017 ] - ] 1 [ - // a player has the flag - if (< (mod $getmillis 1000) 500) [ - do [ radar_icon @(getclientpos (flagowner $i)) 0 (+s "blip_" (? (= (flagteam $i) 1) "blue" "red") "_flag") 0.02 0.017 -0.017 ] - ] - ] 2 [ - // flag is dropped somewhere - if (< (mod $getmillis 300) 150) [ - do [ radar_icon @(flagdroploc $i) 0 (+s "blip_" (? (= (flagteam $i) 1) "blue" "red") "_flag") 0.02 0.017 -0.017 ] - ] - ] // 3 - flag fell under the map, don't show icon - ] - ] ; uialign- 1 -1 - ] + if (&& $showminimap [m_ctf $getmode]) [ + uispace $uiPad:L $uiPad:L [ + local cteam + + if (>= $getfollow 0) [ + cteam = (getclientteam $getfollow) + ] [ cteam = $getteam ] + + uiminimap $getcamx $getcamy $getcamyaw $MINIMAP_SIZE 16 // minimap background + uiFastImg "" "hud/" "radar" "" (+f $MINIMAP_SIZE 0.005) // minimap border + + // flag bases + loop i $numflags [ + do [ radar_icon @(flagbase $i) 0 (+s "blip_" (? (= (flagteam $i) 1) "blue" "red")) 0.01 ] + ] + + // players + looplist cn (listclients 1 1) [ + if (&& $radarteammates [! (isspectator $cn)] [= $cteam (getclientteam $cn)] [!=s $cn (? $isspectator $getfollow $getclientnum)] [! (getclientflag $cn)]) [ + if (isdead $cn) [ + do [ radar_icon @(getclientpos $cn) 0 (+s "blip_" (? (= (getclientteam $cn) 1) "blue" "red") "_dead") ] + ] [ + do [ radar_icon @(getclientpos $cn) (- $getcamyaw (getclientyaw $cn)) (+s "blip_" (? (= (getclientteam $cn) 1) "blue" "red") "_alive") ] + ] + ] + ] + + // flags + loop i $numflags [ + case (flagstate $i) 0 [ + // flag is at base + do [ radar_icon @(flagbase $i) 0 (+s "blip_" (? (= (flagteam $i) 1) "blue" "red") "_flag") 0.02 0.017 -0.017 ] + ] 1 [ + // a player has the flag + if (< (mod $getmillis 1000) 500) [ + do [ radar_icon @(getclientpos (flagowner $i)) 0 (+s "blip_" (? (= (flagteam $i) 1) "blue" "red") "_flag") 0.02 0.017 -0.017 ] + ] + ] 2 [ + // flag is dropped somewhere + if (< (mod $getmillis 300) 150) [ + do [ radar_icon @(flagdroploc $i) 0 (+s "blip_" (? (= (flagteam $i) 1) "blue" "red") "_flag") 0.02 0.017 -0.017 ] + ] + ] // 3 - flag fell under the map, don't show icon + ] + ] ; uialign- 1 -1 + ] x = (maxf 0 (-f $uiaspect $uiWideHUD)) if (>f $x 0) [ x = (*f $x 0.5) ] @@ -216,9 +216,9 @@ radar_icon = [ uispace $uiPad:L $uiPad:L [ uivlist 0 [ local i timestamp - if (&& $showminimap [m_ctf $getmode]) [ - uifill 0 (+f $MINIMAP_SIZE $uiPad:L) - ] + if (&& $showminimap [m_ctf $getmode]) [ + uifill 0 (+f $MINIMAP_SIZE $uiPad:L) + ] looplistrev n $fragQueue [ case $i 0 [ timestamp = $n ] 1 [ do [ .killfeed.row @n ] @@ -248,7 +248,7 @@ radar_icon = [ uivlist 0 [ if (getclientshield $cn) [ .bar.shield $uiPad:D5XL (getclientshield $cn) 200 shield - uifill 0 $uiPad:XL + uifill 0 $uiPad:XL ] .bar.health $uiPad:D6XL (getclienthealth $cn) 100 health ] ; uialign- 0 1 @@ -310,121 +310,121 @@ radar_icon = [ uiFancyText "wide" (getclientammo $cn) 2 uialign- 1 1 ] ; uialign- 1 1 - ] + ] ] // temporary measure to set Y pos of chat input newui "hud_" [ - uiallowinput 0 - uiabovehud - uialign -1 1 - uifill 0 0.19 + uiallowinput 0 + uiabovehud + uialign -1 1 + uifill 0 0.19 ] // XXX MOVE TO A DIFFERENT FILE LATER // linear:HSV INT1 INT2 X-TIME WAVELENGTH linear:HSV = [ - local r1 g1 b1 r2 g2 b2 - INT>HSV $arg1 [ h1 = $h ; s1 = $s ; v1 = $v ] - INT>HSV $arg2 [ h2 = $h ; s2 = $s ; v2 = $v ] - HSV>RGB ( - +f $h1 (*f (/f (-f $h2 $h1) $arg4) $arg3) ) ( - +f $s1 (*f (/f (-f $s2 $s1) $arg4) $arg3) ) ( - +f $v1 (*f (/f (-f $v2 $v1) $arg4) $arg3) - ) [ RGB>INT $r $g $b ] + local r1 g1 b1 r2 g2 b2 + INT>HSV $arg1 [ h1 = $h ; s1 = $s ; v1 = $v ] + INT>HSV $arg2 [ h2 = $h ; s2 = $s ; v2 = $v ] + HSV>RGB ( + +f $h1 (*f (/f (-f $h2 $h1) $arg4) $arg3) ) ( + +f $s1 (*f (/f (-f $s2 $s1) $arg4) $arg3) ) ( + +f $v1 (*f (/f (-f $v2 $v1) $arg4) $arg3) + ) [ RGB>INT $r $g $b ] ] .bar.health = [ - local y ; y = (*f $arg1 0.1) - if (> $arg2 $arg3) [ - arg5 = 0x80F0FF ; arg6 = 0xD0F0FF // supercharged - ] [ push n (%f $arg2 $arg3) [ // normal health - if ( $arg2 $arg3) [ + arg5 = 0x80F0FF ; arg6 = 0xD0F0FF // supercharged + ] [ push n (%f $arg2 $arg3) [ // normal health + if ( (ceil (/f $over 50)) $i) [ - i = (sinBounce $uiPad:XL (* $i 100) 400) - uioffset 0 (+f (minf 0 $i) $uiPad:S) [ - uiShadowedImg (mad $arg6) "hud/" "shield2" "" $y - ] - ] - ] ] - ] ; uiclamp-y ; uialign- 1 - ] - ] + arg6 = (linear:HSV 0xF01000 0x00F080 $n 1) + ] ] + .bar:toparea $arg1 $arg2 $arg3 $arg4 $arg5 $arg6 [ + pushif over (max 0 (- $arg2 $arg3)) [ + uihlist $uiPad:2XL- [ + push y (*f $y 0.75) [ loop i 3 [ + if (> (ceil (/f $over 50)) $i) [ + i = (sinBounce $uiPad:XL (* $i 100) 400) + uioffset 0 (+f (minf 0 $i) $uiPad:S) [ + uiShadowedImg (mad $arg6) "hud/" "shield2" "" $y + ] + ] + ] ] + ] ; uiclamp-y ; uialign- 1 + ] + ] ] .bar.shield = [ - local y ; y = (*f $arg1 0.1) - .bar:toparea $arg1 $arg2 $arg3 $arg4 0xE06000 0xFFD000 + local y ; y = (*f $arg1 0.1) + .bar:toparea $arg1 $arg2 $arg3 $arg4 0xE06000 0xFFD000 ] .bar.powerup = [ - local y ; y = (*f $arg1 0.1) - .bar:resource $arg1 $arg2 $arg3 0x80D0FF 0xD0F0FF + local y ; y = (*f $arg1 0.1) + .bar:resource $arg1 $arg2 $arg3 0x80D0FF 0xD0F0FF ] // use: 1:WIDTH 2:VALUE 3:MAXVAL 4:IMG/ID 5:COL1 6:COL2 7:[CHILDREN] .bar:toparea = [ - uivlist 0 [ - uigroup [ - do $arg7 - uihlist $uiPad:S [ - nodebug [ - if (!= $arg2 $[uiResource:@arg4.val]) [ - [uiResource:@arg4.mul] = 0.95 - [uiResource:@arg4.ts] = $getmillis - [uiResource:@arg4.val] = $arg2 - ] - if (> $getmillis (+ $[uiResource:@arg4.ts] 120)) [ - [uiResource:@arg4.mul] = 0.8 - ] - ] - uifill $y $y [ push y (*f $y $[uiResource:@arg4.mul]) [ - uiShadowedImg (intmul $arg6) "hud/" $arg4 1 $y - uiShadowedImg "" "hud/" $arg4 2 $y - ] ] - uiclip 0 $y [ uioffset 0 (*f $y -0.15) [ - uiFancyText "wide" $arg2 (*f $arg1 2.875) $arg6 - ] ] - ] ; uialign- -1 - ] ; uiclamp-x - .bar:resource $arg1 $arg2 $arg3 $arg5 $arg6 - ] ; uialign- 1 + uivlist 0 [ + uigroup [ + do $arg7 + uihlist $uiPad:S [ + nodebug [ + if (!= $arg2 $[uiResource:@arg4.val]) [ + [uiResource:@arg4.mul] = 0.95 + [uiResource:@arg4.ts] = $getmillis + [uiResource:@arg4.val] = $arg2 + ] + if (> $getmillis (+ $[uiResource:@arg4.ts] 120)) [ + [uiResource:@arg4.mul] = 0.8 + ] + ] + uifill $y $y [ push y (*f $y $[uiResource:@arg4.mul]) [ + uiShadowedImg (intmul $arg6) "hud/" $arg4 1 $y + uiShadowedImg "" "hud/" $arg4 2 $y + ] ] + uiclip 0 $y [ uioffset 0 (*f $y -0.15) [ + uiFancyText "wide" $arg2 (*f $arg1 2.875) $arg6 + ] ] + ] ; uialign- -1 + ] ; uiclamp-x + .bar:resource $arg1 $arg2 $arg3 $arg5 $arg6 + ] ; uialign- 1 ] // use: 1:WIDTH 2:VALUE 3:MAXVAL 4:COL1 5:COL2 .bar:resource = [ - uigroup [ - local w.lt w.rt - if $arg2 [ - w.lt = (%f (min $arg2 $arg3) $arg3 $arg1) - uihgradient (|A 0xC0 $arg4) (|A 0xE0 $arg5) $w.lt $y [ - uiDrawSpriteTiled "hud" "" "data/interface/debug/causticmap" $uiPad:DM $w.lt $y 128 0 50 $uiSpriteAnim - uiFastImgStretched "" "shadow3" "" "" [ uiclamp.e ] (minf $w.lt $uiPad:S) - ] ; uialign- -1 - ] - if (< $arg2 $arg3) [ - w.rt = (-f $arg1 $w.lt) - uicolor (|A 0x30 $arg4) $w.rt $y [ - uiFastImgTiled "" "ui/" "ui_bg2" "" [ uiclamp.e ] - uiFastImgStretched "" "shadow3" "" "" [ uiclamp.e ] (minf $w.rt $uiPad:S) - ] ; uialign- 1 - ] - uioutline (INT:TRANS $arg4 % 400) $arg1 $y - ] + uigroup [ + local w.lt w.rt + if $arg2 [ + w.lt = (%f (min $arg2 $arg3) $arg3 $arg1) + uihgradient (|A 0xC0 $arg4) (|A 0xE0 $arg5) $w.lt $y [ + uiDrawSpriteTiled "hud" "" "data/interface/debug/causticmap" $uiPad:DM $w.lt $y 128 0 50 $uiSpriteAnim + uiFastImgStretched "" "shadow3" "" "" [ uiclamp.e ] (minf $w.lt $uiPad:S) + ] ; uialign- -1 + ] + if (< $arg2 $arg3) [ + w.rt = (-f $arg1 $w.lt) + uicolor (|A 0x30 $arg4) $w.rt $y [ + uiFastImgTiled "" "ui/" "ui_bg2" "" [ uiclamp.e ] + uiFastImgStretched "" "shadow3" "" "" [ uiclamp.e ] (minf $w.rt $uiPad:S) + ] ; uialign- 1 + ] + uioutline (INT:TRANS $arg4 % 400) $arg1 $y + ] ] @@ -437,78 +437,78 @@ linear:HSV = [ // 1: 2: 3: 4: 5: 6: 7: 8: // actor.name, target.name, weapon, crit, actor.cn, target.cn, actor.color, target.color .killfeed.row = [ - local msElapsed animIntro animOutro animProgress weaponIcon - msElapsed = (- $getmillis $timestamp) - - if (< $msElapsed $uiKillDecay) [ - msElapsed = (%f $msElapsed $uiKillDecay) - animIntro = (%f $uiKillFade $uiKillDecay) - animOutro = (-f 1 $animIntro) - - cond [f $msElapsed $animOutro] [ - animProgress = (-f 1 (%f (-f $msElapsed $animOutro) $animIntro)) - ] [ animProgress = 1 ] - - if ( "hud/" "row" "" [ - uialign 1 -2 - uispace $uiPad:M 0 [ - uifill 0 $uiPad:5XL - uihlist 0 [ - case $arg3 -4 [ - uiFancyText "" $arg2 0.65 (INT:TRANS $arg8 * 2) - uiFancyText "" " was torn apart" 0.65 - uifill $uiPad:XL - uiShadowedImg "" "hud/" "monster" "" $uiPad:3XL - ] -3 [ - uiFancyText "" $arg2 0.65 (INT:TRANS $arg8 * 2) - uiFancyText "" " was assassinated" 0.65 - uifill $uiPad:XL - uiShadowedImg "" "hud/" "death" "" $uiPad:3XL - ] -2 [ - if (isai $arg5) [ - uiShadowedImg "" "hud/" "bot" "" $uiPad:3XL - uifill $uiPad:O3 - ] - uiFancyText "" $arg1 0.65 (INT:TRANS $arg7 * 2) - uiFancyText "" " committed seppuku" 0.65 - uifill $uiPad:XL - uiShadowedImg "" "hud/" "death" "" $uiPad:3XL - ] () [ - weaponIcon = (at [ - "melee" "scattergun" "smg" "pulse" "rocket" - "railgun" "grenade" "pistol" "railgun" "zombie" - ] (+ $arg3 1)) - - if (isai $arg5) [ - uiShadowedImg "" "hud/" "bot" "" $uiPad:3XL - uifill $uiPad:O3 - ] - uiFancyText "" $arg1 0.65 (INT:TRANS $arg7 * 2) - uifill $uiPad:XL - uiShadowedImg "" "hud/" $weaponIcon "_raw" (*f (gettexaspectwh ( - +s "data/interface/hud/" $weaponIcon "_raw.png" - )) $uiPad:3XL) $uiPad:3XL - if $arg4 [ - uifill $uiPad:S - uiShadowedImg "" "hud/" "crit" "" $uiPad:3XL - ] - uifill $uiPad:XL - if (isai $arg6) [ - uiShadowedImg "" "hud/" "bot" "" $uiPad:3XL - uifill $uiPad:O3 - ] - uiFancyText "" $arg2 0.65 (INT:TRANS $arg8 * 2) - ] - ] - ] - ] $uiPad:5XL - uifill 0 $uiPad:L - ] [ uifill 0 (*f $animProgress (+f $uiPad:5XL $uiPad:L)) ] - ] + local msElapsed animIntro animOutro animProgress weaponIcon + msElapsed = (- $getmillis $timestamp) + + if (< $msElapsed $uiKillDecay) [ + msElapsed = (%f $msElapsed $uiKillDecay) + animIntro = (%f $uiKillFade $uiKillDecay) + animOutro = (-f 1 $animIntro) + + cond [f $msElapsed $animOutro] [ + animProgress = (-f 1 (%f (-f $msElapsed $animOutro) $animIntro)) + ] [ animProgress = 1 ] + + if ( "hud/" "row" "" [ + uialign 1 -2 + uispace $uiPad:M 0 [ + uifill 0 $uiPad:5XL + uihlist 0 [ + case $arg3 -4 [ + uiFancyText "" $arg2 0.65 (INT:TRANS $arg8 * 2) + uiFancyText "" " was torn apart" 0.65 + uifill $uiPad:XL + uiShadowedImg "" "hud/" "monster" "" $uiPad:3XL + ] -3 [ + uiFancyText "" $arg2 0.65 (INT:TRANS $arg8 * 2) + uiFancyText "" " was assassinated" 0.65 + uifill $uiPad:XL + uiShadowedImg "" "hud/" "death" "" $uiPad:3XL + ] -2 [ + if (isai $arg5) [ + uiShadowedImg "" "hud/" "bot" "" $uiPad:3XL + uifill $uiPad:O3 + ] + uiFancyText "" $arg1 0.65 (INT:TRANS $arg7 * 2) + uiFancyText "" " committed seppuku" 0.65 + uifill $uiPad:XL + uiShadowedImg "" "hud/" "death" "" $uiPad:3XL + ] () [ + weaponIcon = (at [ + "melee" "scattergun" "smg" "pulse" "rocket" + "railgun" "grenade" "pistol" "railgun" "zombie" + ] (+ $arg3 1)) + + if (isai $arg5) [ + uiShadowedImg "" "hud/" "bot" "" $uiPad:3XL + uifill $uiPad:O3 + ] + uiFancyText "" $arg1 0.65 (INT:TRANS $arg7 * 2) + uifill $uiPad:XL + uiShadowedImg "" "hud/" $weaponIcon "_raw" (*f (gettexaspectwh ( + +s "data/interface/hud/" $weaponIcon "_raw.png" + )) $uiPad:3XL) $uiPad:3XL + if $arg4 [ + uifill $uiPad:S + uiShadowedImg "" "hud/" "crit" "" $uiPad:3XL + ] + uifill $uiPad:XL + if (isai $arg6) [ + uiShadowedImg "" "hud/" "bot" "" $uiPad:3XL + uifill $uiPad:O3 + ] + uiFancyText "" $arg2 0.65 (INT:TRANS $arg8 * 2) + ] + ] + ] + ] $uiPad:5XL + uifill 0 $uiPad:L + ] [ uifill 0 (*f $animProgress (+f $uiPad:5XL $uiPad:L)) ] + ] ] /////////////////////////////////////////////////////////////////////////////// @@ -516,257 +516,257 @@ linear:HSV = [ /////////////////////////////////////////////////////////////////////////////// newui "scoreboard" [ - uiallowinput 0 - refreshscoreboard + uiallowinput 0 + refreshscoreboard ] [ - uiSetBgBlur 1 - .sb_offset = 3 + uiSetBgBlur 1 + .sb_offset = 3 ] [ uiSetBgBlur -1 ] .scoreboard.update = [ - local bit cc - - if $getmode [ bit = (^ $bit 0x01) ; ++ cc ] // score - if $showkills [ bit = (^ $bit 0x02) ; ++ cc ] // kills - if $showdeaths [ bit = (^ $bit 0x04) ; ++ cc ] // deaths - if $scoreboardmultiplayer [ - if $showpj [ bit = (^ $bit 0x08) ; ++ cc ] // packet jump - if $showping [ bit = (^ $bit 0x10) ; ++ cc ] // ping - ] - if $showclientnum [ bit = (^ $bit 0x20) ; ++ cc ] // client num - - if (|| $arg1 [!= (getalias .sb_bitmask) $bit] [!= (getalias .sb_modemask) $bit]) [ - .sb_bitmask = $bit - .sb_modemask = $getmode - - local .sb_namewidth .sb_cellwidth .sb_rowwidth - local .scoreboard.cells .scoreboard.icons - local .scoreboard.header .scoreboard.row - local .scoreboard.shelf.top3 .scoreboard.shelf.rest - - .sb_namewidth = $uiPad:UXS - .sb_cellwidth = (+f $uiPad:DSS $uiPad:M) - .sb_rowwidth = (+f $.sb_namewidth (*f $.sb_cellwidth $cc)) - - loop n 6 [ if (& $bit (<< 1 $n)) [ - append .scoreboard.cells [[uifill @@[.sb_cellwidth] 0 [@@@(at [ - [uiFancyText "" (min (getclientscore $cn) 9999) 0.58] - [uiFancyText "" (clamp (getclientfrags $cn) -999 9999) 0.58] - [uiFancyText "" (min (getclientdeaths $cn) 9999) 0.58] - [if (scoreboardpj $cn) [uiFancyText "" (min (scoreboardpj $cn) 9999) 0.58] [uiFancyText "" "--" 0.58 $c_gray]] - [if (scoreboardping $cn) [uiFancyText "" (min (scoreboardping $cn) 9999) 0.58] [uiFancyText "" "--" 0.58 $c_gray]] - [uiFancyText "" $cn 0.58 (at [@@@(concat $c_white $c_green $c_orange $c_magenta)] (getclientprivilege $cn))] - ] $n)]]] - append .scoreboard.icons (at [ - @@@(at "0 star star flag star crown crown star" $getmode) - "frag" "death" "pj" "ping" "cn" - ] $n) - ] ] - - // 1:TEAM - .scoreboard.header = [ - local bflip - bflip = (= $arg1 2) // right-to-left flip bit - result [ - uifill @[.sb_rowwidth] $uiPad:DM [ - uispace 0 $uiPad:S [ - uiFastImg "hud/" "scorebg" - uiclamp-e - ] ; uiclamp-e - uispace $uiPad:M $uiPad:M- [ - uiFancyText "wide" @@@( - at "FFA Aesir Vanir" $arg1 - ) 1.25 @@@(INT:TRANS ( - at [0x40D060 0x4060D0 0xD04040] $arg1 - ) % 80) - ] ; uialign- @@(? $bflip 1 -1) -1 - uispace $uiPad:O5 (*f $uiPad:O5 2.9) [ - uiFancyText "wide" (getteamscore @@@arg1) 0.85 @@@( - at [0x40D060 0x4060D0 0xD04040] $arg1 - ) - ] ; uialign- @@(? $bflip -1 1) -1 - if (numscoreboard @@arg1) [ - uihlist 0 [ - @@@@(? $bflip [ - uialign -1 1 - @(looplistconcatrev x $.scoreboard.icons [ result [ - uifill @@[.sb_cellwidth] @@(*f $uiPad:O4 7) [ - uiShadowedImg "" "hud/" @@x "" $uiPad:3XL - ] - ] ]) - ] [ - uialign 1 1 - @(looplistconcat x $.scoreboard.icons [ result [ - uifill @@[.sb_cellwidth] @@(*f $uiPad:O4 7) [ - uiShadowedImg "" "hud/" @@x "" $uiPad:3XL - ] - ] ]) - ]) - ] - ] - ] - ] - ] - - // 1:TEAM 2:[CHILDREN] 3:SIDE-EXTRAS - .scoreboard.row = [ - local bflip - bflip = (= $arg1 2) // right-to-left flip bit - result [ - uiFastImgStretched @(? (> $arg1 -1) [( - intmul (at [ - [0x303030 0x282828] // ffa - [0x1C2E70 0x122466] // aesir - [0x701C1C 0x661212] // vanir - ] @arg1 (& $n 1)) - )] [ @(intmul 0x303840) ]) "hud/" "row" "" [ - uifill @@(? $arg3 $arg3 $.sb_rowwidth) - @@(? $arg2 [ uigroup [ uiclamp.e ; @@arg2 ] ]) - if (scoreboardhighlight $cn) [ - uispace $uiPad:O3 $uiPad:O3 [ - uiShadowedImg "hud/" "edge" "" $uiPad:M - ] ; uialign- @@@(? $bflip 1 -1) -1 - ] - @@(? $bflip [ - uihlist 0 [ - @@(looplistconcatrev x $.scoreboard.cells [ result [ @x; ] ]) + local bit cc + + if $getmode [ bit = (^ $bit 0x01) ; ++ cc ] // score + if $showkills [ bit = (^ $bit 0x02) ; ++ cc ] // kills + if $showdeaths [ bit = (^ $bit 0x04) ; ++ cc ] // deaths + if $scoreboardmultiplayer [ + if $showpj [ bit = (^ $bit 0x08) ; ++ cc ] // packet jump + if $showping [ bit = (^ $bit 0x10) ; ++ cc ] // ping + ] + if $showclientnum [ bit = (^ $bit 0x20) ; ++ cc ] // client num + + if (|| $arg1 [!= (getalias .sb_bitmask) $bit] [!= (getalias .sb_modemask) $bit]) [ + .sb_bitmask = $bit + .sb_modemask = $getmode + + local .sb_namewidth .sb_cellwidth .sb_rowwidth + local .scoreboard.cells .scoreboard.icons + local .scoreboard.header .scoreboard.row + local .scoreboard.shelf.top3 .scoreboard.shelf.rest + + .sb_namewidth = $uiPad:UXS + .sb_cellwidth = (+f $uiPad:DSS $uiPad:M) + .sb_rowwidth = (+f $.sb_namewidth (*f $.sb_cellwidth $cc)) + + loop n 6 [ if (& $bit (<< 1 $n)) [ + append .scoreboard.cells [[uifill @@[.sb_cellwidth] 0 [@@@(at [ + [uiFancyText "" (min (getclientscore $cn) 9999) 0.58] + [uiFancyText "" (clamp (getclientfrags $cn) -999 9999) 0.58] + [uiFancyText "" (min (getclientdeaths $cn) 9999) 0.58] + [if (scoreboardpj $cn) [uiFancyText "" (min (scoreboardpj $cn) 9999) 0.58] [uiFancyText "" "--" 0.58 $c_gray]] + [if (scoreboardping $cn) [uiFancyText "" (min (scoreboardping $cn) 9999) 0.58] [uiFancyText "" "--" 0.58 $c_gray]] + [uiFancyText "" $cn 0.58 (at [@@@(concat $c_white $c_green $c_orange $c_magenta)] (getclientprivilege $cn))] + ] $n)]]] + append .scoreboard.icons (at [ + @@@(at "0 star star flag star crown crown star" $getmode) + "frag" "death" "pj" "ping" "cn" + ] $n) + ] ] + + // 1:TEAM + .scoreboard.header = [ + local bflip + bflip = (= $arg1 2) // right-to-left flip bit + result [ + uifill @[.sb_rowwidth] $uiPad:DM [ + uispace 0 $uiPad:S [ + uiFastImg "hud/" "scorebg" + uiclamp-e + ] ; uiclamp-e + uispace $uiPad:M $uiPad:M- [ + uiFancyText "wide" @@@( + at "FFA Aesir Vanir" $arg1 + ) 1.25 @@@(INT:TRANS ( + at [0x40D060 0x4060D0 0xD04040] $arg1 + ) % 80) + ] ; uialign- @@(? $bflip 1 -1) -1 + uispace $uiPad:O5 (*f $uiPad:O5 2.9) [ + uiFancyText "wide" (getteamscore @@@arg1) 0.85 @@@( + at [0x40D060 0x4060D0 0xD04040] $arg1 + ) + ] ; uialign- @@(? $bflip -1 1) -1 + if (numscoreboard @@arg1) [ + uihlist 0 [ + @@@@(? $bflip [ + uialign -1 1 + @(looplistconcatrev x $.scoreboard.icons [ result [ + uifill @@[.sb_cellwidth] @@(*f $uiPad:O4 7) [ + uiShadowedImg "" "hud/" @@x "" $uiPad:3XL + ] + ] ]) + ] [ + uialign 1 1 + @(looplistconcat x $.scoreboard.icons [ result [ + uifill @@[.sb_cellwidth] @@(*f $uiPad:O4 7) [ + uiShadowedImg "" "hud/" @@x "" $uiPad:3XL + ] + ] ]) + ]) + ] + ] + ] + ] + ] + + // 1:TEAM 2:[CHILDREN] 3:SIDE-EXTRAS + .scoreboard.row = [ + local bflip + bflip = (= $arg1 2) // right-to-left flip bit + result [ + uiFastImgStretched @(? (> $arg1 -1) [( + intmul (at [ + [0x303030 0x282828] // ffa + [0x1C2E70 0x122466] // aesir + [0x701C1C 0x661212] // vanir + ] @arg1 (& $n 1)) + )] [ @(intmul 0x303840) ]) "hud/" "row" "" [ + uifill @@(? $arg3 $arg3 $.sb_rowwidth) + @@(? $arg2 [ uigroup [ uiclamp.e ; @@arg2 ] ]) + if (scoreboardhighlight $cn) [ + uispace $uiPad:O3 $uiPad:O3 [ + uiShadowedImg "hud/" "edge" "" $uiPad:M + ] ; uialign- @@@(? $bflip 1 -1) -1 + ] + @@(? $bflip [ + uihlist 0 [ + @@(looplistconcatrev x $.scoreboard.cells [ result [ @x; ] ]) if (isai $cn) [ uiFastImg "" "hud/" "bot" "" $uiPad:3XL ] [ uiFastImg "" "countryflag/" (strlower (getclientcountrycode $cn)) "" (*f 1.5 $uiPad:4XL) $uiPad:4XL ] - ] ; uiclamp-y ; uialign- -1 - uispace $uiPad:M 0 [ uihlist $uiPad:O3 [ - uiFancyText "" (getclientname $cn) 0.6 (scoreboardstatus $cn) - ] ] ; uialign- 1 - ] [ - uispace $uiPad:M 0 [ uihlist $uiPad:O3 [ - uiFancyText "" (getclientname $cn) 0.6 (scoreboardstatus $cn) - ] ] ; uialign- -1 - @(? (> $arg1 -1) [ - uihlist 0 [ + ] ; uiclamp-y ; uialign- -1 + uispace $uiPad:M 0 [ uihlist $uiPad:O3 [ + uiFancyText "" (getclientname $cn) 0.6 (scoreboardstatus $cn) + ] ] ; uialign- 1 + ] [ + uispace $uiPad:M 0 [ uihlist $uiPad:O3 [ + uiFancyText "" (getclientname $cn) 0.6 (scoreboardstatus $cn) + ] ] ; uialign- -1 + @(? (> $arg1 -1) [ + uihlist 0 [ if (isai $cn) [ uiFastImg "" "hud/" "bot" "" $uiPad:3XL ] [ uiFastImg "" "countryflag/" (strlower (getclientcountrycode $cn)) "" (*f 1.5 $uiPad:4XL) $uiPad:4XL ] - @@(looplistconcat x $.scoreboard.cells [ result [ @x; ] ]) - ] ; uiclamp-y ; uialign- 1 - ] [ - @(? $showclientnum [ - uihlist 0 [ - @@(at $.scoreboard.cells -1) - @(? (!=s "" (getclientcountrycode $cn)) [ - uiFastImg "" "countryflags/" (strlower (getclientcountrycode $cn)) "" (*f 1.5 $uiPad:4XL) $uiPad:4XL - uispace $uiPad:O2 0 [] - ]) - ] - ] [ - uihlist 0 [ - uiFastImg "" "countryflag/" (strlower (getclientcountrycode $cn)) "" (*f 1.5 $uiPad:4XL) $uiPad:4XL - uispace $uiPad:O2 0 [] - ] - ]) - uiclamp-y ; uialign- 1 - ]) - ]) - ] $uiPad:5XL - ++ n - ] - ] - - if (m_teammode $getmode) [ - .scoreboard.aesir = [ - local n - uivlist 0 [ - @@(.scoreboard.header 1) - loopscoreboard cn 1 [ @@@(.scoreboard.row 1) ] - ] ; uialign- -2 -1 - ] - .scoreboard.vanir = [ - local n - uivlist 0 [ - @@(.scoreboard.header 2) - loopscoreboard cn 2 [ @@@(.scoreboard.row 2) ] - ] ; uialign- -2 -1 - ] - .scoreboard.ctf = [ - uigroup [ - uioffset 0 $uiPad:O5 [ - uioffset $uiPad:2XL- $uiPad:2XL- [ uiFancyText "wide" "V" 1 ] - uioffset $uiPad:2XL $uiPad:2XL [ uiFancyText "wide" "S" 1 ] - ] ; uialign- 0 -1 - uihlist $uiPad:DM [ - .scoreboard.aesir - .scoreboard.vanir - uialign* -2 -1 - ] - ] - ] - ] - - .scoreboard.spec = [ - uigrid 3 $uiPad:L 0 [ - loopscoreboard cn -1 [ - @@@(.scoreboard.row -1 [] (+f $.sb_namewidth ( - ? $showclientnum $.sb_cellwidth 0 - ))) - ] - ] - ] - - .scoreboard.shelf.top3 = [ - uioffset $uiPad:5XL- 0 [ - uiFastImg (fade 0.5) "hud/" "shelf" "" $uiPad:5XL - push n (at [ ] $n) [ - uiFastImg $n "hud/" "glow" "" $uiPad:5XL - uiFastImg $n "hud/" "crown" "" $uiPad:3XL - ] - ] ; uialign- -1 - ] - .scoreboard.shelf.rest = [ - arg1 = (= $cn $getclientnum) - uioffset $uiPad:5XL- 0 [ - uiFastImg (fade (? $arg1 0.5 0.25)) "hud/" "shelf" "" $uiPad:5XL - uiFastImg "hud/" "glow" "" $uiPad:5XL - push n (+ $n 1) [ - uifontoutline 1 0x7F [ - uifontcolortext "default" $n (|A! (? $arg1 0xB0 0x60)) 0.55 - ] - ] - ] ; uialign- -1 - ] - - .scoreboard.ffa = [ - local n x - uigroup [ - uivlist 0 [ - @@@(.scoreboard.header 0) - loopscoreboard cn 0 [ - if (< $n 3) [ - if (= $cn $getclientnum) [ x = 1 ] - @@@@@(.scoreboard.row 0 $.scoreboard.shelf.top3) - if (= $n 3) [ uifill 0 $uiPad:M ] - ] [ - if (= $cn $getclientnum) [ - if (> $n (+ $.sb_offset 10)) [ uifill 0 $uiPad:M ] - @@@@@@(.scoreboard.row 0 $.scoreboard.shelf.rest) - if (< $n (+ $.sb_offset 0)) [ uifill 0 $uiPad:M ] - ] [ - if (&& [<= $n (+ $.sb_offset 10 $x $isspectator)] [>= $n $.sb_offset]) [ - @@@@@@@(.scoreboard.row 0 $.scoreboard.shelf.rest) - ] [ ++ n ] - ] - ] - ] - ] - uioffset $uiPad:2XL 0 [ - uialign 1 - // XXX need a way to scan for flag carrier - ] - ] - ] - ] + @@(looplistconcat x $.scoreboard.cells [ result [ @x; ] ]) + ] ; uiclamp-y ; uialign- 1 + ] [ + @(? $showclientnum [ + uihlist 0 [ + @@(at $.scoreboard.cells -1) + @(? (!=s "" (getclientcountrycode $cn)) [ + uiFastImg "" "countryflags/" (strlower (getclientcountrycode $cn)) "" (*f 1.5 $uiPad:4XL) $uiPad:4XL + uispace $uiPad:O2 0 [] + ]) + ] + ] [ + uihlist 0 [ + uiFastImg "" "countryflag/" (strlower (getclientcountrycode $cn)) "" (*f 1.5 $uiPad:4XL) $uiPad:4XL + uispace $uiPad:O2 0 [] + ] + ]) + uiclamp-y ; uialign- 1 + ]) + ]) + ] $uiPad:5XL + ++ n + ] + ] + + if (m_teammode $getmode) [ + .scoreboard.aesir = [ + local n + uivlist 0 [ + @@(.scoreboard.header 1) + loopscoreboard cn 1 [ @@@(.scoreboard.row 1) ] + ] ; uialign- -2 -1 + ] + .scoreboard.vanir = [ + local n + uivlist 0 [ + @@(.scoreboard.header 2) + loopscoreboard cn 2 [ @@@(.scoreboard.row 2) ] + ] ; uialign- -2 -1 + ] + .scoreboard.ctf = [ + uigroup [ + uioffset 0 $uiPad:O5 [ + uioffset $uiPad:2XL- $uiPad:2XL- [ uiFancyText "wide" "V" 1 ] + uioffset $uiPad:2XL $uiPad:2XL [ uiFancyText "wide" "S" 1 ] + ] ; uialign- 0 -1 + uihlist $uiPad:DM [ + .scoreboard.aesir + .scoreboard.vanir + uialign* -2 -1 + ] + ] + ] + ] + + .scoreboard.spec = [ + uigrid 3 $uiPad:L 0 [ + loopscoreboard cn -1 [ + @@@(.scoreboard.row -1 [] (+f $.sb_namewidth ( + ? $showclientnum $.sb_cellwidth 0 + ))) + ] + ] + ] + + .scoreboard.shelf.top3 = [ + uioffset $uiPad:5XL- 0 [ + uiFastImg (fade 0.5) "hud/" "shelf" "" $uiPad:5XL + push n (at [ ] $n) [ + uiFastImg $n "hud/" "glow" "" $uiPad:5XL + uiFastImg $n "hud/" "crown" "" $uiPad:3XL + ] + ] ; uialign- -1 + ] + .scoreboard.shelf.rest = [ + arg1 = (= $cn $getclientnum) + uioffset $uiPad:5XL- 0 [ + uiFastImg (fade (? $arg1 0.5 0.25)) "hud/" "shelf" "" $uiPad:5XL + uiFastImg "hud/" "glow" "" $uiPad:5XL + push n (+ $n 1) [ + uifontoutline 1 0x7F [ + uifontcolortext "default" $n (|A! (? $arg1 0xB0 0x60)) 0.55 + ] + ] + ] ; uialign- -1 + ] + + .scoreboard.ffa = [ + local n x + uigroup [ + uivlist 0 [ + @@@(.scoreboard.header 0) + loopscoreboard cn 0 [ + if (< $n 3) [ + if (= $cn $getclientnum) [ x = 1 ] + @@@@@(.scoreboard.row 0 $.scoreboard.shelf.top3) + if (= $n 3) [ uifill 0 $uiPad:M ] + ] [ + if (= $cn $getclientnum) [ + if (> $n (+ $.sb_offset 10)) [ uifill 0 $uiPad:M ] + @@@@@@(.scoreboard.row 0 $.scoreboard.shelf.rest) + if (< $n (+ $.sb_offset 0)) [ uifill 0 $uiPad:M ] + ] [ + if (&& [<= $n (+ $.sb_offset 10 $x $isspectator)] [>= $n $.sb_offset]) [ + @@@@@@@(.scoreboard.row 0 $.scoreboard.shelf.rest) + ] [ ++ n ] + ] + ] + ] + ] + uioffset $uiPad:2XL 0 [ + uialign 1 + // XXX need a way to scan for flag carrier + ] + ] + ] + ] ] ////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/config/ui/hud/geo_prefab.cfg b/config/ui/hud/geo_prefab.cfg index 9e8eb1182..e234d4d64 100644 --- a/config/ui/hud/geo_prefab.cfg +++ b/config/ui/hud/geo_prefab.cfg @@ -1,53 +1,53 @@ uiLiteMenu "geo_prefab" [ - uialign 1 + uialign 1 ] [ - uiHudCell 0 0 [ - uiScrollBox "credits" $.x $.y [ - uihover [] [ .UI_obrsel = -1 ] - uigrid 2 0 0 [ - uialign -1 -1 - local i ; i = 0 - loopfiles f "data/prefab" obr [ - uiButtonArea "none" $uiPad:D4XL $uiPad:D4XL [ - uispace 0.0006 0.0006 [ - if (!= $i $.UI_obrsel) [ - uimodcolor $c_boxmod - uioutline $c_line1 - ] [ - uimodcolor $c_grdmod - uioutline (uiCosWave:INT $c_green $c_line1 (uihold? 250 500)) - ] - uiclamp*e - ] - uiprefabpreview $f $c_gray_l - uiclamp*e - uispace $uiPad:S $uiPad:O3 [ - uialign -1 -1 - uifontoutline 1 0x7F [ - uifontcolortext "default" $i (|A! 0x98) 0.55 - ] - ] - uihover [ - if (!= $i $.UI_obrsel) [ - .UI_obrsel = $i - uiSetMillis - ] - ] - uipress [ uiSetMillis ] - uirelease [ pasteprefab $f ] - ] - i = (+ $i 1) - ] - ] - ] - ] + uiHudCell 0 0 [ + uiScrollBox "credits" $.x $.y [ + uihover [] [ .UI_obrsel = -1 ] + uigrid 2 0 0 [ + uialign -1 -1 + local i ; i = 0 + loopfiles f "data/prefab" obr [ + uiButtonArea "none" $uiPad:D4XL $uiPad:D4XL [ + uispace 0.0006 0.0006 [ + if (!= $i $.UI_obrsel) [ + uimodcolor $c_boxmod + uioutline $c_line1 + ] [ + uimodcolor $c_grdmod + uioutline (uiCosWave:INT $c_green $c_line1 (uihold? 250 500)) + ] + uiclamp*e + ] + uiprefabpreview $f $c_gray_l + uiclamp*e + uispace $uiPad:S $uiPad:O3 [ + uialign -1 -1 + uifontoutline 1 0x7F [ + uifontcolortext "default" $i (|A! 0x98) 0.55 + ] + ] + uihover [ + if (!= $i $.UI_obrsel) [ + .UI_obrsel = $i + uiSetMillis + ] + ] + uipress [ uiSetMillis ] + uirelease [ pasteprefab $f ] + ] + i = (+ $i 1) + ] + ] + ] + ] ] [ - .UI_obrsel = -1 - .x = (*f $uiPad:D4XL 2) - .y = (-f 1 (*f $uiPad:L 4)) - hideui "texture_browser" - hideui "mapmodel_browser" - hideui "map_browser" - hideui "editvars" + .UI_obrsel = -1 + .x = (*f $uiPad:D4XL 2) + .y = (-f 1 (*f $uiPad:L 4)) + hideui "texture_browser" + hideui "mapmodel_browser" + hideui "map_browser" + hideui "editvars" ] [ showui "editvars" ] diff --git a/config/ui/hud/mapmodel_browser.cfg b/config/ui/hud/mapmodel_browser.cfg index 1924e9930..975b8b42b 100644 --- a/config/ui/hud/mapmodel_browser.cfg +++ b/config/ui/hud/mapmodel_browser.cfg @@ -1,137 +1,137 @@ uiLiteMenu "mapmodel_browser" [ - uialign 1 - if (> $.mm_totalpg 1) [ - uiscrolldown [ UIscrollsound .mm_curpage (? (iskeyheld "LCTRL") 999 1) (- $.mm_totalpg 1) ] - uiscrollup [ UIscrollsound .mm_curpage (? (iskeyheld "LCTRL") -999 -1) 0 ] - ] + uialign 1 + if (> $.mm_totalpg 1) [ + uiscrolldown [ UIscrollsound .mm_curpage (? (iskeyheld "LCTRL") 999 1) (- $.mm_totalpg 1) ] + uiscrollup [ UIscrollsound .mm_curpage (? (iskeyheld "LCTRL") -999 -1) 0 ] + ] ] [ - uiHudCell 0 0 [ - uivlist $uiPad:L [ - uihlist 0 [ - uiButton "hold2 $c_diag2" [ - uiCheckbox [= $.mm_doaction 1] 0 0 0 [ - if $arg1 [ uiTriangle $c_line1 0 $uiPad:O5 0 180 ] - ] - uitext "Create" 0.6 - ] $.z 0 [ .mm_doaction = 1 ] -1 - uigroup [ - uifill $.z $uiPad:6XL - uihlist $uiPad:O3- [ - uiclamp.y - uifill $uiPad:DXS 0 [ - uifonttext "mono" (+ $.mm_curpage 1) 0.7 - uialign- 1 -1 - ] - uioffset 0 $uiPad:L- [ - uiclip 0 $uiPad:6XL [ uicolortext "/" $c_gray 1.2 ] - ] // text takes up a lot of vertical space, have to "cut" it - uifill $uiPad:DXS 0 [ - uifonttext "mono" (max $.mm_totalpg 1) 0.7 - uialign- -1 1 - ] - uiclamp*y - ] - ] - uiButton "hold2 $c_diag2" [ - uitext "Replace" 0.6 - uiCheckbox [= $.mm_doaction 0] 0 0 0 [ - if $arg1 [ uiTriangle $c_line1 0 $uiPad:O5 0 180 ] - ] - ] $.z 0 [ .mm_doaction = 0 ] 1 - uiclamp*y - ] - uigroup [ - uifill $.x $.y - uihover [] [ uihold [] [ .mm_selidx = -1 ] ] - uigrid 3 0 0 [ - uialign -1 -1 - loopwhile+ i (* $.mm_curpage 15) 15 [< $i $nummapmodels] [ // XXX this condition doesn't work as intended, needs redesign - n = (mod $i 15) // page normalized index - uitarget $.z $.z [ - if (= $n $.mm_selidx) [ - uimodcolor $c_grdmod - uimodelpreview (mapmodelname $i 1) "mapmodel" - uiclamp*e - uispace $uiPad:S $uiPad:O3 [ - uifontoutline 1 0x7F [ uifonttext "default" $i 0.55 ] - ] ; uialign- -1 -1 - if (iskeyheld "LSHIFT") [ - uispace $uiPad:S 0 [ - uifontoutline 1 0x7F [uifonttext "modernpics" "R" 0.65 ] - ] ; uialign- -1 1 - uirelease [ clearmodel (+s "mapmodel/" (mapmodelname $i)) ] - ] [ - uirelease [ - if $.mm_doaction [ newent mapmodel $i ] [ - if $enthavesel [ - entloop [ if (=s $enttype "mapmodel") [ - entset "mapmodel" $i (entattr 1) (entattr 2) (entattr 3) (entattr 4) - ] ] - ] [ newent mapmodel $i ] - ] - ] - ] - ] [ - uimodcolor $c_boxmod - uimodelpreview (mapmodelname $i 1) "mapmodel" - uiclamp*e - uispace $uiPad:S $uiPad:O3 [ - uifontoutline 1 0x7F [ uifontcolortext "default" $i (|A! 0xA0) 0.55 ] - ] ; uialign- -1 -1 - uihover [ .mm_selidx = $n ; uiSetMillis ] - uioutline $c_line1 $.z $.z - ] - if (mapmodelloaded $i) [ - uispace $uiPad:S $uiPad:O3 [ - uifontoutline 1 0x7F [ uifonttext "modernpics" "%" 0.55 ] - ] ; uialign- 1 -1 - ] - uiHoverOnce [ uiHoverSound ] - ] - ] - ] - if (> $.mm_selidx -1) [ // drawing on top to avoid overlap from other cells - uioffset (*f $.z (mod $.mm_selidx 3)) (*f $.z (/ $.mm_selidx 3)) [ - uioutline (uiCosWave:INT $c_green $c_line1 500) $.z $.z - ] ; uialign- -1 -1 - ] - ] - uihlist 0 [ - uifont "modernpics" [ - uifill $.z - if (> $.mm_curpage 0) [ - uiButtonArea "hold2 $c_diag2" 0 0 [ - uiclamp.e - uitext "<" 0.9 - uirelease [ -- .mm_curpage ] - ] - ] [ uicolortext "<" $c_gray 0.9 ] - ] - uifill $.z 0 [ uiFastImg "" "ui/" "io/" "mouseM" $uiPad:6XL ] - uifont "modernpics" [ - uifill $.z - if (< (+ $.mm_curpage 1) $.mm_totalpg) [ - uiButtonArea "hold2 $c_diag2" 0 0 [ - uiclamp.e - uitext ">" 0.9 - uirelease [ ++ .mm_curpage ] - ] - ] [ uicolortext ">" $c_gray 0.9 ] - ] - uiclamp*y - ] - ] - ] + uiHudCell 0 0 [ + uivlist $uiPad:L [ + uihlist 0 [ + uiButton "hold2 $c_diag2" [ + uiCheckbox [= $.mm_doaction 1] 0 0 0 [ + if $arg1 [ uiTriangle $c_line1 0 $uiPad:O5 0 180 ] + ] + uitext "Create" 0.6 + ] $.z 0 [ .mm_doaction = 1 ] -1 + uigroup [ + uifill $.z $uiPad:6XL + uihlist $uiPad:O3- [ + uiclamp.y + uifill $uiPad:DXS 0 [ + uifonttext "mono" (+ $.mm_curpage 1) 0.7 + uialign- 1 -1 + ] + uioffset 0 $uiPad:L- [ + uiclip 0 $uiPad:6XL [ uicolortext "/" $c_gray 1.2 ] + ] // text takes up a lot of vertical space, have to "cut" it + uifill $uiPad:DXS 0 [ + uifonttext "mono" (max $.mm_totalpg 1) 0.7 + uialign- -1 1 + ] + uiclamp*y + ] + ] + uiButton "hold2 $c_diag2" [ + uitext "Replace" 0.6 + uiCheckbox [= $.mm_doaction 0] 0 0 0 [ + if $arg1 [ uiTriangle $c_line1 0 $uiPad:O5 0 180 ] + ] + ] $.z 0 [ .mm_doaction = 0 ] 1 + uiclamp*y + ] + uigroup [ + uifill $.x $.y + uihover [] [ uihold [] [ .mm_selidx = -1 ] ] + uigrid 3 0 0 [ + uialign -1 -1 + loopwhile+ i (* $.mm_curpage 15) 15 [< $i $nummapmodels] [ // XXX this condition doesn't work as intended, needs redesign + n = (mod $i 15) // page normalized index + uitarget $.z $.z [ + if (= $n $.mm_selidx) [ + uimodcolor $c_grdmod + uimodelpreview (mapmodelname $i 1) "mapmodel" + uiclamp*e + uispace $uiPad:S $uiPad:O3 [ + uifontoutline 1 0x7F [ uifonttext "default" $i 0.55 ] + ] ; uialign- -1 -1 + if (iskeyheld "LSHIFT") [ + uispace $uiPad:S 0 [ + uifontoutline 1 0x7F [uifonttext "modernpics" "R" 0.65 ] + ] ; uialign- -1 1 + uirelease [ clearmodel (+s "mapmodel/" (mapmodelname $i)) ] + ] [ + uirelease [ + if $.mm_doaction [ newent mapmodel $i ] [ + if $enthavesel [ + entloop [ if (=s $enttype "mapmodel") [ + entset "mapmodel" $i (entattr 1) (entattr 2) (entattr 3) (entattr 4) + ] ] + ] [ newent mapmodel $i ] + ] + ] + ] + ] [ + uimodcolor $c_boxmod + uimodelpreview (mapmodelname $i 1) "mapmodel" + uiclamp*e + uispace $uiPad:S $uiPad:O3 [ + uifontoutline 1 0x7F [ uifontcolortext "default" $i (|A! 0xA0) 0.55 ] + ] ; uialign- -1 -1 + uihover [ .mm_selidx = $n ; uiSetMillis ] + uioutline $c_line1 $.z $.z + ] + if (mapmodelloaded $i) [ + uispace $uiPad:S $uiPad:O3 [ + uifontoutline 1 0x7F [ uifonttext "modernpics" "%" 0.55 ] + ] ; uialign- 1 -1 + ] + uiHoverOnce [ uiHoverSound ] + ] + ] + ] + if (> $.mm_selidx -1) [ // drawing on top to avoid overlap from other cells + uioffset (*f $.z (mod $.mm_selidx 3)) (*f $.z (/ $.mm_selidx 3)) [ + uioutline (uiCosWave:INT $c_green $c_line1 500) $.z $.z + ] ; uialign- -1 -1 + ] + ] + uihlist 0 [ + uifont "modernpics" [ + uifill $.z + if (> $.mm_curpage 0) [ + uiButtonArea "hold2 $c_diag2" 0 0 [ + uiclamp.e + uitext "<" 0.9 + uirelease [ -- .mm_curpage ] + ] + ] [ uicolortext "<" $c_gray 0.9 ] + ] + uifill $.z 0 [ uiFastImg "" "ui/" "io/" "mouseM" $uiPad:6XL ] + uifont "modernpics" [ + uifill $.z + if (< (+ $.mm_curpage 1) $.mm_totalpg) [ + uiButtonArea "hold2 $c_diag2" 0 0 [ + uiclamp.e + uitext ">" 0.9 + uirelease [ ++ .mm_curpage ] + ] + ] [ uicolortext ">" $c_gray 0.9 ] + ] + uiclamp*y + ] + ] + ] ] [ - .y = (-f 1 $uiPad:DM $uiPad:DS) - .z = (/f $.y 5) - .x = (*f $.z 3) - .mm_selidx = -1 - .mm_totalpg = (ceil (/f $nummapmodels 15)) - .mm_curpage = (min $.mm_curpage (- $.mm_totalpg 1)) - hideui "texture_browser" - hideui "map_browser" - hideui "geo_prefab" - hideui "editvars" + .y = (-f 1 $uiPad:DM $uiPad:DS) + .z = (/f $.y 5) + .x = (*f $.z 3) + .mm_selidx = -1 + .mm_totalpg = (ceil (/f $nummapmodels 15)) + .mm_curpage = (min $.mm_curpage (- $.mm_totalpg 1)) + hideui "texture_browser" + hideui "map_browser" + hideui "geo_prefab" + hideui "editvars" ] [ if $editing [ showui "editvars" ] ] diff --git a/config/ui/hud/texture_browser.cfg b/config/ui/hud/texture_browser.cfg index 1a9de4df4..b767ba829 100644 --- a/config/ui/hud/texture_browser.cfg +++ b/config/ui/hud/texture_browser.cfg @@ -1,179 +1,179 @@ uiLiteMenu "texture_browser" [ - uialign 1 + uialign 1 ] [ - uiHudCell 0 0 [ - uiScrollBox "credits" $.x $.y [ - uihover [] [ .UI_texsel = -1 ] - uigrid $.UI_texcol 0 0 [ - uialign -1 -1 - loop i $numslots [ // or looptexmru - uiButtonArea "none" $uiPad:DXL $uiPad:DXL [ - uispace 0.0006 0.0006 [ - uiclamp.e - if (!= $i $.UI_texsel) [ - uimodcolor $c_boxmod - uislotview $i - uioutline $c_line1 - ] [ - uimodcolor $c_grdmod - uislotview $i - uioutline (uiCosWave:INT $c_green $c_line1 (uihold? 250 500)) - ] - uiclamp*e - ] - uihover [ - if (!= $.UI_texsel $i) [ - .UI_texsel = $i - uiSetMillis - ] - uispace $uiPad:S $uiPad:O3 [ - uialign -1 -1 - uifontoutline 1 0x7F [ - uifontcolortext "default" $i (|A! 0x98) 0.55 - ] - ] - ] - uipress [ uiSetMillis ] - uirelease [ - settex (getslottex $i) - hideui $uiname - ] - ] - ] - ] - ] - ] + uiHudCell 0 0 [ + uiScrollBox "credits" $.x $.y [ + uihover [] [ .UI_texsel = -1 ] + uigrid $.UI_texcol 0 0 [ + uialign -1 -1 + loop i $numslots [ // or looptexmru + uiButtonArea "none" $uiPad:DXL $uiPad:DXL [ + uispace 0.0006 0.0006 [ + uiclamp.e + if (!= $i $.UI_texsel) [ + uimodcolor $c_boxmod + uislotview $i + uioutline $c_line1 + ] [ + uimodcolor $c_grdmod + uislotview $i + uioutline (uiCosWave:INT $c_green $c_line1 (uihold? 250 500)) + ] + uiclamp*e + ] + uihover [ + if (!= $.UI_texsel $i) [ + .UI_texsel = $i + uiSetMillis + ] + uispace $uiPad:S $uiPad:O3 [ + uialign -1 -1 + uifontoutline 1 0x7F [ + uifontcolortext "default" $i (|A! 0x98) 0.55 + ] + ] + ] + uipress [ uiSetMillis ] + uirelease [ + settex (getslottex $i) + hideui $uiname + ] + ] + ] + ] + ] + ] ] [ - .UI_texsel = -1 - .UI_texcol = (+ (>f (/f $screenw $screenh) 1.3) 3) - .x = (*f $uiPad:DXL $.UI_texcol) - .y = (-f 1 (*f $uiPad:L 4)) - hideui "mapmodel_browser" - hideui "map_browser" - hideui "geo_prefab" - hideui "editvars" + .UI_texsel = -1 + .UI_texcol = (+ (>f (/f $screenw $screenh) 1.3) 3) + .x = (*f $uiPad:DXL $.UI_texcol) + .y = (-f 1 (*f $uiPad:L 4)) + hideui "mapmodel_browser" + hideui "map_browser" + hideui "geo_prefab" + hideui "editvars" ] [ if $editing [ showui "editvars" ] ] //================================================================== uiLiteMenu "texture_browser" [ - uialign 1 - if (> $.tb_totalpg 1) [ - uiscrolldown [ UIscrollsound .tb_curpage (? (iskeyheld "LCTRL") 999 1) (- $.tb_totalpg 1) ] - uiscrollup [ UIscrollsound .tb_curpage (? (iskeyheld "LCTRL") -999 -1) 0 ] - ] + uialign 1 + if (> $.tb_totalpg 1) [ + uiscrolldown [ UIscrollsound .tb_curpage (? (iskeyheld "LCTRL") 999 1) (- $.tb_totalpg 1) ] + uiscrollup [ UIscrollsound .tb_curpage (? (iskeyheld "LCTRL") -999 -1) 0 ] + ] ] [ - uiHudCell 0 0 [ - uivlist $uiPad:L [ - uihlist 0 [ - uiButton "hold2 $c_diag2" [ - //uiCheckbox [= $.tb_doaction 1] 0 0 0 [ - // if $arg1 [ uiTriangle $c_line1 0 $uiPad:O5 0 180 ] - //] - uitext "Standard" 0.6 - ] $.z 0 [ .tb_doaction = 1 ] -1 - uigroup [ - uifill $.z $uiPad:6XL - uihlist $uiPad:O3- [ - uiclamp.y - uifill $uiPad:DXS 0 [ - uifonttext "mono" (+ $.tb_curpage 1) 0.7 - uialign- 1 -1 - ] - uioffset 0 $uiPad:L- [ - uiclip 0 $uiPad:6XL [ uicolortext "/" $c_gray 1.2 ] - ] // text takes up a lot of vertical space, have to "cut" it - uifill $uiPad:DXS 0 [ - uifonttext "mono" (max $.tb_totalpg 1) 0.7 - uialign- -1 1 - ] - uiclamp*y - ] - ] - uiButton "hold2 $c_diag2" [ - uitext "M.R.U." 0.6 - //uiCheckbox [= $.tb_doaction 0] 0 0 0 [ - // if $arg1 [ uiTriangle $c_line1 0 $uiPad:O5 0 180 ] - //] - ] $.z 0 [ .tb_doaction = 0 ] 1 - uiclamp*y - ] - uigroup [ - uifill $.x $.y - uihover [] [ uihold [] [ .tb_selidx = -1 ] ] - uigrid 6 0 0 [ - uialign -1 -1 - loopwhile+ i (* $.tb_curpage 60) 60 [< $i $numslots] [ - n = (mod $i 60) // page normalized index - uitarget $.w $.w [ - if (= $n $.tb_selidx) [ - uimodcolor $c_grdmod - uislotview $i - uiclamp*e - uispace $uiPad:S $uiPad:O3 [ - uifontoutline 1 0x7F [ - uifontcolortext "default" $i (|A! 0xA0) 0.55 - ] - ] ; uialign- -1 -1 - uirelease [ - settex (getslottex $i) - hideui $uiname - ] - ] [ - uimodcolor $c_boxmod - uislotview $i - uioutline $c_line1 - uiclamp*e - uihover [ .tb_selidx = $n ; uiSetMillis ] - - ] - uiHoverOnce [ uiHoverSound ] - ] - ] - ] - if (> $.tb_selidx -1) [ // drawing on top to avoid overlap from other cells - uioffset (*f $.w (mod $.tb_selidx 6)) (*f $.w (/ $.tb_selidx 6)) [ - uioutline (uiCosWave:INT $c_green $c_line1 500) $.w $.w - ] ; uialign- -1 -1 - ] - ] - uihlist 0 [ - uifont "modernpics" [ - uifill $.z - if (> $.tb_curpage 0) [ - uiButtonArea "hold2 $c_diag2" 0 0 [ - uiclamp.e - uitext "<" 0.9 - uirelease [ -- .tb_curpage ] - ] - ] [ uicolortext "<" $c_gray 0.9 ] - ] - uifill $.z 0 [ uiFastImg "" "ui/" "io/" "mouseM" $uiPad:6XL ] - uifont "modernpics" [ - uifill $.z - if (< (+ $.tb_curpage 1) $.tb_totalpg) [ - uiButtonArea "hold2 $c_diag2" 0 0 [ - uiclamp.e - uitext ">" 0.9 - uirelease [ ++ .tb_curpage ] - ] - ] [ uicolortext ">" $c_gray 0.9 ] - ] - uiclamp*y - ] - ] - ] + uiHudCell 0 0 [ + uivlist $uiPad:L [ + uihlist 0 [ + uiButton "hold2 $c_diag2" [ + //uiCheckbox [= $.tb_doaction 1] 0 0 0 [ + // if $arg1 [ uiTriangle $c_line1 0 $uiPad:O5 0 180 ] + //] + uitext "Standard" 0.6 + ] $.z 0 [ .tb_doaction = 1 ] -1 + uigroup [ + uifill $.z $uiPad:6XL + uihlist $uiPad:O3- [ + uiclamp.y + uifill $uiPad:DXS 0 [ + uifonttext "mono" (+ $.tb_curpage 1) 0.7 + uialign- 1 -1 + ] + uioffset 0 $uiPad:L- [ + uiclip 0 $uiPad:6XL [ uicolortext "/" $c_gray 1.2 ] + ] // text takes up a lot of vertical space, have to "cut" it + uifill $uiPad:DXS 0 [ + uifonttext "mono" (max $.tb_totalpg 1) 0.7 + uialign- -1 1 + ] + uiclamp*y + ] + ] + uiButton "hold2 $c_diag2" [ + uitext "M.R.U." 0.6 + //uiCheckbox [= $.tb_doaction 0] 0 0 0 [ + // if $arg1 [ uiTriangle $c_line1 0 $uiPad:O5 0 180 ] + //] + ] $.z 0 [ .tb_doaction = 0 ] 1 + uiclamp*y + ] + uigroup [ + uifill $.x $.y + uihover [] [ uihold [] [ .tb_selidx = -1 ] ] + uigrid 6 0 0 [ + uialign -1 -1 + loopwhile+ i (* $.tb_curpage 60) 60 [< $i $numslots] [ + n = (mod $i 60) // page normalized index + uitarget $.w $.w [ + if (= $n $.tb_selidx) [ + uimodcolor $c_grdmod + uislotview $i + uiclamp*e + uispace $uiPad:S $uiPad:O3 [ + uifontoutline 1 0x7F [ + uifontcolortext "default" $i (|A! 0xA0) 0.55 + ] + ] ; uialign- -1 -1 + uirelease [ + settex (getslottex $i) + hideui $uiname + ] + ] [ + uimodcolor $c_boxmod + uislotview $i + uioutline $c_line1 + uiclamp*e + uihover [ .tb_selidx = $n ; uiSetMillis ] + + ] + uiHoverOnce [ uiHoverSound ] + ] + ] + ] + if (> $.tb_selidx -1) [ // drawing on top to avoid overlap from other cells + uioffset (*f $.w (mod $.tb_selidx 6)) (*f $.w (/ $.tb_selidx 6)) [ + uioutline (uiCosWave:INT $c_green $c_line1 500) $.w $.w + ] ; uialign- -1 -1 + ] + ] + uihlist 0 [ + uifont "modernpics" [ + uifill $.z + if (> $.tb_curpage 0) [ + uiButtonArea "hold2 $c_diag2" 0 0 [ + uiclamp.e + uitext "<" 0.9 + uirelease [ -- .tb_curpage ] + ] + ] [ uicolortext "<" $c_gray 0.9 ] + ] + uifill $.z 0 [ uiFastImg "" "ui/" "io/" "mouseM" $uiPad:6XL ] + uifont "modernpics" [ + uifill $.z + if (< (+ $.tb_curpage 1) $.tb_totalpg) [ + uiButtonArea "hold2 $c_diag2" 0 0 [ + uiclamp.e + uitext ">" 0.9 + uirelease [ ++ .tb_curpage ] + ] + ] [ uicolortext ">" $c_gray 0.9 ] + ] + uiclamp*y + ] + ] + ] ] [ - .y = (-f 1 $uiPad:DM $uiPad:DS) //0.86 - .z = (/f $.y 5) // 0.172 - .w = (/f $.y 10) // 0.086 - .x = (*f $.z 3) // 0.516 - .tb_selidx = -1 - .tb_totalpg = (ceil (/f $numslots 60)) - .tb_curpage = (min $.tb_curpage (- $.tb_totalpg 1)) - hideui "mapmodel_browser" - hideui "map_browser" - hideui "geo_prefab" - hideui "editvars" + .y = (-f 1 $uiPad:DM $uiPad:DS) //0.86 + .z = (/f $.y 5) // 0.172 + .w = (/f $.y 10) // 0.086 + .x = (*f $.z 3) // 0.516 + .tb_selidx = -1 + .tb_totalpg = (ceil (/f $numslots 60)) + .tb_curpage = (min $.tb_curpage (- $.tb_totalpg 1)) + hideui "mapmodel_browser" + hideui "map_browser" + hideui "geo_prefab" + hideui "editvars" ] [ if $editing [ showui "editvars" ] ] diff --git a/config/ui/lib.cfg b/config/ui/lib.cfg index fde3ee2e5..7591b8165 100644 --- a/config/ui/lib.cfg +++ b/config/ui/lib.cfg @@ -793,30 +793,30 @@ uiButton = [ // uiFancyText FONT TEXT SIZE 0xCOLOR TRANSPARENCY uiFancyText = [ - if (=s $arg1 "") [ arg1 = "default" ] - if $arg4 [] [ arg4 = $c_white ] - if (< $numargs 5) [ arg5 = 1 ] - uifont $arg1 [ - uifontoutline 1 0x20 [ - uishadow 255 [ - uicolortext $arg2 (|A (*fA $arg5) $arg4) $arg3 - ] - ] - ] + if (=s $arg1 "") [ arg1 = "default" ] + if $arg4 [] [ arg4 = $c_white ] + if (< $numargs 5) [ arg5 = 1 ] + uifont $arg1 [ + uifontoutline 1 0x20 [ + uishadow 255 [ + uicolortext $arg2 (|A (*fA $arg5) $arg4) $arg3 + ] + ] + ] ] // uiEmbossText FONT TEXT SIZE 0xCOLOR uiEmbossText = [ - if (=s $arg1 "") [ arg1 = "default" ] - uigroup [ - uioffset $uiPad:O2- $uiPad:O2 [ - uifontcolortext $arg1 (stripcolors $arg2) 0 $arg3 - ] - uioffset $uiPad:O2 $uiPad:O2- [ - uifontcolortext $arg1 (stripcolors $arg2) (INT:TRANS $arg4 * 2) $arg3 - ] - uifontcolortext $arg1 $arg2 (? (< $arg4 0) $c_white $arg4) $arg3 - ] + if (=s $arg1 "") [ arg1 = "default" ] + uigroup [ + uioffset $uiPad:O2- $uiPad:O2 [ + uifontcolortext $arg1 (stripcolors $arg2) 0 $arg3 + ] + uioffset $uiPad:O2 $uiPad:O2- [ + uifontcolortext $arg1 (stripcolors $arg2) (INT:TRANS $arg4 * 2) $arg3 + ] + uifontcolortext $arg1 $arg2 (? (< $arg4 0) $c_white $arg4) $arg3 + ] ] @@ -828,128 +828,128 @@ uiEmbossText = [ // uiVerSld VAR MIN MAX STEP [ on-change ] [ label formatting ] X Y [ pulse trigger cond ] uiVerSld = [ - if $arg7 [] [ arg7 = $uiPad:5XL ] - if $arg8 [] [ arg8 = $uiPad:5XL ] - if $arg6 [ arg6 = (do $arg6) ] [ arg6 = $$arg1 ] - if $arg9 [ arg9 = (do $arg9) ] [ arg9 = (|| uihover+? uihold+?) ] - uitarget $arg7 $arg8 [ - uiHoverOnce [ uiHoverSound 2 ] - uiSetScrollLock 1 - uispace $uiPad:O4 0 [ - style_generic_box $c_main1 n d d (? $arg9 p d) - uiclamp*e - ] - uivslider $arg1 $arg2 $arg3 $arg4 [ doargs $arg5 ] [ - uisliderbutton [ - if (<=f $arg2 $$arg1 $arg3) [ - uifill 0 $uiPad:3XL - uiline $c_baige - uiclamp*x - ] - ] ; uiclamp-x - uifontoutline 1 0xFF [ - uivlist $uiPad:M- [ - loop i (strlen $arg6) [ - uifontcolortext "default" (substr $arg6 $i 1) (|A! (? $arg9 0xA0 0x60)) 0.65 - ] - ] - ] - ] - uiclamp*e - ] + if $arg7 [] [ arg7 = $uiPad:5XL ] + if $arg8 [] [ arg8 = $uiPad:5XL ] + if $arg6 [ arg6 = (do $arg6) ] [ arg6 = $$arg1 ] + if $arg9 [ arg9 = (do $arg9) ] [ arg9 = (|| uihover+? uihold+?) ] + uitarget $arg7 $arg8 [ + uiHoverOnce [ uiHoverSound 2 ] + uiSetScrollLock 1 + uispace $uiPad:O4 0 [ + style_generic_box $c_main1 n d d (? $arg9 p d) + uiclamp*e + ] + uivslider $arg1 $arg2 $arg3 $arg4 [ doargs $arg5 ] [ + uisliderbutton [ + if (<=f $arg2 $$arg1 $arg3) [ + uifill 0 $uiPad:3XL + uiline $c_baige + uiclamp*x + ] + ] ; uiclamp-x + uifontoutline 1 0xFF [ + uivlist $uiPad:M- [ + loop i (strlen $arg6) [ + uifontcolortext "default" (substr $arg6 $i 1) (|A! (? $arg9 0xA0 0x60)) 0.65 + ] + ] + ] + ] + uiclamp*e + ] ] // uiHorSld VAR MIN MAX STEP [ on-change ] [ label formatting ] X Y [ pulse trigger cond ] uiHorSld = [ - if $arg7 [] [ arg7 = $uiPad:5XL ] - if $arg8 [] [ arg8 = $uiPad:5XL ] - if $arg6 [ arg6 = (do $arg6) ] [ arg6 = $$arg1 ] - if $arg9 [ arg9 = (do $arg9) ] [ arg9 = (|| uihover+? uihold+?) ] - uitarget $arg7 $arg8 [ - uiHoverOnce [ uiHoverSound 2 ] - uiSetScrollLock 1 - uispace 0 $uiPad:O4 [ - style_generic_box $c_main1 n d d (? $arg9 p d) - uiclamp*e - ] - uihslider $arg1 $arg2 $arg3 $arg4 [ doargs $arg5 ] [ - uisliderbutton [ - if (<=f $arg2 $$arg1 $arg3) [ - uifill $uiPad:3XL - uiline $c_baige - uiclamp*y - ] - ] ; uiclamp-y - uifontoutline 1 0xFF [ - uifontcolortext "default" $arg6 (|A! (? $arg9 0xA0 0x60)) 0.65 - ] - ] - uiclamp*e - ] + if $arg7 [] [ arg7 = $uiPad:5XL ] + if $arg8 [] [ arg8 = $uiPad:5XL ] + if $arg6 [ arg6 = (do $arg6) ] [ arg6 = $$arg1 ] + if $arg9 [ arg9 = (do $arg9) ] [ arg9 = (|| uihover+? uihold+?) ] + uitarget $arg7 $arg8 [ + uiHoverOnce [ uiHoverSound 2 ] + uiSetScrollLock 1 + uispace 0 $uiPad:O4 [ + style_generic_box $c_main1 n d d (? $arg9 p d) + uiclamp*e + ] + uihslider $arg1 $arg2 $arg3 $arg4 [ doargs $arg5 ] [ + uisliderbutton [ + if (<=f $arg2 $$arg1 $arg3) [ + uifill $uiPad:3XL + uiline $c_baige + uiclamp*y + ] + ] ; uiclamp-y + uifontoutline 1 0xFF [ + uifontcolortext "default" $arg6 (|A! (? $arg9 0xA0 0x60)) 0.65 + ] + ] + uiclamp*e + ] ] // uiVerColorSld VAR MIN MAX 0xCOLOR1 0xCOLOR2 [ on-change ] X Y [ pulse trigger cond ] uiVerColorSld = [ - if $arg7 [] [ arg7 = $uiPad:5XL ] - if $arg8 [] [ arg8 = $uiPad:5XL ] - if $arg9 [ arg9 = (do $arg9) ] [ arg9 = (|| uihover+? uihold+?) ] - uitarget $arg7 $arg8 [ - uiHoverOnce [ uiHoverSound 2 ] - uiSetScrollLock 1 - uispace $uiPad:O4 0 [ - uivgradient $arg4 $arg5 - uiFastImgStretched "" "shadow3" - uioutline $c_diag1 - uiclamp*e - ] - uivslider $arg1 $arg2 $arg3 1 [ doargs $arg6 ] [ - uisliderbutton [ - if (<=f $arg2 $$arg1 $arg3) [ - uifill 0 $uiPad:3XL - uiline $c_baige - uiclamp*x - ] - ] ; uiclamp-x - uifontoutline 1 0xFF [ - uivlist $uiPad:M- [ - loop i (strlen $$arg1) [ - uifontcolortext "wide" (substr $$arg1 $i 1) (|A! (? $arg9 0xA0 0x60)) 0.7 - ] - ] - ] - ] - uiclamp*e - ] + if $arg7 [] [ arg7 = $uiPad:5XL ] + if $arg8 [] [ arg8 = $uiPad:5XL ] + if $arg9 [ arg9 = (do $arg9) ] [ arg9 = (|| uihover+? uihold+?) ] + uitarget $arg7 $arg8 [ + uiHoverOnce [ uiHoverSound 2 ] + uiSetScrollLock 1 + uispace $uiPad:O4 0 [ + uivgradient $arg4 $arg5 + uiFastImgStretched "" "shadow3" + uioutline $c_diag1 + uiclamp*e + ] + uivslider $arg1 $arg2 $arg3 1 [ doargs $arg6 ] [ + uisliderbutton [ + if (<=f $arg2 $$arg1 $arg3) [ + uifill 0 $uiPad:3XL + uiline $c_baige + uiclamp*x + ] + ] ; uiclamp-x + uifontoutline 1 0xFF [ + uivlist $uiPad:M- [ + loop i (strlen $$arg1) [ + uifontcolortext "wide" (substr $$arg1 $i 1) (|A! (? $arg9 0xA0 0x60)) 0.7 + ] + ] + ] + ] + uiclamp*e + ] ] // uiHorColorSld VAR MIN MAX 0xCOLOR1 0xCOLOR2 [ on-change ] X Y [ pulse trigger cond ] uiHorColorSld = [ - if $arg7 [] [ arg7 = $uiPad:5XL ] - if $arg8 [] [ arg8 = $uiPad:5XL ] - if $arg9 [ arg9 = (do $arg9) ] [ arg9 = (|| uihover+? uihold+?) ] - uitarget $arg7 $arg8 [ - uiHoverOnce [ uiHoverSound 2 ] - uiSetScrollLock 1 - uispace 0 $uiPad:O4 [ - uihgradient $arg4 $arg5 - uiFastImgStretched "" "shadow3" - uioutline $c_diag1 - uiclamp*e - ] - uihslider $arg1 $arg2 $arg3 1 [ doargs $arg6 ] [ - uisliderbutton [ - if (<=f $arg2 $$arg1 $arg3) [ - uifill $uiPad:3XL - uiline $c_baige - uiclamp*y - ] - ] ; uiclamp-y - uifontoutline 1 0xFF [ - uifontcolortext "wide" $$arg1 (|A! (? $arg9 0xA0 0x60)) 0.7 - ] - ] - uiclamp*e - ] + if $arg7 [] [ arg7 = $uiPad:5XL ] + if $arg8 [] [ arg8 = $uiPad:5XL ] + if $arg9 [ arg9 = (do $arg9) ] [ arg9 = (|| uihover+? uihold+?) ] + uitarget $arg7 $arg8 [ + uiHoverOnce [ uiHoverSound 2 ] + uiSetScrollLock 1 + uispace 0 $uiPad:O4 [ + uihgradient $arg4 $arg5 + uiFastImgStretched "" "shadow3" + uioutline $c_diag1 + uiclamp*e + ] + uihslider $arg1 $arg2 $arg3 1 [ doargs $arg6 ] [ + uisliderbutton [ + if (<=f $arg2 $$arg1 $arg3) [ + uifill $uiPad:3XL + uiline $c_baige + uiclamp*y + ] + ] ; uiclamp-y + uifontoutline 1 0xFF [ + uifontcolortext "wide" $$arg1 (|A! (? $arg9 0xA0 0x60)) 0.7 + ] + ] + uiclamp*e + ] ] // convenience block that allows an INT-based color slider group diff --git a/config/ui/libnew.cfg b/config/ui/libnew.cfg index d8c29e613..12af21429 100644 --- a/config/ui/libnew.cfg +++ b/config/ui/libnew.cfg @@ -7,36 +7,36 @@ // usage: /toggleUI "name" MILLIS ACTION toggleUI = [ - local idxWindow idxState curState - nodebug [ idxWindow = (indexof $uiAnimWindows $arg1) ] - - if (> $idxWindow -1) [ - idxState = (+ $idxWindow 1) - curState = (at $uiAnimWindows $idxState) - if (&& [> $numargs 3] [= $arg4 $curState]) [] [ - if $arg2 [] [ arg2 = $uiWindowSpeed ] - [.@arg1.timestamp] = $getmillis - [.@arg1.duration] = $arg2 - [.@arg1.progress] = (% (*f $arg2 $[.@arg1.opacity]) -100) - if $curState [ [.@arg1.progress] = (+ $arg2 $[.@arg1.progress]) ] - listsplice= uiAnimWindows (! $curState) $idxState 1 - ] - ] [ - if $arg2 [] [ arg2 = $uiWindowSpeed ] - [.@arg1.timestamp] = $getmillis - [.@arg1.duration] = $arg2 - if (> $numargs 3) [ - if (!= $arg4 (uivisible $arg1)) [ - append uiAnimWindows $arg1 - append uiAnimWindows $arg4 - ] - ] [ - arg4 = (! (uivisible $arg1)) - append uiAnimWindows $arg1 - append uiAnimWindows $arg4 - ] - if $arg4 [ showui $arg1 ] - ] + local idxWindow idxState curState + nodebug [ idxWindow = (indexof $uiAnimWindows $arg1) ] + + if (> $idxWindow -1) [ + idxState = (+ $idxWindow 1) + curState = (at $uiAnimWindows $idxState) + if (&& [> $numargs 3] [= $arg4 $curState]) [] [ + if $arg2 [] [ arg2 = $uiWindowSpeed ] + [.@arg1.timestamp] = $getmillis + [.@arg1.duration] = $arg2 + [.@arg1.progress] = (% (*f $arg2 $[.@arg1.opacity]) -100) + if $curState [ [.@arg1.progress] = (+ $arg2 $[.@arg1.progress]) ] + listsplice= uiAnimWindows (! $curState) $idxState 1 + ] + ] [ + if $arg2 [] [ arg2 = $uiWindowSpeed ] + [.@arg1.timestamp] = $getmillis + [.@arg1.duration] = $arg2 + if (> $numargs 3) [ + if (!= $arg4 (uivisible $arg1)) [ + append uiAnimWindows $arg1 + append uiAnimWindows $arg4 + ] + ] [ + arg4 = (! (uivisible $arg1)) + append uiAnimWindows $arg1 + append uiAnimWindows $arg4 + ] + if $arg4 [ showui $arg1 ] + ] ] // usage: /enableUI "name" MILLIS @@ -48,61 +48,61 @@ disableUI = [ toggleUI $arg1 $arg2 $arg3 0 ] // controls opacity percentage of given color // usage: 0xAARRGGBB |f = [ - if (!= $(+s . $uiname .opacity) 1) [ - arg2 = (& (>> $arg1 24) 0xFF) - if $arg2 [ - | (<< (ceil (*f $(+s . $uiname .opacity) $arg2)) 24) (& $arg1 0xFFFFFF) - ] [ | (<< (ceil (*f $(+s . $uiname .opacity) 0xFF)) 24) $arg1 ] - ] [ result $arg1 ] + if (!= $(+s . $uiname .opacity) 1) [ + arg2 = (& (>> $arg1 24) 0xFF) + if $arg2 [ + | (<< (ceil (*f $(+s . $uiname .opacity) $arg2)) 24) (& $arg1 0xFFFFFF) + ] [ | (<< (ceil (*f $(+s . $uiname .opacity) 0xFF)) 24) $arg1 ] + ] [ result $arg1 ] ] // TexCMD Fade-Multiply combo function // usage: alpha(0-1) 0xRRGGBB ifm = [ - arg1 = (*f $arg1 $[.@uiname.opacity]) - arg1 = (+s < fade: (round $arg1 0.1) >) - if (> $numargs 1) [ - arg2 = (+s < intmul: (+ $arg2) >) - ] ; +s $arg1 $arg2 + arg1 = (*f $arg1 $[.@uiname.opacity]) + arg1 = (+s < fade: (round $arg1 0.1) >) + if (> $numargs 1) [ + arg2 = (+s < intmul: (+ $arg2) >) + ] ; +s $arg1 $arg2 ] //////////////////////////////////////////////////////////////////////////////////////// uiWindow = [ - newui $arg1 [ - uieschide 0 - @arg5 - if $uiInputOOB [ - uieschide 0 - uipress [ uiCleanKB ] - uiescpress [ uiCleanKB ] - ] - uivlist 0 [ - @@(? $arg6 [ - uicolor (|f 0xEE181818) 0 0 [ - uiFastImgStretched (ifm 0.5) "shadow2" "" "" [] $uiPad:6XL - uiclamp*e ; @@arg6 - ] - ]) - uicolor (|f 0xEE202020) 0.01 0.01 [ - uiFastImgTiled (ifm 1 0) "ui/" "ui_bg0" - uiFastImgStretched (ifm 0.5) "shadow3" "" "" [] $uiPad:M - uispace $uiPad:L $uiPad:L [ @@@@arg2 ] ; uiclamp*e - ] ; uiclamp*x - ] - uioutline (|f 0xEE181818) - uiclamp*e - ] [ - uiSetBgBlur 1 - uiSetMillis @arg1 - uiSetLanguage @arg1 - @arg3 - ] [ - uiSetMillis @arg1 - uiSetBgBlur -1 - if (=s $.UI_KBthisui @arg1) [ uiCleanKB ] - @arg4 - ] + newui $arg1 [ + uieschide 0 + @arg5 + if $uiInputOOB [ + uieschide 0 + uipress [ uiCleanKB ] + uiescpress [ uiCleanKB ] + ] + uivlist 0 [ + @@(? $arg6 [ + uicolor (|f 0xEE181818) 0 0 [ + uiFastImgStretched (ifm 0.5) "shadow2" "" "" [] $uiPad:6XL + uiclamp*e ; @@arg6 + ] + ]) + uicolor (|f 0xEE202020) 0.01 0.01 [ + uiFastImgTiled (ifm 1 0) "ui/" "ui_bg0" + uiFastImgStretched (ifm 0.5) "shadow3" "" "" [] $uiPad:M + uispace $uiPad:L $uiPad:L [ @@@@arg2 ] ; uiclamp*e + ] ; uiclamp*x + ] + uioutline (|f 0xEE181818) + uiclamp*e + ] [ + uiSetBgBlur 1 + uiSetMillis @arg1 + uiSetLanguage @arg1 + @arg3 + ] [ + uiSetMillis @arg1 + uiSetBgBlur -1 + if (=s $.UI_KBthisui @arg1) [ uiCleanKB ] + @arg4 + ] ] uiText = [ uicolortext $arg1 (|f 0xFFFFFF) $arg2 ] @@ -111,36 +111,36 @@ uiFontText = [ uifont $arg1 [ uiText $arg2 $arg3 ] ] uiFontColorText = [ uifont $arg1 [ uiColorText $arg2 $arg3 $arg4 ] ] uiBarH = [ - if (< $numargs 3) [ - arg3 = (|f $c_line1) - ] [ arg3 = (|f $arg3) ] - if (|| $arg1 $arg2) [ - uispace $arg1 $arg2 [ - uiline $arg3 ; uiclamp-x - ] ; uiclamp-x - ] [ uiline $arg3 ; uiclamp-x ] + if (< $numargs 3) [ + arg3 = (|f $c_line1) + ] [ arg3 = (|f $arg3) ] + if (|| $arg1 $arg2) [ + uispace $arg1 $arg2 [ + uiline $arg3 ; uiclamp-x + ] ; uiclamp-x + ] [ uiline $arg3 ; uiclamp-x ] ] uiBarV = [ - if (< $numargs 3) [ - arg3 = (|f $c_line1) - ] [ arg3 = (|f $arg3) ] - if (|| $arg1 $arg2) [ - uispace $arg1 $arg2 [ - uiline $arg3 ; uiclamp-y - ] ; uiclamp-y - ] [ uiline $arg3 ; uiclamp-y ] + if (< $numargs 3) [ + arg3 = (|f $c_line1) + ] [ arg3 = (|f $arg3) ] + if (|| $arg1 $arg2) [ + uispace $arg1 $arg2 [ + uiline $arg3 ; uiclamp-y + ] ; uiclamp-y + ] [ uiline $arg3 ; uiclamp-y ] ] uiWindow "test1" [ - uivlist $uiPad:3XL [ - uiSliderH uiscale 0.5 1.5 0.1 [] [ +s (+ (% $$arg1 1)) "%" ] ; uiclamp-x - uicolor (|f 0x404040) 0 0.2 [ - uiFontColorText "wide" "SCALE" 3 0x202020 - uiFontText "wide" "TESTING" 0.8 -1 - uioutline (|f 0xFF0000) ; uiclamp-e - ] - ] + uivlist $uiPad:3XL [ + uiSliderH uiscale 0.5 1.5 0.1 [] [ +s (+ (% $$arg1 1)) "%" ] ; uiclamp-x + uicolor (|f 0x404040) 0 0.2 [ + uiFontColorText "wide" "SCALE" 3 0x202020 + uiFontText "wide" "TESTING" 0.8 -1 + uioutline (|f 0xFF0000) ; uiclamp-e + ] + ] ] [] [] [] [ uiText "TEST" 0.8 ] //////////////////////////////////////////////////////////////////////////////////////// @@ -148,36 +148,36 @@ uiWindow "test1" [ // uiHorSld VAR MIN MAX STEP [ on-change ] [ label formatting ] X Y [ pulse trigger cond ] uiSliderH = [ - if $arg7 [] [ arg7 = $uiPad:5XL ] - if $arg8 [] [ arg8 = $uiPad:5XL ] - if $arg6 [ arg6 = (do $arg6) ] [ arg6 = $$arg1 ] - if $arg9 [ arg9 = (do $arg9) ] [ arg9 = (|| uihover+? uihold+?) ] - uitarget $arg7 $arg8 [ - uiHoverOnce [ uiHoverSound ] - uiSetScrollLock 1 - uihslider $arg1 $arg2 $arg3 $arg4 [ doargs $arg5 ] [ - uihlist 0 [ - loop n 31 [ - uiBarV 0 (*f $arg8 (? (mod $n 5) 0.175 0.05)) $c_cb0 - ] - ] ; uiclamp-e - uiBarH 0 0 $c_cb0 - uisliderbutton [ - if (<=f $arg2 $$arg1 $arg3) [ - uicolor (|f $c_main1) 0 0 [ - uioutline (|f $c_main1) - uiFastImgTiled "ui/" "ui_bg2" "" [ uiclamp.e ] - uiFastImgStretched "" "shadow2" "" "" [ uiclamp.x ] - uiclamp*e - if $arg9 [ arg10 = 0xA0FFFFFF ] [ arg10 = 0x60FFFFFF ] - uifontoutline 1 0xFF [ - uiFontColorText "default" $arg6 0.65 $arg10 - ] - ] ; uiclamp-y - ] - ] ; uiclamp-y - ] ; uiclamp-e - ] + if $arg7 [] [ arg7 = $uiPad:5XL ] + if $arg8 [] [ arg8 = $uiPad:5XL ] + if $arg6 [ arg6 = (do $arg6) ] [ arg6 = $$arg1 ] + if $arg9 [ arg9 = (do $arg9) ] [ arg9 = (|| uihover+? uihold+?) ] + uitarget $arg7 $arg8 [ + uiHoverOnce [ uiHoverSound ] + uiSetScrollLock 1 + uihslider $arg1 $arg2 $arg3 $arg4 [ doargs $arg5 ] [ + uihlist 0 [ + loop n 31 [ + uiBarV 0 (*f $arg8 (? (mod $n 5) 0.175 0.05)) $c_cb0 + ] + ] ; uiclamp-e + uiBarH 0 0 $c_cb0 + uisliderbutton [ + if (<=f $arg2 $$arg1 $arg3) [ + uicolor (|f $c_main1) 0 0 [ + uioutline (|f $c_main1) + uiFastImgTiled "ui/" "ui_bg2" "" [ uiclamp.e ] + uiFastImgStretched "" "shadow2" "" "" [ uiclamp.x ] + uiclamp*e + if $arg9 [ arg10 = 0xA0FFFFFF ] [ arg10 = 0x60FFFFFF ] + uifontoutline 1 0xFF [ + uiFontColorText "default" $arg6 0.65 $arg10 + ] + ] ; uiclamp-y + ] + ] ; uiclamp-y + ] ; uiclamp-e + ] ] diff --git a/config/ui/permanent.cfg b/config/ui/permanent.cfg index 10c117c91..89ea57597 100644 --- a/config/ui/permanent.cfg +++ b/config/ui/permanent.cfg @@ -4,104 +4,104 @@ /////////////////////////////////////////////////////////////////////////////// newui "permanent" [ - uieschide 0 - uiclamp.e + uieschide 0 + uiclamp.e - // Disables active input field if clicking Out-Of-Bounds - if $uiInputOOB [ - uiallowinput 1 - uipress [ uiCleanKB ] - uiescpress [ uiCleanKB ] - uitarget - ] [ uiallowinput 0 ] + // Disables active input field if clicking Out-Of-Bounds + if $uiInputOOB [ + uiallowinput 1 + uipress [ uiCleanKB ] + uiescpress [ uiCleanKB ] + uitarget + ] [ uiallowinput 0 ] - nodebug [ - .AnimProgress - ] + nodebug [ + .AnimProgress + ] - if $mainmenu [ - uiallowinput (uivisible "main") - uigroup [ - uitarget 0 0 [ - uipress [ changeui "credits" "main" ] - uispace $uiPad:4XL $uiPad:4XL [ - uivlist $uiPad:3XL- [ - uiFastImg "" "badge_tesseract" "" "" $uiPad:D2XL - uifontoutline 1 0x7F [ - uivlist $uiPad:M- [ - uifontcolortext "wide" "TESSERACT" $c_cyan_t 0.6 - uifontcolortext "wide" "ENGINE" $c_cyan_t 0.7 - ] - ] - ] - ] - ] ; uialign- 1 1 - uispace $uiPad:M $uiPad:M [ + if $mainmenu [ + uiallowinput (uivisible "main") + uigroup [ + uitarget 0 0 [ + uipress [ changeui "credits" "main" ] + uispace $uiPad:4XL $uiPad:4XL [ + uivlist $uiPad:3XL- [ + uiFastImg "" "badge_tesseract" "" "" $uiPad:D2XL + uifontoutline 1 0x7F [ + uivlist $uiPad:M- [ + uifontcolortext "wide" "TESSERACT" $c_cyan_t 0.6 + uifontcolortext "wide" "ENGINE" $c_cyan_t 0.7 + ] + ] + ] + ] + ] ; uialign- 1 1 + uispace $uiPad:M $uiPad:M [ uitext $currentversionstring 0.5 ] ; uialign- 1 -1 - ] - ] [ nodebug [ - .CheckPermBits - ] ] ; uiclamp*e + ] + ] [ nodebug [ + .CheckPermBits + ] ] ; uiclamp*e ] [ - showui "tips" - uiEffects = 0 // stores bits for fullscreen effects + showui "tips" + uiEffects = 0 // stores bits for fullscreen effects ] // arg1: var // arg2: duration // arg3: condition uiAnimate = [ - arg1 = [.@arg1.anim] ; if $arg3 [ - if (f $$arg1 0.01) [ - $arg1 = (-f $$arg1 (/f $getframemillis $arg2)) - ] [ $arg1 = (max 0 $$arg1) ] - ] + arg1 = [.@arg1.anim] ; if $arg3 [ + if (f $$arg1 0.01) [ + $arg1 = (-f $$arg1 (/f $getframemillis $arg2)) + ] [ $arg1 = (max 0 $$arg1) ] + ] ] .AnimProgress = [ - local animProgress windowsCache - windowsCache = $uiAnimWindows - looplist2 ui curState $windowsCache [ - if $curState [ - animProgress = (- $getmillis $[.@ui.timestamp] $[.@ui.progress] -1) - ] [ animProgress = (- (+ $[.@ui.timestamp] $[.@ui.duration]) $getmillis $[.@ui.progress]) ] - if (<= 0 $animProgress $[.@ui.duration]) [ - [.@ui.opacity] = (%f $animProgress $[.@ui.duration]) - ] [ - listsplice= uiAnimWindows "" (indexof $uiAnimWindows $ui) 2 - [.@ui.opacity] = $curState - [.@ui.progress] = 0 - if $curState [] [ hideui $ui ] - ] - ] + local animProgress windowsCache + windowsCache = $uiAnimWindows + looplist2 ui curState $windowsCache [ + if $curState [ + animProgress = (- $getmillis $[.@ui.timestamp] $[.@ui.progress] -1) + ] [ animProgress = (- (+ $[.@ui.timestamp] $[.@ui.duration]) $getmillis $[.@ui.progress]) ] + if (<= 0 $animProgress $[.@ui.duration]) [ + [.@ui.opacity] = (%f $animProgress $[.@ui.duration]) + ] [ + listsplice= uiAnimWindows "" (indexof $uiAnimWindows $ui) 2 + [.@ui.opacity] = $curState + [.@ui.progress] = 0 + if $curState [] [ hideui $ui ] + ] + ] ] .CheckPermBits = [ - local bits - if (&& $uiBlurBg $uiBlurring) [ ^= bits 1 ] // blur condition - if (&& $uiVignette $uiBlurring) [ ^= bits 2 ] // vignette condition - if (|| (isdead $getclientnum) [ && $spectating [ - < (+ (getclienthealth $getfollow) 1) 0 - ] ]) [ ^= bits 4 ] // damage condition + local bits + if (&& $uiBlurBg $uiBlurring) [ ^= bits 1 ] // blur condition + if (&& $uiVignette $uiBlurring) [ ^= bits 2 ] // vignette condition + if (|| (isdead $getclientnum) [ && $spectating [ + < (+ (getclienthealth $getfollow) 1) 0 + ] ]) [ ^= bits 4 ] // damage condition - uiAnimate effects 120 $bits + uiAnimate effects 120 $bits - if $bits [ uiEffects = $bits ] [ - if (=f $.effects.anim 0) [ uiEffects = 0 ] - ] + if $bits [ uiEffects = $bits ] [ + if (=f $.effects.anim 0) [ uiEffects = 0 ] + ] - if (& $uiEffects 7) [ - uiFastImgTiled (fade $.effects.anim) "ui/" "ui_bg2" - ] - if (& $uiEffects 4) [ - uiFastImg (fade $.effects.anim) "hud/" "damage" - ] - if (& $uiEffects 2) [ - uiFastImg (fade $.effects.anim) "shadow" - ] + if (& $uiEffects 7) [ + uiFastImgTiled (fade $.effects.anim) "ui/" "ui_bg2" + ] + if (& $uiEffects 4) [ + uiFastImg (fade $.effects.anim) "hud/" "damage" + ] + if (& $uiEffects 2) [ + uiFastImg (fade $.effects.anim) "shadow" + ] ] From 71eb7a407dd1d69f83d6fe93b26b0348ac69f5ac Mon Sep 17 00:00:00 2001 From: Jed- Date: Sat, 30 Nov 2024 14:25:57 +0100 Subject: [PATCH 40/68] Update Makefile --- source/Makefile | 89 ++++++++++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 38 deletions(-) diff --git a/source/Makefile b/source/Makefile index edc27f3cd..c3567c307 100644 --- a/source/Makefile +++ b/source/Makefile @@ -462,67 +462,77 @@ engine/worldio.o: engine/texture.h engine/bih.h engine/model.h game/ai.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/ai.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h game/ai.o: engine/sound.h shared/iengine.h shared/igame.h shared/unicode.h -game/ai.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h -game/ai.o: game/monster.h +game/ai.o: game/projectile.h game/weapon.h game/ai.h game/gamemode.h +game/ai.o: game/entity.h game/monster.h game/gameclient.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/gameclient.o: shared/ents.h shared/command.h shared/glexts.h game/gameclient.o: shared/glemu.h engine/sound.h shared/iengine.h -game/gameclient.o: shared/igame.h shared/unicode.h game/weapon.h game/ai.h -game/gameclient.o: game/gamemode.h game/entity.h game/monster.h game/ctf.h -game/gameclient.o: game/elimination.h +game/gameclient.o: shared/igame.h shared/unicode.h game/projectile.h +game/gameclient.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h +game/gameclient.o: game/monster.h game/ctf.h game/elimination.h game/entity.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/entity.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h game/entity.o: engine/sound.h shared/iengine.h shared/igame.h -game/entity.o: shared/unicode.h game/weapon.h game/ai.h game/gamemode.h -game/entity.o: game/entity.h game/monster.h +game/entity.o: shared/unicode.h game/projectile.h game/weapon.h game/ai.h +game/entity.o: game/gamemode.h game/entity.h game/monster.h game/game.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/game.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h game/game.o: engine/sound.h shared/iengine.h shared/igame.h shared/unicode.h -game/game.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h -game/game.o: game/monster.h +game/game.o: game/projectile.h game/weapon.h game/ai.h game/gamemode.h +game/game.o: game/entity.h game/monster.h game/render.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/render.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h game/render.o: engine/sound.h shared/iengine.h shared/igame.h -game/render.o: shared/unicode.h game/weapon.h game/ai.h game/gamemode.h -game/render.o: game/entity.h game/monster.h +game/render.o: shared/unicode.h game/projectile.h game/weapon.h game/ai.h +game/render.o: game/gamemode.h game/entity.h game/monster.h game/scoreboard.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/scoreboard.o: shared/ents.h shared/command.h shared/glexts.h game/scoreboard.o: shared/glemu.h engine/sound.h shared/iengine.h -game/scoreboard.o: shared/igame.h shared/unicode.h game/weapon.h game/ai.h -game/scoreboard.o: game/gamemode.h game/entity.h game/monster.h +game/scoreboard.o: shared/igame.h shared/unicode.h game/projectile.h +game/scoreboard.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h +game/scoreboard.o: game/monster.h game/gameserver.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/gameserver.o: shared/ents.h shared/command.h shared/glexts.h game/gameserver.o: shared/glemu.h engine/sound.h shared/iengine.h -game/gameserver.o: shared/igame.h shared/unicode.h game/weapon.h game/ai.h -game/gameserver.o: game/gamemode.h game/entity.h game/monster.h game/geoip.h -game/gameserver.o: game/ctf.h game/elimination.h game/extinfo.h -game/gameserver.o: game/aimanager.h +game/gameserver.o: shared/igame.h shared/unicode.h game/projectile.h +game/gameserver.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h +game/gameserver.o: game/monster.h game/geoip.h game/ctf.h game/elimination.h +game/gameserver.o: game/extinfo.h game/aimanager.h game/waypoint.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/waypoint.o: shared/ents.h shared/command.h shared/glexts.h game/waypoint.o: shared/glemu.h engine/sound.h shared/iengine.h -game/waypoint.o: shared/igame.h shared/unicode.h game/weapon.h game/ai.h -game/waypoint.o: game/gamemode.h game/entity.h game/monster.h +game/waypoint.o: shared/igame.h shared/unicode.h game/projectile.h +game/waypoint.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h +game/waypoint.o: game/monster.h game/monster.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/monster.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h game/monster.o: engine/sound.h shared/iengine.h shared/igame.h -game/monster.o: shared/unicode.h game/weapon.h game/ai.h game/gamemode.h -game/monster.o: game/entity.h game/monster.h +game/monster.o: shared/unicode.h game/projectile.h game/weapon.h game/ai.h +game/monster.o: game/gamemode.h game/entity.h game/monster.h game/weapon.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/weapon.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h game/weapon.o: engine/sound.h shared/iengine.h shared/igame.h -game/weapon.o: shared/unicode.h game/weapon.h game/ai.h game/gamemode.h -game/weapon.o: game/entity.h game/monster.h +game/weapon.o: shared/unicode.h game/projectile.h game/weapon.h game/ai.h +game/weapon.o: game/gamemode.h game/entity.h game/monster.h game/gamephysics.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/gamephysics.o: shared/ents.h shared/command.h shared/glexts.h game/gamephysics.o: shared/glemu.h engine/sound.h shared/iengine.h -game/gamephysics.o: shared/igame.h shared/unicode.h game/weapon.h game/ai.h -game/gamephysics.o: game/gamemode.h game/entity.h game/monster.h +game/gamephysics.o: shared/igame.h shared/unicode.h game/projectile.h +game/gamephysics.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h +game/gamephysics.o: game/monster.h game/hud.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/hud.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h game/hud.o: engine/sound.h shared/iengine.h shared/igame.h shared/unicode.h -game/hud.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h -game/hud.o: game/monster.h engine/engine.h engine/world.h engine/octa.h -game/hud.o: engine/light.h engine/texture.h engine/bih.h engine/model.h +game/hud.o: game/projectile.h game/weapon.h game/ai.h game/gamemode.h +game/hud.o: game/entity.h game/monster.h engine/engine.h engine/world.h +game/hud.o: engine/octa.h engine/light.h engine/texture.h engine/bih.h +game/hud.o: engine/model.h +game/projectile.o: game/game.h shared/cube.h shared/tools.h shared/geom.h +game/projectile.o: shared/ents.h shared/command.h shared/glexts.h +game/projectile.o: shared/glemu.h engine/sound.h shared/iengine.h +game/projectile.o: shared/igame.h shared/unicode.h game/projectile.h +game/projectile.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h +game/projectile.o: game/monster.h shared/cube.h.gch: shared/tools.h shared/geom.h shared/ents.h shared/cube.h.gch: shared/command.h shared/glexts.h shared/glemu.h @@ -535,8 +545,8 @@ engine/engine.h.gch: engine/texture.h engine/bih.h engine/model.h game/game.h.gch: shared/cube.h shared/tools.h shared/geom.h shared/ents.h game/game.h.gch: shared/command.h shared/glexts.h shared/glemu.h game/game.h.gch: engine/sound.h shared/iengine.h shared/igame.h -game/game.h.gch: shared/unicode.h game/weapon.h game/ai.h game/gamemode.h -game/game.h.gch: game/entity.h game/monster.h +game/game.h.gch: shared/unicode.h game/projectile.h game/game.h game/weapon.h +game/game.h.gch: game/ai.h game/gamemode.h game/entity.h game/monster.h standalone/shared/crypto.o: shared/cube.h shared/tools.h shared/geom.h standalone/shared/crypto.o: shared/ents.h shared/command.h engine/sound.h @@ -562,15 +572,17 @@ standalone/engine/worldio.o: engine/world.h standalone/game/entity.o: game/game.h shared/cube.h shared/tools.h standalone/game/entity.o: shared/geom.h shared/ents.h shared/command.h standalone/game/entity.o: engine/sound.h shared/iengine.h shared/igame.h -standalone/game/entity.o: shared/unicode.h game/weapon.h game/ai.h -standalone/game/entity.o: game/gamemode.h game/entity.h game/monster.h +standalone/game/entity.o: shared/unicode.h game/projectile.h game/weapon.h +standalone/game/entity.o: game/ai.h game/gamemode.h game/entity.h +standalone/game/entity.o: game/monster.h standalone/game/gameserver.o: game/game.h shared/cube.h shared/tools.h standalone/game/gameserver.o: shared/geom.h shared/ents.h shared/command.h standalone/game/gameserver.o: engine/sound.h shared/iengine.h shared/igame.h -standalone/game/gameserver.o: shared/unicode.h game/weapon.h game/ai.h -standalone/game/gameserver.o: game/gamemode.h game/entity.h game/monster.h -standalone/game/gameserver.o: game/geoip.h game/ctf.h game/elimination.h -standalone/game/gameserver.o: game/extinfo.h game/aimanager.h +standalone/game/gameserver.o: shared/unicode.h game/projectile.h +standalone/game/gameserver.o: game/weapon.h game/ai.h game/gamemode.h +standalone/game/gameserver.o: game/entity.h game/monster.h game/geoip.h +standalone/game/gameserver.o: game/ctf.h game/elimination.h game/extinfo.h +standalone/game/gameserver.o: game/aimanager.h standalone/engine/master.o: shared/cube.h shared/tools.h shared/geom.h standalone/engine/master.o: shared/ents.h shared/command.h engine/sound.h standalone/engine/master.o: shared/iengine.h shared/igame.h @@ -585,5 +597,6 @@ standalone/engine/engine.h.gch: engine/world.h standalone/game/game.h.gch: shared/cube.h shared/tools.h shared/geom.h standalone/game/game.h.gch: shared/ents.h shared/command.h engine/sound.h standalone/game/game.h.gch: shared/iengine.h shared/igame.h shared/unicode.h -standalone/game/game.h.gch: game/weapon.h game/ai.h game/gamemode.h -standalone/game/game.h.gch: game/entity.h game/monster.h +standalone/game/game.h.gch: game/projectile.h game/game.h game/weapon.h +standalone/game/game.h.gch: game/ai.h game/gamemode.h game/entity.h +standalone/game/game.h.gch: game/monster.h From 05e9c766679299893a4b46b8fa9e34b6cc672c84 Mon Sep 17 00:00:00 2001 From: Jed- Date: Sat, 30 Nov 2024 17:38:39 +0100 Subject: [PATCH 41/68] Fix `iskeyheld` command and get rid of `holdingctrl` --- source/engine/console.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/source/engine/console.cpp b/source/engine/console.cpp index 5b345dd4a..a254c6378 100644 --- a/source/engine/console.cpp +++ b/source/engine/console.cpp @@ -593,12 +593,17 @@ void execbind(keym &k, bool isdown) k.pressed = isdown; } -void iskeyheld(char *key) +int iskeyheld(char *key) { - keym* km = findbind(key); - intret(km->pressed ? 1 : 0); + // look for the key name in the keymap + const keym* km = findbind(key); + if(!km) return 0; + + // convert keycode to scancode + const SDL_Scancode scancode = SDL_GetScancodeFromKey(km->code); + return SDL_GetKeyboardState(nullptr)[scancode]; } -ICOMMAND(iskeyheld, "s", (char* key), iskeyheld(key)); +ICOMMAND(iskeyheld, "s", (char* key), intret(iskeyheld(key))); bool consoleinput(const char *str, int len) { @@ -817,9 +822,6 @@ bool consolekey(int code, bool isdown) return true; } -// returns 1 if the user is holding either of the CTRL keys -ICOMMAND(holdingctrl, "", (), intret(SDL_GetModState()&MOD_KEYS ? 1 : 0)); - void processtextinput(const char *str, int len) { if(!UI::textinput(str, len)) From 517d5fc1edc52396504c513dab9087d06eff857a Mon Sep 17 00:00:00 2001 From: Jed- Date: Sun, 1 Dec 2024 14:18:32 +0100 Subject: [PATCH 42/68] Remove unnecessary memset --- source/engine/console.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/source/engine/console.cpp b/source/engine/console.cpp index a254c6378..dca1fc269 100644 --- a/source/engine/console.cpp +++ b/source/engine/console.cpp @@ -50,11 +50,6 @@ const char *getprefix(int type) void conline(int type, const char *sf) // add a line to the console buffer { - if(!conlines.length()) - { - // initialize the queue with zeroes - memset(conlines.data, 0, MAXCONLINES * sizeof(cline)); - } char *buf = NULL; if(type&CON_TAG_MASK) for(int i = conlines.length()-1; i >= max(conlines.length()-contags, 0); i--) { From 4bdb3577383ad981ac616ad68a60ec878411c5de Mon Sep 17 00:00:00 2001 From: Jed- Date: Mon, 2 Dec 2024 13:05:33 +0100 Subject: [PATCH 43/68] Don't open the scoreboard when pressing "TAB" if the command line is open Add `iscmdlineopen` command --- config/ui/hud/gamehud.cfg | 2 +- source/engine/console.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/config/ui/hud/gamehud.cfg b/config/ui/hud/gamehud.cfg index 6fd1978dc..bff068769 100644 --- a/config/ui/hud/gamehud.cfg +++ b/config/ui/hud/gamehud.cfg @@ -78,7 +78,7 @@ newui "gamehud" [ // SCOREBOARD DISPLAY GROUP // ////////////////////////////////////////////////////////////////////////////////////////////////////////////// - if (iskeyheld "TAB") [ + if (&& [iskeyheld "TAB"] [! $iscmdlineopen]) [ if (uivisible "scoreboard") [] [ showui "scoreboard" ] uivlist $uiPad:5XL [ uialign 0 -1 diff --git a/source/engine/console.cpp b/source/engine/console.cpp index dca1fc269..5954bbec3 100644 --- a/source/engine/console.cpp +++ b/source/engine/console.cpp @@ -599,6 +599,7 @@ int iskeyheld(char *key) return SDL_GetKeyboardState(nullptr)[scancode]; } ICOMMAND(iskeyheld, "s", (char* key), intret(iskeyheld(key))); +ICOMMAND(iscmdlineopen, "", (), intret(commandmillis >= 0 ? 1 : 0)); bool consoleinput(const char *str, int len) { From d66cefe541cb9a418ea52d270df9eb034225bdb9 Mon Sep 17 00:00:00 2001 From: Jed- Date: Tue, 3 Dec 2024 14:25:50 +0100 Subject: [PATCH 44/68] Allow UI elements to receive mouse button press events --- source/engine/ui.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/engine/ui.cpp b/source/engine/ui.cpp index 0e087cc96..de3411c85 100644 --- a/source/engine/ui.cpp +++ b/source/engine/ui.cpp @@ -2152,14 +2152,14 @@ namespace UI uicursorindex = label.xy_to_index(cx/k, cy/k); } - bool key(int code, bool isdown) + bool rawkey(int code, bool isdown) { - if(Object::key(code, isdown)) return true; + if(Object::rawkey(code, isdown)) return true; if(!isdown || cursor < 0) return false; const char *keyname = getkeyname(code); if(!keyname) return false; copystring(uikeycode, keyname); - return true; + return code != -1; // propagate left clicks } bool textinput(const char *str, int len) From 32db006424a0f1451d6d99f81d3adf73ef93d497 Mon Sep 17 00:00:00 2001 From: Jed- Date: Fri, 6 Dec 2024 14:14:33 +0100 Subject: [PATCH 45/68] Allow other text elements to have a cursor and listen for keyboard and mouse events --- source/engine/ui.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/source/engine/ui.cpp b/source/engine/ui.cpp index de3411c85..fa8e49819 100644 --- a/source/engine/ui.cpp +++ b/source/engine/ui.cpp @@ -3757,20 +3757,20 @@ namespace UI } } - ICOMMAND(uicolortext, "tife", (tagval *text, int *c, float *scale, uint *children), - buildtext(*text, *scale, uitextscale, Color(*c), -1, -1, false, children)); + ICOMMAND(uicolortext, "tifieN", (tagval *text, int *c, float *scale, int *cursor, uint *children, int *numargs), + buildtext(*text, *scale, uitextscale, Color(*c), -1, *numargs>=4 ? *cursor : -1, *numargs>=4, children)); ICOMMAND(uitext, "tfieN", (tagval *text, float *scale, int *cursor, uint *children, int *numargs), - buildtext(*text, *scale, uitextscale, Color(255, 255, 255), -1, *numargs >= 3 ? *cursor : -1, *numargs >= 3, children)); + buildtext(*text, *scale, uitextscale, Color(255, 255, 255), -1, *numargs>=3 ? *cursor : -1, *numargs>=3, children)); ICOMMAND(uitextfill, "ffe", (float *minw, float *minh, uint *children), BUILD(Filler, o, o->setup(*minw * uitextscale*0.5f, *minh * uitextscale), children)); - ICOMMAND(uiwrapcolortext, "tfife", (tagval *text, float *wrap, int *c, float *scale, uint *children), - buildtext(*text, *scale, uitextscale, Color(*c), *wrap, -1, false, children)); + ICOMMAND(uiwrapcolortext, "tfifieN", (tagval *text, float *wrap, int *c, float *scale, int *cursor, uint *children, int *numargs), + buildtext(*text, *scale, uitextscale, Color(*c), *wrap, *numargs>=5 ? *cursor : -1, *numargs>=5, children)); - ICOMMAND(uiwraptext, "tffe", (tagval *text, float *wrap, float *scale, uint *children), - buildtext(*text, *scale, uitextscale, Color(255, 255, 255), *wrap, -1, false, children)); + ICOMMAND(uiwraptext, "tffieN", (tagval *text, float *wrap, float *scale, int *cursor, uint *children, int *numargs), + buildtext(*text, *scale, uitextscale, Color(255, 255, 255), *wrap, *numargs>=4 ? *cursor : -1, *numargs>=4, children)); ICOMMAND(uicolorcontext, "tife", (tagval *text, int *c, float *scale, uint *children), buildtext(*text, *scale, FONTH*uicontextscale, Color(*c), -1, -1, false, children)); From db3bcf40cf1d7074a33da2a9c18f631b44611f98 Mon Sep 17 00:00:00 2001 From: Jed- Date: Fri, 6 Dec 2024 14:44:35 +0100 Subject: [PATCH 46/68] Rename function --- source/engine/engine.h | 2 +- source/engine/rendertext.cpp | 2 +- source/engine/ui.cpp | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/source/engine/engine.h b/source/engine/engine.h index f3efaf29f..3a9466a60 100644 --- a/source/engine/engine.h +++ b/source/engine/engine.h @@ -834,7 +834,7 @@ namespace UI void update(); void render(); void cleanup(); - void cleartext(); + void clearlabels(); void resetcursor(); void getcursorpos(float& x, float& y); diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index 9591faefe..01937eacd 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -413,7 +413,7 @@ static PangoLayout *measure_text_internal(const char *str, int len, int maxw, in return layout; } -void reloadfonts() { clear_text_particles(); UI::cleartext(); } +void reloadfonts() { clear_text_particles(); UI::clearlabels(); } namespace text { diff --git a/source/engine/ui.cpp b/source/engine/ui.cpp index fa8e49819..05075d5d8 100644 --- a/source/engine/ui.cpp +++ b/source/engine/ui.cpp @@ -303,11 +303,11 @@ namespace UI }); } - virtual void cleartext() + virtual void clearlabels() { loopchildren(o, { - o->cleartext(); + o->clearlabels(); }); } @@ -2198,9 +2198,9 @@ namespace UI void hide() { Object::hide(); label.clear(); } - void cleartext() + void clearlabels() { - Object::cleartext(); + Object::clearlabels(); label.clear(); } @@ -3961,9 +3961,9 @@ namespace UI DELETEP(world); } - void cleartext() + void clearlabels() { - world->cleartext(); + world->clearlabels(); } void calctextscale() From 69c9a658fcb29348cd6e95ca51f16660f8ed5406 Mon Sep 17 00:00:00 2001 From: Jed- Date: Fri, 6 Dec 2024 15:41:31 +0100 Subject: [PATCH 47/68] Cleanup --- source/engine/engine.h | 10 +++++----- source/engine/rendertext.cpp | 24 ++++++++++++------------ source/engine/ui.cpp | 27 +++++++++++---------------- source/game/game.cpp | 2 +- source/game/game.h | 3 +-- source/game/gameserver.cpp | 2 +- 6 files changed, 31 insertions(+), 37 deletions(-) diff --git a/source/engine/engine.h b/source/engine/engine.h index 3a9466a60..5cf2187ed 100644 --- a/source/engine/engine.h +++ b/source/engine/engine.h @@ -47,7 +47,7 @@ extern vector entgroup; extern Shader *textshader; extern const matrix4x3 *textmatrix; extern float textscale; -extern float fontsize; +extern double fontsize; extern bool init_pangocairo(); extern void done_pangocairo(); @@ -56,7 +56,7 @@ extern bool setfont(const char *name); extern void pushfont(); extern bool popfont(); extern void reloadfonts(); -static inline void setfontsize(float size) { fontsize = size; } +static inline void setfontsize(double size) { fontsize = size; } struct _PangoLayout; namespace text @@ -93,7 +93,7 @@ namespace text // do not call if the label was not prepared with `keep_layout=true` int xy_to_index(float x, float y) const; - friend Label prepare(const char *, int, bvec, int, float, bvec4, int, int, const char *, bool, bool); + friend Label prepare(const char *, int, bvec, int, double, bvec4, int, int, const char *, bool, bool); }; // measure text before creating the label @@ -108,7 +108,7 @@ namespace text Label prepare(const char *str, int maxw, bvec color = bvec(255, 255, 255), int cursor = -1, - float outline = 0, + double outline = 0, bvec4 ol_color = bvec4(0, 0, 0, 0), int align = -1, int justify = 0, @@ -119,7 +119,7 @@ namespace text Label prepare_for_console(const char *str, int maxw, int cursor); const Label& prepare_for_particle(const char *str, bvec color = bvec(255, 255, 255), - float outline = 0, + double outline = 0, bvec4 ol_color = bvec4(0, 0, 0, 0), const char *lang = NULL, bool no_fallback = false diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index 01937eacd..379be22c0 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -5,7 +5,7 @@ #include static int fontid = 0; // used by UI for change detection -float fontsize = 0; // pixel height of the current font +double fontsize = 0; // pixel height of the current font const matrix4x3 *textmatrix = NULL; // used for text particles Shader *textshader = NULL; // used for text particles @@ -506,7 +506,7 @@ namespace text { if(conshadow) { - const double d = 3.f / 4.f * conscale; + const double d = 0.75 * conscale; draw(left - d, top + d, conshadow, true); } draw(left, top); @@ -532,7 +532,7 @@ namespace text if(layout) g_object_unref(layout); } - Label prepare(const char *str, int maxw, bvec color, int cursor, float outline, bvec4 ol_color, int align, int justify, const char *lang, bool no_fallback, bool keep_layout) + Label prepare(const char *str, int maxw, bvec color, int cursor, double outline, bvec4 ol_color, int align, int justify, const char *lang, bool no_fallback, bool keep_layout) { Label label; @@ -548,7 +548,7 @@ namespace text } // create surface and cairo context - if(cursor >= 0) width += max(4.f, fontsize); // make space for the cursor + if(cursor >= 0) width += max(4., fontsize); // make space for the cursor const int ol_offset = ceil(outline); width += 2 * ol_offset; height += 2 * ol_offset; @@ -560,7 +560,7 @@ namespace text // draw text outline if(outline) { - cairo_set_source_rgba(cr, ol_color.r/255.f, ol_color.g/255.f, ol_color.b/255.f, ol_color.a/255.f); + cairo_set_source_rgba(cr, ol_color.r/255., ol_color.g/255., ol_color.b/255., ol_color.a/255.); cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND); cairo_set_line_width(cr, 2 * outline); pango_cairo_layout_path(cr, label.layout); @@ -575,14 +575,14 @@ namespace text PangoRectangle cur_rect; pango_layout_get_cursor_pos(label.layout, cursor, &cur_rect, NULL); - const float curw = max(1.f, fontsize / 16); + const double curw = max(1., fontsize / 16.); cairo_rectangle(cr, cur_rect.x/PANGO_SCALE+ol_offset, cur_rect.y/PANGO_SCALE+ol_offset, curw, cur_rect.height/PANGO_SCALE); if(outline) { - cairo_set_source_rgba(cr, ol_color.r/255.f, ol_color.g/255, ol_color.b/255.f, ol_color.a/255.f); + cairo_set_source_rgba(cr, ol_color.r/255., ol_color.g/255., ol_color.b/255., ol_color.a/255.); cairo_stroke_preserve(cr); } - cairo_set_source_rgba(cr, cursorcolor.r/255.f, cursorcolor.g/255.f, cursorcolor.b/255.f, 1.0); + cairo_set_source_rgba(cr, cursorcolor.r/255., cursorcolor.g/255., cursorcolor.b/255., 1.); cairo_fill(cr); } @@ -620,16 +620,16 @@ namespace text Label prepare_for_console(const char *str, int maxw, int cursor) { - return prepare(str, maxw, bvec(255, 255, 255), cursor, conoutline ? ceil(FONTH / 32.f) : 0, bvec4(0, 0, 0, conoutline)); + return prepare(str, maxw, bvec(255, 255, 255), cursor, conoutline ? ceil(FONTH / 32.) : 0, bvec4(0, 0, 0, conoutline)); } - const Label& prepare_for_particle(const char *str, bvec color, float outline, bvec4 ol_color, const char *lang, bool no_fallback) + const Label& prepare_for_particle(const char *str, bvec color, double outline, bvec4 ol_color, const char *lang, bool no_fallback) { const int c = color.tohexcolor(); const char *l = lang ? lang : ""; uint key = crc32(0 , (const Bytef *)str, strlen(str)) + curfont->id; - key = crc32(key, (const Bytef *)(&outline), sizeof(float)); + key = crc32(key, (const Bytef *)(&outline), sizeof(double)); key = crc32(key, (const Bytef *)(&c), sizeof(int)); key = crc32(key, (const Bytef *)(&ol_color.mask), sizeof(uint)); key = crc32(key, (const Bytef *)l, strlen(l)); @@ -660,7 +660,7 @@ namespace text { if(conshadow) { - const double d = 3.f / 4.f * conscale; + const double d = 0.75 * conscale; label.draw(left - d, top + d, conshadow, true); } label.draw(left, top); diff --git a/source/engine/ui.cpp b/source/engine/ui.cpp index 05075d5d8..3747b4c41 100644 --- a/source/engine/ui.cpp +++ b/source/engine/ui.cpp @@ -1986,11 +1986,12 @@ namespace UI bool curjustify = false, curnofallback = false; static const char *curlanguage = newstring(""); - #define WITHTEXTATTR(name, tmp, val, body) \ + #define WITHTEXTATTR(name, tmp, val, body) do { \ tmp = cur##name; \ cur##name = val; \ body; \ - cur##name = tmp; + cur##name = tmp; \ + } while(0) struct WrapAlign : Object { @@ -2105,7 +2106,6 @@ namespace UI Object::setup(); changed = false; const float newscale = scale_ * uiscale; - const int curfontid = getcurfontid(); if(!uimillis || (totalmillis - lastchange >= uimillis) || !lastchange) { @@ -2185,8 +2185,8 @@ namespace UI setfontsize(scale * hudh); - const float textscale = drawscale(), - x = round(sx/textscale), y = round(sy/textscale); + const double textscale = drawscale(); + const double x = round(sx/textscale), y = round(sy/textscale); pushhudscale(textscale); if(shadow) { @@ -2196,13 +2196,8 @@ namespace UI pophudmatrix(); } - void hide() { Object::hide(); label.clear(); } - - void clearlabels() - { - Object::clearlabels(); - label.clear(); - } + void hide() { Object::hide() ; label.clear(); } + void clearlabels() { Object::clearlabels(); label.clear(); } ~Text() { delete[] language; } @@ -2216,7 +2211,7 @@ namespace UI setfontsize(scale * hudh); - float k = drawscale(); + const float k = drawscale(); // text changes are detected here const char *text = getstr(); @@ -2348,7 +2343,7 @@ namespace UI setfont(font); \ Object::func##children(cx, cy, mask, inside, setflags); \ popfont(); \ - } \ + } DOSTATES #undef DOSTATE @@ -2401,7 +2396,7 @@ namespace UI changedraw(CHANGE_SHADER | CHANGE_COLOR); - float k = drawscale() / uiscale; + const float k = drawscale() / uiscale; pushhudtranslate(sx, sy, k); renderfullconsole(w/k, h/k); pophudmatrix(); @@ -2992,7 +2987,7 @@ namespace UI setfontsize(scale * hudh); float k = drawscale(); - w = max(w, (edit->pixelwidth + FONTW)*k); + w = max(w, (edit->pixelwidth + (float)FONTW)*k); h = max(h, edit->pixelheight*k); } diff --git a/source/game/game.cpp b/source/game/game.cpp index b53ca3f19..400c86a1e 100644 --- a/source/game/game.cpp +++ b/source/game/game.cpp @@ -979,7 +979,7 @@ namespace game return NULL; } - inline bool duplicatename(gameent *d, const char *name = NULL, const char *alt = NULL) + bool duplicatename(gameent *d, const char *name = NULL, const char *alt = NULL) { if(!name) name = d->name; if(alt && d != self && duplicatestring(name, alt)) return true; diff --git a/source/game/game.h b/source/game/game.h index b74ec157c..ff6fd9a39 100644 --- a/source/game/game.h +++ b/source/game/game.h @@ -670,8 +670,7 @@ namespace physics // checks if two strings are visually identical or very similar static inline bool duplicatestring(const char *a, const char *b) { - const uint alen = uni_strlen(a); - if(alen != uni_strlen(b)) return false; + if(uni_strlen(a) != uni_strlen(b)) return false; uint c, d; size_t s = uni_getchar(a, c), z = uni_getchar(b, d); for(const char *p = a, *q = b; c; p += s, q += z, s = uni_getchar(p, c), z = uni_getchar(q, d)) diff --git a/source/game/gameserver.cpp b/source/game/gameserver.cpp index 744a43d32..19bc6caa2 100644 --- a/source/game/gameserver.cpp +++ b/source/game/gameserver.cpp @@ -865,7 +865,7 @@ namespace server return n; } - inline bool duplicatename(clientinfo *ci, const char *name) + bool duplicatename(clientinfo *ci, const char *name) { if(!name) name = ci->name; loopv(clients) if(clients[i]!=ci && duplicatestring(name, clients[i]->name)) return true; From 44d1b8ffa5c7a2c8ece79cf13f3d0978702a1ed2 Mon Sep 17 00:00:00 2001 From: Jed- Date: Wed, 11 Dec 2024 17:13:51 +0100 Subject: [PATCH 48/68] Cleanup Reserve just enough space for the cursor --- source/engine/engine.h | 22 ++--- source/engine/rendertext.cpp | 151 +++++++++++++++++++---------------- source/engine/ui.cpp | 12 +-- source/shared/tools.h | 7 ++ 4 files changed, 103 insertions(+), 89 deletions(-) diff --git a/source/engine/engine.h b/source/engine/engine.h index 5cf2187ed..99e824b10 100644 --- a/source/engine/engine.h +++ b/source/engine/engine.h @@ -64,9 +64,9 @@ namespace text // A rendered text label, ready to be drawn class Label { - GLuint tex; int w, h; int ox, oy; // offsets + GLuint tex; _PangoLayout *layout; int *map_markup_to_text; int *map_text_to_markup; @@ -93,15 +93,14 @@ namespace text // do not call if the label was not prepared with `keep_layout=true` int xy_to_index(float x, float y) const; - friend Label prepare(const char *, int, bvec, int, double, bvec4, int, int, const char *, bool, bool); + friend Label prepare(const char *, int, bvec, int, double, bvec4, int, int, const char *, bool, bool, bool); }; // measure text before creating the label - // TODO: consider removing in favor of `Label::prepare()` void measure(const char *str, int maxw, int &w, int &h, int align = -1, int justify = 0, - const char *lang = NULL, + const char *lang = nullptr, bool no_fallback = false ); @@ -112,26 +111,27 @@ namespace text bvec4 ol_color = bvec4(0, 0, 0, 0), int align = -1, int justify = 0, - const char *lang = NULL, // language code, used for text shaping + const char *lang = nullptr, // language code, used for text shaping bool no_fallback = false, // don't use fallback fonts for unavailable glyphs - bool keep_layout = false // use only if you need to call `xy_to_index()` + bool keep_layout = false, // use only if you need to call `xy_to_index()` + bool reserve_cursor = false // reserve space for the cursor, even if the cursor is absent ); Label prepare_for_console(const char *str, int maxw, int cursor); const Label& prepare_for_particle(const char *str, bvec color = bvec(255, 255, 255), double outline = 0, bvec4 ol_color = bvec4(0, 0, 0, 0), - const char *lang = NULL, + const char *lang = nullptr, bool no_fallback = false ); void draw(const char *str, double left, double top, bvec color = bvec(255, 255, 255), - int a = 255, + int alpha = 255, int maxw = 0, int align = -1, int justify = 0, - const char *lang = NULL, + const char *lang = nullptr, bool no_fallback = false ); static inline void draw_fmt(const char *fstr, double left, double top, ...) @@ -153,13 +153,13 @@ namespace text int visible(const char *str, float hitx, float hity, int maxw, int align = -1, int justify = 0, - const char *lang = NULL, + const char *lang = nullptr, bool no_fallback = false ); void pos(const char *str, int cursor, int &cx, int &cy, int maxw, int align = -1, int justify = 0, - const char *lang = NULL, + const char *lang = nullptr, bool no_fallback = false ); } diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index 379be22c0..e5d48c06f 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -4,10 +4,10 @@ #include #include -static int fontid = 0; // used by UI for change detection -double fontsize = 0; // pixel height of the current font -const matrix4x3 *textmatrix = NULL; // used for text particles -Shader *textshader = NULL; // used for text particles +static int fontid = 0; // used by UI for change detection +double fontsize = 0; // pixel height of the current font +const matrix4x3 *textmatrix = nullptr; // used for text particles +Shader *textshader = nullptr; // used for text particles // apply a black shadow or outline to console text to improve visibility VARFP(conshadow, 0, 255, 255, clearconsoletextures()); @@ -22,11 +22,11 @@ ICOMMAND(textcolor, "ii", (int *i, int *c), reloadfonts(); }); -static cairo_font_options_t *options = NULL; // global font options -static cairo_surface_t *dummy_surface = NULL; // used to measure text +static cairo_font_options_t *options = nullptr; // global font options +static cairo_surface_t *dummy_surface = nullptr; // used to measure text -static cairo_antialias_t antialias_ = CAIRO_ANTIALIAS_GRAY; -static cairo_hint_style_t hintstyle_= CAIRO_HINT_STYLE_DEFAULT; +static cairo_antialias_t antialias_ = CAIRO_ANTIALIAS_GRAY; +static cairo_hint_style_t hintstyle_ = CAIRO_HINT_STYLE_DEFAULT; VARFP(fontantialias, 0, 1, 1, { antialias_ = fontantialias ? CAIRO_ANTIALIAS_GRAY : CAIRO_ANTIALIAS_NONE; @@ -77,26 +77,29 @@ struct font { char *name; int id; - string features; // OpenType features + char *opentype_features; PangoFontDescription *desc; - font() : name(NULL), desc(NULL) { features[0] = '\0'; }; + font() : name(nullptr), opentype_features(nullptr), desc(nullptr) {} ~font() { - DELETEA(name); + delete[] name; + delete[] opentype_features; if(desc) pango_font_description_free(desc); } }; -static font *curfont = NULL, *lastfont = NULL; +static font *curfont = nullptr, *lastfont = nullptr; static hashnameset fonts; static vector fontstack; void newfont(const char *name, const char *family) { font *f = &fonts[name]; - if(!f->name) f->name = newstring(name); - if(!f->desc) f->desc = pango_font_description_from_string(family); + if(f->name) f->~font(); + f->name = newstring(name); f->id = fontid++; + f->opentype_features = newstring(""); + f->desc = pango_font_description_from_string(family); lastfont = f; } @@ -162,7 +165,12 @@ void fontsmallcaps(int *val) } COMMAND(fontsmallcaps, "i"); -void fontfeatures(char *features) { if(lastfont) copystring(lastfont->features, features, MAXSTRLEN); } +void fontfeatures(char *features) +{ + if(!lastfont) return; + if(lastfont->opentype_features) delete[] lastfont->opentype_features; + lastfont->opentype_features = newstring(features); +} COMMAND(fontfeatures, "s"); void fontvariations(char *variations) @@ -218,7 +226,7 @@ void done_pangocairo() } //stack[sp] is current color index -static inline bvec text_color(char c, char *stack, int size, int &sp, bvec color) +static inline bvec text_color(char c, char *stack, int size, int& sp, bvec color) { if(c=='s') // save color { @@ -254,13 +262,13 @@ static inline bvec text_color(char c, char *stack, int size, int &sp, bvec color // adds a string to the layout parsing basic markup (\f codes) // NOTE: `markup` is the original string with \f codes; `text` is the stripped version without \f codes -static void add_text_to_layout(const char *markup, int len, PangoLayout *layout, bvec color, int *map_markup_to_text, int *map_text_to_markup, const char *language, bool no_fallback) +static void add_text_to_layout(const char *markup, int len, PangoLayout *layout, bvec color, int *map_markup_to_text, int *map_text_to_markup, const char *lang, bool no_fallback) { char *text = newstring(len); bvec tcolor = color; char colorstack[10]; - colorstack[0] = 'c'; + colorstack[0] = '\0'; int cpos = 0; PangoAttrList *list = pango_attr_list_new(); @@ -268,9 +276,9 @@ static void add_text_to_layout(const char *markup, int len, PangoLayout *layout, PangoAttribute *m; // markup // OpenType features - if(curfont->features[0]) + if(curfont->opentype_features[0]) { - attr = pango_attr_font_features_new(curfont->features); // pango 1.38 + attr = pango_attr_font_features_new(curfont->opentype_features); // pango 1.38 pango_attr_list_insert(list, attr); } @@ -282,9 +290,9 @@ static void add_text_to_layout(const char *markup, int len, PangoLayout *layout, } // language - if(language) + if(lang) { - attr = pango_attr_language_new(pango_language_from_string(language)); + attr = pango_attr_language_new(pango_language_from_string(lang)); pango_attr_list_insert(list, attr); } @@ -314,7 +322,7 @@ static void add_text_to_layout(const char *markup, int len, PangoLayout *layout, attr->end_index = j + 1; pango_attr_list_insert(list, attr); - attr = NULL; + attr = nullptr; } } if(map_markup_to_text) map_markup_to_text[i] = j; @@ -371,10 +379,15 @@ static void add_text_to_layout(const char *markup, int len, PangoLayout *layout, } #undef MARKUP_CASE -static PangoLayout *measure_text_internal(const char *str, int len, int maxw, int align, int justify, bvec color, int &w, int &h, int &offset, int *map_markup_to_text, int *map_text_to_markup, const char *lang, bool no_fallback) +static PangoLayout *measure_text_internal(const char *str, int len, int maxw, int align, int justify, bvec color, int& w, int& h, int& offset, int *map_markup_to_text, int *map_text_to_markup, const char *lang, bool no_fallback) { // create cairo context cairo_t *cr = cairo_create(dummy_surface); + if(!cr) + { + w = h = offset = 0; + return nullptr; + } cairo_set_font_options(cr, options); // create layout @@ -383,7 +396,7 @@ static PangoLayout *measure_text_internal(const char *str, int len, int maxw, in { w = h = offset = 0; cairo_destroy(cr); - return NULL; + return nullptr; } // set font family and size @@ -404,7 +417,7 @@ static PangoLayout *measure_text_internal(const char *str, int len, int maxw, in // get pixel size PangoRectangle r; - pango_layout_get_extents(layout, NULL, &r); + pango_layout_get_extents(layout, nullptr, &r); w = r.width / PANGO_SCALE; h = r.height / PANGO_SCALE; offset = -r.x / PANGO_SCALE; @@ -420,37 +433,35 @@ namespace text Label::Label() : tex(0), layout(nullptr), map_markup_to_text(nullptr), map_text_to_markup(nullptr) {}; Label::~Label() { + if(tex != 0) glDeleteTextures(1, &tex); + if(layout != nullptr) g_object_unref(layout); delete[] map_markup_to_text; delete[] map_text_to_markup; - if(layout) g_object_unref(layout); - if(tex != 0) glDeleteTextures(1, &tex); } - Label::Label(Label&& other) noexcept : tex(other.tex), w(other.w), h(other.h), ox(other.ox), oy(other.oy), layout(other.layout), map_markup_to_text(other.map_markup_to_text), map_text_to_markup(other.map_text_to_markup) + Label::Label(Label&& other) noexcept : + w(other.w), h(other.h), ox(other.ox), oy(other.oy), + tex(other.tex), + layout(other.layout), + map_markup_to_text(other.map_markup_to_text), + map_text_to_markup(other.map_text_to_markup) { + other.tex = 0; + other.layout = nullptr; other.map_markup_to_text = nullptr; other.map_text_to_markup = nullptr; - other.layout = nullptr; - other.tex = 0; } Label& Label::operator=(Label&& other) noexcept { if(&other == this) return *this; - delete[] map_markup_to_text; - delete[] map_text_to_markup; - if(layout) g_object_unref(layout); - if(tex != 0) glDeleteTextures(1, &tex); - tex = other.tex; + this->~Label(); w = other.w; h = other.h; ox = other.ox; oy = other.oy; - layout = other.layout; - map_markup_to_text = other.map_markup_to_text; - map_text_to_markup = other.map_text_to_markup; - other.tex = 0; - other.layout = nullptr; - other.map_markup_to_text = nullptr; - other.map_text_to_markup = nullptr; + tex = exchange(other.tex, 0); + layout = exchange(other.layout, nullptr); + map_markup_to_text = exchange(other.map_markup_to_text, nullptr); + map_text_to_markup = exchange(other.map_text_to_markup, nullptr); return *this; } void Label::clear() @@ -465,20 +476,18 @@ namespace text g_object_unref(layout); layout = nullptr; } - delete[] map_markup_to_text; - delete[] map_text_to_markup; - map_markup_to_text = nullptr; - map_text_to_markup = nullptr; + DELETEA(map_markup_to_text); + DELETEA(map_text_to_markup); } - void Label::draw(double left, double top, int a, bool black) const + void Label::draw(double left, double top, int alpha, bool black) const { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); if(textshader) textshader->set(); // text particles else hudtextshader->set(); // UI text - gle::color(black ? bvec(0, 0, 0) : bvec(255*a/255.f, 255*a/255.f, 255*a/255.f), a); + gle::color(black ? bvec(0, 0, 0) : bvec(255*alpha/255.f, 255*alpha/255.f, 255*alpha/255.f), alpha); glBindTexture(GL_TEXTURE_RECTANGLE, tex); gle::defvertex(textmatrix ? 3 : 2); gle::deftexcoord0(); @@ -512,7 +521,6 @@ namespace text draw(left, top); } - // TODO: apply x/y offsets int Label::xy_to_index(float x, float y) const { const int px = (x - ox) * PANGO_SCALE, py = (y - oy) * PANGO_SCALE; @@ -525,14 +533,7 @@ namespace text return map_text_to_markup ? map_text_to_markup[ix] : ix; } - void measure(const char *str, int maxw, int &w, int &h, int align, int justify, const char *lang, bool no_fallback) - { - int _offset; - PangoLayout *layout = measure_text_internal(str, strlen(str), maxw, align, justify, bvec(0, 0, 0), w, h, _offset, NULL, NULL, lang, no_fallback); - if(layout) g_object_unref(layout); - } - - Label prepare(const char *str, int maxw, bvec color, int cursor, double outline, bvec4 ol_color, int align, int justify, const char *lang, bool no_fallback, bool keep_layout) + Label prepare(const char *str, int maxw, bvec color, int cursor, double outline, bvec4 ol_color, int align, int justify, const char *lang, bool no_fallback, bool keep_layout, bool reserve_cursor) { Label label; @@ -541,14 +542,17 @@ namespace text const int len = strlen(str); if(cursor >= 0 || keep_layout) label.map_markup_to_text = new int[len+1]; if(keep_layout) label.map_text_to_markup = new int[len+1]; - label.layout = measure_text_internal(str, len, maxw, align, justify, color, width, height, offset, label.map_markup_to_text ? label.map_markup_to_text : NULL, label.map_text_to_markup ? label.map_text_to_markup : NULL, lang, no_fallback); + label.layout = measure_text_internal(str, len, maxw, align, justify, color, width, height, offset, label.map_markup_to_text, label.map_text_to_markup, lang, no_fallback); if(!label.layout || !width || !height) { return label; } // create surface and cairo context - if(cursor >= 0) width += max(4., fontsize); // make space for the cursor + if(cursor >= 0 || reserve_cursor) // reserve space for the cursor + { + width += max(1., ceil(fontsize / 16.)); + } const int ol_offset = ceil(outline); width += 2 * ol_offset; height += 2 * ol_offset; @@ -573,7 +577,7 @@ namespace text if(cursor > len) cursor = len; cursor = min((int)strlen(pango_layout_get_text(label.layout)), label.map_markup_to_text[cursor]); PangoRectangle cur_rect; - pango_layout_get_cursor_pos(label.layout, cursor, &cur_rect, NULL); + pango_layout_get_cursor_pos(label.layout, cursor, &cur_rect, nullptr); const double curw = max(1., fontsize / 16.); cairo_rectangle(cr, cur_rect.x/PANGO_SCALE+ol_offset, cur_rect.y/PANGO_SCALE+ol_offset, curw, cur_rect.height/PANGO_SCALE); @@ -618,6 +622,13 @@ namespace text return label; } + void measure(const char *str, int maxw, int& w, int& h, int align, int justify, const char *lang, bool no_fallback) + { + int _offset; + PangoLayout *layout = measure_text_internal(str, strlen(str), maxw, align, justify, bvec(0, 0, 0), w, h, _offset, nullptr, nullptr, lang, no_fallback); + if(layout) g_object_unref(layout); + } + Label prepare_for_console(const char *str, int maxw, int cursor) { return prepare(str, maxw, bvec(255, 255, 255), cursor, conoutline ? ceil(FONTH / 32.) : 0, bvec4(0, 0, 0, conoutline)); @@ -633,7 +644,7 @@ namespace text key = crc32(key, (const Bytef *)(&c), sizeof(int)); key = crc32(key, (const Bytef *)(&ol_color.mask), sizeof(uint)); key = crc32(key, (const Bytef *)l, strlen(l)); - Label &label = particle_cache[key]; + Label& label = particle_cache[key]; if(label.valid()) return label; label = prepare(str, 0, color, -1, outline, ol_color, -1, 0, lang, no_fallback); if(particle_queue.length() >= 256) @@ -647,10 +658,10 @@ namespace text return label; } - void draw(const char *str, double left, double top, bvec color, int a, int maxw, int align, int justify, const char *lang, bool no_fallback) + void draw(const char *str, double left, double top, bvec color, int alpha, int maxw, int align, int justify, const char *lang, bool no_fallback) { - const Label label = prepare(str, maxw, color, -1, 0, bvec4(color, a), align, justify, lang, no_fallback); - if(label.valid()) label.draw(left, top, a); + const Label label = prepare(str, maxw, color, -1, 0, bvec4(color, alpha), align, justify, lang, no_fallback); + if(label.valid()) label.draw(left, top, alpha); } void draw_as_console(const char *str, double left, double top, int maxw, int cursor) @@ -667,7 +678,7 @@ namespace text } } - void getres(int &w, int &h) + void getres(int& w, int& h) { if(w < MINRESW || h < MINRESH) { @@ -691,7 +702,7 @@ namespace text const int len = strlen(str); if(!len) return 0; int *map_text_to_markup = new int[len+1]; - PangoLayout *layout = measure_text_internal(str, len, maxw, align, justify, bvec(0, 0, 0), width, height, _offset, NULL, map_text_to_markup, lang, no_fallback); + PangoLayout *layout = measure_text_internal(str, len, maxw, align, justify, bvec(0, 0, 0), width, height, _offset, nullptr, map_text_to_markup, lang, no_fallback); if(!layout) { delete[] map_text_to_markup; @@ -705,7 +716,7 @@ namespace text } int index; - const int res = pango_layout_xy_to_index(layout, hitx * PANGO_SCALE, hity * PANGO_SCALE, &index, NULL); + const int res = pango_layout_xy_to_index(layout, hitx * PANGO_SCALE, hity * PANGO_SCALE, &index, nullptr); g_object_unref(layout); if(!res) { @@ -718,7 +729,7 @@ namespace text } // used by the text editor - void pos(const char *str, int cursor, int &cx, int &cy, int maxw, int align, int justify, const char *lang, bool no_fallback) + void pos(const char *str, int cursor, int& cx, int& cy, int maxw, int align, int justify, const char *lang, bool no_fallback) { int width, height, _offset; const int len = strlen(str); @@ -728,7 +739,7 @@ namespace text return; } int *map_markup_to_text = new int[len+1]; - PangoLayout *layout = measure_text_internal(str, len, maxw, align, justify, bvec(0, 0, 0), width, height, _offset, map_markup_to_text, NULL, lang, no_fallback); + PangoLayout *layout = measure_text_internal(str, len, maxw, align, justify, bvec(0, 0, 0), width, height, _offset, map_markup_to_text, nullptr, lang, no_fallback); if(!layout) { cx = cy = 0; diff --git a/source/engine/ui.cpp b/source/engine/ui.cpp index 3747b4c41..645eea75a 100644 --- a/source/engine/ui.cpp +++ b/source/engine/ui.cpp @@ -2053,7 +2053,7 @@ namespace UI const char *gettype() const { return typestr(); } const char *val, *tmp; - Language() : val(NULL), tmp(NULL) {} + Language() : val(nullptr), tmp(nullptr) {} ~Language() { DELETEA(val); DELETEA(tmp); } void setup(const char *val_) { SETSTR(val, val_); } void layout() { SETSTR(tmp, curlanguage); SETSTR(curlanguage, val); Object::layout(); SETSTR(curlanguage, tmp); } @@ -2099,7 +2099,7 @@ namespace UI bool changed; uint crc; // string hash used for change detection - Text() : scale(0), wrap(0), color(0), lastchange(0), align(curwrapalign), shadow(curshadow), outlinealpha(curfontoutlinealpha), outline(curfontoutline), justify(curjustify), nofallback(curnofallback), language(NULL), cursor(-1), has_cursor(false), crc(0) {} + Text() : scale(0), wrap(0), color(0), lastchange(0), align(curwrapalign), shadow(curshadow), outlinealpha(curfontoutlinealpha), outline(curfontoutline), justify(curjustify), nofallback(curnofallback), language(nullptr), cursor(-1), has_cursor(false), crc(0) {} void setup(float scale_ = 1, const Color &color_ = Color(255, 255, 255), float wrap_ = -1, int cursor_ = -1, bool has_cursor_ = false) { @@ -2151,7 +2151,6 @@ namespace UI const float k = drawscale(); uicursorindex = label.xy_to_index(cx/k, cy/k); } - bool rawkey(int code, bool isdown) { if(Object::rawkey(code, isdown)) return true; @@ -2161,7 +2160,6 @@ namespace UI copystring(uikeycode, keyname); return code != -1; // propagate left clicks } - bool textinput(const char *str, int len) { if(Object::textinput(str, len)) return true; @@ -2183,8 +2181,6 @@ namespace UI changedraw(CHANGE_SHADER | CHANGE_COLOR); - setfontsize(scale * hudh); - const double textscale = drawscale(); const double x = round(sx/textscale), y = round(sy/textscale); pushhudscale(textscale); @@ -2229,7 +2225,7 @@ namespace UI if(!label.valid()) { - label = text::prepare(text, int(wrap/k), bvec(color.r, color.g, color.b), cursor, outline * FONTH / 16.f, bvec4(0, 0, 0, outlinealpha), align, justify, language, nofallback, /*keep_layout=*/has_cursor); + label = text::prepare(text, int(wrap/k), bvec(color.r, color.g, color.b), cursor, outline * FONTH / 16.f, bvec4(0, 0, 0, outlinealpha), align, justify, language, nofallback, /*keep_layout=*/has_cursor, /*reserve_cursor=*/has_cursor); } w = max(w, label.width()*k); h = max(h, label.height()*k); @@ -2300,7 +2296,7 @@ namespace UI { char *font; - Font() : font(NULL) {} + Font() : font(nullptr) {} ~Font() { delete[] font; } static const char* typestr() { return "#Font"; } diff --git a/source/shared/tools.h b/source/shared/tools.h index b62676442..2e4a5abd7 100644 --- a/source/shared/tools.h +++ b/source/shared/tools.h @@ -51,6 +51,13 @@ static inline void swap(T &a, T &b) a = b; b = t; } +template +static inline T exchange(T& a, U&& b) +{ + T old_value = (T&&)a; + a = b; // should be `a = std::forward(b)` or use `std::exchange` directly + return old_value; +} #ifdef max #undef max #endif From 75987affbc4d3ea87261fbc2147fce42fe8a8112 Mon Sep 17 00:00:00 2001 From: Jed- Date: Wed, 11 Dec 2024 17:57:49 +0100 Subject: [PATCH 49/68] Unicode cleanup --- source/shared/unicode.h | 76 ++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/source/shared/unicode.h b/source/shared/unicode.h index d46cd0b4b..5c2139bad 100644 --- a/source/shared/unicode.h +++ b/source/shared/unicode.h @@ -1,11 +1,12 @@ // UTF-8 support -#include "cube.h" +#include // reads the next character into `codepoint` and returns the number of bytes it takes up -static inline int uni_getchar(const char *str, uint &codepoint) +// `str` must be null-terminated +static inline int uni_getchar(const char *str, unsigned int& codepoint) { - const uchar *p = (const uchar *)str; + const unsigned char *p = (const unsigned char *)str; if(p[0] <= 0x7F) { codepoint = p[0]; @@ -37,7 +38,7 @@ static inline int uni_getchar(const char *str, uint &codepoint) static inline int uni_prevchar(const char *str, int i) { if(i <= 0) return 0; - const uchar *p = (const uchar *)str; + const unsigned char *p = (const unsigned char *)str; if(p[i-1] <= 0x7F) return 1; if(i >= 2 && p[i-2] >> 5 == 0x06 && p[i-1] >> 6 == 0x02) return 2; if(i >= 3 && p[i-3] >> 4 == 0x0E && p[i-2] >> 6 == 0x02 && p[i-1] >> 6 == 0x02) return 3; @@ -46,7 +47,7 @@ static inline int uni_prevchar(const char *str, int i) } // encodes a single codepoint -static inline int uni_code2str(uint codepoint, char *dst) +static inline int uni_code2str(unsigned int codepoint, char *dst) { if(codepoint <= 0x007F) { @@ -82,63 +83,60 @@ static inline int uni_code2str(uint codepoint, char *dst) return 0; } -static inline uint uni_strlen(const char *str) +static inline unsigned int uni_strlen(const char *str) { - uint c, len = 0; - const char *p = str; - while(*p) + unsigned int _c, len = 0; + while(*str) { - p += uni_getchar(p, c); + str += uni_getchar(str, _c); ++len; } return len; } // returns the codepoint of the character at index `ix` -static inline uint uni_charat(const char *str, int ix) +static inline unsigned int uni_charat(const char *str, int ix) { + unsigned int c; if(ix >= 0) { - const char *p = str; - uint c; - loopi(ix) + for(int i = 0; i < ix; ++i) { - if(!*p) return 0; - p += uni_getchar(p, c); + if(!*str) return 0; + str += uni_getchar(str, c); } - if(!*p) return 0; - uni_getchar(p, c); + if(!*str) return 0; + uni_getchar(str, c); return c; } - uint len = strlen(str), b = 0; - const char *p = str+len; - loopi(-ix) + const unsigned int len = strlen(str); + unsigned int b = 0; + for(int i = 0; i > ix; --i) { b += uni_prevchar(str, len-b); } - uint c; - uni_getchar(p-b, c); + uni_getchar(str+len-b, c); return c; } // offset of the character at index `ix` -static inline int uni_offset(const char *str, uint ix) +static inline int uni_offset(const char *str, unsigned int ix) { const char *p = str; - uint c; - loopi(ix) + unsigned int _c; + for(int i = 0; i < ix; ++i) { if(!*p) break; - p += uni_getchar(p, c); + p += uni_getchar(p, _c); } return (p - str); } // same but starting from the end of the string (str[-ix]) -static inline int uni_negoffset(const char *str, uint ix) +static inline int uni_negoffset(const char *str, unsigned int ix) { const char *p = str + strlen(str); - loopi(ix) + for(int i = 0; i < ix; ++i) { if(p <= str) break; p -= uni_prevchar(str, p - str); @@ -146,25 +144,25 @@ static inline int uni_negoffset(const char *str, uint ix) return (p - str); } -// returns the index of the unicode character at offset `i` -static inline int uni_index(const char *str, uint i) +// returns the index of the unicode character at offset `off` +static inline int uni_index(const char *str, unsigned int off) { const char *p = str, *end = str + strlen(str); - uint _c; + unsigned int _c; int ret = 0; - loopj(i) + while(ret < off) { - if(p >= end || p >= str + i) return ret; + if(p >= end || p >= str + off) return ret; p += uni_getchar(p, _c); - ret++; + ++ret; } return ret; } // only supports ASCII -static inline void uni_strlower(char *src, char *dst) +static inline void uni_strlower(const char *src, char *dst) { - for(uchar *p = (uchar *)src; *p; ++p) + for(const unsigned char *p = (unsigned char *)src; *p; ++p) { if(*p >= 'A' && *p <= 'Z') { @@ -174,9 +172,9 @@ static inline void uni_strlower(char *src, char *dst) ++dst; } } -static inline void uni_strupper(char *src, char *dst) +static inline void uni_strupper(const char *src, char *dst) { - for(uchar *p = (uchar *)src; *p; ++p) + for(const unsigned char *p = (unsigned char *)src; *p; ++p) { if(*p >= 'a' && *p <= 'z') { From 70c2c6f6208407d28df730b9a3b872f210021d7f Mon Sep 17 00:00:00 2001 From: Jed- Date: Mon, 16 Dec 2024 12:37:32 +0100 Subject: [PATCH 50/68] Pre-merge changes --- source/engine/ui.cpp | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/source/engine/ui.cpp b/source/engine/ui.cpp index 645eea75a..0c44705cf 100644 --- a/source/engine/ui.cpp +++ b/source/engine/ui.cpp @@ -2063,26 +2063,6 @@ namespace UI #undef WITHTEXTATTR - static int uicursorindex = -1; - ICOMMAND(uicursorindex, "", (), intret(uicursorindex)); - - // makes the text input cursor stop blinking for a short while, call this when setting the cursor position from cubescript - ICOMMAND(resetcursorblink, "", (), { inputmillis = totalmillis; }); - - string uikeycode, uitextinput; - ICOMMAND(uikeycode, "", (), result(uikeycode)); - ICOMMAND(uitextinput, "", (), result(uitextinput)); - ICOMMAND(uienabletextinput, "", (), - { - ::textinput(true, TI_GUI); - ::keyrepeat(true, KR_GUI); - }); - ICOMMAND(uidisabletextinput, "", (), - { - ::textinput(false, TI_GUI); - ::keyrepeat(false, KR_GUI); - }) - // NOTE: `scale` is the text height in screenfuls at `uiscale 1` struct Text : Object { From 1c02ea3d0f1984b45efbe0bccea824f2b5c20bae Mon Sep 17 00:00:00 2001 From: Jed- Date: Mon, 16 Dec 2024 13:00:31 +0100 Subject: [PATCH 51/68] Post-merge fixes --- source/engine/console.cpp | 4 ++-- source/engine/ui.cpp | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/source/engine/console.cpp b/source/engine/console.cpp index 2aa2abdfe..b7d204e4f 100644 --- a/source/engine/console.cpp +++ b/source/engine/console.cpp @@ -588,12 +588,12 @@ void execbind(keym &k, bool isdown) k.pressed = isdown; } -int iskeyheld(char *key) +void iskeyheld(char *key) { const keym *km = findbind(key); intret(km && km->pressed ? 1 : 0); } -ICOMMAND(iskeyheld, "s", (char* key), intret(iskeyheld(key))); +ICOMMAND(iskeyheld, "s", (char* key), iskeyheld(key)); ICOMMAND(iscmdlineopen, "", (), intret(commandmillis >= 0 ? 1 : 0)); bool consoleinput(const char *str, int len) diff --git a/source/engine/ui.cpp b/source/engine/ui.cpp index ffb1d4b4d..7f41ac10f 100644 --- a/source/engine/ui.cpp +++ b/source/engine/ui.cpp @@ -2066,6 +2066,9 @@ namespace UI static int uicursorindex = -1; ICOMMAND(uicursorindex, "", (), intret(uicursorindex)); + // makes the text input cursor stop blinking for a short while, call this when setting the cursor position from cubescript + ICOMMAND(resetcursorblink, "", (), { inputmillis = totalmillis; }); + string uikeycode, uitextinput; ICOMMAND(uikeycode, "", (), result(uikeycode)); ICOMMAND(uitextinput, "", (), result(uitextinput)); From 916bb7dd8af97a2ddb108c1188a747204e05fdcf Mon Sep 17 00:00:00 2001 From: Jed- Date: Mon, 16 Dec 2024 13:04:54 +0100 Subject: [PATCH 52/68] Unicode fixes --- source/shared/unicode.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/shared/unicode.h b/source/shared/unicode.h index 5c2139bad..eff836784 100644 --- a/source/shared/unicode.h +++ b/source/shared/unicode.h @@ -125,7 +125,7 @@ static inline int uni_offset(const char *str, unsigned int ix) { const char *p = str; unsigned int _c; - for(int i = 0; i < ix; ++i) + for(unsigned int i = 0; i < ix; ++i) { if(!*p) break; p += uni_getchar(p, _c); @@ -136,7 +136,7 @@ static inline int uni_offset(const char *str, unsigned int ix) static inline int uni_negoffset(const char *str, unsigned int ix) { const char *p = str + strlen(str); - for(int i = 0; i < ix; ++i) + for(unsigned int i = 0; i < ix; ++i) { if(p <= str) break; p -= uni_prevchar(str, p - str); @@ -149,7 +149,7 @@ static inline int uni_index(const char *str, unsigned int off) { const char *p = str, *end = str + strlen(str); unsigned int _c; - int ret = 0; + unsigned int ret = 0; while(ret < off) { if(p >= end || p >= str + off) return ret; From a47b38776ec69b21d0defa6346178b0f90ec717d Mon Sep 17 00:00:00 2001 From: Jed- Date: Sun, 19 Jan 2025 15:12:45 +0100 Subject: [PATCH 53/68] Post-merge fixes --- source/Makefile | 67 +++++++++++++++++++++------------------ source/engine/console.cpp | 5 +-- 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/source/Makefile b/source/Makefile index f83b919b0..dbf5eaad0 100644 --- a/source/Makefile +++ b/source/Makefile @@ -289,16 +289,18 @@ engine/client.o: shared/ents.h shared/command.h shared/glexts.h engine/client.o: shared/glemu.h engine/sound.h shared/iengine.h engine/client.o: shared/igame.h engine/world.h engine/octa.h engine/light.h engine/client.o: engine/texture.h engine/bih.h engine/model.h -engine/command.o: shared/unicode.h shared/cube.h shared/tools.h shared/geom.h -engine/command.o: shared/ents.h shared/command.h shared/glexts.h -engine/command.o: shared/glemu.h engine/sound.h shared/iengine.h -engine/command.o: shared/igame.h engine/engine.h engine/world.h engine/octa.h -engine/command.o: engine/light.h engine/texture.h engine/bih.h engine/model.h -engine/console.o: shared/unicode.h shared/cube.h shared/tools.h shared/geom.h -engine/console.o: shared/ents.h shared/command.h shared/glexts.h -engine/console.o: shared/glemu.h engine/sound.h shared/iengine.h -engine/console.o: shared/igame.h engine/engine.h engine/world.h engine/octa.h -engine/console.o: engine/light.h engine/texture.h engine/bih.h engine/model.h +engine/command.o: shared/unicode.h engine/engine.h shared/cube.h +engine/command.o: shared/tools.h shared/geom.h shared/ents.h shared/command.h +engine/command.o: shared/glexts.h shared/glemu.h engine/sound.h +engine/command.o: shared/iengine.h shared/igame.h engine/world.h +engine/command.o: engine/octa.h engine/light.h engine/texture.h engine/bih.h +engine/command.o: engine/model.h +engine/console.o: shared/unicode.h engine/engine.h shared/cube.h +engine/console.o: shared/tools.h shared/geom.h shared/ents.h shared/command.h +engine/console.o: shared/glexts.h shared/glemu.h engine/sound.h +engine/console.o: shared/iengine.h shared/igame.h engine/world.h +engine/console.o: engine/octa.h engine/light.h engine/texture.h engine/bih.h +engine/console.o: engine/model.h engine/dynlight.o: engine/engine.h shared/cube.h shared/tools.h shared/geom.h engine/dynlight.o: shared/ents.h shared/command.h shared/glexts.h engine/dynlight.o: shared/glemu.h engine/sound.h shared/iengine.h @@ -522,27 +524,32 @@ game/gamephysics.o: shared/glemu.h engine/sound.h shared/iengine.h game/gamephysics.o: shared/igame.h shared/unicode.h game/projectile.h game/gamephysics.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h game/gamephysics.o: game/monster.h -game/hud.o: game/game.h shared/cube.h shared/tools.h shared/geom.h +game/hud.o: engine/engine.h shared/cube.h shared/tools.h shared/geom.h game/hud.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h -game/hud.o: engine/sound.h shared/iengine.h shared/igame.h shared/unicode.h -game/hud.o: game/projectile.h game/weapon.h game/ai.h game/gamemode.h -game/hud.o: game/entity.h game/monster.h engine/engine.h engine/world.h +game/hud.o: engine/sound.h shared/iengine.h shared/igame.h engine/world.h game/hud.o: engine/octa.h engine/light.h engine/texture.h engine/bih.h -game/hud.o: engine/model.h +game/hud.o: engine/model.h game/game.h shared/unicode.h game/projectile.h +game/hud.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h +game/hud.o: game/monster.h game/projectile.o: game/game.h shared/cube.h shared/tools.h shared/geom.h -game/projectile.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h -game/projectile.o: engine/sound.h shared/iengine.h shared/igame.h game/weapon.h game/projectile.h -game/projectile.o: game/ai.h game/gamemode.h game/entity.h game/monster.h +game/projectile.o: shared/ents.h shared/command.h shared/glexts.h +game/projectile.o: shared/glemu.h engine/sound.h shared/iengine.h +game/projectile.o: shared/igame.h shared/unicode.h game/projectile.h +game/projectile.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h +game/projectile.o: game/monster.h game/announcer.o: game/game.h shared/cube.h shared/tools.h shared/geom.h -game/announcer.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h -game/announcer.o: engine/sound.h shared/iengine.h shared/igame.h game/weapon.h game/projectile.h -game/announcer.o: game/ai.h game/gamemode.h game/entity.h game/monster.h -game/camera.o: game/game.h shared/cube.h shared/tools.h shared/geom.h +game/announcer.o: shared/ents.h shared/command.h shared/glexts.h +game/announcer.o: shared/glemu.h engine/sound.h shared/iengine.h +game/announcer.o: shared/igame.h shared/unicode.h game/projectile.h +game/announcer.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h +game/announcer.o: game/monster.h +game/camera.o: engine/engine.h shared/cube.h shared/tools.h shared/geom.h game/camera.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h -game/camera.o: engine/sound.h shared/iengine.h shared/igame.h game/weapon.h game/projectile.h -game/camera.o: game/ai.h game/gamemode.h game/entity.h game/monster.h -game/camera.o: engine/engine.h engine/world.h engine/octa.h engine/light.h -game/camera.o: engine/texture.h engine/bih.h engine/model.h +game/camera.o: engine/sound.h shared/iengine.h shared/igame.h engine/world.h +game/camera.o: engine/octa.h engine/light.h engine/texture.h engine/bih.h +game/camera.o: engine/model.h game/game.h shared/unicode.h game/projectile.h +game/camera.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h +game/camera.o: game/monster.h shared/cube.h.gch: shared/tools.h shared/geom.h shared/ents.h shared/cube.h.gch: shared/command.h shared/glexts.h shared/glemu.h @@ -567,10 +574,10 @@ standalone/shared/stream.o: shared/iengine.h shared/igame.h standalone/shared/tools.o: shared/cube.h shared/tools.h shared/geom.h standalone/shared/tools.o: shared/ents.h shared/command.h engine/sound.h standalone/shared/tools.o: shared/iengine.h shared/igame.h shared/unicode.h -standalone/engine/command.o: shared/unicode.h shared/cube.h shared/tools.h -standalone/engine/command.o: shared/geom.h shared/ents.h shared/command.h -standalone/engine/command.o: engine/sound.h shared/iengine.h shared/igame.h -standalone/engine/command.o: engine/engine.h engine/world.h +standalone/engine/command.o: shared/unicode.h engine/engine.h shared/cube.h +standalone/engine/command.o: shared/tools.h shared/geom.h shared/ents.h +standalone/engine/command.o: shared/command.h engine/sound.h shared/iengine.h +standalone/engine/command.o: shared/igame.h engine/world.h standalone/engine/server.o: engine/engine.h shared/cube.h shared/tools.h standalone/engine/server.o: shared/geom.h shared/ents.h shared/command.h standalone/engine/server.o: engine/sound.h shared/iengine.h shared/igame.h diff --git a/source/engine/console.cpp b/source/engine/console.cpp index d2269ea27..0c0c00fb7 100644 --- a/source/engine/console.cpp +++ b/source/engine/console.cpp @@ -635,10 +635,7 @@ void getclipboard() result(""); return; } - string paste; - size_t decoded = decodeutf8((uchar *)paste, sizeof(paste)-1, (const uchar *)cb, strlen(cb)); - paste[decoded] = '\0'; - result(paste); + result(cb); SDL_free(cb); } COMMAND(getclipboard, ""); From 2c866f606e908a5f74dcb7a1dd88af61269f169d Mon Sep 17 00:00:00 2001 From: Jed- Date: Sun, 19 Jan 2025 15:24:45 +0100 Subject: [PATCH 54/68] Don't draw an outline around the text cursor To avoid overlapping semitransparent elements --- source/engine/rendertext.cpp | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index e5d48c06f..47a7ebb8a 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -571,30 +571,25 @@ namespace text cairo_stroke(cr); } - // draw cursor outline and the cursor itself on top of it - if(cursor >= 0 && ((totalmillis - inputmillis <= cursorblink) || !cursorblink || ((totalmillis - inputmillis) % (2*cursorblink)) <= cursorblink)) + // draw text + cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0); + cairo_move_to(cr, offset + ol_offset, ol_offset); + pango_cairo_show_layout(cr, label.layout); + + // draw the cursor + if(cursor >= 0 && (!cursorblink || (totalmillis - inputmillis <= cursorblink) || (totalmillis - inputmillis % (2 * cursorblink)) <= cursorblink)) { if(cursor > len) cursor = len; cursor = min((int)strlen(pango_layout_get_text(label.layout)), label.map_markup_to_text[cursor]); - PangoRectangle cur_rect; - pango_layout_get_cursor_pos(label.layout, cursor, &cur_rect, nullptr); + PangoRectangle cursor_rect; + pango_layout_get_cursor_pos(label.layout, cursor, &cursor_rect, nullptr); - const double curw = max(1., fontsize / 16.); - cairo_rectangle(cr, cur_rect.x/PANGO_SCALE+ol_offset, cur_rect.y/PANGO_SCALE+ol_offset, curw, cur_rect.height/PANGO_SCALE); - if(outline) - { - cairo_set_source_rgba(cr, ol_color.r/255., ol_color.g/255., ol_color.b/255., ol_color.a/255.); - cairo_stroke_preserve(cr); - } - cairo_set_source_rgba(cr, cursorcolor.r/255., cursorcolor.g/255., cursorcolor.b/255., 1.); + const double cursor_width = max(1., fontsize / 16.); + cairo_rectangle(cr, cursor_rect.x / PANGO_SCALE + ol_offset, cursor_rect.y / PANGO_SCALE + ol_offset, cursor_width, cursor_rect.height / PANGO_SCALE); + cairo_set_source_rgba(cr, cursorcolor.r / 255., cursorcolor.g / 255., cursorcolor.b / 255., 1.); cairo_fill(cr); } - // draw text on top of everything - cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0); - cairo_move_to(cr, offset + ol_offset, ol_offset); - pango_cairo_show_layout(cr, label.layout); - if(!keep_layout) { g_object_unref(label.layout); From 03e15107a794c34c2e21ee326f7acefd8fcf2b1c Mon Sep 17 00:00:00 2001 From: Jed- Date: Sun, 19 Jan 2025 17:46:23 +0100 Subject: [PATCH 55/68] Rename variables, document function parameters --- source/engine/engine.h | 107 ++++++---- source/engine/rendertext.cpp | 386 +++++++++++++++++++++++------------ source/engine/ui.cpp | 2 +- 3 files changed, 323 insertions(+), 172 deletions(-) diff --git a/source/engine/engine.h b/source/engine/engine.h index 99e824b10..cd602cb6c 100644 --- a/source/engine/engine.h +++ b/source/engine/engine.h @@ -51,7 +51,7 @@ extern double fontsize; extern bool init_pangocairo(); extern void done_pangocairo(); -extern int getcurfontid(); +extern int getcurrentfontid(); extern bool setfont(const char *name); extern void pushfont(); extern bool popfont(); @@ -82,65 +82,89 @@ namespace text bool valid() const { return tex != 0; } int width() const { return w; } int height() const { return h; } + + // empties the text content of the label void clear(); - void draw(double left, double top, - int alpha = 255, - bool black = false + // draws the label to the screen + void draw( + double left, // screen X coordinate + double top, // screen Y coordinate + int alpha = 255, // text opacity (0-255) + bool black = false // make it black? (used for shadows) ) const; + // like `draw()` but with a shadow void draw_as_console(double left, double top) const; - - // do not call if the label was not prepared with `keep_layout=true` + + // converts (x,y) pixel coordinates to a character (byte) index + // NOTE: do not call if the label was not prepared with `keep_layout=true` int xy_to_index(float x, float y) const; - friend Label prepare(const char *, int, bvec, int, double, bvec4, int, int, const char *, bool, bool, bool); + friend Label prepare(const char *, int, const bvec&, int, double, const bvec4&, int, int, const char *, bool, bool, bool); }; - // measure text before creating the label - void measure(const char *str, int maxw, int &w, int &h, - int align = -1, - int justify = 0, - const char *lang = nullptr, - bool no_fallback = false + // creates a label from a string + Label prepare( + const char *str, // the label text + int max_width, // maximum width in pixels + const bvec& color = bvec(255, 255, 255), // initial color of the text + int cursor = -1, // byte index of the cursor (disabled if <0) + double outline = 0, // outline thickness + const bvec4& outline_color = bvec4(0, 0, 0, 0), // outline color (RGBA) + int align = -1, // text alignment: -1 = left, 0 = center, 1 = right + int justify = 0, // text justification (0 = disable, enable otherwise) + const char *lang = nullptr, // optional language code of the text (can affect text shaping) + bool no_fallback = false, // disable fallback fonts for unavailable glyphs? + bool keep_layout = false, // keep layout in memory? (necessary if you need to call `xy_to_index()` on the label) + bool reserve_cursor = false // reserve space to the right for the cursor? ); - Label prepare(const char *str, int maxw, - bvec color = bvec(255, 255, 255), - int cursor = -1, - double outline = 0, - bvec4 ol_color = bvec4(0, 0, 0, 0), - int align = -1, - int justify = 0, - const char *lang = nullptr, // language code, used for text shaping - bool no_fallback = false, // don't use fallback fonts for unavailable glyphs - bool keep_layout = false, // use only if you need to call `xy_to_index()` - bool reserve_cursor = false // reserve space for the cursor, even if the cursor is absent - ); - Label prepare_for_console(const char *str, int maxw, int cursor); + // same as `prepare()` but with appropriate settings for console text + Label prepare_for_console(const char *str, int max_width, int cursor); + + // same as `prepare()` but use a cache so that the same label doesn't have to be recreated every time const Label& prepare_for_particle(const char *str, - bvec color = bvec(255, 255, 255), + const bvec& color = bvec(255, 255, 255), double outline = 0, - bvec4 ol_color = bvec4(0, 0, 0, 0), + const bvec4& outline_color = bvec4(0, 0, 0, 0), const char *lang = nullptr, bool no_fallback = false ); - void draw(const char *str, double left, double top, - bvec color = bvec(255, 255, 255), - int alpha = 255, - int maxw = 0, - int align = -1, - int justify = 0, - const char *lang = nullptr, - bool no_fallback = false + // measure text before creating the label + void measure( + const char *str, // the string to measure + int max_width, // maximum width in pixels + int& width, // output: the measured width + int& height, // output: the measured height + int align = -1, // text alignment: -1 = left, 0 = center, 1 = right + int justify = 0, // text justification (0 = disable, enable otherwise) + const char *lang = nullptr, // optional language code of the text (can affect text shaping) + bool no_fallback = false // disable fallback fonts for unavailable glyphs? + ); + + // draw a string directly to the screen + void draw( + const char *str, // the string to draw + double left, // screen X coordinate + double top, // screen Y coordinate + const bvec& color = bvec(255, 255, 255), // initial color of the text + int alpha = 255, // text opacity (0-255) + int max_width = 0, // maximum width in pixels + int align = -1, // text alignment: -1 = left, 0 = center, 1 = right + int justify = 0, // text justification (0 = disable, enable otherwise) + const char *lang = nullptr, // optional language code of the text (can affect text shaping) + bool no_fallback = false // disable fallback fonts for unavailable glyphs? ); static inline void draw_fmt(const char *fstr, double left, double top, ...) { defvformatstring(str, top, fstr); draw(str, left, top); } + + // same as `draw()` but with appropriate settings for console text void draw_as_console(const char *str, double left, double top, - int maxw = 0, + int max_width = 0, int cursor = -1 ); static inline void draw_as_console_fmt(const char *fstr, double left, double top, ...) @@ -149,14 +173,17 @@ namespace text draw_as_console(str, left, top); } - void getres(int &w, int &h); - int visible(const char *str, float hitx, float hity, int maxw, + void getres(int& w, int& h); + + // used by the text editor + int visible(const char *str, float hitx, float hity, int max_width, int align = -1, int justify = 0, const char *lang = nullptr, bool no_fallback = false ); - void pos(const char *str, int cursor, int &cx, int &cy, int maxw, + // used by the text editor + void pos(const char *str, int cursor, int& cx, int& cy, int max_width, int align = -1, int justify = 0, const char *lang = nullptr, diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index 47a7ebb8a..e910cd239 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -9,28 +9,42 @@ double fontsize = 0; // pixel height of the current font const matrix4x3 *textmatrix = nullptr; // used for text particles Shader *textshader = nullptr; // used for text particles +#pragma region global_font_settings // apply a black shadow or outline to console text to improve visibility -VARFP(conshadow, 0, 255, 255, clearconsoletextures()); -VARFP(conoutline, 0, 0, 255, clearconsoletextures()); +VARFP(conshadow, 0, 255, 255, clearconsoletextures()); +VARFP(conoutline, 0, 0, 255, clearconsoletextures()); -// text colors +// how often the cursor blinks, in milliseconds; set to zero to disable blinking +VARFP(cursorblink, 0, 750, 2000, +{ + cursorblink = cursorblink ? max(250, cursorblink) : 0; +}); + +CVARP(cursorcolor, 0xFFFFFF); + +// configurable text colors static bvec palette[10]; ICOMMAND(textcolor, "ii", (int *i, int *c), { - if(*i >= 0 && *i <= 9) palette[*i] = bvec::hexcolor(*c); - clearconsoletextures(); - reloadfonts(); + if(*i >= 0 && *i <= 9) + { + palette[*i] = bvec::hexcolor(*c); + clearconsoletextures(); + reloadfonts(); + } }); -static cairo_font_options_t *options = nullptr; // global font options -static cairo_surface_t *dummy_surface = nullptr; // used to measure text +static cairo_font_options_t *global_font_options = nullptr; // global font options +static cairo_surface_t *dummy_surface = nullptr; // used to measure text +// NOTE: subpixel antialiasing is not available because we need to render on transparent backgrounds static cairo_antialias_t antialias_ = CAIRO_ANTIALIAS_GRAY; static cairo_hint_style_t hintstyle_ = CAIRO_HINT_STYLE_DEFAULT; VARFP(fontantialias, 0, 1, 1, { antialias_ = fontantialias ? CAIRO_ANTIALIAS_GRAY : CAIRO_ANTIALIAS_NONE; - if(options) cairo_font_options_set_antialias(options, antialias_); + if(!global_font_options) return; + cairo_font_options_set_antialias(global_font_options, antialias_); clearconsoletextures(); reloadfonts(); }); @@ -44,14 +58,14 @@ VARFP(fonthinting, -1, -1, 3, case 3: hintstyle_ = CAIRO_HINT_STYLE_FULL ; break; default: hintstyle_ = CAIRO_HINT_STYLE_DEFAULT; } - if(options) cairo_font_options_set_hint_style(options, hintstyle_); + if(!global_font_options) return; + cairo_font_options_set_hint_style(global_font_options, hintstyle_); clearconsoletextures(); reloadfonts(); }) +#pragma endregion global_font_settings -VARFP(cursorblink, 0, 750, 2000, { cursorblink = cursorblink ? max(250, cursorblink) : 0; }); -CVARP(cursorcolor, 0xFFFFFF); - +#pragma region particles // a cache for rendered text particles static hashtable particle_cache; static vector particle_queue; @@ -61,7 +75,9 @@ static void clear_text_particles() particle_cache.clear(); particle_queue.setsize(0); } +#pragma endregion particles +#pragma region font_management // register a TTF file // NOTE: on Windows and MacOs, this assumes that pango was compiled with fontconfig support, // and that the `PANGOCAIRO_BACKEND` env var is set to `fc` or `fontconfig` @@ -73,29 +89,29 @@ void addfontfile(const char *filename) } COMMANDN(registerfont, addfontfile, "s"); -struct font +struct Font { char *name; int id; char *opentype_features; PangoFontDescription *desc; - font() : name(nullptr), opentype_features(nullptr), desc(nullptr) {} - ~font() + Font() : name(nullptr), opentype_features(nullptr), desc(nullptr) {} + ~Font() { delete[] name; delete[] opentype_features; if(desc) pango_font_description_free(desc); } }; -static font *curfont = nullptr, *lastfont = nullptr; -static hashnameset fonts; -static vector fontstack; +static Font *curfont = nullptr, *lastfont = nullptr; +static hashnameset fonts; +static vector fontstack; void newfont(const char *name, const char *family) { - font *f = &fonts[name]; - if(f->name) f->~font(); + Font *f = &fonts[name]; + if(f->name) f->~Font(); f->name = newstring(name); f->id = fontid++; f->opentype_features = newstring(""); @@ -180,7 +196,8 @@ void fontvariations(char *variations) } COMMAND(fontvariations, "s"); -static inline bool setfont(font *f) +// set the current font +static inline bool setfont(Font *f) { if(!f) return false; curfont = f; @@ -188,29 +205,52 @@ static inline bool setfont(font *f) } bool setfont(const char *name) { - font *f = fonts.access(name); + Font *f = fonts.access(name); return setfont(f); } -void pushfont() { fontstack.add(curfont); } + +// font stack management +void pushfont() +{ + fontstack.add(curfont); +} bool popfont() { if(fontstack.empty()) return false; curfont = fontstack.pop(); return true; } -int getcurfontid() { return curfont->id; } +int getcurrentfontid() +{ + return curfont->id; +} + +void reloadfonts() +{ + clear_text_particles(); + UI::clearlabels(); +} +#pragma endregion font_management + +#pragma region pango_initialization bool init_pangocairo() { - options = cairo_font_options_create(); - if(!options) return false; - cairo_font_options_set_antialias(options, antialias_); - cairo_font_options_set_hint_style(options, hintstyle_); - cairo_font_options_set_hint_metrics(options, CAIRO_HINT_METRICS_ON); - cairo_font_options_set_color_mode(options, CAIRO_COLOR_MODE_COLOR); // cairo 1.18 + global_font_options = cairo_font_options_create(); + if(CAIRO_STATUS_SUCCESS != cairo_font_options_status(global_font_options)) + { + return false; + } + cairo_font_options_set_antialias (global_font_options, antialias_); + cairo_font_options_set_hint_style (global_font_options, hintstyle_); + cairo_font_options_set_hint_metrics(global_font_options, CAIRO_HINT_METRICS_ON); + cairo_font_options_set_color_mode (global_font_options, CAIRO_COLOR_MODE_COLOR); // cairo 1.18 dummy_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0); - if(!dummy_surface) return false; + if(CAIRO_STATUS_SUCCESS != cairo_surface_status(dummy_surface)) + { + return false; + } loopi(10) palette[i] = bvec(255, 255, 255); @@ -221,10 +261,12 @@ bool init_pangocairo() void done_pangocairo() { fonts.clear(); - if(options) cairo_font_options_destroy(options); + if(global_font_options) cairo_font_options_destroy(global_font_options); if(dummy_surface) cairo_surface_destroy(dummy_surface); } +#pragma endregion pango_initialization +#pragma region markup_and_layout //stack[sp] is current color index static inline bvec text_color(char c, char *stack, int size, int& sp, bvec color) { @@ -262,9 +304,18 @@ static inline bvec text_color(char c, char *stack, int size, int& sp, bvec color // adds a string to the layout parsing basic markup (\f codes) // NOTE: `markup` is the original string with \f codes; `text` is the stripped version without \f codes -static void add_text_to_layout(const char *markup, int len, PangoLayout *layout, bvec color, int *map_markup_to_text, int *map_text_to_markup, const char *lang, bool no_fallback) +static void add_text_to_layout( + const char *markup, // the input string with \f codes + int markup_length, // length of the above + PangoLayout *layout, // the layout to fill + const bvec& color, // initial color of the text + int *map_markup_to_text, // output: maps character indices from markup to text + int *map_text_to_markup, // output: reverse of the above + const char *lang, // optional language code of the text (can affect text shaping) + bool no_fallback // disable fallback fonts for unavailable glyphs? +) { - char *text = newstring(len); + char *text = newstring(markup_length); bvec tcolor = color; char colorstack[10]; @@ -273,7 +324,7 @@ static void add_text_to_layout(const char *markup, int len, PangoLayout *layout, PangoAttrList *list = pango_attr_list_new(); PangoAttribute *attr; // colors and global features - PangoAttribute *m; // markup + PangoAttribute *m; // used for markup attributes // OpenType features if(curfont->opentype_features[0]) @@ -281,14 +332,12 @@ static void add_text_to_layout(const char *markup, int len, PangoLayout *layout, attr = pango_attr_font_features_new(curfont->opentype_features); // pango 1.38 pango_attr_list_insert(list, attr); } - // no fallback to system fonts if(no_fallback) { attr = pango_attr_fallback_new(FALSE); pango_attr_list_insert(list, attr); } - // language if(lang) { @@ -296,18 +345,18 @@ static void add_text_to_layout(const char *markup, int len, PangoLayout *layout, pango_attr_list_insert(list, attr); } - // arguments are 16-bit values so colors must be multiplied by 257 + // initial color: arguments must be converted to 16-bit values attr = pango_attr_foreground_new(tcolor.r * 257, tcolor.g * 257, tcolor.b * 257); - if(attr) attr->start_index = 0; + attr->start_index = 0; int begin_bold = -1, begin_italic = -1, begin_underline = -1, begin_strikethrough = -1; - int n_bold = 0, n_italic = 0, n_underline = 0, n_strikethrough = 0; + int n_bold = 0, n_italic = 0, n_underline = 0, n_strikethrough = 0; // counters used for nesting // parse markup int i = 0, j = 0; - for(; i < len; ++i) + for(; i < markup_length; ++i) { - if(markup[i] == '\f' && i < (len - 1)) + if(markup[i] == '\f' && i < (markup_length - 1)) { switch(markup[i+1]) { @@ -332,7 +381,7 @@ static void add_text_to_layout(const char *markup, int len, PangoLayout *layout, if(!attr) { attr = pango_attr_foreground_new(tcolor.r * 257, tcolor.g * 257, tcolor.b * 257); - if(attr) attr->start_index = j; + attr->start_index = j; } if(map_markup_to_text) map_markup_to_text[i] = j; if(map_text_to_markup) map_text_to_markup[j] = i; @@ -342,7 +391,7 @@ static void add_text_to_layout(const char *markup, int len, PangoLayout *layout, if(map_markup_to_text) map_markup_to_text[i] = j; if(map_text_to_markup) map_text_to_markup[j] = i; - if(attr) + if(attr) // color { attr->end_index = j; pango_attr_list_insert(list, attr); @@ -379,34 +428,38 @@ static void add_text_to_layout(const char *markup, int len, PangoLayout *layout, } #undef MARKUP_CASE -static PangoLayout *measure_text_internal(const char *str, int len, int maxw, int align, int justify, bvec color, int& w, int& h, int& offset, int *map_markup_to_text, int *map_text_to_markup, const char *lang, bool no_fallback) +// creates a layout and fills it with text, and measures its width, height and horizontal offset +static PangoLayout *measure_text_internal( + const char *str, // the string to measure + int len, // length of the string + int max_width, // maximum width in pixels (no limit if <=0) + int align, // text alignment: -1 = left, 0 = center, 1 = right + int justify, // text justification (0 = disable, enable otherwise) + const bvec& color, // initial color of the text + int& width, // output: the measured width + int& height, // output: the measured height + int& offset, // output: the measured offset + int *map_markup_to_text, // output: maps character indices from markup to text + int *map_text_to_markup, // output: reverse of the above + const char *lang, // optional language code of the text (can affect text shaping) + bool no_fallback // disable fallback fonts for unavailable glyphs? +) { // create cairo context - cairo_t *cr = cairo_create(dummy_surface); - if(!cr) - { - w = h = offset = 0; - return nullptr; - } - cairo_set_font_options(cr, options); + cairo_t *cairo_context = cairo_create(dummy_surface); + cairo_set_font_options(cairo_context, global_font_options); // create layout - PangoLayout *layout = pango_cairo_create_layout(cr); - if(!layout) - { - w = h = offset = 0; - cairo_destroy(cr); - return nullptr; - } + PangoLayout *layout = pango_cairo_create_layout(cairo_context); // set font family and size pango_font_description_set_absolute_size(curfont->desc, fontsize * PANGO_SCALE); // pango 1.8 pango_layout_set_font_description(layout, curfont->desc); // line wrapping: set maximum width, alignment and justification - if(maxw > 0) + if(max_width > 0) { - pango_layout_set_width(layout, maxw * PANGO_SCALE); + pango_layout_set_width(layout, max_width * PANGO_SCALE); pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); pango_layout_set_alignment(layout, align > 0 ? PANGO_ALIGN_RIGHT : align < 0 ? PANGO_ALIGN_LEFT : PANGO_ALIGN_CENTER); pango_layout_set_justify(layout, justify ? TRUE : FALSE); @@ -418,19 +471,24 @@ static PangoLayout *measure_text_internal(const char *str, int len, int maxw, in // get pixel size PangoRectangle r; pango_layout_get_extents(layout, nullptr, &r); - w = r.width / PANGO_SCALE; - h = r.height / PANGO_SCALE; + width = r.width / PANGO_SCALE; + height = r.height / PANGO_SCALE; offset = -r.x / PANGO_SCALE; - cairo_destroy(cr); + cairo_destroy(cairo_context); return layout; } +#pragma endregion markup_and_layout -void reloadfonts() { clear_text_particles(); UI::clearlabels(); } - +#pragma region label namespace text { - Label::Label() : tex(0), layout(nullptr), map_markup_to_text(nullptr), map_text_to_markup(nullptr) {}; + Label::Label() : + tex(0), + layout(nullptr), + map_markup_to_text(nullptr), + map_text_to_markup(nullptr) + {}; Label::~Label() { if(tex != 0) glDeleteTextures(1, &tex); @@ -464,6 +522,7 @@ namespace text map_text_to_markup = exchange(other.map_text_to_markup, nullptr); return *this; } + // empties the text content of the label void Label::clear() { if(tex != 0) @@ -476,11 +535,18 @@ namespace text g_object_unref(layout); layout = nullptr; } - DELETEA(map_markup_to_text); - DELETEA(map_text_to_markup); + delete[] map_markup_to_text; + delete[] map_text_to_markup; + map_markup_to_text = map_text_to_markup = nullptr; } - void Label::draw(double left, double top, int alpha, bool black) const + // draws the label to the screen + void Label::draw( + double left, // screen X coordinate + double top, // screen Y coordinate + int alpha, // text opacity (0-255) + bool black // make it black? (used for shadows) + ) const { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -510,7 +576,7 @@ namespace text } gle::end(); } - + // like `draw()` but with a shadow void Label::draw_as_console(double left, double top) const { if(conshadow) @@ -521,6 +587,8 @@ namespace text draw(left, top); } + // converts (x,y) pixel coordinates to a character (byte) index + // NOTE: do not call if the label was not prepared with `keep_layout=true` int Label::xy_to_index(float x, float y) const { const int px = (x - ox) * PANGO_SCALE, py = (y - oy) * PANGO_SCALE; @@ -533,7 +601,21 @@ namespace text return map_text_to_markup ? map_text_to_markup[ix] : ix; } - Label prepare(const char *str, int maxw, bvec color, int cursor, double outline, bvec4 ol_color, int align, int justify, const char *lang, bool no_fallback, bool keep_layout, bool reserve_cursor) + // creates a label from a string + Label prepare( + const char *str, // the label text + int max_width, // maximum width in pixels + const bvec& color, // initial color of the text + int cursor, // byte index of the cursor (disabled if <0) + double outline, // outline thickness + const bvec4& outline_color, // outline color (RGBA) + int align, // text alignment: -1 = left, 0 = center, 1 = right + int justify, // text justification (0 = disable, enable otherwise) + const char *lang, // optional language code of the text (can affect text shaping) + bool no_fallback, // disable fallback fonts for unavailable glyphs? + bool keep_layout, // keep layout in memory? (necessary if you need to call `xy_to_index()` on the label) + bool reserve_cursor // reserve space to the right for the cursor? + ) { Label label; @@ -542,8 +624,8 @@ namespace text const int len = strlen(str); if(cursor >= 0 || keep_layout) label.map_markup_to_text = new int[len+1]; if(keep_layout) label.map_text_to_markup = new int[len+1]; - label.layout = measure_text_internal(str, len, maxw, align, justify, color, width, height, offset, label.map_markup_to_text, label.map_text_to_markup, lang, no_fallback); - if(!label.layout || !width || !height) + label.layout = measure_text_internal(str, len, max_width, align, justify, color, width, height, offset, label.map_markup_to_text, label.map_text_to_markup, lang, no_fallback); + if(!width || !height) { return label; } @@ -553,31 +635,40 @@ namespace text { width += max(1., ceil(fontsize / 16.)); } - const int ol_offset = ceil(outline); - width += 2 * ol_offset; - height += 2 * ol_offset; + const int outline_offset = ceil(outline); + width += 2 * outline_offset; + height += 2 * outline_offset; cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); - cairo_t *cr = cairo_create(surface); - cairo_set_font_options(cr, options); - cairo_move_to(cr, offset + ol_offset, ol_offset); + cairo_t *cairo_context = cairo_create(surface); + cairo_set_font_options(cairo_context, global_font_options); + cairo_move_to(cairo_context, offset + outline_offset, outline_offset); // draw text outline if(outline) { - cairo_set_source_rgba(cr, ol_color.r/255., ol_color.g/255., ol_color.b/255., ol_color.a/255.); - cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND); - cairo_set_line_width(cr, 2 * outline); - pango_cairo_layout_path(cr, label.layout); - cairo_stroke(cr); + cairo_set_source_rgba(cairo_context, + outline_color.r/255., + outline_color.g/255., + outline_color.b/255., + outline_color.a/255. + ); + cairo_set_line_join(cairo_context, CAIRO_LINE_JOIN_ROUND); + cairo_set_line_width(cairo_context, 2 * outline); + pango_cairo_layout_path(cairo_context, label.layout); + cairo_stroke(cairo_context); } // draw text - cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0); - cairo_move_to(cr, offset + ol_offset, ol_offset); - pango_cairo_show_layout(cr, label.layout); + cairo_set_source_rgba(cairo_context, 1.0, 1.0, 1.0, 1.0); + cairo_move_to(cairo_context, offset + outline_offset, outline_offset); + pango_cairo_show_layout(cairo_context, label.layout); // draw the cursor - if(cursor >= 0 && (!cursorblink || (totalmillis - inputmillis <= cursorblink) || (totalmillis - inputmillis % (2 * cursorblink)) <= cursorblink)) + if(cursor >= 0 && ( + !cursorblink || + (totalmillis - inputmillis <= cursorblink) || + (totalmillis - inputmillis % (2 * cursorblink)) <= cursorblink + )) { if(cursor > len) cursor = len; cursor = min((int)strlen(pango_layout_get_text(label.layout)), label.map_markup_to_text[cursor]); @@ -585,9 +676,19 @@ namespace text pango_layout_get_cursor_pos(label.layout, cursor, &cursor_rect, nullptr); const double cursor_width = max(1., fontsize / 16.); - cairo_rectangle(cr, cursor_rect.x / PANGO_SCALE + ol_offset, cursor_rect.y / PANGO_SCALE + ol_offset, cursor_width, cursor_rect.height / PANGO_SCALE); - cairo_set_source_rgba(cr, cursorcolor.r / 255., cursorcolor.g / 255., cursorcolor.b / 255., 1.); - cairo_fill(cr); + cairo_rectangle(cairo_context, + cursor_rect.x / PANGO_SCALE + outline_offset, + cursor_rect.y / PANGO_SCALE + outline_offset, + cursor_width, + cursor_rect.height / PANGO_SCALE + ); + cairo_set_source_rgba(cairo_context, + cursorcolor.r / 255., + cursorcolor.g / 255., + cursorcolor.b / 255., + 1. + ); + cairo_fill(cairo_context); } if(!keep_layout) @@ -596,11 +697,11 @@ namespace text label.layout = nullptr; } - // create and upload texture + // create GPU texture glGenTextures(1, &label.tex); if(!label.tex) { - cairo_destroy(cr); + cairo_destroy(cairo_context); cairo_surface_destroy(surface); return label; } @@ -608,28 +709,30 @@ namespace text glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_COMPRESSED_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, cairo_image_surface_get_data(surface)); label.w = width; label.h = height; - label.ox = offset + ol_offset; - label.oy = ol_offset; + label.ox = offset + outline_offset; + label.oy = outline_offset; // clean up - cairo_destroy(cr); + cairo_destroy(cairo_context); cairo_surface_destroy(surface); return label; } - void measure(const char *str, int maxw, int& w, int& h, int align, int justify, const char *lang, bool no_fallback) + // same as `prepare()` but with appropriate settings for console text + Label prepare_for_console(const char *str, int max_width, int cursor) { - int _offset; - PangoLayout *layout = measure_text_internal(str, strlen(str), maxw, align, justify, bvec(0, 0, 0), w, h, _offset, nullptr, nullptr, lang, no_fallback); - if(layout) g_object_unref(layout); - } - - Label prepare_for_console(const char *str, int maxw, int cursor) - { - return prepare(str, maxw, bvec(255, 255, 255), cursor, conoutline ? ceil(FONTH / 32.) : 0, bvec4(0, 0, 0, conoutline)); + return prepare(str, max_width, bvec(255, 255, 255), cursor, conoutline ? ceil(FONTH / 32.) : 0, bvec4(0, 0, 0, conoutline)); } - const Label& prepare_for_particle(const char *str, bvec color, double outline, bvec4 ol_color, const char *lang, bool no_fallback) + // same as `prepare()` but use a cache so that the same label doesn't have to be recreated every time + const Label& prepare_for_particle( + const char *str, + const bvec& color, + double outline, + const bvec4& outline_color, + const char *lang, + bool no_fallback + ) { const int c = color.tohexcolor(); const char *l = lang ? lang : ""; @@ -637,11 +740,13 @@ namespace text key = crc32(0 , (const Bytef *)str, strlen(str)) + curfont->id; key = crc32(key, (const Bytef *)(&outline), sizeof(double)); key = crc32(key, (const Bytef *)(&c), sizeof(int)); - key = crc32(key, (const Bytef *)(&ol_color.mask), sizeof(uint)); + key = crc32(key, (const Bytef *)(&outline_color.mask), sizeof(uint)); key = crc32(key, (const Bytef *)l, strlen(l)); + Label& label = particle_cache[key]; if(label.valid()) return label; - label = prepare(str, 0, color, -1, outline, ol_color, -1, 0, lang, no_fallback); + + label = prepare(str, 0, color, -1, outline, outline_color, -1, 0, lang, no_fallback); if(particle_queue.length() >= 256) { const uint oldkey = particle_queue[0]; @@ -653,15 +758,45 @@ namespace text return label; } - void draw(const char *str, double left, double top, bvec color, int alpha, int maxw, int align, int justify, const char *lang, bool no_fallback) + // measure the dimensions of a string without creating the label (skips texture creation) + void measure( + const char *str, // the string to measure + int max_width, // maximum width in pixels + int& width, // output: the measured width + int& height, // output: the measured height + int align, // text alignment: -1 = left, 0 = center, 1 = right + int justify, // text justification (0 = disable, enable otherwise) + const char *lang, // optional language code of the text (can affect text shaping) + bool no_fallback // disable fallback fonts for unavailable glyphs? + ) { - const Label label = prepare(str, maxw, color, -1, 0, bvec4(color, alpha), align, justify, lang, no_fallback); + int _offset; + PangoLayout *layout = measure_text_internal(str, strlen(str), max_width, align, justify, bvec(0, 0, 0), width, height, _offset, nullptr, nullptr, lang, no_fallback); + g_object_unref(layout); + } + + // draw a string directly to the screen + void draw( + const char *str, // the string to draw + double left, // screen X coordinate + double top, // screen Y coordinate + const bvec& color, // initial color of the text + int alpha, // text opacity (0-255) + int max_width, // maximum width in pixels + int align, // text alignment: -1 = left, 0 = center, 1 = right + int justify, // text justification (0 = disable, enable otherwise) + const char *lang, // optional language code of the text (can affect text shaping) + bool no_fallback // disable fallback fonts for unavailable glyphs? + ) + { + const Label label = prepare(str, max_width, color, -1, 0, bvec4(color, alpha), align, justify, lang, no_fallback); if(label.valid()) label.draw(left, top, alpha); } - void draw_as_console(const char *str, double left, double top, int maxw, int cursor) + // same as `draw()` but with appropriate settings for console text + void draw_as_console(const char *str, double left, double top, int max_width, int cursor) { - const Label label = prepare_for_console(str, maxw, cursor); + const Label label = prepare_for_console(str, max_width, cursor); if(label.valid()) { if(conshadow) @@ -691,18 +826,13 @@ namespace text } // used by the text editor - int visible(const char *str, float hitx, float hity, int maxw, int align, int justify, const char *lang, bool no_fallback) + int visible(const char *str, float hitx, float hity, int max_width, int align, int justify, const char *lang, bool no_fallback) { int width, height, _offset; const int len = strlen(str); if(!len) return 0; int *map_text_to_markup = new int[len+1]; - PangoLayout *layout = measure_text_internal(str, len, maxw, align, justify, bvec(0, 0, 0), width, height, _offset, nullptr, map_text_to_markup, lang, no_fallback); - if(!layout) - { - delete[] map_text_to_markup; - return len; - } + PangoLayout *layout = measure_text_internal(str, len, max_width, align, justify, bvec(0, 0, 0), width, height, _offset, nullptr, map_text_to_markup, lang, no_fallback); if(!width || !height) { g_object_unref(layout); @@ -722,9 +852,8 @@ namespace text delete[] map_text_to_markup; return ret; } - // used by the text editor - void pos(const char *str, int cursor, int& cx, int& cy, int maxw, int align, int justify, const char *lang, bool no_fallback) + void pos(const char *str, int cursor, int& cx, int& cy, int max_width, int align, int justify, const char *lang, bool no_fallback) { int width, height, _offset; const int len = strlen(str); @@ -734,13 +863,7 @@ namespace text return; } int *map_markup_to_text = new int[len+1]; - PangoLayout *layout = measure_text_internal(str, len, maxw, align, justify, bvec(0, 0, 0), width, height, _offset, map_markup_to_text, nullptr, lang, no_fallback); - if(!layout) - { - cx = cy = 0; - delete[] map_markup_to_text; - return; - } + PangoLayout *layout = measure_text_internal(str, len, max_width, align, justify, bvec(0, 0, 0), width, height, _offset, map_markup_to_text, nullptr, lang, no_fallback); if(!width || !height) { g_object_unref(layout); @@ -758,4 +881,5 @@ namespace text g_object_unref(layout); delete[] map_markup_to_text; } -} // namespace text \ No newline at end of file +} // namespace text +#pragma endregion label \ No newline at end of file diff --git a/source/engine/ui.cpp b/source/engine/ui.cpp index 7f41ac10f..0360545f5 100644 --- a/source/engine/ui.cpp +++ b/source/engine/ui.cpp @@ -2102,7 +2102,7 @@ namespace UI Object::setup(); changed = false; const float newscale = scale_ * uiscale; - const int curfontid = getcurfontid(); + const int curfontid = getcurrentfontid(); if(!uimillis || (totalmillis - lastchange >= uimillis) || !lastchange) { if( From 5e4e3c7cfebce5d858dcc23dcd76e6a12f8f4a5c Mon Sep 17 00:00:00 2001 From: Jed- Date: Sun, 19 Jan 2025 17:47:02 +0100 Subject: [PATCH 56/68] Don't depend on cairo 1.18 --- source/engine/rendertext.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index e910cd239..7a7942e3c 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -244,7 +244,6 @@ bool init_pangocairo() cairo_font_options_set_antialias (global_font_options, antialias_); cairo_font_options_set_hint_style (global_font_options, hintstyle_); cairo_font_options_set_hint_metrics(global_font_options, CAIRO_HINT_METRICS_ON); - cairo_font_options_set_color_mode (global_font_options, CAIRO_COLOR_MODE_COLOR); // cairo 1.18 dummy_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0); if(CAIRO_STATUS_SUCCESS != cairo_surface_status(dummy_surface)) From 7e796ff255d54d9beea05bd65a9a829679b55fd5 Mon Sep 17 00:00:00 2001 From: Jed- Date: Mon, 20 Jan 2025 12:53:49 +0100 Subject: [PATCH 57/68] Fix UI input fields and cursor blink --- config/ui/lib.cfg | 42 +++++++++++++++++++++++++----------- source/engine/rendertext.cpp | 2 +- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/config/ui/lib.cfg b/config/ui/lib.cfg index b0ba0ca1b..88e982155 100644 --- a/config/ui/lib.cfg +++ b/config/ui/lib.cfg @@ -1190,6 +1190,7 @@ uiEntColor24B = [ uioutline (? $isfocused $c_red $c_line1) uipress [ local newvarcur + resetcursorblink newvarcur = (escape (+s "#" $type "bind" " " $action)) if (=s $newvarcur $.UI_KBvarcur) [ (+s $type "bind") MOUSELEFT $action @@ -1212,6 +1213,7 @@ uiEntColor24B = [ .UI_KBvarcur = "" ] [ if (!=s "MOUSELEFT" $uikeycode) [ + resetcursorblink (+s $type "bind") $uikeycode $action ] [ .UI_KBvarcur = "" @@ -1301,6 +1303,7 @@ uiKeyField = [ uitarget $width 0 [ uioutline (? $isfocused $c_red $c_line1) uipress [ + resetcursorblink if (< $uicursorindex 0) [ .UI_KBcaret = 0 ] [ @@ -1319,6 +1322,7 @@ uiKeyField = [ .UI_KBcaret = -1 uisettextinput 0 ] [ + resetcursorblink cases $uikeycode "DELETE" [ $.UI_KBvarcur = (clamp (.delzeroes (strsplice $$.UI_KBvarcur "" $.UI_KBcaret 1)) $min_ $max_) do $onchange @@ -1432,12 +1436,12 @@ uiTextField = [ uitarget $width 0 [ uioutline (? $isfocused $c_red $c_line1) uipress [ + resetcursorblink if (< $uicursorindex 0) [ - .UI_KBcaret = (? (> $align 0) 0 (strlen $$var)) + .UI_KBcaret = (? (> $align 0) 0 (char2byteindex $$var (strlen $$var))) ] [ .UI_KBcaret = (? (> (strlen $$var) 0) $uicursorindex 0) ] - .UI_KBvarcur = $arg1 ] @@ -1453,37 +1457,49 @@ uiTextField = [ .UI_KBcaret = -1 uisettextinput 0 ] [ + resetcursorblink cases $uikeycode "DELETE" [ - $.UI_KBvarcur = (strsplice $$.UI_KBvarcur "" $.UI_KBcaret 1) + $.UI_KBvarcur = (strsplice $$.UI_KBvarcur "" (byte2charindex $$.UI_KBvarcur $.UI_KBcaret) 1) do $onchange ] "BACKSPACE" [ if (> $.UI_KBcaret 0) [ - $.UI_KBvarcur = (strsplice $$.UI_KBvarcur "" (- $.UI_KBcaret 1) 1) - -- .UI_KBcaret + local ix_c + ix_c = (byte2charindex $$.UI_KBvarcur $.UI_KBcaret) + $.UI_KBvarcur = (strsplice $$.UI_KBvarcur "" (- $ix_c 1) 1) + .UI_KBcaret = (char2byteindex $$.UI_KBvarcur (- $ix_c 1)) ] do $onchange ] "LEFT" [ + // move cursor to the left by 1 character (not byte!) if (> $.UI_KBcaret 0) [ - -- .UI_KBcaret + local ix_c + ix_c = (byte2charindex $$.UI_KBvarcur $.UI_KBcaret) + .UI_KBcaret = (char2byteindex $$.UI_KBvarcur (- $ix_c 1)) ] ] "RIGHT" [ - if (&& [>= $.UI_KBcaret 0] [< $.UI_KBcaret (strlen $$.UI_KBvarcur)]) [ - ++ .UI_KBcaret + // move cursor to the right by 1 character (not byte!) + if (&& [>= $.UI_KBcaret 0] [< (byte2charindex $$.UI_KBvarcur $.UI_KBcaret) (strlen $$.UI_KBvarcur)]) [ + local ix_c + ix_c = (byte2charindex $$.UI_KBvarcur $.UI_KBcaret) + .UI_KBcaret = (char2byteindex $$.UI_KBvarcur (+ $ix_c 1)) ] ] () [ - local inputlen pastestr + local inputlen pastestr pastebytelen if (&& [=s "V" $uikeycode] [|| [iskeyheld "LCTRL"] [iskeyheld "RCTRL"]]) [ // CTRL-V inputlen = (clamp (strlen $getclipboard) 0 (- $maxstrlen (strlen $$.UI_KBvarcur))) pastestr = (substr $getclipboard 0 $inputlen) - $.UI_KBvarcur = (substr (strsplice $$.UI_KBvarcur $pastestr $.UI_KBcaret 0) 0 $maxstrlen) - .UI_KBcaret = (min $maxstrlen (+ $.UI_KBcaret $inputlen)) + pastebytelen = (strbytelen $pastestr) + $.UI_KBvarcur = (strsplice $$.UI_KBvarcur $pastestr (byte2charindex $$.UI_KBvarcur $.UI_KBcaret) 0) + .UI_KBcaret = (+ $.UI_KBcaret $pastebytelen) do $onchange ] [ if (&& [!=s "" $uitextinput] [< (strlen $$.UI_KBvarcur) $maxstrlen]) [ inputlen = (strlen $uitextinput) - $.UI_KBvarcur = (substr (strsplice $$.UI_KBvarcur $uitextinput $.UI_KBcaret 0) 0 $maxstrlen) - .UI_KBcaret = (min $maxstrlen (+ $.UI_KBcaret $inputlen)) + $.UI_KBvarcur = (substr (strsplice $$.UI_KBvarcur $uitextinput (byte2charindex $$.UI_KBvarcur $.UI_KBcaret) 0) 0 $maxstrlen) + .UI_KBcaret = (char2byteindex $$.UI_KBvarcur (min $maxstrlen (+ (byte2charindex $$.UI_KBvarcur $.UI_KBcaret) $inputlen))) + do $onchange + ] ] ] diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index 7a7942e3c..2cc9e3751 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -666,7 +666,7 @@ namespace text if(cursor >= 0 && ( !cursorblink || (totalmillis - inputmillis <= cursorblink) || - (totalmillis - inputmillis % (2 * cursorblink)) <= cursorblink + ((totalmillis - inputmillis) % (2 * cursorblink)) <= cursorblink )) { if(cursor > len) cursor = len; From 60035fd7005d4ae42010ba230b64e025de8a61c8 Mon Sep 17 00:00:00 2001 From: Jed- Date: Thu, 23 Jan 2025 15:24:17 +0100 Subject: [PATCH 58/68] Add arguments to `uijustify` and `uinofallback` so they can be more easily turned on and off --- source/engine/ui.cpp | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/source/engine/ui.cpp b/source/engine/ui.cpp index 0360545f5..8a2e02683 100644 --- a/source/engine/ui.cpp +++ b/source/engine/ui.cpp @@ -1981,9 +1981,10 @@ namespace UI else dst = newstring(src); \ } while(0) + // text attributes static int curwrapalign = -1, curshadow = 0, curfontoutlinealpha = 0; - float curfontoutline = 0.f; - bool curjustify = false, curnofallback = false; + static float curfontoutline = 0.f; + static int curjustify = false, curnofallback = false; static const char *curlanguage = newstring(""); #define WITHTEXTATTR(name, tmp, val, body) do { \ @@ -2009,10 +2010,11 @@ namespace UI static const char *typestr() { return "#Justify"; } const char *gettype() const { return typestr(); } - bool tmp; - void layout() { tmp = curjustify; curjustify = true; Object::layout(); curjustify = tmp; } - void draw(float sx, float sy) { tmp = curjustify; curjustify = true; Object::draw(sx, sy); curjustify = tmp; } - void buildchildren(uint *contents) { tmp = curjustify; curjustify = true; Object::buildchildren(contents); curjustify = tmp; } + int val, tmp; + void setup(int val_) { val = val_; } + void layout() { WITHTEXTATTR(justify, tmp, val, Object::layout()); } + void draw(float sx, float sy) { WITHTEXTATTR(justify, tmp, val, Object::draw(sx, sy)); } + void buildchildren(uint *contents) { WITHTEXTATTR(justify, tmp, val, Object::buildchildren(contents)); } }; struct Shadow : Object { @@ -2031,21 +2033,22 @@ namespace UI const char *gettype() const { return typestr(); } float val, tmp; - int aval, atmp; - void setup(float val_, int aval_) { val = val_; aval = aval_; } - void layout() { WITHTEXTATTR(fontoutline, tmp, val, WITHTEXTATTR(fontoutlinealpha, atmp, aval, Object::layout())); } - void draw(float sx, float sy) { WITHTEXTATTR(fontoutline, tmp, val, WITHTEXTATTR(fontoutlinealpha, atmp, aval, Object::draw(sx, sy))); } - void buildchildren(uint *contents) { WITHTEXTATTR(fontoutline, tmp, val, WITHTEXTATTR(fontoutlinealpha, atmp, aval, Object::buildchildren(contents))); } + int alpha_val, alpha_tmp; + void setup(float val_, int alpha_val_) { val = val_; alpha_val = alpha_val_; } + void layout() { WITHTEXTATTR(fontoutline, tmp, val, WITHTEXTATTR(fontoutlinealpha, alpha_tmp, alpha_val, Object::layout())); } + void draw(float sx, float sy) { WITHTEXTATTR(fontoutline, tmp, val, WITHTEXTATTR(fontoutlinealpha, alpha_tmp, alpha_val, Object::draw(sx, sy))); } + void buildchildren(uint *contents) { WITHTEXTATTR(fontoutline, tmp, val, WITHTEXTATTR(fontoutlinealpha, alpha_tmp, alpha_val, Object::buildchildren(contents))); } }; struct NoFallback : Object { static const char *typestr() { return "#NoFallback"; } const char *gettype() const { return typestr(); } - bool tmp; - void layout() { tmp = curnofallback; curnofallback = true; Object::layout(); curnofallback = tmp; } - void draw(float sx, float sy) { tmp = curnofallback; curnofallback = true; Object::draw(sx, sy); curnofallback = tmp; } - void buildchildren(uint *contents) { tmp = curnofallback; curnofallback = true; Object::buildchildren(contents); curnofallback = tmp; } + int val, tmp; + void setup(int val_) { val = val_; } + void layout() { WITHTEXTATTR(nofallback, tmp, val, Object::layout()); } + void draw(float sx, float sy) { WITHTEXTATTR(nofallback, tmp, val, Object::draw(sx, sy)); } + void buildchildren(uint *contents) { WITHTEXTATTR(nofallback, tmp, val, Object::buildchildren(contents)); } }; struct Language : Object { @@ -2088,7 +2091,7 @@ namespace UI int fontid, lastchange; int align, shadow, outlinealpha; float outline; - bool justify, nofallback; + int justify, nofallback; const char *language; int cursor; bool has_cursor; @@ -3702,8 +3705,8 @@ namespace UI ICOMMAND(uiwrapalign, "ie", (int *val, uint *children), BUILD(WrapAlign, o, o->setup(*val), children)); - ICOMMAND(uijustify, "e", (uint *children), - BUILD(Justify, o, o->setup(), children)); + ICOMMAND(uijustify, "ie", (int *val, uint *children), + BUILD(Justify, o, o->setup(*val), children)); ICOMMAND(uishadow, "ie", (int *val, uint *children), BUILD(Shadow, o, o->setup(*val), children)); @@ -3711,8 +3714,8 @@ namespace UI ICOMMAND(uifontoutline, "fie", (float *val, int *a, uint *children), BUILD(FontOutline, o, o->setup(*val, *a), children)); - ICOMMAND(uinofallback, "e", (uint *children), - BUILD(NoFallback, o, o->setup(), children)); + ICOMMAND(uinofallback, "ie", (int *val, uint *children), + BUILD(NoFallback, o, o->setup(*val), children)); ICOMMAND(uilanguage, "se", (char *val, uint *children), BUILD(Language, o, o->setup(val), children)); From d5e680adcff1eaa62c11b2eff188938f3e0ae618 Mon Sep 17 00:00:00 2001 From: Jed- Date: Thu, 23 Jan 2025 15:35:33 +0100 Subject: [PATCH 59/68] Remove duplicate command --- source/engine/console.cpp | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/source/engine/console.cpp b/source/engine/console.cpp index 0c0c00fb7..c6224034c 100644 --- a/source/engine/console.cpp +++ b/source/engine/console.cpp @@ -649,28 +649,6 @@ void pasteconsole() SDL_free(cb); } -// returns the contents of the clipboard -ICOMMAND(getclipboard, "", (), -{ - if(!SDL_HasClipboardText()) - { - result(""); - } - else - { - char *cb = SDL_GetClipboardText(); - if(!cb) - { - result(""); - } - else - { - result(cb); - SDL_free(cb); - } - } -}); - static char *skipword(char *s) { while(int c = *s++) if(!iscubespace(c)) From 6ae5a62e04c2c0170ee918c6f76653a12be990a1 Mon Sep 17 00:00:00 2001 From: Jed- Date: Thu, 23 Jan 2025 15:54:50 +0100 Subject: [PATCH 60/68] Unicode cleanup --- source/Makefile | 35 ++++++++++++++++++----------------- source/engine/command.cpp | 2 +- source/engine/console.cpp | 2 +- source/shared/stream.cpp | 11 +++-------- source/shared/unicode.h | 28 ++++++++++++++-------------- 5 files changed, 37 insertions(+), 41 deletions(-) diff --git a/source/Makefile b/source/Makefile index dbf5eaad0..89cd2437c 100644 --- a/source/Makefile +++ b/source/Makefile @@ -262,6 +262,7 @@ shared/glemu.o: engine/sound.h shared/iengine.h shared/igame.h shared/stream.o: shared/cube.h shared/tools.h shared/geom.h shared/ents.h shared/stream.o: shared/command.h shared/glexts.h shared/glemu.h shared/stream.o: engine/sound.h shared/iengine.h shared/igame.h +shared/stream.o: shared/unicode.h shared/tools.o: shared/cube.h shared/tools.h shared/geom.h shared/ents.h shared/tools.o: shared/command.h shared/glexts.h shared/glemu.h shared/tools.o: engine/sound.h shared/iengine.h shared/igame.h @@ -289,18 +290,18 @@ engine/client.o: shared/ents.h shared/command.h shared/glexts.h engine/client.o: shared/glemu.h engine/sound.h shared/iengine.h engine/client.o: shared/igame.h engine/world.h engine/octa.h engine/light.h engine/client.o: engine/texture.h engine/bih.h engine/model.h -engine/command.o: shared/unicode.h engine/engine.h shared/cube.h -engine/command.o: shared/tools.h shared/geom.h shared/ents.h shared/command.h -engine/command.o: shared/glexts.h shared/glemu.h engine/sound.h -engine/command.o: shared/iengine.h shared/igame.h engine/world.h -engine/command.o: engine/octa.h engine/light.h engine/texture.h engine/bih.h -engine/command.o: engine/model.h -engine/console.o: shared/unicode.h engine/engine.h shared/cube.h -engine/console.o: shared/tools.h shared/geom.h shared/ents.h shared/command.h -engine/console.o: shared/glexts.h shared/glemu.h engine/sound.h -engine/console.o: shared/iengine.h shared/igame.h engine/world.h -engine/console.o: engine/octa.h engine/light.h engine/texture.h engine/bih.h -engine/console.o: engine/model.h +engine/command.o: engine/engine.h shared/cube.h shared/tools.h shared/geom.h +engine/command.o: shared/ents.h shared/command.h shared/glexts.h +engine/command.o: shared/glemu.h engine/sound.h shared/iengine.h +engine/command.o: shared/igame.h engine/world.h engine/octa.h engine/light.h +engine/command.o: engine/texture.h engine/bih.h engine/model.h +engine/command.o: shared/unicode.h +engine/console.o: engine/engine.h shared/cube.h shared/tools.h shared/geom.h +engine/console.o: shared/ents.h shared/command.h shared/glexts.h +engine/console.o: shared/glemu.h engine/sound.h shared/iengine.h +engine/console.o: shared/igame.h engine/world.h engine/octa.h engine/light.h +engine/console.o: engine/texture.h engine/bih.h engine/model.h +engine/console.o: shared/unicode.h engine/dynlight.o: engine/engine.h shared/cube.h shared/tools.h shared/geom.h engine/dynlight.o: shared/ents.h shared/command.h shared/glexts.h engine/dynlight.o: shared/glemu.h engine/sound.h shared/iengine.h @@ -570,14 +571,14 @@ standalone/shared/crypto.o: shared/ents.h shared/command.h engine/sound.h standalone/shared/crypto.o: shared/iengine.h shared/igame.h standalone/shared/stream.o: shared/cube.h shared/tools.h shared/geom.h standalone/shared/stream.o: shared/ents.h shared/command.h engine/sound.h -standalone/shared/stream.o: shared/iengine.h shared/igame.h +standalone/shared/stream.o: shared/iengine.h shared/igame.h shared/unicode.h standalone/shared/tools.o: shared/cube.h shared/tools.h shared/geom.h standalone/shared/tools.o: shared/ents.h shared/command.h engine/sound.h standalone/shared/tools.o: shared/iengine.h shared/igame.h shared/unicode.h -standalone/engine/command.o: shared/unicode.h engine/engine.h shared/cube.h -standalone/engine/command.o: shared/tools.h shared/geom.h shared/ents.h -standalone/engine/command.o: shared/command.h engine/sound.h shared/iengine.h -standalone/engine/command.o: shared/igame.h engine/world.h +standalone/engine/command.o: engine/engine.h shared/cube.h shared/tools.h +standalone/engine/command.o: shared/geom.h shared/ents.h shared/command.h +standalone/engine/command.o: engine/sound.h shared/iengine.h shared/igame.h +standalone/engine/command.o: engine/world.h shared/unicode.h standalone/engine/server.o: engine/engine.h shared/cube.h shared/tools.h standalone/engine/server.o: shared/geom.h shared/ents.h shared/command.h standalone/engine/server.o: engine/sound.h shared/iengine.h shared/igame.h diff --git a/source/engine/command.cpp b/source/engine/command.cpp index a9b18f8ea..a803ebc6c 100644 --- a/source/engine/command.cpp +++ b/source/engine/command.cpp @@ -1,8 +1,8 @@ // command.cpp: implements the parsing and execution of a tiny script language which // is largely backwards compatible with the quake console language. -#include "unicode.h" #include "engine.h" +#include "unicode.h" hashnameset idents; // contains ALL vars/commands/aliases vector identmap; diff --git a/source/engine/console.cpp b/source/engine/console.cpp index c6224034c..ba4522485 100644 --- a/source/engine/console.cpp +++ b/source/engine/console.cpp @@ -1,7 +1,7 @@ // console.cpp: the console buffer, its display, and command line control -#include "unicode.h" #include "engine.h" +#include "unicode.h" #define MAXCONLINES 1000 struct cline { char *line; int type, outtime; float fontsize, w; text::Label label; }; diff --git a/source/shared/stream.cpp b/source/shared/stream.cpp index 1462393ef..423fd5c76 100644 --- a/source/shared/stream.cpp +++ b/source/shared/stream.cpp @@ -1,4 +1,5 @@ #include "cube.h" +#include "unicode.h" ///////////////////////////// console //////////////////////// @@ -28,18 +29,12 @@ void conoutf(int type, int tag, const char *fmt, ...) ///////////////////////// character conversion /////////////// -static inline int uni_charlower(char c) -{ - if(c >= 'A' && c <= 'Z') return 'a' + (c - 'A'); - return c; -} - int cubecasecmp(const char *s1, const char *s2, int n) { if(!s1 || !s2) return !s2 - !s1; while(n-- > 0) { - int c1 = uni_charlower(*s1++), c2 = uni_charlower(*s2++); + const char c1 = uni_charlower(*s1++), c2 = uni_charlower(*s2++); if(c1 != c2) return c1 - c2; if(!c1) break; } @@ -50,7 +45,7 @@ char *cubecasefind(const char *haystack, const char *needle) { if(haystack && needle) for(const char *h = haystack, *n = needle;;) { - int hc = uni_charlower(*h++), nc = uni_charlower(*n++); + const char hc = uni_charlower(*h++), nc = uni_charlower(*n++); if(!nc) return (char*)h - (n - needle); if(hc != nc) { diff --git a/source/shared/unicode.h b/source/shared/unicode.h index eff836784..7ddea6a84 100644 --- a/source/shared/unicode.h +++ b/source/shared/unicode.h @@ -160,27 +160,27 @@ static inline int uni_index(const char *str, unsigned int off) } // only supports ASCII +static inline char uni_charlower(char c) +{ + if(c >= 'A' && c <= 'Z') return 'a' + (c - 'A'); + return c; +} +static inline char uni_charupper(char c) +{ + if(c >= 'a' && c <= 'z') return 'A' + (c - 'a'); + return c; +} static inline void uni_strlower(const char *src, char *dst) { - for(const unsigned char *p = (unsigned char *)src; *p; ++p) + for(const char *p = src; *p; ++p) { - if(*p >= 'A' && *p <= 'Z') - { - *dst = 'a' + (*p - 'A'); - } - else *dst = *p; - ++dst; + *dst++ = uni_charlower(*p); } } static inline void uni_strupper(const char *src, char *dst) { - for(const unsigned char *p = (unsigned char *)src; *p; ++p) + for(const char *p = src; *p; ++p) { - if(*p >= 'a' && *p <= 'z') - { - *dst = 'A' + (*p - 'a'); - } - else *dst = *p; - ++dst; + *dst++ = uni_charupper(*p); } } \ No newline at end of file From e9d3e761df14cda4591350be2d2b2f56f13f8f6f Mon Sep 17 00:00:00 2001 From: Jed- Date: Thu, 23 Jan 2025 16:40:37 +0100 Subject: [PATCH 61/68] Clean up `homoglyph()` function and add a script to generate it from latest Unicode data --- source/confusables.py | 73 +++++++++++++++++ source/shared/tools.h | 178 ++++++++++++++++++------------------------ 2 files changed, 148 insertions(+), 103 deletions(-) create mode 100755 source/confusables.py diff --git a/source/confusables.py b/source/confusables.py new file mode 100755 index 000000000..802999e88 --- /dev/null +++ b/source/confusables.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 + +import requests + +CONFUSABLES_URL = "https://www.unicode.org/Public/security/latest/confusables.txt" + +def is_codepoint_allowed(codepoint: int) -> bool: + return ( + (codepoint >= 0x0021 and codepoint <= 0x007E) # ASCII + + or (codepoint >= 0x00A1 and codepoint <= 0x00AC) + or (codepoint >= 0x00AE and codepoint <= 0x00FF) # Latin-1 Supplement + + or (codepoint >= 0x0100 and codepoint <= 0x0131) + or (codepoint >= 0x0134 and codepoint <= 0x0148) + or (codepoint >= 0x014A and codepoint <= 0x017E) # Latin Extended-A (exclude 0x131, 0x132, 0x149 and 0x17F) + + or (codepoint >= 0x0218 and codepoint <= 0x021B) # Latin Extended-B: Romanian + + or (codepoint == 0x0386) + or (codepoint >= 0x0388 and codepoint <= 0x038A) + or (codepoint == 0x038C) + or (codepoint >= 0x038E and codepoint <= 0x03A1) + or (codepoint >= 0x03A3 and codepoint <= 0x03CE) # Greek and Coptic + + or (codepoint >= 0x0400 and codepoint <= 0x045F) # Cyrillic + + or (codepoint >= 0x1EA0 and codepoint <= 0x1EF9) # Latin Extended Additional: Vietnamese + ) + +confusables = dict() + +# download confusables file from Unicode +response = requests.get(CONFUSABLES_URL).text + +# parse each line +for line in response.split('\n'): + line = line.split('#', 1)[0].strip() # ignore everything after the `#` character (comments) and remove whitespaces + if not len(line): continue # skip empty lines + + # format: 123A ;\t456B ;\tMA + # we want the lines where both the first and the second column have exactly one entry + src, dst, _ = [x.strip() for x in line.split(';', 2)] + if len(src.split(' ', 1)) != 1 or len(dst.split(' ', 1)) != 1: continue + + # check that both codepoints are allowed + src_n = int(src, 16) + dst_n = int(dst, 16) + if not is_codepoint_allowed(src_n) or not is_codepoint_allowed(dst_n): continue + + # save the entry + if confusables.get(dst_n) == None: + confusables[dst_n] = [src_n] + else: + confusables[dst_n].append(src_n) + +# print homoglyph function +print('static inline uint homoglyph(uint c)') +print('{') +print('\tswitch(c)') +print('\t{') + +for dst_n in sorted(confusables.keys()): + src_list = confusables.get(dst_n) + n = 0 + for src_n in src_list: + n = n + 1 + print(f'\t\tcase {hex(src_n)}:', end='' if n >= len(src_list) else '\n') + print(f' return {hex(dst_n)};') + +print('\t}') +print('\treturn c;') +print('}') \ No newline at end of file diff --git a/source/shared/tools.h b/source/shared/tools.h index 2e4a5abd7..4212e64fd 100644 --- a/source/shared/tools.h +++ b/source/shared/tools.h @@ -1370,34 +1370,6 @@ enum T_NAME = 1<<4 }; -/* (enable all of unicode for now) -// characters that can be shown in the console -static inline int iscubeprint(uint c) -{ - return (c >= 0x0021 && c <= 0x007E) // ASCII [94] - - || (c >= 0x00A1 && c <= 0x00AC) // (exclude 0x00AD soft hyphen) - || (c >= 0x00AE && c <= 0x00FF) // Latin-1 Supplement [94] - - || (c >= 0x0100 && c <= 0x017F) // Latin Extended-A [128] - || (c >= 0x0180 && c <= 0x024F) // Latin Extended-B [208] - - || (c >= 0x0370 && c <= 0x0377) - || (c >= 0x037A && c <= 0x037F) - || (c >= 0x0384 && c <= 0x038A) - || (c == 0x038C) - || (c >= 0x038E && c <= 0x03A1) - || (c >= 0x03A3 && c <= 0x03FF) // Greek and Coptic [135] - - || (c >= 0x0400 && c <= 0x0482) // (exclude combining cyrillic characters) - || (c >= 0x048A && c <= 0x04FF) // Cyrillic [249] - - || (c >= 0x0500 && c <= 0x052F) // Cyrillic Supplement [48] - || (c >= 0x1E00 && c <= 0x1EFF) // Latin Extended Additional [256] - - || (c >= 0x20A0 && c <= 0x20C0) // Currency Symbols [33] - ; -}*/ static inline int iscubespace(uint c) { return c == ' ' || c == '\n' || c == '\r' || c == '\t' ? 1 : 0; } static inline int iscubealpha(uint c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ? 1 : 0; } static inline int iscubealnum(uint c) { return (c >= '0' && c <= '9') || iscubealpha(c) ? 1 : 0; } @@ -1405,7 +1377,7 @@ static inline int iscubelower(uint c) { return (c >= 'a' && c <= 'z') ? 1 : 0; } static inline int iscubeupper(uint c) { return (c >= 'A' && c <= 'Z') ? 1 : 0; } static inline int iscubecntrl(uint c) { return (c <= 0x08 || c == 0x0B || (c >= 0x0E && c <= 0x1F) || (c >= 0x7F && c <= 0x9F)) ? 1 : 0; } static inline int iscubepunct(uint c) { return (c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') ? 1 : 0; } -// a subeset of console characters that are allowed in player names +// a subset of console characters that are allowed in player names // the default font should contain glyphs for all of these static inline int iscubenamesafe(uint c) { @@ -1437,80 +1409,80 @@ static inline uint homoglyph(uint c) { switch(c) { - case 0x00B8: return 0x002C; - case 0x0060: return 0x0027; - case 0x00B4: return 0x0027; - case 0x0417: return 0x0033; - case 0x0431: return 0x0036; - case 0x03B1: return 0x0061; - case 0x0430: return 0x0061; - case 0x0391: return 0x0041; - case 0x0410: return 0x0041; - case 0x042C: return 0x0062; - case 0x0392: return 0x0042; - case 0x0412: return 0x0042; - case 0x0441: return 0x0063; - case 0x0421: return 0x0043; - case 0x0435: return 0x0065; - case 0x0395: return 0x0045; - case 0x0415: return 0x0045; - case 0x011B: return 0x0115; - case 0x011A: return 0x0114; - case 0x0397: return 0x0048; - case 0x041D: return 0x0048; - case 0x0131: return 0x0069; - case 0x03B9: return 0x0069; - case 0x0456: return 0x0069; - case 0x0458: return 0x006A; - case 0x0408: return 0x004A; - case 0x039A: return 0x004B; - case 0x041A: return 0x004B; - case 0x007C: return 0x006C; - case 0x0031: return 0x006C; - case 0x0049: return 0x006C; - case 0x0399: return 0x006C; - case 0x0406: return 0x006C; - case 0x039C: return 0x004D; - case 0x041C: return 0x004D; - case 0x039D: return 0x004E; - case 0x03BF: return 0x006F; - case 0x03C3: return 0x006F; - case 0x043E: return 0x006F; - case 0x0030: return 0x004F; - case 0x039F: return 0x004F; - case 0x041E: return 0x004F; - case 0x0150: return 0x00D6; - case 0x03C1: return 0x0070; - case 0x0440: return 0x0070; - case 0x03A1: return 0x0050; - case 0x0420: return 0x0050; - case 0x03BA: return 0x0138; - case 0x043A: return 0x0138; - case 0x0433: return 0x0072; - case 0x0455: return 0x0073; - case 0x0405: return 0x0053; - case 0x03B2: return 0x00DF; - case 0x03A4: return 0x0054; - case 0x0422: return 0x0054; - case 0x021A: return 0x0162; - case 0x03C5: return 0x0075; - case 0x03BD: return 0x0076; - case 0x00D7: return 0x0078; - case 0x0445: return 0x0078; - case 0x03A7: return 0x0058; - case 0x0425: return 0x0058; - case 0x03B3: return 0x0079; - case 0x0443: return 0x0079; - case 0x03A5: return 0x0059; - case 0x0423: return 0x0059; - case 0x0396: return 0x005A; - case 0x0413: return 0x0393; - case 0x00B5: return 0x03BC; - case 0x043F: return 0x03C0; - case 0x041F: return 0x03A0; - case 0x0424: return 0x03A6; - case 0x0419: return 0x040D; - case 0x045D: return 0x0439; + case 0x60: + case 0xb4: return 0x27; + case 0xb8: return 0x2c; + case 0x417: return 0x33; + case 0x431: return 0x36; + case 0x391: + case 0x410: return 0x41; + case 0x392: + case 0x412: return 0x42; + case 0x421: return 0x43; + case 0x395: + case 0x415: return 0x45; + case 0x397: + case 0x41d: return 0x48; + case 0x408: return 0x4a; + case 0x39a: + case 0x41a: return 0x4b; + case 0x39c: + case 0x41c: return 0x4d; + case 0x39d: return 0x4e; + case 0x30: + case 0x39f: + case 0x41e: return 0x4f; + case 0x3a1: + case 0x420: return 0x50; + case 0x405: return 0x53; + case 0x3a4: + case 0x422: return 0x54; + case 0x3a7: + case 0x425: return 0x58; + case 0x3a5: + case 0x423: return 0x59; + case 0x396: return 0x5a; + case 0x3b1: + case 0x430: return 0x61; + case 0x42c: return 0x62; + case 0x441: return 0x63; + case 0x435: return 0x65; + case 0x131: + case 0x3b9: + case 0x456: return 0x69; + case 0x458: return 0x6a; + case 0x7c: + case 0x31: + case 0x49: + case 0x399: + case 0x406: return 0x6c; + case 0x3bf: + case 0x3c3: + case 0x43e: return 0x6f; + case 0x3c1: + case 0x440: return 0x70; + case 0x433: return 0x72; + case 0x455: return 0x73; + case 0x3c5: return 0x75; + case 0x3bd: return 0x76; + case 0xd7: + case 0x445: return 0x78; + case 0x3b3: + case 0x443: return 0x79; + case 0x150: return 0xd6; + case 0x3b2: return 0xdf; + case 0x11a: return 0x114; + case 0x11b: return 0x115; + case 0x3ba: + case 0x43a: return 0x138; + case 0x21a: return 0x162; + case 0x413: return 0x393; + case 0x41f: return 0x3a0; + case 0x424: return 0x3a6; + case 0xb5: return 0x3bc; + case 0x43f: return 0x3c0; + case 0x419: return 0x40d; + case 0x45d: return 0x439; } return c; } From fbd4a6e2ab42cc0a3d0748bfe08f09fd0653ca0c Mon Sep 17 00:00:00 2001 From: Jed- Date: Tue, 4 Feb 2025 14:26:09 +0100 Subject: [PATCH 62/68] Remove the built-in text editor --- source/Makefile | 2 +- source/engine/engine.h | 15 - source/engine/rendertext.cpp | 57 --- source/engine/textedit.h | 814 ----------------------------------- source/engine/ui.cpp | 306 ------------- 5 files changed, 1 insertion(+), 1193 deletions(-) delete mode 100644 source/engine/textedit.h diff --git a/source/Makefile b/source/Makefile index 89cd2437c..d91244768 100644 --- a/source/Makefile +++ b/source/Makefile @@ -448,7 +448,7 @@ engine/ui.o: engine/engine.h shared/cube.h shared/tools.h shared/geom.h engine/ui.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h engine/ui.o: engine/sound.h shared/iengine.h shared/igame.h engine/world.h engine/ui.o: engine/octa.h engine/light.h engine/texture.h engine/bih.h -engine/ui.o: engine/model.h engine/textedit.h shared/unicode.h +engine/ui.o: engine/model.h engine/liquid.o: engine/engine.h shared/cube.h shared/tools.h shared/geom.h engine/liquid.o: shared/ents.h shared/command.h shared/glexts.h engine/liquid.o: shared/glemu.h engine/sound.h shared/iengine.h diff --git a/source/engine/engine.h b/source/engine/engine.h index cd602cb6c..001ae660a 100644 --- a/source/engine/engine.h +++ b/source/engine/engine.h @@ -174,21 +174,6 @@ namespace text } void getres(int& w, int& h); - - // used by the text editor - int visible(const char *str, float hitx, float hity, int max_width, - int align = -1, - int justify = 0, - const char *lang = nullptr, - bool no_fallback = false - ); - // used by the text editor - void pos(const char *str, int cursor, int& cx, int& cy, int max_width, - int align = -1, - int justify = 0, - const char *lang = nullptr, - bool no_fallback = false - ); } // texture diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index 2cc9e3751..65a764d42 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -823,62 +823,5 @@ namespace text } } } - - // used by the text editor - int visible(const char *str, float hitx, float hity, int max_width, int align, int justify, const char *lang, bool no_fallback) - { - int width, height, _offset; - const int len = strlen(str); - if(!len) return 0; - int *map_text_to_markup = new int[len+1]; - PangoLayout *layout = measure_text_internal(str, len, max_width, align, justify, bvec(0, 0, 0), width, height, _offset, nullptr, map_text_to_markup, lang, no_fallback); - if(!width || !height) - { - g_object_unref(layout); - delete[] map_text_to_markup; - return len; - } - - int index; - const int res = pango_layout_xy_to_index(layout, hitx * PANGO_SCALE, hity * PANGO_SCALE, &index, nullptr); - g_object_unref(layout); - if(!res) - { - delete[] map_text_to_markup; - return len; - } - const int ret = map_text_to_markup[index]; - delete[] map_text_to_markup; - return ret; - } - // used by the text editor - void pos(const char *str, int cursor, int& cx, int& cy, int max_width, int align, int justify, const char *lang, bool no_fallback) - { - int width, height, _offset; - const int len = strlen(str); - if(!len) - { - cx = cy = 0; - return; - } - int *map_markup_to_text = new int[len+1]; - PangoLayout *layout = measure_text_internal(str, len, max_width, align, justify, bvec(0, 0, 0), width, height, _offset, map_markup_to_text, nullptr, lang, no_fallback); - if(!width || !height) - { - g_object_unref(layout); - cx = cy = 0; - delete[] map_markup_to_text; - return; - } - - cursor = max(0, min((int)strlen(pango_layout_get_text(layout)), map_markup_to_text[cursor])); - PangoRectangle pos; - pango_layout_index_to_pos(layout, cursor, &pos); - cx = pos.x / PANGO_SCALE; - cy = pos.y / PANGO_SCALE; - - g_object_unref(layout); - delete[] map_markup_to_text; - } } // namespace text #pragma endregion label \ No newline at end of file diff --git a/source/engine/textedit.h b/source/engine/textedit.h deleted file mode 100644 index 6fed35ad1..000000000 --- a/source/engine/textedit.h +++ /dev/null @@ -1,814 +0,0 @@ -#include "unicode.h" - -struct editline -{ - enum { CHUNKSIZE = 256 }; - - char *text; - int len, maxlen; - - editline() : text(NULL), len(0), maxlen(0) {} - editline(const char *init) : text(NULL), len(0), maxlen(0) - { - set(init); - } - - bool empty() { return len <= 0; } - - void clear() - { - DELETEA(text); - len = maxlen = 0; - } - - bool grow(int total, const char *fmt = "", ...) - { - if(total + 1 <= maxlen) return false; - maxlen = (total + CHUNKSIZE) - total%CHUNKSIZE; - char *newtext = new char[maxlen]; - if(fmt) - { - va_list args; - va_start(args, fmt); - vformatstring(newtext, fmt, args, maxlen); - va_end(args); - } - else newtext[0] = '\0'; - DELETEA(text); - text = newtext; - return true; - } - - void set(const char *str, int slen = -1) - { - if(slen < 0) - { - slen = strlen(str); - if(!grow(slen, "%s", str)) memcpy(text, str, slen + 1); - } - else - { - grow(slen); - memcpy(text, str, slen); - text[slen] = '\0'; - } - len = slen; - } - - void prepend(const char *str) - { - int slen = strlen(str); - if(!grow(slen + len, "%s%s", str, text ? text : "")) - { - memmove(&text[slen], text, len + 1); - memcpy(text, str, slen + 1); - } - len += slen; - } - - void append(const char *str) - { - int slen = strlen(str); - if(!grow(len + slen, "%s%s", text ? text : "", str)) memcpy(&text[len], str, slen + 1); - len += slen; - } - - bool read(stream *f, int chop = -1) - { - if(chop < 0) chop = INT_MAX; else chop++; - set(""); - while(len + 1 < chop && f->getline(&text[len], min(maxlen, chop) - len)) - { - len += strlen(&text[len]); - if(len > 0 && text[len-1] == '\n') - { - text[--len] = '\0'; - return true; - } - if(len + 1 >= maxlen && len + 1 < chop) grow(len + CHUNKSIZE, "%s", text); - } - if(len + 1 >= chop) - { - char buf[CHUNKSIZE]; - while(f->getline(buf, sizeof(buf))) - { - int blen = strlen(buf); - if(blen > 0 && buf[blen-1] == '\n') return true; - } - } - return len > 0; - } - - void del(int start, int count) - { - if(!text) return; - if(start < 0) { count += start; start = 0; } - if(count <= 0 || start >= len) return; - if(start + count > len) count = len - start - 1; - memmove(&text[start], &text[start+count], len + 1 - (start + count)); - len -= count; - } - - void chop(int newlen) - { - if(!text) return; - len = clamp(newlen, 0, len); - text[len] = '\0'; - } - - void insert(char *str, int start, int count = 0) - { - if(count <= 0) count = strlen(str); - start = clamp(start, 0, len); - grow(len + count, "%s", text ? text : ""); - memmove(&text[start + count], &text[start], len - start + 1); - memcpy(&text[start], str, count); - len += count; - } - - void combinelines(vector &src) - { - if(src.empty()) set(""); - else loopv(src) - { - if(i) append("\n"); - if(!i) set(src[i].text, src[i].len); - else insert(src[i].text, len, src[i].len); - } - } -}; - -enum { EDITORFOCUSED = 1, EDITORUSED, EDITORFOREVER }; - -struct editor -{ - int mode; //editor mode - 1= keep while focused, 2= keep while used in gui, 3= keep forever (i.e. until mode changes) - bool active, rendered; - const char *name; - const char *filename; - - int cx, cy; // cursor position - ensured to be valid after a region() or currentline() - int mx, my; // selection mark, mx=-1 if following cursor - avoid direct access, instead use region() - int maxx, maxy; // maxy=-1 if unlimited lines, 1 if single line editor - - int scrolly; // vertical scroll offset - - bool linewrap; - int pixelwidth; // required for up/down/hit/draw/bounds - int pixelheight; // -1 for variable sized, i.e. from bounds() - - vector lines; // MUST always contain at least one line! - - editor(const char *name, int mode, const char *initval) : - mode(mode), active(true), rendered(false), name(newstring(name)), filename(NULL), - cx(0), cy(0), mx(-1), maxx(-1), maxy(-1), scrolly(0), linewrap(false), pixelwidth(-1), pixelheight(-1) - { - //printf("editor %08x '%s'\n", this, name); - lines.add().set(initval ? initval : ""); - } - - ~editor() - { - //printf("~editor %08x '%s'\n", this, name); - DELETEA(name); - DELETEA(filename); - clear(NULL); - } - - bool empty() { return lines.length() == 1 && lines[0].empty(); } - - void clear(const char *init = "") - { - inputmillis = totalmillis; - cx = cy = 0; - mark(false); - loopv(lines) lines[i].clear(); - lines.shrink(0); - if(init) lines.add().set(init); - } - - void init(const char *inittext) - { - if(strcmp(lines[0].text, inittext)) clear(inittext); - } - - void updateheight() - { - int width; - text::measure(lines[0].text, pixelwidth, width, pixelheight); - } - - void setfile(const char *fname) - { - DELETEA(filename); - if(fname) filename = newstring(fname); - } - - void load() - { - if(!filename) return; - clear(NULL); - stream *file = openfile(filename, "r"); - if(file) - { - while(lines.add().read(file, maxx) && (maxy < 0 || lines.length() <= maxy)); - lines.pop().clear(); - delete file; - } - if(lines.empty()) lines.add().set(""); - } - - void save() - { - if(!filename) return; - stream *file = openfile(filename, "w"); - if(!file) return; - loopv(lines) file->putline(lines[i].text); - delete file; - } - - void mark(bool enable) - { - mx = (enable) ? cx : -1; - my = cy; - } - - void selectall() - { - mx = my = INT_MAX; - cx = cy = 0; - } - - // constrain results to within buffer - s=start, e=end, return true if a selection range - // also ensures that cy is always within lines[] and cx is valid - bool region(int &sx, int &sy, int &ex, int &ey) - { - int n = lines.length(); - ASSERT(n != 0); - if(cy < 0) cy = 0; else if(cy >= n) cy = n-1; - int len = lines[cy].len; - if(cx < 0) cx = 0; else if(cx > len) cx = len; - if(mx >= 0) - { - if(my < 0) my = 0; else if(my >= n) my = n-1; - len = lines[my].len; - if(mx > len) mx = len; - sx = mx; sy = my; - } - else { sx = cx; sy = cy; } - ex = cx; - ey = cy; - if(sy > ey) { swap(sy, ey); swap(sx, ex); } - else if(sy==ey && sx > ex) swap(sx, ex); - if(mx >= 0) ex++; - return (sx != ex) || (sy != ey); - } - - bool region() { int sx, sy, ex, ey; return region(sx, sy, ex, ey); } - - // also ensures that cy is always within lines[] and cx is valid - editline ¤tline() - { - int n = lines.length(); - ASSERT(n != 0); - if(cy < 0) cy = 0; else if(cy >= n) cy = n-1; - if(cx < 0) cx = 0; else if(cx > lines[cy].len) cx = lines[cy].len; - return lines[cy]; - } - - void copyselectionto(editor *b) - { - if(b==this) return; - - b->clear(NULL); - int sx, sy, ex, ey; - region(sx, sy, ex, ey); - loopi(1+ey-sy) - { - if(b->maxy != -1 && b->lines.length() >= b->maxy) break; - int y = sy+i; - char *line = lines[y].text; - int len = lines[y].len; - if(y == sy && y == ey) - { - line += sx; - len = ex - sx; - } - else if(y == sy) line += sx; - else if(y == ey) len = ex; - b->lines.add().set(line, len); - } - if(b->lines.empty()) b->lines.add().set(""); - } - - char *tostring() - { - int len = 0; - loopv(lines) len += lines[i].len + 1; - char *str = newstring(len); - int offset = 0; - loopv(lines) - { - editline &l = lines[i]; - memcpy(&str[offset], l.text, l.len); - offset += l.len; - str[offset++] = '\n'; - } - str[offset] = '\0'; - return str; - } - - char *selectiontostring() - { - vector buf; - int sx, sy, ex, ey; - region(sx, sy, ex, ey); - loopi(1+ey-sy) - { - int y = sy+i; - char *line = lines[y].text; - int len = lines[y].len; - if(y == sy && y == ey) - { - line += sx; - len = ex - sx; - } - else if(y == sy) line += sx; - else if(y == ey) len = ex; - buf.put(line, len); - buf.add('\n'); - } - buf.add('\0'); - return newstring(buf.getbuf(), buf.length()-1); - } - - void removelines(int start, int count) - { - loopi(count) lines[start+i].clear(); - lines.remove(start, count); - } - - bool del() // removes the current selection (if any) - { - inputmillis = totalmillis; - int sx, sy, ex, ey; - if(!region(sx, sy, ex, ey)) - { - mark(false); - return false; - } - if(sy == ey) - { - if(sx == 0 && ex == lines[ey].len) removelines(sy, 1); - else lines[sy].del(sx, ex - sx); - } - else - { - if(ey > sy+1) { removelines(sy+1, ey-(sy+1)); ey = sy+1; } - if(ex == lines[ey].len) removelines(ey, 1); else lines[ey].del(0, ex); - if(sx == 0) removelines(sy, 1); else lines[sy].del(sx, lines[sy].len - sx); - } - if(lines.empty()) lines.add().set(""); - mark(false); - cx = sx; - cy = sy; - editline ¤t = currentline(); - if(cx >= current.len && cy < lines.length() - 1) - { - current.append(lines[cy+1].text); - removelines(cy + 1, 1); - } - return true; - } - - void insert(char ch) - { - del(); - editline ¤t = currentline(); - if(ch == '\n') - { - if(maxy == -1 || cy < maxy-1) - { - editline newline(¤t.text[cx]); - current.chop(cx); - cy = min(lines.length(), cy+1); - lines.insert(cy, newline); - } - else current.chop(cx); - cx = 0; - } - else - { - int len = current.len; - if(maxx >= 0 && len > maxx-1) len = maxx-1; - if(cx <= len) current.insert(&ch, cx++, 1); - } - } - - void insert(const char *s) - { - while(*s) insert(*s++); - } - - void insertallfrom(editor *b) - { - if(b==this) return; - - del(); - - if(b->lines.length() == 1 || maxy == 1) - { - editline ¤t = currentline(); - char *str = b->lines[0].text; - int slen = b->lines[0].len; - if(maxx >= 0 && b->lines[0].len + cx > maxx) slen = maxx-cx; - if(slen > 0) - { - int len = current.len; - if(maxx >= 0 && slen + cx + len > maxx) len = max(0, maxx-(cx+slen)); - current.insert(str, cx, slen); - cx += slen; - } - } - else - { - loopv(b->lines) - { - if(!i) - { - lines[cy++].append(b->lines[i].text); - } - else if(i >= b->lines.length()) - { - cx = b->lines[i].len; - lines[cy].prepend(b->lines[i].text); - } - else if(maxy < 0 || lines.length() < maxy) lines.insert(cy++, editline(b->lines[i].text)); - } - } - } - - void scrollup() - { - inputmillis = totalmillis; - cy--; - } - - void scrolldown() - { - inputmillis = totalmillis; - cy++; - } - - void key(int code) - { - inputmillis = totalmillis; - switch(code) - { - case SDLK_UP: - if(linewrap) - { - int x, y; - char *str = currentline().text; - text::pos(str, cx+1, x, y, pixelwidth); - if(y > 0) { cx = text::visible(str, x, y-FONTH, pixelwidth); break; } - } - cy--; - break; - case SDLK_DOWN: - if(linewrap) - { - int x, y, width, height; - char *str = currentline().text; - text::pos(str, cx, x, y, pixelwidth); - text::measure(str, pixelwidth, width, height); - y += FONTH; - if(y < height) { cx = text::visible(str, x, y, pixelwidth); break; } - } - cy++; - break; - case SDLK_PAGEUP: - cy-=pixelheight/FONTH; - break; - case SDLK_PAGEDOWN: - cy+=pixelheight/FONTH; - break; - case SDLK_HOME: - cx = cy = 0; - break; - case SDLK_END: - cx = cy = INT_MAX; - break; - case SDLK_LEFT: - { - editline ¤t = currentline(); - cx -= uni_prevchar(current.text, cx); - break; - } - case SDLK_RIGHT: - { - editline ¤t = currentline(); - uint _codepoint; - cx += uni_getchar(¤t.text[cx], _codepoint); - break; - } - case SDLK_DELETE: - if(!del()) - { - editline ¤t = currentline(); - if(cx < current.len) - { - uint _codepoint; - const int s = uni_getchar(¤t.text[cx], _codepoint); - current.del(cx, s); - } - else if(cy < lines.length()-1) - { //combine with next line - current.append(lines[cy+1].text); - removelines(cy+1, 1); - } - } - break; - case SDLK_BACKSPACE: - if(!del()) - { - editline ¤t = currentline(); - if(cx > 0) - { - const int s = uni_prevchar(current.text, cx); - cx -= s; - current.del(cx, s); - } - else if(cy > 0) - { //combine with previous line - cx = lines[cy-1].len; - lines[cy-1].append(current.text); - removelines(cy--, 1); - } - } - break; - case SDLK_LSHIFT: - case SDLK_RSHIFT: - break; - case SDLK_RETURN: - insert('\n'); - break; - case SDLK_TAB: - insert('\t'); - break; - } - } - - void input(const char *str, int len) - { - loopi(len) insert(str[i]); - } - - void hit(int hitx, int hity, bool dragged) - { - inputmillis = totalmillis; - int maxwidth = linewrap?pixelwidth:-1; - int h = 0; - for(int i = scrolly; i < lines.length(); i++) - { - int width, height; - text::measure(lines[i].text, maxwidth, width, height); - if(h + height > pixelheight) break; - - if(hity >= h && hity <= h+height) - { - int x = text::visible(lines[i].text, hitx, hity-h, maxwidth); - if(dragged) { mx = x; my = i; } else { cx = x; cy = i; }; - break; - } - h+=height; - } - } - - int limitscrolly() - { - int maxwidth = linewrap?pixelwidth:-1; - int slines = lines.length(); - for(int ph = pixelheight; slines > 0 && ph > 0;) - { - int width, height; - text::measure(lines[slines-1].text, maxwidth, width, height); - if(height > ph) break; - ph -= height; - slines--; - } - return slines; - } - - void draw(int x, int y, int color, bool hit) - { - int maxwidth = linewrap?pixelwidth:-1; - - int sx, sy, ex, ey; - bool selection = region(sx, sy, ex, ey); - - // fix scrolly so that is always on screen - if(cy < scrolly) scrolly = cy; - else - { - if(scrolly < 0) scrolly = 0; - int h = 0; - for(int i = cy; i >= scrolly; i--) - { - int width, height; - text::measure(lines[i].text, maxwidth, width, height); - if(h + height > pixelheight) { scrolly = i+1; break; } - h += height; - } - } - - if(selection) - { - // convert from cursor coords into pixel coords - int psx, psy, pex, pey; - text::pos(lines[sy].text, sx, psx, psy, maxwidth); - text::pos(lines[ey].text, ex, pex, pey, maxwidth); - int maxy = lines.length(); - int h = 0; - for(int i = scrolly; i < maxy; i++) - { - int width, height; - text::measure(lines[i].text, maxwidth, width, height); - if(h + height > pixelheight) { maxy = i; break; } - if(i == sy) psy += h; - if(i == ey) { pey += h; break; } - h += height; - } - maxy--; - - if(ey >= scrolly && sy <= maxy) - { - // crop top/bottom within window - if(sy < scrolly) { sy = scrolly; psy = 0; psx = 0; } - if(ey > maxy) { ey = maxy; pey = pixelheight - FONTH; pex = pixelwidth; } - - hudnotextureshader->set(); - gle::colorub(0xA0, 0x80, 0x80); - gle::defvertex(2); - gle::begin(GL_QUADS); - if(psy == pey) - { - gle::attribf(x+psx, y+psy); - gle::attribf(x+pex, y+psy); - gle::attribf(x+pex, y+pey+FONTH); - gle::attribf(x+psx, y+pey+FONTH); - } - else - { gle::attribf(x+psx, y+psy); - gle::attribf(x+psx, y+psy+FONTH); - gle::attribf(x+pixelwidth, y+psy+FONTH); - gle::attribf(x+pixelwidth, y+psy); - if(pey-psy > FONTH) - { - gle::attribf(x, y+psy+FONTH); - gle::attribf(x+pixelwidth, y+psy+FONTH); - gle::attribf(x+pixelwidth, y+pey); - gle::attribf(x, y+pey); - } - gle::attribf(x, y+pey); - gle::attribf(x, y+pey+FONTH); - gle::attribf(x+pex, y+pey+FONTH); - gle::attribf(x+pex, y+pey); - } - gle::end(); - } - } - - int h = 0; - for(int i = scrolly; i < lines.length(); i++) - { - const text::Label label = text::prepare(lines[i].text, maxwidth, bvec(255, 255, 255), hit&&(cy==i)?cx:-1); - if(h + label.height() > pixelheight) break; - - if(label.valid()) label.draw(x, y); - - if(linewrap && label.height() > FONTH) // line wrap indicator - { - hudnotextureshader->set(); - gle::colorub(0x80, 0xA0, 0x80); - gle::defvertex(2); - gle::begin(GL_TRIANGLE_STRIP); - gle::attribf(x, y+h+FONTH); - gle::attribf(x, y+h+label.height()); - gle::attribf(x-FONTW/2, y+h+FONTH); - gle::attribf(x-FONTW/2, y+h+label.height()); - gle::end(); - } - h+=label.height(); - } - } -}; - -static vector editors; -static editor *textfocus = NULL; - -static void readyeditors() -{ - loopv(editors) editors[i]->active = (editors[i]->mode==EDITORFOREVER); -} - -static void flusheditors() -{ - loopvrev(editors) if(!editors[i]->active) - { - editor *e = editors.remove(i); - if(e == textfocus) textfocus = NULL; - delete e; - } -} - -static editor *useeditor(const char *name, int mode, bool focus, const char *initval = NULL) -{ - loopv(editors) if(!strcmp(editors[i]->name, name)) - { - editor *e = editors[i]; - if(focus) textfocus = e; - e->active = true; - return e; - } - if(mode < 0) return NULL; - editor *e = new editor(name, mode, initval); - editors.add(e); - if(focus) textfocus = e; - return e; -} - - -#define TEXTCOMMAND(f, s, d, body) ICOMMAND(f, s, d,\ - if(!textfocus || identflags&IDF_OVERRIDDEN) return;\ - body\ -) - -ICOMMAND(textlist, "", (), // @DEBUG return list of all the editors - vector s; - loopv(editors) - { - if(i > 0) s.put(", ", 2); - s.put(editors[i]->name, strlen(editors[i]->name)); - } - s.add('\0'); - result(s.getbuf()); -); -TEXTCOMMAND(textshow, "", (), // @DEBUG return the start of the buffer - editline line; - line.combinelines(textfocus->lines); - result(line.text); - line.clear(); -); -ICOMMAND(textfocus, "si", (char *name, int *mode), // focus on a (or create a persistent) specific editor, else returns current name - if(identflags&IDF_OVERRIDDEN) return; - if(*name) useeditor(name, *mode<=0 ? EDITORFOREVER : *mode, true); - else if(editors.length() > 0) result(editors.last()->name); -); -TEXTCOMMAND(textprev, "", (), editors.insert(0, textfocus); editors.pop();); // return to the previous editor -TEXTCOMMAND(textmode, "i", (int *m), // (1= keep while focused, 2= keep while used in gui, 3= keep forever (i.e. until mode changes)) topmost editor, return current setting if no args - if(*m) textfocus->mode = *m; - else intret(textfocus->mode); -); -TEXTCOMMAND(textsave, "s", (char *file), // saves the topmost (filename is optional) - if(*file) textfocus->setfile(path(file, true)); - textfocus->save(); -); -TEXTCOMMAND(textload, "s", (char *file), // loads into the textfocusmost editor, returns filename if no args - if(*file) - { - textfocus->setfile(path(file, true)); - textfocus->load(); - } - else if(textfocus->filename) result(textfocus->filename); -); -ICOMMAND(textinit, "sss", (char *name, char *file, char *initval), // loads into named editor if no file assigned and editor has been rendered -{ - if(identflags&IDF_OVERRIDDEN) return; - editor *e = NULL; - loopv(editors) if(!strcmp(editors[i]->name, name)) { e = editors[i]; break; } - if(e && e->rendered && !e->filename && *file && (e->lines.empty() || (e->lines.length() == 1 && !strcmp(e->lines[0].text, initval)))) - { - e->setfile(path(file, true)); - e->load(); - } -}); - -#define PASTEBUFFER "#pastebuffer" - -TEXTCOMMAND(textcopy, "", (), editor *b = useeditor(PASTEBUFFER, EDITORFOREVER, false); textfocus->copyselectionto(b);); -TEXTCOMMAND(textpaste, "", (), editor *b = useeditor(PASTEBUFFER, EDITORFOREVER, false); textfocus->insertallfrom(b);); -TEXTCOMMAND(textmark, "i", (int *m), // (1=mark, 2=unmark), return current mark setting if no args - if(*m) textfocus->mark(*m==1); - else intret(textfocus->region() ? 1 : 2); -); -TEXTCOMMAND(textselectall, "", (), textfocus->selectall();); -TEXTCOMMAND(textclear, "", (), textfocus->clear();); -TEXTCOMMAND(textcurrentline, "", (), result(textfocus->currentline().text);); - -TEXTCOMMAND(textexec, "i", (int *selected), // execute script commands from the buffer (0=all, 1=selected region only) - char *script = *selected ? textfocus->selectiontostring() : textfocus->tostring(); - execute(script); - delete[] script; -); - diff --git a/source/engine/ui.cpp b/source/engine/ui.cpp index 8a2e02683..371e10877 100644 --- a/source/engine/ui.cpp +++ b/source/engine/ui.cpp @@ -1,5 +1,4 @@ #include "engine.h" -#include "textedit.h" namespace UI { @@ -2896,289 +2895,6 @@ namespace UI int wheelscrolldirection() const { return -1; } }; - struct TextEditor : Object - { - static TextEditor *focus; - - float scale, offsetx, offsety; - editor *edit; - char *keyfilter; - - TextEditor() : edit(NULL), keyfilter(NULL) {} - - void setup(const char *name, int length, int height, float scale_ = 1, const char *initval = NULL, int mode = EDITORUSED, const char *keyfilter_ = NULL) - { - Object::setup(); - setfontsize(scale * hudh); - editor *edit_ = useeditor(name, mode, false, initval); - if(edit_ != edit) - { - if(edit) clearfocus(); - edit = edit_; - } - else if(isfocus() && !hasstate(STATE_HOVER)) commit(); - if(initval && edit->mode == EDITORFOCUSED && !isfocus()) edit->init(initval); - edit->active = true; - edit->linewrap = length < 0; - edit->maxx = edit->linewrap ? -1 : length; - edit->maxy = height <= 0 ? 1 : -1; - edit->pixelwidth = abs(length)*FONTW; - if(edit->linewrap && edit->maxy == 1) edit->updateheight(); - else edit->pixelheight = FONTH*1.5*max(height, 1); - scale = scale_; - if(keyfilter_) SETSTR(keyfilter, keyfilter_); - else DELETEA(keyfilter); - } - ~TextEditor() - { - clearfocus(); - DELETEA(keyfilter); - } - - static void setfocus(TextEditor *e) - { - if(focus == e) return; - focus = e; - bool allowtextinput = focus!=NULL && focus->allowtextinput(); - ::textinput(allowtextinput, TI_GUI); - ::keyrepeat(allowtextinput, KR_GUI); - } - void setfocus() { setfocus(this); } - void clearfocus() { if(focus == this) setfocus(NULL); } - bool isfocus() const { return focus == this; } - - static const char *typestr() { return "#TextEditor"; } - const char *gettype() const { return typestr(); } - - bool target(float cx, float cy) - { - return true; - } - - float drawscale() const { return 1.f / hudh; } - - void draw(float sx, float sy) - { - changedraw(CHANGE_SHADER | CHANGE_COLOR); - - edit->rendered = true; - - setfontsize(scale * hudh); - - float k = drawscale(); - pushhudtranslate(sx, sy, k); - - edit->draw(FONTW/2, 0, 0xFFFFFF, isfocus()); - - pophudmatrix(); - - Object::draw(sx, sy); - } - - void layout() - { - Object::layout(); - - setfontsize(scale * hudh); - - float k = drawscale(); - w = max(w, (edit->pixelwidth + (float)FONTW)*k); - h = max(h, edit->pixelheight*k); - } - - virtual void resetmark(float cx, float cy) - { - edit->mark(false); - offsetx = cx; - offsety = cy; - } - - void press(float cx, float cy) - { - setfocus(); - resetmark(cx, cy); - } - - void hold(float cx, float cy) - { - if(isfocus()) - { - setfontsize(scale * hudh); - - float k = drawscale(); - bool dragged = max(fabs(cx - offsetx), fabs(cy - offsety)) > (FONTH/8.0f)*k; - edit->hit(int(floor(cx/k - FONTW/2)), int(floor(cy/k)), dragged); - } - } - - void scrollup(float cx, float cy) - { - edit->scrollup(); - } - - void scrolldown(float cx, float cy) - { - edit->scrolldown(); - } - - virtual void cancel() - { - clearfocus(); - } - - virtual void commit() - { - clearfocus(); - } - - bool key(int code, bool isdown) - { - if(Object::key(code, isdown)) return true; - if(!isfocus()) return false; - switch(code) - { - case SDLK_ESCAPE: - if(isdown) cancel(); - return true; - case SDLK_RETURN: - case SDLK_TAB: - if(edit->maxy != 1) break; - // fall-through - case SDLK_KP_ENTER: - if(isdown) commit(); - return true; - } - if(isdown) edit->key(code); - return true; - } - - virtual bool allowtextinput() const { return true; } - - bool textinput(const char *str, int len) - { - if(Object::textinput(str, len)) return true; - if(!isfocus() || !allowtextinput()) return false; - if(!keyfilter) edit->input(str, len); - else while(len > 0) - { - int accept = min(len, (int)strspn(str, keyfilter)); - if(accept > 0) edit->input(str, accept); - str += accept + 1; - len -= accept + 1; - if(len <= 0) break; - int reject = (int)strcspn(str, keyfilter); - str += reject; - str -= reject; - } - return true; - } - }; - - TextEditor *TextEditor::focus = NULL; - - static const char *getsval(ident *id, bool &shouldfree, const char *val = "") - { - switch(id->type) - { - case ID_VAR: val = intstr(*id->storage.i); break; - case ID_FVAR: val = floatstr(*id->storage.f); break; - case ID_SVAR: val = *id->storage.s; break; - case ID_ALIAS: val = id->getstr(); break; - case ID_COMMAND: val = executestr(id, NULL, 0, true); shouldfree = true; break; - } - return val; - } - - static void setsval(ident *id, const char *val, uint *onchange = NULL) - { - switch(id->type) - { - case ID_VAR: setvarchecked(id, parseint(val)); break; - case ID_FVAR: setfvarchecked(id, parsefloat(val)); break; - case ID_SVAR: setsvarchecked(id, val); break; - case ID_ALIAS: alias(id->name, val); break; - case ID_COMMAND: - { - tagval t; - t.setstr(newstring(val)); - execute(id, &t, 1); - break; - } - } - if(onchange && (*onchange&CODE_OP_MASK) != CODE_EXIT) execute(onchange); - } - - struct Field : TextEditor - { - ident *id; - bool changed; - - Field() : id(NULL), changed(false) {} - - void setup(ident *id_, int length, uint *onchange, float scale = 1, const char *keyfilter_ = NULL) - { - if(isfocus() && !hasstate(STATE_HOVER)) commit(); - if(changed) - { - if(edit->lines.length()) if(id == id_) setsval(id, edit->lines[0].text, onchange); - changed = false; - } - bool shouldfree = false; - const char *initval = id != id_ || !isfocus() ? getsval(id_, shouldfree) : NULL; - TextEditor::setup(id_->name, length, 0, scale, initval, EDITORFOCUSED, keyfilter_); - if(shouldfree) delete[] initval; - id = id_; - } - - static const char *typestr() { return "#Field"; } - const char *gettype() const { return typestr(); } - - void commit() - { - TextEditor::commit(); - changed = true; - } - - void cancel() - { - TextEditor::cancel(); - changed = false; - } - }; - - struct KeyField : Field - { - static const char *typestr() { return "#KeyField"; } - const char *gettype() const { return typestr(); } - - void resetmark(float cx, float cy) - { - edit->clear(); - Field::resetmark(cx, cy); - } - - void insertkey(int code) - { - const char *keyname = getkeyname(code); - if(keyname) - { - if(!edit->empty()) edit->insert(" "); - edit->insert(keyname); - } - } - - bool rawkey(int code, bool isdown) - { - if(Object::rawkey(code, isdown)) return true; - if(!isfocus() || !isdown) return false; - if(code == SDLK_ESCAPE) commit(); - else insertkey(code); - return true; - } - - bool allowtextinput() const { return false; } - }; - struct Preview : Target { void startdraw() @@ -3533,15 +3249,6 @@ namespace UI DOSTATES #undef DOSTATE - ICOMMANDNS("uifocus", uifocus_, "ee", (uint *t, uint *f), - executeret(buildparent && TextEditor::focus == buildparent ? t : f)); - ICOMMANDNS("uifocus?", uifocus__, "tt", (tagval *t, tagval *f), - IFSTATEVAL(buildparent && TextEditor::focus == buildparent, t, f)); - ICOMMANDNS("uifocus+", uinextfocus_, "ee", (uint *t, uint *f), - executeret(buildparent && buildparent->children.inrange(buildchild) && TextEditor::focus == buildparent->children[buildchild] ? t : f)); - ICOMMANDNS("uifocus+?", uinextfocus__, "tt", (tagval *t, tagval *f), - IFSTATEVAL(buildparent && buildparent->children.inrange(buildchild) && TextEditor::focus == buildparent->children[buildchild], t, f)); - ICOMMAND(uialign, "ii", (int *xalign, int *yalign), { if(buildparent) buildparent->setalign(*xalign, *yalign); @@ -3777,9 +3484,6 @@ namespace UI ICOMMAND(uiwrapcontext, "tffe", (tagval *text, float *wrap, float *scale, uint *children), buildtext(*text, *scale, FONTH*uicontextscale, Color(255, 255, 255), *wrap, -1, false, children)); - ICOMMAND(uitexteditor, "siifsie", (char *name, int *length, int *height, float *scale, char *initval, int *mode, uint *children), - BUILD(TextEditor, o, o->setup(name, *length, *height, (*scale <= 0 ? 1 : *scale) * uitextscale, initval, *mode <= 0 ? EDITORFOREVER : *mode), children)); - ICOMMAND(uifont, "se", (char *name, uint *children), BUILD(Font, o, o->setup(name), children)); @@ -3788,12 +3492,6 @@ namespace UI ICOMMAND(uiconsole, "ffe", (float *minw, float *minh, uint *children), BUILD(Console, o, o->setup(*minw, *minh), children)); - ICOMMAND(uifield, "riefe", (ident *var, int *length, uint *onchange, float *scale, uint *children), - BUILD(Field, o, o->setup(var, *length, onchange, (*scale <= 0 ? 1 : *scale) * uitextscale), children)); - - ICOMMAND(uikeyfield, "riefe", (ident *var, int *length, uint *onchange, float *scale, uint *children), - BUILD(KeyField, o, o->setup(var, *length, onchange, (*scale <= 0 ? 1 : *scale) * uitextscale), children)); - ICOMMAND(uiminimap, "ffffie", (float *ox, float *oy, float *yaw, float *size, int *sides, uint *children), BUILD(Minimap, o, o->setup(vec(*ox, *oy, 0), *yaw, *size, *sides), children)); @@ -3972,8 +3670,6 @@ namespace UI cursorlockedx = false; cursorlockedy = false; - readyeditors(); - world->setstate(STATE_HOVER, cursorx, cursory, world->childstate&STATE_HOLD_MASK); if(world->childstate&STATE_HOLD) world->setstate(STATE_HOLD, cursorx, cursory, STATE_HOLD, false); if(world->childstate&STATE_ALT_HOLD) world->setstate(STATE_ALT_HOLD, cursorx, cursory, STATE_ALT_HOLD, false); @@ -3984,8 +3680,6 @@ namespace UI world->build(); if(!mousetracking) mousetrackvec = vec2(0, 0); - - flusheditors(); } void render() From 843fdc392cd458d31efb07c0c25b1863add91140 Mon Sep 17 00:00:00 2001 From: Jed- Date: Tue, 4 Feb 2025 15:28:32 +0100 Subject: [PATCH 63/68] Update Makefile --- source/Makefile | 98 +++++++++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 43 deletions(-) diff --git a/source/Makefile b/source/Makefile index 4e60770fd..e3f472f6f 100644 --- a/source/Makefile +++ b/source/Makefile @@ -466,84 +466,92 @@ engine/worldio.o: shared/igame.h engine/world.h engine/octa.h engine/light.h engine/worldio.o: engine/texture.h engine/bih.h engine/model.h game/ai.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/ai.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h -game/ai.o: engine/sound.h shared/iengine.h shared/igame.h game/projectile.h -game/ai.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h -game/ai.o: game/monster.h +game/ai.o: engine/sound.h shared/iengine.h shared/igame.h shared/unicode.h +game/ai.o: game/projectile.h game/weapon.h game/ai.h game/gamemode.h +game/ai.o: game/entity.h game/announcer.h game/monster.h game/gameclient.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/gameclient.o: shared/ents.h shared/command.h shared/glexts.h game/gameclient.o: shared/glemu.h engine/sound.h shared/iengine.h -game/gameclient.o: shared/igame.h game/projectile.h game/weapon.h game/ai.h -game/gameclient.o: game/gamemode.h game/entity.h game/monster.h game/ctf.h +game/gameclient.o: shared/igame.h shared/unicode.h game/projectile.h +game/gameclient.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h +game/gameclient.o: game/announcer.h game/monster.h game/ctf.h game/gameclient.o: game/elimination.h game/entity.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/entity.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h game/entity.o: engine/sound.h shared/iengine.h shared/igame.h -game/entity.o: game/projectile.h game/weapon.h game/ai.h game/gamemode.h -game/entity.o: game/entity.h game/monster.h +game/entity.o: shared/unicode.h game/projectile.h game/weapon.h game/ai.h +game/entity.o: game/gamemode.h game/entity.h game/announcer.h game/monster.h game/game.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/game.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h -game/game.o: engine/sound.h shared/iengine.h shared/igame.h game/projectile.h -game/game.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h -game/game.o: game/monster.h +game/game.o: engine/sound.h shared/iengine.h shared/igame.h shared/unicode.h +game/game.o: game/projectile.h game/weapon.h game/ai.h game/gamemode.h +game/game.o: game/entity.h game/announcer.h game/monster.h game/render.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/render.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h game/render.o: engine/sound.h shared/iengine.h shared/igame.h -game/render.o: game/projectile.h game/weapon.h game/ai.h game/gamemode.h -game/render.o: game/entity.h game/monster.h +game/render.o: shared/unicode.h game/projectile.h game/weapon.h game/ai.h +game/render.o: game/gamemode.h game/entity.h game/announcer.h game/monster.h game/scoreboard.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/scoreboard.o: shared/ents.h shared/command.h shared/glexts.h game/scoreboard.o: shared/glemu.h engine/sound.h shared/iengine.h -game/scoreboard.o: shared/igame.h game/projectile.h game/weapon.h game/ai.h -game/scoreboard.o: game/gamemode.h game/entity.h game/monster.h +game/scoreboard.o: shared/igame.h shared/unicode.h game/projectile.h +game/scoreboard.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h +game/scoreboard.o: game/announcer.h game/monster.h game/gameserver.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/gameserver.o: shared/ents.h shared/command.h shared/glexts.h game/gameserver.o: shared/glemu.h engine/sound.h shared/iengine.h -game/gameserver.o: shared/igame.h game/projectile.h game/weapon.h game/ai.h -game/gameserver.o: game/gamemode.h game/entity.h game/monster.h game/geoip.h -game/gameserver.o: game/ctf.h game/elimination.h game/extinfo.h -game/gameserver.o: game/aimanager.h +game/gameserver.o: shared/igame.h shared/unicode.h game/projectile.h +game/gameserver.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h +game/gameserver.o: game/announcer.h game/monster.h game/geoip.h game/ctf.h +game/gameserver.o: game/elimination.h game/extinfo.h game/aimanager.h game/waypoint.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/waypoint.o: shared/ents.h shared/command.h shared/glexts.h game/waypoint.o: shared/glemu.h engine/sound.h shared/iengine.h -game/waypoint.o: shared/igame.h game/projectile.h game/weapon.h game/ai.h -game/waypoint.o: game/gamemode.h game/entity.h game/monster.h +game/waypoint.o: shared/igame.h shared/unicode.h game/projectile.h +game/waypoint.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h +game/waypoint.o: game/announcer.h game/monster.h game/monster.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/monster.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h game/monster.o: engine/sound.h shared/iengine.h shared/igame.h -game/monster.o: game/projectile.h game/weapon.h game/ai.h game/gamemode.h -game/monster.o: game/entity.h game/monster.h +game/monster.o: shared/unicode.h game/projectile.h game/weapon.h game/ai.h +game/monster.o: game/gamemode.h game/entity.h game/announcer.h game/monster.h game/weapon.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/weapon.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h game/weapon.o: engine/sound.h shared/iengine.h shared/igame.h -game/weapon.o: game/projectile.h game/weapon.h game/ai.h game/gamemode.h -game/weapon.o: game/entity.h game/monster.h +game/weapon.o: shared/unicode.h game/projectile.h game/weapon.h game/ai.h +game/weapon.o: game/gamemode.h game/entity.h game/announcer.h game/monster.h game/gamephysics.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/gamephysics.o: shared/ents.h shared/command.h shared/glexts.h game/gamephysics.o: shared/glemu.h engine/sound.h shared/iengine.h -game/gamephysics.o: shared/igame.h game/projectile.h game/weapon.h game/ai.h -game/gamephysics.o: game/gamemode.h game/entity.h game/monster.h +game/gamephysics.o: shared/igame.h shared/unicode.h game/projectile.h +game/gamephysics.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h +game/gamephysics.o: game/announcer.h game/monster.h game/hud.o: engine/engine.h shared/cube.h shared/tools.h shared/geom.h game/hud.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h game/hud.o: engine/sound.h shared/iengine.h shared/igame.h engine/world.h game/hud.o: engine/octa.h engine/light.h engine/texture.h engine/bih.h -game/hud.o: engine/model.h game/game.h game/projectile.h game/weapon.h -game/hud.o: game/ai.h game/gamemode.h game/entity.h game/monster.h +game/hud.o: engine/model.h game/game.h shared/unicode.h game/projectile.h +game/hud.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h +game/hud.o: game/announcer.h game/monster.h game/projectile.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/projectile.o: shared/ents.h shared/command.h shared/glexts.h game/projectile.o: shared/glemu.h engine/sound.h shared/iengine.h -game/projectile.o: shared/igame.h game/projectile.h game/weapon.h game/ai.h -game/projectile.o: game/gamemode.h game/entity.h game/monster.h +game/projectile.o: shared/igame.h shared/unicode.h game/projectile.h +game/projectile.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h +game/projectile.o: game/announcer.h game/monster.h game/announcer.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/announcer.o: shared/ents.h shared/command.h shared/glexts.h game/announcer.o: shared/glemu.h engine/sound.h shared/iengine.h -game/announcer.o: shared/igame.h game/projectile.h game/weapon.h game/ai.h -game/announcer.o: game/gamemode.h game/entity.h game/monster.h +game/announcer.o: shared/igame.h shared/unicode.h game/projectile.h +game/announcer.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h +game/announcer.o: game/announcer.h game/monster.h game/camera.o: engine/engine.h shared/cube.h shared/tools.h shared/geom.h game/camera.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h game/camera.o: engine/sound.h shared/iengine.h shared/igame.h engine/world.h game/camera.o: engine/octa.h engine/light.h engine/texture.h engine/bih.h -game/camera.o: engine/model.h game/game.h game/projectile.h game/weapon.h -game/camera.o: game/ai.h game/gamemode.h game/entity.h game/monster.h +game/camera.o: engine/model.h game/game.h shared/unicode.h game/projectile.h +game/camera.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h +game/camera.o: game/announcer.h game/monster.h shared/cube.h.gch: shared/tools.h shared/geom.h shared/ents.h shared/cube.h.gch: shared/command.h shared/glexts.h shared/glemu.h @@ -556,8 +564,9 @@ engine/engine.h.gch: engine/texture.h engine/bih.h engine/model.h game/game.h.gch: shared/cube.h shared/tools.h shared/geom.h shared/ents.h game/game.h.gch: shared/command.h shared/glexts.h shared/glemu.h game/game.h.gch: engine/sound.h shared/iengine.h shared/igame.h -game/game.h.gch: game/projectile.h game/game.h game/weapon.h game/ai.h -game/game.h.gch: game/gamemode.h game/entity.h game/monster.h +game/game.h.gch: shared/unicode.h game/projectile.h game/game.h game/weapon.h +game/game.h.gch: game/ai.h game/gamemode.h game/entity.h game/announcer.h +game/game.h.gch: game/monster.h standalone/shared/crypto.o: shared/cube.h shared/tools.h shared/geom.h standalone/shared/crypto.o: shared/ents.h shared/command.h engine/sound.h @@ -583,13 +592,15 @@ standalone/engine/worldio.o: engine/world.h standalone/game/entity.o: game/game.h shared/cube.h shared/tools.h standalone/game/entity.o: shared/geom.h shared/ents.h shared/command.h standalone/game/entity.o: engine/sound.h shared/iengine.h shared/igame.h -standalone/game/entity.o: game/projectile.h game/weapon.h game/ai.h -standalone/game/entity.o: game/gamemode.h game/entity.h game/monster.h +standalone/game/entity.o: shared/unicode.h game/projectile.h game/weapon.h +standalone/game/entity.o: game/ai.h game/gamemode.h game/entity.h +standalone/game/entity.o: game/announcer.h game/monster.h standalone/game/gameserver.o: game/game.h shared/cube.h shared/tools.h standalone/game/gameserver.o: shared/geom.h shared/ents.h shared/command.h standalone/game/gameserver.o: engine/sound.h shared/iengine.h shared/igame.h -standalone/game/gameserver.o: game/projectile.h game/weapon.h game/ai.h -standalone/game/gameserver.o: game/gamemode.h game/entity.h game/monster.h +standalone/game/gameserver.o: shared/unicode.h game/projectile.h +standalone/game/gameserver.o: game/weapon.h game/ai.h game/gamemode.h +standalone/game/gameserver.o: game/entity.h game/announcer.h game/monster.h standalone/game/gameserver.o: game/geoip.h game/ctf.h game/elimination.h standalone/game/gameserver.o: game/extinfo.h game/aimanager.h standalone/engine/master.o: shared/cube.h shared/tools.h shared/geom.h @@ -605,6 +616,7 @@ standalone/engine/engine.h.gch: shared/iengine.h shared/igame.h standalone/engine/engine.h.gch: engine/world.h standalone/game/game.h.gch: shared/cube.h shared/tools.h shared/geom.h standalone/game/game.h.gch: shared/ents.h shared/command.h engine/sound.h -standalone/game/game.h.gch: shared/iengine.h shared/igame.h game/projectile.h -standalone/game/game.h.gch: game/game.h game/weapon.h game/ai.h -standalone/game/game.h.gch: game/gamemode.h game/entity.h game/monster.h +standalone/game/game.h.gch: shared/iengine.h shared/igame.h shared/unicode.h +standalone/game/game.h.gch: game/projectile.h game/game.h game/weapon.h +standalone/game/game.h.gch: game/ai.h game/gamemode.h game/entity.h +standalone/game/game.h.gch: game/announcer.h game/monster.h From e57fd15525acf3d6bb2a5614fcd0c885d48736ed Mon Sep 17 00:00:00 2001 From: Jed- Date: Tue, 4 Feb 2025 15:42:47 +0100 Subject: [PATCH 64/68] Loading screen fix --- source/engine/main.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source/engine/main.cpp b/source/engine/main.cpp index 47c009420..4de89045f 100644 --- a/source/engine/main.cpp +++ b/source/engine/main.cpp @@ -255,7 +255,14 @@ void renderbackgroundview(int w, int h, const char *caption, Texture *mapshot, c popfont(); float tsz = sz/(8*FONTH); - name_label.draw(x + sz + (infowidth - name_label.width()) / 2, y); + if(mapshot && mapshot!=notexture && mapinfo) + { + name_label.draw(x + sz + (infowidth - name_label.width()) / 2, y); + } + else + { + name_label.draw(x - name_label.width() / 2, y); + } my = 1.5f*FONTH*tsz; } if(mapinfo) From 1cd6a465c42b84340d724c13038911947ee807ba Mon Sep 17 00:00:00 2001 From: Jed- Date: Tue, 4 Feb 2025 16:06:58 +0100 Subject: [PATCH 65/68] Remove usage of `tabify` --- config/ui/hud/editstats.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/ui/hud/editstats.cfg b/config/ui/hud/editstats.cfg index ae5165c15..9cc38e98b 100644 --- a/config/ui/hud/editstats.cfg +++ b/config/ui/hud/editstats.cfg @@ -335,8 +335,8 @@ uiLiteMenu "edithud" [ uigrid 2 $uiPad:3XL $uiPad:L [ uihlist $uiPad:XL [ uitext "WTR" 0.5 - uitext (tabify (format "%1k" $editstatwtr) 1) 0.5 - uitext (tabify (format "(%1%%)" $editstatvtr) 2) 0.5 + uitext (format "%1k^t" $editstatwtr) 0.5 + uitext (format "(%1%%)^t^t" $editstatvtr) 0.5 ] uihlist $uiPad:XL [ uitext "WVT" 0.5 From e0d2b1050d44091280b90d9c2d9ae247abaf7130 Mon Sep 17 00:00:00 2001 From: Jed- Date: Tue, 4 Feb 2025 16:09:14 +0100 Subject: [PATCH 66/68] Depend on `pango >= 1.56` and remove dependency on `fontconfig` On Linux, the game can still be compiled against `pango >= 1.38` and `fontconfig` by adding `-DOLDPANGO` to the compile flags. --- source/engine/rendertext.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index 65a764d42..8415bc2f0 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -2,7 +2,19 @@ #include #include -#include + +#if (!PANGO_VERSION_CHECK(1, 56, 0) || defined(OLDPANGO)) + #if (defined(WIN32) || defined(__APPLE__)) + #error this project requires pango >= 1.56 + #else + #define OLDPANGO 1 + #pragma message ("compiling with support for old pango (>= 1.38)") + #endif +#endif + +#ifdef OLDPANGO + #include +#endif static int fontid = 0; // used by UI for change detection double fontsize = 0; // pixel height of the current font @@ -79,13 +91,17 @@ static void clear_text_particles() #pragma region font_management // register a TTF file -// NOTE: on Windows and MacOs, this assumes that pango was compiled with fontconfig support, -// and that the `PANGOCAIRO_BACKEND` env var is set to `fc` or `fontconfig` void addfontfile(const char *filename) { const char *found = findfile(filename, "rb"); if(!found || !found[0]) return; - FcConfigAppFontAddFile(FcConfigGetCurrent(), (const unsigned char *)found); + //conoutf(CON_DEBUG, "Loading font file: %s", found); + #ifdef OLDPANGO + FcConfigAppFontAddFile(FcConfigGetCurrent(), (const unsigned char *)found); + #else + static PangoFontMap *fontMap = pango_cairo_font_map_get_default(); + pango_font_map_add_font_file(fontMap, found, nullptr); + #endif } COMMANDN(registerfont, addfontfile, "s"); From b9d32a741345640d2acbade3bb08d52ee7be34f4 Mon Sep 17 00:00:00 2001 From: Jed- Date: Tue, 4 Feb 2025 16:12:06 +0100 Subject: [PATCH 67/68] Remove redundant include --- source/engine/rendertext.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/source/engine/rendertext.cpp b/source/engine/rendertext.cpp index 8415bc2f0..227c3418f 100644 --- a/source/engine/rendertext.cpp +++ b/source/engine/rendertext.cpp @@ -1,6 +1,5 @@ #include "engine.h" -#include #include #if (!PANGO_VERSION_CHECK(1, 56, 0) || defined(OLDPANGO)) From 93d95eafea3b1fbbd8951560fcedd9aa8897bc65 Mon Sep 17 00:00:00 2001 From: Jed- Date: Fri, 14 Feb 2025 14:17:59 +0100 Subject: [PATCH 68/68] Update Makefile --- source/Makefile | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/source/Makefile b/source/Makefile index 2c96e36e7..e3f472f6f 100644 --- a/source/Makefile +++ b/source/Makefile @@ -500,10 +500,10 @@ game/scoreboard.o: game/announcer.h game/monster.h game/gameserver.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/gameserver.o: shared/ents.h shared/command.h shared/glexts.h game/gameserver.o: shared/glemu.h engine/sound.h shared/iengine.h -game/gameserver.o: shared/igame.h game/projectile.h game/weapon.h game/ai.h -game/gameserver.o: game/gamemode.h game/entity.h game/monster.h game/geoip.h -game/gameserver.o: game/ctf.h game/elimination.h game/extinfo.h -game/gameserver.o: game/aimanager.h game/announcer.h +game/gameserver.o: shared/igame.h shared/unicode.h game/projectile.h +game/gameserver.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h +game/gameserver.o: game/announcer.h game/monster.h game/geoip.h game/ctf.h +game/gameserver.o: game/elimination.h game/extinfo.h game/aimanager.h game/waypoint.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/waypoint.o: shared/ents.h shared/command.h shared/glexts.h game/waypoint.o: shared/glemu.h engine/sound.h shared/iengine.h @@ -542,8 +542,9 @@ game/projectile.o: game/announcer.h game/monster.h game/announcer.o: game/game.h shared/cube.h shared/tools.h shared/geom.h game/announcer.o: shared/ents.h shared/command.h shared/glexts.h game/announcer.o: shared/glemu.h engine/sound.h shared/iengine.h -game/announcer.o: shared/igame.h game/projectile.h game/weapon.h game/ai.h -game/announcer.o: game/gamemode.h game/entity.h game/monster.h game/announcer.h +game/announcer.o: shared/igame.h shared/unicode.h game/projectile.h +game/announcer.o: game/weapon.h game/ai.h game/gamemode.h game/entity.h +game/announcer.o: game/announcer.h game/monster.h game/camera.o: engine/engine.h shared/cube.h shared/tools.h shared/geom.h game/camera.o: shared/ents.h shared/command.h shared/glexts.h shared/glemu.h game/camera.o: engine/sound.h shared/iengine.h shared/igame.h engine/world.h @@ -563,8 +564,9 @@ engine/engine.h.gch: engine/texture.h engine/bih.h engine/model.h game/game.h.gch: shared/cube.h shared/tools.h shared/geom.h shared/ents.h game/game.h.gch: shared/command.h shared/glexts.h shared/glemu.h game/game.h.gch: engine/sound.h shared/iengine.h shared/igame.h -game/game.h.gch: game/projectile.h game/game.h game/weapon.h game/ai.h -game/game.h.gch: game/gamemode.h game/entity.h game/monster.h game/announcer.h +game/game.h.gch: shared/unicode.h game/projectile.h game/game.h game/weapon.h +game/game.h.gch: game/ai.h game/gamemode.h game/entity.h game/announcer.h +game/game.h.gch: game/monster.h standalone/shared/crypto.o: shared/cube.h shared/tools.h shared/geom.h standalone/shared/crypto.o: shared/ents.h shared/command.h engine/sound.h @@ -600,7 +602,7 @@ standalone/game/gameserver.o: shared/unicode.h game/projectile.h standalone/game/gameserver.o: game/weapon.h game/ai.h game/gamemode.h standalone/game/gameserver.o: game/entity.h game/announcer.h game/monster.h standalone/game/gameserver.o: game/geoip.h game/ctf.h game/elimination.h -standalone/game/gameserver.o: game/extinfo.h game/aimanager.h game/announcer.h +standalone/game/gameserver.o: game/extinfo.h game/aimanager.h standalone/engine/master.o: shared/cube.h shared/tools.h shared/geom.h standalone/engine/master.o: shared/ents.h shared/command.h engine/sound.h standalone/engine/master.o: shared/iengine.h shared/igame.h @@ -614,7 +616,7 @@ standalone/engine/engine.h.gch: shared/iengine.h shared/igame.h standalone/engine/engine.h.gch: engine/world.h standalone/game/game.h.gch: shared/cube.h shared/tools.h shared/geom.h standalone/game/game.h.gch: shared/ents.h shared/command.h engine/sound.h -standalone/game/game.h.gch: shared/iengine.h shared/igame.h game/projectile.h -standalone/game/game.h.gch: game/game.h game/weapon.h game/ai.h -standalone/game/game.h.gch: game/gamemode.h game/entity.h game/monster.h -standalone/game/game.h.gch: game/announcer.h +standalone/game/game.h.gch: shared/iengine.h shared/igame.h shared/unicode.h +standalone/game/game.h.gch: game/projectile.h game/game.h game/weapon.h +standalone/game/game.h.gch: game/ai.h game/gamemode.h game/entity.h +standalone/game/game.h.gch: game/announcer.h game/monster.h