| 1 | #ifndef GAME_CLIENT_COMPONENTS_TOUCH_CONTROLS_H |
| 2 | #define GAME_CLIENT_COMPONENTS_TOUCH_CONTROLS_H |
| 3 | |
| 4 | #include <base/color.h> |
| 5 | #include <base/vmath.h> |
| 6 | |
| 7 | #include <engine/input.h> |
| 8 | |
| 9 | #include <game/client/component.h> |
| 10 | #include <game/client/ui_rect.h> |
| 11 | |
| 12 | #include <array> |
| 13 | #include <chrono> |
| 14 | #include <functional> |
| 15 | #include <memory> |
| 16 | #include <optional> |
| 17 | #include <string> |
| 18 | #include <vector> |
| 19 | |
| 20 | class CJsonWriter; |
| 21 | typedef struct _json_value json_value; |
| 22 | |
| 23 | class CTouchControls : public CComponent |
| 24 | { |
| 25 | public: |
| 26 | static constexpr const int BUTTON_SIZE_SCALE = 1000000; |
| 27 | static constexpr const int BUTTON_SIZE_MINIMUM = 50000; |
| 28 | static constexpr const int BUTTON_SIZE_MAXIMUM = 500000; |
| 29 | enum class EDirectTouchIngameMode |
| 30 | { |
| 31 | DISABLED, |
| 32 | ACTION, |
| 33 | AIM, |
| 34 | FIRE, |
| 35 | HOOK, |
| 36 | NUM_STATES |
| 37 | }; |
| 38 | enum class EDirectTouchSpectateMode |
| 39 | { |
| 40 | DISABLED, |
| 41 | AIM, |
| 42 | NUM_STATES |
| 43 | }; |
| 44 | |
| 45 | int Sizeof() const override { return sizeof(*this); } |
| 46 | void OnInit() override; |
| 47 | void OnReset() override; |
| 48 | void OnWindowResize() override; |
| 49 | bool OnTouchState(const std::vector<IInput::CTouchFingerState> &vTouchFingerStates) override; |
| 50 | void OnRender() override; |
| 51 | |
| 52 | bool LoadConfigurationFromFile(int StorageType); |
| 53 | bool LoadConfigurationFromClipboard(); |
| 54 | bool SaveConfigurationToFile(); |
| 55 | void SaveConfigurationToClipboard(); |
| 56 | |
| 57 | EDirectTouchIngameMode DirectTouchIngame() const { return m_DirectTouchIngame; } |
| 58 | void SetDirectTouchIngame(EDirectTouchIngameMode DirectTouchIngame) |
| 59 | { |
| 60 | m_DirectTouchIngame = DirectTouchIngame; |
| 61 | m_EditingChanges = true; |
| 62 | } |
| 63 | EDirectTouchSpectateMode DirectTouchSpectate() const { return m_DirectTouchSpectate; } |
| 64 | void SetDirectTouchSpectate(EDirectTouchSpectateMode DirectTouchSpectate) |
| 65 | { |
| 66 | m_DirectTouchSpectate = DirectTouchSpectate; |
| 67 | m_EditingChanges = true; |
| 68 | } |
| 69 | bool IsEditingActive() const { return m_EditingActive; } |
| 70 | void SetEditingActive(bool EditingActive) { m_EditingActive = EditingActive; } |
| 71 | bool HasEditingChanges() const { return m_EditingChanges; } |
| 72 | void SetEditingChanges(bool EditingChanges) { m_EditingChanges = EditingChanges; } |
| 73 | |
| 74 | class CUnitRect |
| 75 | { |
| 76 | public: |
| 77 | int m_X; |
| 78 | int m_Y; |
| 79 | int m_W; |
| 80 | int m_H; |
| 81 | float Distance(const CUnitRect &Other) const; |
| 82 | bool IsOverlap(const CUnitRect &Other) const |
| 83 | { |
| 84 | return (m_X < Other.m_X + Other.m_W) && (m_X + m_W > Other.m_X) && (m_Y < Other.m_Y + Other.m_H) && (m_Y + m_H > Other.m_Y); |
| 85 | } |
| 86 | bool operator==(const CUnitRect &Other) const |
| 87 | { |
| 88 | return m_X == Other.m_X && m_Y == Other.m_Y && m_W == Other.m_W && m_H == Other.m_H; |
| 89 | } |
| 90 | }; |
| 91 | |
| 92 | enum class EButtonVisibility |
| 93 | { |
| 94 | INGAME, |
| 95 | ZOOM_ALLOWED, |
| 96 | VOTE_ACTIVE, |
| 97 | DUMMY_ALLOWED, |
| 98 | DUMMY_CONNECTED, |
| 99 | RCON_AUTHED, |
| 100 | DEMO_PLAYER, |
| 101 | , |
| 102 | , |
| 103 | , |
| 104 | , |
| 105 | , |
| 106 | NUM_VISIBILITIES |
| 107 | }; |
| 108 | static const constexpr int = (int)EButtonVisibility::EXTRA_MENU_5 - (int)EButtonVisibility::EXTRA_MENU_1 + 1; |
| 109 | |
| 110 | enum class EButtonShape |
| 111 | { |
| 112 | RECT, |
| 113 | CIRCLE, |
| 114 | NUM_SHAPES |
| 115 | }; |
| 116 | |
| 117 | class CButtonLabel |
| 118 | { |
| 119 | public: |
| 120 | enum class EType |
| 121 | { |
| 122 | /** |
| 123 | * Label is used as is. |
| 124 | */ |
| 125 | PLAIN, |
| 126 | /** |
| 127 | * Label is localized. Only usable for default button labels for which there must be |
| 128 | * corresponding `Localizable`-calls in code and string in the translation files. |
| 129 | */ |
| 130 | LOCALIZED, |
| 131 | /** |
| 132 | * Icon font is used for the label. |
| 133 | */ |
| 134 | ICON, |
| 135 | /** |
| 136 | * Number of label types. |
| 137 | */ |
| 138 | NUM_TYPES |
| 139 | }; |
| 140 | |
| 141 | EType m_Type; |
| 142 | const char *m_pLabel; |
| 143 | }; |
| 144 | |
| 145 | private: |
| 146 | static constexpr const char *const DIRECT_TOUCH_INGAME_MODE_NAMES[(int)EDirectTouchIngameMode::NUM_STATES] = {"disabled" , "action" , "aim" , "fire" , "hook" }; |
| 147 | static constexpr const char *const DIRECT_TOUCH_SPECTATE_MODE_NAMES[(int)EDirectTouchSpectateMode::NUM_STATES] = {"disabled" , "aim" }; |
| 148 | static constexpr const char *const SHAPE_NAMES[(int)EButtonShape::NUM_SHAPES] = {"rect" , "circle" }; |
| 149 | |
| 150 | class CButtonVisibility |
| 151 | { |
| 152 | public: |
| 153 | EButtonVisibility m_Type; |
| 154 | bool m_Parity; |
| 155 | |
| 156 | CButtonVisibility(EButtonVisibility Type, bool Parity) : |
| 157 | m_Type(Type), m_Parity(Parity) {} |
| 158 | }; |
| 159 | |
| 160 | class CButtonVisibilityData |
| 161 | { |
| 162 | public: |
| 163 | const char *m_pId; |
| 164 | std::function<bool()> m_Function; |
| 165 | }; |
| 166 | |
| 167 | CButtonVisibilityData m_aVisibilityFunctions[(int)EButtonVisibility::NUM_VISIBILITIES]; |
| 168 | |
| 169 | enum |
| 170 | { |
| 171 | ACTION_AIM, |
| 172 | ACTION_FIRE, |
| 173 | ACTION_HOOK, |
| 174 | NUM_ACTIONS |
| 175 | }; |
| 176 | |
| 177 | static constexpr const char *const LABEL_TYPE_NAMES[(int)CButtonLabel::EType::NUM_TYPES] = {"plain" , "localized" , "icon" }; |
| 178 | |
| 179 | public: |
| 180 | class CTouchButtonBehavior; |
| 181 | |
| 182 | class CTouchButton |
| 183 | { |
| 184 | public: |
| 185 | CTouchButton(CTouchControls *pTouchControls); |
| 186 | CTouchButton(CTouchButton &&Other) noexcept; |
| 187 | CTouchButton(const CTouchButton &Other) = delete; |
| 188 | |
| 189 | CTouchButton &operator=(const CTouchButton &Other) = delete; |
| 190 | CTouchButton &operator=(CTouchButton &&Other) noexcept; |
| 191 | |
| 192 | CTouchControls *m_pTouchControls; |
| 193 | |
| 194 | CUnitRect m_UnitRect; // {0,0,BUTTON_SIZE_MINIMUM,BUTTON_SIZE_MINIMUM} = default |
| 195 | CUIRect m_ScreenRect; |
| 196 | |
| 197 | EButtonShape m_Shape; // Rect = default |
| 198 | int m_BackgroundCorners; // only used with EButtonShape::RECT |
| 199 | |
| 200 | std::vector<CButtonVisibility> m_vVisibilities; |
| 201 | std::unique_ptr<CTouchButtonBehavior> m_pBehavior; // nullptr = default. In button editor the default is bind behavior with nothing. |
| 202 | |
| 203 | bool m_VisibilityCached; |
| 204 | std::chrono::nanoseconds m_VisibilityStartTime; |
| 205 | |
| 206 | void UpdatePointers(); |
| 207 | void UpdateScreenFromUnitRect(); |
| 208 | void UpdateBackgroundCorners(); |
| 209 | |
| 210 | vec2 ClampTouchPosition(vec2 TouchPosition) const; |
| 211 | bool IsInside(vec2 TouchPosition) const; |
| 212 | void UpdateVisibilityGame(); |
| 213 | void UpdateVisibilityEditor(); |
| 214 | bool IsVisible() const; |
| 215 | // Force using Selected for button colors, Rect for rendering rects. |
| 216 | void Render(std::optional<bool> Selected = std::nullopt, std::optional<CUnitRect> Rect = std::nullopt) const; |
| 217 | void WriteToConfiguration(CJsonWriter *pWriter); |
| 218 | }; |
| 219 | |
| 220 | class CTouchButtonBehavior |
| 221 | { |
| 222 | public: |
| 223 | CTouchButton *m_pTouchButton; |
| 224 | CTouchControls *m_pTouchControls; |
| 225 | |
| 226 | bool m_Active; // variables below must only be used when active |
| 227 | IInput::CTouchFinger m_Finger; |
| 228 | vec2 m_ActivePosition; |
| 229 | vec2 m_AccumulatedDelta; |
| 230 | std::chrono::nanoseconds m_ActivationStartTime; |
| 231 | |
| 232 | virtual ~CTouchButtonBehavior() = default; |
| 233 | virtual void Init(CTouchButton *pTouchButton); |
| 234 | |
| 235 | void Reset(); |
| 236 | void SetActive(const IInput::CTouchFingerState &FingerState); |
| 237 | void SetInactive(bool ByFinger); |
| 238 | bool IsActive() const; |
| 239 | bool IsActive(const IInput::CTouchFinger &Finger) const; |
| 240 | |
| 241 | virtual CButtonLabel GetLabel() const = 0; |
| 242 | virtual void OnActivate() {} |
| 243 | virtual void OnDeactivate(bool ByFinger) {} |
| 244 | virtual void OnUpdate() {} |
| 245 | virtual void WriteToConfiguration(CJsonWriter *pWriter) = 0; |
| 246 | virtual const char *GetBehaviorType() const = 0; |
| 247 | }; |
| 248 | |
| 249 | /** |
| 250 | * Abstract class for predefined behaviors. |
| 251 | * |
| 252 | * Subclasses must implemented the concrete behavior and provide the label. |
| 253 | */ |
| 254 | class CPredefinedTouchButtonBehavior : public CTouchButtonBehavior |
| 255 | { |
| 256 | public: |
| 257 | static constexpr const char *const BEHAVIOR_TYPE = "predefined" ; |
| 258 | |
| 259 | CPredefinedTouchButtonBehavior(const char *pId) : |
| 260 | m_pId(pId) {} |
| 261 | |
| 262 | /** |
| 263 | * Implements the serialization for predefined behaviors. Subclasses |
| 264 | * may override this, but they should call the parent function first. |
| 265 | */ |
| 266 | void WriteToConfiguration(CJsonWriter *pWriter) override; |
| 267 | const char *GetBehaviorType() const override { return BEHAVIOR_TYPE; } |
| 268 | const char *GetPredefinedType() { return m_pId; } |
| 269 | |
| 270 | private: |
| 271 | const char *m_pId; |
| 272 | }; |
| 273 | |
| 274 | class : public CPredefinedTouchButtonBehavior |
| 275 | { |
| 276 | public: |
| 277 | static constexpr const char *const = "ingame-menu" ; |
| 278 | |
| 279 | () : |
| 280 | CPredefinedTouchButtonBehavior(BEHAVIOR_ID) {} |
| 281 | |
| 282 | CButtonLabel () const override; |
| 283 | void (bool ByFinger) override; |
| 284 | }; |
| 285 | |
| 286 | class : public CPredefinedTouchButtonBehavior |
| 287 | { |
| 288 | public: |
| 289 | static constexpr const char *const = "extra-menu" ; |
| 290 | |
| 291 | (int Number); |
| 292 | |
| 293 | CButtonLabel () const override; |
| 294 | void (bool ByFinger) override; |
| 295 | int () const { return m_Number; } |
| 296 | void (CJsonWriter *pWriter) override; |
| 297 | |
| 298 | private: |
| 299 | int ; |
| 300 | char [16]; |
| 301 | }; |
| 302 | |
| 303 | class CEmoticonTouchButtonBehavior : public CPredefinedTouchButtonBehavior |
| 304 | { |
| 305 | public: |
| 306 | static constexpr const char *const BEHAVIOR_ID = "emoticon" ; |
| 307 | |
| 308 | CEmoticonTouchButtonBehavior() : |
| 309 | CPredefinedTouchButtonBehavior(BEHAVIOR_ID) {} |
| 310 | |
| 311 | CButtonLabel GetLabel() const override; |
| 312 | void OnDeactivate(bool ByFinger) override; |
| 313 | }; |
| 314 | |
| 315 | class CSpectateTouchButtonBehavior : public CPredefinedTouchButtonBehavior |
| 316 | { |
| 317 | public: |
| 318 | static constexpr const char *const BEHAVIOR_ID = "spectate" ; |
| 319 | |
| 320 | CSpectateTouchButtonBehavior() : |
| 321 | CPredefinedTouchButtonBehavior(BEHAVIOR_ID) {} |
| 322 | |
| 323 | CButtonLabel GetLabel() const override; |
| 324 | void OnDeactivate(bool ByFinger) override; |
| 325 | }; |
| 326 | |
| 327 | class CSwapActionTouchButtonBehavior : public CPredefinedTouchButtonBehavior |
| 328 | { |
| 329 | public: |
| 330 | static constexpr const char *const BEHAVIOR_ID = "swap-action" ; |
| 331 | |
| 332 | CSwapActionTouchButtonBehavior() : |
| 333 | CPredefinedTouchButtonBehavior(BEHAVIOR_ID) {} |
| 334 | |
| 335 | CButtonLabel GetLabel() const override; |
| 336 | void OnActivate() override; |
| 337 | void OnDeactivate(bool ByFinger) override; |
| 338 | |
| 339 | private: |
| 340 | int m_ActiveAction = NUM_ACTIONS; |
| 341 | }; |
| 342 | |
| 343 | class CUseActionTouchButtonBehavior : public CPredefinedTouchButtonBehavior |
| 344 | { |
| 345 | public: |
| 346 | static constexpr const char *const BEHAVIOR_ID = "use-action" ; |
| 347 | |
| 348 | CUseActionTouchButtonBehavior() : |
| 349 | CPredefinedTouchButtonBehavior(BEHAVIOR_ID) {} |
| 350 | |
| 351 | CButtonLabel GetLabel() const override; |
| 352 | void OnActivate() override; |
| 353 | void OnDeactivate(bool ByFinger) override; |
| 354 | |
| 355 | private: |
| 356 | int m_ActiveAction = NUM_ACTIONS; |
| 357 | }; |
| 358 | |
| 359 | class CJoystickTouchButtonBehavior : public CPredefinedTouchButtonBehavior |
| 360 | { |
| 361 | public: |
| 362 | CJoystickTouchButtonBehavior(const char *pId) : |
| 363 | CPredefinedTouchButtonBehavior(pId) {} |
| 364 | |
| 365 | CButtonLabel GetLabel() const override; |
| 366 | void OnActivate() override; |
| 367 | void OnDeactivate(bool ByFinger) override; |
| 368 | void OnUpdate() override; |
| 369 | int ActiveAction() const { return m_ActiveAction; } |
| 370 | virtual int SelectedAction() const = 0; |
| 371 | |
| 372 | private: |
| 373 | int m_ActiveAction = NUM_ACTIONS; |
| 374 | }; |
| 375 | |
| 376 | class CJoystickActionTouchButtonBehavior : public CJoystickTouchButtonBehavior |
| 377 | { |
| 378 | public: |
| 379 | static constexpr const char *const BEHAVIOR_ID = "joystick-action" ; |
| 380 | |
| 381 | CJoystickActionTouchButtonBehavior() : |
| 382 | CJoystickTouchButtonBehavior(BEHAVIOR_ID) {} |
| 383 | |
| 384 | void Init(CTouchButton *pTouchButton) override; |
| 385 | int SelectedAction() const override; |
| 386 | }; |
| 387 | |
| 388 | class CJoystickAimTouchButtonBehavior : public CJoystickTouchButtonBehavior |
| 389 | { |
| 390 | public: |
| 391 | static constexpr const char *const BEHAVIOR_ID = "joystick-aim" ; |
| 392 | |
| 393 | CJoystickAimTouchButtonBehavior() : |
| 394 | CJoystickTouchButtonBehavior(BEHAVIOR_ID) {} |
| 395 | |
| 396 | int SelectedAction() const override; |
| 397 | }; |
| 398 | |
| 399 | class CJoystickFireTouchButtonBehavior : public CJoystickTouchButtonBehavior |
| 400 | { |
| 401 | public: |
| 402 | static constexpr const char *const BEHAVIOR_ID = "joystick-fire" ; |
| 403 | |
| 404 | CJoystickFireTouchButtonBehavior() : |
| 405 | CJoystickTouchButtonBehavior(BEHAVIOR_ID) {} |
| 406 | |
| 407 | int SelectedAction() const override; |
| 408 | }; |
| 409 | |
| 410 | class CJoystickHookTouchButtonBehavior : public CJoystickTouchButtonBehavior |
| 411 | { |
| 412 | public: |
| 413 | static constexpr const char *const BEHAVIOR_ID = "joystick-hook" ; |
| 414 | |
| 415 | CJoystickHookTouchButtonBehavior() : |
| 416 | CJoystickTouchButtonBehavior(BEHAVIOR_ID) {} |
| 417 | |
| 418 | int SelectedAction() const override; |
| 419 | }; |
| 420 | |
| 421 | /** |
| 422 | * Generic behavior implementation that executes a console command like a bind. |
| 423 | */ |
| 424 | class CBindTouchButtonBehavior : public CTouchButtonBehavior |
| 425 | { |
| 426 | public: |
| 427 | static constexpr const char *const BEHAVIOR_TYPE = "bind" ; |
| 428 | |
| 429 | CBindTouchButtonBehavior(const char *pLabel, CButtonLabel::EType LabelType, const char *pCommand) : |
| 430 | m_Label(pLabel), |
| 431 | m_LabelType(LabelType), |
| 432 | m_Command(pCommand) {} |
| 433 | |
| 434 | CButtonLabel GetLabel() const override; |
| 435 | const char *GetCommand() const { return m_Command.c_str(); } |
| 436 | void OnActivate() override; |
| 437 | void OnDeactivate(bool ByFinger) override; |
| 438 | void OnUpdate() override; |
| 439 | void WriteToConfiguration(CJsonWriter *pWriter) override; |
| 440 | const char *GetBehaviorType() const override { return BEHAVIOR_TYPE; } |
| 441 | |
| 442 | private: |
| 443 | std::string m_Label; |
| 444 | CButtonLabel::EType m_LabelType; |
| 445 | std::string m_Command; |
| 446 | |
| 447 | bool m_Repeating = false; |
| 448 | std::chrono::nanoseconds m_LastUpdateTime; |
| 449 | std::chrono::nanoseconds m_AccumulatedRepeatingTime; |
| 450 | }; |
| 451 | |
| 452 | /** |
| 453 | * Generic behavior implementation that switches between executing one of two or more console commands. |
| 454 | */ |
| 455 | class CBindToggleTouchButtonBehavior : public CTouchButtonBehavior |
| 456 | { |
| 457 | public: |
| 458 | static constexpr const char *const BEHAVIOR_TYPE = "bind-toggle" ; |
| 459 | |
| 460 | class CCommand |
| 461 | { |
| 462 | public: |
| 463 | std::string m_Label; |
| 464 | CButtonLabel::EType m_LabelType; |
| 465 | std::string m_Command; |
| 466 | |
| 467 | CCommand(const char *pLabel, CButtonLabel::EType LabelType, const char *pCommand) : |
| 468 | m_Label(pLabel), |
| 469 | m_LabelType(LabelType), |
| 470 | m_Command(pCommand) {} |
| 471 | CCommand() : |
| 472 | m_LabelType(CButtonLabel::EType::PLAIN) {} |
| 473 | }; |
| 474 | |
| 475 | CBindToggleTouchButtonBehavior(std::vector<CCommand> &&vCommands) : |
| 476 | m_vCommands(std::move(vCommands)) {} |
| 477 | |
| 478 | CButtonLabel GetLabel() const override; |
| 479 | std::vector<CCommand> GetCommand() const { return m_vCommands; } |
| 480 | size_t GetActiveCommandIndex() const { return m_ActiveCommandIndex; } |
| 481 | void OnActivate() override; |
| 482 | void WriteToConfiguration(CJsonWriter *pWriter) override; |
| 483 | const char *GetBehaviorType() const override { return BEHAVIOR_TYPE; } |
| 484 | |
| 485 | private: |
| 486 | std::vector<CCommand> m_vCommands; |
| 487 | size_t m_ActiveCommandIndex = 0; |
| 488 | }; |
| 489 | |
| 490 | private: |
| 491 | /** |
| 492 | * Mode of direct touch input while ingame. |
| 493 | * |
| 494 | * Saved to the touch controls configuration. |
| 495 | */ |
| 496 | EDirectTouchIngameMode m_DirectTouchIngame = EDirectTouchIngameMode::ACTION; |
| 497 | |
| 498 | /** |
| 499 | * Mode of direct touch input while spectating. |
| 500 | * |
| 501 | * Saved to the touch controls configuration. |
| 502 | */ |
| 503 | EDirectTouchSpectateMode m_DirectTouchSpectate = EDirectTouchSpectateMode::AIM; |
| 504 | |
| 505 | /** |
| 506 | * Background color of inactive touch buttons. |
| 507 | * |
| 508 | * Saved to the touch controls configuration. |
| 509 | */ |
| 510 | ColorRGBA m_BackgroundColorInactive = ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f); |
| 511 | |
| 512 | /** |
| 513 | * Background color of active touch buttons. |
| 514 | * |
| 515 | * Saved to the touch controls configuration. |
| 516 | */ |
| 517 | ColorRGBA m_BackgroundColorActive = ColorRGBA(0.2f, 0.2f, 0.2f, 0.25f); |
| 518 | |
| 519 | /** |
| 520 | * All touch buttons. |
| 521 | * |
| 522 | * Saved to the touch controls configuration. |
| 523 | */ |
| 524 | std::vector<CTouchButton> m_vTouchButtons; |
| 525 | |
| 526 | /** |
| 527 | * The activation states of the different extra menus which are toggle by the extra menu button behavior. |
| 528 | */ |
| 529 | bool [(int)EButtonVisibility::EXTRA_MENU_5 - (int)EButtonVisibility::EXTRA_MENU_1 + 1] = {false}; |
| 530 | |
| 531 | /** |
| 532 | * The currently selected action which is used for direct touch and is changed and used by some button behaviors. |
| 533 | */ |
| 534 | int m_ActionSelected = ACTION_FIRE; |
| 535 | |
| 536 | /** |
| 537 | * Counts how many joysticks are pressed. |
| 538 | */ |
| 539 | int m_JoystickPressCount = 0; |
| 540 | |
| 541 | /** |
| 542 | * The action that was last activated with direct touch input, which will determine the finger that will |
| 543 | * be used to update the mouse position from direct touch input. |
| 544 | */ |
| 545 | int m_DirectTouchLastAction = ACTION_FIRE; |
| 546 | |
| 547 | class CActionState |
| 548 | { |
| 549 | public: |
| 550 | bool m_Active = false; |
| 551 | IInput::CTouchFinger m_Finger; |
| 552 | }; |
| 553 | |
| 554 | /** |
| 555 | * The states of the different actions for direct touch input. |
| 556 | */ |
| 557 | CActionState m_aDirectTouchActionStates[NUM_ACTIONS]; |
| 558 | |
| 559 | /** |
| 560 | * These fingers were activating buttons that became invisible and were therefore deactivated. The fingers |
| 561 | * are stored until they are released so they do not activate direct touch input or touch buttons anymore. |
| 562 | */ |
| 563 | std::vector<IInput::CTouchFinger> m_vStaleFingers; |
| 564 | |
| 565 | /** |
| 566 | * Whether editing mode is currently active. |
| 567 | */ |
| 568 | bool m_EditingActive = false; |
| 569 | |
| 570 | /** |
| 571 | * Whether there are changes to the current configuration in editing mode. |
| 572 | */ |
| 573 | bool m_EditingChanges = false; |
| 574 | |
| 575 | void InitVisibilityFunctions(); |
| 576 | int NextActiveAction(int Action) const; |
| 577 | int NextDirectTouchAction() const; |
| 578 | void UpdateButtonsGame(const std::vector<IInput::CTouchFingerState> &vTouchFingerStates); |
| 579 | void ResetButtons(); |
| 580 | void RenderButtonsGame(); |
| 581 | vec2 CalculateScreenSize() const; |
| 582 | |
| 583 | bool ParseConfiguration(const void *pFileData, unsigned FileLength); |
| 584 | std::optional<EDirectTouchIngameMode> ParseDirectTouchIngameMode(const json_value *pModeValue); |
| 585 | std::optional<EDirectTouchSpectateMode> ParseDirectTouchSpectateMode(const json_value *pModeValue); |
| 586 | std::optional<ColorRGBA> ParseColor(const json_value *pColorValue, const char *pAttributeName, std::optional<ColorRGBA> DefaultColor) const; |
| 587 | std::optional<CTouchButton> ParseButton(const json_value *pButtonObject); |
| 588 | std::unique_ptr<CTouchButtonBehavior> ParseBehavior(const json_value *pBehaviorObject); |
| 589 | std::unique_ptr<CPredefinedTouchButtonBehavior> ParsePredefinedBehavior(const json_value *pBehaviorObject); |
| 590 | std::unique_ptr<CExtraMenuTouchButtonBehavior> (const json_value *pBehaviorObject); |
| 591 | std::unique_ptr<CBindTouchButtonBehavior> ParseBindBehavior(const json_value *pBehaviorObject); |
| 592 | std::unique_ptr<CBindToggleTouchButtonBehavior> ParseBindToggleBehavior(const json_value *pBehaviorObject); |
| 593 | void WriteConfiguration(CJsonWriter *pWriter); |
| 594 | |
| 595 | std::vector<ivec2> m_vTargets; |
| 596 | std::vector<CUnitRect> m_vLastUpdateRects; |
| 597 | std::vector<CUnitRect> m_vXSortedRects; |
| 598 | std::vector<CUnitRect> m_vYSortedRects; |
| 599 | int m_LastWidth = -10; |
| 600 | int m_LastHeight = -10; |
| 601 | void BuildPositionXY(std::vector<CUnitRect> vVisibleButtonRects, CUnitRect MyRect); |
| 602 | CUnitRect FindPositionXY(std::vector<CUnitRect> &vVisibleButtonRects, CUnitRect MyRect); |
| 603 | |
| 604 | // This is how editor render buttons. |
| 605 | void RenderButtonsEditor(); |
| 606 | // This is how editor deal with touch inputs. |
| 607 | void UpdateButtonsEditor(const std::vector<IInput::CTouchFingerState> &vTouchFingerStates); |
| 608 | void UpdateSampleButton(const CTouchButton &SrcButton); |
| 609 | |
| 610 | // For process fingerstates in button editor. |
| 611 | bool m_PreventSaving = false; |
| 612 | std::optional<IInput::CTouchFingerState> m_ActiveFingerState; |
| 613 | std::optional<IInput::CTouchFingerState> m_ZoomFingerState; |
| 614 | std::optional<IInput::CTouchFingerState> m_LongPressFingerState; |
| 615 | vec2 m_ZoomStartPos = vec2(0.0f, 0.0f); |
| 616 | vec2 m_AccumulatedDelta = vec2(0.0f, 0.0f); |
| 617 | std::vector<IInput::CTouchFingerState> m_vDeletedFingerState; |
| 618 | std::array<bool, (size_t)EButtonVisibility::NUM_VISIBILITIES> m_aVirtualVisibilities; |
| 619 | |
| 620 | // Partially copied so it looks the same as m_pSelectedButton. Follows along the fingers while sliding. |
| 621 | std::unique_ptr<CTouchButton> m_pSampleButton = nullptr; |
| 622 | // For rendering. Calculated from m_pSampleButton's m_UnitRect, will not overlapping with any rect. |
| 623 | std::optional<CUnitRect> m_ShownRect; |
| 624 | CTouchButton *m_pSelectedButton = nullptr; |
| 625 | |
| 626 | bool m_UnsavedChanges = false; |
| 627 | bool m_PreviewAllButtons = false; |
| 628 | |
| 629 | public: |
| 630 | CTouchButton *NewButton(); |
| 631 | void DeleteSelectedButton(); |
| 632 | bool IsRectOverlapping(CUnitRect MyRect, EButtonShape Shape) const; |
| 633 | CUnitRect UpdatePosition(CUnitRect MyRect, EButtonShape Shape, bool Ignore = false); // If Ignore == true, then the function will also try to avoid m_pSelectedButton. |
| 634 | void ResetButtonPointers(); |
| 635 | void ResetVirtualVisibilities(); |
| 636 | CUIRect CalculateScreenFromUnitRect(CUnitRect Unit, EButtonShape Shape = EButtonShape::RECT) const; |
| 637 | CUnitRect CalculateHitbox(const CUnitRect &Rect, EButtonShape Shape) const; |
| 638 | |
| 639 | // Getters and setters. |
| 640 | bool HasUnsavedChanges() const { return m_UnsavedChanges; } |
| 641 | void SetUnsavedChanges(bool UnsavedChanges) { m_UnsavedChanges = UnsavedChanges; } |
| 642 | std::array<bool, (size_t)EButtonVisibility::NUM_VISIBILITIES> VirtualVisibilities() const { return m_aVirtualVisibilities; } |
| 643 | void ReverseVirtualVisibilities(int Number) { m_aVirtualVisibilities[Number] = !m_aVirtualVisibilities[Number]; } |
| 644 | std::optional<CUnitRect> ShownRect() const { return m_ShownRect; } |
| 645 | void SetShownRect(std::optional<CUnitRect> Rect) { m_ShownRect = Rect; } |
| 646 | CTouchButton *SelectedButton() const { return m_pSelectedButton; } |
| 647 | void SetSelectedButton(CTouchButton *TargetButton) { m_pSelectedButton = TargetButton; } |
| 648 | bool NoRealButtonSelected() const { return m_pSelectedButton == nullptr; } |
| 649 | void RemakeSampleButton() { m_pSampleButton = std::make_unique<CTouchButton>(args: this); } |
| 650 | CTouchButton *SampleButton() const { return m_pSampleButton.get(); } |
| 651 | bool IsButtonEditing() const { return m_pSelectedButton != nullptr || m_pSampleButton != nullptr; } |
| 652 | ColorRGBA DefaultBackgroundColorInactive() const { return ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f); } |
| 653 | ColorRGBA DefaultBackgroundColorActive() const { return ColorRGBA(0.2f, 0.2f, 0.2f, 0.25f); } |
| 654 | ColorRGBA BackgroundColorInactive() const { return m_BackgroundColorInactive; } |
| 655 | ColorRGBA BackgroundColorActive() const { return m_BackgroundColorActive; } |
| 656 | void SetBackgroundColorInactive(ColorRGBA Color) { m_BackgroundColorInactive = Color; } |
| 657 | void SetBackgroundColorActive(ColorRGBA Color) { m_BackgroundColorActive = Color; } |
| 658 | std::vector<CTouchButton *> GetButtonsEditor(); |
| 659 | bool PreviewAllButtons() const { return m_PreviewAllButtons; } |
| 660 | void SetPreviewAllButtons(bool Preview) { m_PreviewAllButtons = Preview; } |
| 661 | |
| 662 | // Set the EPopupType and call |
| 663 | enum class |
| 664 | { |
| 665 | // Unsaved settings when changing selected button. |
| 666 | , |
| 667 | // FindPositionXY can't find an empty space for the selected button(Currently it's overlapping). |
| 668 | , |
| 669 | // Selected button is not visible. |
| 670 | , |
| 671 | |
| 672 | }; |
| 673 | |
| 674 | // These things must be set before opening the menu for calling the popup. |
| 675 | // After setting these, use GameClient()->m_Menus.SetActive(true), then the popup could be called automatically if EPopupType is not NUM_POPUPS. |
| 676 | class |
| 677 | { |
| 678 | public: |
| 679 | EPopupType = EPopupType::NUM_POPUPS; |
| 680 | CTouchButton * = nullptr; |
| 681 | CTouchButton * = nullptr; |
| 682 | bool = false; |
| 683 | }; |
| 684 | |
| 685 | CPopupParam (); |
| 686 | |
| 687 | // The issues won't be resolved until the Button Editor is rendered. If you want to solve issues right now don't use this. |
| 688 | // This is usually for update cached settings in button editor. |
| 689 | enum class EIssueType |
| 690 | { |
| 691 | CACHE_SETTINGS, // Update Cached settings from m_pTargetButton. |
| 692 | SAVE_SETTINGS, // Save Cached settings to m_pTargetButton. |
| 693 | CACHE_POSITION, // Update position from m_pTargetButton. |
| 694 | NUM_ISSUES |
| 695 | }; |
| 696 | |
| 697 | class CIssueParam |
| 698 | { |
| 699 | public: |
| 700 | bool m_Resolved = true; |
| 701 | CTouchButton *m_pTargetButton = nullptr; |
| 702 | }; |
| 703 | |
| 704 | bool AnyIssueNotResolved() const; |
| 705 | std::array<CTouchControls::CIssueParam, (unsigned)CTouchControls::EIssueType::NUM_ISSUES> Issues(); |
| 706 | |
| 707 | private: |
| 708 | CPopupParam ; |
| 709 | std::array<CIssueParam, (int)EIssueType::NUM_ISSUES> m_aIssueParam; |
| 710 | }; |
| 711 | |
| 712 | #endif |
| 713 | |