diff --git a/CVARINFO b/CVARINFO index 311cdc9709..343362dba9 100644 --- a/CVARINFO +++ b/CVARINFO @@ -520,3 +520,63 @@ server noarchive bool CanGive = false; server noarchive int SMO = 0; server int DynamicSpawnMonsters = 1; server int levelsToPlay = 32; + +//gearbox cvars +// When adding new options, don't forget to add them to: +// - zscript/zabor/event_handler.zs:printGearboxCvars(). +// - keyconf.txt:Alias gb_reset. + +// Options ///////////////////////////////////////////////////////////////////////////////////////// + +user int gb_scale = 1; +user color gb_color = "22 22 CC"; +user color gb_dim_color = "99 99 99"; +user bool gb_show_tags = true; + +// 0 - blocky view +// 1 - wheel view +user int gb_view_type = 1; + +user bool gb_enable_dim = true; +user bool gb_enable_blur = false; +user float gb_wheel_position = 1.0; +user float gb_wheel_scale = 1.0; +user bool gb_wheel_tint = true; +user int gb_multiwheel_limit = 12; + +user float gb_blocks_position_x = 0.0; +user float gb_blocks_position_y = 0.0; + +user int gb_text_scale = 1; +user float gb_text_position_x = 0.0; +user float gb_text_position_y = 0.0; +user float gb_text_position_y_max = 1.0; +user int gb_text_usual_color = 21; // cyan +user int gb_text_selected_color = 9; // white + +user string gb_font = "NewSmallFont"; + +user bool gb_open_on_scroll = false; + +user bool gb_open_on_slot = true; +user bool gb_reverse_slot_cycle_order = false; +user bool gb_select_first_slot_weapon = false; + +user bool gb_mouse_in_wheel = true; +user bool gb_select_on_key_up = false; +user bool gb_no_menu_if_one = false; +user bool gb_on_automap = false; +user bool gb_lock_positions = false; +user bool gb_enable_sounds = true; +user bool gb_frozen_can_open = false; + +user int gb_time_freeze = 0; + +user float gb_mouse_sensitivity_x = 1.0; +user float gb_mouse_sensitivity_y = 1.0; + +user bool gb_zabor_enabled = true; + +// Custom weapon order storage ///////////////////////////////////////////////////////////////////// + +nosave string gb_custom_weapon_order = ""; \ No newline at end of file diff --git a/GLDEFS b/GLDEFS index 7433614e34..36a24c14dc 100644 --- a/GLDEFS +++ b/GLDEFS @@ -21,6 +21,13 @@ #include "models/nashgore/CorpseBlood/_materials.txt" #include "models/nashgore/Footprint/_materials.txt" +HardwareShader postprocess scene +{ + Name "gb_blur" + Shader "shaders/mfx_bss_blur.fp" 330 +} + + pulselight M1HeatWave { color 0.0 0.1 1.0 diff --git a/GRAPHICS/Gearbox/gb_ammo.png b/GRAPHICS/Gearbox/gb_ammo.png new file mode 100644 index 0000000000..1ff059534c Binary files /dev/null and b/GRAPHICS/Gearbox/gb_ammo.png differ diff --git a/GRAPHICS/Gearbox/gb_box.png b/GRAPHICS/Gearbox/gb_box.png new file mode 100644 index 0000000000..b15da62a6d Binary files /dev/null and b/GRAPHICS/Gearbox/gb_box.png differ diff --git a/GRAPHICS/Gearbox/gb_circ.png b/GRAPHICS/Gearbox/gb_circ.png new file mode 100644 index 0000000000..2ca424f4e5 Binary files /dev/null and b/GRAPHICS/Gearbox/gb_circ.png differ diff --git a/GRAPHICS/Gearbox/gb_cor.png b/GRAPHICS/Gearbox/gb_cor.png new file mode 100644 index 0000000000..97b5d6dcb0 Binary files /dev/null and b/GRAPHICS/Gearbox/gb_cor.png differ diff --git a/GRAPHICS/Gearbox/gb_desc.png b/GRAPHICS/Gearbox/gb_desc.png new file mode 100644 index 0000000000..3cf6cee375 Binary files /dev/null and b/GRAPHICS/Gearbox/gb_desc.png differ diff --git a/GRAPHICS/Gearbox/gb_hand.png b/GRAPHICS/Gearbox/gb_hand.png new file mode 100644 index 0000000000..063c5780a6 Binary files /dev/null and b/GRAPHICS/Gearbox/gb_hand.png differ diff --git a/GRAPHICS/Gearbox/gb_hcir.png b/GRAPHICS/Gearbox/gb_hcir.png new file mode 100644 index 0000000000..e2b403f0e2 Binary files /dev/null and b/GRAPHICS/Gearbox/gb_hcir.png differ diff --git a/GRAPHICS/Gearbox/gb_nope.png b/GRAPHICS/Gearbox/gb_nope.png new file mode 100644 index 0000000000..9cbcd64910 Binary files /dev/null and b/GRAPHICS/Gearbox/gb_nope.png differ diff --git a/GRAPHICS/Gearbox/gb_pip.png b/GRAPHICS/Gearbox/gb_pip.png new file mode 100644 index 0000000000..eb3b672416 Binary files /dev/null and b/GRAPHICS/Gearbox/gb_pip.png differ diff --git a/GRAPHICS/Gearbox/gb_pntr.png b/GRAPHICS/Gearbox/gb_pntr.png new file mode 100644 index 0000000000..ca43e7deac Binary files /dev/null and b/GRAPHICS/Gearbox/gb_pntr.png differ diff --git a/GRAPHICS/Gearbox/gb_weap.png b/GRAPHICS/Gearbox/gb_weap.png new file mode 100644 index 0000000000..b4dad3e21f Binary files /dev/null and b/GRAPHICS/Gearbox/gb_weap.png differ diff --git a/GRAPHICS/Gearbox/gb_wpsel.png b/GRAPHICS/Gearbox/gb_wpsel.png new file mode 100644 index 0000000000..645b3390a9 Binary files /dev/null and b/GRAPHICS/Gearbox/gb_wpsel.png differ diff --git a/KEYCONF.txt b/KEYCONF.txt index 7d16ea0297..07e001cd62 100644 --- a/KEYCONF.txt +++ b/KEYCONF.txt @@ -3,13 +3,21 @@ addplayerclass PB_PlayerPawn addkeysection "Project Brutality" PBAdvMoves - AddMenuKey "Weapon Special Wheel" "+PBWeaponSpecial" + Alias +pb_specialwheel "event pb_special_wheel" + Alias -pb_specialwheel "event pb_special_wheel_up" + Alias +pb_equipmenu "event pb_equip_wheel" + Alias -pb_equipmenu "event pb_equip_wheel_up" + + AddMenuKey "$PB_TOGGLE_SPECIALS_MENU" +pb_specialwheel + AddMenuKey "$PB_TOGGLE_EQUIP_MENU" +pb_equipmenu + + /*AddMenuKey "Weapon Special Wheel" "+PBWeaponSpecial" alias "+PBWeaponSpecial" "netevent PBWeaponSpecialOn" alias "-PBWeaponSpecial" "netevent PBWeaponSpecialOff" AddMenuKey "Equipment Selection Wheel" "+PBEquipSpecial" alias "+PBEquipSpecial" "netevent PBEquipSpecialOn" - alias "-PBEquipSpecial" "netevent PBEquipSpecialOff" + alias "-PBEquipSpecial" "netevent PBEquipSpecialOff"*/ addmenukey "Unload Gun" unreloader alias unreloader "puke 363" @@ -53,3 +61,35 @@ defaultbind L "netevent EV_ClearGore" alias pb_minimal_hud_style "set pb_hudxmargin -9; set pb_hudymargin -9; set pb_hudalpha 0.2; set pb_hudboxalpha 0; set pb_hideunusedtypes true; set pb_curmaxammolist false; set pb_showlevelstats false; set pb_showhudvisor false; set pb_showhudvisorglass false; set pb_huddynamics false; set pb_showammolist false" alias pb_full_hud_style "set pb_hudxmargin 0; set pb_hudymargin 0; set pb_hudalpha 1; set pb_hudboxalpha 1; set pb_hideunusedtypes true; set pb_curmaxammolist true; set pb_showlevelstats false; set pb_showhudvisor true; set pb_showhudvisorglass true; set pb_huddynamics true; set pb_showammolist true" + +//gearbox +// Aliases ///////////////////////////////////////////////////////////////////////////////////////// + +Alias +gb_toggle_weapon_menu "event gb_toggle_weapon_menu" +Alias -gb_toggle_weapon_menu "event gb_toggle_weapon_menu_up" + +Alias +gb_toggle_inventory_menu "event gb_toggle_inventory_menu" +Alias -gb_toggle_inventory_menu "event gb_toggle_inventory_menu_up" + +Alias gb_prev_weapon "netevent gb_prev_weapon" + +Alias gb_rotate_weapon_priority "event gb_rotate_weapon_priority" +Alias gb_rotate_weapon_slot "event gb_rotate_weapon_slot" +Alias gb_reset_custom_order "netevent gb_reset_custom_order" + + +Alias zabor "event zabor" + +Alias gb_copy_player_color "gb_color $color" + +Alias gb_reset "ResetCvar gb_scale; ResetCvar gb_color; ResetCvar gb_dim_color; ResetCvar gb_show_tags; ResetCvar gb_view_type; ResetCvar gb_enable_dim; ResetCvar gb_enable_blur; ResetCvar gb_wheel_position; ResetCvar gb_wheel_scale; ResetCvar gb_wheel_tint; ResetCvar gb_multiwheel_limit; ResetCvar gb_blocks_position_x; ResetCvar gb_blocks_position_y; ResetCvar gb_text_scale; ResetCvar gb_text_position_x; ResetCvar gb_text_position_y; ResetCvar gb_text_position_y_max; ResetCvar gb_text_usual_color; ResetCvar gb_text_selected_color; ResetCvar gb_open_on_scroll; ResetCvar gb_open_on_slot; ResetCvar gb_reverse_slot_cycle_order; ResetCvar gb_select_first_slot_weapon; ResetCvar gb_mouse_in_wheel; ResetCvar gb_select_on_key_up; ResetCvar gb_no_menu_if_one; ResetCvar gb_on_automap; ResetCvar gb_lock_positions; ResetCvar gb_enable_sounds; ResetCvar gb_frozen_can_open; ResetCvar gb_time_freeze; ResetCvar gb_mouse_sensitivity_x; ResetCvar gb_mouse_sensitivity_y; ResetCvar gb_zabor_enabled" + +// Keys //////////////////////////////////////////////////////////////////////////////////////////// + +AddKeySection "$GB_KEYSECTION" gb_Keys + +AddMenuKey "$GB_TOGGLE_WEAPON_MENU" +gb_toggle_weapon_menu +AddMenuKey "$GB_TOGGLE_INVENTORY_MENU" +gb_toggle_inventory_menu +AddMenuKey "$GB_PREV_WEAPON" gb_prev_weapon +AddMenuKey "$GB_ROTATE_WEAPON_PRIORITY" gb_rotate_weapon_priority +AddMenuKey "$GB_ROTATE_WEAPON_SLOT" gb_rotate_weapon_slot diff --git a/MENUDEF.txt b/MENUDEF.txt index 55f8d284c1..16acc959c2 100644 --- a/MENUDEF.txt +++ b/MENUDEF.txt @@ -122,6 +122,8 @@ OptionMenu "PBSettings" Submenu "Visual Settings", "RenderingSettings" StaticText " " Submenu "HUD Settings", "PB_HUDOptions" + statictext "" + Submenu "$GB_OPTIONS","gb_Options" StaticText " " StaticText " " StaticText " " @@ -272,7 +274,7 @@ OptionMenu "GameplaySettings" StaticText " " StaticText " " - Option "Weapon Wheel Freezes Time", "py_weaponwheel_freeze", "OnOff" + //Option "Weapon Wheel Freezes Time", "py_weaponwheel_freeze", "OnOff" Option "Weapon ADS Controls","pb_toggle_aim_hold", "AimingStyle" //Looks like we are using this newer line. StaticText " " StaticText "--------Grenade Bounce/Explosion Behavior" @@ -890,3 +892,192 @@ OptionValue "NashGoreGibTypes" 1, "$NASHGOREMNU_GIB_TYPE_NOSTICKYGIBS" 2, "$NASHGOREMNU_VANILLA" } + +// +// Gearbox +// + +// Menus /////////////////////////////////////////////////////////////////////////////////////////// + +OptionMenu gb_Options +{ + + Title "$GB_OPTIONS" + + StaticText "" + Option "$GB_VIEW_TYPE", gb_view_type, gb_ViewTypeValues + + StaticText "" + StaticText "$GB_KEYSECTION", 1 + Control "$GB_TOGGLE_WEAPON_MENU" , "+gb_toggle_weapon_menu" + Control "$GB_TOGGLE_INVENTORY_MENU" , "+gb_toggle_inventory_menu" + Control "$GB_PREV_WEAPON" , "gb_prev_weapon" + Control "$GB_ROTATE_WEAPON_PRIORITY", "gb_rotate_weapon_priority" + Control "$GB_ROTATE_WEAPON_SLOT" , "gb_rotate_weapon_slot" + + StaticText "" + Submenu "$GB_BEHAVIOR_OPTIONS" , gb_BehaviorOptions + Submenu "$GB_UI_OPTIONS" , gb_UiOptions + Submenu "$GB_WHEEL_OPTIONS" , gb_WheelOptions + Submenu "$GB_BLOCKS_OPTIONS" , gb_BlocksOptions + Submenu "$GB_TEXT_OPTIONS" , gb_TextOptions + Submenu "$GB_ADVANCED_OPTIONS" , gb_AdvancedOptions + Submenu "$GB_GZDOOM_OPTIONS" , gb_GZDoomOptions + + StaticText "" + SafeCommand "$GB_RESET" , gb_reset , "$GB_RESET_PROMPT" + SafeCommand "$GB_RESET_CUSTOM_ORDER" , gb_reset_custom_order , "$GB_RESET_CUSTOM_ORDER_PROMPT" + StaticText "$GB_RESET_CUSTOM_ORDER_NOTE", black + +} + +OptionMenu gb_BehaviorOptions +{ + + Title "$GB_BEHAVIOR_OPTIONS_TITLE" + + Option "$GB_OPEN_ON_PREV_NEXT" , gb_open_on_scroll , OnOff + + StaticText "" + Option "$GB_OPEN_ON_SLOT" , gb_open_on_slot , OnOff + Option "$GB_REVERSE_SLOT_CYCLE_ORDER" , gb_reverse_slot_cycle_order , OnOff + Option "$GB_SELECT_FIRST_SLOT_WEAPON" , gb_select_first_slot_weapon , OnOff + Option "$GB_NO_MENU_IF_ONE" , gb_no_menu_if_one , OnOff + + StaticText "" + Option "$GB_SELECT_ON_KEY_UP" , gb_select_on_key_up , OnOff + Option "$GB_FREEZE_TIME" , gb_time_freeze , gb_FreezeValues + Option "$GB_ENABLE_ON_AUTOMAP" , gb_on_automap , OnOff + Option "$GB_LOCK_POSITION" , gb_lock_positions , OnOff + Option "$GB_FROZEN_CAN_OPEN" , gb_frozen_can_open , OnOff + +} + +OptionMenu gb_GZDoomOptions +{ + + Title "$GB_GZDOOM_OPTIONS_TITLE" + + StaticText "" + Option "$SCALEMNU_HUDASPECT", hud_AspectScale, OnOff + StaticText "$GB_ASPECT_SCALE_NOTE", black + +} + +OptionMenu gb_UiOptions +{ + + Title "$GB_UI_OPTIONS_TITLE" + + StaticText "" + ColorPicker "$GB_COLOR" , gb_color + Command "$GB_PLAYER_COLOR", gb_copy_player_color + + StaticText "" + Option "$GB_SHOW_TAGS" , gb_show_tags , OnOff + + StaticText "" + Option "$GB_DIM" , gb_enable_dim , OnOff + ColorPicker "$GB_DIM_COLOR" , gb_dim_color + Option "$GB_BLUR" , gb_enable_blur , OnOff + + StaticText "" + Option "$GB_SOUND" , gb_enable_sounds , OnOff + + StaticText "" + Option "$GB_FONT" , gb_font , gb_FontValues + TextField "$GB_FONT_CUSTOM" , gb_font +} + +OptionMenu gb_WheelOptions +{ + + Title "$GB_WHEEL_OPTIONS_TITLE" + + StaticText "" + Slider "$GB_WHEEL_X" , gb_wheel_position , -1.5, 1.5, 0.1, 1 + Slider "$GB_WHEEL_SCALE" , gb_wheel_scale , 0.1, 2, 0.1, 1 + Option "$GB_WHEEL_TINT" , gb_wheel_tint , OnOff + + StaticText "" + Option "$GB_MOUSE_IN_WHEEL" , gb_mouse_in_wheel , OnOff + Slider "$GB_MULTIWHEEL_LIMIT" , gb_multiwheel_limit , 3, 100, 1, 0 + + StaticText "" + StaticText "$GB_MOUSE_SENSITIVITY", 1 + Slider "$GB_X", gb_mouse_sensitivity_x, 0.1, 5, 0.1, 1 + Slider "$GB_Y", gb_mouse_sensitivity_y, 0.1, 5, 0.1, 1 + +} + +OptionMenu gb_BlocksOptions +{ + + Title "$GB_BLOCKS_OPTIONS_TITLE" + + StaticText "" + Slider "$GB_SCALE" , gb_scale, 1, 8, 1, 0 + + StaticText "" + StaticText "$GB_POSITION", 1 + Slider "$GB_X" , gb_blocks_position_x, 0.0, 1.0, 0.01, 2 + Slider "$GB_Y" , gb_blocks_position_y, 0.0, 1.0, 0.01, 2 + +} + +OptionMenu gb_TextOptions +{ + + Title "$GB_TEXT_OPTIONS_TITLE" + + StaticText "" + Slider "$GB_SCALE" , gb_text_scale, 1, 8, 1, 0 + + StaticText "" + StaticText "$GB_POSITION", 1 + Slider "$GB_X" , gb_text_position_x , 0.0, 1.0, 0.01, 2 + Slider "$GB_Y" , gb_text_position_y , 0.0, 1.0, 0.01, 2 + Slider "$GB_Y_BOUNDARY" , gb_text_position_y_max , 0.0, 1.0, 0.01, 2 + + StaticText "" + Option "$GB_TEXT_USUAL_COLOR" , gb_text_usual_color , TextColors + Option "$GB_TEXT_SELECTED_COLOR" , gb_text_selected_color , TextColors + +} + +OptionMenu gb_AdvancedOptions +{ + + Title "$GB_ADVANCED_OPTIONS_TITLE" + + StaticText "" + Option "$GB_VM_ABORT_INFO_ENABLED", gb_zabor_enabled, OnOff + +} + +// Option Values /////////////////////////////////////////////////////////////////////////////////// + +OptionValue gb_ViewTypeValues +{ + 0, "$GB_BLOCKY_VIEW" + 1, "$GB_WHEEL_VIEW" + 2, "$GB_TEXT_VIEW" +} + +OptionValue gb_FreezeValues +{ + 0, "$OPTVAL_OFF" + 1, "$GB_LEVEL_AND_PLAYER" + 2, "$GB_PLAYER" +} + +OptionString gb_FontValues +{ + "NewSmallFont" , "$GB_NEW_SMALL_FONT" + "SmallFont" , "$GB_OLD_SMALL_FONT" + "ConsoleFont" , "$GB_CONSOLE_FONT" + "BigFont" , "$GB_BIG_FONT" + "PBFONT" , "$PB_FONT" + "PBBOLD" , "$PB_BOLDFONT" + "LOWQFONT" , "$PB_LOWQFONT" +} diff --git a/SOUNDS/gb_nope.ogg b/SOUNDS/gb_nope.ogg new file mode 100644 index 0000000000..0740f56202 Binary files /dev/null and b/SOUNDS/gb_nope.ogg differ diff --git a/SOUNDS/gb_tick.ogg b/SOUNDS/gb_tick.ogg new file mode 100644 index 0000000000..d045440ba8 Binary files /dev/null and b/SOUNDS/gb_tick.ogg differ diff --git a/SOUNDS/gb_toggle.ogg b/SOUNDS/gb_toggle.ogg new file mode 100644 index 0000000000..02f8172d67 Binary files /dev/null and b/SOUNDS/gb_toggle.ogg differ diff --git a/ZSCRIPT.zc b/ZSCRIPT.zc index bb027e7426..11462ba8f3 100644 --- a/ZSCRIPT.zc +++ b/ZSCRIPT.zc @@ -25,9 +25,90 @@ const MAXITERATIONS = 32676; #include "zscript/Weapons/executionGUI/pb_uihack.zs" #include "zscript/Weapons/executionGUI/pb_execution_handler.zs" #include "zscript/ZMoveMenu.ZMV" -#include "zscript/PbWheel/parse.zsc" -#include "zscript/PbWheel/ev_core_special.zsc" -#include "zscript/PbWheel/ev_drawer_special.zsc" +//#include "zscript/PbWheel/parse.zsc" +//#include "zscript/PbWheel/ev_core_special.zsc" +//#include "zscript/PbWheel/ev_drawer_special.zsc" + +//////////////////////////////////////////////////////// +// Gearbox includes +/////////////////////////////////////////////////////// +#include "zscript/gearbox/weapon_data.zs" +#include "zscript/gearbox/weapon_data_loader.zs" +#include "zscript/gearbox/printer.zs" + +#include "zscript/MD5/MD5.zs" +#include "zscript/gearbox/custom_weapon_order_storage.zs" + +#include "zscript/gearbox/activity.zs" +#include "zscript/gearbox/input.zs" +#include "zscript/gearbox/input_processor.zs" +#include "zscript/gearbox/event_processor.zs" +#include "zscript/gearbox/sounds.zs" + +#include "zscript/gearbox/weapon_menu.zs" +#include "zscript/gearbox/inventory_menu.zs" + +//pb things +#include "zscript/gearbox/specials_menu.zs" +#include "zscript/gearbox/EquipmentMenu.zs" +#include "zscript/gearbox/pb/tokens.zs" //this is were specials and equipments are defined to be used by the handler + +#include "zscript/gearbox/inventory_user.zs" + +#include "zscript/gearbox/sender.zs" + +#include "zscript/gearbox/netevent_processor.zs" +#include "zscript/gearbox/changer.zs" + +#include "zscript/gearbox/view_model.zs" + +// Displaying data on screen. +#include "zscript/gearbox/display/dim.zs" +#include "zscript/gearbox/display/blur.zs" +#include "zscript/gearbox/display/blocky_view.zs" +#include "zscript/gearbox/display/text_view.zs" +#include "zscript/gearbox/display/fade_in_out.zs" +#include "zscript/gearbox/display/caption.zs" + +#include "zscript/gearbox/options.zs" +#include "zscript/gearbox/font_selector.zs" + +// Weapon Wheel implementation. +#include "zscript/gearbox/wheel/view.zs" +#include "zscript/gearbox/wheel/controller.zs" +#include "zscript/gearbox/wheel/controller_model.zs" +#include "zscript/gearbox/wheel/inner_indexer.zs" +#include "zscript/gearbox/wheel/indexer.zs" +#include "zscript/gearbox/wheel/multiwheel.zs" +#include "zscript/gearbox/wheel/multiwheel_mode.zs" +#include "zscript/gearbox/wheel/text.zs" +#include "zscript/gearbox/wheel/screen.zs" + +// Utility tools. +#include "zscript/gearbox/tools/cvar.zs" +#include "zscript/gearbox/tools/log.zs" +#include "zscript/gearbox/tools/texture_cache.zs" +#include "zscript/gearbox/tools/ammo.zs" + +// Helper classes gearbox/that wrap access to game information provided by the engine. +#include "zscript/gearbox/engine/level.zs" +#include "zscript/gearbox/engine/weapon_watcher.zs" +#include "zscript/gearbox/engine/player.zs" + +#include "zscript/gearbox/freezer.zs" + +#include "zscript/gearbox/event_handler.zs" + +#include "zscript/gearbox/service/service.zs" +#include "zscript/gearbox/service/icon_service.zs" +#include "zscript/gearbox/service/hide_service.zs" + +// Libraries +#include "zscript/m_gizmos/previous_weapon.zs" +#include "zscript/zabor/event_handler.zs" +// +// end of gearbox includes +// //Spices and Seasoning #include "zscript/TiltPlus/TiltPlusPlus.zc" diff --git a/language.gearbox b/language.gearbox new file mode 100644 index 0000000000..e7d182eab4 --- /dev/null +++ b/language.gearbox @@ -0,0 +1,102 @@ +[enu default] + +GB_OPTIONS = "\ch⚙\c- Gearbox Options"; + +GB_UI_OPTIONS = "UI options"; +GB_UI_OPTIONS_TITLE = "Gearbox UI Options"; + +GB_COLOR = "Color"; +GB_PLAYER_COLOR = "Copy custom player color"; +GB_DIM_COLOR = "Background dimming color"; + +GB_SHOW_TAGS = "Tags"; +GB_SOUND = "Sound effects"; + +GB_KEYSECTION = "Gearbox Keys"; +GB_TOGGLE_WEAPON_MENU = "Toggle weapon menu"; +GB_TOGGLE_INVENTORY_MENU = "Toggle inventory menu"; +GB_PREV_WEAPON = "Select previous weapon"; +PB_TOGGLE_SPECIALS_MENU = "Activate weapon special"; +PB_TOGGLE_EQUIP_MENU = "Open equipments wheel"; + +GB_VIEW_TYPE = "Selector type"; +GB_BLOCKY_VIEW = "Blocks"; +GB_WHEEL_VIEW = "Wheel"; +GB_TEXT_VIEW = "Plain text"; +GB_DIM = "Background dimming"; +GB_BLUR = "Background blur"; +GB_WHEEL_X = "Horizontal position"; +GB_WHEEL_SCALE = "Scale"; +GB_WHEEL_TINT = "Colored tint on weapons"; + +GB_BEHAVIOR_OPTIONS = "Behavior options"; +GB_BEHAVIOR_OPTIONS_TITLE = "Gearbox Behavior Options"; + +GB_OPEN_ON_PREV_NEXT = "Intercept next/previous weapon keys"; +GB_OPEN_ON_SLOT = "Intercept slot keys"; +GB_SELECT_ON_KEY_UP = "Select on Toggle Menu key release"; +GB_NO_MENU_IF_ONE = "Slot keys select if weapon is only one in slot"; +GB_ENABLE_ON_AUTOMAP = "Gearbox on automap"; +GB_LOCK_POSITION = "Locked positions"; +GB_FROZEN_CAN_OPEN = "Frozen player can open Gearbox"; + +GB_REVERSE_SLOT_CYCLE_ORDER = "Reverse cycling order for the slot key"; +GB_SELECT_FIRST_SLOT_WEAPON = "Always start at first weapon for the slot key"; + +GB_FREEZE_TIME = "Freeze"; +GB_LEVEL_AND_PLAYER = "Enemies and player (single-player only)"; +GB_PLAYER = "Player"; + +GB_MOUSE_IN_WHEEL = "Enable mouse input"; +GB_MULTIWHEEL_LIMIT = "Multiwheel threshold"; + +GB_MOUSE_SENSITIVITY = "Mouse sensitivity"; +GB_X = "Horizontal"; +GB_Y = "Vertical"; +GB_Y_BOUNDARY = "Vertical lower boundary"; + +GB_WHEEL_OPTIONS = "Wheel options"; +GB_WHEEL_OPTIONS_TITLE = "Gearbox Wheel Options"; +GB_BLOCKS_OPTIONS = "Blocks options"; +GB_BLOCKS_OPTIONS_TITLE = "Gearbox Blocks Options"; + +GB_TEXT_OPTIONS = "Plain text options"; +GB_TEXT_OPTIONS_TITLE = "Gearbox Plain Text Options"; +GB_TEXT_USUAL_COLOR = "Usual color"; +GB_TEXT_SELECTED_COLOR = "Selected color"; + +GB_FONT = "Font"; +GB_FONT_CUSTOM = "Font (custom)"; +GB_BAD_FONT = "Font \"%s\" not found."; + +GB_SCALE = "Scale"; +GB_POSITION = "Position"; +GB_X = "Horizontal"; +GB_Y = "Vertical"; + +GB_RESET = "Reset Gearbox options to defaults"; +GB_RESET_PROMPT = "Do you really want to reset Gearbox options to defaults?"; + +GB_RESET_CUSTOM_ORDER = "Reset current custom weapon order"; +GB_RESET_CUSTOM_ORDER_PROMPT = "Do you really want to reset custom weapon order?"; +GB_RESET_CUSTOM_ORDER_NOTE = "Works only while in game."; + +GB_GZDOOM_OPTIONS = "Relevant GZDoom options"; +GB_GZDOOM_OPTIONS_TITLE = "GZDoom Options that affect Gearbox"; +GB_ASPECT_SCALE_NOTE = "Doesn't affect weapons in wheel for now."; + +GB_ADVANCED_OPTIONS = "Advanced options"; +GB_ADVANCED_OPTIONS_TITLE = "Gearbox Advanced Options"; +GB_VM_ABORT_INFO_ENABLED = "VM abort info"; + +GB_ROTATE_WEAPON_PRIORITY = "Cycle weapon priority"; +GB_ROTATE_WEAPON_SLOT = "Cycle weapon slot"; + +GB_NEW_SMALL_FONT = "New small font"; +GB_OLD_SMALL_FONT = "Old small font"; +GB_CONSOLE_FONT = "Console font"; +GB_BIG_FONT = "Big font"; + +PB_FONT = "PB Font"; +PB_BOLDFONT = "PB Bold Font"; +PB_LOWQFONT = "PB Hud low res font"; diff --git a/shaders/mfx_bss_blur.fp b/shaders/mfx_bss_blur.fp new file mode 100644 index 0000000000..491708920f --- /dev/null +++ b/shaders/mfx_bss_blur.fp @@ -0,0 +1,31 @@ +/* + BlurSharpShift blur from MariENB + (C)2012-2019 Marisa Kirisame + + Modified by m8f 2021 (made radius a constant). +*/ +void main() +{ + vec2 coord = TexCoord; + vec4 res = texture(InputTexture,coord); + vec2 ofs[16] = vec2[] + ( + vec2(1.0,1.0), vec2(-1.0,-1.0), + vec2(-1.0,1.0), vec2(1.0,-1.0), + + vec2(1.0,0.0), vec2(-1.0,0.0), + vec2(0.0,1.0), vec2(0.0,-1.0), + + vec2(1.41,0.0), vec2(-1.41,0.0), + vec2(0.0,1.41), vec2(0.0,-1.41), + + vec2(1.41,1.41), vec2(-1.41,-1.41), + vec2(-1.41,1.41), vec2(1.41,-1.41) + ); + vec2 bresl = vec2(textureSize(InputTexture,0)); + float radius = 2.0; + vec2 bof = radius/bresl; + for ( int i=0; i<16; i++ ) res.rgb += texture(InputTexture,coord+ofs[i]*bof).rgb; + res.rgb /= 17.0; + FragColor = res; +} diff --git a/sndinfo.gearbox b/sndinfo.gearbox new file mode 100644 index 0000000000..e2efd2e6c9 --- /dev/null +++ b/sndinfo.gearbox @@ -0,0 +1,11 @@ +gearbox/tick sounds/gb_tick.ogg +$volume gearbox/tick 0.5 + +gearbox/open sounds/gb_toggle.ogg +$volume gearbox/open 0.2 + +gearbox/close sounds/gb_toggle.ogg +$volume gearbox/close 0.2 + +gearbox/nope sounds/gb_nope.ogg +$volume gearbox/nope 0.5 diff --git a/zmapinfo.txt b/zmapinfo.txt index 84a8977882..b48c054e4d 100644 --- a/zmapinfo.txt +++ b/zmapinfo.txt @@ -108,7 +108,9 @@ GameInfo DimColor = "Black" DimAmount = 0.70 NoRandomPlayerClass = True - AddEventHandlers = "PB_ExecutionHandler", "PB_HudFXHandler", "PB_EventHandler", "MBlurHandler", "NashGoreHandler", "TiltPlusPlusHandler", "DEDashJumpHandler", "SpeedoMeterHandler", "WallSlideHandler", "PB_SpecialWheelHandler" + AddEventHandlers = "PB_ExecutionHandler", "PB_HudFXHandler", "PB_EventHandler", "MBlurHandler", "NashGoreHandler", "TiltPlusPlusHandler", "DEDashJumpHandler", "SpeedoMeterHandler", "WallSlideHandler", //"PB_SpecialWheelHandler" + "gb_EventHandler", "gb_PreviousWeaponEventHandler", "gb_PreviousWeaponStorage", "gb_VmAbortHandler" + NormForwardMove = 0x32, 0x32 //Walk speed is modulated through NormSideMove = 0x32, 0x32 //CVar in the menu options AddEventHandlers = "UnmakerStuff" diff --git a/zscript/MD5/MD5.zs b/zscript/MD5/MD5.zs new file mode 100644 index 0000000000..89bedfe5f6 --- /dev/null +++ b/zscript/MD5/MD5.zs @@ -0,0 +1,196 @@ +// Copyright 2020 3saster +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. The name of the author may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +//=========================================================================== +// +// MD5 Implementation in ZScript +// by 3saster +// +// A class that returns a string containing the MD5 of an input string, using +// the Hash method. In particular, this is designed to hash the result of the +// output of Wads.ReadLump. The "trunc" variable ignores the last "letter" of +// the input before hashing; this is because ReadLump adds a null character +// to the array. If you are not hashing the result of ReadLump, you will +// likely want to set that variable to false. +// +// You are welcome to to use this in your mods, no need to ask for permission, +// as long as the above copyright notice is included, and credit is given. +// +//=========================================================================== + +Class gb_MD5 +{ + // Some functions needed for MD5 + protected static uint F (uint B, uint C, uint D) { return (B & C) | (~B & D); } + protected static uint G (uint B, uint C, uint D) { return (B & D) | (C & ~D); } + protected static uint H (uint B, uint C, uint D) { return B ^ C ^ D; } + protected static uint I (uint B, uint C, uint D) { return C ^ (B | ~D); } + protected static uint leftrotate (uint x, uint c) { return (x << c) | (x >> (32-c)); } + // Swap endianess of byte bitwise + protected static uint swapByte (uint x) + { + uint r = 0; + for(int i=0; i<8; i++) + r += ((x>>i) & 1) << (7-i); + return r; + } + // Swap endianess of word bytewise + protected static uint swapWord (uint x) + { + uint r = 0; + for(int i=0; i<32; i += 8) + r += ((x>>i) & 0xFF) << (24-i); + return r; + } + + static string Hash(string key, bool trunc = true) + { + // Cut the last letter off, since readLump adds a null character to the string, + // causing the hash obtained to not match the actual hash of the file + if(trunc) + key.Truncate(key.length() - 1); + + // s specifies the per-round shift amounts + uint s[64] = { 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 }; + + // Constants + uint K[64] = { 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 }; + + // Initialize variables: + uint a0 = 0x67452301; // A + uint b0 = 0xefcdab89; // B + uint c0 = 0x98badcfe; // C + uint d0 = 0x10325476; // D + + // Turn string to byte array + Array input; + for(uint i=0; i < key.length(); i++) + { + input.push(key.ByteAt(i)); + } + + // Padding - add a single 1 bit, then add zeros until the number of bytes is 56 mod 64 + input.push(0x80); + while( input.size()%64 != 56 ) + { + input.push(0x00); + } + + // Pad the remaining 8 bytes with the original message length in little endian + uint strLen = 8*key.length(); + for(uint i=0; i<8; i++) + { + input.push(strLen & 0xFF); + strLen >>= 8; + } + + // Swap endianess of each byte + for(uint i=0; i < input.size(); i++) + { + input[i] = swapByte(input[i]); + } + + // Break into 64 byte chunks + for(uint front=0; front < input.size(); front += 64) + { + //break chunk into 16 four-byte words + Array M; + for(int k=0; k<64; k += 4) + { + M.push( + ( swapByte(input[front + k + 0]) << 0 ) + + ( swapByte(input[front + k + 1]) << 8 ) + + ( swapByte(input[front + k + 2]) << 16) + + ( swapByte(input[front + k + 3]) << 24) + ); + } + // Initialize hash value for this chunk: + uint A = a0; + uint B = b0; + uint C = c0; + uint D = d0; + + uint f; + uint g; + + // Main Loop + for(uint i=0; i<64; i++) + { + if(0 <= i && i <= 15) + { + f = F(B,C,D); + g = i; + } + else if(16 <= i && i <= 31) + { + f = G(B,C,D); + g = (5*i+1)%16; + } + else if(32 <= i && i <= 47) + { + f = H(B,C,D); + g = (3*i+5)%16; + } + else //if(48 <= i && i <= 63) + { + f = I(B,C,D); + g = (7*i)%16; + } + + f = f + A + K[i] + M[g]; + A = D; + D = C; + C = B; + B = B + leftrotate(f, s[i]); + } + // Add this chunk's hash to result so far: + a0 += A; + b0 += B; + c0 += C; + d0 += D; + } + + return string.format("%08x%08x%08x%08x",swapWord(a0),swapWord(b0),swapWord(c0),swapWord(d0)); + } +} \ No newline at end of file diff --git a/zscript/gearbox/EquipmentMenu.zs b/zscript/gearbox/EquipmentMenu.zs new file mode 100644 index 0000000000..a327fa6edf --- /dev/null +++ b/zscript/gearbox/EquipmentMenu.zs @@ -0,0 +1,111 @@ +Class gb_equipmentmenu +{ + static gb_equipmentmenu from() + { + let nc = new("gb_equipmentmenu"); + nc.mSelectedIndex = 0; + nc.Load(); //load its definitions + return nc; + } + + //this shouldnt be needed, but anyways + bool noequipments() + { + return getEquipmentNumber() == 0; + } + + int getEquipmentNumber() + { + return tags.size(); + } + + ui bool selectNext() + { + int nItems = getEquipmentNumber(); + if (nItems == 0) return false; + + mSelectedIndex = (mSelectedIndex + 1) % nItems; + + return true; + } + + ui bool selectPrev() + { + int nItems = getEquipmentNumber(); + if (nItems == 0) return false; + + mSelectedIndex = (mSelectedIndex - 1 + nItems) % nItems; + + return true; + } + + ui bool setSelectedIndex(int index) + { + if (index == -1 || mSelectedIndex == index) return false; + + int nItems = getEquipmentNumber(); + if(nItems == 0) + return false; + index = clamp(index,0,nItems); + + + mSelectedIndex = index; + + return true; + } + + ui int getSelectedIndex() const + { + return mSelectedIndex; + } + + string ConfirmSelection() const + { + if(token.size() > 0) + return token[mSelectedIndex]; + return ""; + } + + + //just in case anything is added in the future, is automatically handled here + //this will only be called once, when the handler is initialized + //not sure if this may be too heavy + private void Load() + { + for(int i = 0; i < AllClasses.size(); i++) + { + if(AllClasses[i] is "equipmentCard") + { + let eq = equipmentCard(new(AllClasses[i])); + if(eq) + eq.InfoFiller(tags,token,img,scalex,scaley); + } + } + } + + ui void fill(out gb_ViewModel viewModel) + { + for(int i = 0; i < tags.size(); i++) + { + viewModel.tags .push(tags[i]); + viewModel.slots .push(i + 1); + viewModel.indices .push(i); + viewModel.icons .push(texman.checkfortexture(img[i])); + viewModel.iconScaleXs .push(scalex[i]); + viewModel.iconScaleYs .push(scaley[i]); + viewModel.quantity1 .push(-1); //no ammount for you >:( + viewModel.maxQuantity1.push(-1); // + viewModel.quantity2 .push(-1); // + viewModel.maxQuantity2.push(-1); // + } + + viewModel.selectedIndex = clamp(mSelectedIndex,0,tags.size()-1); //without this, blocks/text wont work + } + + array tags; + array token; + array img; + array scalex; + array scaley; + private int mSelectedIndex; +} diff --git a/zscript/gearbox/activity.zs b/zscript/gearbox/activity.zs new file mode 100644 index 0000000000..54117f1331 --- /dev/null +++ b/zscript/gearbox/activity.zs @@ -0,0 +1,72 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +/** + * This class stores top-level Gearbox state. It can be either Weapons, + * Inventory, or None. + */ +class gb_Activity +{ + + static + gb_Activity from() + { + let result = new("gb_Activity"); + result.mActivity = gb_Activity.None; + return result; + } + + bool isNone() const { return mActivity == gb_Activity.None; } + bool isWeapons() const { return mActivity == gb_Activity.Weapons; } + bool isInventory() const { return mActivity == gb_Activity.Inventory; } + bool isSpecials() const { return mActivity == gb_Activity.Specials; } + bool isEquipment() const { return mActivity == gb_Activity.Equipments; } + + int getActivity() const { return mActivity; } + + void toggleWeaponMenu() + { + switch (mActivity) + { + case gb_Activity.Inventory: case gb_Activity.Specials: + case gb_activity.Equipments: + case gb_Activity.None: mActivity = gb_Activity.Weapons; break; + case gb_Activity.Weapons: mActivity = gb_Activity.None; break; + } + } + + void close() { mActivity = gb_Activity.None; } + void openWeapons() { mActivity = gb_Activity.Weapons; } + void openInventory() { mActivity = gb_Activity.Inventory; } + void openSpecials() { mActivity = gb_Activity.Specials; } + void openEquipment() { mActivity = gb_Activity.Equipments; } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + enum Activity + { + + None, + Weapons, + Inventory, + Specials, + Equipments, + } + + private int mActivity; + +} // class gb_Activity diff --git a/zscript/gearbox/changer.zs b/zscript/gearbox/changer.zs new file mode 100644 index 0000000000..7849041488 --- /dev/null +++ b/zscript/gearbox/changer.zs @@ -0,0 +1,89 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_Changer play +{ + + static + gb_Changer from(gb_Caption caption, gb_Options options, gb_InventoryUser inventoryUser) + { + let result = new("gb_Changer"); + result.mCaption = caption; + result.mOptions = options; + result.mInventoryUser = inventoryUser; + return result; + } + + void selectWeapon(PlayerInfo player, string weapon) + { + Weapon targetWeapon = Weapon(player.mo.findInventory(weapon)); + if (targetWeapon && gb_WeaponWatcher.currentFor(player) != targetWeapon.getClass()) + { + player.pendingWeapon = targetWeapon; + if (mOptions.isShowingWeaponTagsOnChange()) mCaption.setActor(targetWeapon); + } + } + + void useItem(PlayerInfo player, string item) + { + mInventoryUser.addToQueue(player, item); + } + + void giveItem(PlayerInfo player, string item) //added to, well, give items to the player + { + player.mo.A_giveinventory(item,1); + } + + static + void setAngles(PlayerInfo player, double pitch, double angle) + { + if (player.mo == NULL) return; + + player.cheats |= CF_InterpView; + player.mo.pitch = pitch; + player.mo.angle = angle; + + // To prevent mods that add weapon sway from swaying while moving mouse in wheel. + player.cmd.yaw = 0; + player.cmd.pitch = 0; + } + + static + void freezePlayer( PlayerInfo player + , int cheats + , double velocityX + , double velocityY + , double velocityZ + , double gravity + ) + { + if (player.mo == NULL) return; + + vector3 velocity = (velocityX, velocityY, velocityZ); + player.cheats = cheats; + player.vel = velocity.xy; + player.mo.vel = velocity; + player.mo.gravity = gravity; + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private gb_Caption mCaption; + private gb_Options mOptions; + private gb_InventoryUser mInventoryUser; + +} // class gb_Changer diff --git a/zscript/gearbox/custom_weapon_order_storage.zs b/zscript/gearbox/custom_weapon_order_storage.zs new file mode 100644 index 0000000000..c094726f83 --- /dev/null +++ b/zscript/gearbox/custom_weapon_order_storage.zs @@ -0,0 +1,186 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_WeaponOrderOperations +{ + + static + gb_WeaponOrderOperations from() + { + let result = new("gb_WeaponOrderOperations"); + result.index.clear(); + result.operationType.clear(); + return result; + } + + Array index; + Array operationType; + +} // class gb_WeaponOrderOperations + +class gb_CustomWeaponOrderStorage +{ + + enum OperationType + { + RotatePriority, + RotateSlot, + } + + static + string calculateHash(gb_WeaponData data) + { + string dataString; + + uint nWeapons = data.weapons.size(); + for (uint i = 0; i < nWeapons; ++i) + { + dataString.appendFormat("%s%d", data.weapons[i].getClassName(), data.slots[i]); + } + + return gb_MD5.hash(dataString, false); + } + + static + void applyOperations(string weaponSetHash, gb_WeaponMenu menu) + { + let operations = gb_WeaponOrderOperations.from(); + string operationsData = loadFromStorage(weaponSetHash); + deserializeOperations(operationsData, operations); + + uint nOperations = operations.index.size(); + for (uint i = 0; i < nOperations; ++i) + { + switch (operations.operationType[i]) + { + case RotatePriority: + menu.rotatePriorityForIndex(operations.index[i]); + break; + + case RotateSlot: + menu.rotateSlotForIndex(operations.index[i]); + break; + + default: + Console.printf("Unknown operation: %d.", operations.operationType[i]); + } + } + } + + static + void savePriorityRotation(string weaponSetHash, int index) + { + saveOperation(weaponSetHash, index, RotatePriority); + } + + static + void saveSlotRotation(string weaponSetHash, int index) + { + saveOperation(weaponSetHash, index, RotateSlot); + } + + static + void reset(string weaponSetHash) + { + Dictionary weaponSetOrders = readWeaponSetOrders(); + weaponSetOrders.remove(weaponSetHash); + getStorageCvar().setString(weaponSetOrders.toString()); + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + static + void saveOperation(string weaponSetHash, int index, int operationType) + { + let operations = gb_WeaponOrderOperations.from(); + { + string operationsData = loadFromStorage(weaponSetHash); + deserializeOperations(operationsData, operations); + } + + operations.index .push(index); + operations.operationType.push(operationType); + + string operationsData = serializeOperations(operations); + saveToStorage(weaponSetHash, operationsData); + } + + private static + void saveToStorage(string hash, string data) + { + Dictionary weaponSetOrders = readWeaponSetOrders(); + weaponSetOrders.insert(hash, data); + getStorageCvar().setString(weaponSetOrders.toString()); + } + + private static + string loadFromStorage(string hash) + { + return readWeaponSetOrders().at(hash); + } + + private static + Dictionary readWeaponSetOrders() + { + return Dictionary.fromString(getStorageCvar().getString()); + } + + private static + Cvar getStorageCvar() + { + return Cvar.getCvar(STORAGE_CVAR_NAME); + } + + private static + string serializeOperations(gb_WeaponOrderOperations operations) + { + string result; + uint nOperations = operations.index.size(); + for (uint i = 0; i < nOperations; ++i) + { + result.appendFormat( "%d" .. DELIMITER .. "%d" .. DELIMITER + , operations.index[i] + , operations.operationType[i] + ); + } + return result; + } + + private static + void deserializeOperations(string data, out gb_WeaponOrderOperations operations) + { + Array tokens; + data.split(tokens, DELIMITER, TOK_SkipEmpty); + + if (tokens.size() % 2 != 0) + { + Console.printf("Weapon order data is corrupted!"); + return; + } + + uint nTokens = tokens.size(); + for (uint i = 0; i < nTokens; i += 2) + { + operations.index .push(tokens[i ].toInt()); + operations.operationType.push(tokens[i + 1].toInt()); + } + } + + const STORAGE_CVAR_NAME = "gb_custom_weapon_order"; + const DELIMITER = ";"; + +} // class gb_CustomWeaponOrderStorage diff --git a/zscript/gearbox/display/blocky_view.zs b/zscript/gearbox/display/blocky_view.zs new file mode 100644 index 0000000000..55957c0b57 --- /dev/null +++ b/zscript/gearbox/display/blocky_view.zs @@ -0,0 +1,518 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_BlockyView +{ + + static + gb_BlockyView from(gb_TextureCache textureCache, gb_Options options, gb_FontSelector fontSelector) + { + let result = new("gb_BlockyView"); + + result.setAlpha(1.0); + result.setScale(1); + result.setBaseColor(0x2222CC); + result.mTextureCache = textureCache; + result.mOptions = options; + result.mFontSelector = fontSelector; + + return result; + } + + void setAlpha(double alpha) + { + mAlpha = alpha; + } + + void setScale(int scale) + { + if (scale < 1) scale = 1; + + double scaleFactor = getScaleFactor(); + + mScreenWidth = int(Screen.getWidth() / scale / scaleFactor); + mScreenHeight = int(Screen.getHeight() / scale / scaleFactor); + } + + void setBaseColor(int color) + { + mBaseColor = color; + mSlotColor = addColor(mBaseColor, -0x22); + mSelectedColor = addColor(mBaseColor, 0x44); + mAmmoBackColor = addColor(mBaseColor, 0x66); + } + + void display(gb_ViewModel viewModel) const + { + int selectedIndex = viewModel.selectedIndex; + if (selectedIndex >= viewModel.slots.size() || selectedIndex == -1) return; + + vector2 position = mOptions.getBlocksPosition(); + int maxWidth = getMaxWidth(getSlotsNumber(viewModel)); + int maxHeight = getMaxHeight(viewModel); + int startX = int(min(BORDER + (mScreenWidth - BORDER) * position.x, mScreenWidth - maxWidth)); + int startY = int(min(BORDER + (mScreenHeight - BORDER) * position.y, mScreenHeight - maxHeight)); + + int lastDrawnSlot = 0; + int slotX = startX; + int inSlotIndex = 0; + + int selectedSlot = viewModel.slots[selectedIndex]; + + Font aFont = mFontSelector.getFont(); + int fontHeight = aFont.getHeight(); + int textY = startY + SLOT_SIZE / 2 - fontHeight / 2; + + uint nWeapons = viewModel.tags.size(); + for (uint i = 0; i < nWeapons; ++i) + { + int slot = viewModel.slots[i]; + + // slot number box + if (slot != lastDrawnSlot) + { + drawAlphaTexture(mTextureCache.blockBox, slotX, startY, mSlotColor,false); + + string slotText = string.format("%d", slot); + int textX = slotX + SLOT_SIZE / 2 - aFont.stringWidth(slotText) / 2; + + int col = (slot == selectedSlot) ? Font.CR_YELLOW : Font.CR_WHITE; + drawText(aFont, col, textX, textY, slotText); + + lastDrawnSlot = slot; + } + + if (slot == selectedSlot) // selected slot (big boxes) + { + bool isSelectedWeapon = (i == selectedIndex); + int weaponColor = isSelectedWeapon ? mSelectedColor : mBaseColor; + int weaponY = startY + SLOT_SIZE + (SELECTED_WEAPON_HEIGHT + MARGIN) * inSlotIndex; + + // big box blockBigSel + drawAlphaTexture(isSelectedWeapon ? mTextureCache.blockBigSel : mTextureCache.blockBig, + slotX, weaponY, weaponColor,false); + + // weapon + { + // code is adapted from GZDoom AltHud.DrawImageToBox. + TextureID weaponTexture = viewModel.icons[i]; + let weaponSize = TexMan.getScaledSize(weaponTexture); + weaponSize.x *= viewModel.iconScaleXs[i]; + weaponSize.y *= viewModel.iconScaleYs[i]; + if (mOptions.isPreservingAspectRatio()) weaponSize.y *= 1.2; + + int allowedWidth = SELECTED_SLOT_WIDTH - MARGIN * 2; + int allowedHeight = SELECTED_WEAPON_HEIGHT - MARGIN * 2; + + double scaleHor = (allowedWidth < weaponSize.x) ? allowedWidth / weaponSize.x : 1.0; + double scaleVer = (allowedHeight < weaponSize.y) ? allowedHeight / weaponSize.y : 1.0; + double scale = min(scaleHor, scaleVer); + + int weaponWidth = int(round(weaponSize.x * scale)); + int weaponHeight = int(round(weaponSize.y * scale)); + + drawWeapon( weaponTexture + , slotX + SELECTED_SLOT_WIDTH / 2 + , weaponY + SELECTED_WEAPON_HEIGHT / 2 + , weaponWidth + , weaponHeight + ); + } + + // corners + if (isSelectedWeapon) + { + int lineEndX = slotX + SELECTED_SLOT_WIDTH - CORNER_SIZE; + int lineEndY = weaponY + SELECTED_WEAPON_HEIGHT - CORNER_SIZE; + TextureID cornerTexture = mTextureCache.corner; + // top left, top right, bottom left, bottom right + drawFlippedTexture(cornerTexture, slotX, weaponY, NoHorizontalFlip, NoVerticalFlip); + drawFlippedTexture(cornerTexture, lineEndX, weaponY, HorizontalFlip, NoVerticalFlip); + drawFlippedTexture(cornerTexture, slotX, lineEndY, NoHorizontalFlip, VerticalFlip); + drawFlippedTexture(cornerTexture, lineEndX, lineEndY, HorizontalFlip, VerticalFlip); + } + + // quantity indicators + TextureID ammoTexture = mTextureCache.ammoLine; + int ammoY = weaponY; + if (gb_Ammo.isValid(viewModel.quantity1[i], viewModel.maxQuantity1[i])) + { + drawAlphaTexture( ammoTexture + , slotX + MARGIN * 2 + , ammoY + , EMPTY_AMMO_COLOR//mAmmoBackColor + ); + int ammoRatioWidth = ammoRatioWidthFor(viewModel.quantity1[i], viewModel.maxQuantity1[i]); + drawAlphaWidthTexture( ammoTexture + , slotX + MARGIN * 2 + , ammoY + , FILLED_AMMO_COLOR + , ammoRatioWidth + ); + ammoY += MARGIN + AMMO_HEIGHT; + } + if (gb_Ammo.isValid(viewModel.quantity2[i], viewModel.maxQuantity2[i])) + { + drawAlphaTexture( ammoTexture + , slotX + MARGIN * 2 + , ammoY + , EMPTY_AMMO_COLOR//mAmmoBackColor + ); + int ammoRatioWidth = ammoRatioWidthFor(viewModel.quantity2[i], viewModel.maxQuantity2[i]); + drawAlphaWidthTexture( ammoTexture + , slotX + MARGIN * 2 + , ammoY + , FILLED_AMMO_COLOR + , ammoRatioWidth + ); + } + + if (mOptions.isShowingTags()) drawTag(viewModel.tags[i], aFont, slotX, weaponY); + } + else // not selected slot (small boxes) + { + int boxY = startY - MARGIN + (SLOT_SIZE + MARGIN) * (inSlotIndex + 1); + drawAlphaTexture(mTextureCache.blockBox, slotX, boxY, mBaseColor,false); + } + + if (i + 1 < nWeapons && viewModel.slots[i + 1] != slot) + { + slotX += ((slot == selectedSlot) ? SELECTED_SLOT_WIDTH : SLOT_SIZE) + MARGIN; + inSlotIndex = 0; + } + else + { + ++inSlotIndex; + } + } + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private static + int getSlotsNumber(gb_ViewModel viewModel) + { + uint nEntries = viewModel.slots.size(); + int nSlots = 0; + int lastSlot = -1; + for (uint i = 0; i < nEntries; ++i) + { + if (viewModel.slots[i] != lastSlot) + { + ++nSlots; + lastSlot = viewModel.slots[i]; + } + } + return nSlots; + } + + private static + int getMaxWidth(int nSlots) + { + return (nSlots - 1) * (SLOT_SIZE + MARGIN) + SELECTED_SLOT_WIDTH + BORDER; + } + + private static + int getMaxHeight(gb_ViewModel viewModel) + { + uint nEntries = viewModel.slots.size(); + int lastSlot = -1; + int itemsInSlot = 1; + int maxItemsInSlot = -1; + for (uint i = 0; i < nEntries; ++i) + { + if (viewModel.slots[i] == lastSlot) + { + ++itemsInSlot; + } + else + { + lastSlot = viewModel.slots[i]; + if (itemsInSlot > maxItemsInSlot) maxItemsInSlot = itemsInSlot; + itemsInSlot = 1; + } + } + if (itemsInSlot > maxItemsInSlot) maxItemsInSlot = itemsInSlot; + + return maxItemsInSlot * (SELECTED_WEAPON_HEIGHT + MARGIN) - MARGIN + BORDER + SLOT_SIZE; + } + + private + void drawTag(string tag, Font aFont, double startX, double startY) + { + // >_< + // + // Trying to put as much text into the the box as possible, while gracefully + // (hopefully) handling when the whole tag cannot fit in the box. + // + // This code doesn't take the icon and quantity bars into account. Just print + // semi-transparent text over them. + + if (tag.length() == 0) return; + + Array words; + tag.split(words, " ", Tok_SkipEmpty); + + // Start filling lines with words from bottom to top. If the word doesn't + // fit into the line, it is pushed into the new top line. + Array lines; + int nWords = words.size(); + lines.push(words[nWords - 1]); + int spaceWidth = aFont.stringWidth(" "); + int allowedStringWidth = SELECTED_SLOT_WIDTH - 2; + for (int i = nWords - 2; i >= 0; --i) + { + uint newWidth = (aFont.stringWidth(lines[0]) + spaceWidth + aFont.stringWidth(words[i])); + if (newWidth < allowedStringWidth) + { + lines[0] = words[i] .. " " .. lines[0]; + } + else + { + lines.insert(0, words[i]); + } + } + + // If there are too many lines, put them on the third line and mark the it + // with ellipsis. + string ellipsis = aFont.getGlyphHeight(ELLIPSIS_CODE) ? "…" : "..."; + uint nLines = lines.size(); + if (nLines > 3) + { + for (uint i = 3; i < nLines; ++i) + { + lines[2].appendFormat(" %s", lines[i]); + } + lines[2] = lines[2] .. ellipsis; + } + + // If a line is too long to fit in the box, replace the part that doesn't + // fit with ellipsis. + uint linesEnd = min(nLines, 3); + int ellipsisWidth = aFont.stringWidth(ellipsis); + for (uint i = 0; i < linesEnd; ++i) + { + if (aFont.stringWidth(lines[i]) <= allowedStringWidth) continue; + + while (aFont.stringWidth(lines[i]) + ellipsisWidth > allowedStringWidth) + { + lines[i].deleteLastCharacter(); + } + lines[i] = lines[i] .. ellipsis; + } + + // Finally, print lines. + int lineHeight = aFont.getHeight(); + for (uint i = 0; i < linesEnd; ++i) + { + double y = startY + SELECTED_WEAPON_HEIGHT + (i - linesEnd) * lineHeight; + drawText(aFont, Font.CR_WHITE, startX + 1, y, lines[i], 0.3); + } + } + + private static + int ammoRatioWidthFor(int ammoCount, int ammoMax) + { + return int(round(float(ammoCount) / ammoMax * AMMO_WIDTH)); + } + + private + void drawAlphaTexture(TextureID texture, int x, int y, int color, bool docolor = true) const + { + if(docolor) + { + Screen.drawTexture( texture + , NO_ANIMATION + , x + , y + , DTA_FillColor , color + , DTA_AlphaChannel , true + , DTA_Alpha , mAlpha + , DTA_VirtualWidth , mScreenWidth + , DTA_VirtualHeight , mScreenHeight + , DTA_KeepRatio , true + ); + } + else + { + Screen.drawTexture( texture + , NO_ANIMATION + , x + , y + , DTA_AlphaChannel , true + , DTA_Alpha , mAlpha + , DTA_VirtualWidth , mScreenWidth + , DTA_VirtualHeight , mScreenHeight + , DTA_KeepRatio , true + ); + + } + } + + private + void drawAlphaWidthTexture(TextureID texture, int x, int y, int color, int width,bool docolor = true) const + { + if(docolor) + { + Screen.drawTexture( texture + , NO_ANIMATION + , x + , y + , DTA_FillColor , color + , DTA_AlphaChannel , true + , DTA_Alpha , mAlpha + , DTA_VirtualWidth , mScreenWidth + , DTA_VirtualHeight , mScreenHeight + , DTA_KeepRatio , true + , DTA_DestWidth , width + ); + } + else + { + Screen.drawTexture( texture + , NO_ANIMATION + , x + , y + , DTA_AlphaChannel , true + , DTA_Alpha , mAlpha + , DTA_VirtualWidth , mScreenWidth + , DTA_VirtualHeight , mScreenHeight + , DTA_KeepRatio , true + , DTA_DestWidth , width + ); + } + } + + enum HorizontalFlipOptions + { + NoHorizontalFlip = 0, + HorizontalFlip = 1, + } + + enum VerticalFlipOptions + { + NoVerticalFlip = 0, + VerticalFlip = 1, + } + + private + void drawFlippedTexture(TextureID texture, int x, int y, int horFlip, int verFlip) const + { + Screen.drawTexture( texture + , NO_ANIMATION + , x + , y + , DTA_Alpha , mAlpha + , DTA_VirtualWidth , mScreenWidth + , DTA_VirtualHeight , mScreenHeight + , DTA_KeepRatio , true + , DTA_FlipX , horFlip + , DTA_FlipY , verFlip + ); + } + + private + void drawWeapon(TextureID texture, int x, int y, int w, int h) const + { + Screen.drawTexture( texture + , NO_ANIMATION + , x + , y + , DTA_CenterOffset , true + , DTA_KeepRatio , true + , DTA_DestWidth , w + , DTA_DestHeight , h + , DTA_Alpha , mAlpha + , DTA_VirtualWidth , mScreenWidth + , DTA_VirtualHeight , mScreenHeight + ); + } + + private + void drawText(Font aFont, int color, double x, double y, string text, double alpha = 1.0) const + { + Screen.drawText( aFont + , color + , x + , y + , text + , DTA_Alpha , mAlpha * alpha + , DTA_VirtualWidth , mScreenWidth + , DTA_VirtualHeight , mScreenHeight + , DTA_KeepRatio , true + ); + } + + private static + color addColor(color base, int add) + { + uint newRed = clamp(base.r + add, 0, 255); + uint newGreen = clamp(base.g + add, 0, 255); + uint newBlue = clamp(base.b + add, 0, 255); + + if ( abs(int(newRed ) - base.r) < abs(add) + && abs(int(newGreen) - base.g) < abs(add) + && abs(int(newBlue ) - base.b) < abs(add) + ) + { + return addColor(base, -add); + } + + uint result = (newRed << 16) + (newGreen << 8) + newBlue; + return result; + } + + private static + double getScaleFactor() + { + return Screen.getHeight() / 1080.0; + } + + const BORDER = 20; + const MARGIN = 4; + + const SLOT_SIZE = 25; + const SELECTED_SLOT_WIDTH = 100; + const SELECTED_WEAPON_HEIGHT = SLOT_SIZE * 2 + MARGIN; + + const NO_ANIMATION = 0; // == false + + const CORNER_SIZE = 4; + + const AMMO_WIDTH = 40; + const AMMO_HEIGHT = 6; + + const FILLED_AMMO_COLOR = 0x00F200; //0x22DD22; + const EMPTY_AMMO_COLOR = 0xE6010D; + + const ELLIPSIS_CODE = 8230; + + private double mAlpha; + + private int mScreenWidth; + private int mScreenHeight; + + private color mBaseColor; + private color mSlotColor; + private color mSelectedColor; + private color mAmmoBackColor; + + private gb_TextureCache mTextureCache; + private gb_Options mOptions; + private gb_FontSelector mFontSelector; + +} // class gb_BlockyView diff --git a/zscript/gearbox/display/blur.zs b/zscript/gearbox/display/blur.zs new file mode 100644 index 0000000000..ec1651877d --- /dev/null +++ b/zscript/gearbox/display/blur.zs @@ -0,0 +1,27 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_Blur +{ + + static + void setEnabled(bool isEnabled) + { + Shader.setEnabled(players[consolePlayer], "gb_blur", isEnabled); + } + +} // class gb_Blur diff --git a/zscript/gearbox/display/caption.zs b/zscript/gearbox/display/caption.zs new file mode 100644 index 0000000000..46bcc3d258 --- /dev/null +++ b/zscript/gearbox/display/caption.zs @@ -0,0 +1,47 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_Caption +{ + + static + gb_Caption from() + { + let result = new("gb_Caption"); + result.mActor = NULL; + return result; + } + + void setActor(Actor anActor) + { + mActor = anActor; + } + + ui + void show() + { + if (mActor == NULL) return; + + mActor.displayNameTag(); + mActor = NULL; + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private Actor mActor; + +} // class gb_Caption diff --git a/zscript/gearbox/display/dim.zs b/zscript/gearbox/display/dim.zs new file mode 100644 index 0000000000..3c756a860a --- /dev/null +++ b/zscript/gearbox/display/dim.zs @@ -0,0 +1,29 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_Dim +{ + + static + void dim(double alpha, gb_Options options) + { + if (!options.isDimEnabled()) return; + + Screen.Dim(options.getDimColor(), alpha * 0.4, 0, 0, Screen.getWidth(), Screen.getHeight()); + } + +} // class gb_Dim diff --git a/zscript/gearbox/display/fade_in_out.zs b/zscript/gearbox/display/fade_in_out.zs new file mode 100644 index 0000000000..7682a17476 --- /dev/null +++ b/zscript/gearbox/display/fade_in_out.zs @@ -0,0 +1,43 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_FadeInOut +{ + + static + gb_FadeInOut from() + { + let result = new("gb_FadeInOut"); + result.mAlpha = 0.0; + return result; + } + + void fadeInOut(double step) + { + mAlpha = clamp(mAlpha + step, 0.0, 1.0); + } + + double getAlpha() const + { + return mAlpha; + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private double mAlpha; + +} diff --git a/zscript/gearbox/display/text_view.zs b/zscript/gearbox/display/text_view.zs new file mode 100644 index 0000000000..0c557257f9 --- /dev/null +++ b/zscript/gearbox/display/text_view.zs @@ -0,0 +1,191 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_TextView +{ + + static + gb_TextView from(gb_Options options, gb_FontSelector fontSelector) + { + let result = new("gb_TextView"); + + result.setAlpha(1.0); + result.setScale(1); + result.mOptions = options; + result.mFontSelector = fontSelector; + + return result; + } + + void setAlpha(double alpha) + { + mAlpha = alpha; + } + + void setScale(int scale) + { + if (scale < 1) scale = 1; + + double scaleFactor = getScaleFactor(); + + mScreenWidth = int(Screen.getWidth() / scale / scaleFactor); + mScreenHeight = int(Screen.getHeight() / scale / scaleFactor); + } + + void display(gb_ViewModel viewModel) const + { + uint nWeapons = viewModel.tags.size(); + if (nWeapons == 0) return; + + vector2 start = mOptions.getTextPosition(); + start.x *= mScreenWidth; + start.y *= mScreenHeight; + double x = start.x; + double y = start.y; + Font aFont = mFontSelector.getFont(); + double lineHeight = aFont.getHeight(); + int usualColor = mOptions.getTextUsualColor(); + int selectedColor = mOptions.getTextSelectedColor(); + + double maxX = 0; + double maxY = 0; + + // dry run + int maxSlotWidth = 0; + for (uint i = 0; i < nWeapons; ++i) + { + string slotString = string.format("%d. ", viewModel.slots[i]); + maxSlotWidth = max(maxSlotWidth, aFont.stringWidth(slotString)); + } + for (uint i = 0; i < nWeapons; ++i) + { + string itemString = makeItemString(viewModel, i); + maxX = max(maxX, maxSlotWidth + x + aFont.stringWidth(itemString)); + y += lineHeight; + if (i + 1 < nWeapons && viewModel.slots[i] != viewModel.slots[i + 1]) y += lineHeight / 3; + } + maxY = y; + + if (maxX > mScreenWidth) x = max(0, mScreenWidth - (maxX - x)); + + double yBoundary = mScreenHeight * mOptions.getTextPositionYMax(); + if (maxY > yBoundary && (maxY - lineHeight != start.y)) + { + lineHeight *= (yBoundary - lineHeight - start.y) / (maxY - lineHeight - start.y); + } + + y = start.y; + + // real run + double selectedY = 0; + for (uint i = 0; i < nWeapons; ++i) + { + if (i == viewModel.selectedIndex) + { + selectedY = y; + } + else + { + string slotString = string.format("%d. ", viewModel.slots[i]); + drawText(aFont, usualColor, x, y, slotString, mAlpha); + } + y += lineHeight; + if (i + 1 < nWeapons && viewModel.slots[i] != viewModel.slots[i + 1]) y += lineHeight / 3; + } + { + string slotString = string.format("%d. ", viewModel.slots[viewModel.selectedIndex]); + drawText(aFont, selectedColor, x, selectedY, slotString, mAlpha); + } + + y = start.y; + + for (uint i = 0; i < nWeapons; ++i) + { + if (i != viewModel.selectedIndex) + { + string itemText = makeItemString(viewModel, i); + drawText(aFont, usualColor, x + maxSlotWidth, y, itemText, mAlpha); + } + y += lineHeight; + if (i + 1 < nWeapons && viewModel.slots[i] != viewModel.slots[i + 1]) y += lineHeight / 3; + } + { + string itemText = makeItemString(viewModel, viewModel.selectedIndex); + drawText(aFont, selectedColor, x + maxSlotWidth, selectedY, itemText, mAlpha); + } + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private + string makeItemString(gb_ViewModel viewModel, int i) + { + string result = viewModel.tags[i]; + + bool isQuantity1Valid = gb_Ammo.isValid(viewModel.quantity1[i], viewModel.maxQuantity1[i]); + bool isQuantity2Valid = gb_Ammo.isValid(viewModel.quantity2[i], viewModel.maxQuantity2[i]); + + if (isQuantity1Valid && isQuantity2Valid) + { + result.appendFormat(" (%d/%d, %d/%d)" + , viewModel.quantity1[i] + , viewModel.maxQuantity1[i] + , viewModel.quantity2[i] + , viewModel.maxQuantity2[i] + ); + } + else if (isQuantity1Valid) + { + result.appendFormat(" (%d/%d)", viewModel.quantity1[i], viewModel.maxQuantity1[i]); + } + else if (isQuantity2Valid) + { + result.appendFormat(" (%d/%d)", viewModel.quantity2[i], viewModel.maxQuantity2[i]); + } + + return result; + } + + private + void drawText(Font aFont, int color, double x, double y, string text, double alpha = 1.0) const + { + Screen.drawText( aFont + , color + , x + , y + , text + , DTA_Alpha , mAlpha * alpha + , DTA_VirtualWidth , mScreenWidth + , DTA_VirtualHeight , mScreenHeight + , DTA_KeepRatio , true + ); + } + + private static + double getScaleFactor() + { + return Screen.getHeight() / 1080.0; + } + + private double mAlpha; + private int mScreenWidth; + private int mScreenHeight; + + private gb_Options mOptions; + private gb_FontSelector mFontSelector; + +} // class gb_TextView diff --git a/zscript/gearbox/engine/level.zs b/zscript/gearbox/engine/level.zs new file mode 100644 index 0000000000..1ae3ef8dfb --- /dev/null +++ b/zscript/gearbox/engine/level.zs @@ -0,0 +1,35 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_Level +{ + + enum _ { NotInGame, Loading, JustLoaded, Loaded } + + static + int getState() + { + // In the best case, the world is fully loaded on tick 0, but if player + // weapons are configured via keyconf, they require a bit of network + // communication, which finishes at tick 1. + if (level.mapName ~== "titlemap") return gb_Level.NotInGame; + if (level.mapTime == 0) return gb_Level.Loading; + if (level.mapTime == 1) return gb_Level.JustLoaded; + return gb_Level.Loaded; + } + +} // class gb_Level diff --git a/zscript/gearbox/engine/player.zs b/zscript/gearbox/engine/player.zs new file mode 100644 index 0000000000..8ccef2fce4 --- /dev/null +++ b/zscript/gearbox/engine/player.zs @@ -0,0 +1,27 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_Player +{ + + static + bool isDead() + { + return (players[consolePlayer].mo.health <= 0); + } + +} // class gb_Player diff --git a/zscript/gearbox/engine/weapon_watcher.zs b/zscript/gearbox/engine/weapon_watcher.zs new file mode 100644 index 0000000000..5714306daf --- /dev/null +++ b/zscript/gearbox/engine/weapon_watcher.zs @@ -0,0 +1,40 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +/** + * This class provides information on what weapon the players hold. + */ +class gb_WeaponWatcher +{ + + static + class current() + { + return currentFor(players[consolePlayer]); + } + + static + class currentFor(PlayerInfo player) + { + let currentWeapon = player.pendingWeapon.getClassName() != "Object" + ? player.pendingWeapon + : player.readyWeapon; + + return currentWeapon ? currentWeapon.getClass() : NULL; + } + +} // class gb_WeaponWatcher diff --git a/zscript/gearbox/event_handler.zs b/zscript/gearbox/event_handler.zs new file mode 100644 index 0000000000..6ffcc7d37c --- /dev/null +++ b/zscript/gearbox/event_handler.zs @@ -0,0 +1,632 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2022 + * Carrascado 2022 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +/** + * This class is the core of Gearbox. + * + * It delegates as much work to other classes while minimizing the relationships + * between those classes. + * + * To ensure multiplayer compatibility, Gearbox does the following: + * + * 1. All visuals and input processing happens on client side and is invisible + * to the network. + * + * 2. Actual game changing things, like switching weapons, are done through + * network - even for the current player, even for the single-player game. + */ +class gb_EventHandler : EventHandler +{ + + override + void worldTick() + { + switch (gb_Level.getState()) + { + case gb_Level.NotInGame: return; + case gb_Level.Loading: return; + case gb_Level.JustLoaded: initialize(); // fall through + case gb_Level.Loaded: break; + } + + bool isClosed = mActivity.isNone(); + + // Thaw regardless of the option to prevent player being locked frozen after + // changing options. + if (isClosed) mFreezer.thaw(); + else mFreezer.freeze(); + + if (!isClosed && (gb_Player.isDead() || isDisabledOnAutomap())) + { + close(); + } + + if (isClosed) + { + // Watch for the current weapon, because player can change it without + // Gearbox. Also handles the case when Gearbox hasn't been opened yet, + // initializing weapon menu. + mWeaponMenu.setSelectedWeapon(gb_WeaponWatcher.current()); + } + else if (mOptions.getViewType() == VIEW_TYPE_WHEEL || mActivity.isSpecials() || mActivity.isequipment()) + { + mWheelController.process(); + } + + mInventoryUser.use(); + } + + /** + * This function processes key bindings specific for Gearbox. + */ + override + void consoleProcess(ConsoleEvent event) + { + if (players[consolePlayer].mo == NULL) return; + + if (!mIsInitialized || isDisabledOnAutomap()) return; + if (isPlayerFrozen() && mActivity.isNone()) return; + + switch (gb_EventProcessor.process(event, mOptions.isSelectOnKeyUp())) + { + case InputToggleWeaponMenu: toggleWeapons(); break; + case InputConfirmSelection: confirmSelection(); close(); break; + case InputToggleInventoryMenu: toggleInventory(); break; + case InputRotateWeaponPriority: rotateWeaponPriority(); break; + case InputRotateWeaponSlot: rotateWeaponSlot(); break; + case InputToggleSpecialMenu: toggleSpecials(); break; + case InputToggleEquipMenu: toggleEquipments(); break; + } + + if (!mActivity.isNone()) mWheelController.reset(); + } + + /** + * This function provides latching to existing key bindings, and processing mouse input. + */ + override + bool inputProcess(InputEvent event) + { + if (players[consolePlayer].mo == NULL) return false; + if (!mIsInitialized || isDisabledOnAutomap() || gameState != GS_LEVEL) return false; + if (isPlayerFrozen() && mActivity.isNone()) return false; + + int input = gb_InputProcessor.process(event); + + if (mActivity.isWeapons()) + { + switch (input) + { + case InputSelectNextWeapon: tickIf(mWeaponMenu.selectNextWeapon()); mWheelController.reset(); break; + case InputSelectPrevWeapon: tickIf(mWeaponMenu.selectPrevWeapon()); mWheelController.reset(); break; + case InputConfirmSelection: confirmSelection(); close(); break; + case InputClose: close(); break; + + default: + if (!gb_Input.isSlot(input)) return false; + mWheelController.reset(); + tickIf(mWeaponMenu.selectSlot(gb_Input.getSlot(input))); + break; + } + + return true; + } + else if (mActivity.isInventory()) + { + switch (input) + { + case InputSelectNextWeapon: tickIf(mInventoryMenu.selectNext()); mWheelController.reset(); break; + case InputSelectPrevWeapon: tickIf(mInventoryMenu.selectPrev()); mWheelController.reset(); break; + case InputConfirmSelection: confirmSelection(); close(); break; + case InputClose: close(); break; + + default: + { + if (!gb_Input.isSlot(input)) return false; + mWheelController.reset(); + int slot = gb_Input.getSlot(input); + int index = (slot == 0) ? 9 : slot - 1; + tickIf(mInventoryMenu.setSelectedIndex(index)); + break; + } + } + return true; + } + else if(mActivity.isSpecials()) + { + switch (input) + { + case InputSelectNextWeapon: tickIf(mspecialsmenu.selectNext()); mWheelController.reset(); break; + case InputSelectPrevWeapon: tickIf(mspecialsmenu.selectPrev()); mWheelController.reset(); break; + case InputConfirmSelection: confirmSelection(); close(); break; + case InputClose: close(); break; + + default: + { + if (!gb_Input.isSlot(input)) return false; + mWheelController.reset(); + int slot = gb_Input.getSlot(input); + int index = (slot == 0) ? 9 : slot - 1; + tickIf(mspecialsmenu.setSelectedIndex(index)); + break; + } + } + return true; + } + else if (mactivity.isEquipment()) + { + //gb_Activity.Equipments + switch (input) + { + case InputSelectNextWeapon: tickIf(mEquipmenu.selectNext()); mWheelController.reset(); break; + case InputSelectPrevWeapon: tickIf(mEquipmenu.selectPrev()); mWheelController.reset(); break; + case InputConfirmSelection: confirmSelection(); close(); break; + case InputClose: close(); break; + + default: + { + if (!gb_Input.isSlot(input)) return false; + mWheelController.reset(); + int slot = gb_Input.getSlot(input); + int index = (slot == 0) ? 9 : slot - 1; + tickIf(mEquipmenu.setSelectedIndex(index)); + break; + } + } + return true; + } + else if (mActivity.isNone()) + { + mWheelController.reset(); + + if (gb_Input.isSlot(input) && mOptions.isOpenOnSlot()) + { + int slot = gb_Input.getSlot(input); + + if (mOptions.isNoMenuIfOne() && mWeaponMenu.isOneWeaponInSlot(slot)) + { + tickIf(mWeaponMenu.selectSlot(slot)); + gb_Sender.sendSelectEvent(mWeaponMenu.confirmSelection()); + } + else if (mWeaponMenu.selectSlot(slot, mOptions.isSelectFirstSlotWeapon())) + { + mSounds.playOpen(); + mActivity.openWeapons(); + } + else + { + mSounds.playNope(); + return false; + } + + return true; + } + + if (!mOptions.isOpenOnScroll()) return false; + + switch (input) + { + case InputSelectNextWeapon: toggleWeapons(); mWeaponMenu.selectNextWeapon(); return true; + case InputSelectPrevWeapon: toggleWeapons(); mWeaponMenu.selectPrevWeapon(); return true; + } + } + + return false; + } + + override + void networkProcess(ConsoleEvent event) + { + if (players[consolePlayer].mo == NULL) return; + + int input = mNeteventProcessor.process(event); + + switch (input) + { + case InputResetCustomOrder: resetCustomOrder(); break; + } + } + + override + void renderOverlay(RenderEvent event) + { + if (!mIsInitialized) return; + + if (!mTextureCache.isLoaded) mTextureCache.load(); + + mCaption.show(); + mFadeInOut.fadeInOut((mActivity.isNone()) ? -0.1 : 0.2); + gb_Blur.setEnabled(mOptions.isBlurEnabled() && !mActivity.isNone()); + + double alpha = mFadeInOut.getAlpha(); + + if (mActivity.isNone() && alpha == 0.0) return; + + gb_ViewModel viewModel; + if (mActivity.isWeapons()) mWeaponMenu.fill(viewModel); + else if (mActivity.isInventory()) mInventoryMenu.fill(viewModel); + else if (mActivity.isSpecials()) mspecialsmenu.fill(viewModel); + else if (mActivity.isEquipment()) mEquipmenu.fill(viewModel); + + verifyViewModel(viewModel); + + gb_Dim.dim(alpha, mOptions); + int viewtype = mOptions.getViewType(); + switch(mActivity.getActivity()) + { + case mActivity.Weapons: + case mActivity.Inventory: break; + case mActivity.Specials: viewtype = VIEW_TYPE_WHEEL; break; + case mActivity.Equipments: viewtype = VIEW_TYPE_WHEEL; break; + } + + switch (viewtype) + { + case VIEW_TYPE_BLOCKY: + mBlockyView.setAlpha(alpha); + mBlockyView.setScale(mOptions.getScale()); + mBlockyView.setBaseColor(mOptions.getColor()); + mBlockyView.display(viewModel); + break; + + case VIEW_TYPE_WHEEL: + { + gb_WheelControllerModel controllerModel; + mWheelController.fill(controllerModel); + mWheelIndexer.update(viewModel, controllerModel); + int selectedViewIndex = mWheelIndexer.getSelectedIndex(); + + if (mActivity.isWeapons()) tickIf(mWeaponMenu.setSelectedIndexFromView(viewModel, selectedViewIndex)); + else if (mActivity.isInventory()) tickIf(mInventoryMenu.setSelectedIndex(selectedViewIndex)); + else if (mActivity.isSpecials()) tickIf(mspecialsmenu.setSelectedIndex(selectedViewIndex)); + else if (mactivity.isequipment()) tickIf(mEquipmenu.setSelectedIndex(selectedViewIndex)); + if (selectedViewIndex != -1) viewModel.selectedIndex = selectedViewIndex; + + int selectedIndex; + switch(mActivity.getActivity()) + { + case mActivity.Weapons: selectedIndex = mWeaponMenu.getSelectedIndex(); break; + case mActivity.Inventory: selectedIndex = mInventoryMenu.getSelectedIndex(); break; + case mActivity.Specials: selectedIndex = mspecialsmenu.getSelectedIndex(); break; + case mActivity.Equipments: selectedIndex = mEquipmenu.getSelectedIndex(); break; + } + + mWheelView.setAlpha(alpha); + mWheelView.setBaseColor(mOptions.getColor()); + mWheelView.setRotating(mActivity.isWeapons()); + + int innerIndex = mWheelIndexer.getInnerIndex(selectedIndex, viewModel); + int outerIndex = mWheelIndexer.getOuterIndex(selectedIndex, viewModel); + mWheelView.display( viewModel + , controllerModel + , mOptions.isMouseInWheel() + , innerIndex + , outerIndex + ); + break; + } + + case VIEW_TYPE_TEXT: + mTextView.setAlpha(alpha); + mTextView.setScale(mOptions.getTextScale()); + mTextView.display(viewModel); + break; + + } + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private play + bool isDisabledOnAutomap() const + { + return automapActive && !mOptions.isOnAutomap(); + } + + private play + bool isPlayerFrozen() const + { + return players[consolePlayer].isTotallyFrozen() && !mOptions.isFrozenCanOpen(); + } + + private ui void tickIf(bool mustTick) + { + if (mustTick) mSounds.playTick(); + } + + private ui void toggleWeapons() + { + if (mActivity.isWeapons()) close(); + else openWeapons(); + } + + private ui void toggleInventory() + { + if (mActivity.isInventory()) close(); + else openInventory(); + } + + private ui void toggleSpecials() + { + if(mActivity.isSpecials()) close(); + else openSpecials(); + } + + private ui void toggleEquipments() + { + if(mActivity.isEquipment()) close(); + else openEquipment(); + } + + private ui + void openWeapons() + { + if (gb_Player.isDead() || mWeaponMenu.isThereNoWeapons()) + { + mSounds.playNope(); + return; + } + + mWeaponMenu.setSelectedWeapon(gb_WeaponWatcher.current()); + mSounds.playOpen(); + mActivity.openWeapons(); + } + + private ui + void openInventory() + { + if (gb_Player.isDead() || gb_InventoryMenu.thereAreNoItems()) + { + mSounds.playNope(); + return; + } + + mSounds.playOpen(); + mActivity.openInventory(); + } + + private ui void openSpecials() + { + if (gb_Player.isDead() || playercantspecial()) + { + mSounds.playNope(); + return; + } + + mspecialsmenu.getspecials(players[consolePlayer].readyweapon);//(gb_WeaponWatcher.current()); + if(mspecialsmenu.thereAreNoSpecials()) + { + + //mSounds.playNope(); + gb_Sender.sendGiveItemEvent("GoWeaponSpecialAbility"); //no wheel, instead go normal + return; + } + + mSounds.playOpen(); + mActivity.openSpecials(); + } + + private ui void openEquipment() + { + if (gb_Player.isDead() || mEquipmenu.noequipments()) + { + mSounds.playNope(); + return; + } + + mSounds.playOpen(); + mActivity.openEquipment(); + } + + private clearscope void close() + { + mSounds.playClose(); + mActivity.close(); + } + + private ui bool playercantspecial() + { + return (players[consoleplayer].mo.findinventory("CantWeaponSpecial")); + } + + private ui void confirmSelection() + { + switch(mActivity.getactivity()) + { + case gb_activity.Weapons: + gb_Sender.sendSelectEvent(mWeaponMenu.confirmSelection()); break; + case gb_activity.Inventory: + gb_Sender.sendUseItemEvent(mInventoryMenu.confirmSelection()); break; + case gb_activity.Specials: + gb_Sender.sendGiveItemEvent("GoWeaponSpecialAbility"); //AAAA i forgot this, and was wondering why this wasnt working + gb_Sender.sendGiveItemEvent(mspecialsmenu.ConfirmSelection()); break; + case gb_activity.Equipments: + gb_Sender.sendGiveItemEvent("ToggleEquipment"); + gb_Sender.sendGiveItemEvent(mEquipmenu.ConfirmSelection()); break; + } + } + + private ui + void rotateWeaponPriority() + { + if (mActivity.isWeapons()) + { + gb_CustomWeaponOrderStorage.savePriorityRotation(mWeaponSetHash, mWeaponMenu.getSelectedIndex()); + mWeaponMenu.rotatePriority(); + } + } + + private ui + void rotateWeaponSlot() + { + if (mActivity.isWeapons()) + { + gb_CustomWeaponOrderStorage.saveSlotRotation(mWeaponSetHash, mWeaponMenu.getSelectedIndex()); + mWeaponMenu.rotateSlot(); + } + } + + private + void resetCustomOrder() + { + gb_CustomWeaponOrderStorage.reset(mWeaponSetHash); + gb_WeaponData weaponData; + gb_WeaponDataLoader.load(weaponData); + mWeaponMenu = gb_WeaponMenu.from(weaponData, mOptions); + } + + private ui + void verifyViewModel(gb_ViewModel model) + { + uint nTags = model.tags .size(); + uint nSlots = model.slots .size(); + uint nIndices = model.indices .size(); + uint nIcons = model.icons .size(); + uint nIconScaleXs = model.iconScaleXs .size(); + uint nIconScaleYs = model.iconScaleYs .size(); + uint nQuantities1 = model.quantity1 .size(); + uint nQuantitiesMax1 = model.maxQuantity1.size(); + uint nQuantities2 = model.quantity2 .size(); + uint nQuantitiesMax2 = model.maxQuantity2.size(); + + if (nTags > 0 + && (model.selectedIndex >= nTags + || nTags != nSlots + || nTags != nIndices + || nTags != nIcons + || nTags != nIconScaleXs + || nTags != nIconScaleYs + || nTags != nQuantities1 + || nTags != nQuantitiesMax1 + || nTags != nQuantities2 + || nTags != nQuantitiesMax2)) + { + Console.printf("Bad view model:\n" + "selected index: %d,\n" + "tags: %d,\n" + "slots: %d,\n" + "indices: %d,\n" + "icons: %d,\n" + "icon scale X: %d,\n" + "icon scale Y: %d,\n" + "quantities 1: %d,\n" + "max quantities 1: %d,\n" + "quantities 2: %d,\n" + "max quantities 2: %d,\n" + , model.selectedIndex + , nTags + , nSlots + , nIndices + , nIcons + , nIconScaleXs + , nIconScaleYs + , nQuantities1 + , nQuantitiesMax1 + , nQuantities2 + , nQuantitiesMax2 + ); + } + } + + bool isOpened() //ig this could work to indicate when the wheel is opened, just need a pointer to the handler + { + if(!mIsInitialized) //it cant be opened if its not initialized + return false; + return !mActivity.isNone(); + } + + enum ViewTypes + { + VIEW_TYPE_BLOCKY = 0, + VIEW_TYPE_WHEEL = 1, + VIEW_TYPE_TEXT = 2, + } + + private + void initialize() + { + mOptions = gb_Options.from(); + mFontSelector = gb_FontSelector.from(); + mSounds = gb_Sounds.from(mOptions); + + gb_WeaponData weaponData; + gb_WeaponDataLoader.load(weaponData); + mWeaponSetHash = gb_CustomWeaponOrderStorage.calculateHash(weaponData); + mWeaponMenu = gb_WeaponMenu.from(weaponData, mOptions); + gb_CustomWeaponOrderStorage.applyOperations(mWeaponSetHash, mWeaponMenu); + mInventoryMenu = gb_InventoryMenu.from(); + mSpecialsmenu = gb_specialsmenu.from(); + mEquipmenu = gb_equipmentmenu.from(); + + mActivity = gb_Activity.from(); + mFadeInOut = gb_FadeInOut.from(); + mFreezer = gb_Freezer.from(mOptions); + + mTextureCache = gb_TextureCache.from(); + mCaption = gb_Caption.from(); + mInventoryUser = gb_InventoryUser.from(); + mChanger = gb_Changer.from(mCaption, mOptions, mInventoryUser); + mNeteventProcessor = gb_NeteventProcessor.from(mChanger); + + mBlockyView = gb_BlockyView.from(mTextureCache, mOptions, mFontSelector); + mTextView = gb_TextView.from(mOptions, mFontSelector); + + mMultiWheelMode = gb_MultiWheelMode.from(mOptions); + let screen = gb_Screen.from(mOptions); + mWheelView = gb_WheelView.from( mOptions + , mMultiWheelMode + , mTextureCache + , screen + , mFontSelector + ); + mWheelController = gb_WheelController.from(mOptions, screen); + mWheelIndexer = gb_WheelIndexer.from(mMultiWheelMode, screen); + + mIsInitialized = true; + } + + private gb_Options mOptions; + private gb_FontSelector mFontSelector; + private gb_Sounds mSounds; + + private string mWeaponSetHash; + private gb_WeaponMenu mWeaponMenu; + private gb_InventoryMenu mInventoryMenu; + private gb_specialsmenu mspecialsmenu; //pb specials thing + private gb_equipmentmenu mEquipmenu; //pb equipments thing + private gb_Activity mActivity; + private gb_FadeInOut mFadeInOut; + private gb_Freezer mFreezer; + + private gb_TextureCache mTextureCache; + private gb_Caption mCaption; + private gb_InventoryUser mInventoryUser; + private gb_Changer mChanger; + private gb_NeteventProcessor mNeteventProcessor; + + private gb_BlockyView mBlockyView; + private gb_TextView mTextView; + + private gb_MultiWheelMode mMultiWheelMode; + private gb_WheelView mWheelView; + private gb_WheelController mWheelController; + private gb_WheelIndexer mWheelIndexer; + + private bool mIsInitialized; + +} // class gb_EventHandler diff --git a/zscript/gearbox/event_processor.zs b/zscript/gearbox/event_processor.zs new file mode 100644 index 0000000000..eb23805ea1 --- /dev/null +++ b/zscript/gearbox/event_processor.zs @@ -0,0 +1,40 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_EventProcessor +{ + + static + int process(ConsoleEvent event, bool isSelectOnKeyUp) + { + if (event.name == "gb_toggle_weapon_menu") return InputToggleWeaponMenu; + if (event.name == "gb_toggle_weapon_menu_up" && isSelectOnKeyUp) return InputConfirmSelection; + + if (event.name == "gb_toggle_inventory_menu") return InputToggleInventoryMenu; + if (event.name == "gb_toggle_inventory_menu_up" && isSelectOnKeyUp) return InputConfirmSelection; + if (event.name == "gb_rotate_weapon_priority") return InputRotateWeaponPriority; + if (event.name == "gb_rotate_weapon_slot" ) return InputRotateWeaponSlot; + + if (event.name == "pb_special_wheel") return InputToggleSpecialMenu; + if (event.name == "pb_special_wheel_up" && isSelectOnKeyUp) return InputConfirmSelection; + if (event.name == "pb_equip_wheel") return InputToggleEquipMenu; + if (event.name == "pb_equip_wheel_up" && isSelectOnKeyUp) return InputConfirmSelection; + + return InputNothing; + } + +} // class gb_EventProcessor diff --git a/zscript/gearbox/font_selector.zs b/zscript/gearbox/font_selector.zs new file mode 100644 index 0000000000..fa10c6d978 --- /dev/null +++ b/zscript/gearbox/font_selector.zs @@ -0,0 +1,53 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_FontSelector +{ + + static + gb_FontSelector from() + { + let result = new("gb_FontSelector"); + result.mFontString = gb_Cvar.from("gb_font"); + return result; + } + + Font getFont() + { + Font newFont = Font.getFont(mFontString.getString()); + + if (newFont == NULL && mOldFont != NULL) + { + Console.printf(StringTable.localize("$GB_BAD_FONT"), mFontString.getString()); + } + + mOldFont = newFont; + + if (newFont == NULL) + { + newFont = NewSmallFont; + } + + return newFont; + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + + private gb_Cvar mFontString; + private transient Font mOldFont; +} diff --git a/zscript/gearbox/freezer.zs b/zscript/gearbox/freezer.zs new file mode 100644 index 0000000000..a6383a0d58 --- /dev/null +++ b/zscript/gearbox/freezer.zs @@ -0,0 +1,129 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_Freezer play +{ + + static + gb_Freezer from(gb_Options options) + { + let result = new("gb_Freezer"); + result.mWasFrozen = false; + result.mOptions = options; + result.mWasLevelFrozen = false; + return result; + } + + void freeze() + { + if (mWasFrozen) return; + mWasFrozen = true; + + int freezeMode = mOptions.getTimeFreezeMode(); + if (isLevelFreezeEnabled (freezeMode)) freezeLevel(); + if (isPlayerFreezeEnabled(freezeMode)) freezePlayer(); + } + + void thaw() + { + if (!mWasFrozen) return; + mWasFrozen = false; + + if (isLevelThawEnabled()) thawLevel(); + thawPlayer(); + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + /** + * Corresponds to gb_FreezeValues in menudef. + */ + private static + bool isLevelFreezeEnabled(int freezeMode) + { + return !multiplayer && (freezeMode == 1); + } + + /** + * Corresponds to gb_FreezeValues in menudef. + * + * Freezing level without player causes weird behavior, like weapon bobbing + * while Gearbox is open. So, freeze player when level is frozen too. + */ + private static + bool isPlayerFreezeEnabled(int freezeMode) + { + return freezeMode != 0; + } + + /** + * Thaw regardless of freeze mode. + */ + private static + bool isLevelThawEnabled() + { + return !multiplayer; + } + + private + void freezeLevel() + { + mWasLevelFrozen = level.isFrozen(); + level.setFrozen(true); + } + + private + void freezePlayer() + { + mWasPlayerFrozen = true; + + PlayerInfo player = players[consolePlayer]; + + mCheats = player.cheats; + mVelocity = player.mo.vel; + mGravity = player.mo.gravity; + + gb_Sender.sendFreezePlayerEvent(player.cheats | FROZEN_CHEATS_FLAGS, (0, 0, 0), 0); + } + + private + void thawLevel() const + { + level.setFrozen(mWasLevelFrozen); + } + + private + void thawPlayer() const + { + if (mWasPlayerFrozen) gb_Sender.sendFreezePlayerEvent(mCheats, mVelocity, mGravity); + mWasPlayerFrozen = false; + } + + const FROZEN_CHEATS_FLAGS = CF_TotallyFrozen | CF_Frozen; + + private bool mWasFrozen; + + private bool mWasLevelFrozen; + private bool mWasPlayerFrozen; + + private int mCheats; + private vector3 mVelocity; // to reset weapon bobbing. + private double mGravity; + + private gb_Options mOptions; + +} // class gb_Freezer diff --git a/zscript/gearbox/gearboxInfo/Readme.gearbox b/zscript/gearbox/gearboxInfo/Readme.gearbox new file mode 100644 index 0000000000..f5c910b4d1 --- /dev/null +++ b/zscript/gearbox/gearboxInfo/Readme.gearbox @@ -0,0 +1,83 @@ +# Gearbox + +Gearbox is an add-on for GZDoom engine that provides more convenient ways to +select weapons and items. + +This mod is a part of [m8f's toolbox](https://mmaulwurff.github.io/pages/toolbox). + +![Blocks with Treasure Tech](screenshots/blocks-treasure-tech.png) + +(Blocks with [Treasure Tech](https://forum.zdoom.org/viewtopic.php?f=43&t=66995)) + +[![Wheel with Heretic](screenshots/inventory-wheel-heretic.png)](https://youtu.be/WXZUBtbsY7Y) + +(Wheel with Heretic) + +## How to Use + +GZDoom 4.5 required. + +1. open the menu by assigned key, or by next/previous weapon keys, if enabled in + options +2. select the weapon with next/previous weapon keys, or with mouse (wheel only) + +## Features + +- Different representations: blocks, wheel, plain text +- Press Fire key to select and Alt Fire key to cancel +- Color and scale options +- Behavior options +- Multiplayer compatible +- Reaction to number keys +- extras.wad icon support for vanilla weapons +- Inventory item selection + +## Planned + +- Patches for weapon icon adjustments +- More representations +- Moving weapon between slots and changing order + +## Note for Weapon Mod Authors + +If you want Gearbox to support your mod out of the box, assign +Inventory.AltHudIcon for your weapons! Tag property is also nice to have. + +## Compatibility Issues + +- [PyWeaponWheel v0.3](https://forum.zdoom.org/viewtopic.php?f=43&t=61061) + overrides time freezing. If you are using both mods and want to freeze time + with Gearbox, set PyWeaponWheel's option "Freeze when wheel is open" + (`py_weaponwheel_freeze` CVar) to Off. + + Note that PyWeaponWheel may be built in some mods, for example in Project + Brutality. The solution is the same: disable time PyWeaponWheel's time + freezing. + +## Known Issues + +- Weapon icons in wheel aren't affected by "HUD preserves aspect ration" option. +- Mouse input in wheel in multiplayer causes screen shake. + +## License + +- code: [GPLv3](copying.txt) + +## Acknowledgments + +- Thanks to kadu522 for general help and support. +- Blocky view is designed to resemble the weapon menu from Half-Life by Valve. +- Thanks to Marrub for [ZScriptDoc](https://github.com/marrub--/zdoom-doc). +- Thanks to Talon1024 for help with time freezing option. +- Thanks to Player701 for help with key event processing code. +- Thanks to KeksDose for a concept of VM abort handler. +- Thanks to DrPyspy for allowing to use mouse input code from PyWeaponWheel. +- Thanks to Carrascado for bug fixes and new features. +- Thanks to Accensus, Proydoha, mamaluigisbagel, TheRailgunner, Captain J, + Enjay, StroggVorbis, krutomisi, Cutmanmike, StraightWhiteMan, JohnDoe8, HDV, + Zhs2 and Apollucas for feature suggestions. +- Thanks to Accensus, Proydoha, mamaluigisbagel, Ac!d, wildweasel, + Dark-Assassin, rparhkdtp, Samarai1000, Mr. Blazkowicz, lucker42, spectrefps, + Someone64, Lippeth, JMartinez9820, generic name guy and sebastianpanetta for + bug reports. +- See also [credits list](credits.md). diff --git a/zscript/gearbox/gearboxInfo/changelog.gearbox b/zscript/gearbox/gearboxInfo/changelog.gearbox new file mode 100644 index 0000000000..7365f62a9b --- /dev/null +++ b/zscript/gearbox/gearboxInfo/changelog.gearbox @@ -0,0 +1,333 @@ + +v0.7.2 +- 2022-07-13 don't open weapon menu if there is no weapons +- 2022-07-13 fix text out of weapon blocks +- 2022-07-13 replace ellipsis with three dots for fonts that don't have ellipsis +- 2022-07-13 Merge remote-tracking branch 'origin/master' +- 2022-07-13 Feature/option select first slot weapon (#32) +- 2022-07-12 update copyright notice +- (Carrascado's/master) 2022-07-12 Feature: option to reverse slot cycle order (#31) +- 2022-07-12 update copyright notice and Readme +- 2022-07-12 add audio feedback when menu cannot be opened +- 2022-07-11 I fix a bug by which the inventory could be "invisibly" opened and closed when there were no items +- 2022-07-12 Merge pull request #29 from Carrascado/feature/turn-toggle-sound-into-open-and-close +- (Carrascado's/feature/turn-toggle-sound-into-open-and-close, feature/turn-toggle-sound-into-open-and-close) 2022-07-11 Now there is a code-wise difference between the open and close menu sounds +- 2022-07-11 Fix #28: toggle and tick sounds played at the same time + +v0.7.1 +- 2022-06-26 fix some spelling +- 2022-06-26 delete scripts (moved to a separate repository) +- (tag: v0.7.1-b) 2022-06-26 fix a crash on Delta Touch with GLES 3.2 caused by blur shader +- 2021-11-07 add menu command to copy custom player color +- 2021-10-23 make custom weapon order keys descriptions more clear +- 2021-10-01 fix gitignore +- 2021-08-28 search for textures more precisely + +v0.7.0 +- 2021-08-28 don't scale weapons with no icons in wheel +- 2021-08-28 prevent weapon sway in wheel +- 2021-08-28 make Zabor shorter +- 2021-08-28 fix changing weapon order +- 2021-08-22 fix keyconf lump +- 2021-08-22 adapt to inverted mouse settings +- 2021-08-22 organize menus +- 2021-08-22 fix #15: VM abort in inventory menu +- 2021-08-22 more readable zabor +- 2021-08-22 synchronize cvar lists +- 2021-08-21 add resetting custom weapon order +- 2021-08-18 add key to change weapon slot +- 2021-08-15 add key to change weapon priority +- 2021-08-13 add font selection +- 2021-08-12 update Readme again +- 2021-08-12 update Readme + +v0.6.0 +- 2021-08-12 fix VM abort in text view when no weapons +- 2021-08-12 add plain text view +- 2021-08-10 fix #12: take into account weapon scaling properties for icons +- 2021-08-10 fix #11: SWWM GZ - two Explodium guns are selectable +- 2021-08-10 fix #10: velocity after freezing was restored even if freezing is off +- 2021-08-10 fix #8: unblock mouse input in Blocks view +- 2021-07-06 update build script +- 2021-04-14 add more null checks + +v0.5.1 +- 2021-04-13 fix menudef code style +- 2021-04-13 scripts cleanup +- 2021-04-13 check spelling +- 2021-04-13 add space after menu titles +- 2021-04-13 fix using inventory items when player is frozen +- 2021-04-11 fix wheel hands length + +v0.5.0 +- 2021-04-11 fix truncation of floating point warning +- 2021-04-11 update Zabor, and an option to disable it +- 2021-04-11 Revert "wheel: draw weapon tag if it has no icon" +- 2021-04-10 add wheel scale +- 2021-04-07 separate Screen class from blocks view, move to wheel +- 2021-04-07 remove redundant menu option +- 2021-04-07 move text drawing class to wheel +- 2021-04-07 display weapon name tags on change with native GZDoom function +- 2021-04-04 fix gravity after closing gearbox +- 2021-04-04 wheel: draw weapon tag if it has no icon +- 2021-04-03 add compatibility with Event Handler Destroyer +- 2021-04-01 add Gearbox options menu to simplified options menu +- 2021-03-26 don't call statusbar instance for static getInventoryIcon function +- 2021-03-25 fix height when frozen in Heretic water +- 2021-03-24 move networking to sender +- 2021-03-24 add icon to options menu +- 2021-03-20 rework player and enemies freezing +- 2021-03-20 add KeksDose to Acknowledgments +- 2021-03-07 new input +- 2021-03-12 update VM abort handler +- 2021-03-03 fix blocks view positioning + +v0.4.1 +- 2021-03-02 update Readme +- 2021-03-02 hardcode icons for Smooth Doom +- 2021-03-02 prevent vm abort when no inventory items +- 2021-03-02 fix selecting hidden weapons +- 2021-03-02 make previous weapon carry over to the next level +- 2021-03-01 prevent blocks inventory view from aborting VM when there are no items +- 2021-03-01 don't draw placeholder icon in blocks view +- 2021-03-01 increase max multiwheel threshold, draw selected weapon on top +- 2021-03-01 add note about keys to Readme +- 2021-03-01 fix constant ticking on tomed weapons +- 2021-03-01 fix vm abort when the last inventory item is used up +- 2021-03-01 fix misaligned quantity dots when quantity is odd +- 2021-02-28 fix spelling, add missing cvars to zabor and gb_reset +- 2021-02-28 fix truncation of floating point values + +v0.4 +- 2021-02-28 update Readme +- 2021-02-28 better handling of tall weapon icons +- 2021-02-28 minor naming refactoring (still a lot of names to go...) +- 2021-02-28 don't rotate items in wheel, respect preserve aspect ration setting +- 2021-02-28 minor refactoring +- 2021-02-28 naming refactoring +- 2021-02-27 add an option to set if frozen player can open Gearbox +- 2021-02-27 move UI options to a submenu +- 2021-02-27 add reset to defaults command +- 2021-02-27 make position configurable for blocks view +- 2021-02-27 add blur option +- 2021-02-27 code style +- 2021-02-27 options refactoring +- 2021-02-25 add a command for manual report +- 2021-02-24 add cvars dump to vm event handler +- 2021-02-24 add vm abort handler +- 2021-02-23 add select previous weapon key from m_gizmos +- 2021-02-23 fix complementary colors in blocks view +- 2021-02-21 fix division by zero when max ammo is 0 +- 2021-02-20 don't draw 10 pips if ammo or number of items is less than 10 +- 2021-02-20 wheel: don't move weapon description if not necessary +- 2021-02-19 fix repeated ticking when player has no weapons +- 2021-02-19 don't show amount for items with max amount 1 +- 2021-02-19 add menudef entry for inventory menu key +- 2021-02-18 allow slot keys in inventory menu +- 2021-02-18 add tick sound to inventory menu +- 2021-02-18 fix next/previous buttons in inventory menu +- 2021-02-15 barebones inventory menu +- 2021-02-16 update screenshots, update Readme + +v0.3.2 +- 2021-02-16 increase minimal angle between wheel hands for clarity +- 2021-02-16 make wheel hands transparent in the middle +- 2021-02-16 draw the selected thing in the second wheel on top of other things +- 2021-02-16 remove a message for obsolete key bind +- 2021-02-16 fix out of bounds angle on the second wheel +- 2021-02-15 limit minimal angle between wheel hands +- 2021-02-15 let Gearbox know the current selected weapon + +v0.3.1 +- 2021-02-14 add missing people to Acknowledgments +- 2021-02-14 fix spelling +- 2021-02-14 menu cleanup +- 2021-02-14 make sound effects optional +- 2021-02-14 wheel: don't move weapon description if not in multiwheel mode +- 2021-02-14 disable Gearbox in intermission screen +- 2021-02-13 remove partial LZDoom compatibility + +v0.3 +- 2021-02-13 fix VM abort when no weapons, AGAIN +- 2021-02-13 update Readme +- 2021-02-13 smoother text background image +- 2021-02-13 make lower limit for text box width +- 2021-02-13 add optional locked position mode +- 2021-02-13 more distinct elements color for black or white blocks view +- 2021-02-13 make displaying weapon tags on weapon change respect GZDoom option +- 2021-02-13 fix drawing text box on top of wheel when the screen is scaled +- 2021-02-13 add an option to display weapon tags +- 2021-02-13 unify fonts, remove font selection +- 2021-02-13 add weapon tags to blocks view +- 2021-02-13 add weapon tag display after changing weapons +- 2021-02-12 remove one parameter from text box drawing function +- 2021-02-12 move drawing text into a separate file +- 2021-02-12 micro cleanup +- 2021-02-12 move some display-related files into a directory +- 2021-02-12 move texture cache out of wheel view, cache blocks view textures +- 2021-02-12 more text box drawing refactoring +- 2021-02-11 fix text box color and increase background image resolution +- 2021-02-11 refactor drawing weapon description +- 2021-02-11 add weapon tag display to wheel +- 2021-02-05 fix blocks view index +- 2021-02-02 add open/close sound +- 2021-02-01 add tick sound +- 2021-01-29 allow catching more than 2 key bindings +- 2021-01-26 add a note regarding PyWeaponWheel +- 2021-01-26 cache view model +- 2021-01-26 store ammo pip size in cache +- 2021-01-26 cache scale factor in wheel +- 2021-01-26 add texture cache to wheel. performance gain is non-existent +- 2021-01-25 fix warnings, combine coordinate operations into vector operations +- 2021-01-24 implement ammo display on wheel +- 2021-01-23 make disabling Gearbox on automap optional +- 2021-01-23 fix overriding icons from invalid ones from service +- 2021-01-22 LZDoom half-compatibility: only Blocks view +- 2021-01-21 update Readme +- 2021-01-21 add HideService to show DualExplodiumGun from SWWM GZ only when appropriate +- 2021-01-21 implement icon patches via Service +- 2021-01-21 Merge branch 'master' into mpatch +- 2021-01-20 more consistent selection in multiwheel +- 2021-01-20 multiwheel: click on slot selects the first weapon in this slot +- 2021-01-19 add patches with icon support +- 2021-01-19 update Readme +- 2021-01-19 fix bug with not selecting slot on slot key +- 2021-01-19 add an option to open menu on slot key +- 2021-01-19 move slot input check to a function +- 2021-01-18 don't open gearbox in automap +- 2021-01-18 fix file conflict with other mods +- 2021-01-17 add screen dim color option +- 2021-01-17 fix VM abort on no weapons again +- 2021-01-17 tweak wheel hands graphics +- 2021-01-17 remove unnecessary hiding of wheel elements on alpha != 1 +- 2021-01-17 fix weapon selection snapping to selected weapon on fade out +- 2021-01-17 close weapon selection by altfire +- 2021-01-16 add smooth wheel graphics by Accensus + +v0.2.2 +- 2021-01-13 add gradient to wheel background +- 2021-01-13 fix spelling +- 2021-01-13 add multiwheel engagement weapon number slider +- 2021-01-13 refactoring: move wheel/multiwheel mode decision to a separate class +- 2021-01-12 prevent opening gearbox while dead +- 2021-01-12 more reliable freezing while menu is opened +- 2021-01-11 fix VM abort in hubs +- 2021-01-09 add note to weapon mod authors to Readme +- 2021-01-07 remove obsolete comment + +v0.2.1 +- 2021-01-07 add time freezing +- 2021-01-07 fix multiplayer synchronization +- 2021-01-07 fix reaction to slot keys in some circumstances +- 2021-01-07 fix selecting a weapon if it's only one in slot +- 2021-01-06 add protection against the case when player has no weapons at all +- 2021-01-06 make colored tint on wheel weapons optional +- 2021-01-06 add an option to select font for wheel slots +- 2021-01-06 add little notches on sides of wheel selection bars +- 2021-01-06 add a link to toolbox page to Readme +- 2021-01-06 update Readme + +v0.2 +- 2021-01-06 reorganize option menu +- 2021-01-06 add an option for wheel horizontal position +- 2021-01-06 fix preventing cheat code input in some circumstances +- 2021-01-06 fix possible vm abort when dropping weapons +- 2021-01-06 fix scaling for blocks view +- 2021-01-06 fix scaling wheel pointer +- 2021-01-06 fix wheel weapon selection scaling +- 2021-01-06 fix slot label scaling +- 2021-01-06 remove hardcoded radius +- 2021-01-06 react to scaling changes +- 2021-01-06 fix VM abort due to computational error +- 2021-01-06 add screenshot with multiwheel +- 2021-01-06 fix VM abort with QCDE +- 2021-01-05 restore indexing the wheel by keys +- 2021-01-05 multiwheel: indexer works fine +- 2021-01-05 prettier multiwheel +- 2021-01-05 wip multiwheel more +- 2021-01-05 optimize graphics +- 2021-01-04 wip multiwheel +- 2021-01-04 don't let wheel pointer get from screen bounds +- 2021-01-04 fix VM abort with blocks view when there is only one weapon +- 2021-01-04 reset wheel pointer on number keys +- 2021-01-04 fix VM abort with QCDE +- 2021-01-04 don't reset wheel state on unrelated input keys +- 2021-01-04 update Readme +- 2021-01-04 fix bug with cloning the wheel twice +- 2021-01-04 minor wheel view cleanup +- 2021-01-04 add changelog script +- 2021-01-04 update Readme +- 2021-01-04 add an option to immediately select weapon if it's only one in slot +- 2021-01-04 add reaction to number keys +- 2021-01-04 move screen dimming out of the wheel view and make it optional +- 2021-01-04 minor wheel view cleanup +- 2021-01-04 update Readme +- 2021-01-04 add an option to select the weapon on key release +- 2021-01-04 implement mouse sensitivity sliders +- 2021-01-04 code style +- 2021-01-04 minor cleanup +- 2021-01-04 don't show ammo2 view if it's the same as ammo1 +- 2021-01-03 add an option to disable mouse input in weapon wheel +- 2021-01-03 bump required GZDoom version +- 2021-01-03 update Readme once more + +v0.1 +- 2021-01-03 update Readme +- 2021-01-03 update Readme, add screenshots +- 2021-01-03 better reaction to opening menu of prev/next weapon key +- 2021-01-03 make weapon wheel selection distinguishable when it's two weapons +- 2021-01-03 add an option to open menu on next/previous weapon key press +- 2021-01-03 update copyright year +- 2021-01-03 move options access to a separate file +- 2021-01-03 fix compatibility with Treasure Tech +- 2021-01-03 fix VM abort on titlemaps +- 2021-01-03 enable switching between views +- 2021-01-03 minor refactoring +- 2021-01-03 get rid of buggy wheel selection interpolation +- 2021-01-03 rotate tall weapons so they fit in the weapon wheel +- 2021-01-03 fix wrong index in weapon wheel +- 2021-01-02 stop capturing mouse events after selecting weapons +- 2021-01-02 move some files to directories +- 2020-12-09 add a separate flag variable for event handler initialization state +- 2020-12-09 refactor weapon watcher +- 2020-12-09 squash script warnings +- 2020-12-09 refactor level loading state into a function +- 2020-12-09 refactor Activity class +- 2020-11-23 implement weapon wheel selection interpolation +- 2020-11-23 implement weapon wheel indexer +- 2020-11-23 add visible weapon selection to weapon wheel +- 2020-10-18 add weapon wheel +- 2020-10-15 add Marrub to Acknowledgments +- 2020-10-15 remove unused stuff from Blocky view +- 2020-10-15 add color setting +- 2020-10-15 rename HL weapon view to Blocky View +- 2020-10-14 add scaling +- 2020-10-13 HL view refactoring +- 2020-10-13 add placeholder weapon icon +- 2020-10-11 add fade in-out +- 2020-10-11 made view static +- 2020-10-11 add automatic scaling if weapon doesn't fit into the box in HL view +- 2020-10-11 add scrolling weapons back +- 2020-10-11 add ammo2 display +- 2020-10-11 add selected slot drawing +- 2020-10-11 add basic Half-Life-like weapon menu (WIP) +- 2020-10-11 fix view model +- 2020-10-10 fix weapon order +- 2020-10-10 add known words list for code spellcheck +- 2020-10-10 add view model filling +- 2020-10-09 add view model +- 2020-10-09 implement selection confirmation +- 2020-10-09 propagate changes through network +- 2020-10-08 add keysection +- 2020-10-08 add toggling weapon menu +- 2020-10-08 add input processor +- 2020-10-08 add command and selecting next weapon logic +- 2020-10-08 add weapon watcher +- 2020-10-08 add weapon menu state +- 2020-10-08 weapon data loading algorithm tweak +- 2020-10-08 add code spelling script +- 2020-10-07 add weapon data loader to event handler +- 2020-10-07 add weapon data loader +- 2020-10-07 add license +- 2020-10-07 add build script +- 2020-10-06 add Readme diff --git a/zscript/gearbox/gearboxInfo/copying.gearbox b/zscript/gearbox/gearboxInfo/copying.gearbox new file mode 100644 index 0000000000..f288702d2f --- /dev/null +++ b/zscript/gearbox/gearboxInfo/copying.gearbox @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/zscript/gearbox/gearboxInfo/credits.gearbox b/zscript/gearbox/gearboxInfo/credits.gearbox new file mode 100644 index 0000000000..f1cfb0e1c3 --- /dev/null +++ b/zscript/gearbox/gearboxInfo/credits.gearbox @@ -0,0 +1,16 @@ +# Gearbox Credits + +- all graphics are under [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/). +- gb_circ.png, gb_hand.png, gb_hcir.png and gb_pntr.png are made by Accensus with minor edits by m8f. +- gb_tick.ogg is converted from + [384187__malle99__click-tick.wav](https://freesound.org/people/malle99/sounds/384187/) + by malle99. [Creative Commons 0 License](http://creativecommons.org/publicdomain/zero/1.0/) +- gb_toggle.ogg is converted from + [362625__kermite607__metal-clang-sound.wav](https://freesound.org/people/kermite607/sounds/362625/) + by kermite607. [Creative Commons 0 License.](http://creativecommons.org/publicdomain/zero/1.0/) +- gb_nope.ogg is [Switch.ogg](https://freesound.org/people/egomassive/sounds/536802/) by egomassive [Creative Commons 0 License.](http://creativecommons.org/publicdomain/zero/1.0/) +- shaders/mfx_bss_blur.fp is made by [Marisa + Kirisame](https://github.com/OrdinaryMagician/), taken from + [MariFX](https://github.com/OrdinaryMagician/marifx_m) (GPLv3), modified by m8f. +- Wheel mouse input code is based on code from PyWeaponWheel by DrPyspy. +- [MD5 Implementation in ZScript](https://github.com/3saster/GZDoom_Utilities) by 3saster (see [MD5.zs](zscript/MD5/MD5.zs) for license). diff --git a/zscript/gearbox/input.zs b/zscript/gearbox/input.zs new file mode 100644 index 0000000000..9a76669bcb --- /dev/null +++ b/zscript/gearbox/input.zs @@ -0,0 +1,58 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +enum gb_Inputs +{ + + InputNothing, + + InputSelectNextWeapon, + InputSelectPrevWeapon, + InputConfirmSelection, + InputToggleWeaponMenu, + + InputSelectSlotBegin, + InputSelectSlotEnd = InputSelectSlotBegin + 11, + InputClose, + + InputToggleInventoryMenu, + InputConfirmInventorySelection, + + InputRotateWeaponPriority, + InputRotateWeaponSlot, + InputResetCustomOrder, + + InputToggleSpecialMenu, + InputToggleEquipMenu, +} // enum gb_Inputs + +class gb_Input +{ + + static + bool isSlot(gb_Inputs input) + { + return (InputSelectSlotBegin <= input && input <= InputSelectSlotEnd); + } + + static + int getSlot(gb_Inputs input) + { + return input - InputSelectSlotBegin; + } + +} // class gb_Input diff --git a/zscript/gearbox/input_processor.zs b/zscript/gearbox/input_processor.zs new file mode 100644 index 0000000000..d70fc0dc56 --- /dev/null +++ b/zscript/gearbox/input_processor.zs @@ -0,0 +1,55 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_InputProcessor +{ + + static + gb_Inputs process(InputEvent event) + { + if (event.type != InputEvent.Type_KeyDown) return InputNothing; + + int key = event.keyScan; + if (isKeyForCommand(key, "weapNext" )) return InputSelectNextWeapon; + if (isKeyForCommand(key, "weapPrev" )) return InputSelectPrevWeapon; + if (isKeyForCommand(key, "+attack" )) return InputConfirmSelection; + if (isKeyForCommand(key, "+altAttack")) return InputClose; + + for (int i = 0; i <= 11; ++i) + { + if (isKeyForCommand(key, string.format("slot %d", i))) return i + InputSelectSlotBegin; + } + + return InputNothing; + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private static + bool isKeyForCommand(int key, string command) + { + Array keys; + bindings.getAllKeysForCommand(keys, command); + uint nKeys = keys.size(); + for (uint i = 0; i < nKeys; ++i) + { + if (keys[i] == key) return true; + } + return false; + } + +} // class gb_InputProcessor diff --git a/zscript/gearbox/inventory_menu.zs b/zscript/gearbox/inventory_menu.zs new file mode 100644 index 0000000000..e880162b64 --- /dev/null +++ b/zscript/gearbox/inventory_menu.zs @@ -0,0 +1,143 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2021 + * Carrascado 2022 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_InventoryMenu +{ + + static + gb_InventoryMenu from() + { + let result = new("gb_InventoryMenu"); + + result.mSelectedIndex = 0; + + return result; + } + + static + bool thereAreNoItems() + { + return getItemsNumber() == 0; + } + + string confirmSelection() const + { + let item = players[consolePlayer].mo.inv; + int index = 0; + while (item != NULL) + { + if (item.bInvBar) + { + if (index == mSelectedIndex) return item.getClassName(); + ++index; + } + item = item.inv; + } + + return ""; + } + + ui + bool selectNext() + { + int nItems = getItemsNumber(); + if (nItems == 0) return false; + + mSelectedIndex = (mSelectedIndex + 1) % nItems; + + return true; + } + + ui + bool selectPrev() + { + int nItems = getItemsNumber(); + if (nItems == 0) return false; + + mSelectedIndex = (mSelectedIndex - 1 + nItems) % nItems; + + return true; + } + + ui + bool setSelectedIndex(int index) + { + if (index == -1 || mSelectedIndex == index) return false; + + mSelectedIndex = index; + + return true; + } + + ui + int getSelectedIndex() const + { + return mSelectedIndex; + } + + ui + void fill(out gb_ViewModel viewModel) + { + let item = players[consolePlayer].mo.inv; + int index = 0; + while (item != NULL) + { + if (item.bInvBar) + { + string tag = item.getTag(); + let icon = BaseStatusBar.getInventoryIcon(item, BaseStatusBar.DI_AltIconFirst); + //int(BaseStatusBar.getInventoryIcon(item, BaseStatusBar.DI_AltIconFirst)); + viewModel.tags .push(tag); + viewModel.slots .push(index + 1); + viewModel.indices .push(index); + viewModel.icons .push(icon); + viewModel.iconScaleXs .push(1); + viewModel.iconScaleYs .push(1); + viewModel.quantity1 .push(item.maxAmount > 1 ? item.amount : -1); + viewModel.maxQuantity1.push(item.maxAmount); + viewModel.quantity2 .push(-1); + viewModel.maxQuantity2.push(-1); + + ++index; + } + item = item.inv; + } + + mSelectedIndex = min(mSelectedIndex, getItemsNumber() - 1); + if (mSelectedIndex == -1 && getItemsNumber() > 0) mSelectedIndex = 0; + viewModel.selectedIndex = mSelectedIndex; + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private static + int getItemsNumber() + { + let item = players[consolePlayer].mo.inv; + int result = 0; + while (item != NULL) + { + result += item.bInvBar; + item = item.inv; + } + return result; + } + + private int mSelectedIndex; + +} // class gb_InventoryMenu diff --git a/zscript/gearbox/inventory_user.zs b/zscript/gearbox/inventory_user.zs new file mode 100644 index 0000000000..d7a9844807 --- /dev/null +++ b/zscript/gearbox/inventory_user.zs @@ -0,0 +1,80 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +/** + * We could use inventory items directly, but player cannot use items when + * totally frozen. Therefore, we have to wait until player is not frozen. + */ +class gb_InventoryUser play +{ + + static + gb_InventoryUser from() + { + return new("gb_InventoryUser"); + } + + void use() + { + for (uint i = 0; i < mItemQueue.size();) + { + PlayerPawn player = mPlayerQueue[i].mo; + if (player == NULL) + { + deleteFromQueue(i); + continue; + } + + Inventory item = player.findInventory(mItemQueue[i]); + if (item == NULL) + { + deleteFromQueue(i); + continue; + } + + if (player.player.isTotallyFrozen()) + { + ++i; + continue; + } + else + { + player.useInventory(item); + deleteFromQueue(i); + } + } + } + + void addToQueue(PlayerInfo player, string item) + { + mPlayerQueue.push(player); + mItemQueue.push(item); + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private + void deleteFromQueue(uint index) + { + mPlayerQueue.delete(index); + mItemQueue.delete(index); + } + + private Array mPlayerQueue; + private Array mItemQueue; + +} // class gb_InventoryUser diff --git a/zscript/gearbox/netevent_processor.zs b/zscript/gearbox/netevent_processor.zs new file mode 100644 index 0000000000..157202bfc3 --- /dev/null +++ b/zscript/gearbox/netevent_processor.zs @@ -0,0 +1,59 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_NeteventProcessor play +{ + + static + gb_NeteventProcessor from(gb_Changer changer) + { + let result = new("gb_NeteventProcessor"); + result.mChanger = changer; + return result; + } + + int process(ConsoleEvent event) + { + if (event.name.left(3) != "gb_") return InputNothing; + + Array args; + event.name.split(args, ":"); + + PlayerInfo player = players[event.player]; + + if (args[0] == "gb_select_weapon") mChanger.selectWeapon(player, args[1]); + else if (args[0] == "gb_use_item" ) mChanger.useItem (player, args[1]); + else if (args[0] == "gb_set_angles" ) mChanger.setAngles (player, args[1].toDouble() + , args[2].toDouble() + ); + else if (args[0] == "gb_freeze_player") mChanger.freezePlayer(player, args[1].toInt() + , args[2].toInt() + , args[3].toDouble() + , args[4].toDouble() + , args[5].toDouble() + ); + else if (args[0] == "gb_reset_custom_order") return InputResetCustomOrder; + else if (args[0] == "gb_give_item") mChanger.giveItem(player,args[1]); + + return InputNothing; + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private gb_Changer mChanger; + +} // class gb_NeteventProcessor diff --git a/zscript/gearbox/options.zs b/zscript/gearbox/options.zs new file mode 100644 index 0000000000..5f97246f6a --- /dev/null +++ b/zscript/gearbox/options.zs @@ -0,0 +1,173 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * Carrascado 2022 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_Options +{ + + static + gb_Options from() + { + let result = new("gb_Options"); + + result.mScale = gb_Cvar.from("gb_scale"); + result.mColor = gb_Cvar.from("gb_color"); + result.mDimColor = gb_Cvar.from("gb_dim_color"); + result.mViewType = gb_Cvar.from("gb_view_type"); + result.mIsDimEnabled = gb_Cvar.from("gb_enable_dim"); + result.mIsBlurEnabled = gb_Cvar.from("gb_enable_blur"); + result.mWheelTint = gb_Cvar.from("gb_wheel_tint"); + result.mMultiWheelLimit = gb_Cvar.from("gb_multiwheel_limit"); + result.mShowTags = gb_Cvar.from("gb_show_tags"); + result.mShowWeaponTagsOnChange = gb_Cvar.from("DisplayNameTags"); + result.mIsPositionLocked = gb_Cvar.from("gb_lock_positions"); + result.mFrozenCanOpen = gb_Cvar.from("gb_frozen_can_open"); + result.mPreserveAspectRatio = gb_Cvar.from("hud_AspectScale"); + + result.mOpenOnScroll = gb_Cvar.from("gb_open_on_scroll"); + result.mOpenOnSlot = gb_Cvar.from("gb_open_on_slot"); + result.mReverseSlotCycleOrder = gb_Cvar.from("gb_reverse_slot_cycle_order"); + result.mSelectFirstSlotWeapon = gb_Cvar.from("gb_select_first_slot_weapon"); + result.mMouseInWheel = gb_Cvar.from("gb_mouse_in_wheel"); + result.mSelectOnKeyUp = gb_Cvar.from("gb_select_on_key_up"); + result.mNoMenuIfOne = gb_Cvar.from("gb_no_menu_if_one"); + result.mTimeFreeze = gb_Cvar.from("gb_time_freeze"); + result.mOnAutomap = gb_Cvar.from("gb_on_automap"); + result.mEnableSounds = gb_Cvar.from("gb_enable_sounds"); + + result.mMouseSensitivityX = gb_Cvar.from("gb_mouse_sensitivity_x"); + result.mMouseSensitivityY = gb_Cvar.from("gb_mouse_sensitivity_y"); + result.mInvertMouseX = gb_Cvar.from("invertMouseX"); + result.mInvertMouseY = gb_Cvar.from("invertMouse"); + + result.mBlocksPositionX = gb_Cvar.from("gb_blocks_position_x"); + result.mBlocksPositionY = gb_Cvar.from("gb_blocks_position_y"); + + result.mTextScale = gb_Cvar.from("gb_text_scale"); + result.mTextPositionX = gb_Cvar.from("gb_text_position_x"); + result.mTextPositionY = gb_Cvar.from("gb_text_position_y"); + result.mTextPositionYMax = gb_Cvar.from("gb_text_position_y_max"); + result.mTextUsualColor = gb_Cvar.from("gb_text_usual_color"); + result.mTextSelectedColor = gb_Cvar.from("gb_text_selected_color"); + + result.mWheelPosition = gb_Cvar.from("gb_wheel_position"); + result.mWheelScale = gb_Cvar.from("gb_wheel_scale"); + + return result; + } + + int getViewType() const { return mViewType .getInt(); } + int getScale() const { return mScale .getInt(); } + int getColor() const { return mColor .getInt(); } + int getDimColor() const { return mDimColor .getInt(); } + bool isDimEnabled() const { return mIsDimEnabled .getBool(); } + bool isBlurEnabled() const { return mIsBlurEnabled .getBool(); } + bool getWheelTint() const { return mWheelTint .getBool(); } + int getMultiWheelLimit() const { return mMultiWheelLimit .getInt(); } + bool isShowingTags() const { return mShowTags .getBool(); } + bool isShowingWeaponTagsOnChange() const { return mShowWeaponTagsOnChange.getInt() & 2; } + bool isPositionLocked() const { return mIsPositionLocked .getBool(); } + bool isFrozenCanOpen() const { return mFrozenCanOpen .getBool(); } + bool isPreservingAspectRatio() const { return mPreserveAspectRatio .getBool(); } + + bool isOpenOnScroll() const { return mOpenOnScroll .getBool(); } + bool isOpenOnSlot() const { return mOpenOnSlot .getBool(); } + bool isSlotCycleOrderReversed() const { return mReverseSlotCycleOrder .getBool(); } + bool isSelectFirstSlotWeapon() const { return mSelectFirstSlotWeapon .getBool(); } + bool isMouseInWheel() const { return mMouseInWheel .getBool(); } + bool isSelectOnKeyUp() const { return mSelectOnKeyUp .getBool(); } + bool isNoMenuIfOne() const { return mNoMenuIfOne .getBool(); } + bool isOnAutomap() const { return mOnAutomap .getBool(); } + bool isSoundEnabled() const { return mEnableSounds .getBool(); } + + int getTimeFreezeMode() const { return mTimeFreeze .getInt(); } + + vector2 getMouseSensitivity() const + { + double xDirection = mInvertMouseX.getBool() ? -1 : 1; + double yDirection = mInvertMouseY.getBool() ? -1 : 1; + return ( mMouseSensitivityX.getDouble() * xDirection + , mMouseSensitivityY.getDouble() * yDirection + ); + } + + vector2 getBlocksPosition() const + { + return (mBlocksPositionX.getDouble(), mBlocksPositionY.getDouble()); + } + + int getTextScale() const { return mTextScale .getInt(); } + int getTextUsualColor() const { return mTextUsualColor .getInt(); } + int getTextSelectedColor() const { return mTextSelectedColor.getInt(); } + double getTextPositionYMax() const { return mTextPositionYMax.getDouble(); } + + vector2 getTextPosition() const + { + return (mTextPositionX.getDouble(), mTextPositionY.getDouble()); + } + + + double getWheelPosition() const { return mWheelPosition.getDouble(); } + double getWheelScale() const { return mWheelScale.getDouble(); } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private gb_Cvar mScale; + private gb_Cvar mColor; + private gb_Cvar mDimColor; + private gb_Cvar mViewType; + private gb_Cvar mIsDimEnabled; + private gb_Cvar mIsBlurEnabled; + private gb_Cvar mWheelTint; + private gb_Cvar mMultiWheelLimit; + private gb_Cvar mShowTags; + private gb_Cvar mShowWeaponTagsOnChange; + private gb_Cvar mIsPositionLocked; + + private gb_Cvar mOpenOnScroll; + private gb_Cvar mOpenOnSlot; + private gb_Cvar mReverseSlotCycleOrder; + private gb_Cvar mSelectFirstSlotWeapon; + private gb_Cvar mMouseInWheel; + private gb_Cvar mSelectOnKeyUp; + private gb_Cvar mNoMenuIfOne; + private gb_Cvar mTimeFreeze; + private gb_Cvar mOnAutomap; + private gb_Cvar mEnableSounds; + private gb_Cvar mFrozenCanOpen; + private gb_Cvar mPreserveAspectRatio; + + private gb_Cvar mMouseSensitivityX; + private gb_Cvar mMouseSensitivityY; + + private gb_Cvar mInvertMouseX; + private gb_Cvar mInvertMouseY; + + private gb_Cvar mBlocksPositionX; + private gb_Cvar mBlocksPositionY; + + private gb_Cvar mTextScale; + private gb_Cvar mTextPositionX; + private gb_Cvar mTextPositionY; + private gb_Cvar mTextPositionYMax; + private gb_Cvar mTextUsualColor; + private gb_Cvar mTextSelectedColor; + + private gb_Cvar mWheelPosition; + private gb_Cvar mWheelScale; + +} // class gb_Options diff --git a/zscript/gearbox/pb/tokens.zs b/zscript/gearbox/pb/tokens.zs new file mode 100644 index 0000000000..cd01dea9ee --- /dev/null +++ b/zscript/gearbox/pb/tokens.zs @@ -0,0 +1,784 @@ +// +// PB_SpecialWheel_Mode contains info used by the event handler to know what to display in screen +// and what to give to the player based on what they choose; +// +// + +Class PB_SpecialWheel_Mode +{ + string img; // icon, as string, can be a full path + string Alias; // name of the mode + string tokentogive; // token class to give + double scalex; // x scale of the icon + double scaley; // y scale of the icon +} + +// +// these hold the info about the weapon wheel icons for their respective weapon +// + +class WheelInfoContainer +{ + virtual void GetSpecials(in out array spw, actor requester) //receives an array and fills it with PB_SpecialWheel_Mode instances, also receives a pointer to the actual player, so it can check for tokens + { + if(!spw || !requester) + return; + } + + virtual int GetSPCount(actor requester) //this was the simplest and fastest thing i could think, returns the ammount of specials this class has + { + return 0; + } +} + +Class PB_CarbineWeaponWheel : wheelinfocontainer +{ + override int GetSPCount(actor requester) + { + return 4; + } + + override void GetSpecials(in out array spw, actor requester) + { + if(!spw || !requester) + return; + + bool dualling = requester.FindInventory("DualWieldingCarbines"); + + vector2 iconScale = (0.65, 0.65); + + PB_SpecialWheel_Mode carbine_fullauto = new ("PB_SpecialWheel_Mode"); + carbine_fullauto.img = "graphics/pywheel/Carbine_Auto.png"; + carbine_fullauto.Alias = "Toggle Full-Auto Fire"; + carbine_fullauto.tokentogive = "SelectCarbine_FullAutoFire"; + carbine_fullauto.scalex = iconscale.x; + carbine_fullauto.scaley = iconscale.y; + + + + PB_SpecialWheel_Mode carbine_burst = new ("PB_SpecialWheel_Mode"); + carbine_burst.img = "graphics/pywheel/Carbine_Semi.png"; + carbine_burst.Alias = "Toggle Semi-Auto Fire"; + carbine_burst.tokentogive = "SelectCarbine_SemiFire"; + carbine_burst.scalex = iconscale.x; + carbine_burst.scaley = iconscale.y; + + + PB_SpecialWheel_Mode carbine_semi = new ("PB_SpecialWheel_Mode"); + carbine_semi.img = "graphics/pywheel/Carbine_Burst.png"; + carbine_semi.Alias = "Toggle Burst Fire"; + carbine_semi.tokentogive = "SelectCarbine_BurstFire"; + carbine_semi.scalex = iconscale.x; + carbine_semi.scaley = iconscale.y; + + + spw.Push(carbine_fullauto); + spw.Push(carbine_burst); + spw.Push(carbine_semi); + + + if(!dualling) + { + PB_SpecialWheel_Mode carbine_dualwield = new ("PB_SpecialWheel_Mode"); + carbine_dualwield.img = "graphics/pywheel/Carbine_Dual.png"; + carbine_dualwield.Alias = "Akimbo Carbines"; + carbine_dualwield.tokentogive = "SelectCarbine_DualWield"; + carbine_dualwield.scalex = iconscale.x; + carbine_dualwield.scaley = iconscale.y; + + spw.Push(carbine_dualwield); + + } + else + { + PB_SpecialWheel_Mode carbine_dualwield = new ("PB_SpecialWheel_Mode"); + carbine_dualwield.img = "sprites/weapons/Slot 4/Carbine/CB00Z0.png"; + carbine_dualwield.Alias = "Single Carbine"; + carbine_dualwield.tokentogive = "SelectCarbine_DualWield"; + carbine_dualwield.scalex = iconscale.x; + carbine_dualwield.scaley = iconscale.y; + + spw.Push(carbine_dualwield); + } + + } +} + +class PB_pistolWheel : wheelinfocontainer +{ + override int GetSPCount(actor requester) + { + return 3; + } + + override void GetSpecials(in out array spw, actor requester) + { + if(!spw || !requester) + return; + + vector2 iconScale = (0.75, 0.75); + + //check suppresor + if(requester.FindInventory("SilencerEquipped")) + { + PB_SpecialWheel_Mode pistol_unsilenced = new ("PB_SpecialWheel_Mode"); + pistol_unsilenced.img = "graphics/pywheel/PISTOL_5.png"; + pistol_unsilenced.Alias = "Detach Suppressor"; + pistol_unsilenced.tokentogive = "SelectPistolSuppressor"; + pistol_unsilenced.scalex = iconscale.x; + pistol_unsilenced.scaley = iconscale.y; + + spw.Push(pistol_unsilenced); + } + else + { + PB_SpecialWheel_Mode pistol_silencer = new ("PB_SpecialWheel_Mode"); + pistol_silencer.img = "graphics/pywheel/PISTOL_1.png"; + pistol_silencer.Alias = "Attach Suppressor"; + pistol_silencer.tokentogive = "SelectPistolSuppressor"; + pistol_silencer.scalex = iconscale.x; + pistol_silencer.scaley = iconscale.y; + + spw.Push(pistol_silencer); + } + + //check dw + if(requester.FindInventory("DualWieldingPistols")) + { + PB_SpecialWheel_Mode pistol_single = new ("PB_SpecialWheel_Mode"); + pistol_single.img = "graphics/pywheel/PISTOL_0.png"; + pistol_single.Alias = "Single Pistol"; + pistol_single.tokentogive = "SelectDualWieldPistols"; + pistol_single.scalex = iconscale.x; + pistol_single.scaley = iconscale.y; + + spw.Push(pistol_single); + } + else + { + PB_SpecialWheel_Mode pistol_dual = new ("PB_SpecialWheel_Mode"); + pistol_dual.img = "graphics/pywheel/PISTOL_4.png"; + pistol_dual.Alias = "Akimbo Pistols"; + pistol_dual.tokentogive = "SelectDualWieldPistols"; + pistol_dual.scalex = iconscale.x; + pistol_dual.scaley = iconscale.y; + + spw.Push(pistol_dual); + } + + //check burst + if(requester.FindInventory("ToggledPistolBurstFire")) + { + PB_SpecialWheel_Mode pistol_semi = new ("PB_SpecialWheel_Mode"); + pistol_semi.img = "graphics/pywheel/PISTOL_3.png"; + pistol_semi.Alias = "Semi Fire"; + pistol_semi.tokentogive = "SelectPistolBurstFire"; + pistol_semi.scalex = iconscale.x; + pistol_semi.scaley = iconscale.y; + + spw.Push(pistol_semi); + } + else + { + PB_SpecialWheel_Mode pistol_burst = new ("PB_SpecialWheel_Mode"); + pistol_burst.img = "graphics/pywheel/PISTOL_2.png"; + pistol_burst.Alias = "Burst Fire"; + pistol_burst.tokentogive = "SelectPistolBurstFire"; + pistol_burst.scalex = iconscale.x; + pistol_burst.scaley = iconscale.y; + + spw.Push(pistol_burst); + } + + } +} + +Class PB_SGLWheel : wheelinfocontainer +{ + override int GetSPCount(actor requester) + { + return 5; + } + + override void GetSpecials(in out array spw, actor requester) + { + if(!spw || !requester) + return; + + vector2 iconScale = (0.5, 0.5); + + PB_SpecialWheel_Mode grenade_impact = new ("PB_SpecialWheel_Mode"); + grenade_impact.img = "graphics/pywheel/grenade_impact.png"; + grenade_impact.Alias = "Frag Grenade"; + grenade_impact.tokentogive = "GrenadeTypeImpact"; + grenade_impact.scalex = iconscale.x; + grenade_impact.scaley = iconscale.y; + + PB_SpecialWheel_Mode grenade_sticky = new ("PB_SpecialWheel_Mode"); + grenade_sticky.img = "graphics/pywheel/grenade_sticky.png"; + grenade_sticky.Alias = "Sticky Bomb"; + grenade_sticky.tokentogive = "GrenadeTypeSticky"; + grenade_sticky.scalex = iconscale.x; + grenade_sticky.scaley = iconscale.y; + + PB_SpecialWheel_Mode grenade_incendiary = new ("PB_SpecialWheel_Mode"); + grenade_incendiary.img = "graphics/pywheel/grenade_incendiary.png"; + grenade_incendiary.Alias = "Incendiary Grenade"; + grenade_incendiary.tokentogive = "GrenadeTypeIncendiary"; + grenade_incendiary.scalex = iconscale.x; + grenade_incendiary.scaley = iconscale.y; + + PB_SpecialWheel_Mode grenade_cryo = new ("PB_SpecialWheel_Mode"); + grenade_cryo.img = "graphics/pywheel/grenade_cryo.png"; + grenade_cryo.Alias = "Cryogenic Grenade"; + grenade_cryo.tokentogive = "GrenadeTypeCryo"; + grenade_cryo.scalex = iconscale.x; + grenade_cryo.scaley = iconscale.y; + + PB_SpecialWheel_Mode grenade_acid = new ("PB_SpecialWheel_Mode"); + grenade_acid.img = "graphics/pywheel/grenade_acid.png"; + grenade_acid.Alias = "Acid Grenade"; + grenade_acid.tokentogive = "GrenadeTypeAcid"; + grenade_acid.scalex = iconscale.x; + grenade_acid.scaley = iconscale.y; + + spw.Push(grenade_impact); + spw.Push(grenade_sticky); + spw.Push(grenade_incendiary); + spw.Push(grenade_cryo); + spw.Push(grenade_acid); + } +} + + +Class PB_SMGWheel : wheelinfocontainer +{ + override int GetSPCount(actor requester) + { + return 2; + } + + override void GetSpecials(in out array spw, actor requester) + { + if(!spw || !requester) + return; + + vector2 iconScale = (0.5, 0.5); + + if(!requester.FindInventory("DualWieldingSMGs")) + { + PB_SpecialWheel_Mode smg_dualwield = new ("PB_SpecialWheel_Mode"); + smg_dualwield.img = "graphics/pywheel/SMG/SMG_DUAL.png"; + smg_dualwield.Alias = "Akimbo SMGs"; + smg_dualwield.tokentogive = "SelectDualWieldSMG"; + smg_dualwield.scalex = iconscale.x; + smg_dualwield.scaley = iconscale.y; + + spw.Push(smg_dualwield); + } + else + { + PB_SpecialWheel_Mode smg_dualwield = new ("PB_SpecialWheel_Mode"); + smg_dualwield.img = "sprites/weapons/Slot 2/UACSMG/Pickup/ATFLA0.png"; + smg_dualwield.Alias = "Single SMG"; + smg_dualwield.tokentogive = "SelectDualWieldSMG"; + smg_dualwield.scalex = iconscale.x; + smg_dualwield.scaley = iconscale.y; + + spw.Push(smg_dualwield); + } + if(!requester.FindInventory("LaserSightActivated")) + { + PB_SpecialWheel_Mode smg_laser = new ("PB_SpecialWheel_Mode"); + smg_laser.img = "graphics/pywheel/SMG/SMG_LASER.png"; + smg_laser.Alias = "Activate Laser Sight"; + smg_laser.tokentogive = "SelectLaserSight"; + smg_laser.scalex = iconscale.x; + smg_laser.scaley = iconscale.y; + + spw.Push(smg_laser); + } + else + { + PB_SpecialWheel_Mode smg_laser = new ("PB_SpecialWheel_Mode"); + smg_laser.img = "sprites/weapons/Slot 2/UACSMG/Pickup/ATFLA0.png"; + smg_laser.Alias = "Deactivate Laser Sight"; + smg_laser.tokentogive = "SelectLaserSight"; + smg_laser.scalex = iconscale.x; + smg_laser.scaley = iconscale.y; + + spw.Push(smg_laser); + } + } +} + +Class PB_RifleWheel : wheelinfocontainer +{ + override int GetSPCount(actor requester) + { + return 3; + } + + override void GetSpecials(in out array spw, actor requester) + { + if(!spw || !requester) + return; + + vector2 iconScale = (0.55, 0.55); + + // Check Dual Wield Icons + if(requester.FindInventory("DualWieldingDMRs")) + { + PB_SpecialWheel_Mode rifle_single = new ("PB_SpecialWheel_Mode"); + rifle_single.img = "graphics/pywheel/hdmr_single.png"; + rifle_single.Alias = "Single DMR"; + rifle_single.tokentogive = "SelectDualWieldRifles"; + rifle_single.scalex = iconscale.x; + rifle_single.scaley = iconscale.y; + + spw.Push(rifle_single); + } + else + { + PB_SpecialWheel_Mode rifle_dual = new ("PB_SpecialWheel_Mode"); + rifle_dual.img = "graphics/pywheel/hdmr_dual.png"; + rifle_dual.Alias = "Akimbo DMRs"; + rifle_dual.tokentogive = "SelectDualWieldRifles"; + rifle_dual.scalex = iconscale.x; + rifle_dual.scaley = iconscale.y; + + spw.Push(rifle_dual); + } + + if(requester.FindInventory("HDMRGrenadeMode")) + { + PB_SpecialWheel_Mode rifle_grenade_off = new ("PB_SpecialWheel_Mode"); + rifle_grenade_off.img = "graphics/pywheel/hdmr_grenade_off.png"; + rifle_grenade_off.Alias = "Aiming Secondary Fire"; + rifle_grenade_off.tokentogive = "SelectHDMRGrenade"; + rifle_grenade_off.scalex = iconscale.x; + rifle_grenade_off.scaley = iconscale.y; + + spw.Push(rifle_grenade_off); + } + else + { + PB_SpecialWheel_Mode rifle_grenade_on = new ("PB_SpecialWheel_Mode"); + rifle_grenade_on.img = "graphics/pywheel/hdmr_grenade_on.png"; + rifle_grenade_on.Alias = "Grenade Secondary Fire"; + rifle_grenade_on.tokentogive = "SelectHDMRGrenade"; + rifle_grenade_on.scalex = iconscale.x; + rifle_grenade_on.scaley = iconscale.y; + + spw.Push(rifle_grenade_on); + } + + + if(requester.FindInventory("HDMRSniperMode")) + { + PB_SpecialWheel_Mode rifle_normal = new ("PB_SpecialWheel_Mode"); + rifle_normal.img = "graphics/pywheel/hdmr_normal.png"; + rifle_normal.Alias = "DMR Mode"; + rifle_normal.tokentogive = "SelectHDMRMode"; + rifle_normal.scalex = iconscale.x; + rifle_normal.scaley = iconscale.y; + + spw.Push(rifle_normal); + } + else + { + PB_SpecialWheel_Mode rifle_sniper = new ("PB_SpecialWheel_Mode"); + rifle_sniper.img = "graphics/pywheel/hdmr_sniper.png"; + rifle_sniper.Alias = "Heavy Sniper Mode"; + rifle_sniper.tokentogive = "SelectHDMRMode"; + rifle_sniper.scalex = iconscale.x; + rifle_sniper.scaley = iconscale.y; + + spw.Push(rifle_sniper); + } + } +} + +Class PB_QSGWheel : wheelinfocontainer +{ + override int GetSPCount(actor requester) + { + return 3; + } + + override void GetSpecials(in out array spw, actor requester) + { + if(!spw || !requester) + return; + + vector2 iconScale = (0.55, 0.55); + + if(requester.FindInventory("QuadAkimboMode")) + { + PB_SpecialWheel_Mode qsg_undual = new ("PB_SpecialWheel_Mode"); + qsg_undual.img = "graphics/pywheel/Quad_Single.png"; + qsg_undual.Alias = "Single Quad Shotgun"; + qsg_undual.tokentogive = "SelectDualWieldQuads"; + qsg_undual.scalex = iconscale.x; + qsg_undual.scaley = iconscale.y; + + spw.Push(qsg_undual); + } + else + { + PB_SpecialWheel_Mode qsg_dual = new ("PB_SpecialWheel_Mode"); + qsg_dual.img = "graphics/pywheel/Quad_Dual.png"; + qsg_dual.Alias = "Akimbo Quad Shotguns"; + qsg_dual.tokentogive = "SelectDualWieldQuads"; + qsg_dual.scalex = iconscale.x; + qsg_dual.scaley = iconscale.y; + + spw.Push(qsg_dual); + } + + if(requester.FindInventory("FullBlastMode")) + { + PB_SpecialWheel_Mode qsg_halfnormal = new ("PB_SpecialWheel_Mode"); + qsg_halfnormal.img = "graphics/pywheel/Quad_Half.png"; + qsg_halfnormal.Alias = "Half Blast"; + qsg_halfnormal.tokentogive = "BlastToggle"; + qsg_halfnormal.scalex = iconscale.x; + qsg_halfnormal.scaley = iconscale.y; + + spw.Push(qsg_halfnormal); + } + else + { + PB_SpecialWheel_Mode qsg_fullnormal = new ("PB_SpecialWheel_Mode"); + qsg_fullnormal.img = "graphics/pywheel/Quad_Full.png"; + qsg_fullnormal.Alias = "Full Blast"; + qsg_fullnormal.tokentogive = "BlastToggle"; + qsg_fullnormal.scalex = iconscale.x; + qsg_fullnormal.scaley = iconscale.y; + + spw.Push(qsg_fullnormal); + } + + if(requester.FindInventory("BreathMode")) + { + PB_SpecialWheel_Mode qsg_shell = new ("PB_SpecialWheel_Mode"); + qsg_shell.img = "graphics/pywheel/Quad_Shells.png"; + qsg_shell.Alias = "Shells mode"; + qsg_shell.tokentogive = "BreathToggle"; + qsg_shell.scalex = iconscale.x; + qsg_shell.scaley = iconscale.y; + + spw.Push(qsg_shell); + } + else + { + PB_SpecialWheel_Mode qsg_demon = new ("PB_SpecialWheel_Mode"); + qsg_demon.img = "graphics/pywheel/Quad_Demonic.png"; + qsg_demon.Alias = "Demonic Breath mode"; + qsg_demon.tokentogive = "BreathToggle"; + qsg_demon.scalex = iconscale.x; + qsg_demon.scaley = iconscale.y; + + spw.Push(qsg_demon); + } + } +} + +Class PB_CryoRifleWheel : wheelinfocontainer +{ + override int GetSPCount(actor requester) + { + return 4; + } + + override void GetSpecials(in out array spw, actor requester) + { + if(!spw || !requester) + return; + + vector2 iconScale = (0.8, 0.8); + + PB_SpecialWheel_Mode cryorifle_missile = new ("PB_SpecialWheel_Mode"); + cryorifle_missile.img = "graphics/pywheel/CryoRifle_Missile.png"; + cryorifle_missile.Alias = "Primary: Ice Missile"; + cryorifle_missile.tokentogive = "FireModeCryoRifleMissile_WW"; + cryorifle_missile.scalex = iconscale.x; + cryorifle_missile.scaley = iconscale.y; + + spw.Push(cryorifle_missile); + + PB_SpecialWheel_Mode cryorifle_beam = new ("PB_SpecialWheel_Mode"); + cryorifle_beam.img = "graphics/pywheel/cryorifle_beam.png"; + cryorifle_beam.Alias = "Primary: Ice Beam"; + cryorifle_beam.tokentogive = "FireModeCryoRifleBeam_WW"; + cryorifle_beam.scalex = iconscale.x; + cryorifle_beam.scaley = iconscale.y; + + spw.Push(cryorifle_beam); + + PB_SpecialWheel_Mode cryorifle_spear = new ("PB_SpecialWheel_Mode"); + cryorifle_spear.img = "graphics/pywheel/CryoRifle_Spear.png"; + cryorifle_spear.Alias = "Secondary: Ice Spear"; + cryorifle_spear.tokentogive = "FireModeCryoRifleSpear_WW"; + cryorifle_spear.scalex = iconscale.x; + cryorifle_spear.scaley = iconscale.y; + + spw.Push(cryorifle_spear); + + PB_SpecialWheel_Mode cryorifle_flak = new ("PB_SpecialWheel_Mode"); + cryorifle_flak.img = "graphics/pywheel/CryoRifle_Flak.png"; + cryorifle_flak.Alias = "Secondary: Ice Flak"; + cryorifle_flak.tokentogive = "FireModeCryoRifleFlak_WW"; + cryorifle_flak.scalex = iconscale.x; + cryorifle_flak.scaley = iconscale.y; + + spw.Push(cryorifle_flak); + } +} + +Class PB_MinigunWheel : wheelinfocontainer +{ + override int GetSPCount(actor requester) + { + if(requester.FindInventory("MinigunUpgraded")) + return 3; + return 2; + } + + override void GetSpecials(in out array spw, actor requester) + { + if(!spw || !requester) + return; + + vector2 iconScale = (0.8, 0.8); + + PB_SpecialWheel_Mode minigun_chaingun = new ("PB_SpecialWheel_Mode"); + minigun_chaingun.img = "graphics/pywheel/Minigun_1.png"; + minigun_chaingun.Alias = "Chaingun Mode"; + minigun_chaingun.tokentogive = "SelectMinigun_Chaingun"; + minigun_chaingun.scalex = iconscale.x; + minigun_chaingun.scaley = iconscale.y; + + spw.Push(minigun_chaingun); + + PB_SpecialWheel_Mode minigun_gatling = new ("PB_SpecialWheel_Mode"); + minigun_gatling.img = "graphics/pywheel/Minigun_2.png"; + minigun_gatling.Alias = "Gatling Mode"; + minigun_gatling.tokentogive = "SelectMinigun_Gatling"; + minigun_gatling.scalex = iconscale.x; + minigun_gatling.scaley = iconscale.y; + + spw.Push(minigun_gatling); + + + if(requester.FindInventory("MinigunUpgraded")) + { + PB_SpecialWheel_Mode minigun_triple = new ("PB_SpecialWheel_Mode"); + minigun_triple.img = "graphics/pywheel/Minigun_3.png"; + minigun_triple.Alias = "Triple Rotary Mode"; + minigun_triple.tokentogive = "SelectMinigun_Triple"; + minigun_triple.scalex = iconscale.x; + minigun_triple.scaley = iconscale.y; + + spw.Push(minigun_triple); + } + } +} + +Class PB_PumpShotgunWheel : wheelinfocontainer +{ + override int GetSPCount(actor requester) + { + return 3; + } + + override void GetSpecials(in out array spw, actor requester) + { + if(!spw || !requester) + return; + + vector2 iconScale = (0.7, 0.7); + + if(requester.FindInventory("DragonBreathUpgrade")) + { + PB_SpecialWheel_Mode shotgun_dragonbreath = new ("PB_SpecialWheel_Mode"); + shotgun_dragonbreath.img = "graphics/pywheel/SG_DB.png"; + shotgun_dragonbreath.Alias = "Dragon's Breath Shells"; + shotgun_dragonbreath.tokentogive = "SelectShotgun_Dragonsbreath"; + shotgun_dragonbreath.scalex = iconscale.x; + shotgun_dragonbreath.scaley = iconscale.y; + + spw.Push(shotgun_dragonbreath); + } + else + { + PB_SpecialWheel_Mode shotgun_No = new ("PB_SpecialWheel_Mode"); + shotgun_No.img = "graphics/pywheel/SG_NO.png"; + shotgun_No.Alias = "Not Available"; + shotgun_No.tokentogive = "SelectShotgun_No"; + shotgun_No.scalex = iconscale.x; + shotgun_No.scaley = iconscale.y; + + spw.Push(shotgun_No); + } + + PB_SpecialWheel_Mode shotgun_buckshot = new ("PB_SpecialWheel_Mode"); + shotgun_buckshot.img = "graphics/pywheel/SG_Buck.png"; + shotgun_buckshot.Alias = "Buckshot Shells"; + shotgun_buckshot.tokentogive = "SelectShotgun_Buckshot"; + shotgun_buckshot.scalex = iconscale.x; + shotgun_buckshot.scaley = iconscale.y; + + + PB_SpecialWheel_Mode shotgun_slugshot = new ("PB_SpecialWheel_Mode"); + shotgun_slugshot.img = "graphics/pywheel/SG_Slug.png"; + shotgun_slugshot.Alias = "Slug Shells"; + shotgun_slugshot.tokentogive = "SelectShotgun_Slugshot"; + shotgun_slugshot.scalex = iconscale.x; + shotgun_slugshot.scaley = iconscale.y; + + + spw.Push(shotgun_buckshot); + spw.Push(shotgun_slugshot); + } +} + +Class PB_RocketLauncherWheel : wheelinfocontainer +{ + override int GetSPCount(actor requester) + { + return 3; + } + + override void GetSpecials(in out array spw, actor requester) + { + if(!spw || !requester) + return; + + vector2 iconScale = (0.5, 0.5); + + PB_SpecialWheel_Mode rocket_standard = new ("PB_SpecialWheel_Mode"); + rocket_standard.img = "graphics/pywheel/rocket_standard.png"; + rocket_standard.Alias = "Standard Rocket Mode"; + rocket_standard.tokentogive = "RocketLauncher_Standard"; + rocket_standard.scalex = iconscale.x; + rocket_standard.scaley = iconscale.y; + + PB_SpecialWheel_Mode rocket_homing = new ("PB_SpecialWheel_Mode"); + rocket_homing.img = "graphics/pywheel/rocket_homing.png"; + rocket_homing.Alias = "Lock-On Rocket Mode"; + rocket_homing.tokentogive = "RocketLauncher_Homing"; + rocket_homing.scalex = iconscale.x; + rocket_homing.scaley = iconscale.y; + + PB_SpecialWheel_Mode rocket_laser = new ("PB_SpecialWheel_Mode"); + rocket_laser.img = "graphics/pywheel/rocket_laser.png"; + rocket_laser.Alias = "Laser Rocket Mode"; + rocket_laser.tokentogive = "RocketLauncher_Laser"; + rocket_laser.scalex = iconscale.x; + rocket_laser.scaley = iconscale.y; + + /*if(requester.FindInventory("RL_ScopeMode")) { + PB_SpecialWheel_Mode rocket_multi = new ("PB_SpecialWheel_Mode"); + rocket_multi.img = "graphics/pywheel/multirocket.png"; + rocket_multi.Alias = "Multi Rocket Mode"; + rocket_multi.tokentogive = "RocketLauncher_Multi"; + rocket_multi.scalex = iconscale.x; + rocket_multi.scaley = iconscale.y; + + spw.Push(rocket_multi); + } + else + { + PB_SpecialWheel_Mode rocket_scope = new ("PB_SpecialWheel_Mode"); + rocket_scope.img = "graphics/pywheel/rocketscope.png"; + rocket_scope.Alias = "Scope Mode"; + rocket_scope.tokentogive = "RocketLauncher_Scope"; + rocket_scope.scalex = iconscale.x; + rocket_scope.scaley = iconscale.y; + + spw.Push(rocket_scope); + }*/ + + spw.Push(rocket_standard); + spw.Push(rocket_homing); + spw.Push(rocket_laser); + } +} + + +///////////////////// +// +// equipments +// +///////////////////// + +class equipmentCard +{ + //this function fills the respective arrays to correctly display the equipments in the wheel + //if any new equipment is added, create a new class inheriting from this class for the handler to catch it + virtual void InfoFiller(out array tags,out array tokens,out arrayimg,out arraysx,out arraysy) + { + return; + } +} + +class ProxMinCard : equipmentCard +{ + override void InfoFiller(out array tags,out array tokens,out arrayimg,out arraysx,out arraysy) + { + tags.push("Proximity Mine"); + tokens.push("WW_ProximityMineSelected"); + img.push("graphics/pywheel/Equip_Mine.png"); + sx.push(1.3); + sy.push(1.3); + } +} + +Class StunGrenCard : equipmentCard +{ + override void InfoFiller(out array tags,out array tokens,out arrayimg,out arraysx,out arraysy) + { + tags.push("Stun Grenade"); + tokens.push("WW_StunGrenadeSelected"); + img.push("graphics/pywheel/Equip_Stun.png"); + sx.push(1.3); + sy.push(1.3); + } +} + +Class LeechCard : equipmentCard +{ + override void InfoFiller(out array tags,out array tokens,out arrayimg,out arraysx,out arraysy) + { + tags.push("Leech"); + tokens.push("WW_LeechSelected"); + img.push("graphics/pywheel/Equip_Leech.png"); + sx.push(1.3); + sy.push(1.3); + } +} + +class FragGrenCard : equipmentCard +{ + override void InfoFiller(out array tags,out array tokens,out arrayimg,out arraysx,out arraysy) + { + tags.push("Frag Grenade"); + tokens.push("WW_FragGrenadeSelected"); + img.push("graphics/pywheel/Equip_Frag.png"); + sx.push(1.3); + sy.push(1.3); + } +} + +class ShouldCanCard : equipmentCard +{ + override void InfoFiller(out array tags,out array tokens,out arrayimg,out arraysx,out arraysy) + { + tags.push("Shoulder Cannon"); + tokens.push("WW_RevGunSelected"); + img.push("graphics/pywheel/Equip_RevGun.png"); + sx.push(1.3); + sy.push(1.3); + } +} \ No newline at end of file diff --git a/zscript/gearbox/printer.zs b/zscript/gearbox/printer.zs new file mode 100644 index 0000000000..15a4c85fb0 --- /dev/null +++ b/zscript/gearbox/printer.zs @@ -0,0 +1,40 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_Printer +{ + + static + void printWeaponData(gb_WeaponData data) + { + uint nWeapons = data.weapons.size(); + uint nSlots = data.slots.size(); + + console.printf("numbers: %d, %d", nWeapons, nSlots); + + for (uint i = 0; i < nWeapons; ++i) + { + let default = getDefaultByType(data.weapons[i]); + console.printf( "%s (%s), slot %d" + , default.getTag() + , default.getClassName() + , data.slots[i] + ); + } + } + +} // class gb_Printer diff --git a/zscript/gearbox/sender.zs b/zscript/gearbox/sender.zs new file mode 100644 index 0000000000..412ecbf3b0 --- /dev/null +++ b/zscript/gearbox/sender.zs @@ -0,0 +1,56 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_Sender +{ + + static + void sendSelectEvent(string className) + { + EventHandler.sendNetworkEvent(string.format("gb_select_weapon:%s", className)); + } + + static + void sendUseItemEvent(string className) + { + EventHandler.sendNetworkEvent(string.format("gb_use_item:%s", className)); + } + + static void sendGiveItemEvent(string classname) + { + EventHandler.sendNetworkEvent(string.format("gb_give_item:%s", className)); + } + + static + void sendFreezePlayerEvent(int cheats, vector3 velocity, double gravity) + { + EventHandler.sendNetworkEvent(string.format( "gb_freeze_player:%d:%f:%f:%f:%f" + , cheats + , velocity.x + , velocity.y + , velocity.z + , gravity + )); + } + + static + void sendPlayerAngles(double pitch, double yaw) + { + EventHandler.sendNetworkEvent("gb_set_angles:" .. pitch .. ":" .. yaw); + } + +} // class gb_Sender diff --git a/zscript/gearbox/service/hide_service.zs b/zscript/gearbox/service/hide_service.zs new file mode 100644 index 0000000000..988c28cf7b --- /dev/null +++ b/zscript/gearbox/service/hide_service.zs @@ -0,0 +1,41 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_HideService : gb_Service +{ + + override + string get(string className) + { + switch (Name(className)) + { + // SWWM GZ: https://forum.zdoom.org/viewtopic.php?f=43&t=67687 + case 'DualExplodiumGun': + { + string explodiumGunClass = "ExplodiumGun"; + return (players[consolePlayer].mo.countInv(explodiumGunClass) > 1) ? SHOW : HIDE; + } + + default: return IGNORE; + } + } + + const HIDE = "1"; + const SHOW = "0"; + const IGNORE = ""; + +} // class gb_HideService diff --git a/zscript/gearbox/service/icon_service.zs b/zscript/gearbox/service/icon_service.zs new file mode 100644 index 0000000000..35f3d3d5cd --- /dev/null +++ b/zscript/gearbox/service/icon_service.zs @@ -0,0 +1,66 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_IconService : gb_Service +{ + + override + string uiGet(string className) + { + /*if (isSmoothDoom()) switch (Name(className)) + { + case 'PerkFist' : return "PUNGB0"; + case 'Z86Chainsaw' : return "CSAWA0"; + case 'PerkShotgun' : return "SHOTA0"; + case 'PerkSuperShotgun' : return "SGN2A0"; + case 'Z86Chaingun' : return "MGUNA0"; + case 'PerkRocketLauncher' : return "LAUNA0"; + case 'BloxPlasmaRifle' : return "PLRLA0"; + case 'Z86BFG9000' : return "BFG9A0"; + }*/ + + switch (Name(className)) + { + case 'Fist' : return "SMFIST0"; + case 'Chainsaw' : return "SMCSAW0"; + case 'Pistol' : return "SMPISG0"; + case 'Shotgun' : return "SMSHOT0"; + case 'SuperShotgun' : return "SMSGN20"; + case 'Chaingun' : return "SMMGUN0"; + case 'RocketLauncher' : return "SMLAUN0"; + case 'PlasmaRifle' : return "SMPLAS0"; + case 'BFG9000' : return "SMBFGG0"; + + default: return IGNORE; + } + } + + const IGNORE = ""; + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private static + bool isSmoothDoom() + { + string s1 = "SmoothFloatingSkull"; + class c1 = s1; + string s2 = "SmoothDoomImp"; + class c2 = s2; + return (c1 && c2); + } + +} // class gb_IconService1 diff --git a/zscript/gearbox/service/service.zs b/zscript/gearbox/service/service.zs new file mode 100644 index 0000000000..0cf21a48f6 --- /dev/null +++ b/zscript/gearbox/service/service.zs @@ -0,0 +1,126 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +/** + * This is Service interface. + */ +class gb_Service abstract +{ + + virtual play + string get(string request) + { + return ""; + } + + virtual ui + string uiGet(string request) + { + return ""; + } + +} // class gb_Service + +/** + * Use this class to find and iterate over services. + * + * Example usage: + * + * @code + * ServiceIterator i = ServiceIterator.find("MyService"); + * + * Service s; + * while (s = i.next()) + * { + * string request = ... + * string answer = s.get(request); + * ... + * } + * @endcode + * + * If no services are found, the all calls to next() will return NULL. + */ +class gb_ServiceIterator +{ + /** + * Creates a Service iterator for a service name. It will iterate over all existing Services + * with names that match @a serviceName or have it as a part of their names. + * + * Matching is case-independent. + * + * @param serviceName class name of service to find. + */ + static + gb_ServiceIterator find(string serviceName) + { + let result = new("gb_ServiceIterator"); + + result.mServiceName = serviceName; + result.mClassIndex = 0; + result.findNextService(); + + return result; + } + + /** + * Gets the service and advances the iterator. + * + * @returns service instance, or NULL if no more servers found. + * + * @note Each ServiceIterator will return new instances of services. + */ + gb_Service next() + { + uint classesNumber = AllClasses.size(); + gb_Service result = (mClassIndex == classesNumber) + ? NULL + : gb_Service(new(AllClasses[mClassIndex])); + + ++mClassIndex; + findNextService(); + + return result; + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private + void findNextService() + { + uint classesNumber = AllClasses.size(); + while (mClassIndex < classesNumber && !ServiceNameContains(AllClasses[mClassIndex], mServiceName)) + { + ++mClassIndex; + } + } + + private static + bool serviceNameContains(class aClass, string substring) + { + if (!(aClass is "gb_Service")) return false; + + string className = aClass.getClassName(); + string lowerClassName = className.makeLower(); + string lowerSubstring = substring.makeLower(); + bool result = lowerClassName.indexOf(lowerSubstring) != -1; + return result; + } + + private string mServiceName; + private uint mClassIndex; + +} // class gb_ServiceIterator diff --git a/zscript/gearbox/sounds.zs b/zscript/gearbox/sounds.zs new file mode 100644 index 0000000000..31d0f48c63 --- /dev/null +++ b/zscript/gearbox/sounds.zs @@ -0,0 +1,62 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2021-2022 + * Carrascado 2022 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_Sounds +{ + + static + gb_Sounds from(gb_Options options) + { + let result = new("gb_Sounds"); + result.mOptions = options; + return result; + } + + void playTick() + { + playSound("gearbox/tick"); + } + + void playOpen() + { + playSound("gearbox/open"); + } + + void playClose() + { + playSound("gearbox/close"); + } + + void playNope() + { + playSound("gearbox/nope"); + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private + void playSound(string sound) + { + if (!mOptions.isSoundEnabled()) return; + + players[consolePlayer].mo.a_StartSound(sound, CHAN_AUTO, CHANF_UI | CHANF_OVERLAP | CHANF_LOCAL); + } + + private gb_Options mOptions; + +} // class gb_Sounds diff --git a/zscript/gearbox/specials_menu.zs b/zscript/gearbox/specials_menu.zs new file mode 100644 index 0000000000..a175d85a8b --- /dev/null +++ b/zscript/gearbox/specials_menu.zs @@ -0,0 +1,140 @@ +class gb_specialsmenu +{ + static gb_specialsmenu from() + { + let nc = new("gb_specialsmenu"); + nc.mSelectedIndex = 0; + return nc; + } + + ui void getspecials(weapon act) + { + ClearSpecials(); //empty the arrays, so everything displays correctly + let pl = players[consoleplayer]; + let toRead = PB_WeaponBase(act); + if(toRead) + { + if(!toRead.wheelinfo) + return; + let wif = wheelinfocontainer(new(toRead.wheelinfo)); + if(!wif || !toRead.hasWheelSpecial) + return; + specialsinfo.clear(); + wif.GetSpecials(specialsinfo,pl.mo); + for(int i = 0; i < specialsinfo.size(); i++) + { + stag.push(specialsinfo[i].Alias); + imgs.push(specialsinfo[i].img); + Xscales.push(specialsinfo[i].scalex); + Yscales.push(specialsinfo[i].scaley); + token.push(specialsinfo[i].tokentogive); + } + + } + } + + static bool thereAreNoSpecials() + { + return getSpecialsNumber() == 0; + } + + private static int getSpecialsNumber() + { + let pl = players[consoleplayer]; + let toRead = PB_WeaponBase(pl.readyweapon); + if(toRead) + { + if(!toRead.wheelinfo) + return 0; + let wif = wheelinfocontainer(new(toRead.wheelinfo)); + if(!wif || !toRead.hasWheelSpecial) + return 0; + return wif.GetSPCount(pl.mo); + + } + return 0; + } + + ui bool selectNext() + { + int nItems = getSpecialsNumber(); + if (nItems == 0) return false; + + mSelectedIndex = (mSelectedIndex + 1) % nItems; + + return true; + } + + ui bool selectPrev() + { + int nItems = getSpecialsNumber(); + if (nItems == 0) return false; + + mSelectedIndex = (mSelectedIndex - 1 + nItems) % nItems; + + return true; + } + + ui bool setSelectedIndex(int index) + { + if (index == -1 || mSelectedIndex == index) return false; + + int nItems = getSpecialsNumber(); + if(nItems == 0) + return false; + index = clamp(index,0,nItems); + + mSelectedIndex = index; + + return true; + } + + ui int getSelectedIndex() const + { + return mSelectedIndex; + } + + string ConfirmSelection() const + { + if(token.size() > 0) + return token[mSelectedIndex]; + return ""; + } + + ui void Fill(out gb_ViewModel viewModel) + { + for(int i = 0; i < stag.size(); i++) + { + viewModel.tags .push(stag[i]); + viewModel.slots .push(i + 1); + viewModel.indices .push(i); + viewModel.icons .push(texman.checkfortexture(imgs[i])); + viewModel.iconScaleXs .push(Xscales[i]); + viewModel.iconScaleYs .push(Yscales[i]); + viewModel.quantity1 .push(-1); //no ammount for you >:( + viewModel.maxQuantity1.push(-1); // + viewModel.quantity2 .push(-1); // + viewModel.maxQuantity2.push(-1); // + } + viewModel.selectedIndex = clamp(mSelectedIndex,0,stag.size()-1); + } + + private ui void ClearSpecials() + { + stag.clear(); + token.clear(); + mSelectedIndex = 0; + imgs.clear(); + Xscales.clear(); + Yscales.clear(); + specialsinfo.clear(); + } + + array stag; + arrayXscales; + arrayYscales; + private int mSelectedIndex; + array token; + array imgs; + Array specialsinfo; +} diff --git a/zscript/gearbox/tools/ammo.zs b/zscript/gearbox/tools/ammo.zs new file mode 100644 index 0000000000..1d7b2ce327 --- /dev/null +++ b/zscript/gearbox/tools/ammo.zs @@ -0,0 +1,27 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_Ammo +{ + + static + bool isValid(int ammo, int maxAmmo) + { + return (ammo != -1 && maxAmmo > 0); + } + +} // class gb_Ammo diff --git a/zscript/gearbox/tools/cvar.zs b/zscript/gearbox/tools/cvar.zs new file mode 100644 index 0000000000..f39d2a8b34 --- /dev/null +++ b/zscript/gearbox/tools/cvar.zs @@ -0,0 +1,62 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +/** + * This class provides access to a user or server Cvar. + * + * Accessing Cvars through this class is faster because calling Cvar.GetCvar() + * is costly. This class caches the result of Cvar.GetCvar() and handles + * loading a savegame. + */ +class gb_Cvar +{ + +// public: ///////////////////////////////////////////////////////////////////////////////////////// + + static + gb_Cvar from(string name) + { + let result = new("gb_Cvar"); + + result.mName = name; + result.load(); + + return result; + } + + string getString() { if (!mCvar) load(); return mCvar.getString(); } + bool getBool() { if (!mCvar) load(); return mCvar.getInt(); } + int getInt() { if (!mCvar) load(); return mCvar.getInt(); } + double getDouble() { if (!mCvar) load(); return mCvar.getFloat(); } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private + void load() + { + mCvar = Cvar.getCvar(mName, players[consolePlayer]); + + if (mCvar == NULL) + { + gb_Log.error(string.Format("cvar %s not found", mName)); + } + } + + private string mName; + private transient Cvar mCvar; + +} // class gb_Cvar diff --git a/zscript/gearbox/tools/log.zs b/zscript/gearbox/tools/log.zs new file mode 100644 index 0000000000..a69dce9997 --- /dev/null +++ b/zscript/gearbox/tools/log.zs @@ -0,0 +1,58 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_Log +{ + + static + void print(string s) + { + Console.printf("%s", StringTable.localize(s, false)); + } + + static + void notice(string s) + { + Console.printf("[NOTICE] %s: %s", MOD_NAME, StringTable.localize(s, false)); + } + + static + void error(string s) + { + Console.printf("[ERROR] %s: %s.", MOD_NAME, s); + } + + static + void log(string s) + { + Console.printf("[LOG] %s: %s.", MOD_NAME, s); + } + + static + void debug(string s) + { + if (DEBUG_ENABLED) + { + Console.printf("[DEBUG] %s: %s.", MOD_NAME, s); + } + } + + const DEBUG_ENABLED = 0; // == false + + const MOD_NAME = "Gearbox"; + +} // class gb_Log diff --git a/zscript/gearbox/tools/texture_cache.zs b/zscript/gearbox/tools/texture_cache.zs new file mode 100644 index 0000000000..e2afae048a --- /dev/null +++ b/zscript/gearbox/tools/texture_cache.zs @@ -0,0 +1,71 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_TextureCache +{ + + static + gb_TextureCache from() + { + return new("gb_TextureCache"); + } + + void load() + { + isLoaded = true; + + // Wheel + circle = TexMan.checkForTexture("gb_circ", TexMan.Type_MiscPatch, TEXTURE_FLAGS); + halfCircle = TexMan.checkForTexture("gb_hcir", TexMan.Type_MiscPatch, TEXTURE_FLAGS); + ammoPip = TexMan.checkForTexture("gb_pip" , TexMan.Type_MiscPatch, TEXTURE_FLAGS); + hand = TexMan.checkForTexture("gb_hand", TexMan.Type_MiscPatch, TEXTURE_FLAGS); + pointer = TexMan.checkForTexture("gb_pntr", TexMan.Type_MiscPatch, TEXTURE_FLAGS); + textBox = TexMan.checkForTexture("gb_desc", TexMan.Type_MiscPatch, TEXTURE_FLAGS); + noIcon = TexMan.checkForTexture("gb_nope", TexMan.Type_MiscPatch, TEXTURE_FLAGS); + + // Blocks gb_wpsel.png + blockBox = TexMan.checkForTexture("gb_box", TexMan.Type_MiscPatch, TEXTURE_FLAGS); + blockBig = TexMan.checkForTexture("gb_weap", TexMan.Type_MiscPatch, TEXTURE_FLAGS); + corner = TexMan.checkForTexture("gb_cor", TexMan.Type_MiscPatch, TEXTURE_FLAGS); + ammoLine = TexMan.checkForTexture("gb_ammo", TexMan.Type_MiscPatch, TEXTURE_FLAGS); + blockBigSel = TexMan.checkForTexture("gb_wpsel", TexMan.Type_MiscPatch, TEXTURE_FLAGS); + + // Sizes + ammoPipSize = TexMan.getScaledSize(ammoPip); + } + + transient TextureID circle; + transient TextureID halfCircle; + transient TextureID ammoPip; + transient TextureID hand; + transient TextureID pointer; + transient TextureID textBox; + transient TextureID noIcon; + + transient TextureID blockBox; + transient TextureID blockBig; + transient TextureID blockBigSel; + transient TextureID corner; + transient TextureID ammoLine; + + transient vector2 ammoPipSize; + + transient bool isLoaded; + + const TEXTURE_FLAGS = 0; + +} // class gb_TextureCache diff --git a/zscript/gearbox/view_model.zs b/zscript/gearbox/view_model.zs new file mode 100644 index 0000000000..ec326187af --- /dev/null +++ b/zscript/gearbox/view_model.zs @@ -0,0 +1,37 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +struct gb_ViewModel +{ + + int selectedIndex; + + Array tags; + Array slots; + Array indices; + + Array icons; + Array iconScaleXs; + Array iconScaleYs; + + Array quantity1; + Array maxQuantity1; + + Array quantity2; + Array maxQuantity2; + +} // struct gb_ViewModel diff --git a/zscript/gearbox/weapon_data.zs b/zscript/gearbox/weapon_data.zs new file mode 100644 index 0000000000..99de501e63 --- /dev/null +++ b/zscript/gearbox/weapon_data.zs @@ -0,0 +1,24 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +struct gb_WeaponData +{ + + Array< class > weapons; + Array slots; + +} // struct gb_WeaponData diff --git a/zscript/gearbox/weapon_data_loader.zs b/zscript/gearbox/weapon_data_loader.zs new file mode 100644 index 0000000000..25cd8f69d6 --- /dev/null +++ b/zscript/gearbox/weapon_data_loader.zs @@ -0,0 +1,154 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_WeaponDataLoader play +{ + + /** + * Code is adapted from + * GZDoom::src/src/gzdoom/wadsrc/static/zscript/actors/player/player_cheat.zs::CheatGive. + */ + static + void load(out gb_WeaponData data) + { + int nClasses = AllActorClasses.Size(); + + gb_WeaponInfo info; + PlayerInfo player = players[consolePlayer]; + + for (int i = 0; i < nClasses; ++i) + { + let type = (class)(AllActorClasses[i]); + + if (type == NULL || type == "Weapon") continue; + + // This check from CheatGive doesn't work here. + // Why does it allow giving weapons for Treasure Tech there? + // Anyway, adding some replaced weapons to the data list won't harm, they + // won't show up unless the player has them. + //let replacement = Actor.getReplacement(type); + //if (replacement != type && !(replacement is "DehackedPickup")) continue; + + bool located; + int slot; + int priority; + [located, slot, priority] = player.weapons.LocateWeapon(type); + + if (!located) continue; + + readonly def = GetDefaultByType(type); + if (!def.CanPickup(player.mo)) continue; + + info.push(type, slot, priority); + } + + sortWeapons(info); + + data.weapons.move(info.classes); + data.slots.move(info.slots); + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private static + void sortWeapons(gb_WeaponInfo info) + { + int nWeapons = info.classes.size(); + + quickSortWeapons(info, 0, nWeapons - 1); + } + + private static + void quickSortWeapons(gb_WeaponInfo info, int lo, int hi) + { + if (lo < hi) + { + int p = quickSortWeaponsPartition(info, lo, hi); + quickSortWeapons(info, lo, p - 1); + quickSortWeapons(info, p + 1, hi ); + } + } + + private static + int quickSortWeaponsPartition(gb_WeaponInfo info, int lo, int hi) + { + int pivot = measure(info, hi); + int i = lo - 1; + + for (int j = lo; j <= hi - 1; ++j) + { + if (measure(info, j) <= pivot) + { + ++i; + info.swap(i, j); + } + } + info.swap(i + 1, hi); + + return i + 1; + } + + private static + int measure(gb_WeaponInfo info, int index) + { + int slot = info.slots[index]; + if (slot == 0) slot = 99; + + int result = slot * 100 + info.priorities[index]; + return result; + } + +} // class gb_WeaponDataLoader + +struct gb_WeaponInfo +{ + +// public: ///////////////////////////////////////////////////////////////////////////////////////// + + void push(class aClass, int slot, int priority) + { + classes .push(aClass); + slots .push(slot); + priorities.push(priority); + } + + void swap(int i, int j) + { + { + let tmp = classes[i]; + classes[i] = classes[j]; + classes[j] = tmp; + } + { + int tmp = slots[i]; + slots[i] = slots[j]; + slots[j] = tmp; + } + { + int tmp = priorities[i]; + priorities[i] = priorities[j]; + priorities[j] = tmp; + } + } + +// public: ///////////////////////////////////////////////////////////////////////////////////////// + + Array< class > classes; + Array slots; + Array priorities; + +} // struct gb_WeaponInfo diff --git a/zscript/gearbox/weapon_menu.zs b/zscript/gearbox/weapon_menu.zs new file mode 100644 index 0000000000..48239fb9a9 --- /dev/null +++ b/zscript/gearbox/weapon_menu.zs @@ -0,0 +1,444 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * Carrascado 2022 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_WeaponMenu +{ + + static + gb_WeaponMenu from(gb_WeaponData weaponData, gb_Options options) + { + let result = new("gb_WeaponMenu"); + + result.mWeapons.move(weaponData.weapons); + result.mSlots.move(weaponData.slots); + result.mSelectedIndex = 0; + result.mCacheTime = 0; + result.mOptions = options; + + loadIconServices(result.mIconServices); + loadHideServices(result.mHideServices); + + return result; + } + + int getSelectedIndex() const + { + return mSelectedIndex; + } + + bool setSelectedIndexFromView(gb_ViewModel viewModel, int index) + { + if (index == -1 || mSelectedIndex == viewModel.indices[index]) return false; + + mSelectedIndex = viewModel.indices[index]; + return true; + } + + void setSelectedWeapon(class aClass) + { + if (aClass == NULL) return; + + uint index = getIndexOf(aClass); + if (index != mWeapons.size()) mSelectedIndex = index; + } + + ui + bool selectNextWeapon() + { + mSelectedIndex = findNextWeapon(); + return mSelectedIndex != mWeapons.size(); + } + + ui + bool selectPrevWeapon() + { + mSelectedIndex = findPrevWeapon(); + return mSelectedIndex != mWeapons.size(); + } + + bool selectSlot(int slot, bool selectFirstWeapon = false) + { + uint nWeapons = mWeapons.size(); + int direction = mOptions.isSlotCycleOrderReversed() ? -1 : 1; + int startOffset = selectFirstWeapon ? 0 : (mSelectedIndex + direction); + + for (uint i = 0; i < nWeapons; ++i) + { + uint index = (startOffset + nWeapons + direction * i) % nWeapons; + if (mSlots[index] == slot && isInInventory(index) && !isHidden(mWeapons[index].getClassName())) + { + mSelectedIndex = index; + return true; + } + } + + return false; + } + + bool isOneWeaponInSlot(int slot) const + { + uint nWeapons = mWeapons.size(); + int nWeaponsInSlot = 0; + for (uint i = 1; i < nWeapons; ++i) + { + uint index = (mSelectedIndex + nWeapons - i) % nWeapons; + nWeaponsInSlot += (mSlots[index] == slot && isInInventory(index)); + if (nWeaponsInSlot > 1) return false; + } + if (nWeaponsInSlot == 0) return false; + return true; + } + + bool isInInventory(int index) const + { + return NULL != players[consolePlayer].mo.findInventory(mWeapons[index]); + } + + string confirmSelection() const + { + if (mSelectedIndex >= mWeapons.size()) return ""; + + return getDefaultByType(mWeapons[mSelectedIndex]).getClassName(); + } + + ui + void fill(out gb_ViewModel viewModel) + { + // every other tic. + bool isCacheValid = (level.time <= mCacheTime + 1); + if (!isCacheValid) + { + mCacheTime = level.time; + mCachedViewModel.tags .clear(); + mCachedViewModel.slots .clear(); + mCachedViewModel.indices .clear(); + mCachedViewModel.icons .clear(); + mCachedViewModel.iconScaleXs .clear(); + mCachedViewModel.iconScaleYs .clear(); + mCachedViewModel.quantity1 .clear(); + mCachedViewModel.maxQuantity1.clear(); + mCachedViewModel.quantity2 .clear(); + mCachedViewModel.maxQuantity2.clear(); + + fillDirect(mCachedViewModel); + } + + copy(mCachedViewModel, viewModel); + } + + void rotatePriority() + { + mSelectedIndex = rotatePriorityForIndex(mSelectedIndex); + } + + uint rotatePriorityForIndex(int index) + { + bool isIndexFound; + uint targetIndex; + [isIndexFound, targetIndex] = findIndexOfNextWeaponWithSlot(index, mSlots[index]); + + if (!isIndexFound) return mSelectedIndex; + + rotate(index, targetIndex); + return targetIndex; + } + + void rotateSlot() + { + mSelectedIndex = rotateSlotForIndex(mSelectedIndex); + } + + int rotateSlotForIndex(int oldIndex) + { + uint nWeapons = mWeapons.size(); + if (nWeapons < 2) return oldIndex; + + int oldSlot = mSlots[oldIndex]; + int newSlot = getNextSlot(oldSlot); + + uint i = 0; + for (int slot = 1;;) + { + while (i < nWeapons && mSlots[i] == slot) ++i; + + if (slot == newSlot) + { + mSlots[oldIndex] = newSlot; + move(oldIndex, i); + return (oldIndex < i) ? i - 1 : i; + } + + slot = getNextSlot(slot); + if (slot == 1) break; + } + + return oldIndex; + } + + bool isThereNoWeapons() const + { + bool isNothingFound = true; + uint nWeapons = mWeapons.size(); + for (uint i = 0; i < nWeapons; ++i) + { + if (isInInventory(i) && !isHidden(mWeapons[i].getClassName())) return false; + } + return isNothingFound; + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private + void rotate(uint oldIndex, uint newIndex) + { + if (oldIndex > newIndex) move(oldIndex, newIndex); + else move(oldIndex, newIndex + 1); + } + + private + void move(uint oldIndex, uint newIndex) + { + if (oldIndex == newIndex) return; + + mWeapons.insert(newIndex, mWeapons[oldIndex]); + mSlots .insert(newIndex, mSlots [oldIndex]); + + if (newIndex < oldIndex) ++oldIndex; + + mWeapons.delete(oldIndex); + mSlots .delete(oldIndex); + } + + private + bool, uint findIndexOfNextWeaponWithSlot(uint weaponIndex, int slot) + { + uint nWeapons = mWeapons.size(); + for (uint i = 1; i < nWeapons; ++i) + { + uint targetCandidateIndex = (weaponIndex + i) % nWeapons; + if (mSlots[targetCandidateIndex] == slot) + { + return true, targetCandidateIndex; + } + } + + return false, 0; + } + + private static + int getNextSlot(int slot) + { + if (1 <= slot && slot <= 8) return slot + 1; + + switch (slot) + { + case 9: return 0; + case 0: return 10; + case 10: return 11; + case 11: return 1; + } + + Console.printf("Unexpected slot: %d.", slot); + return 1; + } + + private static ui + void copy(gb_ViewModel source, out gb_ViewModel destination) + { + destination.selectedIndex = source.selectedIndex; + + destination.tags .copy(source.tags); + destination.slots .copy(source.slots); + destination.indices .copy(source.indices); + destination.icons .copy(source.icons); + destination.iconScaleXs .copy(source.iconScaleXs); + destination.iconScaleYs .copy(source.iconScaleYs); + destination.quantity1 .copy(source.quantity1); + destination.maxQuantity1.copy(source.maxQuantity1); + destination.quantity2 .copy(source.quantity2); + destination.maxQuantity2.copy(source.maxQuantity2); + } + + private ui + void fillDirect(out gb_ViewModel viewModel) + { + uint nWeapons = mWeapons.size(); + for (uint i = 0; i < nWeapons; ++i) + { + let aWeapon = Weapon(players[consolePlayer].mo.findInventory(mWeapons[i])); + + if (aWeapon == NULL) + { + if (mOptions.isPositionLocked()) + { + viewModel.tags .push(""); + viewModel.slots .push(mSlots[i]); + viewModel.indices .push(i); + viewModel.icons .push(texman.checkfortexture("TNT1A0"));//-1); + viewModel.iconScaleXs .push(-1); + viewModel.iconScaleYs .push(-1); + viewModel.quantity1 .push(-1); + viewModel.maxQuantity1.push(-1); + viewModel.quantity2 .push(-1); + viewModel.maxQuantity2.push(-1); + } + continue; + } + + if (isHidden(aWeapon.getClassName())) continue; + + if (mSelectedIndex == i) viewModel.selectedIndex = viewModel.tags.size(); + + viewModel.tags.push(aWeapon.getTag()); + viewModel.slots.push(mSlots[i]); + viewModel.indices.push(i); + + TextureID icon = getIconFor(aWeapon); + + // Workaround, casting TextureID to int may be unreliable. + viewModel.icons.push(icon);//(int(icon)); + viewModel.iconScaleXs.push(aWeapon.scale.x); + viewModel.iconScaleYs.push(aWeapon.scale.y); + + bool hasAmmo1 = aWeapon.ammo1; + bool hasAmmo2 = aWeapon.ammo2 && aWeapon.ammo2 != aWeapon.ammo1; + + viewModel. quantity1.push(hasAmmo1 ? aWeapon.ammo1. amount : -1); + viewModel.maxQuantity1.push(hasAmmo1 ? aWeapon.ammo1.maxAmount : -1); + viewModel. quantity2.push(hasAmmo2 ? aWeapon.ammo2. amount : -1); + viewModel.maxQuantity2.push(hasAmmo2 ? aWeapon.ammo2.maxAmount : -1); + } + } + + private play + bool isHidden(string className) const + { + bool result = false; + + uint nServices = mHideServices.size(); + for (uint i = 0; i < nServices; ++i) + { + let service = mHideServices[i]; + string hideResponse = service.get(className); + if (hideResponse.length() != 0) + { + bool isHidden = hideResponse.byteAt(0) - 48; // convert to bool from "0" or "1". + result = isHidden; + } + } + + return result; + } + + private ui + TextureID getIconFor(Weapon aWeapon) const + { + TextureID icon = BaseStatusBar.getInventoryIcon(aWeapon, BaseStatusBar.DI_AltIconFirst); + + { + uint nServices = mIconServices.size(); + string className = aWeapon.getClassName(); + for (uint i = 0; i < nServices; ++i) + { + let service = mIconServices[i]; + string iconResponse = service.uiGet(className); + if (iconResponse.length() != 0) + { + TextureID iconFromService = TexMan.checkForTexture(iconResponse, TexMan.Type_Any); + if (iconFromService.isValid()) icon = iconFromService; + } + } + } + + return icon; + } + + private play State getReadyState(Weapon w) const { return w.getReadyState(); } + + private ui uint findNextWeapon() const { return findWeapon( 1); } + private ui uint findPrevWeapon() const { return findWeapon(-1); } + + /** + * @param direction search direction from the selected weapon: 1 or -1. + * + * @returns a weapon in the direction. If there is only one weapon, return + * it. If there are no weapons, return weapons number. + */ + private ui + uint findWeapon(int direction) const + { + uint nWeapons = mWeapons.size(); + // Note range: [1; nWeapons + 1) instead of [0; nWeapons). + // This is because I want the current weapon to be found last. + for (uint i = 1; i < nWeapons + 1; ++i) + { + uint index = (mSelectedIndex + i * direction + nWeapons) % nWeapons; + if (isHidden(mWeapons[index].getClassName())) continue; + if (isInInventory(index)) return index; + } + + return nWeapons; + } + + private + uint getIndexOf(class aClass) const + { + uint nWeapons = mWeapons.size(); + for (uint i = 0; i < nWeapons; ++i) + { + if (mWeapons[i] == aClass) return i; + } + return nWeapons; + } + + private static + void loadIconServices(out Array services) + { + loadServices("gb_IconService", services); + } + + private static + void loadHideServices(out Array services) + { + loadServices("gb_HideService", services); + } + + private static + void loadServices(string serviceName, out Array services) + { + let iterator = gb_ServiceIterator.find(serviceName); + gb_Service aService; + while (aService = iterator.next()) + { + services.push(aService); + } + } + + private Array< class > mWeapons; + private Array< int > mSlots; + private uint mSelectedIndex; + + private Array mIconServices; + private Array mHideServices; + + private gb_ViewModel mCachedViewModel; + private int mCacheTime; + + private gb_Options mOptions; + +} // class gb_WeaponMenu diff --git a/zscript/gearbox/wheel/controller.zs b/zscript/gearbox/wheel/controller.zs new file mode 100644 index 0000000000..acbabc262e --- /dev/null +++ b/zscript/gearbox/wheel/controller.zs @@ -0,0 +1,102 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_WheelController +{ + + static + gb_WheelController from(gb_Options options, gb_Screen screen) + { + let result = new("gb_WheelController"); + + result.reset(); + result.mScreen = screen; + result.mOptions = options; + + return result; + } + + void reset() + { + mX = 0; + mY = 0; + + PlayerInfo player = players[consolePlayer]; + mStartPitch = player.mo.pitch; + mStartYaw = player.mo.angle; + } + + void fill(out gb_WheelControllerModel model) + { + model.angle = getAngle(); + model.radius = getRadius(); + } + + play + void process() const + { + if (!mOptions.isMouseInWheel()) return; + + vector2 mouseSensitivity = mOptions.getMouseSensitivity(); + double yawMod = 0.08 * mouseSensitivity.x; + double pitchMod = 0.08 * mouseSensitivity.y; + + PlayerInfo player = players[consolePlayer]; + // Code derived from PyWeaponWheel's mouse input handling. + mX -= int(round(player.original_cmd.yaw * yawMod )); + mY -= int(round(player.original_cmd.pitch * pitchMod)); + + if (multiplayer) + { + gb_Sender.sendPlayerAngles(mStartPitch, mStartYaw); + } + else + { + gb_Changer.setAngles(players[consolePlayer], mStartPitch, mStartYaw); + } + + vector2 center = mScreen.getWheelCenter(); + int centerX = int(center.x); + int centerY = int(center.y); + mX = clamp(mX, -centerX, Screen.getWidth() - centerX); + mY = clamp(mY, -centerY, Screen.getHeight() - centerY); + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private + double getAngle() const + { + return -atan2(mX, mY) + 180; + } + + private + double getRadius() const + { + return sqrt(mX * mX + mY * mY); + } + + private int mX; + private int mY; + + private double mStartPitch; + private double mStartYaw; + + private gb_Screen mScreen; + private gb_Options mOptions; + +} // class gb_WheelController diff --git a/zscript/gearbox/wheel/controller_model.zs b/zscript/gearbox/wheel/controller_model.zs new file mode 100644 index 0000000000..82295b7a6f --- /dev/null +++ b/zscript/gearbox/wheel/controller_model.zs @@ -0,0 +1,24 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +struct gb_WheelControllerModel +{ + + double angle; + double radius; + +} // struct gb_WheelControllerModel diff --git a/zscript/gearbox/wheel/indexer.zs b/zscript/gearbox/wheel/indexer.zs new file mode 100644 index 0000000000..a05183b4ff --- /dev/null +++ b/zscript/gearbox/wheel/indexer.zs @@ -0,0 +1,237 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_WheelIndexer +{ + + static + gb_WheelIndexer from(gb_MultiWheelMode multiWheelMode, gb_Screen screen) + { + let result = new("gb_WheelIndexer"); + result.mSelectedIndex = UNDEFINED_INDEX; + result.mLastSlotIndex = UNDEFINED_INDEX; + result.mInnerIndex = UNDEFINED_INDEX; + result.mOuterIndex = UNDEFINED_INDEX; + result.mMultiWheelMode = multiWheelMode; + result.mScreen = screen; + return result; + } + + int getSelectedIndex() const { return mSelectedIndex; } + + int getInnerIndex(int externalSelectedIndex, gb_ViewModel viewModel) const + { + if (areIndicesDefined()) return mInnerIndex; + + uint nWeapons = viewModel.tags.size(); + uint externalSelectedIndexInModel = UNDEFINED_INDEX; + for (uint i = 0; i < nWeapons; ++i) + { + if (viewModel.indices[i] == externalSelectedIndex) + { + externalSelectedIndexInModel = i; + break; + } + } + + if (!mMultiWheelMode.isEngaged(viewModel)) return externalSelectedIndexInModel; + + gb_MultiWheelModel multiWheelModel; + gb_MultiWheel.fill(viewModel, multiWheelModel); + + uint nPlaces = multiWheelModel.data.size(); + for (uint i = 0; i < nPlaces; ++i) + { + if (multiWheelModel.isWeapon[i]) + { + if (viewModel.indices[multiWheelModel.data[i]] == externalSelectedIndex) return i; + } + else + { + if (multiWheelModel.data[i] == viewModel.slots[externalSelectedIndexInModel]) return i; + } + } + + return UNDEFINED_INDEX; + } + + int getOuterIndex(int externalSelectedIndex, gb_ViewModel viewModel) const + { + if (areIndicesDefined()) return mOuterIndex; + + if (!mMultiWheelMode.isEngaged(viewModel)) return UNDEFINED_INDEX; + + uint nWeapons = viewModel.tags.size(); + uint externalSelectedIndexInModel = UNDEFINED_INDEX; + for (uint i = 0; i < nWeapons; ++i) + { + if (viewModel.indices[i] == externalSelectedIndex) + { + externalSelectedIndexInModel = i; + break; + } + } + + int slot = viewModel.slots[externalSelectedIndexInModel]; + + uint start = 0; + for (; start < nWeapons && viewModel.slots[start] != slot; ++start); + + return externalSelectedIndexInModel - start; + } + + void update(gb_ViewModel viewModel, gb_WheelControllerModel controllerModel) + { + if (controllerModel.radius < mScreen.getWheelDeadRadius()) + { + mSelectedIndex = UNDEFINED_INDEX; + mLastSlotIndex = UNDEFINED_INDEX; + mInnerIndex = UNDEFINED_INDEX; + mOuterIndex = UNDEFINED_INDEX; + return; + } + + uint nWeapons = viewModel.tags.size(); + + if (!mMultiWheelMode.isEngaged(viewModel)) + { + mSelectedIndex = gb_WheelInnerIndexer.getSelectedIndex(nWeapons, controllerModel, mScreen); + mLastSlotIndex = UNDEFINED_INDEX; + mInnerIndex = mSelectedIndex; + mOuterIndex = UNDEFINED_INDEX; + return; + } + + gb_MultiWheelModel multiWheelModel; + gb_MultiWheel.fill(viewModel, multiWheelModel); + + uint nPlaces = multiWheelModel.data.size(); + int wheelRadius = mScreen.getWheelRadius(); + + if (controllerModel.radius < wheelRadius) + { + reportInnerIndex(nPlaces, controllerModel, multiWheelModel, viewModel); + return; + } + + if (mLastSlotIndex == UNDEFINED_INDEX || mLastSlotIndex >= multiWheelModel.data.size()) + { + mSelectedIndex = UNDEFINED_INDEX; + mInnerIndex = UNDEFINED_INDEX; + mOuterIndex = UNDEFINED_INDEX; + return; + } + + int slot = multiWheelModel.data[mLastSlotIndex]; + + uint start = 0; + for (; start < nWeapons && viewModel.slots[start] != slot; ++start); + uint end = start; + for (; end < nWeapons && viewModel.slots[end] == slot; ++end); + uint nWeaponsInSlot = end - start; + + double slotAngle = itemAngle(nPlaces, mLastSlotIndex); + + double r = controllerModel.radius; + double w = wheelRadius; + double forSlotAngle = slotAngle - controllerModel.angle; + double side = sqrt(r * r + w * w - 2 * r * w * cos(forSlotAngle)); + + if (side < mScreen.getWheelDeadRadius()) + { + reportInnerIndex(nPlaces, controllerModel, multiWheelModel, viewModel); + return; + } + + // due to computation precision, the value of ratio may be slightly out of range [-1, 1]. + double ratio = clamp(r / side * sin(forSlotAngle), -1.0, 1.0); + double angle = -asin(ratio); + + // Limit angle on the borders of the second wheel: + double borderRadius = wheelRadius / sin(90 - forSlotAngle); + if (r < borderRadius) + { + angle = (angle > 0) ? 89.9999 : -89.9999; + } + + angle += 90; + angle %= 180; + + int indexInSlot = int((angle * nWeaponsInSlot / 180.0) % nWeaponsInSlot); + + mSelectedIndex = start + indexInSlot; + mInnerIndex = mLastSlotIndex; + mOuterIndex = indexInSlot; + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private + void reportInnerIndex( uint nPlaces + , gb_WheelControllerModel controllerModel + , gb_MultiWheelModel multiWheelModel + , gb_ViewModel viewModel + ) + { + int innerIndex = gb_WheelInnerIndexer.getSelectedIndex(nPlaces, controllerModel, mScreen); + bool isWeapon = multiWheelModel.isWeapon[innerIndex]; + + mSelectedIndex = isWeapon + ? multiWheelModel.data[innerIndex] + : firstInSlot(viewModel, multiWheelModel.data[innerIndex]); + + mLastSlotIndex = isWeapon ? UNDEFINED_INDEX : innerIndex; + mInnerIndex = innerIndex; + mOuterIndex = 0; + } + + private static + int firstInSlot(gb_ViewModel viewModel, int slot) + { + int nWeapons = viewModel.tags.size(); + for (int i = 0; i < nWeapons; ++i) + { + if (viewModel.slots[i] == slot) return i; + } + + return UNDEFINED_INDEX; + } + + private + bool areIndicesDefined() const + { + return (mInnerIndex != UNDEFINED_INDEX); + } + + private static + double itemAngle(uint nItems, uint index) + { + return 360.0 / nItems * index; + } + + const UNDEFINED_INDEX = -1; + + private int mSelectedIndex; + + private int mLastSlotIndex; + private int mInnerIndex; + private int mOuterIndex; + + private gb_MultiWheelMode mMultiWheelMode; + private gb_Screen mScreen; + +} // class gb_WheelIndexer diff --git a/zscript/gearbox/wheel/inner_indexer.zs b/zscript/gearbox/wheel/inner_indexer.zs new file mode 100644 index 0000000000..91bc528f44 --- /dev/null +++ b/zscript/gearbox/wheel/inner_indexer.zs @@ -0,0 +1,33 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_WheelInnerIndexer +{ + + static + int getSelectedIndex(uint nItems, gb_WheelControllerModel controllerModel, gb_Screen screen) + { + if (controllerModel.radius < screen.getWheelDeadRadius() || nItems == 0) + { + return -1; + } + + int result = int(round(controllerModel.angle * nItems / 360.0)) % nItems; + return result; + } + +} // class gb_WheelInnerIndexer diff --git a/zscript/gearbox/wheel/multiwheel.zs b/zscript/gearbox/wheel/multiwheel.zs new file mode 100644 index 0000000000..da011635e8 --- /dev/null +++ b/zscript/gearbox/wheel/multiwheel.zs @@ -0,0 +1,66 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +struct gb_MultiWheelModel +{ + Array isWeapon; + Array data; // slot or weapon index +} + +class gb_MultiWheel +{ + + static + void fill(gb_ViewModel viewModel, out gb_MultiWheelModel model) + { + uint nWeapons = viewModel.tags.size(); + int lastSlot = -1; + + for (uint i = 0; i < nWeapons; ++i) + { + int slot = viewModel.slots[i]; + bool isSingle = isSingleWeaponInSlot(viewModel, nWeapons, slot); + if (isSingle) + { + model.isWeapon.push(true); + model.data.push(i); + } + else + { + if (slot != lastSlot) + { + lastSlot = slot; + model.isWeapon.push(false); + model.data.push(slot); + } + } + } + } + + static + bool isSingleWeaponInSlot(gb_ViewModel viewModel, uint nWeapons, int slot) + { + int nWeaponsInSlot = 0; + for (uint i = 0; i < nWeapons; ++i) + { + nWeaponsInSlot += (viewModel.slots[i] == slot); + if (nWeaponsInSlot > 1) return false; + } + return true; + } + +} // class gb_MultiWheel diff --git a/zscript/gearbox/wheel/multiwheel_mode.zs b/zscript/gearbox/wheel/multiwheel_mode.zs new file mode 100644 index 0000000000..b9080c33d3 --- /dev/null +++ b/zscript/gearbox/wheel/multiwheel_mode.zs @@ -0,0 +1,40 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_MultiWheelMode +{ + + static + gb_MultiWheelMode from(gb_Options options) + { + let result = new("gb_MultiWheelMode"); + result.mOptions = options; + return result; + } + + bool isEngaged(gb_ViewModel viewModel) + { + uint nWeapons = viewModel.tags.size(); + bool result = (nWeapons > mOptions.getMultiWheelLimit()); + return result; + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private gb_Options mOptions; + +} // class gb_MultiWheelMode diff --git a/zscript/gearbox/wheel/screen.zs b/zscript/gearbox/wheel/screen.zs new file mode 100644 index 0000000000..64372d9749 --- /dev/null +++ b/zscript/gearbox/wheel/screen.zs @@ -0,0 +1,69 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +/** + * This class provides helper functions for screen position and sizes. + */ +class gb_Screen +{ + + static + gb_Screen from(gb_Options options) + { + let result = new("gb_Screen"); + result.mOptions = options; + return result; + } + + vector2 getWheelCenter() const + { + int screenWidth = Screen.getWidth(); + int halfScreenHeight = Screen.getHeight() / 2; + int centerPosition = int((screenWidth / 2 - halfScreenHeight) * mOptions.getWheelPosition()); + return (screenWidth / 2 + centerPosition, halfScreenHeight); + } + + int getWheelRadius() const + { + return getScaledScreenHeight() / 4; + } + + int getWheelDeadRadius() const + { + return getScaledScreenHeight() / 16; + } + + double getScaleFactor() const + { + return getScaledScreenHeight() / 1080.0; + } + + int getScaledScreenHeight() const + { + return int(Screen.getHeight() * mOptions.getWheelScale()); + } + + int getScaledScreenWidth() const + { + return int(Screen.getWidth() * mOptions.getWheelScale()); + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private gb_Options mOptions; + +} // class gb_Screen diff --git a/zscript/gearbox/wheel/text.zs b/zscript/gearbox/wheel/text.zs new file mode 100644 index 0000000000..13fdb7c2f9 --- /dev/null +++ b/zscript/gearbox/wheel/text.zs @@ -0,0 +1,137 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +/** + * This class helps displaying text on screen. + */ +class gb_Text +{ + + static + gb_Text from(gb_TextureCache textureCache, gb_Screen screen, gb_FontSelector fontSelector) + { + let result = new("gb_Text"); + result.mTextureCache = textureCache; + result.mScreen = screen; + result.mFontSelector = fontSelector; + return result; + } + + static + void draw(string aString, vector2 pos, Font aFont, double alpha, bool isBig = false, int fontcol = font.CR_WHITE) + { + int textScale = isBig ? getBigTextScale() : getTextScale(); + + pos.x -= aFont.stringWidth(aString) * textScale / 2; + pos.y -= aFont.getHeight() * textScale / 2; + + Screen.drawText( aFont + , fontcol //added to change the color for each specific call instead of just generally + , pos.x + , pos.y + , aString + , DTA_Alpha , alpha + , DTA_ScaleX , textScale + , DTA_ScaleY , textScale + ); + } + + void drawBox( string topText + , string middleText + , string bottomText + , vector2 pos + , bool isPosTop // true: pos is the top of text box, false: bottom. + , color baseColor + , double alpha, + int fontcol = font.CR_WHITE, //added to change the color for each specific call instead of just generally + bool novertscale = false //dont adjust the vertical scale of the box + ) + { + double scaleFactor = mScreen.getScaleFactor(); + Font aFont = mFontSelector.getFont(); + int textScale = getTextScale(); + int lineHeight = aFont.getHeight() * textScale; + int margin = int(10 * scaleFactor); + int height = int((margin * 2 + lineHeight * 3) / scaleFactor); + + if (!isPosTop) pos.y -= height * scaleFactor; + + int topTextWidth = aFont.stringWidth(topText); + int middleTextWidth = aFont.stringWidth(middleText); + int bottomTextWidth = aFont.stringWidth(bottomText); + + int width = max(int( ( max(middleTextWidth, max(topTextWidth, bottomTextWidth)) * textScale + + margin * 2 + ) / scaleFactor + ), height * 2); + vector2 size = (width, height) * scaleFactor; + vector2 center = pos + (0, size.y / 2); + + if(novertscale) + { + Screen.drawTexture( mTextureCache.textBox + , NO_ANIMATION + , center.x + , center.y + //, DTA_FillColor , baseColor + , DTA_AlphaChannel , true + , DTA_CenterOffset , true + , DTA_Alpha , alpha + , DTA_DestWidth , int(size.x) + ); + } + else + { + Screen.drawTexture( mTextureCache.textBox + , NO_ANIMATION + , center.x + , center.y + //, DTA_FillColor , baseColor + , DTA_AlphaChannel , true + , DTA_CenterOffset , true + , DTA_Alpha , alpha + , DTA_DestWidth , int(size.x) + , DTA_DestHeight , int(size.y) //aaa + ); + } + vector2 line = (0, lineHeight); + if (topText .length() > 0) draw(topText , center - line, aFont, alpha,fontcol:fontcol); + if (middleText.length() > 0) draw(middleText, center , aFont, alpha,fontcol:fontcol); + if (bottomText.length() > 0) draw(bottomText, center + line, aFont, alpha,fontcol:fontcol); + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private static + int getBigTextScale() + { + return getTextScale() * 2; + } + + private static + int getTextScale() + { + return max(Screen.getHeight() / 720, 1); + } + + const NO_ANIMATION = 0; // == false + + private gb_TextureCache mTextureCache; + private gb_Screen mScreen; + private gb_FontSelector mFontSelector; + +} // class gb_Text diff --git a/zscript/gearbox/wheel/view.zs b/zscript/gearbox/wheel/view.zs new file mode 100644 index 0000000000..8b0161fcb7 --- /dev/null +++ b/zscript/gearbox/wheel/view.zs @@ -0,0 +1,577 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2020-2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_WheelView +{ + + static + gb_WheelView from( gb_Options options + , gb_MultiWheelMode multiWheelMode + , gb_TextureCache textureCache + , gb_Screen screen + , gb_FontSelector fontSelector + ) + { + let result = new("gb_WheelView"); + + result.setAlpha(1.0); + result.setBaseColor(0x2222CC); + + result.mScreen = screen; + result.mOptions = options; + result.mFontSelector = fontSelector; + result.mMultiWheelMode = multiWheelMode; + result.mText = gb_Text.from(textureCache, screen, fontSelector); + result.mTextureCache = textureCache; + result.mIsRotating = true; + + return result; + } + + void setAlpha(double alpha) + { + mAlpha = alpha; + } + + void setBaseColor(int color) + { + mBaseColor = color; + } + + void setRotating(bool isRotating) + { + mIsRotating = isRotating; + } + + void display( gb_ViewModel viewModel + , gb_WheelControllerModel controllerModel + , bool showPointer + , int innerIndex + , int outerIndex + ) const + { + mScaleFactor = mScreen.getScaleFactor(); + mCenter = mScreen.getWheelCenter(); + + drawInnerWheel(); + + uint nWeapons = viewModel.tags.size(); + if (nWeapons == 0) return; + + int screenHeight = mScreen.getScaledScreenHeight(); + int radius = screenHeight * 5 / 32; + int allowedWidth = int(screenHeight * 3 / 16 - MARGIN * 2 * mScaleFactor); + + int nPlaces = 0; + + bool isSlotExpanded = false; + bool isMultiWheelMode = mMultiWheelMode.isEngaged(viewModel); + if (isMultiWheelMode) + { + gb_MultiWheelModel multiWheelModel; + gb_MultiWheel.fill(viewModel, multiWheelModel); + + nPlaces = multiWheelModel.data.size(); + for (uint i = 0; i < nPlaces; ++i) + { + bool isWeapon = multiWheelModel.isWeapon[i]; + int data = multiWheelModel.data[i]; + + if (isWeapon) displayWeapon(i, data, nPlaces, radius, allowedWidth, viewModel, mCenter); + else displaySlot (i, data, nPlaces, radius); + } + + drawHands(nPlaces, innerIndex, mCenter, 0); + + isSlotExpanded = (outerIndex != UNDEFINED_INDEX && !multiWheelModel.isWeapon[innerIndex]); + if (isSlotExpanded) + { + int slot = multiWheelModel.data[innerIndex]; + drawOuterWeapons( innerIndex + , outerIndex + , nPlaces + , slot + , viewModel + , radius + , allowedWidth + , int(controllerModel.radius) + ); + } + } + else + { + isSlotExpanded = false; + for (uint i = 0; i < nWeapons; ++i) + { + if (i == innerIndex) continue; + displayWeapon(i, i, nWeapons, radius, allowedWidth, viewModel, mCenter); + } + + nPlaces = nWeapons; + if (innerIndex != -1) + { + displayWeapon(innerIndex, innerIndex, nPlaces, radius, allowedWidth, viewModel, mCenter); + } + drawHands(nPlaces, innerIndex, mCenter, 0); + } + + if (showPointer) + { + drawPointer(controllerModel.angle, controllerModel.radius); + } + + if (mOptions.isShowingTags()) + { + drawWeaponDescription(viewModel, innerIndex, nPlaces, isSlotExpanded); + } + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private + void drawOuterWeapons( int innerIndex + , int outerIndex + , uint nPlaces + , int slot + , gb_ViewModel viewModel + , int radius + , int allowedWidth + , int controllerRadius + ) + { + int wheelRadius = mScreen.getWheelRadius(); + double angle = itemAngle(nPlaces, innerIndex); + vector2 outerWheelCenter = (sin(angle), -cos(angle)) * wheelRadius + mCenter; + drawOuterWheel(outerWheelCenter.x, outerWheelCenter.y, -angle); + + uint nWeapons = viewModel.tags.size(); + + uint start = 0; + for (; start < nWeapons && viewModel.slots[start] != slot; ++start); + uint end = start; + for (; end < nWeapons && viewModel.slots[end] == slot; ++end); + + uint nWeaponsInSlot = end - start; + double startingAngle = angle - 90 + (180.0 / nWeaponsInSlot / 2); + + uint place = 0; + uint selectedPlace = 0; + for (uint i = start; i < end; ++i, ++place) + { + if (i == viewModel.selectedIndex) + { + selectedPlace = place; + continue; + } + + displayWeapon( place + , i + , nWeaponsInSlot * 2 + , radius + , allowedWidth + , viewModel + , outerWheelCenter + , startingAngle + ); + } + + // draw the selected thing last in case of icon overlapping + displayWeapon( selectedPlace + , viewModel.selectedIndex + , nWeaponsInSlot * 2 + , radius + , allowedWidth + , viewModel + , outerWheelCenter + , startingAngle + ); + + int deadRadius = mScreen.getWheelDeadRadius(); + + drawHands( nWeaponsInSlot * 2 + , outerIndex + , outerWheelCenter + , -startingAngle + ); + } + + private + void drawInnerWheel() + { + int wheelDiameter = mScreen.getWheelRadius() * 2; + Screen.drawTexture( mTextureCache.circle + , NO_ANIMATION + , mCenter.x + , mCenter.y + //, DTA_FillColor , mBaseColor + , DTA_AlphaChannel , true + , DTA_Alpha , mAlpha + , DTA_CenterOffset , true + , DTA_DestWidth , wheelDiameter + , DTA_DestHeight , wheelDiameter + ); + } + + private + void drawOuterWheel(double x, double y, double angle) + { + int wheelDiameter = mScreen.getWheelRadius() * 2; + Screen.drawTexture( mTextureCache.halfCircle + , NO_ANIMATION + , x + , y + //, DTA_FillColor , mBaseColor + , DTA_AlphaChannel , true + , DTA_Alpha , mAlpha + , DTA_CenterOffset , true + , DTA_Rotate , angle + , DTA_DestWidth , wheelDiameter + , DTA_DestHeight , wheelDiameter + ); + } + + private + void displayWeapon( uint place + , uint iconIndex + , uint nPlaces + , int radius + , int allowedWidth + , gb_ViewModel viewModel + , vector2 center + , double startingAngle = 0.0 + ) const + { + // Code is adapted from GZDoom AltHud.DrawImageToBox. + + TextureID texture = viewModel.icons[iconIndex]; + vector2 textureSize = TexMan.getScaledSize(texture) * 2 * mScaleFactor; + + if (texture.isValid()) + { + textureSize.x *= viewModel.iconScaleXs[iconIndex]; + textureSize.y *= viewModel.iconScaleYs[iconIndex]; + } + + bool isTall = (textureSize.y * 1.2 > textureSize.x); + + double scale = isTall + ? ((allowedWidth < textureSize.y) ? allowedWidth / textureSize.y : 1.0) + : ((allowedWidth < textureSize.x) ? allowedWidth / textureSize.x : 1.0) + ; + + double angle = (startingAngle + itemAngle(nPlaces, place)) % 360; + vector2 xy = (sin(angle), -cos(angle)) * radius + center; + int width = int(textureSize.x * scale); + int height = int(textureSize.y * scale); + + drawIcon(texture, xy, width, height, angle, isTall); + drawAmmo(angle, center, viewModel, iconIndex); + } + + private + void drawIcon(TextureID texture, vector2 xy, int w, int h, double angle, bool isTall) const + { + bool flipX; + double scaleY; + + if (mIsRotating) + { + flipX = (angle > 180); + if (flipX) angle -= 180; + angle = -angle + 90; + + if (isTall) angle += flipX ? 90 : -90; + + scaleY = 1.0; + } + else + { + flipX = false; + angle = 0; + scaleY = mOptions.isPreservingAspectRatio() ? 1.2 : 1.0; + } + + if (!texture.isValid()) texture = mTextureCache.noIcon; + + Screen.drawTexture( texture + , NO_ANIMATION + , xy.x + , xy.y + , DTA_CenterOffset , true + , DTA_DestWidth , w + , DTA_DestHeight , h + , DTA_ScaleY , scaleY + , DTA_Alpha , mAlpha + , DTA_Rotate , angle + , DTA_FlipX , flipX + , DTA_KeepRatio, false + ); + + return; //no tint for you + //if (!mOptions.getWheelTint()) return; + + Screen.drawTexture( texture + , NO_ANIMATION + , xy.x + , xy.y + , DTA_CenterOffset , true + , DTA_DestWidth , w + , DTA_DestHeight , h + , DTA_ScaleY , scaleY + , DTA_Alpha , mAlpha * 0.3 + , DTA_FillColor , mBaseColor + , DTA_Rotate , angle + , DTA_FlipX , flipX + ); + + } + + private + void drawAmmoPip(double angle, double radius, vector2 center, bool colored) + { + vector2 xy = (sin(angle), -cos(angle)) * radius + center; + vector2 size = mTextureCache.ammoPipSize * mScaleFactor; + + if (colored) + { + Screen.drawTexture( mTextureCache.ammoPip + , NO_ANIMATION + , round(xy.x) + , round(xy.y) + , DTA_CenterOffset , true + , DTA_Alpha , mAlpha + , DTA_DestWidth , int(size.x) + , DTA_DestHeight , int(size.y) + , DTA_FillColor , FILLED_QUANTITY_COLOR + ); + } + else + { + Screen.drawTexture( mTextureCache.ammoPip + , NO_ANIMATION + , round(xy.x) + , round(xy.y) + , DTA_CenterOffset , true + , DTA_Alpha , mAlpha + , DTA_DestWidth , int(size.x) + , DTA_DestHeight , int(size.y) + ); + } + } + + private static + int, int makePipsNumbers(int items, int maxItems) + { + if (maxItems <= MAX_N_PIPS) return items, maxItems; + + int nColoredPips = int(ceil(MAX_N_PIPS * double(items) / maxItems)); + return nColoredPips, MAX_N_PIPS; + } + + private + void drawAmmo(double weaponAngle, vector2 center, gb_ViewModel viewModel, uint weaponIndex) + { + if (gb_Ammo.isValid(viewModel.quantity1[weaponIndex], viewModel.maxQuantity1[weaponIndex])) + { + int margin = int(10 * mScaleFactor); + int radius = mScreen.getScaledScreenHeight() / 4 - margin; + int nColoredPips, nTotalPips; + [nColoredPips, nTotalPips] = makePipsNumbers( viewModel.quantity1 [weaponIndex] + , viewModel.maxQuantity1[weaponIndex] + ); + drawQuantityPips(radius, weaponAngle, center, nColoredPips, nTotalPips); + } + + if (gb_Ammo.isValid(viewModel.quantity2[weaponIndex], viewModel.maxQuantity2[weaponIndex])) + { + int margin = int(20 * mScaleFactor); + int radius = mScreen.getScaledScreenHeight() / 4 - margin; + int nColoredPips, nTotalPips; + [nColoredPips, nTotalPips] = makePipsNumbers( viewModel.quantity2 [weaponIndex] + , viewModel.maxQuantity2[weaponIndex] + ); + drawQuantityPips(radius, weaponAngle, center, nColoredPips, nTotalPips); + } + } + + void drawQuantityPips( double radius + , double centerAngle + , vector2 center + , int nColoredPips + , int nTotalPips + ) + { + if (nTotalPips % 2 == 0) + { + int nTotalPipsHalved = nTotalPips / 2; + + for (int i = -nTotalPipsHalved + 1; i <= 0; ++i) + { + double angle = centerAngle - PIPS_GAP + i * PIPS_STEP; + drawAmmoPip(angle, radius, center, nColoredPips > 0); + --nColoredPips; + } + + for (int i = 0; i < nTotalPipsHalved; ++i) + { + double angle = centerAngle + PIPS_GAP + i * PIPS_STEP; + drawAmmoPip(angle, radius, center, nColoredPips > 0); + --nColoredPips; + } + } + else + { + double angleStart = centerAngle - (nTotalPips - 1) * PIPS_STEP / 2; + for (int i = 0; i < nTotalPips; ++i) + { + double angle = angleStart + i * PIPS_STEP; + drawAmmoPip(angle, radius, center, nColoredPips > 0); + --nColoredPips; + } + } + } + + private + void displaySlot(uint place, int slot, uint nPlaces, int radius) + { + double angle = itemAngle(nPlaces, place); + vector2 pos = (sin(angle), -cos(angle)) * radius + mCenter; + Font aFont = mFontSelector.getFont(); + + gb_Text.draw(string.format("%d", slot), pos, aFont, mAlpha, true,font.CR_SAPPHIRE); + } + + private static + double itemAngle(uint nItems, uint index) + { + return 360.0 / nItems * index; + } + + private + void drawHands(uint nPlaces, uint selectedIndex, vector2 center, double startAngle) + { + if (nPlaces < 2) return; + + double handsAngle = startAngle - itemAngle(nPlaces, selectedIndex); + double sectorAngleHalfWidth = max(6, 360.0 / 2.0 / nPlaces - 2); + double scaleFactor = mScreen.getScaleFactor(); + vector2 size = TexMan.getScaledSize(mTextureCache.hand) * scaleFactor; + + Screen.drawTexture( mTextureCache.hand + , NO_ANIMATION + , center.x + , center.y + , DTA_KeepRatio , true + , DTA_CenterOffset , true + , DTA_Alpha , mAlpha + , DTA_Rotate , handsAngle - sectorAngleHalfWidth + , DTA_FlipX , true + , DTA_DestWidthF , size.x + , DTA_DestHeightF , size.y + ); + + Screen.drawTexture( mTextureCache.hand + , NO_ANIMATION + , center.x + , center.y + , DTA_KeepRatio , true + , DTA_CenterOffset , true + , DTA_CenterOffset , true + , DTA_Alpha , mAlpha + , DTA_Rotate , handsAngle + sectorAngleHalfWidth + , DTA_DestWidthF , size.x + , DTA_DestHeightF , size.y + ); + } + + private + void drawWeaponDescription( gb_ViewModel viewModel + , int innerIndex + , int nPlaces + , bool isSlotExpanded + ) + { + int index = viewModel.selectedIndex; + string description = viewModel.tags[index]; + if (description.length() == 0) return; + + string ammo1 = gb_Ammo.isValid(viewModel.quantity1[index], viewModel.maxQuantity1[index]) + ? string.format("%d/%d", viewModel.quantity1[index], viewModel.maxQuantity1[index]) + : ""; + string ammo2 = gb_Ammo.isValid(viewModel.quantity2[index], viewModel.maxQuantity2[index]) + ? string.format("%d/%d", viewModel.quantity2[index], viewModel.maxQuantity2[index]) + : ""; + + double angle = itemAngle(nPlaces, innerIndex); + bool isOnTop = isSlotExpanded && (90.0 < angle && angle < 270.0); + vector2 pos = mCenter; + pos.y += mScreen.getWheelRadius() * (isOnTop ? -1 : 1); + + mText.drawBox(ammo1, description, ammo2, pos, !isOnTop, mBaseColor, mAlpha,font.CR_LIGHTBLUE,true); + } + + private + void drawPointer(double angle, double radius) + { + vector2 pos = (sin(angle), -cos(angle)) * radius + mCenter; + vector2 size = TexMan.getScaledSize(mTextureCache.pointer); + size *= mScaleFactor; + + Screen.drawTexture( mTextureCache.pointer + , NO_ANIMATION + , pos.x + , pos.y + , DTA_CenterOffset , true + , DTA_Alpha , mAlpha + , DTA_DestWidth , int(size.x) + , DTA_DestHeight , int(size.y) + , DTA_TranslationIndex, Translation.GetID('reddenwep') + ); + } + + const NO_ANIMATION = 0; // == false + + const MARGIN = 4; + + const UNDEFINED_INDEX = -1; + + const MAX_N_PIPS = 10; + + const PIPS_GAP = 1.2; + const PIPS_STEP = 1.5; + + const FILLED_QUANTITY_COLOR = 0x22DD22; + + private double mAlpha; + private color mBaseColor; + private vector2 mCenter; + + private gb_Screen mScreen; + private gb_Options mOptions; + private gb_FontSelector mFontSelector; + private gb_MultiWheelMode mMultiWheelMode; + + private gb_Text mText; + + private bool mIsRotating; + +// cache /////////////////////////////////////////////////////////////////////////////////////////// + + private gb_TextureCache mTextureCache; + private double mScaleFactor; + +} // class gb_WheelView diff --git a/zscript/m_gizmos/previous_weapon.zs b/zscript/m_gizmos/previous_weapon.zs new file mode 100644 index 0000000000..1f7a7780bc --- /dev/null +++ b/zscript/m_gizmos/previous_weapon.zs @@ -0,0 +1,119 @@ +/* Copyright Alexander 'm8f' Kromm (mmaulwurff@gmail.com) 2021 + * + * This file is a part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +/** + * To save data into savegame. + */ +class gb_PreviousWeaponStorage : EventHandler +{ + + Weapon mCurrentWeapon; + Weapon mPreviousWeapon; + bool mIsHolstered; + +} // class gb_PreviousWeaponEventHandlerStorage + +/** + * Main event handler is static so data is carried over to next level. + */ +class gb_PreviousWeaponEventHandler : StaticEventHandler +{ + +// public: // EventHandler ///////////////////////////////////////////////////////////////////////// + + override + void playerEntered(PlayerEvent event) + { + mStorage = gb_PreviousWeaponStorage(EventHandler.find("gb_PreviousWeaponStorage")); + if (event.playerNumber == consolePlayer) updatePreviousWeapon(); + } + + override + void worldLoaded(WorldEvent event) + { + if (!event.isSaveGame) return; + + mStorage = gb_PreviousWeaponStorage(EventHandler.find("gb_PreviousWeaponStorage")); + + if (mStorage == NULL) return; + + mCurrentWeapon = mStorage.mCurrentWeapon; + mPreviousWeapon = mStorage.mPreviousWeapon; + mIsHolstered = mStorage.mIsHolstered; + } + + override + void worldTick() + { + if (mStorage == NULL) return; + + updatePreviousWeapon(); + + mStorage.mCurrentWeapon = mCurrentWeapon; + mStorage.mPreviousWeapon = mPreviousWeapon; + mStorage.mIsHolstered = mIsHolstered; + } + + override + void networkProcess(ConsoleEvent event) + { + if (mStorage == NULL) return; + + if (event.name == "gb_prev_weapon" + && mPreviousWeapon != NULL + && mPreviousWeapon != players[event.player].ReadyWeapon) + { + players[event.player].PendingWeapon = mPreviousWeapon; + } + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private + void updatePreviousWeapon() + { + Weapon ready = players[consolePlayer].readyWeapon; + + if (ready == NULL || ready.getClassName() == "mg_Holstered") + { + if (!mIsHolstered) + { + // swap + let tmp = mCurrentWeapon; + mCurrentWeapon = mPreviousWeapon; + mPreviousWeapon = tmp; + } + mIsHolstered = true; + return; + } + + mIsHolstered = false; + + if (mCurrentWeapon != ready) + { + mPreviousWeapon = mCurrentWeapon; + } + + mCurrentWeapon = ready; + } + + private gb_PreviousWeaponStorage mStorage; + private Weapon mCurrentWeapon; + private Weapon mPreviousWeapon; + private bool mIsHolstered; + +} // class gb_PreviousWeaponEventHandler diff --git a/zscript/zabor/event_handler.zs b/zscript/zabor/event_handler.zs new file mode 100644 index 0000000000..0112c72096 --- /dev/null +++ b/zscript/zabor/event_handler.zs @@ -0,0 +1,326 @@ +/* Copyright Alexander Kromm (mmaulwurff@gmail.com) 2021 + * + * This file is part of Gearbox. + * + * Gearbox is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Gearbox is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Gearbox. If not, see . + */ + +class gb_VmAbortHandler : EventHandler +{ + + override + void playerSpawned(PlayerEvent event) + { + if (event.playerNumber != consolePlayer) return; + + mPlayerClassName = players[consolePlayer].mo.getClassName(); + mSkillName = g_SkillName(); + } + + override + void uiTick() + { + if (level.totalTime % 35 == 0) rememberSystemTime(SystemTime.now()); + } + + override + void onDestroy() + { + if (gameState != GS_FullConsole + || !Cvar.getCvar("gb_zabor_enabled", players[consolePlayer]).getBool()) + { + return; + } + + + if (!amIFirst()) + { + printGearboxCvars(); + return; + } + + printInfo(); + printAttention(); + } + + override + void consoleProcess(ConsoleEvent event) + { + if (event.name != "zabor") return; + + if (!amIFirst()) + { + printGearboxCvars(); + return; + } + + printInfo(); + } + +// private: //////////////////////////////////////////////////////////////////////////////////////// + + private clearscope + void printInfo() + { + printZabor(getGameInfo(), getConfiguration(), getRealTime()); + printGearboxCvars(); + printEventHandlers(); + } + + private static clearscope + void printGearboxCvars() + { + Array gearboxConfiguration = + { + intCvarToString ("gb_scale" ), + colorCvarToString ("gb_color" ), + colorCvarToString ("gb_dim_color" ), + intCvarToString ("gb_show_tags" ), + + intCvarToString ("gb_view_type" ), + + intCvarToString ("gb_enable_dim" ), + intCvarToString ("gb_enable_blur" ), + floatCvarToString ("gb_wheel_position" ), + floatCvarToString ("gb_wheel_scale" ), + intCvarToString ("gb_wheel_tint" ), + intCvarToString ("gb_multiwheel_limit" ), + + floatCvarToString ("gb_blocks_position_x" ), + floatCvarToString ("gb_blocks_position_y" ), + + intCvarToString ("gb_text_scale" ), + floatCvarToString ("gb_text_position_x" ), + floatCvarToString ("gb_text_position_y" ), + floatCvarToString ("gb_text_position_y_max" ), + intCvarToString ("gb_text_usual_color" ), + intCvarToString ("gb_text_selected_color" ), + + stringCvarToString("gb_font" ), + + intCvarToString ("gb_open_on_scroll" ), + intCvarToString ("gb_open_on_slot" ), + intCvarToString ("gb_reverse_slot_cycle_order"), + intCvarToString ("gb_select_first_slot_weapon"), + intCvarToString ("gb_mouse_in_wheel" ), + intCvarToString ("gb_select_on_key_up" ), + intCvarToString ("gb_no_menu_if_one" ), + intCvarToString ("gb_on_automap" ), + intCvarToString ("gb_lock_positions" ), + intCvarToString ("gb_enable_sounds" ), + intCvarToString ("gb_frozen_can_open" ), + + intCvarToString ("gb_time_freeze" ), + + floatCvarToString ("gb_mouse_sensitivity_x" ), + floatCvarToString ("gb_mouse_sensitivity_y" ) + }; + + // cut prefix: + uint nCvars = gearboxConfiguration.size(); + for (uint i = 0; i < nCvars; ++i) + { + string value = gearboxConfiguration[i]; + if (value.left(3) == "gb_") + { + gearboxConfiguration[i] = value.mid(3); + } + } + + string report = "\cigb_*\c-: " .. join(gearboxConfiguration, ", "); + + Console.printf("%s", report); + } + + private static clearscope + string intCvarToString(string cvarName) + { + let aCvar = Cvar.getCvar(cvarName, players[consolePlayer]); + return aCvar ? string.format("%s:\cu%d\c-", cvarName, aCvar.getInt()) : ""; + } + + private static clearscope + string floatCvarToString(string cvarName) + { + let aCvar = Cvar.getCvar(cvarName, players[consolePlayer]); + return aCvar ? string.format("%s:\cu%f\c-", cvarName, aCvar.getFloat()) : ""; + } + + private static clearscope + string colorCvarToString(string cvarName) + { + let aCvar = Cvar.getCvar(cvarName, players[consolePlayer]); + return aCvar ? string.format("%s:\cu0x%x\c-", cvarName, aCvar.getInt()) : ""; + } + + private static clearscope + string stringCvarToString(string cvarName) + { + let aCvar = Cvar.getCvar(cvarName, players[consolePlayer]); + return aCvar ? string.format("%s:\cu%s\c-", cvarName, aCvar.getString()) : ""; + } + + private static clearscope + string getConfiguration() + { + Array configuration = + { + intCvarToString("compatflags"), + intCvarToString("compatflags2"), + intCvarToString("dmflags"), + intCvarToString("dmflags2"), + floatCvarToString("autoaim") + }; + + return string.format("%s", join(configuration, ", ")); + } + + private clearscope + void printAttention() + { + string userName = players[consolePlayer].getUserName(); + string message1 = string.format( " # %s\cg, please report this VM abort to mod author." + , userName + ); + string message2 = " # Attach screenshot to the report."; + string message3 = " # Type \"screenshot\" below to take a screenshot."; + + Array tokens; + userName.split(tokens, "\c"); + int colorCharsCount = (tokens.size() - 1) * 3; + int length = max(max(message1.length() - colorCharsCount, message2.length()), message3.length()); + + message1 = fillBox(message1, length); + message2 = fillBox(message2, length); + message3 = fillBox(message3, length); + + string hashes; + for (int i = 0; i < length; ++i) + { + hashes = hashes .. "#"; + } + Console.printf("\n\cg %s\n%s\n%s\n%s\n %s\n", hashes, message1, message2, message3, hashes); + } + + private static clearscope + string fillBox(string result, int length) + { + for (int i = result.length(); i < length; ++i) result.appendFormat(" "); + result.appendFormat(" #"); + return result; + } + + private static clearscope + void printZabor(string s1 = "", string s2 = "", string s3 = "") + { + Console.printf( + "\ci __ __ __ __ __ __\n" + "\ci/ \\/ \\/ \\/ \\/ \\/ \\\n" + "\ci|Za||bo||r ||v1||.1||.0| \c-%s\n" + "\ci|..||..||..||..||..||..| \c-%s\n" + "\ci|__||__||__||__||__||__| \c-%s\n" + , s1, s2, s3 + ); + } + + private clearscope + bool amIFirst() + { + uint nClasses = AllClasses.size(); + for (uint i = 0; i < nClasses; ++i) + { + class aClass = AllClasses[i]; + string className = aClass.getClassName(); + bool isVmAbortHandler = (className.indexOf("VmAbortHandler") != -1); + if (!isVmAbortHandler) continue; + + return aClass.getClassName() == getClassName(); + } + return false; + } + + private clearscope + string getGameInfo() + { + return string.format( "map:\cu%s\c-, time:\cu%d\c-, multiplayer:\cu%d\c-, " + "class:\cu%s\c-, skill:\cu%s\c-" + , level.mapName + , level.totalTime + , multiplayer + , mPlayerClassName + , mSkillName + ); + } + + private static clearscope + void printEventHandlers() + { + Array eventHandlers; + + uint nClasses = AllClasses.size(); + for (uint i = 0; i < nClasses; ++i) + { + class aClass = AllClasses[i]; + + if ( aClass is "StaticEventHandler" + && aClass != "StaticEventHandler" + && aClass != "EventHandler" + ) + { + eventHandlers.push(aClass.getClassName()); + } + } + + Console.printf("\cievent handlers\c-: %s", join(eventHandlers, ", ")); + } + + private clearscope + string getRealTime() + { + return string.format("system time: \cu%s", SystemTime.format("%F %T %Z", mSystemTime)); + } + + private static clearscope + string join(Array strings, string delimiter) + { + string result; + + uint nStrings = strings.size(); + for (uint i = 0; i < nStrings; ++i) + { + if (strings[i].length() == 0) continue; + + if (result.length() == 0) + { + result = strings[i]; + } + else + { + result.appendFormat("%s%s", delimiter, strings[i]); + } + } + + return result; + } + + private play + void rememberSystemTime(int value) const + { + mSystemTime = value; + } + + private string mPlayerClassName; + private string mSkillName; + private int mSystemTime; + +} // class gb_VmAbortHandler