Restore the Pro Libertate Games title and splash; list Katan under Board Games
Build and Release APK / Build APK and publish release (push) Canceled after 0s
Build and Release APK / Build APK and publish release (push) Canceled after 0s
The bootstrap rebranded the app to Pilgrims of Katan (WALA-118 review); the decision is to keep the upstream's own identity instead. - Title and splash: the upstream originals restored exactly — the Wallace tartan splash under the "Pro Libertate Games" uncial wordmark (SplashScreen.kt, Tartan.kt), the tartan launcher icon, and the Theme.ProLibertateGames window theme. The game screens go back to the upstream WallaceGold/WallaceRed accents the tartan is woven from. - Names and colours: launcher names back to "Pro Libertate Games" / "Pro Libertate Games DEV"; the channel colours back to the clan red / hunting green setts (resValue tartan_field and friends). The installed identity stays ca.wallacenet.pilgrimsofkatan (+ .dev) — that is the package, not the brand. - Catalogue: Katan is listed under Board Games (3-4 players, original blurb, no third-party marks) with available = false, so dev builds show it as "Coming soon" and a production build hides it until its engine and screen land. - Docs: README/CLAUDE.md/docs/CI.md updated to the restored identity; CatalogTest's invariant now names Katan as the one deliberate exception rather than asserting nothing is unfinished. Structural bootstrap untouched: applicationId, OTA repointed at the Gitea releases, CI workflows, version stamping. Verified: ./gradlew assembleDebug and testDebugUnitTest green — 628 tests, 0 failures. Co-Authored-By: Paperclip <[email protected]>
This commit is contained in:
co-authored by
Paperclip
parent
05c443a4b5
commit
5c797c8807
@@ -1,9 +1,9 @@
|
|||||||
# Working on this repo (agent notes)
|
# Working on this repo (agent notes)
|
||||||
|
|
||||||
Pilgrims of Katan — an Android collection of card and board games, playable
|
ProLibertateGames — Wallacenet's build of an Android collection of card and
|
||||||
against AI or over Wi-Fi (LAN). Bootstrapped from
|
board games, playable against AI or over Wi-Fi (LAN). Upstream:
|
||||||
[adrianoftyriel/ProLibertateGames](https://github.com/adrianoftyriel/ProLibertateGames)
|
[adrianoftyriel/ProLibertateGames](https://github.com/adrianoftyriel/ProLibertateGames)
|
||||||
(GPL-3.0, kept as the `upstream` remote) for Wallacenet. Canonical repo:
|
(GPL-3.0, kept as the `upstream` remote). Canonical repo:
|
||||||
<https://git.wallacenet.ca/devadmin/PilgrimsOfKatan>.
|
<https://git.wallacenet.ca/devadmin/PilgrimsOfKatan>.
|
||||||
|
|
||||||
## Ground rules
|
## Ground rules
|
||||||
@@ -11,13 +11,15 @@ against AI or over Wi-Fi (LAN). Bootstrapped from
|
|||||||
- **Licences travel.** `LICENSE` is GPL-3.0 and must stay. The Uncial Antiqua
|
- **Licences travel.** `LICENSE` is GPL-3.0 and must stay. The Uncial Antiqua
|
||||||
font ships with its OFL licence at `docs/licenses/UncialAntiqua-OFL.txt`;
|
font ships with its OFL licence at `docs/licenses/UncialAntiqua-OFL.txt`;
|
||||||
never delete or edit either. Attribution to the upstream lives in the README.
|
never delete or edit either. Attribution to the upstream lives in the README.
|
||||||
- **No Catan trademarks or brand assets**, and none of the upstream's Wallace
|
- **No Catan trademarks or brand assets** — the one hard line. The app's own
|
||||||
clan branding (it was removed in the rebrand; see README → Branding). The
|
branding is the upstream's, restored (see README → Branding): tartan launcher
|
||||||
brand is: *Pilgrims of Katan*, `ca.wallacenet.pilgrimsofkatan` (+ `.dev`),
|
icon and splash, Wallace clan red on production and hunting green on dev,
|
||||||
slate blue / dev green, hexagon emblem.
|
launcher names *Pro Libertate Games* / *Pro Libertate Games DEV*. The
|
||||||
|
installed identity is `ca.wallacenet.pilgrimsofkatan` (+ `.dev`).
|
||||||
- **The existing catalogue is the regression safety net.** Keep every game
|
- **The existing catalogue is the regression safety net.** Keep every game
|
||||||
green; new games (Catan) arrive as separate tasks, not edits to old ones.
|
green; new games (Katan) arrive as separate tasks, not edits to old ones.
|
||||||
All 22 games are `available = true`.
|
The 22 reference games are `available = true`; Katan is listed with
|
||||||
|
`available = false` (*Coming soon*) until its game task lands.
|
||||||
- **Docs conventions**: every game gets `docs/RULES-<id>.md` (index and gaps in
|
- **Docs conventions**: every game gets `docs/RULES-<id>.md` (index and gaps in
|
||||||
`docs/README.md`); build/CI/release decisions go in `docs/CI.md`. Update the
|
`docs/README.md`); build/CI/release decisions go in `docs/CI.md`. Update the
|
||||||
docs when behaviour changes — they are the check against the rules code.
|
docs when behaviour changes — they are the check against the rules code.
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
# Pilgrims of Katan
|
# ProLibertateGames
|
||||||
|
|
||||||
A collection of card and board games for Android, playable against the computer
|
A collection of card and board games for Android, playable against the computer
|
||||||
or against other people over Wi-Fi (LAN).
|
or against other people over Wi-Fi (LAN).
|
||||||
|
|
||||||
Derived from [ProLibertateGames](https://github.com/adrianoftyriel/ProLibertateGames)
|
Wallacenet's build of [ProLibertateGames](https://github.com/adrianoftyriel/ProLibertateGames)
|
||||||
by adrianoftyriel, used under the GNU GPL v3 — see [LICENSE](LICENSE). Game
|
by adrianoftyriel (GNU GPL v3 — see [LICENSE](LICENSE)), hosted at
|
||||||
rules documentation lives under [docs/](docs/README.md); build, CI and release
|
<https://git.wallacenet.ca/devadmin/PilgrimsOfKatan>. Game rules documentation
|
||||||
decisions live in [docs/CI.md](docs/CI.md). Catan itself arrives in later work;
|
lives under [docs/](docs/README.md); build, CI and release decisions live in
|
||||||
this app ships the reference catalogue unchanged as the regression safety net.
|
[docs/CI.md](docs/CI.md). Katan itself arrives in later work — it is listed under
|
||||||
|
Board Games as *Coming soon* — and this app ships the reference catalogue
|
||||||
|
unchanged as the regression safety net.
|
||||||
|
|
||||||
## What works today
|
## What works today
|
||||||
|
|
||||||
@@ -16,15 +18,15 @@ The app shell is complete and every game in the catalogue is playable end to end
|
|||||||
| | |
|
| | |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| **Cards** | Euchre, Kaiser, President, Golf, Wizard, Crazy 8s, Cribbage, Hearts, Klondike, FreeCell, Spider, Pyramid |
|
| **Cards** | Euchre, Kaiser, President, Golf, Wizard, Crazy 8s, Cribbage, Hearts, Klondike, FreeCell, Spider, Pyramid |
|
||||||
| **Table** | Sequence, Chess, Checkers, Backgammon, Mastermind, Nine Men's Morris, Peg Solitaire, Yahtzee, Pirates and Bulgars, Ta Yü |
|
| **Table** | Sequence, Chess, Checkers, Backgammon, Mastermind, Nine Men's Morris, Peg Solitaire, Yahtzee, Pirates and Bulgars, Ta Yü, Katan *(coming soon)* |
|
||||||
|
|
||||||
Nothing is marked *Coming soon* at the moment, because nothing is waiting. The
|
One entry is marked *Coming soon*: Katan, the island game, whose engine and
|
||||||
machinery for it is still there and still tested: a game listed in `GameCatalog`
|
screen land with their own task. The machinery is still there and still tested:
|
||||||
with `available = false` appears in the menu marked *Coming soon* **on dev builds
|
a game listed in `GameCatalog` with `available = false` appears in the menu
|
||||||
only**, so a production release lists just what can actually be played and nobody
|
marked *Coming soon* **on dev builds only**, so a production release lists just
|
||||||
installs it and taps into a dead end. The menu decides this from the installed
|
what can actually be played and nobody installs it and taps into a dead end. The
|
||||||
APK's own version name — the same source the updater uses to know which channel
|
menu decides this from the installed APK's own version name — the same source
|
||||||
it is on.
|
the updater uses to know which channel it is on.
|
||||||
|
|
||||||
Adding a game means writing a `GameRules` implementation, a `GameAi` and a
|
Adding a game means writing a `GameRules` implementation, a `GameAi` and a
|
||||||
screen, then flipping `available` — the menu, lobby, networking, settings and OTA
|
screen, then flipping `available` — the menu, lobby, networking, settings and OTA
|
||||||
@@ -135,21 +137,21 @@ it: every rule above is a pure function and unit-tested on the JVM.
|
|||||||
|
|
||||||
## Branding
|
## Branding
|
||||||
|
|
||||||
Neutral, original, and deliberately *not* Catan's: no third-party marks or
|
The app wears the upstream's own branding, restored (WALA-118 review): the
|
||||||
trade dress ship in this app (rebrand scope: WALA-118). The launcher icon is a
|
Wallace tartan is the launcher icon and the splash, drawn as vector drawables and
|
||||||
hexagonal ring around a solid core — a game tile, not any game's logo — drawn
|
Compose code so it stays sharp at every density and needs no PNGs. The app opens
|
||||||
as vector drawables so it stays sharp at every density and needs no PNGs. The
|
on the tartan held under the name, set in a Celtic uncial hand for two seconds
|
||||||
app opens on the brand field fading to its deep shade, the name set in a Celtic
|
before it fades into the menu.
|
||||||
uncial hand for two seconds before it fades into the menu.
|
|
||||||
|
|
||||||
A dev build wears the **dev green** field where production wears **slate
|
A dev build wears the **hunting green** sett where production wears the **clan
|
||||||
blue**, and installs as *Pilgrims of Katan DEV*. That is what tells the two
|
red**, and installs as *Pro Libertate Games DEV*. That is what tells the two
|
||||||
copies apart when both are on the phone; see
|
copies apart when both are on the phone; see
|
||||||
[Both channels on one device](#both-channels-on-one-device). The palette lives
|
[Both channels on one device](#both-channels-on-one-device). The tartan is drawn
|
||||||
in `ui/theme/Theme.kt` (`BrandField`, `BrandDeep`, `BrandAccent`) and the two
|
by `ui/theme/Tartan.kt` (`TartanBackground`, `threadWidthFor`) off
|
||||||
channel colours are generated per build by `resValue` in `app/build.gradle.kts`
|
`BuildConfig.DEV_BUILD`; the matching vector colours are generated per build by
|
||||||
— the icon vectors reference `@color/brand_field` rather than a literal, which
|
`resValue` in `app/build.gradle.kts` — the icon vectors reference
|
||||||
is how one set of vectors serves both colourways.
|
`@color/tartan_field` rather than a literal, which is how one set of vectors
|
||||||
|
serves both colourways.
|
||||||
|
|
||||||
Two honest caveats:
|
Two honest caveats:
|
||||||
|
|
||||||
@@ -158,9 +160,10 @@ Two honest caveats:
|
|||||||
the SIL Open Font License 1.1. The full licence is at
|
the SIL Open Font License 1.1. The full licence is at
|
||||||
[docs/licenses/UncialAntiqua-OFL.txt](docs/licenses/UncialAntiqua-OFL.txt) and
|
[docs/licenses/UncialAntiqua-OFL.txt](docs/licenses/UncialAntiqua-OFL.txt) and
|
||||||
must stay with the font — the OFL requires it to be distributed alongside.
|
must stay with the font — the OFL requires it to be distributed alongside.
|
||||||
- **The upstream's clan tartan is gone; its code is not.** The Wallace tartan
|
- **Katan is the one place this app deliberately diverges.** No Catan
|
||||||
was the upstream's brand and left with the rebrand. The GPL-licensed history
|
trademarks or brand assets ship anywhere in it; the island game is listed
|
||||||
that produced this app remains in git, and the source stays at
|
under its own name with original art only. The GPL-licensed history that
|
||||||
|
produced this app remains in git, and the source stays at
|
||||||
[adrianoftyriel/ProLibertateGames](https://github.com/adrianoftyriel/ProLibertateGames)
|
[adrianoftyriel/ProLibertateGames](https://github.com/adrianoftyriel/ProLibertateGames)
|
||||||
as the `upstream` remote.
|
as the `upstream` remote.
|
||||||
|
|
||||||
@@ -187,10 +190,10 @@ smaller than a fingertip.
|
|||||||
Requires JDK 17 and the Android SDK (compileSdk 34).
|
Requires JDK 17 and the Android SDK (compileSdk 34).
|
||||||
|
|
||||||
A local build is a dev build: it installs as
|
A local build is a dev build: it installs as
|
||||||
`ca.wallacenet.pilgrimsofkatan.dev` under the name *Pilgrims of Katan DEV* on
|
`ca.wallacenet.pilgrimsofkatan.dev` under the name *Pro Libertate Games DEV* on
|
||||||
the dev green field, so it goes on beside a production copy rather than over it.
|
the hunting-green sett, so it goes on beside a production copy rather than over
|
||||||
Set `PLG_CHANNEL=production` to build the production package
|
it. Set `PLG_CHANNEL=production` to build the production package
|
||||||
(`ca.wallacenet.pilgrimsofkatan`, slate blue).
|
(`ca.wallacenet.pilgrimsofkatan`, clan red).
|
||||||
|
|
||||||
## CI
|
## CI
|
||||||
|
|
||||||
@@ -271,8 +274,8 @@ Three things move together, all off `isDevBuild` in `app/build.gradle.kts`:
|
|||||||
| | Production | Dev |
|
| | Production | Dev |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `applicationId` | `ca.wallacenet.pilgrimsofkatan` | `ca.wallacenet.pilgrimsofkatan.dev` |
|
| `applicationId` | `ca.wallacenet.pilgrimsofkatan` | `ca.wallacenet.pilgrimsofkatan.dev` |
|
||||||
| Launcher name | Pilgrims of Katan | Pilgrims of Katan DEV |
|
| Launcher name | Pro Libertate Games | Pro Libertate Games DEV |
|
||||||
| Brand field | slate blue | deep green |
|
| Tartan sett | clan red | hunting green |
|
||||||
|
|
||||||
Anything that is not an explicit `PLG_CHANNEL=production` build is a dev build,
|
Anything that is not an explicit `PLG_CHANNEL=production` build is a dev build,
|
||||||
so a local `assembleDebug` is a dev build too and installs alongside a release
|
so a local `assembleDebug` is a dev build too and installs alongside a release
|
||||||
|
|||||||
+17
-15
@@ -40,9 +40,9 @@ val versionSeries = (project.findProperty("plgVersionSeries") as String?) ?: "1.
|
|||||||
//
|
//
|
||||||
// This one flag drives everything that makes a dev build a separate app rather
|
// This one flag drives everything that makes a dev build a separate app rather
|
||||||
// than a replacement for the production one — its own applicationId, its own
|
// than a replacement for the production one — its own applicationId, its own
|
||||||
// launcher name, and its own brand colour. They have to move together: a dev
|
// launcher name, and the hunting sett in place of the clan sett. They have to
|
||||||
// package wearing the production name and colours is exactly the confusion
|
// move together: a dev package wearing the production name and colours is
|
||||||
// having both installed is meant to avoid.
|
// exactly the confusion having both installed is meant to avoid.
|
||||||
val isDevBuild = buildChannel != "production"
|
val isDevBuild = buildChannel != "production"
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@@ -83,19 +83,21 @@ android {
|
|||||||
resValue(
|
resValue(
|
||||||
"string",
|
"string",
|
||||||
"app_name",
|
"app_name",
|
||||||
if (isDevBuild) "Pilgrims of Katan DEV" else "Pilgrims of Katan",
|
if (isDevBuild) "Pro Libertate Games DEV" else "Pro Libertate Games",
|
||||||
)
|
)
|
||||||
|
|
||||||
// The one colour that separates the two installs: slate blue on
|
// The one colour that separates the two setts: Wallace red for
|
||||||
// production, deep green on dev. The launcher icon and the splash are
|
// production, Wallace Hunting green for dev. The icon vectors reference
|
||||||
// both built from it, so the drawer shows at a glance which of the two
|
// this rather than a literal, and Tartan.kt picks the matching Compose
|
||||||
// copies is which — the two have to stay in step or the icon and the
|
// colour off DEV_BUILD below — the two have to stay in step or the
|
||||||
// splash it opens on would be different brands.
|
// launcher icon and the splash it opens on would be different cloth.
|
||||||
resValue("color", "brand_field", if (isDevBuild) "#2A6B54" else "#2B4A78")
|
resValue("color", "tartan_field", if (isDevBuild) "#1E7A3C" else "#E01B24")
|
||||||
// A darker shade of the same hue, held behind the window for the
|
// Shades of the same field, so a launch does not flash the wrong colour
|
||||||
// instant before the splash draws, so a launch never flashes the wrong
|
// before the tartan is drawn: splash_background is held behind the
|
||||||
// colour.
|
// window for that instant, ic_launcher_background is the icon's flat
|
||||||
resValue("color", "splash_background", if (isDevBuild) "#153B2F" else "#182A44")
|
// fallback.
|
||||||
|
resValue("color", "splash_background", if (isDevBuild) "#0D2E17" else "#4A0B10")
|
||||||
|
resValue("color", "ic_launcher_background", if (isDevBuild) "#145C2D" else "#B3121B")
|
||||||
|
|
||||||
buildConfigField("boolean", "DEV_BUILD", isDevBuild.toString())
|
buildConfigField("boolean", "DEV_BUILD", isDevBuild.toString())
|
||||||
|
|
||||||
@@ -131,7 +133,7 @@ android {
|
|||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
compose = true
|
compose = true
|
||||||
// For DEV_BUILD, which is how the Compose splash picks its brand colour.
|
// For DEV_BUILD, which is how the Compose tartan picks its sett.
|
||||||
buildConfig = true
|
buildConfig = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.PilgrimsOfKatan">
|
android:theme="@style/Theme.ProLibertateGames">
|
||||||
|
|
||||||
<!-- No fixed orientation: the UI adapts to portrait, landscape and
|
<!-- No fixed orientation: the UI adapts to portrait, landscape and
|
||||||
tablet widths. configChanges keeps Compose state across rotation
|
tablet widths. configChanges keeps Compose state across rotation
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import org.prolibertate.games.score.ScorekeeperRepository
|
|||||||
import org.prolibertate.games.settings.SettingsRepository
|
import org.prolibertate.games.settings.SettingsRepository
|
||||||
import org.prolibertate.games.ui.AppEnv
|
import org.prolibertate.games.ui.AppEnv
|
||||||
import org.prolibertate.games.ui.AppRoot
|
import org.prolibertate.games.ui.AppRoot
|
||||||
import org.prolibertate.games.ui.theme.PilgrimsTheme
|
import org.prolibertate.games.ui.theme.ProLibertateTheme
|
||||||
import org.prolibertate.games.update.Updater
|
import org.prolibertate.games.update.Updater
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
PilgrimsTheme {
|
ProLibertateTheme {
|
||||||
AppRoot(env)
|
AppRoot(env)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,6 +94,13 @@ object GameCatalog {
|
|||||||
const val MORRIS = "morris"
|
const val MORRIS = "morris"
|
||||||
const val PIRATES = "pirates"
|
const val PIRATES = "pirates"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The island game the collection is named for. Listed under Board Games
|
||||||
|
* and marked *Coming soon*; the engine and its screen land with the game
|
||||||
|
* task, and `available` flips on once two-device play is verified.
|
||||||
|
*/
|
||||||
|
const val CATAN = "catan"
|
||||||
|
|
||||||
val all: List<GameDescriptor> = listOf(
|
val all: List<GameDescriptor> = listOf(
|
||||||
GameDescriptor(
|
GameDescriptor(
|
||||||
id = EUCHRE,
|
id = EUCHRE,
|
||||||
@@ -333,6 +340,19 @@ object GameCatalog {
|
|||||||
"because your score multiplies them.",
|
"because your score multiplies them.",
|
||||||
available = true,
|
available = true,
|
||||||
),
|
),
|
||||||
|
GameDescriptor(
|
||||||
|
id = CATAN,
|
||||||
|
title = "Katan",
|
||||||
|
menus = setOf(GameMenu.BOARD),
|
||||||
|
minPlayers = 3,
|
||||||
|
maxPlayers = 4,
|
||||||
|
teamBased = false,
|
||||||
|
blurb = "Trade, build, and settle the island.",
|
||||||
|
// Listed, not playable: the engine and screen land with the Katan
|
||||||
|
// game task. Dev builds show the entry as *Coming soon*; a
|
||||||
|
// production build lists only what can actually be played.
|
||||||
|
available = false,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
fun byId(id: String): GameDescriptor? = all.firstOrNull { it.id == id }
|
fun byId(id: String): GameDescriptor? = all.firstOrNull { it.id == id }
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import androidx.compose.animation.core.animateFloatAsState
|
|||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@@ -19,7 +20,6 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.compose.ui.graphics.Brush
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.Shadow
|
import androidx.compose.ui.graphics.Shadow
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
@@ -30,9 +30,8 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import org.prolibertate.games.R
|
import org.prolibertate.games.R
|
||||||
import org.prolibertate.games.ui.theme.BrandAccent
|
import org.prolibertate.games.ui.theme.TartanBackground
|
||||||
import org.prolibertate.games.ui.theme.BrandDeep
|
import org.prolibertate.games.ui.theme.threadWidthFor
|
||||||
import org.prolibertate.games.ui.theme.BrandField
|
|
||||||
|
|
||||||
/** How long the splash holds at full opacity before fading. */
|
/** How long the splash holds at full opacity before fading. */
|
||||||
private const val SPLASH_HOLD_MILLIS = 2_000L
|
private const val SPLASH_HOLD_MILLIS = 2_000L
|
||||||
@@ -46,11 +45,7 @@ private const val SPLASH_FADE_MILLIS = 600
|
|||||||
val CelticFontFamily = FontFamily(Font(R.font.uncial_antiqua))
|
val CelticFontFamily = FontFamily(Font(R.font.uncial_antiqua))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The launch screen: the brand field under the name of the game.
|
* The launch screen: Wallace tartan under the name of the house.
|
||||||
*
|
|
||||||
* The gradient runs from the same hue as the window's `splash_background`
|
|
||||||
* held behind it, so the first frame and the splash are one brand in two
|
|
||||||
* shades rather than a colour change.
|
|
||||||
*
|
*
|
||||||
* Drawn as an overlay rather than a separate destination, so the menu is
|
* Drawn as an overlay rather than a separate destination, so the menu is
|
||||||
* already composed and ready behind it and the fade reveals a live screen
|
* already composed and ready behind it and the fade reveals a live screen
|
||||||
@@ -75,17 +70,30 @@ fun SplashScreen(
|
|||||||
onFinished()
|
onFinished()
|
||||||
}
|
}
|
||||||
|
|
||||||
BoxWithConstraints(
|
BoxWithConstraints(modifier = modifier.fillMaxSize().alpha(alpha)) {
|
||||||
modifier = modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.alpha(alpha)
|
|
||||||
.background(Brush.verticalGradient(listOf(BrandField, BrandDeep))),
|
|
||||||
) {
|
|
||||||
// Read the constraints here and hold them in locals: inside Column the
|
// Read the constraints here and hold them in locals: inside Column the
|
||||||
// implicit receiver becomes ColumnScope, and maxWidth/maxHeight are no
|
// implicit receiver becomes ColumnScope, and maxWidth/maxHeight are no
|
||||||
// longer in scope.
|
// longer in scope.
|
||||||
val shortestSide = minOf(maxWidth, maxHeight).value
|
val shortestSide = minOf(maxWidth, maxHeight).value
|
||||||
|
|
||||||
|
// Weave scale is derived from the sett, not a fixed fraction, so a
|
||||||
|
// couple of full repeats are always on screen whatever the threadcount
|
||||||
|
// says and whatever the display is.
|
||||||
|
val threadWidth = threadWidthFor(across = shortestSide.dp, repeats = 2f)
|
||||||
val titleSize = (shortestSide * 0.13f).sp
|
val titleSize = (shortestSide * 0.13f).sp
|
||||||
|
TartanBackground(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
threadWidth = threadWidth,
|
||||||
|
// On the bias, matching the icon.
|
||||||
|
rotationDegrees = 45f,
|
||||||
|
)
|
||||||
|
|
||||||
|
// A scrim keeps the lettering legible over the busiest part of the sett.
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(Color.Black.copy(alpha = 0.45f))
|
||||||
|
)
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -95,7 +103,7 @@ fun SplashScreen(
|
|||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "Pilgrims",
|
text = "Pro Libertate",
|
||||||
style = TextStyle(
|
style = TextStyle(
|
||||||
fontFamily = CelticFontFamily,
|
fontFamily = CelticFontFamily,
|
||||||
fontSize = titleSize,
|
fontSize = titleSize,
|
||||||
@@ -109,11 +117,11 @@ fun SplashScreen(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = "of Katan",
|
text = "Games",
|
||||||
style = TextStyle(
|
style = TextStyle(
|
||||||
fontFamily = CelticFontFamily,
|
fontFamily = CelticFontFamily,
|
||||||
fontSize = titleSize,
|
fontSize = titleSize,
|
||||||
color = BrandAccent,
|
color = Color(0xFFF2C200),
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
shadow = Shadow(
|
shadow = Shadow(
|
||||||
color = Color.Black,
|
color = Color.Black,
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import org.prolibertate.games.ui.common.CARD_ASPECT
|
import org.prolibertate.games.ui.common.CARD_ASPECT
|
||||||
import org.prolibertate.games.ui.theme.FeltGreenDark
|
import org.prolibertate.games.ui.theme.FeltGreenDark
|
||||||
import org.prolibertate.games.ui.theme.Parchment
|
import org.prolibertate.games.ui.theme.Parchment
|
||||||
import org.prolibertate.games.ui.theme.AccentGold
|
import org.prolibertate.games.ui.theme.WallaceGold
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The furniture every patience table needs.
|
* The furniture every patience table needs.
|
||||||
@@ -48,7 +48,7 @@ fun TableSlot(
|
|||||||
modifier = modifier
|
modifier = modifier
|
||||||
.width(width)
|
.width(width)
|
||||||
.border(
|
.border(
|
||||||
border = BorderStroke(if (highlighted) 3.dp else 0.dp, AccentGold),
|
border = BorderStroke(if (highlighted) 3.dp else 0.dp, WallaceGold),
|
||||||
shape = RoundedCornerShape(4.dp),
|
shape = RoundedCornerShape(4.dp),
|
||||||
)
|
)
|
||||||
.clickable { onClick() },
|
.clickable { onClick() },
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ import org.prolibertate.games.ui.common.CardBackView
|
|||||||
import org.prolibertate.games.ui.common.PlayingCardView
|
import org.prolibertate.games.ui.common.PlayingCardView
|
||||||
import org.prolibertate.games.ui.theme.FeltGreenDark
|
import org.prolibertate.games.ui.theme.FeltGreenDark
|
||||||
import org.prolibertate.games.ui.theme.Parchment
|
import org.prolibertate.games.ui.theme.Parchment
|
||||||
import org.prolibertate.games.ui.theme.AccentGold
|
import org.prolibertate.games.ui.theme.WallaceGold
|
||||||
|
|
||||||
private const val TITLE = "Klondike"
|
private const val TITLE = "Klondike"
|
||||||
|
|
||||||
@@ -311,7 +311,7 @@ private fun Slot(
|
|||||||
modifier = modifier
|
modifier = modifier
|
||||||
.width(CARD_WIDTH)
|
.width(CARD_WIDTH)
|
||||||
.border(
|
.border(
|
||||||
border = BorderStroke(if (highlighted) 3.dp else 0.dp, AccentGold),
|
border = BorderStroke(if (highlighted) 3.dp else 0.dp, WallaceGold),
|
||||||
shape = RoundedCornerShape(4.dp),
|
shape = RoundedCornerShape(4.dp),
|
||||||
)
|
)
|
||||||
.clickable { onClick() },
|
.clickable { onClick() },
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ import org.prolibertate.games.net.MatchController
|
|||||||
import org.prolibertate.games.ui.ScreenScaffold
|
import org.prolibertate.games.ui.ScreenScaffold
|
||||||
import org.prolibertate.games.ui.theme.FeltGreenDark
|
import org.prolibertate.games.ui.theme.FeltGreenDark
|
||||||
import org.prolibertate.games.ui.theme.Parchment
|
import org.prolibertate.games.ui.theme.Parchment
|
||||||
import org.prolibertate.games.ui.theme.AccentGold
|
import org.prolibertate.games.ui.theme.WallaceGold
|
||||||
import org.prolibertate.games.ui.theme.AccentRed
|
import org.prolibertate.games.ui.theme.WallaceRed
|
||||||
|
|
||||||
private const val TITLE = "Peg Solitaire"
|
private const val TITLE = "Peg Solitaire"
|
||||||
|
|
||||||
@@ -214,15 +214,15 @@ private fun HoleView(
|
|||||||
Box(modifier = modifier.size(size), contentAlignment = Alignment.Center) {
|
Box(modifier = modifier.size(size), contentAlignment = Alignment.Center) {
|
||||||
val diameter = size * 0.72f
|
val diameter = size * 0.72f
|
||||||
val ring = when {
|
val ring = when {
|
||||||
selected -> AccentGold
|
selected -> WallaceGold
|
||||||
isDestination -> AccentGold
|
isDestination -> WallaceGold
|
||||||
else -> Parchment.copy(alpha = 0.35f)
|
else -> Parchment.copy(alpha = 0.35f)
|
||||||
}
|
}
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(diameter)
|
.size(diameter)
|
||||||
.background(
|
.background(
|
||||||
color = if (filled) AccentRed else FeltGreenDark,
|
color = if (filled) WallaceRed else FeltGreenDark,
|
||||||
shape = CircleShape,
|
shape = CircleShape,
|
||||||
)
|
)
|
||||||
.border(
|
.border(
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ import org.prolibertate.games.game.yahtzee.scoreOf
|
|||||||
import org.prolibertate.games.net.MatchController
|
import org.prolibertate.games.net.MatchController
|
||||||
import org.prolibertate.games.ui.ScreenScaffold
|
import org.prolibertate.games.ui.ScreenScaffold
|
||||||
import org.prolibertate.games.ui.theme.Parchment
|
import org.prolibertate.games.ui.theme.Parchment
|
||||||
import org.prolibertate.games.ui.theme.AccentGold
|
import org.prolibertate.games.ui.theme.WallaceGold
|
||||||
import org.prolibertate.games.ui.theme.AccentRed
|
import org.prolibertate.games.ui.theme.WallaceRed
|
||||||
|
|
||||||
private const val TITLE = "Yahtzee"
|
private const val TITLE = "Yahtzee"
|
||||||
|
|
||||||
@@ -183,7 +183,7 @@ private fun Die(face: Int?, held: Boolean, enabled: Boolean, onClick: () -> Unit
|
|||||||
.background(if (face == null) Parchment.copy(alpha = 0.3f) else Parchment, RoundedCornerShape(8.dp))
|
.background(if (face == null) Parchment.copy(alpha = 0.3f) else Parchment, RoundedCornerShape(8.dp))
|
||||||
.border(
|
.border(
|
||||||
width = if (held) 3.dp else 1.dp,
|
width = if (held) 3.dp else 1.dp,
|
||||||
color = if (held) AccentGold else AccentRed.copy(alpha = 0.4f),
|
color = if (held) WallaceGold else WallaceRed.copy(alpha = 0.4f),
|
||||||
shape = RoundedCornerShape(8.dp),
|
shape = RoundedCornerShape(8.dp),
|
||||||
)
|
)
|
||||||
.clickable(enabled = enabled) { onClick() },
|
.clickable(enabled = enabled) { onClick() },
|
||||||
@@ -193,7 +193,7 @@ private fun Die(face: Int?, held: Boolean, enabled: Boolean, onClick: () -> Unit
|
|||||||
text = face?.toString() ?: "·",
|
text = face?.toString() ?: "·",
|
||||||
style = MaterialTheme.typography.titleLarge,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
color = AccentRed,
|
color = WallaceRed,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,7 +263,7 @@ private fun CardRow(
|
|||||||
},
|
},
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
color = if (written == null && open) AccentRed else MaterialTheme.colorScheme.onSurface,
|
color = if (written == null && open) WallaceRed else MaterialTheme.colorScheme.onSurface,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,187 @@
|
|||||||
|
package org.prolibertate.games.ui.theme
|
||||||
|
|
||||||
|
import androidx.compose.foundation.Canvas
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.geometry.Size
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||||
|
import androidx.compose.ui.graphics.drawscope.rotate
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import org.prolibertate.games.BuildConfig
|
||||||
|
|
||||||
|
/** One band of a tartan sett, measured in threads. */
|
||||||
|
data class TartanStripe(val color: Color, val threads: Int)
|
||||||
|
|
||||||
|
val WallaceRed = Color(0xFFE01B24)
|
||||||
|
val WallaceBlack = Color(0xFF0C0C0C)
|
||||||
|
val WallaceGold = Color(0xFFF2C518)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The green the hunting sett puts where the clan sett puts red.
|
||||||
|
*
|
||||||
|
* Must match `tartan_field` for a dev build in app/build.gradle.kts, which is
|
||||||
|
* what the launcher icon is woven from — otherwise the icon and the splash it
|
||||||
|
* opens on would be visibly different cloth.
|
||||||
|
*/
|
||||||
|
val WallaceHuntingGreen = Color(0xFF1E7A3C)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expands a half-sett into a full repeat by reflecting it.
|
||||||
|
*
|
||||||
|
* Tartans are specified as a half-sett between two pivots and woven out to
|
||||||
|
* both sides, so `A B C` becomes `A B C B` — the pivot stripes are not
|
||||||
|
* doubled. Writing the data this way keeps it in the same form a threadcount
|
||||||
|
* is published in, instead of a flat list that has to be kept symmetric by
|
||||||
|
* hand.
|
||||||
|
*/
|
||||||
|
fun List<TartanStripe>.pivoted(): List<TartanStripe> =
|
||||||
|
if (size < 3) this else this + drop(1).dropLast(1).reversed()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Wallace sett, from its published threadcount: **K/4 R32 K32 Y/4**.
|
||||||
|
*
|
||||||
|
* The slashes mark the pivots, so this is the half-sett and it reflects out to
|
||||||
|
* `K4 R32 K32 Y4 K32 R32` — 136 threads, roughly half black, half red, with a
|
||||||
|
* yellow line worth 3%. Two consequences that are easy to get backwards and
|
||||||
|
* that define the pattern:
|
||||||
|
*
|
||||||
|
* - the yellow overstripe runs down the **centre of a wide black band**, not
|
||||||
|
* between red and black;
|
||||||
|
* - the narrow black guard sits **between two red blocks**, splitting the red
|
||||||
|
* into pairs.
|
||||||
|
*
|
||||||
|
* The threadcount itself lives in [wallaceSett], because the hunting colourway
|
||||||
|
* below is the same cloth in a different colour and must not drift from it.
|
||||||
|
*/
|
||||||
|
val WallaceSett: List<TartanStripe> = wallaceSett(WallaceRed)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wallace Hunting: the same threadcount woven in green.
|
||||||
|
*
|
||||||
|
* Hunting setts are the clan cloth in muted, outdoor colours, and for Wallace
|
||||||
|
* that is the one substitution — green for the red — with the black ground and
|
||||||
|
* the yellow overstripe untouched. Deriving it from the same builder rather
|
||||||
|
* than writing the numbers out twice is what keeps that true: a change to the
|
||||||
|
* threadcount cannot land in one colourway and miss the other.
|
||||||
|
*/
|
||||||
|
val WallaceHuntingSett: List<TartanStripe> = wallaceSett(WallaceHuntingGreen)
|
||||||
|
|
||||||
|
/** The Wallace threadcount woven with [field] in place of the red. */
|
||||||
|
private fun wallaceSett(field: Color): List<TartanStripe> = listOf(
|
||||||
|
TartanStripe(WallaceBlack, 4),
|
||||||
|
TartanStripe(field, 32),
|
||||||
|
TartanStripe(WallaceBlack, 32),
|
||||||
|
TartanStripe(WallaceGold, 4),
|
||||||
|
).pivoted()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The cloth this build wears: the clan sett on production, the hunting sett on
|
||||||
|
* dev.
|
||||||
|
*
|
||||||
|
* A dev build installs as its own app beside the production one, so it has to
|
||||||
|
* be recognisable at a glance in the app drawer and on the splash — the green
|
||||||
|
* says which of the two copies just opened without anything having to be read.
|
||||||
|
*/
|
||||||
|
val AppSett: List<TartanStripe> =
|
||||||
|
if (BuildConfig.DEV_BUILD) WallaceHuntingSett else WallaceSett
|
||||||
|
|
||||||
|
/** Threads in one full repeat of a sett. */
|
||||||
|
val List<TartanStripe>.threadsPerRepeat: Int get() = sumOf { it.threads }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The thread width that fits [repeats] repeats of [sett] across [across].
|
||||||
|
*
|
||||||
|
* Scale has to be derived from the sett rather than fixed, because changing a
|
||||||
|
* threadcount changes how many threads a repeat contains — a fixed width and a
|
||||||
|
* larger sett silently zooms in until the cloth is a couple of enormous blocks
|
||||||
|
* and stops reading as tartan at all.
|
||||||
|
*/
|
||||||
|
fun threadWidthFor(
|
||||||
|
sett: List<TartanStripe> = AppSett,
|
||||||
|
across: Dp,
|
||||||
|
repeats: Float = 2f,
|
||||||
|
): Dp = across / (sett.threadsPerRepeat * repeats)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a tartan by laying the sett down vertically as warp, then across as
|
||||||
|
* weft at half opacity. The overlap is what produces the blended squares and
|
||||||
|
* the sense of a woven cloth rather than a printed grid — red warp under black
|
||||||
|
* weft comes out a darker red, exactly as it does on the loom.
|
||||||
|
*
|
||||||
|
* [rotationDegrees] turns the cloth on the bias. The stripes are drawn well
|
||||||
|
* past the edges of the canvas so the corners stay covered once it is turned.
|
||||||
|
*/
|
||||||
|
fun DrawScope.drawTartan(
|
||||||
|
sett: List<TartanStripe> = AppSett,
|
||||||
|
threadSize: Float,
|
||||||
|
rotationDegrees: Float = 0f,
|
||||||
|
) {
|
||||||
|
if (sett.isEmpty() || threadSize <= 0f) return
|
||||||
|
val repeat = sett.sumOf { it.threads } * threadSize
|
||||||
|
if (repeat <= 0f) return
|
||||||
|
|
||||||
|
// Turning a square by 45 degrees needs about 0.21 of its size spare on
|
||||||
|
// every edge; three quarters is generous enough for any angle.
|
||||||
|
val pad = if (rotationDegrees == 0f) 0f else size.maxDimension * 0.75f
|
||||||
|
val left = -pad
|
||||||
|
val top = -pad
|
||||||
|
val right = size.width + pad
|
||||||
|
val bottom = size.height + pad
|
||||||
|
|
||||||
|
rotate(degrees = rotationDegrees) {
|
||||||
|
// Warp: full-height bands.
|
||||||
|
var x = left - (left.mod(repeat))
|
||||||
|
while (x < right) {
|
||||||
|
var offset = x
|
||||||
|
for (stripe in sett) {
|
||||||
|
val width = stripe.threads * threadSize
|
||||||
|
drawRect(
|
||||||
|
color = stripe.color,
|
||||||
|
topLeft = Offset(offset, top),
|
||||||
|
size = Size(width, bottom - top),
|
||||||
|
)
|
||||||
|
offset += width
|
||||||
|
}
|
||||||
|
x += repeat
|
||||||
|
}
|
||||||
|
|
||||||
|
// Weft: full-width bands, half opacity so the warp shows through.
|
||||||
|
var y = top - (top.mod(repeat))
|
||||||
|
while (y < bottom) {
|
||||||
|
var offset = y
|
||||||
|
for (stripe in sett) {
|
||||||
|
val height = stripe.threads * threadSize
|
||||||
|
drawRect(
|
||||||
|
color = stripe.color.copy(alpha = 0.5f),
|
||||||
|
topLeft = Offset(left, offset),
|
||||||
|
size = Size(right - left, height),
|
||||||
|
)
|
||||||
|
offset += height
|
||||||
|
}
|
||||||
|
y += repeat
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A tartan fill. [threadWidth] sets the weave scale, so the same cloth can be
|
||||||
|
* a fine weave on a phone and a coarse one on a tablet without redrawing it.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun TartanBackground(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
sett: List<TartanStripe> = AppSett,
|
||||||
|
threadWidth: Dp = 2.dp,
|
||||||
|
rotationDegrees: Float = 0f,
|
||||||
|
) {
|
||||||
|
Canvas(modifier = modifier) {
|
||||||
|
drawTartan(
|
||||||
|
sett = sett,
|
||||||
|
threadSize = threadWidth.toPx(),
|
||||||
|
rotationDegrees = rotationDegrees,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,6 @@ import androidx.compose.material3.darkColorScheme
|
|||||||
import androidx.compose.material3.lightColorScheme
|
import androidx.compose.material3.lightColorScheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import org.prolibertate.games.BuildConfig
|
|
||||||
|
|
||||||
/** Card-table green, which the board and table surfaces are drawn on. */
|
/** Card-table green, which the board and table surfaces are drawn on. */
|
||||||
val FeltGreen = Color(0xFF1B5E20)
|
val FeltGreen = Color(0xFF1B5E20)
|
||||||
@@ -15,31 +14,6 @@ val Parchment = Color(0xFFF4EFE4)
|
|||||||
val CardRed = Color(0xFFB3261E)
|
val CardRed = Color(0xFFB3261E)
|
||||||
val CardBlack = Color(0xFF1C1B1F)
|
val CardBlack = Color(0xFF1C1B1F)
|
||||||
|
|
||||||
/**
|
|
||||||
* The brand palette for Pilgrims of Katan — original and neutral, shared by
|
|
||||||
* every game in the catalogue. Slate blue on production, deep green on dev, so
|
|
||||||
* the two installs are told apart at a glance; both must stay in step with the
|
|
||||||
* resValues in app/build.gradle.kts or the icon and the splash it opens on
|
|
||||||
* would be different brands.
|
|
||||||
*
|
|
||||||
* The upstream shipped a clan tartan here. This project carries no clan or
|
|
||||||
* third-party trade dress (WALA-118).
|
|
||||||
*/
|
|
||||||
val BrandField: Color = if (BuildConfig.DEV_BUILD) Color(0xFF2A6B54) else Color(0xFF2B4A78)
|
|
||||||
val BrandDeep: Color = if (BuildConfig.DEV_BUILD) Color(0xFF153B2F) else Color(0xFF182A44)
|
|
||||||
|
|
||||||
/** The parchment accent: the emblem on the icon, the second line of the splash. */
|
|
||||||
val BrandAccent: Color = Color(0xFFEFD9A4)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Accents used *inside* a game table — a highlight, a held die, a danger mark.
|
|
||||||
* Deliberately channel-free: what a game shows must not change with which
|
|
||||||
* install it is running in. Values unchanged from the upstream, so the games
|
|
||||||
* themselves look exactly as they did.
|
|
||||||
*/
|
|
||||||
val AccentRed = Color(0xFFE01B24)
|
|
||||||
val AccentGold = Color(0xFFF2C518)
|
|
||||||
|
|
||||||
val TeamColours: List<Color> = listOf(
|
val TeamColours: List<Color> = listOf(
|
||||||
Color(0xFF1565C0), // blue
|
Color(0xFF1565C0), // blue
|
||||||
Color(0xFFC62828), // red
|
Color(0xFFC62828), // red
|
||||||
@@ -65,7 +39,7 @@ private val LightColours = lightColorScheme(
|
|||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PilgrimsTheme(
|
fun ProLibertateTheme(
|
||||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||||
content: @Composable () -> Unit,
|
content: @Composable () -> Unit,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -1,16 +1,70 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
The flat field behind the launcher emblem: brand_field, slate blue on
|
A single square of the Wallace tartan, set on the bias.
|
||||||
production and deep green on dev, generated by the build (see resValue in
|
|
||||||
app/build.gradle.kts) so the two installs are told apart in the app drawer
|
Generated from the published threadcount K/4 R32 K32 Y/4 — the same numbers
|
||||||
by the icon alone.
|
as WallaceSett in Tartan.kt. The slashes are pivots, so the half-sett
|
||||||
|
reflects out to K4 R32 K32 Y4 K32 R32: the yellow overstripe runs down the
|
||||||
|
centre of a wide black band, and the narrow black guard splits the red into
|
||||||
|
pairs.
|
||||||
|
|
||||||
|
The stripes reference @color/tartan_field rather than a literal, because the
|
||||||
|
same threadcount is woven in two colourways: Wallace red on a production
|
||||||
|
build, Wallace Hunting green on a dev one, so the two installs are told apart
|
||||||
|
in the app drawer by the icon alone. The build generates that colour; see
|
||||||
|
resValue in app/build.gradle.kts.
|
||||||
|
|
||||||
|
Warp is opaque, weft laid over it at half opacity, which is what gives the
|
||||||
|
blended squares rather than a flat grid. Paths run well past the viewport
|
||||||
|
because the group is rotated 45 degrees; the overspill keeps the corners
|
||||||
|
covered. One full repeat fills the adaptive icon's safe zone.
|
||||||
-->
|
-->
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="108dp"
|
android:width="108dp"
|
||||||
android:height="108dp"
|
android:height="108dp"
|
||||||
android:viewportWidth="108"
|
android:viewportWidth="108"
|
||||||
android:viewportHeight="108">
|
android:viewportHeight="108">
|
||||||
<path
|
<group
|
||||||
android:fillColor="@color/brand_field"
|
android:rotation="45"
|
||||||
android:pathData="M0,0h108v108h-108z" />
|
android:pivotX="54.0"
|
||||||
|
android:pivotY="54.0">
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:pathData="M-90.00,-90.00h288.00v288.00h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:pathData="M-88.94,-90.00h16.94v288.00h-16.94z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:pathData="M-69.88,-90.00h16.94v288.00h-16.94z" />
|
||||||
|
<path android:fillColor="@color/tartan_over" android:pathData="M-36.00,-90.00h2.12v288.00h-2.12z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:pathData="M-16.94,-90.00h16.94v288.00h-16.94z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:pathData="M2.12,-90.00h16.94v288.00h-16.94z" />
|
||||||
|
<path android:fillColor="@color/tartan_over" android:pathData="M36.00,-90.00h2.12v288.00h-2.12z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:pathData="M55.06,-90.00h16.94v288.00h-16.94z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:pathData="M74.12,-90.00h16.94v288.00h-16.94z" />
|
||||||
|
<path android:fillColor="@color/tartan_over" android:pathData="M108.00,-90.00h2.12v288.00h-2.12z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:pathData="M127.06,-90.00h16.94v288.00h-16.94z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:pathData="M146.12,-90.00h16.94v288.00h-16.94z" />
|
||||||
|
<path android:fillColor="@color/tartan_over" android:pathData="M180.00,-90.00h2.12v288.00h-2.12z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,-105.88h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:fillAlpha="0.5" android:pathData="M-90.00,-88.94h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,-72.00h288.00v2.12h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:fillAlpha="0.5" android:pathData="M-90.00,-69.88h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,-52.94h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_over" android:fillAlpha="0.5" android:pathData="M-90.00,-36.00h288.00v2.12h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,-33.88h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:fillAlpha="0.5" android:pathData="M-90.00,-16.94h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,0.00h288.00v2.12h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:fillAlpha="0.5" android:pathData="M-90.00,2.12h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,19.06h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_over" android:fillAlpha="0.5" android:pathData="M-90.00,36.00h288.00v2.12h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,38.12h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:fillAlpha="0.5" android:pathData="M-90.00,55.06h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,72.00h288.00v2.12h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:fillAlpha="0.5" android:pathData="M-90.00,74.12h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,91.06h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_over" android:fillAlpha="0.5" android:pathData="M-90.00,108.00h288.00v2.12h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,110.12h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:fillAlpha="0.5" android:pathData="M-90.00,127.06h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,144.00h288.00v2.12h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:fillAlpha="0.5" android:pathData="M-90.00,146.12h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,163.06h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_over" android:fillAlpha="0.5" android:pathData="M-90.00,180.00h288.00v2.12h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,182.12h288.00v16.94h-288.00z" />
|
||||||
|
</group>
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
@@ -1,23 +1,15 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
The brand emblem: a hexagonal ring around a solid hexagonal core, in
|
Deliberately empty. The icon is the cloth itself, so the adaptive icon's
|
||||||
parchment on the brand field behind it (see ic_launcher_background).
|
foreground layer carries nothing — anything drawn here would sit on top of the
|
||||||
|
tartan and break the "single square of tartan" idea.
|
||||||
The hexagon is a game tile, not any game's mark — deliberately generic
|
|
||||||
branding for Pilgrims of Katan, carrying no third-party logo or trade dress.
|
|
||||||
Everything sits inside the adaptive icon's safe zone (a centred 66dp circle
|
|
||||||
on this 108dp canvas) so no launcher mask can clip it.
|
|
||||||
-->
|
-->
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="108dp"
|
android:width="108dp"
|
||||||
android:height="108dp"
|
android:height="108dp"
|
||||||
android:viewportWidth="108"
|
android:viewportWidth="108"
|
||||||
android:viewportHeight="108">
|
android:viewportHeight="108">
|
||||||
<!-- Ring and core in one even-odd path: outer hexagon, inner hexagon,
|
|
||||||
core hexagon — odd coverage fills, so the ring and the core are drawn
|
|
||||||
and the gap between them stays open to the field showing through. -->
|
|
||||||
<path
|
<path
|
||||||
android:fillColor="@color/brand_accent"
|
android:fillColor="#00000000"
|
||||||
android:fillType="evenOdd"
|
android:pathData="M0,0h108v108h-108z" />
|
||||||
android:pathData="M84,54 L69,79.98 L39,79.98 L24,54 L39,28.02 L69,28.02 Z M72,54 L63,69.59 L45,69.59 L36,54 L45,38.41 L63,38.41 Z M63,54 L58.5,61.79 L49.5,61.79 L45,54 L49.5,46.21 L58.5,46.21 Z" />
|
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
@@ -1,22 +1,70 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
The legacy (pre-API 26) launcher icon: the same brand emblem as the adaptive
|
A single square of the Wallace tartan, set on the bias.
|
||||||
icon's foreground, over the same brand field, in the one layer those
|
|
||||||
launchers get.
|
|
||||||
|
|
||||||
Field first, emblem second: two paths, because the emblem's ring relies on
|
Generated from the published threadcount K/4 R32 K32 Y/4 — the same numbers
|
||||||
even-odd parity and a filled background inside the same path would flip it.
|
as WallaceSett in Tartan.kt. The slashes are pivots, so the half-sett
|
||||||
|
reflects out to K4 R32 K32 Y4 K32 R32: the yellow overstripe runs down the
|
||||||
|
centre of a wide black band, and the narrow black guard splits the red into
|
||||||
|
pairs.
|
||||||
|
|
||||||
|
The stripes reference @color/tartan_field rather than a literal, because the
|
||||||
|
same threadcount is woven in two colourways: Wallace red on a production
|
||||||
|
build, Wallace Hunting green on a dev one, so the two installs are told apart
|
||||||
|
in the app drawer by the icon alone. The build generates that colour; see
|
||||||
|
resValue in app/build.gradle.kts.
|
||||||
|
|
||||||
|
Warp is opaque, weft laid over it at half opacity, which is what gives the
|
||||||
|
blended squares rather than a flat grid. Paths run well past the viewport
|
||||||
|
because the group is rotated 45 degrees; the overspill keeps the corners
|
||||||
|
covered. One full repeat fills the adaptive icon's safe zone.
|
||||||
-->
|
-->
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="48dp"
|
android:width="48dp"
|
||||||
android:height="48dp"
|
android:height="48dp"
|
||||||
android:viewportWidth="108"
|
android:viewportWidth="108"
|
||||||
android:viewportHeight="108">
|
android:viewportHeight="108">
|
||||||
<path
|
<group
|
||||||
android:fillColor="@color/brand_field"
|
android:rotation="45"
|
||||||
android:pathData="M0,0h108v108h-108z" />
|
android:pivotX="54.0"
|
||||||
<path
|
android:pivotY="54.0">
|
||||||
android:fillColor="@color/brand_accent"
|
<path android:fillColor="@color/tartan_ground" android:pathData="M-90.00,-90.00h288.00v288.00h-288.00z" />
|
||||||
android:fillType="evenOdd"
|
<path android:fillColor="@color/tartan_field" android:pathData="M-88.94,-90.00h16.94v288.00h-16.94z" />
|
||||||
android:pathData="M84,54 L69,79.98 L39,79.98 L24,54 L39,28.02 L69,28.02 Z M72,54 L63,69.59 L45,69.59 L36,54 L45,38.41 L63,38.41 Z M63,54 L58.5,61.79 L49.5,61.79 L45,54 L49.5,46.21 L58.5,46.21 Z" />
|
<path android:fillColor="@color/tartan_field" android:pathData="M-69.88,-90.00h16.94v288.00h-16.94z" />
|
||||||
|
<path android:fillColor="@color/tartan_over" android:pathData="M-36.00,-90.00h2.12v288.00h-2.12z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:pathData="M-16.94,-90.00h16.94v288.00h-16.94z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:pathData="M2.12,-90.00h16.94v288.00h-16.94z" />
|
||||||
|
<path android:fillColor="@color/tartan_over" android:pathData="M36.00,-90.00h2.12v288.00h-2.12z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:pathData="M55.06,-90.00h16.94v288.00h-16.94z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:pathData="M74.12,-90.00h16.94v288.00h-16.94z" />
|
||||||
|
<path android:fillColor="@color/tartan_over" android:pathData="M108.00,-90.00h2.12v288.00h-2.12z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:pathData="M127.06,-90.00h16.94v288.00h-16.94z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:pathData="M146.12,-90.00h16.94v288.00h-16.94z" />
|
||||||
|
<path android:fillColor="@color/tartan_over" android:pathData="M180.00,-90.00h2.12v288.00h-2.12z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,-105.88h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:fillAlpha="0.5" android:pathData="M-90.00,-88.94h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,-72.00h288.00v2.12h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:fillAlpha="0.5" android:pathData="M-90.00,-69.88h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,-52.94h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_over" android:fillAlpha="0.5" android:pathData="M-90.00,-36.00h288.00v2.12h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,-33.88h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:fillAlpha="0.5" android:pathData="M-90.00,-16.94h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,0.00h288.00v2.12h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:fillAlpha="0.5" android:pathData="M-90.00,2.12h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,19.06h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_over" android:fillAlpha="0.5" android:pathData="M-90.00,36.00h288.00v2.12h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,38.12h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:fillAlpha="0.5" android:pathData="M-90.00,55.06h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,72.00h288.00v2.12h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:fillAlpha="0.5" android:pathData="M-90.00,74.12h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,91.06h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_over" android:fillAlpha="0.5" android:pathData="M-90.00,108.00h288.00v2.12h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,110.12h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:fillAlpha="0.5" android:pathData="M-90.00,127.06h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,144.00h288.00v2.12h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:fillAlpha="0.5" android:pathData="M-90.00,146.12h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,163.06h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_over" android:fillAlpha="0.5" android:pathData="M-90.00,180.00h288.00v2.12h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,182.12h288.00v16.94h-288.00z" />
|
||||||
|
</group>
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
@@ -1,22 +1,70 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
The legacy (pre-API 26) launcher icon: the same brand emblem as the adaptive
|
A single square of the Wallace tartan, set on the bias.
|
||||||
icon's foreground, over the same brand field, in the one layer those
|
|
||||||
launchers get.
|
|
||||||
|
|
||||||
Field first, emblem second: two paths, because the emblem's ring relies on
|
Generated from the published threadcount K/4 R32 K32 Y/4 — the same numbers
|
||||||
even-odd parity and a filled background inside the same path would flip it.
|
as WallaceSett in Tartan.kt. The slashes are pivots, so the half-sett
|
||||||
|
reflects out to K4 R32 K32 Y4 K32 R32: the yellow overstripe runs down the
|
||||||
|
centre of a wide black band, and the narrow black guard splits the red into
|
||||||
|
pairs.
|
||||||
|
|
||||||
|
The stripes reference @color/tartan_field rather than a literal, because the
|
||||||
|
same threadcount is woven in two colourways: Wallace red on a production
|
||||||
|
build, Wallace Hunting green on a dev one, so the two installs are told apart
|
||||||
|
in the app drawer by the icon alone. The build generates that colour; see
|
||||||
|
resValue in app/build.gradle.kts.
|
||||||
|
|
||||||
|
Warp is opaque, weft laid over it at half opacity, which is what gives the
|
||||||
|
blended squares rather than a flat grid. Paths run well past the viewport
|
||||||
|
because the group is rotated 45 degrees; the overspill keeps the corners
|
||||||
|
covered. One full repeat fills the adaptive icon's safe zone.
|
||||||
-->
|
-->
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="48dp"
|
android:width="48dp"
|
||||||
android:height="48dp"
|
android:height="48dp"
|
||||||
android:viewportWidth="108"
|
android:viewportWidth="108"
|
||||||
android:viewportHeight="108">
|
android:viewportHeight="108">
|
||||||
<path
|
<group
|
||||||
android:fillColor="@color/brand_field"
|
android:rotation="45"
|
||||||
android:pathData="M0,0h108v108h-108z" />
|
android:pivotX="54.0"
|
||||||
<path
|
android:pivotY="54.0">
|
||||||
android:fillColor="@color/brand_accent"
|
<path android:fillColor="@color/tartan_ground" android:pathData="M-90.00,-90.00h288.00v288.00h-288.00z" />
|
||||||
android:fillType="evenOdd"
|
<path android:fillColor="@color/tartan_field" android:pathData="M-88.94,-90.00h16.94v288.00h-16.94z" />
|
||||||
android:pathData="M84,54 L69,79.98 L39,79.98 L24,54 L39,28.02 L69,28.02 Z M72,54 L63,69.59 L45,69.59 L36,54 L45,38.41 L63,38.41 Z M63,54 L58.5,61.79 L49.5,61.79 L45,54 L49.5,46.21 L58.5,46.21 Z" />
|
<path android:fillColor="@color/tartan_field" android:pathData="M-69.88,-90.00h16.94v288.00h-16.94z" />
|
||||||
|
<path android:fillColor="@color/tartan_over" android:pathData="M-36.00,-90.00h2.12v288.00h-2.12z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:pathData="M-16.94,-90.00h16.94v288.00h-16.94z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:pathData="M2.12,-90.00h16.94v288.00h-16.94z" />
|
||||||
|
<path android:fillColor="@color/tartan_over" android:pathData="M36.00,-90.00h2.12v288.00h-2.12z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:pathData="M55.06,-90.00h16.94v288.00h-16.94z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:pathData="M74.12,-90.00h16.94v288.00h-16.94z" />
|
||||||
|
<path android:fillColor="@color/tartan_over" android:pathData="M108.00,-90.00h2.12v288.00h-2.12z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:pathData="M127.06,-90.00h16.94v288.00h-16.94z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:pathData="M146.12,-90.00h16.94v288.00h-16.94z" />
|
||||||
|
<path android:fillColor="@color/tartan_over" android:pathData="M180.00,-90.00h2.12v288.00h-2.12z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,-105.88h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:fillAlpha="0.5" android:pathData="M-90.00,-88.94h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,-72.00h288.00v2.12h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:fillAlpha="0.5" android:pathData="M-90.00,-69.88h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,-52.94h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_over" android:fillAlpha="0.5" android:pathData="M-90.00,-36.00h288.00v2.12h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,-33.88h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:fillAlpha="0.5" android:pathData="M-90.00,-16.94h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,0.00h288.00v2.12h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:fillAlpha="0.5" android:pathData="M-90.00,2.12h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,19.06h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_over" android:fillAlpha="0.5" android:pathData="M-90.00,36.00h288.00v2.12h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,38.12h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:fillAlpha="0.5" android:pathData="M-90.00,55.06h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,72.00h288.00v2.12h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:fillAlpha="0.5" android:pathData="M-90.00,74.12h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,91.06h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_over" android:fillAlpha="0.5" android:pathData="M-90.00,108.00h288.00v2.12h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,110.12h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:fillAlpha="0.5" android:pathData="M-90.00,127.06h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,144.00h288.00v2.12h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_field" android:fillAlpha="0.5" android:pathData="M-90.00,146.12h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,163.06h288.00v16.94h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_over" android:fillAlpha="0.5" android:pathData="M-90.00,180.00h288.00v2.12h-288.00z" />
|
||||||
|
<path android:fillColor="@color/tartan_ground" android:fillAlpha="0.5" android:pathData="M-90.00,182.12h288.00v16.94h-288.00z" />
|
||||||
|
</group>
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
The colours the brand shares between channels. The two that differ —
|
The colours the sett shares between channels. The one that differs —
|
||||||
brand_field (slate blue on production, deep green on dev) and its darker
|
tartan_field, red on production and hunting green on dev — is generated by the
|
||||||
shade splash_background — are generated by the build. See resValue in
|
build, along with splash_background and ic_launcher_background, which are
|
||||||
app/build.gradle.kts.
|
shades of it. See resValue in app/build.gradle.kts.
|
||||||
-->
|
-->
|
||||||
<resources>
|
<resources>
|
||||||
<color name="black">#FF000000</color>
|
<color name="black">#FF000000</color>
|
||||||
<color name="white">#FFFFFFFF</color>
|
<color name="white">#FFFFFFFF</color>
|
||||||
|
|
||||||
<!-- The parchment accent drawn on the brand field: the emblem on the
|
<!-- The ground the sett is woven on, and the overstripe down its centre.
|
||||||
launcher icon and the second line of the splash. Same number as the
|
Same numbers as WallaceBlack and WallaceGold in Tartan.kt. -->
|
||||||
accent in Theme.kt. -->
|
<color name="tartan_ground">#0C0C0C</color>
|
||||||
<color name="brand_accent">#EFD9A4</color>
|
<color name="tartan_over">#F2C518</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
app_name is generated by the build rather than declared here, because it
|
app_name is generated by the build rather than declared here, because it
|
||||||
differs by channel: a dev build installs as "Pilgrims of Katan DEV"
|
differs by channel: a dev build installs as "Pro Libertate Games DEV"
|
||||||
alongside the production copy. See resValue in app/build.gradle.kts.
|
alongside the production copy. See resValue in app/build.gradle.kts.
|
||||||
-->
|
-->
|
||||||
<resources>
|
<resources>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<!-- Compose supplies the real theme; this only needs to be a Material 3
|
<!-- Compose supplies the real theme; this only needs to be a Material 3
|
||||||
base with no action bar so the app draws its own top bars. -->
|
base with no action bar so the app draws its own top bars. -->
|
||||||
<style name="Theme.PilgrimsOfKatan" parent="android:Theme.Material.NoActionBar">
|
<style name="Theme.ProLibertateGames" parent="android:Theme.Material.NoActionBar">
|
||||||
<item name="android:windowNoTitle">true</item>
|
<item name="android:windowNoTitle">true</item>
|
||||||
<item name="android:windowContentOverlay">@null</item>
|
<item name="android:windowContentOverlay">@null</item>
|
||||||
<item name="android:windowBackground">@color/splash_background</item>
|
<item name="android:windowBackground">@color/splash_background</item>
|
||||||
|
|||||||
@@ -106,17 +106,19 @@ class CatalogTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `every game in the catalogue can be played`() {
|
fun `every reference game in the catalogue can be played`() {
|
||||||
// There is nothing left marked "coming soon". The machinery that hides
|
// Every game the app ships is playable. The one exception is Katan,
|
||||||
// unfinished games from a production build is still there and still
|
// listed so the slot exists and marked *Coming soon* until its engine
|
||||||
// tested above — it simply has nothing to hide at the moment, and this
|
// and screen land — `available = false` is what keeps an unfinished
|
||||||
// is what will fail the day something is added to the list before its
|
// game out of a production build while leaving it visible on a dev one.
|
||||||
// engine is written.
|
|
||||||
assertTrue(GameCatalog.all.all { it.available })
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
GameCatalog.inMenu(GameMenu.BOARD, includeComingSoon = false),
|
listOf(GameCatalog.CATAN),
|
||||||
GameCatalog.inMenu(GameMenu.BOARD, includeComingSoon = true),
|
GameCatalog.all.filterNot { it.available }.map { it.id },
|
||||||
)
|
)
|
||||||
|
// It is also the only difference the two channels see in a menu.
|
||||||
|
val hiddenFromProduction = GameCatalog.inMenu(GameMenu.BOARD, includeComingSoon = true) -
|
||||||
|
GameCatalog.inMenu(GameMenu.BOARD, includeComingSoon = false)
|
||||||
|
assertEquals(listOf(GameCatalog.byId(GameCatalog.CATAN)!!), hiddenFromProduction)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
+2
-2
@@ -87,8 +87,8 @@ PLG_CHANNEL=production ./gradlew assembleDebug # production app (ca.wallacenet
|
|||||||
```
|
```
|
||||||
|
|
||||||
The two variants install side by side: different applicationId, different
|
The two variants install side by side: different applicationId, different
|
||||||
launcher name (`Pilgrims of Katan DEV` vs `Pilgrims of Katan`), different brand
|
launcher name (`Pro Libertate Games DEV` vs `Pro Libertate Games`), different
|
||||||
colour.
|
tartan sett.
|
||||||
|
|
||||||
## Manual release (works today, no runner needed)
|
## Manual release (works today, no runner needed)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user