Compare commits

..

99 commits

Author SHA1 Message Date
ebbit1q
ae37762ee1
fix crash when pressing Back to results (#7043)
Some checks failed
Build Desktop / Configure (push) Has been cancelled
Build Docker Image / amd64 & arm64 (push) Has been cancelled
Build Desktop / Debian 13 (push) Has been cancelled
Build Desktop / Debian 12 (push) Has been cancelled
Build Desktop / Fedora 44 (push) Has been cancelled
Build Desktop / Fedora 43 (push) Has been cancelled
Build Desktop / Servatrice_Debian 12 (push) Has been cancelled
Build Desktop / Ubuntu 26.04 (push) Has been cancelled
Build Desktop / Ubuntu 24.04 (push) Has been cancelled
Build Desktop / Arch (push) Has been cancelled
Build Desktop / macOS 14 (push) Has been cancelled
Build Desktop / macOS 15 (push) Has been cancelled
Build Desktop / macOS 13 Intel (push) Has been cancelled
Build Desktop / macOS 15 Debug (push) Has been cancelled
Build Desktop / Windows 10 (push) Has been cancelled
2026-07-16 11:06:18 +02:00
github-actions[bot]
6fe46542f7
Update translation files (#7044) 2026-07-16 09:07:33 +02:00
DawnFire42
5bb6073422
Fix card position when added to the stack (#7041)
Some checks failed
Build Desktop / Configure (push) Has been cancelled
Build Docker Image / amd64 & arm64 (push) Has been cancelled
Build Desktop / Debian 13 (push) Has been cancelled
Build Desktop / Debian 12 (push) Has been cancelled
Build Desktop / Fedora 44 (push) Has been cancelled
Build Desktop / Fedora 43 (push) Has been cancelled
Build Desktop / Servatrice_Debian 12 (push) Has been cancelled
Build Desktop / Ubuntu 26.04 (push) Has been cancelled
Build Desktop / Ubuntu 24.04 (push) Has been cancelled
Build Desktop / Arch (push) Has been cancelled
Build Desktop / macOS 14 (push) Has been cancelled
Build Desktop / macOS 15 (push) Has been cancelled
Build Desktop / macOS 13 Intel (push) Has been cancelled
Build Desktop / macOS 15 Debug (push) Has been cancelled
Build Desktop / Windows 10 (push) Has been cancelled
2026-07-12 08:33:26 +02:00
tooomm
6c960fdf31
Use default values (#7017)
Some checks failed
Build Desktop / Configure (push) Has been cancelled
Build Docker Image / amd64 & arm64 (push) Has been cancelled
Build Desktop / Debian 13 (push) Has been cancelled
Build Desktop / Debian 12 (push) Has been cancelled
Build Desktop / Fedora 44 (push) Has been cancelled
Build Desktop / Fedora 43 (push) Has been cancelled
Build Desktop / Servatrice_Debian 12 (push) Has been cancelled
Build Desktop / Ubuntu 26.04 (push) Has been cancelled
Build Desktop / Ubuntu 24.04 (push) Has been cancelled
Build Desktop / Arch (push) Has been cancelled
Build Desktop / macOS 14 (push) Has been cancelled
Build Desktop / macOS 15 (push) Has been cancelled
Build Desktop / macOS 13 Intel (push) Has been cancelled
Build Desktop / macOS 15 Debug (push) Has been cancelled
Build Desktop / Windows 10 (push) Has been cancelled
2026-07-06 11:24:11 +02:00
tooomm
f3b41432cb
CI: Fix login+push for publishing Servatrice Docker images (#7023) 2026-07-06 07:01:12 +02:00
tooomm
808d991489
Delete .codacy.yml (#6957) 2026-07-06 07:00:03 +02:00
RickyRister
c5b37cdffe
[Game] Refactor subtype tally code to be more generic (#7021)
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 15 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run
* [Game] Refactor subtype tally code to be more generic

* remove default case

* consolidate empty check

* fix include guard

* backwards compatibility with qt5
2026-07-05 19:11:08 -07:00
RickyRister
430fc117b4
[UserContextMenu] Refactor: Consolidate actions (#7022) 2026-07-05 18:24:52 -07:00
github-actions[bot]
baddbfae14
Update translation source strings (#7028)
Some checks failed
Build Desktop / Configure (push) Has been cancelled
Build Docker Image / amd64 & arm64 (push) Has been cancelled
Build Desktop / Debian 13 (push) Has been cancelled
Build Desktop / Debian 12 (push) Has been cancelled
Build Desktop / Fedora 44 (push) Has been cancelled
Build Desktop / Fedora 43 (push) Has been cancelled
Build Desktop / Servatrice_Debian 12 (push) Has been cancelled
Build Desktop / Ubuntu 26.04 (push) Has been cancelled
Build Desktop / Ubuntu 24.04 (push) Has been cancelled
Build Desktop / Arch (push) Has been cancelled
Build Desktop / macOS 14 (push) Has been cancelled
Build Desktop / macOS 15 (push) Has been cancelled
Build Desktop / macOS 13 Intel (push) Has been cancelled
Build Desktop / macOS 15 Debug (push) Has been cancelled
Build Desktop / Windows 10 (push) Has been cancelled
Co-authored-by: github-actions <github-actions@github.com>
2026-07-01 16:25:10 +02:00
Skagra42
9b0348240d
Search by date. (#7027)
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 15 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run
2026-06-30 23:46:37 -07:00
DawnFire42
18b23b19a7
Split trice_limits.h into dedicated headers (#7025)
Some checks failed
Build Desktop / Configure (push) Has been cancelled
Build Docker Image / amd64 & arm64 (push) Has been cancelled
Build Desktop / Debian 13 (push) Has been cancelled
Build Desktop / Debian 12 (push) Has been cancelled
Build Desktop / Fedora 44 (push) Has been cancelled
Build Desktop / Fedora 43 (push) Has been cancelled
Build Desktop / Servatrice_Debian 12 (push) Has been cancelled
Build Desktop / Ubuntu 26.04 (push) Has been cancelled
Build Desktop / Ubuntu 24.04 (push) Has been cancelled
Build Desktop / Arch (push) Has been cancelled
Build Desktop / macOS 14 (push) Has been cancelled
Build Desktop / macOS 15 (push) Has been cancelled
Build Desktop / macOS 13 Intel (push) Has been cancelled
Build Desktop / macOS 15 Debug (push) Has been cancelled
Build Desktop / Windows 10 (push) Has been cancelled
* Split trice_limits.h into dedicated headers

* Updated docstrings
2026-06-29 14:37:52 -07:00
DawnFire42
4a384f2a75
fix theme manager return type warning (#7026)
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 15 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run
2026-06-29 08:03:30 -07:00
DawnFire42
05ae6f47a6
Unify counter clamp arithmetic into shared addClamped() helper (#7009)
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 15 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run
* Unify counter clamp arithmetic into shared addClamped() helper

- Add addClamped() in new header clamped_arithmetic.h; uses a 64-bit
  intermediate so the addition cannot overflow int.
- Use it in Server_Card::incrementCounter() (clamps [0, MAX_COUNTERS_ON_CARD])
  and Server_Counter::incrementCount() (clamps [INT_MIN, INT_MAX]), removing
  the duplicated overflow-safe logic and its keep-in-sync TODO.
- Inline incrementCount() into server_counter.h; server_counter.cpp now holds
  only the constructor and getInfo().
- Clarify the card-counter bounds comment in trice_limits.h.

* Rename MAX_COUNTERS_ON_CARD to MAX_COUNTER_VALUE

The constant caps the counter's value, not how many counters can be on the card

* Add direct unit tests for addClamped() helper

* Harden offsetCardCounter() against signed-int overflow

Replace the raw oldValue + offset sum with addClamped(), clamping to [0, MAX_COUNTER_VALUE] without overflow.

* Comment update

* Remove class names from addClamped() docstring
2026-06-28 16:10:57 -07:00
RickyRister
fcac7493ad
[Card] Add facedown property to CardRelation (#6997)
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 15 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run
* [Card] Add facedown property to CardRelation

* trailing newline

* fix comments

* update schema
2026-06-28 02:03:07 -07:00
DawnFire42
055ba9a16f
Add subtype breakdown counter for card selection (#6923)
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 15 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run
* Add subtype breakdown counter for card selection

  Display a categorized count of creature subtypes (and other card type
  subtypes) when multiple cards are selected. The breakdown appears above
  the total selection counter in the bottom-right corner.

  Subtypes are grouped by main card type and sorted by frequency, with
  the most common subtypes positioned adjacent to the total count for
  quick reference. The feature can be toggled via a new checkbox in
  Settings > User Interface.

* Alignment fix

* Computation logic moved to helper funtction in separate file

* Rename SubtypeCounter to SubtypeTally

* Fix subtype tally alignment by using grid layout instead of character padding

* Rename count to tally in the subtype breakdown feature

* partial rename

* list position fixed

* Clean up code and documentation

* Rename subtypeCountLabelStyle to subtypeTallyLabelStyle and fix include ordering

* Fix include path for selection_subtype_tally.h after file relocation

* fixed count to tally rename inconsistencies
2026-06-27 15:53:21 -07:00
BruebachL
ad4922537d
[UserListDelegate] Supply providerid in cmd, position popup correctly (#7020)
* [UserListDelegate] Transmit providerId in cmd when setting user banner card

* [UserListDelegate] Position popup correctly

* Lint.

---------

Co-authored-by: Lukas Brübach <lukas.bruebach@bdosecurity.de>
2026-06-27 14:44:24 -04:00
dependabot[bot]
4cbc00b9c4
Bump actions/cache from 5 to 6 (#7019)
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 15 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run
2026-06-27 17:25:30 +02:00
BruebachL
6dc974a05d
[UserListDelegate] Consider providerId (#7018)
Co-authored-by: Lukas Brübach <lukas.bruebach@bdosecurity.de>
2026-06-27 11:23:55 -04:00
BruebachL
2914874720
[Room][UserList] Introduce style delegate (#6981)
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 15 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run
* [Room] Additionally show a tab for friends and ignored users instead of just all online users.

Took 21 minutes


Took 12 minutes

* [Room][UserList] Introduce style delegate for user list
- Allow users to set a card name and parameters as their background banner
- Allow mods to white/blacklist cards
- Allow toggling back to the old display style

Took 7 minutes

Took 28 seconds

Took 2 minutes

Took 2 minutes

* Right checkstate.

Took 14 minutes


Took 2 minutes

* Utility for test.

Took 9 minutes

Took 8 seconds

Took 2 seconds

* Lint.

Took 10 minutes

* Algorithm for sql schema migration

Took 13 minutes

* Use {prefix}, bound card name, return errors.

Took 27 seconds

* Convert queue to while loop.

Took 19 seconds

* Hover popup.

Took 36 minutes

Took 1 minute

* More granular signals, popup for user info.

Took 25 minutes

Took 8 seconds

Took 16 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-06-26 20:52:24 -04:00
tooomm
c9ebdb451f
CI: Avoid failing tx step if secret can not be accessed (#7012)
Some checks are pending
Build Docker Image / amd64 & arm64 (push) Waiting to run
2026-06-26 16:39:33 +02:00
Zach H
80426d77bc
Add Discord account registration monitor (#7013)
Some checks failed
Build Desktop / Configure (push) Has been cancelled
Build Docker Image / amd64 & arm64 (push) Has been cancelled
Build Desktop / Debian 13 (push) Has been cancelled
Build Desktop / Debian 12 (push) Has been cancelled
Build Desktop / Fedora 44 (push) Has been cancelled
Build Desktop / Fedora 43 (push) Has been cancelled
Build Desktop / Servatrice_Debian 12 (push) Has been cancelled
Build Desktop / Ubuntu 26.04 (push) Has been cancelled
Build Desktop / Ubuntu 24.04 (push) Has been cancelled
Build Desktop / Arch (push) Has been cancelled
Build Desktop / macOS 14 (push) Has been cancelled
Build Desktop / macOS 15 (push) Has been cancelled
Build Desktop / macOS 13 Intel (push) Has been cancelled
Build Desktop / macOS 15 Debug (push) Has been cancelled
Build Desktop / Windows 10 (push) Has been cancelled
A read-only cron script that posts new Servatrice account registrations to a
Discord channel via webhook. Dedups by an auto-increment id high-water-mark
(single-integer state, no duplicates, nothing missed across downtime). Reads DB
credentials and the webhook from a servatrice-style ini via --config.
2026-06-21 01:09:57 -04:00
Phred Lane
e99a55ccab
CI: Simplified build workflow (#6995)
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 15 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run
* simplified build workflow

* cleaned up translation workflows

* fixed typo

* updated format based on code review

Co-authored-by: tooomm <tooomm@users.noreply.github.com>

* restored quotes around `group` string

---------

Co-authored-by: tooomm <tooomm@users.noreply.github.com>
2026-06-20 22:44:37 +02:00
dependabot[bot]
2a3c4a2455
Bump actions/checkout from 6 to 7 (#7011) 2026-06-20 19:11:10 +02:00
Phred Lane
687e6644bc
replaced dynamically injected variables with environment variables (#6996)
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 15 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run
This prevents some injection attacks and makes all the scripts valid Bash.
2026-06-19 16:42:15 +02:00
transifex-integration[bot]
e28f31c93e
Translate oracle/oracle_en@source.ts in es (#7006)
Some checks failed
Build Desktop / Configure (push) Has been cancelled
Build Docker Image / amd64 & arm64 (push) Has been cancelled
Build Desktop / Debian 13 (push) Has been cancelled
Build Desktop / Debian 12 (push) Has been cancelled
Build Desktop / Fedora 44 (push) Has been cancelled
Build Desktop / Fedora 43 (push) Has been cancelled
Build Desktop / Servatrice_Debian 12 (push) Has been cancelled
Build Desktop / Ubuntu 26.04 (push) Has been cancelled
Build Desktop / Ubuntu 24.04 (push) Has been cancelled
Build Desktop / Arch (push) Has been cancelled
Build Desktop / macOS 14 (push) Has been cancelled
Build Desktop / macOS 15 (push) Has been cancelled
Build Desktop / macOS 13 Intel (push) Has been cancelled
Build Desktop / macOS 15 Debug (push) Has been cancelled
Build Desktop / Windows 10 (push) Has been cancelled
2026-06-17 22:16:26 +02:00
DawnFire42
0c140d866f
[Card] Respect disabled sets when loading v3 card databases (#6882) (#7004)
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 15 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run
2026-06-17 14:09:05 +02:00
RickyRister
45d0cedb5c
[Game] Setting to restore old chat autofocus behavior (#6992)
Some checks failed
Build Desktop / Configure (push) Has been cancelled
Build Docker Image / amd64 & arm64 (push) Has been cancelled
Build Desktop / Debian 13 (push) Has been cancelled
Build Desktop / Debian 12 (push) Has been cancelled
Build Desktop / Fedora 44 (push) Has been cancelled
Build Desktop / Fedora 43 (push) Has been cancelled
Build Desktop / Servatrice_Debian 12 (push) Has been cancelled
Build Desktop / Ubuntu 26.04 (push) Has been cancelled
Build Desktop / Ubuntu 24.04 (push) Has been cancelled
Build Desktop / Arch (push) Has been cancelled
Build Desktop / macOS 14 (push) Has been cancelled
Build Desktop / macOS 15 (push) Has been cancelled
Build Desktop / macOS 13 Intel (push) Has been cancelled
Build Desktop / macOS 15 Debug (push) Has been cancelled
Build Desktop / Windows 10 (push) Has been cancelled
* [Game] Setting to restore old chat autofocus behavior

* fixes
2026-06-15 17:04:30 +02:00
BruebachL
309e4730a3
[Lobby][DeckView] Always at minimum create main and side deck zone containers. (#7001)
Took 12 minutes

Took 3 seconds

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-06-15 15:50:04 +02:00
BruebachL
0c4cc3f824
[DeckView][DeckEditor] Implement shortcut for load deck from website (default: Ctrl+Shift+O) (#7002)
Took 12 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-06-15 15:49:47 +02:00
BruebachL
dfbe944c31
[App][Windows][NSIS] Use QProcess::setNativeArguments on Windows, properly order portable detection (#6989)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-06-15 15:23:18 +02:00
RickyRister
0f3e6fbe26
[CardDatabase] Pass CardInfoPtr by const ref (#6998)
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 15 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run
* [CardDatabase] Pass CardInfoPtr by const ref

* trailing newline
2026-06-14 04:46:18 -07:00
RickyRister
5ffe344779
[Game] Fix facedown predefined tokens leaking tablerow (#7000) 2026-06-14 03:21:17 -07:00
kongwu
f28ede7ae3
[UserContextMenu] Add confirmation dialog before kicking a player (#6987)
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 15 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run
2026-06-13 02:42:55 -07:00
BruebachL
7aaacbf347
[Update][NSIS] Use single string shell invocation (#6986)
Some checks failed
Build Desktop / Configure (push) Has been cancelled
Build Docker Image / amd64 & arm64 (push) Has been cancelled
Build Desktop / Debian 13 (push) Has been cancelled
Build Desktop / Debian 12 (push) Has been cancelled
Build Desktop / Fedora 44 (push) Has been cancelled
Build Desktop / Fedora 43 (push) Has been cancelled
Build Desktop / Servatrice_Debian 12 (push) Has been cancelled
Build Desktop / Ubuntu 26.04 (push) Has been cancelled
Build Desktop / Ubuntu 24.04 (push) Has been cancelled
Build Desktop / Arch (push) Has been cancelled
Build Desktop / macOS 14 (push) Has been cancelled
Build Desktop / macOS 15 (push) Has been cancelled
Build Desktop / macOS 13 Intel (push) Has been cancelled
Build Desktop / macOS 15 Debug (push) Has been cancelled
Build Desktop / Windows 10 (push) Has been cancelled
Took 18 minutes


Took 2 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-06-11 21:17:28 +02:00
tooomm
694adc9e64
Use Visual Studio 2026 (#6985)
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 15 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run
2026-06-11 10:45:17 +02:00
BruebachL
b17d879da8
[Game][Graphics][Player] Add named zone lookup-map to player graphics. (#6984)
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 15 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run
Took 16 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-06-10 10:58:28 +02:00
ebbit1q
6be9cec6e2
do not save a const reference to the user data in the info dialog (#6974)
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 15 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run
* do not save a const reference to the user data in the info dialog

* cmake format
2026-06-09 23:35:00 -07:00
kongwu
6d0a423dcf
[Messages] Add option to ignore private messages from non-buddy users (#6966)
* [Messages] Add option to ignore private messages from non-buddy users

* [Messages] Exclude Moderator/Admin from non-buddy ignore filter

Moderator and Admin messages should not be filtered out when the
'Ignore private messages from non-buddies' setting is enabled, to
ensure that important warnings from server staff reach users.
2026-06-09 20:49:29 -07:00
kongwu
f72c82d0f9
[DeckEditor] Replace mainboard/sideboard with tokensboard for tokens (#6971)
* [DeckEditor] Replace mainboard/sideboard with tokensboard for token cards (#6546)

* [PrintingSelector] Replace std::tuple with ZoneCounts struct for readability (#6546)
2026-06-09 20:46:43 -07:00
BruebachL
da4ba222c0
[Game] Move graphics out of game and into game_graphics (#6928)
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 15 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run
* [Game][Player] Pull out graphics_items out of player_logic

Took 25 seconds


Took 9 minutes

* [Game] Move graphics files into game_graphics

Took 1 minute

Took 2 minutes

Took 23 seconds

Took 1 minute

Took 2 seconds

* Include.

Took 4 minutes

Took 3 minutes

Took 4 minutes

Took 1 minute

Took 3 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-06-09 09:51:13 +02:00
BruebachL
cbfd286908
[Game][Player] Move dialog creation out of player_actions and into player_dialogs (#6946)
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 15 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run
* [Game][Player] Split Player into PlayerLogic/PlayerGraphicsItem

Took 4 minutes

Took 48 seconds

Took 2 minutes

* Drop early return.

Took 1 hour 13 minutes


Took 2 minutes

Took 1 minute

Took 24 seconds

* [Game][Player] Split Player into PlayerLogic/PlayerGraphicsItem

Took 4 minutes

Took 58 seconds

* [Game][Menus] Make Menus accept PlayerGraphicsItem instead of PlayerLogic

Took 7 minutes

Took 4 minutes

Took 9 seconds

Took 2 minutes


Took 5 minutes

Took 58 seconds

* [Game][Player] Split Player into PlayerLogic/PlayerGraphicsItem

Took 4 minutes

Took 2 minutes

* [Game][Menus] Make Menus accept PlayerGraphicsItem instead of PlayerLogic

Took 7 minutes


Took 1 minute

Took 57 seconds

* [Game][Player] Move dialog creation out of player_actions and into player_dialogs

Took 3 minutes

Took 1 second

* Fix typo.

Took 5 minutes

* Addressed comments.

Took 16 minutes

Took 11 seconds

* Reintroduce clearCardsToDelete check.

Took 3 minutes

* Capture cards before semaphore.

Took 1 minute

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-06-09 08:22:59 +02:00
BruebachL
487bb84b6f
[Game][Menus] Make Menus accept PlayerGraphicsItem instead of PlayerLogic (#6945)
* [Game][Player] Split Player into PlayerLogic/PlayerGraphicsItem

Took 4 minutes

Took 58 seconds


Took 2 minutes

* [Game][Menus] Make Menus accept PlayerGraphicsItem instead of PlayerLogic

Took 7 minutes

Took 4 minutes

Took 9 seconds

Took 2 minutes


Took 5 minutes

Took 58 seconds

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-06-09 08:07:06 +02:00
BruebachL
9e03f82616
[Game][Player] Split Player into PlayerLogic/PlayerGraphicsItem (#6944)
* [Game][Player] Split Player into PlayerLogic/PlayerGraphicsItem

Took 4 minutes

Took 48 seconds

* Drop early return.

Took 1 hour 13 minutes


Took 2 minutes

Took 1 minute

* Delete player view.

Took 37 seconds

* Restore card counter color in menu.

Took 5 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-06-09 08:05:39 +02:00
Christo
e674a39b87
Fix #6952: prevent deck loss when saving to a full disk (#6978)
* Fix #6952: prevent deck loss when saving to a full disk

DeckLoader::saveToFile() opened the target with QFile in WriteOnly mode,
which truncates the existing file to 0 bytes the moment it is opened. The
serializers (DeckList::saveToFile_Native/_Plain) always return true and the
result of flush() was ignored, so a write that failed part-way -- e.g.
because the disk was full -- left a 0-byte file behind yet was still
reported (and logged) as a successful save. The same truncate-then-write
pattern in updateLastLoadedTimestamp() could destroy a deck on load.

Switch both paths to QSaveFile, which writes to a temporary file and only
atomically replaces the target if commit() succeeds. On any write or flush
failure commit() returns false, the original deck is left untouched, and
the failure is logged instead of being reported as success.

* Use QSaveFile in convertToCockatriceFormat() too

convertToCockatriceFormat() had the same data-loss pattern: QFile WriteOnly
truncated the .cod, saveToFile_Native() always returns true, and the original
file was then removed unconditionally -- so a full disk during conversion wrote
a 0-byte .cod and then deleted the source deck.

Switch to QSaveFile (write + atomic commit), remove the original only after a
successful commit, and move the format check ahead of the file open so an
already-Cockatrice or unsupported deck never truncates or deletes anything.

Raised in review by ZeldaZach.
2026-06-09 07:54:01 +02:00
tooomm
1efc382c05
CI: Cleanup (#6959)
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 15 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run
* Label & variables

* fix bracket

* other workflows

* fix trailing whitespace

* fixes
2026-06-08 19:37:50 +02:00
tooomm
dc152e89f7
CI: Print colored diff for lint check (#6975)
* print colored diff in gha

* use spaces
2026-06-08 19:19:28 +02:00
RickyRister
20cdcdb382
[ReplayManager] Refactor to send replayed events through signal (#6979)
* [ReplayManager] Refactor to send replayed events through signal

* remove blank

* pass by const auto ref
2026-06-07 19:39:31 -07:00
BruebachL
23da49ee5b
[Game] [Arrows] Use arrowData/registry and generate unique server-side ids (#6973)
* [Game] [Arrows] Track creatorId, use arrowData in arrowItem, use registry, generate unique arrow id's on server side and delete-on-exist inserts.

Took 2 minutes

Took 1 minute

* Fix emitting slot instead of signal.

Took 15 minutes

* Clear arrows locally in special circumstances i.e. teardown.

Took 28 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-06-07 21:11:02 +02:00
RickyRister
c14a008080
[TabDeckEditor] Refactor card database view into own class (#6967)
* rename method

* [TabDeckEditor] Refactor card database view into own class

* fix include guard

* directly get key signals for eventFilter

* fix includes
2026-06-05 10:20:46 -07:00
RickyRister
0da2ac4087
[TabDeckEditor] Refactor: pass nullable deck model into filter widget (#6969) 2026-06-05 09:21:28 -07:00
RickyRister
29cc622ce3
[TabDeckEditor] Refactor: Create shared CardDatabaseModel for tab (#6968) 2026-06-05 09:21:13 -07:00
RickyRister
86256602ff
[TabDeckEditor] Refactor to use signal instead of calling tab (#6965)
* [TabDeckEditor] Refactor to use signal instead of calling tab

* update docs

* fix cardInfoRequest
2026-06-03 10:41:55 -07:00
RickyRister
f37c418865
[TabDeckEditor] Refactor: Remove cardDatabase field from analysis interfaces (#6963)
* [TabDeckEditor] Refactor: Remove cardDatabase field from analysis interfaces

* update includes
2026-06-03 10:08:57 -07:00
RickyRister
46d3b820db
[TabDeckEditor] Refactor: pull up showPrintingSelector (#6964)
* [TabDeckEditor] Refactor: pull up showPrintingSelector

* trailing newline
2026-06-03 09:35:48 -07:00
RickyRister
e0cbb7f06c
[TabDeckEditor] Refactor: pass ExactCard in signal instead of widget (#6962)
* [TabDeckEditor] Refactor: pass ExactCard in signal instead of widget

* address comments
2026-06-02 21:22:06 -07:00
RickyRister
f52dc6dda8
[TabDeckEditor] Refactor: consolidate add/decrement card signals (#6961) 2026-06-02 20:13:39 -07:00
RickyRister
3fa377a11c
[TabDeckEditor] Refactor check ctrl to be on click (#6956) 2026-05-31 03:44:40 -07:00
RickyRister
c5372a9e92
[DeckEditor] Refactor: clean up addCardHelper (#6939)
* [DeckEditor] Refactor: clean up addCardHelper

* remove setSaveStatus
2026-05-31 03:14:21 -07:00
RickyRister
6de55e9096
[Game][Arrow] Correctly call clear all arrows for player (#6951) 2026-05-28 23:51:12 -07:00
RickyRister
43c3bf5966
[Game][Arrow] Refactor: Rename arrow methods in GameScene (#6949)
* [Game][Arrow] Rename methods in GameScene

* move stuff around and docs
2026-05-28 02:32:40 -07:00
RickyRister
c4f4cece01
[Game] Show counter color icons in game log (#6948) 2026-05-28 11:15:30 +02:00
RickyRister
0d7047a728
[Game] Show color icons in counters menu (#6947) 2026-05-28 11:05:26 +02:00
RickyRister
7f30728f87
[CardDatabaseModel] Pass CardInfoPtr by const ref (#6940) 2026-05-26 18:53:20 -07:00
RickyRister
1d5d3f2d38
Run formatter on all our files (#6942) 2026-05-26 15:11:38 -07:00
BruebachL
b3c89167c5
[Game][Arrows] Hook up to the state zone change properly. (#6937)
Took 17 minutes

Took 3 seconds

Took 2 minutes

Took 10 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-05-25 08:19:27 +02:00
BruebachL
90ab663212
[Server][Game][Arrows] Properly notify clients when deleting arrows on card move and transform into (#6936)
* [Server][Game][Arrows] Properly notify clients when deleting arrows on card move and transform into

Took 15 minutes

* Observe "not found" response

Took 18 minutes

Took 4 seconds

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-05-25 08:19:17 +02:00
BruebachL
98c00c55ed
[Game][Arrows] ArrowItem should not send deletion game commands by itself (#6932)
Took 13 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-05-23 02:20:41 +02:00
BruebachL
d2164c3f08
[Oracle] Change basic lands exception to regex to deal with snow basics. (#6931)
* [Oracle] Change basic lands exception to regex to deal with snow basics.

Took 45 minutes

* Update oracle/src/oracleimporter.cpp

Co-authored-by: ebbit1q <ebbit1q@gmail.com>

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: ebbit1q <ebbit1q@gmail.com>
2026-05-22 22:36:53 +02:00
RickyRister
8004d4f2d4
[VDE] Disable filter dock widget (#6924) 2026-05-22 03:26:52 -07:00
BruebachL
8751f0605d
[Game][Arrows] Don't request deletion on inbound arrow deletion signal again but react to it locally (#6927)
* [Game][Arrows] Deleting arrows has no acknowledgement command so we have to delete locally as well.

Took 22 minutes

* Fix properly.

Took 15 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-05-22 10:25:13 +02:00
RickyRister
09d817770e
[Game] Refactor: move proliferate action code to PlayerActions (#6926) 2026-05-22 10:25:00 +02:00
DawnFire42
81a2712b92
Fix card stacking overflow when vertical zone has many cards (#6925)
Previously, minOffset (10px) was enforced unconditionally, causing card
  tops to overflow zone bounds when many cards were stacked. For example,
  50 cards in a 200px zone would place the last card's top at y=490.

  Now offsets compress below minOffset when necessary to keep all card
  tops visible. The constraint is guarded by !allowBottomOverflow to
  preserve future clipping zone semantics.
2026-05-21 22:56:24 -07:00
DawnFire42
8dca14933c
Centralize counter API with server-side bounds and no-op filtering (#6879)
* Refactor server counter API to own overflow protection and filter no-op events

  Counter modifications now clamp to int bounds server-side and return change
  status, allowing command handlers to skip network broadcasts when values
  don't actually change.

* Centralize MAX_COUNTERS_ON_CARD and enforce [0, 999] bounds on server

  - Move MAX_COUNTERS_ON_CARD to trice_limits.h
  - Server clamps values in setCounter() and incrementCounter()
  - Client uses clamped comparison to allow recovery from invalid states
  - Add tests for clamping behavior

* move incrementCount() implementation from header to cpp
2026-05-21 20:39:35 -07:00
tooomm
74102aa1ec
Update external c libs: SFMT & peglib (#6901) 2026-05-22 03:31:59 +02:00
ebbit1q
a9003be30f
update version to 3.1.0 for subsequent beta releases (#6921) 2026-05-22 01:35:01 +02:00
github-actions[bot]
6faa0d54e3
Update translation files (#6920)
Co-authored-by: github-actions <github-actions@github.com>
2026-05-21 19:14:58 -04:00
DawnFire42
33e0f8699b
Standardize Doxygen documentation (#6885) 2026-05-21 22:58:07 +02:00
tooomm
03d54265fe
escape ampersand (#6900) 2026-05-21 22:02:09 +02:00
BruebachL
491d1c9187
[Game][Arrows] Split Arrows into ArrowData and ArrowItem (#6918)
* [Game][Arrows] Split Arrows into ArrowData and ArrowItem

Took 13 minutes

Took 5 seconds

Took 1 minute

Took 26 seconds

* Address comments.

Took 17 minutes

Took 9 seconds


Took 1 minute

* Change check.

Took 3 minutes

* Pass by const reference.

Took 10 minutes

* Remove extra method

Took 2 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-05-21 20:31:14 +02:00
BruebachL
bddf9bd818
[Game][Counters] Split counters into AbstractCounter (graphics) and CounterState (logic) (#6917)
* [Counters] Split counters into graphics and logic states

Took 22 minutes

* Don't have widget hold pointer to state -> Copy what we need and subscribe to changes.

Took 12 minutes

Took 5 seconds

* Sync value too.

Took 3 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-05-21 20:16:28 +02:00
RickyRister
0549892092
[Settings] Regroup card layout settings (#6914) 2026-05-20 14:00:47 -07:00
RickyRister
10b9a65f17
[Server][Game] Make undo draw failure visible in chat (#6889)
* [Server][Game] Make undo draw failure visible in chat

* genericize the proto
2026-05-20 02:23:02 -07:00
BruebachL
5219cffa6b
[Player] Rename player to player logic (#6913)
Took 13 minutes

Took 6 seconds

Took 2 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-05-19 12:36:31 +02:00
RickyRister
71790d8e10
[Settings] Split printing settings into own group (#6906) 2026-05-19 03:34:42 -07:00
RickyRister
fe31a49f86
[Settings] Split appearance home tab settings into own group (#6905) 2026-05-19 03:33:25 -07:00
RickyRister
55c84ca860
[Settings] Clean up groupings in general settings (#6907) 2026-05-19 03:32:45 -07:00
ebbit1q
40b947c1e7
fix cards attaching/transforming to the wrong card across players (#6909)
* fix cards attaching/transforming to the wrong card across players

* move comment
2026-05-19 12:26:17 +02:00
ebbit1q
40cef0e436
fix resizing the visual card database loading more pages forever (#6884)
* fix resizing the vde loading more pages forever

* make near end of page const
2026-05-19 12:25:35 +02:00
BruebachL
9f1c225b7a
[Player] Stop reaching into graphics_item and emit signals instead for conceded and zoneId (#6912)
* [Player] Stop reaching into graphics_item and emit signals instead for conceded and zoneId

Took 7 minutes

Took 3 seconds

* Add sameValue check.

Took 3 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-05-19 12:22:40 +02:00
BruebachL
af2f888293
[Player] Pull handVisible out of player_info and let graphics_item just determine this on its own. (#6911)
Took 12 minutes


Took 15 seconds

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-05-19 12:10:19 +02:00
BruebachL
7a5b2e9f0e
[Game] Move state fields out of CardItem (#6904)
* [Game] Move state fields out of CardItem

Took 1 hour 2 minutes

* Move stuff into .cpp

Took 14 minutes

* Signals pass changed values as params

Took 2 minutes

* Comments.

Took 23 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-05-18 20:21:57 +02:00
RickyRister
021a9f8383
[DlgSettings] Refactor: split SettingsPage into separate files (#6899) 2026-05-18 01:59:02 -07:00
BruebachL
cba9ce2b2b
[Game/Zones] Simple move refactor to differentiate between logic and graphics for zones (#6903)
* [Game/Zones] Simple move refactor to differentiate between logic and graphics for zones

Took 21 minutes

* Clean up game/zones/logic folder.

Took 6 minutes

* Adjust tests.

Took 3 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-05-18 06:36:18 +02:00
SlightlyCircuitous
bb1a5b33a1
Add generic colorless mana symbol (#6873)
* Add generic colorless mana symbol

* Add generic colorless mana symbol
2026-05-17 19:48:48 +02:00
BruebachL
6ac340026f
Update CMakeLists.txt (#6898) 2026-05-17 16:15:15 +02:00
BruebachL
059eeebe89
[Settings] Fix typo for schemeComboLabel (#6897)
Took 6 minutes

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-05-17 10:12:34 +02:00
BruebachL
117ea543c5
[App/Theme] Palette Editor (#6877)
* [App/Theme] Palette Editor

Took 1 minute

Took 1 hour 47 minutes

Took 6 seconds

Took 3 minutes

Took 5 minutes


Took 3 minutes

* Add oracle, add palette files and configs.

Took 10 minutes

* Fix a stupid include mistake, thanks IDE

Took 3 minutes

Took 20 seconds

* Includes.

Took 4 minutes

* Fix ampersand not displaying correctly.

Took 14 minutes

* Longer variable names.

Took 10 minutes

Took 5 seconds

* Change ampersand everywhere

Took 23 seconds

* Doxygen properly.

Took 1 minute

* Remove namespace, fold I/O into structs.

Took 12 minutes

* Remove namespace, fold I/O into structs.

Took 33 seconds

* Alphabetize.

Took 35 seconds

* Lint.

Took 49 seconds

* Add a combo box to quick switch settings.

Took 19 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-05-17 10:08:00 +02:00
tooomm
989a5be23b
Enable RemoveSemicolon & update format.sh (#6896)
* Update .clang-format

* Add libs that got added in the meantime

* Use libcockatrice_*
2026-05-16 20:26:37 +02:00
BruebachL
f8ce5c2e39
[PictureLoader] Allow saving downloaded images to local storage and not just the QNetworkManager cache (#6620)
* [PictureLoader] Allow saving downloaded images to local storage and not just the QNetworkManager cache.

Took 1 hour 11 minutes

Took 4 seconds


Took 25 seconds

* Give people options from a dropdown.

Took 1 hour 6 minutes

Took 3 seconds

* Simplify directory removal code.

Took 5 minutes

Took 8 seconds

* Merge pull request #8

* Create new category for new settings

* Split off storage settings

Took 47 minutes

Took 4 seconds

* Allow toggling between caching methods.

Took 1 hour 30 minutes

Took 9 seconds

* Adjust settings dialog.

Took 5 minutes

Took 59 seconds

Took 22 seconds

Took 6 seconds

* tr() strings

Took 1 minute

Took 6 seconds

* Readjust layout, default naming scheme.

Took 5 minutes

* Add stretch.

Took 9 minutes

* Make scrollable.

Took 2 minutes

* Add icon.

Took 7 minutes

* Change naming to be uniform.

Took 3 minutes

Took 3 seconds

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: RickyRister <42636155+RickyRister@users.noreply.github.com>
2026-05-16 20:15:10 +02:00
628 changed files with 75087 additions and 49169 deletions

View file

@ -13,17 +13,9 @@ fi
# Check formatting using format.sh # Check formatting using format.sh
echo "Checking your code using format.sh..." echo "Checking your code using format.sh..."
diff="$(./format.sh --diff --cmake --shell --print-version --branch origin/master)" ./format.sh --color-diff --cmake --shell --print-version --branch origin/master
err=$? err=$?
sep="
----------
"
used_version="${diff%%"$sep"*}"
diff="${diff#*"$sep"}"
changes_to_make="${diff%%"$sep"*}"
files_to_edit="${diff#*"$sep"}"
case $err in case $err in
1) 1)
cat <<EOM cat <<EOM
@ -36,19 +28,10 @@ case $err in
*** Then commit and push those changes to this branch. *** *** Then commit and push those changes to this branch. ***
*** Check our CONTRIBUTING.md file for more details. *** *** Check our CONTRIBUTING.md file for more details. ***
*** *** *** ***
*** Thank you ❤️ *** *** Thank you ❤️ ***
*** *** *** ***
*********************************************************** ***********************************************************
Used version:
$used_version
Affected files:
$files_to_edit
The following changes should be made:
$changes_to_make
Exiting... Exiting...
EOM EOM
exit 2 exit 2
@ -65,9 +48,6 @@ EOM
*** *** *** ***
*********************************************************** ***********************************************************
Used version:
$used_version
Exiting... Exiting...
EOM EOM
exit 0 exit 0

View file

@ -89,6 +89,8 @@ else
echo "'$previous' to '$TAG' ($count commits)" echo "'$previous' to '$TAG' ($count commits)"
# --> is the markdown comment escape sequence, emojis are way better # --> is the markdown comment escape sequence, emojis are way better
generated_list="${generated_list//-->/→}" generated_list="${generated_list//-->/→}"
# Escape & to preserve it from commit message into markdown output
generated_list="${generated_list//&/\\&}"
body="${body//--REPLACE-WITH-GENERATED-LIST--/$generated_list}" body="${body//--REPLACE-WITH-GENERATED-LIST--/$generated_list}"
body="${body//--REPLACE-WITH-COMMIT-COUNT--/$count}" body="${body//--REPLACE-WITH-COMMIT-COUNT--/$count}"
body="${body//--REPLACE-WITH-PREVIOUS-RELEASE-TAG--/$previous}" body="${body//--REPLACE-WITH-PREVIOUS-RELEASE-TAG--/$previous}"

View file

@ -3,7 +3,9 @@ AccessModifierOffset: -4
ColumnLimit: 120 ColumnLimit: 120
--- ---
Language: Cpp Language: Cpp
BreakBeforeBraces: Custom AllowAllParametersOfDeclarationOnNextLine: false
AllowShortFunctionsOnASingleLine: None
BinPackParameters: false
BraceWrapping: BraceWrapping:
AfterClass: true AfterClass: true
AfterControlStatement: false AfterControlStatement: false
@ -18,18 +20,14 @@ BraceWrapping:
SplitEmptyFunction: true SplitEmptyFunction: true
SplitEmptyRecord: true SplitEmptyRecord: true
SplitEmptyNamespace: true SplitEmptyNamespace: true
AllowShortFunctionsOnASingleLine: None BreakBeforeBraces: Custom
BinPackParameters: false
AllowAllParametersOfDeclarationOnNextLine: false
IndentCaseLabels: true
PointerAlignment: Right
SortIncludes: true
IncludeBlocks: Regroup IncludeBlocks: Regroup
StatementAttributeLikeMacros: [emit] IndentCaseLabels: true
# requires clang-format 15
InsertBraces: true InsertBraces: true
# requires clang-format 16 PointerAlignment: Right
# RemoveSemicolon: true RemoveSemicolon: true
SortIncludes: true
StatementAttributeLikeMacros: [emit]
--- ---
Language: Proto Language: Proto
AllowShortFunctionsOnASingleLine: None AllowShortFunctionsOnASingleLine: None

View file

@ -1,5 +0,0 @@
---
exclude_paths:
- '**/translations/*.ts'
# codacy config documentation: https://support.codacy.com/hc/en-us/articles/115002130625-Codacy-Configuration-File

View file

@ -209,6 +209,16 @@ nowadays and clean it up for you.
Lines should be 120 characters or less. Please break up lines that are too long Lines should be 120 characters or less. Please break up lines that are too long
into smaller parts, for example at spaces or after opening a brace. into smaller parts, for example at spaces or after opening a brace.
### Documentation Comments ###
Use [Doxygen](https://www.doxygen.nl/) for code documentation:
- **Doc blocks**: Use `/** @brief Description */` (Javadoc-style), not `///`
- **Member comments**: Use trailing `///<` for inline member documentation
- **TODOs**: Use `//! \todo Description` (Qt-style), Doxygen collects them into a Todo List
(uses [Qt-style comments](https://www.doxygen.nl/manual/docblocks.html) with
Doxygen's [\todo command](https://www.doxygen.nl/manual/commands.html#cmdtodo))
### Memory Management ### ### Memory Management ###
New code should be written using references over pointers and stack allocation New code should be written using references over pointers and stack allocation

View file

@ -1,10 +1,10 @@
name: Build Desktop name: Build Desktop
permissions: permissions:
actions: write # needed to delete entries in GHA cache (update ccache)
attestations: write # needed to persist the attestation.
contents: write contents: write
id-token: write id-token: write # needed for signing certificate in attestation
attestations: write
actions: write # needed for ccache action to be able to delete gha caches
on: on:
push: push:
@ -19,7 +19,7 @@ on:
- '.github/workflows/desktop-build.yml' - '.github/workflows/desktop-build.yml'
- 'CMakeLists.txt' - 'CMakeLists.txt'
- 'vcpkg.json' - 'vcpkg.json'
- 'vcpkg' - 'vcpkg' # needed to match submodule bumps (gitlink)
tags: tags:
- '*' - '*'
pull_request: pull_request:
@ -32,7 +32,7 @@ on:
- '.github/workflows/desktop-build.yml' - '.github/workflows/desktop-build.yml'
- 'CMakeLists.txt' - 'CMakeLists.txt'
- 'vcpkg.json' - 'vcpkg.json'
- 'vcpkg' - 'vcpkg' # needed to match submodule bumps (gitlink)
# Cancel earlier, unfinished runs of this workflow on the same branch (unless on release) # Cancel earlier, unfinished runs of this workflow on the same branch (unless on release)
concurrency: concurrency:
@ -44,216 +44,217 @@ jobs:
name: Configure name: Configure
runs-on: ubuntu-slim runs-on: ubuntu-slim
outputs: outputs:
tag: ${{steps.configure.outputs.tag}} tag: ${{ steps.configure.outputs.tag }}
sha: ${{steps.configure.outputs.sha}} sha: ${{ steps.configure.outputs.sha }}
steps: steps:
- name: Configure - name: "Configure"
env:
RESOLVED_SHA: ${{ case(github.event_name == 'pull_request', github.event.pull_request.head.sha, github.sha) }}
id: configure id: configure
shell: bash shell: bash
run: | run: |
tag_regex='^refs/tags/' if [[ "$GITHUB_REF_TYPE" == 'tag' ]]; then # release
if [[ $GITHUB_EVENT_NAME == pull-request ]]; then # pull request echo "tag=$GITHUB_REF_NAME" >> "$GITHUB_OUTPUT"
sha="${{github.event.pull_request.head.sha}}"
elif [[ $GITHUB_REF =~ $tag_regex ]]; then # release
sha="$GITHUB_SHA"
tag="${GITHUB_REF/refs\/tags\//}"
echo "tag=$tag" >>"$GITHUB_OUTPUT"
else # push to branch
sha="$GITHUB_SHA"
fi fi
echo "sha=$sha" >>"$GITHUB_OUTPUT" echo "sha=$RESOLVED_SHA" >> "$GITHUB_OUTPUT"
- name: Checkout - name: "Checkout"
if: steps.configure.outputs.tag != null if: steps.configure.outputs.tag != null
uses: actions/checkout@v6 uses: actions/checkout@v7
with: with:
fetch-depth: 0 fetch-depth: 0 # fetch all history for all branches and tags
- name: Prepare release parameters - name: "Prepare release parameters"
id: prepare id: prepare
if: steps.configure.outputs.tag != null if: steps.configure.outputs.tag != null
shell: bash shell: bash
env: env:
TAG: ${{steps.configure.outputs.tag}} TAG: ${{ steps.configure.outputs.tag }}
run: .ci/prep_release.sh run: .ci/prep_release.sh
- name: Create release - name: "Create release"
if: steps.configure.outputs.tag != null if: steps.configure.outputs.tag != null
id: create_release id: create_release
shell: bash shell: bash
env: env:
GH_TOKEN: ${{github.token}} GH_TOKEN: ${{ github.token }}
tag_name: ${{steps.configure.outputs.tag}} tag_name: ${{ steps.configure.outputs.tag }}
target: ${{steps.configure.outputs.sha}} target: ${{ steps.configure.outputs.sha }}
release_name: ${{steps.prepare.outputs.title}} release_name: ${{ steps.prepare.outputs.title }}
body_path: ${{steps.prepare.outputs.body_path}} body_path: ${{ steps.prepare.outputs.body_path }}
prerelease: ${{steps.prepare.outputs.is_beta}} prerelease: ${{ steps.prepare.outputs.is_beta }}
run: | run: |
if [[ $prerelease == yes ]]; then args=()
args="--prerelease" [[ $prerelease == yes ]] && args+=(--prerelease)
fi
gh release create "$tag_name" --draft --verify-tag $args \ gh release create "$tag_name" --verify-tag --draft "${args[@]}" \
--target "$target" --title "$release_name" \ --target "$target" \
--notes-file "$body_path" --title "$release_name" \
--notes-file "$body_path"
build-linux: build-linux:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
# These names correspond to the files in ".ci/$distro$version" # The files in ".ci/$distro$version" correspond to the values given here
include: include:
- distro: Arch - distro: Arch
package: skip # We are packaged in Arch already
allow-failure: yes allow-failure: yes
package: skip # We are packaged in Arch already
- distro: Servatrice_Debian - distro: Servatrice_Debian
version: 12 version: 12
package: DEB package: DEB
test: skip
server_only: yes server_only: yes
test: skip
- distro: Debian - distro: Debian
version: 12 version: 12
package: DEB package: DEB
test: skip # Running tests on all distros is superfluous test: skip # Running tests on all distros is superfluous
- distro: Debian - distro: Debian
version: 13 version: 13
package: DEB package: DEB
- distro: Fedora - distro: Fedora
version: 43 version: 43
package: RPM package: RPM
test: skip # Running tests on all distros is superfluous test: skip # Running tests on all distros is superfluous
- distro: Fedora - distro: Fedora
version: 44 version: 44
package: RPM package: RPM
- distro: Ubuntu - distro: Ubuntu
version: 24.04 version: 24.04
package: DEB package: DEB
test: skip # Running tests on all distros is superfluous test: skip # Running tests on all distros is superfluous
- distro: Ubuntu - distro: Ubuntu
version: 26.04 version: 26.04
package: DEB package: DEB
name: ${{matrix.distro}} ${{matrix.version}} name: ${{ matrix.distro }} ${{ matrix.version }}
needs: configure needs: configure
runs-on: ubuntu-latest runs-on: ubuntu-latest
continue-on-error: ${{matrix.allow-failure == 'yes'}} continue-on-error: ${{ matrix.allow-failure == 'yes' }}
timeout-minutes: 70 timeout-minutes: 70
env: env:
NAME: ${{matrix.distro}}${{matrix.version}} CACHE: ${{ github.workspace }}/.cache/${{ matrix.distro }}${{ matrix.version }} # directory for caching docker image and ccache
CACHE: ${{github.workspace}}/.cache/${{matrix.distro}}${{matrix.version}} # directory for caching docker image and ccache
# Cache size over the entire repo is 10Gi:
# https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy
CCACHE_SIZE: 550M
CCACHE_EVICTION_AGE: 7d CCACHE_EVICTION_AGE: 7d
CCACHE_SIZE: 550M # space of all repo is 10Gi: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy
CMAKE_GENERATOR: 'Ninja' CMAKE_GENERATOR: 'Ninja'
NAME: ${{ matrix.distro }}${{ matrix.version }}
steps: steps:
- name: Checkout - name: "Checkout"
uses: actions/checkout@v6 uses: actions/checkout@v7
- name: Restore compiler cache (ccache) - name: "Restore compiler cache (ccache)"
id: ccache_restore id: ccache_restore
uses: actions/cache/restore@v5 uses: actions/cache/restore@v6
env: env:
BRANCH_NAME: ${{ github.head_ref || github.ref_name }} BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
with: with:
path: ${{env.CACHE}} key: ccache-${{ matrix.distro }}${{ matrix.version }}-${{ env.BRANCH_NAME }}
key: ccache-${{matrix.distro}}${{matrix.version}}-${{env.BRANCH_NAME}} path: ${{ env.CACHE }}
restore-keys: ccache-${{matrix.distro}}${{matrix.version}}- restore-keys: ccache-${{ matrix.distro }}${{ matrix.version }}-
- name: Build ${{matrix.distro}} ${{matrix.version}} Docker image - name: "Build ${{ matrix.distro }} ${{ matrix.version }} Docker image"
shell: bash shell: bash
run: source .ci/docker.sh --build run: source .ci/docker.sh --build
- name: Build debug and test - name: "Build debug and test"
if: matrix.test != 'skip' if: matrix.test != 'skip'
shell: bash shell: bash
run: | run: |
source .ci/docker.sh source .ci/docker.sh
RUN --server --debug --test --ccache "$CCACHE_SIZE" \ RUN --server --debug --test --ccache "$CCACHE_SIZE" \
--cmake-generator "$CMAKE_GENERATOR" --cmake-generator "$CMAKE_GENERATOR"
- name: Build release package - name: "Build release package"
id: build id: build
if: matrix.package != 'skip' if: matrix.package != 'skip'
shell: bash shell: bash
env: env:
SUFFIX: '-${{matrix.distro}}${{matrix.version}}' SUFFIX: '-${{ matrix.distro }}${{ matrix.version }}'
package: '${{matrix.package}}' package: '${{ matrix.package }}'
server_only: '${{matrix.server_only}}' server_only: '${{ matrix.server_only }}'
run: | run: |
source .ci/docker.sh source .ci/docker.sh
args=() args=()
if [[ $server_only == yes ]]; then [[ $server_only == yes ]] && args+=(--no-client)
args+=(--no-client) [[ $GITHUB_REF == "refs/heads/master" ]] && args+=(--evict-ccache "$CCACHE_EVICTION_AGE")
fi
if [[ $GITHUB_REF == "refs/heads/master" ]]; then
args+=(--evict-ccache "$CCACHE_EVICTION_AGE")
fi
args+=(--ccache "$CCACHE_SIZE") args+=(--ccache "$CCACHE_SIZE")
args+=(--cmake-generator "$CMAKE_GENERATOR") args+=(--cmake-generator "$CMAKE_GENERATOR")
args+=(--suffix "$SUFFIX") args+=(--suffix "$SUFFIX")
RUN --server --release --package "$package" "${args[@]}" RUN --server --release --package "$package" "${args[@]}"
# Delete used cache to emulate a ccache update. See https://github.com/actions/cache/issues/342 # Delete used cache to emulate a ccache update. See https://github.com/actions/cache/issues/342
- name: Delete remote compiler cache (ccache) - name: "Delete remote compiler cache (ccache)"
if: github.ref == 'refs/heads/master' && steps.ccache_restore.outputs.cache-hit if: github.ref == 'refs/heads/master' && steps.ccache_restore.outputs.cache-hit
continue-on-error: true continue-on-error: true
env: env:
CACHE_PRIMARY_KEY: ${{ steps.ccache_restore.outputs.cache-primary-key }}
GH_TOKEN: ${{ github.token }} GH_TOKEN: ${{ github.token }}
run: | run: |
if gh cache delete --repo ${{ github.repository }} ${{ steps.ccache_restore.outputs.cache-primary-key }}; then if gh cache delete --repo "$GITHUB_REPOSITORY" "$CACHE_PRIMARY_KEY"; then
echo "Cache deleted successfully" echo "Cache deleted successfully"
fi fi
- name: Save updated compiler cache (ccache) - name: "Save updated compiler cache (ccache)"
if: github.ref == 'refs/heads/master' if: github.ref == 'refs/heads/master'
uses: actions/cache/save@v5 uses: actions/cache/save@v6
with: with:
path: ${{env.CACHE}}
key: ${{ steps.ccache_restore.outputs.cache-primary-key }} key: ${{ steps.ccache_restore.outputs.cache-primary-key }}
path: ${{ env.CACHE }}
- name: Upload artifact - name: "Upload artifact"
id: upload_artifact id: upload_artifact
if: matrix.package != 'skip' if: matrix.package != 'skip'
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v7
with: with:
path: ${{steps.build.outputs.path}}
archive: false archive: false
if-no-files-found: error if-no-files-found: error
path: ${{ steps.build.outputs.path }}
- name: Upload to release - name: "Upload to release"
id: upload_release id: upload_release
if: matrix.package != 'skip' && needs.configure.outputs.tag != null if: matrix.package != 'skip' && needs.configure.outputs.tag != null
shell: bash shell: bash
env: env:
GH_TOKEN: ${{github.token}} asset_name: ${{ steps.build.outputs.fullname }}
tag_name: ${{needs.configure.outputs.tag}} asset_path: ${{ steps.build.outputs.path }}
asset_name: ${{steps.build.outputs.fullname}} GH_TOKEN: ${{ github.token }}
asset_path: ${{steps.build.outputs.path}} tag_name: ${{ needs.configure.outputs.tag }}
run: gh release upload "$tag_name" "$asset_path#$asset_name" run: gh release upload "$tag_name" "$asset_path#$asset_name"
- name: Attest binary provenance - name: "Attest binary provenance"
id: attestation id: attestation
if: steps.upload_release.outcome == 'success' if: steps.upload_release.outcome == 'success'
uses: actions/attest@v4 uses: actions/attest@v4
with: with:
subject-path: ${{steps.build.outputs.path}}
show-summary: false show-summary: false
subject-path: ${{ steps.build.outputs.path }}
- name: Verify binary attestation - name: "Verify binary attestation"
if: steps.attestation.outcome == 'success' if: steps.attestation.outcome == 'success'
shell: bash shell: bash
env: env:
GH_TOKEN: ${{github.token}} BUILD_PATH: ${{ steps.build.outputs.path }}
run: gh attestation verify "${{steps.build.outputs.path}}" --repo Cockatrice/Cockatrice GH_TOKEN: ${{ github.token }}
run: gh attestation verify "$BUILD_PATH" --repo Cockatrice/Cockatrice
build-vcpkg: build-vcpkg:
strategy: strategy:
@ -263,234 +264,234 @@ jobs:
- os: macOS - os: macOS
target: 13 target: 13
runner: macos-15-intel runner: macos-15-intel
soc: Intel
xcode: "16.4" ccache_eviction_age: 7d
type: Release cmake_generator: Ninja
override_target: 13
make_package: 1 make_package: 1
override_target: 13
package_suffix: "-macOS13_Intel" package_suffix: "-macOS13_Intel"
qt_version: 6.11.0 qt_version: 6.11.0
qt_arch: clang_64
qt_modules: qtimageformats qtmultimedia qtwebsockets qt_modules: qtimageformats qtmultimedia qtwebsockets
cmake_generator: Ninja soc: Intel
type: Release
use_ccache: 1 use_ccache: 1
ccache_eviction_age: 7d xcode: "16.4"
- os: macOS - os: macOS
target: 14 target: 14
runner: macos-14 runner: macos-14
soc: Apple
xcode: "15.4" ccache_eviction_age: 7d
type: Release cmake_generator: Ninja
make_package: 1 make_package: 1
package_suffix: "-macOS14" package_suffix: "-macOS14"
qt_version: 6.11.0 qt_version: 6.11.0
qt_arch: clang_64
qt_modules: qtimageformats qtmultimedia qtwebsockets qt_modules: qtimageformats qtmultimedia qtwebsockets
cmake_generator: Ninja soc: Apple
type: Release
use_ccache: 1 use_ccache: 1
ccache_eviction_age: 7d xcode: "15.4"
- os: macOS - os: macOS
target: 15 target: 15
runner: macos-15 runner: macos-15
soc: Apple
xcode: "16.4" ccache_eviction_age: 7d
type: Release cmake_generator: Ninja
make_package: 1 make_package: 1
package_suffix: "-macOS15" package_suffix: "-macOS15"
qt_version: 6.11.0 qt_version: 6.11.0
qt_arch: clang_64
qt_modules: qtimageformats qtmultimedia qtwebsockets qt_modules: qtimageformats qtmultimedia qtwebsockets
cmake_generator: Ninja soc: Apple
type: Release
use_ccache: 1 use_ccache: 1
ccache_eviction_age: 7d xcode: "16.4"
- os: macOS - os: macOS
target: 15 target: 15
runner: macos-15 runner: macos-15
soc: Apple
xcode: "16.4"
type: Debug
qt_version: 6.11.0
qt_arch: clang_64
qt_modules: qtimageformats qtmultimedia qtwebsockets
cmake_generator: Ninja
use_ccache: 1
ccache_eviction_age: 7d ccache_eviction_age: 7d
cmake_generator: Ninja
qt_version: 6.11.0
qt_modules: qtimageformats qtmultimedia qtwebsockets
soc: Apple
type: Debug
use_ccache: 1
xcode: "16.4"
- os: Windows - os: Windows
target: 10 target: 10
runner: windows-2025 runner: windows-2025
type: Release
cmake_generator: "Visual Studio 18 2026"
cmake_generator_platform: x64
make_package: 1 make_package: 1
package_suffix: "-Win10" package_suffix: "-Win10"
qt_version: 6.11.0 qt_version: 6.11.0
qt_arch: win64_msvc2022_64
qt_modules: qtimageformats qtmultimedia qtwebsockets qt_modules: qtimageformats qtmultimedia qtwebsockets
cmake_generator: "Visual Studio 17 2022" type: Release
cmake_generator_platform: x64
name: ${{matrix.os}} ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }} name: ${{ matrix.os }} ${{ matrix.target }}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }}
needs: configure needs: configure
runs-on: ${{matrix.runner}} runs-on: ${{ matrix.runner }}
timeout-minutes: 100 timeout-minutes: 100
env: env:
CCACHE_DIR: ${{github.workspace}}/.cache/ CCACHE_DIR: ${{ github.workspace }}/.cache/
# Cache size over the entire repo is 10Gi: CCACHE_SIZE: 550M # space of all repo is 10Gi: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy
# https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy
CCACHE_SIZE: 550M
steps: steps:
- name: Checkout - name: "Checkout"
uses: actions/checkout@v6 uses: actions/checkout@v7
with: with:
submodules: recursive submodules: recursive
- name: Add msbuild to PATH - name: "[Windows] Add msbuild to PATH"
if: matrix.os == 'Windows' if: matrix.os == 'Windows'
id: add-msbuild id: add-msbuild
uses: microsoft/setup-msbuild@v3 uses: microsoft/setup-msbuild@v3
with: with:
msbuild-architecture: x64 msbuild-architecture: x64
- name: Setup ccache - name: "[macOS] Setup ccache"
if: matrix.use_ccache == 1 && matrix.os == 'macOS' if: matrix.os == 'macOS' && matrix.use_ccache == 1
run: brew install ccache run: brew install ccache
- name: Restore compiler cache (ccache) - name: "[macOS] Restore compiler cache (ccache)"
if: matrix.use_ccache == 1 if: matrix.os == 'macOS' && matrix.use_ccache == 1
id: ccache_restore id: ccache_restore
uses: actions/cache/restore@v5 uses: actions/cache/restore@v6
env: env:
BRANCH_NAME: ${{ github.head_ref || github.ref_name }} BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
with: with:
path: ${{env.CCACHE_DIR}} key: ccache-${{ matrix.runner }}-${{ matrix.soc }}-${{ matrix.type }}-${{ env.BRANCH_NAME }}
key: ccache-${{matrix.runner}}-${{matrix.soc}}-${{matrix.type}}-${{env.BRANCH_NAME}} path: ${{ env.CCACHE_DIR }}
restore-keys: ccache-${{matrix.runner}}-${{matrix.soc}}-${{matrix.type}}- restore-keys: ccache-${{ matrix.runner }}-${{ matrix.soc }}-${{ matrix.type }}-
- name: Install aqtinstall - name: "Install aqtinstall"
run: pipx install aqtinstall run: pipx install aqtinstall
# Resolve given wildcard versions (e.g. Qt 6.6.*) to latest version via aqtinstall to avoid stale caches on new releases # Resolve given wildcard versions (e.g. Qt 6.6.*) to latest version via aqtinstall to avoid stale caches on new releases
- name: Resolve latest Qt patch version - name: "Resolve latest Qt patch version"
env:
QT_VERSION: ${{ matrix.qt_version }}
id: resolve_qt_version id: resolve_qt_version
shell: bash shell: bash
run: .ci/resolve_latest_aqt_qt_version.sh "${{matrix.qt_version}}" run: .ci/resolve_latest_aqt_qt_version.sh "$QT_VERSION"
- name: Restore thin Qt ${{ steps.resolve_qt_version.outputs.version }} libraries (${{ matrix.soc }} macOS) - name: "[macOS] Restore thin Qt ${{ steps.resolve_qt_version.outputs.version }} libraries"
if: matrix.os == 'macOS' if: matrix.os == 'macOS'
id: restore_qt id: restore_qt
uses: actions/cache/restore@v5 uses: actions/cache/restore@v6
with: with:
path: ${{ github.workspace }}/Qt
key: thin-qt-macos-${{ matrix.soc }}-${{ steps.resolve_qt_version.outputs.version }} key: thin-qt-macos-${{ matrix.soc }}-${{ steps.resolve_qt_version.outputs.version }}
path: ${{ github.workspace }}/Qt
# Using jurplel/install-qt-action to install Qt without using brew # Using jurplel/install-qt-action to install Qt without using brew
# qt build using vcpkg either just fails or takes too long to build # Qt build using vcpkg either just fails or takes too long to build
- name: Install fat Qt ${{ steps.resolve_qt_version.outputs.version }} (${{ matrix.soc }} macOS) - name: "[macOS] Install fat Qt ${{ steps.resolve_qt_version.outputs.version }}"
if: matrix.os == 'macOS' && steps.restore_qt.outputs.cache-hit != 'true' if: matrix.os == 'macOS' && steps.restore_qt.outputs.cache-hit != 'true'
uses: jurplel/install-qt-action@v4 uses: jurplel/install-qt-action@v4
with: with:
version: ${{ steps.resolve_qt_version.outputs.version }}
arch: ${{matrix.qt_arch}}
modules: ${{matrix.qt_modules}}
cache: false cache: false
dir: ${{github.workspace}} dir: ${{ github.workspace }}
modules: ${{ matrix.qt_modules }}
version: ${{ steps.resolve_qt_version.outputs.version }}
- name: Thin Qt libraries (${{ matrix.soc }} macOS) - name: "[macOS] Create thin Qt libraries"
if: matrix.os == 'macOS' && steps.restore_qt.outputs.cache-hit != 'true' if: matrix.os == 'macOS' && steps.restore_qt.outputs.cache-hit != 'true'
run: .ci/thin_macos_qtlib.sh run: .ci/thin_macos_qtlib.sh
- name: Cache thin Qt libraries (${{ matrix.soc }} macOS) - name: "[macOS] Cache thin Qt libraries"
if: matrix.os == 'macOS' && steps.restore_qt.outputs.cache-hit != 'true' if: matrix.os == 'macOS' && steps.restore_qt.outputs.cache-hit != 'true'
uses: actions/cache/save@v5 uses: actions/cache/save@v6
with: with:
path: ${{ github.workspace }}/Qt
key: thin-qt-macos-${{ matrix.soc }}-${{ steps.resolve_qt_version.outputs.version }} key: thin-qt-macos-${{ matrix.soc }}-${{ steps.resolve_qt_version.outputs.version }}
path: ${{ github.workspace }}/Qt
- name: Install Qt ${{matrix.qt_version}} (Windows) - name: "[Windows] Install Qt ${{ matrix.qt_version }}"
if: matrix.os == 'Windows' if: matrix.os == 'Windows'
uses: jurplel/install-qt-action@v4 uses: jurplel/install-qt-action@v4
with: with:
# qt 6.11.0 only works with aqtinstall directly from git until aqtinstall 3.4 is released # Qt 6.11.0 only works with aqtinstall directly from git until aqtinstall 3.4 is released
aqtsource: git+https://github.com/miurahr/aqtinstall.git aqtsource: git+https://github.com/miurahr/aqtinstall.git
version: ${{ steps.resolve_qt_version.outputs.version }}
arch: ${{matrix.qt_arch}}
modules: ${{matrix.qt_modules}}
cache: true cache: true
modules: ${{ matrix.qt_modules }}
version: ${{ steps.resolve_qt_version.outputs.version }}
- name: Install NSIS - name: "[Windows] Install NSIS"
if: matrix.os == 'Windows' if: matrix.os == 'Windows'
shell: bash shell: bash
run: choco install nsis run: choco install nsis
- name: Setup vcpkg cache - name: "Setup vcpkg cache"
id: vcpkg-cache id: vcpkg-cache
uses: TAServers/vcpkg-cache@v3 uses: TAServers/vcpkg-cache@v3
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
# uses environment variables, see compile.sh for more details # Uses environment variables, see compile.sh for more details
- name: Build Cockatrice - name: "Build Cockatrice"
id: build id: build
shell: bash shell: bash
env: env:
BUILDTYPE: '${{matrix.type}}' BUILDTYPE: '${{ matrix.type }}'
MAKE_PACKAGE: '${{matrix.make_package}}'
PACKAGE_SUFFIX: '${{matrix.package_suffix}}'
CMAKE_GENERATOR: ${{matrix.cmake_generator}}
CMAKE_GENERATOR_PLATFORM: ${{matrix.cmake_generator_platform}}
USE_CCACHE: ${{matrix.use_ccache}}
VCPKG_DISABLE_METRICS: 1
VCPKG_BINARY_SOURCES: 'clear;files,${{ steps.vcpkg-cache.outputs.path }},readwrite'
# macOS-specific environment variables, will be ignored on Windows
MACOS_CERTIFICATE: ${{ secrets.PROD_MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PWD: ${{ secrets.PROD_MACOS_CERTIFICATE_PWD }}
MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}
MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}
DEVELOPER_DIR: '/Applications/Xcode_${{matrix.xcode}}.app/Contents/Developer'
TARGET_MACOS_VERSION: ${{ matrix.override_target }}
CCACHE_EVICTION_AGE: ${{ matrix.ccache_eviction_age }} CCACHE_EVICTION_AGE: ${{ matrix.ccache_eviction_age }}
CMAKE_GENERATOR: ${{ matrix.cmake_generator }}
CMAKE_GENERATOR_PLATFORM: ${{ matrix.cmake_generator_platform }}
DEVELOPER_DIR: '/Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer'
MACOS_CERTIFICATE: ${{ secrets.PROD_MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}
MACOS_CERTIFICATE_PWD: ${{ secrets.PROD_MACOS_CERTIFICATE_PWD }}
MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}
MAKE_PACKAGE: '${{ matrix.make_package }}'
PACKAGE_SUFFIX: '${{ matrix.package_suffix }}'
TARGET_MACOS_VERSION: ${{ matrix.override_target }}
USE_CCACHE: ${{ matrix.use_ccache }}
VCPKG_BINARY_SOURCES: 'clear;files,${{ steps.vcpkg-cache.outputs.path }},readwrite'
VCPKG_DISABLE_METRICS: 1
run: .ci/compile.sh --server --test --vcpkg run: .ci/compile.sh --server --test --vcpkg
# Delete used cache to emulate a ccache update. See https://github.com/actions/cache/issues/342 # Delete used cache to emulate a ccache update. See https://github.com/actions/cache/issues/342
- name: Delete remote compiler cache (ccache) - name: "[macOS] Delete remote compiler cache (ccache)"
if: github.ref == 'refs/heads/master' && matrix.use_ccache == 1 && steps.ccache_restore.outputs.cache-hit if: matrix.os == 'macOS' && matrix.use_ccache == 1 && github.ref == 'refs/heads/master' && steps.ccache_restore.outputs.cache-hit
continue-on-error: true continue-on-error: true
env: env:
CACHE_PRIMARY_KEY: ${{ steps.ccache_restore.outputs.cache-primary-key }}
GH_TOKEN: ${{ github.token }} GH_TOKEN: ${{ github.token }}
run: | run: |
if gh cache delete --repo ${{ github.repository }} ${{ steps.ccache_restore.outputs.cache-primary-key }}; then if gh cache delete --repo "$GITHUB_REPOSITORY" "$CACHE_PRIMARY_KEY"; then
echo "Cache deleted successfully" echo "Cache deleted successfully"
fi fi
- name: Save updated compiler cache (ccache) - name: "[macOS] Save updated compiler cache (ccache)"
if: github.ref == 'refs/heads/master' && matrix.use_ccache == 1 if: matrix.os == 'macOS' && matrix.use_ccache == 1 && github.ref == 'refs/heads/master'
uses: actions/cache/save@v5 uses: actions/cache/save@v6
with: with:
path: ${{env.CCACHE_DIR}}
key: ${{ steps.ccache_restore.outputs.cache-primary-key }} key: ${{ steps.ccache_restore.outputs.cache-primary-key }}
path: ${{ env.CCACHE_DIR }}
- name: Sign app bundle - name: "[macOS] Sign app bundle"
if: matrix.os == 'macOS' && matrix.make_package && needs.configure.outputs.tag != null if: matrix.os == 'macOS' && matrix.make_package && needs.configure.outputs.tag != null
id: sign_macos id: sign_macos
env: env:
BUILD_PATH: ${{ steps.build.outputs.path }}
MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }} MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}
MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }} MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}
run: | run: |
if [[ -n "$MACOS_CERTIFICATE_NAME" ]] if [[ -n "$MACOS_CERTIFICATE_NAME" ]]
then then
security unlock-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain security unlock-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain
/usr/bin/codesign --sign="$MACOS_CERTIFICATE_NAME" --entitlements=".ci/macos.entitlements" --options=runtime --force --deep --timestamp --verbose "${{steps.build.outputs.path}}" /usr/bin/codesign --sign="$MACOS_CERTIFICATE_NAME" --entitlements=".ci/macos.entitlements" --options=runtime --force --deep --timestamp --verbose "$BUILD_PATH"
fi fi
- name: Notarize app bundle - name: "[macOS] Notarize app bundle"
if: steps.sign_macos.outcome == 'success' if: matrix.os == 'macOS' && steps.sign_macos.outcome == 'success'
env: env:
BUILD_PATH: ${{ steps.build.outputs.path }}
MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }} MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }}
MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }}
MACOS_NOTARIZATION_PWD: ${{ secrets.PROD_MACOS_NOTARIZATION_PWD }} MACOS_NOTARIZATION_PWD: ${{ secrets.PROD_MACOS_NOTARIZATION_PWD }}
MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }}
run: | run: |
if [[ -n "$MACOS_NOTARIZATION_APPLE_ID" ]] if [[ -n "$MACOS_NOTARIZATION_APPLE_ID" ]]
then then
@ -502,7 +503,7 @@ jobs:
# Therefore, we create a zip file containing our app bundle, so that we can send it to the # Therefore, we create a zip file containing our app bundle, so that we can send it to the
# notarization service # notarization service
echo "Creating temp notarization archive" echo "Creating temp notarization archive"
ditto -c -k --keepParent "${{steps.build.outputs.path}}" "notarization.zip" ditto -c -k --keepParent "$BUILD_PATH" "notarization.zip"
# Here we send the notarization request to the Apple's Notarization service, waiting for the result. # Here we send the notarization request to the Apple's Notarization service, waiting for the result.
# This typically takes a few seconds inside a CI environment, but it might take more depending on the App # This typically takes a few seconds inside a CI environment, but it might take more depending on the App
@ -514,51 +515,52 @@ jobs:
# Finally, we need to "attach the staple" to our executable, which will allow our app to be # Finally, we need to "attach the staple" to our executable, which will allow our app to be
# validated by macOS even when an internet connection is not available. # validated by macOS even when an internet connection is not available.
echo "Attach staple" echo "Attach staple"
xcrun stapler staple "${{steps.build.outputs.path}}" xcrun stapler staple "$BUILD_PATH"
fi fi
- name: Upload artifact - name: "Upload artifact"
if: matrix.make_package if: matrix.make_package
id: upload_artifact id: upload_artifact
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v7
with: with:
path: ${{steps.build.outputs.path}}
archive: false archive: false
if-no-files-found: error if-no-files-found: error
path: ${{ steps.build.outputs.path }}
- name: Upload PDBs (Program Databases) - name: "[Windows] Upload PDBs (Program Databases)"
if: matrix.os == 'Windows' && github.ref_type != 'tag' if: matrix.os == 'Windows' && github.ref_type != 'tag'
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v7
with: with:
name: ${{steps.build.outputs.name}}-PDBs if-no-files-found: error
name: ${{ steps.build.outputs.name }}-PDBs
path: | path: |
build/cockatrice/Release/*.pdb build/cockatrice/Release/*.pdb
build/oracle/Release/*.pdb build/oracle/Release/*.pdb
build/servatrice/Release/*.pdb build/servatrice/Release/*.pdb
if-no-files-found: error
- name: Upload to release - name: "Upload to release"
if: needs.configure.outputs.tag != null && matrix.make_package == '1' if: needs.configure.outputs.tag != null && matrix.make_package == '1'
id: upload_release id: upload_release
shell: bash shell: bash
env: env:
GH_TOKEN: ${{github.token}} asset_name: ${{ steps.build.outputs.fullname }}
tag_name: ${{needs.configure.outputs.tag}} asset_path: ${{ steps.build.outputs.path }}
asset_name: ${{steps.build.outputs.fullname}} GH_TOKEN: ${{ github.token }}
asset_path: ${{steps.build.outputs.path}} tag_name: ${{ needs.configure.outputs.tag }}
run: gh release upload "$tag_name" "$asset_path#$asset_name" run: gh release upload "$tag_name" "$asset_path#$asset_name"
- name: Attest binary provenance - name: "Attest binary provenance"
if: steps.upload_release.outcome == 'success' if: steps.upload_release.outcome == 'success'
id: attestation id: attestation
uses: actions/attest@v4 uses: actions/attest@v4
with: with:
subject-path: ${{steps.build.outputs.path}}
show-summary: false show-summary: false
subject-path: ${{ steps.build.outputs.path }}
- name: Verify binary attestation - name: "Verify binary attestation"
if: steps.attestation.outcome == 'success' if: steps.attestation.outcome == 'success'
shell: bash shell: bash
env: env:
GH_TOKEN: ${{github.token}} BUILD_PATH: ${{ steps.build.outputs.path }}
run: gh attestation verify "${{steps.build.outputs.path}}" --repo Cockatrice/Cockatrice GH_TOKEN: ${{ github.token }}
run: gh attestation verify "$BUILD_PATH" --repo Cockatrice/Cockatrice

View file

@ -1,7 +1,7 @@
name: Code Style (C++) name: Code Style (C++)
on: on:
# push trigger not needed for linting, we do not allow direct pushes to master # Push trigger not needed for linting, we do not allow direct pushes to master
pull_request: pull_request:
paths: paths:
- '*/**' # matches all files not in root - '*/**' # matches all files not in root
@ -21,17 +21,20 @@ jobs:
runs-on: ubuntu-slim runs-on: ubuntu-slim
steps: steps:
- name: Checkout - name: "Checkout"
uses: actions/checkout@v6 uses: actions/checkout@v7
with: with:
fetch-depth: 20 # should be enough to find merge base fetch-depth: 20 # should be enough to find merge base
- name: Install dependencies - name: "Install dependencies"
shell: bash shell: bash
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y --no-install-recommends clang-format cmake-format shellcheck sudo apt-get install -y --no-install-recommends \
clang-format \
cmake-format \
shellcheck
- name: Check code formatting - name: "Check code formatting"
shell: bash shell: bash
run: ./.ci/lint_cpp.sh run: ./.ci/lint_cpp.sh

View file

@ -1,9 +1,10 @@
name: Build Docker Image name: Build Docker Image
permissions:
contents: read
packages: write
on: on:
release:
types:
- released # publishing of stable releases
push: push:
branches: branches:
- master - master
@ -13,6 +14,9 @@ on:
paths: paths:
- '.github/workflows/docker-release.yml' - '.github/workflows/docker-release.yml'
- 'Dockerfile' - 'Dockerfile'
release:
types:
- released # publishing of stable releases
# Cancel earlier, unfinished runs of this workflow on the same branch (unless on release) # Cancel earlier, unfinished runs of this workflow on the same branch (unless on release)
concurrency: concurrency:
@ -24,54 +28,51 @@ jobs:
name: amd64 & arm64 name: amd64 & arm64
if: ${{ github.repository_owner == 'Cockatrice' }} if: ${{ github.repository_owner == 'Cockatrice' }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps: steps:
- name: Checkout - name: "Checkout"
uses: actions/checkout@v6 uses: actions/checkout@v7
- name: Docker metadata - name: "Docker metadata"
id: metadata id: metadata
uses: docker/metadata-action@v6 uses: docker/metadata-action@v6
env: env:
DOCKER_METADATA_ANNOTATIONS_LEVELS: index # needed for GHCR DOCKER_METADATA_ANNOTATIONS_LEVELS: index # needed for GHCR
with: with:
annotations: |
org.opencontainers.image.title=Servatrice
org.opencontainers.image.url=https://cockatrice.github.io/
org.opencontainers.image.description=Server for Cockatrice, a cross-platform virtual tabletop for multiplayer card games
images: | images: |
ghcr.io/cockatrice/servatrice ghcr.io/cockatrice/servatrice
labels: | labels: |
org.opencontainers.image.title=Servatrice org.opencontainers.image.title=Servatrice
org.opencontainers.image.url=https://cockatrice.github.io/ org.opencontainers.image.url=https://cockatrice.github.io/
org.opencontainers.image.description=Server for Cockatrice, a cross-platform virtual tabletop for multiplayer card games org.opencontainers.image.description=Server for Cockatrice, a cross-platform virtual tabletop for multiplayer card games
annotations: |
org.opencontainers.image.title=Servatrice
org.opencontainers.image.url=https://cockatrice.github.io/
org.opencontainers.image.description=Server for Cockatrice, a cross-platform virtual tabletop for multiplayer card games
- name: Set up QEMU - name: "Set up QEMU"
uses: docker/setup-qemu-action@v4 uses: docker/setup-qemu-action@v4
- name: Set up Docker buildx - name: "Set up Docker buildx"
uses: docker/setup-buildx-action@v4 uses: docker/setup-buildx-action@v4
- name: Login to GitHub Container Registry - name: "Login to GitHub Container Registry (GHCR)"
if: contains(github.event.release.tag_name, 'Release') && github.event.release.target_commitish == 'master' if: github.event_name == 'release' && github.event.release.prerelease == false
id: login
uses: docker/login-action@v4 uses: docker/login-action@v4
with: with:
password: ${{ github.token }}
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ github.token }}
- name: Build and push Docker image - name: "Build and push Docker image"
uses: docker/build-push-action@v7 uses: docker/build-push-action@v7
with: with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.ref_type == 'tag' }}
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}
annotations: ${{ steps.metadata.outputs.annotations }} annotations: ${{ steps.metadata.outputs.annotations }}
cache-from: type=gha,scope=servatrice cache-from: type=gha,scope=servatrice
cache-to: type=gha,mode=max,scope=servatrice cache-to: type=gha,mode=max,scope=servatrice
context: .
labels: ${{ steps.metadata.outputs.labels }}
platforms: linux/amd64,linux/arm64
push: ${{ steps.login.outcome == 'success' }}
tags: ${{ steps.metadata.outputs.tags }}

View file

@ -1,18 +1,18 @@
name: Generate Docs name: Generate Docs
on: on:
release:
types:
- published # publishing of stable releases and pre-releases
pull_request: pull_request:
paths: paths:
- 'doc/doxygen/**' - 'doc/doxygen/**'
- '.github/workflows/documentation-build.yml' - '.github/workflows/documentation-build.yml'
- 'Doxyfile' - 'Doxyfile'
release:
types:
- published # publishing of stable releases and pre-releases
workflow_dispatch: workflow_dispatch:
env: env:
COCKATRICE_REF: ${{ github.ref_name }} # Tag name if the commit is tagged, otherwise branch name COCKATRICE_REF: ${{ github.ref_name }} # tag name if the commit is tagged, otherwise branch name
jobs: jobs:
docs: docs:
@ -20,22 +20,22 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: "Checkout code"
uses: actions/checkout@v6 uses: actions/checkout@v7
with: with:
submodules: recursive submodules: recursive
- name: Install Graphviz - name: "Install Graphviz"
run: | run: |
sudo apt-get install -y graphviz sudo apt-get install -y graphviz
dot -V dot -V
- name: Install Doxygen - name: "Install Doxygen"
uses: ssciwr/doxygen-install@v2 uses: ssciwr/doxygen-install@v2
with: with:
version: "1.16.1" version: "1.16.1"
- name: Update Doxygen Configuration - name: "Update Doxygen Configuration"
run: | run: |
git diff Doxyfile git diff Doxyfile
doxygen -u Doxyfile doxygen -u Doxyfile
@ -48,16 +48,16 @@ jobs:
exit 1 exit 1
fi fi
- name: Generate Documentation - name: "Generate Documentation"
if: always() if: always()
run: doxygen Doxyfile run: doxygen Doxyfile
- name: Deploy to cockatrice.github.io - name: "Deploy to cockatrice.github.io"
if: github.event_name == 'release' || github.event_name == 'workflow_dispatch' if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
uses: peaceiris/actions-gh-pages@v4 uses: peaceiris/actions-gh-pages@v4
with: with:
deploy_key: ${{ secrets.DOCS_DEPLOY_KEY }} deploy_key: ${{ secrets.DOCS_DEPLOY_KEY }}
destination_dir: docs # docs will be available at https://cockatrice.github.io/docs/
external_repository: Cockatrice/cockatrice.github.io external_repository: Cockatrice/cockatrice.github.io
publish_branch: master publish_branch: master
publish_dir: ./docs/html publish_dir: ./docs/html
destination_dir: docs # Docs will live under https://cockatrice.github.io/docs/

View file

@ -1,14 +1,14 @@
name: Update Translations name: Update Translations
on: on:
workflow_dispatch:
schedule:
# runs in the middle of each month starting a quarter (UTC) = two weeks after new strings are built
- cron: '0 0 15 1,4,7,10 *'
pull_request: pull_request:
paths: paths:
- '.tx/**' - '.tx/**'
- '.github/workflows/translations-pull.yml' - '.github/workflows/translations-pull.yml'
schedule:
# Runs in the middle of each month starting a quarter (UTC) = two weeks after new strings are built
- cron: '0 0 15 1,4,7,10 *'
workflow_dispatch:
jobs: jobs:
translations: translations:
@ -19,18 +19,20 @@ jobs:
runs-on: ubuntu-slim runs-on: ubuntu-slim
steps: steps:
- name: Checkout repo - name: "Checkout repo"
uses: actions/checkout@v6 uses: actions/checkout@v7
- name: Pull translated strings from Transifex - name: "Pull translated strings from Transifex"
# Do not run this step for PR's from forks, they don't have access to the secret
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false
uses: transifex/cli-action@v2 uses: transifex/cli-action@v2
with: with:
# used config file: https://github.com/Cockatrice/Cockatrice/blob/master/.tx/config # Used config file: https://github.com/Cockatrice/Cockatrice/blob/master/.tx/config
# https://github.com/transifex/cli#pulling-files-from-transifex # Docs: https://github.com/transifex/cli#pulling-files-from-transifex
token: ${{ secrets.TX_TOKEN }}
args: pull --force --all args: pull --force --all
token: ${{ secrets.TX_TOKEN }}
- name: Create pull request - name: "Create pull request"
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
id: create_pr id: create_pr
uses: peter-evans/create-pull-request@v8 uses: peter-evans/create-pull-request@v8
@ -38,35 +40,32 @@ jobs:
add-paths: | add-paths: |
cockatrice/translations/*.ts cockatrice/translations/*.ts
oracle/translations/*.ts oracle/translations/*.ts
commit-message: Update translation files author: github-actions <github-actions@github.com> # owner of the commit
# author is the owner of the commit
author: github-actions <github-actions@github.com>
branch: ci-update_translations
delete-branch: true
title: 'Update translations'
body: | body: |
Pulled all translated strings from [Transifex][1]. Pulled all translated strings from [Transifex][1].
--- ---
*This PR is automatically generated and updated by the workflow at `.github/workflows/translations-pull.yml`. Review [action runs][2].*<br> *This PR is automatically generated and updated by the workflow at `.github/workflows/translations-pull.yml`. Review [action runs][2].*<br>
*After merging, all new languages and translations are available in the next build.* *After merging, all new languages and translations are available in the next build.*
[1]: https://explore.transifex.com/cockatrice/cockatrice/ [1]: https://explore.transifex.com/cockatrice/cockatrice/
[2]: https://github.com/Cockatrice/Cockatrice/actions/workflows/translations-pull.yml?query=branch%3Amaster [2]: https://github.com/Cockatrice/Cockatrice/actions/workflows/translations-pull.yml?query=branch%3Amaster
branch: ci-update_translations
commit-message: Update translation files
delete-branch: true
draft: false
labels: | labels: |
CI CI
Translation Translation
draft: false title: 'Update translations'
- name: PR Status - name: "PR Status"
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
shell: bash shell: bash
env: env:
STATUS: ${{ steps.create_pr.outputs.pull-request-operation }} PR_NUMBER: ${{ steps.create_pr.outputs.pull-request-number }}
PR_URL: ${{ steps.create_pr.outputs.pull-request-url }}
STATUS: ${{ case(steps.create_pr.outputs.pull-request-operation == 'none', 'unchanged', steps.create_pr.outputs.pull-request-operation) }}
run: | run: |
if [[ "$STATUS" == "none" ]]; then echo "PR #$PR_NUMBER $STATUS!" >> "$GITHUB_STEP_SUMMARY"
echo "PR #${{ steps.create_pr.outputs.pull-request-number }} unchanged!" >> $GITHUB_STEP_SUMMARY echo "URL: $PR_URL" >> "$GITHUB_STEP_SUMMARY"
else
echo "PR #${{ steps.create_pr.outputs.pull-request-number }} $STATUS!" >> $GITHUB_STEP_SUMMARY
fi
echo "URL: ${{ steps.create_pr.outputs.pull-request-url }}" >> $GITHUB_STEP_SUMMARY

View file

@ -1,14 +1,14 @@
name: Update Translation Source name: Update Translation Source
on: on:
workflow_dispatch:
schedule:
# runs at the start of each quarter (UTC)
- cron: '0 0 1 1,4,7,10 *'
pull_request: pull_request:
paths: paths:
- '.ci/update_translation_source_strings.sh' - '.ci/update_translation_source_strings.sh'
- '.github/workflows/translations-push.yml' - '.github/workflows/translations-push.yml'
schedule:
# Runs at the start of each quarter (UTC)
- cron: '0 0 1 1,4,7,10 *'
workflow_dispatch:
jobs: jobs:
translations: translations:
@ -19,32 +19,33 @@ jobs:
runs-on: ubuntu-slim runs-on: ubuntu-slim
steps: steps:
- name: Checkout repo - name: "Checkout repo"
uses: actions/checkout@v6 uses: actions/checkout@v7
- name: Install lupdate - name: "Install lupdate"
shell: bash shell: bash
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y --no-install-recommends qttools5-dev-tools sudo apt-get install -y --no-install-recommends qttools5-dev-tools
- name: Update Cockatrice translation source - name: "Update Cockatrice translation source"
env:
FILE: cockatrice/cockatrice_en@source.ts
id: cockatrice id: cockatrice
shell: bash shell: bash
run: | run: >
FILE="cockatrice/cockatrice_en@source.ts" DIRS="cockatrice/src $(find . -maxdepth 1 -type d -name 'libcockatrice_*')"
export DIRS="cockatrice/src $(find . -maxdepth 1 -type d -name 'libcockatrice_*')" .ci/update_translation_source_strings.sh
FILE="$FILE" DIRS="$DIRS" .ci/update_translation_source_strings.sh
- name: Update Oracle translation source - name: "Update Oracle translation source"
id: oracle id: oracle
shell: bash shell: bash
env: env:
FILE: 'oracle/oracle_en@source.ts'
DIRS: 'oracle/src' DIRS: 'oracle/src'
FILE: 'oracle/oracle_en@source.ts'
run: .ci/update_translation_source_strings.sh run: .ci/update_translation_source_strings.sh
- name: Render template - name: "Render template"
id: template id: template
uses: chuhlomin/render-template/binary@v1 uses: chuhlomin/render-template/binary@v1
with: with:
@ -54,7 +55,7 @@ jobs:
oracle_output: ${{ steps.oracle.outputs.output }} oracle_output: ${{ steps.oracle.outputs.output }}
commit: ${{ github.sha }} commit: ${{ github.sha }}
- name: Create pull request - name: "Create pull request"
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
id: create_pr id: create_pr
uses: peter-evans/create-pull-request@v8 uses: peter-evans/create-pull-request@v8
@ -62,27 +63,24 @@ jobs:
add-paths: | add-paths: |
cockatrice/cockatrice_en@source.ts cockatrice/cockatrice_en@source.ts
oracle/oracle_en@source.ts oracle/oracle_en@source.ts
commit-message: Update translation source strings author: github-actions <github-actions@github.com> # owner of the commit
# author is the owner of the commit
author: github-actions <github-actions@github.com>
branch: ci-update_translation_source
delete-branch: true
title: 'Update source strings'
body: ${{ steps.template.outputs.result }} body: ${{ steps.template.outputs.result }}
branch: ci-update_translation_source
commit-message: Update translation source strings
delete-branch: true
draft: false
labels: | labels: |
CI CI
Translation Translation
draft: false title: 'Update source strings'
- name: PR Status - name: "PR Status"
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
shell: bash shell: bash
env: env:
STATUS: ${{ steps.create_pr.outputs.pull-request-operation }} PR_NUMBER: ${{ steps.create_pr.outputs.pull-request-number }}
PR_URL: ${{ steps.create_pr.outputs.pull-request-url }}
STATUS: ${{ case(steps.create_pr.outputs.pull-request-operation == 'none', 'unchanged', steps.create_pr.outputs.pull-request-operation) }}
run: | run: |
if [[ "$STATUS" == "none" ]]; then echo "PR #$PR_NUMBER $STATUS!" >> "$GITHUB_STEP_SUMMARY"
echo "PR #${{ steps.create_pr.outputs.pull-request-number }} unchanged!" >> $GITHUB_STEP_SUMMARY echo "URL: $PR_URL" >> "$GITHUB_STEP_SUMMARY"
else
echo "PR #${{ steps.create_pr.outputs.pull-request-number }} $STATUS!" >> $GITHUB_STEP_SUMMARY
fi
echo "URL: ${{ steps.create_pr.outputs.pull-request-url }}" >> $GITHUB_STEP_SUMMARY

View file

@ -74,7 +74,7 @@ endif()
# A project name is needed for CPack # A project name is needed for CPack
# Version can be overriden by git tags, see cmake/getversion.cmake # Version can be overriden by git tags, see cmake/getversion.cmake
project("Cockatrice" VERSION 3.0.1) project("Cockatrice" VERSION 3.1.0)
# Set release name if not provided via env/cmake var # Set release name if not provided via env/cmake var
if(NOT DEFINED GIT_TAG_RELEASENAME) if(NOT DEFINED GIT_TAG_RELEASENAME)

View file

@ -117,21 +117,22 @@ ${If} $InstDir == ""
; we need to set a default based on the install mode ; we need to set a default based on the install mode
StrCpy $InstDir $0 StrCpy $InstDir $0
${EndIf} ${EndIf}
Call SetModeDestinationFromInstdir
; --- Detect portable install when using /R --- ; --- Detect portable install when using /R (must come BEFORE SetModeDestinationFromInstdir) ---
${If} $ReinstallMode = 1 ${If} $ReinstallMode = 1
IfFileExists "$InstDir\portable.dat" 0 not_portable IfFileExists "$InstDir\portable.dat" 0 not_portable
StrCpy $PortableMode 1 StrCpy $PortableMode 1
Goto portable_done Goto portable_done
not_portable: not_portable:
StrCpy $PortableMode 0 StrCpy $PortableMode 0
portable_done: portable_done:
${EndIf} ${EndIf}
; Now that $PortableMode reflects reality, commit InstDir into the correct slot
Call SetModeDestinationFromInstdir
${If} $ReinstallMode = 1 ${If} $ReinstallMode = 1
${AndIf} $PortableMode = 0
Call AutoUninstallIfNeeded Call AutoUninstallIfNeeded
${EndIf} ${EndIf}

View file

@ -56,68 +56,75 @@ set(cockatrice_SOURCES
src/filters/filter_tree_model.cpp src/filters/filter_tree_model.cpp
src/filters/syntax_help.cpp src/filters/syntax_help.cpp
src/game/abstract_game.cpp src/game/abstract_game.cpp
src/game/board/abstract_card_drag_item.cpp src/game/arrow_registry.cpp
src/game/board/abstract_card_item.cpp src/game_graphics/board/abstract_card_drag_item.cpp
src/game/board/abstract_counter.cpp src/game_graphics/board/abstract_card_item.cpp
src/game/board/arrow_item.cpp src/game_graphics/board/abstract_counter.cpp
src/game/board/arrow_target.cpp src/game/board/arrow_data.cpp
src/game/board/card_drag_item.cpp src/game_graphics/board/arrow_item.cpp
src/game/board/card_item.cpp src/game_graphics/board/arrow_target.cpp
src/game_graphics/board/card_drag_item.cpp
src/game_graphics/board/card_item.cpp
src/game/board/card_list.cpp src/game/board/card_list.cpp
src/game/board/counter_general.cpp src/game/board/card_state.cpp
src/game/board/translate_counter_name.cpp src/game_graphics/board/counter_general.cpp
src/game/deckview/deck_view.cpp src/game/board/counter_state.cpp
src/game/deckview/deck_view_container.cpp src/game_graphics/board/translate_counter_name.cpp
src/game/deckview/tabbed_deck_view_container.cpp src/game_graphics/deckview/deck_view.cpp
src/game/dialogs/dlg_create_token.cpp src/game_graphics/deckview/deck_view_container.cpp
src/game/dialogs/dlg_move_top_cards_until.cpp src/game_graphics/deckview/tabbed_deck_view_container.cpp
src/game/dialogs/dlg_roll_dice.cpp src/game_graphics/dialogs/dlg_create_token.cpp
src/game_graphics/dialogs/dlg_move_top_cards_until.cpp
src/game_graphics/dialogs/dlg_roll_dice.cpp
src/game/game.cpp src/game/game.cpp
src/game/game_event_handler.cpp src/game/game_event_handler.cpp
src/game/game_meta_info.cpp src/game/game_meta_info.cpp
src/game/game_scene.cpp src/game_graphics/game_scene.cpp
src/game/game_state.cpp src/game/game_state.cpp
src/game/game_view.cpp src/game_graphics/game_view.cpp
src/game/hand_counter.cpp src/game_graphics/hand_counter.cpp
src/game/log/message_log_widget.cpp src/game_graphics/log/message_log_widget.cpp
src/game/phase.cpp src/game/phase.cpp
src/game/phases_toolbar.cpp src/game_graphics/phases_toolbar.cpp
src/game/player/menu/card_menu.cpp src/game_graphics/player/menu/card_menu.cpp
src/game/player/menu/custom_zone_menu.cpp src/game_graphics/player/menu/custom_zone_menu.cpp
src/game/player/menu/grave_menu.cpp src/game_graphics/player/menu/grave_menu.cpp
src/game/player/menu/hand_menu.cpp src/game_graphics/player/menu/hand_menu.cpp
src/game/player/menu/library_menu.cpp src/game_graphics/player/menu/library_menu.cpp
src/game/player/menu/move_menu.cpp src/game_graphics/player/menu/move_menu.cpp
src/game/player/menu/player_menu.cpp src/game_graphics/player/menu/player_menu.cpp
src/game/player/menu/pt_menu.cpp src/game_graphics/player/menu/pt_menu.cpp
src/game/player/menu/rfg_menu.cpp src/game_graphics/player/menu/rfg_menu.cpp
src/game/player/menu/say_menu.cpp src/game_graphics/player/menu/say_menu.cpp
src/game/player/menu/sideboard_menu.cpp src/game_graphics/player/menu/sideboard_menu.cpp
src/game/player/menu/utility_menu.cpp src/game_graphics/player/menu/utility_menu.cpp
src/game/player/player.cpp src/game_graphics/tally/subtype_tally.cpp
src/game_graphics/tally/tally.cpp
src/game/player/player_actions.cpp src/game/player/player_actions.cpp
src/game/player/player_area.cpp src/game_graphics/player/player_area.cpp
src/game_graphics/player/player_dialogs.cpp
src/game/player/player_event_handler.cpp src/game/player/player_event_handler.cpp
src/game/player/player_graphics_item.cpp src/game_graphics/player/player_graphics_item.cpp
src/game/player/player_info.cpp src/game/player/player_info.cpp
src/game/player/player_list_widget.cpp src/game_graphics/player/player_list_widget.cpp
src/game/player/player_logic.cpp
src/game/player/player_manager.cpp src/game/player/player_manager.cpp
src/game/player/player_target.cpp src/game_graphics/player/player_target.cpp
src/game/replay.cpp src/game/replay.cpp
src/game/zones/card_zone.cpp src/game/zones/card_zone_logic.cpp
src/game/zones/hand_zone.cpp src/game/zones/hand_zone_logic.cpp
src/game/zones/logic/card_zone_logic.cpp src/game/zones/pile_zone_logic.cpp
src/game/zones/logic/hand_zone_logic.cpp src/game/zones/stack_zone_logic.cpp
src/game/zones/logic/pile_zone_logic.cpp src/game/zones/table_zone_logic.cpp
src/game/zones/logic/stack_zone_logic.cpp src/game/zones/view_zone_logic.cpp
src/game/zones/logic/table_zone_logic.cpp src/game_graphics/zones/card_zone.cpp
src/game/zones/logic/view_zone_logic.cpp src/game_graphics/zones/hand_zone.cpp
src/game/zones/pile_zone.cpp src/game_graphics/zones/pile_zone.cpp
src/game/zones/select_zone.cpp src/game_graphics/zones/select_zone.cpp
src/game/zones/stack_zone.cpp src/game_graphics/zones/stack_zone.cpp
src/game/zones/table_zone.cpp src/game_graphics/zones/table_zone.cpp
src/game/zones/view_zone.cpp src/game_graphics/zones/view_zone.cpp
src/game/zones/view_zone_widget.cpp src/game_graphics/zones/view_zone_widget.cpp
src/game_graphics/board/abstract_graphics_item.cpp src/game_graphics/board/abstract_graphics_item.cpp
src/interface/card_picture_loader/card_picture_loader.cpp src/interface/card_picture_loader/card_picture_loader.cpp
src/interface/card_picture_loader/card_picture_loader_local.cpp src/interface/card_picture_loader/card_picture_loader_local.cpp
@ -130,7 +137,13 @@ set(cockatrice_SOURCES
src/interface/layouts/overlap_layout.cpp src/interface/layouts/overlap_layout.cpp
src/interface/widgets/utility/line_edit_completer.cpp src/interface/widgets/utility/line_edit_completer.cpp
src/interface/pixel_map_generator.cpp src/interface/pixel_map_generator.cpp
src/interface/theme_config.cpp
src/interface/theme_manager.cpp src/interface/theme_manager.cpp
src/interface/palette_editor/color_button.cpp
src/interface/palette_editor/palette_generator.cpp
src/interface/palette_editor/quick_setup_panel.cpp
src/interface/palette_editor/palette_grid_widget.cpp
src/interface/palette_editor/palette_editor_dialog.cpp
src/interface/widgets/cards/additional_info/color_identity_widget.cpp src/interface/widgets/cards/additional_info/color_identity_widget.cpp
src/interface/widgets/cards/additional_info/mana_cost_widget.cpp src/interface/widgets/cards/additional_info/mana_cost_widget.cpp
src/interface/widgets/cards/additional_info/mana_symbol_widget.cpp src/interface/widgets/cards/additional_info/mana_symbol_widget.cpp
@ -172,6 +185,7 @@ set(cockatrice_SOURCES
src/interface/widgets/deck_analytics/analyzer_modules/mana_distribution/mana_distribution_single_display_widget.cpp src/interface/widgets/deck_analytics/analyzer_modules/mana_distribution/mana_distribution_single_display_widget.cpp
src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_total_widget.cpp src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_total_widget.cpp
src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_category_widget.cpp src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_category_widget.cpp
src/interface/widgets/deck_editor/card_database_view.cpp
src/interface/widgets/deck_editor/deck_list_history_manager_widget.cpp src/interface/widgets/deck_editor/deck_list_history_manager_widget.cpp
src/interface/widgets/deck_editor/deck_editor_card_database_dock_widget.cpp src/interface/widgets/deck_editor/deck_editor_card_database_dock_widget.cpp
src/interface/widgets/deck_editor/deck_editor_card_info_dock_widget.cpp src/interface/widgets/deck_editor/deck_editor_card_info_dock_widget.cpp
@ -224,11 +238,23 @@ set(cockatrice_SOURCES
src/interface/widgets/server/handle_public_servers.cpp src/interface/widgets/server/handle_public_servers.cpp
src/interface/widgets/server/remote/remote_decklist_tree_widget.cpp src/interface/widgets/server/remote/remote_decklist_tree_widget.cpp
src/interface/widgets/server/remote/remote_replay_list_tree_widget.cpp src/interface/widgets/server/remote/remote_replay_list_tree_widget.cpp
src/interface/widgets/server/user/user_avatar_provider.cpp
src/interface/widgets/server/user/user_card_art_provider.cpp
src/interface/widgets/server/user/user_card_settings_dialog.cpp
src/interface/widgets/server/user/user_context_menu.cpp src/interface/widgets/server/user/user_context_menu.cpp
src/interface/widgets/server/user/user_info_box.cpp src/interface/widgets/server/user/user_info_box.cpp
src/interface/widgets/server/user/user_info_connection.cpp src/interface/widgets/server/user/user_info_connection.cpp
src/interface/widgets/server/user/user_list_manager.cpp src/interface/widgets/server/user/user_list_manager.cpp
src/interface/widgets/server/user/user_list_painter.cpp
src/interface/widgets/server/user/user_list_widget.cpp src/interface/widgets/server/user/user_list_widget.cpp
src/interface/widgets/settings_page/appearance_settings_page.cpp
src/interface/widgets/settings_page/deck_editor_settings_page.cpp
src/interface/widgets/settings_page/general_settings_page.cpp
src/interface/widgets/settings_page/messages_settings_page.cpp
src/interface/widgets/settings_page/shortcut_settings_page.cpp
src/interface/widgets/settings_page/sound_settings_page.cpp
src/interface/widgets/settings_page/storage_settings_page.cpp
src/interface/widgets/settings_page/user_interface_settings_page.cpp
src/interface/widgets/utility/custom_line_edit.cpp src/interface/widgets/utility/custom_line_edit.cpp
src/interface/widgets/utility/get_text_with_max.cpp src/interface/widgets/utility/get_text_with_max.cpp
src/interface/widgets/utility/sequence_edit.cpp src/interface/widgets/utility/sequence_edit.cpp
@ -307,6 +333,7 @@ set(cockatrice_SOURCES
src/interface/widgets/tabs/tab.cpp src/interface/widgets/tabs/tab.cpp
src/interface/widgets/tabs/tab_account.cpp src/interface/widgets/tabs/tab_account.cpp
src/interface/widgets/tabs/tab_admin.cpp src/interface/widgets/tabs/tab_admin.cpp
src/interface/widgets/tabs/tab_card_art_rules.cpp
src/interface/widgets/tabs/tab_deck_editor.cpp src/interface/widgets/tabs/tab_deck_editor.cpp
src/interface/widgets/tabs/tab_deck_storage.cpp src/interface/widgets/tabs/tab_deck_storage.cpp
src/interface/widgets/tabs/tab_game.cpp src/interface/widgets/tabs/tab_game.cpp
@ -329,6 +356,8 @@ set(cockatrice_SOURCES
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.h src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.h
src/interface/widgets/utility/compact_push_button.cpp src/interface/widgets/utility/compact_push_button.cpp
src/interface/widgets/utility/compact_push_button.h src/interface/widgets/utility/compact_push_button.h
src/interface/widgets/server/user/user_info_popup.cpp
src/interface/widgets/server/user/user_info_popup.h
) )
add_subdirectory(sounds) add_subdirectory(sounds)

View file

@ -55,6 +55,7 @@
<file>resources/icons/view.svg</file> <file>resources/icons/view.svg</file>
<file>resources/icons/mana/B.svg</file> <file>resources/icons/mana/B.svg</file>
<file>resources/icons/mana/C.svg</file>
<file>resources/icons/mana/G.svg</file> <file>resources/icons/mana/G.svg</file>
<file>resources/icons/mana/R.svg</file> <file>resources/icons/mana/R.svg</file>
<file>resources/icons/mana/U.svg</file> <file>resources/icons/mana/U.svg</file>
@ -69,6 +70,7 @@
<file>resources/config/interface.svg</file> <file>resources/config/interface.svg</file>
<file>resources/config/messages.svg</file> <file>resources/config/messages.svg</file>
<file>resources/config/deckeditor.svg</file> <file>resources/config/deckeditor.svg</file>
<file>resources/config/storage.svg</file>
<file>resources/config/shorcuts.svg</file> <file>resources/config/shorcuts.svg</file>
<file>resources/config/sound.svg</file> <file>resources/config/sound.svg</file>
<file>resources/config/debug.ini</file> <file>resources/config/debug.ini</file>

File diff suppressed because it is too large Load diff

View file

@ -28,6 +28,8 @@
#dlg_tip_of_the_day = true #dlg_tip_of_the_day = true
#dlg_update = true #dlg_update = true
#general_settings_page = true
#settings_cache = true #settings_cache = true
#servers_settings = true #servers_settings = true
#shortcuts_settings = true #shortcuts_settings = true

View file

@ -0,0 +1,799 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
width="62.636364"
height="62.090908"
id="svg2"
sodipodi:version="0.32"
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
sodipodi:docname="storage.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
version="1.0"
xml:space="preserve"
xmlns="http://www.w3.org/2000/svg"><defs
id="defs4"><linearGradient
id="linearGradient3169"><stop
style="stop-color:#0000ff;stop-opacity:1;"
offset="0"
id="stop3171" /><stop
style="stop-color:#000067;stop-opacity:1;"
offset="1"
id="stop3173" /></linearGradient><linearGradient
id="linearGradient4766"><stop
style="stop-color:#784421;stop-opacity:1;"
offset="0"
id="stop4768" /><stop
style="stop-color:#3d2210;stop-opacity:0;"
offset="1"
id="stop4770" /></linearGradient><linearGradient
id="linearGradient4758"><stop
style="stop-color:#a05a2c;stop-opacity:1;"
offset="0"
id="stop4760" /><stop
style="stop-color:#3d2210;stop-opacity:1;"
offset="1"
id="stop4762" /></linearGradient><inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective10" /><inkscape:perspective
id="perspective2484"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 526.18109 : 1"
sodipodi:type="inkscape:persp3d" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4758"
id="linearGradient4764"
x1="466.09601"
y1="485.96021"
x2="715.14801"
y2="485.96021"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4766"
id="linearGradient4772"
x1="496.548"
y1="485.26816"
x2="683.31201"
y2="485.26816"
gradientUnits="userSpaceOnUse" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3169"
id="radialGradient3175"
cx="120.07376"
cy="56.138123"
fx="120.07376"
fy="56.138123"
r="82.790039"
gradientTransform="matrix(1,0,0,0.2116376,0,44.257186)"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient6482"
id="linearGradient6488"
x1="32.18182"
y1="3.2835093"
x2="32.18182"
y2="13.02554"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0281354,0,0,1.0429299,85.21874,131.0326)" /><linearGradient
id="linearGradient6482"><stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop6484" /><stop
style="stop-color:#00ff00;stop-opacity:0;"
offset="1"
id="stop6486" /></linearGradient><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient6464"
id="linearGradient6470"
x1="32.090908"
y1="1.8181819"
x2="31.09091"
y2="62.909088"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0,-0.1818182)" /><linearGradient
id="linearGradient6464"><stop
style="stop-color:#0061ff;stop-opacity:1;"
offset="0"
id="stop6466" /><stop
style="stop-color:#001c4c;stop-opacity:1;"
offset="1"
id="stop6468" /></linearGradient><linearGradient
y2="62.909088"
x2="31.09091"
y1="1.8181819"
x1="32.090908"
gradientTransform="translate(86.2151,131.5372)"
gradientUnits="userSpaceOnUse"
id="linearGradient4477"
xlink:href="#linearGradient6464"
inkscape:collect="always" /><linearGradient
inkscape:collect="always"
id="linearGradient2916"><stop
style="stop-color:white;stop-opacity:1;"
offset="0"
id="stop2918" /><stop
style="stop-color:white;stop-opacity:0;"
offset="1"
id="stop2920" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient2902"><stop
style="stop-color:black;stop-opacity:1;"
offset="0"
id="stop2905" /><stop
style="stop-color:black;stop-opacity:0;"
offset="1"
id="stop2907" /></linearGradient><linearGradient
id="linearGradient2064"><stop
id="stop2066"
offset="0"
style="stop-color:white;stop-opacity:1;" /><stop
style="stop-color:#555753;stop-opacity:0.60000002;"
offset="0.5"
id="stop2070" /><stop
id="stop2068"
offset="1"
style="stop-color:#555753;stop-opacity:0;" /></linearGradient><linearGradient
id="linearGradient9641"><stop
style="stop-color:white;stop-opacity:1"
offset="0"
id="stop9643" /><stop
style="stop-color:#888a85;stop-opacity:1"
offset="1"
id="stop9645" /></linearGradient><linearGradient
id="linearGradient9633"><stop
style="stop-color:#eeeeec;stop-opacity:1"
offset="0"
id="stop9635" /><stop
style="stop-color:#888a85;stop-opacity:1"
offset="1"
id="stop9639" /></linearGradient><linearGradient
id="linearGradient9613"><stop
style="stop-color:white;stop-opacity:1"
offset="0"
id="stop9615" /><stop
id="stop9619"
offset="0.5"
style="stop-color:white;stop-opacity:1;" /><stop
style="stop-color:#cccfca;stop-opacity:1"
offset="1"
id="stop9617" /></linearGradient><linearGradient
id="linearGradient8710"><stop
style="stop-color:black;stop-opacity:1;"
offset="0"
id="stop8712" /><stop
style="stop-color:white;stop-opacity:1;"
offset="1"
id="stop8714" /></linearGradient><linearGradient
id="linearGradient8631"><stop
id="stop8633"
offset="0"
style="stop-color:#eeeeec;stop-opacity:1" /><stop
style="stop-color:#eeeeec;stop-opacity:1;"
offset="0.2"
id="stop8637" /><stop
id="stop8635"
offset="1"
style="stop-color:#babdb6;stop-opacity:1" /></linearGradient><linearGradient
id="linearGradient8625"><stop
id="stop8627"
offset="0"
style="stop-color:white;stop-opacity:1" /><stop
id="stop8629"
offset="1"
style="stop-color:#babdb6;stop-opacity:1" /></linearGradient><linearGradient
id="linearGradient8613"><stop
style="stop-color:#babdb6;stop-opacity:1"
offset="0"
id="stop8615" /><stop
style="stop-color:#2e3436;stop-opacity:1"
offset="1"
id="stop8617" /></linearGradient><linearGradient
id="linearGradient5740"><stop
style="stop-color:#d0d0cb;stop-opacity:1;"
offset="0"
id="stop5742" /><stop
style="stop-color:#babdb6;stop-opacity:1"
offset="1"
id="stop5744" /></linearGradient><linearGradient
id="linearGradient5690"><stop
style="stop-color:white;stop-opacity:1;"
offset="0"
id="stop5692" /><stop
style="stop-color:#888a85;stop-opacity:0.59848487"
offset="1"
id="stop5694" /></linearGradient><linearGradient
id="linearGradient2899"><stop
id="stop2901"
offset="0"
style="stop-color:#555753;stop-opacity:1" /><stop
id="stop2903"
offset="1"
style="stop-color:#2e3436;stop-opacity:1" /></linearGradient><linearGradient
id="linearGradient3468"><stop
style="stop-color:#fdfdfc;stop-opacity:1"
offset="0"
id="stop3470" /><stop
style="stop-color:white;stop-opacity:0.37121212"
offset="1"
id="stop3472" /></linearGradient><linearGradient
id="linearGradient2909"><stop
style="stop-color:white;stop-opacity:0;"
offset="0"
id="stop2911" /><stop
id="stop2917"
offset="0.5"
style="stop-color:white;stop-opacity:1;" /><stop
style="stop-color:white;stop-opacity:0;"
offset="1"
id="stop2913" /></linearGradient><linearGradient
id="linearGradient2839"><stop
style="stop-color:white;stop-opacity:0.25773194;"
offset="0"
id="stop2841" /><stop
id="stop2847"
offset="0.5472973"
style="stop-color:white;stop-opacity:1;" /><stop
style="stop-color:white;stop-opacity:0.24705882;"
offset="0.66243607"
id="stop2849" /><stop
id="stop2851"
offset="0.875"
style="stop-color:white;stop-opacity:0.83505154;" /><stop
style="stop-color:white;stop-opacity:0;"
offset="1"
id="stop2843" /></linearGradient><linearGradient
id="linearGradient2900"><stop
style="stop-color:black;stop-opacity:0;"
offset="0"
id="stop2902" /><stop
id="stop2908"
offset="0.5"
style="stop-color:black;stop-opacity:1;" /><stop
style="stop-color:black;stop-opacity:0;"
offset="1"
id="stop2904" /></linearGradient><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3468"
id="linearGradient3474"
x1="24.748737"
y1="35.354588"
x2="24.998737"
y2="14.997767"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.995556,0,-3.931113)" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2902"
id="radialGradient4700"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(4.095822,0,0,3.101282,-9.53921,-94.5433)"
cx="0"
cy="17"
fx="0"
fy="17"
r="2" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2902"
id="radialGradient4702"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(4.095822,0,0,3.101282,38.20996,-10.90025)"
cx="0"
cy="17"
fx="0"
fy="17"
r="2" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2900"
id="linearGradient4704"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.047911,0,0,2.067521,1.347566,6.673675)"
x1="9.8994951"
y1="20"
x2="9.8994951"
y2="13.979153" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2909"
id="linearGradient4711"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,1.42294,10.5,-14.95703)"
x1="15.335379"
y1="33.06237"
x2="20.329321"
y2="36.37693" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2909"
id="linearGradient4713"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,1.42294,-0.875,-15.04578)"
x1="15.335379"
y1="33.06237"
x2="20.329321"
y2="36.37693" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2909"
id="linearGradient4715"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.459833,0,-0.391165,1.370105,40.62503,-13.29892)"
x1="15.335379"
y1="33.06237"
x2="20.329321"
y2="36.37693" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient5740"
id="radialGradient5748"
cx="25.251999"
cy="16.47991"
fx="25.251999"
fy="16.47991"
r="21.980215"
gradientTransform="matrix(1.032991,-0.596398,0.575121,0.99614,-12.23456,11.55448)"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2064"
id="linearGradient5790"
gradientUnits="userSpaceOnUse"
x1="18.048874"
y1="25.461344"
x2="22.211937"
y2="12.143078"
gradientTransform="matrix(0.940224,0,0,0.931632,1.331811,1.401537)" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient8631"
id="linearGradient5865"
x1="24"
y1="36.638382"
x2="25.818018"
y2="6.8314762"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2839"
id="linearGradient7658"
gradientUnits="userSpaceOnUse"
x1="27.057796"
y1="12.669416"
x2="32.042896"
y2="31.219666" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5690"
id="linearGradient8603"
x1="20.304037"
y1="24.035707"
x2="18.498415"
y2="40.647167"
gradientUnits="userSpaceOnUse" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient8613"
id="radialGradient8619"
cx="7.5177727"
cy="30.573555"
fx="7.5177727"
fy="30.573555"
r="0.53125"
gradientTransform="matrix(1.662477,0,0,1.61358,-4.989175,-18.65647)"
gradientUnits="userSpaceOnUse" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient9613"
id="radialGradient8623"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.389748,0,0,1.348872,-2.91982,-10.63815)"
cx="7.5191436"
cy="30.304251"
fx="7.5191436"
fy="30.304251"
r="0.53125" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient9633"
id="radialGradient8664"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.569487,0,0,1.523325,-4.288627,-15.92107)"
cx="7.5336008"
cy="30.307562"
fx="7.5336008"
fy="30.307562"
r="0.53125" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient8613"
id="radialGradient8666"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.662477,0,0,1.61358,-4.989175,-18.65647)"
cx="7.5177727"
cy="30.573555"
fx="7.5177727"
fy="30.573555"
r="0.53125" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient8625"
id="radialGradient8676"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.662477,0,0,1.61358,-4.989175,-18.65647)"
cx="7.4792061"
cy="30.36071"
fx="7.4792061"
fy="30.36071"
r="0.53125" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient8613"
id="radialGradient8678"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.662477,0,0,1.61358,-4.989175,-18.65647)"
cx="7.5177727"
cy="30.573555"
fx="7.5177727"
fy="30.573555"
r="0.53125" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient9641"
id="radialGradient8680"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.662477,0,0,1.61358,-4.989175,-18.65647)"
cx="7.4893188"
cy="30.337601"
fx="7.4893188"
fy="30.337601"
r="0.53125" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient8613"
id="radialGradient8682"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.662477,0,0,1.61358,-4.989175,-18.65647)"
cx="7.5177727"
cy="30.573555"
fx="7.5177727"
fy="30.573555"
r="0.53125" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient8710"
id="linearGradient8716"
x1="40.617188"
y1="30.554688"
x2="40.710938"
y2="30.359375"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient8710"
id="linearGradient9605"
gradientUnits="userSpaceOnUse"
x1="40.617188"
y1="30.554688"
x2="40.710938"
y2="30.359375"
gradientTransform="matrix(0.602867,-0.797841,0.797841,0.602867,-41.12611,44.62773)" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient8710"
id="linearGradient9649"
gradientUnits="userSpaceOnUse"
x1="40.617188"
y1="30.554688"
x2="40.710938"
y2="30.359375"
gradientTransform="rotate(-30.000012,-5.5813167,76.089146)" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient8710"
id="linearGradient9654"
gradientUnits="userSpaceOnUse"
x1="40.617188"
y1="30.554688"
x2="40.710938"
y2="30.359375"
gradientTransform="matrix(0.707107,0.527555,-0.707107,0.527555,29.0058,-24.09196)" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2916"
id="linearGradient2973"
x1="12.5"
y1="43.1875"
x2="12.5"
y2="34.045513"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2899"
id="linearGradient5655"
gradientUnits="userSpaceOnUse"
x1="53.812813"
y1="43.573235"
x2="-2.8138931"
y2="35.500015"
gradientTransform="translate(0,50)" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2902"
id="linearGradient2992"
x1="21.9375"
y1="39"
x2="21.9375"
y2="37.995617"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2902"
id="linearGradient2910"
x1="22.101398"
y1="27.658131"
x2="22.971142"
y2="20.903238"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0,2)" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2916"
id="linearGradient2922"
x1="24.847851"
y1="28.908398"
x2="24.847851"
y2="25.757175"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0,2)" /></defs><sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.8830488"
inkscape:cx="-3.9945275"
inkscape:cy="-14.363301"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="2560"
inkscape:window-height="1408"
inkscape:window-x="0"
inkscape:window-y="32"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:showpageshadow="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050" /><metadata
id="metadata7"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-86.987816,-132.85536)"><rect
style="fill:url(#linearGradient4477);fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-opacity:1"
id="rect6462"
width="61.636364"
height="61.090908"
x="87.487816"
y="133.35536"
ry="5.6363635" /><rect
style="fill:url(#linearGradient6488);fill-opacity:1;fill-rule:nonzero;stroke:none"
id="rect6472"
width="59.796619"
height="13.251164"
x="88.407707"
y="134.45705"
ry="4.7325583" /><g
inkscape:label="Livello 1"
id="layer1-3"
style="display:inline"
transform="matrix(1.1537183,0,0,1.1537183,91.003924,136.40297)"><g
id="g3519"
style="opacity:0.7"
transform="matrix(1.030831,0,0,1.151147,-0.73609,-12.57431)"
inkscape:export-filename="/home/lapo/Desktop/uhm.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"><rect
transform="scale(-1)"
y="-48.024086"
x="-9.5392103"
height="12.405126"
width="8.1916437"
id="rect2884"
style="opacity:1;fill:url(#radialGradient4700);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /><rect
y="35.618961"
x="38.209965"
height="12.405126"
width="8.1916437"
id="rect2894"
style="opacity:1;fill:url(#radialGradient4702);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /><rect
y="35.618961"
x="9.5392103"
height="12.405126"
width="28.670753"
id="rect2898"
style="opacity:1;fill:url(#linearGradient4704);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /></g><g
id="g5672"
transform="translate(0,-48.99747)"><path
sodipodi:nodetypes="ccccccccccccc"
id="rect2010"
d="M 4.5182287,80.500013 H 43.481768 c 0.564099,0 1.018229,0.45413 1.018229,1.018229 v 2.963543 c 0,1.315584 -0.450231,3.018228 -2.455729,3.018228 L 40.5,87.5 v 1 h -33 v -1 l -1.8567713,1.3e-5 c -1.2712053,0 -2.1432282,-0.884627 -2.1432282,-2.255665 v -3.726106 c 0,-0.564099 0.4541297,-1.018229 1.0182282,-1.018229 z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient5655);fill-opacity:1;fill-rule:nonzero;stroke:#2e3436;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.4;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none" /><path
transform="translate(0,50)"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient2973);stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:1.4;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none"
d="m 4.59375,31.59375 v 3.729743 c 0,0.599619 0.3756505,1.104854 0.8863276,1.104854 H 42.426407 c 0.512469,0 0.979843,-0.507235 0.979843,-1.016466 V 31.59375 Z"
id="path2076"
sodipodi:nodetypes="ccccccc" /><g
transform="translate(0,50)"
style="opacity:0.5"
id="g4706"><path
style="opacity:0.109524;fill:url(#linearGradient4711);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 26.144738,32.088747 c 0,0 -1.502602,5.533939 -3.226175,5.911253 0,0 6.231378,-0.125771 6.231378,-0.125771 1.387072,-0.317461 3.358758,-5.785482 3.358758,-5.785482 z"
id="path2907"
sodipodi:nodetypes="ccccc"
inkscape:export-filename="/home/lapo/Desktop/uhm.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" /><path
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
inkscape:export-filename="/home/lapo/Desktop/uhm.png"
sodipodi:nodetypes="ccccc"
id="path2892"
d="m 14.769738,32 c 0,0 -1.502602,5.533939 -3.226175,5.911253 0,0 6.231378,-0.125771 6.231378,-0.125771 C 19.162013,37.468021 21.133699,32 21.133699,32 Z"
style="opacity:0.109524;fill:url(#linearGradient4713);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
inkscape:export-filename="/home/lapo/Desktop/uhm.png"
sodipodi:nodetypes="ccccc"
id="path2896"
d="m 34.886139,32 c 0,0 -2.212224,5.328458 -3.108503,5.691761 0,0 2.899969,-0.121101 2.899969,-0.121101 C 35.402697,37.264987 37.8125,32 37.8125,32 Z"
style="opacity:0.109524;fill:url(#linearGradient4715);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /></g></g><path
style="fill:url(#radialGradient5748);fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
d="m 11.693127,10.498788 h 24.572566 c 1.68417,0 2.396517,0.117479 3.040019,2.385005 l 5.074491,17.881119 c 0.501024,1.765471 -1.355848,2.735101 -3.040018,2.735101 H 6.6186312 c -1.868408,0 -3.4893833,-1.181417 -3.0400182,-2.735101 L 8.8290448,12.611497 c 0.5683008,-1.964905 1.1799122,-2.112709 2.8640822,-2.112709 z"
id="rect1879"
sodipodi:nodetypes="cczzcczzc"
inkscape:export-filename="/home/lapo/Desktop/uhm.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" /><path
sodipodi:type="inkscape:offset"
inkscape:radius="-0.5"
inkscape:original="M 11.6875 10.5 C 10.00333 10.5 9.4120513 10.660095 8.84375 12.625 L 3.59375 30.75 C 3.1443849 32.303684 4.7565918 33.500002 6.625 33.5 L 41.34375 33.5 C 43.02792 33.5 44.876024 32.515471 44.375 30.75 L 39.3125 12.875 C 38.668998 10.607474 37.965419 10.5 36.28125 10.5 L 11.6875 10.5 z "
style="display:inline;opacity:0.462406;fill:url(#linearGradient7658);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
id="path5806"
d="m 11.6875,11 c -0.826242,0 -1.28475,0.05742 -1.5625,0.242188 -0.2777497,0.184768 -0.5284825,0.580009 -0.8007812,1.521484 l -5.2500001,18.125 c -0.1708248,0.590628 0.021709,1.039316 0.4902344,1.4375 C 5.0329784,32.724356 5.7975106,33.000001 6.625,33 h 34.71875 c 0.744655,0 1.538941,-0.232575 2.03125,-0.609375 0.492309,-0.3768 0.719298,-0.799984 0.519531,-1.503906 l -5.0625,-17.875 C 38.52278,11.922001 38.224454,11.462814 37.910156,11.253906 37.595859,11.044998 37.112699,11 36.28125,11 Z" /><ellipse
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:url(#radialGradient8623);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.4;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none"
id="path8621"
transform="matrix(-2.628602,0,0,1.777765,27.79309,-23.77739)"
cx="7.625"
cy="30.578125"
rx="0.53125"
ry="0.515625" /><path
style="fill:none;fill-opacity:1;stroke:url(#linearGradient5790);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 16.110953,12.552805 c -0.573581,0 -1.02837,0.431821 -1.02837,0.989859 l -0.940223,3.230801 c -2.859962,1.276514 -4.6423552,3.099073 -4.6423552,5.123976 0,3.856957 6.4790242,6.987239 14.4853222,6.98724 8.006296,0 14.514705,-3.130284 14.514704,-6.98724 0,-2.039034 -1.835591,-3.875388 -4.730501,-5.153089 l -0.940224,-3.201688 c 0,-0.558038 -0.454788,-0.989859 -1.02837,-0.989859 z"
id="path2784"
sodipodi:nodetypes="cccssscccc" /><path
style="display:inline;fill:none;fill-opacity:1;stroke:url(#linearGradient3474);stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
d="m 11.6875,11.500005 c -0.803124,0 -1.097168,0.07051 -1.21875,0.155556 -0.121582,0.08504 -0.357707,0.40212 -0.6875,1.306667 l -5.25,18.137786 c -0.1337204,0.366765 -0.054827,0.533865 0.3125,0.84 0.3673267,0.306136 1.066693,0.56 1.78125,0.560001 h 34.71875 c 0.639793,0 1.393345,-0.237954 1.78125,-0.52889 0.387905,-0.290935 0.488311,-0.382809 0.3125,-0.871111 L 38.375,13.242228 c -0.377206,-1.04766 -0.68208,-1.439297 -0.84375,-1.555556 -0.16167,-0.116259 -0.443711,-0.186667 -1.25,-0.186667 z"
id="path3394"
sodipodi:nodetypes="csccsccsccscc" /><g
id="g5657"
transform="translate(7,-1)"
style="opacity:0.302857"><rect
ry="0.74712253"
rx="0.75130093"
y="35.500008"
x="18.499996"
height="1.9999924"
width="14.000004"
id="rect5641"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#eeeeec;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.4;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none" /><rect
y="36"
x="19"
height="1"
width="1"
id="rect5645"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.4;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none" /><rect
y="36"
x="22"
height="1"
width="1"
id="rect5647"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.4;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none" /><rect
y="36"
x="24"
height="1"
width="1"
id="rect5649"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.4;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none" /><rect
y="36"
x="26"
height="1"
width="1"
id="rect5651"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.4;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none" /><rect
y="36"
x="29"
height="1"
width="2"
id="rect5653"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.4;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none" /></g><path
sodipodi:type="inkscape:offset"
inkscape:radius="-0.44194174"
inkscape:original="M 16.125 12.5625 C 15.55142 12.5625 15.09375 12.973212 15.09375 13.53125 L 14.15625 16.78125 C 11.296288 18.057765 9.5 19.881347 9.5 21.90625 C 9.5 25.763206 15.993702 28.874999 24 28.875 C 32.006296 28.874999 38.500001 25.763206 38.5 21.90625 C 38.5 19.867215 36.67616 18.027701 33.78125 16.75 L 32.84375 13.53125 C 32.843748 12.973212 32.386082 12.5625 31.8125 12.5625 L 16.125 12.5625 z "
style="display:inline;fill:url(#linearGradient5865);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path5857"
d="m 16.125,13.003906 c -0.362612,0 -0.589844,0.210942 -0.589844,0.527344 a 0.44198593,0.44198593 0 0 1 -0.01758,0.123047 l -0.9375,3.25 a 0.44198593,0.44198593 0 0 1 -0.24414,0.28125 c -1.389903,0.620369 -2.504368,1.368471 -3.25586,2.177734 -0.751491,0.809263 -1.1386718,1.661199 -1.1386717,2.542969 0,1.680455 1.4530107,3.311153 3.9980467,4.533203 2.545037,1.22205 6.114654,1.99414 10.060547,1.994141 3.945892,-1e-6 7.51551,-0.772091 10.060547,-1.994141 2.545037,-1.22205 3.998047,-2.852748 3.998047,-4.533203 0,-0.887751 -0.391823,-1.747213 -1.154297,-2.5625 -0.762474,-0.815287 -1.893636,-1.568394 -3.300781,-2.189453 a 0.44198593,0.44198593 0 0 1 -0.246094,-0.28125 l -0.9375,-3.21875 a 0.44198593,0.44198593 0 0 1 -0.01758,-0.123047 c -10e-7,-0.316404 -0.22723,-0.527344 -0.589844,-0.527344 z" /><ellipse
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.9;fill:#000000;fill-opacity:0.0530303;fill-rule:nonzero;stroke:url(#linearGradient8603);stroke-width:2.52015;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.4;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none"
id="path8595"
transform="matrix(0.449978,0,0,0.349909,16.36363,12.21469)"
cx="16.970562"
cy="25.107418"
rx="7.7781744"
ry="4.2868347" /><ellipse
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:url(#radialGradient8619);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.4;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none"
id="path8611"
transform="matrix(1.411772,0,0,0.969697,-3.014767,0.848485)"
cx="7.625"
cy="30.578125"
rx="0.53125"
ry="0.515625" /><ellipse
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:url(#radialGradient8664);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.462594;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.4;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none"
id="path8660"
transform="matrix(-2.628602,0,0,1.777765,60.79309,-23.77739)"
cx="7.625"
cy="30.578125"
rx="0.53125"
ry="0.515625" /><ellipse
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:url(#radialGradient8666);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.4;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none"
id="path8662"
transform="matrix(1.411772,0,0,0.969697,29.98523,0.848485)"
cx="7.625"
cy="30.578125"
rx="0.53125"
ry="0.515625" /><ellipse
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:url(#radialGradient8676);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.4;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none"
id="path8668"
transform="matrix(-2.628602,0,0,1.777765,31.79309,-40.77739)"
cx="7.625"
cy="30.578125"
rx="0.53125"
ry="0.515625" /><ellipse
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:url(#radialGradient8678);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.4;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none"
id="path8670"
transform="matrix(1.411772,0,0,0.969697,0.985233,-16.15152)"
cx="7.625"
cy="30.578125"
rx="0.53125"
ry="0.515625" /><ellipse
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:url(#radialGradient8680);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.4;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none"
id="path8672"
transform="matrix(-2.628602,0,0,1.777765,56.3029,-40.77739)"
cx="7.625"
cy="30.578125"
rx="0.53125"
ry="0.515625" /><ellipse
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:url(#radialGradient8682);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.4;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none"
id="path8674"
transform="matrix(1.411772,0,0,0.969697,25.49504,-16.15152)"
cx="7.625"
cy="30.578125"
rx="0.53125"
ry="0.515625" /><path
style="opacity:0.4;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient8716);stroke-width:0.3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 40.328109,30.261401 0.874999,0.430332"
id="path8700" /><path
style="display:inline;opacity:0.4;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient9605);stroke-width:0.3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 7.330186,30.695906 8.201031,30.257228"
id="path9603" /><path
style="display:inline;opacity:0.4;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient9649);stroke-width:0.3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 11.263531,13.446473 0.972937,-0.06482"
id="path9647" /><path
style="display:inline;opacity:0.4;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient9654);stroke-width:0.3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 36.124038,13.147874 0.314427,0.688634"
id="path9652" /><rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.12;fill:url(#linearGradient2992);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.681836;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none"
id="rect2984"
width="32.03125"
height="1"
x="8"
y="38" /><path
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.12;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient2910);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none"
d="M 10.460155,15.082355 6.8513979,27.675762 C 8.2982685,28.375511 10.625,29.167061 10.429825,31.533131 H 37.299883 C 37.869398,29.640915 39.875,28.375 41.34614,28.25 L 37.498106,15.082355 32.350135,12.523347 H 14.318912 Z"
id="path1997"
sodipodi:nodetypes="ccccccccc" /><path
sodipodi:nodetypes="ccccc"
id="path2912"
d="m 7.9763979,27.050762 c 1.4468706,0.699749 3.1789321,1.433241 3.4256991,3.357369 H 36.857941 C 37.427456,28.515915 38.875,27.5 40.34614,27.375 Z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.834286;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient2922);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none" /></g></g></svg>

After

Width:  |  Height:  |  Size: 40 KiB

View file

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="169.33115mm"
height="169.59981mm"
viewBox="0 0 169.33115 169.59981"
version="1.1"
id="svg1"
inkscape:export-filename="C.svg"
inkscape:export-xdpi="96.000015"
inkscape:export-ydpi="96.000015"
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
sodipodi:docname="colorless.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="0.71535494"
inkscape:cx="397.00572"
inkscape:cy="536.09751"
inkscape:window-width="1853"
inkscape:window-height="1011"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:page
x="0"
y="0"
width="169.33115"
height="169.59981"
id="page2"
margin="0"
bleed="0" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-20.334425,-63.700102)">
<ellipse
style="fill:#cccccc;stroke:#000000;stroke-width:0.165333;stroke-linecap:round"
id="path1"
cx="-30.617094"
cy="179.22736"
transform="matrix(0.70654605,-0.70766707,0.70654608,0.70766704,0,0)"
rx="84.650612"
ry="84.65062" />
<rect
style="fill:none;stroke:#000000;stroke-width:14.5003;stroke-linecap:round;stroke-linejoin:bevel;stroke-dasharray:none"
id="rect1"
width="84.683701"
height="84.683769"
x="-221.60611"
y="-73.174698"
ry="0.084683768"
transform="matrix(-0.7073973,-0.70681615,0.7073973,-0.70681615,0,0)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -6,12 +6,12 @@
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QRegularExpression> #include <QRegularExpression>
#include <QUrlQuery> #include <QUrlQuery>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/deck_list/deck_list.h> #include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/tree/deck_list_card_node.h> #include <libcockatrice/deck_list/tree/deck_list_card_node.h>
#include <version_string.h> #include <version_string.h>
DeckStatsInterface::DeckStatsInterface(CardDatabase &_cardDatabase, QObject *parent) DeckStatsInterface::DeckStatsInterface(QObject *parent) : QObject(parent)
: QObject(parent), cardDatabase(_cardDatabase)
{ {
manager = new QNetworkAccessManager(this); manager = new QNetworkAccessManager(this);
connect(manager, &QNetworkAccessManager::finished, this, &DeckStatsInterface::queryFinished); connect(manager, &QNetworkAccessManager::finished, this, &DeckStatsInterface::queryFinished);
@ -70,8 +70,8 @@ void DeckStatsInterface::analyzeDeck(const DeckList &deck)
void DeckStatsInterface::copyDeckWithoutTokens(const DeckList &source, DeckList &destination) void DeckStatsInterface::copyDeckWithoutTokens(const DeckList &source, DeckList &destination)
{ {
auto copyIfNotAToken = [this, &destination](const auto node, const auto card) { auto copyIfNotAToken = [&destination](const auto node, const auto card) {
CardInfoPtr dbCard = cardDatabase.query()->getCardInfo(card->getName()); CardInfoPtr dbCard = CardDatabaseManager::query()->getCardInfo(card->getName());
if (dbCard && !dbCard->getIsToken()) { if (dbCard && !dbCard->getIsToken()) {
DecklistCardNode *addedCard = destination.addCard(card->getName(), node->getName(), -1); DecklistCardNode *addedCard = destination.addCard(card->getName(), node->getName(), -1);
addedCard->setNumber(card->getNumber()); addedCard->setNumber(card->getNumber());

View file

@ -1,13 +1,12 @@
/** /**
* @file deck_stats_interface.h * @file deck_stats_interface.h
* @ingroup ApiInterfaces * @ingroup ApiInterfaces
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef DECKSTATS_INTERFACE_H #ifndef DECKSTATS_INTERFACE_H
#define DECKSTATS_INTERFACE_H #define DECKSTATS_INTERFACE_H
#include <libcockatrice/card/database/card_database.h>
#include <libcockatrice/deck_list/deck_list.h> #include <libcockatrice/deck_list/deck_list.h>
class QByteArray; class QByteArray;
@ -21,8 +20,6 @@ class DeckStatsInterface : public QObject
private: private:
QNetworkAccessManager *manager; QNetworkAccessManager *manager;
CardDatabase &cardDatabase;
/** /**
* Deckstats doesn't recognize token cards, and instead tries to find the * Deckstats doesn't recognize token cards, and instead tries to find the
* closest non-token card instead. So we construct a new deck which has no * closest non-token card instead. So we construct a new deck which has no
@ -35,7 +32,7 @@ private slots:
void getAnalyzeRequestData(const DeckList &deck, QByteArray &data); void getAnalyzeRequestData(const DeckList &deck, QByteArray &data);
public: public:
explicit DeckStatsInterface(CardDatabase &_cardDatabase, QObject *parent = nullptr); explicit DeckStatsInterface(QObject *parent = nullptr);
void analyzeDeck(const DeckList &deck); void analyzeDeck(const DeckList &deck);
}; };

View file

@ -6,12 +6,12 @@
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QRegularExpression> #include <QRegularExpression>
#include <QUrlQuery> #include <QUrlQuery>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/deck_list/deck_list.h> #include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/tree/deck_list_card_node.h> #include <libcockatrice/deck_list/tree/deck_list_card_node.h>
#include <version_string.h> #include <version_string.h>
TappedOutInterface::TappedOutInterface(CardDatabase &_cardDatabase, QObject *parent) TappedOutInterface::TappedOutInterface(QObject *parent) : QObject(parent)
: QObject(parent), cardDatabase(_cardDatabase)
{ {
manager = new QNetworkAccessManager(this); manager = new QNetworkAccessManager(this);
connect(manager, &QNetworkAccessManager::finished, this, &TappedOutInterface::queryFinished); connect(manager, &QNetworkAccessManager::finished, this, &TappedOutInterface::queryFinished);
@ -97,8 +97,8 @@ void TappedOutInterface::analyzeDeck(const DeckList &deck)
void TappedOutInterface::copyDeckSplitMainAndSide(const DeckList &source, DeckList &mainboard, DeckList &sideboard) void TappedOutInterface::copyDeckSplitMainAndSide(const DeckList &source, DeckList &mainboard, DeckList &sideboard)
{ {
auto copyMainOrSide = [this, &mainboard, &sideboard](const auto node, const auto card) { auto copyMainOrSide = [&mainboard, &sideboard](const auto node, const auto card) {
CardInfoPtr dbCard = cardDatabase.query()->getCardInfo(card->getName()); CardInfoPtr dbCard = CardDatabaseManager::query()->getCardInfo(card->getName());
if (!dbCard || dbCard->getIsToken()) { if (!dbCard || dbCard->getIsToken()) {
return; return;
} }

View file

@ -1,14 +1,14 @@
/** /**
* @file tapped_out_interface.h * @file tapped_out_interface.h
* @ingroup ApiInterfaces * @ingroup ApiInterfaces
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef TAPPEDOUT_INTERFACE_H #ifndef TAPPEDOUT_INTERFACE_H
#define TAPPEDOUT_INTERFACE_H #define TAPPEDOUT_INTERFACE_H
#include <libcockatrice/card/database/card_database.h> #include <QLoggingCategory>
#include <libcockatrice/deck_list/deck_list.h> #include <QObject>
inline Q_LOGGING_CATEGORY(TappedOutInterfaceLog, "tapped_out_interface"); inline Q_LOGGING_CATEGORY(TappedOutInterfaceLog, "tapped_out_interface");
@ -29,14 +29,13 @@ class TappedOutInterface : public QObject
private: private:
QNetworkAccessManager *manager; QNetworkAccessManager *manager;
CardDatabase &cardDatabase;
void copyDeckSplitMainAndSide(const DeckList &source, DeckList &mainboard, DeckList &sideboard); void copyDeckSplitMainAndSide(const DeckList &source, DeckList &mainboard, DeckList &sideboard);
private slots: private slots:
void queryFinished(QNetworkReply *reply); void queryFinished(QNetworkReply *reply);
void getAnalyzeRequestData(const DeckList &deck, QByteArray &data); void getAnalyzeRequestData(const DeckList &deck, QByteArray &data);
public: public:
explicit TappedOutInterface(CardDatabase &_cardDatabase, QObject *parent = nullptr); explicit TappedOutInterface(QObject *parent = nullptr);
void analyzeDeck(const DeckList &deck); void analyzeDeck(const DeckList &deck);
}; };

View file

@ -1,8 +1,8 @@
/** /**
* @file deck_link_to_api_transformer.h * @file deck_link_to_api_transformer.h
* @ingroup ApiInterfaces * @ingroup ApiInterfaces
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef DECK_LINK_TO_API_TRANSFORMER_H #ifndef DECK_LINK_TO_API_TRANSFORMER_H
#define DECK_LINK_TO_API_TRANSFORMER_H #define DECK_LINK_TO_API_TRANSFORMER_H

View file

@ -1,8 +1,8 @@
/** /**
* @file interface_json_deck_parser.h * @file interface_json_deck_parser.h
* @ingroup ApiInterfaces * @ingroup ApiInterfaces
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef INTERFACE_JSON_DECK_PARSER_H #ifndef INTERFACE_JSON_DECK_PARSER_H
#define INTERFACE_JSON_DECK_PARSER_H #define INTERFACE_JSON_DECK_PARSER_H

View file

@ -1,8 +1,8 @@
/** /**
* @file spoiler_background_updater.h * @file spoiler_background_updater.h
* @ingroup Client * @ingroup Client
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef COCKATRICE_SPOILER_DOWNLOADER_H #ifndef COCKATRICE_SPOILER_DOWNLOADER_H
#define COCKATRICE_SPOILER_DOWNLOADER_H #define COCKATRICE_SPOILER_DOWNLOADER_H

View file

@ -1,8 +1,8 @@
/** /**
* @file client_update_checker.h * @file client_update_checker.h
* @ingroup ClientUpdate * @ingroup ClientUpdate
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef CLIENT_UPDATE_CHECKER_H #ifndef CLIENT_UPDATE_CHECKER_H
#define CLIENT_UPDATE_CHECKER_H #define CLIENT_UPDATE_CHECKER_H

View file

@ -1,8 +1,8 @@
/** /**
* @file release_channel.h * @file release_channel.h
* @ingroup ClientUpdate * @ingroup ClientUpdate
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef RELEASECHANNEL_H #ifndef RELEASECHANNEL_H
#define RELEASECHANNEL_H #define RELEASECHANNEL_H

View file

@ -1,8 +1,8 @@
/** /**
* @file update_downloader.h * @file update_downloader.h
* @ingroup ClientUpdate * @ingroup ClientUpdate
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef COCKATRICE_UPDATEDOWNLOADER_H #ifndef COCKATRICE_UPDATEDOWNLOADER_H
#define COCKATRICE_UPDATEDOWNLOADER_H #define COCKATRICE_UPDATEDOWNLOADER_H

View file

@ -1,5 +1,7 @@
#include "cache_settings.h" #include "cache_settings.h"
#include "../../interface/card_picture_loader/card_picture_loader_cache_method.h"
#include "../../interface/card_picture_loader/card_picture_loader_local_schemes.h"
#include "../network/update/client/release_channel.h" #include "../network/update/client/release_channel.h"
#include "card_counter_settings.h" #include "card_counter_settings.h"
#include "version_string.h" #include "version_string.h"
@ -278,6 +280,16 @@ SettingsCache::SettingsCache()
networkCacheSize = settings->value("personal/networkCacheSize", NETWORK_CACHE_SIZE_DEFAULT).toInt(); networkCacheSize = settings->value("personal/networkCacheSize", NETWORK_CACHE_SIZE_DEFAULT).toInt();
redirectCacheTtl = settings->value("personal/redirectCacheTtl", NETWORK_REDIRECT_CACHE_TTL_DEFAULT).toInt(); redirectCacheTtl = settings->value("personal/redirectCacheTtl", NETWORK_REDIRECT_CACHE_TTL_DEFAULT).toInt();
cardPictureLoaderCacheMethod =
settings
->value("personal/cardPictureLoaderCacheMethod",
static_cast<int>(CardPictureLoaderCacheMethod::CacheMethod::NETWORK_CACHE))
.toInt();
localCardImageStorageNamingScheme =
settings
->value("personal/localCardImageStorageNamingScheme",
static_cast<int>(CardPictureLoaderLocalSchemes::NamingScheme::Set_Folder_Name_Set_Collector))
.toInt();
picDownload = settings->value("personal/picturedownload", true).toBool(); picDownload = settings->value("personal/picturedownload", true).toBool();
showStatusBar = settings->value("personal/showStatusBar", false).toBool(); showStatusBar = settings->value("personal/showStatusBar", false).toBool();
@ -297,9 +309,11 @@ SettingsCache::SettingsCache()
cardViewExpandedRowsMax = settings->value("interface/cardViewExpandedRowsMax", 20).toInt(); cardViewExpandedRowsMax = settings->value("interface/cardViewExpandedRowsMax", 20).toInt();
closeEmptyCardView = settings->value("interface/closeEmptyCardView", true).toBool(); closeEmptyCardView = settings->value("interface/closeEmptyCardView", true).toBool();
focusCardViewSearchBar = settings->value("interface/focusCardViewSearchBar", true).toBool(); focusCardViewSearchBar = settings->value("interface/focusCardViewSearchBar", true).toBool();
keepGameChatFocus = settings->value("interface/keepGameChatFocus", false).toBool();
showDragSelectionCount = settings->value("interface/showlassoselectioncount", true).toBool(); showDragSelectionCount = settings->value("interface/showlassoselectioncount", true).toBool();
showTotalSelectionCount = settings->value("interface/showpersistentselectioncount", true).toBool(); showTotalSelectionCount = settings->value("interface/showpersistentselectioncount", true).toBool();
showSubtypeSelectionTally = settings->value("interface/showsubtypeselectiontally", true).toBool();
showShortcuts = settings->value("menu/showshortcuts", true).toBool(); showShortcuts = settings->value("menu/showshortcuts", true).toBool();
showGameSelectorFilterToolbar = settings->value("menu/showgameselectorfiltertoolbar", true).toBool(); showGameSelectorFilterToolbar = settings->value("menu/showgameselectorfiltertoolbar", true).toBool();
@ -358,6 +372,7 @@ SettingsCache::SettingsCache()
openDeckInNewTab = settings->value("editor/openDeckInNewTab", false).toBool(); openDeckInNewTab = settings->value("editor/openDeckInNewTab", false).toBool();
rewindBufferingMs = settings->value("replay/rewindBufferingMs", 200).toInt(); rewindBufferingMs = settings->value("replay/rewindBufferingMs", 200).toInt();
styleUserList = settings->value("appearance/styleUserList", true).toBool();
chatMention = settings->value("chat/mention", true).toBool(); chatMention = settings->value("chat/mention", true).toBool();
chatMentionCompleter = settings->value("chat/mentioncompleter", true).toBool(); chatMentionCompleter = settings->value("chat/mentioncompleter", true).toBool();
chatMentionForeground = settings->value("chat/mentionforeground", true).toBool(); chatMentionForeground = settings->value("chat/mentionforeground", true).toBool();
@ -376,6 +391,7 @@ SettingsCache::SettingsCache()
ignoreUnregisteredUsers = settings->value("chat/ignore_unregistered", false).toBool(); ignoreUnregisteredUsers = settings->value("chat/ignore_unregistered", false).toBool();
ignoreUnregisteredUserMessages = settings->value("chat/ignore_unregistered_messages", false).toBool(); ignoreUnregisteredUserMessages = settings->value("chat/ignore_unregistered_messages", false).toBool();
ignoreNonBuddyUserMessages = settings->value("chat/ignore_nonbuddy_messages", false).toBool();
scaleCards = settings->value("cards/scaleCards", true).toBool(); scaleCards = settings->value("cards/scaleCards", true).toBool();
verticalCardOverlapPercent = settings->value("cards/verticalCardOverlapPercent", 33).toInt(); verticalCardOverlapPercent = settings->value("cards/verticalCardOverlapPercent", 33).toInt();
@ -444,6 +460,13 @@ void SettingsCache::setFocusCardViewSearchBar(QT_STATE_CHANGED_T value)
settings->setValue("interface/focusCardViewSearchBar", focusCardViewSearchBar); settings->setValue("interface/focusCardViewSearchBar", focusCardViewSearchBar);
} }
void SettingsCache::setKeepGameChatFocus(QT_STATE_CHANGED_T value)
{
keepGameChatFocus = value;
settings->setValue("interface/keepGameChatFocus", keepGameChatFocus);
emit keepGameChatFocusChanged(keepGameChatFocus);
}
void SettingsCache::setKnownMissingFeatures(const QString &_knownMissingFeatures) void SettingsCache::setKnownMissingFeatures(const QString &_knownMissingFeatures)
{ {
knownMissingFeatures = _knownMissingFeatures; knownMissingFeatures = _knownMissingFeatures;
@ -1024,6 +1047,13 @@ void SettingsCache::setRewindBufferingMs(int _rewindBufferingMs)
settings->setValue("replay/rewindBufferingMs", rewindBufferingMs); settings->setValue("replay/rewindBufferingMs", rewindBufferingMs);
} }
void SettingsCache::setStyleUserList(QT_STATE_CHANGED_T _styleUserList)
{
styleUserList = static_cast<bool>(_styleUserList);
settings->setValue("appearance/styleUserList", styleUserList);
emit styleUserListChanged();
}
void SettingsCache::setChatMention(QT_STATE_CHANGED_T _chatMention) void SettingsCache::setChatMention(QT_STATE_CHANGED_T _chatMention)
{ {
chatMention = static_cast<bool>(_chatMention); chatMention = static_cast<bool>(_chatMention);
@ -1105,6 +1135,12 @@ void SettingsCache::setIgnoreUnregisteredUserMessages(QT_STATE_CHANGED_T _ignore
settings->setValue("chat/ignore_unregistered_messages", ignoreUnregisteredUserMessages); settings->setValue("chat/ignore_unregistered_messages", ignoreUnregisteredUserMessages);
} }
void SettingsCache::setIgnoreNonBuddyUserMessages(QT_STATE_CHANGED_T _ignoreNonBuddyUserMessages)
{
ignoreNonBuddyUserMessages = static_cast<bool>(_ignoreNonBuddyUserMessages);
settings->setValue("chat/ignore_nonbuddy_messages", ignoreNonBuddyUserMessages);
}
void SettingsCache::setPixmapCacheSize(const int _pixmapCacheSize) void SettingsCache::setPixmapCacheSize(const int _pixmapCacheSize)
{ {
pixmapCacheSize = _pixmapCacheSize; pixmapCacheSize = _pixmapCacheSize;
@ -1112,6 +1148,13 @@ void SettingsCache::setPixmapCacheSize(const int _pixmapCacheSize)
emit pixmapCacheSizeChanged(pixmapCacheSize); emit pixmapCacheSizeChanged(pixmapCacheSize);
} }
void SettingsCache::setCardImageCacheMethod(const CardPictureLoaderCacheMethod::CacheMethod _cardImageCachingMethod)
{
cardPictureLoaderCacheMethod = static_cast<int>(_cardImageCachingMethod);
settings->setValue("personal/cardPictureLoaderCacheMethod", cardPictureLoaderCacheMethod);
emit cardPictureLoaderCacheMethodChanged(cardPictureLoaderCacheMethod);
}
void SettingsCache::setNetworkCacheSizeInMB(const int _networkCacheSize) void SettingsCache::setNetworkCacheSizeInMB(const int _networkCacheSize)
{ {
networkCacheSize = _networkCacheSize; networkCacheSize = _networkCacheSize;
@ -1126,6 +1169,14 @@ void SettingsCache::setNetworkRedirectCacheTtl(const int _redirectCacheTtl)
emit redirectCacheTtlChanged(redirectCacheTtl); emit redirectCacheTtlChanged(redirectCacheTtl);
} }
void SettingsCache::setLocalCardImageStorageNamingScheme(
const CardPictureLoaderLocalSchemes::NamingScheme _localCardImageStorageNamingScheme)
{
localCardImageStorageNamingScheme = static_cast<int>(_localCardImageStorageNamingScheme);
settings->setValue("personal/localCardImageStorageNamingScheme", localCardImageStorageNamingScheme);
emit localCardImageStorageNamingSchemeChanged(localCardImageStorageNamingScheme);
}
void SettingsCache::setClientID(const QString &_clientID) void SettingsCache::setClientID(const QString &_clientID)
{ {
clientID = _clientID; clientID = _clientID;
@ -1345,6 +1396,12 @@ void SettingsCache::setShowTotalSelectionCount(QT_STATE_CHANGED_T _showTotalSele
settings->setValue("interface/showpersistentselectioncount", showTotalSelectionCount); settings->setValue("interface/showpersistentselectioncount", showTotalSelectionCount);
} }
void SettingsCache::setShowSubtypeSelectionTally(QT_STATE_CHANGED_T _showSubtypeSelectionTally)
{
showSubtypeSelectionTally = static_cast<bool>(_showSubtypeSelectionTally);
settings->setValue("interface/showsubtypeselectiontally", showSubtypeSelectionTally);
}
void SettingsCache::loadPaths() void SettingsCache::loadPaths()
{ {
QString dataPath = getDataPath(); QString dataPath = getDataPath();

View file

@ -1,12 +1,14 @@
/** /**
* @file cache_settings.h * @file cache_settings.h
* @ingroup Settings * @ingroup Settings
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef SETTINGSCACHE_H #ifndef SETTINGSCACHE_H
#define SETTINGSCACHE_H #define SETTINGSCACHE_H
#include "../../interface/card_picture_loader/card_picture_loader_cache_method.h"
#include "../../interface/card_picture_loader/card_picture_loader_local_schemes.h"
#include "shortcuts_settings.h" #include "shortcuts_settings.h"
#include <QDate> #include <QDate>
@ -181,15 +183,20 @@ signals:
void soundThemeChanged(); void soundThemeChanged();
void ignoreUnregisteredUsersChanged(); void ignoreUnregisteredUsersChanged();
void ignoreUnregisteredUserMessagesChanged(); void ignoreUnregisteredUserMessagesChanged();
void ignoreNonBuddyUserMessagesChanged();
void pixmapCacheSizeChanged(int newSizeInMBs); void pixmapCacheSizeChanged(int newSizeInMBs);
void networkCacheSizeChanged(int newSizeInMBs); void networkCacheSizeChanged(int newSizeInMBs);
void redirectCacheTtlChanged(int newTtl); void redirectCacheTtlChanged(int newTtl);
void cardPictureLoaderCacheMethodChanged(int cardPictureLoaderCacheMethod);
void localCardImageStorageNamingSchemeChanged(int localCardImageStorageNamingScheme);
void masterVolumeChanged(int value); void masterVolumeChanged(int value);
void styleUserListChanged();
void chatMentionCompleterChanged(); void chatMentionCompleterChanged();
void downloadSpoilerTimeIndexChanged(); void downloadSpoilerTimeIndexChanged();
void downloadSpoilerStatusChanged(); void downloadSpoilerStatusChanged();
void useTearOffMenusChanged(bool state); void useTearOffMenusChanged(bool state);
void roundCardCornersChanged(bool roundCardCorners); void roundCardCornersChanged(bool roundCardCorners);
void keepGameChatFocusChanged(bool value);
private: private:
QSettings *settings; QSettings *settings;
@ -278,6 +285,7 @@ private:
bool autoRotateSidewaysLayoutCards; bool autoRotateSidewaysLayoutCards;
bool openDeckInNewTab; bool openDeckInNewTab;
int rewindBufferingMs; int rewindBufferingMs;
bool styleUserList;
bool chatMention; bool chatMention;
bool chatMentionCompleter; bool chatMentionCompleter;
QString chatMentionColor; QString chatMentionColor;
@ -290,6 +298,7 @@ private:
QString soundThemeName; QString soundThemeName;
bool ignoreUnregisteredUsers; bool ignoreUnregisteredUsers;
bool ignoreUnregisteredUserMessages; bool ignoreUnregisteredUserMessages;
bool ignoreNonBuddyUserMessages;
QString picUrl; QString picUrl;
QString picUrlFallback; QString picUrlFallback;
QString clientID; QString clientID;
@ -300,9 +309,12 @@ private:
int cardViewExpandedRowsMax; int cardViewExpandedRowsMax;
bool closeEmptyCardView; bool closeEmptyCardView;
bool focusCardViewSearchBar; bool focusCardViewSearchBar;
bool keepGameChatFocus;
int pixmapCacheSize; int pixmapCacheSize;
int networkCacheSize; int networkCacheSize;
int redirectCacheTtl; int redirectCacheTtl;
int cardPictureLoaderCacheMethod;
int localCardImageStorageNamingScheme;
bool scaleCards; bool scaleCards;
int verticalCardOverlapPercent; int verticalCardOverlapPercent;
bool showMessagePopups; bool showMessagePopups;
@ -343,6 +355,7 @@ private:
bool showStatusBar; bool showStatusBar;
bool showDragSelectionCount; bool showDragSelectionCount;
bool showTotalSelectionCount; bool showTotalSelectionCount;
bool showSubtypeSelectionTally;
public: public:
SettingsCache(); SettingsCache();
@ -466,6 +479,10 @@ public:
{ {
return showTotalSelectionCount; return showTotalSelectionCount;
} }
[[nodiscard]] bool getShowSubtypeSelectionTally() const
{
return showSubtypeSelectionTally;
}
[[nodiscard]] bool getNotificationsEnabled() const [[nodiscard]] bool getNotificationsEnabled() const
{ {
return notificationsEnabled; return notificationsEnabled;
@ -728,6 +745,10 @@ public:
{ {
return rewindBufferingMs; return rewindBufferingMs;
} }
[[nodiscard]] bool getStyleUserList() const
{
return styleUserList;
}
[[nodiscard]] bool getChatMention() const [[nodiscard]] bool getChatMention() const
{ {
return chatMention; return chatMention;
@ -782,10 +803,18 @@ public:
{ {
return ignoreUnregisteredUserMessages; return ignoreUnregisteredUserMessages;
} }
[[nodiscard]] bool getIgnoreNonBuddyUserMessages() const
{
return ignoreNonBuddyUserMessages;
}
[[nodiscard]] int getPixmapCacheSize() const [[nodiscard]] int getPixmapCacheSize() const
{ {
return pixmapCacheSize; return pixmapCacheSize;
} }
[[nodiscard]] CardPictureLoaderCacheMethod::CacheMethod getCardPictureLoaderCacheMethod() const
{
return static_cast<CardPictureLoaderCacheMethod::CacheMethod>(cardPictureLoaderCacheMethod);
}
[[nodiscard]] int getNetworkCacheSizeInMB() const [[nodiscard]] int getNetworkCacheSizeInMB() const
{ {
return networkCacheSize; return networkCacheSize;
@ -794,6 +823,10 @@ public:
{ {
return redirectCacheTtl; return redirectCacheTtl;
} }
[[nodiscard]] CardPictureLoaderLocalSchemes::NamingScheme getLocalCardImageStorageNamingScheme() const
{
return static_cast<CardPictureLoaderLocalSchemes::NamingScheme>(localCardImageStorageNamingScheme);
}
[[nodiscard]] bool getScaleCards() const [[nodiscard]] bool getScaleCards() const
{ {
return scaleCards; return scaleCards;
@ -915,6 +948,7 @@ public:
void setCardViewExpandedRowsMax(int value); void setCardViewExpandedRowsMax(int value);
void setCloseEmptyCardView(QT_STATE_CHANGED_T value); void setCloseEmptyCardView(QT_STATE_CHANGED_T value);
void setFocusCardViewSearchBar(QT_STATE_CHANGED_T value); void setFocusCardViewSearchBar(QT_STATE_CHANGED_T value);
void setKeepGameChatFocus(QT_STATE_CHANGED_T value);
QString getClientID() override QString getClientID() override
{ {
return clientID; return clientID;
@ -947,6 +981,10 @@ public:
{ {
return focusCardViewSearchBar; return focusCardViewSearchBar;
} }
[[nodiscard]] bool getKeepGameChatFocus() const
{
return keepGameChatFocus;
}
[[nodiscard]] ShortcutsSettings &shortcuts() const [[nodiscard]] ShortcutsSettings &shortcuts() const
{ {
return *shortcutsSettings; return *shortcutsSettings;
@ -1086,6 +1124,7 @@ public slots:
void setAutoRotateSidewaysLayoutCards(QT_STATE_CHANGED_T _autoRotateSidewaysLayoutCards); void setAutoRotateSidewaysLayoutCards(QT_STATE_CHANGED_T _autoRotateSidewaysLayoutCards);
void setOpenDeckInNewTab(QT_STATE_CHANGED_T _openDeckInNewTab); void setOpenDeckInNewTab(QT_STATE_CHANGED_T _openDeckInNewTab);
void setRewindBufferingMs(int _rewindBufferingMs); void setRewindBufferingMs(int _rewindBufferingMs);
void setStyleUserList(QT_STATE_CHANGED_T _styleUserList);
void setChatMention(QT_STATE_CHANGED_T _chatMention); void setChatMention(QT_STATE_CHANGED_T _chatMention);
void setChatMentionCompleter(QT_STATE_CHANGED_T _chatMentionCompleter); void setChatMentionCompleter(QT_STATE_CHANGED_T _chatMentionCompleter);
void setChatMentionForeground(QT_STATE_CHANGED_T _chatMentionForeground); void setChatMentionForeground(QT_STATE_CHANGED_T _chatMentionForeground);
@ -1097,9 +1136,13 @@ public slots:
void setSoundThemeName(const QString &_soundThemeName); void setSoundThemeName(const QString &_soundThemeName);
void setIgnoreUnregisteredUsers(QT_STATE_CHANGED_T _ignoreUnregisteredUsers); void setIgnoreUnregisteredUsers(QT_STATE_CHANGED_T _ignoreUnregisteredUsers);
void setIgnoreUnregisteredUserMessages(QT_STATE_CHANGED_T _ignoreUnregisteredUserMessages); void setIgnoreUnregisteredUserMessages(QT_STATE_CHANGED_T _ignoreUnregisteredUserMessages);
void setIgnoreNonBuddyUserMessages(QT_STATE_CHANGED_T _ignoreNonBuddyUserMessages);
void setPixmapCacheSize(const int _pixmapCacheSize); void setPixmapCacheSize(const int _pixmapCacheSize);
void setCardImageCacheMethod(CardPictureLoaderCacheMethod::CacheMethod _cardImageCachingMethod);
void setNetworkCacheSizeInMB(const int _networkCacheSize); void setNetworkCacheSizeInMB(const int _networkCacheSize);
void setNetworkRedirectCacheTtl(const int _redirectCacheTtl); void setNetworkRedirectCacheTtl(const int _redirectCacheTtl);
void setLocalCardImageStorageNamingScheme(
const CardPictureLoaderLocalSchemes::NamingScheme _localCardImageStorageNamingScheme);
void setCardScaling(const QT_STATE_CHANGED_T _scaleCards); void setCardScaling(const QT_STATE_CHANGED_T _scaleCards);
void setStackCardOverlapPercent(const int _verticalCardOverlapPercent); void setStackCardOverlapPercent(const int _verticalCardOverlapPercent);
void setShowMessagePopups(const QT_STATE_CHANGED_T _showMessagePopups); void setShowMessagePopups(const QT_STATE_CHANGED_T _showMessagePopups);
@ -1138,5 +1181,6 @@ public slots:
void setRoundCardCorners(bool _roundCardCorners); void setRoundCardCorners(bool _roundCardCorners);
void setShowDragSelectionCount(QT_STATE_CHANGED_T _showDragSelectionCount); void setShowDragSelectionCount(QT_STATE_CHANGED_T _showDragSelectionCount);
void setShowTotalSelectionCount(QT_STATE_CHANGED_T _showTotalSelectionCount); void setShowTotalSelectionCount(QT_STATE_CHANGED_T _showTotalSelectionCount);
void setShowSubtypeSelectionTally(QT_STATE_CHANGED_T _showSubtypeSelectionTally);
}; };
#endif #endif

View file

@ -1,8 +1,8 @@
/** /**
* @file card_counter_settings.h * @file card_counter_settings.h
* @ingroup GameSettings * @ingroup GameSettings
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef CARD_COUNTER_SETTINGS_H #ifndef CARD_COUNTER_SETTINGS_H
#define CARD_COUNTER_SETTINGS_H #define CARD_COUNTER_SETTINGS_H

View file

@ -1,8 +1,8 @@
/** /**
* @file shortcut_treeview.h * @file shortcut_treeview.h
* @ingroup CoreSettings * @ingroup CoreSettings
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef SHORTCUT_TREEVIEW_H #ifndef SHORTCUT_TREEVIEW_H
#define SHORTCUT_TREEVIEW_H #define SHORTCUT_TREEVIEW_H

View file

@ -64,8 +64,13 @@ ShortcutsSettings::ShortcutsSettings(const QString &settingsPath, QObject *paren
} }
} }
/// PR 5079 changes Textbox/unfocusTextBox to Player/unfocusTextBox and tab_game/aFocusChat to Player/aFocusChat. /**
/// A migration is necessary to let players keep their already configured shortcuts. * @brief Migrates legacy shortcut key names to current naming scheme.
*
* PR 5079 changed Textbox/unfocusTextBox to Player/unfocusTextBox and
* tab_game/aFocusChat to Player/aFocusChat. This migration allows players
* to keep their already configured shortcuts.
*/
void ShortcutsSettings::migrateShortcuts() void ShortcutsSettings::migrateShortcuts()
{ {
if (QFile(settingsFilePath).exists()) { if (QFile(settingsFilePath).exists()) {
@ -236,9 +241,7 @@ bool ShortcutsSettings::isValid(const QString &name, const QString &sequences) c
return findOverlaps(name, sequences).isEmpty(); return findOverlaps(name, sequences).isEmpty();
} }
/** /** @brief Checks if the shortcut is a shortcut that is active in all windows. */
* Checks if the shortcut is a shortcut that is active in all windows
*/
static bool isAlwaysActiveShortcut(const QString &shortcutName) static bool isAlwaysActiveShortcut(const QString &shortcutName)
{ {
return shortcutName.startsWith("MainWindow") || shortcutName.startsWith("Tabs"); return shortcutName.startsWith("MainWindow") || shortcutName.startsWith("Tabs");

View file

@ -1,8 +1,8 @@
/** /**
* @file shortcuts_settings.h * @file shortcuts_settings.h
* @ingroup CoreSettings * @ingroup CoreSettings
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef SHORTCUTSSETTINGS_H #ifndef SHORTCUTSSETTINGS_H
#define SHORTCUTSSETTINGS_H #define SHORTCUTSSETTINGS_H
@ -223,6 +223,10 @@ private:
{"TabDeckEditor/aLoadDeck", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load Deck..."), {"TabDeckEditor/aLoadDeck", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load Deck..."),
parseSequenceString("Ctrl+O"), parseSequenceString("Ctrl+O"),
ShortcutGroup::Deck_Editor)}, ShortcutGroup::Deck_Editor)},
{"TabDeckEditor/aLoadDeckFromWebsite",
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load deck from online service..."),
parseSequenceString("Ctrl+Shift+O"),
ShortcutGroup::Deck_Editor)},
{"TabDeckEditor/aLoadDeckFromClipboard", {"TabDeckEditor/aLoadDeckFromClipboard",
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load Deck from Clipboard..."), ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load Deck from Clipboard..."),
parseSequenceString("Ctrl+Shift+V"), parseSequenceString("Ctrl+Shift+V"),
@ -283,6 +287,10 @@ private:
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load Deck from Clipboard..."), ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load Deck from Clipboard..."),
parseSequenceString("Ctrl+Shift+V"), parseSequenceString("Ctrl+Shift+V"),
ShortcutGroup::Game_Lobby)}, ShortcutGroup::Game_Lobby)},
{"DeckViewContainer/loadFromWebsiteButton",
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load from website..."),
parseSequenceString("Ctrl+Shift+O"),
ShortcutGroup::Game_Lobby)},
{"DeckViewContainer/unloadDeckButton", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Unload Deck"), {"DeckViewContainer/unloadDeckButton", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Unload Deck"),
parseSequenceString("Ctrl+Alt+U"), parseSequenceString("Ctrl+Alt+U"),
ShortcutGroup::Game_Lobby)}, ShortcutGroup::Game_Lobby)},

View file

@ -1,8 +1,8 @@
/** /**
* @file sound_engine.h * @file sound_engine.h
* @ingroup Core * @ingroup Core
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef SOUNDENGINE_H #ifndef SOUNDENGINE_H
#define SOUNDENGINE_H #define SOUNDENGINE_H

View file

@ -1,8 +1,8 @@
/** /**
* @file deck_filter_string.h * @file deck_filter_string.h
* @ingroup DeckStorageWidgets * @ingroup DeckStorageWidgets
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef DECK_FILTER_STRING_H #ifndef DECK_FILTER_STRING_H
#define DECK_FILTER_STRING_H #define DECK_FILTER_STRING_H

View file

@ -1,8 +1,8 @@
/** /**
* @file filter_builder.h * @file filter_builder.h
* @ingroup CardDatabaseModelFilters * @ingroup CardDatabaseModelFilters
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef FILTERBUILDER_H #ifndef FILTERBUILDER_H
#define FILTERBUILDER_H #define FILTERBUILDER_H

View file

@ -1,8 +1,8 @@
/** /**
* @file filter_tree_model.h * @file filter_tree_model.h
* @ingroup CardDatabaseModelFilters * @ingroup CardDatabaseModelFilters
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef FILTERTREEMODEL_H #ifndef FILTERTREEMODEL_H
#define FILTERTREEMODEL_H #define FILTERTREEMODEL_H

View file

@ -2,8 +2,8 @@
* @file syntax_help.h * @file syntax_help.h
* @ingroup CardDatabaseModelFilters * @ingroup CardDatabaseModelFilters
* @ingroup DeckStorageWidgets * @ingroup DeckStorageWidgets
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef SEARCH_SYNTAX_HELP_H #ifndef SEARCH_SYNTAX_HELP_H
#define SEARCH_SYNTAX_HELP_H #define SEARCH_SYNTAX_HELP_H

View file

@ -1,9 +1,9 @@
#include "abstract_game.h" #include "abstract_game.h"
#include "../interface/widgets/tabs/tab_game.h" #include "../interface/widgets/tabs/tab_game.h"
#include "player/player.h" #include "player/player_logic.h"
AbstractGame::AbstractGame(TabGame *_tab) : QObject(_tab), tab(_tab) AbstractGame::AbstractGame(QObject *_parent) : QObject(_parent)
{ {
gameMetaInfo = new GameMetaInfo(this); gameMetaInfo = new GameMetaInfo(this);
gameEventHandler = new GameEventHandler(this); gameEventHandler = new GameEventHandler(this);
@ -44,7 +44,7 @@ void AbstractGame::setActiveCard(CardItem *card)
CardItem *AbstractGame::getCard(int playerId, const QString &zoneName, int cardId) const CardItem *AbstractGame::getCard(int playerId, const QString &zoneName, int cardId) const
{ {
Player *player = playerManager->getPlayer(playerId); PlayerLogic *player = playerManager->getPlayer(playerId);
if (!player) { if (!player) {
return nullptr; return nullptr;
} }

View file

@ -1,8 +1,8 @@
/** /**
* @file abstract_game.h * @file abstract_game.h
* @ingroup GameLogic * @ingroup GameLogic
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef COCKATRICE_ABSTRACT_GAME_H #ifndef COCKATRICE_ABSTRACT_GAME_H
#define COCKATRICE_ABSTRACT_GAME_H #define COCKATRICE_ABSTRACT_GAME_H
@ -16,26 +16,19 @@
#include <libcockatrice/protocol/pb/game_replay.pb.h> #include <libcockatrice/protocol/pb/game_replay.pb.h>
class CardItem; class CardItem;
class TabGame;
class AbstractGame : public QObject class AbstractGame : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit AbstractGame(TabGame *tab); explicit AbstractGame(QObject *parent);
TabGame *tab;
GameMetaInfo *gameMetaInfo; GameMetaInfo *gameMetaInfo;
GameState *gameState; GameState *gameState;
GameEventHandler *gameEventHandler; GameEventHandler *gameEventHandler;
PlayerManager *playerManager; PlayerManager *playerManager;
CardItem *activeCard; CardItem *activeCard;
TabGame *getTab() const
{
return tab;
}
GameMetaInfo *getGameMetaInfo() GameMetaInfo *getGameMetaInfo()
{ {
return gameMetaInfo; return gameMetaInfo;

View file

@ -0,0 +1,48 @@
#include "arrow_registry.h"
#include "../game_graphics/board/arrow_item.h"
void ArrowRegistry::insert(QSharedPointer<ArrowData> data, ArrowItem *arrow)
{
const ArrowKey key{data->creatorId, data->id};
if (auto *existing = take(data->creatorId, data->id)) {
existing->delArrow();
}
dataStore.insert(key, data);
items.insert(key, arrow);
byPlayer[data->creatorId].insert(data->id);
}
ArrowItem *ArrowRegistry::take(int creatorId, int arrowId)
{
const ArrowKey key{creatorId, arrowId};
dataStore.remove(key);
auto &playerSet = byPlayer[creatorId];
playerSet.remove(arrowId);
if (playerSet.isEmpty()) {
byPlayer.remove(creatorId);
}
return items.take(key);
}
ArrowItem *ArrowRegistry::get(int creatorId, int arrowId) const
{
return items.value(ArrowKey{creatorId, arrowId}, nullptr);
}
bool ArrowRegistry::contains(int creatorId, int arrowId) const
{
return items.contains(ArrowKey{creatorId, arrowId});
}
QSet<int> ArrowRegistry::idsForPlayer(int playerId) const
{
return byPlayer.value(playerId);
}
QList<ArrowItem *> ArrowRegistry::all() const
{
return items.values();
}

View file

@ -0,0 +1,43 @@
#ifndef COCKATRICE_ARROW_REGISTRY_H
#define COCKATRICE_ARROW_REGISTRY_H
#include "board/arrow_data.h"
#include <QMap>
#include <QSet>
#include <QSharedPointer>
class ArrowItem;
struct ArrowKey
{
int creatorId;
int arrowId;
bool operator<(const ArrowKey &other) const
{
if (creatorId != other.creatorId) {
return creatorId < other.creatorId;
}
return arrowId < other.arrowId;
}
};
class ArrowRegistry
{
public:
void insert(QSharedPointer<ArrowData> data, ArrowItem *arrow);
ArrowItem *take(int creatorId, int arrowId);
[[nodiscard]] ArrowItem *get(int creatorId, int arrowId) const;
[[nodiscard]] bool contains(int creatorId, int arrowId) const;
[[nodiscard]] QSet<int> idsForPlayer(int playerId) const;
[[nodiscard]] QList<ArrowItem *> all() const;
private:
QMap<ArrowKey, QSharedPointer<ArrowData>> dataStore;
QMap<ArrowKey, ArrowItem *> items;
QMap<int, QSet<int>> byPlayer;
};
#endif

View file

@ -0,0 +1,21 @@
#include "arrow_data.h"
ArrowData ArrowData::fromProto(const ServerInfo_Arrow &arrow, int creatorId, bool isLocalCreator)
{
ArrowData data;
data.creatorId = creatorId;
data.isLocalCreator = isLocalCreator;
data.id = arrow.id();
data.startPlayerId = arrow.start_player_id();
data.startZone = QString::fromStdString(arrow.start_zone());
data.startCardId = arrow.start_card_id();
data.targetPlayerId = arrow.target_player_id();
data.color = convertColorToQColor(arrow.arrow_color());
if (arrow.has_target_zone()) {
data.targetZone = QString::fromStdString(arrow.target_zone());
data.targetCardId = arrow.target_card_id();
}
return data;
}

View file

@ -0,0 +1,30 @@
#ifndef COCKATRICE_ARROW_DATA_H
#define COCKATRICE_ARROW_DATA_H
#include <QColor>
#include <QString>
#include <libcockatrice/protocol/pb/serverinfo_arrow.pb.h>
#include <libcockatrice/utility/color.h>
struct ArrowData
{
int creatorId = -1;
bool isLocalCreator = false;
int id = -1;
int startPlayerId = -1;
QString startZone = "";
int startCardId = -1;
int targetPlayerId = -1;
QString targetZone = "";
int targetCardId = -1;
QColor color = "";
static ArrowData fromProto(const ServerInfo_Arrow &arrow, int creatorId, bool isLocalCreator);
bool isPlayerTargeted() const
{
return targetZone.isEmpty();
}
};
#endif // COCKATRICE_ARROW_DATA_H

View file

@ -1,43 +0,0 @@
#include "arrow_target.h"
#include "../player/player.h"
#include "arrow_item.h"
ArrowTarget::ArrowTarget(Player *_owner, QGraphicsItem *parent)
: AbstractGraphicsItem(parent), owner(_owner), beingPointedAt(false)
{
setFlag(ItemSendsScenePositionChanges);
}
ArrowTarget::~ArrowTarget()
{
for (int i = 0; i < arrowsFrom.size(); ++i) {
arrowsFrom[i]->setStartItem(0);
arrowsFrom[i]->delArrow();
}
for (int i = 0; i < arrowsTo.size(); ++i) {
arrowsTo[i]->setTargetItem(0);
arrowsTo[i]->delArrow();
}
}
void ArrowTarget::setBeingPointedAt(bool _beingPointedAt)
{
beingPointedAt = _beingPointedAt;
update();
}
QVariant ArrowTarget::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
{
if (change == ItemScenePositionHasChanged && scene()) {
for (auto *arrow : arrowsFrom) {
arrow->updatePath();
}
for (auto *arrow : arrowsTo) {
arrow->updatePath();
}
}
return QGraphicsItem::itemChange(change, value);
}

View file

@ -1,70 +0,0 @@
/**
* @file arrow_target.h
* @ingroup GameGraphics
* @brief TODO: Document this.
*/
#ifndef ARROWTARGET_H
#define ARROWTARGET_H
#include "../../game_graphics/board/abstract_graphics_item.h"
#include <QList>
class Player;
class ArrowItem;
class ArrowTarget : public AbstractGraphicsItem
{
Q_OBJECT
protected:
Player *owner;
private:
bool beingPointedAt;
QList<ArrowItem *> arrowsFrom, arrowsTo;
public:
explicit ArrowTarget(Player *_owner, QGraphicsItem *parent = nullptr);
~ArrowTarget() override;
[[nodiscard]] Player *getOwner() const
{
return owner;
}
void setBeingPointedAt(bool _beingPointedAt);
[[nodiscard]] bool getBeingPointedAt() const
{
return beingPointedAt;
}
[[nodiscard]] const QList<ArrowItem *> &getArrowsFrom() const
{
return arrowsFrom;
}
void addArrowFrom(ArrowItem *arrow)
{
arrowsFrom.append(arrow);
}
void removeArrowFrom(ArrowItem *arrow)
{
arrowsFrom.removeOne(arrow);
}
[[nodiscard]] const QList<ArrowItem *> &getArrowsTo() const
{
return arrowsTo;
}
void addArrowTo(ArrowItem *arrow)
{
arrowsTo.append(arrow);
}
void removeArrowTo(ArrowItem *arrow)
{
arrowsTo.removeOne(arrow);
}
protected:
QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override;
};
#endif

View file

@ -1,6 +1,6 @@
#include "card_list.h" #include "card_list.h"
#include "card_item.h" #include "../../game_graphics/board/card_item.h"
#include <QDebug> #include <QDebug>
#include <algorithm> #include <algorithm>

View file

@ -1,8 +1,8 @@
/** /**
* @file card_list.h * @file card_list.h
* @ingroup GameLogicCards * @ingroup GameLogicCards
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef CARDLIST_H #ifndef CARDLIST_H
#define CARDLIST_H #define CARDLIST_H

View file

@ -0,0 +1,111 @@
#include "card_state.h"
void CardState::resetState(bool keepAnnotations)
{
attacking = false;
counters.clear();
pt.clear();
if (!keepAnnotations) {
annotation.clear();
}
attachedTo = nullptr;
}
void CardState::setZone(CardZoneLogic *_zone)
{
if (zone == _zone) {
return;
}
zone = _zone;
emit zoneChanged(this, zone);
emit stateChanged();
}
void CardState::setAttacking(bool _attacking)
{
if (attacking == _attacking) {
return;
}
attacking = _attacking;
emit attackingChanged(_attacking);
emit stateChanged();
}
void CardState::insertCounter(int id, int value)
{
counters.insert(id, value);
emit countersChanged(counters);
emit stateChanged();
}
void CardState::setCounter(int id, int value)
{
if (value) {
counters[id] = value;
} else {
counters.remove(id);
}
emit countersChanged(counters);
emit stateChanged();
}
void CardState::clearCounters()
{
counters.clear();
emit countersChanged(counters);
emit stateChanged();
}
void CardState::setAnnotation(const QString &_annotation)
{
if (annotation == _annotation) {
return;
}
annotation = _annotation;
emit annotationChanged(annotation);
emit stateChanged();
}
void CardState::setPT(const QString &_pt)
{
if (pt == _pt) {
return;
}
pt = _pt;
emit ptChanged(pt);
emit stateChanged();
}
void CardState::setDoesntUntap(bool _doesntUntap)
{
if (doesntUntap == _doesntUntap) {
return;
}
doesntUntap = _doesntUntap;
emit doesntUntapChanged(_doesntUntap);
emit stateChanged();
}
void CardState::setDestroyOnZoneChange(bool _destroyOnZoneChange)
{
if (destroyOnZoneChange == _destroyOnZoneChange) {
return;
}
destroyOnZoneChange = _destroyOnZoneChange;
emit destroyOnZoneChangeChanged(_destroyOnZoneChange);
emit stateChanged();
}
void CardState::setAttachedTo(CardItem *_attachedTo)
{
if (attachedTo == _attachedTo) {
return;
}
attachedTo = _attachedTo;
emit attachedToChanged(_attachedTo);
emit stateChanged();
}

View file

@ -0,0 +1,103 @@
#ifndef COCKATRICE_CARD_STATE_H
#define COCKATRICE_CARD_STATE_H
#include <QMap>
#include <QObject>
class CardZoneLogic;
class CardItem;
class CardState : public QObject
{
Q_OBJECT
private:
bool attacking = false;
QMap<int, int> counters;
QString annotation;
QString pt;
bool doesntUntap = false;
bool destroyOnZoneChange = false;
CardItem *attachedTo = nullptr;
CardZoneLogic *zone = nullptr;
signals:
void stateChanged();
void attackingChanged(bool newValue);
void countersChanged(const QMap<int, int> &newCounters);
void annotationChanged(const QString &newAnnotation);
void ptChanged(const QString &newPt);
void doesntUntapChanged(bool newValue);
void destroyOnZoneChangeChanged(bool newValue);
void attachedToChanged(CardItem *newAttachedTo);
void zoneChanged(CardState *changedCard, CardZoneLogic *newZone);
public:
explicit CardState(QObject *parent, CardZoneLogic *_zone) : QObject(parent), zone(_zone)
{
}
void resetState(bool keepAnnotations);
CardZoneLogic *getZone() const
{
return zone;
}
void setZone(CardZoneLogic *_zone);
bool getAttacking() const
{
return attacking;
}
void setAttacking(bool _attacking);
const QMap<int, int> &getCounters() const
{
return counters;
}
void insertCounter(int id, int value);
void setCounter(int id, int value);
void clearCounters();
QString getAnnotation() const
{
return annotation;
}
void setAnnotation(const QString &_annotation);
QString getPT() const
{
return pt;
}
void setPT(const QString &_pt);
bool getDoesntUntap() const
{
return doesntUntap;
}
void setDoesntUntap(bool _doesntUntap);
bool getDestroyOnZoneChange() const
{
return destroyOnZoneChange;
}
void setDestroyOnZoneChange(bool _destroyOnZoneChange);
CardItem *getAttachedTo() const
{
return attachedTo;
}
void setAttachedTo(CardItem *_attachedTo);
};
#endif // COCKATRICE_CARD_STATE_H

View file

@ -0,0 +1,24 @@
#include "counter_state.h"
#include <libcockatrice/utility/color.h>
CounterState::CounterState(int id, const QString &name, const QColor &color, int radius, int value, QObject *parent)
: QObject(parent), id(id), name(name), color(color), radius(radius), value(value)
{
}
CounterState *CounterState::fromProto(const ServerInfo_Counter &counter, QObject *parent)
{
return new CounterState(counter.id(), QString::fromStdString(counter.name()),
convertColorToQColor(counter.counter_color()), counter.radius(), counter.count(), parent);
}
void CounterState::setValue(int newValue)
{
if (newValue == value) {
return;
}
int old = value;
value = newValue;
emit valueChanged(old, newValue);
}

View file

@ -0,0 +1,51 @@
#ifndef COCKATRICE_COUNTER_STATE_H
#define COCKATRICE_COUNTER_STATE_H
#include <QColor>
#include <QObject>
#include <QString>
#include <libcockatrice/protocol/pb/serverinfo_counter.pb.h>
class CounterState : public QObject
{
Q_OBJECT
public:
CounterState(int id, const QString &name, const QColor &color, int radius, int value, QObject *parent = nullptr);
static CounterState *fromProto(const ServerInfo_Counter &counter, QObject *parent = nullptr);
int getId() const
{
return id;
}
QString getName() const
{
return name;
}
QColor getColor() const
{
return color;
}
int getRadius() const
{
return radius;
}
int getValue() const
{
return value;
}
void setValue(int newValue);
signals:
void valueChanged(int oldValue, int newValue);
private:
int id;
QString name;
QColor color;
int radius;
int value;
};
#endif // COCKATRICE_COUNTER_STATE_H

View file

@ -4,16 +4,16 @@
#include <libcockatrice/protocol/pb/event_game_joined.pb.h> #include <libcockatrice/protocol/pb/event_game_joined.pb.h>
Game::Game(TabGame *_tab, Game::Game(QObject *_parent,
bool isLocalGame,
QList<AbstractClient *> &_clients, QList<AbstractClient *> &_clients,
const Event_GameJoined &event, const Event_GameJoined &event,
const QMap<int, QString> &_roomGameTypes) const QMap<int, QString> &_roomGameTypes)
: AbstractGame(_tab) : AbstractGame(_parent)
{ {
gameMetaInfo->setFromProto(event.game_info()); gameMetaInfo->setFromProto(event.game_info());
gameMetaInfo->setRoomGameTypes(_roomGameTypes); gameMetaInfo->setRoomGameTypes(_roomGameTypes);
gameState = new GameState(this, 0, event.host_id(), tab->getTabSupervisor()->getIsLocalGame(), _clients, false, gameState = new GameState(this, 0, event.host_id(), isLocalGame, _clients, false, event.resuming(), -1, false);
event.resuming(), -1, false);
connect(gameMetaInfo, &GameMetaInfo::startedChanged, gameState, &GameState::onStartedChanged); connect(gameMetaInfo, &GameMetaInfo::startedChanged, gameState, &GameState::onStartedChanged);
playerManager = new PlayerManager(this, event.player_id(), event.judge(), event.spectator()); playerManager = new PlayerManager(this, event.player_id(), event.judge(), event.spectator());
gameMetaInfo->setStarted(false); gameMetaInfo->setStarted(false);

View file

@ -1,8 +1,8 @@
/** /**
* @file game.h * @file game.h
* @ingroup GameLogic * @ingroup GameLogic
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef COCKATRICE_GAME_H #ifndef COCKATRICE_GAME_H
#define COCKATRICE_GAME_H #define COCKATRICE_GAME_H
@ -16,7 +16,8 @@ class Game : public AbstractGame
Q_OBJECT Q_OBJECT
public: public:
Game(TabGame *tab, Game(QObject *parent,
bool isLocalGame,
QList<AbstractClient *> &_clients, QList<AbstractClient *> &_clients,
const Event_GameJoined &event, const Event_GameJoined &event,
const QMap<int, QString> &_roomGameTypes); const QMap<int, QString> &_roomGameTypes);

View file

@ -1,8 +1,8 @@
#include "game_event_handler.h" #include "game_event_handler.h"
#include "../game_graphics/log/message_log_widget.h"
#include "../interface/widgets/tabs/tab_game.h" #include "../interface/widgets/tabs/tab_game.h"
#include "abstract_game.h" #include "abstract_game.h"
#include "log/message_log_widget.h"
#include <libcockatrice/network/client/abstract/abstract_client.h> #include <libcockatrice/network/client/abstract/abstract_client.h>
#include <libcockatrice/protocol/get_pb_extension.h> #include <libcockatrice/protocol/get_pb_extension.h>
@ -99,7 +99,7 @@ void GameEventHandler::processGameEventContainer(const GameEventContainer &cont,
if (cont.has_forced_by_judge()) { if (cont.has_forced_by_judge()) {
auto id = cont.forced_by_judge(); auto id = cont.forced_by_judge();
Player *judgep = game->getPlayerManager()->getPlayers().value(id, nullptr); PlayerLogic *judgep = game->getPlayerManager()->getPlayers().value(id, nullptr);
if (judgep) { if (judgep) {
emit setContextJudgeName(judgep->getPlayerInfo()->getName()); emit setContextJudgeName(judgep->getPlayerInfo()->getName());
} else if (game->getPlayerManager()->getSpectators().contains(id)) { } else if (game->getPlayerManager()->getSpectators().contains(id)) {
@ -160,7 +160,7 @@ void GameEventHandler::processGameEventContainer(const GameEventContainer &cont,
break; break;
default: { default: {
Player *player = game->getPlayerManager()->getPlayers().value(playerId, 0); PlayerLogic *player = game->getPlayerManager()->getPlayers().value(playerId, 0);
if (!player) { if (!player) {
qCWarning(GameEventHandlerLog) << "unhandled game event: invalid player id"; qCWarning(GameEventHandlerLog) << "unhandled game event: invalid player id";
break; break;
@ -213,11 +213,25 @@ void GameEventHandler::handleChatMessageSent(const QString &chatMessage)
sendGameCommand(cmd); sendGameCommand(cmd);
} }
void GameEventHandler::handleArrowDeletion(int arrowId) void GameEventHandler::handleArrowDeletion(int creatorId, int arrowId)
{ {
Command_DeleteArrow cmd; Command_DeleteArrow cmd;
cmd.set_arrow_id(arrowId); cmd.set_arrow_id(arrowId);
sendGameCommand(cmd);
auto preparedCommand = prepareGameCommand(cmd);
connect(preparedCommand, &PendingCommand::finished, this, [creatorId, arrowId, this](const Response &response) {
handleArrowDeletionFinished(response, creatorId, arrowId);
});
sendGameCommand(preparedCommand);
}
void GameEventHandler::handleArrowDeletionFinished(const Response &response, int creatorId, int arrowId)
{
if (response.response_code() == Response::RespNameNotFound) {
emit arrowDeleted(creatorId, arrowId);
}
} }
void GameEventHandler::eventSpectatorSay(const Event_GameSay &event, void GameEventHandler::eventSpectatorSay(const Event_GameSay &event,
@ -261,7 +275,7 @@ void GameEventHandler::eventGameStateChanged(const Event_GameStateChanged &event
emit spectatorJoined(prop); emit spectatorJoined(prop);
} }
} else { } else {
Player *player = game->getPlayerManager()->getPlayers().value(playerId, 0); PlayerLogic *player = game->getPlayerManager()->getPlayers().value(playerId, 0);
if (!player) { if (!player) {
player = game->getPlayerManager()->addPlayer(playerId, prop.user_info()); player = game->getPlayerManager()->addPlayer(playerId, prop.user_info());
emit playerJoined(prop); emit playerJoined(prop);
@ -310,7 +324,7 @@ void GameEventHandler::processCardAttachmentsForPlayers(const Event_GameStateCha
const ServerInfo_Player &playerInfo = event.player_list(i); const ServerInfo_Player &playerInfo = event.player_list(i);
const ServerInfo_PlayerProperties &prop = playerInfo.properties(); const ServerInfo_PlayerProperties &prop = playerInfo.properties();
if (!prop.spectator()) { if (!prop.spectator()) {
Player *player = game->getPlayerManager()->getPlayers().value(prop.player_id(), 0); PlayerLogic *player = game->getPlayerManager()->getPlayers().value(prop.player_id(), 0);
if (!player) { if (!player) {
continue; continue;
} }
@ -323,7 +337,7 @@ void GameEventHandler::eventPlayerPropertiesChanged(const Event_PlayerProperties
int eventPlayerId, int eventPlayerId,
const GameEventContext &context) const GameEventContext &context)
{ {
Player *player = game->getPlayerManager()->getPlayers().value(eventPlayerId, 0); PlayerLogic *player = game->getPlayerManager()->getPlayers().value(eventPlayerId, 0);
if (!player) { if (!player) {
return; return;
} }
@ -347,7 +361,7 @@ void GameEventHandler::eventPlayerPropertiesChanged(const Event_PlayerProperties
case GameEventContext::CONCEDE: { case GameEventContext::CONCEDE: {
player->setConceded(true); player->setConceded(true);
QMapIterator<int, Player *> playerIterator(game->getPlayerManager()->getPlayers()); QMapIterator<int, PlayerLogic *> playerIterator(game->getPlayerManager()->getPlayers());
while (playerIterator.hasNext()) { while (playerIterator.hasNext()) {
playerIterator.next().value()->updateZones(); playerIterator.next().value()->updateZones();
} }
@ -359,7 +373,7 @@ void GameEventHandler::eventPlayerPropertiesChanged(const Event_PlayerProperties
case GameEventContext::UNCONCEDE: { case GameEventContext::UNCONCEDE: {
player->setConceded(false); player->setConceded(false);
QMapIterator<int, Player *> playerIterator(game->getPlayerManager()->getPlayers()); QMapIterator<int, PlayerLogic *> playerIterator(game->getPlayerManager()->getPlayers());
while (playerIterator.hasNext()) { while (playerIterator.hasNext()) {
playerIterator.next().value()->updateZones(); playerIterator.next().value()->updateZones();
} }
@ -409,7 +423,7 @@ void GameEventHandler::eventJoin(const Event_Join &event, int /*eventPlayerId*/,
emit logJoinSpectator(playerName); emit logJoinSpectator(playerName);
emit spectatorJoined(playerInfo); emit spectatorJoined(playerInfo);
} else { } else {
Player *newPlayer = game->getPlayerManager()->addPlayer(playerId, playerInfo.user_info()); PlayerLogic *newPlayer = game->getPlayerManager()->addPlayer(playerId, playerInfo.user_info());
emit logJoinPlayer(newPlayer); emit logJoinPlayer(newPlayer);
emit playerJoined(playerInfo); emit playerJoined(playerInfo);
} }
@ -437,7 +451,7 @@ QString GameEventHandler::getLeaveReason(Event_Leave::LeaveReason reason)
} }
void GameEventHandler::eventLeave(const Event_Leave &event, int eventPlayerId, const GameEventContext & /*context*/) void GameEventHandler::eventLeave(const Event_Leave &event, int eventPlayerId, const GameEventContext & /*context*/)
{ {
Player *player = game->getPlayerManager()->getPlayers().value(eventPlayerId, 0); PlayerLogic *player = game->getPlayerManager()->getPlayers().value(eventPlayerId, 0);
if (!player) { if (!player) {
return; return;
} }
@ -452,7 +466,7 @@ void GameEventHandler::eventLeave(const Event_Leave &event, int eventPlayerId, c
player->deleteLater(); player->deleteLater();
// Rearrange all remaining zones so that attachment relationship updates take place // Rearrange all remaining zones so that attachment relationship updates take place
QMapIterator<int, Player *> playerIterator(game->getPlayerManager()->getPlayers()); QMapIterator<int, PlayerLogic *> playerIterator(game->getPlayerManager()->getPlayers());
while (playerIterator.hasNext()) { while (playerIterator.hasNext()) {
playerIterator.next().value()->updateZones(); playerIterator.next().value()->updateZones();
} }
@ -474,7 +488,7 @@ void GameEventHandler::eventReverseTurn(const Event_ReverseTurn &event,
int eventPlayerId, int eventPlayerId,
const GameEventContext & /*context*/) const GameEventContext & /*context*/)
{ {
Player *player = game->getPlayerManager()->getPlayers().value(eventPlayerId, 0); PlayerLogic *player = game->getPlayerManager()->getPlayers().value(eventPlayerId, 0);
if (!player) { if (!player) {
return; return;
} }
@ -505,7 +519,7 @@ void GameEventHandler::eventSetActivePlayer(const Event_SetActivePlayer &event,
const GameEventContext & /*context*/) const GameEventContext & /*context*/)
{ {
game->getGameState()->setActivePlayer(event.active_player_id()); game->getGameState()->setActivePlayer(event.active_player_id());
Player *player = game->getPlayerManager()->getPlayer(event.active_player_id()); PlayerLogic *player = game->getPlayerManager()->getPlayer(event.active_player_id());
if (!player) { if (!player) {
return; return;
} }

View file

@ -1,8 +1,8 @@
/** /**
* @file game_event_handler.h * @file game_event_handler.h
* @ingroup GameLogic * @ingroup GameLogic
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef COCKATRICE_GAME_EVENT_HANDLER_H #ifndef COCKATRICE_GAME_EVENT_HANDLER_H
#define COCKATRICE_GAME_EVENT_HANDLER_H #define COCKATRICE_GAME_EVENT_HANDLER_H
@ -38,7 +38,7 @@ class Event_Kicked;
class Event_ReverseTurn; class Event_ReverseTurn;
class AbstractGame; class AbstractGame;
class PendingCommand; class PendingCommand;
class Player; class PlayerLogic;
inline Q_LOGGING_CATEGORY(GameEventHandlerLog, "game_event_handler"); inline Q_LOGGING_CATEGORY(GameEventHandlerLog, "game_event_handler");
@ -60,7 +60,8 @@ public:
void handleActivePhaseChanged(int phase); void handleActivePhaseChanged(int phase);
void handleGameLeft(); void handleGameLeft();
void handleChatMessageSent(const QString &chatMessage); void handleChatMessageSent(const QString &chatMessage);
void handleArrowDeletion(int arrowId); void handleArrowDeletion(int creatorId, int arrowId);
void handleArrowDeletionFinished(const Response &response, int creatorId, int arrowId);
void eventSpectatorSay(const Event_GameSay &event, int eventPlayerId, const GameEventContext &context); void eventSpectatorSay(const Event_GameSay &event, int eventPlayerId, const GameEventContext &context);
void eventSpectatorLeave(const Event_Leave &event, int eventPlayerId, const GameEventContext &context); void eventSpectatorLeave(const Event_Leave &event, int eventPlayerId, const GameEventContext &context);
@ -95,7 +96,7 @@ public slots:
signals: signals:
void emitUserEvent(); void emitUserEvent();
void addPlayerToAutoCompleteList(QString playerName); void addPlayerToAutoCompleteList(QString playerName);
void localPlayerDeckSelected(Player *localPlayer, int playerId, ServerInfo_Player playerInfo); void localPlayerDeckSelected(PlayerLogic *localPlayer, int playerId, ServerInfo_Player playerInfo);
void remotePlayerDeckSelected(QString deckList, int playerId, QString playerName); void remotePlayerDeckSelected(QString deckList, int playerId, QString playerName);
void remotePlayersDecksSelected(QVector<QPair<int, QPair<QString, QString>>> opponentDecks); void remotePlayersDecksSelected(QVector<QPair<int, QPair<QString, QString>>> opponentDecks);
void localPlayerSideboardLocked(int playerId, bool sideboardLocked); void localPlayerSideboardLocked(int playerId, bool sideboardLocked);
@ -112,21 +113,22 @@ signals:
void containerProcessingStarted(GameEventContext context); void containerProcessingStarted(GameEventContext context);
void setContextJudgeName(QString judgeName); void setContextJudgeName(QString judgeName);
void containerProcessingDone(); void containerProcessingDone();
void arrowDeleted(int creatorId, int arrowId);
void logSpectatorSay(ServerInfo_User userInfo, QString message); void logSpectatorSay(ServerInfo_User userInfo, QString message);
void logSpectatorLeave(QString name, QString reason); void logSpectatorLeave(QString name, QString reason);
void logGameStart(); void logGameStart();
void logReadyStart(Player *player); void logReadyStart(PlayerLogic *player);
void logNotReadyStart(Player *player); void logNotReadyStart(PlayerLogic *player);
void logDeckSelect(Player *player, QString deckHash, int sideboardSize); void logDeckSelect(PlayerLogic *player, QString deckHash, int sideboardSize);
void logSideboardLockSet(Player *player, bool sideboardLocked); void logSideboardLockSet(PlayerLogic *player, bool sideboardLocked);
void logConnectionStateChanged(Player *player, bool connected); void logConnectionStateChanged(PlayerLogic *player, bool connected);
void logJoinSpectator(QString spectatorName); void logJoinSpectator(QString spectatorName);
void logJoinPlayer(Player *player); void logJoinPlayer(PlayerLogic *player);
void logLeave(Player *player, QString reason); void logLeave(PlayerLogic *player, QString reason);
void logKicked(); void logKicked();
void logTurnReversed(Player *player, bool reversed); void logTurnReversed(PlayerLogic *player, bool reversed);
void logGameClosed(); void logGameClosed();
void logActivePlayer(Player *activePlayer); void logActivePlayer(PlayerLogic *activePlayer);
void logActivePhaseChanged(int activePhase); void logActivePhaseChanged(int activePhase);
void logConcede(int playerId); void logConcede(int playerId);
void logUnconcede(int playerId); void logUnconcede(int playerId);

View file

@ -1,8 +1,8 @@
/** /**
* @file game_meta_info.h * @file game_meta_info.h
* @ingroup GameLogic * @ingroup GameLogic
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef GAME_META_INFO_H #ifndef GAME_META_INFO_H
#define GAME_META_INFO_H #define GAME_META_INFO_H

View file

@ -1,8 +1,8 @@
/** /**
* @file game_state.h * @file game_state.h
* @ingroup GameLogic * @ingroup GameLogic
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef COCKATRICE_GAME_STATE_H #ifndef COCKATRICE_GAME_STATE_H
#define COCKATRICE_GAME_STATE_H #define COCKATRICE_GAME_STATE_H

View file

@ -1,109 +0,0 @@
/**
* @file message_log_widget.h
* @ingroup GameWidgets
* @brief TODO: Document this.
*/
#ifndef MESSAGELOGWIDGET_H
#define MESSAGELOGWIDGET_H
#include "../../interface/widgets/server/chat_view/chat_view.h"
#include "../zones/logic/card_zone_logic.h"
class AbstractGame;
class CardItem;
class GameEventContext;
class Player;
class PlayerEventHandler;
class MessageLogWidget : public ChatView
{
Q_OBJECT
private:
enum MessageContext
{
MessageContext_None,
MessageContext_MoveCard,
MessageContext_Mulligan
};
MessageContext currentContext;
QString messagePrefix, messageSuffix;
static QPair<QString, QString> getFromStr(CardZoneLogic *zone, QString cardName, int position, bool ownerChange);
public:
void connectToPlayerEventHandler(PlayerEventHandler *player);
MessageLogWidget(TabSupervisor *_tabSupervisor, AbstractGame *_game, QWidget *parent = nullptr);
public slots:
void containerProcessingDone();
void containerProcessingStarted(const GameEventContext &context);
void logAlwaysRevealTopCard(Player *player, CardZoneLogic *zone, bool reveal);
void logAlwaysLookAtTopCard(Player *player, CardZoneLogic *zone, bool reveal);
void logAttachCard(Player *player, QString cardName, Player *targetPlayer, QString targetCardName);
void logConcede(int playerId);
void logUnconcede(int playerId);
void logConnectionStateChanged(Player *player, bool connectionState);
void logCreateArrow(Player *player,
Player *startPlayer,
QString startCard,
Player *targetPlayer,
QString targetCard,
bool playerTarget);
void logCreateToken(Player *player, QString cardName, QString pt, bool faceDown);
void logDeckSelect(Player *player, QString deckHash, int sideboardSize);
void logDestroyCard(Player *player, QString cardName);
void logDrawCards(Player *player, int number, bool deckIsEmpty);
void logDumpZone(Player *player, CardZoneLogic *zone, int numberCards, bool isReversed = false);
void logFlipCard(Player *player, QString cardName, bool faceDown);
void logGameClosed();
void logGameStart();
void logGameFlooded();
void logJoin(Player *player);
void logJoinSpectator(QString name);
void logKicked();
void logLeave(Player *player, QString reason);
void logLeaveSpectator(QString name, QString reason);
void logNotReadyStart(Player *player);
void logMoveCard(Player *player,
CardItem *card,
CardZoneLogic *startZone,
int oldX,
CardZoneLogic *targetZone,
int newX);
void logMulligan(Player *player, int number);
void logReplayStarted(int gameId);
void logReadyStart(Player *player);
void logRevealCards(Player *player,
CardZoneLogic *zone,
int cardId,
QString cardName,
Player *otherPlayer,
bool faceDown,
int amount,
bool isLentToAnotherPlayer);
void logReverseTurn(Player *player, bool reversed);
void logRollDie(Player *player, int sides, const QList<uint> &rolls);
void logSay(Player *player, QString message);
void logSetActivePhase(int phase);
void logSetActivePlayer(Player *player);
void logSetAnnotation(Player *player, CardItem *card, QString newAnnotation);
void logSetCardCounter(Player *player, QString cardName, int counterId, int value, int oldValue);
void logSetCounter(Player *player, QString counterName, int value, int oldValue);
void logSetDoesntUntap(Player *player, CardItem *card, bool doesntUntap);
void logSetPT(Player *player, CardItem *card, QString newPT);
void logSetSideboardLock(Player *player, bool locked);
void logSetTapped(Player *player, CardItem *card, bool tapped);
void logShuffle(Player *player, CardZoneLogic *zone, int start, int end);
void logSpectatorSay(const ServerInfo_User &spectator, QString message);
void logUnattachCard(Player *player, QString cardName);
void logUndoDraw(Player *player, QString cardName);
void logUndoDrawFailed(Player *player);
void setContextJudgeName(QString player);
void appendHtmlServerMessage(const QString &html,
bool optionalIsBold = false,
QString optionalFontColor = QString()) override;
};
#endif

View file

@ -1,8 +1,8 @@
/** /**
* @file phase.h * @file phase.h
* @ingroup GameLogic * @ingroup GameLogic
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef PHASE_H #ifndef PHASE_H
#define PHASE_H #define PHASE_H

View file

@ -1,8 +1,8 @@
/** /**
* @file event_processing_options.h * @file event_processing_options.h
* @ingroup GameLogicPlayers * @ingroup GameLogicPlayers
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef COCKATRICE_EVENT_PROCESSING_OPTIONS_H #ifndef COCKATRICE_EVENT_PROCESSING_OPTIONS_H
#define COCKATRICE_EVENT_PROCESSING_OPTIONS_H #define COCKATRICE_EVENT_PROCESSING_OPTIONS_H

View file

@ -1,149 +0,0 @@
#include "player_menu.h"
#include "../../../interface/widgets/tabs/tab_game.h"
#include "../../board/card_item.h"
#include "../../zones/hand_zone.h"
#include "../../zones/pile_zone.h"
#include "../../zones/table_zone.h"
#include "card_menu.h"
#include "hand_menu.h"
#include <libcockatrice/protocol/pb/command_reveal_cards.pb.h>
PlayerMenu::PlayerMenu(Player *_player) : QObject(_player), player(_player)
{
playerMenu = new TearOffMenu();
if (player->getPlayerInfo()->getLocalOrJudge()) {
handMenu = addManagedMenu<HandMenu>(player, player->getPlayerActions(), playerMenu);
libraryMenu = addManagedMenu<LibraryMenu>(player, playerMenu);
} else {
handMenu = nullptr;
libraryMenu = nullptr;
}
graveMenu = addManagedMenu<GraveyardMenu>(player, playerMenu);
rfgMenu = addManagedMenu<RfgMenu>(player, playerMenu);
if (player->getPlayerInfo()->getLocalOrJudge()) {
sideboardMenu = addManagedMenu<SideboardMenu>(player, playerMenu);
customZonesMenu = addManagedMenu<CustomZoneMenu>(player);
playerMenu->addSeparator();
countersMenu = playerMenu->addMenu(QString());
utilityMenu = createManagedComponent<UtilityMenu>(player, playerMenu);
} else {
sideboardMenu = nullptr;
customZonesMenu = nullptr;
countersMenu = nullptr;
utilityMenu = nullptr;
}
if (player->getPlayerInfo()->getLocal()) {
sayMenu = addManagedMenu<SayMenu>(player);
} else {
sayMenu = nullptr;
}
connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this,
&PlayerMenu::refreshShortcuts);
refreshShortcuts();
retranslateUi();
}
void PlayerMenu::setMenusForGraphicItems()
{
player->getGraphicsItem()->getTableZoneGraphicsItem()->setMenu(playerMenu);
player->getGraphicsItem()->getGraveyardZoneGraphicsItem()->setMenu(graveMenu, graveMenu->aViewGraveyard);
player->getGraphicsItem()->getRfgZoneGraphicsItem()->setMenu(rfgMenu, rfgMenu->aViewRfg);
if (player->getPlayerInfo()->getLocalOrJudge()) {
player->getGraphicsItem()->getHandZoneGraphicsItem()->setMenu(handMenu);
player->getGraphicsItem()->getDeckZoneGraphicsItem()->setMenu(libraryMenu, libraryMenu->aDrawCard);
player->getGraphicsItem()->getSideboardZoneGraphicsItem()->setMenu(sideboardMenu);
}
}
QMenu *PlayerMenu::updateCardMenu(const CardItem *card)
{
if (!card) {
emit cardMenuUpdated(nullptr);
return nullptr;
}
// If is spectator (as spectators don't need card menus), return
// only update the menu if the card is actually selected
if ((player->getGame()->getPlayerManager()->isSpectator() && !player->getGame()->getPlayerManager()->isJudge()) ||
player->getGame()->getActiveCard() != card) {
return nullptr;
}
QMenu *menu = new CardMenu(player, card, shortcutsActive);
emit cardMenuUpdated(menu);
return menu;
}
void PlayerMenu::retranslateUi()
{
playerMenu->setTitle(tr("Player \"%1\"").arg(player->getPlayerInfo()->getName()));
for (auto *component : managedComponents) {
component->retranslateUi();
}
if (countersMenu) {
countersMenu->setTitle(tr("&Counters"));
}
QMapIterator<int, AbstractCounter *> counterIterator(player->getCounters());
while (counterIterator.hasNext()) {
counterIterator.next().value()->retranslateUi();
}
}
void PlayerMenu::refreshShortcuts()
{
if (shortcutsActive) {
// Judges get access to every player's menus but only want shortcuts to be set for their own.
if (player->getPlayerInfo()->getLocalOrJudge() && !player->getPlayerInfo()->getLocal()) {
setShortcutsInactive();
} else {
setShortcutsActive();
}
} else {
setShortcutsInactive();
}
}
void PlayerMenu::setShortcutsActive()
{
shortcutsActive = true;
for (auto *component : managedComponents) {
component->setShortcutsActive();
}
// Counters implement AbstractPlayerComponent but are iterated via Player::counters
// (the authoritative source) rather than managedComponents to avoid a redundant
// list that must stay in sync with the map.
QMapIterator<int, AbstractCounter *> counterIterator(player->getCounters());
while (counterIterator.hasNext()) {
counterIterator.next().value()->setShortcutsActive();
}
}
void PlayerMenu::setShortcutsInactive()
{
shortcutsActive = false;
for (auto *component : managedComponents) {
component->setShortcutsInactive();
}
QMapIterator<int, AbstractCounter *> counterIterator(player->getCounters());
while (counterIterator.hasNext()) {
counterIterator.next().value()->setShortcutsInactive();
}
}

File diff suppressed because it is too large Load diff

View file

@ -2,15 +2,18 @@
* @file player_actions.h * @file player_actions.h
* @ingroup GameLogicActions * @ingroup GameLogicActions
* @ingroup GameLogicPlayers * @ingroup GameLogicPlayers
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef COCKATRICE_PLAYER_ACTIONS_H #ifndef COCKATRICE_PLAYER_ACTIONS_H
#define COCKATRICE_PLAYER_ACTIONS_H #define COCKATRICE_PLAYER_ACTIONS_H
#include "../dialogs/dlg_create_token.h"
#include "../dialogs/dlg_move_top_cards_until.h" #include "../../game_graphics/board/card_item.h"
#include "../../game_graphics/dialogs/dlg_create_token.h"
#include "../../game_graphics/dialogs/dlg_move_top_cards_until.h"
#include "../../game_graphics/player/card_menu_action_type.h"
#include "event_processing_options.h" #include "event_processing_options.h"
#include "player.h" #include "player_logic.h"
#include <QMenu> #include <QMenu>
#include <QObject> #include <QObject>
@ -25,11 +28,10 @@ class Message;
} }
} // namespace google } // namespace google
class CardItem;
class Command_MoveCard; class Command_MoveCard;
class GameEventContext; class GameEventContext;
class PendingCommand; class PendingCommand;
class Player; class PlayerLogic;
class PlayerActions : public QObject class PlayerActions : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -40,7 +42,7 @@ public:
RANDOM_CARD_FROM_ZONE = -2 RANDOM_CARD_FROM_ZONE = -2
}; };
explicit PlayerActions(Player *player); explicit PlayerActions(PlayerLogic *player);
void sendGameCommand(PendingCommand *pend); void sendGameCommand(PendingCommand *pend);
void sendGameCommand(const google::protobuf::Message &command); void sendGameCommand(const google::protobuf::Message &command);
@ -56,30 +58,75 @@ public:
return movingCardsUntil; return movingCardsUntil;
} }
signals:
void requestViewTopCardsDialog(int defaultNumberTopCards, int deckSize);
void requestViewBottomCardsDialog(int defaultNumberBottomCards, int deckSize);
void requestShuffleTopDialog(int defaultNumberTopCards, int maxCards);
void requestShuffleBottomDialog(int defaultNumberBottomCards, int maxCards);
void requestMulliganDialog(int startSize, int handSize, int deckSize);
void requestDrawCardsDialog(int defaultNumberTopCards, int deckSize);
void requestMoveTopCardsToDialog(int defaultNumberTopCards,
int maxCards,
const QString &targetZone,
const QString &zoneDisplayName,
bool faceDown);
void requestMoveTopCardsUntilDialog(MoveTopCardsUntilOptions options);
void requestMoveBottomCardsToDialog(int defaultNumberBottomCards,
int maxCards,
const QString &targetZone,
const QString &zoneDisplayName,
bool faceDown);
void requestDrawBottomCardsDialog(int defaultNumberBottomCards, int maxCards);
void requestRollDieDialog();
void requestCreateTokenDialog(const QStringList &predefinedTokens);
void requestCreateRelatedFromRelationDialog(const CardItem *sourceCard, const CardRelation *cardRelation);
void requestMoveCardXCardsFromTopDialog(int defaultNumberTopCardsToPlaceBelow, int deckSize);
void requestSetPTDialog(const QString &oldPT);
void requestSetAnnotationDialog(const QString &oldAnnotation);
void requestSetCardCounterDialog(int counterId, const QString &oldValueForDlg);
void requestZoneViewToggle(const QString &zoneName, int numberCards, bool isReversed = false);
void requestSortHand(const QList<CardList::SortOption> &options);
void requestEnableAndSetCreateAnotherTokenAction(const QString &lastTokenName);
void requestSetLastToken(CardInfoPtr lastToken);
public slots: public slots:
void setLastToken(CardInfoPtr cardInfo); void setLastToken(CardInfoPtr cardInfo);
void setLastTokenInfo(CardInfoPtr cardInfo);
void playCard(CardItem *c, bool faceDown); void playCard(CardItem *c, bool faceDown);
void playCardToTable(const CardItem *c, bool faceDown); void playCardToTable(const CardItem *c, bool faceDown);
void actUntapAll(); void actUntapAll();
void actRollDie(); void actRequestRollDieDialog();
void actRollDie(int sides, int count);
void actFlipCoin(); void actFlipCoin();
void actCreateToken(); void actRequestCreateTokenDialog(const QStringList &predefinedTokens);
void actCreateToken(TokenInfo tokenToCreate);
void actCreateAnotherToken(); void actCreateAnotherToken();
void actRequestCreateRelatedFromRelationDialog(const CardItem *sourceCard, const CardRelation *cardRelation);
bool createRelatedFromRelation(const CardItem *sourceCard, const CardRelation *cardRelation, int variableCount);
void onRelatedCardCreated(const CardItem *sourceCard, const CardRelation *cardRelation);
void setLastRelatedCreationSucceeded(bool succeeded)
{
lastRelatedCreationSucceeded = succeeded;
}
void actShuffle(); void actShuffle();
void actShuffleTop(); void actRequestShuffleTopDialog();
void actShuffleBottom(); void actShuffleTop(int number);
void actRequestShuffleBottomDialog();
void actShuffleBottom(int number);
void actDrawCard(); void actDrawCard();
void actDrawCards(); void actRequestDrawCardsDialog();
void actDrawCards(int number);
void actUndoDraw(); void actUndoDraw();
void actMulligan(); void actRequestMulliganDialog();
void actMulligan(int number);
void actMulliganSameSize(); void actMulliganSameSize();
void actMulliganMinusOne(); void actMulliganMinusOne();
void doMulligan(int number); void doMulligan(int number);
void actPlay(); void actPlay(QList<CardItem *> selectedCards);
void actPlayFacedown(); void actPlayFacedown(QList<CardItem *> selectedCards);
void actHide(); void actHide(QList<CardItem *> selectedCards);
void actMoveTopCardToPlay(); void actMoveTopCardToPlay();
void actMoveTopCardToPlayFaceDown(); void actMoveTopCardToPlayFaceDown();
@ -89,10 +136,14 @@ public slots:
void actMoveTopCardsToGraveFaceDown(); void actMoveTopCardsToGraveFaceDown();
void actMoveTopCardsToExile(); void actMoveTopCardsToExile();
void actMoveTopCardsToExileFaceDown(); void actMoveTopCardsToExileFaceDown();
void actMoveTopCardsUntil(); void actRequestMoveTopCardsUntilDialog();
void moveTopCardsUntil(const QString &expr, MoveTopCardsUntilOptions options);
void actMoveTopCardToBottom(); void actMoveTopCardToBottom();
void actRequestMoveTopCardsToDialog(const QString &targetZone, const QString &zoneDisplayName, bool faceDown);
void moveTopCardsTo(int number, const QString &targetZone, bool faceDown);
void actDrawBottomCard(); void actDrawBottomCard();
void actDrawBottomCards(); void actRequestDrawBottomCardsDialog();
void actDrawBottomCards(int number);
void actMoveBottomCardToPlay(); void actMoveBottomCardToPlay();
void actMoveBottomCardToPlayFaceDown(); void actMoveBottomCardToPlayFaceDown();
void actMoveBottomCardToGrave(); void actMoveBottomCardToGrave();
@ -102,6 +153,8 @@ public slots:
void actMoveBottomCardsToExile(); void actMoveBottomCardsToExile();
void actMoveBottomCardsToExileFaceDown(); void actMoveBottomCardsToExileFaceDown();
void actMoveBottomCardToTop(); void actMoveBottomCardToTop();
void actRequestMoveBottomCardsToDialog(const QString &targetZone, const QString &zoneDisplayName, bool faceDown);
void moveBottomCardsTo(int number, const QString &targetZone, bool faceDown);
void actSelectAll(); void actSelectAll();
void actSelectRow(); void actSelectRow();
@ -109,10 +162,12 @@ public slots:
void actViewLibrary(); void actViewLibrary();
void actViewHand(); void actViewHand();
void actViewTopCards(); void actRequestViewTopCardsDialog();
void actViewBottomCards(); void actViewTopCards(int number);
void actAlwaysRevealTopCard(); void actRequestViewBottomCardsDialog();
void actAlwaysLookAtTopCard(); void actViewBottomCards(int number);
void actAlwaysRevealTopCard(bool alwaysRevealTopCard);
void actAlwaysLookAtTopCard(bool alwaysRevealTopCard);
void actViewGraveyard(); void actViewGraveyard();
void actLendLibrary(int lendToPlayerId); void actLendLibrary(int lendToPlayerId);
void actRevealTopCards(int revealToPlayerId, int amount); void actRevealTopCards(int revealToPlayerId, int amount);
@ -127,39 +182,44 @@ public slots:
void actCreateRelatedCard(); void actCreateRelatedCard();
void actCreateAllRelatedCards(); void actCreateAllRelatedCards();
void actMoveCardXCardsFromTop(); void actRequestMoveCardXCardsFromTopDialog();
void actRemoveCardCounter(int counterId); void actMoveCardXCardsFromTop(QList<CardItem *> selectedCards, int number);
void actAddCardCounter(int counterId); void actRemoveCardCounter(QList<CardItem *> selectedCards, int counterId);
void actSetCardCounter(int counterId); void actAddCardCounter(QList<CardItem *> selectedCards, int counterId);
void actRequestSetCardCounterDialog(QList<CardItem *> selectedCards, int counterId);
void actSetCardCounter(QList<CardItem *> selectedCards, int counterId, const QString &counterValue);
void actIncrementAllCardCounters(QList<CardItem *> cardsToUpdate);
void actAttach(); void actAttach();
void actUnattach(); void actUnattach(QList<CardItem *> selectedCards);
void actDrawArrow(); void actDrawArrow();
void actIncPT(int deltaP, int deltaT); void actIncPT(QList<CardItem *> selectedCards, int deltaP, int deltaT);
void actResetPT(); void actResetPT(QList<CardItem *> selectedCards);
void actSetPT(); void actRequestSetPTDialog(QList<CardItem *> selectedCards);
void actIncP(); void actSetPT(QList<CardItem *> selectedCards, const QString &pt);
void actDecP(); void actIncP(QList<CardItem *> selectedCards);
void actIncT(); void actDecP(QList<CardItem *> selectedCards);
void actDecT(); void actIncT(QList<CardItem *> selectedCards);
void actIncPT(); void actDecT(QList<CardItem *> selectedCards);
void actDecPT(); void actIncPT(QList<CardItem *> selectedCards);
void actFlowP(); void actDecPT(QList<CardItem *> selectedCards);
void actFlowT(); void actFlowP(QList<CardItem *> selectedCards);
void actFlowT(QList<CardItem *> selectedCards);
void actReduceLifeByPower(); void actReduceLifeByPower(QList<CardItem *> selectedCards);
void actSetAnnotation(); void actRequestSetAnnotationDialog(QList<CardItem *> selectedCards);
void actReveal(QAction *action); void actSetAnnotation(QList<CardItem *> selectedCards, const QString &annotation);
void actReveal(QList<CardItem *> selectedCards, QAction *action);
void actRevealHand(int revealToPlayerId); void actRevealHand(int revealToPlayerId);
void actRevealRandomHandCard(int revealToPlayerId); void actRevealRandomHandCard(int revealToPlayerId);
void actRevealLibrary(int revealToPlayerId); void actRevealLibrary(int revealToPlayerId);
void actSortHand(); void actSortHand();
void cardMenuAction(); void cardMenuAction(QList<CardItem *> selectedCards, CardMenuActionType type);
private: private:
Player *player; PlayerLogic *player;
int defaultNumberTopCards = 1; int defaultNumberTopCards = 1;
int defaultNumberTopCardsToPlaceBelow = 1; int defaultNumberTopCardsToPlaceBelow = 1;
@ -175,21 +235,20 @@ private:
int movingCardsUntilCounter = 0; int movingCardsUntilCounter = 0;
MoveTopCardsUntilOptions movingCardsUntilOptions; MoveTopCardsUntilOptions movingCardsUntilOptions;
void moveTopCardsTo(const QString &targetZone, const QString &zoneDisplayName, bool faceDown); bool lastRelatedCreationSucceeded = false;
void moveBottomCardsTo(const QString &targetZone, const QString &zoneDisplayName, bool faceDown);
void createCard(const CardItem *sourceCard, void createCard(const CardItem *sourceCard,
const QString &dbCardName, const QString &dbCardName,
CardRelationType attach = CardRelationType::DoesNotAttach, CardRelationType attach = CardRelationType::DoesNotAttach,
bool persistent = false); bool persistent = false,
bool createRelatedFromRelation(const CardItem *sourceCard, const CardRelation *cardRelation); bool faceDown = false);
void playSelectedCards(bool faceDown = false); void playSelectedCards(QList<CardItem *> selectedCards, bool faceDown = false);
void cmdSetTopCard(Command_MoveCard &cmd); void cmdSetTopCard(Command_MoveCard &cmd);
void cmdSetBottomCard(Command_MoveCard &cmd); void cmdSetBottomCard(Command_MoveCard &cmd);
void offsetCardCounter(int counterId, int offset); void offsetCardCounter(QList<CardItem *> selectedCards, int counterId, int offset);
}; };
#endif // COCKATRICE_PLAYER_ACTIONS_H #endif // COCKATRICE_PLAYER_ACTIONS_H

View file

@ -1,12 +1,13 @@
#include "player_event_handler.h" #include "player_event_handler.h"
#include "../../game_graphics/board/arrow_item.h"
#include "../../game_graphics/board/card_item.h"
#include "../../game_graphics/zones/view_zone.h"
#include "../../interface/widgets/tabs/tab_game.h" #include "../../interface/widgets/tabs/tab_game.h"
#include "../board/arrow_item.h" #include "../board/arrow_data.h"
#include "../board/card_item.h"
#include "../board/card_list.h" #include "../board/card_list.h"
#include "../zones/view_zone.h"
#include "player.h"
#include "player_actions.h" #include "player_actions.h"
#include "player_logic.h"
#include <libcockatrice/protocol/pb/command_set_card_attr.pb.h> #include <libcockatrice/protocol/pb/command_set_card_attr.pb.h>
#include <libcockatrice/protocol/pb/context_move_card.pb.h> #include <libcockatrice/protocol/pb/context_move_card.pb.h>
@ -31,10 +32,12 @@
#include <libcockatrice/protocol/pb/event_set_card_counter.pb.h> #include <libcockatrice/protocol/pb/event_set_card_counter.pb.h>
#include <libcockatrice/protocol/pb/event_set_counter.pb.h> #include <libcockatrice/protocol/pb/event_set_counter.pb.h>
#include <libcockatrice/protocol/pb/event_shuffle.pb.h> #include <libcockatrice/protocol/pb/event_shuffle.pb.h>
#include <libcockatrice/utility/color.h>
#include <libcockatrice/utility/zone_names.h> #include <libcockatrice/utility/zone_names.h>
PlayerEventHandler::PlayerEventHandler(Player *_player) : QObject(_player), player(_player) PlayerEventHandler::PlayerEventHandler(PlayerLogic *_player) : QObject(_player), player(_player)
{ {
connect(this, &PlayerEventHandler::requestCardMenuUpdate, player, &PlayerLogic::requestCardMenuUpdate);
} }
void PlayerEventHandler::eventGameSay(const Event_GameSay &event) void PlayerEventHandler::eventGameSay(const Event_GameSay &event)
@ -90,25 +93,40 @@ void PlayerEventHandler::eventRollDie(const Event_RollDie &event)
void PlayerEventHandler::eventCreateArrow(const Event_CreateArrow &event) void PlayerEventHandler::eventCreateArrow(const Event_CreateArrow &event)
{ {
ArrowItem *arrow = player->addArrow(event.arrow_info()); auto data = QSharedPointer<ArrowData>::create(ArrowData::fromProto(
if (!arrow) { event.arrow_info(), player->getPlayerInfo()->getId(), player->getPlayerInfo()->getLocal()));
return;
const auto &playerList = player->getGame()->getPlayerManager()->getPlayers();
PlayerLogic *startPlayer = playerList.value(data->startPlayerId);
PlayerLogic *targetPlayer = playerList.value(data->targetPlayerId);
QString startCardName, targetCardName;
if (startPlayer) {
if (auto *zone = startPlayer->getZones().value(data->startZone)) {
if (auto *card = zone->getCard(data->startCardId)) {
startCardName = card->getName();
}
}
}
if (!data->isPlayerTargeted() && targetPlayer) {
if (auto *zone = targetPlayer->getZones().value(data->targetZone)) {
if (auto *card = zone->getCard(data->targetCardId)) {
targetCardName = card->getName();
}
}
} }
auto *startCard = static_cast<CardItem *>(arrow->getStartItem()); emit player->arrowCreateRequested(data);
auto *targetCard = qgraphicsitem_cast<CardItem *>(arrow->getTargetItem());
if (targetCard) { if (startPlayer && targetPlayer && !startCardName.isEmpty() &&
emit logCreateArrow(player, startCard->getOwner(), startCard->getName(), targetCard->getOwner(), (data->isPlayerTargeted() || !targetCardName.isEmpty())) {
targetCard->getName(), false); emit logCreateArrow(player, startPlayer, startCardName, targetPlayer, targetCardName, data->isPlayerTargeted());
} else {
emit logCreateArrow(player, startCard->getOwner(), startCard->getName(), arrow->getTargetItem()->getOwner(),
QString(), true);
} }
} }
void PlayerEventHandler::eventDeleteArrow(const Event_DeleteArrow &event) void PlayerEventHandler::eventDeleteArrow(const Event_DeleteArrow &event)
{ {
player->delArrow(event.arrow_id()); emit player->arrowDeleted(player->getPlayerInfo()->getId(), event.arrow_id());
} }
void PlayerEventHandler::eventCreateToken(const Event_CreateToken &event) void PlayerEventHandler::eventCreateToken(const Event_CreateToken &event)
@ -235,7 +253,7 @@ void PlayerEventHandler::eventSetCardCounter(const Event_SetCardCounter &event)
int oldValue = card->getCounters().value(event.counter_id(), 0); int oldValue = card->getCounters().value(event.counter_id(), 0);
card->setCounter(event.counter_id(), event.counter_value()); card->setCounter(event.counter_id(), event.counter_value());
player->getPlayerMenu()->updateCardMenu(card); emit requestCardMenuUpdate(card);
emit logSetCardCounter(player, card->getName(), event.counter_id(), event.counter_value(), oldValue); emit logSetCardCounter(player, card->getName(), event.counter_id(), event.counter_value(), oldValue);
} }
@ -246,7 +264,7 @@ void PlayerEventHandler::eventCreateCounter(const Event_CreateCounter &event)
void PlayerEventHandler::eventSetCounter(const Event_SetCounter &event) void PlayerEventHandler::eventSetCounter(const Event_SetCounter &event)
{ {
AbstractCounter *ctr = player->getCounters().value(event.counter_id(), 0); CounterState *ctr = player->getCounters().value(event.counter_id(), nullptr);
if (!ctr) { if (!ctr) {
return; return;
} }
@ -262,7 +280,7 @@ void PlayerEventHandler::eventDelCounter(const Event_DelCounter &event)
void PlayerEventHandler::eventDumpZone(const Event_DumpZone &event) void PlayerEventHandler::eventDumpZone(const Event_DumpZone &event)
{ {
Player *zoneOwner = player->getGame()->getPlayerManager()->getPlayers().value(event.zone_owner_id(), 0); PlayerLogic *zoneOwner = player->getGame()->getPlayerManager()->getPlayers().value(event.zone_owner_id(), 0);
if (!zoneOwner) { if (!zoneOwner) {
return; return;
} }
@ -275,13 +293,13 @@ void PlayerEventHandler::eventDumpZone(const Event_DumpZone &event)
void PlayerEventHandler::eventMoveCard(const Event_MoveCard &event, const GameEventContext &context) void PlayerEventHandler::eventMoveCard(const Event_MoveCard &event, const GameEventContext &context)
{ {
Player *startPlayer = player->getGame()->getPlayerManager()->getPlayers().value(event.start_player_id()); PlayerLogic *startPlayer = player->getGame()->getPlayerManager()->getPlayers().value(event.start_player_id());
if (!startPlayer) { if (!startPlayer) {
return; return;
} }
QString startZoneString = QString::fromStdString(event.start_zone()); QString startZoneString = QString::fromStdString(event.start_zone());
CardZoneLogic *startZone = startPlayer->getZones().value(startZoneString, 0); CardZoneLogic *startZone = startPlayer->getZones().value(startZoneString, 0);
Player *targetPlayer = player->getGame()->getPlayerManager()->getPlayers().value(event.target_player_id()); PlayerLogic *targetPlayer = player->getGame()->getPlayerManager()->getPlayers().value(event.target_player_id());
if (!targetPlayer) { if (!targetPlayer) {
return; return;
} }
@ -352,29 +370,8 @@ void PlayerEventHandler::eventMoveCard(const Event_MoveCard &event, const GameEv
targetZone->addCard(card, true, x, y); targetZone->addCard(card, true, x, y);
// Look at all arrows from and to the card. emit cardZoneChanged(card, startZone == targetZone);
// If the card was moved to another zone, delete the arrows, otherwise update them. emit requestCardMenuUpdate(card);
QMapIterator<int, Player *> playerIterator(player->getGame()->getPlayerManager()->getPlayers());
while (playerIterator.hasNext()) {
Player *p = playerIterator.next().value();
QList<ArrowItem *> arrowsToDelete;
QMapIterator<int, ArrowItem *> arrowIterator(p->getArrows());
while (arrowIterator.hasNext()) {
ArrowItem *arrow = arrowIterator.next().value();
if ((arrow->getStartItem() == card) || (arrow->getTargetItem() == card)) {
if (startZone == targetZone) {
arrow->updatePath();
} else {
arrowsToDelete.append(arrow);
}
}
}
for (auto &i : arrowsToDelete) {
i->delArrow();
}
}
player->getPlayerMenu()->updateCardMenu(card);
if (player->getPlayerActions()->isMovingCardsUntil() && startZoneString == ZoneNames::DECK && if (player->getPlayerActions()->isMovingCardsUntil() && startZoneString == ZoneNames::DECK &&
targetZone->getName() == ZoneNames::STACK) { targetZone->getName() == ZoneNames::STACK) {
@ -401,7 +398,7 @@ void PlayerEventHandler::eventFlipCard(const Event_FlipCard &event)
emit logFlipCard(player, card->getName(), event.face_down()); emit logFlipCard(player, card->getName(), event.face_down());
card->setFaceDown(event.face_down()); card->setFaceDown(event.face_down());
player->getPlayerMenu()->updateCardMenu(card); emit requestCardMenuUpdate(card);
} }
void PlayerEventHandler::eventDestroyCard(const Event_DestroyCard &event) void PlayerEventHandler::eventDestroyCard(const Event_DestroyCard &event)
@ -429,8 +426,8 @@ void PlayerEventHandler::eventDestroyCard(const Event_DestroyCard &event)
void PlayerEventHandler::eventAttachCard(const Event_AttachCard &event) void PlayerEventHandler::eventAttachCard(const Event_AttachCard &event)
{ {
const QMap<int, Player *> &playerList = player->getGame()->getPlayerManager()->getPlayers(); const QMap<int, PlayerLogic *> &playerList = player->getGame()->getPlayerManager()->getPlayers();
Player *targetPlayer = nullptr; PlayerLogic *targetPlayer = nullptr;
CardZoneLogic *targetZone = nullptr; CardZoneLogic *targetZone = nullptr;
CardItem *targetCard = nullptr; CardItem *targetCard = nullptr;
if (event.has_target_player_id()) { if (event.has_target_player_id()) {
@ -470,7 +467,7 @@ void PlayerEventHandler::eventAttachCard(const Event_AttachCard &event)
} else { } else {
emit logUnattachCard(player, startCard->getName()); emit logUnattachCard(player, startCard->getName());
} }
player->getPlayerMenu()->updateCardMenu(startCard); emit requestCardMenuUpdate(startCard);
} }
void PlayerEventHandler::eventDrawCards(const Event_DrawCards &event) void PlayerEventHandler::eventDrawCards(const Event_DrawCards &event)
@ -507,7 +504,7 @@ void PlayerEventHandler::eventRevealCards(const Event_RevealCards &event, EventP
if (!zone) { if (!zone) {
return; return;
} }
Player *otherPlayer = nullptr; PlayerLogic *otherPlayer = nullptr;
if (event.has_other_player_id()) { if (event.has_other_player_id()) {
otherPlayer = player->getGame()->getPlayerManager()->getPlayers().value(event.other_player_id()); otherPlayer = player->getGame()->getPlayerManager()->getPlayers().value(event.other_player_id());
if (!otherPlayer) { if (!otherPlayer) {
@ -556,7 +553,7 @@ void PlayerEventHandler::eventRevealCards(const Event_RevealCards &event, EventP
} }
if (!options.testFlag(SKIP_REVEAL_WINDOW) && showZoneView && !cardList.isEmpty()) { if (!options.testFlag(SKIP_REVEAL_WINDOW) && showZoneView && !cardList.isEmpty()) {
player->getGameScene()->addRevealedZoneView(player, zone, cardList, event.grant_write_access()); emit player->requestRevealedZoneView(player, zone, cardList, event.grant_write_access());
} }
emit logRevealCards(player, zone, cardId, cardName, otherPlayer, false, emit logRevealCards(player, zone, cardId, cardName, otherPlayer, false,

View file

@ -1,8 +1,8 @@
/** /**
* @file player_event_handler.h * @file player_event_handler.h
* @ingroup GameLogicPlayers * @ingroup GameLogicPlayers
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef COCKATRICE_PLAYER_EVENT_HANDLER_H #ifndef COCKATRICE_PLAYER_EVENT_HANDLER_H
#define COCKATRICE_PLAYER_EVENT_HANDLER_H #define COCKATRICE_PLAYER_EVENT_HANDLER_H
@ -15,7 +15,7 @@
class CardItem; class CardItem;
class CardZoneLogic; class CardZoneLogic;
class Player; class PlayerLogic;
class Event_AttachCard; class Event_AttachCard;
class Event_ChangeZoneProperties; class Event_ChangeZoneProperties;
class Event_CreateArrow; class Event_CreateArrow;
@ -42,49 +42,51 @@ class PlayerEventHandler : public QObject
Q_OBJECT Q_OBJECT
signals: signals:
void logSay(Player *player, QString message); void logSay(PlayerLogic *player, QString message);
void logShuffle(Player *player, CardZoneLogic *zone, int start, int end); void logShuffle(PlayerLogic *player, CardZoneLogic *zone, int start, int end);
void logRollDie(Player *player, int sides, const QList<uint> &rolls); void logRollDie(PlayerLogic *player, int sides, const QList<uint> &rolls);
void logCreateArrow(Player *player, void logCreateArrow(PlayerLogic *player,
Player *startPlayer, PlayerLogic *startPlayer,
QString startCard, QString startCard,
Player *targetPlayer, PlayerLogic *targetPlayer,
QString targetCard, QString targetCard,
bool _playerTarget); bool _playerTarget);
void logCreateToken(Player *player, QString cardName, QString pt, bool faceDown); void logCreateToken(PlayerLogic *player, QString cardName, QString pt, bool faceDown);
void logDrawCards(Player *player, int number, bool deckIsEmpty); void logDrawCards(PlayerLogic *player, int number, bool deckIsEmpty);
void logUndoDraw(Player *player, QString cardName); void logUndoDraw(PlayerLogic *player, QString cardName);
void logUndoDrawFailed(Player *player); void logUndoDrawFailed(PlayerLogic *player);
void logMoveCard(Player *player, void logMoveCard(PlayerLogic *player,
CardItem *card, CardItem *card,
CardZoneLogic *startZone, CardZoneLogic *startZone,
int oldX, int oldX,
CardZoneLogic *targetZone, CardZoneLogic *targetZone,
int newX); int newX);
void logFlipCard(Player *player, QString cardName, bool faceDown); void logFlipCard(PlayerLogic *player, QString cardName, bool faceDown);
void logDestroyCard(Player *player, QString cardName); void logDestroyCard(PlayerLogic *player, QString cardName);
void logAttachCard(Player *player, QString cardName, Player *targetPlayer, QString targetCardName); void logAttachCard(PlayerLogic *player, QString cardName, PlayerLogic *targetPlayer, QString targetCardName);
void logUnattachCard(Player *player, QString cardName); void logUnattachCard(PlayerLogic *player, QString cardName);
void logSetCardCounter(Player *player, QString cardName, int counterId, int value, int oldValue); void logSetCardCounter(PlayerLogic *player, QString cardName, int counterId, int value, int oldValue);
void logSetTapped(Player *player, CardItem *card, bool tapped); void logSetTapped(PlayerLogic *player, CardItem *card, bool tapped);
void logSetCounter(Player *player, QString counterName, int value, int oldValue); void logSetCounter(PlayerLogic *player, QString counterName, int value, int oldValue);
void logSetDoesntUntap(Player *player, CardItem *card, bool doesntUntap); void logSetDoesntUntap(PlayerLogic *player, CardItem *card, bool doesntUntap);
void logSetPT(Player *player, CardItem *card, QString newPT); void logSetPT(PlayerLogic *player, CardItem *card, QString newPT);
void logSetAnnotation(Player *player, CardItem *card, QString newAnnotation); void logSetAnnotation(PlayerLogic *player, CardItem *card, QString newAnnotation);
void logDumpZone(Player *player, CardZoneLogic *zone, int numberCards, bool isReversed = false); void logDumpZone(PlayerLogic *player, CardZoneLogic *zone, int numberCards, bool isReversed = false);
void logRevealCards(Player *player, void logRevealCards(PlayerLogic *player,
CardZoneLogic *zone, CardZoneLogic *zone,
int cardId, int cardId,
QString cardName, QString cardName,
Player *otherPlayer, PlayerLogic *otherPlayer,
bool faceDown, bool faceDown,
int amount, int amount,
bool isLentToAnotherPlayer = false); bool isLentToAnotherPlayer = false);
void logAlwaysRevealTopCard(Player *player, CardZoneLogic *zone, bool reveal); void logAlwaysRevealTopCard(PlayerLogic *player, CardZoneLogic *zone, bool reveal);
void logAlwaysLookAtTopCard(Player *player, CardZoneLogic *zone, bool reveal); void logAlwaysLookAtTopCard(PlayerLogic *player, CardZoneLogic *zone, bool reveal);
void cardZoneChanged(CardItem *card, bool sameZone);
void requestCardMenuUpdate(const CardItem *card);
public: public:
PlayerEventHandler(Player *player); PlayerEventHandler(PlayerLogic *player);
void processGameEvent(GameEvent::GameEventType type, void processGameEvent(GameEvent::GameEventType type,
const GameEvent &event, const GameEvent &event,
@ -114,7 +116,7 @@ public:
void eventGameLogNotice(const Event_GameLogNotice &event); void eventGameLogNotice(const Event_GameLogNotice &event);
private: private:
Player *player; PlayerLogic *player;
void setCardAttrHelper(const GameEventContext &context, void setCardAttrHelper(const GameEventContext &context,
CardItem *card, CardItem *card,

View file

@ -1,7 +1,7 @@
#include "player_info.h" #include "player_info.h"
PlayerInfo::PlayerInfo(const ServerInfo_User &info, int _id, bool _local, bool _judge) PlayerInfo::PlayerInfo(const ServerInfo_User &info, int _id, bool _local, bool _judge)
: id(_id), local(_local), judge(_judge), handVisible(false) : id(_id), local(_local), judge(_judge)
{ {
userInfo = new ServerInfo_User; userInfo = new ServerInfo_User;
userInfo->CopyFrom(info); userInfo->CopyFrom(info);

View file

@ -1,13 +1,13 @@
/** /**
* @file player_info.h * @file player_info.h
* @ingroup GameLogicPlayers * @ingroup GameLogicPlayers
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef COCKATRICE_PLAYER_INFO_H #ifndef COCKATRICE_PLAYER_INFO_H
#define COCKATRICE_PLAYER_INFO_H #define COCKATRICE_PLAYER_INFO_H
#include "player_target.h" #include "../../game_graphics/player/player_target.h"
#include <QObject> #include <QObject>
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h> #include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
@ -22,7 +22,6 @@ public:
int id; int id;
bool local; bool local;
bool judge; bool judge;
bool handVisible;
int getId() const int getId() const
{ {
@ -51,16 +50,6 @@ public:
return judge; return judge;
} }
void setHandVisible(bool _handVisible)
{
handVisible = _handVisible;
}
bool getHandVisible() const
{
return handVisible;
}
QString getName() const QString getName() const
{ {
return QString::fromStdString(userInfo->name()); return QString::fromStdString(userInfo->name());

View file

@ -1,18 +1,18 @@
#include "player.h" #include "player_logic.h"
#include "../../game_graphics/board/arrow_item.h"
#include "../../game_graphics/board/card_item.h"
#include "../../game_graphics/board/counter_general.h"
#include "../../game_graphics/game_scene.h"
#include "../../game_graphics/player/player_target.h"
#include "../../game_graphics/zones/hand_zone.h"
#include "../../game_graphics/zones/pile_zone.h"
#include "../../game_graphics/zones/stack_zone.h"
#include "../../game_graphics/zones/table_zone.h"
#include "../../interface/theme_manager.h" #include "../../interface/theme_manager.h"
#include "../../interface/widgets/tabs/tab_game.h" #include "../../interface/widgets/tabs/tab_game.h"
#include "../board/arrow_item.h"
#include "../board/card_item.h"
#include "../board/card_list.h" #include "../board/card_list.h"
#include "../board/counter_general.h"
#include "../game_scene.h"
#include "../zones/hand_zone.h"
#include "../zones/pile_zone.h"
#include "../zones/stack_zone.h"
#include "../zones/table_zone.h"
#include "player_actions.h" #include "player_actions.h"
#include "player_target.h"
#include <QDebug> #include <QDebug>
#include <QMenu> #include <QMenu>
@ -29,23 +29,15 @@
#include <libcockatrice/protocol/pb/serverinfo_zone.pb.h> #include <libcockatrice/protocol/pb/serverinfo_zone.pb.h>
#include <libcockatrice/utility/color.h> #include <libcockatrice/utility/color.h>
Player::Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, AbstractGame *_parent) PlayerLogic::PlayerLogic(const ServerInfo_User &info, int _id, bool _local, bool _judge, AbstractGame *_parent)
: QObject(_parent), game(_parent), playerInfo(new PlayerInfo(info, _id, _local, _judge)), : QObject(_parent), game(_parent), playerInfo(new PlayerInfo(info, _id, _local, _judge)),
playerEventHandler(new PlayerEventHandler(this)), playerActions(new PlayerActions(this)), active(false), playerEventHandler(new PlayerEventHandler(this)), playerActions(new PlayerActions(this)), active(false),
conceded(false), zoneId(0), dialogSemaphore(false) conceded(false), zoneId(0), dialogSemaphore(false)
{ {
initializeZones(); initializeZones();
playerMenu = new PlayerMenu(this);
graphicsItem = new PlayerGraphicsItem(this);
playerMenu->setMenusForGraphicItems();
connect(this, &Player::activeChanged, graphicsItem, &PlayerGraphicsItem::onPlayerActiveChanged);
connect(this, &Player::openDeckEditor, game->getTab(), &TabGame::openDeckEditor);
} }
void Player::initializeZones() void PlayerLogic::initializeZones()
{ {
addZone(new PileZoneLogic(this, ZoneNames::DECK, false, true, false, this)); addZone(new PileZoneLogic(this, ZoneNames::DECK, false, true, false, this));
addZone(new PileZoneLogic(this, ZoneNames::GRAVE, false, false, true, this)); addZone(new PileZoneLogic(this, ZoneNames::GRAVE, false, false, true, this));
@ -58,7 +50,7 @@ void Player::initializeZones()
addZone(new HandZoneLogic(this, ZoneNames::HAND, false, false, visibleHand, this)); addZone(new HandZoneLogic(this, ZoneNames::HAND, false, false, visibleHand, this));
} }
Player::~Player() PlayerLogic::~PlayerLogic()
{ {
qCInfo(PlayerLog) << "Player destructor:" << getPlayerInfo()->getName(); qCInfo(PlayerLog) << "Player destructor:" << getPlayerInfo()->getName();
@ -68,13 +60,12 @@ Player::~Player()
} }
zones.clear(); zones.clear();
delete playerMenu;
delete getPlayerInfo()->userInfo; delete getPlayerInfo()->userInfo;
} }
void Player::clear() void PlayerLogic::clear()
{ {
clearArrows(); emit arrowsClearedLocally();
QMapIterator<QString, CardZoneLogic *> i(zones); QMapIterator<QString, CardZoneLogic *> i(zones);
while (i.hasNext()) { while (i.hasNext()) {
@ -84,12 +75,11 @@ void Player::clear()
clearCounters(); clearCounters();
} }
void Player::setConceded(bool _conceded) void PlayerLogic::setConceded(bool _conceded)
{ {
if (conceded != _conceded) { if (conceded != _conceded) {
conceded = _conceded; conceded = _conceded;
getGraphicsItem()->setVisible(!conceded);
if (conceded) { if (conceded) {
clear(); clear();
} }
@ -97,13 +87,15 @@ void Player::setConceded(bool _conceded)
} }
} }
void Player::setZoneId(int _zoneId) void PlayerLogic::setZoneId(int _zoneId)
{ {
zoneId = _zoneId; if (zoneId != _zoneId) {
graphicsItem->getPlayerArea()->setPlayerZoneId(zoneId); zoneId = _zoneId;
emit zoneIdChanged(zoneId);
}
} }
void Player::processPlayerInfo(const ServerInfo_Player &info) void PlayerLogic::processPlayerInfo(const ServerInfo_Player &info)
{ {
static QSet<QString> builtinZones{/* PileZones */ static QSet<QString> builtinZones{/* PileZones */
ZoneNames::DECK, ZoneNames::GRAVE, ZoneNames::EXILE, ZoneNames::SIDEBOARD, ZoneNames::DECK, ZoneNames::GRAVE, ZoneNames::EXILE, ZoneNames::SIDEBOARD,
@ -114,7 +106,7 @@ void Player::processPlayerInfo(const ServerInfo_Player &info)
/* HandZone */ /* HandZone */
ZoneNames::HAND}; ZoneNames::HAND};
clearCounters(); clearCounters();
clearArrows(); emit arrowsClearedLocally();
QMutableMapIterator<QString, CardZoneLogic *> zoneIt(zones); QMutableMapIterator<QString, CardZoneLogic *> zoneIt(zones);
while (zoneIt.hasNext()) { while (zoneIt.hasNext()) {
@ -201,7 +193,7 @@ void Player::processPlayerInfo(const ServerInfo_Player &info)
setConceded(info.properties().conceded()); setConceded(info.properties().conceded());
} }
void Player::processCardAttachment(const ServerInfo_Player &info) void PlayerLogic::processCardAttachment(const ServerInfo_Player &info)
{ {
const int zoneListSize = info.zone_list_size(); const int zoneListSize = info.zone_list_size();
for (int i = 0; i < zoneListSize; ++i) { for (int i = 0; i < zoneListSize; ++i) {
@ -230,16 +222,17 @@ void Player::processCardAttachment(const ServerInfo_Player &info)
const int arrowListSize = info.arrow_list_size(); const int arrowListSize = info.arrow_list_size();
for (int i = 0; i < arrowListSize; ++i) { for (int i = 0; i < arrowListSize; ++i) {
addArrow(info.arrow_list(i)); emit arrowCreateRequested(QSharedPointer<ArrowData>::create(
ArrowData::fromProto(info.arrow_list(i), getPlayerInfo()->getId(), getPlayerInfo()->getLocal())));
} }
} }
void Player::addCard(CardItem *card) void PlayerLogic::addCard(CardItem *card)
{ {
emit newCardAdded(card); emit newCardAdded(card);
} }
void Player::deleteCard(CardItem *card) void PlayerLogic::deleteCard(CardItem *card)
{ {
if (card == nullptr) { if (card == nullptr) {
return; return;
@ -250,180 +243,60 @@ void Player::deleteCard(CardItem *card)
} }
} }
void Player::setDeck(const DeckList &_deck) void PlayerLogic::setDeck(const DeckList &_deck)
{ {
deck = _deck; deck = _deck;
emit deckChanged(); emit deckChanged();
} }
AbstractCounter *Player::addCounter(const ServerInfo_Counter &counter) CounterState *PlayerLogic::addCounter(const ServerInfo_Counter &counter)
{ {
return addCounter(counter.id(), QString::fromStdString(counter.name()), return addCounter(counter.id(), QString::fromStdString(counter.name()),
convertColorToQColor(counter.counter_color()), counter.radius(), counter.count()); convertColorToQColor(counter.counter_color()), counter.radius(), counter.count());
} }
AbstractCounter *Player::addCounter(int counterId, const QString &name, QColor color, int radius, int value) CounterState *PlayerLogic::addCounter(int id, const QString &name, const QColor &color, int radius, int value)
{ {
if (counters.contains(counterId)) { if (counters.contains(id)) {
return nullptr; return nullptr;
} }
auto *state = new CounterState(id, name, color, radius, value, this);
AbstractCounter *ctr; counters.insert(id, state);
if (name == "life") { emit counterAdded(state);
ctr = getGraphicsItem()->getPlayerTarget()->addCounter(counterId, name, value); return state;
} else {
ctr = new GeneralCounter(this, counterId, name, color, radius, value, true, graphicsItem);
}
counters.insert(counterId, ctr);
if (playerMenu->getCountersMenu() && ctr->getMenu()) {
playerMenu->getCountersMenu()->addMenu(ctr->getMenu());
}
if (playerMenu->getShortcutsActive()) {
ctr->setShortcutsActive();
}
emit rearrangeCounters();
return ctr;
} }
void Player::delCounter(int counterId) void PlayerLogic::delCounter(int id)
{ {
AbstractCounter *ctr = counters.value(counterId, 0); auto *state = counters.take(id);
if (!ctr) { if (!state) {
return; return;
} }
emit counterRemoved(id);
ctr->delCounter(); state->deleteLater();
counters.remove(counterId);
emit rearrangeCounters();
} }
void Player::clearCounters() void PlayerLogic::clearCounters()
{ {
QMapIterator<int, AbstractCounter *> counterIterator(counters); for (int id : counters.keys()) {
while (counterIterator.hasNext()) { emit counterRemoved(id);
counterIterator.next().value()->delCounter();
} }
qDeleteAll(counters);
counters.clear(); counters.clear();
} }
void Player::incrementAllCardCounters() CounterState *PlayerLogic::getLifeCounter() const
{ {
auto cardsToUpdate = getGameScene()->selectedCards(); for (auto *s : counters.values()) {
if (cardsToUpdate.isEmpty()) { if (s->getName() == "life") {
// If no cards selected, update all cards on table return s;
cardsToUpdate = static_cast<QList<CardItem *>>(getTableZone()->getCards());
}
QList<const ::google::protobuf::Message *> commandList;
for (const auto *card : cardsToUpdate) {
const auto &cardCounters = card->getCounters();
QMapIterator<int, int> counterIterator(cardCounters);
while (counterIterator.hasNext()) {
counterIterator.next();
int counterId = counterIterator.key();
int currentValue = counterIterator.value();
if (currentValue >= MAX_COUNTERS_ON_CARD) {
continue;
}
auto cmd = std::make_unique<Command_SetCardCounter>();
cmd->set_zone(card->getZone()->getName().toStdString());
cmd->set_card_id(card->getId());
cmd->set_counter_id(counterId);
cmd->set_counter_value(currentValue + 1);
commandList.append(cmd.release());
}
}
if (!commandList.isEmpty()) {
playerActions->sendGameCommand(playerActions->prepareGameCommand(commandList));
}
}
AbstractCounter *Player::getLifeCounter() const
{
for (auto counter : counters.values()) {
if (counter->getName() == "life") {
return counter;
} }
} }
return nullptr; return nullptr;
} }
ArrowItem *Player::addArrow(const ServerInfo_Arrow &arrow) bool PlayerLogic::clearCardsToDelete()
{
const QMap<int, Player *> &playerList = game->getPlayerManager()->getPlayers();
Player *startPlayer = playerList.value(arrow.start_player_id(), 0);
Player *targetPlayer = playerList.value(arrow.target_player_id(), 0);
if (!startPlayer || !targetPlayer) {
return nullptr;
}
CardZoneLogic *startZone = startPlayer->getZones().value(QString::fromStdString(arrow.start_zone()), 0);
CardZoneLogic *targetZone = nullptr;
if (arrow.has_target_zone()) {
targetZone = targetPlayer->getZones().value(QString::fromStdString(arrow.target_zone()), 0);
}
if (!startZone || (!targetZone && arrow.has_target_zone())) {
return nullptr;
}
CardItem *startCard = startZone->getCard(arrow.start_card_id());
CardItem *targetCard = nullptr;
if (targetZone) {
targetCard = targetZone->getCard(arrow.target_card_id());
}
if (!startCard || (!targetCard && arrow.has_target_card_id())) {
return nullptr;
}
if (targetCard) {
return addArrow(arrow.id(), startCard, targetCard, convertColorToQColor(arrow.arrow_color()));
} else {
return addArrow(arrow.id(), startCard, targetPlayer->getGraphicsItem()->getPlayerTarget(),
convertColorToQColor(arrow.arrow_color()));
}
}
ArrowItem *Player::addArrow(int arrowId, CardItem *startCard, ArrowTarget *targetItem, const QColor &color)
{
auto *arrow = new ArrowItem(this, arrowId, startCard, targetItem, color);
arrows.insert(arrowId, arrow);
getGameScene()->addItem(arrow);
return arrow;
}
void Player::delArrow(int arrowId)
{
ArrowItem *arr = arrows.value(arrowId, 0);
if (!arr) {
return;
}
arr->delArrow();
}
void Player::removeArrow(ArrowItem *arrow)
{
if (arrow->getId() != -1) {
arrows.remove(arrow->getId());
}
}
void Player::clearArrows()
{
QMapIterator<int, ArrowItem *> arrowIterator(arrows);
while (arrowIterator.hasNext()) {
arrowIterator.next().value()->delArrow();
}
arrows.clear();
}
bool Player::clearCardsToDelete()
{ {
if (cardsToDelete.isEmpty()) { if (cardsToDelete.isEmpty()) {
return false; return false;
@ -439,28 +312,22 @@ bool Player::clearCardsToDelete()
return true; return true;
} }
void Player::setActive(bool _active) void PlayerLogic::setActive(bool _active)
{ {
active = _active; active = _active;
emit activeChanged(active); emit activeChanged(active);
} }
void PlayerLogic::onRequestZoneViewToggle(const QString &zoneName, int numberCards, bool isReversed)
{
emit requestZoneViewToggle(this, zoneName, numberCards, isReversed);
}
void Player::updateZones() void PlayerLogic::updateZones()
{ {
getTableZone()->reorganizeCards(); getTableZone()->reorganizeCards();
} }
PlayerGraphicsItem *Player::getGraphicsItem() void PlayerLogic::setGameStarted()
{
return graphicsItem;
}
GameScene *Player::getGameScene()
{
return getGraphicsItem()->getGameScene();
}
void Player::setGameStarted()
{ {
if (playerInfo->local) { if (playerInfo->local) {
emit resetTopCardMenuActions(); emit resetTopCardMenuActions();

View file

@ -1,23 +1,21 @@
/** /**
* @file player.h * @file player.h
* @ingroup GameLogicPlayers * @ingroup GameLogicPlayers
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef PLAYER_H #ifndef PLAYER_H
#define PLAYER_H #define PLAYER_H
#include "../../game_graphics/board/abstract_graphics_item.h" #include "../../game_graphics/player/player_area.h"
#include "../../interface/widgets/menus/tearoff_menu.h" #include "../../interface/widgets/menus/tearoff_menu.h"
#include "../board/arrow_data.h"
#include "../interface/deck_loader/loaded_deck.h" #include "../interface/deck_loader/loaded_deck.h"
#include "../zones/logic/hand_zone_logic.h" #include "../zones/hand_zone_logic.h"
#include "../zones/logic/pile_zone_logic.h" #include "../zones/pile_zone_logic.h"
#include "../zones/logic/stack_zone_logic.h" #include "../zones/stack_zone_logic.h"
#include "../zones/logic/table_zone_logic.h" #include "../zones/table_zone_logic.h"
#include "menu/player_menu.h"
#include "player_area.h"
#include "player_event_handler.h" #include "player_event_handler.h"
#include "player_graphics_item.h"
#include "player_info.h" #include "player_info.h"
#include <QInputDialog> #include <QInputDialog>
@ -39,7 +37,6 @@ class Message;
} }
} // namespace google } // namespace google
class AbstractCardItem; class AbstractCardItem;
class AbstractCounter;
class AbstractGame; class AbstractGame;
class ArrowItem; class ArrowItem;
class ArrowTarget; class ArrowTarget;
@ -55,6 +52,7 @@ class PlayerMenu;
class QAction; class QAction;
class QMenu; class QMenu;
class ServerInfo_Arrow; class ServerInfo_Arrow;
class ServerInfo_Card;
class ServerInfo_Counter; class ServerInfo_Counter;
class ServerInfo_Player; class ServerInfo_Player;
class ServerInfo_User; class ServerInfo_User;
@ -62,27 +60,41 @@ class TabGame;
const int MAX_TOKENS_PER_DIALOG = 99; const int MAX_TOKENS_PER_DIALOG = 99;
class Player : public QObject class PlayerLogic : public QObject
{ {
Q_OBJECT Q_OBJECT
signals: signals:
void openDeckEditor(const LoadedDeck &deck); void openDeckEditor(const LoadedDeck &deck);
void requestZoneViewToggle(PlayerLogic *player, const QString &zoneName, int numberCards, bool isReversed);
void requestRevealedZoneView(PlayerLogic *player,
CardZoneLogic *zone,
const QList<const ServerInfo_Card *> &cardList,
bool withWritePermission);
void deckChanged(); void deckChanged();
void newCardAdded(AbstractCardItem *card); void newCardAdded(AbstractCardItem *card);
void requestCardMenuUpdate(const CardItem *card);
void counterAdded(CounterState *state);
void counterRemoved(int counterId);
void rearrangeCounters(); void rearrangeCounters();
void activeChanged(bool active); void activeChanged(bool active);
void zoneIdChanged(int zoneId);
void concededChanged(int playerId, bool conceded); void concededChanged(int playerId, bool conceded);
void clearCustomZonesMenu(); void clearCustomZonesMenu();
void addViewCustomZoneActionToCustomZoneMenu(QString zoneName); void addViewCustomZoneActionToCustomZoneMenu(QString zoneName);
void resetTopCardMenuActions(); void resetTopCardMenuActions();
void arrowCreateRequested(QSharedPointer<ArrowData> data);
void arrowDeleteRequested(int creatorId, int arrowId);
void arrowDeleted(int creatorId, int arrowId);
void arrowsClearedLocally(); // fires on clear() and processPlayerInfo
public slots: public slots:
void setActive(bool _active); void setActive(bool _active);
void onRequestZoneViewToggle(const QString &zoneName, int numberCards, bool isReversed);
public: public:
Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, AbstractGame *_parent); PlayerLogic(const ServerInfo_User &info, int _id, bool _local, bool _judge, AbstractGame *_parent);
~Player() override; ~PlayerLogic() override;
void initializeZones(); void initializeZones();
void updateZones(); void updateZones();
@ -106,10 +118,6 @@ public:
return game; return game;
} }
GameScene *getGameScene();
[[nodiscard]] PlayerGraphicsItem *getGraphicsItem();
[[nodiscard]] PlayerActions *getPlayerActions() const [[nodiscard]] PlayerActions *getPlayerActions() const
{ {
return playerActions; return playerActions;
@ -125,11 +133,6 @@ public:
return playerInfo; return playerInfo;
} }
[[nodiscard]] PlayerMenu *getPlayerMenu() const
{
return playerMenu;
}
void setDeck(const DeckList &_deck); void setDeck(const DeckList &_deck);
[[nodiscard]] const DeckList &getDeck() const [[nodiscard]] const DeckList &getDeck() const
@ -188,13 +191,12 @@ public:
return qobject_cast<HandZoneLogic *>(zones.value(ZoneNames::HAND)); return qobject_cast<HandZoneLogic *>(zones.value(ZoneNames::HAND));
} }
AbstractCounter *addCounter(const ServerInfo_Counter &counter); CounterState *addCounter(const ServerInfo_Counter &counter);
AbstractCounter *addCounter(int counterId, const QString &name, QColor color, int radius, int value); CounterState *addCounter(int id, const QString &name, const QColor &color, int radius, int value);
void delCounter(int counterId); void delCounter(int counterId);
void clearCounters(); void clearCounters();
void incrementAllCardCounters();
QMap<int, AbstractCounter *> getCounters() QMap<int, CounterState *> getCounters() const
{ {
return counters; return counters;
} }
@ -202,18 +204,7 @@ public:
/** /**
* Gets the counter that represents the life total. * Gets the counter that represents the life total.
*/ */
AbstractCounter *getLifeCounter() const; CounterState *getLifeCounter() const;
ArrowItem *addArrow(const ServerInfo_Arrow &arrow);
ArrowItem *addArrow(int arrowId, CardItem *startCard, ArrowTarget *targetItem, const QColor &color);
void delArrow(int arrowId);
void removeArrow(ArrowItem *arrow);
void clearArrows();
const QMap<int, ArrowItem *> &getArrows() const
{
return arrows;
}
void setConceded(bool _conceded); void setConceded(bool _conceded);
bool getConceded() const bool getConceded() const
@ -240,8 +231,6 @@ private:
PlayerInfo *playerInfo; PlayerInfo *playerInfo;
PlayerEventHandler *playerEventHandler; PlayerEventHandler *playerEventHandler;
PlayerActions *playerActions; PlayerActions *playerActions;
PlayerMenu *playerMenu;
PlayerGraphicsItem *graphicsItem;
bool active; bool active;
bool conceded; bool conceded;
@ -250,13 +239,10 @@ private:
int zoneId; int zoneId;
QMap<QString, CardZoneLogic *> zones; QMap<QString, CardZoneLogic *> zones;
QMap<int, AbstractCounter *> counters; QMap<int, CounterState *> counters;
QMap<int, ArrowItem *> arrows;
bool dialogSemaphore; bool dialogSemaphore;
QList<CardItem *> cardsToDelete; QList<CardItem *> cardsToDelete;
// void eventConnectionStateChanged(const Event_ConnectionStateChanged &event);
}; };
class AnnotationDialog : public QInputDialog class AnnotationDialog : public QInputDialog

View file

@ -1,35 +1,35 @@
#include "player_manager.h" #include "player_manager.h"
#include "../abstract_game.h" #include "../abstract_game.h"
#include "player.h" #include "player_logic.h"
PlayerManager::PlayerManager(AbstractGame *_game, PlayerManager::PlayerManager(AbstractGame *_game,
int _localPlayerId, int _localPlayerId,
bool _localPlayerIsJudge, bool _localPlayerIsJudge,
bool localPlayerIsSpectator) bool localPlayerIsSpectator)
: QObject(_game), game(_game), players(QMap<int, Player *>()), localPlayerId(_localPlayerId), : QObject(_game), game(_game), players(QMap<int, PlayerLogic *>()), localPlayerId(_localPlayerId),
localPlayerIsJudge(_localPlayerIsJudge), localPlayerIsSpectator(localPlayerIsSpectator) localPlayerIsJudge(_localPlayerIsJudge), localPlayerIsSpectator(localPlayerIsSpectator)
{ {
} }
bool PlayerManager::isMainPlayerConceded() const bool PlayerManager::isMainPlayerConceded() const
{ {
Player *player = players.value(localPlayerId, nullptr); PlayerLogic *player = players.value(localPlayerId, nullptr);
return player && player->getConceded(); return player && player->getConceded();
} }
Player *PlayerManager::getActiveLocalPlayer(int activePlayer) const PlayerLogic *PlayerManager::getActiveLocalPlayer(int activePlayer) const
{ {
Player *active = players.value(activePlayer, 0); PlayerLogic *active = players.value(activePlayer, 0);
if (active) { if (active) {
if (active->getPlayerInfo()->getLocal()) { if (active->getPlayerInfo()->getLocal()) {
return active; return active;
} }
} }
QMapIterator<int, Player *> playerIterator(players); QMapIterator<int, PlayerLogic *> playerIterator(players);
while (playerIterator.hasNext()) { while (playerIterator.hasNext()) {
Player *temp = playerIterator.next().value(); PlayerLogic *temp = playerIterator.next().value();
if (temp->getPlayerInfo()->getLocal()) { if (temp->getPlayerInfo()->getLocal()) {
return temp; return temp;
} }
@ -43,11 +43,11 @@ bool PlayerManager::isLocalPlayer(int playerId)
return game->getGameState()->getIsLocalGame() || playerId == localPlayerId; return game->getGameState()->getIsLocalGame() || playerId == localPlayerId;
} }
Player *PlayerManager::addPlayer(int playerId, const ServerInfo_User &info) PlayerLogic *PlayerManager::addPlayer(int playerId, const ServerInfo_User &info)
{ {
auto *newPlayer = new Player(info, playerId, isLocalPlayer(playerId) || game->getGameState()->getIsLocalGame(), auto *newPlayer = new PlayerLogic(info, playerId, isLocalPlayer(playerId) || game->getGameState()->getIsLocalGame(),
isJudge(), getGame()); isJudge(), getGame());
connect(newPlayer, &Player::concededChanged, this, &PlayerManager::onPlayerConceded); connect(newPlayer, &PlayerLogic::concededChanged, this, &PlayerManager::onPlayerConceded);
players.insert(playerId, newPlayer); players.insert(playerId, newPlayer);
emit playerAdded(newPlayer); emit playerAdded(newPlayer);
emit playerCountChanged(); emit playerCountChanged();
@ -56,7 +56,7 @@ Player *PlayerManager::addPlayer(int playerId, const ServerInfo_User &info)
void PlayerManager::removePlayer(int playerId) void PlayerManager::removePlayer(int playerId)
{ {
Player *player = getPlayer(playerId); PlayerLogic *player = getPlayer(playerId);
if (!player) { if (!player) {
return; return;
} }
@ -66,9 +66,9 @@ void PlayerManager::removePlayer(int playerId)
player->deleteLater(); player->deleteLater();
} }
Player *PlayerManager::getPlayer(int playerId) const PlayerLogic *PlayerManager::getPlayer(int playerId) const
{ {
Player *player = players.value(playerId, 0); PlayerLogic *player = players.value(playerId, 0);
if (!player) { if (!player) {
return nullptr; return nullptr;
} }

View file

@ -1,8 +1,8 @@
/** /**
* @file player_manager.h * @file player_manager.h
* @ingroup GameLogicPlayers * @ingroup GameLogicPlayers
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef COCKATRICE_PLAYER_MANAGER_H #ifndef COCKATRICE_PLAYER_MANAGER_H
#define COCKATRICE_PLAYER_MANAGER_H #define COCKATRICE_PLAYER_MANAGER_H
@ -12,7 +12,7 @@
#include <libcockatrice/protocol/pb/serverinfo_playerproperties.pb.h> #include <libcockatrice/protocol/pb/serverinfo_playerproperties.pb.h>
class AbstractGame; class AbstractGame;
class Player; class PlayerLogic;
class PlayerManager : public QObject class PlayerManager : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -21,7 +21,7 @@ public:
PlayerManager(AbstractGame *_game, int _localPlayerId, bool _localPlayerIsJudge, bool localPlayerIsSpectator); PlayerManager(AbstractGame *_game, int _localPlayerId, bool _localPlayerIsJudge, bool localPlayerIsSpectator);
AbstractGame *game; AbstractGame *game;
QMap<int, Player *> players; QMap<int, PlayerLogic *> players;
int localPlayerId; int localPlayerId;
bool localPlayerIsJudge; bool localPlayerIsJudge;
bool localPlayerIsSpectator; bool localPlayerIsSpectator;
@ -42,7 +42,7 @@ public:
return localPlayerId; return localPlayerId;
} }
[[nodiscard]] const QMap<int, Player *> &getPlayers() const [[nodiscard]] const QMap<int, PlayerLogic *> &getPlayers() const
{ {
return players; return players;
} }
@ -52,14 +52,14 @@ public:
return players.size(); return players.size();
} }
[[nodiscard]] Player *getActiveLocalPlayer(int activePlayer) const; [[nodiscard]] PlayerLogic *getActiveLocalPlayer(int activePlayer) const;
bool isLocalPlayer(int playerId); bool isLocalPlayer(int playerId);
Player *addPlayer(int playerId, const ServerInfo_User &info); PlayerLogic *addPlayer(int playerId, const ServerInfo_User &info);
void removePlayer(int playerId); void removePlayer(int playerId);
[[nodiscard]] Player *getPlayer(int playerId) const; [[nodiscard]] PlayerLogic *getPlayer(int playerId) const;
void onPlayerConceded(int playerId, bool conceded); void onPlayerConceded(int playerId, bool conceded);
@ -106,8 +106,8 @@ public:
} }
signals: signals:
void playerAdded(Player *player); void playerAdded(PlayerLogic *player);
void playerRemoved(Player *player); void playerRemoved(PlayerLogic *player);
void activeLocalPlayerConceded(); void activeLocalPlayerConceded();
void activeLocalPlayerUnconceded(); void activeLocalPlayerUnconceded();
void playerConceded(int playerId); void playerConceded(int playerId);

View file

@ -2,9 +2,9 @@
#include "../interface/widgets/tabs/tab_game.h" #include "../interface/widgets/tabs/tab_game.h"
Replay::Replay(TabGame *_tab, GameReplay *_replay) : AbstractGame(_tab) Replay::Replay(QObject *_parent, GameReplay *_replay, bool isLocalGame) : AbstractGame(_parent)
{ {
gameState = new GameState(this, 0, -1, tab->getTabSupervisor()->getIsLocalGame(), {}, false, false, -1, false); gameState = new GameState(this, 0, -1, isLocalGame, {}, false, false, -1, false);
connect(gameMetaInfo, &GameMetaInfo::startedChanged, gameState, &GameState::onStartedChanged); connect(gameMetaInfo, &GameMetaInfo::startedChanged, gameState, &GameState::onStartedChanged);
playerManager = new PlayerManager(this, -1, false, true); playerManager = new PlayerManager(this, -1, false, true);
loadReplay(_replay); loadReplay(_replay);

View file

@ -2,8 +2,8 @@
* @file replay.h * @file replay.h
* @ingroup GameLogic * @ingroup GameLogic
* @ingroup Replay * @ingroup Replay
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef COCKATRICE_REPLAY_H #ifndef COCKATRICE_REPLAY_H
#define COCKATRICE_REPLAY_H #define COCKATRICE_REPLAY_H
@ -15,7 +15,7 @@ class Replay : public AbstractGame
Q_OBJECT Q_OBJECT
public: public:
explicit Replay(TabGame *_tab, GameReplay *_replay); explicit Replay(QObject *_parent, GameReplay *_replay, bool isLocalGame);
}; };
#endif // COCKATRICE_REPLAY_H #endif // COCKATRICE_REPLAY_H

View file

@ -1,9 +1,9 @@
#include "card_zone_logic.h" #include "card_zone_logic.h"
#include "../../board/card_item.h" #include "../../game_graphics/board/card_item.h"
#include "../../player/player.h" #include "../../game_graphics/zones/view_zone.h"
#include "../../player/player_actions.h" #include "../player/player_actions.h"
#include "../view_zone.h" #include "../player/player_logic.h"
#include "view_zone_logic.h" #include "view_zone_logic.h"
#include <QAction> #include <QAction>
@ -20,7 +20,7 @@
* @param _contentsKnown whether the cards in the zone are known to the client * @param _contentsKnown whether the cards in the zone are known to the client
* @param parent the parent QObject. * @param parent the parent QObject.
*/ */
CardZoneLogic::CardZoneLogic(Player *_player, CardZoneLogic::CardZoneLogic(PlayerLogic *_player,
const QString &_name, const QString &_name,
bool _hasCardAttr, bool _hasCardAttr,
bool _isShufflable, bool _isShufflable,

View file

@ -1,21 +1,21 @@
/** /**
* @file card_zone_logic.h * @file card_zone_logic.h
* @ingroup GameLogicZones * @ingroup GameLogicZones
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef COCKATRICE_CARD_ZONE_LOGIC_H #ifndef COCKATRICE_CARD_ZONE_LOGIC_H
#define COCKATRICE_CARD_ZONE_LOGIC_H #define COCKATRICE_CARD_ZONE_LOGIC_H
#include "../../../client/translation.h" #include "../../client/translation.h"
#include "../../board/card_list.h" #include "../board/card_list.h"
#include <QLoggingCategory> #include <QLoggingCategory>
#include <QObject> #include <QObject>
inline Q_LOGGING_CATEGORY(CardZoneLogicLog, "card_zone_logic"); inline Q_LOGGING_CATEGORY(CardZoneLogicLog, "card_zone_logic");
class Player; class PlayerLogic;
class ZoneViewZone; class ZoneViewZone;
class QMenu; class QMenu;
class QAction; class QAction;
@ -35,7 +35,7 @@ signals:
void retranslateUi(); void retranslateUi();
public: public:
explicit CardZoneLogic(Player *_player, explicit CardZoneLogic(PlayerLogic *_player,
const QString &_name, const QString &_name,
bool _hasCardAttr, bool _hasCardAttr,
bool _isShufflable, bool _isShufflable,
@ -68,7 +68,7 @@ public:
return name; return name;
} }
[[nodiscard]] QString getTranslatedName(bool theirOwn, GrammaticalCase gc) const; [[nodiscard]] QString getTranslatedName(bool theirOwn, GrammaticalCase gc) const;
[[nodiscard]] Player *getPlayer() const [[nodiscard]] PlayerLogic *getPlayer() const
{ {
return player; return player;
} }
@ -105,7 +105,7 @@ private slots:
void refreshCardInfos(); void refreshCardInfos();
protected: protected:
Player *player; PlayerLogic *player;
QString name; QString name;
CardList cards; CardList cards;
QList<ZoneViewZone *> views; QList<ZoneViewZone *> views;

View file

@ -1,9 +1,9 @@
#include "hand_zone_logic.h" #include "hand_zone_logic.h"
#include "../../board/card_item.h" #include "../../game_graphics/board/card_item.h"
#include "card_zone_algorithms.h" #include "card_zone_algorithms.h"
HandZoneLogic::HandZoneLogic(Player *_player, HandZoneLogic::HandZoneLogic(PlayerLogic *_player,
const QString &_name, const QString &_name,
bool _hasCardAttr, bool _hasCardAttr,
bool _isShufflable, bool _isShufflable,

View file

@ -1,8 +1,8 @@
/** /**
* @file hand_zone_logic.h * @file hand_zone_logic.h
* @ingroup GameLogicZones * @ingroup GameLogicZones
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef COCKATRICE_HAND_ZONE_LOGIC_H #ifndef COCKATRICE_HAND_ZONE_LOGIC_H
#define COCKATRICE_HAND_ZONE_LOGIC_H #define COCKATRICE_HAND_ZONE_LOGIC_H
@ -12,7 +12,7 @@ class HandZoneLogic : public CardZoneLogic
{ {
Q_OBJECT Q_OBJECT
public: public:
HandZoneLogic(Player *_player, HandZoneLogic(PlayerLogic *_player,
const QString &_name, const QString &_name,
bool _hasCardAttr, bool _hasCardAttr,
bool _isShufflable, bool _isShufflable,

View file

@ -1,8 +1,8 @@
#include "pile_zone_logic.h" #include "pile_zone_logic.h"
#include "../../board/card_item.h" #include "../../game_graphics/board/card_item.h"
PileZoneLogic::PileZoneLogic(Player *_player, PileZoneLogic::PileZoneLogic(PlayerLogic *_player,
const QString &_name, const QString &_name,
bool _hasCardAttr, bool _hasCardAttr,
bool _isShufflable, bool _isShufflable,

View file

@ -1,8 +1,8 @@
/** /**
* @file pile_zone_logic.h * @file pile_zone_logic.h
* @ingroup GameLogicZones * @ingroup GameLogicZones
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef COCKATRICE_PILE_ZONE_LOGIC_H #ifndef COCKATRICE_PILE_ZONE_LOGIC_H
#define COCKATRICE_PILE_ZONE_LOGIC_H #define COCKATRICE_PILE_ZONE_LOGIC_H
@ -17,7 +17,7 @@ signals:
void callUpdate(); void callUpdate();
public: public:
PileZoneLogic(Player *_player, PileZoneLogic(PlayerLogic *_player,
const QString &_name, const QString &_name,
bool _hasCardAttr, bool _hasCardAttr,
bool _isShufflable, bool _isShufflable,

View file

@ -1,9 +1,9 @@
#include "stack_zone_logic.h" #include "stack_zone_logic.h"
#include "../../board/card_item.h" #include "../../game_graphics/board/card_item.h"
#include "card_zone_algorithms.h" #include "card_zone_algorithms.h"
StackZoneLogic::StackZoneLogic(Player *_player, StackZoneLogic::StackZoneLogic(PlayerLogic *_player,
const QString &_name, const QString &_name,
bool _hasCardAttr, bool _hasCardAttr,
bool _isShufflable, bool _isShufflable,

View file

@ -1,8 +1,8 @@
/** /**
* @file stack_zone_logic.h * @file stack_zone_logic.h
* @ingroup GameLogicZones * @ingroup GameLogicZones
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef COCKATRICE_STACK_ZONE_LOGIC_H #ifndef COCKATRICE_STACK_ZONE_LOGIC_H
#define COCKATRICE_STACK_ZONE_LOGIC_H #define COCKATRICE_STACK_ZONE_LOGIC_H
@ -12,7 +12,7 @@ class StackZoneLogic : public CardZoneLogic
{ {
Q_OBJECT Q_OBJECT
public: public:
StackZoneLogic(Player *_player, StackZoneLogic(PlayerLogic *_player,
const QString &_name, const QString &_name,
bool _hasCardAttr, bool _hasCardAttr,
bool _isShufflable, bool _isShufflable,

View file

@ -1,8 +1,8 @@
#include "table_zone_logic.h" #include "table_zone_logic.h"
#include "../../board/card_item.h" #include "../../game_graphics/board/card_item.h"
TableZoneLogic::TableZoneLogic(Player *_player, TableZoneLogic::TableZoneLogic(PlayerLogic *_player,
const QString &_name, const QString &_name,
bool _hasCardAttr, bool _hasCardAttr,
bool _isShufflable, bool _isShufflable,

View file

@ -1,8 +1,8 @@
/** /**
* @file table_zone_logic.h * @file table_zone_logic.h
* @ingroup GameLogicZones * @ingroup GameLogicZones
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef COCKATRICE_TABLE_ZONE_LOGIC_H #ifndef COCKATRICE_TABLE_ZONE_LOGIC_H
#define COCKATRICE_TABLE_ZONE_LOGIC_H #define COCKATRICE_TABLE_ZONE_LOGIC_H
@ -16,7 +16,7 @@ signals:
void toggleTapped(); void toggleTapped();
public: public:
TableZoneLogic(Player *_player, TableZoneLogic(PlayerLogic *_player,
const QString &_name, const QString &_name,
bool _hasCardAttr, bool _hasCardAttr,
bool _isShufflable, bool _isShufflable,

View file

@ -1,7 +1,7 @@
#include "view_zone_logic.h" #include "view_zone_logic.h"
#include "../../../client/settings/cache_settings.h" #include "../../client/settings/cache_settings.h"
#include "../../board/card_item.h" #include "../../game_graphics/board/card_item.h"
/** /**
* @param _player the player that the cards are revealed to. * @param _player the player that the cards are revealed to.
@ -9,7 +9,7 @@
* @param _revealZone if false, the cards will be face down. * @param _revealZone if false, the cards will be face down.
* @param _writeableRevealZone whether the player can interact with the revealed cards. * @param _writeableRevealZone whether the player can interact with the revealed cards.
*/ */
ZoneViewZoneLogic::ZoneViewZoneLogic(Player *_player, ZoneViewZoneLogic::ZoneViewZoneLogic(PlayerLogic *_player,
CardZoneLogic *_origZone, CardZoneLogic *_origZone,
int _numberCards, int _numberCards,
bool _revealZone, bool _revealZone,

View file

@ -1,8 +1,8 @@
/** /**
* @file view_zone_logic.h * @file view_zone_logic.h
* @ingroup GameLogicZones * @ingroup GameLogicZones
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef COCKATRICE_VIEW_ZONE_LOGIC_H #ifndef COCKATRICE_VIEW_ZONE_LOGIC_H
#define COCKATRICE_VIEW_ZONE_LOGIC_H #define COCKATRICE_VIEW_ZONE_LOGIC_H
@ -30,7 +30,7 @@ public:
REMOVE_CARD REMOVE_CARD
}; };
ZoneViewZoneLogic(Player *_player, ZoneViewZoneLogic(PlayerLogic *_player,
CardZoneLogic *_origZone, CardZoneLogic *_origZone,
int _numberCards, int _numberCards,
bool _revealZone, bool _revealZone,

View file

@ -1,8 +1,8 @@
/** /**
* @file abstract_card_drag_item.h * @file abstract_card_drag_item.h
* @ingroup GameGraphicsCards * @ingroup GameGraphicsCards
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef ABSTRACTCARDDRAGITEM_H #ifndef ABSTRACTCARDDRAGITEM_H
#define ABSTRACTCARDDRAGITEM_H #define ABSTRACTCARDDRAGITEM_H

View file

@ -13,7 +13,7 @@
#include <libcockatrice/card/database/card_database.h> #include <libcockatrice/card/database/card_database.h>
#include <libcockatrice/card/database/card_database_manager.h> #include <libcockatrice/card/database/card_database_manager.h>
AbstractCardItem::AbstractCardItem(QGraphicsItem *parent, const CardRef &cardRef, Player *_owner, int _id) AbstractCardItem::AbstractCardItem(QGraphicsItem *parent, const CardRef &cardRef, PlayerLogic *_owner, int _id)
: ArrowTarget(_owner, parent), id(_id), cardRef(cardRef), tapped(false), facedown(false), tapAngle(0), : ArrowTarget(_owner, parent), id(_id), cardRef(cardRef), tapped(false), facedown(false), tapAngle(0),
bgColor(Qt::transparent), isHovered(false), realZValue(0) bgColor(Qt::transparent), isHovered(false), realZValue(0)
{ {

View file

@ -7,14 +7,14 @@
#ifndef ABSTRACTCARDITEM_H #ifndef ABSTRACTCARDITEM_H
#define ABSTRACTCARDITEM_H #define ABSTRACTCARDITEM_H
#include "../../game_graphics/board/graphics_item_type.h"
#include "../card_dimensions.h" #include "../card_dimensions.h"
#include "arrow_target.h" #include "arrow_target.h"
#include "graphics_item_type.h"
#include <libcockatrice/card/printing/exact_card.h> #include <libcockatrice/card/printing/exact_card.h>
#include <libcockatrice/utility/card_ref.h> #include <libcockatrice/utility/card_ref.h>
class Player; class PlayerLogic;
class AbstractCardItem : public ArrowTarget class AbstractCardItem : public ArrowTarget
{ {
@ -44,6 +44,11 @@ signals:
void deleteCardInfoPopup(QString cardName); void deleteCardInfoPopup(QString cardName);
void sigPixmapUpdated(); void sigPixmapUpdated();
void cardShiftClicked(QString cardName); void cardShiftClicked(QString cardName);
void rightClicked(AbstractCardItem *card, QPoint screenPos);
void playSelected(AbstractCardItem *card);
void playSelectedFaceDown(AbstractCardItem *card);
void hideSelected(AbstractCardItem *card);
void selectionChanged(AbstractCardItem *card, bool selected);
public: public:
enum enum
@ -56,7 +61,7 @@ public:
} }
explicit AbstractCardItem(QGraphicsItem *parent = nullptr, explicit AbstractCardItem(QGraphicsItem *parent = nullptr,
const CardRef &cardRef = {}, const CardRef &cardRef = {},
Player *_owner = nullptr, PlayerLogic *_owner = nullptr,
int _id = -1); int _id = -1);
~AbstractCardItem() override; ~AbstractCardItem() override;
QRectF boundingRect() const override; QRectF boundingRect() const override;

View file

@ -1,14 +1,15 @@
#include "abstract_counter.h" #include "abstract_counter.h"
#include "../../client/settings/cache_settings.h" #include "../../client/settings/cache_settings.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_logic.h"
#include "../../game_graphics/board/translate_counter_name.h"
#include "../../interface/widgets/tabs/tab_game.h" #include "../../interface/widgets/tabs/tab_game.h"
#include "../player/player.h"
#include "../player/player_actions.h"
#include "translate_counter_name.h"
#include <QAction> #include <QAction>
#include <QApplication> #include <QApplication>
#include <QGraphicsSceneMouseEvent> #include <QGraphicsSceneMouseEvent>
#include <QGraphicsView>
#include <QKeyEvent> #include <QKeyEvent>
#include <QMenu> #include <QMenu>
#include <QString> #include <QString>
@ -16,24 +17,24 @@
#include <libcockatrice/protocol/pb/command_set_counter.pb.h> #include <libcockatrice/protocol/pb/command_set_counter.pb.h>
#include <libcockatrice/utility/expression.h> #include <libcockatrice/utility/expression.h>
AbstractCounter::AbstractCounter(Player *_player, AbstractCounter::AbstractCounter(CounterState *state,
int _id, PlayerLogic *_player,
const QString &_name,
bool _shownInCounterArea, bool _shownInCounterArea,
int _value,
bool _useNameForShortcut, bool _useNameForShortcut,
QGraphicsItem *parent) QGraphicsItem *parent)
: QGraphicsItem(parent), player(_player), id(_id), name(_name), value(_value), : QGraphicsItem(parent), player(_player), id(state->getId()), name(state->getName()), value(state->getValue()),
useNameForShortcut(_useNameForShortcut), hovered(false), aDec(nullptr), aInc(nullptr), dialogSemaphore(false), color(state->getColor()), radius(state->getRadius()), useNameForShortcut(_useNameForShortcut),
deleteAfterDialog(false), shownInCounterArea(_shownInCounterArea) shownInCounterArea(_shownInCounterArea)
{ {
setAcceptHoverEvents(true); setAcceptHoverEvents(true);
shortcutActive = false; connect(state, &CounterState::valueChanged, this, [this](int, int newValue) {
value = newValue;
update();
});
if (player->getPlayerInfo()->getLocalOrJudge()) { if (player->getPlayerInfo()->getLocalOrJudge()) {
QString displayName = TranslateCounterName::getDisplayName(_name); menu = new TearOffMenu(TranslateCounterName::getDisplayName(state->getName()));
menu = new TearOffMenu(displayName);
aSet = new QAction(this); aSet = new QAction(this);
connect(aSet, &QAction::triggered, this, &AbstractCounter::setCounter); connect(aSet, &QAction::triggered, this, &AbstractCounter::setCounter);
menu->addAction(aSet); menu->addAction(aSet);
@ -41,17 +42,18 @@ AbstractCounter::AbstractCounter(Player *_player,
for (int i = 10; i >= -10; --i) { for (int i = 10; i >= -10; --i) {
if (i == 0) { if (i == 0) {
menu->addSeparator(); menu->addSeparator();
} else { continue;
QAction *aIncrement = new QAction(QString(i < 0 ? "%1" : "+%1").arg(i), this);
if (i == -1) {
aDec = aIncrement;
} else if (i == 1) {
aInc = aIncrement;
}
aIncrement->setData(i);
connect(aIncrement, &QAction::triggered, this, &AbstractCounter::incrementCounter);
menu->addAction(aIncrement);
} }
auto *a = new QAction(QString(i < 0 ? "%1" : "+%1").arg(i), this);
if (i == -1) {
aDec = a;
}
if (i == 1) {
aInc = a;
}
a->setData(i);
connect(a, &QAction::triggered, this, &AbstractCounter::incrementCounter);
menu->addAction(a);
} }
} else { } else {
menu = nullptr; menu = nullptr;
@ -79,31 +81,26 @@ void AbstractCounter::delCounter()
void AbstractCounter::retranslateUi() void AbstractCounter::retranslateUi()
{ {
if (menu) { if (aSet) {
aSet->setText(tr("&Set counter...")); aSet->setText(tr("&Set counter..."));
} }
} }
void AbstractCounter::setShortcutsActive() void AbstractCounter::setShortcutsActive()
{ {
if (!menu) { if (!menu || !player->getPlayerInfo()->getLocal()) {
return; return;
} }
if (!player->getPlayerInfo()->getLocal()) { ShortcutsSettings &sc = SettingsCache::instance().shortcuts();
return; shortcutActive = true;
}
ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts();
if (name == "life") { if (name == "life") {
shortcutActive = true; aSet->setShortcuts(sc.getShortcut("Player/aSet"));
aSet->setShortcuts(shortcuts.getShortcut("Player/aSet")); aDec->setShortcuts(sc.getShortcut("Player/aDec"));
aDec->setShortcuts(shortcuts.getShortcut("Player/aDec")); aInc->setShortcuts(sc.getShortcut("Player/aInc"));
aInc->setShortcuts(shortcuts.getShortcut("Player/aInc"));
} else if (useNameForShortcut) { } else if (useNameForShortcut) {
shortcutActive = true; aSet->setShortcuts(sc.getShortcut("Player/aSetCounter_" + name));
aSet->setShortcuts(shortcuts.getShortcut("Player/aSetCounter_" + name)); aDec->setShortcuts(sc.getShortcut("Player/aDecCounter_" + name));
aDec->setShortcuts(shortcuts.getShortcut("Player/aDecCounter_" + name)); aInc->setShortcuts(sc.getShortcut("Player/aIncCounter_" + name));
aInc->setShortcuts(shortcuts.getShortcut("Player/aIncCounter_" + name));
} }
} }
@ -128,45 +125,32 @@ void AbstractCounter::refreshShortcuts()
} }
} }
void AbstractCounter::setValue(int _value)
{
value = _value;
update();
}
void AbstractCounter::mousePressEvent(QGraphicsSceneMouseEvent *event) void AbstractCounter::mousePressEvent(QGraphicsSceneMouseEvent *event)
{ {
if (isUnderMouse() && player->getPlayerInfo()->getLocalOrJudge()) { if (!isUnderMouse() || !player->getPlayerInfo()->getLocalOrJudge()) {
if (event->button() == Qt::MiddleButton || (QApplication::keyboardModifiers() & Qt::ShiftModifier)) { event->ignore();
if (menu) { return;
menu->exec(event->screenPos()); }
}
event->accept(); if (event->button() == Qt::MiddleButton || QApplication::keyboardModifiers() & Qt::ShiftModifier) {
} else if (event->button() == Qt::LeftButton) { if (menu) {
Command_IncCounter cmd; menu->exec(event->screenPos());
cmd.set_counter_id(id);
cmd.set_delta(1);
player->getPlayerActions()->sendGameCommand(cmd);
event->accept();
} else if (event->button() == Qt::RightButton) {
Command_IncCounter cmd;
cmd.set_counter_id(id);
cmd.set_delta(-1);
player->getPlayerActions()->sendGameCommand(cmd);
event->accept();
} }
} else { } else {
event->ignore(); Command_IncCounter cmd;
cmd.set_counter_id(id);
cmd.set_delta(event->button() == Qt::LeftButton ? 1 : -1);
player->getPlayerActions()->sendGameCommand(cmd);
} }
event->accept();
} }
void AbstractCounter::hoverEnterEvent(QGraphicsSceneHoverEvent * /*event*/) void AbstractCounter::hoverEnterEvent(QGraphicsSceneHoverEvent *)
{ {
hovered = true; hovered = true;
update(); update();
} }
void AbstractCounter::hoverLeaveEvent(QGraphicsSceneHoverEvent *)
void AbstractCounter::hoverLeaveEvent(QGraphicsSceneHoverEvent * /*event*/)
{ {
hovered = false; hovered = false;
update(); update();
@ -174,35 +158,36 @@ void AbstractCounter::hoverLeaveEvent(QGraphicsSceneHoverEvent * /*event*/)
void AbstractCounter::incrementCounter() void AbstractCounter::incrementCounter()
{ {
const int delta = static_cast<QAction *>(sender())->data().toInt();
Command_IncCounter cmd; Command_IncCounter cmd;
cmd.set_counter_id(id); cmd.set_counter_id(id);
cmd.set_delta(delta); cmd.set_delta(static_cast<QAction *>(sender())->data().toInt());
player->getPlayerActions()->sendGameCommand(cmd); player->getPlayerActions()->sendGameCommand(cmd);
} }
void AbstractCounter::setCounter() void AbstractCounter::setCounter()
{ {
QWidget *parent = nullptr;
if (auto *view = scene() ? scene()->views().value(0) : nullptr) {
parent = view->window();
}
dialogSemaphore = true; dialogSemaphore = true;
AbstractCounterDialog dialog(name, QString::number(value), player->getGame()->getTab()); AbstractCounterDialog dlg(name, QString::number(value), parent);
const int ok = dialog.exec(); const int ok = dlg.exec();
dialogSemaphore = false;
if (deleteAfterDialog) { if (deleteAfterDialog) {
deleteLater(); deleteLater();
return; return;
} }
dialogSemaphore = false;
if (!ok) { if (!ok) {
return; return;
} }
Expression exp(value); Expression exp(value);
int newValue = static_cast<int>(exp.parse(dialog.textValue()));
Command_SetCounter cmd; Command_SetCounter cmd;
cmd.set_counter_id(id); cmd.set_counter_id(id);
cmd.set_value(newValue); cmd.set_value(static_cast<int>(exp.parse(dlg.textValue())));
player->getPlayerActions()->sendGameCommand(cmd); player->getPlayerActions()->sendGameCommand(cmd);
} }

View file

@ -1,19 +1,20 @@
/** /**
* @file abstract_counter.h * @file abstract_counter.h
* @ingroup GameGraphicsPlayers * @ingroup GameGraphicsPlayers
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef COUNTER_H #ifndef COUNTER_H
#define COUNTER_H #define COUNTER_H
#include "../../game/board/counter_state.h"
#include "../../interface/widgets/menus/tearoff_menu.h" #include "../../interface/widgets/menus/tearoff_menu.h"
#include "../player/menu/abstract_player_component.h" #include "../player/menu/abstract_player_component.h"
#include <QGraphicsItem> #include <QGraphicsItem>
#include <QInputDialog> #include <QInputDialog>
class Player; class PlayerLogic;
class QAction; class QAction;
class QKeyEvent; class QKeyEvent;
class QMenu; class QMenu;
@ -25,22 +26,26 @@ class AbstractCounter : public QObject, public QGraphicsItem, public AbstractPla
Q_INTERFACES(QGraphicsItem) Q_INTERFACES(QGraphicsItem)
protected: protected:
Player *player; PlayerLogic *player;
int id; int id;
QString name; QString name;
int value; int value;
bool useNameForShortcut, hovered; QColor color;
int radius;
bool hovered = false;
bool useNameForShortcut;
void mousePressEvent(QGraphicsSceneMouseEvent *event) override; void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override; void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override; void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;
private: private:
QAction *aSet, *aDec, *aInc; QAction *aSet = nullptr, *aDec = nullptr, *aInc = nullptr;
TearOffMenu *menu; TearOffMenu *menu = nullptr;
bool dialogSemaphore, deleteAfterDialog; bool dialogSemaphore = false;
bool deleteAfterDialog = false;
bool shownInCounterArea; bool shownInCounterArea;
bool shortcutActive; bool shortcutActive = false;
private slots: private slots:
void refreshShortcuts(); void refreshShortcuts();
@ -48,17 +53,14 @@ private slots:
void setCounter(); void setCounter();
public: public:
AbstractCounter(Player *_player, AbstractCounter(CounterState *state,
int _id, PlayerLogic *player,
const QString &_name, bool shownInCounterArea,
bool _shownInCounterArea, bool useNameForShortcut = false,
int _value,
bool _useNameForShortcut = false,
QGraphicsItem *parent = nullptr); QGraphicsItem *parent = nullptr);
~AbstractCounter() override; ~AbstractCounter() override;
void retranslateUi() override; void retranslateUi() override;
void setValue(int _value);
void setShortcutsActive() override; void setShortcutsActive() override;
void setShortcutsInactive() override; void setShortcutsInactive() override;
void delCounter(); void delCounter();
@ -67,7 +69,6 @@ public:
{ {
return menu; return menu;
} }
int getId() const int getId() const
{ {
return id; return id;
@ -76,14 +77,22 @@ public:
{ {
return name; return name;
} }
bool getShownInCounterArea() const QColor getColor() const
{ {
return shownInCounterArea; return color;
}
int getRadius() const
{
return radius;
} }
int getValue() const int getValue() const
{ {
return value; return value;
} }
bool getShownInCounterArea() const
{
return shownInCounterArea;
}
}; };
class AbstractCounterDialog : public QInputDialog class AbstractCounterDialog : public QInputDialog

View file

@ -1,8 +1,8 @@
/** /**
* @file abstract_graphics_item.h * @file abstract_graphics_item.h
* @ingroup GameGraphics * @ingroup GameGraphics
* @brief TODO: Document this.
*/ */
//! \todo Document this file.
#ifndef ABSTRACTGRAPHICSITEM_H #ifndef ABSTRACTGRAPHICSITEM_H
#define ABSTRACTGRAPHICSITEM_H #define ABSTRACTGRAPHICSITEM_H

View file

@ -2,8 +2,8 @@
#include "arrow_item.h" #include "arrow_item.h"
#include "../../client/settings/cache_settings.h" #include "../../client/settings/cache_settings.h"
#include "../player/player.h" #include "../../game/player/player_actions.h"
#include "../player/player_actions.h" #include "../../game/player/player_logic.h"
#include "../player/player_target.h" #include "../player/player_target.h"
#include "../z_values.h" #include "../z_values.h"
#include "../zones/card_zone.h" #include "../zones/card_zone.h"
@ -21,17 +21,24 @@
#include <libcockatrice/utility/color.h> #include <libcockatrice/utility/color.h>
#include <libcockatrice/utility/zone_names.h> #include <libcockatrice/utility/zone_names.h>
ArrowItem::ArrowItem(Player *_player, int _id, ArrowTarget *_startItem, ArrowTarget *_targetItem, const QColor &_color) ArrowItem::ArrowItem(QSharedPointer<const ArrowData> _data, ArrowTarget *_startItem, ArrowTarget *_targetItem)
: QGraphicsItem(), player(_player), id(_id), startItem(_startItem), targetItem(_targetItem), targetLocked(false), : data(std::move(_data)), startItem(_startItem), targetItem(_targetItem)
color(_color), fullColor(true)
{ {
setZValue(ZValues::ARROWS); setZValue(ZValues::ARROWS);
auto doUpdate = [this]() {
if (startItem && targetItem) {
updatePath();
}
};
if (startItem) { if (startItem) {
startItem->addArrowFrom(this); connect(startItem, &ArrowTarget::scenePositionChanged, this, doUpdate);
connect(startItem, &QObject::destroyed, this, &ArrowItem::onTargetDestroyed);
} }
if (targetItem) { if (targetItem) {
targetItem->addArrowTo(this); connect(targetItem, &ArrowTarget::scenePositionChanged, this, doUpdate);
connect(targetItem, &QObject::destroyed, this, &ArrowItem::onTargetDestroyed);
} }
if (startItem && targetItem) { if (startItem && targetItem) {
@ -39,24 +46,16 @@ ArrowItem::ArrowItem(Player *_player, int _id, ArrowTarget *_startItem, ArrowTar
} }
} }
ArrowItem::~ArrowItem() void ArrowItem::onTargetDestroyed()
{ {
emit requestDeletion(data->creatorId, data->id);
} }
void ArrowItem::delArrow() void ArrowItem::delArrow()
{ {
if (startItem) {
startItem->removeArrowFrom(this);
startItem = 0;
}
if (targetItem) { if (targetItem) {
targetItem->setBeingPointedAt(false); targetItem->setBeingPointedAt(false);
targetItem->removeArrowTo(this);
targetItem = 0;
} }
player->removeArrow(this);
deleteLater(); deleteLater();
} }
@ -127,7 +126,7 @@ void ArrowItem::updatePath(const QPointF &endPoint)
void ArrowItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) void ArrowItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
{ {
QColor paintColor(color); QColor paintColor(data->color);
if (fullColor) { if (fullColor) {
paintColor.setAlpha(200); paintColor.setAlpha(200);
} else { } else {
@ -139,13 +138,12 @@ void ArrowItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*opti
void ArrowItem::mousePressEvent(QGraphicsSceneMouseEvent *event) void ArrowItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{ {
if (!player->getPlayerInfo()->getLocal()) { if (!data->isLocalCreator) {
event->ignore(); event->ignore();
return; return;
} }
QList<QGraphicsItem *> colliding = scene()->items(event->scenePos()); for (auto *item : scene()->items(event->scenePos())) {
for (QGraphicsItem *item : colliding) {
if (qgraphicsitem_cast<CardItem *>(item)) { if (qgraphicsitem_cast<CardItem *>(item)) {
event->ignore(); event->ignore();
return; return;
@ -154,66 +152,72 @@ void ArrowItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
event->accept(); event->accept();
if (event->button() == Qt::RightButton) { if (event->button() == Qt::RightButton) {
Command_DeleteArrow cmd; emit requestDeletion(data->creatorId, data->id);
cmd.set_arrow_id(id);
player->getPlayerActions()->sendGameCommand(cmd);
} }
} }
ArrowDragItem::ArrowDragItem(Player *_owner, ArrowTarget *_startItem, const QColor &_color, int _deleteInPhase) // ArrowDragItem
: ArrowItem(_owner, -1, _startItem, 0, _color), deleteInPhase(_deleteInPhase)
ArrowDragItem::ArrowDragItem(PlayerLogic *_owner, ArrowTarget *_startItem, const QColor &_color, int _deleteInPhase)
: ArrowItem(QSharedPointer<ArrowData>::create(ArrowData{.creatorId = _owner->getPlayerInfo()->getId(),
.isLocalCreator = true,
.id = -1,
.color = _color}),
_startItem,
nullptr),
player(_owner), deleteInPhase(_deleteInPhase)
{ {
} }
void ArrowDragItem::addChildArrow(ArrowDragItem *childArrow) void ArrowDragItem::addChildArrow(ArrowDragItem *child)
{ {
childArrows.append(childArrow); childArrows.append(child);
} }
void ArrowDragItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) void ArrowDragItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{ {
// This ensures that if a mouse move event happens after a call to delArrow(),
// the event will be discarded as it would create some stray pointers.
if (targetLocked || !startItem) { if (targetLocked || !startItem) {
return; return;
} }
QPointF endPos = event->scenePos(); const QPointF endPos = event->scenePos();
QList<QGraphicsItem *> colliding = scene()->items(endPos); ArrowTarget *cursorItem = nullptr;
ArrowTarget *cursorItem = 0;
qreal cursorItemZ = -1; qreal cursorItemZ = -1;
for (int i = colliding.size() - 1; i >= 0; i--) { for (auto *item : scene()->items(endPos)) {
if (qgraphicsitem_cast<PlayerTarget *>(colliding.at(i)) || qgraphicsitem_cast<CardItem *>(colliding.at(i))) { ArrowTarget *candidate = nullptr;
if (colliding.at(i)->zValue() > cursorItemZ) { if (auto *card = qgraphicsitem_cast<CardItem *>(item)) {
cursorItem = static_cast<ArrowTarget *>(colliding.at(i)); candidate = card;
cursorItemZ = cursorItem->zValue(); } else if (auto *pt = qgraphicsitem_cast<PlayerTarget *>(item)) {
} candidate = pt;
}
if (candidate && candidate->zValue() > cursorItemZ) {
cursorItem = candidate;
cursorItemZ = candidate->zValue();
} }
} }
if ((cursorItem != targetItem) && targetItem) { if (cursorItem != targetItem) {
targetItem->setBeingPointedAt(false); if (targetItem) {
targetItem->removeArrowTo(this); disconnect(positionConnection);
} targetItem->setBeingPointedAt(false);
if (!cursorItem) { }
fullColor = false;
targetItem = 0; targetItem = cursorItem;
updatePath(endPos); fullColor = (cursorItem != nullptr);
} else {
if (cursorItem != targetItem) { if (cursorItem && cursorItem != startItem) {
fullColor = true; cursorItem->setBeingPointedAt(true);
if (cursorItem != startItem) { positionConnection =
cursorItem->setBeingPointedAt(true); connect(cursorItem, &ArrowTarget::scenePositionChanged, this, [this]() { updatePath(); });
cursorItem->addArrowTo(this);
}
targetItem = cursorItem;
} }
updatePath();
} }
targetItem ? updatePath() : updatePath(endPos);
update(); update();
for (ArrowDragItem *child : childArrows) { for (auto *child : childArrows) {
child->mouseMoveEvent(event); child->mouseMoveEvent(event);
} }
} }
@ -224,27 +228,33 @@ void ArrowDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
return; return;
} }
if (targetItem && (targetItem != startItem)) { if (targetItem && targetItem != startItem) {
CardZoneLogic *startZone = static_cast<CardItem *>(startItem)->getZone(); CardItem *startCard = qgraphicsitem_cast<CardItem *>(startItem);
// For now, we can safely assume that the start item is always a card. // For now, we can safely assume that the start item is always a card.
// The target item can be a player as well. // The target item can be a player as well.
CardItem *startCard = qgraphicsitem_cast<CardItem *>(startItem); if (!startCard) {
CardItem *targetCard = qgraphicsitem_cast<CardItem *>(targetItem); delArrow();
return;
}
CardZoneLogic *startZone = startCard->getZone();
Command_CreateArrow cmd; Command_CreateArrow cmd;
cmd.mutable_arrow_color()->CopyFrom(convertQColorToColor(color)); cmd.mutable_arrow_color()->CopyFrom(convertQColorToColor(data->color));
cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId()); cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId());
cmd.set_start_zone(startZone->getName().toStdString()); cmd.set_start_zone(startZone->getName().toStdString());
cmd.set_start_card_id(startCard->getId()); cmd.set_start_card_id(startCard->getId());
if (targetCard) { if (auto *targetCard = qgraphicsitem_cast<CardItem *>(targetItem)) {
CardZoneLogic *targetZone = targetCard->getZone(); CardZoneLogic *targetZone = targetCard->getZone();
cmd.set_target_player_id(targetZone->getPlayer()->getPlayerInfo()->getId()); cmd.set_target_player_id(targetZone->getPlayer()->getPlayerInfo()->getId());
cmd.set_target_zone(targetZone->getName().toStdString()); cmd.set_target_zone(targetZone->getName().toStdString());
cmd.set_target_card_id(targetCard->getId()); cmd.set_target_card_id(targetCard->getId());
} else { // failed to cast target to card, this means it's a player } else if (auto *targetPlayer = qgraphicsitem_cast<PlayerTarget *>(targetItem)) {
PlayerTarget *targetPlayer = qgraphicsitem_cast<PlayerTarget *>(targetItem);
cmd.set_target_player_id(targetPlayer->getOwner()->getPlayerInfo()->getId()); cmd.set_target_player_id(targetPlayer->getOwner()->getPlayerInfo()->getId());
} else {
delArrow();
return;
} }
// if the card is in hand then we will move the card to stack or table as part of drawing the arrow // if the card is in hand then we will move the card to stack or table as part of drawing the arrow
@ -267,21 +277,29 @@ void ArrowDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
player->getPlayerActions()->sendGameCommand(cmd); player->getPlayerActions()->sendGameCommand(cmd);
} }
delArrow();
for (ArrowDragItem *child : childArrows) { delArrow();
for (auto *child : childArrows) {
child->mouseReleaseEvent(event); child->mouseReleaseEvent(event);
} }
} }
// ArrowAttachItem
ArrowAttachItem::ArrowAttachItem(ArrowTarget *_startItem) ArrowAttachItem::ArrowAttachItem(ArrowTarget *_startItem)
: ArrowItem(_startItem->getOwner(), -1, _startItem, 0, Qt::green) : ArrowItem(
QSharedPointer<ArrowData>::create(ArrowData{.creatorId = _startItem->getOwner()->getPlayerInfo()->getId(),
.isLocalCreator = true,
.id = -1,
.color = Qt::green}),
_startItem,
nullptr),
player(_startItem->getOwner())
{ {
} }
void ArrowAttachItem::addChildArrow(ArrowAttachItem *childArrow) void ArrowAttachItem::addChildArrow(ArrowAttachItem *child)
{ {
childArrows.append(childArrow); childArrows.append(child);
} }
void ArrowAttachItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) void ArrowAttachItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
@ -290,67 +308,43 @@ void ArrowAttachItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
return; return;
} }
QPointF endPos = event->scenePos(); const QPointF endPos = event->scenePos();
QList<QGraphicsItem *> colliding = scene()->items(endPos); ArrowTarget *cursorItem = nullptr;
ArrowTarget *cursorItem = 0;
qreal cursorItemZ = -1; qreal cursorItemZ = -1;
for (int i = colliding.size() - 1; i >= 0; i--) { for (auto *item : scene()->items(endPos)) {
if (qgraphicsitem_cast<CardItem *>(colliding.at(i))) { if (auto *card = qgraphicsitem_cast<CardItem *>(item)) {
if (colliding.at(i)->zValue() > cursorItemZ) { if (card->zValue() > cursorItemZ) {
cursorItem = static_cast<ArrowTarget *>(colliding.at(i)); cursorItem = card;
cursorItemZ = cursorItem->zValue(); cursorItemZ = card->zValue();
} }
} }
} }
if ((cursorItem != targetItem) && targetItem) { if (cursorItem != targetItem) {
targetItem->setBeingPointedAt(false); if (targetItem) {
} disconnect(positionConnection);
if (!cursorItem) { targetItem->setBeingPointedAt(false);
fullColor = false;
targetItem = 0;
updatePath(endPos);
} else {
fullColor = true;
if (cursorItem != startItem) {
cursorItem->setBeingPointedAt(true);
} }
targetItem = cursorItem; targetItem = cursorItem;
updatePath(); fullColor = (cursorItem != nullptr);
if (cursorItem && cursorItem != startItem) {
cursorItem->setBeingPointedAt(true);
positionConnection =
connect(cursorItem, &ArrowTarget::scenePositionChanged, this, [this]() { updatePath(); });
}
} }
targetItem ? updatePath() : updatePath(endPos);
update(); update();
for (ArrowAttachItem *child : childArrows) { for (auto *child : childArrows) {
child->mouseMoveEvent(event); child->mouseMoveEvent(event);
} }
} }
void ArrowAttachItem::attachCards(CardItem *startCard, const CardItem *targetCard)
{
// do nothing if target is already attached to another card or is not in play
if (targetCard->getAttachedTo() || targetCard->getZone()->getName() != ZoneNames::TABLE) {
return;
}
CardZoneLogic *startZone = startCard->getZone();
CardZoneLogic *targetZone = targetCard->getZone();
// move card onto table first if attaching from some other zone
if (startZone->getName() != ZoneNames::TABLE) {
player->getPlayerActions()->playCardToTable(startCard, false);
}
Command_AttachCard cmd;
cmd.set_start_zone(ZoneNames::TABLE);
cmd.set_card_id(startCard->getId());
cmd.set_target_player_id(targetZone->getPlayer()->getPlayerInfo()->getId());
cmd.set_target_zone(targetZone->getName().toStdString());
cmd.set_target_card_id(targetCard->getId());
player->getPlayerActions()->sendGameCommand(cmd);
}
void ArrowAttachItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) void ArrowAttachItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{ {
if (!startItem) { if (!startItem) {
@ -359,21 +353,40 @@ void ArrowAttachItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
// Attaching could move startItem under the current cursor position, causing all children to retarget to it right // Attaching could move startItem under the current cursor position, causing all children to retarget to it right
// before they are processed. Prevent that. // before they are processed. Prevent that.
for (ArrowAttachItem *child : childArrows) { for (auto *child : childArrows) {
child->setTargetLocked(true); child->setTargetLocked(true);
} }
if (targetItem && (targetItem != startItem)) { if (targetItem && targetItem != startItem) {
auto startCard = qgraphicsitem_cast<CardItem *>(startItem); auto *startCard = qgraphicsitem_cast<CardItem *>(startItem);
auto targetCard = qgraphicsitem_cast<CardItem *>(targetItem); auto *targetCard = qgraphicsitem_cast<CardItem *>(targetItem);
if (startCard && targetCard) { if (startCard && targetCard) {
attachCards(startCard, targetCard); attachCards(startCard, targetCard);
} }
} }
delArrow(); delArrow();
for (auto *child : childArrows) {
for (ArrowAttachItem *child : childArrows) {
child->mouseReleaseEvent(event); child->mouseReleaseEvent(event);
} }
} }
void ArrowAttachItem::attachCards(CardItem *startCard, const CardItem *targetCard)
{
if (targetCard->getAttachedTo() || targetCard->getZone()->getName() != ZoneNames::TABLE) {
return;
}
// move card onto table first if attaching from some other zone
if (startCard->getZone()->getName() != ZoneNames::TABLE) {
player->getPlayerActions()->playCardToTable(startCard, false);
}
Command_AttachCard cmd;
cmd.set_start_zone(ZoneNames::TABLE);
cmd.set_card_id(startCard->getId());
cmd.set_target_player_id(targetCard->getZone()->getPlayer()->getPlayerInfo()->getId());
cmd.set_target_zone(targetCard->getZone()->getName().toStdString());
cmd.set_target_card_id(targetCard->getId());
player->getPlayerActions()->sendGameCommand(cmd);
}

View file

@ -1,40 +1,44 @@
/**
* @file arrow_item.h
* @ingroup GameGraphics
* @brief TODO: Document this.
*/
#ifndef ARROWITEM_H #ifndef ARROWITEM_H
#define ARROWITEM_H #define ARROWITEM_H
#include "../../game/board/arrow_data.h"
#include "arrow_target.h"
#include <QGraphicsItem> #include <QGraphicsItem>
#include <QPointer>
#include <QSharedPointer>
class CardItem; class CardItem;
class QGraphicsSceneMouseEvent; class QGraphicsSceneMouseEvent;
class QMenu; class PlayerLogic;
class Player;
class ArrowTarget;
class ArrowItem : public QObject, public QGraphicsItem class ArrowItem : public QObject, public QGraphicsItem
{ {
Q_OBJECT Q_OBJECT
Q_INTERFACES(QGraphicsItem) Q_INTERFACES(QGraphicsItem)
signals:
void requestDeletion(int creatorId, int id);
private: private:
QPainterPath path; QPainterPath path;
QMenu *menu;
protected: protected:
Player *player; QSharedPointer<const ArrowData> data;
int id; QPointer<ArrowTarget> startItem;
ArrowTarget *startItem, *targetItem; QPointer<ArrowTarget> targetItem;
bool targetLocked; bool targetLocked = false;
QColor color; bool fullColor = true;
bool fullColor;
void mousePressEvent(QGraphicsSceneMouseEvent *event) override; void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
public: public:
ArrowItem(Player *_player, int _id, ArrowTarget *_startItem, ArrowTarget *_targetItem, const QColor &color); ArrowItem(QSharedPointer<const ArrowData> _data, ArrowTarget *_startItem, ArrowTarget *_targetItem);
~ArrowItem() override;
void onTargetDestroyed();
void delArrow();
void updatePath();
void updatePath(const QPointF &endPoint);
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
[[nodiscard]] QRectF boundingRect() const override [[nodiscard]] QRectF boundingRect() const override
{ {
@ -44,24 +48,13 @@ public:
{ {
return path; return path;
} }
void updatePath();
void updatePath(const QPointF &endPoint);
[[nodiscard]] int getId() const [[nodiscard]] int getId() const
{ {
return id; return data->id;
} }
[[nodiscard]] Player *getPlayer() const [[nodiscard]] int getCreatorId() const
{ {
return player; return data->creatorId;
}
void setStartItem(ArrowTarget *_item)
{
startItem = _item;
}
void setTargetItem(ArrowTarget *_item)
{
targetItem = _item;
} }
[[nodiscard]] ArrowTarget *getStartItem() const [[nodiscard]] ArrowTarget *getStartItem() const
{ {
@ -75,19 +68,20 @@ public:
{ {
targetLocked = _targetLocked; targetLocked = _targetLocked;
} }
void delArrow();
}; };
class ArrowDragItem : public ArrowItem class ArrowDragItem : public ArrowItem
{ {
Q_OBJECT Q_OBJECT
private: private:
PlayerLogic *player;
int deleteInPhase; int deleteInPhase;
QList<ArrowDragItem *> childArrows; QList<ArrowDragItem *> childArrows;
QMetaObject::Connection positionConnection;
public: public:
ArrowDragItem(Player *_owner, ArrowTarget *_startItem, const QColor &_color, int _deleteInPhase); ArrowDragItem(PlayerLogic *_owner, ArrowTarget *_startItem, const QColor &_color, int _deleteInPhase);
void addChildArrow(ArrowDragItem *childArrow); void addChildArrow(ArrowDragItem *child);
protected: protected:
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
@ -98,17 +92,18 @@ class ArrowAttachItem : public ArrowItem
{ {
Q_OBJECT Q_OBJECT
private: private:
PlayerLogic *player;
QList<ArrowAttachItem *> childArrows; QList<ArrowAttachItem *> childArrows;
QMetaObject::Connection positionConnection;
void attachCards(CardItem *startCard, const CardItem *targetCard); void attachCards(CardItem *startCard, const CardItem *targetCard);
public: public:
explicit ArrowAttachItem(ArrowTarget *_startItem); explicit ArrowAttachItem(ArrowTarget *_startItem);
void addChildArrow(ArrowAttachItem *childArrow); void addChildArrow(ArrowAttachItem *child);
protected: protected:
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
}; };
#endif // ARROWITEM_H #endif

View file

@ -0,0 +1,23 @@
#include "arrow_target.h"
#include "../../game/player/player_logic.h"
#include "arrow_item.h"
ArrowTarget::ArrowTarget(PlayerLogic *_owner, QGraphicsItem *parent) : AbstractGraphicsItem(parent), owner(_owner)
{
setFlag(ItemSendsScenePositionChanges);
}
void ArrowTarget::setBeingPointedAt(bool _beingPointedAt)
{
beingPointedAt = _beingPointedAt;
update();
}
QVariant ArrowTarget::itemChange(GraphicsItemChange change, const QVariant &value)
{
if (change == ItemScenePositionHasChanged) {
emit scenePositionChanged();
}
return AbstractGraphicsItem::itemChange(change, value);
}

View file

@ -0,0 +1,47 @@
/**
* @file arrow_target.h
* @ingroup GameGraphics
*/
//! \todo Document this file.
#ifndef ARROWTARGET_H
#define ARROWTARGET_H
#include "abstract_graphics_item.h"
#include <QList>
class PlayerLogic;
class ArrowItem;
class ArrowTarget : public AbstractGraphicsItem
{
Q_OBJECT
protected:
PlayerLogic *owner;
private:
bool beingPointedAt = false;
signals:
void scenePositionChanged();
public:
explicit ArrowTarget(PlayerLogic *_owner, QGraphicsItem *parent = nullptr);
~ArrowTarget() override = default;
[[nodiscard]] PlayerLogic *getOwner() const
{
return owner;
}
void setBeingPointedAt(bool _beingPointedAt);
[[nodiscard]] bool getBeingPointedAt() const
{
return beingPointedAt;
}
protected:
QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
};
#endif

Some files were not shown because too many files have changed in this diff Show more