Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ local.properties

# Log/OS Files
*.log
.DS_Store

# Android Studio generated files and folders
captures/
Expand Down Expand Up @@ -40,3 +41,6 @@ nul

keystore.base64
keystore.properties

# Temporary data and scripts
tmp/
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ Glyph Catch is a fan-made Pokémon catching game for the Nothing Phone 3 that re

![Screenshots: Home screen, Pokédex screen, Pokémon (caught) screen, Bag screen](/visual.png)

**Pokémon appear based on real-world conditions.** Weather conditions boost certain Pokémon types. Zubat comes out at night. Phone running low on battery? Voltorb and its evolution might show up. And with Halloween approaching, you can of course expect Ghost Pokémon. Keep an eye on the daily Trainer Tips in the app for hints! **All 151 Kanto Pokémon are obtainable.**
**Pokémon appear based on real-world conditions.** Weather conditions boost certain Pokémon types. Zubat comes out at night. Phone running low on battery? Voltorb and its evolution might show up. And with Halloween approaching, you can of course expect Ghost Pokémon. Keep an eye on the daily Trainer Tips in the app for hints! **All 251 Kanto & Johto Pokémon are obtainable.**

**There's more to do than just catching.** Choose a Pokémon to level up, and it gains EXP while your phone is face-down. Pokémon will evolve when they hit the right level. You'll also find items, like evolution stones, Rare Candies for leveling, and the Super Rod to fish for certain aquatic species.
**There's more to do than just catching.** Choose up to two Pokémon to level up, and they gain EXP while your phone is face-down. Pokémon evolve when they hit the right level, when you use an evolution item, or—for certain species—once they're happy enough. You'll also find items, like evolution stones, Rare Candies for leveling, Soothe Bell Cookies to raise friendship, and the Super Rod to fish for certain aquatic species.

**Breed, hatch, and discover.** Leave two compatible Pokémon training together and you might eventually find an Egg. Eggs hatch as you walk—the more you move, the sooner they're ready. Every Pokémon now has a gender, which determines breeding compatibility and the species an Egg will hatch into. Keep an eye out for Unown, too: the shape it takes depends on the exact minute you set your phone down.

When there are no Pokémon around to catch, the Glyph Toy displays a handy digital clock instead.

Expand All @@ -30,6 +32,10 @@ When there are no Pokémon around to catch, the Glyph Toy displays a handy digit

No: when you throw a Poké Ball, you have a 100% chance of catching the Pokémon. Glyph Catch is designed to be consistently rewarding and stress-free. Up to four Pokémon can accumulate, and they stay indefinitely—even if you do unlock your phone after. The only chance element is finding Pokémon.

### How do eggs work?

Leave two compatible Pokémon (matching Egg Groups, opposite genders—or anything paired with Ditto) training together. After enough time has passed, an Egg may appear on the Glyph Toy for you to collect. Place an Egg in your Egg Pouch and it will hatch once you've walked enough steps, so it pays to stay active. The Pokémon that hatches is the same species as its mother (or the non-Ditto parent), at its base evolution stage.

### What if the phone is in my pocket instead of face-down?

Technically, as long as your phone stays locked and the Glyph Matrix is on, it's counted as "screen-off time". So you won't lose your streak, even if you quickly check your lock screen for a potentially urgent message.
Expand All @@ -44,7 +50,7 @@ Not that I've noticed. And if you actually end up using your phone for just a fe

### Can I set a dark background for the Glyph Toy?

No - the source sprites would need manual reworking for dark backgrounds (shading, outlines), on top of already adapting them to the 25x25 monochrome display. With 151+ Pokémon, it's beyond the scope of this fan project.
No - the source sprites would need manual reworking for dark backgrounds (shading, outlines), on top of already adapting them to the 25x25 monochrome display. With 251+ Pokémon, it's beyond the scope of this fan project.

However: if you prefer, you can dim the sprite and background slightly under Settings > Graphics (as of v1.2.0).

Expand All @@ -60,6 +66,8 @@ Note that Essential Notifications may disconnect the Glyph Toy and disrupt the t

Glyph Catch does not collect any personal data.

Hatching Eggs uses your device's step counter, which requires the Physical Activity permission. Step data is only read on-device to track Egg hatching progress and is never transmitted anywhere. You can decline the permission and still play—only Egg hatching is affected.

Live weather sync uses the popular OpenWeatherMap API with a geographical location that you configure, no live location. Refer to the [OpenWeatherMap privacy policy](https://openweather.co.uk/privacy-policy) for details.

There is a "Save logs" option (off by default) under "Debugging" for troubleshooting purposes. If enabled, these logs are saved locally on your device, not automatically sent to a server. You can inspect or delete the logs at any point.
6 changes: 6 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ android {
buildFeatures {
compose = true
}

testOptions {
unitTests.all {
it.jvmArgs("-Dnet.bytebuddy.experimental=true")
}
}
}

kotlin {
Expand Down
280 changes: 280 additions & 0 deletions app/schemas/dev.equalparts.glyph_catch.data.PokemonDatabase/11.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
{
"formatVersion": 1,
"database": {
"version": 11,
"identityHash": "2af8897c3637536f69d2e37c31fae188",
"entities": [
{
"tableName": "caught_pokemon",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `speciesId` INTEGER NOT NULL, `caughtAt` INTEGER NOT NULL, `spawnedAt` INTEGER NOT NULL DEFAULT 0, `nickname` TEXT, `isFavorite` INTEGER NOT NULL, `isTraining` INTEGER NOT NULL, `currentExp` INTEGER NOT NULL, `level` INTEGER NOT NULL, `spawnPoolName` TEXT, `isSpecialSpawn` INTEGER NOT NULL, `isConditionalSpawn` INTEGER NOT NULL, `screenOffDurationMinutes` INTEGER NOT NULL DEFAULT 0, `gender` TEXT NOT NULL DEFAULT 'GENDERLESS', PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "speciesId",
"columnName": "speciesId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "caughtAt",
"columnName": "caughtAt",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "spawnedAt",
"columnName": "spawnedAt",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "nickname",
"columnName": "nickname",
"affinity": "TEXT"
},
{
"fieldPath": "isFavorite",
"columnName": "isFavorite",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isTraining",
"columnName": "isTraining",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "exp",
"columnName": "currentExp",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "level",
"columnName": "level",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "spawnPoolName",
"columnName": "spawnPoolName",
"affinity": "TEXT"
},
{
"fieldPath": "isSpecialSpawn",
"columnName": "isSpecialSpawn",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isConditionalSpawn",
"columnName": "isConditionalSpawn",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "screenOffDurationMinutes",
"columnName": "screenOffDurationMinutes",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "gender",
"columnName": "gender",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'GENDERLESS'"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "pokedex_records",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`speciesId` INTEGER NOT NULL, PRIMARY KEY(`speciesId`))",
"fields": [
{
"fieldPath": "speciesId",
"columnName": "speciesId",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"speciesId"
]
}
},
{
"tableName": "inventory",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` INTEGER NOT NULL, `quantity` INTEGER NOT NULL, PRIMARY KEY(`itemId`))",
"fields": [
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "quantity",
"columnName": "quantity",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"itemId"
]
}
},
{
"tableName": "active_items",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` INTEGER NOT NULL, `activatedAt` INTEGER NOT NULL, `expiresAt` INTEGER NOT NULL, PRIMARY KEY(`itemId`))",
"fields": [
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "activatedAt",
"columnName": "activatedAt",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "expiresAt",
"columnName": "expiresAt",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"itemId"
]
}
},
{
"tableName": "debug_events",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `timestamp` INTEGER NOT NULL, `eventType` TEXT NOT NULL, `phoneBattery` INTEGER NOT NULL, `phoneIsInteractive` INTEGER NOT NULL, `phoneMinutesOff` INTEGER NOT NULL, `phoneMinutesOffOutsideBedtime` INTEGER NOT NULL, `queueSize` INTEGER NOT NULL, `hasSleepBonus` INTEGER NOT NULL, `isBedtime` INTEGER NOT NULL, `payloadJson` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "timestamp",
"columnName": "timestamp",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "eventType",
"columnName": "eventType",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "phoneBattery",
"columnName": "phoneBattery",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "phoneIsInteractive",
"columnName": "phoneIsInteractive",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "phoneMinutesOff",
"columnName": "phoneMinutesOff",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "phoneMinutesOffOutsideBedtime",
"columnName": "phoneMinutesOffOutsideBedtime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "queueSize",
"columnName": "queueSize",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "hasSleepBonus",
"columnName": "hasSleepBonus",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isBedtime",
"columnName": "isBedtime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "payloadJson",
"columnName": "payloadJson",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_debug_events_timestamp",
"unique": false,
"columnNames": [
"timestamp"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_debug_events_timestamp` ON `${TABLE_NAME}` (`timestamp`)"
},
{
"name": "index_debug_events_eventType",
"unique": false,
"columnNames": [
"eventType"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_debug_events_eventType` ON `${TABLE_NAME}` (`eventType`)"
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '2af8897c3637536f69d2e37c31fae188')"
]
}
}
Loading